diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index d4980a7d6a..99e4212082 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -104,6 +104,10 @@ set( PCBNEW_DIALOGS dialogs/dialog_pcb_text_properties_base.cpp dialogs/dialog_pns_settings.cpp dialogs/dialog_pns_settings_base.cpp + dialogs/dialog_pns_diff_pair_dimensions.cpp + dialogs/dialog_pns_diff_pair_dimensions_base.cpp + dialogs/dialog_pns_length_tuning_settings.cpp + dialogs/dialog_pns_length_tuning_settings_base.cpp dialogs/dialog_non_copper_zones_properties.cpp dialogs/dialog_non_copper_zones_properties_base.cpp dialogs/dialog_pad_properties.cpp @@ -275,6 +279,7 @@ set( PCBNEW_CLASS_SRCS tools/module_tools.cpp tools/placement_tool.cpp tools/common_actions.cpp + tools/grid_helper.cpp tools/tools_common.cpp ) diff --git a/pcbnew/dialogs/dialog_pns_diff_pair_dimensions.cpp b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions.cpp new file mode 100644 index 0000000000..6704edc70a --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions.cpp @@ -0,0 +1,91 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2014-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +/** + * Push and Shove diff pair dimensions (gap) settings dialog. + */ + +#include "dialog_pns_diff_pair_dimensions.h" +#include + +DIALOG_PNS_DIFF_PAIR_DIMENSIONS::DIALOG_PNS_DIFF_PAIR_DIMENSIONS( wxWindow* aParent, PNS_SIZES_SETTINGS& aSizes ) : + DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE( aParent ), + m_traceWidth ( this, m_traceWidthText, m_traceWidthUnit ), + m_traceGap (this, m_traceGapText, m_traceGapUnit ), + m_viaGap ( this, m_viaGapText, m_viaGapUnit ), + m_sizes( aSizes ) + +{ + m_traceWidth.SetValue ( aSizes.DiffPairWidth() ); + m_traceGap.SetValue ( aSizes.DiffPairGap() ); + m_viaGap.SetValue ( aSizes.DiffPairViaGap() ); + m_viaTraceGapEqual->SetValue ( m_sizes.DiffPairViaGapSameAsTraceGap() ); + + updateCheckbox(); +} + +void DIALOG_PNS_DIFF_PAIR_DIMENSIONS::updateCheckbox() +{ + printf("Checked: %d", m_viaTraceGapEqual->GetValue()); + if( m_viaTraceGapEqual->GetValue() ) + { + m_sizes.SetDiffPairViaGapSameAsTraceGap( true ); + m_viaGapText->Disable(); + m_viaGapLabel->Disable(); + m_viaGapUnit->Disable(); + } else { + m_sizes.SetDiffPairViaGapSameAsTraceGap( false ); + m_viaGapText->Enable(); + m_viaGapLabel->Enable(); + m_viaGapUnit->Enable(); + } +} + +void DIALOG_PNS_DIFF_PAIR_DIMENSIONS::OnClose( wxCloseEvent& aEvent ) +{ + // Do nothing, it is result of ESC pressing + EndModal( 0 ); +} + + +void DIALOG_PNS_DIFF_PAIR_DIMENSIONS::OnOkClick( wxCommandEvent& aEvent ) +{ + // Save widgets' values to settings + m_sizes.SetDiffPairGap ( m_traceGap.GetValue() ); + m_sizes.SetDiffPairViaGap ( m_viaGap.GetValue() ); + m_sizes.SetDiffPairWidth ( m_traceWidth.GetValue() ); + + // todo: verify against design rules + EndModal( 1 ); +} + + +void DIALOG_PNS_DIFF_PAIR_DIMENSIONS::OnCancelClick( wxCommandEvent& aEvent ) +{ + // Do nothing + EndModal( 0 ); +} + +void DIALOG_PNS_DIFF_PAIR_DIMENSIONS::OnViaTraceGapEqualCheck( wxCommandEvent& event ) +{ + event.Skip(); + updateCheckbox(); +} + \ No newline at end of file diff --git a/pcbnew/dialogs/dialog_pns_diff_pair_dimensions.h b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions.h new file mode 100644 index 0000000000..1ea6318fc8 --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions.h @@ -0,0 +1,55 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2014-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +/** + * Push and Shove diff pair dimensions (gap) settings dialog. + */ + +#ifndef __dialog_diff_pair_dimensions_settings__ +#define __dialog_diff_pair_dimensions_settings__ + +#include + +#include "dialog_pns_diff_pair_dimensions_base.h" + +class PNS_SIZES_SETTINGS; + +class DIALOG_PNS_DIFF_PAIR_DIMENSIONS : public DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE +{ + public: + DIALOG_PNS_DIFF_PAIR_DIMENSIONS( wxWindow* aParent, PNS_SIZES_SETTINGS& aSizes ); + + virtual void OnClose( wxCloseEvent& aEvent ); + virtual void OnOkClick( wxCommandEvent& aEvent ); + virtual void OnCancelClick( wxCommandEvent& aEvent ); + virtual void OnViaTraceGapEqualCheck( wxCommandEvent& event ); + + + private: + void updateCheckbox( ); + + WX_UNIT_BINDER m_traceWidth; + WX_UNIT_BINDER m_traceGap; + WX_UNIT_BINDER m_viaGap; + + PNS_SIZES_SETTINGS& m_sizes; +}; + +#endif // __dialog_pns_settings__ diff --git a/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.cpp b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.cpp new file mode 100644 index 0000000000..a74a8b0b1b --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.cpp @@ -0,0 +1,102 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 6 2014) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_pns_diff_pair_dimensions_base.h" + +/////////////////////////////////////////////////////////////////////////// + +DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxSize( 400,-1 ), wxDefaultSize ); + + wxBoxSizer* bSizer7; + bSizer7 = new wxBoxSizer( wxVERTICAL ); + + wxFlexGridSizer* fgSizer1; + fgSizer1 = new wxFlexGridSizer( 0, 3, 0, 0 ); + fgSizer1->AddGrowableCol( 1 ); + fgSizer1->SetFlexibleDirection( wxBOTH ); + fgSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_traceWidthLabel = new wxStaticText( this, wxID_ANY, _("Width:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_traceWidthLabel->Wrap( -1 ); + fgSizer1->Add( m_traceWidthLabel, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 ); + + m_traceWidthText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer1->Add( m_traceWidthText, 0, wxALL|wxEXPAND, 5 ); + + m_traceWidthUnit = new wxStaticText( this, wxID_ANY, _("u"), wxDefaultPosition, wxDefaultSize, 0 ); + m_traceWidthUnit->Wrap( -1 ); + fgSizer1->Add( m_traceWidthUnit, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_traceGapLabel = new wxStaticText( this, wxID_ANY, _("Trace gap:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_traceGapLabel->Wrap( -1 ); + fgSizer1->Add( m_traceGapLabel, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT, 5 ); + + m_traceGapText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer1->Add( m_traceGapText, 0, wxALL|wxEXPAND, 5 ); + + m_traceGapUnit = new wxStaticText( this, wxID_ANY, _("u"), wxDefaultPosition, wxDefaultSize, 0 ); + m_traceGapUnit->Wrap( -1 ); + m_traceGapUnit->SetMaxSize( wxSize( 40,-1 ) ); + + fgSizer1->Add( m_traceGapUnit, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_viaGapLabel = new wxStaticText( this, wxID_ANY, _("Via gap:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_viaGapLabel->Wrap( -1 ); + m_viaGapLabel->Enable( false ); + + fgSizer1->Add( m_viaGapLabel, 0, wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL, 5 ); + + m_viaGapText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + m_viaGapText->Enable( false ); + + fgSizer1->Add( m_viaGapText, 0, wxALL|wxEXPAND, 5 ); + + m_viaGapUnit = new wxStaticText( this, wxID_ANY, _("u"), wxDefaultPosition, wxDefaultSize, 0 ); + m_viaGapUnit->Wrap( -1 ); + m_viaGapUnit->Enable( false ); + m_viaGapUnit->SetMaxSize( wxSize( 40,-1 ) ); + + fgSizer1->Add( m_viaGapUnit, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + bSizer7->Add( fgSizer1, 0, wxEXPAND, 5 ); + + m_viaTraceGapEqual = new wxCheckBox( this, wxID_ANY, _("Via gap same as trace gap"), wxDefaultPosition, wxDefaultSize, 0 ); + m_viaTraceGapEqual->SetValue(true); + bSizer7->Add( m_viaTraceGapEqual, 0, wxALL|wxEXPAND, 5 ); + + m_stdButtons = new wxStdDialogButtonSizer(); + m_stdButtonsOK = new wxButton( this, wxID_OK ); + m_stdButtons->AddButton( m_stdButtonsOK ); + m_stdButtonsCancel = new wxButton( this, wxID_CANCEL ); + m_stdButtons->AddButton( m_stdButtonsCancel ); + m_stdButtons->Realize(); + + bSizer7->Add( m_stdButtons, 0, wxEXPAND, 5 ); + + + this->SetSizer( bSizer7 ); + this->Layout(); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::OnClose ) ); + m_viaTraceGapEqual->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::onViaTraceGapEqualCheck ), NULL, this ); + m_stdButtonsCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::OnCancelClick ), NULL, this ); + m_stdButtonsOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::OnOkClick ), NULL, this ); +} + +DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::~DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE() +{ + // Disconnect Events + this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::OnClose ) ); + m_viaTraceGapEqual->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::onViaTraceGapEqualCheck ), NULL, this ); + m_stdButtonsCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::OnCancelClick ), NULL, this ); + m_stdButtonsOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE::OnOkClick ), NULL, this ); + +} diff --git a/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.fbp b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.fbp new file mode 100644 index 0000000000..d87bb97fa9 --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.fbp @@ -0,0 +1,1002 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + connect + dialog_pns_diff_pair_dimensions_base + 1000 + none + 1 + DIALOG_PNS_SETTINGS_BASE + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + 400,-1 + DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE + + 400,-1 + wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER + DIALOG_SHIM; dialog_shim.h + Differential Pair Dimensions + + + + + + + + + + + + + + OnClose + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer7 + wxVERTICAL + none + + 5 + wxEXPAND + 0 + + 3 + wxBOTH + 1 + + 0 + + fgSizer1 + wxFLEX_GROWMODE_SPECIFIED + none + 0 + 0 + + 5 + wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Width: + + 0 + + + 0 + + 1 + m_traceWidthLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_traceWidthText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + u + + 0 + + + 0 + + 1 + m_traceWidthUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Trace gap: + + 0 + + + 0 + + 1 + m_traceGapLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_traceGapText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + u + + 0 + 40,-1 + + 0 + + 1 + m_traceGapUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + Via gap: + + 0 + + + 0 + + 1 + m_viaGapLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_viaGapText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + u + + 0 + 40,-1 + + 0 + + 1 + m_viaGapUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Via gap same as trace gap + + 0 + + + 0 + + 1 + m_viaTraceGapEqual + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + OnViaTraceGapEqualCheck + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + + m_stdButtons + protected + + OnCancelClick + + + + OnOkClick + + + + + + + + diff --git a/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.h b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.h new file mode 100644 index 0000000000..4300d7f697 --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_diff_pair_dimensions_base.h @@ -0,0 +1,68 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 6 2014) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE_H__ +#define __DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE_H__ + +#include +#include +#include +class DIALOG_SHIM; + +#include "dialog_shim.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxStaticText* m_traceWidthLabel; + wxTextCtrl* m_traceWidthText; + wxStaticText* m_traceWidthUnit; + wxStaticText* m_traceGapLabel; + wxTextCtrl* m_traceGapText; + wxStaticText* m_traceGapUnit; + wxStaticText* m_viaGapLabel; + wxTextCtrl* m_viaGapText; + wxStaticText* m_viaGapUnit; + wxCheckBox* m_viaTraceGapEqual; + wxStdDialogButtonSizer* m_stdButtons; + wxButton* m_stdButtonsOK; + wxButton* m_stdButtonsCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void onViaTraceGapEqualCheck( wxCommandEvent& event ) { event.Skip(); } + virtual void OnCancelClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOkClick( wxCommandEvent& event ) { event.Skip(); } + + + public: + + DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Differential Pair Dimensions"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 400,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); + ~DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE(); + +}; + +#endif //__DIALOG_PNS_DIFF_PAIR_DIMENSIONS_BASE_H__ diff --git a/pcbnew/dialogs/dialog_pns_length_tuning_settings.cpp b/pcbnew/dialogs/dialog_pns_length_tuning_settings.cpp new file mode 100644 index 0000000000..1b5524db8b --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_length_tuning_settings.cpp @@ -0,0 +1,122 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2014-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +/** + * Length tuner settings dialog. + */ + +#include "dialog_pns_length_tuning_settings.h" +#include + +DIALOG_PNS_LENGTH_TUNING_SETTINGS::DIALOG_PNS_LENGTH_TUNING_SETTINGS( wxWindow* aParent, PNS_MEANDER_SETTINGS& aSettings, PNS_ROUTER_MODE aMode ) : + DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE( aParent ), + m_minAmpl ( this, m_minAmplText, m_minAmplUnit ), + m_maxAmpl (this, m_maxAmplText, m_maxAmplUnit ), + m_spacing ( this, m_spacingText, m_spacingUnit ), + m_targetLength ( this, m_targetLengthText, m_targetLengthUnit ), + m_settings( aSettings ), + m_mode ( aMode ) +{ + + m_miterStyle->Enable ( false ); + m_radiusText->Enable ( aMode != PNS_MODE_TUNE_DIFF_PAIR ); + //m_minAmpl.Enable ( aMode != PNS_MODE_TUNE_DIFF_PAIR_SKEW ); + + m_minAmpl.SetValue ( m_settings.m_minAmplitude ); + m_maxAmpl.SetValue ( m_settings.m_maxAmplitude ); + + m_spacing.SetValue ( m_settings.m_spacing ); + m_radiusText->SetValue ( wxString::Format(wxT("%i"), m_settings.m_cornerRadiusPercentage) ); + + + m_miterStyle->SetSelection ( m_settings.m_cornerType == PNS_MEANDER_SETTINGS::ROUND ? 1 : 0 ); + + switch( aMode ) + { + case PNS_MODE_TUNE_SINGLE: + SetTitle ( _("Single track length tuning") ); + m_legend->SetBitmap( KiBitmap( tune_single_track_length_legend_xpm ) ); + m_targetLength.SetValue ( m_settings.m_targetLength ); + + break; + + case PNS_MODE_TUNE_DIFF_PAIR: + SetTitle ( _("Differential pair length tuning") ); + m_legend->SetBitmap( KiBitmap( tune_diff_pair_length_legend_xpm ) ); + m_targetLength.SetValue ( m_settings.m_targetLength ); + + break; + + case PNS_MODE_TUNE_DIFF_PAIR_SKEW: + SetTitle ( _("Differential pair skew tuning") ); + m_legend->SetBitmap( KiBitmap( tune_diff_pair_skew_legend_xpm ) ); + m_targetLengthLabel->SetLabel( _("Target skew: ") ); + m_targetLength.SetValue ( m_settings.m_targetSkew ); + break; + + default: + break; + } + + m_stdButtonsOK->SetDefault(); + m_targetLengthText->SetSelection(-1, -1); + m_targetLengthText->SetFocus(); +} + + +void DIALOG_PNS_LENGTH_TUNING_SETTINGS::OnClose( wxCloseEvent& aEvent ) +{ + // Do nothing, it is result of ESC pressing + EndModal( 0 ); +} + + +void DIALOG_PNS_LENGTH_TUNING_SETTINGS::OnOkClick( wxCommandEvent& aEvent ) +{ + + // fixme: use validators and TransferDataFromWindow + m_settings.m_minAmplitude = m_minAmpl.GetValue(); + m_settings.m_maxAmplitude = m_maxAmpl.GetValue(); + m_settings.m_spacing = m_spacing.GetValue(); + + m_settings.m_cornerRadiusPercentage = wxAtoi( m_radiusText->GetValue() ); + + if (m_mode == PNS_MODE_TUNE_DIFF_PAIR_SKEW) + m_settings.m_targetSkew = m_targetLength.GetValue(); + else + m_settings.m_targetLength = m_targetLength.GetValue(); + + if ( m_settings.m_maxAmplitude < m_settings.m_minAmplitude ) + m_settings.m_maxAmplitude = m_settings.m_maxAmplitude; + + + m_settings.m_cornerType = m_miterStyle->GetSelection( ) ? PNS_MEANDER_SETTINGS::CHAMFER : PNS_MEANDER_SETTINGS::ROUND; + + + + EndModal( 1 ); +} + + +void DIALOG_PNS_LENGTH_TUNING_SETTINGS::OnCancelClick( wxCommandEvent& aEvent ) +{ + // Do nothing + EndModal( 0 ); +} diff --git a/pcbnew/dialogs/dialog_pns_length_tuning_settings.h b/pcbnew/dialogs/dialog_pns_length_tuning_settings.h new file mode 100644 index 0000000000..ae83c36125 --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_length_tuning_settings.h @@ -0,0 +1,56 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2014 CERN + * Author: Maciej Suminski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +/** + * Push and Shove router settings dialog. + */ + +#ifndef __dialog_pns_length_tuning_settings__ +#define __dialog_pns_length_tuning_settings__ + +#include "dialog_pns_length_tuning_settings_base.h" + +#include + +#include + +class PNS_MEANDER_SETTINGS; + +class DIALOG_PNS_LENGTH_TUNING_SETTINGS : public DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE +{ + public: + DIALOG_PNS_LENGTH_TUNING_SETTINGS( wxWindow* aParent, PNS_MEANDER_SETTINGS& aSettings, PNS_ROUTER_MODE aMode ); + + virtual void OnClose( wxCloseEvent& aEvent ); + virtual void OnOkClick( wxCommandEvent& aEvent ); + virtual void OnCancelClick( wxCommandEvent& aEvent ); + + private: + + WX_UNIT_BINDER m_minAmpl; + WX_UNIT_BINDER m_maxAmpl; + WX_UNIT_BINDER m_spacing; + WX_UNIT_BINDER m_targetLength; + + PNS_MEANDER_SETTINGS& m_settings; + PNS_ROUTER_MODE m_mode; +}; + +#endif // __dialog_pns_settings__ diff --git a/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.cpp b/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.cpp new file mode 100644 index 0000000000..5cb8aa03da --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.cpp @@ -0,0 +1,183 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 6 2014) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#include "dialog_pns_length_tuning_settings_base.h" + +/////////////////////////////////////////////////////////////////////////// + +DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE::DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style ) +{ + this->SetSizeHints( wxSize( 345,668 ), wxDefaultSize ); + + wxBoxSizer* bMainSizer; + bMainSizer = new wxBoxSizer( wxVERTICAL ); + + wxStaticBoxSizer* sbSizer1; + sbSizer1 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Length/skew") ), wxVERTICAL ); + + wxFlexGridSizer* fgSizer4; + fgSizer4 = new wxFlexGridSizer( 0, 2, 0, 0 ); + fgSizer4->AddGrowableCol( 1 ); + fgSizer4->SetFlexibleDirection( wxBOTH ); + fgSizer4->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_staticText4 = new wxStaticText( this, wxID_ANY, _("Tune from:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText4->Wrap( -1 ); + fgSizer4->Add( m_staticText4, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + wxArrayString m_choicePathFromChoices; + m_choicePathFrom = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choicePathFromChoices, 0 ); + m_choicePathFrom->SetSelection( 0 ); + fgSizer4->Add( m_choicePathFrom, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText15 = new wxStaticText( this, wxID_ANY, _("Tune to:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText15->Wrap( -1 ); + fgSizer4->Add( m_staticText15, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + wxArrayString m_choice4Choices; + m_choice4 = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choice4Choices, 0 ); + m_choice4->SetSelection( 0 ); + fgSizer4->Add( m_choice4, 0, wxALL, 5 ); + + m_staticText3 = new wxStaticText( this, wxID_ANY, _("Constraint:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText3->Wrap( -1 ); + fgSizer4->Add( m_staticText3, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + wxString m_constraintSourceChoices[] = { _("from Design Rules"), _("manual") }; + int m_constraintSourceNChoices = sizeof( m_constraintSourceChoices ) / sizeof( wxString ); + m_constraintSource = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_constraintSourceNChoices, m_constraintSourceChoices, 0 ); + m_constraintSource->SetSelection( 1 ); + m_constraintSource->Enable( false ); + + fgSizer4->Add( m_constraintSource, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_targetLengthLabel = new wxStaticText( this, wxID_ANY, _("Target length:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_targetLengthLabel->Wrap( -1 ); + fgSizer4->Add( m_targetLengthLabel, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + wxGridSizer* gSizer2; + gSizer2 = new wxGridSizer( 0, 2, 0, 0 ); + + m_targetLengthText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + gSizer2->Add( m_targetLengthText, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_targetLengthUnit = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 ); + m_targetLengthUnit->Wrap( -1 ); + gSizer2->Add( m_targetLengthUnit, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 ); + + + fgSizer4->Add( gSizer2, 1, wxEXPAND, 5 ); + + + sbSizer1->Add( fgSizer4, 1, wxEXPAND, 5 ); + + + bMainSizer->Add( sbSizer1, 0, wxEXPAND|wxALL, 5 ); + + wxStaticBoxSizer* sbSizer2; + sbSizer2 = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Meandering") ), wxVERTICAL ); + + m_legend = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0 ); + sbSizer2->Add( m_legend, 1, wxALL|wxEXPAND, 5 ); + + wxFlexGridSizer* fgSizer3; + fgSizer3 = new wxFlexGridSizer( 0, 3, 0, 0 ); + fgSizer3->AddGrowableCol( 2 ); + fgSizer3->SetFlexibleDirection( wxBOTH ); + fgSizer3->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); + + m_staticText9 = new wxStaticText( this, wxID_ANY, _("Min amplitude (Amin):"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText9->Wrap( -1 ); + fgSizer3->Add( m_staticText9, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_minAmplText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer3->Add( m_minAmplText, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_minAmplUnit = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 ); + m_minAmplUnit->Wrap( -1 ); + fgSizer3->Add( m_minAmplUnit, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + m_staticText91 = new wxStaticText( this, wxID_ANY, _("Max amplitude (Amax):"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText91->Wrap( -1 ); + fgSizer3->Add( m_staticText91, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_maxAmplText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer3->Add( m_maxAmplText, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_maxAmplUnit = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 ); + m_maxAmplUnit->Wrap( -1 ); + fgSizer3->Add( m_maxAmplUnit, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + m_staticText11 = new wxStaticText( this, wxID_ANY, _("Spacing (s):"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText11->Wrap( -1 ); + fgSizer3->Add( m_staticText11, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_spacingText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer3->Add( m_spacingText, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_spacingUnit = new wxStaticText( this, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 ); + m_spacingUnit->Wrap( -1 ); + fgSizer3->Add( m_spacingUnit, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + m_staticText13 = new wxStaticText( this, wxID_ANY, _("Miter radius (r):"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText13->Wrap( -1 ); + fgSizer3->Add( m_staticText13, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_radiusText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer3->Add( m_radiusText, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_radiusUnit = new wxStaticText( this, wxID_ANY, _("%"), wxDefaultPosition, wxDefaultSize, 0 ); + m_radiusUnit->Wrap( -1 ); + fgSizer3->Add( m_radiusUnit, 0, wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT, 5 ); + + m_staticText14 = new wxStaticText( this, wxID_ANY, _("Miter style:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText14->Wrap( -1 ); + m_staticText14->Enable( false ); + + fgSizer3->Add( m_staticText14, 1, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + wxString m_miterStyleChoices[] = { _("45 degree"), _("arc") }; + int m_miterStyleNChoices = sizeof( m_miterStyleChoices ) / sizeof( wxString ); + m_miterStyle = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_miterStyleNChoices, m_miterStyleChoices, 0 ); + m_miterStyle->SetSelection( 0 ); + m_miterStyle->Enable( false ); + + fgSizer3->Add( m_miterStyle, 0, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + + sbSizer2->Add( fgSizer3, 1, wxEXPAND, 5 ); + + + bMainSizer->Add( sbSizer2, 1, wxALL|wxEXPAND, 5 ); + + m_stdButtons = new wxStdDialogButtonSizer(); + m_stdButtonsOK = new wxButton( this, wxID_OK ); + m_stdButtons->AddButton( m_stdButtonsOK ); + m_stdButtonsCancel = new wxButton( this, wxID_CANCEL ); + m_stdButtons->AddButton( m_stdButtonsCancel ); + m_stdButtons->Realize(); + + bMainSizer->Add( m_stdButtons, 0, wxEXPAND|wxTOP|wxBOTTOM, 5 ); + + + this->SetSizer( bMainSizer ); + this->Layout(); + + // Connect Events + this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE::OnClose ) ); + m_stdButtonsCancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE::OnCancelClick ), NULL, this ); + m_stdButtonsOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE::OnOkClick ), NULL, this ); +} + +DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE::~DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE() +{ + // Disconnect Events + this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE::OnClose ) ); + m_stdButtonsCancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE::OnCancelClick ), NULL, this ); + m_stdButtonsOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE::OnOkClick ), NULL, this ); + +} diff --git a/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.fbp b/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.fbp new file mode 100644 index 0000000000..2727188e68 --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.fbp @@ -0,0 +1,2253 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + /home/twl/Kicad-dev/kicad-git/bitmaps_png/cpp_other + UTF-8 + connect + dialog_pns_length_tuning_settings_base + 1000 + none + 1 + DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + 345,668 + DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE + + 345,668 + wxDEFAULT_DIALOG_STYLE + DIALOG_SHIM; dialog_shim.h + Trace length tuning + + + + + + + + + + + + + + OnClose + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bMainSizer + wxVERTICAL + none + + 5 + wxEXPAND|wxALL + 0 + + wxID_ANY + Length/skew + + sbSizer1 + wxVERTICAL + none + + + 5 + wxEXPAND + 1 + + 2 + wxBOTH + 1 + + 0 + + fgSizer4 + wxFLEX_GROWMODE_SPECIFIED + none + 0 + 0 + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Tune from: + + 0 + + + 0 + + 1 + m_staticText4 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_choicePathFrom + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Tune to: + + 0 + + + 0 + + 1 + m_staticText15 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_choice4 + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Constraint: + + 0 + + + 0 + + 1 + m_staticText3 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + "from Design Rules" "manual" + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_constraintSource + 1 + + + protected + 1 + + Resizable + 1 + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Target length: + + 0 + + + 0 + + 1 + m_targetLengthLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 2 + 0 + + gSizer2 + none + 0 + 0 + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_targetLengthText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + unit + + 0 + + + 0 + + 1 + m_targetLengthUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 1 + + wxID_ANY + Meandering + + sbSizer2 + wxVERTICAL + none + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + Load From Resource; + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_legend + 1 + + + protected + 1 + + Resizable + 1 + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 3 + wxBOTH + 2 + + 0 + + fgSizer3 + wxFLEX_GROWMODE_SPECIFIED + none + 0 + 0 + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Min amplitude (Amin): + + 0 + + + 0 + + 1 + m_staticText9 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_minAmplText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + unit + + 0 + + + 0 + + 1 + m_minAmplUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Max amplitude (Amax): + + 0 + + + 0 + + 1 + m_staticText91 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_maxAmplText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + unit + + 0 + + + 0 + + 1 + m_maxAmplUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Spacing (s): + + 0 + + + 0 + + 1 + m_staticText11 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_spacingText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + unit + + 0 + + + 0 + + 1 + m_spacingUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Miter radius (r): + + 0 + + + 0 + + 1 + m_staticText13 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_radiusText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxTOP|wxBOTTOM|wxRIGHT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + % + + 0 + + + 0 + + 1 + m_radiusUnit + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + Miter style: + + 0 + + + 0 + + 1 + m_staticText14 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + "45 degree" "arc" + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_miterStyle + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND|wxTOP|wxBOTTOM + 0 + + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + + m_stdButtons + protected + + OnCancelClick + + + + OnOkClick + + + + + + + + diff --git a/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.h b/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.h new file mode 100644 index 0000000000..f0e8217ac7 --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_length_tuning_settings_base.h @@ -0,0 +1,86 @@ +/////////////////////////////////////////////////////////////////////////// +// C++ code generated with wxFormBuilder (version Jun 6 2014) +// http://www.wxformbuilder.org/ +// +// PLEASE DO "NOT" EDIT THIS FILE! +/////////////////////////////////////////////////////////////////////////// + +#ifndef __DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE_H__ +#define __DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE_H__ + +#include +#include +#include +class DIALOG_SHIM; + +#include "dialog_shim.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +/// Class DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE +/////////////////////////////////////////////////////////////////////////////// +class DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE : public DIALOG_SHIM +{ + private: + + protected: + wxStaticText* m_staticText4; + wxChoice* m_choicePathFrom; + wxStaticText* m_staticText15; + wxChoice* m_choice4; + wxStaticText* m_staticText3; + wxChoice* m_constraintSource; + wxStaticText* m_targetLengthLabel; + wxTextCtrl* m_targetLengthText; + wxStaticText* m_targetLengthUnit; + wxStaticBitmap* m_legend; + wxStaticText* m_staticText9; + wxTextCtrl* m_minAmplText; + wxStaticText* m_minAmplUnit; + wxStaticText* m_staticText91; + wxTextCtrl* m_maxAmplText; + wxStaticText* m_maxAmplUnit; + wxStaticText* m_staticText11; + wxTextCtrl* m_spacingText; + wxStaticText* m_spacingUnit; + wxStaticText* m_staticText13; + wxTextCtrl* m_radiusText; + wxStaticText* m_radiusUnit; + wxStaticText* m_staticText14; + wxChoice* m_miterStyle; + wxStdDialogButtonSizer* m_stdButtons; + wxButton* m_stdButtonsOK; + wxButton* m_stdButtonsCancel; + + // Virtual event handlers, overide them in your derived class + virtual void OnClose( wxCloseEvent& event ) { event.Skip(); } + virtual void OnCancelClick( wxCommandEvent& event ) { event.Skip(); } + virtual void OnOkClick( wxCommandEvent& event ) { event.Skip(); } + + + public: + + DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Trace length tuning"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 345,668 ), long style = wxDEFAULT_DIALOG_STYLE ); + ~DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE(); + +}; + +#endif //__DIALOG_PNS_LENGTH_TUNING_SETTINGS_BASE_H__ diff --git a/pcbnew/dialogs/dialog_pns_settings_base.fbp.bak b/pcbnew/dialogs/dialog_pns_settings_base.fbp.bak new file mode 100644 index 0000000000..fdda4f1fef --- /dev/null +++ b/pcbnew/dialogs/dialog_pns_settings_base.fbp.bak @@ -0,0 +1,1334 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + connect + dialog_pns_settings_base + 1000 + none + 1 + DIALOG_PNS_SETTINGS_BASE + + . + + 1 + 1 + 1 + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + DIALOG_PNS_SETTINGS_BASE + + 279,481 + wxDEFAULT_DIALOG_STYLE + DIALOG_SHIM; dialog_shim.h + Interactive Router settings + + + + + + + + + + + + + + OnClose + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bMainSizer + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + "Highlight collisions" "Shove" "Walk around" "Figure out what's best" + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Mode + 1 + + 0 + + + 0 + + 1 + m_mode + 1 + + + protected + 1 + + Resizable + 0 + 1 + + wxRA_SPECIFY_COLS + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND|wxALL + 1 + + wxID_ANY + Options + + bOptions + wxVERTICAL + none + + + 5 + wxTOP|wxRIGHT|wxLEFT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Shove vias + + 0 + + + 0 + + 1 + m_shoveVias + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxTOP|wxRIGHT|wxLEFT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Jump over obstacles + + 0 + + + 0 + + 1 + m_backPressure + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxTOP|wxRIGHT|wxLEFT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Remove redundant tracks + + 0 + + + 0 + + 1 + m_removeLoops + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxTOP|wxRIGHT|wxLEFT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Automatic neckdown + + 0 + + + 0 + + 1 + m_autoNeckdown + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxTOP|wxRIGHT|wxLEFT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Smooth dragged segments + + 0 + + + 0 + + 1 + m_smoothDragged + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxTOP|wxRIGHT|wxLEFT + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Allow DRC violations + + 0 + + + 0 + + 1 + m_violateDrc + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + Suggest track finish + + 0 + + + 0 + + 1 + m_suggestEnding + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + + bEffort + wxHORIZONTAL + none + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Optimizer effort + + 0 + + + 0 + + 1 + m_effortLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + 0 + protected + 0 + + + + 5 + wxEXPAND + 1 + + + bSlider + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + 2 + + 1 + + 0 + + 0 + + 1 + m_effort + 1 + + + protected + 1 + + Resizable + 1 + + wxSL_AUTOTICKS|wxSL_BOTTOM|wxSL_HORIZONTAL|wxSL_TOP + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + + bSliderLabels + wxHORIZONTAL + none + + 5 + + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + ,90,90,8,70,0 + 0 + 0 + wxID_ANY + low + + 0 + + + 0 + + 1 + m_lowLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + ,90,90,8,70,0 + 0 + 0 + wxID_ANY + high + + 0 + + + 0 + + 1 + m_highLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND | wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_staticline1 + 1 + + + protected + 1 + + Resizable + 1 + + wxLI_HORIZONTAL + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + + m_stdButtons + protected + + OnCancelClick + + + + OnOkClick + + + + + + + + + + diff --git a/pcbnew/dialogs/dialog_track_via_size.cpp b/pcbnew/dialogs/dialog_track_via_size.cpp index ab9e1cac37..82abe58169 100644 --- a/pcbnew/dialogs/dialog_track_via_size.cpp +++ b/pcbnew/dialogs/dialog_track_via_size.cpp @@ -31,15 +31,20 @@ DIALOG_TRACK_VIA_SIZE::DIALOG_TRACK_VIA_SIZE( wxWindow* aParent, BOARD_DESIGN_SETTINGS& aSettings ) : DIALOG_TRACK_VIA_SIZE_BASE( aParent ), + m_trackWidth( aParent, m_trackWidthText, m_trackWidthLabel ), + m_viaDiameter( aParent, m_viaDiameterText, m_viaDiameterLabel ), + m_viaDrill( aParent, m_viaDrillText, m_viaDrillLabel ), m_settings( aSettings ) { // Load router settings to dialog fields - m_trackWidth->SetValue( To_User_Unit( m_trackWidth->GetUnits(), m_settings.GetCustomTrackWidth() ) ); - m_viaDiameter->SetValue( To_User_Unit( m_viaDiameter->GetUnits(), m_settings.GetCustomViaSize() ) ); - m_viaDrill->SetValue( To_User_Unit( m_viaDrill->GetUnits(), m_settings.GetCustomViaDrill() ) ); - - m_trackWidth->SetFocus(); + m_trackWidth.SetValue( m_settings.GetCustomTrackWidth() ); + m_viaDiameter.SetValue( m_settings.GetCustomViaSize() ); + m_viaDrill.SetValue( m_settings.GetCustomViaDrill() ); + m_trackWidthText->SetFocus(); + m_trackWidthText->SetSelection(-1, -1); + m_stdButtonsOK->SetDefault(); + // Pressing ENTER when any of the text input fields is active applies changes #if wxCHECK_VERSION( 3, 0, 0 ) Connect( wxEVT_TEXT_ENTER, wxCommandEventHandler( DIALOG_TRACK_VIA_SIZE::onOkClick ), NULL, this ); @@ -54,11 +59,11 @@ DIALOG_TRACK_VIA_SIZE::DIALOG_TRACK_VIA_SIZE( wxWindow* aParent, BOARD_DESIGN_SE bool DIALOG_TRACK_VIA_SIZE::check() { // Wrong input - if( !m_trackWidth->GetValue() || !m_viaDiameter->GetValue() || !m_viaDrill->GetValue() ) + if( m_trackWidth.GetValue() < 0 || m_viaDiameter.GetValue() < 0 || m_viaDrill.GetValue() < 0 ) return false; // Via drill should be smaller than via diameter - if( m_viaDrill->GetValue() >= m_viaDiameter->GetValue() ) + if( m_viaDrill.GetValue() >= m_viaDiameter.GetValue() ) return false; return true; @@ -76,15 +81,15 @@ void DIALOG_TRACK_VIA_SIZE::onOkClick( wxCommandEvent& aEvent ) if( check() ) { // Store dialog values to the router settings - m_settings.SetCustomTrackWidth( From_User_Unit( m_trackWidth->GetUnits(), *m_trackWidth->GetValue() ) ); - m_settings.SetCustomViaSize( From_User_Unit( m_viaDiameter->GetUnits(), *m_viaDiameter->GetValue() ) ); - m_settings.SetCustomViaDrill( From_User_Unit( m_viaDrill->GetUnits(), *m_viaDrill->GetValue() ) ); + m_settings.SetCustomTrackWidth( m_trackWidth.GetValue() ); + m_settings.SetCustomViaSize( m_viaDiameter.GetValue() ); + m_settings.SetCustomViaDrill( m_viaDrill.GetValue() ); EndModal( 1 ); } else { DisplayError( GetParent(), _( "Settings are incorrect" ) ); - m_trackWidth->SetFocus(); + m_trackWidthText->SetFocus(); } } diff --git a/pcbnew/dialogs/dialog_track_via_size.h b/pcbnew/dialogs/dialog_track_via_size.h index 5db003d7de..c265e53311 100644 --- a/pcbnew/dialogs/dialog_track_via_size.h +++ b/pcbnew/dialogs/dialog_track_via_size.h @@ -25,6 +25,8 @@ #ifndef __dialog_track_via_size__ #define __dialog_track_via_size__ +#include + #include "dialog_track_via_size_base.h" class BOARD_DESIGN_SETTINGS; @@ -37,6 +39,11 @@ class DIALOG_TRACK_VIA_SIZE : public DIALOG_TRACK_VIA_SIZE_BASE DIALOG_TRACK_VIA_SIZE( wxWindow* aParent, BOARD_DESIGN_SETTINGS& aSettings ); protected: + + WX_UNIT_BINDER m_trackWidth; + WX_UNIT_BINDER m_viaDiameter; + WX_UNIT_BINDER m_viaDrill; + // Routings settings that are modified by the dialog. BOARD_DESIGN_SETTINGS& m_settings; diff --git a/pcbnew/dialogs/dialog_track_via_size_base.cpp b/pcbnew/dialogs/dialog_track_via_size_base.cpp index a1bfad0ce4..1c963f6bae 100644 --- a/pcbnew/dialogs/dialog_track_via_size_base.cpp +++ b/pcbnew/dialogs/dialog_track_via_size_base.cpp @@ -16,14 +16,46 @@ DIALOG_TRACK_VIA_SIZE_BASE::DIALOG_TRACK_VIA_SIZE_BASE( wxWindow* parent, wxWind wxBoxSizer* bSizes; bSizes = new wxBoxSizer( wxVERTICAL ); - m_trackWidth = new WX_UNIT_TEXT( this, _("Track width:") ); - bSizes->Add( m_trackWidth, 0, wxALL|wxEXPAND, 5 ); + wxFlexGridSizer* fgSizer1; + fgSizer1 = new wxFlexGridSizer( 0, 3, 0, 0 ); + fgSizer1->SetFlexibleDirection( wxBOTH ); + fgSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED ); - m_viaDiameter = new WX_UNIT_TEXT( this, _("Via diameter:") ); - bSizes->Add( m_viaDiameter, 0, wxALL|wxEXPAND, 5 ); + m_staticText3 = new wxStaticText( this, wxID_ANY, _("Track width:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText3->Wrap( -1 ); + fgSizer1->Add( m_staticText3, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); - m_viaDrill = new WX_UNIT_TEXT( this, _("Via drill:") ); - bSizes->Add( m_viaDrill, 0, wxALL|wxEXPAND, 5 ); + m_trackWidthText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer1->Add( m_trackWidthText, 1, wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 ); + + m_trackWidthLabel = new wxStaticText( this, wxID_ANY, _("inch"), wxDefaultPosition, wxDefaultSize, 0 ); + m_trackWidthLabel->Wrap( -1 ); + fgSizer1->Add( m_trackWidthLabel, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText5 = new wxStaticText( this, wxID_ANY, _("Via diameter:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText5->Wrap( -1 ); + fgSizer1->Add( m_staticText5, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_viaDiameterText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer1->Add( m_viaDiameterText, 1, wxALL|wxEXPAND, 5 ); + + m_viaDiameterLabel = new wxStaticText( this, wxID_ANY, _("u"), wxDefaultPosition, wxDefaultSize, 0 ); + m_viaDiameterLabel->Wrap( -1 ); + fgSizer1->Add( m_viaDiameterLabel, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_staticText7 = new wxStaticText( this, wxID_ANY, _("Via drill:"), wxDefaultPosition, wxDefaultSize, 0 ); + m_staticText7->Wrap( -1 ); + fgSizer1->Add( m_staticText7, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + m_viaDrillText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); + fgSizer1->Add( m_viaDrillText, 1, wxALL|wxEXPAND, 5 ); + + m_viaDrillLabel = new wxStaticText( this, wxID_ANY, _("u"), wxDefaultPosition, wxDefaultSize, 0 ); + m_viaDrillLabel->Wrap( -1 ); + fgSizer1->Add( m_viaDrillLabel, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 ); + + + bSizes->Add( fgSizer1, 1, wxEXPAND|wxALL, 5 ); m_stdButtons = new wxStdDialogButtonSizer(); m_stdButtonsOK = new wxButton( this, wxID_OK ); diff --git a/pcbnew/dialogs/dialog_track_via_size_base.fbp b/pcbnew/dialogs/dialog_track_via_size_base.fbp index c2e3325af7..6f660994ad 100644 --- a/pcbnew/dialogs/dialog_track_via_size_base.fbp +++ b/pcbnew/dialogs/dialog_track_via_size_base.fbp @@ -95,257 +95,791 @@ none 5 - wxALL|wxEXPAND - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - WX_UNIT_TEXT - 1 - m_trackWidth = new WX_UNIT_TEXT( this, _("Track width:") ); - - 1 - WX_UNIT_TEXT* m_trackWidth; - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - #include <wxunittext.h> - - 0 - - - 0 + wxEXPAND|wxALL + 1 + + 3 + wxBOTH + + + 0 - 1 - m_trackWidth - 1 - - - protected - 1 - - Resizable - - 1 - - - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 5 - wxALL|wxEXPAND - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - WX_UNIT_TEXT - 1 - m_viaDiameter = new WX_UNIT_TEXT( this, _("Via diameter:") ); - - 1 - WX_UNIT_TEXT* m_viaDiameter; - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - #include <wxunittext.h> - - 0 - - - 0 - - 1 - m_viaDiameter - 1 - - - protected - 1 - - Resizable - - 1 - - - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 5 - wxALL|wxEXPAND - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - WX_UNIT_TEXT - 1 - m_viaDrill = new WX_UNIT_TEXT( this, _("Via drill:") ); - - 1 - WX_UNIT_TEXT* m_viaDrill; - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - #include <wxunittext.h> - - 0 - - - 0 - - 1 - m_viaDrill - 1 - - - protected - 1 - - Resizable - - 1 - - - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - + fgSizer1 + wxFLEX_GROWMODE_SPECIFIED + none + 0 + 0 + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Track width: + + 0 + + + 0 + + 1 + m_staticText3 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_trackWidthText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + inch + + 0 + + + 0 + + 1 + m_trackWidthLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Via diameter: + + 0 + + + 0 + + 1 + m_staticText5 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_viaDiameterText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + u + + 0 + + + 0 + + 1 + m_viaDiameterLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Via drill: + + 0 + + + 0 + + 1 + m_staticText7 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_viaDrillText + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + u + + 0 + + + 0 + + 1 + m_viaDrillLabel + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pcbnew/dialogs/dialog_track_via_size_base.h b/pcbnew/dialogs/dialog_track_via_size_base.h index 3f62c01588..8cff008abf 100644 --- a/pcbnew/dialogs/dialog_track_via_size_base.h +++ b/pcbnew/dialogs/dialog_track_via_size_base.h @@ -11,12 +11,13 @@ #include #include #include -#include +#include +#include #include #include #include #include -#include +#include #include #include #include @@ -32,9 +33,15 @@ class DIALOG_TRACK_VIA_SIZE_BASE : public wxDialog private: protected: - WX_UNIT_TEXT* m_trackWidth; - WX_UNIT_TEXT* m_viaDiameter; - WX_UNIT_TEXT* m_viaDrill; + wxStaticText* m_staticText3; + wxTextCtrl* m_trackWidthText; + wxStaticText* m_trackWidthLabel; + wxStaticText* m_staticText5; + wxTextCtrl* m_viaDiameterText; + wxStaticText* m_viaDiameterLabel; + wxStaticText* m_staticText7; + wxTextCtrl* m_viaDrillText; + wxStaticText* m_viaDrillLabel; wxStdDialogButtonSizer* m_stdButtons; wxButton* m_stdButtonsOK; wxButton* m_stdButtonsCancel; diff --git a/pcbnew/menubar_pcbframe.cpp b/pcbnew/menubar_pcbframe.cpp index 92008ca165..4c86dbceef 100644 --- a/pcbnew/menubar_pcbframe.cpp +++ b/pcbnew/menubar_pcbframe.cpp @@ -433,6 +433,49 @@ void PCB_EDIT_FRAME::ReCreateMenuBar() _( "Set the origin point for the grid" ), KiBitmap( grid_select_axis_xpm ) ); + wxMenu* routeMenu = new wxMenu; + + AddMenuItem( routeMenu, ID_TRACK_BUTT, + _( "Single Track" ), + _( "Interactively route a single track" ), + KiBitmap( add_tracks_xpm ) ); + + AddMenuItem( routeMenu, ID_DIFF_PAIR_BUTT, + _( "Differential Pair" ), + _( "Interactively route a differential pair" ), + KiBitmap( add_tracks_xpm ) ); + + routeMenu->AppendSeparator(); + + AddMenuItem( routeMenu, ID_TUNE_SINGLE_TRACK_LEN_BUTT, + _( "Tune Track Length" ), + _( "Tune length of a single track" ), + KiBitmap( add_tracks_xpm ) ); + + AddMenuItem( routeMenu, ID_TUNE_DIFF_PAIR_LEN_BUTT, + _( "Tune Differential Pair Length" ), + _( "Tune length of a differential pair" ), + KiBitmap( add_tracks_xpm ) ); + + AddMenuItem( routeMenu, ID_TUNE_DIFF_PAIR_SKEW_BUTT, + _( "Tune Differential Pair Skew/Phase" ), + _( "Tune skew/phase of a differential pair" ), + KiBitmap( add_tracks_xpm ) ); + +/* Fixme: add icons & missing menu entries! + routeMenu->AppendSeparator(); + + AddMenuItem( routeMenu, ID_MENU_MITER_TRACES, + _( "Miter traces..." ), + _( "Miter trace corners with arcs" ), + KiBitmap( grid_select_axis_xpm ) ); + + AddMenuItem( routeMenu, ID_MENU_ADD_TEARDROPS, + _( "Teardrops..." ), + _( "Add teardrops to pads/vias" ), + KiBitmap( grid_select_axis_xpm ) ); +*/ + //----- Preferences and configuration menu------------------------------------ wxMenu* configmenu = new wxMenu; @@ -467,6 +510,12 @@ void PCB_EDIT_FRAME::ReCreateMenuBar() _( "Select how items (pads, tracks texts ... ) are displayed" ), KiBitmap( display_options_xpm ) ); + AddMenuItem( configmenu, ID_MENU_INTERACTIVE_ROUTER_SETTINGS, + _( "Interactive Routing" ), + _( "Configure Interactive Routing." ), + KiBitmap( add_tracks_xpm ) ); // fixme: icon + + //--- dimensions submenu ------------------------------------------------------ wxMenu* dimensionsMenu = new wxMenu; @@ -488,6 +537,11 @@ void PCB_EDIT_FRAME::ReCreateMenuBar() _( "Adjust the global clearance between pads and the solder resist mask" ), KiBitmap( pads_mask_layers_xpm ) ); + AddMenuItem( dimensionsMenu, ID_MENU_DIFF_PAIR_DIMENSIONS, + _( "Differential Pairs" ), + _( "Define the global gap/width for differential pairs." ), + KiBitmap( add_tracks_xpm ) ); // fixme: icon + dimensionsMenu->AppendSeparator(); AddMenuItem( dimensionsMenu, ID_CONFIG_SAVE, _( "&Save" ), _( "Save dimension preferences" ), @@ -592,6 +646,7 @@ void PCB_EDIT_FRAME::ReCreateMenuBar() menuBar->Append( editMenu, _( "&Edit" ) ); menuBar->Append( viewMenu, _( "&View" ) ); menuBar->Append( placeMenu, _( "&Place" ) ); + menuBar->Append( routeMenu, _( "&Route" ) ); menuBar->Append( configmenu, _( "P&references" ) ); menuBar->Append( dimensionsMenu, _( "D&imensions" ) ); menuBar->Append( toolsMenu, _( "&Tools" ) ); diff --git a/pcbnew/router/CMakeLists.txt b/pcbnew/router/CMakeLists.txt index a69b09118f..2f324def01 100644 --- a/pcbnew/router/CMakeLists.txt +++ b/pcbnew/router/CMakeLists.txt @@ -13,12 +13,19 @@ set( PCBNEW_PNS_SRCS time_limit.cpp pns_algo_base.cpp + pns_diff_pair.cpp + pns_diff_pair_placer.cpp + pns_dp_meander_placer.cpp pns_dragger.cpp pns_item.cpp pns_itemset.cpp pns_line.cpp pns_line_placer.cpp pns_logger.cpp + pns_meander.cpp + pns_meander_placer.cpp + pns_meander_placer_base.cpp + pns_meander_skew_placer.cpp pns_node.cpp pns_optimizer.cpp pns_router.cpp @@ -26,11 +33,15 @@ set( PCBNEW_PNS_SRCS pns_shove.cpp pns_sizes_settings.cpp pns_solid.cpp + pns_tool_base.cpp + pns_topology.cpp + pns_tune_status_popup.cpp pns_utils.cpp pns_via.cpp pns_walkaround.cpp router_preview_item.cpp router_tool.cpp + length_tuner_tool.cpp ) add_library( pnsrouter STATIC ${PCBNEW_PNS_SRCS} ) diff --git a/pcbnew/router/direction.h b/pcbnew/router/direction.h index eb37a14988..44f137e68d 100644 --- a/pcbnew/router/direction.h +++ b/pcbnew/router/direction.h @@ -73,7 +73,7 @@ public: */ DIRECTION_45( const VECTOR2I& aVec ) { - construct( aVec ); + construct_( aVec ); } /** @@ -82,7 +82,7 @@ public: */ DIRECTION_45( const SEG& aSeg ) { - construct( aSeg.B - aSeg.A ); + construct_( aSeg.B - aSeg.A ); } /** @@ -309,6 +309,11 @@ public: } } + int Mask() const + { + return 1 << ( (int) m_dir ); + } + private: /** @@ -316,7 +321,7 @@ private: * Calculates the direction from a vector. If the vector's angle is not a multiple of 45 * degrees, the direction is rounded to the nearest octant. * @param aVec our vector */ - void construct( const VECTOR2I& aVec ) + void construct_( const VECTOR2I& aVec ) { m_dir = UNDEFINED; diff --git a/pcbnew/router/length_tuner_tool.cpp b/pcbnew/router/length_tuner_tool.cpp new file mode 100644 index 0000000000..3bfe403cc9 --- /dev/null +++ b/pcbnew/router/length_tuner_tool.cpp @@ -0,0 +1,319 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include + +#include "class_draw_panel_gal.h" +#include "class_board.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "pns_segment.h" +#include "pns_router.h" +#include "pns_meander_placer.h" // fixme: move settings to separate header +#include "pns_tune_status_popup.h" + +#include "length_tuner_tool.h" + +#include "trace.h" + +using namespace KIGFX; +using boost::optional; + +static TOOL_ACTION ACT_StartTuning( "pcbnew.LengthTuner.StartTuning", + AS_CONTEXT, 'X', + "New Track", "Starts laying a new track."); +static TOOL_ACTION ACT_EndTuning( "pcbnew.LengthTuner.EndTuning", + AS_CONTEXT, WXK_END, + "End Track", "Stops laying the current meander."); + +static TOOL_ACTION ACT_Settings( "pcbnew.LengthTuner.Settings", + AS_CONTEXT, 'L', + "Length Tuning Settings", "Sets the length tuning parameters for currently routed item."); + +static TOOL_ACTION ACT_SpacingIncrease( "pcbnew.LengthTuner.SpacingIncrease", + AS_CONTEXT, '1', + "Increase spacing", "Increase meander spacing by one step."); + +static TOOL_ACTION ACT_SpacingDecrease( "pcbnew.LengthTuner.SpacingDecrease", + AS_CONTEXT, '2', + "Decrease spacing ", "Decrease meander spacing by one step."); + +static TOOL_ACTION ACT_AmplIncrease( "pcbnew.LengthTuner.AmplIncrease", + AS_CONTEXT, '3', + "Increase amplitude", "Increase meander amplitude by one step."); + +static TOOL_ACTION ACT_AmplDecrease( "pcbnew.LengthTuner.AmplDecrease", + AS_CONTEXT, '4', + "Decrease amplitude", "Decrease meander amplitude by one step."); + + +LENGTH_TUNER_TOOL::LENGTH_TUNER_TOOL() : + PNS_TOOL_BASE( "pcbnew.LengthTuner" ) +{ +} + + +class TUNER_TOOL_MENU: public CONTEXT_MENU +{ +public: + TUNER_TOOL_MENU( BOARD* aBoard ) + { + SetTitle( wxT( "Length Tuner" ) ); + + //Add( ACT_StartTuning ); + //Add( ACT_EndTuning ); + + //AppendSeparator(); + + Add( ACT_SpacingIncrease ); + Add( ACT_SpacingDecrease ); + Add( ACT_AmplIncrease ); + Add( ACT_AmplDecrease ); + Add( ACT_Settings ); + } +}; + + +LENGTH_TUNER_TOOL::~LENGTH_TUNER_TOOL() +{ + delete m_router; +} + +void LENGTH_TUNER_TOOL::Reset( RESET_REASON aReason ) +{ + PNS_TOOL_BASE::Reset( aReason ); + + Go( &LENGTH_TUNER_TOOL::TuneSingleTrace, COMMON_ACTIONS::routerActivateTuneSingleTrace.MakeEvent() ); + Go( &LENGTH_TUNER_TOOL::TuneDiffPair, COMMON_ACTIONS::routerActivateTuneDiffPair.MakeEvent() ); + Go( &LENGTH_TUNER_TOOL::TuneDiffPairSkew, COMMON_ACTIONS::routerActivateTuneDiffPairSkew.MakeEvent() ); +} + + +void LENGTH_TUNER_TOOL::handleCommonEvents( const TOOL_EVENT& aEvent ) +{ + if( aEvent.IsAction( &ACT_RouterOptions ) ) + { + DIALOG_PNS_SETTINGS settingsDlg( m_frame, m_router->Settings() ); + + if( settingsDlg.ShowModal() ) + { + // FIXME: do we need an explicit update? + } + } + + PNS_MEANDER_PLACER_BASE *placer = static_cast ( m_router->Placer() ); + + if (!placer) + return; + + if( aEvent.IsAction( &ACT_Settings ) ) + { + PNS_MEANDER_SETTINGS settings = placer->MeanderSettings(); + DIALOG_PNS_LENGTH_TUNING_SETTINGS settingsDlg( m_frame, settings, m_router->Mode() ); + + if( settingsDlg.ShowModal() ) + { + placer->UpdateSettings ( settings ); + } + + m_savedMeanderSettings = placer->MeanderSettings( ); + } +} + +void LENGTH_TUNER_TOOL::performTuning() +{ + bool saveUndoBuffer = true; + + if(m_startItem) + { + m_frame->SetActiveLayer( ToLAYER_ID ( m_startItem->Layers().Start() ) ); + + if( m_startItem->Net() >= 0 ) + highlightNet( true, m_startItem->Net() ); + } + + m_ctls->ForceCursorPosition( false ); + m_ctls->SetAutoPan( true ); + + if ( !m_router->StartRouting( m_startSnapPoint, m_startItem, 0 ) ) + { + wxMessageBox ( m_router->FailureReason(), _("Error") ); + highlightNet ( false ); + return; + } + + PNS_TUNE_STATUS_POPUP statusPopup ( m_frame ); + statusPopup.Popup(); + + PNS_MEANDER_PLACER *placer = static_cast ( m_router->Placer() ); + VECTOR2I end; + + placer->UpdateSettings( m_savedMeanderSettings ); + + + while( OPT_TOOL_EVENT evt = Wait() ) + { + if( evt->IsCancel() || evt->IsActivate() ) + break; + else if( evt->Action() == TA_UNDO_REDO ) + { + saveUndoBuffer = false; + break; + } + else if( evt->IsMotion() ) + { + end = evt->Position(); + m_router->Move( end, NULL ); + + wxPoint p = wxGetMousePosition(); + + p.x+=20; + p.y+=20; + + statusPopup.Update ( m_router ); + statusPopup.Move( p ); + } + else if( evt->IsClick( BUT_LEFT ) ) + { + if( m_router->FixRoute( evt->Position(), NULL ) ) + break; + } + else if( evt->IsAction( &ACT_EndTuning ) ) + { + if( m_router->FixRoute( end, NULL ) ) + break; + } else if (evt->IsAction ( &ACT_AmplDecrease ) ) { + placer->AmplitudeStep( -1 ); + m_router->Move( end, NULL ); + } else if (evt->IsAction ( &ACT_AmplIncrease ) ) { + placer->AmplitudeStep( 1 ); + m_router->Move( end, NULL ); + } else if (evt->IsAction ( &ACT_SpacingDecrease ) ) { + placer->SpacingStep( -1 ); + m_router->Move( end, NULL ); + } else if (evt->IsAction ( &ACT_SpacingIncrease ) ) { + placer->SpacingStep( 1 ); + m_router->Move( end, NULL ); + } + + handleCommonEvents( *evt ); + } + + m_router->StopRouting(); + + if( saveUndoBuffer ) + { + // Save the recent changes in the undo buffer + m_frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED ); + m_router->ClearUndoBuffer(); + m_frame->OnModify(); + } + else + { + // It was interrupted by TA_UNDO_REDO event, so we have to sync the world now + m_needsSync = true; + } + + m_ctls->SetAutoPan( false ); + m_ctls->ForceCursorPosition( false ); + highlightNet( false ); + +} + +int LENGTH_TUNER_TOOL::TuneSingleTrace ( const TOOL_EVENT& aEvent ) +{ + m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Tune Trace Length" ) ); + return mainLoop( PNS_MODE_TUNE_SINGLE ); +} + +int LENGTH_TUNER_TOOL::TuneDiffPair ( const TOOL_EVENT& aEvent ) +{ + m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Tune Diff Pair Length" ) ); + return mainLoop( PNS_MODE_TUNE_DIFF_PAIR ); +} + +int LENGTH_TUNER_TOOL::TuneDiffPairSkew ( const TOOL_EVENT& aEvent ) +{ + m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Tune Diff Pair Skew" ) ); + return mainLoop( PNS_MODE_TUNE_DIFF_PAIR_SKEW ); +} + + +int LENGTH_TUNER_TOOL::mainLoop( PNS_ROUTER_MODE aMode ) +{ + // Deselect all items + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + + Activate(); + + m_router->SetMode ( aMode ); + + m_ctls->SetSnapping( true ); + m_ctls->ShowCursor( true ); + + std::auto_ptr ctxMenu ( new TUNER_TOOL_MENU( m_board ) ); + SetContextMenu ( ctxMenu.get() ); + + // Main loop: keep receiving events + while( OPT_TOOL_EVENT evt = Wait() ) + { + if( m_needsSync ) + { + m_router->SyncWorld(); + m_router->SetView( getView() ); + m_needsSync = false; + } + + if( evt->IsCancel() || evt->IsActivate() ) + break; // Finish + else if( evt->Action() == TA_UNDO_REDO ) + m_needsSync = true; + else if( evt->IsMotion() ) + updateStartItem( *evt ); + else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACT_StartTuning ) ) + { + updateStartItem( *evt ); + performTuning( ); + } + + handleCommonEvents( *evt ); + } + + // Restore the default settings + m_ctls->SetAutoPan( false ); + m_ctls->ShowCursor( false ); + m_frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString ); + + // Store routing settings till the next invocation + m_savedSettings = m_router->Settings(); + m_savedSizes = m_router->Sizes(); + + return 0; +} \ No newline at end of file diff --git a/pcbnew/router/length_tuner_tool.h b/pcbnew/router/length_tuner_tool.h new file mode 100644 index 0000000000..79a2cd39ab --- /dev/null +++ b/pcbnew/router/length_tuner_tool.h @@ -0,0 +1,52 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * Author: Maciej Suminski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __LENGTH_TUNER_TOOL_H +#define __LENGTH_TUNER_TOOL_H + +#include "pns_tool_base.h" +#include "pns_meander.h" + +class PNS_TUNE_STATUS_POPUP; + +class APIEXPORT LENGTH_TUNER_TOOL : public PNS_TOOL_BASE +{ +public: + LENGTH_TUNER_TOOL(); + ~LENGTH_TUNER_TOOL(); + + void Reset( RESET_REASON aReason ); + + int TuneSingleTrace ( const TOOL_EVENT& aEvent ); + int TuneDiffPair ( const TOOL_EVENT& aEvent ); + int TuneDiffPairSkew ( const TOOL_EVENT& aEvent ); + int ClearMeanders ( const TOOL_EVENT& aEvent ); + +private: + + void performTuning( ); + int mainLoop( PNS_ROUTER_MODE aMode ); + void handleCommonEvents( const TOOL_EVENT& evt ); + + PNS_MEANDER_SETTINGS m_savedMeanderSettings; +}; + +#endif diff --git a/pcbnew/router/pns_algo_base.h b/pcbnew/router/pns_algo_base.h index 1aa94333fc..e48fcf8137 100644 --- a/pcbnew/router/pns_algo_base.h +++ b/pcbnew/router/pns_algo_base.h @@ -21,6 +21,8 @@ #ifndef __PNS_ALGO_BASE_H #define __PNS_ALGO_BASE_H +#include // for wxString + #include "pns_routing_settings.h" class PNS_ROUTER; @@ -53,6 +55,7 @@ public: ///> Returns the logger object, allowing to dump geometry to a file. virtual PNS_LOGGER* Logger(); + private: PNS_ROUTER* m_router; diff --git a/pcbnew/router/pns_diff_pair.cpp b/pcbnew/router/pns_diff_pair.cpp new file mode 100644 index 0000000000..dba1e3153a --- /dev/null +++ b/pcbnew/router/pns_diff_pair.cpp @@ -0,0 +1,825 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "direction.h" + +#include "pns_diff_pair.h" +#include "pns_router.h" +#include "pns_solid.h" +#include "pns_utils.h" + + +class PNS_LINE; + +PNS_DP_PRIMITIVE_PAIR::PNS_DP_PRIMITIVE_PAIR ( PNS_ITEM *aPrimP, PNS_ITEM *aPrimN ) +{ + m_primP = aPrimP->Clone(); + m_primN = aPrimN->Clone(); + + m_anchorP = m_primP->Anchor(0); + m_anchorN = m_primN->Anchor(0); +} + +void PNS_DP_PRIMITIVE_PAIR::SetAnchors( const VECTOR2I& aAnchorP, const VECTOR2I& aAnchorN ) +{ + m_anchorP = aAnchorP; + m_anchorN = aAnchorN; +} + +PNS_DP_PRIMITIVE_PAIR::PNS_DP_PRIMITIVE_PAIR ( const VECTOR2I& aAnchorP, const VECTOR2I& aAnchorN ) +{ + m_anchorP = aAnchorP; + m_anchorN = aAnchorN; + m_primP = m_primN = NULL; +} + +PNS_DP_PRIMITIVE_PAIR::PNS_DP_PRIMITIVE_PAIR ( const PNS_DP_PRIMITIVE_PAIR& aOther ) +{ + if(aOther.m_primP) + m_primP = aOther.m_primP->Clone(); + if(aOther.m_primN) + m_primN = aOther.m_primN->Clone(); + + m_anchorP = aOther.m_anchorP; + m_anchorN = aOther.m_anchorN; +} + +PNS_DP_PRIMITIVE_PAIR& PNS_DP_PRIMITIVE_PAIR::operator= ( const PNS_DP_PRIMITIVE_PAIR& aOther ) +{ + if(aOther.m_primP) + m_primP = aOther.m_primP->Clone(); + if(aOther.m_primN) + m_primN = aOther.m_primN->Clone(); + + m_anchorP = aOther.m_anchorP; + m_anchorN = aOther.m_anchorN; + + return *this; +} + + +PNS_DP_PRIMITIVE_PAIR::~PNS_DP_PRIMITIVE_PAIR() +{ + delete m_primP; + delete m_primN; +} + +bool PNS_DP_PRIMITIVE_PAIR::Directional() const +{ + if (!m_primP) + return false; + + return m_primP->OfKind(PNS_ITEM::SEGMENT); +} + +DIRECTION_45 PNS_DP_PRIMITIVE_PAIR::anchorDirection ( PNS_ITEM *aItem, const VECTOR2I& aP ) const +{ + if( !aItem->OfKind ( PNS_ITEM::SEGMENT ) ) + return DIRECTION_45(); + + PNS_SEGMENT *s = static_cast (aItem); + + if(s->Seg().A == aP) + return DIRECTION_45 ( s->Seg().A - s->Seg().B ); + else + return DIRECTION_45 ( s->Seg().B - s->Seg().A ); +} + +DIRECTION_45 PNS_DP_PRIMITIVE_PAIR::DirP () const +{ + return anchorDirection ( m_primP, m_anchorP ); +} + +DIRECTION_45 PNS_DP_PRIMITIVE_PAIR::DirN () const +{ + return anchorDirection ( m_primN, m_anchorN ); +} + +static void drawGw ( VECTOR2I p, int color ) +{ + SHAPE_LINE_CHAIN l; + + l.Append ( p - VECTOR2I(-50000, -50000) ); + l.Append ( p + VECTOR2I(-50000, -50000) ); + + //printf("router @ %p\n", PNS_ROUTER::GetInstance()); +// PNS_ROUTER::GetInstance()->DisplayDebugLine ( l, color, 10000 ); + + l.Clear(); + l.Append ( p - VECTOR2I(50000, -50000) ); + l.Append ( p + VECTOR2I(50000, -50000) ); + +// PNS_ROUTER::GetInstance()->DisplayDebugLine ( l, color, 10000 ); + + +} + + +static DIRECTION_45::AngleType angle ( const VECTOR2I &a, const VECTOR2I &b ) +{ + DIRECTION_45 dir_a(a); + DIRECTION_45 dir_b(b); + + return dir_a.Angle(dir_b); + +} + +static bool checkGap ( const SHAPE_LINE_CHAIN &p, const SHAPE_LINE_CHAIN &n, int gap ) +{ + int i, j; + + for (i = 0; i < p.SegmentCount() ;i++) + for (j = 0; j < n.SegmentCount() ; j++) + { + int dist = p.CSegment(i).Distance (n.CSegment(j)); + if (dist < gap - 100) + return false; + } + return true; +} + +void PNS_DP_GATEWAY::Reverse() +{ + m_entryN = m_entryN.Reverse(); + m_entryP = m_entryP.Reverse(); +} + +bool PNS_DIFF_PAIR::BuildInitial ( PNS_DP_GATEWAY& aEntry, PNS_DP_GATEWAY &aTarget, bool aPrefDiagonal ) +{ + SHAPE_LINE_CHAIN p = DIRECTION_45().BuildInitialTrace ( aEntry.AnchorP(), aTarget.AnchorP(), aPrefDiagonal ); + SHAPE_LINE_CHAIN n = DIRECTION_45().BuildInitialTrace ( aEntry.AnchorN(), aTarget.AnchorN(), aPrefDiagonal ); + + if(!checkGap ( p, n, m_gapConstraint )) + return false; + + if (p.SelfIntersecting() || n.SelfIntersecting() ) + return false; + + if(p.Intersects(n)) + return false; + + int mask = aEntry.AllowedAngles() | DIRECTION_45::ANG_STRAIGHT | DIRECTION_45::ANG_OBTUSE; + + SHAPE_LINE_CHAIN sum_n, sum_p; + m_p = p; + m_n = n; + + if( aEntry.HasEntryLines() ) + { + if ( !aEntry.Entry().CheckConnectionAngle( *this, mask ) ) + return false; + sum_p = aEntry.Entry().CP(); + sum_n = aEntry.Entry().CN(); + sum_p.Append(p); + sum_n.Append(n); + } else { + sum_p = p; + sum_n = n; + } + + mask = aTarget.AllowedAngles() | DIRECTION_45::ANG_STRAIGHT | DIRECTION_45::ANG_OBTUSE; + + m_p = sum_p; + m_n = sum_n; + + if( aTarget.HasEntryLines() ) + { + PNS_DP_GATEWAY t(aTarget) ; + t.Reverse(); + + if( !CheckConnectionAngle( t.Entry(), mask ) ) + return false; + + sum_p.Append( t.Entry().CP() ); + sum_n.Append( t.Entry().CN() ); + + } + + m_p = sum_p; + m_n = sum_n; + + return true; +} + +bool PNS_DIFF_PAIR::CheckConnectionAngle ( const PNS_DIFF_PAIR &aOther, int allowedAngles ) const +{ + bool checkP, checkN; + + + if( m_p.SegmentCount() == 0 || aOther.m_p.SegmentCount() == 0) + checkP = true; + else { + DIRECTION_45 p0 ( m_p.CSegment(-1) ); + DIRECTION_45 p1 ( aOther.m_p.CSegment(0) ); + + checkP = (p0.Angle(p1) & allowedAngles) != 0; + } + + + if( m_n.SegmentCount() == 0 || aOther.m_n.SegmentCount() == 0) + checkN = true; + else { + DIRECTION_45 n0 ( m_n.CSegment(-1) ); + DIRECTION_45 n1 ( aOther.m_n.CSegment(0) ); + + checkN = (n0.Angle(n1) & allowedAngles) != 0; + } + + return checkP && checkN; +} + +const PNS_DIFF_PAIR PNS_DP_GATEWAY::Entry() const +{ + return PNS_DIFF_PAIR(m_entryP, m_entryN, 0); +} + +void PNS_DP_GATEWAYS::BuildOrthoProjections ( PNS_DP_GATEWAYS& aEntries, const VECTOR2I& aCursorPos, int aOrthoScore ) +{ + BOOST_FOREACH(PNS_DP_GATEWAY g, aEntries.Gateways()) + { + VECTOR2I dir = (g.AnchorP() - g.AnchorN()).Perpendicular(); + VECTOR2I midpoint ( ( g.AnchorP() + g.AnchorN() ) / 2); + SEG guide ( midpoint, midpoint + dir ); + VECTOR2I proj = guide.LineProject(aCursorPos); + + + PNS_DP_GATEWAYS targets(m_gap); + + targets.m_viaGap = m_viaGap; + targets.m_viaDiameter = m_viaDiameter; + targets.m_fitVias = m_fitVias; + + targets.BuildForCursor ( proj ); + + BOOST_FOREACH ( PNS_DP_GATEWAY t, targets.Gateways() ) + { + t.SetPriority ( aOrthoScore ); + m_gateways.push_back ( t ); + } + } +} + + +bool PNS_DP_GATEWAYS::FitGateways ( PNS_DP_GATEWAYS& aEntry, PNS_DP_GATEWAYS& aTarget, bool aPrefDiagonal, PNS_DIFF_PAIR& aDp ) +{ + std::vector candidates; + + BOOST_FOREACH( PNS_DP_GATEWAY g_entry, aEntry.Gateways() ) + { + BOOST_FOREACH ( PNS_DP_GATEWAY g_target, aTarget.Gateways() ) + { + for(int attempt = 0; attempt < 2; attempt ++) + { + PNS_DIFF_PAIR l ( m_gap ); + + if ( l.BuildInitial( g_entry, g_target, aPrefDiagonal ^ (attempt ? true : false) ) ) + { + int score = (attempt == 1 ? -3 : 0); + score +=g_entry.Priority(); + score +=g_target.Priority(); + + + DP_CANDIDATE c; + c.score = score; + c.p = l.CP(); + c.n = l.CN(); + candidates.push_back(c); + } + } + } + } + + int bestScore = -1000; + DP_CANDIDATE best; + bool found; + + BOOST_FOREACH( DP_CANDIDATE c, candidates ) + { + if ( c.score > bestScore ) + { + bestScore = c.score; + best = c; + found = true; + } + } + + if ( found ) + { + aDp.SetGap ( m_gap ); + aDp.SetShape( best.p, best.n ); + return true; + } + + return false; +} + +bool PNS_DP_GATEWAYS::checkDiagonalAlignment ( const VECTOR2I& a, const VECTOR2I& b) const +{ + VECTOR2I dir ( std::abs (a.x - b.x), std::abs ( a.y - b.y )); + + return (dir.x == 0 && dir.y != 0) || (dir.x == dir.y) || (dir.y == 0 && dir.x != 0); +} + + +void PNS_DP_GATEWAYS::BuildFromPrimitivePair( PNS_DP_PRIMITIVE_PAIR aPair, bool aPreferDiagonal ) +{ + VECTOR2I majorDirection; + VECTOR2I p0_p, p0_n; + int orthoFanDistance; + int diagFanDistance; + const SHAPE *shP = NULL; + + if( aPair.PrimP() == NULL) + { + BuildGeneric ( aPair.AnchorP(), aPair.AnchorN(), true ); + return; + } + + const int pvMask = PNS_ITEM::SOLID | PNS_ITEM::VIA; + + if ( aPair.PrimP()->OfKind ( pvMask ) && aPair.PrimN()->OfKind ( pvMask ) ) + { + p0_p = aPair.AnchorP(); + p0_n = aPair.AnchorN(); + + shP = aPair.PrimP()->Shape(); + + } else if ( aPair.PrimP()->OfKind ( PNS_ITEM::SEGMENT ) && aPair.PrimN()->OfKind ( PNS_ITEM::SEGMENT ) ) { + buildDpContinuation ( aPair, aPreferDiagonal ); + + return; + } + + majorDirection = (p0_p - p0_n).Perpendicular(); + + switch( shP->Type() ) + { + case SH_RECT: + { + int w = static_cast ( shP )->GetWidth(); + int h = static_cast ( shP )->GetHeight(); + + if(w < h) + std::swap(w,h); + + orthoFanDistance = w * 3/4; + diagFanDistance = (w - h) / 2; + break; + } + + case SH_SEGMENT: + { + int w = static_cast ( shP )->GetWidth(); + SEG s = static_cast ( shP )->GetSeg(); + + orthoFanDistance = w + (s.B - s.A).EuclideanNorm() / 2; + diagFanDistance = (s.B - s.A).EuclideanNorm() / 2; + break; + } + + default: + BuildGeneric ( p0_p, p0_n, true ); + return; + } + + if(checkDiagonalAlignment ( p0_p, p0_n )) + { + int padDist = (p0_p - p0_n).EuclideanNorm(); + + for(int k = 0; k < 2; k++ ) + { + VECTOR2I dir, dp, dv; + + if(k == 0) + { + + dir = majorDirection.Resize(orthoFanDistance); + int d = (padDist - m_gap) / 2; + + dp = dir.Resize( d ); + dv = (p0_n - p0_p).Resize( d ); + } else { + dir = majorDirection.Resize(diagFanDistance); + int d = (padDist - m_gap) / 2; + dp = dir.Resize( d ); + dv = (p0_n - p0_p).Resize( d ); + } + + for(int i = 0; i < 2; i++) + { + int sign = i ? -1 : 1; + + VECTOR2I gw_p ( p0_p + sign * (dir + dp) + dv ); + VECTOR2I gw_n ( p0_n + sign * (dir + dp) - dv ); + + SHAPE_LINE_CHAIN entryP (p0_p, p0_p + sign * dir, gw_p); + SHAPE_LINE_CHAIN entryN (p0_n, p0_n + sign * dir, gw_n); + + PNS_DP_GATEWAY gw ( gw_p, gw_n, false ); + + gw.SetEntryLines ( entryP, entryN ); + gw.SetPriority(100 - k); + m_gateways.push_back( gw ); + } + } + } + + BuildGeneric ( p0_p, p0_n, true ); +} + + +void PNS_DP_GATEWAYS::BuildForCursor( const VECTOR2I& aCursorPos ) +{ + int gap = m_fitVias ? m_viaGap + m_viaDiameter : m_gap; + + for (int attempt = 0; attempt < 2; attempt ++) + { + for(int i = 0; i < 4; i++ ) + { + VECTOR2I dir; + + + if( !attempt ) + { + dir = VECTOR2I( gap, gap ).Resize( gap / 2 ); + + if( i % 2 == 0 ) + dir.x = -dir.x; + if( i / 2 == 0 ) + dir.y = -dir.y; + } + else + { + if( i /2 == 0) + dir = VECTOR2I( gap / 2 * ( (i % 2) ? -1 : 1), 0 ); + else + dir = VECTOR2I( 0, gap / 2 * ( (i % 2) ? -1 : 1) ); + } + + if( m_fitVias ) + BuildGeneric ( aCursorPos + dir, aCursorPos - dir, true, true ); + else + m_gateways.push_back( PNS_DP_GATEWAY( aCursorPos + dir, aCursorPos - dir, attempt ? true : false ) ); + + drawGw ( aCursorPos + dir, 2 ); + drawGw ( aCursorPos - dir, 3 ); + } + } +} + + +void PNS_DP_GATEWAYS::buildEntries ( const VECTOR2I& p0_p, const VECTOR2I& p0_n ) +{ + BOOST_FOREACH (PNS_DP_GATEWAY &g, m_gateways ) + { + if ( !g.HasEntryLines() ) + { + SHAPE_LINE_CHAIN lead_p = DIRECTION_45().BuildInitialTrace ( g.AnchorP(), p0_p, g.IsDiagonal() ).Reverse(); + SHAPE_LINE_CHAIN lead_n = DIRECTION_45().BuildInitialTrace ( g.AnchorN(), p0_n, g.IsDiagonal() ).Reverse(); + g.SetEntryLines(lead_p, lead_n); + } + } +} + + +void PNS_DP_GATEWAYS::buildDpContinuation ( PNS_DP_PRIMITIVE_PAIR aPair, bool aIsDiagonal ) +{ + PNS_DP_GATEWAY gw ( aPair.AnchorP(), aPair.AnchorN(), aIsDiagonal ); + gw.SetPriority( 100 ); + m_gateways.push_back ( gw ); + + if ( !aPair.Directional() ) + return; + + DIRECTION_45 dP = aPair.DirP(); + DIRECTION_45 dN = aPair.DirN(); + + int gap = (aPair.AnchorP() - aPair.AnchorN()).EuclideanNorm(); + + VECTOR2I vdP = aPair.AnchorP() + dP.Left().ToVector(); + VECTOR2I vdN = aPair.AnchorN() + dN.Left().ToVector(); + + PNS_SEGMENT *sP = static_cast (aPair.PrimP()); + + VECTOR2I t1, t2; + + if( sP->Seg().Side(vdP) == sP->Seg().Side(vdN )) + { + t1 = aPair.AnchorP() + dP.Left().ToVector().Resize( gap ); + t2 = aPair.AnchorN() + dP.Right().ToVector().Resize( gap ); + } + else + { + t1 = aPair.AnchorP() + dP.Right().ToVector().Resize( gap ); + t2 = aPair.AnchorN() + dP.Left().ToVector().Resize( gap ); + } + + + PNS_DP_GATEWAY gwL ( t2, aPair.AnchorN(), !aIsDiagonal ); + SHAPE_LINE_CHAIN ep = dP.BuildInitialTrace ( aPair.AnchorP(), t2, !aIsDiagonal ); + + gwL.SetPriority(10); + gwL.SetEntryLines ( ep , SHAPE_LINE_CHAIN( ) ); + + m_gateways.push_back(gwL); + + PNS_DP_GATEWAY gwR (aPair.AnchorP(), t1, !aIsDiagonal ); + SHAPE_LINE_CHAIN en = dP.BuildInitialTrace ( aPair.AnchorN(), t1, !aIsDiagonal ); + gwR.SetPriority(10); + gwR.SetEntryLines ( SHAPE_LINE_CHAIN( ), en ); + + m_gateways.push_back(gwR); +} + + +void PNS_DP_GATEWAYS::BuildGeneric( const VECTOR2I& p0_p, const VECTOR2I& p0_n, bool aBuildEntries, bool aViaMode ) +{ + SEG st_p[2], st_n[2]; + SEG d_n[2], d_p[2]; + + const int padToGapThreshold = 3; + int padDist = ( p0_p - p0_p ).EuclideanNorm( ); + + st_p[0] = SEG(p0_p + VECTOR2I(-100, 0), p0_p + VECTOR2I(100, 0) ); + st_n[0] = SEG(p0_n + VECTOR2I(-100, 0), p0_n + VECTOR2I(100, 0) ); + st_p[1] = SEG(p0_p + VECTOR2I(0, -100), p0_p + VECTOR2I(0, 100) ); + st_n[1] = SEG(p0_n + VECTOR2I(0, -100), p0_n + VECTOR2I(0, 100) ); + d_p[0] = SEG ( p0_p + VECTOR2I (-100, -100), p0_p + VECTOR2I(100, 100)); + d_p[1] = SEG ( p0_p + VECTOR2I (100, -100), p0_p + VECTOR2I(-100, 100)); + d_n[0] = SEG ( p0_n + VECTOR2I (-100, -100), p0_n + VECTOR2I(100, 100)); + d_n[1] = SEG ( p0_n + VECTOR2I (100, -100), p0_n + VECTOR2I(-100, 100)); + + // midpoint exit & side-by exits + for(int i = 0; i < 2; i++) + { + bool straightColl = st_p[i].Collinear ( st_n[i] ); + bool diagColl = d_p[i].Collinear( d_n[i] ); + + if( straightColl || diagColl ) + { + VECTOR2I dir = ( p0_n - p0_p ).Resize( m_gap / 2 ); + VECTOR2I m = ( p0_p + p0_n ) / 2; + int prio = ( padDist > padToGapThreshold * m_gap ? 2 : 1); + + if(!aViaMode) + { + m_gateways.push_back( PNS_DP_GATEWAY( m - dir, m + dir, diagColl, DIRECTION_45::ANG_RIGHT, prio ) ); + + dir = ( p0_n - p0_p ).Resize( m_gap ); + m_gateways.push_back( PNS_DP_GATEWAY( p0_p - dir, p0_p - dir + dir.Perpendicular(), diagColl ) ); + m_gateways.push_back( PNS_DP_GATEWAY( p0_p - dir, p0_p - dir - dir.Perpendicular(), diagColl ) ); + m_gateways.push_back( PNS_DP_GATEWAY( p0_n + dir + dir.Perpendicular(), p0_n + dir, diagColl ) ); + m_gateways.push_back( PNS_DP_GATEWAY( p0_n + dir - dir.Perpendicular(), p0_n + dir, diagColl ) ); + } + } + } + + + for (int i = 0; i < 2; i++) + for(int j = 0; j < 2; j++) + { + OPT_VECTOR2I ips[2], m; + + ips[0] = d_n[i].IntersectLines( d_p[j] ); + ips[1] = st_p[i].IntersectLines( st_n[j] ); + + if ( d_n[i].Collinear (d_p[j]) ) + ips [0] = OPT_VECTOR2I(); + if ( st_p[i].Collinear (st_p[j]) ) + ips [1] = OPT_VECTOR2I(); + + // diagonal-diagonal and straight-straight cases - the most typical case if the pads + // are on the same straight/diagonal line + for ( int k = 0; k < 2; k++ ) + { + m = ips [ k ]; + if(m && *m != p0_p && *m != p0_n ) + { + int prio = ( padDist > padToGapThreshold * m_gap ? 10 : 20); + VECTOR2I g_p ( ( p0_p - *m ).Resize ( ( double ) m_gap * M_SQRT1_2 ) ); + VECTOR2I g_n ( ( p0_n - *m ).Resize ( ( double ) m_gap * M_SQRT1_2 ) ); + + m_gateways.push_back( PNS_DP_GATEWAY( *m + g_p, *m + g_n, k == 0 ? true : false, DIRECTION_45::ANG_OBTUSE, prio ) ); + } + } + + ips[0] = st_n[i].IntersectLines( d_p[j] ); + ips[1] = st_p[i].IntersectLines( d_n[j] ); + + // diagonal-straight cases: 8 possibilities of "weirder" exists + for ( int k = 0; k < 2; k++ ) + { + m = ips[k]; + + if(!aViaMode && m && *m != p0_p && *m != p0_n ) + { + VECTOR2I g_p, g_n; + + g_p = ( p0_p - *m ).Resize ((double)m_gap * M_SQRT2 ); + g_n = ( p0_n - *m ).Resize ((double)m_gap ); + + if ( angle ( g_p, g_n ) != DIRECTION_45::ANG_ACUTE ) + m_gateways.push_back( PNS_DP_GATEWAY( *m + g_p, *m + g_n, true ) ); + + g_p = ( p0_p - *m ).Resize ( m_gap ); + g_n = ( p0_n - *m ).Resize ( (double)m_gap * M_SQRT2 ); + + if ( angle ( g_p, g_n ) != DIRECTION_45::ANG_ACUTE ) + m_gateways.push_back( PNS_DP_GATEWAY( *m + g_p, *m + g_n, true ) ); + + } + } + } + + if (aBuildEntries) + buildEntries(p0_p, p0_n); +} + + +PNS_DP_PRIMITIVE_PAIR PNS_DIFF_PAIR::EndingPrimitives() +{ + if (m_hasVias) + return PNS_DP_PRIMITIVE_PAIR ( &m_via_p, &m_via_n ); + else + { + const PNS_LINE lP ( PLine() ); + const PNS_LINE lN ( NLine() ); + + PNS_SEGMENT sP ( lP, lP.CSegment(-1) ); + PNS_SEGMENT sN ( lN, lN.CSegment(-1) ); + + PNS_DP_PRIMITIVE_PAIR dpair ( &sP, &sN ); + dpair.SetAnchors ( sP.Seg().B, sN.Seg().B ); + return dpair; + } +} + +bool commonParallelProjection ( SEG n, SEG p, SEG &pClip, SEG& nClip ) +{ + SEG n_proj_p ( p.LineProject(n.A), p.LineProject(n.B) ); + + int64_t t_a = 0; + int64_t t_b = p.TCoef(p.B); + + int64_t tproj_a = p.TCoef(n_proj_p.A); + int64_t tproj_b = p.TCoef(n_proj_p.B); + + if(t_b < t_a) + std::swap ( t_b, t_a ); + + if(tproj_b < tproj_a) + std::swap ( tproj_b, tproj_a ); + + + if(t_b <= tproj_a) + return false; + + if(t_a >= tproj_b) + return false; + + + int64_t t[4] = { 0, p.TCoef ( p.B ), p.TCoef ( n_proj_p.A ), p.TCoef ( n_proj_p.B ) }; + std::vector tv(t, t+4); + std::sort(tv.begin(), tv.end()); // fixme: awful and disgusting way of finding 2 midpoints + + int64_t pLenSq = p.SquaredLength(); + + VECTOR2I dp = p.B - p.A; + pClip.A.x = p.A.x + rescale ( (int64_t)dp.x, tv[1], pLenSq ); + pClip.A.y = p.A.y + rescale ( (int64_t)dp.y, tv[1], pLenSq ); + + pClip.B.x = p.A.x + rescale ( (int64_t)dp.x, tv[2], pLenSq ); + pClip.B.y = p.A.y + rescale ( (int64_t)dp.y, tv[2], pLenSq ); + + nClip.A = n.LineProject(pClip.A); + nClip.B = n.LineProject(pClip.B); + + return true; + +} + +double PNS_DIFF_PAIR::Skew () const +{ + return m_p.Length() - m_n.Length(); +} + +void PNS_DIFF_PAIR::CoupledSegmentPairs ( COUPLED_SEGMENTS_VEC& aPairs ) const +{ + SHAPE_LINE_CHAIN p ( m_p ); + SHAPE_LINE_CHAIN n ( m_n ); + + p.Simplify(); + n.Simplify(); + + for(int i = 0; i < p.SegmentCount(); i++ ) + { + for (int j = 0; j < n.SegmentCount(); j++ ) + { + SEG sp = p.CSegment(i); + SEG sn = n.CSegment(j); + + SEG p_clip, n_clip; + + int64_t dist = std::abs ( sp.Distance(sn) - m_width ); + + if( sp.ApproxParallel(sn) && m_gapConstraint.Matches ( dist ) && commonParallelProjection ( sp, sn, p_clip, n_clip )) + { + const COUPLED_SEGMENTS spair ( p_clip, sp, i, n_clip, sn, j); + aPairs.push_back( spair ); + } + } + } +} + +int64_t PNS_DIFF_PAIR::CoupledLength ( const SHAPE_LINE_CHAIN& aP, const SHAPE_LINE_CHAIN& aN ) const +{ + int64_t total = 0; + + for(int i = 0; i < aP.SegmentCount(); i++ ) + { + for (int j = 0; j < aN.SegmentCount(); j++ ) + { + SEG sp = aP.CSegment(i); + SEG sn = aN.CSegment(j); + + SEG p_clip, n_clip; + + int64_t dist = std::abs ( sp.Distance(sn) - m_width ); + + if( sp.ApproxParallel(sn) && m_gapConstraint.Matches ( dist ) && commonParallelProjection ( sp, sn, p_clip, n_clip )) + total += p_clip.Length(); + } + } + return total; +} + + + +double PNS_DIFF_PAIR::CoupledLength() const +{ + COUPLED_SEGMENTS_VEC pairs; + + CoupledSegmentPairs(pairs); + + double l = 0.0; + for(unsigned int i = 0; i < pairs.size();i++) + l += pairs[i].coupledP.Length(); + + return l; +} + +double PNS_DIFF_PAIR::CoupledLengthFactor() const +{ + double t = TotalLength(); + if( t == 0.0 ) + return 0.0; + return CoupledLength() / t; +} + +double PNS_DIFF_PAIR::TotalLength() const +{ + double lenP = m_p.Length(); + double lenN = m_n.Length(); + + return (lenN + lenP ) / 2.0; +} + +int PNS_DIFF_PAIR::CoupledLength ( const SEG& aP, const SEG& aN ) const +{ + SEG p_clip, n_clip; + int64_t dist = std::abs ( aP.Distance(aN) - m_width ); + + if( aP.ApproxParallel(aN) && m_gapConstraint.Matches ( dist ) && commonParallelProjection ( aP, aN, p_clip, n_clip )) + return p_clip.Length(); + + return 0; +} + diff --git a/pcbnew/router/pns_diff_pair.h b/pcbnew/router/pns_diff_pair.h new file mode 100644 index 0000000000..d680adf166 --- /dev/null +++ b/pcbnew/router/pns_diff_pair.h @@ -0,0 +1,457 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + + +#ifndef __PNS_DIFF_PAIR_H +#define __PNS_DIFF_PAIR_H + +#include + +#include +#include + +#include "pns_line.h" +#include "pns_via.h" + +#include "ranged_num.h" + +class PNS_DIFF_PAIR; + +/** + * Class PNS_DP_GATEWAY + * + * Defines a "gateway" for routing a differential pair - e.g. a pair of points (anchors) with certain + * orientation, spacing and (optionally) predefined entry paths. The routing algorithm connects such + * gateways with parallel lines, thus creating a difrerential pair. + **/ +class PNS_DP_GATEWAY { +public: + PNS_DP_GATEWAY ( const VECTOR2I& aAnchorP, + const VECTOR2I& aAnchorN, + bool aIsDiagonal, + int aAllowedEntryAngles = DIRECTION_45::ANG_OBTUSE, + int aPriority = 0 ) + : m_anchorP(aAnchorP), + m_anchorN (aAnchorN), + m_isDiagonal( aIsDiagonal ), + m_allowedEntryAngles (aAllowedEntryAngles), + m_priority(aPriority) + { + m_hasEntryLines = false; + } + + ~PNS_DP_GATEWAY () + { + + } + + /** + * Function IsDiagonal() + * + * @return true, if the gateway anchors lie on a diagonal line + */ + + bool IsDiagonal() const + { + return m_isDiagonal; + } + + const VECTOR2I& AnchorP () const { return m_anchorP; } + const VECTOR2I& AnchorN () const { return m_anchorN; } + + /** + * Function AllowedAngles() + * + * @return a mask of 45-degree entry directoins allowed for the + * gateway. + */ + int AllowedAngles () const { return m_allowedEntryAngles; } + + /** + * Function Priority() + * + * @return priority/score value for gateway matching + */ + int Priority() const + { + return m_priority; + } + + void SetPriority(int aPriority) + { + m_priority = aPriority; + } + + void SetEntryLines ( const SHAPE_LINE_CHAIN& aEntryP, const SHAPE_LINE_CHAIN& aEntryN ) + { + m_entryP = aEntryP; + m_entryN = aEntryN; + m_hasEntryLines = true; + } + + const SHAPE_LINE_CHAIN& EntryP () const { return m_entryP; } + const SHAPE_LINE_CHAIN& EntryN () const { return m_entryN; } + const PNS_DIFF_PAIR Entry() const ; + + void Reverse(); + + bool HasEntryLines () const + { + return m_hasEntryLines; + } + +private: + + SHAPE_LINE_CHAIN m_entryP, m_entryN; + bool m_hasEntryLines; + VECTOR2I m_anchorP, m_anchorN; + bool m_isDiagonal; + int m_allowedEntryAngles; + int m_priority; +}; + +/** + * Class PNS_DP_PRIMITIVE_PAIR + * + * Stores staring/ending primitives (pads, vias or segments) for a differential pair. + **/ +class PNS_DP_PRIMITIVE_PAIR +{ + +public: + PNS_DP_PRIMITIVE_PAIR(): + m_primP (NULL), m_primN ( NULL ) {}; + + PNS_DP_PRIMITIVE_PAIR ( const PNS_DP_PRIMITIVE_PAIR& aOther ); + PNS_DP_PRIMITIVE_PAIR ( PNS_ITEM *aPrimP, PNS_ITEM *aPrimN ); + PNS_DP_PRIMITIVE_PAIR ( const VECTOR2I& aAnchorP, const VECTOR2I& aAnchorN ); + + ~PNS_DP_PRIMITIVE_PAIR(); + + void SetAnchors ( const VECTOR2I& aAnchorP, const VECTOR2I& aAnchorN ); + + const VECTOR2I& AnchorP () const { return m_anchorP; } + const VECTOR2I& AnchorN () const { return m_anchorN; } + + PNS_DP_PRIMITIVE_PAIR& operator= ( const PNS_DP_PRIMITIVE_PAIR& aOther ); + + PNS_ITEM* PrimP () const { return m_primP; } + PNS_ITEM* PrimN () const { return m_primN; } + + bool Directional() const; + + DIRECTION_45 DirP () const; + DIRECTION_45 DirN () const; + +private: + + DIRECTION_45 anchorDirection ( PNS_ITEM *aItem, const VECTOR2I& aP) const; + + PNS_ITEM *m_primP, *m_primN; + VECTOR2I m_anchorP, m_anchorN; +}; + +/** + * Class PNS_GATEWAYS + * + * A set of gateways calculated for the cursor or starting/ending primitive pair. + **/ + +class PNS_DP_GATEWAYS +{ + + public: + PNS_DP_GATEWAYS ( int aGap ): + m_gap(aGap), m_viaGap( aGap ) {}; + + void SetGap ( int aGap ) { + m_gap = aGap; + m_viaGap = aGap; + } + + void Clear() + { + m_gateways.clear(); + } + + void SetFitVias ( bool aEnable, int aDiameter = 0, int aViaGap = -1 ) + { + m_fitVias = aEnable; + m_viaDiameter = aDiameter; + if(aViaGap < 0) + m_viaGap = m_gap; + else + m_viaGap = aViaGap; + } + + + void BuildForCursor ( const VECTOR2I& aCursorPos ); + void BuildOrthoProjections ( PNS_DP_GATEWAYS &aEntries, const VECTOR2I& aCursorPos, int aOrthoScore ); + void BuildGeneric ( const VECTOR2I& p0_p, const VECTOR2I& p0_n, bool aBuildEntries = false, bool aViaMode = false ); + void BuildFromPrimitivePair( PNS_DP_PRIMITIVE_PAIR aPair, bool aPreferDiagonal ); + + bool FitGateways ( PNS_DP_GATEWAYS& aEntry, PNS_DP_GATEWAYS& aTarget, bool aPrefDiagonal, PNS_DIFF_PAIR& aDp ); + + std::vector& Gateways() + { + return m_gateways; + } + + private: + + struct DP_CANDIDATE + { + SHAPE_LINE_CHAIN p, n; + VECTOR2I gw_p, gw_n; + int score; + }; + + bool checkDiagonalAlignment ( const VECTOR2I& a, const VECTOR2I& b) const; + void buildDpContinuation ( PNS_DP_PRIMITIVE_PAIR aPair, bool aIsDiagonal ); + void buildEntries ( const VECTOR2I& p0_p, const VECTOR2I& p0_n ); + + int m_gap; + int m_viaGap; + int m_viaDiameter; + bool m_fitVias; + + std::vector m_gateways; +}; + + +/** + * Class PNS_DIFF_PAIR + * + * Basic class for a differential pair. Stores two PNS_LINEs (for positive and negative nets, respectively), + * the gap and coupling constraints. + **/ +class PNS_DIFF_PAIR : public PNS_ITEM { + +public: + struct COUPLED_SEGMENTS { + COUPLED_SEGMENTS ( const SEG& aCoupledP, const SEG& aParentP, int aIndexP, + const SEG& aCoupledN, const SEG& aParentN, int aIndexN ) : + coupledP ( aCoupledP ), + coupledN ( aCoupledN ), + parentP ( aParentP ), + parentN ( aParentN ), + indexP ( aIndexP ), + indexN ( aIndexN ) + {} + + SEG coupledP; + SEG coupledN; + SEG parentP; + SEG parentN; + int indexP; + int indexN; + }; + + typedef std::vector COUPLED_SEGMENTS_VEC; + + PNS_DIFF_PAIR ( ) : PNS_ITEM ( DIFF_PAIR ), m_hasVias (false) {} + + PNS_DIFF_PAIR ( int aGap ) : + PNS_ITEM ( DIFF_PAIR ), + m_hasVias (false) + { + m_gapConstraint = aGap; + } + + PNS_DIFF_PAIR ( const SHAPE_LINE_CHAIN &aP, const SHAPE_LINE_CHAIN& aN, int aGap = 0 ): + PNS_ITEM ( DIFF_PAIR ), + m_n (aN), + m_p (aP), + m_hasVias (false) + { + m_gapConstraint = aGap; + } + + PNS_DIFF_PAIR ( const PNS_LINE &aLineP, const PNS_LINE &aLineN, int aGap = 0 ): + PNS_ITEM ( DIFF_PAIR ), + m_line_p ( aLineP ), + m_line_n ( aLineN ), + m_hasVias (false) + { + m_gapConstraint = aGap; + m_net_p = aLineP.Net(); + m_net_n = aLineN.Net(); + m_p = aLineP.CLine(); + m_n = aLineN.CLine(); + } + + static inline bool ClassOf( const PNS_ITEM* aItem ) + { + return aItem && DIFF_PAIR == aItem->Kind(); + } + + PNS_DIFF_PAIR * Clone() const { assert(false); return NULL; } + + static PNS_DIFF_PAIR* AssembleDp ( PNS_LINE *aLine ); + + void SetShape ( const SHAPE_LINE_CHAIN &aP, const SHAPE_LINE_CHAIN& aN, bool aSwapLanes = false) + { + if (aSwapLanes) + { + m_p = aN; + m_n = aP; + } else { + m_p = aP; + m_n = aN; + } + } + + void SetShape ( const PNS_DIFF_PAIR& aPair ) + { + m_p = aPair.m_p; + m_n = aPair.m_n; + } + + void SetNets ( int aP, int aN ) + { + m_net_p = aP; + m_net_n = aN; + } + + void SetWidth ( int aWidth ) + { + m_width = aWidth; + } + + int Width() const { return m_width; } + + void SetGap ( int aGap) + { + m_gap = aGap; + m_gapConstraint = RANGED_NUM ( m_gap, 10000, 10000 ); + } + + int Gap() const { + return m_gap; + } + + void AppendVias ( const PNS_VIA &aViaP, const PNS_VIA& aViaN ) + { + m_hasVias = true; + m_via_p = aViaP; + m_via_n = aViaN; + } + + void RemoveVias () + { + m_hasVias = false; + } + + bool EndsWithVias() const + { + return m_hasVias; + } + + int NetP() const + { + return m_net_p; + } + + int NetN() const + { + return m_net_n; + } + + PNS_LINE& PLine() + { + if ( !m_line_p.IsLinked ( ) ) + updateLine(m_line_p, m_p, m_net_p, m_via_p ); + return m_line_p; + } + + PNS_LINE& NLine() + { + if ( !m_line_n.IsLinked ( ) ) + updateLine(m_line_n, m_n, m_net_n, m_via_n ); + return m_line_n; + } + + PNS_DP_PRIMITIVE_PAIR EndingPrimitives(); + + double CoupledLength() const; + double TotalLength() const; + double CoupledLengthFactor () const; + double Skew () const; + + void CoupledSegmentPairs ( COUPLED_SEGMENTS_VEC& aPairs ) const; + + void Clear() + { + m_n.Clear(); + m_p.Clear(); + } + + void Append (const PNS_DIFF_PAIR& aOther ) + { + m_n.Append ( aOther.m_n ); + m_p.Append ( aOther.m_p ); + } + + bool Empty() const + { + return (m_n.SegmentCount() == 0) || (m_p.SegmentCount() == 0); + } + const SHAPE_LINE_CHAIN& CP() const { return m_p; } + const SHAPE_LINE_CHAIN& CN() const { return m_n; } + + bool BuildInitial ( PNS_DP_GATEWAY& aEntry, PNS_DP_GATEWAY& aTarget, bool aPrefDiagonal ); + bool CheckConnectionAngle ( const PNS_DIFF_PAIR &aOther, int allowedAngles ) const; + int CoupledLength ( const SEG& aP, const SEG& aN ) const; + + int64_t CoupledLength ( const SHAPE_LINE_CHAIN& aP, const SHAPE_LINE_CHAIN& aN ) const; + + const RANGED_NUM GapConstraint() const { + return m_gapConstraint; + } + +private: + + void updateLine( PNS_LINE &aLine, const SHAPE_LINE_CHAIN& aShape, int aNet, PNS_VIA& aVia ) + { + aLine.SetShape( aShape ); + aLine.SetWidth( m_width ); + aLine.SetNet(aNet); + aLine.SetLayer (Layers().Start()); + + if(m_hasVias) + aLine.AppendVia ( aVia ); + } + + SHAPE_LINE_CHAIN m_n, m_p; + PNS_LINE m_line_p, m_line_n; + PNS_VIA m_via_p, m_via_n; + + bool m_hasVias; + int m_net_p, m_net_n; + int m_width; + int m_gap; + int m_viaGap; + int m_maxUncoupledLength; + int m_chamferLimit; + RANGED_NUM m_gapConstraint; +}; + + +#endif diff --git a/pcbnew/router/pns_diff_pair_placer.cpp b/pcbnew/router/pns_diff_pair_placer.cpp new file mode 100644 index 0000000000..ff1d7f3f49 --- /dev/null +++ b/pcbnew/router/pns_diff_pair_placer.cpp @@ -0,0 +1,778 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include + +#include +#include +#include +#include + +#include "trace.h" + +#include "pns_node.h" +#include "pns_walkaround.h" +#include "pns_shove.h" +#include "pns_utils.h" +#include "pns_router.h" +#include "pns_diff_pair_placer.h" +#include "pns_solid.h" +#include "pns_topology.h" + +using boost::optional; + +PNS_DIFF_PAIR_PLACER::PNS_DIFF_PAIR_PLACER( PNS_ROUTER* aRouter ) : + PNS_PLACEMENT_ALGO ( aRouter ) +{ + m_initialDiagonal = false; + m_startDiagonal = false; + m_world = NULL; + m_shove = NULL; + m_currentNode = NULL; + m_idle = true; +} + +PNS_DIFF_PAIR_PLACER::~PNS_DIFF_PAIR_PLACER() +{ + if( m_shove ) + delete m_shove; +} + + +void PNS_DIFF_PAIR_PLACER::setWorld ( PNS_NODE* aWorld ) +{ + m_world = aWorld; +} + +const PNS_VIA PNS_DIFF_PAIR_PLACER::makeVia ( const VECTOR2I& aP, int aNet ) +{ + const PNS_LAYERSET layers( m_sizes.GetLayerTop(), m_sizes.GetLayerBottom() ); + + PNS_VIA v( aP, layers, m_sizes.ViaDiameter(), m_sizes.ViaDrill(), -1, m_sizes.ViaType() ); + v.SetNet (aNet); + return v; +} + +void PNS_DIFF_PAIR_PLACER::SetOrthoMode ( bool aOrthoMode ) +{ + m_orthoMode = aOrthoMode; + if(!m_idle) + Move ( m_currentEnd, NULL ); +} + +bool PNS_DIFF_PAIR_PLACER::ToggleVia( bool aEnabled ) +{ + m_placingVia = aEnabled; + if(!m_idle) + Move ( m_currentEnd, NULL ); + + return true; +} + +bool PNS_DIFF_PAIR_PLACER::rhMarkObstacles( const VECTOR2I& aP ) +{ + if( !routeHead ( aP ) ) + return false; + + bool collP = m_currentNode->CheckColliding( &m_currentTrace.PLine() ); + bool collN = m_currentNode->CheckColliding( &m_currentTrace.NLine() ); + + m_fitOk = !(collP || collN); + return m_fitOk; +} + +bool PNS_DIFF_PAIR_PLACER::propagateDpHeadForces ( const VECTOR2I& aP, VECTOR2I& aNewP ) +{ + PNS_VIA virtHead = makeVia ( aP, -1 ); + + if ( m_placingVia ) + virtHead.SetDiameter ( viaGap() + 2 * virtHead.Diameter() ); + else + { + virtHead.SetLayer ( m_currentLayer ); + virtHead.SetDiameter ( m_sizes.DiffPairGap() + 2 * m_sizes.TrackWidth() ); + } + + VECTOR2I lead(0, 0);// = aP - m_currentStart ; + VECTOR2I force; + bool solidsOnly = true; + + + if(m_currentMode == RM_MarkObstacles ) + { + aNewP = aP; + return true; + } else if (m_currentMode == RM_Walkaround ) + { + solidsOnly = false; + } + + // fixme: I'm too lazy to do it well. Circular approximaton will do for the moment. + if( virtHead.PushoutForce( m_currentNode, lead, force, solidsOnly, 40 ) ) + { + aNewP = aP + force; + return true; + } + + return false; +} + + +bool PNS_DIFF_PAIR_PLACER::attemptWalk ( PNS_NODE *aNode, PNS_DIFF_PAIR *aCurrent, PNS_DIFF_PAIR& aWalk, bool aPFirst, bool aWindCw, bool aSolidsOnly ) +{ + PNS_WALKAROUND walkaround( aNode, Router() ); + PNS_WALKAROUND::WALKAROUND_STATUS wf1; + + Router()->GetClearanceFunc()->OverrideClearance ( true, aCurrent->NetP(), aCurrent->NetN(), aCurrent->Gap() - 20 ); + + walkaround.SetSolidsOnly( aSolidsOnly ); + walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() ); + + PNS_SHOVE shove(aNode, Router()); + PNS_LINE walkP, walkN; + + aWalk = *aCurrent; + + int iter = 0; + + PNS_DIFF_PAIR cur (*aCurrent); + + bool currentIsP = aPFirst; + + int mask = aSolidsOnly ? PNS_ITEM::SOLID : PNS_ITEM::ANY; + + //Router()->DisplayDebugLine( aCurrent->CP(), 4, 10000 ); + //Router()->DisplayDebugLine( aCurrent->CN(), 5, 10000 ); + + do { + PNS_LINE preWalk = (currentIsP ? cur.PLine() : cur.NLine() ); + PNS_LINE preShove = (currentIsP ? cur.NLine() : cur.PLine() ); + PNS_LINE postWalk; + + if (!aNode->CheckColliding ( &preWalk, mask ) ) + { + + currentIsP = !currentIsP; + + if (!aNode->CheckColliding ( &preShove, mask ) ) + break; + else + continue; + } + + wf1 = walkaround.Route( preWalk, postWalk, false ); + + if(wf1 != PNS_WALKAROUND::DONE) + return false; + + PNS_LINE postShove ( preShove ); + + shove.ForceClearance(true, cur.Gap() - 12); + + PNS_SHOVE::SHOVE_STATUS sh1; + + sh1 = shove.ProcessSingleLine( &postWalk, &preShove, &postShove ); + + if(sh1 != PNS_SHOVE::SH_OK) + return false; + + postWalk.Line().Simplify(); + postShove.Line().Simplify(); + + + cur.SetShape ( postWalk.CLine(), postShove.CLine(), !currentIsP ); + + currentIsP = !currentIsP; + + if (!aNode->CheckColliding ( &postShove, mask ) ) + break; + + + iter++; + } while (iter < 3); + + if(iter == 3) + return false; + + + + aWalk.SetShape(cur.CP(), cur.CN() ); + Router()->GetClearanceFunc()->OverrideClearance ( false ); + + return true; +} + +bool PNS_DIFF_PAIR_PLACER::tryWalkDp ( PNS_NODE* aNode, PNS_DIFF_PAIR &aPair, bool aSolidsOnly ) +{ + PNS_DIFF_PAIR best; + double bestScore = 100000000000000.0; + + for(int attempt = 0; attempt <= 1; attempt ++) + { + PNS_DIFF_PAIR p; + PNS_NODE *tmp = m_currentNode->Branch(); + + bool pfirst = attempt % 2 ? true : false; + bool wind_cw = attempt / 2 ? true : false; + + if ( attemptWalk ( tmp, &aPair, p, pfirst, wind_cw, aSolidsOnly ) ) + { + // double len = p.TotalLength(); + double cl = p.CoupledLength(); + double skew = p.Skew(); + + double score = cl + fabs(skew) * 3.0; + + if(score < bestScore) + { + bestScore = score; + best = p; + } + } + + delete tmp; + } + + if(bestScore > 0.0) + { + PNS_OPTIMIZER optimizer( m_currentNode ); + + aPair.SetShape ( best ); + + optimizer.Optimize ( &aPair ); + return true; + } + + return false; +} + +bool PNS_DIFF_PAIR_PLACER::rhWalkOnly( const VECTOR2I& aP ) +{ + if ( !routeHead ( aP ) ) + return false; + + m_fitOk = tryWalkDp ( m_currentNode, m_currentTrace, false ); + + return m_fitOk; +} + + +bool PNS_DIFF_PAIR_PLACER::route ( const VECTOR2I& aP ) +{ + switch( m_currentMode ) + { + case RM_MarkObstacles: + return rhMarkObstacles( aP ); + case RM_Walkaround: + return rhWalkOnly ( aP ); + case RM_Shove: + return rhShoveOnly ( aP ); + default: + break; + } + + return false; +} + +bool PNS_DIFF_PAIR_PLACER::rhShoveOnly ( const VECTOR2I& aP ) +{ + m_currentNode = m_shove->CurrentNode(); + + bool ok = routeHead ( aP ); + + m_fitOk = false; + + if(!ok) + return false; + + if (!tryWalkDp ( m_currentNode, m_currentTrace, true ) ) + return false; + + PNS_LINE pLine ( m_currentTrace.PLine() ); + PNS_LINE nLine ( m_currentTrace.NLine() ); + PNS_ITEMSET head; + + head.Add ( &pLine ); + head.Add ( &nLine ); + + PNS_SHOVE::SHOVE_STATUS status = m_shove->ShoveMultiLines( head ); + + m_currentNode = m_shove->CurrentNode(); + + if( status == PNS_SHOVE::SH_OK ) + { + m_currentNode = m_shove->CurrentNode(); + + if( !m_currentNode->CheckColliding ( &m_currentTrace.PLine() ) && + !m_currentNode->CheckColliding ( &m_currentTrace.NLine() ) ) + { + m_fitOk = true; + } + } + + return m_fitOk; +} + + + +const PNS_ITEMSET PNS_DIFF_PAIR_PLACER::Traces() +{ + PNS_ITEMSET t; + + t.Add( const_cast ( &m_currentTrace.PLine( ) ) ); + t.Add( const_cast ( &m_currentTrace.NLine( ) ) ); + + return t; +} + + +void PNS_DIFF_PAIR_PLACER::FlipPosture() +{ + m_startDiagonal = !m_startDiagonal; + + if(!m_idle) + Move ( m_currentEnd, NULL ); +} + + +PNS_NODE* PNS_DIFF_PAIR_PLACER::CurrentNode( bool aLoopsRemoved ) const +{ + if( m_lastNode ) + return m_lastNode; + + return m_currentNode; +} + + +bool PNS_DIFF_PAIR_PLACER::SetLayer( int aLayer ) +{ + if( m_idle ) + { + m_currentLayer = aLayer; + return true; + } else if( m_chainedPlacement ) + return false; + else if( !m_prevPair ) + return false; + else if( m_prevPair->PrimP() || ( m_prevPair->PrimP()->OfKind( PNS_ITEM::VIA ) && m_prevPair->PrimP()->Layers().Overlaps( aLayer ) ) ) { + m_currentLayer = aLayer; + m_start = *m_prevPair; + initPlacement ( false ); + Move ( m_currentEnd, NULL ); + return true; + } + + return false; +} + +int PNS_DIFF_PAIR_PLACER::matchDpSuffix ( wxString aNetName, wxString& aComplementNet, wxString& aBaseDpName ) +{ + int rv = 0; + if (aNetName.EndsWith("+")) + { + aComplementNet = "-"; + rv = 1; + } else if (aNetName.EndsWith("_P")) + { + aComplementNet = "_N"; + rv = 1; + } else if (aNetName.EndsWith("-")) + { + aComplementNet = "+"; + rv = -1; + } else if (aNetName.EndsWith("_N")) + { + aComplementNet = "_P"; + rv = -1; + } + + if (rv != 0) { + aBaseDpName = aNetName.Left ( aNetName.Length() - aComplementNet.Length() ); + } + + return rv; +} + +OPT_VECTOR2I PNS_DIFF_PAIR_PLACER::getDanglingAnchor ( PNS_NODE *aNode, PNS_ITEM *aItem ) +{ + switch(aItem->Kind()) + { + case PNS_ITEM::VIA: + case PNS_ITEM::SOLID: + return aItem->Anchor(0); + + case PNS_ITEM::SEGMENT: + { + PNS_SEGMENT *s =static_cast ( aItem ); + + PNS_JOINT *jA = aNode->FindJoint( s->Seg().A, s ); + PNS_JOINT *jB = aNode->FindJoint( s->Seg().B, s ); + + if(jA->LinkCount() == 1) + return s->Seg().A; + else if (jB->LinkCount() == 1) + return s->Seg().B; + else + return OPT_VECTOR2I(); + } + default: + return OPT_VECTOR2I(); + break; + } +} + +bool PNS_DIFF_PAIR_PLACER::findDpPrimitivePair ( const VECTOR2I& aP, PNS_ITEM *aItem, PNS_DP_PRIMITIVE_PAIR& aPair ) +{ + + if(!aItem || !aItem->Parent() || !aItem->Parent()->GetNet() ) + return false; + + wxString netNameP = aItem->Parent()->GetNet()->GetNetname(); + wxString netNameN, netNameBase; + + + BOARD *brd = Router()->GetBoard(); + PNS_ITEM *primRef = NULL, *primP = NULL, *primN = NULL; + + // printf("Current %p\n", m_currentNode); + int refNet; + + wxString suffix; + + int r = matchDpSuffix ( netNameP, suffix, netNameBase ); + + if(r == 0) + return false; + else if(r == 1) + { + primRef = primP = static_cast (aItem); + netNameN = netNameBase + suffix; + } else { + primRef = primN = static_cast (aItem); + netNameN = netNameP; + netNameP = netNameBase + suffix; + } + + int netP = brd->FindNet ( netNameP )->GetNet(); + int netN = brd->FindNet ( netNameN )->GetNet(); + + if ( primP ) + refNet = netN; + else + refNet = netP; + +// printf("Net: P: %s N: %s\n", (const char *)(netNameP.c_str()), (const char *)(netNameN.c_str())); + + std::set items; + + OPT_VECTOR2I refAnchor = getDanglingAnchor ( m_currentNode, primRef ); + + if(!refAnchor) + return false; + + m_currentNode->AllItemsInNet( refNet, items ); + double bestDist = std::numeric_limits::max(); + bool found = false; + + BOOST_FOREACH (PNS_ITEM *item, items) + { + if ( item->Kind() == aItem->Kind() ) + { + OPT_VECTOR2I anchor = getDanglingAnchor ( m_currentNode, item ); + if(!anchor) + continue; + + double dist = (*anchor - *refAnchor).EuclideanNorm(); + + if (dist < bestDist) + { + found = true; + bestDist = dist; + + if (refNet == netP) + { + aPair = PNS_DP_PRIMITIVE_PAIR ( item, primRef ); + aPair.SetAnchors ( *anchor, *refAnchor ); + } else { + aPair = PNS_DP_PRIMITIVE_PAIR ( primRef, item ); + aPair.SetAnchors ( *refAnchor, *anchor ); + } + } + } + } + + return found; +} + + +int PNS_DIFF_PAIR_PLACER::viaGap() const +{ + return m_sizes.DiffPairViaGap(); +} + +int PNS_DIFF_PAIR_PLACER::gap() const +{ + return m_sizes.DiffPairGap() + m_sizes.DiffPairWidth(); +} + +bool PNS_DIFF_PAIR_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) +{ + VECTOR2I p( aP ); + + bool split; + + if( Router()->SnappingEnabled() ) + p = Router()->SnapToItem( aStartItem, aP, split ); + + if(!aStartItem) + { + Router()->SetFailureReason( _( "Can't start a differential pair " + " in the middle of nowhere." ) ); + return false; + } + + PNS_DP_PRIMITIVE_PAIR start; + + m_currentNode = Router()->GetWorld(); + + if (!findDpPrimitivePair(aP, aStartItem, m_start ) ) + { + Router()->SetFailureReason( _( "Unable to find complementary differential pair " + "net. Make sure the names of the nets belonging " + "to a differential pair end with either _N/_P or +/-." ) ); + return false; + } + + m_netP = m_start.PrimP()->Net(); + m_netN = m_start.PrimN()->Net(); + + m_currentStart = p; + m_currentEnd = p; + m_placingVia = false; + m_chainedPlacement = false; + + initPlacement( false ); + return true; +} + +void PNS_DIFF_PAIR_PLACER::initPlacement( bool aSplitSeg ) +{ + m_idle = false; + m_orthoMode = false; + m_currentEndItem = NULL; + m_startDiagonal = m_initialDiagonal; + + PNS_NODE* world = Router()->GetWorld(); + + world->KillChildren(); + PNS_NODE* rootNode = world->Branch(); + + setWorld( rootNode ); + + TRACE( 1, "world %p, intitial-direction %s layer %d\n", + m_world % m_direction.Format().c_str() % aLayer ); + + m_lastNode = NULL; + m_currentNode = rootNode; + m_currentMode = Settings().Mode(); + + if( m_shove ) + delete m_shove; + + m_shove = NULL; + + if( m_currentMode == RM_Shove || m_currentMode == RM_Smart ) + { + m_shove = new PNS_SHOVE( m_currentNode, Router() ); + } +} + +bool PNS_DIFF_PAIR_PLACER::routeHead( const VECTOR2I& aP ) +{ + m_fitOk = false; + + PNS_DP_GATEWAYS gwsEntry ( gap() ); + PNS_DP_GATEWAYS gwsTarget ( gap() ); + + + if(!m_prevPair) + m_prevPair = m_start; + + gwsEntry.BuildFromPrimitivePair ( *m_prevPair, m_startDiagonal ); + + PNS_DP_PRIMITIVE_PAIR target; + + if (findDpPrimitivePair ( aP, m_currentEndItem, target )) + { + gwsTarget.BuildFromPrimitivePair( target, m_startDiagonal ); + m_snapOnTarget = true; + } else { + VECTOR2I fp; + + if( !propagateDpHeadForces ( aP, fp ) ) + return false; + + gwsTarget.SetFitVias ( m_placingVia, m_sizes.ViaDiameter(), viaGap() ); + gwsTarget.BuildForCursor ( fp ); + gwsTarget.BuildOrthoProjections ( gwsEntry, fp, m_orthoMode ? 200 : -200 ); + m_snapOnTarget = false; + } + + m_currentTrace = PNS_DIFF_PAIR(); + m_currentTrace.SetGap ( gap() ); + m_currentTrace.SetLayer( m_currentLayer ); + + + if ( gwsEntry.FitGateways(gwsEntry, gwsTarget, m_startDiagonal, m_currentTrace ) ) + { + m_currentTrace.SetNets ( m_netP, m_netN ); + m_currentTrace.SetWidth ( m_sizes.DiffPairWidth() ); + m_currentTrace.SetGap ( m_sizes.DiffPairGap() ); + + if(m_placingVia) + { + m_currentTrace.AppendVias ( makeVia ( m_currentTrace.CP().CPoint(-1), m_netP ), + makeVia ( m_currentTrace.CN().CPoint(-1), m_netN ) ); + } + return true; + } + + return false; + +} + +bool PNS_DIFF_PAIR_PLACER::Move(const VECTOR2I& aP , PNS_ITEM* aEndItem ) +{ + m_currentEndItem = aEndItem; + m_fitOk = false; + + delete m_lastNode; + m_lastNode = NULL; + + if ( !route( aP ) ) + return false; + + PNS_NODE* latestNode = m_currentNode; + m_lastNode = latestNode->Branch(); + + assert (m_lastNode != NULL); + m_currentEnd = aP; + + updateLeadingRatLine(); + + return true; +} + +void PNS_DIFF_PAIR_PLACER::UpdateSizes( const PNS_SIZES_SETTINGS& aSizes ) +{ + m_sizes = aSizes; + if( !m_idle ) + { + initPlacement ( ); + Move ( m_currentEnd, NULL ); + } +} + +bool PNS_DIFF_PAIR_PLACER::FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ) +{ + if (!m_fitOk) + return false; + + if (m_currentTrace.CP().SegmentCount() < 1 || + m_currentTrace.CN().SegmentCount() < 1 ) + return false; + + if( m_currentTrace.CP().SegmentCount() > 1) + m_initialDiagonal = !DIRECTION_45( m_currentTrace.CP().CSegment(-2) ).IsDiagonal(); + + PNS_TOPOLOGY topo ( m_lastNode ); + + if( !m_snapOnTarget && !m_currentTrace.EndsWithVias() ) + { + SHAPE_LINE_CHAIN newP ( m_currentTrace.CP() ); + SHAPE_LINE_CHAIN newN ( m_currentTrace.CN() ); + + if( newP.SegmentCount() > 1 && newN.SegmentCount() > 1) + { + newP.Remove(-1, -1); + newN.Remove(-1, -1); + } + + m_currentTrace.SetShape ( newP, newN ); + } + + if( m_currentTrace.EndsWithVias() ) + { + m_lastNode->Add( m_currentTrace.PLine().Via().Clone() ); + m_lastNode->Add( m_currentTrace.NLine().Via().Clone() ); + m_chainedPlacement = false; + } else + m_chainedPlacement = !m_snapOnTarget; + + PNS_LINE lineP ( m_currentTrace.PLine() ); + PNS_LINE lineN ( m_currentTrace.NLine() ); + + m_lastNode->Add ( &lineP ); + m_lastNode->Add ( &lineN ); + + topo.SimplifyLine( &lineP ); + topo.SimplifyLine( &lineN ); + + m_prevPair = m_currentTrace.EndingPrimitives(); + + Router()->CommitRouting( m_lastNode ); + + m_lastNode = NULL; + m_placingVia = false; + + if(m_snapOnTarget) + { + m_idle = true; + return true; + } else { + initPlacement(); + return false; + } +} + + +void PNS_DIFF_PAIR_PLACER::GetModifiedNets( std::vector &aNets ) const +{ + aNets.push_back( m_netP ); + aNets.push_back( m_netN ); +} + +void PNS_DIFF_PAIR_PLACER::updateLeadingRatLine() +{ + SHAPE_LINE_CHAIN ratLineN, ratLineP; + PNS_TOPOLOGY topo ( m_lastNode ); + + if( topo.LeadingRatLine ( &m_currentTrace.PLine(), ratLineP )) + { + Router()->DisplayDebugLine( ratLineP, 1, 10000 ); + } + + if( topo.LeadingRatLine ( &m_currentTrace.NLine(), ratLineN )) + { + Router()->DisplayDebugLine( ratLineN, 3, 10000 ); + } + +} diff --git a/pcbnew/router/pns_diff_pair_placer.h b/pcbnew/router/pns_diff_pair_placer.h new file mode 100644 index 0000000000..87b8001484 --- /dev/null +++ b/pcbnew/router/pns_diff_pair_placer.h @@ -0,0 +1,303 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2014 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_DIFF_PLACER_H +#define __PNS_DIFF_PLACER_H + +#include + +#include +#include + +#include "pns_sizes_settings.h" +#include "pns_node.h" +#include "pns_via.h" +#include "pns_line.h" +#include "pns_algo_base.h" +#include "pns_diff_pair.h" + +#include "pns_placement_algo.h" + +class PNS_ROUTER; +class PNS_SHOVE; +class PNS_OPTIMIZER; +class PNS_ROUTER_BASE; +class PNS_VIA; +class PNS_SIZES_SETTINGS; + + +/** + * Class PNS_LINE_PLACER + * + * Single track placement algorithm. Interactively routes a track. + * Applies shove and walkaround algorithms when needed. + */ + +class PNS_DIFF_PAIR_PLACER : public PNS_PLACEMENT_ALGO +{ +public: + PNS_DIFF_PAIR_PLACER( PNS_ROUTER* aRouter ); + ~PNS_DIFF_PAIR_PLACER(); + + /** + * Function Start() + * + * Starts routing a single track at point aP, taking item aStartItem as anchor + * (unless NULL). + */ + bool Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ); + + /** + * Function Move() + * + * Moves the end of the currently routed trace to the point aP, taking + * aEndItem as anchor (if not NULL). + * (unless NULL). + */ + bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ); + + /** + * Function FixRoute() + * + * Commits the currently routed track to the parent node, taking + * aP as the final end point and aEndItem as the final anchor (if provided). + * @return true, if route has been commited. May return false if the routing + * result is violating design rules - in such case, the track is only committed + * if Settings.CanViolateDRC() is on. + */ + bool FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ); + + /** + * Function ToggleVia() + * + * Enables/disables a via at the end of currently routed trace. + */ + bool ToggleVia( bool aEnabled ); + + /** + * Function SetLayer() + * + * Sets the current routing layer. + */ + bool SetLayer( int aLayer ); + + /** + * Function Traces() + * + * Returns the complete routed line, as a single-member PNS_ITEMSET. + */ + const PNS_ITEMSET Traces(); + + /** + * Function CurrentEnd() + * + * Returns the current end of the line being placed. It may not be equal + * to the cursor position due to collisions. + */ + const VECTOR2I& CurrentEnd() const + { + return m_currentEnd; + } + + /** + * Function CurrentNet() + * + * Returns the net code of currently routed track. + */ + int CurrentNet() const + { + return m_currentNet; + } + + /** + * Function CurrentLayer() + * + * Returns the layer of currently routed track. + */ + int CurrentLayer() const + { + return m_currentLayer; + } + + /** + * Function CurrentNode() + * + * Returns the most recent world state. + */ + PNS_NODE* CurrentNode( bool aLoopsRemoved = false ) const; + + /** + * Function FlipPosture() + * + * Toggles the current posture (straight/diagonal) of the trace head. + */ + void FlipPosture(); + + /** + * Function UpdateSizes() + * + * Performs on-the-fly update of the width, via diameter & drill size from + * a settings class. Used to dynamically change these parameters as + * the track is routed. + */ + void UpdateSizes( const PNS_SIZES_SETTINGS& aSizes ); + + bool IsPlacingVia() const { return m_placingVia; } + + void SetOrthoMode ( bool aOrthoMode ); + + void GetModifiedNets( std::vector &aNets ) const; +private: + + int viaGap() const; + int gap() const; + + /** + * Function route() + * + * Re-routes the current track to point aP. Returns true, when routing has + * completed successfully (i.e. the trace end has reached point aP), and false + * if the trace was stuck somewhere on the way. May call routeStep() + * repetitively due to mouse smoothing. + * @param aP ending point of current route. + * @return true, if the routing is complete. + */ + bool route( const VECTOR2I& aP ); + + /** + * Function updateLeadingRatLine() + * + * Draws the "leading" ratsnest line, which connects the end of currently + * routed track and the nearest yet unrouted item. If the routing for + * current net is complete, draws nothing. + */ + void updateLeadingRatLine(); + + /** + * Function setWorld() + * + * Sets the board to route. + */ + void setWorld( PNS_NODE* aWorld ); + + /** + * Function startPlacement() + * + * Initializes placement of a new line with given parameters. + */ + void initPlacement( bool aSplitSeg = false ); + + /** + * Function setInitialDirection() + * + * Sets preferred direction of the very first track segment to be laid. + * Used by posture switching mechanism. + */ + void setInitialDirection( const DIRECTION_45& aDirection ); + + + bool routeHead( const VECTOR2I& aP ); + bool tryWalkDp ( PNS_NODE* aNode, PNS_DIFF_PAIR &aPair, bool aSolidsOnly ); + + ///> route step, walkaround mode + bool rhWalkOnly( const VECTOR2I& aP ); + + ///> route step, shove mode + bool rhShoveOnly ( const VECTOR2I& aP ); + + ///> route step, mark obstacles mode + bool rhMarkObstacles( const VECTOR2I& aP ); + + const PNS_VIA makeVia ( const VECTOR2I& aP, int aNet ); + + bool findDpPrimitivePair ( const VECTOR2I& aP, PNS_ITEM *aItem, PNS_DP_PRIMITIVE_PAIR& aPair ); + OPT_VECTOR2I getDanglingAnchor ( PNS_NODE *aNode, PNS_ITEM *aItem ); + int matchDpSuffix ( wxString aNetName, wxString& aComplementNet, wxString& aBaseDpName ); + bool attemptWalk ( PNS_NODE *aNode, PNS_DIFF_PAIR *aCurrent, PNS_DIFF_PAIR& aWalk, bool aPFirst, bool aWindCw, bool aSolidsOnly ); + bool propagateDpHeadForces ( const VECTOR2I& aP, VECTOR2I& aNewP ); + + enum State { + RT_START = 0, + RT_ROUTE = 1, + RT_FINISH = 2 + }; + + State m_state; + + bool m_chainedPlacement; + bool m_initialDiagonal; + bool m_startDiagonal; + bool m_fitOk; + + int m_netP, m_netN; + + PNS_DP_PRIMITIVE_PAIR m_start; + boost::optional m_prevPair; + + + ///> current algorithm iteration + int m_iteration; + + ///> pointer to world to search colliding items + PNS_NODE* m_world; + + ///> current routing start point (end of tail, beginning of head) + VECTOR2I m_p_start; + + ///> The shove engine + PNS_SHOVE* m_shove; + + ///> Current world state + PNS_NODE* m_currentNode; + + ///> Postprocessed world state (including marked collisions & removed loops) + PNS_NODE* m_lastNode; + + PNS_SIZES_SETTINGS m_sizes; + + ///> Are we placing a via? + bool m_placingVia; + + ///> current via diameter + int m_viaDiameter; + + ///> current via drill + int m_viaDrill; + + ///> current track width + int m_currentWidth; + + int m_currentNet; + int m_currentLayer; + + bool m_startsOnVia; + bool m_orthoMode; + bool m_snapOnTarget; + + VECTOR2I m_currentEnd, m_currentStart; + PNS_DIFF_PAIR m_currentTrace; + + PNS_ITEM *m_currentEndItem; + PNS_MODE m_currentMode; + + bool m_idle; +}; + +#endif // __PNS_LINE_PLACER_H diff --git a/pcbnew/router/pns_dp_meander_placer.cpp b/pcbnew/router/pns_dp_meander_placer.cpp new file mode 100644 index 0000000000..8f36dde975 --- /dev/null +++ b/pcbnew/router/pns_dp_meander_placer.cpp @@ -0,0 +1,459 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2014 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include + +#include // God forgive me doing this... +#include + +#include "trace.h" + +#include "pns_node.h" +#include "pns_itemset.h" +#include "pns_topology.h" +#include "pns_dp_meander_placer.h" +#include "pns_diff_pair.h" +#include "pns_router.h" +#include "pns_utils.h" + + +using boost::optional; + + + +PNS_DP_MEANDER_PLACER::PNS_DP_MEANDER_PLACER( PNS_ROUTER* aRouter ) : + PNS_MEANDER_PLACER_BASE ( aRouter ) +{ + m_world = NULL; + m_currentNode = NULL; +} + + +PNS_DP_MEANDER_PLACER::~PNS_DP_MEANDER_PLACER() +{ + +} + +const PNS_LINE PNS_DP_MEANDER_PLACER::Trace() const +{ + return m_currentTraceP; +} + +PNS_NODE* PNS_DP_MEANDER_PLACER::CurrentNode( bool aLoopsRemoved ) const +{ + if(!m_currentNode) + return m_world; + return m_currentNode; +} + +bool PNS_DP_MEANDER_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) +{ + VECTOR2I p; + if(!aStartItem || !aStartItem->OfKind ( PNS_ITEM::SEGMENT )) + { + Router()->SetFailureReason( _("Please select a track whose length you want to tune.") ); + return false; + } + + m_initialSegment = static_cast(aStartItem); + + p = m_initialSegment->Seg().NearestPoint( aP ); + + m_currentNode=NULL; + m_currentStart = p; + + m_world = Router()->GetWorld()->Branch(); + + PNS_TOPOLOGY topo ( m_world ); + + if( !topo.AssembleDiffPair ( m_initialSegment, m_originPair ) ) + { + Router()->SetFailureReason( _( "Unable to find complementary differential pair " + "net for length tuning. Make sure the names of the nets belonging " + "to a differential pair end with either _N/_P or +/-." ) ); + return false; + } + + + m_originPair.SetGap ( Router()->Sizes().DiffPairGap() ); + + if( !m_originPair.PLine().SegmentCount() || + !m_originPair.NLine().SegmentCount() ) + return false; + + m_tunedPathP = topo.AssembleTrivialPath ( m_originPair.PLine().GetLink(0) ); + m_tunedPathN = topo.AssembleTrivialPath ( m_originPair.NLine().GetLink(0) ); + + m_world->Remove ( m_originPair.PLine() ); + m_world->Remove ( m_originPair.NLine() ); + + m_currentWidth = m_originPair.Width(); + + return true; +} + + +void PNS_DP_MEANDER_PLACER::release() +{ + #if 0 + BOOST_FOREACH(PNS_MEANDER *m, m_meanders) + { + delete m; + } + + m_meanders.clear(); + #endif +} + +int PNS_DP_MEANDER_PLACER::origPathLength () const +{ + int totalP = 0; + int totalN = 0; + + BOOST_FOREACH ( const PNS_ITEM *item, m_tunedPathP.CItems() ) + { + if ( const PNS_LINE *l = dyn_cast (item) ) + totalP += l->CLine( ).Length( ); + + } + BOOST_FOREACH ( const PNS_ITEM *item, m_tunedPathN.CItems() ) + { + if ( const PNS_LINE *l = dyn_cast (item) ) + totalN += l->CLine( ).Length( ); + + } + + return std::max(totalP, totalN); +} + +const SEG PNS_DP_MEANDER_PLACER::baselineSegment ( const PNS_DIFF_PAIR::COUPLED_SEGMENTS& aCoupledSegs ) +{ + const VECTOR2I a( ( aCoupledSegs.coupledP.A + aCoupledSegs.coupledN.A ) / 2 ); + const VECTOR2I b( ( aCoupledSegs.coupledP.B + aCoupledSegs.coupledN.B ) / 2 ); + + return SEG (a, b); +} + + +#if 0 +PNS_MEANDER_PLACER_BASE::TUNING_STATUS PNS_DP_MEANDER_PLACER::tuneLineLength ( PNS_MEANDERED_LINE& aTuned, int aElongation ) +{ + int remaining = aElongation; + bool finished = false; + + BOOST_FOREACH(PNS_MEANDER_SHAPE *m, aTuned.Meanders()) + { + + if(m->Type() != MT_CORNER ) + { + + if(remaining >= 0) + remaining -= m->MaxTunableLength() - m->BaselineLength(); + + if(remaining < 0) + { + if(!finished) + { + PNS_MEANDER_TYPE newType; + + if ( m->Type() == MT_START || m->Type() == MT_SINGLE) + newType = MT_SINGLE; + else + newType = MT_FINISH; + + m->SetType ( newType ); + m->Recalculate( ); + + finished = true; + } else { + m->MakeEmpty(); + } + } + } + } + + remaining = aElongation; + int meanderCount = 0; + + BOOST_FOREACH(PNS_MEANDER_SHAPE *m, aTuned.Meanders()) + { + if( m->Type() != MT_CORNER && m->Type() != MT_EMPTY ) + { + if(remaining >= 0) + { + remaining -= m->MaxTunableLength() - m->BaselineLength(); + meanderCount ++; + } + } + } + + int balance = 0; + + + if( meanderCount ) + balance = -remaining / meanderCount; + + if (balance >= 0) + { + BOOST_FOREACH(PNS_MEANDER_SHAPE *m, aTuned.Meanders()) + { + if(m->Type() != MT_CORNER && m->Type() != MT_EMPTY) + { +// int pre = m->MaxTunableLength(); + m->Resize ( std::max( m->Amplitude() - balance / 2, m_settings.m_minAmplitude ) ); + } + } + + } + return TUNED; +} +#endif + + + +bool pairOrientation( const PNS_DIFF_PAIR::COUPLED_SEGMENTS& aPair ) +{ + VECTOR2I midp = ( aPair.coupledP.A + aPair.coupledN.A ) / 2; + + //DrawDebugPoint (midp, 6); + + return aPair.coupledP.Side ( midp ) > 0; +} + +bool PNS_DP_MEANDER_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ) +{ + + + +// return false; + + if(m_currentNode) + delete m_currentNode; + + m_currentNode = m_world->Branch(); + + SHAPE_LINE_CHAIN preP, tunedP, postP; + SHAPE_LINE_CHAIN preN, tunedN, postN; + + cutTunedLine ( m_originPair.CP(), m_currentStart, aP, preP, tunedP, postP ); + cutTunedLine ( m_originPair.CN(), m_currentStart, aP, preN, tunedN, postN ); + + PNS_DIFF_PAIR tuned ( m_originPair ); + + tuned.SetShape ( tunedP, tunedN ); + + m_coupledSegments.clear(); + + tuned.CoupledSegmentPairs ( m_coupledSegments ); + + if (m_coupledSegments.size() == 0) + return false; + + //Router()->DisplayDebugLine ( tuned.CP(), 5, 20000 ); + //Router()->DisplayDebugLine ( tuned.CN(), 4, 20000 ); + + //Router()->DisplayDebugLine ( m_originPair.CP(), 5, 20000 ); + //Router()->DisplayDebugLine ( m_originPair.CN(), 4, 20000 ); + + m_result = PNS_MEANDERED_LINE (this, true ); + m_result.SetWidth ( tuned.Width() ); + + int offset = ( tuned.Gap() + tuned.Width() ) / 2; + + if ( !pairOrientation ( m_coupledSegments[0] ) ) + offset *= -1; + + + m_result.SetBaselineOffset ( offset ); + + + BOOST_FOREACH ( const PNS_ITEM *item, m_tunedPathP.CItems() ) + { + if ( const PNS_LINE *l = dyn_cast (item) ) + Router()->DisplayDebugLine ( l->CLine(), 5, 10000 ); + + } + + BOOST_FOREACH ( const PNS_ITEM *item, m_tunedPathN.CItems() ) + { + if ( const PNS_LINE *l = dyn_cast (item) ) + Router()->DisplayDebugLine ( l->CLine(), 5, 10000 ); + } + + + BOOST_FOREACH ( const PNS_DIFF_PAIR::COUPLED_SEGMENTS& sp, m_coupledSegments ) + { + SEG base = baselineSegment ( sp ); + + // DrawDebugSeg ( base, 3 ); + + m_result.AddCorner ( sp.parentP.A, sp.parentN.A ); + m_result.MeanderSegment ( base ); + m_result.AddCorner ( sp.parentP.B, sp.parentN.B ); + } + + int dpLen = origPathLength(); + + m_lastStatus = TUNED; + + if (dpLen - m_settings.m_targetLength > m_settings.m_lengthTollerance) + { + m_lastStatus = TOO_LONG; + m_lastLength = dpLen; + } else { + + m_lastLength = dpLen - std::max ( tunedP.Length(), tunedN.Length() ); + tuneLineLength(m_result, m_settings.m_targetLength - dpLen ); + } + + if (m_lastStatus != TOO_LONG) + { + tunedP.Clear(); + tunedN.Clear(); + + BOOST_FOREACH ( PNS_MEANDER_SHAPE *m, m_result.Meanders() ) + { + if( m->Type() != MT_EMPTY ) + { + tunedP.Append ( m->CLine(0) ); + tunedN.Append ( m->CLine(1) ); + } + } + + m_lastLength += std::max ( tunedP.Length(), tunedN.Length() ); + + int comp = compareWithTollerance( m_lastLength - m_settings.m_targetLength, 0, m_settings.m_lengthTollerance ); + + if( comp > 0 ) + m_lastStatus = TOO_LONG; + else if( comp < 0 ) + m_lastStatus = TOO_SHORT; + else + m_lastStatus = TUNED; + + } + + m_finalShapeP.Clear( ); + m_finalShapeP.Append( preP ); + m_finalShapeP.Append( tunedP ); + m_finalShapeP.Append( postP ); + m_finalShapeP.Simplify(); + + m_finalShapeN.Clear( ); + m_finalShapeN.Append( preN ); + m_finalShapeN.Append( tunedN ); + m_finalShapeN.Append( postN ); + m_finalShapeN.Simplify(); + + return true; +} + + +bool PNS_DP_MEANDER_PLACER::FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ) +{ + PNS_LINE lP ( m_originPair.PLine(), m_finalShapeP ); + PNS_LINE lN ( m_originPair.NLine(), m_finalShapeN ); + + m_currentNode->Add ( &lP ); + m_currentNode->Add ( &lN ); + + Router()->CommitRouting( m_currentNode ); + + return true; +} + +bool PNS_DP_MEANDER_PLACER::CheckFit ( PNS_MEANDER_SHAPE *aShape ) +{ + PNS_LINE l1 ( m_originPair.PLine(), aShape->CLine(0) ); + PNS_LINE l2 ( m_originPair.NLine(), aShape->CLine(1) ); + + if( m_currentNode->CheckColliding( &l1 ) ) + return false; + + if( m_currentNode->CheckColliding( &l2 ) ) + return false; + + int w = aShape->Width(); + int clearance = w + m_settings.m_spacing; + + return m_result.CheckSelfIntersections( aShape, clearance ); +} + + +const PNS_ITEMSET PNS_DP_MEANDER_PLACER::Traces() +{ + + m_currentTraceP = PNS_LINE ( m_originPair.PLine(), m_finalShapeP ); + m_currentTraceN = PNS_LINE ( m_originPair.NLine(), m_finalShapeN ); + + PNS_ITEMSET traces; + + traces.Add (&m_currentTraceP); + traces.Add (&m_currentTraceN); + + return traces; +} + +const VECTOR2I& PNS_DP_MEANDER_PLACER::CurrentEnd() const +{ + return m_currentEnd; +} + +int PNS_DP_MEANDER_PLACER::CurrentNet() const +{ + return m_initialSegment->Net(); +} + +int PNS_DP_MEANDER_PLACER::CurrentLayer() const +{ + return m_initialSegment->Layers().Start(); +} + +const wxString PNS_DP_MEANDER_PLACER::TuningInfo() const +{ + wxString status; + + switch (m_lastStatus) + { + case TOO_LONG: + status = _("Too long: "); + break; + case TOO_SHORT: + status = _("Too short: "); + break; + case TUNED: + status = _("Tuned: "); + break; + default: + return _("?"); + } + + status += LengthDoubleToString( (double) m_lastLength, false ); + status += "/"; + status += LengthDoubleToString( (double) m_settings.m_targetLength, false ); + + return status; +} + +PNS_DP_MEANDER_PLACER::TUNING_STATUS PNS_DP_MEANDER_PLACER::TuningStatus() const +{ + return m_lastStatus; +} diff --git a/pcbnew/router/pns_dp_meander_placer.h b/pcbnew/router/pns_dp_meander_placer.h new file mode 100644 index 0000000000..5f2f54f500 --- /dev/null +++ b/pcbnew/router/pns_dp_meander_placer.h @@ -0,0 +1,148 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2014 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_DP_MEANDER_PLACER_H +#define __PNS_DP_MEANDER_PLACER_H + +#include + +#include +#include + +#include "pns_node.h" +#include "pns_via.h" +#include "pns_line.h" +#include "pns_placement_algo.h" +#include "pns_meander.h" +#include "pns_meander_placer_base.h" +#include "pns_diff_pair.h" + +class PNS_ROUTER; +class PNS_SHOVE; +class PNS_OPTIMIZER; +class PNS_ROUTER_BASE; + +/** + * Class PNS_DP_MEANDER_PLACER + * + * Differential Pair length-matching/meandering tool. + */ + +class PNS_DP_MEANDER_PLACER : public PNS_MEANDER_PLACER_BASE +{ +public: + + PNS_DP_MEANDER_PLACER( PNS_ROUTER* aRouter ); + ~PNS_DP_MEANDER_PLACER(); + + /** + * Function Start() + * + * Starts routing a single track at point aP, taking item aStartItem as anchor + * (unless NULL). + */ + bool Start ( const VECTOR2I& aP, PNS_ITEM* aStartItem ); + + /** + * Function Move() + * + * Moves the end of the currently routed trace to the point aP, taking + * aEndItem as anchor (if not NULL). + * (unless NULL). + */ + bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ); + + /** + * Function FixRoute() + * + * Commits the currently routed track to the parent node, taking + * aP as the final end point and aEndItem as the final anchor (if provided). + * @return true, if route has been commited. May return false if the routing + * result is violating design rules - in such case, the track is only committed + * if Settings.CanViolateDRC() is on. + */ + bool FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ); + + const PNS_LINE Trace() const; + + /** + * Function CurrentNode() + * + * Returns the most recent world state. + */ + PNS_NODE* CurrentNode( bool aLoopsRemoved = false ) const; + + const PNS_ITEMSET Traces(); + + const VECTOR2I& CurrentEnd() const; + + int CurrentNet() const; + int CurrentLayer() const; + + + int totalLength(); + + const wxString TuningInfo() const; + TUNING_STATUS TuningStatus() const; + + bool CheckFit ( PNS_MEANDER_SHAPE* aShape ); + + +private: + friend class PNS_MEANDER_SHAPE; + + void meanderSegment ( const SEG& aBase ); + + + +// void addMeander ( PNS_MEANDER *aM ); +// void addCorner ( const VECTOR2I& aP ); + + const SEG baselineSegment ( const PNS_DIFF_PAIR::COUPLED_SEGMENTS& aCoupledSegs ); + + void setWorld ( PNS_NODE* aWorld ); + void release(); + + int origPathLength () const; + + ///> pointer to world to search colliding items + PNS_NODE* m_world; + + ///> current routing start point (end of tail, beginning of head) + VECTOR2I m_currentStart; + + ///> Current world state + PNS_NODE* m_currentNode; + + PNS_DIFF_PAIR m_originPair; + PNS_DIFF_PAIR::COUPLED_SEGMENTS_VEC m_coupledSegments; + + PNS_LINE m_currentTraceN, m_currentTraceP; + PNS_ITEMSET m_tunedPath, m_tunedPathP, m_tunedPathN; + + SHAPE_LINE_CHAIN m_finalShapeP, m_finalShapeN; + PNS_MEANDERED_LINE m_result; + PNS_SEGMENT *m_initialSegment; + + int m_lastLength; + TUNING_STATUS m_lastStatus; +}; + +#endif // __PNS_DP_MEANDER_PLACER_H diff --git a/pcbnew/router/pns_item.h b/pcbnew/router/pns_item.h index 7e4333052e..cd53b76b61 100644 --- a/pcbnew/router/pns_item.h +++ b/pcbnew/router/pns_item.h @@ -36,7 +36,8 @@ class PNS_NODE; enum LineMarker { MK_HEAD = ( 1 << 0 ), MK_VIOLATION = ( 1 << 3 ), - MK_LOCKED = ( 1 << 4 ) + MK_LOCKED = ( 1 << 4 ), + MK_DP_COUPLED = ( 1 << 5 ) }; /** @@ -58,6 +59,7 @@ public: JOINT = 4, SEGMENT = 8, VIA = 16, + DIFF_PAIR = 32, ANY = 0xff }; diff --git a/pcbnew/router/pns_itemset.cpp b/pcbnew/router/pns_itemset.cpp index 837f5e4ee5..9c41b2eb66 100644 --- a/pcbnew/router/pns_itemset.cpp +++ b/pcbnew/router/pns_itemset.cpp @@ -22,12 +22,22 @@ #include "pns_itemset.h" -PNS_ITEMSET::PNS_ITEMSET( PNS_ITEM* aInitialItem ) +PNS_ITEMSET::PNS_ITEMSET( PNS_ITEM* aInitialItem ) : + m_owner ( false ) { if( aInitialItem ) m_items.push_back( aInitialItem ); } +PNS_ITEMSET::~PNS_ITEMSET() +{ + if (m_owner) + { + BOOST_FOREACH ( PNS_ITEM *item, m_items ) + delete item; + } +} + PNS_ITEMSET& PNS_ITEMSET::FilterLayers( int aStart, int aEnd, bool aInvert ) { ITEMS newItems; @@ -64,6 +74,21 @@ PNS_ITEMSET& PNS_ITEMSET::FilterKinds( int aKindMask, bool aInvert ) return *this; } +PNS_ITEMSET& PNS_ITEMSET::FilterMarker( int aMarker, bool aInvert ) +{ + ITEMS newItems; + + BOOST_FOREACH( PNS_ITEM* item, m_items ) + { + if( item->Marker() & aMarker ) + newItems.push_back( item ); + } + + m_items = newItems; + + return *this; +} + PNS_ITEMSET& PNS_ITEMSET::FilterNet( int aNet, bool aInvert ) { diff --git a/pcbnew/router/pns_itemset.h b/pcbnew/router/pns_itemset.h index d229fc017c..278714db77 100644 --- a/pcbnew/router/pns_itemset.h +++ b/pcbnew/router/pns_itemset.h @@ -21,7 +21,7 @@ #ifndef __PNS_ITEMSET_H #define __PNS_ITEMSET_H -#include +#include #include #include "pns_item.h" @@ -36,15 +36,23 @@ class PNS_ITEMSET { public: - typedef std::vector ITEMS; + typedef std::deque ITEMS; PNS_ITEMSET( PNS_ITEM* aInitialItem = NULL ); - PNS_ITEMSET( const PNS_ITEMSET& aOther ) + PNS_ITEMSET( const PNS_ITEMSET& aOther ): + m_owner ( false ) { m_items = aOther.m_items; } + ~PNS_ITEMSET(); + + void MakeOwner ( ) + { + m_owner = true; + } + const PNS_ITEMSET& operator=( const PNS_ITEMSET& aOther ) { m_items = aOther.m_items; @@ -68,6 +76,7 @@ public: PNS_ITEMSET& FilterLayers( int aStart, int aEnd = -1, bool aInvert = false ); PNS_ITEMSET& FilterKinds( int aKindMask, bool aInvert = false ); PNS_ITEMSET& FilterNet( int aNet, bool aInvert = false ); + PNS_ITEMSET& FilterMarker( int aMarker, bool aInvert = false ); PNS_ITEMSET& ExcludeLayers( int aStart, int aEnd = -1 ) { @@ -96,6 +105,11 @@ public: m_items.push_back( aItem ); } + void Prepend ( PNS_ITEM *aItem ) + { + m_items.push_front ( aItem ); + } + PNS_ITEM* Get( int index ) const { return m_items[index]; @@ -126,6 +140,7 @@ public: private: ITEMS m_items; + bool m_owner; }; #endif diff --git a/pcbnew/router/pns_joint.h b/pcbnew/router/pns_joint.h index 43ca70daf0..566ade3bd0 100644 --- a/pcbnew/router/pns_joint.h +++ b/pcbnew/router/pns_joint.h @@ -41,7 +41,7 @@ class PNS_JOINT : public PNS_ITEM { public: - typedef std::vector LINKED_ITEMS; + typedef std::deque LINKED_ITEMS; ///> Joints are hashed by their position, layers and net. /// Linked items are, obviously, not hashed @@ -95,6 +95,22 @@ public: return seg1->Width() == seg2->Width(); } + bool IsNonFanoutVia () const + { + if ( m_linkedItems.Size() != 3 ) + return false; + + int vias = 0, segs = 0; + + for(int i = 0; i < 3; i++) + { + vias += m_linkedItems[i]->Kind() == VIA ? 1 : 0; + segs += m_linkedItems[i]->Kind() == SEGMENT ? 1 : 0; + } + + return (vias == 1 && segs == 2); + } + ///> Links the joint to a given board item (when it's added to the PNS_NODE) void Link( PNS_ITEM* aItem ) { diff --git a/pcbnew/router/pns_line.cpp b/pcbnew/router/pns_line.cpp index c391f31a9e..bf02010bfe 100644 --- a/pcbnew/router/pns_line.cpp +++ b/pcbnew/router/pns_line.cpp @@ -701,6 +701,9 @@ void PNS_LINE::Reverse() void PNS_LINE::AppendVia( const PNS_VIA& aVia ) { + if(m_line.PointCount() == 0) + return; + if( aVia.Pos() == m_line.CPoint( 0 ) ) { Reverse(); @@ -781,3 +784,91 @@ void PNS_LINE::ClearSegmentLinks() m_segmentRefs = NULL; } + +static void extendBox( BOX2I& aBox, bool &aDefined, const VECTOR2I& aP ) +{ + if( aDefined ) + aBox.Merge ( aP ); + else { + aBox = BOX2I ( aP, VECTOR2I (0, 0 ) ); + aDefined = true; + } +} + +OPT_BOX2I PNS_LINE::ChangedArea ( const PNS_LINE *aOther ) const +{ + BOX2I area; + bool areaDefined = false; + + + int i_start = -1; + int i_end_self = -1, i_end_other = -1; + + SHAPE_LINE_CHAIN self ( m_line ); + self.Simplify(); + SHAPE_LINE_CHAIN other ( aOther->m_line ); + other.Simplify(); + + int np_self = self.PointCount(); + int np_other = other.PointCount(); + + int n = std::min ( np_self, np_other ); + + for( int i = 0; i < n; i++) + { + const VECTOR2I p1 = self.CPoint(i); + const VECTOR2I p2 = other.CPoint(i); + if (p1 != p2) + { + if (i != n - 1) + { + SEG s = self.CSegment(i); + if( !s.Contains( p2 ) ) + { + i_start = i; + break; + } + } else { + i_start = i; + break; + } + + } + } + + for( int i = 0; i < n; i++) + { + + const VECTOR2I p1 = self.CPoint( np_self - 1 - i ); + const VECTOR2I p2 = other.CPoint( np_other - 1 - i ); + if (p1 != p2) + { + i_end_self = np_self - 1 - i; + i_end_other = np_other - 1 - i; + break; + } + } + + if( i_start < 0 ) + i_start = n; + + if( i_end_self < 0 ) + i_end_self = np_self - 1; + + if( i_end_other < 0 ) + i_end_other = np_other - 1; + + for (int i = i_start; i <= i_end_self; i++ ) + extendBox ( area, areaDefined, self.CPoint(i) ); + + for (int i = i_start; i <= i_end_other; i++ ) + extendBox ( area, areaDefined, other.CPoint(i) ); + + if( areaDefined ) + { + area.Inflate ( std::max( Width(), aOther->Width() ) ); + return area; + } + + return OPT_BOX2I ( ); +} diff --git a/pcbnew/router/pns_line.h b/pcbnew/router/pns_line.h index d77c37ec96..5a12fab96f 100644 --- a/pcbnew/router/pns_line.h +++ b/pcbnew/router/pns_line.h @@ -88,7 +88,12 @@ public: } ~PNS_LINE(); - + + static inline bool ClassOf( const PNS_ITEM* aItem ) + { + return aItem && LINE == aItem->Kind(); + } + /// @copydoc PNS_ITEM::Clone() virtual PNS_LINE* Clone() const; @@ -179,6 +184,11 @@ public: return m_segmentRefs; } + bool IsLinked () const + { + return m_segmentRefs != NULL; + } + ///> Checks if the segment aSeg is a part of the line. bool ContainsSegment( PNS_SEGMENT* aSeg ) const { @@ -189,6 +199,11 @@ public: aSeg ) != m_segmentRefs->end(); } + PNS_SEGMENT* GetLink ( int aIndex ) const + { + return (*m_segmentRefs) [ aIndex ]; + } + ///> Erases the linking information. Used to detach the line from the owning node. void ClearSegmentLinks(); @@ -251,6 +266,8 @@ public: bool HasLoops() const; + OPT_BOX2I ChangedArea ( const PNS_LINE *aOther ) const; + private: VECTOR2I snapToNeighbourSegments( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I &aP, int aIndex, int aThreshold) const; diff --git a/pcbnew/router/pns_line_placer.cpp b/pcbnew/router/pns_line_placer.cpp index 093fbe6218..2d6014784e 100644 --- a/pcbnew/router/pns_line_placer.cpp +++ b/pcbnew/router/pns_line_placer.cpp @@ -31,13 +31,14 @@ #include "pns_shove.h" #include "pns_utils.h" #include "pns_router.h" +#include "pns_topology.h" #include using boost::optional; PNS_LINE_PLACER::PNS_LINE_PLACER( PNS_ROUTER* aRouter ) : - PNS_ALGO_BASE ( aRouter ) + PNS_PLACEMENT_ALGO ( aRouter ) { m_initial_direction = DIRECTION_45::N; m_world = NULL; @@ -67,11 +68,13 @@ const PNS_VIA PNS_LINE_PLACER::makeVia ( const VECTOR2I& aP ) } -void PNS_LINE_PLACER::ToggleVia( bool aEnabled ) +bool PNS_LINE_PLACER::ToggleVia( bool aEnabled ) { m_placingVia = aEnabled; if(!m_idle) Move ( m_currentEnd, NULL ); + + return true; } @@ -225,8 +228,6 @@ bool PNS_LINE_PLACER::reduceTail( const VECTOR2I& aEnd ) VECTOR2I new_start; int reduce_index = -1; - DIRECTION_45 head_dir( head.CSegment( 0 ) ); - for( int i = tail.SegmentCount() - 1; i >= 0; i-- ) { const SEG s = tail.CSegment( i ); @@ -376,7 +377,7 @@ bool PNS_LINE_PLACER::handleViaPlacement( PNS_LINE& aHead ) bool PNS_LINE_PLACER::rhWalkOnly( const VECTOR2I& aP, PNS_LINE& aNewHead ) { - SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace( m_p_start, aP ); + SHAPE_LINE_CHAIN line = buildInitialLine ( aP ); PNS_LINE initTrack( m_head, line ), walkFull; int effort = 0; bool viaOk = handleViaPlacement( initTrack ); @@ -430,7 +431,7 @@ bool PNS_LINE_PLACER::rhWalkOnly( const VECTOR2I& aP, PNS_LINE& aNewHead ) bool PNS_LINE_PLACER::rhMarkObstacles( const VECTOR2I& aP, PNS_LINE& aNewHead ) { - m_head.SetShape( m_direction.BuildInitialTrace( m_p_start, aP ) ); + m_head.SetShape( buildInitialLine ( aP ) ); if( m_placingVia ) { @@ -445,7 +446,7 @@ bool PNS_LINE_PLACER::rhMarkObstacles( const VECTOR2I& aP, PNS_LINE& aNewHead ) bool PNS_LINE_PLACER::rhShoveOnly ( const VECTOR2I& aP, PNS_LINE& aNewHead ) { - SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace( m_p_start, aP ); + SHAPE_LINE_CHAIN line = buildInitialLine ( aP ); PNS_LINE initTrack( m_head, line ); PNS_LINE walkSolids, l2; @@ -744,7 +745,7 @@ bool PNS_LINE_PLACER::SetLayer( int aLayer ) return false; } -void PNS_LINE_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) +bool PNS_LINE_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) { VECTOR2I p( aP ); @@ -772,6 +773,7 @@ void PNS_LINE_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) setInitialDirection( Settings().InitialDirection() ); initPlacement( m_splitSeg ); + return true; } void PNS_LINE_PLACER::initPlacement( bool aSplitSeg ) @@ -819,7 +821,7 @@ void PNS_LINE_PLACER::initPlacement( bool aSplitSeg ) } -void PNS_LINE_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ) +bool PNS_LINE_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ) { PNS_LINE current; VECTOR2I p = aP; @@ -856,6 +858,7 @@ void PNS_LINE_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ) } updateLeadingRatLine(); + return true; } @@ -962,11 +965,6 @@ void PNS_LINE_PLACER::removeLoops( PNS_NODE* aNode, PNS_LINE* aLatest ) if( !( line->ContainsSegment( seg ) ) && line->SegmentCount() ) { - Router()->DisplayDebugLine ( line->CLine(), -1, 10000 ); - - for( int i = 0; i < line->PointCount(); i++ ) - Router()->DisplayDebugPoint( line->CPoint( i ), -1 ); - aNode->Remove( line ); removedCount ++; } @@ -1012,27 +1010,39 @@ void PNS_LINE_PLACER::UpdateSizes( const PNS_SIZES_SETTINGS& aSizes ) void PNS_LINE_PLACER::updateLeadingRatLine() { PNS_LINE current = Trace(); + SHAPE_LINE_CHAIN ratLine; + PNS_TOPOLOGY topo ( m_lastNode ); - if( !current.PointCount() ) - return; - - std::auto_ptr tmpNode ( m_lastNode->Branch() ); - tmpNode->Add( ¤t ); - - PNS_JOINT* jt = tmpNode->FindJoint( current.CPoint( -1 ), - current.Layers().Start(), current.Net() ); - - if( !jt ) - return; - - int anchor; - PNS_ITEM* it = tmpNode->NearestUnconnectedItem( jt, &anchor ); - - if( it ) - { - SHAPE_LINE_CHAIN lc; - lc.Append ( current.CPoint( -1 ) ); - lc.Append ( it->Anchor( anchor ) ); - Router()->DisplayDebugLine( lc, 5, 10000 ); - } + if( topo.LeadingRatLine ( ¤t, ratLine )) + Router()->DisplayDebugLine( ratLine, 5, 10000 ); +} + +void PNS_LINE_PLACER::SetOrthoMode ( bool aOrthoMode ) +{ + m_orthoMode = aOrthoMode; + if(!m_idle) + Move ( m_currentEnd, NULL ); +} + +const SHAPE_LINE_CHAIN PNS_LINE_PLACER::buildInitialLine ( const VECTOR2I& aP ) +{ + SHAPE_LINE_CHAIN l (m_direction.BuildInitialTrace( m_p_start, aP ) ); + + if( l.SegmentCount() <= 1 ) + return l; + + if (m_orthoMode) + { + VECTOR2I newLast = l.CSegment(0).LineProject ( l.CPoint(-1) ); + + l.Remove(-1, -1); + l.Point(1) = newLast; + } + + return l; +} + +void PNS_LINE_PLACER::GetModifiedNets( std::vector &aNets ) const +{ + aNets.push_back( m_currentNet ); } diff --git a/pcbnew/router/pns_line_placer.h b/pcbnew/router/pns_line_placer.h index 1d603bd9b6..ec7ace7412 100644 --- a/pcbnew/router/pns_line_placer.h +++ b/pcbnew/router/pns_line_placer.h @@ -30,7 +30,7 @@ #include "pns_node.h" #include "pns_via.h" #include "pns_line.h" -#include "pns_algo_base.h" +#include "pns_placement_algo.h" class PNS_ROUTER; class PNS_SHOVE; @@ -40,6 +40,7 @@ class PNS_VIA; class PNS_SIZES_SETTINGS; + /** * Class PNS_LINE_PLACER * @@ -47,7 +48,7 @@ class PNS_SIZES_SETTINGS; * Applies shove and walkaround algorithms when needed. */ -class PNS_LINE_PLACER : public PNS_ALGO_BASE +class PNS_LINE_PLACER : public PNS_PLACEMENT_ALGO { public: PNS_LINE_PLACER( PNS_ROUTER* aRouter ); @@ -59,7 +60,7 @@ public: * Starts routing a single track at point aP, taking item aStartItem as anchor * (unless NULL). */ - void Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ); + bool Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ); /** * Function Move() @@ -68,7 +69,7 @@ public: * aEndItem as anchor (if not NULL). * (unless NULL). */ - void Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ); + bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ); /** * Function FixRoute() @@ -86,7 +87,7 @@ public: * * Enables/disables a via at the end of currently routed trace. */ - void ToggleVia( bool aEnabled ); + bool ToggleVia( bool aEnabled ); /** * Function SetLayer() @@ -180,7 +181,11 @@ public: */ void UpdateSizes( const PNS_SIZES_SETTINGS& aSizes ); + void SetOrthoMode ( bool aOrthoMode ); + bool IsPlacingVia() const { return m_placingVia; } + + void GetModifiedNets( std::vector &aNets ) const; private: /** * Function route() @@ -343,7 +348,8 @@ private: bool rhMarkObstacles( const VECTOR2I& aP, PNS_LINE& aNewHead ); const PNS_VIA makeVia ( const VECTOR2I& aP ); - + const SHAPE_LINE_CHAIN buildInitialLine ( const VECTOR2I& aP ); + ///> current routing direction DIRECTION_45 m_direction; @@ -403,6 +409,7 @@ private: bool m_idle; bool m_chainedPlacement; bool m_splitSeg; + bool m_orthoMode; }; #endif // __PNS_LINE_PLACER_H diff --git a/pcbnew/router/pns_meander.cpp b/pcbnew/router/pns_meander.cpp new file mode 100644 index 0000000000..952a27e403 --- /dev/null +++ b/pcbnew/router/pns_meander.cpp @@ -0,0 +1,580 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2014 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include + +#include // God forgive me doing this... +#include + +#include "trace.h" + +#include "pns_node.h" +#include "pns_itemset.h" +#include "pns_topology.h" +#include "pns_meander.h" +#include "pns_meander_placer_base.h" +#include "pns_router.h" + +const PNS_MEANDER_SETTINGS& PNS_MEANDER_SHAPE::Settings( ) const +{ + return m_placer->MeanderSettings( ); +} + +const PNS_MEANDER_SETTINGS& PNS_MEANDERED_LINE::Settings( ) const +{ + return m_placer->MeanderSettings( ); +} + +void PNS_MEANDERED_LINE::MeanderSegment( const SEG &aBase, int aBaseIndex ) +{ + double base_len = aBase.Length( ); + + SHAPE_LINE_CHAIN lc; + + bool side = true; + VECTOR2D dir( aBase.B - aBase.A ); + + if(!m_dual) + AddCorner( aBase.A ); + + bool turning = false; + bool started = false; + + m_last = aBase.A; + + do + { + PNS_MEANDER_SHAPE *m = new PNS_MEANDER_SHAPE( m_placer, m_width, m_dual ); + m->SetBaselineOffset( m_baselineOffset ); + m->SetBaseIndex( aBaseIndex ); + + double thr = (double) m->spacing( ); + + bool fail = false; + double remaining = base_len - ( m_last - aBase.A ).EuclideanNorm( ); + + if( remaining < Settings( ).m_step ) + break; + + if( remaining > 3.0 * thr ) + { + + if( !turning ) + { + for( int i = 0; i < 2; i++ ) + { + if ( m->Fit( MT_CHECK_START, aBase, m_last, i ) ) + { + turning = true; + AddMeander( m ); + side = !i; + started = true; + break; + } + } + + if( !turning ) + { + fail = true; + + for( int i = 0; i < 2; i++ ) + { + if ( m->Fit ( MT_SINGLE, aBase, m_last, i ) ) + { + AddMeander( m ); + fail = false; + started = false; + side = !i; + break; + } + } + } + + } else { + bool rv = m->Fit( MT_CHECK_FINISH, aBase, m_last, side ); + + if( rv ) + { + m->Fit( MT_TURN, aBase, m_last, side ); + AddMeander( m ); + started = true; + } else { + m->Fit( MT_FINISH, aBase, m_last, side ); + started = false; + AddMeander( m ); + turning = false; + } + + side = !side; + } + + } else if( started ) + { + bool rv = m->Fit( MT_FINISH, aBase, m_last, side ); + if( rv ) + AddMeander(m); + + break; + + } else { + fail = true; + } + + remaining = base_len - ( m_last - aBase.A ).EuclideanNorm( ); + + if( remaining < Settings( ).m_step ) + break; + + + if( fail ) + { + PNS_MEANDER_SHAPE tmp( m_placer, m_width, m_dual ); + tmp.SetBaselineOffset( m_baselineOffset ); + tmp.SetBaseIndex( aBaseIndex ); + + int nextP = tmp.spacing( ) - 2 * tmp.cornerRadius( ) + Settings( ).m_step; + VECTOR2I pn = m_last + dir.Resize ( nextP ); + + if (aBase.Contains( pn ) && !m_dual) + { + AddCorner ( pn ); + } else + break; + } + + + } while( true ); + + if( !m_dual ) + AddCorner( aBase.B ); +} + + +int PNS_MEANDER_SHAPE::cornerRadius( ) const +{ + int cr = (int64_t) spacing( ) * Settings( ).m_cornerRadiusPercentage / 200; + + return cr; +} + +int PNS_MEANDER_SHAPE::spacing( ) const +{ + if ( !m_dual ) + return std::max ( 2 * m_width, Settings( ).m_spacing ); + else + { + int sp = 2 * ( m_width + std::abs( m_baselineOffset ) ); + return std::max ( sp, Settings( ).m_spacing ); + } +} + + +SHAPE_LINE_CHAIN PNS_MEANDER_SHAPE::circleQuad( VECTOR2D aP, VECTOR2D aDir, bool side ) +{ + SHAPE_LINE_CHAIN lc; + + if( aDir.EuclideanNorm( ) == 0.0f ) + { + lc.Append( aP ); + return lc; + } + + VECTOR2D dir_u( aDir ); + VECTOR2D dir_v( aDir.Perpendicular( ) ); + + const int ArcSegments = Settings( ).m_cornerArcSegments; + + for(int i = ArcSegments - 1; i >= 0; i--) + { + VECTOR2D p; + double alpha = (double) i / (double) (ArcSegments - 1) * M_PI / 2.0; + p = aP + dir_u * cos( alpha ) + dir_v * ( side ? -1.0 : 1.0 ) * ( 1.0 - sin( alpha ) ); + lc.Append( ( int ) p.x, ( int ) p.y ); + } + return lc; +} + +VECTOR2I PNS_MEANDER_SHAPE::reflect( VECTOR2I p, const SEG& line ) +{ + typedef int64_t ecoord; + VECTOR2I d = line.B - line.A; + ecoord l_squared = d.Dot( d ); + ecoord t = d.Dot( p - line.A ); + VECTOR2I c, rv; + + if(!l_squared) + c = p; + else { + c.x = line.A.x + rescale( t, (ecoord)d.x, l_squared ); + c.y = line.A.y + rescale( t, (ecoord)d.y, l_squared ); + } + + return 2 * c - p; +} + +void PNS_MEANDER_SHAPE::start( SHAPE_LINE_CHAIN* aTarget, const VECTOR2D& aWhere, const VECTOR2D& aDir ) +{ + m_currentTarget = aTarget; + m_currentTarget->Clear( ); + m_currentTarget->Append( aWhere ); + m_currentDir = aDir; + m_currentPos = aWhere; +} + +void PNS_MEANDER_SHAPE::forward( int aLength ) +{ + m_currentPos += m_currentDir.Resize( aLength ); + m_currentTarget->Append( m_currentPos ); +} + +void PNS_MEANDER_SHAPE::turn( int aAngle ) +{ + m_currentDir = m_currentDir.Rotate( (double)aAngle * M_PI / 180.0 ); +} + +void PNS_MEANDER_SHAPE::arc( int aRadius, bool aSide ) +{ + if( aRadius <= 0 ) + { + turn ( aSide ? -90 : 90 ); + return; + } + + VECTOR2D dir = m_currentDir.Resize( (double) aRadius ); + SHAPE_LINE_CHAIN arc = circleQuad( m_currentPos, dir, aSide ); + m_currentPos = arc.CPoint( -1 ); + m_currentDir = dir.Rotate( aSide ? -M_PI/2.0 : M_PI/2.0 ); + + m_currentTarget->Append ( arc ); +} + +void PNS_MEANDER_SHAPE::uShape ( int aSides, int aCorner, int aTop ) +{ + forward( aSides ); + arc( aCorner, true ); + forward( aTop ); + arc( aCorner, true ); + forward( aSides ); +} + +SHAPE_LINE_CHAIN PNS_MEANDER_SHAPE::genMeanderShape ( VECTOR2D aP, VECTOR2D aDir, bool aSide, PNS_MEANDER_TYPE aType, int aAmpl, int aBaselineOffset ) +{ + const PNS_MEANDER_SETTINGS& st = Settings(); + int cr = cornerRadius(); + int offset = aBaselineOffset; + int spc = spacing(); + + if (aSide) + offset *= -1; + + VECTOR2D dir_u_b ( aDir.Resize( offset ) ); + VECTOR2D dir_v_b (dir_u_b.Perpendicular()); + + if (2 * cr > aAmpl) + { + cr = aAmpl / 2; + } + + if (2 * cr > spc) + { + cr = spc / 2; + } + + + SHAPE_LINE_CHAIN lc; + + start ( &lc, aP + dir_v_b, aDir ); + + switch (aType) + { + case MT_EMPTY: + { + lc.Append( aP + dir_v_b + aDir ); + break; + } + case MT_START: + { + arc( cr - offset, false ); + uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr ); + forward( std::min(cr - offset, cr + offset) ); + forward( std::abs( offset ) ); + + break; + } + + case MT_FINISH: + { + start( &lc, aP - dir_u_b, aDir ); + turn ( 90 ); + forward( std::min(cr - offset, cr + offset) ); + forward( std::abs( offset ) ); + uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr ); + arc( cr - offset, false ); + break; + } + + case MT_TURN: + { + + start( &lc, aP - dir_u_b, aDir ); + turn( 90 ); + forward( std::abs( offset ) ); + uShape ( aAmpl - cr, cr + offset, spc - 2 * cr ); + forward( std::abs( offset ) ); + + break; + } + + case MT_SINGLE: + { + arc( cr - offset, false ); + uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr ); + arc( cr - offset, false ); + lc.Append ( aP + dir_v_b + aDir.Resize ( 2 * st.m_spacing) ); + break; + } + + default: + break; + } + + if( aSide ) + { + SEG axis ( aP, aP + aDir ); + for(int i = 0; i < lc.PointCount(); i++ ) + lc.Point(i) = reflect ( lc.CPoint(i), axis ); + } + + return lc; +} + +bool PNS_MEANDERED_LINE::CheckSelfIntersections ( PNS_MEANDER_SHAPE *aShape, int aClearance ) +{ + for (int i = m_meanders.size() - 1; i >= 0; i--) + { + PNS_MEANDER_SHAPE *m = m_meanders[i]; + + if (m->Type() == MT_EMPTY || m->Type() == MT_CORNER ) + continue; + + const SEG& b1 = aShape->BaseSegment(); + const SEG& b2 = m->BaseSegment(); + + if (b1.ApproxParallel(b2)) + continue; + + int n = m->CLine(0).SegmentCount(); + + for (int j = n - 1; j >= 0; j--) + if ( aShape->CLine(0).Collide ( m->CLine(0).CSegment(j), aClearance ) ) + return false; + } + + return true; +} + +bool PNS_MEANDER_SHAPE::Fit ( PNS_MEANDER_TYPE aType, const SEG& aSeg, const VECTOR2I& aP, bool aSide ) +{ + const PNS_MEANDER_SETTINGS& st = Settings(); + + bool checkMode = false; + PNS_MEANDER_TYPE prim1, prim2; + + if(aType == MT_CHECK_START) + { + prim1 = MT_START; + prim2 = MT_TURN; + checkMode = true; + } else if (aType == MT_CHECK_FINISH ) { + prim1 = MT_TURN; + prim2 = MT_FINISH; + checkMode = true; + } + + if(checkMode) + { + PNS_MEANDER_SHAPE m1 ( m_placer, m_width, m_dual ); + PNS_MEANDER_SHAPE m2 ( m_placer, m_width, m_dual ); + + m1.SetBaselineOffset ( m_baselineOffset ); + m2.SetBaselineOffset ( m_baselineOffset ); + + bool c1 = m1.Fit ( prim1, aSeg, aP, aSide ); + bool c2 = false; + + if(c1) + c2 = m2.Fit ( prim2, aSeg, m1.End(), !aSide ); + + if(c1 && c2) + { + m_type = prim1; + m_shapes[0] = m1.m_shapes[0]; + m_shapes[1] = m1.m_shapes[1]; + m_baseSeg =aSeg; + m_p0 = aP; + m_side = aSide; + m_amplitude = m1.Amplitude(); + m_dual = m1.m_dual; + m_baseSeg = m1.m_baseSeg; + m_baseIndex = m1.m_baseIndex; + updateBaseSegment (); + m_baselineOffset = m1.m_baselineOffset; + return true; + } else + return false; + + } + + int minAmpl = st.m_minAmplitude; + int maxAmpl = st.m_maxAmplitude; + + if (m_dual) + { + minAmpl = std::max( minAmpl, 2 * std::abs(m_baselineOffset) ); + maxAmpl = std::max( maxAmpl, 2 * std::abs(m_baselineOffset) ); + } + + for(int ampl = maxAmpl; ampl >= minAmpl; ampl -= st.m_step) + { + if (m_dual) + { + m_shapes[0] = genMeanderShape ( aP, aSeg.B - aSeg.A, aSide, aType, ampl, m_baselineOffset ); + m_shapes[1] = genMeanderShape ( aP, aSeg.B - aSeg.A, aSide, aType, ampl, -m_baselineOffset ); + } else { + m_shapes[0] = genMeanderShape ( aP, aSeg.B - aSeg.A, aSide, aType, ampl, 0); + } + + m_type = aType; + m_baseSeg =aSeg; + m_p0 = aP; + m_side = aSide; + m_amplitude = ampl; + + updateBaseSegment(); + + if( m_placer->CheckFit ( this ) ) + return true; + } + + return false; +} + +void PNS_MEANDER_SHAPE::Recalculate ( ) +{ + + m_shapes[0] = genMeanderShape ( m_p0, m_baseSeg.B - m_baseSeg.A, m_side, m_type, m_amplitude, m_dual ? m_baselineOffset : 0); + + if (m_dual) + m_shapes[1] = genMeanderShape ( m_p0, m_baseSeg.B - m_baseSeg.A, m_side, m_type, m_amplitude, -m_baselineOffset ); + + updateBaseSegment(); + +} + +void PNS_MEANDER_SHAPE::Resize ( int aAmpl ) +{ + if(aAmpl < 0) + return; + + m_amplitude = aAmpl; + + Recalculate(); +} + +void PNS_MEANDER_SHAPE::MakeEmpty() +{ + updateBaseSegment(); + + VECTOR2I dir = m_clippedBaseSeg.B - m_clippedBaseSeg.A; + + m_type = MT_EMPTY; + + m_shapes[0] = genMeanderShape ( m_p0, dir, m_side, m_type, 0, m_dual ? m_baselineOffset : 0); + + if(m_dual) + m_shapes[1] = genMeanderShape ( m_p0, dir, m_side, m_type, 0, -m_baselineOffset ); + + +} + + +void PNS_MEANDERED_LINE::AddCorner ( const VECTOR2I& aA, const VECTOR2I& aB ) +{ + PNS_MEANDER_SHAPE *m = new PNS_MEANDER_SHAPE ( m_placer, m_width, m_dual ); + + m->MakeCorner ( aA, aB ); + m_last = aA; + + m_meanders.push_back( m ); +} + +void PNS_MEANDER_SHAPE::MakeCorner ( VECTOR2I aP1, VECTOR2I aP2 ) +{ + SetType( MT_CORNER ); + m_shapes[ 0 ].Clear( ); + m_shapes[ 1 ].Clear( ); + m_shapes[ 0 ].Append( aP1 ); + m_shapes[ 1 ].Append( aP2 ); + m_clippedBaseSeg.A = aP1; + m_clippedBaseSeg.B = aP1; + +} + +void PNS_MEANDERED_LINE::AddMeander ( PNS_MEANDER_SHAPE *aShape ) +{ + m_last = aShape->BaseSegment( ).B; + m_meanders.push_back( aShape ); +} + + +void PNS_MEANDERED_LINE::Clear( ) +{ + BOOST_FOREACH( PNS_MEANDER_SHAPE *m, m_meanders ) + { + delete m; + } + + m_meanders.clear( ); +} + +int PNS_MEANDER_SHAPE::BaselineLength( ) const +{ + return m_clippedBaseSeg.Length( ); +} + +int PNS_MEANDER_SHAPE::MaxTunableLength( ) const +{ + return CLine( 0 ).Length( ); +} + +void PNS_MEANDER_SHAPE::updateBaseSegment( ) +{ + if( m_dual ) + { + VECTOR2I midpA = ( CLine( 0 ).CPoint( 0 ) + CLine( 1 ).CPoint( 0 ) ) / 2; + VECTOR2I midpB = ( CLine( 0 ).CPoint( -1 ) + CLine( 1 ).CPoint( -1 ) ) / 2; + + m_clippedBaseSeg.A = m_baseSeg.LineProject( midpA ); + m_clippedBaseSeg.B = m_baseSeg.LineProject( midpB ); + } else { + m_clippedBaseSeg.A = m_baseSeg.LineProject( CLine( 0 ).CPoint( 0 ) ); + m_clippedBaseSeg.B = m_baseSeg.LineProject( CLine( 0 ).CPoint( -1 ) ); + } +} diff --git a/pcbnew/router/pns_meander.h b/pcbnew/router/pns_meander.h new file mode 100644 index 0000000000..0abcbaaa13 --- /dev/null +++ b/pcbnew/router/pns_meander.h @@ -0,0 +1,498 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_MEANDER_H +#define __PNS_MEANDER_H + +#include + +#include +#include + +class PNS_MEANDER_PLACER_BASE; + +///< Shapes of available meanders +enum PNS_MEANDER_TYPE { + MT_SINGLE, // _|^|_, single-sided + MT_START, // _|^| + MT_FINISH, // |^|_ + MT_TURN, // |^| or |_| + MT_CHECK_START, // try fitting a start type, but don't produce a line + MT_CHECK_FINISH, // try fitting a finish type, but don't produce a line + MT_CORNER, // line corner + MT_EMPTY // no meander (straight line) +}; + +/** + * Class PNS_MEANDER_SETTINGS + * + * Holds dimensions for the meandering algorithm. + */ +class PNS_MEANDER_SETTINGS +{ +public: + + ///> meander corner shape + enum CornerType { + ROUND = 1, // rounded (90 degree arc) + CHAMFER // chamfered (45 degree segment) + }; + + PNS_MEANDER_SETTINGS () + { + m_minAmplitude = 100000; + m_maxAmplitude = 1000000; + m_step = 50000; + m_spacing = 600000; + m_targetLength = 100000000; + m_targetSkew = 0; + m_cornerType = ROUND; + m_cornerRadiusPercentage = 100; + m_lengthTollerance = 100000; + m_cornerArcSegments = 8; + } + + ///> minimum meandering amplitude + int m_minAmplitude; + ///> maximum meandering amplitude + int m_maxAmplitude; + ///> meandering period/spacing (see dialog picture for explanation) + int m_spacing; + ///> amplitude/spacing adjustment step + int m_step; + ///> desired length of the tuned line/diff pair + int m_targetLength; + ///> type of corners for the meandered line + CornerType m_cornerType; + ///> rounding percentage (0 - 100) + int m_cornerRadiusPercentage; + ///> allowable tuning error + int m_lengthTollerance; + ///> number of line segments for arc approximation + int m_cornerArcSegments; + ///> target skew value for diff pair de-skewing + int m_targetSkew; +}; + +class PNS_MEANDERED_LINE; + +/** + * Class PNS_MEANDER_SETTINGS + * + * Holds the geometry of a single meander. + */ +class PNS_MEANDER_SHAPE +{ + public: + + /** + * Constructor + * + * @param aPlacer the meander placer instance + * @param aWidth width of the meandered line + * @param aIsDual when true, the shape contains two meandered + * lines at a given offset (diff pairs) + */ + PNS_MEANDER_SHAPE( PNS_MEANDER_PLACER_BASE *aPlacer, int aWidth, bool aIsDual = false ) : + m_placer( aPlacer ), + m_dual( aIsDual ), + m_width( aWidth ), + m_baselineOffset( 0 ) + { + + } + + /** + * Function SetType() + * + * Sets the type of the meander. + */ + void SetType( PNS_MEANDER_TYPE aType ) + { + m_type = aType; + } + + /** + * Function Type() + * + * @return the type of the meander. + */ + PNS_MEANDER_TYPE Type( ) const + { + return m_type; + } + + /** + * Function SetBaseIndex() + * + * Sets an auxillary index of the segment being meandered in its original PNS_LINE. + */ + void SetBaseIndex( int aIndex ) + { + m_baseIndex = aIndex; + } + + /** + * Function BaseIndex() + * + * @return auxillary index of the segment being meandered in its original PNS_LINE. + */ + int BaseIndex( ) const + { + return m_baseIndex; + } + + /** + * Function Amplitude() + * + * @return the amplitude of the meander shape. + */ + int Amplitude( ) const + { + return m_amplitude; + } + + /** + * Function MakeCorner() + * + * Creates a dummy meander shape representing a line corner. Used to define + * the starts/ends of meandered segments. + * @param aP1 corner point of the 1st line + * @param aP2 corner point of the 2nd line (if m_dual == true) + */ + void MakeCorner( VECTOR2I aP1, VECTOR2I aP2 = VECTOR2I ( 0, 0 ) ); + + /** + * Function Resize() + * + * Changes the amplitude of the meander shape to aAmpl and recalculates + * the resulting line chain. + * @param aAmpl new amplitude. + */ + void Resize( int aAmpl ); + + /** + * Function Recalculate() + * + * Recalculates the line chain representing the meanders's shape. + */ + void Recalculate ( ); + + /** + * Function IsDual() + * + * @return true if the shape represents 2 parallel lines (diff pair). + */ + bool IsDual( ) const + { + return m_dual; + } + + /** + * Function Side() + * + * @return true if the meander is to the right of its base segment. + */ + bool Side( ) const + { + return m_side; + } + + /** + * Function End() + * + * @return end vertex of the base segment of the meander shape. + */ + VECTOR2I End( ) const + { + return m_clippedBaseSeg.B; + } + + /** + * Function CLine() + * + * @return the line chain representing the shape of the meander. + */ + const SHAPE_LINE_CHAIN& CLine( int aShape ) const + { + return m_shapes[ aShape ]; + } + + /** + * Function MakeEmpty() + * + * Replaces the meander with straight bypass line(s), effectively + * clearing it. + */ + void MakeEmpty( ); + + /** + * Function Fit() + * + * Attempts to fit a meander of a given type onto a segment, avoiding + * collisions with other board features. + * @param aType type of meander shape + * @param aSeg base segment for meandering + * @param aP start point of the meander + * @param aSide side of aSeg to put the meander on (true = right) + * @return true on success. + */ + bool Fit( PNS_MEANDER_TYPE aType, const SEG& aSeg, const VECTOR2I& aP, bool aSide ); + + /** + * Function BaseSegment() + * + * Returns the base segment the meadner was fitted to. + * @return the base segment. + */ + const SEG& BaseSegment( ) const + { + return m_clippedBaseSeg; + } + + /** + * Function BaselineLength() + * + * @return length of the base segment for the meander (i.e. + * the minimim tuned length. + */ + int BaselineLength( ) const; + + /** + * Function MaxTunableLength() + * + * @return the length of the fitted line chain. + */ + int MaxTunableLength( ) const; + + /** + * Function Settings() + * + * @return the current meandering settings. + */ + const PNS_MEANDER_SETTINGS& Settings( ) const; + + /** + * Function Width() + * + * @return width of the meandered line. + */ + int Width() const + { + return m_width; + } + + /** + * Function SetBaselineOffset() + * + * Sets the parallel offset between the base segment and the meandered + * line. Used for dual menaders (diff pair) only. + * @param aOffset the offset + */ + void SetBaselineOffset ( int aOffset ) + { + m_baselineOffset = aOffset; + } + + private: + friend class PNS_MEANDERED_LINE; + + ///> starts turtle drawing + void start ( SHAPE_LINE_CHAIN* aTarget, const VECTOR2D& aWhere, const VECTOR2D& aDir ); + ///> moves turtle forward by aLength + void forward ( int aLength ); + ///> turns the turtle by aAngle + void turn ( int aAngle ); + ///> tells the turtle to draw an arc of given radius and turn direction + void arc ( int aRadius, bool aSide ); + ///> tells the turtle to draw an U-like shape + void uShape ( int aSides, int aCorner, int aTop ); + + ///> generates a 90-degree circular arc + SHAPE_LINE_CHAIN circleQuad ( VECTOR2D aP, VECTOR2D aDir, bool side ); + + ///> reflects a point onto other side of a given segment + VECTOR2I reflect ( VECTOR2I p, const SEG& line ); + + ///> produces a meander shape of given type + SHAPE_LINE_CHAIN genMeanderShape ( VECTOR2D aP, VECTOR2D aDir, bool aSide, PNS_MEANDER_TYPE aType, int aAmpl, int aBaselineOffset = 0); + + ///> recalculates the clipped baseline after the parameters of + ///> the meander have been changed. + void updateBaseSegment(); + + ///> returns sanitized corner radius value + int cornerRadius ( ) const; + + ///> returns sanitized spacing value + int spacing ( ) const; + + ///> the type + PNS_MEANDER_TYPE m_type; + ///> the placer that placed this meander + PNS_MEANDER_PLACER_BASE *m_placer; + ///> dual or single line + bool m_dual; + ///> width of the line + int m_width; + ///> amplitude of the meander + int m_amplitude; + ///> offset wrs the base segment (dual only) + int m_baselineOffset; + ///> first point of the meandered line + VECTOR2I m_p0; + ///> base segment (unclipped) + SEG m_baseSeg; + ///> base segment (clipped) + SEG m_clippedBaseSeg; + ///> side (true = right) + bool m_side; + ///> the actual shapes (0 used for single, both for dual) + SHAPE_LINE_CHAIN m_shapes[2]; + ///> index of the meandered segment in the base line + int m_baseIndex; + ///> current turtle direction + VECTOR2D m_currentDir; + ///> current turtle position + VECTOR2D m_currentPos; + ///> the line the turtle is drawing on + SHAPE_LINE_CHAIN* m_currentTarget; +}; + + +/** + * Class PNS_MEANDERED_LINE + * + * Represents a set of meanders fitted over a single or two lines. + */ +class PNS_MEANDERED_LINE +{ + public: + PNS_MEANDERED_LINE( ) + { + + } + + /** + * Constructor + * + * @param aPlacer the meander placer instance + * @param aIsDual when true, the meanders are generated for two coupled lines + */ + PNS_MEANDERED_LINE( PNS_MEANDER_PLACER_BASE *aPlacer, bool aIsDual = false ) : + m_placer( aPlacer ), + m_dual( aIsDual ) + { + + } + + /** + * Function AddCorner() + * + * Creates a dummy meander shape representing a line corner. Used to define + * the starts/ends of meandered segments. + * @param aA corner point of the 1st line + * @param aB corner point of the 2nd line (if m_dual == true) + */ + void AddCorner( const VECTOR2I& aA, const VECTOR2I& aB = VECTOR2I( 0, 0 ) ); + + /** + * Function AddMeander() + * + * Adds a new meander shape the the meandered line. + * @param aShape the meander shape to add + */ + void AddMeander( PNS_MEANDER_SHAPE *aShape ); + + /** + * Function Clear() + * + * Clears the line geometry, removing all corners and meanders. + */ + void Clear( ); + + /** + * Function SetWidth() + * + * Sets the line width. + */ + void SetWidth( int aWidth ) + { + m_width = aWidth; + } + + /** + * Function MeanderSegment() + * + * Fits maximum amplitude meanders on a given segment and adds to the + * current line. + * @param aSeg the base segment to meander + * @param aBaseIndex index of the base segment in the original line + */ + void MeanderSegment( const SEG& aSeg, int aBaseIndex = 0 ); + + /// @copydoc PNS_MEANDER_SHAPE::SetBaselineOffset() + void SetBaselineOffset( int aOffset ) + { + m_baselineOffset = aOffset; + } + + /** + * Function Meanders() + * + * @return set of meander shapes for this line + */ + std::vector & Meanders() + { + return m_meanders; + } + + + /** + * Function CheckSelfIntersections() + * + * Checks if the given shape is intersecting with any other meander in + * the current line. + * @param aShape the shape to check + * @param aClearance clearance value + * @return true, if the meander shape is not colliding + */ + bool CheckSelfIntersections ( PNS_MEANDER_SHAPE *aShape, int aClearance ); + + /** + * Function Settings() + * + * @return the current meandering settings. + */ + const PNS_MEANDER_SETTINGS& Settings() const; + + private: + + VECTOR2I m_last; + + PNS_MEANDER_PLACER_BASE *m_placer; + std::vector m_meanders; + + bool m_dual; + int m_width; + int m_baselineOffset; +}; + +#endif // __PNS_MEANDER_H diff --git a/pcbnew/router/pns_meander_placer.cpp b/pcbnew/router/pns_meander_placer.cpp new file mode 100644 index 0000000000..e0875309d1 --- /dev/null +++ b/pcbnew/router/pns_meander_placer.cpp @@ -0,0 +1,262 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include + +#include // God forgive me doing this... +#include + +#include "trace.h" + +#include "pns_node.h" +#include "pns_itemset.h" +#include "pns_topology.h" +#include "pns_meander_placer.h" +#include "pns_router.h" + + +PNS_MEANDER_PLACER::PNS_MEANDER_PLACER( PNS_ROUTER* aRouter ) : + PNS_MEANDER_PLACER_BASE ( aRouter ) +{ + m_world = NULL; + m_currentNode = NULL; + m_originLine = NULL; +} + + +PNS_MEANDER_PLACER::~PNS_MEANDER_PLACER( ) +{ + +} + +PNS_NODE* PNS_MEANDER_PLACER::CurrentNode( bool aLoopsRemoved ) const +{ + if( !m_currentNode ) + return m_world; + + return m_currentNode; +} + +bool PNS_MEANDER_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) +{ + VECTOR2I p; + + if( !aStartItem || !aStartItem->OfKind( PNS_ITEM::SEGMENT ) ) + { + Router( )->SetFailureReason( _( "Please select a track whose length you want to tune." ) ); + return false; + } + + m_initialSegment = static_cast( aStartItem ); + + p = m_initialSegment->Seg( ).NearestPoint( aP ); + + m_originLine = NULL; + m_currentNode = NULL; + m_currentStart = p; + + m_world = Router( )->GetWorld( )->Branch( ); + m_originLine = m_world->AssembleLine( m_initialSegment ); + + PNS_TOPOLOGY topo( m_world ); + m_tunedPath = topo.AssembleTrivialPath( m_initialSegment ); + + m_world->Remove( m_originLine ); + + m_currentWidth = m_originLine->Width( ); + m_currentEnd = VECTOR2I( 0, 0 ); + + return true; +} + + +int PNS_MEANDER_PLACER::origPathLength( ) const +{ + int total = 0; + BOOST_FOREACH( const PNS_ITEM *item, m_tunedPath.CItems( ) ) + { + if( const PNS_LINE *l = dyn_cast( item ) ) + { + total += l->CLine( ).Length( ); + } + } + + return total; +} + +bool PNS_MEANDER_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ) +{ + return doMove ( aP, aEndItem, m_settings.m_targetLength ); +} + +bool PNS_MEANDER_PLACER::doMove( const VECTOR2I& aP, PNS_ITEM* aEndItem, int aTargetLength ) +{ + SHAPE_LINE_CHAIN pre, tuned, post; + + if(m_currentNode) + delete m_currentNode; + + m_currentNode = m_world->Branch( ); + + cutTunedLine ( m_originLine->CLine( ), m_currentStart, aP, pre, tuned, post ); + + m_result = PNS_MEANDERED_LINE( this, false ); + m_result.SetWidth( m_originLine->Width() ); + m_result.SetBaselineOffset( 0 ); + + for( int i = 0; i < tuned.SegmentCount(); i++ ) + { + const SEG s = tuned.CSegment( i ); + m_result.AddCorner( s.A ); + m_result.MeanderSegment( s ); + m_result.AddCorner( s.B ); + } + + int lineLen = origPathLength( ); + + m_lastLength = lineLen; + m_lastStatus = TUNED; + + if( compareWithTollerance ( lineLen, aTargetLength, m_settings.m_lengthTollerance ) > 0 ) + { + m_lastStatus = TOO_LONG; + } else { + m_lastLength = lineLen - tuned.Length( ); + tuneLineLength( m_result, aTargetLength - lineLen ); + } + + BOOST_FOREACH ( const PNS_ITEM *item, m_tunedPath.CItems( ) ) + { + if ( const PNS_LINE *l = dyn_cast( item ) ) + { + Router( )->DisplayDebugLine( l->CLine( ), 5, 10000 ); + } + } + + if (m_lastStatus != TOO_LONG) + { + tuned.Clear( ); + + BOOST_FOREACH ( PNS_MEANDER_SHAPE *m, m_result.Meanders() ) + { + if( m->Type() != MT_EMPTY ) + { + tuned.Append ( m->CLine(0) ); + } + } + + m_lastLength += tuned.Length( ); + + int comp = compareWithTollerance( m_lastLength - aTargetLength, 0, m_settings.m_lengthTollerance ); + + if( comp > 0 ) + m_lastStatus = TOO_LONG; + else if( comp < 0 ) + m_lastStatus = TOO_SHORT; + else + m_lastStatus = TUNED; + + } + + m_finalShape.Clear( ); + m_finalShape.Append( pre ); + m_finalShape.Append( tuned ); + m_finalShape.Append( post ); + m_finalShape.Simplify( ); + + return true; +} + + +bool PNS_MEANDER_PLACER::FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ) +{ + m_currentTrace = PNS_LINE( *m_originLine, m_finalShape ); + m_currentNode->Add ( &m_currentTrace ); + + Router( )->CommitRouting( m_currentNode ); + return true; +} + +bool PNS_MEANDER_PLACER::CheckFit( PNS_MEANDER_SHAPE *aShape ) +{ + PNS_LINE l( *m_originLine, aShape->CLine( 0 ) ); + + if( m_currentNode->CheckColliding( &l ) ) + return false; + + int w = aShape->Width( ); + int clearance = w + m_settings.m_spacing; + + return m_result.CheckSelfIntersections( aShape, clearance ); +} + + +const PNS_ITEMSET PNS_MEANDER_PLACER::Traces() +{ + m_currentTrace = PNS_LINE( *m_originLine, m_finalShape ); + return PNS_ITEMSET( &m_currentTrace ); +} + +const VECTOR2I& PNS_MEANDER_PLACER::CurrentEnd() const +{ + return m_currentEnd; +} + +int PNS_MEANDER_PLACER::CurrentNet() const +{ + return m_initialSegment->Net( ); +} + +int PNS_MEANDER_PLACER::CurrentLayer() const +{ + return m_initialSegment->Layers( ).Start( ); +} + + +const wxString PNS_MEANDER_PLACER::TuningInfo( ) const +{ + wxString status; + + switch ( m_lastStatus ) + { + case TOO_LONG: + status = _( "Too long: " ); + break; + case TOO_SHORT: + status = _(" Too short: " ); + break; + case TUNED: + status = _(" Tuned: " ); + break; + default: + return _( "?" ); + } + + status += LengthDoubleToString( (double) m_lastLength, false ); + status += "/"; + status += LengthDoubleToString( (double) m_settings.m_targetLength, false ); + + return status; +} + +PNS_MEANDER_PLACER::TUNING_STATUS PNS_MEANDER_PLACER::TuningStatus( ) const +{ + return m_lastStatus; +} diff --git a/pcbnew/router/pns_meander_placer.h b/pcbnew/router/pns_meander_placer.h new file mode 100644 index 0000000000..98c6670629 --- /dev/null +++ b/pcbnew/router/pns_meander_placer.h @@ -0,0 +1,115 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2014 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_MEANDER_PLACER_H +#define __PNS_MEANDER_PLACER_H + +#include + +#include +#include + +#include "pns_node.h" +#include "pns_via.h" +#include "pns_line.h" +#include "pns_placement_algo.h" +#include "pns_meander.h" +#include "pns_meander_placer_base.h" + +class PNS_ROUTER; +class PNS_SHOVE; +class PNS_OPTIMIZER; +class PNS_ROUTER_BASE; + +/** + * Class PNS_MEANDER_PLACER + * + * Single track length matching/meandering tool. + */ +class PNS_MEANDER_PLACER : public PNS_MEANDER_PLACER_BASE +{ +public: + + PNS_MEANDER_PLACER( PNS_ROUTER* aRouter ); + virtual ~PNS_MEANDER_PLACER(); + + /// @copydoc PNS_PLACEMENT_ALGO::Start() + virtual bool Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ); + + /// @copydoc PNS_PLACEMENT_ALGO::Move() + virtual bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ); + + /// @copydoc PNS_PLACEMENT_ALGO::FixRoute() + virtual bool FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ); + + /// @copydoc PNS_PLACEMENT_ALGO::CurrentNode() + PNS_NODE* CurrentNode( bool aLoopsRemoved = false ) const; + + /// @copydoc PNS_PLACEMENT_ALGO::Traces() + const PNS_ITEMSET Traces(); + + /// @copydoc PNS_PLACEMENT_ALGO::CurrentEnd() + const VECTOR2I& CurrentEnd() const; + + /// @copydoc PNS_PLACEMENT_ALGO::CurrentNet() + int CurrentNet() const; + + /// @copydoc PNS_PLACEMENT_ALGO::CurrentLayer() + int CurrentLayer() const; + + /// @copydoc PNS_MEANDER_PLACER_BASE::TuningInfo() + virtual const wxString TuningInfo() const; + + /// @copydoc PNS_MEANDER_PLACER_BASE::TuningStatus() + virtual TUNING_STATUS TuningStatus() const; + + /// @copydoc PNS_MEANDER_PLACER_BASE::CheckFit() + bool CheckFit ( PNS_MEANDER_SHAPE* aShape ); + +protected: + + bool doMove( const VECTOR2I& aP, PNS_ITEM* aEndItem, int aTargetLength ); + + void setWorld ( PNS_NODE* aWorld ); + + virtual int origPathLength () const; + + ///> pointer to world to search colliding items + PNS_NODE* m_world; + + ///> current routing start point (end of tail, beginning of head) + VECTOR2I m_currentStart; + + ///> Current world state + PNS_NODE* m_currentNode; + + PNS_LINE* m_originLine; + PNS_LINE m_currentTrace; + PNS_ITEMSET m_tunedPath; + + SHAPE_LINE_CHAIN m_finalShape; + PNS_MEANDERED_LINE m_result; + PNS_SEGMENT *m_initialSegment; + + int m_lastLength; + TUNING_STATUS m_lastStatus; +}; + +#endif // __PNS_MEANDER_PLACER_H diff --git a/pcbnew/router/pns_meander_placer_base.cpp b/pcbnew/router/pns_meander_placer_base.cpp new file mode 100644 index 0000000000..c35204a9d6 --- /dev/null +++ b/pcbnew/router/pns_meander_placer_base.cpp @@ -0,0 +1,167 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "pns_router.h" +#include "pns_meander.h" +#include "pns_meander_placer_base.h" + +PNS_MEANDER_PLACER_BASE::PNS_MEANDER_PLACER_BASE( PNS_ROUTER* aRouter ) : + PNS_PLACEMENT_ALGO( aRouter ) +{ + +}; + +PNS_MEANDER_PLACER_BASE::~PNS_MEANDER_PLACER_BASE( ) +{ + +}; + +void PNS_MEANDER_PLACER_BASE::AmplitudeStep( int aSign ) +{ + int a = m_settings.m_maxAmplitude + aSign * m_settings.m_step; + a = std::max( a, m_settings.m_minAmplitude ); + + m_settings.m_maxAmplitude = a; +} + +void PNS_MEANDER_PLACER_BASE::SpacingStep( int aSign ) +{ + int s = m_settings.m_spacing + aSign * m_settings.m_step; + s = std::max( s, 2 * m_currentWidth ); + + m_settings.m_spacing = s; +} + +void PNS_MEANDER_PLACER_BASE::UpdateSettings( const PNS_MEANDER_SETTINGS& aSettings ) +{ + m_settings = aSettings; +} + +void PNS_MEANDER_PLACER_BASE::cutTunedLine( const SHAPE_LINE_CHAIN& aOrigin, + const VECTOR2I& aTuneStart, + const VECTOR2I& aCursorPos, + SHAPE_LINE_CHAIN& aPre, + SHAPE_LINE_CHAIN& aTuned, + SHAPE_LINE_CHAIN& aPost ) +{ + VECTOR2I n = aOrigin.NearestPoint( aCursorPos ); + VECTOR2I m = aOrigin.NearestPoint( aTuneStart ); + + SHAPE_LINE_CHAIN l( aOrigin ); + l.Split( n ); + l.Split( m ); + + int i_start = l.Find( m ); + int i_end = l.Find( n ); + + if( i_start > i_end ) + { + l = l.Reverse( ); + i_start = l.Find( m ); + i_end = l.Find( n ); + } + + aPre = l.Slice( 0, i_start ); + aPost = l.Slice( i_end, -1 ); + aTuned = l.Slice( i_start, i_end ); + aTuned.Simplify( ); +} + +void PNS_MEANDER_PLACER_BASE::tuneLineLength( PNS_MEANDERED_LINE& aTuned, int aElongation ) +{ + int remaining = aElongation; + bool finished = false; + + BOOST_FOREACH(PNS_MEANDER_SHAPE *m, aTuned.Meanders()) + { + if(m->Type() != MT_CORNER ) + { + if(remaining >= 0) + remaining -= m->MaxTunableLength() - m->BaselineLength(); + + if(remaining < 0) + { + if(!finished) + { + PNS_MEANDER_TYPE newType; + + if ( m->Type() == MT_START || m->Type() == MT_SINGLE) + newType = MT_SINGLE; + else + newType = MT_FINISH; + + m->SetType ( newType ); + m->Recalculate( ); + + finished = true; + } else { + m->MakeEmpty(); + } + } + } + } + + remaining = aElongation; + int meanderCount = 0; + + BOOST_FOREACH(PNS_MEANDER_SHAPE *m, aTuned.Meanders()) + { + if( m->Type() != MT_CORNER && m->Type() != MT_EMPTY ) + { + if(remaining >= 0) + { + remaining -= m->MaxTunableLength() - m->BaselineLength(); + meanderCount ++; + } + } + } + + int balance = 0; + + if( meanderCount ) + balance = -remaining / meanderCount; + + if (balance >= 0) + { + BOOST_FOREACH(PNS_MEANDER_SHAPE *m, aTuned.Meanders()) + { + if(m->Type() != MT_CORNER && m->Type() != MT_EMPTY) + { + m->Resize ( std::max( m->Amplitude() - balance / 2, m_settings.m_minAmplitude ) ); + } + } + + } +} + +const PNS_MEANDER_SETTINGS& PNS_MEANDER_PLACER_BASE::MeanderSettings( ) const +{ + return m_settings; +} + +int PNS_MEANDER_PLACER_BASE::compareWithTollerance ( int aValue, int aExpected, int aTollerance ) const +{ + if( aValue < aExpected - aTollerance ) + return -1; + else if( aValue > aExpected + aTollerance ) + return 1; + else + return 0; +} \ No newline at end of file diff --git a/pcbnew/router/pns_meander_placer_base.h b/pcbnew/router/pns_meander_placer_base.h new file mode 100644 index 0000000000..ac94b97a67 --- /dev/null +++ b/pcbnew/router/pns_meander_placer_base.h @@ -0,0 +1,167 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_MEANDER_PLACER_BASE_H +#define __PNS_MEANDER_PLACER_BASE_H + +#include + +#include +#include + +#include "pns_node.h" +#include "pns_via.h" +#include "pns_line.h" +#include "pns_placement_algo.h" +#include "pns_meander.h" + +class PNS_ROUTER; +class PNS_SHOVE; +class PNS_OPTIMIZER; +class PNS_ROUTER_BASE; + +/** + * Class PNS_MEANDER_PLACER_BASE + * + * Base class for Single trace & Differenial pair meandering tools, as + * both of them share a lot of code. + */ +class PNS_MEANDER_PLACER_BASE : public PNS_PLACEMENT_ALGO +{ +public: + ///> Result of the length tuning operation + enum TUNING_STATUS { + TOO_SHORT = 0, + TOO_LONG, + TUNED + }; + + PNS_MEANDER_PLACER_BASE( PNS_ROUTER* aRouter ); + virtual ~PNS_MEANDER_PLACER_BASE( ); + + /** + * Function TuningInfo() + * + * Returns a string describing the status and length of the + * tuned traces. + */ + virtual const wxString TuningInfo( ) const = 0; + + /** + * Function TuningStatus() + * + * Returns the tuning status (too short, too long, etc.) + * of the trace(s) being tuned. + */ + virtual TUNING_STATUS TuningStatus( ) const = 0; + + /** + * Function AmplitudeStep() + * + * Increases/decreases the current meandering amplitude by one step. + * @param aSign direction (negative = decrease, positive = increase). + */ + virtual void AmplitudeStep( int aSign ); + + /** + * Function SpacingStep() + * + * Increases/decreases the current meandering spcing by one step. + * @param aSign direction (negative = decrease, positive = increase). + */ + virtual void SpacingStep( int aSign ); + + /** + * Function MeanderSettings() + * + * Returns the current meandering configuration. + * @return the settings + */ + virtual const PNS_MEANDER_SETTINGS& MeanderSettings() const; + + /* + * Function UpdateSettings() + * + * Sets the current meandering configuration. + * @param aSettings the settings + */ + virtual void UpdateSettings( const PNS_MEANDER_SETTINGS& aSettings); + + /** + * Function CheckFit() + * + * Checks if it's ok to place the shape aShape (i.e. + * if it doesn't cause DRC violations or collide with + * other meanders). + * @param aShape the shape to check + * @return true if the shape fits + */ + virtual bool CheckFit ( PNS_MEANDER_SHAPE* aShape ) + { + return false; + } + + + +protected: + + /** + * Function cutTunedLine() + * + * Extracts the part of a track to be meandered, depending on the + * starting point and the cursor position. + * @param aOrigin the original line + * @param aTuneStart point where we start meandering (start click coorinates) + * @param aCursorPos current cursor position + * @param aPre part before the beginning of meanders + * @param aTuned part to be meandered + * @param aPost part after the end of meanders + */ + void cutTunedLine( const SHAPE_LINE_CHAIN& aOrigin, + const VECTOR2I& aTuneStart, + const VECTOR2I& aCursorPos, + SHAPE_LINE_CHAIN& aPre, + SHAPE_LINE_CHAIN& aTuned, + SHAPE_LINE_CHAIN& aPost ); + + /** + * Function tuneLineLength() + * + * Takes a set of meanders in aTuned and tunes their length to + * extend the original line length by aElongation. + */ + void tuneLineLength( PNS_MEANDERED_LINE& aTuned, int aElongation ); + + /** + * Function compareWithTollerance() + * + * Compares aValue against aExpected with given tollerance. + */ + int compareWithTollerance ( int aValue, int aExpected, int aTollerance = 0 ) const; + + ///> width of the meandered trace(s) + int m_currentWidth; + ///> meandering settings + PNS_MEANDER_SETTINGS m_settings; + ///> current end point + VECTOR2I m_currentEnd; +}; + +#endif // __PNS_MEANDER_PLACER_BASE_H diff --git a/pcbnew/router/pns_meander_skew_placer.cpp b/pcbnew/router/pns_meander_skew_placer.cpp new file mode 100644 index 0000000000..5904dcd07d --- /dev/null +++ b/pcbnew/router/pns_meander_skew_placer.cpp @@ -0,0 +1,157 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include + +#include // God forgive me doing this... +#include + +#include "trace.h" + +#include "pns_node.h" +#include "pns_itemset.h" +#include "pns_topology.h" +#include "pns_meander_skew_placer.h" + +#include "pns_router.h" + + +PNS_MEANDER_SKEW_PLACER::PNS_MEANDER_SKEW_PLACER ( PNS_ROUTER* aRouter ) : + PNS_MEANDER_PLACER ( aRouter ) +{ +} + +PNS_MEANDER_SKEW_PLACER::~PNS_MEANDER_SKEW_PLACER( ) +{ + +} + +bool PNS_MEANDER_SKEW_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) +{ + VECTOR2I p; + + if( !aStartItem || !aStartItem->OfKind( PNS_ITEM::SEGMENT ) ) + { + Router( )->SetFailureReason( _( "Please select a differential pair trace you want to tune." ) ); + return false; + } + + m_initialSegment = static_cast( aStartItem ); + + p = m_initialSegment->Seg( ).NearestPoint( aP ); + + m_originLine = NULL; + m_currentNode = NULL; + m_currentStart = p; + + m_world = Router( )->GetWorld( )->Branch( ); + m_originLine = m_world->AssembleLine( m_initialSegment ); + + PNS_TOPOLOGY topo( m_world ); + m_tunedPath = topo.AssembleTrivialPath( m_initialSegment ); + + if( !topo.AssembleDiffPair ( m_initialSegment, m_originPair ) ) + { + Router()->SetFailureReason( _( "Unable to find complementary differential pair " + "net for skew tuning. Make sure the names of the nets belonging " + "to a differential pair end with either _N/_P or +/-." ) ); + return false; + } + + + m_originPair.SetGap ( Router()->Sizes().DiffPairGap() ); + + if( !m_originPair.PLine().SegmentCount() || + !m_originPair.NLine().SegmentCount() ) + return false; + + m_tunedPathP = topo.AssembleTrivialPath ( m_originPair.PLine().GetLink(0) ); + m_tunedPathN = topo.AssembleTrivialPath ( m_originPair.NLine().GetLink(0) ); + + m_world->Remove( m_originLine ); + + m_currentWidth = m_originLine->Width( ); + m_currentEnd = VECTOR2I( 0, 0 ); + + if ( m_originPair.PLine().Net () == m_originLine->Net() ) + m_coupledLength = itemsetLength ( m_tunedPathN ); + else + m_coupledLength = itemsetLength ( m_tunedPathP ); + + return true; +} + + +int PNS_MEANDER_SKEW_PLACER::origPathLength( ) const +{ + return itemsetLength ( m_tunedPath ); +} + +int PNS_MEANDER_SKEW_PLACER::itemsetLength( const PNS_ITEMSET& aSet ) const +{ + int total = 0; + BOOST_FOREACH( const PNS_ITEM *item, aSet.CItems( ) ) + { + if( const PNS_LINE *l = dyn_cast( item ) ) + { + total += l->CLine( ).Length( ); + } + } + + return total; +} + +int PNS_MEANDER_SKEW_PLACER::currentSkew () const +{ + return m_lastLength - m_coupledLength; +} + +bool PNS_MEANDER_SKEW_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ) +{ + return doMove ( aP, aEndItem, m_coupledLength + m_settings.m_targetSkew ); +} + + +const wxString PNS_MEANDER_SKEW_PLACER::TuningInfo( ) const +{ + wxString status; + + switch ( m_lastStatus ) + { + case TOO_LONG: + status = _( "Too long: skew " ); + break; + case TOO_SHORT: + status = _(" Too short: skew " ); + break; + case TUNED: + status = _(" Tuned: skew " ); + break; + default: + return _( "?" ); + } + + status += LengthDoubleToString( (double) m_lastLength - m_coupledLength, false ); + status += "/"; + status += LengthDoubleToString( (double) m_settings.m_targetSkew, false ); + + return status; +} + diff --git a/pcbnew/router/pns_meander_skew_placer.h b/pcbnew/router/pns_meander_skew_placer.h new file mode 100644 index 0000000000..a7f3a90773 --- /dev/null +++ b/pcbnew/router/pns_meander_skew_placer.h @@ -0,0 +1,66 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_MEANDER_SKEW_PLACER_H +#define __PNS_MEANDER_SKEW_PLACER_H + +#include "pns_meander_placer.h" +#include "pns_diff_pair.h" + +class PNS_ROUTER; +class PNS_SHOVE; +class PNS_OPTIMIZER; +class PNS_ROUTER_BASE; + +/** + * Class PNS_MEANDER_SKEW_PLACER + * + * Differential pair skew adjustment algorithm. + */ +class PNS_MEANDER_SKEW_PLACER : public PNS_MEANDER_PLACER +{ +public: + + PNS_MEANDER_SKEW_PLACER( PNS_ROUTER* aRouter ); + ~PNS_MEANDER_SKEW_PLACER(); + + /// @copydoc PNS_PLACEMENT_ALGO::Start() + bool Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ); + + /// @copydoc PNS_PLACEMENT_ALGO::Move() + bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ); + + /// @copydoc PNS_MEANDER_PLACER_BASE::TuningInfo() + const wxString TuningInfo() const; + +private: + + int currentSkew( ) const; + int itemsetLength( const PNS_ITEMSET& aSet ) const; + + int origPathLength () const; + + PNS_DIFF_PAIR m_originPair; + PNS_ITEMSET m_tunedPath, m_tunedPathP, m_tunedPathN; + + int m_coupledLength; +}; + +#endif // __PNS_MEANDER_SKEW_PLACER_H diff --git a/pcbnew/router/pns_node.cpp b/pcbnew/router/pns_node.cpp index 8ee7feeb3d..a553871d7f 100644 --- a/pcbnew/router/pns_node.cpp +++ b/pcbnew/router/pns_node.cpp @@ -53,6 +53,7 @@ PNS_NODE::PNS_NODE() m_parent = NULL; m_maxClearance = 800000; // fixme: depends on how thick traces are. m_index = new PNS_INDEX; + m_collisionFilter = NULL; #ifdef DEBUG allocNodes.insert( this ); @@ -109,6 +110,7 @@ PNS_NODE* PNS_NODE::Branch() child->m_parent = this; child->m_clearanceFunctor = m_clearanceFunctor; child->m_root = isRoot() ? this : m_root; + child->m_collisionFilter = m_collisionFilter; // immmediate offspring of the root branch needs not copy anything. // For the rest, deep-copy joints, overridden item map and pointers @@ -211,6 +213,9 @@ struct PNS_NODE::OBSTACLE_VISITOR int clearance = m_extraClearance + m_node->GetClearance( aItem, m_item ); + if( m_node->m_collisionFilter && (*m_node->m_collisionFilter)(aItem, m_item)) + return true; + if( aItem->Kind() == PNS_ITEM::LINE ) clearance += static_cast(aItem)->Width() / 2; @@ -220,6 +225,7 @@ struct PNS_NODE::OBSTACLE_VISITOR PNS_OBSTACLE obs; obs.m_item = aItem; + obs.m_head = m_item; m_tab.push_back( obs ); m_matchCount++; @@ -414,10 +420,14 @@ PNS_NODE::OPT_OBSTACLE PNS_NODE::CheckColliding( const PNS_ITEM* aItemA, int aKi } -bool PNS_NODE::CheckColliding( const PNS_ITEM* aItemA, const PNS_ITEM* aItemB, int aKindMask ) +bool PNS_NODE::CheckColliding( const PNS_ITEM* aItemA, const PNS_ITEM* aItemB, int aKindMask, int aForceClearance ) { assert( aItemB ); - int clearance = GetClearance( aItemA, aItemB ); + int clearance; + if( aForceClearance >= 0 ) + clearance = aForceClearance; + else + clearance = GetClearance( aItemA, aItemB ); // fixme: refactor if( aItemA->Kind() == PNS_ITEM::LINE ) @@ -713,6 +723,11 @@ void PNS_NODE::Remove( PNS_ITEM* aItem ) } } +void PNS_NODE::Remove ( PNS_LINE& aLine ) +{ + removeLine ( &aLine ); +} + void PNS_NODE::followLine( PNS_SEGMENT* aCurrent, bool aScanDirection, int& aPos, int aLimit, VECTOR2I* aCorners, PNS_SEGMENT** aSegments, bool& aGuardHit ) @@ -755,7 +770,7 @@ void PNS_NODE::followLine( PNS_SEGMENT* aCurrent, bool aScanDirection, int& aPos PNS_LINE* PNS_NODE::AssembleLine( PNS_SEGMENT* aSeg, int* aOriginSegmentIndex) { - const int MaxVerts = 1024; + const int MaxVerts = 1024 * 16; VECTOR2I corners[MaxVerts + 1]; PNS_SEGMENT* segs[MaxVerts + 1]; @@ -812,6 +827,7 @@ void PNS_NODE::FindLineEnds( PNS_LINE* aLine, PNS_JOINT& aA, PNS_JOINT& aB ) } +#if 0 void PNS_NODE::MapConnectivity ( PNS_JOINT* aStart, std::vector& aFoundJoints ) { std::deque searchQueue; @@ -846,51 +862,9 @@ void PNS_NODE::MapConnectivity ( PNS_JOINT* aStart, std::vector& aFo BOOST_FOREACH(PNS_JOINT* jt, processed) aFoundJoints.push_back( jt ); } +#endif -PNS_ITEM* PNS_NODE::NearestUnconnectedItem( PNS_JOINT* aStart, int* aAnchor, int aKindMask ) -{ - std::set disconnected; - std::vector joints; - - AllItemsInNet( aStart->Net(), disconnected ); - MapConnectivity ( aStart, joints ); - - BOOST_FOREACH( PNS_JOINT *jt, joints ) - { - BOOST_FOREACH( PNS_ITEM* link, jt->LinkList() ) - { - if( disconnected.find( link ) != disconnected.end() ) - disconnected.erase( link ); - } - } - - int best_dist = INT_MAX; - PNS_ITEM* best = NULL; - - BOOST_FOREACH( PNS_ITEM* item, disconnected ) - { - if( item->OfKind( aKindMask ) ) - { - for(int i = 0; i < item->AnchorCount(); i++) - { - VECTOR2I p = item->Anchor( i ); - int d = ( p - aStart->Pos() ).EuclideanNorm(); - - if( d < best_dist ) - { - best_dist = d; - best = item; - - if( aAnchor ) - *aAnchor = i; - } - } - } - } - - return best; -} int PNS_NODE::FindLinesBetweenJoints( PNS_JOINT& aA, PNS_JOINT& aB, std::vector& aLines ) @@ -1192,13 +1166,13 @@ void PNS_NODE::AllItemsInNet( int aNet, std::set& aItems ) } -void PNS_NODE::ClearRanks() +void PNS_NODE::ClearRanks( int aMarkerMask ) { - for( PNS_INDEX::ITEM_SET::iterator i = m_index->begin(); i != m_index->end(); ++i ) - { - (*i)->SetRank( -1 ); - (*i)->Mark( 0 ); - } + for( PNS_INDEX::ITEM_SET::iterator i = m_index->begin(); i != m_index->end(); ++i ) + { + (*i)->SetRank( -1 ); + (*i)->Mark( (*i)->Marker() & (~aMarkerMask) ); + } } @@ -1262,3 +1236,8 @@ PNS_SEGMENT* PNS_NODE::findRedundantSegment ( PNS_SEGMENT *aSeg ) return NULL; } + +void PNS_NODE::SetCollisionFilter ( PNS_COLLISION_FILTER *aFilter ) +{ + m_collisionFilter = aFilter; +} diff --git a/pcbnew/router/pns_node.h b/pcbnew/router/pns_node.h index 13e1672f6a..eb7b8b503e 100644 --- a/pcbnew/router/pns_node.h +++ b/pcbnew/router/pns_node.h @@ -52,8 +52,28 @@ class PNS_INDEX; class PNS_CLEARANCE_FUNC { public: - virtual int operator()( const PNS_ITEM* aA, const PNS_ITEM* aB ) = 0; virtual ~PNS_CLEARANCE_FUNC() {} + virtual int operator()( const PNS_ITEM* aA, const PNS_ITEM* aB ) = 0; + virtual void OverrideClearance (bool aEnable, int aNetA = 0, int aNetB = 0, int aClearance = 0) = 0; + +}; + +class PNS_PCBNEW_CLEARANCE_FUNC : public PNS_CLEARANCE_FUNC +{ +public: + PNS_PCBNEW_CLEARANCE_FUNC( BOARD *aBoard ); + virtual ~PNS_PCBNEW_CLEARANCE_FUNC(); + + virtual int operator()( const PNS_ITEM* aA, const PNS_ITEM* aB ); + virtual void OverrideClearance (bool aEnable, int aNetA = 0, int aNetB = 0, int aClearance = 0); + +private: + int localPadClearance( const PNS_ITEM* aItem ) const; + std::vector m_clearanceCache; + int m_defaultClearance; + bool m_overrideEnabled; + int m_overrideNetA, m_overrideNetB; + int m_overrideClearance; }; /** @@ -65,7 +85,7 @@ public: struct PNS_OBSTACLE { ///> Item we search collisions with - PNS_ITEM* m_head; + const PNS_ITEM* m_head; ///> Item found to be colliding with m_head PNS_ITEM* m_item; @@ -81,6 +101,15 @@ struct PNS_OBSTACLE int m_distFirst, m_distLast; }; +/** + * Struct PNS_COLLISION_FILTER + * Used to override the decision of the collision search algorithm whether two + * items collide. + **/ +struct PNS_COLLISION_FILTER { + virtual bool operator()( const PNS_ITEM *aItemA, const PNS_ITEM *aItemB ) const = 0; +}; + /** * Class PNS_NODE * @@ -201,7 +230,8 @@ public: */ bool CheckColliding( const PNS_ITEM* aItemA, const PNS_ITEM* aItemB, - int aKindMask = PNS_ITEM::ANY ); + int aKindMask = PNS_ITEM::ANY, + int aForceClearance = -1 ); /** * Function HitTest() @@ -230,6 +260,15 @@ public: */ void Remove( PNS_ITEM* aItem ); + /** + * Function Remove() + * + * Just as the name says, removes a line from this branch. + * @param aItem item to remove + */ + void Remove( PNS_LINE& aLine ); + + /** * Function Replace() * @@ -296,16 +335,18 @@ public: * Searches for a joint at a given position, linked to given item. * @return the joint, if found, otherwise empty */ - PNS_JOINT* FindJoint( const VECTOR2I& aPos, PNS_ITEM* aItem ) + PNS_JOINT* FindJoint( const VECTOR2I& aPos, const PNS_ITEM* aItem ) { return FindJoint( aPos, aItem->Layers().Start(), aItem->Net() ); } +#if 0 void MapConnectivity( PNS_JOINT* aStart, std::vector & aFoundJoints ); PNS_ITEM* NearestUnconnectedItem( PNS_JOINT* aStart, int *aAnchor = NULL, int aKindMask = PNS_ITEM::ANY); +#endif ///> finds all lines between a pair of joints. Used by the loop removal procedure. int FindLinesBetweenJoints( PNS_JOINT& aA, @@ -320,10 +361,11 @@ public: void AllItemsInNet( int aNet, std::set& aItems ); - void ClearRanks(); + void ClearRanks( int aMarkerMask = MK_HEAD | MK_VIOLATION ); int FindByMarker( int aMarker, PNS_ITEMSET& aItems ); int RemoveByMarker( int aMarker ); + void SetCollisionFilter ( PNS_COLLISION_FILTER *aFilter ); private: struct OBSTACLE_VISITOR; @@ -411,6 +453,9 @@ private: ///> depth of the node (number of parent nodes in the inheritance chain) int m_depth; + + ///> optional collision filtering object + PNS_COLLISION_FILTER *m_collisionFilter; }; #endif diff --git a/pcbnew/router/pns_optimizer.cpp b/pcbnew/router/pns_optimizer.cpp index 6451bca600..f6b6def28c 100644 --- a/pcbnew/router/pns_optimizer.cpp +++ b/pcbnew/router/pns_optimizer.cpp @@ -24,6 +24,7 @@ #include #include "pns_line.h" +#include "pns_diff_pair.h" #include "pns_node.h" #include "pns_optimizer.h" #include "pns_utils.h" @@ -121,6 +122,7 @@ PNS_OPTIMIZER::PNS_OPTIMIZER( PNS_NODE* aWorld ) : m_world( aWorld ), m_collisionKindMask( PNS_ITEM::ANY ), m_effortLevel( MERGE_SEGMENTS ) { // m_cache = new SHAPE_INDEX_LIST(); + m_restrictAreaActive = false; } @@ -222,6 +224,154 @@ void PNS_OPTIMIZER::ClearCache( bool aStaticOnly ) } } +class LINE_RESTRICTIONS +{ + public: + LINE_RESTRICTIONS( ) {}; + ~LINE_RESTRICTIONS( ) {}; + + void Build( PNS_NODE *aWorld, PNS_LINE *aOriginLine, const SHAPE_LINE_CHAIN& aLine, const BOX2I& aRestrictedArea, bool aRestrictedAreaEnable ); + bool Check ( int aVertex1, int aVertex2, const SHAPE_LINE_CHAIN& aReplacement ); + void Dump(); + + + private: + int allowedAngles ( PNS_NODE *aWorld, const PNS_LINE *aLine, const VECTOR2I& aP, bool aFirst ); + + struct RVERTEX + { + RVERTEX ( bool aRestricted, int aAllowedAngles ) : + restricted ( aRestricted ), + allowedAngles ( aAllowedAngles ) + { + + } + + bool restricted; + int allowedAngles; + }; + + std::vector m_rs; +}; + +// fixme: use later +int LINE_RESTRICTIONS::allowedAngles ( PNS_NODE *aWorld, const PNS_LINE *aLine, const VECTOR2I& aP, bool aFirst ) +{ + PNS_JOINT* jt = aWorld->FindJoint( aP , aLine ); + + if( !jt ) + return 0xff; + + + DIRECTION_45 dirs [8]; + + int n_dirs = 0; + + BOOST_FOREACH ( const PNS_ITEM *item, jt->Links().CItems() ) + { + if (item->OfKind (PNS_ITEM::VIA) || item->OfKind (PNS_ITEM::SOLID)) + return 0xff; + else if ( const PNS_SEGMENT *seg = dyn_cast ( item ) ) + { + SEG s = seg->Seg(); + if (s.A != aP) + s.Reverse(); + + if (n_dirs < 8) + dirs [ n_dirs++ ] = aFirst ? DIRECTION_45 ( s ) : DIRECTION_45 ( s ).Opposite(); + } + } + + + + const int angleMask = DIRECTION_45::ANG_OBTUSE | DIRECTION_45::ANG_HALF_FULL | DIRECTION_45::ANG_STRAIGHT; + int outputMask = 0xff; + + for (int d = 0; d < 8; d ++) + { + DIRECTION_45 refDir( (DIRECTION_45::Directions) d ); + for (int i = 0; i < n_dirs; i++ ) + { + if (! (refDir.Angle(dirs [ i ] ) & angleMask) ) + outputMask &= ~refDir.Mask(); + } + } + + DrawDebugDirs ( aP, outputMask, 3 ); + return 0xff; +} + + +void LINE_RESTRICTIONS::Build( PNS_NODE *aWorld, PNS_LINE *aOriginLine, const SHAPE_LINE_CHAIN& aLine, const BOX2I& aRestrictedArea, bool aRestrictedAreaEnable ) +{ + const SHAPE_LINE_CHAIN& l = aLine; + VECTOR2I v_prev; + int n = l.PointCount( ); + + m_rs.reserve( n ); + + for (int i = 0; i < n; i++) + { + const VECTOR2I &v = l.CPoint(i), v_next; + RVERTEX r ( false, 0xff ); + + if ( aRestrictedAreaEnable ) + { + bool exiting = ( i > 0 && aRestrictedArea.Contains( v_prev ) && !aRestrictedArea.Contains(v) ); + bool entering = false; + + if( i != l.PointCount() - 1) + { + const VECTOR2I& v_next = l.CPoint(i + 1); + entering = ( !aRestrictedArea.Contains( v ) && aRestrictedArea.Contains( v_next ) ); + } + + if(entering) + { + const SEG& sp = l.CSegment(i); + r.allowedAngles = DIRECTION_45(sp).Mask(); + } else if (exiting) { + const SEG& sp = l.CSegment(i - 1); + r.allowedAngles = DIRECTION_45(sp).Mask(); + } else { + r.allowedAngles = (! aRestrictedArea.Contains ( v ) ) ? 0 : 0xff; + r.restricted = r.allowedAngles ? false : true; + } + } + v_prev = v; + m_rs.push_back(r); + } +} + +void LINE_RESTRICTIONS::Dump() +{ +} + +bool LINE_RESTRICTIONS::Check ( int aVertex1, int aVertex2, const SHAPE_LINE_CHAIN& aReplacement ) +{ + if( m_rs.empty( ) ) + return true; + + for(int i = aVertex1; i <= aVertex2; i++) + if ( m_rs[i].restricted ) + return false; + + const RVERTEX& v1 = m_rs[ aVertex1 ]; + const RVERTEX& v2 = m_rs[ aVertex2 ]; + + int m1 = DIRECTION_45( aReplacement.CSegment( 0 ) ).Mask(); + int m2; + if (aReplacement.SegmentCount() == 1) + m2 = m1; + else + m2 = DIRECTION_45 ( aReplacement.CSegment( 1 ) ).Mask(); + + + return ((v1.allowedAngles & m1) != 0) && + ((v2.allowedAngles & m2) != 0); +} + + bool PNS_OPTIMIZER::checkColliding( PNS_ITEM* aItem, bool aUpdateCache ) { @@ -391,7 +541,7 @@ bool PNS_OPTIMIZER::mergeFull( PNS_LINE* aLine ) } -bool PNS_OPTIMIZER::Optimize( PNS_LINE* aLine, PNS_LINE* aResult )//, int aStartVertex, int aEndVertex ) +bool PNS_OPTIMIZER::Optimize( PNS_LINE* aLine, PNS_LINE* aResult ) { if( !aResult ) aResult = aLine; @@ -425,12 +575,16 @@ bool PNS_OPTIMIZER::mergeStep( PNS_LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, int cost_orig = PNS_COST_ESTIMATOR::CornerCost( aCurrentPath ); + LINE_RESTRICTIONS restr; + if( aLine->SegmentCount() < 4 ) return false; DIRECTION_45 orig_start( aLine->CSegment( 0 ) ); DIRECTION_45 orig_end( aLine->CSegment( -1 ) ); + restr.Build( m_world, aLine, aCurrentPath, m_restrictArea, m_restrictAreaActive ); + while( n < n_segs - step ) { const SEG s1 = aCurrentPath.CSegment( n ); @@ -446,13 +600,14 @@ bool PNS_OPTIMIZER::mergeStep( PNS_LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace( s1.A, s2.B, i ); cost[i] = INT_MAX; + bool restrictionsOK = restr.Check ( n, n + step + 1, bypass ); if( n == 0 && orig_start != DIRECTION_45( bypass.CSegment( 0 ) ) ) postureMatch = false; else if( n == n_segs - step && orig_end != DIRECTION_45( bypass.CSegment( -1 ) ) ) postureMatch = false; - if( (postureMatch || !m_keepPostures) && !checkColliding( aLine, bypass ) ) + if( restrictionsOK && (postureMatch || !m_keepPostures) && !checkColliding( aLine, bypass ) ) { path[i] = aCurrentPath; path[i].Replace( s1.Index(), s2.Index(), bypass ); @@ -782,7 +937,6 @@ bool PNS_OPTIMIZER::fanoutCleanup( PNS_LINE* aLine ) for(int i = 0; i < 2; i++ ) { SHAPE_LINE_CHAIN l2 = DIRECTION_45().BuildInitialTrace( p_start, p_end, i ); - PNS_ROUTER::GetInstance()->DisplayDebugLine( l2, 4, 10000 ); PNS_LINE repl; repl = PNS_LINE( *aLine, l2 ); @@ -796,3 +950,210 @@ bool PNS_OPTIMIZER::fanoutCleanup( PNS_LINE* aLine ) return false; } + +int findCoupledVertices ( const VECTOR2I& aVertex, const SEG& aOrigSeg, const SHAPE_LINE_CHAIN& aCoupled, PNS_DIFF_PAIR *aPair, int *aIndices ) +{ + int count = 0; + for ( int i = 0; i < aCoupled.SegmentCount(); i++ ) + { + SEG s = aCoupled.CSegment(i); + VECTOR2I projOverCoupled = s.LineProject ( aVertex ); + + + if( s.ApproxParallel ( aOrigSeg ) ) + { + int64_t dist = ( projOverCoupled - aVertex ).EuclideanNorm() - aPair->Width(); + + if( aPair->GapConstraint().Matches(dist) ) + { + *aIndices++ = i; + count++; + } + } + } + + return count; +} + +bool verifyDpBypass ( PNS_NODE *aNode, PNS_DIFF_PAIR *aPair, bool aRefIsP, const SHAPE_LINE_CHAIN& aNewRef, const SHAPE_LINE_CHAIN& aNewCoupled ) +{ + PNS_LINE refLine ( aRefIsP ? aPair->PLine() : aPair->NLine(), aNewRef ); + PNS_LINE coupledLine ( aRefIsP ? aPair->NLine() : aPair->PLine(), aNewCoupled ); + + if ( aNode->CheckColliding( &refLine, &coupledLine, PNS_ITEM::ANY, aPair->Gap() - 10 ) ) + return false; + + if ( aNode->CheckColliding ( &refLine ) ) + return false; + + if ( aNode->CheckColliding ( &coupledLine ) ) + return false; + + return true; +} + +bool coupledBypass ( PNS_NODE *aNode, PNS_DIFF_PAIR *aPair, bool aRefIsP, const SHAPE_LINE_CHAIN& aRef, const SHAPE_LINE_CHAIN& aRefBypass, const SHAPE_LINE_CHAIN& aCoupled, SHAPE_LINE_CHAIN& aNewCoupled ) +{ + int vStartIdx[1024]; // fixme: possible overflow + + int nStarts = findCoupledVertices ( aRefBypass.CPoint(0), aRefBypass.CSegment(0), aCoupled, aPair, vStartIdx ); + DIRECTION_45 dir( aRefBypass.CSegment(0) ); + + int64_t bestLength = -1; + bool found = false; + SHAPE_LINE_CHAIN bestBypass; + int si, ei; + + for(int i=0; i< nStarts ;i++) + for ( int j = 1; j < aCoupled.PointCount() - 1; j++) + { + int delta = std::abs ( vStartIdx[i] - j ); + if(delta > 1) + { + const VECTOR2I& vs = aCoupled.CPoint( vStartIdx[i] ); + SHAPE_LINE_CHAIN bypass = dir.BuildInitialTrace( vs, aCoupled.CPoint(j), dir.IsDiagonal() ); + + int64_t coupledLength = aPair->CoupledLength ( aRef, bypass ); + + SHAPE_LINE_CHAIN newCoupled = aCoupled; + + si = vStartIdx[i]; + ei = j; + + if(si < ei) + newCoupled.Replace( si, ei, bypass ); + else + newCoupled.Replace( ei, si, bypass.Reverse() ); + + if(coupledLength > bestLength && verifyDpBypass ( aNode, aPair, aRefIsP, aRef, newCoupled) ) + { + bestBypass = newCoupled; + bestLength = coupledLength; + found = true; + } + } + } + + + if(found) + aNewCoupled = bestBypass; + + return found; +} + +bool checkDpColliding ( PNS_NODE *aNode, PNS_DIFF_PAIR *aPair, bool aIsP, const SHAPE_LINE_CHAIN& aPath ) +{ + PNS_LINE tmp ( aIsP ? aPair->PLine() : aPair->NLine(), aPath ); + + return aNode->CheckColliding( &tmp ); +} + + +bool PNS_OPTIMIZER::mergeDpStep( PNS_DIFF_PAIR *aPair, bool aTryP, int step ) +{ + int n = 1; + + SHAPE_LINE_CHAIN currentPath = aTryP ? aPair->CP() : aPair->CN(); + SHAPE_LINE_CHAIN coupledPath = aTryP ? aPair->CN() : aPair->CP(); + + int n_segs = currentPath.SegmentCount() - 1; + + int64_t clenPre = aPair->CoupledLength ( currentPath, coupledPath ); + int64_t budget = clenPre / 10; // fixme: come up with somethig more intelligent here... + + while( n < n_segs - step ) + { + const SEG s1 = currentPath.CSegment( n ); + const SEG s2 = currentPath.CSegment( n + step ); + + DIRECTION_45 dir1 (s1); + DIRECTION_45 dir2 (s2); + + if( dir1.IsObtuse(dir2 ) ) + { + SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace( s1.A, s2.B, dir1.IsDiagonal() ); + SHAPE_LINE_CHAIN newRef; + SHAPE_LINE_CHAIN newCoup; + int64_t deltaCoupled = -1, deltaUni = -1; + + + newRef = currentPath; + newRef.Replace( s1.Index(), s2.Index(), bypass ); + + deltaUni = aPair->CoupledLength ( newRef, coupledPath ) - clenPre + budget; + + if ( coupledBypass ( m_world, aPair, aTryP, newRef, bypass, coupledPath, newCoup ) ) + { + deltaCoupled = aPair->CoupledLength ( newRef, newCoup ) - clenPre + budget; + + if(deltaCoupled >= 0) + { + newRef.Simplify(); + newCoup.Simplify(); + + aPair->SetShape ( newRef, newCoup, !aTryP ); + return true; + } + } + else if( deltaUni >= 0 && verifyDpBypass ( m_world, aPair, aTryP, newRef, coupledPath ) ) + { + newRef.Simplify(); + coupledPath.Simplify(); + + aPair->SetShape ( newRef, coupledPath, !aTryP ); + return true; + } + } + + n++; + } + + return false; +} + +bool PNS_OPTIMIZER::mergeDpSegments( PNS_DIFF_PAIR *aPair ) +{ + int step_p = aPair->CP().SegmentCount() - 2; + int step_n = aPair->CN().SegmentCount() - 2; + + while( 1 ) + { + int n_segs_p = aPair->CP().SegmentCount(); + int n_segs_n = aPair->CN().SegmentCount(); + + int max_step_p = n_segs_p - 2; + int max_step_n = n_segs_n - 2; + + if( step_p > max_step_p ) + step_p = max_step_p; + + if( step_n > max_step_n ) + step_n = max_step_n; + + if( step_p < 1 && step_n < 1) + break; + + bool found_anything_p = false; + bool found_anything_n = false; + + if(step_p > 1) + found_anything_p = mergeDpStep( aPair, true, step_p ); + + if(step_n > 1) + found_anything_n = mergeDpStep( aPair, false, step_n ); + + if( !found_anything_n && !found_anything_p ) + { + step_n--; + step_p--; + } + } + return true; +} + +bool PNS_OPTIMIZER::Optimize( PNS_DIFF_PAIR* aPair ) +{ + + + return mergeDpSegments ( aPair ); +} \ No newline at end of file diff --git a/pcbnew/router/pns_optimizer.h b/pcbnew/router/pns_optimizer.h index fb75b41a53..f48d6ec632 100644 --- a/pcbnew/router/pns_optimizer.h +++ b/pcbnew/router/pns_optimizer.h @@ -30,8 +30,9 @@ #include "range.h" class PNS_NODE; -class PNS_LINE; class PNS_ROUTER; +class PNS_LINE; +class PNS_DIFF_PAIR; /** * Class PNS_COST_ESTIMATOR @@ -101,6 +102,8 @@ public: static bool Optimize( PNS_LINE* aLine, int aEffortLevel, PNS_NODE* aWorld); bool Optimize( PNS_LINE* aLine, PNS_LINE* aResult = NULL ); + bool Optimize( PNS_DIFF_PAIR* aPair ); + void SetWorld( PNS_NODE* aNode ) { m_world = aNode; } void CacheStaticItem( PNS_ITEM* aItem ); @@ -117,6 +120,13 @@ public: m_effortLevel = aEffort; } + + void SetRestrictArea( const BOX2I& aArea ) + { + m_restrictArea = aArea; + m_restrictAreaActive = true; + } + private: static const int MaxCachedItems = 256; @@ -136,6 +146,8 @@ private: bool runSmartPads( PNS_LINE* aLine ); bool mergeStep( PNS_LINE* aLine, SHAPE_LINE_CHAIN& aCurrentLine, int step ); bool fanoutCleanup( PNS_LINE * aLine ); + bool mergeDpSegments( PNS_DIFF_PAIR *aPair ); + bool mergeDpStep( PNS_DIFF_PAIR *aPair, bool aTryP, int step ); bool checkColliding( PNS_ITEM* aItem, bool aUpdateCache = true ); bool checkColliding( PNS_LINE* aLine, const SHAPE_LINE_CHAIN& aOptPath ); @@ -160,6 +172,9 @@ private: int m_collisionKindMask; int m_effortLevel; bool m_keepPostures; + + BOX2I m_restrictArea; + bool m_restrictAreaActive; }; #endif diff --git a/pcbnew/router/pns_placement_algo.h b/pcbnew/router/pns_placement_algo.h new file mode 100644 index 0000000000..7de571692e --- /dev/null +++ b/pcbnew/router/pns_placement_algo.h @@ -0,0 +1,189 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2014 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_PLACEMENT_ALGO_H +#define __PNS_PLACEMENT_ALGO_H + +#include + +#include "pns_algo_base.h" +#include "pns_sizes_settings.h" +#include "pns_itemset.h" + +class PNS_ROUTER; +class PNS_ITEM; +class PNS_NODE; + +/** + * Class PNS_PLACEMENT_ALGO + * + * Abstract class for a P&S placement/dragging algorithm. + * All subtools (drag, single/diff pair routing and meandering) + * are derived from it. + */ + +class PNS_PLACEMENT_ALGO : public PNS_ALGO_BASE +{ +public: + PNS_PLACEMENT_ALGO( PNS_ROUTER* aRouter ) : + PNS_ALGO_BASE( aRouter ) {}; + + virtual ~PNS_PLACEMENT_ALGO () {}; + + /** + * Function Start() + * + * Starts placement/drag operation at point aP, taking item aStartItem as anchor + * (unless NULL). + */ + virtual bool Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) = 0; + + /** + * Function Move() + * + * Moves the end of the currently routed primtive(s) to the point aP, taking + * aEndItem as the anchor (if not NULL). + * (unless NULL). + */ + virtual bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ) = 0; + + /** + * Function FixRoute() + * + * Commits the currently routed items to the parent node, taking + * aP as the final end point and aEndItem as the final anchor (if provided). + * @return true, if route has been commited. May return false if the routing + * result is violating design rules - in such case, the track is only committed + * if Settings.CanViolateDRC() is on. + */ + virtual bool FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ) = 0; + + /** + * Function ToggleVia() + * + * Enables/disables a via at the end of currently routed trace. + */ + virtual bool ToggleVia( bool aEnabled ) + { + return false; + } + + /** + * Function IsPlacingVia() + * + * Returns true if the placer is placing a via (or more vias). + */ + virtual bool IsPlacingVia() const + { + return false; + } + + /** + * Function SetLayer() + * + * Sets the current routing layer. + */ + virtual bool SetLayer( int aLayer ) + { + return false; + } + + /** + * Function Traces() + * + * Returns all routed/tuned traces. + */ + virtual const PNS_ITEMSET Traces() = 0; + + /** + * Function CurrentEnd() + * + * Returns the current end of the line being placed/tuned. It may not be equal + * to the cursor position due to collisions. + */ + virtual const VECTOR2I& CurrentEnd() const = 0; + + /** + * Function CurrentNet() + * + * Returns the net code of currently routed track. + */ + virtual int CurrentNet() const = 0; + + /** + * Function CurrentLayer() + * + * Returns the layer of currently routed track. + */ + virtual int CurrentLayer() const = 0; + + /** + * Function CurrentNode() + * + * Returns the most recent board state. + */ + virtual PNS_NODE* CurrentNode( bool aLoopsRemoved = false ) const = 0; + + /** + * Function FlipPosture() + * + * Toggles the current posture (straight/diagonal) of the trace head. + */ + virtual void FlipPosture() + { + + } + + /** + * Function UpdateSizes() + * + * Performs on-the-fly update of the width, via diameter & drill size from + * a settings class. Used to dynamically change these parameters as + * the track is routed. + */ + virtual void UpdateSizes( const PNS_SIZES_SETTINGS& aSizes ) + { + + } + + /** + * Function SetOrthoMode() + * + * Forces the router to place a straight 90/45 degree trace (with the end + * as near to the cursor as possible) instead of a standard 135 degree + * two-segment bend. + */ + virtual void SetOrthoMode ( bool aOrthoMode ) + { + + } + + /** + * Function GetModifiedNets + * + * Returns the net codes of all currently routed trace(s) + */ + virtual void GetModifiedNets( std::vector &aNets ) const + { + + } +}; + +#endif diff --git a/pcbnew/router/pns_router.cpp b/pcbnew/router/pns_router.cpp index d8c9f23678..c1a78c97e6 100644 --- a/pcbnew/router/pns_router.cpp +++ b/pcbnew/router/pns_router.cpp @@ -44,6 +44,12 @@ #include "pns_router.h" #include "pns_shove.h" #include "pns_dragger.h" +#include "pns_diff_pair_placer.h" +#include "pns_meander_placer.h" +#include "pns_meander_skew_placer.h" +#include "pns_dp_meander_placer.h" + + #include @@ -58,66 +64,85 @@ // To be fixed sometime in the future. static PNS_ROUTER* theRouter; -class PCBNEW_CLEARANCE_FUNC : public PNS_CLEARANCE_FUNC + +PNS_PCBNEW_CLEARANCE_FUNC::PNS_PCBNEW_CLEARANCE_FUNC( BOARD* aBoard ) { -public: - PCBNEW_CLEARANCE_FUNC( BOARD* aBoard ) + m_clearanceCache.resize( aBoard->GetNetCount() ); + + for( unsigned int i = 0; i < aBoard->GetNetCount(); i++ ) { - m_clearanceCache.resize( aBoard->GetNetCount() ); + NETINFO_ITEM* ni = aBoard->FindNet( i ); + if( ni == NULL ) + continue; - for( unsigned int i = 0; i < aBoard->GetNetCount(); i++ ) - { - NETINFO_ITEM* ni = aBoard->FindNet( i ); - if( ni == NULL ) - continue; - - wxString netClassName = ni->GetClassName(); - NETCLASSPTR nc = aBoard->GetDesignSettings().m_NetClasses.Find( netClassName ); - int clearance = nc->GetClearance(); - m_clearanceCache[i] = clearance; - TRACE( 1, "Add net %d netclass %s clearance %d", i % netClassName.mb_str() % - clearance ); - } - - m_defaultClearance = 254000; // aBoard->m_NetClasses.Find ("Default clearance")->GetClearance(); + wxString netClassName = ni->GetClassName(); + NETCLASSPTR nc = aBoard->GetDesignSettings().m_NetClasses.Find( netClassName ); + int clearance = nc->GetClearance(); + m_clearanceCache[i] = clearance; + TRACE( 1, "Add net %d netclass %s clearance %d", i % netClassName.mb_str() % + clearance ); } - int localPadClearance( const PNS_ITEM* aItem ) const + m_overrideEnabled = false; + m_defaultClearance = 254000; // aBoard->m_NetClasses.Find ("Default clearance")->GetClearance(); +} + +PNS_PCBNEW_CLEARANCE_FUNC::~PNS_PCBNEW_CLEARANCE_FUNC() +{ + +} + +int PNS_PCBNEW_CLEARANCE_FUNC::localPadClearance( const PNS_ITEM* aItem ) const +{ + if( !aItem->Parent() || aItem->Parent()->Type() != PCB_PAD_T ) + return 0; + + const D_PAD* pad = static_cast( aItem->Parent() ); + return pad->GetLocalClearance(); +} + +int PNS_PCBNEW_CLEARANCE_FUNC::operator()( const PNS_ITEM* aA, const PNS_ITEM* aB ) +{ + int net_a = aA->Net(); + int cl_a = ( net_a >= 0 ? m_clearanceCache[net_a] : m_defaultClearance ); + int net_b = aB->Net(); + int cl_b = ( net_b >= 0 ? m_clearanceCache[net_b] : m_defaultClearance ); + + if(m_overrideEnabled && aA->OfKind(PNS_ITEM::SEGMENT) && aB->OfKind(PNS_ITEM::SEGMENT)) { - if( !aItem->Parent() || aItem->Parent()->Type() != PCB_PAD_T ) - return 0; - - const D_PAD* pad = static_cast( aItem->Parent() ); - - return pad->GetLocalClearance(); + if( net_a == m_overrideNetA && net_b == m_overrideNetB ) + return m_overrideClearance; + else if( net_a == m_overrideNetB && net_b == m_overrideNetA ) + return m_overrideClearance; } - int operator()( const PNS_ITEM* aA, const PNS_ITEM* aB ) - { - int net_a = aA->Net(); - int cl_a = ( net_a >= 0 ? m_clearanceCache[net_a] : m_defaultClearance ); - int net_b = aB->Net(); - int cl_b = ( net_b >= 0 ? m_clearanceCache[net_b] : m_defaultClearance ); + int pad_a = localPadClearance( aA ); + int pad_b = localPadClearance( aB ); - int pad_a = localPadClearance( aA ); - int pad_b = localPadClearance( aB ); + cl_a = std::max( cl_a, pad_a ); + cl_b = std::max( cl_b, pad_b ); - cl_a = std::max( cl_a, pad_a ); - cl_b = std::max( cl_b, pad_b ); - - return std::max( cl_a, cl_b ); - } - -private: - std::vector m_clearanceCache; - int m_defaultClearance; + return std::max( cl_a, cl_b ); }; +// fixme: ugly hack to make the optimizer respect gap width for currently routed differential pair. +void PNS_PCBNEW_CLEARANCE_FUNC::OverrideClearance (bool aEnable, int aNetA , int aNetB , int aClearance ) +{ + m_overrideEnabled = aEnable; + m_overrideNetA = aNetA; + m_overrideNetB = aNetB; + m_overrideClearance = aClearance; +} + PNS_ITEM* PNS_ROUTER::syncPad( D_PAD* aPad ) { PNS_LAYERSET layers( 0, MAX_CU_LAYERS - 1 ); + // ignore non-copper pads + if ( (aPad->GetLayerSet() & LSET::AllCuMask()).none() ) + return NULL; + switch( aPad->GetAttribute() ) { case PAD_STANDARD: @@ -222,6 +247,8 @@ PNS_ITEM* PNS_ROUTER::syncTrack( TRACK* aTrack ) PNS_SEGMENT* s = new PNS_SEGMENT( SEG( aTrack->GetStart(), aTrack->GetEnd() ), aTrack->GetNetCode() ); + if( aTrack->GetFlags( ) & DP_COUPLED ) + s->Mark ( MK_DP_COUPLED ); s->SetWidth( aTrack->GetWidth() ); s->SetLayers( PNS_LAYERSET( aTrack->GetLayer() ) ); s->SetParent( aTrack ); @@ -265,7 +292,7 @@ void PNS_ROUTER::SyncWorld() int worstClearance = m_board->GetDesignSettings().GetBiggestClearanceValue(); - m_clearanceFunc = new PCBNEW_CLEARANCE_FUNC( m_board ); + m_clearanceFunc = new PNS_PCBNEW_CLEARANCE_FUNC( m_board ); m_world = new PNS_NODE(); m_world->SetClearanceFunctor( m_clearanceFunc ); m_world->SetMaxClearance( 4 * worstClearance ); @@ -309,6 +336,7 @@ PNS_ROUTER::PNS_ROUTER() m_previewItems = NULL; m_board = NULL; m_dragger = NULL; + m_mode = PNS_MODE_ROUTE_SINGLE; } @@ -460,17 +488,45 @@ bool PNS_ROUTER::StartDragging( const VECTOR2I& aP, PNS_ITEM* aStartItem ) bool PNS_ROUTER::StartRouting( const VECTOR2I& aP, PNS_ITEM* aStartItem, int aLayer ) { - m_placer = new PNS_LINE_PLACER( this ); + switch (m_mode) + { + case PNS_MODE_ROUTE_SINGLE: + m_placer = new PNS_LINE_PLACER( this ); + break; + case PNS_MODE_ROUTE_DIFF_PAIR: + m_placer = new PNS_DIFF_PAIR_PLACER( this ); + break; + case PNS_MODE_TUNE_SINGLE: + m_placer = new PNS_MEANDER_PLACER( this ); + break; + case PNS_MODE_TUNE_DIFF_PAIR: + m_placer = new PNS_DP_MEANDER_PLACER( this ); + break; + case PNS_MODE_TUNE_DIFF_PAIR_SKEW: + m_placer = new PNS_MEANDER_SKEW_PLACER( this ); + break; + + default: + return false; + } m_placer->UpdateSizes ( m_sizes ); m_placer->SetLayer( aLayer ); - m_placer->Start( aP, aStartItem ); + + bool rv = m_placer->Start( aP, aStartItem ); + + if(!rv) + return false; m_currentEnd = aP; m_currentEndItem = NULL; m_state = ROUTE_TRACK; + return rv; +} - return true; +BOARD *PNS_ROUTER::GetBoard() +{ + return m_board; } void PNS_ROUTER::eraseView() @@ -652,15 +708,25 @@ void PNS_ROUTER::movePlacing( const VECTOR2I& aP, PNS_ITEM* aEndItem ) eraseView(); m_placer->Move( aP, aEndItem ); - PNS_LINE current = m_placer->Trace(); + PNS_ITEMSET current = m_placer->Traces(); - DisplayItem( ¤t ); - if( current.EndsWithVia() ) - DisplayItem( ¤t.Via() ); + BOOST_FOREACH( const PNS_ITEM* item, current.CItems() ) + { + if( !item->OfKind ( PNS_ITEM::LINE ) ) + continue; - PNS_ITEMSET tmp( ¤t ); - updateView( m_placer->CurrentNode( true ), tmp ); + const PNS_LINE *l = static_cast (item); + DisplayItem(l); + + if( l->EndsWithVia() ) + DisplayItem( &l->Via() ); + } + + + //PNS_ITEMSET tmp( ¤t ); + + updateView( m_placer->CurrentNode( true ), current ); } @@ -765,11 +831,13 @@ bool PNS_ROUTER::FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ) void PNS_ROUTER::StopRouting() { // Update the ratsnest with new changes + if( m_placer ) { - int n = m_placer->CurrentNet(); + std::vector nets; + m_placer->GetModifiedNets( nets ); - if( n >= 0) + BOOST_FOREACH ( int n, nets ) { // Update the ratsnest with new changes m_board->GetRatsnest()->Recalculate( n ); @@ -871,3 +939,17 @@ bool PNS_ROUTER::IsPlacingVia() const return m_placer->IsPlacingVia(); } +void PNS_ROUTER::SetOrthoMode( bool aEnable ) +{ + if(!m_placer) + return; + + m_placer->SetOrthoMode ( aEnable ); +} + +void PNS_ROUTER::SetMode ( PNS_ROUTER_MODE aMode ) +{ + m_mode = aMode; +} + + diff --git a/pcbnew/router/pns_router.h b/pcbnew/router/pns_router.h index 0969bade68..48fed80321 100644 --- a/pcbnew/router/pns_router.h +++ b/pcbnew/router/pns_router.h @@ -41,6 +41,8 @@ class D_PAD; class TRACK; class VIA; class PNS_NODE; +class PNS_DIFF_PAIR_PLACER; +class PNS_PLACEMENT_ALGO; class PNS_LINE_PLACER; class PNS_ITEM; class PNS_LINE; @@ -59,6 +61,14 @@ namespace KIGFX }; +enum PNS_ROUTER_MODE { + PNS_MODE_ROUTE_SINGLE = 1, + PNS_MODE_ROUTE_DIFF_PAIR, + PNS_MODE_TUNE_SINGLE, + PNS_MODE_TUNE_DIFF_PAIR, + PNS_MODE_TUNE_DIFF_PAIR_SKEW +}; + /** * Class PNS_ROUTER * @@ -78,6 +88,9 @@ public: PNS_ROUTER(); ~PNS_ROUTER(); + void SetMode ( PNS_ROUTER_MODE aMode ); + PNS_ROUTER_MODE Mode() const { return m_mode; } + static PNS_ROUTER* GetInstance(); void ClearWorld(); @@ -113,6 +126,7 @@ public: void SwitchLayer( int layer ); void ToggleViaPlacement(); + void SetOrthoMode ( bool aEnable ); int GetCurrentLayer() const; int GetCurrentNet() const; @@ -193,6 +207,15 @@ public: return m_sizes; } + PNS_ITEM *QueryItemByParent ( const BOARD_ITEM *aItem ) const; + + BOARD *GetBoard(); + + void SetFailureReason ( const wxString& aReason ) { m_failureReason = aReason; } + const wxString& FailureReason() const { return m_failureReason; } + + PNS_PLACEMENT_ALGO *Placer() { return m_placer; } + private: void movePlacing( const VECTOR2I& aP, PNS_ITEM* aItem ); void moveDragging( const VECTOR2I& aP, PNS_ITEM* aItem ); @@ -225,7 +248,7 @@ private: BOARD* m_board; PNS_NODE* m_world; PNS_NODE* m_lastNode; - PNS_LINE_PLACER* m_placer; + PNS_PLACEMENT_ALGO * m_placer; PNS_DRAGGER* m_dragger; PNS_SHOVE* m_shove; int m_iterLimit; @@ -250,6 +273,11 @@ private: ///> Stores list of modified items in the current operation PICKED_ITEMS_LIST m_undoBuffer; PNS_SIZES_SETTINGS m_sizes; + PNS_ROUTER_MODE m_mode; + + wxString m_toolStatusbarName; + wxString m_failureReason; + }; #endif diff --git a/pcbnew/router/pns_routing_settings.cpp b/pcbnew/router/pns_routing_settings.cpp index bb67840fbe..81ae5f7b5f 100644 --- a/pcbnew/router/pns_routing_settings.cpp +++ b/pcbnew/router/pns_routing_settings.cpp @@ -24,7 +24,7 @@ PNS_ROUTING_SETTINGS::PNS_ROUTING_SETTINGS() { m_routingMode = RM_Walkaround; - m_optimizerEffort = OE_FULL; + m_optimizerEffort = OE_MEDIUM; m_removeLoops = true; m_smartPads = true; m_shoveVias = true; diff --git a/pcbnew/router/pns_segment.h b/pcbnew/router/pns_segment.h index 2550526c2e..dc9e870cdd 100644 --- a/pcbnew/router/pns_segment.h +++ b/pcbnew/router/pns_segment.h @@ -55,6 +55,11 @@ public: m_rank = aParentLine.Rank(); }; + static inline bool ClassOf( const PNS_ITEM* aItem ) + { + return aItem && SEGMENT == aItem->Kind(); + } + PNS_SEGMENT* Clone( ) const; const SHAPE* Shape() const diff --git a/pcbnew/router/pns_shove.cpp b/pcbnew/router/pns_shove.cpp index 1338557f1a..48a3227088 100644 --- a/pcbnew/router/pns_shove.cpp +++ b/pcbnew/router/pns_shove.cpp @@ -36,11 +36,36 @@ #include "pns_utils.h" #include "pns_router.h" #include "pns_shove.h" +#include "pns_utils.h" #include "time_limit.h" #include +void PNS_SHOVE::replaceItems ( PNS_ITEM *aOld, PNS_ITEM *aNew ) +{ + OPT_BOX2I changed_area = ChangedArea( aOld, aNew ); + + + if(changed_area) + { + assert ( !changed_area->Contains ( VECTOR2I (0, 0 ) ) ); + + m_affectedAreaSum = m_affectedAreaSum ? m_affectedAreaSum->Merge ( *changed_area ) : *changed_area; + } + + m_currentNode->Replace( aOld, aNew ); +} + + +int PNS_SHOVE::getClearance( PNS_ITEM *aA, PNS_ITEM *aB ) const +{ + if(m_forceClearance >= 0) + return m_forceClearance; + + return m_currentNode->GetClearance( aA, aB ); +} + static void sanityCheck( PNS_LINE* aOld, PNS_LINE* aNew ) { assert( aOld->CPoint( 0 ) == aNew->CPoint( 0 ) ); @@ -51,6 +76,7 @@ static void sanityCheck( PNS_LINE* aOld, PNS_LINE* aNew ) PNS_SHOVE::PNS_SHOVE( PNS_NODE* aWorld, PNS_ROUTER* aRouter ) : PNS_ALGO_BASE ( aRouter ) { + m_forceClearance = -1; m_root = aWorld; } @@ -59,7 +85,7 @@ PNS_SHOVE::~PNS_SHOVE() { // free all the stuff we've created during routing/dragging operation. BOOST_FOREACH( PNS_ITEM* item, m_gcItems ) - delete item; + delete item; } @@ -73,17 +99,6 @@ PNS_LINE* PNS_SHOVE::assembleLine( const PNS_SEGMENT* aSeg, int* aIndex ) return l; } - -// garbage-collected line cloning -PNS_LINE* PNS_SHOVE::cloneLine ( const PNS_LINE* aLine ) -{ - PNS_LINE* l = aLine->Clone(); - - m_gcItems.push_back( l ); - return l; -} - - // A dumb function that checks if the shoved line is shoved the right way, e.g. // visually "outwards" of the line/via applying pressure on it. Unfortunately there's no // mathematical concept of orientation of an open curve, so we use some primitive heuristics: @@ -92,7 +107,7 @@ bool PNS_SHOVE::checkBumpDirection( PNS_LINE* aCurrent, PNS_LINE* aShoved ) cons { const SEG ss = aCurrent->CSegment( 0 ); - int dist = m_currentNode->GetClearance( aCurrent, aShoved ) + PNS_HULL_MARGIN; + int dist = getClearance( aCurrent, aShoved ) + PNS_HULL_MARGIN; dist += aCurrent->Width() / 2; dist += aShoved->Width() / 2; @@ -106,7 +121,7 @@ bool PNS_SHOVE::checkBumpDirection( PNS_LINE* aCurrent, PNS_LINE* aShoved ) cons PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::walkaroundLoneVia( PNS_LINE* aCurrent, PNS_LINE* aObstacle, PNS_LINE* aShoved ) { - int clearance = m_currentNode->GetClearance( aCurrent, aObstacle ); + int clearance = getClearance( aCurrent, aObstacle ); const SHAPE_LINE_CHAIN hull = aCurrent->Via().Hull( clearance, aObstacle->Width() ); SHAPE_LINE_CHAIN path_cw, path_ccw; @@ -204,7 +219,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::processHullSet( PNS_LINE* aCurrent, PNS_LINE* continue; } - bool colliding = m_currentNode->CheckColliding( &l, aCurrent ); + bool colliding = m_currentNode->CheckColliding( &l, aCurrent, PNS_ITEM::ANY, m_forceClearance ); if( ( aCurrent->Marker() & MK_HEAD ) && !colliding ) { @@ -232,7 +247,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::processHullSet( PNS_LINE* aCurrent, PNS_LINE* } -PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::processSingleLine( PNS_LINE* aCurrent, PNS_LINE* aObstacle, +PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ProcessSingleLine( PNS_LINE* aCurrent, PNS_LINE* aObstacle, PNS_LINE* aShoved ) { aShoved->ClearSegmentLinks(); @@ -262,7 +277,8 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::processSingleLine( PNS_LINE* aCurrent, PNS_LI { int w = aObstacle->Width(); int n_segs = aCurrent->SegmentCount(); - int clearance = m_currentNode->GetClearance( aCurrent, aObstacle ); + + int clearance = getClearance( aCurrent, aObstacle ); HULL_SET hulls; @@ -291,19 +307,24 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSegment( PNS_LINE* aCurrent, PNS_S { int segIndex; PNS_LINE* obstacleLine = assembleLine( aObstacleSeg, &segIndex ); - PNS_LINE* shovedLine = cloneLine( obstacleLine ); + PNS_LINE* shovedLine = clone( obstacleLine ); - SHOVE_STATUS rv = processSingleLine( aCurrent, obstacleLine, shovedLine ); + SHOVE_STATUS rv = ProcessSingleLine( aCurrent, obstacleLine, shovedLine ); assert ( obstacleLine->LayersOverlap( shovedLine ) ); if( rv == SH_OK ) { if( shovedLine->Marker() & MK_HEAD ) + { + if( m_multiLineMode ) + return SH_INCOMPLETE; + m_newHead = *shovedLine; + } sanityCheck( obstacleLine, shovedLine ); - m_currentNode->Replace( obstacleLine, shovedLine ); + replaceItems ( obstacleLine, shovedLine ); sanityCheck( obstacleLine, shovedLine ); int rank = aCurrent->Rank(); @@ -326,17 +347,24 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSegment( PNS_LINE* aCurrent, PNS_S PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingLine( PNS_LINE* aCurrent, PNS_LINE* aObstacle ) { - PNS_LINE* shovedLine = cloneLine( aObstacle ); + PNS_LINE* shovedLine = clone( aObstacle ); + + SHOVE_STATUS rv = ProcessSingleLine( aCurrent, aObstacle, shovedLine ); + - SHOVE_STATUS rv = processSingleLine( aCurrent, aObstacle, shovedLine ); if( rv == SH_OK ) { if( shovedLine->Marker() & MK_HEAD ) + { + if (m_multiLineMode) + return SH_INCOMPLETE; + m_newHead = *shovedLine; + } sanityCheck( aObstacle, shovedLine ); - m_currentNode->Replace( aObstacle, shovedLine ); + replaceItems( aObstacle, shovedLine ); sanityCheck( aObstacle, shovedLine ); int rank = aObstacle->Rank(); @@ -358,8 +386,10 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingLine( PNS_LINE* aCurrent, PNS_LINE PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSolid( PNS_LINE* aCurrent, PNS_SOLID* aObstacleSolid ) { + //printf("pre2-v %d\n", aCurrent->EndsWithVia()); + PNS_WALKAROUND walkaround( m_currentNode, Router() ); - PNS_LINE* walkaroundLine = cloneLine( aCurrent ); + PNS_LINE* walkaroundLine = clone( aCurrent ); if( aCurrent->EndsWithVia() ) { @@ -413,10 +443,14 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSolid( PNS_LINE* aCurrent, PNS_SOL if( aCurrent->Marker() & MK_HEAD ) { walkaroundLine->Mark( MK_HEAD ); + + if(m_multiLineMode) + return SH_INCOMPLETE; + m_newHead = *walkaroundLine; } - m_currentNode->Replace( aCurrent, walkaroundLine ); + replaceItems ( aCurrent, walkaroundLine ); walkaroundLine->SetRank( nextRank ); #ifdef DEBUG @@ -447,7 +481,7 @@ bool PNS_SHOVE::reduceSpringback( const PNS_ITEMSET& aHeadSet ) delete spTag.m_node; m_nodeStack.pop_back(); - } + } else break; } @@ -457,32 +491,59 @@ bool PNS_SHOVE::reduceSpringback( const PNS_ITEMSET& aHeadSet ) bool PNS_SHOVE::pushSpringback( PNS_NODE* aNode, const PNS_ITEMSET& aHeadItems, - const PNS_COST_ESTIMATOR& aCost ) + const PNS_COST_ESTIMATOR& aCost, const OPT_BOX2I& aAffectedArea ) { SPRINGBACK_TAG st; + OPT_BOX2I prev_area; + + if(!m_nodeStack.empty()) + prev_area = m_nodeStack.back().m_affectedArea; + st.m_node = aNode; st.m_cost = aCost; st.m_headItems = aHeadItems; + + if( aAffectedArea ) + { + if( prev_area ) + st.m_affectedArea = prev_area->Merge ( *aAffectedArea ); + else + st.m_affectedArea = aAffectedArea; + } else + st.m_affectedArea = prev_area; + m_nodeStack.push_back( st ); return true; } -PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForce, int aCurrentRank ) +PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForce, int aCurrentRank, bool aDryRun ) { LINE_PAIR_VEC draggedLines; VECTOR2I p0 ( aVia->Pos() ); - PNS_JOINT* jt = m_currentNode->FindJoint( p0, 1, aVia->Net() ); - PNS_VIA* pushedVia = aVia -> Clone(); + PNS_JOINT* jt = m_currentNode->FindJoint( p0, aVia ); + VECTOR2I p0_pushed( p0 + aForce ); - pushedVia->SetPos( p0 + aForce ); + while (aForce.x != 0 || aForce.y != 0) + { + PNS_JOINT *jt_next = m_currentNode->FindJoint ( p0_pushed, aVia ); + + if(!jt_next) + break; + + p0_pushed += aForce.Resize ( 2 ); // make sure pushed via does not overlap with any existing joint + } + + PNS_VIA* pushedVia = aVia->Clone(); + pushedVia->SetPos( p0_pushed ); pushedVia->Mark( aVia->Marker() ); if( aVia->Marker() & MK_HEAD ) { m_draggedVia = pushedVia; + m_draggedViaHeadSet.Clear(); } if( !jt ) @@ -493,9 +554,8 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForc BOOST_FOREACH( PNS_ITEM* item, jt->LinkList() ) { - if( item->OfKind( PNS_ITEM::SEGMENT ) ) + if( PNS_SEGMENT *seg = dyn_cast( item ) ) { - PNS_SEGMENT* seg = (PNS_SEGMENT*) item; LINE_PAIR lp; int segIndex; @@ -506,19 +566,27 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForc if( segIndex == 0 ) lp.first->Reverse(); - lp.second = cloneLine( lp.first ); + lp.second = clone( lp.first ); lp.second->ClearSegmentLinks(); - lp.second->DragCorner( p0 + aForce, lp.second->CLine().Find( p0 ) ); + lp.second->DragCorner( p0_pushed, lp.second->CLine().Find( p0 ) ); lp.second->AppendVia ( *pushedVia ); draggedLines.push_back( lp ); + + if (aVia->Marker() & MK_HEAD ) + m_draggedViaHeadSet.Add ( clone ( lp.second ) ); + } } - m_currentNode->Remove( aVia ); - m_currentNode->Add ( pushedVia ); + m_draggedViaHeadSet.Add ( pushedVia ); + if ( aDryRun ) + return SH_OK; + + replaceItems ( aVia, pushedVia ); + if( aVia->BelongsTo( m_currentNode ) ) - delete aVia; + delete aVia; pushedVia->SetRank( aCurrentRank - 1 ); @@ -532,6 +600,10 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForc if( lp.first->Marker() & MK_HEAD ) { lp.second->Mark( MK_HEAD ); + + if ( m_multiLineMode ) + return SH_INCOMPLETE; + m_newHead = *lp.second; } @@ -539,7 +611,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForc if( lp.second->SegmentCount() ) { - m_currentNode->Replace( lp.first, lp.second ); + replaceItems( lp.first, lp.second ); lp.second->SetRank( aCurrentRank - 1 ); pushLine( lp.second ); } @@ -558,7 +630,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForc PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingVia( PNS_ITEM* aCurrent, PNS_VIA* aObstacleVia ) { - int clearance = m_currentNode->GetClearance( aCurrent, aObstacleVia ) ; + int clearance = getClearance( aCurrent, aObstacleVia ) ; LINE_PAIR_VEC draggedLines; bool colLine = false, colVia = false; PNS_LINE* currentLine = NULL; @@ -609,11 +681,11 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onReverseCollidingVia( PNS_LINE* aCurrent, PN { std::vector steps; int n = 0; - PNS_LINE* cur = cloneLine( aCurrent ); + PNS_LINE* cur = clone( aCurrent ); cur->ClearSegmentLinks(); PNS_JOINT* jt = m_currentNode->FindJoint( aObstacleVia->Pos(), aObstacleVia ); - PNS_LINE* shoved = cloneLine( aCurrent ); + PNS_LINE* shoved = clone( aCurrent ); shoved->ClearSegmentLinks(); cur->RemoveVia(); @@ -628,7 +700,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onReverseCollidingVia( PNS_LINE* aCurrent, PN head->AppendVia( *aObstacleVia ); - SHOVE_STATUS st = processSingleLine( head, cur, shoved ); + SHOVE_STATUS st = ProcessSingleLine( head, cur, shoved ); if( st != SH_OK ) { @@ -660,7 +732,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onReverseCollidingVia( PNS_LINE* aCurrent, PN head.AppendVia( *aObstacleVia ); head.ClearSegmentLinks(); - SHOVE_STATUS st = processSingleLine( &head, aCurrent, shoved ); + SHOVE_STATUS st = ProcessSingleLine( &head, aCurrent, shoved ); if( st != SH_OK ) return st; @@ -678,7 +750,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onReverseCollidingVia( PNS_LINE* aCurrent, PN m_logger.Log( shoved, 3, "shoved-line" ); #endif int currentRank = aCurrent->Rank(); - m_currentNode->Replace( aCurrent, shoved ); + replaceItems( aCurrent, shoved ); pushLine( shoved ); shoved->SetRank( currentRank ); @@ -727,7 +799,15 @@ void PNS_SHOVE::unwindStack( PNS_ITEM* aItem ) void PNS_SHOVE::pushLine( PNS_LINE* aL ) { if( aL->LinkCount() >= 0 && ( aL->LinkCount() != aL->SegmentCount() ) ) + { + printf("LC: %d SC %d\n", aL->LinkCount(), aL->SegmentCount() ); + for(int i=0;iSegmentCount();i++) + { + SEG s = aL->CLine().CSegment(i); + printf("s %d: %d %d %d %d\n", i, s.A.x, s.A.y, s.B.x, s.B.y ); + } assert( false ); + } m_lineStack.push_back( aL ); m_optimizerQueue.push_back( aL ); @@ -847,6 +927,8 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::shoveMainLoop() { SHOVE_STATUS st = SH_OK; + m_affectedAreaSum = OPT_BOX2I(); + TRACE( 1, "ShoveStart [root: %d jts, current: %d jts]", m_root->JointCount() % m_currentNode->JointCount() ); @@ -873,16 +955,34 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::shoveMainLoop() return st; } +OPT_BOX2I PNS_SHOVE::totalAffectedArea ( ) const +{ + OPT_BOX2I area; + if(!m_nodeStack.empty()) + area = m_nodeStack.back().m_affectedArea; + + if( area ) + { + if ( m_affectedAreaSum ) + area->Merge ( *m_affectedAreaSum ); + } else + area = m_affectedAreaSum; + + return area; +} + PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead ) { SHOVE_STATUS st = SH_OK; + m_multiLineMode = false; + // empty head? nothing to shove... if( !aCurrentHead.SegmentCount() ) return SH_INCOMPLETE; - PNS_LINE* head = cloneLine( &aCurrentHead ); + PNS_LINE* head = clone( &aCurrentHead ); head->ClearSegmentLinks(); m_lineStack.clear(); @@ -890,7 +990,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead ) m_newHead = OPT_LINE(); m_logger.Clear(); - PNS_ITEMSET headSet( cloneLine( &aCurrentHead ) ); + PNS_ITEMSET headSet( clone( &aCurrentHead ) ); reduceSpringback( headSet ); @@ -924,7 +1024,6 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead ) if( m_newHead && st == SH_OK ) { st = SH_HEAD_MODIFIED; - //Router()->DisplayDebugLine( m_newHead->CLine(), 3, 20000 ); } m_currentNode->RemoveByMarker( MK_HEAD ); @@ -934,7 +1033,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead ) if( st == SH_OK || st == SH_HEAD_MODIFIED ) { - pushSpringback( m_currentNode, headSet, PNS_COST_ESTIMATOR() ); + pushSpringback( m_currentNode, headSet, PNS_COST_ESTIMATOR(), m_affectedAreaSum); } else { @@ -948,19 +1047,107 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead ) } + +PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveMultiLines( const PNS_ITEMSET& aHeadSet ) +{ + SHOVE_STATUS st = SH_OK; + + m_multiLineMode = true; + + PNS_ITEMSET headSet; + + BOOST_FOREACH ( const PNS_ITEM *item, aHeadSet.CItems() ) + { + const PNS_LINE *headOrig = static_cast (item); + + // empty head? nothing to shove... + if( !headOrig->SegmentCount() ) + return SH_INCOMPLETE; + + headSet.Add ( clone ( headOrig ) ); + } + + + m_lineStack.clear(); + m_optimizerQueue.clear(); + m_logger.Clear(); + + reduceSpringback( headSet ); + + PNS_NODE* parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node; + + m_currentNode = parent->Branch(); + m_currentNode->ClearRanks(); + int n = 0; + BOOST_FOREACH ( const PNS_ITEM *item, aHeadSet.CItems() ) + { + const PNS_LINE *headOrig = static_cast (item); + PNS_LINE *head = clone ( headOrig ); + head->ClearSegmentLinks(); + + m_currentNode->Add( head ); + + head->Mark( MK_HEAD ); + head->SetRank( 100000 ); + n++; + pushLine( head ); + + PNS_VIA* headVia = NULL; + + if( head->EndsWithVia() ) + { + headVia = head->Via().Clone(); // fixme: leak + m_currentNode->Add( headVia ); + headVia->Mark( MK_HEAD ); + headVia->SetRank( 100000 ); + m_logger.Log( headVia, 0, "head-via" ); + } + } + + m_logger.NewGroup( "initial", 0 ); + //m_logger.Log( head, 0, "head" ); + + st = shoveMainLoop(); + runOptimizer( m_currentNode, NULL ); + + m_currentNode->RemoveByMarker( MK_HEAD ); + + TRACE( 1, "Shove status : %s after %d iterations", + ( st == SH_OK ? "OK" : "FAILURE") % m_iter ); + + if( st == SH_OK ) + { + pushSpringback( m_currentNode, PNS_ITEMSET(), PNS_COST_ESTIMATOR(), m_affectedAreaSum ); + } + else + { + delete m_currentNode; + m_currentNode = parent; + } + + return st; +} + PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveDraggingVia( PNS_VIA* aVia, const VECTOR2I& aWhere, PNS_VIA** aNewVia ) { SHOVE_STATUS st = SH_OK; + m_lineStack.clear(); m_optimizerQueue.clear(); m_newHead = OPT_LINE(); m_draggedVia = NULL; - - //reduceSpringback( aCurrentHead ); + m_draggedViaHeadSet.Clear(); PNS_NODE* parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node; + + m_currentNode = parent; + //st = pushVia( aVia, ( aWhere - aVia->Pos() ), 0, true ); + //reduceSpringback( m_draggedViaHeadSet ); + + parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node; + m_currentNode = parent->Branch(); m_currentNode->ClearRanks(); @@ -969,10 +1156,11 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveDraggingVia( PNS_VIA* aVia, const VECTOR st = pushVia( aVia, ( aWhere - aVia->Pos() ), 0 ); st = shoveMainLoop(); runOptimizer( m_currentNode, NULL ); + //m_currentNode->RemoveByMarker( MK_HEAD ); if( st == SH_OK || st == SH_HEAD_MODIFIED ) { - pushSpringback( m_currentNode, PNS_ITEMSET(), PNS_COST_ESTIMATOR() ); + pushSpringback( m_currentNode, m_draggedViaHeadSet, PNS_COST_ESTIMATOR(), m_affectedAreaSum); } else { @@ -981,8 +1169,9 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveDraggingVia( PNS_VIA* aVia, const VECTOR } if( aNewVia ) + { *aNewVia = m_draggedVia; - + } return st; } @@ -990,22 +1179,39 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveDraggingVia( PNS_VIA* aVia, const VECTOR void PNS_SHOVE::runOptimizer( PNS_NODE* aNode, PNS_LINE* aHead ) { PNS_OPTIMIZER optimizer( aNode ); - int optFlags = 0, n_passes = 0, extend = 0; + int optFlags = 0, n_passes = 0; PNS_OPTIMIZATION_EFFORT effort = Settings().OptimizerEffort(); + OPT_BOX2I area = totalAffectedArea(); + + int maxWidth = 0; + + for( std::vector::iterator i = m_optimizerQueue.begin(); + i != m_optimizerQueue.end(); ++i ) + { + maxWidth = std::max ( (*i)->Width(), maxWidth ); + } + + if(area) + { + area->Inflate ( 10 * maxWidth ); + } + switch( effort ) { case OE_LOW: optFlags = PNS_OPTIMIZER::MERGE_OBTUSE; n_passes = 1; - extend = 0; break; case OE_MEDIUM: - optFlags = PNS_OPTIMIZER::MERGE_OBTUSE; + optFlags = PNS_OPTIMIZER::MERGE_SEGMENTS; + + if( area ) + optimizer.SetRestrictArea ( *area ); + n_passes = 2; - extend = 1; break; case OE_FULL: @@ -1017,6 +1223,7 @@ void PNS_SHOVE::runOptimizer( PNS_NODE* aNode, PNS_LINE* aHead ) break; } + if( Settings().SmartPads() ) optFlags |= PNS_OPTIMIZER::SMART_PADS ; @@ -1034,18 +1241,6 @@ void PNS_SHOVE::runOptimizer( PNS_NODE* aNode, PNS_LINE* aHead ) if( !( line -> Marker() & MK_HEAD ) ) { - if( effort == OE_MEDIUM || effort == OE_LOW ) - { - RANGE r = findShovedVertexRange( line ); - - if( r.Defined() ) - { - int start_v = std::max( 0, r.MinV() - extend ); - int end_v = std::min( line->PointCount() - 1 , r.MaxV() + extend ); - line->ClipVertexRange( start_v, end_v ); - } - } - PNS_LINE optimized; if( optimizer.Optimize( line, &optimized ) ) @@ -1059,48 +1254,6 @@ void PNS_SHOVE::runOptimizer( PNS_NODE* aNode, PNS_LINE* aHead ) } } - -const RANGE PNS_SHOVE::findShovedVertexRange( PNS_LINE* aL ) -{ - RANGE r; - - for( int i = 0; i < aL->SegmentCount(); i++ ) - { - PNS_SEGMENT* s = (*aL->LinkedSegments())[i]; - PNS_JOINT* jt = m_root->FindJoint( s->Seg().A, s->Layer(), s->Net() ); - bool found = false; - - if( jt ) - { - BOOST_FOREACH( PNS_ITEM* item, jt->LinkList() ) - { - if( item->OfKind( PNS_ITEM::SEGMENT ) ) - { - PNS_SEGMENT* s_old = (PNS_SEGMENT*) item; - - if( s_old->Net() == s->Net() && - s_old->Layer() == s->Layer() && - s_old->Seg().A == s->Seg().A && - s_old->Seg().B == s->Seg().B ) - { - found = true; - break; - } - } - } - } - - if( !found ) - { - r.Grow( i ); - r.Grow( i + 1 ); - } - } - - return r; -} - - PNS_NODE* PNS_SHOVE::CurrentNode() { return m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node; diff --git a/pcbnew/router/pns_shove.h b/pcbnew/router/pns_shove.h index 2b4f32729f..d9fbeb9cee 100644 --- a/pcbnew/router/pns_shove.h +++ b/pcbnew/router/pns_shove.h @@ -61,7 +61,19 @@ public: } SHOVE_STATUS ShoveLines( const PNS_LINE& aCurrentHead ); + SHOVE_STATUS ShoveMultiLines( const PNS_ITEMSET& aHeadSet ); + SHOVE_STATUS ShoveDraggingVia( PNS_VIA*aVia, const VECTOR2I& aWhere, PNS_VIA** aNewVia ); + SHOVE_STATUS ProcessSingleLine( PNS_LINE* aCurrent, PNS_LINE* aObstacle, + PNS_LINE* aShoved ); + + void ForceClearance ( bool aEnabled, int aClearance ) + { + if( aEnabled ) + m_forceClearance = aClearance; + else + m_forceClearance = -1; + } PNS_NODE* CurrentNode(); @@ -83,15 +95,15 @@ private: PNS_NODE* m_node; PNS_ITEMSET m_headItems; PNS_COST_ESTIMATOR m_cost; + OPT_BOX2I m_affectedArea; }; - SHOVE_STATUS processSingleLine( PNS_LINE* aCurrent, PNS_LINE* aObstacle, PNS_LINE* aShoved ); SHOVE_STATUS processHullSet( PNS_LINE* aCurrent, PNS_LINE* aObstacle, PNS_LINE* aShoved, const HULL_SET& hulls ); bool reduceSpringback( const PNS_ITEMSET& aHeadItems ); - bool pushSpringback( PNS_NODE* aNode, const PNS_ITEMSET &aHeadItems, - const PNS_COST_ESTIMATOR& aCost ); + bool pushSpringback( PNS_NODE* aNode, const PNS_ITEMSET& aHeadItems, + const PNS_COST_ESTIMATOR& aCost, const OPT_BOX2I& aAffectedArea ); SHOVE_STATUS walkaroundLoneVia( PNS_LINE* aCurrent, PNS_LINE* aObstacle, PNS_LINE* aShoved ); bool checkBumpDirection( PNS_LINE* aCurrent, PNS_LINE* aShoved ) const; @@ -101,8 +113,11 @@ private: SHOVE_STATUS onCollidingSolid( PNS_LINE* aCurrent, PNS_SOLID* aObstacleSolid ); SHOVE_STATUS onCollidingVia( PNS_ITEM* aCurrent, PNS_VIA* aObstacleVia ); SHOVE_STATUS onReverseCollidingVia( PNS_LINE* aCurrent, PNS_VIA* aObstacleVia ); - SHOVE_STATUS pushVia( PNS_VIA* aVia, const VECTOR2I& aForce, int aCurrentRank ); + SHOVE_STATUS pushVia( PNS_VIA* aVia, const VECTOR2I& aForce, int aCurrentRank, bool aDryRun = false ); + OPT_BOX2I totalAffectedArea ( ) const; + + void unwindStack( PNS_SEGMENT* aSeg ); void unwindStack( PNS_ITEM* aItem ); @@ -111,14 +126,26 @@ private: void pushLine( PNS_LINE* aL ); void popLine(); - const RANGE findShovedVertexRange( PNS_LINE* aL ); - PNS_LINE* assembleLine( const PNS_SEGMENT* aSeg, int* aIndex = NULL ); - PNS_LINE* cloneLine( const PNS_LINE* aLine ); + + void replaceItems ( PNS_ITEM *aOld, PNS_ITEM *aNew ); + + template T* clone ( const T* aItem ) + { + T *cloned = aItem->Clone(); + + m_gcItems.push_back( cloned ); + return cloned; + } + + OPT_BOX2I m_affectedAreaSum; + SHOVE_STATUS shoveIteration( int aIter ); SHOVE_STATUS shoveMainLoop(); + int getClearance( PNS_ITEM *aA, PNS_ITEM *aB ) const; + std::vector m_nodeStack; std::vector m_lineStack; std::vector m_optimizerQueue; @@ -131,8 +158,12 @@ private: PNS_LOGGER m_logger; PNS_VIA* m_draggedVia; + PNS_ITEMSET m_draggedViaHeadSet; int m_iter; + int m_forceClearance; + bool m_multiLineMode; + bool m_headModified; }; #endif // __PNS_SHOVE_H diff --git a/pcbnew/router/pns_sizes_settings.h b/pcbnew/router/pns_sizes_settings.h index 2cd0dae9a1..8c84256215 100644 --- a/pcbnew/router/pns_sizes_settings.h +++ b/pcbnew/router/pns_sizes_settings.h @@ -34,11 +34,13 @@ class PNS_SIZES_SETTINGS { public: PNS_SIZES_SETTINGS() : - m_trackWidth( 100000 ), - m_diffPairWidth( 100000 ), - m_diffPairGap( 125000 ), - m_viaDiameter( 500000 ), - m_viaDrill( 200000 ), + m_trackWidth( 155000 ), + m_diffPairWidth( 125000 ), + m_diffPairGap( 180000 ), + m_diffPairViaGap ( 180000 ), + m_viaDiameter( 600000 ), + m_viaDrill( 250000 ), + m_diffPairViaGapSameAsTraceGap ( true ), m_viaType( VIA_THROUGH ) {}; @@ -54,8 +56,21 @@ public: void SetTrackWidth( int aWidth ) { m_trackWidth = aWidth; } int DiffPairWidth() const { return m_diffPairWidth; } - int DiffPairGap() const { return m_diffPairGap; } + + int DiffPairViaGap() const { + if(m_diffPairViaGapSameAsTraceGap) + return m_diffPairGap; + else + return m_diffPairViaGap; + } + + bool DiffPairViaGapSameAsTraceGap() const { return m_diffPairViaGapSameAsTraceGap; } + + void SetDiffPairWidth( int aWidth ) { m_diffPairWidth = aWidth; } + void SetDiffPairGap( int aGap ) { m_diffPairGap = aGap; } + void SetDiffPairViaGapSameAsTraceGap ( bool aEnable ) { m_diffPairViaGapSameAsTraceGap = aEnable; } + void SetDiffPairViaGap( int aGap ) { m_diffPairViaGap = aGap; } int ViaDiameter() const { return m_viaDiameter; } void SetViaDiameter( int aDiameter) { m_viaDiameter = aDiameter; } @@ -84,9 +99,12 @@ private: int m_trackWidth; int m_diffPairWidth; int m_diffPairGap; + int m_diffPairViaGap; int m_viaDiameter; int m_viaDrill; + bool m_diffPairViaGapSameAsTraceGap; + VIATYPE_T m_viaType; std::map m_layerPairs; diff --git a/pcbnew/router/pns_solid.h b/pcbnew/router/pns_solid.h index 88f8431d24..527a08142f 100644 --- a/pcbnew/router/pns_solid.h +++ b/pcbnew/router/pns_solid.h @@ -48,7 +48,12 @@ public: m_shape = aSolid.m_shape->Clone(); m_pos = aSolid.m_pos; } - + + static inline bool ClassOf( const PNS_ITEM* aItem ) + { + return aItem && SOLID == aItem->Kind(); + } + PNS_ITEM* Clone() const; const SHAPE* Shape() const { return m_shape; } diff --git a/pcbnew/router/pns_tool_base.cpp b/pcbnew/router/pns_tool_base.cpp new file mode 100644 index 0000000000..0e83041791 --- /dev/null +++ b/pcbnew/router/pns_tool_base.cpp @@ -0,0 +1,273 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include + +#include +#include +#include + +#include "class_draw_panel_gal.h" +#include "class_board.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "pns_tool_base.h" +#include "pns_segment.h" +#include "pns_router.h" +#include "pns_meander_placer.h" // fixme: move settings to separate header +#include "pns_tune_status_popup.h" +#include "trace.h" + +using namespace KIGFX; +using boost::optional; + +TOOL_ACTION PNS_TOOL_BASE::ACT_RouterOptions( "pcbnew.InteractiveRouter.RouterOptions", + AS_CONTEXT, 'E', + "Routing Options...", "Shows a dialog containing router options."); + + +PNS_TOOL_BASE::PNS_TOOL_BASE( const std::string& aToolName ) : + TOOL_INTERACTIVE( aToolName ) +{ + m_router = NULL; +} + + +PNS_TOOL_BASE::~PNS_TOOL_BASE() +{ + delete m_router; +} + + +void PNS_TOOL_BASE::Reset( RESET_REASON aReason ) +{ + if( m_router ) + delete m_router; + + m_frame = getEditFrame(); + m_ctls = getViewControls(); + m_board = getModel(); + + m_router = new PNS_ROUTER; + + m_router->ClearWorld(); + m_router->SetBoard( m_board ); + m_router->SyncWorld(); + m_router->LoadSettings( m_savedSettings ); + m_router->UpdateSizes( m_savedSizes ); + m_needsSync = false; + + if( getView() ) + m_router->SetView( getView() ); +} + +PNS_ITEM* PNS_TOOL_BASE::pickSingleItem( const VECTOR2I& aWhere, int aNet, int aLayer ) +{ + int tl = getView()->GetTopLayer(); + + if( aLayer > 0 ) + tl = aLayer; + + PNS_ITEM* prioritized[4]; + + for( int i = 0; i < 4; i++ ) + prioritized[i] = 0; + + PNS_ITEMSET candidates = m_router->QueryHoverItems( aWhere ); + + BOOST_FOREACH( PNS_ITEM* item, candidates.Items() ) + { + if( !IsCopperLayer( item->Layers().Start() ) ) + continue; + + // fixme: this causes flicker with live loop removal... + //if( item->Parent() && !item->Parent()->ViewIsVisible() ) + // continue; + + if( aNet < 0 || item->Net() == aNet ) + { + if( item->OfKind( PNS_ITEM::VIA | PNS_ITEM::SOLID ) ) + { + if( !prioritized[2] ) + prioritized[2] = item; + if( item->Layers().Overlaps( tl ) ) + prioritized[0] = item; + } + else + { + if( !prioritized[3] ) + prioritized[3] = item; + if( item->Layers().Overlaps( tl ) ) + prioritized[1] = item; + } + } + } + + PNS_ITEM* rv = NULL; + PCB_EDIT_FRAME* frame = getEditFrame (); + DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)frame->GetDisplayOptions(); + + for( int i = 0; i < 4; i++ ) + { + PNS_ITEM* item = prioritized[i]; + + if( displ_opts->m_ContrastModeDisplay ) + if( item && !item->Layers().Overlaps( tl ) ) + item = NULL; + + if( item ) + { + rv = item; + break; + } + } + + if( rv && aLayer >= 0 && !rv->Layers().Overlaps( aLayer ) ) + rv = NULL; + + if( rv ) + TRACE( 0, "%s, layer : %d, tl: %d", rv->KindStr().c_str() % rv->Layers().Start() % tl ); + + return rv; +} + + +void PNS_TOOL_BASE::highlightNet( bool aEnabled, int aNetcode ) +{ + RENDER_SETTINGS* rs = getView()->GetPainter()->GetSettings(); + + if( aNetcode >= 0 && aEnabled ) + rs->SetHighlight( true, aNetcode ); + else + rs->SetHighlight( false ); + + getView()->UpdateAllLayersColor(); +} + +void PNS_TOOL_BASE::updateStartItem( TOOL_EVENT& aEvent ) +{ + int tl = getView()->GetTopLayer(); + VECTOR2I cp = m_ctls->GetCursorPosition(); + PNS_ITEM* startItem = NULL; + + if( aEvent.IsMotion() || aEvent.IsClick() ) + { + bool snapEnabled = !aEvent.Modifier( MD_SHIFT ); + + VECTOR2I p( aEvent.Position() ); + startItem = pickSingleItem( p ); + m_router->EnableSnapping ( snapEnabled ); + + if( !snapEnabled && startItem && !startItem->Layers().Overlaps( tl ) ) + startItem = NULL; + + if( startItem && startItem->Net() >= 0 ) + { + bool dummy; + VECTOR2I psnap = m_router->SnapToItem( startItem, p, dummy ); + + if( snapEnabled ) + { + m_startSnapPoint = psnap; + m_ctls->ForceCursorPosition( true, psnap ); + } + else + { + m_startSnapPoint = cp; + m_ctls->ForceCursorPosition( false ); + } + +// if( startItem->Layers().IsMultilayer() ) +// m_startLayer = tl; +// else +// m_startLayer = startItem->Layers().Start(); + + m_startItem = startItem; + } + else + { + m_startItem = NULL; + m_startSnapPoint = cp; + m_ctls->ForceCursorPosition( false ); + } + } +} + +void PNS_TOOL_BASE::updateEndItem( TOOL_EVENT& aEvent ) +{ + VECTOR2I mp = m_ctls->GetMousePosition(); + VECTOR2I p = getView()->ToWorld( mp ); + VECTOR2I cp = m_ctls->GetCursorPosition(); + int layer; + bool snapEnabled = !aEvent.Modifier( MD_CTRL ); + + m_router->EnableSnapping( snapEnabled ); + + if( !snapEnabled || m_router->GetCurrentNet() < 0 || !m_startItem ) + { + m_endItem = NULL; + m_endSnapPoint = cp; + return; + } + + bool dummy; + + if( m_router->IsPlacingVia() ) + layer = -1; + else + layer = m_router->GetCurrentLayer(); + + PNS_ITEM* endItem = pickSingleItem( p, m_startItem->Net(), layer ); + + if( endItem ) + { + VECTOR2I cursorPos = m_router->SnapToItem( endItem, p, dummy ); + m_ctls->ForceCursorPosition( true, cursorPos ); + m_endItem = endItem; + m_endSnapPoint = cursorPos; + } + else + { + m_endItem = NULL; + m_endSnapPoint = cp; + m_ctls->ForceCursorPosition( false ); + } + + if( m_endItem ) + TRACE( 0, "%s, layer : %d", m_endItem->KindStr().c_str() % m_endItem->Layers().Start() ); +} + diff --git a/pcbnew/router/pns_tool_base.h b/pcbnew/router/pns_tool_base.h new file mode 100644 index 0000000000..9778b0e999 --- /dev/null +++ b/pcbnew/router/pns_tool_base.h @@ -0,0 +1,74 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2014 CERN + * Author: Tomasz Wlostowski + * Author: Maciej Suminski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_TOOL_BASE_H +#define __PNS_TOOL_BASE_H + +#include + +#include +#include + +#include + +#include "pns_router.h" + +class PNS_TUNE_STATUS_POPUP; + +class APIEXPORT PNS_TOOL_BASE : public TOOL_INTERACTIVE +{ +public: + static TOOL_ACTION ACT_RouterOptions; + + PNS_TOOL_BASE( const std::string& aToolName ); + virtual ~PNS_TOOL_BASE(); + + virtual void Reset( RESET_REASON aReason ); + +protected: + + virtual PNS_ITEM* pickSingleItem( const VECTOR2I& aWhere, int aNet = -1, int aLayer = -1 ); + virtual void highlightNet( bool aEnabled, int aNetcode = -1 ); + virtual void updateStartItem( TOOL_EVENT& aEvent ); + virtual void updateEndItem( TOOL_EVENT& aEvent ); + + MSG_PANEL_ITEMS m_panelItems; + + PNS_ROUTER* m_router; + PNS_ROUTING_SETTINGS m_savedSettings; ///< Stores routing settings between router invocations + PNS_SIZES_SETTINGS m_savedSizes; ///< Stores sizes settings between router invocations + PNS_ITEM* m_startItem; + int m_startLayer; + VECTOR2I m_startSnapPoint; + + PNS_ITEM* m_endItem; + VECTOR2I m_endSnapPoint; + + ///> Flag marking that the router's world needs syncing. + bool m_needsSync; + + PCB_EDIT_FRAME *m_frame; + KIGFX::VIEW_CONTROLS *m_ctls; + BOARD *m_board; + +}; + +#endif diff --git a/pcbnew/router/pns_topology.cpp b/pcbnew/router/pns_topology.cpp new file mode 100644 index 0000000000..8fdc9a4770 --- /dev/null +++ b/pcbnew/router/pns_topology.cpp @@ -0,0 +1,360 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "pns_line.h" +#include "pns_segment.h" +#include "pns_node.h" +#include "pns_joint.h" +#include "pns_solid.h" +#include "pns_router.h" + +#include "pns_diff_pair.h" +#include "pns_topology.h" + +#include + +bool PNS_TOPOLOGY::SimplifyLine ( PNS_LINE *aLine ) +{ + + if( !aLine->LinkedSegments() || !aLine->SegmentCount() ) + return false; + + PNS_SEGMENT *root = (*aLine->LinkedSegments())[0]; + std::auto_ptr l ( m_world->AssembleLine( root ) ); + SHAPE_LINE_CHAIN simplified ( l->CLine() ); + + simplified.Simplify(); + + if( simplified.PointCount() != l->PointCount() ) + { + std::auto_ptr lnew ( l->Clone() ); + m_world -> Remove( l.get() ); + lnew->SetShape( simplified ); + m_world -> Add( lnew.get() ); + return true; + } + + return false; +} + +const PNS_TOPOLOGY::JOINT_SET PNS_TOPOLOGY::ConnectedJoints ( PNS_JOINT* aStart ) +{ + std::deque searchQueue; + JOINT_SET processed; + + searchQueue.push_back( aStart ); + processed.insert( aStart ); + + while( !searchQueue.empty() ) + { + PNS_JOINT* current = searchQueue.front(); + searchQueue.pop_front(); + + BOOST_FOREACH( PNS_ITEM* item, current->LinkList() ) + { + if ( item->OfKind( PNS_ITEM::SEGMENT ) ) + { + PNS_SEGMENT* seg = static_cast( item ); + PNS_JOINT* a = m_world->FindJoint( seg->Seg().A, seg ); + PNS_JOINT* b = m_world->FindJoint( seg->Seg().B, seg ); + PNS_JOINT* next = ( *a == *current ) ? b : a; + + if( processed.find( next ) == processed.end() ) + { + processed.insert( next ); + searchQueue.push_back( next ); + } + } + } + } + + return processed; +} + + +bool PNS_TOPOLOGY::LeadingRatLine( const PNS_LINE *aTrack, SHAPE_LINE_CHAIN& aRatLine ) +{ + PNS_LINE track ( *aTrack ); + VECTOR2I end; + + if( !track.PointCount() ) + return false; + + std::auto_ptr tmpNode ( m_world->Branch() ); + tmpNode->Add( &track ); + + PNS_JOINT* jt = tmpNode->FindJoint( track.CPoint( -1 ), &track ); + + if ( !jt ) + return false; + + if( (!track.EndsWithVia() && jt->LinkCount() >= 2) || (track.EndsWithVia() && jt->LinkCount() >= 3 )) // we got something connected + { + end = jt->Pos(); + } else { + int anchor; + + PNS_TOPOLOGY topo ( tmpNode.get() ); + PNS_ITEM* it = topo.NearestUnconnectedItem( jt, &anchor ); + + if( !it ) + return false; + + end = it->Anchor( anchor ); + } + + aRatLine.Clear(); + aRatLine.Append ( track.CPoint( -1 ) ); + aRatLine.Append ( end ); + return true; +} + +PNS_ITEM* PNS_TOPOLOGY::NearestUnconnectedItem( PNS_JOINT* aStart, int* aAnchor, int aKindMask ) +{ + std::set disconnected; + + m_world->AllItemsInNet( aStart->Net(), disconnected ); + + BOOST_FOREACH( const PNS_JOINT *jt, ConnectedJoints ( aStart ) ) + { + BOOST_FOREACH( PNS_ITEM* link, jt->LinkList() ) + { + if( disconnected.find( link ) != disconnected.end() ) + disconnected.erase( link ); + } + } + + int best_dist = INT_MAX; + PNS_ITEM* best = NULL; + + BOOST_FOREACH( PNS_ITEM* item, disconnected ) + { + if( item->OfKind( aKindMask ) ) + { + for(int i = 0; i < item->AnchorCount(); i++) + { + VECTOR2I p = item->Anchor( i ); + int d = ( p - aStart->Pos() ).EuclideanNorm(); + + if( d < best_dist ) + { + best_dist = d; + best = item; + + if( aAnchor ) + *aAnchor = i; + } + } + } + } + + return best; +} + + +bool PNS_TOPOLOGY::followTrivialPath ( PNS_LINE *aLine, bool aLeft, PNS_ITEMSET& aSet, std::set& aVisited ) +{ + VECTOR2I anchor = aLeft ? aLine->CPoint(0) : aLine->CPoint(-1); + PNS_SEGMENT *last = aLeft ? aLine->LinkedSegments()->front() : aLine->LinkedSegments()->back(); + PNS_JOINT *jt = m_world->FindJoint ( anchor, aLine ); + + assert (jt != NULL); + + aVisited.insert( last ); + + if( jt->IsNonFanoutVia() ) + { + PNS_ITEM *via = NULL; + PNS_SEGMENT *next_seg = NULL; + + BOOST_FOREACH ( PNS_ITEM *link, jt->Links().Items() ) + { + if( link->OfKind ( PNS_ITEM::VIA ) ) + via = link; + else if( aVisited.find(link) == aVisited.end() ) + next_seg = static_cast (link); + } + + if(!next_seg) + return false; + + PNS_LINE *l = m_world->AssembleLine ( next_seg ); + + VECTOR2I nextAnchor = (aLeft ? l->CLine().CPoint(-1) : l->CLine().CPoint(0) ); + + if (nextAnchor != anchor) + { + l->Reverse(); + } + + if (aLeft) + { + aSet.Prepend ( via ); + aSet.Prepend ( l ); + } else { + aSet.Add ( via ); + aSet.Add ( l ); + } + + return followTrivialPath ( l, aLeft, aSet, aVisited ); + } + return false; +} + +const PNS_ITEMSET PNS_TOPOLOGY::AssembleTrivialPath ( PNS_SEGMENT *aStart ) +{ + PNS_ITEMSET path; + std::set visited; + + PNS_LINE *l = m_world->AssembleLine ( aStart ); + + path.Add ( l ); + + followTrivialPath( l, false, path, visited ); + followTrivialPath( l, true, path, visited ); + + return path; +} + +const PNS_ITEMSET PNS_TOPOLOGY::ConnectedItems( PNS_JOINT* aStart, int aKindMask ) +{ + return PNS_ITEMSET(); +} + +const PNS_ITEMSET PNS_TOPOLOGY::ConnectedItems( PNS_ITEM* aStart, int aKindMask ) +{ + return PNS_ITEMSET(); +} + +int PNS_TOPOLOGY::MatchDpSuffix ( wxString aNetName, wxString& aComplementNet, wxString& aBaseDpName ) +{ + int rv = 0; + if (aNetName.EndsWith("+")) + { + aComplementNet = "-"; + rv = 1; + } else if (aNetName.EndsWith("_P")) + { + aComplementNet = "_N"; + rv = 1; + } else if (aNetName.EndsWith("-")) + { + aComplementNet = "+"; + rv = -1; + } else if (aNetName.EndsWith("_N")) + { + aComplementNet = "_P"; + rv = -1; + } + + if (rv != 0) { + aBaseDpName = aNetName.Left ( aNetName.Length() - aComplementNet.Length() ); + aComplementNet = aBaseDpName + aComplementNet; + } + + return rv; +} + + +int PNS_TOPOLOGY::DpCoupledNet( int aNet ) +{ + BOARD *brd = PNS_ROUTER::GetInstance()->GetBoard(); + + wxString refName = brd->FindNet ( aNet )->GetNetname(); + wxString dummy, coupledNetName; + + if ( MatchDpSuffix ( refName, coupledNetName, dummy ) ) + { + NETINFO_ITEM *net = brd->FindNet ( coupledNetName ); + + if(!net) + return -1; + + return net->GetNet(); + + } + + return -1; +} + + +int PNS_TOPOLOGY::DpNetPolarity( int aNet ) +{ + BOARD *brd = PNS_ROUTER::GetInstance()->GetBoard(); + + wxString refName = brd->FindNet ( aNet )->GetNetname(); + wxString dummy1, dummy2; + + return MatchDpSuffix ( refName, dummy1, dummy2 ); +} + + +bool PNS_TOPOLOGY::AssembleDiffPair ( PNS_ITEM *aStart, PNS_DIFF_PAIR& aPair ) +{ + int refNet = aStart->Net(); + int coupledNet = DpCoupledNet ( refNet ); + + if(coupledNet < 0) + return false; + + std::set coupledItems; + + m_world->AllItemsInNet( coupledNet, coupledItems ); + + PNS_SEGMENT *coupledSeg = NULL, *refSeg; + int minDist = std::numeric_limits::max(); + + if( ( refSeg = dyn_cast( aStart ) ) != NULL ) + { + BOOST_FOREACH ( PNS_ITEM *item, coupledItems ) + { + if ( PNS_SEGMENT *s = dyn_cast(item) ) + { + if( s->Layers().Start() == refSeg->Layers().Start() && s->Width() == refSeg->Width() ) + { + int dist = s->Seg().Distance( refSeg->Seg() ); + + if(dist < minDist) + { + minDist = dist; + coupledSeg = s; + } + } + } + } + } else + return false; + + if(!coupledSeg) + return false; + + std::auto_ptr lp ( m_world->AssembleLine ( refSeg ) ); + std::auto_ptr ln ( m_world->AssembleLine ( coupledSeg ) ); + + if(DpNetPolarity(refNet) < 0) + { + std::swap (lp, ln); + } + + aPair = PNS_DIFF_PAIR ( *lp, *ln ); + aPair.SetWidth ( lp->Width() ); + aPair.SetLayers ( lp->Layers() ); + return true; +} diff --git a/pcbnew/router/pns_topology.h b/pcbnew/router/pns_topology.h new file mode 100644 index 0000000000..5233cb71c0 --- /dev/null +++ b/pcbnew/router/pns_topology.h @@ -0,0 +1,75 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __PNS_TOPOLOGY_H +#define __PNS_TOPOLOGY_H + +#include +#include + +#include "pns_itemset.h" + +class PNS_NODE; +class PNS_SEGMENT; +class PNS_JOINT; +class PNS_ITEM; +class PNS_SOLID; +class PNS_DIFF_PAIR; + +class PNS_TOPOLOGY +{ + public: + typedef std::set JOINT_SET; + + PNS_TOPOLOGY ( PNS_NODE *aNode ): + m_world ( aNode ) {}; + + ~PNS_TOPOLOGY ( ) {}; + + bool SimplifyLine ( PNS_LINE *aLine ); + PNS_ITEM* NearestUnconnectedItem( PNS_JOINT* aStart, int* aAnchor = NULL, int aKindMask = PNS_ITEM::ANY ); + bool LeadingRatLine( const PNS_LINE *aTrack, SHAPE_LINE_CHAIN& aRatLine ); + + const JOINT_SET ConnectedJoints ( PNS_JOINT* aStart ); + const PNS_ITEMSET ConnectedItems ( PNS_JOINT* aStart, int aKindMask = PNS_ITEM::ANY ); + const PNS_ITEMSET ConnectedItems ( PNS_ITEM* aStart, int aKindMask = PNS_ITEM::ANY ); + int64_t ShortestConnectionLength ( PNS_ITEM *aFrom, PNS_ITEM *aTo ); + + + + const PNS_ITEMSET AssembleTrivialPath ( PNS_SEGMENT *aStart ); + const PNS_DIFF_PAIR AssembleDiffPair ( PNS_SEGMENT *aStart ); + + int MatchDpSuffix ( wxString aNetName, wxString& aComplementNet, wxString& aBaseDpName ); + int DpCoupledNet( int aNet ); + int DpNetPolarity( int aNet ); + const PNS_LINE DpCoupledLine( PNS_LINE *aLine ); + bool AssembleDiffPair ( PNS_ITEM *aStart, PNS_DIFF_PAIR& aPair ); + + + + private: + + bool followTrivialPath ( PNS_LINE *aLine, bool aLeft, PNS_ITEMSET& aSet, std::set& aVisited ); + + PNS_NODE *m_world; +}; + +#endif diff --git a/pcbnew/router/pns_tune_status_popup.cpp b/pcbnew/router/pns_tune_status_popup.cpp new file mode 100644 index 0000000000..924169af97 --- /dev/null +++ b/pcbnew/router/pns_tune_status_popup.cpp @@ -0,0 +1,69 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "pns_tune_status_popup.h" +#include "pns_router.h" +#include "pns_meander_placer.h" + +PNS_TUNE_STATUS_POPUP::PNS_TUNE_STATUS_POPUP ( PCB_EDIT_FRAME *parent ) : + WX_STATUS_POPUP ( parent ) +{ + m_panel->SetBackgroundColour( wxColour(64,64,64) ); + m_statusLine = new wxStaticText( m_panel, wxID_ANY, + wxT("Status text 1\n") ) ; + m_topSizer->Add( m_statusLine, 1, wxALL | wxEXPAND, 5 ); + + updateSize(); +} + +PNS_TUNE_STATUS_POPUP::~PNS_TUNE_STATUS_POPUP() +{ + +} + +void PNS_TUNE_STATUS_POPUP::Update( PNS_ROUTER *aRouter ) +{ + PNS_MEANDER_PLACER_BASE *placer = dynamic_cast ( aRouter->Placer() ); + + if(!placer) + return; + + m_statusLine->SetLabel ( placer->TuningInfo() ); + + wxColour color; + + switch ( placer->TuningStatus() ) + { + case PNS_MEANDER_PLACER::TUNED: + color = wxColour ( 0, 255, 0 ); + break; + case PNS_MEANDER_PLACER::TOO_SHORT: + color = wxColour ( 255, 128, 128 ); + break; + case PNS_MEANDER_PLACER::TOO_LONG: + color = wxColour ( 128, 128, 255 ); + break; + } + + m_statusLine->SetForegroundColour (color); + + updateSize(); +} + diff --git a/pcbnew/router/pns_tune_status_popup.h b/pcbnew/router/pns_tune_status_popup.h new file mode 100644 index 0000000000..9a894f3ed4 --- /dev/null +++ b/pcbnew/router/pns_tune_status_popup.h @@ -0,0 +1,45 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, you may find one here: + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + * or you may search the http://www.gnu.org website for the version 2 license, + * or you may write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __PNS_TUNE_STATUS_POPUP_H_ +#define __PNS_TUNE_STATUS_POPUP_H_ + +#include + +class PNS_ROUTER; + +class PNS_TUNE_STATUS_POPUP : public WX_STATUS_POPUP +{ +public: + PNS_TUNE_STATUS_POPUP ( PCB_EDIT_FRAME *parent ); + ~PNS_TUNE_STATUS_POPUP(); + + + void Update( PNS_ROUTER *aRouter ); + +private: + wxStaticText *m_statusLine; +}; + +#endif /* __PNS_TUNE_STATUS_POPUP_H_*/ diff --git a/pcbnew/router/pns_utils.cpp b/pcbnew/router/pns_utils.cpp index 5a69e673d8..5635893e10 100644 --- a/pcbnew/router/pns_utils.cpp +++ b/pcbnew/router/pns_utils.cpp @@ -20,6 +20,7 @@ #include "pns_utils.h" #include "pns_line.h" +#include "pns_via.h" #include "pns_router.h" #include @@ -91,3 +92,83 @@ SHAPE_RECT ApproximateSegmentAsRect( const SHAPE_SEGMENT& aSeg ) return SHAPE_RECT( std::min( p0.x, p1.x ), std::min( p0.y, p1.y ), std::abs( p1.x - p0.x ), std::abs( p1.y - p0.y ) ); } + +void DrawDebugPoint ( VECTOR2I p, int color ) +{ + SHAPE_LINE_CHAIN l; + + l.Append ( p - VECTOR2I(-50000, -50000) ); + l.Append ( p + VECTOR2I(-50000, -50000) ); + + //printf("router @ %p\n", PNS_ROUTER::GetInstance()); + PNS_ROUTER::GetInstance()->DisplayDebugLine ( l, color, 10000 ); + + l.Clear(); + l.Append ( p - VECTOR2I(50000, -50000) ); + l.Append ( p + VECTOR2I(50000, -50000) ); + + PNS_ROUTER::GetInstance()->DisplayDebugLine ( l, color, 10000 ); +} + +void DrawDebugBox ( BOX2I b, int color ) +{ + SHAPE_LINE_CHAIN l; + + VECTOR2I o = b.GetOrigin(); + VECTOR2I s = b.GetSize(); + + l.Append ( o ); + l.Append ( o.x + s.x, o.y ); + l.Append ( o.x + s.x, o.y + s.y ); + l.Append ( o.x, o.y + s.y ); + l.Append ( o ); + + //printf("router @ %p\n", PNS_ROUTER::GetInstance()); + PNS_ROUTER::GetInstance()->DisplayDebugLine ( l, color, 10000 ); +} + +void DrawDebugSeg ( SEG s, int color ) +{ + SHAPE_LINE_CHAIN l; + + l.Append ( s.A ); + l.Append ( s.B ); + + PNS_ROUTER::GetInstance()->DisplayDebugLine ( l, color, 10000 ); +} + +void DrawDebugDirs ( VECTOR2D p, int mask, int color ) +{ + BOX2I b ( p - VECTOR2I ( 10000, 10000 ), VECTOR2I ( 20000, 20000 ) ); + + DrawDebugBox ( b, color ); + for (int i = 0; i < 8; i++) + { + if ( (1<OfKind ( PNS_ITEM::VIA ) && aItemB->OfKind ( PNS_ITEM::VIA ) ) + { + const PNS_VIA *va = static_cast (aItemA); + const PNS_VIA *vb = static_cast (aItemB); + + return va->ChangedArea (vb); + } else if ( aItemA->OfKind ( PNS_ITEM::LINE ) && aItemB->OfKind ( PNS_ITEM::LINE ) ) + { + const PNS_LINE *la = static_cast (aItemA); + const PNS_LINE *lb = static_cast (aItemB); + + return la->ChangedArea (lb); + } + return OPT_BOX2I(); +} \ No newline at end of file diff --git a/pcbnew/router/pns_utils.h b/pcbnew/router/pns_utils.h index 5bf6d9313b..01f1f0bb6f 100644 --- a/pcbnew/router/pns_utils.h +++ b/pcbnew/router/pns_utils.h @@ -22,12 +22,15 @@ #define __PNS_UTILS_H #include +#include #include #include #include #define HULL_MARGIN 10 +class PNS_ITEM; + /** Various utility functions */ const SHAPE_LINE_CHAIN OctagonalHull( const VECTOR2I& aP0, const VECTOR2I& aSize, @@ -38,4 +41,12 @@ const SHAPE_LINE_CHAIN SegmentHull ( const SHAPE_SEGMENT& aSeg, int aClearance, SHAPE_RECT ApproximateSegmentAsRect( const SHAPE_SEGMENT& aSeg ); +void DrawDebugPoint ( VECTOR2I p, int color ); +void DrawDebugBox ( BOX2I b, int color ); +void DrawDebugSeg ( SEG s, int color ); +void DrawDebugDirs ( VECTOR2D p, int mask, int color ); + +OPT_BOX2I ChangedArea ( const PNS_ITEM *aItemA, const PNS_ITEM *aItemB ); + + #endif // __PNS_UTILS_H diff --git a/pcbnew/router/pns_via.cpp b/pcbnew/router/pns_via.cpp index 1b5c369bf2..06acf533ea 100644 --- a/pcbnew/router/pns_via.cpp +++ b/pcbnew/router/pns_via.cpp @@ -95,3 +95,15 @@ PNS_VIA* PNS_VIA::Clone ( ) const return v; } + +OPT_BOX2I PNS_VIA::ChangedArea ( const PNS_VIA *aOther ) const +{ + if ( aOther->Pos() != Pos() ) + { + BOX2I tmp = Shape()->BBox( ); + tmp.Merge ( aOther->Shape()->BBox( ) ); + return tmp; + } + + return OPT_BOX2I(); +} diff --git a/pcbnew/router/pns_via.h b/pcbnew/router/pns_via.h index b7637e9f13..1a2dc1d7e8 100644 --- a/pcbnew/router/pns_via.h +++ b/pcbnew/router/pns_via.h @@ -73,6 +73,12 @@ public: m_viaType = aB.m_viaType; } + static inline bool ClassOf( const PNS_ITEM* aItem ) + { + return aItem && VIA == aItem->Kind(); + } + + const VECTOR2I& Pos() const { return m_pos; @@ -140,6 +146,8 @@ public: return 1; } + OPT_BOX2I ChangedArea ( const PNS_VIA *aOther ) const; + private: int m_diameter; int m_drill; diff --git a/pcbnew/router/pns_walkaround.cpp b/pcbnew/router/pns_walkaround.cpp index bbe8d2bca3..9c81860551 100644 --- a/pcbnew/router/pns_walkaround.cpp +++ b/pcbnew/router/pns_walkaround.cpp @@ -136,7 +136,7 @@ PNS_WALKAROUND::WALKAROUND_STATUS PNS_WALKAROUND::Route( const PNS_LINE& aInitia PNS_LINE& aWalkPath, bool aOptimize ) { PNS_LINE path_cw( aInitialPath ), path_ccw( aInitialPath ); - WALKAROUND_STATUS s_cw = IN_PROGRESS, s_ccw = IN_PROGRESS; + WALKAROUND_STATUS s_cw, s_ccw; // = IN_PROGRESS, s_ccw = IN_PROGRESS; SHAPE_LINE_CHAIN best_path; start( aInitialPath ); @@ -146,6 +146,17 @@ PNS_WALKAROUND::WALKAROUND_STATUS PNS_WALKAROUND::Route( const PNS_LINE& aInitia aWalkPath = aInitialPath; + if(m_forceWinding) + { + s_cw = m_forceCw ? IN_PROGRESS : STUCK; + s_ccw = m_forceCw ? STUCK : IN_PROGRESS; + m_forceSingleDirection = true; + + } else { + + m_forceSingleDirection = false; + } + while( m_iteration < m_iterationLimit ) { if( s_cw != STUCK ) @@ -229,17 +240,20 @@ PNS_WALKAROUND::WALKAROUND_STATUS PNS_WALKAROUND::Route( const PNS_LINE& aInitia if( aWalkPath.SegmentCount() < 1 ) return STUCK; - if( aWalkPath.CPoint( -1 ) != aInitialPath.CPoint( -1 ) ) return STUCK; - if( aWalkPath.CPoint( 0 ) != aInitialPath.CPoint( 0 ) ) return STUCK; - + WALKAROUND_STATUS st = s_ccw == DONE || s_cw == DONE ? DONE : STUCK; - if( aOptimize && st == DONE ) - PNS_OPTIMIZER::Optimize( &aWalkPath, PNS_OPTIMIZER::MERGE_OBTUSE, m_world ); + if( st == DONE ) + { + if( aOptimize ) + PNS_OPTIMIZER::Optimize( &aWalkPath, PNS_OPTIMIZER::MERGE_OBTUSE, m_world ); + } + + return st; } diff --git a/pcbnew/router/pns_walkaround.h b/pcbnew/router/pns_walkaround.h index 95c2079b61..939ba426d0 100644 --- a/pcbnew/router/pns_walkaround.h +++ b/pcbnew/router/pns_walkaround.h @@ -39,6 +39,7 @@ public: { m_forceSingleDirection = false; m_forceLongerPath = false; + m_forceWinding = false; m_cursorApproachMode = false; m_itemMask = PNS_ITEM::ANY; } @@ -79,7 +80,11 @@ public: { m_forceSingleDirection = aForceSingleDirection; m_forceLongerPath = aForceSingleDirection; - //printf("FSD %d FPD %d\n", m_forceSingleDirection?1:0, m_forceLongerPath ? 1: 0); + } + + void SetSingleDirection2( bool aForceSingleDirection ) + { + m_forceSingleDirection = aForceSingleDirection; } void SetApproachCursor( bool aEnabled, const VECTOR2I& aPos ) @@ -88,6 +93,12 @@ public: m_cursorApproachMode = aEnabled; } + void SetForceWinding ( bool aEnabled, bool aCw ) + { + m_forceCw = aCw; + m_forceWinding = aEnabled; + } + WALKAROUND_STATUS Route( const PNS_LINE& aInitialPath, PNS_LINE& aWalkPath, bool aOptimize = true ); @@ -110,6 +121,8 @@ private: int m_itemMask; bool m_forceSingleDirection, m_forceLongerPath; bool m_cursorApproachMode; + bool m_forceWinding; + bool m_forceCw; VECTOR2I m_cursorPos; PNS_NODE::OPT_OBSTACLE m_currentObstacle[2]; bool m_recursiveCollision[2]; diff --git a/pcbnew/router/ranged_num.h b/pcbnew/router/ranged_num.h new file mode 100644 index 0000000000..99a5940e71 --- /dev/null +++ b/pcbnew/router/ranged_num.h @@ -0,0 +1,53 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013-2015 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef __RANGED_NUM_H +#define __RANGED_NUM_H + +template class RANGED_NUM { + public: + RANGED_NUM ( T aValue = 0, T aTollerancePlus = 0, T aTolleranceMinus = 0) : + m_value (aValue), + m_tollerancePlus ( aTollerancePlus ), + m_tolleranceMinus ( aTolleranceMinus ) + {} + + operator T() + { + return m_value; + } + + RANGED_NUM& operator= ( const T aValue ) + { + m_value = aValue; + return *this; + } + + bool Matches ( const T& aOther ) const + { + return ( aOther >= m_value - m_tolleranceMinus && aOther <= m_value + m_tollerancePlus ); + } + + private: + T m_value, m_tollerancePlus, m_tolleranceMinus; + +}; + +#endif diff --git a/pcbnew/router/router_menus.h b/pcbnew/router/router_menus.h new file mode 100644 index 0000000000..bafd7360ca --- /dev/null +++ b/pcbnew/router/router_menus.h @@ -0,0 +1,981 @@ +/* + * KiRouter - a push-and-(sometimes-)shove PCB router + * + * Copyright (C) 2013 CERN + * Author: Tomasz Wlostowski + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include + +#include +#include +#include + +#include "class_draw_panel_gal.h" +#include "class_board.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "router_tool.h" +#include "pns_segment.h" +#include "pns_router.h" +#include "pns_meander_placer.h" // fixme: move settings to separate header +#include "pns_tune_status_popup.h" +#include "trace.h" + +using namespace KIGFX; +using boost::optional; + +static TOOL_ACTION ACT_NewTrack( "pcbnew.InteractiveRouter.NewTrack", + AS_CONTEXT, 'X', + "New Track", "Starts laying a new track."); +static TOOL_ACTION ACT_EndTrack( "pcbnew.InteractiveRouter.EndTrack", + AS_CONTEXT, WXK_END, + "End Track", "Stops laying the current track."); +static TOOL_ACTION ACT_AutoEndRoute( "pcbnew.InteractiveRouter.AutoEndRoute", + AS_CONTEXT, 'F', + "Auto-end Track", "Automagically finishes currently routed track." ); +static TOOL_ACTION ACT_Drag( "pcbnew.InteractiveRouter.Drag", + AS_CONTEXT, 'G', + "Drag Track/Via", "Drags a track or a via." ); +static TOOL_ACTION ACT_PlaceThroughVia( "pcbnew.InteractiveRouter.PlaceVia", + AS_CONTEXT, 'V', + "Place Through Via", "Adds a through-hole via at the end of currently routed track." ); +static TOOL_ACTION ACT_PlaceBlindVia( "pcbnew.InteractiveRouter.PlaceBlindVia", + AS_CONTEXT, 'Z', + "Place Blind/Buried Via", "Adds a blind or buried via at the end of currently routed track." ); +static TOOL_ACTION ACT_PlaceMicroVia( "pcbnew.InteractiveRouter.PlaceMicroVia", + AS_CONTEXT, 'Q', + "Place Microvia", "Adds a microvia at the end of currently routed track." ); +static TOOL_ACTION ACT_CustomTrackWidth( "pcbnew.InteractiveRouter.CustomTrackWidth", + AS_CONTEXT, 'W', + "Custom Track Width", "Shows a dialog for changing the track width and via size."); +static TOOL_ACTION ACT_RouterOptions( "pcbnew.InteractiveRouter.RouterOptions", + AS_CONTEXT, 'E', + "Routing Options...", "Shows a dialog containing router options."); +static TOOL_ACTION ACT_SwitchPosture( "pcbnew.InteractiveRouter.SwitchPosture", + AS_CONTEXT, '/', + "Switch Track Posture", "Switches posture of the currenly routed track."); + +static TOOL_ACTION ACT_SetDpDimensions( "pcbnew.InteractiveRouter.SetDpDimensions", + AS_CONTEXT, 'D', + "Differential Pair Dimensions...", "Sets the width and gap of the currently routed differential pair."); + +static TOOL_ACTION ACT_SetLengthTune( "pcbnew.InteractiveRouter.LengthTunerSettings", + AS_CONTEXT, 'L', + "Length Tuning Settings", "Sets the length tuning parameters for currently routed item."); + +static TOOL_ACTION ACT_LengthTuneSpacingIncrease( "pcbnew.InteractiveRouter.LengthTunerSpacingIncrease", + AS_CONTEXT, '1', + "Length Tuning Settings", "Sets the length tuning parameters for currently routed item."); + +static TOOL_ACTION ACT_LengthTuneSpacingDecrease( "pcbnew.InteractiveRouter.LengthTunerSpacingDecrease", + AS_CONTEXT, '2', + "Length Tuning Settings", "Sets the length tuning parameters for currently routed item."); + +static TOOL_ACTION ACT_SetLengthTuneAmplIncrease( "pcbnew.InteractiveRouter.LengthTunerAmplIncrease", + AS_CONTEXT, '3', + "Length Tuning Settings", "Sets the length tuning parameters for currently routed item."); + +static TOOL_ACTION ACT_SetLengthTuneAmplDecrease( "pcbnew.InteractiveRouter.LengthTunerAmplDecrease", + AS_CONTEXT, '4', + "Length Tuning Settings", "Sets the length tuning parameters for currently routed item."); + + +ROUTER_TOOL::ROUTER_TOOL() : + TOOL_INTERACTIVE( "pcbnew.InteractiveRouter" ) +{ + m_router = NULL; +} + + +class CONTEXT_TRACK_WIDTH_MENU: public CONTEXT_MENU +{ +public: + CONTEXT_TRACK_WIDTH_MENU() + { + setCustomEventHandler( boost::bind( &CONTEXT_TRACK_WIDTH_MENU::handleCustomEvent, + this, _1 ) ); + } + + void SetBoard( BOARD* aBoard ) + { + BOARD_DESIGN_SETTINGS& bds = aBoard->GetDesignSettings(); + + wxString msg; + m_board = aBoard; + + Append( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, _( "Custom size" ), + wxEmptyString, wxITEM_CHECK ); + + Append( ID_POPUP_PCB_SELECT_AUTO_WIDTH, _( "Use the starting track width" ), + _( "Route using the width of the starting track." ), wxITEM_CHECK ); + + Append( ID_POPUP_PCB_SELECT_USE_NETCLASS_VALUES, _( "Use netclass values" ), + _( "Use track and via sizes from the net class" ), wxITEM_CHECK ); + + for( unsigned i = 0; i < bds.m_TrackWidthList.size(); i++ ) + { + msg = _( "Track "); + msg << StringFromValue( g_UserUnit, bds.m_TrackWidthList[i], true ); + + if( i == 0 ) + msg << _( " (from netclass)" ); + + Append( ID_POPUP_PCB_SELECT_WIDTH1 + i, msg, wxEmptyString, wxITEM_CHECK ); + } + + AppendSeparator(); + + for( unsigned i = 0; i < bds.m_ViasDimensionsList.size(); i++ ) + { + msg = _("Via "); + msg << StringFromValue( g_UserUnit, bds.m_ViasDimensionsList[i].m_Diameter, true ); + wxString drill = StringFromValue( g_UserUnit, + bds.m_ViasDimensionsList[i].m_Drill, + true ); + + if( bds.m_ViasDimensionsList[i].m_Drill <= 0 ) + { + msg << _ (", drill: default"); + } + else + { + msg << _ (", drill: ") << drill; + } + + if( i == 0 ) + msg << _( " (from netclass)" ); + + Append( ID_POPUP_PCB_SELECT_VIASIZE1 + i, msg, wxEmptyString, wxITEM_CHECK ); + } + } + +protected: + OPT_TOOL_EVENT handleCustomEvent( const wxEvent& aEvent ) + { +#if ID_POPUP_PCB_SELECT_VIASIZE1 < ID_POPUP_PCB_SELECT_WIDTH1 +#error You have changed event ids order, it breaks code. Check the source code for more details. +// Recognising type of event (track width/via size) is based on comparison if the event id is +// within a specific range. If ranges of event ids changes, then the following is not valid anymore. +#endif + BOARD_DESIGN_SETTINGS &bds = m_board->GetDesignSettings(); + + int id = aEvent.GetId(); + + // Initial settings, to be modified below + bds.m_UseConnectedTrackWidth = false; + bds.UseCustomTrackViaSize( false ); + + if( id == ID_POPUP_PCB_SELECT_CUSTOM_WIDTH ) + { + bds.UseCustomTrackViaSize( true ); + } + + else if( id == ID_POPUP_PCB_SELECT_AUTO_WIDTH ) + { + bds.m_UseConnectedTrackWidth = true; + } + + else if( id == ID_POPUP_PCB_SELECT_USE_NETCLASS_VALUES ) + { + bds.SetViaSizeIndex( 0 ); + bds.SetTrackWidthIndex( 0 ); + } + + else if( id > ID_POPUP_PCB_SELECT_VIASIZE1 ) // via size has changed + { + assert( id < ID_POPUP_PCB_SELECT_WIDTH_END_RANGE ); + + bds.SetViaSizeIndex( id - ID_POPUP_PCB_SELECT_VIASIZE1 ); + } + + else // track width has changed + { + assert( id >= ID_POPUP_PCB_SELECT_WIDTH1 ); + assert( id < ID_POPUP_PCB_SELECT_VIASIZE ); + + bds.SetTrackWidthIndex( id - ID_POPUP_PCB_SELECT_WIDTH1 ); + } + + return OPT_TOOL_EVENT( COMMON_ACTIONS::trackViaSizeChanged.MakeEvent() ); + } + + BOARD* m_board; +}; + + +class ROUTER_TOOL_MENU: public CONTEXT_MENU +{ +public: + ROUTER_TOOL_MENU( BOARD* aBoard, PNS_ROUTER_MODE aMode ) + { + SetTitle( wxT( "Interactive Router" ) ); + Add( ACT_NewTrack ); + Add( ACT_EndTrack ); +// Add( ACT_AutoEndRoute ); // fixme: not implemented yet. Sorry. + Add( ACT_Drag ); + Add( ACT_PlaceThroughVia ); + Add( ACT_PlaceBlindVia ); + Add( ACT_PlaceMicroVia ); + Add( ACT_SwitchPosture ); + + AppendSeparator(); + + CONTEXT_TRACK_WIDTH_MENU* trackMenu = new CONTEXT_TRACK_WIDTH_MENU; + trackMenu->SetBoard( aBoard ); + AppendSubMenu( trackMenu, wxT( "Select Track Width" ) ); + + Add( ACT_CustomTrackWidth ); + + if ( aMode == PNS_MODE_ROUTE_DIFF_PAIR ) + Add( ACT_SetDpDimensions ); + + AppendSeparator(); + Add( ACT_RouterOptions ); + } +}; + + +ROUTER_TOOL::~ROUTER_TOOL() +{ + delete m_router; +} + + +void ROUTER_TOOL::Reset( RESET_REASON aReason ) +{ + printf("RESET\n"); + if( m_router ) + delete m_router; + + m_frame = getEditFrame(); + m_ctls = getViewControls(); + m_board = getModel(); + + m_router = new PNS_ROUTER; + + m_router->ClearWorld(); + m_router->SetBoard( m_board ); + m_router->SyncWorld(); + m_router->LoadSettings( m_savedSettings ); + m_router->UpdateSizes( m_savedSizes ); + m_needsSync = false; + + if( getView() ) + m_router->SetView( getView() ); + + Go( &ROUTER_TOOL::RouteSingleTrace, COMMON_ACTIONS::routerActivateSingle.MakeEvent() ); + Go( &ROUTER_TOOL::RouteDiffPair, COMMON_ACTIONS::routerActivateDiffPair.MakeEvent() ); + Go( &ROUTER_TOOL::TuneSingleTrace, COMMON_ACTIONS::routerActivateTuneSingleTrace.MakeEvent() ); +} + +int ROUTER_TOOL::getDefaultWidth( int aNetCode ) +{ + int w, d1, d2; + + getNetclassDimensions( aNetCode, w, d1, d2 ); + + return w; +} + + +void ROUTER_TOOL::getNetclassDimensions( int aNetCode, int& aWidth, + int& aViaDiameter, int& aViaDrill ) +{ + BOARD_DESIGN_SETTINGS &bds = m_board->GetDesignSettings(); + + NETCLASSPTR netClass; + NETINFO_ITEM* ni = m_board->FindNet( aNetCode ); + + if( ni ) + { + wxString netClassName = ni->GetClassName(); + netClass = bds.m_NetClasses.Find( netClassName ); + } + + if( !netClass ) + netClass = bds.GetDefault(); + + aWidth = netClass->GetTrackWidth(); + aViaDiameter = netClass->GetViaDiameter(); + aViaDrill = netClass->GetViaDrill(); +} + + +PNS_ITEM* ROUTER_TOOL::pickSingleItem( const VECTOR2I& aWhere, int aNet, int aLayer ) +{ + int tl = getView()->GetTopLayer(); + + if( aLayer > 0 ) + tl = aLayer; + + PNS_ITEM* prioritized[4]; + + for( int i = 0; i < 4; i++ ) + prioritized[i] = 0; + + PNS_ITEMSET candidates = m_router->QueryHoverItems( aWhere ); + + BOOST_FOREACH( PNS_ITEM* item, candidates.Items() ) + { + if( !IsCopperLayer( item->Layers().Start() ) ) + continue; + + // fixme: this causes flicker with live loop removal... + //if( item->Parent() && !item->Parent()->ViewIsVisible() ) + // continue; + + if( aNet < 0 || item->Net() == aNet ) + { + if( item->OfKind( PNS_ITEM::VIA | PNS_ITEM::SOLID ) ) + { + if( !prioritized[2] ) + prioritized[2] = item; + if( item->Layers().Overlaps( tl ) ) + prioritized[0] = item; + } + else + { + if( !prioritized[3] ) + prioritized[3] = item; + if( item->Layers().Overlaps( tl ) ) + prioritized[1] = item; + } + } + } + + PNS_ITEM* rv = NULL; + PCB_EDIT_FRAME* frame = getEditFrame (); + DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)frame->GetDisplayOptions(); + + for( int i = 0; i < 4; i++ ) + { + PNS_ITEM* item = prioritized[i]; + + if( displ_opts->m_ContrastModeDisplay ) + if( item && !item->Layers().Overlaps( tl ) ) + item = NULL; + + if( item ) + { + rv = item; + break; + } + } + + if( rv && aLayer >= 0 && !rv->Layers().Overlaps( aLayer ) ) + rv = NULL; + + if( rv ) + TRACE( 0, "%s, layer : %d, tl: %d", rv->KindStr().c_str() % rv->Layers().Start() % tl ); + + return rv; +} + + +void ROUTER_TOOL::highlightNet( bool aEnabled, int aNetcode ) +{ + RENDER_SETTINGS* rs = getView()->GetPainter()->GetSettings(); + + if( aNetcode >= 0 && aEnabled ) + rs->SetHighlight( true, aNetcode ); + else + rs->SetHighlight( false ); + + getView()->UpdateAllLayersColor(); +} + + +void ROUTER_TOOL::handleCommonEvents( TOOL_EVENT& aEvent ) +{ +#ifdef DEBUG + if( aEvent.IsKeyPressed() ) + { + switch( aEvent.KeyCode() ) + { + case 'S': + TRACEn( 2, "saving drag/route log...\n" ); + m_router->DumpLog(); + break; + } + } + else +#endif + if( aEvent.IsAction( &ACT_RouterOptions ) ) + { + DIALOG_PNS_SETTINGS settingsDlg( m_frame, m_router->Settings() ); + + if( settingsDlg.ShowModal() ) + { + // FIXME: do we need an explicit update? + } + } + else if( aEvent.IsAction( &ACT_SetDpDimensions ) ) + { + PNS_SIZES_SETTINGS sizes = m_router->Sizes(); + DIALOG_PNS_DIFF_PAIR_DIMENSIONS settingsDlg( m_frame, sizes ); + + if( settingsDlg.ShowModal() ) + { + m_router->UpdateSizes( sizes ); + } + } + else if( aEvent.IsAction( &ACT_SetLengthTune ) ) + { + PNS_MEANDER_PLACER *placer = dynamic_cast ( m_router->Placer() ); + + if(!placer) + return; + + PNS_MEANDER_SETTINGS settings = placer->Settings(); + DIALOG_PNS_LENGTH_TUNING_SETTINGS settingsDlg( m_frame, settings, m_router->Mode() ); + + if( settingsDlg.ShowModal() ) + { + placer->UpdateSettings ( settings ); + } + } + else if( aEvent.IsAction( &ACT_CustomTrackWidth ) ) + { + BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); + DIALOG_TRACK_VIA_SIZE sizeDlg( m_frame, bds ); + + if( sizeDlg.ShowModal() ) + { + bds.UseCustomTrackViaSize( true ); + m_toolMgr->RunAction( COMMON_ACTIONS::trackViaSizeChanged ); + } + } + + else if( aEvent.IsAction( &COMMON_ACTIONS::trackViaSizeChanged ) ) + { + + PNS_SIZES_SETTINGS sizes ( m_savedSizes ); + sizes.ImportCurrent ( m_board->GetDesignSettings() ); + m_router->UpdateSizes ( sizes ); + } +} + + +void ROUTER_TOOL::updateStartItem( TOOL_EVENT& aEvent ) +{ + int tl = getView()->GetTopLayer(); + VECTOR2I cp = m_ctls->GetCursorPosition(); + PNS_ITEM* startItem = NULL; + + if( aEvent.IsMotion() || aEvent.IsClick() ) + { + bool snapEnabled = !aEvent.Modifier( MD_SHIFT ); + + VECTOR2I p( aEvent.Position() ); + startItem = pickSingleItem( p ); + m_router->EnableSnapping ( snapEnabled ); + + if( !snapEnabled && startItem && !startItem->Layers().Overlaps( tl ) ) + startItem = NULL; + + if( startItem && startItem->Net() >= 0 ) + { + bool dummy; + VECTOR2I psnap = m_router->SnapToItem( startItem, p, dummy ); + + if( snapEnabled ) + { + m_startSnapPoint = psnap; + m_ctls->ForceCursorPosition( true, psnap ); + } + else + { + m_startSnapPoint = cp; + m_ctls->ForceCursorPosition( false ); + } + +// if( startItem->Layers().IsMultilayer() ) +// m_startLayer = tl; +// else +// m_startLayer = startItem->Layers().Start(); + + m_startItem = startItem; + } + else + { + m_startItem = NULL; + m_startSnapPoint = cp; + m_ctls->ForceCursorPosition( false ); + } + } +} + + +void ROUTER_TOOL::updateEndItem( TOOL_EVENT& aEvent ) +{ + VECTOR2I mp = m_ctls->GetMousePosition(); + VECTOR2I p = getView()->ToWorld( mp ); + VECTOR2I cp = m_ctls->GetCursorPosition(); + int layer; + bool snapEnabled = !aEvent.Modifier( MD_CTRL ); + + m_router->EnableSnapping( snapEnabled ); + + if( !snapEnabled || m_router->GetCurrentNet() < 0 || !m_startItem ) + { + m_endItem = NULL; + m_endSnapPoint = cp; + return; + } + + bool dummy; + + if( m_router->IsPlacingVia() ) + layer = -1; + else + layer = m_router->GetCurrentLayer(); + + PNS_ITEM* endItem = pickSingleItem( p, m_startItem->Net(), layer ); + + if( endItem ) + { + VECTOR2I cursorPos = m_router->SnapToItem( endItem, p, dummy ); + m_ctls->ForceCursorPosition( true, cursorPos ); + m_endItem = endItem; + m_endSnapPoint = cursorPos; + } + else + { + m_endItem = NULL; + m_endSnapPoint = cp; + m_ctls->ForceCursorPosition( false ); + } + + if( m_endItem ) + TRACE( 0, "%s, layer : %d", m_endItem->KindStr().c_str() % m_endItem->Layers().Start() ); +} + +int ROUTER_TOOL::getStartLayer( const PNS_ITEM* aItem ) +{ + int tl = getView()->GetTopLayer(); + + if( m_startItem ) + { + const PNS_LAYERSET& ls = m_startItem->Layers(); + + if( ls.Overlaps( tl ) ) + return tl; + else + return ls.Start(); + } + + return tl; +} +void ROUTER_TOOL::switchLayerOnViaPlacement() +{ + int al = m_frame->GetActiveLayer(); + int cl = m_router->GetCurrentLayer(); + + if( cl != al ) + { + m_router->SwitchLayer( al ); + } + + optional newLayer = m_router->Sizes().PairedLayer( cl ); + + if( newLayer ) + { + m_router->SwitchLayer ( *newLayer ); + m_frame->SetActiveLayer ( ToLAYER_ID( *newLayer ) ); + } +} + +bool ROUTER_TOOL::onViaCommand( VIATYPE_T aType ) +{ + BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); + + const int layerCount = bds.GetCopperLayerCount(); + int currentLayer = m_router->GetCurrentLayer(); + + PNS_SIZES_SETTINGS sizes = m_router->Sizes(); + + // fixme: P&S supports more than one fixed layer pair. Update the dialog? + sizes.ClearLayerPairs(); + sizes.AddLayerPair( m_frame->GetScreen()->m_Route_Layer_TOP, + m_frame->GetScreen()->m_Route_Layer_BOTTOM ); + + if( !m_router->IsPlacingVia() ) + { + // Cannot place microvias or blind vias if not allowed (obvious) + if( ( aType == VIA_BLIND_BURIED ) && ( !bds.m_BlindBuriedViaAllowed ) ) + return false; + if( ( aType == VIA_MICROVIA ) && ( !bds.m_MicroViasAllowed ) ) + return false; + + //Can only place through vias on 2-layer boards + if( ( aType != VIA_THROUGH ) && ( layerCount <= 2 ) ) + return false; + + //Can only place microvias if we're on an outer layer, or directly adjacent to one + if( ( aType == VIA_MICROVIA ) && ( currentLayer > In1_Cu ) && ( currentLayer < layerCount-2 ) ) + return false; + + //Cannot place blind vias with front/back as the layer pair, this doesn't make sense + if( ( aType == VIA_BLIND_BURIED ) && ( sizes.GetLayerTop() == F_Cu ) && ( sizes.GetLayerBottom() == B_Cu ) ) + return false; + } + + + sizes.SetViaType ( aType ); + m_router->ToggleViaPlacement( ); + m_router->UpdateSizes( sizes ); + + m_router->Move( m_endSnapPoint, m_endItem ); // refresh + + return false; +} + +//void ROUTER_TOOL::prepareRouting() +//{} + +void ROUTER_TOOL::performRouting() +{ + bool saveUndoBuffer = true; + + int routingLayer = getStartLayer ( m_startItem ); + m_frame->SetActiveLayer( ToLAYER_ID ( routingLayer ) ); + // fixme: switch on invisible layer + + if( m_startItem && m_startItem->Net() >= 0 ) + { + highlightNet( true, m_startItem->Net() ); + // Update track width and via size shown in main toolbar comboboxes + m_frame->SetCurrentNetClass( m_startItem->Parent()->GetNetClass()->GetName() ); + } + else + m_frame->SetCurrentNetClass( NETCLASS::Default ); + + m_ctls->ForceCursorPosition( false ); + m_ctls->SetAutoPan( true ); + + PNS_SIZES_SETTINGS sizes ( m_savedSizes ); + sizes.Init ( m_board, m_startItem ); + sizes.AddLayerPair ( m_frame->GetScreen()->m_Route_Layer_TOP, + m_frame->GetScreen()->m_Route_Layer_BOTTOM ); + m_router->UpdateSizes( sizes ); + + if ( !m_router->StartRouting( m_startSnapPoint, m_startItem, routingLayer ) ) + { + wxMessageBox ( m_router->FailureReason(), _("Error") ); + highlightNet ( false ); + return; + } + + + m_tuneStatusPopup = new PNS_TUNE_STATUS_POPUP ( m_frame ); + m_tuneStatusPopup->Popup(); + + m_endItem = NULL; + m_endSnapPoint = m_startSnapPoint; + + while( OPT_TOOL_EVENT evt = Wait() ) + { + if( evt->IsCancel() || evt->IsActivate() ) + break; + else if( evt->Action() == TA_UNDO_REDO ) + { + saveUndoBuffer = false; + break; + } + else if( evt->IsMotion() ) + { + + wxPoint p = wxGetMousePosition(); + p.x+=20; + p.y+=20; + m_tuneStatusPopup->Update (m_router); + m_tuneStatusPopup->Move(p); + + + updateEndItem( *evt ); + m_router->SetOrthoMode ( evt->Modifier ( MD_CTRL ) ); + m_router->Move( m_endSnapPoint, m_endItem ); + } + else if( evt->IsClick( BUT_LEFT ) ) + { + updateEndItem( *evt ); + bool needLayerSwitch = m_router->IsPlacingVia(); + + if( m_router->FixRoute( m_endSnapPoint, m_endItem ) ) + break; + + if( needLayerSwitch ) + switchLayerOnViaPlacement(); + + // Synchronize the indicated layer + m_frame->SetActiveLayer( ToLAYER_ID( m_router->GetCurrentLayer() ) ); + m_router->Move( m_endSnapPoint, m_endItem ); + } + else if( evt->IsAction( &ACT_PlaceThroughVia ) ) + { + onViaCommand ( VIA_THROUGH ); + } + else if( evt->IsAction( &ACT_PlaceBlindVia ) ) + { + onViaCommand ( VIA_BLIND_BURIED ); + } + else if( evt->IsAction( &ACT_PlaceMicroVia ) ) + { + onViaCommand ( VIA_MICROVIA ); + } + else if( evt->IsAction( &ACT_SwitchPosture ) ) + { + m_router->FlipPosture(); + m_router->Move( m_endSnapPoint, m_endItem ); // refresh + } + else if( evt->IsAction( &COMMON_ACTIONS::layerChanged ) ) + { + updateEndItem( *evt ); + m_router->SwitchLayer( m_frame->GetActiveLayer() ); + m_router->Move( m_endSnapPoint, m_endItem ); // refresh + } + else if( evt->IsAction( &ACT_EndTrack ) ) + { + if( m_router->FixRoute( m_endSnapPoint, m_endItem ) ) + break; + } + + handleCommonEvents( *evt ); + } + + m_router->StopRouting(); + + if( saveUndoBuffer ) + { + // Save the recent changes in the undo buffer + m_frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED ); + m_router->ClearUndoBuffer(); + m_frame->OnModify(); + } + else + { + // It was interrupted by TA_UNDO_REDO event, so we have to sync the world now + m_needsSync = true; + } + + delete m_tuneStatusPopup; + + m_ctls->SetAutoPan( false ); + m_ctls->ForceCursorPosition( false ); + highlightNet( false ); +} + +int ROUTER_TOOL::RouteSingleTrace( TOOL_EVENT& aEvent ) +{ + m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Route Track" ) ); + return mainLoop( PNS_MODE_ROUTE_SINGLE ); +} + +int ROUTER_TOOL::RouteDiffPair( TOOL_EVENT& aEvent ) +{ + m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Router Differential Pair" ) ); + return mainLoop( PNS_MODE_ROUTE_DIFF_PAIR ); +} + +int ROUTER_TOOL::TuneSingleTrace( TOOL_EVENT& aEvent ) +{ + m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Tune Track Length" ) ); + return mainLoop( PNS_MODE_TUNE_SINGLE ); +} + +int ROUTER_TOOL::mainLoop( PNS_ROUTER_MODE aMode ) +{ + PCB_EDIT_FRAME* frame = getEditFrame(); + BOARD* board = getModel(); + + // Deselect all items + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + + Activate(); + + m_router->SetMode ( aMode ); + + m_ctls->SetSnapping( true ); + m_ctls->ShowCursor( true ); + + std::auto_ptr ctxMenu ( new ROUTER_TOOL_MENU( board, aMode ) ); + SetContextMenu ( ctxMenu.get() ); + + // Main loop: keep receiving events + while( OPT_TOOL_EVENT evt = Wait() ) + { + if( m_needsSync ) + { + m_router->SyncWorld(); + m_router->SetView( getView() ); + m_needsSync = false; + } + + if( evt->IsCancel() || evt->IsActivate() ) + break; // Finish + else if( evt->Action() == TA_UNDO_REDO ) + m_needsSync = true; + else if( evt->IsMotion() ) + updateStartItem( *evt ); + else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACT_NewTrack ) ) + { + updateStartItem( *evt ); + + if( evt->Modifier( MD_CTRL ) ) + performDragging(); + else + performRouting(); + } + else if( evt->IsAction( &ACT_Drag ) ) + performDragging(); + + handleCommonEvents( *evt ); + } + + // Restore the default settings + m_ctls->SetAutoPan( false ); + m_ctls->ShowCursor( false ); + frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString ); + + // Store routing settings till the next invocation + m_savedSettings = m_router->Settings(); + m_savedSizes = m_router->Sizes(); + + return 0; +} + +int ROUTER_TOOL::InlineDrag ( TOOL_EVENT& aEvent ) +{ + return 0; + + #if 0 + VIEW_CONTROLS* ctls = getViewControls(); + PCB_EDIT_FRAME* frame = getEditFrame(); + BOARD* board = getModel(); + + printf("RouterTool::InlineDrag!\n"); + Reset(); + + m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); + ctls->SetSnapping( true ); + ctls->ShowCursor( true ); + + m_startItem = m_router->QueryItemByParent ( ) + + bool dragStarted = m_router->StartDragging( m_startSnapPoint, m_startItem ); + + while( OPT_TOOL_EVENT evt = Wait() ) + { + if( evt->IsCancel() ) + break; + else if( evt->IsMotion() ) + { + updateEndItem( *evt ); + m_router->Move( m_endSnapPoint, m_endItem ); + } + else if( evt->IsUp( BUT_LEFT ) ) + { + if( m_router->FixRoute( m_endSnapPoint, m_endItem ) ) + break; + } + } + + if( m_router->RoutingInProgress() ) + m_router->StopRouting(); + + return 0; + #endif + +} + +void ROUTER_TOOL::performDragging() +{ + PCB_EDIT_FRAME* frame = getEditFrame(); + bool saveUndoBuffer = true; + VIEW_CONTROLS* ctls = getViewControls(); + + bool dragStarted = m_router->StartDragging( m_startSnapPoint, m_startItem ); + + if( !dragStarted ) + return; + + if( m_startItem && m_startItem->Net() >= 0 ) + highlightNet( true, m_startItem->Net() ); + + ctls->ForceCursorPosition( false ); + ctls->SetAutoPan( true ); + + while( OPT_TOOL_EVENT evt = Wait() ) + { + if( evt->IsCancel() || evt->IsActivate() ) + break; + else if( evt->Action() == TA_UNDO_REDO ) + { + saveUndoBuffer = false; + break; + } + else if( evt->IsMotion() ) + { + updateEndItem( *evt ); + m_router->Move( m_endSnapPoint, m_endItem ); + } + else if( evt->IsClick( BUT_LEFT ) ) + { + if( m_router->FixRoute( m_endSnapPoint, m_endItem ) ) + break; + } + + handleCommonEvents( *evt ); + } + + if( m_router->RoutingInProgress() ) + m_router->StopRouting(); + + if( saveUndoBuffer ) + { + // Save the recent changes in the undo buffer + frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED ); + m_router->ClearUndoBuffer(); + frame->OnModify(); + } + else + { + // It was interrupted by TA_UNDO_REDO event, so we have to sync the world now + m_needsSync = true; + } + + ctls->SetAutoPan( false ); + ctls->ForceCursorPosition( false ); + highlightNet( false ); +} diff --git a/pcbnew/router/router_preview_item.cpp b/pcbnew/router/router_preview_item.cpp index c51c91c18c..a3a44baf5a 100644 --- a/pcbnew/router/router_preview_item.cpp +++ b/pcbnew/router/router_preview_item.cpp @@ -57,8 +57,16 @@ void ROUTER_PREVIEW_ITEM::Update( const PNS_ITEM* aItem ) { m_originLayer = aItem->Layers().Start(); - assert( m_originLayer >= 0 ); + if (aItem->OfKind ( PNS_ITEM::LINE )) + { + const PNS_LINE *l=static_cast (aItem); + if(!l->SegmentCount()) + return; + } + + assert( m_originLayer >= 0 ); + m_layer = m_originLayer; m_color = getLayerColor( m_originLayer ); m_color.a = 0.8; @@ -159,65 +167,65 @@ void ROUTER_PREVIEW_ITEM::ViewDraw( int aLayer, KIGFX::GAL* aGal ) const switch( m_shape->Type() ) { - case SH_LINE_CHAIN: - { - const SHAPE_LINE_CHAIN* l = (const SHAPE_LINE_CHAIN*) m_shape; - drawLineChain( *l, aGal ); - break; - } - - case SH_SEGMENT: - { - const SHAPE_SEGMENT* s = (const SHAPE_SEGMENT*) m_shape; - aGal->DrawLine( s->GetSeg().A, s->GetSeg().B ); - - if( m_clearance > 0 ) + case SH_LINE_CHAIN: { - aGal->SetLayerDepth( ClearanceOverlayDepth ); - aGal->SetStrokeColor( COLOR4D( DARKDARKGRAY ) ); - aGal->SetLineWidth( m_width + 2 * m_clearance ); + const SHAPE_LINE_CHAIN* l = (const SHAPE_LINE_CHAIN*) m_shape; + drawLineChain( *l, aGal ); + break; + } + + case SH_SEGMENT: + { + const SHAPE_SEGMENT* s = (const SHAPE_SEGMENT*) m_shape; aGal->DrawLine( s->GetSeg().A, s->GetSeg().B ); + + if( m_clearance > 0 ) + { + aGal->SetLayerDepth( ClearanceOverlayDepth ); + aGal->SetStrokeColor( COLOR4D( DARKDARKGRAY ) ); + aGal->SetLineWidth( m_width + 2 * m_clearance ); + aGal->DrawLine( s->GetSeg().A, s->GetSeg().B ); + } + + break; } - break; - } - - case SH_CIRCLE: - { - const SHAPE_CIRCLE* c = (const SHAPE_CIRCLE*) m_shape; - aGal->DrawCircle( c->GetCenter(), c->GetRadius() ); - - if( m_clearance > 0 ) + case SH_CIRCLE: { - aGal->SetLayerDepth( ClearanceOverlayDepth ); - aGal->SetFillColor( COLOR4D( DARKDARKGRAY ) ); - aGal->SetIsStroke( false ); - aGal->DrawCircle( c->GetCenter(), c->GetRadius() + m_clearance ); + const SHAPE_CIRCLE* c = (const SHAPE_CIRCLE*) m_shape; + aGal->DrawCircle( c->GetCenter(), c->GetRadius() ); + + if( m_clearance > 0 ) + { + aGal->SetLayerDepth( ClearanceOverlayDepth ); + aGal->SetFillColor( COLOR4D( DARKDARKGRAY ) ); + aGal->SetIsStroke( false ); + aGal->DrawCircle( c->GetCenter(), c->GetRadius() + m_clearance ); + } + + break; } - break; - } - - case SH_RECT: - { - const SHAPE_RECT* r = (const SHAPE_RECT*) m_shape; - aGal->DrawRectangle( r->GetPosition(), r->GetPosition() + r->GetSize() ); - - if( m_clearance > 0 ) + case SH_RECT: { - aGal->SetLayerDepth( ClearanceOverlayDepth ); - VECTOR2I p0( r->GetPosition() ), s( r->GetSize() ); - aGal->SetStrokeColor( COLOR4D( DARKDARKGRAY ) ); - aGal->SetIsStroke( true ); - aGal->SetLineWidth( 2 * m_clearance ); - aGal->DrawLine( p0, VECTOR2I( p0.x + s.x, p0.y ) ); - aGal->DrawLine( p0, VECTOR2I( p0.x, p0.y + s.y ) ); - aGal->DrawLine( p0 + s , VECTOR2I( p0.x + s.x, p0.y ) ); - aGal->DrawLine( p0 + s, VECTOR2I( p0.x, p0.y + s.y ) ); - } + const SHAPE_RECT* r = (const SHAPE_RECT*) m_shape; + aGal->DrawRectangle( r->GetPosition(), r->GetPosition() + r->GetSize() ); - break; - } + if( m_clearance > 0 ) + { + aGal->SetLayerDepth( ClearanceOverlayDepth ); + VECTOR2I p0( r->GetPosition() ), s( r->GetSize() ); + aGal->SetStrokeColor( COLOR4D( DARKDARKGRAY ) ); + aGal->SetIsStroke( true ); + aGal->SetLineWidth( 2 * m_clearance ); + aGal->DrawLine( p0, VECTOR2I( p0.x + s.x, p0.y ) ); + aGal->DrawLine( p0, VECTOR2I( p0.x, p0.y + s.y ) ); + aGal->DrawLine( p0 + s , VECTOR2I( p0.x + s.x, p0.y ) ); + aGal->DrawLine( p0 + s, VECTOR2I( p0.x, p0.y + s.y ) ); + } + + break; + } case SH_CONVEX: case SH_POLYGON: diff --git a/pcbnew/router/router_tool.cpp b/pcbnew/router/router_tool.cpp index a36bc83da4..09f30942d5 100644 --- a/pcbnew/router/router_tool.cpp +++ b/pcbnew/router/router_tool.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -75,17 +76,18 @@ static TOOL_ACTION ACT_PlaceMicroVia( "pcbnew.InteractiveRouter.PlaceMicroVia", static TOOL_ACTION ACT_CustomTrackWidth( "pcbnew.InteractiveRouter.CustomTrackWidth", AS_CONTEXT, 'W', "Custom Track Width", "Shows a dialog for changing the track width and via size."); -static TOOL_ACTION ACT_RouterOptions( "pcbnew.InteractiveRouter.RouterOptions", - AS_CONTEXT, 'E', - "Routing Options...", "Shows a dialog containing router options."); static TOOL_ACTION ACT_SwitchPosture( "pcbnew.InteractiveRouter.SwitchPosture", AS_CONTEXT, '/', "Switch Track Posture", "Switches posture of the currenly routed track."); +static TOOL_ACTION ACT_SetDpDimensions( "pcbnew.InteractiveRouter.SetDpDimensions", + AS_CONTEXT, 'D', + "Differential Pair Dimensions...", "Sets the width and gap of the currently routed differential pair."); + + ROUTER_TOOL::ROUTER_TOOL() : - TOOL_INTERACTIVE( "pcbnew.InteractiveRouter" ) -{ - m_router = NULL; + PNS_TOOL_BASE( "pcbnew.InteractiveRouter" ) +{ } @@ -208,7 +210,7 @@ protected: class ROUTER_TOOL_MENU: public CONTEXT_MENU { public: - ROUTER_TOOL_MENU( BOARD* aBoard ) + ROUTER_TOOL_MENU( BOARD* aBoard, PNS_ROUTER_MODE aMode ) { SetTitle( wxT( "Interactive Router" ) ); Add( ACT_NewTrack ); @@ -227,40 +229,33 @@ public: AppendSubMenu( trackMenu, wxT( "Select Track Width" ) ); Add( ACT_CustomTrackWidth ); - + + if ( aMode == PNS_MODE_ROUTE_DIFF_PAIR ) + Add( ACT_SetDpDimensions ); + AppendSeparator(); - Add( ACT_RouterOptions ); + Add( PNS_TOOL_BASE::ACT_RouterOptions ); } }; ROUTER_TOOL::~ROUTER_TOOL() { - delete m_router; + } void ROUTER_TOOL::Reset( RESET_REASON aReason ) { - if( m_router ) - delete m_router; - - m_router = new PNS_ROUTER; - - TRACEn( 0, "Reset" ); - m_router->ClearWorld(); - m_router->SetBoard( getModel() ); - m_router->SyncWorld(); - m_router->LoadSettings( m_settings ); - m_needsSync = false; - - if( getView() ) - m_router->SetView( getView() ); - - Go( &ROUTER_TOOL::Main, COMMON_ACTIONS::routerActivate.MakeEvent() ); + PNS_TOOL_BASE::Reset( aReason ); + + Go( &ROUTER_TOOL::RouteSingleTrace, COMMON_ACTIONS::routerActivateSingle.MakeEvent() ); + Go( &ROUTER_TOOL::RouteDiffPair, COMMON_ACTIONS::routerActivateDiffPair.MakeEvent() ); + Go( &ROUTER_TOOL::DpDimensionsDialog, COMMON_ACTIONS::routerActivateDpDimensionsDialog.MakeEvent() ); + Go( &ROUTER_TOOL::SettingsDialog, COMMON_ACTIONS::routerActivateSettingsDialog.MakeEvent() ); + } - int ROUTER_TOOL::getDefaultWidth( int aNetCode ) { int w, d1, d2; @@ -274,11 +269,10 @@ int ROUTER_TOOL::getDefaultWidth( int aNetCode ) void ROUTER_TOOL::getNetclassDimensions( int aNetCode, int& aWidth, int& aViaDiameter, int& aViaDrill ) { - BOARD* board = getModel(); - BOARD_DESIGN_SETTINGS &bds = board->GetDesignSettings(); + BOARD_DESIGN_SETTINGS &bds = m_board->GetDesignSettings(); NETCLASSPTR netClass; - NETINFO_ITEM* ni = board->FindNet( aNetCode ); + NETINFO_ITEM* ni = m_board->FindNet( aNetCode ); if( ni ) { @@ -294,96 +288,8 @@ void ROUTER_TOOL::getNetclassDimensions( int aNetCode, int& aWidth, aViaDrill = netClass->GetViaDrill(); } - -PNS_ITEM* ROUTER_TOOL::pickSingleItem( const VECTOR2I& aWhere, int aNet, int aLayer ) +void ROUTER_TOOL::handleCommonEvents( const TOOL_EVENT& aEvent ) { - int tl = getView()->GetTopLayer(); - - if( aLayer > 0 ) - tl = aLayer; - - PNS_ITEM* prioritized[4]; - - for( int i = 0; i < 4; i++ ) - prioritized[i] = 0; - - PNS_ITEMSET candidates = m_router->QueryHoverItems( aWhere ); - - BOOST_FOREACH( PNS_ITEM* item, candidates.Items() ) - { - if( !IsCopperLayer( item->Layers().Start() ) ) - continue; - - // fixme: this causes flicker with live loop removal... - //if( item->Parent() && !item->Parent()->ViewIsVisible() ) - // continue; - - if( aNet < 0 || item->Net() == aNet ) - { - if( item->OfKind( PNS_ITEM::VIA | PNS_ITEM::SOLID ) ) - { - if( !prioritized[2] ) - prioritized[2] = item; - if( item->Layers().Overlaps( tl ) ) - prioritized[0] = item; - } - else - { - if( !prioritized[3] ) - prioritized[3] = item; - if( item->Layers().Overlaps( tl ) ) - prioritized[1] = item; - } - } - } - - PNS_ITEM* rv = NULL; - PCB_EDIT_FRAME* frame = getEditFrame (); - DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)frame->GetDisplayOptions(); - - for( int i = 0; i < 4; i++ ) - { - PNS_ITEM* item = prioritized[i]; - - if( displ_opts->m_ContrastModeDisplay ) - if( item && !item->Layers().Overlaps( tl ) ) - item = NULL; - - if( item ) - { - rv = item; - break; - } - } - - if( rv && aLayer >= 0 && !rv->Layers().Overlaps( aLayer ) ) - rv = NULL; - - if( rv ) - TRACE( 0, "%s, layer : %d, tl: %d", rv->KindStr().c_str() % rv->Layers().Start() % tl ); - - return rv; -} - - -void ROUTER_TOOL::highlightNet( bool aEnabled, int aNetcode ) -{ - RENDER_SETTINGS* rs = getView()->GetPainter()->GetSettings(); - - if( aNetcode >= 0 && aEnabled ) - rs->SetHighlight( true, aNetcode ); - else - rs->SetHighlight( false ); - - getView()->UpdateAllLayersColor(); -} - - -void ROUTER_TOOL::handleCommonEvents( TOOL_EVENT& aEvent ) -{ - PCB_EDIT_FRAME* frame = getEditFrame (); - BOARD* board = getModel (); - #ifdef DEBUG if( aEvent.IsKeyPressed() ) { @@ -399,18 +305,27 @@ void ROUTER_TOOL::handleCommonEvents( TOOL_EVENT& aEvent ) #endif if( aEvent.IsAction( &ACT_RouterOptions ) ) { - DIALOG_PNS_SETTINGS settingsDlg( frame, m_router->Settings() ); + DIALOG_PNS_SETTINGS settingsDlg( m_frame, m_router->Settings() ); if( settingsDlg.ShowModal() ) { // FIXME: do we need an explicit update? } } + else if( aEvent.IsAction( &ACT_SetDpDimensions ) ) + { + PNS_SIZES_SETTINGS sizes = m_router->Sizes(); + DIALOG_PNS_DIFF_PAIR_DIMENSIONS settingsDlg( m_frame, sizes ); + if( settingsDlg.ShowModal() ) + { + m_router->UpdateSizes( sizes ); + } + } else if( aEvent.IsAction( &ACT_CustomTrackWidth ) ) { - BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings(); - DIALOG_TRACK_VIA_SIZE sizeDlg( frame, bds ); + BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); + DIALOG_TRACK_VIA_SIZE sizeDlg( m_frame, bds ); if( sizeDlg.ShowModal() ) { @@ -422,107 +337,12 @@ void ROUTER_TOOL::handleCommonEvents( TOOL_EVENT& aEvent ) else if( aEvent.IsAction( &COMMON_ACTIONS::trackViaSizeChanged ) ) { - PNS_SIZES_SETTINGS sizes; - sizes.ImportCurrent ( board->GetDesignSettings() ); + PNS_SIZES_SETTINGS sizes ( m_router->Sizes() ); + sizes.ImportCurrent ( m_board->GetDesignSettings() ); m_router->UpdateSizes ( sizes ); } } - -void ROUTER_TOOL::updateStartItem( TOOL_EVENT& aEvent ) -{ - VIEW_CONTROLS* ctls = getViewControls(); - int tl = getView()->GetTopLayer(); - VECTOR2I cp = ctls->GetCursorPosition(); - PNS_ITEM* startItem = NULL; - - if( aEvent.IsMotion() || aEvent.IsClick() ) - { - VECTOR2I p = aEvent.Position(); - startItem = pickSingleItem( p ); - bool snapEnabled = !aEvent.Modifier( MD_SHIFT ); - m_router->EnableSnapping ( snapEnabled ); - - if( !snapEnabled && startItem && !startItem->Layers().Overlaps( tl ) ) - startItem = NULL; - - if( startItem && startItem->Net() >= 0 ) - { - bool dummy; - VECTOR2I psnap = m_router->SnapToItem( startItem, p, dummy ); - - if( snapEnabled ) - { - m_startSnapPoint = psnap; - ctls->ForceCursorPosition( true, psnap ); - } - else - { - m_startSnapPoint = cp; - ctls->ForceCursorPosition( false ); - } - -// if( startItem->Layers().IsMultilayer() ) -// m_startLayer = tl; -// else -// m_startLayer = startItem->Layers().Start(); - - m_startItem = startItem; - } - else - { - m_startItem = NULL; - m_startSnapPoint = cp; - ctls->ForceCursorPosition( false ); - } - } -} - - -void ROUTER_TOOL::updateEndItem( TOOL_EVENT& aEvent ) -{ - VIEW_CONTROLS* ctls = getViewControls(); - VECTOR2I p = getView()->ToWorld( ctls->GetMousePosition() ); - VECTOR2I cp = ctls->GetCursorPosition(); - int layer; - bool snapEnabled = !aEvent.Modifier( MD_SHIFT ); - - m_router->EnableSnapping( snapEnabled ); - - if( !snapEnabled || m_router->GetCurrentNet() < 0 || !m_startItem ) - { - m_endItem = NULL; - m_endSnapPoint = cp; - return; - } - - bool dummy; - - if( m_router->IsPlacingVia() ) - layer = -1; - else - layer = m_router->GetCurrentLayer(); - - PNS_ITEM* endItem = pickSingleItem( p, m_startItem->Net(), layer ); - - if( endItem ) - { - VECTOR2I cursorPos = m_router->SnapToItem( endItem, p, dummy ); - ctls->ForceCursorPosition( true, cursorPos ); - m_endItem = endItem; - m_endSnapPoint = cursorPos; - } - else - { - m_endItem = NULL; - m_endSnapPoint = cp; - ctls->ForceCursorPosition( false ); - } - - if( m_endItem ) - TRACE( 0, "%s, layer : %d", m_endItem->KindStr().c_str() % m_endItem->Layers().Start() ); -} - int ROUTER_TOOL::getStartLayer( const PNS_ITEM* aItem ) { int tl = getView()->GetTopLayer(); @@ -541,9 +361,7 @@ int ROUTER_TOOL::getStartLayer( const PNS_ITEM* aItem ) } void ROUTER_TOOL::switchLayerOnViaPlacement() { - PCB_EDIT_FRAME* frame = getEditFrame(); - - int al = frame->GetActiveLayer(); + int al = m_frame->GetActiveLayer(); int cl = m_router->GetCurrentLayer(); if( cl != al ) @@ -556,24 +374,23 @@ void ROUTER_TOOL::switchLayerOnViaPlacement() if( newLayer ) { m_router->SwitchLayer ( *newLayer ); - frame->SetActiveLayer ( ToLAYER_ID( *newLayer ) ); + m_frame->SetActiveLayer ( ToLAYER_ID( *newLayer ) ); } } bool ROUTER_TOOL::onViaCommand( VIATYPE_T aType ) { - BOARD* board = getModel (); - BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings(); - PCB_EDIT_FRAME* frame = getEditFrame(); - + BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); + const int layerCount = bds.GetCopperLayerCount(); int currentLayer = m_router->GetCurrentLayer(); PNS_SIZES_SETTINGS sizes = m_router->Sizes(); + // fixme: P&S supports more than one fixed layer pair. Update the dialog? sizes.ClearLayerPairs(); - sizes.AddLayerPair( frame->GetScreen()->m_Route_Layer_TOP, - frame->GetScreen()->m_Route_Layer_BOTTOM ); + sizes.AddLayerPair( m_frame->GetScreen()->m_Route_Layer_TOP, + m_frame->GetScreen()->m_Route_Layer_BOTTOM ); if( !m_router->IsPlacingVia() ) { @@ -606,40 +423,77 @@ bool ROUTER_TOOL::onViaCommand( VIATYPE_T aType ) return false; } -void ROUTER_TOOL::performRouting() -{ - PCB_EDIT_FRAME* frame = getEditFrame(); - bool saveUndoBuffer = true; - VIEW_CONTROLS* ctls = getViewControls(); - BOARD* board = getModel(); - int routingLayer = getStartLayer ( m_startItem ); - frame->SetActiveLayer( ToLAYER_ID ( routingLayer ) ); +bool ROUTER_TOOL::prepareInteractive() +{ + int routingLayer = getStartLayer ( m_startItem ); + m_frame->SetActiveLayer( ToLAYER_ID ( routingLayer ) ); + // fixme: switch on invisible layer if( m_startItem && m_startItem->Net() >= 0 ) { highlightNet( true, m_startItem->Net() ); // Update track width and via size shown in main toolbar comboboxes - frame->SetCurrentNetClass( m_startItem->Parent()->GetNetClass()->GetName() ); + m_frame->SetCurrentNetClass( m_startItem->Parent()->GetNetClass()->GetName() ); } else - frame->SetCurrentNetClass( NETCLASS::Default ); + m_frame->SetCurrentNetClass( NETCLASS::Default ); - ctls->ForceCursorPosition( false ); - ctls->SetAutoPan( true ); + m_ctls->ForceCursorPosition( false ); + m_ctls->SetAutoPan( true ); - PNS_SIZES_SETTINGS sizes; - sizes.Init ( board, m_startItem ); - sizes.AddLayerPair ( frame->GetScreen()->m_Route_Layer_TOP, - frame->GetScreen()->m_Route_Layer_BOTTOM ); + PNS_SIZES_SETTINGS sizes ( m_router->Sizes() ); + + sizes.Init ( m_board, m_startItem ); + sizes.AddLayerPair ( m_frame->GetScreen()->m_Route_Layer_TOP, + m_frame->GetScreen()->m_Route_Layer_BOTTOM ); m_router->UpdateSizes( sizes ); - - m_router->StartRouting( m_startSnapPoint, m_startItem, routingLayer ); + + if ( !m_router->StartRouting( m_startSnapPoint, m_startItem, routingLayer ) ) + { + wxMessageBox ( m_router->FailureReason(), _("Error") ); + highlightNet ( false ); + return false; + } m_endItem = NULL; m_endSnapPoint = m_startSnapPoint; + return true; +} + +bool ROUTER_TOOL::finishInteractive( bool aSaveUndoBuffer ) +{ + m_router->StopRouting(); + + if( aSaveUndoBuffer ) + { + // Save the recent changes in the undo buffer + m_frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED ); + m_router->ClearUndoBuffer(); + m_frame->OnModify(); + } + else + { + // It was interrupted by TA_UNDO_REDO event, so we have to sync the world now + m_needsSync = true; + } + + m_ctls->SetAutoPan( false ); + m_ctls->ForceCursorPosition( false ); + highlightNet( false ); + + return true; +} + +void ROUTER_TOOL::performRouting() +{ + bool saveUndoBuffer = true; + + if ( !prepareInteractive( ) ) + return; + while( OPT_TOOL_EVENT evt = Wait() ) { if( evt->IsCancel() || evt->IsActivate() ) @@ -652,6 +506,7 @@ void ROUTER_TOOL::performRouting() else if( evt->IsMotion() ) { updateEndItem( *evt ); + m_router->SetOrthoMode ( evt->Modifier ( MD_CTRL ) ); m_router->Move( m_endSnapPoint, m_endItem ); } else if( evt->IsClick( BUT_LEFT ) ) @@ -663,13 +518,10 @@ void ROUTER_TOOL::performRouting() break; if( needLayerSwitch ) - { switchLayerOnViaPlacement(); - } // Synchronize the indicated layer - frame->SetActiveLayer( ToLAYER_ID( m_router->GetCurrentLayer() ) ); - + m_frame->SetActiveLayer( ToLAYER_ID( m_router->GetCurrentLayer() ) ); m_router->Move( m_endSnapPoint, m_endItem ); } else if( evt->IsAction( &ACT_PlaceThroughVia ) ) @@ -692,7 +544,7 @@ void ROUTER_TOOL::performRouting() else if( evt->IsAction( &COMMON_ACTIONS::layerChanged ) ) { updateEndItem( *evt ); - m_router->SwitchLayer( frame->GetActiveLayer() ); + m_router->SwitchLayer( m_frame->GetActiveLayer() ); m_router->Move( m_endSnapPoint, m_endItem ); // refresh } else if( evt->IsAction( &ACT_EndTrack ) ) @@ -704,42 +556,67 @@ void ROUTER_TOOL::performRouting() handleCommonEvents( *evt ); } - m_router->StopRouting(); + finishInteractive ( saveUndoBuffer ); +} - if( saveUndoBuffer ) - { - // Save the recent changes in the undo buffer - frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED ); - m_router->ClearUndoBuffer(); - frame->OnModify(); - } - else - { - // It was interrupted by TA_UNDO_REDO event, so we have to sync the world now - m_needsSync = true; - } +int ROUTER_TOOL::DpDimensionsDialog( const TOOL_EVENT& aEvent ) +{ + Activate(); - ctls->SetAutoPan( false ); - ctls->ForceCursorPosition( false ); - highlightNet( false ); + PNS_SIZES_SETTINGS sizes = m_router->Sizes(); + DIALOG_PNS_DIFF_PAIR_DIMENSIONS settingsDlg( m_frame, sizes ); + + if( settingsDlg.ShowModal() ) + { + m_router->UpdateSizes( sizes ); + m_savedSizes = sizes; + } + + return 0; +} + +int ROUTER_TOOL::SettingsDialog( const TOOL_EVENT& aEvent ) +{ + Activate(); + + DIALOG_PNS_SETTINGS settingsDlg( m_frame, m_router->Settings() ); + + if( settingsDlg.ShowModal() ) + { + m_savedSettings = m_router->Settings(); + } + return 0; } -int ROUTER_TOOL::Main( const TOOL_EVENT& aEvent ) +int ROUTER_TOOL::RouteSingleTrace( const TOOL_EVENT& aEvent ) +{ + m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Route Track" ) ); + return mainLoop( PNS_MODE_ROUTE_SINGLE ); +} + +int ROUTER_TOOL::RouteDiffPair( const TOOL_EVENT& aEvent ) +{ + m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Router Differential Pair" ) ); + return mainLoop( PNS_MODE_ROUTE_DIFF_PAIR ); +} + +int ROUTER_TOOL::mainLoop( PNS_ROUTER_MODE aMode ) { - VIEW_CONTROLS* ctls = getViewControls(); PCB_EDIT_FRAME* frame = getEditFrame(); BOARD* board = getModel(); // Deselect all items m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); - frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Interactive Router" ) ); + Activate(); - ctls->SetSnapping( true ); - ctls->ShowCursor( true ); + m_router->SetMode ( aMode ); + + m_ctls->SetSnapping( true ); + m_ctls->ShowCursor( true ); - std::auto_ptr ctxMenu ( new ROUTER_TOOL_MENU( board ) ); + std::auto_ptr ctxMenu ( new ROUTER_TOOL_MENU( board, aMode ) ); SetContextMenu ( ctxMenu.get() ); // Main loop: keep receiving events @@ -774,17 +651,17 @@ int ROUTER_TOOL::Main( const TOOL_EVENT& aEvent ) } // Restore the default settings - ctls->SetAutoPan( false ); - ctls->ShowCursor( false ); + m_ctls->SetAutoPan( false ); + m_ctls->ShowCursor( false ); frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString ); // Store routing settings till the next invocation - m_settings = m_router->Settings(); + m_savedSettings = m_router->Settings(); + m_savedSizes = m_router->Sizes(); return 0; } - void ROUTER_TOOL::performDragging() { PCB_EDIT_FRAME* frame = getEditFrame(); @@ -845,3 +722,9 @@ void ROUTER_TOOL::performDragging() ctls->ForceCursorPosition( false ); highlightNet( false ); } + +int ROUTER_TOOL::InlineDrag ( const TOOL_EVENT& aEvent ) +{ + return 0; +} + diff --git a/pcbnew/router/router_tool.h b/pcbnew/router/router_tool.h index 314e844ae5..73c4ea917f 100644 --- a/pcbnew/router/router_tool.h +++ b/pcbnew/router/router_tool.h @@ -22,61 +22,41 @@ #ifndef __ROUTER_TOOL_H #define __ROUTER_TOOL_H -#include +#include "pns_tool_base.h" -#include -#include - -#include - -#include "pns_routing_settings.h" - -class PNS_ROUTER; -class PNS_ITEM; - -class APIEXPORT ROUTER_TOOL : public TOOL_INTERACTIVE +class APIEXPORT ROUTER_TOOL : public PNS_TOOL_BASE { public: ROUTER_TOOL(); ~ROUTER_TOOL(); void Reset( RESET_REASON aReason ); - int Main( const TOOL_EVENT& aEvent ); + + int RouteSingleTrace ( const TOOL_EVENT& aEvent ); + int RouteDiffPair ( const TOOL_EVENT& aEvent ); + int InlineDrag ( const TOOL_EVENT& aEvent ); + + int DpDimensionsDialog ( const TOOL_EVENT& aEvent ); + int SettingsDialog ( const TOOL_EVENT& aEvent ); private: - PNS_ITEM* pickSingleItem( const VECTOR2I& aWhere, int aNet = -1, int aLayer = -1 ); + + int mainLoop( PNS_ROUTER_MODE aMode ); int getDefaultWidth( int aNetCode ); void performRouting(); void performDragging(); - void highlightNet( bool aEnabled, int aNetcode = -1 ); - - void updateStartItem( TOOL_EVENT& aEvent ); - void updateEndItem( TOOL_EVENT& aEvent ); - void getNetclassDimensions( int aNetCode, int& aWidth, int& aViaDiameter, int& aViaDrill ); - void handleCommonEvents( TOOL_EVENT& evt ); + void handleCommonEvents( const TOOL_EVENT& evt ); int getStartLayer( const PNS_ITEM* aItem ); void switchLayerOnViaPlacement(); bool onViaCommand( VIATYPE_T aType ); - MSG_PANEL_ITEMS m_panelItems; - - PNS_ROUTER* m_router; - PNS_ROUTING_SETTINGS m_settings; ///< Stores routing settings between router invocations - - PNS_ITEM* m_startItem; - int m_startLayer; - VECTOR2I m_startSnapPoint; - - PNS_ITEM* m_endItem; - VECTOR2I m_endSnapPoint; - - ///> Flag marking that the router's world needs syncing. - bool m_needsSync; + bool prepareInteractive( ); + bool finishInteractive( bool aSaveUndoBuffer ); }; #endif diff --git a/pcbnew/tools/common_actions.cpp b/pcbnew/tools/common_actions.cpp index 759dae77f5..2a5f4e84ea 100644 --- a/pcbnew/tools/common_actions.cpp +++ b/pcbnew/tools/common_actions.cpp @@ -390,11 +390,41 @@ TOOL_ACTION COMMON_ACTIONS::toBeDone( "pcbnew.Control.toBeDone", AS_GLOBAL, 0, // dialog saying it is not implemented yet "", "" ); // so users are aware of that -TOOL_ACTION COMMON_ACTIONS::routerActivate( "pcbnew.InteractiveRouter", +TOOL_ACTION COMMON_ACTIONS::routerActivateSingle( "pcbnew.InteractiveRouter.SingleTrack", AS_GLOBAL, 'X', - "Run push & shove router", "Run push & shove router", AF_ACTIVATE ); + "Run push & shove router (single tracks)", "Run push & shove router (single tracks)", AF_ACTIVATE ); +TOOL_ACTION COMMON_ACTIONS::routerActivateDiffPair( "pcbnew.InteractiveRouter.DiffPair", + AS_GLOBAL, '6', + "Run push & shove router (differential pairs)", "Run push & shove router (differential pairs)", AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::routerActivateSettingsDialog( "pcbnew.InteractiveRouter.SettingsDialog", + AS_GLOBAL, 0, + "Open Interactive Router settings", "Open Interactive Router settings", AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::routerActivateDpDimensionsDialog( "pcbnew.InteractiveRouter.DpDimensionsDialog", + AS_GLOBAL, 0, + "Open Differential Pair Dimension settings", "Open Differential Pair Dimension settings", AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::routerActivateTuneSingleTrace( "pcbnew.LengthTuner.TuneSingleTrack", + AS_GLOBAL, '7', + "Tune length of a single track", "", AF_ACTIVATE ); + + +TOOL_ACTION COMMON_ACTIONS::routerActivateTuneDiffPair( "pcbnew.LengthTuner.TuneDiffPair", + AS_GLOBAL, '8', + "Tune length of a differential pair", "", AF_ACTIVATE ); + + +TOOL_ACTION COMMON_ACTIONS::routerActivateTuneDiffPairSkew( "pcbnew.LengthTuner.TuneDiffPairSkew", + AS_GLOBAL, '9', + "Tune skew of a differential pair", "", AF_ACTIVATE ); + +TOOL_ACTION COMMON_ACTIONS::routerInlineDrag( "pcbnew.InteractiveRouter.InlineDrag", + AS_GLOBAL, 0, + "", "" ); + // Point editor TOOL_ACTION COMMON_ACTIONS::pointEditorUpdate( "pcbnew.PointEditor.update", AS_GLOBAL, 0, @@ -404,7 +434,6 @@ TOOL_ACTION COMMON_ACTIONS::pointEditorBreakOutline( "pcbnew.PointEditor.breakOu AS_GLOBAL, 0, "Create corner", "Create corner" ); - // Placement tool TOOL_ACTION COMMON_ACTIONS::alignTop( "pcbnew.Place.alignTop", AS_GLOBAL, 0, @@ -445,7 +474,25 @@ boost::optional COMMON_ACTIONS::TranslateLegacyId( int aId ) return COMMON_ACTIONS::placeModule.MakeEvent(); case ID_TRACK_BUTT: - return COMMON_ACTIONS::routerActivate.MakeEvent(); + return COMMON_ACTIONS::routerActivateSingle.MakeEvent(); + + case ID_DIFF_PAIR_BUTT: + return COMMON_ACTIONS::routerActivateDiffPair.MakeEvent(); + + case ID_TUNE_SINGLE_TRACK_LEN_BUTT: + return COMMON_ACTIONS::routerActivateTuneSingleTrace.MakeEvent(); + + case ID_TUNE_DIFF_PAIR_LEN_BUTT: + return COMMON_ACTIONS::routerActivateTuneDiffPair.MakeEvent(); + + case ID_TUNE_DIFF_PAIR_SKEW_BUTT: + return COMMON_ACTIONS::routerActivateTuneDiffPairSkew.MakeEvent(); + + case ID_MENU_INTERACTIVE_ROUTER_SETTINGS: + return COMMON_ACTIONS::routerActivateSettingsDialog.MakeEvent(); + + case ID_MENU_DIFF_PAIR_DIMENSIONS: + return COMMON_ACTIONS::routerActivateDpDimensionsDialog.MakeEvent(); case ID_PCB_ZONES_BUTT: return COMMON_ACTIONS::drawZone.MakeEvent(); diff --git a/pcbnew/tools/common_actions.h b/pcbnew/tools/common_actions.h index 850459bad7..5efe6e9515 100644 --- a/pcbnew/tools/common_actions.h +++ b/pcbnew/tools/common_actions.h @@ -116,8 +116,29 @@ public: static TOOL_ACTION arcPosture; // Push and Shove Router Tool + /// Activation of the Push and Shove router - static TOOL_ACTION routerActivate; + static TOOL_ACTION routerActivateSingle; + + /// Activation of the Push and Shove router (differential pair mode) + static TOOL_ACTION routerActivateDiffPair; + + /// Activation of the Push and Shove router (tune single line mode) + static TOOL_ACTION routerActivateTuneSingleTrace; + + /// Activation of the Push and Shove router (diff pair tuning mode) + static TOOL_ACTION routerActivateTuneDiffPair; + + /// Activation of the Push and Shove router (skew tuning mode) + static TOOL_ACTION routerActivateTuneDiffPairSkew; + + /// Activation of the Push and Shove settings dialogs + static TOOL_ACTION routerActivateSettingsDialog; + static TOOL_ACTION routerActivateDpDimensionsDialog; + + + /// Activation of the Push and Shove router (inline dragging mode) + static TOOL_ACTION routerInlineDrag; // Point Editor /// Update edit points