KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_dp_meander_placer.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 <optional>
23
24#include "pns_node.h"
25#include "pns_itemset.h"
26#include "pns_topology.h"
28#include "pns_diff_pair.h"
29#include "pns_router.h"
30#include "pns_solid.h"
31
32namespace PNS {
33
35 MEANDER_PLACER_BASE( aRouter )
36{
37 m_world = nullptr;
38 m_currentNode = nullptr;
39
42
45
46 // Init temporary variables (do not leave uninitialized members)
47 m_initialSegment = nullptr;
48 m_lastLength = 0;
49 m_lastDelay = 0;
51
52 m_netClass = nullptr;
53}
54
55
59
60
62{
63 return m_currentTraceP;
64}
65
66
71
72
73NODE* DP_MEANDER_PLACER::CurrentNode( bool aLoopsRemoved ) const
74{
75 if( !m_currentNode )
76 return m_world;
77
78 return m_currentNode;
79}
80
81
82bool DP_MEANDER_PLACER::Start( const VECTOR2I& aP, ITEM* aStartItem )
83{
84 if( !aStartItem || !aStartItem->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
85 {
86 Router()->SetFailureReason( _( "Please select a track whose length you want to tune." ) );
87 return false;
88 }
89
90 m_initialSegment = static_cast<LINKED_ITEM*>( aStartItem );
91 m_currentNode = nullptr;
93
94 m_world = Router()->GetWorld()->Branch();
95
96 TOPOLOGY topo( m_world );
97
99 {
100 Router()->SetFailureReason( _( "Unable to find complementary differential pair "
101 "net for length tuning. Make sure the names of the nets "
102 "belonging to a differential pair end with either _N/_P "
103 "or +/-." ) );
104 return false;
105 }
106
107 if( m_originPair.Gap() < 0 )
108 m_originPair.SetGap( Router()->Sizes().DiffPairGap() );
109
110 if( !m_originPair.PLine().SegmentCount() || !m_originPair.NLine().SegmentCount() )
111 return false;
112
113 m_tunedPathP = topo.AssembleTuningPath( Router()->GetInterface(), m_originPair.PLine().GetLink( 0 ), &m_startPad_p,
114 &m_endPad_p );
115
118
119 if( m_startPad_p )
120 {
121 m_padToDieLengthP += m_startPad_p->GetPadToDie();
122 m_padToDieDelayP += m_startPad_p->GetPadToDieDelay();
123 }
124
125 if( m_endPad_p )
126 {
127 m_padToDieLengthP += m_endPad_p->GetPadToDie();
128 m_padToDieDelayP += m_endPad_p->GetPadToDieDelay();
129 }
130
131 m_tunedPathN = topo.AssembleTuningPath( Router()->GetInterface(), m_originPair.NLine().GetLink( 0 ), &m_startPad_n,
132 &m_endPad_n );
133
136
137 if( m_startPad_n )
138 {
139 m_padToDieLengthN += m_startPad_n->GetPadToDie();
140 m_padToDieDelayN += m_startPad_n->GetPadToDieDelay();
141 }
142
143 if( m_endPad_n )
144 {
145 m_padToDieLengthN += m_endPad_n->GetPadToDie();
146 m_padToDieDelayN += m_endPad_n->GetPadToDieDelay();
147 }
148
149 m_world->Remove( m_originPair.PLine() );
150 m_world->Remove( m_originPair.NLine() );
151
153
154 const BOARD_CONNECTED_ITEM* conItem = static_cast<BOARD_CONNECTED_ITEM*>( aStartItem->GetSourceItem() );
155 m_netClass = conItem->GetEffectiveNetClass();
156
158
159 return true;
160}
161
162
166
167
169{
172 return std::max( totalP, totalN );
173}
174
175
177{
178 const int64_t totalP = m_padToDieDelayP + lineDelay( m_tunedPathP, m_startPad_p, m_endPad_p );
179 const int64_t totalN = m_padToDieDelayP + lineDelay( m_tunedPathN, m_startPad_n, m_endPad_n );
180 return std::max( totalP, totalN );
181}
182
183
185{
186 const VECTOR2I a( ( aCoupledSegs.coupledP.A + aCoupledSegs.coupledN.A ) / 2 );
187 const VECTOR2I b( ( aCoupledSegs.coupledP.B + aCoupledSegs.coupledN.B ) / 2 );
188
189 return SEG( a, b );
190}
191
192
194{
195 VECTOR2I midp = ( aPair.coupledP.A + aPair.coupledN.A ) / 2;
196
197 //DrawDebugPoint(midp, 6);
198
199 return aPair.coupledP.Side( midp ) > 0;
200}
201
202
203bool DP_MEANDER_PLACER::Move( const VECTOR2I& aP, ITEM* aEndItem )
204{
206
207 if( m_currentStart == aP )
208 return false;
209
210 DIFF_PAIR::COUPLED_SEGMENTS_VEC coupledSegments;
211
212 if( m_currentNode )
213 delete m_currentNode;
214
215 m_currentNode = m_world->Branch();
216
217 SHAPE_LINE_CHAIN preP, tunedP, postP;
218 SHAPE_LINE_CHAIN preN, tunedN, postN;
219
220 m_originPair.CP().Split( m_currentStart, aP, preP, tunedP, postP );
221 m_originPair.CN().Split( m_currentStart, aP, preN, tunedN, postN );
222
223 // Bail out early if the tuned sections are empty (issue #22041). This can happen when the
224 // split points are too close together or outside the line chain.
225 if( tunedP.PointCount() == 0 || tunedN.PointCount() == 0 )
226 {
231
232 return false;
233 }
234
235 auto updateStatus =
236 [&]()
237 {
238 if( m_lastLength > m_settings.m_targetLength.Max() )
240 else if( m_lastLength < m_settings.m_targetLength.Min() )
242 else
244 };
245
246 DIFF_PAIR tuned( m_originPair );
247
248 tuned.SetShape( tunedP, tunedN );
249
250 tuned.CoupledSegmentPairs( coupledSegments );
251
252 if( coupledSegments.size() == 0 )
253 {
254 // Tuning started at an uncoupled area of the DP; we won't get a valid result until the
255 // cursor is moved far enough along a coupled area. Prevent the track from disappearing and
256 // the length from being zero by just using the original.
260 updateStatus();
261
262 return false;
263 }
264
265 m_result = MEANDERED_LINE( this, true );
266 m_result.SetWidth( tuned.Width() );
267
268 int offset = ( tuned.Gap() + tuned.Width() ) / 2;
269
270 if( pairOrientation( coupledSegments[0] ) )
271 offset *= -1;
272
273 m_result.SetBaselineOffset( offset );
274
275 for( const ITEM* item : m_tunedPathP.CItems() )
276 {
277 if( const LINE* l = dyn_cast<const LINE*>( item ) )
278 {
279 PNS_DBG( Dbg(), AddShape, &l->CLine(), YELLOW, 10000, wxT( "tuned-path-p" ) );
280
281 m_router->GetInterface()->DisplayPathLine( l->CLine(), 1 );
282 }
283 }
284
285 for( const ITEM* item : m_tunedPathN.CItems() )
286 {
287 if( const LINE* l = dyn_cast<const LINE*>( item ) )
288 {
289 PNS_DBG( Dbg(), AddShape, &l->CLine(), YELLOW, 10000, wxT( "tuned-path-n" ) );
290
291 m_router->GetInterface()->DisplayPathLine( l->CLine(), 1 );
292 }
293 }
294
295 int curIndexP = 0, curIndexN = 0;
296
297 for( const DIFF_PAIR::COUPLED_SEGMENTS& sp : coupledSegments )
298 {
299 SEG base = baselineSegment( sp );
300 bool side = false;
301
302 if( m_settings.m_initialSide == 0 )
303 side = base.Side( aP ) < 0;
304 else
305 side = m_settings.m_initialSide < 0;
306
307 PNS_DBG( Dbg(), AddShape, base, GREEN, 10000, wxT( "dp-baseline" ) );
308
309 while( sp.indexP >= curIndexP && curIndexP != -1 )
310 {
311 if( tunedP.IsArcSegment( curIndexP ) )
312 {
313 ssize_t arcIndex = tunedP.ArcIndex( curIndexP );
314
315 m_result.AddArcAndPt( tunedP.Arc( arcIndex ), tunedN.CPoint( curIndexN ) );
316 }
317 else
318 {
319 m_result.AddCorner( tunedP.CPoint( curIndexP ), tunedN.CPoint( curIndexN ) );
320 }
321
322 curIndexP = tunedP.NextShape( curIndexP );
323 }
324
325 while( sp.indexN >= curIndexN && curIndexN != -1 )
326 {
327 if( tunedN.IsArcSegment( curIndexN ) )
328 {
329 ssize_t arcIndex = tunedN.ArcIndex( curIndexN );
330
331 m_result.AddPtAndArc( tunedP.CPoint( sp.indexP ), tunedN.Arc( arcIndex ) );
332 }
333 else
334 {
335 m_result.AddCorner( tunedP.CPoint( sp.indexP ), tunedN.CPoint( curIndexN ) );
336 }
337
338 curIndexN = tunedN.NextShape( curIndexN );
339 }
340
341 m_result.MeanderSegment( base, side );
342 }
343
344 while( curIndexP < tunedP.PointCount() && curIndexP != -1 )
345 {
346 if( tunedP.IsArcSegment( curIndexP ) )
347 {
348 ssize_t arcIndex = tunedP.ArcIndex( curIndexP );
349
350 m_result.AddArcAndPt( tunedP.Arc( arcIndex ), tunedN.CPoint( curIndexN ) );
351 }
352 else
353 {
354 m_result.AddCorner( tunedP.CPoint( curIndexP ), tunedN.CPoint( curIndexN ) );
355 }
356
357 curIndexP = tunedP.NextShape( curIndexP );
358 }
359
360 while( curIndexN < tunedN.PointCount() && curIndexN != -1 )
361 {
362 if( tunedN.IsArcSegment( curIndexN ) )
363 {
364 ssize_t arcIndex = tunedN.ArcIndex( curIndexN );
365
366 m_result.AddPtAndArc( tunedP.CLastPoint(), tunedN.Arc( arcIndex ) );
367 }
368 else
369 {
370 m_result.AddCorner( tunedP.CLastPoint(), tunedN.CPoint( curIndexN ) );
371 }
372
373 curIndexN = tunedN.NextShape( curIndexN );
374 }
375
376 m_result.AddCorner( tunedP.CLastPoint(), tunedN.CLastPoint() );
377
378 long long int dpLen = origPathLength();
379 int64_t dpDelay = origPathDelay();
380
382
383 if( dpLen > m_settings.m_targetLength.Max() )
384 {
386 m_lastLength = dpLen;
387 m_lastDelay = dpDelay;
388 }
389 else
390 {
391 m_lastLength = dpLen - std::max( tunedP.Length(), tunedN.Length() );
392
393 if( m_settings.m_isTimeDomain )
394 {
395 int64_t tunedPDelay = m_router->GetInterface()->CalculateDelayForShapeLineChain(
396 tunedP, GetOriginPair().Width(), true, GetOriginPair().Gap(), m_router->GetCurrentLayer(),
397 m_netClass );
398 int64_t tunedNDelay = m_router->GetInterface()->CalculateDelayForShapeLineChain(
399 tunedN, GetOriginPair().Width(), true, GetOriginPair().Gap(), m_router->GetCurrentLayer(),
400 m_netClass );
401
402 m_lastDelay = dpDelay - std::max( tunedPDelay, tunedNDelay );
403 }
404
405 tuneLineLength( m_result, m_settings.m_targetLength.Opt() - dpLen );
406 }
407
408 if( m_lastStatus != TOO_LONG )
409 {
410 tunedP.Clear();
411 tunedN.Clear();
412
413 for( MEANDER_SHAPE* m : m_result.Meanders() )
414 {
415 if( m->Type() != MT_EMPTY )
416 {
417 tunedP.Append( m->CLine( 0 ) );
418 tunedN.Append( m->CLine( 1 ) );
419 }
420 }
421
422 m_lastLength += std::max( tunedP.Length(), tunedN.Length() );
423
424 if( m_settings.m_isTimeDomain )
425 {
426 int64_t tunedPDelay = m_router->GetInterface()->CalculateDelayForShapeLineChain(
427 tunedP, GetOriginPair().Width(), true, GetOriginPair().Gap(), m_router->GetCurrentLayer(),
428 m_netClass );
429 int64_t tunedNDelay = m_router->GetInterface()->CalculateDelayForShapeLineChain(
430 tunedN, GetOriginPair().Width(), true, GetOriginPair().Gap(), m_router->GetCurrentLayer(),
431 m_netClass );
432
433 m_lastDelay += std::max( tunedPDelay, tunedNDelay );
434 }
435
436 updateStatus();
437 }
438
439 m_finalShapeP.Clear();
440 m_finalShapeN.Clear();
441
442 if( m_settings.m_keepEndpoints )
443 {
444 preP.Simplify();
445 tunedP.Simplify();
446 postP.Simplify();
447
448 m_finalShapeP.Append( preP );
449 m_finalShapeP.Append( tunedP );
450 m_finalShapeP.Append( postP );
451
452 preN.Simplify();
453 tunedN.Simplify();
454 postN.Simplify();
455
456 m_finalShapeN.Append( preN );
457 m_finalShapeN.Append( tunedN );
458 m_finalShapeN.Append( postN );
459 }
460 else
461 {
462 m_finalShapeP.Append( preP );
463 m_finalShapeP.Append( tunedP );
464 m_finalShapeP.Append( postP );
465 m_finalShapeP.Simplify();
466
467 m_finalShapeN.Append( preN );
468 m_finalShapeN.Append( tunedN );
469 m_finalShapeN.Append( postN );
470 m_finalShapeN.Simplify();
471 }
472
473 return true;
474}
475
476
477bool DP_MEANDER_PLACER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinish )
478{
479 LINE lP( m_originPair.PLine(), m_finalShapeP );
480 LINE lN( m_originPair.NLine(), m_finalShapeN );
481
482 m_currentNode->Add( lP );
483 m_currentNode->Add( lN );
484
486
487 return true;
488}
489
490
492{
493 m_world->KillChildren();
494 return true;
495}
496
497
499{
500 return m_originPair.CP().SegmentCount() > 0 || m_originPair.CN().SegmentCount() > 0;
501}
502
503
505{
506 if( m_currentNode )
508
509 m_currentNode = nullptr;
510 return true;
511}
512
513
515{
516 LINE l1( m_originPair.PLine(), aShape->CLine( 0 ) );
517 LINE l2( m_originPair.NLine(), aShape->CLine( 1 ) );
518
519 if( m_currentNode->CheckColliding( &l1 ) )
520 return false;
521
522 if( m_currentNode->CheckColliding( &l2 ) )
523 return false;
524
525 int w = aShape->Width();
526 int clearance = w + w * 3;
527
528 return m_result.CheckSelfIntersections( aShape, clearance );
529}
530
531
533{
536
537 ITEM_SET traces;
538
539 traces.Add( &m_currentTraceP );
540 traces.Add( &m_currentTraceN );
541
542 return traces;
543}
544
545
547{
548 ITEM_SET lines;
549
550 for( ITEM* item : m_tunedPathN )
551 lines.Add( item );
552
553 for( ITEM* item : m_tunedPathP )
554 lines.Add( item );
555
556 return lines;
557}
558
559
561{
562 return m_currentStart;
563}
564
565
567{
568 return m_currentEnd;
569}
570
571
573{
574 return m_initialSegment->Layers().Start();
575}
576
577
579{
580 if( m_lastLength )
581 return m_lastLength;
582 else
583 return origPathLength();
584}
585
586
588{
589 if( m_lastDelay )
590 return m_lastDelay;
591 else
592 return origPathDelay();
593}
594
595
600
601
602const std::vector<NET_HANDLE> DP_MEANDER_PLACER::CurrentNets() const
603{
604 std::vector<NET_HANDLE> rv;
605 rv.push_back( m_originPair.NetP() );
606 rv.push_back( m_originPair.NetN() );
607 return rv;
608}
609
610
612{
613 // If this is a time domain tuning, calculate the target length for the desired total delay
614 if( m_settings.m_isTimeDomain )
615 {
616 const int64_t curDelay = origPathDelay();
617
618 const int64_t desiredDelayMin = m_settings.m_targetLengthDelay.Min();
619 const int64_t desiredDelayOpt = m_settings.m_targetLengthDelay.Opt();
620 const int64_t desiredDelayMax = m_settings.m_targetLengthDelay.Max();
621
622 const int64_t delayDifferenceOpt = desiredDelayOpt - curDelay;
623
624 const int64_t curLength = origPathLength();
625 const int64_t lengthDiffMin = m_router->GetInterface()->CalculateLengthForDelay(
626 desiredDelayOpt - desiredDelayMin, GetOriginPair().Width(), true, GetOriginPair().Gap(),
627 m_router->GetCurrentLayer(), m_netClass );
628 int64_t lengthDiffOpt = m_router->GetInterface()->CalculateLengthForDelay(
629 std::abs( delayDifferenceOpt ), GetOriginPair().Width(), true, GetOriginPair().Gap(),
630 m_router->GetCurrentLayer(), m_netClass );
631 const int64_t lengthDiffMax = m_router->GetInterface()->CalculateLengthForDelay(
632 desiredDelayMax - desiredDelayOpt, GetOriginPair().Width(), true, GetOriginPair().Gap(),
633 m_router->GetCurrentLayer(), m_netClass );
634
635 lengthDiffOpt = delayDifferenceOpt > 0 ? lengthDiffOpt : -lengthDiffOpt;
636
637 m_settings.m_targetLength.SetMin( curLength + lengthDiffOpt - lengthDiffMin );
638 m_settings.m_targetLength.SetOpt( curLength + lengthDiffOpt );
639 m_settings.m_targetLength.SetMax( curLength + lengthDiffOpt + lengthDiffMax );
640 }
641}
642}
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
virtual NETCLASS * GetEffectiveNetClass() const
Return the NETCLASS for this item.
ROUTER * Router() const
Return current router settings.
ROUTER * m_router
DEBUG_DECORATOR * Dbg() const
Basic class for a differential pair.
int Width() const
std::vector< COUPLED_SEGMENTS > COUPLED_SEGMENTS_VEC
void SetShape(const SHAPE_LINE_CHAIN &aP, const SHAPE_LINE_CHAIN &aN, bool aSwapLanes=false)
int Gap() const
void CoupledSegmentPairs(COUPLED_SEGMENTS_VEC &aPairs) const
bool Start(const VECTOR2I &aP, ITEM *aStartItem) override
Start routing a single track at point aP, taking item aStartItem as anchor (unless NULL).
bool CheckFit(MEANDER_SHAPE *aShape) override
Checks if it's OK to place the shape aShape (i.e.
const ITEM_SET Traces() override
Function Traces()
bool pairOrientation(const DIFF_PAIR::COUPLED_SEGMENTS &aPair)
void calculateTimeDomainTargets()
Current routing start point (end of tail, beginning of head).
VECTOR2I m_currentStart
Current world state.
bool FixRoute(const VECTOR2I &aP, ITEM *aEndItem, bool aForceFinish=false) override
Commit the currently routed track to the parent node, taking aP as the final end point and aEndItem a...
int CurrentLayer() const override
Function CurrentLayer()
TUNING_STATUS TuningStatus() const override
Return the tuning status (too short, too long, etc.) of the trace(s) being tuned.
long long int TuningLengthResult() const override
Return the resultant length or skew of the tuned traces.
const SEG baselineSegment(const DIFF_PAIR::COUPLED_SEGMENTS &aCoupledSegs)
const DIFF_PAIR & GetOriginPair()
const ITEM_SET TunedPath() override
bool HasPlacedAnything() const override
bool Move(const VECTOR2I &aP, ITEM *aEndItem) override
Move the end of the currently routed trace to the point aP, taking aEndItem as anchor (if not NULL).
const VECTOR2I & CurrentEnd() const override
Function CurrentEnd()
const VECTOR2I & CurrentStart() const override
Function CurrentStart()
int64_t TuningDelayResult() const override
Return the resultant delay or skew of the tuned traces.
long long int origPathLength() const
const std::vector< NET_HANDLE > CurrentNets() const override
Function CurrentNets()
NODE * CurrentNode(bool aLoopsRemoved=false) const override
Return the most recent world state.
void Add(const LINE &aLine)
Base class for PNS router board items.
Definition pns_item.h:98
BOARD_ITEM * GetSourceItem() const
Definition pns_item.h:202
bool OfKind(int aKindMask) const
Definition pns_item.h:181
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition pns_line.h:62
Represent a set of meanders fitted over a single or two lines.
void tuneLineLength(MEANDERED_LINE &aTuned, long long int aElongation)
Take a set of meanders in aTuned and tunes their length to extend the original line length by aElonga...
TUNING_STATUS
< Result of the length tuning operation
int m_currentWidth
Meander settings.
MEANDER_SETTINGS m_settings
The current end point.
int64_t lineDelay(const ITEM_SET &aLine, const SOLID *aStartPad, const SOLID *aEndPad) const
Calculate the total delay of the line represented by an item set (tracks and vias)
NODE * m_world
Width of the meandered trace(s).
VECTOR2I getSnappedStartPoint(LINKED_ITEM *aStartItem, VECTOR2I aStartPoint)
long long int lineLength(const ITEM_SET &aLine, const SOLID *aStartPad, const SOLID *aEndPad) const
Calculate the total length of the line represented by an item set (tracks and vias)
int Width() const
const SHAPE_LINE_CHAIN & CLine(int aShape) const
Keep the router "world" - i.e.
Definition pns_node.h:232
NODE * Branch()
Create a lightweight copy (called branch) of self that tracks the changes (added/removed items) wrs t...
Definition pns_node.cpp:143
void SetFailureReason(const wxString &aReason)
Definition pns_router.h:227
void CommitRouting()
NODE * GetWorld() const
Definition pns_router.h:178
const DIFF_PAIR AssembleDiffPair(SEGMENT *aStart)
const ITEM_SET AssembleTuningPath(ROUTER_IFACE *aRouterIface, ITEM *aStart, SOLID **aStartPad=nullptr, SOLID **aEndPad=nullptr)
Like AssembleTrivialPath, but follows the track length algorithm, which discards segments that are fu...
Definition seg.h:42
VECTOR2I A
Definition seg.h:49
VECTOR2I B
Definition seg.h:50
int Side(const VECTOR2I &aP) const
Determine on which side of directed line passing via segment ends point aP lies.
Definition seg.h:143
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_ARC & Arc(size_t aArc) const
int PointCount() const
Return the number of points (vertices) in this line chain.
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
void Clear()
Remove all points from the line chain.
void Simplify(int aTolerance=0)
Simplify the line chain by removing colinear adjacent segments and duplicate vertices.
int NextShape(int aPointIndex) const
Return the vertex index of the next shape in the chain, or -1 if aPointIndex is the last shape.
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
bool IsArcSegment(size_t aSegment) const
long long int Length() const
Return length of the line chain in Euclidean metric.
@ GREEN
Definition color4d.h:57
@ YELLOW
Definition color4d.h:67
#define _(s)
Push and Shove diff pair dimensions (gap) settings dialog.
@ MT_EMPTY
Definition pns_meander.h:47
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
#define PNS_DBG(dbg, method,...)
int clearance
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:61
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695