From 1532a833308bef15f92485d43756aa29548a5e60 Mon Sep 17 00:00:00 2001 From: Tomasz Wlostowski Date: Wed, 14 Sep 2022 22:02:30 +0100 Subject: [PATCH] router: hole as first-class-object, initial version of rewritten collision check routine Rebased and modified by Jeff Young 6 April 2023 --- pcbnew/router/pns_hole.h | 5 + pcbnew/router/pns_item.cpp | 233 ++++++------------------ pcbnew/router/pns_item.h | 2 +- pcbnew/router/pns_kicad_iface.cpp | 47 ++++- pcbnew/router/pns_node.h | 1 + qa/unittests/pcbnew/test_pns_basics.cpp | 9 +- 6 files changed, 113 insertions(+), 184 deletions(-) diff --git a/pcbnew/router/pns_hole.h b/pcbnew/router/pns_hole.h index f75de4668b..a72b7a52c6 100644 --- a/pcbnew/router/pns_hole.h +++ b/pcbnew/router/pns_hole.h @@ -61,6 +61,11 @@ public: ITEM* ParentPadVia() const { return m_parentPadVia; } + BOARD_ITEM* Parent() const override + { + return m_parentPadVia ? m_parentPadVia->Parent() : nullptr; + } + void SetCenter( const VECTOR2I& aCenter ); void SetRadius( int aRadius ); diff --git a/pcbnew/router/pns_item.cpp b/pcbnew/router/pns_item.cpp index cfde15d769..4bbf81203d 100644 --- a/pcbnew/router/pns_item.cpp +++ b/pcbnew/router/pns_item.cpp @@ -19,12 +19,10 @@ * with this program. If not, see . */ -#include #include "pns_node.h" #include "pns_item.h" #include "pns_line.h" #include "pns_router.h" -#include "pns_utils.h" #include #include @@ -60,13 +58,43 @@ bool ITEM::collideSimple( const ITEM* aHead, const NODE* aNode, int clearanceEpsilon = aNode->GetRuleResolver()->ClearanceEpsilon(); bool collisionsFound = false; - printf( "******************** CollideSimple %lu\n", aCtx->obstacles.size() ); + //printf( "******************** CollideSimple %lu\n", aCtx->obstacles.size() ); //printf( "h %p n %p t %p ctx %p\n", aHead, aNode, this, aCtx ); if( aHead == this ) // we cannot be self-colliding return false; + // prevent bogus collisions between the item and its own hole. + // FIXME: figure out a cleaner way of doing that + if( holeI && aHead == holeI->ParentPadVia() ) + return false; + + if( holeH && this == holeH->ParentPadVia() ) + return false; + + if( holeH && this == holeH ) + return false; + + if( holeI && aHead == holeI ) + return false; + + // Special cases for "head" lines with vias attached at the end. Note that this does not + // support head-line-via to head-line-via collisions, but you can't route two independent + // tracks at once so it shouldn't come up. + + if( const auto line = dyn_cast( this ) ) + { + if( line->EndsWithVia() ) + collisionsFound |= line->Via().collideSimple( aHead, aNode, aCtx ); + } + + if( const auto line = dyn_cast( aHead ) ) + { + if( line->EndsWithVia() ) + collisionsFound |= line->Via().collideSimple( this, aNode, aCtx ); + } + // Sadly collision routines ignore SHAPE_POLY_LINE widths so we have to pass them in as part // of the clearance value. if( m_kind == LINE_T ) @@ -93,142 +121,14 @@ bool ITEM::collideSimple( const ITEM* aHead, const NODE* aNode, if( !m_layers.Overlaps( aHead->m_layers ) ) return false; - auto checkKeepout = - []( const ZONE* aKeepout, const BOARD_ITEM* aOther ) - { - if( aKeepout->GetDoNotAllowTracks() && aOther->IsType( { PCB_ARC_T, PCB_TRACE_T } ) ) - return true; + if( holeH ) + collisionsFound |= collideSimple( holeH, aNode, aCtx ); - if( aKeepout->GetDoNotAllowVias() && aOther->Type() == PCB_VIA_T ) - return true; + if( holeI ) + collisionsFound |= holeI->collideSimple( aHead, aNode, aCtx ); - if( aKeepout->GetDoNotAllowPads() && aOther->Type() == PCB_PAD_T ) - return true; - - // Incomplete test, but better than nothing: - if( aKeepout->GetDoNotAllowFootprints() && aOther->Type() == PCB_PAD_T ) - { - return !aKeepout->GetParentFootprint() - || aKeepout->GetParentFootprint() != aOther->GetParentFootprint(); - } - - return false; - }; - - const ZONE* zoneA = dynamic_cast( Parent() ); - const ZONE* zoneB = dynamic_cast( aHead->Parent() ); - - if( zoneA && aHead->Parent() && !checkKeepout( zoneA, aHead->Parent() ) ) - return false; - - if( zoneB && Parent() && !checkKeepout( zoneB, Parent() ) ) - return false; - - // fixme: this f***ing singleton must go... - ROUTER *router = ROUTER::GetInstance(); - ROUTER_IFACE* iface = router ? router->GetInterface() : nullptr; - - bool thisNotFlashed = false; - bool otherNotFlashed = false; - - if( iface ) - { - thisNotFlashed = !iface->IsFlashedOnLayer( this, aHead->Layer() ); - otherNotFlashed = !iface->IsFlashedOnLayer( aHead, Layer() ); - } - - if( ( aNode->GetCollisionQueryScope() == NODE::CQS_ALL_RULES - || ( thisNotFlashed || otherNotFlashed ) ) ) - { - if( holeI && holeI->ParentPadVia() != aHead && holeI != aHead ) - { - int holeClearance = aNode->GetClearance( this, holeI ); - printf("HCH1 %d\n", holeClearance); - - if( holeI->Shape()->Collide( shapeH, holeClearance + lineWidthH - clearanceEpsilon ) ) - { - if( aCtx ) - { - OBSTACLE obs; - obs.m_clearance = holeClearance; - obs.m_head = const_cast( aHead ); - obs.m_item = const_cast( holeI ); - - aCtx->obstacles.insert( obs ); - dumpObstacles( aCtx->obstacles ); - collisionsFound = true; - } - else - { - return true; - } - } - } - - if( holeH && holeH->ParentPadVia() != this && holeH != this ) - { - int holeClearance = aNode->GetClearance( this, holeH ); - - printf("HCH2 %d\n", holeClearance); - - if( holeH->Shape()->Collide( shapeI, holeClearance + lineWidthI - clearanceEpsilon ) ) - { - if( aCtx ) - { - OBSTACLE obs; - obs.m_clearance = holeClearance; - obs.m_head = const_cast( holeH ); - obs.m_item = const_cast( this ); - - aCtx->obstacles.insert( obs ); - dumpObstacles( aCtx->obstacles ); - collisionsFound = true; - } - else - { - return true; - } - } - } - - if( holeI && holeH && ( holeI != holeH ) ) - { - int holeClearance = aNode->GetClearance( holeI, holeH ); - - printf("HCH3 %d\n", holeClearance); - - if( holeI->Shape()->Collide( holeH->Shape(), holeClearance - clearanceEpsilon ) ) - { - if( aCtx ) - { - OBSTACLE obs; - obs.m_clearance = holeClearance; - - // printf("pushh3 %p %p\n", obs.m_head, obs.m_item ); - - obs.m_head = const_cast( holeH ); - obs.m_item = const_cast( holeI ); - - aCtx->obstacles.insert( obs ); - dumpObstacles( aCtx->obstacles ); - - collisionsFound = true; - } - else - { - return true; - } - } - } - } - - printf("HCHE\n"); - - if( !aHead->Layers().IsMultilayer() && thisNotFlashed ) - return false; - - if( !Layers().IsMultilayer() && otherNotFlashed ) - return false; + if( holeH && holeI ) + collisionsFound |= holeI->collideSimple( holeH, aNode, aCtx ); int clearance; @@ -236,28 +136,35 @@ bool ITEM::collideSimple( const ITEM* aHead, const NODE* aNode, { clearance = aCtx->options.m_overrideClearance; } + else if( aNode->GetRuleResolver()->IsKeepout( this, aHead ) ) + { + clearance = 0; // keepouts are exact boundary; no clearance + } else { clearance = aNode->GetClearance( this, aHead ); } - // prevent bogus collisions between the item and its own hole. FIXME: figure out a cleaner way of doing that - if( holeI && aHead == holeI->ParentPadVia() ) - return false; + // fixme: this f***ing singleton must go... + ROUTER* router = ROUTER::GetInstance(); + ROUTER_IFACE* iface = router ? router->GetInterface() : nullptr; - if( holeH && this == holeH->ParentPadVia() ) - return false; + if( iface ) + { + if( !iface->IsFlashedOnLayer( this, aHead->Layer() ) ) + return collisionsFound; - if( holeH && this == holeH ) - return false; - - if( holeI && aHead == holeI ) - return false; + if( !iface->IsFlashedOnLayer( aHead, Layer() ) ) + return collisionsFound; + } if( clearance >= 0 ) { + // Note: we can't do castellation or net-tie processing in GetClearance() because they + // depend on *where* the collision is. + bool checkCastellation = ( m_parent && m_parent->GetLayer() == Edge_Cuts ); - bool checkNetTie = aNode->GetRuleResolver()->IsInNetTie( this ); + bool checkNetTie = ( m_parent && aNode->GetRuleResolver()->IsInNetTie( this ) ); if( checkCastellation || checkNetTie ) { @@ -301,17 +208,7 @@ bool ITEM::collideSimple( const ITEM* aHead, const NODE* aNode, obs.m_head = const_cast( aHead ); obs.m_item = const_cast( this ); obs.m_clearance = clearance; - - //printf( "i %p h %p ih %p hh %p\n", this ,aHead, holeI, holeH ); - printf( "HCHX %d %d\n", clearance, clearance + lineWidthH + lineWidthI - clearanceEpsilon); - //printf( "pushc %p %p cl %d cle %d\n", obs.m_head, obs.m_item, clearance, clearance + lineWidthH + lineWidthI - clearanceEpsilon ); - //printf( "SH %s\n", shapeH->Format().c_str(), aHead ); - //printf( "SI %s\n", shapeI->Format().c_str(), this ); - aCtx->obstacles.insert( obs ); - - dumpObstacles( aCtx->obstacles ); - printf("--EndDump\n"); } else { @@ -330,26 +227,6 @@ bool ITEM::Collide( const ITEM* aOther, const NODE* aNode, COLLISION_SEARCH_CONT if( collideSimple( aOther, aNode, aCtx ) ) return true; - // Special cases for "head" lines with vias attached at the end. Note that this does not - // support head-line-via to head-line-via collisions, but you can't route two independent - // tracks at once so it shouldn't come up. - - if( m_kind == LINE_T ) - { - const LINE* line = static_cast( this ); - - if( line->EndsWithVia() && line->Via().collideSimple( aOther, aNode, aCtx ) ) - return true; - } - - if( aOther->m_kind == LINE_T ) - { - const LINE* line = static_cast( aOther ); // fixme - - if( line->EndsWithVia() && line->Via().collideSimple( this, aNode, aCtx ) ) - return true; - } - return false; } diff --git a/pcbnew/router/pns_item.h b/pcbnew/router/pns_item.h index 7fd8963ab1..fb6eceb929 100644 --- a/pcbnew/router/pns_item.h +++ b/pcbnew/router/pns_item.h @@ -178,7 +178,7 @@ public: std::string KindStr() const; void SetParent( BOARD_ITEM* aParent ) { m_parent = aParent; } - BOARD_ITEM* Parent() const { return m_parent; } + virtual BOARD_ITEM* Parent() const { return m_parent; } void SetNet( int aNet ) { m_net = aNet; } int Net() const { return m_net; } diff --git a/pcbnew/router/pns_kicad_iface.cpp b/pcbnew/router/pns_kicad_iface.cpp index 17e0806536..7425a933f2 100644 --- a/pcbnew/router/pns_kicad_iface.cpp +++ b/pcbnew/router/pns_kicad_iface.cpp @@ -116,6 +116,8 @@ public: virtual bool IsNetTieExclusion( const PNS::ITEM* aItem, const VECTOR2I& aCollisionPos, const PNS::ITEM* aCollidingItem ) override; + virtual bool IsKeepout( const PNS::ITEM* aA, const PNS::ITEM* aB ) override; + virtual bool QueryConstraint( PNS::CONSTRAINT_TYPE aType, const PNS::ITEM* aItemA, const PNS::ITEM* aItemB, int aLayer, PNS::CONSTRAINT* aConstraint ) override; @@ -246,6 +248,46 @@ bool PNS_PCBNEW_RULE_RESOLVER::IsNetTieExclusion( const PNS::ITEM* aItem, } +bool PNS_PCBNEW_RULE_RESOLVER::IsKeepout( const PNS::ITEM* aA, const PNS::ITEM* aB ) +{ + auto checkKeepout = + []( const ZONE* aKeepout, const BOARD_ITEM* aOther ) + { + if( aKeepout->GetDoNotAllowTracks() && aOther->IsType( { PCB_ARC_T, PCB_TRACE_T } ) ) + return true; + + if( aKeepout->GetDoNotAllowVias() && aOther->Type() == PCB_VIA_T ) + return true; + + if( aKeepout->GetDoNotAllowPads() && aOther->Type() == PCB_PAD_T ) + return true; + + // Incomplete test, but better than nothing: + if( aKeepout->GetDoNotAllowFootprints() && aOther->Type() == PCB_PAD_T ) + { + return !aKeepout->GetParentFootprint() + || aKeepout->GetParentFootprint() != aOther->GetParentFootprint(); + } + + return false; + }; + + if( const ZONE* zoneA = dynamic_cast( aA->Parent() ) ) + { + if( zoneA->GetIsRuleArea() && aB->Parent() ) + return checkKeepout( zoneA, aB->Parent() ); + } + + if( const ZONE* zoneB = dynamic_cast( aB->Parent() ) ) + { + if( zoneB->GetIsRuleArea() && aA->Parent() ) + return checkKeepout( zoneB, aA->Parent() ); + } + + return false; +} + + static bool isCopper( const PNS::ITEM* aItem ) { if ( !aItem ) @@ -429,7 +471,8 @@ int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* a rv = constraint.m_Value.Min(); } } - else if( isHole( aA ) || isHole( aB ) ) + + if( isHole( aA ) || isHole( aB ) ) { if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE, aA, aB, layer, &constraint ) ) { @@ -1460,7 +1503,7 @@ bool PNS_KICAD_IFACE_BASE::IsFlashedOnLayer( const PNS::ITEM* aItem, int aLayer if( aLayer < 0 ) return true; - if( aItem->Parent() ) + if( !aItem->OfKind( PNS::ITEM::HOLE_T ) && aItem->Parent() ) { switch( aItem->Parent()->Type() ) { diff --git a/pcbnew/router/pns_node.h b/pcbnew/router/pns_node.h index c76a099fc9..20ea54aa1a 100644 --- a/pcbnew/router/pns_node.h +++ b/pcbnew/router/pns_node.h @@ -148,6 +148,7 @@ public: virtual bool IsNetTieExclusion( const PNS::ITEM* aItem, const VECTOR2I& aCollisionPos, const PNS::ITEM* aCollidingItem )= 0; + virtual bool IsKeepout( const ITEM* aA, const ITEM* aB ) = 0; virtual bool QueryConstraint( CONSTRAINT_TYPE aType, const PNS::ITEM* aItemA, const PNS::ITEM* aItemB, int aLayer, diff --git a/qa/unittests/pcbnew/test_pns_basics.cpp b/qa/unittests/pcbnew/test_pns_basics.cpp index 3527c8dbd0..d6c6b9a0bf 100644 --- a/qa/unittests/pcbnew/test_pns_basics.cpp +++ b/qa/unittests/pcbnew/test_pns_basics.cpp @@ -208,13 +208,16 @@ public: } }; - virtual bool IsInNetTie( const PNS::ITEM* aA ) override { return false; } - virtual bool IsNetTieExclusion( const PNS::ITEM* aItem, const VECTOR2I& aCollisionPos, - const PNS::ITEM* aCollidingItem ) override + bool IsInNetTie( const PNS::ITEM* aA ) override { return false; } + + bool IsNetTieExclusion( const PNS::ITEM* aItem, const VECTOR2I& aCollisionPos, + const PNS::ITEM* aCollidingItem ) override { return false; } + bool IsKeepout( const PNS::ITEM* aA, const PNS::ITEM* aB ) override { return false; } + void AddMockRule( PNS::CONSTRAINT_TYPE aType, const PNS::ITEM* aItemA, const PNS::ITEM* aItemB, PNS::CONSTRAINT aConstraint ) {