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