diff --git a/libs/kimath/include/geometry/shape_index.h b/libs/kimath/include/geometry/shape_index.h index d76d7d7922..16103bc1f2 100644 --- a/libs/kimath/include/geometry/shape_index.h +++ b/libs/kimath/include/geometry/shape_index.h @@ -44,9 +44,9 @@ * @return a SHAPE* object equivalent to object. */ template -static const SHAPE* shapeFunctor( T aItem ) +static const SHAPE* shapeFunctor( T aItem, int aLayer ) { - return aItem->Shape(); + return aItem->Shape( aLayer ); } /** @@ -59,9 +59,9 @@ static const SHAPE* shapeFunctor( T aItem ) * @return a BOX2I object containing the bounding box of the T object. */ template -BOX2I boundingBox( T aObject ) +BOX2I boundingBox( T aObject, int aLayer ) { - BOX2I bbox = shapeFunctor( aObject )->BBox(); + BOX2I bbox = shapeFunctor( aObject, aLayer )->BBox(); return bbox; } @@ -89,13 +89,14 @@ void acceptVisitor( T aObject, V aVisitor ) * * @param aObject is a generic T object. * @param aAnotherObject is a generic U object. + * @param aLayer is the layer to test * @param aMinDistance is the minimum collision distance. * @return true if object and anotherObject collide. */ template -bool collide( T aObject, U aAnotherObject, int aMinDistance ) +bool collide( T aObject, U aAnotherObject, int aLayer, int aMinDistance ) { - return shapeFunctor( aObject )->Collide( aAnotherObject, aMinDistance ); + return shapeFunctor( aObject, aLayer )->Collide( aAnotherObject, aMinDistance ); } template @@ -197,7 +198,7 @@ class SHAPE_INDEX } }; - SHAPE_INDEX(); + explicit SHAPE_INDEX( int aLayer ); ~SHAPE_INDEX(); @@ -281,6 +282,7 @@ class SHAPE_INDEX private: RTree* m_tree; + int m_shapeLayer; }; /* @@ -288,9 +290,10 @@ class SHAPE_INDEX */ template -SHAPE_INDEX::SHAPE_INDEX() +SHAPE_INDEX::SHAPE_INDEX( int aLayer ) { this->m_tree = new RTree(); + this->m_shapeLayer = aLayer; } template @@ -311,7 +314,7 @@ void SHAPE_INDEX::Add( T aShape, const BOX2I& aBbox ) template void SHAPE_INDEX::Add( T aShape ) { - BOX2I box = boundingBox( aShape ); + BOX2I box = boundingBox( aShape, this->m_shapeLayer ); int min[2] = { box.GetX(), box.GetY() }; int max[2] = { box.GetRight(), box.GetBottom() }; @@ -321,7 +324,7 @@ void SHAPE_INDEX::Add( T aShape ) template void SHAPE_INDEX::Remove( T aShape ) { - BOX2I box = boundingBox( aShape ); + BOX2I box = boundingBox( aShape, this->m_shapeLayer ); int min[2] = { box.GetX(), box.GetY() }; int max[2] = { box.GetRight(), box.GetBottom() }; @@ -345,7 +348,7 @@ void SHAPE_INDEX::Reindex() while( !iter.IsNull() ) { T shape = *iter; - BOX2I box = boundingBox( shape ); + BOX2I box = boundingBox( shape, this->m_shapeLayer ); int min[2] = { box.GetX(), box.GetY() }; int max[2] = { box.GetRight(), box.GetBottom() }; newTree->Insert( min, max, shape ); diff --git a/libs/kimath/include/geometry/shape_index_list.h b/libs/kimath/include/geometry/shape_index_list.h index c38b55e9ae..081d7c9982 100644 --- a/libs/kimath/include/geometry/shape_index_list.h +++ b/libs/kimath/include/geometry/shape_index_list.h @@ -35,7 +35,7 @@ template const SHAPE* defaultShapeFunctor( const T aItem ) { - return aItem->Shape(); + return aItem->Shape( -1 ); } template > diff --git a/pcbnew/generators/pcb_tuning_pattern.cpp b/pcbnew/generators/pcb_tuning_pattern.cpp index d9ee6d05e9..784a43cd8d 100644 --- a/pcbnew/generators/pcb_tuning_pattern.cpp +++ b/pcbnew/generators/pcb_tuning_pattern.cpp @@ -952,7 +952,7 @@ static PNS::LINKED_ITEM* pickSegment( PNS::ROUTER* aRouter, const VECTOR2I& aWhe if( aBaseline.PointCount() > 0 ) { SEG::ecoord dcBaseline; - VECTOR2I target = segm->Shape()->Centre(); + VECTOR2I target = segm->Shape( -1 )->Centre(); if( aBaseline.SegmentCount() > 0 ) dcBaseline = aBaseline.SquaredDistance( target ); diff --git a/pcbnew/padstack.cpp b/pcbnew/padstack.cpp index 7a919624c6..3ce7694782 100644 --- a/pcbnew/padstack.cpp +++ b/pcbnew/padstack.cpp @@ -832,11 +832,15 @@ void PADSTACK::ForEachUniqueLayer( const std::function& aM break; case MODE::CUSTOM: - for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, MAX_CU_LAYERS ) ) + { + int layerCount = m_parent ? m_parent->BoardCopperLayerCount() : MAX_CU_LAYERS; + + for( PCB_LAYER_ID layer : LAYER_RANGE( F_Cu, B_Cu, layerCount ) ) aMethod( layer ); break; } + } } diff --git a/pcbnew/router/pns_arc.cpp b/pcbnew/router/pns_arc.cpp index 6489c9043b..1875cd1384 100644 --- a/pcbnew/router/pns_arc.cpp +++ b/pcbnew/router/pns_arc.cpp @@ -45,8 +45,8 @@ ARC* ARC::Clone() const OPT_BOX2I ARC::ChangedArea( const ARC* aOther ) const { - BOX2I tmp = Shape()->BBox(); - tmp.Merge( aOther->Shape()->BBox() ); + BOX2I tmp = Shape( -1 )->BBox(); + tmp.Merge( aOther->Shape( -1 )->BBox() ); return tmp; } diff --git a/pcbnew/router/pns_arc.h b/pcbnew/router/pns_arc.h index b20dd84de9..72e35cceaf 100644 --- a/pcbnew/router/pns_arc.h +++ b/pcbnew/router/pns_arc.h @@ -75,7 +75,7 @@ public: ARC* Clone() const override; - const SHAPE* Shape() const override + const SHAPE* Shape( int aLayer ) const override { return static_cast( &m_arc ); } diff --git a/pcbnew/router/pns_component_dragger.cpp b/pcbnew/router/pns_component_dragger.cpp index 42d409dde2..55110a7e0c 100644 --- a/pcbnew/router/pns_component_dragger.cpp +++ b/pcbnew/router/pns_component_dragger.cpp @@ -143,7 +143,7 @@ bool COMPONENT_DRAGGER::Start( const VECTOR2I& aP, ITEM_SET& aPrimitives ) { LINKED_ITEM* li = static_cast( extraJoint->LinkList().front() ); - if( li->Collide( solid, m_world ) ) + if( li->Collide( solid, m_world, solid->Layer() ) ) addLinked( solid, extraJoint, li, extraJoint->Pos() - solid->Pos() ); } } diff --git a/pcbnew/router/pns_diff_pair.cpp b/pcbnew/router/pns_diff_pair.cpp index 64ec6e5920..0cf1798a25 100644 --- a/pcbnew/router/pns_diff_pair.cpp +++ b/pcbnew/router/pns_diff_pair.cpp @@ -436,12 +436,13 @@ void DP_GATEWAYS::BuildFromPrimitivePair( const DP_PRIMITIVE_PAIR& aPair, bool a const int pvMask = ITEM::SOLID_T | ITEM::VIA_T; - if( aPair.PrimP()->OfKind( pvMask ) && aPair.PrimN()->OfKind( pvMask ) ) + if( aPair.PrimP()->OfKind( pvMask ) && aPair.PrimN()->OfKind( pvMask ) ) { p0_p = aPair.AnchorP(); p0_n = aPair.AnchorN(); - shP = aPair.PrimP()->Shape(); + // TODO(JE) padstacks + shP = aPair.PrimP()->Shape( -1 ); } else if( aPair.PrimP()->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) && aPair.PrimN()->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) ) diff --git a/pcbnew/router/pns_diff_pair.h b/pcbnew/router/pns_diff_pair.h index b3af45ca3c..8fd1ed153b 100644 --- a/pcbnew/router/pns_diff_pair.h +++ b/pcbnew/router/pns_diff_pair.h @@ -413,8 +413,8 @@ public: void SetViaDiameter( int aDiameter ) { - m_via_p.SetDiameter( aDiameter ); - m_via_n.SetDiameter( aDiameter ); + m_via_p.SetDiameter( VIA::ALL_LAYERS, aDiameter ); + m_via_n.SetDiameter( VIA::ALL_LAYERS, aDiameter ); } void SetViaDrill( int aDrill ) diff --git a/pcbnew/router/pns_diff_pair_placer.cpp b/pcbnew/router/pns_diff_pair_placer.cpp index 64fd0249f6..993a76e704 100644 --- a/pcbnew/router/pns_diff_pair_placer.cpp +++ b/pcbnew/router/pns_diff_pair_placer.cpp @@ -118,12 +118,12 @@ bool DIFF_PAIR_PLACER::propagateDpHeadForces ( const VECTOR2I& aP, VECTOR2I& aNe if( m_placingVia ) { - virtHead.SetDiameter( viaGap() + 2 * virtHead.Diameter() ); + virtHead.SetDiameter( 0, viaGap() + 2 * virtHead.Diameter( 0 ) ); } else { virtHead.SetLayer( m_currentLayer ); - virtHead.SetDiameter( m_sizes.DiffPairGap() + 2 * m_sizes.DiffPairWidth() ); + virtHead.SetDiameter( 0, m_sizes.DiffPairGap() + 2 * m_sizes.DiffPairWidth() ); } bool solidsOnly = true; @@ -160,7 +160,8 @@ bool DIFF_PAIR_PLACER::propagateDpHeadForces ( const VECTOR2I& aP, VECTOR2I& aNe int clearance = m_currentNode->GetClearance( obs->m_item, &m_currentTrace.PLine(), false ); - if( obs->m_item->Shape()->Collide( virtHead.Shape(), clearance, &force ) ) + // TODO(JE) padstacks - this won't work + if( obs->m_item->Shape( 0 )->Collide( virtHead.Shape( 0 ), clearance, &force ) ) { collided = true; totalForce += force; diff --git a/pcbnew/router/pns_hole.h b/pcbnew/router/pns_hole.h index ec53f40492..204a5a9e8d 100644 --- a/pcbnew/router/pns_hole.h +++ b/pcbnew/router/pns_hole.h @@ -66,7 +66,7 @@ public: int Radius() const; - const SHAPE* Shape() const override { return m_holeShape; } + const SHAPE* Shape( int aLayer ) const override { return m_holeShape; } void SetParentPadVia( ITEM* aParent ) { m_parentPadVia = aParent; } ITEM* ParentPadVia() const override { return m_parentPadVia; } diff --git a/pcbnew/router/pns_index.cpp b/pcbnew/router/pns_index.cpp index 2350d666f6..2c5fa07708 100644 --- a/pcbnew/router/pns_index.cpp +++ b/pcbnew/router/pns_index.cpp @@ -31,10 +31,13 @@ void INDEX::Add( ITEM* aItem ) assert( range.Start() != -1 && range.End() != -1 ); if( m_subIndices.size() <= static_cast( range.End() ) ) - m_subIndices.resize( 2 * range.End() + 1 ); // +1 handles the 0 case + { + for( int i = 0; i <= range.End(); ++i ) + m_subIndices.emplace_back( std::make_unique( i ) ); + } for( int i = range.Start(); i <= range.End(); ++i ) - m_subIndices[i].Add( aItem ); + m_subIndices[i]->Add( aItem ); m_allItems.insert( aItem ); NET_HANDLE net = aItem->Net(); @@ -53,7 +56,7 @@ void INDEX::Remove( ITEM* aItem ) return; for( int i = range.Start(); i <= range.End(); ++i ) - m_subIndices[i].Remove( aItem ); + m_subIndices[i]->Remove( aItem ); m_allItems.erase( aItem ); NET_HANDLE net = aItem->Net(); diff --git a/pcbnew/router/pns_index.h b/pcbnew/router/pns_index.h index df297c2b21..9df5b0af5f 100644 --- a/pcbnew/router/pns_index.h +++ b/pcbnew/router/pns_index.h @@ -31,6 +31,7 @@ #include #include "pns_item.h" +#include "pns_node.h" namespace PNS { @@ -122,7 +123,7 @@ private: int querySingle( std::size_t aIndex, const SHAPE* aShape, int aMinDistance, Visitor& aVisitor ) const; private: - std::deque m_subIndices; + std::deque> m_subIndices; std::map m_netMap; ITEM_SET m_allItems; }; @@ -134,7 +135,8 @@ int INDEX::querySingle( std::size_t aIndex, const SHAPE* aShape, int aMinDistanc if( aIndex >= m_subIndices.size() ) return 0; - return m_subIndices[aIndex].Query( aShape, aMinDistance, aVisitor); + LAYER_CONTEXT_SETTER layerContext( aVisitor, aIndex ); + return m_subIndices[aIndex]->Query( aShape, aMinDistance, aVisitor); } template @@ -147,7 +149,7 @@ int INDEX::Query( const ITEM* aItem, int aMinDistance, Visitor& aVisitor ) const const PNS_LAYER_RANGE& layers = aItem->Layers(); for( int i = layers.Start(); i <= layers.End(); ++i ) - total += querySingle( i, aItem->Shape(), aMinDistance, aVisitor ); + total += querySingle( i, aItem->Shape( i ), aMinDistance, aVisitor ); return total; } diff --git a/pcbnew/router/pns_item.cpp b/pcbnew/router/pns_item.cpp index cfe7a79e66..31ada99ba3 100644 --- a/pcbnew/router/pns_item.cpp +++ b/pcbnew/router/pns_item.cpp @@ -55,6 +55,7 @@ static bool shouldWeConsiderHoleCollisions( const ITEM* aItem, const ITEM* aHead { const ITEM* parentI = holeI->ParentPadVia(); const ITEM* parentH = holeH->ParentPadVia(); + if( !parentH || !parentI ) return true; @@ -73,7 +74,7 @@ static bool shouldWeConsiderHoleCollisions( const ITEM* aItem, const ITEM* aHead // identical and belonging to the same net as non-colliding. if( parentViaI && parentViaH && parentViaI->Pos() == parentViaH->Pos() - && parentViaI->Diameter() == parentViaH->Diameter() + && parentViaI->PadstackMatches( *parentViaH ) && parentViaI->Net() == parentViaH->Net() && parentViaI->Drill() == parentViaH->Drill() ) return false; @@ -90,17 +91,32 @@ static bool shouldWeConsiderHoleCollisions( const ITEM* aItem, const ITEM* aHead } -bool ITEM::collideSimple( const ITEM* aHead, const NODE* aNode, +std::set ITEM::RelevantShapeLayers( const ITEM* aOther ) const +{ + std::vector myLayers = UniqueShapeLayers(); + std::vector otherLayers = aOther->UniqueShapeLayers(); + + if( !HasUniqueShapeLayers() && !aOther->HasUniqueShapeLayers() ) + return { -1 }; + + std::set relevantLayers; + + std::set_union( myLayers.begin(), myLayers.end(), otherLayers.begin(), otherLayers.end(), + std::inserter( relevantLayers, relevantLayers.begin() ) ); + + return relevantLayers; +} + + +bool ITEM::collideSimple( const ITEM* aHead, const NODE* aNode, int aLayer, COLLISION_SEARCH_CONTEXT* aCtx ) const { // Note: if 'this' is a pad or a via then its hole is a separate PNS::ITEM in the node's // index and we don't need to deal with holeI here. The same is *not* true of the routing // "head", so we do need to handle holeH. - - const SHAPE* shapeI = Shape(); int lineWidthI = 0; - const SHAPE* shapeH = aHead->Shape(); + //const SHAPE* shapeH = aHead->Shape(); const HOLE* holeH = aHead->Hole(); int lineWidthH = 0; bool collisionsFound = false; @@ -118,19 +134,19 @@ bool ITEM::collideSimple( const ITEM* aHead, const NODE* aNode, if( const auto line = dyn_cast( this ) ) { if( line->EndsWithVia() ) - collisionsFound |= line->Via().collideSimple( aHead, aNode, aCtx ); + collisionsFound |= line->Via().collideSimple( aHead, aNode, aLayer, aCtx ); } if( const auto line = dyn_cast( aHead ) ) { if( line->EndsWithVia() ) - collisionsFound |= line->Via().collideSimple( this, aNode, aCtx ); + collisionsFound |= line->Via().collideSimple( this, aNode, aLayer, aCtx ); } // And a special case for the "head" via's hole. if( holeH && shouldWeConsiderHoleCollisions( this, holeH ) ) { - if( Net() != holeH->Net() && collideSimple( holeH, aNode, aCtx ) ) + if( Net() != holeH->Net() && collideSimple( holeH, aNode, aLayer, aCtx ) ) collisionsFound = true; } @@ -205,6 +221,9 @@ bool ITEM::collideSimple( const ITEM* aHead, const NODE* aNode, bool checkNetTie = aNode->GetRuleResolver()->IsInNetTie( this ); + const SHAPE* shapeI = Shape( aLayer ); + const SHAPE* shapeH = aHead->Shape( aLayer ); + if( checkCastellation || checkNetTie ) { // Slow method @@ -270,9 +289,10 @@ bool ITEM::collideSimple( const ITEM* aHead, const NODE* aNode, } -bool ITEM::Collide( const ITEM* aOther, const NODE* aNode, COLLISION_SEARCH_CONTEXT *aCtx ) const +bool ITEM::Collide( const ITEM* aOther, const NODE* aNode, int aLayer, + COLLISION_SEARCH_CONTEXT *aCtx ) const { - if( collideSimple( aOther, aNode, aCtx ) ) + if( collideSimple( aOther, aNode, aLayer, aCtx ) ) return true; return false; diff --git a/pcbnew/router/pns_item.h b/pcbnew/router/pns_item.h index 6beb702904..615079e70e 100644 --- a/pcbnew/router/pns_item.h +++ b/pcbnew/router/pns_item.h @@ -155,6 +155,8 @@ public: * * @param aClearance defines how far from the body of the item the hull should be, * @param aWalkaroundThickness is the width of the line that walks around this hull. + * @param aLayer is the layer to build a hull for (the item may have different shapes on each + * layer). If aLayer is -1, the hull will be a merged hull from all layers. */ virtual const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0, int aLayer = -1 ) const @@ -217,17 +219,32 @@ public: * @param aOther is the item to check collision against. * @return true, if a collision was found. */ - bool Collide( const ITEM* aHead, const NODE* aNode, + bool Collide( const ITEM* aHead, const NODE* aNode, int aLayer, COLLISION_SEARCH_CONTEXT* aCtx = nullptr ) const; /** * Return the geometrical shape of the item. Used for collision detection and spatial indexing. + * @param aLayer is the layer to query shape for (items may have different shapes on different layers) */ - virtual const SHAPE* Shape() const + virtual const SHAPE* Shape( int aLayer ) const { return nullptr; } + /** + * Return a list of layers that have unique (potentially different) shapes + */ + virtual std::vector UniqueShapeLayers() const { return { -1 }; } + + virtual bool HasUniqueShapeLayers() const { return false; } + + /** + * Returns the set of layers on which either this or the other item can have a unique shape. + * Use this to loop over layers when hit-testing objects that can have different shapes on + * each layer (currently only VIA) + */ + std::set RelevantShapeLayers( const ITEM* aOther ) const; + virtual void Mark( int aMarker ) const { m_marker = aMarker; } virtual void Unmark( int aMarker = -1 ) const { m_marker &= ~aMarker; } virtual int Marker() const { return m_marker; } @@ -279,7 +296,7 @@ public: virtual const NODE* OwningNode() const; private: - bool collideSimple( const ITEM* aHead, const NODE* aNode, + bool collideSimple( const ITEM* aHead, const NODE* aNode, int aLayer, COLLISION_SEARCH_CONTEXT* aCtx ) const; protected: diff --git a/pcbnew/router/pns_kicad_iface.cpp b/pcbnew/router/pns_kicad_iface.cpp index 80d1831726..b503b3caf7 100644 --- a/pcbnew/router/pns_kicad_iface.cpp +++ b/pcbnew/router/pns_kicad_iface.cpp @@ -1129,7 +1129,7 @@ PNS_KICAD_IFACE::~PNS_KICAD_IFACE() std::vector> PNS_KICAD_IFACE_BASE::syncPad( PAD* aPad ) { std::vector> solids; - PNS_LAYER_RANGE layers( 0, aPad->BoardCopperLayerCount() ); + PNS_LAYER_RANGE layers( 0, aPad->BoardCopperLayerCount() - 1 ); LSEQ lmsk = aPad->GetLayerSet().CuStack(); // ignore non-copper pads except for those with holes @@ -1174,14 +1174,14 @@ std::vector> PNS_KICAD_IFACE_BASE::syncPad( PAD* aPa if( aPad->Padstack().Mode() == PADSTACK::MODE::CUSTOM ) { - solid->SetLayers( GetPNSLayerFromBoardLayer( aLayer ) ); + solid->SetLayer( GetPNSLayerFromBoardLayer( aLayer ) ); } else if( aPad->Padstack().Mode() == PADSTACK::MODE::FRONT_INNER_BACK ) { if( aLayer == F_Cu || aLayer == B_Cu ) - solid->SetLayers( GetPNSLayerFromBoardLayer( aLayer ) ); + solid->SetLayer( GetPNSLayerFromBoardLayer( aLayer ) ); else - solid->SetLayers( PNS_LAYER_RANGE( 1, aPad->BoardCopperLayerCount() - 1 ) ); + solid->SetLayers( PNS_LAYER_RANGE( 1, aPad->BoardCopperLayerCount() - 2 ) ); } else { @@ -1207,7 +1207,10 @@ std::vector> PNS_KICAD_IFACE_BASE::syncPad( PAD* aPa solid->SetOffset( VECTOR2I( offset.x, offset.y ) ); if( aPad->GetDrillSize().x > 0 ) + { solid->SetHole( new PNS::HOLE( aPad->GetEffectiveHoleShape()->Clone() ) ); + solid->Hole()->SetLayer( GetPNSLayerFromBoardLayer( aLayer ) ); + } // We generate a single SOLID for a pad, so we have to treat it as ALWAYS_FLASHED and // then perform layer-specific flashing tests internally. @@ -1285,41 +1288,81 @@ std::unique_ptr PNS_KICAD_IFACE_BASE::syncArc( PCB_ARC* aArc ) } -std::vector> PNS_KICAD_IFACE_BASE::syncVia( PCB_VIA* aVia ) +std::unique_ptr PNS_KICAD_IFACE_BASE::syncVia( PCB_VIA* aVia ) { - std::vector> vias; PCB_LAYER_ID top, bottom; aVia->LayerPair( &top, &bottom ); - aVia->Padstack().ForEachUniqueLayer( + /* + * NOTE about PNS via padstacks: + * + * PNS::VIA has no knowledge about how many layers are in the board, and there is no fixed + * reference to the "back layer" in the PNS. That means that there is no way for a VIA to know + * the difference between its bottom layer and the bottom layer of the overall board (i.e. if + * the via is a blind/buried via). For this reason, PNS::VIA::STACK_MODE::FRONT_INNER_BACK + * cannot be used for blind/buried vias. This mode will always assume that the via's top layer + * is the "front" layer and the via's bottom layer is the "back" layer, but from KiCad's point + * of view, at least at the moment, front/inner/back padstack mode is board-scoped, not + * via-scoped, so a buried via would only use the inner layer size even if its padstack mode is + * set to PADSTACK::MODE::FRONT_INNER_BACK and different sizes are defined for front or back. + * For this kind of via, the PNS VIA stack mode will be set to NORMAL because effectively it has + * the same size on every layer it exists on. + */ + + auto via = std::make_unique( aVia->GetPosition(), + SetLayersFromPCBNew( aVia->TopLayer(), aVia->BottomLayer() ), + 0, + aVia->GetDrillValue(), + aVia->GetNet(), + aVia->GetViaType() ); + + auto syncDiameter = [&]( PCB_LAYER_ID aLayer ) { - auto via = std::make_unique( aVia->GetPosition(), - SetLayersFromPCBNew( aVia->TopLayer(), aVia->BottomLayer() ), - aVia->GetWidth( aLayer ), - aVia->GetDrillValue(), - aVia->GetNet(), - aVia->GetViaType() ); + via->SetDiameter( GetPNSLayerFromBoardLayer( aLayer ), aVia->GetWidth( aLayer ) ); + }; - via->SetParent( aVia ); + switch( aVia->Padstack().Mode() ) + { + case PADSTACK::MODE::NORMAL: + via->SetDiameter( 0, aVia->GetWidth( PADSTACK::ALL_LAYERS ) ); + break; - if( aVia->IsLocked() ) - via->Mark( PNS::MK_LOCKED ); + case PADSTACK::MODE::FRONT_INNER_BACK: + if( aVia->GetViaType() == VIATYPE::BLIND_BURIED ) + { + via->SetDiameter( 0, aVia->GetWidth( PADSTACK::INNER_LAYERS ) ); + } + else + { + via->SetStackMode( PNS::VIA::STACK_MODE::FRONT_INNER_BACK ); + aVia->Padstack().ForEachUniqueLayer( syncDiameter ); + } - if( PCB_GENERATOR* generator = dynamic_cast( aVia->GetParentGroup() ) ) - { - if( !generator->HasFlag( IN_EDIT ) ) - via->Mark( PNS::MK_LOCKED ); - } + break; - via->SetIsFree( aVia->GetIsFree() ); - via->SetHole( PNS::HOLE::MakeCircularHole( aVia->GetPosition(), - aVia->GetDrillValue() / 2, - SetLayersFromPCBNew( aVia->TopLayer(), aVia->BottomLayer() ) ) ); - vias.emplace_back( std::move( via ) ); - } ); + case PADSTACK::MODE::CUSTOM: + via->SetStackMode( PNS::VIA::STACK_MODE::CUSTOM ); + aVia->Padstack().ForEachUniqueLayer( syncDiameter ); + } - return vias; + via->SetParent( aVia ); + + if( aVia->IsLocked() ) + via->Mark( PNS::MK_LOCKED ); + + if( PCB_GENERATOR* generator = dynamic_cast( aVia->GetParentGroup() ) ) + { + if( !generator->HasFlag( IN_EDIT ) ) + via->Mark( PNS::MK_LOCKED ); + } + + via->SetIsFree( aVia->GetIsFree() ); + via->SetHole( PNS::HOLE::MakeCircularHole( aVia->GetPosition(), + aVia->GetDrillValue() / 2, + SetLayersFromPCBNew( aVia->TopLayer(), aVia->BottomLayer() ) ) ); + + return via; } @@ -1432,7 +1475,7 @@ bool PNS_KICAD_IFACE_BASE::syncGraphicalItem( PNS::NODE* aWorld, PCB_SHAPE* aIte if( aItem->GetLayer() == Edge_Cuts || aItem->GetLayer() == Margin ) { - solid->SetLayers( PNS_LAYER_RANGE( 0, m_board->GetCopperLayerCount() ) ); + solid->SetLayers( PNS_LAYER_RANGE( 0, m_board->GetCopperLayerCount() - 1 ) ); solid->SetRoutable( false ); } else @@ -1707,9 +1750,7 @@ void PNS_KICAD_IFACE_BASE::SyncWorld( PNS::NODE *aWorld ) } else if( type == PCB_VIA_T ) { - std::vector> vias = syncVia( static_cast( t ) ); - - for( std::unique_ptr& via : vias ) + if( std::unique_ptr via = syncVia( static_cast( t ) ) ) aWorld->Add( std::move( via ) ); } } @@ -1878,7 +1919,7 @@ void PNS_KICAD_IFACE::HideItem( PNS::ITEM* aItem ) { if( td->IsTeardropArea() && td->GetBoundingBox().Intersects( aItem->Parent()->GetBoundingBox() ) - && td->Outline()->Collide( aItem->Shape() ) ) + && td->Outline()->Collide( aItem->Shape( td->GetLayer() ) ) ) { m_view->SetVisible( td, false ); m_view->Update( td, KIGFX::APPEARANCE ); @@ -1928,7 +1969,7 @@ void PNS_KICAD_IFACE::modifyBoardItem( PNS::ITEM* aItem ) { PNS::ARC* arc = static_cast( aItem ); PCB_ARC* arc_board = static_cast( board_item ); - const SHAPE_ARC* arc_shape = static_cast( arc->Shape() ); + const SHAPE_ARC* arc_shape = static_cast( arc->Shape( -1 ) ); m_commit->Modify( arc_board ); @@ -1961,7 +2002,7 @@ void PNS_KICAD_IFACE::modifyBoardItem( PNS::ITEM* aItem ) m_commit->Modify( via_board ); via_board->SetPosition( VECTOR2I( via->Pos().x, via->Pos().y ) ); - via_board->SetWidth( PADSTACK::ALL_LAYERS, via->Diameter() ); + via_board->SetWidth( PADSTACK::ALL_LAYERS, via->Diameter( 0 ) ); via_board->SetDrill( via->Drill() ); via_board->SetNet( static_cast( via->Net() ) ); via_board->SetViaType( via->ViaType() ); // MUST be before SetLayerPair() @@ -2014,7 +2055,7 @@ BOARD_CONNECTED_ITEM* PNS_KICAD_IFACE::createBoardItem( PNS::ITEM* aItem ) case PNS::ITEM::ARC_T: { PNS::ARC* arc = static_cast( aItem ); - PCB_ARC* new_arc = new PCB_ARC( m_board, static_cast( arc->Shape() ) ); + PCB_ARC* new_arc = new PCB_ARC( m_board, static_cast( arc->Shape( -1 ) ) ); new_arc->SetWidth( arc->Width() ); new_arc->SetLayer( GetBoardLayerFromPNSLayer( arc->Layers().Start() ) ); new_arc->SetNet( net ); @@ -2041,7 +2082,7 @@ BOARD_CONNECTED_ITEM* PNS_KICAD_IFACE::createBoardItem( PNS::ITEM* aItem ) PCB_VIA* via_board = new PCB_VIA( m_board ); PNS::VIA* via = static_cast( aItem ); via_board->SetPosition( VECTOR2I( via->Pos().x, via->Pos().y ) ); - via_board->SetWidth( PADSTACK::ALL_LAYERS, via->Diameter() ); + via_board->SetWidth( PADSTACK::ALL_LAYERS, via->Diameter( 0 ) ); via_board->SetDrill( via->Drill() ); via_board->SetNet( net ); via_board->SetViaType( via->ViaType() ); // MUST be before SetLayerPair() diff --git a/pcbnew/router/pns_kicad_iface.h b/pcbnew/router/pns_kicad_iface.h index 0f9a87335a..2e05c1d8cc 100644 --- a/pcbnew/router/pns_kicad_iface.h +++ b/pcbnew/router/pns_kicad_iface.h @@ -107,7 +107,7 @@ protected: std::vector> syncPad( PAD* aPad ); std::unique_ptr syncTrack( PCB_TRACK* aTrack ); std::unique_ptr syncArc( PCB_ARC* aArc ); - std::vector> syncVia( PCB_VIA* aVia ); + std::unique_ptr syncVia( PCB_VIA* aVia ); bool syncTextItem( PNS::NODE* aWorld, PCB_TEXT* aText, PCB_LAYER_ID aLayer ); bool syncGraphicalItem( PNS::NODE* aWorld, PCB_SHAPE* aItem ); bool syncZone( PNS::NODE* aWorld, ZONE* aZone, SHAPE_POLY_SET* aBoardOutline ); diff --git a/pcbnew/router/pns_line.h b/pcbnew/router/pns_line.h index e92e7d3a1b..bec0b74a29 100644 --- a/pcbnew/router/pns_line.h +++ b/pcbnew/router/pns_line.h @@ -98,7 +98,8 @@ public: m_blockingObstacle( nullptr ) { m_via = aVia; - m_width = aVia->Diameter(); + // TODO(JE) Padstacks - does this matter? + m_width = aVia->Diameter( aVia->Layers().Start() ); m_net = aVia->Net(); m_layers = aVia->Layers(); m_rank = aVia->Rank(); @@ -130,7 +131,7 @@ public: } ///< Return the shape of the line. - const SHAPE* Shape() const override { return &m_line; } + const SHAPE* Shape( int aLayer ) const override { return &m_line; } ///< Modifiable accessor to the underlying shape. SHAPE_LINE_CHAIN& Line() { return m_line; } @@ -197,7 +198,15 @@ public: VIA& Via() { return *m_via; } const VIA& Via() const { return *m_via; } - void SetViaDiameter( int aDiameter ) { assert(m_via); m_via->SetDiameter( aDiameter ); } + void SetViaDiameter( int aDiameter ) + { + wxCHECK( m_via, /* void */ ); + wxCHECK2_MSG( m_via->StackMode() == VIA::STACK_MODE::NORMAL, + m_via->SetStackMode( VIA::STACK_MODE::NORMAL ), + wxS( "Warning: converting a complex viastack to normal in PNS_LINE" ) ); + + m_via->SetDiameter( VIA::ALL_LAYERS, aDiameter ); + } void SetViaDrill( int aDrill ) { assert(m_via); m_via->SetDrill( aDrill ); } virtual void Mark( int aMarker ) const override; diff --git a/pcbnew/router/pns_line_placer.cpp b/pcbnew/router/pns_line_placer.cpp index d80257b7f8..abbf18ee34 100644 --- a/pcbnew/router/pns_line_placer.cpp +++ b/pcbnew/router/pns_line_placer.cpp @@ -825,7 +825,7 @@ bool LINE_PLACER::rhMarkObstacles( const VECTOR2I& aP, LINE& aNewHead, LINE& aNe if( obs ) { int clearance = m_currentNode->GetClearance( obs->m_item, &m_head, false ); - SHAPE_LINE_CHAIN hull = obs->m_item->Hull( clearance, m_head.Width() ); + SHAPE_LINE_CHAIN hull = obs->m_item->Hull( clearance, m_head.Width(), m_head.Layer() ); VECTOR2I nearest; DIRECTION_45::CORNER_MODE cornerMode = Settings().GetCornerMode(); diff --git a/pcbnew/router/pns_multi_dragger.cpp b/pcbnew/router/pns_multi_dragger.cpp index 3c23422541..1feec036df 100644 --- a/pcbnew/router/pns_multi_dragger.cpp +++ b/pcbnew/router/pns_multi_dragger.cpp @@ -302,7 +302,7 @@ bool clipToOtherLine( NODE* aNode, const LINE& aRef, LINE& aClipped ) //PNS_DBG( dbg, 3int, pclip, WHITE, 500000, wxT("")); - if( l.Collide( &aRef, aNode, &ctx ) ) + if( l.Collide( &aRef, aNode, l.Layer(), &ctx ) ) { didClip = true; curL -= step; diff --git a/pcbnew/router/pns_node.cpp b/pcbnew/router/pns_node.cpp index 1ff498a207..c2ad76f620 100644 --- a/pcbnew/router/pns_node.cpp +++ b/pcbnew/router/pns_node.cpp @@ -241,7 +241,7 @@ struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR if( visit( aCandidate ) ) return true; - if( !aCandidate->Collide( m_item, m_node, m_ctx ) ) + if( !aCandidate->Collide( m_item, m_node, m_layerContext.value_or( -1 ), m_ctx ) ) return true; if( m_ctx->options.m_limitCount > 0 && m_ctx->obstacles.size() >= m_ctx->options.m_limitCount ) @@ -363,7 +363,7 @@ NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, { const VIA& via = aLine->Via(); int viaClearance = GetClearance( obstacle.m_item, &via, aOpts.m_useClearanceEpsilon ) - + via.Diameter() / 2; + + via.Diameter( aLine->Layer() ) / 2; obstacleHull = obstacle.m_item->Hull( viaClearance, 0, layer ); @@ -478,7 +478,8 @@ struct HIT_VISITOR : public OBSTACLE_VISITOR int cl = 0; - if( aItem->Shape()->Collide( &cp, cl ) ) + // TODO(JE) padstacks -- this may not work + if( aItem->Shape( -1 )->Collide( &cp, cl ) ) m_items.Add( aItem ); return true; @@ -1088,7 +1089,7 @@ const LINE NODE::AssembleLine( LINKED_ITEM* aSeg, int* aOriginSegmentIndex, if( li->Kind() == ITEM::ARC_T ) { const ARC* arc = static_cast( li ); - const SHAPE_ARC* sa = static_cast( arc->Shape() ); + const SHAPE_ARC* sa = static_cast( arc->Shape( -1 ) ); int nSegs = line.PointCount(); VECTOR2I last = nSegs ? line.CPoint( -1 ) : VECTOR2I(); diff --git a/pcbnew/router/pns_node.h b/pcbnew/router/pns_node.h index 920f36dd7b..f870250605 100644 --- a/pcbnew/router/pns_node.h +++ b/pcbnew/router/pns_node.h @@ -117,6 +117,7 @@ struct COLLISION_SEARCH_OPTIONS int m_kindMask = -1; bool m_useClearanceEpsilon = true; std::function m_filter = nullptr; + int m_layer = -1; }; @@ -182,6 +183,9 @@ public: void SetWorld( const NODE* aNode, const NODE* aOverride = nullptr ); + void SetLayerContext( int aLayer ) { m_layerContext = aLayer; } + void ClearLayerContext() { m_layerContext = std::nullopt; } + virtual bool operator()( ITEM* aCandidate ) = 0; protected: @@ -192,6 +196,26 @@ protected: const NODE* m_node; ///< node we are searching in (either root or a branch) const NODE* m_override; ///< node that overrides root entries + std::optional m_layerContext; +}; + + +class LAYER_CONTEXT_SETTER +{ +public: + LAYER_CONTEXT_SETTER( OBSTACLE_VISITOR& aVisitor, int aLayer ) : + m_visitor( aVisitor ) + { + m_visitor.SetLayerContext( aLayer ); + } + + ~LAYER_CONTEXT_SETTER() + { + m_visitor.ClearLayerContext(); + } + +private: + OBSTACLE_VISITOR& m_visitor; }; /** diff --git a/pcbnew/router/pns_optimizer.cpp b/pcbnew/router/pns_optimizer.cpp index e46a23c764..d3c2bce154 100644 --- a/pcbnew/router/pns_optimizer.cpp +++ b/pcbnew/router/pns_optimizer.cpp @@ -141,7 +141,8 @@ struct OPTIMIZER::CACHE_VISITOR if( !( m_mask & aOtherItem->Kind() ) ) return true; - if( !aOtherItem->Collide( m_ourItem, m_node ) ) + // TODO(JE) viastacks + if( !aOtherItem->Collide( m_ourItem, m_node, m_ourItem->Layer() ) ) return true; m_collidingItem = aOtherItem; @@ -789,7 +790,7 @@ OPTIMIZER::BREAKOUT_LIST OPTIMIZER::customBreakouts( int aWidth, const ITEM* aIt bool aPermitDiagonal ) const { BREAKOUT_LIST breakouts; - const SHAPE_SIMPLE* convex = static_cast( aItem->Shape() ); + const SHAPE_SIMPLE* convex = static_cast( aItem->Shape( -1 ) ); BOX2I bbox = convex->BBox( 0 ); VECTOR2I p0 = static_cast( aItem )->Pos(); @@ -896,12 +897,13 @@ OPTIMIZER::BREAKOUT_LIST OPTIMIZER::computeBreakouts( int aWidth, const ITEM* aI case ITEM::VIA_T: { const VIA* via = static_cast( aItem ); - return circleBreakouts( aWidth, via->Shape(), aPermitDiagonal ); + // TODO(JE) padstacks -- computeBreakouts needs to have a layer argument + return circleBreakouts( aWidth, via->Shape( 0 ), aPermitDiagonal ); } case ITEM::SOLID_T: { - const SHAPE* shape = aItem->Shape(); + const SHAPE* shape = aItem->Shape( -1 ); switch( shape->Type() ) { @@ -982,7 +984,7 @@ int OPTIMIZER::smartPadsSingle( LINE* aLine, ITEM* aPad, bool aEnd, int aEndVert for( int p = 1; p <= p_end; p++ ) { // If the line is contained inside the pad, don't optimize - if( solid && solid->Shape() && !solid->Shape()->Collide( + if( solid && solid->Shape( -1 ) && !solid->Shape( -1 )->Collide( SEG( line.CPoint( 0 ), line.CPoint( p ) ), aLine->Width() / 2 ) ) { continue; @@ -1198,7 +1200,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( refLine.Collide( &coupledLine, aNode ) ) + if( refLine.Collide( &coupledLine, aNode, refLine.Layer() ) ) return false; if( aNode->CheckColliding ( &refLine ) ) diff --git a/pcbnew/router/pns_router.cpp b/pcbnew/router/pns_router.cpp index f42d8c633c..ee3cec64fa 100644 --- a/pcbnew/router/pns_router.cpp +++ b/pcbnew/router/pns_router.cpp @@ -774,7 +774,7 @@ bool ROUTER::movePlacing( const VECTOR2I& aP, ITEM* aEndItem ) if( via.HasHole() ) { int holeClearance = GetRuleResolver()->Clearance( via.Hole(), nullptr ); - int annularWidth = std::max( 0, via.Diameter() - via.Drill() ) / 2; + int annularWidth = std::max( 0, via.Diameter( l->Layer() ) - via.Drill() ) / 2; int excessHoleClearance = holeClearance - annularWidth; if( excessHoleClearance > clearance ) diff --git a/pcbnew/router/pns_segment.h b/pcbnew/router/pns_segment.h index 2401a24d54..bfe34d73c1 100644 --- a/pcbnew/router/pns_segment.h +++ b/pcbnew/router/pns_segment.h @@ -72,7 +72,7 @@ public: SEGMENT* Clone() const override; - const SHAPE* Shape() const override + const SHAPE* Shape( int aLayer ) const override { return static_cast( &m_seg ); } diff --git a/pcbnew/router/pns_shove.cpp b/pcbnew/router/pns_shove.cpp index 53bd54bdcf..ecd1b962fb 100644 --- a/pcbnew/router/pns_shove.cpp +++ b/pcbnew/router/pns_shove.cpp @@ -257,8 +257,8 @@ bool SHOVE::shoveLineFromLoneVia( const LINE& aCurLine, const LINE& aObstacleLin HOLE* viaHole = via.Hole(); int holeClearance = getClearance( viaHole, &aObstacleLine ); - if( holeClearance + via.Drill() / 2 > clearance + via.Diameter() / 2 ) - clearance = holeClearance + via.Drill() / 2 - via.Diameter() / 2; + if( holeClearance + via.Drill() / 2 > clearance + via.Diameter( aObstacleLine.Layer() ) / 2 ) + clearance = holeClearance + via.Drill() / 2 - via.Diameter( aObstacleLine.Layer() ) / 2; SHAPE_LINE_CHAIN hull = aCurLine.Via().Hull( clearance, obstacleLineWidth, aCurLine.Layer() ); SHAPE_LINE_CHAIN path_cw; @@ -283,7 +283,7 @@ bool SHOVE::shoveLineFromLoneVia( const LINE& aCurLine, const LINE& aObstacleLin aResultLine.SetShape( shortest ); - if( aResultLine.Collide( &aCurLine, m_currentNode ) ) + if( aResultLine.Collide( &aCurLine, m_currentNode, aResultLine.Layer() ) ) return false; return true; @@ -383,7 +383,7 @@ bool SHOVE::shoveLineToHullSet( const LINE& aCurLine, const LINE& aObstacleLine, continue; } - bool colliding = l.Collide( &aCurLine, m_currentNode ); + bool colliding = l.Collide( &aCurLine, m_currentNode, l.Layer() ); #if 0 if(( aCurLine.Marker() & MK_HEAD ) && !colliding ) @@ -392,7 +392,7 @@ bool SHOVE::shoveLineToHullSet( const LINE& aCurLine, const LINE& aObstacleLine, for( ITEM* item : jtStart->LinkList() ) { - if( item->Collide( &l, m_currentNode ) ) + if( item->Collide( &l, m_currentNode, l.Layer() ) ) colliding = true; } } @@ -494,11 +494,14 @@ bool SHOVE::ShoveObstacleLine( const LINE& aCurLine, const LINE& aObstacleLine, int viaClearance = getClearance( &via, &obstacleLine ); HOLE* viaHole = via.Hole(); int holeClearance = getClearance( viaHole, &obstacleLine ); + int layer = aObstacleLine.Layer(); - if( holeClearance + via.Drill() / 2 > viaClearance + via.Diameter() / 2 ) - viaClearance = holeClearance + via.Drill() / 2 - via.Diameter() / 2; + if( holeClearance + via.Drill() / 2 > viaClearance + via.Diameter( layer ) / 2 ) + { + viaClearance = holeClearance + via.Drill() / 2 - via.Diameter( layer ) / 2; + } - hulls.push_back( aCurLine.Via().Hull( viaClearance, obstacleLineWidth ) ); + hulls.push_back( aCurLine.Via().Hull( viaClearance, obstacleLineWidth, layer ) ); } if (shoveLineToHullSet( aCurLine, obstacleLine, aResultLine, hulls ) ) @@ -688,7 +691,8 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingSolid( LINE& aCurrent, ITEM* aObstacle, OB } } - if( via && via->Collide( aObstacle, m_currentNode ) ) + // TODO(JE) viastacks -- can aObstacle be a via? + if( via && via->Collide( aObstacle, m_currentNode, aObstacle->Layer() ) ) return onCollidingVia( aObstacle, via, aObstacleInfo, aObstacle->Rank() - 1 ); } @@ -757,7 +761,7 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingSolid( LINE& aCurrent, ITEM* aObstacle, OB { LINE lastLine = m_lineStack.front(); - if( lastLine.Collide( &walkaroundLine, m_currentNode ) ) + if( lastLine.Collide( &walkaroundLine, m_currentNode, lastLine.Layer() ) ) { LINE dummy( lastLine ); @@ -1076,10 +1080,12 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia, OB if( aCurrent->OfKind( ITEM::LINE_T ) ) { VIA vtmp ( *aObstacleVia ); + int layer = aCurrent->Layer(); - if( aObstacleInfo.m_maxFanoutWidth > 0 && aObstacleInfo.m_maxFanoutWidth > aObstacleVia->Diameter() ) + if( aObstacleInfo.m_maxFanoutWidth > 0 + && aObstacleInfo.m_maxFanoutWidth > aObstacleVia->Diameter( layer ) ) { - vtmp.SetDiameter( aObstacleInfo.m_maxFanoutWidth ); + vtmp.SetDiameter( layer, aObstacleInfo.m_maxFanoutWidth ); } LINE* currentLine = (LINE*) aCurrent; @@ -1093,7 +1099,7 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia, OB PNS_DBG( Dbg(), AddItem, vtmp.Clone(), LIGHTRED, 100000, wxT( "orig-via" ) ); - lineCollision = vtmp.Shape()->Collide( currentLine->Shape(), + lineCollision = vtmp.Shape( layer )->Collide( currentLine->Shape( -1 ), clearance + currentLine->Width() / 2, &mtvLine ); @@ -1102,14 +1108,24 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia, OB { const VIA& currentVia = currentLine->Via(); int viaClearance = getClearance( ¤tVia, &vtmp ); + VECTOR2I layerMtv; - viaCollision = currentVia.Shape()->Collide( vtmp.Shape(), viaClearance, &mtvVia ); + for( int viaLayer : currentVia.RelevantShapeLayers( &vtmp ) ) + { + viaCollision |= currentVia.Shape( viaLayer )->Collide( vtmp.Shape( viaLayer ), + viaClearance, + &layerMtv ); + + if( layerMtv.SquaredEuclideanNorm() > mtvVia.SquaredEuclideanNorm() ) + mtvVia = layerMtv; + } } } else if( aCurrent->OfKind( ITEM::SOLID_T ) ) { PNS_DBG( Dbg(), Message, wxT("collidee-is-solid" ) ); - solidCollision = aCurrent->Shape()->Collide( aObstacleVia->Shape(), clearance, + // TODO(JE) if this case is real, handle via stacks + solidCollision = aCurrent->Shape( -1 )->Collide( aObstacleVia->Shape( -1 ), clearance, &mtvSolid ); //PNS_DBGN( Dbg(), EndGroup ); @@ -1148,18 +1164,29 @@ SHOVE::SHOVE_STATUS SHOVE::onReverseCollidingVia( LINE& aCurrent, VIA* aObstacle auto p0 = aCurrent.Via().Pos(); auto p1 = aObstacleVia->Pos(); - int dist = (p0 - p1).EuclideanNorm() - aCurrent.Via().Diameter() / 2 - aObstacleVia->Diameter() / 2; + int layer = aCurrent.Layer(); + int dist = (p0 - p1).EuclideanNorm() - aCurrent.Via().Diameter( layer ) / 2 + - aObstacleVia->Diameter( layer ) / 2; int clearance = getClearance( &aCurrent.Via(), aObstacleVia ); - SHAPE_LINE_CHAIN hull = aObstacleVia->Hull( clearance, aCurrent.Width(), aCurrent.Layer() ); + SHAPE_LINE_CHAIN hull = aObstacleVia->Hull( clearance, aCurrent.Width(), layer ); auto epInsideHull = hull.PointInside( p0 ); PNS_DBG( Dbg(), AddShape, &hull, LIGHTYELLOW, 100000, wxT( "obstacle-via-hull" ) ); PNS_DBG( Dbg(), Message, wxString::Format("via2via coll check dist %d cl %d delta %d pi %d\n", dist, clearance, dist - clearance, epInsideHull ? 1 : 0) ); - if( aCurrent.Via().Collide( aObstacleVia, m_currentNode ) ) + bool viaCollision = false; + + for( int viaLayer : aCurrent.Via().RelevantShapeLayers( aObstacleVia ) ) + { + viaCollision |= + aCurrent.Via().Shape( viaLayer )->Collide( aObstacleVia->Shape( viaLayer ), + clearance ); + } + + if( viaCollision ) { return onCollidingVia( &aCurrent, aObstacleVia, aObstacleInfo, aCurrent.Rank() - 1 ); } @@ -1346,6 +1373,8 @@ void SHOVE::popLineStack( ) bool SHOVE::fixupViaCollisions( const LINE* aCurrent, OBSTACLE& obs ) { + int layer = aCurrent->Layer(); + // if the current obstacle is a via, consider also the lines connected to it // if their widths are larger or equal than the via diameter, the shove algorithm // will very likely fail in the subsequent iterations (as our base assumption is track @@ -1376,11 +1405,11 @@ bool SHOVE::fixupViaCollisions( const LINE* aCurrent, OBSTACLE& obs ) obs.m_maxFanoutWidth = 0; - if( maxw > 0 && maxw >= v->Diameter() ) + if( maxw > 0 && maxw >= v->Diameter( layer ) ) { obs.m_maxFanoutWidth = maxw + 1; PNS_DBG( Dbg(), Message, - wxString::Format( "Fixup via: new-w %d via-w %d", maxw, v->Diameter() ) ); + wxString::Format( "Fixup via: new-w %d via-w %d", maxw, v->Diameter( layer ) ) ); return true; } @@ -1393,6 +1422,7 @@ bool SHOVE::fixupViaCollisions( const LINE* aCurrent, OBSTACLE& obs ) return false; const SEGMENT* s = static_cast( obs.m_item ); + int sl = s->Layer(); const JOINT* ja = m_currentNode->FindJoint( s->Seg().A, s ); const JOINT* jb = m_currentNode->FindJoint( s->Seg().B, s ); @@ -1405,14 +1435,14 @@ bool SHOVE::fixupViaCollisions( const LINE* aCurrent, OBSTACLE& obs ) // via diameter is larger than the segment width - cool, the force propagation algo // will be able to deal with it, no need to intervene - if( !v || v->Diameter() > s->Width() ) + if( !v || v->Diameter( sl ) > s->Width() ) continue; VIA vtest( *v ); - vtest.SetDiameter( s->Width() ); + vtest.SetDiameter( sl, s->Width() ); // enlarge the via to the width of the segment - if( vtest.Collide( aCurrent, m_currentNode ) ) + if( vtest.Collide( aCurrent, m_currentNode, aCurrent->Layer() ) ) { // if colliding, drop the segment in the shove iteration loop and force-propagate the via instead obs.m_item = v; @@ -1475,7 +1505,7 @@ SHOVE::SHOVE_STATUS SHOVE::shoveIteration( int aIter ) if( nearest ) { - PNS_DBG( Dbg(), AddShape, nearest->m_item->Shape(), YELLOW, 10000, + PNS_DBG( Dbg(), AddShape, nearest->m_item->Shape( currentLine.Layer() ), YELLOW, 10000, wxString::Format( "nearest %p %s rank %d", nearest->m_item, nearest->m_item->KindStr(), @@ -1521,7 +1551,10 @@ SHOVE::SHOVE_STATUS SHOVE::shoveIteration( int aIter ) { PNS_DBG( Dbg(), BeginGroup, wxString::Format( wxT( "iter %d: reverse-collide-via" ), aIter ), 0 ); - if( currentLine.EndsWithVia() && nearest->m_item->Collide( ¤tLine.Via(), m_currentNode ) ) + // TODO(JE) viastacks -- via-via collisions here? + if( currentLine.EndsWithVia() + && nearest->m_item->Collide( ¤tLine.Via(), m_currentNode, + nearest->m_item->Layer() ) ) { PNS_DBG( Dbg(), AddItem, nearest->m_item, YELLOW, 100000, wxT("v2v nearesti" ) ); //PNS_DBG( Dbg(), AddItem, nearest->m_head,RED, 100000, wxString::Format("v2v nearesth force=%d,%d" ) ); @@ -1551,7 +1584,8 @@ SHOVE::SHOVE_STATUS SHOVE::shoveIteration( int aIter ) popLineStack(); - if( currentLine.EndsWithVia() && currentLine.Via().Collide( (SEGMENT*) ni, m_currentNode ) ) + if( currentLine.EndsWithVia() + && currentLine.Via().Collide( (SEGMENT*) ni, m_currentNode, currentLine.Layer() ) ) { VIA_HANDLE vh; vh.layers = currentLine.Via().Layers(); diff --git a/pcbnew/router/pns_solid.h b/pcbnew/router/pns_solid.h index 988c08387f..a080d212e3 100644 --- a/pcbnew/router/pns_solid.h +++ b/pcbnew/router/pns_solid.h @@ -73,7 +73,7 @@ public: SetShape( aB.m_shape->Clone() ); if( aB.m_hole ) - SetHole( new PNS::HOLE( aB.m_hole->Shape()->Clone() ) ); + SetHole( new PNS::HOLE( aB.m_hole->Shape( -1 )->Clone() ) ); m_pos = aB.m_pos; m_padToDie = aB.m_padToDie; @@ -89,7 +89,7 @@ public: ITEM* Clone() const override; - const SHAPE* Shape() const override { return m_shape; } + const SHAPE* Shape( int aLayer ) const override { return m_shape; } const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0, diff --git a/pcbnew/router/pns_tool_base.cpp b/pcbnew/router/pns_tool_base.cpp index 303688f84d..ec9f4650cc 100644 --- a/pcbnew/router/pns_tool_base.cpp +++ b/pcbnew/router/pns_tool_base.cpp @@ -167,7 +167,7 @@ ITEM* TOOL_BASE::pickSingleItem( const VECTOR2I& aWhere, NET_HANDLE aNet, int aL { if( item->OfKind( ITEM::VIA_T | ITEM::SOLID_T ) ) { - SEG::ecoord d = ( item->Shape()->Centre() - aWhere ).SquaredEuclideanNorm(); + SEG::ecoord d = ( item->Shape( aLayer )->Centre() - aWhere ).SquaredEuclideanNorm(); if( d < dist[2] ) { @@ -203,7 +203,7 @@ ITEM* TOOL_BASE::pickSingleItem( const VECTOR2I& aWhere, NET_HANDLE aNet, int aL else if( item->OfKind( ITEM::SOLID_T ) && item->IsFreePad() ) { // Allow free pads only when already inside pad - if( item->Shape()->Collide( aWhere ) ) + if( item->Shape( -1 )->Collide( aWhere ) ) { prioritized[0] = item; dist[0] = 0; @@ -500,7 +500,7 @@ const VECTOR2I TOOL_BASE::snapToItem( ITEM* aItem, const VECTOR2I& aP ) else if( aItem->Kind() == ITEM::ARC_T ) { ARC* arc = static_cast( li ); - return m_gridHelper->AlignToArc( aP, *static_cast( arc->Shape() ) ); + return m_gridHelper->AlignToArc( aP, *static_cast( arc->Shape( -1 ) ) ); } break; diff --git a/pcbnew/router/pns_topology.cpp b/pcbnew/router/pns_topology.cpp index 866128f013..880652448f 100644 --- a/pcbnew/router/pns_topology.cpp +++ b/pcbnew/router/pns_topology.cpp @@ -504,7 +504,7 @@ bool TOPOLOGY::AssembleDiffPair( ITEM* aStart, DIFF_PAIR& aPair ) LINKED_ITEM* coupledItem = nullptr; SEG::ecoord minDist_sq = std::numeric_limits::max(); SEG::ecoord minDistTarget_sq = std::numeric_limits::max(); - VECTOR2I targetPoint = aStart->Shape()->Centre(); + VECTOR2I targetPoint = aStart->Shape( -1 )->Centre(); auto findNItem = [&]( ITEM* p_item ) { @@ -552,7 +552,7 @@ bool TOPOLOGY::AssembleDiffPair( ITEM* aStart, DIFF_PAIR& aPair ) if( dist_sq <= minDist_sq ) { - SEG::ecoord distTarget_sq = n_item->Shape()->SquaredDistance( targetPoint ); + SEG::ecoord distTarget_sq = n_item->Shape( -1 )->SquaredDistance( targetPoint ); if( distTarget_sq < minDistTarget_sq ) { minDistTarget_sq = distTarget_sq; @@ -634,7 +634,7 @@ const TOPOLOGY::CLUSTER TOPOLOGY::AssembleCluster( ITEM* aStart, int aLayer, dou pending.push_back( aStart ); - BOX2I clusterBBox = aStart->Shape()->BBox(); + BOX2I clusterBBox = aStart->Shape( aLayer )->BBox(); int64_t initialArea = clusterBBox.GetArea(); while( !pending.empty() ) @@ -665,7 +665,7 @@ const TOPOLOGY::CLUSTER TOPOLOGY::AssembleCluster( ITEM* aStart, int aLayer, dou } else { - clusterBBox.Merge( obs.m_item->Shape()->BBox() ); + clusterBBox.Merge( obs.m_item->Shape( aLayer )->BBox() ); } const int64_t currentArea = clusterBBox.GetArea(); diff --git a/pcbnew/router/pns_via.cpp b/pcbnew/router/pns_via.cpp index 143206cbe8..4a8cb5f7ac 100644 --- a/pcbnew/router/pns_via.cpp +++ b/pcbnew/router/pns_via.cpp @@ -30,25 +30,98 @@ namespace PNS { +int VIA::EffectiveLayer( int aLayer ) const +{ + switch( m_stackMode ) + { + default: + case STACK_MODE::NORMAL: + return ALL_LAYERS; + + case STACK_MODE::FRONT_INNER_BACK: + if( aLayer == m_layers.Start() || aLayer == m_layers.End() ) + return aLayer; + + if( m_layers.Start() + 1 < m_layers.End() ) + return m_layers.Start() + 1; + + return m_layers.Start(); + + case STACK_MODE::CUSTOM: + return m_layers.Overlaps( aLayer ) ? aLayer : m_layers.Start(); + } +} + + +std::vector VIA::UniqueShapeLayers() const +{ + switch( m_stackMode ) + { + default: + case STACK_MODE::NORMAL: + return { ALL_LAYERS }; + + case STACK_MODE::FRONT_INNER_BACK: + return { 0, ALL_LAYERS, m_layers.End() }; + + case STACK_MODE::CUSTOM: + std::vector ret; + + for( int l = m_layers.Start(); l <= m_layers.End(); l++ ) + ret.push_back( l ); + + return ret; + } +} + + +void VIA::SetStackMode( STACK_MODE aStackMode ) +{ + m_stackMode = aStackMode; + + wxASSERT_MSG( m_stackMode != STACK_MODE::FRONT_INNER_BACK || m_layers.Start() == 0, + wxT( "Cannot use FRONT_INNER_BACK with blind/buried vias!" ) ); + + // In theory, it might be good to do some housekeeping on m_diameters and m_shapes here, + // but it's not yet clear if the stack mode needs to be changed after initial creation. +} + + +bool VIA::PadstackMatches( const VIA& aOther ) const +{ + std::vector myLayers = UniqueShapeLayers(); + std::vector otherLayers = aOther.UniqueShapeLayers(); + + if( !std::equal( myLayers.begin(), myLayers.end(), otherLayers.begin() ) ) + return false; + + for( int i : myLayers ) + { + if( Diameter( i ) != aOther.Diameter( i ) ) + return false; + } + + return true; +} + + bool VIA::PushoutForce( NODE* aNode, const ITEM* aOther, VECTOR2I& aForce ) { int clearance = aNode->GetClearance( this, aOther, false ); - VECTOR2I elementForces[4], force; - size_t nf = 0; + VECTOR2I elementForce; - aOther->Shape()->Collide( Shape(), clearance, &elementForces[nf++] ); - - for( size_t i = 0; i < nf; i++ ) + for( int layer : RelevantShapeLayers( aOther ) ) { - if( elementForces[i].SquaredEuclideanNorm() > force.SquaredEuclideanNorm() ) - force = elementForces[i]; + aOther->Shape( layer )->Collide( Shape( layer ), clearance, &elementForce ); + + if( elementForce.SquaredEuclideanNorm() > aForce.SquaredEuclideanNorm() ) + aForce = elementForce; } - aForce = force; - - return ( force != VECTOR2I( 0, 0 ) ); + return ( aForce != VECTOR2I( 0, 0 ) ); } + bool VIA::PushoutForce( NODE* aNode, const VECTOR2I& aDirection, VECTOR2I& aForce, int aCollisionMask, int aMaxIterations ) { @@ -86,7 +159,8 @@ bool VIA::PushoutForce( NODE* aNode, const VECTOR2I& aDirection, VECTOR2I& aForc break; } - const int threshold = Diameter() / 4; // another stupid heuristic. + // TODO(JE) padstacks -- what is the correct logic here? + const int threshold = Diameter( EffectiveLayer( 0 ) ) / 4; // another stupid heuristic. const int forceMag = force.EuclideanNorm(); // We've been through a lot of iterations already and our pushout force is still too big? @@ -143,7 +217,7 @@ bool VIA::PushoutForce( NODE* aNode, const VECTOR2I& aDirection, VECTOR2I& aForc const SHAPE_LINE_CHAIN VIA::Hull( int aClearance, int aWalkaroundThickness, int aLayer ) const { int cl = ( aClearance + aWalkaroundThickness / 2 ); - int width = m_diameter; + int width = Diameter( aLayer ); if( m_hole && !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( this, aLayer ) ) width = m_hole->Radius() * 2; @@ -163,9 +237,13 @@ VIA* VIA::Clone() const v->SetNet( Net() ); v->SetLayers( Layers() ); v->m_pos = m_pos; - v->m_diameter = m_diameter; + v->m_stackMode = m_stackMode; + v->m_diameters = m_diameters; v->m_drill = m_drill; - v->m_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 ); + + for( const auto& [layer, shape] : m_shapes ) + v->m_shapes[layer] = SHAPE_CIRCLE( m_pos, shape.GetRadius() ); + v->SetHole( HOLE::MakeCircularHole( m_pos, m_drill / 2, m_layers ) ); v->m_rank = m_rank; v->m_marker = m_marker; @@ -182,8 +260,14 @@ OPT_BOX2I VIA::ChangedArea( const VIA* aOther ) const { if( aOther->Pos() != Pos() ) { - BOX2I tmp = Shape()->BBox(); - tmp.Merge( aOther->Shape()->BBox() ); + BOX2I tmp; + + for( int layer : UniqueShapeLayers() ) + tmp.Merge( Shape( layer )->BBox() ); + + for( int layer : aOther->UniqueShapeLayers() ) + tmp.Merge( aOther->Shape( layer )->BBox() ); + return tmp; } @@ -206,7 +290,8 @@ const std::string VIA::Format( ) const { std::stringstream ss; ss << ITEM::Format() << " drill " << m_drill << " "; - ss << m_shape.Format( false ); + // TODO(JE) padstacks + ss << Shape( 0 )->Format( false ); return ss.str(); } diff --git a/pcbnew/router/pns_via.h b/pcbnew/router/pns_via.h index 1b5f30bd64..1710d238c5 100644 --- a/pcbnew/router/pns_via.h +++ b/pcbnew/router/pns_via.h @@ -22,6 +22,7 @@ #ifndef __PNS_VIA_H #define __PNS_VIA_H +#include #include #include #include @@ -47,7 +48,7 @@ struct VIA_HANDLE pos( aPos ), layers (aLayers), net (aNet ) {}; - + bool valid = false; VECTOR2I pos; PNS_LAYER_RANGE layers; @@ -57,11 +58,30 @@ struct VIA_HANDLE class VIA : public LINKED_ITEM { public: + enum class STACK_MODE + { + // The via is the same size on every layer + NORMAL, + + // The via can have three different sizes -- note that in this context, front means + // m_layers.Start() and back means m_layers.End(), which does not align with KiCad in the + // case of blind/buried vias. Using this STACK_MODE only makes sense for vias that extend + // through the whole PCB + FRONT_INNER_BACK, + + // The via can have a different size on each layer + CUSTOM + }; + + static constexpr int ALL_LAYERS = 0; + static constexpr int INNER_LAYERS = 1; + VIA() : LINKED_ITEM( VIA_T ), m_hole( nullptr ) { - m_diameter = 2; // Dummy value + m_stackMode = STACK_MODE::NORMAL; + m_diameters[0] = 2; // Dummy value m_drill = 1; // Dummy value m_viaType = VIATYPE::THROUGH; m_isFree = false; @@ -77,9 +97,10 @@ public: SetNet( aNet ); SetLayers( aLayers ); m_pos = aPos; - m_diameter = aDiameter; + m_stackMode = STACK_MODE::NORMAL; + m_diameters[0] = aDiameter; m_drill = aDrill; - m_shape = SHAPE_CIRCLE( aPos, aDiameter / 2 ); + m_shapes[0] = SHAPE_CIRCLE( aPos, aDiameter / 2 ); SetHole( HOLE::MakeCircularHole( m_pos, aDrill / 2, PNS_LAYER_RANGE() ) ); m_viaType = aViaType; m_isFree = false; @@ -93,8 +114,12 @@ public: SetNet( aB.Net() ); SetLayers( aB.Layers() ); m_pos = aB.m_pos; - m_diameter = aB.m_diameter; - m_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 ); + m_stackMode = aB.m_stackMode; + m_diameters = aB.m_diameters; + + for( const auto& [layer, shape] : aB.m_shapes ) + m_shapes[layer] = SHAPE_CIRCLE( m_pos, shape.GetRadius() ); + m_drill = aB.m_drill; SetHole( HOLE::MakeCircularHole( m_pos, m_drill / 2, PNS_LAYER_RANGE() ) ); m_marker = aB.m_marker; @@ -115,8 +140,12 @@ public: SetNet( aB.Net() ); SetLayers( aB.Layers() ); m_pos = aB.m_pos; - m_diameter = aB.m_diameter; - m_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 ); + m_stackMode = aB.m_stackMode; + m_diameters = aB.m_diameters; + + for( const auto& [layer, shape] : aB.m_shapes ) + m_shapes[layer] = SHAPE_CIRCLE( m_pos, shape.GetRadius() ); + m_drill = aB.m_drill; SetHole( HOLE::MakeCircularHole( m_pos, m_drill / 2, PNS_LAYER_RANGE() ) ); m_marker = aB.m_marker; @@ -133,12 +162,24 @@ public: return aItem && VIA_T == aItem->Kind(); } + STACK_MODE StackMode() const { return m_stackMode; } + + void SetStackMode( STACK_MODE aStackMode ); + + int EffectiveLayer( int aLayer ) const; + + std::vector UniqueShapeLayers() const override; + + bool HasUniqueShapeLayers() const override { return true; } + const VECTOR2I& Pos() const { return m_pos; } void SetPos( const VECTOR2I& aPos ) { m_pos = aPos; - m_shape.SetCenter( aPos ); + + for( auto& [layer, shape] : m_shapes ) + shape.SetCenter( aPos ); if( m_hole ) m_hole->SetCenter( aPos ); @@ -147,14 +188,26 @@ public: VIATYPE ViaType() const { return m_viaType; } void SetViaType( VIATYPE aViaType ) { m_viaType = aViaType; } - int Diameter() const { return m_diameter; } - - void SetDiameter( int aDiameter ) + int Diameter( int aLayer ) const { - m_diameter = aDiameter; - m_shape.SetRadius( m_diameter / 2 ); + int layer = EffectiveLayer( aLayer ); + wxCHECK( m_diameters.contains( layer ), m_diameters.begin()->second ); + return m_diameters.at( layer ); } + void SetDiameter( int aLayer, int aDiameter ) + { + int layer = EffectiveLayer( aLayer ); + m_diameters[layer] = aDiameter; + + if( !m_shapes.contains( layer ) ) + m_shapes[layer] = SHAPE_CIRCLE( m_pos, aDiameter / 2 ); + else + m_shapes[layer].SetRadius( aDiameter / 2 ); + } + + bool PadstackMatches( const VIA& aOther ) const; + int Drill() const { return m_drill; } void SetDrill( int aDrill ) @@ -173,7 +226,12 @@ public: bool PushoutForce( NODE* aNode, const ITEM* aOther, VECTOR2I& aForce ); - const SHAPE* Shape() const override { return &m_shape; } + const SHAPE* Shape( int aLayer ) const override + { + int layer = EffectiveLayer( aLayer ); + wxCHECK( m_shapes.contains( layer ), nullptr ); + return &m_shapes.at( layer ); + } VIA* Clone() const override; @@ -212,10 +270,14 @@ public: virtual const std::string Format() const override; private: - int m_diameter; + STACK_MODE m_stackMode; + + /// May contain 1..n diameters depending on m_stackMode + std::map m_diameters; + std::map m_shapes; + int m_drill; VECTOR2I m_pos; - SHAPE_CIRCLE m_shape; VIATYPE m_viaType; bool m_isFree; HOLE* m_hole; diff --git a/pcbnew/router/pns_walkaround.cpp b/pcbnew/router/pns_walkaround.cpp index 9044be6160..9c602a0910 100644 --- a/pcbnew/router/pns_walkaround.cpp +++ b/pcbnew/router/pns_walkaround.cpp @@ -212,7 +212,7 @@ bool WALKAROUND::singleStep() std::optional shortest; std::optional shortest_alt; - + if( st_cw && st_ccw ) { if( !cw_coll && !ccw_coll || ( cw_coll && ccw_coll) ) @@ -232,7 +232,7 @@ bool WALKAROUND::singleStep() shortest = path_cw; else if( !ccw_coll ) shortest = path_ccw; - + } else if( st_ccw ) shortest = path_ccw; @@ -245,7 +245,7 @@ bool WALKAROUND::singleStep() { for( auto& item : m_lastShortestCluster->m_items ) { - if( shortest->Collide( item, m_world) ) + if( shortest->Collide( item, m_world, shortest->Layer() ) ) { anyColliding = true; break; @@ -357,7 +357,7 @@ const WALKAROUND::RESULT WALKAROUND::Route( const LINE& aInitialPath ) if( ln.PointCount() > 0 && ln.CPoint( -1 ) != aInitialPath.CPoint( -1 ) ) { st = ST_ALMOST_DONE; - + } PNS_DBG( Dbg(), Message, wxString::Format( "stat=%d", st ) ); diff --git a/pcbnew/router/router_preview_item.cpp b/pcbnew/router/router_preview_item.cpp index 92178c9de9..e5d1e25f7d 100644 --- a/pcbnew/router/router_preview_item.cpp +++ b/pcbnew/router/router_preview_item.cpp @@ -58,10 +58,11 @@ ROUTER_PREVIEW_ITEM::ROUTER_PREVIEW_ITEM( const PNS::ITEM* aItem, PNS::ROUTER_IF } else if( aItem ) { - m_shape = aItem->Shape()->Clone(); + // TODO(JE) padstacks -- need to know the layer here + m_shape = aItem->Shape( -1 )->Clone(); if( aItem->Hole() ) - m_hole = aItem->Hole()->Shape()->Clone(); + m_hole = aItem->Hole()->Shape( -1 )->Clone(); } m_clearance = -1; @@ -149,6 +150,7 @@ void ROUTER_PREVIEW_ITEM::Update( const PNS::ITEM* aItem ) break; case PNS::ITEM::VIA_T: + { m_originLayer = m_layer = LAYER_VIAS; m_type = PR_SHAPE; m_width = 0; @@ -158,16 +160,30 @@ void ROUTER_PREVIEW_ITEM::Update( const PNS::ITEM* aItem ) delete m_shape; m_shape = nullptr; - if( aItem->Shape() ) - m_shape = aItem->Shape()->Clone(); + auto via = static_cast( aItem ); + int shapeLayer = -1; + int largestDiameter = 0; + + for( int layer : via->UniqueShapeLayers() ) + { + if( via->Diameter( layer ) > largestDiameter ) + { + largestDiameter = via->Diameter( layer ); + shapeLayer = layer; + } + } + + if( aItem->Shape( shapeLayer ) ) + m_shape = aItem->Shape( shapeLayer )->Clone(); delete m_hole; m_hole = nullptr; if( aItem->Hole() ) - m_hole = aItem->Hole()->Shape()->Clone(); + m_hole = aItem->Hole()->Shape( -1 )->Clone(); break; + } case PNS::ITEM::SOLID_T: m_type = PR_SHAPE; diff --git a/pcbnew/router/router_tool.cpp b/pcbnew/router/router_tool.cpp index f34193a379..a0748547f0 100644 --- a/pcbnew/router/router_tool.cpp +++ b/pcbnew/router/router_tool.cpp @@ -2240,9 +2240,11 @@ int ROUTER_TOOL::InlineDrag( const TOOL_EVENT& aEvent ) if( itemsToDrag.Count() >= 1 ) { + int layer = m_iface->GetPNSLayerFromBoardLayer( m_originalActiveLayer ); + for( PNS::ITEM* pitem : itemsToDrag.Items() ) { - if( pitem->Shape()->Collide( p0, 0 ) ) + if( pitem->Shape( layer )->Collide( p0, 0 ) ) { p = snapToItem( pitem, p0 ); m_startItem = pitem; diff --git a/thirdparty/rtree/geometry/rtree.h b/thirdparty/rtree/geometry/rtree.h index 06d6c9a33e..f8479dac56 100644 --- a/thirdparty/rtree/geometry/rtree.h +++ b/thirdparty/rtree/geometry/rtree.h @@ -50,6 +50,7 @@ // NOTE These next few lines may be win32 specific, you may need to modify them to compile on other platform #include +#include #include #include #include