1281 lines
32 KiB
C++
1281 lines
32 KiB
C++
/*
|
|
* 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 "time_limit.h"
|
|
|
|
#include <profile.h>
|
|
|
|
void PNS_SHOVE::replaceItems( PNS_ITEM* aOld, PNS_ITEM* aNew )
|
|
{
|
|
OPT_BOX2I changed_area = ChangedArea( aOld, aNew );
|
|
|
|
if( changed_area )
|
|
{
|
|
assert( !changed_area->Contains( VECTOR2I( 0, 0 ) ) );
|
|
|
|
m_affectedAreaSum = m_affectedAreaSum ? m_affectedAreaSum->Merge ( *changed_area ) : *changed_area;
|
|
}
|
|
|
|
m_currentNode->Replace( aOld, aNew );
|
|
}
|
|
|
|
|
|
int PNS_SHOVE::getClearance( PNS_ITEM *aA, PNS_ITEM *aB ) const
|
|
{
|
|
if( m_forceClearance >= 0 )
|
|
return m_forceClearance;
|
|
|
|
return m_currentNode->GetClearance( aA, aB );
|
|
}
|
|
|
|
|
|
static void sanityCheck( PNS_LINE* aOld, PNS_LINE* aNew )
|
|
{
|
|
assert( aOld->CPoint( 0 ) == aNew->CPoint( 0 ) );
|
|
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;
|
|
|
|
// Initialize other temporary variables:
|
|
m_currentNode = NULL;
|
|
m_draggedVia = NULL;
|
|
m_iter = 0;
|
|
m_multiLineMode = false;
|
|
}
|
|
|
|
|
|
PNS_SHOVE::~PNS_SHOVE()
|
|
{
|
|
// free all the stuff we've created during routing/dragging operation.
|
|
BOOST_FOREACH( PNS_ITEM* item, m_gcItems )
|
|
delete item;
|
|
}
|
|
|
|
|
|
// garbage-collected line assembling
|
|
PNS_LINE* PNS_SHOVE::assembleLine( const PNS_SEGMENT* aSeg, int* aIndex )
|
|
{
|
|
PNS_LINE* l = m_currentNode->AssembleLine( const_cast<PNS_SEGMENT*>( aSeg ), aIndex );
|
|
|
|
m_gcItems.push_back( l );
|
|
|
|
return l;
|
|
}
|
|
|
|
|
|
// A dumb function that checks if the shoved line is shoved the right way, e.g.
|
|
// visually "outwards" of the line/via applying pressure on it. Unfortunately there's no
|
|
// mathematical concept of orientation of an open curve, so we use some primitive heuristics:
|
|
// if the shoved line wraps around the start of the "pusher", it's likely shoved in wrong direction.
|
|
bool PNS_SHOVE::checkBumpDirection( PNS_LINE* aCurrent, PNS_LINE* aShoved ) const
|
|
{
|
|
const SEG ss = aCurrent->CSegment( 0 );
|
|
|
|
int 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();
|
|
bool failingDirCheck = false;
|
|
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 );
|
|
failingDirCheck = true;
|
|
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 failingDirCheck ? SH_OK : 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 );
|
|
|
|
HULL_SET hulls;
|
|
|
|
hulls.reserve( n_segs + 1 );
|
|
|
|
for( int i = 0; i < n_segs; i++ )
|
|
{
|
|
PNS_SEGMENT seg( *aCurrent, aCurrent->CSegment( i ) );
|
|
hulls.push_back( seg.Hull( clearance, w ) );
|
|
}
|
|
|
|
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 = clone( obstacleLine );
|
|
|
|
SHOVE_STATUS rv = ProcessSingleLine( aCurrent, obstacleLine, shovedLine );
|
|
|
|
assert( obstacleLine->LayersOverlap( shovedLine ) );
|
|
|
|
if( rv == SH_OK )
|
|
{
|
|
if( shovedLine->Marker() & MK_HEAD )
|
|
{
|
|
if( m_multiLineMode )
|
|
return SH_INCOMPLETE;
|
|
|
|
m_newHead = *shovedLine;
|
|
}
|
|
|
|
sanityCheck( obstacleLine, shovedLine );
|
|
replaceItems ( obstacleLine, shovedLine );
|
|
sanityCheck( obstacleLine, shovedLine );
|
|
|
|
int rank = aCurrent->Rank();
|
|
shovedLine->SetRank( rank - 1 );
|
|
|
|
pushLine( shovedLine );
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
m_logger.NewGroup( "on-colliding-segment", m_iter );
|
|
m_logger.Log( aObstacleSeg, 0, "obstacle-segment" );
|
|
m_logger.Log( aCurrent, 1, "current-line" );
|
|
m_logger.Log( obstacleLine, 2, "obstacle-line" );
|
|
m_logger.Log( shovedLine, 3, "shoved-line" );
|
|
#endif
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingLine( PNS_LINE* aCurrent, PNS_LINE* aObstacle )
|
|
{
|
|
PNS_LINE* shovedLine = clone( aObstacle );
|
|
|
|
SHOVE_STATUS rv = ProcessSingleLine( aCurrent, aObstacle, shovedLine );
|
|
|
|
if( rv == SH_OK )
|
|
{
|
|
if( shovedLine->Marker() & MK_HEAD )
|
|
{
|
|
if( m_multiLineMode )
|
|
return SH_INCOMPLETE;
|
|
|
|
m_newHead = *shovedLine;
|
|
}
|
|
|
|
sanityCheck( aObstacle, shovedLine );
|
|
replaceItems( aObstacle, shovedLine );
|
|
sanityCheck( aObstacle, shovedLine );
|
|
|
|
int rank = aObstacle->Rank();
|
|
shovedLine->SetRank ( rank );
|
|
|
|
pushLine( shovedLine );
|
|
|
|
#ifdef DEBUG
|
|
m_logger.NewGroup( "on-colliding-line", m_iter );
|
|
m_logger.Log( aObstacle, 0, "obstacle-line" );
|
|
m_logger.Log( aCurrent, 1, "current-line" );
|
|
m_logger.Log( shovedLine, 3, "shoved-line" );
|
|
#endif
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
|
|
PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::onCollidingSolid( PNS_LINE* aCurrent, PNS_SOLID* aObstacleSolid )
|
|
{
|
|
PNS_WALKAROUND walkaround( m_currentNode, Router() );
|
|
PNS_LINE* walkaroundLine = clone( 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, aObstacleSolid ) )
|
|
return onCollidingVia( aObstacleSolid, via );
|
|
}
|
|
|
|
walkaround.SetSolidsOnly( true );
|
|
walkaround.SetIterationLimit ( 8 ); // fixme: make configurable
|
|
|
|
int currentRank = aCurrent->Rank();
|
|
int nextRank;
|
|
|
|
if( !Settings().JumpOverObstacles() )
|
|
{
|
|
nextRank = currentRank + 10000;
|
|
walkaround.SetSingleDirection( false );
|
|
}
|
|
else
|
|
{
|
|
nextRank = currentRank - 1;
|
|
walkaround.SetSingleDirection( true );
|
|
}
|
|
|
|
if( walkaround.Route( *aCurrent, *walkaroundLine, false ) != PNS_WALKAROUND::DONE )
|
|
return SH_INCOMPLETE;
|
|
|
|
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;
|
|
}
|
|
|
|
replaceItems ( aCurrent, walkaroundLine );
|
|
walkaroundLine->SetRank( nextRank );
|
|
|
|
#ifdef DEBUG
|
|
m_logger.NewGroup( "on-colliding-solid", m_iter );
|
|
m_logger.Log( aObstacleSolid, 0, "obstacle-solid" );
|
|
m_logger.Log( aCurrent, 1, "current-line" );
|
|
m_logger.Log( walkaroundLine, 3, "walk-line" );
|
|
#endif
|
|
|
|
popLine();
|
|
pushLine( walkaroundLine );
|
|
|
|
return SH_OK;
|
|
}
|
|
|
|
|
|
bool PNS_SHOVE::reduceSpringback( 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 );
|
|
|
|
while( aForce.x != 0 || aForce.y != 0 )
|
|
{
|
|
PNS_JOINT* jt_next = m_currentNode->FindJoint( p0_pushed, aVia );
|
|
|
|
if( !jt_next )
|
|
break;
|
|
|
|
p0_pushed += aForce.Resize( 2 ); // make sure pushed via does not overlap with any existing joint
|
|
}
|
|
|
|
PNS_VIA* pushedVia = aVia->Clone();
|
|
pushedVia->SetPos( p0_pushed );
|
|
pushedVia->Mark( aVia->Marker() );
|
|
|
|
if( aVia->Marker() & MK_HEAD )
|
|
{
|
|
m_draggedVia = pushedVia;
|
|
m_draggedViaHeadSet.Clear();
|
|
}
|
|
|
|
if( !jt )
|
|
{
|
|
TRACEn( 1, "weird, can't find the center-of-via joint\n" );
|
|
return SH_INCOMPLETE;
|
|
}
|
|
|
|
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 = clone( 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( clone ( lp.second ) );
|
|
}
|
|
}
|
|
|
|
m_draggedViaHeadSet.Add ( pushedVia );
|
|
|
|
if ( aDryRun )
|
|
return SH_OK;
|
|
|
|
replaceItems ( aVia, pushedVia );
|
|
|
|
#ifdef DEBUG
|
|
m_logger.Log( aVia, 0, "obstacle-via" );
|
|
#endif
|
|
|
|
if( aVia->BelongsTo( m_currentNode ) )
|
|
delete aVia;
|
|
|
|
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 );
|
|
pushLine( lp.second );
|
|
}
|
|
else
|
|
m_currentNode->Remove( lp.first );
|
|
|
|
#ifdef DEBUG
|
|
m_logger.Log( lp.first, 2, "fan-pre" );
|
|
m_logger.Log( lp.second, 3, "fan-post" );
|
|
#endif
|
|
}
|
|
|
|
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 )
|
|
{
|
|
std::vector<PNS_LINE*> steps;
|
|
int n = 0;
|
|
PNS_LINE* cur = clone( aCurrent );
|
|
cur->ClearSegmentLinks();
|
|
|
|
PNS_JOINT* jt = m_currentNode->FindJoint( aObstacleVia->Pos(), aObstacleVia );
|
|
PNS_LINE* shoved = clone( 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 );
|
|
|
|
pushLine( shoved );
|
|
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 );
|
|
}
|
|
}
|
|
|
|
|
|
void PNS_SHOVE::pushLine( PNS_LINE* aL )
|
|
{
|
|
if( aL->LinkCount() >= 0 && ( aL->LinkCount() != aL->SegmentCount() ) )
|
|
{
|
|
printf("LC: %d SC %d\n", aL->LinkCount(), aL->SegmentCount() );
|
|
for(int i=0;i<aL->SegmentCount();i++)
|
|
{
|
|
SEG s = aL->CLine().CSegment(i);
|
|
printf("s %d: %d %d %d %d\n", i, s.A.x, s.A.y, s.B.x, s.B.y );
|
|
}
|
|
assert( false );
|
|
}
|
|
|
|
m_lineStack.push_back( aL );
|
|
m_optimizerQueue.push_back( aL );
|
|
}
|
|
|
|
|
|
void PNS_SHOVE::popLine( )
|
|
{
|
|
PNS_LINE* l = m_lineStack.back();
|
|
|
|
for( std::vector<PNS_LINE*>::iterator i = m_optimizerQueue.begin(); i != m_optimizerQueue.end(); )
|
|
{
|
|
if( ( *i ) == l )
|
|
{
|
|
i = m_optimizerQueue.erase( i );
|
|
}
|
|
else
|
|
i++;
|
|
}
|
|
|
|
m_lineStack.pop_back();
|
|
}
|
|
|
|
|
|
PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::shoveIteration( int aIter )
|
|
{
|
|
PNS_LINE* currentLine = m_lineStack.back();
|
|
PNS_NODE::OPT_OBSTACLE nearest;
|
|
SHOVE_STATUS st = SH_NULL;
|
|
|
|
PNS_ITEM::PnsKind search_order[] = { PNS_ITEM::SOLID, PNS_ITEM::VIA, PNS_ITEM::SEGMENT };
|
|
|
|
for( int i = 0; i < 3; i++ )
|
|
{
|
|
nearest = m_currentNode->NearestObstacle( currentLine, search_order[i] );
|
|
|
|
if( nearest )
|
|
break;
|
|
}
|
|
|
|
if( !nearest )
|
|
{
|
|
m_lineStack.pop_back();
|
|
return SH_OK;
|
|
}
|
|
|
|
PNS_ITEM* ni = nearest->m_item;
|
|
|
|
unwindStack( ni );
|
|
|
|
if( !ni->OfKind( PNS_ITEM::SOLID ) && ni->Rank() >= 0 && ni->Rank() > currentLine->Rank() )
|
|
{
|
|
switch( ni->Kind() )
|
|
{
|
|
case PNS_ITEM::VIA:
|
|
{
|
|
PNS_VIA* revVia = (PNS_VIA*) ni;
|
|
TRACE( 2, "iter %d: reverse-collide-via", aIter );
|
|
|
|
if( currentLine->EndsWithVia() && m_currentNode->CheckColliding( ¤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 );
|
|
pushLine( revLine );
|
|
break;
|
|
}
|
|
|
|
default:
|
|
assert( false );
|
|
}
|
|
}
|
|
else
|
|
{ // "forward" collisoins
|
|
switch( ni->Kind() )
|
|
{
|
|
case PNS_ITEM::SEGMENT:
|
|
TRACE( 2, "iter %d: collide-segment ", aIter );
|
|
st = onCollidingSegment( currentLine, (PNS_SEGMENT*) ni );
|
|
break;
|
|
|
|
case PNS_ITEM::VIA:
|
|
TRACE( 2, "iter %d: shove-via ", aIter );
|
|
st = onCollidingVia( currentLine, (PNS_VIA*) ni );
|
|
break;
|
|
|
|
case PNS_ITEM::SOLID:
|
|
TRACE( 2, "iter %d: walk-solid ", aIter );
|
|
st = onCollidingSolid( currentLine, (PNS_SOLID*) ni );
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
|
|
PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::shoveMainLoop()
|
|
{
|
|
SHOVE_STATUS st = SH_OK;
|
|
|
|
m_affectedAreaSum = OPT_BOX2I();
|
|
|
|
TRACE( 1, "ShoveStart [root: %d jts, current: %d jts]", m_root->JointCount() %
|
|
m_currentNode->JointCount() );
|
|
|
|
int iterLimit = Settings().ShoveIterationLimit();
|
|
TIME_LIMIT timeLimit = Settings().ShoveTimeLimit();
|
|
|
|
m_iter = 0;
|
|
|
|
timeLimit.Restart();
|
|
|
|
while( !m_lineStack.empty() )
|
|
{
|
|
st = shoveIteration( m_iter );
|
|
|
|
m_iter++;
|
|
|
|
if( st == SH_INCOMPLETE || timeLimit.Expired() || m_iter >= iterLimit )
|
|
{
|
|
st = SH_INCOMPLETE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
|
|
OPT_BOX2I PNS_SHOVE::totalAffectedArea() const
|
|
{
|
|
OPT_BOX2I area;
|
|
if( !m_nodeStack.empty() )
|
|
area = m_nodeStack.back().m_affectedArea;
|
|
|
|
if( area )
|
|
{
|
|
if ( m_affectedAreaSum )
|
|
area->Merge ( *m_affectedAreaSum );
|
|
} else
|
|
area = m_affectedAreaSum;
|
|
|
|
return area;
|
|
}
|
|
|
|
|
|
PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveLines( const PNS_LINE& aCurrentHead )
|
|
{
|
|
SHOVE_STATUS st = SH_OK;
|
|
|
|
m_multiLineMode = false;
|
|
|
|
// empty head? nothing to shove...
|
|
if( !aCurrentHead.SegmentCount() )
|
|
return SH_INCOMPLETE;
|
|
|
|
PNS_LINE* head = clone( &aCurrentHead );
|
|
head->ClearSegmentLinks();
|
|
|
|
m_lineStack.clear();
|
|
m_optimizerQueue.clear();
|
|
m_newHead = OPT_LINE();
|
|
m_logger.Clear();
|
|
|
|
PNS_ITEMSET headSet( clone( &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 );
|
|
|
|
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" );
|
|
}
|
|
|
|
pushLine( head );
|
|
st = shoveMainLoop();
|
|
runOptimizer( m_currentNode, head );
|
|
|
|
if( m_newHead && st == SH_OK )
|
|
{
|
|
st = SH_HEAD_MODIFIED;
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
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( clone( headOrig ) );
|
|
}
|
|
|
|
m_lineStack.clear();
|
|
m_optimizerQueue.clear();
|
|
m_logger.Clear();
|
|
|
|
reduceSpringback( headSet );
|
|
|
|
PNS_NODE* parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node;
|
|
|
|
m_currentNode = parent->Branch();
|
|
m_currentNode->ClearRanks();
|
|
int n = 0;
|
|
BOOST_FOREACH ( const PNS_ITEM* item, aHeadSet.CItems() )
|
|
{
|
|
const PNS_LINE* headOrig = static_cast<const PNS_LINE*>( item );
|
|
PNS_LINE* head = clone( headOrig );
|
|
head->ClearSegmentLinks();
|
|
|
|
m_currentNode->Add( head );
|
|
|
|
head->Mark( MK_HEAD );
|
|
head->SetRank( 100000 );
|
|
n++;
|
|
pushLine( head );
|
|
|
|
PNS_VIA* headVia = NULL;
|
|
|
|
if( head->EndsWithVia() )
|
|
{
|
|
headVia = head->Via().Clone(); // fixme: leak
|
|
m_currentNode->Add( headVia );
|
|
headVia->Mark( MK_HEAD );
|
|
headVia->SetRank( 100000 );
|
|
m_logger.Log( headVia, 0, "head-via" );
|
|
}
|
|
}
|
|
|
|
m_logger.NewGroup( "initial", 0 );
|
|
//m_logger.Log( head, 0, "head" );
|
|
|
|
st = shoveMainLoop();
|
|
runOptimizer( m_currentNode, NULL );
|
|
|
|
m_currentNode->RemoveByMarker( MK_HEAD );
|
|
|
|
TRACE( 1, "Shove status : %s after %d iterations",
|
|
( st == SH_OK ? "OK" : "FAILURE") % m_iter );
|
|
|
|
if( st == SH_OK )
|
|
{
|
|
pushSpringback( m_currentNode, PNS_ITEMSET(), PNS_COST_ESTIMATOR(), m_affectedAreaSum );
|
|
}
|
|
else
|
|
{
|
|
delete m_currentNode;
|
|
m_currentNode = parent;
|
|
}
|
|
|
|
return st;
|
|
}
|
|
|
|
|
|
PNS_SHOVE::SHOVE_STATUS PNS_SHOVE::ShoveDraggingVia( PNS_VIA* aVia, const VECTOR2I& aWhere,
|
|
PNS_VIA** aNewVia )
|
|
{
|
|
SHOVE_STATUS st = SH_OK;
|
|
|
|
m_lineStack.clear();
|
|
m_optimizerQueue.clear();
|
|
m_newHead = OPT_LINE();
|
|
m_draggedVia = NULL;
|
|
m_draggedViaHeadSet.Clear();
|
|
|
|
PNS_NODE* parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node;
|
|
|
|
m_currentNode = parent;
|
|
//st = pushVia( aVia, ( aWhere - aVia->Pos() ), 0, true );
|
|
//reduceSpringback( m_draggedViaHeadSet );
|
|
|
|
parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().m_node;
|
|
|
|
m_currentNode = parent->Branch();
|
|
m_currentNode->ClearRanks();
|
|
|
|
aVia->Mark( MK_HEAD );
|
|
|
|
st = pushVia( aVia, ( aWhere - aVia->Pos() ), 0 );
|
|
st = shoveMainLoop();
|
|
runOptimizer( m_currentNode, NULL );
|
|
//m_currentNode->RemoveByMarker( MK_HEAD );
|
|
|
|
if( st == SH_OK || st == SH_HEAD_MODIFIED )
|
|
{
|
|
pushSpringback( m_currentNode, 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_LINE* aHead )
|
|
{
|
|
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 );
|
|
}
|