PNS: Support via stacks

This commit is contained in:
Jon Evans 2024-10-19 19:37:07 -04:00
parent be728f7800
commit d0b2334ceb
35 changed files with 507 additions and 179 deletions

View File

@ -44,9 +44,9 @@
* @return a SHAPE* object equivalent to object. * @return a SHAPE* object equivalent to object.
*/ */
template <class T> template <class T>
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. * @return a BOX2I object containing the bounding box of the T object.
*/ */
template <class T> template <class T>
BOX2I boundingBox( T aObject ) BOX2I boundingBox( T aObject, int aLayer )
{ {
BOX2I bbox = shapeFunctor( aObject )->BBox(); BOX2I bbox = shapeFunctor( aObject, aLayer )->BBox();
return bbox; return bbox;
} }
@ -89,13 +89,14 @@ void acceptVisitor( T aObject, V aVisitor )
* *
* @param aObject is a generic T object. * @param aObject is a generic T object.
* @param aAnotherObject is a generic U object. * @param aAnotherObject is a generic U object.
* @param aLayer is the layer to test
* @param aMinDistance is the minimum collision distance. * @param aMinDistance is the minimum collision distance.
* @return true if object and anotherObject collide. * @return true if object and anotherObject collide.
*/ */
template <class T, class U> template <class T, class U>
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 <class T, class V> template <class T, class V>
@ -197,7 +198,7 @@ class SHAPE_INDEX
} }
}; };
SHAPE_INDEX(); explicit SHAPE_INDEX( int aLayer );
~SHAPE_INDEX(); ~SHAPE_INDEX();
@ -281,6 +282,7 @@ class SHAPE_INDEX
private: private:
RTree<T, int, 2, double>* m_tree; RTree<T, int, 2, double>* m_tree;
int m_shapeLayer;
}; };
/* /*
@ -288,9 +290,10 @@ class SHAPE_INDEX
*/ */
template <class T> template <class T>
SHAPE_INDEX<T>::SHAPE_INDEX() SHAPE_INDEX<T>::SHAPE_INDEX( int aLayer )
{ {
this->m_tree = new RTree<T, int, 2, double>(); this->m_tree = new RTree<T, int, 2, double>();
this->m_shapeLayer = aLayer;
} }
template <class T> template <class T>
@ -311,7 +314,7 @@ void SHAPE_INDEX<T>::Add( T aShape, const BOX2I& aBbox )
template <class T> template <class T>
void SHAPE_INDEX<T>::Add( T aShape ) void SHAPE_INDEX<T>::Add( T aShape )
{ {
BOX2I box = boundingBox( aShape ); BOX2I box = boundingBox( aShape, this->m_shapeLayer );
int min[2] = { box.GetX(), box.GetY() }; int min[2] = { box.GetX(), box.GetY() };
int max[2] = { box.GetRight(), box.GetBottom() }; int max[2] = { box.GetRight(), box.GetBottom() };
@ -321,7 +324,7 @@ void SHAPE_INDEX<T>::Add( T aShape )
template <class T> template <class T>
void SHAPE_INDEX<T>::Remove( T aShape ) void SHAPE_INDEX<T>::Remove( T aShape )
{ {
BOX2I box = boundingBox( aShape ); BOX2I box = boundingBox( aShape, this->m_shapeLayer );
int min[2] = { box.GetX(), box.GetY() }; int min[2] = { box.GetX(), box.GetY() };
int max[2] = { box.GetRight(), box.GetBottom() }; int max[2] = { box.GetRight(), box.GetBottom() };
@ -345,7 +348,7 @@ void SHAPE_INDEX<T>::Reindex()
while( !iter.IsNull() ) while( !iter.IsNull() )
{ {
T shape = *iter; T shape = *iter;
BOX2I box = boundingBox( shape ); BOX2I box = boundingBox( shape, this->m_shapeLayer );
int min[2] = { box.GetX(), box.GetY() }; int min[2] = { box.GetX(), box.GetY() };
int max[2] = { box.GetRight(), box.GetBottom() }; int max[2] = { box.GetRight(), box.GetBottom() };
newTree->Insert( min, max, shape ); newTree->Insert( min, max, shape );

View File

@ -35,7 +35,7 @@
template <class T> template <class T>
const SHAPE* defaultShapeFunctor( const T aItem ) const SHAPE* defaultShapeFunctor( const T aItem )
{ {
return aItem->Shape(); return aItem->Shape( -1 );
} }
template <class T, const SHAPE* (ShapeFunctor) (const T) = defaultShapeFunctor<T> > template <class T, const SHAPE* (ShapeFunctor) (const T) = defaultShapeFunctor<T> >

View File

@ -952,7 +952,7 @@ static PNS::LINKED_ITEM* pickSegment( PNS::ROUTER* aRouter, const VECTOR2I& aWhe
if( aBaseline.PointCount() > 0 ) if( aBaseline.PointCount() > 0 )
{ {
SEG::ecoord dcBaseline; SEG::ecoord dcBaseline;
VECTOR2I target = segm->Shape()->Centre(); VECTOR2I target = segm->Shape( -1 )->Centre();
if( aBaseline.SegmentCount() > 0 ) if( aBaseline.SegmentCount() > 0 )
dcBaseline = aBaseline.SquaredDistance( target ); dcBaseline = aBaseline.SquaredDistance( target );

View File

@ -832,12 +832,16 @@ void PADSTACK::ForEachUniqueLayer( const std::function<void( PCB_LAYER_ID )>& aM
break; break;
case MODE::CUSTOM: 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 ); aMethod( layer );
break; break;
} }
} }
}
PCB_LAYER_ID PADSTACK::EffectiveLayerFor( PCB_LAYER_ID aLayer ) const PCB_LAYER_ID PADSTACK::EffectiveLayerFor( PCB_LAYER_ID aLayer ) const

View File

@ -45,8 +45,8 @@ ARC* ARC::Clone() const
OPT_BOX2I ARC::ChangedArea( const ARC* aOther ) const OPT_BOX2I ARC::ChangedArea( const ARC* aOther ) const
{ {
BOX2I tmp = Shape()->BBox(); BOX2I tmp = Shape( -1 )->BBox();
tmp.Merge( aOther->Shape()->BBox() ); tmp.Merge( aOther->Shape( -1 )->BBox() );
return tmp; return tmp;
} }

View File

@ -75,7 +75,7 @@ public:
ARC* Clone() const override; ARC* Clone() const override;
const SHAPE* Shape() const override const SHAPE* Shape( int aLayer ) const override
{ {
return static_cast<const SHAPE*>( &m_arc ); return static_cast<const SHAPE*>( &m_arc );
} }

View File

@ -143,7 +143,7 @@ bool COMPONENT_DRAGGER::Start( const VECTOR2I& aP, ITEM_SET& aPrimitives )
{ {
LINKED_ITEM* li = static_cast<LINKED_ITEM*>( extraJoint->LinkList().front() ); LINKED_ITEM* li = static_cast<LINKED_ITEM*>( 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() ); addLinked( solid, extraJoint, li, extraJoint->Pos() - solid->Pos() );
} }
} }

View File

@ -441,7 +441,8 @@ void DP_GATEWAYS::BuildFromPrimitivePair( const DP_PRIMITIVE_PAIR& aPair, bool a
p0_p = aPair.AnchorP(); p0_p = aPair.AnchorP();
p0_n = aPair.AnchorN(); 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 ) else if( aPair.PrimP()->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T )
&& aPair.PrimN()->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) ) && aPair.PrimN()->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )

View File

@ -413,8 +413,8 @@ public:
void SetViaDiameter( int aDiameter ) void SetViaDiameter( int aDiameter )
{ {
m_via_p.SetDiameter( aDiameter ); m_via_p.SetDiameter( VIA::ALL_LAYERS, aDiameter );
m_via_n.SetDiameter( aDiameter ); m_via_n.SetDiameter( VIA::ALL_LAYERS, aDiameter );
} }
void SetViaDrill( int aDrill ) void SetViaDrill( int aDrill )

View File

@ -118,12 +118,12 @@ bool DIFF_PAIR_PLACER::propagateDpHeadForces ( const VECTOR2I& aP, VECTOR2I& aNe
if( m_placingVia ) if( m_placingVia )
{ {
virtHead.SetDiameter( viaGap() + 2 * virtHead.Diameter() ); virtHead.SetDiameter( 0, viaGap() + 2 * virtHead.Diameter( 0 ) );
} }
else else
{ {
virtHead.SetLayer( m_currentLayer ); 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; 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 ); 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; collided = true;
totalForce += force; totalForce += force;

View File

@ -66,7 +66,7 @@ public:
int Radius() const; 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; } void SetParentPadVia( ITEM* aParent ) { m_parentPadVia = aParent; }
ITEM* ParentPadVia() const override { return m_parentPadVia; } ITEM* ParentPadVia() const override { return m_parentPadVia; }

View File

@ -31,10 +31,13 @@ void INDEX::Add( ITEM* aItem )
assert( range.Start() != -1 && range.End() != -1 ); assert( range.Start() != -1 && range.End() != -1 );
if( m_subIndices.size() <= static_cast<size_t>( range.End() ) ) if( m_subIndices.size() <= static_cast<size_t>( 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<ITEM_SHAPE_INDEX>( i ) );
}
for( int i = range.Start(); i <= range.End(); ++i ) for( int i = range.Start(); i <= range.End(); ++i )
m_subIndices[i].Add( aItem ); m_subIndices[i]->Add( aItem );
m_allItems.insert( aItem ); m_allItems.insert( aItem );
NET_HANDLE net = aItem->Net(); NET_HANDLE net = aItem->Net();
@ -53,7 +56,7 @@ void INDEX::Remove( ITEM* aItem )
return; return;
for( int i = range.Start(); i <= range.End(); ++i ) for( int i = range.Start(); i <= range.End(); ++i )
m_subIndices[i].Remove( aItem ); m_subIndices[i]->Remove( aItem );
m_allItems.erase( aItem ); m_allItems.erase( aItem );
NET_HANDLE net = aItem->Net(); NET_HANDLE net = aItem->Net();

View File

@ -31,6 +31,7 @@
#include <geometry/shape_index.h> #include <geometry/shape_index.h>
#include "pns_item.h" #include "pns_item.h"
#include "pns_node.h"
namespace PNS { namespace PNS {
@ -122,7 +123,7 @@ private:
int querySingle( std::size_t aIndex, const SHAPE* aShape, int aMinDistance, Visitor& aVisitor ) const; int querySingle( std::size_t aIndex, const SHAPE* aShape, int aMinDistance, Visitor& aVisitor ) const;
private: private:
std::deque<ITEM_SHAPE_INDEX> m_subIndices; std::deque<std::unique_ptr<ITEM_SHAPE_INDEX>> m_subIndices;
std::map<NET_HANDLE, NET_ITEMS_LIST> m_netMap; std::map<NET_HANDLE, NET_ITEMS_LIST> m_netMap;
ITEM_SET m_allItems; 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() ) if( aIndex >= m_subIndices.size() )
return 0; 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<class Visitor> template<class Visitor>
@ -147,7 +149,7 @@ int INDEX::Query( const ITEM* aItem, int aMinDistance, Visitor& aVisitor ) const
const PNS_LAYER_RANGE& layers = aItem->Layers(); const PNS_LAYER_RANGE& layers = aItem->Layers();
for( int i = layers.Start(); i <= layers.End(); ++i ) 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; return total;
} }

View File

@ -55,6 +55,7 @@ static bool shouldWeConsiderHoleCollisions( const ITEM* aItem, const ITEM* aHead
{ {
const ITEM* parentI = holeI->ParentPadVia(); const ITEM* parentI = holeI->ParentPadVia();
const ITEM* parentH = holeH->ParentPadVia(); const ITEM* parentH = holeH->ParentPadVia();
if( !parentH || !parentI ) if( !parentH || !parentI )
return true; 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. // identical and belonging to the same net as non-colliding.
if( parentViaI && parentViaH && parentViaI->Pos() == parentViaH->Pos() if( parentViaI && parentViaH && parentViaI->Pos() == parentViaH->Pos()
&& parentViaI->Diameter() == parentViaH->Diameter() && parentViaI->PadstackMatches( *parentViaH )
&& parentViaI->Net() == parentViaH->Net() && parentViaI->Net() == parentViaH->Net()
&& parentViaI->Drill() == parentViaH->Drill() ) && parentViaI->Drill() == parentViaH->Drill() )
return false; 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<int> ITEM::RelevantShapeLayers( const ITEM* aOther ) const
{
std::vector<int> myLayers = UniqueShapeLayers();
std::vector<int> otherLayers = aOther->UniqueShapeLayers();
if( !HasUniqueShapeLayers() && !aOther->HasUniqueShapeLayers() )
return { -1 };
std::set<int> 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 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 // 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 // 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. // "head", so we do need to handle holeH.
const SHAPE* shapeI = Shape();
int lineWidthI = 0; int lineWidthI = 0;
const SHAPE* shapeH = aHead->Shape(); //const SHAPE* shapeH = aHead->Shape();
const HOLE* holeH = aHead->Hole(); const HOLE* holeH = aHead->Hole();
int lineWidthH = 0; int lineWidthH = 0;
bool collisionsFound = false; bool collisionsFound = false;
@ -118,19 +134,19 @@ bool ITEM::collideSimple( const ITEM* aHead, const NODE* aNode,
if( const auto line = dyn_cast<const LINE*>( this ) ) if( const auto line = dyn_cast<const LINE*>( this ) )
{ {
if( line->EndsWithVia() ) if( line->EndsWithVia() )
collisionsFound |= line->Via().collideSimple( aHead, aNode, aCtx ); collisionsFound |= line->Via().collideSimple( aHead, aNode, aLayer, aCtx );
} }
if( const auto line = dyn_cast<const LINE*>( aHead ) ) if( const auto line = dyn_cast<const LINE*>( aHead ) )
{ {
if( line->EndsWithVia() ) 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. // And a special case for the "head" via's hole.
if( holeH && shouldWeConsiderHoleCollisions( this, holeH ) ) if( holeH && shouldWeConsiderHoleCollisions( this, holeH ) )
{ {
if( Net() != holeH->Net() && collideSimple( holeH, aNode, aCtx ) ) if( Net() != holeH->Net() && collideSimple( holeH, aNode, aLayer, aCtx ) )
collisionsFound = true; collisionsFound = true;
} }
@ -205,6 +221,9 @@ bool ITEM::collideSimple( const ITEM* aHead, const NODE* aNode,
bool checkNetTie = aNode->GetRuleResolver()->IsInNetTie( this ); bool checkNetTie = aNode->GetRuleResolver()->IsInNetTie( this );
const SHAPE* shapeI = Shape( aLayer );
const SHAPE* shapeH = aHead->Shape( aLayer );
if( checkCastellation || checkNetTie ) if( checkCastellation || checkNetTie )
{ {
// Slow method // 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 true;
return false; return false;

View File

@ -155,6 +155,8 @@ public:
* *
* @param aClearance defines how far from the body of the item the hull should be, * @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 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, virtual const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0,
int aLayer = -1 ) const int aLayer = -1 ) const
@ -217,17 +219,32 @@ public:
* @param aOther is the item to check collision against. * @param aOther is the item to check collision against.
* @return true, if a collision was found. * @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; COLLISION_SEARCH_CONTEXT* aCtx = nullptr ) const;
/** /**
* Return the geometrical shape of the item. Used for collision detection and spatial indexing. * 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 nullptr;
} }
/**
* Return a list of layers that have unique (potentially different) shapes
*/
virtual std::vector<int> 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<int> RelevantShapeLayers( const ITEM* aOther ) const;
virtual void Mark( int aMarker ) const { m_marker = aMarker; } virtual void Mark( int aMarker ) const { m_marker = aMarker; }
virtual void Unmark( int aMarker = -1 ) const { m_marker &= ~aMarker; } virtual void Unmark( int aMarker = -1 ) const { m_marker &= ~aMarker; }
virtual int Marker() const { return m_marker; } virtual int Marker() const { return m_marker; }
@ -279,7 +296,7 @@ public:
virtual const NODE* OwningNode() const; virtual const NODE* OwningNode() const;
private: private:
bool collideSimple( const ITEM* aHead, const NODE* aNode, bool collideSimple( const ITEM* aHead, const NODE* aNode, int aLayer,
COLLISION_SEARCH_CONTEXT* aCtx ) const; COLLISION_SEARCH_CONTEXT* aCtx ) const;
protected: protected:

View File

@ -1129,7 +1129,7 @@ PNS_KICAD_IFACE::~PNS_KICAD_IFACE()
std::vector<std::unique_ptr<PNS::SOLID>> PNS_KICAD_IFACE_BASE::syncPad( PAD* aPad ) std::vector<std::unique_ptr<PNS::SOLID>> PNS_KICAD_IFACE_BASE::syncPad( PAD* aPad )
{ {
std::vector<std::unique_ptr<PNS::SOLID>> solids; std::vector<std::unique_ptr<PNS::SOLID>> solids;
PNS_LAYER_RANGE layers( 0, aPad->BoardCopperLayerCount() ); PNS_LAYER_RANGE layers( 0, aPad->BoardCopperLayerCount() - 1 );
LSEQ lmsk = aPad->GetLayerSet().CuStack(); LSEQ lmsk = aPad->GetLayerSet().CuStack();
// ignore non-copper pads except for those with holes // ignore non-copper pads except for those with holes
@ -1174,14 +1174,14 @@ std::vector<std::unique_ptr<PNS::SOLID>> PNS_KICAD_IFACE_BASE::syncPad( PAD* aPa
if( aPad->Padstack().Mode() == PADSTACK::MODE::CUSTOM ) 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 ) else if( aPad->Padstack().Mode() == PADSTACK::MODE::FRONT_INNER_BACK )
{ {
if( aLayer == F_Cu || aLayer == B_Cu ) if( aLayer == F_Cu || aLayer == B_Cu )
solid->SetLayers( GetPNSLayerFromBoardLayer( aLayer ) ); solid->SetLayer( GetPNSLayerFromBoardLayer( aLayer ) );
else else
solid->SetLayers( PNS_LAYER_RANGE( 1, aPad->BoardCopperLayerCount() - 1 ) ); solid->SetLayers( PNS_LAYER_RANGE( 1, aPad->BoardCopperLayerCount() - 2 ) );
} }
else else
{ {
@ -1207,7 +1207,10 @@ std::vector<std::unique_ptr<PNS::SOLID>> PNS_KICAD_IFACE_BASE::syncPad( PAD* aPa
solid->SetOffset( VECTOR2I( offset.x, offset.y ) ); solid->SetOffset( VECTOR2I( offset.x, offset.y ) );
if( aPad->GetDrillSize().x > 0 ) if( aPad->GetDrillSize().x > 0 )
{
solid->SetHole( new PNS::HOLE( aPad->GetEffectiveHoleShape()->Clone() ) ); 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 // 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. // then perform layer-specific flashing tests internally.
@ -1285,22 +1288,64 @@ std::unique_ptr<PNS::ARC> PNS_KICAD_IFACE_BASE::syncArc( PCB_ARC* aArc )
} }
std::vector<std::unique_ptr<PNS::VIA>> PNS_KICAD_IFACE_BASE::syncVia( PCB_VIA* aVia ) std::unique_ptr<PNS::VIA> PNS_KICAD_IFACE_BASE::syncVia( PCB_VIA* aVia )
{ {
std::vector<std::unique_ptr<PNS::VIA>> vias;
PCB_LAYER_ID top, bottom; PCB_LAYER_ID top, bottom;
aVia->LayerPair( &top, &bottom ); aVia->LayerPair( &top, &bottom );
aVia->Padstack().ForEachUniqueLayer( /*
[&]( PCB_LAYER_ID aLayer ) * 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<PNS::VIA>( aVia->GetPosition(), auto via = std::make_unique<PNS::VIA>( aVia->GetPosition(),
SetLayersFromPCBNew( aVia->TopLayer(), aVia->BottomLayer() ), SetLayersFromPCBNew( aVia->TopLayer(), aVia->BottomLayer() ),
aVia->GetWidth( aLayer ), 0,
aVia->GetDrillValue(), aVia->GetDrillValue(),
aVia->GetNet(), aVia->GetNet(),
aVia->GetViaType() ); aVia->GetViaType() );
auto syncDiameter =
[&]( PCB_LAYER_ID aLayer )
{
via->SetDiameter( GetPNSLayerFromBoardLayer( aLayer ), aVia->GetWidth( aLayer ) );
};
switch( aVia->Padstack().Mode() )
{
case PADSTACK::MODE::NORMAL:
via->SetDiameter( 0, aVia->GetWidth( PADSTACK::ALL_LAYERS ) );
break;
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 );
}
break;
case PADSTACK::MODE::CUSTOM:
via->SetStackMode( PNS::VIA::STACK_MODE::CUSTOM );
aVia->Padstack().ForEachUniqueLayer( syncDiameter );
}
via->SetParent( aVia ); via->SetParent( aVia );
if( aVia->IsLocked() ) if( aVia->IsLocked() )
@ -1316,10 +1361,8 @@ std::vector<std::unique_ptr<PNS::VIA>> PNS_KICAD_IFACE_BASE::syncVia( PCB_VIA* a
via->SetHole( PNS::HOLE::MakeCircularHole( aVia->GetPosition(), via->SetHole( PNS::HOLE::MakeCircularHole( aVia->GetPosition(),
aVia->GetDrillValue() / 2, aVia->GetDrillValue() / 2,
SetLayersFromPCBNew( aVia->TopLayer(), aVia->BottomLayer() ) ) ); SetLayersFromPCBNew( aVia->TopLayer(), aVia->BottomLayer() ) ) );
vias.emplace_back( std::move( via ) );
} );
return vias; 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 ) 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 ); solid->SetRoutable( false );
} }
else else
@ -1707,9 +1750,7 @@ void PNS_KICAD_IFACE_BASE::SyncWorld( PNS::NODE *aWorld )
} }
else if( type == PCB_VIA_T ) else if( type == PCB_VIA_T )
{ {
std::vector<std::unique_ptr<PNS::VIA>> vias = syncVia( static_cast<PCB_VIA*>( t ) ); if( std::unique_ptr<PNS::VIA> via = syncVia( static_cast<PCB_VIA*>( t ) ) )
for( std::unique_ptr<PNS::VIA>& via : vias )
aWorld->Add( std::move( via ) ); aWorld->Add( std::move( via ) );
} }
} }
@ -1878,7 +1919,7 @@ void PNS_KICAD_IFACE::HideItem( PNS::ITEM* aItem )
{ {
if( td->IsTeardropArea() if( td->IsTeardropArea()
&& td->GetBoundingBox().Intersects( aItem->Parent()->GetBoundingBox() ) && 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->SetVisible( td, false );
m_view->Update( td, KIGFX::APPEARANCE ); m_view->Update( td, KIGFX::APPEARANCE );
@ -1928,7 +1969,7 @@ void PNS_KICAD_IFACE::modifyBoardItem( PNS::ITEM* aItem )
{ {
PNS::ARC* arc = static_cast<PNS::ARC*>( aItem ); PNS::ARC* arc = static_cast<PNS::ARC*>( aItem );
PCB_ARC* arc_board = static_cast<PCB_ARC*>( board_item ); PCB_ARC* arc_board = static_cast<PCB_ARC*>( board_item );
const SHAPE_ARC* arc_shape = static_cast<const SHAPE_ARC*>( arc->Shape() ); const SHAPE_ARC* arc_shape = static_cast<const SHAPE_ARC*>( arc->Shape( -1 ) );
m_commit->Modify( arc_board ); m_commit->Modify( arc_board );
@ -1961,7 +2002,7 @@ void PNS_KICAD_IFACE::modifyBoardItem( PNS::ITEM* aItem )
m_commit->Modify( via_board ); m_commit->Modify( via_board );
via_board->SetPosition( VECTOR2I( via->Pos().x, via->Pos().y ) ); 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->SetDrill( via->Drill() );
via_board->SetNet( static_cast<NETINFO_ITEM*>( via->Net() ) ); via_board->SetNet( static_cast<NETINFO_ITEM*>( via->Net() ) );
via_board->SetViaType( via->ViaType() ); // MUST be before SetLayerPair() 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: case PNS::ITEM::ARC_T:
{ {
PNS::ARC* arc = static_cast<PNS::ARC*>( aItem ); PNS::ARC* arc = static_cast<PNS::ARC*>( aItem );
PCB_ARC* new_arc = new PCB_ARC( m_board, static_cast<const SHAPE_ARC*>( arc->Shape() ) ); PCB_ARC* new_arc = new PCB_ARC( m_board, static_cast<const SHAPE_ARC*>( arc->Shape( -1 ) ) );
new_arc->SetWidth( arc->Width() ); new_arc->SetWidth( arc->Width() );
new_arc->SetLayer( GetBoardLayerFromPNSLayer( arc->Layers().Start() ) ); new_arc->SetLayer( GetBoardLayerFromPNSLayer( arc->Layers().Start() ) );
new_arc->SetNet( net ); 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 ); PCB_VIA* via_board = new PCB_VIA( m_board );
PNS::VIA* via = static_cast<PNS::VIA*>( aItem ); PNS::VIA* via = static_cast<PNS::VIA*>( aItem );
via_board->SetPosition( VECTOR2I( via->Pos().x, via->Pos().y ) ); 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->SetDrill( via->Drill() );
via_board->SetNet( net ); via_board->SetNet( net );
via_board->SetViaType( via->ViaType() ); // MUST be before SetLayerPair() via_board->SetViaType( via->ViaType() ); // MUST be before SetLayerPair()

View File

@ -107,7 +107,7 @@ protected:
std::vector<std::unique_ptr<PNS::SOLID>> syncPad( PAD* aPad ); std::vector<std::unique_ptr<PNS::SOLID>> syncPad( PAD* aPad );
std::unique_ptr<PNS::SEGMENT> syncTrack( PCB_TRACK* aTrack ); std::unique_ptr<PNS::SEGMENT> syncTrack( PCB_TRACK* aTrack );
std::unique_ptr<PNS::ARC> syncArc( PCB_ARC* aArc ); std::unique_ptr<PNS::ARC> syncArc( PCB_ARC* aArc );
std::vector<std::unique_ptr<PNS::VIA>> syncVia( PCB_VIA* aVia ); std::unique_ptr<PNS::VIA> syncVia( PCB_VIA* aVia );
bool syncTextItem( PNS::NODE* aWorld, PCB_TEXT* aText, PCB_LAYER_ID aLayer ); bool syncTextItem( PNS::NODE* aWorld, PCB_TEXT* aText, PCB_LAYER_ID aLayer );
bool syncGraphicalItem( PNS::NODE* aWorld, PCB_SHAPE* aItem ); bool syncGraphicalItem( PNS::NODE* aWorld, PCB_SHAPE* aItem );
bool syncZone( PNS::NODE* aWorld, ZONE* aZone, SHAPE_POLY_SET* aBoardOutline ); bool syncZone( PNS::NODE* aWorld, ZONE* aZone, SHAPE_POLY_SET* aBoardOutline );

View File

@ -98,7 +98,8 @@ public:
m_blockingObstacle( nullptr ) m_blockingObstacle( nullptr )
{ {
m_via = aVia; 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_net = aVia->Net();
m_layers = aVia->Layers(); m_layers = aVia->Layers();
m_rank = aVia->Rank(); m_rank = aVia->Rank();
@ -130,7 +131,7 @@ public:
} }
///< Return the shape of the line. ///< 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. ///< Modifiable accessor to the underlying shape.
SHAPE_LINE_CHAIN& Line() { return m_line; } SHAPE_LINE_CHAIN& Line() { return m_line; }
@ -197,7 +198,15 @@ public:
VIA& Via() { return *m_via; } VIA& Via() { return *m_via; }
const VIA& Via() const { 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 ); } void SetViaDrill( int aDrill ) { assert(m_via); m_via->SetDrill( aDrill ); }
virtual void Mark( int aMarker ) const override; virtual void Mark( int aMarker ) const override;

View File

@ -825,7 +825,7 @@ bool LINE_PLACER::rhMarkObstacles( const VECTOR2I& aP, LINE& aNewHead, LINE& aNe
if( obs ) if( obs )
{ {
int clearance = m_currentNode->GetClearance( obs->m_item, &m_head, false ); 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; VECTOR2I nearest;
DIRECTION_45::CORNER_MODE cornerMode = Settings().GetCornerMode(); DIRECTION_45::CORNER_MODE cornerMode = Settings().GetCornerMode();

View File

@ -302,7 +302,7 @@ bool clipToOtherLine( NODE* aNode, const LINE& aRef, LINE& aClipped )
//PNS_DBG( dbg, 3int, pclip, WHITE, 500000, wxT("")); //PNS_DBG( dbg, 3int, pclip, WHITE, 500000, wxT(""));
if( l.Collide( &aRef, aNode, &ctx ) ) if( l.Collide( &aRef, aNode, l.Layer(), &ctx ) )
{ {
didClip = true; didClip = true;
curL -= step; curL -= step;

View File

@ -241,7 +241,7 @@ struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR
if( visit( aCandidate ) ) if( visit( aCandidate ) )
return true; 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; return true;
if( m_ctx->options.m_limitCount > 0 && m_ctx->obstacles.size() >= m_ctx->options.m_limitCount ) 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(); const VIA& via = aLine->Via();
int viaClearance = GetClearance( obstacle.m_item, &via, aOpts.m_useClearanceEpsilon ) 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 ); obstacleHull = obstacle.m_item->Hull( viaClearance, 0, layer );
@ -478,7 +478,8 @@ struct HIT_VISITOR : public OBSTACLE_VISITOR
int cl = 0; 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 ); m_items.Add( aItem );
return true; return true;
@ -1088,7 +1089,7 @@ const LINE NODE::AssembleLine( LINKED_ITEM* aSeg, int* aOriginSegmentIndex,
if( li->Kind() == ITEM::ARC_T ) if( li->Kind() == ITEM::ARC_T )
{ {
const ARC* arc = static_cast<const ARC*>( li ); const ARC* arc = static_cast<const ARC*>( li );
const SHAPE_ARC* sa = static_cast<const SHAPE_ARC*>( arc->Shape() ); const SHAPE_ARC* sa = static_cast<const SHAPE_ARC*>( arc->Shape( -1 ) );
int nSegs = line.PointCount(); int nSegs = line.PointCount();
VECTOR2I last = nSegs ? line.CPoint( -1 ) : VECTOR2I(); VECTOR2I last = nSegs ? line.CPoint( -1 ) : VECTOR2I();

View File

@ -117,6 +117,7 @@ struct COLLISION_SEARCH_OPTIONS
int m_kindMask = -1; int m_kindMask = -1;
bool m_useClearanceEpsilon = true; bool m_useClearanceEpsilon = true;
std::function<bool(const ITEM*)> m_filter = nullptr; std::function<bool(const ITEM*)> m_filter = nullptr;
int m_layer = -1;
}; };
@ -182,6 +183,9 @@ public:
void SetWorld( const NODE* aNode, const NODE* aOverride = nullptr ); 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; virtual bool operator()( ITEM* aCandidate ) = 0;
protected: protected:
@ -192,6 +196,26 @@ protected:
const NODE* m_node; ///< node we are searching in (either root or a branch) const NODE* m_node; ///< node we are searching in (either root or a branch)
const NODE* m_override; ///< node that overrides root entries const NODE* m_override; ///< node that overrides root entries
std::optional<int> 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;
}; };
/** /**

View File

@ -141,7 +141,8 @@ struct OPTIMIZER::CACHE_VISITOR
if( !( m_mask & aOtherItem->Kind() ) ) if( !( m_mask & aOtherItem->Kind() ) )
return true; return true;
if( !aOtherItem->Collide( m_ourItem, m_node ) ) // TODO(JE) viastacks
if( !aOtherItem->Collide( m_ourItem, m_node, m_ourItem->Layer() ) )
return true; return true;
m_collidingItem = aOtherItem; m_collidingItem = aOtherItem;
@ -789,7 +790,7 @@ OPTIMIZER::BREAKOUT_LIST OPTIMIZER::customBreakouts( int aWidth, const ITEM* aIt
bool aPermitDiagonal ) const bool aPermitDiagonal ) const
{ {
BREAKOUT_LIST breakouts; BREAKOUT_LIST breakouts;
const SHAPE_SIMPLE* convex = static_cast<const SHAPE_SIMPLE*>( aItem->Shape() ); const SHAPE_SIMPLE* convex = static_cast<const SHAPE_SIMPLE*>( aItem->Shape( -1 ) );
BOX2I bbox = convex->BBox( 0 ); BOX2I bbox = convex->BBox( 0 );
VECTOR2I p0 = static_cast<const SOLID*>( aItem )->Pos(); VECTOR2I p0 = static_cast<const SOLID*>( aItem )->Pos();
@ -896,12 +897,13 @@ OPTIMIZER::BREAKOUT_LIST OPTIMIZER::computeBreakouts( int aWidth, const ITEM* aI
case ITEM::VIA_T: case ITEM::VIA_T:
{ {
const VIA* via = static_cast<const VIA*>( aItem ); const VIA* via = static_cast<const VIA*>( 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: case ITEM::SOLID_T:
{ {
const SHAPE* shape = aItem->Shape(); const SHAPE* shape = aItem->Shape( -1 );
switch( shape->Type() ) 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++ ) for( int p = 1; p <= p_end; p++ )
{ {
// If the line is contained inside the pad, don't optimize // 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 ) ) SEG( line.CPoint( 0 ), line.CPoint( p ) ), aLine->Width() / 2 ) )
{ {
continue; 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 refLine ( aRefIsP ? aPair->PLine() : aPair->NLine(), aNewRef );
LINE coupledLine ( aRefIsP ? aPair->NLine() : aPair->PLine(), aNewCoupled ); LINE coupledLine ( aRefIsP ? aPair->NLine() : aPair->PLine(), aNewCoupled );
if( refLine.Collide( &coupledLine, aNode ) ) if( refLine.Collide( &coupledLine, aNode, refLine.Layer() ) )
return false; return false;
if( aNode->CheckColliding ( &refLine ) ) if( aNode->CheckColliding ( &refLine ) )

View File

@ -774,7 +774,7 @@ bool ROUTER::movePlacing( const VECTOR2I& aP, ITEM* aEndItem )
if( via.HasHole() ) if( via.HasHole() )
{ {
int holeClearance = GetRuleResolver()->Clearance( via.Hole(), nullptr ); 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; int excessHoleClearance = holeClearance - annularWidth;
if( excessHoleClearance > clearance ) if( excessHoleClearance > clearance )

View File

@ -72,7 +72,7 @@ public:
SEGMENT* Clone() const override; SEGMENT* Clone() const override;
const SHAPE* Shape() const override const SHAPE* Shape( int aLayer ) const override
{ {
return static_cast<const SHAPE*>( &m_seg ); return static_cast<const SHAPE*>( &m_seg );
} }

View File

@ -257,8 +257,8 @@ bool SHOVE::shoveLineFromLoneVia( const LINE& aCurLine, const LINE& aObstacleLin
HOLE* viaHole = via.Hole(); HOLE* viaHole = via.Hole();
int holeClearance = getClearance( viaHole, &aObstacleLine ); int holeClearance = getClearance( viaHole, &aObstacleLine );
if( holeClearance + via.Drill() / 2 > clearance + via.Diameter() / 2 ) if( holeClearance + via.Drill() / 2 > clearance + via.Diameter( aObstacleLine.Layer() ) / 2 )
clearance = holeClearance + via.Drill() / 2 - via.Diameter() / 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 hull = aCurLine.Via().Hull( clearance, obstacleLineWidth, aCurLine.Layer() );
SHAPE_LINE_CHAIN path_cw; SHAPE_LINE_CHAIN path_cw;
@ -283,7 +283,7 @@ bool SHOVE::shoveLineFromLoneVia( const LINE& aCurLine, const LINE& aObstacleLin
aResultLine.SetShape( shortest ); aResultLine.SetShape( shortest );
if( aResultLine.Collide( &aCurLine, m_currentNode ) ) if( aResultLine.Collide( &aCurLine, m_currentNode, aResultLine.Layer() ) )
return false; return false;
return true; return true;
@ -383,7 +383,7 @@ bool SHOVE::shoveLineToHullSet( const LINE& aCurLine, const LINE& aObstacleLine,
continue; continue;
} }
bool colliding = l.Collide( &aCurLine, m_currentNode ); bool colliding = l.Collide( &aCurLine, m_currentNode, l.Layer() );
#if 0 #if 0
if(( aCurLine.Marker() & MK_HEAD ) && !colliding ) if(( aCurLine.Marker() & MK_HEAD ) && !colliding )
@ -392,7 +392,7 @@ bool SHOVE::shoveLineToHullSet( const LINE& aCurLine, const LINE& aObstacleLine,
for( ITEM* item : jtStart->LinkList() ) for( ITEM* item : jtStart->LinkList() )
{ {
if( item->Collide( &l, m_currentNode ) ) if( item->Collide( &l, m_currentNode, l.Layer() ) )
colliding = true; colliding = true;
} }
} }
@ -494,11 +494,14 @@ bool SHOVE::ShoveObstacleLine( const LINE& aCurLine, const LINE& aObstacleLine,
int viaClearance = getClearance( &via, &obstacleLine ); int viaClearance = getClearance( &via, &obstacleLine );
HOLE* viaHole = via.Hole(); HOLE* viaHole = via.Hole();
int holeClearance = getClearance( viaHole, &obstacleLine ); int holeClearance = getClearance( viaHole, &obstacleLine );
int layer = aObstacleLine.Layer();
if( holeClearance + via.Drill() / 2 > viaClearance + via.Diameter() / 2 ) if( holeClearance + via.Drill() / 2 > viaClearance + via.Diameter( layer ) / 2 )
viaClearance = holeClearance + via.Drill() / 2 - via.Diameter() / 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 ) ) 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 ); 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(); LINE lastLine = m_lineStack.front();
if( lastLine.Collide( &walkaroundLine, m_currentNode ) ) if( lastLine.Collide( &walkaroundLine, m_currentNode, lastLine.Layer() ) )
{ {
LINE dummy( lastLine ); LINE dummy( lastLine );
@ -1076,10 +1080,12 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia, OB
if( aCurrent->OfKind( ITEM::LINE_T ) ) if( aCurrent->OfKind( ITEM::LINE_T ) )
{ {
VIA vtmp ( *aObstacleVia ); 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; 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" ) ); 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, clearance + currentLine->Width() / 2,
&mtvLine ); &mtvLine );
@ -1102,14 +1108,24 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia, OB
{ {
const VIA& currentVia = currentLine->Via(); const VIA& currentVia = currentLine->Via();
int viaClearance = getClearance( &currentVia, &vtmp ); int viaClearance = getClearance( &currentVia, &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 ) ) else if( aCurrent->OfKind( ITEM::SOLID_T ) )
{ {
PNS_DBG( Dbg(), Message, wxT("collidee-is-solid" ) ); 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 ); &mtvSolid );
//PNS_DBGN( Dbg(), EndGroup ); //PNS_DBGN( Dbg(), EndGroup );
@ -1148,18 +1164,29 @@ SHOVE::SHOVE_STATUS SHOVE::onReverseCollidingVia( LINE& aCurrent, VIA* aObstacle
auto p0 = aCurrent.Via().Pos(); auto p0 = aCurrent.Via().Pos();
auto p1 = aObstacleVia->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 ); 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 ); auto epInsideHull = hull.PointInside( p0 );
PNS_DBG( Dbg(), AddShape, &hull, LIGHTYELLOW, 100000, wxT( "obstacle-via-hull" ) ); 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) ); 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 ); return onCollidingVia( &aCurrent, aObstacleVia, aObstacleInfo, aCurrent.Rank() - 1 );
} }
@ -1346,6 +1373,8 @@ void SHOVE::popLineStack( )
bool SHOVE::fixupViaCollisions( const LINE* aCurrent, OBSTACLE& obs ) 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 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 // 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 // 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; obs.m_maxFanoutWidth = 0;
if( maxw > 0 && maxw >= v->Diameter() ) if( maxw > 0 && maxw >= v->Diameter( layer ) )
{ {
obs.m_maxFanoutWidth = maxw + 1; obs.m_maxFanoutWidth = maxw + 1;
PNS_DBG( Dbg(), Message, 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; return true;
} }
@ -1393,6 +1422,7 @@ bool SHOVE::fixupViaCollisions( const LINE* aCurrent, OBSTACLE& obs )
return false; return false;
const SEGMENT* s = static_cast<const SEGMENT*>( obs.m_item ); const SEGMENT* s = static_cast<const SEGMENT*>( obs.m_item );
int sl = s->Layer();
const JOINT* ja = m_currentNode->FindJoint( s->Seg().A, s ); const JOINT* ja = m_currentNode->FindJoint( s->Seg().A, s );
const JOINT* jb = m_currentNode->FindJoint( s->Seg().B, 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 // via diameter is larger than the segment width - cool, the force propagation algo
// will be able to deal with it, no need to intervene // 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; continue;
VIA vtest( *v ); VIA vtest( *v );
vtest.SetDiameter( s->Width() ); vtest.SetDiameter( sl, s->Width() );
// enlarge the via to the width of the segment // 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 // if colliding, drop the segment in the shove iteration loop and force-propagate the via instead
obs.m_item = v; obs.m_item = v;
@ -1475,7 +1505,7 @@ SHOVE::SHOVE_STATUS SHOVE::shoveIteration( int aIter )
if( nearest ) 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", wxString::Format( "nearest %p %s rank %d",
nearest->m_item, nearest->m_item,
nearest->m_item->KindStr(), 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 ); PNS_DBG( Dbg(), BeginGroup, wxString::Format( wxT( "iter %d: reverse-collide-via" ), aIter ), 0 );
if( currentLine.EndsWithVia() && nearest->m_item->Collide( &currentLine.Via(), m_currentNode ) ) // TODO(JE) viastacks -- via-via collisions here?
if( currentLine.EndsWithVia()
&& nearest->m_item->Collide( &currentLine.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_item, YELLOW, 100000, wxT("v2v nearesti" ) );
//PNS_DBG( Dbg(), AddItem, nearest->m_head,RED, 100000, wxString::Format("v2v nearesth force=%d,%d" ) ); //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(); 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; VIA_HANDLE vh;
vh.layers = currentLine.Via().Layers(); vh.layers = currentLine.Via().Layers();

View File

@ -73,7 +73,7 @@ public:
SetShape( aB.m_shape->Clone() ); SetShape( aB.m_shape->Clone() );
if( aB.m_hole ) 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_pos = aB.m_pos;
m_padToDie = aB.m_padToDie; m_padToDie = aB.m_padToDie;
@ -89,7 +89,7 @@ public:
ITEM* Clone() const override; 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, const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0,

View File

@ -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 ) ) 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] ) 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() ) else if( item->OfKind( ITEM::SOLID_T ) && item->IsFreePad() )
{ {
// Allow free pads only when already inside pad // Allow free pads only when already inside pad
if( item->Shape()->Collide( aWhere ) ) if( item->Shape( -1 )->Collide( aWhere ) )
{ {
prioritized[0] = item; prioritized[0] = item;
dist[0] = 0; dist[0] = 0;
@ -500,7 +500,7 @@ const VECTOR2I TOOL_BASE::snapToItem( ITEM* aItem, const VECTOR2I& aP )
else if( aItem->Kind() == ITEM::ARC_T ) else if( aItem->Kind() == ITEM::ARC_T )
{ {
ARC* arc = static_cast<ARC*>( li ); ARC* arc = static_cast<ARC*>( li );
return m_gridHelper->AlignToArc( aP, *static_cast<const SHAPE_ARC*>( arc->Shape() ) ); return m_gridHelper->AlignToArc( aP, *static_cast<const SHAPE_ARC*>( arc->Shape( -1 ) ) );
} }
break; break;

View File

@ -504,7 +504,7 @@ bool TOPOLOGY::AssembleDiffPair( ITEM* aStart, DIFF_PAIR& aPair )
LINKED_ITEM* coupledItem = nullptr; LINKED_ITEM* coupledItem = nullptr;
SEG::ecoord minDist_sq = std::numeric_limits<SEG::ecoord>::max(); SEG::ecoord minDist_sq = std::numeric_limits<SEG::ecoord>::max();
SEG::ecoord minDistTarget_sq = std::numeric_limits<SEG::ecoord>::max(); SEG::ecoord minDistTarget_sq = std::numeric_limits<SEG::ecoord>::max();
VECTOR2I targetPoint = aStart->Shape()->Centre(); VECTOR2I targetPoint = aStart->Shape( -1 )->Centre();
auto findNItem = [&]( ITEM* p_item ) auto findNItem = [&]( ITEM* p_item )
{ {
@ -552,7 +552,7 @@ bool TOPOLOGY::AssembleDiffPair( ITEM* aStart, DIFF_PAIR& aPair )
if( dist_sq <= minDist_sq ) 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 ) if( distTarget_sq < minDistTarget_sq )
{ {
minDistTarget_sq = distTarget_sq; minDistTarget_sq = distTarget_sq;
@ -634,7 +634,7 @@ const TOPOLOGY::CLUSTER TOPOLOGY::AssembleCluster( ITEM* aStart, int aLayer, dou
pending.push_back( aStart ); pending.push_back( aStart );
BOX2I clusterBBox = aStart->Shape()->BBox(); BOX2I clusterBBox = aStart->Shape( aLayer )->BBox();
int64_t initialArea = clusterBBox.GetArea(); int64_t initialArea = clusterBBox.GetArea();
while( !pending.empty() ) while( !pending.empty() )
@ -665,7 +665,7 @@ const TOPOLOGY::CLUSTER TOPOLOGY::AssembleCluster( ITEM* aStart, int aLayer, dou
} }
else else
{ {
clusterBBox.Merge( obs.m_item->Shape()->BBox() ); clusterBBox.Merge( obs.m_item->Shape( aLayer )->BBox() );
} }
const int64_t currentArea = clusterBBox.GetArea(); const int64_t currentArea = clusterBBox.GetArea();

View File

@ -30,25 +30,98 @@
namespace PNS { 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<int> 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<int> 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<int> myLayers = UniqueShapeLayers();
std::vector<int> 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 ) bool VIA::PushoutForce( NODE* aNode, const ITEM* aOther, VECTOR2I& aForce )
{ {
int clearance = aNode->GetClearance( this, aOther, false ); int clearance = aNode->GetClearance( this, aOther, false );
VECTOR2I elementForces[4], force; VECTOR2I elementForce;
size_t nf = 0;
aOther->Shape()->Collide( Shape(), clearance, &elementForces[nf++] ); for( int layer : RelevantShapeLayers( aOther ) )
for( size_t i = 0; i < nf; i++ )
{ {
if( elementForces[i].SquaredEuclideanNorm() > force.SquaredEuclideanNorm() ) aOther->Shape( layer )->Collide( Shape( layer ), clearance, &elementForce );
force = elementForces[i];
if( elementForce.SquaredEuclideanNorm() > aForce.SquaredEuclideanNorm() )
aForce = elementForce;
} }
aForce = force; return ( aForce != VECTOR2I( 0, 0 ) );
return ( force != VECTOR2I( 0, 0 ) );
} }
bool VIA::PushoutForce( NODE* aNode, const VECTOR2I& aDirection, VECTOR2I& aForce, bool VIA::PushoutForce( NODE* aNode, const VECTOR2I& aDirection, VECTOR2I& aForce,
int aCollisionMask, int aMaxIterations ) int aCollisionMask, int aMaxIterations )
{ {
@ -86,7 +159,8 @@ bool VIA::PushoutForce( NODE* aNode, const VECTOR2I& aDirection, VECTOR2I& aForc
break; 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(); const int forceMag = force.EuclideanNorm();
// We've been through a lot of iterations already and our pushout force is still too big? // 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 const SHAPE_LINE_CHAIN VIA::Hull( int aClearance, int aWalkaroundThickness, int aLayer ) const
{ {
int cl = ( aClearance + aWalkaroundThickness / 2 ); int cl = ( aClearance + aWalkaroundThickness / 2 );
int width = m_diameter; int width = Diameter( aLayer );
if( m_hole && !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( this, aLayer ) ) if( m_hole && !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( this, aLayer ) )
width = m_hole->Radius() * 2; width = m_hole->Radius() * 2;
@ -163,9 +237,13 @@ VIA* VIA::Clone() const
v->SetNet( Net() ); v->SetNet( Net() );
v->SetLayers( Layers() ); v->SetLayers( Layers() );
v->m_pos = m_pos; 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_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->SetHole( HOLE::MakeCircularHole( m_pos, m_drill / 2, m_layers ) );
v->m_rank = m_rank; v->m_rank = m_rank;
v->m_marker = m_marker; v->m_marker = m_marker;
@ -182,8 +260,14 @@ OPT_BOX2I VIA::ChangedArea( const VIA* aOther ) const
{ {
if( aOther->Pos() != Pos() ) if( aOther->Pos() != Pos() )
{ {
BOX2I tmp = Shape()->BBox(); BOX2I tmp;
tmp.Merge( aOther->Shape()->BBox() );
for( int layer : UniqueShapeLayers() )
tmp.Merge( Shape( layer )->BBox() );
for( int layer : aOther->UniqueShapeLayers() )
tmp.Merge( aOther->Shape( layer )->BBox() );
return tmp; return tmp;
} }
@ -206,7 +290,8 @@ const std::string VIA::Format( ) const
{ {
std::stringstream ss; std::stringstream ss;
ss << ITEM::Format() << " drill " << m_drill << " "; ss << ITEM::Format() << " drill " << m_drill << " ";
ss << m_shape.Format( false ); // TODO(JE) padstacks
ss << Shape( 0 )->Format( false );
return ss.str(); return ss.str();
} }

View File

@ -22,6 +22,7 @@
#ifndef __PNS_VIA_H #ifndef __PNS_VIA_H
#define __PNS_VIA_H #define __PNS_VIA_H
#include <geometry/shape_index.h>
#include <geometry/shape_line_chain.h> #include <geometry/shape_line_chain.h>
#include <geometry/shape_circle.h> #include <geometry/shape_circle.h>
#include <math/box2.h> #include <math/box2.h>
@ -57,11 +58,30 @@ struct VIA_HANDLE
class VIA : public LINKED_ITEM class VIA : public LINKED_ITEM
{ {
public: 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() : VIA() :
LINKED_ITEM( VIA_T ), LINKED_ITEM( VIA_T ),
m_hole( nullptr ) 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_drill = 1; // Dummy value
m_viaType = VIATYPE::THROUGH; m_viaType = VIATYPE::THROUGH;
m_isFree = false; m_isFree = false;
@ -77,9 +97,10 @@ public:
SetNet( aNet ); SetNet( aNet );
SetLayers( aLayers ); SetLayers( aLayers );
m_pos = aPos; m_pos = aPos;
m_diameter = aDiameter; m_stackMode = STACK_MODE::NORMAL;
m_diameters[0] = aDiameter;
m_drill = aDrill; 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() ) ); SetHole( HOLE::MakeCircularHole( m_pos, aDrill / 2, PNS_LAYER_RANGE() ) );
m_viaType = aViaType; m_viaType = aViaType;
m_isFree = false; m_isFree = false;
@ -93,8 +114,12 @@ public:
SetNet( aB.Net() ); SetNet( aB.Net() );
SetLayers( aB.Layers() ); SetLayers( aB.Layers() );
m_pos = aB.m_pos; m_pos = aB.m_pos;
m_diameter = aB.m_diameter; m_stackMode = aB.m_stackMode;
m_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 ); 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; m_drill = aB.m_drill;
SetHole( HOLE::MakeCircularHole( m_pos, m_drill / 2, PNS_LAYER_RANGE() ) ); SetHole( HOLE::MakeCircularHole( m_pos, m_drill / 2, PNS_LAYER_RANGE() ) );
m_marker = aB.m_marker; m_marker = aB.m_marker;
@ -115,8 +140,12 @@ public:
SetNet( aB.Net() ); SetNet( aB.Net() );
SetLayers( aB.Layers() ); SetLayers( aB.Layers() );
m_pos = aB.m_pos; m_pos = aB.m_pos;
m_diameter = aB.m_diameter; m_stackMode = aB.m_stackMode;
m_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 ); 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; m_drill = aB.m_drill;
SetHole( HOLE::MakeCircularHole( m_pos, m_drill / 2, PNS_LAYER_RANGE() ) ); SetHole( HOLE::MakeCircularHole( m_pos, m_drill / 2, PNS_LAYER_RANGE() ) );
m_marker = aB.m_marker; m_marker = aB.m_marker;
@ -133,12 +162,24 @@ public:
return aItem && VIA_T == aItem->Kind(); 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<int> UniqueShapeLayers() const override;
bool HasUniqueShapeLayers() const override { return true; }
const VECTOR2I& Pos() const { return m_pos; } const VECTOR2I& Pos() const { return m_pos; }
void SetPos( const VECTOR2I& aPos ) void SetPos( const VECTOR2I& aPos )
{ {
m_pos = aPos; m_pos = aPos;
m_shape.SetCenter( aPos );
for( auto& [layer, shape] : m_shapes )
shape.SetCenter( aPos );
if( m_hole ) if( m_hole )
m_hole->SetCenter( aPos ); m_hole->SetCenter( aPos );
@ -147,14 +188,26 @@ public:
VIATYPE ViaType() const { return m_viaType; } VIATYPE ViaType() const { return m_viaType; }
void SetViaType( VIATYPE aViaType ) { m_viaType = aViaType; } void SetViaType( VIATYPE aViaType ) { m_viaType = aViaType; }
int Diameter() const { return m_diameter; } int Diameter( int aLayer ) const
void SetDiameter( int aDiameter )
{ {
m_diameter = aDiameter; int layer = EffectiveLayer( aLayer );
m_shape.SetRadius( m_diameter / 2 ); 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; } int Drill() const { return m_drill; }
void SetDrill( int aDrill ) void SetDrill( int aDrill )
@ -173,7 +226,12 @@ public:
bool PushoutForce( NODE* aNode, const ITEM* aOther, VECTOR2I& aForce ); 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; VIA* Clone() const override;
@ -212,10 +270,14 @@ public:
virtual const std::string Format() const override; virtual const std::string Format() const override;
private: private:
int m_diameter; STACK_MODE m_stackMode;
/// May contain 1..n diameters depending on m_stackMode
std::map<int, int> m_diameters;
std::map<int, SHAPE_CIRCLE> m_shapes;
int m_drill; int m_drill;
VECTOR2I m_pos; VECTOR2I m_pos;
SHAPE_CIRCLE m_shape;
VIATYPE m_viaType; VIATYPE m_viaType;
bool m_isFree; bool m_isFree;
HOLE* m_hole; HOLE* m_hole;

View File

@ -245,7 +245,7 @@ bool WALKAROUND::singleStep()
{ {
for( auto& item : m_lastShortestCluster->m_items ) for( auto& item : m_lastShortestCluster->m_items )
{ {
if( shortest->Collide( item, m_world) ) if( shortest->Collide( item, m_world, shortest->Layer() ) )
{ {
anyColliding = true; anyColliding = true;
break; break;

View File

@ -58,10 +58,11 @@ ROUTER_PREVIEW_ITEM::ROUTER_PREVIEW_ITEM( const PNS::ITEM* aItem, PNS::ROUTER_IF
} }
else if( aItem ) 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() ) if( aItem->Hole() )
m_hole = aItem->Hole()->Shape()->Clone(); m_hole = aItem->Hole()->Shape( -1 )->Clone();
} }
m_clearance = -1; m_clearance = -1;
@ -149,6 +150,7 @@ void ROUTER_PREVIEW_ITEM::Update( const PNS::ITEM* aItem )
break; break;
case PNS::ITEM::VIA_T: case PNS::ITEM::VIA_T:
{
m_originLayer = m_layer = LAYER_VIAS; m_originLayer = m_layer = LAYER_VIAS;
m_type = PR_SHAPE; m_type = PR_SHAPE;
m_width = 0; m_width = 0;
@ -158,16 +160,30 @@ void ROUTER_PREVIEW_ITEM::Update( const PNS::ITEM* aItem )
delete m_shape; delete m_shape;
m_shape = nullptr; m_shape = nullptr;
if( aItem->Shape() ) auto via = static_cast<const PNS::VIA*>( aItem );
m_shape = aItem->Shape()->Clone(); 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; delete m_hole;
m_hole = nullptr; m_hole = nullptr;
if( aItem->Hole() ) if( aItem->Hole() )
m_hole = aItem->Hole()->Shape()->Clone(); m_hole = aItem->Hole()->Shape( -1 )->Clone();
break; break;
}
case PNS::ITEM::SOLID_T: case PNS::ITEM::SOLID_T:
m_type = PR_SHAPE; m_type = PR_SHAPE;

View File

@ -2240,9 +2240,11 @@ int ROUTER_TOOL::InlineDrag( const TOOL_EVENT& aEvent )
if( itemsToDrag.Count() >= 1 ) if( itemsToDrag.Count() >= 1 )
{ {
int layer = m_iface->GetPNSLayerFromBoardLayer( m_originalActiveLayer );
for( PNS::ITEM* pitem : itemsToDrag.Items() ) for( PNS::ITEM* pitem : itemsToDrag.Items() )
{ {
if( pitem->Shape()->Collide( p0, 0 ) ) if( pitem->Shape( layer )->Collide( p0, 0 ) )
{ {
p = snapToItem( pitem, p0 ); p = snapToItem( pitem, p0 );
m_startItem = pitem; m_startItem = pitem;

View File

@ -50,6 +50,7 @@
// NOTE These next few lines may be win32 specific, you may need to modify them to compile on other platform // NOTE These next few lines may be win32 specific, you may need to modify them to compile on other platform
#include <cassert> #include <cassert>
#include <climits>
#include <cmath> #include <cmath>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>