KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_item.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright (C) 2013-2014 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * Author: Tomasz Wlostowski <[email protected]>
7 *
8 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#include "pns_node.h"
23#include "pns_item.h"
24#include "pns_line.h"
25#include "pns_router.h"
26
29
30#include <board_item.h>
31
32
34
35namespace PNS {
36
37// prune self-collisions, i.e. a via/pad annular ring with its own hole
38static bool shouldWeConsiderHoleCollisions( const ITEM* aItem, const ITEM* aHead )
39{
40 const HOLE* holeI = aItem->OfKind( ITEM::HOLE_T ) ? static_cast<const HOLE*>( aItem ) : nullptr;
41 const HOLE* holeH = aHead->OfKind( ITEM::HOLE_T ) ? static_cast<const HOLE*>( aHead ) : nullptr;
42
43 if( holeI && holeH ) // hole-to-hole case
44 {
45 const ITEM* parentI = holeI->ParentPadVia();
46 const ITEM* parentH = holeH->ParentPadVia();
47
48 if( !parentH || !parentI )
49 return true;
50
51 const VIA* parentViaI = dyn_cast<const VIA*>( parentI );
52 const VIA* parentViaH = dyn_cast<const VIA*>( parentH );
53
54 // Note to self: the if() below is an ugly heuristic to determine if we aren't trying
55 // to check for collisions of the hole of the via with another (although identical)
56 // copy of it. Such case occurs when checking a LINE against a NODE where this LINE
57 // has been already added. LINE has no notion of ownership of it's via (it's just a
58 // copy) and before hole-to-hole clearance support has been introduced it didn't matter
59 // becasue we didn't consider collisions of the objects belonging to the same net anyway
60 // Now that hole clearance check doesn't care about the nets assigned to the parent
61 // vias/solids, I'll probably have to refactor the LINE class to manage ownership of
62 // its (optional) VIA. For the moment, we just treat via holes that are geometrically
63 // identical and belonging to the same net as non-colliding.
64
65 if( parentViaI && parentViaH && parentViaI->Pos() == parentViaH->Pos()
66 && parentViaI->PadstackMatches( *parentViaH )
67 && parentViaI->Net() == parentViaH->Net()
68 && parentViaI->Drill() == parentViaH->Drill() )
69 return false;
70
71 return parentI != parentH;
72 }
73
74 if( holeI )
75 return holeI->ParentPadVia() != aHead;
76 else if( holeH )
77 return holeH->ParentPadVia() != aItem;
78 else
79 return true;
80}
81
82
83std::set<int> ITEM::RelevantShapeLayers( const ITEM* aOther ) const
84{
85 std::vector<int> myLayers = UniqueShapeLayers();
86 std::vector<int> otherLayers = aOther->UniqueShapeLayers();
87
88 if( !HasUniqueShapeLayers() && !aOther->HasUniqueShapeLayers() )
89 return { -1 };
90
91 // TODO(JE) at this point we should also mask off the layers of each item.
92 // In the case that one item is a via and the other is a track, we don't want to test
93 // more than once even if the via has multiple unique layers
94
95 std::set<int> relevantLayers;
96
97 std::set_union( myLayers.begin(), myLayers.end(), otherLayers.begin(), otherLayers.end(),
98 std::inserter( relevantLayers, relevantLayers.begin() ) );
99
100 return relevantLayers;
101}
102
103
104bool ITEM::collideSimple( const ITEM* aHead, const NODE* aNode, int aLayer,
105 COLLISION_SEARCH_CONTEXT* aCtx ) const
106{
107 // Note: if 'this' is a pad or a via then its hole is a separate PNS::ITEM in the node's
108 // index and we don't need to deal with holeI here. The same is *not* true of the routing
109 // "head", so we do need to handle holeH.
110 int lineWidthI = 0;
111
112 //const SHAPE* shapeH = aHead->Shape();
113 const HOLE* holeH = aHead->Hole();
114 const HOLE* holeI = Hole();
115
116 int lineWidthH = 0;
117 bool collisionsFound = false;
118
119 if( this == aHead ) // we cannot be self-colliding
120 return false;
121
122 if ( !shouldWeConsiderHoleCollisions( this, aHead ) )
123 return false;
124
125 // Special cases for "head" lines with vias attached at the end. Note that this does not
126 // support head-line-via to head-line-via collisions, but you can't route two independent
127 // tracks at once so it shouldn't come up.
128
129 if( const auto line = dyn_cast<const LINE*>( this ) )
130 {
131 if( line->EndsWithVia() )
132 collisionsFound |= line->Via().collideSimple( aHead, aNode, aLayer, aCtx );
133 }
134
135 if( const auto line = dyn_cast<const LINE*>( aHead ) )
136 {
137 if( line->EndsWithVia() )
138 collisionsFound |= line->Via().collideSimple( this, aNode, aLayer, aCtx );
139 }
140
141 // And a special case for the "head" via's hole.
142 if( aHead->HasHole() && shouldWeConsiderHoleCollisions( this, holeH ) )
143 {
144 // Skip net check when doing hole-to-hole collisions.
145 if( Kind() == HOLE_T || Net() != holeH->Net() )
146 collisionsFound |= collideSimple( holeH, aNode, aLayer, aCtx );
147 }
148
149 if( HasHole() && shouldWeConsiderHoleCollisions( holeI, aHead ) )
150 {
151 collisionsFound |= holeI->collideSimple( aHead, aNode, aLayer, aCtx );
152 }
153
154 // Sadly collision routines ignore SHAPE_POLY_LINE widths so we have to pass them in as part
155 // of the clearance value.
156 if( m_kind == LINE_T )
157 lineWidthI = static_cast<const LINE*>( this )->Width() / 2;
158
159 if( aHead->m_kind == LINE_T )
160 lineWidthH = static_cast<const LINE*>( aHead )->Width() / 2;
161
162 // check if we are not on completely different layers first
163 if( !m_layers.Overlaps( aHead->m_layers ) )
164 return false;
165
166 // fixme: this f***ing singleton must go...
167 ROUTER* router = ROUTER::GetInstance();
168 ROUTER_IFACE* iface = router ? router->GetInterface() : nullptr;
169 bool differentNetsOnly = true;
170 bool enforce = false;
171 int clearance;
172
173 if( aCtx )
174 differentNetsOnly = aCtx->options.m_differentNetsOnly;
175
176 // Hole-to-hole collisions don't have anything to do with nets
177 if( Kind() == HOLE_T && aHead->Kind() == HOLE_T )
178 differentNetsOnly = false;
179
180 if( differentNetsOnly && Net() == aHead->Net() && aHead->Net() )
181 {
182 // same nets? no clearance!
183 clearance = -1;
184 }
185 else if( differentNetsOnly && ( IsFreePad() || aHead->IsFreePad() ) )
186 {
187 // a pad associated with a "free" pin (NIC) doesn't have a net until it has been used
188 clearance = -1;
189 }
190 else if( aNode->GetRuleResolver()->IsKeepout( this, aHead, &enforce )
191 || aNode->GetRuleResolver()->IsKeepout( aHead, this, &enforce ) )
192 {
193 if( enforce )
194 clearance = 0; // keepouts are exact boundary; no clearance
195 else
196 clearance = -1;
197 }
198 else if( iface && !iface->IsFlashedOnLayer( this, aHead->Layers() ) )
199 {
200 clearance = -1;
201 }
202 else if( iface && !iface->IsFlashedOnLayer( aHead, Layers() ) )
203 {
204 clearance = -1;
205 }
206 else if( aCtx && aCtx->options.m_overrideClearance >= 0 )
207 {
209 }
210 else
211 {
212 clearance = aNode->GetClearance( this, aHead, aCtx ? aCtx->options.m_useClearanceEpsilon
213 : false );
214 }
215
216 if( clearance >= 0 )
217 {
218 // Note: we can't do castellation or net-tie processing in GetClearance() because they
219 // depend on *where* the collision is.
220
221 bool checkCastellation = ( m_parent && m_parent->GetLayer() == Edge_Cuts )
222 || aNode->GetRuleResolver()->IsNonPlatedSlot( this );
223
224 bool checkNetTie = aNode->GetRuleResolver()->IsInNetTie( this );
225
226 const SHAPE* shapeI = Shape( aLayer );
227 const SHAPE* shapeH = aHead->Shape( aLayer );
228
229 if( checkCastellation || checkNetTie )
230 {
231 // Slow method
232 int actual;
233 VECTOR2I pos;
234
235 // The extra "1" here is to account for the fact that the hulls are built to exactly
236 // the clearance distance, so we need to allow for no collision when exactly at the
237 // clearance distance.
238 if( shapeH->Collide( shapeI, clearance + lineWidthH + lineWidthI - 1, &actual, &pos ) )
239 {
240 if( checkCastellation && aNode->QueryEdgeExclusions( pos ) )
241 return false;
242
243 if( checkNetTie && aNode->GetRuleResolver()->IsNetTieExclusion( aHead, pos, this ) )
244 return false;
245
246 if( aCtx )
247 {
248 collisionsFound = true;
249 OBSTACLE obs;
250 obs.m_head = const_cast<ITEM*>( aHead );
251 obs.m_item = const_cast<ITEM*>( this );
253 obs.m_distFirst = 0;
254 obs.m_maxFanoutWidth = 0;
255 aCtx->obstacles.insert( obs );
256 }
257 else
258 {
259 return true;
260 }
261 }
262 }
263 else
264 {
265 // Fast method
266 // The extra "1" here is to account for the fact that the hulls are built to exactly
267 // the clearance distance, so we need to allow for no collision when exactly at the
268 // clearance distance.
269 if( shapeH->Collide( shapeI, clearance + lineWidthH + lineWidthI - 1 ) )
270 {
271 if( aCtx )
272 {
273 collisionsFound = true;
274 OBSTACLE obs;
275 obs.m_head = const_cast<ITEM*>( aHead );
276 obs.m_item = const_cast<ITEM*>( this );
278 obs.m_distFirst = 0;
279 obs.m_maxFanoutWidth = 0;
280 aCtx->obstacles.insert( obs );
281 }
282 else
283 {
284 return true;
285 }
286 }
287 }
288 }
289
290 return collisionsFound;
291}
292
293
294bool ITEM::Collide( const ITEM* aOther, const NODE* aNode, int aLayer,
295 COLLISION_SEARCH_CONTEXT *aCtx ) const
296{
297 if( collideSimple( aOther, aNode, aLayer, aCtx ) )
298 return true;
299
300 return false;
301}
302
303
304std::string ITEM::KindStr() const
305{
306 switch( m_kind )
307 {
308 case ARC_T: return "arc";
309 case LINE_T: return "line";
310 case SEGMENT_T: return "segment";
311 case VIA_T: return "via";
312 case JOINT_T: return "joint";
313 case SOLID_T: return "solid";
314 case DIFF_PAIR_T: return "diff-pair";
315 case HOLE_T: return "hole";
316
317 default: return "unknown";
318 }
319}
320
321
323{
324}
325
326
327const std::string ITEM::Format() const
328{
329 ROUTER* router = ROUTER::GetInstance();
330 ROUTER_IFACE* iface = router ? router->GetInterface() : nullptr;
331
332 std::stringstream ss;
333 ss << KindStr() << " ";
334
335 if( iface )
336 ss << "net " << iface->GetNetName( Net() ) << " ";
337
338 ss << "layers " << m_layers.Start() << " " << m_layers.End();
339 return ss.str();
340}
341
342
343const NODE* ITEM::OwningNode() const
344{
345 if( ParentPadVia() )
346 return static_cast<const NODE*>( ParentPadVia()->Owner() );
347 else
348 return static_cast<const NODE*>( Owner() );
349}
350
352{
353 static UNIQ_ID uidCount = 0; // fixme: make atomic
354 return uidCount++;
355}
356
357} // namespace PNS
virtual NET_HANDLE Net() const override
Definition pns_hole.h:56
ITEM * ParentPadVia() const override
Definition pns_hole.h:72
Base class for PNS router board items.
Definition pns_item.h:98
bool IsFreePad() const
Definition pns_item.h:288
PnsKind m_kind
Definition pns_item.h:316
virtual bool HasUniqueShapeLayers() const
Definition pns_item.h:252
virtual const std::string Format() const
Definition pns_item.cpp:327
virtual ITEM * ParentPadVia() const
Definition pns_item.h:293
virtual std::vector< int > UniqueShapeLayers() const
Return a list of layers that have unique (potentially different) shapes.
Definition pns_item.h:250
virtual const SHAPE * Shape(int aLayer) const
Return the geometrical shape of the item.
Definition pns_item.h:242
const PNS_LAYER_RANGE & Layers() const
Definition pns_item.h:212
ITEM(PnsKind aKind)
Definition pns_item.h:116
virtual NET_HANDLE Net() const
Definition pns_item.h:210
PNS_LAYER_RANGE m_layers
Definition pns_item.h:323
PnsKind Kind() const
Return the type (kind) of the item.
Definition pns_item.h:173
std::set< int > RelevantShapeLayers(const ITEM *aOther) const
Returns the set of layers on which either this or the other item can have a unique shape.
Definition pns_item.cpp:83
@ DIFF_PAIR_T
Definition pns_item.h:110
bool Collide(const ITEM *aHead, const NODE *aNode, int aLayer, COLLISION_SEARCH_CONTEXT *aCtx=nullptr) const
Check for a collision (clearance violation) with between us and item aOther.
Definition pns_item.cpp:294
bool collideSimple(const ITEM *aHead, const NODE *aNode, int aLayer, COLLISION_SEARCH_CONTEXT *aCtx) const
Definition pns_item.cpp:104
virtual ~ITEM()
Definition pns_item.cpp:322
virtual const NODE * OwningNode() const
Definition pns_item.cpp:343
bool OfKind(int aKindMask) const
Definition pns_item.h:181
std::string KindStr() const
Definition pns_item.cpp:304
virtual HOLE * Hole() const
Definition pns_item.h:304
virtual bool HasHole() const
Definition pns_item.h:303
BOARD_ITEM * m_parent
Definition pns_item.h:317
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition pns_line.h:62
static UNIQ_ID genNextUid()
Definition pns_item.cpp:351
Keep the router "world" - i.e.
Definition pns_node.h:240
int GetClearance(const ITEM *aA, const ITEM *aB, bool aUseClearanceEpsilon=true) const
Return the pre-set worst case clearance between any pair of items.
Definition pns_node.cpp:143
bool QueryEdgeExclusions(const VECTOR2I &aPos) const
Definition pns_node.cpp:799
RULE_RESOLVER * GetRuleResolver() const
Return the number of joints.
Definition pns_node.h:278
const ITEM_OWNER * Owner() const
Return the owner of this item, or NULL if there's none.
Definition pns_item.h:72
virtual wxString GetNetName(PNS::NET_HANDLE aNet) const =0
virtual bool IsFlashedOnLayer(const PNS::ITEM *aItem, int aLayer) const =0
ROUTER_IFACE * GetInterface() const
Definition pns_router.h:236
static ROUTER * GetInstance()
virtual bool IsNonPlatedSlot(const PNS::ITEM *aItem)=0
virtual bool IsNetTieExclusion(const ITEM *aItem, const VECTOR2I &aCollisionPos, const ITEM *aCollidingItem)=0
virtual bool IsKeepout(const ITEM *aObstacle, const ITEM *aItem, bool *aEnforce)=0
virtual bool IsInNetTie(const ITEM *aA)=0
const VECTOR2I & Pos() const
Definition pns_via.h:206
int Drill() const
Definition pns_via.h:247
bool PadstackMatches(const VIA &aOther) const
Definition pns_via.cpp:108
An abstract shape on 2D plane.
Definition shape.h:126
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const
Check if the boundary of shape (this) lies closer to the point aP than aClearance,...
Definition shape.h:181
VECTOR2_TRAITS< int32_t >::extended_type extended_type
Definition vector2d.h:73
@ Edge_Cuts
Definition layer_ids.h:112
Push and Shove diff pair dimensions (gap) settings dialog.
static bool shouldWeConsiderHoleCollisions(const ITEM *aItem, const ITEM *aHead)
Definition pns_item.cpp:38
VECTOR2I::extended_type ecoord
const COLLISION_SEARCH_OPTIONS options
Definition pns_node.h:134
std::set< OBSTACLE > & obstacles
Definition pns_node.h:133
Hold an object colliding with another object, along with some useful data about the collision.
Definition pns_node.h:88
int m_distFirst
... and the distance thereof
Definition pns_node.h:94
int m_clearance
Definition pns_node.h:92
int m_maxFanoutWidth
worst case (largest) width of the tracks connected to the item
Definition pns_node.h:95
ITEM * m_head
Line we search collisions against.
Definition pns_node.h:89
ITEM * m_item
Item found to be colliding with m_head.
Definition pns_node.h:90
int clearance
int actual
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:61
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695