Commit 061660e9 authored by Tomasz Włostowski's avatar Tomasz Włostowski

Long-awaited new version of the P&S router

parent e9afb10a
/*
* 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.
*/
#include "dialog_pns_settings.h"
#include <router/pns_routing_settings.h>
DIALOG_PNS_SETTINGS::DIALOG_PNS_SETTINGS( wxWindow* aParent, PNS_ROUTING_SETTINGS& aSettings ) :
DIALOG_PNS_SETTINGS_BASE( aParent ), m_settings( aSettings )
{
// "Figure out what's best" is not available yet
m_mode->Enable( RM_Smart, false );
// Load widgets' values from settings
m_mode->SetSelection( m_settings.Mode() );
m_shoveVias->SetValue( m_settings.ShoveVias() );
m_backPressure->SetValue( m_settings.JumpOverObstacles() );
m_removeLoops->SetValue( m_settings.RemoveLoops() );
m_suggestEnding->SetValue( m_settings.SuggestFinish() );
m_autoNeckdown->SetValue( m_settings.SmartPads() );
m_effort->SetValue( m_settings.OptimizerEffort() );
m_smoothDragged->SetValue( m_settings.SmoothDraggedSegments() );
m_violateDrc->SetValue( m_settings.CanViolateDRC() );
}
void DIALOG_PNS_SETTINGS::OnClose( wxCloseEvent& aEvent )
{
// Do nothing, it is result of ESC pressing
EndModal( 0 );
}
void DIALOG_PNS_SETTINGS::OnOkClick( wxCommandEvent& aEvent )
{
// Save widgets' values to settings
m_settings.SetMode( (PNS_MODE) m_mode->GetSelection() );
m_settings.SetShoveVias( m_shoveVias->GetValue() );
m_settings.SetJumpOverObstacles( m_backPressure->GetValue() );
m_settings.SetRemoveLoops( m_removeLoops->GetValue() );
m_settings.SetSuggestFinish ( m_suggestEnding->GetValue() );
m_settings.SetSmartPads( m_autoNeckdown->GetValue() );
m_settings.SetOptimizerEffort( (PNS_OPTIMIZATION_EFFORT) m_effort->GetValue() );
m_settings.SetSmoothDraggedSegments( m_smoothDragged->GetValue() );
m_settings.SetCanViolateDRC( m_violateDrc->GetValue() );
EndModal( 1 );
}
void DIALOG_PNS_SETTINGS::OnCancelClick( wxCommandEvent& aEvent )
{
// Do nothing
EndModal( 0 );
}
/*
* 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_settings__
#define __dialog_pns_settings__
#include "dialog_pns_settings_base.h"
class PNS_ROUTING_SETTINGS;
class DIALOG_PNS_SETTINGS : public DIALOG_PNS_SETTINGS_BASE
{
public:
DIALOG_PNS_SETTINGS( wxWindow* aParent, PNS_ROUTING_SETTINGS& aSettings );
virtual void OnClose( wxCloseEvent& aEvent );
virtual void OnOkClick( wxCommandEvent& aEvent );
virtual void OnCancelClick( wxCommandEvent& aEvent );
private:
PNS_ROUTING_SETTINGS& m_settings;
};
#endif // __dialog_pns_settings__
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Apr 30 2013)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "dialog_pns_settings_base.h"
///////////////////////////////////////////////////////////////////////////
DIALOG_PNS_SETTINGS_BASE::DIALOG_PNS_SETTINGS_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
wxBoxSizer* bMainSizer;
bMainSizer = new wxBoxSizer( wxVERTICAL );
wxString m_modeChoices[] = { _("Highlight collisions"), _("Shove"), _("Walk around"), _("Figure out what's best") };
int m_modeNChoices = sizeof( m_modeChoices ) / sizeof( wxString );
m_mode = new wxRadioBox( this, wxID_ANY, _("Mode"), wxDefaultPosition, wxDefaultSize, m_modeNChoices, m_modeChoices, 1, wxRA_SPECIFY_COLS );
m_mode->SetSelection( 1 );
bMainSizer->Add( m_mode, 0, wxALL, 5 );
wxStaticBoxSizer* bOptions;
bOptions = new wxStaticBoxSizer( new wxStaticBox( this, wxID_ANY, _("Options") ), wxVERTICAL );
m_shoveVias = new wxCheckBox( this, wxID_ANY, _("Shove vias"), wxDefaultPosition, wxDefaultSize, 0 );
bOptions->Add( m_shoveVias, 0, wxALL, 5 );
m_backPressure = new wxCheckBox( this, wxID_ANY, _("Jump over obstacles"), wxDefaultPosition, wxDefaultSize, 0 );
bOptions->Add( m_backPressure, 0, wxALL, 5 );
m_removeLoops = new wxCheckBox( this, wxID_ANY, _("Remove redundant tracks"), wxDefaultPosition, wxDefaultSize, 0 );
bOptions->Add( m_removeLoops, 0, wxALL, 5 );
m_autoNeckdown = new wxCheckBox( this, wxID_ANY, _("Automatic neckdown"), wxDefaultPosition, wxDefaultSize, 0 );
bOptions->Add( m_autoNeckdown, 0, wxALL, 5 );
m_smoothDragged = new wxCheckBox( this, wxID_ANY, _("Smooth dragged segments"), wxDefaultPosition, wxDefaultSize, 0 );
bOptions->Add( m_smoothDragged, 0, wxALL, 5 );
m_violateDrc = new wxCheckBox( this, wxID_ANY, _("Allow DRC violations"), wxDefaultPosition, wxDefaultSize, 0 );
bOptions->Add( m_violateDrc, 0, wxALL, 5 );
m_suggestEnding = new wxCheckBox( this, wxID_ANY, _("Suggest track finish"), wxDefaultPosition, wxDefaultSize, 0 );
m_suggestEnding->Enable( false );
bOptions->Add( m_suggestEnding, 0, wxALL, 5 );
wxBoxSizer* bEffort;
bEffort = new wxBoxSizer( wxHORIZONTAL );
m_effortLabel = new wxStaticText( this, wxID_ANY, _("Optimizer effort"), wxDefaultPosition, wxDefaultSize, 0 );
m_effortLabel->Wrap( -1 );
bEffort->Add( m_effortLabel, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
bEffort->Add( 0, 0, 0, wxEXPAND, 5 );
wxBoxSizer* bSlider;
bSlider = new wxBoxSizer( wxVERTICAL );
m_effort = new wxSlider( this, wxID_ANY, 1, 0, 2, wxDefaultPosition, wxDefaultSize, wxSL_AUTOTICKS|wxSL_BOTTOM|wxSL_HORIZONTAL|wxSL_TOP );
bSlider->Add( m_effort, 1, wxEXPAND, 5 );
wxBoxSizer* bSliderLabels;
bSliderLabels = new wxBoxSizer( wxHORIZONTAL );
m_lowLabel = new wxStaticText( this, wxID_ANY, _("low"), wxDefaultPosition, wxDefaultSize, 0 );
m_lowLabel->Wrap( -1 );
m_lowLabel->SetFont( wxFont( 8, 70, 90, 90, false, wxEmptyString ) );
bSliderLabels->Add( m_lowLabel, 0, 0, 5 );
bSliderLabels->Add( 0, 0, 1, wxEXPAND, 5 );
m_highLabel = new wxStaticText( this, wxID_ANY, _("high"), wxDefaultPosition, wxDefaultSize, 0 );
m_highLabel->Wrap( -1 );
m_highLabel->SetFont( wxFont( 8, 70, 90, 90, false, wxEmptyString ) );
bSliderLabels->Add( m_highLabel, 0, 0, 5 );
bSlider->Add( bSliderLabels, 1, wxEXPAND, 5 );
bEffort->Add( bSlider, 1, wxEXPAND, 5 );
bOptions->Add( bEffort, 1, wxALL|wxEXPAND, 5 );
wxBoxSizer* bButtons;
bButtons = new wxBoxSizer( wxHORIZONTAL );
m_ok = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxDefaultSize, 0 );
m_ok->SetDefault();
bButtons->Add( m_ok, 1, wxALL, 5 );
m_cancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxDefaultSize, 0 );
bButtons->Add( m_cancel, 1, wxALL, 5 );
bOptions->Add( bButtons, 1, wxEXPAND, 5 );
bMainSizer->Add( bOptions, 1, wxEXPAND, 5 );
this->SetSizer( bMainSizer );
this->Layout();
// Connect Events
this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_PNS_SETTINGS_BASE::OnClose ) );
m_ok->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_SETTINGS_BASE::OnOkClick ), NULL, this );
m_cancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_SETTINGS_BASE::OnCancelClick ), NULL, this );
}
DIALOG_PNS_SETTINGS_BASE::~DIALOG_PNS_SETTINGS_BASE()
{
// Disconnect Events
this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_PNS_SETTINGS_BASE::OnClose ) );
m_ok->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_SETTINGS_BASE::OnOkClick ), NULL, this );
m_cancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_PNS_SETTINGS_BASE::OnCancelClick ), NULL, this );
}
This source diff could not be displayed because it is too large. You can view the blob instead.
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Apr 30 2013)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#ifndef __DIALOG_PNS_SETTINGS_BASE_H__
#define __DIALOG_PNS_SETTINGS_BASE_H__
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
#include <wx/string.h>
#include <wx/radiobox.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/checkbox.h>
#include <wx/stattext.h>
#include <wx/slider.h>
#include <wx/sizer.h>
#include <wx/button.h>
#include <wx/statbox.h>
#include <wx/dialog.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// Class DIALOG_PNS_SETTINGS_BASE
///////////////////////////////////////////////////////////////////////////////
class DIALOG_PNS_SETTINGS_BASE : public wxDialog
{
private:
protected:
wxRadioBox* m_mode;
wxCheckBox* m_shoveVias;
wxCheckBox* m_backPressure;
wxCheckBox* m_removeLoops;
wxCheckBox* m_autoNeckdown;
wxCheckBox* m_smoothDragged;
wxCheckBox* m_violateDrc;
wxCheckBox* m_suggestEnding;
wxStaticText* m_effortLabel;
wxSlider* m_effort;
wxStaticText* m_lowLabel;
wxStaticText* m_highLabel;
wxButton* m_ok;
wxButton* m_cancel;
// Virtual event handlers, overide them in your derived class
virtual void OnClose( wxCloseEvent& event ) { event.Skip(); }
virtual void OnOkClick( wxCommandEvent& event ) { event.Skip(); }
virtual void OnCancelClick( wxCommandEvent& event ) { event.Skip(); }
public:
DIALOG_PNS_SETTINGS_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Interactive Router settings"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 289,504 ), long style = wxDEFAULT_DIALOG_STYLE );
~DIALOG_PNS_SETTINGS_BASE();
};
#endif //__DIALOG_PNS_SETTINGS_BASE_H__
/*
* 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 track width and via size dialog.
*/
#include "dialog_track_via_size.h"
#include <router/pns_routing_settings.h>
#include <base_units.h>
#include <confirm.h>
DIALOG_TRACK_VIA_SIZE::DIALOG_TRACK_VIA_SIZE( wxWindow* aParent, PNS_ROUTING_SETTINGS& aSettings ) :
DIALOG_TRACK_VIA_SIZE_BASE( aParent ),
m_settings( aSettings )
{
// Load router settings to dialog fields
m_trackWidth->SetValue( To_User_Unit( m_trackWidth->GetUnits(), m_settings.GetTrackWidth() ) );
m_viaDiameter->SetValue( To_User_Unit( m_viaDiameter->GetUnits(), m_settings.GetViaDiameter() ) );
m_viaDrill->SetValue( To_User_Unit( m_viaDrill->GetUnits(), m_settings.GetViaDrill() ) );
m_trackWidth->SetFocus();
// Pressing ENTER when any of the text input fields is active applies changes
#if wxCHECK_VERSION( 3, 0, 0 )
Connect( wxEVT_TEXT_ENTER, wxCommandEventHandler( DIALOG_TRACK_VIA_SIZE::onOkClick ), NULL, this );
#else
Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( DIALOG_TRACK_VIA_SIZE::onOkClick ), NULL, this );
#endif
}
bool DIALOG_TRACK_VIA_SIZE::check()
{
// Wrong input
if( !m_trackWidth->GetValue() || !m_viaDiameter->GetValue() || !m_viaDrill->GetValue() )
return false;
// Via drill should be smaller than via diameter
if( *m_viaDrill->GetValue() >= m_viaDiameter->GetValue() )
return false;
return true;
}
void DIALOG_TRACK_VIA_SIZE::onClose( wxCloseEvent& aEvent )
{
EndModal( 0 );
}
void DIALOG_TRACK_VIA_SIZE::onOkClick( wxCommandEvent& aEvent )
{
if( check() )
{
// Store dialog values to the router settings
m_settings.SetTrackWidth( From_User_Unit( m_trackWidth->GetUnits(), *m_trackWidth->GetValue() ) );
m_settings.SetViaDiameter( From_User_Unit( m_viaDiameter->GetUnits(), *m_viaDiameter->GetValue() ) );
m_settings.SetViaDrill( From_User_Unit( m_viaDrill->GetUnits(), *m_viaDrill->GetValue() ) );
EndModal( 1 );
}
else
{
DisplayError( GetParent(), _( "Settings are incorrect" ) );
m_trackWidth->SetFocus();
}
}
void DIALOG_TRACK_VIA_SIZE::onCancelClick( wxCommandEvent& aEvent )
{
EndModal( 0 );
}
/*
* 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 track width and via size dialog.
*/
#ifndef __dialog_track_via_size__
#define __dialog_track_via_size__
#include "dialog_track_via_size_base.h"
class PNS_ROUTING_SETTINGS;
/** Implementing DIALOG_TRACK_VIA_SIZE_BASE */
class DIALOG_TRACK_VIA_SIZE : public DIALOG_TRACK_VIA_SIZE_BASE
{
public:
/** Constructor */
DIALOG_TRACK_VIA_SIZE( wxWindow* aParent, PNS_ROUTING_SETTINGS& aSettings );
protected:
// Routings settings that are modified by the dialog.
PNS_ROUTING_SETTINGS& m_settings;
///> Checks if values given in the dialog are sensible.
bool check();
// Handlers for DIALOG_TRACK_VIA_SIZE_BASE events.
void onClose( wxCloseEvent& aEvent );
void onOkClick( wxCommandEvent& aEvent );
void onCancelClick( wxCommandEvent& aEvent );
};
#endif // __dialog_track_via_size__
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Apr 30 2013)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "dialog_track_via_size_base.h"
///////////////////////////////////////////////////////////////////////////
DIALOG_TRACK_VIA_SIZE_BASE::DIALOG_TRACK_VIA_SIZE_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxDialog( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
wxBoxSizer* bSizes;
bSizes = new wxBoxSizer( wxVERTICAL );
m_trackWidth = new WX_UNIT_TEXT( this, _("Track width:") );
bSizes->Add( m_trackWidth, 0, wxALL, 5 );
m_viaDiameter = new WX_UNIT_TEXT( this, _("Via diameter:") );
bSizes->Add( m_viaDiameter, 0, wxALL, 5 );
m_viaDrill = new WX_UNIT_TEXT( this, _("Via drill:") );
bSizes->Add( m_viaDrill, 0, wxALL, 5 );
wxBoxSizer* bButtons;
bButtons = new wxBoxSizer( wxHORIZONTAL );
m_ok = new wxButton( this, wxID_OK, _("OK"), wxDefaultPosition, wxDefaultSize, 0 );
bButtons->Add( m_ok, 1, wxALL, 5 );
m_cancel = new wxButton( this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxDefaultSize, 0 );
bButtons->Add( m_cancel, 1, wxALL, 5 );
bSizes->Add( bButtons, 0, wxEXPAND, 5 );
this->SetSizer( bSizes );
this->Layout();
this->Centre( wxBOTH );
// Connect Events
this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_TRACK_VIA_SIZE_BASE::onClose ) );
m_ok->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_TRACK_VIA_SIZE_BASE::onOkClick ), NULL, this );
m_cancel->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_TRACK_VIA_SIZE_BASE::onCancelClick ), NULL, this );
}
DIALOG_TRACK_VIA_SIZE_BASE::~DIALOG_TRACK_VIA_SIZE_BASE()
{
// Disconnect Events
this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DIALOG_TRACK_VIA_SIZE_BASE::onClose ) );
m_ok->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_TRACK_VIA_SIZE_BASE::onOkClick ), NULL, this );
m_cancel->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_TRACK_VIA_SIZE_BASE::onCancelClick ), NULL, this );
}
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<wxFormBuilder_Project>
<FileVersion major="1" minor="11" />
<object class="Project" expanded="1">
<property name="class_decoration"></property>
<property name="code_generation">C++</property>
<property name="disconnect_events">1</property>
<property name="disconnect_mode">source_name</property>
<property name="disconnect_php_events">0</property>
<property name="disconnect_python_events">0</property>
<property name="embedded_files_path">res</property>
<property name="encoding">UTF-8</property>
<property name="event_generation">connect</property>
<property name="file">dialog_track_via_size_base</property>
<property name="first_id">1000</property>
<property name="help_provider">none</property>
<property name="internationalize">1</property>
<property name="name">DIALOG_TRACK_VIA_SIZE_BASE</property>
<property name="namespace"></property>
<property name="path">.</property>
<property name="precompiled_header"></property>
<property name="relative_path">1</property>
<property name="skip_php_events">1</property>
<property name="skip_python_events">1</property>
<property name="use_enum">0</property>
<property name="use_microsoft_bom">0</property>
<object class="Dialog" expanded="1">
<property name="aui_managed">0</property>
<property name="aui_manager_style">wxAUI_MGR_DEFAULT</property>
<property name="bg"></property>
<property name="center">wxBOTH</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="enabled">1</property>
<property name="event_handler">impl_virtual</property>
<property name="extra_style"></property>
<property name="fg"></property>
<property name="font"></property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="maximum_size"></property>
<property name="minimum_size"></property>
<property name="name">DIALOG_TRACK_VIA_SIZE_BASE</property>
<property name="pos"></property>
<property name="size">388,164</property>
<property name="style">wxDEFAULT_DIALOG_STYLE</property>
<property name="subclass"></property>
<property name="title">Track width and via size</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnActivate"></event>
<event name="OnActivateApp"></event>
<event name="OnAuiFindManager"></event>
<event name="OnAuiPaneButton"></event>
<event name="OnAuiPaneClose"></event>
<event name="OnAuiPaneMaximize"></event>
<event name="OnAuiPaneRestore"></event>
<event name="OnAuiRender"></event>
<event name="OnChar"></event>
<event name="OnClose">onClose</event>
<event name="OnEnterWindow"></event>
<event name="OnEraseBackground"></event>
<event name="OnHibernate"></event>
<event name="OnIconize"></event>
<event name="OnIdle"></event>
<event name="OnInitDialog"></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 class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bSizes</property>
<property name="orient">wxVERTICAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">0</property>
<object class="CustomControl" 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="class">WX_UNIT_TEXT</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_menu">1</property>
<property name="declaration">WX_UNIT_TEXT* m_trackWidth;</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="include">#include &lt;wxunittext.h&gt;</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_trackWidth</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="settings"></property>
<property name="show">1</property>
<property name="size"></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>
<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</property>
<property name="proportion">0</property>
<object class="CustomControl" 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="class">WX_UNIT_TEXT</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_menu">1</property>
<property name="declaration">WX_UNIT_TEXT* m_viaDiameter;</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="include">#include &lt;wxunittext.h&gt;</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_viaDiameter</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="settings"></property>
<property name="show">1</property>
<property name="size"></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>
<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</property>
<property name="proportion">0</property>
<object class="CustomControl" 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="class">WX_UNIT_TEXT</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_menu">1</property>
<property name="declaration">WX_UNIT_TEXT* m_viaDrill;</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="include">#include &lt;wxunittext.h&gt;</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_viaDrill</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="settings"></property>
<property name="show">1</property>
<property name="size"></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>
<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">wxEXPAND</property>
<property name="proportion">0</property>
<object class="wxBoxSizer" expanded="1">
<property name="minimum_size"></property>
<property name="name">bButtons</property>
<property name="orient">wxHORIZONTAL</property>
<property name="permission">none</property>
<object class="sizeritem" expanded="1">
<property name="border">5</property>
<property name="flag">wxALL</property>
<property name="proportion">1</property>
<object class="wxButton" 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">0</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_OK</property>
<property name="label">OK</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_ok</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="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">onOkClick</event>
<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</property>
<property name="proportion">1</property>
<object class="wxButton" 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">0</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_CANCEL</property>
<property name="label">Cancel</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_cancel</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="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<event name="OnButtonClick">onCancelClick</event>
<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>
</object>
</object>
</wxFormBuilder_Project>
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version Apr 30 2013)
// http://www.wxformbuilder.org/
//
// PLEASE DO "NOT" EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#ifndef __DIALOG_TRACK_VIA_SIZE_BASE_H__
#define __DIALOG_TRACK_VIA_SIZE_BASE_H__
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
#include <wxunittext.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/string.h>
#include <wx/button.h>
#include <wx/sizer.h>
#include <wx/dialog.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// Class DIALOG_TRACK_VIA_SIZE_BASE
///////////////////////////////////////////////////////////////////////////////
class DIALOG_TRACK_VIA_SIZE_BASE : public wxDialog
{
private:
protected:
WX_UNIT_TEXT* m_trackWidth;
WX_UNIT_TEXT* m_viaDiameter;
WX_UNIT_TEXT* m_viaDrill;
wxButton* m_ok;
wxButton* m_cancel;
// Virtual event handlers, overide them in your derived class
virtual void onClose( wxCloseEvent& event ) { event.Skip(); }
virtual void onOkClick( wxCommandEvent& event ) { event.Skip(); }
virtual void onCancelClick( wxCommandEvent& event ) { event.Skip(); }
public:
DIALOG_TRACK_VIA_SIZE_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Track width and via size"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( 388,164 ), long style = wxDEFAULT_DIALOG_STYLE );
~DIALOG_TRACK_VIA_SIZE_BASE();
};
#endif //__DIALOG_TRACK_VIA_SIZE_BASE_H__
...@@ -11,40 +11,50 @@ include_directories( ...@@ -11,40 +11,50 @@ include_directories(
set( PCBNEW_PNS_SRCS set( PCBNEW_PNS_SRCS
direction.h direction.h
pns_via.h time_limit.h
pns_routing_settings.h time_limit.cpp
pns_shove.cpp
pns_line.cpp
pns_utils.h
pns_layerset.h
trace.h trace.h
pns_algo_base.h
pns_algo_base.cpp
pns_dragger.cpp
pns_dragger.h
pns_index.h
pns_item.h
pns_item.cpp
pns_itemset.h
pns_itemset.cpp
pns_joint.h
pns_layerset.h
pns_line.h pns_line.h
pns_walkaround.cpp pns_line.cpp
pns_node.h pns_line_placer.h
pns_line_placer.cpp pns_line_placer.cpp
pns_utils.cpp pns_logger.h
pns_solid.h pns_logger.cpp
pns_item.cpp pns_node.h
pns_via.cpp
pns_node.cpp pns_node.cpp
pns_solid.cpp
pns_line_placer.h
pns_optimizer.h pns_optimizer.h
pns_walkaround.h pns_optimizer.cpp
pns_shove.h
pns_router.h pns_router.h
pns_router.cpp pns_router.cpp
pns_index.h pns_routing_settings.h
pns_item.h pns_routing_settings.cpp
pns_optimizer.cpp
pns_joint.h
pns_segment.h pns_segment.h
pns_itemset.h pns_shove.h
pns_itemset.cpp pns_shove.cpp
router_tool.cpp pns_solid.h
router_tool.h pns_solid.cpp
pns_utils.h
pns_utils.cpp
pns_via.h
pns_via.cpp
pns_walkaround.h
pns_walkaround.cpp
router_preview_item.cpp router_preview_item.cpp
router_preview_item.h router_preview_item.h
router_tool.cpp
router_tool.h
) )
add_library( pnsrouter STATIC ${PCBNEW_PNS_SRCS} ) add_library( pnsrouter STATIC ${PCBNEW_PNS_SRCS} )
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -180,6 +180,11 @@ public: ...@@ -180,6 +180,11 @@ public:
return ( m_dir % 2 ) == 1; return ( m_dir % 2 ) == 1;
} }
bool IsDefined() const
{
return m_dir != UNDEFINED;
}
/** /**
* Function BuildInitialTrace() * Function BuildInitialTrace()
* *
...@@ -244,32 +249,75 @@ public: ...@@ -244,32 +249,75 @@ public:
return aOther.m_dir != m_dir; return aOther.m_dir != m_dir;
} }
/**
* Function Right()
*
* Returns the direction on the right side of this (i.e. turns right
* by 45 deg)
*/
const DIRECTION_45 Right() const const DIRECTION_45 Right() const
{ {
DIRECTION_45 r; DIRECTION_45 r;
r.m_dir = (Directions) (m_dir + 1); if ( m_dir != UNDEFINED )
r.m_dir = static_cast<Directions>( ( m_dir + 1 ) % 8 );
if( r.m_dir == NW )
r.m_dir = N;
return r; return r;
} }
private: /**
* Function Left()
*
* Returns the direction on the left side of this (i.e. turns left
* by 45 deg)
*/
const DIRECTION_45 Left() const
{
DIRECTION_45 l;
if (m_dir == UNDEFINED)
return l;
template <typename T> if(m_dir == N)
int sign( T val ) const l.m_dir = NW;
else
l.m_dir = static_cast<Directions> (m_dir - 1);
return l;
}
/**
* Function ToVector()
*
* Returns a unit vector corresponding to our direction.
*/
const VECTOR2I ToVector() const
{ {
return (T( 0 ) < val) - ( val < T( 0 ) ); switch(m_dir)
{
case N: return VECTOR2I(0, 1);
case S: return VECTOR2I(0, -1);
case E: return VECTOR2I(1, 0);
case W: return VECTOR2I(-1, 0);
case NE: return VECTOR2I(1, 1);
case NW: return VECTOR2I(-1, 1);
case SE: return VECTOR2I(1, -1);
case SW: return VECTOR2I(-1, -1);
default:
return VECTOR2I(0, 0);
}
} }
private:
/** /**
* Function construct() * Function construct()
* 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;
...@@ -321,8 +369,9 @@ private: ...@@ -321,8 +369,9 @@ private:
m_dir = S; m_dir = S;
} }
} }
Directions m_dir; ///> our actual direction ///> our actual direction
Directions m_dir;
}; };
#endif // __DIRECTION_H #endif // __DIRECTION_H
/*
* 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 "pns_algo_base.h"
#include "pns_router.h"
PNS_ROUTING_SETTINGS& PNS_ALGO_BASE::Settings() const
{
return m_router->Settings();
}
PNS_LOGGER *PNS_ALGO_BASE::Logger()
{
return NULL;
}
\ No newline at end of file
/*
* 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_ALGO_BASE_H
#define __PNS_ALGO_BASE_H
#include "pns_routing_settings.h"
class PNS_ROUTER;
class PNS_LOGGER;
/**
* Class PNS_ALGO_BASE
*
* Base class for all P&S algorithms (shoving, walkaround, line placement, dragging, etc.)
* Holds a bunch of objects commonly used by all algorithms (P&S settings, parent router instance, logging)
**/
class PNS_ALGO_BASE {
public:
PNS_ALGO_BASE ( PNS_ROUTER *aRouter ):
m_router ( aRouter )
{};
virtual ~PNS_ALGO_BASE() {}
///> Returns the instance of our router
PNS_ROUTER *Router() const {
return m_router;
}
///> Returns current router settings
PNS_ROUTING_SETTINGS& Settings() const;
///> Returns the logger object, allowing to dump geometry to a file.
virtual PNS_LOGGER *Logger();
private:
PNS_ROUTER *m_router;
};
#endif
/*
* 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 "pns_dragger.h"
#include "pns_shove.h"
#include "pns_router.h"
PNS_DRAGGER::PNS_DRAGGER( PNS_ROUTER* aRouter ) :
PNS_ALGO_BASE ( aRouter )
{
m_world = NULL;
m_shove = NULL;
};
PNS_DRAGGER::~PNS_DRAGGER()
{
if( m_shove )
delete m_shove;
}
void PNS_DRAGGER::SetWorld ( PNS_NODE *aWorld )
{
m_world = aWorld;
}
bool PNS_DRAGGER::startDragSegment( const VECTOR2D& aP, PNS_SEGMENT *aSeg )
{
int w2 = aSeg->Width() / 2;
m_draggedLine = m_world->AssembleLine ( aSeg, &m_draggedSegmentIndex );
m_shove->SetInitialLine (m_draggedLine);
m_lastValidDraggedLine = *m_draggedLine;
m_lastValidDraggedLine.ClearSegmentLinks();
if( (aP - aSeg->Seg().A).EuclideanNorm() <= w2 )
m_mode = CORNER;
else if( (aP - aSeg->Seg().B).EuclideanNorm() <= w2 )
{
m_draggedSegmentIndex ++;
m_mode = CORNER;
} else
m_mode = SEGMENT;
return true;
}
bool PNS_DRAGGER::startDragVia( const VECTOR2D& aP, PNS_VIA *aVia )
{
m_draggedVia = aVia;
m_initialVia = aVia;
m_mode = VIA;
VECTOR2I p0 ( aVia->Pos() );
PNS_JOINT *jt = m_world->FindJoint( p0, aVia->Layers().Start(), aVia->Net() );
BOOST_FOREACH(PNS_ITEM *item, jt->LinkList() )
{
if(item->OfKind( PNS_ITEM::SEGMENT ))
{
int segIndex;
PNS_SEGMENT *seg = (PNS_SEGMENT *) item;
std::auto_ptr<PNS_LINE> l ( m_world->AssembleLine(seg, &segIndex) );
if(segIndex != 0)
l->Reverse();
m_origViaConnections.push_back (*l);
}
}
return true;
}
bool PNS_DRAGGER::Start ( const VECTOR2I& aP, PNS_ITEM* aStartItem )
{
m_shove = new PNS_SHOVE ( m_world, Router() );
m_lastNode = NULL;
m_draggedItems.Clear();
m_currentMode = Settings().Mode();
TRACE(2, "StartDragging: item %p [kind %d]", aStartItem % aStartItem->Kind());
switch( aStartItem->Kind() )
{
case PNS_ITEM::SEGMENT:
return startDragSegment ( aP, static_cast<PNS_SEGMENT *> (aStartItem) );
case PNS_ITEM::VIA:
return startDragVia ( aP, static_cast<PNS_VIA *> (aStartItem) );
default:
return false;
}
}
bool PNS_DRAGGER::dragMarkObstacles(const VECTOR2I& aP)
{
if(m_lastNode)
{
delete m_lastNode;
m_lastNode = NULL;
}
switch(m_mode)
{
case SEGMENT:
case CORNER:
{
int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine->Width() / 4 : 0;
PNS_LINE tmp (*m_draggedLine);
if(m_mode == SEGMENT)
tmp.DragSegment ( aP, m_draggedSegmentIndex, thresh );
else
tmp.DragCorner ( aP, m_draggedSegmentIndex, thresh );
m_lastNode = m_shove->CurrentNode()->Branch();
m_lastValidDraggedLine = tmp;
m_lastValidDraggedLine.ClearSegmentLinks();
m_lastValidDraggedLine.Unmark();
m_lastNode->Add ( &m_lastValidDraggedLine );
m_draggedItems = PNS_ITEMSET ( &m_lastValidDraggedLine );
break;
}
case VIA: // fixme...
{
m_lastNode = m_shove->CurrentNode()->Branch();
dumbDragVia ( m_initialVia, m_lastNode, aP );
break;
}
}
if (Settings().CanViolateDRC())
m_dragStatus = true;
else
m_dragStatus = !m_world->CheckColliding( m_draggedItems );
return true;
}
void PNS_DRAGGER::dumbDragVia ( PNS_VIA *aVia, PNS_NODE *aNode, const VECTOR2I& aP )
{
// fixme: this is awful.
m_draggedVia = aVia->Clone();
m_draggedVia->SetPos( aP );
m_draggedItems.Clear();
m_draggedItems.Add(m_draggedVia);
m_lastNode->Remove ( aVia );
m_lastNode->Add ( m_draggedVia );
BOOST_FOREACH(PNS_LINE &l, m_origViaConnections)
{
PNS_LINE origLine (l);
PNS_LINE *draggedLine = l.Clone();
draggedLine->DragCorner( aP, 0 );
draggedLine->ClearSegmentLinks();
m_draggedItems.AddOwned( draggedLine );
m_lastNode->Remove ( &origLine );
m_lastNode->Add ( draggedLine );
}
}
bool PNS_DRAGGER::dragShove(const VECTOR2I& aP)
{
bool ok = false;
if(m_lastNode)
{
delete m_lastNode;
m_lastNode = NULL;
}
switch(m_mode)
{
case SEGMENT:
case CORNER:
{
int thresh = Settings().SmoothDraggedSegments() ? m_draggedLine->Width() / 4 : 0;
PNS_LINE tmp (*m_draggedLine);
if(m_mode == SEGMENT)
tmp.DragSegment ( aP, m_draggedSegmentIndex, thresh );
else
tmp.DragCorner ( aP, m_draggedSegmentIndex, thresh );
PNS_SHOVE::ShoveStatus st = m_shove->ShoveLines( tmp );
if(st == PNS_SHOVE::SH_OK)
ok = true;
else if (st == PNS_SHOVE::SH_HEAD_MODIFIED)
{
tmp = m_shove->NewHead();
ok = true;
}
m_lastNode = m_shove->CurrentNode()->Branch();
if(ok)
m_lastValidDraggedLine = tmp;
m_lastValidDraggedLine.ClearSegmentLinks();
m_lastValidDraggedLine.Unmark();
m_lastNode->Add ( &m_lastValidDraggedLine );
m_draggedItems = PNS_ITEMSET ( &m_lastValidDraggedLine );
break;
}
case VIA:
{
PNS_VIA *newVia;
PNS_SHOVE::ShoveStatus st = m_shove -> ShoveDraggingVia ( m_draggedVia, aP, &newVia );
if(st == PNS_SHOVE::SH_OK || st == PNS_SHOVE::SH_HEAD_MODIFIED)
ok = true;
m_lastNode = m_shove->CurrentNode()->Branch();
if( ok )
{
m_draggedVia = newVia;
m_draggedItems.Clear();
}
break;
}
}
m_dragStatus = ok;
return ok;
}
bool PNS_DRAGGER::FixRoute( )
{
if(m_dragStatus)
{
Router()->CommitRouting( CurrentNode() );
return true;
}
return false;
}
bool PNS_DRAGGER::Drag ( const VECTOR2I& aP )
{
switch ( m_currentMode )
{
case RM_MarkObstacles:
return dragMarkObstacles (aP);
case RM_Shove:
case RM_Walkaround:
case RM_Smart:
return dragShove ( aP );
default:
return false;
}
}
PNS_NODE *PNS_DRAGGER::CurrentNode() const
{
return m_lastNode;
}
const PNS_ITEMSET PNS_DRAGGER::Traces()
{
return m_draggedItems;
}
PNS_LOGGER *PNS_DRAGGER::Logger()
{
if(m_shove)
return m_shove->Logger();
return NULL;
}
/*
* 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_DRAGGER_H
#define __PNS_DRAGGER_H
#include <math/vector2d.h>
#include "pns_node.h"
#include "pns_via.h"
#include "pns_line.h"
#include "pns_algo_base.h"
#include "pns_itemset.h"
class PNS_ROUTER;
class PNS_SHOVE;
class PNS_OPTIMIZER;
class PNS_ROUTER_BASE;
/**
* Class PNS_DRAGGER
*
* Via, segment and corner dragging algorithm.
*/
class PNS_DRAGGER : public PNS_ALGO_BASE
{
public:
PNS_DRAGGER( PNS_ROUTER *aRouter );
~PNS_DRAGGER();
/**
* Function SetWorld()
*
* Sets the board to work on.
*/
void SetWorld ( PNS_NODE *aWorld );
/**
* Function Start()
*
* Starts routing a single track at point aP, taking item aStartItem as anchor
* (unless NULL). Returns true if a dragging operation has started.
*/
bool Start ( const VECTOR2I& aP, PNS_ITEM* aStartItem );
/**
* Function Drag()
*
* Drags the current segment/corner/via to the point aP.
* @return true, if dragging finished with success.
*/
bool Drag ( const VECTOR2I& aP );
/**
* Function FixRoute()
*
* Checks if the result of current dragging operation is correct
* and eventually commits it to the world.
* @return true, if dragging finished with success.
*/
bool FixRoute ( );
/**
* Function CurrentNode()
*
* Returns the most recent world state, including all
* items changed due to dragging operation.
*/
PNS_NODE* CurrentNode() const;
/**
* Function Traces()
*
* Returns the set of dragged items.
*/
const PNS_ITEMSET Traces();
/// @copydoc PNS_ALGO_BASE::Logger()
virtual PNS_LOGGER *Logger();
private:
typedef std::pair <PNS_LINE *, PNS_LINE *> LinePair;
typedef std::vector<LinePair> LinePairVec;
enum DragMode {
CORNER = 0,
SEGMENT,
VIA
};
bool dragMarkObstacles(const VECTOR2I& aP);
bool dragShove(const VECTOR2I& aP);
bool startDragSegment( const VECTOR2D& aP, PNS_SEGMENT *aSeg );
bool startDragVia( const VECTOR2D& aP, PNS_VIA *aVia );
void dumbDragVia ( PNS_VIA *aVia, PNS_NODE *aNode, const VECTOR2I& aP );
PNS_NODE * m_world;
PNS_NODE * m_lastNode;
DragMode m_mode;
PNS_LINE * m_draggedLine;
PNS_VIA * m_draggedVia;
PNS_LINE m_lastValidDraggedLine;
PNS_SHOVE * m_shove;
int m_draggedSegmentIndex;
bool m_dragStatus;
PNS_MODE m_currentMode;
std::vector<PNS_LINE> m_origViaConnections;
std::vector<PNS_LINE> m_draggedViaConnections;
PNS_VIA * m_initialVia;
PNS_ITEMSET m_draggedItems;
};
#endif
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,12 +15,15 @@ ...@@ -15,12 +15,15 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_INDEX_H #ifndef __PNS_INDEX_H
#define __PNS_INDEX_H #define __PNS_INDEX_H
#include <layers_id_colors_and_visibility.h>
#include <map>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/range/adaptor/map.hpp> #include <boost/range/adaptor/map.hpp>
...@@ -33,7 +36,7 @@ ...@@ -33,7 +36,7 @@
* Class PNS_INDEX * Class PNS_INDEX
* *
* Custom spatial index, holding our board items and allowing for very fast searches. Items * Custom spatial index, holding our board items and allowing for very fast searches. Items
* are assigned to separate R-Tree subundices depending on their type and spanned layers, reducing * are assigned to separate R-Tree subindices depending on their type and spanned layers, reducing
* overlap and improving search time. * overlap and improving search time.
**/ **/
...@@ -47,30 +50,93 @@ public: ...@@ -47,30 +50,93 @@ public:
PNS_INDEX(); PNS_INDEX();
~PNS_INDEX(); ~PNS_INDEX();
/**
* Function Add()
*
* Adds item to the spatial index.
*/
void Add( PNS_ITEM* aItem ); void Add( PNS_ITEM* aItem );
/**
* Function Remove()
*
* Removes an item from the spatial index.
*/
void Remove( PNS_ITEM* aItem ); void Remove( PNS_ITEM* aItem );
/**
* Function Add()
*
* Replaces one item with another.
*/
void Replace( PNS_ITEM* aOldItem, PNS_ITEM* aNewItem ); void Replace( PNS_ITEM* aOldItem, PNS_ITEM* aNewItem );
/**
* Function Query()
*
* Searches items in the index that are in proximity of aItem.
* For each item, function object aVisitor is called. Only items on
* overlapping layers are considered.
*
* @param aItem item to search against
* @param aMinDistance proximity distance (wrs to the item's shape)
* @param aVisitor function object called on each found item. Return
false from the visitor to stop searching.
* @return number of items found.
*/
template<class Visitor> template<class Visitor>
int Query( const PNS_ITEM* aItem, int aMinDistance, Visitor& v ); int Query( const PNS_ITEM* aItem, int aMinDistance, Visitor& aVisitor );
/**
* Function Query()
*
* Searches items in the index that are in proximity of aShape.
* For each item, function object aVisitor is called. Treats all
* layers as colliding.
*
* @param aShape shape to search against
* @param aMinDistance proximity distance (wrs to the item's shape)
* @param aVisitor function object called on each found item. Return
false from the visitor to stop searching.
* @return number of items found.
*/
template<class Visitor> template<class Visitor>
int Query( const SHAPE* aShape, int aMinDistance, Visitor& v ); int Query( const SHAPE* aShape, int aMinDistance, Visitor& aVisitor );
/**
* Function Clear()
*
* Removes all items from the index.
*/
void Clear(); void Clear();
/**
* Function GetItemsForNet()
*
* Returns list of all items in a given net.
*/
NetItemsList* GetItemsForNet( int aNet ); NetItemsList* GetItemsForNet( int aNet );
ItemSet::iterator begin() { return m_allItems.begin(); } /**
ItemSet::iterator end() { return m_allItems.end(); } * Function Contains()
*
* Returns true if item aItem exists in the index.
*/
bool Contains( PNS_ITEM* aItem ) const bool Contains( PNS_ITEM* aItem ) const
{ {
return m_allItems.find( aItem ) != m_allItems.end(); return m_allItems.find( aItem ) != m_allItems.end();
} }
/**
* Function Size()
*
* Returns number of items stored in the index.
*/
int Size() const { return m_allItems.size(); } int Size() const { return m_allItems.size(); }
ItemSet::iterator begin() { return m_allItems.begin(); }
ItemSet::iterator end() { return m_allItems.end(); }
private: private:
static const int MaxSubIndices = 64; static const int MaxSubIndices = 64;
static const int SI_Multilayer = 2; static const int SI_Multilayer = 2;
...@@ -81,7 +147,7 @@ private: ...@@ -81,7 +147,7 @@ private:
static const int SI_PadsBottom = 1; static const int SI_PadsBottom = 1;
template <class Visitor> template <class Visitor>
int querySingle( int index, const SHAPE* aShape, int aMinDistance, Visitor& v ); int querySingle( int index, const SHAPE* aShape, int aMinDistance, Visitor& aVisitor );
ItemShapeIndex* getSubindex( const PNS_ITEM* aItem ); ItemShapeIndex* getSubindex( const PNS_ITEM* aItem );
...@@ -101,9 +167,9 @@ PNS_INDEX::ItemShapeIndex* PNS_INDEX::getSubindex( const PNS_ITEM* aItem ) ...@@ -101,9 +167,9 @@ PNS_INDEX::ItemShapeIndex* PNS_INDEX::getSubindex( const PNS_ITEM* aItem )
{ {
int idx_n = -1; int idx_n = -1;
const PNS_LAYERSET l = aItem->GetLayers(); const PNS_LAYERSET l = aItem->Layers();
switch( aItem->GetKind() ) switch( aItem->Kind() )
{ {
case PNS_ITEM::VIA: case PNS_ITEM::VIA:
idx_n = SI_Multilayer; idx_n = SI_Multilayer;
...@@ -113,9 +179,9 @@ PNS_INDEX::ItemShapeIndex* PNS_INDEX::getSubindex( const PNS_ITEM* aItem ) ...@@ -113,9 +179,9 @@ PNS_INDEX::ItemShapeIndex* PNS_INDEX::getSubindex( const PNS_ITEM* aItem )
{ {
if( l.IsMultilayer() ) if( l.IsMultilayer() )
idx_n = SI_Multilayer; idx_n = SI_Multilayer;
else if( l.Start() == 0 ) // fixme: use kicad layer codes else if( l.Start() == LAYER_N_BACK) // fixme: use kicad layer codes
idx_n = SI_PadsTop; idx_n = SI_PadsTop;
else if( l.Start() == 15 ) else if( l.Start() == LAYER_N_FRONT )
idx_n = SI_PadsBottom; idx_n = SI_PadsBottom;
break; break;
...@@ -143,9 +209,11 @@ void PNS_INDEX::Add( PNS_ITEM* aItem ) ...@@ -143,9 +209,11 @@ void PNS_INDEX::Add( PNS_ITEM* aItem )
{ {
ItemShapeIndex* idx = getSubindex( aItem ); ItemShapeIndex* idx = getSubindex( aItem );
idx->Add( aItem ); idx->Add( aItem );
m_allItems.insert( aItem ); m_allItems.insert( aItem );
int net = aItem->GetNet(); int net = aItem->Net();
if( net >= 0 ) if( net >= 0 )
{ {
...@@ -161,7 +229,7 @@ void PNS_INDEX::Remove( PNS_ITEM* aItem ) ...@@ -161,7 +229,7 @@ void PNS_INDEX::Remove( PNS_ITEM* aItem )
idx->Remove( aItem ); idx->Remove( aItem );
m_allItems.erase( aItem ); m_allItems.erase( aItem );
int net = aItem->GetNet(); int net = aItem->Net();
if( net >= 0 && m_netMap.find( net ) != m_netMap.end() ) if( net >= 0 && m_netMap.find( net ) != m_netMap.end() )
m_netMap[net].remove( aItem ); m_netMap[net].remove( aItem );
...@@ -176,43 +244,43 @@ void PNS_INDEX::Replace( PNS_ITEM* aOldItem, PNS_ITEM* aNewItem ) ...@@ -176,43 +244,43 @@ void PNS_INDEX::Replace( PNS_ITEM* aOldItem, PNS_ITEM* aNewItem )
template<class Visitor> template<class Visitor>
int PNS_INDEX::querySingle( int index, const SHAPE* aShape, int aMinDistance, Visitor& v ) int PNS_INDEX::querySingle( int index, const SHAPE* aShape, int aMinDistance, Visitor& aVisitor )
{ {
if( !m_subIndices[index] ) if( !m_subIndices[index] )
return 0; return 0;
return m_subIndices[index]->Query( aShape, aMinDistance, v, false ); return m_subIndices[index]->Query( aShape, aMinDistance, aVisitor, false );
} }
template<class Visitor> template<class Visitor>
int PNS_INDEX::Query( const PNS_ITEM* aItem, int aMinDistance, Visitor& v ) int PNS_INDEX::Query( const PNS_ITEM* aItem, int aMinDistance, Visitor& aVisitor )
{ {
const SHAPE* shape = aItem->GetShape(); const SHAPE* shape = aItem->Shape();
int total = 0; int total = 0;
total += querySingle( SI_Multilayer, shape, aMinDistance, v ); total += querySingle( SI_Multilayer, shape, aMinDistance, aVisitor );
const PNS_LAYERSET layers = aItem->GetLayers(); const PNS_LAYERSET layers = aItem->Layers();
if( layers.IsMultilayer() ) if( layers.IsMultilayer() )
{ {
total += querySingle( SI_PadsTop, shape, aMinDistance, v ); total += querySingle( SI_PadsTop, shape, aMinDistance, aVisitor );
total += querySingle( SI_PadsBottom, shape, aMinDistance, v ); total += querySingle( SI_PadsBottom, shape, aMinDistance, aVisitor );
for( int i = layers.Start(); i <= layers.End(); ++i ) for( int i = layers.Start(); i <= layers.End(); ++i )
total += querySingle( SI_Traces + 2 * i + SI_SegStraight, shape, aMinDistance, v ); total += querySingle( SI_Traces + 2 * i + SI_SegStraight, shape, aMinDistance, aVisitor );
} }
else else
{ {
int l = layers.Start(); int l = layers.Start();
if( l == 0 ) if( l == LAYER_N_BACK )
total += querySingle( SI_PadsTop, shape, aMinDistance, v ); total += querySingle( SI_PadsTop, shape, aMinDistance, aVisitor );
else if( l == 15 ) else if( l == LAYER_N_FRONT )
total += querySingle( SI_PadsBottom, shape, aMinDistance, v ); total += querySingle( SI_PadsBottom, shape, aMinDistance, aVisitor );
total += querySingle( SI_Traces + 2 * l + SI_SegStraight, shape, aMinDistance, v ); total += querySingle( SI_Traces + 2 * l + SI_SegStraight, shape, aMinDistance, aVisitor );
} }
return total; return total;
...@@ -220,12 +288,12 @@ int PNS_INDEX::Query( const PNS_ITEM* aItem, int aMinDistance, Visitor& v ) ...@@ -220,12 +288,12 @@ int PNS_INDEX::Query( const PNS_ITEM* aItem, int aMinDistance, Visitor& v )
template<class Visitor> template<class Visitor>
int PNS_INDEX::Query( const SHAPE* aShape, int aMinDistance, Visitor& v ) int PNS_INDEX::Query( const SHAPE* aShape, int aMinDistance, Visitor& aVisitor )
{ {
int total = 0; int total = 0;
for( int i = 0; i < MaxSubIndices; i++ ) for( int i = 0; i < MaxSubIndices; i++ )
total += querySingle( i, aShape, aMinDistance, v ); total += querySingle( i, aShape, aMinDistance, aVisitor );
return total; return total;
} }
......
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "pns_item.h" #include "pns_item.h"
...@@ -32,7 +32,7 @@ bool PNS_ITEM::collideSimple( const PNS_ITEM* aOther, int aClearance, bool aNeed ...@@ -32,7 +32,7 @@ bool PNS_ITEM::collideSimple( const PNS_ITEM* aOther, int aClearance, bool aNeed
if( !m_layers.Overlaps( aOther->m_layers ) ) if( !m_layers.Overlaps( aOther->m_layers ) )
return false; return false;
return GetShape()->Collide( aOther->GetShape(), aClearance ); return Shape()->Collide( aOther->Shape(), aClearance );
// fixme: MTV // fixme: MTV
} }
...@@ -50,7 +50,7 @@ bool PNS_ITEM::Collide( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV, ...@@ -50,7 +50,7 @@ bool PNS_ITEM::Collide( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV,
const PNS_LINE* line = static_cast<const PNS_LINE*>( aOther ); const PNS_LINE* line = static_cast<const PNS_LINE*>( aOther );
if( line->EndsWithVia() ) if( line->EndsWithVia() )
return collideSimple( &line->GetVia(), aClearance - line->GetWidth() / 2, aNeedMTV, return collideSimple( &line->Via(), aClearance - line->Width() / 2, aNeedMTV,
aMTV ); aMTV );
} }
...@@ -58,7 +58,7 @@ bool PNS_ITEM::Collide( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV, ...@@ -58,7 +58,7 @@ bool PNS_ITEM::Collide( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV,
} }
const std::string PNS_ITEM::GetKindStr() const const std::string PNS_ITEM::KindStr() const
{ {
switch( m_kind ) switch( m_kind )
{ {
...@@ -85,4 +85,5 @@ const std::string PNS_ITEM::GetKindStr() const ...@@ -85,4 +85,5 @@ const std::string PNS_ITEM::GetKindStr() const
PNS_ITEM::~PNS_ITEM() PNS_ITEM::~PNS_ITEM()
{ {
} }
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_ITEM_H #ifndef __PNS_ITEM_H
...@@ -26,18 +26,25 @@ ...@@ -26,18 +26,25 @@
#include <geometry/shape.h> #include <geometry/shape.h>
#include <geometry/shape_line_chain.h> #include <geometry/shape_line_chain.h>
#include "trace.h"
#include "pns_layerset.h" #include "pns_layerset.h"
class BOARD_CONNECTED_ITEM; class BOARD_CONNECTED_ITEM;
class PNS_NODE; class PNS_NODE;
enum LineMarker {
MK_HEAD = ( 1 << 0 ),
MK_VIOLATION = ( 1 << 3 ),
MK_LOCKED = ( 1 << 4 )
};
/** /**
* Class PNS_ITEM * Class PNS_ITEM
* *
* Base class for PNS router board items. Implements the shared properties of all PCB items - * Base class for PNS router board items. Implements the shared properties of all PCB items -
* net, spanned layers, geometric shape & refererence to owning model. * net, spanned layers, geometric shape & refererence to owning model.
*/ */
class PNS_ITEM class PNS_ITEM
{ {
public: public:
...@@ -60,8 +67,9 @@ public: ...@@ -60,8 +67,9 @@ public:
m_movable = true; m_movable = true;
m_kind = aKind; m_kind = aKind;
m_parent = NULL; m_parent = NULL;
m_world = NULL;
m_owner = NULL; m_owner = NULL;
m_marker = 0;
m_rank = -1;
} }
PNS_ITEM( const PNS_ITEM& aOther ) PNS_ITEM( const PNS_ITEM& aOther )
...@@ -70,61 +78,203 @@ public: ...@@ -70,61 +78,203 @@ public:
m_net = aOther.m_net; m_net = aOther.m_net;
m_movable = aOther.m_movable; m_movable = aOther.m_movable;
m_kind = aOther.m_kind; m_kind = aOther.m_kind;
m_world = aOther.m_world;
m_parent = aOther.m_parent; m_parent = aOther.m_parent;
m_owner = NULL; m_owner = NULL;
m_marker = aOther.m_marker;
m_rank = aOther.m_rank;
} }
virtual ~PNS_ITEM(); virtual ~PNS_ITEM();
virtual PNS_ITEM* Clone() const = 0; /**
* Function Clone()
*
* Returns a deep copy of the item
*/
virtual PNS_ITEM* Clone( ) const = 0;
///> Returns a convex polygon "hull" of a the item, that is used as the walkaround /*
/// path. * Function Hull()
/// aClearance defines how far from the body of the item the hull should be, *
/// aWalkaroundThickness is the width of the line that walks around this hull. * Returns a convex polygon "hull" of a the item, that is used as the walk-around
* path.
* @param aClearance defines how far from the body of the item the hull should be,
* @param aWalkaroundThickness is the width of the line that walks around this hull.
*/
virtual const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0 ) const virtual const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0 ) const
{ {
return SHAPE_LINE_CHAIN(); return SHAPE_LINE_CHAIN();
}; };
PnsKind GetKind() const { return m_kind; } /**
bool OfKind( int aKind ) const { return (aKind & m_kind) != 0; } * Function Kind()
*
* Returns the type (kind) of the item
*/
PnsKind Kind() const
{
return m_kind;
}
/**
* Function OfKind()
*
* Returns true if the item's type matches the mask aKindMask.
*/
bool OfKind( int aKindMask ) const
{
return (aKindMask & m_kind) != 0;
}
const std::string GetKindStr() const; /**
* Function KindStr()
*
* Returns the kind of the item, as string
*/
const std::string KindStr() const;
///> Gets/Sets the corresponding parent object in the host application's model (pcbnew) /**
void SetParent( BOARD_CONNECTED_ITEM* aParent ) { m_parent = aParent; } * Function SetParent()
BOARD_CONNECTED_ITEM* GetParent() const { return m_parent; } *
* Sets the corresponding parent object in the host application's model.
*/
void SetParent( BOARD_CONNECTED_ITEM* aParent )
{
m_parent = aParent;
}
/**
* Function Parent()
*
* Returns the corresponding parent object in the host application's model.
*/
BOARD_CONNECTED_ITEM* Parent() const
{
return m_parent;
}
///> Net accessors /**
int GetNet() const { return m_net; } * Function SetNet()
void SetNet( int aNet ) { m_net = aNet; } *
* Sets the item's net to aNet
*/
void SetNet( int aNet )
{
m_net = aNet;
}
///> Layers accessors /**
const PNS_LAYERSET& GetLayers() const { return m_layers; } * Function Net()
void SetLayers( const PNS_LAYERSET& aLayers ) { m_layers = aLayers; } *
* Returns the item's net.
*/
int Net() const
{
return m_net;
}
/**
* Function SetLayers()
*
* Sets the layers spanned by the item to aLayers.
*/
void SetLayers( const PNS_LAYERSET& aLayers )
{
m_layers = aLayers;
}
/**
* Function SetLayer()
*
* Sets the layers spanned by the item to a single layer aLayer.
*/
void SetLayer( int aLayer ) void SetLayer( int aLayer )
{ {
m_layers = PNS_LAYERSET( aLayer, aLayer ); m_layers = PNS_LAYERSET( aLayer, aLayer );
} }
///> Ownership management. An item can belong to a single PNS_NODE or stay unowned. /**
void SetOwner( PNS_NODE* aOwner ) { m_owner = aOwner; } * Function Layers()
bool BelongsTo( PNS_NODE* aNode ) const { return m_owner == aNode; } *
PNS_NODE* GetOwner() const { return m_owner; } * Returns the contiguous set of layers spanned by the item.
*/
const PNS_LAYERSET& Layers() const
{
return m_layers;
}
/**
* Function Layer()
*
* Returns the item's layer, for single-layered items only.
*/
virtual int Layer() const
{
return Layers().Start();
}
/**
* Function LayersOverlap()
*
* Returns true if the set of layers spanned by aOther overlaps our
* layers.
*/
bool LayersOverlap( const PNS_ITEM *aOther ) const
{
return Layers().Overlaps( aOther->Layers() );
}
///> Sets the world that is used for collision resolution. /**
void SetWorld( PNS_NODE* aWorld ) { m_world = aWorld; } * Functon SetOwner()
PNS_NODE* GetWorld() const { return m_world; } *
* Sets the node that owns this item. An item can belong to a single
* PNS_NODE or stay unowned.
*/
void SetOwner( PNS_NODE* aOwner )
{
m_owner = aOwner;
}
/**
* Function BelongsTo()
*
* Returns true if the item is owned by the node aNode.
*/
bool BelongsTo( PNS_NODE* aNode ) const
{
return m_owner == aNode;
}
/**
* Function Owner()
*
* Returns the owner of this item, or NULL if there's none.
*/
PNS_NODE* Owner() const { return m_owner; }
///> Collision function. Checks if the item aOther is closer to us than
/// aClearance and returns true if so. It can also calculate a minimum translation vector that /**
/// resolves the collision if needed. * Function Collide()
*
* Checks for a collision (clearance violation) with between us and item aOther.
* Collision checking takes all PCB stuff into accound (layers, nets, DRC rules).
* Optionally returns a minimum translation vector for force propagation
* algorithm.
*
* @param aOther item to check collision against
* @param aClearance desired clearance
* @param aNeedMTV when true, the minimum translation vector is calculated
* @param aMTV the minimum translation vector
* @param true, if a collision was found.
*/
virtual bool Collide( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV, virtual bool Collide( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV,
VECTOR2I& aMTV ) const; VECTOR2I& aMTV ) const;
///> A shortcut without MTV calculation /**
* Function Collide()
*
* A shortcut for PNS_ITEM::Colllide() without MTV stuff.
*/
bool Collide( const PNS_ITEM* aOther, int aClearance ) const bool Collide( const PNS_ITEM* aOther, int aClearance ) const
{ {
VECTOR2I dummy; VECTOR2I dummy;
...@@ -132,26 +282,68 @@ public: ...@@ -132,26 +282,68 @@ public:
return Collide( aOther, aClearance, false, dummy ); return Collide( aOther, aClearance, false, dummy );
} }
///> Returns the geometric shape of the item /**
virtual const SHAPE* GetShape() const * Function Shape()
*
* Returns the geometrical shape of the item. Used
* for collision detection & spatial indexing.
*/
virtual const SHAPE* Shape() const
{ {
return NULL; return NULL;
} }
virtual void Mark(int aMarker)
{
m_marker = aMarker;
}
virtual void Unmark ()
{
m_marker = 0;
}
virtual int Marker() const
{
return m_marker;
}
virtual void SetRank ( int aRank )
{
m_rank = aRank;
}
virtual int Rank() const
{
return m_rank;
}
virtual VECTOR2I Anchor(int n) const
{
return VECTOR2I ();
};
virtual int AnchorCount() const
{
return 0;
}
private: private:
bool collideSimple( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV, bool collideSimple( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV,
VECTOR2I& aMTV ) const; VECTOR2I& aMTV ) const;
protected: protected:
PnsKind m_kind; PnsKind m_kind;
BOARD_CONNECTED_ITEM* m_parent; BOARD_CONNECTED_ITEM *m_parent;
PNS_NODE* m_world; PNS_NODE *m_owner;
PNS_NODE* m_owner; PNS_LAYERSET m_layers;
PNS_LAYERSET m_layers;
bool m_movable; bool m_movable;
int m_net; int m_net;
int m_marker;
int m_rank;
}; };
#endif // __PNS_ITEM_H #endif // __PNS_ITEM_H
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
...@@ -23,16 +23,30 @@ ...@@ -23,16 +23,30 @@
#include "pns_itemset.h" #include "pns_itemset.h"
PNS_ITEMSET::PNS_ITEMSET() PNS_ITEMSET::PNS_ITEMSET( PNS_ITEM *aInitialItem )
{ {
if(aInitialItem)
m_items.push_back(aInitialItem);
} }
PNS_ITEMSET::~PNS_ITEMSET() PNS_ITEMSET::~PNS_ITEMSET()
{ {
Clear();
} }
void PNS_ITEMSET::Clear()
{
BOOST_FOREACH(PNS_ITEM *item, m_ownedItems)
{
delete item;
}
m_items.clear();
m_ownedItems.clear();
}
PNS_ITEMSET& PNS_ITEMSET::FilterLayers( int aStart, int aEnd ) PNS_ITEMSET& PNS_ITEMSET::FilterLayers( int aStart, int aEnd )
{ {
ItemVector newItems; ItemVector newItems;
...@@ -45,7 +59,7 @@ PNS_ITEMSET& PNS_ITEMSET::FilterLayers( int aStart, int aEnd ) ...@@ -45,7 +59,7 @@ PNS_ITEMSET& PNS_ITEMSET::FilterLayers( int aStart, int aEnd )
BOOST_FOREACH( PNS_ITEM * item, m_items ) BOOST_FOREACH( PNS_ITEM * item, m_items )
if( item->GetLayers().Overlaps( l ) ) if( item->Layers().Overlaps( l ) )
newItems.push_back( item ); newItems.push_back( item );
m_items = newItems; m_items = newItems;
...@@ -59,7 +73,7 @@ PNS_ITEMSET& PNS_ITEMSET::FilterKinds( int aKindMask ) ...@@ -59,7 +73,7 @@ PNS_ITEMSET& PNS_ITEMSET::FilterKinds( int aKindMask )
BOOST_FOREACH( PNS_ITEM * item, m_items ) BOOST_FOREACH( PNS_ITEM * item, m_items )
if( item->GetKind() & aKindMask ) if( item->OfKind ( aKindMask ) )
newItems.push_back( item ); newItems.push_back( item );
m_items = newItems; m_items = newItems;
...@@ -73,7 +87,7 @@ PNS_ITEMSET& PNS_ITEMSET::FilterNet( int aNet ) ...@@ -73,7 +87,7 @@ PNS_ITEMSET& PNS_ITEMSET::FilterNet( int aNet )
BOOST_FOREACH( PNS_ITEM * item, m_items ) BOOST_FOREACH( PNS_ITEM * item, m_items )
if( item->GetNet() == aNet ) if( item->Net() == aNet )
newItems.push_back( item ); newItems.push_back( item );
m_items = newItems; m_items = newItems;
......
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_ITEMSET_H #ifndef __PNS_ITEMSET_H
...@@ -37,10 +37,25 @@ class PNS_ITEMSET ...@@ -37,10 +37,25 @@ class PNS_ITEMSET
public: public:
typedef std::vector<PNS_ITEM*> ItemVector; typedef std::vector<PNS_ITEM*> ItemVector;
PNS_ITEMSET(); PNS_ITEMSET( PNS_ITEM *aInitialItem = NULL );
PNS_ITEMSET (const PNS_ITEMSET &aOther )
{
m_items = aOther.m_items;
m_ownedItems = ItemVector();
}
const PNS_ITEMSET& operator= (const PNS_ITEMSET &aOther)
{
m_items = aOther.m_items;
m_ownedItems = ItemVector();
return *this;
}
~PNS_ITEMSET(); ~PNS_ITEMSET();
ItemVector& Items() { return m_items; } ItemVector& Items() { return m_items; }
const ItemVector& CItems() const { return m_items; }
PNS_ITEMSET& FilterLayers( int aStart, int aEnd = -1 ); PNS_ITEMSET& FilterLayers( int aStart, int aEnd = -1 );
PNS_ITEMSET& FilterKinds( int aKindMask ); PNS_ITEMSET& FilterKinds( int aKindMask );
...@@ -55,8 +70,17 @@ public: ...@@ -55,8 +70,17 @@ public:
PNS_ITEM* Get( int index ) const { return m_items[index]; } PNS_ITEM* Get( int index ) const { return m_items[index]; }
void Clear();
void AddOwned ( PNS_ITEM *aItem )
{
m_items.push_back( aItem );
m_ownedItems.push_back( aItem );
}
private: private:
ItemVector m_items; ItemVector m_items;
ItemVector m_ownedItems;
}; };
#endif #endif
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_JOINT_H #ifndef __PNS_JOINT_H
...@@ -72,7 +72,7 @@ public: ...@@ -72,7 +72,7 @@ public:
m_layers = b.m_layers; m_layers = b.m_layers;
} }
PNS_ITEM* Clone() const PNS_ITEM* Clone ( ) const
{ {
assert( false ); assert( false );
return NULL; return NULL;
...@@ -85,15 +85,15 @@ public: ...@@ -85,15 +85,15 @@ public:
if( m_linkedItems.size() != 2 ) if( m_linkedItems.size() != 2 )
return false; return false;
if( m_linkedItems[0]->GetKind() != SEGMENT || if( m_linkedItems[0]->Kind() != SEGMENT ||
m_linkedItems[1]->GetKind() != SEGMENT ) m_linkedItems[1]->Kind() != SEGMENT )
return false; return false;
PNS_SEGMENT* seg1 = static_cast<PNS_SEGMENT*>( m_linkedItems[0] ); PNS_SEGMENT* seg1 = static_cast<PNS_SEGMENT*>( m_linkedItems[0] );
PNS_SEGMENT* seg2 = static_cast<PNS_SEGMENT*>( m_linkedItems[1] ); PNS_SEGMENT* seg2 = static_cast<PNS_SEGMENT*>( m_linkedItems[1] );
// joints between segments of different widths are not trivial. // joints between segments of different widths are not considered trivial.
return seg1->GetWidth() == seg2->GetWidth(); return seg1->Width() == seg2->Width();
} }
///> 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)
...@@ -131,11 +131,35 @@ public: ...@@ -131,11 +131,35 @@ public:
return static_cast<PNS_SEGMENT*>( m_linkedItems[m_linkedItems[0] == aCurrent ? 1 : 0] ); return static_cast<PNS_SEGMENT*>( m_linkedItems[m_linkedItems[0] == aCurrent ? 1 : 0] );
} }
PNS_VIA *Via()
{
for( LinkedItems::iterator i = m_linkedItems.begin();
i != m_linkedItems.end(); ++i )
if( (*i)->Kind() == PNS_ITEM::VIA )
return (PNS_VIA *)(*i);
return NULL;
}
/// trivial accessors /// trivial accessors
const HashTag& GetTag() const { return m_tag; } const HashTag& Tag() const
const VECTOR2I& GetPos() const { return m_tag.pos; } {
int GetNet() const { return m_tag.net; } return m_tag;
LinkedItems& GetLinkList() { return m_linkedItems; }; }
const VECTOR2I& Pos() const
{
return m_tag.pos;
}
int Net() const
{
return m_tag.net;
}
LinkedItems& LinkList()
{
return m_linkedItems;
}
///> Returns the number of linked items of types listed in aMask. ///> Returns the number of linked items of types listed in aMask.
int LinkCount( int aMask = -1 ) const int LinkCount( int aMask = -1 ) const
...@@ -144,7 +168,7 @@ public: ...@@ -144,7 +168,7 @@ public:
for( LinkedItems::const_iterator i = m_linkedItems.begin(); for( LinkedItems::const_iterator i = m_linkedItems.begin();
i != m_linkedItems.end(); ++i ) i != m_linkedItems.end(); ++i )
if( (*i)->GetKind() & aMask ) if( (*i)->Kind() & aMask )
n++; n++;
return n; return n;
......
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_LAYERSET_H #ifndef __PNS_LAYERSET_H
......
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
...@@ -29,718 +29,699 @@ ...@@ -29,718 +29,699 @@
#include "pns_utils.h" #include "pns_utils.h"
#include "pns_router.h" #include "pns_router.h"
#include <geometry/shape_rect.h>
using boost::optional; using boost::optional;
PNS_LINE* PNS_LINE::Clone() const PNS_LINE::PNS_LINE( const PNS_LINE& aOther ) :
PNS_ITEM( aOther ),
m_line( aOther.m_line ),
m_width( aOther.m_width )
{
m_net = aOther.m_net;
m_movable = aOther.m_movable;
m_layers = aOther.m_layers;
m_owner = aOther.m_owner;
m_via = aOther.m_via;
m_hasVia = aOther.m_hasVia;
m_marker = aOther.m_marker;
m_rank = aOther.m_rank;
copyLinks ( &aOther );
}
PNS_LINE::~PNS_LINE()
{ {
PNS_LINE* l = new PNS_LINE(); if( m_segmentRefs )
delete m_segmentRefs;
};
l->m_line = m_line;
l->m_width = m_width;
l->m_layers = m_layers;
l->m_net = m_net;
l->m_movable = m_movable;
l->m_segmentRefs = NULL;
l->m_hasVia = m_hasVia;
l->m_via = m_via;
const PNS_LINE& PNS_LINE :: operator= (const PNS_LINE& aOther)
{
m_line = aOther.m_line;
m_width = aOther.m_width;
m_net = aOther.m_net;
m_movable = aOther.m_movable;
m_owner = aOther.m_owner;
m_layers = aOther.m_layers;
m_via = aOther.m_via;
m_hasVia = aOther.m_hasVia;
m_marker = aOther.m_marker;
m_rank = aOther.m_rank;
copyLinks ( &aOther );
return *this;
}
PNS_LINE* PNS_LINE::Clone( ) const
{
PNS_LINE* l = new PNS_LINE( *this );
return l; return l;
} }
void PNS_LINE::Mark(int aMarker)
{
m_marker = aMarker;
if(m_segmentRefs)
{
BOOST_FOREACH( PNS_SEGMENT *s, *m_segmentRefs )
s->Mark(aMarker);
}
}
PNS_LINE* PNS_LINE::CloneProperties() const void PNS_LINE::Unmark ()
{ {
PNS_LINE* l = new PNS_LINE(); if(m_segmentRefs)
{
BOOST_FOREACH( PNS_SEGMENT *s, *m_segmentRefs )
s->Unmark();
}
m_marker = 0;
}
l->m_width = m_width; int PNS_LINE::Marker()const
l->m_layers = m_layers; {
l->m_net = m_net; int marker = m_marker;
l->m_movable = m_movable; if(m_segmentRefs)
{
BOOST_FOREACH( PNS_SEGMENT *s, *m_segmentRefs )
marker |= s->Marker();
}
return marker;
}
return l; void PNS_LINE::copyLinks( const PNS_LINE *aParent )
{
if(aParent->m_segmentRefs == NULL)
{
m_segmentRefs = NULL;
return;
}
m_segmentRefs = new SegmentRefs();
*m_segmentRefs = *aParent->m_segmentRefs;
} }
PNS_SEGMENT* PNS_SEGMENT::Clone() const PNS_SEGMENT* PNS_SEGMENT::Clone( ) const
{ {
PNS_SEGMENT* s = new PNS_SEGMENT; PNS_SEGMENT* s = new PNS_SEGMENT;
s->m_width = m_width; s->m_seg = m_seg;
s->m_net = m_net; s->m_net = m_net;
s->m_shape = m_shape;
s->m_layers = m_layers; s->m_layers = m_layers;
s->m_marker = m_marker;
s->m_rank = m_rank;
s->m_owner = m_owner;
return s; // assert(false); return s;
} }
int PNS_LINE::CountCorners( int aAngles )
#if 1
bool PNS_LINE::MergeObtuseSegments()
{ {
int step = m_line.PointCount() - 3; int count = 0;
int iter = 0;
int segs_pre = m_line.SegmentCount(); for( int i = 0; i < m_line.SegmentCount() - 1; i++ )
{
const SEG seg1 = m_line.CSegment( i );
const SEG seg2 = m_line.CSegment( i + 1 );
if( step < 0 ) const DIRECTION_45 dir1( seg1 );
return false; const DIRECTION_45 dir2( seg2 );
SHAPE_LINE_CHAIN current_path( m_line ); DIRECTION_45::AngleType a = dir1.Angle( dir2 );
while( 1 ) if( a & aAngles )
{ count++;
iter++; }
int n_segs = current_path.SegmentCount();
int max_step = n_segs - 2;
if( step > max_step ) return count;
step = max_step; }
if( step < 2 ) bool PNS_LINE::Walkaround( SHAPE_LINE_CHAIN aObstacle, SHAPE_LINE_CHAIN& aPre,
{ SHAPE_LINE_CHAIN& aWalk, SHAPE_LINE_CHAIN& aPost, bool aCw ) const
m_line = current_path; {
return current_path.SegmentCount() < segs_pre; const SHAPE_LINE_CHAIN& line ( CLine() );
} VECTOR2I ip_start;
VECTOR2I ip_end;
bool found_anything = false; if( line.SegmentCount() < 1 )
int n = 0; return false;
while( n < n_segs - step ) if( aObstacle.PointInside( line.CPoint( 0 ) ) ||
{ aObstacle.PointInside( line.CPoint( -1 ) ) )
const SEG s1 = current_path.CSegment( n ); return false;
const SEG s2 = current_path.CSegment( n + step );
SEG s1opt, s2opt;
if( DIRECTION_45( s1 ).IsObtuse( DIRECTION_45( s2 ) ) ) SHAPE_LINE_CHAIN::INTERSECTIONS ips, ips2;
{
VECTOR2I ip = *s1.IntersectLines( s2 );
if( s1.Distance( ip ) <= 1 || s2.Distance( ip ) <= 1 ) line.Intersect(aObstacle, ips);
{
s1opt = SEG( s1.A, ip );
s2opt = SEG( ip, s2.B );
}
else
{
s1opt = SEG( s1.A, ip );
s2opt = SEG( ip, s2.B );
}
int nearest_dist = INT_MAX;
int farthest_dist = 0;
if( DIRECTION_45( s1opt ).IsObtuse( DIRECTION_45( s2opt ) ) ) SHAPE_LINE_CHAIN::INTERSECTION nearest, farthest;
{
SHAPE_LINE_CHAIN opt_path;
opt_path.Append( s1opt.A );
opt_path.Append( s1opt.B );
opt_path.Append( s2opt.B );
PNS_LINE opt_track( *this, opt_path );
if( !m_world->CheckColliding( &opt_track, PNS_ITEM::ANY ) )
{
current_path.Replace( s1.Index() + 1, s2.Index(), ip );
n_segs = current_path.SegmentCount();
found_anything = true;
break;
}
}
}
n++; for(int i = 0; i < (int) ips.size(); i++)
{
const VECTOR2I p = ips[i].p;
int dist = line.PathLength(p);
if(dist <= nearest_dist)
{
nearest_dist = dist;
nearest = ips[i];
} }
if( !found_anything ) if(dist >= farthest_dist)
{ {
if( step <= 2 ) farthest_dist = dist;
{ farthest = ips[i];
m_line = current_path;
return m_line.SegmentCount() < segs_pre;
}
step--;
} }
} }
return m_line.SegmentCount() < segs_pre; if(ips.size() <= 1 || nearest.p == farthest.p)
} {
aPre = line;
return true;
bool PNS_LINE::MergeSegments() }
{
int step = m_line.PointCount() - 3;
int iter = 0;
int segs_pre = m_line.SegmentCount();
if( step < 0 ) aPre = line.Slice( 0, nearest.our.Index() );
return false; aPre.Append( nearest.p );
aPre.Simplify();
aWalk.Clear();
aWalk.SetClosed(false);
aWalk.Append( nearest.p );
SHAPE_LINE_CHAIN current_path( m_line ); int i = nearest.their.Index();
while( 1 ) assert ( nearest.their.Index() >= 0 );
{ assert ( farthest.their.Index() >= 0 );
iter++;
int n_segs = current_path.SegmentCount();
int max_step = n_segs - 2;
if( step > max_step ) assert( nearest_dist <= farthest_dist );
step = max_step;
if( step < 2 ) aObstacle.Split( nearest.p );
{ aObstacle.Split( farthest.p );
m_line = current_path;
return current_path.SegmentCount() < segs_pre; int i_first = aObstacle.Find( nearest.p );
} int i_last = aObstacle.Find( farthest.p );
bool found_anything = false; i = i_first;
int n = 0;
while( n < n_segs - step ) while (i != i_last)
{ {
const SEG s1 = current_path.CSegment( n ); aWalk.Append(aObstacle.CPoint(i));
const SEG s2 = current_path.CSegment( n + step ); i += (aCw ? 1 : -1);
SEG s1opt, s2opt;
if (i < 0)
i = aObstacle.PointCount() - 1;
else if (i == aObstacle.PointCount())
i = 0;
}
aWalk.Append( farthest.p );
aWalk.Simplify();
if( n > 0 ) aPost.Clear();
{ aPost.Append( farthest.p );
SHAPE_LINE_CHAIN path_straight = DIRECTION_45().BuildInitialTrace( s1.A, aPost.Append( line.Slice( farthest.our.Index() + 1, -1 ) );
s2.A, false ); aPost.Simplify();
SHAPE_LINE_CHAIN path_diagonal = DIRECTION_45().BuildInitialTrace( s1.A, return true;
s2.A, true ); }
}
if( DIRECTION_45( s1 ) == DIRECTION_45( s2 ) ) void PNS_LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle,
{ SHAPE_LINE_CHAIN& aPath,
if( s1.Collinear( s2 ) ) bool aCw ) const
{ {
// printf("Colinear: np %d step %d n1 %d n2 %d\n", n_segs, step, n, n+step); SHAPE_LINE_CHAIN walk, post;
SHAPE_LINE_CHAIN opt_path; Walkaround( aObstacle, aPath, walk, post, aCw );
opt_path.Append( s1.A ); aPath.Append( walk );
opt_path.Append( s2.B ); aPath.Append( post );
aPath.Simplify();
}
PNS_LINE tmp( *this, opt_path );
if( !m_world->CheckColliding( &tmp, PNS_ITEM::ANY ) ) const SHAPE_LINE_CHAIN PNS_SEGMENT::Hull( int aClearance, int aWalkaroundThickness ) const
{ {
current_path.Remove( s1.Index() + 1, s2.Index() ); return SegmentHull ( m_seg, aClearance, aWalkaroundThickness );
n_segs = current_path.SegmentCount(); }
found_anything = true;
break;
}
}
}
else if( DIRECTION_45( s1 ).IsObtuse( DIRECTION_45( s2 ) ) )
{
VECTOR2I ip = *s1.IntersectLines( s2 );
if( s1.Distance( ip ) <= 1 || s2.Distance( ip ) <= 1 )
{
s1opt = SEG( s1.A, ip );
s2opt = SEG( ip, s2.B );
}
else
{
s1opt = SEG( s1.A, ip );
s2opt = SEG( ip, s2.B );
}
bool PNS_LINE::Is45Degree()
{
for( int i = 0; i < m_line.SegmentCount(); i++ )
{
const SEG& s = m_line.CSegment( i );
if( DIRECTION_45( s1opt ).IsObtuse( DIRECTION_45( s2opt ) ) ) double angle = 180.0 / M_PI *
{ atan2( (double) s.B.y - (double) s.A.y,
SHAPE_LINE_CHAIN opt_path; (double) s.B.x - (double) s.A.x );
opt_path.Append( s1opt.A );
opt_path.Append( s1opt.B );
opt_path.Append( s2opt.B );
PNS_LINE opt_track( *this, opt_path );
if( !m_world->CheckColliding( &opt_track, PNS_ITEM::ANY ) )
{
current_path.Replace( s1.Index() + 1, s2.Index(), ip );
n_segs = current_path.SegmentCount();
found_anything = true;
break;
}
}
}
n++; if( angle < 0 )
} angle += 360.0;
if( !found_anything ) double angle_a = fabs( fmod( angle, 45.0 ) );
{
if( step <= 2 )
{
m_line = current_path;
return m_line.SegmentCount() < segs_pre;
}
step--; if( angle_a > 1.0 && angle_a < 44.0 )
} return false;
} }
return m_line.SegmentCount() < segs_pre; return true;
} }
#endif
int PNS_LINE::CountCorners( int aAngles ) const PNS_LINE PNS_LINE::ClipToNearestObstacle( PNS_NODE* aNode ) const
{ {
int count = 0; PNS_LINE l( *this );
for( int i = 0; i < m_line.SegmentCount() - 1; i++ ) PNS_NODE::OptObstacle obs = aNode->NearestObstacle( &l );
if( obs )
{ {
const SEG seg1 = m_line.CSegment( i ); l.RemoveVia();
const SEG seg2 = m_line.CSegment( i + 1 ); int p = l.Line().Split( obs->ip_first );
l.Line().Remove( p + 1, -1 );
}
const DIRECTION_45 dir1( seg1 ); return l;
const DIRECTION_45 dir2( seg2 ); }
DIRECTION_45::AngleType a = dir1.Angle( dir2 );
if( a & aAngles ) void PNS_LINE::ShowLinks()
count++; {
if( !m_segmentRefs )
{
printf( "line %p: no links\n", this );
return;
} }
return count; printf( "line %p: %d linked segs\n", this, (int) m_segmentRefs->size() );
}
// #define DUMP_TEST_CASES for( int i = 0; i < (int) m_segmentRefs->size(); i++ )
printf( "seg %d: %p\n", i, (*m_segmentRefs)[i] );
}
// fixme: damn f*****g inefficient and incredibly crappily written SHAPE_LINE_CHAIN dragCornerInternal ( const SHAPE_LINE_CHAIN& origin, const VECTOR2I& aP )
void PNS_LINE::NewWalkaround( const SHAPE_LINE_CHAIN& aObstacle,
SHAPE_LINE_CHAIN& aPrePath,
SHAPE_LINE_CHAIN& aWalkaroundPath,
SHAPE_LINE_CHAIN& aPostPath,
bool aCw ) const
{ {
typedef SHAPE_LINE_CHAIN::INTERSECTION INTERSECTION; optional<SHAPE_LINE_CHAIN> picked;
int i;
SHAPE_LINE_CHAIN l_orig( m_line ); int d = 2;
SHAPE_LINE_CHAIN l_hull;
std::vector<bool> outside, on_edge, inside;
SHAPE_LINE_CHAIN path;
std::vector<INTERSECTION> isects;
// don't calculate walkaround for empty lines if(origin.CSegment(-1).Length() > 100000 * 30) // fixme: constant/parameter?
if( m_line.PointCount() < 2 ) d = 1;
return;
#ifdef DUMP_TEST_CASES for(i = origin.SegmentCount() - d; i >= 0; i--)
printf( "%s\n", m_line.Format().c_str() ); {
printf( "%s\n", aObstacle.Format().c_str() );
#endif DIRECTION_45 d_start (origin.CSegment(i));
VECTOR2I p_start = origin.CPoint(i);
SHAPE_LINE_CHAIN paths [2];
DIRECTION_45 dirs[2];
DIRECTION_45 d_prev = (i > 0 ? DIRECTION_45(origin.CSegment(i-1)) : DIRECTION_45());
for(int j = 0; j < 2; j++)
{
paths [j] = d_start.BuildInitialTrace( p_start, aP, j );
dirs [j] = DIRECTION_45(paths[j].CSegment(0));
}
aObstacle.Intersect( m_line, isects );
for( int j = 0; j < 2; j++)
if(dirs[j] == d_start)
{
picked = paths[j];
break;
}
// printf("NewWalk intersectiosn :%d\n" ,isects.size()); if(picked)
if( !aCw ) break;
l_hull = aObstacle.Reverse();
else
l_hull = aObstacle;
BOOST_FOREACH( INTERSECTION isect, isects ) { for(int j = 0; j < 2; j++)
l_orig.Split( isect.p ); if (dirs[j].IsObtuse(d_prev))
l_hull.Split( isect.p ); {
picked = paths[j];
break;
}
if(picked)
break;
} }
if(picked)
{
SHAPE_LINE_CHAIN path = origin.Slice(0, i);
path.Append(*picked);
return path;
}
#ifdef DUMP_TEST_CASES
printf( "%s\n", m_line.Format().c_str() ); return DIRECTION_45().BuildInitialTrace(origin.CPoint(0), aP, true);
printf( "%s\n", aObstacle.Format().c_str() ); }
printf( "%s\n", l_orig.Format().c_str() );
printf( "%s\n", l_hull.Format().c_str() );
#endif
// printf("Pts: line %d hull %d\n", l_orig.PointCount(), l_hull.PointCount());
int first_post = -1; void PNS_LINE::DragCorner ( const VECTOR2I& aP, int aIndex, int aSnappingThreshold )
int last_pre = -1; {
SHAPE_LINE_CHAIN path;
for( int i = 0; i < l_orig.PointCount(); i++ ) VECTOR2I snapped = snapDraggedCorner( m_line, aP, aIndex, aSnappingThreshold );
{
int ei = l_hull.Find( l_orig.CPoint( i ) ); if( aIndex == 0)
bool edge = ei >= 0; path = dragCornerInternal( m_line.Reverse(), snapped ).Reverse();
bool in = l_hull.PointInside( l_orig.CPoint( i ) ) && !edge; else if ( aIndex == m_line.SegmentCount() )
bool out = !( in || edge); path = dragCornerInternal( m_line, snapped );
else {
outside.push_back( out ); // fixme: awkward behaviour for "outwards" drags
on_edge.push_back( edge ); path = dragCornerInternal( m_line.Slice (0, aIndex), snapped );
inside.push_back( in ); SHAPE_LINE_CHAIN path_rev = dragCornerInternal( m_line.Slice (aIndex, -1).Reverse(), snapped ).Reverse();
path.Append(path_rev);
} }
path.Simplify();
m_line = path;
}
for( int i = l_orig.PointCount() - 1; i >= 1; i-- ) VECTOR2I PNS_LINE::snapDraggedCorner( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I &aP, int aIndex, int aThreshold ) const
if( inside[i] && outside[i - 1] ) {
{ int s_start = std::max(aIndex - 2, 0);
SHAPE_LINE_CHAIN::INTERSECTIONS ips; int s_end = std::min(aIndex + 2, aPath.SegmentCount() - 1);
l_hull.Intersect( SEG( l_orig.CPoint( i ), l_orig.CPoint( i - 1 ) ), ips );
l_orig.Remove( i, -1 );
l_orig.Append( ips[0].p );
break;
}
else if( inside[i] && on_edge[i - 1] )
{
l_orig.Remove( i, -1 );
// n = i;
}
else if( !inside[i] )
break;
if( !outside.size() && on_edge.size() < 2 ) int i, j;
return; int best_dist = INT_MAX;
VECTOR2I best_snap = aP;
for( int i = 0; i < l_orig.PointCount(); i++ ) if(aThreshold <= 0)
{ return aP;
const VECTOR2I p = l_orig.Point( i );
if( outside[i] || ( on_edge[i] && i == ( l_orig.PointCount() - 1 ) ) ) for(i = s_start; i <= s_end; i++)
{ {
if( last_pre < 0 ) const SEG& a = aPath.CSegment(i);
aPrePath.Append( p );
path.Append( p ); for(j = s_start; j < i; j++)
}
else if( on_edge[i] )
{ {
int li = -1; const SEG& b = aPath.CSegment(j);
if( last_pre < 0 ) if( ! (DIRECTION_45(a).IsObtuse(DIRECTION_45(b))) )
{ continue;
aPrePath.Append( p );
last_pre = path.PointCount();
}
if( i == l_orig.PointCount() - 1 || outside[i + 1] ) OPT_VECTOR2I ip = a.IntersectLines(b);
if(ip)
{ {
path.Append( p ); int dist = (*ip - aP).EuclideanNorm();
} if( dist < aThreshold && dist < best_dist )
else
{
int vi2 = l_hull.Find( l_orig.CPoint( i ) );
path.Append( l_hull.CPoint( vi2 ) );
for( int j = (vi2 + 1) % l_hull.PointCount();
j != vi2;
j = (j + 1) % l_hull.PointCount() )
{
path.Append( l_hull.CPoint( j ) );
li = l_orig.Find( l_hull.CPoint( j ) );
if( li >= 0 && ( li == ( l_orig.PointCount() - 1 ) ||
outside[li + 1] ) )
break;
}
if( li >= 0 )
{ {
if( i >= li ) best_dist = dist;
break; best_snap = *ip;
else
i = li;
} }
} }
first_post = path.PointCount() - 1;
} }
} }
if( last_pre < 0 && first_post < 0 ) return best_snap;
return;
aWalkaroundPath = path.Slice( last_pre, first_post );
if( first_post >= 0 )
aPostPath = path.Slice( first_post, -1 );
} }
VECTOR2I PNS_LINE::snapToNeighbourSegments( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I &aP, int aIndex, int aThreshold ) const
bool PNS_LINE::onEdge( const SHAPE_LINE_CHAIN& obstacle, VECTOR2I p, int& ei,
bool& is_vertex ) const
{ {
int vtx = obstacle.Find( p ); VECTOR2I snap_p[2];
DIRECTION_45 dragDir ( aPath.CSegment(aIndex) );
int snap_d[2] = {-1, -1};
if( aThreshold == 0 )
return aP;
if( vtx >= 0 ) if(aIndex >= 2)
{ {
ei = vtx; SEG s = aPath.CSegment(aIndex - 2);
is_vertex = true; if(DIRECTION_45(s) == dragDir)
return true; snap_d[0] = s.LineDistance(aP);
} snap_p[0] = s.A;
}
for( int s = 0; s < obstacle.SegmentCount(); s++ ) if(aIndex < aPath.SegmentCount() - 2)
{ {
if( obstacle.CSegment( s ).Contains( p ) ) SEG s = aPath.CSegment(aIndex + 2);
if(DIRECTION_45(s) == dragDir)
snap_d[1] = s.LineDistance(aP);
snap_p[1] = s.A;
}
VECTOR2I best = aP;
int minDist = INT_MAX;
for(int i = 0; i < 2; i++)
if(snap_d[i] >= 0 && snap_d[i] < minDist && snap_d[i] <= aThreshold)
{ {
ei = s; minDist = snap_d[i];
is_vertex = false; best = snap_p[i];
return true;
} }
}
return false; return best;
} }
bool PNS_LINE::walkScan( const SHAPE_LINE_CHAIN& aLine, const SHAPE_LINE_CHAIN& aObstacle, void PNS_LINE::DragSegment ( const VECTOR2I& aP, int aIndex, int aSnappingThreshold )
bool aReverse, VECTOR2I& aIp, int& aIndexO, int& aIndexL, bool& aIsVertex ) const
{ {
int sc = aLine.SegmentCount(); SHAPE_LINE_CHAIN path (m_line);
VECTOR2I target (aP) ;
for( int i = 0; i < aLine.SegmentCount(); i++ ) SEG guideA[2], guideB[2];
{ int index = aIndex;
printf( "check-seg rev %d %d/%d %d\n", aReverse, i, sc, sc - 1 - i );
SEG tmp = aLine.CSegment( aReverse ? sc - 1 - i : i );
SEG s( tmp.A, tmp.B );
if( aReverse )
{
s.A = tmp.B;
s.B = tmp.A;
}
if( onEdge( aObstacle, s.A, aIndexO, aIsVertex ) ) target = snapToNeighbourSegments( path, aP, aIndex, aSnappingThreshold );
{
aIndexL = (aReverse ? sc - 1 - i : i);
aIp = s.A;
printf( "vertex %d on-%s %d\n", aIndexL,
aIsVertex ? "vertex" : "edge", aIndexO );
return true;
}
if( onEdge( aObstacle, s.B, aIndexO, aIsVertex ) ) if(index == 0)
{ {
aIndexL = (aReverse ? sc - 1 - i - 1 : i + 1); path.Insert (0, path.CPoint(0));
aIp = s.B; index++;
printf( "vertex %d on-%s %d\n", aIndexL, }
aIsVertex ? "vertex" : "edge", aIndexO );
return true;
}
SHAPE_LINE_CHAIN::INTERSECTIONS ips;
int n_is = aObstacle.Intersect( s, ips );
if( n_is > 0 ) if(index == path.SegmentCount()-1)
{ {
aIndexO = ips[0].our.Index(); path.Insert(path.PointCount() - 1, path.CPoint(-1));
aIndexL = aReverse ? sc - 1 - i : i;
printf( "segment-%d intersects edge-%d\n", aIndexL, aIndexO );
aIp = ips[0].p;
return true;
}
} }
return false; SEG dragged = path.CSegment(index);
} DIRECTION_45 drag_dir (dragged);
SEG s_prev = path.CSegment(index - 1);
SEG s_next = path.CSegment(index + 1);
bool PNS_LINE::Walkaround( SHAPE_LINE_CHAIN aObstacle, SHAPE_LINE_CHAIN& aPre, DIRECTION_45 dir_prev (s_prev);
SHAPE_LINE_CHAIN& aWalk, SHAPE_LINE_CHAIN& aPost, bool aCw ) const DIRECTION_45 dir_next (s_next);
{
const SHAPE_LINE_CHAIN& line = GetCLine();
VECTOR2I ip_start;
int index_o_start, index_l_start;
VECTOR2I ip_end;
int index_o_end, index_l_end;
bool is_vertex_start, is_vertex_end; if(dir_prev == drag_dir)
{
dir_prev = dir_prev.Left();
path.Insert( index, path.CPoint(index) );
index++;
}
if( line.SegmentCount() < 1 ) if(dir_next == drag_dir)
return false; {
dir_next = dir_next.Right();
path.Insert( index + 1, path.CPoint(index + 1) );
}
if( aObstacle.PointInside( line.CPoint( 0 ) ) ||
aObstacle.PointInside( line.CPoint( -1 ) ) )
return false;
// printf("forward:\n"); s_prev = path.CSegment(index - 1);
bool found = walkScan( line, aObstacle, false, ip_start, index_o_start, s_next = path.CSegment(index + 1);
index_l_start, is_vertex_start ); dragged = path.CSegment(index);
// printf("reverse:\n");
found |= walkScan( line, aObstacle, true, ip_end, index_o_end, index_l_end, is_vertex_end );
if( !found || ip_start == ip_end ) bool lockEndpointA = true;
bool lockEndpointB = true;
if(aIndex == 0)
{ {
aPre = line; if(!lockEndpointA)
return true; guideA[0] = guideA[1] = SEG( dragged.A, dragged.A + drag_dir.Right().Right().ToVector() );
else {
guideA[0] = SEG( dragged.A, dragged.A + drag_dir.Right().ToVector() );
guideA[1] = SEG( dragged.A, dragged.A + drag_dir.Left().ToVector() );
}
} else {
if(dir_prev.IsObtuse(drag_dir))
{
guideA[0] = SEG( s_prev.A, s_prev.A + drag_dir.Left().ToVector() );
guideA[1] = SEG( s_prev.A, s_prev.A + drag_dir.Right().ToVector() );
} else
guideA[0] = guideA[1] = SEG( dragged.A, dragged.A + dir_prev.ToVector() );
} }
aPre = line.Slice( 0, index_l_start ); if(aIndex == m_line.SegmentCount() - 1)
aPre.Append( ip_start );
aWalk.Clear();
aWalk.Append( ip_start );
if( aCw )
{ {
int is = ( index_o_start + 1 ) % aObstacle.PointCount(); if(!lockEndpointB)
int ie = ( is_vertex_end ? index_o_end : index_o_end + 1 ) % aObstacle.PointCount(); guideB[0] = guideB[1] = SEG( dragged.B, dragged.B + drag_dir.Right().Right().ToVector() );
else {
while( 1 ) guideB[0] = SEG( dragged.B, dragged.B + drag_dir.Right().ToVector() );
guideB[1] = SEG( dragged.B, dragged.B + drag_dir.Left().ToVector() );
}
} else {
if(dir_next.IsObtuse(drag_dir))
{ {
printf( "is %d\n", is ); guideB[0] = SEG( s_next.B, s_next.B + drag_dir.Left().ToVector() );
aWalk.Append( aObstacle.CPoint( is ) ); guideB[1] = SEG( s_next.B, s_next.B + drag_dir.Right().ToVector() );
} else
guideB[0] = guideB[1] = SEG( dragged.B, dragged.B + dir_next.ToVector() );
}
if( is == ie ) SEG s_current (target, target + drag_dir.ToVector());
break;
is++; int best_len = INT_MAX;
SHAPE_LINE_CHAIN best;
if( is == aObstacle.PointCount() ) for(int i = 0; i < 2; i++)
is = 0;
}
}
else
{ {
int is = index_o_start; for(int j = 0; j < 2; j++)
int ie = ( is_vertex_end ? index_o_end : index_o_end ) % aObstacle.PointCount();
while( 1 )
{ {
printf( "is %d\n", is ); OPT_VECTOR2I ip1 = s_current.IntersectLines(guideA[i]);
aWalk.Append( aObstacle.CPoint( is ) ); OPT_VECTOR2I ip2 = s_current.IntersectLines(guideB[j]);
if( is == ie ) SHAPE_LINE_CHAIN np;
break;
is--; if(!ip1 || !ip2)
continue;
if( is < 0 ) SEG s1 ( s_prev.A, *ip1 );
is = aObstacle.PointCount() - 1; SEG s2 ( *ip1, *ip2 );
} SEG s3 ( *ip2, s_next.B );
}
aWalk.Append( ip_end ); OPT_VECTOR2I ip;
aPost.Clear(); if(ip = s1.Intersect(s_next))
aPost.Append( ip_end ); {
aPost.Append( line.Slice( is_vertex_end ? index_l_end : index_l_end + 1, -1 ) ); np.Append ( s1.A );
np.Append ( *ip );
np.Append ( s_next.B );
} else if(ip = s3.Intersect(s_prev))
{
np.Append ( s_prev.A );
np.Append ( *ip );
np.Append ( s3.B );
} else if(ip = s1.Intersect(s3))
{
np.Append( s_prev.A );
np.Append( *ip );
np.Append( s_next.B );
} else {
np.Append( s_prev.A );
np.Append( *ip1 );
np.Append( *ip2 );
np.Append( s_next.B );
}
if(np.Length() < best_len)
{
best_len = np.Length();
best = np;
}
}
}
// for(int i = (index_o_start + 1) % obstacle.PointCount(); if(!lockEndpointA && aIndex == 0)
// i != (index_o_end + 1) % obstacle.PointCount(); i=(i+1) % obstacle.PointCount()) best.Remove(0, 0);
// { if(!lockEndpointB && aIndex == m_line.SegmentCount() - 1)
// printf("append %d\n", i); best.Remove(-1, -1);
// walk.Append(obstacle.CPoint(i));
// }
if(m_line.PointCount() == 1)
m_line = best;
else if (aIndex == 0)
m_line.Replace(0, 1, best);
else if (aIndex == m_line.SegmentCount() - 1)
m_line.Replace(-2, -1, best);
else
m_line.Replace(aIndex, aIndex + 1, best);
return true; m_line.Simplify();
} }
void PNS_LINE::NewWalkaround( const SHAPE_LINE_CHAIN& aObstacle, bool PNS_LINE::CompareGeometry( const PNS_LINE& aOther )
SHAPE_LINE_CHAIN& aPath,
bool aCw ) const
{ {
SHAPE_LINE_CHAIN walk, post; return m_line.CompareGeometry(aOther.m_line);
NewWalkaround( aObstacle, aPath, walk, post, aCw );
aPath.Append( walk );
aPath.Append( post );
aPath.Simplify();
} }
void PNS_LINE::Reverse()
void PNS_LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle,
SHAPE_LINE_CHAIN& aPath,
bool aCw ) const
{ {
SHAPE_LINE_CHAIN walk, post; m_line = m_line.Reverse();
if(m_segmentRefs)
Walkaround( aObstacle, aPath, walk, post, aCw ); std::reverse(m_segmentRefs->begin(), m_segmentRefs->end() );
aPath.Append( walk );
aPath.Append( post );
aPath.Simplify();
} }
void PNS_LINE::AppendVia(const PNS_VIA& aVia)
const SHAPE_LINE_CHAIN PNS_SEGMENT::Hull( int aClearance, int aWalkaroundThickness ) const
{ {
int d = aClearance + 10; if(aVia.Pos() == m_line.CPoint(0))
int x = (int)( 2.0 / ( 1.0 + M_SQRT2 ) * d ) + 2; {
const VECTOR2I a = m_shape.CPoint( 0 ); Reverse();
const VECTOR2I b = m_shape.CPoint( 1 ); }
VECTOR2I dir = b - a; m_hasVia = true;
m_via = aVia;
VECTOR2I p0 = dir.Perpendicular().Resize( d ); m_via.SetNet( m_net );
VECTOR2I ds = dir.Perpendicular().Resize( x / 2 );
VECTOR2I pd = dir.Resize( x / 2 );
VECTOR2I dp = dir.Resize( d );
SHAPE_LINE_CHAIN s;
s.SetClosed( true );
s.Append( b + p0 + pd );
s.Append( b + dp + ds );
s.Append( b + dp - ds );
s.Append( b - p0 + pd );
s.Append( a - p0 - pd );
s.Append( a - dp - ds );
s.Append( a - dp + ds );
s.Append( a + p0 - pd );
// make sure the hull outline is always clockwise
if( s.CSegment( 0 ).Side( a ) < 0 )
return s.Reverse();
else
return s;
} }
void PNS_LINE::SetRank(int aRank)
bool PNS_LINE::Is45Degree()
{ {
for( int i = 0; i < m_line.SegmentCount(); i++ ) m_rank = aRank;
if(m_segmentRefs)
{ {
const SEG& s = m_line.CSegment( i ); BOOST_FOREACH( PNS_SEGMENT *s, *m_segmentRefs )
s->SetRank(aRank);
double angle = 180.0 / M_PI *
atan2( (double) s.B.y - (double) s.A.y,
(double) s.B.x - (double) s.A.x );
if( angle < 0 )
angle += 360.0;
double angle_a = fabs( fmod( angle, 45.0 ) );
if( angle_a > 1.0 && angle_a < 44.0 )
return false;
} }
return true;
} }
int PNS_LINE::Rank() const
const PNS_LINE PNS_LINE::ClipToNearestObstacle( PNS_NODE* aNode ) const
{ {
PNS_LINE l( *this ); int min_rank = INT_MAX;
int rank;
PNS_NODE::OptObstacle obs = aNode->NearestObstacle( &l ); if(m_segmentRefs)
if( obs )
{ {
l.RemoveVia(); BOOST_FOREACH( PNS_SEGMENT *s, *m_segmentRefs )
int p = l.GetLine().Split( obs->ip_first ); min_rank = std::min(min_rank, s->Rank());
l.GetLine().Remove( p + 1, -1 ); rank = (min_rank == INT_MAX) ? -1 : min_rank;
} else {
rank = m_rank;
} }
return l; return rank;
} }
void PNS_LINE::ClipVertexRange ( int aStart, int aEnd )
void PNS_LINE::ShowLinks()
{ {
if( !m_segmentRefs ) m_line = m_line.Slice (aStart, aEnd);
if(m_segmentRefs)
{ {
printf( "line %p: no links\n", this ); SegmentRefs *snew = new SegmentRefs( m_segmentRefs->begin() + aStart, m_segmentRefs->begin() + aEnd );
return;
delete m_segmentRefs;
m_segmentRefs = snew;
} }
}
printf( "line %p: %d linked segs\n", this, (int) m_segmentRefs->size() ); bool PNS_LINE::HasLoops() const
{
for(int i = 0; i < PointCount(); i++)
for(int j = 0; j < PointCount(); j++)
{
if( (std::abs(i-j) > 1) && CPoint(i) == CPoint(j))
return true;
}
return false;
}
for( int i = 0; i < (int) m_segmentRefs->size(); i++ ) void PNS_LINE::ClearSegmentLinks()
printf( "seg %d: %p\n", i, (*m_segmentRefs)[i] ); {
if(m_segmentRefs)
delete m_segmentRefs;
m_segmentRefs = NULL;
} }
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_LINE_H #ifndef __PNS_LINE_H
...@@ -42,60 +42,42 @@ class PNS_VIA; ...@@ -42,60 +42,42 @@ class PNS_VIA;
* vias, pads, junctions between multiple traces or two traces different widths * vias, pads, junctions between multiple traces or two traces different widths
* and combinations of these). PNS_LINEs are NOT stored in the model (PNS_NODE). * and combinations of these). PNS_LINEs are NOT stored in the model (PNS_NODE).
* Instead, they are assembled on-the-fly, based on a via/pad/segment that * Instead, they are assembled on-the-fly, based on a via/pad/segment that
* belongs/begins them. * belongs to/starts/ends them.
* *
* PNS_LINEs can be either loose (consisting of segments that do not belong to * PNS_LINEs can be either loose (consisting of segments that do not belong to
* any PNS_NODE) or owned (with segments taken from a PNS_NODE) - these are * any PNS_NODE) or owned (with segments taken from a PNS_NODE) - these are
* returned by PNS_NODE::AssembleLine and friends. * returned by PNS_NODE::AssembleLine and friends.
* *
* A PNS_LINE may have a PNS_VIA attached at its and - this is used by via * A PNS_LINE may have a PNS_VIA attached at its end (i.e. the last point) - this is used by via
* dragging/force propagation stuff. * dragging/force propagation stuff.
*/ */
#define PNS_HULL_MARGIN 10
class PNS_LINE : public PNS_ITEM class PNS_LINE : public PNS_ITEM
{ {
public: public:
typedef std::vector<PNS_SEGMENT*> LinkedSegments; typedef std::vector<PNS_SEGMENT*> SegmentRefs;
PNS_LINE() : /**
PNS_ITEM( LINE ) * Constructor
{ * Makes an empty line.
m_segmentRefs = NULL; */
m_hasVia = false; PNS_LINE() : PNS_ITEM (LINE)
m_affectedRangeStart = -1;
};
PNS_LINE( int aLayer, int aWidth, const SHAPE_LINE_CHAIN& aLine ) :
PNS_ITEM( LINE )
{ {
m_line = aLine;
m_width = aWidth;
m_segmentRefs = NULL; m_segmentRefs = NULL;
m_hasVia = false; m_hasVia = false;
m_affectedRangeStart = -1;
SetLayer( aLayer );
}
PNS_LINE( const PNS_LINE& aOther ) :
PNS_ITEM( aOther ),
m_line( aOther.m_line ),
m_width( aOther.m_width )
{
m_net = aOther.m_net;
m_movable = aOther.m_movable;
m_world = aOther.m_world;
m_layers = aOther.m_layers;
m_segmentRefs = NULL;
m_via = aOther.m_via;
m_hasVia = aOther.m_hasVia;
m_affectedRangeStart = -1;
} }
PNS_LINE( const PNS_LINE& aOther ) ;
/** /**
* Constructor * Constructor
* copies properties (net, layers from a base line), and replaces the shape * Copies properties (net, layers, etc.) from a base line and replaces the shape
* by aLine * by another
**/ **/
PNS_LINE( const PNS_LINE& aBase, const SHAPE_LINE_CHAIN& aLine ) : PNS_LINE( const PNS_LINE& aBase, const SHAPE_LINE_CHAIN& aLine ) :
PNS_ITEM( aBase ), PNS_ITEM( aBase ),
m_line( aLine ), m_line( aLine ),
...@@ -105,49 +87,101 @@ public: ...@@ -105,49 +87,101 @@ public:
m_layers = aBase.m_layers; m_layers = aBase.m_layers;
m_segmentRefs = NULL; m_segmentRefs = NULL;
m_hasVia = false; m_hasVia = false;
m_affectedRangeStart = -1;
} }
~PNS_LINE() ~PNS_LINE();
/// @copydoc PNS_ITEM::Clone()
virtual PNS_LINE* Clone( ) const;
const PNS_LINE& operator= (const PNS_LINE& aOther);
///> Assigns a shape to the line (a polyline/line chain)
void SetShape ( const SHAPE_LINE_CHAIN& aLine )
{
m_line = aLine;
}
///> Returns the shape of the line
const SHAPE* Shape() const
{
return &m_line;
}
///> Modifiable accessor to the underlying shape
SHAPE_LINE_CHAIN& Line()
{
return m_line;
}
///> Const accessor to the underlying shape
const SHAPE_LINE_CHAIN& CLine() const
{
return m_line;
}
///> Returns the number of segments in the line
int SegmentCount() const
{ {
if( m_segmentRefs ) return m_line.SegmentCount();
delete m_segmentRefs; }
};
virtual PNS_LINE* Clone() const; ///> Returns the number of points in the line
int PointCount() const
{
return m_line.PointCount();
}
///> clones the line without cloning the shape ///> Returns the aIdx-th point of the line
///> (just the properties - net, width, layers, etc.) const VECTOR2I& CPoint( int aIdx ) const
PNS_LINE* CloneProperties() const; {
return m_line.CPoint(aIdx);
}
int GetLayer() const { return GetLayers().Start(); } ///> Returns the aIdx-th segment of the line
const SEG CSegment (int aIdx ) const
{
return m_line.CSegment(aIdx);
}
///> Geometry accessors ///> Sets line width
void SetShape( const SHAPE_LINE_CHAIN& aLine ) { m_line = aLine; } void SetWidth( int aWidth )
const SHAPE* GetShape() const { return &m_line; } {
SHAPE_LINE_CHAIN& GetLine() { return m_line; } m_width = aWidth;
const SHAPE_LINE_CHAIN& GetCLine() const { return m_line; } }
///> Returns line width
int Width() const
{
return m_width;
}
///> Returns true if the line is geometrically identical as line aOther
bool CompareGeometry( const PNS_LINE& aOther );
///> Reverses the point/vertex order
void Reverse();
///> Width accessors
void SetWidth( int aWidth ) { m_width = aWidth; }
int GetWidth() const { return m_width; }
///> Links a segment from a PNS_NODE to this line, making it owned by the node /* Linking functions */
///> Adds a reference to a segment registered in a PNS_NODE that is a part of this line.
void LinkSegment( PNS_SEGMENT* aSeg ) void LinkSegment( PNS_SEGMENT* aSeg )
{ {
if( !m_segmentRefs ) if( !m_segmentRefs )
m_segmentRefs = new std::vector<PNS_SEGMENT*> (); m_segmentRefs = new SegmentRefs();
m_segmentRefs->push_back( aSeg ); m_segmentRefs->push_back( aSeg );
} }
///> Returns a list of segments from the owning node that constitute this ///> Returns the list of segments from the owning node that constitute this
///> line (or NULL if the line is loose) ///> line (or NULL if the line is not linked)
LinkedSegments* GetLinkedSegments() SegmentRefs* LinkedSegments()
{ {
return m_segmentRefs; return m_segmentRefs;
} }
///> Checks if the segment aSeg is a part of the line.
bool ContainsSegment( PNS_SEGMENT* aSeg ) const bool ContainsSegment( PNS_SEGMENT* aSeg ) const
{ {
if( !m_segmentRefs ) if( !m_segmentRefs )
...@@ -157,13 +191,23 @@ public: ...@@ -157,13 +191,23 @@ public:
aSeg ) != m_segmentRefs->end(); aSeg ) != m_segmentRefs->end();
} }
///> Returns this line, but clipped to the nearest obstacle ///> Erases the linking information. Used to detach the line from the owning node.
///> along, to avoid collision. void ClearSegmentLinks();
///> Returns the number of segments that were assembled together to form this line.
int LinkCount() const {
if(!m_segmentRefs)
return -1;
return m_segmentRefs->size();
}
///> Clips the line to the nearest obstacle, traversing from the line's start vertex (0).
///> Returns the clipped line.
const PNS_LINE ClipToNearestObstacle( PNS_NODE* aNode ) const; const PNS_LINE ClipToNearestObstacle( PNS_NODE* aNode ) const;
///> DEPRECATED optimization functions (moved to PNS_OPTIMIZER) ///> Clips the line to a given range of vertices.
bool MergeObtuseSegments(); void ClipVertexRange ( int aStart, int aEnd );
bool MergeSegments();
///> Returns the number of corners of angles specified by mask aAngles. ///> Returns the number of corners of angles specified by mask aAngles.
int CountCorners( int aAngles ); int CountCorners( int aAngles );
...@@ -173,17 +217,7 @@ public: ...@@ -173,17 +217,7 @@ public:
///> aPrePath = path from origin to the obstacle ///> aPrePath = path from origin to the obstacle
///> aWalkaroundPath = path around the obstacle ///> aWalkaroundPath = path around the obstacle
///> aPostPath = past from obstacle till the end ///> aPostPath = past from obstacle till the end
///> aCW = whether to walkaround in clockwise or counter-clockwise direction. ///> aCW = whether to walk around in clockwise or counter-clockwise direction.
void NewWalkaround( const SHAPE_LINE_CHAIN& aObstacle,
SHAPE_LINE_CHAIN& aPrePath,
SHAPE_LINE_CHAIN& aWalkaroundPath,
SHAPE_LINE_CHAIN& aPostPath,
bool aCw ) const;
void NewWalkaround( const SHAPE_LINE_CHAIN& aObstacle,
SHAPE_LINE_CHAIN& aPath,
bool aCw ) const;
bool Walkaround( SHAPE_LINE_CHAIN obstacle, bool Walkaround( SHAPE_LINE_CHAIN obstacle,
SHAPE_LINE_CHAIN& pre, SHAPE_LINE_CHAIN& pre,
...@@ -202,62 +236,46 @@ public: ...@@ -202,62 +236,46 @@ public:
bool EndsWithVia() const { return m_hasVia; } bool EndsWithVia() const { return m_hasVia; }
void AppendVia( const PNS_VIA& aVia ) void AppendVia( const PNS_VIA& aVia );
{
m_hasVia = true;
m_via = aVia;
m_via.SetNet( m_net );
}
void RemoveVia() { m_hasVia = false; } void RemoveVia() { m_hasVia = false; }
const PNS_VIA& GetVia() const { return m_via; }
void SetAffectedRange( int aStart, int aEnd ) const PNS_VIA& Via() const { return m_via; }
{
m_affectedRangeStart = aStart; virtual void Mark(int aMarker);
m_affectedRangeEnd = aEnd; virtual void Unmark ();
} virtual int Marker() const;
void DragSegment ( const VECTOR2I& aP, int aIndex, int aSnappingThreshold = 0 );
void DragCorner ( const VECTOR2I& aP, int aIndex, int aSnappingThreshold = 0 );
void ClearAffectedRange() void SetRank ( int aRank );
{ int Rank() const;
m_affectedRangeStart = -1;
} bool HasLoops() const;
bool GetAffectedRange( int& aStart, int& aEnd )
{
if( m_affectedRangeStart >= 0 )
{
aStart = m_affectedRangeStart;
aEnd = m_affectedRangeEnd;
return true;
}
else
{
aStart = 0;
aEnd = m_line.PointCount();
return false;
}
}
private: private:
bool onEdge( const SHAPE_LINE_CHAIN& obstacle, VECTOR2I p, int& ei, bool& is_vertex ) const;
bool walkScan( const SHAPE_LINE_CHAIN& line, const SHAPE_LINE_CHAIN& obstacle,
bool reverse, VECTOR2I& ip, int& index_o, int& index_l, bool& is_vertex ) const;
///> List of semgments in a PNS_NODE (PNS_ITEM::m_owner) that constitute this line. VECTOR2I snapToNeighbourSegments( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I &aP, int aIndex, int aThreshold) const;
LinkedSegments* m_segmentRefs; VECTOR2I snapDraggedCorner( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I &aP, int aIndex, int aThreshold ) const;
///> Shape of the line ///> Copies m_segmentRefs from the line aParent.
void copyLinks( const PNS_LINE *aParent ) ;
///> List of segments in the owning PNS_NODE (PNS_ITEM::m_owner) that constitute this line, or NULL
///> if the line is not a part of any node.
SegmentRefs* m_segmentRefs;
///> The actual shape of the line
SHAPE_LINE_CHAIN m_line; SHAPE_LINE_CHAIN m_line;
///> our width
int m_width; int m_width;
///> Via at the end and a flag indicating if it's enabled. ///> If true, the line ends with a via
PNS_VIA m_via;
bool m_hasVia; bool m_hasVia;
int m_affectedRangeStart; ///> Via at the end point, if m_hasVia == true
int m_affectedRangeEnd; PNS_VIA m_via;
}; };
#endif // __PNS_LINE_H #endif // __PNS_LINE_H
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,12 +15,14 @@ ...@@ -15,12 +15,14 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <colors.h>
#include "trace.h" #include "trace.h"
#include "pns_node.h" #include "pns_node.h"
...@@ -28,44 +30,46 @@ ...@@ -28,44 +30,46 @@
#include "pns_walkaround.h" #include "pns_walkaround.h"
#include "pns_shove.h" #include "pns_shove.h"
#include "pns_utils.h" #include "pns_utils.h"
#include "pns_router.h"
using boost::optional; #include <class_board_item.h>
const double PNS_LINE_PLACER::m_shoveLengthThreshold = 1.7; using boost::optional;
PNS_LINE_PLACER::PNS_LINE_PLACER( PNS_NODE* aWorld ) PNS_LINE_PLACER::PNS_LINE_PLACER( PNS_ROUTER* aRouter ) :
PNS_ALGO_BASE ( aRouter )
{ {
m_initial_direction = DIRECTION_45( DIRECTION_45::N ); m_initial_direction = DIRECTION_45::N;
m_follow_mouse = false;
m_smoothing_step = 100000;
m_smooth_mouse = false;
m_iteration = 0; m_iteration = 0;
m_world = aWorld; m_world = NULL;
m_mode = RM_Smart;
m_follow_mouse = true;
m_shove = NULL; m_shove = NULL;
m_currentNode = NULL;
}; };
PNS_LINE_PLACER::~PNS_LINE_PLACER() PNS_LINE_PLACER::~PNS_LINE_PLACER()
{ {
if( m_shove ) if( m_shove )
delete m_shove; delete m_shove;
} }
void PNS_LINE_PLACER::setWorld ( PNS_NODE *aWorld )
void PNS_LINE_PLACER::ApplySettings( const PNS_ROUTING_SETTINGS& aSettings )
{ {
m_follow_mouse = aSettings.m_followMouse; m_world = aWorld;
m_mode = aSettings.m_routingMode;
m_walkaroundIterationLimit = aSettings.m_walkaroundIterationLimit;
m_smartPads = aSettings.m_smartPads;
} }
void PNS_LINE_PLACER::AddVia( bool aEnabled, int aDiameter, int aDrill )
{
m_viaDiameter = aDiameter;
m_viaDrill = aDrill;
m_placingVia = aEnabled;
}
void PNS_LINE_PLACER::StartPlacement( const VECTOR2I& aStart, int aNet, void PNS_LINE_PLACER::startPlacement( const VECTOR2I& aStart, int aNet,
int aWidth, int aLayer ) int aWidth, int aLayer )
{ {
assert(m_world != NULL);
m_direction = m_initial_direction; m_direction = m_initial_direction;
TRACE( 1, "world %p, intitial-direction %s layer %d\n", TRACE( 1, "world %p, intitial-direction %s layer %d\n",
m_world % m_direction.Format().c_str() % aLayer ); m_world % m_direction.Format().c_str() % aLayer );
...@@ -73,27 +77,37 @@ void PNS_LINE_PLACER::StartPlacement( const VECTOR2I& aStart, int aNet, ...@@ -73,27 +77,37 @@ void PNS_LINE_PLACER::StartPlacement( const VECTOR2I& aStart, int aNet,
m_tail.SetNet( aNet ); m_tail.SetNet( aNet );
m_head.SetWidth( aWidth ); m_head.SetWidth( aWidth );
m_tail.SetWidth( aWidth ); m_tail.SetWidth( aWidth );
m_head.GetLine().Clear(); m_head.Line().Clear();
m_tail.GetLine().Clear(); m_tail.Line().Clear();
m_head.SetLayer( aLayer ); m_head.SetLayer( aLayer );
m_tail.SetLayer( aLayer ); m_tail.SetLayer( aLayer );
m_iteration = 0; m_iteration = 0;
m_p_start = aStart; m_p_start = aStart;
m_currentNode = m_world->Branch();
m_head.SetWorld( m_currentNode ); m_lastNode = NULL;
m_tail.SetWorld( m_currentNode ); m_currentNode = m_world;
// if(m_shove)
// delete m_shove; m_currentMode = Settings().Mode();
m_shove = new PNS_SHOVE( m_currentNode );
if(m_shove)
delete m_shove;
m_shove = NULL;
if(m_currentMode == RM_Shove || m_currentMode == RM_Smart)
{
m_shove = new PNS_SHOVE( m_world -> Branch(), Router() );
}
m_placingVia = false; m_placingVia = false;
} }
void PNS_LINE_PLACER::SetInitialDirection( const DIRECTION_45& aDirection ) void PNS_LINE_PLACER::setInitialDirection( const DIRECTION_45& aDirection )
{ {
m_initial_direction = aDirection; m_initial_direction = aDirection;
if( m_tail.GetCLine().SegmentCount() == 0 ) if( m_tail.SegmentCount() == 0 )
m_direction = aDirection; m_direction = aDirection;
} }
...@@ -101,8 +115,8 @@ void PNS_LINE_PLACER::SetInitialDirection( const DIRECTION_45& aDirection ) ...@@ -101,8 +115,8 @@ void PNS_LINE_PLACER::SetInitialDirection( const DIRECTION_45& aDirection )
bool PNS_LINE_PLACER::handleSelfIntersections() bool PNS_LINE_PLACER::handleSelfIntersections()
{ {
SHAPE_LINE_CHAIN::INTERSECTIONS ips; SHAPE_LINE_CHAIN::INTERSECTIONS ips;
SHAPE_LINE_CHAIN& head = m_head.GetLine(); SHAPE_LINE_CHAIN& head = m_head.Line();
SHAPE_LINE_CHAIN& tail = m_tail.GetLine(); SHAPE_LINE_CHAIN& tail = m_tail.Line();
// if there is no tail, there is nothing to intersect with // if there is no tail, there is nothing to intersect with
if( tail.PointCount() < 2 ) if( tail.PointCount() < 2 )
...@@ -159,8 +173,11 @@ bool PNS_LINE_PLACER::handleSelfIntersections() ...@@ -159,8 +173,11 @@ bool PNS_LINE_PLACER::handleSelfIntersections()
bool PNS_LINE_PLACER::handlePullback() bool PNS_LINE_PLACER::handlePullback()
{ {
SHAPE_LINE_CHAIN& head = m_head.GetLine(); SHAPE_LINE_CHAIN& head = m_head.Line();
SHAPE_LINE_CHAIN& tail = m_tail.GetLine(); SHAPE_LINE_CHAIN& tail = m_tail.Line();
if(head.PointCount() < 2)
return false;
int n = tail.PointCount(); int n = tail.PointCount();
...@@ -173,8 +190,8 @@ bool PNS_LINE_PLACER::handlePullback() ...@@ -173,8 +190,8 @@ bool PNS_LINE_PLACER::handlePullback()
return true; return true;
} }
DIRECTION_45 first_head( head.Segment( 0 ) ); DIRECTION_45 first_head( head.CSegment( 0 ) );
DIRECTION_45 last_tail( tail.Segment( -1 ) ); DIRECTION_45 last_tail( tail.CSegment( -1 ) );
DIRECTION_45::AngleType angle = first_head.Angle( last_tail ); DIRECTION_45::AngleType angle = first_head.Angle( last_tail );
// case 1: we have a defined routing direction, and the currently computed // case 1: we have a defined routing direction, and the currently computed
...@@ -215,11 +232,14 @@ bool PNS_LINE_PLACER::handlePullback() ...@@ -215,11 +232,14 @@ bool PNS_LINE_PLACER::handlePullback()
bool PNS_LINE_PLACER::reduceTail( const VECTOR2I& aEnd ) bool PNS_LINE_PLACER::reduceTail( const VECTOR2I& aEnd )
{ {
SHAPE_LINE_CHAIN& head = m_head.GetLine(); SHAPE_LINE_CHAIN& head = m_head.Line();
SHAPE_LINE_CHAIN& tail = m_tail.GetLine(); SHAPE_LINE_CHAIN& tail = m_tail.Line();
int n = tail.SegmentCount(); int n = tail.SegmentCount();
if(head.SegmentCount() < 1)
return false;
// Don't attempt this for too short tails // Don't attempt this for too short tails
if( n < 2 ) if( n < 2 )
return false; return false;
...@@ -231,7 +251,7 @@ bool PNS_LINE_PLACER::reduceTail( const VECTOR2I& aEnd ) ...@@ -231,7 +251,7 @@ 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.Segment( 0 ) ); 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-- )
{ {
...@@ -247,7 +267,7 @@ bool PNS_LINE_PLACER::reduceTail( const VECTOR2I& aEnd ) ...@@ -247,7 +267,7 @@ bool PNS_LINE_PLACER::reduceTail( const VECTOR2I& aEnd )
if( m_currentNode->CheckColliding( &tmp, PNS_ITEM::ANY ) ) if( m_currentNode->CheckColliding( &tmp, PNS_ITEM::ANY ) )
break; break;
if( DIRECTION_45( replacement.Segment( 0 ) ) == dir ) if( DIRECTION_45( replacement.CSegment( 0 ) ) == dir )
{ {
new_start = s.A; new_start = s.A;
new_direction = dir; new_direction = dir;
...@@ -285,8 +305,8 @@ bool PNS_LINE_PLACER::checkObtusity( const SEG& a, const SEG& b ) const ...@@ -285,8 +305,8 @@ bool PNS_LINE_PLACER::checkObtusity( const SEG& a, const SEG& b ) const
bool PNS_LINE_PLACER::mergeHead() bool PNS_LINE_PLACER::mergeHead()
{ {
SHAPE_LINE_CHAIN& head = m_head.GetLine(); SHAPE_LINE_CHAIN& head = m_head.Line();
SHAPE_LINE_CHAIN& tail = m_tail.GetLine(); SHAPE_LINE_CHAIN& tail = m_tail.Line();
const int ForbiddenAngles = DIRECTION_45::ANG_ACUTE | const int ForbiddenAngles = DIRECTION_45::ANG_ACUTE |
DIRECTION_45::ANG_HALF_FULL | DIRECTION_45::ANG_HALF_FULL |
...@@ -357,146 +377,157 @@ bool PNS_LINE_PLACER::handleViaPlacement( PNS_LINE& aHead ) ...@@ -357,146 +377,157 @@ bool PNS_LINE_PLACER::handleViaPlacement( PNS_LINE& aHead )
return true; return true;
PNS_LAYERSET allLayers( 0, 15 ); PNS_LAYERSET allLayers( 0, 15 );
PNS_VIA v( aHead.GetCLine().CPoint( -1 ), allLayers, m_viaDiameter, aHead.GetNet() ); PNS_VIA v( aHead.CPoint( -1 ), allLayers, m_viaDiameter, aHead.Net() );
v.SetDrill( m_viaDrill ); v.SetDrill( m_viaDrill );
VECTOR2I force; VECTOR2I force;
VECTOR2I lead = aHead.GetCLine().CPoint( -1 ) - aHead.GetCLine().CPoint( 0 ); VECTOR2I lead = aHead.CPoint( -1 ) - aHead.CPoint( 0 );
bool solidsOnly = ( m_currentMode != RM_Walkaround );
if( v.PushoutForce( m_shove->GetCurrentNode(), lead, force, true, 20 ) ) if( v.PushoutForce( m_currentNode, lead, force, solidsOnly, 40 ) )
{ {
SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace( SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace(
aHead.GetCLine().CPoint( 0 ), aHead.CPoint( 0 ),
aHead.GetCLine().CPoint( -1 ) + force ); aHead.CPoint( -1 ) + force );
aHead = PNS_LINE( aHead, line ); aHead = PNS_LINE( aHead, line );
v.SetPos( v.GetPos() + force ); v.SetPos( v.Pos() + force );
return true; return true;
} }
return false; return false;
} }
bool PNS_LINE_PLACER::rhWalkOnly ( const VECTOR2I& aP, PNS_LINE& aNewHead )
bool PNS_LINE_PLACER::routeHead( const VECTOR2I& aP, PNS_LINE& aNewHead,
bool aCwWalkaround )
{ {
// STAGE 1: route a simple two-segment trace between m_p_start and aP...
SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace( m_p_start, aP ); SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace( m_p_start, aP );
PNS_LINE initTrack( m_head, line ), walkFull;
int effort = 0;
bool viaOk = handleViaPlacement( initTrack );
bool rv = true;
PNS_LINE initTrack( m_head, line ); PNS_WALKAROUND walkaround( m_currentNode, Router() );
PNS_LINE walkFull, walkSolids;
if( m_mode == RM_Ignore ) walkaround.SetSolidsOnly( false );
walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() );
PNS_WALKAROUND::WalkaroundStatus wf = walkaround.Route( initTrack, walkFull, false );
switch(Settings().OptimizerEffort())
{ {
aNewHead = initTrack; case OE_Low:
return true; effort = 0;
break;
case OE_Medium:
case OE_Full:
effort = PNS_OPTIMIZER::MERGE_SEGMENTS;
break;
} }
if(Settings().SmartPads())
effort |= PNS_OPTIMIZER::SMART_PADS;
handleViaPlacement( initTrack ); if( wf == PNS_WALKAROUND::STUCK )
{
m_currentNode = m_shove->GetCurrentNode(); walkFull = walkFull.ClipToNearestObstacle( m_currentNode );
rv = true;
PNS_OPTIMIZER optimizer( m_currentNode ); } else if( m_placingVia && viaOk ) {
PNS_WALKAROUND walkaround( m_currentNode ); PNS_LAYERSET allLayers( 0, 15 );
PNS_VIA v1( walkFull.CPoint( -1 ), allLayers, m_viaDiameter );
walkFull.AppendVia( v1 );
}
walkaround.SetSolidsOnly( false ); PNS_OPTIMIZER::Optimize( &walkFull, effort, m_currentNode );
walkaround.SetIterationLimit( m_mode == RM_Walkaround ? 8 : 5 );
// walkaround.SetApproachCursor(true, aP);
PNS_WALKAROUND::WalkaroundStatus wf = walkaround.Route( initTrack, walkFull ); if( m_currentNode->CheckColliding(&walkFull) )
{
TRACEn(0, "strange, walk line colliding\n");
}
#if 0 m_head = walkFull;
aNewHead = walkFull;
return rv;
}
if( m_mode == RM_Walkaround ) bool PNS_LINE_PLACER::rhMarkObstacles ( const VECTOR2I& aP, PNS_LINE& aNewHead )
{
m_head.SetShape ( m_direction.BuildInitialTrace( m_p_start, aP ) );
if( m_placingVia )
{ {
// walkaround. PNS_LAYERSET allLayers( 0, 15 );
// PNSDisplayDebugLine (walkFull.GetCLine(), 4); PNS_VIA v1( m_head.CPoint( -1 ), allLayers, m_viaDiameter );
m_head.AppendVia( v1 );
if( wf == PNS_WALKAROUND::STUCK ) }
{
aNewHead = m_head; aNewHead = m_head;
aNewHead.SetShape( walkFull.GetCLine() );
aNewHead = aNewHead.ClipToNearestObstacle( m_currentNode ); return m_currentNode->CheckColliding( &m_head );
return false; }
}
aNewHead = m_head; bool PNS_LINE_PLACER::rhShoveOnly ( const VECTOR2I& aP, PNS_LINE& aNewHead )
aNewHead.SetShape( walkFull.GetCLine() ); {
SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace( m_p_start, aP );
PNS_LINE initTrack( m_head, line );
PNS_LINE walkSolids, l2;
// printf("nh w %d l %d\n", aNewHead.GetWidth(), aNewHead.GetLayers().Start()); handleViaPlacement( initTrack );
return true;
}
#endif m_currentNode = m_shove->CurrentNode();
PNS_OPTIMIZER optimizer( m_currentNode );
PNS_COST_ESTIMATOR cost_walk, cost_orig; PNS_WALKAROUND walkaround( m_currentNode, Router() );
walkaround.SetApproachCursor( false, aP );
walkaround.SetSolidsOnly( true ); walkaround.SetSolidsOnly( true );
walkaround.SetIterationLimit( 10 ); walkaround.SetIterationLimit( 10 );
PNS_WALKAROUND::WalkaroundStatus stat_solids = walkaround.Route( initTrack, walkSolids ); PNS_WALKAROUND::WalkaroundStatus stat_solids = walkaround.Route( initTrack, walkSolids );
optimizer.SetEffortLevel( PNS_OPTIMIZER::MERGE_SEGMENTS ); optimizer.SetEffortLevel( PNS_OPTIMIZER::MERGE_SEGMENTS );
optimizer.SetCollisionMask( PNS_ITEM::SOLID ); optimizer.SetCollisionMask ( PNS_ITEM::SOLID );
optimizer.Optimize( &walkSolids ); optimizer.Optimize( &walkSolids );
#if 0
optimizer.SetCollisionMask( -1 ); if( stat_solids == PNS_WALKAROUND::DONE )
optimizer.Optimize( &walkFull );
#endif
cost_orig.Add( initTrack );
cost_walk.Add( walkFull );
if( m_mode == RM_Smart || m_mode == RM_Shove )
{
PNS_LINE l2;
bool walk_better = cost_orig.IsBetter( cost_walk, 1.5, 10.0 );
walk_better = false;
#if 0
printf( "RtTrk width %d %d %d", initTrack.GetWidth(),
walkFull.GetWidth(), walkSolids.GetWidth() );
printf( "init-coll %d\n", m_currentNode->CheckColliding( &initTrack ) ? 1 : 0 );
printf( "total cost: walk cor %.0f len %.0f orig cor %.0f len %.0f walk-better %d\n",
cost_walk.GetCornerCost(), cost_walk.GetLengthCost(),
cost_orig.GetCornerCost(), cost_orig.GetLengthCost(),
walk_better );
#endif
if( m_mode == RM_Smart && wf == PNS_WALKAROUND::DONE && walk_better
&& walkFull.GetCLine().CPoint( -1 ) == initTrack.GetCLine().CPoint( -1 ) )
l2 = walkFull;
else if( stat_solids == PNS_WALKAROUND::DONE )
l2 = walkSolids; l2 = walkSolids;
else else
l2 = initTrack.ClipToNearestObstacle( m_shove->GetCurrentNode() ); l2 = initTrack.ClipToNearestObstacle( m_shove->CurrentNode() );
PNS_LINE l( m_tail ); PNS_LINE l( m_tail );
l.GetLine().Append( l2.GetCLine() ); l.Line().Append( l2.CLine() );
l.GetLine().Simplify(); l.Line().Simplify();
if( m_placingVia ) if( m_placingVia )
{ {
PNS_LAYERSET allLayers( 0, 15 ); PNS_LAYERSET allLayers( 0, 15 );
PNS_VIA v1( l.GetCLine().CPoint( -1 ), allLayers, m_viaDiameter ); PNS_VIA v1( l.CPoint( -1 ), allLayers, m_viaDiameter );
PNS_VIA v2( l2.GetCLine().CPoint( -1 ), allLayers, m_viaDiameter ); PNS_VIA v2( l2.CPoint( -1 ), allLayers, m_viaDiameter );
v1.SetDrill( m_viaDrill ); v1.SetDrill( m_viaDrill );
v2.SetDrill( m_viaDrill ); v2.SetDrill( m_viaDrill );
l.AppendVia( v1 ); l.AppendVia( v1 );
l2.AppendVia( v2 ); l2.AppendVia( v2 );
} }
l.Line().Simplify();
PNS_SHOVE::ShoveStatus status = m_shove->ShoveLines( &l ); // in certain, uncommon cases there may be loops in the head+tail, In such case, we don't shove to avoid
m_currentNode = m_shove->GetCurrentNode(); // screwing up the database.
if ( l.HasLoops() )
{
aNewHead = m_head;
return false;
}
PNS_SHOVE::ShoveStatus status = m_shove->ShoveLines( l );
if( status == PNS_SHOVE::SH_OK ) m_currentNode = m_shove->CurrentNode();
if( status == PNS_SHOVE::SH_OK )
{ {
optimizer.SetWorld( m_currentNode ); optimizer.SetWorld( m_currentNode );
optimizer.ClearCache();
optimizer.SetEffortLevel( PNS_OPTIMIZER::MERGE_OBTUSE | PNS_OPTIMIZER::SMART_PADS ); optimizer.SetEffortLevel( PNS_OPTIMIZER::MERGE_OBTUSE | PNS_OPTIMIZER::SMART_PADS );
optimizer.SetCollisionMask( -1 ); optimizer.SetCollisionMask( PNS_ITEM::ANY );
optimizer.Optimize( &l2 ); optimizer.Optimize( &l2 );
aNewHead = l2; aNewHead = l2;
...@@ -510,25 +541,59 @@ bool PNS_LINE_PLACER::routeHead( const VECTOR2I& aP, PNS_LINE& aNewHead, ...@@ -510,25 +541,59 @@ bool PNS_LINE_PLACER::routeHead( const VECTOR2I& aP, PNS_LINE& aNewHead,
walkaround.SetIterationLimit( 10 ); walkaround.SetIterationLimit( 10 );
walkaround.SetApproachCursor( true, aP ); walkaround.SetApproachCursor( true, aP );
walkaround.Route( initTrack, l2 ); walkaround.Route( initTrack, l2 );
aNewHead = l2.ClipToNearestObstacle( m_shove->GetCurrentNode() ); aNewHead = l2.ClipToNearestObstacle( m_shove->CurrentNode() );
// aNewHead = l2;
return false; return false;
} }
}
return false; return false;
} }
bool PNS_LINE_PLACER::routeHead( const VECTOR2I& aP, PNS_LINE& aNewHead )
{
switch( m_currentMode )
{
case RM_MarkObstacles:
return rhMarkObstacles( aP, aNewHead );
case RM_Walkaround:
return rhWalkOnly ( aP, aNewHead );
case RM_Shove:
return rhShoveOnly ( aP, aNewHead );
default:
break;
}
return false;
}
bool PNS_LINE_PLACER::optimizeTailHeadTransition() bool PNS_LINE_PLACER::optimizeTailHeadTransition()
{ {
SHAPE_LINE_CHAIN& head = m_head.GetLine();
SHAPE_LINE_CHAIN& tail = m_tail.GetLine(); PNS_LINE tmp = Trace();
if(PNS_OPTIMIZER::Optimize(&tmp, PNS_OPTIMIZER::FANOUT_CLEANUP, m_currentNode))
{
if(tmp.SegmentCount() < 1)
return false;
m_head = tmp;
m_p_start = tmp.CLine().CPoint( 0 );
m_direction = DIRECTION_45( tmp.CSegment( 0 ) );
m_tail.Line().Clear();
return true;
}
SHAPE_LINE_CHAIN& head = m_head.Line();
SHAPE_LINE_CHAIN& tail = m_tail.Line();
int tailLookbackSegments = 3;
const int TailLookbackSegments = 5; //if(m_currentMode() == RM_Walkaround)
// tailLookbackSegments = 10000;
int threshold = std::min( tail.PointCount(), TailLookbackSegments + 1 ); int threshold = std::min( tail.PointCount(), tailLookbackSegments + 1 );
if( tail.SegmentCount() < 3 ) if( tail.SegmentCount() < 3 )
return false; return false;
...@@ -536,8 +601,9 @@ bool PNS_LINE_PLACER::optimizeTailHeadTransition() ...@@ -536,8 +601,9 @@ bool PNS_LINE_PLACER::optimizeTailHeadTransition()
// assemble TailLookbackSegments tail segments with the current head // assemble TailLookbackSegments tail segments with the current head
SHAPE_LINE_CHAIN opt_line = tail.Slice( -threshold, -1 ); SHAPE_LINE_CHAIN opt_line = tail.Slice( -threshold, -1 );
opt_line.Append( head ); int end = std::min(2, head.PointCount() - 1 );
// opt_line.Simplify();
opt_line.Append( head.Slice( 0, end ) );
PNS_LINE new_head( m_tail, opt_line ); PNS_LINE new_head( m_tail, opt_line );
...@@ -545,20 +611,18 @@ bool PNS_LINE_PLACER::optimizeTailHeadTransition() ...@@ -545,20 +611,18 @@ bool PNS_LINE_PLACER::optimizeTailHeadTransition()
// If so, replace the (threshold) last tail points and the head with // If so, replace the (threshold) last tail points and the head with
// the optimized line // the optimized line
// if(PNS_OPTIMIZER::Optimize(&new_head, PNS_OPTIMIZER::MERGE_SEGMENTS)) if(PNS_OPTIMIZER::Optimize(&new_head, PNS_OPTIMIZER::MERGE_OBTUSE, m_currentNode))
if( new_head.MergeSegments() )
{ {
PNS_LINE tmp( m_tail, opt_line ); PNS_LINE tmp( m_tail, opt_line );
TRACE( 0, "Placer: optimize tail-head [%d]", threshold ); TRACE( 0, "Placer: optimize tail-head [%d]", threshold );
head.Clear(); head.Clear();
tail.Replace( -threshold, -1, new_head.GetCLine() ); tail.Replace( -threshold, -1, new_head.CLine() );
tail.Simplify(); tail.Simplify();
m_p_start = new_head.GetCLine().CPoint( -1 ); m_p_start = new_head.CLine().CPoint( -1 );
m_direction = DIRECTION_45( new_head.GetCLine().CSegment( -1 ) ); m_direction = DIRECTION_45( new_head.CSegment( -1 ) );
return true; return true;
} }
...@@ -576,26 +640,24 @@ void PNS_LINE_PLACER::routeStep( const VECTOR2I& aP ) ...@@ -576,26 +640,24 @@ void PNS_LINE_PLACER::routeStep( const VECTOR2I& aP )
PNS_LINE new_head; PNS_LINE new_head;
m_follow_mouse = true;
TRACE( 2, "INIT-DIR: %s head: %d, tail: %d segs\n", TRACE( 2, "INIT-DIR: %s head: %d, tail: %d segs\n",
m_initial_direction.Format().c_str() % m_head.GetCLine().SegmentCount() % m_initial_direction.Format().c_str() % m_head.SegmentCount() %
m_tail.GetCLine().SegmentCount() ); m_tail.SegmentCount() );
for( i = 0; i < n_iter; i++ ) for( i = 0; i < n_iter; i++ )
{ {
if( !go_back && m_follow_mouse ) if( !go_back && Settings().FollowMouse() )
reduceTail( aP ); reduceTail( aP );
go_back = false; go_back = false;
if( !routeHead( aP, new_head, true ) ) if( !routeHead( aP, new_head ) )
fail = true; fail = true;
if( !new_head.Is45Degree() ) if( !new_head.Is45Degree() )
fail = true; fail = true;
if( !m_follow_mouse ) if( !Settings().FollowMouse() )
return; return;
m_head = new_head; m_head = new_head;
...@@ -615,48 +677,36 @@ void PNS_LINE_PLACER::routeStep( const VECTOR2I& aP ) ...@@ -615,48 +677,36 @@ void PNS_LINE_PLACER::routeStep( const VECTOR2I& aP )
if( !fail ) if( !fail )
{ {
if( optimizeTailHeadTransition() ) if( optimizeTailHeadTransition() )
return; return;
mergeHead(); mergeHead();
} }
} }
bool PNS_LINE_PLACER::Route( const VECTOR2I& aP ) bool PNS_LINE_PLACER::route( const VECTOR2I& aP )
{ {
if( m_smooth_mouse ) routeStep( aP );
{
VECTOR2I p_cur = m_p_start;
VECTOR2I step = (aP - m_p_start).Resize( m_smoothing_step );
do
{
if( (p_cur - aP).EuclideanNorm() <= m_smoothing_step )
p_cur = aP;
else
p_cur += step;
routeStep( p_cur );
} while( p_cur != aP );
}
else
routeStep( aP );
return CurrentEnd() == aP; return CurrentEnd() == aP;
} }
const PNS_LINE PNS_LINE_PLACER::GetTrace() const const PNS_LINE PNS_LINE_PLACER::Trace() const
{ {
PNS_LINE tmp( m_head ); PNS_LINE tmp( m_head );
tmp.SetShape( m_tail.GetCLine() ); tmp.SetShape( m_tail.CLine() );
tmp.GetLine().Append( m_head.GetCLine() ); tmp.Line().Append( m_head.CLine() );
tmp.GetLine().Simplify(); tmp.Line().Simplify();
return tmp; return tmp;
} }
const PNS_ITEMSET PNS_LINE_PLACER::Traces()
{
m_currentTrace = Trace();
return PNS_ITEMSET( &m_currentTrace );
}
void PNS_LINE_PLACER::FlipPosture() void PNS_LINE_PLACER::FlipPosture()
{ {
...@@ -664,15 +714,301 @@ void PNS_LINE_PLACER::FlipPosture() ...@@ -664,15 +714,301 @@ void PNS_LINE_PLACER::FlipPosture()
m_direction = m_direction.Right(); m_direction = m_direction.Right();
} }
PNS_NODE* PNS_LINE_PLACER::CurrentNode(bool aLoopsRemoved) const
{
if(aLoopsRemoved && m_lastNode)
return m_lastNode;
return m_currentNode;
}
void PNS_LINE_PLACER::GetUpdatedItems( PNS_NODE::ItemVector& aRemoved,
PNS_NODE::ItemVector& aAdded ) void PNS_LINE_PLACER::splitAdjacentSegments( PNS_NODE* aNode, PNS_ITEM* aSeg, const VECTOR2I& aP )
{
if( aSeg && aSeg->OfKind( PNS_ITEM::SEGMENT ) )
{
PNS_JOINT *jt = aNode->FindJoint( aP, aSeg );
if( jt && jt->LinkCount() >= 1 )
return;
PNS_SEGMENT* s_old = static_cast<PNS_SEGMENT*>( aSeg );
PNS_SEGMENT* s_new[2];
s_new[0] = s_old->Clone();
s_new[1] = s_old->Clone();
s_new[0]->SetEnds( s_old->Seg().A, aP );
s_new[1]->SetEnds( aP, s_old->Seg().B );
aNode->Remove( s_old );
aNode->Add( s_new[0], true );
aNode->Add( s_new[1], true );
}
}
void PNS_LINE_PLACER::SetLayer(int aLayer)
{ {
return m_shove->GetCurrentNode()->GetUpdatedItems( aRemoved, aAdded ); m_currentLayer = aLayer;
} }
void PNS_LINE_PLACER::SetWidth(int aWidth)
{
m_currentWidth = aWidth;
}
PNS_NODE* PNS_LINE_PLACER::GetCurrentNode() const void PNS_LINE_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem )
{ {
return m_shove->GetCurrentNode(); VECTOR2I p( aP );
static int unknowNetIdx = 0; // -10000;
int net = -1;
m_lastNode = NULL;
m_placingVia = false;
m_startsOnVia = false;
bool splitSeg = false;
if( Router()->SnappingEnabled() )
p = Router()->SnapToItem( aStartItem, aP, splitSeg );
if( !aStartItem || aStartItem->Net() < 0 )
net = unknowNetIdx--;
else
net = aStartItem->Net();
m_currentStart = p;
m_originalStart = p;
m_currentEnd = p;
m_currentNet = net;
PNS_NODE *rootNode = Router()->GetWorld()->Branch();
if( splitSeg )
splitAdjacentSegments( rootNode, aStartItem, p );
setWorld ( rootNode );
setInitialDirection( Settings().InitialDirection() );
startPlacement( p, m_currentNet, m_currentWidth, m_currentLayer );
} }
void PNS_LINE_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem )
{
PNS_LINE current;
VECTOR2I p = aP;
int eiDepth = -1;
if(aEndItem && aEndItem->Owner())
eiDepth = aEndItem->Owner()->Depth();
if( m_lastNode )
{
delete m_lastNode;
m_lastNode = NULL;
}
route( p );
current = Trace();
if(!current.PointCount())
m_currentEnd = m_p_start;
else
m_currentEnd = current.CLine().CPoint(-1);
PNS_NODE *latestNode = m_currentNode;
m_lastNode = latestNode->Branch();
if(eiDepth >= 0 && aEndItem && latestNode->Depth() > eiDepth && current.SegmentCount() && current.CPoint(-1) == aP)
{
splitAdjacentSegments( m_lastNode, aEndItem, current.CPoint(-1) );
if(Settings().RemoveLoops())
removeLoops( m_lastNode, &current );
}
updateLeadingRatLine();
}
bool PNS_LINE_PLACER::FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem )
{
bool realEnd = false;
int lastV;
PNS_LINE pl = Trace();
if (m_currentMode == RM_MarkObstacles &&
!Settings().CanViolateDRC() &&
m_world->CheckColliding( &pl ) )
return false;
const SHAPE_LINE_CHAIN& l = pl.CLine();
if( !l.SegmentCount() )
return true;
VECTOR2I p_pre_last = l.CPoint( -1 );
const VECTOR2I p_last = l.CPoint( -1 );
DIRECTION_45 d_last( l.CSegment( -1 ) );
if( l.PointCount() > 2 )
p_pre_last = l.CPoint( -2 );
if( aEndItem && m_currentNet >= 0 && m_currentNet == aEndItem->Net() )
realEnd = true;
if(realEnd || m_placingVia)
lastV = l.SegmentCount();
else
lastV = std::max( 1, l.SegmentCount() - 1 );
PNS_SEGMENT* lastSeg = NULL;
for( int i = 0; i < lastV; i++ )
{
const SEG& s = pl.CSegment( i );
PNS_SEGMENT* seg = new PNS_SEGMENT( s, m_currentNet );
seg->SetWidth( pl.Width() );
seg->SetLayer( m_currentLayer );
m_lastNode->Add( seg );
lastSeg = seg;
}
if( pl.EndsWithVia() )
m_lastNode->Add( pl.Via().Clone() );
if(realEnd)
simplifyNewLine ( m_lastNode, lastSeg );
Router()->CommitRouting ( m_lastNode );
m_lastNode = NULL;
if(!realEnd)
{
setInitialDirection( d_last );
VECTOR2I p_start = m_placingVia ? p_last : p_pre_last;
if( m_placingVia )
m_currentLayer = Router()->NextCopperLayer( true );
setWorld ( Router()->GetWorld()->Branch() );
startPlacement( p_start, m_head.Net(), m_head.Width(), m_currentLayer );
m_startsOnVia = m_placingVia;
m_placingVia = false;
}
return realEnd;
}
void PNS_LINE_PLACER::removeLoops( PNS_NODE* aNode, PNS_LINE* aLatest )
{
if(!aLatest->SegmentCount())
return;
aNode->Add( aLatest, true );
for(int s = 0; s < aLatest->SegmentCount(); s++ )
{
PNS_SEGMENT *seg = (*aLatest->LinkedSegments())[s];
PNS_LINE* ourLine = aNode->AssembleLine( seg ) ;
PNS_JOINT a, b;
std::vector<PNS_LINE*> lines;
aNode->FindLineEnds( ourLine, a, b );
if( a == b )
{
aNode->FindLineEnds( aLatest, a, b);
}
aNode->FindLinesBetweenJoints( a, b, lines );
int removedCount = 0;
int total = 0;
BOOST_FOREACH( PNS_LINE* line, lines )
{
total++;
if( !( line->ContainsSegment( seg ) ) && line->SegmentCount() )
{
Router()->DisplayDebugLine ( line->CLine(), -1, 10000 );
for(int i = 0; i < line->PointCount(); i++ )
Router()->DisplayDebugPoint ( line->CPoint(i), -1 );
aNode->Remove( line );
removedCount ++;
}
}
TRACE(0, "total segs removed: %d/%d\n", removedCount % total);
delete ourLine;
}
aNode->Remove( aLatest );
}
void PNS_LINE_PLACER::simplifyNewLine ( PNS_NODE *aNode, PNS_SEGMENT *aLatest )
{
PNS_LINE *l = aNode->AssembleLine( aLatest) ;
SHAPE_LINE_CHAIN simplified ( l->CLine() );
simplified.Simplify();
if(simplified.PointCount() != l->PointCount())
{
std::auto_ptr<PNS_LINE> lnew ( l->Clone() );
aNode -> Remove(l);
lnew->SetShape(simplified);
aNode -> Add( lnew.get() );
}
}
void PNS_LINE_PLACER::UpdateSizes( const PNS_ROUTING_SETTINGS& aSettings )
{
int trackWidth = aSettings.GetTrackWidth();
m_head.SetWidth( trackWidth );
m_tail.SetWidth( trackWidth );
m_viaDiameter = aSettings.GetViaDiameter();
m_viaDrill = aSettings.GetViaDrill();
}
void PNS_LINE_PLACER::updateLeadingRatLine()
{
PNS_LINE current = Trace();
if(! current.PointCount() )
return;
std::auto_ptr<PNS_NODE> tmpNode ( m_lastNode->Branch() );
tmpNode->Add( &current );
PNS_JOINT *jt = tmpNode->FindJoint( current.CPoint(-1), current.Layers().Start(), current.Net() );
if(!jt)
return;
int anchor;
PNS_ITEM *it = tmpNode->NearestUnconnectedItem ( jt, &anchor );
if(it)
{
SHAPE_LINE_CHAIN lc;
lc.Append ( current.CPoint(-1) );
lc.Append ( it->Anchor(anchor) );
Router()->DisplayDebugLine( lc, 5, 10000 );
}
}
\ No newline at end of file
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_LINE_PLACER_H #ifndef __PNS_LINE_PLACER_H
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,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_routing_settings.h" #include "pns_algo_base.h"
class PNS_ROUTER; class PNS_ROUTER;
class PNS_SHOVE; class PNS_SHOVE;
...@@ -39,29 +39,156 @@ class PNS_ROUTER_BASE; ...@@ -39,29 +39,156 @@ class PNS_ROUTER_BASE;
/** /**
* Class PNS_LINE_PLACER * Class PNS_LINE_PLACER
* *
* Interactively routes a single track. Runs shove and walkaround * Single track placement algorithm. Interactively routes a track.
* algorithms when needed. * Applies shove and walkaround algorithms when needed.
*/ */
class PNS_LINE_PLACER class PNS_LINE_PLACER : public PNS_ALGO_BASE
{ {
public: public:
PNS_LINE_PLACER( PNS_NODE* aWorld ); PNS_LINE_PLACER( PNS_ROUTER *aRouter );
~PNS_LINE_PLACER(); ~PNS_LINE_PLACER();
///> Appends a via at the end of currently placed line. /**
void AddVia( bool aEnabled, int aDiameter, int aDrill ) * Function Start()
*
* Starts routing a single track at point aP, taking item aStartItem as anchor
* (unless NULL).
*/
void 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).
*/
void 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 AddVia()
*
* Enables/disables a via at the end of currently routed trace.
* @param aEnabled if true, a via is attached during placement
* @param aDiameter diameter of the via
* @param aDrill drill of the via
*/
void AddVia( bool aEnabled, int aDiameter, int aDrill );
/**
* Function SetLayer()
*
* Sets the current routing layer.
*/
void SetLayer ( int aLayer );
/**
* Function SetWidth()
*
* Sets the current track width.
*/
void SetWidth ( int aWidth );
/**
* Function Head()
*
* Returns the "head" of the line being placed, that is the volatile part
* that has not "settled" yet.
*/
const PNS_LINE& Head() const { return m_head; }
/**
* Function Tail()
*
* Returns the "tail" of the line being placed, the part which has already wrapped around
* and shoved some obstacles.
*/
const PNS_LINE& Tail() const { return m_tail; }
/**
* Function Trace()
*
* Returns the complete routed line.
*/
const PNS_LINE Trace() const;
/**
* 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
{ {
m_viaDiameter = aDiameter; return m_currentEnd;
m_viaDrill = aDrill;
m_placingVia = aEnabled;
} }
///> Starts placement of a line at point aStart. /**
void StartPlacement( const VECTOR2I& aStart, int aNet, int aWidth, int aLayer ); * 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_ROUTING_SETTINGS& aSettings );
private:
/** /**
* Function Route() * Function route()
* *
* Re-routes the current track to point aP. Returns true, when routing has * 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 * completed successfully (i.e. the trace end has reached point aP), and false
...@@ -70,55 +197,76 @@ public: ...@@ -70,55 +197,76 @@ public:
* @param aP ending point of current route. * @param aP ending point of current route.
* @return true, if the routing is complete. * @return true, if the routing is complete.
*/ */
bool Route( const VECTOR2I& aP );
///> Sets initial routing direction/posture
void SetInitialDirection( const DIRECTION_45& aDirection );
void ApplySettings( const PNS_ROUTING_SETTINGS& aSettings );
///> Returns the "head" of the line being placed, that is the volatile part bool route( const VECTOR2I& aP );
///> that has not been settled yet
const PNS_LINE& GetHead() const { return m_head; }
///> Returns the "tail" of the line being placed the part that has been
///> fixed already (follow mouse mode only)
const PNS_LINE& GetTail() const { return m_tail; }
///> Returns the whole routed line /**
const PNS_LINE GetTrace() const; * Function updateLeadingRatLine()
*
///> Returns the current end of the line being placed. It may not be equal * Draws the "leading" ratsnest line, which connects the end of currently
///> to the cursor position due to collisions. * routed track and the nearest yet unrouted item. If the routing for
const VECTOR2I& CurrentEnd() const * current net is complete, draws nothing.
{ */
if( m_head.GetCLine().PointCount() > 0 ) void updateLeadingRatLine();
return m_head.GetCLine().CPoint( -1 );
else if( m_tail.GetCLine().PointCount() > 0 ) /**
return m_tail.GetCLine().CPoint( -1 ); * Function setWorld()
else *
return m_p_start; * Sets the board to route.
} */
void setWorld ( PNS_NODE *aWorld );
/**
* Function startPlacement()
*
* Initializes placement of a new line with given parameters.
*/
void startPlacement( const VECTOR2I& aStart, int aNet, int aWidth, int aLayer );
///> Returns all items in the world that have been affected by the routing /**
///> operation. Used to update data structures of the host application * Function setInitialDirection()
void GetUpdatedItems( PNS_NODE::ItemVector& aRemoved, *
PNS_NODE::ItemVector& aAdded ); * Sets preferred direction of the very first track segment to be laid.
* Used by posture switching mechanism.
*/
void setInitialDirection( const DIRECTION_45& aDirection );
///> Toggles the current posture (straight/diagonal) of the trace head. /**
void FlipPosture(); * Function splitAdjacentSegments()
*
* Checks if point aP lies on segment aSeg. If so, splits the segment in two,
* forming a joint at aP and stores updated topology in node aNode.
*/
void splitAdjacentSegments( PNS_NODE* aNode, PNS_ITEM* aSeg, const VECTOR2I& aP );
///> Returns the most recent world state /**
PNS_NODE* GetCurrentNode() const; * Function removeLoops()
*
* Searches aNode for traces concurrent to aLatest and removes them. Updated
* topology is stored in aNode.
*/
void removeLoops( PNS_NODE* aNode, PNS_LINE* aLatest );
private: /**
static const double m_shoveLengthThreshold; * Function simplifyNewLine()
*
* Assembles a line starting from segment aLatest, removes collinear segments
* and redundant vertexes. If a simplification bhas been found, replaces the
* old line with the simplified one in aNode.
*/
void simplifyNewLine ( PNS_NODE *aNode, PNS_SEGMENT *aLatest );
/**
* Function handleViaPlacement()
*
* Attempts to find a spot to place the via at the end of line aHead.
*/
bool handleViaPlacement( PNS_LINE& aHead ); bool handleViaPlacement( PNS_LINE& aHead );
/** /**
* Function checkObtusity() * Function checkObtusity()
* *
* Helper that checks if segments a and b form an obtuse angle * Helper function, checking if segments a and b form an obtuse angle
* (in 45-degree regime). * (in 45-degree regime).
* @return true, if angle (a, b) is obtuse * @return true, if angle (a, b) is obtuse
*/ */
...@@ -163,8 +311,6 @@ private: ...@@ -163,8 +311,6 @@ private:
*/ */
bool reduceTail( const VECTOR2I& aEnd ); bool reduceTail( const VECTOR2I& aEnd );
void fixHeadPosture();
/** /**
* Function optimizeTailHeadTransition() * Function optimizeTailHeadTransition()
* *
...@@ -182,8 +328,7 @@ private: ...@@ -182,8 +328,7 @@ private:
* around all colliding solid or non-movable items. Movable segments are * around all colliding solid or non-movable items. Movable segments are
* ignored, as they'll be handled later by the shove algorithm. * ignored, as they'll be handled later by the shove algorithm.
*/ */
bool routeHead( const VECTOR2I& aP, PNS_LINE& aNewHead, bool routeHead( const VECTOR2I& aP, PNS_LINE& aNewHead);
bool aCwWalkaround = true );
/** /**
* Function routeStep() * Function routeStep()
...@@ -194,18 +339,15 @@ private: ...@@ -194,18 +339,15 @@ private:
*/ */
void routeStep( const VECTOR2I& aP ); void routeStep( const VECTOR2I& aP );
///> routing mode (walkaround, shove, etc.) ///< route step, walkaround mode
PNS_MODE m_mode; bool rhWalkOnly ( const VECTOR2I& aP, PNS_LINE& aNewHead);
///> follow mouse trail by attaching new segments to the head
///> as the cursor moves
bool m_follow_mouse;
///> mouse smoothing active ///< route step, shove mode
bool m_smooth_mouse; bool rhShoveOnly ( const VECTOR2I& aP, PNS_LINE& aNewHead);
///> mouse smoothing step (in world units) ///< route step, mark obstacles mode
int m_smoothing_step; bool rhMarkObstacles ( const VECTOR2I& aP, PNS_LINE& aNewHead );
///> current routing direction ///> current routing direction
DIRECTION_45 m_direction; DIRECTION_45 m_direction;
...@@ -235,6 +377,9 @@ private: ...@@ -235,6 +377,9 @@ private:
///> Current world state ///> Current world state
PNS_NODE* m_currentNode; PNS_NODE* m_currentNode;
///> Postprocessed world state (including marked collisions & removed loops)
PNS_NODE* m_lastNode;
///> Are we placing a via? ///> Are we placing a via?
bool m_placingVia; bool m_placingVia;
...@@ -244,11 +389,17 @@ private: ...@@ -244,11 +389,17 @@ private:
///> current via drill ///> current via drill
int m_viaDrill; int m_viaDrill;
///> walkaround algorithm iteration limit int m_currentWidth;
int m_walkaroundIterationLimit; int m_currentNet;
int m_currentLayer;
bool m_startsOnVia;
VECTOR2I m_originalStart, m_currentEnd, m_currentStart;
PNS_LINE m_currentTrace;
PNS_MODE m_currentMode;
///> smart pads optimizer enabled.
bool m_smartPads;
}; };
#endif // __PNS_LINE_PLACER_H #endif // __PNS_LINE_PLACER_H
/*
* 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 "pns_logger.h"
#include "pns_item.h"
#include "pns_via.h"
#include "pns_line.h"
#include "pns_segment.h"
#include "pns_solid.h"
#include <geometry/shape.h>
#include <geometry/shape_line_chain.h>
#include <geometry/shape_rect.h>
#include <geometry/shape_circle.h>
PNS_LOGGER::PNS_LOGGER( )
{
m_groupOpened = false;
}
PNS_LOGGER::~PNS_LOGGER()
{
}
void PNS_LOGGER::Clear()
{
m_theLog.str(std::string());
m_groupOpened = false;
}
void PNS_LOGGER::NewGroup ( const std::string& name, int iter)
{
if(m_groupOpened)
m_theLog << "endgroup" << std::endl;
m_theLog << "group " << name << " " << iter << std::endl;
m_groupOpened = true;
}
void PNS_LOGGER::EndGroup()
{
if(!m_groupOpened)
return;
m_groupOpened = false;
m_theLog << "endgroup" << std::endl;
}
void PNS_LOGGER::Log ( const PNS_ITEM *item, int kind, const std::string name )
{
m_theLog << "item " << kind << " " << name << " ";
m_theLog << item->Net() << " " << item->Layers().Start() << " " << item->Layers().End() << " " << item->Marker() << " " << item->Rank();
switch(item->Kind())
{
case PNS_ITEM::LINE:
{
PNS_LINE *l = (PNS_LINE *) item;
m_theLog << " line ";
m_theLog << l->Width() << " " << (l->EndsWithVia() ? 1 : 0) << " ";
dumpShape ( l->Shape() );
m_theLog << std::endl;
break;
}
case PNS_ITEM::VIA:
{
m_theLog << " via 0 0 ";
dumpShape ( item->Shape() );
m_theLog << std::endl;
break;
}
case PNS_ITEM::SEGMENT:
{
PNS_SEGMENT *s =(PNS_SEGMENT *) item;
m_theLog << " line ";
m_theLog << s->Width() << " 0 linechain 2 0 " << s->Seg().A.x << " " <<s->Seg().A.y << " " << s->Seg().B.x << " " <<s->Seg().B.y << std::endl;
break;
}
case PNS_ITEM::SOLID:
{
PNS_SOLID *s = (PNS_SOLID*) item;
m_theLog << " solid 0 0 ";
dumpShape ( s->Shape() );
m_theLog << std::endl;
break;
}
default:
break;
}
}
void PNS_LOGGER::Log ( const SHAPE_LINE_CHAIN *l, int kind, const std::string name )
{
m_theLog << "item " << kind << " " << name << " ";
m_theLog << 0 << " " << 0 << " " << 0 << " " << 0 << " " << 0;
m_theLog << " line ";
m_theLog << 0 << " " << 0 << " ";
dumpShape ( l );
m_theLog << std::endl;
}
void PNS_LOGGER::Log ( const VECTOR2I& start, const VECTOR2I& end, int kind , const std::string name)
{
}
void PNS_LOGGER::dumpShape ( const SHAPE * sh )
{
switch(sh->Type())
{
case SH_LINE_CHAIN:
{
const SHAPE_LINE_CHAIN *lc = (const SHAPE_LINE_CHAIN *) sh;
m_theLog << "linechain " << lc->PointCount() << " " << (lc->IsClosed() ? 1 : 0) << " ";
for(int i = 0; i < lc->PointCount(); i++)
m_theLog << lc->CPoint(i).x << " " << lc->CPoint(i).y << " ";
break;
}
case SH_CIRCLE:
{
const SHAPE_CIRCLE *c = (const SHAPE_CIRCLE *) sh;
m_theLog << "circle " << c->GetCenter().x << " " << c->GetCenter().y << " " << c->GetRadius();
break;
}
case SH_RECT:
{
const SHAPE_RECT *r = (const SHAPE_RECT *) sh;
m_theLog << "rect " << r->GetPosition().x << " " << r->GetPosition().y << " " << r->GetSize().x << " " <<r->GetSize().y;
break;
}
case SH_SEGMENT:
{
const SHAPE_SEGMENT *s = (const SHAPE_SEGMENT *) sh;
m_theLog << "linechain 2 0 " << s->GetSeg().A.x << " " << s->GetSeg().A.y << " " << s->GetSeg().B.x << " " << s->GetSeg().B.y;
break;
}
default:
break;
}
}
void PNS_LOGGER::Save ( const std::string& filename )
{
EndGroup();
FILE *f=fopen(filename.c_str(),"wb");
printf("Saving to '%s' [%p]\n", filename.c_str(), f);
const std::string s = m_theLog.str();
fwrite(s.c_str(), 1, s.length(), f);
fclose(f);
}
/*
* 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_LOGGER_H
#define __PNS_LOGGER_H
#include <cstdio>
#include <vector>
#include <string>
#include <sstream>
#include <math/vector2d.h>
class PNS_ITEM;
class SHAPE_LINE_CHAIN;
class SHAPE;
class PNS_LOGGER {
public:
PNS_LOGGER();
~PNS_LOGGER();
void Save ( const std::string& filename );
void Clear();
void NewGroup ( const std::string& name, int iter = 0);
void EndGroup();
void Log ( const PNS_ITEM *item, int kind = 0, const std::string name = std::string () );
void Log ( const SHAPE_LINE_CHAIN *l, int kind = 0, const std::string name = std::string () );
void Log ( const VECTOR2I& start, const VECTOR2I& end, int kind = 0, const std::string name = std::string () );
private:
void dumpShape ( const SHAPE* sh );
bool m_groupOpened;
std::stringstream m_theLog;
};
#endif
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <vector> #include <vector>
...@@ -36,31 +36,40 @@ ...@@ -36,31 +36,40 @@
#include "pns_solid.h" #include "pns_solid.h"
#include "pns_joint.h" #include "pns_joint.h"
#include "pns_index.h" #include "pns_index.h"
#include "pns_router.h"
using boost::unordered_set; using boost::unordered_set;
using boost::unordered_map; using boost::unordered_map;
#ifdef DEBUG
static boost::unordered_set<PNS_NODE*> allocNodes; static boost::unordered_set<PNS_NODE*> allocNodes;
#endif
PNS_NODE::PNS_NODE() PNS_NODE::PNS_NODE()
{ {
// printf("MakeNode [%p, total = %d]\n", this, allocNodes.size()); TRACE( 0, "PNS_NODE::create %p", this );
m_depth = 0;
m_root = this; m_root = this;
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;
#ifdef DEBUG
allocNodes.insert( this ); allocNodes.insert( this );
#endif
} }
PNS_NODE::~PNS_NODE() PNS_NODE::~PNS_NODE()
{ {
TRACE( 0, "PNS_NODE::delete %p", this );
if( !m_children.empty() ) if( !m_children.empty() )
{ {
TRACEn( 0, "attempting to free a node that has kids.\n" ); TRACEn( 0, "attempting to free a node that has kids.\n" );
assert( false ); assert( false );
} }
#ifdef DEBUG
if( allocNodes.find( this ) == allocNodes.end() ) if( allocNodes.find( this ) == allocNodes.end() )
{ {
TRACEn( 0, "attempting to free an already-free'd node.\n" ); TRACEn( 0, "attempting to free an already-free'd node.\n" );
...@@ -69,6 +78,8 @@ PNS_NODE::~PNS_NODE() ...@@ -69,6 +78,8 @@ PNS_NODE::~PNS_NODE()
allocNodes.erase( this ); allocNodes.erase( this );
#endif
for( PNS_INDEX::ItemSet::iterator i = m_index->begin(); for( PNS_INDEX::ItemSet::iterator i = m_index->begin();
i != m_index->end(); ++i ) i != m_index->end(); ++i )
if( (*i)->BelongsTo( this ) ) if( (*i)->BelongsTo( this ) )
...@@ -81,30 +92,19 @@ PNS_NODE::~PNS_NODE() ...@@ -81,30 +92,19 @@ PNS_NODE::~PNS_NODE()
int PNS_NODE::GetClearance( const PNS_ITEM* a, const PNS_ITEM* b ) const int PNS_NODE::GetClearance( const PNS_ITEM* a, const PNS_ITEM* b ) const
{ {
int clearance = (*m_clearanceFunctor)( a, b ); return (*m_clearanceFunctor)( a, b );
if( a->OfKind( PNS_ITEM::SEGMENT ) )
clearance += static_cast<const PNS_SEGMENT*>(a)->GetWidth() / 2;
if( a->OfKind( PNS_ITEM::LINE ) )
clearance += static_cast<const PNS_LINE*>(a)->GetWidth() / 2;
if( b->OfKind( PNS_ITEM::SEGMENT ) )
clearance += static_cast<const PNS_SEGMENT*>(b)->GetWidth() / 2;
if( b->OfKind( PNS_ITEM::LINE ) )
clearance += static_cast<const PNS_LINE*>(b)->GetWidth() / 2;
return clearance;
} }
PNS_NODE* PNS_NODE::Branch() PNS_NODE* PNS_NODE::Branch()
{ {
PNS_NODE* child = new PNS_NODE; PNS_NODE* child = new PNS_NODE;
TRACE( 0, "PNS_NODE::branch %p (parent %p)", child % this );
m_children.push_back( child ); m_children.push_back( child );
child->m_depth = m_depth + 1;
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;
...@@ -173,14 +173,21 @@ struct PNS_NODE::obstacleVisitor ...@@ -173,14 +173,21 @@ struct PNS_NODE::obstacleVisitor
///> number of items found so far ///> number of items found so far
int m_matchCount; int m_matchCount;
///> additional clearance
int m_extraClearance;
obstacleVisitor( PNS_NODE::Obstacles& aTab, const PNS_ITEM* aItem, obstacleVisitor( PNS_NODE::Obstacles& aTab, const PNS_ITEM* aItem,
int aKindMask ) : int aKindMask ) :
m_tab( aTab ), m_tab( aTab ),
m_item( aItem ), m_item( aItem ),
m_kindMask( aKindMask ), m_kindMask( aKindMask ),
m_limitCount( -1 ), m_limitCount( -1 ),
m_matchCount( 0 ) m_matchCount( 0 ),
{}; m_extraClearance( 0 )
{
if(aItem->Kind() == PNS_ITEM::LINE)
m_extraClearance += static_cast<const PNS_LINE *>(aItem)->Width() / 2;
};
void SetCountLimit( int aLimit ) void SetCountLimit( int aLimit )
{ {
...@@ -203,7 +210,10 @@ struct PNS_NODE::obstacleVisitor ...@@ -203,7 +210,10 @@ struct PNS_NODE::obstacleVisitor
if( m_override && m_override->overrides( aItem ) ) if( m_override && m_override->overrides( aItem ) )
return true; return true;
int clearance = m_node->GetClearance( aItem, m_item ); int clearance = m_extraClearance + m_node->GetClearance( aItem, m_item );
if(aItem->Kind() == PNS_ITEM::LINE)
clearance += static_cast<PNS_LINE *>(aItem)->Width() / 2;
if( !aItem->Collide( m_item, clearance ) ) if( !aItem->Collide( m_item, clearance ) )
return true; return true;
...@@ -228,12 +238,14 @@ int PNS_NODE::QueryColliding( const PNS_ITEM* aItem, ...@@ -228,12 +238,14 @@ int PNS_NODE::QueryColliding( const PNS_ITEM* aItem,
{ {
obstacleVisitor visitor( aObstacles, aItem, aKindMask ); obstacleVisitor visitor( aObstacles, aItem, aKindMask );
#ifdef DEBUG
assert( allocNodes.find( this ) != allocNodes.end() ); assert( allocNodes.find( this ) != allocNodes.end() );
#endif
visitor.SetCountLimit( aLimitCount ); visitor.SetCountLimit( aLimitCount );
visitor.SetWorld( this, NULL ); visitor.SetWorld( this, NULL );
// first, look for colliding items ourselves // first, look for colliding items in the local index
m_index->Query( aItem, m_maxClearance, visitor ); m_index->Query( aItem, m_maxClearance, visitor );
// if we haven't found enough items, look in the root branch as well. // if we haven't found enough items, look in the root branch as well.
...@@ -252,7 +264,7 @@ PNS_NODE::OptObstacle PNS_NODE::NearestObstacle( const PNS_LINE* aItem, int aKin ...@@ -252,7 +264,7 @@ PNS_NODE::OptObstacle PNS_NODE::NearestObstacle( const PNS_LINE* aItem, int aKin
Obstacles obs_list; Obstacles obs_list;
bool found_isects = false; bool found_isects = false;
const SHAPE_LINE_CHAIN& line = aItem->GetCLine(); const SHAPE_LINE_CHAIN& line = aItem->CLine();
obs_list.reserve( 100 ); obs_list.reserve( 100 );
...@@ -265,7 +277,7 @@ PNS_NODE::OptObstacle PNS_NODE::NearestObstacle( const PNS_LINE* aItem, int aKin ...@@ -265,7 +277,7 @@ PNS_NODE::OptObstacle PNS_NODE::NearestObstacle( const PNS_LINE* aItem, int aKin
} }
if( aItem->EndsWithVia() ) if( aItem->EndsWithVia() )
n += QueryColliding( &aItem->GetVia(), obs_list, aKindMask ); n += QueryColliding( &aItem->Via(), obs_list, aKindMask );
// if(! QueryColliding ( aItem, obs_list, aKindMask )) // if(! QueryColliding ( aItem, obs_list, aKindMask ))
if( !n ) if( !n )
...@@ -286,20 +298,20 @@ PNS_NODE::OptObstacle PNS_NODE::NearestObstacle( const PNS_LINE* aItem, int aKin ...@@ -286,20 +298,20 @@ PNS_NODE::OptObstacle PNS_NODE::NearestObstacle( const PNS_LINE* aItem, int aKin
int clearance = GetClearance( obs.item, &aLine ); int clearance = GetClearance( obs.item, &aLine );
SHAPE_LINE_CHAIN hull = obs.item->Hull( clearance ); SHAPE_LINE_CHAIN hull = obs.item->Hull( clearance, aItem->Width() );
if( aLine.EndsWithVia() ) if( aLine.EndsWithVia() )
{ {
int clearance = GetClearance( obs.item, &aLine.GetVia() ); int clearance = GetClearance( obs.item, &aLine.Via() );
SHAPE_LINE_CHAIN viaHull = aLine.GetVia().Hull( clearance ); SHAPE_LINE_CHAIN viaHull = aLine.Via().Hull( clearance, aItem->Width() );
viaHull.Intersect( hull, isect_list ); viaHull.Intersect( hull, isect_list );
BOOST_FOREACH( SHAPE_LINE_CHAIN::INTERSECTION isect, isect_list ) BOOST_FOREACH( SHAPE_LINE_CHAIN::INTERSECTION isect, isect_list )
{ {
int dist = aLine.GetCLine().Length() + int dist = aLine.CLine().Length() +
( isect.p - aLine.GetVia().GetPos() ).EuclideanNorm(); ( isect.p - aLine.Via().Pos() ).EuclideanNorm();
if( dist < nearest.dist_first ) if( dist < nearest.dist_first )
{ {
...@@ -320,11 +332,11 @@ PNS_NODE::OptObstacle PNS_NODE::NearestObstacle( const PNS_LINE* aItem, int aKin ...@@ -320,11 +332,11 @@ PNS_NODE::OptObstacle PNS_NODE::NearestObstacle( const PNS_LINE* aItem, int aKin
isect_list.clear(); isect_list.clear();
hull.Intersect( aLine.GetCLine(), isect_list ); hull.Intersect( aLine.CLine(), isect_list );
BOOST_FOREACH( SHAPE_LINE_CHAIN::INTERSECTION isect, isect_list ) BOOST_FOREACH( SHAPE_LINE_CHAIN::INTERSECTION isect, isect_list )
{ {
int dist = aLine.GetCLine().PathLength( isect.p ); int dist = aLine.CLine().PathLength( isect.p );
if( dist < nearest.dist_first ) if( dist < nearest.dist_first )
{ {
...@@ -346,9 +358,22 @@ PNS_NODE::OptObstacle PNS_NODE::NearestObstacle( const PNS_LINE* aItem, int aKin ...@@ -346,9 +358,22 @@ PNS_NODE::OptObstacle PNS_NODE::NearestObstacle( const PNS_LINE* aItem, int aKin
nearest.dist_last = dist_max; nearest.dist_last = dist_max;
} }
return found_isects ? nearest : OptObstacle(); if(!found_isects)
nearest.item = obs_list[0].item;
return nearest;
} }
PNS_NODE::OptObstacle PNS_NODE::CheckColliding( const PNS_ITEMSET& aSet, int aKindMask )
{
BOOST_FOREACH( const PNS_ITEM *item, aSet.CItems() )
{
OptObstacle obs = CheckColliding(item, aKindMask);
if(obs)
return obs;
}
return OptObstacle();
}
PNS_NODE::OptObstacle PNS_NODE::CheckColliding( const PNS_ITEM* aItemA, int aKindMask ) PNS_NODE::OptObstacle PNS_NODE::CheckColliding( const PNS_ITEM* aItemA, int aKindMask )
{ {
...@@ -356,11 +381,11 @@ PNS_NODE::OptObstacle PNS_NODE::CheckColliding( const PNS_ITEM* aItemA, int aKin ...@@ -356,11 +381,11 @@ PNS_NODE::OptObstacle PNS_NODE::CheckColliding( const PNS_ITEM* aItemA, int aKin
obs.reserve( 100 ); obs.reserve( 100 );
if( aItemA->GetKind() == PNS_ITEM::LINE ) if( aItemA->Kind() == PNS_ITEM::LINE )
{ {
int n = 0; int n = 0;
const PNS_LINE* line = static_cast<const PNS_LINE*>(aItemA); const PNS_LINE* line = static_cast<const PNS_LINE*>(aItemA);
const SHAPE_LINE_CHAIN& l = line->GetCLine(); const SHAPE_LINE_CHAIN& l = line->CLine();
for( int i = 0; i < l.SegmentCount(); i++ ) for( int i = 0; i < l.SegmentCount(); i++ )
{ {
...@@ -373,7 +398,7 @@ PNS_NODE::OptObstacle PNS_NODE::CheckColliding( const PNS_ITEM* aItemA, int aKin ...@@ -373,7 +398,7 @@ PNS_NODE::OptObstacle PNS_NODE::CheckColliding( const PNS_ITEM* aItemA, int aKin
if( line->EndsWithVia() ) if( line->EndsWithVia() )
{ {
n += QueryColliding( &line->GetVia(), obs, aKindMask, 1 ); n += QueryColliding( &line->Via(), obs, aKindMask, 1 );
if( n ) if( n )
return OptObstacle( obs[0] ); return OptObstacle( obs[0] );
...@@ -388,12 +413,16 @@ PNS_NODE::OptObstacle PNS_NODE::CheckColliding( const PNS_ITEM* aItemA, int aKin ...@@ -388,12 +413,16 @@ PNS_NODE::OptObstacle PNS_NODE::CheckColliding( const PNS_ITEM* aItemA, int aKin
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 )
{ {
Obstacles dummy;
assert( aItemB ); assert( aItemB );
// return QueryColliding(aItemA, dummy, aKindMask, 1) > 0; int clearance = GetClearance( aItemA, aItemB );
// fixme: refactor
if(aItemA->Kind() == PNS_ITEM::LINE)
clearance += static_cast<const PNS_LINE *>(aItemA)->Width() / 2;
if(aItemB->Kind() == PNS_ITEM::LINE)
clearance += static_cast<const PNS_LINE *>(aItemB)->Width() / 2;
return aItemA->Collide( aItemB, GetClearance( aItemA, aItemB ) ); return aItemA->Collide( aItemB, clearance );
} }
...@@ -412,10 +441,7 @@ struct hitVisitor ...@@ -412,10 +441,7 @@ struct hitVisitor
int cl = 0; int cl = 0;
if( aItem->GetKind() == PNS_ITEM::SEGMENT ) if( aItem->Shape()->Collide( &cp, cl ) )
cl += static_cast<PNS_SEGMENT*>(aItem)->GetWidth() / 2;
if( aItem->GetShape()->Collide( &cp, cl ) )
m_items.Add( aItem ); m_items.Add( aItem );
return true; return true;
...@@ -426,6 +452,7 @@ struct hitVisitor ...@@ -426,6 +452,7 @@ struct hitVisitor
const PNS_ITEMSET PNS_NODE::HitTest( const VECTOR2I& aPoint ) const const PNS_ITEMSET PNS_NODE::HitTest( const VECTOR2I& aPoint ) const
{ {
PNS_ITEMSET items; PNS_ITEMSET items;
// fixme: we treat a point as an infinitely small circle - this is inefficient. // fixme: we treat a point as an infinitely small circle - this is inefficient.
SHAPE_CIRCLE s( aPoint, 0 ); SHAPE_CIRCLE s( aPoint, 0 );
hitVisitor visitor( items, aPoint, this ); hitVisitor visitor( items, aPoint, this );
...@@ -451,21 +478,21 @@ const PNS_ITEMSET PNS_NODE::HitTest( const VECTOR2I& aPoint ) const ...@@ -451,21 +478,21 @@ const PNS_ITEMSET PNS_NODE::HitTest( const VECTOR2I& aPoint ) const
void PNS_NODE::addSolid( PNS_SOLID* aSolid ) void PNS_NODE::addSolid( PNS_SOLID* aSolid )
{ {
linkJoint( aSolid->GetCenter(), aSolid->GetLayers(), aSolid->GetNet(), aSolid ); linkJoint( aSolid->Pos(), aSolid->Layers(), aSolid->Net(), aSolid );
m_index->Add( aSolid ); m_index->Add( aSolid );
} }
void PNS_NODE::addVia( PNS_VIA* aVia ) void PNS_NODE::addVia( PNS_VIA* aVia )
{ {
linkJoint( aVia->GetPos(), aVia->GetLayers(), aVia->GetNet(), aVia ); linkJoint( aVia->Pos(), aVia->Layers(), aVia->Net(), aVia );
m_index->Add( aVia ); m_index->Add( aVia );
} }
void PNS_NODE::addLine( PNS_LINE* aLine ) void PNS_NODE::addLine( PNS_LINE* aLine, bool aAllowRedundant )
{ {
const SHAPE_LINE_CHAIN& l = aLine->GetLine(); SHAPE_LINE_CHAIN& l = aLine->Line();
for( int i = 0; i < l.SegmentCount(); i++ ) for( int i = 0; i < l.SegmentCount(); i++ )
{ {
...@@ -474,53 +501,65 @@ void PNS_NODE::addLine( PNS_LINE* aLine ) ...@@ -474,53 +501,65 @@ void PNS_NODE::addLine( PNS_LINE* aLine )
if( s.A != s.B ) if( s.A != s.B )
{ {
PNS_SEGMENT* pseg = new PNS_SEGMENT( *aLine, s ); PNS_SEGMENT* pseg = new PNS_SEGMENT( *aLine, s );
PNS_SEGMENT* psegR = NULL;
pseg->SetOwner( this ); if ( !aAllowRedundant )
psegR = findRedundantSegment( pseg );
linkJoint( s.A, pseg->GetLayers(), aLine->GetNet(), pseg ); if(psegR)
linkJoint( s.B, pseg->GetLayers(), aLine->GetNet(), pseg ); aLine->LinkSegment(psegR);
else {
pseg->SetOwner( this );
aLine->LinkSegment( pseg ); linkJoint( s.A, pseg->Layers(), aLine->Net(), pseg );
linkJoint( s.B, pseg->Layers(), aLine->Net(), pseg );
m_index->Add( pseg ); aLine->LinkSegment( pseg );
}
m_index->Add( pseg );
}
}
} }
} }
void PNS_NODE::addSegment( PNS_SEGMENT* aSeg ) void PNS_NODE::addSegment( PNS_SEGMENT* aSeg, bool aAllowRedundant )
{ {
if( aSeg->GetSeg().A == aSeg->GetSeg().B ) if( aSeg->Seg().A == aSeg->Seg().B )
{ {
TRACEn( 0, "attempting to add a segment with same end coordinates, ignoring." ) TRACEn( 0, "attempting to add a segment with same end coordinates, ignoring." )
return; return;
} }
if ( !aAllowRedundant && findRedundantSegment ( aSeg ) )
return;
aSeg->SetOwner( this ); aSeg->SetOwner( this );
linkJoint( aSeg->GetSeg().A, aSeg->GetLayers(), aSeg->GetNet(), aSeg ); linkJoint( aSeg->Seg().A, aSeg->Layers(), aSeg->Net(), aSeg );
linkJoint( aSeg->GetSeg().B, aSeg->GetLayers(), aSeg->GetNet(), aSeg ); linkJoint( aSeg->Seg().B, aSeg->Layers(), aSeg->Net(), aSeg );
m_index->Add( aSeg ); m_index->Add( aSeg );
} }
void PNS_NODE::Add( PNS_ITEM* aItem ) void PNS_NODE::Add( PNS_ITEM* aItem, bool aAllowRedundant )
{ {
aItem->SetOwner( this ); aItem->SetOwner( this );
switch( aItem->GetKind() ) switch( aItem->Kind() )
{ {
case PNS_ITEM::SOLID: case PNS_ITEM::SOLID:
addSolid( static_cast<PNS_SOLID*>( aItem ) ); addSolid( static_cast<PNS_SOLID*>( aItem ) );
break; break;
case PNS_ITEM::SEGMENT: case PNS_ITEM::SEGMENT:
addSegment( static_cast<PNS_SEGMENT*>( aItem ) ); addSegment( static_cast<PNS_SEGMENT*>( aItem ), aAllowRedundant );
break; break;
case PNS_ITEM::LINE: case PNS_ITEM::LINE:
addLine( static_cast<PNS_LINE*> (aItem) ); addLine( static_cast<PNS_LINE*> (aItem), aAllowRedundant );
break; break;
case PNS_ITEM::VIA: case PNS_ITEM::VIA:
...@@ -535,6 +574,9 @@ void PNS_NODE::Add( PNS_ITEM* aItem ) ...@@ -535,6 +574,9 @@ void PNS_NODE::Add( PNS_ITEM* aItem )
void PNS_NODE::doRemove( PNS_ITEM* aItem ) void PNS_NODE::doRemove( PNS_ITEM* aItem )
{ {
// assert(m_root->m_index->Contains(aItem) || m_index->Contains(aItem));
// case 1: removing an item that is stored in the root node from any branch: // case 1: removing an item that is stored in the root node from any branch:
// mark it as overridden, but do not remove // mark it as overridden, but do not remove
if( aItem->BelongsTo( m_root ) && !isRoot() ) if( aItem->BelongsTo( m_root ) && !isRoot() )
...@@ -553,8 +595,8 @@ void PNS_NODE::doRemove( PNS_ITEM* aItem ) ...@@ -553,8 +595,8 @@ void PNS_NODE::doRemove( PNS_ITEM* aItem )
void PNS_NODE::removeSegment( PNS_SEGMENT* aSeg ) void PNS_NODE::removeSegment( PNS_SEGMENT* aSeg )
{ {
unlinkJoint( aSeg->GetSeg().A, aSeg->GetLayers(), aSeg->GetNet(), aSeg ); unlinkJoint( aSeg->Seg().A, aSeg->Layers(), aSeg->Net(), aSeg );
unlinkJoint( aSeg->GetSeg().B, aSeg->GetLayers(), aSeg->GetNet(), aSeg ); unlinkJoint( aSeg->Seg().B, aSeg->Layers(), aSeg->Net(), aSeg );
doRemove( aSeg ); doRemove( aSeg );
} }
...@@ -562,12 +604,18 @@ void PNS_NODE::removeSegment( PNS_SEGMENT* aSeg ) ...@@ -562,12 +604,18 @@ void PNS_NODE::removeSegment( PNS_SEGMENT* aSeg )
void PNS_NODE::removeLine( PNS_LINE* aLine ) void PNS_NODE::removeLine( PNS_LINE* aLine )
{ {
std::vector<PNS_SEGMENT*>* segRefs = aLine->GetLinkedSegments(); std::vector<PNS_SEGMENT*>* segRefs = aLine->LinkedSegments();
if( !segRefs ) if(! aLine->SegmentCount() )
return; return;
assert( aLine->GetOwner() ); assert (segRefs != NULL);
assert (aLine->Owner());
if ( (int)segRefs->size() != aLine->SegmentCount())
{
//printf("******weird deletion: segrefs %d segcount %d hasloops %d\n", segRefs->size(), aLine->SegmentCount(), aLine->HasLoops());
}
BOOST_FOREACH( PNS_SEGMENT* seg, *segRefs ) BOOST_FOREACH( PNS_SEGMENT* seg, *segRefs )
{ {
...@@ -575,16 +623,59 @@ void PNS_NODE::removeLine( PNS_LINE* aLine ) ...@@ -575,16 +623,59 @@ void PNS_NODE::removeLine( PNS_LINE* aLine )
} }
aLine->SetOwner( NULL ); aLine->SetOwner( NULL );
aLine->ClearSegmentLinks();
} }
void PNS_NODE::removeVia( PNS_VIA* aVia ) void PNS_NODE::removeVia( PNS_VIA* aVia )
{ {
unlinkJoint( aVia->GetPos(), aVia->GetLayers(), aVia->GetNet(), aVia ); // We have to split a single joint (associated with a via, binding together multiple layers)
// into multiple independent joints. As I'm a lazy bastard, I simply delete the via and all its links and re-insert them.
doRemove( aVia ); PNS_JOINT::HashTag tag;
}
VECTOR2I p ( aVia->Pos() );
PNS_LAYERSET vLayers ( aVia->Layers() );
int net = aVia->Net();
PNS_JOINT *jt = FindJoint( p, vLayers.Start(), net );
PNS_JOINT::LinkedItems links ( jt->LinkList() );
tag.net = net;
tag.pos = p;
bool split;
do
{
split = false;
std::pair<JointMap::iterator, JointMap::iterator> range = m_joints.equal_range( tag );
if( range.first == m_joints.end() )
break;
// find and remove all joints containing the via to be removed
for( JointMap::iterator f = range.first; f != range.second; ++f )
{
if( aVia->LayersOverlap ( &f->second ) )
{
m_joints.erase( f );
split = true;
break;
}
}
} while (split);
// and re-link them, using the former via's link list
BOOST_FOREACH(PNS_ITEM *item, links)
{
if( item != aVia )
linkJoint ( p, item->Layers(), net, item );
}
doRemove( aVia );
}
void PNS_NODE::Replace( PNS_ITEM* aOldItem, PNS_ITEM* aNewItem ) void PNS_NODE::Replace( PNS_ITEM* aOldItem, PNS_ITEM* aNewItem )
{ {
...@@ -595,10 +686,11 @@ void PNS_NODE::Replace( PNS_ITEM* aOldItem, PNS_ITEM* aNewItem ) ...@@ -595,10 +686,11 @@ void PNS_NODE::Replace( PNS_ITEM* aOldItem, PNS_ITEM* aNewItem )
void PNS_NODE::Remove( PNS_ITEM* aItem ) void PNS_NODE::Remove( PNS_ITEM* aItem )
{ {
switch( aItem->GetKind() ) switch( aItem->Kind() )
{ {
case PNS_ITEM::SOLID: case PNS_ITEM::SOLID:
assert( false ); // fixme: this fucks up the joints, but it's only used for marking colliding obstacles for the moment, so we don't care.
doRemove ( aItem );
break; break;
case PNS_ITEM::SEGMENT: case PNS_ITEM::SEGMENT:
...@@ -627,13 +719,13 @@ void PNS_NODE::followLine( PNS_SEGMENT* current, bool scanDirection, int& pos, ...@@ -627,13 +719,13 @@ void PNS_NODE::followLine( PNS_SEGMENT* current, bool scanDirection, int& pos,
for( ; ; ) for( ; ; )
{ {
const VECTOR2I p = const VECTOR2I p =
(scanDirection ^ prevReversed) ? current->GetSeg().B : current->GetSeg().A; (scanDirection ^ prevReversed) ? current->Seg().B : current->Seg().A;
const OptJoint jt = FindJoint( p, current->GetLayer(), current->GetNet() ); const PNS_JOINT *jt = FindJoint( p, current );
assert( jt ); assert( jt );
assert( pos > 0 && pos < limit ); assert( pos > 0 && pos < limit );
corners[pos] = jt->GetPos(); corners[pos] = jt->Pos();
segments[pos] = current; segments[pos] = current;
pos += (scanDirection ? 1 : -1); pos += (scanDirection ? 1 : -1);
...@@ -642,13 +734,14 @@ void PNS_NODE::followLine( PNS_SEGMENT* current, bool scanDirection, int& pos, ...@@ -642,13 +734,14 @@ void PNS_NODE::followLine( PNS_SEGMENT* current, bool scanDirection, int& pos,
break; break;
current = jt->NextSegment( current ); current = jt->NextSegment( current );
prevReversed = prevReversed =
( jt->GetPos() == (scanDirection ? current->GetSeg().B : current->GetSeg().A ) ); ( jt->Pos() == (scanDirection ? current->Seg().B : current->Seg().A ) );
} }
} }
PNS_LINE* PNS_NODE::AssembleLine( PNS_SEGMENT* aSeg, const OptJoint& a, const OptJoint& b ) PNS_LINE* PNS_NODE::AssembleLine( PNS_SEGMENT* aSeg, int *aOriginSegmentIndex)
{ {
const int MaxVerts = 1024; const int MaxVerts = 1024;
...@@ -658,61 +751,151 @@ PNS_LINE* PNS_NODE::AssembleLine( PNS_SEGMENT* aSeg, const OptJoint& a, const Op ...@@ -658,61 +751,151 @@ PNS_LINE* PNS_NODE::AssembleLine( PNS_SEGMENT* aSeg, const OptJoint& a, const Op
PNS_LINE* pl = new PNS_LINE; PNS_LINE* pl = new PNS_LINE;
int i_start = MaxVerts / 2, i_end = i_start + 1; int i_start = MaxVerts / 2, i_end = i_start + 1;
pl->SetWidth( aSeg->GetWidth() ); pl->SetWidth( aSeg->Width() );
pl->SetLayers( aSeg->GetLayers() ); pl->SetLayers( aSeg->Layers() );
pl->SetNet( aSeg->GetNet() ); pl->SetNet( aSeg->Net() );
pl->SetOwner( this ); pl->SetOwner( this );
// pl->LinkSegment(aSeg);
followLine( aSeg, false, i_start, MaxVerts, corners, segs ); followLine( aSeg, false, i_start, MaxVerts, corners, segs );
followLine( aSeg, true, i_end, MaxVerts, corners, segs ); followLine( aSeg, true, i_end, MaxVerts, corners, segs );
int n = 0;
int clip_start = -1, clip_end = -1; PNS_SEGMENT *prev_seg = NULL;
for( int i = i_start + 1; i < i_end; i++ ) for( int i = i_start + 1; i < i_end; i++ )
{ {
const VECTOR2I& p = corners[i]; const VECTOR2I& p = corners[i];
if( a && ( p == a->GetPos() || p == b->GetPos() ) ) pl->Line().Append( p );
if( prev_seg != segs[i] )
{ {
clip_start = std::min( clip_start, i ); pl->LinkSegment( segs[i] );
clip_end = std::max( clip_end, i );
}
pl->GetLine().Append( p ); if(segs[i] == aSeg && aOriginSegmentIndex)
*aOriginSegmentIndex = n;
n++;
}
if( segs[i - 1] != segs[i] ) prev_seg = segs[i];
pl->LinkSegment( segs[i] );
} }
assert (pl->SegmentCount() != 0);
assert (pl->SegmentCount() == (int) pl->LinkedSegments()->size());
return pl; return pl;
} }
void PNS_NODE::FindLineEnds( PNS_LINE* aLine, PNS_JOINT& a, PNS_JOINT& b ) void PNS_NODE::FindLineEnds( PNS_LINE* aLine, PNS_JOINT& a, PNS_JOINT& b )
{ {
a = *FindJoint( aLine->GetCLine().CPoint( 0 ), aLine->GetLayers().Start(), aLine->GetNet() ); a = *FindJoint( aLine->CPoint( 0 ), aLine );
b = *FindJoint( aLine->GetCLine().CPoint( -1 ), aLine->GetLayers().Start(), aLine->GetNet() ); b = *FindJoint( aLine->CPoint( -1 ), aLine );
} }
void PNS_NODE::MapConnectivity ( PNS_JOINT* aStart, std::vector<PNS_JOINT*> & aFoundJoints )
{
std::deque<PNS_JOINT*> searchQueue;
std::set<PNS_JOINT*> 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 = FindJoint( seg->Seg().A, seg );
PNS_JOINT *b = 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 );
}
}
}
BOOST_FOREACH(PNS_JOINT *jt, processed)
aFoundJoints.push_back( jt );
}
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& a, PNS_JOINT& b, std::vector<PNS_LINE*>& aLines ) int PNS_NODE::FindLinesBetweenJoints( PNS_JOINT& a, PNS_JOINT& b, std::vector<PNS_LINE*>& aLines )
{ {
BOOST_FOREACH( PNS_ITEM* item, a.GetLinkList() ) BOOST_FOREACH( PNS_ITEM* item, a.LinkList() )
{ {
if( item->GetKind() == PNS_ITEM::SEGMENT ) if( item->Kind() == PNS_ITEM::SEGMENT )
{ {
PNS_SEGMENT* seg = static_cast<PNS_SEGMENT*>(item); PNS_SEGMENT* seg = static_cast<PNS_SEGMENT*>(item);
PNS_LINE* line = AssembleLine( seg ); PNS_LINE* line = AssembleLine( seg );
PNS_JOINT j_start, j_end; PNS_JOINT j_start, j_end;
FindLineEnds( line, j_start, j_end ); FindLineEnds( line, j_start, j_end );
if( (j_start == a && j_end == b )|| (j_end == a && j_start == b) )
int id_start = line->CLine().Find (a.Pos());
int id_end = line->CLine().Find (b.Pos());
if(id_end < id_start)
std::swap(id_end, id_start);
if(id_start >= 0 && id_end >= 0)
{
line->ClipVertexRange ( id_start, id_end );
aLines.push_back( line ); aLines.push_back( line );
else } else
delete line; delete line;
} }
} }
...@@ -721,7 +904,7 @@ int PNS_NODE::FindLinesBetweenJoints( PNS_JOINT& a, PNS_JOINT& b, std::vector<PN ...@@ -721,7 +904,7 @@ int PNS_NODE::FindLinesBetweenJoints( PNS_JOINT& a, PNS_JOINT& b, std::vector<PN
} }
const PNS_NODE::OptJoint PNS_NODE::FindJoint( const VECTOR2I& aPos, int aLayer, int aNet ) PNS_JOINT* PNS_NODE::FindJoint( const VECTOR2I& aPos, int aLayer, int aNet )
{ {
PNS_JOINT::HashTag tag; PNS_JOINT::HashTag tag;
...@@ -737,17 +920,17 @@ const PNS_NODE::OptJoint PNS_NODE::FindJoint( const VECTOR2I& aPos, int aLayer, ...@@ -737,17 +920,17 @@ const PNS_NODE::OptJoint PNS_NODE::FindJoint( const VECTOR2I& aPos, int aLayer,
} }
if( f == end ) if( f == end )
return OptJoint(); return NULL;
while( f != end ) while( f != end )
{ {
if( f->second.GetLayers().Overlaps( aLayer ) ) if( f->second.Layers().Overlaps( aLayer ) )
return f->second; return &f->second;
++f; ++f;
} }
return OptJoint(); return NULL;
} }
...@@ -787,7 +970,7 @@ PNS_JOINT& PNS_NODE::touchJoint( const VECTOR2I& aPos, const PNS_LAYERSET& aLaye ...@@ -787,7 +970,7 @@ PNS_JOINT& PNS_NODE::touchJoint( const VECTOR2I& aPos, const PNS_LAYERSET& aLaye
for( f = range.first; f != range.second; ++f ) for( f = range.first; f != range.second; ++f )
{ {
if( aLayers.Overlaps( f->second.GetLayers() ) ) if( aLayers.Overlaps( f->second.Layers() ) )
{ {
jt.Merge( f->second ); jt.Merge( f->second );
m_joints.erase( f ); m_joints.erase( f );
...@@ -949,7 +1132,11 @@ void PNS_NODE::Commit( PNS_NODE* aNode ) ...@@ -949,7 +1132,11 @@ void PNS_NODE::Commit( PNS_NODE* aNode )
for( PNS_INDEX::ItemSet::iterator i = aNode->m_index->begin(); for( PNS_INDEX::ItemSet::iterator i = aNode->m_index->begin();
i != aNode->m_index->end(); ++i ) i != aNode->m_index->end(); ++i )
{
(*i)->SetRank ( -1 );
(*i)->Unmark ();
Add( *i ); Add( *i );
}
releaseChildren(); releaseChildren();
} }
...@@ -957,29 +1144,81 @@ void PNS_NODE::Commit( PNS_NODE* aNode ) ...@@ -957,29 +1144,81 @@ void PNS_NODE::Commit( PNS_NODE* aNode )
void PNS_NODE::KillChildren() void PNS_NODE::KillChildren()
{ {
assert( isRoot() ); assert ( isRoot() );
releaseChildren(); releaseChildren();
} }
void PNS_NODE::AllItemsInNet( int aNet, std::list<PNS_ITEM*>& aItems ) void PNS_NODE::AllItemsInNet( int aNet, std::set<PNS_ITEM*>& aItems )
{ {
PNS_INDEX::NetItemsList* l_cur = m_index->GetItemsForNet( aNet ); PNS_INDEX::NetItemsList* l_cur = m_index->GetItemsForNet( aNet );
if( !l_cur )
return;
std::copy( aItems.begin(), l_cur->begin(), l_cur->end() );
if(l_cur)
{
BOOST_FOREACH (PNS_ITEM *item, *l_cur )
aItems.insert ( item );
}
if( !isRoot() ) if( !isRoot() )
{ {
PNS_INDEX::NetItemsList* l_root = m_root->m_index->GetItemsForNet( aNet ); PNS_INDEX::NetItemsList* l_root = m_root->m_index->GetItemsForNet( aNet );
for( PNS_INDEX::NetItemsList::iterator i = l_root->begin(); i!= l_root->end(); ++i ) if(l_root)
if( !overrides( *i ) ) for( PNS_INDEX::NetItemsList::iterator i = l_root->begin(); i!= l_root->end(); ++i )
aItems.push_back( *i ); if( !overrides( *i ) )
aItems.insert( *i );
}
}
void PNS_NODE::ClearRanks()
{
for( PNS_INDEX::ItemSet::iterator i = m_index->begin(); i != m_index->end(); ++i )
{
(*i)->SetRank(-1);
(*i)->Mark(0);
}
}
int PNS_NODE::FindByMarker ( int aMarker, PNS_ITEMSET& aItems )
{
for( PNS_INDEX::ItemSet::iterator i = m_index->begin(); i != m_index->end(); ++i )
if ( (*i)->Marker() & aMarker ) aItems.Add(*i);
return 0;
}
int PNS_NODE::RemoveByMarker ( int aMarker )
{
for( PNS_INDEX::ItemSet::iterator i = m_index->begin(); i != m_index->end(); ++i )
if ( (*i)->Marker() & aMarker )
{
Remove (*i);
}
return 0;
}
PNS_SEGMENT* PNS_NODE::findRedundantSegment ( PNS_SEGMENT *aSeg )
{
PNS_JOINT *jtStart = FindJoint ( aSeg->Seg().A, aSeg );
if(!jtStart)
return NULL;
} BOOST_FOREACH( PNS_ITEM *item, jtStart->LinkList() )
if(item->OfKind(PNS_ITEM::SEGMENT))
{
PNS_SEGMENT *seg2 = (PNS_SEGMENT *) item;
const VECTOR2I a1 ( aSeg->Seg().A );
const VECTOR2I b1 ( aSeg->Seg().B );
const VECTOR2I a2 ( seg2->Seg().A );
const VECTOR2I b2 ( seg2->Seg().B );
if( seg2->Layers().Start() == aSeg->Layers().Start() &&
((a1 == a2 && b1 == b2) || (a1 == b2 && a2 == b1)))
return seg2;
}
return NULL;
} }
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,8 +15,9 @@ ...@@ -15,8 +15,9 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_NODE_H #ifndef __PNS_NODE_H
#define __PNS_NODE_H #define __PNS_NODE_H
...@@ -26,7 +27,6 @@ ...@@ -26,7 +27,6 @@
#include <boost/unordered_set.hpp> #include <boost/unordered_set.hpp>
#include <boost/unordered_map.hpp> #include <boost/unordered_map.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/smart_ptr.hpp>
#include <geometry/shape.h> #include <geometry/shape.h>
#include <geometry/shape_line_chain.h> #include <geometry/shape_line_chain.h>
...@@ -43,8 +43,12 @@ class PNS_VIA; ...@@ -43,8 +43,12 @@ class PNS_VIA;
class PNS_RATSNEST; class PNS_RATSNEST;
class PNS_INDEX; class PNS_INDEX;
using boost::shared_ptr;
/**
* Class PNS_CLEARANCE_FUNC
*
* An abstract function object, returning a required clearance between two items.
**/
class PNS_CLEARANCE_FUNC class PNS_CLEARANCE_FUNC
{ {
public: public:
...@@ -84,7 +88,7 @@ struct PNS_OBSTACLE ...@@ -84,7 +88,7 @@ struct PNS_OBSTACLE
* hierarchical and indexed way. * hierarchical and indexed way.
* Features: * Features:
* - spatial-indexed container for PCB item shapes * - spatial-indexed container for PCB item shapes
* - collision search (with clearance checking) * - collision search & clearance checking
* - assembly of lines connecting joints, finding loops and unique paths * - assembly of lines connecting joints, finding loops and unique paths
* - lightweight cloning/branching (for recursive optimization and shove * - lightweight cloning/branching (for recursive optimization and shove
* springback) * springback)
...@@ -96,101 +100,236 @@ public: ...@@ -96,101 +100,236 @@ public:
typedef boost::optional<PNS_OBSTACLE> OptObstacle; typedef boost::optional<PNS_OBSTACLE> OptObstacle;
typedef std::vector<PNS_ITEM*> ItemVector; typedef std::vector<PNS_ITEM*> ItemVector;
typedef std::vector<PNS_OBSTACLE> Obstacles; typedef std::vector<PNS_OBSTACLE> Obstacles;
typedef boost::optional<PNS_JOINT> OptJoint;
PNS_NODE ();
PNS_NODE(); ~PNS_NODE ();
~PNS_NODE();
///> Returns the expected clearance between items a and b. ///> Returns the expected clearance between items a and b.
int GetClearance( const PNS_ITEM* a, const PNS_ITEM* b ) const; int GetClearance ( const PNS_ITEM* a, const PNS_ITEM* b ) const;
///> Returns the pre-set worst case clearance between any pair of items ///> Returns the pre-set worst case clearance between any pair of items
int GetMaxClearance() const int GetMaxClearance () const
{ {
return m_maxClearance; return m_maxClearance;
} }
void SetMaxClearance( int aClearance ) ///> Sets the worst-case clerance between any pair of items
void SetMaxClearance ( int aClearance )
{ {
m_maxClearance = aClearance; m_maxClearance = aClearance;
} }
void SetClearanceFunctor( PNS_CLEARANCE_FUNC* aFunc ) ///> Assigns a clerance resolution function object
void SetClearanceFunctor ( PNS_CLEARANCE_FUNC* aFunc )
{ {
m_clearanceFunctor = aFunc; m_clearanceFunctor = aFunc;
} }
///> Finds items that collide with aItem and stores collision information
///> in aObstacles.
int QueryColliding( const PNS_ITEM* aItem,
Obstacles& aObstacles,
int aKindMask = PNS_ITEM::ANY,
int aLimitCount = -1 );
///> Finds the nearest item that collides with aItem.
OptObstacle NearestObstacle( const PNS_LINE* aItem, int aKindMask = PNS_ITEM::ANY );
///> Checks if the item collides with anything else in the world,
///> and returns it if so.
OptObstacle CheckColliding( const PNS_ITEM* aItem, int aKindMask = PNS_ITEM::ANY );
///> Checks if two items collide [deprecated].
bool CheckColliding( const PNS_ITEM* aItemA,
const PNS_ITEM* aItemB,
int aKindMask = PNS_ITEM::ANY );
///> Hit detection
const PNS_ITEMSET HitTest( const VECTOR2I& aPoint ) const;
void Add( PNS_ITEM* aItem );
void Remove( PNS_ITEM* aItem );
void Replace( PNS_ITEM* aOldItem, PNS_ITEM* aNewItem );
///> Creates a lightweight copy ("branch") of self. Note that if there are
///> any branches in use, their parents must NOT be deleted.
PNS_NODE* Branch();
///> Assembles a line connecting two non-trivial joints the
///> segment aSeg belongs to.
PNS_LINE* AssembleLine( PNS_SEGMENT* aSeg,
const OptJoint& a = OptJoint(), const OptJoint& b = OptJoint() );
///> Dumps the contents and joints structure
void Dump( bool aLong = false );
///> Returns the number of joints ///> Returns the number of joints
int JointCount() const int JointCount() const
{ {
return m_joints.size(); return m_joints.size();
} }
///> Returns the lists of items removed and added in this branch, with ///> Returns the number of nodes in the inheritance chain (wrs to the root node)
///> respect to the root. int Depth() const
{
return m_depth;
}
/**
* Function QueryColliding()
*
* Finds items collliding (closer than clearance) with the item aItem.
* @param aItem item to check collisions against
* @param aObstacles set of colliding objects found
* @param aKindMask mask of obstacle types to take into account
* @param aLimitCount stop looking for collisions after finding this number of colliding items
* @return number of obstacles found
*/
int QueryColliding ( const PNS_ITEM* aItem,
Obstacles& aObstacles,
int aKindMask = PNS_ITEM::ANY,
int aLimitCount = -1 );
/**
* Function NearestObstacle()
*
* Follows the line in search of an obstacle that is nearest to the starting to the line's starting
* point.
* @param aItem the item to find collisions with
* @param aKindMask mask of obstacle types to take into account
* @return the obstacle, if found, otherwise empty.
*/
OptObstacle NearestObstacle ( const PNS_LINE* aItem,
int aKindMask = PNS_ITEM::ANY );
/**
* Function CheckColliding()
*
* Checks if the item collides with anything else in the world,
* and if found, returns the obstacle.
* @param aItem the item to find collisions with
* @param aKindMask mask of obstacle types to take into account
* @return the obstacle, if found, otherwise empty.
*/
OptObstacle CheckColliding ( const PNS_ITEM* aItem,
int aKindMask = PNS_ITEM::ANY );
/**
* Function CheckColliding()
*
* Checks if any item in the set collides with anything else in the world,
* and if found, returns the obstacle.
* @param aSet set of items to find collisions with
* @param aKindMask mask of obstacle types to take into account
* @return the obstacle, if found, otherwise empty.
*/
OptObstacle CheckColliding ( const PNS_ITEMSET& aSet,
int aKindMask = PNS_ITEM::ANY );
/**
* Function CheckColliding()
*
* Checks if any item in the set collides with anything else in the world,
* and if found, returns the obstacle.
* @param aSet set of items to find collisions with
* @param aKindMask mask of obstacle types to take into account
* @return the obstacle, if found, otherwise empty.
*/
bool CheckColliding ( const PNS_ITEM* aItemA,
const PNS_ITEM* aItemB,
int aKindMask = PNS_ITEM::ANY );
/**
* Function HitTest()
*
* Finds all items that contain the point aPoint.
* @param aPoint the point
* @return the items
*/
const PNS_ITEMSET HitTest ( const VECTOR2I& aPoint ) const;
/**
* Function Add()
*
* Adds an item to the current node.
* @param aItem item to add
* @param aAllowRedundant if true, duplicate items are allowed (e.g. a segment or via
* at the same coordinates as an existing one)
*/
void Add ( PNS_ITEM* aItem, bool aAllowRedundant = false );
/**
* Function Remove()
*
* Just as the name says, removes an item from this branch.
* @param aItem item to remove
*/
void Remove ( PNS_ITEM* aItem );
/**
* Function Replace()
*
* Just as the name says, replaces an item with another one.
* @param aOldItem item to be removed
* @param aNewItem item add instead
*/
void Replace ( PNS_ITEM* aOldItem, PNS_ITEM* aNewItem );
/**
* Function Branch()
*
* Creates a lightweight copy (called branch) of self that tracks
* the changes (added/removed items) wrs to the root. Note that if there are
* any branches in use, their parents must NOT be deleted.
* @return the new branch
*/
PNS_NODE* Branch ( );
/**
* Function AssembleLine()
*
* Follows the joint map to assemble a line connecting two non-trivial
* joints starting from segment aSeg.
* @param aSeg the initial segment
* @param aOriginSegmentIndex index of aSeg in the resulting line
* @return the line
*/
PNS_LINE* AssembleLine ( PNS_SEGMENT* aSeg,
int *aOriginSegmentIndex = NULL );
///> Prints the contents and joints structure
void Dump ( bool aLong = false );
/**
* Function GetUpdatedItems()
*
* Returns the lists of items removed and added in this branch, with
* respect to the root branch.
* @param aRemoved removed items
* @param aAdded added items
*/
void GetUpdatedItems( ItemVector& aRemoved, ItemVector& aAdded ); void GetUpdatedItems( ItemVector& aRemoved, ItemVector& aAdded );
///> Copies the changes from a given branch (aNode) to the root. Called on
///> a non-root branch will fail. /**
* Function Commit()
*
* Applies the changes from a given branch (aNode) to the root branch. Called on
* a non-root branch will fail. Calling commit also kills all children nodes of the root branch.
* @param aNode node to commit changes from
*/
void Commit( PNS_NODE* aNode ); void Commit( PNS_NODE* aNode );
///> finds a joint at a given position, layer and nets /**
const OptJoint FindJoint( const VECTOR2I& aPos, int aLayer, int aNet ); * Function FindJoint()
*
* Searches for a joint at a given position, layer and belonging to given net.
* @return the joint, if found, otherwise empty
*/
PNS_JOINT* FindJoint( const VECTOR2I& aPos, int aLayer, int aNet );
/**
* Function FindJoint()
*
* Searches for a joint at a given position, linked to given item.
* @return the joint, if found, otherwise empty
*/
PNS_JOINT* FindJoint( const VECTOR2I& aPos, PNS_ITEM *aItem )
{
return FindJoint( aPos, aItem->Layers().Start(), aItem->Net() );
}
///> finds all linest between a pair of joints. Used by the loop removal engine. void MapConnectivity ( PNS_JOINT* aStart, std::vector<PNS_JOINT *> & aFoundJoints );
int FindLinesBetweenJoints( PNS_JOINT& a, PNS_JOINT& b,
std::vector<PNS_LINE*>& aLines );
///> finds the joints corresponding to the ends of line aLine PNS_ITEM *NearestUnconnectedItem ( PNS_JOINT *aStart, int *aAnchor = NULL, int aKindMask = PNS_ITEM::ANY);
void FindLineEnds( PNS_LINE* aLine, PNS_JOINT& a, PNS_JOINT& b );
///> finds all joints that have an (in)direct connection(s)
///> (i.e. segments/vias) with the joint aJoint.
void FindConnectedJoints( const PNS_JOINT& aJoint,
std::vector<PNS_JOINT*>& aConnectedJoints );
///> finds all lines between a pair of joints. Used by the loop removal procedure.
int FindLinesBetweenJoints( PNS_JOINT& a,
PNS_JOINT& b,
std::vector<PNS_LINE*>& aLines );
///> finds the joints corresponding to the ends of line aLine
void FindLineEnds( PNS_LINE* aLine, PNS_JOINT& a, PNS_JOINT& b );
///> Destroys all child nodes. Applicable only to the root node. ///> Destroys all child nodes. Applicable only to the root node.
void KillChildren(); void KillChildren();
void AllItemsInNet( int aNet, std::list<PNS_ITEM*>& aItems ); void AllItemsInNet( int aNet, std::set<PNS_ITEM*>& aItems );
void ClearRanks();
int FindByMarker ( int aMarker, PNS_ITEMSET& aItems );
int RemoveByMarker ( int aMarker );
private: private:
struct obstacleVisitor; struct obstacleVisitor;
...@@ -198,12 +337,13 @@ private: ...@@ -198,12 +337,13 @@ private:
typedef JointMap::value_type TagJointPair; typedef JointMap::value_type TagJointPair;
/// nodes are not copyable /// nodes are not copyable
PNS_NODE( const PNS_NODE& b ); PNS_NODE ( const PNS_NODE& b );
PNS_NODE& operator=( const PNS_NODE& b ); PNS_NODE& operator= ( const PNS_NODE& b );
///> tries to find matching joint and creates a new one if not found ///> tries to find matching joint and creates a new one if not found
PNS_JOINT& touchJoint( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers, PNS_JOINT& touchJoint( const VECTOR2I& aPos,
int aNet ); const PNS_LAYERSET& aLayers,
int aNet );
///> touches a joint and links it to an item ///> touches a joint and links it to an item
void linkJoint( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers, void linkJoint( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers,
...@@ -215,8 +355,8 @@ private: ...@@ -215,8 +355,8 @@ private:
///> helpers for adding/removing items ///> helpers for adding/removing items
void addSolid( PNS_SOLID* aSeg ); void addSolid( PNS_SOLID* aSeg );
void addSegment( PNS_SEGMENT* aSeg ); void addSegment( PNS_SEGMENT* aSeg, bool aAllowRedundant );
void addLine( PNS_LINE* aLine ); void addLine( PNS_LINE* aLine, bool aAllowRedundant );
void addVia( PNS_VIA* aVia ); void addVia( PNS_VIA* aVia );
void removeSolid( PNS_SOLID* aSeg ); void removeSolid( PNS_SOLID* aSeg );
void removeLine( PNS_LINE* aLine ); void removeLine( PNS_LINE* aLine );
...@@ -239,16 +379,15 @@ private: ...@@ -239,16 +379,15 @@ private:
return m_override.find( aItem ) != m_override.end(); return m_override.find( aItem ) != m_override.end();
} }
///> scans the joint map, forming a line starting from segment (current). PNS_SEGMENT *findRedundantSegment ( PNS_SEGMENT *aSeg );
void followLine( PNS_SEGMENT* current,
bool scanDirection,
int& pos,
int limit,
VECTOR2I* corners,
PNS_SEGMENT** segments );
///> spatial index of all items ///> scans the joint map, forming a line starting from segment (current).
// SHAPE_INDEX_LIST<PNS_ITEM *> m_items; void followLine ( PNS_SEGMENT* current,
bool scanDirection,
int& pos,
int limit,
VECTOR2I* corners,
PNS_SEGMENT** segments );
///> hash table with the joints, linking the items. Joints are hashed by ///> hash table with the joints, linking the items. Joints are hashed by
///> their position, layer set and net. ///> their position, layer set and net.
...@@ -263,7 +402,7 @@ private: ...@@ -263,7 +402,7 @@ private:
///> list of nodes branched from this one ///> list of nodes branched from this one
std::vector<PNS_NODE*> m_children; std::vector<PNS_NODE*> m_children;
///> hash of root's items that are more recent in this node ///> hash of root's items that have been changed in this node
boost::unordered_set<PNS_ITEM*> m_override; boost::unordered_set<PNS_ITEM*> m_override;
///> worst case item-item clearance ///> worst case item-item clearance
...@@ -275,8 +414,8 @@ private: ...@@ -275,8 +414,8 @@ private:
///> Geometric/Net index of the items ///> Geometric/Net index of the items
PNS_INDEX* m_index; PNS_INDEX* m_index;
///> list of currently processed obstacles. ///> depth of the node (number of parent nodes in the inheritance chain)
Obstacles m_obstacleList; int m_depth;
}; };
#endif #endif
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
...@@ -27,12 +27,13 @@ ...@@ -27,12 +27,13 @@
#include "pns_node.h" #include "pns_node.h"
#include "pns_optimizer.h" #include "pns_optimizer.h"
#include "pns_utils.h" #include "pns_utils.h"
#include "pns_router.h"
/** /**
* *
* Cost Estimator Methods * Cost Estimator Methods
* *
**/ */
int PNS_COST_ESTIMATOR::CornerCost( const SEG& a, const SEG& b ) int PNS_COST_ESTIMATOR::CornerCost( const SEG& a, const SEG& b )
{ {
...@@ -74,29 +75,29 @@ int PNS_COST_ESTIMATOR::CornerCost( const SHAPE_LINE_CHAIN& aLine ) ...@@ -74,29 +75,29 @@ int PNS_COST_ESTIMATOR::CornerCost( const SHAPE_LINE_CHAIN& aLine )
int PNS_COST_ESTIMATOR::CornerCost( const PNS_LINE& aLine ) int PNS_COST_ESTIMATOR::CornerCost( const PNS_LINE& aLine )
{ {
return CornerCost( aLine.GetCLine() ); return CornerCost( aLine.CLine() );
} }
void PNS_COST_ESTIMATOR::Add( PNS_LINE& aLine ) void PNS_COST_ESTIMATOR::Add( PNS_LINE& aLine )
{ {
m_lengthCost += aLine.GetCLine().Length(); m_lengthCost += aLine.CLine().Length();
m_cornerCost += CornerCost( aLine ); m_cornerCost += CornerCost( aLine );
} }
void PNS_COST_ESTIMATOR::Remove( PNS_LINE& aLine ) void PNS_COST_ESTIMATOR::Remove( PNS_LINE& aLine )
{ {
m_lengthCost -= aLine.GetCLine().Length(); m_lengthCost -= aLine.CLine().Length();
m_cornerCost -= CornerCost( aLine ); m_cornerCost -= CornerCost( aLine );
} }
void PNS_COST_ESTIMATOR::Replace( PNS_LINE& aOldLine, PNS_LINE& aNewLine ) void PNS_COST_ESTIMATOR::Replace( PNS_LINE& aOldLine, PNS_LINE& aNewLine )
{ {
m_lengthCost -= aOldLine.GetCLine().Length(); m_lengthCost -= aOldLine.CLine().Length();
m_cornerCost -= CornerCost( aOldLine ); m_cornerCost -= CornerCost( aOldLine );
m_lengthCost += aNewLine.GetCLine().Length(); m_lengthCost += aNewLine.CLine().Length();
m_cornerCost += CornerCost( aNewLine ); m_cornerCost += CornerCost( aNewLine );
} }
...@@ -145,7 +146,7 @@ struct PNS_OPTIMIZER::CacheVisitor ...@@ -145,7 +146,7 @@ struct PNS_OPTIMIZER::CacheVisitor
bool operator()( PNS_ITEM* aOtherItem ) bool operator()( PNS_ITEM* aOtherItem )
{ {
if( !m_mask & aOtherItem->GetKind() ) if( !m_mask & aOtherItem->Kind() )
return true; return true;
int clearance = m_node->GetClearance( aOtherItem, m_ourItem ); int clearance = m_node->GetClearance( aOtherItem, m_ourItem );
...@@ -177,13 +178,13 @@ void PNS_OPTIMIZER::cacheAdd( PNS_ITEM* aItem, bool aIsStatic = false ) ...@@ -177,13 +178,13 @@ void PNS_OPTIMIZER::cacheAdd( PNS_ITEM* aItem, bool aIsStatic = false )
void PNS_OPTIMIZER::removeCachedSegments( PNS_LINE* aLine, int aStartVertex, int aEndVertex ) void PNS_OPTIMIZER::removeCachedSegments( PNS_LINE* aLine, int aStartVertex, int aEndVertex )
{ {
std::vector<PNS_SEGMENT*>* segs = aLine->GetLinkedSegments(); PNS_LINE::SegmentRefs* segs = aLine->LinkedSegments();
if( !segs ) if( !segs )
return; return;
if( aEndVertex < 0 ) if( aEndVertex < 0 )
aEndVertex += aLine->GetCLine().PointCount(); aEndVertex += aLine->PointCount();
for( int i = aStartVertex; i < aEndVertex - 1; i++ ) for( int i = aStartVertex; i < aEndVertex - 1; i++ )
{ {
...@@ -196,7 +197,7 @@ void PNS_OPTIMIZER::removeCachedSegments( PNS_LINE* aLine, int aStartVertex, int ...@@ -196,7 +197,7 @@ void PNS_OPTIMIZER::removeCachedSegments( PNS_LINE* aLine, int aStartVertex, int
void PNS_OPTIMIZER::CacheRemove( PNS_ITEM* aItem ) void PNS_OPTIMIZER::CacheRemove( PNS_ITEM* aItem )
{ {
if( aItem->GetKind() == PNS_ITEM::LINE ) if( aItem->Kind() == PNS_ITEM::LINE )
removeCachedSegments( static_cast<PNS_LINE*> (aItem) ); removeCachedSegments( static_cast<PNS_LINE*> (aItem) );
} }
...@@ -234,7 +235,7 @@ bool PNS_OPTIMIZER::checkColliding( PNS_ITEM* aItem, bool aUpdateCache ) ...@@ -234,7 +235,7 @@ bool PNS_OPTIMIZER::checkColliding( PNS_ITEM* aItem, bool aUpdateCache )
return m_world->CheckColliding( aItem ); return m_world->CheckColliding( aItem );
// something is wrong with the cache, need to investigate. // something is wrong with the cache, need to investigate.
m_cache.Query( aItem->GetShape(), m_world->GetMaxClearance(), v, false ); m_cache.Query( aItem->Shape(), m_world->GetMaxClearance(), v, false );
if( !v.m_collidingItem ) if( !v.m_collidingItem )
{ {
...@@ -268,7 +269,7 @@ bool PNS_OPTIMIZER::checkColliding( PNS_LINE* aLine, const SHAPE_LINE_CHAIN& aOp ...@@ -268,7 +269,7 @@ bool PNS_OPTIMIZER::checkColliding( PNS_LINE* aLine, const SHAPE_LINE_CHAIN& aOp
bool PNS_OPTIMIZER::mergeObtuse( PNS_LINE* aLine ) bool PNS_OPTIMIZER::mergeObtuse( PNS_LINE* aLine )
{ {
SHAPE_LINE_CHAIN& line = aLine->GetLine(); SHAPE_LINE_CHAIN& line = aLine->Line();
int step = line.PointCount() - 3; int step = line.PointCount() - 3;
int iter = 0; int iter = 0;
...@@ -360,7 +361,7 @@ bool PNS_OPTIMIZER::mergeObtuse( PNS_LINE* aLine ) ...@@ -360,7 +361,7 @@ bool PNS_OPTIMIZER::mergeObtuse( PNS_LINE* aLine )
bool PNS_OPTIMIZER::mergeFull( PNS_LINE* aLine ) bool PNS_OPTIMIZER::mergeFull( PNS_LINE* aLine )
{ {
SHAPE_LINE_CHAIN& line = aLine->GetLine(); SHAPE_LINE_CHAIN& line = aLine->Line();
int step = line.SegmentCount() - 1; int step = line.SegmentCount() - 1;
int segs_pre = line.SegmentCount(); int segs_pre = line.SegmentCount();
...@@ -395,7 +396,7 @@ bool PNS_OPTIMIZER::mergeFull( PNS_LINE* aLine ) ...@@ -395,7 +396,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 )//, int aStartVertex, int aEndVertex )
{ {
if( !aResult ) if( !aResult )
aResult = aLine; aResult = aLine;
...@@ -415,6 +416,9 @@ bool PNS_OPTIMIZER::Optimize( PNS_LINE* aLine, PNS_LINE* aResult, int aStartVert ...@@ -415,6 +416,9 @@ bool PNS_OPTIMIZER::Optimize( PNS_LINE* aLine, PNS_LINE* aResult, int aStartVert
if( m_effortLevel & SMART_PADS ) if( m_effortLevel & SMART_PADS )
rv |= runSmartPads( aResult ); rv |= runSmartPads( aResult );
if( m_effortLevel & FANOUT_CLEANUP )
rv |= fanoutCleanup( aResult );
return rv; return rv;
} }
...@@ -427,11 +431,11 @@ bool PNS_OPTIMIZER::mergeStep( PNS_LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, ...@@ -427,11 +431,11 @@ 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 );
if( aLine->GetCLine().SegmentCount() < 4 ) if( aLine->SegmentCount() < 4 )
return false; return false;
DIRECTION_45 orig_start( aLine->GetCLine().CSegment( 0 ) ); DIRECTION_45 orig_start( aLine->CSegment( 0 ) );
DIRECTION_45 orig_end( aLine->GetCLine().CSegment( -1 ) ); DIRECTION_45 orig_end( aLine->CSegment( -1 ) );
while( n < n_segs - step ) while( n < n_segs - step )
{ {
...@@ -559,23 +563,30 @@ PNS_OPTIMIZER::BreakoutList PNS_OPTIMIZER::rectBreakouts( int aWidth, ...@@ -559,23 +563,30 @@ PNS_OPTIMIZER::BreakoutList PNS_OPTIMIZER::rectBreakouts( int aWidth,
PNS_OPTIMIZER::BreakoutList PNS_OPTIMIZER::computeBreakouts( int aWidth, PNS_OPTIMIZER::BreakoutList PNS_OPTIMIZER::computeBreakouts( int aWidth,
const PNS_ITEM* aItem, bool aPermitDiagonal ) const const PNS_ITEM* aItem, bool aPermitDiagonal ) const
{ {
switch( aItem->GetKind() ) switch( aItem->Kind() )
{ {
case PNS_ITEM::VIA: case PNS_ITEM::VIA:
{ {
const PNS_VIA* via = static_cast<const PNS_VIA*>( aItem ); const PNS_VIA* via = static_cast<const PNS_VIA*>( aItem );
return circleBreakouts( aWidth, via->GetShape(), aPermitDiagonal ); return circleBreakouts( aWidth, via->Shape(), aPermitDiagonal );
} }
case PNS_ITEM::SOLID: case PNS_ITEM::SOLID:
{ {
const SHAPE* shape = aItem->GetShape(); const SHAPE* shape = aItem->Shape();
switch( shape->Type() ) switch( shape->Type() )
{ {
case SH_RECT: case SH_RECT:
return rectBreakouts( aWidth, shape, aPermitDiagonal ); return rectBreakouts( aWidth, shape, aPermitDiagonal );
case SH_SEGMENT:
{
const SHAPE_SEGMENT *seg = static_cast<const SHAPE_SEGMENT*> (shape);
const SHAPE_RECT rect = ApproximateSegmentAsRect ( *seg );
return rectBreakouts( aWidth, &rect, aPermitDiagonal );
}
case SH_CIRCLE: case SH_CIRCLE:
return circleBreakouts( aWidth, shape, aPermitDiagonal ); return circleBreakouts( aWidth, shape, aPermitDiagonal );
...@@ -594,14 +605,14 @@ PNS_OPTIMIZER::BreakoutList PNS_OPTIMIZER::computeBreakouts( int aWidth, ...@@ -594,14 +605,14 @@ PNS_OPTIMIZER::BreakoutList PNS_OPTIMIZER::computeBreakouts( int aWidth,
PNS_ITEM* PNS_OPTIMIZER::findPadOrVia( int aLayer, int aNet, const VECTOR2I& aP ) const PNS_ITEM* PNS_OPTIMIZER::findPadOrVia( int aLayer, int aNet, const VECTOR2I& aP ) const
{ {
PNS_NODE::OptJoint jt = m_world->FindJoint( aP, aLayer, aNet ); PNS_JOINT *jt = m_world->FindJoint( aP, aLayer, aNet );
if( !jt ) if( !jt )
return NULL; return NULL;
BOOST_FOREACH( PNS_ITEM* item, jt->GetLinkList() ) BOOST_FOREACH( PNS_ITEM* item, jt->LinkList() )
{ {
if( item->GetKind() == PNS_ITEM::VIA || item->GetKind() == PNS_ITEM::SOLID ) if( item->OfKind (PNS_ITEM::VIA | PNS_ITEM::SOLID ) )
return item; return item;
} }
...@@ -621,19 +632,16 @@ int PNS_OPTIMIZER::smartPadsSingle( PNS_LINE* aLine, PNS_ITEM* aPad, bool aEnd, ...@@ -621,19 +632,16 @@ int PNS_OPTIMIZER::smartPadsSingle( PNS_LINE* aLine, PNS_ITEM* aPad, bool aEnd,
typedef std::pair<int, SHAPE_LINE_CHAIN> RtVariant; typedef std::pair<int, SHAPE_LINE_CHAIN> RtVariant;
std::vector<RtVariant> variants; std::vector<RtVariant> variants;
BreakoutList breakouts = computeBreakouts( aLine->GetWidth(), aPad, true ); BreakoutList breakouts = computeBreakouts( aLine->Width(), aPad, true );
SHAPE_LINE_CHAIN line = ( aEnd ? aLine->GetCLine().Reverse() : aLine->GetCLine() ); SHAPE_LINE_CHAIN line = ( aEnd ? aLine->CLine().Reverse() : aLine->CLine() );
// bool startDiagonal = DIRECTION_45( line.CSegment(0) ).IsDiagonal();
int p_end = std::min( aEndVertex, std::min( 3, line.PointCount() - 1 ) ); int p_end = std::min( aEndVertex, std::min( 3, line.PointCount() - 1 ) );
for( int p = 1; p <= p_end; p++ ) for( int p = 1; p <= p_end; p++ )
{ {
BOOST_FOREACH( SHAPE_LINE_CHAIN & l, breakouts ) { BOOST_FOREACH( SHAPE_LINE_CHAIN & l, breakouts ) {
// PNSDisplayDebugLine (l, 0);
for( int diag = 0; diag < 2; diag++ ) for( int diag = 0; diag < 2; diag++ )
{ {
...@@ -642,11 +650,12 @@ int PNS_OPTIMIZER::smartPadsSingle( PNS_LINE* aLine, PNS_ITEM* aPad, bool aEnd, ...@@ -642,11 +650,12 @@ int PNS_OPTIMIZER::smartPadsSingle( PNS_LINE* aLine, PNS_ITEM* aPad, bool aEnd,
line.CPoint( p ), diag == 0 ); line.CPoint( p ), diag == 0 );
DIRECTION_45 dir_bkout( l.CSegment( -1 ) ); DIRECTION_45 dir_bkout( l.CSegment( -1 ) );
// DIRECTION_45 dir_head ( line.CSegment(p + 1));
if(!connect.SegmentCount())
continue;
int ang1 = dir_bkout.Angle( DIRECTION_45( connect.CSegment( 0 ) ) ); int ang1 = dir_bkout.Angle( DIRECTION_45( connect.CSegment( 0 ) ) );
int ang2 = 0; int ang2 = 0;
// int ang2 = dir_head.Angle ( DIRECTION_45(connect.CSegment(-1) ));
if( (ang1 | ang2) & ForbiddenAngles ) if( (ang1 | ang2) & ForbiddenAngles )
continue; continue;
...@@ -662,7 +671,6 @@ int PNS_OPTIMIZER::smartPadsSingle( PNS_LINE* aLine, PNS_ITEM* aPad, bool aEnd, ...@@ -662,7 +671,6 @@ int PNS_OPTIMIZER::smartPadsSingle( PNS_LINE* aLine, PNS_ITEM* aPad, bool aEnd,
v.Append( line.CPoint( i ) ); v.Append( line.CPoint( i ) );
PNS_LINE tmp( *aLine, v ); PNS_LINE tmp( *aLine, v );
// tmp.GetLine().Simplify();
int cc = tmp.CountCorners( ForbiddenAngles ); int cc = tmp.CountCorners( ForbiddenAngles );
if( cc == 0 ) if( cc == 0 )
...@@ -689,18 +697,12 @@ int PNS_OPTIMIZER::smartPadsSingle( PNS_LINE* aLine, PNS_ITEM* aPad, bool aEnd, ...@@ -689,18 +697,12 @@ int PNS_OPTIMIZER::smartPadsSingle( PNS_LINE* aLine, PNS_ITEM* aPad, bool aEnd,
if( !checkColliding( &tmp ) ) if( !checkColliding( &tmp ) )
{ {
/* if(aEnd)
* PNSDisplayDebugLine (l_best, 6);
* else
* PNSDisplayDebugLine (l_best, 5);*/
if( cost < min_cost || ( cost == min_cost && len < min_len ) ) if( cost < min_cost || ( cost == min_cost && len < min_len ) )
{ {
l_best = vp.second; l_best = vp.second;
p_best = vp.first; p_best = vp.first;
found = true; found = true;
// if(cost == min_cost)
if( cost == min_cost ) if( cost == min_cost )
min_len = std::min( len, min_len ); min_len = std::min( len, min_len );
...@@ -711,12 +713,6 @@ int PNS_OPTIMIZER::smartPadsSingle( PNS_LINE* aLine, PNS_ITEM* aPad, bool aEnd, ...@@ -711,12 +713,6 @@ int PNS_OPTIMIZER::smartPadsSingle( PNS_LINE* aLine, PNS_ITEM* aPad, bool aEnd,
if( found ) if( found )
{ {
// printf("end: %d, p-best: %d, p-end: %d, p-total: %d\n", aEnd, p_best, p_end, l_best.PointCount());
// if(!aEnd)
// PNSDisplayDebugLine (l_best, 5);
// else
aLine->SetShape( l_best ); aLine->SetShape( l_best );
return p_best; return p_best;
} }
...@@ -724,18 +720,17 @@ int PNS_OPTIMIZER::smartPadsSingle( PNS_LINE* aLine, PNS_ITEM* aPad, bool aEnd, ...@@ -724,18 +720,17 @@ int PNS_OPTIMIZER::smartPadsSingle( PNS_LINE* aLine, PNS_ITEM* aPad, bool aEnd,
return -1; return -1;
} }
bool PNS_OPTIMIZER::runSmartPads( PNS_LINE* aLine ) bool PNS_OPTIMIZER::runSmartPads( PNS_LINE* aLine )
{ {
SHAPE_LINE_CHAIN& line = aLine->GetLine(); SHAPE_LINE_CHAIN& line = aLine->Line();
if( line.PointCount() < 3 ) if( line.PointCount() < 3 )
return false; return false;
VECTOR2I p_start = line.CPoint( 0 ), p_end = line.CPoint( -1 ); VECTOR2I p_start = line.CPoint( 0 ), p_end = line.CPoint( -1 );
PNS_ITEM* startPad = findPadOrVia( aLine->GetLayer(), aLine->GetNet(), p_start ); PNS_ITEM* startPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_start );
PNS_ITEM* endPad = findPadOrVia( aLine->GetLayer(), aLine->GetNet(), p_end ); PNS_ITEM* endPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_end );
int vtx = -1; int vtx = -1;
...@@ -746,16 +741,68 @@ bool PNS_OPTIMIZER::runSmartPads( PNS_LINE* aLine ) ...@@ -746,16 +741,68 @@ bool PNS_OPTIMIZER::runSmartPads( PNS_LINE* aLine )
smartPadsSingle( aLine, endPad, true, smartPadsSingle( aLine, endPad, true,
vtx < 0 ? line.PointCount() - 1 : line.PointCount() - 1 - vtx ); vtx < 0 ? line.PointCount() - 1 : line.PointCount() - 1 - vtx );
aLine->GetLine().Simplify(); aLine->Line().Simplify();
return true; return true;
} }
bool PNS_OPTIMIZER::Optimize( PNS_LINE* aLine, int aEffortLevel, PNS_NODE* aWorld ) bool PNS_OPTIMIZER::Optimize( PNS_LINE* aLine, int aEffortLevel, PNS_NODE* aWorld )
{ {
PNS_OPTIMIZER opt( aWorld ? aWorld : aLine->GetWorld() ); PNS_OPTIMIZER opt( aWorld );
opt.SetEffortLevel( aEffortLevel ); opt.SetEffortLevel( aEffortLevel );
opt.SetCollisionMask( -1 ); opt.SetCollisionMask( -1 );
return opt.Optimize( aLine ); return opt.Optimize( aLine );
} }
bool PNS_OPTIMIZER::fanoutCleanup( PNS_LINE * aLine )
{
if( aLine->PointCount() < 3 )
return false;
VECTOR2I p_start = aLine->CPoint( 0 ), p_end = aLine->CPoint( -1 );
PNS_ITEM* startPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_start );
PNS_ITEM* endPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_end );
int thr = aLine->Width() * 10;
int len = aLine->CLine().Length();
if(!startPad)
return false;
bool startMatch = startPad->OfKind(PNS_ITEM::VIA | PNS_ITEM::SOLID);
bool endMatch = false;
if(endPad)
{
endMatch = endPad->OfKind(PNS_ITEM::VIA | PNS_ITEM::SOLID);
} else {
endMatch = aLine->EndsWithVia();
}
if(startMatch && endMatch && len < thr)
{
for(int i = 0; i < 2; i++ )
{
SHAPE_LINE_CHAIN l2 = DIRECTION_45().BuildInitialTrace(p_start, p_end, i);
PNS_ROUTER::GetInstance()->DisplayDebugLine (l2, 4, 10000);
PNS_LINE repl;
repl = PNS_LINE (*aLine, l2 );
if (!m_world->CheckColliding(&repl))
{
aLine->SetShape(repl.CLine());
return true;
}
}
}
return false;
}
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_OPTIMIZER_H #ifndef __PNS_OPTIMIZER_H
...@@ -27,6 +27,8 @@ ...@@ -27,6 +27,8 @@
#include <geometry/shape_index_list.h> #include <geometry/shape_index_list.h>
#include <geometry/shape_line_chain.h> #include <geometry/shape_line_chain.h>
#include "range.h"
class PNS_NODE; class PNS_NODE;
class PNS_LINE; class PNS_LINE;
class PNS_ROUTER; class PNS_ROUTER;
...@@ -90,17 +92,17 @@ public: ...@@ -90,17 +92,17 @@ public:
{ {
MERGE_SEGMENTS = 0x01, MERGE_SEGMENTS = 0x01,
SMART_PADS = 0x02, SMART_PADS = 0x02,
MERGE_OBTUSE = 0x04 MERGE_OBTUSE = 0x04,
FANOUT_CLEANUP = 0x08
}; };
PNS_OPTIMIZER( PNS_NODE* aWorld ); PNS_OPTIMIZER( PNS_NODE* aWorld );
~PNS_OPTIMIZER(); ~PNS_OPTIMIZER();
///> a quick shortcut to optmize a line without creating and setting up an optimizer ///> a quick shortcut to optmize a line without creating and setting up an optimizer
static bool Optimize( PNS_LINE* aLine, int aEffortLevel, PNS_NODE* aWorld = NULL ); 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 );
int aStartVertex = 0, int aEndVertex = -1 );
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 );
...@@ -135,6 +137,7 @@ private: ...@@ -135,6 +137,7 @@ private:
bool removeUglyCorners( PNS_LINE* aLine ); bool removeUglyCorners( PNS_LINE* aLine );
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 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 );
......
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cstdio> #include <cstdio>
...@@ -42,11 +42,13 @@ ...@@ -42,11 +42,13 @@
#include "pns_solid.h" #include "pns_solid.h"
#include "pns_utils.h" #include "pns_utils.h"
#include "pns_router.h" #include "pns_router.h"
#include "pns_shove.h"
#include "pns_dragger.h"
#include <router/router_preview_item.h> #include <router/router_preview_item.h>
#include <class_board.h> #include <class_board.h>
#include <class_board_item.h> #include <class_board_connected_item.h>
#include <class_module.h> #include <class_module.h>
#include <class_track.h> #include <class_track.h>
#include <ratsnest_data.h> #include <ratsnest_data.h>
...@@ -80,13 +82,29 @@ public: ...@@ -80,13 +82,29 @@ public:
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 * item ) const
{
if(!item->Parent() || item->Parent()->Type() != PCB_PAD_T )
return 0;
const D_PAD *pad = static_cast<D_PAD *>( item->Parent() );
return pad->GetLocalClearance();
}
int operator()( const PNS_ITEM* a, const PNS_ITEM* b ) int operator()( const PNS_ITEM* a, const PNS_ITEM* b )
{ {
int net_a = a->GetNet(); int net_a = a->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 = b->GetNet(); int net_b = b->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);
int pad_a = localPadClearance( a );
int pad_b = localPadClearance( b );
cl_a = std::max(cl_a, pad_a);
cl_b = std::max(cl_b, pad_b);
return std::max( cl_a, cl_b ); return std::max( cl_a, cl_b );
} }
...@@ -97,11 +115,12 @@ private: ...@@ -97,11 +115,12 @@ private:
PNS_ITEM* PNS_ROUTER::syncPad( D_PAD* aPad ) PNS_ITEM* PNS_ROUTER::syncPad( D_PAD* aPad )
{ {
PNS_LAYERSET layers( 0, 15 ); PNS_LAYERSET layers ( 0, 15 );
switch( aPad->GetAttribute() ) switch( aPad->GetAttribute() )
{ {
case PAD_STANDARD: case PAD_STANDARD:
layers = PNS_LAYERSET( 0, 15 );
break; break;
case PAD_SMD: case PAD_SMD:
...@@ -130,49 +149,64 @@ PNS_ITEM* PNS_ROUTER::syncPad( D_PAD* aPad ) ...@@ -130,49 +149,64 @@ PNS_ITEM* PNS_ROUTER::syncPad( D_PAD* aPad )
solid->SetLayers( layers ); solid->SetLayers( layers );
solid->SetNet( aPad->GetNetCode() ); solid->SetNet( aPad->GetNetCode() );
solid->SetParent( aPad );
wxPoint wx_c = aPad->GetPosition(); wxPoint wx_c = aPad->GetPosition();
wxSize wx_sz = aPad->GetSize(); wxSize wx_sz = aPad->GetSize();
VECTOR2I c( wx_c.x, wx_c.y ); VECTOR2I c( wx_c.x, wx_c.y );
VECTOR2I sz( wx_sz.x, wx_sz.y ); VECTOR2I sz( wx_sz.x, wx_sz.y );
solid->SetCenter( c ); solid->SetPos( c );
double orient = aPad->GetOrientation() / 10.0; double orient = aPad->GetOrientation() / 10.0;
bool nonOrtho = false;
if( orient == 90.0 || orient == 270.0 ) if( orient == 90.0 || orient == 270.0 )
sz = VECTOR2I( sz.y, sz.x ); sz = VECTOR2I( sz.y, sz.x );
else if( orient != 0.0 && orient != 180.0 ) else if( orient != 0.0 && orient != 180.0 )
{ {
TRACEn( 0, "non-orthogonal pad rotations not supported yet" ); // rotated pads are replaced by for the moment by circles due to my laziness ;)
delete solid; solid->SetShape ( new SHAPE_CIRCLE (c, std::min(sz.x, sz.y) / 2 ) );
return NULL; nonOrtho = true;
} }
switch( aPad->GetShape() ) if(!nonOrtho)
{ {
case PAD_CIRCLE: switch( aPad->GetShape() )
solid->SetShape( new SHAPE_CIRCLE( c, sz.x / 2 ) ); {
break; case PAD_CIRCLE:
case PAD_OVAL:
if( sz.x == sz.y )
solid->SetShape( new SHAPE_CIRCLE( c, sz.x / 2 ) ); solid->SetShape( new SHAPE_CIRCLE( c, sz.x / 2 ) );
else break;
solid->SetShape( new SHAPE_RECT( c - sz / 2, sz.x, sz.y ) );
break;
case PAD_RECT: case PAD_OVAL:
solid->SetShape( new SHAPE_RECT( c - sz / 2, sz.x, sz.y ) ); if( sz.x == sz.y )
break; solid->SetShape( new SHAPE_CIRCLE( c, sz.x / 2 ) );
else {
VECTOR2I delta;
default: if (sz.x > sz.y)
TRACEn( 0, "unsupported pad shape" ); delta = VECTOR2I((sz.x - sz.y) / 2, 0);
delete solid; else
return NULL; delta = VECTOR2I(0, (sz.y - sz.x) / 2);
SHAPE_SEGMENT *shape = new SHAPE_SEGMENT( c - delta, c + delta, std::min(sz.x, sz.y) );
solid->SetShape( shape );
}
break;
case PAD_RECT:
solid->SetShape( new SHAPE_RECT( c - sz / 2, sz.x, sz.y ) );
break;
default:
TRACEn( 0, "unsupported pad shape" );
delete solid;
return NULL;
}
} }
solid->SetParent( aPad );
return solid; return solid;
} }
...@@ -197,7 +231,9 @@ PNS_ITEM* PNS_ROUTER::syncVia( VIA* aVia ) ...@@ -197,7 +231,9 @@ PNS_ITEM* PNS_ROUTER::syncVia( VIA* aVia )
aVia->GetWidth(), aVia->GetWidth(),
aVia->GetNetCode() ); aVia->GetNetCode() );
v->SetDrill ( aVia->GetDrill() );
v->SetParent( aVia ); v->SetParent( aVia );
return v; return v;
} }
...@@ -233,7 +269,6 @@ int PNS_ROUTER::NextCopperLayer( bool aUp ) ...@@ -233,7 +269,6 @@ int PNS_ROUTER::NextCopperLayer( bool aUp )
void PNS_ROUTER::SyncWorld() void PNS_ROUTER::SyncWorld()
{ {
std::vector<D_PAD*> pads;
if( !m_board ) if( !m_board )
{ {
...@@ -243,11 +278,12 @@ void PNS_ROUTER::SyncWorld() ...@@ -243,11 +278,12 @@ void PNS_ROUTER::SyncWorld()
ClearWorld(); ClearWorld();
int worstClearance = m_board->GetDesignSettings().GetBiggestClearanceValue();
m_clearanceFunc = new PCBNEW_CLEARANCE_FUNC( m_board ); m_clearanceFunc = new 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( 1000000 ); // m_board->GetBiggestClearanceValue()); m_world->SetMaxClearance( 4 * worstClearance );
pads = m_board->GetPads();
for( MODULE* module = m_board->m_Modules; module; module = module->Next() ) for( MODULE* module = m_board->m_Modules; module; module = module->Next() )
{ {
...@@ -273,8 +309,6 @@ void PNS_ROUTER::SyncWorld() ...@@ -273,8 +309,6 @@ void PNS_ROUTER::SyncWorld()
if( item ) if( item )
m_world->Add( item ); m_world->Add( item );
} }
m_placer = new PNS_LINE_PLACER( m_world );
} }
...@@ -291,10 +325,8 @@ PNS_ROUTER::PNS_ROUTER() ...@@ -291,10 +325,8 @@ PNS_ROUTER::PNS_ROUTER()
m_world = NULL; m_world = NULL;
m_placer = NULL; m_placer = NULL;
m_previewItems = NULL; m_previewItems = NULL;
m_start_diagonal = false;
m_board = NULL; m_board = NULL;
m_dragger = NULL;
TRACE( 1, "m_board = %p\n", m_board );
} }
...@@ -351,11 +383,11 @@ void PNS_ROUTER::ClearWorld() ...@@ -351,11 +383,11 @@ void PNS_ROUTER::ClearWorld()
} }
void PNS_ROUTER::SetCurrentWidth( int w ) /*void PNS_ROUTER::SetCurrentWidth( int w )
{ {
// fixme: change width while routing // fixme: change width while routing
m_currentWidth = w; m_currentWidth = w;
} }*/
bool PNS_ROUTER::RoutingInProgress() const bool PNS_ROUTER::RoutingInProgress() const
...@@ -369,7 +401,11 @@ const PNS_ITEMSET PNS_ROUTER::QueryHoverItems( const VECTOR2I& aP ) ...@@ -369,7 +401,11 @@ const PNS_ITEMSET PNS_ROUTER::QueryHoverItems( const VECTOR2I& aP )
if( m_state == IDLE ) if( m_state == IDLE )
return m_world->HitTest( aP ); return m_world->HitTest( aP );
else else
return m_placer->GetCurrentNode()->HitTest( aP ); {
//assert ( m_placer->GetCurrentNode()->checkExists() );
//TRACE(0,"query-hover [%p]", m_placer->GetCurrentNode());
return m_placer->CurrentNode()->HitTest( aP );
}
} }
...@@ -383,23 +419,23 @@ const VECTOR2I PNS_ROUTER::SnapToItem( PNS_ITEM* item, VECTOR2I aP, bool& aSplit ...@@ -383,23 +419,23 @@ const VECTOR2I PNS_ROUTER::SnapToItem( PNS_ITEM* item, VECTOR2I aP, bool& aSplit
return aP; return aP;
} }
switch( item->GetKind() ) switch( item->Kind() )
{ {
case PNS_ITEM::SOLID: case PNS_ITEM::SOLID:
anchor = static_cast<PNS_SOLID*>(item)->GetCenter(); anchor = static_cast<PNS_SOLID*>(item)->Pos();
aSplitsSegment = false; aSplitsSegment = false;
break; break;
case PNS_ITEM::VIA: case PNS_ITEM::VIA:
anchor = static_cast<PNS_VIA*>(item)->GetPos(); anchor = static_cast<PNS_VIA*>(item)->Pos();
aSplitsSegment = false; aSplitsSegment = false;
break; break;
case PNS_ITEM::SEGMENT: case PNS_ITEM::SEGMENT:
{ {
PNS_SEGMENT* seg = static_cast<PNS_SEGMENT*>( item ); PNS_SEGMENT* seg = static_cast<PNS_SEGMENT*>( item );
const SEG& s = seg->GetSeg(); const SEG& s = seg->Seg();
int w = seg->GetWidth(); int w = seg->Width();
aSplitsSegment = false; aSplitsSegment = false;
...@@ -423,50 +459,52 @@ const VECTOR2I PNS_ROUTER::SnapToItem( PNS_ITEM* item, VECTOR2I aP, bool& aSplit ...@@ -423,50 +459,52 @@ const VECTOR2I PNS_ROUTER::SnapToItem( PNS_ITEM* item, VECTOR2I aP, bool& aSplit
return anchor; return anchor;
} }
bool PNS_ROUTER::StartDragging( const VECTOR2I& aP, PNS_ITEM* aStartItem )
{
if(!aStartItem || aStartItem->OfKind(PNS_ITEM::SOLID))
return false;
m_dragger = new PNS_DRAGGER ( this );
m_dragger->SetWorld( m_world );
if( m_dragger->Start ( aP, aStartItem ) )
m_state = DRAG_SEGMENT;
else {
delete m_dragger;
m_state = IDLE;
return false;
}
return true;
}
void PNS_ROUTER::StartRouting( const VECTOR2I& aP, PNS_ITEM* aStartItem )
{
VECTOR2I p;
static int unknowNetIdx = 0; // -10000;
m_placingVia = false;
m_startsOnVia = false;
m_currentNet = -1;
bool splitSeg = false;
p = SnapToItem( aStartItem, aP, splitSeg );
if( !aStartItem || aStartItem->GetNet() < 0 )
m_currentNet = unknowNetIdx--;
else
m_currentNet = aStartItem->GetNet();
m_currentStart = p; bool PNS_ROUTER::StartRouting( const VECTOR2I& aP, PNS_ITEM* aStartItem )
m_originalStart = p; {
m_currentEnd = p;
m_placer->SetInitialDirection( m_start_diagonal ? DIRECTION_45(
DIRECTION_45::NE ) : DIRECTION_45( DIRECTION_45::N ) );
m_placer->StartPlacement( m_originalStart, m_currentNet, m_currentWidth, m_currentLayer );
m_state = ROUTE_TRACK; m_state = ROUTE_TRACK;
if( splitSeg ) m_placer = new PNS_LINE_PLACER( this );
splitAdjacentSegments( m_placer->GetCurrentNode(), aStartItem, p ); m_placer->SetLayer( m_currentLayer );
m_placer->SetWidth ( m_settings.GetTrackWidth() );
m_placer->Start( aP, aStartItem );
m_currentEnd = aP;
m_currentEndItem = NULL;
return true;
} }
const VECTOR2I PNS_ROUTER::GetCurrentEnd() const const VECTOR2I PNS_ROUTER::CurrentEnd() const
{ {
return m_currentEnd; return m_currentEnd;
} }
void PNS_ROUTER::EraseView() void PNS_ROUTER::eraseView()
{ {
BOOST_FOREACH( BOARD_ITEM* item, m_hiddenItems ) BOOST_FOREACH( BOARD_ITEM* item, m_hiddenItems )
{ {
item->ViewSetVisible( true ); item->ViewSetVisible( true );
} }
...@@ -480,71 +518,131 @@ void PNS_ROUTER::EraseView() ...@@ -480,71 +518,131 @@ void PNS_ROUTER::EraseView()
} }
void PNS_ROUTER::DisplayItem( const PNS_ITEM* aItem, bool aIsHead ) void PNS_ROUTER::DisplayItem( const PNS_ITEM* aItem, int aColor, int aClearance )
{ {
ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( aItem, m_previewItems ); ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( aItem, m_previewItems );
m_previewItems->Add( pitem ); if(aColor >= 0)
pitem->SetColor ( KIGFX::COLOR4D ( aColor ));
if( aIsHead ) if(aClearance >= 0)
pitem->MarkAsHead(); pitem->SetClearance ( aClearance );
m_previewItems->Add( pitem );
pitem->ViewSetVisible( true ); pitem->ViewSetVisible( true );
m_previewItems->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY | KIGFX::VIEW_ITEM::APPEARANCE ); m_previewItems->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY | KIGFX::VIEW_ITEM::APPEARANCE );
} }
void PNS_ROUTER::DisplayItems( const PNS_ITEMSET& aItems )
{
BOOST_FOREACH(const PNS_ITEM *item, aItems.CItems())
DisplayItem(item);
}
void PNS_ROUTER::DisplayDebugLine( const SHAPE_LINE_CHAIN& aLine, int aType, int aWidth ) void PNS_ROUTER::DisplayDebugLine( const SHAPE_LINE_CHAIN& aLine, int aType, int aWidth )
{ {
ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( NULL, m_previewItems ); ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( NULL, m_previewItems );
pitem->DebugLine( aLine, aWidth, aType ); pitem->Line( aLine, aWidth, aType );
m_previewItems->Add( pitem ); m_previewItems->Add( pitem );
pitem->ViewSetVisible( true ); pitem->ViewSetVisible( true );
m_previewItems->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY | KIGFX::VIEW_ITEM::APPEARANCE ); m_previewItems->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY | KIGFX::VIEW_ITEM::APPEARANCE );
} }
void PNS_ROUTER::DisplayDebugBox( const BOX2I& aBox, int aType, int aWidth )
void PNS_ROUTER::DisplayDebugPoint( const VECTOR2I pos, int aType )
{ {
} ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( NULL, m_previewItems );
pitem->Point( pos, aType );
m_previewItems->Add( pitem );
pitem->ViewSetVisible( true );
m_previewItems->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY | KIGFX::VIEW_ITEM::APPEARANCE );
}
void PNS_ROUTER::Move( const VECTOR2I& aP, PNS_ITEM* endItem ) void PNS_ROUTER::Move( const VECTOR2I& aP, PNS_ITEM* endItem )
{ {
PNS_NODE::ItemVector removed, added; m_currentEnd = aP;
VECTOR2I p = aP; m_currentEndItem = endItem;
if( m_state == IDLE ) switch( m_state )
return;
// TODO is something missing here?
if( m_state == START_ROUTING )
{ {
case ROUTE_TRACK:
movePlacing( aP, endItem );
break;
case DRAG_SEGMENT:
moveDragging (aP, endItem );
break;
default:
break;
} }
}
EraseView(); void PNS_ROUTER::moveDragging( const VECTOR2I& aP, PNS_ITEM* endItem )
{
eraseView();
m_currentEnd = p; m_dragger->Drag( aP );
m_placer->Route( p ); PNS_ITEMSET dragged = m_dragger->Traces();
PNS_LINE current = m_placer->GetTrace(); updateView ( m_dragger->CurrentNode ( ), dragged );
}
DisplayItem( &current, true ); void PNS_ROUTER::markViolations( PNS_NODE *aNode, PNS_ITEMSET& aCurrent, PNS_NODE::ItemVector& aRemoved )
{
BOOST_FOREACH(PNS_ITEM *item, aCurrent.Items())
{
PNS_NODE::Obstacles obstacles;
if( current.EndsWithVia() ) aNode->QueryColliding( item, obstacles, PNS_ITEM::ANY );
DisplayItem( &current.GetVia(), true );
if ( item->OfKind(PNS_ITEM::LINE ) )
{
PNS_LINE *l = static_cast<PNS_LINE *> (item);
if (l->EndsWithVia())
{
PNS_VIA v ( l->Via() );
aNode->QueryColliding(&v , obstacles, PNS_ITEM::ANY );
}
}
m_placer->GetCurrentNode()->GetUpdatedItems( removed, added ); BOOST_FOREACH(PNS_OBSTACLE& obs, obstacles)
{
int clearance = aNode->GetClearance( item, obs.item );
std::auto_ptr<PNS_ITEM> tmp ( obs.item->Clone() );
tmp->Mark ( MK_VIOLATION );
DisplayItem( tmp.get(), -1, clearance );
aRemoved.push_back(obs.item);
}
}
}
void PNS_ROUTER::updateView( PNS_NODE *aNode, PNS_ITEMSET& aCurrent )
{
PNS_NODE::ItemVector removed, added;
PNS_NODE::Obstacles obstacles;
if(!aNode)
return;
if( Settings().Mode() == RM_MarkObstacles )
markViolations(aNode, aCurrent, removed);
aNode->GetUpdatedItems( removed, added );
BOOST_FOREACH( PNS_ITEM* item, added ) BOOST_FOREACH( PNS_ITEM* item, added )
{ {
DisplayItem( item ); DisplayItem( item );
} }
BOOST_FOREACH( PNS_ITEM* item, removed ) BOOST_FOREACH( PNS_ITEM* item, removed )
{ {
BOARD_ITEM* parent = item->GetParent(); BOARD_CONNECTED_ITEM* parent = item->Parent();
if( parent ) if( parent )
{ {
...@@ -558,32 +656,36 @@ void PNS_ROUTER::Move( const VECTOR2I& aP, PNS_ITEM* endItem ) ...@@ -558,32 +656,36 @@ void PNS_ROUTER::Move( const VECTOR2I& aP, PNS_ITEM* endItem )
} }
void PNS_ROUTER::splitAdjacentSegments( PNS_NODE* aNode, PNS_ITEM* aSeg, const VECTOR2I& aP ) void PNS_ROUTER::ApplySettings()
{ {
if( aSeg && aSeg->OfKind( PNS_ITEM::SEGMENT ) ) // Change track/via size settings
if( m_state == ROUTE_TRACK)
{ {
PNS_NODE::OptJoint jt = aNode->FindJoint( aP, aSeg->GetLayers().Start(), aSeg->GetNet() ); m_placer->UpdateSizes( m_settings );
m_placer->Move( m_currentEnd, m_currentEndItem );
movePlacing( m_currentEnd, m_currentEndItem );
}
if( jt && jt->LinkCount() >= 1 ) // TODO handle mode/optimization/other options change
return; }
PNS_SEGMENT* s_old = static_cast<PNS_SEGMENT*>( aSeg ); void PNS_ROUTER::movePlacing( const VECTOR2I& aP, PNS_ITEM* endItem )
PNS_SEGMENT* s_new[2]; {
eraseView();
s_new[0] = s_old->Clone(); m_placer->Move( aP, endItem );
s_new[1] = s_old->Clone(); PNS_LINE current = m_placer->Trace();
DisplayItem( &current );
s_new[0]->SetEnds( s_old->GetSeg().A, aP ); if( current.EndsWithVia() )
s_new[1]->SetEnds( aP, s_old->GetSeg().B ); DisplayItem( &current.Via() );
aNode->Remove( s_old ); PNS_ITEMSET tmp ( &current );
aNode->Add( s_new[0] ); updateView ( m_placer->CurrentNode ( true ), tmp );
aNode->Add( s_new[1] );
}
} }
void PNS_ROUTER::CommitRouting( PNS_NODE* aNode )
void PNS_ROUTER::commitRouting( PNS_NODE* aNode )
{ {
PNS_NODE::ItemVector removed, added; PNS_NODE::ItemVector removed, added;
...@@ -591,13 +693,13 @@ void PNS_ROUTER::commitRouting( PNS_NODE* aNode ) ...@@ -591,13 +693,13 @@ void PNS_ROUTER::commitRouting( PNS_NODE* aNode )
for( unsigned int i = 0; i < removed.size(); i++ ) for( unsigned int i = 0; i < removed.size(); i++ )
{ {
BOARD_CONNECTED_ITEM* parent = removed[i]->GetParent(); BOARD_CONNECTED_ITEM* parent = removed[i]->Parent();
if( parent ) if( parent )
{ {
m_undoBuffer.PushItem( ITEM_PICKER( parent, UR_DELETED ) );
m_board->Remove( parent );
m_view->Remove( parent ); m_view->Remove( parent );
m_board->Remove( parent );
m_undoBuffer.PushItem( ITEM_PICKER( parent, UR_DELETED ) );
} }
} }
...@@ -605,19 +707,19 @@ void PNS_ROUTER::commitRouting( PNS_NODE* aNode ) ...@@ -605,19 +707,19 @@ void PNS_ROUTER::commitRouting( PNS_NODE* aNode )
{ {
BOARD_CONNECTED_ITEM* newBI = NULL; BOARD_CONNECTED_ITEM* newBI = NULL;
switch( item->GetKind() ) switch( item->Kind() )
{ {
case PNS_ITEM::SEGMENT: case PNS_ITEM::SEGMENT:
{ {
PNS_SEGMENT* seg = static_cast<PNS_SEGMENT*>( item ); PNS_SEGMENT* seg = static_cast<PNS_SEGMENT*>( item );
TRACK* track = new TRACK( m_board ); TRACK* track = new TRACK( m_board );
const SEG& s = seg->GetSeg(); const SEG& s = seg->Seg();
track->SetStart( wxPoint( s.A.x, s.A.y ) ); track->SetStart( wxPoint( s.A.x, s.A.y ) );
track->SetEnd( wxPoint( s.B.x, s.B.y ) ); track->SetEnd( wxPoint( s.B.x, s.B.y ) );
track->SetWidth( seg->GetWidth() ); track->SetWidth( seg->Width() );
track->SetLayer( seg->GetLayers().Start() ); track->SetLayer( seg->Layers().Start() );
track->SetNetCode( seg->GetNet() ); track->SetNetCode( seg->Net() );
newBI = track; newBI = track;
break; break;
} }
...@@ -626,9 +728,10 @@ void PNS_ROUTER::commitRouting( PNS_NODE* aNode ) ...@@ -626,9 +728,10 @@ void PNS_ROUTER::commitRouting( PNS_NODE* aNode )
{ {
VIA* via_board = new VIA( m_board ); VIA* via_board = new VIA( m_board );
PNS_VIA* via = static_cast<PNS_VIA*>( item ); PNS_VIA* via = static_cast<PNS_VIA*>( item );
via_board->SetPosition( wxPoint( via->GetPos().x, via->GetPos().y ) ); via_board->SetPosition( wxPoint( via->Pos().x, via->Pos().y ) );
via_board->SetWidth( via->GetDiameter() ); via_board->SetWidth( via->Diameter() );
via_board->SetNetCode( via->GetNet() ); via_board->SetDrill( via->Drill() );
via_board->SetNetCode( via->Net() );
newBI = via_board; newBI = via_board;
break; break;
} }
...@@ -657,12 +760,12 @@ PNS_VIA* PNS_ROUTER::checkLoneVia( PNS_JOINT* aJoint ) const ...@@ -657,12 +760,12 @@ PNS_VIA* PNS_ROUTER::checkLoneVia( PNS_JOINT* aJoint ) const
PNS_VIA* theVia = NULL; PNS_VIA* theVia = NULL;
PNS_LAYERSET l; PNS_LAYERSET l;
BOOST_FOREACH( PNS_ITEM* item, aJoint->GetLinkList() ) BOOST_FOREACH( PNS_ITEM* item, aJoint->LinkList() )
{ {
if( item->GetKind() == PNS_ITEM::VIA ) if( item->Kind() == PNS_ITEM::VIA )
theVia = static_cast<PNS_VIA*>( item ); theVia = static_cast<PNS_VIA*>( item );
l.Merge( item->GetLayers() ); l.Merge( item->Layers() );
} }
if( l.Start() == l.End() ) if( l.Start() == l.End() )
...@@ -671,103 +774,34 @@ PNS_VIA* PNS_ROUTER::checkLoneVia( PNS_JOINT* aJoint ) const ...@@ -671,103 +774,34 @@ PNS_VIA* PNS_ROUTER::checkLoneVia( PNS_JOINT* aJoint ) const
return NULL; return NULL;
} }
PNS_NODE* PNS_ROUTER::removeLoops( PNS_NODE* aNode, PNS_SEGMENT* aLatestSeg )
{
PNS_LINE* ourLine = aNode->AssembleLine( aLatestSeg );
PNS_NODE* cleaned = aNode->Branch();
PNS_JOINT a, b;
std::vector<PNS_LINE*> lines;
cleaned->FindLineEnds( ourLine, a, b );
cleaned->FindLinesBetweenJoints( a, b, lines );
BOOST_FOREACH( PNS_LINE* line, lines )
{
if( !( line->ContainsSegment( aLatestSeg ) ) )
{
cleaned->Remove( line );
}
}
return cleaned;
}
bool PNS_ROUTER::FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ) bool PNS_ROUTER::FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem )
{ {
bool real_end = false; bool rv = false;
PNS_LINE pl = m_placer->GetTrace();
const SHAPE_LINE_CHAIN& l = pl.GetCLine();
if( !l.SegmentCount() )
return true;
VECTOR2I p_pre_last = l.CPoint( -1 );
const VECTOR2I p_last = l.CPoint( -1 );
DIRECTION_45 d_last( l.CSegment( -1 ) );
if( l.PointCount() > 2 )
p_pre_last = l.CPoint( -2 );
if( aEndItem && m_currentNet >= 0 && m_currentNet == aEndItem->GetNet() ) switch(m_state)
real_end = true;
int last = ( real_end || m_placingVia ) ? l.SegmentCount() : std::max( 1, l.SegmentCount() - 1 );
PNS_NODE* latest = m_placer->GetCurrentNode();
if( real_end )
splitAdjacentSegments( latest, aEndItem, aP );
PNS_SEGMENT* lastSeg = NULL;
for( int i = 0; i < last; i++ )
{ {
const SEG& s = pl.GetCLine().CSegment( i ); case ROUTE_TRACK:
PNS_SEGMENT* seg = new PNS_SEGMENT( s, m_currentNet ); rv = m_placer->FixRoute (aP, aEndItem);
seg->SetWidth( pl.GetWidth() ); m_placingVia = false;
seg->SetLayer( m_currentLayer );
latest->Add( seg ); break;
lastSeg = seg; case DRAG_SEGMENT:
} rv = m_dragger->FixRoute ();
break;
if( pl.EndsWithVia() )
latest->Add( pl.GetVia().Clone() ); default:
break;
if( real_end ) }
latest = removeLoops( latest, lastSeg );
commitRouting( latest ); if(rv)
StopRouting();
EraseView();
return rv;
if( real_end )
{
m_state = IDLE;
// m_world->KillChildren();
}
else
{
m_state = ROUTE_TRACK;
m_placer->SetInitialDirection( d_last );
m_currentStart = m_placingVia ? p_last : p_pre_last;
if( m_placingVia )
m_currentLayer = NextCopperLayer( true );
m_placer->StartPlacement( m_currentStart, m_currentNet, m_currentWidth, m_currentLayer );
m_startsOnVia = m_placingVia;
m_placingVia = false;
}
return real_end;
} }
void PNS_ROUTER::StopRouting() void PNS_ROUTER::StopRouting()
{ {
// Update the ratsnest with new changes // Update the ratsnest with new changes
...@@ -776,25 +810,30 @@ void PNS_ROUTER::StopRouting() ...@@ -776,25 +810,30 @@ void PNS_ROUTER::StopRouting()
if( !RoutingInProgress() ) if( !RoutingInProgress() )
return; return;
EraseView(); if(m_placer)
delete m_placer;
if(m_dragger)
delete m_dragger;
m_placer = NULL;
m_dragger = NULL;
eraseView();
m_state = IDLE; m_state = IDLE;
m_world->KillChildren(); m_world->KillChildren();
m_world->ClearRanks();
} }
void PNS_ROUTER::FlipPosture() void PNS_ROUTER::FlipPosture()
{ {
if( m_placer->GetTail().GetCLine().SegmentCount() == 0 ) if(m_state == ROUTE_TRACK)
{ {
m_start_diagonal = !m_start_diagonal;
m_placer->SetInitialDirection( m_start_diagonal ?
DIRECTION_45( DIRECTION_45::NE ) : DIRECTION_45( DIRECTION_45::N ) );
}
else
m_placer->FlipPosture(); m_placer->FlipPosture();
m_placer->Move ( m_currentEnd, m_currentEndItem );
Move( m_currentEnd, NULL ); }
} }
...@@ -810,10 +849,10 @@ void PNS_ROUTER::SwitchLayer( int layer ) ...@@ -810,10 +849,10 @@ void PNS_ROUTER::SwitchLayer( int layer )
if( m_startsOnVia ) if( m_startsOnVia )
{ {
m_currentLayer = layer; m_currentLayer = layer;
m_placer->StartPlacement( m_currentStart, m_currentNet, m_currentWidth, //m_placer->StartPlacement( m_currentStart, m_currentNet, m_currentWidth,
m_currentLayer ); // m_currentLayer );
} }
break;
default: default:
break; break;
} }
...@@ -825,6 +864,45 @@ void PNS_ROUTER::ToggleViaPlacement() ...@@ -825,6 +864,45 @@ void PNS_ROUTER::ToggleViaPlacement()
if( m_state == ROUTE_TRACK ) if( m_state == ROUTE_TRACK )
{ {
m_placingVia = !m_placingVia; m_placingVia = !m_placingVia;
m_placer->AddVia( m_placingVia, m_currentViaDiameter, m_currentViaDrill ); m_placer->AddVia( m_placingVia, m_settings.GetViaDiameter(), m_settings.GetViaDrill() );
} }
} }
int PNS_ROUTER::GetCurrentNet() const
{
switch(m_state)
{
case ROUTE_TRACK:
return m_placer->CurrentNet();
default:
return m_currentNet;
}
}
int PNS_ROUTER::GetCurrentLayer() const
{
switch(m_state)
{
case ROUTE_TRACK:
return m_placer->CurrentLayer();
default:
return m_currentLayer;
}
}
void PNS_ROUTER::DumpLog()
{
PNS_LOGGER *logger = NULL;
switch(m_state)
{
case DRAG_SEGMENT:
logger = m_dragger->Logger();
break;
default:
break;
}
if(logger)
logger->Save ( "/tmp/shove.log" );
}
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_ROUTER_H #ifndef __PNS_ROUTER_H
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "pns_routing_settings.h" #include "pns_routing_settings.h"
#include "pns_item.h" #include "pns_item.h"
#include "pns_itemset.h" #include "pns_itemset.h"
#include "pns_node.h"
class BOARD; class BOARD;
class BOARD_ITEM; class BOARD_ITEM;
...@@ -47,7 +48,8 @@ class PNS_SEGMENT; ...@@ -47,7 +48,8 @@ class PNS_SEGMENT;
class PNS_JOINT; class PNS_JOINT;
class PNS_VIA; class PNS_VIA;
class PNS_CLEARANCE_FUNC; class PNS_CLEARANCE_FUNC;
class VIEW_GROUP; class PNS_SHOVE;
class PNS_DRAGGER;
namespace KIGFX { namespace KIGFX {
class VIEW; class VIEW;
...@@ -67,9 +69,8 @@ private: ...@@ -67,9 +69,8 @@ private:
enum RouterState enum RouterState
{ {
IDLE, IDLE,
START_ROUTING, DRAG_SEGMENT,
ROUTE_TRACK, ROUTE_TRACK
FINISH_TRACK
}; };
public: public:
...@@ -85,13 +86,13 @@ public: ...@@ -85,13 +86,13 @@ public:
void SetView( KIGFX::VIEW* aView ); void SetView( KIGFX::VIEW* aView );
bool RoutingInProgress() const; bool RoutingInProgress() const;
void StartRouting( const VECTOR2I& aP, PNS_ITEM* aItem ); bool StartRouting( const VECTOR2I& aP, PNS_ITEM* aItem );
void Move( const VECTOR2I& aP, PNS_ITEM* aItem ); void Move( const VECTOR2I& aP, PNS_ITEM* aItem );
bool FixRoute( const VECTOR2I& aP, PNS_ITEM* aItem ); bool FixRoute( const VECTOR2I& aP, PNS_ITEM* aItem );
void StopRouting(); void StopRouting();
const VECTOR2I GetCurrentEnd() const; const VECTOR2I CurrentEnd() const;
int GetClearance( const PNS_ITEM* a, const PNS_ITEM* b ) const; int GetClearance( const PNS_ITEM* a, const PNS_ITEM* b ) const;
...@@ -102,25 +103,22 @@ public: ...@@ -102,25 +103,22 @@ public:
void FlipPosture(); void FlipPosture();
void DisplayItem( const PNS_ITEM* aItem, bool aIsHead = false ); void DisplayItem( const PNS_ITEM* aItem, int aColor = -1, int aClearance = -1 );
void DisplayItems( const PNS_ITEMSET& aItems );
void DisplayDebugLine( const SHAPE_LINE_CHAIN& aLine, int aType = 0, int aWidth = 0 ); void DisplayDebugLine( const SHAPE_LINE_CHAIN& aLine, int aType = 0, int aWidth = 0 );
void DisplayDebugPoint( const VECTOR2I aPos, int aType = 0);
void DisplayDebugBox( const BOX2I& aBox, int aType = 0, int aWidth = 0 ); void DisplayDebugBox( const BOX2I& aBox, int aType = 0, int aWidth = 0 );
void EraseView();
void SwitchLayer( int layer ); void SwitchLayer( int layer );
int GetCurrentLayer() const { return m_currentLayer; }
void ToggleViaPlacement(); void ToggleViaPlacement();
void SetCurrentWidth( int w ); int GetCurrentLayer() const;// { return m_currentLayer; }
int GetCurrentNet() const;// { return m_currentNet; }
void SetCurrentViaDiameter( int d ) { m_currentViaDiameter = d; }
void SetCurrentViaDrill( int d ) { m_currentViaDrill = d; }
int GetCurrentWidth() const { return m_currentWidth; }
int GetCurrentViaDiameter() const { return m_currentViaDiameter; }
int GetCurrentViaDrill() const { return m_currentViaDrill; }
int GetCurrentNet() const { return m_currentNet; }
void DumpLog();
PNS_CLEARANCE_FUNC* GetClearanceFunc() const PNS_CLEARANCE_FUNC* GetClearanceFunc() const
{ {
return m_clearanceFunc; return m_clearanceFunc;
...@@ -138,11 +136,24 @@ public: ...@@ -138,11 +136,24 @@ public:
const PNS_ITEMSET QueryHoverItems( const VECTOR2I& aP ); const PNS_ITEMSET QueryHoverItems( const VECTOR2I& aP );
const VECTOR2I SnapToItem( PNS_ITEM* item, VECTOR2I aP, bool& aSplitsSegment ); const VECTOR2I SnapToItem( PNS_ITEM* item, VECTOR2I aP, bool& aSplitsSegment );
bool StartDragging( const VECTOR2I& aP, PNS_ITEM* aItem );
void SetIterLimit( int aX ) { m_iterLimit = aX; }
int GetIterLimit() const { return m_iterLimit; };
void SetShowIntermediateSteps(bool aX, int aSnapshotIter = -1 ) { m_showInterSteps = aX; m_snapshotIter = aSnapshotIter; }
bool GetShowIntermediateSteps() const { return m_showInterSteps; }
int GetShapshotIter() const { return m_snapshotIter; }
PNS_ROUTING_SETTINGS& Settings() { return m_settings; }
void CommitRouting( PNS_NODE* aNode );
/** /**
* Returns the last changes introduced by the router (since the last time ClearLastChanges() * Returns the last changes introduced by the router (since the last time ClearLastChanges()
* was called or a new track has been started). * was called or a new track has been started).
*/ */
const PICKED_ITEMS_LIST& GetLastChanges() const const PICKED_ITEMS_LIST& GetUndoBuffer() const
{ {
return m_undoBuffer; return m_undoBuffer;
} }
...@@ -150,21 +161,41 @@ public: ...@@ -150,21 +161,41 @@ public:
/** /**
* Clears the list of recent changes, saved to be stored in the undo buffer. * Clears the list of recent changes, saved to be stored in the undo buffer.
*/ */
void ClearLastChanges() void ClearUndoBuffer()
{ {
m_undoBuffer.ClearItemsList(); m_undoBuffer.ClearItemsList();
} }
/**
* Applies stored settings.
* \see Settings()
*/
void ApplySettings();
void EnableSnapping ( bool aEnable )
{
m_snappingEnabled = aEnable;
}
bool SnappingEnabled () const
{
return m_snappingEnabled;
}
private: private:
void movePlacing ( const VECTOR2I& aP, PNS_ITEM* aItem );
void moveDragging ( const VECTOR2I& aP, PNS_ITEM* aItem );
void eraseView();
void updateView( PNS_NODE *aNode, PNS_ITEMSET &aCurrent ); //PNS_LINE *aCurrent = NULL );
void clearViewFlags(); void clearViewFlags();
// optHoverItem queryHoverItemEx(const VECTOR2I& aP); // optHoverItem queryHoverItemEx(const VECTOR2I& aP);
PNS_ITEM* pickSingleItem( PNS_ITEMSET& aItems ) const; // std::vector<PNS_ITEM*> aItems) const; PNS_ITEM* pickSingleItem( PNS_ITEMSET& aItems ) const; // std::vector<PNS_ITEM*> aItems) const;
void splitAdjacentSegments( PNS_NODE* aNode, PNS_ITEM* aSeg, const VECTOR2I& aP ); // optHoverItem& aItem); void splitAdjacentSegments( PNS_NODE* aNode, PNS_ITEM* aSeg, const VECTOR2I& aP ); // optHoverItem& aItem);
void commitRouting( PNS_NODE* aNode );
PNS_NODE* removeLoops( PNS_NODE* aNode, PNS_SEGMENT* aLatestSeg );
PNS_NODE* removeLoops( PNS_NODE* aNode, PNS_LINE* aNewLine );
PNS_VIA* checkLoneVia( PNS_JOINT* aJoint ) const; PNS_VIA* checkLoneVia( PNS_JOINT* aJoint ) const;
PNS_ITEM* syncPad( D_PAD* aPad ); PNS_ITEM* syncPad( D_PAD* aPad );
...@@ -177,35 +208,45 @@ private: ...@@ -177,35 +208,45 @@ private:
void highlightCurrent( bool enabled ); void highlightCurrent( bool enabled );
void markViolations( PNS_NODE *aNode, PNS_ITEMSET& aCurrent, PNS_NODE::ItemVector& aRemoved );
int m_currentLayer; int m_currentLayer;
int m_currentNet; int m_currentNet;
int m_currentWidth;
int m_currentViaDiameter;
int m_currentViaDrill;
bool m_start_diagonal;
RouterState m_state; RouterState m_state;
BOARD* m_board; BOARD* m_board;
PNS_NODE* m_world; PNS_NODE* m_world;
PNS_NODE* m_lastNode;
PNS_LINE_PLACER* m_placer; PNS_LINE_PLACER* m_placer;
PNS_DRAGGER *m_dragger;
PNS_LINE* m_draggedLine;
PNS_SHOVE* m_shove;
int m_draggedSegmentIndex;
int m_iterLimit;
bool m_showInterSteps;
int m_snapshotIter;
KIGFX::VIEW* m_view; KIGFX::VIEW* m_view;
KIGFX::VIEW_GROUP* m_previewItems; KIGFX::VIEW_GROUP* m_previewItems;
PNS_ITEM *m_currentEndItem;
VECTOR2I m_currentEnd; VECTOR2I m_currentEnd;
VECTOR2I m_currentStart; VECTOR2I m_currentStart;
VECTOR2I m_originalStart; VECTOR2I m_originalStart;
bool m_placingVia; bool m_placingVia;
bool m_startsOnVia; bool m_startsOnVia;
bool m_snappingEnabled;
bool m_violation;
// optHoverItem m_startItem, m_endItem; // optHoverItem m_startItem, m_endItem;
PNS_ROUTING_SETTINGS m_settings; PNS_ROUTING_SETTINGS m_settings;
PNS_CLEARANCE_FUNC* m_clearanceFunc; PNS_CLEARANCE_FUNC* m_clearanceFunc;
boost::unordered_set<BOARD_ITEM*> m_hiddenItems; boost::unordered_set<BOARD_CONNECTED_ITEM*> m_hiddenItems;
///> 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;
......
/*
* 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 "pns_routing_settings.h"
PNS_ROUTING_SETTINGS::PNS_ROUTING_SETTINGS()
{
m_routingMode = RM_Walkaround;
m_optimizerEffort = OE_Full;
m_removeLoops = true;
m_smartPads = true;
m_shoveVias = true;
m_suggestFinish = false;
m_followMouse = true;
m_startDiagonal = false;
m_shoveIterationLimit = 250;
m_shoveTimeLimit = 1000;
m_walkaroundIterationLimit = 40;
m_jumpOverObstacles = false;
m_smoothDraggedSegments = true;
m_canViolateDRC = false;
}
TIME_LIMIT PNS_ROUTING_SETTINGS::ShoveTimeLimit() const
{
return TIME_LIMIT ( m_shoveTimeLimit );
}
int PNS_ROUTING_SETTINGS::ShoveIterationLimit() const
{
return m_shoveIterationLimit;
}
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,39 +15,147 @@ ...@@ -15,39 +15,147 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_ROUTER_SETTINGS #ifndef __PNS_ROUTING_SETTINGS
#define __PNS_ROUTER_SETTINGS #define __PNS_ROUTING_SETTINGS
#include "direction.h"
#include "time_limit.h"
///> Routing modes ///> Routing modes
enum PNS_MODE enum PNS_MODE
{ {
RM_Ignore = 0, ///> Ignore collisions RM_MarkObstacles = 0, ///> Ignore collisions, mark obstacles
RM_Shove, ///> Only shove RM_Shove, ///> Only shove
RM_Walkaround, ///> Only walkaround RM_Walkaround, ///> Only walkaround
RM_Smart ///> Guess what's better RM_Smart ///> Guess what's better, try to make least mess on the PCB
}; };
///> Optimization effort
enum PNS_OPTIMIZATION_EFFORT
{
OE_Low = 0,
OE_Medium = 1,
OE_Full = 2
};
/**
* Class PNS_ROUTING_SETTINGS
*
* Contains all persistent settings of the router, such as the mode, optimization effort, etc.
*/
class PNS_ROUTING_SETTINGS class PNS_ROUTING_SETTINGS
{ {
public: public:
PNS_MODE m_routingMode; PNS_ROUTING_SETTINGS();
///> Returns the routing mode.
PNS_MODE Mode() const { return m_routingMode; }
///> Sets the routing mode.
void SetMode( PNS_MODE aMode ) { m_routingMode = aMode; }
///> Returns the optimizer effort. Bigger means cleaner traces, but slower routing.
PNS_OPTIMIZATION_EFFORT OptimizerEffort() const { return m_optimizerEffort; }
///> Sets the optimizer effort. Bigger means cleaner traces, but slower routing.
void SetOptimizerEffort( PNS_OPTIMIZATION_EFFORT aEffort ) { m_optimizerEffort = aEffort; }
///> Returns true if shoving vias is enbled.
bool ShoveVias() const { return m_shoveVias; }
///> Enables/disables shoving vias.
void SetShoveVias( bool aShoveVias ) { m_shoveVias = aShoveVias; }
///> Returns true if loop (redundant track) removal is on.
bool RemoveLoops() const { return m_removeLoops; }
///> Enables/disables loop (redundant track) removal.
void SetRemoveLoops( bool aRemoveLoops ) { m_removeLoops = aRemoveLoops; }
///> Returns true if suggesting the finish of currently placed track is on.
bool SuggestFinish() { return m_suggestFinish; }
///> Enables displaying suggestions for finishing the currently placed track.
void SetSuggestFinish( bool aSuggestFinish ) { m_suggestFinish = aSuggestFinish; }
///> Returns true if Smart Pads (automatic neckdown) is enabled.
bool SmartPads () const { return m_smartPads; }
///> Enables/disables Smart Pads (automatic neckdown).
void SetSmartPads( bool aSmartPads ) { m_smartPads = aSmartPads; }
///> Returns true if follow mouse mode is active (permanently on for the moment).
bool FollowMouse() const
{
return m_followMouse && !(Mode() == RM_MarkObstacles);
}
///> Returns true if smoothing segments durign dragging is enabled.
bool SmoothDraggedSegments() const { return m_smoothDraggedSegments; }
///> Enables/disabled smoothing segments during dragging.
void SetSmoothDraggedSegments( bool aSmooth ) { m_smoothDraggedSegments = aSmooth; }
///> Returns true if jumping over unmovable obstacles is on.
bool JumpOverObstacles() const { return m_jumpOverObstacles; }
///> Enables/disables jumping over unmovable obstacles.
void SetJumpOverObstacles( bool aJumpOverObstacles ) { m_jumpOverObstacles = aJumpOverObstacles; }
void SetStartDiagonal(bool aStartDiagonal) { m_startDiagonal = aStartDiagonal; }
bool CanViolateDRC() const { return m_canViolateDRC; }
void SetCanViolateDRC( bool aViolate ) { m_canViolateDRC = aViolate; }
void SetTrackWidth( int aWidth ) { m_trackWidth = aWidth; }
int GetTrackWidth() const { return m_trackWidth; }
void SetViaDiameter( int aDiameter ) { m_viaDiameter = aDiameter; }
int GetViaDiameter() const { return m_viaDiameter; }
void SetViaDrill( int aDrill ) { m_viaDrill = aDrill; }
int GetViaDrill() const { return m_viaDrill; }
const DIRECTION_45 InitialDirection() const
{
if(m_startDiagonal)
return DIRECTION_45 (DIRECTION_45::NE);
else
return DIRECTION_45 (DIRECTION_45::N);
}
int ShoveIterationLimit() const;
TIME_LIMIT ShoveTimeLimit() const;
int WalkaroundIterationLimit() const { return m_walkaroundIterationLimit; };
TIME_LIMIT WalkaroundTimeLimit() const;
private:
bool m_shoveVias;
bool m_startDiagonal;
bool m_removeLoops; bool m_removeLoops;
bool m_smartPads; bool m_smartPads;
bool m_suggestEnding; bool m_suggestFinish;
bool m_shoveOnRequest;
bool m_changePostures;
bool m_followMouse; bool m_followMouse;
bool m_jumpOverObstacles;
bool m_smoothDraggedSegments;
bool m_canViolateDRC;
int m_lineWidth; PNS_MODE m_routingMode;
PNS_OPTIMIZATION_EFFORT m_optimizerEffort;
int m_trackWidth;
int m_viaDiameter; int m_viaDiameter;
int m_viaDrill; int m_viaDrill;
int m_preferredLayer; int m_preferredLayer;
int m_walkaroundIterationLimit; int m_walkaroundIterationLimit;
int m_shoveIterationLimit; int m_shoveIterationLimit;
TIME_LIMIT m_shoveTimeLimit;
TIME_LIMIT m_walkaroundTimeLimit;
}; };
#endif #endif
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_SEGMENT_H #ifndef __PNS_SEGMENT_H
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
#include <math/vector2d.h> #include <math/vector2d.h>
#include <geometry/seg.h> #include <geometry/seg.h>
#include <geometry/shape.h> #include <geometry/shape_segment.h>
#include <geometry/shape_line_chain.h> #include <geometry/shape_line_chain.h>
#include "pns_item.h" #include "pns_item.h"
...@@ -40,31 +40,27 @@ public: ...@@ -40,31 +40,27 @@ public:
{}; {};
PNS_SEGMENT( const SEG& aSeg, int aNet ) : PNS_SEGMENT( const SEG& aSeg, int aNet ) :
PNS_ITEM( SEGMENT ) PNS_ITEM( SEGMENT ), m_seg(aSeg, 0)
{ {
m_net = aNet; m_net = aNet;
m_shape.Clear();
m_shape.Append( aSeg.A );
m_shape.Append( aSeg.B );
}; };
PNS_SEGMENT( const PNS_LINE& aParentLine, const SEG& aSeg ) : PNS_SEGMENT( const PNS_LINE& aParentLine, const SEG& aSeg ) :
PNS_ITEM( SEGMENT ) PNS_ITEM( SEGMENT ),
m_seg(aSeg, aParentLine.Width())
{ {
m_net = aParentLine.GetNet(); m_net = aParentLine.Net();
m_layers = aParentLine.GetLayers(); m_layers = aParentLine.Layers();
m_width = aParentLine.GetWidth(); m_marker = aParentLine.Marker();
m_shape.Clear(); m_rank = aParentLine.Rank();
m_shape.Append( aSeg.A );
m_shape.Append( aSeg.B );
}; };
PNS_SEGMENT* Clone() const; PNS_SEGMENT* Clone( ) const;
const SHAPE* GetShape() const const SHAPE* Shape() const
{ {
return static_cast<const SHAPE*>( &m_shape ); return static_cast<const SHAPE*>( &m_seg );
} }
void SetLayer( int aLayer ) void SetLayer( int aLayer )
...@@ -72,53 +68,59 @@ public: ...@@ -72,53 +68,59 @@ public:
SetLayers( PNS_LAYERSET( aLayer ) ); SetLayers( PNS_LAYERSET( aLayer ) );
} }
int GetLayer() const int Layer() const
{ {
return GetLayers().Start(); return Layers().Start();
} }
const SHAPE_LINE_CHAIN& GetCLine() const void SetWidth( int aWidth )
{ {
return m_shape; m_seg.SetWidth(aWidth);
} }
void SetWidth( int aWidth ) int Width() const
{ {
m_width = aWidth; return m_seg.GetWidth();
} }
int GetWidth() const const SEG& Seg() const
{ {
return m_width; return m_seg.GetSeg();
} }
const SEG GetSeg() const const SHAPE_LINE_CHAIN CLine() const
{ {
assert( m_shape.PointCount() >= 1 ); return SHAPE_LINE_CHAIN( m_seg.GetSeg().A, m_seg.GetSeg().B );
if( m_shape.PointCount() == 1 )
return SEG( m_shape.CPoint( 0 ), m_shape.CPoint( 0 ) );
return SEG( m_shape.CPoint( 0 ), m_shape.CPoint( 1 ) );
} }
void SetEnds( const VECTOR2I& a, const VECTOR2I& b ) void SetEnds( const VECTOR2I& a, const VECTOR2I& b )
{ {
m_shape.Clear(); m_seg.SetSeg( SEG ( a, b ) );
m_shape.Append( a ); }
m_shape.Append( b );
}
void SwapEnds() void SwapEnds()
{ {
m_shape = m_shape.Reverse(); SEG tmp = m_seg.GetSeg();
m_seg.SetSeg( SEG (tmp.B , tmp.A ));
} }
const SHAPE_LINE_CHAIN Hull( int aClearance, int aWalkaroundThickness ) const; const SHAPE_LINE_CHAIN Hull( int aClearance, int aWalkaroundThickness ) const;
virtual VECTOR2I Anchor(int n) const
{
if(n == 0)
return m_seg.GetSeg().A;
else
return m_seg.GetSeg().B;
}
virtual int AnchorCount() const
{
return 2;
}
private: private:
SHAPE_LINE_CHAIN m_shape; SHAPE_SEGMENT m_seg;
int m_width;
}; };
#endif #endif
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,477 +15,1067 @@ ...@@ -15,477 +15,1067 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <deque> #include <deque>
#include <cassert> #include <cassert>
#include <wx/timer.h> #include <boost/foreach.hpp>
#include "trace.h" #include "trace.h"
#include "range.h"
#include "pns_line.h" #include "pns_line.h"
#include "pns_node.h" #include "pns_node.h"
#include "pns_walkaround.h" #include "pns_walkaround.h"
#include "pns_shove.h" #include "pns_shove.h"
#include "pns_solid.h"
#include "pns_optimizer.h" #include "pns_optimizer.h"
#include "pns_via.h" #include "pns_via.h"
#include "pns_utils.h" #include "pns_utils.h"
#include "pns_router.h"
#include "pns_shove.h"
#include "time_limit.h"
#include <profile.h> #include <profile.h>
PNS_SHOVE::PNS_SHOVE( PNS_NODE* aWorld ) static void sanityCheck (PNS_LINE *l_old, PNS_LINE *l_new)
{
assert (l_old->CPoint(0) == l_new->CPoint(0) );
assert (l_old->CPoint(-1) == l_new->CPoint(-1 ));
}
PNS_SHOVE::PNS_SHOVE( PNS_NODE* aWorld, PNS_ROUTER *aRouter ) :
PNS_ALGO_BASE ( aRouter )
{ {
m_root = aWorld; m_root = aWorld;
m_iterLimit = 100;
}; };
PNS_SHOVE::~PNS_SHOVE() PNS_SHOVE::~PNS_SHOVE()
{ {
// free all the stuff we've created during routing/dragging operation.
BOOST_FOREACH(PNS_ITEM *item, m_gcItems)
delete item;
} }
// garbage-collected line assembling
struct range PNS_LINE* PNS_SHOVE::assembleLine ( const PNS_SEGMENT *aSeg, int *aIndex )
{ {
range() PNS_LINE *l = m_currentNode->AssembleLine( const_cast<PNS_SEGMENT* > (aSeg), aIndex);
{
min_v = max_v = -1;
}
void add( int x ) m_gcItems.push_back(l);
{ return l;
if( min_v < 0 ) min_v = x; }
if( max_v < 0 ) max_v = x; // garbage-collected line cloning
PNS_LINE *PNS_SHOVE::cloneLine ( const PNS_LINE *aLine )
{
PNS_LINE *l = aLine->Clone();
if( x < min_v ) m_gcItems.push_back(l);
min_v = x; return l;
else if( x > max_v ) }
max_v = x;
}
int start() // 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
return min_v; // mathematical concept of orientation of an open curve, so we use some primitive heuristics:
} // if the shoved line wraps around the start of the "pusher", it's likely shoved in wrong direction.
bool PNS_SHOVE::checkBumpDirection ( PNS_LINE *aCurrent, PNS_LINE *aShoved ) const
{
const SEG ss = aCurrent->CSegment(0);
int end() int dist = m_currentNode->GetClearance(aCurrent, aShoved) + PNS_HULL_MARGIN;
{
return max_v;
}
int min_v, max_v; dist += aCurrent->Width() / 2;
}; dist += aShoved->Width() / 2;
const VECTOR2I ps = ss.A - (ss.B - ss.A).Resize(dist);
return !aShoved->CLine().PointOnEdge(ps);
}
// fixme: this is damn f***ing inefficient. And fails much too often due to broken direction finding algorithm. PNS_SHOVE::ShoveStatus PNS_SHOVE::walkaroundLoneVia ( PNS_LINE *aCurrent, PNS_LINE *aObstacle, PNS_LINE *aShoved )
bool PNS_SHOVE::tryShove( PNS_NODE* aNode, PNS_LINE* aHead, PNS_LINE* aObstacle,
PNS_SEGMENT& aObstacleSeg, PNS_LINE* aResult, bool aInvertWinding )
{ {
const SHAPE_LINE_CHAIN& head = aHead->GetCLine(); int clearance = m_currentNode->GetClearance( aCurrent, aObstacle );
bool cw = false; const SHAPE_LINE_CHAIN hull = aCurrent->Via().Hull( clearance, aObstacle->Width() );
int i; SHAPE_LINE_CHAIN path_cw, path_ccw;
if( aHead->EndsWithVia() && !aHead->GetLayers().Overlaps( aObstacle->GetLayers() ) ) aObstacle->Walkaround( hull, path_cw, true );
{ aObstacle->Walkaround( hull, path_ccw, false );
int clearance = aNode->GetClearance( aHead, aObstacle );
SHAPE_LINE_CHAIN hull = aHead->GetVia().Hull( clearance - aObstacle->GetWidth() / 2 ); const SHAPE_LINE_CHAIN& shortest = path_ccw.Length() < path_cw.Length() ? path_ccw : path_cw;
if(shortest.PointCount() < 2)
return SH_INCOMPLETE;
if(aObstacle->CPoint(-1) != shortest.CPoint(-1))
return SH_INCOMPLETE;
if(aObstacle->CPoint(0) != shortest.CPoint(0))
return SH_INCOMPLETE;
aShoved->SetShape( shortest );
// SHAPE_LINE_CHAIN path_pre, path_walk_cw, path_walk_ccw, path_post; if( m_currentNode->CheckColliding( aShoved, aCurrent ) )
return SH_INCOMPLETE;
SHAPE_LINE_CHAIN path_cw, path_ccw, * path; return SH_OK;
}
PNS_SHOVE::ShoveStatus PNS_SHOVE::processHullSet ( PNS_LINE *aCurrent, PNS_LINE *aObstacle, PNS_LINE *aShoved, const HullSet& hulls )
{
const SHAPE_LINE_CHAIN& obs = aObstacle->CLine();
bool failingDirCheck = false;
int attempt;
aObstacle->NewWalkaround( hull, path_cw, true ); for(attempt = 0; attempt < 4; attempt++)
aObstacle->NewWalkaround( hull, path_ccw, false ); {
bool invertTraversal = (attempt >= 2);
bool clockwise = attempt % 2;
int vFirst = -1, vLast = -1;
path = path_ccw.Length() < path_cw.Length() ? &path_ccw : &path_cw; SHAPE_LINE_CHAIN path;
aResult->SetShape( *path ); PNS_LINE l ( *aObstacle );
// PNSDisplayDebugLine (*path, 5); for(int i = 0; i < (int)hulls.size(); i++ )
{
const SHAPE_LINE_CHAIN& hull = hulls[invertTraversal ? hulls.size() - 1 - i : i];
if( !aResult->Is45Degree() ) l.Walkaround( hull, path, clockwise );
path.Simplify();
l.SetShape( path );
}
for(int i = 0; i < std::min ( path.PointCount(), obs.PointCount() ); i++)
{ {
// printf("polyset non-45\npoly %s\nendpolyset\n", aResult->GetCLine().Format().c_str()); if(path.CPoint(i) != obs.CPoint(i))
{
vFirst = i;
break;
}
} }
/*... special case for vias? */ int k = obs.PointCount() - 1;
for(int i = path.PointCount() - 1; i >= 0 && k >= 0; i--, k--)
{
if(path.CPoint(i) != obs.CPoint(k))
{
vLast = i;
break;
}
}
return !aNode->CheckColliding( aResult, aHead ); if( ( vFirst < 0 || vLast < 0) && !path.CompareGeometry( aObstacle->CLine() ))
} {
TRACE( 100, "attempt %d fail vfirst-last", attempt );
continue;
}
int ns = head.SegmentCount(); if(path.CPoint(-1) != obs.CPoint(-1) || path.CPoint(0) != obs.CPoint(0))
{
TRACE(100, "attempt %d fail vend-start\n", attempt);
continue;
}
if( aHead->EndsWithVia() ) if(!checkBumpDirection(aCurrent, &l))
ns++; {
TRACE( 100, "attempt %d fail direction-check", attempt );
failingDirCheck = true;
aShoved->SetShape(l.CLine());
continue;
}
for( i = 0; i < head.SegmentCount(); i++ ) if(path.SelfIntersecting())
{ {
const PNS_SEGMENT hs( *aHead, head.CSegment( i ) ); TRACE( 100, "attempt %d fail self-intersect", attempt );
continue;
}
bool colliding = m_currentNode->CheckColliding( &l, aCurrent );
if( (aCurrent->Marker() & MK_HEAD) && !colliding)
{
PNS_JOINT *jtStart = m_currentNode->FindJoint ( aCurrent->CPoint(0), aCurrent );
BOOST_FOREACH( PNS_ITEM *item, jtStart->LinkList() )
{
if(m_currentNode->CheckColliding(item, &l))
colliding = true;
}
}
if( colliding )
{
TRACE( 100, "attempt %d fail coll-check", attempt );
continue;
}
aShoved->SetShape( l.CLine() );
return SH_OK;
}
if( aNode->CheckColliding( &hs, aObstacle ) ) return failingDirCheck ? SH_OK : SH_INCOMPLETE;
{ }
VECTOR2I v1 = hs.GetSeg().B - hs.GetSeg().A;
VECTOR2I v2 = aObstacleSeg.GetSeg().B - aObstacleSeg.GetSeg().A;
VECTOR2I::extended_type det = v1.Cross( v2 ); PNS_SHOVE::ShoveStatus PNS_SHOVE::processSingleLine ( PNS_LINE *aCurrent, PNS_LINE *aObstacle, PNS_LINE *aShoved )
{
aShoved->ClearSegmentLinks();
if( det > 0 ) bool obstacleIsHead = false;
cw = true;
else
cw = false;
if( aObstacle->LinkedSegments() )
{
BOOST_FOREACH( PNS_SEGMENT *s, *aObstacle->LinkedSegments() )
if(s->Marker() & MK_HEAD)
{
obstacleIsHead = true;
break; break;
} }
} }
if( aInvertWinding ) ShoveStatus rv;
{
if( cw )
cw = false;
else
cw = true;
}
PNS_LINE shoved( *aObstacle ); bool viaOnEnd = aCurrent->EndsWithVia();
int clearance = aNode->GetClearance( aHead, aObstacle ); if( viaOnEnd && ( !aCurrent->LayersOverlap( aObstacle ) || aCurrent->SegmentCount() == 0 ) )
{
rv = walkaroundLoneVia( aCurrent, aObstacle, aShoved );
} else {
int w = aObstacle->Width();
int n_segs = aCurrent->SegmentCount();
int clearance = m_currentNode->GetClearance( aCurrent, aObstacle );
range r; HullSet hulls;
for( i = 0; i < ns; i++ ) hulls.reserve( n_segs + 1 );
{
SHAPE_LINE_CHAIN hull;
if( i < head.SegmentCount() ) for( int i = 0; i < n_segs; i++ )
{ {
const PNS_SEGMENT hs( *aHead, head.CSegment( i ) ); PNS_SEGMENT seg ( *aCurrent, aCurrent->CSegment(i) );
hull = hs.Hull( clearance, 0 ); hulls.push_back ( seg.Hull( clearance, w ) );
} }
else
hull = aHead->GetVia().Hull( clearance - aObstacle->GetWidth() / 2 );
SHAPE_LINE_CHAIN path_pre, path_walk, path_post, tmp; if( viaOnEnd )
SHAPE_LINE_CHAIN path_pre2, path_walk2, path_post2; hulls.push_back ( aCurrent->Via().Hull( clearance, w ) );
// shoved.NewWalkaround(hull, path_pre, path_walk, path_post, cw); rv = processHullSet ( aCurrent, aObstacle, aShoved, hulls);
shoved.NewWalkaround( hull, path_pre, path_walk, path_post, cw ); }
if(obstacleIsHead)
aShoved->Mark( aShoved->Marker() | MK_HEAD );
/*if(path_pre != path_pre2 || path_post != path_post2 || path_walk != path_walk2 ) return rv;
* { }
* TRACE(5, "polyset orig\npoly %s\npoly %s\npoly %s\nendpolyset\n", path_pre.Format().c_str() % path_walk.Format().c_str() % path_post.Format().c_str());
* TRACE(5, "polyset err\npoly %s\npoly %s\npoly %s\nendpolyset\n", path_pre2.Format().c_str() % path_walk2.Format().c_str() % path_post2.Format().c_str());
* }*/
tmp = shoved.GetCLine();
if( path_walk.SegmentCount() ) PNS_SHOVE::ShoveStatus PNS_SHOVE::onCollidingSegment( PNS_LINE *aCurrent, PNS_SEGMENT *aObstacleSeg )
r.add( i ); {
int segIndex;
PNS_LINE *obstacleLine = assembleLine (aObstacleSeg, &segIndex);
PNS_LINE *shovedLine = cloneLine ( obstacleLine );
ShoveStatus rv = processSingleLine ( aCurrent, obstacleLine, shovedLine );
path_pre.Append( path_walk ); assert ( obstacleLine->LayersOverlap (shovedLine) );
path_pre.Append( path_post );
path_pre.Simplify();
shoved.SetShape( path_pre );
// shoved.SetAffectedRange ( start, end );
*aResult = shoved;
if( !aResult->Is45Degree() )
{ if(rv == SH_OK)
// TRACE(5, "polyset non-45\npoly %s\npoly %s\npoly %s\nendpolyset\n", tmp.Format().c_str() % hull.Format().c_str() % aResult->GetCLine().Format().c_str()); {
} if ( shovedLine->Marker() & MK_HEAD )
m_newHead = *shovedLine;
sanityCheck(obstacleLine, shovedLine);
m_currentNode->Replace (obstacleLine, shovedLine);
sanityCheck(obstacleLine, shovedLine);
int rank = aCurrent->Rank();
shovedLine->SetRank ( rank - 1 );
pushLine(shovedLine);
} }
TRACE( 2, "CW %d affectedRange %d-%d [total %d]", (cw ? 1 : 0) % r.start() % r.end() % ns ); #ifdef DEBUG
m_logger.NewGroup ("on-colliding-segment", m_iter);
m_logger.Log ( aObstacleSeg, 0, "obstacle-segment");
m_logger.Log ( aCurrent, 1, "current-line");
m_logger.Log ( obstacleLine, 2, "obstacle-line");
m_logger.Log ( shovedLine, 3, "shoved-line");
#endif
return !aNode->CheckColliding( aResult, aHead ); return rv;
} }
PNS_SHOVE::ShoveStatus PNS_SHOVE::onCollidingLine( PNS_LINE *aCurrent, PNS_LINE *aObstacle )
PNS_SHOVE::ShoveStatus PNS_SHOVE::shoveSingleLine( PNS_NODE* aNode, PNS_LINE* aCurrent,
PNS_LINE* aObstacle, PNS_SEGMENT& aObstacleSeg, PNS_LINE* aResult )
{ {
bool rv = tryShove( aNode, aCurrent, aObstacle, aObstacleSeg, aResult, false ); PNS_LINE *shovedLine = cloneLine(aObstacle);
if( !rv ) ShoveStatus rv = processSingleLine ( aCurrent, aObstacle, shovedLine );
rv = tryShove( aNode, aCurrent, aObstacle, aObstacleSeg, aResult, true );
if( !rv ) if(rv == SH_OK)
{ {
TRACEn( 2, "Shove failed" ); if ( shovedLine->Marker() & MK_HEAD )
return SH_INCOMPLETE; m_newHead = *shovedLine;
sanityCheck(aObstacle,shovedLine);
m_currentNode->Replace( aObstacle, shovedLine );
sanityCheck(aObstacle,shovedLine);
int rank = aObstacle->Rank();
shovedLine->SetRank ( rank );
pushLine(shovedLine);
#ifdef DEBUG
m_logger.NewGroup ("on-colliding-line", m_iter);
m_logger.Log ( aObstacle, 0, "obstacle-line");
m_logger.Log ( aCurrent, 1, "current-line");
m_logger.Log ( shovedLine, 3, "shoved-line");
#endif
} }
aResult->GetLine().Simplify();
return rv;
}
const SHAPE_LINE_CHAIN& sh_shoved = aResult->GetCLine(); PNS_SHOVE::ShoveStatus PNS_SHOVE::onCollidingSolid( PNS_LINE *aCurrent, PNS_SOLID *aObstacleSolid )
const SHAPE_LINE_CHAIN& sh_orig = aObstacle->GetCLine(); {
PNS_WALKAROUND walkaround( m_currentNode, Router() );
PNS_LINE* walkaroundLine = cloneLine(aCurrent);
if(aCurrent->EndsWithVia())
{
PNS_VIA vh = aCurrent->Via();
PNS_VIA *via = NULL;
PNS_JOINT *jtStart = m_currentNode->FindJoint ( vh.Pos(), aCurrent );
if( sh_shoved.SegmentCount() > 1 && sh_shoved.CPoint( 0 ) == sh_orig.CPoint( 0 ) if(!jtStart)
&& sh_shoved.CPoint( -1 ) == sh_orig.CPoint( -1 ) ) return SH_INCOMPLETE;
return SH_OK;
else if( !sh_shoved.SegmentCount() ) BOOST_FOREACH( PNS_ITEM *item, jtStart->LinkList() )
return SH_NULL; if(item->OfKind(PNS_ITEM::VIA))
else {
via = (PNS_VIA *) item;
break;
}
if( via && m_currentNode->CheckColliding(via, aObstacleSolid) )
return onCollidingVia ( aObstacleSolid, via );
}
walkaround.SetSolidsOnly( true );
walkaround.SetIterationLimit ( 8 ); // fixme: make configurable
int currentRank = aCurrent->Rank();
int nextRank;
if (!Settings().JumpOverObstacles() )
{
nextRank = currentRank + 10000;
walkaround.SetSingleDirection( false );
} else {
nextRank = currentRank - 1;
walkaround.SetSingleDirection( true );
}
if (walkaround.Route( *aCurrent, *walkaroundLine, false ) != PNS_WALKAROUND::DONE )
return SH_INCOMPLETE; return SH_INCOMPLETE;
walkaroundLine->ClearSegmentLinks();
walkaroundLine->Unmark();
walkaroundLine->Line().Simplify();
if(walkaroundLine->HasLoops())
return SH_INCOMPLETE;
if (aCurrent->Marker() & MK_HEAD)
{
walkaroundLine->Mark(MK_HEAD);
m_newHead = *walkaroundLine;
}
m_currentNode->Replace( aCurrent, walkaroundLine );
walkaroundLine->SetRank ( nextRank );
#ifdef DEBUG
m_logger.NewGroup ("on-colliding-solid", m_iter);
m_logger.Log ( aObstacleSolid, 0, "obstacle-solid");
m_logger.Log ( aCurrent, 1, "current-line");
m_logger.Log ( walkaroundLine, 3, "walk-line");
#endif
popLine();
pushLine(walkaroundLine);
return SH_OK;
} }
bool PNS_SHOVE::reduceSpringback( PNS_LINE* aHead ) bool PNS_SHOVE::reduceSpringback( const PNS_ITEMSET& aHeadSet )
{ {
bool rv = false; bool rv = false;
while( !m_nodeStack.empty() ) while( !m_nodeStack.empty() )
{ {
SpringbackTag st_stack = m_nodeStack.back(); SpringbackTag spTag = m_nodeStack.back();
bool tail_ok = true;
if( !st_stack.node->CheckColliding( aHead ) && tail_ok ) if( !spTag.node->CheckColliding( aHeadSet ) )
{ {
rv = true; rv = true;
delete st_stack.node;
delete spTag.node;
m_nodeStack.pop_back(); m_nodeStack.pop_back();
} }
else else
break; break;
} }
return rv; return rv;
} }
bool PNS_SHOVE::pushSpringback( PNS_NODE* aNode, PNS_LINE* aHead, const PNS_COST_ESTIMATOR& aCost )
bool PNS_SHOVE::pushSpringback( PNS_NODE* aNode, const PNS_ITEMSET& aHeadItems, const PNS_COST_ESTIMATOR& aCost )
{ {
BOX2I headBB = aHead->GetCLine().BBox();
SpringbackTag st; SpringbackTag st;
st.node = aNode; st.node = aNode;
st.cost = aCost; st.cost = aCost;
st.length = std::max( headBB.GetWidth(), headBB.GetHeight() );; st.headItems = aHeadItems;
m_nodeStack.push_back( st ); m_nodeStack.push_back( st );
return true; return true;
} }
const PNS_COST_ESTIMATOR PNS_SHOVE::TotalCost() const PNS_SHOVE::ShoveStatus PNS_SHOVE::pushVia ( PNS_VIA *aVia, const VECTOR2I& aForce, int aCurrentRank )
{ {
if( m_nodeStack.empty() )
return PNS_COST_ESTIMATOR(); LinePairVec draggedLines;
else VECTOR2I p0 ( aVia->Pos() );
return m_nodeStack.back().cost; PNS_JOINT *jt = m_currentNode->FindJoint( p0, 1, aVia->Net() );
} PNS_VIA *pushedVia = aVia -> Clone();
pushedVia->SetPos( p0 + aForce );
pushedVia->Mark( aVia->Marker() ) ;
PNS_SHOVE::ShoveStatus PNS_SHOVE::ShoveLines( PNS_LINE* aCurrentHead ) if(aVia->Marker() & MK_HEAD)
{ {
std::stack<PNS_LINE*> lineStack; m_draggedVia = pushedVia;
PNS_NODE* node, * parent; }
PNS_VIA* headVia = NULL;
bool fail = false;
int iter = 0;
PNS_LINE* head = aCurrentHead->Clone(); if(!jt)
{
TRACEn(1, "weird, can't find the center-of-via joint\n");
return SH_INCOMPLETE;
}
reduceSpringback( aCurrentHead ); BOOST_FOREACH(PNS_ITEM *item, jt->LinkList() )
{
if(item->OfKind( PNS_ITEM::SEGMENT ))
{
PNS_SEGMENT *seg = (PNS_SEGMENT *) item;
LinePair lp;
int segIndex;
lp.first = assembleLine(seg, &segIndex);
assert(segIndex == 0 || (segIndex == (lp.first->SegmentCount() - 1) ));
if(segIndex == 0)
lp.first->Reverse();
lp.second = cloneLine( lp.first );
lp.second->ClearSegmentLinks();
lp.second->DragCorner( p0 + aForce, lp.second->CLine().Find( p0 ));
lp.second->AppendVia ( *pushedVia );
draggedLines.push_back(lp);
}
}
parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().node; m_currentNode->Remove( aVia );
node = parent->Branch(); m_currentNode->Add ( pushedVia );
lineStack.push( head ); if(aVia->BelongsTo(m_currentNode))
delete aVia;
// node->Add(tail); pushedVia -> SetRank (aCurrentRank - 1);
node->Add( head );
if( head->EndsWithVia() ) #ifdef DEBUG
m_logger.Log ( aVia, 0, "obstacle-via");
m_logger.Log ( pushedVia, 1, "pushed-via");
#endif
BOOST_FOREACH( LinePair lp, draggedLines )
{ {
headVia = head->GetVia().Clone(); if(lp.first->Marker() & MK_HEAD)
node->Add( headVia ); {
lp.second->Mark ( MK_HEAD );
m_newHead = *lp.second;
}
unwindStack(lp.first);
if(lp.second->SegmentCount())
{
m_currentNode->Replace ( lp.first, lp.second );
lp.second->SetRank( aCurrentRank - 1);
pushLine(lp.second);
} else
m_currentNode->Remove(lp.first);
#ifdef DEBUG
m_logger.Log ( lp.first, 2, "fan-pre");
m_logger.Log ( lp.second, 3, "fan-post");
#endif
} }
PNS_OPTIMIZER optimizer( node ); return SH_OK;
}
optimizer.SetEffortLevel( PNS_OPTIMIZER::MERGE_SEGMENTS | PNS_OPTIMIZER::SMART_PADS );
optimizer.SetCollisionMask( -1 );
PNS_NODE::OptObstacle nearest;
optimizer.CacheStaticItem( head ); PNS_SHOVE::ShoveStatus PNS_SHOVE::onCollidingVia (PNS_ITEM *aCurrent, PNS_VIA *aObstacleVia )
{
int clearance = m_currentNode->GetClearance( aCurrent, aObstacleVia ) ;
LinePairVec draggedLines;
VECTOR2I p0 ( aObstacleVia->Pos() );
bool colLine = false, colVia = false;
PNS_LINE *currentLine = NULL;
VECTOR2I mtvLine, mtvVia, mtv, mtvSolid;
int rank = -1;
if( aCurrent->OfKind (PNS_ITEM::LINE))
{
if( headVia ) #ifdef DEBUG
optimizer.CacheStaticItem( headVia ); m_logger.NewGroup ("push-via-by-line", m_iter);
m_logger.Log(aCurrent, 4, "current");
#endif
TRACE( 1, "ShoveStart [root: %d jts, node: %d jts]", m_root->JointCount() % currentLine = (PNS_LINE*) aCurrent;
node->JointCount() ); colLine = CollideShapes( aObstacleVia->Shape(), currentLine->Shape(), clearance + currentLine->Width() / 2 + PNS_HULL_MARGIN, true, mtvLine );
// PNS_ITEM *lastWalkSolid = NULL; if(currentLine->EndsWithVia())
prof_counter totalRealTime; colVia = CollideShapes (currentLine->Via().Shape(), aObstacleVia->Shape(), clearance + PNS_HULL_MARGIN, true, mtvVia);
wxLongLong t_start = wxGetLocalTimeMillis(); if(!colLine && !colVia)
return SH_OK;
while( !lineStack.empty() ) if(colLine && colVia)
mtv = mtvVia.EuclideanNorm() > mtvLine.EuclideanNorm() ? mtvVia : mtvLine;
else if (colLine)
mtv = mtvLine;
else
mtv = mtvVia;
rank = currentLine->Rank();
}
else if (aCurrent->OfKind(PNS_ITEM::SOLID))
{ {
wxLongLong t_cur = wxGetLocalTimeMillis(); CollideShapes( aObstacleVia->Shape(), aCurrent->Shape(), clearance + PNS_HULL_MARGIN, true, mtvSolid );
mtv = mtvSolid;
rank = aCurrent->Rank() + 10000;
}
return pushVia ( aObstacleVia, mtv, rank );
}
PNS_SHOVE::ShoveStatus PNS_SHOVE::onReverseCollidingVia (PNS_LINE *aCurrent, PNS_VIA *aObstacleVia )
{
std::vector<PNS_LINE *> steps;
int n = 0;
PNS_LINE *cur = cloneLine( aCurrent );
cur->ClearSegmentLinks();
PNS_JOINT *jt = m_currentNode->FindJoint ( aObstacleVia->Pos(), aObstacleVia );
PNS_LINE *shoved = cloneLine( aCurrent );
shoved->ClearSegmentLinks();
if( (t_cur - t_start).ToLong() > ShoveTimeLimit )
{
fail = true;
break;
}
iter++; cur->RemoveVia();
unwindStack(aCurrent);
if( iter > m_iterLimit ) BOOST_FOREACH( PNS_ITEM *item, jt->LinkList() )
{
if (item->OfKind(PNS_ITEM::SEGMENT) && item->LayersOverlap (aCurrent) )
{ {
fail = true; PNS_SEGMENT *seg = (PNS_SEGMENT *) item;
break; PNS_LINE *head = assembleLine( seg );
head->AppendVia( *aObstacleVia );
ShoveStatus st = processSingleLine ( head, cur, shoved );
if( st != SH_OK )
{
#ifdef DEBUG
m_logger.NewGroup ("on-reverse-via-fail-shove", m_iter);
m_logger.Log ( aObstacleVia, 0, "the-via");
m_logger.Log ( aCurrent, 1, "current-line");
m_logger.Log ( shoved, 3, "shoved-line");
#endif
return st;
}
cur->SetShape ( shoved->CLine() );
n++;
} }
}
if(!n)
{
#ifdef DEBUG
m_logger.NewGroup ("on-reverse-via-fail-lonevia", m_iter);
m_logger.Log ( aObstacleVia, 0, "the-via");
m_logger.Log ( aCurrent, 1, "current-line");
#endif
PNS_LINE head(*aCurrent);
head.Line().Clear();
head.AppendVia( *aObstacleVia );
head.ClearSegmentLinks();
ShoveStatus st = processSingleLine ( &head, aCurrent, shoved );
if( st != SH_OK )
return st;
cur->SetShape ( shoved->CLine() );
}
if(aCurrent->EndsWithVia())
shoved->AppendVia( aCurrent->Via( ));
#ifdef DEBUG
m_logger.NewGroup ("on-reverse-via", m_iter);
m_logger.Log ( aObstacleVia, 0, "the-via");
m_logger.Log ( aCurrent, 1, "current-line");
m_logger.Log ( shoved, 3, "shoved-line");
#endif
int currentRank = aCurrent->Rank();
m_currentNode->Replace ( aCurrent, shoved );
PNS_LINE* currentLine = lineStack.top(); pushLine(shoved);
shoved->SetRank( currentRank );
return SH_OK;
}
prof_start( &totalRealTime );
nearest = node->NearestObstacle( currentLine, PNS_ITEM::ANY );
prof_end( &totalRealTime );
TRACE( 2, "t-nearestObstacle %lld us", totalRealTime.usecs() ); void PNS_SHOVE::unwindStack ( PNS_SEGMENT *seg )
{
for (std::vector<PNS_LINE *>::iterator i = m_lineStack.begin(); i != m_lineStack.end(); )
{
if( (*i)->ContainsSegment ( seg ) )
i = m_lineStack.erase( i );
else
i++;
}
if( !nearest ) for (std::vector<PNS_LINE *>::iterator i = m_optimizerQueue.begin(); i != m_optimizerQueue.end(); )
{
if( (*i)->ContainsSegment ( seg ) )
i = m_optimizerQueue.erase( i );
else
i++;
}
}
void PNS_SHOVE::unwindStack ( PNS_ITEM *item )
{
if (item->OfKind(PNS_ITEM::SEGMENT))
unwindStack(static_cast<PNS_SEGMENT *>(item));
else if (item->OfKind(PNS_ITEM::LINE)) {
PNS_LINE *l = static_cast<PNS_LINE *>( item );
if (!l->LinkedSegments())
return;
BOOST_FOREACH(PNS_SEGMENT *seg, *l->LinkedSegments() )
unwindStack(seg);
}
}
void PNS_SHOVE::pushLine (PNS_LINE *l)
{
if(l->LinkCount() >= 0 && (l->LinkCount() != l->SegmentCount()))
assert(false);
m_lineStack.push_back(l);
m_optimizerQueue.push_back(l);
}
void PNS_SHOVE::popLine( )
{
PNS_LINE *l = m_lineStack.back();
for (std::vector<PNS_LINE *>::iterator i = m_optimizerQueue.begin(); i != m_optimizerQueue.end(); )
{
if( (*i) == l )
{ {
if( lineStack.size() > 1 ) i = m_optimizerQueue.erase( i );
{ } else
PNS_LINE* original = lineStack.top(); i++;
PNS_LINE optimized; }
int r_start, r_end;
original->GetAffectedRange( r_start, r_end ); m_lineStack.pop_back();
}
TRACE( 1, "Iter %d optimize-line [range %d-%d, total %d]", PNS_SHOVE::ShoveStatus PNS_SHOVE::shoveIteration(int aIter)
iter % r_start % r_end % original->GetCLine().PointCount() ); {
// lastWalkSolid = NULL; PNS_LINE* currentLine = m_lineStack.back();
prof_start( &totalRealTime ); PNS_NODE::OptObstacle nearest;
ShoveStatus st;
PNS_ITEM::PnsKind search_order[] = { PNS_ITEM::SOLID, PNS_ITEM::VIA, PNS_ITEM::SEGMENT };
for(int i = 0; i < 3; i++)
{
nearest = m_currentNode->NearestObstacle( currentLine, search_order[i] );
if(nearest)
break;
}
if( !nearest )
{
m_lineStack.pop_back();
return SH_OK;
}
if( optimizer.Optimize( original, &optimized ) ) PNS_ITEM *ni = nearest->item;
{
node->Remove( original );
optimizer.CacheRemove( original );
node->Add( &optimized );
if( original->BelongsTo( node ) ) unwindStack(ni);
delete original;
if( !ni->OfKind(PNS_ITEM::SOLID) && ni->Rank() >= 0 && ni->Rank() > currentLine->Rank() )
{
switch( ni->Kind() )
{
case PNS_ITEM::VIA:
{
PNS_VIA *revVia = (PNS_VIA *) ni;
TRACE( 2, "iter %d: reverse-collide-via", aIter );
if (currentLine->EndsWithVia() && m_currentNode->CheckColliding(&currentLine->Via(), revVia))
{
st = SH_INCOMPLETE;
} else {
st = onReverseCollidingVia ( currentLine, revVia );
} }
prof_end( &totalRealTime ); break;
}
case PNS_ITEM::SEGMENT:
{
PNS_SEGMENT *seg = (PNS_SEGMENT* ) ni;
TRACE( 2, "iter %d: reverse-collide-segment ", aIter );
PNS_LINE *revLine = assembleLine ( seg );
TRACE( 2, "t-optimizeObstacle %lld us", totalRealTime.usecs() ); popLine();
st = onCollidingLine( revLine, currentLine );
pushLine(revLine);
break;
} }
lineStack.pop(); default:
assert(false);
} }
else } else { // "forward" collisoins
switch( ni->Kind() )
{ {
switch( nearest->item->GetKind() )
{
case PNS_ITEM::SEGMENT: case PNS_ITEM::SEGMENT:
{ TRACE( 2, "iter %d: collide-segment ", aIter );
TRACE( 1, "Iter %d shove-line", iter ); st = onCollidingSegment( currentLine, (PNS_SEGMENT* ) ni );
break;
PNS_SEGMENT* pseg = static_cast<PNS_SEGMENT*>(nearest->item); case PNS_ITEM::VIA:
PNS_LINE* collidingLine = node->AssembleLine( pseg ); TRACE( 2, "iter %d: shove-via ", aIter );
PNS_LINE* shovedLine = collidingLine->CloneProperties(); st = onCollidingVia ( currentLine, (PNS_VIA *) ni );
break;
prof_start( &totalRealTime ); case PNS_ITEM::SOLID:
ShoveStatus st = shoveSingleLine( node, currentLine, collidingLine, TRACE( 2, "iter %d: walk-solid ", aIter );
*pseg, shovedLine ); st = onCollidingSolid ( currentLine, (PNS_SOLID *) ni );
prof_end( &totalRealTime ); break;
default:
break;
}
}
TRACE( 2, "t-shoveSingle %lld us", totalRealTime.usecs() ); return st;
}
if( st == SH_OK ) PNS_SHOVE::ShoveStatus PNS_SHOVE::shoveMainLoop()
{ {
node->Replace( collidingLine, shovedLine ); ShoveStatus st = SH_OK;
if( collidingLine->BelongsTo( node ) ) TRACE( 1, "ShoveStart [root: %d jts, current: %d jts]", m_root->JointCount() %
delete collidingLine; m_currentNode->JointCount() );
optimizer.CacheRemove( collidingLine ); int iterLimit = Settings().ShoveIterationLimit();
lineStack.push( shovedLine ); TIME_LIMIT timeLimit = Settings().ShoveTimeLimit();
}
else
fail = true;
// lastWalkSolid = NULL; m_iter = 0;
break; timeLimit.Restart();
} // case SEGMENT
case PNS_ITEM::SOLID: while( !m_lineStack.empty() )
case PNS_ITEM::VIA: {
{ st = shoveIteration(m_iter);
TRACE( 1, "Iter %d walkaround-solid [%p]", iter % nearest->item );
if( lineStack.size() == 1 ) m_iter++;
{
fail = true;
break;
}
/* if(lastWalkSolid == nearest->item) if( st == SH_INCOMPLETE || timeLimit.Expired() || m_iter >= iterLimit )
* { {
* fail = true; st = SH_INCOMPLETE;
* break; break;
* }*/ }
}
PNS_WALKAROUND walkaround( node ); return st;
PNS_LINE* walkaroundLine = currentLine->CloneProperties(); }
walkaround.SetSolidsOnly( true );
walkaround.SetSingleDirection( true );
prof_start( &totalRealTime ); PNS_SHOVE::ShoveStatus PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead )
walkaround.Route( *currentLine, *walkaroundLine, false ); {
prof_end( &totalRealTime ); ShoveStatus st = SH_OK;
TRACE( 2, "t-walkSolid %lld us", totalRealTime.usecs() ); // empty head? nothing to shove...
if( ! aCurrentHead.SegmentCount() )
return SH_INCOMPLETE;
PNS_LINE* head = cloneLine ( &aCurrentHead );
head->ClearSegmentLinks();
m_lineStack.clear();
m_optimizerQueue.clear();
m_newHead = OptLine();
m_logger.Clear();
node->Replace( currentLine, walkaroundLine ); PNS_ITEMSET headSet ( cloneLine( &aCurrentHead ) );
if( currentLine->BelongsTo( node ) ) reduceSpringback( headSet );
delete currentLine;
optimizer.CacheRemove( currentLine ); PNS_NODE *parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().node;
lineStack.top() = walkaroundLine;
// lastWalkSolid = nearest->item; m_currentNode = parent->Branch();
break; m_currentNode->ClearRanks();
} m_currentNode->Add( head );
head->Mark ( MK_HEAD );
head->SetRank ( 100000 );
default: m_logger.NewGroup ("initial", 0);
break; m_logger.Log ( head, 0, "head");
} // switch
PNS_VIA* headVia = NULL;
if( head->EndsWithVia() )
{
headVia = head->Via().Clone();
m_currentNode->Add( headVia );
headVia->Mark( MK_HEAD );
headVia->SetRank ( 100000 );
m_logger.Log ( headVia, 0, "head-via");
if( fail )
break;
}
} }
node->Remove( head ); pushLine (head);
delete head; st = shoveMainLoop();
runOptimizer ( m_currentNode, head );
if( headVia ) if( m_newHead && st == SH_OK )
{ {
node->Remove( headVia ); st = SH_HEAD_MODIFIED;
delete headVia; Router()->DisplayDebugLine ( m_newHead->CLine(), 3, 20000 );
}
m_currentNode->RemoveByMarker ( MK_HEAD );
TRACE( 1, "Shove status : %s after %d iterations", ((st == SH_OK || st == SH_HEAD_MODIFIED) ? "OK" : "FAILURE") % m_iter );
if( st == SH_OK || st == SH_HEAD_MODIFIED )
{
pushSpringback( m_currentNode, headSet, PNS_COST_ESTIMATOR() );
}
else
{
delete m_currentNode;
m_currentNode = parent;
m_newHead = OptLine();
} }
TRACE( 1, "Shove status : %s after %d iterations", (fail ? "FAILED" : "OK") % iter ); return st;
}
PNS_SHOVE::ShoveStatus PNS_SHOVE::ShoveDraggingVia ( PNS_VIA *aVia, const VECTOR2I& aWhere, PNS_VIA **aNewVia )
{
ShoveStatus st = SH_OK;
m_lineStack.clear();
m_optimizerQueue.clear();
m_newHead = OptLine();
m_draggedVia = NULL;
//reduceSpringback( aCurrentHead );
PNS_NODE *parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().node;
m_currentNode = parent->Branch();
m_currentNode->ClearRanks();
aVia->Mark( MK_HEAD );
st = pushVia ( aVia, (aWhere - aVia->Pos()), 0 );
st = shoveMainLoop();
runOptimizer ( m_currentNode, NULL );
if( !fail ) if( st == SH_OK || st == SH_HEAD_MODIFIED )
{ {
pushSpringback( node, aCurrentHead, PNS_COST_ESTIMATOR() ); pushSpringback( m_currentNode, PNS_ITEMSET(), PNS_COST_ESTIMATOR() );
return SH_OK;
} }
else else
{ {
delete node; delete m_currentNode;
return SH_INCOMPLETE; m_currentNode = parent;
}
if(aNewVia)
*aNewVia = m_draggedVia;
return st;
}
void PNS_SHOVE::runOptimizer ( PNS_NODE *node, PNS_LINE *head )
{
PNS_OPTIMIZER optimizer( node );
int optFlags = 0, n_passes = 0, extend = 0;
PNS_OPTIMIZATION_EFFORT effort = Settings().OptimizerEffort();
switch(effort)
{
case OE_Low:
optFlags = PNS_OPTIMIZER::MERGE_OBTUSE;
n_passes = 1;
extend = 0;
break;
case OE_Medium:
optFlags = PNS_OPTIMIZER::MERGE_OBTUSE;
n_passes = 2;
extend = 1;
break;
case OE_Full:
optFlags = PNS_OPTIMIZER::MERGE_SEGMENTS;
n_passes = 2;
break;
default:
break;
}
if(Settings().SmartPads())
optFlags |= PNS_OPTIMIZER::SMART_PADS ;
optimizer.SetEffortLevel( optFlags );
optimizer.SetCollisionMask( PNS_ITEM::ANY );
for(int pass = 0; pass < n_passes; pass ++)
{
std::reverse ( m_optimizerQueue.begin(), m_optimizerQueue.end() );
for(std::vector<PNS_LINE*>::iterator i = m_optimizerQueue.begin(); i != m_optimizerQueue.end(); ++i)
{
PNS_LINE *line = *i;
if( ! (line -> Marker() & MK_HEAD ) )
{
if(effort == OE_Medium || effort == OE_Low )
{
RANGE<int> r = findShovedVertexRange ( line );
if (r.Defined())
{
int start_v = std::max(0, r.MinV() - extend);
int end_v = std::min(line->PointCount() - 1 , r.MaxV() + extend );
line->ClipVertexRange ( start_v, end_v );
}
}
PNS_LINE optimized;
if( optimizer.Optimize( line, &optimized ) )
{
node->Remove( line );
line->SetShape(optimized.CLine());
node->Add( line );
}
}
}
}
}
const RANGE<int> PNS_SHOVE::findShovedVertexRange ( PNS_LINE *l )
{
RANGE<int> r;
for(int i = 0; i < l->SegmentCount(); i++)
{
PNS_SEGMENT *s = (*l->LinkedSegments())[i];
PNS_JOINT *jt = m_root->FindJoint( s->Seg().A, s->Layer(), s->Net() );
bool found = false;
if(jt)
{
BOOST_FOREACH( PNS_ITEM *item, jt->LinkList() )
{
if(item->OfKind(PNS_ITEM::SEGMENT))
{
PNS_SEGMENT *s_old = (PNS_SEGMENT *) item;
if( s_old->Net() == s->Net() &&
s_old->Layer() == s->Layer() &&
s_old->Seg().A == s->Seg().A &&
s_old->Seg().B == s->Seg().B )
{
found = true;
break;
}
}
}
}
if(!found)
{
r.Grow(i);
r.Grow(i + 1);
}
} }
return r;
}
PNS_NODE* PNS_SHOVE::CurrentNode()
{
return m_nodeStack.empty() ? m_root : m_nodeStack.back().node;
}
const PNS_LINE PNS_SHOVE::NewHead() const
{
assert(m_newHead);
return *m_newHead;
}
void PNS_SHOVE::SetInitialLine ( PNS_LINE *aInitial )
{
m_root = m_root->Branch();
m_root->Remove ( aInitial );
} }
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_SHOVE_H #ifndef __PNS_SHOVE_H
...@@ -25,61 +25,114 @@ ...@@ -25,61 +25,114 @@
#include <stack> #include <stack>
#include "pns_optimizer.h" #include "pns_optimizer.h"
#include "pns_routing_settings.h"
#include "pns_algo_base.h"
#include "pns_logger.h"
#include "range.h"
class PNS_LINE; class PNS_LINE;
class PNS_NODE; class PNS_NODE;
class PNS_ROUTER; class PNS_ROUTER;
class PNS_SHOVE /**
* Class PNS_SHOVE
*
* The actual Push and Shove algorithm.
*/
class PNS_SHOVE : public PNS_ALGO_BASE
{ {
public: public:
PNS_SHOVE( PNS_NODE* aWorld );
~PNS_SHOVE();
enum ShoveStatus enum ShoveStatus
{ {
SH_OK = 0, SH_OK = 0,
SH_NULL, SH_NULL,
SH_INCOMPLETE SH_INCOMPLETE,
SH_HEAD_MODIFIED
}; };
ShoveStatus ShoveLines( PNS_LINE* aCurrentHead ); PNS_SHOVE( PNS_NODE* aWorld, PNS_ROUTER *aRouter );
~PNS_SHOVE();
PNS_NODE* GetCurrentNode() virtual PNS_LOGGER *Logger()
{ {
return m_nodeStack.empty() ? m_root : m_nodeStack.back().node; return &m_logger;
} }
const PNS_COST_ESTIMATOR TotalCost() const; ShoveStatus ShoveLines( const PNS_LINE& aCurrentHead );
ShoveStatus ShoveDraggingVia ( PNS_VIA *aVia, const VECTOR2I& aWhere, PNS_VIA **aNewVia );
void Reset(); PNS_NODE* CurrentNode();
void KillChildNodes();
private: const PNS_LINE NewHead() const;
static const int ShoveTimeLimit = 3000;
bool tryShove( PNS_NODE* aWorld, PNS_LINE* aTrack, PNS_LINE* aObstacle,
PNS_SEGMENT& aObstacleSeg, PNS_LINE* aResult, bool aInvertWinding );
ShoveStatus shoveSingleLine( PNS_NODE* aNode, PNS_LINE* aCurrent, PNS_LINE* aObstacle, void SetInitialLine ( PNS_LINE *aInitial );
PNS_SEGMENT& aObstacleSeg, PNS_LINE* aResult );
bool reduceSpringback( PNS_LINE* aHead ); private:
bool pushSpringback( PNS_NODE* aNode, PNS_LINE* aHead, const PNS_COST_ESTIMATOR& aCost ); typedef std::vector<SHAPE_LINE_CHAIN> HullSet;
typedef boost::optional<PNS_LINE> OptLine;
typedef std::pair <PNS_LINE *, PNS_LINE *> LinePair;
typedef std::vector<LinePair> LinePairVec;
struct SpringbackTag struct SpringbackTag
{ {
int64_t length; int64_t length;
int segments; int segments;
VECTOR2I p; VECTOR2I p;
PNS_NODE* node; PNS_NODE *node;
PNS_ITEMSET headItems;
PNS_COST_ESTIMATOR cost; PNS_COST_ESTIMATOR cost;
}; };
std::vector<SpringbackTag> m_nodeStack; ShoveStatus processSingleLine(PNS_LINE *aCurrent, PNS_LINE* aObstacle, PNS_LINE *aShoved );
PNS_NODE* m_root; ShoveStatus processHullSet ( PNS_LINE *aCurrent, PNS_LINE *aObstacle, PNS_LINE *aShoved, const HullSet& hulls );
PNS_NODE* m_currentNode;
int m_iterLimit; bool reduceSpringback( const PNS_ITEMSET &aHeadItems );
bool pushSpringback( PNS_NODE* aNode, const PNS_ITEMSET &aHeadItems, const PNS_COST_ESTIMATOR& aCost );
ShoveStatus walkaroundLoneVia ( PNS_LINE *aCurrent, PNS_LINE *aObstacle, PNS_LINE *aShoved );
bool checkBumpDirection ( PNS_LINE *aCurrent, PNS_LINE *aShoved ) const;
ShoveStatus onCollidingLine( PNS_LINE *aCurrent, PNS_LINE *aObstacle );
ShoveStatus onCollidingSegment( PNS_LINE *aCurrent, PNS_SEGMENT *aObstacleSeg );
ShoveStatus onCollidingSolid( PNS_LINE *aCurrent, PNS_SOLID *aObstacleSolid );
ShoveStatus onCollidingVia( PNS_ITEM *aCurrent, PNS_VIA *aObstacleVia );
ShoveStatus onReverseCollidingVia( PNS_LINE *aCurrent, PNS_VIA *aObstacleVia );
ShoveStatus pushVia ( PNS_VIA *aVia, const VECTOR2I& aForce, int aCurrentRank );
void unwindStack ( PNS_SEGMENT *seg );
void unwindStack ( PNS_ITEM *item );
void runOptimizer ( PNS_NODE *node, PNS_LINE *head );
void pushLine ( PNS_LINE *l );
void popLine();
const RANGE<int> findShovedVertexRange ( PNS_LINE *l );
PNS_LINE *assembleLine ( const PNS_SEGMENT *aSeg, int *aIndex = NULL );
PNS_LINE *cloneLine ( const PNS_LINE *aLine );
ShoveStatus shoveIteration(int aIter);
ShoveStatus shoveMainLoop();
std::vector<SpringbackTag> m_nodeStack;
std::vector<PNS_LINE*> m_lineStack;
std::vector<PNS_LINE*> m_optimizerQueue;
std::vector<PNS_ITEM*> m_gcItems;
PNS_NODE* m_root;
PNS_NODE* m_currentNode;
PNS_LINE* m_currentHead;
PNS_LINE* m_collidingLine;
OptLine m_newHead;
PNS_LOGGER m_logger;
PNS_VIA* m_draggedVia;
int m_iter;
}; };
#endif #endif // __PNS_SHOVE_H
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <math/vector2d.h> #include <math/vector2d.h>
...@@ -30,13 +30,15 @@ ...@@ -30,13 +30,15 @@
const SHAPE_LINE_CHAIN PNS_SOLID::Hull( int aClearance, int aWalkaroundThickness ) const const SHAPE_LINE_CHAIN PNS_SOLID::Hull( int aClearance, int aWalkaroundThickness ) const
{ {
int cl = aClearance + aWalkaroundThickness / 2;
switch( m_shape->Type() ) switch( m_shape->Type() )
{ {
case SH_RECT: case SH_RECT:
{ {
SHAPE_RECT* rect = static_cast<SHAPE_RECT*>( m_shape ); SHAPE_RECT* rect = static_cast<SHAPE_RECT*>( m_shape );
return OctagonalHull( rect->GetPosition(), rect->GetSize(), return OctagonalHull( rect->GetPosition(), rect->GetSize(),
aClearance + 1, 0.2 * aClearance ); cl + 1, 0.2 * cl );
} }
case SH_CIRCLE: case SH_CIRCLE:
...@@ -44,7 +46,12 @@ const SHAPE_LINE_CHAIN PNS_SOLID::Hull( int aClearance, int aWalkaroundThickness ...@@ -44,7 +46,12 @@ const SHAPE_LINE_CHAIN PNS_SOLID::Hull( int aClearance, int aWalkaroundThickness
SHAPE_CIRCLE* circle = static_cast<SHAPE_CIRCLE*>( m_shape ); SHAPE_CIRCLE* circle = static_cast<SHAPE_CIRCLE*>( m_shape );
int r = circle->GetRadius(); int r = circle->GetRadius();
return OctagonalHull( circle->GetCenter() - VECTOR2I( r, r ), VECTOR2I( 2 * r, 2 * r ), return OctagonalHull( circle->GetCenter() - VECTOR2I( r, r ), VECTOR2I( 2 * r, 2 * r ),
aClearance + 1, 0.52 * (r + aClearance) ); cl + 1, 0.52 * (r + cl) );
}
case SH_SEGMENT:
{
SHAPE_SEGMENT *seg = static_cast<SHAPE_SEGMENT *> ( m_shape );
return SegmentHull (*seg, aClearance, aWalkaroundThickness );
} }
default: default:
...@@ -55,10 +62,8 @@ const SHAPE_LINE_CHAIN PNS_SOLID::Hull( int aClearance, int aWalkaroundThickness ...@@ -55,10 +62,8 @@ const SHAPE_LINE_CHAIN PNS_SOLID::Hull( int aClearance, int aWalkaroundThickness
} }
PNS_ITEM* PNS_SOLID::Clone() const PNS_ITEM* PNS_SOLID::Clone ( ) const
{ {
// solids are never cloned as the shove algorithm never moves them PNS_ITEM *solid = new PNS_SOLID ( *this );
assert( false ); return solid;
return NULL;
} }
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_SOLID_H #ifndef __PNS_SOLID_H
...@@ -42,9 +42,16 @@ public: ...@@ -42,9 +42,16 @@ public:
delete m_shape; delete m_shape;
} }
PNS_ITEM* Clone() const; PNS_SOLID( const PNS_SOLID& aSolid ) :
PNS_ITEM ( aSolid )
{
m_shape = aSolid.m_shape->Clone();
m_pos = aSolid.m_pos;
}
PNS_ITEM* Clone( ) const;
const SHAPE* GetShape() const { return m_shape; } const SHAPE* Shape() const { return m_shape; }
const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0 ) const; const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0 ) const;
...@@ -56,18 +63,29 @@ public: ...@@ -56,18 +63,29 @@ public:
m_shape = shape; m_shape = shape;
} }
const VECTOR2I& GetCenter() const const VECTOR2I& Pos() const
{
return m_pos;
}
void SetPos( const VECTOR2I& aCenter )
{ {
return m_center; m_pos = aCenter;
} }
void SetCenter( const VECTOR2I& aCenter ) virtual VECTOR2I Anchor(int n) const
{ {
m_center = aCenter; return m_pos;
} }
virtual int AnchorCount() const
{
return 1;
}
private: private:
VECTOR2I m_center; VECTOR2I m_pos;
SHAPE* m_shape; SHAPE* m_shape;
}; };
......
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,13 +15,15 @@ ...@@ -15,13 +15,15 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "pns_utils.h" #include "pns_utils.h"
#include "pns_line.h" #include "pns_line.h"
#include "pns_router.h" #include "pns_router.h"
#include <geometry/shape_segment.h>
const SHAPE_LINE_CHAIN OctagonalHull( const VECTOR2I& aP0, const SHAPE_LINE_CHAIN OctagonalHull( const VECTOR2I& aP0,
const VECTOR2I& aSize, const VECTOR2I& aSize,
int aClearance, int aClearance,
...@@ -42,3 +44,51 @@ const SHAPE_LINE_CHAIN OctagonalHull( const VECTOR2I& aP0, ...@@ -42,3 +44,51 @@ const SHAPE_LINE_CHAIN OctagonalHull( const VECTOR2I& aP0,
return s; return s;
} }
const SHAPE_LINE_CHAIN SegmentHull ( const SHAPE_SEGMENT& aSeg,
int aClearance,
int aWalkaroundThickness )
{
int d = aSeg.GetWidth() / 2 + aClearance + aWalkaroundThickness / 2 + HULL_MARGIN;
int x = (int)( 2.0 / ( 1.0 + M_SQRT2 ) * d );
const VECTOR2I a = aSeg.GetSeg().A;
const VECTOR2I b = aSeg.GetSeg().B;
VECTOR2I dir = b - a;
VECTOR2I p0 = dir.Perpendicular().Resize( d );
VECTOR2I ds = dir.Perpendicular().Resize( x / 2 );
VECTOR2I pd = dir.Resize( x / 2 );
VECTOR2I dp = dir.Resize( d );
SHAPE_LINE_CHAIN s;
s.SetClosed( true );
s.Append( b + p0 + pd );
s.Append( b + dp + ds );
s.Append( b + dp - ds );
s.Append( b - p0 + pd );
s.Append( a - p0 - pd );
s.Append( a - dp - ds );
s.Append( a - dp + ds );
s.Append( a + p0 - pd );
// make sure the hull outline is always clockwise
if( s.CSegment( 0 ).Side( a ) < 0 )
return s.Reverse();
else
return s;
}
SHAPE_RECT ApproximateSegmentAsRect( const SHAPE_SEGMENT& aSeg )
{
SHAPE_RECT r;
VECTOR2I delta ( aSeg.GetWidth() / 2, aSeg.GetWidth() / 2 );
VECTOR2I p0 ( aSeg.GetSeg().A - delta );
VECTOR2I p1 ( aSeg.GetSeg().B + delta );
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 ));
}
\ No newline at end of file
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_UTILS_H #ifndef __PNS_UTILS_H
...@@ -23,10 +23,20 @@ ...@@ -23,10 +23,20 @@
#include <math/vector2d.h> #include <math/vector2d.h>
#include <geometry/shape_line_chain.h> #include <geometry/shape_line_chain.h>
#include <geometry/shape_segment.h>
#include <geometry/shape_rect.h>
#define HULL_MARGIN 10
/** 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,
int aClearance, int aChamfer ); int aClearance, int aChamfer );
const SHAPE_LINE_CHAIN SegmentHull ( const SHAPE_SEGMENT& aSeg,
int aClearance,
int aWalkaroundThickness );
SHAPE_RECT ApproximateSegmentAsRect( const SHAPE_SEGMENT& aSeg );
#endif // __PNS_UTILS_H #endif // __PNS_UTILS_H
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,98 +15,16 @@ ...@@ -15,98 +15,16 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include "pns_via.h" #include "pns_via.h"
#include "pns_node.h" #include "pns_node.h"
#include "pns_utils.h" #include "pns_utils.h"
#include "pns_router.h"
#include <geometry/shape_rect.h> #include <geometry/shape_rect.h>
static bool Circle2Circle( VECTOR2I p1, VECTOR2I p2, int r1, int r2, VECTOR2I& force )
{
int mindist = r1 + r2;
VECTOR2I delta = p2 - p1;
int dist = delta.EuclideanNorm();
if( dist >= mindist )
return false;
force = delta.Resize( abs( mindist - dist ) + 1 );
return true;
};
static bool Rect2Circle( VECTOR2I rp0, VECTOR2I rsize, VECTOR2I cc, int cr, VECTOR2I& force )
{
VECTOR2I vts[] =
{
VECTOR2I( rp0.x, rp0.y ),
VECTOR2I( rp0.x, rp0.y + rsize.y ),
VECTOR2I( rp0.x + rsize.x, rp0.y + rsize.y ),
VECTOR2I( rp0.x + rsize.x, rp0.y ),
VECTOR2I( rp0.x, rp0.y )
};
int dist = INT_MAX;
VECTOR2I nearest;
for( int i = 0; i < 4; i++ )
{
SEG s( vts[i], vts[i + 1] );
VECTOR2I pn = s.NearestPoint( cc );
int d = (pn - cc).EuclideanNorm();
if( d < dist )
{
nearest = pn;
dist = d;
}
}
bool inside = cc.x >= rp0.x && cc.x <= (rp0.x + rsize.x)
&& cc.y >= rp0.y && cc.y <= (rp0.y + rsize.y);
VECTOR2I delta = cc - nearest;
if( dist >= cr && !inside )
return false;
if( inside )
force = -delta.Resize( abs( cr + dist ) + 1 );
else
force = delta.Resize( abs( cr - dist ) + 1 );
return true;
};
static bool ShPushoutForce( const SHAPE* shape, VECTOR2I p, int r, VECTOR2I& force, int clearance )
{
switch( shape->Type() )
{
case SH_CIRCLE:
{
const SHAPE_CIRCLE* cir = static_cast<const SHAPE_CIRCLE*>(shape);
return Circle2Circle( cir->GetCenter(), p, cir->GetRadius(), r + clearance + 1, force );
}
case SH_RECT:
{
const SHAPE_RECT* rect = static_cast<const SHAPE_RECT*>(shape);
return Rect2Circle( rect->GetPosition(), rect->GetSize(), p, r + clearance + 1, force );
}
default:
return false;
}
return false;
}
bool PNS_VIA::PushoutForce( PNS_NODE* aNode, bool PNS_VIA::PushoutForce( PNS_NODE* aNode,
const VECTOR2I& aDirection, const VECTOR2I& aDirection,
VECTOR2I& aForce, VECTOR2I& aForce,
...@@ -115,30 +33,31 @@ bool PNS_VIA::PushoutForce( PNS_NODE* aNode, ...@@ -115,30 +33,31 @@ bool PNS_VIA::PushoutForce( PNS_NODE* aNode,
{ {
int iter = 0; int iter = 0;
PNS_VIA mv( *this ); PNS_VIA mv( *this );
VECTOR2I force, totalForce; VECTOR2I force, totalForce, force2;
while( iter < aMaxIterations ) while( iter < aMaxIterations )
{ {
PNS_NODE::OptObstacle obs = aNode->CheckColliding( &mv,
aSolidsOnly ? PNS_ITEM::SOLID : PNS_ITEM::ANY ); PNS_NODE::OptObstacle obs = aNode->CheckColliding( &mv, aSolidsOnly ? PNS_ITEM::SOLID : PNS_ITEM::ANY );
if( !obs ) if( !obs )
break; break;
int clearance = aNode->GetClearance( obs->item, &mv ); int clearance = aNode->GetClearance( obs->item, &mv );
if( iter > 10 ) if( iter > aMaxIterations / 2 )
{ {
VECTOR2I l = -aDirection.Resize( m_diameter / 4 ); VECTOR2I l = aDirection.Resize( m_diameter / 2 );
totalForce += l; totalForce += l;
mv.SetPos( mv.GetPos() + l ); mv.SetPos( mv.Pos() + l );
} }
if( ShPushoutForce( obs->item->GetShape(), mv.GetPos(), mv.GetDiameter() / 2, force, bool col = CollideShapes( obs->item->Shape(), mv.Shape(), clearance, true, force2 );
clearance ) )
{ if(col) {
totalForce += force; totalForce += force2;
mv.SetPos( mv.GetPos() + force ); mv.SetPos( mv.Pos() + force2 );
} }
...@@ -155,7 +74,26 @@ bool PNS_VIA::PushoutForce( PNS_NODE* aNode, ...@@ -155,7 +74,26 @@ bool PNS_VIA::PushoutForce( PNS_NODE* aNode,
const SHAPE_LINE_CHAIN PNS_VIA::Hull( int aClearance, int aWalkaroundThickness ) const const SHAPE_LINE_CHAIN PNS_VIA::Hull( int aClearance, int aWalkaroundThickness ) const
{ {
int cl = (aClearance + aWalkaroundThickness / 2);
return OctagonalHull( m_pos - return OctagonalHull( m_pos -
VECTOR2I( m_diameter / 2, m_diameter / 2 ), VECTOR2I( m_diameter, VECTOR2I( m_diameter / 2, m_diameter / 2 ), VECTOR2I( m_diameter,
m_diameter ), aClearance + 1, (2 * aClearance + m_diameter) * 0.26 ); m_diameter ), cl + 1, (2 * cl + m_diameter) * 0.26 );
}
PNS_VIA* PNS_VIA::Clone ( ) const
{
PNS_VIA* v = new PNS_VIA();
v->SetNet( Net() );
v->SetLayers( Layers() );
v->m_pos = m_pos;
v->m_diameter = m_diameter;
v->m_drill = m_drill;
v->m_owner = NULL;
v->m_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 );
v->m_rank = m_rank;
v->m_marker = m_marker;
return v;
} }
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_VIA_H #ifndef __PNS_VIA_H
...@@ -45,16 +45,21 @@ public: ...@@ -45,16 +45,21 @@ public:
}; };
PNS_VIA( const PNS_VIA& b ) : PNS_ITEM( VIA ) PNS_VIA( const PNS_VIA& b ) :
PNS_ITEM( VIA )
{ {
SetNet( b.GetNet() ); SetNet( b.Net() );
SetLayers( b.GetLayers() ); SetLayers( b.Layers() );
m_pos = b.m_pos; m_pos = b.m_pos;
m_diameter = b.m_diameter; m_diameter = b.m_diameter;
m_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 ); m_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 );
m_marker = b.m_marker;
m_rank = b.m_rank;
m_owner = b.m_owner;
m_drill = b.m_drill;
} }
const VECTOR2I& GetPos() const const VECTOR2I& Pos() const
{ {
return m_pos; return m_pos;
} }
...@@ -65,7 +70,7 @@ public: ...@@ -65,7 +70,7 @@ public:
m_shape.SetCenter( aPos ); m_shape.SetCenter( aPos );
} }
int GetDiameter() const int Diameter() const
{ {
return m_diameter; return m_diameter;
} }
...@@ -76,7 +81,7 @@ public: ...@@ -76,7 +81,7 @@ public:
m_shape.SetRadius( m_diameter / 2 ); m_shape.SetRadius( m_diameter / 2 );
} }
int GetDrill() const int Drill() const
{ {
return m_drill; return m_drill;
} }
...@@ -92,25 +97,24 @@ public: ...@@ -92,25 +97,24 @@ public:
bool aSolidsOnly = true, bool aSolidsOnly = true,
int aMaxIterations = 10 ); int aMaxIterations = 10 );
const SHAPE* GetShape() const const SHAPE* Shape() const
{ {
return &m_shape; return &m_shape;
} }
PNS_VIA* Clone() const PNS_VIA* Clone ( ) const;
{
PNS_VIA* v = new PNS_VIA();
v->SetNet( GetNet() );
v->SetLayers( GetLayers() );
v->m_pos = m_pos;
v->m_diameter = m_diameter;
v->m_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 );
return v; const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0 ) const;
virtual VECTOR2I Anchor(int n) const
{
return m_pos;
} }
const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0 ) const; virtual int AnchorCount() const
{
return 1;
}
private: private:
......
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
...@@ -38,8 +38,8 @@ void PNS_WALKAROUND::start( const PNS_LINE& aInitialPath ) ...@@ -38,8 +38,8 @@ void PNS_WALKAROUND::start( const PNS_LINE& aInitialPath )
PNS_NODE::OptObstacle PNS_WALKAROUND::nearestObstacle( const PNS_LINE& aPath ) PNS_NODE::OptObstacle PNS_WALKAROUND::nearestObstacle( const PNS_LINE& aPath )
{ {
return m_world->NearestObstacle( &aPath, return m_world->NearestObstacle( &aPath, m_item_mask);
m_solids_only ? (PNS_ITEM::SOLID | PNS_ITEM::VIA) : PNS_ITEM::ANY );
} }
...@@ -55,33 +55,42 @@ PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::singleStep( PNS_LINE& aPath, ...@@ -55,33 +55,42 @@ PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::singleStep( PNS_LINE& aPath,
SHAPE_LINE_CHAIN path_pre[2], path_walk[2], path_post[2]; SHAPE_LINE_CHAIN path_pre[2], path_walk[2], path_post[2];
VECTOR2I last = aPath.GetCLine().CPoint( -1 ); VECTOR2I last = aPath.CPoint( -1 );;
if( ( current_obs->hull ).PointInside( last ) ) if( ( current_obs->hull ).PointInside( last ) )
{ {
m_recursiveBlockageCount++; m_recursiveBlockageCount++;
if( m_recursiveBlockageCount < 3 ) if( m_recursiveBlockageCount < 3 )
aPath.GetLine().Append( current_obs->hull.NearestPoint( last ) ); aPath.Line().Append( current_obs->hull.NearestPoint( last ) );
else else
{ {
aPath = aPath.ClipToNearestObstacle( m_world ); aPath = aPath.ClipToNearestObstacle( m_world );
return STUCK; return DONE;
} }
} }
aPath.NewWalkaround( current_obs->hull, path_pre[0], path_walk[0], aPath.Walkaround( current_obs->hull, path_pre[0], path_walk[0],
path_post[0], aWindingDirection ); path_post[0], aWindingDirection );
aPath.NewWalkaround( current_obs->hull, path_pre[1], path_walk[1], aPath.Walkaround( current_obs->hull, path_pre[1], path_walk[1],
path_post[1], !aWindingDirection ); path_post[1], !aWindingDirection );
#ifdef DEBUG
m_logger.NewGroup (aWindingDirection ? "walk-cw" : "walk-ccw", m_iteration);
m_logger.Log ( &path_walk[0], 0, "path-walk");
m_logger.Log ( &path_pre[0], 1, "path-pre");
m_logger.Log ( &path_post[0], 4, "path-post");
m_logger.Log ( &current_obs->hull, 2, "hull");
m_logger.Log ( current_obs->item, 3, "item");
#endif
int len_pre = path_walk[0].Length(); int len_pre = path_walk[0].Length();
int len_alt = path_walk[1].Length(); int len_alt = path_walk[1].Length();
PNS_LINE walk_path( aPath, path_walk[1] ); PNS_LINE walk_path( aPath, path_walk[1] );
bool alt_collides = m_world->CheckColliding( &walk_path, bool alt_collides = m_world->CheckColliding( &walk_path, m_item_mask );
m_solids_only ? PNS_ITEM::SOLID : PNS_ITEM::ANY );
SHAPE_LINE_CHAIN pnew; SHAPE_LINE_CHAIN pnew;
...@@ -126,6 +135,7 @@ PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::Route( const PNS_LINE& aInitial ...@@ -126,6 +135,7 @@ PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::Route( const PNS_LINE& aInitial
WalkaroundStatus s_cw = IN_PROGRESS, s_ccw = IN_PROGRESS; WalkaroundStatus s_cw = IN_PROGRESS, s_ccw = IN_PROGRESS;
SHAPE_LINE_CHAIN best_path; SHAPE_LINE_CHAIN best_path;
start( aInitialPath ); start( aInitialPath );
m_currentObstacle[0] = m_currentObstacle[1] = nearestObstacle( aInitialPath ); m_currentObstacle[0] = m_currentObstacle[1] = nearestObstacle( aInitialPath );
...@@ -143,9 +153,10 @@ PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::Route( const PNS_LINE& aInitial ...@@ -143,9 +153,10 @@ PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::Route( const PNS_LINE& aInitial
if( ( s_cw == DONE && s_ccw == DONE ) || ( s_cw == STUCK && s_ccw == STUCK ) ) if( ( s_cw == DONE && s_ccw == DONE ) || ( s_cw == STUCK && s_ccw == STUCK ) )
{ {
int len_cw = path_cw.GetCLine().Length(); int len_cw = path_cw.CLine().Length();
int len_ccw = path_ccw.GetCLine().Length(); int len_ccw = path_ccw.CLine().Length();
if( m_forceLongerPath ) if( m_forceLongerPath )
aWalkPath = (len_cw > len_ccw ? path_cw : path_ccw); aWalkPath = (len_cw > len_ccw ? path_cw : path_ccw);
else else
...@@ -166,11 +177,11 @@ PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::Route( const PNS_LINE& aInitial ...@@ -166,11 +177,11 @@ PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::Route( const PNS_LINE& aInitial
m_iteration++; m_iteration++;
} }
if( m_iteration == m_iteration_limit ) if( m_iteration == m_iteration_limit )
{ {
int len_cw = path_cw.GetCLine().Length(); int len_cw = path_cw.CLine().Length();
int len_ccw = path_ccw.GetCLine().Length(); int len_ccw = path_ccw.CLine().Length();
if( m_forceLongerPath ) if( m_forceLongerPath )
...@@ -185,7 +196,7 @@ PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::Route( const PNS_LINE& aInitial ...@@ -185,7 +196,7 @@ PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::Route( const PNS_LINE& aInitial
// int len_ccw = path_ccw.GetCLine().Length(); // int len_ccw = path_ccw.GetCLine().Length();
bool found = false; bool found = false;
SHAPE_LINE_CHAIN l = aWalkPath.GetCLine(); SHAPE_LINE_CHAIN l = aWalkPath.CLine();
for( int i = 0; i < l.SegmentCount(); i++ ) for( int i = 0; i < l.SegmentCount(); i++ )
{ {
...@@ -198,7 +209,6 @@ PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::Route( const PNS_LINE& aInitial ...@@ -198,7 +209,6 @@ PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::Route( const PNS_LINE& aInitial
if( dist_n <= dist_a && dist_n < dist_b ) if( dist_n <= dist_a && dist_n < dist_b )
{ {
// PNSDisplayDebugLine( l, 3 );
l.Remove( i + 1, -1 ); l.Remove( i + 1, -1 );
l.Append( nearest ); l.Append( nearest );
l.Simplify(); l.Simplify();
...@@ -214,13 +224,21 @@ PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::Route( const PNS_LINE& aInitial ...@@ -214,13 +224,21 @@ PNS_WALKAROUND::WalkaroundStatus PNS_WALKAROUND::Route( const PNS_LINE& aInitial
} }
} }
aWalkPath.SetWorld( m_world ); aWalkPath.Line().Simplify();
aWalkPath.GetLine().Simplify();
if(aWalkPath.SegmentCount() < 1)
return STUCK;
if(aWalkPath.CPoint(-1) != aInitialPath.CPoint(-1))
return STUCK;
if(aWalkPath.CPoint(0) != aInitialPath.CPoint(0))
return STUCK;
WalkaroundStatus st = s_ccw == DONE || s_cw == DONE ? DONE : STUCK; WalkaroundStatus st = s_ccw == DONE || s_cw == DONE ? DONE : STUCK;
if( aOptimize && st == DONE ) if( aOptimize && st == DONE )
PNS_OPTIMIZER::Optimize( &aWalkPath, PNS_OPTIMIZER::MERGE_OBTUSE, m_world ); PNS_OPTIMIZER::Optimize( &aWalkPath, PNS_OPTIMIZER::MERGE_OBTUSE, m_world );
return st; return st;
} }
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __PNS_WALKAROUND_H #ifndef __PNS_WALKAROUND_H
...@@ -23,19 +23,26 @@ ...@@ -23,19 +23,26 @@
#include "pns_line.h" #include "pns_line.h"
#include "pns_node.h" #include "pns_node.h"
#include "pns_router.h"
#include "pns_logger.h"
#include "pns_algo_base.h"
class PNS_WALKAROUND class PNS_WALKAROUND : public PNS_ALGO_BASE
{ {
static const int DefaultIterationLimit = 50; static const int DefaultIterationLimit = 50;
public: public:
PNS_WALKAROUND( PNS_NODE* aWorld ) : PNS_WALKAROUND( PNS_NODE* aWorld, PNS_ROUTER *aRouter ) :
m_world( aWorld ), m_iteration_limit( DefaultIterationLimit ) PNS_ALGO_BASE ( aRouter ),
m_world( aWorld ),
m_iteration_limit( DefaultIterationLimit )
{ {
m_forceSingleDirection = false; m_forceSingleDirection = false;
m_forceLongerPath = false; m_forceLongerPath = false;
m_cursorApproachMode = false; m_cursorApproachMode = false;
m_item_mask = PNS_ITEM::ANY;
}; };
~PNS_WALKAROUND() {}; ~PNS_WALKAROUND() {};
enum WalkaroundStatus enum WalkaroundStatus
...@@ -57,13 +64,22 @@ public: ...@@ -57,13 +64,22 @@ public:
void SetSolidsOnly( bool aSolidsOnly ) void SetSolidsOnly( bool aSolidsOnly )
{ {
m_solids_only = aSolidsOnly; if(aSolidsOnly)
m_item_mask = PNS_ITEM::SOLID;
else
m_item_mask = PNS_ITEM::ANY;
}
void SetItemMask ( int aMask )
{
m_item_mask = aMask;
} }
void SetSingleDirection( bool aForceSingleDirection ) void SetSingleDirection( bool aForceSingleDirection )
{ {
m_forceSingleDirection = aForceSingleDirection; m_forceSingleDirection = aForceSingleDirection;
m_forceLongerPath = true; m_forceLongerPath = aForceSingleDirection;
//printf("FSD %d FPD %d\n", m_forceSingleDirection?1:0, m_forceLongerPath ? 1: 0);
} }
void SetApproachCursor( bool aEnabled, const VECTOR2I& aPos ) void SetApproachCursor( bool aEnabled, const VECTOR2I& aPos )
...@@ -75,6 +91,10 @@ public: ...@@ -75,6 +91,10 @@ public:
WalkaroundStatus Route( const PNS_LINE& aInitialPath, PNS_LINE& aWalkPath, WalkaroundStatus Route( const PNS_LINE& aInitialPath, PNS_LINE& aWalkPath,
bool aOptimize = true ); bool aOptimize = true );
virtual PNS_LOGGER *Logger() {
return &m_logger;
}
private: private:
void start( const PNS_LINE& aInitialPath ); void start( const PNS_LINE& aInitialPath );
...@@ -86,12 +106,13 @@ private: ...@@ -86,12 +106,13 @@ private:
int m_recursiveBlockageCount; int m_recursiveBlockageCount;
int m_iteration; int m_iteration;
int m_iteration_limit; int m_iteration_limit;
bool m_solids_only; int m_item_mask;
bool m_forceSingleDirection, m_forceLongerPath; bool m_forceSingleDirection, m_forceLongerPath;
bool m_cursorApproachMode; bool m_cursorApproachMode;
VECTOR2I m_cursorPos; VECTOR2I m_cursorPos;
PNS_NODE::OptObstacle m_currentObstacle[2]; PNS_NODE::OptObstacle m_currentObstacle[2];
bool m_recursiveCollision[2]; bool m_recursiveCollision[2];
PNS_LOGGER m_logger;
}; };
#endif // __PNS_WALKAROUND_H #endif // __PNS_WALKAROUND_H
/*
* 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 __RANGE_H
#define __RANGE_H
template<class T> class RANGE {
public:
RANGE (T aMin, T aMax) :
m_min(aMin),
m_max(aMax),
m_defined(true) {}
RANGE ():
m_defined (false) {};
T MinV() const
{
return m_min;
}
T MaxV() const
{
return m_max;
}
void Set ( T aMin, T aMax ) const
{
m_max = aMax;
m_min = aMin;
}
void Grow ( T value )
{
if(!m_defined)
{
m_min = value;
m_max = value;
m_defined = true;
} else {
m_min = std::min(m_min, value);
m_max = std::max(m_max, value);
}
}
bool Inside ( const T& value ) const
{
if(!m_defined)
return true;
return value >= m_min && value <= m_max;
}
bool Overlaps ( const RANGE<T> &aOther ) const
{
if(!m_defined || !aOther.m_defined)
return true;
return m_max >= aOther.m_min && m_min <= aOther.m_max;
}
bool Defined() const
{
return m_defined;
}
private:
T m_min, m_max;
bool m_defined;
};
#endif
\ No newline at end of file
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,13 +15,16 @@ ...@@ -15,13 +15,16 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <gal/color4d.h> #include <gal/color4d.h>
#include <geometry/shape_rect.h>
#include "class_track.h" #include "class_track.h"
#include <pcb_painter.h> #include <pcb_painter.h>
#include <colors.h>
#include "router_preview_item.h" #include "router_preview_item.h"
...@@ -34,9 +37,12 @@ using namespace KIGFX; ...@@ -34,9 +37,12 @@ using namespace KIGFX;
ROUTER_PREVIEW_ITEM::ROUTER_PREVIEW_ITEM( const PNS_ITEM* aItem, VIEW_GROUP* aParent ) : ROUTER_PREVIEW_ITEM::ROUTER_PREVIEW_ITEM( const PNS_ITEM* aItem, VIEW_GROUP* aParent ) :
EDA_ITEM( NOT_USED ) EDA_ITEM( NOT_USED )
{ {
m_Flags = 0;
m_parent = aParent; m_parent = aParent;
m_layer = DRAW_N;
m_shape = NULL;
m_clearance = -1;
m_originLayer = m_layer = ITEM_GAL_LAYER ( GP_OVERLAY );
if( aItem ) if( aItem )
Update( aItem ); Update( aItem );
...@@ -50,62 +56,73 @@ ROUTER_PREVIEW_ITEM::~ROUTER_PREVIEW_ITEM() ...@@ -50,62 +56,73 @@ ROUTER_PREVIEW_ITEM::~ROUTER_PREVIEW_ITEM()
void ROUTER_PREVIEW_ITEM::Update( const PNS_ITEM* aItem ) void ROUTER_PREVIEW_ITEM::Update( const PNS_ITEM* aItem )
{ {
m_layer = aItem->GetLayers().Start();
m_color = getLayerColor( m_layer ); m_originLayer = aItem->Layers().Start();
m_color.a = 0.8;
switch( aItem->GetKind() )
{
case PNS_ITEM::LINE:
m_type = PR_LINE;
m_width = static_cast<const PNS_LINE*>(aItem)->GetWidth();
m_line = *static_cast<const SHAPE_LINE_CHAIN*>( aItem->GetShape() );
break;
case PNS_ITEM::SEGMENT: assert (m_originLayer >= 0);
m_type = PR_LINE;
m_width = static_cast<const PNS_SEGMENT*>(aItem)->GetWidth();
m_line = *static_cast<const SHAPE_LINE_CHAIN*>( aItem->GetShape() );
break;
case PNS_ITEM::VIA: m_layer = m_originLayer;
m_type = PR_VIA; m_color = getLayerColor( m_originLayer );
m_color = COLOR4D( 0.7, 0.7, 0.7, 0.8 ); m_color.a = 0.8;
m_width = static_cast<const PNS_VIA*>(aItem)->GetDiameter(); m_depth = BaseOverlayDepth - aItem->Layers().Start();
m_viaCenter = static_cast<const PNS_VIA*>(aItem)->GetPos();
break; m_shape = aItem->Shape()->Clone();
switch( aItem->Kind() )
{
case PNS_ITEM::LINE:
m_type = PR_SHAPE;
m_width = ((PNS_LINE *) aItem)->Width();
break;
case PNS_ITEM::SEGMENT:
{
PNS_SEGMENT *seg = (PNS_SEGMENT *)aItem;
m_type = PR_SHAPE;
m_width = seg->Width();
break;
}
case PNS_ITEM::VIA:
m_type = PR_SHAPE;
m_width = 0;
m_color = COLOR4D( 0.7, 0.7, 0.7, 0.8 );
m_depth = ViaOverlayDepth;
break;
case PNS_ITEM::SOLID:
m_type = PR_SHAPE;
m_width = 0;
break;
default: default:
break; break;
} }
if(aItem->Marker() & MK_VIOLATION)
m_color = COLOR4D (0, 1, 0, 1);
if(aItem->Marker() & MK_HEAD)
m_color.Brighten(0.7);
ViewSetVisible( true ); ViewSetVisible( true );
ViewUpdate( GEOMETRY | APPEARANCE ); ViewUpdate( GEOMETRY | APPEARANCE );
} }
void ROUTER_PREVIEW_ITEM::MarkAsHead()
{
if( m_type != PR_VIA )
m_color.Saturate( 1.0 );
}
const BOX2I ROUTER_PREVIEW_ITEM::ViewBBox() const const BOX2I ROUTER_PREVIEW_ITEM::ViewBBox() const
{ {
BOX2I bbox; BOX2I bbox;
switch( m_type ) switch( m_type )
{ {
case PR_LINE: case PR_SHAPE:
bbox = m_line.BBox(); bbox = m_shape->BBox();
bbox.Inflate( m_width / 2 ); bbox.Inflate( m_width / 2 );
return bbox; return bbox;
case PR_VIA: case PR_POINT:
bbox = BOX2I( m_viaCenter, VECTOR2I( 0, 0 ) ); bbox = BOX2I ( m_pos - VECTOR2I(100000, 100000), VECTOR2I( 200000, 200000 ));
bbox.Inflate( m_width / 2 );
return bbox; return bbox;
default: default:
...@@ -115,76 +132,124 @@ const BOX2I ROUTER_PREVIEW_ITEM::ViewBBox() const ...@@ -115,76 +132,124 @@ const BOX2I ROUTER_PREVIEW_ITEM::ViewBBox() const
return bbox; return bbox;
} }
void ROUTER_PREVIEW_ITEM::drawLineChain( const SHAPE_LINE_CHAIN &l, KIGFX::GAL* aGal ) const
{
for( int s = 0; s < l.SegmentCount(); s++ )
aGal->DrawLine( l.CSegment( s ).A, l.CSegment( s ).B );
if( l.IsClosed() )
aGal->DrawLine( l.CSegment( -1 ).B, l.CSegment( 0 ).A );
}
void ROUTER_PREVIEW_ITEM::ViewDraw( int aLayer, KIGFX::GAL* aGal ) const void ROUTER_PREVIEW_ITEM::ViewDraw( int aLayer, KIGFX::GAL* aGal ) const
{ {
switch( m_type ) //col.Brighten(0.7);
aGal->SetLayerDepth( m_depth );
if(m_type == PR_SHAPE)
{ {
case PR_LINE:
aGal->SetLayerDepth( -100.0 );
aGal->SetLineWidth( m_width ); aGal->SetLineWidth( m_width );
aGal->SetStrokeColor( m_color ); aGal->SetStrokeColor( m_color );
aGal->SetIsStroke( true );
aGal->SetIsFill( false );
for( int s = 0; s < m_line.SegmentCount(); s++ )
aGal->DrawLine( m_line.CSegment( s ).A, m_line.CSegment( s ).B );
if( m_line.IsClosed() )
aGal->DrawLine( m_line.CSegment( -1 ).B, m_line.CSegment( 0 ).A );
break;
case PR_VIA:
aGal->SetLayerDepth( -101.0 );
aGal->SetIsStroke( false );
aGal->SetIsFill( true );
aGal->SetFillColor( m_color ); aGal->SetFillColor( m_color );
aGal->DrawCircle( m_viaCenter, m_width / 2 ); aGal->SetIsStroke( m_width ? true : false );
break; aGal->SetIsFill( true );
default:
break; if(!m_shape)
return;
switch( m_shape->Type() )
{
case SH_LINE_CHAIN:
{
const SHAPE_LINE_CHAIN *l = (const SHAPE_LINE_CHAIN *) m_shape;
drawLineChain(*l, aGal);
break;
}
case SH_SEGMENT:
{
const SHAPE_SEGMENT *s = (const SHAPE_SEGMENT *) m_shape;
aGal->DrawLine( s->GetSeg().A, s->GetSeg().B );
if(m_clearance > 0)
{
aGal->SetLayerDepth ( ClearanceOverlayDepth );
aGal->SetStrokeColor ( COLOR4D( DARKDARKGRAY ));
aGal->SetLineWidth( m_width + 2 * m_clearance );
aGal->DrawLine( s->GetSeg().A, s->GetSeg().B );
}
break;
}
case SH_CIRCLE:
{
const SHAPE_CIRCLE *c = (const SHAPE_CIRCLE *) m_shape;
aGal->DrawCircle( c->GetCenter(), c->GetRadius() );
if(m_clearance > 0)
{
aGal->SetLayerDepth ( ClearanceOverlayDepth );
aGal->SetFillColor ( COLOR4D( DARKDARKGRAY ));
aGal->SetIsStroke( false );
aGal->DrawCircle( c->GetCenter(), c->GetRadius() + m_clearance );
}
break;
}
case SH_RECT:
{
const SHAPE_RECT *r = (const SHAPE_RECT *) m_shape;
aGal->DrawRectangle (r->GetPosition(), r->GetPosition() + r->GetSize());
if(m_clearance > 0)
{
aGal->SetLayerDepth ( ClearanceOverlayDepth );
VECTOR2I p0 (r -> GetPosition() ), s ( r->GetSize() );
aGal->SetStrokeColor ( COLOR4D( DARKDARKGRAY ));
aGal->SetIsStroke( true );
aGal->SetLineWidth ( 2 * m_clearance );
aGal->DrawLine( p0, VECTOR2I(p0.x + s.x, p0.y) );
aGal->DrawLine( p0, VECTOR2I(p0.x, p0.y + s.y) );
aGal->DrawLine( p0 + s , VECTOR2I(p0.x + s.x, p0.y) );
aGal->DrawLine( p0 + s, VECTOR2I(p0.x, p0.y + s.y) );
}
break;
}
}
} }
} }
void ROUTER_PREVIEW_ITEM::DebugLine( const SHAPE_LINE_CHAIN& aLine, int aWidth, int aStyle ) void ROUTER_PREVIEW_ITEM::Line( const SHAPE_LINE_CHAIN& aLine, int aWidth, int aStyle )
{ {
#if 0
m_line = aLine; m_originLayer = m_layer = 0;
m_width = aWidth; m_width = aWidth;
m_color = assignColor( aStyle ); m_color = assignColor( aStyle );
m_type = PR_SHAPE;
m_depth = -2047;
m_shape = aLine.Clone();
ViewSetVisible(true);
m_type = PR_LINE;
ViewUpdate( GEOMETRY | APPEARANCE ); ViewUpdate( GEOMETRY | APPEARANCE );
#endif
} }
void ROUTER_PREVIEW_ITEM::Point( const VECTOR2I& aPos, int aStyle )
{
}
void ROUTER_PREVIEW_ITEM::DebugBox( const BOX2I& aBox, int aStyle ) void ROUTER_PREVIEW_ITEM::Box( const BOX2I& aBox, int aStyle )
{ {
#if 0
assert( false );
m_line.Clear();
m_line.Append( aBox.GetX(), aBox.GetY() );
m_line.Append( aBox.GetX() + aBox.GetWidth(), aBox.GetY() + aBox.GetHeight() );
m_line.Append( aBox.GetX() + aBox.GetWidth(), aBox.GetY() + aBox.GetHeight() );
m_line.Append( aBox.GetX(), aBox.GetY() + aBox.GetHeight() );
m_line.SetClosed( true );
m_width = 20000;
m_color = assignColor( aStyle );
m_type = PR_LINE;
ViewUpdate( GEOMETRY | APPEARANCE );
#endif
} }
const COLOR4D ROUTER_PREVIEW_ITEM::getLayerColor( int aLayer ) const const COLOR4D ROUTER_PREVIEW_ITEM::getLayerColor( int aLayer ) const
{ {
// assert (m_view != NULL);
PCB_RENDER_SETTINGS* settings = PCB_RENDER_SETTINGS* settings =
static_cast <PCB_RENDER_SETTINGS*> ( m_parent->GetView()->GetPainter()->GetSettings() ); static_cast <PCB_RENDER_SETTINGS*> ( m_parent->GetView()->GetPainter()->GetSettings() );
...@@ -202,10 +267,10 @@ const COLOR4D ROUTER_PREVIEW_ITEM::assignColor( int aStyle ) const ...@@ -202,10 +267,10 @@ const COLOR4D ROUTER_PREVIEW_ITEM::assignColor( int aStyle ) const
color = COLOR4D( 0, 1, 0, 1 ); break; color = COLOR4D( 0, 1, 0, 1 ); break;
case 1: case 1:
color = COLOR4D( 1, 0, 0, 0.3 ); break; color = COLOR4D( 1, 0, 0, 1 ); break;
case 2: case 2:
color = COLOR4D( 1, 0.5, 0.5, 1 ); break; color = COLOR4D( 1, 1, 0, 1 ); break;
case 3: case 3:
color = COLOR4D( 0, 0, 1, 1 ); break; color = COLOR4D( 0, 0, 1, 1 ); break;
...@@ -220,9 +285,11 @@ const COLOR4D ROUTER_PREVIEW_ITEM::assignColor( int aStyle ) const ...@@ -220,9 +285,11 @@ const COLOR4D ROUTER_PREVIEW_ITEM::assignColor( int aStyle ) const
color = COLOR4D( 0, 1, 1, 1 ); break; color = COLOR4D( 0, 1, 1, 1 ); break;
case 32: case 32:
color = COLOR4D( 0, 0, 1, 0.5 ); break; color = COLOR4D( 0, 0, 1, 1 ); break;
default: default:
color = COLOR4D( 0.4, 0.4, 0.4, 1 ); break;
break; break;
} }
......
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
* *
* This program is free software: you can redistribute it and/or modify it * This program is free software: you can redistribute it and/or modify it
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __ROUTER_PREVIEW_ITEM_H #ifndef __ROUTER_PREVIEW_ITEM_H
...@@ -46,24 +46,32 @@ class ROUTER_PREVIEW_ITEM : public EDA_ITEM ...@@ -46,24 +46,32 @@ class ROUTER_PREVIEW_ITEM : public EDA_ITEM
public: public:
enum ItemType enum ItemType
{ {
PR_VIA, PR_STUCK_MARKER = 0,
PR_LINE, PR_POINT,
PR_STUCK_MARKER PR_SHAPE
};
enum ItemFlags
{
PR_SUGGESTION = 1
}; };
ROUTER_PREVIEW_ITEM( const PNS_ITEM* aItem = NULL, KIGFX::VIEW_GROUP* aParent = NULL ); ROUTER_PREVIEW_ITEM( const PNS_ITEM* aItem = NULL, KIGFX::VIEW_GROUP* aParent = NULL );
~ROUTER_PREVIEW_ITEM(); ~ROUTER_PREVIEW_ITEM();
void Update( const PNS_ITEM* aItem ); void Update( const PNS_ITEM* aItem );
void StuckMarker( VECTOR2I& aPosition ); void StuckMarker( VECTOR2I& aPosition );
void DebugLine( const SHAPE_LINE_CHAIN& aLine, int aWidth = 0, int aStyle = 0 );
void DebugBox( const BOX2I& aBox, int aStyle = 0 ); void Line( const SHAPE_LINE_CHAIN& aLine, int aWidth = 0, int aStyle = 0 );
void Box( const BOX2I& aBox, int aStyle = 0 );
void Point ( const VECTOR2I& aPos, int aStyle = 0);
void SetColor( const KIGFX::COLOR4D& aColor )
{
m_color = aColor;
}
void SetClearance ( int aClearance )
{
m_clearance = aClearance;
}
void Show( int a, std::ostream& b ) const {}; void Show( int a, std::ostream& b ) const {};
...@@ -77,7 +85,7 @@ public: ...@@ -77,7 +85,7 @@ public:
aCount = 1; aCount = 1;
} }
void MarkAsHead(); void drawLineChain( const SHAPE_LINE_CHAIN &l, KIGFX::GAL *aGal ) const;
private: private:
const KIGFX::COLOR4D assignColor( int aStyle ) const; const KIGFX::COLOR4D assignColor( int aStyle ) const;
...@@ -86,17 +94,25 @@ private: ...@@ -86,17 +94,25 @@ private:
KIGFX::VIEW_GROUP* m_parent; KIGFX::VIEW_GROUP* m_parent;
PNS_ROUTER* m_router; PNS_ROUTER* m_router;
SHAPE_LINE_CHAIN m_line; SHAPE *m_shape;
ItemType m_type; ItemType m_type;
int m_style; int m_style;
int m_width; int m_width;
int m_layer; int m_layer;
int m_originLayer;
int m_clearance;
KIGFX::COLOR4D m_color; // fixme: shouldn't this go to VIEW?
static const int ClearanceOverlayDepth = -2000;
static const int BaseOverlayDepth = -2020;
static const int ViaOverlayDepth = -2046;
double m_depth;
VECTOR2I m_stuckPosition; KIGFX::COLOR4D m_color;
VECTOR2I m_viaCenter; VECTOR2I m_pos;
}; };
#endif #endif
...@@ -15,22 +15,28 @@ ...@@ -15,22 +15,28 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <wx/numdlg.h>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/bind.hpp>
#include "class_draw_panel_gal.h" #include "class_draw_panel_gal.h"
#include "class_board_item.h"
#include "class_board.h" #include "class_board.h"
#include <wxPcbStruct.h> #include <wxPcbStruct.h>
#include <id.h> #include <id.h>
#include <macros.h>
#include <pcbnew_id.h> #include <pcbnew_id.h>
#include <view/view_controls.h> #include <view/view_controls.h>
#include <pcbcommon.h> #include <pcbcommon.h>
#include <pcb_painter.h> #include <pcb_painter.h>
#include <dialogs/dialog_pns_settings.h>
#include <dialogs/dialog_track_via_size.h>
#include <base_units.h>
#include <tool/context_menu.h> #include <tool/context_menu.h>
#include <tools/common_actions.h> #include <tools/common_actions.h>
...@@ -45,30 +51,182 @@ ...@@ -45,30 +51,182 @@
using namespace KIGFX; using namespace KIGFX;
using boost::optional; using boost::optional;
//static TOOL_ACTION ACT_AutoEndRoute( "pcbnew.InteractiveRouter.AutoEndRoute", AS_CONTEXT, 'G' ); static TOOL_ACTION ACT_NewTrack( "pcbnew.InteractiveRouter.NewTrack",
//static TOOL_ACTION ACT_PlaceVia( "pcbnew.InteractiveRouter.PlaceVia", AS_CONTEXT, 'V' ); AS_CONTEXT, 'X',
//static TOOL_ACTION ACT_OpenRouteOptions( "pcbnew.InteractiveRouter.OpenRouterOptions", AS_CONTEXT, 'T' ); "New Track", "Starts laying a new track.");
//static TOOL_ACTION ACT_SwitchPosture( "pcbnew.InteractiveRouter.SwitchPosture", AS_CONTEXT, '/' ); static TOOL_ACTION ACT_EndTrack( "pcbnew.InteractiveRouter.EndTrack",
//static TOOL_ACTION ACT_EndTrack( "pcbnew.InteractiveRouter.EndTrack", AS_CONTEXT, WXK_END ); 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_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.");
ROUTER_TOOL::ROUTER_TOOL() : ROUTER_TOOL::ROUTER_TOOL() :
TOOL_INTERACTIVE( "pcbnew.InteractiveRouter" ) TOOL_INTERACTIVE( "pcbnew.InteractiveRouter" )
{ {
m_router = NULL; m_router = NULL;
m_menu = new CONTEXT_MENU; }
m_menu->SetTitle( wxT( "Interactive router" ) ); // fixme: not implemented yet. Sorry. class CONTEXT_TRACK_WIDTH_MENU: public CONTEXT_MENU
m_menu->Add( wxT( "Cancel" ), 1 ); {
m_menu->Add( wxT( "New track" ), 2 ); public:
m_menu->Add( wxT( "End track" ), 3 ); CONTEXT_TRACK_WIDTH_MENU()
m_menu->Add( wxT( "Auto-end track" ), 4 ); {
m_menu->Add( wxT( "Place via" ), 5 ); setCustomEventHandler( boost::bind( &CONTEXT_TRACK_WIDTH_MENU::handleCustomEvent, this, _1 ) );
m_menu->Add( wxT( "Switch posture" ), 6 ); }
m_menu->Add( wxT( "Routing options..." ), 7 ); 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, it breaks a piece of code. Lookup this line 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();
// General 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 CONTEXT_GRID_MENU: public CONTEXT_MENU { };
class ROUTER_TOOL_MENU: public CONTEXT_MENU {
public:
ROUTER_TOOL_MENU( BOARD *aBoard )
{
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_SwitchPosture );
AppendSeparator ( );
CONTEXT_TRACK_WIDTH_MENU* trackMenu = new CONTEXT_TRACK_WIDTH_MENU;
trackMenu->SetBoard( aBoard );
AppendSubMenu( trackMenu, wxT( "Select Track Width" ) );
Add( ACT_CustomTrackWidth );
AppendSeparator ( );
Add( ACT_RouterOptions );
}
};
ROUTER_TOOL::~ROUTER_TOOL() ROUTER_TOOL::~ROUTER_TOOL()
{ {
delete m_router; delete m_router;
...@@ -108,6 +266,7 @@ void ROUTER_TOOL::getNetclassDimensions( int aNetCode, int& aWidth, ...@@ -108,6 +266,7 @@ void ROUTER_TOOL::getNetclassDimensions( int aNetCode, int& aWidth,
int& aViaDiameter, int& aViaDrill ) int& aViaDiameter, int& aViaDrill )
{ {
BOARD* board = getModel<BOARD>( PCB_T ); BOARD* board = getModel<BOARD>( PCB_T );
BOARD_DESIGN_SETTINGS &bds = board->GetDesignSettings();
NETCLASS* netClass = NULL; NETCLASS* netClass = NULL;
NETINFO_ITEM* ni = board->FindNet( aNetCode ); NETINFO_ITEM* ni = board->FindNet( aNetCode );
...@@ -115,11 +274,11 @@ void ROUTER_TOOL::getNetclassDimensions( int aNetCode, int& aWidth, ...@@ -115,11 +274,11 @@ void ROUTER_TOOL::getNetclassDimensions( int aNetCode, int& aWidth,
if( ni ) if( ni )
{ {
wxString netClassName = ni->GetClassName(); wxString netClassName = ni->GetClassName();
netClass = board->GetDesignSettings().m_NetClasses.Find( netClassName ); netClass = bds.m_NetClasses.Find( netClassName );
} }
if( !netClass ) if( !netClass )
netClass = board->GetDesignSettings().m_NetClasses.GetDefault(); netClass = bds.m_NetClasses.GetDefault();
aWidth = netClass->GetTrackWidth(); aWidth = netClass->GetTrackWidth();
aViaDiameter = netClass->GetViaDiameter(); aViaDiameter = netClass->GetViaDiameter();
...@@ -134,74 +293,67 @@ PNS_ITEM* ROUTER_TOOL::pickSingleItem( const VECTOR2I& aWhere, int aNet, int aLa ...@@ -134,74 +293,67 @@ PNS_ITEM* ROUTER_TOOL::pickSingleItem( const VECTOR2I& aWhere, int aNet, int aLa
if( aLayer > 0 ) if( aLayer > 0 )
tl = aLayer; tl = aLayer;
PNS_ITEM* picked_seg = NULL; PNS_ITEM* prioritized[4];
PNS_ITEM* picked_via = NULL;
for(int i = 0; i < 4; i++)
prioritized[i] = 0;
PNS_ITEMSET candidates = m_router->QueryHoverItems( aWhere ); PNS_ITEMSET candidates = m_router->QueryHoverItems( aWhere );
BOOST_FOREACH( PNS_ITEM* item, candidates.Items() ) BOOST_FOREACH( PNS_ITEM* item, candidates.Items() )
{ {
if( !IsCopperLayer( item->GetLayers().Start() ) ) if( !IsCopperLayer( item->Layers().Start() ) )
continue; continue;
if( item->GetParent() && !item->GetParent()->ViewIsVisible() && // fixme: this causes flicker with live loop removal...
!item->GetParent()->IsSelected() ) //if( item->Parent() && !item->Parent()->ViewIsVisible() )
continue; // continue;
if( aNet < 0 || item->GetNet() == aNet ) if( aNet < 0 || item->Net() == aNet )
{ {
if( item->OfKind( PNS_ITEM::VIA | PNS_ITEM::SOLID ) ) if( item->OfKind( PNS_ITEM::VIA | PNS_ITEM::SOLID ) )
{ {
if( item->GetLayers().Overlaps( tl ) || !picked_via ) if( !prioritized[2] )
picked_via = item; prioritized[2] = item;
if( item->Layers().Overlaps( tl ))
prioritized[0] = item;
} }
else else
{ {
if( item->GetLayers().Overlaps( tl ) || !picked_seg ) if( !prioritized[3] )
picked_seg = item; prioritized[3] = item;
if (item->Layers().Overlaps( tl ))
prioritized[1] = item;
} }
} }
} }
if( DisplayOpt.ContrastModeDisplay ) PNS_ITEM *rv = NULL;
for(int i = 0; i < 4; i++)
{ {
if( picked_seg && !picked_seg->GetLayers().Overlaps( tl ) ) PNS_ITEM *item = prioritized[i];
picked_seg = NULL;
} if( DisplayOpt.ContrastModeDisplay )
if( item && !item->Layers().Overlaps( tl ) )
item = NULL;
PNS_ITEM* rv = picked_via ? picked_via : picked_seg; if(item)
{
rv = item;
break;
}
}
if( rv && aLayer >= 0 && !rv->GetLayers().Overlaps( aLayer ) ) if( rv && aLayer >= 0 && !rv->Layers().Overlaps( aLayer ) )
rv = NULL; rv = NULL;
if( rv ) if( rv )
TRACE( 0, "%s, layer : %d, tl: %d", rv->GetKindStr().c_str() % rv->GetLayers().Start() % TRACE( 0, "%s, layer : %d, tl: %d", rv->KindStr().c_str() % rv->Layers().Start() %
tl ); tl );
return rv; return rv;
} }
void ROUTER_TOOL::setMsgPanel( bool aEnabled, int aEntry,
const wxString& aUpperMessage, const wxString& aLowerMessage )
{
PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME> ();
if( m_panelItems.size() <= (unsigned int) aEntry )
m_panelItems.resize( aEntry + 1 );
m_panelItems[aEntry] = MSG_PANEL_ITEM( aUpperMessage, aLowerMessage, BLACK );
frame->SetMsgPanel( m_panelItems );
}
void ROUTER_TOOL::clearMsgPanel()
{
PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME> ();
frame->ClearMsgPanel();
}
void ROUTER_TOOL::highlightNet( bool aEnabled, int aNetcode ) void ROUTER_TOOL::highlightNet( bool aEnabled, int aNetcode )
{ {
RENDER_SETTINGS* rs = getView()->GetPainter()->GetSettings(); RENDER_SETTINGS* rs = getView()->GetPainter()->GetSettings();
...@@ -214,37 +366,104 @@ void ROUTER_TOOL::highlightNet( bool aEnabled, int aNetcode ) ...@@ -214,37 +366,104 @@ void ROUTER_TOOL::highlightNet( bool aEnabled, int aNetcode )
getView()->UpdateAllLayersColor(); getView()->UpdateAllLayersColor();
} }
void ROUTER_TOOL::handleCommonEvents( TOOL_EVENT& evt )
{
#ifdef DEBUG
if( evt.IsKeyPressed() )
{
switch( evt.KeyCode() )
{
case 'S':
TRACEn(2, "saving drag/route log...\n");
m_router->DumpLog();
break;
}
}
else
#endif
if( evt.IsAction( &ACT_RouterOptions ) )
{
DIALOG_PNS_SETTINGS settingsDlg( m_toolMgr->GetEditFrame(), m_router->Settings() );
if( settingsDlg.ShowModal() )
m_router->ApplySettings();
}
else if( evt.IsAction( &ACT_CustomTrackWidth ) )
{
DIALOG_TRACK_VIA_SIZE sizeDlg( m_toolMgr->GetEditFrame(), m_router->Settings() );
BOARD_DESIGN_SETTINGS& bds = getModel<BOARD>( PCB_T )->GetDesignSettings();
sizeDlg.ShowModal();
// TODO it should be changed, router settings won't keep track & via sizes in the future
bds.SetCustomTrackWidth( m_router->Settings().GetTrackWidth() );
bds.SetCustomViaSize( m_router->Settings().GetViaDiameter() );
bds.SetCustomViaDrill( m_router->Settings().GetViaDrill() );
bds.UseCustomTrackViaSize( true );
// TODO Should be done another way, but RunAction() won't work here. As the ROUTER_TOOL
// did not call Wait(), it does not wait for events and therefore the sent event
// won't arrive here
TOOL_EVENT event = COMMON_ACTIONS::trackViaSizeChanged.MakeEvent();
handleCommonEvents( event );
}
else if( evt.IsAction( &COMMON_ACTIONS::trackViaSizeChanged ) )
{
BOARD_DESIGN_SETTINGS& bds = getModel<BOARD>( PCB_T )->GetDesignSettings();
m_router->Settings().SetTrackWidth( bds.GetCurrentTrackWidth() );
m_router->Settings().SetViaDiameter( bds.GetCurrentViaSize() );
m_router->Settings().SetViaDrill( bds.GetCurrentViaDrill() );
m_router->ApplySettings();
}
}
void ROUTER_TOOL::updateStartItem( TOOL_EVENT& aEvent ) void ROUTER_TOOL::updateStartItem( TOOL_EVENT& aEvent )
{ {
VIEW_CONTROLS* ctls = getViewControls(); VIEW_CONTROLS* ctls = getViewControls();
int tl = getView()->GetTopLayer(); int tl = getView()->GetTopLayer();
VECTOR2I cp = ctls->GetCursorPosition();
PNS_ITEM* startItem = NULL; PNS_ITEM* startItem = NULL;
if( aEvent.IsMotion() || aEvent.IsClick() ) if( aEvent.IsMotion() || aEvent.IsClick() )
{ {
VECTOR2I p = aEvent.Position(); VECTOR2I p = aEvent.Position();
startItem = pickSingleItem( p ); startItem = pickSingleItem( p );
if( startItem && startItem->GetNet() >= 0 ) 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; bool dummy;
VECTOR2I cursorPos = m_router->SnapToItem( startItem, p, dummy ); VECTOR2I psnap = m_router->SnapToItem( startItem, p, dummy );
ctls->ForceCursorPosition( true, cursorPos );
if (snapEnabled) {
m_startSnapPoint = cursorPos; m_startSnapPoint = psnap;
ctls->ForceCursorPosition( true, psnap );
} else {
m_startSnapPoint = cp;
ctls->ForceCursorPosition( false );
}
if( startItem->GetLayers().IsMultilayer() ) if( startItem->Layers().IsMultilayer() )
m_startLayer = tl; m_startLayer = tl;
else else
m_startLayer = startItem->GetLayers().Start(); m_startLayer = startItem->Layers().Start();
m_startItem = startItem; m_startItem = startItem;
} }
else else
{ {
m_startItem = NULL; m_startItem = NULL;
m_startSnapPoint = p; m_startSnapPoint = cp;
m_startLayer = tl; m_startLayer = tl;
ctls->ForceCursorPosition( false ); ctls->ForceCursorPosition( false );
} }
...@@ -255,13 +474,18 @@ void ROUTER_TOOL::updateStartItem( TOOL_EVENT& aEvent ) ...@@ -255,13 +474,18 @@ void ROUTER_TOOL::updateStartItem( TOOL_EVENT& aEvent )
void ROUTER_TOOL::updateEndItem( TOOL_EVENT& aEvent ) void ROUTER_TOOL::updateEndItem( TOOL_EVENT& aEvent )
{ {
VIEW_CONTROLS* ctls = getViewControls(); VIEW_CONTROLS* ctls = getViewControls();
VECTOR2I p = aEvent.Position(); VECTOR2I p = getView()->ToWorld( ctls->GetMousePosition() );
VECTOR2I cp = ctls->GetCursorPosition();
int layer; int layer;
if( m_router->GetCurrentNet() < 0 || !m_startItem ) bool snapEnabled = !aEvent.Modifier(MD_SHIFT);
m_router->EnableSnapping ( snapEnabled );
if( !snapEnabled || m_router->GetCurrentNet() < 0 || !m_startItem )
{ {
m_endItem = NULL; m_endItem = NULL;
m_endSnapPoint = p; m_endSnapPoint = cp;
return; return;
} }
...@@ -272,7 +496,7 @@ void ROUTER_TOOL::updateEndItem( TOOL_EVENT& aEvent ) ...@@ -272,7 +496,7 @@ void ROUTER_TOOL::updateEndItem( TOOL_EVENT& aEvent )
else else
layer = m_router->GetCurrentLayer(); layer = m_router->GetCurrentLayer();
PNS_ITEM* endItem = pickSingleItem( p, m_startItem->GetNet(), layer ); PNS_ITEM* endItem = pickSingleItem( p, m_startItem->Net(), layer );
if( endItem ) if( endItem )
{ {
...@@ -284,57 +508,39 @@ void ROUTER_TOOL::updateEndItem( TOOL_EVENT& aEvent ) ...@@ -284,57 +508,39 @@ void ROUTER_TOOL::updateEndItem( TOOL_EVENT& aEvent )
else else
{ {
m_endItem = NULL; m_endItem = NULL;
m_endSnapPoint = ctls->GetCursorPosition(); m_endSnapPoint = cp;
ctls->ForceCursorPosition( false ); ctls->ForceCursorPosition( false );
} }
// Draw ratsnest for the currently routed track
RN_DATA* ratsnest = getModel<BOARD>( PCB_T )->GetRatsnest();
ratsnest->ClearSimple();
if( ( m_endItem == NULL || m_endItem == m_startItem ) && m_startItem->GetNet() > 0 )
{
// The ending node has to be first, so the line for the track is drawn first
ratsnest->AddSimple( m_endSnapPoint, m_startItem->GetNet() );
// Those nodes are added just to force ratsnest not to drawn
// lines to already routed parts of the track
const PICKED_ITEMS_LIST& changes = m_router->GetLastChanges();
for( unsigned int i = 0; i < changes.GetCount(); ++i )
{
// Block the new tracks, do not handle tracks that were moved
// (moved tracks are saved in the undo buffer with UR_DELETED status instead)
if( changes.GetPickedItemStatus( i ) == UR_NEW )
ratsnest->AddBlocked( static_cast<BOARD_CONNECTED_ITEM*>( changes.GetPickedItem( i ) ) );
}
// Also the origin of the new track should be skipped in the ratsnest shown for the routed track
ratsnest->AddBlocked( static_cast<BOARD_ITEM*>( m_startItem->GetParent() ) );
}
if( m_endItem ) if( m_endItem )
TRACE( 0, "%s, layer : %d", m_endItem->GetKindStr().c_str() % TRACE( 0, "%s, layer : %d", m_endItem->KindStr().c_str() %
m_endItem->GetLayers().Start() ); m_endItem->Layers().Start() );
} }
void ROUTER_TOOL::startRouting() void ROUTER_TOOL::performRouting()
{ {
PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
bool saveUndoBuffer = true; bool saveUndoBuffer = true;
VIEW_CONTROLS* ctls = getViewControls(); VIEW_CONTROLS* ctls = getViewControls();
int width = getDefaultWidth( m_startItem ? m_startItem->GetNet() : -1 ); if( getModel<BOARD>( PCB_T )->GetDesignSettings().m_UseConnectedTrackWidth )
{
int width = getDefaultWidth( m_startItem ? m_startItem->Net() : -1 );
if( m_startItem && m_startItem->OfKind( PNS_ITEM::SEGMENT ) )
width = static_cast<PNS_SEGMENT*>( m_startItem )->Width();
if( m_startItem && m_startItem->OfKind( PNS_ITEM::SEGMENT ) ) m_router->Settings().SetTrackWidth( width );
width = static_cast<PNS_SEGMENT*>( m_startItem )->GetWidth(); }
m_router->SetCurrentWidth( width );
m_router->SwitchLayer( m_startLayer ); m_router->SwitchLayer( m_startLayer );
getEditFrame<PCB_EDIT_FRAME>()->SetTopLayer( m_startLayer ); frame->SetActiveLayer( m_startLayer );
frame->GetGalCanvas()->SetFocus();
if( m_startItem && m_startItem->GetNet() >= 0 ) if( m_startItem && m_startItem->Net() >= 0 )
highlightNet( true, m_startItem->GetNet() ); highlightNet( true, m_startItem->Net() );
ctls->ForceCursorPosition( false ); ctls->ForceCursorPosition( false );
ctls->SetAutoPan( true ); ctls->SetAutoPan( true );
...@@ -366,43 +572,37 @@ void ROUTER_TOOL::startRouting() ...@@ -366,43 +572,37 @@ void ROUTER_TOOL::startRouting()
break; break;
m_router->Move( m_endSnapPoint, m_endItem ); m_router->Move( m_endSnapPoint, m_endItem );
} else if( evt->IsAction( &ACT_PlaceThroughVia ) )
{
m_router->ToggleViaPlacement();
frame->SetTopLayer( m_router->GetCurrentLayer() );
m_router->Move( m_endSnapPoint, m_endItem );
} }
else if( evt->IsKeyPressed() ) else if( evt->IsAction( &ACT_SwitchPosture ) )
{ {
switch( std::toupper( evt->KeyCode() ) ) m_router->FlipPosture();
{ m_router->Move( m_endSnapPoint, m_endItem );
case 'V': }
{ else if( evt->IsAction( &COMMON_ACTIONS::layerNext ) )
int w, diameter, drill; {
getNetclassDimensions( m_router->GetCurrentNet(), w, diameter, drill ); m_router->SwitchLayer( m_router->NextCopperLayer( true ) );
m_router->SetCurrentViaDiameter( diameter ); updateEndItem( *evt );
m_router->SetCurrentViaDrill( drill ); frame->SetActiveLayer( m_router->GetCurrentLayer() );
m_router->ToggleViaPlacement(); m_router->Move( m_endSnapPoint, m_endItem );
getEditFrame<PCB_EDIT_FRAME>()->SetTopLayer( m_router->GetCurrentLayer() ); }
m_router->Move( m_endSnapPoint, m_endItem ); else if( evt->IsAction( &COMMON_ACTIONS::layerPrev ) )
break; {
} m_router->SwitchLayer( m_router->NextCopperLayer( false ) );
frame->SetActiveLayer( m_router->GetCurrentLayer() );
case '/': m_router->Move( m_endSnapPoint, m_endItem );
m_router->FlipPosture(); }
break; else if( evt->IsAction( &ACT_EndTrack ) )
{
case '+': if( m_router->FixRoute( m_endSnapPoint, m_endItem ) )
case '=':
m_router->SwitchLayer( m_router->NextCopperLayer( true ) );
updateEndItem( *evt );
getEditFrame<PCB_EDIT_FRAME>()->SetTopLayer( m_router->GetCurrentLayer() );
m_router->Move( m_endSnapPoint, m_endItem );
break;
case '-':
m_router->SwitchLayer( m_router->NextCopperLayer( false ) );
getEditFrame<PCB_EDIT_FRAME>()->SetTopLayer( m_router->GetCurrentLayer() );
m_router->Move( m_endSnapPoint, m_endItem );
break; break;
}
} }
handleCommonEvents(*evt);
} }
m_router->StopRouting(); m_router->StopRouting();
...@@ -410,10 +610,9 @@ void ROUTER_TOOL::startRouting() ...@@ -410,10 +610,9 @@ void ROUTER_TOOL::startRouting()
if( saveUndoBuffer ) if( saveUndoBuffer )
{ {
// Save the recent changes in the undo buffer // Save the recent changes in the undo buffer
getEditFrame<PCB_EDIT_FRAME>()->SaveCopyInUndoList( m_router->GetLastChanges(), frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED );
UR_UNSPECIFIED ); m_router->ClearUndoBuffer();
m_router->ClearLastChanges(); frame->OnModify();
getEditFrame<PCB_EDIT_FRAME>()->OnModify();
} }
else else
{ {
...@@ -430,14 +629,23 @@ void ROUTER_TOOL::startRouting() ...@@ -430,14 +629,23 @@ void ROUTER_TOOL::startRouting()
int ROUTER_TOOL::Main( TOOL_EVENT& aEvent ) int ROUTER_TOOL::Main( TOOL_EVENT& aEvent )
{ {
VIEW_CONTROLS* ctls = getViewControls(); VIEW_CONTROLS* ctls = getViewControls();
BOARD* board = getModel<BOARD>( PCB_T );
BOARD_DESIGN_SETTINGS& bds = board->GetDesignSettings();
// SetContextMenu ( m_menu ); getEditFrame<PCB_EDIT_FRAME>()->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Interactive Router" ) );
// setMsgPanel(true, 0, wxT("KiRouter"), wxT("Pick an item to start routing"));
getEditFrame<PCB_EDIT_FRAME>()->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Add tracks" ) );
ctls->SetSnapping( true ); ctls->SetSnapping( true );
ctls->ShowCursor( true ); ctls->ShowCursor( true );
// Set current track widths & via size
m_router->Settings().SetTrackWidth( bds.GetCurrentTrackWidth() );
m_router->Settings().SetViaDiameter( bds.GetCurrentViaSize() );
m_router->Settings().SetViaDrill( bds.GetCurrentViaDrill() );
ROUTER_TOOL_MENU *ctxMenu = new ROUTER_TOOL_MENU( board );
SetContextMenu ( ctxMenu );
// Main loop: keep receiving events // Main loop: keep receiving events
while( OPT_TOOL_EVENT evt = Wait() ) while( OPT_TOOL_EVENT evt = Wait() )
{ {
...@@ -453,21 +661,69 @@ int ROUTER_TOOL::Main( TOOL_EVENT& aEvent ) ...@@ -453,21 +661,69 @@ int ROUTER_TOOL::Main( TOOL_EVENT& aEvent )
m_needsSync = true; m_needsSync = true;
else if( evt->IsMotion() ) else if( evt->IsMotion() )
updateStartItem( *evt ); updateStartItem( *evt );
else if( evt->IsClick( BUT_LEFT ) ) else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACT_NewTrack ) )
{ {
updateStartItem( *evt ); updateStartItem( *evt );
startRouting();
}
}
// clearMsgPanel(); if( evt->Modifier( MD_CTRL ) )
performDragging();
else
performRouting();
} else if ( evt->IsAction( &ACT_Drag ) )
performDragging();
handleCommonEvents(*evt);
}
// Restore the default settings // Restore the default settings
ctls->SetAutoPan( false ); ctls->SetAutoPan( false );
ctls->ShowCursor( false ); ctls->ShowCursor( false );
ctls->ForceCursorPosition( false );
getEditFrame<PCB_EDIT_FRAME>()->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString ); getEditFrame<PCB_EDIT_FRAME>()->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
delete ctxMenu;
return 0; return 0;
} }
void ROUTER_TOOL::performDragging()
{
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() )
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();
ctls->SetAutoPan( false );
ctls->ForceCursorPosition( false );
highlightNet( false );
}
/* /*
* KiRouter - a push-and-(sometimes-)shove PCB router * KiRouter - a push-and-(sometimes-)shove PCB router
* *
* Copyright (C) 2013 CERN * Copyright (C) 2013-2014 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch> * 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 * 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 * under the terms of the GNU General Public License as published by the
...@@ -15,7 +16,7 @@ ...@@ -15,7 +16,7 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __ROUTER_TOOL_H #ifndef __ROUTER_TOOL_H
...@@ -24,6 +25,8 @@ ...@@ -24,6 +25,8 @@
#include <set> #include <set>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include <import_export.h>
#include <math/vector2d.h> #include <math/vector2d.h>
#include <tool/tool_interactive.h> #include <tool/tool_interactive.h>
...@@ -35,7 +38,7 @@ ...@@ -35,7 +38,7 @@
class PNS_ROUTER; class PNS_ROUTER;
class PNS_ITEM; class PNS_ITEM;
class ROUTER_TOOL : public TOOL_INTERACTIVE class APIEXPORT ROUTER_TOOL : public TOOL_INTERACTIVE
{ {
public: public:
ROUTER_TOOL(); ROUTER_TOOL();
...@@ -48,12 +51,11 @@ private: ...@@ -48,12 +51,11 @@ private:
PNS_ITEM* pickSingleItem( const VECTOR2I& aWhere, int aNet = -1, int aLayer = -1 ); PNS_ITEM* pickSingleItem( const VECTOR2I& aWhere, int aNet = -1, int aLayer = -1 );
void setMsgPanel( bool enabled, int entry, const wxString& aUpperMessage = wxT(""),
const wxString& aLowerMessage = wxT("") );
void clearMsgPanel();
int getDefaultWidth( int aNetCode ); int getDefaultWidth( int aNetCode );
void startRouting();
void performRouting();
void performDragging();
void highlightNet( bool enabled, int netcode = -1 ); void highlightNet( bool enabled, int netcode = -1 );
void updateStartItem( TOOL_EVENT& aEvent ); void updateStartItem( TOOL_EVENT& aEvent );
...@@ -61,6 +63,8 @@ private: ...@@ -61,6 +63,8 @@ private:
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 );
MSG_PANEL_ITEMS m_panelItems; MSG_PANEL_ITEMS m_panelItems;
PNS_ROUTER* m_router; PNS_ROUTER* m_router;
...@@ -72,11 +76,11 @@ private: ...@@ -72,11 +76,11 @@ private:
PNS_ITEM* m_endItem; PNS_ITEM* m_endItem;
VECTOR2I m_endSnapPoint; VECTOR2I m_endSnapPoint;
///> Flag marking that the router's world needs syncing.
bool m_needsSync;
/*boost::shared_ptr<CONTEXT_MENU> m_menu;*/
CONTEXT_MENU* m_menu; CONTEXT_MENU* m_menu;
///> Flag marking that the router's world needs syncing.
bool m_needsSync;
}; };
#endif #endif
/*
* 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 <wx/timer.h>
#include "time_limit.h"
TIME_LIMIT::TIME_LIMIT( int aMilliseconds ) :
m_limitMs( aMilliseconds )
{
Restart();
};
TIME_LIMIT::~TIME_LIMIT () {}
bool TIME_LIMIT::Expired() const
{
return ( wxGetLocalTimeMillis().GetValue() - m_startTics ) >= m_limitMs;
}
void TIME_LIMIT::Restart()
{
m_startTics = wxGetLocalTimeMillis().GetValue();
}
void TIME_LIMIT::Set ( int aMilliseconds )
{
m_limitMs = aMilliseconds;
}
/*
* 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 __TIME_LIMIT_H
#define __TIME_LIMIT_H
#include <stdint.h>
class TIME_LIMIT {
public:
TIME_LIMIT( int aMilliseconds = 0);
~TIME_LIMIT ();
bool Expired() const;
void Restart();
void Set ( int aMilliseconds );
private:
int m_limitMs;
int64_t m_startTics;
};
#endif
\ No newline at end of file
...@@ -15,22 +15,21 @@ ...@@ -15,22 +15,21 @@
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along * You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>. * with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef __TRACE_H #ifndef __TRACE_H
#define __TRACE_H #define __TRACE_H
// #ifdef DEBUG
#if 0
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <boost/format.hpp> #include <boost/format.hpp>
static void _trace_print( const char* aFuncName, int level, const std::string& aMsg ) static inline void _trace_print( const char* aFuncName, int level, const std::string& aMsg )
{ {
#ifdef DEBUG
std::cerr << "trace[" << level << "]: " << aFuncName << ": " << aMsg << std::endl; std::cerr << "trace[" << level << "]: " << aFuncName << ": " << aMsg << std::endl;
#endif
} }
#define TRACE( level, fmt, ... ) \ #define TRACE( level, fmt, ... ) \
...@@ -39,11 +38,4 @@ static void _trace_print( const char* aFuncName, int level, const std::string& a ...@@ -39,11 +38,4 @@ static void _trace_print( const char* aFuncName, int level, const std::string& a
#define TRACEn( level, msg ) \ #define TRACEn( level, msg ) \
_trace_print( __FUNCTION__, level, std::string( msg ) ); _trace_print( __FUNCTION__, level, std::string( msg ) );
#else
#define TRACE( level, fmt, ... )
#define TRACEn( level, msg )
#endif
#endif #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment