mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-04-20 00:21:31 +00:00
PNS: Support via stacks
This commit is contained in:
parent
be728f7800
commit
d0b2334ceb
libs/kimath/include/geometry
pcbnew
generators
padstack.cpprouter
pns_arc.cpppns_arc.hpns_component_dragger.cpppns_diff_pair.cpppns_diff_pair.hpns_diff_pair_placer.cpppns_hole.hpns_index.cpppns_index.hpns_item.cpppns_item.hpns_kicad_iface.cpppns_kicad_iface.hpns_line.hpns_line_placer.cpppns_multi_dragger.cpppns_node.cpppns_node.hpns_optimizer.cpppns_router.cpppns_segment.hpns_shove.cpppns_solid.hpns_tool_base.cpppns_topology.cpppns_via.cpppns_via.hpns_walkaround.cpprouter_preview_item.cpprouter_tool.cpp
thirdparty/rtree/geometry
@ -44,9 +44,9 @@
|
||||
* @return a SHAPE* object equivalent to object.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
@ -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 <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>
|
||||
@ -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<T, int, 2, double>* m_tree;
|
||||
int m_shapeLayer;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -288,9 +290,10 @@ class SHAPE_INDEX
|
||||
*/
|
||||
|
||||
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_shapeLayer = aLayer;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
@ -311,7 +314,7 @@ void SHAPE_INDEX<T>::Add( T aShape, const BOX2I& aBbox )
|
||||
template <class T>
|
||||
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 max[2] = { box.GetRight(), box.GetBottom() };
|
||||
|
||||
@ -321,7 +324,7 @@ void SHAPE_INDEX<T>::Add( T aShape )
|
||||
template <class T>
|
||||
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 max[2] = { box.GetRight(), box.GetBottom() };
|
||||
|
||||
@ -345,7 +348,7 @@ void SHAPE_INDEX<T>::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 );
|
||||
|
@ -35,7 +35,7 @@
|
||||
template <class T>
|
||||
const SHAPE* defaultShapeFunctor( const T aItem )
|
||||
{
|
||||
return aItem->Shape();
|
||||
return aItem->Shape( -1 );
|
||||
}
|
||||
|
||||
template <class T, const SHAPE* (ShapeFunctor) (const T) = defaultShapeFunctor<T> >
|
||||
|
@ -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 );
|
||||
|
@ -832,11 +832,15 @@ void PADSTACK::ForEachUniqueLayer( const std::function<void( PCB_LAYER_ID )>& 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ public:
|
||||
|
||||
ARC* Clone() const override;
|
||||
|
||||
const SHAPE* Shape() const override
|
||||
const SHAPE* Shape( int aLayer ) const override
|
||||
{
|
||||
return static_cast<const SHAPE*>( &m_arc );
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ bool COMPONENT_DRAGGER::Start( const VECTOR2I& aP, ITEM_SET& aPrimitives )
|
||||
{
|
||||
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() );
|
||||
}
|
||||
}
|
||||
|
@ -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 ) )
|
||||
|
@ -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 )
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
@ -31,10 +31,13 @@ void INDEX::Add( ITEM* aItem )
|
||||
assert( range.Start() != -1 && range.End() != -1 );
|
||||
|
||||
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 )
|
||||
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();
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <geometry/shape_index.h>
|
||||
|
||||
#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<ITEM_SHAPE_INDEX> m_subIndices;
|
||||
std::deque<std::unique_ptr<ITEM_SHAPE_INDEX>> m_subIndices;
|
||||
std::map<NET_HANDLE, NET_ITEMS_LIST> 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<class Visitor>
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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<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
|
||||
{
|
||||
// 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<const LINE*>( 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<const LINE*>( 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;
|
||||
|
@ -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<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 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:
|
||||
|
@ -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>> 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<std::unique_ptr<PNS::SOLID>> 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<std::unique_ptr<PNS::SOLID>> 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::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;
|
||||
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<PNS::VIA>( 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<PNS::VIA>( 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<PCB_GENERATOR*>( 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<PCB_GENERATOR*>( 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<std::unique_ptr<PNS::VIA>> vias = syncVia( static_cast<PCB_VIA*>( t ) );
|
||||
|
||||
for( std::unique_ptr<PNS::VIA>& via : vias )
|
||||
if( std::unique_ptr<PNS::VIA> via = syncVia( static_cast<PCB_VIA*>( 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<PNS::ARC*>( aItem );
|
||||
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 );
|
||||
|
||||
@ -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<NETINFO_ITEM*>( 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<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->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<PNS::VIA*>( 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()
|
||||
|
@ -107,7 +107,7 @@ protected:
|
||||
std::vector<std::unique_ptr<PNS::SOLID>> syncPad( PAD* aPad );
|
||||
std::unique_ptr<PNS::SEGMENT> syncTrack( PCB_TRACK* aTrack );
|
||||
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 syncGraphicalItem( PNS::NODE* aWorld, PCB_SHAPE* aItem );
|
||||
bool syncZone( PNS::NODE* aWorld, ZONE* aZone, SHAPE_POLY_SET* aBoardOutline );
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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<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();
|
||||
VECTOR2I last = nSegs ? line.CPoint( -1 ) : VECTOR2I();
|
||||
|
@ -117,6 +117,7 @@ struct COLLISION_SEARCH_OPTIONS
|
||||
int m_kindMask = -1;
|
||||
bool m_useClearanceEpsilon = true;
|
||||
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 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<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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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<const SHAPE_SIMPLE*>( aItem->Shape() );
|
||||
const SHAPE_SIMPLE* convex = static_cast<const SHAPE_SIMPLE*>( aItem->Shape( -1 ) );
|
||||
|
||||
BOX2I bbox = convex->BBox( 0 );
|
||||
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:
|
||||
{
|
||||
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:
|
||||
{
|
||||
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 ) )
|
||||
|
@ -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 )
|
||||
|
@ -72,7 +72,7 @@ public:
|
||||
|
||||
SEGMENT* Clone() const override;
|
||||
|
||||
const SHAPE* Shape() const override
|
||||
const SHAPE* Shape( int aLayer ) const override
|
||||
{
|
||||
return static_cast<const SHAPE*>( &m_seg );
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user