kicad/pcbnew/router/pns_shove.cpp

492 lines
13 KiB
C++
Raw Normal View History

/*
* KiRouter - a push-and-(sometimes-)shove PCB router
*
* Copyright (C) 2013 CERN
* Author: Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
2013-09-26 21:53:54 +00:00
*
* 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.
2013-09-26 21:53:54 +00:00
*
* 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.
2013-09-26 21:53:54 +00:00
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.or/licenses/>.
*/
#include <deque>
#include <cassert>
#include <wx/timer.h>
#include "trace.h"
#include "pns_line.h"
#include "pns_node.h"
#include "pns_walkaround.h"
#include "pns_shove.h"
#include "pns_optimizer.h"
#include "pns_via.h"
#include "pns_utils.h"
#include <profile.h>
2013-09-26 21:53:54 +00:00
PNS_SHOVE::PNS_SHOVE( PNS_NODE* aWorld )
{
2013-09-26 21:53:54 +00:00
m_root = aWorld;
m_iterLimit = 100;
};
2013-09-26 21:53:54 +00:00
PNS_SHOVE::~PNS_SHOVE()
{
}
2013-09-26 21:53:54 +00:00
struct range
{
range()
{
min_v = max_v = -1;
}
void add( int x )
{
if( min_v < 0 ) min_v = x;
if( max_v < 0 ) max_v = x;
if( x < min_v )
min_v = x;
else if( x > max_v )
max_v = x;
}
int start()
{
return min_v;
}
int end()
{
return max_v;
}
int min_v, max_v;
};
// fixme: this is damn f***ing inefficient. And fails much too often due to broken direction finding algorithm.
2013-09-26 21:53:54 +00:00
bool PNS_SHOVE::tryShove( PNS_NODE* aNode, PNS_LINE* aHead, PNS_LINE* aObstacle,
PNS_SEGMENT& aObstacleSeg, PNS_LINE* aResult, bool aInvertWinding )
{
2013-09-26 21:53:54 +00:00
const SHAPE_LINE_CHAIN& head = aHead->GetCLine();
bool cw = false;
int i;
if( aHead->EndsWithVia() && !aHead->GetLayers().Overlaps( aObstacle->GetLayers() ) )
{
int clearance = aNode->GetClearance( aHead, aObstacle );
SHAPE_LINE_CHAIN hull = aHead->GetVia().Hull( clearance - aObstacle->GetWidth() / 2 );
// SHAPE_LINE_CHAIN path_pre, path_walk_cw, path_walk_ccw, path_post;
SHAPE_LINE_CHAIN path_cw, path_ccw, * path;
aObstacle->NewWalkaround( hull, path_cw, true );
aObstacle->NewWalkaround( hull, path_ccw, false );
path = path_ccw.Length() < path_cw.Length() ? &path_ccw : &path_cw;
aResult->SetShape( *path );
// PNSDisplayDebugLine (*path, 5);
if( !aResult->Is45Degree() )
{
// printf("polyset non-45\npoly %s\nendpolyset\n", aResult->GetCLine().Format().c_str());
}
/*... special case for vias? */
return !aNode->CheckColliding( aResult, aHead );
}
int ns = head.SegmentCount();
if( aHead->EndsWithVia() )
ns++;
for( i = 0; i < head.SegmentCount(); i++ )
{
const PNS_SEGMENT hs( *aHead, head.CSegment( i ) );
if( aNode->CheckColliding( &hs, aObstacle ) )
{
2013-10-14 18:40:36 +00:00
VECTOR2I v1 = hs.GetSeg().B - hs.GetSeg().A;
VECTOR2I v2 = aObstacleSeg.GetSeg().B - aObstacleSeg.GetSeg().A;
2013-09-26 21:53:54 +00:00
VECTOR2I::extended_type det = v1.Cross( v2 );
if( det > 0 )
cw = true;
else
cw = false;
break;
}
}
if( aInvertWinding )
{
if( cw )
cw = false;
else
cw = true;
}
PNS_LINE shoved( *aObstacle );
int clearance = aNode->GetClearance( aHead, aObstacle );
range r;
for( i = 0; i < ns; i++ )
{
SHAPE_LINE_CHAIN hull;
if( i < head.SegmentCount() )
{
const PNS_SEGMENT hs( *aHead, head.CSegment( i ) );
hull = hs.Hull( clearance, 0 );
}
else
hull = aHead->GetVia().Hull( clearance - aObstacle->GetWidth() / 2 );
SHAPE_LINE_CHAIN path_pre, path_walk, path_post, tmp;
SHAPE_LINE_CHAIN path_pre2, path_walk2, path_post2;
// shoved.NewWalkaround(hull, path_pre, path_walk, path_post, cw);
shoved.NewWalkaround( hull, path_pre, path_walk, path_post, cw );
/*if(path_pre != path_pre2 || path_post != path_post2 || path_walk != path_walk2 )
* {
* TRACE(5, "polyset orig\npoly %s\npoly %s\npoly %s\nendpolyset\n", path_pre.Format().c_str() % path_walk.Format().c_str() % path_post.Format().c_str());
* TRACE(5, "polyset err\npoly %s\npoly %s\npoly %s\nendpolyset\n", path_pre2.Format().c_str() % path_walk2.Format().c_str() % path_post2.Format().c_str());
* }*/
tmp = shoved.GetCLine();
if( path_walk.SegmentCount() )
r.add( i );
path_pre.Append( path_walk );
path_pre.Append( path_post );
path_pre.Simplify();
shoved.SetShape( path_pre );
// shoved.SetAffectedRange ( start, end );
*aResult = shoved;
if( !aResult->Is45Degree() )
{
// TRACE(5, "polyset non-45\npoly %s\npoly %s\npoly %s\nendpolyset\n", tmp.Format().c_str() % hull.Format().c_str() % aResult->GetCLine().Format().c_str());
}
}
TRACE( 2, "CW %d affectedRange %d-%d [total %d]", (cw ? 1 : 0) % r.start() % r.end() % ns );
return !aNode->CheckColliding( aResult, aHead );
}
2013-09-26 21:53:54 +00:00
PNS_SHOVE::ShoveStatus PNS_SHOVE::shoveSingleLine( PNS_NODE* aNode, PNS_LINE* aCurrent,
PNS_LINE* aObstacle, PNS_SEGMENT& aObstacleSeg, PNS_LINE* aResult )
{
2013-09-26 21:53:54 +00:00
bool rv = tryShove( aNode, aCurrent, aObstacle, aObstacleSeg, aResult, false );
2013-09-26 21:53:54 +00:00
if( !rv )
rv = tryShove( aNode, aCurrent, aObstacle, aObstacleSeg, aResult, true );
2013-09-26 21:53:54 +00:00
if( !rv )
{
TRACEn( 2, "Shove failed" );
return SH_INCOMPLETE;
}
2013-09-26 21:53:54 +00:00
aResult->GetLine().Simplify();
2013-09-26 21:53:54 +00:00
const SHAPE_LINE_CHAIN& sh_shoved = aResult->GetCLine();
const SHAPE_LINE_CHAIN& sh_orig = aObstacle->GetCLine();
2013-09-26 21:53:54 +00:00
if( sh_shoved.SegmentCount() > 1 && sh_shoved.CPoint( 0 ) == sh_orig.CPoint( 0 )
&& sh_shoved.CPoint( -1 ) == sh_orig.CPoint( -1 ) )
return SH_OK;
else if( !sh_shoved.SegmentCount() )
return SH_NULL;
else
return SH_INCOMPLETE;
}
2013-09-26 21:53:54 +00:00
bool PNS_SHOVE::reduceSpringback( PNS_LINE* aHead )
{
2013-09-26 21:53:54 +00:00
bool rv = false;
while( !m_nodeStack.empty() )
{
SpringbackTag st_stack = m_nodeStack.back();
bool tail_ok = true;
if( !st_stack.node->CheckColliding( aHead ) && tail_ok )
{
rv = true;
delete st_stack.node;
m_nodeStack.pop_back();
}
else
break;
}
return rv;
}
2013-09-26 21:53:54 +00:00
bool PNS_SHOVE::pushSpringback( PNS_NODE* aNode, PNS_LINE* aHead, const PNS_COST_ESTIMATOR& aCost )
{
2013-09-26 21:53:54 +00:00
BOX2I headBB = aHead->GetCLine().BBox();
SpringbackTag st;
st.node = aNode;
st.cost = aCost;
st.length = std::max( headBB.GetWidth(), headBB.GetHeight() );;
m_nodeStack.push_back( st );
return true;
}
2013-09-26 21:53:54 +00:00
const PNS_COST_ESTIMATOR PNS_SHOVE::TotalCost() const
{
2013-09-26 21:53:54 +00:00
if( m_nodeStack.empty() )
return PNS_COST_ESTIMATOR();
else
return m_nodeStack.back().cost;
}
2013-09-26 21:53:54 +00:00
PNS_SHOVE::ShoveStatus PNS_SHOVE::ShoveLines( PNS_LINE* aCurrentHead )
{
std::stack<PNS_LINE*> lineStack;
2013-09-26 21:53:54 +00:00
PNS_NODE* node, * parent;
PNS_VIA* headVia = NULL;
2013-09-26 21:53:54 +00:00
bool fail = false;
int iter = 0;
2013-09-26 21:53:54 +00:00
PNS_LINE* head = aCurrentHead->Clone();
2013-09-26 21:53:54 +00:00
reduceSpringback( aCurrentHead );
2013-09-26 21:53:54 +00:00
parent = m_nodeStack.empty() ? m_root : m_nodeStack.back().node;
node = parent->Branch();
2013-09-26 21:53:54 +00:00
lineStack.push( head );
2013-09-26 21:53:54 +00:00
// node->Add(tail);
node->Add( head );
2013-09-26 21:53:54 +00:00
if( head->EndsWithVia() )
{
headVia = head->GetVia().Clone();
node->Add( headVia );
}
2013-09-26 21:53:54 +00:00
PNS_OPTIMIZER optimizer( node );
2013-09-26 21:53:54 +00:00
optimizer.SetEffortLevel( PNS_OPTIMIZER::MERGE_SEGMENTS | PNS_OPTIMIZER::SMART_PADS );
optimizer.SetCollisionMask( -1 );
PNS_NODE::OptObstacle nearest;
2013-09-26 21:53:54 +00:00
optimizer.CacheStaticItem( head );
2013-09-26 21:53:54 +00:00
if( headVia )
optimizer.CacheStaticItem( headVia );
TRACE( 1, "ShoveStart [root: %d jts, node: %d jts]", m_root->JointCount() %
node->JointCount() );
// PNS_ITEM *lastWalkSolid = NULL;
prof_counter totalRealTime;
wxLongLong t_start = wxGetLocalTimeMillis();
while( !lineStack.empty() )
{
wxLongLong t_cur = wxGetLocalTimeMillis();
if( (t_cur - t_start).ToLong() > ShoveTimeLimit )
{
fail = true;
break;
}
iter++;
if( iter > m_iterLimit )
{
fail = true;
break;
}
PNS_LINE* currentLine = lineStack.top();
prof_start( &totalRealTime );
2013-09-26 21:53:54 +00:00
nearest = node->NearestObstacle( currentLine, PNS_ITEM::ANY );
prof_end( &totalRealTime );
TRACE( 2, "t-nearestObstacle %lld us", totalRealTime.usecs() );
2013-09-26 21:53:54 +00:00
if( !nearest )
{
if( lineStack.size() > 1 )
{
PNS_LINE* original = lineStack.top();
PNS_LINE optimized;
int r_start, r_end;
original->GetAffectedRange( r_start, r_end );
TRACE( 1, "Iter %d optimize-line [range %d-%d, total %d]",
iter % r_start % r_end % original->GetCLine().PointCount() );
// lastWalkSolid = NULL;
prof_start( &totalRealTime );
2013-09-26 21:53:54 +00:00
if( optimizer.Optimize( original, &optimized ) )
{
node->Remove( original );
optimizer.CacheRemove( original );
node->Add( &optimized );
if( original->BelongsTo( node ) )
delete original;
}
prof_end( &totalRealTime );
TRACE( 2, "t-optimizeObstacle %lld us", totalRealTime.usecs() );
2013-09-26 21:53:54 +00:00
}
lineStack.pop();
}
else
{
switch( nearest->item->GetKind() )
{
case PNS_ITEM::SEGMENT:
{
TRACE( 1, "Iter %d shove-line", iter );
PNS_SEGMENT* pseg = static_cast<PNS_SEGMENT*>(nearest->item);
PNS_LINE* collidingLine = node->AssembleLine( pseg );
PNS_LINE* shovedLine = collidingLine->CloneProperties();
prof_start( &totalRealTime );
2013-09-26 21:53:54 +00:00
ShoveStatus st = shoveSingleLine( node, currentLine, collidingLine,
*pseg, shovedLine );
prof_end( &totalRealTime );
TRACE( 2, "t-shoveSingle %lld us", totalRealTime.usecs() );
2013-09-26 21:53:54 +00:00
if( st == SH_OK )
{
node->Replace( collidingLine, shovedLine );
if( collidingLine->BelongsTo( node ) )
delete collidingLine;
optimizer.CacheRemove( collidingLine );
lineStack.push( shovedLine );
}
else
fail = true;
// lastWalkSolid = NULL;
break;
} // case SEGMENT
case PNS_ITEM::SOLID:
case PNS_ITEM::VIA:
{
TRACE( 1, "Iter %d walkaround-solid [%p]", iter % nearest->item );
if( lineStack.size() == 1 )
{
fail = true;
break;
}
/* if(lastWalkSolid == nearest->item)
2013-09-26 21:53:54 +00:00
* {
* fail = true;
* break;
* }*/
PNS_WALKAROUND walkaround( node );
PNS_LINE* walkaroundLine = currentLine->CloneProperties();
walkaround.SetSolidsOnly( true );
walkaround.SetSingleDirection( true );
prof_start( &totalRealTime );
2013-09-26 21:53:54 +00:00
walkaround.Route( *currentLine, *walkaroundLine, false );
prof_end( &totalRealTime );
TRACE( 2, "t-walkSolid %lld us", totalRealTime.usecs() );
2013-09-26 21:53:54 +00:00
node->Replace( currentLine, walkaroundLine );
if( currentLine->BelongsTo( node ) )
delete currentLine;
optimizer.CacheRemove( currentLine );
lineStack.top() = walkaroundLine;
// lastWalkSolid = nearest->item;
break;
}
default:
break;
} // switch
if( fail )
break;
}
}
node->Remove( head );
delete head;
if( headVia )
{
node->Remove( headVia );
delete headVia;
}
TRACE( 1, "Shove status : %s after %d iterations", (fail ? "FAILED" : "OK") % iter );
if( !fail )
{
pushSpringback( node, aCurrentHead, PNS_COST_ESTIMATOR() );
return SH_OK;
}
else
{
delete node;
return SH_INCOMPLETE;
}
}