diff options
author | saurabhb17 | 2020-02-26 16:00:53 +0530 |
---|---|---|
committer | GitHub | 2020-02-26 16:00:53 +0530 |
commit | 886d9cb772e81d2e5262284bc3082664f084337f (patch) | |
tree | 6acee185a4dc19113fcbf0f9a3d6941085dedaf7 /pcbnew/router | |
parent | 0db48f6533517ecebfd9f0693f89deca28408b76 (diff) | |
parent | aa35045840b78d3f48212db45da59a2e5c69b223 (diff) | |
download | KiCad-eSim-886d9cb772e81d2e5262284bc3082664f084337f.tar.gz KiCad-eSim-886d9cb772e81d2e5262284bc3082664f084337f.tar.bz2 KiCad-eSim-886d9cb772e81d2e5262284bc3082664f084337f.zip |
Merge pull request #1 from saurabhb17/develop
Added main functions
Diffstat (limited to 'pcbnew/router')
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( ¤t, 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( ¤t ); + + 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( ¤tLine, 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( ¤tLine.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( ¤tLine, (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( ¤t_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 |