KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_diff_pair.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright (C) 2013-2015 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
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <cstdio>
23#include <cstdlib>
24#include <cmath>
25#include <limits>
26
27#include <algorithm>
28#include <core/typeinfo.h>
29#include <geometry/shape_rect.h>
30
31#include "pns_diff_pair.h"
32#include "pns_router.h"
33#include "pns_debug_decorator.h"
34#include "pns_utils.h"
35#include "pns_arc.h"
36
37namespace PNS {
38
39class LINE;
40
41
43{
44 m_primP = aPrimP;
45 m_primN = aPrimN;
46
47 m_anchorP = m_primP->Anchor( 0 );
48 m_anchorN = m_primN->Anchor( 0 );
49}
50
51
52void DP_PRIMITIVE_PAIR::SetAnchors( const VECTOR2I& aAnchorP, const VECTOR2I& aAnchorN )
53{
54 m_anchorP = aAnchorP;
55 m_anchorN = aAnchorN;
56}
57
59{
60 m_primP = aPrimP;
61 m_primN = aPrimN;
62}
63
64
65DP_PRIMITIVE_PAIR::DP_PRIMITIVE_PAIR( const VECTOR2I& aAnchorP, const VECTOR2I& aAnchorN )
66{
67 m_anchorP = aAnchorP;
68 m_anchorN = aAnchorN;
69 m_primP = m_primN = nullptr;
70}
71
72
74{
75 m_primP = m_primN = nullptr;
76 m_primP = aOther.m_primP;
77 m_primN = aOther.m_primN;
78
79 m_anchorP = aOther.m_anchorP;
80 m_anchorN = aOther.m_anchorN;
82 m_name = aOther.m_name;
83}
84
85
87{
88 if( aOther.m_primP )
89 {
90 m_primP = aOther.m_primP;
91 }
92
93 if( aOther.m_primN )
94 {
95 m_primN = aOther.m_primN;
96 }
97
98 m_anchorP = aOther.m_anchorP;
99 m_anchorN = aOther.m_anchorN;
100
101 m_isMidtrace = aOther.m_isMidtrace;
102 m_name = aOther.m_name;
103
104 return *this;
105}
106
107
111
112
114{
115 if( !m_primP )
116 return false;
117
118 return m_primP->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T );
119}
120
121
123{
124 if( !aItem->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
125 return DIRECTION_45();
126
127 if( aItem->Anchor( 0 ) == aP )
128 return DIRECTION_45( aItem->Anchor( 0 ) - aItem->Anchor( 1 ) );
129 else
130 return DIRECTION_45( aItem->Anchor( 1 ) - aItem->Anchor( 0 ) );
131}
132
133
134void DP_PRIMITIVE_PAIR::CursorOrientation( const VECTOR2I& aCursorPos, VECTOR2I& aMidpoint, VECTOR2I& aDirection ) const
135{
136 if( !m_primN || !m_primP )
137 return;
138
139 VECTOR2I aP, aN;
140
141 if( m_primP->OfKind( ITEM::SEGMENT_T ) && m_primN->OfKind( ITEM::SEGMENT_T ) )
142 {
143 aP = m_primP->Anchor( 1 );
144 aN = m_primN->Anchor( 1 );
145
146 // If both segments are parallel, use that as the direction. Otherwise, fall back on the
147 // direction perpendicular to the anchor points.
148 const SEG& segP = static_cast<SEGMENT*>( m_primP )->Seg();
149 const SEG& segN = static_cast<SEGMENT*>( m_primN )->Seg();
150
151 if( ( segP.B != segP.A ) && ( segN.B != segN.A ) && segP.ApproxParallel( segN ) )
152 {
153 aMidpoint = ( aP + aN ) / 2;
154 aDirection = segP.B - segP.A;
155 aDirection = aDirection.Resize( ( aP - aN ).EuclideanNorm() );
156 return;
157 }
158 }
159 else
160 {
161 aP = m_primP->Anchor( 0 );
162 aN = m_primN->Anchor( 0 );
163 }
164
165 aMidpoint = ( aP + aN ) / 2;
166 aDirection = ( aP - aN ).Perpendicular();
167
168 if( aDirection.Dot( aCursorPos - aMidpoint ) < 0 )
169 aDirection = -aDirection;
170}
171
172
177
178
183
184
185static DIRECTION_45::AngleType angle( const VECTOR2I &a, const VECTOR2I &b )
186{
187 DIRECTION_45 dir_a( a );
188 DIRECTION_45 dir_b( b );
189
190 return dir_a.Angle( dir_b );
191}
192
193
195{
196 m_entryN = m_entryN.Reverse();
197 m_entryP = m_entryP.Reverse();
198}
199
200
201DIRECTION_45 DIFF_PAIR::getDirection( bool aIsP, bool aEnd ) const
202{
203 const SHAPE_LINE_CHAIN& l = aIsP ? m_p : m_n;
204
205 if( !l.SegmentCount() )
206 return DIRECTION_45();
207
208 const SEG s = aEnd ? l.CSegment( l.SegmentCount() - 1 ) : l.CSegment( 0 );
209
210 if( aEnd )
211 return DIRECTION_45( s ).Opposite();
212 else
213 return DIRECTION_45( s );
214}
215
216
217bool DIFF_PAIR::BuildInitial( const DP_GATEWAY& aEntry, const DP_GATEWAY& aTarget, bool aPrefDiagonal, bool aFitVias,
218 float& aBestCouplingRatio, float& aAspectRatio )
219{
220 SHAPE_LINE_CHAIN p = DIRECTION_45().BuildInitialTrace( aEntry.AnchorP(), aTarget.AnchorP(), aPrefDiagonal );
221 SHAPE_LINE_CHAIN n = DIRECTION_45().BuildInitialTrace( aEntry.AnchorN(), aTarget.AnchorN(), aPrefDiagonal );
222
225 wxString failReason;
226 bool fail = false;
227
228#ifdef DIFF_PLACER_EXTRA_VERBOSE
229 PNS_DBG( dbg, AddShape, &p, RED, 20000,
230 wxString::Format( "init+ prefDiag %d (dims %s) %s/%s cl %d %d %d", aPrefDiagonal ? 1 : 0, m_dims.Format(),
231 aEntry.GetName(), aTarget.GetName(), m_dims.MinClearance(),
232 aEntry.Dimensions().MinClearance(), aTarget.Dimensions().MinClearance() ) );
233 PNS_DBG( dbg, AddShape, &n, BLUE, 20000, wxT( "init-" ) );
234#endif
235
236 SHAPE_LINE_CHAIN sum_n, sum_p;
237 m_p = p;
238 m_n = n;
239
240 bool entryIsStraight = false;
241 bool targetIsStraight = false;
242
243 if( aEntry.HasPrimaryDirection() && m_p.SegmentCount() >= 1 )
244 {
245 auto dirMask = aEntry.PrimaryDirectionMask();
246 DIRECTION_45 dir2( m_p.CSegment( 0 ) );
247 if( !( dirMask & dir2.Mask() ) )
248 {
249 fail = true;
250 failReason = wxT( "fail-primary-entry" );
251 }
252 }
253
254 if( aTarget.HasPrimaryDirection() && m_p.SegmentCount() >= 1 )
255 {
256 auto dirMask = aTarget.PrimaryDirectionMask();
257 DIRECTION_45 dir2( m_p.CSegment( -1 ) );
258 if( !( dirMask & ( dir2.Opposite().Mask() ) ) )
259 {
260 fail = true;
261 failReason = wxT( "fail-primary-target" );
262 }
263 }
264
265
266 if( aEntry.HasEntryLines() )
267 {
268 if( !aEntry.Entry().CheckConnectionAngle( *this, mask ) )
269 {
270 fail = true;
271 failReason = wxT( "fail-entry-angle" );
272 }
273
274
275 sum_p = aEntry.Entry().CP();
276 sum_n = aEntry.Entry().CN();
277 sum_p.Append( p );
278 sum_n.Append( n );
279 }
280 else
281 {
282 sum_p = p;
283 sum_n = n;
284 }
285
287
288 m_p = sum_p;
289 m_n = sum_n;
290
291 if( !fail && aTarget.HasEntryLines() )
292 {
293 DP_GATEWAY t( aTarget );
294 t.Reverse();
295
296 if( !CheckConnectionAngle( t.Entry(), mask ) )
297 {
298 fail = true;
299 failReason = wxT( "fail-exit-angle" );
300 }
301
302 sum_p.Append( t.Entry().CP() );
303 sum_n.Append( t.Entry().CN() );
304 }
305
306 m_p = sum_p;
307 m_n = sum_n;
308 m_p.Simplify2();
309 m_n.Simplify2();
310
311 if( !fail )
312 {
313 float coupledLength;
314 bool gapOK;
315 std::tie( coupledLength, gapOK ) = CoupledLength( m_p, m_n );
316
317 if( !gapOK )
318 {
319 fail = true;
320 failReason = wxT( "fail-gap" );
321 }
322
323 float minLength = std::min( m_p.Length(), m_n.Length() );
324 if( minLength >= 1.0 )
325 aBestCouplingRatio = coupledLength / minLength;
326 else
327 aBestCouplingRatio = 0;
328 }
329
330
331 auto ip_p = p.SelfIntersecting();
332 auto ip_n = n.SelfIntersecting();
333
334 if( !fail && ( ip_p || ip_n ) )
335 {
336 PNS_DBG( dbg, AddPoint, ip_p->p, RED, 20000, wxT( "ip+" ) );
337 PNS_DBG( dbg, AddPoint, ip_n->p, BLUE, 20000, wxT( "ip-" ) );
338
339 fail = true;
340 failReason = wxT( "fail-self-intersect" );
341 }
342
343
344 if( !fail && m_p.Intersects( m_n ) )
345 {
346 fail = true;
347 failReason = wxT( "fail-intersect" );
348 }
349 int distP = 0, distN = 0, threshold = 0;
350
351 if( aFitVias )
352 {
353 distP = m_n.Distance( m_p.CLastPoint() );
354 distN = m_p.Distance( m_n.CLastPoint() );
355
356 threshold = ( m_dims.ViaDiameter() / 2 + 1 ) + m_dims.MinClearance() - m_dims.Width() / 2;
357
358 if( distP < threshold || distN < threshold )
359 {
360 fail = true;
361 failReason = wxString::Format( "fail-vias dp=%d dn=%d thr=%d cl=%d w=%d", distP, distN, threshold,
362 m_dims.MinClearance(), m_dims.Width() );
363 }
364 }
365
366 if( !fail )
367 failReason = wxT( "OK" );
368
369 if( entryIsStraight && targetIsStraight )
370 aAspectRatio = 1.0;
371
372#ifdef DIFF_PLACER_EXTRA_VERBOSE
373 PNS_DBG( dbg, BeginGroup,
374 wxString::Format( "fit-%s-%s-%s fail=%d gap=[%s] prioE=%d prioT=%d d=%d eis=%d tis=%d cpr=%.2f ar =%.2f v "
375 "%d %d %d fv %d",
376 failReason, aEntry.GetName(), aTarget.GetName(), fail ? 1 : 0,
377 ::PNS::Format( m_dims.GapConstraint() ), aEntry.Priority(), aTarget.Priority(),
378 aEntry.IsDiagonal() ? 1 : 0, entryIsStraight ? 1 : 0, targetIsStraight ? 1 : 0,
379 aBestCouplingRatio, aAspectRatio, distP, distN, threshold, aFitVias ? 1 : 0 ),
380 0 );
381 PNS_DBG( dbg, AddShape, &m_p, RED, 100000, wxT( "+" ) );
382 PNS_DBG( dbg, AddShape, &m_n, BLUE, 100000, wxT( "-" ) );
383
384 PNS_DBGN( dbg, EndGroup );
385#endif
386
387 return !fail;
388}
389
390const wxString DP_GATEWAY::GetName() const
391{
392 wxString s = wxString::Format("%s [p:%d d:%s/%s]", m_name, m_priority, m_dirP.Format(), m_dirN.Format() );
393 return s;
394}
395
396
397bool DIFF_PAIR::CheckConnectionAngle( const DIFF_PAIR& aOther, int aAllowedAngles ) const
398{
399 bool checkP, checkN;
400
401 if( m_p.SegmentCount() == 0 || aOther.m_p.SegmentCount() == 0 )
402 {
403 checkP = true;
404 }
405 else
406 {
407 DIRECTION_45 p0( m_p.CSegment( -1 ) );
408 DIRECTION_45 p1( aOther.m_p.CSegment( 0 ) );
409
410 checkP = ( p0.Angle( p1 ) & aAllowedAngles ) != 0;
411 }
412
413 if( m_n.SegmentCount() == 0 || aOther.m_n.SegmentCount() == 0 )
414 {
415 checkN = true;
416 }
417 else
418 {
419 DIRECTION_45 n0( m_n.CSegment( -1 ) );
420 DIRECTION_45 n1( aOther.m_n.CSegment( 0 ) );
421
422 checkN = ( n0.Angle( n1 ) & aAllowedAngles ) != 0;
423 }
424
425 return checkP && checkN;
426}
427
428
430{
431 return DIFF_PAIR( m_entryP, m_entryN, 0 );
432}
433
434
435std::optional<DP_GATEWAY> DP_GATEWAY::Extend( int aLength )
436{
437 DIRECTION_45 dP = m_dirP;
438 DIRECTION_45 dN = m_dirN;
439
440 if( dP != dN )
441 return std::nullopt;
442
443 DP_GATEWAY extended( *this );
444
445 VECTOR2I d = dP.ToVector();
446 VECTOR2I l = d.Resize( aLength );
447 VECTOR2I perp = dP.Right().Right().ToVector();
448
449 SEG sN( AnchorN(), AnchorN() + l );
450 SEG sP( AnchorP(), AnchorP() + l );
451
452 SEG test( sN.B, sN.B + perp );
453 int dist = test.LineDistance( sP.B, true );
454
455 SEG test2( sP.B, sP.B + perp );
456 int dist2 = test2.LineDistance( sN.B, true );
457
458 // fixme: rework
459 const int epsilon = 10;
460
461 if( dist < -epsilon )
462 {
463 sP.B = test.LineProject( sP.B );
464 }
465 else if( dist2 < -epsilon )
466 {
467 sN.B = test2.LineProject( sN.B );
468 }
469
470
471 extended.m_entryP.Append( sP.B );
472 extended.m_entryN.Append( sN.B );
473 extended.m_anchorP = sP.B;
474 extended.m_anchorN = sN.B;
475
476 return extended;
477}
478
479
480std::optional<DP_GATEWAY> DP_GATEWAY::AddTurns( bool aSide, bool a90Deg, bool aLeft, bool aWiggle )
481{
482 DIRECTION_45 dP = m_dirP;
483 DIRECTION_45 dN = m_dirN;
484 VECTOR2I perp = dP.Right().Right().ToVector();
485 VECTOR2I str = dP.ToVector();
486
488
489
490 SEG sN( AnchorN(), AnchorN() + perp );
491 SEG sP( AnchorP(), AnchorP() + perp );
492 SEG stest( AnchorN(), AnchorN() + str );
493
494 bool invert = stest.Side( AnchorP() ) > 0;
495 int side = 0;
496
497 const double turnAngle = aWiggle ? 22.6 : 22.5;
498 int gap = m_dims.Gap() + m_dims.Width();
499 int leadLen = (int) ( (double) (gap) *tan( turnAngle * M_PI / 180.0 ) ) + 1;
500
501 PNS_DBG( dbg, Message,
502 wxString::Format( "addturn orig %s gap %d %s %s lead %d", m_name, gap, dP.Format(), dN.Format(),
503 leadLen ) );
504
505
506 SHAPE_LINE_CHAIN leadP45( EntryP() );
507 SHAPE_LINE_CHAIN leadN45( EntryN() );
508 VECTOR2I dpr = dP.ToVector().Resize( leadLen );
509 VECTOR2I dnr = dN.ToVector().Resize( leadLen );
510 const VECTOR2I& lastN = EntryN().CLastPoint();
511 const VECTOR2I& lastP = EntryP().CLastPoint();
512
513 if( side )
514 {
515 dpr = -dpr;
516 dnr = -dnr;
517 }
518
519 leadP45.Append( EntryP().CLastPoint() + dpr );
520 leadN45.Append( EntryN().CLastPoint() + dnr );
521
522 if( !aLeft )
523 {
524 DP_GATEWAY gw45_r( *this );
525 gw45_r.SetAnchors( leadP45.CLastPoint(), lastN );
526 gw45_r.m_isDiagonal = !IsDiagonal();
527 //gw45_r.SetPriority( 10 );
528 gw45_r.SetEntryLines( leadP45, EntryN() );
529 DIRECTION_45 dPR = ( invert ? dP.Left() : dP.Right() );
530 DIRECTION_45 dNR = ( invert ? dN.Left() : dN.Right() );
531 gw45_r.SetDirections( dPR, dNR );
532 gw45_r.SetPrimaryDirection( dPR );
533 return gw45_r;
534 }
535 else
536 {
537 DP_GATEWAY gw45_l( *this );
538 gw45_l.SetAnchors( lastP, leadN45.CLastPoint() );
539 gw45_l.m_isDiagonal = !IsDiagonal();
540 //gw45_l.SetPriority( 10 );
541 gw45_l.SetEntryLines( EntryP(), leadN45 );
542 DIRECTION_45 dPL = ( invert ? dP.Right() : dP.Left() );
543 DIRECTION_45 dNL = ( invert ? dN.Right() : dN.Left() );
544 gw45_l.SetDirections( dPL, dNL );
545 gw45_l.SetPrimaryDirection( dPL );
546 return gw45_l;
547 }
548}
549
550
551void DP_GATEWAYS::addGateway( DP_GATEWAY& aGw, const wxString& name, bool aAddTurns )
552{
553 if( name != wxT( "" ) )
554 aGw.SetName( name );
555
556 aGw.SetDimensions( m_dims );
557 m_gateways.push_back( aGw );
558
559 if( aAddTurns )
560 {
561 auto router = ROUTER::GetInstance();
562 const double widthToMiterRatio = router->Settings().DiffPairWidthToMiterRatio();
563 const int extensionDist = (int) ( widthToMiterRatio * (double) aGw.Dimensions().Width() );
564 std::optional<DP_GATEWAY> extend, turn45_l, turn45_r, turn45_lw, turn45_rw;
565 std::optional<DP_GATEWAY> turn45_le, turn45_re;
566
567 turn45_l = aGw.AddTurns( false, false, true, false );
568 if( turn45_l.has_value() )
569 {
570 addGateway( *turn45_l, "45-l" );
571 turn45_le = turn45_l->Extend( extensionDist );
572 if( turn45_le.has_value() )
573 {
574 addGateway( *turn45_le, "45-lext" );
575
576 std::optional<DP_GATEWAY> turn90_lel = turn45_le->AddTurns( false, false, true, false );
577 if( turn90_lel.has_value() )
578 addGateway( *turn90_lel, "90-lext-l" );
579 }
580 }
581 //if( turn45_lw = aGw.AddTurns( false, false, true, true ) )
582 // addGateway( *turn45_lw, "45-lw" );
583 turn45_r = aGw.AddTurns( false, false, false, false );
584 if( turn45_r.has_value() )
585 {
586 addGateway( *turn45_r, "45-r" );
587 turn45_re = turn45_r->Extend( extensionDist );
588 if( turn45_re.has_value() )
589 {
590 std::optional<DP_GATEWAY> turn90_rer = turn45_re->AddTurns( false, false, false, false );
591 if( turn90_rer.has_value() )
592 addGateway( *turn90_rer, "90-rext-r" );
593 }
594 }
595
596 //if( turn45_rw = aGw.AddTurns( false, false, false, true ) )
597 // addGateway( *turn45_rw, "45-rw" );
598 }
599}
600
601
602void DP_GATEWAYS::BuildOrthoProjections( DP_GATEWAYS& aEntries, const VECTOR2I& aCursorPos,
603 int aOrthoScore )
604{
605 int cnt = 0;
606 for( const DP_GATEWAY& g : aEntries.Gateways() )
607 {
608 VECTOR2I midpoint( ( g.AnchorP() + g.AnchorN() ) / 2 );
609 SEG guide_s( midpoint, midpoint + VECTOR2I( 1, 0 ) );
610 SEG guide_d( midpoint, midpoint + VECTOR2I( 1, 1 ) );
611
612 VECTOR2I proj_s = guide_s.LineProject( aCursorPos );
613 VECTOR2I proj_d = guide_d.LineProject( aCursorPos );
614
615 int dist_s = ( proj_s - aCursorPos ).EuclideanNorm();
616 int dist_d = ( proj_d - aCursorPos ).EuclideanNorm();
617
618 VECTOR2I proj = ( dist_s < dist_d ? proj_s : proj_d );
619
620 DP_GATEWAYS targets( m_dims );
621 targets.m_fitVias = m_fitVias;
622
623 targets.BuildForCursor( proj );
624
625 for( DP_GATEWAY t : targets.Gateways() )
626 {
627 t.SetPriority( aOrthoScore );
628 t.SetName( wxString::Format("ortho-%d", cnt).ToStdString() );
629 m_gateways.push_back( t );
630 cnt++;
631 }
632 }
633}
634
635
636std::vector<DP_GATEWAYS::FIT_RESULT> DP_GATEWAYS::FitGateways( DP_GATEWAYS& aEntry, DP_GATEWAYS& aTarget,
637 bool aFitVias )
638{
639 std::vector<DP_GATEWAYS::FIT_RESULT> results;
640
642
643 PNS_DBG( dbg, BeginGroup, wxT( "fit-gateways" ), 0 );
644
645 for( bool diagonal : { true, false } )
646 {
647 for( const DP_GATEWAY& g_entry : aEntry.Gateways() )
648 {
649 for( const DP_GATEWAY& g_target : aTarget.Gateways() )
650 {
652 result.score = g_entry.Priority();
653 result.score += g_target.Priority();
654
655 DIFF_PAIR l( m_dims );
656 if( l.BuildInitial( g_entry, g_target, diagonal, aFitVias, result.coupledRatio, result.aspectRatio ) )
657 {
658 result.p = l.CP();
659 result.n = l.CN();
660 result.diagonal = diagonal;
661 result.entry = g_entry;
662 result.target = g_target;
663 results.push_back( result );
664 }
665 }
666 }
667 }
668 PNS_DBGN( dbg, EndGroup );
669
670 return results;
671}
672
673
675{
676 VECTOR2I dir( std::abs( a.x - b.x ), std::abs( a.y - b.y ) );
677
678 return ( dir.x == 0 && dir.y != 0 ) || ( dir.x == dir.y ) || ( dir.y == 0 && dir.x != 0 );
679}
680
681
682void DP_GATEWAYS::FilterByOrientation( int aDirectionMask )
683{
684 std::erase_if( m_gateways,
685 [aDirectionMask]( const DP_GATEWAY& dp )
686 {
687 return ( !( !dp.HasPrimaryDirection() || ( dp.PrimaryDirectionMask() & aDirectionMask ) ) );
688 } );
689}
690
691
692static VECTOR2I makeGapVector( VECTOR2I dir, int length )
693{
694 int l = length / 2;
695 VECTOR2I rv;
696
697 if( dir.EuclideanNorm() == 0 )
698 return dir;
699
700 do
701 {
702 rv = dir.Resize( l );
703 l++;
704 } while( ( rv * 2 ).EuclideanNorm() < length );
705
706 return rv;
707}
708
709
710void DP_GATEWAYS::BuildFromPrimitivePair( const DP_PRIMITIVE_PAIR& aPair, bool aPreferDiagonal )
711{
712 VECTOR2I majorDirection;
713 VECTOR2I p0_p, p0_n;
714 int orthoFanDistance = 0;
715 int diagFanDistance = 0;
716 const int gap = m_dims.Gap() + m_dims.Width();
717 const SHAPE* shP = nullptr;
718
719 if( aPair.PrimP() == nullptr || aPair.PrimN() == nullptr )
720 {
721 BuildGeneric( aPair.AnchorP(), aPair.AnchorN(), 0, true );
722 return;
723 }
724
725 const int pvMask = ITEM::SOLID_T | ITEM::VIA_T;
726
727 if( aPair.PrimP()->OfKind( pvMask ) && aPair.PrimN()->OfKind( pvMask ) )
728 {
729 p0_p = aPair.AnchorP();
730 p0_n = aPair.AnchorN();
731
732 // TODO(JE) padstacks
733 shP = aPair.PrimP()->Shape( -1 );
734 }
735 else if( aPair.PrimP()->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T )
736 && aPair.PrimN()->OfKind( ITEM::SEGMENT_T | ITEM::ARC_T ) )
737 {
738 buildDpContinuation( aPair, aPreferDiagonal );
739
740 return;
741 }
742
743 majorDirection = ( p0_p - p0_n ).Perpendicular();
744
745 int colinearityThreshold = DP_PRIMITIVE_PAIR::DP_ASSUME_PRIMS_COLINEAR_FACTOR * aPair.GetMinDimension();
746
747 if( shP == nullptr )
748 return;
749
750 switch( shP->Type() )
751 {
752 case SH_CIRCLE:
753 BuildGeneric ( p0_p, p0_n, colinearityThreshold, true );
754 return;
755
756 case SH_RECT:
757 {
758 int w = static_cast<const SHAPE_RECT*>( shP )->GetWidth();
759 int h = static_cast<const SHAPE_RECT*>( shP )->GetHeight();
760
761 if( w < h )
762 std::swap( w, h );
763
764 orthoFanDistance = ( w + 1 )* 3 / 2;
765 diagFanDistance = ( w - h );
766 break;
767 }
768
769 case SH_SEGMENT:
770 {
771 int w = static_cast<const SHAPE_SEGMENT*>( shP )->GetWidth();
772 SEG s = static_cast<const SHAPE_SEGMENT*>( shP )->GetSeg();
773
774 orthoFanDistance = w + ( s.B - s.A ).EuclideanNorm();
775 diagFanDistance = ( s.B - s.A ).EuclideanNorm();
776 break;
777 }
778
779 case SH_SIMPLE:
780 case SH_COMPOUND:
781 {
782 BOX2I bbox = shP->BBox();
783 int w = bbox.GetWidth();
784 int h = bbox.GetHeight();
785
786 if( w < h )
787 std::swap( w, h );
788
789 orthoFanDistance = ( w + 1 )* 3 / 2;
790 diagFanDistance = ( w - h );
791 break;
792 }
793
794 default:
795 wxFAIL_MSG( wxString::Format( wxT( "Unsupported starting primitive: %d (%s)." ),
796 shP->Type(),
797 SHAPE_TYPE_asString( shP->Type() ) ) );
798 break;
799 }
800
801 if( checkDiagonalAlignment( p0_p, p0_n ) )
802 {
803 int padDist = ( p0_p - p0_n ).EuclideanNorm();
804
805 for( int k = 0; k < 2; k++ )
806 {
807 VECTOR2I dir, dp, dv;
808
809 if( k == 0 )
810 dir = makeGapVector( majorDirection, orthoFanDistance );
811 else
812 dir = makeGapVector( majorDirection, diagFanDistance );
813
814 int d = std::max( 0, padDist - gap );
815 dp = makeGapVector( dir, d );
816 dv = makeGapVector( p0_n - p0_p, d );
817
818 for( int i = 0; i < 2; i++ )
819 {
820 int sign = i ? -1 : 1;
821
822 VECTOR2I gw_p( p0_p + sign * ( dir + dp ) + dv );
823 VECTOR2I gw_n( p0_n + sign * ( dir + dp ) - dv );
824
825 SHAPE_LINE_CHAIN entryP( { p0_p, p0_p + sign * dir, gw_p } );
826 SHAPE_LINE_CHAIN entryN( { p0_n, p0_n + sign * dir, gw_n } );
827
828 DP_GATEWAY gw( gw_p, gw_n, false );
829
830 gw.SetName( wxString::Format( "pp-%d-%d", k, i ).ToStdString() );
831 gw.SetEntryLines( entryP, entryN );
832
833 DIRECTION_45 dir1 = DIRECTION_45( sign * dir );
834
835 gw.SetDimensions( m_dims );
836 gw.SetPriority( 101 - k );
837 gw.SetDirections( dir1, dir1 );
838 gw.SetPrimaryDirection( dir1 );
839 m_gateways.push_back( gw );
840
841 auto gw_ext = gw.Extend( 400000 );
842 if( gw_ext )
843 addGateway( *gw_ext, "pp-ext", true );
844 }
845 }
846 }
847
848 BuildGeneric( p0_p, p0_n, colinearityThreshold, true );
849}
850
851
852void DP_GATEWAYS::BuildForCursor( const VECTOR2I& aCursorPos, int aDirectionMask )
853{
854 int gap = m_fitVias ? m_dims.ViaGap() + m_dims.ViaDiameter() : m_dims.Gap() + m_dims.Width();
855
856 for( bool diagonal : { false, true } )
857 {
858 for( int i = 0; i < 4; i++ )
859 {
860 VECTOR2I dir;
861
862 if( !diagonal )
863 {
864 dir = makeGapVector( VECTOR2I( gap, gap ), gap );
865
866 if( i % 2 == 0 )
867 dir.x = -dir.x;
868
869 if( i / 2 == 0 )
870 dir.y = -dir.y;
871 }
872 else
873 {
874 if( i /2 == 0 )
875 dir = VECTOR2I( (gap + 1) / 2 * ( ( i % 2 ) ? -1 : 1 ), 0 );
876 else
877 dir = VECTOR2I( 0, (gap + 1) / 2 * ( ( i % 2 ) ? -1 : 1 ) );
878 }
879
880 if( m_fitVias )
881 {
882 DIRECTION_45 dirV( dir );
883 BuildGeneric( aCursorPos + dir, aCursorPos - dir, 0, true, true );
884 }
885 else
886 {
887 DP_GATEWAY gw( aCursorPos + dir, aCursorPos - dir, diagonal );
888 gw.SetName( wxString::Format( "cursor-%d-%d", diagonal ? 1 : 0, i ).ToStdString() );
889 gw.SetPrimaryDirection( DIRECTION_45( dir ).Right().Right() );
890 gw.AddPrimaryDirection( DIRECTION_45( dir ).Right().Right().Opposite() );
891 m_gateways.emplace_back( gw );
892 }
893 }
894 }
895}
896
897
898void DP_GATEWAYS::buildEntries( DP_GATEWAY& aGw, const VECTOR2I& p0_p, const VECTOR2I& p0_n )
899{
900 if( !aGw.HasEntryLines() )
901 {
902 SHAPE_LINE_CHAIN lead_p = DIRECTION_45().BuildInitialTrace( aGw.AnchorP(), p0_p, aGw.IsDiagonal() ).Reverse();
903 SHAPE_LINE_CHAIN lead_n = DIRECTION_45().BuildInitialTrace( aGw.AnchorN(), p0_n, aGw.IsDiagonal() ).Reverse();
904 aGw.SetEntryLines( lead_p, lead_n );
905 }
906}
907
908
909void DP_GATEWAYS::buildDpContinuation( const DP_PRIMITIVE_PAIR& aPair, bool aIsDiagonal )
910{
912
913 DP_GATEWAY gw( aPair.AnchorP(), aPair.AnchorN(), aIsDiagonal );
914 gw.SetPriority( 100 );
915 m_gateways.push_back( gw );
916
917 if( !aPair.Directional() )
918 return;
919
920 DIRECTION_45 dP = aPair.DirP();
921 DIRECTION_45 dN = aPair.DirN();
922
923 if( dN != dP )
924 return;
925
926 VECTOR2I perp = dP.Right().Right().ToVector();
927
928 SEG sN( aPair.AnchorN(), aPair.AnchorN() + perp );
929 SEG sP( aPair.AnchorP(), aPair.AnchorP() + perp );
930
931 SEGMENT* primN = static_cast<SEGMENT*>( aPair.PrimN() );
932 SEGMENT* primP = static_cast<SEGMENT*>( aPair.PrimP() );
933
934 int gap = primP->Seg().LineDistance( aPair.AnchorN() );
935
936 OPT_VECTOR2I ipN = sN.IntersectLines( primP->Seg() );
937 OPT_VECTOR2I ipP = sP.IntersectLines( primN->Seg() );
938
939 PNS_DBG( dbg, Message, wxString::Format( "buildDpCont: gap=%d dn=%s dp=%s", gap, dN.Format(), dP.Format() ) );
940 PNS_DBG( dbg, AddItem, aPair.PrimP(), RED, 100000, "+" );
941 PNS_DBG( dbg, AddItem, aPair.PrimN(), BLUE, 100000, "-" );
942
943 SHAPE_LINE_CHAIN leadP, leadN;
944
945 leadP.Append( aPair.AnchorP() );
946 leadN.Append( aPair.AnchorN() );
947
948 if( ipN && !primP->Seg().Contains( *ipN ) )
949 {
950 leadP.Append( *ipN );
951 }
952
953 if( ipP && !primN->Seg().Contains( *ipP ) )
954 {
955 leadN.Append( *ipP );
956 }
957
958 // now leadP/leadN are aligned for a 0/180-degree turn
959
960 DP_GATEWAY gw0( leadP.CPoint( -1 ), leadN.CPoint( -1 ), !aIsDiagonal );
961 gw0.SetPriority( 100 );
962 gw0.SetEntryLines( leadP, leadN );
963 gw0.SetName( "0" );
964 gw0.SetDirections( dP, dN );
965 gw0.SetPrimaryDirection( dP );
966 gw0.SetDimensions( m_dims );
967
968 addGateway( gw0, "gw0", true );
969
970 DP_GATEWAY gw180( gw0 );
971 gw180.SetDirections( dP.Opposite(), dN.Opposite() );
972 gw0.SetPrimaryDirection( dP.Opposite() );
973 addGateway( gw180, "gw180", true );
974}
975
976
977void DP_GATEWAYS::BuildGeneric( const VECTOR2I& p0_p, const VECTOR2I& p0_n, int aColinearityThreshold, bool aBuildEntries,
978 bool aViaMode )
979{
980 SEG st_p[2], st_n[2];
981 SEG d_n[2], d_p[2];
982 const int gap = m_dims.Gap() + m_dims.Width();
983
984 const int padToGapThreshold = 3;
985 int padDist = ( p0_n - p0_p ).EuclideanNorm();
986
987 st_p[0] = SEG(p0_p + VECTOR2I( -100, 0 ), p0_p + VECTOR2I( 100, 0 ) );
988 st_n[0] = SEG(p0_n + VECTOR2I( -100, 0 ), p0_n + VECTOR2I( 100, 0 ) );
989 st_p[1] = SEG(p0_p + VECTOR2I( 0, -100 ), p0_p + VECTOR2I( 0, 100 ) );
990 st_n[1] = SEG(p0_n + VECTOR2I( 0, -100 ), p0_n + VECTOR2I( 0, 100 ) );
991 d_p[0] = SEG( p0_p + VECTOR2I( -100, -100 ), p0_p + VECTOR2I( 100, 100 ) );
992 d_p[1] = SEG( p0_p + VECTOR2I( 100, -100 ), p0_p + VECTOR2I( -100, 100 ) );
993 d_n[0] = SEG( p0_n + VECTOR2I( -100, -100 ), p0_n + VECTOR2I( 100, 100 ) );
994 d_n[1] = SEG( p0_n + VECTOR2I( 100, -100 ), p0_n + VECTOR2I( -100, 100 ) );
995
996 DIRECTION_45 fallbackDir( p0_p - p0_n );
997
998 int mask = fallbackDir.Right().Right().Mask() | fallbackDir.Right().Right().Opposite().Mask();
999
1000 m_gateways.emplace_back( p0_p, p0_n, false, DIRECTION_45::ANG_UNDEFINED, -1, mask, "gen-fallback" );
1001
1002 // midpoint exit & side-by exits
1003 for( int i = 0; i < 2; i++ )
1004 {
1005 int threshold = aColinearityThreshold ? aColinearityThreshold : DIFF_PAIR::DP_PARALLELITY_THRESHOLD;
1006 bool straightColl = st_p[i].ApproxCollinear( st_n[i], threshold );
1007 bool diagColl = d_p[i].ApproxCollinear( d_n[i], threshold );
1008
1009 if( straightColl || diagColl )
1010 {
1011 VECTOR2I dir = makeGapVector( p0_n - p0_p, gap + gap / 2 );
1012 VECTOR2I m = ( p0_p + p0_n ) / 2;
1013 int prio = ( padDist > padToGapThreshold * gap ) ? 2 : 1;
1014
1015 if( !aViaMode )
1016 {
1017 m_gateways.emplace_back( m - dir, m + dir, diagColl, DIRECTION_45::ANG_OBTUSE, prio, DIRECTION_45::AllDirectionsMask(), wxString::Format( "gen-mp-gap %d", gap ).ToStdString() );
1018
1019 dir = makeGapVector( p0_n - p0_p, 2 * gap );
1020 m_gateways.emplace_back( p0_p - dir, p0_p - dir + dir.Perpendicular(), diagColl, DIRECTION_45::ANG_OBTUSE, 2, DIRECTION_45::AllDirectionsMask(), "gen-d1" );
1021 m_gateways.emplace_back( p0_p - dir, p0_p - dir - dir.Perpendicular(), diagColl, DIRECTION_45::ANG_OBTUSE, 2, DIRECTION_45::AllDirectionsMask(), "gen-d2" );
1022 m_gateways.emplace_back( p0_n + dir + dir.Perpendicular(), p0_n + dir, diagColl, DIRECTION_45::ANG_OBTUSE, 2, DIRECTION_45::AllDirectionsMask(), "gen-d3" );
1023 m_gateways.emplace_back( p0_n + dir - dir.Perpendicular(), p0_n + dir, diagColl, DIRECTION_45::ANG_OBTUSE, 2, DIRECTION_45::AllDirectionsMask(), "gen-d4" );
1024 }
1025 }
1026 }
1027
1028 for( int i = 0; i < 2; i++ )
1029 {
1030 for( int j = 0; j < 2; j++ )
1031 {
1032 OPT_VECTOR2I ips[2];
1033
1034 ips[0] = d_n[i].IntersectLines( d_p[j] );
1035 ips[1] = st_p[i].IntersectLines( st_n[j] );
1036
1037 if( d_n[i].Collinear( d_p[j] ) )
1038 ips[0] = OPT_VECTOR2I();
1039
1040 if( st_p[i].Collinear( st_p[j] ) )
1041 ips[1] = OPT_VECTOR2I();
1042
1043 DIRECTION_45 dir1 = DIRECTION_45( p0_p - p0_n ).Left().Left();
1044
1045 // diagonal-diagonal and straight-straight cases - the most typical case if the pads
1046 // are on the same straight/diagonal line
1047 for( int k = 0; k < 2; k++ )
1048 {
1049 if( ips[k] )
1050 {
1051 const VECTOR2I m( *ips[k] );
1052
1053 if( m != p0_p && m != p0_n )
1054 {
1055 int prio = ( padDist > padToGapThreshold * gap ? 10 : 20 );
1056 VECTOR2I g_p( ( p0_p - m ).Resize( ceil( (double) gap * M_SQRT1_2 ) ) );
1057 VECTOR2I g_n( ( p0_n - m ).Resize( ceil( (double) gap * M_SQRT1_2 ) ) );
1058
1059 DP_GATEWAY gw( m + g_p, m + g_n, k == 0 ? true : false, DIRECTION_45::ANG_OBTUSE, prio, 0,
1060 "gen-s" );
1061
1062 DIRECTION_45 dir2( g_p );
1063 DIRECTION_45 dir_next = dir1.IsObtuse( dir2 ) ? dir1.Opposite() : dir1;
1064
1065 gw.SetDimensions( m_dims );
1066 gw.SetDirections( dir_next, dir_next );
1067 gw.SetPrimaryDirection( dir_next );
1068 buildEntries( gw, p0_p, p0_n );
1069
1070 addGateway( gw, "gw-gen-s", false );
1071
1072 auto gw_ext = gw.Extend( 400000 );
1073 if( gw_ext && !aViaMode )
1074 addGateway( *gw_ext, "gw-gen-s-ext", true );
1075 }
1076 }
1077 }
1078
1079 ips[0] = st_n[i].IntersectLines( d_p[j] );
1080 ips[1] = st_p[i].IntersectLines( d_n[j] );
1081
1082 // diagonal-straight cases: 8 possibilities of "weirder" exists
1083 for( int k = 0; k < 2; k++ )
1084 {
1085 if( ips[k] )
1086 {
1087 const VECTOR2I m( *ips[k] );
1088
1089 if( !aViaMode && m != p0_p && m != p0_n )
1090 {
1091 VECTOR2I g_p, g_n;
1092
1093 g_p = ( p0_p - m ).Resize( ceil( (double) gap * M_SQRT2 ) );
1094 g_n = ( p0_n - m ).Resize( ceil( (double) gap ) );
1095
1096 if( angle( g_p, g_n ) != DIRECTION_45::ANG_ACUTE )
1097 m_gateways.emplace_back( m + g_p, m + g_n, true );
1098
1099 g_p = ( p0_p - m ).Resize( gap );
1100 g_n = ( p0_n - m ).Resize( ceil( (double) gap * M_SQRT2 ) );
1101
1102 if( angle( g_p, g_n ) != DIRECTION_45::ANG_ACUTE )
1103 m_gateways.emplace_back( m + g_p, m + g_n, true );
1104 }
1105 }
1106 }
1107 }
1108 }
1109
1110
1111 if( aBuildEntries )
1112 {
1113 for( auto&gw : m_gateways )
1114 buildEntries( gw, p0_p, p0_n );
1115 }
1116
1117}
1118
1119
1120static int minDimensionForPrimitive( const ITEM* aPrim )
1121{
1122 if( const SEGMENT* seg = dyn_cast<const SEGMENT*>( aPrim ) )
1123 return seg->Width();
1124 else if( const ARC* arc = dyn_cast<const ARC*>( aPrim ) )
1125 return arc->Width();
1126 else
1127 {
1128 const SHAPE* shape = aPrim->Shape( -1 );
1129 if( !shape )
1130 return 0;
1131
1132 const BOX2I& bbox = shape->BBox();
1133 return std::min( bbox.GetWidth(), bbox.GetHeight() );
1134 }
1135
1136 return 0;
1137}
1138
1139
1141{
1142 if( !m_primN || !m_primN )
1143 return 0;
1144
1146}
1147
1148
1150{
1151 if( m_hasVias )
1152 {
1153 return DP_PRIMITIVE_PAIR( &m_via_p, &m_via_n );
1154 }
1155 else
1156 {
1157 const LINE lP( PLine() );
1158 const LINE lN( NLine() );
1159
1160 SEGMENT sP( lP, lP.CSegment( -1 ) );
1161 SEGMENT sN( lN, lN.CSegment( -1 ) );
1162
1163 DP_PRIMITIVE_PAIR dpair( &sP, &sN );
1164
1165 if( PLine().IsLinked() )
1166 {
1167 auto lp = PLine().GetLink( PLine().LinkCount() - 1 );
1168 auto ln = NLine().GetLink( NLine().LinkCount() - 1 );
1169 dpair.SetPrimitives( lp, ln );
1170 }
1171 dpair.SetAnchors( sP.Seg().B, sN.Seg().B );
1172
1173 return dpair;
1174 }
1175}
1176
1177
1178bool commonParallelProjection( SEG p, SEG n, SEG &pClip, SEG& nClip )
1179{
1180 SEG n_proj_p( p.LineProject( n.A ), p.LineProject( n.B ) );
1181
1182 int64_t t_a = 0;
1183 int64_t t_b = p.TCoef( p.B );
1184
1185 int64_t tproj_a = p.TCoef( n_proj_p.A );
1186 int64_t tproj_b = p.TCoef( n_proj_p.B );
1187
1188 if( t_b < t_a )
1189 std::swap( t_b, t_a );
1190
1191 if( tproj_b < tproj_a )
1192 std::swap( tproj_b, tproj_a );
1193
1194 if( t_b <= tproj_a )
1195 return false;
1196
1197 if( t_a >= tproj_b )
1198 return false;
1199
1200 int64_t t[4] = { 0, p.TCoef( p.B ), p.TCoef( n_proj_p.A ), p.TCoef( n_proj_p.B ) };
1201 std::vector<int64_t> tv( t, t + 4 );
1202 std::sort( tv.begin(), tv.end() ); // fixme: awful and disgusting way of finding 2 midpoints
1203
1204 int64_t pLenSq = p.SquaredLength();
1205
1206 VECTOR2I dp = p.B - p.A;
1207 pClip.A.x = p.A.x + rescale( (int64_t)dp.x, tv[1], pLenSq );
1208 pClip.A.y = p.A.y + rescale( (int64_t)dp.y, tv[1], pLenSq );
1209
1210 pClip.B.x = p.A.x + rescale( (int64_t)dp.x, tv[2], pLenSq );
1211 pClip.B.y = p.A.y + rescale( (int64_t)dp.y, tv[2], pLenSq );
1212
1213 nClip.A = n.LineProject( pClip.A );
1214 nClip.B = n.LineProject( pClip.B );
1215
1216 return true;
1217}
1218
1219
1220double DIFF_PAIR::Skew() const
1221{
1222 return m_p.Length() - m_n.Length();
1223}
1224
1225
1227 bool aUseGapConstraint,
1228 const std::optional<DP_GAP_CONSTRAINT>& aOverrideGapConstraint ) const
1229{
1232
1233 // Do not simplify the line chains here, otherwise the indices will be invalid
1235
1236 MINOPTMAX<int> gapConstraint;
1237 gapConstraint.SetMin( 0 );
1238 gapConstraint.SetMax( (int) ( (double)m_dims.Width() * threshold ) );
1239
1240 if ( aOverrideGapConstraint )
1241 gapConstraint = aOverrideGapConstraint.value();
1242 else if( aUseGapConstraint )
1243 gapConstraint = m_dims.GapConstraint();
1244
1245 double opt = gapConstraint.Opt();
1246
1247 if( !gapConstraint.HasMax() )
1248 {
1249 gapConstraint.SetMax( opt + 10000 );
1250 }
1251
1252 if( !gapConstraint.HasMin() )
1253 {
1254 gapConstraint.SetMin( opt - 10000 );
1255 }
1256
1257 for( int i = 0; i < p.SegmentCount(); i++ )
1258 {
1259 if( p.IsArcSegment( i ) )
1260 continue;
1261
1262 for( int j = 0; j < n.SegmentCount(); j++ )
1263 {
1264 if( n.IsArcSegment( j ) )
1265 continue;
1266
1267 SEG sp = p.Segment( i );
1268 SEG sn = n.Segment( j );
1269
1270 SEG p_clip, n_clip;
1271
1272 int64_t dist = std::abs( sp.Distance( sn ) ) - m_dims.Width();
1273
1274 if( sp.ApproxParallel( sn, DIFF_PAIR::DP_PARALLELITY_THRESHOLD ) && gapConstraint.Matches( dist ) &&
1275 commonParallelProjection( sp, sn, p_clip, n_clip ) )
1276 {
1277 SEG test0 ( p_clip.A, n_clip.A );
1278 SEG test1 ( p_clip.B, n_clip.B );
1279
1280 // fixme: gives false negatives
1281 /*if( m_p.Intersects( test0 ) )
1282 continue;
1283 if( m_n.Intersects( test0 ) )
1284 continue;
1285 if( m_p.Intersects( test1 ) )
1286 continue;
1287 if( m_n.Intersects( test1 ) )
1288 continue;*/
1289
1290 COUPLED_SEGMENTS spair( p_clip, sp, i, n_clip, sn, j );
1291
1292 spair.linkP = m_line_p.FindLinkedSegment( sp );
1293 spair.linkN = m_line_n.FindLinkedSegment( sn );
1294
1295 aPairs.push_back( spair );
1296 }
1297 }
1298 }
1299}
1300
1301
1302std::pair<int64_t, bool> DIFF_PAIR::CoupledLength( const SHAPE_LINE_CHAIN& aP, const SHAPE_LINE_CHAIN& aN ) const
1303{
1304 int64_t total = 0;
1305 int clearance = m_dims.MinClearance();
1306
1307
1308 if( m_dims.GapConstraint().HasMin() && m_dims.GapConstraint().Min() < clearance )
1309 clearance = std::min( clearance, m_dims.GapConstraint().Min() );
1310
1311 for( int i = 0; i < aP.SegmentCount(); i++ )
1312 {
1313 for( int j = 0; j < aN.SegmentCount(); j++ )
1314 {
1315 SEG sp = aP.CSegment( i );
1316 SEG sn = aN.CSegment( j );
1317
1318 SEG p_clip, n_clip;
1319
1320 int64_t dist = std::abs( sp.Distance( sn ) ) - m_dims.Width();
1321
1322 if( dist < clearance )
1323 return { 0, false };
1324
1325 if( !( sp.ApproxParallel( sn, DP_PARALLELITY_THRESHOLD ) ) )
1326 continue;
1327
1328 if( !commonParallelProjection( sp, sn, p_clip, n_clip ) )
1329 continue;
1330
1331 if( m_dims.GapConstraint().Matches( dist ) )
1332 {
1333 total += p_clip.Length();
1334 }
1335 }
1336 }
1337
1338 return { total, true };
1339}
1340
1341
1343{
1345
1346 CoupledSegmentPairs( pairs );
1347
1348 double l = 0.0;
1349
1350 for( const COUPLED_SEGMENTS& pair : pairs )
1351 l += pair.coupledP.Length();
1352
1353 return l;
1354}
1355
1357{
1358 double lenP = m_p.Length();
1359 double lenN = m_n.Length();
1360
1361 return (lenN + lenP ) / 2.0;
1362}
1363
1364
1365int DIFF_PAIR::CoupledLength( const SEG& aP, const SEG& aN ) const
1366{
1367 SEG p_clip, n_clip;
1368 int64_t dist = std::abs( aP.Distance( aN ) - m_dims.Width() );
1369
1370 if( aP.ApproxParallel( aN ) && m_dims.GapConstraint().Matches( dist )
1371 && commonParallelProjection( aP, aN, p_clip, n_clip ) )
1372 {
1373 return p_clip.Length();
1374 }
1375
1376 return 0;
1377}
1378
1379
1380std::optional<DP_PRIMITIVE_PAIR> DIFF_PAIR::BuildMidpairIntersection( PNS::SEGMENT* aStartSeg, const VECTOR2I& aP )
1381{
1382 bool nHasStart = NLine().ContainsLink( aStartSeg );
1383 const PNS::LINE& refLine = nHasStart ? NLine() : PLine();
1384 const PNS::LINE& coupledLine = nHasStart ? PLine() : NLine();
1385
1387 CoupledSegmentPairs( csVec );
1388 std::optional<PNS::DP_PRIMITIVE_PAIR> prims;
1389
1390 VECTOR2I pproj = refLine.CLine().NearestPoint( aP );
1391
1392 // coupled segments take priority
1393 for( auto& cpair : csVec )
1394 {
1395 if( cpair.coupledN.Contains( pproj ) )
1396 {
1397 auto cproj = cpair.coupledP.LineProject( pproj );
1398 prims = PNS::DP_PRIMITIVE_PAIR( cproj, pproj );
1399 prims->SetPrimitives( cpair.linkP, cpair.linkN );
1400 prims->SetName( wxT( "prim-coupled-p" ) );
1401 break;
1402 }
1403 else if( cpair.coupledP.Contains( pproj ) )
1404 {
1405 auto cproj = cpair.coupledN.LineProject( pproj );
1406 prims = PNS::DP_PRIMITIVE_PAIR( pproj, cproj );
1407 prims->SetPrimitives( cpair.linkP, cpair.linkN );
1408 prims->SetName( wxT( "prim-coupled-n" ) );
1409 break;
1410 }
1411 }
1412
1413 // parallel segments, but noncoupled parts (bends, corner, etc) go second
1414 if( !prims )
1415 {
1416 for( auto& cpair : csVec )
1417 {
1418 auto origP = PLine().CSegment( cpair.indexP );
1419 auto origN = NLine().CSegment( cpair.indexN );
1420
1421 auto dirP = DIRECTION_45( origP );
1422 auto dirN = DIRECTION_45( origN );
1423
1424 if( dirP != dirN )
1425 continue;
1426
1427
1428 if( origN.Contains( pproj ) )
1429 {
1430 auto cproj = origP.LineProject( pproj );
1431 cproj = origP.NearestPoint( cproj );
1432 prims = PNS::DP_PRIMITIVE_PAIR( cproj, pproj );
1433 prims->SetPrimitives( cpair.linkP, cpair.linkN );
1434 prims->SetName( wxT( "prim-extend-n" ) );
1435 break;
1436 }
1437 else if( origP.Contains( pproj ) )
1438 {
1439 auto cproj = origN.LineProject( pproj );
1440 cproj = origN.NearestPoint( cproj );
1441 prims = PNS::DP_PRIMITIVE_PAIR( pproj, cproj );
1442 prims->SetPrimitives( cpair.linkP, cpair.linkN );
1443 prims->SetName( wxT( "prim-extend-p" ) );
1444 break;
1445 }
1446 }
1447 }
1448
1449 // still nothing? take the nearest vertex of the complement track
1450 if( !prims )
1451 {
1452 auto nearest = coupledLine.CLine().NearestPoint( pproj );
1453
1454 if( nHasStart )
1455 {
1456 prims = PNS::DP_PRIMITIVE_PAIR( nearest, pproj );
1457 prims->SetPrimitives( coupledLine.FindLinkContainingVertex( nearest ),
1458 refLine.FindLinkContainingVertex( pproj ) );
1459 }
1460 else
1461 {
1462 prims = PNS::DP_PRIMITIVE_PAIR( pproj, nearest );
1463 prims->SetPrimitives( refLine.FindLinkContainingVertex( pproj ),
1464 coupledLine.FindLinkContainingVertex( nearest ) );
1465 }
1466
1467 prims->SetName( wxT( "nearest-fallback" ) );
1468 }
1469
1470 return prims;
1471}
1472
1473
1475{
1476 const int gapTollerance = 100;
1478
1479 CoupledSegmentPairs( csVec );
1480
1481 std::map<int, int> gapMap;
1482
1483 for( auto& cs : csVec )
1484 {
1485 auto segP = dyn_cast<SEGMENT*>( cs.linkP );
1486 auto segN = dyn_cast<SEGMENT*>( cs.linkN );
1487
1488 if( !segN || !segP )
1489 continue;
1490
1491 int gap = cs.coupledN.LineDistance( cs.coupledP.A ) - ( segP->Width() + segN->Width() ) / 2;
1492
1493 auto iter = gapMap.lower_bound( gap - gapTollerance );
1494 for( ; iter != gapMap.end(); ++iter )
1495 {
1496 if( iter->first < gap + gapTollerance )
1497 {
1498 iter->second += cs.coupledN.Length();
1499 break;
1500 }
1501 }
1502
1503 if( iter == gapMap.end() )
1504 gapMap[gap] = cs.coupledN.Length();
1505 }
1506
1507 int bestGapLen = 0;
1508 int bestGap = 0;
1509
1510 for( auto iter : gapMap )
1511 {
1512 if( bestGapLen < iter.second )
1513 {
1514 bestGapLen = iter.second;
1515 bestGap = iter.first;
1516 }
1517 }
1518
1519 return bestGap;
1520}
1521
1522
1523const wxString DP_DIMENSIONS::Format() const
1524{
1525 wxString ret = wxString::Format( "w:%d gap:%d vgap:%d vdiam:%d mincl:%d gap:[%s]", m_width, m_gap, m_viaGap,
1527
1528 return ret;
1529}
1530}
const char * name
BOX2< VECTOR2I > BOX2I
Definition box2.h:914
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr size_type GetHeight() const
Definition box2.h:212
Represent route directions & corner angles in a 45-degree metric.
Definition direction45.h:37
const SHAPE_LINE_CHAIN BuildInitialTrace(const VECTOR2I &aP0, const VECTOR2I &aP1, bool aStartDiagonal=false, CORNER_MODE aMode=CORNER_MODE::MITERED_45) const
Build a 2-segment line chain between points aP0 and aP1 and following 45-degree routing regime.
AngleType Angle(const DIRECTION_45 &aOther) const
Return the type of angle between directions (this) and aOther.
int Mask() const
const DIRECTION_45 Left() const
Return the direction on the left side of this (i.e.
static int AllDirectionsMask()
const VECTOR2I ToVector() const
AngleType
Represent kind of angle formed by vectors heading in two DIRECTION_45s.
Definition direction45.h:78
const DIRECTION_45 Right() const
Return the direction on the right side of this (i.e.
const std::string Format() const
Format the direction in a human readable word.
bool IsObtuse(const DIRECTION_45 &aOther) const
DIRECTION_45 Opposite() const
Return a direction opposite (180 degree) to (this).
void SetMin(T v)
Definition minoptmax.h:41
bool HasMax() const
Definition minoptmax.h:38
bool Matches(const T v) const
Definition minoptmax.h:47
bool HasMin() const
Definition minoptmax.h:37
void SetMax(T v)
Definition minoptmax.h:42
T Opt() const
Definition minoptmax.h:31
Basic class for a differential pair.
bool CheckConnectionAngle(const DIFF_PAIR &aOther, int allowedAngles) const
SHAPE_LINE_CHAIN m_p
const SHAPE_LINE_CHAIN & CN() const
DP_PRIMITIVE_PAIR EndingPrimitives()
std::vector< COUPLED_SEGMENTS > COUPLED_SEGMENTS_VEC
double CoupledLength() const
double Skew() const
DP_DIMENSIONS m_dims
DIFF_PAIR(const DP_DIMENSIONS &aDims=DP_DIMENSIONS())
bool BuildInitial(const DP_GATEWAY &aEntry, const DP_GATEWAY &aTarget, bool aPrefDiagonal, bool aFitVias, float &aBestCouplingRatio, float &aAspectRatio)
int GuessMostLikelyGap() const
double TotalLength() const
std::optional< DP_PRIMITIVE_PAIR > BuildMidpairIntersection(PNS::SEGMENT *aStartSeg, const VECTOR2I &aP)
DIRECTION_45 getDirection(bool aIsP, bool aEnd) const
SHAPE_LINE_CHAIN m_n
static constexpr int DP_PARALLELITY_THRESHOLD
const SHAPE_LINE_CHAIN & CP() const
void CoupledSegmentPairs(COUPLED_SEGMENTS_VEC &aPairs, bool aUseGapConstraint=true, const std::optional< DP_GAP_CONSTRAINT > &aOverrideGapConstraint=std::optional< DP_GAP_CONSTRAINT >()) const
const wxString Format() const
DP_GAP_CONSTRAINT m_gapConstraint
int MinClearance() const
void BuildForCursor(const VECTOR2I &aCursorPos, int aDirectionMask=-1)
DP_GATEWAYS(const DP_DIMENSIONS &aDims=DP_DIMENSIONS())
void BuildGeneric(const VECTOR2I &p0_p, const VECTOR2I &p0_n, int aColinearityThreshold=0, bool aBuildEntries=false, bool aViaMode=false)
void BuildFromPrimitivePair(const DP_PRIMITIVE_PAIR &aPair, bool aPreferDiagonal)
void FilterByOrientation(int aDirectionMask)
void buildEntries(DP_GATEWAY &aGw, const VECTOR2I &p0_p, const VECTOR2I &p0_n)
void addGateway(DP_GATEWAY &aGw, const wxString &name=wxT(""), bool aAddTurns=false)
std::vector< DP_GATEWAY > & Gateways()
bool checkDiagonalAlignment(const VECTOR2I &a, const VECTOR2I &b) const
void BuildOrthoProjections(DP_GATEWAYS &aEntries, const VECTOR2I &aCursorPos, int aOrthoScore)
void buildDpContinuation(const DP_PRIMITIVE_PAIR &aPair, bool aIsDiagonal)
std::vector< DP_GATEWAY > m_gateways
DP_DIMENSIONS m_dims
std::vector< FIT_RESULT > FitGateways(DP_GATEWAYS &aEntry, DP_GATEWAYS &aTarget, bool aFitVias)
Define a "gateway" for routing a differential pair - e.g.
SHAPE_LINE_CHAIN m_entryP
DIRECTION_45 m_dirP
bool HasPrimaryDirection() const
bool IsDiagonal() const
std::optional< DP_GATEWAY > AddTurns(bool aSide, bool a90Deg, bool aLeft, bool aWiggle)
const DP_DIMENSIONS & Dimensions() const
void SetName(const wxString &aName)
const SHAPE_LINE_CHAIN & EntryP() const
bool HasEntryLines() const
int AllowedAngles() const
DP_GATEWAY(const VECTOR2I &aAnchorP, const VECTOR2I &aAnchorN, bool aIsDiagonal, int aAllowedEntryAngles=DIRECTION_45::ANG_OBTUSE, int aPriority=0, int aDirectionMask=0, const wxString aName=wxT(""))
const VECTOR2I & AnchorN() const
void SetAnchors(const VECTOR2I &aP, const VECTOR2I &aN)
int PrimaryDirectionMask() const
SHAPE_LINE_CHAIN m_entryN
void AddPrimaryDirection(DIRECTION_45 aPrimDir)
void SetDimensions(const DP_DIMENSIONS &aDims)
void SetEntryLines(const SHAPE_LINE_CHAIN &aEntryP, const SHAPE_LINE_CHAIN &aEntryN)
const VECTOR2I & AnchorP() const
const SHAPE_LINE_CHAIN & EntryN() const
DP_DIMENSIONS m_dims
const DIFF_PAIR Entry() const
std::optional< DP_GATEWAY > Extend(int aLength)
void SetPrimaryDirection(DIRECTION_45 aPrimDir)
DIRECTION_45 m_dirN
const wxString GetName() const
int Priority() const
void SetDirections(DIRECTION_45 dP, DIRECTION_45 dN)
void SetPriority(int aPriority)
Store starting/ending primitives (pads, vias or segments) for a differential pair.
DIRECTION_45 DirN() const
const VECTOR2I & AnchorN() const
static constexpr double DP_ASSUME_PRIMS_COLINEAR_FACTOR
const VECTOR2I & AnchorP() const
DIRECTION_45 anchorDirection(const ITEM *aItem, const VECTOR2I &aP) const
void CursorOrientation(const VECTOR2I &aCursorPos, VECTOR2I &aMidpoint, VECTOR2I &aDirection) const
DP_PRIMITIVE_PAIR & operator=(const DP_PRIMITIVE_PAIR &aOther)
void SetPrimitives(ITEM *aPrimP, ITEM *aPrimN)
void SetAnchors(const VECTOR2I &aAnchorP, const VECTOR2I &aAnchorN)
DIRECTION_45 DirP() const
Base class for PNS router board items.
Definition pns_item.h:98
virtual const SHAPE * Shape(int aLayer) const
Return the geometrical shape of the item.
Definition pns_item.h:246
bool OfKind(int aKindMask) const
Definition pns_item.h:181
virtual VECTOR2I Anchor(int n) const
Definition pns_item.h:272
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition pns_line.h:62
const SHAPE_LINE_CHAIN & CLine() const
Definition pns_line.h:146
SEGMENT * FindLinkContainingVertex(const VECTOR2I &aP) const
const SEG CSegment(int aIdx) const
Definition pns_line.h:156
virtual DEBUG_DECORATOR * GetDebugDecorator()=0
ROUTER_IFACE * GetInterface() const
Definition pns_router.h:261
ROUTING_SETTINGS & Settings()
Definition pns_router.h:235
static ROUTER * GetInstance()
double DiffPairGapCouplingRecognitionThreshold() const
const SEG & Seg() const
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
int LineDistance(const VECTOR2I &aP, bool aDetermineSide=false) const
Return the closest Euclidean distance between point aP and the line defined by the ends of segment (t...
Definition seg.cpp:710
VECTOR2I B
Definition seg.h:46
int Length() const
Return the length (this).
Definition seg.h:340
bool ApproxParallel(const SEG &aSeg, int aDistanceThreshold=1) const
Definition seg.cpp:771
ecoord TCoef(const VECTOR2I &aP) const
Definition seg.h:402
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition seg.h:217
ecoord SquaredLength() const
Definition seg.h:345
bool ApproxCollinear(const SEG &aSeg, int aDistanceThreshold=1) const
Definition seg.cpp:759
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:668
bool Contains(const SEG &aSeg) const
Definition seg.h:321
VECTOR2I LineProject(const VECTOR2I &aP) const
Compute the perpendicular projection point of aP on a line passing through ends of the segment.
Definition seg.cpp:651
int Side(const VECTOR2I &aP) const
Determine on which side of directed line passing via segment ends point aP lies.
Definition seg.h:139
SHAPE_TYPE Type() const
Return the type of the shape.
Definition shape.h:96
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
const SHAPE_LINE_CHAIN Reverse() const
Reverse point order in the line chain.
const std::optional< INTERSECTION > SelfIntersecting() const
Check if the line chain is self-intersecting.
SEG Segment(int aIndex) const
Return a copy of the aIndex-th segment in the line chain.
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 NearestPoint(const VECTOR2I &aP, bool aAllowInternalShapePoints=true) const
Find a point on the line chain that is closest to point aP.
int SegmentCount() const
Return the number of segments in this line chain.
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
bool IsArcSegment(size_t aSegment) const
An abstract shape on 2D plane.
Definition shape.h:124
virtual const BOX2I BBox(int aClearance=0) const =0
Compute a bounding box of the shape, with a margin of aClearance a collision.
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:281
constexpr VECTOR2< T > Perpendicular() const
Compute the perpendicular vector.
Definition vector2d.h:335
constexpr extended_type Dot(const VECTOR2< T > &aVector) const
Compute dot product of self with aVector.
Definition vector2d.h:567
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:406
@ BLUE
Definition color4d.h:52
@ RED
Definition color4d.h:55
static std::string ToStdString(const wxString &aStr)
Push and Shove diff pair dimensions (gap) settings dialog.
bool commonParallelProjection(SEG p, SEG n, SEG &pClip, SEG &nClip)
const wxString Format(const MINOPTMAX< int > x)
static int minDimensionForPrimitive(const ITEM *aPrim)
static VECTOR2I makeGapVector(VECTOR2I dir, int length)
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
@ DIFF_PAIR
#define PNS_DBG(dbg, method,...)
#define PNS_DBGN(dbg, method)
const double epsilon
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:35
@ SH_RECT
axis-aligned rectangle
Definition shape.h:43
@ SH_CIRCLE
circle
Definition shape.h:46
@ SH_SIMPLE
simple polygon
Definition shape.h:47
@ SH_SEGMENT
line segment
Definition shape.h:44
@ SH_COMPOUND
compound shape, consisting of multiple simple shapes
Definition shape.h:49
static wxString SHAPE_TYPE_asString(SHAPE_TYPE a)
Definition shape.h:56
int clearance
wxString result
Test unit parsing edge cases and error handling.
#define M_PI
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:55
constexpr int sign(T val)
Definition util.h:166
T rescale(T aNumerator, T aValue, T aDenominator)
Scale a number (value) by rational (numerator/denominator).
Definition util.h:160
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708