router: differential pairs & length tuning support

This commit is contained in:
Tomasz Włostowski 2015-02-18 01:29:54 +01:00
parent e5deafb4bb
commit 112adccbcb
77 changed files with 15413 additions and 942 deletions

View File

@ -104,6 +104,10 @@ set( PCBNEW_DIALOGS
dialogs/dialog_pcb_text_properties_base.cpp dialogs/dialog_pcb_text_properties_base.cpp
dialogs/dialog_pns_settings.cpp dialogs/dialog_pns_settings.cpp
dialogs/dialog_pns_settings_base.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.cpp
dialogs/dialog_non_copper_zones_properties_base.cpp dialogs/dialog_non_copper_zones_properties_base.cpp
dialogs/dialog_pad_properties.cpp dialogs/dialog_pad_properties.cpp
@ -275,6 +279,7 @@ set( PCBNEW_CLASS_SRCS
tools/module_tools.cpp tools/module_tools.cpp
tools/placement_tool.cpp tools/placement_tool.cpp
tools/common_actions.cpp tools/common_actions.cpp
tools/grid_helper.cpp
tools/tools_common.cpp tools/tools_common.cpp
) )

View File

@ -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();
}

View File

@ -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__

View File

@ -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

View File

@ -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__

View File

@ -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 );
}

View File

@ -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__

View File

@ -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

View File

@ -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

View File

@ -31,14 +31,19 @@
DIALOG_TRACK_VIA_SIZE::DIALOG_TRACK_VIA_SIZE( wxWindow* aParent, BOARD_DESIGN_SETTINGS& aSettings ) : DIALOG_TRACK_VIA_SIZE::DIALOG_TRACK_VIA_SIZE( wxWindow* aParent, BOARD_DESIGN_SETTINGS& aSettings ) :
DIALOG_TRACK_VIA_SIZE_BASE( aParent ), 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 ) m_settings( aSettings )
{ {
// Load router settings to dialog fields // Load router settings to dialog fields
m_trackWidth->SetValue( To_User_Unit( m_trackWidth->GetUnits(), m_settings.GetCustomTrackWidth() ) ); m_trackWidth.SetValue( m_settings.GetCustomTrackWidth() );
m_viaDiameter->SetValue( To_User_Unit( m_viaDiameter->GetUnits(), m_settings.GetCustomViaSize() ) ); m_viaDiameter.SetValue( m_settings.GetCustomViaSize() );
m_viaDrill->SetValue( To_User_Unit( m_viaDrill->GetUnits(), m_settings.GetCustomViaDrill() ) ); 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 // Pressing ENTER when any of the text input fields is active applies changes
#if wxCHECK_VERSION( 3, 0, 0 ) #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() bool DIALOG_TRACK_VIA_SIZE::check()
{ {
// Wrong input // 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; return false;
// Via drill should be smaller than via diameter // 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 false;
return true; return true;
@ -76,15 +81,15 @@ void DIALOG_TRACK_VIA_SIZE::onOkClick( wxCommandEvent& aEvent )
if( check() ) if( check() )
{ {
// Store dialog values to the router settings // Store dialog values to the router settings
m_settings.SetCustomTrackWidth( From_User_Unit( m_trackWidth->GetUnits(), *m_trackWidth->GetValue() ) ); m_settings.SetCustomTrackWidth( m_trackWidth.GetValue() );
m_settings.SetCustomViaSize( From_User_Unit( m_viaDiameter->GetUnits(), *m_viaDiameter->GetValue() ) ); m_settings.SetCustomViaSize( m_viaDiameter.GetValue() );
m_settings.SetCustomViaDrill( From_User_Unit( m_viaDrill->GetUnits(), *m_viaDrill->GetValue() ) ); m_settings.SetCustomViaDrill( m_viaDrill.GetValue() );
EndModal( 1 ); EndModal( 1 );
} }
else else
{ {
DisplayError( GetParent(), _( "Settings are incorrect" ) ); DisplayError( GetParent(), _( "Settings are incorrect" ) );
m_trackWidth->SetFocus(); m_trackWidthText->SetFocus();
} }
} }

View File

@ -25,6 +25,8 @@
#ifndef __dialog_track_via_size__ #ifndef __dialog_track_via_size__
#define __dialog_track_via_size__ #define __dialog_track_via_size__
#include <wx_unit_binder.h>
#include "dialog_track_via_size_base.h" #include "dialog_track_via_size_base.h"
class BOARD_DESIGN_SETTINGS; 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 ); DIALOG_TRACK_VIA_SIZE( wxWindow* aParent, BOARD_DESIGN_SETTINGS& aSettings );
protected: protected:
WX_UNIT_BINDER m_trackWidth;
WX_UNIT_BINDER m_viaDiameter;
WX_UNIT_BINDER m_viaDrill;
// Routings settings that are modified by the dialog. // Routings settings that are modified by the dialog.
BOARD_DESIGN_SETTINGS& m_settings; BOARD_DESIGN_SETTINGS& m_settings;

View File

@ -16,14 +16,46 @@ DIALOG_TRACK_VIA_SIZE_BASE::DIALOG_TRACK_VIA_SIZE_BASE( wxWindow* parent, wxWind
wxBoxSizer* bSizes; wxBoxSizer* bSizes;
bSizes = new wxBoxSizer( wxVERTICAL ); bSizes = new wxBoxSizer( wxVERTICAL );
m_trackWidth = new WX_UNIT_TEXT( this, _("Track width:") ); wxFlexGridSizer* fgSizer1;
bSizes->Add( m_trackWidth, 0, wxALL|wxEXPAND, 5 ); fgSizer1 = new wxFlexGridSizer( 0, 3, 0, 0 );
fgSizer1->SetFlexibleDirection( wxBOTH );
fgSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_viaDiameter = new WX_UNIT_TEXT( this, _("Via diameter:") ); m_staticText3 = new wxStaticText( this, wxID_ANY, _("Track width:"), wxDefaultPosition, wxDefaultSize, 0 );
bSizes->Add( m_viaDiameter, 0, wxALL|wxEXPAND, 5 ); m_staticText3->Wrap( -1 );
fgSizer1->Add( m_staticText3, 0, wxALL|wxALIGN_CENTER_VERTICAL, 5 );
m_viaDrill = new WX_UNIT_TEXT( this, _("Via drill:") ); m_trackWidthText = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 );
bSizes->Add( m_viaDrill, 0, wxALL|wxEXPAND, 5 ); 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_stdButtons = new wxStdDialogButtonSizer();
m_stdButtonsOK = new wxButton( this, wxID_OK ); m_stdButtonsOK = new wxButton( this, wxID_OK );

View File

@ -95,9 +95,25 @@
<property name="permission">none</property> <property name="permission">none</property>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="1">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property> <property name="flag">wxEXPAND|wxALL</property>
<property name="proportion">1</property>
<object class="wxFlexGridSizer" expanded="1">
<property name="cols">3</property>
<property name="flexible_direction">wxBOTH</property>
<property name="growablecols"></property>
<property name="growablerows"></property>
<property name="hgap">0</property>
<property name="minimum_size"></property>
<property name="name">fgSizer1</property>
<property name="non_flexible_grow_mode">wxFLEX_GROWMODE_SPECIFIED</property>
<property name="permission">none</property>
<property name="rows">0</property>
<property name="vgap">0</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property>
<property name="proportion">0</property> <property name="proportion">0</property>
<object class="CustomControl" expanded="1"> <object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property> <property name="BottomDockable">1</property>
<property name="LeftDockable">1</property> <property name="LeftDockable">1</property>
<property name="RightDockable">1</property> <property name="RightDockable">1</property>
@ -111,12 +127,9 @@
<property name="caption"></property> <property name="caption"></property>
<property name="caption_visible">1</property> <property name="caption_visible">1</property>
<property name="center_pane">0</property> <property name="center_pane">0</property>
<property name="class">WX_UNIT_TEXT</property>
<property name="close_button">1</property> <property name="close_button">1</property>
<property name="construction">m_trackWidth = new WX_UNIT_TEXT( this, _(&quot;Track width:&quot;) );</property>
<property name="context_help"></property> <property name="context_help"></property>
<property name="context_menu">1</property> <property name="context_menu">1</property>
<property name="declaration">WX_UNIT_TEXT* m_trackWidth;</property>
<property name="default_pane">0</property> <property name="default_pane">0</property>
<property name="dock">Dock</property> <property name="dock">Dock</property>
<property name="dock_fixed">0</property> <property name="dock_fixed">0</property>
@ -128,7 +141,7 @@
<property name="gripper">0</property> <property name="gripper">0</property>
<property name="hidden">0</property> <property name="hidden">0</property>
<property name="id">wxID_ANY</property> <property name="id">wxID_ANY</property>
<property name="include">#include &lt;wxunittext.h&gt;</property> <property name="label">Track width:</property>
<property name="max_size"></property> <property name="max_size"></property>
<property name="maximize_button">0</property> <property name="maximize_button">0</property>
<property name="maximum_size"></property> <property name="maximum_size"></property>
@ -136,7 +149,7 @@
<property name="minimize_button">0</property> <property name="minimize_button">0</property>
<property name="minimum_size"></property> <property name="minimum_size"></property>
<property name="moveable">1</property> <property name="moveable">1</property>
<property name="name">m_trackWidth</property> <property name="name">m_staticText3</property>
<property name="pane_border">1</property> <property name="pane_border">1</property>
<property name="pane_position"></property> <property name="pane_position"></property>
<property name="pane_size"></property> <property name="pane_size"></property>
@ -144,15 +157,273 @@
<property name="pin_button">1</property> <property name="pin_button">1</property>
<property name="pos"></property> <property name="pos"></property>
<property name="resize">Resizable</property> <property name="resize">Resizable</property>
<property name="settings"></property>
<property name="show">1</property> <property name="show">1</property>
<property name="size"></property> <property name="size"></property>
<property name="style"></property>
<property name="subclass"></property> <property name="subclass"></property>
<property name="toolbar_pane">0</property> <property name="toolbar_pane">0</property>
<property name="tooltip"></property> <property name="tooltip"></property>
<property name="window_extra_style"></property> <property name="window_extra_style"></property>
<property name="window_name"></property> <property name="window_name"></property>
<property name="window_style"></property> <property name="window_style"></property>
<property name="wrap">-1</property>
<event name="OnChar"></event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
<event name="OnKeyUp"></event>
<event name="OnKillFocus"></event>
<event name="OnLeaveWindow"></event>
<event name="OnLeftDClick"></event>
<event name="OnLeftDown"></event>
<event name="OnLeftUp"></event>
<event name="OnMiddleDClick"></event>
<event name="OnMiddleDown"></event>
<event name="OnMiddleUp"></event>
<event name="OnMotion"></event>
<event name="OnMouseEvents"></event>
<event name="OnMouseWheel"></event>
<event name="OnPaint"></event>
<event name="OnRightDClick"></event>
<event name="OnRightDown"></event>
<event name="OnRightUp"></event>
<event name="OnSetFocus"></event>
<event name="OnSize"></event>
<event name="OnUpdateUI"></event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND</property>
<property name="proportion">1</property>
<object class="wxTextCtrl" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="maxlength"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_trackWidthText</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="value"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnChar"></event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
<event name="OnKeyUp"></event>
<event name="OnKillFocus"></event>
<event name="OnLeaveWindow"></event>
<event name="OnLeftDClick"></event>
<event name="OnLeftDown"></event>
<event name="OnLeftUp"></event>
<event name="OnMiddleDClick"></event>
<event name="OnMiddleDown"></event>
<event name="OnMiddleUp"></event>
<event name="OnMotion"></event>
<event name="OnMouseEvents"></event>
<event name="OnMouseWheel"></event>
<event name="OnPaint"></event>
<event name="OnRightDClick"></event>
<event name="OnRightDown"></event>
<event name="OnRightUp"></event>
<event name="OnSetFocus"></event>
<event name="OnSize"></event>
<event name="OnText"></event>
<event name="OnTextEnter"></event>
<event name="OnTextMaxLen"></event>
<event name="OnTextURL"></event>
<event name="OnUpdateUI"></event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">inch</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_trackWidthLabel</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
<event name="OnChar"></event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
<event name="OnKeyUp"></event>
<event name="OnKillFocus"></event>
<event name="OnLeaveWindow"></event>
<event name="OnLeftDClick"></event>
<event name="OnLeftDown"></event>
<event name="OnLeftUp"></event>
<event name="OnMiddleDClick"></event>
<event name="OnMiddleDown"></event>
<event name="OnMiddleUp"></event>
<event name="OnMotion"></event>
<event name="OnMouseEvents"></event>
<event name="OnMouseWheel"></event>
<event name="OnPaint"></event>
<event name="OnRightDClick"></event>
<event name="OnRightDown"></event>
<event name="OnRightUp"></event>
<event name="OnSetFocus"></event>
<event name="OnSize"></event>
<event name="OnUpdateUI"></event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Via diameter:</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_staticText5</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
<event name="OnChar"></event> <event name="OnChar"></event>
<event name="OnEnterWindow"></event> <event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event> <event name="OnEraseBackground"></event>
@ -181,8 +452,8 @@
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="1">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property> <property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">0</property> <property name="proportion">1</property>
<object class="CustomControl" expanded="1"> <object class="wxTextCtrl" expanded="1">
<property name="BottomDockable">1</property> <property name="BottomDockable">1</property>
<property name="LeftDockable">1</property> <property name="LeftDockable">1</property>
<property name="RightDockable">1</property> <property name="RightDockable">1</property>
@ -196,12 +467,9 @@
<property name="caption"></property> <property name="caption"></property>
<property name="caption_visible">1</property> <property name="caption_visible">1</property>
<property name="center_pane">0</property> <property name="center_pane">0</property>
<property name="class">WX_UNIT_TEXT</property>
<property name="close_button">1</property> <property name="close_button">1</property>
<property name="construction">m_viaDiameter = new WX_UNIT_TEXT( this, _(&quot;Via diameter:&quot;) );</property>
<property name="context_help"></property> <property name="context_help"></property>
<property name="context_menu">1</property> <property name="context_menu">1</property>
<property name="declaration">WX_UNIT_TEXT* m_viaDiameter;</property>
<property name="default_pane">0</property> <property name="default_pane">0</property>
<property name="dock">Dock</property> <property name="dock">Dock</property>
<property name="dock_fixed">0</property> <property name="dock_fixed">0</property>
@ -213,15 +481,15 @@
<property name="gripper">0</property> <property name="gripper">0</property>
<property name="hidden">0</property> <property name="hidden">0</property>
<property name="id">wxID_ANY</property> <property name="id">wxID_ANY</property>
<property name="include">#include &lt;wxunittext.h&gt;</property>
<property name="max_size"></property> <property name="max_size"></property>
<property name="maximize_button">0</property> <property name="maximize_button">0</property>
<property name="maximum_size"></property> <property name="maximum_size"></property>
<property name="maxlength"></property>
<property name="min_size"></property> <property name="min_size"></property>
<property name="minimize_button">0</property> <property name="minimize_button">0</property>
<property name="minimum_size"></property> <property name="minimum_size"></property>
<property name="moveable">1</property> <property name="moveable">1</property>
<property name="name">m_viaDiameter</property> <property name="name">m_viaDiameterText</property>
<property name="pane_border">1</property> <property name="pane_border">1</property>
<property name="pane_position"></property> <property name="pane_position"></property>
<property name="pane_size"></property> <property name="pane_size"></property>
@ -229,15 +497,190 @@
<property name="pin_button">1</property> <property name="pin_button">1</property>
<property name="pos"></property> <property name="pos"></property>
<property name="resize">Resizable</property> <property name="resize">Resizable</property>
<property name="settings"></property>
<property name="show">1</property> <property name="show">1</property>
<property name="size"></property> <property name="size"></property>
<property name="style"></property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="value"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnChar"></event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
<event name="OnKeyUp"></event>
<event name="OnKillFocus"></event>
<event name="OnLeaveWindow"></event>
<event name="OnLeftDClick"></event>
<event name="OnLeftDown"></event>
<event name="OnLeftUp"></event>
<event name="OnMiddleDClick"></event>
<event name="OnMiddleDown"></event>
<event name="OnMiddleUp"></event>
<event name="OnMotion"></event>
<event name="OnMouseEvents"></event>
<event name="OnMouseWheel"></event>
<event name="OnPaint"></event>
<event name="OnRightDClick"></event>
<event name="OnRightDown"></event>
<event name="OnRightUp"></event>
<event name="OnSetFocus"></event>
<event name="OnSize"></event>
<event name="OnText"></event>
<event name="OnTextEnter"></event>
<event name="OnTextMaxLen"></event>
<event name="OnTextURL"></event>
<event name="OnUpdateUI"></event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">u</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_viaDiameterLabel</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass"></property> <property name="subclass"></property>
<property name="toolbar_pane">0</property> <property name="toolbar_pane">0</property>
<property name="tooltip"></property> <property name="tooltip"></property>
<property name="window_extra_style"></property> <property name="window_extra_style"></property>
<property name="window_name"></property> <property name="window_name"></property>
<property name="window_style"></property> <property name="window_style"></property>
<property name="wrap">-1</property>
<event name="OnChar"></event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
<event name="OnKeyUp"></event>
<event name="OnKillFocus"></event>
<event name="OnLeaveWindow"></event>
<event name="OnLeftDClick"></event>
<event name="OnLeftDown"></event>
<event name="OnLeftUp"></event>
<event name="OnMiddleDClick"></event>
<event name="OnMiddleDown"></event>
<event name="OnMiddleUp"></event>
<event name="OnMotion"></event>
<event name="OnMouseEvents"></event>
<event name="OnMouseWheel"></event>
<event name="OnPaint"></event>
<event name="OnRightDClick"></event>
<event name="OnRightDown"></event>
<event name="OnRightUp"></event>
<event name="OnSetFocus"></event>
<event name="OnSize"></event>
<event name="OnUpdateUI"></event>
</object>
</object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Via drill:</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_staticText7</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
<event name="OnChar"></event> <event name="OnChar"></event>
<event name="OnEnterWindow"></event> <event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event> <event name="OnEraseBackground"></event>
@ -266,8 +709,8 @@
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="1">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxALL|wxEXPAND</property> <property name="flag">wxALL|wxEXPAND</property>
<property name="proportion">0</property> <property name="proportion">1</property>
<object class="CustomControl" expanded="1"> <object class="wxTextCtrl" expanded="1">
<property name="BottomDockable">1</property> <property name="BottomDockable">1</property>
<property name="LeftDockable">1</property> <property name="LeftDockable">1</property>
<property name="RightDockable">1</property> <property name="RightDockable">1</property>
@ -281,12 +724,9 @@
<property name="caption"></property> <property name="caption"></property>
<property name="caption_visible">1</property> <property name="caption_visible">1</property>
<property name="center_pane">0</property> <property name="center_pane">0</property>
<property name="class">WX_UNIT_TEXT</property>
<property name="close_button">1</property> <property name="close_button">1</property>
<property name="construction">m_viaDrill = new WX_UNIT_TEXT( this, _(&quot;Via drill:&quot;) );</property>
<property name="context_help"></property> <property name="context_help"></property>
<property name="context_menu">1</property> <property name="context_menu">1</property>
<property name="declaration">WX_UNIT_TEXT* m_viaDrill;</property>
<property name="default_pane">0</property> <property name="default_pane">0</property>
<property name="dock">Dock</property> <property name="dock">Dock</property>
<property name="dock_fixed">0</property> <property name="dock_fixed">0</property>
@ -298,15 +738,15 @@
<property name="gripper">0</property> <property name="gripper">0</property>
<property name="hidden">0</property> <property name="hidden">0</property>
<property name="id">wxID_ANY</property> <property name="id">wxID_ANY</property>
<property name="include">#include &lt;wxunittext.h&gt;</property>
<property name="max_size"></property> <property name="max_size"></property>
<property name="maximize_button">0</property> <property name="maximize_button">0</property>
<property name="maximum_size"></property> <property name="maximum_size"></property>
<property name="maxlength"></property>
<property name="min_size"></property> <property name="min_size"></property>
<property name="minimize_button">0</property> <property name="minimize_button">0</property>
<property name="minimum_size"></property> <property name="minimum_size"></property>
<property name="moveable">1</property> <property name="moveable">1</property>
<property name="name">m_viaDrill</property> <property name="name">m_viaDrillText</property>
<property name="pane_border">1</property> <property name="pane_border">1</property>
<property name="pane_position"></property> <property name="pane_position"></property>
<property name="pane_size"></property> <property name="pane_size"></property>
@ -314,12 +754,17 @@
<property name="pin_button">1</property> <property name="pin_button">1</property>
<property name="pos"></property> <property name="pos"></property>
<property name="resize">Resizable</property> <property name="resize">Resizable</property>
<property name="settings"></property>
<property name="show">1</property> <property name="show">1</property>
<property name="size"></property> <property name="size"></property>
<property name="style"></property>
<property name="subclass"></property> <property name="subclass"></property>
<property name="toolbar_pane">0</property> <property name="toolbar_pane">0</property>
<property name="tooltip"></property> <property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="value"></property>
<property name="window_extra_style"></property> <property name="window_extra_style"></property>
<property name="window_name"></property> <property name="window_name"></property>
<property name="window_style"></property> <property name="window_style"></property>
@ -345,9 +790,98 @@
<event name="OnRightUp"></event> <event name="OnRightUp"></event>
<event name="OnSetFocus"></event> <event name="OnSetFocus"></event>
<event name="OnSize"></event> <event name="OnSize"></event>
<event name="OnText"></event>
<event name="OnTextEnter"></event>
<event name="OnTextMaxLen"></event>
<event name="OnTextURL"></event>
<event name="OnUpdateUI"></event> <event name="OnUpdateUI"></event>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL|wxALIGN_CENTER_VERTICAL</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="1">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer"></property>
<property name="aui_name"></property>
<property name="aui_position"></property>
<property name="aui_row"></property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">u</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_viaDrillLabel</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass"></property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
<event name="OnChar"></event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnKeyDown"></event>
<event name="OnKeyUp"></event>
<event name="OnKillFocus"></event>
<event name="OnLeaveWindow"></event>
<event name="OnLeftDClick"></event>
<event name="OnLeftDown"></event>
<event name="OnLeftUp"></event>
<event name="OnMiddleDClick"></event>
<event name="OnMiddleDown"></event>
<event name="OnMiddleUp"></event>
<event name="OnMotion"></event>
<event name="OnMouseEvents"></event>
<event name="OnMouseWheel"></event>
<event name="OnPaint"></event>
<event name="OnRightDClick"></event>
<event name="OnRightDown"></event>
<event name="OnRightUp"></event>
<event name="OnSetFocus"></event>
<event name="OnSize"></event>
<event name="OnUpdateUI"></event>
</object>
</object>
</object>
</object>
<object class="sizeritem" expanded="1"> <object class="sizeritem" expanded="1">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxEXPAND|wxALL</property> <property name="flag">wxEXPAND|wxALL</property>

View File

@ -11,12 +11,13 @@
#include <wx/artprov.h> #include <wx/artprov.h>
#include <wx/xrc/xmlres.h> #include <wx/xrc/xmlres.h>
#include <wx/intl.h> #include <wx/intl.h>
#include <wxunittext.h> #include <wx/string.h>
#include <wx/stattext.h>
#include <wx/gdicmn.h> #include <wx/gdicmn.h>
#include <wx/font.h> #include <wx/font.h>
#include <wx/colour.h> #include <wx/colour.h>
#include <wx/settings.h> #include <wx/settings.h>
#include <wx/string.h> #include <wx/textctrl.h>
#include <wx/sizer.h> #include <wx/sizer.h>
#include <wx/button.h> #include <wx/button.h>
#include <wx/dialog.h> #include <wx/dialog.h>
@ -32,9 +33,15 @@ class DIALOG_TRACK_VIA_SIZE_BASE : public wxDialog
private: private:
protected: protected:
WX_UNIT_TEXT* m_trackWidth; wxStaticText* m_staticText3;
WX_UNIT_TEXT* m_viaDiameter; wxTextCtrl* m_trackWidthText;
WX_UNIT_TEXT* m_viaDrill; wxStaticText* m_trackWidthLabel;
wxStaticText* m_staticText5;
wxTextCtrl* m_viaDiameterText;
wxStaticText* m_viaDiameterLabel;
wxStaticText* m_staticText7;
wxTextCtrl* m_viaDrillText;
wxStaticText* m_viaDrillLabel;
wxStdDialogButtonSizer* m_stdButtons; wxStdDialogButtonSizer* m_stdButtons;
wxButton* m_stdButtonsOK; wxButton* m_stdButtonsOK;
wxButton* m_stdButtonsCancel; wxButton* m_stdButtonsCancel;

View File

@ -433,6 +433,49 @@ void PCB_EDIT_FRAME::ReCreateMenuBar()
_( "Set the origin point for the grid" ), _( "Set the origin point for the grid" ),
KiBitmap( grid_select_axis_xpm ) ); 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------------------------------------ //----- Preferences and configuration menu------------------------------------
wxMenu* configmenu = new wxMenu; wxMenu* configmenu = new wxMenu;
@ -467,6 +510,12 @@ void PCB_EDIT_FRAME::ReCreateMenuBar()
_( "Select how items (pads, tracks texts ... ) are displayed" ), _( "Select how items (pads, tracks texts ... ) are displayed" ),
KiBitmap( display_options_xpm ) ); KiBitmap( display_options_xpm ) );
AddMenuItem( configmenu, ID_MENU_INTERACTIVE_ROUTER_SETTINGS,
_( "Interactive Routing" ),
_( "Configure Interactive Routing." ),
KiBitmap( add_tracks_xpm ) ); // fixme: icon
//--- dimensions submenu ------------------------------------------------------ //--- dimensions submenu ------------------------------------------------------
wxMenu* dimensionsMenu = new wxMenu; wxMenu* dimensionsMenu = new wxMenu;
@ -488,6 +537,11 @@ void PCB_EDIT_FRAME::ReCreateMenuBar()
_( "Adjust the global clearance between pads and the solder resist mask" ), _( "Adjust the global clearance between pads and the solder resist mask" ),
KiBitmap( pads_mask_layers_xpm ) ); 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(); dimensionsMenu->AppendSeparator();
AddMenuItem( dimensionsMenu, ID_CONFIG_SAVE, AddMenuItem( dimensionsMenu, ID_CONFIG_SAVE,
_( "&Save" ), _( "Save dimension preferences" ), _( "&Save" ), _( "Save dimension preferences" ),
@ -592,6 +646,7 @@ void PCB_EDIT_FRAME::ReCreateMenuBar()
menuBar->Append( editMenu, _( "&Edit" ) ); menuBar->Append( editMenu, _( "&Edit" ) );
menuBar->Append( viewMenu, _( "&View" ) ); menuBar->Append( viewMenu, _( "&View" ) );
menuBar->Append( placeMenu, _( "&Place" ) ); menuBar->Append( placeMenu, _( "&Place" ) );
menuBar->Append( routeMenu, _( "&Route" ) );
menuBar->Append( configmenu, _( "P&references" ) ); menuBar->Append( configmenu, _( "P&references" ) );
menuBar->Append( dimensionsMenu, _( "D&imensions" ) ); menuBar->Append( dimensionsMenu, _( "D&imensions" ) );
menuBar->Append( toolsMenu, _( "&Tools" ) ); menuBar->Append( toolsMenu, _( "&Tools" ) );

View File

@ -13,12 +13,19 @@ set( PCBNEW_PNS_SRCS
time_limit.cpp time_limit.cpp
pns_algo_base.cpp pns_algo_base.cpp
pns_diff_pair.cpp
pns_diff_pair_placer.cpp
pns_dp_meander_placer.cpp
pns_dragger.cpp pns_dragger.cpp
pns_item.cpp pns_item.cpp
pns_itemset.cpp pns_itemset.cpp
pns_line.cpp pns_line.cpp
pns_line_placer.cpp pns_line_placer.cpp
pns_logger.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_node.cpp
pns_optimizer.cpp pns_optimizer.cpp
pns_router.cpp pns_router.cpp
@ -26,11 +33,15 @@ set( PCBNEW_PNS_SRCS
pns_shove.cpp pns_shove.cpp
pns_sizes_settings.cpp pns_sizes_settings.cpp
pns_solid.cpp pns_solid.cpp
pns_tool_base.cpp
pns_topology.cpp
pns_tune_status_popup.cpp
pns_utils.cpp pns_utils.cpp
pns_via.cpp pns_via.cpp
pns_walkaround.cpp pns_walkaround.cpp
router_preview_item.cpp router_preview_item.cpp
router_tool.cpp router_tool.cpp
length_tuner_tool.cpp
) )
add_library( pnsrouter STATIC ${PCBNEW_PNS_SRCS} ) add_library( pnsrouter STATIC ${PCBNEW_PNS_SRCS} )

View File

@ -73,7 +73,7 @@ public:
*/ */
DIRECTION_45( const VECTOR2I& aVec ) DIRECTION_45( const VECTOR2I& aVec )
{ {
construct( aVec ); construct_( aVec );
} }
/** /**
@ -82,7 +82,7 @@ public:
*/ */
DIRECTION_45( const SEG& aSeg ) 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: private:
/** /**
@ -316,7 +321,7 @@ private:
* Calculates the direction from a vector. If the vector's angle is not a multiple of 45 * 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. * degrees, the direction is rounded to the nearest octant.
* @param aVec our vector */ * @param aVec our vector */
void construct( const VECTOR2I& aVec ) void construct_( const VECTOR2I& aVec )
{ {
m_dir = UNDEFINED; m_dir = UNDEFINED;

View File

@ -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;
}

View File

@ -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

View File

@ -21,6 +21,8 @@
#ifndef __PNS_ALGO_BASE_H #ifndef __PNS_ALGO_BASE_H
#define __PNS_ALGO_BASE_H #define __PNS_ALGO_BASE_H
#include <wx/wx.h> // for wxString
#include "pns_routing_settings.h" #include "pns_routing_settings.h"
class PNS_ROUTER; class PNS_ROUTER;
@ -54,6 +56,7 @@ public:
///> Returns the logger object, allowing to dump geometry to a file. ///> Returns the logger object, allowing to dump geometry to a file.
virtual PNS_LOGGER* Logger(); virtual PNS_LOGGER* Logger();
private: private:
PNS_ROUTER* m_router; PNS_ROUTER* m_router;
}; };

View File

@ -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;
}

View File

@ -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

View File

@ -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 );
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -36,7 +36,8 @@ class PNS_NODE;
enum LineMarker { enum LineMarker {
MK_HEAD = ( 1 << 0 ), MK_HEAD = ( 1 << 0 ),
MK_VIOLATION = ( 1 << 3 ), MK_VIOLATION = ( 1 << 3 ),
MK_LOCKED = ( 1 << 4 ) MK_LOCKED = ( 1 << 4 ),
MK_DP_COUPLED = ( 1 << 5 )
}; };
/** /**
@ -58,6 +59,7 @@ public:
JOINT = 4, JOINT = 4,
SEGMENT = 8, SEGMENT = 8,
VIA = 16, VIA = 16,
DIFF_PAIR = 32,
ANY = 0xff ANY = 0xff
}; };

View File

@ -22,12 +22,22 @@
#include "pns_itemset.h" #include "pns_itemset.h"
PNS_ITEMSET::PNS_ITEMSET( PNS_ITEM* aInitialItem ) PNS_ITEMSET::PNS_ITEMSET( PNS_ITEM* aInitialItem ) :
m_owner ( false )
{ {
if( aInitialItem ) if( aInitialItem )
m_items.push_back( 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 ) PNS_ITEMSET& PNS_ITEMSET::FilterLayers( int aStart, int aEnd, bool aInvert )
{ {
ITEMS newItems; ITEMS newItems;
@ -64,6 +74,21 @@ PNS_ITEMSET& PNS_ITEMSET::FilterKinds( int aKindMask, bool aInvert )
return *this; 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 ) PNS_ITEMSET& PNS_ITEMSET::FilterNet( int aNet, bool aInvert )
{ {

View File

@ -21,7 +21,7 @@
#ifndef __PNS_ITEMSET_H #ifndef __PNS_ITEMSET_H
#define __PNS_ITEMSET_H #define __PNS_ITEMSET_H
#include <vector> #include <deque>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include "pns_item.h" #include "pns_item.h"
@ -36,15 +36,23 @@
class PNS_ITEMSET class PNS_ITEMSET
{ {
public: public:
typedef std::vector<PNS_ITEM*> ITEMS; typedef std::deque<PNS_ITEM*> ITEMS;
PNS_ITEMSET( PNS_ITEM* aInitialItem = NULL ); 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; m_items = aOther.m_items;
} }
~PNS_ITEMSET();
void MakeOwner ( )
{
m_owner = true;
}
const PNS_ITEMSET& operator=( const PNS_ITEMSET& aOther ) const PNS_ITEMSET& operator=( const PNS_ITEMSET& aOther )
{ {
m_items = aOther.m_items; m_items = aOther.m_items;
@ -68,6 +76,7 @@ public:
PNS_ITEMSET& FilterLayers( int aStart, int aEnd = -1, bool aInvert = false ); PNS_ITEMSET& FilterLayers( int aStart, int aEnd = -1, bool aInvert = false );
PNS_ITEMSET& FilterKinds( int aKindMask, bool aInvert = false ); PNS_ITEMSET& FilterKinds( int aKindMask, bool aInvert = false );
PNS_ITEMSET& FilterNet( int aNet, 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 ) PNS_ITEMSET& ExcludeLayers( int aStart, int aEnd = -1 )
{ {
@ -96,6 +105,11 @@ public:
m_items.push_back( aItem ); m_items.push_back( aItem );
} }
void Prepend ( PNS_ITEM *aItem )
{
m_items.push_front ( aItem );
}
PNS_ITEM* Get( int index ) const PNS_ITEM* Get( int index ) const
{ {
return m_items[index]; return m_items[index];
@ -126,6 +140,7 @@ public:
private: private:
ITEMS m_items; ITEMS m_items;
bool m_owner;
}; };
#endif #endif

View File

@ -41,7 +41,7 @@
class PNS_JOINT : public PNS_ITEM class PNS_JOINT : public PNS_ITEM
{ {
public: public:
typedef std::vector<PNS_ITEM*> LINKED_ITEMS; typedef std::deque<PNS_ITEM*> LINKED_ITEMS;
///> Joints are hashed by their position, layers and net. ///> Joints are hashed by their position, layers and net.
/// Linked items are, obviously, not hashed /// Linked items are, obviously, not hashed
@ -95,6 +95,22 @@ public:
return seg1->Width() == seg2->Width(); 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) ///> Links the joint to a given board item (when it's added to the PNS_NODE)
void Link( PNS_ITEM* aItem ) void Link( PNS_ITEM* aItem )
{ {

View File

@ -701,6 +701,9 @@ void PNS_LINE::Reverse()
void PNS_LINE::AppendVia( const PNS_VIA& aVia ) void PNS_LINE::AppendVia( const PNS_VIA& aVia )
{ {
if(m_line.PointCount() == 0)
return;
if( aVia.Pos() == m_line.CPoint( 0 ) ) if( aVia.Pos() == m_line.CPoint( 0 ) )
{ {
Reverse(); Reverse();
@ -781,3 +784,91 @@ void PNS_LINE::ClearSegmentLinks()
m_segmentRefs = NULL; 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 ( );
}

View File

@ -89,6 +89,11 @@ public:
~PNS_LINE(); ~PNS_LINE();
static inline bool ClassOf( const PNS_ITEM* aItem )
{
return aItem && LINE == aItem->Kind();
}
/// @copydoc PNS_ITEM::Clone() /// @copydoc PNS_ITEM::Clone()
virtual PNS_LINE* Clone() const; virtual PNS_LINE* Clone() const;
@ -179,6 +184,11 @@ public:
return m_segmentRefs; return m_segmentRefs;
} }
bool IsLinked () const
{
return m_segmentRefs != NULL;
}
///> Checks if the segment aSeg is a part of the line. ///> Checks if the segment aSeg is a part of the line.
bool ContainsSegment( PNS_SEGMENT* aSeg ) const bool ContainsSegment( PNS_SEGMENT* aSeg ) const
{ {
@ -189,6 +199,11 @@ public:
aSeg ) != m_segmentRefs->end(); 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. ///> Erases the linking information. Used to detach the line from the owning node.
void ClearSegmentLinks(); void ClearSegmentLinks();
@ -251,6 +266,8 @@ public:
bool HasLoops() const; bool HasLoops() const;
OPT_BOX2I ChangedArea ( const PNS_LINE *aOther ) const;
private: private:
VECTOR2I snapToNeighbourSegments( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I &aP, VECTOR2I snapToNeighbourSegments( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I &aP,
int aIndex, int aThreshold) const; int aIndex, int aThreshold) const;

View File

@ -31,13 +31,14 @@
#include "pns_shove.h" #include "pns_shove.h"
#include "pns_utils.h" #include "pns_utils.h"
#include "pns_router.h" #include "pns_router.h"
#include "pns_topology.h"
#include <class_board_item.h> #include <class_board_item.h>
using boost::optional; using boost::optional;
PNS_LINE_PLACER::PNS_LINE_PLACER( PNS_ROUTER* aRouter ) : PNS_LINE_PLACER::PNS_LINE_PLACER( PNS_ROUTER* aRouter ) :
PNS_ALGO_BASE ( aRouter ) PNS_PLACEMENT_ALGO ( aRouter )
{ {
m_initial_direction = DIRECTION_45::N; m_initial_direction = DIRECTION_45::N;
m_world = NULL; 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; m_placingVia = aEnabled;
if(!m_idle) if(!m_idle)
Move ( m_currentEnd, NULL ); Move ( m_currentEnd, NULL );
return true;
} }
@ -225,8 +228,6 @@ bool PNS_LINE_PLACER::reduceTail( const VECTOR2I& aEnd )
VECTOR2I new_start; VECTOR2I new_start;
int reduce_index = -1; int reduce_index = -1;
DIRECTION_45 head_dir( head.CSegment( 0 ) );
for( int i = tail.SegmentCount() - 1; i >= 0; i-- ) for( int i = tail.SegmentCount() - 1; i >= 0; i-- )
{ {
const SEG s = tail.CSegment( 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 ) 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; PNS_LINE initTrack( m_head, line ), walkFull;
int effort = 0; int effort = 0;
bool viaOk = handleViaPlacement( initTrack ); 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 ) 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 ) 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 ) 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 initTrack( m_head, line );
PNS_LINE walkSolids, l2; PNS_LINE walkSolids, l2;
@ -744,7 +745,7 @@ bool PNS_LINE_PLACER::SetLayer( int aLayer )
return false; 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 ); VECTOR2I p( aP );
@ -772,6 +773,7 @@ void PNS_LINE_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem )
setInitialDirection( Settings().InitialDirection() ); setInitialDirection( Settings().InitialDirection() );
initPlacement( m_splitSeg ); initPlacement( m_splitSeg );
return true;
} }
void PNS_LINE_PLACER::initPlacement( bool aSplitSeg ) 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; PNS_LINE current;
VECTOR2I p = aP; VECTOR2I p = aP;
@ -856,6 +858,7 @@ void PNS_LINE_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem )
} }
updateLeadingRatLine(); updateLeadingRatLine();
return true;
} }
@ -962,11 +965,6 @@ void PNS_LINE_PLACER::removeLoops( PNS_NODE* aNode, PNS_LINE* aLatest )
if( !( line->ContainsSegment( seg ) ) && line->SegmentCount() ) 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 ); aNode->Remove( line );
removedCount ++; removedCount ++;
} }
@ -1012,27 +1010,39 @@ void PNS_LINE_PLACER::UpdateSizes( const PNS_SIZES_SETTINGS& aSizes )
void PNS_LINE_PLACER::updateLeadingRatLine() void PNS_LINE_PLACER::updateLeadingRatLine()
{ {
PNS_LINE current = Trace(); PNS_LINE current = Trace();
SHAPE_LINE_CHAIN ratLine;
PNS_TOPOLOGY topo ( m_lastNode );
if( !current.PointCount() ) if( topo.LeadingRatLine ( &current, ratLine ))
return; Router()->DisplayDebugLine( ratLine, 5, 10000 );
}
std::auto_ptr<PNS_NODE> tmpNode ( m_lastNode->Branch() );
tmpNode->Add( &current ); void PNS_LINE_PLACER::SetOrthoMode ( bool aOrthoMode )
{
PNS_JOINT* jt = tmpNode->FindJoint( current.CPoint( -1 ), m_orthoMode = aOrthoMode;
current.Layers().Start(), current.Net() ); if(!m_idle)
Move ( m_currentEnd, NULL );
if( !jt ) }
return;
const SHAPE_LINE_CHAIN PNS_LINE_PLACER::buildInitialLine ( const VECTOR2I& aP )
int anchor; {
PNS_ITEM* it = tmpNode->NearestUnconnectedItem( jt, &anchor ); SHAPE_LINE_CHAIN l (m_direction.BuildInitialTrace( m_p_start, aP ) );
if( it ) if( l.SegmentCount() <= 1 )
{ return l;
SHAPE_LINE_CHAIN lc;
lc.Append ( current.CPoint( -1 ) ); if (m_orthoMode)
lc.Append ( it->Anchor( anchor ) ); {
Router()->DisplayDebugLine( lc, 5, 10000 ); 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 );
} }

View File

@ -30,7 +30,7 @@
#include "pns_node.h" #include "pns_node.h"
#include "pns_via.h" #include "pns_via.h"
#include "pns_line.h" #include "pns_line.h"
#include "pns_algo_base.h" #include "pns_placement_algo.h"
class PNS_ROUTER; class PNS_ROUTER;
class PNS_SHOVE; class PNS_SHOVE;
@ -40,6 +40,7 @@ class PNS_VIA;
class PNS_SIZES_SETTINGS; class PNS_SIZES_SETTINGS;
/** /**
* Class PNS_LINE_PLACER * Class PNS_LINE_PLACER
* *
@ -47,7 +48,7 @@ class PNS_SIZES_SETTINGS;
* Applies shove and walkaround algorithms when needed. * Applies shove and walkaround algorithms when needed.
*/ */
class PNS_LINE_PLACER : public PNS_ALGO_BASE class PNS_LINE_PLACER : public PNS_PLACEMENT_ALGO
{ {
public: public:
PNS_LINE_PLACER( PNS_ROUTER* aRouter ); PNS_LINE_PLACER( PNS_ROUTER* aRouter );
@ -59,7 +60,7 @@ public:
* Starts routing a single track at point aP, taking item aStartItem as anchor * Starts routing a single track at point aP, taking item aStartItem as anchor
* (unless NULL). * (unless NULL).
*/ */
void Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ); bool Start( const VECTOR2I& aP, PNS_ITEM* aStartItem );
/** /**
* Function Move() * Function Move()
@ -68,7 +69,7 @@ public:
* aEndItem as anchor (if not NULL). * aEndItem as anchor (if not NULL).
* (unless NULL). * (unless NULL).
*/ */
void Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ); bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem );
/** /**
* Function FixRoute() * Function FixRoute()
@ -86,7 +87,7 @@ public:
* *
* Enables/disables a via at the end of currently routed trace. * Enables/disables a via at the end of currently routed trace.
*/ */
void ToggleVia( bool aEnabled ); bool ToggleVia( bool aEnabled );
/** /**
* Function SetLayer() * Function SetLayer()
@ -180,7 +181,11 @@ public:
*/ */
void UpdateSizes( const PNS_SIZES_SETTINGS& aSizes ); void UpdateSizes( const PNS_SIZES_SETTINGS& aSizes );
void SetOrthoMode ( bool aOrthoMode );
bool IsPlacingVia() const { return m_placingVia; } bool IsPlacingVia() const { return m_placingVia; }
void GetModifiedNets( std::vector<int> &aNets ) const;
private: private:
/** /**
* Function route() * Function route()
@ -343,6 +348,7 @@ private:
bool rhMarkObstacles( const VECTOR2I& aP, PNS_LINE& aNewHead ); bool rhMarkObstacles( const VECTOR2I& aP, PNS_LINE& aNewHead );
const PNS_VIA makeVia ( const VECTOR2I& aP ); const PNS_VIA makeVia ( const VECTOR2I& aP );
const SHAPE_LINE_CHAIN buildInitialLine ( const VECTOR2I& aP );
///> current routing direction ///> current routing direction
DIRECTION_45 m_direction; DIRECTION_45 m_direction;
@ -403,6 +409,7 @@ private:
bool m_idle; bool m_idle;
bool m_chainedPlacement; bool m_chainedPlacement;
bool m_splitSeg; bool m_splitSeg;
bool m_orthoMode;
}; };
#endif // __PNS_LINE_PLACER_H #endif // __PNS_LINE_PLACER_H

View File

@ -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 ) );
}
}

498
pcbnew/router/pns_meander.h Normal file
View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -53,6 +53,7 @@ PNS_NODE::PNS_NODE()
m_parent = NULL; m_parent = NULL;
m_maxClearance = 800000; // fixme: depends on how thick traces are. m_maxClearance = 800000; // fixme: depends on how thick traces are.
m_index = new PNS_INDEX; m_index = new PNS_INDEX;
m_collisionFilter = NULL;
#ifdef DEBUG #ifdef DEBUG
allocNodes.insert( this ); allocNodes.insert( this );
@ -109,6 +110,7 @@ PNS_NODE* PNS_NODE::Branch()
child->m_parent = this; child->m_parent = this;
child->m_clearanceFunctor = m_clearanceFunctor; child->m_clearanceFunctor = m_clearanceFunctor;
child->m_root = isRoot() ? this : m_root; child->m_root = isRoot() ? this : m_root;
child->m_collisionFilter = m_collisionFilter;
// immmediate offspring of the root branch needs not copy anything. // immmediate offspring of the root branch needs not copy anything.
// For the rest, deep-copy joints, overridden item map and pointers // 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 ); 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 ) if( aItem->Kind() == PNS_ITEM::LINE )
clearance += static_cast<PNS_LINE *>(aItem)->Width() / 2; clearance += static_cast<PNS_LINE *>(aItem)->Width() / 2;
@ -220,6 +225,7 @@ struct PNS_NODE::OBSTACLE_VISITOR
PNS_OBSTACLE obs; PNS_OBSTACLE obs;
obs.m_item = aItem; obs.m_item = aItem;
obs.m_head = m_item;
m_tab.push_back( obs ); m_tab.push_back( obs );
m_matchCount++; 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 ); assert( aItemB );
int clearance = GetClearance( aItemA, aItemB ); int clearance;
if( aForceClearance >= 0 )
clearance = aForceClearance;
else
clearance = GetClearance( aItemA, aItemB );
// fixme: refactor // fixme: refactor
if( aItemA->Kind() == PNS_ITEM::LINE ) 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, void PNS_NODE::followLine( PNS_SEGMENT* aCurrent, bool aScanDirection, int& aPos,
int aLimit, VECTOR2I* aCorners, PNS_SEGMENT** aSegments, bool& aGuardHit ) 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) PNS_LINE* PNS_NODE::AssembleLine( PNS_SEGMENT* aSeg, int* aOriginSegmentIndex)
{ {
const int MaxVerts = 1024; const int MaxVerts = 1024 * 16;
VECTOR2I corners[MaxVerts + 1]; VECTOR2I corners[MaxVerts + 1];
PNS_SEGMENT* segs[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 ) void PNS_NODE::MapConnectivity ( PNS_JOINT* aStart, std::vector<PNS_JOINT*>& aFoundJoints )
{ {
std::deque<PNS_JOINT*> searchQueue; 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) BOOST_FOREACH(PNS_JOINT* jt, processed)
aFoundJoints.push_back( jt ); 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 ) int PNS_NODE::FindLinesBetweenJoints( PNS_JOINT& aA, PNS_JOINT& aB, std::vector<PNS_LINE*>& aLines )
@ -1192,12 +1166,12 @@ 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 ) for( PNS_INDEX::ITEM_SET::iterator i = m_index->begin(); i != m_index->end(); ++i )
{ {
(*i)->SetRank( -1 ); (*i)->SetRank( -1 );
(*i)->Mark( 0 ); (*i)->Mark( (*i)->Marker() & (~aMarkerMask) );
} }
} }
@ -1262,3 +1236,8 @@ PNS_SEGMENT* PNS_NODE::findRedundantSegment ( PNS_SEGMENT *aSeg )
return NULL; return NULL;
} }
void PNS_NODE::SetCollisionFilter ( PNS_COLLISION_FILTER *aFilter )
{
m_collisionFilter = aFilter;
}

View File

@ -52,8 +52,28 @@ class PNS_INDEX;
class PNS_CLEARANCE_FUNC class PNS_CLEARANCE_FUNC
{ {
public: public:
virtual int operator()( const PNS_ITEM* aA, const PNS_ITEM* aB ) = 0;
virtual ~PNS_CLEARANCE_FUNC() {} 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 struct PNS_OBSTACLE
{ {
///> Item we search collisions with ///> Item we search collisions with
PNS_ITEM* m_head; const PNS_ITEM* m_head;
///> Item found to be colliding with m_head ///> Item found to be colliding with m_head
PNS_ITEM* m_item; PNS_ITEM* m_item;
@ -81,6 +101,15 @@ struct PNS_OBSTACLE
int m_distFirst, m_distLast; 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 * Class PNS_NODE
* *
@ -201,7 +230,8 @@ public:
*/ */
bool CheckColliding( const PNS_ITEM* aItemA, bool CheckColliding( const PNS_ITEM* aItemA,
const PNS_ITEM* aItemB, const PNS_ITEM* aItemB,
int aKindMask = PNS_ITEM::ANY ); int aKindMask = PNS_ITEM::ANY,
int aForceClearance = -1 );
/** /**
* Function HitTest() * Function HitTest()
@ -230,6 +260,15 @@ public:
*/ */
void Remove( PNS_ITEM* aItem ); 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() * Function Replace()
* *
@ -296,16 +335,18 @@ public:
* Searches for a joint at a given position, linked to given item. * Searches for a joint at a given position, linked to given item.
* @return the joint, if found, otherwise empty * @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() ); return FindJoint( aPos, aItem->Layers().Start(), aItem->Net() );
} }
#if 0
void MapConnectivity( PNS_JOINT* aStart, std::vector<PNS_JOINT*> & aFoundJoints ); void MapConnectivity( PNS_JOINT* aStart, std::vector<PNS_JOINT*> & aFoundJoints );
PNS_ITEM* NearestUnconnectedItem( PNS_JOINT* aStart, int *aAnchor = NULL, PNS_ITEM* NearestUnconnectedItem( PNS_JOINT* aStart, int *aAnchor = NULL,
int aKindMask = PNS_ITEM::ANY); int aKindMask = PNS_ITEM::ANY);
#endif
///> finds all lines between a pair of joints. Used by the loop removal procedure. ///> finds all lines between a pair of joints. Used by the loop removal procedure.
int FindLinesBetweenJoints( PNS_JOINT& aA, int FindLinesBetweenJoints( PNS_JOINT& aA,
@ -320,10 +361,11 @@ public:
void AllItemsInNet( int aNet, std::set<PNS_ITEM*>& aItems ); 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 FindByMarker( int aMarker, PNS_ITEMSET& aItems );
int RemoveByMarker( int aMarker ); int RemoveByMarker( int aMarker );
void SetCollisionFilter ( PNS_COLLISION_FILTER *aFilter );
private: private:
struct OBSTACLE_VISITOR; struct OBSTACLE_VISITOR;
@ -411,6 +453,9 @@ private:
///> depth of the node (number of parent nodes in the inheritance chain) ///> depth of the node (number of parent nodes in the inheritance chain)
int m_depth; int m_depth;
///> optional collision filtering object
PNS_COLLISION_FILTER *m_collisionFilter;
}; };
#endif #endif

View File

@ -24,6 +24,7 @@
#include <geometry/shape_rect.h> #include <geometry/shape_rect.h>
#include "pns_line.h" #include "pns_line.h"
#include "pns_diff_pair.h"
#include "pns_node.h" #include "pns_node.h"
#include "pns_optimizer.h" #include "pns_optimizer.h"
#include "pns_utils.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_world( aWorld ), m_collisionKindMask( PNS_ITEM::ANY ), m_effortLevel( MERGE_SEGMENTS )
{ {
// m_cache = new SHAPE_INDEX_LIST<PNS_ITEM*>(); // 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 ) 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 ) if( !aResult )
aResult = aLine; 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 ); int cost_orig = PNS_COST_ESTIMATOR::CornerCost( aCurrentPath );
LINE_RESTRICTIONS restr;
if( aLine->SegmentCount() < 4 ) if( aLine->SegmentCount() < 4 )
return false; return false;
DIRECTION_45 orig_start( aLine->CSegment( 0 ) ); DIRECTION_45 orig_start( aLine->CSegment( 0 ) );
DIRECTION_45 orig_end( aLine->CSegment( -1 ) ); DIRECTION_45 orig_end( aLine->CSegment( -1 ) );
restr.Build( m_world, aLine, aCurrentPath, m_restrictArea, m_restrictAreaActive );
while( n < n_segs - step ) while( n < n_segs - step )
{ {
const SEG s1 = aCurrentPath.CSegment( n ); 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 ); SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace( s1.A, s2.B, i );
cost[i] = INT_MAX; cost[i] = INT_MAX;
bool restrictionsOK = restr.Check ( n, n + step + 1, bypass );
if( n == 0 && orig_start != DIRECTION_45( bypass.CSegment( 0 ) ) ) if( n == 0 && orig_start != DIRECTION_45( bypass.CSegment( 0 ) ) )
postureMatch = false; postureMatch = false;
else if( n == n_segs - step && orig_end != DIRECTION_45( bypass.CSegment( -1 ) ) ) else if( n == n_segs - step && orig_end != DIRECTION_45( bypass.CSegment( -1 ) ) )
postureMatch = false; postureMatch = false;
if( (postureMatch || !m_keepPostures) && !checkColliding( aLine, bypass ) ) if( restrictionsOK && (postureMatch || !m_keepPostures) && !checkColliding( aLine, bypass ) )
{ {
path[i] = aCurrentPath; path[i] = aCurrentPath;
path[i].Replace( s1.Index(), s2.Index(), bypass ); 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++ ) for(int i = 0; i < 2; i++ )
{ {
SHAPE_LINE_CHAIN l2 = DIRECTION_45().BuildInitialTrace( p_start, p_end, i ); SHAPE_LINE_CHAIN l2 = DIRECTION_45().BuildInitialTrace( p_start, p_end, i );
PNS_ROUTER::GetInstance()->DisplayDebugLine( l2, 4, 10000 );
PNS_LINE repl; PNS_LINE repl;
repl = PNS_LINE( *aLine, l2 ); repl = PNS_LINE( *aLine, l2 );
@ -796,3 +950,210 @@ bool PNS_OPTIMIZER::fanoutCleanup( PNS_LINE* aLine )
return false; 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 );
}

View File

@ -30,8 +30,9 @@
#include "range.h" #include "range.h"
class PNS_NODE; class PNS_NODE;
class PNS_LINE;
class PNS_ROUTER; class PNS_ROUTER;
class PNS_LINE;
class PNS_DIFF_PAIR;
/** /**
* Class PNS_COST_ESTIMATOR * Class PNS_COST_ESTIMATOR
@ -101,6 +102,8 @@ public:
static bool Optimize( PNS_LINE* aLine, int aEffortLevel, PNS_NODE* aWorld); static bool Optimize( PNS_LINE* aLine, int aEffortLevel, PNS_NODE* aWorld);
bool Optimize( PNS_LINE* aLine, PNS_LINE* aResult = NULL ); bool Optimize( PNS_LINE* aLine, PNS_LINE* aResult = NULL );
bool Optimize( PNS_DIFF_PAIR* aPair );
void SetWorld( PNS_NODE* aNode ) { m_world = aNode; } void SetWorld( PNS_NODE* aNode ) { m_world = aNode; }
void CacheStaticItem( PNS_ITEM* aItem ); void CacheStaticItem( PNS_ITEM* aItem );
@ -117,6 +120,13 @@ public:
m_effortLevel = aEffort; m_effortLevel = aEffort;
} }
void SetRestrictArea( const BOX2I& aArea )
{
m_restrictArea = aArea;
m_restrictAreaActive = true;
}
private: private:
static const int MaxCachedItems = 256; static const int MaxCachedItems = 256;
@ -136,6 +146,8 @@ private:
bool runSmartPads( PNS_LINE* aLine ); bool runSmartPads( PNS_LINE* aLine );
bool mergeStep( PNS_LINE* aLine, SHAPE_LINE_CHAIN& aCurrentLine, int step ); bool mergeStep( PNS_LINE* aLine, SHAPE_LINE_CHAIN& aCurrentLine, int step );
bool fanoutCleanup( PNS_LINE * aLine ); 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_ITEM* aItem, bool aUpdateCache = true );
bool checkColliding( PNS_LINE* aLine, const SHAPE_LINE_CHAIN& aOptPath ); bool checkColliding( PNS_LINE* aLine, const SHAPE_LINE_CHAIN& aOptPath );
@ -160,6 +172,9 @@ private:
int m_collisionKindMask; int m_collisionKindMask;
int m_effortLevel; int m_effortLevel;
bool m_keepPostures; bool m_keepPostures;
BOX2I m_restrictArea;
bool m_restrictAreaActive;
}; };
#endif #endif

View File

@ -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

View File

@ -44,6 +44,12 @@
#include "pns_router.h" #include "pns_router.h"
#include "pns_shove.h" #include "pns_shove.h"
#include "pns_dragger.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> #include <router/router_preview_item.h>
@ -58,11 +64,9 @@
// To be fixed sometime in the future. // To be fixed sometime in the future.
static PNS_ROUTER* theRouter; 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() ); m_clearanceCache.resize( aBoard->GetNetCount() );
for( unsigned int i = 0; i < aBoard->GetNetCount(); i++ ) for( unsigned int i = 0; i < aBoard->GetNetCount(); i++ )
@ -79,26 +83,39 @@ public:
clearance ); clearance );
} }
m_overrideEnabled = false;
m_defaultClearance = 254000; // aBoard->m_NetClasses.Find ("Default clearance")->GetClearance(); m_defaultClearance = 254000; // aBoard->m_NetClasses.Find ("Default clearance")->GetClearance();
} }
int localPadClearance( const PNS_ITEM* aItem ) const 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 ) if( !aItem->Parent() || aItem->Parent()->Type() != PCB_PAD_T )
return 0; return 0;
const D_PAD* pad = static_cast<D_PAD*>( aItem->Parent() ); const D_PAD* pad = static_cast<D_PAD*>( aItem->Parent() );
return pad->GetLocalClearance(); return pad->GetLocalClearance();
} }
int operator()( const PNS_ITEM* aA, const PNS_ITEM* aB ) int PNS_PCBNEW_CLEARANCE_FUNC::operator()( const PNS_ITEM* aA, const PNS_ITEM* aB )
{ {
int net_a = aA->Net(); int net_a = aA->Net();
int cl_a = ( net_a >= 0 ? m_clearanceCache[net_a] : m_defaultClearance ); int cl_a = ( net_a >= 0 ? m_clearanceCache[net_a] : m_defaultClearance );
int net_b = aB->Net(); int net_b = aB->Net();
int cl_b = ( net_b >= 0 ? m_clearanceCache[net_b] : m_defaultClearance ); 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( 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 pad_a = localPadClearance( aA ); int pad_a = localPadClearance( aA );
int pad_b = localPadClearance( aB ); int pad_b = localPadClearance( aB );
@ -106,18 +123,26 @@ public:
cl_b = std::max( cl_b, pad_b ); cl_b = std::max( cl_b, pad_b );
return std::max( cl_a, cl_b ); return std::max( cl_a, cl_b );
}
private:
std::vector<int> m_clearanceCache;
int m_defaultClearance;
}; };
// 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_ITEM* PNS_ROUTER::syncPad( D_PAD* aPad )
{ {
PNS_LAYERSET layers( 0, MAX_CU_LAYERS - 1 ); PNS_LAYERSET layers( 0, MAX_CU_LAYERS - 1 );
// ignore non-copper pads
if ( (aPad->GetLayerSet() & LSET::AllCuMask()).none() )
return NULL;
switch( aPad->GetAttribute() ) switch( aPad->GetAttribute() )
{ {
case PAD_STANDARD: case PAD_STANDARD:
@ -222,6 +247,8 @@ PNS_ITEM* PNS_ROUTER::syncTrack( TRACK* aTrack )
PNS_SEGMENT* s = PNS_SEGMENT* s =
new PNS_SEGMENT( SEG( aTrack->GetStart(), aTrack->GetEnd() ), aTrack->GetNetCode() ); 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->SetWidth( aTrack->GetWidth() );
s->SetLayers( PNS_LAYERSET( aTrack->GetLayer() ) ); s->SetLayers( PNS_LAYERSET( aTrack->GetLayer() ) );
s->SetParent( aTrack ); s->SetParent( aTrack );
@ -265,7 +292,7 @@ void PNS_ROUTER::SyncWorld()
int worstClearance = m_board->GetDesignSettings().GetBiggestClearanceValue(); 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 = new PNS_NODE();
m_world->SetClearanceFunctor( m_clearanceFunc ); m_world->SetClearanceFunctor( m_clearanceFunc );
m_world->SetMaxClearance( 4 * worstClearance ); m_world->SetMaxClearance( 4 * worstClearance );
@ -309,6 +336,7 @@ PNS_ROUTER::PNS_ROUTER()
m_previewItems = NULL; m_previewItems = NULL;
m_board = NULL; m_board = NULL;
m_dragger = 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 ) bool PNS_ROUTER::StartRouting( const VECTOR2I& aP, PNS_ITEM* aStartItem, int aLayer )
{ {
switch (m_mode)
{
case PNS_MODE_ROUTE_SINGLE:
m_placer = new PNS_LINE_PLACER( this ); 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->UpdateSizes ( m_sizes );
m_placer->SetLayer( aLayer ); m_placer->SetLayer( aLayer );
m_placer->Start( aP, aStartItem );
bool rv = m_placer->Start( aP, aStartItem );
if(!rv)
return false;
m_currentEnd = aP; m_currentEnd = aP;
m_currentEndItem = NULL; m_currentEndItem = NULL;
m_state = ROUTE_TRACK; m_state = ROUTE_TRACK;
return rv;
}
return true; BOARD *PNS_ROUTER::GetBoard()
{
return m_board;
} }
void PNS_ROUTER::eraseView() void PNS_ROUTER::eraseView()
@ -652,15 +708,25 @@ void PNS_ROUTER::movePlacing( const VECTOR2I& aP, PNS_ITEM* aEndItem )
eraseView(); eraseView();
m_placer->Move( aP, aEndItem ); m_placer->Move( aP, aEndItem );
PNS_LINE current = m_placer->Trace(); PNS_ITEMSET current = m_placer->Traces();
DisplayItem( &current );
if( current.EndsWithVia() ) BOOST_FOREACH( const PNS_ITEM* item, current.CItems() )
DisplayItem( &current.Via() ); {
if( !item->OfKind ( PNS_ITEM::LINE ) )
continue;
PNS_ITEMSET tmp( &current ); const PNS_LINE *l = static_cast <const PNS_LINE *> (item);
updateView( m_placer->CurrentNode( true ), tmp ); DisplayItem(l);
if( l->EndsWithVia() )
DisplayItem( &l->Via() );
}
//PNS_ITEMSET tmp( &current );
updateView( m_placer->CurrentNode( true ), current );
} }
@ -765,11 +831,13 @@ bool PNS_ROUTER::FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem )
void PNS_ROUTER::StopRouting() void PNS_ROUTER::StopRouting()
{ {
// Update the ratsnest with new changes // Update the ratsnest with new changes
if( m_placer ) 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 // Update the ratsnest with new changes
m_board->GetRatsnest()->Recalculate( n ); m_board->GetRatsnest()->Recalculate( n );
@ -871,3 +939,17 @@ bool PNS_ROUTER::IsPlacingVia() const
return m_placer->IsPlacingVia(); 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;
}

View File

@ -41,6 +41,8 @@ class D_PAD;
class TRACK; class TRACK;
class VIA; class VIA;
class PNS_NODE; class PNS_NODE;
class PNS_DIFF_PAIR_PLACER;
class PNS_PLACEMENT_ALGO;
class PNS_LINE_PLACER; class PNS_LINE_PLACER;
class PNS_ITEM; class PNS_ITEM;
class PNS_LINE; 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 * Class PNS_ROUTER
* *
@ -78,6 +88,9 @@ public:
PNS_ROUTER(); PNS_ROUTER();
~PNS_ROUTER(); ~PNS_ROUTER();
void SetMode ( PNS_ROUTER_MODE aMode );
PNS_ROUTER_MODE Mode() const { return m_mode; }
static PNS_ROUTER* GetInstance(); static PNS_ROUTER* GetInstance();
void ClearWorld(); void ClearWorld();
@ -113,6 +126,7 @@ public:
void SwitchLayer( int layer ); void SwitchLayer( int layer );
void ToggleViaPlacement(); void ToggleViaPlacement();
void SetOrthoMode ( bool aEnable );
int GetCurrentLayer() const; int GetCurrentLayer() const;
int GetCurrentNet() const; int GetCurrentNet() const;
@ -193,6 +207,15 @@ public:
return m_sizes; 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: private:
void movePlacing( const VECTOR2I& aP, PNS_ITEM* aItem ); void movePlacing( const VECTOR2I& aP, PNS_ITEM* aItem );
void moveDragging( const VECTOR2I& aP, PNS_ITEM* aItem ); void moveDragging( const VECTOR2I& aP, PNS_ITEM* aItem );
@ -225,7 +248,7 @@ private:
BOARD* m_board; BOARD* m_board;
PNS_NODE* m_world; PNS_NODE* m_world;
PNS_NODE* m_lastNode; PNS_NODE* m_lastNode;
PNS_LINE_PLACER* m_placer; PNS_PLACEMENT_ALGO * m_placer;
PNS_DRAGGER* m_dragger; PNS_DRAGGER* m_dragger;
PNS_SHOVE* m_shove; PNS_SHOVE* m_shove;
int m_iterLimit; int m_iterLimit;
@ -250,6 +273,11 @@ private:
///> Stores list of modified items in the current operation ///> Stores list of modified items in the current operation
PICKED_ITEMS_LIST m_undoBuffer; PICKED_ITEMS_LIST m_undoBuffer;
PNS_SIZES_SETTINGS m_sizes; PNS_SIZES_SETTINGS m_sizes;
PNS_ROUTER_MODE m_mode;
wxString m_toolStatusbarName;
wxString m_failureReason;
}; };
#endif #endif

View File

@ -24,7 +24,7 @@
PNS_ROUTING_SETTINGS::PNS_ROUTING_SETTINGS() PNS_ROUTING_SETTINGS::PNS_ROUTING_SETTINGS()
{ {
m_routingMode = RM_Walkaround; m_routingMode = RM_Walkaround;
m_optimizerEffort = OE_FULL; m_optimizerEffort = OE_MEDIUM;
m_removeLoops = true; m_removeLoops = true;
m_smartPads = true; m_smartPads = true;
m_shoveVias = true; m_shoveVias = true;

View File

@ -55,6 +55,11 @@ public:
m_rank = aParentLine.Rank(); m_rank = aParentLine.Rank();
}; };
static inline bool ClassOf( const PNS_ITEM* aItem )
{
return aItem && SEGMENT == aItem->Kind();
}
PNS_SEGMENT* Clone( ) const; PNS_SEGMENT* Clone( ) const;
const SHAPE* Shape() const const SHAPE* Shape() const

View File

@ -36,11 +36,36 @@
#include "pns_utils.h" #include "pns_utils.h"
#include "pns_router.h" #include "pns_router.h"
#include "pns_shove.h" #include "pns_shove.h"
#include "pns_utils.h"
#include "time_limit.h" #include "time_limit.h"
#include <profile.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 ) static void sanityCheck( PNS_LINE* aOld, PNS_LINE* aNew )
{ {
assert( aOld->CPoint( 0 ) == aNew->CPoint( 0 ) ); 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_SHOVE::PNS_SHOVE( PNS_NODE* aWorld, PNS_ROUTER* aRouter ) :
PNS_ALGO_BASE ( aRouter ) PNS_ALGO_BASE ( aRouter )
{ {
m_forceClearance = -1;
m_root = aWorld; m_root = aWorld;
} }
@ -73,17 +99,6 @@ PNS_LINE* PNS_SHOVE::assembleLine( const PNS_SEGMENT* aSeg, int* aIndex )
return l; 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. // 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 // 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: // 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 ); 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 += aCurrent->Width() / 2;
dist += aShoved->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_SHOVE::SHOVE_STATUS PNS_SHOVE::walkaroundLoneVia( PNS_LINE* aCurrent, PNS_LINE* aObstacle,
PNS_LINE* aShoved ) 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() ); const SHAPE_LINE_CHAIN hull = aCurrent->Via().Hull( clearance, aObstacle->Width() );
SHAPE_LINE_CHAIN path_cw, path_ccw; SHAPE_LINE_CHAIN path_cw, path_ccw;
@ -204,7 +219,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::processHullSet( PNS_LINE* aCurrent, PNS_LINE*
continue; 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 ) 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 ) PNS_LINE* aShoved )
{ {
aShoved->ClearSegmentLinks(); aShoved->ClearSegmentLinks();
@ -262,7 +277,8 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::processSingleLine( PNS_LINE* aCurrent, PNS_LI
{ {
int w = aObstacle->Width(); int w = aObstacle->Width();
int n_segs = aCurrent->SegmentCount(); int n_segs = aCurrent->SegmentCount();
int clearance = m_currentNode->GetClearance( aCurrent, aObstacle );
int clearance = getClearance( aCurrent, aObstacle );
HULL_SET hulls; HULL_SET hulls;
@ -291,19 +307,24 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSegment( PNS_LINE* aCurrent, PNS_S
{ {
int segIndex; int segIndex;
PNS_LINE* obstacleLine = assembleLine( aObstacleSeg, &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 ) ); assert ( obstacleLine->LayersOverlap( shovedLine ) );
if( rv == SH_OK ) if( rv == SH_OK )
{ {
if( shovedLine->Marker() & MK_HEAD ) if( shovedLine->Marker() & MK_HEAD )
{
if( m_multiLineMode )
return SH_INCOMPLETE;
m_newHead = *shovedLine; m_newHead = *shovedLine;
}
sanityCheck( obstacleLine, shovedLine ); sanityCheck( obstacleLine, shovedLine );
m_currentNode->Replace( obstacleLine, shovedLine ); replaceItems ( obstacleLine, shovedLine );
sanityCheck( obstacleLine, shovedLine ); sanityCheck( obstacleLine, shovedLine );
int rank = aCurrent->Rank(); 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_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( rv == SH_OK )
{ {
if( shovedLine->Marker() & MK_HEAD ) if( shovedLine->Marker() & MK_HEAD )
{
if (m_multiLineMode)
return SH_INCOMPLETE;
m_newHead = *shovedLine; m_newHead = *shovedLine;
}
sanityCheck( aObstacle, shovedLine ); sanityCheck( aObstacle, shovedLine );
m_currentNode->Replace( aObstacle, shovedLine ); replaceItems( aObstacle, shovedLine );
sanityCheck( aObstacle, shovedLine ); sanityCheck( aObstacle, shovedLine );
int rank = aObstacle->Rank(); 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 ) 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_WALKAROUND walkaround( m_currentNode, Router() );
PNS_LINE* walkaroundLine = cloneLine( aCurrent ); PNS_LINE* walkaroundLine = clone( aCurrent );
if( aCurrent->EndsWithVia() ) if( aCurrent->EndsWithVia() )
{ {
@ -413,10 +443,14 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSolid( PNS_LINE* aCurrent, PNS_SOL
if( aCurrent->Marker() & MK_HEAD ) if( aCurrent->Marker() & MK_HEAD )
{ {
walkaroundLine->Mark( MK_HEAD ); walkaroundLine->Mark( MK_HEAD );
if(m_multiLineMode)
return SH_INCOMPLETE;
m_newHead = *walkaroundLine; m_newHead = *walkaroundLine;
} }
m_currentNode->Replace( aCurrent, walkaroundLine ); replaceItems ( aCurrent, walkaroundLine );
walkaroundLine->SetRank( nextRank ); walkaroundLine->SetRank( nextRank );
#ifdef DEBUG #ifdef DEBUG
@ -457,32 +491,59 @@ bool PNS_SHOVE::reduceSpringback( const PNS_ITEMSET& aHeadSet )
bool PNS_SHOVE::pushSpringback( PNS_NODE* aNode, const PNS_ITEMSET& aHeadItems, 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; SPRINGBACK_TAG st;
OPT_BOX2I prev_area;
if(!m_nodeStack.empty())
prev_area = m_nodeStack.back().m_affectedArea;
st.m_node = aNode; st.m_node = aNode;
st.m_cost = aCost; st.m_cost = aCost;
st.m_headItems = aHeadItems; 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 ); m_nodeStack.push_back( st );
return true; 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; LINE_PAIR_VEC draggedLines;
VECTOR2I p0 ( aVia->Pos() ); VECTOR2I p0 ( aVia->Pos() );
PNS_JOINT* jt = m_currentNode->FindJoint( p0, 1, aVia->Net() ); PNS_JOINT* jt = m_currentNode->FindJoint( p0, aVia );
PNS_VIA* pushedVia = aVia -> Clone(); 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() ); pushedVia->Mark( aVia->Marker() );
if( aVia->Marker() & MK_HEAD ) if( aVia->Marker() & MK_HEAD )
{ {
m_draggedVia = pushedVia; m_draggedVia = pushedVia;
m_draggedViaHeadSet.Clear();
} }
if( !jt ) 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() ) 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; LINE_PAIR lp;
int segIndex; int segIndex;
@ -506,16 +566,24 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForc
if( segIndex == 0 ) if( segIndex == 0 )
lp.first->Reverse(); lp.first->Reverse();
lp.second = cloneLine( lp.first ); lp.second = clone( lp.first );
lp.second->ClearSegmentLinks(); 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 ); lp.second->AppendVia ( *pushedVia );
draggedLines.push_back( lp ); draggedLines.push_back( lp );
if (aVia->Marker() & MK_HEAD )
m_draggedViaHeadSet.Add ( clone ( lp.second ) );
} }
} }
m_currentNode->Remove( aVia ); m_draggedViaHeadSet.Add ( pushedVia );
m_currentNode->Add ( pushedVia );
if ( aDryRun )
return SH_OK;
replaceItems ( aVia, pushedVia );
if( aVia->BelongsTo( m_currentNode ) ) if( aVia->BelongsTo( m_currentNode ) )
delete aVia; delete aVia;
@ -532,6 +600,10 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForc
if( lp.first->Marker() & MK_HEAD ) if( lp.first->Marker() & MK_HEAD )
{ {
lp.second->Mark( MK_HEAD ); lp.second->Mark( MK_HEAD );
if ( m_multiLineMode )
return SH_INCOMPLETE;
m_newHead = *lp.second; 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() ) if( lp.second->SegmentCount() )
{ {
m_currentNode->Replace( lp.first, lp.second ); replaceItems( lp.first, lp.second );
lp.second->SetRank( aCurrentRank - 1 ); lp.second->SetRank( aCurrentRank - 1 );
pushLine( lp.second ); 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 ) 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; LINE_PAIR_VEC draggedLines;
bool colLine = false, colVia = false; bool colLine = false, colVia = false;
PNS_LINE* currentLine = NULL; PNS_LINE* currentLine = NULL;
@ -609,11 +681,11 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onReverseCollidingVia( PNS_LINE* aCurrent, PN
{ {
std::vector<PNS_LINE*> steps; std::vector<PNS_LINE*> steps;
int n = 0; int n = 0;
PNS_LINE* cur = cloneLine( aCurrent ); PNS_LINE* cur = clone( aCurrent );
cur->ClearSegmentLinks(); cur->ClearSegmentLinks();
PNS_JOINT* jt = m_currentNode->FindJoint( aObstacleVia->Pos(), aObstacleVia ); PNS_JOINT* jt = m_currentNode->FindJoint( aObstacleVia->Pos(), aObstacleVia );
PNS_LINE* shoved = cloneLine( aCurrent ); PNS_LINE* shoved = clone( aCurrent );
shoved->ClearSegmentLinks(); shoved->ClearSegmentLinks();
cur->RemoveVia(); cur->RemoveVia();
@ -628,7 +700,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onReverseCollidingVia( PNS_LINE* aCurrent, PN
head->AppendVia( *aObstacleVia ); head->AppendVia( *aObstacleVia );
SHOVE_STATUS st = processSingleLine( head, cur, shoved ); SHOVE_STATUS st = ProcessSingleLine( head, cur, shoved );
if( st != SH_OK ) if( st != SH_OK )
{ {
@ -660,7 +732,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onReverseCollidingVia( PNS_LINE* aCurrent, PN
head.AppendVia( *aObstacleVia ); head.AppendVia( *aObstacleVia );
head.ClearSegmentLinks(); head.ClearSegmentLinks();
SHOVE_STATUS st = processSingleLine( &head, aCurrent, shoved ); SHOVE_STATUS st = ProcessSingleLine( &head, aCurrent, shoved );
if( st != SH_OK ) if( st != SH_OK )
return st; return st;
@ -678,7 +750,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onReverseCollidingVia( PNS_LINE* aCurrent, PN
m_logger.Log( shoved, 3, "shoved-line" ); m_logger.Log( shoved, 3, "shoved-line" );
#endif #endif
int currentRank = aCurrent->Rank(); int currentRank = aCurrent->Rank();
m_currentNode->Replace( aCurrent, shoved ); replaceItems( aCurrent, shoved );
pushLine( shoved ); pushLine( shoved );
shoved->SetRank( currentRank ); shoved->SetRank( currentRank );
@ -727,7 +799,15 @@ void PNS_SHOVE::unwindStack( PNS_ITEM* aItem )
void PNS_SHOVE::pushLine( PNS_LINE* aL ) void PNS_SHOVE::pushLine( PNS_LINE* aL )
{ {
if( aL->LinkCount() >= 0 && ( aL->LinkCount() != aL->SegmentCount() ) ) 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 ); assert( false );
}
m_lineStack.push_back( aL ); m_lineStack.push_back( aL );
m_optimizerQueue.push_back( aL ); m_optimizerQueue.push_back( aL );
@ -847,6 +927,8 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::shoveMainLoop()
{ {
SHOVE_STATUS st = SH_OK; SHOVE_STATUS st = SH_OK;
m_affectedAreaSum = OPT_BOX2I();
TRACE( 1, "ShoveStart [root: %d jts, current: %d jts]", m_root->JointCount() % TRACE( 1, "ShoveStart [root: %d jts, current: %d jts]", m_root->JointCount() %
m_currentNode->JointCount() ); m_currentNode->JointCount() );
@ -873,16 +955,34 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::shoveMainLoop()
return st; 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 ) PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead )
{ {
SHOVE_STATUS st = SH_OK; SHOVE_STATUS st = SH_OK;
m_multiLineMode = false;
// empty head? nothing to shove... // empty head? nothing to shove...
if( !aCurrentHead.SegmentCount() ) if( !aCurrentHead.SegmentCount() )
return SH_INCOMPLETE; return SH_INCOMPLETE;
PNS_LINE* head = cloneLine( &aCurrentHead ); PNS_LINE* head = clone( &aCurrentHead );
head->ClearSegmentLinks(); head->ClearSegmentLinks();
m_lineStack.clear(); m_lineStack.clear();
@ -890,7 +990,7 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead )
m_newHead = OPT_LINE(); m_newHead = OPT_LINE();
m_logger.Clear(); m_logger.Clear();
PNS_ITEMSET headSet( cloneLine( &aCurrentHead ) ); PNS_ITEMSET headSet( clone( &aCurrentHead ) );
reduceSpringback( headSet ); reduceSpringback( headSet );
@ -924,7 +1024,6 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead )
if( m_newHead && st == SH_OK ) if( m_newHead && st == SH_OK )
{ {
st = SH_HEAD_MODIFIED; st = SH_HEAD_MODIFIED;
//Router()->DisplayDebugLine( m_newHead->CLine(), 3, 20000 );
} }
m_currentNode->RemoveByMarker( MK_HEAD ); 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 ) 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 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_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveDraggingVia( PNS_VIA* aVia, const VECTOR2I& aWhere,
PNS_VIA** aNewVia ) PNS_VIA** aNewVia )
{ {
SHOVE_STATUS st = SH_OK; SHOVE_STATUS st = SH_OK;
m_lineStack.clear(); m_lineStack.clear();
m_optimizerQueue.clear(); m_optimizerQueue.clear();
m_newHead = OPT_LINE(); m_newHead = OPT_LINE();
m_draggedVia = NULL; m_draggedVia = NULL;
m_draggedViaHeadSet.Clear();
//reduceSpringback( aCurrentHead );
PNS_NODE* parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node; 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 = parent->Branch();
m_currentNode->ClearRanks(); 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 = pushVia( aVia, ( aWhere - aVia->Pos() ), 0 );
st = shoveMainLoop(); st = shoveMainLoop();
runOptimizer( m_currentNode, NULL ); runOptimizer( m_currentNode, NULL );
//m_currentNode->RemoveByMarker( MK_HEAD );
if( st == SH_OK || st == SH_HEAD_MODIFIED ) 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 else
{ {
@ -981,8 +1169,9 @@ PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveDraggingVia( PNS_VIA* aVia, const VECTOR
} }
if( aNewVia ) if( aNewVia )
{
*aNewVia = m_draggedVia; *aNewVia = m_draggedVia;
}
return st; 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 ) void PNS_SHOVE::runOptimizer( PNS_NODE* aNode, PNS_LINE* aHead )
{ {
PNS_OPTIMIZER optimizer( aNode ); PNS_OPTIMIZER optimizer( aNode );
int optFlags = 0, n_passes = 0, extend = 0; int optFlags = 0, n_passes = 0;
PNS_OPTIMIZATION_EFFORT effort = Settings().OptimizerEffort(); 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 ) switch( effort )
{ {
case OE_LOW: case OE_LOW:
optFlags = PNS_OPTIMIZER::MERGE_OBTUSE; optFlags = PNS_OPTIMIZER::MERGE_OBTUSE;
n_passes = 1; n_passes = 1;
extend = 0;
break; break;
case OE_MEDIUM: case OE_MEDIUM:
optFlags = PNS_OPTIMIZER::MERGE_OBTUSE; optFlags = PNS_OPTIMIZER::MERGE_SEGMENTS;
if( area )
optimizer.SetRestrictArea ( *area );
n_passes = 2; n_passes = 2;
extend = 1;
break; break;
case OE_FULL: case OE_FULL:
@ -1017,6 +1223,7 @@ void PNS_SHOVE::runOptimizer( PNS_NODE* aNode, PNS_LINE* aHead )
break; break;
} }
if( Settings().SmartPads() ) if( Settings().SmartPads() )
optFlags |= PNS_OPTIMIZER::SMART_PADS ; 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( !( 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; PNS_LINE optimized;
if( optimizer.Optimize( 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() PNS_NODE* PNS_SHOVE::CurrentNode()
{ {
return m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node; return m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node;

View File

@ -61,7 +61,19 @@ public:
} }
SHOVE_STATUS ShoveLines( const PNS_LINE& aCurrentHead ); 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 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(); PNS_NODE* CurrentNode();
@ -83,15 +95,15 @@ private:
PNS_NODE* m_node; PNS_NODE* m_node;
PNS_ITEMSET m_headItems; PNS_ITEMSET m_headItems;
PNS_COST_ESTIMATOR m_cost; 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, SHOVE_STATUS processHullSet( PNS_LINE* aCurrent, PNS_LINE* aObstacle,
PNS_LINE* aShoved, const HULL_SET& hulls ); PNS_LINE* aShoved, const HULL_SET& hulls );
bool reduceSpringback( const PNS_ITEMSET& aHeadItems ); bool reduceSpringback( const PNS_ITEMSET& aHeadItems );
bool pushSpringback( PNS_NODE* aNode, const PNS_ITEMSET &aHeadItems, bool pushSpringback( PNS_NODE* aNode, const PNS_ITEMSET& aHeadItems,
const PNS_COST_ESTIMATOR& aCost ); const PNS_COST_ESTIMATOR& aCost, const OPT_BOX2I& aAffectedArea );
SHOVE_STATUS walkaroundLoneVia( PNS_LINE* aCurrent, PNS_LINE* aObstacle, PNS_LINE* aShoved ); SHOVE_STATUS walkaroundLoneVia( PNS_LINE* aCurrent, PNS_LINE* aObstacle, PNS_LINE* aShoved );
bool checkBumpDirection( PNS_LINE* aCurrent, PNS_LINE* aShoved ) const; 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 onCollidingSolid( PNS_LINE* aCurrent, PNS_SOLID* aObstacleSolid );
SHOVE_STATUS onCollidingVia( PNS_ITEM* aCurrent, PNS_VIA* aObstacleVia ); SHOVE_STATUS onCollidingVia( PNS_ITEM* aCurrent, PNS_VIA* aObstacleVia );
SHOVE_STATUS onReverseCollidingVia( PNS_LINE* 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_SEGMENT* aSeg );
void unwindStack( PNS_ITEM* aItem ); void unwindStack( PNS_ITEM* aItem );
@ -111,14 +126,26 @@ private:
void pushLine( PNS_LINE* aL ); void pushLine( PNS_LINE* aL );
void popLine(); void popLine();
const RANGE<int> findShovedVertexRange( PNS_LINE* aL );
PNS_LINE* assembleLine( const PNS_SEGMENT* aSeg, int* aIndex = NULL ); 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 shoveIteration( int aIter );
SHOVE_STATUS shoveMainLoop(); SHOVE_STATUS shoveMainLoop();
int getClearance( PNS_ITEM *aA, PNS_ITEM *aB ) const;
std::vector<SPRINGBACK_TAG> m_nodeStack; std::vector<SPRINGBACK_TAG> m_nodeStack;
std::vector<PNS_LINE*> m_lineStack; std::vector<PNS_LINE*> m_lineStack;
std::vector<PNS_LINE*> m_optimizerQueue; std::vector<PNS_LINE*> m_optimizerQueue;
@ -131,8 +158,12 @@ private:
PNS_LOGGER m_logger; PNS_LOGGER m_logger;
PNS_VIA* m_draggedVia; PNS_VIA* m_draggedVia;
PNS_ITEMSET m_draggedViaHeadSet;
int m_iter; int m_iter;
int m_forceClearance;
bool m_multiLineMode;
bool m_headModified;
}; };
#endif // __PNS_SHOVE_H #endif // __PNS_SHOVE_H

View File

@ -34,11 +34,13 @@ class PNS_SIZES_SETTINGS {
public: public:
PNS_SIZES_SETTINGS() : PNS_SIZES_SETTINGS() :
m_trackWidth( 100000 ), m_trackWidth( 155000 ),
m_diffPairWidth( 100000 ), m_diffPairWidth( 125000 ),
m_diffPairGap( 125000 ), m_diffPairGap( 180000 ),
m_viaDiameter( 500000 ), m_diffPairViaGap ( 180000 ),
m_viaDrill( 200000 ), m_viaDiameter( 600000 ),
m_viaDrill( 250000 ),
m_diffPairViaGapSameAsTraceGap ( true ),
m_viaType( VIA_THROUGH ) m_viaType( VIA_THROUGH )
{}; {};
@ -54,9 +56,22 @@ public:
void SetTrackWidth( int aWidth ) { m_trackWidth = aWidth; } void SetTrackWidth( int aWidth ) { m_trackWidth = aWidth; }
int DiffPairWidth() const { return m_diffPairWidth; } int DiffPairWidth() const { return m_diffPairWidth; }
int DiffPairGap() const { return m_diffPairGap; } 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; } int ViaDiameter() const { return m_viaDiameter; }
void SetViaDiameter( int aDiameter) { m_viaDiameter = aDiameter; } void SetViaDiameter( int aDiameter) { m_viaDiameter = aDiameter; }
@ -84,9 +99,12 @@ private:
int m_trackWidth; int m_trackWidth;
int m_diffPairWidth; int m_diffPairWidth;
int m_diffPairGap; int m_diffPairGap;
int m_diffPairViaGap;
int m_viaDiameter; int m_viaDiameter;
int m_viaDrill; int m_viaDrill;
bool m_diffPairViaGapSameAsTraceGap;
VIATYPE_T m_viaType; VIATYPE_T m_viaType;
std::map<int, int> m_layerPairs; std::map<int, int> m_layerPairs;

View File

@ -49,6 +49,11 @@ public:
m_pos = aSolid.m_pos; m_pos = aSolid.m_pos;
} }
static inline bool ClassOf( const PNS_ITEM* aItem )
{
return aItem && SOLID == aItem->Kind();
}
PNS_ITEM* Clone() const; PNS_ITEM* Clone() const;
const SHAPE* Shape() const { return m_shape; } const SHAPE* Shape() const { return m_shape; }

View File

@ -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() );
}

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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();
}

View File

@ -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_*/

View File

@ -20,6 +20,7 @@
#include "pns_utils.h" #include "pns_utils.h"
#include "pns_line.h" #include "pns_line.h"
#include "pns_via.h"
#include "pns_router.h" #include "pns_router.h"
#include <geometry/shape_segment.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 ), 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 ) ); 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();
}

View File

@ -22,12 +22,15 @@
#define __PNS_UTILS_H #define __PNS_UTILS_H
#include <math/vector2d.h> #include <math/vector2d.h>
#include <math/box2.h>
#include <geometry/shape_line_chain.h> #include <geometry/shape_line_chain.h>
#include <geometry/shape_segment.h> #include <geometry/shape_segment.h>
#include <geometry/shape_rect.h> #include <geometry/shape_rect.h>
#define HULL_MARGIN 10 #define HULL_MARGIN 10
class PNS_ITEM;
/** Various utility functions */ /** Various utility functions */
const SHAPE_LINE_CHAIN OctagonalHull( const VECTOR2I& aP0, const VECTOR2I& aSize, 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 ); 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 #endif // __PNS_UTILS_H

View File

@ -95,3 +95,15 @@ PNS_VIA* PNS_VIA::Clone ( ) const
return v; 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();
}

View File

@ -73,6 +73,12 @@ public:
m_viaType = aB.m_viaType; m_viaType = aB.m_viaType;
} }
static inline bool ClassOf( const PNS_ITEM* aItem )
{
return aItem && VIA == aItem->Kind();
}
const VECTOR2I& Pos() const const VECTOR2I& Pos() const
{ {
return m_pos; return m_pos;
@ -140,6 +146,8 @@ public:
return 1; return 1;
} }
OPT_BOX2I ChangedArea ( const PNS_VIA *aOther ) const;
private: private:
int m_diameter; int m_diameter;
int m_drill; int m_drill;

View File

@ -136,7 +136,7 @@ PNS_WALKAROUND::WALKAROUND_STATUS PNS_WALKAROUND::Route( const PNS_LINE& aInitia
PNS_LINE& aWalkPath, bool aOptimize ) PNS_LINE& aWalkPath, bool aOptimize )
{ {
PNS_LINE path_cw( aInitialPath ), path_ccw( aInitialPath ); 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; SHAPE_LINE_CHAIN best_path;
start( aInitialPath ); start( aInitialPath );
@ -146,6 +146,17 @@ PNS_WALKAROUND::WALKAROUND_STATUS PNS_WALKAROUND::Route( const PNS_LINE& aInitia
aWalkPath = aInitialPath; 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 ) while( m_iteration < m_iterationLimit )
{ {
if( s_cw != STUCK ) if( s_cw != STUCK )
@ -229,17 +240,20 @@ PNS_WALKAROUND::WALKAROUND_STATUS PNS_WALKAROUND::Route( const PNS_LINE& aInitia
if( aWalkPath.SegmentCount() < 1 ) if( aWalkPath.SegmentCount() < 1 )
return STUCK; return STUCK;
if( aWalkPath.CPoint( -1 ) != aInitialPath.CPoint( -1 ) ) if( aWalkPath.CPoint( -1 ) != aInitialPath.CPoint( -1 ) )
return STUCK; return STUCK;
if( aWalkPath.CPoint( 0 ) != aInitialPath.CPoint( 0 ) ) if( aWalkPath.CPoint( 0 ) != aInitialPath.CPoint( 0 ) )
return STUCK; return STUCK;
WALKAROUND_STATUS st = s_ccw == DONE || s_cw == DONE ? DONE : STUCK; WALKAROUND_STATUS st = s_ccw == DONE || s_cw == DONE ? DONE : STUCK;
if( aOptimize && st == DONE ) if( st == DONE )
{
if( aOptimize )
PNS_OPTIMIZER::Optimize( &aWalkPath, PNS_OPTIMIZER::MERGE_OBTUSE, m_world ); PNS_OPTIMIZER::Optimize( &aWalkPath, PNS_OPTIMIZER::MERGE_OBTUSE, m_world );
}
return st; return st;
} }

View File

@ -39,6 +39,7 @@ public:
{ {
m_forceSingleDirection = false; m_forceSingleDirection = false;
m_forceLongerPath = false; m_forceLongerPath = false;
m_forceWinding = false;
m_cursorApproachMode = false; m_cursorApproachMode = false;
m_itemMask = PNS_ITEM::ANY; m_itemMask = PNS_ITEM::ANY;
} }
@ -79,7 +80,11 @@ public:
{ {
m_forceSingleDirection = aForceSingleDirection; m_forceSingleDirection = aForceSingleDirection;
m_forceLongerPath = 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 ) void SetApproachCursor( bool aEnabled, const VECTOR2I& aPos )
@ -88,6 +93,12 @@ public:
m_cursorApproachMode = aEnabled; 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, WALKAROUND_STATUS Route( const PNS_LINE& aInitialPath, PNS_LINE& aWalkPath,
bool aOptimize = true ); bool aOptimize = true );
@ -110,6 +121,8 @@ private:
int m_itemMask; int m_itemMask;
bool m_forceSingleDirection, m_forceLongerPath; bool m_forceSingleDirection, m_forceLongerPath;
bool m_cursorApproachMode; bool m_cursorApproachMode;
bool m_forceWinding;
bool m_forceCw;
VECTOR2I m_cursorPos; VECTOR2I m_cursorPos;
PNS_NODE::OPT_OBSTACLE m_currentObstacle[2]; PNS_NODE::OPT_OBSTACLE m_currentObstacle[2];
bool m_recursiveCollision[2]; bool m_recursiveCollision[2];

View File

@ -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

View File

@ -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 );
}

View File

@ -57,6 +57,14 @@ void ROUTER_PREVIEW_ITEM::Update( const PNS_ITEM* aItem )
{ {
m_originLayer = aItem->Layers().Start(); 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 ); assert( m_originLayer >= 0 );
m_layer = m_originLayer; m_layer = m_originLayer;

View File

@ -35,6 +35,7 @@
#include <pcbcommon.h> #include <pcbcommon.h>
#include <pcb_painter.h> #include <pcb_painter.h>
#include <dialogs/dialog_pns_settings.h> #include <dialogs/dialog_pns_settings.h>
#include <dialogs/dialog_pns_diff_pair_dimensions.h>
#include <dialogs/dialog_track_via_size.h> #include <dialogs/dialog_track_via_size.h>
#include <base_units.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", static TOOL_ACTION ACT_CustomTrackWidth( "pcbnew.InteractiveRouter.CustomTrackWidth",
AS_CONTEXT, 'W', AS_CONTEXT, 'W',
"Custom Track Width", "Shows a dialog for changing the track width and via size."); "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", static TOOL_ACTION ACT_SwitchPosture( "pcbnew.InteractiveRouter.SwitchPosture",
AS_CONTEXT, '/', AS_CONTEXT, '/',
"Switch Track Posture", "Switches posture of the currenly routed track."); "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() : 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 class ROUTER_TOOL_MENU: public CONTEXT_MENU
{ {
public: public:
ROUTER_TOOL_MENU( BOARD* aBoard ) ROUTER_TOOL_MENU( BOARD* aBoard, PNS_ROUTER_MODE aMode )
{ {
SetTitle( wxT( "Interactive Router" ) ); SetTitle( wxT( "Interactive Router" ) );
Add( ACT_NewTrack ); Add( ACT_NewTrack );
@ -228,39 +230,32 @@ public:
Add( ACT_CustomTrackWidth ); Add( ACT_CustomTrackWidth );
if ( aMode == PNS_MODE_ROUTE_DIFF_PAIR )
Add( ACT_SetDpDimensions );
AppendSeparator(); AppendSeparator();
Add( ACT_RouterOptions ); Add( PNS_TOOL_BASE::ACT_RouterOptions );
} }
}; };
ROUTER_TOOL::~ROUTER_TOOL() ROUTER_TOOL::~ROUTER_TOOL()
{ {
delete m_router;
} }
void ROUTER_TOOL::Reset( RESET_REASON aReason ) void ROUTER_TOOL::Reset( RESET_REASON aReason )
{ {
if( m_router ) PNS_TOOL_BASE::Reset( aReason );
delete m_router;
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 ROUTER_TOOL::getDefaultWidth( int aNetCode )
{ {
int w, d1, d2; int w, d1, d2;
@ -274,11 +269,10 @@ int ROUTER_TOOL::getDefaultWidth( int aNetCode )
void ROUTER_TOOL::getNetclassDimensions( int aNetCode, int& aWidth, void ROUTER_TOOL::getNetclassDimensions( int aNetCode, int& aWidth,
int& aViaDiameter, int& aViaDrill ) int& aViaDiameter, int& aViaDrill )
{ {
BOARD* board = getModel<BOARD>(); BOARD_DESIGN_SETTINGS &bds = m_board->GetDesignSettings();
BOARD_DESIGN_SETTINGS &bds = board->GetDesignSettings();
NETCLASSPTR netClass; NETCLASSPTR netClass;
NETINFO_ITEM* ni = board->FindNet( aNetCode ); NETINFO_ITEM* ni = m_board->FindNet( aNetCode );
if( ni ) if( ni )
{ {
@ -294,96 +288,8 @@ void ROUTER_TOOL::getNetclassDimensions( int aNetCode, int& aWidth,
aViaDrill = netClass->GetViaDrill(); aViaDrill = netClass->GetViaDrill();
} }
void ROUTER_TOOL::handleCommonEvents( const TOOL_EVENT& aEvent )
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 )
{
PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME> ();
BOARD* board = getModel<BOARD> ();
#ifdef DEBUG #ifdef DEBUG
if( aEvent.IsKeyPressed() ) if( aEvent.IsKeyPressed() )
{ {
@ -399,18 +305,27 @@ void ROUTER_TOOL::handleCommonEvents( TOOL_EVENT& aEvent )
#endif #endif
if( aEvent.IsAction( &ACT_RouterOptions ) ) 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() ) if( settingsDlg.ShowModal() )
{ {
// FIXME: do we need an explicit update? // 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 ) ) else if( aEvent.IsAction( &ACT_CustomTrackWidth ) )
{ {
BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings(); BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
DIALOG_TRACK_VIA_SIZE sizeDlg( frame, bds ); DIALOG_TRACK_VIA_SIZE sizeDlg( m_frame, bds );
if( sizeDlg.ShowModal() ) if( sizeDlg.ShowModal() )
{ {
@ -422,107 +337,12 @@ void ROUTER_TOOL::handleCommonEvents( TOOL_EVENT& aEvent )
else if( aEvent.IsAction( &COMMON_ACTIONS::trackViaSizeChanged ) ) else if( aEvent.IsAction( &COMMON_ACTIONS::trackViaSizeChanged ) )
{ {
PNS_SIZES_SETTINGS sizes; PNS_SIZES_SETTINGS sizes ( m_router->Sizes() );
sizes.ImportCurrent ( board->GetDesignSettings() ); sizes.ImportCurrent ( m_board->GetDesignSettings() );
m_router->UpdateSizes ( sizes ); 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 ROUTER_TOOL::getStartLayer( const PNS_ITEM* aItem )
{ {
int tl = getView()->GetTopLayer(); int tl = getView()->GetTopLayer();
@ -541,9 +361,7 @@ int ROUTER_TOOL::getStartLayer( const PNS_ITEM* aItem )
} }
void ROUTER_TOOL::switchLayerOnViaPlacement() void ROUTER_TOOL::switchLayerOnViaPlacement()
{ {
PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>(); int al = m_frame->GetActiveLayer();
int al = frame->GetActiveLayer();
int cl = m_router->GetCurrentLayer(); int cl = m_router->GetCurrentLayer();
if( cl != al ) if( cl != al )
@ -556,24 +374,23 @@ void ROUTER_TOOL::switchLayerOnViaPlacement()
if( newLayer ) if( newLayer )
{ {
m_router->SwitchLayer ( *newLayer ); m_router->SwitchLayer ( *newLayer );
frame->SetActiveLayer ( ToLAYER_ID( *newLayer ) ); m_frame->SetActiveLayer ( ToLAYER_ID( *newLayer ) );
} }
} }
bool ROUTER_TOOL::onViaCommand( VIATYPE_T aType ) bool ROUTER_TOOL::onViaCommand( VIATYPE_T aType )
{ {
BOARD* board = getModel<BOARD> (); BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
const int layerCount = bds.GetCopperLayerCount(); const int layerCount = bds.GetCopperLayerCount();
int currentLayer = m_router->GetCurrentLayer(); int currentLayer = m_router->GetCurrentLayer();
PNS_SIZES_SETTINGS sizes = m_router->Sizes(); PNS_SIZES_SETTINGS sizes = m_router->Sizes();
// fixme: P&S supports more than one fixed layer pair. Update the dialog?
sizes.ClearLayerPairs(); sizes.ClearLayerPairs();
sizes.AddLayerPair( frame->GetScreen()->m_Route_Layer_TOP, sizes.AddLayerPair( m_frame->GetScreen()->m_Route_Layer_TOP,
frame->GetScreen()->m_Route_Layer_BOTTOM ); m_frame->GetScreen()->m_Route_Layer_BOTTOM );
if( !m_router->IsPlacingVia() ) if( !m_router->IsPlacingVia() )
{ {
@ -606,40 +423,77 @@ bool ROUTER_TOOL::onViaCommand( VIATYPE_T aType )
return false; 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>();
bool ROUTER_TOOL::prepareInteractive()
{
int routingLayer = getStartLayer ( m_startItem ); int routingLayer = getStartLayer ( m_startItem );
frame->SetActiveLayer( ToLAYER_ID ( routingLayer ) ); m_frame->SetActiveLayer( ToLAYER_ID ( routingLayer ) );
// fixme: switch on invisible layer // fixme: switch on invisible layer
if( m_startItem && m_startItem->Net() >= 0 ) if( m_startItem && m_startItem->Net() >= 0 )
{ {
highlightNet( true, m_startItem->Net() ); highlightNet( true, m_startItem->Net() );
// Update track width and via size shown in main toolbar comboboxes // 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 else
frame->SetCurrentNetClass( NETCLASS::Default ); m_frame->SetCurrentNetClass( NETCLASS::Default );
ctls->ForceCursorPosition( false ); m_ctls->ForceCursorPosition( false );
ctls->SetAutoPan( true ); m_ctls->SetAutoPan( true );
PNS_SIZES_SETTINGS sizes; PNS_SIZES_SETTINGS sizes ( m_router->Sizes() );
sizes.Init ( board, m_startItem );
sizes.AddLayerPair ( frame->GetScreen()->m_Route_Layer_TOP, sizes.Init ( m_board, m_startItem );
frame->GetScreen()->m_Route_Layer_BOTTOM ); sizes.AddLayerPair ( m_frame->GetScreen()->m_Route_Layer_TOP,
m_frame->GetScreen()->m_Route_Layer_BOTTOM );
m_router->UpdateSizes( sizes ); 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_endItem = NULL;
m_endSnapPoint = m_startSnapPoint; 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() ) while( OPT_TOOL_EVENT evt = Wait() )
{ {
if( evt->IsCancel() || evt->IsActivate() ) if( evt->IsCancel() || evt->IsActivate() )
@ -652,6 +506,7 @@ void ROUTER_TOOL::performRouting()
else if( evt->IsMotion() ) else if( evt->IsMotion() )
{ {
updateEndItem( *evt ); updateEndItem( *evt );
m_router->SetOrthoMode ( evt->Modifier ( MD_CTRL ) );
m_router->Move( m_endSnapPoint, m_endItem ); m_router->Move( m_endSnapPoint, m_endItem );
} }
else if( evt->IsClick( BUT_LEFT ) ) else if( evt->IsClick( BUT_LEFT ) )
@ -663,13 +518,10 @@ void ROUTER_TOOL::performRouting()
break; break;
if( needLayerSwitch ) if( needLayerSwitch )
{
switchLayerOnViaPlacement(); switchLayerOnViaPlacement();
}
// Synchronize the indicated layer // 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 ); m_router->Move( m_endSnapPoint, m_endItem );
} }
else if( evt->IsAction( &ACT_PlaceThroughVia ) ) else if( evt->IsAction( &ACT_PlaceThroughVia ) )
@ -692,7 +544,7 @@ void ROUTER_TOOL::performRouting()
else if( evt->IsAction( &COMMON_ACTIONS::layerChanged ) ) else if( evt->IsAction( &COMMON_ACTIONS::layerChanged ) )
{ {
updateEndItem( *evt ); updateEndItem( *evt );
m_router->SwitchLayer( frame->GetActiveLayer() ); m_router->SwitchLayer( m_frame->GetActiveLayer() );
m_router->Move( m_endSnapPoint, m_endItem ); // refresh m_router->Move( m_endSnapPoint, m_endItem ); // refresh
} }
else if( evt->IsAction( &ACT_EndTrack ) ) else if( evt->IsAction( &ACT_EndTrack ) )
@ -704,42 +556,67 @@ void ROUTER_TOOL::performRouting()
handleCommonEvents( *evt ); 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 m_router->UpdateSizes( sizes );
frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED ); m_savedSizes = sizes;
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 ); return 0;
ctls->ForceCursorPosition( false ); }
highlightNet( false );
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>(); PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
BOARD* board = getModel<BOARD>(); BOARD* board = getModel<BOARD>();
// Deselect all items // Deselect all items
m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true ); m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Interactive Router" ) ); Activate();
ctls->SetSnapping( true ); m_router->SetMode ( aMode );
ctls->ShowCursor( true );
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() ); SetContextMenu ( ctxMenu.get() );
// Main loop: keep receiving events // Main loop: keep receiving events
@ -774,17 +651,17 @@ int ROUTER_TOOL::Main( const TOOL_EVENT& aEvent )
} }
// Restore the default settings // Restore the default settings
ctls->SetAutoPan( false ); m_ctls->SetAutoPan( false );
ctls->ShowCursor( false ); m_ctls->ShowCursor( false );
frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString ); frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
// Store routing settings till the next invocation // Store routing settings till the next invocation
m_settings = m_router->Settings(); m_savedSettings = m_router->Settings();
m_savedSizes = m_router->Sizes();
return 0; return 0;
} }
void ROUTER_TOOL::performDragging() void ROUTER_TOOL::performDragging()
{ {
PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>(); PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
@ -845,3 +722,9 @@ void ROUTER_TOOL::performDragging()
ctls->ForceCursorPosition( false ); ctls->ForceCursorPosition( false );
highlightNet( false ); highlightNet( false );
} }
int ROUTER_TOOL::InlineDrag ( const TOOL_EVENT& aEvent )
{
return 0;
}

View File

@ -22,61 +22,41 @@
#ifndef __ROUTER_TOOL_H #ifndef __ROUTER_TOOL_H
#define __ROUTER_TOOL_H #define __ROUTER_TOOL_H
#include <import_export.h> #include "pns_tool_base.h"
#include <math/vector2d.h> class APIEXPORT ROUTER_TOOL : public PNS_TOOL_BASE
#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
{ {
public: public:
ROUTER_TOOL(); ROUTER_TOOL();
~ROUTER_TOOL(); ~ROUTER_TOOL();
void Reset( RESET_REASON aReason ); 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: private:
PNS_ITEM* pickSingleItem( const VECTOR2I& aWhere, int aNet = -1, int aLayer = -1 );
int mainLoop( PNS_ROUTER_MODE aMode );
int getDefaultWidth( int aNetCode ); int getDefaultWidth( int aNetCode );
void performRouting(); void performRouting();
void performDragging(); 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 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 ); int getStartLayer( const PNS_ITEM* aItem );
void switchLayerOnViaPlacement(); void switchLayerOnViaPlacement();
bool onViaCommand( VIATYPE_T aType ); bool onViaCommand( VIATYPE_T aType );
MSG_PANEL_ITEMS m_panelItems; bool prepareInteractive( );
bool finishInteractive( bool aSaveUndoBuffer );
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;
}; };
#endif #endif

View File

@ -390,11 +390,41 @@ TOOL_ACTION COMMON_ACTIONS::toBeDone( "pcbnew.Control.toBeDone",
AS_GLOBAL, 0, // dialog saying it is not implemented yet AS_GLOBAL, 0, // dialog saying it is not implemented yet
"", "" ); // so users are aware of that "", "" ); // so users are aware of that
TOOL_ACTION COMMON_ACTIONS::routerActivate( "pcbnew.InteractiveRouter", TOOL_ACTION COMMON_ACTIONS::routerActivateSingle( "pcbnew.InteractiveRouter.SingleTrack",
AS_GLOBAL, 'X', 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 // Point editor
TOOL_ACTION COMMON_ACTIONS::pointEditorUpdate( "pcbnew.PointEditor.update", TOOL_ACTION COMMON_ACTIONS::pointEditorUpdate( "pcbnew.PointEditor.update",
AS_GLOBAL, 0, AS_GLOBAL, 0,
@ -404,7 +434,6 @@ TOOL_ACTION COMMON_ACTIONS::pointEditorBreakOutline( "pcbnew.PointEditor.breakOu
AS_GLOBAL, 0, AS_GLOBAL, 0,
"Create corner", "Create corner" ); "Create corner", "Create corner" );
// Placement tool // Placement tool
TOOL_ACTION COMMON_ACTIONS::alignTop( "pcbnew.Place.alignTop", TOOL_ACTION COMMON_ACTIONS::alignTop( "pcbnew.Place.alignTop",
AS_GLOBAL, 0, AS_GLOBAL, 0,
@ -445,7 +474,25 @@ boost::optional<TOOL_EVENT> COMMON_ACTIONS::TranslateLegacyId( int aId )
return COMMON_ACTIONS::placeModule.MakeEvent(); return COMMON_ACTIONS::placeModule.MakeEvent();
case ID_TRACK_BUTT: 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: case ID_PCB_ZONES_BUTT:
return COMMON_ACTIONS::drawZone.MakeEvent(); return COMMON_ACTIONS::drawZone.MakeEvent();

View File

@ -116,8 +116,29 @@ public:
static TOOL_ACTION arcPosture; static TOOL_ACTION arcPosture;
// Push and Shove Router Tool // Push and Shove Router Tool
/// Activation of the Push and Shove router /// 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 // Point Editor
/// Update edit points /// Update edit points