summaryrefslogtreecommitdiff
path: root/pcbnew/router
diff options
context:
space:
mode:
Diffstat (limited to 'pcbnew/router')
-rw-r--r--pcbnew/router/CMakeLists.txt47
-rw-r--r--pcbnew/router/direction.h357
-rw-r--r--pcbnew/router/length_tuner_tool.cpp311
-rw-r--r--pcbnew/router/length_tuner_tool.h54
-rw-r--r--pcbnew/router/pns_algo_base.cpp32
-rw-r--r--pcbnew/router/pns_algo_base.h62
-rw-r--r--pcbnew/router/pns_diff_pair.cpp870
-rw-r--r--pcbnew/router/pns_diff_pair.h495
-rw-r--r--pcbnew/router/pns_diff_pair_placer.cpp859
-rw-r--r--pcbnew/router/pns_diff_pair_placer.h299
-rw-r--r--pcbnew/router/pns_dp_meander_placer.cpp406
-rw-r--r--pcbnew/router/pns_dp_meander_placer.h146
-rw-r--r--pcbnew/router/pns_dragger.cpp336
-rw-r--r--pcbnew/router/pns_dragger.h126
-rw-r--r--pcbnew/router/pns_index.h315
-rw-r--r--pcbnew/router/pns_item.cpp87
-rw-r--r--pcbnew/router/pns_item.h350
-rw-r--r--pcbnew/router/pns_itemset.cpp138
-rw-r--r--pcbnew/router/pns_itemset.h227
-rw-r--r--pcbnew/router/pns_joint.h259
-rw-r--r--pcbnew/router/pns_layerset.h129
-rw-r--r--pcbnew/router/pns_line.cpp889
-rw-r--r--pcbnew/router/pns_line.h299
-rw-r--r--pcbnew/router/pns_line_placer.cpp1112
-rw-r--r--pcbnew/router/pns_line_placer.h397
-rw-r--r--pcbnew/router/pns_logger.cpp203
-rw-r--r--pcbnew/router/pns_logger.h59
-rw-r--r--pcbnew/router/pns_meander.cpp617
-rw-r--r--pcbnew/router/pns_meander.h514
-rw-r--r--pcbnew/router/pns_meander_placer.cpp268
-rw-r--r--pcbnew/router/pns_meander_placer.h118
-rw-r--r--pcbnew/router/pns_meander_placer_base.cpp187
-rw-r--r--pcbnew/router/pns_meander_placer_base.h165
-rw-r--r--pcbnew/router/pns_meander_skew_placer.cpp174
-rw-r--r--pcbnew/router/pns_meander_skew_placer.h65
-rw-r--r--pcbnew/router/pns_node.cpp1288
-rw-r--r--pcbnew/router/pns_node.h487
-rw-r--r--pcbnew/router/pns_optimizer.cpp1224
-rw-r--r--pcbnew/router/pns_optimizer.h181
-rw-r--r--pcbnew/router/pns_placement_algo.h185
-rw-r--r--pcbnew/router/pns_router.cpp1084
-rw-r--r--pcbnew/router/pns_router.h285
-rw-r--r--pcbnew/router/pns_routing_settings.cpp105
-rw-r--r--pcbnew/router/pns_routing_settings.h158
-rw-r--r--pcbnew/router/pns_segment.h130
-rw-r--r--pcbnew/router/pns_shove.cpp1375
-rw-r--r--pcbnew/router/pns_shove.h159
-rw-r--r--pcbnew/router/pns_sizes_settings.cpp167
-rw-r--r--pcbnew/router/pns_sizes_settings.h113
-rw-r--r--pcbnew/router/pns_solid.cpp77
-rw-r--r--pcbnew/router/pns_solid.h107
-rw-r--r--pcbnew/router/pns_tool_base.cpp308
-rw-r--r--pcbnew/router/pns_tool_base.h82
-rw-r--r--pcbnew/router/pns_topology.cpp433
-rw-r--r--pcbnew/router/pns_topology.h72
-rw-r--r--pcbnew/router/pns_tune_status_popup.cpp67
-rw-r--r--pcbnew/router/pns_tune_status_popup.h44
-rw-r--r--pcbnew/router/pns_utils.cpp244
-rw-r--r--pcbnew/router/pns_utils.h62
-rw-r--r--pcbnew/router/pns_via.cpp109
-rw-r--r--pcbnew/router/pns_via.h162
-rw-r--r--pcbnew/router/pns_walkaround.cpp273
-rw-r--r--pcbnew/router/pns_walkaround.h149
-rw-r--r--pcbnew/router/range.h93
-rw-r--r--pcbnew/router/ranged_num.h52
-rw-r--r--pcbnew/router/router_preview_item.cpp343
-rw-r--r--pcbnew/router/router_preview_item.h127
-rw-r--r--pcbnew/router/router_tool.cpp854
-rw-r--r--pcbnew/router/router_tool.h63
-rw-r--r--pcbnew/router/time_limit.cpp51
-rw-r--r--pcbnew/router/time_limit.h43
-rw-r--r--pcbnew/router/trace.h46
72 files changed, 21774 insertions, 0 deletions
diff --git a/pcbnew/router/CMakeLists.txt b/pcbnew/router/CMakeLists.txt
new file mode 100644
index 0000000..2f324de
--- /dev/null
+++ b/pcbnew/router/CMakeLists.txt
@@ -0,0 +1,47 @@
+include_directories( BEFORE ${INC_BEFORE} )
+
+include_directories(
+ ./
+ ../
+ ../../include
+ ../../pcbnew
+ ../../polygon
+ ${INC_AFTER}
+)
+
+set( PCBNEW_PNS_SRCS
+ time_limit.cpp
+
+ pns_algo_base.cpp
+ pns_diff_pair.cpp
+ pns_diff_pair_placer.cpp
+ pns_dp_meander_placer.cpp
+ pns_dragger.cpp
+ pns_item.cpp
+ pns_itemset.cpp
+ pns_line.cpp
+ pns_line_placer.cpp
+ pns_logger.cpp
+ pns_meander.cpp
+ pns_meander_placer.cpp
+ pns_meander_placer_base.cpp
+ pns_meander_skew_placer.cpp
+ pns_node.cpp
+ pns_optimizer.cpp
+ pns_router.cpp
+ pns_routing_settings.cpp
+ pns_shove.cpp
+ pns_sizes_settings.cpp
+ pns_solid.cpp
+ pns_tool_base.cpp
+ pns_topology.cpp
+ pns_tune_status_popup.cpp
+ pns_utils.cpp
+ pns_via.cpp
+ pns_walkaround.cpp
+ router_preview_item.cpp
+ router_tool.cpp
+ length_tuner_tool.cpp
+)
+
+add_library( pnsrouter STATIC ${PCBNEW_PNS_SRCS} )
diff --git a/pcbnew/router/direction.h b/pcbnew/router/direction.h
new file mode 100644
index 0000000..402d4c9
--- /dev/null
+++ b/pcbnew/router/direction.h
@@ -0,0 +1,357 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.or/licenses/>.
+ */
+
+#ifndef __DIRECTION_H
+#define __DIRECTION_H
+
+#include <geometry/seg.h>
+#include <geometry/shape_line_chain.h>
+
+/**
+ * Class DIRECTION_45.
+ * Represents route directions & corner angles in a 45-degree metric.
+ */
+
+class DIRECTION_45
+{
+public:
+
+ /**
+ * Enum Directions
+ * Represents available directions - there are 8 of them, as on a rectilinear map (north = up) +
+ * an extra undefined direction, reserved for traces that don't respect 45-degree routing regime.
+ */
+ enum Directions
+ {
+ N = 0,
+ NE = 1,
+ E = 2,
+ SE = 3,
+ S = 4,
+ SW = 5,
+ W = 6,
+ NW = 7,
+ UNDEFINED = -1
+ };
+
+ /**
+ * Enum AngleType
+ * Represents kind of angle formed by vectors heading in two DIRECTION_45s.
+ */
+ enum AngleType
+ {
+ ANG_OBTUSE = 0x01,
+ ANG_RIGHT = 0x02,
+ ANG_ACUTE = 0x04,
+ ANG_STRAIGHT = 0x08,
+ ANG_HALF_FULL = 0x10,
+ ANG_UNDEFINED = 0x20
+ };
+
+ DIRECTION_45( Directions aDir = UNDEFINED ) : m_dir( aDir ) {}
+
+ /**
+ * Constructor
+ * @param aVec vector, whose direction will be translated into a DIRECTION_45.
+ */
+ DIRECTION_45( const VECTOR2I& aVec )
+ {
+ construct_( aVec );
+ }
+
+ /**
+ * Constructor
+ * @param aSeg segment, whose direction will be translated into a DIRECTION_45.
+ */
+ DIRECTION_45( const SEG& aSeg )
+ {
+ construct_( aSeg.B - aSeg.A );
+ }
+
+ /**
+ * Function Format()
+ * Formats the direction in a human readable word.
+ * @return name of the direction
+ */
+ const std::string Format() const
+ {
+ switch( m_dir )
+ {
+ case N:
+ return "north";
+
+ case NE:
+ return "north-east";
+
+ case E:
+ return "east";
+
+ case SE:
+ return "south-east";
+
+ case S:
+ return "south";
+
+ case SW:
+ return "south-west";
+
+ case W:
+ return "west";
+
+ case NW:
+ return "north-west";
+
+ case UNDEFINED:
+ return "undefined";
+
+ default:
+ return "<Error>";
+ }
+ }
+
+ /**
+ * Function Opposite()
+ * Returns a direction opposite (180 degree) to (this)
+ * @return opposite direction
+ */
+ DIRECTION_45 Opposite() const
+ {
+ const Directions OppositeMap[] = { S, SW, W, NW, N, NE, E, SE, UNDEFINED };
+ return OppositeMap[m_dir];
+ }
+
+ /**
+ * Function Angle()
+ * Returns the type of angle between directions (this) and aOther.
+ * @param aOther direction to compare angle with
+ */
+ AngleType Angle( const DIRECTION_45& aOther ) const
+ {
+ if( m_dir == UNDEFINED || aOther.m_dir == UNDEFINED )
+ return ANG_UNDEFINED;
+
+ int d = std::abs( m_dir - aOther.m_dir );
+
+ if( d == 1 || d == 7 )
+ return ANG_OBTUSE;
+ else if( d == 2 || d == 6 )
+ return ANG_RIGHT;
+ else if( d == 3 || d == 5 )
+ return ANG_ACUTE;
+ else if( d == 4 )
+ return ANG_HALF_FULL;
+ else
+ return ANG_STRAIGHT;
+ }
+
+ /**
+ * Function IsObtuse()
+ * @return true, when (this) forms an obtuse angle with aOther
+ */
+ bool IsObtuse( const DIRECTION_45& aOther ) const
+ {
+ return Angle( aOther ) == ANG_OBTUSE;
+ }
+
+ /**
+ * Function IsDiagonal()
+ * Returns true if the direction is diagonal (e.g. North-West, South-East, etc)
+ * @return true, when diagonal.
+ */
+ bool IsDiagonal() const
+ {
+ return ( m_dir % 2 ) == 1;
+ }
+
+ bool IsDefined() const
+ {
+ return m_dir != UNDEFINED;
+ }
+
+ /**
+ * Function BuildInitialTrace()
+ *
+ * Builds a 2-segment line chain between points aP0 and aP1 and following 45-degree routing
+ * regime. If aStartDiagonal is true, the trace starts with a diagonal segment.
+ * @param aP0 starting point
+ * @param aP1 ending point
+ * @param aStartDiagonal whether the first segment has to be diagonal
+ * @return the trace
+ */
+ const SHAPE_LINE_CHAIN BuildInitialTrace( const VECTOR2I& aP0,
+ const VECTOR2I& aP1,
+ bool aStartDiagonal = false ) const
+ {
+ int w = abs( aP1.x - aP0.x );
+ int h = abs( aP1.y - aP0.y );
+ int sw = sign( aP1.x - aP0.x );
+ int sh = sign( aP1.y - aP0.y );
+
+ VECTOR2I mp0, mp1;
+
+ // we are more horizontal than vertical?
+ if( w > h )
+ {
+ mp0 = VECTOR2I( ( w - h ) * sw, 0 ); // direction: E
+ mp1 = VECTOR2I( h * sw, h * sh ); // direction: NE
+ }
+ else
+ {
+ mp0 = VECTOR2I( 0, sh * ( h - w ) ); // direction: N
+ mp1 = VECTOR2I( sw * w, sh * w ); // direction: NE
+ }
+
+ bool start_diagonal;
+
+ if( m_dir == UNDEFINED )
+ start_diagonal = aStartDiagonal;
+ else
+ start_diagonal = IsDiagonal();
+
+ SHAPE_LINE_CHAIN pl;
+
+ pl.Append( aP0 );
+
+ if( start_diagonal )
+ pl.Append( aP0 + mp1 );
+ else
+ pl.Append( aP0 + mp0 );
+
+ pl.Append( aP1 );
+ pl.Simplify();
+ return pl;
+ }
+
+ bool operator==( const DIRECTION_45& aOther ) const
+ {
+ return aOther.m_dir == m_dir;
+ }
+
+ bool operator!=( const DIRECTION_45& aOther ) const
+ {
+ 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
+ {
+ DIRECTION_45 r;
+
+ if ( m_dir != UNDEFINED )
+ r.m_dir = static_cast<Directions>( ( m_dir + 1 ) % 8 );
+
+ return r;
+ }
+
+ /**
+ * 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;
+
+ if( m_dir == N )
+ 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
+ {
+ 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 );
+ }
+ }
+
+ int Mask() const
+ {
+ return 1 << ( (int) m_dir );
+ }
+
+private:
+
+ /**
+ * Function construct()
+ * Calculates the direction from a vector. If the vector's angle is not a multiple of 45
+ * degrees, the direction is rounded to the nearest octant.
+ * @param aVec our vector
+ */
+ void construct_( const VECTOR2I& aVec )
+ {
+ m_dir = UNDEFINED;
+
+ if( aVec.x == 0 && aVec.y == 0 )
+ return;
+
+ double mag = 360.0 - ( 180.0 / M_PI * atan2( (double) aVec.y, (double) aVec.x ) ) + 90.0;
+
+ if( mag >= 360.0 )
+ mag -= 360.0;
+
+ if( mag < 0.0 )
+ mag += 360.0;
+
+ int dir = ( mag + 22.5 ) / 45.0;
+
+ if( dir >= 8 )
+ dir = dir - 8;
+
+ if( dir < 0 )
+ dir = dir + 8;
+
+ m_dir = (Directions) dir;
+
+ return;
+ }
+
+ ///> our actual direction
+ Directions m_dir;
+};
+
+#endif // __DIRECTION_H
diff --git a/pcbnew/router/length_tuner_tool.cpp b/pcbnew/router/length_tuner_tool.cpp
new file mode 100644
index 0000000..7c49db9
--- /dev/null
+++ b/pcbnew/router/length_tuner_tool.cpp
@@ -0,0 +1,311 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/foreach.hpp>
+#include <boost/optional.hpp>
+
+#include "class_draw_panel_gal.h"
+#include "class_board.h"
+
+#include <wxPcbStruct.h>
+#include <pcbnew_id.h>
+#include <view/view_controls.h>
+#include <pcb_painter.h>
+#include <dialogs/dialog_pns_settings.h>
+#include <dialogs/dialog_pns_length_tuning_settings.h>
+
+#include <tool/context_menu.h>
+#include <tool/tool_manager.h>
+#include <tools/common_actions.h>
+
+#include "pns_segment.h"
+#include "pns_router.h"
+#include "pns_meander_placer.h" // fixme: move settings to separate header
+#include "pns_tune_status_popup.h"
+
+#include "length_tuner_tool.h"
+
+#include "trace.h"
+
+using namespace KIGFX;
+using boost::optional;
+
+static TOOL_ACTION ACT_StartTuning( "pcbnew.LengthTuner.StartTuning", AS_CONTEXT, 'X',
+ _( "New Track" ), _( "Starts laying a new track." ) );
+
+static TOOL_ACTION ACT_EndTuning( "pcbnew.LengthTuner.EndTuning", AS_CONTEXT, WXK_END,
+ _( "End Track" ), _( "Stops laying the current meander." ) );
+
+static TOOL_ACTION ACT_Settings( "pcbnew.LengthTuner.Settings", AS_CONTEXT, 'L',
+ _( "Length Tuning Settings" ), _( "Sets the length tuning parameters for currently routed item." ) );
+
+static TOOL_ACTION ACT_SpacingIncrease( "pcbnew.LengthTuner.SpacingIncrease", AS_CONTEXT, '1',
+ _( "Increase spacing" ), _( "Increase meander spacing by one step." ) );
+
+static TOOL_ACTION ACT_SpacingDecrease( "pcbnew.LengthTuner.SpacingDecrease", AS_CONTEXT, '2',
+ _( "Decrease spacing" ), _( "Decrease meander spacing by one step." ) );
+
+static TOOL_ACTION ACT_AmplIncrease( "pcbnew.LengthTuner.AmplIncrease", AS_CONTEXT, '3',
+ _( "Increase amplitude" ), _( "Increase meander amplitude by one step." ) );
+
+static TOOL_ACTION ACT_AmplDecrease( "pcbnew.LengthTuner.AmplDecrease", AS_CONTEXT, '4',
+ _( "Decrease amplitude" ), _( "Decrease meander amplitude by one step." ) );
+
+
+LENGTH_TUNER_TOOL::LENGTH_TUNER_TOOL() :
+ PNS_TOOL_BASE( "pcbnew.LengthTuner" )
+{
+}
+
+
+class TUNER_TOOL_MENU: public CONTEXT_MENU
+{
+public:
+ TUNER_TOOL_MENU( BOARD* aBoard )
+ {
+ SetTitle( _( "Length Tuner" ) );
+
+ //Add( ACT_StartTuning );
+ //Add( ACT_EndTuning );
+
+ //AppendSeparator();
+
+ Add( ACT_SpacingIncrease );
+ Add( ACT_SpacingDecrease );
+ Add( ACT_AmplIncrease );
+ Add( ACT_AmplDecrease );
+ Add( ACT_Settings );
+ }
+};
+
+
+LENGTH_TUNER_TOOL::~LENGTH_TUNER_TOOL()
+{
+}
+
+
+void LENGTH_TUNER_TOOL::Reset( RESET_REASON aReason )
+{
+ PNS_TOOL_BASE::Reset( aReason );
+
+ Go( &LENGTH_TUNER_TOOL::TuneSingleTrace, COMMON_ACTIONS::routerActivateTuneSingleTrace.MakeEvent() );
+ Go( &LENGTH_TUNER_TOOL::TuneDiffPair, COMMON_ACTIONS::routerActivateTuneDiffPair.MakeEvent() );
+ Go( &LENGTH_TUNER_TOOL::TuneDiffPairSkew, COMMON_ACTIONS::routerActivateTuneDiffPairSkew.MakeEvent() );
+}
+
+
+void LENGTH_TUNER_TOOL::handleCommonEvents( const TOOL_EVENT& aEvent )
+{
+ if( aEvent.IsAction( &ACT_RouterOptions ) )
+ {
+ DIALOG_PNS_SETTINGS settingsDlg( m_frame, m_router->Settings() );
+
+ if( settingsDlg.ShowModal() == wxID_OK )
+ {
+ // FIXME: do we need an explicit update?
+ }
+ }
+
+ PNS_MEANDER_PLACER_BASE* placer = static_cast<PNS_MEANDER_PLACER_BASE*>( m_router->Placer() );
+
+ if( !placer )
+ return;
+
+ if( aEvent.IsAction( &ACT_Settings ) )
+ {
+ PNS_MEANDER_SETTINGS settings = placer->MeanderSettings();
+ DIALOG_PNS_LENGTH_TUNING_SETTINGS settingsDlg( m_frame, settings, m_router->Mode() );
+
+ if( settingsDlg.ShowModal() )
+ {
+ placer->UpdateSettings( settings );
+ }
+
+ m_savedMeanderSettings = placer->MeanderSettings();
+ }
+}
+
+void LENGTH_TUNER_TOOL::updateStatusPopup( PNS_TUNE_STATUS_POPUP& aPopup )
+{
+ wxPoint p = wxGetMousePosition();
+
+ p.x += 20;
+ p.y += 20;
+
+ aPopup.UpdateStatus( m_router );
+ aPopup.Move( p );
+}
+
+void LENGTH_TUNER_TOOL::performTuning()
+{
+ if( m_startItem )
+ {
+ m_frame->SetActiveLayer( ToLAYER_ID ( m_startItem->Layers().Start() ) );
+
+ if( m_startItem->Net() >= 0 )
+ highlightNet( true, m_startItem->Net() );
+ }
+
+ m_ctls->ForceCursorPosition( false );
+ m_ctls->SetAutoPan( true );
+
+ if( !m_router->StartRouting( m_startSnapPoint, m_startItem, 0 ) )
+ {
+ wxMessageBox( m_router->FailureReason(), _( "Error" ) );
+ highlightNet( false );
+ return;
+ }
+
+ PNS_MEANDER_PLACER_BASE* placer = static_cast<PNS_MEANDER_PLACER_BASE*>( m_router->Placer() );
+
+ placer->UpdateSettings( m_savedMeanderSettings );
+
+ VECTOR2I end( m_startSnapPoint );
+
+ PNS_TUNE_STATUS_POPUP statusPopup( m_frame );
+ statusPopup.Popup();
+
+ m_router->Move( end, NULL );
+ updateStatusPopup( statusPopup );
+
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ if( evt->IsCancel() || evt->IsActivate() )
+ break;
+ else if( evt->IsMotion() )
+ {
+ end = evt->Position();
+ m_router->Move( end, NULL );
+ updateStatusPopup( statusPopup );
+ }
+ else if( evt->IsClick( BUT_LEFT ) )
+ {
+ if( m_router->FixRoute( evt->Position(), NULL ) )
+ break;
+ }
+ else if( evt->IsAction( &ACT_EndTuning ) )
+ {
+ if( m_router->FixRoute( end, NULL ) )
+ break;
+ }
+ else if( evt->IsAction( &ACT_AmplDecrease ) )
+ {
+ placer->AmplitudeStep( -1 );
+ m_router->Move( end, NULL );
+ }
+ else if( evt->IsAction( &ACT_AmplIncrease ) )
+ {
+ placer->AmplitudeStep( 1 );
+ m_router->Move( end, NULL );
+ }
+ else if(evt->IsAction( &ACT_SpacingDecrease ) )
+ {
+ placer->SpacingStep( -1 );
+ m_router->Move( end, NULL );
+ }
+ else if( evt->IsAction( &ACT_SpacingIncrease ) )
+ {
+ placer->SpacingStep( 1 );
+ m_router->Move( end, NULL );
+ }
+
+ handleCommonEvents( *evt );
+ }
+
+ m_router->StopRouting();
+
+ // Save the recent changes in the undo buffer
+ m_frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED );
+ m_router->ClearUndoBuffer();
+ m_frame->OnModify();
+
+ highlightNet( false );
+}
+
+
+int LENGTH_TUNER_TOOL::TuneSingleTrace( const TOOL_EVENT& aEvent )
+{
+ m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Tune Trace Length" ) );
+ return mainLoop( PNS_MODE_TUNE_SINGLE );
+}
+
+
+int LENGTH_TUNER_TOOL::TuneDiffPair( const TOOL_EVENT& aEvent )
+{
+ m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Tune Diff Pair Length" ) );
+ return mainLoop( PNS_MODE_TUNE_DIFF_PAIR );
+}
+
+
+int LENGTH_TUNER_TOOL::TuneDiffPairSkew( const TOOL_EVENT& aEvent )
+{
+ m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Tune Diff Pair Skew" ) );
+ return mainLoop( PNS_MODE_TUNE_DIFF_PAIR_SKEW );
+}
+
+
+int LENGTH_TUNER_TOOL::mainLoop( PNS_ROUTER_MODE aMode )
+{
+ // Deselect all items
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+
+ Activate();
+
+ m_router->SetMode( aMode );
+
+ m_ctls->SetSnapping( true );
+ m_ctls->ShowCursor( true );
+ m_frame->UndoRedoBlock( true );
+
+ std::auto_ptr<TUNER_TOOL_MENU> ctxMenu( new TUNER_TOOL_MENU( m_board ) );
+ SetContextMenu( ctxMenu.get() );
+
+ // Main loop: keep receiving events
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ if( m_needsSync )
+ {
+ m_router->SyncWorld();
+ m_router->SetView( getView() );
+ m_needsSync = false;
+ }
+
+ if( evt->IsCancel() || evt->IsActivate() )
+ break; // Finish
+ else if( evt->IsMotion() )
+ updateStartItem( *evt );
+ else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACT_StartTuning ) )
+ {
+ updateStartItem( *evt );
+ performTuning();
+ }
+
+ handleCommonEvents( *evt );
+ }
+
+ m_frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+ m_frame->UndoRedoBlock( false );
+
+ // Store routing settings till the next invocation
+ m_savedSettings = m_router->Settings();
+ m_savedSizes = m_router->Sizes();
+
+ return 0;
+}
diff --git a/pcbnew/router/length_tuner_tool.h b/pcbnew/router/length_tuner_tool.h
new file mode 100644
index 0000000..075d907
--- /dev/null
+++ b/pcbnew/router/length_tuner_tool.h
@@ -0,0 +1,54 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ * Author: Maciej Suminski <maciej.suminski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __LENGTH_TUNER_TOOL_H
+#define __LENGTH_TUNER_TOOL_H
+
+#include "pns_tool_base.h"
+#include "pns_meander.h"
+
+class PNS_TUNE_STATUS_POPUP;
+
+class APIEXPORT LENGTH_TUNER_TOOL : public PNS_TOOL_BASE
+{
+public:
+ LENGTH_TUNER_TOOL();
+ ~LENGTH_TUNER_TOOL();
+
+ void Reset( RESET_REASON aReason );
+
+ int TuneSingleTrace( const TOOL_EVENT& aEvent );
+ int TuneDiffPair( const TOOL_EVENT& aEvent );
+ int TuneDiffPairSkew( const TOOL_EVENT& aEvent );
+ int ClearMeanders( const TOOL_EVENT& aEvent );
+
+private:
+ void performTuning( );
+ int mainLoop( PNS_ROUTER_MODE aMode );
+ void handleCommonEvents( const TOOL_EVENT& aEvent );
+ void updateStatusPopup ( PNS_TUNE_STATUS_POPUP& aPopup );
+
+
+
+ PNS_MEANDER_SETTINGS m_savedMeanderSettings;
+};
+
+#endif
diff --git a/pcbnew/router/pns_algo_base.cpp b/pcbnew/router/pns_algo_base.cpp
new file mode 100644
index 0000000..b72243b
--- /dev/null
+++ b/pcbnew/router/pns_algo_base.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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;
+}
diff --git a/pcbnew/router/pns_algo_base.h b/pcbnew/router/pns_algo_base.h
new file mode 100644
index 0000000..a6429ea
--- /dev/null
+++ b/pcbnew/router/pns_algo_base.h
@@ -0,0 +1,62 @@
+/*
+ * 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 <wx/wx.h> // for wxString
+
+#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
diff --git a/pcbnew/router/pns_diff_pair.cpp b/pcbnew/router/pns_diff_pair.cpp
new file mode 100644
index 0000000..771928b
--- /dev/null
+++ b/pcbnew/router/pns_diff_pair.cpp
@@ -0,0 +1,870 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/foreach.hpp>
+
+#include <cstdio>
+#include <cstdlib>
+#include <limits>
+
+#include <geometry/shape.h>
+#include <geometry/shape_rect.h>
+#include <geometry/shape_circle.h>
+#include <geometry/shape_segment.h>
+
+#include "direction.h"
+
+#include "pns_diff_pair.h"
+#include "pns_router.h"
+#include "pns_solid.h"
+#include "pns_utils.h"
+
+
+class PNS_LINE;
+
+PNS_DP_PRIMITIVE_PAIR::PNS_DP_PRIMITIVE_PAIR( PNS_ITEM* aPrimP, PNS_ITEM* aPrimN )
+{
+ m_primP = aPrimP->Clone();
+ m_primN = aPrimN->Clone();
+
+ m_anchorP = m_primP->Anchor( 0 );
+ m_anchorN = m_primN->Anchor( 0 );
+}
+
+
+void PNS_DP_PRIMITIVE_PAIR::SetAnchors( const VECTOR2I& aAnchorP, const VECTOR2I& aAnchorN )
+{
+ m_anchorP = aAnchorP;
+ m_anchorN = aAnchorN;
+}
+
+
+PNS_DP_PRIMITIVE_PAIR::PNS_DP_PRIMITIVE_PAIR( const VECTOR2I& aAnchorP, const VECTOR2I& aAnchorN )
+{
+ m_anchorP = aAnchorP;
+ m_anchorN = aAnchorN;
+ m_primP = m_primN = NULL;
+}
+
+
+PNS_DP_PRIMITIVE_PAIR::PNS_DP_PRIMITIVE_PAIR( const PNS_DP_PRIMITIVE_PAIR& aOther )
+{
+ m_primP = m_primN = NULL;
+
+ if( aOther.m_primP )
+ m_primP = aOther.m_primP->Clone();
+
+ if( aOther.m_primN )
+ m_primN = aOther.m_primN->Clone();
+
+ m_anchorP = aOther.m_anchorP;
+ m_anchorN = aOther.m_anchorN;
+}
+
+
+PNS_DP_PRIMITIVE_PAIR& PNS_DP_PRIMITIVE_PAIR::operator=( const PNS_DP_PRIMITIVE_PAIR& aOther )
+{
+ if( aOther.m_primP )
+ m_primP = aOther.m_primP->Clone();
+ if( aOther.m_primN )
+ m_primN = aOther.m_primN->Clone();
+
+ m_anchorP = aOther.m_anchorP;
+ m_anchorN = aOther.m_anchorN;
+
+ return *this;
+}
+
+
+PNS_DP_PRIMITIVE_PAIR::~PNS_DP_PRIMITIVE_PAIR()
+{
+ delete m_primP;
+ delete m_primN;
+}
+
+
+bool PNS_DP_PRIMITIVE_PAIR::Directional() const
+{
+ if( !m_primP )
+ return false;
+
+ return m_primP->OfKind( PNS_ITEM::SEGMENT );
+}
+
+
+DIRECTION_45 PNS_DP_PRIMITIVE_PAIR::anchorDirection( PNS_ITEM* aItem, const VECTOR2I& aP ) const
+{
+ if( !aItem->OfKind ( PNS_ITEM::SEGMENT ) )
+ return DIRECTION_45();
+
+ PNS_SEGMENT* s = static_cast<PNS_SEGMENT*>( aItem );
+
+ if( s->Seg().A == aP )
+ return DIRECTION_45( s->Seg().A - s->Seg().B );
+ else
+ return DIRECTION_45( s->Seg().B - s->Seg().A );
+}
+
+
+DIRECTION_45 PNS_DP_PRIMITIVE_PAIR::DirP() const
+{
+ return anchorDirection( m_primP, m_anchorP );
+}
+
+
+DIRECTION_45 PNS_DP_PRIMITIVE_PAIR::DirN() const
+{
+ return anchorDirection( m_primN, m_anchorN );
+}
+
+
+static void drawGw( VECTOR2I p, int color )
+{
+ SHAPE_LINE_CHAIN l;
+
+ l.Append( p - VECTOR2I( -50000, -50000 ) );
+ l.Append( p + VECTOR2I( -50000, -50000 ) );
+
+ l.Clear();
+ l.Append( p - VECTOR2I( 50000, -50000 ) );
+ l.Append( p + VECTOR2I( 50000, -50000 ) );
+}
+
+
+static DIRECTION_45::AngleType angle( const VECTOR2I &a, const VECTOR2I &b )
+{
+ DIRECTION_45 dir_a( a );
+ DIRECTION_45 dir_b( b );
+
+ return dir_a.Angle( dir_b );
+}
+
+
+static bool checkGap( const SHAPE_LINE_CHAIN &p, const SHAPE_LINE_CHAIN &n, int gap )
+{
+ int i, j;
+
+ for( i = 0; i < p.SegmentCount(); i++ )
+ {
+ for( j = 0; j < n.SegmentCount() ; j++ )
+ {
+ int dist = p.CSegment( i ).Distance( n.CSegment( j ) );
+
+ if( dist < gap - 100 )
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+void PNS_DP_GATEWAY::Reverse()
+{
+ m_entryN = m_entryN.Reverse();
+ m_entryP = m_entryP.Reverse();
+}
+
+
+bool PNS_DIFF_PAIR::BuildInitial( PNS_DP_GATEWAY& aEntry, PNS_DP_GATEWAY &aTarget, bool aPrefDiagonal )
+{
+ SHAPE_LINE_CHAIN p = DIRECTION_45().BuildInitialTrace ( aEntry.AnchorP(), aTarget.AnchorP(), aPrefDiagonal );
+ SHAPE_LINE_CHAIN n = DIRECTION_45().BuildInitialTrace ( aEntry.AnchorN(), aTarget.AnchorN(), aPrefDiagonal );
+
+ int mask = aEntry.AllowedAngles() | DIRECTION_45::ANG_STRAIGHT | DIRECTION_45::ANG_OBTUSE;
+
+ SHAPE_LINE_CHAIN sum_n, sum_p;
+ m_p = p;
+ m_n = n;
+
+ if( aEntry.HasEntryLines() )
+ {
+ if( !aEntry.Entry().CheckConnectionAngle( *this, mask ) )
+ return false;
+
+ sum_p = aEntry.Entry().CP();
+ sum_n = aEntry.Entry().CN();
+ sum_p.Append( p );
+ sum_n.Append( n );
+ }
+ else
+ {
+ sum_p = p;
+ sum_n = n;
+ }
+
+ mask = aTarget.AllowedAngles() | DIRECTION_45::ANG_STRAIGHT | DIRECTION_45::ANG_OBTUSE;
+
+ m_p = sum_p;
+ m_n = sum_n;
+
+ if( aTarget.HasEntryLines() )
+ {
+ PNS_DP_GATEWAY t(aTarget) ;
+ t.Reverse();
+
+ if( !CheckConnectionAngle( t.Entry(), mask ) )
+ return false;
+
+ sum_p.Append( t.Entry().CP() );
+ sum_n.Append( t.Entry().CN() );
+ }
+
+ m_p = sum_p;
+ m_n = sum_n;
+
+ if( !checkGap ( p, n, m_gapConstraint ) )
+ return false;
+
+ if( p.SelfIntersecting() || n.SelfIntersecting() )
+ return false;
+
+ if( p.Intersects( n ) )
+ return false;
+
+ return true;
+}
+
+
+bool PNS_DIFF_PAIR::CheckConnectionAngle( const PNS_DIFF_PAIR& aOther, int aAllowedAngles ) const
+{
+ bool checkP, checkN;
+
+ if( m_p.SegmentCount() == 0 || aOther.m_p.SegmentCount() == 0 )
+ checkP = true;
+ else
+ {
+ DIRECTION_45 p0( m_p.CSegment( -1 ) );
+ DIRECTION_45 p1( aOther.m_p.CSegment( 0 ) );
+
+ checkP = ( p0.Angle( p1 ) & aAllowedAngles ) != 0;
+ }
+
+ if( m_n.SegmentCount() == 0 || aOther.m_n.SegmentCount() == 0 )
+ checkN = true;
+ else
+ {
+ DIRECTION_45 n0( m_n.CSegment( -1 ) );
+ DIRECTION_45 n1( aOther.m_n.CSegment( 0 ) );
+
+ checkN = ( n0.Angle( n1 ) & aAllowedAngles ) != 0;
+ }
+
+ return checkP && checkN;
+}
+
+
+const PNS_DIFF_PAIR PNS_DP_GATEWAY::Entry() const
+{
+ return PNS_DIFF_PAIR( m_entryP, m_entryN, 0 );
+}
+
+
+void PNS_DP_GATEWAYS::BuildOrthoProjections( PNS_DP_GATEWAYS& aEntries,
+ const VECTOR2I& aCursorPos, int aOrthoScore )
+{
+ BOOST_FOREACH( PNS_DP_GATEWAY g, aEntries.Gateways() )
+ {
+ VECTOR2I midpoint( ( g.AnchorP() + g.AnchorN() ) / 2 );
+ SEG guide_s( midpoint, midpoint + VECTOR2I( 1, 0 ) );
+ SEG guide_d( midpoint, midpoint + VECTOR2I( 1, 1 ) );
+
+ VECTOR2I proj_s = guide_s.LineProject( aCursorPos );
+ VECTOR2I proj_d = guide_d.LineProject( aCursorPos );
+
+ int dist_s = ( proj_s - aCursorPos ).EuclideanNorm();
+ int dist_d = ( proj_d - aCursorPos ).EuclideanNorm();
+
+
+ VECTOR2I proj = ( dist_s < dist_d ? proj_s : proj_d );
+
+ PNS_DP_GATEWAYS targets( m_gap );
+
+ targets.m_viaGap = m_viaGap;
+ targets.m_viaDiameter = m_viaDiameter;
+ targets.m_fitVias = m_fitVias;
+
+ targets.BuildForCursor( proj );
+
+ BOOST_FOREACH( PNS_DP_GATEWAY t, targets.Gateways() )
+ {
+ t.SetPriority( aOrthoScore );
+ m_gateways.push_back( t );
+ }
+ }
+}
+
+
+bool PNS_DP_GATEWAYS::FitGateways( PNS_DP_GATEWAYS& aEntry, PNS_DP_GATEWAYS& aTarget,
+ bool aPrefDiagonal, PNS_DIFF_PAIR& aDp )
+{
+ std::vector<DP_CANDIDATE> candidates;
+
+ BOOST_FOREACH( PNS_DP_GATEWAY g_entry, aEntry.Gateways() )
+ {
+ BOOST_FOREACH( PNS_DP_GATEWAY g_target, aTarget.Gateways() )
+ {
+ for( int attempt = 0; attempt < 2; attempt++ )
+ {
+ PNS_DIFF_PAIR l( m_gap );
+
+ if( l.BuildInitial( g_entry, g_target, aPrefDiagonal ^ ( attempt ? true : false ) ) )
+ {
+ int score = ( attempt == 1 ? -3 : 0 );
+ score +=g_entry.Priority();
+ score +=g_target.Priority();
+
+ DP_CANDIDATE c;
+ c.score = score;
+ c.p = l.CP();
+ c.n = l.CN();
+ candidates.push_back( c );
+ }
+ }
+ }
+ }
+
+ int bestScore = -1000;
+ DP_CANDIDATE best;
+ bool found = false;
+
+ BOOST_FOREACH( DP_CANDIDATE c, candidates )
+ {
+ if( c.score > bestScore )
+ {
+ bestScore = c.score;
+ best = c;
+ found = true;
+ }
+ }
+
+ if( found )
+ {
+ aDp.SetGap( m_gap );
+ aDp.SetShape( best.p, best.n );
+ return true;
+ }
+
+ return false;
+}
+
+
+bool PNS_DP_GATEWAYS::checkDiagonalAlignment( const VECTOR2I& a, const VECTOR2I& b ) const
+{
+ VECTOR2I dir ( std::abs (a.x - b.x), std::abs ( a.y - b.y ));
+
+ return (dir.x == 0 && dir.y != 0) || (dir.x == dir.y) || (dir.y == 0 && dir.x != 0);
+}
+
+
+void PNS_DP_GATEWAYS::BuildFromPrimitivePair( PNS_DP_PRIMITIVE_PAIR aPair, bool aPreferDiagonal )
+{
+ VECTOR2I majorDirection;
+ VECTOR2I p0_p, p0_n;
+ int orthoFanDistance;
+ int diagFanDistance;
+ const SHAPE* shP = NULL;
+
+ if( aPair.PrimP() == NULL )
+ {
+ BuildGeneric( aPair.AnchorP(), aPair.AnchorN(), true );
+ return;
+ }
+
+ const int pvMask = PNS_ITEM::SOLID | PNS_ITEM::VIA;
+
+ if( aPair.PrimP()->OfKind( pvMask ) && aPair.PrimN()->OfKind( pvMask ) )
+ {
+ p0_p = aPair.AnchorP();
+ p0_n = aPair.AnchorN();
+
+ shP = aPair.PrimP()->Shape();
+ }
+ else if( aPair.PrimP()->OfKind( PNS_ITEM::SEGMENT ) && aPair.PrimN()->OfKind( PNS_ITEM::SEGMENT ) )
+ {
+ buildDpContinuation( aPair, aPreferDiagonal );
+
+ return;
+ }
+
+ majorDirection = ( p0_p - p0_n ).Perpendicular();
+
+ if( shP == NULL )
+ return;
+
+ switch( shP->Type() )
+ {
+ case SH_RECT:
+ {
+ int w = static_cast<const SHAPE_RECT*>( shP )->GetWidth();
+ int h = static_cast<const SHAPE_RECT*>( shP )->GetHeight();
+
+ if( w < h )
+ std::swap( w, h );
+
+ orthoFanDistance = w * 3/4;
+ diagFanDistance = ( w - h ) / 2;
+ break;
+ }
+
+ case SH_SEGMENT:
+ {
+ int w = static_cast<const SHAPE_SEGMENT*>( shP )->GetWidth();
+ SEG s = static_cast<const SHAPE_SEGMENT*>( shP )->GetSeg();
+
+ orthoFanDistance = w + ( s.B - s.A ).EuclideanNorm() / 2;
+ diagFanDistance = ( s.B - s.A ).EuclideanNorm() / 2;
+ break;
+ }
+
+ default:
+ BuildGeneric ( p0_p, p0_n, true );
+ return;
+ }
+
+ if( checkDiagonalAlignment( p0_p, p0_n ) )
+ {
+ int padDist = ( p0_p - p0_n ).EuclideanNorm();
+
+ for( int k = 0; k < 2; k++ )
+ {
+ VECTOR2I dir, dp, dv;
+
+ if( k == 0 )
+ {
+ dir = majorDirection.Resize( orthoFanDistance );
+ int d = ( padDist - m_gap ) / 2;
+
+ dp = dir.Resize( d );
+ dv = ( p0_n - p0_p ).Resize( d );
+ }
+ else
+ {
+ dir = majorDirection.Resize( diagFanDistance );
+ int d = ( padDist - m_gap ) / 2;
+ dp = dir.Resize( d );
+ dv = ( p0_n - p0_p ).Resize( d );
+ }
+
+ for( int i = 0; i < 2; i++ )
+ {
+ int sign = i ? -1 : 1;
+
+ VECTOR2I gw_p( p0_p + sign * ( dir + dp ) + dv );
+ VECTOR2I gw_n( p0_n + sign * ( dir + dp ) - dv );
+
+ SHAPE_LINE_CHAIN entryP( p0_p, p0_p + sign * dir, gw_p );
+ SHAPE_LINE_CHAIN entryN( p0_n, p0_n + sign * dir, gw_n );
+
+ PNS_DP_GATEWAY gw( gw_p, gw_n, false );
+
+ gw.SetEntryLines( entryP, entryN );
+ gw.SetPriority( 100 - k );
+ m_gateways.push_back( gw );
+ }
+ }
+ }
+
+ BuildGeneric( p0_p, p0_n, true );
+}
+
+
+void PNS_DP_GATEWAYS::BuildForCursor( const VECTOR2I& aCursorPos )
+{
+ int gap = m_fitVias ? m_viaGap + m_viaDiameter : m_gap;
+
+ for( int attempt = 0; attempt < 2; attempt++ )
+ {
+ for( int i = 0; i < 4; i++ )
+ {
+ VECTOR2I dir;
+
+ if( !attempt )
+ {
+ dir = VECTOR2I( gap, gap ).Resize( gap / 2 );
+
+ if( i % 2 == 0 )
+ dir.x = -dir.x;
+
+ if( i / 2 == 0 )
+ dir.y = -dir.y;
+ }
+ else
+ {
+ if( i /2 == 0 )
+ dir = VECTOR2I( gap / 2 * ( ( i % 2 ) ? -1 : 1 ), 0 );
+ else
+ dir = VECTOR2I( 0, gap / 2 * ( ( i % 2 ) ? -1 : 1) );
+ }
+
+ if( m_fitVias )
+ BuildGeneric( aCursorPos + dir, aCursorPos - dir, true, true );
+ else
+ m_gateways.push_back( PNS_DP_GATEWAY( aCursorPos + dir,
+ aCursorPos - dir, attempt ? true : false ) );
+
+ drawGw ( aCursorPos + dir, 2 );
+ drawGw ( aCursorPos - dir, 3 );
+ }
+ }
+}
+
+
+void PNS_DP_GATEWAYS::buildEntries( const VECTOR2I& p0_p, const VECTOR2I& p0_n )
+{
+ BOOST_FOREACH( PNS_DP_GATEWAY &g, m_gateways )
+ {
+ if( !g.HasEntryLines() )
+ {
+ SHAPE_LINE_CHAIN lead_p = DIRECTION_45().BuildInitialTrace ( g.AnchorP(), p0_p, g.IsDiagonal() ).Reverse();
+ SHAPE_LINE_CHAIN lead_n = DIRECTION_45().BuildInitialTrace ( g.AnchorN(), p0_n, g.IsDiagonal() ).Reverse();
+ g.SetEntryLines( lead_p, lead_n );
+ }
+ }
+}
+
+
+void PNS_DP_GATEWAYS::buildDpContinuation( PNS_DP_PRIMITIVE_PAIR aPair, bool aIsDiagonal )
+{
+ PNS_DP_GATEWAY gw( aPair.AnchorP(), aPair.AnchorN(), aIsDiagonal );
+ gw.SetPriority( 100 );
+ m_gateways.push_back( gw );
+
+ if( !aPair.Directional() )
+ return;
+
+ DIRECTION_45 dP = aPair.DirP();
+ DIRECTION_45 dN = aPair.DirN();
+
+ int gap = ( aPair.AnchorP() - aPair.AnchorN() ).EuclideanNorm();
+
+ VECTOR2I vdP = aPair.AnchorP() + dP.Left().ToVector();
+ VECTOR2I vdN = aPair.AnchorN() + dN.Left().ToVector();
+
+ PNS_SEGMENT* sP = static_cast<PNS_SEGMENT*>( aPair.PrimP() );
+
+ VECTOR2I t1, t2;
+
+ if( sP->Seg().Side( vdP ) == sP->Seg().Side( vdN ) )
+ {
+ t1 = aPair.AnchorP() + dP.Left().ToVector().Resize( gap );
+ t2 = aPair.AnchorN() + dP.Right().ToVector().Resize( gap );
+ }
+ else
+ {
+ t1 = aPair.AnchorP() + dP.Right().ToVector().Resize( gap );
+ t2 = aPair.AnchorN() + dP.Left().ToVector().Resize( gap );
+ }
+
+ PNS_DP_GATEWAY gwL( t2, aPair.AnchorN(), !aIsDiagonal );
+ SHAPE_LINE_CHAIN ep = dP.BuildInitialTrace( aPair.AnchorP(), t2, !aIsDiagonal );
+
+ gwL.SetPriority( 10 );
+ gwL.SetEntryLines( ep , SHAPE_LINE_CHAIN() );
+
+ m_gateways.push_back( gwL );
+
+ PNS_DP_GATEWAY gwR( aPair.AnchorP(), t1, !aIsDiagonal );
+ SHAPE_LINE_CHAIN en = dP.BuildInitialTrace( aPair.AnchorN(), t1, !aIsDiagonal );
+ gwR.SetPriority( 10) ;
+ gwR.SetEntryLines( SHAPE_LINE_CHAIN(), en );
+
+ m_gateways.push_back( gwR );
+}
+
+
+void PNS_DP_GATEWAYS::BuildGeneric( const VECTOR2I& p0_p, const VECTOR2I& p0_n, bool aBuildEntries, bool aViaMode )
+{
+ SEG st_p[2], st_n[2];
+ SEG d_n[2], d_p[2];
+
+ const int padToGapThreshold = 3;
+ int padDist = ( p0_p - p0_p ).EuclideanNorm();
+
+ st_p[0] = SEG(p0_p + VECTOR2I( -100, 0 ), p0_p + VECTOR2I( 100, 0 ) );
+ st_n[0] = SEG(p0_n + VECTOR2I( -100, 0 ), p0_n + VECTOR2I( 100, 0 ) );
+ st_p[1] = SEG(p0_p + VECTOR2I( 0, -100 ), p0_p + VECTOR2I( 0, 100 ) );
+ st_n[1] = SEG(p0_n + VECTOR2I( 0, -100 ), p0_n + VECTOR2I( 0, 100 ) );
+ d_p[0] = SEG( p0_p + VECTOR2I( -100, -100 ), p0_p + VECTOR2I( 100, 100 ) );
+ d_p[1] = SEG( p0_p + VECTOR2I( 100, -100 ), p0_p + VECTOR2I( -100, 100 ) );
+ d_n[0] = SEG( p0_n + VECTOR2I( -100, -100 ), p0_n + VECTOR2I( 100, 100 ) );
+ d_n[1] = SEG( p0_n + VECTOR2I( 100, -100 ), p0_n + VECTOR2I( -100, 100 ) );
+
+ // midpoint exit & side-by exits
+ for( int i = 0; i < 2; i++ )
+ {
+ bool straightColl = st_p[i].Collinear( st_n[i] );
+ bool diagColl = d_p[i].Collinear( d_n[i] );
+
+ if( straightColl || diagColl )
+ {
+ VECTOR2I dir = ( p0_n - p0_p ).Resize( m_gap / 2 );
+ VECTOR2I m = ( p0_p + p0_n ) / 2;
+ int prio = ( padDist > padToGapThreshold * m_gap ? 2 : 1);
+
+ if( !aViaMode )
+ {
+ m_gateways.push_back( PNS_DP_GATEWAY( m - dir, m + dir, diagColl, DIRECTION_45::ANG_RIGHT, prio ) );
+
+ dir = ( p0_n - p0_p ).Resize( m_gap );
+ m_gateways.push_back( PNS_DP_GATEWAY( p0_p - dir, p0_p - dir + dir.Perpendicular(), diagColl ) );
+ m_gateways.push_back( PNS_DP_GATEWAY( p0_p - dir, p0_p - dir - dir.Perpendicular(), diagColl ) );
+ m_gateways.push_back( PNS_DP_GATEWAY( p0_n + dir + dir.Perpendicular(), p0_n + dir, diagColl ) );
+ m_gateways.push_back( PNS_DP_GATEWAY( p0_n + dir - dir.Perpendicular(), p0_n + dir, diagColl ) );
+ }
+ }
+ }
+
+ for( int i = 0; i < 2; i++ )
+ {
+ for( int j = 0; j < 2; j++ )
+ {
+ OPT_VECTOR2I ips[2];
+
+ ips[0] = d_n[i].IntersectLines( d_p[j] );
+ ips[1] = st_p[i].IntersectLines( st_n[j] );
+
+ if( d_n[i].Collinear( d_p[j] ) )
+ ips[0] = boost::none;
+ if( st_p[i].Collinear( st_p[j] ) )
+ ips[1] = boost::none;
+
+ // diagonal-diagonal and straight-straight cases - the most typical case if the pads
+ // are on the same straight/diagonal line
+ for( int k = 0; k < 2; k++ )
+ {
+ if( ips[k] )
+ {
+ const VECTOR2I m( *ips[k] );
+
+ if( m != p0_p && m != p0_n )
+ {
+ int prio = ( padDist > padToGapThreshold * m_gap ? 10 : 20 );
+ VECTOR2I g_p( ( p0_p - m ).Resize( (double) m_gap * M_SQRT1_2 ) );
+ VECTOR2I g_n( ( p0_n - m ).Resize( (double) m_gap * M_SQRT1_2 ) );
+
+ m_gateways.push_back( PNS_DP_GATEWAY( m + g_p, m + g_n, k == 0 ? true : false, DIRECTION_45::ANG_OBTUSE, prio ) );
+ }
+ }
+ }
+
+ ips[0] = st_n[i].IntersectLines( d_p[j] );
+ ips[1] = st_p[i].IntersectLines( d_n[j] );
+
+// diagonal-straight cases: 8 possibilities of "weirder" exists
+ for( int k = 0; k < 2; k++ )
+ {
+ if( ips[k] )
+ {
+ const VECTOR2I m( *ips[k] );
+
+ if( !aViaMode && m != p0_p && m != p0_n )
+ {
+ VECTOR2I g_p, g_n;
+
+ g_p = ( p0_p - m ).Resize( (double) m_gap * M_SQRT2 );
+ g_n = ( p0_n - m ).Resize( (double) m_gap );
+
+ if( angle( g_p, g_n ) != DIRECTION_45::ANG_ACUTE )
+ m_gateways.push_back( PNS_DP_GATEWAY( m + g_p, m + g_n, true ) );
+
+ g_p = ( p0_p - m ).Resize( m_gap );
+ g_n = ( p0_n - m ).Resize( (double) m_gap * M_SQRT2 );
+
+ if( angle( g_p, g_n ) != DIRECTION_45::ANG_ACUTE )
+ m_gateways.push_back( PNS_DP_GATEWAY( m + g_p, m + g_n, true ) );
+ }
+ }
+ }
+ }
+ }
+
+ if( aBuildEntries )
+ buildEntries( p0_p, p0_n );
+}
+
+
+PNS_DP_PRIMITIVE_PAIR PNS_DIFF_PAIR::EndingPrimitives()
+{
+ if( m_hasVias )
+ return PNS_DP_PRIMITIVE_PAIR( &m_via_p, &m_via_n );
+ else
+ {
+ const PNS_LINE lP( PLine() );
+ const PNS_LINE lN( NLine() );
+
+ PNS_SEGMENT sP( lP, lP.CSegment( -1 ) );
+ PNS_SEGMENT sN( lN, lN.CSegment( -1 ) );
+
+ PNS_DP_PRIMITIVE_PAIR dpair( &sP, &sN );
+ dpair.SetAnchors( sP.Seg().B, sN.Seg().B );
+
+ return dpair;
+ }
+}
+
+
+bool commonParallelProjection( SEG n, SEG p, SEG &pClip, SEG& nClip )
+{
+ SEG n_proj_p( p.LineProject( n.A ), p.LineProject( n.B ) );
+
+ int64_t t_a = 0;
+ int64_t t_b = p.TCoef( p.B );
+
+ int64_t tproj_a = p.TCoef( n_proj_p.A );
+ int64_t tproj_b = p.TCoef( n_proj_p.B );
+
+ if( t_b < t_a )
+ std::swap( t_b, t_a );
+
+ if( tproj_b < tproj_a )
+ std::swap( tproj_b, tproj_a );
+
+ if( t_b <= tproj_a )
+ return false;
+
+ if( t_a >= tproj_b )
+ return false;
+
+ int64_t t[4] = { 0, p.TCoef( p.B ), p.TCoef( n_proj_p.A ), p.TCoef( n_proj_p.B ) };
+ std::vector<int64_t> tv( t, t + 4 );
+ std::sort( tv.begin(), tv.end() ); // fixme: awful and disgusting way of finding 2 midpoints
+
+ int64_t pLenSq = p.SquaredLength();
+
+ VECTOR2I dp = p.B - p.A;
+ pClip.A.x = p.A.x + rescale( (int64_t)dp.x, tv[1], pLenSq );
+ pClip.A.y = p.A.y + rescale( (int64_t)dp.y, tv[1], pLenSq );
+
+ pClip.B.x = p.A.x + rescale( (int64_t)dp.x, tv[2], pLenSq );
+ pClip.B.y = p.A.y + rescale( (int64_t)dp.y, tv[2], pLenSq );
+
+ nClip.A = n.LineProject( pClip.A );
+ nClip.B = n.LineProject( pClip.B );
+
+ return true;
+}
+
+
+double PNS_DIFF_PAIR::Skew() const
+{
+ return m_p.Length() - m_n.Length();
+}
+
+
+void PNS_DIFF_PAIR::CoupledSegmentPairs( COUPLED_SEGMENTS_VEC& aPairs ) const
+{
+ SHAPE_LINE_CHAIN p( m_p );
+ SHAPE_LINE_CHAIN n( m_n );
+
+ p.Simplify();
+ n.Simplify();
+
+ for( int i = 0; i < p.SegmentCount(); i++ )
+ {
+ for( int j = 0; j < n.SegmentCount(); j++ )
+ {
+ SEG sp = p.CSegment( i );
+ SEG sn = n.CSegment( j );
+
+ SEG p_clip, n_clip;
+
+ int64_t dist = std::abs( sp.Distance( sn ) - m_width );
+
+ if( sp.ApproxParallel( sn ) && m_gapConstraint.Matches( dist ) && commonParallelProjection( sp, sn, p_clip, n_clip ) )
+ {
+ const COUPLED_SEGMENTS spair( p_clip, sp, i, n_clip, sn, j );
+ aPairs.push_back( spair );
+ }
+ }
+ }
+}
+
+
+int64_t PNS_DIFF_PAIR::CoupledLength( const SHAPE_LINE_CHAIN& aP, const SHAPE_LINE_CHAIN& aN ) const
+{
+ int64_t total = 0;
+
+ for( int i = 0; i < aP.SegmentCount(); i++ )
+ {
+ for( int j = 0; j < aN.SegmentCount(); j++ )
+ {
+ SEG sp = aP.CSegment( i );
+ SEG sn = aN.CSegment( j );
+
+ SEG p_clip, n_clip;
+
+ int64_t dist = std::abs( sp.Distance(sn) - m_width );
+
+ if( sp.ApproxParallel( sn ) && m_gapConstraint.Matches( dist ) &&
+ commonParallelProjection( sp, sn, p_clip, n_clip ) )
+ total += p_clip.Length();
+ }
+ }
+
+ return total;
+}
+
+
+double PNS_DIFF_PAIR::CoupledLength() const
+{
+ COUPLED_SEGMENTS_VEC pairs;
+
+ CoupledSegmentPairs( pairs );
+
+ double l = 0.0;
+ for( unsigned int i = 0; i < pairs.size(); i++ )
+ l += pairs[i].coupledP.Length();
+
+ return l;
+}
+
+
+double PNS_DIFF_PAIR::CoupledLengthFactor() const
+{
+ double t = TotalLength();
+
+ if( t == 0.0 )
+ return 0.0;
+
+ return CoupledLength() / t;
+}
+
+
+double PNS_DIFF_PAIR::TotalLength() const
+{
+ double lenP = m_p.Length();
+ double lenN = m_n.Length();
+
+ return (lenN + lenP ) / 2.0;
+}
+
+
+int PNS_DIFF_PAIR::CoupledLength ( const SEG& aP, const SEG& aN ) const
+{
+ SEG p_clip, n_clip;
+ int64_t dist = std::abs( aP.Distance( aN ) - m_width );
+
+ if( aP.ApproxParallel( aN ) && m_gapConstraint.Matches( dist ) &&
+ commonParallelProjection ( aP, aN, p_clip, n_clip ) )
+ return p_clip.Length();
+
+ return 0;
+}
diff --git a/pcbnew/router/pns_diff_pair.h b/pcbnew/router/pns_diff_pair.h
new file mode 100644
index 0000000..234ef89
--- /dev/null
+++ b/pcbnew/router/pns_diff_pair.h
@@ -0,0 +1,495 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef __PNS_DIFF_PAIR_H
+#define __PNS_DIFF_PAIR_H
+
+#include <vector>
+
+#include <geometry/shape.h>
+#include <geometry/shape_line_chain.h>
+
+#include "pns_line.h"
+#include "pns_via.h"
+
+#include "ranged_num.h"
+
+class PNS_DIFF_PAIR;
+
+/**
+ * Class PNS_DP_GATEWAY
+ *
+ * Defines a "gateway" for routing a differential pair - e.g. a pair of points (anchors) with certain
+ * orientation, spacing and (optionally) predefined entry paths. The routing algorithm connects such
+ * gateways with parallel lines, thus creating a difrerential pair.
+ **/
+class PNS_DP_GATEWAY {
+public:
+ PNS_DP_GATEWAY( const VECTOR2I& aAnchorP,
+ const VECTOR2I& aAnchorN,
+ bool aIsDiagonal,
+ int aAllowedEntryAngles = DIRECTION_45::ANG_OBTUSE,
+ int aPriority = 0 )
+ : m_anchorP( aAnchorP ),
+ m_anchorN( aAnchorN ),
+ m_isDiagonal( aIsDiagonal ),
+ m_allowedEntryAngles( aAllowedEntryAngles ),
+ m_priority( aPriority )
+ {
+ m_hasEntryLines = false;
+ }
+
+ ~PNS_DP_GATEWAY()
+ {
+ }
+
+ /**
+ * Function IsDiagonal()
+ *
+ * @return true, if the gateway anchors lie on a diagonal line
+ */
+ bool IsDiagonal() const
+ {
+ return m_isDiagonal;
+ }
+
+ const VECTOR2I& AnchorP() const { return m_anchorP; }
+
+ const VECTOR2I& AnchorN() const { return m_anchorN; }
+
+ /**
+ * Function AllowedAngles()
+ *
+ * @return a mask of 45-degree entry directoins allowed for the
+ * gateway.
+ */
+ int AllowedAngles () const { return m_allowedEntryAngles; }
+
+ /**
+ * Function Priority()
+ *
+ * @return priority/score value for gateway matching
+ */
+ int Priority() const
+ {
+ return m_priority;
+ }
+
+ void SetPriority(int aPriority)
+ {
+ m_priority = aPriority;
+ }
+
+ void SetEntryLines( const SHAPE_LINE_CHAIN& aEntryP, const SHAPE_LINE_CHAIN& aEntryN )
+ {
+ m_entryP = aEntryP;
+ m_entryN = aEntryN;
+ m_hasEntryLines = true;
+ }
+
+ const SHAPE_LINE_CHAIN& EntryP() const { return m_entryP; }
+ const SHAPE_LINE_CHAIN& EntryN() const { return m_entryN; }
+ const PNS_DIFF_PAIR Entry() const ;
+
+ void Reverse();
+
+ bool HasEntryLines () const
+ {
+ return m_hasEntryLines;
+ }
+
+private:
+ SHAPE_LINE_CHAIN m_entryP, m_entryN;
+ bool m_hasEntryLines;
+ VECTOR2I m_anchorP, m_anchorN;
+ bool m_isDiagonal;
+ int m_allowedEntryAngles;
+ int m_priority;
+};
+
+/**
+ * Class PNS_DP_PRIMITIVE_PAIR
+ *
+ * Stores staring/ending primitives (pads, vias or segments) for a differential pair.
+ **/
+class PNS_DP_PRIMITIVE_PAIR
+{
+public:
+ PNS_DP_PRIMITIVE_PAIR():
+ m_primP( NULL ), m_primN( NULL ) {};
+
+ PNS_DP_PRIMITIVE_PAIR( const PNS_DP_PRIMITIVE_PAIR& aOther );
+ PNS_DP_PRIMITIVE_PAIR( PNS_ITEM* aPrimP, PNS_ITEM* aPrimN );
+ PNS_DP_PRIMITIVE_PAIR( const VECTOR2I& aAnchorP, const VECTOR2I& aAnchorN );
+
+ ~PNS_DP_PRIMITIVE_PAIR();
+
+ void SetAnchors( const VECTOR2I& aAnchorP, const VECTOR2I& aAnchorN );
+
+ const VECTOR2I& AnchorP() const { return m_anchorP; }
+ const VECTOR2I& AnchorN() const { return m_anchorN; }
+
+ PNS_DP_PRIMITIVE_PAIR& operator=( const PNS_DP_PRIMITIVE_PAIR& aOther );
+
+ PNS_ITEM* PrimP() const { return m_primP; }
+ PNS_ITEM* PrimN() const { return m_primN; }
+
+ bool Directional() const;
+
+ DIRECTION_45 DirP() const;
+ DIRECTION_45 DirN() const;
+
+private:
+ DIRECTION_45 anchorDirection( PNS_ITEM* aItem, const VECTOR2I& aP ) const;
+
+ PNS_ITEM* m_primP;
+ PNS_ITEM* m_primN;
+ VECTOR2I m_anchorP, m_anchorN;
+};
+
+/**
+ * Class PNS_GATEWAYS
+ *
+ * A set of gateways calculated for the cursor or starting/ending primitive pair.
+ **/
+
+class PNS_DP_GATEWAYS
+{
+
+ public:
+ PNS_DP_GATEWAYS ( int aGap ):
+ m_gap(aGap), m_viaGap( aGap )
+ {
+ // Do not leave unitialized members, and keep static analyser quiet:
+ m_viaDiameter = 0;
+ m_fitVias = true;
+ }
+
+ void SetGap ( int aGap )
+ {
+ m_gap = aGap;
+ m_viaGap = aGap;
+ }
+
+ void Clear()
+ {
+ m_gateways.clear();
+ }
+
+ void SetFitVias ( bool aEnable, int aDiameter = 0, int aViaGap = -1 )
+ {
+ m_fitVias = aEnable;
+ m_viaDiameter = aDiameter;
+ if(aViaGap < 0)
+ m_viaGap = m_gap;
+ else
+ m_viaGap = aViaGap;
+ }
+
+
+ void BuildForCursor ( const VECTOR2I& aCursorPos );
+ void BuildOrthoProjections ( PNS_DP_GATEWAYS &aEntries, const VECTOR2I& aCursorPos, int aOrthoScore );
+ void BuildGeneric ( const VECTOR2I& p0_p, const VECTOR2I& p0_n, bool aBuildEntries = false, bool aViaMode = false );
+ void BuildFromPrimitivePair( PNS_DP_PRIMITIVE_PAIR aPair, bool aPreferDiagonal );
+
+ bool FitGateways ( PNS_DP_GATEWAYS& aEntry, PNS_DP_GATEWAYS& aTarget, bool aPrefDiagonal, PNS_DIFF_PAIR& aDp );
+
+ std::vector<PNS_DP_GATEWAY>& Gateways()
+ {
+ return m_gateways;
+ }
+
+ private:
+
+ struct DP_CANDIDATE
+ {
+ SHAPE_LINE_CHAIN p, n;
+ VECTOR2I gw_p, gw_n;
+ int score;
+ };
+
+ bool checkDiagonalAlignment ( const VECTOR2I& a, const VECTOR2I& b) const;
+ void buildDpContinuation ( PNS_DP_PRIMITIVE_PAIR aPair, bool aIsDiagonal );
+ void buildEntries ( const VECTOR2I& p0_p, const VECTOR2I& p0_n );
+
+ int m_gap;
+ int m_viaGap;
+ int m_viaDiameter;
+ bool m_fitVias;
+
+ std::vector<PNS_DP_GATEWAY> m_gateways;
+};
+
+
+/**
+ * Class PNS_DIFF_PAIR
+ *
+ * Basic class for a differential pair. Stores two PNS_LINEs (for positive and negative nets, respectively),
+ * the gap and coupling constraints.
+ **/
+class PNS_DIFF_PAIR : public PNS_ITEM {
+
+public:
+ struct COUPLED_SEGMENTS {
+ COUPLED_SEGMENTS ( const SEG& aCoupledP, const SEG& aParentP, int aIndexP,
+ const SEG& aCoupledN, const SEG& aParentN, int aIndexN ) :
+ coupledP ( aCoupledP ),
+ coupledN ( aCoupledN ),
+ parentP ( aParentP ),
+ parentN ( aParentN ),
+ indexP ( aIndexP ),
+ indexN ( aIndexN )
+ {}
+
+ SEG coupledP;
+ SEG coupledN;
+ SEG parentP;
+ SEG parentN;
+ int indexP;
+ int indexN;
+ };
+
+ typedef std::vector<COUPLED_SEGMENTS> COUPLED_SEGMENTS_VEC;
+
+ PNS_DIFF_PAIR ( ) : PNS_ITEM ( DIFF_PAIR ), m_hasVias (false)
+ {
+ // Initialize some members, to avoid uninitialized variables.
+ m_net_p = 0;
+ m_net_n = 0;;
+ m_width = 0;
+ m_gap = 0;
+ m_viaGap = 0;
+ m_maxUncoupledLength = 0;
+ m_chamferLimit = 0;
+ }
+
+ PNS_DIFF_PAIR ( int aGap ) :
+ PNS_ITEM ( DIFF_PAIR ),
+ m_hasVias (false)
+ {
+ m_gapConstraint = aGap;
+
+ // Initialize other members, to avoid uninitialized variables.
+ m_net_p = 0;
+ m_net_n = 0;;
+ m_width = 0;
+ m_gap = 0;
+ m_viaGap = 0;
+ m_maxUncoupledLength = 0;
+ m_chamferLimit = 0;
+ }
+
+ PNS_DIFF_PAIR ( const SHAPE_LINE_CHAIN &aP, const SHAPE_LINE_CHAIN& aN, int aGap = 0 ):
+ PNS_ITEM ( DIFF_PAIR ),
+ m_n (aN),
+ m_p (aP),
+ m_hasVias (false)
+ {
+ m_gapConstraint = aGap;
+
+ // Initialize other members, to avoid uninitialized variables.
+ m_net_p = 0;
+ m_net_n = 0;;
+ m_width = 0;
+ m_gap = 0;
+ m_viaGap = 0;
+ m_maxUncoupledLength = 0;
+ m_chamferLimit = 0;
+ }
+
+ PNS_DIFF_PAIR ( const PNS_LINE &aLineP, const PNS_LINE &aLineN, int aGap = 0 ):
+ PNS_ITEM ( DIFF_PAIR ),
+ m_line_p ( aLineP ),
+ m_line_n ( aLineN ),
+ m_hasVias (false)
+ {
+ m_gapConstraint = aGap;
+ m_net_p = aLineP.Net();
+ m_net_n = aLineN.Net();
+ m_p = aLineP.CLine();
+ m_n = aLineN.CLine();
+
+ // Do not leave unitialized members, and keep static analyser quiet:
+ m_width = 0;
+ m_gap = 0;
+ m_viaGap = 0;
+ m_maxUncoupledLength = 0;
+ m_chamferLimit = 0;
+ }
+
+ static inline bool ClassOf( const PNS_ITEM* aItem )
+ {
+ return aItem && DIFF_PAIR == aItem->Kind();
+ }
+
+ PNS_DIFF_PAIR * Clone() const { assert(false); return NULL; }
+
+ static PNS_DIFF_PAIR* AssembleDp ( PNS_LINE *aLine );
+
+ void SetShape ( const SHAPE_LINE_CHAIN &aP, const SHAPE_LINE_CHAIN& aN, bool aSwapLanes = false)
+ {
+ if (aSwapLanes)
+ {
+ m_p = aN;
+ m_n = aP;
+ } else {
+ m_p = aP;
+ m_n = aN;
+ }
+ }
+
+ void SetShape ( const PNS_DIFF_PAIR& aPair )
+ {
+ m_p = aPair.m_p;
+ m_n = aPair.m_n;
+ }
+
+ void SetNets ( int aP, int aN )
+ {
+ m_net_p = aP;
+ m_net_n = aN;
+ }
+
+ void SetWidth ( int aWidth )
+ {
+ m_width = aWidth;
+ }
+
+ int Width() const { return m_width; }
+
+ void SetGap ( int aGap)
+ {
+ m_gap = aGap;
+ m_gapConstraint = RANGED_NUM<int> ( m_gap, 10000, 10000 );
+ }
+
+ int Gap() const {
+ return m_gap;
+ }
+
+ void AppendVias ( const PNS_VIA &aViaP, const PNS_VIA& aViaN )
+ {
+ m_hasVias = true;
+ m_via_p = aViaP;
+ m_via_n = aViaN;
+ }
+
+ void RemoveVias ()
+ {
+ m_hasVias = false;
+ }
+
+ bool EndsWithVias() const
+ {
+ return m_hasVias;
+ }
+
+ int NetP() const
+ {
+ return m_net_p;
+ }
+
+ int NetN() const
+ {
+ return m_net_n;
+ }
+
+ PNS_LINE& PLine()
+ {
+ if ( !m_line_p.IsLinked ( ) )
+ updateLine(m_line_p, m_p, m_net_p, m_via_p );
+ return m_line_p;
+ }
+
+ PNS_LINE& NLine()
+ {
+ if ( !m_line_n.IsLinked ( ) )
+ updateLine(m_line_n, m_n, m_net_n, m_via_n );
+ return m_line_n;
+ }
+
+ PNS_DP_PRIMITIVE_PAIR EndingPrimitives();
+
+ double CoupledLength() const;
+ double TotalLength() const;
+ double CoupledLengthFactor () const;
+ double Skew () const;
+
+ void CoupledSegmentPairs ( COUPLED_SEGMENTS_VEC& aPairs ) const;
+
+ void Clear()
+ {
+ m_n.Clear();
+ m_p.Clear();
+ }
+
+ void Append (const PNS_DIFF_PAIR& aOther )
+ {
+ m_n.Append ( aOther.m_n );
+ m_p.Append ( aOther.m_p );
+ }
+
+ bool Empty() const
+ {
+ return (m_n.SegmentCount() == 0) || (m_p.SegmentCount() == 0);
+ }
+ const SHAPE_LINE_CHAIN& CP() const { return m_p; }
+ const SHAPE_LINE_CHAIN& CN() const { return m_n; }
+
+ bool BuildInitial ( PNS_DP_GATEWAY& aEntry, PNS_DP_GATEWAY& aTarget, bool aPrefDiagonal );
+ bool CheckConnectionAngle ( const PNS_DIFF_PAIR &aOther, int allowedAngles ) const;
+ int CoupledLength ( const SEG& aP, const SEG& aN ) const;
+
+ int64_t CoupledLength ( const SHAPE_LINE_CHAIN& aP, const SHAPE_LINE_CHAIN& aN ) const;
+
+ const RANGED_NUM<int> GapConstraint() const {
+ return m_gapConstraint;
+ }
+
+private:
+
+ void updateLine( PNS_LINE &aLine, const SHAPE_LINE_CHAIN& aShape, int aNet, PNS_VIA& aVia )
+ {
+ aLine.SetShape( aShape );
+ aLine.SetWidth( m_width );
+ aLine.SetNet(aNet);
+ aLine.SetLayer (Layers().Start());
+
+ if(m_hasVias)
+ aLine.AppendVia ( aVia );
+ }
+
+ SHAPE_LINE_CHAIN m_n, m_p;
+ PNS_LINE m_line_p, m_line_n;
+ PNS_VIA m_via_p, m_via_n;
+
+ bool m_hasVias;
+ int m_net_p, m_net_n;
+ int m_width;
+ int m_gap;
+ int m_viaGap;
+ int m_maxUncoupledLength;
+ int m_chamferLimit;
+ RANGED_NUM<int> m_gapConstraint;
+};
+
+
+#endif
diff --git a/pcbnew/router/pns_diff_pair_placer.cpp b/pcbnew/router/pns_diff_pair_placer.cpp
new file mode 100644
index 0000000..c9fc2bf
--- /dev/null
+++ b/pcbnew/router/pns_diff_pair_placer.cpp
@@ -0,0 +1,859 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/foreach.hpp>
+#include <boost/optional.hpp>
+
+#include <colors.h>
+#include <class_board.h>
+#include <class_board_item.h>
+#include <class_netinfo.h>
+
+#include "trace.h"
+
+#include "pns_node.h"
+#include "pns_walkaround.h"
+#include "pns_shove.h"
+#include "pns_utils.h"
+#include "pns_router.h"
+#include "pns_diff_pair_placer.h"
+#include "pns_solid.h"
+#include "pns_topology.h"
+
+using boost::optional;
+
+PNS_DIFF_PAIR_PLACER::PNS_DIFF_PAIR_PLACER( PNS_ROUTER* aRouter ) :
+ PNS_PLACEMENT_ALGO( aRouter )
+{
+ m_state = RT_START;
+ m_chainedPlacement = false;
+ m_initialDiagonal = false;
+ m_startDiagonal = false;
+ m_fitOk = false;
+ m_netP = 0;
+ m_netN = 0;
+ m_iteration = 0;
+ m_world = NULL;
+ m_shove = NULL;
+ m_currentNode = NULL;
+ m_lastNode = NULL;
+ m_placingVia = false;
+ m_viaDiameter = 0;
+ m_viaDrill = 0;
+ m_currentWidth = 0;
+ m_currentNet = 0;
+ m_currentLayer = 0;
+ m_startsOnVia = false;
+ m_orthoMode = false;
+ m_snapOnTarget = false;
+ m_currentEndItem = NULL;
+ m_currentMode = RM_MarkObstacles;
+ m_idle = true;
+}
+
+PNS_DIFF_PAIR_PLACER::~PNS_DIFF_PAIR_PLACER()
+{
+ if( m_shove )
+ delete m_shove;
+}
+
+
+void PNS_DIFF_PAIR_PLACER::setWorld( PNS_NODE* aWorld )
+{
+ m_world = aWorld;
+}
+
+
+const PNS_VIA PNS_DIFF_PAIR_PLACER::makeVia( const VECTOR2I& aP, int aNet )
+{
+ const PNS_LAYERSET layers( m_sizes.GetLayerTop(), m_sizes.GetLayerBottom() );
+
+ PNS_VIA v( aP, layers, m_sizes.ViaDiameter(), m_sizes.ViaDrill(), -1, m_sizes.ViaType() );
+ v.SetNet( aNet );
+
+ return v;
+}
+
+
+void PNS_DIFF_PAIR_PLACER::SetOrthoMode ( bool aOrthoMode )
+{
+ m_orthoMode = aOrthoMode;
+
+ if( !m_idle )
+ Move( m_currentEnd, NULL );
+}
+
+
+bool PNS_DIFF_PAIR_PLACER::ToggleVia( bool aEnabled )
+{
+ m_placingVia = aEnabled;
+
+ if( !m_idle )
+ Move( m_currentEnd, NULL );
+
+ return true;
+}
+
+
+bool PNS_DIFF_PAIR_PLACER::rhMarkObstacles( const VECTOR2I& aP )
+{
+ if( !routeHead( aP ) )
+ return false;
+
+ bool collP = static_cast<bool>( m_currentNode->CheckColliding( &m_currentTrace.PLine() ) );
+ bool collN = static_cast<bool>( m_currentNode->CheckColliding( &m_currentTrace.NLine() ) );
+
+ m_fitOk = !( collP || collN ) ;
+
+ return m_fitOk;
+}
+
+
+bool PNS_DIFF_PAIR_PLACER::propagateDpHeadForces ( const VECTOR2I& aP, VECTOR2I& aNewP )
+{
+ PNS_VIA virtHead = makeVia( aP, -1 );
+
+ if( m_placingVia )
+ virtHead.SetDiameter( viaGap() + 2 * virtHead.Diameter() );
+ else
+ {
+ virtHead.SetLayer( m_currentLayer );
+ virtHead.SetDiameter( m_sizes.DiffPairGap() + 2 * m_sizes.TrackWidth() );
+ }
+
+ VECTOR2I lead( 0, 0 );// = aP - m_currentStart ;
+ VECTOR2I force;
+ bool solidsOnly = true;
+
+ if( m_currentMode == RM_MarkObstacles )
+ {
+ aNewP = aP;
+ return true;
+ }
+ else if( m_currentMode == RM_Walkaround )
+ {
+ solidsOnly = false;
+ }
+
+ // fixme: I'm too lazy to do it well. Circular approximaton will do for the moment.
+ if( virtHead.PushoutForce( m_currentNode, lead, force, solidsOnly, 40 ) )
+ {
+ aNewP = aP + force;
+ return true;
+ }
+
+ return false;
+}
+
+
+bool PNS_DIFF_PAIR_PLACER::attemptWalk ( PNS_NODE* aNode, PNS_DIFF_PAIR* aCurrent, PNS_DIFF_PAIR& aWalk, bool aPFirst, bool aWindCw, bool aSolidsOnly )
+{
+ PNS_WALKAROUND walkaround( aNode, Router() );
+ PNS_WALKAROUND::WALKAROUND_STATUS wf1;
+
+ Router()->GetClearanceFunc()->OverrideClearance( true, aCurrent->NetP(), aCurrent->NetN(), aCurrent->Gap() );
+
+ walkaround.SetSolidsOnly( aSolidsOnly );
+ walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() );
+
+ PNS_SHOVE shove( aNode, Router() );
+ PNS_LINE walkP, walkN;
+
+ aWalk = *aCurrent;
+
+ int iter = 0;
+
+ PNS_DIFF_PAIR cur( *aCurrent );
+
+ bool currentIsP = aPFirst;
+
+ int mask = aSolidsOnly ? PNS_ITEM::SOLID : PNS_ITEM::ANY;
+
+ do
+ {
+ PNS_LINE preWalk = ( currentIsP ? cur.PLine() : cur.NLine() );
+ PNS_LINE preShove = ( currentIsP ? cur.NLine() : cur.PLine() );
+ PNS_LINE postWalk;
+
+ if( !aNode->CheckColliding ( &preWalk, mask ) )
+ {
+ currentIsP = !currentIsP;
+
+ if( !aNode->CheckColliding( &preShove, mask ) )
+ break;
+ else
+ continue;
+ }
+
+ wf1 = walkaround.Route( preWalk, postWalk, false );
+
+ if( wf1 != PNS_WALKAROUND::DONE )
+ return false;
+
+ PNS_LINE postShove( preShove );
+
+ shove.ForceClearance( true, cur.Gap() - 2 * PNS_HULL_MARGIN );
+
+ PNS_SHOVE::SHOVE_STATUS sh1;
+
+ sh1 = shove.ProcessSingleLine( postWalk, preShove, postShove );
+
+ if( sh1 != PNS_SHOVE::SH_OK )
+ return false;
+
+ postWalk.Line().Simplify();
+ postShove.Line().Simplify();
+
+ cur.SetShape( postWalk.CLine(), postShove.CLine(), !currentIsP );
+
+ currentIsP = !currentIsP;
+
+ if( !aNode->CheckColliding( &postShove, mask ) )
+ break;
+
+ iter++;
+ }
+ while( iter < 3 );
+
+ if( iter == 3 )
+ return false;
+
+ aWalk.SetShape( cur.CP(), cur.CN() );
+ Router()->GetClearanceFunc()->OverrideClearance( false );
+
+ return true;
+}
+
+
+bool PNS_DIFF_PAIR_PLACER::tryWalkDp( PNS_NODE* aNode, PNS_DIFF_PAIR &aPair, bool aSolidsOnly )
+{
+ PNS_DIFF_PAIR best;
+ double bestScore = 100000000000000.0;
+
+ for( int attempt = 0; attempt <= 1; attempt++ )
+ {
+ PNS_DIFF_PAIR p;
+ PNS_NODE *tmp = m_currentNode->Branch();
+
+ bool pfirst = attempt % 2 ? true : false;
+ bool wind_cw = attempt / 2 ? true : false;
+
+ if( attemptWalk ( tmp, &aPair, p, pfirst, wind_cw, aSolidsOnly ) )
+ {
+ // double len = p.TotalLength();
+ double cl = p.CoupledLength();
+ double skew = p.Skew();
+
+ double score = cl + fabs(skew) * 3.0;
+
+ if( score < bestScore )
+ {
+ bestScore = score;
+ best = p;
+ }
+ }
+
+ delete tmp;
+ }
+
+ if( bestScore > 0.0 )
+ {
+ PNS_OPTIMIZER optimizer( m_currentNode );
+
+ aPair.SetShape( best );
+ optimizer.Optimize( &aPair );
+
+ return true;
+ }
+
+ return false;
+}
+
+
+bool PNS_DIFF_PAIR_PLACER::rhWalkOnly( const VECTOR2I& aP )
+{
+ if( !routeHead ( aP ) )
+ return false;
+
+ m_fitOk = tryWalkDp( m_currentNode, m_currentTrace, false );
+
+ return m_fitOk;
+}
+
+
+bool PNS_DIFF_PAIR_PLACER::route( const VECTOR2I& aP )
+{
+ switch( m_currentMode )
+ {
+ case RM_MarkObstacles:
+ return rhMarkObstacles( aP );
+ case RM_Walkaround:
+ return rhWalkOnly( aP );
+ case RM_Shove:
+ return rhShoveOnly( aP );
+ default:
+ break;
+ }
+
+ return false;
+}
+
+
+bool PNS_DIFF_PAIR_PLACER::rhShoveOnly( const VECTOR2I& aP )
+{
+ m_currentNode = m_shove->CurrentNode();
+
+ bool ok = routeHead( aP );
+
+ m_fitOk = false;
+
+ if( !ok )
+ return false;
+
+ if( !tryWalkDp( m_currentNode, m_currentTrace, true ) )
+ return false;
+
+ PNS_LINE pLine( m_currentTrace.PLine() );
+ PNS_LINE nLine( m_currentTrace.NLine() );
+ PNS_ITEMSET head;
+
+ head.Add( &pLine );
+ head.Add( &nLine );
+
+ PNS_SHOVE::SHOVE_STATUS status = m_shove->ShoveMultiLines( head );
+
+ m_currentNode = m_shove->CurrentNode();
+
+ if( status == PNS_SHOVE::SH_OK )
+ {
+ m_currentNode = m_shove->CurrentNode();
+
+ if( !m_currentNode->CheckColliding( &m_currentTrace.PLine() ) &&
+ !m_currentNode->CheckColliding( &m_currentTrace.NLine() ) )
+ {
+ m_fitOk = true;
+ }
+ }
+
+ return m_fitOk;
+}
+
+
+const PNS_ITEMSET PNS_DIFF_PAIR_PLACER::Traces()
+{
+ PNS_ITEMSET t;
+
+ t.Add( const_cast<PNS_LINE*>( &m_currentTrace.PLine() ) );
+ t.Add( const_cast<PNS_LINE*>( &m_currentTrace.NLine() ) );
+
+ return t;
+}
+
+
+void PNS_DIFF_PAIR_PLACER::FlipPosture()
+{
+ m_startDiagonal = !m_startDiagonal;
+
+ if( !m_idle )
+ Move( m_currentEnd, NULL );
+}
+
+
+PNS_NODE* PNS_DIFF_PAIR_PLACER::CurrentNode( bool aLoopsRemoved ) const
+{
+ if( m_lastNode )
+ return m_lastNode;
+
+ return m_currentNode;
+}
+
+
+bool PNS_DIFF_PAIR_PLACER::SetLayer( int aLayer )
+{
+ if( m_idle )
+ {
+ m_currentLayer = aLayer;
+ return true;
+ } else if( m_chainedPlacement )
+ return false;
+ else if( !m_prevPair )
+ return false;
+ else if( m_prevPair->PrimP() || ( m_prevPair->PrimP()->OfKind( PNS_ITEM::VIA ) &&
+ m_prevPair->PrimP()->Layers().Overlaps( aLayer ) ) )
+ {
+ m_currentLayer = aLayer;
+ m_start = *m_prevPair;
+ initPlacement( false );
+ Move( m_currentEnd, NULL );
+ return true;
+ }
+
+ return false;
+}
+
+
+int PNS_DIFF_PAIR_PLACER::matchDpSuffix( wxString aNetName, wxString& aComplementNet, wxString& aBaseDpName )
+{
+ int rv = 0;
+
+ if( aNetName.EndsWith( "+" ) )
+ {
+ aComplementNet = "-";
+ rv = 1;
+ }
+ else if( aNetName.EndsWith( "_P" ) )
+ {
+ aComplementNet = "_N";
+ rv = 1;
+ }
+ else if( aNetName.EndsWith( "-" ) )
+ {
+ aComplementNet = "+";
+ rv = -1;
+ }
+ else if( aNetName.EndsWith( "_N" ) )
+ {
+ aComplementNet = "_P";
+ rv = -1;
+ }
+
+ if( rv != 0 )
+ {
+ aBaseDpName = aNetName.Left( aNetName.Length() - aComplementNet.Length() );
+ }
+
+ return rv;
+}
+
+
+OPT_VECTOR2I PNS_DIFF_PAIR_PLACER::getDanglingAnchor( PNS_NODE* aNode, PNS_ITEM* aItem )
+{
+ switch( aItem->Kind() )
+ {
+ case PNS_ITEM::VIA:
+ case PNS_ITEM::SOLID:
+ return aItem->Anchor( 0 );
+
+ case PNS_ITEM::SEGMENT:
+ {
+ PNS_SEGMENT* s =static_cast<PNS_SEGMENT*>( aItem );
+
+ PNS_JOINT* jA = aNode->FindJoint( s->Seg().A, s );
+ PNS_JOINT* jB = aNode->FindJoint( s->Seg().B, s );
+
+ if( jA->LinkCount() == 1 )
+ return s->Seg().A;
+ else if( jB->LinkCount() == 1 )
+ return s->Seg().B;
+ else
+ return OPT_VECTOR2I();
+ }
+
+ default:
+ return OPT_VECTOR2I();
+ break;
+ }
+}
+
+
+bool PNS_DIFF_PAIR_PLACER::findDpPrimitivePair( const VECTOR2I& aP, PNS_ITEM* aItem, PNS_DP_PRIMITIVE_PAIR& aPair )
+{
+ if( !aItem || !aItem->Parent() || !aItem->Parent()->GetNet() )
+ return false;
+
+ wxString netNameP = aItem->Parent()->GetNet()->GetNetname();
+ wxString netNameN, netNameBase;
+
+ BOARD* brd = Router()->GetBoard();
+ PNS_ITEM *primRef = NULL, *primP = NULL, *primN = NULL;
+
+ int refNet;
+
+ wxString suffix;
+
+ int r = matchDpSuffix ( netNameP, suffix, netNameBase );
+
+ if( r == 0 )
+ return false;
+ else if( r == 1 )
+ {
+ primRef = primP = static_cast<PNS_SOLID*>( aItem );
+ netNameN = netNameBase + suffix;
+ }
+ else
+ {
+ primRef = primN = static_cast<PNS_SOLID*>( aItem );
+ netNameN = netNameP;
+ netNameP = netNameBase + suffix;
+ }
+
+ NETINFO_ITEM* netInfoP = brd->FindNet( netNameP );
+ NETINFO_ITEM* netInfoN = brd->FindNet( netNameN );
+
+ if( !netInfoP || !netInfoN )
+ return false;
+
+ int netP = netInfoP->GetNet();
+ int netN = netInfoN->GetNet();
+
+ if( primP )
+ refNet = netN;
+ else
+ refNet = netP;
+
+
+ std::set<PNS_ITEM*> items;
+
+ OPT_VECTOR2I refAnchor = getDanglingAnchor( m_currentNode, primRef );
+
+ if( !refAnchor )
+ return false;
+
+ m_currentNode->AllItemsInNet( refNet, items );
+ double bestDist = std::numeric_limits<double>::max();
+ bool found = false;
+
+ BOOST_FOREACH( PNS_ITEM* item, items )
+ {
+ if( item->Kind() == aItem->Kind() )
+ {
+ OPT_VECTOR2I anchor = getDanglingAnchor( m_currentNode, item );
+ if( !anchor )
+ continue;
+
+ double dist = ( *anchor - *refAnchor ).EuclideanNorm();
+
+ bool shapeMatches = true;
+
+ if( item->OfKind( PNS_ITEM::SOLID ) && item->Layers() != aItem->Layers() )
+ {
+ shapeMatches = false;
+ }
+
+ if( dist < bestDist && shapeMatches )
+ {
+ found = true;
+ bestDist = dist;
+
+ if( refNet == netP )
+ {
+ aPair = PNS_DP_PRIMITIVE_PAIR ( item, primRef );
+ aPair.SetAnchors( *anchor, *refAnchor );
+ }
+ else
+ {
+ aPair = PNS_DP_PRIMITIVE_PAIR( primRef, item );
+ aPair.SetAnchors( *refAnchor, *anchor );
+ }
+ }
+ }
+ }
+
+ return found;
+}
+
+
+int PNS_DIFF_PAIR_PLACER::viaGap() const
+{
+ return m_sizes.DiffPairViaGap();
+}
+
+
+int PNS_DIFF_PAIR_PLACER::gap() const
+{
+ return m_sizes.DiffPairGap() + m_sizes.DiffPairWidth();
+}
+
+
+bool PNS_DIFF_PAIR_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem )
+{
+ VECTOR2I p( aP );
+
+ bool split;
+
+ if( Router()->SnappingEnabled() )
+ p = Router()->SnapToItem( aStartItem, aP, split );
+
+ if( !aStartItem )
+ {
+ Router()->SetFailureReason( _( "Can't start a differential pair "
+ " in the middle of nowhere." ) );
+ return false;
+ }
+
+ m_currentNode = Router()->GetWorld();
+
+ if( !findDpPrimitivePair( aP, aStartItem, m_start ) )
+ {
+ Router()->SetFailureReason( _( "Unable to find complementary differential pair "
+ "net. Make sure the names of the nets belonging "
+ "to a differential pair end with either _N/_P or +/-." ) );
+ return false;
+ }
+
+ m_netP = m_start.PrimP()->Net();
+ m_netN = m_start.PrimN()->Net();
+
+ // Check if the current track/via gap & track width settings are violated
+ BOARD* brd = Router()->GetBoard();
+ NETCLASSPTR netclassP = brd->FindNet( m_netP )->GetNetClass();
+ NETCLASSPTR netclassN = brd->FindNet( m_netN )->GetNetClass();
+ int clearance = std::min( m_sizes.DiffPairGap(), m_sizes.DiffPairViaGap() );
+
+ if( clearance < netclassP->GetClearance() || clearance < netclassN->GetClearance() )
+ {
+ Router()->SetFailureReason( _( "Current track/via gap setting violates "
+ "design rules for this net." ) );
+ return false;
+ }
+
+ if( m_sizes.DiffPairWidth() < brd->GetDesignSettings().m_TrackMinWidth )
+ {
+ Router()->SetFailureReason( _( "Current track width setting violates design rules." ) );
+ return false;
+ }
+
+ m_currentStart = p;
+ m_currentEnd = p;
+ m_placingVia = false;
+ m_chainedPlacement = false;
+
+ initPlacement( false );
+
+ return true;
+}
+
+
+void PNS_DIFF_PAIR_PLACER::initPlacement( bool aSplitSeg )
+{
+ m_idle = false;
+ m_orthoMode = false;
+ m_currentEndItem = NULL;
+ m_startDiagonal = m_initialDiagonal;
+
+ PNS_NODE* world = Router()->GetWorld();
+
+ world->KillChildren();
+ PNS_NODE* rootNode = world->Branch();
+
+ setWorld( rootNode );
+
+ m_lastNode = NULL;
+ m_currentNode = rootNode;
+ m_currentMode = Settings().Mode();
+
+ if( m_shove )
+ delete m_shove;
+
+ m_shove = NULL;
+
+ if( m_currentMode == RM_Shove || m_currentMode == RM_Smart )
+ {
+ m_shove = new PNS_SHOVE( m_currentNode, Router() );
+ }
+}
+
+bool PNS_DIFF_PAIR_PLACER::routeHead( const VECTOR2I& aP )
+{
+ m_fitOk = false;
+
+ PNS_DP_GATEWAYS gwsEntry( gap() );
+ PNS_DP_GATEWAYS gwsTarget( gap() );
+
+ if( !m_prevPair )
+ m_prevPair = m_start;
+
+ gwsEntry.BuildFromPrimitivePair( *m_prevPair, m_startDiagonal );
+
+ PNS_DP_PRIMITIVE_PAIR target;
+
+ if( findDpPrimitivePair ( aP, m_currentEndItem, target ) )
+ {
+ gwsTarget.BuildFromPrimitivePair( target, m_startDiagonal );
+ m_snapOnTarget = true;
+ } else {
+ VECTOR2I fp;
+
+ if( !propagateDpHeadForces( aP, fp ) )
+ return false;
+
+ gwsTarget.SetFitVias( m_placingVia, m_sizes.ViaDiameter(), viaGap() );
+ gwsTarget.BuildForCursor( fp );
+ gwsTarget.BuildOrthoProjections( gwsEntry, fp, m_orthoMode ? 200 : -200 );
+ m_snapOnTarget = false;
+ }
+
+ m_currentTrace = PNS_DIFF_PAIR();
+ m_currentTrace.SetGap( gap() );
+ m_currentTrace.SetLayer( m_currentLayer );
+
+ if ( gwsEntry.FitGateways( gwsEntry, gwsTarget, m_startDiagonal, m_currentTrace ) )
+ {
+ m_currentTrace.SetNets( m_netP, m_netN );
+ m_currentTrace.SetWidth( m_sizes.DiffPairWidth() );
+ m_currentTrace.SetGap( m_sizes.DiffPairGap() );
+
+ if( m_placingVia )
+ {
+ m_currentTrace.AppendVias ( makeVia ( m_currentTrace.CP().CPoint(-1), m_netP ),
+ makeVia ( m_currentTrace.CN().CPoint(-1), m_netN ) );
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+
+bool PNS_DIFF_PAIR_PLACER::Move( const VECTOR2I& aP , PNS_ITEM* aEndItem )
+{
+ m_currentEndItem = aEndItem;
+ m_fitOk = false;
+
+ delete m_lastNode;
+ m_lastNode = NULL;
+
+ if( !route( aP ) )
+ return false;
+
+ PNS_NODE* latestNode = m_currentNode;
+ m_lastNode = latestNode->Branch();
+
+ assert( m_lastNode != NULL );
+ m_currentEnd = aP;
+
+ updateLeadingRatLine();
+
+ return true;
+}
+
+
+void PNS_DIFF_PAIR_PLACER::UpdateSizes( const PNS_SIZES_SETTINGS& aSizes )
+{
+ m_sizes = aSizes;
+
+ if( !m_idle )
+ {
+ initPlacement();
+ Move( m_currentEnd, NULL );
+ }
+}
+
+
+bool PNS_DIFF_PAIR_PLACER::FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem )
+{
+ if( !m_fitOk )
+ return false;
+
+ if( m_currentTrace.CP().SegmentCount() < 1 ||
+ m_currentTrace.CN().SegmentCount() < 1 )
+ return false;
+
+ if( m_currentTrace.CP().SegmentCount() > 1 )
+ m_initialDiagonal = !DIRECTION_45( m_currentTrace.CP().CSegment( -2 ) ).IsDiagonal();
+
+ PNS_TOPOLOGY topo( m_lastNode );
+
+ if( !m_snapOnTarget && !m_currentTrace.EndsWithVias() )
+ {
+ SHAPE_LINE_CHAIN newP( m_currentTrace.CP() );
+ SHAPE_LINE_CHAIN newN( m_currentTrace.CN() );
+
+ if( newP.SegmentCount() > 1 && newN.SegmentCount() > 1 )
+ {
+ newP.Remove( -1, -1 );
+ newN.Remove( -1, -1 );
+ }
+
+ m_currentTrace.SetShape( newP, newN );
+ }
+
+ if( m_currentTrace.EndsWithVias() )
+ {
+ m_lastNode->Add( m_currentTrace.PLine().Via().Clone() );
+ m_lastNode->Add( m_currentTrace.NLine().Via().Clone() );
+ m_chainedPlacement = false;
+ }
+ else
+ {
+ m_chainedPlacement = !m_snapOnTarget;
+ }
+
+ PNS_LINE lineP( m_currentTrace.PLine() );
+ PNS_LINE lineN( m_currentTrace.NLine() );
+
+ m_lastNode->Add( &lineP );
+ m_lastNode->Add( &lineN );
+
+ topo.SimplifyLine( &lineP );
+ topo.SimplifyLine( &lineN );
+
+ m_prevPair = m_currentTrace.EndingPrimitives();
+
+ Router()->CommitRouting( m_lastNode );
+
+ m_lastNode = NULL;
+ m_placingVia = false;
+
+ if( m_snapOnTarget )
+ {
+ m_idle = true;
+ return true;
+ }
+ else
+ {
+ initPlacement();
+ return false;
+ }
+}
+
+
+void PNS_DIFF_PAIR_PLACER::GetModifiedNets( std::vector<int> &aNets ) const
+{
+ aNets.push_back( m_netP );
+ aNets.push_back( m_netN );
+}
+
+
+void PNS_DIFF_PAIR_PLACER::updateLeadingRatLine()
+{
+ SHAPE_LINE_CHAIN ratLineN, ratLineP;
+ PNS_TOPOLOGY topo( m_lastNode );
+
+ if( topo.LeadingRatLine( &m_currentTrace.PLine(), ratLineP ) )
+ {
+ Router()->DisplayDebugLine( ratLineP, 1, 10000 );
+ }
+
+ if( topo.LeadingRatLine ( &m_currentTrace.NLine(), ratLineN ) )
+ {
+ Router()->DisplayDebugLine( ratLineN, 3, 10000 );
+ }
+}
+
+
+const std::vector<int> PNS_DIFF_PAIR_PLACER::CurrentNets() const
+{
+ std::vector<int> rv;
+ rv.push_back( m_netP );
+ rv.push_back( m_netN );
+ return rv;
+}
diff --git a/pcbnew/router/pns_diff_pair_placer.h b/pcbnew/router/pns_diff_pair_placer.h
new file mode 100644
index 0000000..9bbed4c
--- /dev/null
+++ b/pcbnew/router/pns_diff_pair_placer.h
@@ -0,0 +1,299 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2014 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PNS_DIFF_PLACER_H
+#define __PNS_DIFF_PLACER_H
+
+#include <math/vector2d.h>
+
+#include <geometry/shape.h>
+#include <geometry/shape_line_chain.h>
+
+#include "pns_sizes_settings.h"
+#include "pns_node.h"
+#include "pns_via.h"
+#include "pns_line.h"
+#include "pns_algo_base.h"
+#include "pns_diff_pair.h"
+
+#include "pns_placement_algo.h"
+
+class PNS_ROUTER;
+class PNS_SHOVE;
+class PNS_OPTIMIZER;
+class PNS_ROUTER_BASE;
+class PNS_VIA;
+class PNS_SIZES_SETTINGS;
+
+
+/**
+ * Class PNS_LINE_PLACER
+ *
+ * Single track placement algorithm. Interactively routes a track.
+ * Applies shove and walkaround algorithms when needed.
+ */
+
+class PNS_DIFF_PAIR_PLACER : public PNS_PLACEMENT_ALGO
+{
+public:
+ PNS_DIFF_PAIR_PLACER( PNS_ROUTER* aRouter );
+ ~PNS_DIFF_PAIR_PLACER();
+
+ /**
+ * Function Start()
+ *
+ * Starts routing a single track at point aP, taking item aStartItem as anchor
+ * (unless NULL).
+ */
+ bool Start( const VECTOR2I& aP, PNS_ITEM* aStartItem );
+
+ /**
+ * Function Move()
+ *
+ * Moves the end of the currently routed trace to the point aP, taking
+ * aEndItem as anchor (if not NULL).
+ * (unless NULL).
+ */
+ bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem );
+
+ /**
+ * Function FixRoute()
+ *
+ * Commits the currently routed track to the parent node, taking
+ * aP as the final end point and aEndItem as the final anchor (if provided).
+ * @return true, if route has been commited. May return false if the routing
+ * result is violating design rules - in such case, the track is only committed
+ * if Settings.CanViolateDRC() is on.
+ */
+ bool FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem );
+
+ /**
+ * Function ToggleVia()
+ *
+ * Enables/disables a via at the end of currently routed trace.
+ */
+ bool ToggleVia( bool aEnabled );
+
+ /**
+ * Function SetLayer()
+ *
+ * Sets the current routing layer.
+ */
+ bool SetLayer( int aLayer );
+
+ /**
+ * Function Traces()
+ *
+ * Returns the complete routed line, as a single-member PNS_ITEMSET.
+ */
+ const PNS_ITEMSET Traces();
+
+ /**
+ * Function CurrentEnd()
+ *
+ * Returns the current end of the line being placed. It may not be equal
+ * to the cursor position due to collisions.
+ */
+ const VECTOR2I& CurrentEnd() const
+ {
+ return m_currentEnd;
+ }
+
+ /**
+ * Function CurrentNets()
+ *
+ * Returns the net code of currently routed track.
+ */
+ const std::vector<int> CurrentNets() const;
+
+ /**
+ * Function CurrentLayer()
+ *
+ * Returns the layer of currently routed track.
+ */
+ int CurrentLayer() const
+ {
+ return m_currentLayer;
+ }
+
+ /**
+ * Function CurrentNode()
+ *
+ * Returns the most recent world state.
+ */
+ PNS_NODE* CurrentNode( bool aLoopsRemoved = false ) const;
+
+ /**
+ * Function FlipPosture()
+ *
+ * Toggles the current posture (straight/diagonal) of the trace head.
+ */
+ void FlipPosture();
+
+ /**
+ * Function UpdateSizes()
+ *
+ * Performs on-the-fly update of the width, via diameter & drill size from
+ * a settings class. Used to dynamically change these parameters as
+ * the track is routed.
+ */
+ void UpdateSizes( const PNS_SIZES_SETTINGS& aSizes );
+
+ bool IsPlacingVia() const { return m_placingVia; }
+
+ void SetOrthoMode( bool aOrthoMode );
+
+ void GetModifiedNets( std::vector<int>& aNets ) const;
+
+private:
+ int viaGap() const;
+ int gap() const;
+
+ /**
+ * Function route()
+ *
+ * Re-routes the current track to point aP. Returns true, when routing has
+ * completed successfully (i.e. the trace end has reached point aP), and false
+ * if the trace was stuck somewhere on the way. May call routeStep()
+ * repetitively due to mouse smoothing.
+ * @param aP ending point of current route.
+ * @return true, if the routing is complete.
+ */
+ bool route( const VECTOR2I& aP );
+
+ /**
+ * Function updateLeadingRatLine()
+ *
+ * Draws the "leading" ratsnest line, which connects the end of currently
+ * routed track and the nearest yet unrouted item. If the routing for
+ * current net is complete, draws nothing.
+ */
+ void updateLeadingRatLine();
+
+ /**
+ * Function setWorld()
+ *
+ * Sets the board to route.
+ */
+ void setWorld( PNS_NODE* aWorld );
+
+ /**
+ * Function startPlacement()
+ *
+ * Initializes placement of a new line with given parameters.
+ */
+ void initPlacement( bool aSplitSeg = false );
+
+ /**
+ * Function setInitialDirection()
+ *
+ * Sets preferred direction of the very first track segment to be laid.
+ * Used by posture switching mechanism.
+ */
+ void setInitialDirection( const DIRECTION_45& aDirection );
+
+
+ bool routeHead( const VECTOR2I& aP );
+ bool tryWalkDp( PNS_NODE* aNode, PNS_DIFF_PAIR& aPair, bool aSolidsOnly );
+
+ ///> route step, walkaround mode
+ bool rhWalkOnly( const VECTOR2I& aP );
+
+ ///> route step, shove mode
+ bool rhShoveOnly ( const VECTOR2I& aP );
+
+ ///> route step, mark obstacles mode
+ bool rhMarkObstacles( const VECTOR2I& aP );
+
+ const PNS_VIA makeVia ( const VECTOR2I& aP, int aNet );
+
+ bool findDpPrimitivePair( const VECTOR2I& aP, PNS_ITEM* aItem, PNS_DP_PRIMITIVE_PAIR& aPair );
+ OPT_VECTOR2I getDanglingAnchor( PNS_NODE* aNode, PNS_ITEM* aItem );
+ int matchDpSuffix( wxString aNetName, wxString& aComplementNet, wxString& aBaseDpName );
+ bool attemptWalk( PNS_NODE* aNode, PNS_DIFF_PAIR* aCurrent, PNS_DIFF_PAIR& aWalk, bool aPFirst, bool aWindCw, bool aSolidsOnly );
+ bool propagateDpHeadForces ( const VECTOR2I& aP, VECTOR2I& aNewP );
+
+ enum State {
+ RT_START = 0,
+ RT_ROUTE = 1,
+ RT_FINISH = 2
+ };
+
+ State m_state;
+
+ bool m_chainedPlacement;
+ bool m_initialDiagonal;
+ bool m_startDiagonal;
+ bool m_fitOk;
+
+ int m_netP, m_netN;
+
+ PNS_DP_PRIMITIVE_PAIR m_start;
+ boost::optional<PNS_DP_PRIMITIVE_PAIR> m_prevPair;
+
+ ///> current algorithm iteration
+ int m_iteration;
+
+ ///> pointer to world to search colliding items
+ PNS_NODE* m_world;
+
+ ///> current routing start point (end of tail, beginning of head)
+ VECTOR2I m_p_start;
+
+ ///> The shove engine
+ PNS_SHOVE* m_shove;
+
+ ///> Current world state
+ PNS_NODE* m_currentNode;
+
+ ///> Postprocessed world state (including marked collisions & removed loops)
+ PNS_NODE* m_lastNode;
+
+ PNS_SIZES_SETTINGS m_sizes;
+
+ ///> Are we placing a via?
+ bool m_placingVia;
+
+ ///> current via diameter
+ int m_viaDiameter;
+
+ ///> current via drill
+ int m_viaDrill;
+
+ ///> current track width
+ int m_currentWidth;
+
+ int m_currentNet;
+ int m_currentLayer;
+
+ bool m_startsOnVia;
+ bool m_orthoMode;
+ bool m_snapOnTarget;
+
+ VECTOR2I m_currentEnd, m_currentStart;
+ PNS_DIFF_PAIR m_currentTrace;
+
+ PNS_ITEM* m_currentEndItem;
+ PNS_MODE m_currentMode;
+
+ bool m_idle;
+};
+
+#endif // __PNS_LINE_PLACER_H
diff --git a/pcbnew/router/pns_dp_meander_placer.cpp b/pcbnew/router/pns_dp_meander_placer.cpp
new file mode 100644
index 0000000..5179e19
--- /dev/null
+++ b/pcbnew/router/pns_dp_meander_placer.cpp
@@ -0,0 +1,406 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2014 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/foreach.hpp>
+#include <boost/optional.hpp>
+
+#include <base_units.h> // God forgive me doing this...
+#include <colors.h>
+
+#include "trace.h"
+
+#include "pns_node.h"
+#include "pns_itemset.h"
+#include "pns_topology.h"
+#include "pns_dp_meander_placer.h"
+#include "pns_diff_pair.h"
+#include "pns_router.h"
+#include "pns_utils.h"
+
+using boost::optional;
+
+PNS_DP_MEANDER_PLACER::PNS_DP_MEANDER_PLACER( PNS_ROUTER* aRouter ) :
+ PNS_MEANDER_PLACER_BASE( aRouter )
+{
+ m_world = NULL;
+ m_currentNode = NULL;
+
+ // Init temporary variables (do not leave uninitialized members)
+ m_initialSegment = NULL;
+ m_lastLength = 0;
+ m_lastStatus = TOO_SHORT;
+}
+
+
+PNS_DP_MEANDER_PLACER::~PNS_DP_MEANDER_PLACER()
+{
+}
+
+
+const PNS_LINE PNS_DP_MEANDER_PLACER::Trace() const
+{
+ return m_currentTraceP;
+}
+
+
+PNS_NODE* PNS_DP_MEANDER_PLACER::CurrentNode( bool aLoopsRemoved ) const
+{
+ if( !m_currentNode )
+ return m_world;
+
+ return m_currentNode;
+}
+
+
+bool PNS_DP_MEANDER_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem )
+{
+ VECTOR2I p;
+
+ if( !aStartItem || !aStartItem->OfKind( PNS_ITEM::SEGMENT ) )
+ {
+ Router()->SetFailureReason( _( "Please select a track whose length you want to tune." ) );
+ return false;
+ }
+
+ m_initialSegment = static_cast<PNS_SEGMENT*>( aStartItem );
+
+ p = m_initialSegment->Seg().NearestPoint( aP );
+
+ m_currentNode=NULL;
+ m_currentStart = p;
+
+ m_world = Router()->GetWorld()->Branch();
+
+ PNS_TOPOLOGY topo( m_world );
+
+ if( !topo.AssembleDiffPair( m_initialSegment, m_originPair ) )
+ {
+ Router()->SetFailureReason( _( "Unable to find complementary differential pair "
+ "net for length tuning. Make sure the names of the nets belonging "
+ "to a differential pair end with either _N/_P or +/-." ) );
+ return false;
+ }
+
+ if( m_originPair.Gap() < 0 )
+ m_originPair.SetGap( Router()->Sizes().DiffPairGap() );
+
+ if( !m_originPair.PLine().SegmentCount() ||
+ !m_originPair.NLine().SegmentCount() )
+ return false;
+
+ m_tunedPathP = topo.AssembleTrivialPath( m_originPair.PLine().GetLink( 0 ) );
+ m_tunedPathN = topo.AssembleTrivialPath( m_originPair.NLine().GetLink( 0 ) );
+
+ m_world->Remove( m_originPair.PLine() );
+ m_world->Remove( m_originPair.NLine() );
+
+ m_currentWidth = m_originPair.Width();
+
+ return true;
+}
+
+
+void PNS_DP_MEANDER_PLACER::release()
+{
+
+}
+
+
+int PNS_DP_MEANDER_PLACER::origPathLength() const
+{
+ int totalP = 0;
+ int totalN = 0;
+
+ BOOST_FOREACH( const PNS_ITEM* item, m_tunedPathP.CItems() )
+ {
+ if( const PNS_LINE* l = dyn_cast<const PNS_LINE*>( item ) )
+ totalP += l->CLine().Length();
+
+ }
+
+ BOOST_FOREACH( const PNS_ITEM* item, m_tunedPathN.CItems() )
+ {
+ if( const PNS_LINE* l = dyn_cast<const PNS_LINE*>( item ) )
+ totalN += l->CLine().Length();
+ }
+
+ return std::max( totalP, totalN );
+}
+
+
+const SEG PNS_DP_MEANDER_PLACER::baselineSegment( const PNS_DIFF_PAIR::COUPLED_SEGMENTS& aCoupledSegs )
+{
+ const VECTOR2I a( ( aCoupledSegs.coupledP.A + aCoupledSegs.coupledN.A ) / 2 );
+ const VECTOR2I b( ( aCoupledSegs.coupledP.B + aCoupledSegs.coupledN.B ) / 2 );
+
+ return SEG( a, b );
+}
+
+
+static bool pairOrientation( const PNS_DIFF_PAIR::COUPLED_SEGMENTS& aPair )
+{
+ VECTOR2I midp = ( aPair.coupledP.A + aPair.coupledN.A ) / 2;
+
+ //DrawDebugPoint (midp, 6);
+
+ return aPair.coupledP.Side( midp ) > 0;
+}
+
+
+bool PNS_DP_MEANDER_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem )
+{
+// return false;
+
+ PNS_DIFF_PAIR::COUPLED_SEGMENTS_VEC coupledSegments;
+
+ if( m_currentNode )
+ delete m_currentNode;
+
+ m_currentNode = m_world->Branch();
+
+ SHAPE_LINE_CHAIN preP, tunedP, postP;
+ SHAPE_LINE_CHAIN preN, tunedN, postN;
+
+ cutTunedLine( m_originPair.CP(), m_currentStart, aP, preP, tunedP, postP );
+ cutTunedLine( m_originPair.CN(), m_currentStart, aP, preN, tunedN, postN );
+
+ PNS_DIFF_PAIR tuned ( m_originPair );
+
+ tuned.SetShape( tunedP, tunedN );
+
+ tuned.CoupledSegmentPairs( coupledSegments );
+
+ if( coupledSegments.size() == 0 )
+ return false;
+
+ //Router()->DisplayDebugLine ( tuned.CP(), 5, 20000 );
+ //Router()->DisplayDebugLine ( tuned.CN(), 4, 20000 );
+
+ //Router()->DisplayDebugLine ( m_originPair.CP(), 5, 20000 );
+ //Router()->DisplayDebugLine ( m_originPair.CN(), 4, 20000 );
+
+ m_result = PNS_MEANDERED_LINE( this, true );
+ m_result.SetWidth( tuned.Width() );
+
+ int offset = ( tuned.Gap() + tuned.Width() ) / 2;
+
+ if( !pairOrientation( coupledSegments[0] ) )
+ offset *= -1;
+
+ m_result.SetBaselineOffset( offset );
+
+ BOOST_FOREACH( const PNS_ITEM* item, m_tunedPathP.CItems() )
+ {
+ if( const PNS_LINE* l = dyn_cast<const PNS_LINE*>( item ) )
+ Router()->DisplayDebugLine( l->CLine(), 5, 10000 );
+ }
+
+ BOOST_FOREACH( const PNS_ITEM* item, m_tunedPathN.CItems() )
+ {
+ if( const PNS_LINE* l = dyn_cast<const PNS_LINE*>( item ) )
+ Router()->DisplayDebugLine( l->CLine(), 5, 10000 );
+ }
+
+ int curIndexP = 0, curIndexN = 0;
+
+ BOOST_FOREACH( const PNS_DIFF_PAIR::COUPLED_SEGMENTS& sp, coupledSegments )
+ {
+ SEG base = baselineSegment( sp );
+
+ DrawDebugSeg( base, 3 );
+
+ while( sp.indexP >= curIndexP )
+ {
+ m_result.AddCorner( tunedP.CPoint( curIndexP ), tunedN.CPoint( curIndexN ) );
+ curIndexP++;
+ }
+
+ while( sp.indexN >= curIndexN )
+ {
+ m_result.AddCorner( tunedP.CPoint( sp.indexP ), tunedN.CPoint( curIndexN ) );
+ curIndexN++;
+ }
+
+ m_result.MeanderSegment( base );
+ }
+
+ while( curIndexP < tunedP.PointCount() )
+ m_result.AddCorner( tunedP.CPoint( curIndexP++ ), tunedN.CPoint( curIndexN ) );
+
+ while( curIndexN < tunedN.PointCount() )
+ m_result.AddCorner( tunedP.CPoint( -1 ), tunedN.CPoint( curIndexN++ ) );
+
+ int dpLen = origPathLength();
+
+ m_lastStatus = TUNED;
+
+ if( dpLen - m_settings.m_targetLength > m_settings.m_lengthTolerance )
+ {
+ m_lastStatus = TOO_LONG;
+ m_lastLength = dpLen;
+ }
+ else
+ {
+ m_lastLength = dpLen - std::max( tunedP.Length(), tunedN.Length() );
+ tuneLineLength( m_result, m_settings.m_targetLength - dpLen );
+ }
+
+ if( m_lastStatus != TOO_LONG )
+ {
+ tunedP.Clear();
+ tunedN.Clear();
+
+ BOOST_FOREACH( PNS_MEANDER_SHAPE* m, m_result.Meanders() )
+ {
+ if( m->Type() != MT_EMPTY )
+ {
+ tunedP.Append ( m->CLine( 0 ) );
+ tunedN.Append ( m->CLine( 1 ) );
+ }
+ }
+
+ m_lastLength += std::max( tunedP.Length(), tunedN.Length() );
+
+ int comp = compareWithTolerance( m_lastLength - m_settings.m_targetLength, 0, m_settings.m_lengthTolerance );
+
+ if( comp > 0 )
+ m_lastStatus = TOO_LONG;
+ else if( comp < 0 )
+ m_lastStatus = TOO_SHORT;
+ else
+ m_lastStatus = TUNED;
+ }
+
+ m_finalShapeP.Clear();
+ m_finalShapeP.Append( preP );
+ m_finalShapeP.Append( tunedP );
+ m_finalShapeP.Append( postP );
+ m_finalShapeP.Simplify();
+
+ m_finalShapeN.Clear();
+ m_finalShapeN.Append( preN );
+ m_finalShapeN.Append( tunedN );
+ m_finalShapeN.Append( postN );
+ m_finalShapeN.Simplify();
+
+ return true;
+}
+
+
+bool PNS_DP_MEANDER_PLACER::FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem )
+{
+ PNS_LINE lP( m_originPair.PLine(), m_finalShapeP );
+ PNS_LINE lN( m_originPair.NLine(), m_finalShapeN );
+
+ m_currentNode->Add( &lP );
+ m_currentNode->Add( &lN );
+
+ Router()->CommitRouting( m_currentNode );
+
+ return true;
+}
+
+
+bool PNS_DP_MEANDER_PLACER::CheckFit( PNS_MEANDER_SHAPE* aShape )
+{
+ PNS_LINE l1( m_originPair.PLine(), aShape->CLine( 0 ) );
+ PNS_LINE l2( m_originPair.NLine(), aShape->CLine( 1 ) );
+
+ if( m_currentNode->CheckColliding( &l1 ) )
+ return false;
+
+ if( m_currentNode->CheckColliding( &l2 ) )
+ return false;
+
+ int w = aShape->Width();
+ int clearance = w + m_settings.m_spacing;
+
+ return m_result.CheckSelfIntersections( aShape, clearance );
+}
+
+
+const PNS_ITEMSET PNS_DP_MEANDER_PLACER::Traces()
+{
+ m_currentTraceP = PNS_LINE( m_originPair.PLine(), m_finalShapeP );
+ m_currentTraceN = PNS_LINE( m_originPair.NLine(), m_finalShapeN );
+
+ PNS_ITEMSET traces;
+
+ traces.Add( &m_currentTraceP );
+ traces.Add( &m_currentTraceN );
+
+ return traces;
+}
+
+
+const VECTOR2I& PNS_DP_MEANDER_PLACER::CurrentEnd() const
+{
+ return m_currentEnd;
+}
+
+
+int PNS_DP_MEANDER_PLACER::CurrentLayer() const
+{
+ return m_initialSegment->Layers().Start();
+}
+
+
+const wxString PNS_DP_MEANDER_PLACER::TuningInfo() const
+{
+ wxString status;
+
+ switch( m_lastStatus )
+ {
+ case TOO_LONG:
+ status = _( "Too long: " );
+ break;
+ case TOO_SHORT:
+ status = _("Too short: " );
+ break;
+ case TUNED:
+ status = _( "Tuned: " );
+ break;
+ default:
+ return _( "?" );
+ }
+
+ status += LengthDoubleToString( (double) m_lastLength, false );
+ status += "/";
+ status += LengthDoubleToString( (double) m_settings.m_targetLength, false );
+ status += " (gap: ";
+ status += LengthDoubleToString( (double) m_originPair.Gap(), false );
+ status += ")";
+
+ return status;
+}
+
+
+PNS_DP_MEANDER_PLACER::TUNING_STATUS PNS_DP_MEANDER_PLACER::TuningStatus() const
+{
+ return m_lastStatus;
+}
+
+const std::vector<int> PNS_DP_MEANDER_PLACER::CurrentNets() const
+{
+ std::vector<int> rv;
+ rv.push_back( m_originPair.NetP() );
+ rv.push_back( m_originPair.NetN() );
+ return rv;
+}
diff --git a/pcbnew/router/pns_dp_meander_placer.h b/pcbnew/router/pns_dp_meander_placer.h
new file mode 100644
index 0000000..eb89e10
--- /dev/null
+++ b/pcbnew/router/pns_dp_meander_placer.h
@@ -0,0 +1,146 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2014 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PNS_DP_MEANDER_PLACER_H
+#define __PNS_DP_MEANDER_PLACER_H
+
+#include <math/vector2d.h>
+
+#include <geometry/shape.h>
+#include <geometry/shape_line_chain.h>
+
+#include "pns_node.h"
+#include "pns_via.h"
+#include "pns_line.h"
+#include "pns_placement_algo.h"
+#include "pns_meander.h"
+#include "pns_meander_placer_base.h"
+#include "pns_diff_pair.h"
+
+class PNS_ROUTER;
+class PNS_SHOVE;
+class PNS_OPTIMIZER;
+class PNS_ROUTER_BASE;
+
+/**
+ * Class PNS_DP_MEANDER_PLACER
+ *
+ * Differential Pair length-matching/meandering tool.
+ */
+
+class PNS_DP_MEANDER_PLACER : public PNS_MEANDER_PLACER_BASE
+{
+public:
+ PNS_DP_MEANDER_PLACER( PNS_ROUTER* aRouter );
+ ~PNS_DP_MEANDER_PLACER();
+
+ /**
+ * Function Start()
+ *
+ * Starts routing a single track at point aP, taking item aStartItem as anchor
+ * (unless NULL).
+ */
+ bool Start ( const VECTOR2I& aP, PNS_ITEM* aStartItem );
+
+ /**
+ * Function Move()
+ *
+ * Moves the end of the currently routed trace to the point aP, taking
+ * aEndItem as anchor (if not NULL).
+ * (unless NULL).
+ */
+ bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem );
+
+ /**
+ * Function FixRoute()
+ *
+ * Commits the currently routed track to the parent node, taking
+ * aP as the final end point and aEndItem as the final anchor (if provided).
+ * @return true, if route has been commited. May return false if the routing
+ * result is violating design rules - in such case, the track is only committed
+ * if Settings.CanViolateDRC() is on.
+ */
+ bool FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem );
+
+ const PNS_LINE Trace() const;
+
+ /**
+ * Function CurrentNode()
+ *
+ * Returns the most recent world state.
+ */
+ PNS_NODE* CurrentNode( bool aLoopsRemoved = false ) const;
+
+ const PNS_ITEMSET Traces();
+
+ const VECTOR2I& CurrentEnd() const;
+
+ /// @copydoc PNS_PLACEMENT_ALGO::CurrentNets()
+ const std::vector<int> CurrentNets() const;
+
+ int CurrentLayer() const;
+
+ int totalLength();
+
+ const wxString TuningInfo() const;
+ TUNING_STATUS TuningStatus() const;
+
+ bool CheckFit( PNS_MEANDER_SHAPE* aShape );
+
+
+private:
+ friend class PNS_MEANDER_SHAPE;
+
+ void meanderSegment ( const SEG& aBase );
+
+// void addMeander ( PNS_MEANDER *aM );
+// void addCorner ( const VECTOR2I& aP );
+
+ const SEG baselineSegment( const PNS_DIFF_PAIR::COUPLED_SEGMENTS& aCoupledSegs );
+
+ void setWorld( PNS_NODE* aWorld );
+ void release();
+
+ int origPathLength() const;
+
+ ///> pointer to world to search colliding items
+ PNS_NODE* m_world;
+
+ ///> current routing start point (end of tail, beginning of head)
+ VECTOR2I m_currentStart;
+
+ ///> Current world state
+ PNS_NODE* m_currentNode;
+
+ PNS_DIFF_PAIR m_originPair;
+ PNS_DIFF_PAIR::COUPLED_SEGMENTS_VEC m_coupledSegments;
+
+ PNS_LINE m_currentTraceN, m_currentTraceP;
+ PNS_ITEMSET m_tunedPath, m_tunedPathP, m_tunedPathN;
+
+ SHAPE_LINE_CHAIN m_finalShapeP, m_finalShapeN;
+ PNS_MEANDERED_LINE m_result;
+ PNS_SEGMENT* m_initialSegment;
+
+ int m_lastLength;
+ TUNING_STATUS m_lastStatus;
+};
+
+#endif // __PNS_DP_MEANDER_PLACER_H
diff --git a/pcbnew/router/pns_dragger.cpp b/pcbnew/router/pns_dragger.cpp
new file mode 100644
index 0000000..a30e7cd
--- /dev/null
+++ b/pcbnew/router/pns_dragger.cpp
@@ -0,0 +1,336 @@
+/*
+ * 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_lastNode = NULL;
+ m_mode = SEGMENT;
+ m_draggedVia = NULL;
+ m_shove = NULL;
+ m_draggedSegmentIndex = 0;
+ m_dragStatus = false;
+ m_currentMode = RM_MarkObstacles;
+ m_initialVia = 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() );
+
+ if( !jt )
+ return false;
+
+ BOOST_FOREACH( PNS_ITEM* item, jt->LinkList() )
+ {
+ if( item->OfKind( PNS_ITEM::SEGMENT ) )
+ {
+ int segIndex;
+ PNS_SEGMENT* seg = ( PNS_SEGMENT*) item;
+ PNS_LINE l = m_world->AssembleLine( seg, &segIndex );
+
+ if( segIndex != 0 )
+ l.Reverse();
+
+ m_origViaConnections.Add( 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 dragged( m_draggedLine );
+
+ if( m_mode == SEGMENT )
+ dragged.DragSegment( aP, m_draggedSegmentIndex, thresh );
+ else
+ dragged.DragCorner( aP, m_draggedSegmentIndex, thresh );
+
+ m_lastNode = m_shove->CurrentNode()->Branch();
+
+ m_lastValidDraggedLine = dragged;
+ m_lastValidDraggedLine.ClearSegmentLinks();
+ m_lastValidDraggedLine.Unmark();
+
+ m_lastNode->Add( &m_lastValidDraggedLine );
+ m_draggedItems.Clear();
+ m_draggedItems.Add( 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 )
+{
+ m_draggedItems.Clear();
+
+ // fixme: this is awful.
+ m_draggedVia = aVia->Clone();
+ m_draggedVia->SetPos( aP );
+
+ m_draggedItems.Add( m_draggedVia );
+
+ m_lastNode->Remove( aVia );
+ m_lastNode->Add( m_draggedVia );
+
+ BOOST_FOREACH( PNS_ITEM* item, m_origViaConnections.Items() )
+ {
+ if( const PNS_LINE* l = dyn_cast<const PNS_LINE*>( item ) )
+ {
+ PNS_LINE origLine( *l );
+ PNS_LINE draggedLine( *l );
+
+ draggedLine.DragCorner( aP, origLine.CLine().Find( aVia->Pos() ) );
+ draggedLine.ClearSegmentLinks();
+
+ m_draggedItems.Add( 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 dragged( m_draggedLine );
+
+ if( m_mode == SEGMENT )
+ dragged.DragSegment( aP, m_draggedSegmentIndex, thresh );
+ else
+ dragged.DragCorner( aP, m_draggedSegmentIndex, thresh );
+
+ PNS_SHOVE::SHOVE_STATUS st = m_shove->ShoveLines( dragged );
+
+ if( st == PNS_SHOVE::SH_OK )
+ ok = true;
+ else if( st == PNS_SHOVE::SH_HEAD_MODIFIED )
+ {
+ dragged = m_shove->NewHead();
+ ok = true;
+ }
+
+ m_lastNode = m_shove->CurrentNode()->Branch();
+
+ if( ok )
+ m_lastValidDraggedLine = dragged;
+
+ m_lastValidDraggedLine.ClearSegmentLinks();
+ m_lastValidDraggedLine.Unmark();
+ m_lastNode->Add( &m_lastValidDraggedLine );
+ m_draggedItems.Clear();
+ m_draggedItems.Add( m_lastValidDraggedLine );
+
+ break;
+ }
+
+ case VIA:
+ {
+ PNS_VIA* newVia;
+ PNS_SHOVE::SHOVE_STATUS 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;
+}
diff --git a/pcbnew/router/pns_dragger.h b/pcbnew/router/pns_dragger.h
new file mode 100644
index 0000000..ed6a59b
--- /dev/null
+++ b/pcbnew/router/pns_dragger.h
@@ -0,0 +1,126 @@
+/*
+ * 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:
+ 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;
+ PNS_ITEMSET m_origViaConnections;
+ PNS_VIA* m_initialVia;
+ PNS_ITEMSET m_draggedItems;
+};
+
+#endif
diff --git a/pcbnew/router/pns_index.h b/pcbnew/router/pns_index.h
new file mode 100644
index 0000000..6c895dc
--- /dev/null
+++ b/pcbnew/router/pns_index.h
@@ -0,0 +1,315 @@
+/*
+ * 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_INDEX_H
+#define __PNS_INDEX_H
+
+#include <layers_id_colors_and_visibility.h>
+#include <map>
+
+#include <boost/foreach.hpp>
+#include <boost/range/adaptor/map.hpp>
+
+#include <list>
+#include <geometry/shape_index.h>
+
+#include "pns_item.h"
+
+/**
+ * Class PNS_INDEX
+ *
+ * Custom spatial index, holding our board items and allowing for very fast searches. Items
+ * are assigned to separate R-Tree subindices depending on their type and spanned layers, reducing
+ * overlap and improving search time.
+ **/
+class PNS_INDEX
+{
+public:
+ typedef std::list<PNS_ITEM*> NET_ITEMS_LIST;
+ typedef SHAPE_INDEX<PNS_ITEM*> ITEM_SHAPE_INDEX;
+ typedef boost::unordered_set<PNS_ITEM*> ITEM_SET;
+
+ PNS_INDEX();
+ ~PNS_INDEX();
+
+ /**
+ * Function Add()
+ *
+ * Adds item to the spatial index.
+ */
+ void Add( PNS_ITEM* aItem );
+
+ /**
+ * Function Remove()
+ *
+ * Removes an item from the spatial index.
+ */
+ void Remove( PNS_ITEM* aItem );
+
+ /**
+ * Function Add()
+ *
+ * Replaces one item with another.
+ */
+ 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>
+ 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>
+ int Query( const SHAPE* aShape, int aMinDistance, Visitor& aVisitor );
+
+ /**
+ * Function Clear()
+ *
+ * Removes all items from the index.
+ */
+ void Clear();
+
+ /**
+ * Function GetItemsForNet()
+ *
+ * Returns list of all items in a given net.
+ */
+ NET_ITEMS_LIST* GetItemsForNet( int aNet );
+
+ /**
+ * Function Contains()
+ *
+ * Returns true if item aItem exists in the index.
+ */
+ bool Contains( PNS_ITEM* aItem ) const
+ {
+ 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(); }
+
+ ITEM_SET::iterator begin() { return m_allItems.begin(); }
+ ITEM_SET::iterator end() { return m_allItems.end(); }
+
+private:
+ static const int MaxSubIndices = 128;
+ static const int SI_Multilayer = 2;
+ static const int SI_SegDiagonal = 0;
+ static const int SI_SegStraight = 1;
+ static const int SI_Traces = 3;
+ static const int SI_PadsTop = 0;
+ static const int SI_PadsBottom = 1;
+
+ template <class Visitor>
+ int querySingle( int index, const SHAPE* aShape, int aMinDistance, Visitor& aVisitor );
+
+ ITEM_SHAPE_INDEX* getSubindex( const PNS_ITEM* aItem );
+
+ ITEM_SHAPE_INDEX* m_subIndices[MaxSubIndices];
+ std::map<int, NET_ITEMS_LIST> m_netMap;
+ ITEM_SET m_allItems;
+};
+
+PNS_INDEX::PNS_INDEX()
+{
+ memset( m_subIndices, 0, sizeof( m_subIndices ) );
+}
+
+PNS_INDEX::ITEM_SHAPE_INDEX* PNS_INDEX::getSubindex( const PNS_ITEM* aItem )
+{
+ int idx_n = -1;
+
+ const PNS_LAYERSET l = aItem->Layers();
+
+ switch( aItem->Kind() )
+ {
+ case PNS_ITEM::VIA:
+ idx_n = SI_Multilayer;
+ break;
+
+ case PNS_ITEM::SOLID:
+ {
+ if( l.IsMultilayer() )
+ idx_n = SI_Multilayer;
+ else if( l.Start() == B_Cu ) // fixme: use kicad layer codes
+ idx_n = SI_PadsTop;
+ else if( l.Start() == F_Cu )
+ idx_n = SI_PadsBottom;
+ }
+ break;
+
+ case PNS_ITEM::SEGMENT:
+ case PNS_ITEM::LINE:
+ idx_n = SI_Traces + 2 * l.Start() + SI_SegStraight;
+ break;
+
+ default:
+ break;
+ }
+
+ assert( idx_n >= 0 && idx_n < MaxSubIndices );
+
+ if( !m_subIndices[idx_n] )
+ m_subIndices[idx_n] = new ITEM_SHAPE_INDEX;
+
+ return m_subIndices[idx_n];
+}
+
+void PNS_INDEX::Add( PNS_ITEM* aItem )
+{
+ ITEM_SHAPE_INDEX* idx = getSubindex( aItem );
+
+ idx->Add( aItem );
+ m_allItems.insert( aItem );
+ int net = aItem->Net();
+
+ if( net >= 0 )
+ {
+ m_netMap[net].push_back( aItem );
+ }
+}
+
+void PNS_INDEX::Remove( PNS_ITEM* aItem )
+{
+ ITEM_SHAPE_INDEX* idx = getSubindex( aItem );
+
+ idx->Remove( aItem );
+ m_allItems.erase( aItem );
+
+ int net = aItem->Net();
+
+ if( net >= 0 && m_netMap.find( net ) != m_netMap.end() )
+ m_netMap[net].remove( aItem );
+}
+
+void PNS_INDEX::Replace( PNS_ITEM* aOldItem, PNS_ITEM* aNewItem )
+{
+ Remove( aOldItem );
+ Add( aNewItem );
+}
+
+template<class Visitor>
+int PNS_INDEX::querySingle( int index, const SHAPE* aShape, int aMinDistance, Visitor& aVisitor )
+{
+ if( !m_subIndices[index] )
+ return 0;
+
+ return m_subIndices[index]->Query( aShape, aMinDistance, aVisitor, false );
+}
+
+template<class Visitor>
+int PNS_INDEX::Query( const PNS_ITEM* aItem, int aMinDistance, Visitor& aVisitor )
+{
+ const SHAPE* shape = aItem->Shape();
+ int total = 0;
+
+ total += querySingle( SI_Multilayer, shape, aMinDistance, aVisitor );
+
+ const PNS_LAYERSET layers = aItem->Layers();
+
+ if( layers.IsMultilayer() )
+ {
+ total += querySingle( SI_PadsTop, shape, aMinDistance, aVisitor );
+ total += querySingle( SI_PadsBottom, shape, aMinDistance, aVisitor );
+
+ for( int i = layers.Start(); i <= layers.End(); ++i )
+ total += querySingle( SI_Traces + 2 * i + SI_SegStraight, shape, aMinDistance, aVisitor );
+ }
+ else
+ {
+ int l = layers.Start();
+
+ if( l == B_Cu )
+ total += querySingle( SI_PadsTop, shape, aMinDistance, aVisitor );
+ else if( l == F_Cu )
+ total += querySingle( SI_PadsBottom, shape, aMinDistance, aVisitor );
+
+ total += querySingle( SI_Traces + 2 * l + SI_SegStraight, shape, aMinDistance, aVisitor );
+ }
+
+ return total;
+}
+
+template<class Visitor>
+int PNS_INDEX::Query( const SHAPE* aShape, int aMinDistance, Visitor& aVisitor )
+{
+ int total = 0;
+
+ for( int i = 0; i < MaxSubIndices; i++ )
+ total += querySingle( i, aShape, aMinDistance, aVisitor );
+
+ return total;
+}
+
+void PNS_INDEX::Clear()
+{
+ for( int i = 0; i < MaxSubIndices; ++i )
+ {
+ ITEM_SHAPE_INDEX* idx = m_subIndices[i];
+
+ if( idx )
+ delete idx;
+
+ m_subIndices[i] = NULL;
+ }
+}
+
+PNS_INDEX::~PNS_INDEX()
+{
+ Clear();
+}
+
+PNS_INDEX::NET_ITEMS_LIST* PNS_INDEX::GetItemsForNet( int aNet )
+{
+ if( m_netMap.find( aNet ) == m_netMap.end() )
+ return NULL;
+
+ return &m_netMap[aNet];
+}
+
+#endif
diff --git a/pcbnew/router/pns_item.cpp b/pcbnew/router/pns_item.cpp
new file mode 100644
index 0000000..eb4b607
--- /dev/null
+++ b/pcbnew/router/pns_item.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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_item.h"
+#include "pns_line.h"
+
+bool PNS_ITEM::collideSimple( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV,
+ VECTOR2I& aMTV, bool aDifferentNetsOnly ) const
+{
+ // same nets? no collision!
+ if( aDifferentNetsOnly && m_net == aOther->m_net )
+ return false;
+
+ // check if we are not on completely different layers first
+ if( !m_layers.Overlaps( aOther->m_layers ) )
+ return false;
+
+ return Shape()->Collide( aOther->Shape(), aClearance );
+
+ // fixme: MTV
+}
+
+
+bool PNS_ITEM::Collide( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV,
+ VECTOR2I& aMTV, bool aDifferentNetsOnly ) const
+{
+ if( collideSimple( aOther, aClearance, aNeedMTV, aMTV, aDifferentNetsOnly ) )
+ return true;
+
+ // special case for "head" line with a via attached at the end.
+ if( aOther->m_kind == LINE )
+ {
+ const PNS_LINE* line = static_cast<const PNS_LINE*>( aOther );
+
+ if( line->EndsWithVia() )
+ return collideSimple( &line->Via(), aClearance - line->Width() / 2, aNeedMTV, aMTV, aDifferentNetsOnly );
+ }
+
+ return false;
+}
+
+
+const std::string PNS_ITEM::KindStr() const
+{
+ switch( m_kind )
+ {
+ case LINE:
+ return "line";
+
+ case SEGMENT:
+ return "segment";
+
+ case VIA:
+ return "via";
+
+ case JOINT:
+ return "joint";
+
+ case SOLID:
+ return "solid";
+
+ default:
+ return "unknown";
+ }
+}
+
+
+PNS_ITEM::~PNS_ITEM()
+{
+}
diff --git a/pcbnew/router/pns_item.h b/pcbnew/router/pns_item.h
new file mode 100644
index 0000000..a7511a7
--- /dev/null
+++ b/pcbnew/router/pns_item.h
@@ -0,0 +1,350 @@
+/*
+ * 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_ITEM_H
+#define __PNS_ITEM_H
+
+#include <math/vector2d.h>
+
+#include <geometry/shape.h>
+#include <geometry/shape_line_chain.h>
+
+#include "trace.h"
+
+#include "pns_layerset.h"
+
+class BOARD_CONNECTED_ITEM;
+class PNS_NODE;
+
+enum LineMarker {
+ MK_HEAD = ( 1 << 0 ),
+ MK_VIOLATION = ( 1 << 3 ),
+ MK_LOCKED = ( 1 << 4 ),
+ MK_DP_COUPLED = ( 1 << 5 )
+};
+
+
+/**
+ * Class PNS_ITEM
+ *
+ * Base class for PNS router board items. Implements the shared properties of all PCB items -
+ * net, spanned layers, geometric shape & refererence to owning model.
+ */
+class PNS_ITEM
+{
+public:
+ static const int UnusedNet = INT_MAX;
+
+ ///> Supported item types
+ enum PnsKind
+ {
+ SOLID = 1,
+ LINE = 2,
+ JOINT = 4,
+ SEGMENT = 8,
+ VIA = 16,
+ DIFF_PAIR = 32,
+ ANY = 0xff
+ };
+
+ PNS_ITEM( PnsKind aKind )
+ {
+ m_net = UnusedNet;
+ m_movable = true;
+ m_kind = aKind;
+ m_parent = NULL;
+ m_owner = NULL;
+ m_marker = 0;
+ m_rank = -1;
+ }
+
+ PNS_ITEM( const PNS_ITEM& aOther )
+ {
+ m_layers = aOther.m_layers;
+ m_net = aOther.m_net;
+ m_movable = aOther.m_movable;
+ m_kind = aOther.m_kind;
+ m_parent = aOther.m_parent;
+ m_owner = NULL;
+ m_marker = aOther.m_marker;
+ m_rank = aOther.m_rank;
+ }
+
+ virtual ~PNS_ITEM();
+
+ /**
+ * Function Clone()
+ *
+ * Returns a deep copy of the item
+ */
+ virtual PNS_ITEM* Clone() const = 0;
+
+ /*
+ * Function 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
+ {
+ return SHAPE_LINE_CHAIN();
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Function KindStr()
+ *
+ * Returns the kind of the item, as string
+ */
+ const std::string KindStr() const;
+
+ /**
+ * Function SetParent()
+ *
+ * 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;
+ }
+
+ /**
+ * Function SetNet()
+ *
+ * Sets the item's net to aNet
+ */
+ void SetNet( int aNet )
+ {
+ m_net = aNet;
+ }
+
+ /**
+ * Function Net()
+ *
+ * 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 )
+ {
+ m_layers = PNS_LAYERSET( aLayer, aLayer );
+ }
+
+ /**
+ * Function Layers()
+ *
+ * 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() );
+ }
+
+ /**
+ * Functon SetOwner()
+ *
+ * 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()
+ *
+ * @return 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; }
+
+ /**
+ * 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
+ * @return true, if a collision was found.
+ */
+ virtual bool Collide( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV,
+ VECTOR2I& aMTV, bool aDifferentNetsOnly = true ) const;
+
+ /**
+ * Function Collide()
+ *
+ * A shortcut for PNS_ITEM::Colllide() without MTV stuff.
+ */
+ bool Collide( const PNS_ITEM* aOther, int aClearance, bool aDifferentNetsOnly = true ) const
+ {
+ VECTOR2I dummy;
+
+ return Collide( aOther, aClearance, false, dummy, aDifferentNetsOnly );
+ }
+
+ /**
+ * Function Shape()
+ *
+ * Returns the geometrical shape of the item. Used
+ * for collision detection & spatial indexing.
+ */
+ virtual const SHAPE* Shape() const
+ {
+ 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:
+ bool collideSimple( const PNS_ITEM* aOther, int aClearance, bool aNeedMTV,
+ VECTOR2I& aMTV, bool aDifferentNetsOnly ) const;
+
+protected:
+ PnsKind m_kind;
+
+ BOARD_CONNECTED_ITEM* m_parent;
+ PNS_NODE* m_owner;
+ PNS_LAYERSET m_layers;
+
+ bool m_movable;
+ int m_net;
+ int m_marker;
+ int m_rank;
+};
+
+#endif // __PNS_ITEM_H
diff --git a/pcbnew/router/pns_itemset.cpp b/pcbnew/router/pns_itemset.cpp
new file mode 100644
index 0000000..06561a7
--- /dev/null
+++ b/pcbnew/router/pns_itemset.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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_itemset.h"
+#include "pns_line.h"
+
+
+PNS_ITEMSET::~PNS_ITEMSET()
+{
+}
+
+
+void PNS_ITEMSET::Add( const PNS_LINE& aLine )
+{
+ PNS_LINE* copy = aLine.Clone();
+ m_items.push_back( ENTRY( copy, true ) );
+}
+
+
+void PNS_ITEMSET::Prepend( const PNS_LINE& aLine )
+{
+ PNS_LINE* copy = aLine.Clone();
+ m_items.insert( m_items.begin(), ENTRY( copy, true ) );
+}
+
+
+PNS_ITEMSET& PNS_ITEMSET::FilterLayers( int aStart, int aEnd, bool aInvert )
+{
+ ENTRIES newItems;
+ PNS_LAYERSET l;
+
+ if( aEnd < 0 )
+ l = PNS_LAYERSET( aStart );
+ else
+ l = PNS_LAYERSET( aStart, aEnd );
+
+ BOOST_FOREACH( const ENTRY& ent, m_items )
+ {
+ if( ent.item->Layers().Overlaps( l ) ^ aInvert )
+ {
+ newItems.push_back( ent );
+ }
+ }
+
+ m_items = newItems;
+
+ return *this;
+}
+
+
+PNS_ITEMSET& PNS_ITEMSET::FilterKinds( int aKindMask, bool aInvert )
+{
+ ENTRIES newItems;
+
+ BOOST_FOREACH( const ENTRY& ent, m_items )
+ {
+ if( ent.item->OfKind( aKindMask ) ^ aInvert )
+ {
+ newItems.push_back( ent );
+ }
+ }
+
+ m_items = newItems;
+
+ return *this;
+}
+
+
+PNS_ITEMSET& PNS_ITEMSET::FilterMarker( int aMarker, bool aInvert )
+{
+ ENTRIES newItems;
+
+ BOOST_FOREACH( const ENTRY& ent, m_items )
+ {
+ if( ent.item->Marker() & aMarker )
+ {
+ newItems.push_back( ent );
+ }
+ }
+
+ m_items = newItems;
+
+ return *this;
+}
+
+
+PNS_ITEMSET& PNS_ITEMSET::FilterNet( int aNet, bool aInvert )
+{
+ ENTRIES newItems;
+
+ BOOST_FOREACH( const ENTRY& ent, m_items )
+ {
+ if( ( ent.item->Net() == aNet ) ^ aInvert )
+ {
+ newItems.push_back( ent );
+ }
+ }
+
+ m_items = newItems;
+
+ return *this;
+}
+
+
+PNS_ITEMSET& PNS_ITEMSET::ExcludeItem( const PNS_ITEM* aItem )
+{
+ ENTRIES newItems;
+
+ BOOST_FOREACH( const ENTRY& ent, m_items )
+ {
+ if( ent.item != aItem )
+
+ newItems.push_back( ent );
+ }
+
+ m_items = newItems;
+
+ return *this;
+}
diff --git a/pcbnew/router/pns_itemset.h b/pcbnew/router/pns_itemset.h
new file mode 100644
index 0000000..728d0ae
--- /dev/null
+++ b/pcbnew/router/pns_itemset.h
@@ -0,0 +1,227 @@
+/*
+ * 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_ITEMSET_H
+#define __PNS_ITEMSET_H
+
+#include <vector>
+#include <boost/foreach.hpp>
+
+#include "pns_item.h"
+
+/**
+ * Class PNS_ITEMSET
+ *
+ * Holds a list of board items, that can be filtered against net, kinds,
+ * layers, etc.
+ **/
+class PNS_LINE;
+
+class PNS_ITEMSET
+{
+public:
+ struct ENTRY {
+
+ ENTRY( PNS_ITEM* aItem, bool aOwned = false ) :
+ item( aItem ),
+ owned( aOwned )
+ {}
+
+ ENTRY( const ENTRY& aOther )
+ {
+ owned = aOther.owned;
+
+ if( aOther.owned )
+ item = aOther.item->Clone();
+ else
+ item = aOther.item;
+ }
+
+ ~ENTRY()
+ {
+ if( owned )
+ delete item;
+ }
+
+ bool operator== ( const ENTRY& b ) const
+ {
+ return item == b.item;
+ }
+
+ bool operator< ( const ENTRY& b ) const
+ {
+ return item < b.item;
+ }
+
+ ENTRY& operator= ( const ENTRY& aOther )
+ {
+ owned = aOther.owned;
+
+ if( aOther.owned )
+ item = aOther.item->Clone();
+ else
+ item = aOther.item;
+
+ return *this;
+ }
+
+ operator PNS_ITEM* () const
+ {
+ return item;
+ }
+
+ PNS_ITEM *item;
+ bool owned;
+ };
+
+ typedef std::vector<ENTRY> ENTRIES;
+
+ PNS_ITEMSET( PNS_ITEM* aInitialItem = NULL, bool aBecomeOwner = false )
+ {
+ if( aInitialItem )
+ {
+ m_items.push_back( ENTRY( aInitialItem, aBecomeOwner ) );
+ }
+ }
+
+ PNS_ITEMSET( const PNS_ITEMSET& aOther )
+ {
+ m_items = aOther.m_items;
+ }
+
+ ~PNS_ITEMSET();
+
+ const PNS_ITEMSET& operator=( const PNS_ITEMSET& aOther )
+ {
+ m_items = aOther.m_items;
+ return *this;
+ }
+
+ int Count( int aKindMask = -1 ) const
+ {
+ int n = 0;
+
+ BOOST_FOREACH( PNS_ITEM* item, m_items )
+ {
+ if( item->Kind() & aKindMask )
+ n++;
+ }
+
+ return n;
+ }
+
+ bool Empty() const
+ {
+ return m_items.empty();
+ }
+
+ ENTRIES& Items() { return m_items; }
+ const ENTRIES& CItems() const { return m_items; }
+
+ PNS_ITEMSET& FilterLayers( int aStart, int aEnd = -1, bool aInvert = false );
+ PNS_ITEMSET& FilterKinds( int aKindMask, bool aInvert = false );
+ PNS_ITEMSET& FilterNet( int aNet, bool aInvert = false );
+ PNS_ITEMSET& FilterMarker( int aMarker, bool aInvert = false );
+
+ PNS_ITEMSET& ExcludeLayers( int aStart, int aEnd = -1 )
+ {
+ return FilterLayers( aStart, aEnd, true );
+ }
+
+ PNS_ITEMSET& ExcludeKinds( int aKindMask )
+ {
+ return FilterKinds( aKindMask, true );
+ }
+
+ PNS_ITEMSET& ExcludeNet( int aNet )
+ {
+ return FilterNet( aNet, true );
+ }
+
+ PNS_ITEMSET& ExcludeItem( const PNS_ITEM* aItem );
+
+ int Size() const
+ {
+ return m_items.size();
+ }
+
+ void Add( const PNS_LINE& aLine );
+ void Prepend( const PNS_LINE& aLine );
+
+ PNS_ITEM* operator[] ( int index ) const
+ {
+ return m_items[index].item;
+ }
+
+ void Add( PNS_ITEM* aItem, bool aBecomeOwner = false )
+ {
+ m_items.push_back( ENTRY( aItem, aBecomeOwner ) );
+ }
+
+ void Prepend( PNS_ITEM* aItem, bool aBecomeOwner = false )
+ {
+ m_items.insert( m_items.begin(), ENTRY( aItem, aBecomeOwner ) );
+ }
+
+ void Clear()
+ {
+ m_items.clear();
+ }
+
+ bool Contains( PNS_ITEM* aItem ) const
+ {
+ const ENTRY ent( aItem );
+ return std::find( m_items.begin(), m_items.end(), ent ) != m_items.end();
+ }
+
+ void Erase( PNS_ITEM* aItem )
+ {
+ ENTRY ent( aItem );
+ ENTRIES::iterator f = std::find( m_items.begin(), m_items.end(), ent );
+
+ if( f != m_items.end() )
+ m_items.erase( f );
+ }
+
+ template<class T>
+ T* FindByKind( PNS_ITEM::PnsKind kind, int index = 0 )
+ {
+ int n = 0;
+
+ BOOST_FOREACH( const PNS_ITEM* item, m_items )
+ {
+ if( item->OfKind( kind ) )
+ {
+ if( index == n )
+ return static_cast<T*>( item );
+ else
+ n++;
+ }
+ }
+
+ return NULL;
+ }
+
+private:
+
+ ENTRIES m_items;
+};
+
+#endif
diff --git a/pcbnew/router/pns_joint.h b/pcbnew/router/pns_joint.h
new file mode 100644
index 0000000..eab4a56
--- /dev/null
+++ b/pcbnew/router/pns_joint.h
@@ -0,0 +1,259 @@
+/*
+ * 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_JOINT_H
+#define __PNS_JOINT_H
+
+#include <vector>
+#include <boost/functional/hash.hpp>
+
+#include <math/vector2d.h>
+
+#include "pns_item.h"
+#include "pns_segment.h"
+#include "pns_itemset.h"
+
+/**
+ * Class PNS_JOINT
+ *
+ * Represents a 2D point on a given set of layers and belonging to a certain
+ * net, that links together a number of board items.
+ * A hash table of joints is used by the router to follow connectivity between
+ * the items.
+ **/
+class PNS_JOINT : public PNS_ITEM
+{
+public:
+ typedef PNS_ITEMSET::ENTRIES LINKED_ITEMS;
+
+ ///> Joints are hashed by their position, layers and net.
+ /// Linked items are, obviously, not hashed
+ struct HASH_TAG
+ {
+ VECTOR2I pos;
+ int net;
+ };
+
+ PNS_JOINT() :
+ PNS_ITEM( JOINT ), m_locked( false ) {}
+
+ PNS_JOINT( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers, int aNet = -1 ) :
+ PNS_ITEM( JOINT )
+ {
+ m_tag.pos = aPos;
+ m_tag.net = aNet;
+ m_layers = aLayers;
+ m_locked = false;
+ }
+
+ PNS_JOINT( const PNS_JOINT& aB ) :
+ PNS_ITEM( JOINT )
+ {
+ m_layers = aB.m_layers;
+ m_tag.pos = aB.m_tag.pos;
+ m_tag.net = aB.m_tag.net;
+ m_linkedItems = aB.m_linkedItems;
+ m_layers = aB.m_layers;
+ m_locked = aB.m_locked;
+ }
+
+ PNS_ITEM* Clone( ) const
+ {
+ assert( false );
+ return NULL;
+ }
+
+ ///> Returns true if the joint is a trivial line corner, connecting two
+ /// segments of the same net, on the same layer.
+ bool IsLineCorner() const
+ {
+ if( m_linkedItems.Size() != 2 || m_linkedItems.Count( SEGMENT ) != 2 )
+ return false;
+
+ PNS_SEGMENT* seg1 = static_cast<PNS_SEGMENT*>( m_linkedItems[0] );
+ PNS_SEGMENT* seg2 = static_cast<PNS_SEGMENT*>( m_linkedItems[1] );
+
+ // joints between segments of different widths are not considered trivial.
+ return seg1->Width() == seg2->Width();
+ }
+
+ bool IsNonFanoutVia() const
+ {
+ int vias = m_linkedItems.Count( VIA );
+ int segs = m_linkedItems.Count( SEGMENT );
+
+ return ( m_linkedItems.Size() == 3 && vias == 1 && segs == 2 );
+ }
+
+ bool IsTraceWidthChange() const
+ {
+ if( m_linkedItems.Size() != 2 )
+ return false;
+
+ if( m_linkedItems.Count( SEGMENT ) != 2)
+ return false;
+
+ PNS_SEGMENT* seg1 = static_cast<PNS_SEGMENT*>( m_linkedItems[0] );
+ PNS_SEGMENT* seg2 = static_cast<PNS_SEGMENT*>( m_linkedItems[1] );
+
+ return seg1->Width() != seg2->Width();
+ }
+
+ ///> Links the joint to a given board item (when it's added to the PNS_NODE)
+ void Link( PNS_ITEM* aItem )
+ {
+ if( m_linkedItems.Contains( aItem ) )
+ return;
+
+ m_linkedItems.Add( aItem );
+ }
+
+ ///> Unlinks a given board item from the joint (upon its removal from a PNS_NODE)
+ ///> Returns true if the joint became dangling after unlinking.
+ bool Unlink( PNS_ITEM* aItem )
+ {
+ m_linkedItems.Erase( aItem );
+ return m_linkedItems.Size() == 0;
+ }
+
+ ///> For trivial joints, returns the segment adjacent to (aCurrent). For non-trival ones, returns
+ ///> NULL, indicating the end of line.
+ PNS_SEGMENT* NextSegment( PNS_SEGMENT* aCurrent ) const
+ {
+ if( !IsLineCorner() )
+ return NULL;
+
+ return static_cast<PNS_SEGMENT*>( m_linkedItems[m_linkedItems[0] == aCurrent ? 1 : 0] );
+ }
+
+ PNS_VIA* Via()
+ {
+ BOOST_FOREACH( PNS_ITEM* item, m_linkedItems.Items() )
+ {
+ if( item->OfKind( VIA ) )
+ return static_cast<PNS_VIA*>( item );
+ }
+
+ return NULL;
+ }
+
+
+ /// trivial accessors
+ const HASH_TAG& Tag() const
+ {
+ return m_tag;
+ }
+
+ const VECTOR2I& Pos() const
+ {
+ return m_tag.pos;
+ }
+
+ int Net() const
+ {
+ return m_tag.net;
+ }
+
+ const LINKED_ITEMS& LinkList() const
+ {
+ return m_linkedItems.CItems();
+ }
+
+ const PNS_ITEMSET& CLinks() const
+ {
+ return m_linkedItems;
+ }
+
+ PNS_ITEMSET& Links()
+ {
+ return m_linkedItems;
+ }
+
+ int LinkCount( int aMask = -1 ) const
+ {
+ return m_linkedItems.Count( aMask );
+ }
+
+ void Dump() const;
+
+ bool operator==( const PNS_JOINT& rhs ) const
+ {
+ return m_tag.pos == rhs.m_tag.pos && m_tag.net == rhs.m_tag.net;
+ }
+
+ void Merge( const PNS_JOINT& aJoint )
+ {
+ if( !Overlaps( aJoint ) )
+ return;
+
+ m_layers.Merge( aJoint.m_layers );
+
+ if( aJoint.IsLocked() )
+ m_locked = true;
+
+ BOOST_FOREACH( PNS_ITEM* item, aJoint.LinkList() )
+ {
+ m_linkedItems.Add( item );
+ }
+ }
+
+ bool Overlaps( const PNS_JOINT& rhs ) const
+ {
+ return m_tag.pos == rhs.m_tag.pos &&
+ m_tag.net == rhs.m_tag.net && m_layers.Overlaps( rhs.m_layers );
+ }
+
+ void Lock( bool aLock = true )
+ {
+ m_locked = aLock;
+ }
+
+ bool IsLocked() const
+ {
+ return m_locked;
+ }
+
+private:
+ ///> hash tag for unordered_multimap
+ HASH_TAG m_tag;
+
+ ///> list of items linked to this joint
+ PNS_ITEMSET m_linkedItems;
+
+ ///> locked (non-movable) flag
+ bool m_locked;
+};
+
+inline bool operator==( PNS_JOINT::HASH_TAG const& aP1, PNS_JOINT::HASH_TAG const& aP2 )
+{
+ return aP1.pos == aP2.pos && aP1.net == aP2.net;
+}
+
+inline std::size_t hash_value( PNS_JOINT::HASH_TAG const& aP )
+{
+ std::size_t seed = 0;
+ boost::hash_combine( seed, aP.pos.x );
+ boost::hash_combine( seed, aP.pos.y );
+ boost::hash_combine( seed, aP.net );
+
+ return seed;
+}
+
+#endif // __PNS_JOINT_H
diff --git a/pcbnew/router/pns_layerset.h b/pcbnew/router/pns_layerset.h
new file mode 100644
index 0000000..9d01dd1
--- /dev/null
+++ b/pcbnew/router/pns_layerset.h
@@ -0,0 +1,129 @@
+/*
+ * 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_LAYERSET_H
+#define __PNS_LAYERSET_H
+
+#include <algorithm>
+
+/**
+ * Class PNS_LAYERSET
+ *
+ * Represents a contiguous set of PCB layers.
+ */
+class PNS_LAYERSET
+{
+public:
+ PNS_LAYERSET() :
+ m_start( -1 ),
+ m_end( -1 )
+ {};
+
+ PNS_LAYERSET( int aStart, int aEnd )
+ {
+ if( aStart > aEnd )
+ std::swap( aStart, aEnd );
+
+ m_start = aStart;
+ m_end = aEnd;
+ }
+
+ PNS_LAYERSET( int aLayer )
+ {
+ m_start = m_end = aLayer;
+ }
+
+ PNS_LAYERSET( const PNS_LAYERSET& aB ) :
+ m_start( aB.m_start ),
+ m_end( aB.m_end )
+ {}
+
+ ~PNS_LAYERSET() {};
+
+ const PNS_LAYERSET& operator=( const PNS_LAYERSET& aB )
+ {
+ m_start = aB.m_start;
+ m_end = aB.m_end;
+ return *this;
+ }
+
+ bool Overlaps( const PNS_LAYERSET& aOther ) const
+ {
+ return m_end >= aOther.m_start && m_start <= aOther.m_end;
+ }
+
+ bool Overlaps( const int aLayer ) const
+ {
+ return aLayer >= m_start && aLayer <= m_end;
+ }
+
+ bool IsMultilayer() const
+ {
+ return m_start != m_end;
+ }
+
+ int Start() const
+ {
+ return m_start;
+ }
+
+ int End() const
+ {
+ return m_end;
+ }
+
+ void Merge( const PNS_LAYERSET& aOther )
+ {
+ if( m_start < 0 || m_end < 0 )
+ {
+ m_start = aOther.m_start;
+ m_end = aOther.m_end;
+ return;
+ }
+
+ if( aOther.m_start < m_start )
+ m_start = aOther.m_start;
+
+ if( aOther.m_end > m_end )
+ m_end = aOther.m_end;
+ }
+
+ ///> Shortcut for comparisons/overlap tests
+ static PNS_LAYERSET All()
+ {
+ return PNS_LAYERSET( 0, 256 ); // fixme: use layer IDs header
+ }
+
+ bool operator==( const PNS_LAYERSET& aOther ) const
+ {
+ return ( m_start == aOther.m_start ) && ( m_end == aOther.m_end );
+ }
+
+ bool operator!=( const PNS_LAYERSET& aOther ) const
+ {
+ return ( m_start != aOther.m_start ) || ( m_end != aOther.m_end );
+ }
+
+private:
+ int m_start;
+ int m_end;
+};
+
+#endif // __PNS_LAYERSET_H
diff --git a/pcbnew/router/pns_line.cpp b/pcbnew/router/pns_line.cpp
new file mode 100644
index 0000000..39d5f5c
--- /dev/null
+++ b/pcbnew/router/pns_line.cpp
@@ -0,0 +1,889 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2014 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/foreach.hpp>
+#include <boost/optional.hpp>
+
+#include <math/vector2d.h>
+
+#include "pns_line.h"
+#include "pns_node.h"
+#include "pns_via.h"
+#include "pns_utils.h"
+#include "pns_router.h"
+
+#include <geometry/shape_rect.h>
+
+using boost::optional;
+
+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_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()
+{
+ delete m_segmentRefs;
+}
+
+
+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_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;
+}
+
+
+void PNS_LINE::Mark( int aMarker )
+{
+ m_marker = aMarker;
+
+ if( m_segmentRefs )
+ {
+ BOOST_FOREACH( PNS_SEGMENT* s, *m_segmentRefs )
+ s->Mark( aMarker );
+ }
+}
+
+
+void PNS_LINE::Unmark()
+{
+ if( m_segmentRefs )
+ {
+ BOOST_FOREACH( PNS_SEGMENT* s, *m_segmentRefs )
+ s->Unmark();
+ }
+
+ m_marker = 0;
+}
+
+
+int PNS_LINE::Marker() const
+{
+ int marker = m_marker;
+
+ if( m_segmentRefs )
+ {
+ BOOST_FOREACH( PNS_SEGMENT* s, *m_segmentRefs )
+ {
+ marker |= s->Marker();
+ }
+ }
+
+ return marker;
+}
+
+
+void PNS_LINE::copyLinks( const PNS_LINE* aParent )
+{
+ if( aParent->m_segmentRefs == NULL )
+ {
+ m_segmentRefs = NULL;
+ return;
+ }
+
+ m_segmentRefs = new SEGMENT_REFS();
+ *m_segmentRefs = *aParent->m_segmentRefs;
+}
+
+
+PNS_SEGMENT* PNS_SEGMENT::Clone() const
+{
+ PNS_SEGMENT* s = new PNS_SEGMENT;
+
+ s->m_seg = m_seg;
+ s->m_net = m_net;
+ s->m_layers = m_layers;
+ s->m_marker = m_marker;
+ s->m_rank = m_rank;
+
+ return s;
+}
+
+
+int PNS_LINE::CountCorners( int aAngles )
+{
+ int count = 0;
+
+ 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 );
+
+ const DIRECTION_45 dir1( seg1 );
+ const DIRECTION_45 dir2( seg2 );
+
+ DIRECTION_45::AngleType a = dir1.Angle( dir2 );
+
+ if( a & aAngles )
+ count++;
+ }
+
+ return count;
+}
+
+
+bool PNS_LINE::Walkaround( SHAPE_LINE_CHAIN aObstacle, SHAPE_LINE_CHAIN& aPre,
+ SHAPE_LINE_CHAIN& aWalk, SHAPE_LINE_CHAIN& aPost, bool aCw ) const
+{
+ const SHAPE_LINE_CHAIN& line( CLine() );
+ VECTOR2I ip_start;
+ VECTOR2I ip_end;
+
+ if( line.SegmentCount() < 1 )
+ return false;
+
+ if( aObstacle.PointInside( line.CPoint( 0 ) ) || aObstacle.PointInside( line.CPoint( -1 ) ) )
+ return false;
+
+ SHAPE_LINE_CHAIN::INTERSECTIONS ips, ips2;
+
+ line.Intersect( aObstacle, ips );
+
+ aWalk.Clear();
+ aPost.Clear();
+
+ int nearest_dist = INT_MAX;
+ int farthest_dist = 0;
+
+ SHAPE_LINE_CHAIN::INTERSECTION nearest, farthest;
+
+ for( int i = 0; i < (int) ips.size(); i++ )
+ {
+ const VECTOR2I p = ips[i].p;
+ int dist = line.PathLength( p );
+
+ if( dist < 0 )
+ return false;
+
+ if( dist <= nearest_dist )
+ {
+ nearest_dist = dist;
+ nearest = ips[i];
+ }
+
+ if( dist >= farthest_dist )
+ {
+ farthest_dist = dist;
+ farthest = ips[i];
+ }
+ }
+
+ if( ips.size() <= 1 || nearest.p == farthest.p )
+ {
+ aPre = line;
+ return true;
+ }
+
+ aPre = line.Slice( 0, nearest.our.Index() );
+ aPre.Append( nearest.p );
+ aPre.Simplify();
+
+ aWalk.Clear();
+ aWalk.SetClosed( false );
+ aWalk.Append( nearest.p );
+
+ assert( nearest.their.Index() >= 0 );
+ assert( farthest.their.Index() >= 0 );
+
+ assert( nearest_dist <= farthest_dist );
+
+ aObstacle.Split( nearest.p );
+ aObstacle.Split( farthest.p );
+
+ int i_first = aObstacle.Find( nearest.p );
+ int i_last = aObstacle.Find( farthest.p );
+
+ int i = i_first;
+
+ while( i != i_last )
+ {
+ aWalk.Append( aObstacle.CPoint( i ) );
+ i += ( aCw ? 1 : -1 );
+
+ if( i < 0 )
+ i = aObstacle.PointCount() - 1;
+ else if( i == aObstacle.PointCount() )
+ i = 0;
+ }
+
+ aWalk.Append( farthest.p );
+ aWalk.Simplify();
+
+ aPost.Clear();
+ aPost.Append( farthest.p );
+ aPost.Append( line.Slice( farthest.our.Index() + 1, -1 ) );
+ aPost.Simplify();
+
+ return true;
+}
+
+
+void PNS_LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle,
+ SHAPE_LINE_CHAIN& aPath,
+ bool aCw ) const
+{
+ SHAPE_LINE_CHAIN walk, post;
+
+ Walkaround( aObstacle, aPath, walk, post, aCw );
+ aPath.Append( walk );
+ aPath.Append( post );
+ aPath.Simplify();
+}
+
+
+const SHAPE_LINE_CHAIN PNS_SEGMENT::Hull( int aClearance, int aWalkaroundThickness ) const
+{
+ return SegmentHull ( m_seg, aClearance, aWalkaroundThickness );
+}
+
+
+bool PNS_LINE::Is45Degree()
+{
+ for( int i = 0; i < m_line.SegmentCount(); i++ )
+ {
+ const SEG& s = m_line.CSegment( i );
+
+ if( s.Length() < 10 )
+ continue;
+
+ 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;
+}
+
+
+const PNS_LINE PNS_LINE::ClipToNearestObstacle( PNS_NODE* aNode ) const
+{
+ const int IterationLimit = 5;
+ int i;
+ PNS_LINE l( *this );
+
+ for( i = 0; i < IterationLimit; i++ )
+ {
+ PNS_NODE::OPT_OBSTACLE obs = aNode->NearestObstacle( &l );
+
+ if( obs )
+ {
+ l.RemoveVia();
+ int p = l.Line().Split( obs->m_ipFirst );
+ l.Line().Remove( p + 1, -1 );
+ } else
+ break;
+ }
+
+ if( i == IterationLimit )
+ l.Line().Clear();
+
+ return l;
+}
+
+
+void PNS_LINE::ShowLinks()
+{
+ if( !m_segmentRefs )
+ {
+ printf( "line %p: no links\n", this );
+ return;
+ }
+
+ printf( "line %p: %d linked segs\n", this, (int) m_segmentRefs->size() );
+
+ for( int i = 0; i < (int) m_segmentRefs->size(); i++ )
+ printf( "seg %d: %p\n", i, (*m_segmentRefs)[i] );
+}
+
+SHAPE_LINE_CHAIN dragCornerInternal( const SHAPE_LINE_CHAIN& aOrigin, const VECTOR2I& aP )
+{
+ optional<SHAPE_LINE_CHAIN> picked;
+ int i;
+ int d = 2;
+
+ if( aOrigin.SegmentCount() == 1)
+ {
+ DIRECTION_45 dir( aOrigin.CPoint( 0 ) - aOrigin.CPoint( 1 ) );
+
+ return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP, dir.IsDiagonal() );
+ }
+
+ if( aOrigin.CSegment( -1 ).Length() > 100000 * 30 ) // fixme: constant/parameter?
+ d = 1;
+
+ for( i = aOrigin.SegmentCount() - d; i >= 0; i-- )
+ {
+ DIRECTION_45 d_start ( aOrigin.CSegment( i ) );
+ VECTOR2I p_start = aOrigin.CPoint( i );
+ SHAPE_LINE_CHAIN paths[2];
+ DIRECTION_45 dirs[2];
+ DIRECTION_45 d_prev = ( i > 0 ? DIRECTION_45( aOrigin.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 ) );
+ }
+
+ for( int j = 0; j < 2; j++ )
+ {
+ if( dirs[j] == d_start )
+ {
+ picked = paths[j];
+ break;
+ }
+ }
+
+ if( picked )
+ break;
+
+ for( int j = 0; j < 2; j++ )
+ {
+ if( dirs[j].IsObtuse( d_prev ) )
+ {
+ picked = paths[j];
+ break;
+ }
+ }
+
+ if( picked )
+ break;
+ }
+
+ if( picked )
+ {
+ SHAPE_LINE_CHAIN path = aOrigin.Slice( 0, i );
+ path.Append( *picked );
+
+ return path;
+ }
+
+ DIRECTION_45 dir( aOrigin.CPoint( -1 ) - aOrigin.CPoint( -2 ) );
+
+ return DIRECTION_45().BuildInitialTrace( aOrigin.CPoint( 0 ), aP, dir.IsDiagonal() );
+}
+
+
+void PNS_LINE::DragCorner ( const VECTOR2I& aP, int aIndex, int aSnappingThreshold )
+{
+ SHAPE_LINE_CHAIN path;
+
+ VECTOR2I snapped = snapDraggedCorner( m_line, aP, aIndex, aSnappingThreshold );
+
+ if( aIndex == 0 )
+ path = dragCornerInternal( m_line.Reverse(), snapped ).Reverse();
+ else if( aIndex == m_line.SegmentCount() )
+ path = dragCornerInternal( m_line, snapped );
+ else
+ {
+ // fixme: awkward behaviour for "outwards" drags
+ path = dragCornerInternal( m_line.Slice( 0, aIndex ), snapped );
+ SHAPE_LINE_CHAIN path_rev = dragCornerInternal( m_line.Slice( aIndex, -1 ).Reverse(),
+ snapped ).Reverse();
+ path.Append( path_rev );
+ }
+
+ path.Simplify();
+ m_line = path;
+}
+
+
+VECTOR2I PNS_LINE::snapDraggedCorner( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I& aP,
+ int aIndex, int aThreshold ) const
+{
+ int s_start = std::max( aIndex - 2, 0 );
+ int s_end = std::min( aIndex + 2, aPath.SegmentCount() - 1 );
+
+ int i, j;
+ int best_dist = INT_MAX;
+ VECTOR2I best_snap = aP;
+
+ if( aThreshold <= 0 )
+ return aP;
+
+ for( i = s_start; i <= s_end; i++ )
+ {
+ const SEG& a = aPath.CSegment( i );
+
+ for( j = s_start; j < i; j++ )
+ {
+ const SEG& b = aPath.CSegment( j );
+
+ if( !( DIRECTION_45( a ).IsObtuse(DIRECTION_45( b ) ) ) )
+ continue;
+
+ OPT_VECTOR2I ip = a.IntersectLines(b);
+
+ if( ip )
+ {
+ int dist = ( *ip - aP ).EuclideanNorm();
+
+ if( dist < aThreshold && dist < best_dist )
+ {
+ best_dist = dist;
+ best_snap = *ip;
+ }
+ }
+ }
+ }
+
+ return best_snap;
+}
+
+VECTOR2I PNS_LINE::snapToNeighbourSegments( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I &aP,
+ int aIndex, int aThreshold ) const
+{
+ VECTOR2I snap_p[2];
+ DIRECTION_45 dragDir( aPath.CSegment( aIndex ) );
+ int snap_d[2] = { -1, -1 };
+
+ if( aThreshold == 0 )
+ return aP;
+
+ if( aIndex >= 2 )
+ {
+ SEG s = aPath.CSegment( aIndex - 2 );
+
+ if( DIRECTION_45( s ) == dragDir )
+ snap_d[0] = s.LineDistance( aP );
+
+ snap_p[0] = s.A;
+ }
+
+ if( aIndex < aPath.SegmentCount() - 2 )
+ {
+ 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 )
+ {
+ minDist = snap_d[i];
+ best = snap_p[i];
+ }
+ }
+
+ return best;
+}
+
+
+void PNS_LINE::DragSegment ( const VECTOR2I& aP, int aIndex, int aSnappingThreshold )
+{
+ SHAPE_LINE_CHAIN path( m_line );
+ VECTOR2I target( aP );
+
+ SEG guideA[2], guideB[2];
+ int index = aIndex;
+
+ target = snapToNeighbourSegments( path, aP, aIndex, aSnappingThreshold );
+
+ if( index == 0 )
+ {
+ path.Insert( 0, path.CPoint( 0 ) );
+ index++;
+ }
+
+ if( index == path.SegmentCount() - 1 )
+ {
+ path.Insert( path.PointCount() - 1, path.CPoint( -1 ) );
+ }
+
+ SEG dragged = path.CSegment( index );
+ DIRECTION_45 drag_dir( dragged );
+
+ SEG s_prev = path.CSegment( index - 1 );
+ SEG s_next = path.CSegment( index + 1 );
+
+ DIRECTION_45 dir_prev( s_prev );
+ DIRECTION_45 dir_next( s_next );
+
+ if( dir_prev == drag_dir )
+ {
+ dir_prev = dir_prev.Left();
+ path.Insert( index, path.CPoint( index ) );
+ index++;
+ }
+
+ if( dir_next == drag_dir )
+ {
+ dir_next = dir_next.Right();
+ path.Insert( index + 1, path.CPoint( index + 1 ) );
+ }
+
+ s_prev = path.CSegment( index - 1 );
+ s_next = path.CSegment( index + 1 );
+ dragged = path.CSegment( index );
+
+ bool lockEndpointA = true;
+ bool lockEndpointB = true;
+
+ if( aIndex == 0 )
+ {
+ if( !lockEndpointA )
+ 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() );
+ }
+
+ if( aIndex == m_line.SegmentCount() - 1 )
+ {
+ if( !lockEndpointB )
+ guideB[0] = guideB[1] = SEG( dragged.B, dragged.B + drag_dir.Right().Right().ToVector() );
+ else
+ {
+ 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 ) )
+ {
+ guideB[0] = SEG( s_next.B, s_next.B + drag_dir.Left().ToVector() );
+ 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() );
+ }
+
+ SEG s_current( target, target + drag_dir.ToVector() );
+
+ int best_len = INT_MAX;
+ SHAPE_LINE_CHAIN best;
+
+ for( int i = 0; i < 2; i++ )
+ {
+ for( int j = 0; j < 2; j++ )
+ {
+ OPT_VECTOR2I ip1 = s_current.IntersectLines( guideA[i] );
+ OPT_VECTOR2I ip2 = s_current.IntersectLines( guideB[j] );
+
+ SHAPE_LINE_CHAIN np;
+
+ if( !ip1 || !ip2 )
+ continue;
+
+ SEG s1( s_prev.A, *ip1 );
+ SEG s2( *ip1, *ip2 );
+ SEG s3( *ip2, s_next.B );
+
+ OPT_VECTOR2I ip;
+
+ if( (ip = s1.Intersect( s_next )) )
+ {
+ 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;
+ }
+ }
+ }
+
+ if( !lockEndpointA && aIndex == 0 )
+ best.Remove( 0, 0 );
+ if( !lockEndpointB && aIndex == m_line.SegmentCount() - 1 )
+ best.Remove( -1, -1 );
+
+ 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 );
+
+ m_line.Simplify();
+}
+
+
+bool PNS_LINE::CompareGeometry( const PNS_LINE& aOther )
+{
+ return m_line.CompareGeometry( aOther.m_line );
+}
+
+
+void PNS_LINE::Reverse()
+{
+ m_line = m_line.Reverse();
+
+ if( m_segmentRefs )
+ std::reverse( m_segmentRefs->begin(), m_segmentRefs->end() );
+}
+
+
+void PNS_LINE::AppendVia( const PNS_VIA& aVia )
+{
+ if( m_line.PointCount() > 1 && aVia.Pos() == m_line.CPoint( 0 ) )
+ {
+ Reverse();
+ }
+
+ m_hasVia = true;
+ m_via = aVia;
+ m_via.SetNet( m_net );
+}
+
+
+void PNS_LINE::SetRank( int aRank )
+{
+ m_rank = aRank;
+
+ if( m_segmentRefs )
+ {
+ BOOST_FOREACH( PNS_SEGMENT* s, *m_segmentRefs )
+ s->SetRank( aRank );
+ }
+}
+
+
+int PNS_LINE::Rank() const
+{
+ int min_rank = INT_MAX;
+ int rank;
+
+ if( m_segmentRefs )
+ {
+ BOOST_FOREACH( PNS_SEGMENT *s, *m_segmentRefs )
+ min_rank = std::min( min_rank, s->Rank() );
+ rank = ( min_rank == INT_MAX ) ? -1 : min_rank;
+ }
+ else
+ {
+ rank = m_rank;
+ }
+
+ return rank;
+}
+
+
+void PNS_LINE::ClipVertexRange( int aStart, int aEnd )
+{
+ m_line = m_line.Slice( aStart, aEnd );
+
+ if( m_segmentRefs )
+ {
+ SEGMENT_REFS* snew = new SEGMENT_REFS( m_segmentRefs->begin() + aStart,
+ m_segmentRefs->begin() + aEnd );
+
+ delete m_segmentRefs;
+ m_segmentRefs = snew;
+ }
+}
+
+
+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;
+}
+
+
+void PNS_LINE::ClearSegmentLinks()
+{
+ if( m_segmentRefs )
+ delete m_segmentRefs;
+
+ m_segmentRefs = NULL;
+}
+
+
+static void extendBox( BOX2I& aBox, bool& aDefined, const VECTOR2I& aP )
+{
+ if( aDefined )
+ aBox.Merge ( aP );
+ else {
+ aBox = BOX2I( aP, VECTOR2I( 0, 0 ) );
+ aDefined = true;
+ }
+}
+
+
+OPT_BOX2I PNS_LINE::ChangedArea( const PNS_LINE* aOther ) const
+{
+ BOX2I area;
+ bool areaDefined = false;
+
+ int i_start = -1;
+ int i_end_self = -1, i_end_other = -1;
+
+ SHAPE_LINE_CHAIN self( m_line );
+ self.Simplify();
+ SHAPE_LINE_CHAIN other( aOther->m_line );
+ other.Simplify();
+
+ int np_self = self.PointCount();
+ int np_other = other.PointCount();
+
+ int n = std::min( np_self, np_other );
+
+ for( int i = 0; i < n; i++ )
+ {
+ const VECTOR2I p1 = self.CPoint( i );
+ const VECTOR2I p2 = other.CPoint( i );
+
+ if( p1 != p2 )
+ {
+ if( i != n - 1 )
+ {
+ SEG s = self.CSegment( i );
+
+ if( !s.Contains( p2 ) )
+ {
+ i_start = i;
+ break;
+ }
+ } else {
+ i_start = i;
+ break;
+ }
+ }
+ }
+
+ for( int i = 0; i < n; i++ )
+ {
+ const VECTOR2I p1 = self.CPoint( np_self - 1 - i );
+ const VECTOR2I p2 = other.CPoint( np_other - 1 - i );
+
+ if( p1 != p2 )
+ {
+ i_end_self = np_self - 1 - i;
+ i_end_other = np_other - 1 - i;
+ break;
+ }
+ }
+
+ if( i_start < 0 )
+ i_start = n;
+
+ if( i_end_self < 0 )
+ i_end_self = np_self - 1;
+
+ if( i_end_other < 0 )
+ i_end_other = np_other - 1;
+
+ for( int i = i_start; i <= i_end_self; i++ )
+ extendBox( area, areaDefined, self.CPoint( i ) );
+
+ for( int i = i_start; i <= i_end_other; i++ )
+ extendBox( area, areaDefined, other.CPoint( i ) );
+
+ if( areaDefined )
+ {
+ area.Inflate( std::max( Width(), aOther->Width() ) );
+ return area;
+ }
+
+ return OPT_BOX2I();
+}
diff --git a/pcbnew/router/pns_line.h b/pcbnew/router/pns_line.h
new file mode 100644
index 0000000..32c0a71
--- /dev/null
+++ b/pcbnew/router/pns_line.h
@@ -0,0 +1,299 @@
+/*
+ * 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_LINE_H
+#define __PNS_LINE_H
+
+#include <math/vector2d.h>
+
+#include <geometry/seg.h>
+#include <geometry/shape.h>
+#include <geometry/shape_line_chain.h>
+
+#include "direction.h"
+#include "pns_item.h"
+#include "pns_via.h"
+
+class PNS_NODE;
+class PNS_SEGMENT;
+class PNS_VIA;
+
+/**
+ * Class PNS_LINE
+ *
+ * Represents a track on a PCB, connecting two non-trivial joints (that is,
+ * 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).
+ * Instead, they are assembled on-the-fly, based on a via/pad/segment that
+ * belongs to/starts/ends them.
+ *
+ * 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
+ * returned by PNS_NODE::AssembleLine and friends.
+ *
+ * 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.
+ */
+
+#define PNS_HULL_MARGIN 10
+
+class PNS_LINE : public PNS_ITEM
+{
+public:
+ typedef std::vector<PNS_SEGMENT*> SEGMENT_REFS;
+
+ /**
+ * Constructor
+ * Makes an empty line.
+ */
+ PNS_LINE() : PNS_ITEM( LINE )
+ {
+ m_segmentRefs = NULL;
+ m_hasVia = false;
+ m_width = 1; // Dummy value
+ }
+
+ PNS_LINE( const PNS_LINE& aOther );
+
+ /**
+ * Constructor
+ * Copies properties (net, layers, etc.) from a base line and replaces the shape
+ * by another
+ **/
+ PNS_LINE( const PNS_LINE& aBase, const SHAPE_LINE_CHAIN& aLine ) :
+ PNS_ITEM( aBase ),
+ m_line( aLine ),
+ m_width( aBase.m_width )
+ {
+ m_net = aBase.m_net;
+ m_layers = aBase.m_layers;
+ m_segmentRefs = NULL;
+ m_hasVia = false;
+ }
+
+ ~PNS_LINE();
+
+ static inline bool ClassOf( const PNS_ITEM* aItem )
+ {
+ return aItem && LINE == aItem->Kind();
+ }
+
+ /// @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
+ {
+ return m_line.SegmentCount();
+ }
+
+ ///> Returns the number of points in the line
+ int PointCount() const
+ {
+ return m_line.PointCount();
+ }
+
+ ///> Returns the aIdx-th point of the line
+ const VECTOR2I& CPoint( int aIdx ) const
+ {
+ return m_line.CPoint( aIdx );
+ }
+
+ ///> Returns the aIdx-th segment of the line
+ const SEG CSegment( int aIdx ) const
+ {
+ return m_line.CSegment( aIdx );
+ }
+
+ ///> Sets line width
+ void SetWidth( int aWidth )
+ {
+ m_width = aWidth;
+ }
+
+ ///> 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();
+
+
+ /* 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 )
+ {
+ if( !m_segmentRefs )
+ m_segmentRefs = new SEGMENT_REFS();
+
+ m_segmentRefs->push_back( aSeg );
+ }
+
+ ///> Returns the list of segments from the owning node that constitute this
+ ///> line (or NULL if the line is not linked)
+ SEGMENT_REFS* LinkedSegments()
+ {
+ return m_segmentRefs;
+ }
+
+ bool IsLinked() const
+ {
+ return m_segmentRefs != NULL;
+ }
+
+ ///> Checks if the segment aSeg is a part of the line.
+ bool ContainsSegment( PNS_SEGMENT* aSeg ) const
+ {
+ if( !m_segmentRefs )
+ return false;
+
+ return std::find( m_segmentRefs->begin(), m_segmentRefs->end(),
+ aSeg ) != m_segmentRefs->end();
+ }
+
+ PNS_SEGMENT* GetLink( int aIndex ) const
+ {
+ return (*m_segmentRefs)[aIndex];
+ }
+
+ ///> Erases the linking information. Used to detach the line from the owning node.
+ 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;
+
+ ///> Clips the line to a given range of vertices.
+ void ClipVertexRange ( int aStart, int aEnd );
+
+ ///> Returns the number of corners of angles specified by mask aAngles.
+ int CountCorners( int aAngles );
+
+ ///> Calculates a line thightly wrapping a convex hull
+ ///> of an obstacle object (aObstacle).
+ ///> aPrePath = path from origin to the obstacle
+ ///> aWalkaroundPath = path around the obstacle
+ ///> aPostPath = past from obstacle till the end
+ ///> aCW = whether to walk around in clockwise or counter-clockwise direction.
+ bool Walkaround( SHAPE_LINE_CHAIN aObstacle,
+ SHAPE_LINE_CHAIN& aPre,
+ SHAPE_LINE_CHAIN& aWalk,
+ SHAPE_LINE_CHAIN& aPost,
+ bool aCw ) const;
+
+ void Walkaround( const SHAPE_LINE_CHAIN& aObstacle,
+ SHAPE_LINE_CHAIN& aPath,
+ bool aCw ) const;
+
+ bool Is45Degree();
+
+ ///> Prints out all linked segments
+ void ShowLinks();
+
+ bool EndsWithVia() const { return m_hasVia; }
+
+ void AppendVia( const PNS_VIA& aVia );
+ void RemoveVia() { m_hasVia = false; }
+
+ const PNS_VIA& Via() const { return m_via; }
+
+ virtual void Mark( int aMarker );
+ 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 SetRank( int aRank );
+ int Rank() const;
+
+ bool HasLoops() const;
+
+ OPT_BOX2I ChangedArea( const PNS_LINE* aOther ) const;
+
+private:
+ VECTOR2I snapToNeighbourSegments( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I &aP,
+ int aIndex, int aThreshold) const;
+
+ VECTOR2I snapDraggedCorner( const SHAPE_LINE_CHAIN& aPath, const VECTOR2I &aP,
+ int aIndex, int aThreshold ) const;
+
+ ///> 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.
+ SEGMENT_REFS* m_segmentRefs;
+
+ ///> The actual shape of the line
+ SHAPE_LINE_CHAIN m_line;
+
+ ///> our width
+ int m_width;
+
+ ///> If true, the line ends with a via
+ bool m_hasVia;
+
+ ///> Via at the end point, if m_hasVia == true
+ PNS_VIA m_via;
+};
+
+#endif // __PNS_LINE_H
diff --git a/pcbnew/router/pns_line_placer.cpp b/pcbnew/router/pns_line_placer.cpp
new file mode 100644
index 0000000..7f4efb4
--- /dev/null
+++ b/pcbnew/router/pns_line_placer.cpp
@@ -0,0 +1,1112 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2014 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/foreach.hpp>
+#include <boost/optional.hpp>
+
+#include <colors.h>
+
+#include "trace.h"
+
+#include "pns_node.h"
+#include "pns_line_placer.h"
+#include "pns_walkaround.h"
+#include "pns_shove.h"
+#include "pns_utils.h"
+#include "pns_router.h"
+#include "pns_topology.h"
+
+#include <class_board_item.h>
+
+using boost::optional;
+
+PNS_LINE_PLACER::PNS_LINE_PLACER( PNS_ROUTER* aRouter ) :
+ PNS_PLACEMENT_ALGO( aRouter )
+{
+ m_initial_direction = DIRECTION_45::N;
+ m_world = NULL;
+ m_shove = NULL;
+ m_currentNode = NULL;
+ m_idle = true;
+
+ // Init temporary variables (do not leave uninitialized members)
+ m_lastNode = NULL;
+ m_placingVia = false;
+ m_currentNet = 0;
+ m_currentLayer = 0;
+ m_currentMode = RM_MarkObstacles;
+ m_startItem = NULL;
+ m_chainedPlacement = false;
+ m_splitSeg = false;
+ m_orthoMode = false;
+}
+
+
+PNS_LINE_PLACER::~PNS_LINE_PLACER()
+{
+ if( m_shove )
+ delete m_shove;
+}
+
+
+void PNS_LINE_PLACER::setWorld( PNS_NODE* aWorld )
+{
+ m_world = aWorld;
+}
+
+
+const PNS_VIA PNS_LINE_PLACER::makeVia( const VECTOR2I& aP )
+{
+ const PNS_LAYERSET layers( m_sizes.GetLayerTop(), m_sizes.GetLayerBottom() );
+
+ return PNS_VIA( aP, layers, m_sizes.ViaDiameter(), m_sizes.ViaDrill(), -1, m_sizes.ViaType() );
+}
+
+
+bool PNS_LINE_PLACER::ToggleVia( bool aEnabled )
+{
+ m_placingVia = aEnabled;
+
+ if( !aEnabled )
+ m_head.RemoveVia();
+
+ return true;
+}
+
+
+void PNS_LINE_PLACER::setInitialDirection( const DIRECTION_45& aDirection )
+{
+ m_initial_direction = aDirection;
+
+ if( m_tail.SegmentCount() == 0 )
+ m_direction = aDirection;
+}
+
+
+bool PNS_LINE_PLACER::handleSelfIntersections()
+{
+ SHAPE_LINE_CHAIN::INTERSECTIONS ips;
+ SHAPE_LINE_CHAIN& head = m_head.Line();
+ SHAPE_LINE_CHAIN& tail = m_tail.Line();
+
+ // if there is no tail, there is nothing to intersect with
+ if( tail.PointCount() < 2 )
+ return false;
+
+ tail.Intersect( head, ips );
+
+ // no intesection points - nothing to reduce
+ if( ips.empty() )
+ return false;
+
+ int n = INT_MAX;
+ VECTOR2I ipoint;
+
+ // if there is more than one intersection, find the one that is
+ // closest to the beginning of the tail.
+ BOOST_FOREACH( SHAPE_LINE_CHAIN::INTERSECTION i, ips )
+ {
+ if( i.our.Index() < n )
+ {
+ n = i.our.Index();
+ ipoint = i.p;
+ }
+ }
+
+ // ignore the point where head and tail meet
+ if( ipoint == head.CPoint( 0 ) || ipoint == tail.CPoint( -1 ) )
+ return false;
+
+ // Intersection point is on the first or the second segment: just start routing
+ // from the beginning
+ if( n < 2 )
+ {
+ m_p_start = tail.Point( 0 );
+ m_direction = m_initial_direction;
+ tail.Clear();
+ head.Clear();
+
+ return true;
+ }
+ else
+ {
+ // Clip till the last tail segment before intersection.
+ // Set the direction to the one of this segment.
+ const SEG last = tail.CSegment( n - 1 );
+ m_p_start = last.A;
+ m_direction = DIRECTION_45( last );
+ tail.Remove( n, -1 );
+ return true;
+ }
+
+ return false;
+}
+
+
+bool PNS_LINE_PLACER::handlePullback()
+{
+ SHAPE_LINE_CHAIN& head = m_head.Line();
+ SHAPE_LINE_CHAIN& tail = m_tail.Line();
+
+ if( head.PointCount() < 2 )
+ return false;
+
+ int n = tail.PointCount();
+
+ if( n == 0 )
+ return false;
+ else if( n == 1 )
+ {
+ m_p_start = tail.CPoint( 0 );
+ tail.Clear();
+ return true;
+ }
+
+ DIRECTION_45 first_head( head.CSegment( 0 ) );
+ DIRECTION_45 last_tail( tail.CSegment( -1 ) );
+ DIRECTION_45::AngleType angle = first_head.Angle( last_tail );
+
+ // case 1: we have a defined routing direction, and the currently computed
+ // head goes in different one.
+ bool pullback_1 = false; // (m_direction != DIRECTION_45::UNDEFINED && m_direction != first_head);
+
+ // case 2: regardless of the current routing direction, if the tail/head
+ // extremities form an acute or right angle, reduce the tail by one segment
+ // (and hope that further iterations) will result with a cleaner trace
+ bool pullback_2 = ( angle == DIRECTION_45::ANG_RIGHT || angle == DIRECTION_45::ANG_ACUTE );
+
+ if( pullback_1 || pullback_2 )
+ {
+ const SEG last = tail.CSegment( -1 );
+ m_direction = DIRECTION_45( last );
+ m_p_start = last.A;
+
+ TRACE( 0, "Placer: pullback triggered [%d] [%s %s]",
+ n % last_tail.Format().c_str() % first_head.Format().c_str() );
+
+ // erase the last point in the tail, hoping that the next iteration will
+ // result with a head trace that starts with a segment following our
+ // current direction.
+ if( n < 2 )
+ tail.Clear(); // don't leave a single-point tail
+ else
+ tail.Remove( -1, -1 );
+
+ if( !tail.SegmentCount() )
+ m_direction = m_initial_direction;
+
+ return true;
+ }
+
+ return false;
+}
+
+
+bool PNS_LINE_PLACER::reduceTail( const VECTOR2I& aEnd )
+{
+ SHAPE_LINE_CHAIN& head = m_head.Line();
+ SHAPE_LINE_CHAIN& tail = m_tail.Line();
+
+ int n = tail.SegmentCount();
+
+ if( head.SegmentCount() < 1 )
+ return false;
+
+ // Don't attempt this for too short tails
+ if( n < 2 )
+ return false;
+
+ // Start from the segment farthest from the end of the tail
+ // int start_index = std::max(n - 1 - ReductionDepth, 0);
+
+ DIRECTION_45 new_direction;
+ VECTOR2I new_start;
+ int reduce_index = -1;
+
+ for( int i = tail.SegmentCount() - 1; i >= 0; i-- )
+ {
+ const SEG s = tail.CSegment( i );
+ DIRECTION_45 dir( s );
+
+ // calculate a replacement route and check if it matches
+ // the direction of the segment to be replaced
+ SHAPE_LINE_CHAIN replacement = dir.BuildInitialTrace( s.A, aEnd );
+
+ PNS_LINE tmp( m_tail, replacement );
+
+ if( m_currentNode->CheckColliding( &tmp, PNS_ITEM::ANY ) )
+ break;
+
+ if( DIRECTION_45( replacement.CSegment( 0 ) ) == dir )
+ {
+ new_start = s.A;
+ new_direction = dir;
+ reduce_index = i;
+ }
+ }
+
+ if( reduce_index >= 0 )
+ {
+ TRACE( 0, "Placer: reducing tail: %d", reduce_index );
+ SHAPE_LINE_CHAIN reducedLine = new_direction.BuildInitialTrace( new_start, aEnd );
+
+ m_p_start = new_start;
+ m_direction = new_direction;
+ tail.Remove( reduce_index + 1, -1 );
+ head.Clear();
+ return true;
+ }
+
+ if( !tail.SegmentCount() )
+ m_direction = m_initial_direction;
+
+ return false;
+}
+
+
+bool PNS_LINE_PLACER::checkObtusity( const SEG& aA, const SEG& aB ) const
+{
+ const DIRECTION_45 dir_a( aA );
+ const DIRECTION_45 dir_b( aB );
+
+ return dir_a.IsObtuse( dir_b ) || dir_a == dir_b;
+}
+
+
+bool PNS_LINE_PLACER::mergeHead()
+{
+ SHAPE_LINE_CHAIN& head = m_head.Line();
+ SHAPE_LINE_CHAIN& tail = m_tail.Line();
+
+ const int ForbiddenAngles = DIRECTION_45::ANG_ACUTE |
+ DIRECTION_45::ANG_HALF_FULL |
+ DIRECTION_45::ANG_UNDEFINED;
+
+ head.Simplify();
+ tail.Simplify();
+
+ int n_head = head.SegmentCount();
+ int n_tail = tail.SegmentCount();
+
+ if( n_head < 3 )
+ {
+ TRACEn( 4, "Merge failed: not enough head segs." );
+ return false;
+ }
+
+ if( n_tail && head.CPoint( 0 ) != tail.CPoint( -1 ) )
+ {
+ TRACEn( 4, "Merge failed: head and tail discontinuous." );
+ return false;
+ }
+
+ if( m_head.CountCorners( ForbiddenAngles ) != 0 )
+ return false;
+
+ DIRECTION_45 dir_tail, dir_head;
+
+ dir_head = DIRECTION_45( head.CSegment( 0 ) );
+
+ if( n_tail )
+ {
+ dir_tail = DIRECTION_45( tail.CSegment( -1 ) );
+
+ if( dir_head.Angle( dir_tail ) & ForbiddenAngles )
+ return false;
+ }
+
+ if( !n_tail )
+ tail.Append( head.CSegment( 0 ).A );
+
+ for( int i = 0; i < n_head - 2; i++ )
+ {
+ tail.Append( head.CSegment( i ).B );
+ }
+
+ tail.Simplify();
+
+ SEG last = tail.CSegment( -1 );
+
+ m_p_start = last.B;
+ m_direction = DIRECTION_45( last ).Right();
+
+ head.Remove( 0, n_head - 2 );
+
+ TRACE( 0, "Placer: merge %d, new direction: %s", n_head % m_direction.Format().c_str() );
+
+ head.Simplify();
+ tail.Simplify();
+
+ return true;
+}
+
+
+bool PNS_LINE_PLACER::rhWalkOnly( const VECTOR2I& aP, PNS_LINE& aNewHead )
+{
+ PNS_LINE initTrack( m_head );
+ PNS_LINE walkFull;
+ int effort = 0;
+ bool rv = true, viaOk;
+
+ viaOk = buildInitialLine( aP, initTrack );
+
+ PNS_WALKAROUND walkaround( m_currentNode, Router() );
+
+ walkaround.SetSolidsOnly( false );
+ walkaround.SetIterationLimit( Settings().WalkaroundIterationLimit() );
+
+ PNS_WALKAROUND::WALKAROUND_STATUS wf = walkaround.Route( initTrack, walkFull, false );
+
+ switch( Settings().OptimizerEffort() )
+ {
+ case OE_LOW:
+ effort = 0;
+ break;
+
+ case OE_MEDIUM:
+ case OE_FULL:
+ effort = PNS_OPTIMIZER::MERGE_SEGMENTS;
+ break;
+ }
+
+ if( Settings().SmartPads() )
+ effort |= PNS_OPTIMIZER::SMART_PADS;
+
+ if( wf == PNS_WALKAROUND::STUCK )
+ {
+ walkFull = walkFull.ClipToNearestObstacle( m_currentNode );
+ rv = true;
+ }
+ else if( m_placingVia && viaOk )
+ {
+ walkFull.AppendVia( makeVia( walkFull.CPoint( -1 ) ) );
+ }
+
+ PNS_OPTIMIZER::Optimize( &walkFull, effort, m_currentNode );
+
+ if( m_currentNode->CheckColliding( &walkFull ) )
+ {
+ aNewHead = m_head;
+ return false;
+ }
+
+ m_head = walkFull;
+ aNewHead = walkFull;
+
+ return rv;
+}
+
+
+bool PNS_LINE_PLACER::rhMarkObstacles( const VECTOR2I& aP, PNS_LINE& aNewHead )
+{
+ buildInitialLine( aP, m_head );
+ aNewHead = m_head;
+ return static_cast<bool>( m_currentNode->CheckColliding( &m_head ) );
+}
+
+
+bool PNS_LINE_PLACER::rhShoveOnly( const VECTOR2I& aP, PNS_LINE& aNewHead )
+{
+ PNS_LINE initTrack( m_head );
+ PNS_LINE walkSolids, l2;
+
+ bool viaOk = buildInitialLine( aP, initTrack );
+
+ m_currentNode = m_shove->CurrentNode();
+ PNS_OPTIMIZER optimizer( m_currentNode );
+
+ PNS_WALKAROUND walkaround( m_currentNode, Router() );
+
+ walkaround.SetSolidsOnly( true );
+ walkaround.SetIterationLimit( 10 );
+ PNS_WALKAROUND::WALKAROUND_STATUS stat_solids = walkaround.Route( initTrack, walkSolids );
+
+ optimizer.SetEffortLevel( PNS_OPTIMIZER::MERGE_SEGMENTS );
+ optimizer.SetCollisionMask ( PNS_ITEM::SOLID );
+ optimizer.Optimize( &walkSolids );
+
+ if( stat_solids == PNS_WALKAROUND::DONE )
+ l2 = walkSolids;
+ else
+ l2 = initTrack.ClipToNearestObstacle( m_shove->CurrentNode() );
+
+ PNS_LINE l( m_tail );
+ l.Line().Append( l2.CLine() );
+ l.Line().Simplify();
+
+ if( l.PointCount() == 0 || l2.PointCount() == 0 )
+ {
+ aNewHead = m_head;
+ return false;
+ }
+
+ if( m_placingVia && viaOk )
+ {
+ PNS_VIA v1( makeVia( l.CPoint( -1 ) ) );
+ PNS_VIA v2( makeVia( l2.CPoint( -1 ) ) );
+
+ l.AppendVia( v1 );
+ l2.AppendVia( v2 );
+ }
+
+ l.Line().Simplify();
+
+ // in certain, uncommon cases there may be loops in the head+tail, In such case, we don't shove to avoid
+ // screwing up the database.
+ if( l.HasLoops() )
+ {
+ aNewHead = m_head;
+ return false;
+ }
+
+ PNS_SHOVE::SHOVE_STATUS status = m_shove->ShoveLines( l );
+
+ m_currentNode = m_shove->CurrentNode();
+
+ if( status == PNS_SHOVE::SH_OK || status == PNS_SHOVE::SH_HEAD_MODIFIED )
+ {
+ if( status == PNS_SHOVE::SH_HEAD_MODIFIED )
+ {
+ l2 = m_shove->NewHead();
+ }
+
+ optimizer.SetWorld( m_currentNode );
+ optimizer.SetEffortLevel( PNS_OPTIMIZER::MERGE_OBTUSE | PNS_OPTIMIZER::SMART_PADS );
+ optimizer.SetCollisionMask( PNS_ITEM::ANY );
+ optimizer.Optimize( &l2 );
+
+ aNewHead = l2;
+
+ return true;
+ }
+ else
+ {
+ walkaround.SetWorld( m_currentNode );
+ walkaround.SetSolidsOnly( false );
+ walkaround.SetIterationLimit( 10 );
+ walkaround.SetApproachCursor( true, aP );
+ walkaround.Route( initTrack, l2 );
+ aNewHead = l2.ClipToNearestObstacle( m_shove->CurrentNode() );
+
+ 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()
+{
+ 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;
+
+ //if(m_currentMode() == RM_Walkaround)
+ // tailLookbackSegments = 10000;
+
+ int threshold = std::min( tail.PointCount(), tailLookbackSegments + 1 );
+
+ if( tail.SegmentCount() < 3 )
+ return false;
+
+ // assemble TailLookbackSegments tail segments with the current head
+ SHAPE_LINE_CHAIN opt_line = tail.Slice( -threshold, -1 );
+
+ int end = std::min(2, head.PointCount() - 1 );
+
+ opt_line.Append( head.Slice( 0, end ) );
+
+ PNS_LINE new_head( m_tail, opt_line );
+
+ // and see if it could be made simpler by merging obtuse/collnear segments.
+ // If so, replace the (threshold) last tail points and the head with
+ // the optimized line
+
+ if( PNS_OPTIMIZER::Optimize( &new_head, PNS_OPTIMIZER::MERGE_OBTUSE, m_currentNode ) )
+ {
+ PNS_LINE tmp( m_tail, opt_line );
+
+ TRACE( 0, "Placer: optimize tail-head [%d]", threshold );
+
+ head.Clear();
+ tail.Replace( -threshold, -1, new_head.CLine() );
+ tail.Simplify();
+
+ m_p_start = new_head.CLine().CPoint( -1 );
+ m_direction = DIRECTION_45( new_head.CSegment( -1 ) );
+
+ return true;
+ }
+
+ return false;
+}
+
+
+void PNS_LINE_PLACER::routeStep( const VECTOR2I& aP )
+{
+ bool fail = false;
+ bool go_back = false;
+
+ int i, n_iter = 1;
+
+ PNS_LINE new_head;
+
+ TRACE( 2, "INIT-DIR: %s head: %d, tail: %d segs\n",
+ m_initial_direction.Format().c_str() % m_head.SegmentCount() %
+ m_tail.SegmentCount() );
+
+ for( i = 0; i < n_iter; i++ )
+ {
+ if( !go_back && Settings().FollowMouse() )
+ reduceTail( aP );
+
+ go_back = false;
+
+ if( !routeHead( aP, new_head ) )
+ fail = true;
+
+ if( !new_head.Is45Degree() )
+ fail = true;
+
+ if( !Settings().FollowMouse() )
+ return;
+
+ m_head = new_head;
+
+ if( handleSelfIntersections() )
+ {
+ n_iter++;
+ go_back = true;
+ }
+
+ if( !go_back && handlePullback() )
+ {
+ n_iter++;
+ go_back = true;
+ }
+ }
+
+ if( !fail )
+ {
+ if( optimizeTailHeadTransition() )
+ return;
+
+ mergeHead();
+ }
+}
+
+
+bool PNS_LINE_PLACER::route( const VECTOR2I& aP )
+{
+ routeStep( aP );
+ return CurrentEnd() == aP;
+}
+
+
+const PNS_LINE PNS_LINE_PLACER::Trace() const
+{
+ PNS_LINE tmp( m_head );
+
+ tmp.SetShape( m_tail.CLine() );
+ tmp.Line().Append( m_head.CLine() );
+ tmp.Line().Simplify();
+ return tmp;
+}
+
+
+const PNS_ITEMSET PNS_LINE_PLACER::Traces()
+{
+ m_currentTrace = Trace();
+ return PNS_ITEMSET( &m_currentTrace );
+}
+
+
+void PNS_LINE_PLACER::FlipPosture()
+{
+ m_initial_direction = m_initial_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::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 );
+ }
+}
+
+
+bool PNS_LINE_PLACER::SetLayer( int aLayer )
+{
+ if( m_idle )
+ {
+ m_currentLayer = aLayer;
+ return true;
+ }
+ else if( m_chainedPlacement )
+ {
+ return false;
+ }
+ else if( !m_startItem || ( m_startItem->OfKind( PNS_ITEM::VIA ) && m_startItem->Layers().Overlaps( aLayer ) ) ) {
+ m_currentLayer = aLayer;
+ m_splitSeg = false;
+ initPlacement ( m_splitSeg );
+ Move ( m_currentEnd, NULL );
+ return true;
+ }
+
+ return false;
+}
+
+
+bool PNS_LINE_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem )
+{
+ VECTOR2I p( aP );
+
+ static int unknowNetIdx = 0; // -10000;
+ int net = -1;
+
+ 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_currentEnd = p;
+ m_currentNet = net;
+ m_startItem = aStartItem;
+ m_placingVia = false;
+ m_chainedPlacement = false;
+ m_splitSeg = splitSeg;
+
+ setInitialDirection( Settings().InitialDirection() );
+
+ initPlacement( m_splitSeg );
+ return true;
+}
+
+void PNS_LINE_PLACER::initPlacement( bool aSplitSeg )
+{
+ m_idle = false;
+
+ m_head.Line().Clear();
+ m_tail.Line().Clear();
+ m_head.SetNet( m_currentNet );
+ m_tail.SetNet( m_currentNet );
+ m_head.SetLayer( m_currentLayer );
+ m_tail.SetLayer( m_currentLayer );
+ m_head.SetWidth( m_sizes.TrackWidth() );
+ m_tail.SetWidth( m_sizes.TrackWidth() );
+ m_head.RemoveVia();
+ m_tail.RemoveVia();
+
+ m_p_start = m_currentStart;
+ m_direction = m_initial_direction;
+
+ PNS_NODE* world = Router()->GetWorld();
+
+ world->KillChildren();
+ PNS_NODE* rootNode = world->Branch();
+
+ if( aSplitSeg )
+ splitAdjacentSegments( rootNode, m_startItem, m_currentStart );
+
+ setWorld( rootNode );
+
+ TRACE( 1, "world %p, intitial-direction %s layer %d\n",
+ m_world % m_direction.Format().c_str() % aLayer );
+
+ m_lastNode = NULL;
+ m_currentNode = m_world;
+ m_currentMode = Settings().Mode();
+
+ if( m_shove )
+ delete m_shove;
+
+ m_shove = NULL;
+
+ if( m_currentMode == RM_Shove || m_currentMode == RM_Smart )
+ {
+ m_shove = new PNS_SHOVE( m_world->Branch(), Router() );
+ }
+}
+
+
+bool PNS_LINE_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem )
+{
+ PNS_LINE current;
+ VECTOR2I p = aP;
+ int eiDepth = -1;
+
+ if( aEndItem && aEndItem->Owner() )
+ eiDepth = static_cast<PNS_NODE*>( 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() )
+ {
+ splitAdjacentSegments( m_lastNode, aEndItem, current.CPoint( -1 ) );
+
+ if( Settings().RemoveLoops() )
+ removeLoops( m_lastNode, current );
+ }
+
+ updateLeadingRatLine();
+ return true;
+}
+
+
+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() )
+ {
+ if( pl.EndsWithVia() )
+ {
+ m_lastNode->Add( pl.Via().Clone() );
+ Router()->CommitRouting( m_lastNode );
+
+ m_lastNode = NULL;
+ m_currentNode = NULL;
+
+ m_idle = true;
+ }
+
+ 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;
+ m_currentNode = NULL;
+
+ if( !realEnd )
+ {
+ setInitialDirection( d_last );
+ m_currentStart = m_placingVia ? p_last : p_pre_last;
+ m_startItem = NULL;
+ m_placingVia = false;
+ m_chainedPlacement = !pl.EndsWithVia();
+ m_splitSeg = false;
+ initPlacement();
+ }
+ else
+ {
+ m_idle = true;
+ }
+
+ return realEnd;
+}
+
+
+void PNS_LINE_PLACER::removeLoops( PNS_NODE* aNode, PNS_LINE& aLatest )
+{
+ if( !aLatest.SegmentCount() )
+ return;
+
+ if( aLatest.CLine().CPoint( 0 ) == aLatest.CLine().CPoint( -1 ) )
+ return;
+
+ std::set<PNS_SEGMENT *> toErase;
+ aNode->Add( &aLatest, true );
+
+ for( int s = 0; s < aLatest.LinkCount(); 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() )
+ {
+ BOOST_FOREACH( PNS_SEGMENT *ss, *line.LinkedSegments() )
+ toErase.insert( ss );
+
+ removedCount++;
+ }
+ }
+
+ TRACE( 0, "total segs removed: %d/%d\n", removedCount % total );
+ }
+
+ BOOST_FOREACH( PNS_SEGMENT *s, toErase )
+ aNode->Remove( s );
+
+ 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() )
+ {
+ PNS_LINE lnew( l );
+ aNode->Remove( &l );
+ lnew.SetShape( simplified );
+ aNode->Add( &lnew );
+ }
+}
+
+
+void PNS_LINE_PLACER::UpdateSizes( const PNS_SIZES_SETTINGS& aSizes )
+{
+ m_sizes = aSizes;
+
+ if( !m_idle )
+ {
+ initPlacement( m_splitSeg );
+ }
+}
+
+
+void PNS_LINE_PLACER::updateLeadingRatLine()
+{
+ PNS_LINE current = Trace();
+ SHAPE_LINE_CHAIN ratLine;
+ PNS_TOPOLOGY topo( m_lastNode );
+
+ if( topo.LeadingRatLine( &current, ratLine ) )
+ Router()->DisplayDebugLine( ratLine, 5, 10000 );
+}
+
+
+void PNS_LINE_PLACER::SetOrthoMode( bool aOrthoMode )
+{
+ m_orthoMode = aOrthoMode;
+}
+
+bool PNS_LINE_PLACER::buildInitialLine( const VECTOR2I& aP, PNS_LINE& aHead )
+{
+ SHAPE_LINE_CHAIN l;
+
+ if( m_p_start == aP )
+ {
+ l.Clear();
+ }
+ else
+ {
+ if( Settings().GetFreeAngleMode() && Settings().Mode() == RM_MarkObstacles )
+ {
+ l = SHAPE_LINE_CHAIN( m_p_start, aP );
+ }
+ else
+ {
+ l = m_direction.BuildInitialTrace( m_p_start, aP );
+ }
+
+ if( l.SegmentCount() > 1 && m_orthoMode )
+ {
+ VECTOR2I newLast = l.CSegment( 0 ).LineProject( l.CPoint( -1 ) );
+
+ l.Remove( -1, -1 );
+ l.Point( 1 ) = newLast;
+ }
+ }
+
+ aHead.SetShape( l );
+
+ if( !m_placingVia )
+ return true;
+
+ PNS_VIA v( makeVia( aP ) );
+ v.SetNet( aHead.Net() );
+
+ if( m_currentMode == RM_MarkObstacles )
+ {
+ aHead.AppendVia( v );
+ return true;
+ }
+
+ VECTOR2I force;
+ VECTOR2I lead = aP - m_p_start;
+
+ bool solidsOnly = ( m_currentMode != RM_Walkaround );
+
+ if( v.PushoutForce( m_currentNode, lead, force, solidsOnly, 40 ) )
+ {
+ SHAPE_LINE_CHAIN line = m_direction.BuildInitialTrace( m_p_start, aP + force );
+ aHead = PNS_LINE( aHead, line );
+
+ v.SetPos( v.Pos() + force );
+ return true;
+ }
+
+ return false; // via placement unsuccessful
+}
+
+
+void PNS_LINE_PLACER::GetModifiedNets( std::vector<int>& aNets ) const
+{
+ aNets.push_back( m_currentNet );
+}
+
+PNS_LOGGER* PNS_LINE_PLACER::Logger()
+{
+ if( m_shove )
+ return m_shove->Logger();
+
+ return NULL;
+}
diff --git a/pcbnew/router/pns_line_placer.h b/pcbnew/router/pns_line_placer.h
new file mode 100644
index 0000000..8a1f263
--- /dev/null
+++ b/pcbnew/router/pns_line_placer.h
@@ -0,0 +1,397 @@
+/*
+ * 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_LINE_PLACER_H
+#define __PNS_LINE_PLACER_H
+
+#include <math/vector2d.h>
+
+#include <geometry/shape.h>
+#include <geometry/shape_line_chain.h>
+
+#include "pns_sizes_settings.h"
+#include "pns_node.h"
+#include "pns_via.h"
+#include "pns_line.h"
+#include "pns_placement_algo.h"
+
+class PNS_ROUTER;
+class PNS_SHOVE;
+class PNS_OPTIMIZER;
+class PNS_ROUTER_BASE;
+class PNS_VIA;
+class PNS_SIZES_SETTINGS;
+
+
+/**
+ * Class PNS_LINE_PLACER
+ *
+ * Single track placement algorithm. Interactively routes a track.
+ * Applies shove and walkaround algorithms when needed.
+ */
+
+class PNS_LINE_PLACER : public PNS_PLACEMENT_ALGO
+{
+public:
+ PNS_LINE_PLACER( PNS_ROUTER* aRouter );
+ ~PNS_LINE_PLACER();
+
+ /**
+ * Function Start()
+ *
+ * Starts routing a single track at point aP, taking item aStartItem as anchor
+ * (unless NULL).
+ */
+ bool Start( const VECTOR2I& aP, PNS_ITEM* aStartItem );
+
+ /**
+ * Function Move()
+ *
+ * Moves the end of the currently routed trace to the point aP, taking
+ * aEndItem as anchor (if not NULL).
+ * (unless NULL).
+ */
+ bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem );
+
+ /**
+ * Function FixRoute()
+ *
+ * Commits the currently routed track to the parent node, taking
+ * aP as the final end point and aEndItem as the final anchor (if provided).
+ * @return true, if route has been commited. May return false if the routing
+ * result is violating design rules - in such case, the track is only committed
+ * if Settings.CanViolateDRC() is on.
+ */
+ bool FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem );
+
+ /**
+ * Function ToggleVia()
+ *
+ * Enables/disables a via at the end of currently routed trace.
+ */
+ bool ToggleVia( bool aEnabled );
+
+ /**
+ * Function SetLayer()
+ *
+ * Sets the current routing layer.
+ */
+ bool SetLayer( int aLayer );
+
+ /**
+ * Function 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
+ {
+ return m_currentEnd;
+ }
+
+ /**
+ * Function CurrentNet()
+ *
+ * Returns the net code of currently routed track.
+ */
+ const std::vector<int> CurrentNets() const
+ {
+ return std::vector<int>( 1, m_currentNet );
+ }
+
+ /**
+ * Function CurrentLayer()
+ *
+ * Returns the layer of currently routed track.
+ */
+ int CurrentLayer() const
+ {
+ return m_currentLayer;
+ }
+
+ /**
+ * Function CurrentNode()
+ *
+ * Returns the most recent world state.
+ */
+ PNS_NODE* CurrentNode( bool aLoopsRemoved = false ) const;
+
+ /**
+ * Function FlipPosture()
+ *
+ * Toggles the current posture (straight/diagonal) of the trace head.
+ */
+ void FlipPosture();
+
+ /**
+ * Function UpdateSizes()
+ *
+ * Performs on-the-fly update of the width, via diameter & drill size from
+ * a settings class. Used to dynamically change these parameters as
+ * the track is routed.
+ */
+ void UpdateSizes( const PNS_SIZES_SETTINGS& aSizes );
+
+ void SetOrthoMode( bool aOrthoMode );
+
+ bool IsPlacingVia() const { return m_placingVia; }
+
+ void GetModifiedNets( std::vector<int>& aNets ) const;
+
+ PNS_LOGGER* Logger();
+
+
+private:
+ /**
+ * Function route()
+ *
+ * Re-routes the current track to point aP. Returns true, when routing has
+ * completed successfully (i.e. the trace end has reached point aP), and false
+ * if the trace was stuck somewhere on the way. May call routeStep()
+ * repetitively due to mouse smoothing.
+ * @param aP ending point of current route.
+ * @return true, if the routing is complete.
+ */
+ bool route( const VECTOR2I& aP );
+
+ /**
+ * Function updateLeadingRatLine()
+ *
+ * Draws the "leading" ratsnest line, which connects the end of currently
+ * routed track and the nearest yet unrouted item. If the routing for
+ * current net is complete, draws nothing.
+ */
+ void updateLeadingRatLine();
+
+ /**
+ * Function setWorld()
+ *
+ * Sets the board to route.
+ */
+ void setWorld( PNS_NODE* aWorld );
+
+ /**
+ * Function startPlacement()
+ *
+ * Initializes placement of a new line with given parameters.
+ */
+ void initPlacement( bool aSplitSeg = false );
+
+ /**
+ * Function setInitialDirection()
+ *
+ * Sets preferred direction of the very first track segment to be laid.
+ * Used by posture switching mechanism.
+ */
+ void setInitialDirection( const DIRECTION_45& aDirection );
+
+ /**
+ * 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 );
+
+ /**
+ * 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 );
+
+ /**
+ * 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 checkObtusity()
+ *
+ * Helper function, checking if segments a and b form an obtuse angle
+ * (in 45-degree regime).
+ * @return true, if angle (aA, aB) is obtuse
+ */
+ bool checkObtusity( const SEG& aA, const SEG& aB ) const;
+
+ /**
+ * Function handleSelfIntersections()
+ *
+ * Checks if the head of the track intersects its tail. If so, cuts the
+ * tail up to the intersecting segment and fixes the head direction to match
+ * the last segment before the cut.
+ * @return true if the line has been changed.
+ */
+ bool handleSelfIntersections();
+
+ /**
+ * Function handlePullback()
+ *
+ * Deals with pull-back: reduces the tail if head trace is moved backwards
+ * wrs to the current tail direction.
+ * @return true if the line has been changed.
+ */
+ bool handlePullback();
+
+ /**
+ * Function mergeHead()
+ *
+ * Moves "estabished" segments from the head to the tail if certain
+ * conditions are met.
+ * @return true, if the line has been changed.
+ */
+ bool mergeHead();
+
+ /**
+ * Function reduceTail()
+ *
+ * Attempts to reduce the numer of segments in the tail by trying to replace a
+ * certain number of latest tail segments with a direct trace leading to aEnd
+ * that does not collide with anything.
+ * @param aEnd: current routing destination point.
+ * @return true if the line has been changed.
+ */
+ bool reduceTail( const VECTOR2I& aEnd );
+
+ /**
+ * Function optimizeTailHeadTransition()
+ *
+ * Tries to reduce the corner count of the most recent part of tail/head by
+ * merging obtuse/collinear segments.
+ * @return true, if the line has been changed.
+ */
+ bool optimizeTailHeadTransition();
+
+ /**
+ * Function routeHead()
+ *
+ * Computes the head trace between the current start point (m_p_start) and
+ * point aP, starting with direction defined in m_direction. The trace walks
+ * around all colliding solid or non-movable items. Movable segments are
+ * ignored, as they'll be handled later by the shove algorithm.
+ */
+ bool routeHead( const VECTOR2I& aP, PNS_LINE& aNewHead);
+
+ /**
+ * Function routeStep()
+ *
+ * Performs a single routing alorithm step, for the end point aP.
+ * @param aP ending point of current route
+ * @return true, if the line has been changed.
+ */
+ void routeStep( const VECTOR2I& aP );
+
+ ///> route step, walkaround mode
+ bool rhWalkOnly( const VECTOR2I& aP, PNS_LINE& aNewHead);
+
+ ///> route step, shove mode
+ bool rhShoveOnly( const VECTOR2I& aP, PNS_LINE& aNewHead);
+
+ ///> route step, mark obstacles mode
+ bool rhMarkObstacles( const VECTOR2I& aP, PNS_LINE& aNewHead );
+
+ const PNS_VIA makeVia ( const VECTOR2I& aP );
+
+ bool buildInitialLine( const VECTOR2I& aP, PNS_LINE& aHead );
+
+ ///> current routing direction
+ DIRECTION_45 m_direction;
+
+ ///> routing direction for new traces
+ DIRECTION_45 m_initial_direction;
+
+ ///> routing "head": volatile part of the track from the previously
+ /// analyzed point to the current routing destination
+ PNS_LINE m_head;
+
+ ///> routing "tail": part of the track that has been already fixed due to collisions with obstacles
+ PNS_LINE m_tail;
+
+ ///> pointer to world to search colliding items
+ PNS_NODE* m_world;
+
+ ///> current routing start point (end of tail, beginning of head)
+ VECTOR2I m_p_start;
+
+ ///> The shove engine
+ PNS_SHOVE* m_shove;
+
+ ///> Current world state
+ PNS_NODE* m_currentNode;
+
+ ///> Postprocessed world state (including marked collisions & removed loops)
+ PNS_NODE* m_lastNode;
+
+ PNS_SIZES_SETTINGS m_sizes;
+
+ ///> Are we placing a via?
+ bool m_placingVia;
+
+ int m_currentNet;
+ int m_currentLayer;
+
+ VECTOR2I m_currentEnd, m_currentStart;
+ PNS_LINE m_currentTrace;
+
+ PNS_MODE m_currentMode;
+ PNS_ITEM* m_startItem;
+
+ bool m_idle;
+ bool m_chainedPlacement;
+ bool m_splitSeg;
+ bool m_orthoMode;
+};
+
+#endif // __PNS_LINE_PLACER_H
diff --git a/pcbnew/router/pns_logger.cpp b/pcbnew/router/pns_logger.cpp
new file mode 100644
index 0000000..69c725f
--- /dev/null
+++ b/pcbnew/router/pns_logger.cpp
@@ -0,0 +1,203 @@
+/*
+ * 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>
+#include <geometry/shape_convex.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& aName, int aIter )
+{
+ if( m_groupOpened )
+ m_theLog << "endgroup" << std::endl;
+
+ m_theLog << "group " << aName << " " << aIter << 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* aItem, int aKind, const std::string aName )
+{
+ m_theLog << "item " << aKind << " " << aName << " ";
+ m_theLog << aItem->Net() << " " << aItem->Layers().Start() << " " <<
+ aItem->Layers().End() << " " << aItem->Marker() << " " << aItem->Rank();
+
+ switch( aItem->Kind() )
+ {
+ case PNS_ITEM::LINE:
+ {
+ PNS_LINE* l = (PNS_LINE*) aItem;
+ 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 ( aItem->Shape() );
+ m_theLog << std::endl;
+ break;
+ }
+
+ case PNS_ITEM::SEGMENT:
+ {
+ PNS_SEGMENT* s =(PNS_SEGMENT*) aItem;
+ 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*) aItem;
+ m_theLog << " solid 0 0 ";
+ dumpShape( s->Shape() );
+ m_theLog << std::endl;
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+
+void PNS_LOGGER::Log( const SHAPE_LINE_CHAIN *aL, int aKind, const std::string aName )
+{
+ m_theLog << "item " << aKind << " " << aName << " ";
+ m_theLog << 0 << " " << 0 << " " << 0 << " " << 0 << " " << 0;
+ m_theLog << " line ";
+ m_theLog << 0 << " " << 0 << " ";
+ dumpShape( aL );
+ m_theLog << std::endl;
+}
+
+
+void PNS_LOGGER::Log( const VECTOR2I& aStart, const VECTOR2I& aEnd,
+ int aKind, const std::string aName)
+{
+}
+
+
+void PNS_LOGGER::dumpShape( const SHAPE* aSh )
+{
+ switch( aSh->Type() )
+ {
+ case SH_LINE_CHAIN:
+ {
+ const SHAPE_LINE_CHAIN* lc = (const SHAPE_LINE_CHAIN*) aSh;
+ 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*) aSh;
+ m_theLog << "circle " << c->GetCenter().x << " " << c->GetCenter().y << " " << c->GetRadius();
+ break;
+ }
+
+ case SH_RECT:
+ {
+ const SHAPE_RECT* r = (const SHAPE_RECT*) aSh;
+ 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*) aSh;
+ m_theLog << "linechain 2 0 " << s->GetSeg().A.x << " " << s->GetSeg().A.y << " " <<
+ s->GetSeg().B.x << " " << s->GetSeg().B.y;
+ break;
+ }
+
+ case SH_CONVEX:
+ {
+ const SHAPE_CONVEX* c = (const SHAPE_CONVEX*) aSh;
+ m_theLog << "convex " << c->PointCount() << " ";
+
+ for( int i = 0; i < c->PointCount(); i++ )
+ m_theLog << c->CPoint( i ).x << " " << c->CPoint( i ).y << " ";
+
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+
+void PNS_LOGGER::Save( const std::string& aFilename )
+{
+ EndGroup();
+
+ FILE* f = fopen( aFilename.c_str(), "wb" );
+ printf( "Saving to '%s' [%p]\n", aFilename.c_str(), f );
+ const std::string s = m_theLog.str();
+ fwrite( s.c_str(), 1, s.length(), f );
+ fclose( f );
+}
diff --git a/pcbnew/router/pns_logger.h b/pcbnew/router/pns_logger.h
new file mode 100644
index 0000000..b38fd42
--- /dev/null
+++ b/pcbnew/router/pns_logger.h
@@ -0,0 +1,59 @@
+/*
+ * 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& aFilename );
+ void Clear();
+
+ void NewGroup( const std::string& aName, int aIter = 0 );
+ void EndGroup();
+
+ void Log( const PNS_ITEM* aItem, int aKind = 0, const std::string aName = std::string() );
+ void Log( const SHAPE_LINE_CHAIN *aL, int aKind = 0, const std::string aName = std::string() );
+ void Log( const VECTOR2I& aStart, const VECTOR2I& aEnd, int aKind = 0,
+ const std::string aName = std::string() );
+
+private:
+ void dumpShape( const SHAPE* aSh );
+
+ bool m_groupOpened;
+ std::stringstream m_theLog;
+};
+
+#endif
diff --git a/pcbnew/router/pns_meander.cpp b/pcbnew/router/pns_meander.cpp
new file mode 100644
index 0000000..6739401
--- /dev/null
+++ b/pcbnew/router/pns_meander.cpp
@@ -0,0 +1,617 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2014 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/foreach.hpp>
+
+#include <base_units.h> // God forgive me doing this...
+#include <colors.h>
+
+#include "trace.h"
+
+#include "pns_node.h"
+#include "pns_itemset.h"
+#include "pns_topology.h"
+#include "pns_meander.h"
+#include "pns_meander_placer_base.h"
+#include "pns_router.h"
+
+const PNS_MEANDER_SETTINGS& PNS_MEANDER_SHAPE::Settings() const
+{
+ return m_placer->MeanderSettings();
+}
+
+const PNS_MEANDER_SETTINGS& PNS_MEANDERED_LINE::Settings() const
+{
+ return m_placer->MeanderSettings();
+}
+
+void PNS_MEANDERED_LINE::MeanderSegment( const SEG& aBase, int aBaseIndex )
+{
+ double base_len = aBase.Length();
+
+ SHAPE_LINE_CHAIN lc;
+
+ bool side = true;
+ VECTOR2D dir( aBase.B - aBase.A );
+
+ if( !m_dual )
+ AddCorner( aBase.A );
+
+ bool turning = false;
+ bool started = false;
+
+ m_last = aBase.A;
+
+ do
+ {
+ PNS_MEANDER_SHAPE m( m_placer, m_width, m_dual );
+
+ m.SetBaselineOffset( m_baselineOffset );
+ m.SetBaseIndex( aBaseIndex );
+
+ double thr = (double) m.spacing();
+
+ bool fail = false;
+ double remaining = base_len - ( m_last - aBase.A ).EuclideanNorm();
+
+ if( remaining < Settings( ).m_step )
+ break;
+
+ if( remaining > 3.0 * thr )
+ {
+ if( !turning )
+ {
+ for( int i = 0; i < 2; i++ )
+ {
+ if ( m.Fit( MT_CHECK_START, aBase, m_last, i ) )
+ {
+ turning = true;
+ AddMeander( new PNS_MEANDER_SHAPE( m ) );
+ side = !i;
+ started = true;
+ break;
+ }
+ }
+
+ if( !turning )
+ {
+ fail = true;
+
+ for( int i = 0; i < 2; i++ )
+ {
+ if ( m.Fit ( MT_SINGLE, aBase, m_last, i ) )
+ {
+ AddMeander( new PNS_MEANDER_SHAPE( m ) );
+ fail = false;
+ started = false;
+ side = !i;
+ break;
+ }
+ }
+ }
+ } else {
+ bool rv = m.Fit( MT_CHECK_FINISH, aBase, m_last, side );
+
+ if( rv )
+ {
+ m.Fit( MT_TURN, aBase, m_last, side );
+ AddMeander( new PNS_MEANDER_SHAPE( m ) );
+ started = true;
+ } else {
+ m.Fit( MT_FINISH, aBase, m_last, side );
+ started = false;
+ AddMeander( new PNS_MEANDER_SHAPE( m ) );
+ turning = false;
+ }
+
+ side = !side;
+ }
+ } else if( started )
+ {
+ bool rv = m.Fit( MT_FINISH, aBase, m_last, side );
+ if( rv )
+ AddMeander( new PNS_MEANDER_SHAPE( m ) );
+
+ break;
+
+ } else {
+ fail = true;
+ }
+
+ remaining = base_len - ( m_last - aBase.A ).EuclideanNorm( );
+
+ if( remaining < Settings( ).m_step )
+ break;
+
+ if( fail )
+ {
+ PNS_MEANDER_SHAPE tmp( m_placer, m_width, m_dual );
+ tmp.SetBaselineOffset( m_baselineOffset );
+ tmp.SetBaseIndex( aBaseIndex );
+
+ int nextP = tmp.spacing() - 2 * tmp.cornerRadius() + Settings().m_step;
+ VECTOR2I pn = m_last + dir.Resize( nextP );
+
+ if( aBase.Contains( pn ) && !m_dual )
+ {
+ AddCorner( pn );
+ } else
+ break;
+ }
+
+
+ } while( true );
+
+ if( !m_dual )
+ AddCorner( aBase.B );
+}
+
+
+int PNS_MEANDER_SHAPE::cornerRadius() const
+{
+ int cr = (int64_t) spacing() * Settings().m_cornerRadiusPercentage / 200;
+
+ return cr;
+}
+
+
+int PNS_MEANDER_SHAPE::spacing( ) const
+{
+ if ( !m_dual )
+ return std::max( 2 * m_width, Settings().m_spacing );
+ else
+ {
+ int sp = 2 * ( m_width + std::abs( m_baselineOffset ) );
+ return std::max ( sp, Settings().m_spacing );
+ }
+}
+
+
+SHAPE_LINE_CHAIN PNS_MEANDER_SHAPE::circleQuad( VECTOR2D aP, VECTOR2D aDir, bool aSide )
+{
+ SHAPE_LINE_CHAIN lc;
+
+ if( aDir.EuclideanNorm( ) == 0.0f )
+ {
+ lc.Append( aP );
+ return lc;
+ }
+
+ VECTOR2D dir_u( aDir );
+ VECTOR2D dir_v( aDir.Perpendicular( ) );
+
+ const int ArcSegments = Settings().m_cornerArcSegments;
+
+ double radius = (double) aDir.EuclideanNorm();
+ double angleStep = M_PI / 2.0 / (double) ArcSegments;
+
+ double correction = 12.0 * radius * ( 1.0 - cos( angleStep / 2.0 ) );
+
+ if( !m_dual )
+ correction = 0.0;
+ else if( radius < m_meanCornerRadius )
+ correction = 0.0;
+
+ VECTOR2D p = aP;
+ lc.Append( ( int ) p.x, ( int ) p.y );
+
+ VECTOR2D dir_uu = dir_u.Resize( radius - correction );
+ VECTOR2D dir_vv = dir_v.Resize( radius - correction );
+
+ VECTOR2D shift = dir_u.Resize( correction );
+
+ for( int i = ArcSegments - 1; i >= 0; i-- )
+ {
+ double alpha = (double) i / (double) ( ArcSegments - 1 ) * M_PI / 2.0;
+ p = aP + shift + dir_uu * cos( alpha ) + dir_vv * ( aSide ? -1.0 : 1.0 ) * ( 1.0 - sin( alpha ) );
+ lc.Append( ( int ) p.x, ( int ) p.y );
+ }
+
+ p = aP + dir_u + dir_v * ( aSide ? -1.0 : 1.0 );
+ lc.Append( ( int ) p.x, ( int ) p.y );
+
+ return lc;
+}
+
+
+VECTOR2I PNS_MEANDER_SHAPE::reflect( VECTOR2I p, const SEG& line )
+{
+ typedef int64_t ecoord;
+ VECTOR2I d = line.B - line.A;
+ ecoord l_squared = d.Dot( d );
+ ecoord t = d.Dot( p - line.A );
+ VECTOR2I c, rv;
+
+ if( !l_squared )
+ c = p;
+ else {
+ c.x = line.A.x + rescale( t, (ecoord) d.x, l_squared );
+ c.y = line.A.y + rescale( t, (ecoord) d.y, l_squared );
+ }
+
+ return 2 * c - p;
+}
+
+
+void PNS_MEANDER_SHAPE::start( SHAPE_LINE_CHAIN* aTarget, const VECTOR2D& aWhere, const VECTOR2D& aDir )
+{
+ m_currentTarget = aTarget;
+ m_currentTarget->Clear();
+ m_currentTarget->Append( aWhere );
+ m_currentDir = aDir;
+ m_currentPos = aWhere;
+}
+
+
+void PNS_MEANDER_SHAPE::forward( int aLength )
+{
+ m_currentPos += m_currentDir.Resize( aLength );
+ m_currentTarget->Append( m_currentPos );
+}
+
+
+void PNS_MEANDER_SHAPE::turn( int aAngle )
+{
+ m_currentDir = m_currentDir.Rotate( (double) aAngle * M_PI / 180.0 );
+}
+
+
+void PNS_MEANDER_SHAPE::arc( int aRadius, bool aSide )
+{
+ if( aRadius <= 0 )
+ {
+ turn( aSide ? -90 : 90 );
+ return;
+ }
+
+ VECTOR2D dir = m_currentDir.Resize( (double) aRadius );
+ SHAPE_LINE_CHAIN arc = circleQuad( m_currentPos, dir, aSide );
+ m_currentPos = arc.CPoint( -1 );
+ m_currentDir = dir.Rotate( aSide ? -M_PI / 2.0 : M_PI / 2.0 );
+
+ m_currentTarget->Append ( arc );
+}
+
+
+void PNS_MEANDER_SHAPE::uShape( int aSides, int aCorner, int aTop )
+{
+ forward( aSides );
+ arc( aCorner, true );
+ forward( aTop );
+ arc( aCorner, true );
+ forward( aSides );
+}
+
+
+SHAPE_LINE_CHAIN PNS_MEANDER_SHAPE::genMeanderShape( VECTOR2D aP, VECTOR2D aDir,
+ bool aSide, PNS_MEANDER_TYPE aType, int aAmpl, int aBaselineOffset )
+{
+ const PNS_MEANDER_SETTINGS& st = Settings();
+ int cr = cornerRadius();
+ int offset = aBaselineOffset;
+ int spc = spacing();
+
+ if( aSide )
+ offset *= -1;
+
+ VECTOR2D dir_u_b( aDir.Resize( offset ) );
+ VECTOR2D dir_v_b( dir_u_b.Perpendicular() );
+
+ if( 2 * cr > aAmpl )
+ {
+ cr = aAmpl / 2;
+ }
+
+ if( 2 * cr > spc )
+ {
+ cr = spc / 2;
+ }
+
+ m_meanCornerRadius = cr;
+
+ SHAPE_LINE_CHAIN lc;
+
+ start( &lc, aP + dir_v_b, aDir );
+
+ switch( aType )
+ {
+ case MT_EMPTY:
+ {
+ lc.Append( aP + dir_v_b + aDir );
+ break;
+ }
+ case MT_START:
+ {
+ arc( cr - offset, false );
+ uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr );
+ forward( std::min( cr - offset, cr + offset ) );
+ forward( std::abs( offset ) );
+
+ break;
+ }
+
+ case MT_FINISH:
+ {
+ start( &lc, aP - dir_u_b, aDir );
+ turn ( 90 );
+ forward( std::min( cr - offset, cr + offset ) );
+ forward( std::abs( offset ) );
+ uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr );
+ arc( cr - offset, false );
+ break;
+ }
+
+ case MT_TURN:
+ {
+ start( &lc, aP - dir_u_b, aDir );
+ turn( 90 );
+ forward( std::abs( offset ) );
+ uShape ( aAmpl - cr, cr + offset, spc - 2 * cr );
+ forward( std::abs( offset ) );
+ break;
+ }
+
+ case MT_SINGLE:
+ {
+ arc( cr - offset, false );
+ uShape( aAmpl - 2 * cr + std::abs( offset ), cr + offset, spc - 2 * cr );
+ arc( cr - offset, false );
+ lc.Append( aP + dir_v_b + aDir.Resize ( 2 * st.m_spacing ) );
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if( aSide )
+ {
+ SEG axis ( aP, aP + aDir );
+
+ for( int i = 0; i < lc.PointCount(); i++ )
+ lc.Point( i ) = reflect( lc.CPoint( i ), axis );
+ }
+
+ return lc;
+}
+
+
+bool PNS_MEANDERED_LINE::CheckSelfIntersections( PNS_MEANDER_SHAPE* aShape, int aClearance )
+{
+ for( int i = m_meanders.size() - 1; i >= 0; i-- )
+ {
+ PNS_MEANDER_SHAPE* m = m_meanders[i];
+
+ if( m->Type() == MT_EMPTY || m->Type() == MT_CORNER )
+ continue;
+
+ const SEG& b1 = aShape->BaseSegment();
+ const SEG& b2 = m->BaseSegment();
+
+ if( b1.ApproxParallel( b2 ) )
+ continue;
+
+ int n = m->CLine( 0 ).SegmentCount();
+
+ for( int j = n - 1; j >= 0; j-- )
+ if( aShape->CLine( 0 ).Collide ( m->CLine( 0 ) .CSegment( j ), aClearance ) )
+ return false;
+ }
+
+ return true;
+}
+
+
+bool PNS_MEANDER_SHAPE::Fit( PNS_MEANDER_TYPE aType, const SEG& aSeg, const VECTOR2I& aP, bool aSide )
+{
+ const PNS_MEANDER_SETTINGS& st = Settings();
+
+ bool checkMode = false;
+ PNS_MEANDER_TYPE prim1, prim2;
+
+ if( aType == MT_CHECK_START )
+ {
+ prim1 = MT_START;
+ prim2 = MT_TURN;
+ checkMode = true;
+ }
+ else if( aType == MT_CHECK_FINISH )
+ {
+ prim1 = MT_TURN;
+ prim2 = MT_FINISH;
+ checkMode = true;
+ }
+
+ if( checkMode )
+ {
+ PNS_MEANDER_SHAPE m1( m_placer, m_width, m_dual );
+ PNS_MEANDER_SHAPE m2( m_placer, m_width, m_dual );
+
+ m1.SetBaselineOffset( m_baselineOffset );
+ m2.SetBaselineOffset( m_baselineOffset );
+
+ bool c1 = m1.Fit( prim1, aSeg, aP, aSide );
+ bool c2 = false;
+
+ if( c1 )
+ c2 = m2.Fit( prim2, aSeg, m1.End(), !aSide );
+
+ if( c1 && c2 )
+ {
+ m_type = prim1;
+ m_shapes[0] = m1.m_shapes[0];
+ m_shapes[1] = m1.m_shapes[1];
+ m_baseSeg =aSeg;
+ m_p0 = aP;
+ m_side = aSide;
+ m_amplitude = m1.Amplitude();
+ m_dual = m1.m_dual;
+ m_baseSeg = m1.m_baseSeg;
+ m_baseIndex = m1.m_baseIndex;
+ updateBaseSegment();
+ m_baselineOffset = m1.m_baselineOffset;
+ return true;
+ } else
+ return false;
+ }
+
+ int minAmpl = st.m_minAmplitude;
+ int maxAmpl = st.m_maxAmplitude;
+
+ if( m_dual )
+ {
+ minAmpl = std::max( minAmpl, 2 * std::abs( m_baselineOffset ) );
+ maxAmpl = std::max( maxAmpl, 2 * std::abs( m_baselineOffset ) );
+ }
+
+ for( int ampl = maxAmpl; ampl >= minAmpl; ampl -= st.m_step )
+ {
+ if( m_dual )
+ {
+ m_shapes[0] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, ampl, m_baselineOffset );
+ m_shapes[1] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, ampl, -m_baselineOffset );
+ }
+ else
+ {
+ m_shapes[0] = genMeanderShape( aP, aSeg.B - aSeg.A, aSide, aType, ampl, 0 );
+ }
+
+ m_type = aType;
+ m_baseSeg = aSeg;
+ m_p0 = aP;
+ m_side = aSide;
+ m_amplitude = ampl;
+
+ updateBaseSegment();
+
+ if( m_placer->CheckFit( this ) )
+ return true;
+ }
+
+ return false;
+}
+
+
+void PNS_MEANDER_SHAPE::Recalculate()
+{
+ m_shapes[0] = genMeanderShape( m_p0, m_baseSeg.B - m_baseSeg.A, m_side, m_type, m_amplitude, m_dual ? m_baselineOffset : 0 );
+
+ if( m_dual )
+ m_shapes[1] = genMeanderShape( m_p0, m_baseSeg.B - m_baseSeg.A, m_side, m_type, m_amplitude, -m_baselineOffset );
+
+ updateBaseSegment();
+}
+
+
+void PNS_MEANDER_SHAPE::Resize( int aAmpl )
+{
+ if( aAmpl < 0 )
+ return;
+
+ m_amplitude = aAmpl;
+
+ Recalculate();
+}
+
+
+void PNS_MEANDER_SHAPE::MakeEmpty()
+{
+ updateBaseSegment();
+
+ VECTOR2I dir = m_clippedBaseSeg.B - m_clippedBaseSeg.A;
+
+ m_type = MT_EMPTY;
+
+ m_shapes[0] = genMeanderShape ( m_p0, dir, m_side, m_type, 0, m_dual ? m_baselineOffset : 0 );
+
+ if( m_dual )
+ m_shapes[1] = genMeanderShape( m_p0, dir, m_side, m_type, 0, -m_baselineOffset );
+}
+
+
+void PNS_MEANDERED_LINE::AddCorner( const VECTOR2I& aA, const VECTOR2I& aB )
+{
+ PNS_MEANDER_SHAPE* m = new PNS_MEANDER_SHAPE( m_placer, m_width, m_dual );
+
+ m->MakeCorner( aA, aB );
+ m_last = aA;
+
+ m_meanders.push_back( m );
+}
+
+
+void PNS_MEANDER_SHAPE::MakeCorner( VECTOR2I aP1, VECTOR2I aP2 )
+{
+ SetType( MT_CORNER );
+ m_shapes[0].Clear();
+ m_shapes[1].Clear();
+ m_shapes[0].Append( aP1 );
+ m_shapes[1].Append( aP2 );
+ m_clippedBaseSeg.A = aP1;
+ m_clippedBaseSeg.B = aP1;
+}
+
+
+void PNS_MEANDERED_LINE::AddMeander( PNS_MEANDER_SHAPE* aShape )
+{
+ m_last = aShape->BaseSegment().B;
+ m_meanders.push_back( aShape );
+}
+
+
+void PNS_MEANDERED_LINE::Clear()
+{
+ BOOST_FOREACH( PNS_MEANDER_SHAPE* m, m_meanders )
+ {
+ delete m;
+ }
+
+ m_meanders.clear( );
+}
+
+
+int PNS_MEANDER_SHAPE::BaselineLength() const
+{
+ return m_clippedBaseSeg.Length();
+}
+
+
+int PNS_MEANDER_SHAPE::MaxTunableLength() const
+{
+ return CLine( 0 ).Length();
+}
+
+
+void PNS_MEANDER_SHAPE::updateBaseSegment( )
+{
+ if( m_dual )
+ {
+ VECTOR2I midpA = ( CLine( 0 ).CPoint( 0 ) + CLine( 1 ).CPoint( 0 ) ) / 2;
+ VECTOR2I midpB = ( CLine( 0 ).CPoint( -1 ) + CLine( 1 ).CPoint( -1 ) ) / 2;
+
+ m_clippedBaseSeg.A = m_baseSeg.LineProject( midpA );
+ m_clippedBaseSeg.B = m_baseSeg.LineProject( midpB );
+ }
+ else
+ {
+ m_clippedBaseSeg.A = m_baseSeg.LineProject( CLine( 0 ).CPoint( 0 ) );
+ m_clippedBaseSeg.B = m_baseSeg.LineProject( CLine( 0 ).CPoint( -1 ) );
+ }
+}
diff --git a/pcbnew/router/pns_meander.h b/pcbnew/router/pns_meander.h
new file mode 100644
index 0000000..5d1ae51
--- /dev/null
+++ b/pcbnew/router/pns_meander.h
@@ -0,0 +1,514 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PNS_MEANDER_H
+#define __PNS_MEANDER_H
+
+#include <math/vector2d.h>
+
+#include <geometry/shape.h>
+#include <geometry/shape_line_chain.h>
+
+class PNS_MEANDER_PLACER_BASE;
+
+///< Shapes of available meanders
+enum PNS_MEANDER_TYPE {
+ MT_SINGLE, // _|^|_, single-sided
+ MT_START, // _|^|
+ MT_FINISH, // |^|_
+ MT_TURN, // |^| or |_|
+ MT_CHECK_START, // try fitting a start type, but don't produce a line
+ MT_CHECK_FINISH, // try fitting a finish type, but don't produce a line
+ MT_CORNER, // line corner
+ MT_EMPTY // no meander (straight line)
+};
+
+/**
+ * Class PNS_MEANDER_SETTINGS
+ *
+ * Holds dimensions for the meandering algorithm.
+ */
+class PNS_MEANDER_SETTINGS
+{
+public:
+
+ ///> meander corner shape
+ enum CornerType {
+ ROUND = 1, // rounded (90 degree arc)
+ CHAMFER // chamfered (45 degree segment)
+ };
+
+ PNS_MEANDER_SETTINGS()
+ {
+ m_minAmplitude = 100000;
+ m_maxAmplitude = 1000000;
+ m_step = 50000;
+ m_spacing = 600000;
+ m_targetLength = 100000000;
+ m_targetSkew = 0;
+ m_cornerType = ROUND;
+ m_cornerRadiusPercentage = 100;
+ m_lengthTolerance = 100000;
+ m_cornerArcSegments = 8;
+ }
+
+ ///> minimum meandering amplitude
+ int m_minAmplitude;
+ ///> maximum meandering amplitude
+ int m_maxAmplitude;
+ ///> meandering period/spacing (see dialog picture for explanation)
+ int m_spacing;
+ ///> amplitude/spacing adjustment step
+ int m_step;
+ ///> desired length of the tuned line/diff pair
+ int m_targetLength;
+ ///> type of corners for the meandered line
+ CornerType m_cornerType;
+ ///> rounding percentage (0 - 100)
+ int m_cornerRadiusPercentage;
+ ///> allowable tuning error
+ int m_lengthTolerance;
+ ///> number of line segments for arc approximation
+ int m_cornerArcSegments;
+ ///> target skew value for diff pair de-skewing
+ int m_targetSkew;
+};
+
+class PNS_MEANDERED_LINE;
+
+/**
+ * Class PNS_MEANDER_SETTINGS
+ *
+ * Holds the geometry of a single meander.
+ */
+class PNS_MEANDER_SHAPE
+{
+public:
+ /**
+ * Constructor
+ *
+ * @param aPlacer the meander placer instance
+ * @param aWidth width of the meandered line
+ * @param aIsDual when true, the shape contains two meandered
+ * lines at a given offset (diff pairs)
+ */
+ PNS_MEANDER_SHAPE( PNS_MEANDER_PLACER_BASE *aPlacer, int aWidth, bool aIsDual = false ) :
+ m_placer( aPlacer ),
+ m_dual( aIsDual ),
+ m_width( aWidth ),
+ m_baselineOffset( 0 )
+ {
+ // Do not leave unitialized members, and keep static analyser quiet:
+ m_type = MT_SINGLE;
+ m_amplitude = 0;
+ m_side = false;
+ m_baseIndex = 0;
+ m_currentTarget = NULL;
+ m_meanCornerRadius = 0;
+ }
+
+ /**
+ * Function SetType()
+ *
+ * Sets the type of the meander.
+ */
+ void SetType( PNS_MEANDER_TYPE aType )
+ {
+ m_type = aType;
+ }
+
+ /**
+ * Function Type()
+ *
+ * @return the type of the meander.
+ */
+ PNS_MEANDER_TYPE Type() const
+ {
+ return m_type;
+ }
+
+ /**
+ * Function SetBaseIndex()
+ *
+ * Sets an auxillary index of the segment being meandered in its original PNS_LINE.
+ */
+ void SetBaseIndex( int aIndex )
+ {
+ m_baseIndex = aIndex;
+ }
+
+ /**
+ * Function BaseIndex()
+ *
+ * @return auxillary index of the segment being meandered in its original PNS_LINE.
+ */
+ int BaseIndex() const
+ {
+ return m_baseIndex;
+ }
+
+ /**
+ * Function Amplitude()
+ *
+ * @return the amplitude of the meander shape.
+ */
+ int Amplitude() const
+ {
+ return m_amplitude;
+ }
+
+ /**
+ * Function MakeCorner()
+ *
+ * Creates a dummy meander shape representing a line corner. Used to define
+ * the starts/ends of meandered segments.
+ * @param aP1 corner point of the 1st line
+ * @param aP2 corner point of the 2nd line (if m_dual == true)
+ */
+ void MakeCorner( VECTOR2I aP1, VECTOR2I aP2 = VECTOR2I( 0, 0 ) );
+
+ /**
+ * Function Resize()
+ *
+ * Changes the amplitude of the meander shape to aAmpl and recalculates
+ * the resulting line chain.
+ * @param aAmpl new amplitude.
+ */
+ void Resize( int aAmpl );
+
+ /**
+ * Function Recalculate()
+ *
+ * Recalculates the line chain representing the meanders's shape.
+ */
+ void Recalculate();
+
+ /**
+ * Function IsDual()
+ *
+ * @return true if the shape represents 2 parallel lines (diff pair).
+ */
+ bool IsDual() const
+ {
+ return m_dual;
+ }
+
+ /**
+ * Function Side()
+ *
+ * @return true if the meander is to the right of its base segment.
+ */
+ bool Side() const
+ {
+ return m_side;
+ }
+
+ /**
+ * Function End()
+ *
+ * @return end vertex of the base segment of the meander shape.
+ */
+ VECTOR2I End() const
+ {
+ return m_clippedBaseSeg.B;
+ }
+
+ /**
+ * Function CLine()
+ *
+ * @return the line chain representing the shape of the meander.
+ */
+ const SHAPE_LINE_CHAIN& CLine( int aShape ) const
+ {
+ return m_shapes[aShape];
+ }
+
+ /**
+ * Function MakeEmpty()
+ *
+ * Replaces the meander with straight bypass line(s), effectively
+ * clearing it.
+ */
+ void MakeEmpty();
+
+ /**
+ * Function Fit()
+ *
+ * Attempts to fit a meander of a given type onto a segment, avoiding
+ * collisions with other board features.
+ * @param aType type of meander shape
+ * @param aSeg base segment for meandering
+ * @param aP start point of the meander
+ * @param aSide side of aSeg to put the meander on (true = right)
+ * @return true on success.
+ */
+ bool Fit( PNS_MEANDER_TYPE aType, const SEG& aSeg, const VECTOR2I& aP, bool aSide );
+
+ /**
+ * Function BaseSegment()
+ *
+ * Returns the base segment the meadner was fitted to.
+ * @return the base segment.
+ */
+ const SEG& BaseSegment() const
+ {
+ return m_clippedBaseSeg;
+ }
+
+ /**
+ * Function BaselineLength()
+ *
+ * @return length of the base segment for the meander (i.e.
+ * the minimum tuned length.
+ */
+ int BaselineLength() const;
+
+ /**
+ * Function MaxTunableLength()
+ *
+ * @return the length of the fitted line chain.
+ */
+ int MaxTunableLength() const;
+
+ /**
+ * Function Settings()
+ *
+ * @return the current meandering settings.
+ */
+ const PNS_MEANDER_SETTINGS& Settings() const;
+
+ /**
+ * Function Width()
+ *
+ * @return width of the meandered line.
+ */
+ int Width() const
+ {
+ return m_width;
+ }
+
+ /**
+ * Function SetBaselineOffset()
+ *
+ * Sets the parallel offset between the base segment and the meandered
+ * line. Used for dual menaders (diff pair) only.
+ * @param aOffset the offset
+ */
+ void SetBaselineOffset( int aOffset )
+ {
+ m_baselineOffset = aOffset;
+ }
+
+private:
+ friend class PNS_MEANDERED_LINE;
+
+ ///> starts turtle drawing
+ void start( SHAPE_LINE_CHAIN* aTarget, const VECTOR2D& aWhere, const VECTOR2D& aDir );
+ ///> moves turtle forward by aLength
+ void forward( int aLength );
+ ///> turns the turtle by aAngle
+ void turn( int aAngle );
+ ///> tells the turtle to draw an arc of given radius and turn direction
+ void arc( int aRadius, bool aSide );
+ ///> tells the turtle to draw an U-like shape
+ void uShape( int aSides, int aCorner, int aTop );
+
+ ///> generates a 90-degree circular arc
+ SHAPE_LINE_CHAIN circleQuad( VECTOR2D aP, VECTOR2D aDir, bool aSide );
+
+ ///> reflects a point onto other side of a given segment
+ VECTOR2I reflect( VECTOR2I aP, const SEG& aLine );
+
+ ///> produces a meander shape of given type
+ SHAPE_LINE_CHAIN genMeanderShape( VECTOR2D aP, VECTOR2D aDir, bool aSide, PNS_MEANDER_TYPE aType, int aAmpl, int aBaselineOffset = 0 );
+
+ ///> recalculates the clipped baseline after the parameters of
+ ///> the meander have been changed.
+ void updateBaseSegment();
+
+ ///> returns sanitized corner radius value
+ int cornerRadius() const;
+
+ ///> returns sanitized spacing value
+ int spacing() const;
+
+ ///> the type
+ PNS_MEANDER_TYPE m_type;
+ ///> the placer that placed this meander
+ PNS_MEANDER_PLACER_BASE* m_placer;
+ ///> dual or single line
+ bool m_dual;
+ ///> width of the line
+ int m_width;
+ ///> amplitude of the meander
+ int m_amplitude;
+ ///> offset wrs the base segment (dual only)
+ int m_baselineOffset;
+ ///> average radius of meander corners (for correction of DP meanders)
+ int m_meanCornerRadius;
+ ///> first point of the meandered line
+ VECTOR2I m_p0;
+ ///> base segment (unclipped)
+ SEG m_baseSeg;
+ ///> base segment (clipped)
+ SEG m_clippedBaseSeg;
+ ///> side (true = right)
+ bool m_side;
+ ///> the actual shapes (0 used for single, both for dual)
+ SHAPE_LINE_CHAIN m_shapes[2];
+ ///> index of the meandered segment in the base line
+ int m_baseIndex;
+ ///> current turtle direction
+ VECTOR2D m_currentDir;
+ ///> current turtle position
+ VECTOR2D m_currentPos;
+ ///> the line the turtle is drawing on
+ SHAPE_LINE_CHAIN* m_currentTarget;
+};
+
+
+/**
+ * Class PNS_MEANDERED_LINE
+ *
+ * Represents a set of meanders fitted over a single or two lines.
+ */
+class PNS_MEANDERED_LINE
+{
+public:
+ PNS_MEANDERED_LINE()
+ {
+ // Do not leave unitialized members, and keep static analyser quiet:
+ m_placer = NULL;
+ m_dual = false;
+ m_width = 0;
+ m_baselineOffset = 0;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param aPlacer the meander placer instance
+ * @param aIsDual when true, the meanders are generated for two coupled lines
+ */
+ PNS_MEANDERED_LINE( PNS_MEANDER_PLACER_BASE* aPlacer, bool aIsDual = false ) :
+ m_placer( aPlacer ),
+ m_dual( aIsDual )
+ {
+ // Do not leave unitialized members, and keep static analyser quiet:
+ m_width = 0;
+ m_baselineOffset = 0;
+ }
+
+ ~PNS_MEANDERED_LINE()
+ {
+ Clear();
+ }
+
+ /**
+ * Function AddCorner()
+ *
+ * Creates a dummy meander shape representing a line corner. Used to define
+ * the starts/ends of meandered segments.
+ * @param aA corner point of the 1st line
+ * @param aB corner point of the 2nd line (if m_dual == true)
+ */
+ void AddCorner( const VECTOR2I& aA, const VECTOR2I& aB = VECTOR2I( 0, 0 ) );
+
+ /**
+ * Function AddMeander()
+ *
+ * Adds a new meander shape the the meandered line.
+ * @param aShape the meander shape to add
+ */
+ void AddMeander( PNS_MEANDER_SHAPE* aShape );
+
+ /**
+ * Function Clear()
+ *
+ * Clears the line geometry, removing all corners and meanders.
+ */
+ void Clear();
+
+ /**
+ * Function SetWidth()
+ *
+ * Sets the line width.
+ */
+ void SetWidth( int aWidth )
+ {
+ m_width = aWidth;
+ }
+
+ /**
+ * Function MeanderSegment()
+ *
+ * Fits maximum amplitude meanders on a given segment and adds to the
+ * current line.
+ * @param aSeg the base segment to meander
+ * @param aBaseIndex index of the base segment in the original line
+ */
+ void MeanderSegment( const SEG& aSeg, int aBaseIndex = 0 );
+
+ /// @copydoc PNS_MEANDER_SHAPE::SetBaselineOffset()
+ void SetBaselineOffset( int aOffset )
+ {
+ m_baselineOffset = aOffset;
+ }
+
+ /**
+ * Function Meanders()
+ *
+ * @return set of meander shapes for this line
+ */
+ std::vector<PNS_MEANDER_SHAPE*>& Meanders()
+ {
+ return m_meanders;
+ }
+
+ /**
+ * Function CheckSelfIntersections()
+ *
+ * Checks if the given shape is intersecting with any other meander in
+ * the current line.
+ * @param aShape the shape to check
+ * @param aClearance clearance value
+ * @return true, if the meander shape is not colliding
+ */
+ bool CheckSelfIntersections ( PNS_MEANDER_SHAPE* aShape, int aClearance );
+
+ /**
+ * Function Settings()
+ *
+ * @return the current meandering settings.
+ */
+ const PNS_MEANDER_SETTINGS& Settings() const;
+
+private:
+ VECTOR2I m_last;
+
+ PNS_MEANDER_PLACER_BASE* m_placer;
+ std::vector<PNS_MEANDER_SHAPE*> m_meanders;
+
+ bool m_dual;
+ int m_width;
+ int m_baselineOffset;
+};
+
+#endif // __PNS_MEANDER_H
diff --git a/pcbnew/router/pns_meander_placer.cpp b/pcbnew/router/pns_meander_placer.cpp
new file mode 100644
index 0000000..14b9f9b
--- /dev/null
+++ b/pcbnew/router/pns_meander_placer.cpp
@@ -0,0 +1,268 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/foreach.hpp>
+
+#include <base_units.h> // God forgive me doing this...
+#include <colors.h>
+
+#include "trace.h"
+
+#include "pns_node.h"
+#include "pns_itemset.h"
+#include "pns_topology.h"
+#include "pns_meander_placer.h"
+#include "pns_router.h"
+
+
+PNS_MEANDER_PLACER::PNS_MEANDER_PLACER( PNS_ROUTER* aRouter ) :
+ PNS_MEANDER_PLACER_BASE( aRouter )
+{
+ m_world = NULL;
+ m_currentNode = NULL;
+
+ // Init temporary variables (do not leave uninitialized members)
+ m_initialSegment = NULL;
+ m_lastLength = 0;
+ m_lastStatus = TOO_SHORT;
+}
+
+
+PNS_MEANDER_PLACER::~PNS_MEANDER_PLACER()
+{
+}
+
+
+PNS_NODE* PNS_MEANDER_PLACER::CurrentNode( bool aLoopsRemoved ) const
+{
+ if( !m_currentNode )
+ return m_world;
+
+ return m_currentNode;
+}
+
+
+bool PNS_MEANDER_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem )
+{
+ VECTOR2I p;
+
+ if( !aStartItem || !aStartItem->OfKind( PNS_ITEM::SEGMENT ) )
+ {
+ Router()->SetFailureReason( _( "Please select a track whose length you want to tune." ) );
+ return false;
+ }
+
+ m_initialSegment = static_cast<PNS_SEGMENT*>( aStartItem );
+
+ p = m_initialSegment->Seg().NearestPoint( aP );
+
+ m_currentNode = NULL;
+ m_currentStart = p;
+
+ m_world = Router()->GetWorld()->Branch();
+ m_originLine = m_world->AssembleLine( m_initialSegment );
+
+ PNS_TOPOLOGY topo( m_world );
+ m_tunedPath = topo.AssembleTrivialPath( m_initialSegment );
+
+ m_world->Remove( &m_originLine );
+
+ m_currentWidth = m_originLine.Width();
+ m_currentEnd = VECTOR2I( 0, 0 );
+
+ return true;
+}
+
+
+int PNS_MEANDER_PLACER::origPathLength() const
+{
+ int total = 0;
+ BOOST_FOREACH( const PNS_ITEM* item, m_tunedPath.CItems() )
+ {
+ if( const PNS_LINE* l = dyn_cast<const PNS_LINE*>( item ) )
+ {
+ total += l->CLine().Length();
+ }
+ }
+
+ return total;
+}
+
+
+bool PNS_MEANDER_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem )
+{
+ return doMove( aP, aEndItem, m_settings.m_targetLength );
+}
+
+
+bool PNS_MEANDER_PLACER::doMove( const VECTOR2I& aP, PNS_ITEM* aEndItem, int aTargetLength )
+{
+ SHAPE_LINE_CHAIN pre, tuned, post;
+
+ if( m_currentNode )
+ delete m_currentNode;
+
+ m_currentNode = m_world->Branch();
+
+ cutTunedLine( m_originLine.CLine(), m_currentStart, aP, pre, tuned, post );
+
+ m_result = PNS_MEANDERED_LINE( this, false );
+ m_result.SetWidth( m_originLine.Width() );
+ m_result.SetBaselineOffset( 0 );
+
+ for( int i = 0; i < tuned.SegmentCount(); i++ )
+ {
+ const SEG s = tuned.CSegment( i );
+ m_result.AddCorner( s.A );
+ m_result.MeanderSegment( s );
+ m_result.AddCorner( s.B );
+ }
+
+ int lineLen = origPathLength();
+
+ m_lastLength = lineLen;
+ m_lastStatus = TUNED;
+
+ if( compareWithTolerance( lineLen, aTargetLength, m_settings.m_lengthTolerance ) > 0 )
+ {
+ m_lastStatus = TOO_LONG;
+ } else {
+ m_lastLength = lineLen - tuned.Length();
+ tuneLineLength( m_result, aTargetLength - lineLen );
+ }
+
+ BOOST_FOREACH ( const PNS_ITEM* item, m_tunedPath.CItems() )
+ {
+ if( const PNS_LINE* l = dyn_cast<const PNS_LINE*>( item ) )
+ {
+ Router()->DisplayDebugLine( l->CLine(), 5, 30000 );
+ }
+ }
+
+ if( m_lastStatus != TOO_LONG )
+ {
+ tuned.Clear();
+
+ BOOST_FOREACH( PNS_MEANDER_SHAPE* m, m_result.Meanders() )
+ {
+ if( m->Type() != MT_EMPTY )
+ {
+ tuned.Append ( m->CLine( 0 ) );
+ }
+ }
+
+ m_lastLength += tuned.Length();
+
+ int comp = compareWithTolerance( m_lastLength - aTargetLength, 0, m_settings.m_lengthTolerance );
+
+ if( comp > 0 )
+ m_lastStatus = TOO_LONG;
+ else if( comp < 0 )
+ m_lastStatus = TOO_SHORT;
+ else
+ m_lastStatus = TUNED;
+ }
+
+ m_finalShape.Clear();
+ m_finalShape.Append( pre );
+ m_finalShape.Append( tuned );
+ m_finalShape.Append( post );
+ m_finalShape.Simplify();
+
+ return true;
+}
+
+
+bool PNS_MEANDER_PLACER::FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem )
+{
+ if( !m_currentNode )
+ return false;
+
+ m_currentTrace = PNS_LINE( m_originLine, m_finalShape );
+ m_currentNode->Add( &m_currentTrace );
+
+ Router()->CommitRouting( m_currentNode );
+ return true;
+}
+
+
+bool PNS_MEANDER_PLACER::CheckFit( PNS_MEANDER_SHAPE* aShape )
+{
+ PNS_LINE l( m_originLine, aShape->CLine( 0 ) );
+
+ if( m_currentNode->CheckColliding( &l ) )
+ return false;
+
+ int w = aShape->Width();
+ int clearance = w + m_settings.m_spacing;
+
+ return m_result.CheckSelfIntersections( aShape, clearance );
+}
+
+
+const PNS_ITEMSET PNS_MEANDER_PLACER::Traces()
+{
+ m_currentTrace = PNS_LINE( m_originLine, m_finalShape );
+ return PNS_ITEMSET( &m_currentTrace );
+}
+
+
+const VECTOR2I& PNS_MEANDER_PLACER::CurrentEnd() const
+{
+ return m_currentEnd;
+}
+
+int PNS_MEANDER_PLACER::CurrentLayer() const
+{
+ return m_initialSegment->Layers().Start();
+}
+
+
+const wxString PNS_MEANDER_PLACER::TuningInfo() const
+{
+ wxString status;
+
+ switch ( m_lastStatus )
+ {
+ case TOO_LONG:
+ status = _( "Too long: " );
+ break;
+ case TOO_SHORT:
+ status = _( "Too short: " );
+ break;
+ case TUNED:
+ status = _( "Tuned: " );
+ break;
+ default:
+ return _( "?" );
+ }
+
+ status += LengthDoubleToString( (double) m_lastLength, false );
+ status += "/";
+ status += LengthDoubleToString( (double) m_settings.m_targetLength, false );
+
+ return status;
+}
+
+
+PNS_MEANDER_PLACER::TUNING_STATUS PNS_MEANDER_PLACER::TuningStatus() const
+{
+ return m_lastStatus;
+}
diff --git a/pcbnew/router/pns_meander_placer.h b/pcbnew/router/pns_meander_placer.h
new file mode 100644
index 0000000..b38457e
--- /dev/null
+++ b/pcbnew/router/pns_meander_placer.h
@@ -0,0 +1,118 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2014 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PNS_MEANDER_PLACER_H
+#define __PNS_MEANDER_PLACER_H
+
+#include <math/vector2d.h>
+
+#include <geometry/shape.h>
+#include <geometry/shape_line_chain.h>
+
+#include "pns_node.h"
+#include "pns_via.h"
+#include "pns_line.h"
+#include "pns_placement_algo.h"
+#include "pns_meander.h"
+#include "pns_meander_placer_base.h"
+
+class PNS_ROUTER;
+class PNS_SHOVE;
+class PNS_OPTIMIZER;
+class PNS_ROUTER_BASE;
+
+/**
+ * Class PNS_MEANDER_PLACER
+ *
+ * Single track length matching/meandering tool.
+ */
+class PNS_MEANDER_PLACER : public PNS_MEANDER_PLACER_BASE
+{
+public:
+
+ PNS_MEANDER_PLACER( PNS_ROUTER* aRouter );
+ virtual ~PNS_MEANDER_PLACER();
+
+ /// @copydoc PNS_PLACEMENT_ALGO::Start()
+ virtual bool Start( const VECTOR2I& aP, PNS_ITEM* aStartItem );
+
+ /// @copydoc PNS_PLACEMENT_ALGO::Move()
+ virtual bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem );
+
+ /// @copydoc PNS_PLACEMENT_ALGO::FixRoute()
+ virtual bool FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem );
+
+ /// @copydoc PNS_PLACEMENT_ALGO::CurrentNode()
+ PNS_NODE* CurrentNode( bool aLoopsRemoved = false ) const;
+
+ /// @copydoc PNS_PLACEMENT_ALGO::Traces()
+ const PNS_ITEMSET Traces();
+
+ /// @copydoc PNS_PLACEMENT_ALGO::CurrentEnd()
+ const VECTOR2I& CurrentEnd() const;
+
+ /// @copydoc PNS_PLACEMENT_ALGO::CurrentNets()
+ const std::vector<int> CurrentNets() const
+ {
+ return std::vector<int> (1, m_originLine.Net() );
+ }
+
+ /// @copydoc PNS_PLACEMENT_ALGO::CurrentLayer()
+ int CurrentLayer() const;
+
+ /// @copydoc PNS_MEANDER_PLACER_BASE::TuningInfo()
+ virtual const wxString TuningInfo() const;
+
+ /// @copydoc PNS_MEANDER_PLACER_BASE::TuningStatus()
+ virtual TUNING_STATUS TuningStatus() const;
+
+ /// @copydoc PNS_MEANDER_PLACER_BASE::CheckFit()
+ bool CheckFit ( PNS_MEANDER_SHAPE* aShape );
+
+protected:
+
+ bool doMove( const VECTOR2I& aP, PNS_ITEM* aEndItem, int aTargetLength );
+
+ void setWorld( PNS_NODE* aWorld );
+
+ virtual int origPathLength() const;
+
+ ///> pointer to world to search colliding items
+ PNS_NODE* m_world;
+
+ ///> current routing start point (end of tail, beginning of head)
+ VECTOR2I m_currentStart;
+
+ ///> Current world state
+ PNS_NODE* m_currentNode;
+
+ PNS_LINE m_originLine;
+ PNS_LINE m_currentTrace;
+ PNS_ITEMSET m_tunedPath;
+
+ SHAPE_LINE_CHAIN m_finalShape;
+ PNS_MEANDERED_LINE m_result;
+ PNS_SEGMENT* m_initialSegment;
+
+ int m_lastLength;
+ TUNING_STATUS m_lastStatus;
+};
+
+#endif // __PNS_MEANDER_PLACER_H
diff --git a/pcbnew/router/pns_meander_placer_base.cpp b/pcbnew/router/pns_meander_placer_base.cpp
new file mode 100644
index 0000000..0a655e4
--- /dev/null
+++ b/pcbnew/router/pns_meander_placer_base.cpp
@@ -0,0 +1,187 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pns_router.h"
+#include "pns_meander.h"
+#include "pns_meander_placer_base.h"
+
+PNS_MEANDER_PLACER_BASE::PNS_MEANDER_PLACER_BASE( PNS_ROUTER* aRouter ) :
+ PNS_PLACEMENT_ALGO( aRouter )
+{
+ m_currentWidth = 0;
+}
+
+
+PNS_MEANDER_PLACER_BASE::~PNS_MEANDER_PLACER_BASE()
+{
+}
+
+
+void PNS_MEANDER_PLACER_BASE::AmplitudeStep( int aSign )
+{
+ int a = m_settings.m_maxAmplitude + aSign * m_settings.m_step;
+ a = std::max( a, m_settings.m_minAmplitude );
+
+ m_settings.m_maxAmplitude = a;
+}
+
+
+void PNS_MEANDER_PLACER_BASE::SpacingStep( int aSign )
+{
+ int s = m_settings.m_spacing + aSign * m_settings.m_step;
+ s = std::max( s, 2 * m_currentWidth );
+
+ m_settings.m_spacing = s;
+}
+
+
+void PNS_MEANDER_PLACER_BASE::UpdateSettings( const PNS_MEANDER_SETTINGS& aSettings )
+{
+ m_settings = aSettings;
+}
+
+
+void PNS_MEANDER_PLACER_BASE::cutTunedLine( const SHAPE_LINE_CHAIN& aOrigin,
+ const VECTOR2I& aTuneStart,
+ const VECTOR2I& aCursorPos,
+ SHAPE_LINE_CHAIN& aPre,
+ SHAPE_LINE_CHAIN& aTuned,
+ SHAPE_LINE_CHAIN& aPost )
+{
+ VECTOR2I cp ( aCursorPos );
+
+ if ( cp == aTuneStart ) // we don't like tuning segments with 0 length
+ {
+ int idx = aOrigin.FindSegment( cp );
+ if( idx >= 0 )
+ {
+ const SEG& s = aOrigin.CSegment( idx );
+ cp += (s.B - s.A).Resize(2);
+ } else
+ cp += VECTOR2I (2, 5); // some arbitrary value that is not 45 degrees oriented
+ }
+
+ VECTOR2I n = aOrigin.NearestPoint( cp );
+ VECTOR2I m = aOrigin.NearestPoint( aTuneStart );
+
+ SHAPE_LINE_CHAIN l( aOrigin );
+ l.Split( n );
+ l.Split( m );
+
+ int i_start = l.Find( m );
+ int i_end = l.Find( n );
+
+ if( i_start > i_end )
+ {
+ l = l.Reverse();
+ i_start = l.Find( m );
+ i_end = l.Find( n );
+ }
+
+ aPre = l.Slice( 0, i_start );
+ aPost = l.Slice( i_end, -1 );
+ aTuned = l.Slice( i_start, i_end );
+
+ aTuned.Simplify();
+}
+
+
+void PNS_MEANDER_PLACER_BASE::tuneLineLength( PNS_MEANDERED_LINE& aTuned, int aElongation )
+{
+ int remaining = aElongation;
+ bool finished = false;
+
+ BOOST_FOREACH( PNS_MEANDER_SHAPE* m, aTuned.Meanders() )
+ {
+ if( m->Type() != MT_CORNER )
+ {
+ if( remaining >= 0 )
+ remaining -= m->MaxTunableLength() - m->BaselineLength();
+
+ if( remaining < 0 )
+ {
+ if( !finished )
+ {
+ PNS_MEANDER_TYPE newType;
+
+ if( m->Type() == MT_START || m->Type() == MT_SINGLE )
+ newType = MT_SINGLE;
+ else
+ newType = MT_FINISH;
+
+ m->SetType( newType );
+ m->Recalculate();
+
+ finished = true;
+ } else {
+ m->MakeEmpty();
+ }
+ }
+ }
+ }
+
+ remaining = aElongation;
+ int meanderCount = 0;
+
+ BOOST_FOREACH(PNS_MEANDER_SHAPE* m, aTuned.Meanders())
+ {
+ if( m->Type() != MT_CORNER && m->Type() != MT_EMPTY )
+ {
+ if(remaining >= 0)
+ {
+ remaining -= m->MaxTunableLength() - m->BaselineLength();
+ meanderCount ++;
+ }
+ }
+ }
+
+ int balance = 0;
+
+ if( meanderCount )
+ balance = -remaining / meanderCount;
+
+ if( balance >= 0 )
+ {
+ BOOST_FOREACH( PNS_MEANDER_SHAPE* m, aTuned.Meanders() )
+ {
+ if( m->Type() != MT_CORNER && m->Type() != MT_EMPTY )
+ {
+ m->Resize( std::max( m->Amplitude() - balance / 2, m_settings.m_minAmplitude ) );
+ }
+ }
+ }
+}
+
+
+const PNS_MEANDER_SETTINGS& PNS_MEANDER_PLACER_BASE::MeanderSettings() const
+{
+ return m_settings;
+}
+
+
+int PNS_MEANDER_PLACER_BASE::compareWithTolerance( int aValue, int aExpected, int aTolerance ) const
+{
+ if( aValue < aExpected - aTolerance )
+ return -1;
+ else if( aValue > aExpected + aTolerance )
+ return 1;
+ else
+ return 0;
+}
diff --git a/pcbnew/router/pns_meander_placer_base.h b/pcbnew/router/pns_meander_placer_base.h
new file mode 100644
index 0000000..070e322
--- /dev/null
+++ b/pcbnew/router/pns_meander_placer_base.h
@@ -0,0 +1,165 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PNS_MEANDER_PLACER_BASE_H
+#define __PNS_MEANDER_PLACER_BASE_H
+
+#include <math/vector2d.h>
+
+#include <geometry/shape.h>
+#include <geometry/shape_line_chain.h>
+
+#include "pns_node.h"
+#include "pns_via.h"
+#include "pns_line.h"
+#include "pns_placement_algo.h"
+#include "pns_meander.h"
+
+class PNS_ROUTER;
+class PNS_SHOVE;
+class PNS_OPTIMIZER;
+class PNS_ROUTER_BASE;
+
+/**
+ * Class PNS_MEANDER_PLACER_BASE
+ *
+ * Base class for Single trace & Differenial pair meandering tools, as
+ * both of them share a lot of code.
+ */
+class PNS_MEANDER_PLACER_BASE : public PNS_PLACEMENT_ALGO
+{
+public:
+ ///> Result of the length tuning operation
+ enum TUNING_STATUS {
+ TOO_SHORT = 0,
+ TOO_LONG,
+ TUNED
+ };
+
+ PNS_MEANDER_PLACER_BASE( PNS_ROUTER* aRouter );
+ virtual ~PNS_MEANDER_PLACER_BASE();
+
+ /**
+ * Function TuningInfo()
+ *
+ * Returns a string describing the status and length of the
+ * tuned traces.
+ */
+ virtual const wxString TuningInfo() const = 0;
+
+ /**
+ * Function TuningStatus()
+ *
+ * Returns the tuning status (too short, too long, etc.)
+ * of the trace(s) being tuned.
+ */
+ virtual TUNING_STATUS TuningStatus() const = 0;
+
+ /**
+ * Function AmplitudeStep()
+ *
+ * Increases/decreases the current meandering amplitude by one step.
+ * @param aSign direction (negative = decrease, positive = increase).
+ */
+ virtual void AmplitudeStep( int aSign );
+
+ /**
+ * Function SpacingStep()
+ *
+ * Increases/decreases the current meandering spcing by one step.
+ * @param aSign direction (negative = decrease, positive = increase).
+ */
+ virtual void SpacingStep( int aSign );
+
+ /**
+ * Function MeanderSettings()
+ *
+ * Returns the current meandering configuration.
+ * @return the settings
+ */
+ virtual const PNS_MEANDER_SETTINGS& MeanderSettings() const;
+
+ /*
+ * Function UpdateSettings()
+ *
+ * Sets the current meandering configuration.
+ * @param aSettings the settings
+ */
+ virtual void UpdateSettings( const PNS_MEANDER_SETTINGS& aSettings);
+
+ /**
+ * Function CheckFit()
+ *
+ * Checks if it's ok to place the shape aShape (i.e.
+ * if it doesn't cause DRC violations or collide with
+ * other meanders).
+ * @param aShape the shape to check
+ * @return true if the shape fits
+ */
+ virtual bool CheckFit( PNS_MEANDER_SHAPE* aShape )
+ {
+ return false;
+ }
+
+protected:
+
+ /**
+ * Function cutTunedLine()
+ *
+ * Extracts the part of a track to be meandered, depending on the
+ * starting point and the cursor position.
+ * @param aOrigin the original line
+ * @param aTuneStart point where we start meandering (start click coorinates)
+ * @param aCursorPos current cursor position
+ * @param aPre part before the beginning of meanders
+ * @param aTuned part to be meandered
+ * @param aPost part after the end of meanders
+ */
+ void cutTunedLine( const SHAPE_LINE_CHAIN& aOrigin,
+ const VECTOR2I& aTuneStart,
+ const VECTOR2I& aCursorPos,
+ SHAPE_LINE_CHAIN& aPre,
+ SHAPE_LINE_CHAIN& aTuned,
+ SHAPE_LINE_CHAIN& aPost );
+
+ /**
+ * Function tuneLineLength()
+ *
+ * Takes a set of meanders in aTuned and tunes their length to
+ * extend the original line length by aElongation.
+ */
+ void tuneLineLength( PNS_MEANDERED_LINE& aTuned, int aElongation );
+
+ /**
+ * Function compareWithTolerance()
+ *
+ * Compares aValue against aExpected with given tolerance.
+ */
+ int compareWithTolerance ( int aValue, int aExpected, int aTolerance = 0 ) const;
+
+ ///> width of the meandered trace(s)
+ int m_currentWidth;
+ ///> meandering settings
+ PNS_MEANDER_SETTINGS m_settings;
+ ///> current end point
+ VECTOR2I m_currentEnd;
+};
+
+#endif // __PNS_MEANDER_PLACER_BASE_H
diff --git a/pcbnew/router/pns_meander_skew_placer.cpp b/pcbnew/router/pns_meander_skew_placer.cpp
new file mode 100644
index 0000000..3d4626d
--- /dev/null
+++ b/pcbnew/router/pns_meander_skew_placer.cpp
@@ -0,0 +1,174 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/foreach.hpp>
+
+#include <base_units.h> // God forgive me doing this...
+#include <colors.h>
+
+#include "trace.h"
+
+#include "pns_node.h"
+#include "pns_itemset.h"
+#include "pns_topology.h"
+#include "pns_meander_skew_placer.h"
+
+#include "pns_router.h"
+
+
+PNS_MEANDER_SKEW_PLACER::PNS_MEANDER_SKEW_PLACER ( PNS_ROUTER* aRouter ) :
+ PNS_MEANDER_PLACER ( aRouter )
+{
+ // Init temporary variables (do not leave uninitialized members)
+ m_coupledLength = 0;
+}
+
+
+PNS_MEANDER_SKEW_PLACER::~PNS_MEANDER_SKEW_PLACER( )
+{
+}
+
+
+bool PNS_MEANDER_SKEW_PLACER::Start( const VECTOR2I& aP, PNS_ITEM* aStartItem )
+{
+ VECTOR2I p;
+
+ if( !aStartItem || !aStartItem->OfKind( PNS_ITEM::SEGMENT ) )
+ {
+ Router()->SetFailureReason( _( "Please select a differential pair trace you want to tune." ) );
+ return false;
+ }
+
+ m_initialSegment = static_cast<PNS_SEGMENT*>( aStartItem );
+
+ p = m_initialSegment->Seg().NearestPoint( aP );
+
+ m_currentNode = NULL;
+ m_currentStart = p;
+
+ m_world = Router()->GetWorld( )->Branch();
+ m_originLine = m_world->AssembleLine( m_initialSegment );
+
+ PNS_TOPOLOGY topo( m_world );
+ m_tunedPath = topo.AssembleTrivialPath( m_initialSegment );
+
+ if( !topo.AssembleDiffPair ( m_initialSegment, m_originPair ) )
+ {
+ Router()->SetFailureReason( _( "Unable to find complementary differential pair "
+ "net for skew tuning. Make sure the names of the nets belonging "
+ "to a differential pair end with either _N/_P or +/-." ) );
+ return false;
+ }
+
+ if( m_originPair.Gap() < 0 )
+ m_originPair.SetGap( Router()->Sizes().DiffPairGap() );
+
+ if( !m_originPair.PLine().SegmentCount() ||
+ !m_originPair.NLine().SegmentCount() )
+ return false;
+
+ m_tunedPathP = topo.AssembleTrivialPath( m_originPair.PLine().GetLink( 0 ) );
+ m_tunedPathN = topo.AssembleTrivialPath( m_originPair.NLine().GetLink( 0 ) );
+
+ m_world->Remove( &m_originLine );
+
+ m_currentWidth = m_originLine.Width();
+ m_currentEnd = VECTOR2I( 0, 0 );
+
+ if ( m_originPair.PLine().Net() == m_originLine.Net() )
+ m_coupledLength = itemsetLength( m_tunedPathN );
+ else
+ m_coupledLength = itemsetLength( m_tunedPathP );
+
+ return true;
+}
+
+
+int PNS_MEANDER_SKEW_PLACER::origPathLength( ) const
+{
+ return itemsetLength ( m_tunedPath );
+}
+
+
+int PNS_MEANDER_SKEW_PLACER::itemsetLength( const PNS_ITEMSET& aSet ) const
+{
+ int total = 0;
+ BOOST_FOREACH( const PNS_ITEM* item, aSet.CItems() )
+ {
+ if( const PNS_LINE* l = dyn_cast<const PNS_LINE*>( item ) )
+ {
+ total += l->CLine().Length();
+ }
+ }
+
+ return total;
+}
+
+
+int PNS_MEANDER_SKEW_PLACER::currentSkew() const
+{
+ return m_lastLength - m_coupledLength;
+}
+
+
+bool PNS_MEANDER_SKEW_PLACER::Move( const VECTOR2I& aP, PNS_ITEM* aEndItem )
+{
+ BOOST_FOREACH( const PNS_ITEM* item, m_tunedPathP.CItems() )
+ {
+ if( const PNS_LINE* l = dyn_cast<const PNS_LINE*>( item ) )
+ Router()->DisplayDebugLine( l->CLine(), 5, 10000 );
+ }
+
+ BOOST_FOREACH( const PNS_ITEM* item, m_tunedPathN.CItems() )
+ {
+ if( const PNS_LINE* l = dyn_cast<const PNS_LINE*>( item ) )
+ Router()->DisplayDebugLine( l->CLine(), 4, 10000 );
+ }
+
+ return doMove( aP, aEndItem, m_coupledLength + m_settings.m_targetSkew );
+}
+
+
+const wxString PNS_MEANDER_SKEW_PLACER::TuningInfo() const
+{
+ wxString status;
+
+ switch( m_lastStatus )
+ {
+ case TOO_LONG:
+ status = _( "Too long: skew " );
+ break;
+ case TOO_SHORT:
+ status = _( "Too short: skew " );
+ break;
+ case TUNED:
+ status = _( "Tuned: skew " );
+ break;
+ default:
+ return _( "?" );
+ }
+
+ status += LengthDoubleToString( (double) m_lastLength - m_coupledLength, false );
+ status += "/";
+ status += LengthDoubleToString( (double) m_settings.m_targetSkew, false );
+
+ return status;
+}
+
diff --git a/pcbnew/router/pns_meander_skew_placer.h b/pcbnew/router/pns_meander_skew_placer.h
new file mode 100644
index 0000000..7cb303c
--- /dev/null
+++ b/pcbnew/router/pns_meander_skew_placer.h
@@ -0,0 +1,65 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PNS_MEANDER_SKEW_PLACER_H
+#define __PNS_MEANDER_SKEW_PLACER_H
+
+#include "pns_meander_placer.h"
+#include "pns_diff_pair.h"
+
+class PNS_ROUTER;
+class PNS_SHOVE;
+class PNS_OPTIMIZER;
+class PNS_ROUTER_BASE;
+
+/**
+ * Class PNS_MEANDER_SKEW_PLACER
+ *
+ * Differential pair skew adjustment algorithm.
+ */
+class PNS_MEANDER_SKEW_PLACER : public PNS_MEANDER_PLACER
+{
+public:
+ PNS_MEANDER_SKEW_PLACER( PNS_ROUTER* aRouter );
+ ~PNS_MEANDER_SKEW_PLACER();
+
+ /// @copydoc PNS_PLACEMENT_ALGO::Start()
+ bool Start( const VECTOR2I& aP, PNS_ITEM* aStartItem );
+
+ /// @copydoc PNS_PLACEMENT_ALGO::Move()
+ bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem );
+
+ /// @copydoc PNS_MEANDER_PLACER_BASE::TuningInfo()
+ const wxString TuningInfo() const;
+
+private:
+
+ int currentSkew( ) const;
+ int itemsetLength( const PNS_ITEMSET& aSet ) const;
+
+ int origPathLength () const;
+
+ PNS_DIFF_PAIR m_originPair;
+ PNS_ITEMSET m_tunedPath, m_tunedPathP, m_tunedPathN;
+
+ int m_coupledLength;
+};
+
+#endif // __PNS_MEANDER_SKEW_PLACER_H
diff --git a/pcbnew/router/pns_node.cpp b/pcbnew/router/pns_node.cpp
new file mode 100644
index 0000000..3f9bf12
--- /dev/null
+++ b/pcbnew/router/pns_node.cpp
@@ -0,0 +1,1288 @@
+/*
+ * 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 <vector>
+#include <cassert>
+
+#include <math/vector2d.h>
+
+#include <geometry/seg.h>
+#include <geometry/shape.h>
+#include <geometry/shape_line_chain.h>
+#include <geometry/shape_index.h>
+
+#include "trace.h"
+#include "pns_item.h"
+#include "pns_line.h"
+#include "pns_node.h"
+#include "pns_via.h"
+#include "pns_solid.h"
+#include "pns_joint.h"
+#include "pns_index.h"
+#include "pns_router.h"
+
+using boost::unordered_set;
+using boost::unordered_map;
+
+#ifdef DEBUG
+static boost::unordered_set<PNS_NODE*> allocNodes;
+#endif
+
+PNS_NODE::PNS_NODE()
+{
+ TRACE( 0, "PNS_NODE::create %p", this );
+ m_depth = 0;
+ m_root = this;
+ m_parent = NULL;
+ m_maxClearance = 800000; // fixme: depends on how thick traces are.
+ m_clearanceFunctor = NULL;
+ m_index = new PNS_INDEX;
+ m_collisionFilter = NULL;
+
+#ifdef DEBUG
+ allocNodes.insert( this );
+#endif
+}
+
+
+PNS_NODE::~PNS_NODE()
+{
+ TRACE( 0, "PNS_NODE::delete %p", this );
+
+ if( !m_children.empty() )
+ {
+ TRACEn( 0, "attempting to free a node that has kids.\n" );
+ assert( false );
+ }
+
+#ifdef DEBUG
+ if( allocNodes.find( this ) == allocNodes.end() )
+ {
+ TRACEn( 0, "attempting to free an already-free'd node.\n" );
+ assert( false );
+ }
+
+ allocNodes.erase( this );
+#endif
+
+ m_joints.clear();
+
+ for( PNS_INDEX::ITEM_SET::iterator i = m_index->begin(); i != m_index->end(); ++i )
+ {
+ if( (*i)->BelongsTo( this ) )
+ delete *i;
+ }
+
+ releaseGarbage();
+ unlinkParent();
+
+ delete m_index;
+}
+
+int PNS_NODE::GetClearance( const PNS_ITEM* aA, const PNS_ITEM* aB ) const
+{
+ if( !m_clearanceFunctor )
+ return 100000;
+
+ return (*m_clearanceFunctor)( aA, aB );
+}
+
+
+PNS_NODE* PNS_NODE::Branch()
+{
+ PNS_NODE* child = new PNS_NODE;
+
+ TRACE( 0, "PNS_NODE::branch %p (parent %p)", child % this );
+
+ m_children.insert( child );
+
+ child->m_depth = m_depth + 1;
+ child->m_parent = this;
+ child->m_clearanceFunctor = m_clearanceFunctor;
+ child->m_root = isRoot() ? this : m_root;
+ child->m_collisionFilter = m_collisionFilter;
+
+ // immmediate offspring of the root branch needs not copy anything.
+ // For the rest, deep-copy joints, overridden item map and pointers
+ // to stored items.
+ if( !isRoot() )
+ {
+ JOINT_MAP::iterator j;
+
+ for( PNS_INDEX::ITEM_SET::iterator i = m_index->begin(); i != m_index->end(); ++i )
+ child->m_index->Add( *i );
+
+ child->m_joints = m_joints;
+ child->m_override = m_override;
+ }
+
+ TRACE( 2, "%d items, %d joints, %d overrides",
+ child->m_index->Size() % child->m_joints.size() % child->m_override.size() );
+
+ return child;
+}
+
+
+void PNS_NODE::unlinkParent()
+{
+ if( isRoot() )
+ return;
+
+ m_parent->m_children.erase( this );
+}
+
+
+// function object that visits potential obstacles and performs
+// the actual collision refining
+struct PNS_NODE::OBSTACLE_VISITOR
+{
+ ///> node we are searching in (either root or a branch)
+ PNS_NODE* m_node;
+
+ ///> node that overrides root entries
+ PNS_NODE* m_override;
+
+ ///> list of encountered obstacles
+ OBSTACLES& m_tab;
+
+ ///> the item we are looking for collisions with
+ const PNS_ITEM* m_item;
+
+ ///> acccepted kinds of colliding items (solids, vias, segments, etc...)
+ int m_kindMask;
+
+ ///> max number of hits
+ int m_limitCount;
+
+ ///> number of items found so far
+ int m_matchCount;
+
+ ///> additional clearance
+ int m_extraClearance;
+
+ bool m_differentNetsOnly;
+
+ int m_forceClearance;
+
+ OBSTACLE_VISITOR( PNS_NODE::OBSTACLES& aTab, const PNS_ITEM* aItem, int aKindMask, bool aDifferentNetsOnly ) :
+ m_node( NULL ),
+ m_override( NULL ),
+ m_tab( aTab ),
+ m_item( aItem ),
+ m_kindMask( aKindMask ),
+ m_limitCount( -1 ),
+ m_matchCount( 0 ),
+ m_extraClearance( 0 ),
+ m_differentNetsOnly( aDifferentNetsOnly ),
+ m_forceClearance( -1 )
+ {
+ if( aItem->Kind() == PNS_ITEM::LINE )
+ m_extraClearance += static_cast<const PNS_LINE*>( aItem )->Width() / 2;
+ }
+
+ void SetCountLimit( int aLimit )
+ {
+ m_limitCount = aLimit;
+ }
+
+ void SetWorld( PNS_NODE* aNode, PNS_NODE* aOverride = NULL )
+ {
+ m_node = aNode;
+ m_override = aOverride;
+ }
+
+ bool operator()( PNS_ITEM* aItem )
+ {
+ if( !aItem->OfKind( m_kindMask ) )
+ return true;
+
+ // check if there is a more recent branch with a newer
+ // (possibily modified) version of this item.
+ if( m_override && m_override->overrides( aItem ) )
+ return true;
+
+ int clearance = m_extraClearance + m_node->GetClearance( aItem, m_item );
+
+ if( m_node->m_collisionFilter && (*m_node->m_collisionFilter)( aItem, m_item ) )
+ return true;
+
+ if( aItem->Kind() == PNS_ITEM::LINE )
+ clearance += static_cast<PNS_LINE*>( aItem )->Width() / 2;
+
+ if( m_forceClearance >= 0 )
+ clearance = m_forceClearance;
+
+ if( !aItem->Collide( m_item, clearance, m_differentNetsOnly ) )
+ return true;
+
+ PNS_OBSTACLE obs;
+
+ obs.m_item = aItem;
+ obs.m_head = m_item;
+ m_tab.push_back( obs );
+
+ m_matchCount++;
+
+ if( m_limitCount > 0 && m_matchCount >= m_limitCount )
+ return false;
+
+ return true;
+ };
+};
+
+
+int PNS_NODE::QueryColliding( const PNS_ITEM* aItem,
+ PNS_NODE::OBSTACLES& aObstacles, int aKindMask, int aLimitCount, bool aDifferentNetsOnly, int aForceClearance )
+{
+ OBSTACLE_VISITOR visitor( aObstacles, aItem, aKindMask, aDifferentNetsOnly );
+
+#ifdef DEBUG
+ assert( allocNodes.find( this ) != allocNodes.end() );
+#endif
+
+ visitor.SetCountLimit( aLimitCount );
+ visitor.SetWorld( this, NULL );
+ visitor.m_forceClearance = aForceClearance;
+ // first, look for colliding items in the local index
+ m_index->Query( aItem, m_maxClearance, visitor );
+
+ // if we haven't found enough items, look in the root branch as well.
+ if( !isRoot() && ( visitor.m_matchCount < aLimitCount || aLimitCount < 0 ) )
+ {
+ visitor.SetWorld( m_root, this );
+ m_root->m_index->Query( aItem, m_maxClearance, visitor );
+ }
+
+ return aObstacles.size();
+}
+
+
+PNS_NODE::OPT_OBSTACLE PNS_NODE::NearestObstacle( const PNS_LINE* aItem,
+ int aKindMask,
+ const std::set<PNS_ITEM*>* aRestrictedSet )
+{
+ OBSTACLES obs_list;
+ bool found_isects = false;
+
+ const SHAPE_LINE_CHAIN& line = aItem->CLine();
+
+ obs_list.reserve( 100 );
+
+ int n = 0;
+
+ for( int i = 0; i < line.SegmentCount(); i++ )
+ {
+ const PNS_SEGMENT s( *aItem, line.CSegment( i ) );
+ n += QueryColliding( &s, obs_list, aKindMask );
+ }
+
+ if( aItem->EndsWithVia() )
+ n += QueryColliding( &aItem->Via(), obs_list, aKindMask );
+
+ if( !n )
+ return OPT_OBSTACLE();
+
+ PNS_LINE& aLine = (PNS_LINE&) *aItem;
+
+ PNS_OBSTACLE nearest;
+ nearest.m_item = NULL;
+ nearest.m_distFirst = INT_MAX;
+
+ BOOST_FOREACH( PNS_OBSTACLE obs, obs_list )
+ {
+ VECTOR2I ip_first, ip_last;
+ int dist_max = INT_MIN;
+
+ if( aRestrictedSet && aRestrictedSet->find( obs.m_item ) == aRestrictedSet->end() )
+ continue;
+
+ std::vector<SHAPE_LINE_CHAIN::INTERSECTION> isect_list;
+
+ int clearance = GetClearance( obs.m_item, &aLine );
+
+ SHAPE_LINE_CHAIN hull = obs.m_item->Hull( clearance, aItem->Width() );
+
+ if( aLine.EndsWithVia() )
+ {
+ int clearance = GetClearance( obs.m_item, &aLine.Via() );
+
+ SHAPE_LINE_CHAIN viaHull = aLine.Via().Hull( clearance, aItem->Width() );
+
+ viaHull.Intersect( hull, isect_list );
+
+ BOOST_FOREACH( SHAPE_LINE_CHAIN::INTERSECTION isect, isect_list )
+ {
+ int dist = aLine.CLine().Length() +
+ ( isect.p - aLine.Via().Pos() ).EuclideanNorm();
+
+ if( dist < nearest.m_distFirst )
+ {
+ found_isects = true;
+ nearest.m_distFirst = dist;
+ nearest.m_ipFirst = isect.p;
+ nearest.m_item = obs.m_item;
+ nearest.m_hull = hull;
+ }
+
+ if( dist > dist_max )
+ {
+ dist_max = dist;
+ ip_last = isect.p;
+ }
+ }
+ }
+
+ isect_list.clear();
+
+ hull.Intersect( aLine.CLine(), isect_list );
+
+ BOOST_FOREACH( SHAPE_LINE_CHAIN::INTERSECTION isect, isect_list )
+ {
+ int dist = aLine.CLine().PathLength( isect.p );
+
+ if( dist < nearest.m_distFirst )
+ {
+ found_isects = true;
+ nearest.m_distFirst = dist;
+ nearest.m_ipFirst = isect.p;
+ nearest.m_item = obs.m_item;
+ nearest.m_hull = hull;
+ }
+
+ if( dist > dist_max )
+ {
+ dist_max = dist;
+ ip_last = isect.p;
+ }
+ }
+
+ nearest.m_ipLast = ip_last;
+ nearest.m_distLast = dist_max;
+ }
+
+ if( !found_isects )
+ nearest.m_item = obs_list[0].m_item;
+
+ return nearest;
+}
+
+
+PNS_NODE::OPT_OBSTACLE PNS_NODE::CheckColliding( const PNS_ITEMSET& aSet, int aKindMask )
+{
+ BOOST_FOREACH( const PNS_ITEM* item, aSet.CItems() )
+ {
+ OPT_OBSTACLE obs = CheckColliding( item, aKindMask );
+
+ if( obs )
+ return obs;
+ }
+
+ return OPT_OBSTACLE();
+}
+
+
+PNS_NODE::OPT_OBSTACLE PNS_NODE::CheckColliding( const PNS_ITEM* aItemA, int aKindMask )
+{
+ OBSTACLES obs;
+
+ obs.reserve( 100 );
+
+ if( aItemA->Kind() == PNS_ITEM::LINE )
+ {
+ int n = 0;
+ const PNS_LINE* line = static_cast<const PNS_LINE*>( aItemA );
+ const SHAPE_LINE_CHAIN& l = line->CLine();
+
+ for( int i = 0; i < l.SegmentCount(); i++ )
+ {
+ const PNS_SEGMENT s( *line, l.CSegment( i ) );
+ n += QueryColliding( &s, obs, aKindMask, 1 );
+
+ if( n )
+ return OPT_OBSTACLE( obs[0] );
+ }
+
+ if( line->EndsWithVia() )
+ {
+ n += QueryColliding( &line->Via(), obs, aKindMask, 1 );
+
+ if( n )
+ return OPT_OBSTACLE( obs[0] );
+ }
+ }
+ else if( QueryColliding( aItemA, obs, aKindMask, 1 ) > 0 )
+ return OPT_OBSTACLE( obs[0] );
+
+ return OPT_OBSTACLE();
+}
+
+
+bool PNS_NODE::CheckColliding( const PNS_ITEM* aItemA, const PNS_ITEM* aItemB, int aKindMask, int aForceClearance )
+{
+ assert( aItemB );
+ int clearance;
+ if( aForceClearance >= 0 )
+ clearance = aForceClearance;
+ else
+ 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, clearance );
+}
+
+
+struct HIT_VISITOR
+{
+ PNS_ITEMSET& m_items;
+ const VECTOR2I& m_point;
+ const PNS_NODE* m_world;
+
+ HIT_VISITOR( PNS_ITEMSET& aTab, const VECTOR2I& aPoint, const PNS_NODE* aWorld ) :
+ m_items( aTab ), m_point( aPoint ), m_world( aWorld )
+ {}
+
+ bool operator()( PNS_ITEM* aItem )
+ {
+ SHAPE_CIRCLE cp( m_point, 0 );
+
+ int cl = 0;
+
+ if( aItem->Shape()->Collide( &cp, cl ) )
+ m_items.Add( aItem );
+
+ return true;
+ }
+};
+
+
+const PNS_ITEMSET PNS_NODE::HitTest( const VECTOR2I& aPoint ) const
+{
+ PNS_ITEMSET items;
+
+ // fixme: we treat a point as an infinitely small circle - this is inefficient.
+ SHAPE_CIRCLE s( aPoint, 0 );
+ HIT_VISITOR visitor( items, aPoint, this );
+
+ m_index->Query( &s, m_maxClearance, visitor );
+
+ if( !isRoot() ) // fixme: could be made cleaner
+ {
+ PNS_ITEMSET items_root;
+ HIT_VISITOR visitor_root( items_root, aPoint, m_root );
+ m_root->m_index->Query( &s, m_maxClearance, visitor_root );
+
+ BOOST_FOREACH( PNS_ITEM* item, items_root.Items() )
+ {
+ if( !overrides( item ) )
+ items.Add( item );
+ }
+ }
+
+ return items;
+}
+
+
+void PNS_NODE::addSolid( PNS_SOLID* aSolid )
+{
+ linkJoint( aSolid->Pos(), aSolid->Layers(), aSolid->Net(), aSolid );
+ m_index->Add( aSolid );
+}
+
+
+void PNS_NODE::addVia( PNS_VIA* aVia )
+{
+ linkJoint( aVia->Pos(), aVia->Layers(), aVia->Net(), aVia );
+ m_index->Add( aVia );
+}
+
+
+void PNS_NODE::addLine( PNS_LINE* aLine, bool aAllowRedundant )
+{
+ SHAPE_LINE_CHAIN& l = aLine->Line();
+
+ for( int i = 0; i < l.SegmentCount(); i++ )
+ {
+ SEG s = l.CSegment( i );
+
+ if( s.A != s.B )
+ {
+ PNS_SEGMENT* pseg = new PNS_SEGMENT( *aLine, s );
+ PNS_SEGMENT* psegR = NULL;
+
+ if( !aAllowRedundant )
+ psegR = findRedundantSegment( pseg );
+
+ if( psegR )
+ {
+ aLine->LinkSegment( psegR );
+
+ delete pseg;
+ }
+ else
+ {
+ pseg->SetOwner( this );
+
+ linkJoint( s.A, pseg->Layers(), aLine->Net(), pseg );
+ linkJoint( s.B, pseg->Layers(), aLine->Net(), pseg );
+
+ aLine->LinkSegment( pseg );
+
+ m_index->Add( pseg );
+ }
+ }
+ }
+}
+
+
+void PNS_NODE::addSegment( PNS_SEGMENT* aSeg, bool aAllowRedundant )
+{
+ if( aSeg->Seg().A == aSeg->Seg().B )
+ {
+ TRACEn( 0, "attempting to add a segment with same end coordinates, ignoring." )
+ return;
+ }
+
+ if( !aAllowRedundant && findRedundantSegment ( aSeg ) )
+ return;
+
+ aSeg->SetOwner( this );
+
+ linkJoint( aSeg->Seg().A, aSeg->Layers(), aSeg->Net(), aSeg );
+ linkJoint( aSeg->Seg().B, aSeg->Layers(), aSeg->Net(), aSeg );
+
+ m_index->Add( aSeg );
+}
+
+
+void PNS_NODE::Add( PNS_ITEM* aItem, bool aAllowRedundant )
+{
+ aItem->SetOwner( this );
+
+ switch( aItem->Kind() )
+ {
+ case PNS_ITEM::SOLID:
+ addSolid( static_cast<PNS_SOLID*>( aItem ) );
+ break;
+
+ case PNS_ITEM::SEGMENT:
+ addSegment( static_cast<PNS_SEGMENT*>( aItem ), aAllowRedundant );
+ break;
+
+ case PNS_ITEM::LINE:
+ addLine( static_cast<PNS_LINE*>( aItem ), aAllowRedundant );
+ break;
+
+ case PNS_ITEM::VIA:
+ addVia( static_cast<PNS_VIA*>( aItem ) );
+ break;
+
+ default:
+ assert( false );
+ }
+}
+
+
+void PNS_NODE::doRemove( PNS_ITEM* aItem )
+{
+ // case 1: removing an item that is stored in the root node from any branch:
+ // mark it as overridden, but do not remove
+ if( aItem->BelongsTo( m_root ) && !isRoot() )
+ m_override.insert( aItem );
+
+ // case 2: the item belongs to this branch or a parent, non-root branch,
+ // or the root itself and we are the root: remove from the index
+ else if( !aItem->BelongsTo( m_root ) || isRoot() )
+ m_index->Remove( aItem );
+
+ // the item belongs to this particular branch: un-reference it
+ if( aItem->BelongsTo( this ) )
+ {
+ aItem->SetOwner( NULL );
+ m_root->m_garbageItems.insert( aItem );
+ }
+}
+
+
+void PNS_NODE::removeSegment( PNS_SEGMENT* aSeg )
+{
+ unlinkJoint( aSeg->Seg().A, aSeg->Layers(), aSeg->Net(), aSeg );
+ unlinkJoint( aSeg->Seg().B, aSeg->Layers(), aSeg->Net(), aSeg );
+
+ doRemove( aSeg );
+}
+
+
+void PNS_NODE::removeLine( PNS_LINE* aLine )
+{
+ std::vector<PNS_SEGMENT*>* segRefs = aLine->LinkedSegments();
+
+ assert( segRefs != NULL );
+
+ BOOST_FOREACH( PNS_SEGMENT* seg, *segRefs )
+ {
+ removeSegment( seg );
+ }
+}
+
+void PNS_NODE::removeVia( PNS_VIA* 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.
+
+ PNS_JOINT::HASH_TAG tag;
+
+ VECTOR2I p( aVia->Pos() );
+ PNS_LAYERSET vLayers( aVia->Layers() );
+ int net = aVia->Net();
+
+ PNS_JOINT* jt = FindJoint( p, vLayers.Start(), net );
+ PNS_JOINT::LINKED_ITEMS links( jt->LinkList() );
+
+ tag.net = net;
+ tag.pos = p;
+
+ bool split;
+ do
+ {
+ split = false;
+ std::pair<JOINT_MAP::iterator, JOINT_MAP::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( JOINT_MAP::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 )
+{
+ Remove( aOldItem );
+ Add( aNewItem );
+}
+
+
+void PNS_NODE::Remove( PNS_ITEM* aItem )
+{
+ switch( aItem->Kind() )
+ {
+ case PNS_ITEM::SOLID:
+ // 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;
+
+ case PNS_ITEM::SEGMENT:
+ removeSegment( static_cast<PNS_SEGMENT*>( aItem ) );
+ break;
+
+ case PNS_ITEM::LINE:
+ removeLine( static_cast<PNS_LINE*>( aItem ) );
+ break;
+
+ case PNS_ITEM::VIA:
+ removeVia( static_cast<PNS_VIA*>( aItem ) );
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+void PNS_NODE::Remove( PNS_LINE& aLine )
+{
+ removeLine( &aLine );
+}
+
+
+void PNS_NODE::followLine( PNS_SEGMENT* aCurrent, bool aScanDirection, int& aPos,
+ int aLimit, VECTOR2I* aCorners, PNS_SEGMENT** aSegments, bool& aGuardHit,
+ bool aStopAtLockedJoints )
+{
+ bool prevReversed = false;
+
+ const VECTOR2I guard = aScanDirection ? aCurrent->Seg().B : aCurrent->Seg().A;
+
+ for( int count = 0 ; ; ++count )
+ {
+ const VECTOR2I p =
+ ( aScanDirection ^ prevReversed ) ? aCurrent->Seg().B : aCurrent->Seg().A;
+ const PNS_JOINT* jt = FindJoint( p, aCurrent );
+
+ assert( jt );
+
+ aCorners[aPos] = jt->Pos();
+ aSegments[aPos] = aCurrent;
+ aPos += ( aScanDirection ? 1 : -1 );
+
+ if( count && guard == p)
+ {
+ aSegments[aPos] = NULL;
+ aGuardHit = true;
+ break;
+ }
+
+ bool locked = aStopAtLockedJoints ? jt->IsLocked() : false;
+
+ if( locked || !jt->IsLineCorner() || aPos < 0 || aPos == aLimit )
+ break;
+
+ aCurrent = jt->NextSegment( aCurrent );
+
+ prevReversed =
+ ( jt->Pos() == ( aScanDirection ? aCurrent->Seg().B : aCurrent->Seg().A ) );
+ }
+}
+
+
+const PNS_LINE PNS_NODE::AssembleLine( PNS_SEGMENT* aSeg, int* aOriginSegmentIndex, bool aStopAtLockedJoints )
+{
+ const int MaxVerts = 1024 * 16;
+
+ VECTOR2I corners[MaxVerts + 1];
+ PNS_SEGMENT* segs[MaxVerts + 1];
+
+ PNS_LINE pl;
+ bool guardHit = false;
+
+ int i_start = MaxVerts / 2, i_end = i_start + 1;
+
+ pl.SetWidth( aSeg->Width() );
+ pl.SetLayers( aSeg->Layers() );
+ pl.SetNet( aSeg->Net() );
+ pl.SetOwner( this );
+
+ followLine( aSeg, false, i_start, MaxVerts, corners, segs, guardHit, aStopAtLockedJoints );
+
+ if( !guardHit )
+ followLine( aSeg, true, i_end, MaxVerts, corners, segs, guardHit, aStopAtLockedJoints );
+
+ int n = 0;
+
+ PNS_SEGMENT* prev_seg = NULL;
+ bool originSet = false;
+
+ for( int i = i_start + 1; i < i_end; i++ )
+ {
+ const VECTOR2I& p = corners[i];
+
+ pl.Line().Append( p );
+
+ if( segs[i] && prev_seg != segs[i] )
+ {
+ pl.LinkSegment( segs[i] );
+
+ // latter condition to avoid loops
+ if( segs[i] == aSeg && aOriginSegmentIndex && !originSet )
+ {
+ *aOriginSegmentIndex = n;
+ originSet = true;
+ }
+ n++;
+ }
+
+ prev_seg = segs[i];
+ }
+
+ assert( pl.SegmentCount() != 0 );
+
+ return pl;
+}
+
+
+void PNS_NODE::FindLineEnds( const PNS_LINE& aLine, PNS_JOINT& aA, PNS_JOINT& aB )
+{
+ aA = *FindJoint( aLine.CPoint( 0 ), &aLine );
+ aB = *FindJoint( aLine.CPoint( -1 ), &aLine );
+}
+
+
+#if 0
+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 );
+}
+#endif
+
+
+int PNS_NODE::FindLinesBetweenJoints( PNS_JOINT& aA, PNS_JOINT& aB, std::vector<PNS_LINE>& aLines )
+{
+ BOOST_FOREACH( PNS_ITEM* item, aA.LinkList() )
+ {
+ if( item->Kind() == PNS_ITEM::SEGMENT )
+ {
+ PNS_SEGMENT* seg = static_cast<PNS_SEGMENT*>( item );
+ PNS_LINE line = AssembleLine( seg );
+
+ if ( !line.Layers().Overlaps( aB.Layers() ) )
+ continue;
+
+ PNS_JOINT j_start, j_end;
+
+ FindLineEnds( line, j_start, j_end );
+
+ int id_start = line.CLine().Find( aA.Pos() );
+ int id_end = line.CLine().Find( aB.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 );
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+PNS_JOINT* PNS_NODE::FindJoint( const VECTOR2I& aPos, int aLayer, int aNet )
+{
+ PNS_JOINT::HASH_TAG tag;
+
+ tag.net = aNet;
+ tag.pos = aPos;
+
+ JOINT_MAP::iterator f = m_joints.find( tag ), end = m_joints.end();
+
+ if( f == end && !isRoot() )
+ {
+ end = m_root->m_joints.end();
+ f = m_root->m_joints.find( tag ); // m_root->FindJoint(aPos, aLayer, aNet);
+ }
+
+ if( f == end )
+ return NULL;
+
+ while( f != end )
+ {
+ if( f->second.Layers().Overlaps( aLayer ) )
+ return &f->second;
+
+ ++f;
+ }
+
+ return NULL;
+}
+
+
+void PNS_NODE::LockJoint( const VECTOR2I& aPos, const PNS_ITEM* aItem, bool aLock )
+{
+ PNS_JOINT& jt = touchJoint( aPos, aItem->Layers(), aItem->Net() );
+ jt.Lock( aLock );
+}
+
+
+PNS_JOINT& PNS_NODE::touchJoint( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers, int aNet )
+{
+ PNS_JOINT::HASH_TAG tag;
+
+ tag.pos = aPos;
+ tag.net = aNet;
+
+ // try to find the joint in this node.
+ JOINT_MAP::iterator f = m_joints.find( tag );
+
+ std::pair<JOINT_MAP::iterator, JOINT_MAP::iterator> range;
+
+ // not found and we are not root? find in the root and copy results here.
+ if( f == m_joints.end() && !isRoot() )
+ {
+ range = m_root->m_joints.equal_range( tag );
+
+ for( f = range.first; f != range.second; ++f )
+ m_joints.insert( *f );
+ }
+
+ // now insert and combine overlapping joints
+ PNS_JOINT jt( aPos, aLayers, aNet );
+
+ bool merged;
+
+ do
+ {
+ merged = false;
+ range = m_joints.equal_range( tag );
+
+ if( range.first == m_joints.end() )
+ break;
+
+ for( f = range.first; f != range.second; ++f )
+ {
+ if( aLayers.Overlaps( f->second.Layers() ) )
+ {
+ jt.Merge( f->second );
+ m_joints.erase( f );
+ merged = true;
+ break;
+ }
+ }
+ }
+ while( merged );
+
+ return m_joints.insert( TagJointPair( tag, jt ) )->second;
+}
+
+
+void PNS_JOINT::Dump() const
+{
+ printf( "joint layers %d-%d, net %d, pos %s, links: %d\n", m_layers.Start(),
+ m_layers.End(), m_tag.net, m_tag.pos.Format().c_str(), LinkCount() );
+}
+
+
+void PNS_NODE::linkJoint( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers,
+ int aNet, PNS_ITEM* aWhere )
+{
+ PNS_JOINT& jt = touchJoint( aPos, aLayers, aNet );
+
+ jt.Link( aWhere );
+}
+
+
+void PNS_NODE::unlinkJoint( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers,
+ int aNet, PNS_ITEM* aWhere )
+{
+ // fixme: remove dangling joints
+ PNS_JOINT& jt = touchJoint( aPos, aLayers, aNet );
+
+ jt.Unlink( aWhere );
+}
+
+
+void PNS_NODE::Dump( bool aLong )
+{
+#if 0
+ boost::unordered_set<PNS_SEGMENT*> all_segs;
+ SHAPE_INDEX_LIST<PNS_ITEM*>::iterator i;
+
+ for( i = m_items.begin(); i != m_items.end(); i++ )
+ {
+ if( (*i)->GetKind() == PNS_ITEM::SEGMENT )
+ all_segs.insert( static_cast<PNS_SEGMENT*>( *i ) );
+ }
+
+ if( !isRoot() )
+ {
+ for( i = m_root->m_items.begin(); i != m_root->m_items.end(); i++ )
+ {
+ if( (*i)->GetKind() == PNS_ITEM::SEGMENT && !overrides( *i ) )
+ all_segs.insert( static_cast<PNS_SEGMENT*>(*i) );
+ }
+ }
+
+ JOINT_MAP::iterator j;
+
+ if( aLong )
+ for( j = m_joints.begin(); j != m_joints.end(); ++j )
+ {
+ printf( "joint : %s, links : %d\n",
+ j->second.GetPos().Format().c_str(), j->second.LinkCount() );
+ PNS_JOINT::LINKED_ITEMS::const_iterator k;
+
+ for( k = j->second.GetLinkList().begin(); k != j->second.GetLinkList().end(); ++k )
+ {
+ const PNS_ITEM* m_item = *k;
+
+ switch( m_item->GetKind() )
+ {
+ case PNS_ITEM::SEGMENT:
+ {
+ const PNS_SEGMENT* seg = static_cast<const PNS_SEGMENT*>( m_item );
+ printf( " -> seg %s %s\n", seg->GetSeg().A.Format().c_str(),
+ seg->GetSeg().B.Format().c_str() );
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ }
+
+
+ int lines_count = 0;
+
+ while( !all_segs.empty() )
+ {
+ PNS_SEGMENT* s = *all_segs.begin();
+ PNS_LINE* l = AssembleLine( s );
+
+ PNS_LINE::LinkedSegments* seg_refs = l->GetLinkedSegments();
+
+ if( aLong )
+ printf( "Line: %s, net %d ", l->GetLine().Format().c_str(), l->GetNet() );
+
+ for( std::vector<PNS_SEGMENT*>::iterator j = seg_refs->begin(); j != seg_refs->end(); ++j )
+ {
+ printf( "%s ", (*j)->GetSeg().A.Format().c_str() );
+
+ if( j + 1 == seg_refs->end() )
+ printf( "%s\n", (*j)->GetSeg().B.Format().c_str() );
+
+ all_segs.erase( *j );
+ }
+
+ lines_count++;
+ }
+
+ printf( "Local joints: %d, lines : %d \n", m_joints.size(), lines_count );
+#endif
+}
+
+
+void PNS_NODE::GetUpdatedItems( ITEM_VECTOR& aRemoved, ITEM_VECTOR& aAdded )
+{
+ aRemoved.reserve( m_override.size() );
+ aAdded.reserve( m_index->Size() );
+
+ if( isRoot() )
+ return;
+
+ BOOST_FOREACH( PNS_ITEM* item, m_override )
+ aRemoved.push_back( item );
+
+ for( PNS_INDEX::ITEM_SET::iterator i = m_index->begin(); i != m_index->end(); ++i )
+ aAdded.push_back( *i );
+}
+
+void PNS_NODE::releaseChildren()
+{
+ // copy the kids as the PNS_NODE destructor erases the item from the parent node.
+ std::set<PNS_NODE*> kids = m_children;
+
+ BOOST_FOREACH( PNS_NODE* node, kids )
+ {
+ node->releaseChildren();
+ delete node;
+ }
+}
+
+
+void PNS_NODE::releaseGarbage()
+{
+ if( !isRoot() )
+ return;
+
+ BOOST_FOREACH( PNS_ITEM* item, m_garbageItems )
+ {
+ if( !item->BelongsTo( this ) )
+ delete item;
+ }
+
+ m_garbageItems.clear();
+}
+
+
+void PNS_NODE::Commit( PNS_NODE* aNode )
+{
+ if( aNode->isRoot() )
+ return;
+
+ BOOST_FOREACH( PNS_ITEM* item, aNode->m_override )
+ Remove( item );
+
+ for( PNS_INDEX::ITEM_SET::iterator i = aNode->m_index->begin();
+ i != aNode->m_index->end(); ++i )
+ {
+ (*i)->SetRank( -1 );
+ (*i)->Unmark();
+ Add( *i );
+ }
+
+ releaseChildren();
+ releaseGarbage();
+}
+
+
+void PNS_NODE::KillChildren()
+{
+ assert( isRoot() );
+ releaseChildren();
+}
+
+
+void PNS_NODE::AllItemsInNet( int aNet, std::set<PNS_ITEM*>& aItems )
+{
+ PNS_INDEX::NET_ITEMS_LIST* l_cur = m_index->GetItemsForNet( aNet );
+
+ if( l_cur )
+ {
+ BOOST_FOREACH( PNS_ITEM*item, *l_cur )
+ aItems.insert( item );
+ }
+
+ if( !isRoot() )
+ {
+ PNS_INDEX::NET_ITEMS_LIST* l_root = m_root->m_index->GetItemsForNet( aNet );
+
+ if( l_root )
+ for( PNS_INDEX::NET_ITEMS_LIST::iterator i = l_root->begin(); i!= l_root->end(); ++i )
+ if( !overrides( *i ) )
+ aItems.insert( *i );
+ }
+}
+
+
+void PNS_NODE::ClearRanks( int aMarkerMask )
+{
+ for( PNS_INDEX::ITEM_SET::iterator i = m_index->begin(); i != m_index->end(); ++i )
+ {
+ (*i)->SetRank( -1 );
+ (*i)->Mark( (*i)->Marker() & (~aMarkerMask) );
+ }
+}
+
+
+int PNS_NODE::FindByMarker( int aMarker, PNS_ITEMSET& aItems )
+{
+ for( PNS_INDEX::ITEM_SET::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 )
+{
+ std::list<PNS_ITEM*> garbage;
+
+ for( PNS_INDEX::ITEM_SET::iterator i = m_index->begin(); i != m_index->end(); ++i )
+ {
+ if ( (*i)->Marker() & aMarker )
+ {
+ garbage.push_back( *i );
+ }
+ }
+
+ for( std::list<PNS_ITEM*>::const_iterator i = garbage.begin(), end = garbage.end(); i != end; ++i )
+ {
+ 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;
+}
+
+
+void PNS_NODE::SetCollisionFilter( PNS_COLLISION_FILTER* aFilter )
+{
+ m_collisionFilter = aFilter;
+}
+
+
+PNS_ITEM *PNS_NODE::FindItemByParent( const BOARD_CONNECTED_ITEM* aParent )
+{
+ PNS_INDEX::NET_ITEMS_LIST* l_cur = m_index->GetItemsForNet( aParent->GetNetCode() );
+
+ BOOST_FOREACH( PNS_ITEM*item, *l_cur )
+ if( item->Parent() == aParent )
+ return item;
+
+ return NULL;
+}
diff --git a/pcbnew/router/pns_node.h b/pcbnew/router/pns_node.h
new file mode 100644
index 0000000..9846da8
--- /dev/null
+++ b/pcbnew/router/pns_node.h
@@ -0,0 +1,487 @@
+/*
+ * 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_NODE_H
+#define __PNS_NODE_H
+
+#include <vector>
+#include <list>
+
+#include <boost/unordered_set.hpp>
+#include <boost/unordered_map.hpp>
+#include <boost/optional.hpp>
+
+#include <geometry/shape.h>
+#include <geometry/shape_line_chain.h>
+#include <geometry/shape_index.h>
+
+#include "pns_item.h"
+#include "pns_joint.h"
+#include "pns_itemset.h"
+
+class PNS_SEGMENT;
+class PNS_LINE;
+class PNS_SOLID;
+class PNS_VIA;
+class PNS_RATSNEST;
+class PNS_INDEX;
+class PNS_ROUTER;
+
+/**
+ * Class PNS_CLEARANCE_FUNC
+ *
+ * An abstract function object, returning a required clearance between two items.
+ **/
+class PNS_CLEARANCE_FUNC
+{
+public:
+ virtual ~PNS_CLEARANCE_FUNC() {}
+ virtual int operator()( const PNS_ITEM* aA, const PNS_ITEM* aB ) = 0;
+ virtual void OverrideClearance (bool aEnable, int aNetA = 0, int aNetB = 0, int aClearance = 0) = 0;
+};
+
+class PNS_PCBNEW_CLEARANCE_FUNC : public PNS_CLEARANCE_FUNC
+{
+public:
+ PNS_PCBNEW_CLEARANCE_FUNC( PNS_ROUTER *aRouter );
+ virtual ~PNS_PCBNEW_CLEARANCE_FUNC();
+
+ virtual int operator()( const PNS_ITEM* aA, const PNS_ITEM* aB );
+ virtual void OverrideClearance (bool aEnable, int aNetA = 0, int aNetB = 0, int aClearance = 0);
+
+ void UseDpGap( bool aUseDpGap ) { m_useDpGap = aUseDpGap; }
+
+private:
+ struct CLEARANCE_ENT {
+ int coupledNet;
+ int clearance;
+ };
+
+ PNS_ROUTER *m_router;
+
+ int localPadClearance( const PNS_ITEM* aItem ) const;
+ std::vector<CLEARANCE_ENT> m_clearanceCache;
+ int m_defaultClearance;
+ bool m_overrideEnabled;
+ int m_overrideNetA, m_overrideNetB;
+ int m_overrideClearance;
+ bool m_useDpGap;
+};
+
+/**
+ * Struct PNS_OBSTACLE
+ *
+ * Holds an object colliding with another object, along with
+ * some useful data about the collision.
+ **/
+struct PNS_OBSTACLE
+{
+ ///> Item we search collisions with
+ const PNS_ITEM* m_head;
+
+ ///> Item found to be colliding with m_head
+ PNS_ITEM* m_item;
+
+ ///> Hull of the colliding m_item
+ SHAPE_LINE_CHAIN m_hull;
+
+ ///> First and last intersection point between the head item and the hull
+ ///> of the colliding m_item
+ VECTOR2I m_ipFirst, m_ipLast;
+
+ ///> ... and the distance thereof
+ int m_distFirst, m_distLast;
+};
+
+/**
+ * Struct PNS_COLLISION_FILTER
+ * Used to override the decision of the collision search algorithm whether two
+ * items collide.
+ **/
+struct PNS_COLLISION_FILTER {
+ virtual bool operator()( const PNS_ITEM *aItemA, const PNS_ITEM *aItemB ) const = 0;
+};
+
+/**
+ * Class PNS_NODE
+ *
+ * Keeps the router "world" - i.e. all the tracks, vias, solids in a
+ * hierarchical and indexed way.
+ * Features:
+ * - spatial-indexed container for PCB item shapes
+ * - collision search & clearance checking
+ * - assembly of lines connecting joints, finding loops and unique paths
+ * - lightweight cloning/branching (for recursive optimization and shove
+ * springback)
+ **/
+class PNS_NODE
+{
+public:
+ typedef boost::optional<PNS_OBSTACLE> OPT_OBSTACLE;
+ typedef std::vector<PNS_ITEM*> ITEM_VECTOR;
+ typedef std::vector<PNS_OBSTACLE> OBSTACLES;
+
+ PNS_NODE();
+ ~PNS_NODE();
+
+ ///> Returns the expected clearance between items a and b.
+ int GetClearance( const PNS_ITEM* aA, const PNS_ITEM* aB ) const;
+
+ ///> Returns the pre-set worst case clearance between any pair of items
+ int GetMaxClearance() const
+ {
+ return m_maxClearance;
+ }
+
+ ///> Sets the worst-case clerance between any pair of items
+ void SetMaxClearance( int aClearance )
+ {
+ m_maxClearance = aClearance;
+ }
+
+ ///> Assigns a clerance resolution function object
+ void SetClearanceFunctor( PNS_CLEARANCE_FUNC* aFunc )
+ {
+ m_clearanceFunctor = aFunc;
+ }
+
+ ///> Returns the number of joints
+ int JointCount() const
+ {
+ return m_joints.size();
+ }
+
+ ///> Returns the number of nodes in the inheritance chain (wrs to the root node)
+ 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,
+ bool aDifferentNetsOnly = true,
+ int aForceClearance = -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.
+ */
+ OPT_OBSTACLE NearestObstacle( const PNS_LINE* aItem,
+ int aKindMask = PNS_ITEM::ANY,
+ const std::set<PNS_ITEM*>* aRestrictedSet = NULL );
+
+ /**
+ * 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.
+ */
+ OPT_OBSTACLE 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.
+ */
+ OPT_OBSTACLE CheckColliding( const PNS_ITEMSET& aSet,
+ int aKindMask = PNS_ITEM::ANY );
+
+
+ /**
+ * Function CheckColliding()
+ *
+ * Checks if 2 items collide.
+ * and if found, returns the obstacle.
+ * @param aItemA first item to find collisions with
+ * @param aItemB second item 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,
+ int aForceClearance = -1 );
+
+ /**
+ * 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 Remove()
+ *
+ * Just as the name says, removes a line from this branch.
+ * @param aItem item to remove
+ */
+ void Remove( PNS_LINE& aLine );
+
+
+ /**
+ * 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
+ */
+ const PNS_LINE AssembleLine( PNS_SEGMENT* aSeg, int* aOriginSegmentIndex = NULL,
+ bool aStopAtLockedJoints = false );
+
+ ///> 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( ITEM_VECTOR& aRemoved, ITEM_VECTOR& aAdded );
+
+ /**
+ * 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 );
+
+ /**
+ * 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 );
+
+ void LockJoint( const VECTOR2I& aPos, const PNS_ITEM* aItem, bool aLock );
+
+ /**
+ * 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, const PNS_ITEM* aItem )
+ {
+ return FindJoint( aPos, aItem->Layers().Start(), aItem->Net() );
+ }
+
+#if 0
+ void MapConnectivity( PNS_JOINT* aStart, std::vector<PNS_JOINT*> & aFoundJoints );
+
+ PNS_ITEM* NearestUnconnectedItem( PNS_JOINT* aStart, int *aAnchor = NULL,
+ int aKindMask = PNS_ITEM::ANY);
+
+#endif
+
+ ///> finds all lines between a pair of joints. Used by the loop removal procedure.
+ int FindLinesBetweenJoints( PNS_JOINT& aA,
+ PNS_JOINT& aB,
+ std::vector<PNS_LINE>& aLines );
+
+ ///> finds the joints corresponding to the ends of line aLine
+ void FindLineEnds( const PNS_LINE& aLine, PNS_JOINT& aA, PNS_JOINT& aB );
+
+ ///> Destroys all child nodes. Applicable only to the root node.
+ void KillChildren();
+
+ void AllItemsInNet( int aNet, std::set<PNS_ITEM*>& aItems );
+
+ void ClearRanks( int aMarkerMask = MK_HEAD | MK_VIOLATION );
+
+ int FindByMarker( int aMarker, PNS_ITEMSET& aItems );
+ int RemoveByMarker( int aMarker );
+ void SetCollisionFilter( PNS_COLLISION_FILTER* aFilter );
+
+ PNS_ITEM* FindItemByParent( const BOARD_CONNECTED_ITEM *aParent );
+
+ bool HasChildren() const
+ {
+ return !m_children.empty();
+ }
+
+private:
+ struct OBSTACLE_VISITOR;
+ typedef boost::unordered_multimap<PNS_JOINT::HASH_TAG, PNS_JOINT> JOINT_MAP;
+ typedef JOINT_MAP::value_type TagJointPair;
+
+ /// nodes are not copyable
+ PNS_NODE( const PNS_NODE& aB );
+ PNS_NODE& operator=( const PNS_NODE& aB );
+
+ ///> tries to find matching joint and creates a new one if not found
+ PNS_JOINT& touchJoint( const VECTOR2I& aPos,
+ const PNS_LAYERSET& aLayers,
+ int aNet );
+
+ ///> touches a joint and links it to an m_item
+ void linkJoint( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers,
+ int aNet, PNS_ITEM* aWhere );
+
+ ///> unlinks an item from a joint
+ void unlinkJoint( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers,
+ int aNet, PNS_ITEM* aWhere );
+
+ ///> helpers for adding/removing items
+ void addSolid( PNS_SOLID* aSeg );
+ void addSegment( PNS_SEGMENT* aSeg, bool aAllowRedundant );
+ void addLine( PNS_LINE* aLine, bool aAllowRedundant );
+ void addVia( PNS_VIA* aVia );
+ void removeSolid( PNS_SOLID* aSeg );
+ void removeLine( PNS_LINE* aLine );
+ void removeSegment( PNS_SEGMENT* aSeg );
+ void removeVia( PNS_VIA* aVia );
+
+ void doRemove( PNS_ITEM* aItem );
+ void unlinkParent();
+ void releaseChildren();
+ void releaseGarbage();
+
+ bool isRoot() const
+ {
+ return m_parent == NULL;
+ }
+
+ ///> checks if this branch contains an updated version of the m_item
+ ///> from the root branch.
+ bool overrides( PNS_ITEM* aItem ) const
+ {
+ return m_override.find( aItem ) != m_override.end();
+ }
+
+ PNS_SEGMENT* findRedundantSegment( PNS_SEGMENT* aSeg );
+
+ ///> scans the joint map, forming a line starting from segment (current).
+ void followLine( PNS_SEGMENT* aCurrent,
+ bool aScanDirection,
+ int& aPos,
+ int aLimit,
+ VECTOR2I* aCorners,
+ PNS_SEGMENT** aSegments,
+ bool& aGuardHit,
+ bool aStopAtLockedJoints );
+
+ ///> hash table with the joints, linking the items. Joints are hashed by
+ ///> their position, layer set and net.
+ JOINT_MAP m_joints;
+
+ ///> node this node was branched from
+ PNS_NODE* m_parent;
+
+ ///> root node of the whole hierarchy
+ PNS_NODE* m_root;
+
+ ///> list of nodes branched from this one
+ std::set<PNS_NODE*> m_children;
+
+ ///> hash of root's items that have been changed in this node
+ boost::unordered_set<PNS_ITEM*> m_override;
+
+ ///> worst case item-item clearance
+ int m_maxClearance;
+
+ ///> Clearance resolution functor
+ PNS_CLEARANCE_FUNC* m_clearanceFunctor;
+
+ ///> Geometric/Net index of the items
+ PNS_INDEX* m_index;
+
+ ///> depth of the node (number of parent nodes in the inheritance chain)
+ int m_depth;
+
+ ///> optional collision filtering object
+ PNS_COLLISION_FILTER* m_collisionFilter;
+
+ boost::unordered_set<PNS_ITEM*> m_garbageItems;
+};
+
+#endif
diff --git a/pcbnew/router/pns_optimizer.cpp b/pcbnew/router/pns_optimizer.cpp
new file mode 100644
index 0000000..d18933f
--- /dev/null
+++ b/pcbnew/router/pns_optimizer.cpp
@@ -0,0 +1,1224 @@
+/*
+ * 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 <geometry/shape_line_chain.h>
+#include <geometry/shape_rect.h>
+#include <geometry/shape_convex.h>
+
+#include "pns_line.h"
+#include "pns_diff_pair.h"
+#include "pns_node.h"
+#include "pns_solid.h"
+#include "pns_optimizer.h"
+#include "pns_utils.h"
+#include "pns_router.h"
+
+/**
+ * Cost Estimator Methods
+ */
+int PNS_COST_ESTIMATOR::CornerCost( const SEG& aA, const SEG& aB )
+{
+ DIRECTION_45 dir_a( aA ), dir_b( aB );
+
+ switch( dir_a.Angle( dir_b ) )
+ {
+ case DIRECTION_45::ANG_OBTUSE:
+ return 1;
+
+ case DIRECTION_45::ANG_STRAIGHT:
+ return 0;
+
+ case DIRECTION_45::ANG_ACUTE:
+ return 50;
+
+ case DIRECTION_45::ANG_RIGHT:
+ return 30;
+
+ case DIRECTION_45::ANG_HALF_FULL:
+ return 60;
+
+ default:
+ return 100;
+ }
+}
+
+
+int PNS_COST_ESTIMATOR::CornerCost( const SHAPE_LINE_CHAIN& aLine )
+{
+ int total = 0;
+
+ for( int i = 0; i < aLine.SegmentCount() - 1; ++i )
+ total += CornerCost( aLine.CSegment( i ), aLine.CSegment( i + 1 ) );
+
+ return total;
+}
+
+
+int PNS_COST_ESTIMATOR::CornerCost( const PNS_LINE& aLine )
+{
+ return CornerCost( aLine.CLine() );
+}
+
+
+void PNS_COST_ESTIMATOR::Add( PNS_LINE& aLine )
+{
+ m_lengthCost += aLine.CLine().Length();
+ m_cornerCost += CornerCost( aLine );
+}
+
+
+void PNS_COST_ESTIMATOR::Remove( PNS_LINE& aLine )
+{
+ m_lengthCost -= aLine.CLine().Length();
+ m_cornerCost -= CornerCost( aLine );
+}
+
+
+void PNS_COST_ESTIMATOR::Replace( PNS_LINE& aOldLine, PNS_LINE& aNewLine )
+{
+ m_lengthCost -= aOldLine.CLine().Length();
+ m_cornerCost -= CornerCost( aOldLine );
+ m_lengthCost += aNewLine.CLine().Length();
+ m_cornerCost += CornerCost( aNewLine );
+}
+
+
+bool PNS_COST_ESTIMATOR::IsBetter( PNS_COST_ESTIMATOR& aOther,
+ double aLengthTolerance,
+ double aCornerTolerance ) const
+{
+ if( aOther.m_cornerCost < m_cornerCost && aOther.m_lengthCost < m_lengthCost )
+ return true;
+
+ else if( aOther.m_cornerCost < m_cornerCost * aCornerTolerance &&
+ aOther.m_lengthCost < m_lengthCost * aLengthTolerance )
+ return true;
+
+ return false;
+}
+
+
+/**
+ * Optimizer
+ **/
+PNS_OPTIMIZER::PNS_OPTIMIZER( PNS_NODE* aWorld ) :
+ m_world( aWorld ),
+ m_collisionKindMask( PNS_ITEM::ANY ),
+ m_effortLevel( MERGE_SEGMENTS ),
+ m_keepPostures( false ),
+ m_restrictAreaActive( false )
+{
+}
+
+
+PNS_OPTIMIZER::~PNS_OPTIMIZER()
+{
+}
+
+
+struct PNS_OPTIMIZER::CACHE_VISITOR
+{
+ CACHE_VISITOR( const PNS_ITEM* aOurItem, PNS_NODE* aNode, int aMask ) :
+ m_ourItem( aOurItem ),
+ m_collidingItem( NULL ),
+ m_node( aNode ),
+ m_mask( aMask )
+ {}
+
+ bool operator()( PNS_ITEM* aOtherItem )
+ {
+ if( !( m_mask & aOtherItem->Kind() ) )
+ return true;
+
+ int clearance = m_node->GetClearance( aOtherItem, m_ourItem );
+
+ if( !aOtherItem->Collide( m_ourItem, clearance ) )
+ return true;
+
+ m_collidingItem = aOtherItem;
+ return false;
+ }
+
+ const PNS_ITEM* m_ourItem;
+ PNS_ITEM* m_collidingItem;
+ PNS_NODE* m_node;
+ int m_mask;
+};
+
+
+void PNS_OPTIMIZER::cacheAdd( PNS_ITEM* aItem, bool aIsStatic = false )
+{
+ if( m_cacheTags.find( aItem ) != m_cacheTags.end() )
+ return;
+
+ m_cache.Add( aItem );
+ m_cacheTags[aItem].m_hits = 1;
+ m_cacheTags[aItem].m_isStatic = aIsStatic;
+}
+
+
+void PNS_OPTIMIZER::removeCachedSegments( PNS_LINE* aLine, int aStartVertex, int aEndVertex )
+{
+ PNS_LINE::SEGMENT_REFS* segs = aLine->LinkedSegments();
+
+ if( !segs )
+ return;
+
+ if( aEndVertex < 0 )
+ aEndVertex += aLine->PointCount();
+
+ for( int i = aStartVertex; i < aEndVertex - 1; i++ )
+ {
+ PNS_SEGMENT* s = (*segs)[i];
+ m_cacheTags.erase( s );
+ m_cache.Remove( s );
+ }
+}
+
+
+void PNS_OPTIMIZER::CacheRemove( PNS_ITEM* aItem )
+{
+ if( aItem->Kind() == PNS_ITEM::LINE )
+ removeCachedSegments( static_cast<PNS_LINE*>( aItem ) );
+}
+
+
+void PNS_OPTIMIZER::CacheStaticItem( PNS_ITEM* aItem )
+{
+ cacheAdd( aItem, true );
+}
+
+
+void PNS_OPTIMIZER::ClearCache( bool aStaticOnly )
+{
+ if( !aStaticOnly )
+ {
+ m_cacheTags.clear();
+ m_cache.Clear();
+ return;
+ }
+
+ for( CachedItemTags::iterator i = m_cacheTags.begin(); i!= m_cacheTags.end(); ++i )
+ {
+ if( i->second.m_isStatic )
+ {
+ m_cache.Remove( i->first );
+ m_cacheTags.erase( i->first );
+ }
+ }
+}
+
+
+class LINE_RESTRICTIONS
+{
+ public:
+ LINE_RESTRICTIONS() {};
+ ~LINE_RESTRICTIONS() {};
+
+ void Build( PNS_NODE* aWorld, PNS_LINE* aOriginLine, const SHAPE_LINE_CHAIN& aLine, const BOX2I& aRestrictedArea, bool aRestrictedAreaEnable );
+ bool Check ( int aVertex1, int aVertex2, const SHAPE_LINE_CHAIN& aReplacement );
+ void Dump();
+
+ private:
+ int allowedAngles( PNS_NODE* aWorld, const PNS_LINE* aLine, const VECTOR2I& aP, bool aFirst );
+
+ struct RVERTEX
+ {
+ RVERTEX ( bool aRestricted, int aAllowedAngles ) :
+ restricted( aRestricted ),
+ allowedAngles( aAllowedAngles )
+ {
+ }
+
+ bool restricted;
+ int allowedAngles;
+ };
+
+ std::vector<RVERTEX> m_rs;
+};
+
+
+// fixme: use later
+int LINE_RESTRICTIONS::allowedAngles( PNS_NODE* aWorld, const PNS_LINE* aLine, const VECTOR2I& aP, bool aFirst )
+{
+ PNS_JOINT* jt = aWorld->FindJoint( aP , aLine );
+
+ if( !jt )
+ return 0xff;
+
+ DIRECTION_45 dirs [8];
+
+ int n_dirs = 0;
+
+ BOOST_FOREACH( const PNS_ITEM* item, jt->Links().CItems() )
+ {
+ if( item->OfKind( PNS_ITEM::VIA ) || item->OfKind( PNS_ITEM::SOLID ) )
+ return 0xff;
+ else if( const PNS_SEGMENT* seg = dyn_cast<const PNS_SEGMENT*>( item ) )
+ {
+ SEG s = seg->Seg();
+ if( s.A != aP )
+ s.Reverse();
+
+ if( n_dirs < 8 )
+ dirs[n_dirs++] = aFirst ? DIRECTION_45( s ) : DIRECTION_45( s ).Opposite();
+ }
+ }
+
+ const int angleMask = DIRECTION_45::ANG_OBTUSE | DIRECTION_45::ANG_HALF_FULL | DIRECTION_45::ANG_STRAIGHT;
+ int outputMask = 0xff;
+
+ for( int d = 0; d < 8; d++ )
+ {
+ DIRECTION_45 refDir( ( DIRECTION_45::Directions ) d );
+
+ for( int i = 0; i < n_dirs; i++ )
+ {
+ if( !( refDir.Angle( dirs[i] ) & angleMask ) )
+ outputMask &= ~refDir.Mask();
+ }
+ }
+
+ DrawDebugDirs( aP, outputMask, 3 );
+ return 0xff;
+}
+
+
+void LINE_RESTRICTIONS::Build( PNS_NODE* aWorld, PNS_LINE* aOriginLine, const SHAPE_LINE_CHAIN& aLine, const BOX2I& aRestrictedArea, bool aRestrictedAreaEnable )
+{
+ const SHAPE_LINE_CHAIN& l = aLine;
+ VECTOR2I v_prev;
+ int n = l.PointCount( );
+
+ m_rs.reserve( n );
+
+ for( int i = 0; i < n; i++ )
+ {
+ const VECTOR2I &v = l.CPoint( i ), v_next;
+ RVERTEX r( false, 0xff );
+
+ if( aRestrictedAreaEnable )
+ {
+ bool exiting = ( i > 0 && aRestrictedArea.Contains( v_prev ) && !aRestrictedArea.Contains( v ) );
+ bool entering = false;
+
+ if( i != l.PointCount() - 1 )
+ {
+ const VECTOR2I& v_next = l.CPoint( i + 1 );
+ entering = ( !aRestrictedArea.Contains( v ) && aRestrictedArea.Contains( v_next ) );
+ }
+
+ if( entering )
+ {
+ const SEG& sp = l.CSegment( i );
+ r.allowedAngles = DIRECTION_45( sp ).Mask();
+ }
+ else if( exiting )
+ {
+ const SEG& sp = l.CSegment( i - 1 );
+ r.allowedAngles = DIRECTION_45( sp ).Mask();
+ }
+ else
+ {
+ r.allowedAngles = ( !aRestrictedArea.Contains( v ) ) ? 0 : 0xff;
+ r.restricted = r.allowedAngles ? false : true;
+ }
+ }
+
+ v_prev = v;
+ m_rs.push_back( r );
+ }
+}
+
+
+void LINE_RESTRICTIONS::Dump()
+{
+}
+
+
+bool LINE_RESTRICTIONS::Check( int aVertex1, int aVertex2, const SHAPE_LINE_CHAIN& aReplacement )
+{
+ if( m_rs.empty( ) )
+ return true;
+
+ for( int i = aVertex1; i <= aVertex2; i++ )
+ if ( m_rs[i].restricted )
+ return false;
+
+ const RVERTEX& v1 = m_rs[ aVertex1 ];
+ const RVERTEX& v2 = m_rs[ aVertex2 ];
+
+ int m1 = DIRECTION_45( aReplacement.CSegment( 0 ) ).Mask();
+ int m2;
+
+ if( aReplacement.SegmentCount() == 1 )
+ m2 = m1;
+ else
+ m2 = DIRECTION_45( aReplacement.CSegment( 1 ) ).Mask();
+
+ return ( ( v1.allowedAngles & m1 ) != 0 ) &&
+ ( ( v2.allowedAngles & m2 ) != 0 );
+}
+
+
+bool PNS_OPTIMIZER::checkColliding( PNS_ITEM* aItem, bool aUpdateCache )
+{
+ CACHE_VISITOR v( aItem, m_world, m_collisionKindMask );
+
+ return static_cast<bool>( m_world->CheckColliding( aItem ) );
+
+ // something is wrong with the cache, need to investigate.
+ m_cache.Query( aItem->Shape(), m_world->GetMaxClearance(), v, false );
+
+ if( !v.m_collidingItem )
+ {
+ PNS_NODE::OPT_OBSTACLE obs = m_world->CheckColliding( aItem );
+
+ if( obs )
+ {
+ if( aUpdateCache )
+ cacheAdd( obs->m_item );
+
+ return true;
+ }
+ }
+ else
+ {
+ m_cacheTags[v.m_collidingItem].m_hits++;
+ return true;
+ }
+
+ return false;
+}
+
+
+bool PNS_OPTIMIZER::checkColliding( PNS_LINE* aLine, const SHAPE_LINE_CHAIN& aOptPath )
+{
+ PNS_LINE tmp( *aLine, aOptPath );
+
+ return checkColliding( &tmp );
+}
+
+
+bool PNS_OPTIMIZER::mergeObtuse( PNS_LINE* aLine )
+{
+ SHAPE_LINE_CHAIN& line = aLine->Line();
+
+ int step = line.PointCount() - 3;
+ int iter = 0;
+ int segs_pre = line.SegmentCount();
+
+ if( step < 0 )
+ return false;
+
+ SHAPE_LINE_CHAIN current_path( line );
+
+ while( 1 )
+ {
+ iter++;
+ int n_segs = current_path.SegmentCount();
+ int max_step = n_segs - 2;
+
+ if( step > max_step )
+ step = max_step;
+
+ if( step < 2 )
+ {
+ line = current_path;
+ return current_path.SegmentCount() < segs_pre;
+ }
+
+ bool found_anything = false;
+ int n = 0;
+
+ while( n < n_segs - step )
+ {
+ const SEG s1 = current_path.CSegment( n );
+ const SEG s2 = current_path.CSegment( n + step );
+ SEG s1opt, s2opt;
+
+ 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 );
+ }
+
+ if( DIRECTION_45( s1opt ).IsObtuse( DIRECTION_45( s2opt ) ) )
+ {
+ SHAPE_LINE_CHAIN opt_path;
+ opt_path.Append( s1opt.A );
+ opt_path.Append( s1opt.B );
+ opt_path.Append( s2opt.B );
+
+ PNS_LINE opt_track( *aLine, opt_path );
+
+ if( !checkColliding( &opt_track ) )
+ {
+ current_path.Replace( s1.Index() + 1, s2.Index(), ip );
+ // removeCachedSegments(aLine, s1.Index(), s2.Index());
+ n_segs = current_path.SegmentCount();
+ found_anything = true;
+ break;
+ }
+ }
+ }
+
+ n++;
+ }
+
+ if( !found_anything )
+ {
+ if( step <= 2 )
+ {
+ line = current_path;
+ return line.SegmentCount() < segs_pre;
+ }
+
+ step--;
+ }
+ }
+
+ return line.SegmentCount() < segs_pre;
+}
+
+
+bool PNS_OPTIMIZER::mergeFull( PNS_LINE* aLine )
+{
+ SHAPE_LINE_CHAIN& line = aLine->Line();
+ int step = line.SegmentCount() - 1;
+
+ int segs_pre = line.SegmentCount();
+
+ line.Simplify();
+
+ if( step < 0 )
+ return false;
+
+ SHAPE_LINE_CHAIN current_path( line );
+
+ while( 1 )
+ {
+ int n_segs = current_path.SegmentCount();
+ int max_step = n_segs - 2;
+
+ if( step > max_step )
+ step = max_step;
+
+ if( step < 1 )
+ break;
+
+ bool found_anything = mergeStep( aLine, current_path, step );
+
+ if( !found_anything )
+ step--;
+ }
+
+ aLine->SetShape( current_path );
+
+ return current_path.SegmentCount() < segs_pre;
+}
+
+
+bool PNS_OPTIMIZER::Optimize( PNS_LINE* aLine, PNS_LINE* aResult )
+{
+ if( !aResult )
+ aResult = aLine;
+ else
+ *aResult = *aLine;
+
+ m_keepPostures = false;
+
+ bool rv = false;
+
+ if( m_effortLevel & MERGE_SEGMENTS )
+ rv |= mergeFull( aResult );
+
+ if( m_effortLevel & MERGE_OBTUSE )
+ rv |= mergeObtuse( aResult );
+
+ if( m_effortLevel & SMART_PADS )
+ rv |= runSmartPads( aResult );
+
+ if( m_effortLevel & FANOUT_CLEANUP )
+ rv |= fanoutCleanup( aResult );
+
+ return rv;
+}
+
+
+bool PNS_OPTIMIZER::mergeStep( PNS_LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, int step )
+{
+ int n = 0;
+ int n_segs = aCurrentPath.SegmentCount();
+
+ int cost_orig = PNS_COST_ESTIMATOR::CornerCost( aCurrentPath );
+
+ LINE_RESTRICTIONS restr;
+
+ if( aLine->SegmentCount() < 4 )
+ return false;
+
+ DIRECTION_45 orig_start( aLine->CSegment( 0 ) );
+ DIRECTION_45 orig_end( aLine->CSegment( -1 ) );
+
+ restr.Build( m_world, aLine, aCurrentPath, m_restrictArea, m_restrictAreaActive );
+
+ while( n < n_segs - step )
+ {
+ const SEG s1 = aCurrentPath.CSegment( n );
+ const SEG s2 = aCurrentPath.CSegment( n + step );
+
+ SHAPE_LINE_CHAIN path[2];
+ SHAPE_LINE_CHAIN* picked = NULL;
+ int cost[2];
+
+ for( int i = 0; i < 2; i++ )
+ {
+ bool postureMatch = true;
+ SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace( s1.A, s2.B, i );
+ cost[i] = INT_MAX;
+
+ bool restrictionsOK = restr.Check ( n, n + step + 1, bypass );
+
+ if( n == 0 && orig_start != DIRECTION_45( bypass.CSegment( 0 ) ) )
+ postureMatch = false;
+ else if( n == n_segs - step && orig_end != DIRECTION_45( bypass.CSegment( -1 ) ) )
+ postureMatch = false;
+
+ if( restrictionsOK && (postureMatch || !m_keepPostures) && !checkColliding( aLine, bypass ) )
+ {
+ path[i] = aCurrentPath;
+ path[i].Replace( s1.Index(), s2.Index(), bypass );
+ path[i].Simplify();
+ cost[i] = PNS_COST_ESTIMATOR::CornerCost( path[i] );
+ }
+ }
+
+ if( cost[0] < cost_orig && cost[0] < cost[1] )
+ picked = &path[0];
+ else if( cost[1] < cost_orig )
+ picked = &path[1];
+
+ if( picked )
+ {
+ n_segs = aCurrentPath.SegmentCount();
+ aCurrentPath = *picked;
+ return true;
+ }
+
+ n++;
+ }
+
+ return false;
+}
+
+
+PNS_OPTIMIZER::BREAKOUT_LIST PNS_OPTIMIZER::circleBreakouts( int aWidth,
+ const SHAPE* aShape, bool aPermitDiagonal ) const
+{
+ BREAKOUT_LIST breakouts;
+
+ for( int angle = 0; angle < 360; angle += 45 )
+ {
+ const SHAPE_CIRCLE* cir = static_cast<const SHAPE_CIRCLE*>( aShape );
+ SHAPE_LINE_CHAIN l;
+ VECTOR2I p0 = cir->GetCenter();
+ VECTOR2I v0( cir->GetRadius() * M_SQRT2, 0 );
+ l.Append( p0 );
+ l.Append( p0 + v0.Rotate( angle * M_PI / 180.0 ) );
+ breakouts.push_back( l );
+ }
+
+ return breakouts;
+}
+
+
+PNS_OPTIMIZER::BREAKOUT_LIST PNS_OPTIMIZER::convexBreakouts( int aWidth,
+ const SHAPE* aShape, bool aPermitDiagonal ) const
+{
+ BREAKOUT_LIST breakouts;
+ const SHAPE_CONVEX* convex = static_cast<const SHAPE_CONVEX*>( aShape );
+
+ BOX2I bbox = convex->BBox( 0 );
+ VECTOR2I p0 = bbox.Centre();
+ // must be large enough to guarantee intersecting the convex polygon
+ int length = bbox.GetSize().EuclideanNorm() / 2 + 5;
+
+ for( int angle = 0; angle < 360; angle += ( aPermitDiagonal ? 45 : 90 ) )
+ {
+ SHAPE_LINE_CHAIN l;
+ VECTOR2I v0( p0 + VECTOR2I( length, 0 ).Rotate( angle * M_PI / 180.0 ) );
+ SHAPE_LINE_CHAIN::INTERSECTIONS intersections;
+ int n = convex->Vertices().Intersect( SEG( p0, v0 ), intersections );
+ // if n == 1 intersected a segment
+ // if n == 2 intersected the common point of 2 segments
+ // n == 0 can not happen I think, but...
+ if( n > 0 )
+ {
+ l.Append( p0 );
+
+ // for a breakout distance relative to the distance between
+ // center and polygon edge
+ //l.Append( intersections[0].p + (v0 - p0).Resize( (intersections[0].p - p0).EuclideanNorm() * 0.4 ) );
+
+ // for an absolute breakout distance, e.g. 0.1 mm
+ l.Append( intersections[0].p + (v0 - p0).Resize( 100000 ) );
+
+ // for the breakout right on the polygon edge
+ //l.Append( intersections[0].p );
+
+ breakouts.push_back( l );
+ }
+ }
+
+ return breakouts;
+}
+
+
+PNS_OPTIMIZER::BREAKOUT_LIST PNS_OPTIMIZER::rectBreakouts( int aWidth,
+ const SHAPE* aShape, bool aPermitDiagonal ) const
+{
+ const SHAPE_RECT* rect = static_cast<const SHAPE_RECT*>(aShape);
+ VECTOR2I s = rect->GetSize(), c = rect->GetPosition() + VECTOR2I( s.x / 2, s.y / 2 );
+ BREAKOUT_LIST breakouts;
+
+ VECTOR2I d_offset;
+
+ d_offset.x = ( s.x > s.y ) ? ( s.x - s.y ) / 2 : 0;
+ d_offset.y = ( s.x < s.y ) ? ( s.y - s.x ) / 2 : 0;
+
+ VECTOR2I d_vert = VECTOR2I( 0, s.y / 2 + aWidth );
+ VECTOR2I d_horiz = VECTOR2I( s.x / 2 + aWidth, 0 );
+
+ breakouts.push_back( SHAPE_LINE_CHAIN( c, c + d_horiz ) );
+ breakouts.push_back( SHAPE_LINE_CHAIN( c, c - d_horiz ) );
+ breakouts.push_back( SHAPE_LINE_CHAIN( c, c + d_vert ) );
+ breakouts.push_back( SHAPE_LINE_CHAIN( c, c - d_vert ) );
+
+ if( aPermitDiagonal )
+ {
+ int l = aWidth + std::min( s.x, s.y ) / 2;
+ VECTOR2I d_diag;
+
+ if( s.x >= s.y )
+ {
+ breakouts.push_back( SHAPE_LINE_CHAIN( c, c + d_offset,
+ c + d_offset + VECTOR2I( l, l ) ) );
+ breakouts.push_back( SHAPE_LINE_CHAIN( c, c + d_offset,
+ c + d_offset - VECTOR2I( -l, l ) ) );
+ breakouts.push_back( SHAPE_LINE_CHAIN( c, c - d_offset,
+ c - d_offset + VECTOR2I( -l, l ) ) );
+ breakouts.push_back( SHAPE_LINE_CHAIN( c, c - d_offset,
+ c - d_offset - VECTOR2I( l, l ) ) );
+ }
+ else
+ {
+ // fixme: this could be done more efficiently
+ breakouts.push_back( SHAPE_LINE_CHAIN( c, c + d_offset,
+ c + d_offset + VECTOR2I( l, l ) ) );
+ breakouts.push_back( SHAPE_LINE_CHAIN( c, c - d_offset,
+ c - d_offset - VECTOR2I( -l, l ) ) );
+ breakouts.push_back( SHAPE_LINE_CHAIN( c, c + d_offset,
+ c + d_offset + VECTOR2I( -l, l ) ) );
+ breakouts.push_back( SHAPE_LINE_CHAIN( c, c - d_offset,
+ c - d_offset - VECTOR2I( l, l ) ) );
+ }
+ }
+
+ return breakouts;
+}
+
+
+PNS_OPTIMIZER::BREAKOUT_LIST PNS_OPTIMIZER::computeBreakouts( int aWidth,
+ const PNS_ITEM* aItem, bool aPermitDiagonal ) const
+{
+ switch( aItem->Kind() )
+ {
+ case PNS_ITEM::VIA:
+ {
+ const PNS_VIA* via = static_cast<const PNS_VIA*>( aItem );
+ return circleBreakouts( aWidth, via->Shape(), aPermitDiagonal );
+ }
+
+ case PNS_ITEM::SOLID:
+ {
+ const SHAPE* shape = aItem->Shape();
+
+ switch( shape->Type() )
+ {
+ case SH_RECT:
+ 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:
+ return circleBreakouts( aWidth, shape, aPermitDiagonal );
+
+ case SH_CONVEX:
+ return convexBreakouts( aWidth, shape, aPermitDiagonal );
+
+ default:
+ break;
+ }
+ }
+
+ default:
+ break;
+ }
+
+ return BREAKOUT_LIST();
+}
+
+
+PNS_ITEM* PNS_OPTIMIZER::findPadOrVia( int aLayer, int aNet, const VECTOR2I& aP ) const
+{
+ PNS_JOINT* jt = m_world->FindJoint( aP, aLayer, aNet );
+
+ if( !jt )
+ return NULL;
+
+ BOOST_FOREACH( PNS_ITEM* item, jt->LinkList() )
+ {
+ if( item->OfKind( PNS_ITEM::VIA | PNS_ITEM::SOLID ) )
+ return item;
+ }
+
+ return NULL;
+}
+
+
+int PNS_OPTIMIZER::smartPadsSingle( PNS_LINE* aLine, PNS_ITEM* aPad, bool aEnd, int aEndVertex )
+{
+ int min_cost = INT_MAX; // PNS_COST_ESTIMATOR::CornerCost( line );
+ int min_len = INT_MAX;
+ DIRECTION_45 dir;
+
+ const int ForbiddenAngles = DIRECTION_45::ANG_ACUTE | DIRECTION_45::ANG_RIGHT |
+ DIRECTION_45::ANG_HALF_FULL | DIRECTION_45::ANG_UNDEFINED;
+
+ typedef std::pair<int, SHAPE_LINE_CHAIN> RtVariant;
+ std::vector<RtVariant> variants;
+
+ PNS_SOLID* solid = dyn_cast<PNS_SOLID*>( aPad );
+
+ // don't do auto-neckdown for offset pads
+ if( solid && solid->Offset() != VECTOR2I( 0, 0 ) )
+ return -1;
+
+
+ BREAKOUT_LIST breakouts = computeBreakouts( aLine->Width(), aPad, true );
+
+ SHAPE_LINE_CHAIN line = ( aEnd ? aLine->CLine().Reverse() : aLine->CLine() );
+
+
+ int p_end = std::min( aEndVertex, std::min( 3, line.PointCount() - 1 ) );
+
+ for( int p = 1; p <= p_end; p++ )
+ {
+ BOOST_FOREACH( SHAPE_LINE_CHAIN & l, breakouts ) {
+
+ for( int diag = 0; diag < 2; diag++ )
+ {
+ SHAPE_LINE_CHAIN v;
+ SHAPE_LINE_CHAIN connect = dir.BuildInitialTrace( l.CPoint( -1 ),
+ line.CPoint( p ), diag == 0 );
+
+ DIRECTION_45 dir_bkout( l.CSegment( -1 ) );
+
+ if(!connect.SegmentCount())
+ continue;
+
+ int ang1 = dir_bkout.Angle( DIRECTION_45( connect.CSegment( 0 ) ) );
+ int ang2 = 0;
+
+ if( (ang1 | ang2) & ForbiddenAngles )
+ continue;
+
+ if( l.Length() > line.Length() )
+ continue;
+
+ v = l;
+
+ v.Append( connect );
+
+ for( int i = p + 1; i < line.PointCount(); i++ )
+ v.Append( line.CPoint( i ) );
+
+ PNS_LINE tmp( *aLine, v );
+ int cc = tmp.CountCorners( ForbiddenAngles );
+
+ if( cc == 0 )
+ {
+ RtVariant vp;
+ vp.first = p;
+ vp.second = aEnd ? v.Reverse() : v;
+ vp.second.Simplify();
+ variants.push_back( vp );
+ }
+ }
+ }
+ }
+
+ SHAPE_LINE_CHAIN l_best;
+ bool found = false;
+ int p_best = -1;
+
+ BOOST_FOREACH( RtVariant& vp, variants )
+ {
+ PNS_LINE tmp( *aLine, vp.second );
+ int cost = PNS_COST_ESTIMATOR::CornerCost( vp.second );
+ int len = vp.second.Length();
+
+ if( !checkColliding( &tmp ) )
+ {
+ if( cost < min_cost || ( cost == min_cost && len < min_len ) )
+ {
+ l_best = vp.second;
+ p_best = vp.first;
+ found = true;
+
+ if( cost == min_cost )
+ min_len = std::min( len, min_len );
+
+ min_cost = std::min( cost, min_cost );
+ }
+ }
+ }
+
+ if( found )
+ {
+ aLine->SetShape( l_best );
+ return p_best;
+ }
+
+ return -1;
+}
+
+bool PNS_OPTIMIZER::runSmartPads( PNS_LINE* aLine )
+{
+ SHAPE_LINE_CHAIN& line = aLine->Line();
+
+ if( line.PointCount() < 3 )
+ return false;
+
+ VECTOR2I p_start = line.CPoint( 0 ), p_end = line.CPoint( -1 );
+
+ PNS_ITEM* startPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_start );
+ PNS_ITEM* endPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_end );
+
+ int vtx = -1;
+
+ if( startPad )
+ vtx = smartPadsSingle( aLine, startPad, false, 3 );
+
+ if( endPad )
+ smartPadsSingle( aLine, endPad, true,
+ vtx < 0 ? line.PointCount() - 1 : line.PointCount() - 1 - vtx );
+
+ aLine->Line().Simplify();
+
+ return true;
+}
+
+
+bool PNS_OPTIMIZER::Optimize( PNS_LINE* aLine, int aEffortLevel, PNS_NODE* aWorld )
+{
+ PNS_OPTIMIZER opt( aWorld );
+
+ opt.SetEffortLevel( aEffortLevel );
+ opt.SetCollisionMask( -1 );
+ 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_LINE repl;
+ repl = PNS_LINE( *aLine, l2 );
+
+ if( !m_world->CheckColliding( &repl ) )
+ {
+ aLine->SetShape( repl.CLine() );
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+int findCoupledVertices( const VECTOR2I& aVertex, const SEG& aOrigSeg, const SHAPE_LINE_CHAIN& aCoupled, PNS_DIFF_PAIR* aPair, int* aIndices )
+{
+ int count = 0;
+ for ( int i = 0; i < aCoupled.SegmentCount(); i++ )
+ {
+ SEG s = aCoupled.CSegment( i );
+ VECTOR2I projOverCoupled = s.LineProject ( aVertex );
+
+ if( s.ApproxParallel ( aOrigSeg ) )
+ {
+ int64_t dist = ( projOverCoupled - aVertex ).EuclideanNorm() - aPair->Width();
+
+ if( aPair->GapConstraint().Matches( dist ) )
+ {
+ *aIndices++ = i;
+ count++;
+ }
+ }
+ }
+
+ return count;
+}
+
+
+bool verifyDpBypass( PNS_NODE* aNode, PNS_DIFF_PAIR* aPair, bool aRefIsP, const SHAPE_LINE_CHAIN& aNewRef, const SHAPE_LINE_CHAIN& aNewCoupled )
+{
+ PNS_LINE refLine ( aRefIsP ? aPair->PLine() : aPair->NLine(), aNewRef );
+ PNS_LINE coupledLine ( aRefIsP ? aPair->NLine() : aPair->PLine(), aNewCoupled );
+
+ if( aNode->CheckColliding( &refLine, &coupledLine, PNS_ITEM::ANY, aPair->Gap() - 10 ) )
+ return false;
+
+ if( aNode->CheckColliding ( &refLine ) )
+ return false;
+
+ if( aNode->CheckColliding ( &coupledLine ) )
+ return false;
+
+ return true;
+}
+
+
+bool coupledBypass( PNS_NODE* aNode, PNS_DIFF_PAIR* aPair, bool aRefIsP, const SHAPE_LINE_CHAIN& aRef, const SHAPE_LINE_CHAIN& aRefBypass, const SHAPE_LINE_CHAIN& aCoupled, SHAPE_LINE_CHAIN& aNewCoupled )
+{
+ int vStartIdx[1024]; // fixme: possible overflow
+
+ int nStarts = findCoupledVertices( aRefBypass.CPoint( 0 ), aRefBypass.CSegment( 0 ), aCoupled, aPair, vStartIdx );
+ DIRECTION_45 dir( aRefBypass.CSegment( 0 ) );
+
+ int64_t bestLength = -1;
+ bool found = false;
+ SHAPE_LINE_CHAIN bestBypass;
+ int si, ei;
+
+ for( int i=0; i< nStarts; i++ )
+ {
+ for( int j = 1; j < aCoupled.PointCount() - 1; j++ )
+ {
+ int delta = std::abs ( vStartIdx[i] - j );
+
+ if( delta > 1 )
+ {
+ const VECTOR2I& vs = aCoupled.CPoint( vStartIdx[i] );
+ SHAPE_LINE_CHAIN bypass = dir.BuildInitialTrace( vs, aCoupled.CPoint(j), dir.IsDiagonal() );
+
+ int64_t coupledLength = aPair->CoupledLength( aRef, bypass );
+
+ SHAPE_LINE_CHAIN newCoupled = aCoupled;
+
+ si = vStartIdx[i];
+ ei = j;
+
+ if(si < ei)
+ newCoupled.Replace( si, ei, bypass );
+ else
+ newCoupled.Replace( ei, si, bypass.Reverse() );
+
+ if(coupledLength > bestLength && verifyDpBypass( aNode, aPair, aRefIsP, aRef, newCoupled) )
+ {
+ bestBypass = newCoupled;
+ bestLength = coupledLength;
+ found = true;
+ }
+ }
+ }
+ }
+
+
+ if( found )
+ aNewCoupled = bestBypass;
+
+ return found;
+}
+
+
+bool checkDpColliding( PNS_NODE* aNode, PNS_DIFF_PAIR* aPair, bool aIsP, const SHAPE_LINE_CHAIN& aPath )
+{
+ PNS_LINE tmp ( aIsP ? aPair->PLine() : aPair->NLine(), aPath );
+
+ return static_cast<bool>( aNode->CheckColliding( &tmp ) );
+}
+
+
+bool PNS_OPTIMIZER::mergeDpStep( PNS_DIFF_PAIR* aPair, bool aTryP, int step )
+{
+ int n = 1;
+
+ SHAPE_LINE_CHAIN currentPath = aTryP ? aPair->CP() : aPair->CN();
+ SHAPE_LINE_CHAIN coupledPath = aTryP ? aPair->CN() : aPair->CP();
+
+ int n_segs = currentPath.SegmentCount() - 1;
+
+ int64_t clenPre = aPair->CoupledLength( currentPath, coupledPath );
+ int64_t budget = clenPre / 10; // fixme: come up with somethig more intelligent here...
+
+ while( n < n_segs - step )
+ {
+ const SEG s1 = currentPath.CSegment( n );
+ const SEG s2 = currentPath.CSegment( n + step );
+
+ DIRECTION_45 dir1( s1 );
+ DIRECTION_45 dir2( s2 );
+
+ if( dir1.IsObtuse( dir2 ) )
+ {
+ SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace( s1.A, s2.B, dir1.IsDiagonal() );
+ SHAPE_LINE_CHAIN newRef;
+ SHAPE_LINE_CHAIN newCoup;
+ int64_t deltaCoupled = -1, deltaUni = -1;
+
+ newRef = currentPath;
+ newRef.Replace( s1.Index(), s2.Index(), bypass );
+
+ deltaUni = aPair->CoupledLength ( newRef, coupledPath ) - clenPre + budget;
+
+ if ( coupledBypass( m_world, aPair, aTryP, newRef, bypass, coupledPath, newCoup ) )
+ {
+ deltaCoupled = aPair->CoupledLength( newRef, newCoup ) - clenPre + budget;
+
+ if( deltaCoupled >= 0 )
+ {
+ newRef.Simplify();
+ newCoup.Simplify();
+
+ aPair->SetShape( newRef, newCoup, !aTryP );
+ return true;
+ }
+ }
+ else if( deltaUni >= 0 && verifyDpBypass ( m_world, aPair, aTryP, newRef, coupledPath ) )
+ {
+ newRef.Simplify();
+ coupledPath.Simplify();
+
+ aPair->SetShape( newRef, coupledPath, !aTryP );
+ return true;
+ }
+ }
+
+ n++;
+ }
+
+ return false;
+}
+
+
+bool PNS_OPTIMIZER::mergeDpSegments( PNS_DIFF_PAIR* aPair )
+{
+ int step_p = aPair->CP().SegmentCount() - 2;
+ int step_n = aPair->CN().SegmentCount() - 2;
+
+ while( 1 )
+ {
+ int n_segs_p = aPair->CP().SegmentCount();
+ int n_segs_n = aPair->CN().SegmentCount();
+
+ int max_step_p = n_segs_p - 2;
+ int max_step_n = n_segs_n - 2;
+
+ if( step_p > max_step_p )
+ step_p = max_step_p;
+
+ if( step_n > max_step_n )
+ step_n = max_step_n;
+
+ if( step_p < 1 && step_n < 1)
+ break;
+
+ bool found_anything_p = false;
+ bool found_anything_n = false;
+
+ if( step_p > 1 )
+ found_anything_p = mergeDpStep( aPair, true, step_p );
+
+ if( step_n > 1 )
+ found_anything_n = mergeDpStep( aPair, false, step_n );
+
+ if( !found_anything_n && !found_anything_p )
+ {
+ step_n--;
+ step_p--;
+ }
+ }
+ return true;
+}
+
+
+bool PNS_OPTIMIZER::Optimize( PNS_DIFF_PAIR* aPair )
+{
+ return mergeDpSegments( aPair );
+}
diff --git a/pcbnew/router/pns_optimizer.h b/pcbnew/router/pns_optimizer.h
new file mode 100644
index 0000000..36bced6
--- /dev/null
+++ b/pcbnew/router/pns_optimizer.h
@@ -0,0 +1,181 @@
+/*
+ * 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_OPTIMIZER_H
+#define __PNS_OPTIMIZER_H
+
+#include <boost/unordered_map.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include <geometry/shape_index_list.h>
+#include <geometry/shape_line_chain.h>
+
+#include "range.h"
+
+class PNS_NODE;
+class PNS_ROUTER;
+class PNS_LINE;
+class PNS_DIFF_PAIR;
+
+/**
+ * Class PNS_COST_ESTIMATOR
+ *
+ * Calculates the cost of a given line, taking corner angles and total length into account.
+ **/
+class PNS_COST_ESTIMATOR
+{
+public:
+ PNS_COST_ESTIMATOR() :
+ m_lengthCost( 0 ),
+ m_cornerCost( 0 )
+ {}
+
+ PNS_COST_ESTIMATOR( const PNS_COST_ESTIMATOR& aB ) :
+ m_lengthCost( aB.m_lengthCost ),
+ m_cornerCost( aB.m_cornerCost )
+ {}
+
+ ~PNS_COST_ESTIMATOR() {};
+
+ static int CornerCost( const SEG& aA, const SEG& aB );
+ static int CornerCost( const SHAPE_LINE_CHAIN& aLine );
+ static int CornerCost( const PNS_LINE& aLine );
+
+ void Add( PNS_LINE& aLine );
+ void Remove( PNS_LINE& aLine );
+ void Replace( PNS_LINE& aOldLine, PNS_LINE& aNewLine );
+
+ bool IsBetter( PNS_COST_ESTIMATOR& aOther, double aLengthTolerance,
+ double aCornerTollerace ) const;
+
+ double GetLengthCost() const { return m_lengthCost; }
+ double GetCornerCost() const { return m_cornerCost; }
+
+private:
+ double m_lengthCost;
+ int m_cornerCost;
+};
+
+/**
+ * Class PNS_OPTIMIZER
+ *
+ * Performs various optimizations of the lines being routed, attempting to make the lines shorter
+ * and less cornery. There are 3 kinds of optimizations so far:
+ * - Merging obtuse segments (MERGE_OBTUSE): tries to join together as many
+ * obtuse segments as possible without causing collisions
+ * - Rerouting path between pair of line corners with a 2-segment "\__" line and iteratively repeating
+ * the procedure as long as the total cost of the line keeps decreasing
+ * - "Smart Pads" - that is, rerouting pad/via exits to make them look nice (SMART_PADS).
+ **/
+class PNS_OPTIMIZER
+{
+public:
+ enum OptimizationEffort
+ {
+ MERGE_SEGMENTS = 0x01,
+ SMART_PADS = 0x02,
+ MERGE_OBTUSE = 0x04,
+ FANOUT_CLEANUP = 0x08
+ };
+
+ PNS_OPTIMIZER( PNS_NODE* aWorld );
+ ~PNS_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);
+
+ bool Optimize( PNS_LINE* aLine, PNS_LINE* aResult = NULL );
+ bool Optimize( PNS_DIFF_PAIR* aPair );
+
+
+ void SetWorld( PNS_NODE* aNode ) { m_world = aNode; }
+ void CacheStaticItem( PNS_ITEM* aItem );
+ void CacheRemove( PNS_ITEM* aItem );
+ void ClearCache( bool aStaticOnly = false );
+
+ void SetCollisionMask( int aMask )
+ {
+ m_collisionKindMask = aMask;
+ }
+
+ void SetEffortLevel( int aEffort )
+ {
+ m_effortLevel = aEffort;
+ }
+
+
+ void SetRestrictArea( const BOX2I& aArea )
+ {
+ m_restrictArea = aArea;
+ m_restrictAreaActive = true;
+ }
+
+private:
+ static const int MaxCachedItems = 256;
+
+ typedef std::vector<SHAPE_LINE_CHAIN> BREAKOUT_LIST;
+
+ struct CACHE_VISITOR;
+
+ struct CACHED_ITEM
+ {
+ int m_hits;
+ bool m_isStatic;
+ };
+
+ bool mergeObtuse( PNS_LINE* aLine );
+ bool mergeFull( PNS_LINE* aLine );
+ bool removeUglyCorners( PNS_LINE* aLine );
+ bool runSmartPads( PNS_LINE* aLine );
+ bool mergeStep( PNS_LINE* aLine, SHAPE_LINE_CHAIN& aCurrentLine, int step );
+ bool fanoutCleanup( PNS_LINE * aLine );
+ bool mergeDpSegments( PNS_DIFF_PAIR *aPair );
+ bool mergeDpStep( PNS_DIFF_PAIR *aPair, bool aTryP, int step );
+
+ bool checkColliding( PNS_ITEM* aItem, bool aUpdateCache = true );
+ bool checkColliding( PNS_LINE* aLine, const SHAPE_LINE_CHAIN& aOptPath );
+
+ void cacheAdd( PNS_ITEM* aItem, bool aIsStatic );
+ void removeCachedSegments( PNS_LINE* aLine, int aStartVertex = 0, int aEndVertex = -1 );
+
+ BREAKOUT_LIST circleBreakouts( int aWidth, const SHAPE* aShape, bool aPermitDiagonal ) const;
+ BREAKOUT_LIST rectBreakouts( int aWidth, const SHAPE* aShape, bool aPermitDiagonal ) const;
+ BREAKOUT_LIST ovalBreakouts( int aWidth, const SHAPE* aShape, bool aPermitDiagonal ) const;
+ BREAKOUT_LIST convexBreakouts( int aWidth, const SHAPE* aShape, bool aPermitDiagonal ) const;
+ BREAKOUT_LIST computeBreakouts( int aWidth, const PNS_ITEM* aItem, bool aPermitDiagonal ) const;
+
+ int smartPadsSingle( PNS_LINE* aLine, PNS_ITEM* aPad, bool aEnd, int aEndVertex );
+
+ PNS_ITEM* findPadOrVia( int aLayer, int aNet, const VECTOR2I& aP ) const;
+
+ SHAPE_INDEX_LIST<PNS_ITEM*> m_cache;
+
+ typedef boost::unordered_map<PNS_ITEM*, CACHED_ITEM> CachedItemTags;
+ CachedItemTags m_cacheTags;
+ PNS_NODE* m_world;
+ int m_collisionKindMask;
+ int m_effortLevel;
+ bool m_keepPostures;
+
+ BOX2I m_restrictArea;
+ bool m_restrictAreaActive;
+};
+
+#endif
diff --git a/pcbnew/router/pns_placement_algo.h b/pcbnew/router/pns_placement_algo.h
new file mode 100644
index 0000000..6598d82
--- /dev/null
+++ b/pcbnew/router/pns_placement_algo.h
@@ -0,0 +1,185 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2014 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PNS_PLACEMENT_ALGO_H
+#define __PNS_PLACEMENT_ALGO_H
+
+#include <math/vector2d.h>
+
+#include "pns_algo_base.h"
+#include "pns_sizes_settings.h"
+#include "pns_itemset.h"
+
+class PNS_ROUTER;
+class PNS_ITEM;
+class PNS_NODE;
+
+/**
+ * Class PNS_PLACEMENT_ALGO
+ *
+ * Abstract class for a P&S placement/dragging algorithm.
+ * All subtools (drag, single/diff pair routing and meandering)
+ * are derived from it.
+ */
+
+class PNS_PLACEMENT_ALGO : public PNS_ALGO_BASE
+{
+public:
+ PNS_PLACEMENT_ALGO( PNS_ROUTER* aRouter ) :
+ PNS_ALGO_BASE( aRouter ) {};
+
+ virtual ~PNS_PLACEMENT_ALGO () {};
+
+ /**
+ * Function Start()
+ *
+ * Starts placement/drag operation at point aP, taking item aStartItem as anchor
+ * (unless NULL).
+ */
+ virtual bool Start( const VECTOR2I& aP, PNS_ITEM* aStartItem ) = 0;
+
+ /**
+ * Function Move()
+ *
+ * Moves the end of the currently routed primtive(s) to the point aP, taking
+ * aEndItem as the anchor (if not NULL).
+ * (unless NULL).
+ */
+ virtual bool Move( const VECTOR2I& aP, PNS_ITEM* aEndItem ) = 0;
+
+ /**
+ * Function FixRoute()
+ *
+ * Commits the currently routed items to the parent node, taking
+ * aP as the final end point and aEndItem as the final anchor (if provided).
+ * @return true, if route has been commited. May return false if the routing
+ * result is violating design rules - in such case, the track is only committed
+ * if Settings.CanViolateDRC() is on.
+ */
+ virtual bool FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem ) = 0;
+
+ /**
+ * Function ToggleVia()
+ *
+ * Enables/disables a via at the end of currently routed trace.
+ */
+ virtual bool ToggleVia( bool aEnabled )
+ {
+ return false;
+ }
+
+ /**
+ * Function IsPlacingVia()
+ *
+ * Returns true if the placer is placing a via (or more vias).
+ */
+ virtual bool IsPlacingVia() const
+ {
+ return false;
+ }
+
+ /**
+ * Function SetLayer()
+ *
+ * Sets the current routing layer.
+ */
+ virtual bool SetLayer( int aLayer )
+ {
+ return false;
+ }
+
+ /**
+ * Function Traces()
+ *
+ * Returns all routed/tuned traces.
+ */
+ virtual const PNS_ITEMSET Traces() = 0;
+
+ /**
+ * Function CurrentEnd()
+ *
+ * Returns the current end of the line(s) being placed/tuned. It may not be equal
+ * to the cursor position due to collisions.
+ */
+ virtual const VECTOR2I& CurrentEnd() const = 0;
+
+ /**
+ * Function CurrentNets()
+ *
+ * Returns the net code(s) of currently routed track(s).
+ */
+ virtual const std::vector<int> CurrentNets() const = 0;
+
+ /**
+ * Function CurrentLayer()
+ *
+ * Returns the layer of currently routed track.
+ */
+ virtual int CurrentLayer() const = 0;
+
+ /**
+ * Function CurrentNode()
+ *
+ * Returns the most recent board state.
+ */
+ virtual PNS_NODE* CurrentNode( bool aLoopsRemoved = false ) const = 0;
+
+ /**
+ * Function FlipPosture()
+ *
+ * Toggles the current posture (straight/diagonal) of the trace head.
+ */
+ virtual void FlipPosture()
+ {
+ }
+
+ /**
+ * Function UpdateSizes()
+ *
+ * Performs on-the-fly update of the width, via diameter & drill size from
+ * a settings class. Used to dynamically change these parameters as
+ * the track is routed.
+ */
+ virtual void UpdateSizes( const PNS_SIZES_SETTINGS& aSizes )
+ {
+ }
+
+ /**
+ * Function SetOrthoMode()
+ *
+ * Forces the router to place a straight 90/45 degree trace (with the end
+ * as near to the cursor as possible) instead of a standard 135 degree
+ * two-segment bend.
+ */
+ virtual void SetOrthoMode ( bool aOrthoMode )
+ {
+ }
+
+ /**
+ * Function GetModifiedNets
+ *
+ * Returns the net codes of all currently routed trace(s)
+ */
+ virtual void GetModifiedNets( std::vector<int> &aNets ) const
+ {
+ }
+};
+
+#endif
diff --git a/pcbnew/router/pns_router.cpp b/pcbnew/router/pns_router.cpp
new file mode 100644
index 0000000..6f64f96
--- /dev/null
+++ b/pcbnew/router/pns_router.cpp
@@ -0,0 +1,1084 @@
+/*
+ * 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 <cstdio>
+#include <vector>
+
+#include <boost/foreach.hpp>
+
+#include <view/view.h>
+#include <view/view_item.h>
+#include <view/view_group.h>
+#include <gal/graphics_abstraction_layer.h>
+
+#include <pcb_painter.h>
+
+#include <geometry/shape.h>
+#include <geometry/shape_line_chain.h>
+#include <geometry/shape_rect.h>
+#include <geometry/shape_circle.h>
+
+#include <tools/grid_helper.h>
+
+#include "trace.h"
+#include "pns_node.h"
+#include "pns_line_placer.h"
+#include "pns_line.h"
+#include "pns_solid.h"
+#include "pns_utils.h"
+#include "pns_router.h"
+#include "pns_shove.h"
+#include "pns_dragger.h"
+#include "pns_topology.h"
+#include "pns_diff_pair_placer.h"
+#include "pns_meander_placer.h"
+#include "pns_meander_skew_placer.h"
+#include "pns_dp_meander_placer.h"
+
+#include <router/router_preview_item.h>
+
+#include <class_board.h>
+#include <class_board_connected_item.h>
+#include <class_module.h>
+#include <class_track.h>
+#include <ratsnest_data.h>
+#include <layers_id_colors_and_visibility.h>
+
+// an ugly singleton for drawing debug items within the router context.
+// To be fixed sometime in the future.
+static PNS_ROUTER* theRouter;
+
+
+PNS_PCBNEW_CLEARANCE_FUNC::PNS_PCBNEW_CLEARANCE_FUNC( PNS_ROUTER* aRouter ) :
+ m_router( aRouter )
+{
+ BOARD* brd = m_router->GetBoard();
+ PNS_NODE* world = m_router->GetWorld();
+
+ PNS_TOPOLOGY topo( world );
+ m_clearanceCache.resize( brd->GetNetCount() );
+ m_useDpGap = false;
+
+ for( unsigned int i = 0; i < brd->GetNetCount(); i++ )
+ {
+ NETINFO_ITEM* ni = brd->FindNet( i );
+ if( ni == NULL )
+ continue;
+
+ CLEARANCE_ENT ent;
+ ent.coupledNet = topo.DpCoupledNet( i );
+
+ wxString netClassName = ni->GetClassName();
+ NETCLASSPTR nc = brd->GetDesignSettings().m_NetClasses.Find( netClassName );
+
+ int clearance = nc->GetClearance();
+ ent.clearance = clearance;
+ m_clearanceCache[i] = ent;
+
+ TRACE( 1, "Add net %d netclass %s clearance %d", i % netClassName.mb_str() %
+ clearance );
+ }
+
+ m_overrideEnabled = false;
+ m_defaultClearance = Millimeter2iu( 0.254 ); // aBoard->m_NetClasses.Find ("Default clearance")->GetClearance();
+ m_overrideNetA = 0;
+ m_overrideNetB = 0;
+ m_overrideClearance = 0;
+}
+
+
+PNS_PCBNEW_CLEARANCE_FUNC::~PNS_PCBNEW_CLEARANCE_FUNC()
+{
+}
+
+
+int PNS_PCBNEW_CLEARANCE_FUNC::localPadClearance( const PNS_ITEM* aItem ) const
+{
+ if( !aItem->Parent() || aItem->Parent()->Type() != PCB_PAD_T )
+ return 0;
+
+ const D_PAD* pad = static_cast<D_PAD*>( aItem->Parent() );
+ return pad->GetLocalClearance();
+}
+
+
+int PNS_PCBNEW_CLEARANCE_FUNC::operator()( const PNS_ITEM* aA, const PNS_ITEM* aB )
+{
+ int net_a = aA->Net();
+ int cl_a = ( net_a >= 0 ? m_clearanceCache[net_a].clearance : m_defaultClearance );
+ int net_b = aB->Net();
+ int cl_b = ( net_b >= 0 ? m_clearanceCache[net_b].clearance : m_defaultClearance );
+
+ bool linesOnly = aA->OfKind( PNS_ITEM::SEGMENT | PNS_ITEM::LINE ) && aB->OfKind( PNS_ITEM::SEGMENT | PNS_ITEM::LINE );
+
+ if( net_a == net_b )
+ return 0;
+
+ if( m_useDpGap && linesOnly && net_a >= 0 && net_b >= 0 && m_clearanceCache[net_a].coupledNet == net_b )
+ {
+ cl_a = cl_b = m_router->Sizes().DiffPairGap() - 2 * PNS_HULL_MARGIN;
+ }
+
+ int pad_a = localPadClearance( aA );
+ int pad_b = localPadClearance( aB );
+
+ cl_a = std::max( cl_a, pad_a );
+ cl_b = std::max( cl_b, pad_b );
+
+ return std::max( cl_a, cl_b );
+}
+
+
+// fixme: ugly hack to make the optimizer respect gap width for currently routed differential pair.
+void PNS_PCBNEW_CLEARANCE_FUNC::OverrideClearance( bool aEnable, int aNetA, int aNetB , int aClearance )
+{
+ m_overrideEnabled = aEnable;
+ m_overrideNetA = aNetA;
+ m_overrideNetB = aNetB;
+ m_overrideClearance = aClearance;
+}
+
+
+PNS_ITEM* PNS_ROUTER::syncPad( D_PAD* aPad )
+{
+ PNS_LAYERSET layers( 0, MAX_CU_LAYERS - 1 );
+
+ // ignore non-copper pads
+ if ( (aPad->GetLayerSet() & LSET::AllCuMask()).none() )
+ return NULL;
+
+ switch( aPad->GetAttribute() )
+ {
+ case PAD_ATTRIB_STANDARD:
+ break;
+
+ case PAD_ATTRIB_SMD:
+ case PAD_ATTRIB_HOLE_NOT_PLATED:
+ case PAD_ATTRIB_CONN:
+ {
+ LSET lmsk = aPad->GetLayerSet();
+ bool is_copper = false;
+
+ for( int i = 0; i < MAX_CU_LAYERS; i++ )
+ {
+ if( lmsk[i] )
+ {
+ is_copper = true;
+ if( aPad->GetAttribute() != PAD_ATTRIB_HOLE_NOT_PLATED )
+ layers = PNS_LAYERSET( i );
+ break;
+ }
+ }
+
+ if( !is_copper )
+ return NULL;
+ }
+ break;
+
+ default:
+ TRACE( 0, "unsupported pad type 0x%x", aPad->GetAttribute() );
+ return NULL;
+ }
+
+ PNS_SOLID* solid = new PNS_SOLID;
+
+ solid->SetLayers( layers );
+ solid->SetNet( aPad->GetNetCode() );
+ solid->SetParent( aPad );
+
+ wxPoint wx_c = aPad->ShapePos();
+ wxSize wx_sz = aPad->GetSize();
+ wxPoint offset = aPad->GetOffset();
+
+ VECTOR2I c( wx_c.x, wx_c.y );
+ VECTOR2I sz( wx_sz.x, wx_sz.y );
+
+ RotatePoint( &offset, aPad->GetOrientation() );
+
+ solid->SetPos( VECTOR2I( c.x - offset.x, c.y - offset.y ) );
+ solid->SetOffset ( VECTOR2I ( offset.x, offset.y ) );
+
+ double orient = aPad->GetOrientation() / 10.0;
+
+ if( aPad->GetShape() == PAD_SHAPE_CIRCLE )
+ {
+ solid->SetShape( new SHAPE_CIRCLE( c, sz.x / 2 ) );
+ }
+ else
+ {
+ if( orient == 0.0 || orient == 90.0 || orient == 180.0 || orient == 270.0 )
+ {
+ if( orient == 90.0 || orient == 270.0 )
+ sz = VECTOR2I( sz.y, sz.x );
+
+ switch( aPad->GetShape() )
+ {
+ case PAD_SHAPE_OVAL:
+ if( sz.x == sz.y )
+ solid->SetShape( new SHAPE_CIRCLE( c, sz.x / 2 ) );
+ else
+ {
+ VECTOR2I delta;
+
+ if( sz.x > sz.y )
+ delta = VECTOR2I( ( sz.x - sz.y ) / 2, 0 );
+ else
+ 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_SHAPE_RECT:
+ solid->SetShape( new SHAPE_RECT( c - sz / 2, sz.x, sz.y ) );
+ break;
+
+ case PAD_SHAPE_TRAPEZOID:
+ {
+ wxPoint coords[4];
+ aPad->BuildPadPolygon( coords, wxSize( 0, 0 ), aPad->GetOrientation() );
+ SHAPE_CONVEX* shape = new SHAPE_CONVEX();
+
+ for( int ii = 0; ii < 4; ii++ )
+ {
+ shape->Append( wx_c + coords[ii] );
+ }
+
+ solid->SetShape( shape );
+ break;
+ }
+
+ default:
+ TRACEn( 0, "unsupported pad shape" );
+ delete solid;
+ return NULL;
+ }
+ }
+ else
+ {
+ switch( aPad->GetShape() )
+ {
+ // PAD_SHAPE_CIRCLE already handled above
+
+ case PAD_SHAPE_OVAL:
+ if( sz.x == sz.y )
+ solid->SetShape( new SHAPE_CIRCLE( c, sz.x / 2 ) );
+ else
+ {
+ wxPoint start;
+ wxPoint end;
+ wxPoint corner;
+
+ SHAPE_CONVEX* shape = new SHAPE_CONVEX();
+
+ int w = aPad->BuildSegmentFromOvalShape( start, end, 0.0, wxSize( 0, 0 ) );
+
+ if( start.y == 0 )
+ corner = wxPoint( start.x, -( w / 2 ) );
+ else
+ corner = wxPoint( w / 2, start.y );
+
+ RotatePoint( &start, aPad->GetOrientation() );
+ RotatePoint( &corner, aPad->GetOrientation() );
+ shape->Append( wx_c + corner );
+
+ for( int rot = 100; rot <= 1800; rot += 100 )
+ {
+ wxPoint p( corner );
+ RotatePoint( &p, start, rot );
+ shape->Append( wx_c + p );
+ }
+
+ if( end.y == 0 )
+ corner = wxPoint( end.x, w / 2 );
+ else
+ corner = wxPoint( -( w / 2 ), end.y );
+
+ RotatePoint( &end, aPad->GetOrientation() );
+ RotatePoint( &corner, aPad->GetOrientation() );
+ shape->Append( wx_c + corner );
+
+ for( int rot = 100; rot <= 1800; rot += 100 )
+ {
+ wxPoint p( corner );
+ RotatePoint( &p, end, rot );
+ shape->Append( wx_c + p );
+ }
+
+ solid->SetShape( shape );
+ }
+ break;
+
+ case PAD_SHAPE_RECT:
+ case PAD_SHAPE_TRAPEZOID:
+ {
+ wxPoint coords[4];
+ aPad->BuildPadPolygon( coords, wxSize( 0, 0 ), aPad->GetOrientation() );
+
+ SHAPE_CONVEX* shape = new SHAPE_CONVEX();
+ for( int ii = 0; ii < 4; ii++ )
+ {
+ shape->Append( wx_c + coords[ii] );
+ }
+
+ solid->SetShape( shape );
+ break;
+ }
+
+ default:
+ TRACEn( 0, "unsupported pad shape" );
+ delete solid;
+
+ return NULL;
+ }
+ }
+ }
+ return solid;
+}
+
+
+PNS_ITEM* PNS_ROUTER::syncTrack( TRACK* aTrack )
+{
+ PNS_SEGMENT* s =
+ new PNS_SEGMENT( SEG( aTrack->GetStart(), aTrack->GetEnd() ), aTrack->GetNetCode() );
+
+ s->SetWidth( aTrack->GetWidth() );
+ s->SetLayers( PNS_LAYERSET( aTrack->GetLayer() ) );
+ s->SetParent( aTrack );
+ return s;
+}
+
+
+PNS_ITEM* PNS_ROUTER::syncVia( VIA* aVia )
+{
+ LAYER_ID top, bottom;
+ aVia->LayerPair( &top, &bottom );
+ PNS_VIA* v = new PNS_VIA(
+ aVia->GetPosition(),
+ PNS_LAYERSET( top, bottom ),
+ aVia->GetWidth(),
+ aVia->GetDrillValue(),
+ aVia->GetNetCode(),
+ aVia->GetViaType() );
+
+ v->SetParent( aVia );
+
+ return v;
+}
+
+
+void PNS_ROUTER::SetBoard( BOARD* aBoard )
+{
+ m_board = aBoard;
+ TRACE( 1, "m_board = %p\n", m_board );
+}
+
+
+void PNS_ROUTER::SyncWorld()
+{
+ if( !m_board )
+ {
+ TRACEn( 0, "No board attached, aborting sync." );
+ return;
+ }
+
+ ClearWorld();
+
+ m_world = new PNS_NODE();
+
+ for( MODULE* module = m_board->m_Modules; module; module = module->Next() )
+ {
+ for( D_PAD* pad = module->Pads(); pad; pad = pad->Next() )
+ {
+ PNS_ITEM* solid = syncPad( pad );
+
+ if( solid )
+ m_world->Add( solid );
+ }
+ }
+
+ for( TRACK* t = m_board->m_Track; t; t = t->Next() )
+ {
+ KICAD_T type = t->Type();
+ PNS_ITEM* item = NULL;
+
+ if( type == PCB_TRACE_T )
+ item = syncTrack( t );
+ else if( type == PCB_VIA_T )
+ item = syncVia( static_cast<VIA*>( t ) );
+
+ if( item )
+ m_world->Add( item );
+ }
+
+ int worstClearance = m_board->GetDesignSettings().GetBiggestClearanceValue();
+ m_clearanceFunc = new PNS_PCBNEW_CLEARANCE_FUNC( this );
+ m_world->SetClearanceFunctor( m_clearanceFunc );
+ m_world->SetMaxClearance( 4 * worstClearance );
+}
+
+
+PNS_ROUTER::PNS_ROUTER()
+{
+ theRouter = this;
+
+ m_clearanceFunc = NULL;
+
+ m_state = IDLE;
+ m_world = NULL;
+ m_placer = NULL;
+ m_previewItems = NULL;
+ m_board = NULL;
+ m_dragger = NULL;
+ m_mode = PNS_MODE_ROUTE_SINGLE;
+
+ // Initialize all other variables:
+ m_lastNode = NULL;
+ m_shove = NULL;
+ m_iterLimit = 0;
+ m_showInterSteps = false;
+ m_snapshotIter = 0;
+ m_view = NULL;
+ m_snappingEnabled = false;
+ m_violation = false;
+ m_gridHelper = NULL;
+
+}
+
+
+void PNS_ROUTER::SetView( KIGFX::VIEW* aView )
+{
+ if( m_previewItems )
+ {
+ m_previewItems->FreeItems();
+ delete m_previewItems;
+ }
+
+ m_view = aView;
+ m_previewItems = new KIGFX::VIEW_GROUP( m_view );
+ m_previewItems->SetLayer( ITEM_GAL_LAYER( GP_OVERLAY ) );
+ m_view->Add( m_previewItems );
+ m_previewItems->ViewSetVisible( true );
+}
+
+
+PNS_ROUTER* PNS_ROUTER::GetInstance()
+{
+ return theRouter;
+}
+
+
+PNS_ROUTER::~PNS_ROUTER()
+{
+ ClearWorld();
+ theRouter = NULL;
+
+ if( m_previewItems )
+ delete m_previewItems;
+}
+
+
+void PNS_ROUTER::ClearWorld()
+{
+ if( m_world )
+ {
+ m_world->KillChildren();
+ delete m_world;
+ }
+
+ if( m_clearanceFunc )
+ delete m_clearanceFunc;
+
+ if( m_placer )
+ delete m_placer;
+
+ if( m_previewItems )
+ delete m_previewItems;
+
+ m_clearanceFunc = NULL;
+ m_world = NULL;
+ m_placer = NULL;
+ m_previewItems = NULL;
+}
+
+
+bool PNS_ROUTER::RoutingInProgress() const
+{
+ return m_state != IDLE;
+}
+
+
+const PNS_ITEMSET PNS_ROUTER::QueryHoverItems( const VECTOR2I& aP )
+{
+ if( m_state == IDLE )
+ return m_world->HitTest( aP );
+ else
+ {
+ return m_placer->CurrentNode()->HitTest( aP );
+ }
+}
+
+
+const VECTOR2I PNS_ROUTER::SnapToItem( PNS_ITEM* aItem, VECTOR2I aP, bool& aSplitsSegment )
+{
+ VECTOR2I anchor;
+
+ if( !aItem )
+ {
+ aSplitsSegment = false;
+ return aP;
+ }
+
+ switch( aItem->Kind() )
+ {
+ case PNS_ITEM::SOLID:
+ anchor = static_cast<PNS_SOLID*>( aItem )->Pos();
+ aSplitsSegment = false;
+ break;
+
+ case PNS_ITEM::VIA:
+ anchor = static_cast<PNS_VIA*>( aItem )->Pos();
+ aSplitsSegment = false;
+ break;
+
+ case PNS_ITEM::SEGMENT:
+ {
+ PNS_SEGMENT* seg = static_cast<PNS_SEGMENT*>( aItem );
+ const SEG& s = seg->Seg();
+ int w = seg->Width();
+
+ aSplitsSegment = false;
+
+ if( ( aP - s.A ).EuclideanNorm() < w / 2 )
+ anchor = s.A;
+ else if( ( aP - s.B ).EuclideanNorm() < w / 2 )
+ anchor = s.B;
+ else
+ {
+ anchor = m_gridHelper->AlignToSegment ( aP, s );
+ aSplitsSegment = (anchor != s.A && anchor != s.B );
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ 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;
+}
+
+bool PNS_ROUTER::StartRouting( const VECTOR2I& aP, PNS_ITEM* aStartItem, int aLayer )
+{
+ m_clearanceFunc->UseDpGap( false );
+
+ switch( m_mode )
+ {
+ case PNS_MODE_ROUTE_SINGLE:
+ m_placer = new PNS_LINE_PLACER( this );
+ break;
+ case PNS_MODE_ROUTE_DIFF_PAIR:
+ m_placer = new PNS_DIFF_PAIR_PLACER( this );
+ m_clearanceFunc->UseDpGap( true );
+ break;
+ case PNS_MODE_TUNE_SINGLE:
+ m_placer = new PNS_MEANDER_PLACER( this );
+ break;
+ case PNS_MODE_TUNE_DIFF_PAIR:
+ m_placer = new PNS_DP_MEANDER_PLACER( this );
+ break;
+ case PNS_MODE_TUNE_DIFF_PAIR_SKEW:
+ m_placer = new PNS_MEANDER_SKEW_PLACER( this );
+ break;
+
+ default:
+ return false;
+ }
+
+ m_placer->UpdateSizes ( m_sizes );
+ m_placer->SetLayer( aLayer );
+
+ bool rv = m_placer->Start( aP, aStartItem );
+
+ if( !rv )
+ return false;
+
+ m_currentEnd = aP;
+ m_state = ROUTE_TRACK;
+ return rv;
+}
+
+
+BOARD* PNS_ROUTER::GetBoard()
+{
+ return m_board;
+}
+
+
+void PNS_ROUTER::eraseView()
+{
+ BOOST_FOREACH( BOARD_ITEM* item, m_hiddenItems )
+ {
+ item->ViewSetVisible( true );
+ }
+
+ m_hiddenItems.clear();
+
+ if( m_previewItems )
+ {
+ m_previewItems->FreeItems();
+ m_previewItems->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+}
+
+
+void PNS_ROUTER::DisplayItem( const PNS_ITEM* aItem, int aColor, int aClearance )
+{
+ ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( aItem, m_previewItems );
+
+ if( aColor >= 0 )
+ pitem->SetColor( KIGFX::COLOR4D( aColor ) );
+
+ if( aClearance >= 0 )
+ pitem->SetClearance( aClearance );
+
+ m_previewItems->Add( pitem );
+
+ pitem->ViewSetVisible( true );
+ 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 )
+{
+ ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( NULL, m_previewItems );
+
+ pitem->Line( aLine, aWidth, aType );
+ m_previewItems->Add( pitem );
+ pitem->ViewSetVisible( true );
+ m_previewItems->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY | KIGFX::VIEW_ITEM::APPEARANCE );
+}
+
+
+void PNS_ROUTER::DisplayDebugPoint( const VECTOR2I aPos, int aType )
+{
+ ROUTER_PREVIEW_ITEM* pitem = new ROUTER_PREVIEW_ITEM( NULL, m_previewItems );
+
+ pitem->Point( aPos, 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 )
+{
+ m_currentEnd = aP;
+
+ switch( m_state )
+ {
+ case ROUTE_TRACK:
+ movePlacing( aP, endItem );
+ break;
+
+ case DRAG_SEGMENT:
+ moveDragging( aP, endItem );
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+void PNS_ROUTER::moveDragging( const VECTOR2I& aP, PNS_ITEM* aEndItem )
+{
+ eraseView();
+
+ m_dragger->Drag( aP );
+ PNS_ITEMSET dragged = m_dragger->Traces();
+
+ updateView( m_dragger->CurrentNode(), dragged );
+}
+
+
+void PNS_ROUTER::markViolations( PNS_NODE* aNode, PNS_ITEMSET& aCurrent,
+ PNS_NODE::ITEM_VECTOR& aRemoved )
+{
+ BOOST_FOREACH( PNS_ITEM* item, aCurrent.Items() )
+ {
+ PNS_NODE::OBSTACLES obstacles;
+
+ aNode->QueryColliding( item, obstacles, PNS_ITEM::ANY );
+
+ 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 );
+ }
+ }
+
+ BOOST_FOREACH( PNS_OBSTACLE& obs, obstacles )
+ {
+ int clearance = aNode->GetClearance( item, obs.m_item );
+ std::auto_ptr<PNS_ITEM> tmp( obs.m_item->Clone() );
+ tmp->Mark( MK_VIOLATION );
+ DisplayItem( tmp.get(), -1, clearance );
+ aRemoved.push_back( obs.m_item );
+ }
+ }
+}
+
+
+void PNS_ROUTER::updateView( PNS_NODE* aNode, PNS_ITEMSET& aCurrent )
+{
+ PNS_NODE::ITEM_VECTOR 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 )
+ {
+ DisplayItem( item );
+ }
+
+ BOOST_FOREACH( PNS_ITEM* item, removed )
+ {
+ BOARD_CONNECTED_ITEM* parent = item->Parent();
+
+ if( parent )
+ {
+ if( parent->ViewIsVisible() )
+ m_hiddenItems.insert( parent );
+
+ parent->ViewSetVisible( false );
+ parent->ViewUpdate( KIGFX::VIEW_ITEM::APPEARANCE );
+ }
+ }
+}
+
+
+void PNS_ROUTER::UpdateSizes ( const PNS_SIZES_SETTINGS& aSizes )
+{
+ m_sizes = aSizes;
+
+ // Change track/via size settings
+ if( m_state == ROUTE_TRACK)
+ {
+ m_placer->UpdateSizes( m_sizes );
+ }
+}
+
+
+void PNS_ROUTER::movePlacing( const VECTOR2I& aP, PNS_ITEM* aEndItem )
+{
+ eraseView();
+
+ m_placer->Move( aP, aEndItem );
+ PNS_ITEMSET current = m_placer->Traces();
+
+ BOOST_FOREACH( const PNS_ITEM* item, current.CItems() )
+ {
+ if( !item->OfKind( PNS_ITEM::LINE ) )
+ continue;
+
+ const PNS_LINE* l = static_cast<const PNS_LINE*>( item );
+ DisplayItem( l );
+
+ if( l->EndsWithVia() )
+ DisplayItem( &l->Via() );
+ }
+
+ //PNS_ITEMSET tmp( &current );
+
+ updateView( m_placer->CurrentNode( true ), current );
+}
+
+
+void PNS_ROUTER::CommitRouting( PNS_NODE* aNode )
+{
+ PNS_NODE::ITEM_VECTOR removed, added;
+
+ aNode->GetUpdatedItems( removed, added );
+
+ for( unsigned int i = 0; i < removed.size(); i++ )
+ {
+ BOARD_CONNECTED_ITEM* parent = removed[i]->Parent();
+
+ if( parent )
+ {
+ m_view->Remove( parent );
+ m_board->Remove( parent );
+ m_undoBuffer.PushItem( ITEM_PICKER( parent, UR_DELETED ) );
+ }
+ }
+
+ BOOST_FOREACH( PNS_ITEM* item, added )
+ {
+ BOARD_CONNECTED_ITEM* newBI = NULL;
+
+ switch( item->Kind() )
+ {
+ case PNS_ITEM::SEGMENT:
+ {
+ PNS_SEGMENT* seg = static_cast<PNS_SEGMENT*>( item );
+ TRACK* track = new TRACK( m_board );
+ const SEG& s = seg->Seg();
+
+ track->SetStart( wxPoint( s.A.x, s.A.y ) );
+ track->SetEnd( wxPoint( s.B.x, s.B.y ) );
+ track->SetWidth( seg->Width() );
+ track->SetLayer( ToLAYER_ID( seg->Layers().Start() ) );
+ track->SetNetCode( seg->Net() > 0 ? seg->Net() : 0 );
+ newBI = track;
+ break;
+ }
+
+ case PNS_ITEM::VIA:
+ {
+ VIA* via_board = new VIA( m_board );
+ PNS_VIA* via = static_cast<PNS_VIA*>( item );
+ via_board->SetPosition( wxPoint( via->Pos().x, via->Pos().y ) );
+ via_board->SetWidth( via->Diameter() );
+ via_board->SetDrill( via->Drill() );
+ via_board->SetNetCode( via->Net() > 0 ? via->Net() : 0 );
+ via_board->SetViaType( via->ViaType() ); // MUST be before SetLayerPair()
+ via_board->SetLayerPair( ToLAYER_ID( via->Layers().Start() ),
+ ToLAYER_ID( via->Layers().End() ) );
+ newBI = via_board;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if( newBI )
+ {
+ item->SetParent( newBI );
+ newBI->ClearFlags();
+ m_view->Add( newBI );
+ m_board->Add( newBI );
+ m_undoBuffer.PushItem( ITEM_PICKER( newBI, UR_NEW ) );
+ newBI->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
+ }
+ }
+
+ m_board->GetRatsnest()->Recalculate();
+ m_world->Commit( aNode );
+}
+
+
+bool PNS_ROUTER::FixRoute( const VECTOR2I& aP, PNS_ITEM* aEndItem )
+{
+ bool rv = false;
+
+ switch( m_state )
+ {
+ case ROUTE_TRACK:
+ rv = m_placer->FixRoute( aP, aEndItem );
+ break;
+
+ case DRAG_SEGMENT:
+ rv = m_dragger->FixRoute();
+ break;
+
+ default:
+ break;
+ }
+
+ if( rv )
+ StopRouting();
+
+ return rv;
+}
+
+
+void PNS_ROUTER::StopRouting()
+{
+ // Update the ratsnest with new changes
+
+ if( m_placer )
+ {
+ std::vector<int> nets;
+ m_placer->GetModifiedNets( nets );
+
+ BOOST_FOREACH ( int n, nets )
+ {
+ // Update the ratsnest with new changes
+ m_board->GetRatsnest()->Recalculate( n );
+ }
+ }
+
+ if( !RoutingInProgress() )
+ return;
+
+ if( m_placer )
+ delete m_placer;
+
+ if( m_dragger )
+ delete m_dragger;
+
+ m_placer = NULL;
+ m_dragger = NULL;
+
+ eraseView();
+
+ m_state = IDLE;
+ m_world->KillChildren();
+ m_world->ClearRanks();
+}
+
+
+void PNS_ROUTER::FlipPosture()
+{
+ if( m_state == ROUTE_TRACK )
+ {
+ m_placer->FlipPosture();
+ }
+}
+
+
+void PNS_ROUTER::SwitchLayer( int aLayer )
+{
+ switch( m_state )
+ {
+ case ROUTE_TRACK:
+ m_placer->SetLayer( aLayer );
+ break;
+ default:
+ break;
+ }
+}
+
+
+void PNS_ROUTER::ToggleViaPlacement()
+{
+ if( m_state == ROUTE_TRACK )
+ {
+ bool toggle = !m_placer->IsPlacingVia();
+ m_placer->ToggleVia( toggle );
+ }
+}
+
+
+const std::vector<int> PNS_ROUTER::GetCurrentNets() const
+{
+ if( m_placer )
+ return m_placer->CurrentNets();
+
+ return std::vector<int>();
+}
+
+
+int PNS_ROUTER::GetCurrentLayer() const
+{
+ if( m_placer )
+ return m_placer->CurrentLayer();
+ return -1;
+}
+
+
+void PNS_ROUTER::DumpLog()
+{
+ PNS_LOGGER* logger = NULL;
+
+ switch( m_state )
+ {
+ case DRAG_SEGMENT:
+ logger = m_dragger->Logger();
+ break;
+
+ case ROUTE_TRACK:
+ logger = m_placer->Logger();
+ break;
+
+ default:
+ break;
+ }
+
+ if( logger )
+ logger->Save( "/tmp/shove.log" );
+}
+
+
+bool PNS_ROUTER::IsPlacingVia() const
+{
+ if( !m_placer )
+ return false;
+
+ return m_placer->IsPlacingVia();
+}
+
+
+void PNS_ROUTER::SetOrthoMode( bool aEnable )
+{
+ if( !m_placer )
+ return;
+
+ m_placer->SetOrthoMode( aEnable );
+}
+
+
+void PNS_ROUTER::SetMode( PNS_ROUTER_MODE aMode )
+{
+ m_mode = aMode;
+}
diff --git a/pcbnew/router/pns_router.h b/pcbnew/router/pns_router.h
new file mode 100644
index 0000000..4bc4bc3
--- /dev/null
+++ b/pcbnew/router/pns_router.h
@@ -0,0 +1,285 @@
+/*
+ * 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_ROUTER_H
+#define __PNS_ROUTER_H
+
+#include <list>
+
+#include <boost/optional.hpp>
+#include <boost/unordered_set.hpp>
+
+#include <geometry/shape_line_chain.h>
+#include <class_undoredo_container.h>
+
+#include "pns_routing_settings.h"
+#include "pns_sizes_settings.h"
+#include "pns_item.h"
+#include "pns_itemset.h"
+#include "pns_node.h"
+
+class BOARD;
+class BOARD_ITEM;
+class D_PAD;
+class TRACK;
+class VIA;
+class GRID_HELPER;
+class PNS_NODE;
+class PNS_DIFF_PAIR_PLACER;
+class PNS_PLACEMENT_ALGO;
+class PNS_LINE_PLACER;
+class PNS_ITEM;
+class PNS_LINE;
+class PNS_SOLID;
+class PNS_SEGMENT;
+class PNS_JOINT;
+class PNS_VIA;
+class PNS_CLEARANCE_FUNC;
+class PNS_SHOVE;
+class PNS_DRAGGER;
+
+namespace KIGFX
+{
+ class VIEW;
+ class VIEW_GROUP;
+};
+
+
+enum PNS_ROUTER_MODE {
+ PNS_MODE_ROUTE_SINGLE = 1,
+ PNS_MODE_ROUTE_DIFF_PAIR,
+ PNS_MODE_TUNE_SINGLE,
+ PNS_MODE_TUNE_DIFF_PAIR,
+ PNS_MODE_TUNE_DIFF_PAIR_SKEW
+};
+
+/**
+ * Class PNS_ROUTER
+ *
+ * Main router class.
+ */
+class PNS_ROUTER
+{
+private:
+ enum RouterState
+ {
+ IDLE,
+ DRAG_SEGMENT,
+ ROUTE_TRACK
+ };
+
+public:
+ PNS_ROUTER();
+ ~PNS_ROUTER();
+
+ void SetMode ( PNS_ROUTER_MODE aMode );
+ PNS_ROUTER_MODE Mode() const { return m_mode; }
+
+ static PNS_ROUTER* GetInstance();
+
+ void ClearWorld();
+ void SetBoard( BOARD* aBoard );
+ void SyncWorld();
+
+ void SetView( KIGFX::VIEW* aView );
+
+ bool RoutingInProgress() const;
+ bool StartRouting( const VECTOR2I& aP, PNS_ITEM* aItem, int aLayer );
+ void Move( const VECTOR2I& aP, PNS_ITEM* aItem );
+ bool FixRoute( const VECTOR2I& aP, PNS_ITEM* aItem );
+
+ void StopRouting();
+
+ int GetClearance( const PNS_ITEM* aA, const PNS_ITEM* aB ) const;
+
+ PNS_NODE* GetWorld() const
+ {
+ return m_world;
+ }
+
+ void FlipPosture();
+
+ 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 DisplayDebugPoint( const VECTOR2I aPos, int aType = 0 );
+ void DisplayDebugBox( const BOX2I& aBox, int aType = 0, int aWidth = 0 );
+
+ void SwitchLayer( int layer );
+
+ void ToggleViaPlacement();
+ void SetOrthoMode ( bool aEnable );
+
+ int GetCurrentLayer() const;
+ const std::vector<int> GetCurrentNets() const;
+
+ void DumpLog();
+
+ PNS_CLEARANCE_FUNC* GetClearanceFunc() const
+ {
+ return m_clearanceFunc;
+ }
+ bool IsPlacingVia() const;
+
+ const PNS_ITEMSET QueryHoverItems( const VECTOR2I& aP );
+ const VECTOR2I SnapToItem( PNS_ITEM* aItem, 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()
+ * was called or a new track has been started).
+ */
+ const PICKED_ITEMS_LIST& GetUndoBuffer() const
+ {
+ return m_undoBuffer;
+ }
+
+ /**
+ * Clears the list of recent changes, saved to be stored in the undo buffer.
+ */
+ void ClearUndoBuffer()
+ {
+ m_undoBuffer.ClearItemsList();
+ }
+
+ /**
+ * Applies stored settings.
+ * @see Settings()
+ */
+ void UpdateSizes( const PNS_SIZES_SETTINGS& aSizes );
+
+ /**
+ * Changes routing settings to ones passed in the parameter.
+ * @param aSettings are the new settings.
+ */
+ void LoadSettings( const PNS_ROUTING_SETTINGS& aSettings )
+ {
+ m_settings = aSettings;
+ }
+
+ void EnableSnapping( bool aEnable )
+ {
+ m_snappingEnabled = aEnable;
+ }
+
+ bool SnappingEnabled() const
+ {
+ return m_snappingEnabled;
+ }
+
+ PNS_SIZES_SETTINGS& Sizes()
+ {
+ return m_sizes;
+ }
+
+ PNS_ITEM *QueryItemByParent ( const BOARD_ITEM *aItem ) const;
+
+ BOARD *GetBoard();
+
+ void SetFailureReason ( const wxString& aReason ) { m_failureReason = aReason; }
+ const wxString& FailureReason() const { return m_failureReason; }
+
+ PNS_PLACEMENT_ALGO *Placer() { return m_placer; }
+
+ void SetGrid( GRID_HELPER *aGridHelper )
+ {
+ m_gridHelper = aGridHelper;
+ }
+
+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 );
+
+ void clearViewFlags();
+
+ // optHoverItem queryHoverItemEx(const VECTOR2I& aP);
+
+ PNS_ITEM* pickSingleItem( PNS_ITEMSET& aItems ) const;
+ void splitAdjacentSegments( PNS_NODE* aNode, PNS_ITEM* aSeg, const VECTOR2I& aP );
+
+ PNS_ITEM* syncPad( D_PAD* aPad );
+ PNS_ITEM* syncTrack( TRACK* aTrack );
+ PNS_ITEM* syncVia( VIA* aVia );
+
+ void commitPad( PNS_SOLID* aPad );
+ void commitSegment( PNS_SEGMENT* aTrack );
+ void commitVia( PNS_VIA* aVia );
+
+ void highlightCurrent( bool enabled );
+
+ void markViolations( PNS_NODE* aNode, PNS_ITEMSET& aCurrent, PNS_NODE::ITEM_VECTOR& aRemoved );
+
+ VECTOR2I m_currentEnd;
+ RouterState m_state;
+
+ BOARD* m_board;
+ PNS_NODE* m_world;
+ PNS_NODE* m_lastNode;
+ PNS_PLACEMENT_ALGO * m_placer;
+ PNS_DRAGGER* m_dragger;
+ PNS_SHOVE* m_shove;
+ int m_iterLimit;
+ bool m_showInterSteps;
+ int m_snapshotIter;
+
+ KIGFX::VIEW* m_view;
+ KIGFX::VIEW_GROUP* m_previewItems;
+
+ bool m_snappingEnabled;
+ bool m_violation;
+
+ PNS_ROUTING_SETTINGS m_settings;
+ PNS_PCBNEW_CLEARANCE_FUNC* m_clearanceFunc;
+
+ boost::unordered_set<BOARD_CONNECTED_ITEM*> m_hiddenItems;
+
+ ///> Stores list of modified items in the current operation
+ PICKED_ITEMS_LIST m_undoBuffer;
+ PNS_SIZES_SETTINGS m_sizes;
+ PNS_ROUTER_MODE m_mode;
+
+ wxString m_toolStatusbarName;
+ wxString m_failureReason;
+
+ GRID_HELPER *m_gridHelper;
+};
+
+#endif
diff --git a/pcbnew/router/pns_routing_settings.cpp b/pcbnew/router/pns_routing_settings.cpp
new file mode 100644
index 0000000..4ff25e3
--- /dev/null
+++ b/pcbnew/router/pns_routing_settings.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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 <tool/tool_settings.h>
+
+#include "pns_routing_settings.h"
+#include "direction.h"
+
+PNS_ROUTING_SETTINGS::PNS_ROUTING_SETTINGS()
+{
+ m_routingMode = RM_Walkaround;
+ m_optimizerEffort = OE_MEDIUM;
+ 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;
+ m_freeAngleMode = false;
+ m_inlineDragEnabled = false;
+}
+
+
+void PNS_ROUTING_SETTINGS::Save( TOOL_SETTINGS& aSettings ) const
+{
+ aSettings.Set( "Mode", (int) m_routingMode );
+ aSettings.Set( "OptimizerEffort", (int) m_optimizerEffort );
+ aSettings.Set( "RemoveLoops", m_removeLoops );
+ aSettings.Set( "SmartPads", m_smartPads );
+ aSettings.Set( "ShoveVias", m_shoveVias );
+ aSettings.Set( "StartDiagonal", m_startDiagonal );
+ aSettings.Set( "ShoveTimeLimit", m_shoveTimeLimit.Get() );
+ aSettings.Set( "ShoveIterationLimit", m_shoveIterationLimit );
+ aSettings.Set( "WalkaroundIterationLimit", m_walkaroundIterationLimit );
+ aSettings.Set( "JumpOverObstacles", m_jumpOverObstacles );
+ aSettings.Set( "SmoothDraggedSegments", m_smoothDraggedSegments );
+ aSettings.Set( "CanViolateDRC", m_canViolateDRC );
+ aSettings.Set( "SuggestFinish", m_suggestFinish );
+ aSettings.Set( "FreeAngleMode", m_freeAngleMode );
+ aSettings.Set( "InlineDragEnabled", m_inlineDragEnabled );
+}
+
+
+void PNS_ROUTING_SETTINGS::Load( const TOOL_SETTINGS& aSettings )
+{
+ m_routingMode = (PNS_MODE) aSettings.Get( "Mode", (int) RM_Walkaround );
+ m_optimizerEffort = (PNS_OPTIMIZATION_EFFORT) aSettings.Get( "OptimizerEffort", (int) OE_MEDIUM );
+ m_removeLoops = aSettings.Get( "RemoveLoops", true );
+ m_smartPads = aSettings.Get( "SmartPads", true );
+ m_shoveVias = aSettings.Get( "ShoveVias", true );
+ m_startDiagonal = aSettings.Get( "StartDiagonal", false );
+ m_shoveTimeLimit.Set( aSettings.Get( "ShoveTimeLimit", 1000 ) );
+ m_shoveIterationLimit = aSettings.Get( "ShoveIterationLimit", 250 );
+ m_walkaroundIterationLimit = aSettings.Get( "WalkaroundIterationLimit", 50 );
+ m_jumpOverObstacles = aSettings.Get( "JumpOverObstacles", false );
+ m_smoothDraggedSegments = aSettings.Get( "SmoothDraggedSegments", true );
+ m_canViolateDRC = aSettings.Get( "CanViolateDRC", false );
+ m_suggestFinish = aSettings.Get( "SuggestFinish", false );
+ m_freeAngleMode = aSettings.Get( "FreeAngleMode", false );
+ m_inlineDragEnabled = aSettings.Get( "InlineDragEnabled", false );
+}
+
+
+const DIRECTION_45 PNS_ROUTING_SETTINGS::InitialDirection() const
+{
+ if( m_startDiagonal )
+ return DIRECTION_45( DIRECTION_45::NE );
+ else
+ return DIRECTION_45( DIRECTION_45::N );
+}
+
+
+TIME_LIMIT PNS_ROUTING_SETTINGS::ShoveTimeLimit() const
+{
+ return TIME_LIMIT ( m_shoveTimeLimit );
+}
+
+
+int PNS_ROUTING_SETTINGS::ShoveIterationLimit() const
+{
+ return m_shoveIterationLimit;
+}
diff --git a/pcbnew/router/pns_routing_settings.h b/pcbnew/router/pns_routing_settings.h
new file mode 100644
index 0000000..f390045
--- /dev/null
+++ b/pcbnew/router/pns_routing_settings.h
@@ -0,0 +1,158 @@
+/*
+ * 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_ROUTING_SETTINGS
+#define __PNS_ROUTING_SETTINGS
+
+#include <cstdio>
+
+#include "time_limit.h"
+
+class DIRECTION_45;
+class TOOL_SETTINGS;
+
+///> Routing modes
+enum PNS_MODE
+{
+ RM_MarkObstacles = 0, ///> Ignore collisions, mark obstacles
+ RM_Shove, ///> Only shove
+ RM_Walkaround, ///> Only walkaround
+ 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
+{
+public:
+ PNS_ROUTING_SETTINGS();
+
+ void Load( const TOOL_SETTINGS& where );
+ void Save( TOOL_SETTINGS& where ) const;
+
+ ///> 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; }
+
+ bool GetFreeAngleMode() const { return m_freeAngleMode; }
+
+ void SetFreeAngleMode( bool aEnable ) { m_freeAngleMode = aEnable; }
+
+ const DIRECTION_45 InitialDirection() const;
+
+ int ShoveIterationLimit() const;
+ TIME_LIMIT ShoveTimeLimit() const;
+
+ int WalkaroundIterationLimit() const { return m_walkaroundIterationLimit; };
+ TIME_LIMIT WalkaroundTimeLimit() const;
+
+ void SetInlineDragEnabled ( bool aEnable ) { m_inlineDragEnabled = aEnable; }
+ bool InlineDragEnabled( ) const { return m_inlineDragEnabled; }
+
+private:
+ bool m_shoveVias;
+ bool m_startDiagonal;
+ bool m_removeLoops;
+ bool m_smartPads;
+ bool m_suggestFinish;
+ bool m_followMouse;
+ bool m_jumpOverObstacles;
+ bool m_smoothDraggedSegments;
+ bool m_canViolateDRC;
+ bool m_freeAngleMode;
+ bool m_inlineDragEnabled;
+
+ PNS_MODE m_routingMode;
+ PNS_OPTIMIZATION_EFFORT m_optimizerEffort;
+
+ int m_walkaroundIterationLimit;
+ int m_shoveIterationLimit;
+ TIME_LIMIT m_shoveTimeLimit;
+ TIME_LIMIT m_walkaroundTimeLimit;
+};
+
+#endif
diff --git a/pcbnew/router/pns_segment.h b/pcbnew/router/pns_segment.h
new file mode 100644
index 0000000..dbcb165
--- /dev/null
+++ b/pcbnew/router/pns_segment.h
@@ -0,0 +1,130 @@
+/*
+ * 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_SEGMENT_H
+#define __PNS_SEGMENT_H
+
+#include <math/vector2d.h>
+
+#include <geometry/seg.h>
+#include <geometry/shape_segment.h>
+#include <geometry/shape_line_chain.h>
+
+#include "pns_item.h"
+#include "pns_line.h"
+
+class PNS_NODE;
+
+class PNS_SEGMENT : public PNS_ITEM
+{
+public:
+ PNS_SEGMENT() :
+ PNS_ITEM( SEGMENT )
+ {}
+
+ PNS_SEGMENT( const SEG& aSeg, int aNet ) :
+ PNS_ITEM( SEGMENT ), m_seg( aSeg, 0 )
+ {
+ m_net = aNet;
+ }
+
+ PNS_SEGMENT( const PNS_LINE& aParentLine, const SEG& aSeg ) :
+ PNS_ITEM( SEGMENT ),
+ m_seg( aSeg, aParentLine.Width() )
+ {
+ m_net = aParentLine.Net();
+ m_layers = aParentLine.Layers();
+ m_marker = aParentLine.Marker();
+ m_rank = aParentLine.Rank();
+ }
+
+ static inline bool ClassOf( const PNS_ITEM* aItem )
+ {
+ return aItem && SEGMENT == aItem->Kind();
+ }
+
+ PNS_SEGMENT* Clone() const;
+
+ const SHAPE* Shape() const
+ {
+ return static_cast<const SHAPE*>( &m_seg );
+ }
+
+ void SetLayer( int aLayer )
+ {
+ SetLayers( PNS_LAYERSET( aLayer ) );
+ }
+
+ int Layer() const
+ {
+ return Layers().Start();
+ }
+
+ void SetWidth( int aWidth )
+ {
+ m_seg.SetWidth(aWidth);
+ }
+
+ int Width() const
+ {
+ return m_seg.GetWidth();
+ }
+
+ const SEG& Seg() const
+ {
+ return m_seg.GetSeg();
+ }
+
+ const SHAPE_LINE_CHAIN CLine() const
+ {
+ return SHAPE_LINE_CHAIN( m_seg.GetSeg().A, m_seg.GetSeg().B );
+ }
+
+ void SetEnds( const VECTOR2I& a, const VECTOR2I& b )
+ {
+ m_seg.SetSeg( SEG ( a, b ) );
+ }
+
+ void SwapEnds()
+ {
+ SEG tmp = m_seg.GetSeg();
+ m_seg.SetSeg( SEG (tmp.B , tmp.A ) );
+ }
+
+ 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:
+ SHAPE_SEGMENT m_seg;
+};
+
+#endif
diff --git a/pcbnew/router/pns_shove.cpp b/pcbnew/router/pns_shove.cpp
new file mode 100644
index 0000000..3e72443
--- /dev/null
+++ b/pcbnew/router/pns_shove.cpp
@@ -0,0 +1,1375 @@
+/*
+ * 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/>.
+ */
+
+#define PNS_DEBUG
+
+#include <deque>
+#include <cassert>
+
+#include <boost/foreach.hpp>
+
+#include "trace.h"
+#include "range.h"
+
+#include "pns_line.h"
+#include "pns_node.h"
+#include "pns_walkaround.h"
+#include "pns_shove.h"
+#include "pns_solid.h"
+#include "pns_optimizer.h"
+#include "pns_via.h"
+#include "pns_utils.h"
+#include "pns_router.h"
+#include "pns_shove.h"
+#include "pns_utils.h"
+#include "pns_topology.h"
+
+#include "time_limit.h"
+
+#include <profile.h>
+
+void PNS_SHOVE::replaceItems( PNS_ITEM* aOld, PNS_ITEM* aNew )
+{
+ OPT_BOX2I changed_area = ChangedArea( aOld, aNew );
+
+ if( changed_area )
+ {
+ m_affectedAreaSum = m_affectedAreaSum ? m_affectedAreaSum->Merge ( *changed_area ) : *changed_area;
+ }
+
+ m_currentNode->Replace( aOld, aNew );
+}
+
+
+int PNS_SHOVE::getClearance( const PNS_ITEM* aA, const PNS_ITEM* aB ) const
+{
+ if( m_forceClearance >= 0 )
+ return m_forceClearance;
+
+ return m_currentNode->GetClearance( aA, aB );
+}
+
+
+void PNS_SHOVE::sanityCheck( PNS_LINE* aOld, PNS_LINE* aNew )
+{
+ assert( aOld->CPoint( 0 ) == aNew->CPoint( 0 ) );
+ assert( aOld->CPoint( -1 ) == aNew->CPoint( -1 ) );
+}
+
+
+PNS_SHOVE::PNS_SHOVE( PNS_NODE* aWorld, PNS_ROUTER* aRouter ) :
+ PNS_ALGO_BASE ( aRouter )
+{
+ m_forceClearance = -1;
+ m_root = aWorld;
+ m_currentNode = aWorld;
+
+ // Initialize other temporary variables:
+ m_draggedVia = NULL;
+ m_iter = 0;
+ m_multiLineMode = false;
+}
+
+
+PNS_SHOVE::~PNS_SHOVE()
+{
+}
+
+
+PNS_LINE PNS_SHOVE::assembleLine( const PNS_SEGMENT* aSeg, int* aIndex )
+{
+ return m_currentNode->AssembleLine( const_cast<PNS_SEGMENT*>( aSeg ), aIndex, true );
+}
+
+// A dumb function that checks if the shoved line is shoved the right way, e.g.
+// visually "outwards" of the line/via applying pressure on it. Unfortunately there's no
+// mathematical concept of orientation of an open curve, so we use some primitive heuristics:
+// if the shoved line wraps around the start of the "pusher", it's likely shoved in wrong direction.
+bool PNS_SHOVE::checkBumpDirection( const PNS_LINE& aCurrent, const PNS_LINE& aShoved ) const
+{
+ const SEG& ss = aCurrent.CSegment( 0 );
+
+ int dist = getClearance( &aCurrent, &aShoved ) + PNS_HULL_MARGIN;
+
+ dist += aCurrent.Width() / 2;
+ dist += aShoved.Width() / 2;
+
+ const VECTOR2I ps = ss.A - ( ss.B - ss.A ).Resize( dist );
+
+ return !aShoved.CLine().PointOnEdge( ps );
+}
+
+
+PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::walkaroundLoneVia( PNS_LINE& aCurrent, PNS_LINE& aObstacle,
+ PNS_LINE& aShoved )
+{
+ int clearance = getClearance( &aCurrent, &aObstacle );
+ const SHAPE_LINE_CHAIN hull = aCurrent.Via().Hull( clearance, aObstacle.Width() );
+ SHAPE_LINE_CHAIN path_cw, path_ccw;
+
+ aObstacle.Walkaround( hull, path_cw, true );
+ aObstacle.Walkaround( hull, path_ccw, false );
+
+ 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 );
+
+ if( m_currentNode->CheckColliding( &aShoved, &aCurrent ) )
+ return SH_INCOMPLETE;
+
+ return SH_OK;
+}
+
+
+PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::processHullSet( PNS_LINE& aCurrent, PNS_LINE& aObstacle,
+ PNS_LINE& aShoved, const HULL_SET& aHulls )
+{
+ const SHAPE_LINE_CHAIN& obs = aObstacle.CLine();
+
+ int attempt;
+
+ for( attempt = 0; attempt < 4; attempt++ )
+ {
+ bool invertTraversal = ( attempt >= 2 );
+ bool clockwise = attempt % 2;
+ int vFirst = -1, vLast = -1;
+
+ SHAPE_LINE_CHAIN path;
+ PNS_LINE l( aObstacle );
+
+ for( int i = 0; i < (int) aHulls.size(); i++ )
+ {
+ const SHAPE_LINE_CHAIN& hull = aHulls[invertTraversal ? aHulls.size() - 1 - i : i];
+
+ l.Walkaround( hull, path, clockwise );
+ path.Simplify();
+ l.SetShape( path );
+ }
+
+ for( int i = 0; i < std::min ( path.PointCount(), obs.PointCount() ); i++ )
+ {
+ if( path.CPoint( i ) != obs.CPoint( i ) )
+ {
+ vFirst = i;
+ break;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ if( ( vFirst < 0 || vLast < 0 ) && !path.CompareGeometry( aObstacle.CLine() ) )
+ {
+ TRACE( 100, "attempt %d fail vfirst-last", attempt );
+ continue;
+ }
+
+ if( path.CPoint( -1 ) != obs.CPoint( -1 ) || path.CPoint( 0 ) != obs.CPoint( 0 ) )
+ {
+ TRACE( 100, "attempt %d fail vend-start\n", attempt );
+ continue;
+ }
+
+ if( !checkBumpDirection( aCurrent, l ) )
+ {
+ TRACE( 100, "attempt %d fail direction-check", attempt );
+ aShoved.SetShape( l.CLine() );
+
+ continue;
+ }
+
+ if( path.SelfIntersecting() )
+ {
+ TRACE( 100, "attempt %d fail self-intersect", attempt );
+ continue;
+ }
+
+ bool colliding = m_currentNode->CheckColliding( &l, &aCurrent, PNS_ITEM::ANY, m_forceClearance );
+
+ 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;
+ }
+
+ return SH_INCOMPLETE;
+}
+
+
+PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ProcessSingleLine( PNS_LINE& aCurrent, PNS_LINE& aObstacle,
+ PNS_LINE& aShoved )
+{
+ aShoved.ClearSegmentLinks();
+
+ bool obstacleIsHead = false;
+
+ if( aObstacle.LinkedSegments() )
+ {
+ BOOST_FOREACH( PNS_SEGMENT* s, *aObstacle.LinkedSegments() )
+
+ if( s->Marker() & MK_HEAD )
+ {
+ obstacleIsHead = true;
+ break;
+ }
+ }
+
+ SHOVE_STATUS rv;
+
+ bool viaOnEnd = aCurrent.EndsWithVia();
+
+ 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 = getClearance( &aCurrent, &aObstacle ) + 1;
+
+ HULL_SET hulls;
+
+ hulls.reserve( n_segs + 1 );
+
+ for( int i = 0; i < n_segs; i++ )
+ {
+ PNS_SEGMENT seg( aCurrent, aCurrent.CSegment( i ) );
+ SHAPE_LINE_CHAIN hull = seg.Hull( clearance, w );
+
+ hulls.push_back( hull );
+ }
+
+ if( viaOnEnd )
+ hulls.push_back ( aCurrent.Via().Hull( clearance, w ) );
+
+ rv = processHullSet ( aCurrent, aObstacle, aShoved, hulls );
+ }
+
+ if( obstacleIsHead )
+ aShoved.Mark( aShoved.Marker() | MK_HEAD );
+
+ return rv;
+}
+
+
+PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSegment( PNS_LINE& aCurrent, PNS_SEGMENT* aObstacleSeg )
+{
+ int segIndex;
+ PNS_LINE obstacleLine = assembleLine( aObstacleSeg, &segIndex );
+ PNS_LINE shovedLine( obstacleLine );
+ PNS_SEGMENT tmp( *aObstacleSeg );
+
+ SHOVE_STATUS rv = ProcessSingleLine( aCurrent, obstacleLine, shovedLine );
+
+ const double extensionWalkThreshold = 1.0;
+
+ double obsLen = obstacleLine.CLine().Length();
+ double shovedLen = shovedLine.CLine().Length();
+ double extensionFactor = 0.0;
+
+ if( obsLen != 0.0f )
+ extensionFactor = shovedLen / obsLen - 1.0;
+
+ if( extensionFactor > extensionWalkThreshold )
+ return SH_TRY_WALK;
+
+ assert( obstacleLine.LayersOverlap( &shovedLine ) );
+
+#ifdef DEBUG
+ m_logger.NewGroup( "on-colliding-segment", m_iter );
+ m_logger.Log( &tmp, 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
+
+ if( rv == SH_OK )
+ {
+ if( shovedLine.Marker() & MK_HEAD )
+ {
+ if( m_multiLineMode )
+ return SH_INCOMPLETE;
+
+ m_newHead = shovedLine;
+ }
+
+ int rank = aCurrent.Rank();
+ shovedLine.SetRank( rank - 1 );
+
+ sanityCheck( &obstacleLine, &shovedLine );
+ replaceItems( &obstacleLine, &shovedLine );
+
+ if( !pushLine( shovedLine ) )
+ rv = SH_INCOMPLETE;
+ }
+
+ return rv;
+}
+
+
+PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingLine( PNS_LINE& aCurrent, PNS_LINE& aObstacle )
+{
+ PNS_LINE shovedLine( aObstacle );
+
+ SHOVE_STATUS rv = ProcessSingleLine( aCurrent, aObstacle, 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
+
+ if( rv == SH_OK )
+ {
+ if( shovedLine.Marker() & MK_HEAD )
+ {
+ if( m_multiLineMode )
+ return SH_INCOMPLETE;
+
+ m_newHead = shovedLine;
+ }
+
+ sanityCheck( &aObstacle, &shovedLine );
+ replaceItems( &aObstacle, &shovedLine );
+
+ int rank = aObstacle.Rank();
+ shovedLine.SetRank( rank - 1 );
+
+
+ if( !pushLine( shovedLine ) )
+ {
+ rv = SH_INCOMPLETE;
+ }
+ }
+
+ return rv;
+}
+
+PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSolid( PNS_LINE& aCurrent, PNS_ITEM* aObstacle )
+{
+ PNS_WALKAROUND walkaround( m_currentNode, Router() );
+ PNS_LINE walkaroundLine( aCurrent );
+
+ if( aCurrent.EndsWithVia() )
+ {
+ PNS_VIA vh = aCurrent.Via();
+ PNS_VIA* via = NULL;
+ PNS_JOINT* jtStart = m_currentNode->FindJoint( vh.Pos(), &aCurrent );
+
+ if( !jtStart )
+ return SH_INCOMPLETE;
+
+ BOOST_FOREACH( PNS_ITEM* item, jtStart->LinkList() )
+ {
+ if( item->OfKind( PNS_ITEM::VIA ) )
+ {
+ via = (PNS_VIA*) item;
+ break;
+ }
+ }
+
+ if( via && m_currentNode->CheckColliding( via, aObstacle ) )
+ return onCollidingVia( aObstacle, via );
+ }
+
+ PNS_TOPOLOGY topo( m_currentNode );
+
+ std::set<PNS_ITEM*> cluster = topo.AssembleCluster( aObstacle, aCurrent.Layers().Start() );
+
+#ifdef DEBUG
+ m_logger.NewGroup( "on-colliding-solid-cluster", m_iter );
+ BOOST_FOREACH( PNS_ITEM* item, cluster )
+ {
+ m_logger.Log( item, 0, "cluster-entry" );
+ }
+#endif
+
+ walkaround.SetSolidsOnly( false );
+ walkaround.RestrictToSet( true, cluster );
+ walkaround.SetIterationLimit( 16 ); // fixme: make configurable
+
+ int currentRank = aCurrent.Rank();
+ int nextRank;
+
+ for( int attempt = 0; attempt < 2; attempt++ )
+ {
+
+ if( attempt == 1 || Settings().JumpOverObstacles() )
+ {
+ nextRank = currentRank - 1;
+ walkaround.SetSingleDirection( true );
+ }
+ else
+ {
+ nextRank = currentRank + 10000;
+ walkaround.SetSingleDirection( false );
+ }
+
+
+ if( walkaround.Route( aCurrent, walkaroundLine, false ) != PNS_WALKAROUND::DONE )
+ 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 );
+
+ if( m_multiLineMode )
+ return SH_INCOMPLETE;
+
+ m_newHead = walkaroundLine;
+ }
+
+ sanityCheck( &aCurrent, &walkaroundLine );
+
+ if( !m_lineStack.empty() )
+ {
+ PNS_LINE lastLine = m_lineStack.front();
+
+ if( m_currentNode->CheckColliding( &lastLine, &walkaroundLine ) )
+ {
+ PNS_LINE dummy ( lastLine );
+
+ if( ProcessSingleLine( walkaroundLine, lastLine, dummy ) == SH_OK )
+ break;
+ } else
+ break;
+ }
+ }
+
+ replaceItems( &aCurrent, &walkaroundLine );
+ walkaroundLine.SetRank( nextRank );
+
+#ifdef DEBUG
+ m_logger.NewGroup( "on-colliding-solid", m_iter );
+ m_logger.Log( aObstacle, 0, "obstacle-solid" );
+ m_logger.Log( &aCurrent, 1, "current-line" );
+ m_logger.Log( &walkaroundLine, 3, "walk-line" );
+#endif
+
+ popLine();
+
+ if( !pushLine( walkaroundLine ) )
+ return SH_INCOMPLETE;
+
+ return SH_OK;
+}
+
+
+bool PNS_SHOVE::reduceSpringback( const PNS_ITEMSET& aHeadSet )
+{
+ bool rv = false;
+
+ while( !m_nodeStack.empty() )
+ {
+ SPRINGBACK_TAG spTag = m_nodeStack.back();
+
+ if( !spTag.m_node->CheckColliding( aHeadSet ) )
+ {
+ rv = true;
+
+ delete spTag.m_node;
+ m_nodeStack.pop_back();
+ }
+ else
+ break;
+ }
+
+ return rv;
+}
+
+
+bool PNS_SHOVE::pushSpringback( PNS_NODE* aNode, const PNS_ITEMSET& aHeadItems,
+ const PNS_COST_ESTIMATOR& aCost, const OPT_BOX2I& aAffectedArea )
+{
+ SPRINGBACK_TAG st;
+ OPT_BOX2I prev_area;
+
+ if( !m_nodeStack.empty() )
+ prev_area = m_nodeStack.back().m_affectedArea;
+
+ st.m_node = aNode;
+ st.m_cost = aCost;
+ st.m_headItems = aHeadItems;
+
+ if( aAffectedArea )
+ {
+ if( prev_area )
+ st.m_affectedArea = prev_area->Merge ( *aAffectedArea );
+ else
+ st.m_affectedArea = aAffectedArea;
+ } else
+ st.m_affectedArea = prev_area;
+
+ m_nodeStack.push_back( st );
+
+ return true;
+}
+
+
+PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::pushVia( PNS_VIA* aVia, const VECTOR2I& aForce, int aCurrentRank, bool aDryRun )
+{
+ LINE_PAIR_VEC draggedLines;
+ VECTOR2I p0( aVia->Pos() );
+ PNS_JOINT* jt = m_currentNode->FindJoint( p0, aVia );
+ VECTOR2I p0_pushed( p0 + aForce );
+
+ if( !jt )
+ {
+ TRACEn( 1, "weird, can't find the center-of-via joint\n" );
+ return SH_INCOMPLETE;
+ }
+
+ if( jt->IsLocked() )
+ return SH_INCOMPLETE;
+
+ while( aForce.x != 0 || aForce.y != 0 )
+ {
+ PNS_JOINT* jt_next = m_currentNode->FindJoint( p0_pushed, aVia );
+
+ if( !jt_next )
+ break;
+
+ p0_pushed += aForce.Resize( 2 ); // make sure pushed via does not overlap with any existing joint
+ }
+
+ PNS_VIA* pushedVia = aVia->Clone();
+ pushedVia->SetPos( p0_pushed );
+ pushedVia->Mark( aVia->Marker() );
+
+ if( aVia->Marker() & MK_HEAD )
+ {
+ m_draggedVia = pushedVia;
+ m_draggedViaHeadSet.Clear();
+ }
+
+ BOOST_FOREACH( PNS_ITEM* item, jt->LinkList() )
+ {
+ if( PNS_SEGMENT* seg = dyn_cast<PNS_SEGMENT*>( item ) )
+ {
+ LINE_PAIR lp;
+ int segIndex;
+
+ lp.first = assembleLine( seg, &segIndex );
+
+ assert( segIndex == 0 || ( segIndex == ( lp.first.SegmentCount() - 1 ) ) );
+
+ if( segIndex == 0 )
+ lp.first.Reverse();
+
+ lp.second = lp.first;
+ lp.second.ClearSegmentLinks();
+ lp.second.DragCorner( p0_pushed, lp.second.CLine().Find( p0 ) );
+ lp.second.AppendVia( *pushedVia );
+ draggedLines.push_back( lp );
+
+ if( aVia->Marker() & MK_HEAD )
+ m_draggedViaHeadSet.Add( lp.second );
+ }
+ }
+
+ m_draggedViaHeadSet.Add( pushedVia );
+
+ if( aDryRun )
+ return SH_OK;
+
+ replaceItems( aVia, pushedVia );
+
+#ifdef DEBUG
+ m_logger.Log( aVia, 0, "obstacle-via" );
+#endif
+
+ pushedVia->SetRank( aCurrentRank - 1 );
+
+#ifdef DEBUG
+ m_logger.Log( pushedVia, 1, "pushed-via" );
+#endif
+
+ BOOST_FOREACH( LINE_PAIR lp, draggedLines )
+ {
+ if( lp.first.Marker() & MK_HEAD )
+ {
+ lp.second.Mark( MK_HEAD );
+
+ if( m_multiLineMode )
+ return SH_INCOMPLETE;
+
+ m_newHead = lp.second;
+ }
+
+ unwindStack( &lp.first );
+
+ if( lp.second.SegmentCount() )
+ {
+ replaceItems( &lp.first, &lp.second );
+ lp.second.SetRank( aCurrentRank - 1 );
+
+ if( !pushLine( lp.second, true ) )
+ return SH_INCOMPLETE;
+ }
+ 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
+ }
+
+ return SH_OK;
+}
+
+
+PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingVia( PNS_ITEM* aCurrent, PNS_VIA* aObstacleVia )
+{
+ int clearance = getClearance( aCurrent, aObstacleVia ) ;
+ LINE_PAIR_VEC draggedLines;
+ bool colLine = false, colVia = false;
+ PNS_LINE* currentLine = NULL;
+ VECTOR2I mtvLine, mtvVia, mtv, mtvSolid;
+ int rank = -1;
+
+ if( aCurrent->OfKind( PNS_ITEM::LINE ) )
+ {
+#ifdef DEBUG
+ m_logger.NewGroup( "push-via-by-line", m_iter );
+ m_logger.Log( aCurrent, 4, "current" );
+#endif
+
+ currentLine = (PNS_LINE*) aCurrent;
+ colLine = CollideShapes( aObstacleVia->Shape(), currentLine->Shape(),
+ clearance + currentLine->Width() / 2 + PNS_HULL_MARGIN,
+ true, mtvLine );
+
+ if( currentLine->EndsWithVia() )
+ colVia = CollideShapes( currentLine->Via().Shape(), aObstacleVia->Shape(),
+ clearance + PNS_HULL_MARGIN, true, mtvVia );
+
+ if( !colLine && !colVia )
+ return SH_OK;
+
+ 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 ) )
+ {
+ CollideShapes( aObstacleVia->Shape(), aCurrent->Shape(),
+ clearance + PNS_HULL_MARGIN, true, mtvSolid );
+ mtv = -mtvSolid;
+ rank = aCurrent->Rank() + 10000;
+ }
+
+ return pushVia( aObstacleVia, mtv, rank );
+}
+
+
+PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onReverseCollidingVia( PNS_LINE& aCurrent, PNS_VIA* aObstacleVia )
+{
+ int n = 0;
+ PNS_LINE cur( aCurrent );
+ cur.ClearSegmentLinks();
+
+ PNS_JOINT* jt = m_currentNode->FindJoint( aObstacleVia->Pos(), aObstacleVia );
+ PNS_LINE shoved( aCurrent );
+ shoved.ClearSegmentLinks();
+
+ cur.RemoveVia();
+ unwindStack( &aCurrent );
+
+ BOOST_FOREACH( PNS_ITEM* item, jt->LinkList() )
+ {
+ if( item->OfKind( PNS_ITEM::SEGMENT ) && item->LayersOverlap( &aCurrent ) )
+ {
+ PNS_SEGMENT* seg = (PNS_SEGMENT*) item;
+ PNS_LINE head = assembleLine( seg );
+
+ head.AppendVia( *aObstacleVia );
+
+ SHOVE_STATUS 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();
+
+ SHOVE_STATUS 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();
+ replaceItems( &aCurrent, &shoved );
+
+ if( !pushLine( shoved ) )
+ return SH_INCOMPLETE;
+
+ shoved.SetRank( currentRank );
+
+ return SH_OK;
+}
+
+
+void PNS_SHOVE::unwindStack( PNS_SEGMENT *aSeg )
+{
+ for( std::vector<PNS_LINE>::iterator i = m_lineStack.begin(); i != m_lineStack.end() ; )
+ {
+ if( i->ContainsSegment( aSeg ) )
+ i = m_lineStack.erase( i );
+ else
+ i++;
+ }
+
+ for( std::vector<PNS_LINE>::iterator i = m_optimizerQueue.begin(); i != m_optimizerQueue.end() ; )
+ {
+ if( i->ContainsSegment( aSeg ) )
+ i = m_optimizerQueue.erase( i );
+ else
+ i++;
+ }
+}
+
+
+void PNS_SHOVE::unwindStack( PNS_ITEM* aItem )
+{
+ if( aItem->OfKind( PNS_ITEM::SEGMENT ) )
+ unwindStack( static_cast<PNS_SEGMENT*>( aItem ) );
+ else if( aItem->OfKind( PNS_ITEM::LINE ) )
+ {
+ PNS_LINE* l = static_cast<PNS_LINE*>( aItem );
+
+ if( !l->LinkedSegments() )
+ return;
+
+ BOOST_FOREACH( PNS_SEGMENT* seg, *l->LinkedSegments() )
+ unwindStack( seg );
+ }
+}
+
+
+bool PNS_SHOVE::pushLine( const PNS_LINE& aL, bool aKeepCurrentOnTop )
+{
+ if( aL.LinkCount() >= 0 && ( aL.LinkCount() != aL.SegmentCount() ) )
+ return false;
+
+ if( aKeepCurrentOnTop && m_lineStack.size() > 0)
+ {
+ m_lineStack.insert( m_lineStack.begin() + m_lineStack.size() - 1, aL );
+ }
+ else
+ {
+ m_lineStack.push_back( aL );
+ }
+
+ m_optimizerQueue.push_back( aL );
+
+ return true;
+}
+
+void PNS_SHOVE::popLine( )
+{
+ PNS_LINE& l = m_lineStack.back();
+
+ for( std::vector<PNS_LINE>::iterator i = m_optimizerQueue.begin(); i != m_optimizerQueue.end(); )
+ {
+ bool found = false;
+
+ if( !l.LinkedSegments() )
+ continue;
+
+ BOOST_FOREACH( PNS_SEGMENT *s, *l.LinkedSegments() )
+ {
+ if( i->ContainsSegment( s ) )
+ {
+ i = m_optimizerQueue.erase( i );
+ found = true;
+ break;
+ }
+ }
+
+ if( !found )
+ i++;
+ }
+
+ m_lineStack.pop_back();
+}
+
+
+PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::shoveIteration( int aIter )
+{
+ PNS_LINE currentLine = m_lineStack.back();
+ PNS_NODE::OPT_OBSTACLE nearest;
+ SHOVE_STATUS st = SH_NULL;
+
+ 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;
+ }
+
+ PNS_ITEM* ni = nearest->m_item;
+
+ unwindStack( ni );
+
+ 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 );
+ }
+
+ break;
+ }
+
+ case PNS_ITEM::SEGMENT:
+ {
+ PNS_SEGMENT* seg = (PNS_SEGMENT*) ni;
+ TRACE( 2, "iter %d: reverse-collide-segment ", aIter );
+ PNS_LINE revLine = assembleLine( seg );
+
+ popLine();
+ st = onCollidingLine( revLine, currentLine );
+ if( !pushLine( revLine ) )
+ return SH_INCOMPLETE;
+
+ break;
+ }
+
+ default:
+ assert( false );
+ }
+ }
+ else
+ { // "forward" collisions
+ switch( ni->Kind() )
+ {
+ case PNS_ITEM::SEGMENT:
+ TRACE( 2, "iter %d: collide-segment ", aIter );
+ st = onCollidingSegment( currentLine, (PNS_SEGMENT*) ni );
+
+ if( st == SH_TRY_WALK )
+ {
+ st = onCollidingSolid( currentLine, (PNS_SOLID*) ni );
+ }
+ break;
+
+ case PNS_ITEM::VIA:
+ TRACE( 2, "iter %d: shove-via ", aIter );
+ st = onCollidingVia( &currentLine, (PNS_VIA*) ni );
+ break;
+
+ case PNS_ITEM::SOLID:
+ TRACE( 2, "iter %d: walk-solid ", aIter );
+ st = onCollidingSolid( currentLine, (PNS_SOLID*) ni );
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return st;
+}
+
+
+PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::shoveMainLoop()
+{
+ SHOVE_STATUS st = SH_OK;
+
+ m_affectedAreaSum = OPT_BOX2I();
+
+ TRACE( 1, "ShoveStart [root: %d jts, current: %d jts]", m_root->JointCount() %
+ m_currentNode->JointCount() );
+
+ int iterLimit = Settings().ShoveIterationLimit();
+ TIME_LIMIT timeLimit = Settings().ShoveTimeLimit();
+
+ m_iter = 0;
+
+ timeLimit.Restart();
+
+ while( !m_lineStack.empty() )
+ {
+ st = shoveIteration( m_iter );
+
+ m_iter++;
+
+ if( st == SH_INCOMPLETE || timeLimit.Expired() || m_iter >= iterLimit )
+ {
+ st = SH_INCOMPLETE;
+ break;
+ }
+ }
+
+ return st;
+}
+
+
+OPT_BOX2I PNS_SHOVE::totalAffectedArea() const
+{
+ OPT_BOX2I area;
+ if( !m_nodeStack.empty() )
+ area = m_nodeStack.back().m_affectedArea;
+
+ if( area )
+ {
+ if( m_affectedAreaSum )
+ area->Merge ( *m_affectedAreaSum );
+ } else
+ area = m_affectedAreaSum;
+
+ return area;
+}
+
+
+PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead )
+{
+ SHOVE_STATUS st = SH_OK;
+
+ m_multiLineMode = false;
+
+ // empty head? nothing to shove...
+
+ if( !aCurrentHead.SegmentCount() && !aCurrentHead.EndsWithVia() )
+ return SH_INCOMPLETE;
+
+ PNS_LINE head( aCurrentHead );
+ head.ClearSegmentLinks();
+
+ m_lineStack.clear();
+ m_optimizerQueue.clear();
+ m_newHead = OPT_LINE();
+ m_logger.Clear();
+
+ PNS_ITEMSET headSet;
+ headSet.Add( aCurrentHead );
+
+ reduceSpringback( headSet );
+
+ PNS_NODE* parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node;
+
+ m_currentNode = parent->Branch();
+ m_currentNode->ClearRanks();
+ m_currentNode->Add( &head );
+
+ m_currentNode->LockJoint( head.CPoint(0), &head, true );
+
+ if( !head.EndsWithVia() )
+ m_currentNode->LockJoint( head.CPoint( -1 ), &head, true );
+
+ head.Mark( MK_HEAD );
+ head.SetRank( 100000 );
+
+ m_logger.NewGroup( "initial", 0 );
+ m_logger.Log( &head, 0, "head" );
+
+ 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( !pushLine( head ) )
+ {
+ delete m_currentNode;
+ m_currentNode = parent;
+
+ return SH_INCOMPLETE;
+ }
+
+ st = shoveMainLoop();
+
+ if( st == SH_OK )
+ {
+ runOptimizer( m_currentNode );
+
+ if( m_newHead )
+ st = m_currentNode->CheckColliding( &(*m_newHead) ) ? SH_INCOMPLETE : SH_HEAD_MODIFIED;
+ else
+ st = m_currentNode->CheckColliding( &head ) ? SH_INCOMPLETE : SH_OK;
+ }
+
+ 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(), m_affectedAreaSum );
+ }
+ else
+ {
+ delete m_currentNode;
+
+ m_currentNode = parent;
+ m_newHead = OPT_LINE();
+ }
+
+ if( m_newHead && head.EndsWithVia() )
+ {
+ PNS_VIA v = head.Via();
+ v.SetPos( m_newHead->CPoint( -1 ) );
+ m_newHead->AppendVia(v);
+ }
+
+ return st;
+}
+
+
+PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveMultiLines( const PNS_ITEMSET& aHeadSet )
+{
+ SHOVE_STATUS st = SH_OK;
+
+ m_multiLineMode = true;
+
+ PNS_ITEMSET headSet;
+
+ BOOST_FOREACH( const PNS_ITEM* item, aHeadSet.CItems() )
+ {
+ const PNS_LINE* headOrig = static_cast<const PNS_LINE*>( item );
+
+ // empty head? nothing to shove...
+ if( !headOrig->SegmentCount() )
+ return SH_INCOMPLETE;
+
+ headSet.Add( *headOrig );
+ }
+
+ m_lineStack.clear();
+ m_optimizerQueue.clear();
+ m_logger.Clear();
+
+ reduceSpringback( headSet );
+
+ PNS_NODE* parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node;
+
+ m_currentNode = parent->Branch();
+ m_currentNode->ClearRanks();
+ int n = 0;
+
+ BOOST_FOREACH( const PNS_ITEM* item, aHeadSet.CItems() )
+ {
+ const PNS_LINE* headOrig = static_cast<const PNS_LINE*>( item );
+ PNS_LINE head( *headOrig );
+ head.ClearSegmentLinks();
+
+ m_currentNode->Add( &head );
+
+ head.Mark( MK_HEAD );
+ head.SetRank( 100000 );
+ n++;
+
+ if( !pushLine( head ) )
+ return SH_INCOMPLETE;
+
+ PNS_VIA* headVia = NULL;
+
+ if( head.EndsWithVia() )
+ {
+ headVia = head.Via().Clone(); // fixme: leak
+ m_currentNode->Add( headVia );
+ headVia->Mark( MK_HEAD );
+ headVia->SetRank( 100000 );
+ m_logger.Log( headVia, 0, "head-via" );
+ }
+ }
+
+ m_logger.NewGroup( "initial", 0 );
+ //m_logger.Log( head, 0, "head" );
+
+ st = shoveMainLoop();
+
+ if( st == SH_OK )
+ runOptimizer( m_currentNode );
+
+ m_currentNode->RemoveByMarker( MK_HEAD );
+
+ TRACE( 1, "Shove status : %s after %d iterations",
+ ( st == SH_OK ? "OK" : "FAILURE") % m_iter );
+
+ if( st == SH_OK )
+ {
+ pushSpringback( m_currentNode, PNS_ITEMSET(), PNS_COST_ESTIMATOR(), m_affectedAreaSum );
+ }
+ else
+ {
+ delete m_currentNode;
+ m_currentNode = parent;
+ }
+
+ return st;
+}
+
+
+PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveDraggingVia( PNS_VIA* aVia, const VECTOR2I& aWhere,
+ PNS_VIA** aNewVia )
+{
+ SHOVE_STATUS st = SH_OK;
+
+ m_lineStack.clear();
+ m_optimizerQueue.clear();
+ m_newHead = OPT_LINE();
+ m_draggedVia = NULL;
+ m_draggedViaHeadSet.Clear();
+
+ PNS_NODE* parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node;
+
+ m_currentNode = parent;
+
+ parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node;
+
+ m_currentNode = parent->Branch();
+ m_currentNode->ClearRanks();
+
+ aVia->Mark( MK_HEAD );
+
+ st = pushVia( aVia, ( aWhere - aVia->Pos() ), 0 );
+ st = shoveMainLoop();
+
+ if( st == SH_OK )
+ runOptimizer( m_currentNode );
+
+ if( st == SH_OK || st == SH_HEAD_MODIFIED )
+ {
+ pushSpringback( m_currentNode, m_draggedViaHeadSet, PNS_COST_ESTIMATOR(), m_affectedAreaSum );
+ }
+ else
+ {
+ delete m_currentNode;
+ m_currentNode = parent;
+ }
+
+ if( aNewVia )
+ {
+ *aNewVia = m_draggedVia;
+ }
+ return st;
+}
+
+
+void PNS_SHOVE::runOptimizer( PNS_NODE* aNode )
+{
+ PNS_OPTIMIZER optimizer( aNode );
+ int optFlags = 0, n_passes = 0;
+
+ PNS_OPTIMIZATION_EFFORT effort = Settings().OptimizerEffort();
+
+ OPT_BOX2I area = totalAffectedArea();
+
+ int maxWidth = 0;
+
+ for( std::vector<PNS_LINE>::iterator i = m_optimizerQueue.begin();
+ i != m_optimizerQueue.end(); ++i )
+ {
+ maxWidth = std::max( i->Width(), maxWidth );
+ }
+
+ if( area )
+ {
+ area->Inflate( 10 * maxWidth );
+ }
+
+ switch( effort )
+ {
+ case OE_LOW:
+ optFlags = PNS_OPTIMIZER::MERGE_OBTUSE;
+ n_passes = 1;
+ break;
+
+ case OE_MEDIUM:
+ optFlags = PNS_OPTIMIZER::MERGE_SEGMENTS;
+
+ if( area )
+ optimizer.SetRestrictArea( *area );
+
+ n_passes = 2;
+ 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 ) )
+ {
+ PNS_LINE optimized;
+
+ if( optimizer.Optimize( &line, &optimized ) )
+ {
+ aNode->Remove( &line );
+ line.SetShape( optimized.CLine() );
+ aNode->Add( &line );
+ }
+ }
+ }
+ }
+}
+
+
+PNS_NODE* PNS_SHOVE::CurrentNode()
+{
+ return m_nodeStack.empty() ? m_root : m_nodeStack.back().m_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 );
+}
diff --git a/pcbnew/router/pns_shove.h b/pcbnew/router/pns_shove.h
new file mode 100644
index 0000000..f9eb60b
--- /dev/null
+++ b/pcbnew/router/pns_shove.h
@@ -0,0 +1,159 @@
+/*
+ * 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_SHOVE_H
+#define __PNS_SHOVE_H
+
+#include <vector>
+#include <stack>
+
+#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_NODE;
+class PNS_ROUTER;
+
+/**
+ * Class PNS_SHOVE
+ *
+ * The actual Push and Shove algorithm.
+ */
+
+class PNS_SHOVE : public PNS_ALGO_BASE
+{
+public:
+
+ enum SHOVE_STATUS
+ {
+ SH_OK = 0,
+ SH_NULL,
+ SH_INCOMPLETE,
+ SH_HEAD_MODIFIED,
+ SH_TRY_WALK
+ };
+
+ PNS_SHOVE( PNS_NODE* aWorld, PNS_ROUTER* aRouter );
+ ~PNS_SHOVE();
+
+ virtual PNS_LOGGER* Logger()
+ {
+ return &m_logger;
+ }
+
+ SHOVE_STATUS ShoveLines( const PNS_LINE& aCurrentHead );
+ SHOVE_STATUS ShoveMultiLines( const PNS_ITEMSET& aHeadSet );
+
+ SHOVE_STATUS ShoveDraggingVia( PNS_VIA* aVia, const VECTOR2I& aWhere, PNS_VIA** aNewVia );
+ SHOVE_STATUS ProcessSingleLine( PNS_LINE& aCurrent, PNS_LINE& aObstacle,
+ PNS_LINE& aShoved );
+
+ void ForceClearance ( bool aEnabled, int aClearance )
+ {
+ if( aEnabled )
+ m_forceClearance = aClearance;
+ else
+ m_forceClearance = -1;
+ }
+
+ PNS_NODE* CurrentNode();
+
+ const PNS_LINE NewHead() const;
+
+ void SetInitialLine( PNS_LINE& aInitial );
+
+private:
+ typedef std::vector<SHAPE_LINE_CHAIN> HULL_SET;
+ typedef boost::optional<PNS_LINE> OPT_LINE;
+ typedef std::pair<PNS_LINE, PNS_LINE> LINE_PAIR;
+ typedef std::vector<LINE_PAIR> LINE_PAIR_VEC;
+
+ struct SPRINGBACK_TAG
+ {
+ int64_t m_length;
+ int m_segments;
+ VECTOR2I m_p;
+ PNS_NODE* m_node;
+ PNS_ITEMSET m_headItems;
+ PNS_COST_ESTIMATOR m_cost;
+ OPT_BOX2I m_affectedArea;
+ };
+
+ SHOVE_STATUS processHullSet( PNS_LINE& aCurrent, PNS_LINE& aObstacle,
+ PNS_LINE& aShoved, const HULL_SET& hulls );
+
+ bool reduceSpringback( const PNS_ITEMSET& aHeadItems );
+ bool pushSpringback( PNS_NODE* aNode, const PNS_ITEMSET& aHeadItems,
+ const PNS_COST_ESTIMATOR& aCost, const OPT_BOX2I& aAffectedArea );
+
+ SHOVE_STATUS walkaroundLoneVia( PNS_LINE& aCurrent, PNS_LINE& aObstacle, PNS_LINE& aShoved );
+ bool checkBumpDirection( const PNS_LINE& aCurrent, const PNS_LINE& aShoved ) const;
+
+ SHOVE_STATUS onCollidingLine( PNS_LINE& aCurrent, PNS_LINE& aObstacle );
+ SHOVE_STATUS onCollidingSegment( PNS_LINE& aCurrent, PNS_SEGMENT* aObstacleSeg );
+ SHOVE_STATUS onCollidingSolid( PNS_LINE& aCurrent, PNS_ITEM* aObstacle );
+ SHOVE_STATUS onCollidingVia( PNS_ITEM* aCurrent, PNS_VIA* aObstacleVia );
+ SHOVE_STATUS onReverseCollidingVia( PNS_LINE& aCurrent, PNS_VIA* aObstacleVia );
+ SHOVE_STATUS pushVia( PNS_VIA* aVia, const VECTOR2I& aForce, int aCurrentRank, bool aDryRun = false );
+
+ OPT_BOX2I totalAffectedArea() const;
+
+ void unwindStack( PNS_SEGMENT* aSeg );
+ void unwindStack( PNS_ITEM* aItem );
+
+ void runOptimizer( PNS_NODE* aNode );
+
+ bool pushLine( const PNS_LINE& aL, bool aKeepCurrentOnTop = false );
+ void popLine();
+
+ PNS_LINE assembleLine( const PNS_SEGMENT* aSeg, int* aIndex = NULL );
+
+ void replaceItems( PNS_ITEM* aOld, PNS_ITEM* aNew );
+
+ OPT_BOX2I m_affectedAreaSum;
+
+ SHOVE_STATUS shoveIteration( int aIter );
+ SHOVE_STATUS shoveMainLoop();
+
+ int getClearance( const PNS_ITEM* aA, const PNS_ITEM* aB ) const;
+
+ std::vector<SPRINGBACK_TAG> m_nodeStack;
+ std::vector<PNS_LINE> m_lineStack;
+ std::vector<PNS_LINE> m_optimizerQueue;
+
+ PNS_NODE* m_root;
+ PNS_NODE* m_currentNode;
+
+ OPT_LINE m_newHead;
+
+ PNS_LOGGER m_logger;
+ PNS_VIA* m_draggedVia;
+ PNS_ITEMSET m_draggedViaHeadSet;
+
+ int m_iter;
+ int m_forceClearance;
+ bool m_multiLineMode;
+ void sanityCheck( PNS_LINE* aOld, PNS_LINE* aNew );
+};
+
+#endif // __PNS_SHOVE_H
diff --git a/pcbnew/router/pns_sizes_settings.cpp b/pcbnew/router/pns_sizes_settings.cpp
new file mode 100644
index 0000000..dae3ea3
--- /dev/null
+++ b/pcbnew/router/pns_sizes_settings.cpp
@@ -0,0 +1,167 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 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 <class_board.h>
+
+#include "pns_item.h"
+#include "pns_via.h"
+#include "pns_solid.h"
+#include "pns_node.h"
+#include "pns_sizes_settings.h"
+
+int PNS_SIZES_SETTINGS::inheritTrackWidth( PNS_ITEM* aItem )
+{
+ VECTOR2I p;
+
+ assert( aItem->Owner() != NULL );
+
+ switch( aItem->Kind() )
+ {
+ case PNS_ITEM::VIA:
+ p = static_cast<PNS_VIA*>( aItem )->Pos();
+ break;
+
+ case PNS_ITEM::SOLID:
+ p = static_cast<PNS_SOLID*>( aItem )->Pos();
+ break;
+
+ case PNS_ITEM::SEGMENT:
+ return static_cast<PNS_SEGMENT*>( aItem )->Width();
+
+ default:
+ return 0;
+ }
+
+ PNS_JOINT* jt = static_cast<PNS_NODE*>( aItem->Owner() )->FindJoint( p, aItem );
+
+ assert( jt != NULL );
+
+ int mval = INT_MAX;
+
+
+ PNS_ITEMSET linkedSegs = jt->Links();
+ linkedSegs.ExcludeItem( aItem ).FilterKinds( PNS_ITEM::SEGMENT );
+
+ BOOST_FOREACH( PNS_ITEM* item, linkedSegs.Items() )
+ {
+ int w = static_cast<PNS_SEGMENT*>( item )->Width();
+ mval = std::min( w, mval );
+ }
+
+ return ( mval == INT_MAX ? 0 : mval );
+}
+
+
+void PNS_SIZES_SETTINGS::Init( BOARD* aBoard, PNS_ITEM* aStartItem, int aNet )
+{
+ BOARD_DESIGN_SETTINGS &bds = aBoard->GetDesignSettings();
+
+ NETCLASSPTR netClass;
+ int net = aNet;
+
+ if( aStartItem )
+ net = aStartItem->Net();
+
+ if( net >= 0 )
+ {
+ NETINFO_ITEM* ni = aBoard->FindNet( net );
+
+ if( ni )
+ {
+ wxString netClassName = ni->GetClassName();
+ netClass = bds.m_NetClasses.Find( netClassName );
+ }
+ }
+
+ if( !netClass )
+ netClass = bds.GetDefault();
+
+ m_trackWidth = 0;
+
+ if( bds.m_UseConnectedTrackWidth && aStartItem != NULL )
+ {
+ m_trackWidth = inheritTrackWidth( aStartItem );
+ }
+
+ if( !m_trackWidth && ( bds.UseNetClassTrack() && netClass != NULL ) ) // netclass value
+ {
+ m_trackWidth = netClass->GetTrackWidth();
+ }
+
+ if( !m_trackWidth )
+ {
+ m_trackWidth = bds.GetCurrentTrackWidth();
+ }
+
+ if( bds.UseNetClassVia() && netClass != NULL ) // netclass value
+ {
+ m_viaDiameter = netClass->GetViaDiameter();
+ m_viaDrill = netClass->GetViaDrill();
+ }
+ else
+ {
+ m_viaDiameter = bds.GetCurrentViaSize();
+ m_viaDrill = bds.GetCurrentViaDrill();
+ }
+
+ m_layerPairs.clear();
+}
+
+
+void PNS_SIZES_SETTINGS::ClearLayerPairs()
+{
+ m_layerPairs.clear();
+}
+
+
+void PNS_SIZES_SETTINGS::AddLayerPair( int aL1, int aL2 )
+{
+ int top = std::min( aL1, aL2 );
+ int bottom = std::max( aL1, aL2 );
+
+ m_layerPairs[bottom] = top;
+ m_layerPairs[top] = bottom;
+}
+
+
+void PNS_SIZES_SETTINGS::ImportCurrent( BOARD_DESIGN_SETTINGS& aSettings )
+{
+ m_trackWidth = aSettings.GetCurrentTrackWidth();
+ m_viaDiameter = aSettings.GetCurrentViaSize();
+ m_viaDrill = aSettings.GetCurrentViaDrill();
+}
+
+
+int PNS_SIZES_SETTINGS::GetLayerTop() const
+{
+ if( m_layerPairs.empty() )
+ return F_Cu;
+ else
+ return m_layerPairs.begin()->first;
+}
+
+
+int PNS_SIZES_SETTINGS::GetLayerBottom() const
+{
+ if( m_layerPairs.empty() )
+ return B_Cu;
+ else
+ return m_layerPairs.begin()->second;
+}
diff --git a/pcbnew/router/pns_sizes_settings.h b/pcbnew/router/pns_sizes_settings.h
new file mode 100644
index 0000000..bb30c9a
--- /dev/null
+++ b/pcbnew/router/pns_sizes_settings.h
@@ -0,0 +1,113 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 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_SIZES_SETTINGS_H
+#define __PNS_SIZES_SETTINGS_H
+
+#include <map>
+#include <boost/optional.hpp>
+
+#include "../class_track.h" // for VIATYPE_T
+
+class BOARD;
+class BOARD_DESIGN_SETTINGS;
+class PNS_ITEM;
+
+class PNS_SIZES_SETTINGS {
+
+public:
+ PNS_SIZES_SETTINGS() :
+ m_trackWidth( 155000 ),
+ m_diffPairWidth( 125000 ),
+ m_diffPairGap( 180000 ),
+ m_diffPairViaGap( 180000 ),
+ m_viaDiameter( 600000 ),
+ m_viaDrill( 250000 ),
+ m_diffPairViaGapSameAsTraceGap( true ),
+ m_viaType( VIA_THROUGH )
+ {};
+
+ ~PNS_SIZES_SETTINGS() {};
+
+ void Init( BOARD* aBoard, PNS_ITEM* aStartItem = NULL, int aNet = -1 );
+ void ImportCurrent( BOARD_DESIGN_SETTINGS& aSettings );
+
+ void ClearLayerPairs();
+ void AddLayerPair( int aL1, int aL2 );
+
+ int TrackWidth() const { return m_trackWidth; }
+ void SetTrackWidth( int aWidth ) { m_trackWidth = aWidth; }
+
+ int DiffPairWidth() const { return m_diffPairWidth; }
+ int DiffPairGap() const { return m_diffPairGap; }
+
+ int DiffPairViaGap() const {
+ if( m_diffPairViaGapSameAsTraceGap )
+ return m_diffPairGap;
+ else
+ return m_diffPairViaGap;
+ }
+
+ bool DiffPairViaGapSameAsTraceGap() const { return m_diffPairViaGapSameAsTraceGap; }
+
+ void SetDiffPairWidth( int aWidth ) { m_diffPairWidth = aWidth; }
+ void SetDiffPairGap( int aGap ) { m_diffPairGap = aGap; }
+ void SetDiffPairViaGapSameAsTraceGap ( bool aEnable ) { m_diffPairViaGapSameAsTraceGap = aEnable; }
+ void SetDiffPairViaGap( int aGap ) { m_diffPairViaGap = aGap; }
+
+ int ViaDiameter() const { return m_viaDiameter; }
+ void SetViaDiameter( int aDiameter ) { m_viaDiameter = aDiameter; }
+
+ int ViaDrill() const { return m_viaDrill; }
+ void SetViaDrill( int aDrill ) { m_viaDrill = aDrill; }
+
+ boost::optional<int> PairedLayer( int aLayerId )
+ {
+ if( m_layerPairs.find(aLayerId) == m_layerPairs.end() )
+ return boost::optional<int>();
+
+ return m_layerPairs[aLayerId];
+ }
+
+ int GetLayerTop() const;
+ int GetLayerBottom() const;
+
+ void SetViaType( VIATYPE_T aViaType ) { m_viaType = aViaType; }
+ VIATYPE_T ViaType() const { return m_viaType; }
+
+private:
+
+ int inheritTrackWidth( PNS_ITEM* aItem );
+
+ int m_trackWidth;
+ int m_diffPairWidth;
+ int m_diffPairGap;
+ int m_diffPairViaGap;
+ int m_viaDiameter;
+ int m_viaDrill;
+
+ bool m_diffPairViaGapSameAsTraceGap;
+
+ VIATYPE_T m_viaType;
+
+ std::map<int, int> m_layerPairs;
+};
+
+#endif // __PNS_SIZES_SETTINGS_H
diff --git a/pcbnew/router/pns_solid.cpp b/pcbnew/router/pns_solid.cpp
new file mode 100644
index 0000000..d728015
--- /dev/null
+++ b/pcbnew/router/pns_solid.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 <math/vector2d.h>
+
+#include <geometry/shape.h>
+#include <geometry/shape_line_chain.h>
+#include <geometry/shape_rect.h>
+#include <geometry/shape_circle.h>
+#include <geometry/shape_convex.h>
+
+#include "pns_solid.h"
+#include "pns_utils.h"
+
+const SHAPE_LINE_CHAIN PNS_SOLID::Hull( int aClearance, int aWalkaroundThickness ) const
+{
+ int cl = aClearance + ( aWalkaroundThickness + 1 )/ 2;
+
+ switch( m_shape->Type() )
+ {
+ case SH_RECT:
+ {
+ SHAPE_RECT* rect = static_cast<SHAPE_RECT*>( m_shape );
+ return OctagonalHull( rect->GetPosition(), rect->GetSize(), cl + 1, 0.2 * cl );
+ }
+
+ case SH_CIRCLE:
+ {
+ SHAPE_CIRCLE* circle = static_cast<SHAPE_CIRCLE*>( m_shape );
+ int r = circle->GetRadius();
+ return OctagonalHull( circle->GetCenter() - VECTOR2I( r, r ), VECTOR2I( 2 * r, 2 * r ),
+ cl + 1, 0.52 * ( r + cl ) );
+ }
+
+ case SH_SEGMENT:
+ {
+ SHAPE_SEGMENT* seg = static_cast<SHAPE_SEGMENT*>( m_shape );
+ return SegmentHull( *seg, aClearance, aWalkaroundThickness );
+ }
+
+ case SH_CONVEX:
+ {
+ SHAPE_CONVEX* convex = static_cast<SHAPE_CONVEX*>( m_shape );
+
+ return ConvexHull( *convex, cl );
+ }
+
+ default:
+ break;
+ }
+
+ return SHAPE_LINE_CHAIN();
+}
+
+
+PNS_ITEM* PNS_SOLID::Clone() const
+{
+ PNS_ITEM* solid = new PNS_SOLID( *this );
+ return solid;
+}
diff --git a/pcbnew/router/pns_solid.h b/pcbnew/router/pns_solid.h
new file mode 100644
index 0000000..593b3b0
--- /dev/null
+++ b/pcbnew/router/pns_solid.h
@@ -0,0 +1,107 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PNS_SOLID_H
+#define __PNS_SOLID_H
+
+#include <math/vector2d.h>
+
+#include <geometry/seg.h>
+#include <geometry/shape.h>
+#include <geometry/shape_line_chain.h>
+
+#include "pns_item.h"
+
+class PNS_SOLID : public PNS_ITEM
+{
+public:
+ PNS_SOLID() : PNS_ITEM( SOLID ), m_shape( NULL )
+ {
+ m_movable = false;
+ }
+
+ ~PNS_SOLID()
+ {
+ delete m_shape;
+ }
+
+ PNS_SOLID( const PNS_SOLID& aSolid ) :
+ PNS_ITEM( aSolid )
+ {
+ m_shape = aSolid.m_shape->Clone();
+ m_pos = aSolid.m_pos;
+ }
+
+ static inline bool ClassOf( const PNS_ITEM* aItem )
+ {
+ return aItem && SOLID == aItem->Kind();
+ }
+
+ PNS_ITEM* Clone() const;
+
+ const SHAPE* Shape() const { return m_shape; }
+
+ const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0 ) const;
+
+ void SetShape( SHAPE* shape )
+ {
+ if( m_shape )
+ delete m_shape;
+
+ m_shape = shape;
+ }
+
+ const VECTOR2I& Pos() const
+ {
+ return m_pos;
+ }
+
+ void SetPos( const VECTOR2I& aCenter )
+ {
+ m_pos = aCenter;
+ }
+
+ virtual VECTOR2I Anchor( int aN ) const
+ {
+ return m_pos;
+ }
+
+ virtual int AnchorCount() const
+ {
+ return 1;
+ }
+
+ VECTOR2I Offset() const
+ {
+ return m_offset;
+ }
+
+ void SetOffset( const VECTOR2I& aOffset )
+ {
+ m_offset = aOffset;
+ }
+
+private:
+ VECTOR2I m_pos;
+ SHAPE* m_shape;
+ VECTOR2I m_offset;
+};
+
+#endif
diff --git a/pcbnew/router/pns_tool_base.cpp b/pcbnew/router/pns_tool_base.cpp
new file mode 100644
index 0000000..5ebbb2d
--- /dev/null
+++ b/pcbnew/router/pns_tool_base.cpp
@@ -0,0 +1,308 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <wx/numdlg.h>
+
+#include <boost/foreach.hpp>
+#include <boost/optional.hpp>
+#include <boost/bind.hpp>
+
+#include "class_draw_panel_gal.h"
+#include "class_board.h"
+
+#include <wxPcbStruct.h>
+#include <id.h>
+#include <macros.h>
+#include <pcbnew_id.h>
+#include <view/view_controls.h>
+#include <pcbcommon.h>
+#include <pcb_painter.h>
+#include <dialogs/dialog_pns_settings.h>
+#include <dialogs/dialog_pns_diff_pair_dimensions.h>
+#include <dialogs/dialog_pns_length_tuning_settings.h>
+#include <dialogs/dialog_track_via_size.h>
+#include <base_units.h>
+
+#include <tool/context_menu.h>
+#include <tools/common_actions.h>
+#include <tools/grid_helper.h>
+
+#include <ratsnest_data.h>
+
+#include "pns_tool_base.h"
+#include "pns_segment.h"
+#include "pns_router.h"
+#include "pns_meander_placer.h" // fixme: move settings to separate header
+#include "pns_tune_status_popup.h"
+#include "trace.h"
+
+using namespace KIGFX;
+using boost::optional;
+
+TOOL_ACTION PNS_TOOL_BASE::ACT_RouterOptions( "pcbnew.InteractiveRouter.RouterOptions",
+ AS_CONTEXT, 'E',
+ _( "Routing Options..." ),
+ _( "Shows a dialog containing router options." ), tools_xpm );
+
+
+PNS_TOOL_BASE::PNS_TOOL_BASE( const std::string& aToolName ) :
+ TOOL_INTERACTIVE( aToolName )
+{
+ m_router = NULL;
+ m_startItem = NULL;
+ m_startLayer = 0;
+
+ m_endItem = NULL;
+
+ m_needsSync = false;
+
+ m_frame = NULL;
+ m_ctls = NULL;
+ m_board = NULL;
+ m_gridHelper = NULL;
+}
+
+
+PNS_TOOL_BASE::~PNS_TOOL_BASE()
+{
+ delete m_router;
+ delete m_gridHelper;
+}
+
+
+
+void PNS_TOOL_BASE::Reset( RESET_REASON aReason )
+{
+ if( m_router )
+ delete m_router;
+
+ if( m_gridHelper)
+ delete m_gridHelper;
+
+ m_frame = getEditFrame<PCB_EDIT_FRAME>();
+ m_ctls = getViewControls();
+ m_board = getModel<BOARD>();
+
+ m_router = new PNS_ROUTER;
+
+ m_router->ClearWorld();
+ m_router->SetBoard( m_board );
+ m_router->SyncWorld();
+ m_router->LoadSettings( m_savedSettings );
+ m_router->UpdateSizes( m_savedSizes );
+
+ m_gridHelper = new GRID_HELPER( m_frame );
+ m_router->SetGrid( m_gridHelper );
+
+ m_needsSync = false;
+
+ if( getView() )
+ m_router->SetView( getView() );
+}
+
+
+PNS_ITEM* PNS_TOOL_BASE::pickSingleItem( const VECTOR2I& aWhere, int aNet, int aLayer )
+{
+ int tl = getView()->GetTopLayer();
+
+ if( aLayer > 0 )
+ tl = aLayer;
+
+ PNS_ITEM* prioritized[4];
+
+ for( int i = 0; i < 4; i++ )
+ prioritized[i] = 0;
+
+ PNS_ITEMSET candidates = m_router->QueryHoverItems( aWhere );
+
+ BOOST_FOREACH( PNS_ITEM* item, candidates.Items() )
+ {
+ if( !IsCopperLayer( item->Layers().Start() ) )
+ continue;
+
+ // fixme: this causes flicker with live loop removal...
+ //if( item->Parent() && !item->Parent()->ViewIsVisible() )
+ // continue;
+
+ if( aNet < 0 || item->Net() == aNet )
+ {
+ if( item->OfKind( PNS_ITEM::VIA | PNS_ITEM::SOLID ) )
+ {
+ if( !prioritized[2] )
+ prioritized[2] = item;
+ if( item->Layers().Overlaps( tl ) )
+ prioritized[0] = item;
+ }
+ else
+ {
+ if( !prioritized[3] )
+ prioritized[3] = item;
+ if( item->Layers().Overlaps( tl ) )
+ prioritized[1] = item;
+ }
+ }
+ }
+
+ PNS_ITEM* rv = NULL;
+ PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
+ DISPLAY_OPTIONS* displ_opts = (DISPLAY_OPTIONS*)frame->GetDisplayOptions();
+
+ for( int i = 0; i < 4; i++ )
+ {
+ PNS_ITEM* item = prioritized[i];
+
+ if( displ_opts->m_ContrastModeDisplay )
+ if( item && !item->Layers().Overlaps( tl ) )
+ item = NULL;
+
+ if( item )
+ {
+ rv = item;
+ break;
+ }
+ }
+
+ if( rv && aLayer >= 0 && !rv->Layers().Overlaps( aLayer ) )
+ rv = NULL;
+
+ if( rv )
+ TRACE( 0, "%s, layer : %d, tl: %d", rv->KindStr().c_str() % rv->Layers().Start() % tl );
+
+ return rv;
+}
+
+
+void PNS_TOOL_BASE::highlightNet( bool aEnabled, int aNetcode )
+{
+ RENDER_SETTINGS* rs = getView()->GetPainter()->GetSettings();
+
+ if( aNetcode >= 0 && aEnabled )
+ rs->SetHighlight( true, aNetcode );
+ else
+ rs->SetHighlight( false );
+
+ getView()->UpdateAllLayersColor();
+}
+
+
+void PNS_TOOL_BASE::updateStartItem( TOOL_EVENT& aEvent )
+{
+ int tl = getView()->GetTopLayer();
+ VECTOR2I cp = m_ctls->GetCursorPosition();
+ VECTOR2I p;
+
+ PNS_ITEM* startItem = NULL;
+ bool snapEnabled = true;
+
+ if( aEvent.IsMotion() || aEvent.IsClick() )
+ {
+ snapEnabled = !aEvent.Modifier( MD_SHIFT );
+ p = aEvent.Position();
+ } else {
+ p = cp;
+ }
+
+ startItem = pickSingleItem( p );
+ m_router->EnableSnapping ( snapEnabled );
+
+ if( !snapEnabled && startItem && !startItem->Layers().Overlaps( tl ) )
+ startItem = NULL;
+
+ if( startItem && startItem->Net() >= 0 )
+ {
+ bool dummy;
+ VECTOR2I psnap = m_router->SnapToItem( startItem, p, dummy );
+
+ if( snapEnabled )
+ {
+ m_startSnapPoint = psnap;
+ m_ctls->ForceCursorPosition( true, psnap );
+ }
+ else
+ {
+ m_startSnapPoint = cp;
+ m_ctls->ForceCursorPosition( false );
+ }
+
+ m_startItem = startItem;
+ }
+ else
+ {
+ m_startItem = NULL;
+ m_startSnapPoint = cp;
+ m_ctls->ForceCursorPosition( false );
+ }
+}
+
+
+void PNS_TOOL_BASE::updateEndItem( TOOL_EVENT& aEvent )
+{
+ VECTOR2I mp = m_ctls->GetMousePosition();
+ VECTOR2I p = getView()->ToWorld( mp );
+ VECTOR2I cp = m_ctls->GetCursorPosition();
+ int layer;
+ bool snapEnabled = !aEvent.Modifier( MD_SHIFT );
+
+ m_router->EnableSnapping( snapEnabled );
+
+ if( m_router->GetCurrentNets().empty() || m_router->GetCurrentNets().front() < 0 )
+ {
+ m_endItem = NULL;
+ m_endSnapPoint = cp;
+ return;
+ }
+
+ bool dummy;
+
+ if( m_router->IsPlacingVia() )
+ layer = -1;
+ else
+ layer = m_router->GetCurrentLayer();
+
+ PNS_ITEM* endItem = NULL;
+
+ std::vector<int> nets = m_router->GetCurrentNets();
+
+ BOOST_FOREACH( int net, nets )
+ {
+ endItem = pickSingleItem( p, net, layer );
+
+ if( endItem )
+ break;
+ }
+
+ if( endItem )
+ {
+ VECTOR2I cursorPos = m_router->SnapToItem( endItem, p, dummy );
+ m_ctls->ForceCursorPosition( true, cursorPos );
+ m_endItem = endItem;
+ m_endSnapPoint = cursorPos;
+ }
+ else
+ {
+ m_endItem = NULL;
+ m_endSnapPoint = cp;
+ m_ctls->ForceCursorPosition( false );
+ }
+
+ if( m_endItem )
+ TRACE( 0, "%s, layer : %d", m_endItem->KindStr().c_str() % m_endItem->Layers().Start() );
+}
+
diff --git a/pcbnew/router/pns_tool_base.h b/pcbnew/router/pns_tool_base.h
new file mode 100644
index 0000000..20bed42
--- /dev/null
+++ b/pcbnew/router/pns_tool_base.h
@@ -0,0 +1,82 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2014 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ * Author: Maciej Suminski <maciej.suminski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PNS_TOOL_BASE_H
+#define __PNS_TOOL_BASE_H
+
+#include <import_export.h>
+
+#include <math/vector2d.h>
+#include <tool/tool_interactive.h>
+
+#include <msgpanel.h>
+
+#include "pns_router.h"
+
+class PNS_TUNE_STATUS_POPUP;
+class GRID_HELPER;
+
+class APIEXPORT PNS_TOOL_BASE : public TOOL_INTERACTIVE
+{
+public:
+ static TOOL_ACTION ACT_RouterOptions;
+
+ PNS_TOOL_BASE( const std::string& aToolName );
+ virtual ~PNS_TOOL_BASE();
+
+ virtual void Reset( RESET_REASON aReason );
+
+ const PNS_ROUTING_SETTINGS& PNSSettings() const
+ {
+ return m_savedSettings;
+ }
+
+protected:
+
+ virtual PNS_ITEM* pickSingleItem( const VECTOR2I& aWhere, int aNet = -1, int aLayer = -1 );
+ virtual void highlightNet( bool aEnabled, int aNetcode = -1 );
+ virtual void updateStartItem( TOOL_EVENT& aEvent );
+ virtual void updateEndItem( TOOL_EVENT& aEvent );
+
+ MSG_PANEL_ITEMS m_panelItems;
+
+ PNS_ROUTER* m_router;
+ PNS_ROUTING_SETTINGS m_savedSettings; ///< Stores routing settings between router invocations
+ PNS_SIZES_SETTINGS m_savedSizes; ///< Stores sizes settings between router invocations
+ PNS_ITEM* m_startItem;
+ int m_startLayer;
+ VECTOR2I m_startSnapPoint;
+
+ PNS_ITEM* m_endItem;
+ VECTOR2I m_endSnapPoint;
+
+ ///> Flag marking that the router's world needs syncing.
+ bool m_needsSync;
+
+ PCB_EDIT_FRAME* m_frame;
+ KIGFX::VIEW_CONTROLS* m_ctls;
+ BOARD* m_board;
+ GRID_HELPER* m_gridHelper;
+
+
+};
+
+#endif
diff --git a/pcbnew/router/pns_topology.cpp b/pcbnew/router/pns_topology.cpp
new file mode 100644
index 0000000..3fb17b0
--- /dev/null
+++ b/pcbnew/router/pns_topology.cpp
@@ -0,0 +1,433 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pns_line.h"
+#include "pns_segment.h"
+#include "pns_node.h"
+#include "pns_joint.h"
+#include "pns_solid.h"
+#include "pns_router.h"
+#include "pns_utils.h"
+
+#include "pns_diff_pair.h"
+#include "pns_topology.h"
+
+#include <class_board.h>
+
+bool PNS_TOPOLOGY::SimplifyLine( PNS_LINE* aLine )
+{
+ if( !aLine->LinkedSegments() || !aLine->SegmentCount() )
+ return false;
+
+ PNS_SEGMENT* root = ( *aLine->LinkedSegments() )[0];
+ PNS_LINE l = m_world->AssembleLine( root );
+ SHAPE_LINE_CHAIN simplified( l.CLine() );
+
+ simplified.Simplify();
+
+ if( simplified.PointCount() != l.PointCount() )
+ {
+ PNS_LINE lnew( l );
+ m_world->Remove( &l );
+ lnew.SetShape( simplified );
+ m_world->Add( &lnew );
+ return true;
+ }
+
+ return false;
+}
+
+
+const PNS_TOPOLOGY::JOINT_SET PNS_TOPOLOGY::ConnectedJoints( PNS_JOINT* aStart )
+{
+ std::deque<PNS_JOINT*> searchQueue;
+ JOINT_SET processed;
+
+ searchQueue.push_back( aStart );
+ processed.insert( aStart );
+
+ while( !searchQueue.empty() )
+ {
+ PNS_JOINT* current = searchQueue.front();
+ searchQueue.pop_front();
+
+ BOOST_FOREACH( PNS_ITEM* item, current->LinkList() )
+ {
+ if( item->OfKind( PNS_ITEM::SEGMENT ) )
+ {
+ PNS_SEGMENT* seg = static_cast<PNS_SEGMENT*>( item );
+ PNS_JOINT* a = m_world->FindJoint( seg->Seg().A, seg );
+ PNS_JOINT* b = m_world->FindJoint( seg->Seg().B, seg );
+ PNS_JOINT* next = ( *a == *current ) ? b : a;
+
+ if( processed.find( next ) == processed.end() )
+ {
+ processed.insert( next );
+ searchQueue.push_back( next );
+ }
+ }
+ }
+ }
+
+ return processed;
+}
+
+
+bool PNS_TOPOLOGY::LeadingRatLine( const PNS_LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine )
+{
+ PNS_LINE track( *aTrack );
+ VECTOR2I end;
+
+ if( !track.PointCount() )
+ return false;
+
+ std::auto_ptr<PNS_NODE> tmpNode( m_world->Branch() );
+ tmpNode->Add( &track );
+
+ PNS_JOINT* jt = tmpNode->FindJoint( track.CPoint( -1 ), &track );
+
+ if( !jt )
+ return false;
+
+ if( ( !track.EndsWithVia() && jt->LinkCount() >= 2 ) || ( track.EndsWithVia() && jt->LinkCount() >= 3 ) ) // we got something connected
+ {
+ end = jt->Pos();
+ }
+ else
+ {
+ int anchor;
+
+ PNS_TOPOLOGY topo( tmpNode.get() );
+ PNS_ITEM* it = topo.NearestUnconnectedItem( jt, &anchor );
+
+ if( !it )
+ return false;
+
+ end = it->Anchor( anchor );
+ }
+
+ aRatLine.Clear();
+ aRatLine.Append( track.CPoint( -1 ) );
+ aRatLine.Append( end );
+ return true;
+}
+
+
+PNS_ITEM* PNS_TOPOLOGY::NearestUnconnectedItem( PNS_JOINT* aStart, int* aAnchor, int aKindMask )
+{
+ std::set<PNS_ITEM*> disconnected;
+
+ m_world->AllItemsInNet( aStart->Net(), disconnected );
+
+ BOOST_FOREACH( const PNS_JOINT* jt, ConnectedJoints( aStart ) )
+ {
+ BOOST_FOREACH( PNS_ITEM* link, jt->LinkList() )
+ {
+ if( disconnected.find( link ) != disconnected.end() )
+ disconnected.erase( link );
+ }
+ }
+
+ int best_dist = INT_MAX;
+ PNS_ITEM* best = NULL;
+
+ BOOST_FOREACH( PNS_ITEM* item, disconnected )
+ {
+ if( item->OfKind( aKindMask ) )
+ {
+ for(int i = 0; i < item->AnchorCount(); i++)
+ {
+ VECTOR2I p = item->Anchor( i );
+ int d = ( p - aStart->Pos() ).EuclideanNorm();
+
+ if( d < best_dist )
+ {
+ best_dist = d;
+ best = item;
+
+ if( aAnchor )
+ *aAnchor = i;
+ }
+ }
+ }
+ }
+
+ return best;
+}
+
+
+bool PNS_TOPOLOGY::followTrivialPath( PNS_LINE* aLine, bool aLeft, PNS_ITEMSET& aSet, std::set<PNS_ITEM*>& aVisited )
+{
+ VECTOR2I anchor = aLeft ? aLine->CPoint( 0 ) : aLine->CPoint( -1 );
+ PNS_SEGMENT* last = aLeft ? aLine->LinkedSegments()->front() : aLine->LinkedSegments()->back();
+ PNS_JOINT* jt = m_world->FindJoint( anchor, aLine );
+
+ assert( jt != NULL );
+
+ aVisited.insert( last );
+
+ if( jt->IsNonFanoutVia() || jt->IsTraceWidthChange() )
+ {
+ PNS_ITEM* via = NULL;
+ PNS_SEGMENT* next_seg = NULL;
+
+ BOOST_FOREACH( PNS_ITEM* link, jt->Links().Items() )
+ {
+ if( link->OfKind( PNS_ITEM::VIA ) )
+ via = link;
+ else if( aVisited.find( link ) == aVisited.end() )
+ next_seg = static_cast<PNS_SEGMENT*>( link );
+ }
+
+ if( !next_seg )
+ return false;
+
+ PNS_LINE l = m_world->AssembleLine( next_seg );
+
+ VECTOR2I nextAnchor = ( aLeft ? l.CLine().CPoint( -1 ) : l.CLine().CPoint( 0 ) );
+
+ if( nextAnchor != anchor )
+ {
+ l.Reverse();
+ }
+
+ if( aLeft )
+ {
+ if( via )
+ aSet.Prepend( via );
+
+ aSet.Prepend( l );
+ }
+ else
+ {
+ if( via )
+ aSet.Add( via );
+
+ aSet.Add( l );
+ }
+
+ return followTrivialPath( &l, aLeft, aSet, aVisited );
+ }
+
+ return false;
+}
+
+
+const PNS_ITEMSET PNS_TOPOLOGY::AssembleTrivialPath( PNS_SEGMENT* aStart )
+{
+ PNS_ITEMSET path;
+ std::set<PNS_ITEM*> visited;
+
+ PNS_LINE l = m_world->AssembleLine( aStart );
+
+ path.Add( l );
+
+ followTrivialPath( &l, false, path, visited );
+ followTrivialPath( &l, true, path, visited );
+
+ return path;
+}
+
+
+const PNS_ITEMSET PNS_TOPOLOGY::ConnectedItems( PNS_JOINT* aStart, int aKindMask )
+{
+ return PNS_ITEMSET();
+}
+
+
+const PNS_ITEMSET PNS_TOPOLOGY::ConnectedItems( PNS_ITEM* aStart, int aKindMask )
+{
+ return PNS_ITEMSET();
+}
+
+
+int PNS_TOPOLOGY::MatchDpSuffix( wxString aNetName, wxString& aComplementNet, wxString& aBaseDpName )
+{
+ int rv = 0;
+
+ if( aNetName.EndsWith( "+" ) )
+ {
+ aComplementNet = "-";
+ rv = 1;
+ }
+ else if( aNetName.EndsWith( "_P" ) )
+ {
+ aComplementNet = "_N";
+ rv = 1;
+ }
+ else if( aNetName.EndsWith( "-" ) )
+ {
+ aComplementNet = "+";
+ rv = -1;
+ }
+ else if( aNetName.EndsWith( "_N" ) )
+ {
+ aComplementNet = "_P";
+ rv = -1;
+ }
+
+ if( rv != 0 )
+ {
+ aBaseDpName = aNetName.Left( aNetName.Length() - aComplementNet.Length() );
+ aComplementNet = aBaseDpName + aComplementNet;
+ }
+
+ return rv;
+}
+
+
+int PNS_TOPOLOGY::DpCoupledNet( int aNet )
+{
+ BOARD* brd = PNS_ROUTER::GetInstance()->GetBoard();
+
+ wxString refName = brd->FindNet( aNet )->GetNetname();
+ wxString dummy, coupledNetName;
+
+ if( MatchDpSuffix( refName, coupledNetName, dummy ) )
+ {
+ NETINFO_ITEM* net = brd->FindNet( coupledNetName );
+
+ if( !net )
+ return -1;
+
+ return net->GetNet();
+
+ }
+
+ return -1;
+}
+
+
+int PNS_TOPOLOGY::DpNetPolarity( int aNet )
+{
+ BOARD* brd = PNS_ROUTER::GetInstance()->GetBoard();
+
+ wxString refName = brd->FindNet( aNet )->GetNetname();
+ wxString dummy1, dummy2;
+
+ return MatchDpSuffix( refName, dummy1, dummy2 );
+}
+
+
+bool commonParallelProjection( SEG n, SEG p, SEG &pClip, SEG& nClip );
+
+
+bool PNS_TOPOLOGY::AssembleDiffPair( PNS_ITEM* aStart, PNS_DIFF_PAIR& aPair )
+{
+ int refNet = aStart->Net();
+ int coupledNet = DpCoupledNet( refNet );
+
+ if( coupledNet < 0 )
+ return false;
+
+ std::set<PNS_ITEM*> coupledItems;
+
+ m_world->AllItemsInNet( coupledNet, coupledItems );
+
+ PNS_SEGMENT* coupledSeg = NULL, *refSeg;
+ int minDist = std::numeric_limits<int>::max();
+
+ if( ( refSeg = dyn_cast<PNS_SEGMENT*>( aStart ) ) != NULL )
+ {
+ BOOST_FOREACH( PNS_ITEM* item, coupledItems )
+ {
+ if( PNS_SEGMENT* s = dyn_cast<PNS_SEGMENT*>( item ) )
+ {
+ if( s->Layers().Start() == refSeg->Layers().Start() && s->Width() == refSeg->Width() )
+ {
+ int dist = s->Seg().Distance( refSeg->Seg() );
+ bool isParallel = refSeg->Seg().ApproxParallel( s->Seg() );
+ SEG p_clip, n_clip;
+
+ bool isCoupled = commonParallelProjection( refSeg->Seg(), s->Seg(), p_clip, n_clip );
+
+ if( isParallel && isCoupled && dist < minDist )
+ {
+ minDist = dist;
+ coupledSeg = s;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ if( !coupledSeg )
+ return false;
+
+ PNS_LINE lp = m_world->AssembleLine( refSeg );
+ PNS_LINE ln = m_world->AssembleLine( coupledSeg );
+
+ if( DpNetPolarity( refNet ) < 0 )
+ {
+ std::swap( lp, ln );
+ }
+
+ int gap = -1;
+
+ if( refSeg->Seg().ApproxParallel( coupledSeg->Seg() ) )
+ {
+ // Segments are parallel -> compute pair gap
+ const VECTOR2I refDir = refSeg->Anchor( 1 ) - refSeg->Anchor( 0 );
+ const VECTOR2I displacement = refSeg->Anchor( 1 ) - coupledSeg->Anchor( 1 );
+ gap = (int) std::abs( refDir.Cross( displacement ) / refDir.EuclideanNorm() ) - lp.Width();
+ }
+
+ aPair = PNS_DIFF_PAIR( lp, ln );
+ aPair.SetWidth( lp.Width() );
+ aPair.SetLayers( lp.Layers() );
+ aPair.SetGap( gap );
+
+ return true;
+}
+
+const std::set<PNS_ITEM*> PNS_TOPOLOGY::AssembleCluster( PNS_ITEM* aStart, int aLayer )
+{
+ std::set<PNS_ITEM*> visited;
+ std::deque<PNS_ITEM*> pending;
+
+ pending.push_back( aStart );
+
+ while( !pending.empty() )
+ {
+ PNS_NODE::OBSTACLES obstacles;
+ PNS_ITEM* top = pending.front();
+
+ pending.pop_front();
+
+ visited.insert( top );
+
+ m_world->QueryColliding( top, obstacles, PNS_ITEM::ANY, -1, false, 0 );
+
+ BOOST_FOREACH( PNS_OBSTACLE& obs, obstacles )
+ {
+ if( visited.find( obs.m_item ) == visited.end() && obs.m_item->Layers().Overlaps( aLayer ) && !( obs.m_item->Marker() & MK_HEAD ) )
+ {
+ visited.insert( obs.m_item );
+ pending.push_back( obs.m_item );
+ }
+ }
+ }
+
+ return visited;
+}
diff --git a/pcbnew/router/pns_topology.h b/pcbnew/router/pns_topology.h
new file mode 100644
index 0000000..bbc36a8
--- /dev/null
+++ b/pcbnew/router/pns_topology.h
@@ -0,0 +1,72 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __PNS_TOPOLOGY_H
+#define __PNS_TOPOLOGY_H
+
+#include <vector>
+#include <set>
+
+#include "pns_itemset.h"
+
+class PNS_NODE;
+class PNS_SEGMENT;
+class PNS_JOINT;
+class PNS_ITEM;
+class PNS_SOLID;
+class PNS_DIFF_PAIR;
+
+class PNS_TOPOLOGY
+{
+public:
+ typedef std::set<PNS_JOINT*> JOINT_SET;
+
+ PNS_TOPOLOGY( PNS_NODE* aNode ):
+ m_world( aNode ) {};
+
+ ~PNS_TOPOLOGY() {};
+
+ bool SimplifyLine( PNS_LINE *aLine );
+ PNS_ITEM* NearestUnconnectedItem( PNS_JOINT* aStart, int* aAnchor = NULL, int aKindMask = PNS_ITEM::ANY );
+ bool LeadingRatLine( const PNS_LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine );
+
+ const JOINT_SET ConnectedJoints( PNS_JOINT* aStart );
+ const PNS_ITEMSET ConnectedItems( PNS_JOINT* aStart, int aKindMask = PNS_ITEM::ANY );
+ const PNS_ITEMSET ConnectedItems( PNS_ITEM* aStart, int aKindMask = PNS_ITEM::ANY );
+ int64_t ShortestConnectionLength( PNS_ITEM* aFrom, PNS_ITEM* aTo );
+
+ const PNS_ITEMSET AssembleTrivialPath( PNS_SEGMENT* aStart );
+ const PNS_DIFF_PAIR AssembleDiffPair( PNS_SEGMENT* aStart );
+
+ int MatchDpSuffix( wxString aNetName, wxString& aComplementNet, wxString& aBaseDpName );
+ int DpCoupledNet( int aNet );
+ int DpNetPolarity( int aNet );
+ const PNS_LINE DpCoupledLine( PNS_LINE* aLine );
+ bool AssembleDiffPair( PNS_ITEM* aStart, PNS_DIFF_PAIR& aPair );
+
+ const std::set<PNS_ITEM*> AssembleCluster( PNS_ITEM* aStart, int aLayer );
+
+private:
+ bool followTrivialPath( PNS_LINE* aLine, bool aLeft, PNS_ITEMSET& aSet, std::set<PNS_ITEM*>& aVisited );
+
+ PNS_NODE *m_world;
+};
+
+#endif
diff --git a/pcbnew/router/pns_tune_status_popup.cpp b/pcbnew/router/pns_tune_status_popup.cpp
new file mode 100644
index 0000000..40be366
--- /dev/null
+++ b/pcbnew/router/pns_tune_status_popup.cpp
@@ -0,0 +1,67 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014-2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "pns_tune_status_popup.h"
+#include "pns_router.h"
+#include "pns_meander_placer.h"
+
+PNS_TUNE_STATUS_POPUP::PNS_TUNE_STATUS_POPUP( PCB_EDIT_FRAME* aParent ) :
+ WX_STATUS_POPUP( aParent )
+{
+ m_panel->SetBackgroundColour( wxColour( 64, 64, 64 ) );
+ m_statusLine = new wxStaticText( m_panel, wxID_ANY, wxEmptyString ) ;
+ m_topSizer->Add( m_statusLine, 1, wxALL | wxEXPAND, 5 );
+}
+
+
+PNS_TUNE_STATUS_POPUP::~PNS_TUNE_STATUS_POPUP()
+{
+}
+
+
+void PNS_TUNE_STATUS_POPUP::UpdateStatus( PNS_ROUTER* aRouter )
+{
+ PNS_MEANDER_PLACER_BASE* placer = dynamic_cast<PNS_MEANDER_PLACER_BASE*>( aRouter->Placer() );
+
+ if( !placer )
+ return;
+
+ m_statusLine->SetLabel( placer->TuningInfo() );
+
+ wxColour color;
+
+ switch( placer->TuningStatus() )
+ {
+ case PNS_MEANDER_PLACER::TUNED:
+ color = wxColour( 0, 255, 0 );
+ break;
+ case PNS_MEANDER_PLACER::TOO_SHORT:
+ color = wxColour( 255, 128, 128 );
+ break;
+ case PNS_MEANDER_PLACER::TOO_LONG:
+ color = wxColour( 128, 128, 255 );
+ break;
+ }
+
+ m_statusLine->SetForegroundColour( color );
+
+ updateSize();
+}
+
diff --git a/pcbnew/router/pns_tune_status_popup.h b/pcbnew/router/pns_tune_status_popup.h
new file mode 100644
index 0000000..c6fe469
--- /dev/null
+++ b/pcbnew/router/pns_tune_status_popup.h
@@ -0,0 +1,44 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef __PNS_TUNE_STATUS_POPUP_H_
+#define __PNS_TUNE_STATUS_POPUP_H_
+
+#include <wx_status_popup.h>
+
+class PNS_ROUTER;
+
+class PNS_TUNE_STATUS_POPUP : public WX_STATUS_POPUP
+{
+public:
+ PNS_TUNE_STATUS_POPUP( PCB_EDIT_FRAME* aParent );
+ ~PNS_TUNE_STATUS_POPUP();
+
+ void UpdateStatus( PNS_ROUTER* aRouter );
+
+private:
+ wxStaticText* m_statusLine;
+};
+
+#endif /* __PNS_TUNE_STATUS_POPUP_H_*/
diff --git a/pcbnew/router/pns_utils.cpp b/pcbnew/router/pns_utils.cpp
new file mode 100644
index 0000000..31eb78d
--- /dev/null
+++ b/pcbnew/router/pns_utils.cpp
@@ -0,0 +1,244 @@
+/*
+ * 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_utils.h"
+#include "pns_line.h"
+#include "pns_via.h"
+#include "pns_router.h"
+
+#include <geometry/shape_segment.h>
+
+const SHAPE_LINE_CHAIN OctagonalHull( const VECTOR2I& aP0, const VECTOR2I& aSize,
+ int aClearance, int aChamfer )
+{
+ SHAPE_LINE_CHAIN s;
+
+ s.SetClosed( true );
+
+ s.Append( aP0.x - aClearance, aP0.y - aClearance + aChamfer );
+ s.Append( aP0.x - aClearance + aChamfer, aP0.y - aClearance );
+ s.Append( aP0.x + aSize.x + aClearance - aChamfer, aP0.y - aClearance );
+ s.Append( aP0.x + aSize.x + aClearance, aP0.y - aClearance + aChamfer );
+ s.Append( aP0.x + aSize.x + aClearance, aP0.y + aSize.y + aClearance - aChamfer );
+ s.Append( aP0.x + aSize.x + aClearance - aChamfer, aP0.y + aSize.y + aClearance );
+ s.Append( aP0.x - aClearance + aChamfer, aP0.y + aSize.y + aClearance );
+ s.Append( aP0.x - aClearance, aP0.y + aSize.y + aClearance - aChamfer );
+
+ 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;
+}
+
+
+static void MoveDiagonal( SEG& aDiagonal, const SHAPE_LINE_CHAIN& aVertices, int aClearance )
+{
+ int dist;
+
+ aVertices.NearestPoint( aDiagonal, dist );
+ dist -= HULL_MARGIN;
+ VECTOR2I moveBy = ( aDiagonal.A - aDiagonal.B ).Perpendicular().Resize( dist - aClearance );
+ aDiagonal.A += moveBy;
+ aDiagonal.B += moveBy;
+}
+
+
+const SHAPE_LINE_CHAIN ConvexHull( const SHAPE_CONVEX& convex, int aClearance )
+{
+ // this defines the horizontal and vertical lines in the hull octagon
+ BOX2I box = convex.BBox( aClearance + HULL_MARGIN );
+ box.Normalize();
+
+ SEG topline = SEG( VECTOR2I( box.GetX(), box.GetY() + box.GetHeight() ),
+ VECTOR2I( box.GetX() + box.GetWidth(), box.GetY() + box.GetHeight() ) );
+ SEG rightline = SEG( VECTOR2I( box.GetX() + box.GetWidth(), box.GetY() + box.GetHeight() ),
+ VECTOR2I( box.GetX() + box.GetWidth(), box.GetY() ) );
+ SEG bottomline = SEG( VECTOR2I( box.GetX() + box.GetWidth(), box.GetY() ),
+ box.GetOrigin() );
+ SEG leftline = SEG( box.GetOrigin(), VECTOR2I( box.GetX(), box.GetY() + box.GetHeight() ) );
+
+ const SHAPE_LINE_CHAIN& vertices = convex.Vertices();
+
+ // top right diagonal
+ VECTOR2I corner = box.GetOrigin() + box.GetSize();
+ SEG toprightline = SEG( corner,
+ corner + VECTOR2I( box.GetHeight(), -box.GetHeight() ) );
+ MoveDiagonal( toprightline, vertices, aClearance );
+
+ // bottom right diagonal
+ corner = box.GetOrigin() + VECTOR2I( box.GetWidth(), 0 );
+ SEG bottomrightline = SEG( corner + VECTOR2I( box.GetHeight(), box.GetHeight() ),
+ corner );
+ MoveDiagonal( bottomrightline, vertices, aClearance );
+
+ // bottom left diagonal
+ corner = box.GetOrigin();
+ SEG bottomleftline = SEG( corner,
+ corner + VECTOR2I( -box.GetHeight(), box.GetHeight() ) );
+ MoveDiagonal( bottomleftline, vertices, aClearance );
+
+ // top left diagonal
+ corner = box.GetOrigin() + VECTOR2I( 0, box.GetHeight() );
+ SEG topleftline = SEG( corner + VECTOR2I( -box.GetHeight(), -box.GetHeight() ),
+ corner );
+ MoveDiagonal( topleftline, vertices, aClearance );
+
+ SHAPE_LINE_CHAIN octagon;
+ octagon.SetClosed( true );
+
+ octagon.Append( leftline.IntersectLines( bottomleftline ).get() );
+ octagon.Append( bottomline.IntersectLines( bottomleftline ).get() );
+ octagon.Append( bottomline.IntersectLines( bottomrightline ).get() );
+ octagon.Append( rightline.IntersectLines( bottomrightline ).get() );
+ octagon.Append( rightline.IntersectLines( toprightline ).get() );
+ octagon.Append( topline.IntersectLines( toprightline ).get() );
+ octagon.Append( topline.IntersectLines( topleftline ).get() );
+ octagon.Append( leftline.IntersectLines( topleftline ).get() );
+
+ return octagon;
+}
+
+
+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 ) );
+}
+
+
+void DrawDebugPoint( VECTOR2I aP, int aColor )
+{
+ SHAPE_LINE_CHAIN l;
+
+ l.Append( aP - VECTOR2I( -50000, -50000 ) );
+ l.Append( aP + VECTOR2I( -50000, -50000 ) );
+
+ PNS_ROUTER::GetInstance()->DisplayDebugLine ( l, aColor, 10000 );
+
+ l.Clear();
+ l.Append( aP - VECTOR2I( 50000, -50000 ) );
+ l.Append( aP + VECTOR2I( 50000, -50000 ) );
+
+ PNS_ROUTER::GetInstance()->DisplayDebugLine( l, aColor, 10000 );
+}
+
+
+void DrawDebugBox( BOX2I aB, int aColor )
+{
+ SHAPE_LINE_CHAIN l;
+
+ VECTOR2I o = aB.GetOrigin();
+ VECTOR2I s = aB.GetSize();
+
+ l.Append( o );
+ l.Append( o.x + s.x, o.y );
+ l.Append( o.x + s.x, o.y + s.y );
+ l.Append( o.x, o.y + s.y );
+ l.Append( o );
+
+ PNS_ROUTER::GetInstance()->DisplayDebugLine( l, aColor, 10000 );
+}
+
+
+void DrawDebugSeg( SEG aS, int aColor )
+{
+ SHAPE_LINE_CHAIN l;
+
+ l.Append( aS.A );
+ l.Append( aS.B );
+
+ PNS_ROUTER::GetInstance()->DisplayDebugLine( l, aColor, 10000 );
+}
+
+
+void DrawDebugDirs( VECTOR2D aP, int aMask, int aColor )
+{
+ BOX2I b( aP - VECTOR2I( 10000, 10000 ), VECTOR2I( 20000, 20000 ) );
+
+ DrawDebugBox( b, aColor );
+ for( int i = 0; i < 8; i++ )
+ {
+ if( ( 1 << i ) & aMask )
+ {
+ VECTOR2I v = DIRECTION_45( ( DIRECTION_45::Directions ) i ).ToVector() * 100000;
+ DrawDebugSeg( SEG( aP, aP + v ), aColor );
+ }
+ }
+}
+
+
+OPT_BOX2I ChangedArea( const PNS_ITEM* aItemA, const PNS_ITEM* aItemB )
+{
+ if( aItemA->OfKind( PNS_ITEM::VIA ) && aItemB->OfKind( PNS_ITEM::VIA ) )
+ {
+ const PNS_VIA* va = static_cast<const PNS_VIA*>( aItemA );
+ const PNS_VIA* vb = static_cast<const PNS_VIA*>( aItemB );
+
+ return va->ChangedArea( vb );
+ }
+ else if( aItemA->OfKind( PNS_ITEM::LINE ) && aItemB->OfKind( PNS_ITEM::LINE ) )
+ {
+ const PNS_LINE* la = static_cast<const PNS_LINE*> ( aItemA );
+ const PNS_LINE* lb = static_cast<const PNS_LINE*> ( aItemB );
+
+ return la->ChangedArea( lb );
+ }
+
+ return OPT_BOX2I();
+}
diff --git a/pcbnew/router/pns_utils.h b/pcbnew/router/pns_utils.h
new file mode 100644
index 0000000..bc300a9
--- /dev/null
+++ b/pcbnew/router/pns_utils.h
@@ -0,0 +1,62 @@
+/*
+ * 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_UTILS_H
+#define __PNS_UTILS_H
+
+#include <math/vector2d.h>
+#include <math/box2.h>
+#include <geometry/shape_line_chain.h>
+#include <geometry/shape_segment.h>
+#include <geometry/shape_rect.h>
+#include <geometry/shape_convex.h>
+
+#define HULL_MARGIN 10
+
+class PNS_ITEM;
+
+/** Various utility functions */
+
+const SHAPE_LINE_CHAIN OctagonalHull( const VECTOR2I& aP0, const VECTOR2I& aSize,
+ int aClearance, int aChamfer );
+
+const SHAPE_LINE_CHAIN SegmentHull( const SHAPE_SEGMENT& aSeg, int aClearance,
+ int aWalkaroundThickness );
+
+/**
+ * Function ConvexHull()
+ *
+ * Creates an octagonal hull around a convex polygon.
+ * @param convex The convex polygon.
+ * @param clearance The minimum distance between polygon and hull.
+ * @return A closed line chain describing the octagon.
+ */
+const SHAPE_LINE_CHAIN ConvexHull( const SHAPE_CONVEX& convex, int aClearance );
+
+SHAPE_RECT ApproximateSegmentAsRect( const SHAPE_SEGMENT& aSeg );
+
+void DrawDebugPoint( VECTOR2I aP, int aColor );
+void DrawDebugBox( BOX2I aB, int aColor );
+void DrawDebugSeg( SEG aS, int aColor );
+void DrawDebugDirs( VECTOR2D aP, int aMask, int aColor );
+
+OPT_BOX2I ChangedArea( const PNS_ITEM* aItemA, const PNS_ITEM* aItemB );
+
+#endif // __PNS_UTILS_H
diff --git a/pcbnew/router/pns_via.cpp b/pcbnew/router/pns_via.cpp
new file mode 100644
index 0000000..e090139
--- /dev/null
+++ b/pcbnew/router/pns_via.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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_via.h"
+#include "pns_node.h"
+#include "pns_utils.h"
+#include "pns_router.h"
+
+#include <geometry/shape_rect.h>
+
+bool PNS_VIA::PushoutForce( PNS_NODE* aNode, const VECTOR2I& aDirection, VECTOR2I& aForce,
+ bool aSolidsOnly, int aMaxIterations )
+{
+ int iter = 0;
+ PNS_VIA mv( *this );
+ VECTOR2I force, totalForce, force2;
+
+ while( iter < aMaxIterations )
+ {
+ PNS_NODE::OPT_OBSTACLE obs = aNode->CheckColliding( &mv,
+ aSolidsOnly ? PNS_ITEM::SOLID : PNS_ITEM::ANY );
+
+ if( !obs )
+ break;
+
+ int clearance = aNode->GetClearance( obs->m_item, &mv );
+
+ if( iter > aMaxIterations / 2 )
+ {
+ VECTOR2I l = aDirection.Resize( m_diameter / 2 );
+ totalForce += l;
+ mv.SetPos( mv.Pos() + l );
+ }
+
+ bool col = CollideShapes( obs->m_item->Shape(), mv.Shape(), clearance, true, force2 );
+
+ if( col ) {
+ totalForce += force2;
+ mv.SetPos( mv.Pos() + force2 );
+ }
+
+ iter++;
+ }
+
+ if( iter == aMaxIterations )
+ return false;
+
+ aForce = totalForce;
+
+ return true;
+}
+
+
+const SHAPE_LINE_CHAIN PNS_VIA::Hull( int aClearance, int aWalkaroundThickness ) const
+{
+ int cl = ( aClearance + aWalkaroundThickness / 2 );
+
+ return OctagonalHull( m_pos -
+ VECTOR2I( m_diameter / 2, m_diameter / 2 ), VECTOR2I( m_diameter, 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_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 );
+ v->m_rank = m_rank;
+ v->m_marker = m_marker;
+ v->m_viaType = m_viaType;
+
+ return v;
+}
+
+
+OPT_BOX2I PNS_VIA::ChangedArea( const PNS_VIA* aOther ) const
+{
+ if ( aOther->Pos() != Pos() )
+ {
+ BOX2I tmp = Shape()->BBox();
+ tmp.Merge( aOther->Shape()->BBox() );
+ return tmp;
+ }
+
+ return OPT_BOX2I();
+}
diff --git a/pcbnew/router/pns_via.h b/pcbnew/router/pns_via.h
new file mode 100644
index 0000000..97c9954
--- /dev/null
+++ b/pcbnew/router/pns_via.h
@@ -0,0 +1,162 @@
+/*
+ * 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_VIA_H
+#define __PNS_VIA_H
+
+#include <geometry/shape_line_chain.h>
+#include <geometry/shape_circle.h>
+
+#include "../class_track.h"
+
+#include "pns_item.h"
+
+class PNS_NODE;
+
+class PNS_VIA : public PNS_ITEM
+{
+public:
+ PNS_VIA() :
+ PNS_ITEM( VIA )
+ {
+ m_diameter = 2; // Dummy value
+ m_drill = 0;
+ m_viaType = VIA_THROUGH;
+ }
+
+ PNS_VIA( const VECTOR2I& aPos, const PNS_LAYERSET& aLayers,
+ int aDiameter, int aDrill, int aNet = -1, VIATYPE_T aViaType = VIA_THROUGH ) :
+ PNS_ITEM( VIA )
+ {
+ SetNet( aNet );
+ SetLayers( aLayers );
+ m_pos = aPos;
+ m_diameter = aDiameter;
+ m_drill = aDrill;
+ m_shape = SHAPE_CIRCLE( aPos, aDiameter / 2 );
+ m_viaType = aViaType;
+
+ //If we're a through-board via, use all layers regardless of the set passed
+ if( aViaType == VIA_THROUGH )
+ {
+ PNS_LAYERSET allLayers( 0, MAX_CU_LAYERS - 1 );
+ SetLayers( allLayers );
+ }
+ }
+
+
+ PNS_VIA( const PNS_VIA& aB ) :
+ PNS_ITEM( VIA )
+ {
+ SetNet( aB.Net() );
+ SetLayers( aB.Layers() );
+ m_pos = aB.m_pos;
+ m_diameter = aB.m_diameter;
+ m_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 );
+ m_marker = aB.m_marker;
+ m_rank = aB.m_rank;
+ m_drill = aB.m_drill;
+ m_viaType = aB.m_viaType;
+ }
+
+ static inline bool ClassOf( const PNS_ITEM* aItem )
+ {
+ return aItem && VIA == aItem->Kind();
+ }
+
+
+ const VECTOR2I& Pos() const
+ {
+ return m_pos;
+ }
+
+ void SetPos( const VECTOR2I& aPos )
+ {
+ m_pos = aPos;
+ m_shape.SetCenter( aPos );
+ }
+
+ VIATYPE_T ViaType() const
+ {
+ return m_viaType;
+ }
+
+ void SetViaType( VIATYPE_T aViaType )
+ {
+ m_viaType = aViaType;
+ }
+
+ int Diameter() const
+ {
+ return m_diameter;
+ }
+
+ void SetDiameter( int aDiameter )
+ {
+ m_diameter = aDiameter;
+ m_shape.SetRadius( m_diameter / 2 );
+ }
+
+ int Drill() const
+ {
+ return m_drill;
+ }
+
+ void SetDrill( int aDrill )
+ {
+ m_drill = aDrill;
+ }
+
+ bool PushoutForce( PNS_NODE* aNode,
+ const VECTOR2I& aDirection,
+ VECTOR2I& aForce,
+ bool aSolidsOnly = true,
+ int aMaxIterations = 10 );
+
+ const SHAPE* Shape() const
+ {
+ return &m_shape;
+ }
+
+ PNS_VIA* Clone() const;
+
+ const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0 ) const;
+
+ virtual VECTOR2I Anchor( int n ) const
+ {
+ return m_pos;
+ }
+
+ virtual int AnchorCount() const
+ {
+ return 1;
+ }
+
+ OPT_BOX2I ChangedArea( const PNS_VIA* aOther ) const;
+
+private:
+ int m_diameter;
+ int m_drill;
+ VECTOR2I m_pos;
+ SHAPE_CIRCLE m_shape;
+ VIATYPE_T m_viaType;
+};
+
+#endif
diff --git a/pcbnew/router/pns_walkaround.cpp b/pcbnew/router/pns_walkaround.cpp
new file mode 100644
index 0000000..c7e7e19
--- /dev/null
+++ b/pcbnew/router/pns_walkaround.cpp
@@ -0,0 +1,273 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2014 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <boost/foreach.hpp>
+#include <boost/optional.hpp>
+
+#include <geometry/shape_line_chain.h>
+
+#include "pns_walkaround.h"
+#include "pns_optimizer.h"
+#include "pns_utils.h"
+#include "pns_router.h"
+using boost::optional;
+
+void PNS_WALKAROUND::start( const PNS_LINE& aInitialPath )
+{
+ m_iteration = 0;
+ m_iterationLimit = 50;
+}
+
+
+PNS_NODE::OPT_OBSTACLE PNS_WALKAROUND::nearestObstacle( const PNS_LINE& aPath )
+{
+ PNS_NODE::OPT_OBSTACLE obs = m_world->NearestObstacle( &aPath, m_itemMask, m_restrictedSet.empty() ? NULL : &m_restrictedSet );
+
+ if( m_restrictedSet.empty() )
+ return obs;
+
+ else if( obs && m_restrictedSet.find ( obs->m_item ) != m_restrictedSet.end() )
+ return obs;
+
+ return PNS_NODE::OPT_OBSTACLE();
+}
+
+
+PNS_WALKAROUND::WALKAROUND_STATUS PNS_WALKAROUND::singleStep( PNS_LINE& aPath,
+ bool aWindingDirection )
+{
+ optional<PNS_OBSTACLE>& current_obs =
+ aWindingDirection ? m_currentObstacle[0] : m_currentObstacle[1];
+
+ bool& prev_recursive = aWindingDirection ? m_recursiveCollision[0] : m_recursiveCollision[1];
+
+ if( !current_obs )
+ return DONE;
+
+ SHAPE_LINE_CHAIN path_pre[2], path_walk[2], path_post[2];
+
+ VECTOR2I last = aPath.CPoint( -1 );
+
+ if( ( current_obs->m_hull ).PointInside( last ) || ( current_obs->m_hull ).PointOnEdge( last ) )
+ {
+ m_recursiveBlockageCount++;
+
+ if( m_recursiveBlockageCount < 3 )
+ aPath.Line().Append( current_obs->m_hull.NearestPoint( last ) );
+ else
+ {
+ aPath = aPath.ClipToNearestObstacle( m_world );
+ return DONE;
+ }
+ }
+
+ aPath.Walkaround( current_obs->m_hull, path_pre[0], path_walk[0],
+ path_post[0], aWindingDirection );
+ aPath.Walkaround( current_obs->m_hull, path_pre[1], path_walk[1],
+ 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->m_hull, 2, "hull" );
+ m_logger.Log( current_obs->m_item, 3, "item" );
+#endif
+
+ int len_pre = path_walk[0].Length();
+ int len_alt = path_walk[1].Length();
+
+ PNS_LINE walk_path( aPath, path_walk[1] );
+
+ bool alt_collides = static_cast<bool>( m_world->CheckColliding( &walk_path, m_itemMask ) );
+
+ SHAPE_LINE_CHAIN pnew;
+
+ if( !m_forceLongerPath && len_alt < len_pre && !alt_collides && !prev_recursive )
+ {
+ pnew = path_pre[1];
+ pnew.Append( path_walk[1] );
+ pnew.Append( path_post[1] );
+
+ if( !path_post[1].PointCount() || !path_walk[1].PointCount() )
+ current_obs = nearestObstacle( PNS_LINE( aPath, path_pre[1] ) );
+ else
+ current_obs = nearestObstacle( PNS_LINE( aPath, path_post[1] ) );
+ prev_recursive = false;
+ }
+ else
+ {
+ pnew = path_pre[0];
+ pnew.Append( path_walk[0] );
+ pnew.Append( path_post[0] );
+
+ if( !path_post[0].PointCount() || !path_walk[0].PointCount() )
+ current_obs = nearestObstacle( PNS_LINE( aPath, path_pre[0] ) );
+ else
+ current_obs = nearestObstacle( PNS_LINE( aPath, path_walk[0] ) );
+
+ if( !current_obs )
+ {
+ prev_recursive = false;
+ current_obs = nearestObstacle( PNS_LINE( aPath, path_post[0] ) );
+ }
+ else
+ prev_recursive = true;
+ }
+
+ pnew.Simplify();
+ aPath.SetShape( pnew );
+
+ return IN_PROGRESS;
+}
+
+
+PNS_WALKAROUND::WALKAROUND_STATUS PNS_WALKAROUND::Route( const PNS_LINE& aInitialPath,
+ PNS_LINE& aWalkPath, bool aOptimize )
+{
+ PNS_LINE path_cw( aInitialPath ), path_ccw( aInitialPath );
+ WALKAROUND_STATUS s_cw = IN_PROGRESS, s_ccw = IN_PROGRESS;
+ SHAPE_LINE_CHAIN best_path;
+
+ // special case for via-in-the-middle-of-track placement
+ if( aInitialPath.PointCount() <= 1 )
+ {
+ if( aInitialPath.EndsWithVia() && m_world->CheckColliding( &aInitialPath.Via(), m_itemMask ) )
+ return STUCK;
+
+ aWalkPath = aInitialPath;
+ return DONE;
+ }
+
+ start( aInitialPath );
+
+ m_currentObstacle[0] = m_currentObstacle[1] = nearestObstacle( aInitialPath );
+ m_recursiveBlockageCount = 0;
+
+ aWalkPath = aInitialPath;
+
+ if( m_forceWinding )
+ {
+ s_cw = m_forceCw ? IN_PROGRESS : STUCK;
+ s_ccw = m_forceCw ? STUCK : IN_PROGRESS;
+ m_forceSingleDirection = true;
+ } else {
+ m_forceSingleDirection = false;
+ }
+
+ while( m_iteration < m_iterationLimit )
+ {
+ if( s_cw != STUCK )
+ s_cw = singleStep( path_cw, true );
+
+ if( s_ccw != STUCK )
+ s_ccw = singleStep( path_ccw, false );
+
+ if( ( s_cw == DONE && s_ccw == DONE ) || ( s_cw == STUCK && s_ccw == STUCK ) )
+ {
+ int len_cw = path_cw.CLine().Length();
+ int len_ccw = path_ccw.CLine().Length();
+
+ if( m_forceLongerPath )
+ aWalkPath = ( len_cw > len_ccw ? path_cw : path_ccw );
+ else
+ aWalkPath = ( len_cw < len_ccw ? path_cw : path_ccw );
+
+ break;
+ }
+ else if( s_cw == DONE && !m_forceLongerPath )
+ {
+ aWalkPath = path_cw;
+ break;
+ }
+ else if( s_ccw == DONE && !m_forceLongerPath )
+ {
+ aWalkPath = path_ccw;
+ break;
+ }
+
+ m_iteration++;
+ }
+
+ if( m_iteration == m_iterationLimit )
+ {
+ int len_cw = path_cw.CLine().Length();
+ int len_ccw = path_ccw.CLine().Length();
+
+ if( m_forceLongerPath )
+ aWalkPath = ( len_cw > len_ccw ? path_cw : path_ccw );
+ else
+ aWalkPath = ( len_cw < len_ccw ? path_cw : path_ccw );
+ }
+
+ if( m_cursorApproachMode )
+ {
+ // int len_cw = path_cw.GetCLine().Length();
+ // int len_ccw = path_ccw.GetCLine().Length();
+ bool found = false;
+
+ SHAPE_LINE_CHAIN l = aWalkPath.CLine();
+
+ for( int i = 0; i < l.SegmentCount(); i++ )
+ {
+ const SEG s = l.Segment( i );
+
+ VECTOR2I nearest = s.NearestPoint( m_cursorPos );
+ VECTOR2I::extended_type dist_a = ( s.A - m_cursorPos ).SquaredEuclideanNorm();
+ VECTOR2I::extended_type dist_b = ( s.B - m_cursorPos ).SquaredEuclideanNorm();
+ VECTOR2I::extended_type dist_n = ( nearest - m_cursorPos ).SquaredEuclideanNorm();
+
+ if( dist_n <= dist_a && dist_n < dist_b )
+ {
+ l.Remove( i + 1, -1 );
+ l.Append( nearest );
+ l.Simplify();
+ found = true;
+ break;
+ }
+ }
+
+ if( found )
+ {
+ aWalkPath = aInitialPath;
+ aWalkPath.SetShape( l );
+ }
+ }
+
+ aWalkPath.Line().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;
+
+ WALKAROUND_STATUS st = s_ccw == DONE || s_cw == DONE ? DONE : STUCK;
+
+ if( st == DONE )
+ {
+ if( aOptimize )
+ PNS_OPTIMIZER::Optimize( &aWalkPath, PNS_OPTIMIZER::MERGE_OBTUSE, m_world );
+ }
+
+ return st;
+}
diff --git a/pcbnew/router/pns_walkaround.h b/pcbnew/router/pns_walkaround.h
new file mode 100644
index 0000000..f143b46
--- /dev/null
+++ b/pcbnew/router/pns_walkaround.h
@@ -0,0 +1,149 @@
+/*
+ * 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_WALKAROUND_H
+#define __PNS_WALKAROUND_H
+
+#include <set>
+
+#include "pns_line.h"
+#include "pns_node.h"
+#include "pns_router.h"
+#include "pns_logger.h"
+#include "pns_algo_base.h"
+
+class PNS_WALKAROUND : public PNS_ALGO_BASE
+{
+ static const int DefaultIterationLimit = 50;
+
+public:
+ PNS_WALKAROUND( PNS_NODE* aWorld, PNS_ROUTER* aRouter ) :
+ PNS_ALGO_BASE ( aRouter ),
+ m_world( aWorld ),
+ m_iterationLimit( DefaultIterationLimit )
+ {
+ m_forceSingleDirection = false;
+ m_forceLongerPath = false;
+ m_forceWinding = false;
+ m_cursorApproachMode = false;
+ m_itemMask = PNS_ITEM::ANY;
+
+ // Initialize other members, to avoid uninitialized variables.
+ m_recursiveBlockageCount = 0;
+ m_recursiveCollision[0] = m_recursiveCollision[1] = false;
+ m_iteration = 0;
+ m_forceCw = false;
+ }
+
+ ~PNS_WALKAROUND() {};
+
+ enum WALKAROUND_STATUS
+ {
+ IN_PROGRESS = 0,
+ DONE,
+ STUCK
+ };
+
+ void SetWorld( PNS_NODE* aNode )
+ {
+ m_world = aNode;
+ }
+
+ void SetIterationLimit( const int aIterLimit )
+ {
+ m_iterationLimit = aIterLimit;
+ }
+
+ void SetSolidsOnly( bool aSolidsOnly )
+ {
+ if( aSolidsOnly )
+ m_itemMask = PNS_ITEM::SOLID;
+ else
+ m_itemMask = PNS_ITEM::ANY;
+ }
+
+ void SetItemMask( int aMask )
+ {
+ m_itemMask = aMask;
+ }
+
+ void SetSingleDirection( bool aForceSingleDirection )
+ {
+ m_forceSingleDirection = aForceSingleDirection;
+ m_forceLongerPath = aForceSingleDirection;
+ }
+
+ void SetSingleDirection2( bool aForceSingleDirection )
+ {
+ m_forceSingleDirection = aForceSingleDirection;
+ }
+
+ void SetApproachCursor( bool aEnabled, const VECTOR2I& aPos )
+ {
+ m_cursorPos = aPos;
+ m_cursorApproachMode = aEnabled;
+ }
+
+ void SetForceWinding ( bool aEnabled, bool aCw )
+ {
+ m_forceCw = aCw;
+ m_forceWinding = aEnabled;
+ }
+
+ void RestrictToSet( bool aEnabled, const std::set<PNS_ITEM*>& aSet )
+ {
+ if( aEnabled )
+ m_restrictedSet = aSet;
+ else
+ m_restrictedSet.clear();
+ }
+
+ WALKAROUND_STATUS Route( const PNS_LINE& aInitialPath, PNS_LINE& aWalkPath,
+ bool aOptimize = true );
+
+ virtual PNS_LOGGER* Logger()
+ {
+ return &m_logger;
+ }
+
+private:
+ void start( const PNS_LINE& aInitialPath );
+
+ WALKAROUND_STATUS singleStep( PNS_LINE& aPath, bool aWindingDirection );
+ PNS_NODE::OPT_OBSTACLE nearestObstacle( const PNS_LINE& aPath );
+
+ PNS_NODE* m_world;
+
+ int m_recursiveBlockageCount;
+ int m_iteration;
+ int m_iterationLimit;
+ int m_itemMask;
+ bool m_forceSingleDirection, m_forceLongerPath;
+ bool m_cursorApproachMode;
+ bool m_forceWinding;
+ bool m_forceCw;
+ VECTOR2I m_cursorPos;
+ PNS_NODE::OPT_OBSTACLE m_currentObstacle[2];
+ bool m_recursiveCollision[2];
+ PNS_LOGGER m_logger;
+ std::set<PNS_ITEM*> m_restrictedSet;
+};
+
+#endif // __PNS_WALKAROUND_H
diff --git a/pcbnew/router/range.h b/pcbnew/router/range.h
new file mode 100644
index 0000000..5b47c74
--- /dev/null
+++ b/pcbnew/router/range.h
@@ -0,0 +1,93 @@
+/*
+ * 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 aValue )
+ {
+ if( !m_defined )
+ {
+ m_min = aValue;
+ m_max = aValue;
+ m_defined = true;
+ }
+ else
+ {
+ m_min = std::min( m_min, aValue );
+ m_max = std::max( m_max, aValue );
+ }
+ }
+
+ bool Inside( const T& aValue ) const
+ {
+ if( !m_defined )
+ return true;
+
+ return aValue >= m_min && aValue <= 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
diff --git a/pcbnew/router/ranged_num.h b/pcbnew/router/ranged_num.h
new file mode 100644
index 0000000..5eadaf6
--- /dev/null
+++ b/pcbnew/router/ranged_num.h
@@ -0,0 +1,52 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2015 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __RANGED_NUM_H
+#define __RANGED_NUM_H
+
+template <class T> class RANGED_NUM {
+ public:
+ RANGED_NUM( T aValue = 0, T aTolerancePlus = 0, T aToleranceMinus = 0 ) :
+ m_value( aValue ),
+ m_tolerancePlus( aTolerancePlus ),
+ m_toleranceMinus( aToleranceMinus )
+ {}
+
+ operator T()
+ {
+ return m_value;
+ }
+
+ RANGED_NUM& operator=( const T aValue )
+ {
+ m_value = aValue;
+ return *this;
+ }
+
+ bool Matches( const T& aOther ) const
+ {
+ return ( aOther >= m_value - m_toleranceMinus && aOther <= m_value + m_tolerancePlus );
+ }
+
+ private:
+ T m_value, m_tolerancePlus, m_toleranceMinus;
+};
+
+#endif
diff --git a/pcbnew/router/router_preview_item.cpp b/pcbnew/router/router_preview_item.cpp
new file mode 100644
index 0000000..c490ae4
--- /dev/null
+++ b/pcbnew/router/router_preview_item.cpp
@@ -0,0 +1,343 @@
+/*
+ * 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 <deque>
+#include <gal/color4d.h>
+
+#include <geometry/shape_rect.h>
+#include <geometry/shape_convex.h>
+
+#include "class_track.h"
+#include <pcb_painter.h>
+#include <colors.h>
+
+#include "router_preview_item.h"
+
+#include "pns_line.h"
+#include "pns_segment.h"
+#include "pns_via.h"
+
+using namespace KIGFX;
+
+ROUTER_PREVIEW_ITEM::ROUTER_PREVIEW_ITEM( const PNS_ITEM* aItem, VIEW_GROUP* aParent ) :
+ EDA_ITEM( NOT_USED )
+{
+ m_parent = aParent;
+
+ m_shape = NULL;
+ m_clearance = -1;
+ m_originLayer = m_layer = ITEM_GAL_LAYER( GP_OVERLAY );
+
+ // initialize variables, overwritten by Update( aItem ), if aItem != NULL
+ m_router = NULL;
+ m_type = PR_SHAPE;
+ m_style = 0;
+ m_width = 0;
+ m_depth = 0;
+
+ if( aItem )
+ Update( aItem );
+}
+
+
+ROUTER_PREVIEW_ITEM::~ROUTER_PREVIEW_ITEM()
+{
+ delete m_shape;
+}
+
+
+void ROUTER_PREVIEW_ITEM::Update( const PNS_ITEM* aItem )
+{
+ m_originLayer = aItem->Layers().Start();
+
+ if( aItem->OfKind( PNS_ITEM::LINE ) )
+ {
+ const PNS_LINE* l = static_cast<const PNS_LINE*>( aItem );
+
+ if( !l->SegmentCount() )
+ return;
+ }
+
+ assert( m_originLayer >= 0 );
+
+ m_layer = m_originLayer;
+ m_color = getLayerColor( m_originLayer );
+ m_color.a = 0.8;
+ m_depth = BaseOverlayDepth - aItem->Layers().Start();
+ 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_originLayer = m_layer = ITEM_GAL_LAYER( VIAS_VISIBLE );
+ 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:
+ 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 );
+ ViewUpdate( GEOMETRY | APPEARANCE );
+}
+
+
+const BOX2I ROUTER_PREVIEW_ITEM::ViewBBox() const
+{
+ BOX2I bbox;
+
+ switch( m_type )
+ {
+ case PR_SHAPE:
+ bbox = m_shape->BBox();
+ bbox.Inflate( m_width / 2 );
+ return bbox;
+
+ case PR_POINT:
+ bbox = BOX2I ( m_pos - VECTOR2I( 100000, 100000 ), VECTOR2I( 200000, 200000 ) );
+ return bbox;
+
+ default:
+ break;
+ }
+
+ return bbox;
+}
+
+
+void ROUTER_PREVIEW_ITEM::drawLineChain( const SHAPE_LINE_CHAIN& aL, KIGFX::GAL* aGal ) const
+{
+ for( int s = 0; s < aL.SegmentCount(); s++ )
+ aGal->DrawLine( aL.CSegment( s ).A, aL.CSegment( s ).B );
+
+ if( aL.IsClosed() )
+ aGal->DrawLine( aL.CSegment( -1 ).B, aL.CSegment( 0 ).A );
+}
+
+
+void ROUTER_PREVIEW_ITEM::ViewDraw( int aLayer, KIGFX::GAL* aGal ) const
+{
+ //col.Brighten(0.7);
+ aGal->SetLayerDepth( m_depth );
+
+ if( m_type == PR_SHAPE )
+ {
+ if( !m_shape )
+ return;
+
+ aGal->SetLineWidth( m_width );
+ aGal->SetStrokeColor( m_color );
+ aGal->SetFillColor( m_color );
+ aGal->SetIsStroke( m_width ? true : false );
+ aGal->SetIsFill( true );
+
+ 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->DrawSegment( s->GetSeg().A, s->GetSeg().B, s->GetWidth() );
+
+ if( m_clearance > 0 )
+ {
+ aGal->SetLayerDepth( ClearanceOverlayDepth );
+ aGal->SetStrokeColor( COLOR4D( DARKDARKGRAY ) );
+ aGal->SetFillColor( COLOR4D( DARKDARKGRAY ) );
+ aGal->DrawSegment( s->GetSeg().A, s->GetSeg().B, s->GetWidth() + 2 * m_clearance );
+ }
+
+ 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;
+ }
+
+ case SH_CONVEX:
+ {
+ const SHAPE_CONVEX* c = (const SHAPE_CONVEX*) m_shape;
+ std::deque<VECTOR2D> polygon = std::deque<VECTOR2D>();
+ for( int i = 0; i < c->PointCount(); i++ )
+ {
+ polygon.push_back( c->CDPoint( i ) );
+ }
+ aGal->DrawPolygon( polygon );
+
+ if( m_clearance > 0 )
+ {
+ aGal->SetLayerDepth( ClearanceOverlayDepth );
+ aGal->SetStrokeColor( COLOR4D( DARKDARKGRAY ) );
+ aGal->SetIsStroke( true );
+ aGal->SetLineWidth( 2 * m_clearance );
+ // need the implicit last segment to be explicit for DrawPolyline
+ polygon.push_back( c->CDPoint( 0 ) );
+ aGal->DrawPolyline( polygon );
+ }
+ break;
+ }
+
+ case SH_POLY_SET:
+ case SH_COMPOUND:
+ break; // Not yet in use
+ }
+ }
+}
+
+
+void ROUTER_PREVIEW_ITEM::Line( const SHAPE_LINE_CHAIN& aLine, int aWidth, int aStyle )
+{
+ m_originLayer = m_layer = 0;
+ m_width = aWidth;
+ m_color = assignColor( aStyle );
+ m_type = PR_SHAPE;
+ m_depth = -1024; // TODO gal->GetMinDepth()
+ m_shape = aLine.Clone();
+
+ ViewSetVisible( true );
+ ViewUpdate( GEOMETRY | APPEARANCE );
+}
+
+
+void ROUTER_PREVIEW_ITEM::Point( const VECTOR2I& aPos, int aStyle )
+{
+}
+
+
+void ROUTER_PREVIEW_ITEM::Box( const BOX2I& aBox, int aStyle )
+{
+}
+
+
+const COLOR4D ROUTER_PREVIEW_ITEM::getLayerColor( int aLayer ) const
+{
+ PCB_RENDER_SETTINGS* settings =
+ static_cast<PCB_RENDER_SETTINGS*>( m_parent->GetView()->GetPainter()->GetSettings() );
+
+ return settings->GetLayerColor( aLayer );
+}
+
+
+const COLOR4D ROUTER_PREVIEW_ITEM::assignColor( int aStyle ) const
+{
+ COLOR4D color;
+
+ switch( aStyle )
+ {
+ case 0:
+ color = COLOR4D( 0, 1, 0, 1 ); break;
+
+ case 1:
+ color = COLOR4D( 1, 0, 0, 1 ); break;
+
+ case 2:
+ color = COLOR4D( 1, 1, 0, 1 ); break;
+
+ case 3:
+ color = COLOR4D( 0, 0, 1, 1 ); break;
+
+ case 4:
+ color = COLOR4D( 1, 1, 1, 1 ); break;
+
+ case 5:
+ color = COLOR4D( 1, 1, 0, 1 ); break;
+
+ case 6:
+ color = COLOR4D( 0, 1, 1, 1 ); break;
+
+ case 32:
+ color = COLOR4D( 0, 0, 1, 1 ); break;
+
+ default:
+ color = COLOR4D( 0.4, 0.4, 0.4, 1 ); break;
+ }
+
+ return color;
+}
+
+const int ROUTER_PREVIEW_ITEM::ClearanceOverlayDepth = -300;
+const int ROUTER_PREVIEW_ITEM::BaseOverlayDepth = -310;
+const int ROUTER_PREVIEW_ITEM::ViaOverlayDepth = -346;
diff --git a/pcbnew/router/router_preview_item.h b/pcbnew/router/router_preview_item.h
new file mode 100644
index 0000000..653900d
--- /dev/null
+++ b/pcbnew/router/router_preview_item.h
@@ -0,0 +1,127 @@
+/*
+ * 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 __ROUTER_PREVIEW_ITEM_H
+#define __ROUTER_PREVIEW_ITEM_H
+
+#include <cstdio>
+
+#include <view/view.h>
+#include <view/view_item.h>
+#include <view/view_group.h>
+
+#include <math/vector2d.h>
+#include <math/box2.h>
+
+#include <geometry/shape_line_chain.h>
+#include <geometry/shape_circle.h>
+
+#include <gal/color4d.h>
+#include <gal/graphics_abstraction_layer.h>
+
+#include <layers_id_colors_and_visibility.h>
+
+class PNS_ITEM;
+class PNS_ROUTER;
+
+class ROUTER_PREVIEW_ITEM : public EDA_ITEM
+{
+public:
+ enum ITEM_TYPE
+ {
+ PR_STUCK_MARKER = 0,
+ PR_POINT,
+ PR_SHAPE
+ };
+
+ ROUTER_PREVIEW_ITEM( const PNS_ITEM* aItem = NULL, KIGFX::VIEW_GROUP* aParent = NULL );
+ ~ROUTER_PREVIEW_ITEM();
+
+ void Update( const PNS_ITEM* aItem );
+
+ void StuckMarker( VECTOR2I& aPosition );
+
+ 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;
+ }
+
+#if defined(DEBUG)
+ void Show( int aA, std::ostream& aB ) const {};
+#endif
+
+ /** Get class name
+ * @return string "ROUTER_PREVIEW_ITEM"
+ */
+ virtual wxString GetClass() const
+ {
+ return wxT( "ROUTER_PREVIEW_ITEM" );
+ }
+
+ const BOX2I ViewBBox() const;
+
+ virtual void ViewDraw( int aLayer, KIGFX::GAL* aGal ) const;
+
+ virtual void ViewGetLayers( int aLayers[], int& aCount ) const
+ {
+ aLayers[0] = m_layer;
+ aCount = 1;
+ }
+
+ void drawLineChain( const SHAPE_LINE_CHAIN& aL, KIGFX::GAL* aGal ) const;
+
+private:
+ const KIGFX::COLOR4D assignColor( int aStyle ) const;
+ const KIGFX::COLOR4D getLayerColor( int aLayer ) const;
+
+ KIGFX::VIEW_GROUP* m_parent;
+
+ PNS_ROUTER* m_router;
+ SHAPE* m_shape;
+
+ ITEM_TYPE m_type;
+
+ int m_style;
+ int m_width;
+ int m_layer;
+ int m_originLayer;
+ int m_clearance;
+
+ // fixme: shouldn't this go to VIEW?
+ static const int ClearanceOverlayDepth;
+ static const int BaseOverlayDepth;
+ static const int ViaOverlayDepth;
+
+ double m_depth;
+
+ KIGFX::COLOR4D m_color;
+ VECTOR2I m_pos;
+};
+
+#endif
diff --git a/pcbnew/router/router_tool.cpp b/pcbnew/router/router_tool.cpp
new file mode 100644
index 0000000..7e03f8b
--- /dev/null
+++ b/pcbnew/router/router_tool.cpp
@@ -0,0 +1,854 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <wx/numdlg.h>
+
+#include <boost/foreach.hpp>
+#include <boost/optional.hpp>
+#include <boost/bind.hpp>
+
+#include "class_draw_panel_gal.h"
+#include "class_board.h"
+
+#include <wxPcbStruct.h>
+#include <id.h>
+#include <macros.h>
+#include <pcbnew_id.h>
+#include <view/view_controls.h>
+#include <pcbcommon.h>
+#include <pcb_painter.h>
+#include <dialogs/dialog_pns_settings.h>
+#include <dialogs/dialog_pns_diff_pair_dimensions.h>
+#include <dialogs/dialog_track_via_size.h>
+#include <base_units.h>
+#include <hotkeys.h>
+#include <confirm.h>
+
+#include <tool/context_menu.h>
+#include <tool/tool_manager.h>
+#include <tool/tool_settings.h>
+#include <tools/common_actions.h>
+#include <tools/size_menu.h>
+#include <tools/selection_tool.h>
+#include <tools/edit_tool.h>
+
+#include <ratsnest_data.h>
+
+#include "router_tool.h"
+#include "pns_segment.h"
+#include "pns_router.h"
+#include "trace.h"
+
+using namespace KIGFX;
+using boost::optional;
+
+static TOOL_ACTION ACT_NewTrack( "pcbnew.InteractiveRouter.NewTrack", AS_CONTEXT,
+ TOOL_ACTION::LegacyHotKey( HK_ADD_NEW_TRACK ),
+ _( "New Track" ), _( "Starts laying a new track." ), add_tracks_xpm );
+
+static TOOL_ACTION ACT_EndTrack( "pcbnew.InteractiveRouter.EndTrack", AS_CONTEXT, WXK_END,
+ _( "End Track" ), _( "Stops laying the current track." ), checked_ok_xpm );
+
+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,
+ TOOL_ACTION::LegacyHotKey( HK_DRAG_TRACK_KEEP_SLOPE ),
+ _( "Drag Track/Via" ), _( "Drags a track or a via." ), drag_track_segment_xpm );
+
+static TOOL_ACTION ACT_PlaceThroughVia( "pcbnew.InteractiveRouter.PlaceVia",
+ AS_CONTEXT, TOOL_ACTION::LegacyHotKey( HK_ADD_THROUGH_VIA ),
+ _( "Place Through Via" ),
+ _( "Adds a through-hole via at the end of currently routed track." ),
+ via_xpm );
+
+static TOOL_ACTION ACT_PlaceBlindVia( "pcbnew.InteractiveRouter.PlaceBlindVia",
+ AS_CONTEXT, TOOL_ACTION::LegacyHotKey( HK_ADD_BLIND_BURIED_VIA ),
+ _( "Place Blind/Buried Via" ),
+ _( "Adds a blind or buried via at the end of currently routed track."),
+ via_buried_xpm );
+
+static TOOL_ACTION ACT_PlaceMicroVia( "pcbnew.InteractiveRouter.PlaceMicroVia",
+ AS_CONTEXT, TOOL_ACTION::LegacyHotKey( HK_ADD_MICROVIA ),
+ _( "Place Microvia" ), _( "Adds a microvia at the end of currently routed track." ),
+ via_microvia_xpm );
+
+static TOOL_ACTION ACT_CustomTrackWidth( "pcbnew.InteractiveRouter.CustomTrackViaSize",
+ AS_CONTEXT, 'Q',
+ _( "Custom Track/Via Size" ),
+ _( "Shows a dialog for changing the track width and via size." ),
+ width_track_xpm );
+
+static TOOL_ACTION ACT_SwitchPosture( "pcbnew.InteractiveRouter.SwitchPosture", AS_CONTEXT,
+ TOOL_ACTION::LegacyHotKey( HK_SWITCH_TRACK_POSTURE ),
+ _( "Switch Track Posture" ),
+ _( "Switches posture of the currently routed track." ),
+ change_entry_orient_xpm );
+
+static TOOL_ACTION ACT_SetDpDimensions( "pcbnew.InteractiveRouter.SetDpDimensions",
+ AS_CONTEXT, 'P',
+ _( "Differential Pair Dimensions..." ),
+ _( "Sets the width and gap of the currently routed differential pair." ),
+ ps_diff_pair_tune_length_xpm );
+
+
+ROUTER_TOOL::ROUTER_TOOL() :
+ PNS_TOOL_BASE( "pcbnew.InteractiveRouter" )
+{
+}
+
+
+class CONTEXT_TRACK_WIDTH_MENU: public CONTEXT_TRACK_VIA_SIZE_MENU
+{
+public:
+ CONTEXT_TRACK_WIDTH_MENU()
+ : CONTEXT_TRACK_VIA_SIZE_MENU( true, true ), m_board( NULL )
+ {
+ SetMenuHandler( boost::bind( &CONTEXT_TRACK_WIDTH_MENU::EventHandler, this, _1 ) );
+ }
+
+ void SetBoard( BOARD* aBoard )
+ {
+ 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 net class values" ),
+ _( "Use track and via sizes from the net class" ), wxITEM_CHECK );
+
+ AppendSeparator();
+
+ // Append the list of tracks & via sizes
+ AppendSizes( aBoard );
+ }
+
+ OPT_TOOL_EVENT EventHandler( const wxMenuEvent& aEvent )
+ {
+#if ID_POPUP_PCB_SELECT_VIASIZE1 < ID_POPUP_PCB_SELECT_WIDTH1
+#error You have changed event ids order, it breaks code. Check the source code for more details.
+// Recognising type of event (track width/via size) is based on comparison if the event id is
+// within a specific range. If ranges of event ids changes, then the following is not valid anymore.
+#endif
+ BOARD_DESIGN_SETTINGS &bds = m_board->GetDesignSettings();
+
+ int id = aEvent.GetId();
+
+ // Initial settings, to be modified below
+ bds.m_UseConnectedTrackWidth = false;
+ bds.UseCustomTrackViaSize( false );
+
+ if( id == ID_POPUP_PCB_SELECT_CUSTOM_WIDTH )
+ {
+ bds.UseCustomTrackViaSize( true );
+ }
+
+ else if( id == ID_POPUP_PCB_SELECT_AUTO_WIDTH )
+ {
+ bds.m_UseConnectedTrackWidth = true;
+ }
+
+ else if( id == ID_POPUP_PCB_SELECT_USE_NETCLASS_VALUES )
+ {
+ bds.SetViaSizeIndex( 0 );
+ bds.SetTrackWidthIndex( 0 );
+ }
+
+ else if( id >= ID_POPUP_PCB_SELECT_VIASIZE1 ) // via size has changed
+ {
+ assert( id < ID_POPUP_PCB_SELECT_WIDTH_END_RANGE );
+
+ bds.SetViaSizeIndex( id - ID_POPUP_PCB_SELECT_VIASIZE1 );
+ }
+
+ else // track width has changed
+ {
+ assert( id >= ID_POPUP_PCB_SELECT_WIDTH1 );
+ assert( id < ID_POPUP_PCB_SELECT_VIASIZE );
+
+ bds.SetTrackWidthIndex( id - ID_POPUP_PCB_SELECT_WIDTH1 );
+ }
+
+ return OPT_TOOL_EVENT( COMMON_ACTIONS::trackViaSizeChanged.MakeEvent() );
+ }
+
+private:
+ BOARD* m_board;
+};
+
+
+class ROUTER_TOOL_MENU: public CONTEXT_MENU
+{
+public:
+ ROUTER_TOOL_MENU( BOARD* aBoard, PNS_ROUTER_MODE aMode )
+ {
+ SetTitle( _( "Interactive Router" ) );
+ Add( ACT_NewTrack );
+ Add( ACT_EndTrack );
+// Add( ACT_AutoEndRoute ); // fixme: not implemented yet. Sorry.
+ Add( ACT_Drag );
+ Add( ACT_PlaceThroughVia );
+ Add( ACT_PlaceBlindVia );
+ Add( ACT_PlaceMicroVia );
+ Add( ACT_SwitchPosture );
+
+ AppendSeparator();
+
+ m_widthMenu.SetBoard( aBoard );
+ Add( &m_widthMenu, _( "Select Track/Via Width" ) );
+
+ Add( ACT_CustomTrackWidth );
+
+ if( aMode == PNS_MODE_ROUTE_DIFF_PAIR )
+ Add( ACT_SetDpDimensions );
+
+ AppendSeparator();
+ Add( PNS_TOOL_BASE::ACT_RouterOptions );
+ }
+
+private:
+ CONTEXT_TRACK_WIDTH_MENU m_widthMenu;
+};
+
+
+ROUTER_TOOL::~ROUTER_TOOL()
+{
+ m_savedSettings.Save( GetSettings() );
+}
+
+bool ROUTER_TOOL::Init()
+{
+ m_savedSettings.Load( GetSettings() );
+ return true;
+}
+
+
+void ROUTER_TOOL::Reset( RESET_REASON aReason )
+{
+ PNS_TOOL_BASE::Reset( aReason );
+
+ Go( &ROUTER_TOOL::RouteSingleTrace, COMMON_ACTIONS::routerActivateSingle.MakeEvent() );
+ Go( &ROUTER_TOOL::RouteDiffPair, COMMON_ACTIONS::routerActivateDiffPair.MakeEvent() );
+ Go( &ROUTER_TOOL::DpDimensionsDialog, COMMON_ACTIONS::routerActivateDpDimensionsDialog.MakeEvent() );
+ Go( &ROUTER_TOOL::SettingsDialog, COMMON_ACTIONS::routerActivateSettingsDialog.MakeEvent() );
+ Go( &ROUTER_TOOL::InlineDrag, COMMON_ACTIONS::routerInlineDrag.MakeEvent() );
+}
+
+
+int ROUTER_TOOL::getDefaultWidth( int aNetCode )
+{
+ int w, d1, d2;
+
+ getNetclassDimensions( aNetCode, w, d1, d2 );
+
+ return w;
+}
+
+
+void ROUTER_TOOL::getNetclassDimensions( int aNetCode, int& aWidth,
+ int& aViaDiameter, int& aViaDrill )
+{
+ BOARD_DESIGN_SETTINGS &bds = m_board->GetDesignSettings();
+
+ NETCLASSPTR netClass;
+ NETINFO_ITEM* ni = m_board->FindNet( aNetCode );
+
+ if( ni )
+ {
+ wxString netClassName = ni->GetClassName();
+ netClass = bds.m_NetClasses.Find( netClassName );
+ }
+
+ if( !netClass )
+ netClass = bds.GetDefault();
+
+ aWidth = netClass->GetTrackWidth();
+ aViaDiameter = netClass->GetViaDiameter();
+ aViaDrill = netClass->GetViaDrill();
+}
+
+
+void ROUTER_TOOL::handleCommonEvents( const TOOL_EVENT& aEvent )
+{
+#ifdef DEBUG
+ if( aEvent.IsKeyPressed() )
+ {
+ switch( aEvent.KeyCode() )
+ {
+ case '0':
+ TRACEn( 2, "saving drag/route log...\n" );
+ m_router->DumpLog();
+ break;
+ }
+ }
+ else
+#endif
+ if( aEvent.IsAction( &ACT_RouterOptions ) )
+ {
+ DIALOG_PNS_SETTINGS settingsDlg( m_frame, m_router->Settings() );
+
+ if( settingsDlg.ShowModal() == wxID_OK )
+ {
+ // FIXME: do we need an explicit update?
+ }
+ }
+ else if( aEvent.IsAction( &ACT_SetDpDimensions ) )
+ {
+ PNS_SIZES_SETTINGS sizes = m_router->Sizes();
+ DIALOG_PNS_DIFF_PAIR_DIMENSIONS settingsDlg( m_frame, sizes );
+
+ if( settingsDlg.ShowModal() )
+ {
+ m_router->UpdateSizes( sizes );
+ }
+ }
+ else if( aEvent.IsAction( &ACT_CustomTrackWidth ) )
+ {
+ BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
+ DIALOG_TRACK_VIA_SIZE sizeDlg( m_frame, bds );
+
+ if( sizeDlg.ShowModal() )
+ {
+ bds.UseCustomTrackViaSize( true );
+ m_toolMgr->RunAction( COMMON_ACTIONS::trackViaSizeChanged );
+ }
+ }
+
+ else if( aEvent.IsAction( &COMMON_ACTIONS::trackViaSizeChanged ) )
+ {
+
+ PNS_SIZES_SETTINGS sizes( m_router->Sizes() );
+ sizes.ImportCurrent( m_board->GetDesignSettings() );
+ m_router->UpdateSizes( sizes );
+ }
+}
+
+
+int ROUTER_TOOL::getStartLayer( const PNS_ITEM* aItem )
+{
+ int tl = getView()->GetTopLayer();
+
+ if( m_startItem )
+ {
+ const PNS_LAYERSET& ls = m_startItem->Layers();
+
+ if( ls.Overlaps( tl ) )
+ return tl;
+ else
+ return ls.Start();
+ }
+
+ return tl;
+}
+
+
+void ROUTER_TOOL::switchLayerOnViaPlacement()
+{
+ int al = m_frame->GetActiveLayer();
+ int cl = m_router->GetCurrentLayer();
+
+ if( cl != al )
+ {
+ m_router->SwitchLayer( al );
+ }
+
+ optional<int> newLayer = m_router->Sizes().PairedLayer( cl );
+
+ if( !newLayer )
+ newLayer = m_router->Sizes().GetLayerTop();
+
+ m_router->SwitchLayer( *newLayer );
+ m_frame->SetActiveLayer( ToLAYER_ID( *newLayer ) );
+}
+
+
+bool ROUTER_TOOL::onViaCommand( TOOL_EVENT& aEvent, VIATYPE_T aType )
+{
+ BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
+
+ const int layerCount = bds.GetCopperLayerCount();
+ int currentLayer = m_router->GetCurrentLayer();
+ LAYER_ID pairTop = m_frame->GetScreen()->m_Route_Layer_TOP;
+ LAYER_ID pairBottom = m_frame->GetScreen()->m_Route_Layer_BOTTOM;
+
+ PNS_SIZES_SETTINGS sizes = m_router->Sizes();
+
+ // fixme: P&S supports more than one fixed layer pair. Update the dialog?
+ sizes.ClearLayerPairs();
+
+ if( !m_router->IsPlacingVia() )
+ {
+ // Cannot place microvias or blind vias if not allowed (obvious)
+ if( ( aType == VIA_BLIND_BURIED ) && ( !bds.m_BlindBuriedViaAllowed ) )
+ {
+ DisplayError( m_frame, _( "Blind/buried vias have to be enabled in the design settings." ) );
+ return false;
+ }
+
+ if( ( aType == VIA_MICROVIA ) && ( !bds.m_MicroViasAllowed ) )
+ {
+ DisplayError( m_frame, _( "Microvias have to be enabled in the design settings." ) );
+ return false;
+ }
+
+ // Can only place through vias on 2-layer boards
+ if( ( aType != VIA_THROUGH ) && ( layerCount <= 2 ) )
+ {
+ DisplayError( m_frame, _( "Only through vias are allowed on 2 layer boards." ) );
+ return false;
+ }
+
+ // Can only place microvias if we're on an outer layer, or directly adjacent to one
+ if( ( aType == VIA_MICROVIA ) && ( currentLayer > In1_Cu ) && ( currentLayer < layerCount - 2 ) )
+ {
+ DisplayError( m_frame, _( "Microvias can be placed only between the outer layers " \
+ "(F.Cu/B.Cu) and the ones directly adjacent to them." ) );
+ return false;
+ }
+ }
+
+ // Convert blind/buried via to a through hole one, if it goes through all layers
+ if( aType == VIA_BLIND_BURIED && ( ( currentLayer == B_Cu ) || ( currentLayer == F_Cu ) )
+ && ( ( pairTop == B_Cu && pairBottom == F_Cu )
+ || ( pairTop == F_Cu && pairBottom == B_Cu ) ) )
+ {
+ aType = VIA_THROUGH;
+ }
+
+ switch( aType )
+ {
+ case VIA_THROUGH:
+ sizes.SetViaDiameter( bds.GetCurrentViaSize() );
+ sizes.SetViaDrill( bds.GetCurrentViaDrill() );
+ sizes.AddLayerPair( pairTop, pairBottom );
+ break;
+
+ case VIA_MICROVIA:
+ sizes.SetViaDiameter( bds.GetCurrentMicroViaSize() );
+ sizes.SetViaDrill( bds.GetCurrentMicroViaDrill() );
+
+ if( currentLayer == F_Cu || currentLayer == In1_Cu )
+ sizes.AddLayerPair( F_Cu, In1_Cu );
+ else if( currentLayer == B_Cu || currentLayer == layerCount - 2 )
+ sizes.AddLayerPair( B_Cu, layerCount - 2 );
+ else
+ wxASSERT( false );
+ break;
+
+ case VIA_BLIND_BURIED:
+ sizes.SetViaDiameter( bds.GetCurrentViaSize() );
+ sizes.SetViaDrill( bds.GetCurrentViaDrill() );
+
+ if( currentLayer == pairTop || currentLayer == pairBottom )
+ sizes.AddLayerPair( pairTop, pairBottom );
+ else
+ sizes.AddLayerPair( pairTop, currentLayer );
+ break;
+
+ default:
+ wxASSERT( false );
+ break;
+ }
+
+ sizes.SetViaType( aType );
+
+ m_router->UpdateSizes( sizes );
+ m_router->ToggleViaPlacement();
+
+ updateEndItem( aEvent );
+
+ m_router->Move( m_endSnapPoint, m_endItem ); // refresh
+
+ return false;
+}
+
+
+bool ROUTER_TOOL::prepareInteractive()
+{
+ int routingLayer = getStartLayer( m_startItem );
+
+ if( !IsCopperLayer( routingLayer ) )
+ {
+ DisplayError( m_frame, _( "Tracks on Copper layers only " ) );
+ return false;
+ }
+
+ m_frame->SetActiveLayer( ToLAYER_ID( routingLayer ) );
+
+ // fixme: switch on invisible layer
+
+ // for some reason I don't understand, GetNetclass() may return null sometimes...
+ if( m_startItem &&
+ m_startItem->Net() >= 0 &&
+ m_startItem->Parent() &&
+ m_startItem->Parent()->GetNetClass() )
+ {
+ highlightNet( true, m_startItem->Net() );
+ // Update track width and via size shown in main toolbar comboboxes
+ m_frame->SetCurrentNetClass( m_startItem->Parent()->GetNetClass()->GetName() );
+ }
+ else
+ m_frame->SetCurrentNetClass( NETCLASS::Default );
+
+ m_ctls->ForceCursorPosition( false );
+ m_ctls->SetAutoPan( true );
+
+ PNS_SIZES_SETTINGS sizes( m_router->Sizes() );
+
+ sizes.Init( m_board, m_startItem );
+ sizes.AddLayerPair( m_frame->GetScreen()->m_Route_Layer_TOP,
+ m_frame->GetScreen()->m_Route_Layer_BOTTOM );
+ m_router->UpdateSizes( sizes );
+
+ if( !m_router->StartRouting( m_startSnapPoint, m_startItem, routingLayer ) )
+ {
+ DisplayError( m_frame, m_router->FailureReason() );
+ highlightNet( false );
+ return false;
+ }
+
+ m_endItem = NULL;
+ m_endSnapPoint = m_startSnapPoint;
+
+ return true;
+}
+
+
+bool ROUTER_TOOL::finishInteractive()
+{
+ m_router->StopRouting();
+
+ // Save the recent changes in the undo buffer
+ m_frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED );
+ m_router->ClearUndoBuffer();
+ m_frame->OnModify();
+
+ m_ctls->SetAutoPan( false );
+ m_ctls->ForceCursorPosition( false );
+ highlightNet( false );
+
+ return true;
+}
+
+
+void ROUTER_TOOL::performRouting()
+{
+ if( !prepareInteractive() )
+ return;
+
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ if( evt->IsCancel() || evt->IsActivate() )
+ break;
+ else if( evt->IsMotion() )
+ {
+ m_router->SetOrthoMode( evt->Modifier( MD_CTRL ) );
+ updateEndItem( *evt );
+ m_router->Move( m_endSnapPoint, m_endItem );
+ }
+ else if( evt->IsClick( BUT_LEFT ) )
+ {
+ updateEndItem( *evt );
+ bool needLayerSwitch = m_router->IsPlacingVia();
+
+ if( m_router->FixRoute( m_endSnapPoint, m_endItem ) )
+ break;
+
+ if( needLayerSwitch )
+ switchLayerOnViaPlacement();
+
+ // Synchronize the indicated layer
+ m_frame->SetActiveLayer( ToLAYER_ID( m_router->GetCurrentLayer() ) );
+ updateEndItem( *evt );
+ m_router->Move( m_endSnapPoint, m_endItem );
+ m_startItem = NULL;
+ }
+ else if( evt->IsAction( &ACT_PlaceThroughVia ) )
+ {
+ onViaCommand( *evt, VIA_THROUGH );
+ }
+ else if( evt->IsAction( &ACT_PlaceBlindVia ) )
+ {
+ onViaCommand( *evt, VIA_BLIND_BURIED );
+ }
+ else if( evt->IsAction( &ACT_PlaceMicroVia ) )
+ {
+ onViaCommand( *evt, VIA_MICROVIA );
+ }
+ else if( evt->IsAction( &ACT_SwitchPosture ) )
+ {
+ m_router->FlipPosture();
+ updateEndItem( *evt );
+ m_router->Move( m_endSnapPoint, m_endItem ); // refresh
+ }
+ else if( evt->IsAction( &COMMON_ACTIONS::layerChanged ) )
+ {
+ m_router->SwitchLayer( m_frame->GetActiveLayer() );
+ updateEndItem( *evt );
+ m_router->Move( m_endSnapPoint, m_endItem ); // refresh
+ }
+ else if( evt->IsAction( &ACT_EndTrack ) )
+ {
+ bool still_routing = true;
+ while( still_routing )
+ still_routing = m_router->FixRoute( m_endSnapPoint, m_endItem );
+ break;
+ }
+
+ handleCommonEvents( *evt );
+ }
+
+ finishInteractive();
+}
+
+
+int ROUTER_TOOL::DpDimensionsDialog( const TOOL_EVENT& aEvent )
+{
+ Activate();
+
+ PNS_SIZES_SETTINGS sizes = m_router->Sizes();
+ DIALOG_PNS_DIFF_PAIR_DIMENSIONS settingsDlg( m_frame, sizes );
+
+ if( settingsDlg.ShowModal() )
+ {
+ m_router->UpdateSizes( sizes );
+ m_savedSizes = sizes;
+ }
+
+ return 0;
+}
+
+
+int ROUTER_TOOL::SettingsDialog( const TOOL_EVENT& aEvent )
+{
+ Activate();
+
+ DIALOG_PNS_SETTINGS settingsDlg( m_frame, m_router->Settings() );
+
+ if( settingsDlg.ShowModal() )
+ {
+ m_savedSettings = m_router->Settings();
+ }
+ return 0;
+}
+
+
+int ROUTER_TOOL::RouteSingleTrace( const TOOL_EVENT& aEvent )
+{
+ m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Route Track" ) );
+ return mainLoop( PNS_MODE_ROUTE_SINGLE );
+}
+
+
+int ROUTER_TOOL::RouteDiffPair( const TOOL_EVENT& aEvent )
+{
+ m_frame->SetToolID( ID_TRACK_BUTT, wxCURSOR_PENCIL, _( "Router Differential Pair" ) );
+ return mainLoop( PNS_MODE_ROUTE_DIFF_PAIR );
+}
+
+
+int ROUTER_TOOL::mainLoop( PNS_ROUTER_MODE aMode )
+{
+ PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
+ BOARD* board = getModel<BOARD>();
+
+ // Deselect all items
+ m_toolMgr->RunAction( COMMON_ACTIONS::selectionClear, true );
+
+ Activate();
+
+ m_router->SetMode( aMode );
+
+ m_ctls->SetSnapping( true );
+ m_ctls->ShowCursor( true );
+ frame->UndoRedoBlock( true );
+
+ m_startSnapPoint = getViewControls()->GetCursorPosition();
+
+ std::auto_ptr<ROUTER_TOOL_MENU> ctxMenu( new ROUTER_TOOL_MENU( board, aMode ) );
+ SetContextMenu( ctxMenu.get() );
+
+ // Main loop: keep receiving events
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ if( m_needsSync )
+ {
+ m_router->SyncWorld();
+ m_router->SetView( getView() );
+ m_needsSync = false;
+ }
+
+ if( evt->IsCancel() || evt->IsActivate() )
+ break; // Finish
+ else if( evt->IsMotion() )
+ updateStartItem( *evt );
+ else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACT_NewTrack ) )
+ {
+ updateStartItem( *evt );
+
+ if( evt->Modifier( MD_CTRL ) )
+ performDragging();
+ else
+ performRouting();
+ }
+ else if( evt->IsAction( &ACT_Drag ) )
+ {
+ updateStartItem( *evt );
+ performDragging();
+ }
+ else if( evt->IsAction( &ACT_PlaceThroughVia ) )
+ {
+ m_toolMgr->RunAction( COMMON_ACTIONS::layerToggle, true );
+ }
+
+ handleCommonEvents( *evt );
+ }
+
+ frame->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
+ frame->UndoRedoBlock( false );
+
+ // Store routing settings till the next invocation
+ m_savedSettings = m_router->Settings();
+ m_savedSizes = m_router->Sizes();
+
+ // Disable the context menu before it is destroyed
+ SetContextMenu( NULL, CMENU_OFF );
+
+ return 0;
+}
+
+
+void ROUTER_TOOL::performDragging()
+{
+ PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
+ 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->SetAutoPan( true );
+
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ ctls->ForceCursorPosition( false );
+
+ if( evt->IsCancel() || evt->IsActivate() )
+ 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();
+
+ // Save the recent changes in the undo buffer
+ frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED );
+ m_router->ClearUndoBuffer();
+ frame->OnModify();
+
+ m_startItem = NULL;
+
+ ctls->SetAutoPan( false );
+ ctls->ForceCursorPosition( false );
+ highlightNet( false );
+}
+
+
+int ROUTER_TOOL::InlineDrag( const TOOL_EVENT& aEvent )
+{
+ const BOARD_CONNECTED_ITEM *item = aEvent.Parameter<const BOARD_CONNECTED_ITEM*>();
+ PCB_EDIT_FRAME* frame = getEditFrame<PCB_EDIT_FRAME>();
+ VIEW_CONTROLS* ctls = getViewControls();
+
+ Activate();
+
+ m_router->SyncWorld();
+ m_router->SetView( getView() );
+
+ m_startItem = m_router->GetWorld()->FindItemByParent( item );
+
+ VECTOR2I p0 = ctls->GetCursorPosition();
+
+ bool dragStarted = m_router->StartDragging( p0, m_startItem );
+
+ if( !dragStarted )
+ return 0;
+
+ ctls->ForceCursorPosition( false );
+ ctls->SetAutoPan( true );
+ frame->UndoRedoBlock( true );
+
+ bool saveUndoBuffer = true;
+
+ while( OPT_TOOL_EVENT evt = Wait() )
+ {
+ p0 = ctls->GetCursorPosition();
+
+ if( evt->IsCancel() )
+ {
+ saveUndoBuffer = false;
+ break;
+ }
+ else if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
+ {
+ m_router->Move( p0, NULL );
+ }
+ else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT ) )
+ {
+ saveUndoBuffer = m_router->FixRoute( p0, NULL );
+ break;
+ }
+ }
+
+ if( m_router->RoutingInProgress() )
+ m_router->StopRouting();
+
+ if( saveUndoBuffer )
+ {
+ frame->SaveCopyInUndoList( m_router->GetUndoBuffer(), UR_UNSPECIFIED );
+ m_router->ClearUndoBuffer();
+ frame->OnModify();
+ }
+
+ ctls->SetAutoPan( false );
+ ctls->ShowCursor( false );
+ frame->UndoRedoBlock( false );
+
+ return 0;
+}
+
diff --git a/pcbnew/router/router_tool.h b/pcbnew/router/router_tool.h
new file mode 100644
index 0000000..c1bd1a2
--- /dev/null
+++ b/pcbnew/router/router_tool.h
@@ -0,0 +1,63 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013-2014 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ * Author: Maciej Suminski <maciej.suminski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ROUTER_TOOL_H
+#define __ROUTER_TOOL_H
+
+#include "pns_tool_base.h"
+
+class APIEXPORT ROUTER_TOOL : public PNS_TOOL_BASE
+{
+public:
+ ROUTER_TOOL();
+ ~ROUTER_TOOL();
+
+ bool Init();
+ void Reset( RESET_REASON aReason );
+
+ int RouteSingleTrace ( const TOOL_EVENT& aEvent );
+ int RouteDiffPair ( const TOOL_EVENT& aEvent );
+ int InlineDrag ( const TOOL_EVENT& aEvent );
+
+ int DpDimensionsDialog ( const TOOL_EVENT& aEvent );
+ int SettingsDialog ( const TOOL_EVENT& aEvent );
+
+private:
+
+ int mainLoop( PNS_ROUTER_MODE aMode );
+
+ int getDefaultWidth( int aNetCode );
+
+ void performRouting();
+ void performDragging();
+
+ void getNetclassDimensions( int aNetCode, int& aWidth, int& aViaDiameter, int& aViaDrill );
+ void handleCommonEvents( const TOOL_EVENT& evt );
+
+ int getStartLayer( const PNS_ITEM* aItem );
+ void switchLayerOnViaPlacement();
+ bool onViaCommand( TOOL_EVENT& aEvent, VIATYPE_T aType );
+
+ bool prepareInteractive();
+ bool finishInteractive();
+};
+
+#endif
diff --git a/pcbnew/router/time_limit.cpp b/pcbnew/router/time_limit.cpp
new file mode 100644
index 0000000..b4cef54
--- /dev/null
+++ b/pcbnew/router/time_limit.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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;
+}
diff --git a/pcbnew/router/time_limit.h b/pcbnew/router/time_limit.h
new file mode 100644
index 0000000..d917533
--- /dev/null
+++ b/pcbnew/router/time_limit.h
@@ -0,0 +1,43 @@
+/*
+ * 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 );
+ int Get() const { return m_limitMs; }
+
+private:
+ int m_limitMs;
+ int64_t m_startTics;
+};
+
+#endif
diff --git a/pcbnew/router/trace.h b/pcbnew/router/trace.h
new file mode 100644
index 0000000..c115b67
--- /dev/null
+++ b/pcbnew/router/trace.h
@@ -0,0 +1,46 @@
+/*
+ * KiRouter - a push-and-(sometimes-)shove PCB router
+ *
+ * Copyright (C) 2013 CERN
+ * Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __TRACE_H
+#define __TRACE_H
+
+#include <string>
+#include <iostream>
+#include <boost/format.hpp>
+
+static inline void _trace_print( const char* aFuncName, int aLevel, const std::string& aMsg )
+{
+#ifdef DEBUG
+ std::cerr << "trace[" << aLevel << "]: " << aFuncName << ": " << aMsg << std::endl;
+#endif
+}
+
+#ifdef PNS_DEBUG
+ #define TRACE( level, fmt, ... ) \
+ _trace_print( __FUNCTION__, level, ( boost::format( fmt ) % __VA_ARGS__ ).str() );
+
+ #define TRACEn( level, msg ) \
+ _trace_print( __FUNCTION__, level, std::string( msg ) );
+#else
+ #define TRACE( ... )
+ #define TRACEn( ... )
+#endif
+
+#endif