diff --git a/pcbnew/connectivity/connectivity_items.h b/pcbnew/connectivity/connectivity_items.h index 7f92f6e13e..724e342826 100644 --- a/pcbnew/connectivity/connectivity_items.h +++ b/pcbnew/connectivity/connectivity_items.h @@ -250,6 +250,9 @@ public: virtual int AnchorCount() const; virtual const VECTOR2I GetAnchor( int n ) const; + int GetAnchorItemCount() const { return m_anchors.size(); } + std::shared_ptr GetAnchorItem( int n ) const { return m_anchors[n]; } + int Net() const { return ( !m_parent || !m_valid ) ? -1 : m_parent->GetNetCode(); @@ -343,6 +346,20 @@ public: virtual int AnchorCount() const override; virtual const VECTOR2I GetAnchor( int n ) const override; + const SHAPE_LINE_CHAIN& GetOutline() const + { + return m_triangulatedPoly->Outline( m_subpolyIndex ); + } + + VECTOR2I ClosestPoint( const VECTOR2I aPt ) + { + VECTOR2I closest; + + m_triangulatedPoly->SquaredDistanceToPolygon( aPt, m_subpolyIndex, &closest ); + + return closest; + } + bool Collide( SHAPE* aRefShape ) const { BOX2I bbox = aRefShape->BBox(); @@ -368,7 +385,6 @@ public: } private: - std::vector m_testOutlinePoints; int m_subpolyIndex; PCB_LAYER_ID m_layer; std::shared_ptr m_triangulatedPoly; diff --git a/pcbnew/ratsnest/ratsnest_data.cpp b/pcbnew/ratsnest/ratsnest_data.cpp index 81d63d95d3..0f24c60aac 100644 --- a/pcbnew/ratsnest/ratsnest_data.cpp +++ b/pcbnew/ratsnest/ratsnest_data.cpp @@ -328,11 +328,127 @@ void RN_NET::compute() } +void RN_NET::optimizeRNEdges() +{ + auto findZoneAnchor = + [&]( const VECTOR2I& aPos, const LSET& aLayerSet, + const std::shared_ptr aAnchor ) + { + SEG::ecoord closest_dist_sq = ( aAnchor->Pos() - aPos ).SquaredEuclideanNorm(); + VECTOR2I closest_pt; + CN_ITEM* closest_item = nullptr; + + for( CN_ITEM* item : aAnchor->Item()->ConnectedItems() ) + { + CN_ZONE_LAYER* zoneLayer = dynamic_cast( item ); + + if( zoneLayer && aLayerSet.test( zoneLayer->Layer() ) ) + { + const std::vector& pts = zoneLayer->GetOutline().CPoints(); + + for( VECTOR2I pt : pts ) + { + SEG::ecoord dist_sq = ( pt - aPos ).SquaredEuclideanNorm(); + + if( dist_sq < closest_dist_sq ) + { + closest_pt = pt; + closest_item = zoneLayer; + closest_dist_sq = dist_sq; + } + } + } + } + + if( closest_item ) + { + closest_item->AddAnchor( closest_pt ); + return closest_item->GetAnchorItem( closest_item->GetAnchorItemCount() - 1 ); + } + + return aAnchor; + }; + + auto findZoneToZoneAnchors = + [&]( std::shared_ptr& a, std::shared_ptr& b ) + { + for( CN_ITEM* itemA : a->Item()->ConnectedItems() ) + { + CN_ZONE_LAYER* zoneLayerA = dynamic_cast( itemA ); + + if( !zoneLayerA ) + continue; + + for( CN_ITEM* itemB : b->Item()->ConnectedItems() ) + { + CN_ZONE_LAYER* zoneLayerB = dynamic_cast( itemB ); + + if( zoneLayerB && zoneLayerB->Layer() == zoneLayerA->Layer() ) + { + // Process the first matching layer. We don't really care if it's + // the "best" layer or not, as anything will be better than the + // original anchors (which are connected to the zone and so certainly + // don't look like they should have ratsnest lines coming off them). + + VECTOR2I startA = zoneLayerA->GetOutline().GetPoint( 0 ); + VECTOR2I startB = zoneLayerB->GetOutline().GetPoint( 0 ); + const SHAPE* shapeA = &zoneLayerA->GetOutline(); + const SHAPE* shapeB = &zoneLayerB->GetOutline(); + int startDist = ( startA - startB ).EuclideanNorm(); + + VECTOR2I ptA; + shapeA->Collide( shapeB, startDist + 10, nullptr, &ptA ); + zoneLayerA->AddAnchor( ptA ); + a = zoneLayerA->GetAnchorItem( zoneLayerA->GetAnchorItemCount() - 1 ); + + VECTOR2I ptB; + shapeB->Collide( shapeA, startDist + 10, nullptr, &ptB ); + zoneLayerB->AddAnchor( ptB ); + b = zoneLayerB->GetAnchorItem( zoneLayerB->GetAnchorItemCount() - 1 ); + + return; + } + } + } + }; + + for( CN_EDGE& edge : m_rnEdges ) + { + std::shared_ptr source = edge.GetSourceNode(); + std::shared_ptr target = edge.GetTargetNode(); + + if( source->ConnectedItemsCount() == 0 ) + { + edge.SetTargetNode( findZoneAnchor( source->Pos(), source->Parent()->GetLayerSet(), + target ) ); + } + else if( target->ConnectedItemsCount() == 0 ) + { + edge.SetSourceNode( findZoneAnchor( target->Pos(), target->Parent()->GetLayerSet(), + source ) ); + } + else + { + findZoneToZoneAnchors( source, target ); + edge.SetSourceNode( source ); + edge.SetTargetNode( target ); + } + } +} + void RN_NET::Update() { compute(); +#ifdef PROFILE + PROF_TIMER cnt( "optimize" ); +#endif + optimizeRNEdges(); +#ifdef PROFILE + cnt.Show(); +#endif + m_dirty = false; } @@ -451,4 +567,3 @@ bool RN_NET::NearestBicoloredPair( const RN_NET& aOtherNet, VECTOR2I* aPos1, VEC return rv; } - diff --git a/pcbnew/ratsnest/ratsnest_data.h b/pcbnew/ratsnest/ratsnest_data.h index 6b3cbb2bc0..fb1c9e83a2 100644 --- a/pcbnew/ratsnest/ratsnest_data.h +++ b/pcbnew/ratsnest/ratsnest_data.h @@ -99,6 +99,11 @@ protected: ///< Compute the minimum spanning tree using Kruskal's algorithm void kruskalMST( const std::vector &aEdges ); + ///< Find optimal ends of RNEdges. The MST will have found the closest anchors, but when + ///< zones are involved we might have points closer than the anchors. + void optimizeRNEdges(); + +protected: ///< Vector of nodes std::multiset, CN_PTR_CMP> m_nodes;