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 along
19 * with this program. If not, see <http://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
305
317static bool pointInside2( const SHAPE_LINE_CHAIN& aL, const VECTOR2I& aP )
318{
319 if( !aL.IsClosed() || aL.SegmentCount() < 3 )
320 return false;
321
322 int result = 0;
323 size_t cnt = aL.PointCount();
324
325 VECTOR2I ip = aL.CPoint( 0 );
326
327 for( size_t i = 1; i <= cnt; ++i )
328 {
329 VECTOR2I ipNext = ( i == cnt ? aL.CPoint( 0 ) : aL.CPoint( i ) );
330
331 if( ipNext.y == aP.y )
332 {
333 if( ( ipNext.x == aP.x )
334 || ( ip.y == aP.y && ( ( ipNext.x > aP.x ) == ( ip.x < aP.x ) ) ) )
335 return true; // pt on polyground boundary
336 }
337
338 if( ( ip.y < aP.y ) != ( ipNext.y < aP.y ) )
339 {
340 if( ip.x >=aP.x )
341 {
342 if( ipNext.x >aP.x )
343 {
344 result = 1 - result;
345 }
346 else
347 {
348 double d = static_cast<double>( ip.x - aP.x ) *
349 static_cast<double>( ipNext.y - aP.y ) -
350 static_cast<double>( ipNext.x - aP.x ) *
351 static_cast<double>( ip.y - aP.y );
352
353 if( !d )
354 return true; // pt on polyground boundary
355
356 if( ( d > 0 ) == ( ipNext.y > ip.y ) )
357 result = 1 - result;
358 }
359 }
360 else
361 {
362 if( ipNext.x >aP.x )
363 {
364 double d = ( (double) ip.x - aP.x ) * ( (double) ipNext.y - aP.y )
365 - ( (double) ipNext.x - aP.x ) * ( (double) ip.y - aP.y );
366
367 if( !d )
368 return true; // pt on polyground boundary
369
370 if( ( d > 0 ) == ( ipNext.y > ip.y ) )
371 result = 1 - result;
372 }
373 }
374 }
375
376 ip = ipNext;
377 }
378
379 return result > 0;
380}
381
382
383bool KEEP_TOPOLOGY_CONSTRAINT::Check( int aVertex1, int aVertex2, const LINE* aOriginLine,
384 const SHAPE_LINE_CHAIN& aCurrentPath,
385 const SHAPE_LINE_CHAIN& aReplacement )
386{
387 SHAPE_LINE_CHAIN encPoly = aOriginLine->CLine().Slice( aVertex1, aVertex2 );
388
389 // fixme: this is a remarkably shitty implementation...
390 encPoly.Append( aReplacement.Reverse() );
391 encPoly.SetClosed( true );
392
393 BOX2I bb = encPoly.BBox();
394 std::vector<JOINT*> joints;
395
396 int cnt = m_world->QueryJoints( bb, joints, aOriginLine->Layers(), ITEM::SOLID_T );
397
398 if( !cnt )
399 return true;
400
401 for( JOINT* j : joints )
402 {
403 if( j->Net() == aOriginLine->Net() )
404 continue;
405
406 if( pointInside2( encPoly, j->Pos() ) )
407 {
408 bool falsePositive = false;
409
410 for( int k = 0; k < encPoly.PointCount(); k++ )
411 {
412 if( encPoly.CPoint(k) == j->Pos() )
413 {
414 falsePositive = true;
415 break;
416 }
417 }
418
419 if( !falsePositive )
420 {
421 //dbg->AddPoint(j->Pos(), 5);
422 return false;
423 }
424 }
425 }
426
427 return true;
428}
429
430
431bool OPTIMIZER::checkColliding( ITEM* aItem, bool aUpdateCache )
432{
434
435 return static_cast<bool>( m_world->CheckColliding( aItem ) );
436}
437
438
440{
441 m_constraints.push_back( aConstraint );
442}
443
444
445bool OPTIMIZER::checkConstraints( int aVertex1, int aVertex2, LINE* aOriginLine,
446 const SHAPE_LINE_CHAIN& aCurrentPath,
447 const SHAPE_LINE_CHAIN& aReplacement )
448{
449 for( OPT_CONSTRAINT* c : m_constraints )
450 {
451 if( !c->Check( aVertex1, aVertex2, aOriginLine, aCurrentPath, aReplacement ) )
452 return false;
453 }
454
455 return true;
456}
457
458
459bool OPTIMIZER::checkColliding( LINE* aLine, const SHAPE_LINE_CHAIN& aOptPath )
460{
461 LINE tmp( *aLine, aOptPath );
462
463 return checkColliding( &tmp );
464}
465
466
468{
469 SHAPE_LINE_CHAIN& line = aLine->Line();
470
471 int step = line.PointCount() - 3;
472 int segs_pre = line.SegmentCount();
473
474 if( step < 0 )
475 return false;
476
477 SHAPE_LINE_CHAIN current_path( line );
478
479 while( true )
480 {
481 int n_segs = current_path.SegmentCount();
482 int max_step = n_segs - 2;
483
484 if( step > max_step )
485 step = max_step;
486
487 if( step < 2 )
488 {
489 line = std::move( current_path );
490 return line.SegmentCount() < segs_pre;
491 }
492
493 bool found_anything = false;
494
495 for( int n = 0; n < n_segs - step; n++ )
496 {
497 const SEG s1 = current_path.CSegment( n );
498 const SEG s2 = current_path.CSegment( n + step );
499 SEG s1opt, s2opt;
500
501 if( DIRECTION_45( s1 ).IsObtuse( DIRECTION_45( s2 ) ) )
502 {
503 VECTOR2I ip = *s1.IntersectLines( s2 );
504
505 s1opt = SEG( s1.A, ip );
506 s2opt = SEG( ip, s2.B );
507
508 if( DIRECTION_45( s1opt ).IsObtuse( DIRECTION_45( s2opt ) ) )
509 {
510 SHAPE_LINE_CHAIN opt_path;
511 opt_path.Append( s1opt.A );
512 opt_path.Append( s1opt.B );
513 opt_path.Append( s2opt.B );
514
515 LINE opt_track( *aLine, opt_path );
516
517 if( !checkColliding( &opt_track ) )
518 {
519 current_path.Replace( s1.Index() + 1, s2.Index(), ip );
520
521 // removeCachedSegments(aLine, s1.Index(), s2.Index());
522 n_segs = current_path.SegmentCount();
523 found_anything = true;
524 break;
525 }
526 }
527 }
528 }
529
530 if( !found_anything )
531 {
532 if( step <= 2 )
533 {
534 line = std::move( current_path );
535 return line.SegmentCount() < segs_pre;
536 }
537
538 step--;
539 }
540 }
541}
542
543
545{
546 SHAPE_LINE_CHAIN& line = aLine->Line();
547 int step = line.SegmentCount() - 1;
548
549 int segs_pre = line.SegmentCount();
550
551 line.Simplify2();
552
553 if( step < 0 )
554 return false;
555
556 SHAPE_LINE_CHAIN current_path( line );
557
558 while( true )
559 {
560 int n_segs = current_path.SegmentCount();
561 int max_step = n_segs - 2;
562
563 if( step > max_step )
564 step = max_step;
565
566 if( step < 1 )
567 break;
568
569 bool found_anything = mergeStep( aLine, current_path, step );
570
571 if( !found_anything )
572 step--;
573
574 if( !step )
575 break;
576 }
577
578 aLine->SetShape( current_path );
579
580 return current_path.SegmentCount() < segs_pre;
581}
582
583
585{
586 SHAPE_LINE_CHAIN& line = aLine->Line();
587
588 int nSegs = line.SegmentCount();
589
590 for( int segIdx = 0; segIdx < line.SegmentCount() - 1; ++segIdx )
591 {
592 SEG s1 = line.CSegment( segIdx );
593 SEG s2 = line.CSegment( segIdx + 1 );
594
595 // Skip zero-length segs caused by abutting arcs
596 if( s1.SquaredLength() == 0 || s2.SquaredLength() == 0 )
597 continue;
598
599 if( s1.Collinear( s2 ) && !line.IsPtOnArc( segIdx + 1 ) )
600 {
601 line.Remove( segIdx + 1 );
602 }
603 }
604
605 return line.SegmentCount() < nSegs;
606}
607
608
609bool OPTIMIZER::Optimize( const LINE* aLine, LINE* aResult, LINE* aRoot )
610{
611 if( !aResult )
612 return false;
613
614 *aResult = *aLine;
615 aResult->ClearLinks();
616
617 bool hasArcs = aLine->ArcCount();
618 bool rv = false;
619
620 if( (m_effortLevel & LIMIT_CORNER_COUNT) && aRoot )
621 {
622 const int angleMask = DIRECTION_45::ANG_OBTUSE;
623 int rootObtuseCorners = aRoot->CountCorners( angleMask );
624 auto c = new CORNER_COUNT_LIMIT_CONSTRAINT( m_world, rootObtuseCorners,
625 aLine->SegmentCount(), angleMask );
626 //PNS_DBG( dbg, Message,
627 // wxString::Format( "opt limit-corner-count root %d maxc %d mask %x",
628 // rootObtuseCorners, aLine->SegmentCount(), angleMask ) );
629
630 addConstraint( c );
631 }
632
634 {
636 addConstraint( c );
637 }
638
640 {
643 addConstraint( c );
644 }
645
647 {
650 //PNS_DBG( dbg, AddShape, &r, YELLOW, 0, wxT( "area-constraint" ) );
651 addConstraint( c );
652 }
653
655 {
656 auto c = new KEEP_TOPOLOGY_CONSTRAINT( m_world );
657 addConstraint( c );
658 }
659
660 // TODO: Fix for arcs
661 if( !hasArcs && m_effortLevel & MERGE_SEGMENTS )
662 rv |= mergeFull( aResult );
663
664 // TODO: Fix for arcs
665 if( !hasArcs && m_effortLevel & MERGE_OBTUSE )
666 rv |= mergeObtuse( aResult );
667
669 rv |= mergeColinear( aResult );
670
671 // TODO: Fix for arcs
672 if( !hasArcs && m_effortLevel & SMART_PADS )
673 rv |= runSmartPads( aResult );
674
675 // TODO: Fix for arcs
676 if( !hasArcs && m_effortLevel & FANOUT_CLEANUP )
677 rv |= fanoutCleanup( aResult );
678
679 return rv;
680}
681
682
683bool OPTIMIZER::mergeStep( LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, int step )
684{
685 int n_segs = aCurrentPath.SegmentCount();
686
687 int cost_orig = COST_ESTIMATOR::CornerCost( aCurrentPath );
688
689 if( aLine->SegmentCount() < 2 )
690 return false;
691
693 bool is90mode = cornerMode == DIRECTION_45::MITERED_90 || cornerMode == DIRECTION_45::ROUNDED_90;
694
695 DIRECTION_45 orig_start( aLine->CSegment( 0 ), is90mode );
696 DIRECTION_45 orig_end( aLine->CSegment( -1 ), is90mode );
697
698
699 for( int n = 0; n < n_segs - step; n++ )
700 {
701 // Do not attempt to merge false segments that are part of an arc
702 if( aCurrentPath.IsArcSegment( n )
703 || aCurrentPath.IsArcSegment( static_cast<std::size_t>( n ) + step ) )
704 {
705 continue;
706 }
707
708 const SEG s1 = aCurrentPath.CSegment( n );
709 const SEG s2 = aCurrentPath.CSegment( n + step );
710
712 SHAPE_LINE_CHAIN* picked = nullptr;
713 int cost[2];
714
715 for( int i = 0; i < 2; i++ )
716 {
717 SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace( s1.A, s2.B, i, cornerMode );
718 cost[i] = INT_MAX;
719
720 bool ok = false;
721
722 if( !checkColliding( aLine, bypass ) )
723 {
724 ok = checkConstraints ( n, n + step + 1, aLine, aCurrentPath, bypass );
725 }
726
727 if( ok )
728 {
729 path[i] = aCurrentPath;
730 path[i].Replace( s1.Index(), s2.Index(), bypass );
731 path[i].Simplify2();
732 cost[i] = COST_ESTIMATOR::CornerCost( path[i] );
733 }
734 }
735
736 if( cost[0] < cost_orig && cost[0] < cost[1] )
737 picked = &path[0];
738 else if( cost[1] < cost_orig )
739 picked = &path[1];
740
741 if( picked )
742 {
743 n_segs = aCurrentPath.SegmentCount();
744 aCurrentPath = *picked;
745 return true;
746 }
747 }
748
749 return false;
750}
751
752
754 bool aPermitDiagonal ) const
755{
756 BREAKOUT_LIST breakouts;
757
759 {
760 const SHAPE_CIRCLE* cir = static_cast<const SHAPE_CIRCLE*>( aShape );
762 VECTOR2I p0 = cir->GetCenter();
763 VECTOR2I v0( cir->GetRadius() * M_SQRT2, 0 );
764
765 RotatePoint( v0, -angle );
766
767 l.Append( p0 );
768 l.Append( p0 + v0 );
769 breakouts.push_back( l );
770 }
771
772 return breakouts;
773}
774
775
777 bool aPermitDiagonal ) const
778{
779 BREAKOUT_LIST breakouts;
780 const SHAPE_SIMPLE* convex = static_cast<const SHAPE_SIMPLE*>( aItem->Shape( -1 ) );
781
782 BOX2I bbox = convex->BBox( 0 );
783 VECTOR2I p0 = static_cast<const SOLID*>( aItem )->Pos();
784 // must be large enough to guarantee intersecting the convex polygon
785 int length = std::max( bbox.GetWidth(), bbox.GetHeight() ) / 2 + 5;
786 EDA_ANGLE increment = ( aPermitDiagonal ? ANGLE_45 : ANGLE_90 );
787
788 for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += increment )
789 {
791 VECTOR2I v0( p0 + VECTOR2I( length, 0 ) );
792 RotatePoint( v0, p0, -angle );
793
795 int n = convex->Vertices().Intersect( SEG( p0, v0 ), intersections );
796
797 // if n == 1 intersected a segment
798 // if n == 2 intersected the common point of 2 segments
799 // n == 0 can not happen I think, but...
800 if( n > 0 )
801 {
802 l.Append( p0 );
803
804 // for a breakout distance relative to the distance between
805 // center and polygon edge
806 //l.Append( intersections[0].p + (v0 - p0).Resize( (intersections[0].p - p0).EuclideanNorm() * 0.4 ) );
807
808 // for an absolute breakout distance, e.g. 0.1 mm
809 //l.Append( intersections[0].p + (v0 - p0).Resize( 100000 ) );
810
811 // for the breakout right on the polygon edge
812 l.Append( intersections[0].p );
813
814 breakouts.push_back( l );
815 }
816 }
817
818 return breakouts;
819}
820
821
823 bool aPermitDiagonal ) const
824{
825 const SHAPE_RECT* rect = static_cast<const SHAPE_RECT*>(aShape);
826 VECTOR2I s = rect->GetSize();
827 VECTOR2I c = rect->GetPosition() + VECTOR2I( s.x / 2, s.y / 2 );
828
829 BREAKOUT_LIST breakouts;
830 breakouts.reserve( 12 );
831
832 VECTOR2I d_offset;
833
834 d_offset.x = ( s.x > s.y ) ? ( s.x - s.y ) / 2 : 0;
835 d_offset.y = ( s.x < s.y ) ? ( s.y - s.x ) / 2 : 0;
836
837 VECTOR2I d_vert = VECTOR2I( 0, s.y / 2 + aWidth );
838 VECTOR2I d_horiz = VECTOR2I( s.x / 2 + aWidth, 0 );
839
840 breakouts.emplace_back( SHAPE_LINE_CHAIN( { c, c + d_horiz } ) );
841 breakouts.emplace_back( SHAPE_LINE_CHAIN( { c, c - d_horiz } ) );
842 breakouts.emplace_back( SHAPE_LINE_CHAIN( { c, c + d_vert } ) );
843 breakouts.emplace_back( SHAPE_LINE_CHAIN( { c, c - d_vert } ) );
844
845 if( aPermitDiagonal )
846 {
847 int l = aWidth + std::min( s.x, s.y ) / 2;
848
849 if( s.x >= s.y )
850 {
851 breakouts.emplace_back(
852 SHAPE_LINE_CHAIN( { c, c + d_offset, c + d_offset + VECTOR2I( l, l ) } ) );
853 breakouts.emplace_back(
854 SHAPE_LINE_CHAIN( { c, c + d_offset, c + d_offset - VECTOR2I( -l, l ) } ) );
855 breakouts.emplace_back(
856 SHAPE_LINE_CHAIN( { c, c - d_offset, c - d_offset + VECTOR2I( -l, l ) } ) );
857 breakouts.emplace_back(
858 SHAPE_LINE_CHAIN( { c, c - d_offset, c - d_offset - VECTOR2I( l, l ) } ) );
859 }
860 else
861 {
862 // fixme: this could be done more efficiently
863 breakouts.emplace_back(
864 SHAPE_LINE_CHAIN( { c, c + d_offset, c + d_offset + VECTOR2I( l, l ) } ) );
865 breakouts.emplace_back(
866 SHAPE_LINE_CHAIN( { c, c - d_offset, c - d_offset - VECTOR2I( -l, l ) } ) );
867 breakouts.emplace_back(
868 SHAPE_LINE_CHAIN( { c, c + d_offset, c + d_offset + VECTOR2I( -l, l ) } ) );
869 breakouts.emplace_back(
870 SHAPE_LINE_CHAIN( { c, c - d_offset, c - d_offset - VECTOR2I( l, l ) } ) );
871 }
872 }
873
874 return breakouts;
875}
876
877
879 bool aPermitDiagonal ) const
880{
881 switch( aItem->Kind() )
882 {
883 case ITEM::VIA_T:
884 {
885 const VIA* via = static_cast<const VIA*>( aItem );
886 // TODO(JE) padstacks -- computeBreakouts needs to have a layer argument
887 return circleBreakouts( aWidth, via->Shape( 0 ), aPermitDiagonal );
888 }
889
890 case ITEM::SOLID_T:
891 {
892 const SHAPE* shape = aItem->Shape( -1 );
893
894 switch( shape->Type() )
895 {
896 case SH_RECT:
897 return rectBreakouts( aWidth, shape, aPermitDiagonal );
898
899 case SH_SEGMENT:
900 {
901 const SHAPE_SEGMENT* seg = static_cast<const SHAPE_SEGMENT*> (shape);
902 const SHAPE_RECT rect = ApproximateSegmentAsRect ( *seg );
903 return rectBreakouts( aWidth, &rect, aPermitDiagonal );
904 }
905
906 case SH_CIRCLE:
907 return circleBreakouts( aWidth, shape, aPermitDiagonal );
908
909 case SH_SIMPLE:
910 return customBreakouts( aWidth, aItem, aPermitDiagonal );
911
912 default:
913 break;
914 }
915
916 break;
917 }
918
919 default:
920 break;
921 }
922
923 return BREAKOUT_LIST();
924}
925
926
927ITEM* OPTIMIZER::findPadOrVia( int aLayer, NET_HANDLE aNet, const VECTOR2I& aP ) const
928{
929 const JOINT* jt = m_world->FindJoint( aP, aLayer, aNet );
930
931 if( !jt )
932 return nullptr;
933
934 for( ITEM* item : jt->LinkList() )
935 {
936 if( item->OfKind( ITEM::VIA_T | ITEM::SOLID_T ) )
937 return item;
938 }
939
940 return nullptr;
941}
942
943
944int OPTIMIZER::smartPadsSingle( LINE* aLine, ITEM* aPad, bool aEnd, int aEndVertex )
945{
946 DIRECTION_45 dir;
947
948 const int ForbiddenAngles = DIRECTION_45::ANG_ACUTE | DIRECTION_45::ANG_RIGHT |
950
951 typedef std::tuple<int, long long int, SHAPE_LINE_CHAIN> RtVariant;
952 std::vector<RtVariant> variants;
953
954 SOLID* solid = dyn_cast<SOLID*>( aPad );
955
956 // don't do optimized connections for offset pads
957 if( solid && solid->Offset() != VECTOR2I( 0, 0 ) )
958 return -1;
959
960 // don't do optimization on vias, they are always round at the moment and the optimizer
961 // will possibly mess up an intended via exit posture
962 if( aPad->Kind() == ITEM::VIA_T )
963 return -1;
964
965 BREAKOUT_LIST breakouts = computeBreakouts( aLine->Width(), aPad, true );
966 SHAPE_LINE_CHAIN line = ( aEnd ? aLine->CLine().Reverse() : aLine->CLine() );
967 int p_end = std::min( aEndVertex, std::min( 3, line.PointCount() - 1 ) );
968
969 // Start at 1 to find a potentially better breakout (0 is the pad connection)
970 for( int p = 1; p <= p_end; p++ )
971 {
972 // If the line is contained inside the pad, don't optimize
973 if( solid && solid->Shape( -1 ) && !solid->Shape( -1 )->Collide(
974 SEG( line.CPoint( 0 ), line.CPoint( p ) ), aLine->Width() / 2 ) )
975 {
976 continue;
977 }
978
979 for( SHAPE_LINE_CHAIN & breakout : breakouts )
980 {
981 for( int diag = 0; diag < 2; diag++ )
982 {
985 breakout.CLastPoint(), line.CPoint( p ), diag == 0 );
986
987 DIRECTION_45 dir_bkout( breakout.CSegment( -1 ) );
988
989 if( !connect.SegmentCount() )
990 continue;
991
992 int ang1 = dir_bkout.Angle( DIRECTION_45( connect.CSegment( 0 ) ) );
993
994 if( ang1 & ForbiddenAngles )
995 continue;
996
997 if( breakout.Length() > line.Length() )
998 continue;
999
1000 v = breakout;
1001 v.Append( connect );
1002
1003 for( int i = p + 1; i < line.PointCount(); i++ )
1004 v.Append( line.CPoint( i ) );
1005
1006 LINE tmp( *aLine, v );
1007 int cc = tmp.CountCorners( ForbiddenAngles );
1008
1009 if( cc == 0 )
1010 {
1011 RtVariant vp;
1012 std::get<0>( vp ) = p;
1013 std::get<1>( vp ) = breakout.Length();
1014 std::get<2>( vp ) = aEnd ? v.Reverse() : v;
1015 std::get<2>( vp ).Simplify2();
1016 variants.push_back( std::move( vp ) );
1017 }
1018 }
1019 }
1020 }
1021
1022 // We attempt to minimize the corner cost (minimizes the segments and types of corners)
1023 // but given two, equally valid costs, we want to pick the longer pad exit. The logic
1024 // here is that if the pad is oblong, the track should not exit the shorter side and parallel
1025 // the pad but should follow the pad's preferential direction before exiting.
1026 // The baseline guess is to start with the existing line the user has drawn.
1027 int min_cost = COST_ESTIMATOR::CornerCost( *aLine );
1028 long long int max_length = 0;
1029 bool found = false;
1030 int p_best = -1;
1031 SHAPE_LINE_CHAIN l_best;
1032
1033 for( RtVariant& vp : variants )
1034 {
1035 LINE tmp( *aLine, std::get<2>( vp ) );
1036 int cost = COST_ESTIMATOR::CornerCost( std::get<2>( vp ) );
1037 long long int len = std::get<1>( vp );
1038
1039 if( !checkColliding( &tmp ) )
1040 {
1041 if( cost < min_cost || ( cost == min_cost && len > max_length ) )
1042 {
1043 l_best = std::get<2>( vp );
1044 p_best = std::get<0>( vp );
1045 found = true;
1046
1047 if( cost <= min_cost )
1048 max_length = std::max<int>( len, max_length );
1049
1050 min_cost = std::min( cost, min_cost );
1051 }
1052 }
1053 }
1054
1055 if( found )
1056 {
1057 aLine->SetShape( l_best );
1058 return p_best;
1059 }
1060
1061 return -1;
1062}
1063
1064
1066{
1067 SHAPE_LINE_CHAIN& line = aLine->Line();
1068
1069 if( line.PointCount() < 3 )
1070 return false;
1071
1072 VECTOR2I p_start = line.CPoint( 0 ), p_end = line.CLastPoint();
1073
1074 ITEM* startPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_start );
1075 ITEM* endPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_end );
1076
1077 int vtx = -1;
1078
1079 if( startPad )
1080 vtx = smartPadsSingle( aLine, startPad, false, 3 );
1081
1082 if( endPad )
1083 smartPadsSingle( aLine, endPad, true,
1084 vtx < 0 ? line.PointCount() - 1 : line.PointCount() - 1 - vtx );
1085
1086 aLine->Line().Simplify2();
1087
1088 return true;
1089}
1090
1091
1092bool OPTIMIZER::Optimize( LINE* aLine, int aEffortLevel, NODE* aWorld, const VECTOR2I& aV )
1093{
1094 OPTIMIZER opt( aWorld );
1095
1096 opt.SetEffortLevel( aEffortLevel );
1097 opt.SetCollisionMask( -1 );
1098
1099 if( aEffortLevel & OPTIMIZER::PRESERVE_VERTEX )
1100 opt.SetPreserveVertex( aV );
1101
1102 LINE tmp( *aLine );
1103 return opt.Optimize( &tmp, aLine );
1104}
1105
1106
1108{
1109 if( aLine->PointCount() < 3 )
1110 return false;
1111
1113
1114 VECTOR2I p_start = aLine->CPoint( 0 ), p_end = aLine->CLastPoint();
1115
1116 ITEM* startPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_start );
1117 ITEM* endPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_end );
1118
1119 int thr = aLine->Width() * 10;
1120 int len = aLine->CLine().Length();
1121
1122 if( !startPad )
1123 return false;
1124
1125 bool startMatch = startPad->OfKind( ITEM::VIA_T | ITEM::SOLID_T );
1126 bool endMatch = false;
1127
1128 if(endPad)
1129 {
1130 endMatch = endPad->OfKind( ITEM::VIA_T | ITEM::SOLID_T );
1131 }
1132 else
1133 {
1134 endMatch = aLine->EndsWithVia();
1135 }
1136
1137 if( startMatch && endMatch && len < thr )
1138 {
1139 for( int i = 0; i < 2; i++ )
1140 {
1141 SHAPE_LINE_CHAIN l2 = DIRECTION_45().BuildInitialTrace( p_start, p_end, i, cornerMode );
1142 LINE repl;
1143 repl = LINE( *aLine, l2 );
1144
1145 if( !m_world->CheckColliding( &repl ) )
1146 {
1147 aLine->SetShape( repl.CLine() );
1148 return true;
1149 }
1150 }
1151 }
1152
1153 return false;
1154}
1155
1156
1157int findCoupledVertices( const VECTOR2I& aVertex, const SEG& aOrigSeg,
1158 const SHAPE_LINE_CHAIN& aCoupled, DIFF_PAIR* aPair, int* aIndices )
1159{
1160 int count = 0;
1161
1162 for ( int i = 0; i < aCoupled.SegmentCount(); i++ )
1163 {
1164 SEG s = aCoupled.CSegment( i );
1165 VECTOR2I projOverCoupled = s.LineProject ( aVertex );
1166
1167 if( s.ApproxParallel( aOrigSeg ) )
1168 {
1169 int64_t dist =
1170 int64_t{ ( ( projOverCoupled - aVertex ).EuclideanNorm() ) } - aPair->Width();
1171
1172 if( aPair->GapConstraint().Matches( dist ) )
1173 {
1174 *aIndices++ = i;
1175 count++;
1176 }
1177 }
1178 }
1179
1180 return count;
1181}
1182
1183
1184bool verifyDpBypass( NODE* aNode, DIFF_PAIR* aPair, bool aRefIsP, const SHAPE_LINE_CHAIN& aNewRef,
1185 const SHAPE_LINE_CHAIN& aNewCoupled )
1186{
1187 LINE refLine ( aRefIsP ? aPair->PLine() : aPair->NLine(), aNewRef );
1188 LINE coupledLine ( aRefIsP ? aPair->NLine() : aPair->PLine(), aNewCoupled );
1189
1190 if( refLine.Collide( &coupledLine, aNode, refLine.Layer() ) )
1191 return false;
1192
1193 if( aNode->CheckColliding ( &refLine ) )
1194 return false;
1195
1196 if( aNode->CheckColliding ( &coupledLine ) )
1197 return false;
1198
1199 return true;
1200}
1201
1202
1203bool coupledBypass( NODE* aNode, DIFF_PAIR* aPair, bool aRefIsP, const SHAPE_LINE_CHAIN& aRef,
1204 const SHAPE_LINE_CHAIN& aRefBypass, const SHAPE_LINE_CHAIN& aCoupled,
1205 SHAPE_LINE_CHAIN& aNewCoupled )
1206{
1207 int vStartIdx[1024]; // fixme: possible overflow
1208 int nStarts = findCoupledVertices( aRefBypass.CPoint( 0 ),
1209 aRefBypass.CSegment( 0 ),
1210 aCoupled, aPair, vStartIdx );
1211 DIRECTION_45 dir( aRefBypass.CSegment( 0 ) );
1212
1213 int64_t bestLength = -1;
1214 bool found = false;
1215 SHAPE_LINE_CHAIN bestBypass;
1216 int si, ei;
1217
1218 for( int i=0; i< nStarts; i++ )
1219 {
1220 for( int j = 1; j < aCoupled.PointCount() - 1; j++ )
1221 {
1222 int delta = std::abs ( vStartIdx[i] - j );
1223
1224 if( delta > 1 )
1225 {
1226 const VECTOR2I& vs = aCoupled.CPoint( vStartIdx[i] );
1227 SHAPE_LINE_CHAIN bypass = dir.BuildInitialTrace( vs, aCoupled.CPoint(j),
1228 dir.IsDiagonal() );
1229
1230 int64_t coupledLength = aPair->CoupledLength( aRef, bypass );
1231
1232 SHAPE_LINE_CHAIN newCoupled = aCoupled;
1233
1234 si = vStartIdx[i];
1235 ei = j;
1236
1237 if(si < ei)
1238 newCoupled.Replace( si, ei, bypass );
1239 else
1240 newCoupled.Replace( ei, si, bypass.Reverse() );
1241
1242 if( coupledLength > bestLength && verifyDpBypass( aNode, aPair, aRefIsP, aRef,
1243 newCoupled) )
1244 {
1245 bestBypass = std::move( newCoupled );
1246 bestLength = coupledLength;
1247 found = true;
1248 }
1249 }
1250 }
1251 }
1252
1253 if( found )
1254 aNewCoupled = std::move( bestBypass );
1255
1256 return found;
1257}
1258
1259
1260bool checkDpColliding( NODE* aNode, DIFF_PAIR* aPair, bool aIsP, const SHAPE_LINE_CHAIN& aPath )
1261{
1262 LINE tmp ( aIsP ? aPair->PLine() : aPair->NLine(), aPath );
1263
1264 return static_cast<bool>( aNode->CheckColliding( &tmp ) );
1265}
1266
1267
1268bool OPTIMIZER::mergeDpStep( DIFF_PAIR* aPair, bool aTryP, int step )
1269{
1270 int n = 1;
1271
1272 SHAPE_LINE_CHAIN currentPath = aTryP ? aPair->CP() : aPair->CN();
1273 SHAPE_LINE_CHAIN coupledPath = aTryP ? aPair->CN() : aPair->CP();
1274
1275 int n_segs = currentPath.SegmentCount() - 1;
1276
1277 int64_t clenPre = aPair->CoupledLength( currentPath, coupledPath );
1278 int64_t budget = clenPre / 10; // fixme: come up with something more intelligent here...
1279
1280 while( n < n_segs - step )
1281 {
1282 const SEG s1 = currentPath.CSegment( n );
1283 const SEG s2 = currentPath.CSegment( n + step );
1284
1285 DIRECTION_45 dir1( s1 );
1286 DIRECTION_45 dir2( s2 );
1287
1288 if( dir1.IsObtuse( dir2 ) )
1289 {
1291 dir1.IsDiagonal() );
1292 SHAPE_LINE_CHAIN newRef;
1293 SHAPE_LINE_CHAIN newCoup;
1294 int64_t deltaCoupled = -1, deltaUni = -1;
1295
1296 newRef = currentPath;
1297 newRef.Replace( s1.Index(), s2.Index(), bypass );
1298
1299 deltaUni = aPair->CoupledLength ( newRef, coupledPath ) - clenPre + budget;
1300
1301 if( coupledBypass( m_world, aPair, aTryP, newRef, bypass, coupledPath, newCoup ) )
1302 {
1303 deltaCoupled = aPair->CoupledLength( newRef, newCoup ) - clenPre + budget;
1304
1305 if( deltaCoupled >= 0 )
1306 {
1307 newRef.Simplify2();
1308 newCoup.Simplify2();
1309
1310 aPair->SetShape( newRef, newCoup, !aTryP );
1311 return true;
1312 }
1313 }
1314 else if( deltaUni >= 0 && verifyDpBypass( m_world, aPair, aTryP, newRef, coupledPath ) )
1315 {
1316 newRef.Simplify2();
1317 coupledPath.Simplify2();
1318
1319 aPair->SetShape( newRef, coupledPath, !aTryP );
1320 return true;
1321 }
1322 }
1323
1324 n++;
1325 }
1326
1327 return false;
1328}
1329
1330
1332{
1333 int step_p = aPair->CP().SegmentCount() - 2;
1334 int step_n = aPair->CN().SegmentCount() - 2;
1335
1336 while( 1 )
1337 {
1338 int n_segs_p = aPair->CP().SegmentCount();
1339 int n_segs_n = aPair->CN().SegmentCount();
1340
1341 int max_step_p = n_segs_p - 2;
1342 int max_step_n = n_segs_n - 2;
1343
1344 if( step_p > max_step_p )
1345 step_p = max_step_p;
1346
1347 if( step_n > max_step_n )
1348 step_n = max_step_n;
1349
1350 if( step_p < 1 && step_n < 1 )
1351 break;
1352
1353 bool found_anything_p = false;
1354 bool found_anything_n = false;
1355
1356 if( step_p > 1 )
1357 found_anything_p = mergeDpStep( aPair, true, step_p );
1358
1359 if( step_n > 1 )
1360 found_anything_n = mergeDpStep( aPair, false, step_n );
1361
1362 if( !found_anything_n && !found_anything_p )
1363 {
1364 step_n--;
1365 step_p--;
1366 }
1367 }
1368 return true;
1369}
1370
1371
1373{
1374 return mergeDpSegments( aPair );
1375}
1376
1377
1378static int64_t shovedArea( const SHAPE_LINE_CHAIN& aOld, const SHAPE_LINE_CHAIN& aNew )
1379{
1380 int64_t area = 0;
1381 const int oc = aOld.PointCount();
1382 const int nc = aNew.PointCount();
1383 const int total = oc + nc;
1384
1385 for(int i = 0; i < total; i++)
1386 {
1387 int i_next = (i + 1 == total ? 0 : i + 1);
1388
1389 const VECTOR2I &v0 = i < oc ? aOld.CPoint(i)
1390 : aNew.CPoint( nc - 1 - (i - oc) );
1391 const VECTOR2I &v1 = i_next < oc ? aOld.CPoint ( i_next )
1392 : aNew.CPoint( nc - 1 - (i_next - oc) );
1393 area += -(int64_t) v0.y * v1.x + (int64_t) v0.x * v1.y;
1394 }
1395
1396 return std::abs( area / 2 );
1397}
1398
1399
1400bool tightenSegment( bool dir, NODE *aNode, const LINE& cur, const SHAPE_LINE_CHAIN& in,
1401 SHAPE_LINE_CHAIN& out )
1402{
1403 SEG a = in.CSegment(0);
1404 SEG center = in.CSegment(1);
1405 SEG b = in.CSegment(2);
1406
1407 DIRECTION_45 dirA ( a );
1408 DIRECTION_45 dirCenter ( center );
1409 DIRECTION_45 dirB ( b );
1410
1411 if (!dirA.IsObtuse( dirCenter) || !dirCenter.IsObtuse(dirB))
1412 return false;
1413
1414 SEG guide;
1415 int initial;
1416
1417 //auto dbg = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
1418 if ( dirA.Angle ( dirB ) != DIRECTION_45::ANG_RIGHT )
1419 return false;
1420
1421 {
1422 /*
1423 auto rC = *a.IntersectLines( b );
1424 dbg->AddSegment ( SEG( center.A, rC ), 1 );
1425 dbg->AddSegment ( SEG( center.B, rC ), 2 );
1426 auto perp = dirCenter.Left().Left();
1427
1428 SEG sperp ( center.A, center.A + perp.ToVector() );
1429
1430 auto vpc = sperp.LineProject( rC );
1431 auto vpa = sperp.LineProject( a.A );
1432 auto vpb = sperp.LineProject( b.B );
1433
1434 auto da = (vpc - vpa).EuclideanNorm();
1435 auto db = (vpc - vpb).EuclideanNorm();
1436
1437 auto vp = (da < db) ? vpa : vpb;
1438 dbg->AddSegment ( SEG( vpc, vp ), 5 );
1439
1440
1441 guide = SEG ( vpc, vp );
1442 */
1443 }
1444
1445 int da = a.Length();
1446 int db = b.Length();
1447
1448 if( da < db )
1449 guide = a;
1450 else
1451 guide = b;
1452
1453 initial = guide.Length();
1454
1455 int step = initial;
1456 int current = step;
1457 SHAPE_LINE_CHAIN snew;
1458
1459 while( step > 1 )
1460 {
1461 LINE l( cur );
1462
1463 snew.Clear();
1464 snew.Append( a.A );
1465 snew.Append( a.B + ( a.A - a.B ).Resize( current ) );
1466 snew.Append( b.A + ( b.B - b.A ).Resize( current ) );
1467 snew.Append( b.B );
1468
1469 step /= 2;
1470
1471 l.SetShape(snew);
1472
1473 if( aNode->CheckColliding(&l) )
1474 current -= step;
1475 else if ( current + step >= initial )
1476 current = initial;
1477 else
1478 current += step;
1479
1480 //dbg->AddSegment ( SEG( center.A , a.LineProject( center.A + gr ) ), 3 );
1481 //dbg->AddSegment ( SEG( center.A , center.A + guideA ), 3 );
1482 //dbg->AddSegment ( SEG( center.B , center.B + guideB ), 4 );
1483
1484 if ( current == initial )
1485 break;
1486 }
1487
1488 //dbg->AddLine ( snew, 3, 100000 );
1489
1490 out = std::move( snew );
1491 return true;
1492}
1493
1494
1495void Tighten( NODE *aNode, const SHAPE_LINE_CHAIN& aOldLine, const LINE& aNewLine,
1496 LINE& aOptimized )
1497{
1498 LINE tmp;
1499
1500 if( aNewLine.SegmentCount() < 3 )
1501 return;
1502
1503 SHAPE_LINE_CHAIN current ( aNewLine.CLine() );
1504
1505 for( int step = 0; step < 3; step++ )
1506 {
1507 current.Simplify2();
1508
1509 for( int i = 0; i <= current.SegmentCount() - 3; i++ )
1510 {
1511 SHAPE_LINE_CHAIN l_in, l_out;
1512
1513 l_in = current.Slice( i, i + 3 );
1514
1515 for( int dir = 0; dir <= 1; dir++ )
1516 {
1517 if( tightenSegment( dir ? true : false, aNode, aNewLine, l_in, l_out ) )
1518 {
1519 SHAPE_LINE_CHAIN opt = current;
1520 opt.Replace( i, i + 3, l_out );
1521 long long int optArea = std::abs( shovedArea( aOldLine, opt ) );
1522 long long int prevArea = std::abs( shovedArea( aOldLine, current ) );
1523
1524 if( optArea < prevArea )
1525 current = std::move( opt );
1526
1527 break;
1528 }
1529 }
1530 }
1531 }
1532
1533 aOptimized = LINE( aNewLine, current );
1534
1535 //auto dbg = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
1536 //dbg->AddLine ( current, 4, 100000 );
1537}
1538
1539}
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr size_type GetHeight() const
Definition box2.h:215
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.
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 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
int Width() const
const RANGED_NUM< int > GapConstraint() const
double CoupledLength() const
void SetShape(const SHAPE_LINE_CHAIN &aP, const SHAPE_LINE_CHAIN &aN, bool aSwapLanes=false)
const SHAPE_LINE_CHAIN & CP() 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:294
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:150
int ArcCount() const
Definition pns_line.h:146
void SetShape(const SHAPE_LINE_CHAIN &aLine)
Return the shape of the line.
Definition pns_line.h:131
const SHAPE_LINE_CHAIN & CLine() const
Definition pns_line.h:142
const VECTOR2I & CLastPoint() const
Definition pns_line.h:151
int CountCorners(int aAngles) const
Definition pns_line.cpp:214
SHAPE_LINE_CHAIN & Line()
Definition pns_line.h:141
int SegmentCount() const
Definition pns_line.h:144
int PointCount() const
Definition pns_line.h:145
bool EndsWithVia() const
Definition pns_line.h:195
const SEG CSegment(int aIdx) const
Set line width.
Definition pns_line.h:152
int Width() const
Return true if the line is geometrically identical as line aOther.
Definition pns_line.h:162
Keep the router "world" - i.e.
Definition pns_node.h:240
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:494
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
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)
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.
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:210
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
bool Matches(const T &aOther) const
Definition ranged_num.h:44
Definition seg.h:42
VECTOR2I A
Definition seg.h:49
ecoord SquaredDistance(const SEG &aSeg) const
Definition seg.cpp:80
VECTOR2I::extended_type ecoord
Definition seg.h:44
VECTOR2I B
Definition seg.h:50
int Index() const
Return the index of this segment in its parent shape (applicable only to non-local segments).
Definition seg.h:361
int Length() const
Return the length (this).
Definition seg.h:343
bool ApproxParallel(const SEG &aSeg, int aDistanceThreshold=1) const
Definition seg.cpp:807
bool Collinear(const SEG &aSeg) const
Check if segment aSeg lies on the same line as (this).
Definition seg.h:286
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition seg.h:220
ecoord SquaredLength() const
Definition seg.h:348
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:685
EDA_ANGLE Angle(const SEG &aOther) const
Determine the smallest angle between two segments.
Definition seg.cpp:111
SHAPE_TYPE Type() const
Return the type of the shape.
Definition shape.h:98
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.
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:169
const VECTOR2I GetSize() const
Definition shape_rect.h:177
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:126
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:181
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:385
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
static constexpr EDA_ANGLE ANGLE_45
Definition eda_angle.h:412
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:417
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:400
@ SH_RECT
axis-aligned rectangle
Definition shape.h:47
@ SH_CIRCLE
circle
Definition shape.h:50
@ SH_SIMPLE
simple polygon
Definition shape.h:51
@ SH_SEGMENT
line segment
Definition shape.h:48
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:229
Casted dyn_cast(From aObject)
A lightweight dynamic downcast.
Definition typeinfo.h:61
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:695