diff --git a/pcbnew/router/pns_component_dragger.cpp b/pcbnew/router/pns_component_dragger.cpp index 3aa81b9208..012282d602 100644 --- a/pcbnew/router/pns_component_dragger.cpp +++ b/pcbnew/router/pns_component_dragger.cpp @@ -100,7 +100,7 @@ bool COMPONENT_DRAGGER::Start( const VECTOR2I& aP, ITEM_SET& aPrimitives ) { LINKED_ITEM* li = static_cast( extraJoint->LinkList()[0].item ); - if( li->Collide( solid, 0, false, nullptr, nullptr, false ) ) + if( li->Collide( solid, 0, m_world ) ) addLinked( solid, li, extraJoint->Pos() - solid->Pos() ); } } diff --git a/pcbnew/router/pns_index.cpp b/pcbnew/router/pns_index.cpp index 439a1b90ce..f9e1315b99 100644 --- a/pcbnew/router/pns_index.cpp +++ b/pcbnew/router/pns_index.cpp @@ -33,28 +33,7 @@ void INDEX::Add( ITEM* aItem ) m_subIndices.resize( 2 * range.End() + 1 ); // +1 handles the 0 case for( int i = range.Start(); i <= range.End(); ++i ) - { - if( !ROUTER::GetInstance()->GetInterface()->IsOnLayer( aItem, i ) ) - { - if( aItem->AlternateShape() ) - { - m_subIndices[i].Add( aItem, aItem->AlternateShape()->BBox() ); - } - else - { - wxLogError( "Missing expected Alternate shape for %s at %d %d", - aItem->Parent()->GetClass(), - aItem->Anchor( 0 ).x, - aItem->Anchor( 0 ).y ); - m_subIndices[i].Add( aItem ); - } - - } - else - { - m_subIndices[i].Add( aItem ); - } - } + m_subIndices[i].Add( aItem ); m_allItems.insert( aItem ); int net = aItem->Net(); diff --git a/pcbnew/router/pns_item.cpp b/pcbnew/router/pns_item.cpp index 9a52ab0551..f37799e63a 100644 --- a/pcbnew/router/pns_item.cpp +++ b/pcbnew/router/pns_item.cpp @@ -28,12 +28,23 @@ typedef VECTOR2I::extended_type ecoord; namespace PNS { -bool ITEM::collideSimple( const ITEM* aOther, int aClearance, bool aNeedMTV, VECTOR2I* aMTV, - const NODE* aParentNode, bool aDifferentNetsOnly ) const +bool ITEM::collideSimple( const ITEM* aOther, const NODE* aNode, bool aDifferentNetsOnly ) const { const ROUTER_IFACE* iface = ROUTER::GetInstance()->GetInterface(); const SHAPE* shapeA = Shape(); + const SHAPE* holeA = Hole(); + int lineWidthA = 0; const SHAPE* shapeB = aOther->Shape(); + const SHAPE* holeB = aOther->Hole(); + int lineWidthB = 0; + + // 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 ) + lineWidthA = static_cast( this )->Width() / 2; + + if( aOther->m_kind == LINE_T ) + lineWidthB = static_cast( aOther )->Width() / 2; // same nets? no collision! if( aDifferentNetsOnly && m_net == aOther->m_net && m_net >= 0 && aOther->m_net >= 0 ) @@ -43,62 +54,69 @@ bool ITEM::collideSimple( const ITEM* aOther, int aClearance, bool aNeedMTV, VEC if( !m_layers.Overlaps( aOther->m_layers ) ) return false; - if( !aOther->Layers().IsMultilayer() && !iface->IsOnLayer( this, aOther->Layer() ) ) + if( holeA || holeB ) { - if( !AlternateShape() ) + int holeClearance = aNode->GetHoleClearance( this, aOther ); + + if( holeA && holeA->Collide( shapeB, holeClearance + lineWidthB ) ) { - wxLogError( "Missing expected Alternate shape for %s at %d %d", - m_parent->GetClass(), - Anchor( 0 ).x, - Anchor( 0 ).y ); + Mark( MK_HOLE ); + return true; } - else + + if( holeB && holeB->Collide( shapeA, holeClearance + lineWidthA ) ) { - shapeA = AlternateShape(); - Mark( MK_ALT_SHAPE ); + aOther->Mark( MK_HOLE ); + return true; + } + + if( holeA && holeB ) + { + int holeToHoleClearance = aNode->GetHoleToHoleClearance( this, aOther ); + + if( holeA->Collide( holeB, holeToHoleClearance ) ) + { + Mark( MK_HOLE ); + aOther->Mark( MK_HOLE ); + return true; + } } } - if( !Layers().IsMultilayer() && !iface->IsOnLayer( aOther, Layer() ) ) - { - if( !aOther->AlternateShape() ) - { - wxLogError( "Missing expected Alternate shape for %s at %d %d", - aOther->Parent()->GetClass(), - aOther->Anchor( 0 ).x, - aOther->Anchor( 0 ).y ); - } - else - { - shapeB = aOther->AlternateShape(); - aOther->Mark( MK_ALT_SHAPE ); - } - } + if( !aOther->Layers().IsMultilayer() && !iface->IsFlashedOnLayer( this, aOther->Layer()) ) + return false; - if( aNeedMTV ) - return shapeA->Collide( shapeB, aClearance, aMTV ); - else - return shapeA->Collide( shapeB, aClearance ); + if( !Layers().IsMultilayer() && !iface->IsFlashedOnLayer( aOther, Layer()) ) + return false; + + int clearance = aNode->GetClearance( this, aOther ); + return shapeA->Collide( shapeB, clearance + lineWidthA + lineWidthB ); } -bool ITEM::Collide( const ITEM* aOther, int aClearance, bool aNeedMTV, VECTOR2I* aMTV, - const NODE* aParentNode, bool aDifferentNetsOnly ) const +bool ITEM::Collide( const ITEM* aOther, const NODE* aNode, bool aDifferentNetsOnly ) const { - if( collideSimple( aOther, aClearance, aNeedMTV, aMTV, aParentNode, aDifferentNetsOnly ) ) + if( collideSimple( aOther, aNode, aDifferentNetsOnly ) ) return true; - // special case for "head" line with a via attached at the end. + // 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 independant + // 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, aDifferentNetsOnly ) ) + return true; + } + if( aOther->m_kind == LINE_T ) { const LINE* line = static_cast( aOther ); - int clearance = aClearance - line->Width() / 2; - if( line->EndsWithVia() ) - { - return collideSimple( &line->Via(), clearance, aNeedMTV, aMTV, aParentNode, - aDifferentNetsOnly ); - } + if( line->EndsWithVia() && line->Via().collideSimple( this, aNode, aDifferentNetsOnly ) ) + return true; } return false; @@ -109,14 +127,14 @@ std::string ITEM::KindStr() const { switch( m_kind ) { - case ARC_T: return "arc"; - case LINE_T: return "line"; - case SEGMENT_T: return "segment"; - case VIA_T: return "via"; - case JOINT_T: return "joint"; - case SOLID_T: return "solid"; - case DIFF_PAIR_T: return "diff-pair"; - default: return "unknown"; + case ARC_T: return "arc"; + case LINE_T: return "line"; + case SEGMENT_T: return "segment"; + case VIA_T: return "via"; + case JOINT_T: return "joint"; + case SOLID_T: return "solid"; + case DIFF_PAIR_T: return "diff-pair"; + default: return "unknown"; } } diff --git a/pcbnew/router/pns_item.h b/pcbnew/router/pns_item.h index f7c9a37026..48e8f429b5 100644 --- a/pcbnew/router/pns_item.h +++ b/pcbnew/router/pns_item.h @@ -41,8 +41,7 @@ enum LineMarker { MK_VIOLATION = ( 1 << 3 ), MK_LOCKED = ( 1 << 4 ), MK_DP_COUPLED = ( 1 << 5 ), - MK_ALT_SHAPE = ( 1 << 6 ) // For instance, a simple annular ring when the pad has - // been dropped from a layer. + MK_HOLE = ( 1 << 6 ) }; @@ -117,6 +116,11 @@ public: return SHAPE_LINE_CHAIN(); } + virtual const SHAPE_LINE_CHAIN HoleHull( int aClearance, int aWalkaroundThickness, int aLayer ) const + { + return SHAPE_LINE_CHAIN(); + } + /** * Function Kind() * @@ -199,13 +203,9 @@ public: * 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 ITEM* aOther, int aClearance, bool aNeedMTV, VECTOR2I* aMTV, - const NODE* aParentNode, bool aDifferentNetsOnly = true ) const; + bool Collide( const ITEM* aOther, const NODE* aNode, bool aDifferentNetsOnly = true ) const; /** * Function Shape() @@ -215,12 +215,12 @@ public: */ virtual const SHAPE* Shape() const { - return NULL; + return nullptr; } - virtual const SHAPE* AlternateShape() const + virtual const SHAPE* Hole() const { - return Shape(); + return nullptr; } virtual void Mark( int aMarker ) const { m_marker = aMarker; } @@ -249,8 +249,7 @@ public: bool IsRoutable() const { return m_routable; } private: - bool collideSimple( const ITEM* aOther, int aClearance, bool aNeedMTV, VECTOR2I* aMTV, - const NODE* aParentNode, bool aDifferentNetsOnly ) const; + bool collideSimple( const ITEM* aOther, const NODE* aNode, bool aDifferentNetsOnly ) const; protected: PnsKind m_kind; diff --git a/pcbnew/router/pns_kicad_iface.cpp b/pcbnew/router/pns_kicad_iface.cpp index 7b4378ac78..026a3782a6 100644 --- a/pcbnew/router/pns_kicad_iface.cpp +++ b/pcbnew/router/pns_kicad_iface.cpp @@ -73,10 +73,10 @@ public: PNS_PCBNEW_RULE_RESOLVER( BOARD* aBoard, PNS::ROUTER_IFACE* aRouterIface ); virtual ~PNS_PCBNEW_RULE_RESOLVER(); - virtual bool CollideHoles( const PNS::ITEM* aA, const PNS::ITEM* aB, - bool aNeedMTV, VECTOR2I* aMTV ) const override; - virtual int Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB ) override; + virtual int HoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB ) override; + virtual int HoleToHoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB ) override; + virtual int DpCoupledNet( int aNet ) override; virtual int DpNetPolarity( int aNet ) override; virtual bool DpNetPair( const PNS::ITEM* aItem, int& aNetP, int& aNetN ) override; @@ -97,6 +97,8 @@ private: VIA m_dummyVia; std::map, int> m_clearanceCache; + std::map, int> m_holeClearanceCache; + std::map, int> m_holeToHoleClearanceCache; }; @@ -137,44 +139,6 @@ int PNS_PCBNEW_RULE_RESOLVER::holeRadius( const PNS::ITEM* aItem ) const } -bool PNS_PCBNEW_RULE_RESOLVER::CollideHoles( const PNS::ITEM* aA, const PNS::ITEM* aB, - bool aNeedMTV, VECTOR2I* aMTV ) const -{ - VECTOR2I pos_a = aA->Shape()->Centre(); - VECTOR2I pos_b = aB->Shape()->Centre(); - - // Holes with identical locations are allowable - if( pos_a == pos_b ) - return false; - - int radius_a = holeRadius( aA ); - int radius_b = holeRadius( aB ); - - // Do both objects have holes? - if( radius_a > 0 && radius_b > 0 ) - { - int holeToHoleMin = m_board->GetDesignSettings().m_HoleToHoleMin; - - ecoord min_dist = holeToHoleMin + radius_a + radius_b; - ecoord min_dist_sq = min_dist * min_dist; - - const VECTOR2I delta = pos_b - pos_a; - - ecoord dist_sq = delta.SquaredEuclideanNorm(); - - if( dist_sq == 0 || dist_sq < min_dist_sq ) - { - if( aNeedMTV ) - *aMTV = delta.Resize( min_dist - sqrt( dist_sq ) + 3 ); // fixme: apparent rounding error - - return true; - } - } - - return false; -} - - bool PNS_PCBNEW_RULE_RESOLVER::IsDiffPair( const PNS::ITEM* aA, const PNS::ITEM* aB ) { int net_p, net_n; @@ -191,6 +155,11 @@ bool PNS_PCBNEW_RULE_RESOLVER::IsDiffPair( const PNS::ITEM* aA, const PNS::ITEM* } +bool isEdge( const PNS::ITEM* aItem ) +{ + return aItem->Layer() == Edge_Cuts || aItem->Layer() == Margin; +} + bool PNS_PCBNEW_RULE_RESOLVER::QueryConstraint( PNS::CONSTRAINT_TYPE aType, const PNS::ITEM* aItemA, const PNS::ITEM* aItemB, @@ -205,13 +174,16 @@ bool PNS_PCBNEW_RULE_RESOLVER::QueryConstraint( PNS::CONSTRAINT_TYPE aType, switch ( aType ) { - case PNS::CONSTRAINT_TYPE::CT_CLEARANCE: hostType = CLEARANCE_CONSTRAINT; break; - case PNS::CONSTRAINT_TYPE::CT_WIDTH: hostType = TRACK_WIDTH_CONSTRAINT; break; - case PNS::CONSTRAINT_TYPE::CT_DIFF_PAIR_GAP: hostType = DIFF_PAIR_GAP_CONSTRAINT; break; - case PNS::CONSTRAINT_TYPE::CT_LENGTH: hostType = LENGTH_CONSTRAINT; break; - case PNS::CONSTRAINT_TYPE::CT_VIA_DIAMETER: hostType = VIA_DIAMETER_CONSTRAINT; break; - case PNS::CONSTRAINT_TYPE::CT_VIA_HOLE: hostType = HOLE_SIZE_CONSTRAINT; break; - default: return false; // should not happen + case PNS::CONSTRAINT_TYPE::CT_CLEARANCE: hostType = CLEARANCE_CONSTRAINT; break; + case PNS::CONSTRAINT_TYPE::CT_WIDTH: hostType = TRACK_WIDTH_CONSTRAINT; break; + case PNS::CONSTRAINT_TYPE::CT_DIFF_PAIR_GAP: hostType = DIFF_PAIR_GAP_CONSTRAINT; break; + case PNS::CONSTRAINT_TYPE::CT_LENGTH: hostType = LENGTH_CONSTRAINT; break; + case PNS::CONSTRAINT_TYPE::CT_VIA_DIAMETER: hostType = VIA_DIAMETER_CONSTRAINT; break; + case PNS::CONSTRAINT_TYPE::CT_VIA_HOLE: hostType = HOLE_SIZE_CONSTRAINT; break; + case PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE: hostType = HOLE_CLEARANCE_CONSTRAINT; break; + case PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE: hostType = EDGE_CLEARANCE_CONSTRAINT; break; + case PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE: hostType = HOLE_TO_HOLE_CONSTRAINT; break; + default: return false; // should not happen } BOARD_ITEM* parentA = aItemA ? aItemA->Parent() : nullptr; @@ -271,6 +243,9 @@ bool PNS_PCBNEW_RULE_RESOLVER::QueryConstraint( PNS::CONSTRAINT_TYPE aType, case PNS::CONSTRAINT_TYPE::CT_DIFF_PAIR_GAP: case PNS::CONSTRAINT_TYPE::CT_VIA_DIAMETER: case PNS::CONSTRAINT_TYPE::CT_VIA_HOLE: + case PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE: + case PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE: + case PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE: aConstraint->m_Value = hostConstraint.GetValue(); aConstraint->m_RuleName = hostConstraint.GetName(); aConstraint->m_Type = aType; @@ -291,7 +266,6 @@ int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* a return it->second; PNS::CONSTRAINT constraint; - bool ok = false; int rv = 0; if( aB && IsDiffPair( aA, aB ) ) @@ -301,26 +275,72 @@ int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* a &constraint ) ) { rv = constraint.m_Value.Opt(); - ok = true; + m_clearanceCache[ key ] = rv; + return rv; } } - if( !ok ) + if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, aA->Layer(), + &constraint ) ) { - if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, aA->Layer(), + rv = constraint.m_Value.Min(); + } + + if( isEdge( aA ) || ( aB && isEdge( aB ) ) ) + { + if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE, aA, aB, aA->Layer(), &constraint ) ) { - rv = constraint.m_Value.Min(); - ok = true; + if( constraint.m_Value.Min() > rv ) + rv = constraint.m_Value.Min(); } } - // still no valid clearance rule? fall back to global minimum. - if( !ok ) - rv = m_board->GetDesignSettings().m_MinClearance; - m_clearanceCache[ key ] = rv; + return rv; +} + +int PNS_PCBNEW_RULE_RESOLVER::HoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB ) +{ + std::pair key( aA, aB ); + auto it = m_holeClearanceCache.find( key ); + + if( it != m_holeClearanceCache.end() ) + return it->second; + + PNS::CONSTRAINT constraint; + int rv = 0; + + if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE, aA, aB, aA->Layer(), + &constraint ) ) + { + rv = constraint.m_Value.Min(); + } + + m_holeClearanceCache[ key ] = rv; + return rv; +} + + +int PNS_PCBNEW_RULE_RESOLVER::HoleToHoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB ) +{ + std::pair key( aA, aB ); + auto it = m_holeToHoleClearanceCache.find( key ); + + if( it != m_holeToHoleClearanceCache.end() ) + return it->second; + + PNS::CONSTRAINT constraint; + int rv = 0; + + if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE, aA, aB, aA->Layer(), + &constraint ) ) + { + rv = constraint.m_Value.Min(); + } + + m_holeToHoleClearanceCache[ key ] = rv; return rv; } @@ -821,7 +841,7 @@ std::unique_ptr PNS_KICAD_IFACE_BASE::syncPad( PAD* aPad ) slot->SetWidth( slot->GetWidth() + bds.GetHolePlatingThickness() * 2 ); } - solid->SetAlternateShape( slot ); + solid->SetHole( slot ); } if( aPad->GetAttribute() == PAD_ATTRIB_NPTH ) @@ -923,6 +943,10 @@ std::unique_ptr PNS_KICAD_IFACE_BASE::syncVia( VIA* aVia ) via->SetIsFree( aVia->GetIsFree() ); + BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); + via->SetHole( SHAPE_CIRCLE( aVia->GetPosition(), + aVia->GetDrill() / 2 + bds.GetHolePlatingThickness() ) ); + return via; } @@ -1107,7 +1131,7 @@ bool PNS_KICAD_IFACE::IsAnyLayerVisible( const LAYER_RANGE& aLayer ) const } -bool PNS_KICAD_IFACE::IsOnLayer( const PNS::ITEM* aItem, int aLayer ) const +bool PNS_KICAD_IFACE::IsFlashedOnLayer( const PNS::ITEM* aItem, int aLayer ) const { /// Default is all layers if( aLayer < 0 ) @@ -1170,7 +1194,7 @@ bool PNS_KICAD_IFACE::IsItemVisible( const PNS::ITEM* aItem ) const void PNS_KICAD_IFACE_BASE::SyncWorld( PNS::NODE *aWorld ) { - int worstPadClearance = 0; + int worstClearance = m_board->GetDesignSettings().GetBiggestClearanceValue(); m_world = aWorld; @@ -1210,7 +1234,7 @@ void PNS_KICAD_IFACE_BASE::SyncWorld( PNS::NODE *aWorld ) if( std::unique_ptr solid = syncPad( pad ) ) aWorld->Add( std::move( solid ) ); - worstPadClearance = std::max( worstPadClearance, pad->GetLocalClearance() ); + worstClearance = std::max( worstClearance, pad->GetLocalClearance() ); } syncTextItem( aWorld, &footprint->Reference(), footprint->Reference().GetLayer() ); @@ -1256,15 +1280,13 @@ void PNS_KICAD_IFACE_BASE::SyncWorld( PNS::NODE *aWorld ) } } - int worstRuleClearance = m_board->GetDesignSettings().GetBiggestClearanceValue(); - // NB: if this were ever to become a long-lived object we would need to dirty its // clearance cache here.... delete m_ruleResolver; m_ruleResolver = new PNS_PCBNEW_RULE_RESOLVER( m_board, this ); aWorld->SetRuleResolver( m_ruleResolver ); - aWorld->SetMaxClearance( 4 * std::max(worstPadClearance, worstRuleClearance ) ); + aWorld->SetMaxClearance( 4 * worstClearance ); } diff --git a/pcbnew/router/pns_kicad_iface.h b/pcbnew/router/pns_kicad_iface.h index c03c715b7a..88433abdd7 100644 --- a/pcbnew/router/pns_kicad_iface.h +++ b/pcbnew/router/pns_kicad_iface.h @@ -56,7 +56,7 @@ public: void SetBoard( BOARD* aBoard ); void SyncWorld( PNS::NODE* aWorld ) override; bool IsAnyLayerVisible( const LAYER_RANGE& aLayer ) const override { return true; }; - bool IsOnLayer( const PNS::ITEM* aItem, int aLayer ) const override { return true; }; + bool IsFlashedOnLayer( const PNS::ITEM* aItem, int aLayer ) const override { return true; }; bool IsItemVisible( const PNS::ITEM* aItem ) const override { return true; } void HideItem( PNS::ITEM* aItem ) override {} void DisplayItem( const PNS::ITEM* aItem, int aColor = 0, int aClearance = 0, @@ -115,7 +115,7 @@ public: void EraseView() override; bool IsAnyLayerVisible( const LAYER_RANGE& aLayer ) const override; bool IsItemVisible( const PNS::ITEM* aItem ) const override; - bool IsOnLayer( const PNS::ITEM* aItem, int aLayer ) const override; + bool IsFlashedOnLayer( const PNS::ITEM* aItem, int aLayer ) const override; void HideItem( PNS::ITEM* aItem ) override; void DisplayItem( const PNS::ITEM* aItem, int aColor = 0, int aClearance = 0, bool aEdit = false ) override; diff --git a/pcbnew/router/pns_line_placer.cpp b/pcbnew/router/pns_line_placer.cpp index 0fd0d12ced..e1790d5693 100644 --- a/pcbnew/router/pns_line_placer.cpp +++ b/pcbnew/router/pns_line_placer.cpp @@ -478,55 +478,17 @@ bool LINE_PLACER::rhWalkOnly( const VECTOR2I& aP, LINE& aNewHead ) bool LINE_PLACER::rhMarkObstacles( const VECTOR2I& aP, LINE& aNewHead ) { - LINE newHead( m_head ); - LINE bestHead( m_head ); - bool hasBest = false; + buildInitialLine( aP, m_head ); - buildInitialLine( aP, newHead ); - - NODE::OBSTACLES obstacles; - - m_currentNode->QueryColliding( &newHead, obstacles ); - - // If we are allowing DRC violations, we don't push back to the hull + // If we are enforcing DRC violations, push back to the hull if( !Settings().CanViolateDRC() ) { - int layer = m_head.Layer(); - int lineWidth = m_placingVia ? m_head.Via().Diameter() : m_head.Width(); - int clearance; + NODE::OPT_OBSTACLE obs = m_currentNode->NearestObstacle( &m_head ); - for( OBSTACLE& obs : obstacles ) - { - if( m_placingVia ) - clearance = m_currentNode->GetClearance( obs.m_item, &m_head.Via() ); - else - clearance = m_currentNode->GetClearance( obs.m_item, &m_head ); - - const SHAPE_LINE_CHAIN hull = obs.m_item->Hull( clearance, lineWidth, layer ); - VECTOR2I nearest = hull.NearestPoint( aP ); - - Dbg()->AddLine( hull, 2, 10000 ); - - if( ( nearest - aP ).EuclideanNorm() < lineWidth + clearance ) - { - buildInitialLine( nearest, newHead ); - - // We want the shortest line here to ensure we don't break a clearance - // rule on larger, overlapping items (e.g. vias) - if( !hasBest || newHead.CLine().Length() < bestHead.CLine().Length() ) - { - bestHead = newHead; - hasBest = true; - } - } - } + if( obs && obs->m_distFirst != INT_MAX ) + buildInitialLine( obs->m_ipFirst, m_head ); } - if( hasBest ) - m_head = bestHead; - else - m_head = newHead; - aNewHead = m_head; return static_cast( m_currentNode->CheckColliding( &m_head ) ); diff --git a/pcbnew/router/pns_node.cpp b/pcbnew/router/pns_node.cpp index c86e6e66c9..7674a78c1c 100644 --- a/pcbnew/router/pns_node.cpp +++ b/pcbnew/router/pns_node.cpp @@ -36,6 +36,8 @@ #include "pns_solid.h" #include "pns_joint.h" #include "pns_index.h" +#include "pns_debug_decorator.h" +#include "pns_router.h" namespace PNS { @@ -103,6 +105,24 @@ int NODE::GetClearance( const ITEM* aA, const ITEM* aB ) const } +int NODE::GetHoleClearance( const ITEM* aA, const ITEM* aB ) const + { + if( !m_ruleResolver ) + return 0; + + return m_ruleResolver->HoleClearance( aA, aB ); + } + + +int NODE::GetHoleToHoleClearance( const ITEM* aA, const ITEM* aB ) const +{ + if( !m_ruleResolver ) + return 0; + + return m_ruleResolver->HoleToHoleClearance( aA, aB ); +} + + NODE* NODE::Branch() { NODE* child = new NODE; @@ -194,8 +214,6 @@ struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR bool m_differentNetsOnly; - int m_forceClearance; - DEFAULT_OBSTACLE_VISITOR( NODE::OBSTACLES& aTab, const ITEM* aItem, int aKindMask, bool aDifferentNetsOnly ) : OBSTACLE_VISITOR( aItem ), m_tab( aTab ), @@ -203,8 +221,7 @@ struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR m_limitCount( -1 ), m_matchCount( 0 ), m_extraClearance( 0 ), - m_differentNetsOnly( aDifferentNetsOnly ), - m_forceClearance( -1 ) + m_differentNetsOnly( aDifferentNetsOnly ) { if( aItem && aItem->Kind() == ITEM::LINE_T ) { @@ -229,18 +246,7 @@ struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR if( visit( aCandidate ) ) return true; - int clearance = m_extraClearance + m_node->GetClearance( aCandidate, m_item ); - - if( aCandidate->Kind() == ITEM::LINE_T ) // this should never happen. - { - assert( false ); - clearance += static_cast( aCandidate )->Width() / 2; - } - - if( m_forceClearance >= 0 ) - clearance = m_forceClearance; - - if( !aCandidate->Collide( m_item, clearance, false, nullptr, m_node, m_differentNetsOnly ) ) + if( !aCandidate->Collide( m_item, m_node, m_differentNetsOnly ) ) return true; OBSTACLE obs; @@ -259,24 +265,8 @@ struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR }; -int NODE::QueryColliding( const ITEM* aItem, OBSTACLE_VISITOR& aVisitor ) -{ - aVisitor.SetWorld( this, NULL ); - m_index->Query( aItem, m_maxClearance, aVisitor ); - - // if we haven't found enough items, look in the root branch as well. - if( !isRoot() ) - { - aVisitor.SetWorld( m_root, this ); - m_root->m_index->Query( aItem, m_maxClearance, aVisitor ); - } - - return 0; -} - - int NODE::QueryColliding( const ITEM* aItem, NODE::OBSTACLES& aObstacles, int aKindMask, - int aLimitCount, bool aDifferentNetsOnly, int aForceClearance ) + int aLimitCount, bool aDifferentNetsOnly ) { DEFAULT_OBSTACLE_VISITOR visitor( aObstacles, aItem, aKindMask, aDifferentNetsOnly ); @@ -286,7 +276,7 @@ int NODE::QueryColliding( const ITEM* aItem, NODE::OBSTACLES& aObstacles, int aK 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 ); @@ -304,105 +294,131 @@ int NODE::QueryColliding( const ITEM* aItem, NODE::OBSTACLES& aObstacles, int aK NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, int aKindMask, const std::set* aRestrictedSet ) { - OBSTACLES obs_list; - bool found_isects = false; + OBSTACLES obstacleList; + obstacleList.reserve( 100 ); - const SHAPE_LINE_CHAIN& line = aLine->CLine(); - - obs_list.reserve( 100 ); - - int n = 0; - - for( int i = 0; i < line.SegmentCount(); i++ ) + for( int i = 0; i < aLine->CLine().SegmentCount(); i++ ) { - const SEGMENT s( *aLine, line.CSegment( i ) ); - n += QueryColliding( &s, obs_list, aKindMask ); + const SEGMENT s( *aLine, aLine->CLine().CSegment( i ) ); + QueryColliding( &s, obstacleList, aKindMask ); } if( aLine->EndsWithVia() ) - n += QueryColliding( &aLine->Via(), obs_list, aKindMask ); + QueryColliding( &aLine->Via(), obstacleList, aKindMask ); - if( !n ) + if( obstacleList.empty() ) return OPT_OBSTACLE(); OBSTACLE nearest; nearest.m_item = NULL; nearest.m_distFirst = INT_MAX; - for( const OBSTACLE& obs : obs_list ) - { - VECTOR2I ip_last; - int dist_max = INT_MIN; + auto updateNearest = + [&]( int dist, VECTOR2I pt, ITEM* item, const SHAPE_LINE_CHAIN& hull ) + { + if( dist < nearest.m_distFirst ) + { + nearest.m_distFirst = dist; + nearest.m_ipFirst = pt; + nearest.m_item = item; + nearest.m_hull = hull; + } + }; - if( aRestrictedSet && aRestrictedSet->find( obs.m_item ) == aRestrictedSet->end() ) + for( const OBSTACLE& obstacle : obstacleList ) + { + if( aRestrictedSet && aRestrictedSet->find( obstacle.m_item ) == aRestrictedSet->end() ) continue; - std::vector isect_list; + std::vector intersectingPts; - int clearance = GetClearance( obs.m_item, aLine ); + int clearance = GetClearance( obstacle.m_item, aLine ) + aLine->Width() / 2; + SHAPE_LINE_CHAIN obstacleHull = obstacle.m_item->Hull( clearance, 0, aLine->Layer() ); - SHAPE_LINE_CHAIN obsHull = obs.m_item->Hull( clearance, aLine->Width(), aLine->Layer() ); + ROUTER::GetInstance()->GetInterface()->GetDebugDecorator()->AddLine( obstacleHull, 2, 1000 ); if( aLine->EndsWithVia() ) { - clearance = GetClearance( obs.m_item, &aLine->Via() ); + const VIA& via = aLine->Via(); + int viaClearance = GetClearance( obstacle.m_item, &via ); + int holeClearance = GetHoleClearance( obstacle.m_item, &via ); - SHAPE_LINE_CHAIN viaHull = aLine->Via().Hull( clearance, aLine->Width() ); + if( holeClearance + via.Drill() / 2 > viaClearance + via.Diameter() / 2 ) + viaClearance = holeClearance + via.Drill() / 2 - via.Diameter() / 2; - viaHull.Intersect( obsHull, isect_list ); + // ObstacleHull has line clearance and 1/2 line width already built in. So + // viaHull's clearance needs to be only that portion of the via clearance that + // is *in excess* of the line clearance. + viaClearance = std::max( 0, viaClearance - clearance ); - for( const SHAPE_LINE_CHAIN::INTERSECTION& isect : isect_list ) + SHAPE_LINE_CHAIN viaHull = via.Hull( viaClearance, 0 ); + + obstacleHull.Intersect( viaHull, intersectingPts ); + + for( const SHAPE_LINE_CHAIN::INTERSECTION& ip : intersectingPts ) { - 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 = obsHull; - } - - if( dist > dist_max ) - { - dist_max = dist; - ip_last = isect.p; - } + int dist = aLine->CLine().Length() + ( ip.p - via.Pos() ).EuclideanNorm(); + updateNearest( dist, ip.p, obstacle.m_item, obstacleHull ); } } - isect_list.clear(); + intersectingPts.clear(); + obstacleHull.Intersect( aLine->CLine(), intersectingPts ); - obsHull.Intersect( aLine->CLine(), isect_list ); - - for( const SHAPE_LINE_CHAIN::INTERSECTION& isect : isect_list ) + for( const SHAPE_LINE_CHAIN::INTERSECTION& ip : intersectingPts ) { - 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 = obsHull; - } - - if( dist > dist_max ) - { - dist_max = dist; - ip_last = isect.p; - } + int dist = aLine->CLine().PathLength( ip.p ); + updateNearest( dist, ip.p, obstacle.m_item, obstacleHull ); } - nearest.m_ipLast = ip_last; - nearest.m_distLast = dist_max; + if( obstacle.m_item->Hole() ) + { + clearance = GetHoleClearance( obstacle.m_item, aLine ) + aLine->Width() / 2; + obstacleHull = obstacle.m_item->HoleHull( clearance, 0, aLine->Layer() ); + + ROUTER::GetInstance()->GetInterface()->GetDebugDecorator()->AddLine( obstacleHull, 3, 1000 ); + + if( aLine->EndsWithVia() ) + { + const VIA& via = aLine->Via(); + int viaClearance = GetClearance( obstacle.m_item, &via ); + int holeClearance = GetHoleClearance( obstacle.m_item, &via ); + int holeToHole = GetHoleToHoleClearance( obstacle.m_item, &via ); + + if( holeClearance + via.Drill() / 2 > viaClearance + via.Diameter() / 2 ) + viaClearance = holeClearance + via.Drill() / 2 - via.Diameter() / 2; + + if( holeToHole + via.Drill() / 2 > viaClearance + via.Diameter() / 2 ) + viaClearance = holeToHole + via.Drill() / 2 - via.Diameter() / 2; + + // ObsHull has line clearance and 1/2 line width already built in. So viaHull's + // clearance needs to be just that which is *in excess* of clearance. + viaClearance = std::max( 0, viaClearance - clearance ); + + SHAPE_LINE_CHAIN viaHull = via.Hull( viaClearance, 0 ); + + obstacleHull.Intersect( viaHull, intersectingPts ); + + for( const SHAPE_LINE_CHAIN::INTERSECTION& ip : intersectingPts ) + { + int dist = aLine->CLine().Length() + ( ip.p - via.Pos() ).EuclideanNorm(); + updateNearest( dist, ip.p, obstacle.m_item, obstacleHull ); + } + } + + intersectingPts.clear(); + obstacleHull.Intersect( aLine->CLine(), intersectingPts ); + + for( const SHAPE_LINE_CHAIN::INTERSECTION& ip : intersectingPts ) + { + int dist = aLine->CLine().PathLength( ip.p ); + updateNearest( dist, ip.p, obstacle.m_item, obstacleHull ); + } + } } - if( !found_isects ) - nearest.m_item = obs_list[0].m_item; + if( nearest.m_distFirst == INT_MAX ) + nearest.m_item = obstacleList[0].m_item; return nearest; } @@ -458,25 +474,6 @@ NODE::OPT_OBSTACLE NODE::CheckColliding( const ITEM* aItemA, int aKindMask ) } -bool NODE::CheckColliding( const ITEM* aItemA, const 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() == ITEM::LINE_T ) - clearance += static_cast( aItemA )->Width() / 2; - if( aItemB->Kind() == ITEM::LINE_T ) - clearance += static_cast( aItemB )->Width() / 2; - - return aItemA->Collide( aItemB, clearance, false, nullptr, this ); -} - - struct HIT_VISITOR : public OBSTACLE_VISITOR { ITEM_SET& m_items; diff --git a/pcbnew/router/pns_node.h b/pcbnew/router/pns_node.h index d827d4a049..2a97a278a5 100644 --- a/pcbnew/router/pns_node.h +++ b/pcbnew/router/pns_node.h @@ -64,7 +64,8 @@ enum class CONSTRAINT_TYPE CT_VIA_DIAMETER = 5, CT_VIA_HOLE = 6, CT_HOLE_CLEARANCE = 7, - CT_EDGE_CLEARANCE = 8 + CT_EDGE_CLEARANCE = 8, + CT_HOLE_TO_HOLE = 9 }; struct CONSTRAINT @@ -82,10 +83,10 @@ class RULE_RESOLVER public: virtual ~RULE_RESOLVER() {} - virtual bool CollideHoles( const ITEM* aA, const ITEM* aB, - bool aNeedMTV, VECTOR2I* aMTV ) const = 0; - virtual int Clearance( const ITEM* aA, const ITEM* aB ) = 0; + virtual int HoleClearance( const ITEM* aA, const ITEM* aB ) = 0; + virtual int HoleToHoleClearance( const ITEM* aA, const ITEM* aB ) = 0; + virtual int DpCoupledNet( int aNet ) = 0; virtual int DpNetPolarity( int aNet ) = 0; virtual bool DpNetPair( const ITEM* aItem, int& aNetP, int& aNetN ) = 0; @@ -114,12 +115,11 @@ struct OBSTACLE ///> 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; + ///> First intersection point between the head item and the hull of the colliding m_item + VECTOR2I m_ipFirst; ///> ... and the distance thereof - int m_distFirst, m_distLast; + int m_distFirst; }; /** @@ -180,6 +180,8 @@ public: ///> Returns the expected clearance between items a and b. int GetClearance( const ITEM* aA, const ITEM* aB ) const; + int GetHoleClearance( const ITEM* aA, const ITEM* aB ) const; + int GetHoleToHoleClearance( const ITEM* aA, const ITEM* aB ) const; ///> Returns the pre-set worst case clearance between any pair of items int GetMaxClearance() const @@ -230,16 +232,13 @@ public: OBSTACLES& aObstacles, int aKindMask = ITEM::ANY_T, int aLimitCount = -1, - bool aDifferentNetsOnly = true, - int aForceClearance = -1 ); + bool aDifferentNetsOnly = true ); int QueryJoints( const BOX2I& aBox, std::vector& aJoints, LAYER_RANGE aLayerMask = LAYER_RANGE::All(), int aKindMask = ITEM::ANY_T ); - int QueryColliding( const ITEM* aItem, OBSTACLE_VISITOR& aVisitor ); - /** * Function NearestObstacle() * @@ -278,22 +277,6 @@ public: OPT_OBSTACLE CheckColliding( const ITEM_SET& aSet, int aKindMask = ITEM::ANY_T ); - - /** - * 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 ITEM* aItemA, - const ITEM* aItemB, - int aKindMask = ITEM::ANY_T, - int aForceClearance = -1 ); - /** * Function HitTest() * @@ -439,7 +422,7 @@ public: void AllItemsInNet( int aNet, std::set& aItems, int aKindMask = -1 ); - void ClearRanks( int aMarkerMask = MK_HEAD | MK_VIOLATION | MK_ALT_SHAPE ); + void ClearRanks( int aMarkerMask = MK_HEAD | MK_VIOLATION | MK_HOLE ); void RemoveByMarker( int aMarker ); diff --git a/pcbnew/router/pns_optimizer.cpp b/pcbnew/router/pns_optimizer.cpp index c38ec67ec9..563235707d 100644 --- a/pcbnew/router/pns_optimizer.cpp +++ b/pcbnew/router/pns_optimizer.cpp @@ -158,9 +158,7 @@ struct OPTIMIZER::CACHE_VISITOR if( !( m_mask & aOtherItem->Kind() ) ) return true; - int clearance = m_node->GetClearance( aOtherItem, m_ourItem ); - - if( !aOtherItem->Collide( m_ourItem, clearance, false, nullptr, m_node ) ) + if( !aOtherItem->Collide( m_ourItem, m_node ) ) return true; m_collidingItem = aOtherItem; @@ -1156,7 +1154,7 @@ bool verifyDpBypass( NODE* aNode, DIFF_PAIR* aPair, bool aRefIsP, const SHAPE_LI LINE refLine ( aRefIsP ? aPair->PLine() : aPair->NLine(), aNewRef ); LINE coupledLine ( aRefIsP ? aPair->NLine() : aPair->PLine(), aNewCoupled ); - if( aNode->CheckColliding( &refLine, &coupledLine, ITEM::ANY_T, aPair->Gap() - 10 ) ) + if( refLine.Collide( &coupledLine, aNode ) ) return false; if( aNode->CheckColliding ( &refLine ) ) diff --git a/pcbnew/router/pns_router.cpp b/pcbnew/router/pns_router.cpp index ec6b16d206..0baafc6dba 100644 --- a/pcbnew/router/pns_router.cpp +++ b/pcbnew/router/pns_router.cpp @@ -308,7 +308,13 @@ void ROUTER::markViolations( NODE* aNode, ITEM_SET& aCurrent, NODE::ITEM_VECTOR& for( OBSTACLE& obs : obstacles ) { - int clearance = aNode->GetClearance( item, obs.m_item ); + int clearance; + + if( ( obs.m_item->Marker() & MK_HOLE ) > 0 ) + clearance = aNode->GetHoleClearance( item, obs.m_item ); + else + clearance = aNode->GetClearance( item, obs.m_item ); + std::unique_ptr tmp( obs.m_item->Clone() ); tmp->Mark( tmp->Marker() | MK_VIOLATION ); m_iface->DisplayItem( tmp.get(), -1, clearance ); @@ -370,7 +376,16 @@ void ROUTER::movePlacing( const VECTOR2I& aP, ITEM* aEndItem ) m_iface->DisplayItem( l, -1, clearance ); if( l->EndsWithVia() ) - m_iface->DisplayItem( &l->Via(), -1, clearance ); + { + const VIA& via = l->Via(); + int viaClearance = GetRuleResolver()->Clearance( &via, nullptr ); + int holeClearance = GetRuleResolver()->HoleClearance( &via, nullptr ); + + if( holeClearance + via.Drill() / 2 > viaClearance + via.Diameter() / 2 ) + viaClearance = holeClearance + via.Drill() / 2 - via.Diameter() / 2; + + m_iface->DisplayItem( &l->Via(), -1, viaClearance ); + } } //ITEM_SET tmp( ¤t ); diff --git a/pcbnew/router/pns_router.h b/pcbnew/router/pns_router.h index c058527818..5331333818 100644 --- a/pcbnew/router/pns_router.h +++ b/pcbnew/router/pns_router.h @@ -100,7 +100,7 @@ enum DRAG_MODE virtual void RemoveItem( ITEM* aItem ) = 0; virtual bool IsAnyLayerVisible( const LAYER_RANGE& aLayer ) const = 0; virtual bool IsItemVisible( const PNS::ITEM* aItem ) const = 0; - virtual bool IsOnLayer( const PNS::ITEM* aItem, int aLayer ) const = 0; + virtual bool IsFlashedOnLayer( const PNS::ITEM* aItem, int aLayer ) const = 0; virtual void DisplayItem( const ITEM* aItem, int aColor = -1, int aClearance = -1, bool aEdit = false ) = 0; virtual void DisplayRatline( const SHAPE_LINE_CHAIN& aRatline, int aColor = -1 ) = 0; diff --git a/pcbnew/router/pns_shove.cpp b/pcbnew/router/pns_shove.cpp index 7274449fea..fae7a416bb 100644 --- a/pcbnew/router/pns_shove.cpp +++ b/pcbnew/router/pns_shove.cpp @@ -23,6 +23,8 @@ #include #include +#include + #include "pns_arc.h" #include "pns_line.h" #include "pns_node.h" @@ -72,6 +74,14 @@ int SHOVE::getClearance( const ITEM* aA, const ITEM* aB ) const return m_currentNode->GetClearance( aA, aB ); } +int SHOVE::getHoleClearance( const ITEM* aA, const ITEM* aB ) const +{ + if( m_forceClearance >= 0 ) + return m_forceClearance; + + return m_currentNode->GetHoleClearance( aA, aB ); +} + void SHOVE::sanityCheck( LINE* aOld, LINE* aNew ) { @@ -129,6 +139,11 @@ bool SHOVE::checkBumpDirection( const LINE& aCurrent, const LINE& aObstacle, con SHOVE::SHOVE_STATUS SHOVE::walkaroundLoneVia( LINE& aCurrent, LINE& aObstacle, LINE& aShoved ) { int clearance = getClearance( &aCurrent, &aObstacle ); + int holeClearance = getHoleClearance( &aCurrent.Via(), &aObstacle ); + + if( holeClearance + aCurrent.Via().Drill() / 2 > clearance + aCurrent.Via().Diameter() / 2 ) + clearance = holeClearance + aCurrent.Via().Drill() / 2 - aCurrent.Via().Diameter() / 2; + const SHAPE_LINE_CHAIN hull = aCurrent.Via().Hull( clearance, aObstacle.Width(), aCurrent.Layer() ); SHAPE_LINE_CHAIN path_cw; SHAPE_LINE_CHAIN path_ccw; @@ -152,7 +167,7 @@ SHOVE::SHOVE_STATUS SHOVE::walkaroundLoneVia( LINE& aCurrent, LINE& aObstacle, L aShoved.SetShape( shortest ); - if( m_currentNode->CheckColliding( &aShoved, &aCurrent ) ) + if( aShoved.Collide( &aCurrent, m_currentNode ) ) return SH_INCOMPLETE; return SH_OK; @@ -234,7 +249,7 @@ SHOVE::SHOVE_STATUS SHOVE::processHullSet( LINE& aCurrent, LINE& aObstacle, continue; } - bool colliding = m_currentNode->CheckColliding( &l, &aCurrent, ITEM::ANY_T, m_forceClearance ); + bool colliding = l.Collide( &aCurrent, m_currentNode ); #ifdef DEBUG char str[128]; @@ -248,7 +263,7 @@ SHOVE::SHOVE_STATUS SHOVE::processHullSet( LINE& aCurrent, LINE& aObstacle, for( ITEM* item : jtStart->LinkList() ) { - if( m_currentNode->CheckColliding( item, &l ) ) + if( item->Collide( &l, m_currentNode ) ) colliding = true; } } @@ -321,7 +336,16 @@ SHOVE::SHOVE_STATUS SHOVE::ProcessSingleLine( LINE& aCurrent, LINE& aObstacle, L } if( viaOnEnd ) - hulls.push_back( aCurrent.Via().Hull( clearance, w ) ); + { + const VIA& via = aCurrent.Via(); + int viaClearance = getClearance( &via, &aObstacle ); + int holeClearance = getHoleClearance( &via, &aObstacle ); + + if( holeClearance + via.Drill() / 2 > viaClearance + via.Diameter() / 2 ) + viaClearance = holeClearance + via.Drill() / 2 - via.Diameter() / 2; + + hulls.push_back( aCurrent.Via().Hull( viaClearance, w ) ); + } #ifdef DEBUG char str[128]; @@ -530,7 +554,7 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingSolid( LINE& aCurrent, ITEM* aObstacle ) } } - if( via && m_currentNode->CheckColliding( via, aObstacle ) ) + if( via && via->Collide( aObstacle, m_currentNode ) ) return onCollidingVia( aObstacle, via ); } @@ -598,7 +622,7 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingSolid( LINE& aCurrent, ITEM* aObstacle ) { LINE lastLine = m_lineStack.front(); - if( m_currentNode->CheckColliding( &lastLine, &walkaroundLine ) ) + if( lastLine.Collide( &walkaroundLine, m_currentNode ) ) { LINE dummy( lastLine ); @@ -834,73 +858,51 @@ SHOVE::SHOVE_STATUS SHOVE::pushOrShoveVia( VIA* aVia, const VECTOR2I& aForce, in */ SHOVE::SHOVE_STATUS SHOVE::onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia ) { - int clearance = getClearance( aCurrent, aObstacleVia ) + PNS_HULL_MARGIN; + int clearance = getClearance( aCurrent, aObstacleVia ); LINE_PAIR_VEC draggedLines; - bool lineCollision = false; - bool viaCollision = false; - bool holeCollision = false; - LINE* currentLine = NULL; - VECTOR2I mtvLine; // Minimum translation vector to correct line collisions - VECTOR2I mtvVia; // MTV to correct via collisions - VECTOR2I mtvHoles; // MTV to correct hole collisions - VECTOR2I mtvSolid; // MTV to correct solid collisions - VECTOR2I mtv; // Union of relevant MTVs (will correct all collisions) + VECTOR2I mtv; int rank = -1; + SHAPE_COMPOUND shape; + if( aCurrent->OfKind( ITEM::LINE_T ) ) { + LINE* currentLine = (LINE*) aCurrent; + #if 0 - m_logger.NewGroup( "push-via-by-line", m_iter ); - m_logger.Log( aCurrent, 4, "current" ); + m_logger.NewGroup( "push-via-by-line", m_iter ); + m_logger.Log( currentLine, 4, "current" ); #endif - currentLine = (LINE*) aCurrent; - lineCollision = aObstacleVia->Shape()->Collide( currentLine->Shape(), - clearance + currentLine->Width() / 2, - &mtvLine ); + shape.AddShape( currentLine->Shape()->Clone() ); + // SHAPE_LINE_CHAIN collisions don't currently pay any attention to the line-chain's + // width, so we have to add it to the clearance. + clearance += currentLine->Width() / 2; + + // Add a second shape for the via (if any) if( currentLine->EndsWithVia() ) { - int currentNet = currentLine->Net(); - int obstacleNet = aObstacleVia->Net(); + const VIA& currentVia = currentLine->Via(); + int viaClearance = getClearance( ¤tVia, aObstacleVia ); + int holeClearance = getHoleClearance( ¤tVia, aObstacleVia ); + int effectiveRadius = std::max( currentVia.Diameter() / 2 + viaClearance, + currentVia.Drill() / 2 + holeClearance ); - if( currentNet != obstacleNet && currentNet >= 0 && obstacleNet >= 0 ) - { - viaCollision = currentLine->Via().Shape()->Collide( aObstacleVia->Shape(), - clearance, &mtvVia ); - } - - // hole-to-hole is a mechanical constraint (broken drill bits), not an electrical - // one, so it has to be checked irrespective of matching nets. - - // temporarily removed hole-to-hole collision check due to conflicts with the - // springback algorithm... - // we need to figure out a better solution here - TW - holeCollision = false; //rr->CollideHoles( ¤tLine->Via(), aObstacleVia, true, &mtvHoles ); + // Since we have to run the collision with the line's clearance + 1/2 its width (as + // it's the only way to take the SHAPE_LINE_CHAIN's width into account), we need to + // subtract that clearance back out of the via's effective radius. + shape.AddShape( new SHAPE_CIRCLE( currentVia.Pos(), effectiveRadius - clearance ) ); } - - // These aren't /actually/ lengths as we don't bother to do the square-root part, - // but we're just comparing them to each other so it's faster this way. - ecoord lineMTVLength = lineCollision ? mtvLine.SquaredEuclideanNorm() : 0; - ecoord viaMTVLength = viaCollision ? mtvVia.SquaredEuclideanNorm() : 0; - ecoord holeMTVLength = holeCollision ? mtvHoles.SquaredEuclideanNorm() : 0; - - if( lineMTVLength >= viaMTVLength && lineMTVLength >= holeMTVLength ) - mtv = mtvLine; - else if( viaMTVLength >= lineMTVLength && viaMTVLength >= holeMTVLength ) - mtv = mtvVia; - else - mtv = mtvHoles; - - rank = currentLine->Rank(); } else if( aCurrent->OfKind( ITEM::SOLID_T ) ) { - aObstacleVia->Shape()->Collide( aCurrent->Shape(), clearance, &mtvSolid ); - mtv = -mtvSolid; - rank = aCurrent->Rank() + 10000; + shape.AddShape( aCurrent->Shape()->Clone() ); } + aObstacleVia->Shape()->Collide( &shape, clearance + PNS_HULL_MARGIN, &mtv ); + rank = aCurrent->Rank() + 10000; + return pushOrShoveVia( aObstacleVia, mtv, rank ); } @@ -1111,8 +1113,7 @@ SHOVE::SHOVE_STATUS SHOVE::shoveIteration( int aIter ) { wxLogTrace( "PNS", "iter %d: reverse-collide-via", aIter ); - if( currentLine.EndsWithVia() - && m_currentNode->CheckColliding( ¤tLine.Via(), (VIA*) ni ) ) + if( currentLine.EndsWithVia() ) { st = SH_INCOMPLETE; } diff --git a/pcbnew/router/pns_shove.h b/pcbnew/router/pns_shove.h index 5ffe7fed55..c8ba710b87 100644 --- a/pcbnew/router/pns_shove.h +++ b/pcbnew/router/pns_shove.h @@ -153,6 +153,7 @@ private: SHOVE_STATUS shoveMainLoop(); int getClearance( const ITEM* aA, const ITEM* aB ) const; + int getHoleClearance( const ITEM* aA, const ITEM* aB ) const; std::vector m_nodeStack; std::vector m_lineStack; diff --git a/pcbnew/router/pns_solid.cpp b/pcbnew/router/pns_solid.cpp index ce7ef4cb78..1df8ba7bce 100644 --- a/pcbnew/router/pns_solid.cpp +++ b/pcbnew/router/pns_solid.cpp @@ -79,21 +79,16 @@ static const SHAPE_LINE_CHAIN buildHullForPrimitiveShape( const SHAPE* aShape, i const SHAPE_LINE_CHAIN SOLID::Hull( int aClearance, int aWalkaroundThickness, int aLayer ) const { - SHAPE* shape = m_shape; + if( !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( this, aLayer ) ) + return HoleHull( aClearance, aWalkaroundThickness, aLayer ); - if( !ROUTER::GetInstance()->GetInterface()->IsOnLayer( this, aLayer ) ) + if( !m_shape ) + return SHAPE_LINE_CHAIN(); + + if( m_shape->Type() == SH_COMPOUND ) { - /// The alternate shape is defined for THT pads. If we don't have an alternate shape - /// then the solid shape does not exist on this layer - if( !m_alternateShape ) - return SHAPE_LINE_CHAIN(); + SHAPE_COMPOUND* cmpnd = static_cast( m_shape ); - shape = m_alternateShape; - } - - if( shape->Type() == SH_COMPOUND ) - { - auto cmpnd = static_cast( shape ); if ( cmpnd->Shapes().size() == 1 ) { return buildHullForPrimitiveShape( cmpnd->Shapes()[0], aClearance, aWalkaroundThickness ); @@ -107,10 +102,35 @@ const SHAPE_LINE_CHAIN SOLID::Hull( int aClearance, int aWalkaroundThickness, in } else { - return buildHullForPrimitiveShape( shape, aClearance, aWalkaroundThickness ); + return buildHullForPrimitiveShape( m_shape, aClearance, aWalkaroundThickness ); + } +} + + +const SHAPE_LINE_CHAIN SOLID::HoleHull( int aClearance, int aWalkaroundThickness, int aLayer ) const +{ + if( !m_hole ) + return SHAPE_LINE_CHAIN(); + + if( m_hole->Type() == SH_COMPOUND ) + { + SHAPE_COMPOUND* cmpnd = static_cast( m_hole ); + + if ( cmpnd->Shapes().size() == 1 ) + { + return buildHullForPrimitiveShape( cmpnd->Shapes()[0], aClearance, aWalkaroundThickness ); + } + else + { + // fixme - shouldn't happen but one day we should move TransformShapeWithClearanceToPolygon() + // to the Geometry Library + return SHAPE_LINE_CHAIN(); + } + } + else + { + return buildHullForPrimitiveShape( m_hole, aClearance, aWalkaroundThickness ); } - - return SHAPE_LINE_CHAIN(); } diff --git a/pcbnew/router/pns_solid.h b/pcbnew/router/pns_solid.h index 78de1c6f21..25cb112fee 100644 --- a/pcbnew/router/pns_solid.h +++ b/pcbnew/router/pns_solid.h @@ -38,7 +38,7 @@ public: SOLID() : ITEM( SOLID_T ), m_shape( NULL ), - m_alternateShape( NULL ) + m_hole( NULL ) { m_movable = false; m_padToDie = 0; @@ -48,7 +48,7 @@ public: ~SOLID() { delete m_shape; - delete m_alternateShape; + delete m_hole; } SOLID( const SOLID& aSolid ) : @@ -59,10 +59,10 @@ public: else m_shape = nullptr; - if( aSolid.m_alternateShape ) - m_alternateShape = aSolid.m_alternateShape->Clone(); + if( aSolid.m_hole ) + m_hole = aSolid.m_hole->Clone(); else - m_alternateShape = nullptr; + m_hole = nullptr; m_pos = aSolid.m_pos; m_padToDie = aSolid.m_padToDie; @@ -77,21 +77,24 @@ public: const SHAPE* Shape() const override { return m_shape; } - const SHAPE* AlternateShape() const override { return m_alternateShape; } + const SHAPE* Hole() const override { return m_hole; } const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0, int aLayer = -1 ) const override; + const SHAPE_LINE_CHAIN HoleHull( int aClearance, int aWalkaroundThickness, + int aLayer ) const override; + void SetShape( SHAPE* shape ) { delete m_shape; m_shape = shape; } - void SetAlternateShape( SHAPE* shape ) + void SetHole( SHAPE* shape ) { - delete m_alternateShape; - m_alternateShape = shape; + delete m_hole; + m_hole = shape; } const VECTOR2I& Pos() const { return m_pos; } @@ -119,7 +122,7 @@ public: private: VECTOR2I m_pos; SHAPE* m_shape; - SHAPE* m_alternateShape; + SHAPE* m_hole; VECTOR2I m_offset; int m_padToDie; double m_orientation; // in 1/10 degrees, matching PAD diff --git a/pcbnew/router/pns_utils.cpp b/pcbnew/router/pns_utils.cpp index 3593e5f5ce..acf53b2be8 100644 --- a/pcbnew/router/pns_utils.cpp +++ b/pcbnew/router/pns_utils.cpp @@ -112,12 +112,21 @@ const SHAPE_LINE_CHAIN ArcHull( const SHAPE_ARC& aSeg, int aClearance, const SHAPE_LINE_CHAIN SegmentHull ( const SHAPE_SEGMENT& aSeg, int aClearance, int aWalkaroundThickness ) { - int d = aSeg.GetWidth() / 2 + aClearance + aWalkaroundThickness / 2 + HULL_MARGIN; + int cl = aClearance + aWalkaroundThickness / 2 + HULL_MARGIN; + int d = aSeg.GetWidth() / 2 + cl; int x = (int)( 2.0 / ( 1.0 + M_SQRT2 ) * d ); const VECTOR2I a = aSeg.GetSeg().A; const VECTOR2I b = aSeg.GetSeg().B; + if( a == b ) + { + return OctagonalHull( a - VECTOR2I( aSeg.GetWidth() / 2, aSeg.GetWidth() / 2 ), + VECTOR2I( aSeg.GetWidth(), aSeg.GetWidth() ), + cl + 1, + 0.52 * d ); + } + VECTOR2I dir = b - a; VECTOR2I p0 = dir.Perpendicular().Resize( d ); VECTOR2I ds = dir.Perpendicular().Resize( x / 2 ); diff --git a/pcbnew/router/pns_via.cpp b/pcbnew/router/pns_via.cpp index bca71a6bd0..45d141919a 100644 --- a/pcbnew/router/pns_via.cpp +++ b/pcbnew/router/pns_via.cpp @@ -76,7 +76,7 @@ const SHAPE_LINE_CHAIN VIA::Hull( int aClearance, int aWalkaroundThickness, int int cl = ( aClearance + aWalkaroundThickness / 2 ); int width = m_diameter; - if( !ROUTER::GetInstance()->GetInterface()->IsOnLayer( this, aLayer ) ) + if( !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( this, aLayer ) ) width = m_drill; return OctagonalHull( m_pos - diff --git a/pcbnew/router/pns_via.h b/pcbnew/router/pns_via.h index 4e2053f302..630f33c310 100644 --- a/pcbnew/router/pns_via.h +++ b/pcbnew/router/pns_via.h @@ -67,7 +67,7 @@ public: m_diameter = aDiameter; m_drill = aDrill; m_shape = SHAPE_CIRCLE( aPos, aDiameter / 2 ); - m_alternateShape = SHAPE_CIRCLE( m_pos, aDrill / 2 ); + m_hole = SHAPE_CIRCLE( m_pos, aDrill / 2 ); m_viaType = aViaType; m_isFree = false; } @@ -81,7 +81,7 @@ public: m_pos = aB.m_pos; m_diameter = aB.m_diameter; m_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 ); - m_alternateShape = SHAPE_CIRCLE( m_pos, aB.m_drill / 2 ); + m_hole = SHAPE_CIRCLE( m_pos, aB.m_drill / 2 ); m_marker = aB.m_marker; m_rank = aB.m_rank; m_drill = aB.m_drill; @@ -151,9 +151,14 @@ public: return &m_shape; } - const SHAPE* AlternateShape() const override + const SHAPE_CIRCLE* Hole() const override { - return &m_alternateShape; + return &m_hole; + } + + void SetHole( const SHAPE_CIRCLE& aHole ) + { + m_hole = aHole; } VIA* Clone() const override; @@ -179,7 +184,7 @@ private: int m_drill; VECTOR2I m_pos; SHAPE_CIRCLE m_shape; - SHAPE_CIRCLE m_alternateShape; + SHAPE_CIRCLE m_hole; VIATYPE m_viaType; bool m_isFree; }; diff --git a/pcbnew/router/router_preview_item.cpp b/pcbnew/router/router_preview_item.cpp index 7aeb7af584..a54dfca446 100644 --- a/pcbnew/router/router_preview_item.cpp +++ b/pcbnew/router/router_preview_item.cpp @@ -86,8 +86,8 @@ void ROUTER_PREVIEW_ITEM::Update( const PNS::ITEM* aItem ) m_color.a = 0.8; m_depth = BaseOverlayDepth - aItem->Layers().Start(); - if( ( aItem->Marker() & PNS::MK_ALT_SHAPE ) && aItem->AlternateShape() ) - m_shape = aItem->AlternateShape()->Clone(); + if(( aItem->Marker() & PNS::MK_HOLE ) && aItem->Hole() ) + m_shape = aItem->Hole()->Clone(); else m_shape = aItem->Shape()->Clone();