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.)
This commit is contained in:
Tomasz Wlostowski 2022-08-30 13:52:34 +01:00 committed by Jeff Young
parent 507a25f150
commit bfbda978b8
24 changed files with 1335 additions and 414 deletions

View File

@ -49,21 +49,6 @@ static const SHAPE* shapeFunctor( T aItem )
return aItem->Shape(); 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. * 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(); BOX2I bbox = shapeFunctor( aObject )->BBox();
if( holeFunctor( aObject ) )
bbox.Merge( holeFunctor( aObject )->BBox() );
return bbox; return bbox;
} }

148
pcbnew/router/pns_hole.cpp Normal file
View File

@ -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

79
pcbnew/router/pns_hole.h Normal file
View File

@ -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

View File

@ -24,41 +24,72 @@
#include "pns_item.h" #include "pns_item.h"
#include "pns_line.h" #include "pns_line.h"
#include "pns_router.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; typedef VECTOR2I::extended_type ecoord;
namespace PNS { namespace PNS {
bool ITEM::collideSimple( const ITEM* aOther, const NODE* aNode, static void dumpObstacles( const PNS::NODE::OBSTACLES &obstacles )
const COLLISION_SEARCH_OPTIONS& aOpts, OBSTACLE *aObsInfo ) const
{ {
const ROUTER_IFACE* iface = ROUTER::GetInstance()->GetInterface(); printf("&&&& %d obstacles: \n", obstacles.size() );
const SHAPE* shapeA = Shape();
const SHAPE* holeA = Hole(); for( const auto& obs : obstacles )
int lineWidthA = 0; {
const SHAPE* shapeB = aOther->Shape(); printf("%p [%s] - %p [%s], clearance %d\n", obs.m_head, obs.m_head->KindStr().c_str(),
const SHAPE* holeB = aOther->Hole(); obs.m_item, obs.m_item->KindStr().c_str(),
int lineWidthB = 0; obs.m_clearance );
const int clearanceEpsilon = aNode->GetRuleResolver()->ClearanceEpsilon(); }
}
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 // Sadly collision routines ignore SHAPE_POLY_LINE widths so we have to pass them in as part
// of the clearance value. // of the clearance value.
if( m_kind == LINE_T ) 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 ) if( aHead->m_kind == LINE_T )
lineWidthB = static_cast<const LINE*>( aOther )->Width() / 2; lineWidthH = static_cast<const LINE*>( aHead )->Width() / 2;
// same nets? no collision! // 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; return false;
}
// a pad associated with a "free" pin (NIC) doesn't have a net until it has been used // 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; return false;
}
// check if we are not on completely different layers first // 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; return false;
auto checkKeepout = auto checkKeepout =
@ -84,74 +115,143 @@ bool ITEM::collideSimple( const ITEM* aOther, const NODE* aNode,
}; };
const ZONE* zoneA = dynamic_cast<ZONE*>( Parent() ); 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; return false;
if( zoneB && Parent() && !checkKeepout( zoneB, Parent() ) ) if( zoneB && Parent() && !checkKeepout( zoneB, Parent() ) )
return false; return false;
bool thisNotFlashed = !iface->IsFlashedOnLayer( this, aOther->Layer() ); // fixme: this f***ing singleton must go...
bool otherNotFlashed = !iface->IsFlashedOnLayer( aOther, Layer() ); 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; thisNotFlashed = !iface->IsFlashedOnLayer( this, aHead->Layer() );
aObsInfo->m_itemIsHole = false; otherNotFlashed = !iface->IsFlashedOnLayer( aHead, Layer() );
} }
if( ( aNode->GetCollisionQueryScope() == NODE::CQS_ALL_RULES if( ( aNode->GetCollisionQueryScope() == NODE::CQS_ALL_RULES
|| ( thisNotFlashed || otherNotFlashed ) ) || ( thisNotFlashed || otherNotFlashed ) ) )
&& ( holeA || holeB ) )
{ {
int holeClearance = aNode->GetHoleClearance( this, aOther ); if( holeI && holeI->ParentPadVia() != aHead && holeI != aHead )
if( holeA && holeA->Collide( shapeB, holeClearance + lineWidthB - clearanceEpsilon ) )
{ {
if( aObsInfo ) int holeClearance = aNode->GetClearance( this, holeI );
{ printf("HCH1 %d\n", holeClearance);
aObsInfo->m_headIsHole = true;
aObsInfo->m_clearance = holeClearance;
}
return true;
}
if( holeB && holeB->Collide( shapeA, holeClearance + lineWidthA - clearanceEpsilon ) ) if( holeI->Shape()->Collide( shapeH, holeClearance + lineWidthH - clearanceEpsilon ) )
{
if( aObsInfo )
{ {
aObsInfo->m_itemIsHole = true; if( aCtx )
aObsInfo->m_clearance = holeClearance;
}
return true;
}
if( holeA && holeB )
{
int holeToHoleClearance = aNode->GetHoleToHoleClearance( this, aOther );
if( holeA->Collide( holeB, holeToHoleClearance - clearanceEpsilon ) )
{
if( aObsInfo )
{ {
aObsInfo->m_headIsHole = true; OBSTACLE obs;
aObsInfo->m_itemIsHole = true; obs.m_clearance = holeClearance;
aObsInfo->m_clearance = holeToHoleClearance; 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; return false;
if( !Layers().IsMultilayer() && otherNotFlashed ) if( !Layers().IsMultilayer() && otherNotFlashed )
return false; return false;
int clearance = aOpts.m_overrideClearance >= 0 ? aOpts.m_overrideClearance int clearance;
: aNode->GetClearance( this, aOther );
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 ) if( clearance >= 0 )
{ {
@ -164,41 +264,69 @@ bool ITEM::collideSimple( const ITEM* aOther, const NODE* aNode,
int actual; int actual;
VECTOR2I pos; 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 ) ) if( checkCastellation && aNode->QueryEdgeExclusions( pos ) )
return false; return false;
if( checkNetTie && aNode->GetRuleResolver()->IsNetTieExclusion( aOther, pos, this ) ) if( checkNetTie && aNode->GetRuleResolver()->IsNetTieExclusion( aHead, pos, this ) )
return false; return false;
if( aObsInfo ) if( aCtx )
aObsInfo->m_clearance = clearance; {
collisionsFound = true;
return 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 else
{ {
// Fast method // Fast method
if( shapeA->Collide( shapeB, clearance + lineWidthA + lineWidthB - clearanceEpsilon ) ) if( shapeH->Collide( shapeI, clearance + lineWidthH + lineWidthI - clearanceEpsilon ) )
{ {
if( aObsInfo ) if( aCtx )
aObsInfo->m_clearance = clearance; {
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, bool ITEM::Collide( const ITEM* aOther, const NODE* aNode, COLLISION_SEARCH_CONTEXT *aCtx ) const
const COLLISION_SEARCH_OPTIONS& aOpts, OBSTACLE *aObsInfo ) const
{ {
if( collideSimple( aOther, aNode, aOpts, aObsInfo ) ) if( collideSimple( aOther, aNode, aCtx ) )
return true; return true;
// Special cases for "head" lines with vias attached at the end. Note that this does not // 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 ); 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; return true;
} }
if( aOther->m_kind == LINE_T ) 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; return true;
} }
@ -236,6 +364,8 @@ std::string ITEM::KindStr() const
case JOINT_T: return "joint"; case JOINT_T: return "joint";
case SOLID_T: return "solid"; case SOLID_T: return "solid";
case DIFF_PAIR_T: return "diff-pair"; case DIFF_PAIR_T: return "diff-pair";
case HOLE_T: return "hole";
default: return "unknown"; 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 const std::string ITEM::Format() const
{ {
std::stringstream ss; std::stringstream ss;

View File

@ -24,6 +24,7 @@
#define __PNS_ITEM_H #define __PNS_ITEM_H
#include <memory> #include <memory>
#include <unordered_set>
#include <math/vector2d.h> #include <math/vector2d.h>
#include <geometry/shape.h> #include <geometry/shape.h>
@ -44,28 +45,13 @@ enum LineMarker {
MK_DP_COUPLED = ( 1 << 5 ) 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 {}; 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 class OWNABLE_ITEM
{ {
public: public:
@ -95,7 +81,6 @@ protected:
const ITEM_OWNER *m_owner; const ITEM_OWNER *m_owner;
}; };
/** /**
* Base class for PNS router board items. * Base class for PNS router board items.
* *
@ -117,7 +102,8 @@ public:
ARC_T = 16, ARC_T = 16,
VIA_T = 32, VIA_T = 32,
DIFF_PAIR_T = 64, DIFF_PAIR_T = 64,
ANY_T = 0xff HOLE_T = 128,
ANY_T = 0xffff
}; };
ITEM( PnsKind aKind ) ITEM( PnsKind aKind )
@ -170,12 +156,6 @@ public:
return SHAPE_LINE_CHAIN(); 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. * Return the type (kind) of the item.
*/ */
@ -216,7 +196,7 @@ public:
{ {
return Layers().Overlaps( aOther->Layers() ); return Layers().Overlaps( aOther->Layers() );
} }
/** /**
* Check for a collision (clearance violation) with between us and item \a aOther. * 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. * @param aOther is the item to check collision against.
* @return true, if a collision was found. * @return true, if a collision was found.
*/ */
bool Collide( const ITEM* aOther, const NODE* aNode, bool Collide( const ITEM* aHead, const NODE* aNode,
const COLLISION_SEARCH_OPTIONS& aOpts = COLLISION_SEARCH_OPTIONS(), COLLISION_SEARCH_CONTEXT* aCtx = nullptr ) const;
OBSTACLE *aObsInfo = nullptr ) const;
/** /**
* Return the geometrical shape of the item. Used for collision detection and spatial indexing. * Return the geometrical shape of the item. Used for collision detection and spatial indexing.
@ -238,11 +217,6 @@ public:
return nullptr; return nullptr;
} }
virtual const SHAPE* Hole() const
{
return nullptr;
}
virtual void Mark( int aMarker ) const { m_marker = aMarker; } virtual void Mark( int aMarker ) const { m_marker = aMarker; }
virtual void Unmark( int aMarker = -1 ) const { m_marker &= ~aMarker; } virtual void Unmark( int aMarker = -1 ) const { m_marker &= ~aMarker; }
virtual int Marker() const { return m_marker; } virtual int Marker() const { return m_marker; }
@ -279,18 +253,20 @@ public:
void SetIsCompoundShapePrimitive() { m_isCompoundShapePrimitive = true; } void SetIsCompoundShapePrimitive() { m_isCompoundShapePrimitive = true; }
bool IsCompoundShapePrimitive() const { return m_isCompoundShapePrimitive; } 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; virtual const std::string Format() const;
private: private:
bool collideSimple( const ITEM* aOther, const NODE* aNode, bool collideSimple( const ITEM* aHead, const NODE* aNode,
const COLLISION_SEARCH_OPTIONS& aOpts, COLLISION_SEARCH_CONTEXT* aCtx ) const;
OBSTACLE *aObsInfo = nullptr ) const;
protected: protected:
PnsKind m_kind; PnsKind m_kind;
BOARD_ITEM* m_parent; BOARD_ITEM* m_parent;
NODE* m_owner;
LAYER_RANGE m_layers; LAYER_RANGE m_layers;
bool m_movable; bool m_movable;

View File

@ -106,10 +106,6 @@ public:
virtual int Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB, virtual int Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
bool aUseClearanceEpsilon = true ) override; 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 DpCoupledNet( int aNet ) override;
virtual int DpNetPolarity( int aNet ) override; virtual int DpNetPolarity( int aNet ) override;
@ -153,8 +149,6 @@ private:
int m_clearanceEpsilon; int m_clearanceEpsilon;
std::unordered_map<CLEARANCE_CACHE_KEY, int> m_clearanceCache; 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;
}; };
@ -252,16 +246,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(); const BOARD_ITEM *parent = aItem->Parent();
return !parent || parent->IsOnCopperLayer(); 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(); const BOARD_ITEM *parent = aItem->Parent();
return parent && ( parent->IsOnLayer( Edge_Cuts ) || parent->IsOnLayer( Margin ) ); return parent && ( parent->IsOnLayer( Edge_Cuts ) || parent->IsOnLayer( Margin ) );
@ -382,8 +391,6 @@ void PNS_PCBNEW_RULE_RESOLVER::ClearCacheForItem( const PNS::ITEM* aItem )
void PNS_PCBNEW_RULE_RESOLVER::ClearCaches() void PNS_PCBNEW_RULE_RESOLVER::ClearCaches()
{ {
m_clearanceCache.clear(); m_clearanceCache.clear();
m_holeClearanceCache.clear();
m_holeToHoleClearanceCache.clear();
} }
@ -414,7 +421,23 @@ int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* a
for( int layer = layers.Start(); layer <= layers.End(); ++layer ) 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 ) ) if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, layer, &constraint ) )
{ {
@ -422,8 +445,7 @@ int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* a
rv = constraint.m_Value.Min(); rv = constraint.m_Value.Min();
} }
} }
else if( isEdge( aA ) || ( aB && isEdge( aB ) ) )
if( isEdge( aA ) || ( aB && isEdge( aB ) ) )
{ {
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE, aA, aB, layer, &constraint ) ) if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE, aA, aB, layer, &constraint ) )
{ {
@ -440,6 +462,7 @@ int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* a
return rv; return rv;
} }
#if 0
int PNS_PCBNEW_RULE_RESOLVER::HoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB, int PNS_PCBNEW_RULE_RESOLVER::HoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB,
bool aUseClearanceEpsilon ) bool aUseClearanceEpsilon )
@ -464,6 +487,8 @@ int PNS_PCBNEW_RULE_RESOLVER::HoleClearance( const PNS::ITEM* aA, const PNS::ITE
#define HAS_PLATED_HOLE( a ) ( a )->IsRoutable() #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 ) if( IsCopperLayer( layer )
&& ( HAS_PLATED_HOLE( aA ) || HAS_PLATED_HOLE( aB ) ) && ( HAS_PLATED_HOLE( aA ) || HAS_PLATED_HOLE( aB ) )
&& QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, layer, &constraint ) && QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, layer, &constraint )
@ -508,6 +533,8 @@ int PNS_PCBNEW_RULE_RESOLVER::HoleToHoleClearance( const PNS::ITEM* aA, const PN
return rv; return rv;
} }
#endif
bool PNS_KICAD_IFACE_BASE::inheritTrackWidth( PNS::ITEM* aItem, int* aInheritedWidth ) bool PNS_KICAD_IFACE_BASE::inheritTrackWidth( PNS::ITEM* aItem, int* aInheritedWidth )
{ {
@ -1140,7 +1167,12 @@ std::unique_ptr<PNS::SOLID> PNS_KICAD_IFACE_BASE::syncPad( PAD* aPad )
solid->SetOffset( VECTOR2I( offset.x, offset.y ) ); solid->SetOffset( VECTOR2I( offset.x, offset.y ) );
if( aPad->GetDrillSize().x > 0 ) if( aPad->GetDrillSize().x > 0 )
solid->SetHole( 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 // 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. // perform layer-specific flashing tests internally.
@ -1214,9 +1246,10 @@ std::unique_ptr<PNS::VIA> PNS_KICAD_IFACE_BASE::syncVia( PCB_VIA* aVia )
via->SetIsFree( aVia->GetIsFree() ); via->SetIsFree( aVia->GetIsFree() );
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings(); SHAPE* holeShape = new SHAPE_CIRCLE( aVia->GetPosition(), aVia->GetDrillValue() / 2 );
via->SetHole( SHAPE_CIRCLE( aVia->GetPosition(), PNS::HOLE* viaHole = new PNS::HOLE( via.get(), holeShape );
aVia->GetDrillValue() / 2 ) );
via->SetHole( viaHole );
return via; return via;
} }

View File

@ -36,6 +36,7 @@ class PCB_DISPLAY_OPTIONS;
class PCB_TOOL_BASE; class PCB_TOOL_BASE;
class FOOTPRINT; class FOOTPRINT;
class PAD; class PAD;
class EDA_TEXT;
namespace PNS namespace PNS
{ {

View File

@ -28,6 +28,8 @@
#include "pns_node.h" #include "pns_node.h"
#include "pns_via.h" #include "pns_via.h"
#include "pns_utils.h" #include "pns_utils.h"
#include "pns_router.h"
#include "pns_debug_decorator.h"
#include <geometry/shape_rect.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 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 ); return SegmentHull( m_seg, aClearance, aWalkaroundThickness );
} }

View File

@ -196,6 +196,7 @@ public:
void AppendVia( const VIA& aVia ); void AppendVia( const VIA& aVia );
void RemoveVia() { m_hasVia = false; } void RemoveVia() { m_hasVia = false; }
VIA& Via() { return m_via; }
const VIA& Via() const { return m_via; } const VIA& Via() const { return m_via; }
void SetViaDiameter( int aDiameter ) { m_via.SetDiameter( aDiameter ); } void SetViaDiameter( int aDiameter ) { m_via.SetDiameter( aDiameter ); }

View File

@ -87,10 +87,20 @@ NODE::~NODE()
m_joints.clear(); m_joints.clear();
std::vector<ITEM*> toDelete;
toDelete.reserve( m_index->Size() );
for( ITEM* item : *m_index ) for( ITEM* item : *m_index )
{ {
if( item->BelongsTo( this ) ) if( item->BelongsTo( this ) && item->OfKind( ITEM::HOLE_T ) )
delete item; toDelete.push_back( item );
}
for( ITEM* item : toDelete )
{
printf("del item %p type %s\n", item, item->KindStr().c_str() );
delete item;
} }
releaseGarbage(); releaseGarbage();
@ -105,34 +115,13 @@ int NODE::GetClearance( const ITEM* aA, const ITEM* aB, bool aUseClearanceEpsilo
if( !m_ruleResolver ) if( !m_ruleResolver )
return 100000; 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() ) if( aA->IsVirtual() || aB->IsVirtual() )
return 0; 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 // function object that visits potential obstacles and performs the actual collision refining
struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR
{ {
OBSTACLES& m_tab; COLLISION_SEARCH_CONTEXT* m_ctx;
int m_matchCount;
const COLLISION_SEARCH_OPTIONS& m_opts;
DEFAULT_OBSTACLE_VISITOR( NODE::OBSTACLES& aTab, const ITEM* aItem, DEFAULT_OBSTACLE_VISITOR( COLLISION_SEARCH_CONTEXT* aCtx, const ITEM* aItem ) :
const COLLISION_SEARCH_OPTIONS& aOpts ) :
OBSTACLE_VISITOR( aItem ), OBSTACLE_VISITOR( aItem ),
m_tab( aTab ), m_ctx( aCtx )
m_opts( aOpts ),
m_matchCount( 0 )
{ {
} }
@ -230,26 +214,16 @@ struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR
bool operator()( ITEM* aCandidate ) override bool operator()( ITEM* aCandidate ) override
{ {
if( !aCandidate->OfKind( m_opts.m_kindMask ) ) if( !aCandidate->OfKind( m_ctx->options.m_kindMask ) )
return true; return true;
if( visit( aCandidate ) ) if( visit( aCandidate ) )
return true; return true;
if( !aCandidate->Collide( m_item, m_node, m_opts ) ) if( !aCandidate->Collide( m_item, m_node, m_ctx ) )
return true; return true;
OBSTACLE obs; if( m_ctx->options.m_limitCount > 0 && m_ctx->obstacles.size() >= m_ctx->options.m_limitCount )
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 )
return false; return false;
return true; return true;
@ -258,13 +232,15 @@ struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR
int NODE::QueryColliding( const ITEM* aItem, NODE::OBSTACLES& aObstacles, 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 /// By default, virtual items cannot collide
if( aItem->IsVirtual() ) if( aItem->IsVirtual() )
return 0; return 0;
DEFAULT_OBSTACLE_VISITOR visitor( aObstacles, aItem, aOpts ); DEFAULT_OBSTACLE_VISITOR visitor( &ctx, aItem );
#ifdef DEBUG #ifdef DEBUG
assert( allocNodes.find( this ) != allocNodes.end() ); 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 ); m_index->Query( aItem, m_maxClearance, visitor );
// if we haven't found enough items, look in the root branch as well. // 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 ); visitor.SetWorld( m_root, this );
m_root->m_index->Query( aItem, m_maxClearance, visitor ); 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, NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine,
const std::set<ITEM*>* aRestrictedSet,
const COLLISION_SEARCH_OPTIONS& aOpts ) const COLLISION_SEARCH_OPTIONS& aOpts )
{ {
const int clearanceEpsilon = GetRuleResolver()->ClearanceEpsilon(); const int clearanceEpsilon = GetRuleResolver()->ClearanceEpsilon();
OBSTACLES obstacleList; OBSTACLES obstacleList;
obstacleList.reserve( 100 ); std::vector<SEGMENT> tmpSegs;
tmpSegs.reserve( aLine->CLine().SegmentCount() );
for( int i = 0; i < aLine->CLine().SegmentCount(); i++ ) 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. // which means they'll be the same for all segments in the line.
// Disabling the cache will lead to slowness. // Disabling the cache will lead to slowness.
const SEGMENT s( *aLine, aLine->CLine().CSegment( i ) ); tmpSegs.emplace_back( *aLine, aLine->CLine().CSegment( i ) );
QueryColliding( &s, obstacleList, aOpts ); QueryColliding( &tmpSegs.back(), obstacleList, aOpts );
} }
if( aLine->EndsWithVia() ) if( aLine->EndsWithVia() )
@ -317,22 +295,15 @@ NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, int aKindMask,
nearest.m_maxFanoutWidth = 0; nearest.m_maxFanoutWidth = 0;
auto updateNearest = auto updateNearest =
[&]( const SHAPE_LINE_CHAIN::INTERSECTION& pt, ITEM* obstacle, [&]( const SHAPE_LINE_CHAIN::INTERSECTION& pt, const OBSTACLE& obstacle )
const SHAPE_LINE_CHAIN& hull, bool isHole )
{ {
int dist = aLine->CLine().PathLength( pt.p, pt.index_their ); int dist = aLine->CLine().PathLength( pt.p, pt.index_their );
if( dist < nearest.m_distFirst ) if( dist < nearest.m_distFirst )
{ {
nearest = obstacle;
nearest.m_distFirst = dist; nearest.m_distFirst = dist;
nearest.m_ipFirst = pt.p; 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 ) 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; continue;
int clearance = 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() ); //debugDecorator->AddPoint( ip.p, ip.valid?3:6, 100000, (const char *) wxString::Format("obstacle-isect-point-%d" ).c_str() );
if( ip.valid ) if( ip.valid )
updateNearest( ip, obstacle.m_item, obstacleHull, false ); updateNearest( ip, obstacle );
} }
if( aLine->EndsWithVia() ) if( aLine->EndsWithVia() )
{ {
const VIA& via = aLine->Via(); 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 ) int viaClearance = GetClearance( obstacle.m_item, &via, aOpts.m_useClearanceEpsilon )
+ via.Diameter() / 2; + via.Diameter() / 2;
int holeClearance = GetHoleClearance( obstacle.m_item, &via, aOpts.m_useClearanceEpsilon ) //int holeClearance = GetClearance( obstacle.m_item, viaHole, aOpts.m_useClearanceEpsilon )
+ viaHoleRadius; // + viaHoleRadius;
if( holeClearance > viaClearance ) //if( holeClearance > viaClearance )
viaClearance = holeClearance; // viaClearance = holeClearance;
obstacleHull = obstacle.m_item->Hull( viaClearance, 0, layer ); obstacleHull = obstacle.m_item->Hull( viaClearance, 0, layer );
//debugDecorator->AddLine( obstacleHull, 3 ); //debugDecorator->AddLine( obstacleHull, 3 );
@ -387,9 +359,9 @@ NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, int aKindMask,
// obstacleHull.Intersect( aLine->CLine(), intersectingPts, true ); // obstacleHull.Intersect( aLine->CLine(), intersectingPts, true );
for( const SHAPE_LINE_CHAIN::INTERSECTION& ip : intersectingPts ) 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 if( ( m_collisionQueryScope == CQS_ALL_RULES
|| !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( obstacle.m_item, || !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( obstacle.m_item,
layer ) ) layer ) )
@ -438,10 +410,11 @@ NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, int aKindMask,
updateNearest( ip, obstacle.m_item, obstacleHull, true ); updateNearest( ip, obstacle.m_item, obstacleHull, true );
} }
} }
#endif
} }
if( nearest.m_distFirst == INT_MAX ) if( nearest.m_distFirst == INT_MAX )
nearest.m_item = obstacleList[0].m_item; nearest = (*obstacleList.begin());
return nearest; return nearest;
} }
@ -470,8 +443,6 @@ NODE::OPT_OBSTACLE NODE::CheckColliding( const ITEM* aItemA, int aKindMask )
opts.m_limitCount = 1; opts.m_limitCount = 1;
opts.m_overrideClearance = 1; opts.m_overrideClearance = 1;
obs.reserve( 100 );
if( aItemA->Kind() == ITEM::LINE_T ) if( aItemA->Kind() == ITEM::LINE_T )
{ {
int n = 0; int n = 0;
@ -488,7 +459,7 @@ NODE::OPT_OBSTACLE NODE::CheckColliding( const ITEM* aItemA, int aKindMask )
n += QueryColliding( &s, obs, opts ); n += QueryColliding( &s, obs, opts );
if( n ) if( n )
return OPT_OBSTACLE( obs[0] ); return OPT_OBSTACLE( *obs.begin() );
} }
if( line->EndsWithVia() ) if( line->EndsWithVia() )
@ -496,12 +467,12 @@ NODE::OPT_OBSTACLE NODE::CheckColliding( const ITEM* aItemA, int aKindMask )
n += QueryColliding( &line->Via(), obs, opts ); n += QueryColliding( &line->Via(), obs, opts );
if( n ) if( n )
return OPT_OBSTACLE( obs[0] ); return OPT_OBSTACLE( *obs.begin() );
} }
} }
else if( QueryColliding( aItemA, obs, opts ) > 0 ) else if( QueryColliding( aItemA, obs, opts ) > 0 )
{ {
return OPT_OBSTACLE( obs[0] ); return OPT_OBSTACLE( *obs.begin() );
} }
return OPT_OBSTACLE(); return OPT_OBSTACLE();
@ -568,6 +539,9 @@ const ITEM_SET NODE::HitTest( const VECTOR2I& aPoint ) const
void NODE::addSolid( SOLID* aSolid ) void NODE::addSolid( SOLID* aSolid )
{ {
if( aSolid->HasHole() )
addHole( aSolid->Hole() );
if( aSolid->IsRoutable() ) if( aSolid->IsRoutable() )
linkJoint( aSolid->Pos(), aSolid->Layers(), aSolid->Net(), aSolid ); 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 ) void NODE::addVia( VIA* aVia )
{ {
if( aVia->HasHole() )
addHole( aVia->Hole() );
linkJoint( aVia->Pos(), aVia->Layers(), aVia->Net(), aVia ); linkJoint( aVia->Pos(), aVia->Layers(), aVia->Net(), aVia );
m_index->Add( 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 ) void NODE::Add( std::unique_ptr< VIA > aVia )
{ {
aVia->SetOwner( this ); aVia->SetOwner( this );
addVia( aVia.release() ); 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 ) void NODE::Add( LINE& aLine, bool aAllowRedundant )
{ {
@ -725,6 +734,7 @@ void NODE::Add( std::unique_ptr< ITEM > aItem, bool aAllowRedundant )
Add( *l ); Add( *l );
break; break;
} }
default: default:
assert( false ); 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: // case 1: removing an item that is stored in the root node from any branch:
// mark it as overridden, but do not remove // mark it as overridden, but do not remove
if( aItem->BelongsTo( m_root ) && !isRoot() ) if( aItem->BelongsTo( m_root ) && !isRoot() )
{
m_override.insert( aItem ); 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, // 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 // or the root itself and we are the root: remove from the index
else if( !aItem->BelongsTo( m_root ) || isRoot() ) else if( !aItem->BelongsTo( m_root ) || isRoot() )
{
m_index->Remove( aItem ); m_index->Remove( aItem );
if( aItem->HasHole() )
m_index->Remove( aItem->Hole() );
}
// the item belongs to this particular branch: un-reference it // the item belongs to this particular branch: un-reference it
if( aItem->BelongsTo( this ) ) if( aItem->BelongsTo( this ) )
{ {
aItem->SetOwner( nullptr ); aItem->SetOwner( nullptr );
m_root->m_garbageItems.insert( aItem ); 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 );
}
} }
} }

View File

@ -25,7 +25,7 @@
#include <vector> #include <vector>
#include <list> #include <list>
#include <unordered_set> #include <set>
#include <core/minoptmax.h> #include <core/minoptmax.h>
#include <geometry/shape_line_chain.h> #include <geometry/shape_line_chain.h>
@ -46,10 +46,6 @@ class INDEX;
class ROUTER; class ROUTER;
class NODE; class NODE;
/**
* An abstract function object, returning a design rule (clearance, diff pair gap, etc) required
* between two items.
*/
enum class CONSTRAINT_TYPE enum class CONSTRAINT_TYPE
{ {
@ -64,6 +60,11 @@ enum class CONSTRAINT_TYPE
CT_HOLE_TO_HOLE = 9 CT_HOLE_TO_HOLE = 9
}; };
/**
* An abstract function object, returning a design rule (clearance, diff pair gap, etc) required
* between two items.
*/
struct CONSTRAINT struct CONSTRAINT
{ {
CONSTRAINT_TYPE m_Type; CONSTRAINT_TYPE m_Type;
@ -74,16 +75,68 @@ struct CONSTRAINT
wxString m_ToName; 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 class RULE_RESOLVER
{ {
public: public:
virtual ~RULE_RESOLVER() {} virtual ~RULE_RESOLVER() {}
virtual int Clearance( const ITEM* aA, const ITEM* aB, bool aUseClearanceEpsilon = true ) = 0; 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 DpCoupledNet( int aNet ) = 0;
virtual int DpNetPolarity( int aNet ) = 0; virtual int DpNetPolarity( int aNet ) = 0;
@ -108,23 +161,6 @@ public:
virtual int ClearanceEpsilon() const { return 0; } 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 class OBSTACLE_VISITOR
{ {
@ -171,16 +207,13 @@ public:
typedef std::optional<OBSTACLE> OPT_OBSTACLE; typedef std::optional<OBSTACLE> OPT_OBSTACLE;
typedef std::vector<ITEM*> ITEM_VECTOR; typedef std::vector<ITEM*> ITEM_VECTOR;
typedef std::vector<OBSTACLE> OBSTACLES; typedef std::set<OBSTACLE> OBSTACLES;
NODE(); NODE();
~NODE(); ~NODE();
///< Return the expected clearance between items a and b. ///< Return the expected clearance between items a and b.
int GetClearance( const ITEM* aA, const ITEM* aB, bool aUseClearanceEpsilon = true ) const; 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. ///< Return the pre-set worst case clearance between any pair of items.
int GetMaxClearance() const int GetMaxClearance() const
@ -227,7 +260,7 @@ public:
* @return number of obstacles found * @return number of obstacles found
*/ */
int QueryColliding( const ITEM* aItem, OBSTACLES& aObstacles, 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, int QueryJoints( const BOX2I& aBox, std::vector<JOINT*>& aJoints,
LAYER_RANGE aLayerMask = LAYER_RANGE::All(), int aKindMask = ITEM::ANY_T ); 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 * @param aRestrictedSet is an optional set of items that should be considered as obstacles
* @return the obstacle, if found, otherwise empty. * @return the obstacle, if found, otherwise empty.
*/ */
OPT_OBSTACLE NearestObstacle( const LINE* aLine, int aKindMask = ITEM::ANY_T, OPT_OBSTACLE NearestObstacle( const LINE* aLine,
const std::set<ITEM*>* aRestrictedSet = nullptr,
const COLLISION_SEARCH_OPTIONS& aOpts = COLLISION_SEARCH_OPTIONS() ); const COLLISION_SEARCH_OPTIONS& aOpts = COLLISION_SEARCH_OPTIONS() );
/** /**
@ -289,6 +321,8 @@ public:
void Add( LINE& aLine, bool aAllowRedundant = false ); void Add( LINE& aLine, bool aAllowRedundant = false );
void Add( ITEM* aItem, bool aAllowRedundant = false );
void AddEdgeExclusion( std::unique_ptr<SHAPE> aShape ); void AddEdgeExclusion( std::unique_ptr<SHAPE> aShape );
bool QueryEdgeExclusions( const VECTOR2I& aPos ) const; bool QueryEdgeExclusions( const VECTOR2I& aPos ) const;
@ -450,6 +484,7 @@ private:
void addSegment( SEGMENT* aSeg ); void addSegment( SEGMENT* aSeg );
void addVia( VIA* aVia ); void addVia( VIA* aVia );
void addArc( ARC* aVia ); void addArc( ARC* aVia );
void addHole( HOLE* aHole );
void removeSolidIndex( SOLID* aSeg ); void removeSolidIndex( SOLID* aSeg );
void removeSegmentIndex( SEGMENT* aSeg ); void removeSegmentIndex( SEGMENT* aSeg );

View File

@ -153,8 +153,8 @@ const ITEM_SET ROUTER::QueryHoverItems( const VECTOR2I& aP, bool aUseClearance )
PNS::ITEM_SET ret; PNS::ITEM_SET ret;
for( OBSTACLE& obstacle : obs ) for( const OBSTACLE& obstacle : obs )
ret.Add( obstacle.m_item, false ); ret.Add( obstacle.m_item, false );
return ret; return ret;
} }
@ -646,19 +646,15 @@ void ROUTER::markViolations( NODE* aNode, ITEM_SET& aCurrent, NODE::ITEM_VECTOR&
int clearance; int clearance;
bool removeOriginal = true; bool removeOriginal = true;
bool holeOnly = false; // JEY TODO ( ( itemToMark->Marker() & MK_HOLE )
//&& !( itemToMark->Marker() & MK_VIOLATION ) );
if( holeOnly ) clearance = aNode->GetClearance( currentItem, itemToMark );
clearance = aNode->GetHoleClearance( currentItem, itemToMark );
else
clearance = aNode->GetClearance( currentItem, itemToMark );
if( itemToMark->Layers().IsMultilayer() && !currentItem->Layers().IsMultilayer() ) if( itemToMark->Layers().IsMultilayer() && !currentItem->Layers().IsMultilayer() )
tmp->SetLayer( currentItem->Layer() ); tmp->SetLayer( currentItem->Layer() );
if( itemToMark->Kind() == ITEM::SOLID_T ) if( itemToMark->Kind() == ITEM::SOLID_T )
{ {
#if 0 // fixme holes
if( holeOnly || !m_iface->IsFlashedOnLayer( itemToMark, currentItem->Layer() ) ) if( holeOnly || !m_iface->IsFlashedOnLayer( itemToMark, currentItem->Layer() ) )
{ {
SOLID* solid = static_cast<SOLID*>( tmp.get() ); SOLID* solid = static_cast<SOLID*>( tmp.get() );
@ -671,6 +667,7 @@ void ROUTER::markViolations( NODE* aNode, ITEM_SET& aCurrent, NODE::ITEM_VECTOR&
removeOriginal = false; removeOriginal = false;
} }
} }
#endif
if( itemToMark->IsCompoundShapePrimitive() ) if( itemToMark->IsCompoundShapePrimitive() )
{ {
@ -708,7 +705,7 @@ void ROUTER::markViolations( NODE* aNode, ITEM_SET& aCurrent, NODE::ITEM_VECTOR&
if( GetDragger() ) if( GetDragger() )
draggedItems = GetDragger()->Traces(); draggedItems = GetDragger()->Traces();
for( OBSTACLE& obs : obstacles ) for( const OBSTACLE& obs : obstacles )
{ {
// Don't mark items being dragged; only board items they collide with // Don't mark items being dragged; only board items they collide with
if( draggedItems.Contains( obs.m_item ) ) if( draggedItems.Contains( obs.m_item ) )
@ -788,7 +785,7 @@ bool ROUTER::movePlacing( const VECTOR2I& aP, ITEM* aEndItem )
{ {
const VIA& via = l->Via(); const VIA& via = l->Via();
int viaClearance = GetRuleResolver()->Clearance( &via, nullptr ); 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 ) if( holeClearance + via.Drill() / 2 > viaClearance + via.Diameter() / 2 )
viaClearance = holeClearance + via.Drill() / 2 - via.Diameter() / 2; viaClearance = holeClearance + via.Drill() / 2 - via.Diameter() / 2;

View File

@ -140,10 +140,12 @@ int SHOVE::getClearance( const ITEM* aA, const ITEM* aB ) const
int SHOVE::getHoleClearance( 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_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 } ) for( ITEM::PnsKind search_order : { ITEM::SOLID_T, ITEM::VIA_T, ITEM::SEGMENT_T } )
{ {
nearest = m_currentNode->NearestObstacle( &currentLine, search_order ); COLLISION_SEARCH_OPTIONS opts;
opts.m_kindMask = search_order;
nearest = m_currentNode->NearestObstacle( &currentLine, opts );
if( nearest ) if( nearest )
{ {

View File

@ -37,65 +37,13 @@
namespace PNS { 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 const SHAPE_LINE_CHAIN SOLID::Hull( int aClearance, int aWalkaroundThickness, int aLayer ) const
{ {
if( !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( this, aLayer ) ) //if( !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( this, aLayer ) )
return HoleHull( aClearance, aWalkaroundThickness, aLayer ); // return HoleHull( aClearance, aWalkaroundThickness, aLayer );
// fixme holes
if( !m_shape ) if( !m_shape )
return SHAPE_LINE_CHAIN(); return SHAPE_LINE_CHAIN();
@ -106,7 +54,7 @@ const SHAPE_LINE_CHAIN SOLID::Hull( int aClearance, int aWalkaroundThickness, in
if ( cmpnd->Shapes().size() == 1 ) if ( cmpnd->Shapes().size() == 1 )
{ {
return buildHullForPrimitiveShape( cmpnd->Shapes()[0], aClearance, return BuildHullForPrimitiveShape( cmpnd->Shapes()[0], aClearance,
aWalkaroundThickness ); aWalkaroundThickness );
} }
else else
@ -115,7 +63,7 @@ const SHAPE_LINE_CHAIN SOLID::Hull( int aClearance, int aWalkaroundThickness, in
for( SHAPE* shape : cmpnd->Shapes() ) for( SHAPE* shape : cmpnd->Shapes() )
{ {
hullSet.AddOutline( buildHullForPrimitiveShape( shape, aClearance, hullSet.AddOutline( BuildHullForPrimitiveShape( shape, aClearance,
aWalkaroundThickness ) ); aWalkaroundThickness ) );
} }
@ -125,42 +73,7 @@ const SHAPE_LINE_CHAIN SOLID::Hull( int aClearance, int aWalkaroundThickness, in
} }
else else
{ {
return buildHullForPrimitiveShape( m_shape, aClearance, aWalkaroundThickness ); 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 );
} }
} }
@ -171,6 +84,7 @@ ITEM* SOLID::Clone() const
return solid; return solid;
} }
void SOLID::SetPos( const VECTOR2I& aCenter ) void SOLID::SetPos( const VECTOR2I& aCenter )
{ {
VECTOR2I delta = aCenter - m_pos; VECTOR2I delta = aCenter - m_pos;
@ -178,6 +92,8 @@ void SOLID::SetPos( const VECTOR2I& aCenter )
if( m_shape ) if( m_shape )
m_shape->Move( delta ); m_shape->Move( delta );
//printf("Hole@%p\n", m_hole);
if( m_hole ) if( m_hole )
m_hole->Move( delta ); m_hole->Move( delta );

View File

@ -46,8 +46,10 @@ public:
~SOLID() ~SOLID()
{ {
if ( m_hole )
delete m_hole;
delete m_shape; delete m_shape;
delete m_hole;
} }
SOLID( const SOLID& aSolid ) : SOLID( const SOLID& aSolid ) :
@ -77,26 +79,16 @@ public:
const SHAPE* Shape() const override { return m_shape; } 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, const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0,
int aLayer = -1 ) const override; int aLayer = -1 ) const override;
const SHAPE_LINE_CHAIN HoleHull( int aClearance, int aWalkaroundThickness,
int aLayer ) const override;
void SetShape( SHAPE* shape ) void SetShape( SHAPE* shape )
{ {
delete m_shape; delete m_shape;
m_shape = shape; m_shape = shape;
} }
void SetHole( SHAPE* shape )
{
delete m_hole;
m_hole = shape;
}
const VECTOR2I& Pos() const { return m_pos; } const VECTOR2I& Pos() const { return m_pos; }
void SetPos( const VECTOR2I& aCenter ); void SetPos( const VECTOR2I& aCenter );
@ -119,13 +111,33 @@ public:
EDA_ANGLE GetOrientation() const { return m_orientation; } EDA_ANGLE GetOrientation() const { return m_orientation; }
void SetOrientation( const EDA_ANGLE& aOrientation ) { m_orientation = aOrientation; } 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: private:
VECTOR2I m_pos; VECTOR2I m_pos;
SHAPE* m_shape; SHAPE* m_shape;
SHAPE* m_hole;
VECTOR2I m_offset; VECTOR2I m_offset;
int m_padToDie; int m_padToDie;
EDA_ANGLE m_orientation; EDA_ANGLE m_orientation;
HOLE* m_hole;
}; };
} }

View File

@ -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();
}
} }

View File

@ -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, void HullIntersection( const SHAPE_LINE_CHAIN& hull, const SHAPE_LINE_CHAIN& line,
SHAPE_LINE_CHAIN::INTERSECTIONS& ips ); SHAPE_LINE_CHAIN::INTERSECTIONS& ips );
const SHAPE_LINE_CHAIN BuildHullForPrimitiveShape( const SHAPE* aShape, int aClearance,
int aWalkaroundThickness );
} }

View File

@ -36,6 +36,7 @@ bool VIA::PushoutForce( NODE* aNode, const ITEM* aOther, VECTOR2I& aForce )
VECTOR2I elementForces[4], force; VECTOR2I elementForces[4], force;
size_t nf = 0; size_t nf = 0;
#if 0
if( aNode->GetCollisionQueryScope() == NODE::CQS_ALL_RULES ) if( aNode->GetCollisionQueryScope() == NODE::CQS_ALL_RULES )
{ {
int holeClearance = aNode->GetHoleClearance( this, aOther ); 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++] ); aOther->Shape()->Collide( Hole(), holeClearance, &elementForces[nf++] );
} }
#endif
aOther->Shape()->Collide( Shape(), clearance, &elementForces[nf++] ); 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; int width = m_diameter;
if( !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( this, aLayer ) ) 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 // Chamfer = width * ( 1 - sqrt(2)/2 ) for equilateral octagon
return OctagonalHull( m_pos - VECTOR2I( width / 2, width / 2 ), 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 const SHAPE_LINE_CHAIN VIA::HoleHull( int aClearance, int aWalkaroundThickness, int aLayer ) const
{ {
int cl = ( aClearance + aWalkaroundThickness / 2 ); 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 // Chamfer = width * ( 1 - sqrt(2)/2 ) for equilateral octagon
return OctagonalHull( m_pos - VECTOR2I( width / 2, width / 2 ), VECTOR2I( width, width ), cl, return OctagonalHull( m_pos - VECTOR2I( width / 2, width / 2 ), VECTOR2I( width, width ), cl,
( 2 * cl + width ) * ( 1.0 - M_SQRT1_2 ) ); ( 2 * cl + width ) * ( 1.0 - M_SQRT1_2 ) );
} }
#endif
VIA* VIA::Clone() const VIA* VIA::Clone() const
{ {
@ -185,7 +188,7 @@ VIA* VIA::Clone() const
v->m_diameter = m_diameter; v->m_diameter = m_diameter;
v->m_drill = m_drill; v->m_drill = m_drill;
v->m_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 ); 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_rank = m_rank;
v->m_marker = m_marker; v->m_marker = m_marker;
v->m_viaType = m_viaType; v->m_viaType = m_viaType;

View File

@ -30,6 +30,7 @@
#include "pns_item.h" #include "pns_item.h"
#include "pns_linked_item.h" #include "pns_linked_item.h"
#include "pns_hole.h"
namespace PNS { namespace PNS {
@ -57,6 +58,7 @@ public:
m_viaType = VIATYPE::THROUGH; m_viaType = VIATYPE::THROUGH;
m_isFree = false; m_isFree = false;
m_isVirtual = false; m_isVirtual = false;
m_hole = nullptr;
} }
VIA( const VECTOR2I& aPos, const LAYER_RANGE& aLayers, int aDiameter, int aDrill, VIA( const VECTOR2I& aPos, const LAYER_RANGE& aLayers, int aDiameter, int aDrill,
@ -69,7 +71,8 @@ public:
m_diameter = aDiameter; m_diameter = aDiameter;
m_drill = aDrill; m_drill = aDrill;
m_shape = SHAPE_CIRCLE( aPos, aDiameter / 2 ); 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_viaType = aViaType;
m_isFree = false; m_isFree = false;
m_isVirtual = false; m_isVirtual = false;
@ -83,7 +86,7 @@ public:
m_pos = aB.m_pos; m_pos = aB.m_pos;
m_diameter = aB.m_diameter; m_diameter = aB.m_diameter;
m_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 ); 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_marker = aB.m_marker;
m_rank = aB.m_rank; m_rank = aB.m_rank;
m_drill = aB.m_drill; m_drill = aB.m_drill;
@ -92,6 +95,12 @@ public:
m_isVirtual = aB.m_isVirtual; m_isVirtual = aB.m_isVirtual;
} }
virtual ~VIA()
{
if ( m_hole )
delete m_hole;
}
static inline bool ClassOf( const ITEM* aItem ) static inline bool ClassOf( const ITEM* aItem )
{ {
return aItem && VIA_T == aItem->Kind(); return aItem && VIA_T == aItem->Kind();
@ -103,7 +112,8 @@ public:
{ {
m_pos = aPos; m_pos = aPos;
m_shape.SetCenter( aPos ); m_shape.SetCenter( aPos );
m_hole.SetCenter( aPos ); if( m_hole )
m_hole->SetCenter( aPos );
} }
VIATYPE ViaType() const { return m_viaType; } VIATYPE ViaType() const { return m_viaType; }
@ -122,7 +132,8 @@ public:
void SetDrill( int aDrill ) void SetDrill( int aDrill )
{ {
m_drill = 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; } bool IsFree() const { return m_isFree; }
@ -135,17 +146,11 @@ public:
const SHAPE* Shape() const override { return &m_shape; } 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; VIA* Clone() const override;
const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0, const SHAPE_LINE_CHAIN Hull( int aClearance = 0, int aWalkaroundThickness = 0,
int aLayer = -1 ) const override; 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 virtual VECTOR2I Anchor( int n ) const override
{ {
return m_pos; return m_pos;
@ -160,6 +165,26 @@ public:
const VIA_HANDLE MakeHandle() const; 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; virtual const std::string Format() const override;
private: private:
@ -167,10 +192,9 @@ private:
int m_drill; int m_drill;
VECTOR2I m_pos; VECTOR2I m_pos;
SHAPE_CIRCLE m_shape; SHAPE_CIRCLE m_shape;
SHAPE_CIRCLE m_hole;
VIATYPE m_viaType; VIATYPE m_viaType;
bool m_isFree; bool m_isFree;
HOLE* m_hole;
}; };
@ -181,7 +205,7 @@ public:
VIA( aPos, LAYER_RANGE( aLayer, aLayer ), aDiameter, aDiameter / 2, aNet ) VIA( aPos, LAYER_RANGE( aLayer, aLayer ), aDiameter, aDiameter / 2, aNet )
{ {
m_isVirtual = true; m_isVirtual = true;
SetHole( SHAPE_CIRCLE( Pos(), 1 ) ); //SetHole( SHAPE_CIRCLE( Pos(), 1 ) );
} }
}; };

View File

@ -43,12 +43,17 @@ void WALKAROUND::start( const LINE& aInitialPath )
NODE::OPT_OBSTACLE WALKAROUND::nearestObstacle( const LINE& aPath ) NODE::OPT_OBSTACLE WALKAROUND::nearestObstacle( const LINE& aPath )
{ {
COLLISION_SEARCH_OPTIONS opts; 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; opts.m_useClearanceEpsilon = false;
NODE::OPT_OBSTACLE obs = m_world->NearestObstacle( &aPath, m_itemMask, NODE::OPT_OBSTACLE obs = m_world->NearestObstacle( &aPath, opts );
m_restrictedSet.empty() ? nullptr
: &m_restrictedSet,
opts );
if( m_restrictedSet.empty() ) if( m_restrictedSet.empty() )
return obs; return obs;
@ -91,14 +96,13 @@ WALKAROUND::WALKAROUND_STATUS WALKAROUND::singleStep( LINE& aPath, bool aWinding
SHAPE_LINE_CHAIN path_walk; SHAPE_LINE_CHAIN path_walk;
SHAPE_LINE_CHAIN hull = current_obs->m_item->Hull( current_obs->m_clearance, SHAPE_LINE_CHAIN hull = current_obs->m_item->Hull( current_obs->m_clearance, aPath.Width() );
current_obs->m_walkaroundThickness );
bool s_cw = aPath.Walkaround( hull, path_walk, aWindingDirection ); bool s_cw = aPath.Walkaround( hull, path_walk, aWindingDirection );
PNS_DBG( Dbg(), BeginGroup, "hull/walk", 1 ); 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, &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(), 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 ) ); 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 ) if( s_cw != IN_PROGRESS && s_ccw != IN_PROGRESS )
break; 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 // Safety valve
if( m_lengthLimitOn && path_cw.Line().Length() > lengthLimit && path_ccw.Line().Length() > lengthLimit ) if( m_lengthLimitOn && path_cw.Line().Length() > lengthLimit && path_ccw.Line().Length() > lengthLimit )
break; break;

View File

@ -38,12 +38,26 @@ using namespace KIGFX;
ROUTER_PREVIEW_ITEM::ROUTER_PREVIEW_ITEM( const PNS::ITEM* aItem, KIGFX::VIEW* aView ) : 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; // A PNS::SOLID for an edge-cut item must have 0 width for collision calculations, but when
m_hole = aItem && aItem->Hole() ? aItem->Hole()->Clone() : nullptr; // 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_clearance = -1;
m_originLayer = m_layer = LAYER_SELECT_OVERLAY ; m_originLayer = m_layer = LAYER_SELECT_OVERLAY ;
@ -145,7 +159,7 @@ void ROUTER_PREVIEW_ITEM::Update( const PNS::ITEM* aItem )
m_hole = nullptr; m_hole = nullptr;
if( aItem->Hole() ) if( aItem->Hole() )
m_hole = aItem->Hole()->Clone(); m_hole = aItem->Hole()->Shape()->Clone();
break; break;

View File

@ -96,6 +96,8 @@ std::unique_ptr<BOARD> ReadBoardFromFileOrStream( const std::string& aFilename,
std::istream* in_stream = nullptr; std::istream* in_stream = nullptr;
std::ifstream file_stream; std::ifstream file_stream;
printf("RD from %s\n", aFilename.c_str() );
if( aFilename.empty() ) if( aFilename.empty() )
{ {
// no file, read stdin // no file, read stdin

View File

@ -35,6 +35,7 @@ set( QA_PCBNEW_SRCS
test_board_item.cpp test_board_item.cpp
test_graphics_import_mgr.cpp test_graphics_import_mgr.cpp
test_lset.cpp test_lset.cpp
test_pns_basics.cpp
test_pad_numbering.cpp test_pad_numbering.cpp
test_libeval_compiler.cpp test_libeval_compiler.cpp
test_save_load.cpp test_save_load.cpp

View File

@ -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 );
}
}