kicad/pcbnew/router/pns_topology.cpp

387 lines
9.8 KiB
C++
Raw Normal View History

/*
* 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>
2015-02-18 16:53:46 +00:00
bool PNS_TOPOLOGY::SimplifyLine( PNS_LINE* aLine )
{
2015-02-18 16:53:46 +00:00
if( !aLine->LinkedSegments() || !aLine->SegmentCount() )
return false;
2015-02-18 16:53:46 +00:00
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;
}
2015-02-18 16:53:46 +00:00
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();
for( PNS_ITEM* item : current->LinkList() )
{
2015-02-18 16:53:46 +00:00
if( item->OfKind( PNS_ITEM::SEGMENT ) )
{
2015-02-18 16:53:46 +00:00
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 );
}
2015-02-18 16:53:46 +00:00
}
}
}
return processed;
}
2015-02-18 16:53:46 +00:00
bool PNS_TOPOLOGY::LeadingRatLine( const PNS_LINE* aTrack, SHAPE_LINE_CHAIN& aRatLine )
{
2015-02-18 16:53:46 +00:00
PNS_LINE track( *aTrack );
VECTOR2I end;
if( !track.PointCount() )
return false;
std::unique_ptr<PNS_NODE> tmpNode( m_world->Branch() );
tmpNode->Add( &track );
PNS_JOINT* jt = tmpNode->FindJoint( track.CPoint( -1 ), &track );
2015-02-18 16:53:46 +00:00
if( !jt )
return false;
2015-02-18 16:53:46 +00:00
if( ( !track.EndsWithVia() && jt->LinkCount() >= 2 ) || ( track.EndsWithVia() && jt->LinkCount() >= 3 ) ) // we got something connected
{
end = jt->Pos();
2015-02-18 16:53:46 +00:00
}
else
{
int anchor;
2015-02-18 16:53:46 +00:00
PNS_TOPOLOGY topo( tmpNode.get() );
PNS_ITEM* it = topo.NearestUnconnectedItem( jt, &anchor );
if( !it )
return false;
end = it->Anchor( anchor );
}
aRatLine.Clear();
2015-02-18 16:53:46 +00:00
aRatLine.Append( track.CPoint( -1 ) );
aRatLine.Append( end );
return true;
}
2015-07-02 14:11:15 +00:00
PNS_ITEM* PNS_TOPOLOGY::NearestUnconnectedItem( PNS_JOINT* aStart, int* aAnchor, int aKindMask )
{
std::set<PNS_ITEM*> disconnected;
2015-02-18 16:53:46 +00:00
m_world->AllItemsInNet( aStart->Net(), disconnected );
for( const PNS_JOINT* jt : ConnectedJoints( aStart ) )
{
for( PNS_ITEM* link : jt->LinkList() )
{
if( disconnected.find( link ) != disconnected.end() )
disconnected.erase( link );
2015-02-18 16:53:46 +00:00
}
}
int best_dist = INT_MAX;
PNS_ITEM* best = NULL;
for( PNS_ITEM* item : disconnected )
{
if( item->OfKind( aKindMask ) )
{
2015-02-18 16:53:46 +00:00
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;
}
2015-02-18 16:53:46 +00:00
bool PNS_TOPOLOGY::followTrivialPath( PNS_LINE* aLine, bool aLeft, PNS_ITEMSET& aSet, std::set<PNS_ITEM*>& aVisited )
{
2015-02-18 16:53:46 +00:00
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 );
2015-02-18 16:53:46 +00:00
assert( jt != NULL );
aVisited.insert( last );
if( jt->IsNonFanoutVia() || jt->IsTraceWidthChange() )
{
2015-02-18 16:53:46 +00:00
PNS_ITEM* via = NULL;
PNS_SEGMENT* next_seg = NULL;
for( PNS_ITEM* link : jt->Links().Items() )
{
2015-02-18 16:53:46 +00:00
if( link->OfKind( PNS_ITEM::VIA ) )
via = link;
2015-02-18 16:53:46 +00:00
else if( aVisited.find( link ) == aVisited.end() )
next_seg = static_cast<PNS_SEGMENT*>( link );
}
2015-02-18 16:53:46 +00:00
if( !next_seg )
return false;
PNS_LINE l = m_world->AssembleLine( next_seg );
2015-02-18 16:53:46 +00:00
VECTOR2I nextAnchor = ( aLeft ? l.CLine().CPoint( -1 ) : l.CLine().CPoint( 0 ) );
2015-02-18 16:53:46 +00:00
if( nextAnchor != anchor )
{
l.Reverse();
}
2015-02-18 16:53:46 +00:00
if( aLeft )
{
if( via )
aSet.Prepend( via );
2015-02-18 16:53:46 +00:00
aSet.Prepend( l );
}
else
{
if( via )
aSet.Add( via );
2015-02-18 16:53:46 +00:00
aSet.Add( l );
}
return followTrivialPath( &l, aLeft, aSet, aVisited );
}
2015-02-18 16:53:46 +00:00
return false;
}
2015-02-18 16:53:46 +00:00
const PNS_ITEMSET PNS_TOPOLOGY::AssembleTrivialPath( PNS_ITEM* aStart )
{
PNS_ITEMSET path;
2015-02-18 16:53:46 +00:00
std::set<PNS_ITEM*> visited;
PNS_SEGMENT *seg;
PNS_VIA *via;
seg = dyn_cast<PNS_SEGMENT*> (aStart);
if(!seg && (via = dyn_cast<PNS_VIA *>( aStart ) ) )
{
PNS_JOINT *jt = m_world->FindJoint( via->Pos(), via );
if( !jt->IsNonFanoutVia () )
return PNS_ITEMSET();
for ( auto entry : jt->Links().Items() )
if ( ( seg = dyn_cast<PNS_SEGMENT*>( entry.item ) ) )
break;
}
if( !seg )
return PNS_ITEMSET();
PNS_LINE l = m_world->AssembleLine( seg );
2015-02-18 16:53:46 +00:00
path.Add( l );
followTrivialPath( &l, false, path, visited );
followTrivialPath( &l, true, path, visited );
return path;
}
2015-02-18 16:53:46 +00:00
const PNS_ITEMSET PNS_TOPOLOGY::ConnectedItems( PNS_JOINT* aStart, int aKindMask )
{
return PNS_ITEMSET();
}
2015-02-18 16:53:46 +00:00
const PNS_ITEMSET PNS_TOPOLOGY::ConnectedItems( PNS_ITEM* aStart, int aKindMask )
{
return PNS_ITEMSET();
}
2015-02-18 16:53:46 +00:00
2015-07-02 14:11:15 +00:00
bool commonParallelProjection( SEG n, SEG p, SEG &pClip, SEG& nClip );
2015-02-18 16:53:46 +00:00
2015-07-02 14:11:15 +00:00
2015-02-18 16:53:46 +00:00
bool PNS_TOPOLOGY::AssembleDiffPair( PNS_ITEM* aStart, PNS_DIFF_PAIR& aPair )
{
int refNet = aStart->Net();
int coupledNet = m_world->GetRuleResolver()->DpCoupledNet( refNet );
2015-02-18 16:53:46 +00:00
if( coupledNet < 0 )
return false;
2015-02-18 16:53:46 +00:00
std::set<PNS_ITEM*> coupledItems;
m_world->AllItemsInNet( coupledNet, coupledItems );
2015-07-02 14:11:15 +00:00
PNS_SEGMENT* coupledSeg = NULL, *refSeg;
int minDist = std::numeric_limits<int>::max();
if( ( refSeg = dyn_cast<PNS_SEGMENT*>( aStart ) ) != NULL )
{
for( PNS_ITEM* item : coupledItems )
{
2015-02-18 16:53:46 +00:00
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;
}
2015-02-18 16:53:46 +00:00
if( !coupledSeg )
return false;
PNS_LINE lp = m_world->AssembleLine( refSeg );
PNS_LINE ln = m_world->AssembleLine( coupledSeg );
if( m_world->GetRuleResolver()->DpNetPolarity( refNet ) < 0 )
{
2015-02-18 16:53:46 +00:00
std::swap( lp, ln );
}
2015-02-18 16:53:46 +00:00
int gap = -1;
2015-07-02 14:11:15 +00:00
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 );
2015-02-18 16:53:46 +00:00
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 );
for( 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;
}