router: differential pairs & length tuning support
This commit is contained in:
parent
e5deafb4bb
commit
112adccbcb
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2014-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Push and Shove diff pair dimensions (gap) settings dialog.
|
||||
*/
|
||||
|
||||
#include "dialog_pns_diff_pair_dimensions.h"
|
||||
#include <router/pns_sizes_settings.h>
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2014-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.or/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Push and Shove diff pair dimensions (gap) settings dialog.
|
||||
*/
|
||||
|
||||
#ifndef __dialog_diff_pair_dimensions_settings__
|
||||
#define __dialog_diff_pair_dimensions_settings__
|
||||
|
||||
#include <wx_unit_binder.h>
|
||||
|
||||
#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__
|
|
@ -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 );
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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 <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/intl.h>
|
||||
class DIALOG_SHIM;
|
||||
|
||||
#include "dialog_shim.h"
|
||||
#include <wx/string.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/checkbox.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/dialog.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// 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__
|
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2014-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Length tuner settings dialog.
|
||||
*/
|
||||
|
||||
#include "dialog_pns_length_tuning_settings.h"
|
||||
#include <router/pns_meander_placer.h>
|
||||
|
||||
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 );
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2014 CERN
|
||||
* Author: Maciej Suminski <maciej.suminski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.or/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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 <wx_unit_binder.h>
|
||||
|
||||
#include <router/pns_router.h>
|
||||
|
||||
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__
|
|
@ -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 );
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -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 <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/intl.h>
|
||||
class DIALOG_SHIM;
|
||||
|
||||
#include "dialog_shim.h"
|
||||
#include <wx/string.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/choice.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/statbox.h>
|
||||
#include <wx/bitmap.h>
|
||||
#include <wx/image.h>
|
||||
#include <wx/icon.h>
|
||||
#include <wx/statbmp.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/dialog.h>
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// 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__
|
File diff suppressed because it is too large
Load Diff
|
@ -31,14 +31,19 @@
|
|||
|
||||
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.SetValue( m_settings.GetCustomTrackWidth() );
|
||||
m_viaDiameter.SetValue( m_settings.GetCustomViaSize() );
|
||||
m_viaDrill.SetValue( m_settings.GetCustomViaDrill() );
|
||||
|
||||
m_trackWidth->SetFocus();
|
||||
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 )
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
#ifndef __dialog_track_via_size__
|
||||
#define __dialog_track_via_size__
|
||||
|
||||
#include <wx_unit_binder.h>
|
||||
|
||||
#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;
|
||||
|
||||
|
|
|
@ -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 );
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -11,12 +11,13 @@
|
|||
#include <wx/artprov.h>
|
||||
#include <wx/xrc/xmlres.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wxunittext.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/gdicmn.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/settings.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/dialog.h>
|
||||
|
@ -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;
|
||||
|
|
|
@ -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" ) );
|
||||
|
|
|
@ -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} )
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -0,0 +1,319 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include "class_draw_panel_gal.h"
|
||||
#include "class_board.h"
|
||||
|
||||
#include <wxPcbStruct.h>
|
||||
#include <pcbnew_id.h>
|
||||
#include <view/view_controls.h>
|
||||
#include <pcb_painter.h>
|
||||
#include <dialogs/dialog_pns_settings.h>
|
||||
#include <dialogs/dialog_pns_length_tuning_settings.h>
|
||||
|
||||
#include <tool/context_menu.h>
|
||||
#include <tools/common_actions.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 "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 <PNS_MEANDER_PLACER_BASE *> ( 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 <PNS_MEANDER_PLACER *> ( 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<TUNER_TOOL_MENU> 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;
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
* Author: Maciej Suminski <maciej.suminski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
|
@ -21,6 +21,8 @@
|
|||
#ifndef __PNS_ALGO_BASE_H
|
||||
#define __PNS_ALGO_BASE_H
|
||||
|
||||
#include <wx/wx.h> // for wxString
|
||||
|
||||
#include "pns_routing_settings.h"
|
||||
|
||||
class PNS_ROUTER;
|
||||
|
@ -54,6 +56,7 @@ public:
|
|||
///> Returns the logger object, allowing to dump geometry to a file.
|
||||
virtual PNS_LOGGER* Logger();
|
||||
|
||||
|
||||
private:
|
||||
PNS_ROUTER* m_router;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,825 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
|
||||
#include <geometry/shape.h>
|
||||
#include <geometry/shape_rect.h>
|
||||
#include <geometry/shape_circle.h>
|
||||
#include <geometry/shape_segment.h>
|
||||
|
||||
#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<PNS_SEGMENT *> (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<DP_CANDIDATE> 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<const SHAPE_RECT*> ( shP )->GetWidth();
|
||||
int h = static_cast<const SHAPE_RECT*> ( 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<const SHAPE_SEGMENT*> ( shP )->GetWidth();
|
||||
SEG s = static_cast<const SHAPE_SEGMENT*> ( 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 <PNS_SEGMENT*> (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<int64_t> 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,457 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __PNS_DIFF_PAIR_H
|
||||
#define __PNS_DIFF_PAIR_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <geometry/shape.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
|
||||
#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<PNS_DP_GATEWAY>& 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<PNS_DP_GATEWAY> 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> 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<int> ( 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<int> 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<int> m_gapConstraint;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
|
@ -0,0 +1,778 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <colors.h>
|
||||
#include <class_board.h>
|
||||
#include <class_board_item.h>
|
||||
#include <class_netinfo.h>
|
||||
|
||||
#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<PNS_LINE *> ( &m_currentTrace.PLine( ) ) );
|
||||
t.Add( const_cast<PNS_LINE *> ( &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<PNS_SEGMENT*> ( 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 <PNS_SOLID *> (aItem);
|
||||
netNameN = netNameBase + suffix;
|
||||
} else {
|
||||
primRef = primN = static_cast <PNS_SOLID *> (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<PNS_ITEM*> items;
|
||||
|
||||
OPT_VECTOR2I refAnchor = getDanglingAnchor ( m_currentNode, primRef );
|
||||
|
||||
if(!refAnchor)
|
||||
return false;
|
||||
|
||||
m_currentNode->AllItemsInNet( refNet, items );
|
||||
double bestDist = std::numeric_limits<double>::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<int> &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 );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2014 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PNS_DIFF_PLACER_H
|
||||
#define __PNS_DIFF_PLACER_H
|
||||
|
||||
#include <math/vector2d.h>
|
||||
|
||||
#include <geometry/shape.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
|
||||
#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<int> &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<PNS_DP_PRIMITIVE_PAIR> 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
|
|
@ -0,0 +1,459 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2014 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <base_units.h> // God forgive me doing this...
|
||||
#include <colors.h>
|
||||
|
||||
#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<PNS_SEGMENT *>(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<const PNS_LINE *> (item) )
|
||||
totalP += l->CLine( ).Length( );
|
||||
|
||||
}
|
||||
BOOST_FOREACH ( const PNS_ITEM *item, m_tunedPathN.CItems() )
|
||||
{
|
||||
if ( const PNS_LINE *l = dyn_cast<const PNS_LINE *> (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<const PNS_LINE *> (item) )
|
||||
Router()->DisplayDebugLine ( l->CLine(), 5, 10000 );
|
||||
|
||||
}
|
||||
|
||||
BOOST_FOREACH ( const PNS_ITEM *item, m_tunedPathN.CItems() )
|
||||
{
|
||||
if ( const PNS_LINE *l = dyn_cast<const PNS_LINE *> (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;
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2014 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PNS_DP_MEANDER_PLACER_H
|
||||
#define __PNS_DP_MEANDER_PLACER_H
|
||||
|
||||
#include <math/vector2d.h>
|
||||
|
||||
#include <geometry/shape.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
|
||||
#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
|
|
@ -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
|
||||
};
|
||||
|
||||
|
|
|
@ -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 )
|
||||
{
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#ifndef __PNS_ITEMSET_H
|
||||
#define __PNS_ITEMSET_H
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "pns_item.h"
|
||||
|
@ -36,15 +36,23 @@
|
|||
class PNS_ITEMSET
|
||||
{
|
||||
public:
|
||||
typedef std::vector<PNS_ITEM*> ITEMS;
|
||||
typedef std::deque<PNS_ITEM*> 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
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
class PNS_JOINT : public PNS_ITEM
|
||||
{
|
||||
public:
|
||||
typedef std::vector<PNS_ITEM*> LINKED_ITEMS;
|
||||
typedef std::deque<PNS_ITEM*> 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 )
|
||||
{
|
||||
|
|
|
@ -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 ( );
|
||||
}
|
||||
|
|
|
@ -89,6 +89,11 @@ 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;
|
||||
|
|
|
@ -31,13 +31,14 @@
|
|||
#include "pns_shove.h"
|
||||
#include "pns_utils.h"
|
||||
#include "pns_router.h"
|
||||
#include "pns_topology.h"
|
||||
|
||||
#include <class_board_item.h>
|
||||
|
||||
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<PNS_NODE> 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<int> &aNets ) const
|
||||
{
|
||||
aNets.push_back( m_currentNet );
|
||||
}
|
||||
|
|
|
@ -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<int> &aNets ) const;
|
||||
private:
|
||||
/**
|
||||
* Function route()
|
||||
|
@ -343,6 +348,7 @@ 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
|
||||
|
|
|
@ -0,0 +1,580 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2014 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <base_units.h> // God forgive me doing this...
|
||||
#include <colors.h>
|
||||
|
||||
#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 ) );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,498 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PNS_MEANDER_H
|
||||
#define __PNS_MEANDER_H
|
||||
|
||||
#include <math/vector2d.h>
|
||||
|
||||
#include <geometry/shape.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
|
||||
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<PNS_MEANDER_SHAPE*> & 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<PNS_MEANDER_SHAPE *> m_meanders;
|
||||
|
||||
bool m_dual;
|
||||
int m_width;
|
||||
int m_baselineOffset;
|
||||
};
|
||||
|
||||
#endif // __PNS_MEANDER_H
|
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <base_units.h> // God forgive me doing this...
|
||||
#include <colors.h>
|
||||
|
||||
#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<PNS_SEGMENT *>( 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<const PNS_LINE *>( 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<const PNS_LINE *>( 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;
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2014 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PNS_MEANDER_PLACER_H
|
||||
#define __PNS_MEANDER_PLACER_H
|
||||
|
||||
#include <math/vector2d.h>
|
||||
|
||||
#include <geometry/shape.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
|
||||
#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
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PNS_MEANDER_PLACER_BASE_H
|
||||
#define __PNS_MEANDER_PLACER_BASE_H
|
||||
|
||||
#include <math/vector2d.h>
|
||||
|
||||
#include <geometry/shape.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
|
||||
#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
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <base_units.h> // God forgive me doing this...
|
||||
#include <colors.h>
|
||||
|
||||
#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<PNS_SEGMENT *>( 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<const PNS_LINE *>( 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;
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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
|
|
@ -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<PNS_LINE *>(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<PNS_JOINT*>& aFoundJoints )
|
||||
{
|
||||
std::deque<PNS_JOINT*> searchQueue;
|
||||
|
@ -846,51 +862,9 @@ void PNS_NODE::MapConnectivity ( PNS_JOINT* aStart, std::vector<PNS_JOINT*>& 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<PNS_ITEM*> disconnected;
|
||||
std::vector<PNS_JOINT*> 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<PNS_LINE*>& aLines )
|
||||
|
@ -1192,13 +1166,13 @@ void PNS_NODE::AllItemsInNet( int aNet, std::set<PNS_ITEM*>& 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;
|
||||
}
|
||||
|
|
|
@ -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<int> 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<PNS_JOINT*> & 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<PNS_ITEM*>& 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
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <geometry/shape_rect.h>
|
||||
|
||||
#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<PNS_ITEM*>();
|
||||
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<RVERTEX> 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<const PNS_SEGMENT*> ( 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 );
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2014 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PNS_PLACEMENT_ALGO_H
|
||||
#define __PNS_PLACEMENT_ALGO_H
|
||||
|
||||
#include <math/vector2d.h>
|
||||
|
||||
#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<int> &aNets ) const
|
||||
{
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -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 <router/router_preview_item.h>
|
||||
|
||||
|
@ -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<D_PAD*>( 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<D_PAD*>( 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<int> 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 <const PNS_LINE *> (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<int> 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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <profile.h>
|
||||
|
||||
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<PNS_SEGMENT *>( 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<PNS_LINE*> 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;i<aL->SegmentCount();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<const PNS_LINE*> (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<const PNS_LINE*> (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<PNS_LINE*>::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<int> 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<int> PNS_SHOVE::findShovedVertexRange( PNS_LINE* aL )
|
||||
{
|
||||
RANGE<int> 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;
|
||||
|
|
|
@ -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,7 +113,10 @@ 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<int> 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<class T> 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<SPRINGBACK_TAG> m_nodeStack;
|
||||
std::vector<PNS_LINE*> m_lineStack;
|
||||
std::vector<PNS_LINE*> 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
|
||||
|
|
|
@ -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,9 +56,22 @@ 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<int, int> m_layerPairs;
|
||||
|
|
|
@ -49,6 +49,11 @@ public:
|
|||
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; }
|
||||
|
|
|
@ -0,0 +1,273 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <wx/numdlg.h>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include "class_draw_panel_gal.h"
|
||||
#include "class_board.h"
|
||||
|
||||
#include <wxPcbStruct.h>
|
||||
#include <id.h>
|
||||
#include <macros.h>
|
||||
#include <pcbnew_id.h>
|
||||
#include <view/view_controls.h>
|
||||
#include <pcbcommon.h>
|
||||
#include <pcb_painter.h>
|
||||
#include <dialogs/dialog_pns_settings.h>
|
||||
#include <dialogs/dialog_pns_diff_pair_dimensions.h>
|
||||
#include <dialogs/dialog_pns_length_tuning_settings.h>
|
||||
#include <dialogs/dialog_track_via_size.h>
|
||||
#include <base_units.h>
|
||||
|
||||
#include <tool/context_menu.h>
|
||||
#include <tools/common_actions.h>
|
||||
|
||||
#include <ratsnest_data.h>
|
||||
|
||||
#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<PCB_EDIT_FRAME>();
|
||||
m_ctls = getViewControls();
|
||||
m_board = getModel<BOARD>();
|
||||
|
||||
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<PCB_EDIT_FRAME> ();
|
||||
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() );
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2014 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
* Author: Maciej Suminski <maciej.suminski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PNS_TOOL_BASE_H
|
||||
#define __PNS_TOOL_BASE_H
|
||||
|
||||
#include <import_export.h>
|
||||
|
||||
#include <math/vector2d.h>
|
||||
#include <tool/tool_interactive.h>
|
||||
|
||||
#include <msgpanel.h>
|
||||
|
||||
#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
|
|
@ -0,0 +1,360 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 <class_board.h>
|
||||
|
||||
bool PNS_TOPOLOGY::SimplifyLine ( PNS_LINE *aLine )
|
||||
{
|
||||
|
||||
if( !aLine->LinkedSegments() || !aLine->SegmentCount() )
|
||||
return false;
|
||||
|
||||
PNS_SEGMENT *root = (*aLine->LinkedSegments())[0];
|
||||
std::auto_ptr<PNS_LINE> l ( m_world->AssembleLine( root ) );
|
||||
SHAPE_LINE_CHAIN simplified ( l->CLine() );
|
||||
|
||||
simplified.Simplify();
|
||||
|
||||
if( simplified.PointCount() != l->PointCount() )
|
||||
{
|
||||
std::auto_ptr<PNS_LINE> 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<PNS_JOINT*> 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<PNS_SEGMENT *>( 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<PNS_NODE> 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<PNS_ITEM*> 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<PNS_ITEM *>& 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 <PNS_SEGMENT *> (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<PNS_ITEM *> 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<PNS_ITEM *> coupledItems;
|
||||
|
||||
m_world->AllItemsInNet( coupledNet, coupledItems );
|
||||
|
||||
PNS_SEGMENT *coupledSeg = NULL, *refSeg;
|
||||
int minDist = std::numeric_limits<int>::max();
|
||||
|
||||
if( ( refSeg = dyn_cast<PNS_SEGMENT*>( aStart ) ) != NULL )
|
||||
{
|
||||
BOOST_FOREACH ( PNS_ITEM *item, coupledItems )
|
||||
{
|
||||
if ( PNS_SEGMENT *s = dyn_cast<PNS_SEGMENT* >(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<PNS_LINE> lp ( m_world->AssembleLine ( refSeg ) );
|
||||
std::auto_ptr<PNS_LINE> 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;
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PNS_TOPOLOGY_H
|
||||
#define __PNS_TOPOLOGY_H
|
||||
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
#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<PNS_JOINT *> 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<PNS_ITEM *>& aVisited );
|
||||
|
||||
PNS_NODE *m_world;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -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 <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#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 <PNS_MEANDER_PLACER_BASE *> ( 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();
|
||||
}
|
||||
|
|
@ -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 <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <wx_status_popup.h>
|
||||
|
||||
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_*/
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "pns_utils.h"
|
||||
#include "pns_line.h"
|
||||
#include "pns_via.h"
|
||||
#include "pns_router.h"
|
||||
|
||||
#include <geometry/shape_segment.h>
|
||||
|
@ -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<<i) & mask )
|
||||
{
|
||||
VECTOR2I v = DIRECTION_45((DIRECTION_45::Directions)i).ToVector() * 100000;
|
||||
DrawDebugSeg ( SEG (p, p+v), color );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
OPT_BOX2I ChangedArea ( const PNS_ITEM *aItemA, const PNS_ITEM *aItemB )
|
||||
{
|
||||
if ( aItemA->OfKind ( PNS_ITEM::VIA ) && aItemB->OfKind ( PNS_ITEM::VIA ) )
|
||||
{
|
||||
const PNS_VIA *va = static_cast <const PNS_VIA *> (aItemA);
|
||||
const PNS_VIA *vb = static_cast <const PNS_VIA *> (aItemB);
|
||||
|
||||
return va->ChangedArea (vb);
|
||||
} else if ( aItemA->OfKind ( PNS_ITEM::LINE ) && aItemB->OfKind ( PNS_ITEM::LINE ) )
|
||||
{
|
||||
const PNS_LINE *la = static_cast <const PNS_LINE *> (aItemA);
|
||||
const PNS_LINE *lb = static_cast <const PNS_LINE *> (aItemB);
|
||||
|
||||
return la->ChangedArea (lb);
|
||||
}
|
||||
return OPT_BOX2I();
|
||||
}
|
|
@ -22,12 +22,15 @@
|
|||
#define __PNS_UTILS_H
|
||||
|
||||
#include <math/vector2d.h>
|
||||
#include <math/box2.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
#include <geometry/shape_segment.h>
|
||||
#include <geometry/shape_rect.h>
|
||||
|
||||
#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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013-2015 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __RANGED_NUM_H
|
||||
#define __RANGED_NUM_H
|
||||
|
||||
template <class T> 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
|
|
@ -0,0 +1,981 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2013 CERN
|
||||
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <wx/numdlg.h>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include "class_draw_panel_gal.h"
|
||||
#include "class_board.h"
|
||||
|
||||
#include <wxPcbStruct.h>
|
||||
#include <id.h>
|
||||
#include <macros.h>
|
||||
#include <pcbnew_id.h>
|
||||
#include <view/view_controls.h>
|
||||
#include <pcbcommon.h>
|
||||
#include <pcb_painter.h>
|
||||
#include <dialogs/dialog_pns_settings.h>
|
||||
#include <dialogs/dialog_pns_diff_pair_dimensions.h>
|
||||
#include <dialogs/dialog_pns_length_tuning_settings.h>
|
||||
#include <dialogs/dialog_track_via_size.h>
|
||||
#include <base_units.h>
|
||||
|
||||
#include <tool/context_menu.h>
|
||||
#include <tools/common_actions.h>
|
||||
|
||||
#include <ratsnest_data.h>
|
||||
|
||||
#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<PCB_EDIT_FRAME>();
|
||||
m_ctls = getViewControls();
|
||||
m_board = getModel<BOARD>();
|
||||
|
||||
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<PCB_EDIT_FRAME> ();
|
||||
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 <PNS_MEANDER_PLACER *> ( 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<int> 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<PCB_EDIT_FRAME>();
|
||||
BOARD* board = getModel<BOARD>();
|
||||
|
||||
// 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<ROUTER_TOOL_MENU> 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<PCB_EDIT_FRAME>();
|
||||
BOARD* board = getModel<BOARD>();
|
||||
|
||||
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<PCB_EDIT_FRAME>();
|
||||
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 );
|
||||
}
|
|
@ -57,6 +57,14 @@ void ROUTER_PREVIEW_ITEM::Update( const PNS_ITEM* aItem )
|
|||
{
|
||||
m_originLayer = aItem->Layers().Start();
|
||||
|
||||
|
||||
if (aItem->OfKind ( PNS_ITEM::LINE ))
|
||||
{
|
||||
const PNS_LINE *l=static_cast<const PNS_LINE *> (aItem);
|
||||
if(!l->SegmentCount())
|
||||
return;
|
||||
}
|
||||
|
||||
assert( m_originLayer >= 0 );
|
||||
|
||||
m_layer = m_originLayer;
|
||||
|
@ -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:
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <pcbcommon.h>
|
||||
#include <pcb_painter.h>
|
||||
#include <dialogs/dialog_pns_settings.h>
|
||||
#include <dialogs/dialog_pns_diff_pair_dimensions.h>
|
||||
#include <dialogs/dialog_track_via_size.h>
|
||||
#include <base_units.h>
|
||||
|
||||
|
@ -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" )
|
||||
PNS_TOOL_BASE( "pcbnew.InteractiveRouter" )
|
||||
{
|
||||
m_router = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 );
|
||||
|
@ -228,39 +230,32 @@ public:
|
|||
|
||||
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;
|
||||
PNS_TOOL_BASE::Reset( aReason );
|
||||
|
||||
m_router = new PNS_ROUTER;
|
||||
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() );
|
||||
|
||||
TRACEn( 0, "Reset" );
|
||||
m_router->ClearWorld();
|
||||
m_router->SetBoard( getModel<BOARD>() );
|
||||
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() );
|
||||
}
|
||||
|
||||
|
||||
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>();
|
||||
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<PCB_EDIT_FRAME> ();
|
||||
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<PCB_EDIT_FRAME> ();
|
||||
BOARD* board = getModel<BOARD> ();
|
||||
|
||||
#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<PCB_EDIT_FRAME>();
|
||||
|
||||
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> ();
|
||||
BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
|
||||
PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
|
||||
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<PCB_EDIT_FRAME>();
|
||||
bool saveUndoBuffer = true;
|
||||
VIEW_CONTROLS* ctls = getViewControls();
|
||||
BOARD* board = getModel<BOARD>();
|
||||
|
||||
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 )
|
||||
int ROUTER_TOOL::DpDimensionsDialog( const TOOL_EVENT& aEvent )
|
||||
{
|
||||
Activate();
|
||||
|
||||
PNS_SIZES_SETTINGS sizes = m_router->Sizes();
|
||||
DIALOG_PNS_DIFF_PAIR_DIMENSIONS settingsDlg( m_frame, sizes );
|
||||
|
||||
if( settingsDlg.ShowModal() )
|
||||
{
|
||||
// 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;
|
||||
m_router->UpdateSizes( sizes );
|
||||
m_savedSizes = sizes;
|
||||
}
|
||||
|
||||
ctls->SetAutoPan( false );
|
||||
ctls->ForceCursorPosition( false );
|
||||
highlightNet( false );
|
||||
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<PCB_EDIT_FRAME>();
|
||||
BOARD* board = getModel<BOARD>();
|
||||
|
||||
// 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 );
|
||||
|
||||
std::auto_ptr<ROUTER_TOOL_MENU> ctxMenu ( new ROUTER_TOOL_MENU( board ) );
|
||||
m_ctls->SetSnapping( true );
|
||||
m_ctls->ShowCursor( true );
|
||||
|
||||
std::auto_ptr<ROUTER_TOOL_MENU> 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<PCB_EDIT_FRAME>();
|
||||
|
@ -845,3 +722,9 @@ void ROUTER_TOOL::performDragging()
|
|||
ctls->ForceCursorPosition( false );
|
||||
highlightNet( false );
|
||||
}
|
||||
|
||||
int ROUTER_TOOL::InlineDrag ( const TOOL_EVENT& aEvent )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,61 +22,41 @@
|
|||
#ifndef __ROUTER_TOOL_H
|
||||
#define __ROUTER_TOOL_H
|
||||
|
||||
#include <import_export.h>
|
||||
#include "pns_tool_base.h"
|
||||
|
||||
#include <math/vector2d.h>
|
||||
#include <tool/tool_interactive.h>
|
||||
|
||||
#include <msgpanel.h>
|
||||
|
||||
#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
|
||||
|
|
|
@ -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<TOOL_EVENT> 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();
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue