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
23#include <geometry/shape_rect.h>
25
26#include <cmath>
27
28#include "pns_arc.h"
29#include "pns_line.h"
30#include "pns_diff_pair.h"
31#include "pns_node.h"
32#include "pns_solid.h"
33#include "pns_optimizer.h"
34
35#include "pns_utils.h"
36#include "pns_router.h"
37#include "pns_debug_decorator.h"
38
39
40namespace PNS {
41
42
43int COST_ESTIMATOR::CornerCost( const SEG& aA, const SEG& aB )
44{
45 DIRECTION_45 dir_a( aA ), dir_b( aB );
46
47 switch( dir_a.Angle( dir_b ) )
48 {
49 case DIRECTION_45::ANG_OBTUSE: return 10;
50 case DIRECTION_45::ANG_STRAIGHT: return 5;
51 case DIRECTION_45::ANG_ACUTE: return 50;
52 case DIRECTION_45::ANG_RIGHT: return 30;
53 case DIRECTION_45::ANG_HALF_FULL: return 60;
54 default: return 100;
55 }
56}
57
58
60{
61 int total = 0;
62
63 for( int i = 0; i < aLine.SegmentCount() - 1; ++i )
64 total += CornerCost( aLine.CSegment( i ), aLine.CSegment( i + 1 ) );
65
66 return total;
67}
68
69
71{
72 return CornerCost( aLine.CLine() );
73}
74
75
76void COST_ESTIMATOR::Add( const LINE& aLine )
77{
78 m_lengthCost += aLine.CLine().Length();
79 m_cornerCost += CornerCost( aLine );
80}
81
82
83void COST_ESTIMATOR::Remove( const LINE& aLine )
84{
85 m_lengthCost -= aLine.CLine().Length();
86 m_cornerCost -= CornerCost( aLine );
87}
88
89
90void COST_ESTIMATOR::Replace( const LINE& aOldLine, const LINE& aNewLine )
91{
92 m_lengthCost -= aOldLine.CLine().Length();
93 m_cornerCost -= CornerCost( aOldLine );
94 m_lengthCost += aNewLine.CLine().Length();
95 m_cornerCost += CornerCost( aNewLine );
96}
97
98
99bool COST_ESTIMATOR::IsBetter( const COST_ESTIMATOR& aOther, double aLengthTolerance,
100 double aCornerTolerance ) const
101{
102 if( aOther.m_cornerCost < m_cornerCost && aOther.m_lengthCost < m_lengthCost )
103 return true;
104 else if( aOther.m_cornerCost < m_cornerCost * aCornerTolerance &&
105 aOther.m_lengthCost < m_lengthCost * aLengthTolerance )
106 return true;
107
108 return false;
109}
110
111
113 m_world( aWorld ),
114 m_collisionKindMask( ITEM::ANY_T ),
117{
118}
119
120
122{
123 for( OPT_CONSTRAINT* c : m_constraints )
124 delete c;
125
126 m_constraints.clear();
127}
128
129
131{
132 CACHE_VISITOR( const ITEM* aOurItem, NODE* aNode, int aMask ) :
133 m_ourItem( aOurItem ),
134 m_collidingItem( nullptr ),
135 m_node( aNode ),
136 m_mask( aMask )
137 {}
138
139 bool operator()( ITEM* aOtherItem )
140 {
141 if( !( m_mask & aOtherItem->Kind() ) )
142 return true;
143
144 // TODO(JE) viastacks
145 if( !aOtherItem->Collide( m_ourItem, m_node, m_ourItem->Layer() ) )
146 return true;
147
148 m_collidingItem = aOtherItem;
149 return false;
150 }
151
156};
157
158
159void OPTIMIZER::cacheAdd( ITEM* aItem, bool aIsStatic = false )
160{
161 if( m_cacheTags.find( aItem ) != m_cacheTags.end() )
162 return;
163
164 m_cache.Add( aItem );
165 m_cacheTags[aItem].m_hits = 1;
166 m_cacheTags[aItem].m_isStatic = aIsStatic;
167}
168
169
170void OPTIMIZER::removeCachedSegments( LINE* aLine, int aStartVertex, int aEndVertex )
171{
172 if( !aLine->IsLinked() )
173 return;
174
175 auto links = aLine->Links();
176
177 if( aEndVertex < 0 )
178 aEndVertex += aLine->PointCount();
179
180 for( int i = aStartVertex; i < aEndVertex - 1; i++ )
181 {
182 LINKED_ITEM* s = links[i];
183 m_cacheTags.erase( s );
184 m_cache.Remove( s );
185 }
186}
187
188
190{
191 if( aItem->Kind() == ITEM::LINE_T )
192 removeCachedSegments( static_cast<LINE*>( aItem ) );
193}
194
195
196void OPTIMIZER::ClearCache( bool aStaticOnly )
197{
198 if( !aStaticOnly )
199 {
200 m_cacheTags.clear();
201 m_cache.Clear();
202 return;
203 }
204
205 for( auto i = m_cacheTags.begin(); i!= m_cacheTags.end(); ++i )
206 {
207 if( i->second.m_isStatic )
208 {
209 m_cache.Remove( i->first );
210 m_cacheTags.erase( i->first );
211 }
212 }
213}
214
215
216bool AREA_CONSTRAINT::Check( int aVertex1, int aVertex2, const LINE* aOriginLine,
217 const SHAPE_LINE_CHAIN& aCurrentPath,
218 const SHAPE_LINE_CHAIN& aReplacement )
219{
220 const VECTOR2I& p1 = aCurrentPath.CPoint( aVertex1 );
221 const VECTOR2I& p2 = aCurrentPath.CPoint( aVertex2 );
222
223 bool p1_in = m_allowedArea.Contains( p1 );
224 bool p2_in = m_allowedArea.Contains( p2 );
225
227
228 if( p1_in && p2_in )
229 return true;
230
231 if( aVertex1 < aCurrentPath.PointCount() - 1 && !p1_in && p2_in
232 && m_allowedArea.Contains( aCurrentPath.CPoint( aVertex1 + 1 ) ) )
233 return aReplacement.CSegment( 0 ).Angle( aCurrentPath.CSegment( aVertex1 ) ).IsHorizontal();
234
235 if( p1_in && !p2_in && m_allowedArea.Contains( aCurrentPath.CPoint( aVertex2 - 1 ) ) )
236 return aReplacement.CSegment( -1 )
237 .Angle( aCurrentPath.CSegment( aVertex2 - 1 ) )
238 .IsHorizontal();
239
240 //PNS_DBG( dbg, AddShape, m_allowedArea, YELLOW, 10000, wxT( "drag-affected-area" ) );
241 //PNS_DBG( dbg, AddPoint, p1, YELLOW, 1000000, wxT( "drag-p1" ) );
242 //PNS_DBG( dbg, AddPoint, p2, YELLOW, 1000000, wxT( "drag-p2" ) );
243
244 return false;
245}
246
247
248bool PRESERVE_VERTEX_CONSTRAINT::Check( int aVertex1, int aVertex2, const LINE* aOriginLine,
249 const SHAPE_LINE_CHAIN& aCurrentPath,
250 const SHAPE_LINE_CHAIN& aReplacement )
251{
252 bool cv = false;
253
254 for( int i = aVertex1; i < aVertex2; i++ )
255 {
256 SEG::ecoord dist = aCurrentPath.CSegment(i).SquaredDistance( m_v );
257
258 if ( dist <= 1 )
259 {
260 cv = true;
261 break;
262 }
263 }
264
265 if( !cv )
266 return true;
267
268 for( int i = 0; i < aReplacement.SegmentCount(); i++ )
269 {
270 SEG::ecoord dist = aReplacement.CSegment(i).SquaredDistance( m_v );
271
272 if ( dist <= 1 )
273 return true;
274 }
275
276 return false;
277}
278
279
280bool RESTRICT_VERTEX_RANGE_CONSTRAINT::Check( int aVertex1, int aVertex2, const LINE* aOriginLine,
281 const SHAPE_LINE_CHAIN& aCurrentPath,
282 const SHAPE_LINE_CHAIN& aReplacement )
283{
284 return true;
285}
286
287
288bool CORNER_COUNT_LIMIT_CONSTRAINT::Check( int aVertex1, int aVertex2, const LINE* aOriginLine,
289 const SHAPE_LINE_CHAIN& aCurrentPath,
290 const SHAPE_LINE_CHAIN& aReplacement )
291{
292 LINE newPath( *aOriginLine, aCurrentPath );
293 newPath.Line().Replace( aVertex1, aVertex2, aReplacement );
294 newPath.Line().Simplify2();
295 int cc = newPath.CountCorners( m_angleMask );
296
297 if( cc >= m_minCorners )
298 return true;
299
300 // fixme: something fishy with the max corneriness limit
301 // (cc <= m_maxCorners)
302
303 return true;
304}
305
306
318static bool pointInside2( const SHAPE_LINE_CHAIN& aL, const VECTOR2I& aP )
319{
320 if( !aL.IsClosed() || aL.SegmentCount() < 3 )
321 return false;
322
323 int result = 0;
324 size_t cnt = aL.PointCount();
325
326 VECTOR2I ip = aL.CPoint( 0 );
327
328 for( size_t i = 1; i <= cnt; ++i )
329 {
330 VECTOR2I ipNext = ( i == cnt ? aL.CPoint( 0 ) : aL.CPoint( i ) );
331
332 if( ipNext.y == aP.y )
333 {
334 if( ( ipNext.x == aP.x )
335 || ( ip.y == aP.y && ( ( ipNext.x > aP.x ) == ( ip.x < aP.x ) ) ) )
336 return true; // pt on polyground boundary
337 }
338
339 if( ( ip.y < aP.y ) != ( ipNext.y < aP.y ) )
340 {
341 if( ip.x >=aP.x )
342 {
343 if( ipNext.x >aP.x )
344 {
345 result = 1 - result;
346 }
347 else
348 {
349 double d = static_cast<double>( ip.x - aP.x ) *
350 static_cast<double>( ipNext.y - aP.y ) -
351 static_cast<double>( ipNext.x - aP.x ) *
352 static_cast<double>( ip.y - aP.y );
353
354 if( !d )
355 return true; // pt on polyground boundary
356
357 if( ( d > 0 ) == ( ipNext.y > ip.y ) )
358 result = 1 - result;
359 }
360 }
361 else
362 {
363 if( ipNext.x >aP.x )
364 {
365 double d = ( (double) ip.x - aP.x ) * ( (double) ipNext.y - aP.y )
366 - ( (double) ipNext.x - aP.x ) * ( (double) ip.y - aP.y );
367
368 if( !d )
369 return true; // pt on polyground boundary
370
371 if( ( d > 0 ) == ( ipNext.y > ip.y ) )
372 result = 1 - result;
373 }
374 }
375 }
376
377 ip = ipNext;
378 }
379
380 return result > 0;
381}
382
383
384bool KEEP_TOPOLOGY_CONSTRAINT::Check( int aVertex1, int aVertex2, const LINE* aOriginLine,
385 const SHAPE_LINE_CHAIN& aCurrentPath,
386 const SHAPE_LINE_CHAIN& aReplacement )
387{
388 SHAPE_LINE_CHAIN encPoly = aOriginLine->CLine().Slice( aVertex1, aVertex2 );
389
390 // fixme: this is a remarkably shitty implementation...
391 encPoly.Append( aReplacement.Reverse() );
392 encPoly.SetClosed( true );
393
394 BOX2I bb = encPoly.BBox();
395 std::vector<JOINT*> joints;
396
397 int cnt = m_world->QueryJoints( bb, joints, aOriginLine->Layers(), ITEM::SOLID_T );
398
399 if( !cnt )
400 return true;
401
402 for( JOINT* j : joints )
403 {
404 if( j->Net() == aOriginLine->Net() )
405 continue;
406
407 if( pointInside2( encPoly, j->Pos() ) )
408 {
409 bool falsePositive = false;
410
411 for( int k = 0; k < encPoly.PointCount(); k++ )
412 {
413 if( encPoly.CPoint(k) == j->Pos() )
414 {
415 falsePositive = true;
416 break;
417 }
418 }
419
420 if( !falsePositive )
421 {
422 //dbg->AddPoint(j->Pos(), 5);
423 return false;
424 }
425 }
426 }
427
428 return true;
429}
430
431
432bool OPTIMIZER::checkColliding( ITEM* aItem, bool aUpdateCache )
433{
435
436 return static_cast<bool>( m_world->CheckColliding( aItem ) );
437}
438
439
441{
442 m_constraints.push_back( aConstraint );
443}
444
445
446bool OPTIMIZER::checkConstraints( int aVertex1, int aVertex2, LINE* aOriginLine,
447 const SHAPE_LINE_CHAIN& aCurrentPath,
448 const SHAPE_LINE_CHAIN& aReplacement )
449{
450 for( OPT_CONSTRAINT* c : m_constraints )
451 {
452 if( !c->Check( aVertex1, aVertex2, aOriginLine, aCurrentPath, aReplacement ) )
453 return false;
454 }
455
456 return true;
457}
458
459
460bool OPTIMIZER::checkColliding( LINE* aLine, const SHAPE_LINE_CHAIN& aOptPath )
461{
462 LINE tmp( *aLine, aOptPath );
463
464 return checkColliding( &tmp );
465}
466
467
469{
470 SHAPE_LINE_CHAIN& line = aLine->Line();
471
472 int step = line.PointCount() - 3;
473 int iter = 0;
474 int segs_pre = line.SegmentCount();
475
476 if( step < 0 )
477 return false;
478
479 SHAPE_LINE_CHAIN current_path( line );
480
481 while( true )
482 {
483 iter++;
484 int n_segs = current_path.SegmentCount();
485 int max_step = n_segs - 2;
486
487 if( step > max_step )
488 step = max_step;
489
490 if( step < 2 )
491 {
492 line = std::move( current_path );
493 return line.SegmentCount() < segs_pre;
494 }
495
496 bool found_anything = false;
497
498 for( int n = 0; n < n_segs - step; n++ )
499 {
500 const SEG s1 = current_path.CSegment( n );
501 const SEG s2 = current_path.CSegment( n + step );
502 SEG s1opt, s2opt;
503
504 if( DIRECTION_45( s1 ).IsObtuse( DIRECTION_45( s2 ) ) )
505 {
506 VECTOR2I ip = *s1.IntersectLines( s2 );
507
508 s1opt = SEG( s1.A, ip );
509 s2opt = SEG( ip, s2.B );
510
511 if( DIRECTION_45( s1opt ).IsObtuse( DIRECTION_45( s2opt ) ) )
512 {
513 SHAPE_LINE_CHAIN opt_path;
514 opt_path.Append( s1opt.A );
515 opt_path.Append( s1opt.B );
516 opt_path.Append( s2opt.B );
517
518 LINE opt_track( *aLine, opt_path );
519
520 if( !checkColliding( &opt_track ) )
521 {
522 current_path.Replace( s1.Index() + 1, s2.Index(), ip );
523
524 // removeCachedSegments(aLine, s1.Index(), s2.Index());
525 n_segs = current_path.SegmentCount();
526 found_anything = true;
527 break;
528 }
529 }
530 }
531 }
532
533 if( !found_anything )
534 {
535 if( step <= 2 )
536 {
537 line = std::move( current_path );
538 return line.SegmentCount() < segs_pre;
539 }
540
541 step--;
542 }
543 }
544}
545
546
548{
549 SHAPE_LINE_CHAIN& line = aLine->Line();
550 int step = line.SegmentCount() - 1;
551
552 int segs_pre = line.SegmentCount();
553
554 line.Simplify2();
555
556 if( step < 0 )
557 return false;
558
559 SHAPE_LINE_CHAIN current_path( line );
560
561 while( true )
562 {
563 int n_segs = current_path.SegmentCount();
564 int max_step = n_segs - 2;
565
566 if( step > max_step )
567 step = max_step;
568
569 if( step < 1 )
570 break;
571
572 bool found_anything = mergeStep( aLine, current_path, step );
573
574 if( !found_anything )
575 step--;
576
577 if( !step )
578 break;
579 }
580
581 aLine->SetShape( current_path );
582
583 return current_path.SegmentCount() < segs_pre;
584}
585
586
588{
589 SHAPE_LINE_CHAIN& line = aLine->Line();
590
591 int nSegs = line.SegmentCount();
592
593 for( int segIdx = 0; segIdx < line.SegmentCount() - 1; ++segIdx )
594 {
595 SEG s1 = line.CSegment( segIdx );
596 SEG s2 = line.CSegment( segIdx + 1 );
597
598 // Skip zero-length segs caused by abutting arcs
599 if( s1.SquaredLength() == 0 || s2.SquaredLength() == 0 )
600 continue;
601
602 if( s1.Collinear( s2 ) && !line.IsPtOnArc( segIdx + 1 ) )
603 {
604 line.Remove( segIdx + 1 );
605 }
606 }
607
608 return line.SegmentCount() < nSegs;
609}
610
611
612bool OPTIMIZER::Optimize( const LINE* aLine, LINE* aResult, LINE* aRoot )
613{
615
616 if( aRoot )
617 {
618 //PNS_DBG( dbg, AddItem, aRoot, BLUE, 100000, wxT( "root-line" ) );
619 }
620
621
622 if( !aResult )
623 return false;
624
625 *aResult = *aLine;
626 aResult->ClearLinks();
627
628 bool hasArcs = aLine->ArcCount();
629 bool rv = false;
630
631 if( (m_effortLevel & LIMIT_CORNER_COUNT) && aRoot )
632 {
633 const int angleMask = DIRECTION_45::ANG_OBTUSE;
634 int rootObtuseCorners = aRoot->CountCorners( angleMask );
635 auto c = new CORNER_COUNT_LIMIT_CONSTRAINT( m_world, rootObtuseCorners,
636 aLine->SegmentCount(), angleMask );
637 //PNS_DBG( dbg, Message,
638 // wxString::Format( "opt limit-corner-count root %d maxc %d mask %x",
639 // rootObtuseCorners, aLine->SegmentCount(), angleMask ) );
640
641 addConstraint( c );
642 }
643
645 {
647 addConstraint( c );
648 }
649
651 {
654 addConstraint( c );
655 }
656
658 {
661 //PNS_DBG( dbg, AddShape, &r, YELLOW, 0, wxT( "area-constraint" ) );
662 addConstraint( c );
663 }
664
666 {
667 auto c = new KEEP_TOPOLOGY_CONSTRAINT( m_world );
668 addConstraint( c );
669 }
670
671 // TODO: Fix for arcs
672 if( !hasArcs && m_effortLevel & MERGE_SEGMENTS )
673 rv |= mergeFull( aResult );
674
675 // TODO: Fix for arcs
676 if( !hasArcs && m_effortLevel & MERGE_OBTUSE )
677 rv |= mergeObtuse( aResult );
678
680 rv |= mergeColinear( aResult );
681
682 // TODO: Fix for arcs
683 if( !hasArcs && m_effortLevel & SMART_PADS )
684 rv |= runSmartPads( aResult );
685
686 // TODO: Fix for arcs
687 if( !hasArcs && m_effortLevel & FANOUT_CLEANUP )
688 rv |= fanoutCleanup( aResult );
689
690 return rv;
691}
692
693
694bool OPTIMIZER::mergeStep( LINE* aLine, SHAPE_LINE_CHAIN& aCurrentPath, int step )
695{
696 int n_segs = aCurrentPath.SegmentCount();
697
698 int cost_orig = COST_ESTIMATOR::CornerCost( aCurrentPath );
699
700 if( aLine->SegmentCount() < 2 )
701 return false;
702
704 bool is90mode = cornerMode == DIRECTION_45::MITERED_90 || cornerMode == DIRECTION_45::ROUNDED_90;
705
706 DIRECTION_45 orig_start( aLine->CSegment( 0 ), is90mode );
707 DIRECTION_45 orig_end( aLine->CSegment( -1 ), is90mode );
708
709
710 for( int n = 0; n < n_segs - step; n++ )
711 {
712 // Do not attempt to merge false segments that are part of an arc
713 if( aCurrentPath.IsArcSegment( n )
714 || aCurrentPath.IsArcSegment( static_cast<std::size_t>( n ) + step ) )
715 {
716 continue;
717 }
718
719 const SEG s1 = aCurrentPath.CSegment( n );
720 const SEG s2 = aCurrentPath.CSegment( n + step );
721
723 SHAPE_LINE_CHAIN* picked = nullptr;
724 int cost[2];
725
726 for( int i = 0; i < 2; i++ )
727 {
728 SHAPE_LINE_CHAIN bypass = DIRECTION_45().BuildInitialTrace( s1.A, s2.B, i, cornerMode );
729 cost[i] = INT_MAX;
730
731 bool ok = false;
732
733 if( !checkColliding( aLine, bypass ) )
734 {
735 ok = checkConstraints ( n, n + step + 1, aLine, aCurrentPath, bypass );
736 }
737
738 if( ok )
739 {
740 path[i] = aCurrentPath;
741 path[i].Replace( s1.Index(), s2.Index(), bypass );
742 path[i].Simplify2();
743 cost[i] = COST_ESTIMATOR::CornerCost( path[i] );
744 }
745 }
746
747 if( cost[0] < cost_orig && cost[0] < cost[1] )
748 picked = &path[0];
749 else if( cost[1] < cost_orig )
750 picked = &path[1];
751
752 if( picked )
753 {
754 n_segs = aCurrentPath.SegmentCount();
755 aCurrentPath = *picked;
756 return true;
757 }
758 }
759
760 return false;
761}
762
763
765 bool aPermitDiagonal ) const
766{
767 BREAKOUT_LIST breakouts;
768
770 {
771 const SHAPE_CIRCLE* cir = static_cast<const SHAPE_CIRCLE*>( aShape );
773 VECTOR2I p0 = cir->GetCenter();
774 VECTOR2I v0( cir->GetRadius() * M_SQRT2, 0 );
775
776 RotatePoint( v0, -angle );
777
778 l.Append( p0 );
779 l.Append( p0 + v0 );
780 breakouts.push_back( l );
781 }
782
783 return breakouts;
784}
785
786
788 bool aPermitDiagonal ) const
789{
790 BREAKOUT_LIST breakouts;
791 const SHAPE_SIMPLE* convex = static_cast<const SHAPE_SIMPLE*>( aItem->Shape( -1 ) );
792
793 BOX2I bbox = convex->BBox( 0 );
794 VECTOR2I p0 = static_cast<const SOLID*>( aItem )->Pos();
795 // must be large enough to guarantee intersecting the convex polygon
796 int length = std::max( bbox.GetWidth(), bbox.GetHeight() ) / 2 + 5;
797 EDA_ANGLE increment = ( aPermitDiagonal ? ANGLE_45 : ANGLE_90 );
798
799 for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += increment )
800 {
802 VECTOR2I v0( p0 + VECTOR2I( length, 0 ) );
803 RotatePoint( v0, p0, -angle );
804
806 int n = convex->Vertices().Intersect( SEG( p0, v0 ), intersections );
807
808 // if n == 1 intersected a segment
809 // if n == 2 intersected the common point of 2 segments
810 // n == 0 can not happen I think, but...
811 if( n > 0 )
812 {
813 l.Append( p0 );
814
815 // for a breakout distance relative to the distance between
816 // center and polygon edge
817 //l.Append( intersections[0].p + (v0 - p0).Resize( (intersections[0].p - p0).EuclideanNorm() * 0.4 ) );
818
819 // for an absolute breakout distance, e.g. 0.1 mm
820 //l.Append( intersections[0].p + (v0 - p0).Resize( 100000 ) );
821
822 // for the breakout right on the polygon edge
823 l.Append( intersections[0].p );
824
825 breakouts.push_back( l );
826 }
827 }
828
829 return breakouts;
830}
831
832
834 bool aPermitDiagonal ) const
835{
836 const SHAPE_RECT* rect = static_cast<const SHAPE_RECT*>(aShape);
837 VECTOR2I s = rect->GetSize();
838 VECTOR2I c = rect->GetPosition() + VECTOR2I( s.x / 2, s.y / 2 );
839
840 BREAKOUT_LIST breakouts;
841 breakouts.reserve( 12 );
842
843 VECTOR2I d_offset;
844
845 d_offset.x = ( s.x > s.y ) ? ( s.x - s.y ) / 2 : 0;
846 d_offset.y = ( s.x < s.y ) ? ( s.y - s.x ) / 2 : 0;
847
848 VECTOR2I d_vert = VECTOR2I( 0, s.y / 2 + aWidth );
849 VECTOR2I d_horiz = VECTOR2I( s.x / 2 + aWidth, 0 );
850
851 breakouts.emplace_back( SHAPE_LINE_CHAIN( { c, c + d_horiz } ) );
852 breakouts.emplace_back( SHAPE_LINE_CHAIN( { c, c - d_horiz } ) );
853 breakouts.emplace_back( SHAPE_LINE_CHAIN( { c, c + d_vert } ) );
854 breakouts.emplace_back( SHAPE_LINE_CHAIN( { c, c - d_vert } ) );
855
856 if( aPermitDiagonal )
857 {
858 int l = aWidth + std::min( s.x, s.y ) / 2;
859 VECTOR2I d_diag;
860
861 if( s.x >= s.y )
862 {
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 else
873 {
874 // fixme: this could be done more efficiently
875 breakouts.emplace_back(
876 SHAPE_LINE_CHAIN( { c, c + d_offset, c + d_offset + VECTOR2I( l, l ) } ) );
877 breakouts.emplace_back(
878 SHAPE_LINE_CHAIN( { c, c - d_offset, c - d_offset - VECTOR2I( -l, l ) } ) );
879 breakouts.emplace_back(
880 SHAPE_LINE_CHAIN( { c, c + d_offset, c + d_offset + VECTOR2I( -l, l ) } ) );
881 breakouts.emplace_back(
882 SHAPE_LINE_CHAIN( { c, c - d_offset, c - d_offset - VECTOR2I( l, l ) } ) );
883 }
884 }
885
886 return breakouts;
887}
888
889
891 bool aPermitDiagonal ) const
892{
893 switch( aItem->Kind() )
894 {
895 case ITEM::VIA_T:
896 {
897 const VIA* via = static_cast<const VIA*>( aItem );
898 // TODO(JE) padstacks -- computeBreakouts needs to have a layer argument
899 return circleBreakouts( aWidth, via->Shape( 0 ), aPermitDiagonal );
900 }
901
902 case ITEM::SOLID_T:
903 {
904 const SHAPE* shape = aItem->Shape( -1 );
905
906 switch( shape->Type() )
907 {
908 case SH_RECT:
909 return rectBreakouts( aWidth, shape, aPermitDiagonal );
910
911 case SH_SEGMENT:
912 {
913 const SHAPE_SEGMENT* seg = static_cast<const SHAPE_SEGMENT*> (shape);
914 const SHAPE_RECT rect = ApproximateSegmentAsRect ( *seg );
915 return rectBreakouts( aWidth, &rect, aPermitDiagonal );
916 }
917
918 case SH_CIRCLE:
919 return circleBreakouts( aWidth, shape, aPermitDiagonal );
920
921 case SH_SIMPLE:
922 return customBreakouts( aWidth, aItem, aPermitDiagonal );
923
924 default:
925 break;
926 }
927
928 break;
929 }
930
931 default:
932 break;
933 }
934
935 return BREAKOUT_LIST();
936}
937
938
939ITEM* OPTIMIZER::findPadOrVia( int aLayer, NET_HANDLE aNet, const VECTOR2I& aP ) const
940{
941 const JOINT* jt = m_world->FindJoint( aP, aLayer, aNet );
942
943 if( !jt )
944 return nullptr;
945
946 for( ITEM* item : jt->LinkList() )
947 {
948 if( item->OfKind( ITEM::VIA_T | ITEM::SOLID_T ) )
949 return item;
950 }
951
952 return nullptr;
953}
954
955
956int OPTIMIZER::smartPadsSingle( LINE* aLine, ITEM* aPad, bool aEnd, int aEndVertex )
957{
958 DIRECTION_45 dir;
959
960 const int ForbiddenAngles = DIRECTION_45::ANG_ACUTE | DIRECTION_45::ANG_RIGHT |
962
963 typedef std::tuple<int, long long int, SHAPE_LINE_CHAIN> RtVariant;
964 std::vector<RtVariant> variants;
965
966 SOLID* solid = dyn_cast<SOLID*>( aPad );
967
968 // don't do optimized connections for offset pads
969 if( solid && solid->Offset() != VECTOR2I( 0, 0 ) )
970 return -1;
971
972 // don't do optimization on vias, they are always round at the moment and the optimizer
973 // will possibly mess up an intended via exit posture
974 if( aPad->Kind() == ITEM::VIA_T )
975 return -1;
976
977 BREAKOUT_LIST breakouts = computeBreakouts( aLine->Width(), aPad, true );
978 SHAPE_LINE_CHAIN line = ( aEnd ? aLine->CLine().Reverse() : aLine->CLine() );
979 int p_end = std::min( aEndVertex, std::min( 3, line.PointCount() - 1 ) );
980
981 // Start at 1 to find a potentially better breakout (0 is the pad connection)
982 for( int p = 1; p <= p_end; p++ )
983 {
984 // If the line is contained inside the pad, don't optimize
985 if( solid && solid->Shape( -1 ) && !solid->Shape( -1 )->Collide(
986 SEG( line.CPoint( 0 ), line.CPoint( p ) ), aLine->Width() / 2 ) )
987 {
988 continue;
989 }
990
991 for( SHAPE_LINE_CHAIN & breakout : breakouts )
992 {
993 for( int diag = 0; diag < 2; diag++ )
994 {
997 breakout.CLastPoint(), line.CPoint( p ), diag == 0 );
998
999 DIRECTION_45 dir_bkout( breakout.CSegment( -1 ) );
1000
1001 if( !connect.SegmentCount() )
1002 continue;
1003
1004 int ang1 = dir_bkout.Angle( DIRECTION_45( connect.CSegment( 0 ) ) );
1005
1006 if( ang1 & ForbiddenAngles )
1007 continue;
1008
1009 if( breakout.Length() > line.Length() )
1010 continue;
1011
1012 v = breakout;
1013 v.Append( connect );
1014
1015 for( int i = p + 1; i < line.PointCount(); i++ )
1016 v.Append( line.CPoint( i ) );
1017
1018 LINE tmp( *aLine, v );
1019 int cc = tmp.CountCorners( ForbiddenAngles );
1020
1021 if( cc == 0 )
1022 {
1023 RtVariant vp;
1024 std::get<0>( vp ) = p;
1025 std::get<1>( vp ) = breakout.Length();
1026 std::get<2>( vp ) = aEnd ? v.Reverse() : v;
1027 std::get<2>( vp ).Simplify2();
1028 variants.push_back( std::move( vp ) );
1029 }
1030 }
1031 }
1032 }
1033
1034 // We attempt to minimize the corner cost (minimizes the segments and types of corners)
1035 // but given two, equally valid costs, we want to pick the longer pad exit. The logic
1036 // here is that if the pad is oblong, the track should not exit the shorter side and parallel
1037 // the pad but should follow the pad's preferential direction before exiting.
1038 // The baseline guess is to start with the existing line the user has drawn.
1039 int min_cost = COST_ESTIMATOR::CornerCost( *aLine );
1040 long long int max_length = 0;
1041 bool found = false;
1042 int p_best = -1;
1043 SHAPE_LINE_CHAIN l_best;
1044
1045 for( RtVariant& vp : variants )
1046 {
1047 LINE tmp( *aLine, std::get<2>( vp ) );
1048 int cost = COST_ESTIMATOR::CornerCost( std::get<2>( vp ) );
1049 long long int len = std::get<1>( vp );
1050
1051 if( !checkColliding( &tmp ) )
1052 {
1053 if( cost < min_cost || ( cost == min_cost && len > max_length ) )
1054 {
1055 l_best = std::get<2>( vp );
1056 p_best = std::get<0>( vp );
1057 found = true;
1058
1059 if( cost <= min_cost )
1060 max_length = std::max<int>( len, max_length );
1061
1062 min_cost = std::min( cost, min_cost );
1063 }
1064 }
1065 }
1066
1067 if( found )
1068 {
1069 aLine->SetShape( l_best );
1070 return p_best;
1071 }
1072
1073 return -1;
1074}
1075
1076
1078{
1079 SHAPE_LINE_CHAIN& line = aLine->Line();
1080
1081 if( line.PointCount() < 3 )
1082 return false;
1083
1084 VECTOR2I p_start = line.CPoint( 0 ), p_end = line.CLastPoint();
1085
1086 ITEM* startPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_start );
1087 ITEM* endPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_end );
1088
1089 int vtx = -1;
1090
1091 if( startPad )
1092 vtx = smartPadsSingle( aLine, startPad, false, 3 );
1093
1094 if( endPad )
1095 smartPadsSingle( aLine, endPad, true,
1096 vtx < 0 ? line.PointCount() - 1 : line.PointCount() - 1 - vtx );
1097
1098 aLine->Line().Simplify2();
1099
1100 return true;
1101}
1102
1103
1104bool OPTIMIZER::Optimize( LINE* aLine, int aEffortLevel, NODE* aWorld, const VECTOR2I& aV )
1105{
1106 OPTIMIZER opt( aWorld );
1107
1108 opt.SetEffortLevel( aEffortLevel );
1109 opt.SetCollisionMask( -1 );
1110
1111 if( aEffortLevel & OPTIMIZER::PRESERVE_VERTEX )
1112 opt.SetPreserveVertex( aV );
1113
1114 LINE tmp( *aLine );
1115 return opt.Optimize( &tmp, aLine );
1116}
1117
1118
1120{
1121 if( aLine->PointCount() < 3 )
1122 return false;
1123
1125
1126 VECTOR2I p_start = aLine->CPoint( 0 ), p_end = aLine->CLastPoint();
1127
1128 ITEM* startPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_start );
1129 ITEM* endPad = findPadOrVia( aLine->Layer(), aLine->Net(), p_end );
1130
1131 int thr = aLine->Width() * 10;
1132 int len = aLine->CLine().Length();
1133
1134 if( !startPad )
1135 return false;
1136
1137 bool startMatch = startPad->OfKind( ITEM::VIA_T | ITEM::SOLID_T );
1138 bool endMatch = false;
1139
1140 if(endPad)
1141 {
1142 endMatch = endPad->OfKind( ITEM::VIA_T | ITEM::SOLID_T );
1143 }
1144 else
1145 {
1146 endMatch = aLine->EndsWithVia();
1147 }
1148
1149 if( startMatch && endMatch && len < thr )
1150 {
1151 for( int i = 0; i < 2; i++ )
1152 {
1153 SHAPE_LINE_CHAIN l2 = DIRECTION_45().BuildInitialTrace( p_start, p_end, i, cornerMode );
1154 LINE repl;
1155 repl = LINE( *aLine, l2 );
1156
1157 if( !m_world->CheckColliding( &repl ) )
1158 {
1159 aLine->SetShape( repl.CLine() );
1160 return true;
1161 }
1162 }
1163 }
1164
1165 return false;
1166}
1167
1168
1169int findCoupledVertices( const VECTOR2I& aVertex, const SEG& aOrigSeg,
1170 const SHAPE_LINE_CHAIN& aCoupled, DIFF_PAIR* aPair, int* aIndices )
1171{
1172 int count = 0;
1173
1174 for ( int i = 0; i < aCoupled.SegmentCount(); i++ )
1175 {
1176 SEG s = aCoupled.CSegment( i );
1177 VECTOR2I projOverCoupled = s.LineProject ( aVertex );
1178
1179 if( s.ApproxParallel( aOrigSeg ) )
1180 {
1181 int64_t dist =
1182 int64_t{ ( ( projOverCoupled - aVertex ).EuclideanNorm() ) } - aPair->Width();
1183
1184 if( aPair->GapConstraint().Matches( dist ) )
1185 {
1186 *aIndices++ = i;
1187 count++;
1188 }
1189 }
1190 }
1191
1192 return count;
1193}
1194
1195
1196bool verifyDpBypass( NODE* aNode, DIFF_PAIR* aPair, bool aRefIsP, const SHAPE_LINE_CHAIN& aNewRef,
1197 const SHAPE_LINE_CHAIN& aNewCoupled )
1198{
1199 LINE refLine ( aRefIsP ? aPair->PLine() : aPair->NLine(), aNewRef );
1200 LINE coupledLine ( aRefIsP ? aPair->NLine() : aPair->PLine(), aNewCoupled );
1201
1202 if( refLine.Collide( &coupledLine, aNode, refLine.Layer() ) )
1203 return false;
1204
1205 if( aNode->CheckColliding ( &refLine ) )
1206 return false;
1207
1208 if( aNode->CheckColliding ( &coupledLine ) )
1209 return false;
1210
1211 return true;
1212}
1213
1214
1215bool coupledBypass( NODE* aNode, DIFF_PAIR* aPair, bool aRefIsP, const SHAPE_LINE_CHAIN& aRef,
1216 const SHAPE_LINE_CHAIN& aRefBypass, const SHAPE_LINE_CHAIN& aCoupled,
1217 SHAPE_LINE_CHAIN& aNewCoupled )
1218{
1219 int vStartIdx[1024]; // fixme: possible overflow
1220 int nStarts = findCoupledVertices( aRefBypass.CPoint( 0 ),
1221 aRefBypass.CSegment( 0 ),
1222 aCoupled, aPair, vStartIdx );
1223 DIRECTION_45 dir( aRefBypass.CSegment( 0 ) );
1224
1225 int64_t bestLength = -1;
1226 bool found = false;
1227 SHAPE_LINE_CHAIN bestBypass;
1228 int si, ei;
1229
1230 for( int i=0; i< nStarts; i++ )
1231 {
1232 for( int j = 1; j < aCoupled.PointCount() - 1; j++ )
1233 {
1234 int delta = std::abs ( vStartIdx[i] - j );
1235
1236 if( delta > 1 )
1237 {
1238 const VECTOR2I& vs = aCoupled.CPoint( vStartIdx[i] );
1239 SHAPE_LINE_CHAIN bypass = dir.BuildInitialTrace( vs, aCoupled.CPoint(j),
1240 dir.IsDiagonal() );
1241
1242 int64_t coupledLength = aPair->CoupledLength( aRef, bypass );
1243
1244 SHAPE_LINE_CHAIN newCoupled = aCoupled;
1245
1246 si = vStartIdx[i];
1247 ei = j;
1248
1249 if(si < ei)
1250 newCoupled.Replace( si, ei, bypass );
1251 else
1252 newCoupled.Replace( ei, si, bypass.Reverse() );
1253
1254 if( coupledLength > bestLength && verifyDpBypass( aNode, aPair, aRefIsP, aRef,
1255 newCoupled) )
1256 {
1257 bestBypass = std::move( newCoupled );
1258 bestLength = coupledLength;
1259 found = true;
1260 }
1261 }
1262 }
1263 }
1264
1265 if( found )
1266 aNewCoupled = std::move( bestBypass );
1267
1268 return found;
1269}
1270
1271
1272bool checkDpColliding( NODE* aNode, DIFF_PAIR* aPair, bool aIsP, const SHAPE_LINE_CHAIN& aPath )
1273{
1274 LINE tmp ( aIsP ? aPair->PLine() : aPair->NLine(), aPath );
1275
1276 return static_cast<bool>( aNode->CheckColliding( &tmp ) );
1277}
1278
1279
1280bool OPTIMIZER::mergeDpStep( DIFF_PAIR* aPair, bool aTryP, int step )
1281{
1282 int n = 1;
1283
1284 SHAPE_LINE_CHAIN currentPath = aTryP ? aPair->CP() : aPair->CN();
1285 SHAPE_LINE_CHAIN coupledPath = aTryP ? aPair->CN() : aPair->CP();
1286
1287 int n_segs = currentPath.SegmentCount() - 1;
1288
1289 int64_t clenPre = aPair->CoupledLength( currentPath, coupledPath );
1290 int64_t budget = clenPre / 10; // fixme: come up with something more intelligent here...
1291
1292 while( n < n_segs - step )
1293 {
1294 const SEG s1 = currentPath.CSegment( n );
1295 const SEG s2 = currentPath.CSegment( n + step );
1296
1297 DIRECTION_45 dir1( s1 );
1298 DIRECTION_45 dir2( s2 );
1299
1300 if( dir1.IsObtuse( dir2 ) )
1301 {
1303 dir1.IsDiagonal() );
1304 SHAPE_LINE_CHAIN newRef;
1305 SHAPE_LINE_CHAIN newCoup;
1306 int64_t deltaCoupled = -1, deltaUni = -1;
1307
1308 newRef = currentPath;
1309 newRef.Replace( s1.Index(), s2.Index(), bypass );
1310
1311 deltaUni = aPair->CoupledLength ( newRef, coupledPath ) - clenPre + budget;
1312
1313 if( coupledBypass( m_world, aPair, aTryP, newRef, bypass, coupledPath, newCoup ) )
1314 {
1315 deltaCoupled = aPair->CoupledLength( newRef, newCoup ) - clenPre + budget;
1316
1317 if( deltaCoupled >= 0 )
1318 {
1319 newRef.Simplify2();
1320 newCoup.Simplify2();
1321
1322 aPair->SetShape( newRef, newCoup, !aTryP );
1323 return true;
1324 }
1325 }
1326 else if( deltaUni >= 0 && verifyDpBypass( m_world, aPair, aTryP, newRef, coupledPath ) )
1327 {
1328 newRef.Simplify2();
1329 coupledPath.Simplify2();
1330
1331 aPair->SetShape( newRef, coupledPath, !aTryP );
1332 return true;
1333 }
1334 }
1335
1336 n++;
1337 }
1338
1339 return false;
1340}
1341
1342
1344{
1345 int step_p = aPair->CP().SegmentCount() - 2;
1346 int step_n = aPair->CN().SegmentCount() - 2;
1347
1348 while( 1 )
1349 {
1350 int n_segs_p = aPair->CP().SegmentCount();
1351 int n_segs_n = aPair->CN().SegmentCount();
1352
1353 int max_step_p = n_segs_p - 2;
1354 int max_step_n = n_segs_n - 2;
1355
1356 if( step_p > max_step_p )
1357 step_p = max_step_p;
1358
1359 if( step_n > max_step_n )
1360 step_n = max_step_n;
1361
1362 if( step_p < 1 && step_n < 1 )
1363 break;
1364
1365 bool found_anything_p = false;
1366 bool found_anything_n = false;
1367
1368 if( step_p > 1 )
1369 found_anything_p = mergeDpStep( aPair, true, step_p );
1370
1371 if( step_n > 1 )
1372 found_anything_n = mergeDpStep( aPair, false, step_n );
1373
1374 if( !found_anything_n && !found_anything_p )
1375 {
1376 step_n--;
1377 step_p--;
1378 }
1379 }
1380 return true;
1381}
1382
1383
1385{
1386 return mergeDpSegments( aPair );
1387}
1388
1389
1390static int64_t shovedArea( const SHAPE_LINE_CHAIN& aOld, const SHAPE_LINE_CHAIN& aNew )
1391{
1392 int64_t area = 0;
1393 const int oc = aOld.PointCount();
1394 const int nc = aNew.PointCount();
1395 const int total = oc + nc;
1396
1397 for(int i = 0; i < total; i++)
1398 {
1399 int i_next = (i + 1 == total ? 0 : i + 1);
1400
1401 const VECTOR2I &v0 = i < oc ? aOld.CPoint(i)
1402 : aNew.CPoint( nc - 1 - (i - oc) );
1403 const VECTOR2I &v1 = i_next < oc ? aOld.CPoint ( i_next )
1404 : aNew.CPoint( nc - 1 - (i_next - oc) );
1405 area += -(int64_t) v0.y * v1.x + (int64_t) v0.x * v1.y;
1406 }
1407
1408 return std::abs( area / 2 );
1409}
1410
1411
1412bool tightenSegment( bool dir, NODE *aNode, const LINE& cur, const SHAPE_LINE_CHAIN& in,
1413 SHAPE_LINE_CHAIN& out )
1414{
1415 SEG a = in.CSegment(0);
1416 SEG center = in.CSegment(1);
1417 SEG b = in.CSegment(2);
1418
1419 DIRECTION_45 dirA ( a );
1420 DIRECTION_45 dirCenter ( center );
1421 DIRECTION_45 dirB ( b );
1422
1423 if (!dirA.IsObtuse( dirCenter) || !dirCenter.IsObtuse(dirB))
1424 return false;
1425
1426 //VECTOR2I perp = (center.B - center.A).Perpendicular();
1427 VECTOR2I guideA, guideB ;
1428
1429 SEG guide;
1430 int initial;
1431
1432 //auto dbg = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
1433 if ( dirA.Angle ( dirB ) != DIRECTION_45::ANG_RIGHT )
1434 return false;
1435
1436 {
1437 /*
1438 auto rC = *a.IntersectLines( b );
1439 dbg->AddSegment ( SEG( center.A, rC ), 1 );
1440 dbg->AddSegment ( SEG( center.B, rC ), 2 );
1441 auto perp = dirCenter.Left().Left();
1442
1443 SEG sperp ( center.A, center.A + perp.ToVector() );
1444
1445 auto vpc = sperp.LineProject( rC );
1446 auto vpa = sperp.LineProject( a.A );
1447 auto vpb = sperp.LineProject( b.B );
1448
1449 auto da = (vpc - vpa).EuclideanNorm();
1450 auto db = (vpc - vpb).EuclideanNorm();
1451
1452 auto vp = (da < db) ? vpa : vpb;
1453 dbg->AddSegment ( SEG( vpc, vp ), 5 );
1454
1455
1456 guide = SEG ( vpc, vp );
1457 */
1458 }
1459
1460 int da = a.Length();
1461 int db = b.Length();
1462
1463 if( da < db )
1464 guide = a;
1465 else
1466 guide = b;
1467
1468 initial = guide.Length();
1469
1470 int step = initial;
1471 int current = step;
1472 SHAPE_LINE_CHAIN snew;
1473
1474 while( step > 1 )
1475 {
1476 LINE l( cur );
1477
1478 snew.Clear();
1479 snew.Append( a.A );
1480 snew.Append( a.B + ( a.A - a.B ).Resize( current ) );
1481 snew.Append( b.A + ( b.B - b.A ).Resize( current ) );
1482 snew.Append( b.B );
1483
1484 step /= 2;
1485
1486 l.SetShape(snew);
1487
1488 if( aNode->CheckColliding(&l) )
1489 current -= step;
1490 else if ( current + step >= initial )
1491 current = initial;
1492 else
1493 current += step;
1494
1495 //dbg->AddSegment ( SEG( center.A , a.LineProject( center.A + gr ) ), 3 );
1496 //dbg->AddSegment ( SEG( center.A , center.A + guideA ), 3 );
1497 //dbg->AddSegment ( SEG( center.B , center.B + guideB ), 4 );
1498
1499 if ( current == initial )
1500 break;
1501 }
1502
1503 //dbg->AddLine ( snew, 3, 100000 );
1504
1505 out = std::move( snew );
1506 return true;
1507}
1508
1509
1510void Tighten( NODE *aNode, const SHAPE_LINE_CHAIN& aOldLine, const LINE& aNewLine,
1511 LINE& aOptimized )
1512{
1513 LINE tmp;
1514
1515 if( aNewLine.SegmentCount() < 3 )
1516 return;
1517
1518 SHAPE_LINE_CHAIN current ( aNewLine.CLine() );
1519
1520 for( int step = 0; step < 3; step++ )
1521 {
1522 current.Simplify2();
1523
1524 for( int i = 0; i <= current.SegmentCount() - 3; i++ )
1525 {
1526 SHAPE_LINE_CHAIN l_in, l_out;
1527
1528 l_in = current.Slice( i, i + 3 );
1529
1530 for( int dir = 0; dir <= 1; dir++ )
1531 {
1532 if( tightenSegment( dir ? true : false, aNode, aNewLine, l_in, l_out ) )
1533 {
1534 SHAPE_LINE_CHAIN opt = current;
1535 opt.Replace( i, i + 3, l_out );
1536 long long int optArea = std::abs( shovedArea( aOldLine, opt ) );
1537 long long int prevArea = std::abs( shovedArea( aOldLine, current ) );
1538
1539 if( optArea < prevArea )
1540 current = std::move( opt );
1541
1542 break;
1543 }
1544 }
1545 }
1546 }
1547
1548 aOptimized = LINE( aNewLine, current );
1549
1550 //auto dbg = ROUTER::GetInstance()->GetInterface()->GetDebugDecorator();
1551 //dbg->AddLine ( current, 4, 100000 );
1552}
1553
1554}
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
static int CornerCost(const SHAPE_LINE_CHAIN &aLine)
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: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: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:232
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:410
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
virtual DEBUG_DECORATOR * GetDebugDecorator()=0
ROUTER_IFACE * GetInterface() const
Definition pns_router.h:232
ROUTING_SETTINGS & Settings()
Definition pns_router.h:206
static ROUTER * GetInstance()
DIRECTION_45::CORNER_MODE GetCornerMode() const
VECTOR2I Offset() const
Definition pns_solid.h:133
const SHAPE * Shape(int aLayer) const override
Return the geometrical shape of the item.
Definition pns_solid.h:105
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:778
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:656
EDA_ANGLE Angle(const SEG &aOther) const
Determine the smallest angle between two segments.
Definition seg.cpp:102
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)
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