KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pns_optimizer.cpp
Go to the documentation of this file.
1/*
2 * KiRouter - a push-and-(sometimes-)shove PCB router
3 *
4 * Copyright (C) 2013-2014 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * Author: Tomasz Wlostowski <[email protected]>
7 *
8 * This program is free software: you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation, either version 3 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22#include <core/typeinfo.h>
24#include <geometry/shape_rect.h>
26
27#include <cmath>
28
29#include "pns_arc.h"
30#include "pns_line.h"
31#include "pns_diff_pair.h"
32#include "pns_node.h"
33#include "pns_solid.h"
34#include "pns_optimizer.h"
35
36#include "pns_utils.h"
37#include "pns_router.h"
38#include "pns_debug_decorator.h"
39
40
41namespace PNS {
42
43
44int COST_ESTIMATOR::CornerCost( const SEG& aA, const SEG& aB )
45{
46 DIRECTION_45 dir_a( aA ), dir_b( aB );
47
48 switch( dir_a.Angle( dir_b ) )
49 {
50 case DIRECTION_45::ANG_OBTUSE: return 10;
51 case DIRECTION_45::ANG_STRAIGHT: return 5;
52 case DIRECTION_45::ANG_ACUTE: return 50;
53 case DIRECTION_45::ANG_RIGHT: return 30;
54 case DIRECTION_45::ANG_HALF_FULL: return 60;
55 default: return 100;
56 }
57}
58
59
61{
62 int total = 0;
63
64 for( int i = 0; i < aLine.SegmentCount() - 1; ++i )
65 total += CornerCost( aLine.CSegment( i ), aLine.CSegment( i + 1 ) );
66
67 return total;
68}
69
70
72{
73 return CornerCost( aLine.CLine() );
74}
75
76
77void COST_ESTIMATOR::Add( const LINE& aLine )
78{
79 m_lengthCost += aLine.CLine().Length();
80 m_cornerCost += CornerCost( aLine );
81}
82
83
84void COST_ESTIMATOR::Remove( const LINE& aLine )
85{
86 m_lengthCost -= aLine.CLine().Length();
87 m_cornerCost -= CornerCost( aLine );
88}
89
90
91void COST_ESTIMATOR::Replace( const LINE& aOldLine, const LINE& aNewLine )
92{
93 m_lengthCost -= aOldLine.CLine().Length();
94 m_cornerCost -= CornerCost( aOldLine );
95 m_lengthCost += aNewLine.CLine().Length();
96 m_cornerCost += CornerCost( aNewLine );
97}
98
99
100bool COST_ESTIMATOR::IsBetter( const COST_ESTIMATOR& aOther, double aLengthTolerance,
101 double aCornerTolerance ) const
102{
103 if( aOther.m_cornerCost < m_cornerCost && aOther.m_lengthCost < m_lengthCost )
104 return true;
105 else if( aOther.m_cornerCost < m_cornerCost * aCornerTolerance &&
106 aOther.m_lengthCost < m_lengthCost * aLengthTolerance )
107 return true;
108
109 return false;
110}
111
112
114 m_world( aWorld ),
115 m_collisionKindMask( ITEM::ANY_T ),
118{
119}
120
121
123{
124 for( OPT_CONSTRAINT* c : m_constraints )
125 delete c;
126
127 m_constraints.clear();
128}
129
130
132{
133 CACHE_VISITOR( const ITEM* aOurItem, NODE* aNode, int aMask ) :
134 m_ourItem( aOurItem ),
135 m_collidingItem( nullptr ),
136 m_node( aNode ),
137 m_mask( aMask )
138 {}
139
140 bool operator()( ITEM* aOtherItem )
141 {
142 if( !( m_mask & aOtherItem->Kind() ) )
143 return true;
144
145 // TODO(JE) viastacks
146 if( !aOtherItem->Collide( m_ourItem, m_node, m_ourItem->Layer() ) )
147 return true;
148
149 m_collidingItem = aOtherItem;
150 return false;
151 }
152
157};
158
159
160void OPTIMIZER::cacheAdd( ITEM* aItem, bool aIsStatic = false )
161{
162 if( m_cacheTags.find( aItem ) != m_cacheTags.end() )
163 return;
164
165 m_cache.Add( aItem );
166 m_cacheTags[aItem].m_hits = 1;
167 m_cacheTags[aItem].m_isStatic = aIsStatic;
168}
169
170
171void OPTIMIZER::removeCachedSegments( LINE* aLine, int aStartVertex, int aEndVertex )
172{
173 if( !aLine->IsLinked() )
174 return;
175
176 auto links = aLine->Links();
177
178 if( aEndVertex < 0 )
179 aEndVertex += aLine->PointCount();
180
181 for( int i = aStartVertex; i < aEndVertex - 1; i++ )
182 {
183 LINKED_ITEM* s = links[i];
184 m_cacheTags.erase( s );
185 m_cache.Remove( s );
186 }
187}
188
189
191{
192 if( aItem->Kind() == ITEM::LINE_T )
193 removeCachedSegments( static_cast<LINE*>( aItem ) );
194}
195
196
197void OPTIMIZER::ClearCache( bool aStaticOnly )
198{
199 if( !aStaticOnly )
200 {
201 m_cacheTags.clear();
202 m_cache.Clear();
203 return;
204 }
205
206 for( auto i = m_cacheTags.begin(); i!= m_cacheTags.end(); ++i )
207 {
208 if( i->second.m_isStatic )
209 {
210 m_cache.Remove( i->first );
211 m_cacheTags.erase( i->first );
212 }
213 }
214}
215
216
217bool AREA_CONSTRAINT::Check( int aVertex1, int aVertex2, const LINE* aOriginLine,
218 const SHAPE_LINE_CHAIN& aCurrentPath,
219 const SHAPE_LINE_CHAIN& aReplacement )
220{
221 const VECTOR2I& p1 = aCurrentPath.CPoint( aVertex1 );
222 const VECTOR2I& p2 = aCurrentPath.CPoint( aVertex2 );
223
224 bool p1_in = m_allowedArea.Contains( p1 );
225 bool p2_in = m_allowedArea.Contains( p2 );
226
227 if( p1_in && p2_in )
228 return true;
229
230 if( aVertex1 < aCurrentPath.PointCount() - 1 && !p1_in && p2_in
231 && m_allowedArea.Contains( aCurrentPath.CPoint( aVertex1 + 1 ) ) )
232 return aReplacement.CSegment( 0 ).Angle( aCurrentPath.CSegment( aVertex1 ) ).IsHorizontal();
233
234 if( p1_in && !p2_in && m_allowedArea.Contains( aCurrentPath.CPoint( aVertex2 - 1 ) ) )
235 return aReplacement.CSegment( -1 )
236 .Angle( aCurrentPath.CSegment( aVertex2 - 1 ) )
237 .IsHorizontal();
238
239 //PNS_DBG( dbg, AddShape, m_allowedArea, YELLOW, 10000, wxT( "drag-affected-area" ) );
240 //PNS_DBG( dbg, AddPoint, p1, YELLOW, 1000000, wxT( "drag-p1" ) );
241 //PNS_DBG( dbg, AddPoint, p2, YELLOW, 1000000, wxT( "drag-p2" ) );
242
243 return false;
244}
245
246
247bool PRESERVE_VERTEX_CONSTRAINT::Check( int aVertex1, int aVertex2, const LINE* aOriginLine,
248 const SHAPE_LINE_CHAIN& aCurrentPath,
249 const SHAPE_LINE_CHAIN& aReplacement )
250{
251 bool cv = false;
252
253 for( int i = aVertex1; i < aVertex2; i++ )
254 {
255 SEG::ecoord dist = aCurrentPath.CSegment(i).SquaredDistance( m_v );
256
257 if ( dist <= 1 )
258 {
259 cv = true;
260 break;
261 }
262 }
263
264 if( !cv )
265 return true;
266
267 for( int i = 0; i < aReplacement.SegmentCount(); i++ )
268 {
269 SEG::ecoord dist = aReplacement.CSegment(i).SquaredDistance( m_v );
270
271 if ( dist <= 1 )
272 return true;
273 }
274
275 return false;
276}
277
278
279bool RESTRICT_VERTEX_RANGE_CONSTRAINT::Check( int aVertex1, int aVertex2, const LINE* aOriginLine,
280 const SHAPE_LINE_CHAIN& aCurrentPath,
281 const SHAPE_LINE_CHAIN& aReplacement )
282{
283 return true;
284}
285
286
287bool CORNER_COUNT_LIMIT_CONSTRAINT::Check( int aVertex1, int aVertex2, const LINE* aOriginLine,
288 const SHAPE_LINE_CHAIN& aCurrentPath,
289 const SHAPE_LINE_CHAIN& aReplacement )
290{
291 LINE newPath( *aOriginLine, aCurrentPath );
292 newPath.Line().Replace( aVertex1, aVertex2, aReplacement );
293 newPath.Line().Simplify2();
294 int cc = newPath.CountCorners( m_angleMask );
295
296 if( cc >= m_minCorners )
297 return true;
298
299 // fixme: something fishy with the max corneriness limit
300 // (cc <= m_maxCorners)
301
302 return true;
303}
304
305bool OBTUSE_ONLY_CONSTRAINT::Check( int aVertex1, int aVertex2, const LINE* aOriginLine,
306 const SHAPE_LINE_CHAIN& aCurrentPath,
307 const SHAPE_LINE_CHAIN& aReplacement )
308{
309 auto isAngleOk =
310 []( const SEG& aS1, const SEG& aS2 ) -> bool
311 {
312 DIRECTION_45 d1( aS1 );
313 DIRECTION_45 d2( aS2 );
315 };
316
317 int replSegs = aReplacement.SegmentCount();
318
319 if( replSegs < 1 )
320 return true;
321
322 for( int i = 0; i < replSegs - 1; i++ )
323 {
324 if( !isAngleOk( aReplacement.CSegment( i ), aReplacement.CSegment( i + 1 ) ) )
325 return false;
326 }
327
328 int pathSegs = aCurrentPath.SegmentCount();
329
330 SEG firstReplSeg = aReplacement.CSegment( 0 );
331 SEG lastReplSeg = aReplacement.CSegment( replSegs - 1 );
332
333 if( aVertex1 > 0 )
334 {
335 if( !isAngleOk( aCurrentPath.CSegment( aVertex1 - 1 ), firstReplSeg ) )
336 return false;
337 }
338
339 if( aVertex2 < pathSegs )
340 {
341 if( !isAngleOk( lastReplSeg, aCurrentPath.CSegment( aVertex2 ) ) )
342 return false;
343 }
344
345 return true;
346}
347
348
360static bool pointInside2( const SHAPE_LINE_CHAIN& aL, const VECTOR2I& aP )
361{
362 if( !aL.IsClosed() || aL.SegmentCount() < 3 )
363 return false;
364
365 int result = 0;
366 size_t cnt = aL.PointCount();
367
368 VECTOR2I ip = aL.CPoint( 0 );
369
370 for( size_t i = 1; i <= cnt; ++i )
371 {
372 VECTOR2I ipNext = ( i == cnt ? aL.CPoint( 0 ) : aL.CPoint( i ) );
373
374 if( ipNext.y == aP.y )
375 {
376 if( ( ipNext.x == aP.x )
377 || ( ip.y == aP.y && ( ( ipNext.x > aP.x ) == ( ip.x < aP.x ) ) ) )
378 return true; // pt on polyground boundary
379 }
380
381 if( ( ip.y < aP.y ) != ( ipNext.y < aP.y ) )
382 {
383 if( ip.x >=aP.x )
384 {
385 if( ipNext.x >aP.x )
386 {
387 result = 1 - result;
388 }
389 else
390 {
391 double d = static_cast<double>( ip.x - aP.x ) *
392 static_cast<double>( ipNext.y - aP.y ) -
393 static_cast<double>( ipNext.x - aP.x ) *
394 static_cast<double>( ip.y - aP.y );
395
396 if( !d )
397 return true; // pt on polyground boundary
398
399 if( ( d > 0 ) == ( ipNext.y > ip.y ) )
400 result = 1 - result;
401 }
402 }
403 else
404 {
405 if( ipNext.x >aP.x )
406 {
407 double d = ( (double) ip.x - aP.x ) * ( (double) ipNext.y - aP.y )
408 - ( (double) ipNext.x - aP.x ) * ( (double) ip.y - aP.y );
409
410 if( !d )
411 return true; // pt on polyground boundary
412
413 if( ( d > 0 ) == ( ipNext.y > ip.y ) )
414 result = 1 - result;
415 }
416 }
417 }
418
419 ip = ipNext;
420 }
421
422 return result > 0;
423}
424
425
426bool KEEP_TOPOLOGY_CONSTRAINT::Check( int aVertex1, int aVertex2, const LINE* aOriginLine,
427 const SHAPE_LINE_CHAIN& aCurrentPath,
428 const SHAPE_LINE_CHAIN& aReplacement )
429{
430 SHAPE_LINE_CHAIN encPoly = aOriginLine->CLine().Slice( aVertex1, aVertex2 );
431
432 // fixme: this is a remarkably shitty implementation...
433 encPoly.Append( aReplacement.Reverse() );
434 encPoly.SetClosed( true );
435
436 BOX2I bb = encPoly.BBox();
437 std::vector<JOINT*> joints;
438
439 int cnt = m_world->QueryJoints( bb, joints, aOriginLine->Layers(), ITEM::SOLID_T );
440
441 if( !cnt )
442 return true;
443
444 for( JOINT* j : joints )
445 {
446 if( j->Net() == aOriginLine->Net() )
447 continue;
448
449 if( pointInside2( encPoly, j->Pos() ) )
450 {
451 bool falsePositive = false;
452
453 for( int k = 0; k < encPoly.PointCount(); k++ )
454 {
455 if( encPoly.CPoint(k) == j->Pos() )
456 {
457 falsePositive = true;
458 break;
459 }
460 }
461
462 if( !falsePositive )
463 {
464 //dbg->AddPoint(j->Pos(), 5);
465 return false;
466 }
467 }
468 }
469
470 return true;
471}
472
473
474bool OPTIMIZER::checkColliding( ITEM* aItem, bool aUpdateCache )
475{
477
478 return static_cast<bool>( m_world->CheckColliding( aItem ) );
479}
480
481
483{
484 m_constraints.push_back( aConstraint );
485}
486
487
488bool OPTIMIZER::checkConstraints( int aVertex1, int aVertex2, LINE* aOriginLine,
489 const SHAPE_LINE_CHAIN& aCurrentPath,
490 const SHAPE_LINE_CHAIN& aReplacement )
491{
492 for( OPT_CONSTRAINT* c : m_constraints )
493 {
494 if( !c->Check( aVertex1, aVertex2, aOriginLine, aCurrentPath, aReplacement ) )
495 return false;
496 }
497
498 return true;
499}
500
501
502bool OPTIMIZER::checkColliding( LINE* aLine, const SHAPE_LINE_CHAIN& aOptPath )
503{
504 LINE tmp( *aLine, aOptPath );
505
506 return checkColliding( &tmp );
507}
508
509
511{
512 SHAPE_LINE_CHAIN& line = aLine->Line();
513
514 int step = line.PointCount() - 3;
515 int segs_pre = line.SegmentCount();
516
517 if( step < 0 )
518 return false;
519
520 SHAPE_LINE_CHAIN current_path( line );
521
522 while( true )
523 {
524 int n_segs = current_path.SegmentCount();
525 int max_step = n_segs - 2;
526
527 if( step > max_step )
528 step = max_step;
529
530 if( step < 2 )
531 {
532 line = std::move( current_path );
533 return line.SegmentCount() < segs_pre;
534 }
535
536 bool found_anything = false;
537
538 for( int n = 0; n < n_segs - step; n++ )
539 {
540 const SEG s1 = current_path.CSegment( n );
541 const SEG s2 = current_path.CSegment( n + step );
542 SEG s1opt, s2opt;
543
544 if( DIRECTION_45( s1 ).IsObtuse( DIRECTION_45( s2 ) ) )
545 {
546 VECTOR2I ip = *s1.IntersectLines( s2 );
547
548 s1opt = SEG( s1.A, ip );
549 s2opt = SEG( ip, s2.B );
550
551 if( DIRECTION_45( s1opt ).IsObtuse( DIRECTION_45( s2opt ) ) )
552 {
553 SHAPE_LINE_CHAIN opt_path;
554 opt_path.Append( s1opt.A );
555 opt_path.Append( s1opt.B );
556 opt_path.Append( s2opt.B );
557
558 LINE opt_track( *aLine, opt_path );
559
560 if( !checkColliding( &opt_track ) )
561 {
562 current_path.Replace( s1.Index() + 1, s2.Index(), ip );
563
564 // removeCachedSegments(aLine, s1.Index(), s2.Index());
565 n_segs = current_path.SegmentCount();
566 found_anything = true;
567 break;
568 }
569 }
570 }
571 }
572
573 if( !found_anything )
574 {
575 if( step <= 2 )
576 {
577 line = std::move( current_path );
578 return line.SegmentCount() < segs_pre;
579 }
580
581 step--;
582 }
583 }
584}
585
586
588{
589 SHAPE_LINE_CHAIN& line = aLine->Line();
590 int step = line.SegmentCount() - 1;
591
592 int segs_pre = line.SegmentCount();
593
594 line.Simplify2();
595
596 if( step < 0 )
597 return false;
598
599 SHAPE_LINE_CHAIN current_path( line );
600
601 while( true )
602 {
603 int n_segs = current_path.SegmentCount();
604 int max_step = n_segs - 2;
605
606 if( step > max_step )
607 step = max_step;
608
609 if( step < 1 )
610 break;
611
612 bool found_anything = mergeStep( aLine, current_path, step );
613
614 if( !found_anything )
615 step--;
616
617 if( !step )
618 break;
619 }
620
621 aLine->SetShape( current_path );
622
623 return current_path.SegmentCount() < segs_pre;
624}
625
626
628{
629 SHAPE_LINE_CHAIN& line = aLine->Line();
630
631 int nSegs = line.SegmentCount();
632
633 for( int segIdx = 0; segIdx < line.SegmentCount() - 1; ++segIdx )
634 {
635 SEG s1 = line.CSegment( segIdx );
636 SEG s2 = line.CSegment( segIdx + 1 );
637
638 // Skip zero-length segs caused by abutting arcs
639 if( s1.SquaredLength() == 0 || s2.SquaredLength() == 0 )
640 continue;
641
642 if( s1.Collinear( s2 ) && !line.IsPtOnArc( segIdx + 1 ) )
643 {
644 line.Remove( segIdx + 1 );
645 }
646 }
647
648 return line.SegmentCount() < nSegs;
649}
650
651
652bool OPTIMIZER::Optimize( const LINE* aLine, LINE* aResult, LINE* aRoot )
653{
654 if( !aResult )
655 return false;
656
657 *aResult = *aLine;
658 aResult->ClearLinks();
659
660 bool hasArcs = aLine->ArcCount();
661 bool rv = false;
662
663 if( (m_effortLevel & LIMIT_CORNER_COUNT) && aRoot )
664 {
665 const int angleMask = DIRECTION_45::ANG_OBTUSE;
666 int rootObtuseCorners = aRoot->CountCorners( angleMask );
667 auto c = new CORNER_COUNT_LIMIT_CONSTRAINT( m_world, rootObtuseCorners,
668 aLine->SegmentCount(), angleMask );
669 //PNS_DBG( dbg, Message,
670 // wxString::Format( "opt limit-corner-count root %d maxc %d mask %x",
671 // rootObtuseCorners, aLine->SegmentCount(), angleMask ) );
672
673 addConstraint( c );
674 }
675
677 {
679 addConstraint( c );
680 }
681
683 {
686 addConstraint( c );
687 }
688
690 {
693 //PNS_DBG( dbg, AddShape, &r, YELLOW, 0, wxT( "area-constraint" ) );
694 addConstraint( c );
695 }
696
698 {
699 auto c = new KEEP_TOPOLOGY_CONSTRAINT( m_world );
700 addConstraint( c );
701 }
702
704 {
705 auto c = new OBTUSE_ONLY_CONSTRAINT( m_world );
706 addConstraint( c );
707 }
708
710 rv |= dragFixCorners( aResult );
711
712 // TODO: Fix for arcs
713 if( !hasArcs && m_effortLevel & MERGE_SEGMENTS )
714 rv |= mergeFull( aResult );
715
716 // TODO: Fix for arcs
717 if( !hasArcs && m_effortLevel & MERGE_OBTUSE )
718 rv |= mergeObtuse( aResult );
719
721 rv |= mergeColinear( aResult );
722
723 // TODO: Fix for arcs
724 if( !hasArcs && m_effortLevel & SMART_PADS )
725 rv |= runSmartPads( aResult );
726
727 // TODO: Fix for arcs
728 if( !hasArcs && m_effortLevel & FANOUT_CLEANUP )
729 rv |= fanoutCleanup( aResult );
730
731 return rv;
732}
733
734
735/*
736 * Check if aVIdx is a bad corner; if so, replace it if possible with an obtuse corner
737 */
738bool OPTIMIZER::dragFixCorner( LINE* aLine, int aVIdx )
739{
740 SHAPE_LINE_CHAIN& path = aLine->Line();
741
742 if( aVIdx <= 0 || aVIdx >= path.PointCount() - 1 )
743 return false;
744
745 if( path.IsArcSegment( aVIdx - 1 ) || path.IsArcSegment( aVIdx ) )
746 return false;
747
748 const SEG s1 = path.CSegment( aVIdx - 1 );
749 const SEG s2 = path.CSegment( aVIdx );
750
752
754 return false;
755
757 {
758 if( !m_restrictArea.Contains( path.CPoint( aVIdx ) ) )
759 return false;
760 }
761
762 SHAPE_LINE_CHAIN bestBypass;
763 double bestArea = std::numeric_limits<double>::max();
764
765 for( int posture = 0; posture < 2; posture++ )
766 {
767 SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace( s1.A, s2.B, posture );
768
769 if( bypass.SegmentCount() < 1 )
770 continue;
771
772 if( checkColliding( aLine, bypass ) )
773 continue;
774
775 SHAPE_LINE_CHAIN loop;
776 loop.Append( s1.A );
777 loop.Append( path.CPoint( aVIdx ) );
778 loop.Append( s2.B );
779
780 for( int j = bypass.PointCount() - 1; j >= 0; j-- )
781 loop.Append( bypass.CPoint( j ) );
782
783 loop.SetClosed( true );
784
785 if( double area = std::abs( loop.Area() ); area < bestArea )
786 {
787 bestArea = area;
788 bestBypass = bypass;
789 }
790 }
791
792 if( bestBypass.SegmentCount() < 1 )
793 return false;
794
795 path.Replace( s1.Index(), s2.Index(), bestBypass );
796 path.Simplify2();
797 return true;
798}
799
800
802{
804 return false;
805
806 SHAPE_LINE_CHAIN& path = aLine->Line();
808
809 int anchorIdx = path.Find( anchor );
810
811 if( anchorIdx <= 0 )
812 return false;
813
814 if( path.IsArcSegment( anchorIdx - 1 ) || path.IsArcSegment( anchorIdx ) )
815 return false;
816
817 bool changed = false;
818
819 if( anchorIdx >= path.PointCount() - 1 )
820 {
821 if( anchorIdx > 0 )
822 changed = dragFixCorner( aLine, anchorIdx - 1 );
823
824 return changed;
825 }
826
828 DIRECTION_45( path.CSegment( anchorIdx - 1 ) ).Angle( DIRECTION_45( path.CSegment( anchorIdx ) ) );
829
831 {
832 changed |= dragFixCorner( aLine, anchorIdx - 1 );
833
834 path.Split( anchor );
835 anchorIdx = path.Find( anchor );
836
837 if( anchorIdx > 0 && anchorIdx < path.PointCount() - 1 )
838 changed |= dragFixCorner( aLine, anchorIdx + 1 );
839 }
840 else
841 {
842 changed = dragFixCorner( aLine, anchorIdx );
843 }
844
845 return changed;
846}
847
848
849bool OPTIMIZER::mergeStep( LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, int step )
850{
851 int n_segs = aCurrentPath.SegmentCount();
852
853 int cost_orig = COST_ESTIMATOR::CornerCost( aCurrentPath );
854
855 if( aLine->SegmentCount() < 2 )
856 return false;
857
859 bool is90mode = cornerMode == DIRECTION_45::MITERED_90 || cornerMode == DIRECTION_45::ROUNDED_90;
860
861 DIRECTION_45 orig_start( aLine->CSegment( 0 ), is90mode );
862 DIRECTION_45 orig_end( aLine->CSegment( -1 ), is90mode );
863
864
865 for( int n = 0; n < n_segs - step; n++ )
866 {
867 // Do not attempt to merge false segments that are part of an arc
868 if( aCurrentPath.IsArcSegment( n )
869 || aCurrentPath.IsArcSegment( static_cast<std::size_t>( n ) + step ) )
870 {
871 continue;
872 }
873
874 const SEG s1 = aCurrentPath.CSegment( n );
875 const SEG s2 = aCurrentPath.CSegment( n + step );
876
878 SHAPE_LINE_CHAIN* picked = nullptr;
879 int cost[2];
880
881 for( int i = 0; i < 2; i++ )
882 {
883 SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace( s1.A, s2.B, i, cornerMode );
884 cost[i] = INT_MAX;
885
886 bool ok = false;
887
888 if( !checkColliding( aLine, bypass ) )
889 {
890 ok = checkConstraints ( n, n + step + 1, aLine, aCurrentPath, bypass );
891 }
892
893 if( ok )
894 {
895 path[i] = aCurrentPath;
896 path[i].Replace( s1.Index(), s2.Index(), bypass );
897 path[i].Simplify2();
898 cost[i] = COST_ESTIMATOR::CornerCost( path[i] );
899 }
900 }
901
902 if( cost[0] < cost_orig && cost[0] < cost[1] )
903 picked = &path[0];
904 else if( cost[1] < cost_orig )
905 picked = &path[1];
906
907 if( picked )
908 {
909 n_segs = aCurrentPath.SegmentCount();
910 aCurrentPath = *picked;
911 return true;
912 }
913 }
914
915 return false;
916}
917
918
920 bool aPermitDiagonal ) const
921{
922 BREAKOUT_LIST breakouts;
923
925 {
926 const SHAPE_CIRCLE* cir = static_cast<const SHAPE_CIRCLE*>( aShape );
928 VECTOR2I p0 = cir->GetCenter();
929 VECTOR2I v0( cir->GetRadius() * M_SQRT2, 0 );
930
931 RotatePoint( v0, -angle );
932
933 l.Append( p0 );
934 l.Append( p0 + v0 );
935 breakouts.push_back( l );
936 }
937
938 return breakouts;
939}
940
941
943 bool aPermitDiagonal ) const
944{
945 BREAKOUT_LIST breakouts;
946 const SHAPE_SIMPLE* convex = static_cast<const SHAPE_SIMPLE*>( aItem->Shape( -1 ) );
947
948 BOX2I bbox = convex->BBox( 0 );
949 VECTOR2I p0 = static_cast<const SOLID*>( aItem )->Pos();
950 // must be large enough to guarantee intersecting the convex polygon
951 int length = std::max( bbox.GetWidth(), bbox.GetHeight() ) / 2 + 5;
952 EDA_ANGLE increment = ( aPermitDiagonal ? ANGLE_45 : ANGLE_90 );
953
954 for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += increment )
955 {
957 VECTOR2I v0( p0 + VECTOR2I( length, 0 ) );
958 RotatePoint( v0, p0, -angle );
959
961 int n = convex->Vertices().Intersect( SEG( p0, v0 ), intersections );
962
963 // if n == 1 intersected a segment
964 // if n == 2 intersected the common point of 2 segments
965 // n == 0 can not happen I think, but...
966 if( n > 0 )
967 {
968 l.Append( p0 );
969
970 // for a breakout distance relative to the distance between
971 // center and polygon edge
972 //l.Append( intersections[0].p + (v0 - p0).Resize( (intersections[0].p - p0).EuclideanNorm() * 0.4 ) );
973
974 // for an absolute breakout distance, e.g. 0.1 mm
975 //l.Append( intersections[0].p + (v0 - p0).Resize( 100000 ) );
976
977 // for the breakout right on the polygon edge
978 l.Append( intersections[0].p );
979
980 breakouts.push_back( l );
981 }
982 }
983
984 return breakouts;
985}
986
987
989 bool aPermitDiagonal ) const
990{
991 const SHAPE_RECT* rect = static_cast<const SHAPE_RECT*>(aShape);
992 VECTOR2I s = rect->GetSize();
993 VECTOR2I c = rect->GetPosition() + VECTOR2I( s.x / 2, s.y / 2 );
994
995 BREAKOUT_LIST breakouts;
996 breakouts.reserve( 12 );
997
998 VECTOR2I d_offset;
999
1000 d_offset.x = ( s.x > s.y ) ? ( s.x - s.y ) / 2 : 0;
1001 d_offset.y = ( s.x < s.y ) ? ( s.y - s.x ) / 2 : 0;
1002
1003 VECTOR2I d_vert = VECTOR2I( 0, s.y / 2 + aWidth );
1004 VECTOR2I d_horiz = VECTOR2I( s.x / 2 + aWidth, 0 );
1005
1006 breakouts.emplace_back( SHAPE_LINE_CHAIN( { c, c + d_horiz } ) );
1007 breakouts.emplace_back( SHAPE_LINE_CHAIN( { c, c - d_horiz } ) );
1008 breakouts.emplace_back( SHAPE_LINE_CHAIN( { c, c + d_vert } ) );
1009 breakouts.emplace_back( SHAPE_LINE_CHAIN( { c, c - d_vert } ) );
1010
1011 if( aPermitDiagonal )
1012 {
1013 int l = aWidth + std::min( s.x, s.y ) / 2;
1014
1015 if( s.x >= s.y )
1016 {
1017 breakouts.emplace_back(
1018 SHAPE_LINE_CHAIN( { c, c + d_offset, c + d_offset + VECTOR2I( l, l ) } ) );
1019 breakouts.emplace_back(
1020 SHAPE_LINE_CHAIN( { c, c + d_offset, c + d_offset - VECTOR2I( -l, l ) } ) );
1021 breakouts.emplace_back(
1022 SHAPE_LINE_CHAIN( { c, c - d_offset, c - d_offset + VECTOR2I( -l, l ) } ) );
1023 breakouts.emplace_back(
1024 SHAPE_LINE_CHAIN( { c, c - d_offset, c - d_offset - VECTOR2I( l, l ) } ) );
1025 }
1026 else
1027 {
1028 // fixme: this could be done more efficiently
1029 breakouts.emplace_back(
1030 SHAPE_LINE_CHAIN( { c, c + d_offset, c + d_offset + VECTOR2I( l, l ) } ) );
1031 breakouts.emplace_back(
1032 SHAPE_LINE_CHAIN( { c, c - d_offset, c - d_offset - VECTOR2I( -l, l ) } ) );
1033 breakouts.emplace_back(
1034 SHAPE_LINE_CHAIN( { c, c + d_offset, c + d_offset + VECTOR2I( -l, l ) } ) );
1035 breakouts.emplace_back(
1036 SHAPE_LINE_CHAIN( { c, c - d_offset, c - d_offset - VECTOR2I( l, l ) } ) );
1037 }
1038 }
1039
1040 return breakouts;
1041}
1042
1043
1045 bool aPermitDiagonal ) const
1046{
1047 switch( aItem->Kind() )
1048 {
1049 case ITEM::VIA_T:
1050 {
1051 const VIA* via = static_cast<const VIA*>( aItem );
1052 // TODO(JE) padstacks -- computeBreakouts needs to have a layer argument
1053 return circleBreakouts( aWidth, via->Shape( 0 ), aPermitDiagonal );
1054 }
1055
1056 case ITEM::SOLID_T:
1057 {
1058 const SHAPE* shape = aItem->Shape( -1 );
1059
1060 switch( shape->Type() )
1061 {
1062 case SH_RECT:
1063 return rectBreakouts( aWidth, shape, aPermitDiagonal );
1064
1065 case SH_SEGMENT:
1066 {
1067 const SHAPE_SEGMENT* seg = static_cast<const SHAPE_SEGMENT*> (shape);
1068 const SHAPE_RECT rect = ApproximateSegmentAsRect ( *seg );
1069 return rectBreakouts( aWidth, &rect, aPermitDiagonal );
1070 }
1071
1072 case SH_CIRCLE:
1073 return circleBreakouts( aWidth, shape, aPermitDiagonal );
1074
1075 case SH_SIMPLE:
1076 return customBreakouts( aWidth, aItem, aPermitDiagonal );
1077
1078 default:
1079 break;
1080 }
1081
1082 break;
1083 }
1084
1085 default:
1086 break;
1087 }
1088
1089 return BREAKOUT_LIST();
1090}
1091
1092
1093ITEM* OPTIMIZER::findPadOrVia( int aLayer, NET_HANDLE aNet, const VECTOR2I& aP ) const
1094{
1095 const JOINT* jt = m_world->FindJoint( aP, aLayer, aNet );
1096
1097 if( !jt )
1098 return nullptr;
1099
1100 for( ITEM* item : jt->LinkList() )
1101 {
1102 if( item->OfKind( ITEM::VIA_T | ITEM::SOLID_T ) )
1103 return item;
1104 }
1105
1106 return nullptr;
1107}
1108
1109
1110int OPTIMIZER::smartPadsSingle( LINE* aLine, ITEM* aPad, bool aEnd, int aEndVertex )
1111{
1112 DIRECTION_45 dir;
1113
1114 const int ForbiddenAngles = DIRECTION_45::ANG_ACUTE | DIRECTION_45::ANG_RIGHT |
1116
1117 typedef std::tuple<int, long long int, SHAPE_LINE_CHAIN> RtVariant;
1118 std::vector<RtVariant> variants;
1119
1120 SOLID* solid = dyn_cast<SOLID*>( aPad );
1121
1122 // don't do optimized connections for offset pads
1123 if( solid && solid->Offset() != VECTOR2I( 0, 0 ) )
1124 return -1;
1125
1126 // don't do optimization on vias, they are always round at the moment and the optimizer
1127 // will possibly mess up an intended via exit posture
1128 if( aPad->Kind() == ITEM::VIA_T )
1129 return -1;
1130
1131 BREAKOUT_LIST breakouts = computeBreakouts( aLine->Width(), aPad, true );
1132 SHAPE_LINE_CHAIN line = ( aEnd ? aLine->CLine().Reverse() : aLine->CLine() );
1133 int p_end = std::min( aEndVertex, std::min( 3, line.PointCount() - 1 ) );
1134
1135 // Start at 1 to find a potentially better breakout (0 is the pad connection)
1136 for( int p = 1; p <= p_end; p++ )
1137 {
1138 // If the line is contained inside the pad, don't optimize
1139 if( solid && solid->Shape( -1 ) && !solid->Shape( -1 )->Collide(
1140 SEG( line.CPoint( 0 ), line.CPoint( p ) ), aLine->Width() / 2 ) )
1141 {
1142 continue;
1143 }
1144
1145 for( SHAPE_LINE_CHAIN & breakout : breakouts )
1146 {
1147 for( int diag = 0; diag < 2; diag++ )
1148 {
1150 SHAPE_LINE_CHAIN connect = dir.BuildInitialTrace(
1151 breakout.CLastPoint(), line.CPoint( p ), diag == 0 );
1152
1153 DIRECTION_45 dir_bkout( breakout.CSegment( -1 ) );
1154
1155 if( !connect.SegmentCount() )
1156 continue;
1157
1158 int ang1 = dir_bkout.Angle( DIRECTION_45( connect.CSegment( 0 ) ) );
1159
1160 if( ang1 & ForbiddenAngles )
1161 continue;
1162
1163 if( breakout.Length() > line.Length() )
1164 continue;
1165
1166 v = breakout;
1167 v.Append( connect );
1168
1169 for( int i = p + 1; i < line.PointCount(); i++ )
1170 v.Append( line.CPoint( i ) );
1171
1172 LINE tmp( *aLine, v );
1173 int cc = tmp.CountCorners( ForbiddenAngles );
1174
1175 if( cc == 0 )
1176 {
1177 RtVariant vp;
1178 std::get<0>( vp ) = p;
1179 std::get<1>( vp ) = breakout.Length();
1180 std::get<2>( vp ) = aEnd ? v.Reverse() : v;
1181 std::get<2>( vp ).Simplify2();
1182 variants.push_back( std::move( vp ) );
1183 }
1184 }
1185 }
1186 }
1187
1188 // We attempt to minimize the corner cost (minimizes the segments and types of corners)
1189 // but given two, equally valid costs, we want to pick the longer pad exit. The logic
1190 // here is that if the pad is oblong, the track should not exit the shorter side and parallel
1191 // the pad but should follow the pad's preferential direction before exiting.
1192 // The baseline guess is to start with the existing line the user has drawn.
1193 int min_cost = COST_ESTIMATOR::CornerCost( *aLine );
1194 long long int max_length = 0;
1195 bool found = false;
1196 int p_best = -1;
1197 SHAPE_LINE_CHAIN l_best;
1198
1199 for( RtVariant& vp : variants )
1200 {
1201 LINE tmp( *aLine, std::get<2>( vp ) );
1202 int cost = COST_ESTIMATOR::CornerCost( std::get<2>( vp ) );
1203 long long int len = std::get<1>( vp );
1204
1205 if( !checkColliding( &tmp ) )
1206 {
1207 if( cost < min_cost || ( cost == min_cost && len > max_length ) )
1208 {
1209 l_best = std::get<2>( vp );
1210 p_best = std::get<0>( vp );
1211 found = true;
1212
1213 if( cost <= min_cost )
1214 max_length = std::max<int>( len, max_length );
1215
1216 min_cost = std::min( cost, min_cost );
1217 }
1218 }
1219 }
1220
1221 if( found )
1222 {
1223 aLine->SetShape( l_best );
1224 return p_best;
1225 }
1226
1227 return -1;
1228}
1229
1230
1232{
1233 SHAPE_LINE_CHAIN& line = aLine->Line();
1234
1235 if( line.PointCount() < 3 )
1236 return false;
1237
1238 VECTOR2I p_start = line.CPoint( 0 ), p_end = line.CLastPoint();
1239
1240 ITEM* startPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_start );
1241 ITEM* endPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_end );
1242
1243 int vtx = -1;
1244
1245 if( startPad )
1246 vtx = smartPadsSingle( aLine, startPad, false, 3 );
1247
1248 if( endPad )
1249 smartPadsSingle( aLine, endPad, true,
1250 vtx < 0 ? line.PointCount() - 1 : line.PointCount() - 1 - vtx );
1251
1252 aLine->Line().Simplify2();
1253
1254 return true;
1255}
1256
1257
1258bool OPTIMIZER::Optimize( LINE* aLine, int aEffortLevel, NODE* aWorld, const VECTOR2I& aV )
1259{
1260 OPTIMIZER opt( aWorld );
1261
1262 opt.SetEffortLevel( aEffortLevel );
1263 opt.SetCollisionMask( -1 );
1264
1265 if( aEffortLevel & OPTIMIZER::PRESERVE_VERTEX )
1266 opt.SetPreserveVertex( aV );
1267
1268 LINE tmp( *aLine );
1269 return opt.Optimize( &tmp, aLine );
1270}
1271
1272
1274{
1275 if( aLine->PointCount() < 3 )
1276 return false;
1277
1279
1280 VECTOR2I p_start = aLine->CPoint( 0 ), p_end = aLine->CLastPoint();
1281
1282 ITEM* startPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_start );
1283 ITEM* endPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_end );
1284
1285 int thr = aLine->Width() * 10;
1286 int len = aLine->CLine().Length();
1287
1288 if( !startPad )
1289 return false;
1290
1291 bool startMatch = startPad->OfKind( ITEM::VIA_T | ITEM::SOLID_T );
1292 bool endMatch = false;
1293
1294 if(endPad)
1295 {
1296 endMatch = endPad->OfKind( ITEM::VIA_T | ITEM::SOLID_T );
1297 }
1298 else
1299 {
1300 endMatch = aLine->EndsWithVia();
1301 }
1302
1303 if( startMatch && endMatch && len < thr )
1304 {
1305 for( int i = 0; i < 2; i++ )
1306 {
1307 SHAPE_LINE_CHAIN l2 = DIRECTION_45().BuildInitialTrace( p_start, p_end, i, cornerMode );
1308 LINE repl;
1309 repl = LINE( *aLine, l2 );
1310
1311 if( !m_world->CheckColliding( &repl ) )
1312 {
1313 aLine->SetShape( repl.CLine() );
1314 return true;
1315 }
1316 }
1317 }
1318
1319 return false;
1320}
1321
1322int findCoupledVertices( const VECTOR2I& aVertex, const SEG& aOrigSeg,
1323 const SHAPE_LINE_CHAIN& aCoupled, DIFF_PAIR* aPair, int* aIndices )
1324{
1325 int count = 0;
1326
1327 for ( int i = 0; i < aCoupled.SegmentCount(); i++ )
1328 {
1329 SEG s = aCoupled.CSegment( i );
1330 VECTOR2I projOverCoupled = s.LineProject ( aVertex );
1331
1332 if( s.ApproxParallel( aOrigSeg ) )
1333 {
1334 int64_t dist =
1335 int64_t{ ( ( projOverCoupled - aVertex ).EuclideanNorm() ) } - aPair->Dimensions().Width();
1336
1337 if( aPair->GapConstraint().Matches( dist ) )
1338 {
1339 *aIndices++ = i;
1340 count++;
1341 }
1342 }
1343 }
1344
1345 return count;
1346}
1347
1348
1349bool verifyDpBypass( NODE* aNode, DIFF_PAIR* aPair, bool aRefIsP, const SHAPE_LINE_CHAIN& aNewRef,
1350 const SHAPE_LINE_CHAIN& aNewCoupled )
1351{
1352 LINE refLine ( aRefIsP ? aPair->PLine() : aPair->NLine(), aNewRef );
1353 LINE coupledLine ( aRefIsP ? aPair->NLine() : aPair->PLine(), aNewCoupled );
1354
1355 if( refLine.Collide( &coupledLine, aNode, refLine.Layer() ) )
1356 return false;
1357
1358 if( aNode->CheckColliding ( &refLine ) )
1359 return false;
1360
1361 if( aNode->CheckColliding ( &coupledLine ) )
1362 return false;
1363
1364 return true;
1365}
1366
1367
1368bool coupledBypass( NODE* aNode, DIFF_PAIR* aPair, bool aRefIsP, const SHAPE_LINE_CHAIN& aRef,
1369 const SHAPE_LINE_CHAIN& aRefBypass, const SHAPE_LINE_CHAIN& aCoupled,
1370 SHAPE_LINE_CHAIN& aNewCoupled )
1371{
1372 int vStartIdx[1024]; // fixme: possible overflow
1373 int nStarts = findCoupledVertices( aRefBypass.CPoint( 0 ),
1374 aRefBypass.CSegment( 0 ),
1375 aCoupled, aPair, vStartIdx );
1376 DIRECTION_45 dir( aRefBypass.CSegment( 0 ) );
1377
1378 int64_t bestLength = -1;
1379 bool found = false;
1380 SHAPE_LINE_CHAIN bestBypass;
1381 int si, ei;
1382
1383 for( int i=0; i< nStarts; i++ )
1384 {
1385 for( int j = 1; j < aCoupled.PointCount() - 1; j++ )
1386 {
1387 int delta = std::abs ( vStartIdx[i] - j );
1388
1389 if( delta > 1 )
1390 {
1391 const VECTOR2I& vs = aCoupled.CPoint( vStartIdx[i] );
1392 SHAPE_LINE_CHAIN bypass = dir.BuildInitialTrace( vs, aCoupled.CPoint(j),
1393 dir.IsDiagonal() );
1394
1395 bool tmp;
1396 int64_t coupledLength;
1397
1398 std::tie(coupledLength, tmp)= aPair->CoupledLength( aRef, bypass );
1399
1400 SHAPE_LINE_CHAIN newCoupled = aCoupled;
1401
1402 si = vStartIdx[i];
1403 ei = j;
1404
1405 if(si < ei)
1406 newCoupled.Replace( si, ei, bypass );
1407 else
1408 newCoupled.Replace( ei, si, bypass.Reverse() );
1409
1410 if( coupledLength > bestLength && verifyDpBypass( aNode, aPair, aRefIsP, aRef,
1411 newCoupled) )
1412 {
1413 bestBypass = std::move( newCoupled );
1414 bestLength = coupledLength;
1415 found = true;
1416 }
1417 }
1418 }
1419 }
1420
1421 if( found )
1422 aNewCoupled = std::move( bestBypass );
1423
1424 return found;
1425}
1426
1427
1428bool checkDpColliding( NODE* aNode, DIFF_PAIR* aPair, bool aIsP, const SHAPE_LINE_CHAIN& aPath )
1429{
1430 LINE tmp ( aIsP ? aPair->PLine() : aPair->NLine(), aPath );
1431
1432 return static_cast<bool>( aNode->CheckColliding( &tmp ) );
1433}
1434
1435
1436bool OPTIMIZER::mergeDpStep( DIFF_PAIR* aPair, bool aTryP, int step )
1437{
1438 int n = 1;
1439
1440 SHAPE_LINE_CHAIN currentPath = aTryP ? aPair->CP() : aPair->CN();
1441 SHAPE_LINE_CHAIN coupledPath = aTryP ? aPair->CN() : aPair->CP();
1442
1443 int n_segs = currentPath.SegmentCount() - 1;
1444
1445 bool tmp;
1446 int64_t clenPre;
1447 std::tie(clenPre, tmp) = aPair->CoupledLength( currentPath, coupledPath );
1448 int64_t budget = clenPre / 10; // fixme: come up with something more intelligent here...
1449
1450 while( n < n_segs - step )
1451 {
1452 const SEG s1 = currentPath.CSegment( n );
1453 const SEG s2 = currentPath.CSegment( n + step );
1454
1455 DIRECTION_45 dir1( s1 );
1456 DIRECTION_45 dir2( s2 );
1457
1458 if( dir1.IsObtuse( dir2 ) )
1459 {
1461 dir1.IsDiagonal() );
1462 SHAPE_LINE_CHAIN newRef;
1463 SHAPE_LINE_CHAIN newCoup;
1464 int64_t deltaCoupled = -1, deltaUni = -1;
1465
1466 newRef = currentPath;
1467 newRef.Replace( s1.Index(), s2.Index(), bypass );
1468 bool tmp2;
1469 std::tie(deltaUni, tmp2) = aPair->CoupledLength ( newRef, coupledPath );
1470 deltaUni += (- clenPre + budget);
1471
1472 if( coupledBypass( m_world, aPair, aTryP, newRef, bypass, coupledPath, newCoup ) )
1473 {
1474 std::tie(deltaCoupled, tmp) = aPair->CoupledLength( newRef, newCoup );
1475 deltaCoupled += (- clenPre + budget);
1476
1477 if( deltaCoupled >= 0 )
1478 {
1479 newRef.Simplify2();
1480 newCoup.Simplify2();
1481
1482 aPair->SetShape( newRef, newCoup, !aTryP );
1483 return true;
1484 }
1485 }
1486 else if( deltaUni >= 0 && verifyDpBypass( m_world, aPair, aTryP, newRef, coupledPath ) )
1487 {
1488 newRef.Simplify2();
1489 coupledPath.Simplify2();
1490
1491 aPair->SetShape( newRef, coupledPath, !aTryP );
1492 return true;
1493 }
1494 }
1495
1496 n++;
1497 }
1498
1499 return false;
1500}
1501
1502
1504{
1505 int step_p = aPair->CP().SegmentCount() - 2;
1506 int step_n = aPair->CN().SegmentCount() - 2;
1507
1508 while( 1 )
1509 {
1510 int n_segs_p = aPair->CP().SegmentCount();
1511 int n_segs_n = aPair->CN().SegmentCount();
1512
1513 int max_step_p = n_segs_p - 2;
1514 int max_step_n = n_segs_n - 2;
1515
1516 if( step_p > max_step_p )
1517 step_p = max_step_p;
1518
1519 if( step_n > max_step_n )
1520 step_n = max_step_n;
1521
1522 if( step_p < 1 && step_n < 1 )
1523 break;
1524
1525 bool found_anything_p = false;
1526 bool found_anything_n = false;
1527
1528 if( step_p > 1 )
1529 found_anything_p = mergeDpStep( aPair, true, step_p );
1530
1531 if( step_n > 1 )
1532 found_anything_n = mergeDpStep( aPair, false, step_n );
1533
1534 if( !found_anything_n && !found_anything_p )
1535 {
1536 step_n--;
1537 step_p--;
1538 }
1539 }
1540 return true;
1541}
1542
1543
1545{
1546 return mergeDpSegments( aPair );
1547}
1548
1549
1550static int64_t shovedArea( const SHAPE_LINE_CHAIN& aOld, const SHAPE_LINE_CHAIN& aNew )
1551{
1552 int64_t area = 0;
1553 const int oc = aOld.PointCount();
1554 const int nc = aNew.PointCount();
1555 const int total = oc + nc;
1556
1557 for(int i = 0; i < total; i++)
1558 {
1559 int i_next = (i + 1 == total ? 0 : i + 1);
1560
1561 const VECTOR2I &v0 = i < oc ? aOld.CPoint(i)
1562 : aNew.CPoint( nc - 1 - (i - oc) );
1563 const VECTOR2I &v1 = i_next < oc ? aOld.CPoint ( i_next )
1564 : aNew.CPoint( nc - 1 - (i_next - oc) );
1565 area += -(int64_t) v0.y * v1.x + (int64_t) v0.x * v1.y;
1566 }
1567
1568 return std::abs( area / 2 );
1569}
1570
1571
1572bool tightenSegment( bool dir, NODE *aNode, const LINE& cur, const SHAPE_LINE_CHAIN& in,
1573 SHAPE_LINE_CHAIN& out )
1574{
1575 SEG a = in.CSegment(0);
1576 SEG center = in.CSegment(1);
1577 SEG b = in.CSegment(2);
1578
1579 DIRECTION_45 dirA ( a );
1580 DIRECTION_45 dirCenter ( center );
1581 DIRECTION_45 dirB ( b );
1582
1583 if (!dirA.IsObtuse( dirCenter) || !dirCenter.IsObtuse(dirB))
1584 return false;
1585
1586 SEG guide;
1587 int initial;
1588
1589 //auto dbg = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
1590 if ( dirA.Angle ( dirB ) != DIRECTION_45::ANG_RIGHT )
1591 return false;
1592
1593 {
1594 /*
1595 auto rC = *a.IntersectLines( b );
1596 dbg->AddSegment ( SEG( center.A, rC ), 1 );
1597 dbg->AddSegment ( SEG( center.B, rC ), 2 );
1598 auto perp = dirCenter.Left().Left();
1599
1600 SEG sperp ( center.A, center.A + perp.ToVector() );
1601
1602 auto vpc = sperp.LineProject( rC );
1603 auto vpa = sperp.LineProject( a.A );
1604 auto vpb = sperp.LineProject( b.B );
1605
1606 auto da = (vpc - vpa).EuclideanNorm();
1607 auto db = (vpc - vpb).EuclideanNorm();
1608
1609 auto vp = (da < db) ? vpa : vpb;
1610 dbg->AddSegment ( SEG( vpc, vp ), 5 );
1611
1612
1613 guide = SEG ( vpc, vp );
1614 */
1615 }
1616
1617 int da = a.Length();
1618 int db = b.Length();
1619
1620 if( da < db )
1621 guide = a;
1622 else
1623 guide = b;
1624
1625 initial = guide.Length();
1626
1627 int step = initial;
1628 int current = step;
1629 SHAPE_LINE_CHAIN snew;
1630
1631 while( step > 1 )
1632 {
1633 LINE l( cur );
1634
1635 snew.Clear();
1636 snew.Append( a.A );
1637 snew.Append( a.B + ( a.A - a.B ).Resize( current ) );
1638 snew.Append( b.A + ( b.B - b.A ).Resize( current ) );
1639 snew.Append( b.B );
1640
1641 step /= 2;
1642
1643 l.SetShape(snew);
1644
1645 if( aNode->CheckColliding(&l) )
1646 current -= step;
1647 else if ( current + step >= initial )
1648 current = initial;
1649 else
1650 current += step;
1651
1652 //dbg->AddSegment ( SEG( center.A , a.LineProject( center.A + gr ) ), 3 );
1653 //dbg->AddSegment ( SEG( center.A , center.A + guideA ), 3 );
1654 //dbg->AddSegment ( SEG( center.B , center.B + guideB ), 4 );
1655
1656 if ( current == initial )
1657 break;
1658 }
1659
1660 //dbg->AddLine ( snew, 3, 100000 );
1661
1662 out = std::move( snew );
1663 return true;
1664}
1665
1666void Tighten( NODE *aNode, const SHAPE_LINE_CHAIN& aOldLine, const LINE& aNewLine,
1667 LINE& aOptimized )
1668{
1669 LINE tmp;
1670
1671 if( aNewLine.SegmentCount() < 3 )
1672 return;
1673
1674 SHAPE_LINE_CHAIN current ( aNewLine.CLine() );
1675
1676 for( int step = 0; step < 3; step++ )
1677 {
1678 current.Simplify2();
1679
1680 for( int i = 0; i <= current.SegmentCount() - 3; i++ )
1681 {
1682 SHAPE_LINE_CHAIN l_in, l_out;
1683
1684 l_in = current.Slice( i, i + 3 );
1685
1686 for( int dir = 0; dir <= 1; dir++ )
1687 {
1688 if( tightenSegment( dir ? true : false, aNode, aNewLine, l_in, l_out ) )
1689 {
1690 SHAPE_LINE_CHAIN opt = current;
1691 opt.Replace( i, i + 3, l_out );
1692 long long int optArea = std::abs( shovedArea( aOldLine, opt ) );
1693 long long int prevArea = std::abs( shovedArea( aOldLine, current ) );
1694
1695 if( optArea < prevArea )
1696 current = std::move( opt );
1697
1698 break;
1699 }
1700 }
1701 }
1702 }
1703
1704 aOptimized = LINE( aNewLine, current );
1705
1706 //auto dbg = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
1707 //dbg->AddLine ( current, 4, 100000 );
1708}
1709
1710
1711}
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.
AngleType
Represent kind of angle formed by vectors heading in two DIRECTION_45s.
Definition direction45.h:78
bool IsDiagonal() const
Returns true if the direction is diagonal (e.g.
CORNER_MODE
Corner modes.
Definition direction45.h:67
@ ROUNDED_90
H/V with filleted corners.
Definition direction45.h:71
@ MITERED_90
H/V only (90-degree corners)
Definition direction45.h:70
bool IsObtuse(const DIRECTION_45 &aOther) const
bool IsHorizontal() const
Definition eda_angle.h:142
bool Matches(const T v) const
Definition minoptmax.h:44
bool Check(int aVertex1, int aVertex2, const LINE *aOriginLine, const SHAPE_LINE_CHAIN &aCurrentPath, const SHAPE_LINE_CHAIN &aReplacement) override
virtual bool Check(int aVertex1, int aVertex2, const LINE *aOriginLine, const SHAPE_LINE_CHAIN &aCurrentPath, const SHAPE_LINE_CHAIN &aReplacement) override
void Replace(const LINE &aOldLine, const LINE &aNewLine)
void Remove(const LINE &aLine)
void Add(const LINE &aLine)
static int CornerCost(const SEG &aA, const SEG &aB)
bool IsBetter(const COST_ESTIMATOR &aOther, double aLengthTolerance, double aCornerTollerace) const
Basic class for a differential pair.
const SHAPE_LINE_CHAIN & CN() const
double CoupledLength() const
const DP_DIMENSIONS & Dimensions() const
void SetShape(const SHAPE_LINE_CHAIN &aP, const SHAPE_LINE_CHAIN &aN, bool aSwapLanes=false)
const SHAPE_LINE_CHAIN & CP() const
const DP_GAP_CONSTRAINT GapConstraint() 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
const PNS_LAYER_RANGE & Layers() const
Definition pns_item.h:212
virtual NET_HANDLE Net() const
Definition pns_item.h:210
PnsKind Kind() const
Return the type (kind) of the item.
Definition pns_item.h:173
virtual int Layer() const
Definition pns_item.h:216
bool Collide(const ITEM *aHead, const NODE *aNode, int aLayer, COLLISION_SEARCH_CONTEXT *aCtx=nullptr) const
Check for a collision (clearance violation) with between us and item aOther.
Definition pns_item.cpp:305
bool OfKind(int aKindMask) const
Definition pns_item.h:181
A 2D point on a given set of layers and belonging to a certain net, that links together a number of b...
Definition pns_joint.h:43
const std::vector< ITEM * > & LinkList() const
Definition pns_joint.h:303
bool Check(int aVertex1, int aVertex2, const LINE *aOriginLine, const SHAPE_LINE_CHAIN &aCurrentPath, const SHAPE_LINE_CHAIN &aReplacement) override
Represents a track on a PCB, connecting two non-trivial joints (that is, vias, pads,...
Definition pns_line.h:62
const VECTOR2I & CPoint(int aIdx) const
Definition pns_line.h:154
int ArcCount() const
Definition pns_line.h:150
void SetShape(const SHAPE_LINE_CHAIN &aLine)
Return the shape of the line.
Definition pns_line.h:135
const SHAPE_LINE_CHAIN & CLine() const
Definition pns_line.h:146
const VECTOR2I & CLastPoint() const
Definition pns_line.h:155
int CountCorners(int aAngles) const
Definition pns_line.cpp:218
SHAPE_LINE_CHAIN & Line()
Definition pns_line.h:145
int SegmentCount() const
Definition pns_line.h:148
int PointCount() const
Definition pns_line.h:149
bool EndsWithVia() const
Definition pns_line.h:199
const SEG CSegment(int aIdx) const
Set line width.
Definition pns_line.h:156
int Width() const
Return true if the line is geometrically identical as line aOther.
Definition pns_line.h:166
Keep the router "world" - i.e.
Definition pns_node.h:243
OPT_OBSTACLE CheckColliding(const ITEM *aItem, int aKindMask=ITEM::ANY_T)
Check if the item collides with anything else in the world, and if found, returns the obstacle.
Definition pns_node.cpp:492
bool Check(int aVertex1, int aVertex2, const LINE *aOriginLine, const SHAPE_LINE_CHAIN &aCurrentPath, const SHAPE_LINE_CHAIN &aReplacement) override
std::pair< int, int > m_restrictedVertexRange
std::vector< OPT_CONSTRAINT * > m_constraints
~OPTIMIZER()
A quick shortcut to optimize a line without creating and setting up an optimizer.
bool mergeColinear(LINE *aLine)
void cacheAdd(ITEM *aItem, bool aIsStatic)
void removeCachedSegments(LINE *aLine, int aStartVertex=0, int aEndVertex=-1)
bool m_restrictAreaIsStrict
bool dragFixCorners(LINE *aLine)
BREAKOUT_LIST computeBreakouts(int aWidth, const ITEM *aItem, bool aPermitDiagonal) const
bool fanoutCleanup(LINE *aLine)
std::vector< SHAPE_LINE_CHAIN > BREAKOUT_LIST
bool mergeFull(LINE *aLine)
bool mergeStep(LINE *aLine, SHAPE_LINE_CHAIN &aCurrentLine, int step)
bool mergeDpStep(DIFF_PAIR *aPair, bool aTryP, int step)
void CacheRemove(ITEM *aItem)
bool mergeObtuse(LINE *aLine)
bool checkConstraints(int aVertex1, int aVertex2, LINE *aOriginLine, const SHAPE_LINE_CHAIN &aCurrentPath, const SHAPE_LINE_CHAIN &aReplacement)
OPTIMIZER(NODE *aWorld)
bool checkColliding(ITEM *aItem, bool aUpdateCache=true)
std::unordered_map< ITEM *, CACHED_ITEM > m_cacheTags
bool runSmartPads(LINE *aLine)
bool mergeDpSegments(DIFF_PAIR *aPair)
int smartPadsSingle(LINE *aLine, ITEM *aPad, bool aEnd, int aEndVertex)
bool dragFixCorner(LINE *aLine, int aVIdx)
ITEM * findPadOrVia(int aLayer, NET_HANDLE aNet, const VECTOR2I &aP) const
BREAKOUT_LIST rectBreakouts(int aWidth, const SHAPE *aShape, bool aPermitDiagonal) const
BREAKOUT_LIST customBreakouts(int aWidth, const ITEM *aItem, bool aPermitDiagonal) const
BREAKOUT_LIST circleBreakouts(int aWidth, const SHAPE *aShape, bool aPermitDiagonal) const
VECTOR2I m_preservedVertex
static bool Optimize(LINE *aLine, int aEffortLevel, NODE *aWorld, const VECTOR2I &aV=VECTOR2I(0, 0))
void addConstraint(OPT_CONSTRAINT *aConstraint)
void ClearCache(bool aStaticOnly=false)
@ LIMIT_CORNER_COUNT
Do not attempt to optimize if the resulting line's corner count is outside the predefined range.
@ SMART_PADS
Reroute pad exits.
@ FANOUT_CLEANUP
Simplify pad-pad and pad-via connections if possible.
@ MERGE_SEGMENTS
Reduce corner cost iteratively.
@ MERGE_COLINEAR
Merge co-linear segments.
@ MERGE_OBTUSE
Reduce corner cost by merging obtuse segments.
@ REQUIRE_OBTUSE_ANGLES
Try to prevent 90-degree or acute corners in a drag.
SHAPE_INDEX_LIST< ITEM * > m_cache
bool Check(int aVertex1, int aVertex2, const LINE *aOriginLine, const SHAPE_LINE_CHAIN &aCurrentPath, const SHAPE_LINE_CHAIN &aReplacement) override
virtual bool Check(int aVertex1, int aVertex2, const LINE *aOriginLine, const SHAPE_LINE_CHAIN &aCurrentPath, const SHAPE_LINE_CHAIN &aReplacement) override
ROUTING_SETTINGS & Settings()
Definition pns_router.h:228
static ROUTER * GetInstance()
DIRECTION_45::CORNER_MODE GetCornerMode() const
VECTOR2I Offset() const
Definition pns_solid.h:135
const SHAPE * Shape(int aLayer) const override
Return the geometrical shape of the item.
Definition pns_solid.h:107
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
ecoord SquaredDistance(const SEG &aSeg) const
Definition seg.cpp:76
VECTOR2I::extended_type ecoord
Definition seg.h:40
VECTOR2I B
Definition seg.h:46
int Index() const
Return the index of this segment in its parent shape (applicable only to non-local segments).
Definition seg.h:357
int Length() const
Return the length (this).
Definition seg.h:339
bool ApproxParallel(const SEG &aSeg, int aDistanceThreshold=1) const
Definition seg.cpp:814
bool Collinear(const SEG &aSeg) const
Check if segment aSeg lies on the same line as (this).
Definition seg.h:282
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
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
EDA_ANGLE Angle(const SEG &aOther) const
Determine the smallest angle between two segments.
Definition seg.cpp:107
SHAPE_TYPE Type() const
Return the type of the shape.
Definition shape.h:96
int GetRadius() const
const VECTOR2I GetCenter() const
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.
bool IsPtOnArc(size_t aPtIndex) const
bool IsClosed() const override
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int Intersect(const SEG &aSeg, INTERSECTIONS &aIp) const
Find all intersection points between our line chain and the segment aSeg.
int PointCount() const
Return the number of points (vertices) in this line chain.
void Replace(int aStartIndex, int aEndIndex, const VECTOR2I &aP)
Replace points with indices in range [start_index, end_index] with a single point aP.
void Clear()
Remove all points from the line chain.
double Area(bool aAbsolute=true) const
Return the area of this chain.
SHAPE_LINE_CHAIN & Simplify2(bool aRemoveColinear=true)
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 SHAPE_LINE_CHAIN Slice(int aStartIndex, int aEndIndex) const
Return a subset of this line chain containing the [start_index, end_index] range of points.
int SegmentCount() const
Return the number of segments in this line chain.
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
void Remove(int aStartIndex, int aEndIndex)
Remove the range of points [start_index, end_index] from 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
std::vector< INTERSECTION > INTERSECTIONS
long long int Length() const
Return length of the line chain in Euclidean metric.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
const VECTOR2I & GetPosition() const
Definition shape_rect.h:165
const VECTOR2I GetSize() const
Definition shape_rect.h:173
Represent a simple polygon consisting of a zero-thickness closed chain of connected line segments.
const SHAPE_LINE_CHAIN & Vertices() const
Return the list of vertices defining this simple polygon.
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
An abstract shape on 2D plane.
Definition shape.h:124
virtual bool Collide(const VECTOR2I &aP, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const
Check if the boundary of shape (this) lies closer to the point aP than aClearance,...
Definition shape.h:179
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:381
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:422
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:424
static constexpr EDA_ANGLE ANGLE_45
Definition eda_angle.h:423
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:428
Push and Shove diff pair dimensions (gap) settings dialog.
bool tightenSegment(bool dir, NODE *aNode, const LINE &cur, const SHAPE_LINE_CHAIN &in, SHAPE_LINE_CHAIN &out)
SHAPE_RECT ApproximateSegmentAsRect(const SHAPE_SEGMENT &aSeg)
void * NET_HANDLE
Definition pns_item.h:55
int findCoupledVertices(const VECTOR2I &aVertex, const SEG &aOrigSeg, const SHAPE_LINE_CHAIN &aCoupled, DIFF_PAIR *aPair, int *aIndices)
bool coupledBypass(NODE *aNode, DIFF_PAIR *aPair, bool aRefIsP, const SHAPE_LINE_CHAIN &aRef, const SHAPE_LINE_CHAIN &aRefBypass, const SHAPE_LINE_CHAIN &aCoupled, SHAPE_LINE_CHAIN &aNewCoupled)
void Tighten(NODE *aNode, const SHAPE_LINE_CHAIN &aOldLine, const LINE &aNewLine, LINE &aOptimized)
bool verifyDpBypass(NODE *aNode, DIFF_PAIR *aPair, bool aRefIsP, const SHAPE_LINE_CHAIN &aNewRef, const SHAPE_LINE_CHAIN &aNewCoupled)
bool checkDpColliding(NODE *aNode, DIFF_PAIR *aPair, bool aIsP, const SHAPE_LINE_CHAIN &aPath)
static bool pointInside2(const SHAPE_LINE_CHAIN &aL, const VECTOR2I &aP)
Determine if a point is located within a given polygon.
static int64_t shovedArea(const SHAPE_LINE_CHAIN &aOld, const SHAPE_LINE_CHAIN &aNew)
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:411
@ 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
bool operator()(ITEM *aOtherItem)
CACHE_VISITOR(const ITEM *aOurItem, NODE *aNode, int aMask)
std::string path
VECTOR3I v1(5, 5, 5)
VECTOR2I center
wxString result
Test unit parsing edge cases and error handling.
int delta
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition trigo.cpp:225
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:55
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683