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