diff --git a/pcbnew/router/pns_dragger.cpp b/pcbnew/router/pns_dragger.cpp index 14c1610899..217235576b 100644 --- a/pcbnew/router/pns_dragger.cpp +++ b/pcbnew/router/pns_dragger.cpp @@ -67,6 +67,48 @@ bool DRAGGER::propagateViaForces( NODE* node, std::set& vias ) return false; } + +VVIA* DRAGGER::checkVirtualVia( const VECTOR2D& aP, SEGMENT* aSeg ) +{ + int w2 = aSeg->Width() / 2; + + auto distA = ( aP - aSeg->Seg().A ).EuclideanNorm(); + auto distB = ( aP - aSeg->Seg().B ).EuclideanNorm(); + + VECTOR2I psnap; + + if( distA <= w2 ) + { + psnap = aSeg->Seg().A; + } + else if( distB <= w2 ) + { + psnap = aSeg->Seg().B; + } + else + { + return nullptr; + } + + JOINT *jt = m_world->FindJoint( psnap, aSeg ); + + if ( !jt ) + { + return nullptr; + } + + for( auto lnk : jt->LinkList() ) + { + if( lnk.item->IsVirtual() && lnk.item->OfKind( ITEM::VIA_T )) + { + return static_cast( lnk.item ); + } + } + + return nullptr; +} + + bool DRAGGER::startDragSegment( const VECTOR2D& aP, SEGMENT* aSeg ) { int w2 = aSeg->Width() / 2; @@ -111,7 +153,6 @@ bool DRAGGER::startDragSegment( const VECTOR2D& aP, SEGMENT* aSeg ) } - bool DRAGGER::startDragArc( const VECTOR2D& aP, ARC* aArc ) { m_draggedLine = m_world->AssembleLine( aArc, &m_draggedSegmentIndex ); @@ -193,8 +234,19 @@ bool DRAGGER::Start( const VECTOR2I& aP, ITEM_SET& aPrimitives ) switch( startItem->Kind() ) { case ITEM::SEGMENT_T: - return startDragSegment( aP, static_cast( startItem ) ); + { + SEGMENT* seg = static_cast( startItem ); + VVIA* vvia = checkVirtualVia( aP, seg ); + if( vvia ) + { + return startDragVia( vvia ); + } + else + { + return startDragSegment( aP, seg ); + } + } case ITEM::VIA_T: return startDragVia( static_cast( startItem ) ); diff --git a/pcbnew/router/pns_dragger.h b/pcbnew/router/pns_dragger.h index c597524cca..771c042e02 100644 --- a/pcbnew/router/pns_dragger.h +++ b/pcbnew/router/pns_dragger.h @@ -126,6 +126,7 @@ private: void optimizeAndUpdateDraggedLine( LINE& aDragged, const LINE& aOrig, const VECTOR2I& aP ); bool propagateViaForces( NODE* node, std::set& vias ); bool tryWalkaround( NODE* aNode, LINE& aOrig, LINE& aWalk ); + VVIA* checkVirtualVia( const VECTOR2D& aP, SEGMENT* aSeg ); VIA_HANDLE m_initialVia; diff --git a/pcbnew/router/pns_node.cpp b/pcbnew/router/pns_node.cpp index 0268bcbb4a..2dffa07f1b 100644 --- a/pcbnew/router/pns_node.cpp +++ b/pcbnew/router/pns_node.cpp @@ -1051,6 +1051,58 @@ int NODE::FindLinesBetweenJoints( const JOINT& aA, const JOINT& aB, std::vector< } +void NODE::FixupVirtualVias() +{ + int n; + + std::vector vvias; + + for( auto& joint : m_joints ) + { + if( joint.second.Layers().IsMultilayer() ) + continue; + + int n_seg = 0, n_solid = 0, n_vias = 0; + int prev_w = -1; + int max_w = -1; + bool is_width_change = false; + + for( const auto& lnk : joint.second.LinkList() ) + { + if( lnk.item->OfKind( ITEM::VIA_T ) ) + n_vias++; + else if( lnk.item->OfKind( ITEM::SOLID_T ) ) + n_solid++; + else if( const auto t = dyn_cast( lnk.item ) ) + { + int w = t->Width(); + + if( prev_w >= 0 && w != prev_w ) + { + is_width_change = true; + } + + max_w = std::max( w, max_w ); + prev_w = w; + } + } + + if( ( is_width_change || n_seg >= 3 ) && n_solid == 0 && n_vias == 0 ) + { + // fixme: the hull margin here is an ugly temporary workaround. The real fix + // is to use octagons for via force propagation. + vvias.push_back( new VVIA( joint.second.Pos(), joint.second.Layers().Start(), + max_w + 2 * PNS_HULL_MARGIN, joint.second.Net() ) ); + } + } + + for( auto vvia : vvias ) + { + Add( ItemCast( std::move( std::unique_ptr( vvia ) ) ) ); + } +} + + JOINT* NODE::FindJoint( const VECTOR2I& aPos, int aLayer, int aNet ) { JOINT::HASH_TAG tag; diff --git a/pcbnew/router/pns_node.h b/pcbnew/router/pns_node.h index f62aa8a725..3b3dfb8d27 100644 --- a/pcbnew/router/pns_node.h +++ b/pcbnew/router/pns_node.h @@ -380,6 +380,8 @@ public: return m_override.find( aItem ) != m_override.end(); } + void FixupVirtualVias(); + private: void Add( std::unique_ptr< ITEM > aItem, bool aAllowRedundant = false ); diff --git a/pcbnew/router/pns_router.cpp b/pcbnew/router/pns_router.cpp index 14bf90cc10..1c13a2a960 100644 --- a/pcbnew/router/pns_router.cpp +++ b/pcbnew/router/pns_router.cpp @@ -96,9 +96,10 @@ void ROUTER::SyncWorld() m_world = std::make_unique( ); m_iface->SyncWorld( m_world.get() ); - + m_world->FixupVirtualVias(); } + void ROUTER::ClearWorld() { if( m_world ) @@ -612,15 +613,25 @@ void ROUTER::CommitRouting( NODE* aNode ) } } - if( !is_changed ) + if( !is_changed && !item->IsVirtual() ) m_iface->RemoveItem( item ); } for( ITEM* item : added ) - m_iface->AddItem( item ); + { + if( !item->IsVirtual() ) + { + m_iface->AddItem( item ); + } + } for( ITEM* item : changed ) - m_iface->UpdateItem( item ); + { + if( !item->IsVirtual() ) + { + m_iface->UpdateItem( item ); + } + } m_iface->Commit(); m_world->Commit( aNode ); diff --git a/pcbnew/router/router_preview_item.cpp b/pcbnew/router/router_preview_item.cpp index 49da41906d..9c85ba072c 100644 --- a/pcbnew/router/router_preview_item.cpp +++ b/pcbnew/router/router_preview_item.cpp @@ -70,13 +70,16 @@ void ROUTER_PREVIEW_ITEM::Update( const PNS::ITEM* aItem ) { m_originLayer = aItem->Layers().Start(); - if( aItem->OfKind( PNS::ITEM::LINE_T ) ) + if( const auto l = dyn_cast( aItem ) ) { - const PNS::LINE* l = static_cast( aItem ); - if( !l->SegmentCount() ) return; } + else if( const auto v = dyn_cast( aItem ) ) + { + if( v->IsVirtual() ) + return; + } assert( m_originLayer >= 0 );