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 auto updateStatus =
224 [&]()
225 {
226 if( m_lastLength > m_settings.m_targetLength.Max() )
228 else if( m_lastLength < m_settings.m_targetLength.Min() )
230 else
232 };
233
234 DIFF_PAIR tuned( m_originPair );
235
236 tuned.SetShape( tunedP, tunedN );
237
238 tuned.CoupledSegmentPairs( coupledSegments );
239
240 if( coupledSegments.size() == 0 )
241 {
242 // Tuning started at an uncoupled area of the DP; we won't get a valid result until the
243 // cursor is moved far enough along a coupled area. Prevent the track from disappearing and
244 // the length from being zero by just using the original.
248 updateStatus();
249
250 return false;
251 }
252
253 m_result = MEANDERED_LINE( this, true );
254 m_result.SetWidth( tuned.Width() );
255
256 int offset = ( tuned.Gap() + tuned.Width() ) / 2;
257
258 if( pairOrientation( coupledSegments[0] ) )
259 offset *= -1;
260
261 m_result.SetBaselineOffset( offset );
262
263 for( const ITEM* item : m_tunedPathP.CItems() )
264 {
265 if( const LINE* l = dyn_cast<const LINE*>( item ) )
266 {
267 PNS_DBG( Dbg(), AddShape, &l->CLine(), YELLOW, 10000, wxT( "tuned-path-p" ) );
268
269 m_router->GetInterface()->DisplayPathLine( l->CLine(), 1 );
270 }
271 }
272
273 for( const ITEM* item : m_tunedPathN.CItems() )
274 {
275 if( const LINE* l = dyn_cast<const LINE*>( item ) )
276 {
277 PNS_DBG( Dbg(), AddShape, &l->CLine(), YELLOW, 10000, wxT( "tuned-path-n" ) );
278
279 m_router->GetInterface()->DisplayPathLine( l->CLine(), 1 );
280 }
281 }
282
283 int curIndexP = 0, curIndexN = 0;
284
285 for( const DIFF_PAIR::COUPLED_SEGMENTS& sp : coupledSegments )
286 {
287 SEG base = baselineSegment( sp );
288 bool side = false;
289
290 if( m_settings.m_initialSide == 0 )
291 side = base.Side( aP ) < 0;
292 else
293 side = m_settings.m_initialSide < 0;
294
295 PNS_DBG( Dbg(), AddShape, base, GREEN, 10000, wxT( "dp-baseline" ) );
296
297 while( sp.indexP >= curIndexP && curIndexP != -1 )
298 {
299 if( tunedP.IsArcSegment( curIndexP ) )
300 {
301 ssize_t arcIndex = tunedP.ArcIndex( curIndexP );
302
303 m_result.AddArcAndPt( tunedP.Arc( arcIndex ), tunedN.CPoint( curIndexN ) );
304 }
305 else
306 {
307 m_result.AddCorner( tunedP.CPoint( curIndexP ), tunedN.CPoint( curIndexN ) );
308 }
309
310 curIndexP = tunedP.NextShape( curIndexP );
311 }
312
313 while( sp.indexN >= curIndexN && curIndexN != -1 )
314 {
315 if( tunedN.IsArcSegment( curIndexN ) )
316 {
317 ssize_t arcIndex = tunedN.ArcIndex( curIndexN );
318
319 m_result.AddPtAndArc( tunedP.CPoint( sp.indexP ), tunedN.Arc( arcIndex ) );
320 }
321 else
322 {
323 m_result.AddCorner( tunedP.CPoint( sp.indexP ), tunedN.CPoint( curIndexN ) );
324 }
325
326 curIndexN = tunedN.NextShape( curIndexN );
327 }
328
329 m_result.MeanderSegment( base, side );
330 }
331
332 while( curIndexP < tunedP.PointCount() && curIndexP != -1 )
333 {
334 if( tunedP.IsArcSegment( curIndexP ) )
335 {
336 ssize_t arcIndex = tunedP.ArcIndex( curIndexP );
337
338 m_result.AddArcAndPt( tunedP.Arc( arcIndex ), tunedN.CPoint( curIndexN ) );
339 }
340 else
341 {
342 m_result.AddCorner( tunedP.CPoint( curIndexP ), tunedN.CPoint( curIndexN ) );
343 }
344
345 curIndexP = tunedP.NextShape( curIndexP );
346 }
347
348 while( curIndexN < tunedN.PointCount() && curIndexN != -1 )
349 {
350 if( tunedN.IsArcSegment( curIndexN ) )
351 {
352 ssize_t arcIndex = tunedN.ArcIndex( curIndexN );
353
354 m_result.AddPtAndArc( tunedP.CLastPoint(), tunedN.Arc( arcIndex ) );
355 }
356 else
357 {
358 m_result.AddCorner( tunedP.CLastPoint(), tunedN.CPoint( curIndexN ) );
359 }
360
361 curIndexN = tunedN.NextShape( curIndexN );
362 }
363
364 m_result.AddCorner( tunedP.CLastPoint(), tunedN.CLastPoint() );
365
366 long long int dpLen = origPathLength();
367 int64_t dpDelay = origPathDelay();
368
370
371 if( dpLen > m_settings.m_targetLength.Max() )
372 {
374 m_lastLength = dpLen;
375 m_lastDelay = dpDelay;
376 }
377 else
378 {
379 m_lastLength = dpLen - std::max( tunedP.Length(), tunedN.Length() );
380
381 if( m_settings.m_isTimeDomain )
382 {
383 int64_t tunedPDelay = m_router->GetInterface()->CalculateDelayForShapeLineChain(
384 tunedP, GetOriginPair().Width(), true, GetOriginPair().Gap(), m_router->GetCurrentLayer(),
385 m_netClass );
386 int64_t tunedNDelay = m_router->GetInterface()->CalculateDelayForShapeLineChain(
387 tunedN, GetOriginPair().Width(), true, GetOriginPair().Gap(), m_router->GetCurrentLayer(),
388 m_netClass );
389
390 m_lastDelay = dpDelay - std::max( tunedPDelay, tunedNDelay );
391 }
392
393 tuneLineLength( m_result, m_settings.m_targetLength.Opt() - dpLen );
394 }
395
396 if( m_lastStatus != TOO_LONG )
397 {
398 tunedP.Clear();
399 tunedN.Clear();
400
401 for( MEANDER_SHAPE* m : m_result.Meanders() )
402 {
403 if( m->Type() != MT_EMPTY )
404 {
405 tunedP.Append( m->CLine( 0 ) );
406 tunedN.Append( m->CLine( 1 ) );
407 }
408 }
409
410 m_lastLength += std::max( tunedP.Length(), tunedN.Length() );
411
412 if( m_settings.m_isTimeDomain )
413 {
414 int64_t tunedPDelay = m_router->GetInterface()->CalculateDelayForShapeLineChain(
415 tunedP, GetOriginPair().Width(), true, GetOriginPair().Gap(), m_router->GetCurrentLayer(),
416 m_netClass );
417 int64_t tunedNDelay = m_router->GetInterface()->CalculateDelayForShapeLineChain(
418 tunedN, GetOriginPair().Width(), true, GetOriginPair().Gap(), m_router->GetCurrentLayer(),
419 m_netClass );
420
421 m_lastDelay += std::max( tunedPDelay, tunedNDelay );
422 }
423
424 updateStatus();
425 }
426
427 m_finalShapeP.Clear();
428 m_finalShapeN.Clear();
429
430 if( m_settings.m_keepEndpoints )
431 {
432 preP.Simplify();
433 tunedP.Simplify();
434 postP.Simplify();
435
436 m_finalShapeP.Append( preP );
437 m_finalShapeP.Append( tunedP );
438 m_finalShapeP.Append( postP );
439
440 preN.Simplify();
441 tunedN.Simplify();
442 postN.Simplify();
443
444 m_finalShapeN.Append( preN );
445 m_finalShapeN.Append( tunedN );
446 m_finalShapeN.Append( postN );
447 }
448 else
449 {
450 m_finalShapeP.Append( preP );
451 m_finalShapeP.Append( tunedP );
452 m_finalShapeP.Append( postP );
453 m_finalShapeP.Simplify();
454
455 m_finalShapeN.Append( preN );
456 m_finalShapeN.Append( tunedN );
457 m_finalShapeN.Append( postN );
458 m_finalShapeN.Simplify();
459 }
460
461 return true;
462}
463
464
465bool DP_MEANDER_PLACER::FixRoute( const VECTOR2I& aP, ITEM* aEndItem, bool aForceFinish )
466{
467 LINE lP( m_originPair.PLine(), m_finalShapeP );
468 LINE lN( m_originPair.NLine(), m_finalShapeN );
469
470 m_currentNode->Add( lP );
471 m_currentNode->Add( lN );
472
474
475 return true;
476}
477
478
480{
481 m_world->KillChildren();
482 return true;
483}
484
485
487{
488 return m_originPair.CP().SegmentCount() > 0 || m_originPair.CN().SegmentCount() > 0;
489}
490
491
493{
494 if( m_currentNode )
496
497 m_currentNode = nullptr;
498 return true;
499}
500
501
503{
504 LINE l1( m_originPair.PLine(), aShape->CLine( 0 ) );
505 LINE l2( m_originPair.NLine(), aShape->CLine( 1 ) );
506
507 if( m_currentNode->CheckColliding( &l1 ) )
508 return false;
509
510 if( m_currentNode->CheckColliding( &l2 ) )
511 return false;
512
513 int w = aShape->Width();
514 int clearance = w + w * 3;
515
516 return m_result.CheckSelfIntersections( aShape, clearance );
517}
518
519
521{
524
525 ITEM_SET traces;
526
527 traces.Add( &m_currentTraceP );
528 traces.Add( &m_currentTraceN );
529
530 return traces;
531}
532
533
535{
536 ITEM_SET lines;
537
538 for( ITEM* item : m_tunedPathN )
539 lines.Add( item );
540
541 for( ITEM* item : m_tunedPathP )
542 lines.Add( item );
543
544 return lines;
545}
546
547
549{
550 return m_currentStart;
551}
552
553
555{
556 return m_currentEnd;
557}
558
559
561{
562 return m_initialSegment->Layers().Start();
563}
564
565
567{
568 if( m_lastLength )
569 return m_lastLength;
570 else
571 return origPathLength();
572}
573
574
576{
577 if( m_lastDelay )
578 return m_lastDelay;
579 else
580 return origPathDelay();
581}
582
583
588
589
590const std::vector<NET_HANDLE> DP_MEANDER_PLACER::CurrentNets() const
591{
592 std::vector<NET_HANDLE> rv;
593 rv.push_back( m_originPair.NetP() );
594 rv.push_back( m_originPair.NetN() );
595 return rv;
596}
597
598
600{
601 // If this is a time domain tuning, calculate the target length for the desired total delay
602 if( m_settings.m_isTimeDomain )
603 {
604 const int64_t curDelay = origPathDelay();
605
606 const int64_t desiredDelayMin = m_settings.m_targetLengthDelay.Min();
607 const int64_t desiredDelayOpt = m_settings.m_targetLengthDelay.Opt();
608 const int64_t desiredDelayMax = m_settings.m_targetLengthDelay.Max();
609
610 const int64_t delayDifferenceOpt = desiredDelayOpt - curDelay;
611
612 const int64_t curLength = origPathLength();
613 const int64_t lengthDiffMin = m_router->GetInterface()->CalculateLengthForDelay(
614 desiredDelayOpt - desiredDelayMin, GetOriginPair().Width(), true, GetOriginPair().Gap(),
615 m_router->GetCurrentLayer(), m_netClass );
616 int64_t lengthDiffOpt = m_router->GetInterface()->CalculateLengthForDelay(
617 std::abs( delayDifferenceOpt ), GetOriginPair().Width(), true, GetOriginPair().Gap(),
618 m_router->GetCurrentLayer(), m_netClass );
619 const int64_t lengthDiffMax = m_router->GetInterface()->CalculateLengthForDelay(
620 desiredDelayMax - desiredDelayOpt, GetOriginPair().Width(), true, GetOriginPair().Gap(),
621 m_router->GetCurrentLayer(), m_netClass );
622
623 lengthDiffOpt = delayDifferenceOpt > 0 ? lengthDiffOpt : -lengthDiffOpt;
624
625 m_settings.m_targetLength.SetMin( curLength + lengthDiffOpt - lengthDiffMin );
626 m_settings.m_targetLength.SetOpt( curLength + lengthDiffOpt );
627 m_settings.m_targetLength.SetMax( curLength + lengthDiffOpt + lengthDiffMax );
628 }
629}
630}
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