router: hole as first class objects, wip
Rebased by Jeff Young <jeff@rokeby.ie> 5 April 2023
(Also includes a bug-fix for highlighting collisions with edge-cut
items.)
(cherry picked from commit bfbda978b8
)
This commit is contained in:
parent
7f593d3999
commit
c1274e24b0
|
@ -49,21 +49,6 @@ static const SHAPE* shapeFunctor( T aItem )
|
|||
return aItem->Shape();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by #SHAPE_INDEX to get a SHAPE* for a hole from another type.
|
||||
*
|
||||
* By default relies on T::GetHole() method, should be specialized if the T object
|
||||
* doesn't allow that method.
|
||||
*
|
||||
* @param aItem generic T object.
|
||||
* @return a SHAPE* object equivalent to object.
|
||||
*/
|
||||
template <class T>
|
||||
static const SHAPE* holeFunctor( T aItem )
|
||||
{
|
||||
return aItem->Hole();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by #SHAPE_INDEX to get the bounding box of a generic T object.
|
||||
*
|
||||
|
@ -78,9 +63,6 @@ BOX2I boundingBox( T aObject )
|
|||
{
|
||||
BOX2I bbox = shapeFunctor( aObject )->BBox();
|
||||
|
||||
if( holeFunctor( aObject ) )
|
||||
bbox.Merge( holeFunctor( aObject )->BBox() );
|
||||
|
||||
return bbox;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2022 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pns_item.h"
|
||||
#include "pns_hole.h"
|
||||
#include "pns_node.h"
|
||||
#include "pns_utils.h"
|
||||
|
||||
#include <geometry/shape_circle.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
#include <geometry/shape_compound.h>
|
||||
#include <geometry/shape_poly_set.h>
|
||||
|
||||
namespace PNS
|
||||
{
|
||||
|
||||
HOLE::~HOLE()
|
||||
{
|
||||
delete m_holeShape;
|
||||
}
|
||||
|
||||
HOLE* HOLE::Clone() const
|
||||
{
|
||||
HOLE* h = new HOLE( nullptr, m_holeShape->Clone() );
|
||||
|
||||
h->SetNet( Net() );
|
||||
h->SetLayers( Layers() );
|
||||
h->SetOwner( nullptr );
|
||||
|
||||
h->m_rank = m_rank;
|
||||
h->m_marker = m_marker;
|
||||
h->m_parent = m_parent;
|
||||
h->m_isVirtual = m_isVirtual;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
const SHAPE_LINE_CHAIN HOLE::Hull( int aClearance, int aWalkaroundThickness, int aLayer ) const
|
||||
{
|
||||
if( !m_holeShape )
|
||||
return SHAPE_LINE_CHAIN();
|
||||
|
||||
if( m_holeShape->Type() == SH_CIRCLE )
|
||||
{
|
||||
auto cir = static_cast<SHAPE_CIRCLE*>( m_holeShape );
|
||||
int cl = ( aClearance + aWalkaroundThickness / 2 );
|
||||
int width = cir->GetRadius() * 2;
|
||||
|
||||
// Chamfer = width * ( 1 - sqrt(2)/2 ) for equilateral octagon
|
||||
return OctagonalHull( cir->GetCenter() - VECTOR2I( width / 2, width / 2 ),
|
||||
VECTOR2I( width, width ), cl,
|
||||
( 2 * cl + width ) * ( 1.0 - M_SQRT1_2 ) );
|
||||
}
|
||||
else if( m_holeShape->Type() == SH_COMPOUND )
|
||||
{
|
||||
SHAPE_COMPOUND* cmpnd = static_cast<SHAPE_COMPOUND*>( m_holeShape );
|
||||
|
||||
if( cmpnd->Shapes().size() == 1 )
|
||||
{
|
||||
return BuildHullForPrimitiveShape( cmpnd->Shapes()[0], aClearance,
|
||||
aWalkaroundThickness );
|
||||
}
|
||||
else
|
||||
{
|
||||
SHAPE_POLY_SET hullSet;
|
||||
|
||||
for( SHAPE* shape : cmpnd->Shapes() )
|
||||
{
|
||||
hullSet.AddOutline(
|
||||
BuildHullForPrimitiveShape( shape, aClearance, aWalkaroundThickness ) );
|
||||
}
|
||||
|
||||
hullSet.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
return hullSet.Outline( 0 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return BuildHullForPrimitiveShape( m_holeShape, aClearance, aWalkaroundThickness );
|
||||
}
|
||||
}
|
||||
|
||||
bool HOLE::IsCircular() const
|
||||
{
|
||||
return m_holeShape->Type() == SH_CIRCLE;
|
||||
}
|
||||
|
||||
int HOLE::Radius() const
|
||||
{
|
||||
assert( m_holeShape->Type() == SH_CIRCLE );
|
||||
|
||||
return static_cast<const SHAPE_CIRCLE*>( m_holeShape )->GetRadius();
|
||||
}
|
||||
|
||||
const VECTOR2I HOLE::Pos() const
|
||||
{
|
||||
return VECTOR2I( 0, 0 ); // fixme holes
|
||||
}
|
||||
|
||||
void HOLE::SetCenter( const VECTOR2I& aCenter )
|
||||
{
|
||||
assert( m_holeShape->Type() == SH_CIRCLE );
|
||||
static_cast<SHAPE_CIRCLE*>( m_holeShape )->SetCenter( aCenter );
|
||||
}
|
||||
|
||||
void HOLE::SetRadius( int aRadius )
|
||||
{
|
||||
assert( m_holeShape->Type() == SH_CIRCLE );
|
||||
static_cast<SHAPE_CIRCLE*>( m_holeShape )->SetRadius( aRadius );
|
||||
}
|
||||
|
||||
void HOLE::Move( const VECTOR2I& delta )
|
||||
{
|
||||
m_holeShape->Move( delta );
|
||||
}
|
||||
|
||||
HOLE* HOLE::MakeCircularHole( const VECTOR2I& pos, int radius )
|
||||
{
|
||||
auto circle = new SHAPE_CIRCLE( pos, radius );
|
||||
auto hole = new HOLE( nullptr, circle );
|
||||
hole->SetLayers( LAYER_RANGE( F_Cu, B_Cu ) );
|
||||
return hole;
|
||||
}
|
||||
|
||||
/*bool HOLE::collideSimple( const ITEM* aHead, const NODE* aNode,
|
||||
COLLISION_SEARCH_CONTEXT* aCtx ) const
|
||||
{
|
||||
}*/
|
||||
|
||||
}; // namespace PNS
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
* KiRouter - a push-and-(sometimes-)shove PCB router
|
||||
*
|
||||
* Copyright (C) 2016-2021 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation, either version 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __PNS_HOLE_H
|
||||
#define __PNS_HOLE_H
|
||||
|
||||
#include "pns_item.h"
|
||||
|
||||
#include <geometry/shape.h>
|
||||
#include <geometry/shape_line_chain.h>
|
||||
|
||||
namespace PNS
|
||||
{
|
||||
|
||||
class HOLE : public ITEM
|
||||
{
|
||||
public:
|
||||
HOLE( ITEM* aParentPadVia, SHAPE* aShape ) :
|
||||
ITEM( ITEM::HOLE_T ),
|
||||
m_holeShape( aShape ),
|
||||
m_parentPadVia( aParentPadVia )
|
||||
{
|
||||
}
|
||||
|
||||
HOLE( const ITEM& aOther ) : ITEM( aOther ) {}
|
||||
|
||||
virtual ~HOLE();
|
||||
|
||||
/**
|
||||
* Return a deep copy of the item.
|
||||
*/
|
||||
virtual HOLE* Clone() const override;
|
||||
|
||||
virtual const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0,
|
||||
int aLayer = -1 ) const override;
|
||||
|
||||
bool IsCircular() const;
|
||||
|
||||
const VECTOR2I Pos() const;
|
||||
int Radius() const;
|
||||
|
||||
const SHAPE* Shape() const override { return m_holeShape; }
|
||||
|
||||
ITEM* ParentPadVia() const { return m_parentPadVia; }
|
||||
|
||||
void SetCenter( const VECTOR2I& aCenter );
|
||||
void SetRadius( int aRadius );
|
||||
|
||||
void Move( const VECTOR2I& delta );
|
||||
|
||||
static HOLE* MakeCircularHole( const VECTOR2I& pos, int radius );
|
||||
|
||||
|
||||
private:
|
||||
SHAPE* m_holeShape;
|
||||
ITEM* m_parentPadVia;
|
||||
};
|
||||
|
||||
}; // namespace PNS
|
||||
|
||||
#endif
|
|
@ -24,41 +24,72 @@
|
|||
#include "pns_item.h"
|
||||
#include "pns_line.h"
|
||||
#include "pns_router.h"
|
||||
#include "pns_utils.h"
|
||||
|
||||
#include <geometry/shape_compound.h>
|
||||
#include <geometry/shape_poly_set.h>
|
||||
|
||||
typedef VECTOR2I::extended_type ecoord;
|
||||
|
||||
namespace PNS {
|
||||
|
||||
bool ITEM::collideSimple( const ITEM* aOther, const NODE* aNode,
|
||||
const COLLISION_SEARCH_OPTIONS& aOpts, OBSTACLE *aObsInfo ) const
|
||||
static void dumpObstacles( const PNS::NODE::OBSTACLES &obstacles )
|
||||
{
|
||||
const ROUTER_IFACE* iface = ROUTER::GetInstance()->GetInterface();
|
||||
const SHAPE* shapeA = Shape();
|
||||
const SHAPE* holeA = Hole();
|
||||
int lineWidthA = 0;
|
||||
const SHAPE* shapeB = aOther->Shape();
|
||||
const SHAPE* holeB = aOther->Hole();
|
||||
int lineWidthB = 0;
|
||||
const int clearanceEpsilon = aNode->GetRuleResolver()->ClearanceEpsilon();
|
||||
printf("&&&& %d obstacles: \n", obstacles.size() );
|
||||
|
||||
for( const auto& obs : obstacles )
|
||||
{
|
||||
printf("%p [%s] - %p [%s], clearance %d\n", obs.m_head, obs.m_head->KindStr().c_str(),
|
||||
obs.m_item, obs.m_item->KindStr().c_str(),
|
||||
obs.m_clearance );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ITEM::collideSimple( const ITEM* aHead, const NODE* aNode,
|
||||
COLLISION_SEARCH_CONTEXT* aCtx ) const
|
||||
{
|
||||
const SHAPE* shapeI = Shape();
|
||||
const HOLE* holeI = Hole();
|
||||
int lineWidthI = 0;
|
||||
|
||||
const SHAPE* shapeH = aHead->Shape();
|
||||
const HOLE* holeH = aHead->Hole();
|
||||
int lineWidthH = 0;
|
||||
int clearanceEpsilon = aNode->GetRuleResolver()->ClearanceEpsilon();
|
||||
bool collisionsFound = false;
|
||||
|
||||
printf("******************** CollideSimple %d\n", aCtx->obstacles.size() );
|
||||
|
||||
//printf( "h %p n %p t %p ctx %p\n", aHead, aNode, this, aCtx );
|
||||
|
||||
if( aHead == this ) // we cannot be self-colliding
|
||||
return false;
|
||||
|
||||
// Sadly collision routines ignore SHAPE_POLY_LINE widths so we have to pass them in as part
|
||||
// of the clearance value.
|
||||
if( m_kind == LINE_T )
|
||||
lineWidthA = static_cast<const LINE*>( this )->Width() / 2;
|
||||
lineWidthI = static_cast<const LINE*>( this )->Width() / 2;
|
||||
|
||||
if( aOther->m_kind == LINE_T )
|
||||
lineWidthB = static_cast<const LINE*>( aOther )->Width() / 2;
|
||||
if( aHead->m_kind == LINE_T )
|
||||
lineWidthH = static_cast<const LINE*>( aHead )->Width() / 2;
|
||||
|
||||
// same nets? no collision!
|
||||
if( aOpts.m_differentNetsOnly && m_net == aOther->m_net && m_net >= 0 && aOther->m_net >= 0 )
|
||||
if( aCtx && aCtx->options.m_differentNetsOnly
|
||||
&& m_net == aHead->m_net && m_net >= 0 && aHead->m_net >= 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// a pad associated with a "free" pin (NIC) doesn't have a net until it has been used
|
||||
if( aOpts.m_differentNetsOnly && ( IsFreePad() || aOther->IsFreePad() ) )
|
||||
if( aCtx && aCtx->options.m_differentNetsOnly
|
||||
&& ( IsFreePad() || aHead->IsFreePad() ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if we are not on completely different layers first
|
||||
if( !m_layers.Overlaps( aOther->m_layers ) )
|
||||
if( !m_layers.Overlaps( aHead->m_layers ) )
|
||||
return false;
|
||||
|
||||
auto checkKeepout =
|
||||
|
@ -84,74 +115,143 @@ bool ITEM::collideSimple( const ITEM* aOther, const NODE* aNode,
|
|||
};
|
||||
|
||||
const ZONE* zoneA = dynamic_cast<ZONE*>( Parent() );
|
||||
const ZONE* zoneB = dynamic_cast<ZONE*>( aOther->Parent() );
|
||||
const ZONE* zoneB = dynamic_cast<ZONE*>( aHead->Parent() );
|
||||
|
||||
if( zoneA && aOther->Parent() && !checkKeepout( zoneA, aOther->Parent() ) )
|
||||
if( zoneA && aHead->Parent() && !checkKeepout( zoneA, aHead->Parent() ) )
|
||||
return false;
|
||||
|
||||
if( zoneB && Parent() && !checkKeepout( zoneB, Parent() ) )
|
||||
return false;
|
||||
|
||||
bool thisNotFlashed = !iface->IsFlashedOnLayer( this, aOther->Layer() );
|
||||
bool otherNotFlashed = !iface->IsFlashedOnLayer( aOther, Layer() );
|
||||
// fixme: this f***ing singleton must go...
|
||||
ROUTER *router = ROUTER::GetInstance();
|
||||
ROUTER_IFACE* iface = router ? router->GetInterface() : nullptr;
|
||||
|
||||
if( aObsInfo )
|
||||
bool thisNotFlashed = false;
|
||||
bool otherNotFlashed = false;
|
||||
|
||||
if( iface )
|
||||
{
|
||||
aObsInfo->m_headIsHole = false;
|
||||
aObsInfo->m_itemIsHole = false;
|
||||
thisNotFlashed = !iface->IsFlashedOnLayer( this, aHead->Layer() );
|
||||
otherNotFlashed = !iface->IsFlashedOnLayer( aHead, Layer() );
|
||||
}
|
||||
|
||||
if( ( aNode->GetCollisionQueryScope() == NODE::CQS_ALL_RULES
|
||||
|| ( thisNotFlashed || otherNotFlashed ) )
|
||||
&& ( holeA || holeB ) )
|
||||
|| ( thisNotFlashed || otherNotFlashed ) ) )
|
||||
{
|
||||
int holeClearance = aNode->GetHoleClearance( this, aOther );
|
||||
|
||||
if( holeA && holeA->Collide( shapeB, holeClearance + lineWidthB - clearanceEpsilon ) )
|
||||
if( holeI && holeI->ParentPadVia() != aHead && holeI != aHead )
|
||||
{
|
||||
if( aObsInfo )
|
||||
{
|
||||
aObsInfo->m_headIsHole = true;
|
||||
aObsInfo->m_clearance = holeClearance;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
int holeClearance = aNode->GetClearance( this, holeI );
|
||||
printf("HCH1 %d\n", holeClearance);
|
||||
|
||||
if( holeB && holeB->Collide( shapeA, holeClearance + lineWidthA - clearanceEpsilon ) )
|
||||
{
|
||||
if( aObsInfo )
|
||||
if( holeI->Shape()->Collide( shapeH, holeClearance + lineWidthH - clearanceEpsilon ) )
|
||||
{
|
||||
aObsInfo->m_itemIsHole = true;
|
||||
aObsInfo->m_clearance = holeClearance;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if( holeA && holeB )
|
||||
{
|
||||
int holeToHoleClearance = aNode->GetHoleToHoleClearance( this, aOther );
|
||||
|
||||
if( holeA->Collide( holeB, holeToHoleClearance - clearanceEpsilon ) )
|
||||
{
|
||||
if( aObsInfo )
|
||||
if( aCtx )
|
||||
{
|
||||
aObsInfo->m_headIsHole = true;
|
||||
aObsInfo->m_itemIsHole = true;
|
||||
aObsInfo->m_clearance = holeToHoleClearance;
|
||||
OBSTACLE obs;
|
||||
obs.m_clearance = holeClearance;
|
||||
obs.m_head = const_cast<ITEM*>( aHead );
|
||||
obs.m_item = const_cast<HOLE*>( holeI );
|
||||
|
||||
aCtx->obstacles.insert( obs );
|
||||
dumpObstacles( aCtx->obstacles );
|
||||
collisionsFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( holeH && holeH->ParentPadVia() != this && holeH != this )
|
||||
{
|
||||
int holeClearance = aNode->GetClearance( this, holeH );
|
||||
|
||||
printf("HCH2 %d\n", holeClearance);
|
||||
|
||||
if( holeH->Shape()->Collide( shapeI, holeClearance + lineWidthI - clearanceEpsilon ) )
|
||||
{
|
||||
if( aCtx )
|
||||
{
|
||||
OBSTACLE obs;
|
||||
obs.m_clearance = holeClearance;
|
||||
obs.m_head = const_cast<HOLE*>( holeH );
|
||||
obs.m_item = const_cast<ITEM*>( this );
|
||||
|
||||
aCtx->obstacles.insert( obs );
|
||||
dumpObstacles( aCtx->obstacles );
|
||||
collisionsFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( holeI && holeH && ( holeI != holeH ) )
|
||||
{
|
||||
int holeClearance = aNode->GetClearance( holeI, holeH );
|
||||
|
||||
printf("HCH3 %d\n", holeClearance);
|
||||
|
||||
if( holeI->Shape()->Collide( holeH->Shape(), holeClearance - clearanceEpsilon ) )
|
||||
{
|
||||
if( aCtx )
|
||||
{
|
||||
OBSTACLE obs;
|
||||
obs.m_clearance = holeClearance;
|
||||
|
||||
// printf("pushh3 %p %p\n", obs.m_head, obs.m_item );
|
||||
|
||||
obs.m_head = const_cast<HOLE*>( holeH );
|
||||
obs.m_item = const_cast<HOLE*>( holeI );
|
||||
|
||||
aCtx->obstacles.insert( obs );
|
||||
dumpObstacles( aCtx->obstacles );
|
||||
|
||||
collisionsFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !aOther->Layers().IsMultilayer() && thisNotFlashed )
|
||||
printf("HCHE\n");
|
||||
|
||||
if( !aHead->Layers().IsMultilayer() && thisNotFlashed )
|
||||
return false;
|
||||
|
||||
if( !Layers().IsMultilayer() && otherNotFlashed )
|
||||
return false;
|
||||
|
||||
int clearance = aOpts.m_overrideClearance >= 0 ? aOpts.m_overrideClearance
|
||||
: aNode->GetClearance( this, aOther );
|
||||
int clearance;
|
||||
|
||||
if( aCtx && aCtx->options.m_overrideClearance >= 0 )
|
||||
{
|
||||
clearance = aCtx->options.m_overrideClearance;
|
||||
}
|
||||
else
|
||||
{
|
||||
clearance = aNode->GetClearance( this, aHead );
|
||||
}
|
||||
|
||||
// prevent bogus collisions between the item and its own hole. FIXME: figure out a cleaner way of doing that
|
||||
if( holeI && aHead == holeI->ParentPadVia() )
|
||||
return false;
|
||||
|
||||
if( holeH && this == holeH->ParentPadVia() )
|
||||
return false;
|
||||
|
||||
if( holeH && this == holeH )
|
||||
return false;
|
||||
|
||||
if( holeI && aHead == holeI )
|
||||
return false;
|
||||
|
||||
if( clearance >= 0 )
|
||||
{
|
||||
|
@ -164,41 +264,69 @@ bool ITEM::collideSimple( const ITEM* aOther, const NODE* aNode,
|
|||
int actual;
|
||||
VECTOR2I pos;
|
||||
|
||||
if( shapeA->Collide( shapeB, clearance + lineWidthA, &actual, &pos ) )
|
||||
if( shapeH->Collide( shapeI, clearance + lineWidthH + lineWidthI - clearanceEpsilon,
|
||||
&actual, &pos ) )
|
||||
{
|
||||
if( checkCastellation && aNode->QueryEdgeExclusions( pos ) )
|
||||
return false;
|
||||
|
||||
if( checkNetTie && aNode->GetRuleResolver()->IsNetTieExclusion( aOther, pos, this ) )
|
||||
if( checkNetTie && aNode->GetRuleResolver()->IsNetTieExclusion( aHead, pos, this ) )
|
||||
return false;
|
||||
|
||||
if( aObsInfo )
|
||||
aObsInfo->m_clearance = clearance;
|
||||
|
||||
return true;
|
||||
if( aCtx )
|
||||
{
|
||||
collisionsFound = true;
|
||||
OBSTACLE obs;
|
||||
obs.m_head = const_cast<ITEM*>( aHead );
|
||||
obs.m_item = const_cast<ITEM*>( this );
|
||||
obs.m_clearance = clearance;
|
||||
aCtx->obstacles.insert( obs );
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Fast method
|
||||
if( shapeA->Collide( shapeB, clearance + lineWidthA + lineWidthB - clearanceEpsilon ) )
|
||||
if( shapeH->Collide( shapeI, clearance + lineWidthH + lineWidthI - clearanceEpsilon ) )
|
||||
{
|
||||
if( aObsInfo )
|
||||
aObsInfo->m_clearance = clearance;
|
||||
if( aCtx )
|
||||
{
|
||||
collisionsFound = true;
|
||||
OBSTACLE obs;
|
||||
obs.m_head = const_cast<ITEM*>( aHead );
|
||||
obs.m_item = const_cast<ITEM*>( this );
|
||||
obs.m_clearance = clearance;
|
||||
|
||||
return true;
|
||||
//printf("i %p h %p ih %p hh %p\n", this ,aHead, holeI, holeH);
|
||||
printf("HCHX %d %d\n", clearance, clearance + lineWidthH + lineWidthI - clearanceEpsilon);
|
||||
//printf("pushc %p %p cl %d cle %d\n", obs.m_head, obs.m_item, clearance, clearance + lineWidthH + lineWidthI - clearanceEpsilon );
|
||||
//printf("SH %s\n", shapeH->Format().c_str(), aHead );
|
||||
//printf("SI %s\n", shapeI->Format().c_str(), this );
|
||||
|
||||
aCtx->obstacles.insert( obs );
|
||||
|
||||
dumpObstacles( aCtx->obstacles );
|
||||
printf("--EndDump\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return collisionsFound;
|
||||
}
|
||||
|
||||
|
||||
bool ITEM::Collide( const ITEM* aOther, const NODE* aNode,
|
||||
const COLLISION_SEARCH_OPTIONS& aOpts, OBSTACLE *aObsInfo ) const
|
||||
bool ITEM::Collide( const ITEM* aOther, const NODE* aNode, COLLISION_SEARCH_CONTEXT *aCtx ) const
|
||||
{
|
||||
if( collideSimple( aOther, aNode, aOpts, aObsInfo ) )
|
||||
if( collideSimple( aOther, aNode, aCtx ) )
|
||||
return true;
|
||||
|
||||
// Special cases for "head" lines with vias attached at the end. Note that this does not
|
||||
|
@ -209,15 +337,15 @@ bool ITEM::Collide( const ITEM* aOther, const NODE* aNode,
|
|||
{
|
||||
const LINE* line = static_cast<const LINE*>( this );
|
||||
|
||||
if( line->EndsWithVia() && line->Via().collideSimple( aOther, aNode, aOpts, aObsInfo ) )
|
||||
if( line->EndsWithVia() && line->Via().collideSimple( aOther, aNode, aCtx ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
if( aOther->m_kind == LINE_T )
|
||||
{
|
||||
const LINE* line = static_cast<const LINE*>( aOther );
|
||||
const LINE* line = static_cast<const LINE*>( aOther ); // fixme
|
||||
|
||||
if( line->EndsWithVia() && line->Via().collideSimple( this, aNode, aOpts, aObsInfo ) )
|
||||
if( line->EndsWithVia() && line->Via().collideSimple( this, aNode, aCtx ) )
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -236,6 +364,8 @@ std::string ITEM::KindStr() const
|
|||
case JOINT_T: return "joint";
|
||||
case SOLID_T: return "solid";
|
||||
case DIFF_PAIR_T: return "diff-pair";
|
||||
case HOLE_T: return "hole";
|
||||
|
||||
default: return "unknown";
|
||||
}
|
||||
}
|
||||
|
@ -245,6 +375,113 @@ ITEM::~ITEM()
|
|||
{
|
||||
}
|
||||
|
||||
HOLE::~HOLE()
|
||||
{
|
||||
delete m_holeShape;
|
||||
}
|
||||
|
||||
HOLE* HOLE::Clone() const
|
||||
{
|
||||
HOLE* h = new HOLE( m_parentPadVia, m_holeShape->Clone() );
|
||||
|
||||
h->SetNet( Net() );
|
||||
h->SetLayers( Layers() );
|
||||
|
||||
h->m_rank = m_rank;
|
||||
h->m_marker = m_marker;
|
||||
h->m_parent = m_parent;
|
||||
h->m_isVirtual = m_isVirtual;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
const SHAPE_LINE_CHAIN HOLE::Hull( int aClearance, int aWalkaroundThickness, int aLayer ) const
|
||||
{
|
||||
if( !m_holeShape )
|
||||
return SHAPE_LINE_CHAIN();
|
||||
|
||||
if( m_holeShape->Type() == SH_CIRCLE )
|
||||
{
|
||||
auto cir = static_cast<SHAPE_CIRCLE*>( m_holeShape );
|
||||
int cl = ( aClearance + aWalkaroundThickness / 2 );
|
||||
int width = cir->GetRadius() * 2;
|
||||
|
||||
// Chamfer = width * ( 1 - sqrt(2)/2 ) for equilateral octagon
|
||||
return OctagonalHull( cir->GetCenter() - VECTOR2I( width / 2, width / 2 ), VECTOR2I( width, width ), cl,
|
||||
( 2 * cl + width ) * ( 1.0 - M_SQRT1_2 ) );
|
||||
}
|
||||
else if( m_holeShape->Type() == SH_COMPOUND )
|
||||
{
|
||||
SHAPE_COMPOUND* cmpnd = static_cast<SHAPE_COMPOUND*>( m_holeShape );
|
||||
|
||||
if ( cmpnd->Shapes().size() == 1 )
|
||||
{
|
||||
return BuildHullForPrimitiveShape( cmpnd->Shapes()[0], aClearance,
|
||||
aWalkaroundThickness );
|
||||
}
|
||||
else
|
||||
{
|
||||
SHAPE_POLY_SET hullSet;
|
||||
|
||||
for( SHAPE* shape : cmpnd->Shapes() )
|
||||
{
|
||||
hullSet.AddOutline( BuildHullForPrimitiveShape( shape, aClearance,
|
||||
aWalkaroundThickness ) );
|
||||
}
|
||||
|
||||
hullSet.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
return hullSet.Outline( 0 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return BuildHullForPrimitiveShape( m_holeShape, aClearance, aWalkaroundThickness );
|
||||
}
|
||||
}
|
||||
|
||||
bool HOLE::IsCircular() const
|
||||
{
|
||||
return m_holeShape->Type() == SH_CIRCLE;
|
||||
}
|
||||
|
||||
int HOLE::Radius() const
|
||||
{
|
||||
assert( m_holeShape->Type() == SH_CIRCLE );
|
||||
|
||||
return static_cast<const SHAPE_CIRCLE*>( m_holeShape )->GetRadius();
|
||||
}
|
||||
|
||||
const VECTOR2I HOLE::Pos() const
|
||||
{
|
||||
return VECTOR2I( 0, 0 ); // fixme holes
|
||||
}
|
||||
|
||||
void HOLE::SetCenter( const VECTOR2I& aCenter )
|
||||
{
|
||||
assert( m_holeShape->Type() == SH_CIRCLE );
|
||||
static_cast<SHAPE_CIRCLE*>( m_holeShape )->SetCenter( aCenter );
|
||||
}
|
||||
void HOLE::SetRadius( int aRadius )
|
||||
{
|
||||
assert( m_holeShape->Type() == SH_CIRCLE );
|
||||
static_cast<SHAPE_CIRCLE*>( m_holeShape )->SetRadius( aRadius );
|
||||
}
|
||||
|
||||
|
||||
void HOLE::Move( const VECTOR2I& delta )
|
||||
{
|
||||
m_holeShape->Move( delta );
|
||||
}
|
||||
|
||||
HOLE* HOLE::MakeCircularHole( const VECTOR2I& pos, int radius )
|
||||
{
|
||||
auto circle = new SHAPE_CIRCLE( pos, radius );
|
||||
auto hole = new HOLE( nullptr, circle );
|
||||
hole->SetLayers( LAYER_RANGE( F_Cu, B_Cu ) );
|
||||
return hole;
|
||||
}
|
||||
|
||||
|
||||
const std::string ITEM::Format() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#define __PNS_ITEM_H
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_set>
|
||||
#include <math/vector2d.h>
|
||||
|
||||
#include <geometry/shape.h>
|
||||
|
@ -44,28 +45,13 @@ enum LineMarker {
|
|||
MK_DP_COUPLED = ( 1 << 5 )
|
||||
};
|
||||
|
||||
struct OBSTACLE;
|
||||
|
||||
class ITEM;
|
||||
class HOLE;
|
||||
struct COLLISION_SEARCH_CONTEXT;
|
||||
|
||||
struct COLLISION_SEARCH_OPTIONS
|
||||
{
|
||||
bool m_differentNetsOnly = true;
|
||||
int m_overrideClearance = -1;
|
||||
int m_limitCount = -1;
|
||||
int m_kindMask = -1;
|
||||
bool m_useClearanceEpsilon = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dummy interface for ITEMs that can own other ITEMs
|
||||
*/
|
||||
class ITEM_OWNER {};
|
||||
|
||||
/**
|
||||
* Base class for an item belonging to some container.
|
||||
*
|
||||
* Container can be another ITEM, ITEM_SET or a NODE.
|
||||
*/
|
||||
class OWNABLE_ITEM
|
||||
{
|
||||
public:
|
||||
|
@ -95,7 +81,6 @@ protected:
|
|||
const ITEM_OWNER *m_owner;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Base class for PNS router board items.
|
||||
*
|
||||
|
@ -117,7 +102,8 @@ public:
|
|||
ARC_T = 16,
|
||||
VIA_T = 32,
|
||||
DIFF_PAIR_T = 64,
|
||||
ANY_T = 0xff
|
||||
HOLE_T = 128,
|
||||
ANY_T = 0xffff
|
||||
};
|
||||
|
||||
ITEM( PnsKind aKind )
|
||||
|
@ -170,12 +156,6 @@ public:
|
|||
return SHAPE_LINE_CHAIN();
|
||||
}
|
||||
|
||||
virtual const SHAPE_LINE_CHAIN HoleHull( int aClearance, int aWalkaroundThickness = 0,
|
||||
int aLayer = -1 ) const
|
||||
{
|
||||
return SHAPE_LINE_CHAIN();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type (kind) of the item.
|
||||
*/
|
||||
|
@ -216,7 +196,7 @@ public:
|
|||
{
|
||||
return Layers().Overlaps( aOther->Layers() );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check for a collision (clearance violation) with between us and item \a aOther.
|
||||
*
|
||||
|
@ -226,9 +206,8 @@ public:
|
|||
* @param aOther is the item to check collision against.
|
||||
* @return true, if a collision was found.
|
||||
*/
|
||||
bool Collide( const ITEM* aOther, const NODE* aNode,
|
||||
const COLLISION_SEARCH_OPTIONS& aOpts = COLLISION_SEARCH_OPTIONS(),
|
||||
OBSTACLE *aObsInfo = nullptr ) const;
|
||||
bool Collide( const ITEM* aHead, const NODE* aNode,
|
||||
COLLISION_SEARCH_CONTEXT* aCtx = nullptr ) const;
|
||||
|
||||
/**
|
||||
* Return the geometrical shape of the item. Used for collision detection and spatial indexing.
|
||||
|
@ -238,11 +217,6 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
virtual const SHAPE* Hole() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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,18 +253,20 @@ public:
|
|||
void SetIsCompoundShapePrimitive() { m_isCompoundShapePrimitive = true; }
|
||||
bool IsCompoundShapePrimitive() const { return m_isCompoundShapePrimitive; }
|
||||
|
||||
virtual bool HasHole() const { return false; }
|
||||
virtual HOLE *Hole() const { return nullptr; }
|
||||
virtual void SetHole( HOLE* aHole ) {};
|
||||
|
||||
virtual const std::string Format() const;
|
||||
|
||||
private:
|
||||
bool collideSimple( const ITEM* aOther, const NODE* aNode,
|
||||
const COLLISION_SEARCH_OPTIONS& aOpts,
|
||||
OBSTACLE *aObsInfo = nullptr ) const;
|
||||
bool collideSimple( const ITEM* aHead, const NODE* aNode,
|
||||
COLLISION_SEARCH_CONTEXT* aCtx ) const;
|
||||
|
||||
protected:
|
||||
PnsKind m_kind;
|
||||
|
||||
BOARD_ITEM* m_parent;
|
||||
NODE* m_owner;
|
||||
LAYER_RANGE m_layers;
|
||||
|
||||
bool m_movable;
|
||||
|
|
|
@ -106,10 +106,6 @@ public:
|
|||
|
||||
virtual int Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
|
||||
bool aUseClearanceEpsilon = true ) override;
|
||||
virtual int HoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
|
||||
bool aUseClearanceEpsilon = true ) override;
|
||||
virtual int HoleToHoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
|
||||
bool aUseClearanceEpsilon = true ) override;
|
||||
|
||||
virtual int DpCoupledNet( int aNet ) override;
|
||||
virtual int DpNetPolarity( int aNet ) override;
|
||||
|
@ -153,8 +149,6 @@ private:
|
|||
int m_clearanceEpsilon;
|
||||
|
||||
std::unordered_map<CLEARANCE_CACHE_KEY, int> m_clearanceCache;
|
||||
std::unordered_map<CLEARANCE_CACHE_KEY, int> m_holeClearanceCache;
|
||||
std::unordered_map<CLEARANCE_CACHE_KEY, int> m_holeToHoleClearanceCache;
|
||||
};
|
||||
|
||||
|
||||
|
@ -258,16 +252,31 @@ bool PNS_PCBNEW_RULE_RESOLVER::IsNetTieExclusion( const PNS::ITEM* aItem,
|
|||
}
|
||||
|
||||
|
||||
bool isCopper( const PNS::ITEM* aItem )
|
||||
static bool isCopper( const PNS::ITEM* aItem )
|
||||
{
|
||||
if ( !aItem )
|
||||
return false;
|
||||
|
||||
const BOARD_ITEM *parent = aItem->Parent();
|
||||
|
||||
return !parent || parent->IsOnCopperLayer();
|
||||
}
|
||||
|
||||
|
||||
bool isEdge( const PNS::ITEM* aItem )
|
||||
static bool isHole( const PNS::ITEM* aItem )
|
||||
{
|
||||
if ( !aItem )
|
||||
return false;
|
||||
|
||||
return aItem->OfKind( PNS::ITEM::HOLE_T );
|
||||
}
|
||||
|
||||
|
||||
static bool isEdge( const PNS::ITEM* aItem )
|
||||
{
|
||||
if ( !aItem )
|
||||
return false;
|
||||
|
||||
const BOARD_ITEM *parent = aItem->Parent();
|
||||
|
||||
return parent && ( parent->IsOnLayer( Edge_Cuts ) || parent->IsOnLayer( Margin ) );
|
||||
|
@ -388,8 +397,6 @@ void PNS_PCBNEW_RULE_RESOLVER::ClearCacheForItem( const PNS::ITEM* aItem )
|
|||
void PNS_PCBNEW_RULE_RESOLVER::ClearCaches()
|
||||
{
|
||||
m_clearanceCache.clear();
|
||||
m_holeClearanceCache.clear();
|
||||
m_holeToHoleClearanceCache.clear();
|
||||
}
|
||||
|
||||
|
||||
|
@ -420,7 +427,23 @@ int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* a
|
|||
|
||||
for( int layer = layers.Start(); layer <= layers.End(); ++layer )
|
||||
{
|
||||
if( isCopper( aA ) && ( !aB || isCopper( aB ) ) )
|
||||
if( isHole( aA ) && isHole( aB) )
|
||||
{
|
||||
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE, aA, aB, layer, &constraint ) )
|
||||
{
|
||||
if( constraint.m_Value.Min() > rv )
|
||||
rv = constraint.m_Value.Min();
|
||||
}
|
||||
}
|
||||
else if( isHole( aA ) || isHole( aB ) )
|
||||
{
|
||||
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE, aA, aB, layer, &constraint ) )
|
||||
{
|
||||
if( constraint.m_Value.Min() > rv )
|
||||
rv = constraint.m_Value.Min();
|
||||
}
|
||||
}
|
||||
else if( isCopper( aA ) && ( !aB || isCopper( aB ) ) )
|
||||
{
|
||||
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, layer, &constraint ) )
|
||||
{
|
||||
|
@ -428,8 +451,7 @@ int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* a
|
|||
rv = constraint.m_Value.Min();
|
||||
}
|
||||
}
|
||||
|
||||
if( isEdge( aA ) || ( aB && isEdge( aB ) ) )
|
||||
else if( isEdge( aA ) || ( aB && isEdge( aB ) ) )
|
||||
{
|
||||
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE, aA, aB, layer, &constraint ) )
|
||||
{
|
||||
|
@ -446,6 +468,7 @@ int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* a
|
|||
return rv;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
int PNS_PCBNEW_RULE_RESOLVER::HoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
|
||||
bool aUseClearanceEpsilon )
|
||||
|
@ -470,6 +493,8 @@ int PNS_PCBNEW_RULE_RESOLVER::HoleClearance( const PNS::ITEM* aA, const PNS::ITE
|
|||
|
||||
#define HAS_PLATED_HOLE( a ) ( a )->IsRoutable()
|
||||
|
||||
// JEY TODO: this is a new test introduced after Tom's brach. How does it fit
|
||||
// in the new architecture?
|
||||
if( IsCopperLayer( layer )
|
||||
&& ( HAS_PLATED_HOLE( aA ) || HAS_PLATED_HOLE( aB ) )
|
||||
&& QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, layer, &constraint )
|
||||
|
@ -514,6 +539,8 @@ int PNS_PCBNEW_RULE_RESOLVER::HoleToHoleClearance( const PNS::ITEM* aA, const PN
|
|||
return rv;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
bool PNS_KICAD_IFACE_BASE::inheritTrackWidth( PNS::ITEM* aItem, int* aInheritedWidth )
|
||||
{
|
||||
|
@ -1139,7 +1166,12 @@ std::unique_ptr<PNS::SOLID> PNS_KICAD_IFACE_BASE::syncPad( PAD* aPad )
|
|||
solid->SetOffset( VECTOR2I( offset.x, offset.y ) );
|
||||
|
||||
if( aPad->GetDrillSize().x > 0 )
|
||||
solid->SetHole( aPad->GetEffectiveHoleShape()->Clone() );
|
||||
{
|
||||
SHAPE_SEGMENT* slot = (SHAPE_SEGMENT*) aPad->GetEffectiveHoleShape()->Clone();
|
||||
PNS::HOLE* hole = new PNS::HOLE( solid.get(), slot );
|
||||
|
||||
solid->SetHole( hole );
|
||||
}
|
||||
|
||||
// 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.
|
||||
|
@ -1213,9 +1245,10 @@ std::unique_ptr<PNS::VIA> PNS_KICAD_IFACE_BASE::syncVia( PCB_VIA* aVia )
|
|||
|
||||
via->SetIsFree( aVia->GetIsFree() );
|
||||
|
||||
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
|
||||
via->SetHole( SHAPE_CIRCLE( aVia->GetPosition(),
|
||||
aVia->GetDrillValue() / 2 ) );
|
||||
SHAPE* holeShape = new SHAPE_CIRCLE( aVia->GetPosition(), aVia->GetDrillValue() / 2 );
|
||||
PNS::HOLE* viaHole = new PNS::HOLE( via.get(), holeShape );
|
||||
|
||||
via->SetHole( viaHole );
|
||||
|
||||
return via;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ class PCB_DISPLAY_OPTIONS;
|
|||
class PCB_TOOL_BASE;
|
||||
class FOOTPRINT;
|
||||
class PAD;
|
||||
class EDA_TEXT;
|
||||
|
||||
namespace PNS
|
||||
{
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
#include "pns_node.h"
|
||||
#include "pns_via.h"
|
||||
#include "pns_utils.h"
|
||||
#include "pns_router.h"
|
||||
#include "pns_debug_decorator.h"
|
||||
|
||||
#include <geometry/shape_rect.h>
|
||||
|
||||
|
@ -545,6 +547,12 @@ bool LINE::Walkaround( const SHAPE_LINE_CHAIN& aObstacle, SHAPE_LINE_CHAIN& aPat
|
|||
|
||||
const SHAPE_LINE_CHAIN SEGMENT::Hull( int aClearance, int aWalkaroundThickness, int aLayer ) const
|
||||
{
|
||||
/*DEBUG_DECORATOR* debugDecorator = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
|
||||
|
||||
PNS_DBG( debugDecorator, Message, wxString::Format( wxT( "seghull %d %d" ), aWalkaroundThickness, aClearance ) );
|
||||
PNS_DBG(debugDecorator, AddShape, &m_seg, RED, 0, wxT("theseg") );
|
||||
*/
|
||||
|
||||
return SegmentHull( m_seg, aClearance, aWalkaroundThickness );
|
||||
}
|
||||
|
||||
|
|
|
@ -196,6 +196,7 @@ public:
|
|||
void AppendVia( const VIA& aVia );
|
||||
void RemoveVia() { m_hasVia = false; }
|
||||
|
||||
VIA& Via() { return m_via; }
|
||||
const VIA& Via() const { return m_via; }
|
||||
|
||||
void SetViaDiameter( int aDiameter ) { m_via.SetDiameter( aDiameter ); }
|
||||
|
|
|
@ -87,10 +87,20 @@ NODE::~NODE()
|
|||
|
||||
m_joints.clear();
|
||||
|
||||
std::vector<ITEM*> toDelete;
|
||||
|
||||
toDelete.reserve( m_index->Size() );
|
||||
|
||||
for( ITEM* item : *m_index )
|
||||
{
|
||||
if( item->BelongsTo( this ) )
|
||||
delete item;
|
||||
if( item->BelongsTo( this ) && item->OfKind( ITEM::HOLE_T ) )
|
||||
toDelete.push_back( item );
|
||||
}
|
||||
|
||||
for( ITEM* item : toDelete )
|
||||
{
|
||||
printf("del item %p type %s\n", item, item->KindStr().c_str() );
|
||||
delete item;
|
||||
}
|
||||
|
||||
releaseGarbage();
|
||||
|
@ -105,34 +115,13 @@ int NODE::GetClearance( const ITEM* aA, const ITEM* aB, bool aUseClearanceEpsilo
|
|||
if( !m_ruleResolver )
|
||||
return 100000;
|
||||
|
||||
if( aA->IsVirtual() || aB->IsVirtual() )
|
||||
return 0;
|
||||
|
||||
return m_ruleResolver->Clearance( aA, aB, aUseClearanceEpsilon );
|
||||
}
|
||||
|
||||
|
||||
int NODE::GetHoleClearance( const ITEM* aA, const ITEM* aB, bool aUseClearanceEpsilon ) const
|
||||
{
|
||||
if( !m_ruleResolver )
|
||||
return 0;
|
||||
|
||||
if( aA->IsVirtual() || aB->IsVirtual() )
|
||||
return 0;
|
||||
|
||||
return m_ruleResolver->HoleClearance( aA, aB, aUseClearanceEpsilon );
|
||||
}
|
||||
|
||||
|
||||
int NODE::GetHoleToHoleClearance( const ITEM* aA, const ITEM* aB, bool aUseClearanceEpsilon ) const
|
||||
{
|
||||
if( !m_ruleResolver )
|
||||
return 0;
|
||||
|
||||
if( aA->IsVirtual() || aB->IsVirtual() )
|
||||
return 0;
|
||||
|
||||
return m_ruleResolver->HoleToHoleClearance( aA, aB, aUseClearanceEpsilon );
|
||||
int cl = m_ruleResolver->Clearance( aA, aB, aUseClearanceEpsilon );
|
||||
|
||||
return cl;
|
||||
}
|
||||
|
||||
|
||||
|
@ -211,16 +200,11 @@ bool OBSTACLE_VISITOR::visit( ITEM* aCandidate )
|
|||
// function object that visits potential obstacles and performs the actual collision refining
|
||||
struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR
|
||||
{
|
||||
OBSTACLES& m_tab;
|
||||
int m_matchCount;
|
||||
const COLLISION_SEARCH_OPTIONS& m_opts;
|
||||
COLLISION_SEARCH_CONTEXT* m_ctx;
|
||||
|
||||
DEFAULT_OBSTACLE_VISITOR( NODE::OBSTACLES& aTab, const ITEM* aItem,
|
||||
const COLLISION_SEARCH_OPTIONS& aOpts ) :
|
||||
DEFAULT_OBSTACLE_VISITOR( COLLISION_SEARCH_CONTEXT* aCtx, const ITEM* aItem ) :
|
||||
OBSTACLE_VISITOR( aItem ),
|
||||
m_tab( aTab ),
|
||||
m_opts( aOpts ),
|
||||
m_matchCount( 0 )
|
||||
m_ctx( aCtx )
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -230,26 +214,16 @@ struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR
|
|||
|
||||
bool operator()( ITEM* aCandidate ) override
|
||||
{
|
||||
if( !aCandidate->OfKind( m_opts.m_kindMask ) )
|
||||
if( !aCandidate->OfKind( m_ctx->options.m_kindMask ) )
|
||||
return true;
|
||||
|
||||
if( visit( aCandidate ) )
|
||||
return true;
|
||||
|
||||
if( !aCandidate->Collide( m_item, m_node, m_opts ) )
|
||||
if( !aCandidate->Collide( m_item, m_node, m_ctx ) )
|
||||
return true;
|
||||
|
||||
OBSTACLE obs;
|
||||
|
||||
obs.m_head = m_item;
|
||||
obs.m_item = aCandidate;
|
||||
obs.m_distFirst = INT_MAX;
|
||||
obs.m_maxFanoutWidth = 0;
|
||||
m_tab.push_back( obs );
|
||||
|
||||
m_matchCount++;
|
||||
|
||||
if( m_opts.m_limitCount > 0 && m_matchCount >= m_opts.m_limitCount )
|
||||
if( m_ctx->options.m_limitCount > 0 && m_ctx->obstacles.size() >= m_ctx->options.m_limitCount )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -258,13 +232,15 @@ struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR
|
|||
|
||||
|
||||
int NODE::QueryColliding( const ITEM* aItem, NODE::OBSTACLES& aObstacles,
|
||||
const COLLISION_SEARCH_OPTIONS& aOpts )
|
||||
const COLLISION_SEARCH_OPTIONS& aOpts ) const
|
||||
{
|
||||
COLLISION_SEARCH_CONTEXT ctx( aObstacles, aOpts );
|
||||
|
||||
/// By default, virtual items cannot collide
|
||||
if( aItem->IsVirtual() )
|
||||
return 0;
|
||||
|
||||
DEFAULT_OBSTACLE_VISITOR visitor( aObstacles, aItem, aOpts );
|
||||
DEFAULT_OBSTACLE_VISITOR visitor( &ctx, aItem );
|
||||
|
||||
#ifdef DEBUG
|
||||
assert( allocNodes.find( this ) != allocNodes.end() );
|
||||
|
@ -276,7 +252,7 @@ int NODE::QueryColliding( const ITEM* aItem, NODE::OBSTACLES& aObstacles,
|
|||
m_index->Query( aItem, m_maxClearance, visitor );
|
||||
|
||||
// if we haven't found enough items, look in the root branch as well.
|
||||
if( !isRoot() && ( visitor.m_matchCount < aOpts.m_limitCount || aOpts.m_limitCount < 0 ) )
|
||||
if( !isRoot() && ( ctx.obstacles.size() < aOpts.m_limitCount || aOpts.m_limitCount < 0 ) )
|
||||
{
|
||||
visitor.SetWorld( m_root, this );
|
||||
m_root->m_index->Query( aItem, m_maxClearance, visitor );
|
||||
|
@ -286,22 +262,24 @@ int NODE::QueryColliding( const ITEM* aItem, NODE::OBSTACLES& aObstacles,
|
|||
}
|
||||
|
||||
|
||||
NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, int aKindMask,
|
||||
const std::set<ITEM*>* aRestrictedSet,
|
||||
NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine,
|
||||
const COLLISION_SEARCH_OPTIONS& aOpts )
|
||||
{
|
||||
const int clearanceEpsilon = GetRuleResolver()->ClearanceEpsilon();
|
||||
OBSTACLES obstacleList;
|
||||
obstacleList.reserve( 100 );
|
||||
const int clearanceEpsilon = GetRuleResolver()->ClearanceEpsilon();
|
||||
OBSTACLES obstacleList;
|
||||
std::vector<SEGMENT> tmpSegs;
|
||||
|
||||
tmpSegs.reserve( aLine->CLine().SegmentCount() );
|
||||
|
||||
|
||||
for( int i = 0; i < aLine->CLine().SegmentCount(); i++ )
|
||||
{
|
||||
// Note: Clearances between &s and other items are cached,
|
||||
// Note: Clearances between tmpSegs.back() and other items are cached,
|
||||
// which means they'll be the same for all segments in the line.
|
||||
// Disabling the cache will lead to slowness.
|
||||
|
||||
const SEGMENT s( *aLine, aLine->CLine().CSegment( i ) );
|
||||
QueryColliding( &s, obstacleList, aOpts );
|
||||
tmpSegs.emplace_back( *aLine, aLine->CLine().CSegment( i ) );
|
||||
QueryColliding( &tmpSegs.back(), obstacleList, aOpts );
|
||||
}
|
||||
|
||||
if( aLine->EndsWithVia() )
|
||||
|
@ -317,22 +295,15 @@ NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, int aKindMask,
|
|||
nearest.m_maxFanoutWidth = 0;
|
||||
|
||||
auto updateNearest =
|
||||
[&]( const SHAPE_LINE_CHAIN::INTERSECTION& pt, ITEM* obstacle,
|
||||
const SHAPE_LINE_CHAIN& hull, bool isHole )
|
||||
[&]( const SHAPE_LINE_CHAIN::INTERSECTION& pt, const OBSTACLE& obstacle )
|
||||
{
|
||||
int dist = aLine->CLine().PathLength( pt.p, pt.index_their );
|
||||
|
||||
if( dist < nearest.m_distFirst )
|
||||
{
|
||||
nearest = obstacle;
|
||||
nearest.m_distFirst = dist;
|
||||
nearest.m_ipFirst = pt.p;
|
||||
nearest.m_item = obstacle;
|
||||
nearest.m_itemIsHole = isHole;
|
||||
// JEY TODO: are these no longer needed?
|
||||
//nearest.m_hull = hull;
|
||||
|
||||
//obstacle->Mark( isHole ? obstacle->Marker() | MK_HOLE
|
||||
// : obstacle->Marker() & ~MK_HOLE );
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -343,7 +314,7 @@ NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, int aKindMask,
|
|||
|
||||
for( const OBSTACLE& obstacle : obstacleList )
|
||||
{
|
||||
if( aRestrictedSet && aRestrictedSet->find( obstacle.m_item ) == aRestrictedSet->end() )
|
||||
if( aOpts.m_restrictedSet && aOpts.m_restrictedSet->count( obstacle.m_item ) == 0 )
|
||||
continue;
|
||||
|
||||
int clearance =
|
||||
|
@ -360,23 +331,24 @@ NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, int aKindMask,
|
|||
{
|
||||
//debugDecorator->AddPoint( ip.p, ip.valid?3:6, 100000, (const char *) wxString::Format("obstacle-isect-point-%d" ).c_str() );
|
||||
if( ip.valid )
|
||||
updateNearest( ip, obstacle.m_item, obstacleHull, false );
|
||||
updateNearest( ip, obstacle );
|
||||
}
|
||||
|
||||
if( aLine->EndsWithVia() )
|
||||
{
|
||||
const VIA& via = aLine->Via();
|
||||
// Don't use via.Drill(); it doesn't include the plating thickness
|
||||
// JEY TODO: clean up (or re-enable) all this commented-out stuff....
|
||||
//const HOLE* viaHole = via.Hole();
|
||||
|
||||
int viaHoleRadius = static_cast<const SHAPE_CIRCLE*>( via.Hole() )->GetRadius();
|
||||
//int viaHoleRadius = static_cast<const SHAPE_CIRCLE*>( via.Hole() )->GetRadius();
|
||||
|
||||
int viaClearance = GetClearance( obstacle.m_item, &via, aOpts.m_useClearanceEpsilon )
|
||||
+ via.Diameter() / 2;
|
||||
int holeClearance = GetHoleClearance( obstacle.m_item, &via, aOpts.m_useClearanceEpsilon )
|
||||
+ viaHoleRadius;
|
||||
//int holeClearance = GetClearance( obstacle.m_item, viaHole, aOpts.m_useClearanceEpsilon )
|
||||
// + viaHoleRadius;
|
||||
|
||||
if( holeClearance > viaClearance )
|
||||
viaClearance = holeClearance;
|
||||
//if( holeClearance > viaClearance )
|
||||
// viaClearance = holeClearance;
|
||||
|
||||
obstacleHull = obstacle.m_item->Hull( viaClearance, 0, layer );
|
||||
//debugDecorator->AddLine( obstacleHull, 3 );
|
||||
|
@ -387,9 +359,9 @@ NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, int aKindMask,
|
|||
// obstacleHull.Intersect( aLine->CLine(), intersectingPts, true );
|
||||
|
||||
for( const SHAPE_LINE_CHAIN::INTERSECTION& ip : intersectingPts )
|
||||
updateNearest( ip, obstacle.m_item, obstacleHull, false );
|
||||
updateNearest( ip, obstacle );
|
||||
}
|
||||
|
||||
#if 0
|
||||
if( ( m_collisionQueryScope == CQS_ALL_RULES
|
||||
|| !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( obstacle.m_item,
|
||||
layer ) )
|
||||
|
@ -438,10 +410,11 @@ NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, int aKindMask,
|
|||
updateNearest( ip, obstacle.m_item, obstacleHull, true );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if( nearest.m_distFirst == INT_MAX )
|
||||
nearest.m_item = obstacleList[0].m_item;
|
||||
nearest = (*obstacleList.begin());
|
||||
|
||||
return nearest;
|
||||
}
|
||||
|
@ -470,8 +443,6 @@ NODE::OPT_OBSTACLE NODE::CheckColliding( const ITEM* aItemA, int aKindMask )
|
|||
opts.m_limitCount = 1;
|
||||
opts.m_overrideClearance = 1;
|
||||
|
||||
obs.reserve( 100 );
|
||||
|
||||
if( aItemA->Kind() == ITEM::LINE_T )
|
||||
{
|
||||
int n = 0;
|
||||
|
@ -488,7 +459,7 @@ NODE::OPT_OBSTACLE NODE::CheckColliding( const ITEM* aItemA, int aKindMask )
|
|||
n += QueryColliding( &s, obs, opts );
|
||||
|
||||
if( n )
|
||||
return OPT_OBSTACLE( obs[0] );
|
||||
return OPT_OBSTACLE( *obs.begin() );
|
||||
}
|
||||
|
||||
if( line->EndsWithVia() )
|
||||
|
@ -496,12 +467,12 @@ NODE::OPT_OBSTACLE NODE::CheckColliding( const ITEM* aItemA, int aKindMask )
|
|||
n += QueryColliding( &line->Via(), obs, opts );
|
||||
|
||||
if( n )
|
||||
return OPT_OBSTACLE( obs[0] );
|
||||
return OPT_OBSTACLE( *obs.begin() );
|
||||
}
|
||||
}
|
||||
else if( QueryColliding( aItemA, obs, opts ) > 0 )
|
||||
{
|
||||
return OPT_OBSTACLE( obs[0] );
|
||||
return OPT_OBSTACLE( *obs.begin() );
|
||||
}
|
||||
|
||||
return OPT_OBSTACLE();
|
||||
|
@ -568,6 +539,9 @@ const ITEM_SET NODE::HitTest( const VECTOR2I& aPoint ) const
|
|||
|
||||
void NODE::addSolid( SOLID* aSolid )
|
||||
{
|
||||
if( aSolid->HasHole() )
|
||||
addHole( aSolid->Hole() );
|
||||
|
||||
if( aSolid->IsRoutable() )
|
||||
linkJoint( aSolid->Pos(), aSolid->Layers(), aSolid->Net(), aSolid );
|
||||
|
||||
|
@ -584,18 +558,53 @@ void NODE::Add( std::unique_ptr< SOLID > aSolid )
|
|||
|
||||
void NODE::addVia( VIA* aVia )
|
||||
{
|
||||
if( aVia->HasHole() )
|
||||
addHole( aVia->Hole() );
|
||||
|
||||
linkJoint( aVia->Pos(), aVia->Layers(), aVia->Net(), aVia );
|
||||
|
||||
m_index->Add( aVia );
|
||||
}
|
||||
|
||||
|
||||
void NODE::addHole( HOLE* aHole )
|
||||
{
|
||||
// do we need holes in the connection graph?
|
||||
//linkJoint( aHole->Pos(), aHole->Layers(), aHole->Net(), aHole );
|
||||
|
||||
m_index->Add( aHole );
|
||||
}
|
||||
|
||||
|
||||
void NODE::Add( std::unique_ptr< VIA > aVia )
|
||||
{
|
||||
aVia->SetOwner( this );
|
||||
addVia( aVia.release() );
|
||||
}
|
||||
|
||||
void NODE::Add( ITEM* aItem, bool aAllowRedundant )
|
||||
{
|
||||
aItem->SetOwner( this );
|
||||
|
||||
switch( aItem->Kind() )
|
||||
{
|
||||
case ITEM::ARC_T:
|
||||
addArc( static_cast<ARC*>( aItem ) );
|
||||
break;
|
||||
case ITEM::SEGMENT_T:
|
||||
addSegment( static_cast<SEGMENT*>( aItem ) );
|
||||
break;
|
||||
case ITEM::VIA_T:
|
||||
addVia( static_cast<VIA*>( aItem ) );
|
||||
break;
|
||||
case ITEM::SOLID_T:
|
||||
addSolid( static_cast<SOLID*>( aItem ) );
|
||||
break;
|
||||
default:
|
||||
assert( false );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void NODE::Add( LINE& aLine, bool aAllowRedundant )
|
||||
{
|
||||
|
@ -725,6 +734,7 @@ void NODE::Add( std::unique_ptr< ITEM > aItem, bool aAllowRedundant )
|
|||
Add( *l );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
assert( false );
|
||||
}
|
||||
|
@ -754,18 +764,37 @@ void NODE::doRemove( ITEM* aItem )
|
|||
// case 1: removing an item that is stored in the root node from any branch:
|
||||
// mark it as overridden, but do not remove
|
||||
if( aItem->BelongsTo( m_root ) && !isRoot() )
|
||||
{
|
||||
m_override.insert( aItem );
|
||||
|
||||
if( aItem->HasHole() )
|
||||
m_override.insert( aItem->Hole() );
|
||||
}
|
||||
|
||||
// case 2: the item belongs to this branch or a parent, non-root branch,
|
||||
// or the root itself and we are the root: remove from the index
|
||||
else if( !aItem->BelongsTo( m_root ) || isRoot() )
|
||||
{
|
||||
m_index->Remove( aItem );
|
||||
|
||||
if( aItem->HasHole() )
|
||||
m_index->Remove( aItem->Hole() );
|
||||
}
|
||||
|
||||
// the item belongs to this particular branch: un-reference it
|
||||
if( aItem->BelongsTo( this ) )
|
||||
{
|
||||
aItem->SetOwner( nullptr );
|
||||
|
||||
m_root->m_garbageItems.insert( aItem );
|
||||
|
||||
HOLE *hole = aItem->Hole();
|
||||
|
||||
if( hole )
|
||||
{
|
||||
m_index->Remove( hole ); // hole is not directly owned by NODE but by the parent SOLID/VIA.
|
||||
hole->SetOwner( nullptr );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <unordered_set>
|
||||
#include <set>
|
||||
#include <core/minoptmax.h>
|
||||
|
||||
#include <geometry/shape_line_chain.h>
|
||||
|
@ -46,10 +46,6 @@ class INDEX;
|
|||
class ROUTER;
|
||||
class NODE;
|
||||
|
||||
/**
|
||||
* An abstract function object, returning a design rule (clearance, diff pair gap, etc) required
|
||||
* between two items.
|
||||
*/
|
||||
|
||||
enum class CONSTRAINT_TYPE
|
||||
{
|
||||
|
@ -64,6 +60,11 @@ enum class CONSTRAINT_TYPE
|
|||
CT_HOLE_TO_HOLE = 9
|
||||
};
|
||||
|
||||
/**
|
||||
* An abstract function object, returning a design rule (clearance, diff pair gap, etc) required
|
||||
* between two items.
|
||||
*/
|
||||
|
||||
struct CONSTRAINT
|
||||
{
|
||||
CONSTRAINT_TYPE m_Type;
|
||||
|
@ -74,16 +75,68 @@ struct CONSTRAINT
|
|||
wxString m_ToName;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Hold an object colliding with another object, along with some useful data about the collision.
|
||||
*/
|
||||
struct OBSTACLE
|
||||
{
|
||||
ITEM* m_head = nullptr; ///< Line we search collisions against
|
||||
ITEM* m_item = nullptr; ///< Item found to be colliding with m_head
|
||||
VECTOR2I m_ipFirst; ///< First intersection between m_head and m_hull
|
||||
int m_clearance;
|
||||
VECTOR2I m_pos;
|
||||
int m_distFirst; ///< ... and the distance thereof
|
||||
int m_maxFanoutWidth; ///< worst case (largest) width of the tracks connected to the item
|
||||
|
||||
CONSTRAINT_TYPE m_violatingConstraint;
|
||||
|
||||
bool operator==(const OBSTACLE& other) const
|
||||
{
|
||||
return m_head == other.m_head && m_item == other.m_item;
|
||||
}
|
||||
|
||||
bool operator<(const OBSTACLE& other) const
|
||||
{
|
||||
if( (uintptr_t)m_head < (uintptr_t)other.m_head )
|
||||
return true;
|
||||
else if ( m_head == other.m_head )
|
||||
return (uintptr_t)m_item < (uintptr_t)other.m_item;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct COLLISION_SEARCH_OPTIONS
|
||||
{
|
||||
bool m_differentNetsOnly = true;
|
||||
int m_overrideClearance = -1;
|
||||
int m_limitCount = -1;
|
||||
int m_kindMask = -1;
|
||||
bool m_useClearanceEpsilon = true;
|
||||
std::set<ITEM*>* m_restrictedSet = nullptr;
|
||||
};
|
||||
|
||||
|
||||
struct COLLISION_SEARCH_CONTEXT
|
||||
{
|
||||
COLLISION_SEARCH_CONTEXT( std::set<OBSTACLE>& aObs, const COLLISION_SEARCH_OPTIONS aOpts ) :
|
||||
obstacles( aObs ),
|
||||
options( aOpts )
|
||||
{
|
||||
}
|
||||
|
||||
std::set<OBSTACLE>& obstacles;
|
||||
const COLLISION_SEARCH_OPTIONS options;
|
||||
};
|
||||
|
||||
|
||||
class RULE_RESOLVER
|
||||
{
|
||||
public:
|
||||
virtual ~RULE_RESOLVER() {}
|
||||
|
||||
virtual int Clearance( const ITEM* aA, const ITEM* aB, bool aUseClearanceEpsilon = true ) = 0;
|
||||
virtual int HoleClearance( const ITEM* aA, const ITEM* aB,
|
||||
bool aUseClearanceEpsilon = true ) = 0;
|
||||
virtual int HoleToHoleClearance( const ITEM* aA, const ITEM* aB,
|
||||
bool aUseClearanceEpsilon = true ) = 0;
|
||||
|
||||
virtual int DpCoupledNet( int aNet ) = 0;
|
||||
virtual int DpNetPolarity( int aNet ) = 0;
|
||||
|
@ -108,23 +161,6 @@ public:
|
|||
virtual int ClearanceEpsilon() const { return 0; }
|
||||
};
|
||||
|
||||
/**
|
||||
* Hold an object colliding with another object, along with some useful data about the collision.
|
||||
*/
|
||||
struct OBSTACLE
|
||||
{
|
||||
const ITEM* m_head; ///< Line we search collisions against
|
||||
ITEM* m_item; ///< Item found to be colliding with m_head
|
||||
VECTOR2I m_ipFirst; ///< First intersection between m_head and m_hull
|
||||
int m_clearance;
|
||||
int m_actual;
|
||||
int m_walkaroundThickness;
|
||||
bool m_headIsHole = false;
|
||||
bool m_itemIsHole = false;
|
||||
VECTOR2I m_pos;
|
||||
int m_distFirst; ///< ... and the distance thereof
|
||||
int m_maxFanoutWidth; ///< worst case (largest) width of the tracks connected to the item
|
||||
};
|
||||
|
||||
class OBSTACLE_VISITOR
|
||||
{
|
||||
|
@ -171,16 +207,13 @@ public:
|
|||
|
||||
typedef std::optional<OBSTACLE> OPT_OBSTACLE;
|
||||
typedef std::vector<ITEM*> ITEM_VECTOR;
|
||||
typedef std::vector<OBSTACLE> OBSTACLES;
|
||||
typedef std::set<OBSTACLE> OBSTACLES;
|
||||
|
||||
NODE();
|
||||
~NODE();
|
||||
|
||||
///< Return the expected clearance between items a and b.
|
||||
int GetClearance( const ITEM* aA, const ITEM* aB, bool aUseClearanceEpsilon = true ) const;
|
||||
int GetHoleClearance( const ITEM* aA, const ITEM* aB, bool aUseClearanceEpsilon = true ) const;
|
||||
int GetHoleToHoleClearance( const ITEM* aA, const ITEM* aB,
|
||||
bool aUseClearanceEpsilon = true ) const;
|
||||
|
||||
///< Return the pre-set worst case clearance between any pair of items.
|
||||
int GetMaxClearance() const
|
||||
|
@ -227,7 +260,7 @@ public:
|
|||
* @return number of obstacles found
|
||||
*/
|
||||
int QueryColliding( const ITEM* aItem, OBSTACLES& aObstacles,
|
||||
const COLLISION_SEARCH_OPTIONS& aOpts = COLLISION_SEARCH_OPTIONS() );
|
||||
const COLLISION_SEARCH_OPTIONS& aOpts = COLLISION_SEARCH_OPTIONS() ) const;
|
||||
|
||||
int QueryJoints( const BOX2I& aBox, std::vector<JOINT*>& aJoints,
|
||||
LAYER_RANGE aLayerMask = LAYER_RANGE::All(), int aKindMask = ITEM::ANY_T );
|
||||
|
@ -241,8 +274,7 @@ public:
|
|||
* @param aRestrictedSet is an optional set of items that should be considered as obstacles
|
||||
* @return the obstacle, if found, otherwise empty.
|
||||
*/
|
||||
OPT_OBSTACLE NearestObstacle( const LINE* aLine, int aKindMask = ITEM::ANY_T,
|
||||
const std::set<ITEM*>* aRestrictedSet = nullptr,
|
||||
OPT_OBSTACLE NearestObstacle( const LINE* aLine,
|
||||
const COLLISION_SEARCH_OPTIONS& aOpts = COLLISION_SEARCH_OPTIONS() );
|
||||
|
||||
/**
|
||||
|
@ -289,6 +321,8 @@ public:
|
|||
|
||||
void Add( LINE& aLine, bool aAllowRedundant = false );
|
||||
|
||||
void Add( ITEM* aItem, bool aAllowRedundant = false );
|
||||
|
||||
void AddEdgeExclusion( std::unique_ptr<SHAPE> aShape );
|
||||
|
||||
bool QueryEdgeExclusions( const VECTOR2I& aPos ) const;
|
||||
|
@ -450,6 +484,7 @@ private:
|
|||
void addSegment( SEGMENT* aSeg );
|
||||
void addVia( VIA* aVia );
|
||||
void addArc( ARC* aVia );
|
||||
void addHole( HOLE* aHole );
|
||||
|
||||
void removeSolidIndex( SOLID* aSeg );
|
||||
void removeSegmentIndex( SEGMENT* aSeg );
|
||||
|
|
|
@ -153,8 +153,8 @@ const ITEM_SET ROUTER::QueryHoverItems( const VECTOR2I& aP, bool aUseClearance )
|
|||
|
||||
PNS::ITEM_SET ret;
|
||||
|
||||
for( OBSTACLE& obstacle : obs )
|
||||
ret.Add( obstacle.m_item, false );
|
||||
for( const OBSTACLE& obstacle : obs )
|
||||
ret.Add( obstacle.m_item, false );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -633,19 +633,15 @@ void ROUTER::markViolations( NODE* aNode, ITEM_SET& aCurrent, NODE::ITEM_VECTOR&
|
|||
|
||||
int clearance;
|
||||
bool removeOriginal = true;
|
||||
bool holeOnly = false; // JEY TODO ( ( itemToMark->Marker() & MK_HOLE )
|
||||
//&& !( itemToMark->Marker() & MK_VIOLATION ) );
|
||||
|
||||
if( holeOnly )
|
||||
clearance = aNode->GetHoleClearance( currentItem, itemToMark );
|
||||
else
|
||||
clearance = aNode->GetClearance( currentItem, itemToMark );
|
||||
clearance = aNode->GetClearance( currentItem, itemToMark );
|
||||
|
||||
if( itemToMark->Layers().IsMultilayer() && !currentItem->Layers().IsMultilayer() )
|
||||
tmp->SetLayer( currentItem->Layer() );
|
||||
|
||||
if( itemToMark->Kind() == ITEM::SOLID_T )
|
||||
{
|
||||
#if 0 // fixme holes
|
||||
if( holeOnly || !m_iface->IsFlashedOnLayer( itemToMark, currentItem->Layer() ) )
|
||||
{
|
||||
SOLID* solid = static_cast<SOLID*>( tmp.get() );
|
||||
|
@ -658,6 +654,7 @@ void ROUTER::markViolations( NODE* aNode, ITEM_SET& aCurrent, NODE::ITEM_VECTOR&
|
|||
removeOriginal = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if( itemToMark->IsCompoundShapePrimitive() )
|
||||
{
|
||||
|
@ -695,7 +692,7 @@ void ROUTER::markViolations( NODE* aNode, ITEM_SET& aCurrent, NODE::ITEM_VECTOR&
|
|||
if( GetDragger() )
|
||||
draggedItems = GetDragger()->Traces();
|
||||
|
||||
for( OBSTACLE& obs : obstacles )
|
||||
for( const OBSTACLE& obs : obstacles )
|
||||
{
|
||||
// Don't mark items being dragged; only board items they collide with
|
||||
if( draggedItems.Contains( obs.m_item ) )
|
||||
|
@ -775,7 +772,7 @@ bool ROUTER::movePlacing( const VECTOR2I& aP, ITEM* aEndItem )
|
|||
{
|
||||
const VIA& via = l->Via();
|
||||
int viaClearance = GetRuleResolver()->Clearance( &via, nullptr );
|
||||
int holeClearance = GetRuleResolver()->HoleClearance( &via, nullptr );
|
||||
int holeClearance = 0; // GetRuleResolver()->HoleClearance( &via, nullptr ); // fixme holes
|
||||
|
||||
if( holeClearance + via.Drill() / 2 > viaClearance + via.Diameter() / 2 )
|
||||
viaClearance = holeClearance + via.Drill() / 2 - via.Diameter() / 2;
|
||||
|
|
|
@ -140,10 +140,12 @@ int SHOVE::getClearance( const ITEM* aA, const ITEM* aB ) const
|
|||
|
||||
int SHOVE::getHoleClearance( const ITEM* aA, const ITEM* aB ) const
|
||||
{
|
||||
if( m_forceClearance >= 0 )
|
||||
/* if( m_forceClearance >= 0 )
|
||||
return m_forceClearance;
|
||||
|
||||
return m_currentNode->GetHoleClearance( aA, aB );
|
||||
return m_currentNode->GetHoleClearance( aA, aB );*/
|
||||
|
||||
return -1; // fixme hole
|
||||
}
|
||||
|
||||
|
||||
|
@ -1342,7 +1344,10 @@ SHOVE::SHOVE_STATUS SHOVE::shoveIteration( int aIter )
|
|||
|
||||
for( ITEM::PnsKind search_order : { ITEM::SOLID_T, ITEM::VIA_T, ITEM::SEGMENT_T } )
|
||||
{
|
||||
nearest = m_currentNode->NearestObstacle( ¤tLine, search_order );
|
||||
COLLISION_SEARCH_OPTIONS opts;
|
||||
opts.m_kindMask = search_order;
|
||||
|
||||
nearest = m_currentNode->NearestObstacle( ¤tLine, opts );
|
||||
|
||||
if( nearest )
|
||||
{
|
||||
|
|
|
@ -37,65 +37,13 @@
|
|||
|
||||
namespace PNS {
|
||||
|
||||
static const SHAPE_LINE_CHAIN buildHullForPrimitiveShape( const SHAPE* aShape, int aClearance,
|
||||
int aWalkaroundThickness )
|
||||
{
|
||||
int cl = aClearance + ( aWalkaroundThickness + 1 )/ 2;
|
||||
|
||||
switch( aShape->Type() )
|
||||
{
|
||||
case SH_RECT:
|
||||
{
|
||||
const SHAPE_RECT* rect = static_cast<const SHAPE_RECT*>( aShape );
|
||||
return OctagonalHull( rect->GetPosition(),
|
||||
rect->GetSize(),
|
||||
cl,
|
||||
0 );
|
||||
}
|
||||
|
||||
case SH_CIRCLE:
|
||||
{
|
||||
const SHAPE_CIRCLE* circle = static_cast<const SHAPE_CIRCLE*>( aShape );
|
||||
int r = circle->GetRadius();
|
||||
return OctagonalHull( circle->GetCenter() - VECTOR2I( r, r ),
|
||||
VECTOR2I( 2 * r, 2 * r ),
|
||||
cl,
|
||||
2.0 * ( 1.0 - M_SQRT1_2 ) * ( r + cl ) );
|
||||
}
|
||||
|
||||
case SH_SEGMENT:
|
||||
{
|
||||
const SHAPE_SEGMENT* seg = static_cast<const SHAPE_SEGMENT*>( aShape );
|
||||
return SegmentHull( *seg, aClearance, aWalkaroundThickness );
|
||||
}
|
||||
|
||||
case SH_ARC:
|
||||
{
|
||||
const SHAPE_ARC* arc = static_cast<const SHAPE_ARC*>( aShape );
|
||||
return ArcHull( *arc, aClearance, aWalkaroundThickness );
|
||||
}
|
||||
|
||||
case SH_SIMPLE:
|
||||
{
|
||||
const SHAPE_SIMPLE* convex = static_cast<const SHAPE_SIMPLE*>( aShape );
|
||||
return ConvexHull( *convex, cl );
|
||||
}
|
||||
|
||||
default:
|
||||
wxFAIL_MSG( wxString::Format( wxT( "Unsupported hull shape: %d (%s)." ),
|
||||
aShape->Type(),
|
||||
SHAPE_TYPE_asString( aShape->Type() ) ) );
|
||||
break;
|
||||
}
|
||||
|
||||
return SHAPE_LINE_CHAIN();
|
||||
}
|
||||
|
||||
|
||||
const SHAPE_LINE_CHAIN SOLID::Hull( int aClearance, int aWalkaroundThickness, int aLayer ) const
|
||||
{
|
||||
if( !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( this, aLayer ) )
|
||||
return HoleHull( aClearance, aWalkaroundThickness, aLayer );
|
||||
//if( !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( this, aLayer ) )
|
||||
// return HoleHull( aClearance, aWalkaroundThickness, aLayer );
|
||||
|
||||
// fixme holes
|
||||
|
||||
if( !m_shape )
|
||||
return SHAPE_LINE_CHAIN();
|
||||
|
@ -106,7 +54,7 @@ const SHAPE_LINE_CHAIN SOLID::Hull( int aClearance, int aWalkaroundThickness, in
|
|||
|
||||
if ( cmpnd->Shapes().size() == 1 )
|
||||
{
|
||||
return buildHullForPrimitiveShape( cmpnd->Shapes()[0], aClearance,
|
||||
return BuildHullForPrimitiveShape( cmpnd->Shapes()[0], aClearance,
|
||||
aWalkaroundThickness );
|
||||
}
|
||||
else
|
||||
|
@ -115,7 +63,7 @@ const SHAPE_LINE_CHAIN SOLID::Hull( int aClearance, int aWalkaroundThickness, in
|
|||
|
||||
for( SHAPE* shape : cmpnd->Shapes() )
|
||||
{
|
||||
hullSet.AddOutline( buildHullForPrimitiveShape( shape, aClearance,
|
||||
hullSet.AddOutline( BuildHullForPrimitiveShape( shape, aClearance,
|
||||
aWalkaroundThickness ) );
|
||||
}
|
||||
|
||||
|
@ -125,42 +73,7 @@ const SHAPE_LINE_CHAIN SOLID::Hull( int aClearance, int aWalkaroundThickness, in
|
|||
}
|
||||
else
|
||||
{
|
||||
return buildHullForPrimitiveShape( m_shape, aClearance, aWalkaroundThickness );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const SHAPE_LINE_CHAIN SOLID::HoleHull( int aClearance, int aWalkaroundThickness, int aLayer ) const
|
||||
{
|
||||
if( !m_hole )
|
||||
return SHAPE_LINE_CHAIN();
|
||||
|
||||
if( m_hole->Type() == SH_COMPOUND )
|
||||
{
|
||||
SHAPE_COMPOUND* cmpnd = static_cast<SHAPE_COMPOUND*>( m_hole );
|
||||
|
||||
if ( cmpnd->Shapes().size() == 1 )
|
||||
{
|
||||
return buildHullForPrimitiveShape( cmpnd->Shapes()[0], aClearance,
|
||||
aWalkaroundThickness );
|
||||
}
|
||||
else
|
||||
{
|
||||
SHAPE_POLY_SET hullSet;
|
||||
|
||||
for( SHAPE* shape : cmpnd->Shapes() )
|
||||
{
|
||||
hullSet.AddOutline( buildHullForPrimitiveShape( shape, aClearance,
|
||||
aWalkaroundThickness ) );
|
||||
}
|
||||
|
||||
hullSet.Simplify( SHAPE_POLY_SET::PM_STRICTLY_SIMPLE );
|
||||
return hullSet.Outline( 0 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return buildHullForPrimitiveShape( m_hole, aClearance, aWalkaroundThickness );
|
||||
return BuildHullForPrimitiveShape( m_shape, aClearance, aWalkaroundThickness );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,6 +84,7 @@ ITEM* SOLID::Clone() const
|
|||
return solid;
|
||||
}
|
||||
|
||||
|
||||
void SOLID::SetPos( const VECTOR2I& aCenter )
|
||||
{
|
||||
VECTOR2I delta = aCenter - m_pos;
|
||||
|
@ -178,6 +92,8 @@ void SOLID::SetPos( const VECTOR2I& aCenter )
|
|||
if( m_shape )
|
||||
m_shape->Move( delta );
|
||||
|
||||
//printf("Hole@%p\n", m_hole);
|
||||
|
||||
if( m_hole )
|
||||
m_hole->Move( delta );
|
||||
|
||||
|
|
|
@ -46,8 +46,10 @@ public:
|
|||
|
||||
~SOLID()
|
||||
{
|
||||
if ( m_hole )
|
||||
delete m_hole;
|
||||
|
||||
delete m_shape;
|
||||
delete m_hole;
|
||||
}
|
||||
|
||||
SOLID( const SOLID& aSolid ) :
|
||||
|
@ -77,26 +79,16 @@ public:
|
|||
|
||||
const SHAPE* Shape() const override { return m_shape; }
|
||||
|
||||
const SHAPE* Hole() const override { return m_hole; }
|
||||
|
||||
const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0,
|
||||
int aLayer = -1 ) const override;
|
||||
|
||||
const SHAPE_LINE_CHAIN HoleHull( int aClearance, int aWalkaroundThickness,
|
||||
int aLayer ) const override;
|
||||
|
||||
void SetShape( SHAPE* shape )
|
||||
{
|
||||
delete m_shape;
|
||||
m_shape = shape;
|
||||
}
|
||||
|
||||
void SetHole( SHAPE* shape )
|
||||
{
|
||||
delete m_hole;
|
||||
m_hole = shape;
|
||||
}
|
||||
|
||||
const VECTOR2I& Pos() const { return m_pos; }
|
||||
void SetPos( const VECTOR2I& aCenter );
|
||||
|
||||
|
@ -119,13 +111,33 @@ public:
|
|||
EDA_ANGLE GetOrientation() const { return m_orientation; }
|
||||
void SetOrientation( const EDA_ANGLE& aOrientation ) { m_orientation = aOrientation; }
|
||||
|
||||
virtual void SetHole( HOLE* aHole ) override
|
||||
{
|
||||
if( m_hole )
|
||||
{
|
||||
assert( m_hole->Owner() == nullptr );
|
||||
}
|
||||
|
||||
m_hole = aHole;
|
||||
m_hole->SetNet( Net() );
|
||||
m_hole->SetOwner( this );
|
||||
|
||||
if( m_hole )
|
||||
{
|
||||
m_hole->SetLayers( m_layers ); // fixme: backdrill vias can have hole layer set different than copper layer set
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool HasHole() const override { return m_hole != nullptr; }
|
||||
virtual HOLE *Hole() const override { return m_hole; }
|
||||
|
||||
private:
|
||||
VECTOR2I m_pos;
|
||||
SHAPE* m_shape;
|
||||
SHAPE* m_hole;
|
||||
VECTOR2I m_offset;
|
||||
int m_padToDie;
|
||||
EDA_ANGLE m_orientation;
|
||||
HOLE* m_hole;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -463,4 +463,62 @@ void HullIntersection( const SHAPE_LINE_CHAIN& hull, const SHAPE_LINE_CHAIN& lin
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
const SHAPE_LINE_CHAIN BuildHullForPrimitiveShape( const SHAPE* aShape, int aClearance,
|
||||
int aWalkaroundThickness )
|
||||
{
|
||||
int cl = aClearance + ( aWalkaroundThickness + 1 )/ 2;
|
||||
|
||||
switch( aShape->Type() )
|
||||
{
|
||||
case SH_RECT:
|
||||
{
|
||||
const SHAPE_RECT* rect = static_cast<const SHAPE_RECT*>( aShape );
|
||||
return OctagonalHull( rect->GetPosition(),
|
||||
rect->GetSize(),
|
||||
cl,
|
||||
0 );
|
||||
}
|
||||
|
||||
case SH_CIRCLE:
|
||||
{
|
||||
const SHAPE_CIRCLE* circle = static_cast<const SHAPE_CIRCLE*>( aShape );
|
||||
int r = circle->GetRadius();
|
||||
return OctagonalHull( circle->GetCenter() - VECTOR2I( r, r ),
|
||||
VECTOR2I( 2 * r, 2 * r ),
|
||||
cl,
|
||||
2.0 * ( 1.0 - M_SQRT1_2 ) * ( r + cl ) );
|
||||
}
|
||||
|
||||
case SH_SEGMENT:
|
||||
{
|
||||
const SHAPE_SEGMENT* seg = static_cast<const SHAPE_SEGMENT*>( aShape );
|
||||
return SegmentHull( *seg, aClearance, aWalkaroundThickness );
|
||||
}
|
||||
|
||||
case SH_ARC:
|
||||
{
|
||||
const SHAPE_ARC* arc = static_cast<const SHAPE_ARC*>( aShape );
|
||||
return ArcHull( *arc, aClearance, aWalkaroundThickness );
|
||||
}
|
||||
|
||||
case SH_SIMPLE:
|
||||
{
|
||||
const SHAPE_SIMPLE* convex = static_cast<const SHAPE_SIMPLE*>( aShape );
|
||||
|
||||
return ConvexHull( *convex, cl );
|
||||
}
|
||||
default:
|
||||
{
|
||||
wxFAIL_MSG( wxString::Format( wxT( "Unsupported hull shape: %d (%s)." ),
|
||||
aShape->Type(),
|
||||
SHAPE_TYPE_asString( aShape->Type() ) ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return SHAPE_LINE_CHAIN();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -64,6 +64,8 @@ OPT_BOX2I ChangedArea( const LINE& aLineA, const LINE& aLineB );
|
|||
void HullIntersection( const SHAPE_LINE_CHAIN& hull, const SHAPE_LINE_CHAIN& line,
|
||||
SHAPE_LINE_CHAIN::INTERSECTIONS& ips );
|
||||
|
||||
const SHAPE_LINE_CHAIN BuildHullForPrimitiveShape( const SHAPE* aShape, int aClearance,
|
||||
int aWalkaroundThickness );
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ bool VIA::PushoutForce( NODE* aNode, const ITEM* aOther, VECTOR2I& aForce )
|
|||
VECTOR2I elementForces[4], force;
|
||||
size_t nf = 0;
|
||||
|
||||
#if 0
|
||||
if( aNode->GetCollisionQueryScope() == NODE::CQS_ALL_RULES )
|
||||
{
|
||||
int holeClearance = aNode->GetHoleClearance( this, aOther );
|
||||
|
@ -49,6 +50,7 @@ bool VIA::PushoutForce( NODE* aNode, const ITEM* aOther, VECTOR2I& aForce )
|
|||
|
||||
aOther->Shape()->Collide( Hole(), holeClearance, &elementForces[nf++] );
|
||||
}
|
||||
#endif
|
||||
|
||||
aOther->Shape()->Collide( Shape(), clearance, &elementForces[nf++] );
|
||||
|
||||
|
@ -155,7 +157,7 @@ const SHAPE_LINE_CHAIN VIA::Hull( int aClearance, int aWalkaroundThickness, int
|
|||
int width = m_diameter;
|
||||
|
||||
if( !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( this, aLayer ) )
|
||||
width = m_hole.GetRadius() * 2;
|
||||
width = m_hole->Radius() * 2;
|
||||
|
||||
// Chamfer = width * ( 1 - sqrt(2)/2 ) for equilateral octagon
|
||||
return OctagonalHull( m_pos - VECTOR2I( width / 2, width / 2 ),
|
||||
|
@ -164,16 +166,17 @@ const SHAPE_LINE_CHAIN VIA::Hull( int aClearance, int aWalkaroundThickness, int
|
|||
}
|
||||
|
||||
|
||||
#if 0
|
||||
const SHAPE_LINE_CHAIN VIA::HoleHull( int aClearance, int aWalkaroundThickness, int aLayer ) const
|
||||
{
|
||||
int cl = ( aClearance + aWalkaroundThickness / 2 );
|
||||
int width = m_hole.GetRadius() * 2;
|
||||
int width = m_hole->GetRadius() * 2;
|
||||
|
||||
// Chamfer = width * ( 1 - sqrt(2)/2 ) for equilateral octagon
|
||||
return OctagonalHull( m_pos - VECTOR2I( width / 2, width / 2 ), VECTOR2I( width, width ), cl,
|
||||
( 2 * cl + width ) * ( 1.0 - M_SQRT1_2 ) );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
VIA* VIA::Clone() const
|
||||
{
|
||||
|
@ -185,7 +188,7 @@ VIA* VIA::Clone() const
|
|||
v->m_diameter = m_diameter;
|
||||
v->m_drill = m_drill;
|
||||
v->m_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 );
|
||||
v->m_hole = SHAPE_CIRCLE( m_pos, m_drill / 2 );
|
||||
v->m_hole = m_hole->Clone();
|
||||
v->m_rank = m_rank;
|
||||
v->m_marker = m_marker;
|
||||
v->m_viaType = m_viaType;
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
|
||||
#include "pns_item.h"
|
||||
#include "pns_linked_item.h"
|
||||
#include "pns_hole.h"
|
||||
|
||||
namespace PNS {
|
||||
|
||||
|
@ -57,6 +58,7 @@ public:
|
|||
m_viaType = VIATYPE::THROUGH;
|
||||
m_isFree = false;
|
||||
m_isVirtual = false;
|
||||
m_hole = nullptr;
|
||||
}
|
||||
|
||||
VIA( const VECTOR2I& aPos, const LAYER_RANGE& aLayers, int aDiameter, int aDrill,
|
||||
|
@ -69,7 +71,8 @@ public:
|
|||
m_diameter = aDiameter;
|
||||
m_drill = aDrill;
|
||||
m_shape = SHAPE_CIRCLE( aPos, aDiameter / 2 );
|
||||
m_hole = SHAPE_CIRCLE( m_pos, aDrill / 2 );
|
||||
m_hole = HOLE::MakeCircularHole( m_pos, aDrill / 2 );
|
||||
m_hole->SetNet( aNet );
|
||||
m_viaType = aViaType;
|
||||
m_isFree = false;
|
||||
m_isVirtual = false;
|
||||
|
@ -83,7 +86,7 @@ public:
|
|||
m_pos = aB.m_pos;
|
||||
m_diameter = aB.m_diameter;
|
||||
m_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 );
|
||||
m_hole = SHAPE_CIRCLE( m_pos, aB.m_drill / 2 );
|
||||
m_hole = aB.m_hole->Clone();
|
||||
m_marker = aB.m_marker;
|
||||
m_rank = aB.m_rank;
|
||||
m_drill = aB.m_drill;
|
||||
|
@ -92,6 +95,12 @@ public:
|
|||
m_isVirtual = aB.m_isVirtual;
|
||||
}
|
||||
|
||||
virtual ~VIA()
|
||||
{
|
||||
if ( m_hole )
|
||||
delete m_hole;
|
||||
}
|
||||
|
||||
static inline bool ClassOf( const ITEM* aItem )
|
||||
{
|
||||
return aItem && VIA_T == aItem->Kind();
|
||||
|
@ -103,7 +112,8 @@ public:
|
|||
{
|
||||
m_pos = aPos;
|
||||
m_shape.SetCenter( aPos );
|
||||
m_hole.SetCenter( aPos );
|
||||
if( m_hole )
|
||||
m_hole->SetCenter( aPos );
|
||||
}
|
||||
|
||||
VIATYPE ViaType() const { return m_viaType; }
|
||||
|
@ -122,7 +132,8 @@ public:
|
|||
void SetDrill( int aDrill )
|
||||
{
|
||||
m_drill = aDrill;
|
||||
m_hole.SetRadius( m_drill / 2 );
|
||||
if( m_hole )
|
||||
m_hole->SetRadius( m_drill / 2 );
|
||||
}
|
||||
|
||||
bool IsFree() const { return m_isFree; }
|
||||
|
@ -135,17 +146,11 @@ public:
|
|||
|
||||
const SHAPE* Shape() const override { return &m_shape; }
|
||||
|
||||
const SHAPE_CIRCLE* Hole() const override { return &m_hole; }
|
||||
void SetHole( const SHAPE_CIRCLE& aHole ) { m_hole = aHole; }
|
||||
|
||||
VIA* Clone() const override;
|
||||
|
||||
const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0,
|
||||
int aLayer = -1 ) const override;
|
||||
|
||||
const SHAPE_LINE_CHAIN HoleHull( int aClearance = 0, int aWalkaroundThickness = 0,
|
||||
int aLayer = -1 ) const override;
|
||||
|
||||
virtual VECTOR2I Anchor( int n ) const override
|
||||
{
|
||||
return m_pos;
|
||||
|
@ -160,6 +165,26 @@ public:
|
|||
|
||||
const VIA_HANDLE MakeHandle() const;
|
||||
|
||||
virtual void SetHole( HOLE* aHole ) override
|
||||
{
|
||||
if( m_hole )
|
||||
{
|
||||
assert( m_hole->Owner() == nullptr );
|
||||
}
|
||||
|
||||
m_hole = aHole;
|
||||
m_hole->SetNet( Net() );
|
||||
m_hole->SetOwner( this );
|
||||
|
||||
if( m_hole )
|
||||
{
|
||||
m_hole->SetLayers( m_layers ); // fixme: backdrill vias can have hole layer set different than copper layer set
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool HasHole() const override { return true; }
|
||||
virtual HOLE *Hole() const override { return m_hole; }
|
||||
|
||||
virtual const std::string Format() const override;
|
||||
|
||||
private:
|
||||
|
@ -167,10 +192,9 @@ private:
|
|||
int m_drill;
|
||||
VECTOR2I m_pos;
|
||||
SHAPE_CIRCLE m_shape;
|
||||
SHAPE_CIRCLE m_hole;
|
||||
VIATYPE m_viaType;
|
||||
bool m_isFree;
|
||||
|
||||
HOLE* m_hole;
|
||||
};
|
||||
|
||||
|
||||
|
@ -181,7 +205,7 @@ public:
|
|||
VIA( aPos, LAYER_RANGE( aLayer, aLayer ), aDiameter, aDiameter / 2, aNet )
|
||||
{
|
||||
m_isVirtual = true;
|
||||
SetHole( SHAPE_CIRCLE( Pos(), 1 ) );
|
||||
//SetHole( SHAPE_CIRCLE( Pos(), 1 ) );
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -43,12 +43,17 @@ void WALKAROUND::start( const LINE& aInitialPath )
|
|||
NODE::OPT_OBSTACLE WALKAROUND::nearestObstacle( const LINE& aPath )
|
||||
{
|
||||
COLLISION_SEARCH_OPTIONS opts;
|
||||
|
||||
opts.m_kindMask = m_itemMask;
|
||||
|
||||
if( ! m_restrictedSet.empty() )
|
||||
opts.m_restrictedSet = &m_restrictedSet;
|
||||
else
|
||||
opts.m_restrictedSet = nullptr;
|
||||
|
||||
opts.m_useClearanceEpsilon = false;
|
||||
|
||||
NODE::OPT_OBSTACLE obs = m_world->NearestObstacle( &aPath, m_itemMask,
|
||||
m_restrictedSet.empty() ? nullptr
|
||||
: &m_restrictedSet,
|
||||
opts );
|
||||
NODE::OPT_OBSTACLE obs = m_world->NearestObstacle( &aPath, opts );
|
||||
|
||||
if( m_restrictedSet.empty() )
|
||||
return obs;
|
||||
|
@ -91,14 +96,13 @@ WALKAROUND::WALKAROUND_STATUS WALKAROUND::singleStep( LINE& aPath, bool aWinding
|
|||
|
||||
SHAPE_LINE_CHAIN path_walk;
|
||||
|
||||
|
||||
SHAPE_LINE_CHAIN hull = current_obs->m_item->Hull( current_obs->m_clearance,
|
||||
current_obs->m_walkaroundThickness );
|
||||
|
||||
SHAPE_LINE_CHAIN hull = current_obs->m_item->Hull( current_obs->m_clearance, aPath.Width() );
|
||||
|
||||
bool s_cw = aPath.Walkaround( hull, path_walk, aWindingDirection );
|
||||
|
||||
PNS_DBG( Dbg(), BeginGroup, "hull/walk", 1 );
|
||||
PNS_DBG( Dbg(), AddShape, &hull, RED, 0, wxString::Format( "hull-%s-%d", aWindingDirection ? wxT( "cw" ) : wxT( "ccw" ), m_iteration ) );
|
||||
PNS_DBG( Dbg(), AddShape, &hull, RED, 0, wxString::Format( "hull-%s-%d-cl %d", aWindingDirection ? wxT( "cw" ) : wxT( "ccw" ), m_iteration, current_obs->m_clearance ) );
|
||||
PNS_DBG( Dbg(), AddShape, &aPath.CLine(), GREEN, 0, wxString::Format( "path-%s-%d", aWindingDirection ? wxT( "cw" ) : wxT( "ccw" ), m_iteration ) );
|
||||
PNS_DBG( Dbg(), AddShape, &path_walk, BLUE, 0, wxString::Format( "result-%s-%d", aWindingDirection ? wxT( "cw" ) : wxT( "ccw" ), m_iteration ) );
|
||||
PNS_DBG( Dbg(), Message, wxString::Format( wxT( "Stat cw %d" ), !!s_cw ) );
|
||||
|
@ -180,6 +184,12 @@ const WALKAROUND::RESULT WALKAROUND::Route( const LINE& aInitialPath )
|
|||
if( s_cw != IN_PROGRESS && s_ccw != IN_PROGRESS )
|
||||
break;
|
||||
|
||||
double lcw = path_cw.Line().Length() / (double)aInitialPath.CLine().Length();
|
||||
double lccw = path_ccw.Line().Length() / (double)aInitialPath.CLine().Length();
|
||||
|
||||
PNS_DBG( Dbg(), Message, wxString::Format( wxT( "lcw %.1f lccw %.1f" ), lcw, lccw ) );
|
||||
|
||||
|
||||
// Safety valve
|
||||
if( m_lengthLimitOn && path_cw.Line().Length() > lengthLimit && path_ccw.Line().Length() > lengthLimit )
|
||||
break;
|
||||
|
|
|
@ -38,12 +38,26 @@ using namespace KIGFX;
|
|||
|
||||
|
||||
ROUTER_PREVIEW_ITEM::ROUTER_PREVIEW_ITEM( const PNS::ITEM* aItem, KIGFX::VIEW* aView ) :
|
||||
EDA_ITEM( NOT_USED )
|
||||
EDA_ITEM( NOT_USED ),
|
||||
m_view( aView ),
|
||||
m_shape( nullptr ),
|
||||
m_hole( nullptr )
|
||||
{
|
||||
m_view = aView;
|
||||
BOARD_ITEM* boardItem = aItem ? aItem->Parent() : nullptr;
|
||||
|
||||
m_shape = aItem ? aItem->Shape()->Clone() : nullptr;
|
||||
m_hole = aItem && aItem->Hole() ? aItem->Hole()->Clone() : nullptr;
|
||||
// A PNS::SOLID for an edge-cut item must have 0 width for collision calculations, but when
|
||||
// highlighting an edge we want to show it with its parent PCB_SHAPE's shape.
|
||||
if( boardItem && boardItem->IsOnLayer( Edge_Cuts ) )
|
||||
{
|
||||
m_shape = boardItem->GetEffectiveShape()->Clone();
|
||||
}
|
||||
else if( aItem )
|
||||
{
|
||||
m_shape = aItem->Shape()->Clone();
|
||||
|
||||
if( aItem->Hole() )
|
||||
m_hole = aItem->Hole()->Shape()->Clone();
|
||||
}
|
||||
|
||||
m_clearance = -1;
|
||||
m_originLayer = m_layer = LAYER_SELECT_OVERLAY ;
|
||||
|
@ -145,7 +159,7 @@ void ROUTER_PREVIEW_ITEM::Update( const PNS::ITEM* aItem )
|
|||
m_hole = nullptr;
|
||||
|
||||
if( aItem->Hole() )
|
||||
m_hole = aItem->Hole()->Clone();
|
||||
m_hole = aItem->Hole()->Shape()->Clone();
|
||||
|
||||
break;
|
||||
|
||||
|
|
|
@ -96,6 +96,8 @@ std::unique_ptr<BOARD> ReadBoardFromFileOrStream( const std::string& aFilename,
|
|||
std::istream* in_stream = nullptr;
|
||||
std::ifstream file_stream;
|
||||
|
||||
printf("RD from %s\n", aFilename.c_str() );
|
||||
|
||||
if( aFilename.empty() )
|
||||
{
|
||||
// no file, read stdin
|
||||
|
|
|
@ -35,6 +35,7 @@ set( QA_PCBNEW_SRCS
|
|||
test_board_item.cpp
|
||||
test_graphics_import_mgr.cpp
|
||||
test_lset.cpp
|
||||
test_pns_basics.cpp
|
||||
test_pad_numbering.cpp
|
||||
test_libeval_compiler.cpp
|
||||
test_save_load.cpp
|
||||
|
|
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2021-2023 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#include <qa_utils/wx_utils/unit_test_utils.h>
|
||||
#include <settings/settings_manager.h>
|
||||
|
||||
#include <pcbnew/pad.h>
|
||||
#include <pcbnew/pcb_track.h>
|
||||
|
||||
#include <router/pns_node.h>
|
||||
#include <router/pns_router.h>
|
||||
#include <router/pns_item.h>
|
||||
#include <router/pns_via.h>
|
||||
#include <router/pns_kicad_iface.h>
|
||||
|
||||
static bool isCopper( const PNS::ITEM* aItem )
|
||||
{
|
||||
if( !aItem )
|
||||
return false;
|
||||
|
||||
BOARD_ITEM* parent = aItem->Parent();
|
||||
|
||||
if ( !parent )
|
||||
{
|
||||
return LSET::AllCuMask().Contains( (PCB_LAYER_ID) aItem->Layer() );
|
||||
}
|
||||
|
||||
if( parent && parent->Type() == PCB_PAD_T )
|
||||
{
|
||||
PAD* pad = static_cast<PAD*>( parent );
|
||||
return pad->IsOnCopperLayer() && pad->GetAttribute() != PAD_ATTRIB::NPTH;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool isHole( const PNS::ITEM* aItem )
|
||||
{
|
||||
if( !aItem )
|
||||
return false;
|
||||
|
||||
return aItem->OfKind( PNS::ITEM::HOLE_T );
|
||||
}
|
||||
|
||||
|
||||
static bool isEdge( const PNS::ITEM* aItem )
|
||||
{
|
||||
if( !aItem )
|
||||
return false;
|
||||
|
||||
const BOARD_ITEM* parent = aItem->Parent();
|
||||
|
||||
if ( !parent )
|
||||
{
|
||||
return aItem->Layer() == Edge_Cuts;
|
||||
}
|
||||
|
||||
return parent && ( parent->IsOnLayer( Edge_Cuts ) || parent->IsOnLayer( Margin ) );
|
||||
}
|
||||
|
||||
|
||||
class MOCK_RULE_RESOLVER : public PNS::RULE_RESOLVER
|
||||
{
|
||||
public:
|
||||
MOCK_RULE_RESOLVER() : m_clearanceEpsilon( 10 )
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~MOCK_RULE_RESOLVER() {}
|
||||
|
||||
virtual int Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB ) override
|
||||
{
|
||||
PNS::CONSTRAINT constraint;
|
||||
int rv = -1;
|
||||
int layer;
|
||||
|
||||
if( !aA->Layers().IsMultilayer() || !aB || aB->Layers().IsMultilayer() )
|
||||
layer = aA->Layer();
|
||||
else
|
||||
layer = aB->Layer();
|
||||
|
||||
if( isHole( aA ) && isHole( aB ) )
|
||||
{
|
||||
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE, aA, aB, layer,
|
||||
&constraint ) )
|
||||
rv = constraint.m_Value.Min();
|
||||
}
|
||||
else if( isHole( aA ) || isHole( aB ) )
|
||||
{
|
||||
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE, aA, aB, layer,
|
||||
&constraint ) )
|
||||
rv = constraint.m_Value.Min();
|
||||
}
|
||||
else if( isCopper( aA ) && ( !aB || isCopper( aB ) ) )
|
||||
{
|
||||
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, layer, &constraint ) )
|
||||
rv = constraint.m_Value.Min();
|
||||
}
|
||||
else if( isEdge( aA ) || ( aB && isEdge( aB ) ) )
|
||||
{
|
||||
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE, aA, aB, layer,
|
||||
&constraint ) )
|
||||
{
|
||||
if( constraint.m_Value.Min() > rv )
|
||||
rv = constraint.m_Value.Min();
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
virtual int DpCoupledNet( int aNet ) override { return -1; }
|
||||
virtual int DpNetPolarity( int aNet ) override { return -1; }
|
||||
virtual bool DpNetPair( const PNS::ITEM* aItem, int& aNetP, int& aNetN ) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
virtual bool IsDiffPair( const PNS::ITEM* aA, const PNS::ITEM* aB ) override { return false; }
|
||||
|
||||
virtual bool QueryConstraint( PNS::CONSTRAINT_TYPE aType, const PNS::ITEM* aItemA,
|
||||
const PNS::ITEM* aItemB, int aLayer,
|
||||
PNS::CONSTRAINT* aConstraint ) override
|
||||
{
|
||||
ITEM_KEY key;
|
||||
|
||||
key.a = aItemA;
|
||||
key.b = aItemB;
|
||||
key.type = aType;
|
||||
|
||||
auto it = m_ruleMap.find( key );
|
||||
if( it == m_ruleMap.end() )
|
||||
{
|
||||
int cl;
|
||||
switch( aType )
|
||||
{
|
||||
case PNS::CONSTRAINT_TYPE::CT_CLEARANCE: cl = m_defaultClearance; break;
|
||||
case PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE: cl = m_defaultHole2Hole; break;
|
||||
case PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE: cl = m_defaultHole2Copper; break;
|
||||
default: return false;
|
||||
}
|
||||
|
||||
//printf("GetDef %s %s %d cl %d\n", aItemA->KindStr().c_str(), aItemB->KindStr().c_str(), aType, cl );
|
||||
|
||||
aConstraint->m_Type = aType;
|
||||
aConstraint->m_Value.SetMin( cl );
|
||||
|
||||
return true;
|
||||
} else {
|
||||
*aConstraint = it->second;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual wxString NetName( int aNet ) override { return wxT( "noname" ); }
|
||||
|
||||
int ClearanceEpsilon() const override { return m_clearanceEpsilon; }
|
||||
|
||||
struct ITEM_KEY
|
||||
{
|
||||
const PNS::ITEM* a = nullptr;
|
||||
const PNS::ITEM* b = nullptr;
|
||||
PNS::CONSTRAINT_TYPE type;
|
||||
|
||||
bool operator==( const ITEM_KEY& other ) const
|
||||
{
|
||||
return a == other.a && b == other.b && type == other.type;
|
||||
}
|
||||
|
||||
bool operator<( const ITEM_KEY& other ) const
|
||||
{
|
||||
if( a < other.a )
|
||||
return true;
|
||||
else if ( a == other.a )
|
||||
{
|
||||
if( b < other.b )
|
||||
return true;
|
||||
else if ( b == other.b )
|
||||
{
|
||||
return type < other.type;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
virtual bool IsInNetTie( const PNS::ITEM* aA ) override { return false; }
|
||||
virtual bool IsNetTieExclusion( const PNS::ITEM* aItem, const VECTOR2I& aCollisionPos,
|
||||
const PNS::ITEM* aCollidingItem ) override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void AddMockRule( PNS::CONSTRAINT_TYPE aType, const PNS::ITEM* aItemA, const PNS::ITEM* aItemB,
|
||||
PNS::CONSTRAINT aConstraint )
|
||||
{
|
||||
ITEM_KEY key;
|
||||
|
||||
key.a = aItemA;
|
||||
key.b = aItemB;
|
||||
key.type = aType;
|
||||
|
||||
m_ruleMap[key] = aConstraint;
|
||||
}
|
||||
|
||||
int m_defaultClearance = 200000;
|
||||
int m_defaultHole2Hole = 220000;
|
||||
int m_defaultHole2Copper = 210000;
|
||||
|
||||
private:
|
||||
std::map<ITEM_KEY, PNS::CONSTRAINT> m_ruleMap;
|
||||
|
||||
int m_clearanceEpsilon;
|
||||
};
|
||||
|
||||
struct PNS_TEST_FIXTURE;
|
||||
|
||||
class MOCK_PNS_KICAD_IFACE : public PNS_KICAD_IFACE_BASE
|
||||
{
|
||||
public:
|
||||
MOCK_PNS_KICAD_IFACE( PNS_TEST_FIXTURE *aFixture ) :
|
||||
m_testFixture( aFixture ) {}
|
||||
~MOCK_PNS_KICAD_IFACE() {}
|
||||
|
||||
void HideItem( PNS::ITEM* aItem ) override {};
|
||||
void DisplayItem( const PNS::ITEM* aItem, int aClearance, bool aEdit = false ) override {};
|
||||
PNS::RULE_RESOLVER* GetRuleResolver() override;
|
||||
|
||||
private:
|
||||
PNS_TEST_FIXTURE* m_testFixture;
|
||||
};
|
||||
|
||||
|
||||
struct PNS_TEST_FIXTURE
|
||||
{
|
||||
PNS_TEST_FIXTURE() : m_settingsManager( true /* headless */ )
|
||||
{
|
||||
m_router = new PNS::ROUTER;
|
||||
m_iface = new MOCK_PNS_KICAD_IFACE( this );
|
||||
m_router->SetInterface( m_iface );
|
||||
}
|
||||
|
||||
SETTINGS_MANAGER m_settingsManager;
|
||||
PNS::ROUTER* m_router;
|
||||
MOCK_RULE_RESOLVER m_ruleResolver;
|
||||
MOCK_PNS_KICAD_IFACE *m_iface;
|
||||
//std::unique_ptr<BOARD> m_board;
|
||||
};
|
||||
|
||||
|
||||
PNS::RULE_RESOLVER* MOCK_PNS_KICAD_IFACE::GetRuleResolver()
|
||||
{
|
||||
return &m_testFixture->m_ruleResolver;
|
||||
}
|
||||
|
||||
static void dumpObstacles( const PNS::NODE::OBSTACLES &obstacles )
|
||||
{
|
||||
for ( const auto& obs : obstacles )
|
||||
{
|
||||
printf("%p [%s] - %p [%s], clearance %d\n", obs.m_head, obs.m_head->KindStr().c_str(),
|
||||
obs.m_item, obs.m_item->KindStr().c_str(),
|
||||
obs.m_clearance );
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE( PNSHoleCollisions, PNS_TEST_FIXTURE )
|
||||
{
|
||||
PNS::VIA* v1 = new PNS::VIA ( VECTOR2I( 0, 1000000 ), LAYER_RANGE( F_Cu, B_Cu ), 500000, 100000 );
|
||||
PNS::VIA* v2 = new PNS::VIA ( VECTOR2I( 0, 2000000 ), LAYER_RANGE( F_Cu, B_Cu ), 500000, 100000 );
|
||||
|
||||
std::unique_ptr<PNS::NODE> world ( new PNS::NODE );
|
||||
|
||||
world->SetMaxClearance( 10000000 );
|
||||
world->SetRuleResolver( &m_ruleResolver );
|
||||
|
||||
world->AddRaw( v1 );
|
||||
world->AddRaw( v2 );
|
||||
|
||||
BOOST_TEST_MESSAGE( "via to via, no violations" );
|
||||
{
|
||||
PNS::NODE::OBSTACLES obstacles;
|
||||
int count = world->QueryColliding( v1, obstacles );
|
||||
BOOST_CHECK_EQUAL( obstacles.size(), 0 );
|
||||
BOOST_CHECK_EQUAL( count, 0 );
|
||||
}
|
||||
|
||||
BOOST_TEST_MESSAGE( "via to via, forced copper to copper violation" );
|
||||
{
|
||||
PNS::NODE::OBSTACLES obstacles;
|
||||
m_ruleResolver.m_defaultClearance = 1000000;
|
||||
world->QueryColliding( v1, obstacles );
|
||||
dumpObstacles( obstacles );
|
||||
|
||||
BOOST_CHECK_EQUAL( obstacles.size(), 1 );
|
||||
const auto first = *obstacles.begin();
|
||||
|
||||
BOOST_CHECK_EQUAL( first.m_head, v1 );
|
||||
BOOST_CHECK_EQUAL( first.m_item, v2 );
|
||||
BOOST_CHECK_EQUAL( first.m_clearance, m_ruleResolver.m_defaultClearance );
|
||||
}
|
||||
|
||||
BOOST_TEST_MESSAGE( "via to via, forced copper to hole violation" );
|
||||
{
|
||||
PNS::NODE::OBSTACLES obstacles;
|
||||
m_ruleResolver.m_defaultClearance = 200000;
|
||||
m_ruleResolver.m_defaultHole2Copper = 1000000;
|
||||
|
||||
world->QueryColliding( v1, obstacles );
|
||||
dumpObstacles( obstacles );
|
||||
|
||||
|
||||
|
||||
BOOST_CHECK_EQUAL( obstacles.size(), 2 );
|
||||
auto iter = obstacles.begin();
|
||||
const auto first = *iter++;
|
||||
const auto second = *iter;
|
||||
|
||||
BOOST_CHECK_EQUAL( first.m_head, v1 );
|
||||
BOOST_CHECK_EQUAL( first.m_item, v2 );
|
||||
BOOST_CHECK_EQUAL( first.m_clearance, m_ruleResolver.m_defaultHole2Copper );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue