Implement hole clearance and hole-to-hole clearance in router.

Fixes https://gitlab.com/kicad/code/kicad/issues/6882
This commit is contained in:
Jeff Young 2021-01-01 00:29:05 +00:00
parent 5d8e6020d1
commit ecc0e861d3
20 changed files with 451 additions and 439 deletions

View File

@ -100,7 +100,7 @@ bool COMPONENT_DRAGGER::Start( const VECTOR2I& aP, ITEM_SET& aPrimitives )
{
LINKED_ITEM* li = static_cast<LINKED_ITEM*>( extraJoint->LinkList()[0].item );
if( li->Collide( solid, 0, false, nullptr, nullptr, false ) )
if( li->Collide( solid, 0, m_world ) )
addLinked( solid, li, extraJoint->Pos() - solid->Pos() );
}
}

View File

@ -33,28 +33,7 @@ void INDEX::Add( ITEM* aItem )
m_subIndices.resize( 2 * range.End() + 1 ); // +1 handles the 0 case
for( int i = range.Start(); i <= range.End(); ++i )
{
if( !ROUTER::GetInstance()->GetInterface()->IsOnLayer( aItem, i ) )
{
if( aItem->AlternateShape() )
{
m_subIndices[i].Add( aItem, aItem->AlternateShape()->BBox() );
}
else
{
wxLogError( "Missing expected Alternate shape for %s at %d %d",
aItem->Parent()->GetClass(),
aItem->Anchor( 0 ).x,
aItem->Anchor( 0 ).y );
m_subIndices[i].Add( aItem );
}
}
else
{
m_subIndices[i].Add( aItem );
}
}
m_subIndices[i].Add( aItem );
m_allItems.insert( aItem );
int net = aItem->Net();

View File

@ -28,12 +28,23 @@ typedef VECTOR2I::extended_type ecoord;
namespace PNS {
bool ITEM::collideSimple( const ITEM* aOther, int aClearance, bool aNeedMTV, VECTOR2I* aMTV,
const NODE* aParentNode, bool aDifferentNetsOnly ) const
bool ITEM::collideSimple( const ITEM* aOther, const NODE* aNode, bool aDifferentNetsOnly ) const
{
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;
// 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;
if( aOther->m_kind == LINE_T )
lineWidthB = static_cast<const LINE*>( aOther )->Width() / 2;
// same nets? no collision!
if( aDifferentNetsOnly && m_net == aOther->m_net && m_net >= 0 && aOther->m_net >= 0 )
@ -43,62 +54,69 @@ bool ITEM::collideSimple( const ITEM* aOther, int aClearance, bool aNeedMTV, VEC
if( !m_layers.Overlaps( aOther->m_layers ) )
return false;
if( !aOther->Layers().IsMultilayer() && !iface->IsOnLayer( this, aOther->Layer() ) )
if( holeA || holeB )
{
if( !AlternateShape() )
int holeClearance = aNode->GetHoleClearance( this, aOther );
if( holeA && holeA->Collide( shapeB, holeClearance + lineWidthB ) )
{
wxLogError( "Missing expected Alternate shape for %s at %d %d",
m_parent->GetClass(),
Anchor( 0 ).x,
Anchor( 0 ).y );
Mark( MK_HOLE );
return true;
}
else
if( holeB && holeB->Collide( shapeA, holeClearance + lineWidthA ) )
{
shapeA = AlternateShape();
Mark( MK_ALT_SHAPE );
aOther->Mark( MK_HOLE );
return true;
}
if( holeA && holeB )
{
int holeToHoleClearance = aNode->GetHoleToHoleClearance( this, aOther );
if( holeA->Collide( holeB, holeToHoleClearance ) )
{
Mark( MK_HOLE );
aOther->Mark( MK_HOLE );
return true;
}
}
}
if( !Layers().IsMultilayer() && !iface->IsOnLayer( aOther, Layer() ) )
{
if( !aOther->AlternateShape() )
{
wxLogError( "Missing expected Alternate shape for %s at %d %d",
aOther->Parent()->GetClass(),
aOther->Anchor( 0 ).x,
aOther->Anchor( 0 ).y );
}
else
{
shapeB = aOther->AlternateShape();
aOther->Mark( MK_ALT_SHAPE );
}
}
if( !aOther->Layers().IsMultilayer() && !iface->IsFlashedOnLayer( this, aOther->Layer()) )
return false;
if( aNeedMTV )
return shapeA->Collide( shapeB, aClearance, aMTV );
else
return shapeA->Collide( shapeB, aClearance );
if( !Layers().IsMultilayer() && !iface->IsFlashedOnLayer( aOther, Layer()) )
return false;
int clearance = aNode->GetClearance( this, aOther );
return shapeA->Collide( shapeB, clearance + lineWidthA + lineWidthB );
}
bool ITEM::Collide( const ITEM* aOther, int aClearance, bool aNeedMTV, VECTOR2I* aMTV,
const NODE* aParentNode, bool aDifferentNetsOnly ) const
bool ITEM::Collide( const ITEM* aOther, const NODE* aNode, bool aDifferentNetsOnly ) const
{
if( collideSimple( aOther, aClearance, aNeedMTV, aMTV, aParentNode, aDifferentNetsOnly ) )
if( collideSimple( aOther, aNode, aDifferentNetsOnly ) )
return true;
// special case for "head" line with a via attached at the end.
// Special cases for "head" lines with vias attached at the end. Note that this does not
// support head-line-via to head-line-via collisions, but you can't route two independant
// tracks at once so it shouldn't come up.
if( m_kind == LINE_T )
{
const LINE* line = static_cast<const LINE*>( this );
if( line->EndsWithVia() && line->Via().collideSimple( aOther, aNode, aDifferentNetsOnly ) )
return true;
}
if( aOther->m_kind == LINE_T )
{
const LINE* line = static_cast<const LINE*>( aOther );
int clearance = aClearance - line->Width() / 2;
if( line->EndsWithVia() )
{
return collideSimple( &line->Via(), clearance, aNeedMTV, aMTV, aParentNode,
aDifferentNetsOnly );
}
if( line->EndsWithVia() && line->Via().collideSimple( this, aNode, aDifferentNetsOnly ) )
return true;
}
return false;
@ -109,14 +127,14 @@ std::string ITEM::KindStr() const
{
switch( m_kind )
{
case ARC_T: return "arc";
case LINE_T: return "line";
case SEGMENT_T: return "segment";
case VIA_T: return "via";
case JOINT_T: return "joint";
case SOLID_T: return "solid";
case DIFF_PAIR_T: return "diff-pair";
default: return "unknown";
case ARC_T: return "arc";
case LINE_T: return "line";
case SEGMENT_T: return "segment";
case VIA_T: return "via";
case JOINT_T: return "joint";
case SOLID_T: return "solid";
case DIFF_PAIR_T: return "diff-pair";
default: return "unknown";
}
}

View File

@ -41,8 +41,7 @@ enum LineMarker {
MK_VIOLATION = ( 1 << 3 ),
MK_LOCKED = ( 1 << 4 ),
MK_DP_COUPLED = ( 1 << 5 ),
MK_ALT_SHAPE = ( 1 << 6 ) // For instance, a simple annular ring when the pad has
// been dropped from a layer.
MK_HOLE = ( 1 << 6 )
};
@ -117,6 +116,11 @@ public:
return SHAPE_LINE_CHAIN();
}
virtual const SHAPE_LINE_CHAIN HoleHull( int aClearance, int aWalkaroundThickness, int aLayer ) const
{
return SHAPE_LINE_CHAIN();
}
/**
* Function Kind()
*
@ -199,13 +203,9 @@ public:
* Optionally returns a minimum translation vector for force propagation algorithm.
*
* @param aOther item to check collision against
* @param aClearance desired clearance
* @param aNeedMTV when true, the minimum translation vector is calculated
* @param aMTV the minimum translation vector
* @return true, if a collision was found.
*/
virtual bool Collide( const ITEM* aOther, int aClearance, bool aNeedMTV, VECTOR2I* aMTV,
const NODE* aParentNode, bool aDifferentNetsOnly = true ) const;
bool Collide( const ITEM* aOther, const NODE* aNode, bool aDifferentNetsOnly = true ) const;
/**
* Function Shape()
@ -215,12 +215,12 @@ public:
*/
virtual const SHAPE* Shape() const
{
return NULL;
return nullptr;
}
virtual const SHAPE* AlternateShape() const
virtual const SHAPE* Hole() const
{
return Shape();
return nullptr;
}
virtual void Mark( int aMarker ) const { m_marker = aMarker; }
@ -249,8 +249,7 @@ public:
bool IsRoutable() const { return m_routable; }
private:
bool collideSimple( const ITEM* aOther, int aClearance, bool aNeedMTV, VECTOR2I* aMTV,
const NODE* aParentNode, bool aDifferentNetsOnly ) const;
bool collideSimple( const ITEM* aOther, const NODE* aNode, bool aDifferentNetsOnly ) const;
protected:
PnsKind m_kind;

View File

@ -73,10 +73,10 @@ public:
PNS_PCBNEW_RULE_RESOLVER( BOARD* aBoard, PNS::ROUTER_IFACE* aRouterIface );
virtual ~PNS_PCBNEW_RULE_RESOLVER();
virtual bool CollideHoles( const PNS::ITEM* aA, const PNS::ITEM* aB,
bool aNeedMTV, VECTOR2I* aMTV ) const override;
virtual int Clearance( const PNS::ITEM* aA, const PNS::ITEM* aB ) override;
virtual int HoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB ) override;
virtual int HoleToHoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB ) override;
virtual int DpCoupledNet( int aNet ) override;
virtual int DpNetPolarity( int aNet ) override;
virtual bool DpNetPair( const PNS::ITEM* aItem, int& aNetP, int& aNetN ) override;
@ -97,6 +97,8 @@ private:
VIA m_dummyVia;
std::map<std::pair<const PNS::ITEM*, const PNS::ITEM*>, int> m_clearanceCache;
std::map<std::pair<const PNS::ITEM*, const PNS::ITEM*>, int> m_holeClearanceCache;
std::map<std::pair<const PNS::ITEM*, const PNS::ITEM*>, int> m_holeToHoleClearanceCache;
};
@ -137,44 +139,6 @@ int PNS_PCBNEW_RULE_RESOLVER::holeRadius( const PNS::ITEM* aItem ) const
}
bool PNS_PCBNEW_RULE_RESOLVER::CollideHoles( const PNS::ITEM* aA, const PNS::ITEM* aB,
bool aNeedMTV, VECTOR2I* aMTV ) const
{
VECTOR2I pos_a = aA->Shape()->Centre();
VECTOR2I pos_b = aB->Shape()->Centre();
// Holes with identical locations are allowable
if( pos_a == pos_b )
return false;
int radius_a = holeRadius( aA );
int radius_b = holeRadius( aB );
// Do both objects have holes?
if( radius_a > 0 && radius_b > 0 )
{
int holeToHoleMin = m_board->GetDesignSettings().m_HoleToHoleMin;
ecoord min_dist = holeToHoleMin + radius_a + radius_b;
ecoord min_dist_sq = min_dist * min_dist;
const VECTOR2I delta = pos_b - pos_a;
ecoord dist_sq = delta.SquaredEuclideanNorm();
if( dist_sq == 0 || dist_sq < min_dist_sq )
{
if( aNeedMTV )
*aMTV = delta.Resize( min_dist - sqrt( dist_sq ) + 3 ); // fixme: apparent rounding error
return true;
}
}
return false;
}
bool PNS_PCBNEW_RULE_RESOLVER::IsDiffPair( const PNS::ITEM* aA, const PNS::ITEM* aB )
{
int net_p, net_n;
@ -191,6 +155,11 @@ bool PNS_PCBNEW_RULE_RESOLVER::IsDiffPair( const PNS::ITEM* aA, const PNS::ITEM*
}
bool isEdge( const PNS::ITEM* aItem )
{
return aItem->Layer() == Edge_Cuts || aItem->Layer() == Margin;
}
bool PNS_PCBNEW_RULE_RESOLVER::QueryConstraint( PNS::CONSTRAINT_TYPE aType,
const PNS::ITEM* aItemA, const PNS::ITEM* aItemB,
@ -205,13 +174,16 @@ bool PNS_PCBNEW_RULE_RESOLVER::QueryConstraint( PNS::CONSTRAINT_TYPE aType,
switch ( aType )
{
case PNS::CONSTRAINT_TYPE::CT_CLEARANCE: hostType = CLEARANCE_CONSTRAINT; break;
case PNS::CONSTRAINT_TYPE::CT_WIDTH: hostType = TRACK_WIDTH_CONSTRAINT; break;
case PNS::CONSTRAINT_TYPE::CT_DIFF_PAIR_GAP: hostType = DIFF_PAIR_GAP_CONSTRAINT; break;
case PNS::CONSTRAINT_TYPE::CT_LENGTH: hostType = LENGTH_CONSTRAINT; break;
case PNS::CONSTRAINT_TYPE::CT_VIA_DIAMETER: hostType = VIA_DIAMETER_CONSTRAINT; break;
case PNS::CONSTRAINT_TYPE::CT_VIA_HOLE: hostType = HOLE_SIZE_CONSTRAINT; break;
default: return false; // should not happen
case PNS::CONSTRAINT_TYPE::CT_CLEARANCE: hostType = CLEARANCE_CONSTRAINT; break;
case PNS::CONSTRAINT_TYPE::CT_WIDTH: hostType = TRACK_WIDTH_CONSTRAINT; break;
case PNS::CONSTRAINT_TYPE::CT_DIFF_PAIR_GAP: hostType = DIFF_PAIR_GAP_CONSTRAINT; break;
case PNS::CONSTRAINT_TYPE::CT_LENGTH: hostType = LENGTH_CONSTRAINT; break;
case PNS::CONSTRAINT_TYPE::CT_VIA_DIAMETER: hostType = VIA_DIAMETER_CONSTRAINT; break;
case PNS::CONSTRAINT_TYPE::CT_VIA_HOLE: hostType = HOLE_SIZE_CONSTRAINT; break;
case PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE: hostType = HOLE_CLEARANCE_CONSTRAINT; break;
case PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE: hostType = EDGE_CLEARANCE_CONSTRAINT; break;
case PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE: hostType = HOLE_TO_HOLE_CONSTRAINT; break;
default: return false; // should not happen
}
BOARD_ITEM* parentA = aItemA ? aItemA->Parent() : nullptr;
@ -271,6 +243,9 @@ bool PNS_PCBNEW_RULE_RESOLVER::QueryConstraint( PNS::CONSTRAINT_TYPE aType,
case PNS::CONSTRAINT_TYPE::CT_DIFF_PAIR_GAP:
case PNS::CONSTRAINT_TYPE::CT_VIA_DIAMETER:
case PNS::CONSTRAINT_TYPE::CT_VIA_HOLE:
case PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE:
case PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE:
case PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE:
aConstraint->m_Value = hostConstraint.GetValue();
aConstraint->m_RuleName = hostConstraint.GetName();
aConstraint->m_Type = aType;
@ -291,7 +266,6 @@ int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* a
return it->second;
PNS::CONSTRAINT constraint;
bool ok = false;
int rv = 0;
if( aB && IsDiffPair( aA, aB ) )
@ -301,26 +275,72 @@ int PNS_PCBNEW_RULE_RESOLVER::Clearance( const PNS::ITEM* aA, const PNS::ITEM* a
&constraint ) )
{
rv = constraint.m_Value.Opt();
ok = true;
m_clearanceCache[ key ] = rv;
return rv;
}
}
if( !ok )
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, aA->Layer(),
&constraint ) )
{
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_CLEARANCE, aA, aB, aA->Layer(),
rv = constraint.m_Value.Min();
}
if( isEdge( aA ) || ( aB && isEdge( aB ) ) )
{
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_EDGE_CLEARANCE, aA, aB, aA->Layer(),
&constraint ) )
{
rv = constraint.m_Value.Min();
ok = true;
if( constraint.m_Value.Min() > rv )
rv = constraint.m_Value.Min();
}
}
// still no valid clearance rule? fall back to global minimum.
if( !ok )
rv = m_board->GetDesignSettings().m_MinClearance;
m_clearanceCache[ key ] = rv;
return rv;
}
int PNS_PCBNEW_RULE_RESOLVER::HoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB )
{
std::pair<const PNS::ITEM*, const PNS::ITEM*> key( aA, aB );
auto it = m_holeClearanceCache.find( key );
if( it != m_holeClearanceCache.end() )
return it->second;
PNS::CONSTRAINT constraint;
int rv = 0;
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_CLEARANCE, aA, aB, aA->Layer(),
&constraint ) )
{
rv = constraint.m_Value.Min();
}
m_holeClearanceCache[ key ] = rv;
return rv;
}
int PNS_PCBNEW_RULE_RESOLVER::HoleToHoleClearance( const PNS::ITEM* aA, const PNS::ITEM* aB )
{
std::pair<const PNS::ITEM*, const PNS::ITEM*> key( aA, aB );
auto it = m_holeToHoleClearanceCache.find( key );
if( it != m_holeToHoleClearanceCache.end() )
return it->second;
PNS::CONSTRAINT constraint;
int rv = 0;
if( QueryConstraint( PNS::CONSTRAINT_TYPE::CT_HOLE_TO_HOLE, aA, aB, aA->Layer(),
&constraint ) )
{
rv = constraint.m_Value.Min();
}
m_holeToHoleClearanceCache[ key ] = rv;
return rv;
}
@ -821,7 +841,7 @@ std::unique_ptr<PNS::SOLID> PNS_KICAD_IFACE_BASE::syncPad( PAD* aPad )
slot->SetWidth( slot->GetWidth() + bds.GetHolePlatingThickness() * 2 );
}
solid->SetAlternateShape( slot );
solid->SetHole( slot );
}
if( aPad->GetAttribute() == PAD_ATTRIB_NPTH )
@ -923,6 +943,10 @@ std::unique_ptr<PNS::VIA> PNS_KICAD_IFACE_BASE::syncVia( VIA* aVia )
via->SetIsFree( aVia->GetIsFree() );
BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
via->SetHole( SHAPE_CIRCLE( aVia->GetPosition(),
aVia->GetDrill() / 2 + bds.GetHolePlatingThickness() ) );
return via;
}
@ -1107,7 +1131,7 @@ bool PNS_KICAD_IFACE::IsAnyLayerVisible( const LAYER_RANGE& aLayer ) const
}
bool PNS_KICAD_IFACE::IsOnLayer( const PNS::ITEM* aItem, int aLayer ) const
bool PNS_KICAD_IFACE::IsFlashedOnLayer( const PNS::ITEM* aItem, int aLayer ) const
{
/// Default is all layers
if( aLayer < 0 )
@ -1170,7 +1194,7 @@ bool PNS_KICAD_IFACE::IsItemVisible( const PNS::ITEM* aItem ) const
void PNS_KICAD_IFACE_BASE::SyncWorld( PNS::NODE *aWorld )
{
int worstPadClearance = 0;
int worstClearance = m_board->GetDesignSettings().GetBiggestClearanceValue();
m_world = aWorld;
@ -1210,7 +1234,7 @@ void PNS_KICAD_IFACE_BASE::SyncWorld( PNS::NODE *aWorld )
if( std::unique_ptr<PNS::SOLID> solid = syncPad( pad ) )
aWorld->Add( std::move( solid ) );
worstPadClearance = std::max( worstPadClearance, pad->GetLocalClearance() );
worstClearance = std::max( worstClearance, pad->GetLocalClearance() );
}
syncTextItem( aWorld, &footprint->Reference(), footprint->Reference().GetLayer() );
@ -1256,15 +1280,13 @@ void PNS_KICAD_IFACE_BASE::SyncWorld( PNS::NODE *aWorld )
}
}
int worstRuleClearance = m_board->GetDesignSettings().GetBiggestClearanceValue();
// NB: if this were ever to become a long-lived object we would need to dirty its
// clearance cache here....
delete m_ruleResolver;
m_ruleResolver = new PNS_PCBNEW_RULE_RESOLVER( m_board, this );
aWorld->SetRuleResolver( m_ruleResolver );
aWorld->SetMaxClearance( 4 * std::max(worstPadClearance, worstRuleClearance ) );
aWorld->SetMaxClearance( 4 * worstClearance );
}

View File

@ -56,7 +56,7 @@ public:
void SetBoard( BOARD* aBoard );
void SyncWorld( PNS::NODE* aWorld ) override;
bool IsAnyLayerVisible( const LAYER_RANGE& aLayer ) const override { return true; };
bool IsOnLayer( const PNS::ITEM* aItem, int aLayer ) const override { return true; };
bool IsFlashedOnLayer( const PNS::ITEM* aItem, int aLayer ) const override { return true; };
bool IsItemVisible( const PNS::ITEM* aItem ) const override { return true; }
void HideItem( PNS::ITEM* aItem ) override {}
void DisplayItem( const PNS::ITEM* aItem, int aColor = 0, int aClearance = 0,
@ -115,7 +115,7 @@ public:
void EraseView() override;
bool IsAnyLayerVisible( const LAYER_RANGE& aLayer ) const override;
bool IsItemVisible( const PNS::ITEM* aItem ) const override;
bool IsOnLayer( const PNS::ITEM* aItem, int aLayer ) const override;
bool IsFlashedOnLayer( const PNS::ITEM* aItem, int aLayer ) const override;
void HideItem( PNS::ITEM* aItem ) override;
void DisplayItem( const PNS::ITEM* aItem, int aColor = 0, int aClearance = 0,
bool aEdit = false ) override;

View File

@ -478,55 +478,17 @@ bool LINE_PLACER::rhWalkOnly( const VECTOR2I& aP, LINE& aNewHead )
bool LINE_PLACER::rhMarkObstacles( const VECTOR2I& aP, LINE& aNewHead )
{
LINE newHead( m_head );
LINE bestHead( m_head );
bool hasBest = false;
buildInitialLine( aP, m_head );
buildInitialLine( aP, newHead );
NODE::OBSTACLES obstacles;
m_currentNode->QueryColliding( &newHead, obstacles );
// If we are allowing DRC violations, we don't push back to the hull
// If we are enforcing DRC violations, push back to the hull
if( !Settings().CanViolateDRC() )
{
int layer = m_head.Layer();
int lineWidth = m_placingVia ? m_head.Via().Diameter() : m_head.Width();
int clearance;
NODE::OPT_OBSTACLE obs = m_currentNode->NearestObstacle( &m_head );
for( OBSTACLE& obs : obstacles )
{
if( m_placingVia )
clearance = m_currentNode->GetClearance( obs.m_item, &m_head.Via() );
else
clearance = m_currentNode->GetClearance( obs.m_item, &m_head );
const SHAPE_LINE_CHAIN hull = obs.m_item->Hull( clearance, lineWidth, layer );
VECTOR2I nearest = hull.NearestPoint( aP );
Dbg()->AddLine( hull, 2, 10000 );
if( ( nearest - aP ).EuclideanNorm() < lineWidth + clearance )
{
buildInitialLine( nearest, newHead );
// We want the shortest line here to ensure we don't break a clearance
// rule on larger, overlapping items (e.g. vias)
if( !hasBest || newHead.CLine().Length() < bestHead.CLine().Length() )
{
bestHead = newHead;
hasBest = true;
}
}
}
if( obs && obs->m_distFirst != INT_MAX )
buildInitialLine( obs->m_ipFirst, m_head );
}
if( hasBest )
m_head = bestHead;
else
m_head = newHead;
aNewHead = m_head;
return static_cast<bool>( m_currentNode->CheckColliding( &m_head ) );

View File

@ -36,6 +36,8 @@
#include "pns_solid.h"
#include "pns_joint.h"
#include "pns_index.h"
#include "pns_debug_decorator.h"
#include "pns_router.h"
namespace PNS {
@ -103,6 +105,24 @@ int NODE::GetClearance( const ITEM* aA, const ITEM* aB ) const
}
int NODE::GetHoleClearance( const ITEM* aA, const ITEM* aB ) const
{
if( !m_ruleResolver )
return 0;
return m_ruleResolver->HoleClearance( aA, aB );
}
int NODE::GetHoleToHoleClearance( const ITEM* aA, const ITEM* aB ) const
{
if( !m_ruleResolver )
return 0;
return m_ruleResolver->HoleToHoleClearance( aA, aB );
}
NODE* NODE::Branch()
{
NODE* child = new NODE;
@ -194,8 +214,6 @@ struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR
bool m_differentNetsOnly;
int m_forceClearance;
DEFAULT_OBSTACLE_VISITOR( NODE::OBSTACLES& aTab, const ITEM* aItem, int aKindMask, bool aDifferentNetsOnly ) :
OBSTACLE_VISITOR( aItem ),
m_tab( aTab ),
@ -203,8 +221,7 @@ struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR
m_limitCount( -1 ),
m_matchCount( 0 ),
m_extraClearance( 0 ),
m_differentNetsOnly( aDifferentNetsOnly ),
m_forceClearance( -1 )
m_differentNetsOnly( aDifferentNetsOnly )
{
if( aItem && aItem->Kind() == ITEM::LINE_T )
{
@ -229,18 +246,7 @@ struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR
if( visit( aCandidate ) )
return true;
int clearance = m_extraClearance + m_node->GetClearance( aCandidate, m_item );
if( aCandidate->Kind() == ITEM::LINE_T ) // this should never happen.
{
assert( false );
clearance += static_cast<LINE*>( aCandidate )->Width() / 2;
}
if( m_forceClearance >= 0 )
clearance = m_forceClearance;
if( !aCandidate->Collide( m_item, clearance, false, nullptr, m_node, m_differentNetsOnly ) )
if( !aCandidate->Collide( m_item, m_node, m_differentNetsOnly ) )
return true;
OBSTACLE obs;
@ -259,24 +265,8 @@ struct NODE::DEFAULT_OBSTACLE_VISITOR : public OBSTACLE_VISITOR
};
int NODE::QueryColliding( const ITEM* aItem, OBSTACLE_VISITOR& aVisitor )
{
aVisitor.SetWorld( this, NULL );
m_index->Query( aItem, m_maxClearance, aVisitor );
// if we haven't found enough items, look in the root branch as well.
if( !isRoot() )
{
aVisitor.SetWorld( m_root, this );
m_root->m_index->Query( aItem, m_maxClearance, aVisitor );
}
return 0;
}
int NODE::QueryColliding( const ITEM* aItem, NODE::OBSTACLES& aObstacles, int aKindMask,
int aLimitCount, bool aDifferentNetsOnly, int aForceClearance )
int aLimitCount, bool aDifferentNetsOnly )
{
DEFAULT_OBSTACLE_VISITOR visitor( aObstacles, aItem, aKindMask, aDifferentNetsOnly );
@ -286,7 +276,7 @@ int NODE::QueryColliding( const ITEM* aItem, NODE::OBSTACLES& aObstacles, int aK
visitor.SetCountLimit( aLimitCount );
visitor.SetWorld( this, NULL );
visitor.m_forceClearance = aForceClearance;
// first, look for colliding items in the local index
m_index->Query( aItem, m_maxClearance, visitor );
@ -304,105 +294,131 @@ int NODE::QueryColliding( const ITEM* aItem, NODE::OBSTACLES& aObstacles, int aK
NODE::OPT_OBSTACLE NODE::NearestObstacle( const LINE* aLine, int aKindMask,
const std::set<ITEM*>* aRestrictedSet )
{
OBSTACLES obs_list;
bool found_isects = false;
OBSTACLES obstacleList;
obstacleList.reserve( 100 );
const SHAPE_LINE_CHAIN& line = aLine->CLine();
obs_list.reserve( 100 );
int n = 0;
for( int i = 0; i < line.SegmentCount(); i++ )
for( int i = 0; i < aLine->CLine().SegmentCount(); i++ )
{
const SEGMENT s( *aLine, line.CSegment( i ) );
n += QueryColliding( &s, obs_list, aKindMask );
const SEGMENT s( *aLine, aLine->CLine().CSegment( i ) );
QueryColliding( &s, obstacleList, aKindMask );
}
if( aLine->EndsWithVia() )
n += QueryColliding( &aLine->Via(), obs_list, aKindMask );
QueryColliding( &aLine->Via(), obstacleList, aKindMask );
if( !n )
if( obstacleList.empty() )
return OPT_OBSTACLE();
OBSTACLE nearest;
nearest.m_item = NULL;
nearest.m_distFirst = INT_MAX;
for( const OBSTACLE& obs : obs_list )
{
VECTOR2I ip_last;
int dist_max = INT_MIN;
auto updateNearest =
[&]( int dist, VECTOR2I pt, ITEM* item, const SHAPE_LINE_CHAIN& hull )
{
if( dist < nearest.m_distFirst )
{
nearest.m_distFirst = dist;
nearest.m_ipFirst = pt;
nearest.m_item = item;
nearest.m_hull = hull;
}
};
if( aRestrictedSet && aRestrictedSet->find( obs.m_item ) == aRestrictedSet->end() )
for( const OBSTACLE& obstacle : obstacleList )
{
if( aRestrictedSet && aRestrictedSet->find( obstacle.m_item ) == aRestrictedSet->end() )
continue;
std::vector<SHAPE_LINE_CHAIN::INTERSECTION> isect_list;
std::vector<SHAPE_LINE_CHAIN::INTERSECTION> intersectingPts;
int clearance = GetClearance( obs.m_item, aLine );
int clearance = GetClearance( obstacle.m_item, aLine ) + aLine->Width() / 2;
SHAPE_LINE_CHAIN obstacleHull = obstacle.m_item->Hull( clearance, 0, aLine->Layer() );
SHAPE_LINE_CHAIN obsHull = obs.m_item->Hull( clearance, aLine->Width(), aLine->Layer() );
ROUTER::GetInstance()->GetInterface()->GetDebugDecorator()->AddLine( obstacleHull, 2, 1000 );
if( aLine->EndsWithVia() )
{
clearance = GetClearance( obs.m_item, &aLine->Via() );
const VIA& via = aLine->Via();
int viaClearance = GetClearance( obstacle.m_item, &via );
int holeClearance = GetHoleClearance( obstacle.m_item, &via );
SHAPE_LINE_CHAIN viaHull = aLine->Via().Hull( clearance, aLine->Width() );
if( holeClearance + via.Drill() / 2 > viaClearance + via.Diameter() / 2 )
viaClearance = holeClearance + via.Drill() / 2 - via.Diameter() / 2;
viaHull.Intersect( obsHull, isect_list );
// ObstacleHull has line clearance and 1/2 line width already built in. So
// viaHull's clearance needs to be only that portion of the via clearance that
// is *in excess* of the line clearance.
viaClearance = std::max( 0, viaClearance - clearance );
for( const SHAPE_LINE_CHAIN::INTERSECTION& isect : isect_list )
SHAPE_LINE_CHAIN viaHull = via.Hull( viaClearance, 0 );
obstacleHull.Intersect( viaHull, intersectingPts );
for( const SHAPE_LINE_CHAIN::INTERSECTION& ip : intersectingPts )
{
int dist = aLine->CLine().Length() +
( isect.p - aLine->Via().Pos() ).EuclideanNorm();
if( dist < nearest.m_distFirst )
{
found_isects = true;
nearest.m_distFirst = dist;
nearest.m_ipFirst = isect.p;
nearest.m_item = obs.m_item;
nearest.m_hull = obsHull;
}
if( dist > dist_max )
{
dist_max = dist;
ip_last = isect.p;
}
int dist = aLine->CLine().Length() + ( ip.p - via.Pos() ).EuclideanNorm();
updateNearest( dist, ip.p, obstacle.m_item, obstacleHull );
}
}
isect_list.clear();
intersectingPts.clear();
obstacleHull.Intersect( aLine->CLine(), intersectingPts );
obsHull.Intersect( aLine->CLine(), isect_list );
for( const SHAPE_LINE_CHAIN::INTERSECTION& isect : isect_list )
for( const SHAPE_LINE_CHAIN::INTERSECTION& ip : intersectingPts )
{
int dist = aLine->CLine().PathLength( isect.p );
if( dist < nearest.m_distFirst )
{
found_isects = true;
nearest.m_distFirst = dist;
nearest.m_ipFirst = isect.p;
nearest.m_item = obs.m_item;
nearest.m_hull = obsHull;
}
if( dist > dist_max )
{
dist_max = dist;
ip_last = isect.p;
}
int dist = aLine->CLine().PathLength( ip.p );
updateNearest( dist, ip.p, obstacle.m_item, obstacleHull );
}
nearest.m_ipLast = ip_last;
nearest.m_distLast = dist_max;
if( obstacle.m_item->Hole() )
{
clearance = GetHoleClearance( obstacle.m_item, aLine ) + aLine->Width() / 2;
obstacleHull = obstacle.m_item->HoleHull( clearance, 0, aLine->Layer() );
ROUTER::GetInstance()->GetInterface()->GetDebugDecorator()->AddLine( obstacleHull, 3, 1000 );
if( aLine->EndsWithVia() )
{
const VIA& via = aLine->Via();
int viaClearance = GetClearance( obstacle.m_item, &via );
int holeClearance = GetHoleClearance( obstacle.m_item, &via );
int holeToHole = GetHoleToHoleClearance( obstacle.m_item, &via );
if( holeClearance + via.Drill() / 2 > viaClearance + via.Diameter() / 2 )
viaClearance = holeClearance + via.Drill() / 2 - via.Diameter() / 2;
if( holeToHole + via.Drill() / 2 > viaClearance + via.Diameter() / 2 )
viaClearance = holeToHole + via.Drill() / 2 - via.Diameter() / 2;
// ObsHull has line clearance and 1/2 line width already built in. So viaHull's
// clearance needs to be just that which is *in excess* of clearance.
viaClearance = std::max( 0, viaClearance - clearance );
SHAPE_LINE_CHAIN viaHull = via.Hull( viaClearance, 0 );
obstacleHull.Intersect( viaHull, intersectingPts );
for( const SHAPE_LINE_CHAIN::INTERSECTION& ip : intersectingPts )
{
int dist = aLine->CLine().Length() + ( ip.p - via.Pos() ).EuclideanNorm();
updateNearest( dist, ip.p, obstacle.m_item, obstacleHull );
}
}
intersectingPts.clear();
obstacleHull.Intersect( aLine->CLine(), intersectingPts );
for( const SHAPE_LINE_CHAIN::INTERSECTION& ip : intersectingPts )
{
int dist = aLine->CLine().PathLength( ip.p );
updateNearest( dist, ip.p, obstacle.m_item, obstacleHull );
}
}
}
if( !found_isects )
nearest.m_item = obs_list[0].m_item;
if( nearest.m_distFirst == INT_MAX )
nearest.m_item = obstacleList[0].m_item;
return nearest;
}
@ -458,25 +474,6 @@ NODE::OPT_OBSTACLE NODE::CheckColliding( const ITEM* aItemA, int aKindMask )
}
bool NODE::CheckColliding( const ITEM* aItemA, const ITEM* aItemB, int aKindMask, int aForceClearance )
{
assert( aItemB );
int clearance;
if( aForceClearance >= 0 )
clearance = aForceClearance;
else
clearance = GetClearance( aItemA, aItemB );
// fixme: refactor
if( aItemA->Kind() == ITEM::LINE_T )
clearance += static_cast<const LINE*>( aItemA )->Width() / 2;
if( aItemB->Kind() == ITEM::LINE_T )
clearance += static_cast<const LINE*>( aItemB )->Width() / 2;
return aItemA->Collide( aItemB, clearance, false, nullptr, this );
}
struct HIT_VISITOR : public OBSTACLE_VISITOR
{
ITEM_SET& m_items;

View File

@ -64,7 +64,8 @@ enum class CONSTRAINT_TYPE
CT_VIA_DIAMETER = 5,
CT_VIA_HOLE = 6,
CT_HOLE_CLEARANCE = 7,
CT_EDGE_CLEARANCE = 8
CT_EDGE_CLEARANCE = 8,
CT_HOLE_TO_HOLE = 9
};
struct CONSTRAINT
@ -82,10 +83,10 @@ class RULE_RESOLVER
public:
virtual ~RULE_RESOLVER() {}
virtual bool CollideHoles( const ITEM* aA, const ITEM* aB,
bool aNeedMTV, VECTOR2I* aMTV ) const = 0;
virtual int Clearance( const ITEM* aA, const ITEM* aB ) = 0;
virtual int HoleClearance( const ITEM* aA, const ITEM* aB ) = 0;
virtual int HoleToHoleClearance( const ITEM* aA, const ITEM* aB ) = 0;
virtual int DpCoupledNet( int aNet ) = 0;
virtual int DpNetPolarity( int aNet ) = 0;
virtual bool DpNetPair( const ITEM* aItem, int& aNetP, int& aNetN ) = 0;
@ -114,12 +115,11 @@ struct OBSTACLE
///> Hull of the colliding m_item
SHAPE_LINE_CHAIN m_hull;
///> First and last intersection point between the head item and the hull
///> of the colliding m_item
VECTOR2I m_ipFirst, m_ipLast;
///> First intersection point between the head item and the hull of the colliding m_item
VECTOR2I m_ipFirst;
///> ... and the distance thereof
int m_distFirst, m_distLast;
int m_distFirst;
};
/**
@ -180,6 +180,8 @@ public:
///> Returns the expected clearance between items a and b.
int GetClearance( const ITEM* aA, const ITEM* aB ) const;
int GetHoleClearance( const ITEM* aA, const ITEM* aB ) const;
int GetHoleToHoleClearance( const ITEM* aA, const ITEM* aB ) const;
///> Returns the pre-set worst case clearance between any pair of items
int GetMaxClearance() const
@ -230,16 +232,13 @@ public:
OBSTACLES& aObstacles,
int aKindMask = ITEM::ANY_T,
int aLimitCount = -1,
bool aDifferentNetsOnly = true,
int aForceClearance = -1 );
bool aDifferentNetsOnly = true );
int QueryJoints( const BOX2I& aBox,
std::vector<JOINT*>& aJoints,
LAYER_RANGE aLayerMask = LAYER_RANGE::All(),
int aKindMask = ITEM::ANY_T );
int QueryColliding( const ITEM* aItem, OBSTACLE_VISITOR& aVisitor );
/**
* Function NearestObstacle()
*
@ -278,22 +277,6 @@ public:
OPT_OBSTACLE CheckColliding( const ITEM_SET& aSet,
int aKindMask = ITEM::ANY_T );
/**
* Function CheckColliding()
*
* Checks if 2 items collide.
* and if found, returns the obstacle.
* @param aItemA first item to find collisions with
* @param aItemB second item to find collisions with
* @param aKindMask mask of obstacle types to take into account
* @return the obstacle, if found, otherwise empty.
*/
bool CheckColliding( const ITEM* aItemA,
const ITEM* aItemB,
int aKindMask = ITEM::ANY_T,
int aForceClearance = -1 );
/**
* Function HitTest()
*
@ -439,7 +422,7 @@ public:
void AllItemsInNet( int aNet, std::set<ITEM*>& aItems, int aKindMask = -1 );
void ClearRanks( int aMarkerMask = MK_HEAD | MK_VIOLATION | MK_ALT_SHAPE );
void ClearRanks( int aMarkerMask = MK_HEAD | MK_VIOLATION | MK_HOLE );
void RemoveByMarker( int aMarker );

View File

@ -158,9 +158,7 @@ struct OPTIMIZER::CACHE_VISITOR
if( !( m_mask & aOtherItem->Kind() ) )
return true;
int clearance = m_node->GetClearance( aOtherItem, m_ourItem );
if( !aOtherItem->Collide( m_ourItem, clearance, false, nullptr, m_node ) )
if( !aOtherItem->Collide( m_ourItem, m_node ) )
return true;
m_collidingItem = aOtherItem;
@ -1156,7 +1154,7 @@ bool verifyDpBypass( NODE* aNode, DIFF_PAIR* aPair, bool aRefIsP, const SHAPE_LI
LINE refLine ( aRefIsP ? aPair->PLine() : aPair->NLine(), aNewRef );
LINE coupledLine ( aRefIsP ? aPair->NLine() : aPair->PLine(), aNewCoupled );
if( aNode->CheckColliding( &refLine, &coupledLine, ITEM::ANY_T, aPair->Gap() - 10 ) )
if( refLine.Collide( &coupledLine, aNode ) )
return false;
if( aNode->CheckColliding ( &refLine ) )

View File

@ -308,7 +308,13 @@ void ROUTER::markViolations( NODE* aNode, ITEM_SET& aCurrent, NODE::ITEM_VECTOR&
for( OBSTACLE& obs : obstacles )
{
int clearance = aNode->GetClearance( item, obs.m_item );
int clearance;
if( ( obs.m_item->Marker() & MK_HOLE ) > 0 )
clearance = aNode->GetHoleClearance( item, obs.m_item );
else
clearance = aNode->GetClearance( item, obs.m_item );
std::unique_ptr<ITEM> tmp( obs.m_item->Clone() );
tmp->Mark( tmp->Marker() | MK_VIOLATION );
m_iface->DisplayItem( tmp.get(), -1, clearance );
@ -370,7 +376,16 @@ void ROUTER::movePlacing( const VECTOR2I& aP, ITEM* aEndItem )
m_iface->DisplayItem( l, -1, clearance );
if( l->EndsWithVia() )
m_iface->DisplayItem( &l->Via(), -1, clearance );
{
const VIA& via = l->Via();
int viaClearance = GetRuleResolver()->Clearance( &via, nullptr );
int holeClearance = GetRuleResolver()->HoleClearance( &via, nullptr );
if( holeClearance + via.Drill() / 2 > viaClearance + via.Diameter() / 2 )
viaClearance = holeClearance + via.Drill() / 2 - via.Diameter() / 2;
m_iface->DisplayItem( &l->Via(), -1, viaClearance );
}
}
//ITEM_SET tmp( &current );

View File

@ -100,7 +100,7 @@ enum DRAG_MODE
virtual void RemoveItem( ITEM* aItem ) = 0;
virtual bool IsAnyLayerVisible( const LAYER_RANGE& aLayer ) const = 0;
virtual bool IsItemVisible( const PNS::ITEM* aItem ) const = 0;
virtual bool IsOnLayer( const PNS::ITEM* aItem, int aLayer ) const = 0;
virtual bool IsFlashedOnLayer( const PNS::ITEM* aItem, int aLayer ) const = 0;
virtual void DisplayItem( const ITEM* aItem, int aColor = -1, int aClearance = -1,
bool aEdit = false ) = 0;
virtual void DisplayRatline( const SHAPE_LINE_CHAIN& aRatline, int aColor = -1 ) = 0;

View File

@ -23,6 +23,8 @@
#include <cassert>
#include <math/box2.h>
#include <geometry/shape_compound.h>
#include "pns_arc.h"
#include "pns_line.h"
#include "pns_node.h"
@ -72,6 +74,14 @@ int SHOVE::getClearance( const ITEM* aA, const ITEM* aB ) const
return m_currentNode->GetClearance( aA, aB );
}
int SHOVE::getHoleClearance( const ITEM* aA, const ITEM* aB ) const
{
if( m_forceClearance >= 0 )
return m_forceClearance;
return m_currentNode->GetHoleClearance( aA, aB );
}
void SHOVE::sanityCheck( LINE* aOld, LINE* aNew )
{
@ -129,6 +139,11 @@ bool SHOVE::checkBumpDirection( const LINE& aCurrent, const LINE& aObstacle, con
SHOVE::SHOVE_STATUS SHOVE::walkaroundLoneVia( LINE& aCurrent, LINE& aObstacle, LINE& aShoved )
{
int clearance = getClearance( &aCurrent, &aObstacle );
int holeClearance = getHoleClearance( &aCurrent.Via(), &aObstacle );
if( holeClearance + aCurrent.Via().Drill() / 2 > clearance + aCurrent.Via().Diameter() / 2 )
clearance = holeClearance + aCurrent.Via().Drill() / 2 - aCurrent.Via().Diameter() / 2;
const SHAPE_LINE_CHAIN hull = aCurrent.Via().Hull( clearance, aObstacle.Width(), aCurrent.Layer() );
SHAPE_LINE_CHAIN path_cw;
SHAPE_LINE_CHAIN path_ccw;
@ -152,7 +167,7 @@ SHOVE::SHOVE_STATUS SHOVE::walkaroundLoneVia( LINE& aCurrent, LINE& aObstacle, L
aShoved.SetShape( shortest );
if( m_currentNode->CheckColliding( &aShoved, &aCurrent ) )
if( aShoved.Collide( &aCurrent, m_currentNode ) )
return SH_INCOMPLETE;
return SH_OK;
@ -234,7 +249,7 @@ SHOVE::SHOVE_STATUS SHOVE::processHullSet( LINE& aCurrent, LINE& aObstacle,
continue;
}
bool colliding = m_currentNode->CheckColliding( &l, &aCurrent, ITEM::ANY_T, m_forceClearance );
bool colliding = l.Collide( &aCurrent, m_currentNode );
#ifdef DEBUG
char str[128];
@ -248,7 +263,7 @@ SHOVE::SHOVE_STATUS SHOVE::processHullSet( LINE& aCurrent, LINE& aObstacle,
for( ITEM* item : jtStart->LinkList() )
{
if( m_currentNode->CheckColliding( item, &l ) )
if( item->Collide( &l, m_currentNode ) )
colliding = true;
}
}
@ -321,7 +336,16 @@ SHOVE::SHOVE_STATUS SHOVE::ProcessSingleLine( LINE& aCurrent, LINE& aObstacle, L
}
if( viaOnEnd )
hulls.push_back( aCurrent.Via().Hull( clearance, w ) );
{
const VIA& via = aCurrent.Via();
int viaClearance = getClearance( &via, &aObstacle );
int holeClearance = getHoleClearance( &via, &aObstacle );
if( holeClearance + via.Drill() / 2 > viaClearance + via.Diameter() / 2 )
viaClearance = holeClearance + via.Drill() / 2 - via.Diameter() / 2;
hulls.push_back( aCurrent.Via().Hull( viaClearance, w ) );
}
#ifdef DEBUG
char str[128];
@ -530,7 +554,7 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingSolid( LINE& aCurrent, ITEM* aObstacle )
}
}
if( via && m_currentNode->CheckColliding( via, aObstacle ) )
if( via && via->Collide( aObstacle, m_currentNode ) )
return onCollidingVia( aObstacle, via );
}
@ -598,7 +622,7 @@ SHOVE::SHOVE_STATUS SHOVE::onCollidingSolid( LINE& aCurrent, ITEM* aObstacle )
{
LINE lastLine = m_lineStack.front();
if( m_currentNode->CheckColliding( &lastLine, &walkaroundLine ) )
if( lastLine.Collide( &walkaroundLine, m_currentNode ) )
{
LINE dummy( lastLine );
@ -834,73 +858,51 @@ SHOVE::SHOVE_STATUS SHOVE::pushOrShoveVia( VIA* aVia, const VECTOR2I& aForce, in
*/
SHOVE::SHOVE_STATUS SHOVE::onCollidingVia( ITEM* aCurrent, VIA* aObstacleVia )
{
int clearance = getClearance( aCurrent, aObstacleVia ) + PNS_HULL_MARGIN;
int clearance = getClearance( aCurrent, aObstacleVia );
LINE_PAIR_VEC draggedLines;
bool lineCollision = false;
bool viaCollision = false;
bool holeCollision = false;
LINE* currentLine = NULL;
VECTOR2I mtvLine; // Minimum translation vector to correct line collisions
VECTOR2I mtvVia; // MTV to correct via collisions
VECTOR2I mtvHoles; // MTV to correct hole collisions
VECTOR2I mtvSolid; // MTV to correct solid collisions
VECTOR2I mtv; // Union of relevant MTVs (will correct all collisions)
VECTOR2I mtv;
int rank = -1;
SHAPE_COMPOUND shape;
if( aCurrent->OfKind( ITEM::LINE_T ) )
{
LINE* currentLine = (LINE*) aCurrent;
#if 0
m_logger.NewGroup( "push-via-by-line", m_iter );
m_logger.Log( aCurrent, 4, "current" );
m_logger.NewGroup( "push-via-by-line", m_iter );
m_logger.Log( currentLine, 4, "current" );
#endif
currentLine = (LINE*) aCurrent;
lineCollision = aObstacleVia->Shape()->Collide( currentLine->Shape(),
clearance + currentLine->Width() / 2,
&mtvLine );
shape.AddShape( currentLine->Shape()->Clone() );
// SHAPE_LINE_CHAIN collisions don't currently pay any attention to the line-chain's
// width, so we have to add it to the clearance.
clearance += currentLine->Width() / 2;
// Add a second shape for the via (if any)
if( currentLine->EndsWithVia() )
{
int currentNet = currentLine->Net();
int obstacleNet = aObstacleVia->Net();
const VIA& currentVia = currentLine->Via();
int viaClearance = getClearance( &currentVia, aObstacleVia );
int holeClearance = getHoleClearance( &currentVia, aObstacleVia );
int effectiveRadius = std::max( currentVia.Diameter() / 2 + viaClearance,
currentVia.Drill() / 2 + holeClearance );
if( currentNet != obstacleNet && currentNet >= 0 && obstacleNet >= 0 )
{
viaCollision = currentLine->Via().Shape()->Collide( aObstacleVia->Shape(),
clearance, &mtvVia );
}
// hole-to-hole is a mechanical constraint (broken drill bits), not an electrical
// one, so it has to be checked irrespective of matching nets.
// temporarily removed hole-to-hole collision check due to conflicts with the
// springback algorithm...
// we need to figure out a better solution here - TW
holeCollision = false; //rr->CollideHoles( &currentLine->Via(), aObstacleVia, true, &mtvHoles );
// Since we have to run the collision with the line's clearance + 1/2 its width (as
// it's the only way to take the SHAPE_LINE_CHAIN's width into account), we need to
// subtract that clearance back out of the via's effective radius.
shape.AddShape( new SHAPE_CIRCLE( currentVia.Pos(), effectiveRadius - clearance ) );
}
// These aren't /actually/ lengths as we don't bother to do the square-root part,
// but we're just comparing them to each other so it's faster this way.
ecoord lineMTVLength = lineCollision ? mtvLine.SquaredEuclideanNorm() : 0;
ecoord viaMTVLength = viaCollision ? mtvVia.SquaredEuclideanNorm() : 0;
ecoord holeMTVLength = holeCollision ? mtvHoles.SquaredEuclideanNorm() : 0;
if( lineMTVLength >= viaMTVLength && lineMTVLength >= holeMTVLength )
mtv = mtvLine;
else if( viaMTVLength >= lineMTVLength && viaMTVLength >= holeMTVLength )
mtv = mtvVia;
else
mtv = mtvHoles;
rank = currentLine->Rank();
}
else if( aCurrent->OfKind( ITEM::SOLID_T ) )
{
aObstacleVia->Shape()->Collide( aCurrent->Shape(), clearance, &mtvSolid );
mtv = -mtvSolid;
rank = aCurrent->Rank() + 10000;
shape.AddShape( aCurrent->Shape()->Clone() );
}
aObstacleVia->Shape()->Collide( &shape, clearance + PNS_HULL_MARGIN, &mtv );
rank = aCurrent->Rank() + 10000;
return pushOrShoveVia( aObstacleVia, mtv, rank );
}
@ -1111,8 +1113,7 @@ SHOVE::SHOVE_STATUS SHOVE::shoveIteration( int aIter )
{
wxLogTrace( "PNS", "iter %d: reverse-collide-via", aIter );
if( currentLine.EndsWithVia()
&& m_currentNode->CheckColliding( &currentLine.Via(), (VIA*) ni ) )
if( currentLine.EndsWithVia() )
{
st = SH_INCOMPLETE;
}

View File

@ -153,6 +153,7 @@ private:
SHOVE_STATUS shoveMainLoop();
int getClearance( const ITEM* aA, const ITEM* aB ) const;
int getHoleClearance( const ITEM* aA, const ITEM* aB ) const;
std::vector<SPRINGBACK_TAG> m_nodeStack;
std::vector<LINE> m_lineStack;

View File

@ -79,21 +79,16 @@ static const SHAPE_LINE_CHAIN buildHullForPrimitiveShape( const SHAPE* aShape, i
const SHAPE_LINE_CHAIN SOLID::Hull( int aClearance, int aWalkaroundThickness, int aLayer ) const
{
SHAPE* shape = m_shape;
if( !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( this, aLayer ) )
return HoleHull( aClearance, aWalkaroundThickness, aLayer );
if( !ROUTER::GetInstance()->GetInterface()->IsOnLayer( this, aLayer ) )
if( !m_shape )
return SHAPE_LINE_CHAIN();
if( m_shape->Type() == SH_COMPOUND )
{
/// The alternate shape is defined for THT pads. If we don't have an alternate shape
/// then the solid shape does not exist on this layer
if( !m_alternateShape )
return SHAPE_LINE_CHAIN();
SHAPE_COMPOUND* cmpnd = static_cast<SHAPE_COMPOUND*>( m_shape );
shape = m_alternateShape;
}
if( shape->Type() == SH_COMPOUND )
{
auto cmpnd = static_cast<SHAPE_COMPOUND*>( shape );
if ( cmpnd->Shapes().size() == 1 )
{
return buildHullForPrimitiveShape( cmpnd->Shapes()[0], aClearance, aWalkaroundThickness );
@ -107,10 +102,35 @@ const SHAPE_LINE_CHAIN SOLID::Hull( int aClearance, int aWalkaroundThickness, in
}
else
{
return buildHullForPrimitiveShape( 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
{
// fixme - shouldn't happen but one day we should move TransformShapeWithClearanceToPolygon()
// to the Geometry Library
return SHAPE_LINE_CHAIN();
}
}
else
{
return buildHullForPrimitiveShape( m_hole, aClearance, aWalkaroundThickness );
}
return SHAPE_LINE_CHAIN();
}

View File

@ -38,7 +38,7 @@ public:
SOLID() :
ITEM( SOLID_T ),
m_shape( NULL ),
m_alternateShape( NULL )
m_hole( NULL )
{
m_movable = false;
m_padToDie = 0;
@ -48,7 +48,7 @@ public:
~SOLID()
{
delete m_shape;
delete m_alternateShape;
delete m_hole;
}
SOLID( const SOLID& aSolid ) :
@ -59,10 +59,10 @@ public:
else
m_shape = nullptr;
if( aSolid.m_alternateShape )
m_alternateShape = aSolid.m_alternateShape->Clone();
if( aSolid.m_hole )
m_hole = aSolid.m_hole->Clone();
else
m_alternateShape = nullptr;
m_hole = nullptr;
m_pos = aSolid.m_pos;
m_padToDie = aSolid.m_padToDie;
@ -77,21 +77,24 @@ public:
const SHAPE* Shape() const override { return m_shape; }
const SHAPE* AlternateShape() const override { return m_alternateShape; }
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 SetAlternateShape( SHAPE* shape )
void SetHole( SHAPE* shape )
{
delete m_alternateShape;
m_alternateShape = shape;
delete m_hole;
m_hole = shape;
}
const VECTOR2I& Pos() const { return m_pos; }
@ -119,7 +122,7 @@ public:
private:
VECTOR2I m_pos;
SHAPE* m_shape;
SHAPE* m_alternateShape;
SHAPE* m_hole;
VECTOR2I m_offset;
int m_padToDie;
double m_orientation; // in 1/10 degrees, matching PAD

View File

@ -112,12 +112,21 @@ const SHAPE_LINE_CHAIN ArcHull( const SHAPE_ARC& aSeg, int aClearance,
const SHAPE_LINE_CHAIN SegmentHull ( const SHAPE_SEGMENT& aSeg, int aClearance,
int aWalkaroundThickness )
{
int d = aSeg.GetWidth() / 2 + aClearance + aWalkaroundThickness / 2 + HULL_MARGIN;
int cl = aClearance + aWalkaroundThickness / 2 + HULL_MARGIN;
int d = aSeg.GetWidth() / 2 + cl;
int x = (int)( 2.0 / ( 1.0 + M_SQRT2 ) * d );
const VECTOR2I a = aSeg.GetSeg().A;
const VECTOR2I b = aSeg.GetSeg().B;
if( a == b )
{
return OctagonalHull( a - VECTOR2I( aSeg.GetWidth() / 2, aSeg.GetWidth() / 2 ),
VECTOR2I( aSeg.GetWidth(), aSeg.GetWidth() ),
cl + 1,
0.52 * d );
}
VECTOR2I dir = b - a;
VECTOR2I p0 = dir.Perpendicular().Resize( d );
VECTOR2I ds = dir.Perpendicular().Resize( x / 2 );

View File

@ -76,7 +76,7 @@ const SHAPE_LINE_CHAIN VIA::Hull( int aClearance, int aWalkaroundThickness, int
int cl = ( aClearance + aWalkaroundThickness / 2 );
int width = m_diameter;
if( !ROUTER::GetInstance()->GetInterface()->IsOnLayer( this, aLayer ) )
if( !ROUTER::GetInstance()->GetInterface()->IsFlashedOnLayer( this, aLayer ) )
width = m_drill;
return OctagonalHull( m_pos -

View File

@ -67,7 +67,7 @@ public:
m_diameter = aDiameter;
m_drill = aDrill;
m_shape = SHAPE_CIRCLE( aPos, aDiameter / 2 );
m_alternateShape = SHAPE_CIRCLE( m_pos, aDrill / 2 );
m_hole = SHAPE_CIRCLE( m_pos, aDrill / 2 );
m_viaType = aViaType;
m_isFree = false;
}
@ -81,7 +81,7 @@ public:
m_pos = aB.m_pos;
m_diameter = aB.m_diameter;
m_shape = SHAPE_CIRCLE( m_pos, m_diameter / 2 );
m_alternateShape = SHAPE_CIRCLE( m_pos, aB.m_drill / 2 );
m_hole = SHAPE_CIRCLE( m_pos, aB.m_drill / 2 );
m_marker = aB.m_marker;
m_rank = aB.m_rank;
m_drill = aB.m_drill;
@ -151,9 +151,14 @@ public:
return &m_shape;
}
const SHAPE* AlternateShape() const override
const SHAPE_CIRCLE* Hole() const override
{
return &m_alternateShape;
return &m_hole;
}
void SetHole( const SHAPE_CIRCLE& aHole )
{
m_hole = aHole;
}
VIA* Clone() const override;
@ -179,7 +184,7 @@ private:
int m_drill;
VECTOR2I m_pos;
SHAPE_CIRCLE m_shape;
SHAPE_CIRCLE m_alternateShape;
SHAPE_CIRCLE m_hole;
VIATYPE m_viaType;
bool m_isFree;
};

View File

@ -86,8 +86,8 @@ void ROUTER_PREVIEW_ITEM::Update( const PNS::ITEM* aItem )
m_color.a = 0.8;
m_depth = BaseOverlayDepth - aItem->Layers().Start();
if( ( aItem->Marker() & PNS::MK_ALT_SHAPE ) && aItem->AlternateShape() )
m_shape = aItem->AlternateShape()->Clone();
if(( aItem->Marker() & PNS::MK_HOLE ) && aItem->Hole() )
m_shape = aItem->Hole()->Clone();
else
m_shape = aItem->Shape()->Clone();