KiCad PCB EDA Suite
Loading...
Searching...
No Matches
eda_shape.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
6 * Copyright (C) 2011 Wayne Stambaugh <[email protected]>
7 * Copyright (C) 2023 CERN
8 * Copyright (C) 1992-2024 KiCad Developers, see AUTHORS.txt for contributors.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, you may find one here:
22 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
23 * or you may search the http://www.gnu.org website for the version 2 license,
24 * or you may write to the Free Software Foundation, Inc.,
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
26 */
27
28#include <eda_shape.h>
29
30#include <bezier_curves.h>
32#include <eda_draw_frame.h>
36#include <macros.h>
37#include <math/util.h> // for KiROUND
38#include <eda_item.h>
39#include <plotters/plotter.h>
40
41
42EDA_SHAPE::EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill ) :
43 m_endsSwapped( false ),
44 m_shape( aType ),
45 m_stroke( aLineWidth, LINE_STYLE::DEFAULT, COLOR4D::UNSPECIFIED ),
46 m_fill( aFill ),
47 m_fillColor( COLOR4D::UNSPECIFIED ),
48 m_rectangleHeight( 0 ),
49 m_rectangleWidth( 0 ),
50 m_segmentLength( 0 ),
51 m_editState( 0 ),
52 m_proxyItem( false )
53{
54}
55
56
58{
59}
60
61
62wxString EDA_SHAPE::ShowShape() const
63{
64 if( IsProxyItem() )
65 {
66 switch( m_shape )
67 {
68 case SHAPE_T::SEGMENT: return _( "Thermal Spoke" );
69 case SHAPE_T::RECTANGLE: return _( "Number Box" );
70 default: return wxT( "??" );
71 }
72 }
73 else
74 {
75 switch( m_shape )
76 {
77 case SHAPE_T::SEGMENT: return _( "Line" );
78 case SHAPE_T::RECTANGLE: return _( "Rect" );
79 case SHAPE_T::ARC: return _( "Arc" );
80 case SHAPE_T::CIRCLE: return _( "Circle" );
81 case SHAPE_T::BEZIER: return _( "Bezier Curve" );
82 case SHAPE_T::POLY: return _( "Polygon" );
83 default: return wxT( "??" );
84 }
85 }
86}
87
88
90{
91 switch( m_shape )
92 {
93 case SHAPE_T::SEGMENT: return wxS( "S_SEGMENT" );
94 case SHAPE_T::RECTANGLE: return wxS( "S_RECT" );
95 case SHAPE_T::ARC: return wxS( "S_ARC" );
96 case SHAPE_T::CIRCLE: return wxS( "S_CIRCLE" );
97 case SHAPE_T::POLY: return wxS( "S_POLYGON" );
98 case SHAPE_T::BEZIER: return wxS( "S_CURVE" );
99 case SHAPE_T::UNDEFINED: return wxS( "UNDEFINED" );
100 }
101
102 return wxEmptyString; // Just to quiet GCC.
103}
104
105
107{
108 move( aPos - getPosition() );
109}
110
111
113{
114 if( m_shape == SHAPE_T::ARC )
115 return getCenter();
116 else if( m_shape == SHAPE_T::POLY )
117 return m_poly.CVertex( 0 );
118 else
119 return m_start;
120}
121
122
124{
125 double length = 0.0;
126
127 switch( m_shape )
128 {
129 case SHAPE_T::BEZIER:
130 for( size_t ii = 1; ii < m_bezierPoints.size(); ++ii )
131 length += m_bezierPoints[ ii - 1].Distance( m_bezierPoints[ii] );
132
133 return length;
134
135 case SHAPE_T::SEGMENT:
136 return GetStart().Distance( GetEnd() );
137
138 case SHAPE_T::POLY:
139 for( int ii = 0; ii < m_poly.COutline( 0 ).SegmentCount(); ii++ )
140 length += m_poly.COutline( 0 ).CSegment( ii ).Length();
141
142 return length;
143
144 case SHAPE_T::ARC:
145 return GetRadius() * GetArcAngle().AsRadians();
146
147 default:
149 return 0.0;
150 }
151}
152
154{
155 switch( m_shape )
156 {
157 case SHAPE_T::RECTANGLE:
158 return GetEndY() - GetStartY();
159
160 default:
162 return 0;
163 }
164}
165
167{
168 switch( m_shape )
169 {
170 case SHAPE_T::RECTANGLE:
171 return GetEndX() - GetStartX();
172
173 default:
175 return 0;
176 }
177}
178
179void EDA_SHAPE::SetLength( const double& aLength )
180{
181 switch( m_shape )
182 {
183 case SHAPE_T::SEGMENT:
184 m_segmentLength = aLength; break;
185
186 default:
188 }
189}
190
191void EDA_SHAPE::SetRectangleHeight( const int& aHeight )
192{
193 switch ( m_shape )
194 {
195 case SHAPE_T::RECTANGLE:
196 m_rectangleHeight = aHeight;
198 break;
199
200 default:
202 }
203}
204
205void EDA_SHAPE::SetRectangleWidth( const int& aWidth )
206{
207 switch ( m_shape )
208 {
209 case SHAPE_T::RECTANGLE:
210 m_rectangleWidth = aWidth;
212 break;
213
214 default:
216 }
217}
218
219void EDA_SHAPE::SetRectangle( const long long int& aHeight, const long long int& aWidth )
220{
221 switch ( m_shape )
222 {
223 case SHAPE_T::RECTANGLE:
224 m_rectangleHeight = aHeight;
225 m_rectangleWidth = aWidth;
226 break;
227
228 default:
230 }
231}
232
233
235{
236 switch( m_shape )
237 {
238 case SHAPE_T::SEGMENT:
239 m_segmentAngle = aAngle;
240 break;
241
242 default:
244 }
245}
246
248{
249 switch( m_shape )
250 {
251 case SHAPE_T::CIRCLE:
252 case SHAPE_T::RECTANGLE:
253 return true;
254
255 case SHAPE_T::ARC:
256 case SHAPE_T::SEGMENT:
257 return false;
258
259 case SHAPE_T::POLY:
260 if( m_poly.IsEmpty() )
261 return false;
262 else
263 return m_poly.Outline( 0 ).IsClosed();
264
265 case SHAPE_T::BEZIER:
266 if( m_bezierPoints.size() < 3 )
267 return false;
268 else
269 return m_bezierPoints[0] == m_bezierPoints[ m_bezierPoints.size() - 1 ];
270
271 default:
273 return false;
274 }
275}
276
277
278
279void EDA_SHAPE::move( const VECTOR2I& aMoveVector )
280{
281 switch ( m_shape )
282 {
283 case SHAPE_T::ARC:
284 m_arcCenter += aMoveVector;
286
287 case SHAPE_T::SEGMENT:
288 case SHAPE_T::RECTANGLE:
289 case SHAPE_T::CIRCLE:
290 m_start += aMoveVector;
291 m_end += aMoveVector;
292 break;
293
294 case SHAPE_T::POLY:
295 m_poly.Move( aMoveVector );
296 break;
297
298 case SHAPE_T::BEZIER:
299 m_start += aMoveVector;
300 m_end += aMoveVector;
301 m_bezierC1 += aMoveVector;
302 m_bezierC2 += aMoveVector;
303
304 for( VECTOR2I& pt : m_bezierPoints )
305 pt += aMoveVector;
306
307 break;
308
309 default:
311 break;
312 }
313}
314
315
316void EDA_SHAPE::scale( double aScale )
317{
318 auto scalePt = [&]( VECTOR2I& pt )
319 {
320 pt.x = KiROUND( pt.x * aScale );
321 pt.y = KiROUND( pt.y * aScale );
322 };
323
324 switch( m_shape )
325 {
326 case SHAPE_T::ARC:
327 scalePt( m_arcCenter );
329
330 case SHAPE_T::SEGMENT:
331 case SHAPE_T::RECTANGLE:
332 scalePt( m_start );
333 scalePt( m_end );
334 break;
335
336 case SHAPE_T::CIRCLE: // ring or circle
337 scalePt( m_start );
338 m_end.x = m_start.x + KiROUND( GetRadius() * aScale );
339 m_end.y = m_start.y;
340 break;
341
342 case SHAPE_T::POLY: // polygon
343 {
344 std::vector<VECTOR2I> pts;
345
346 for( int ii = 0; ii < m_poly.OutlineCount(); ++ ii )
347 {
348 for( const VECTOR2I& pt : m_poly.Outline( ii ).CPoints() )
349 {
350 pts.emplace_back( pt );
351 scalePt( pts.back() );
352 }
353 }
354
355 SetPolyPoints( pts );
356 }
357 break;
358
359 case SHAPE_T::BEZIER:
360 scalePt( m_start );
361 scalePt( m_end );
362 scalePt( m_bezierC1 );
363 scalePt( m_bezierC2 );
365 break;
366
367 default:
369 break;
370 }
371}
372
373
374void EDA_SHAPE::rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
375{
376 switch( m_shape )
377 {
378 case SHAPE_T::SEGMENT:
379 case SHAPE_T::CIRCLE:
380 RotatePoint( m_start, aRotCentre, aAngle );
381 RotatePoint( m_end, aRotCentre, aAngle );
382 break;
383
384 case SHAPE_T::ARC:
385 RotatePoint( m_start, aRotCentre, aAngle );
386 RotatePoint( m_end, aRotCentre, aAngle );
387 RotatePoint( m_arcCenter, aRotCentre, aAngle );
388 break;
389
390 case SHAPE_T::RECTANGLE:
391 if( aAngle.IsCardinal() )
392 {
393 RotatePoint( m_start, aRotCentre, aAngle );
394 RotatePoint( m_end, aRotCentre, aAngle );
395 break;
396 }
397
398 // Convert non-cardinally-rotated rect to a diamond
399 m_shape = SHAPE_T::POLY;
406
408
409 case SHAPE_T::POLY:
410 m_poly.Rotate( aAngle, aRotCentre );
411 break;
412
413 case SHAPE_T::BEZIER:
414 RotatePoint( m_start, aRotCentre, aAngle );
415 RotatePoint( m_end, aRotCentre, aAngle );
416 RotatePoint( m_bezierC1, aRotCentre, aAngle );
417 RotatePoint( m_bezierC2, aRotCentre, aAngle );
418
419 for( VECTOR2I& pt : m_bezierPoints )
420 RotatePoint( pt, aRotCentre, aAngle);
421
422 break;
423
424 default:
426 break;
427 }
428}
429
430
431void EDA_SHAPE::flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
432{
433 switch ( m_shape )
434 {
435 case SHAPE_T::SEGMENT:
436 case SHAPE_T::RECTANGLE:
437 MIRROR( m_start, aCentre, aFlipDirection );
438 MIRROR( m_end, aCentre, aFlipDirection );
439 break;
440
441 case SHAPE_T::CIRCLE:
442 MIRROR( m_start, aCentre, aFlipDirection );
443 MIRROR( m_end, aCentre, aFlipDirection );
444 break;
445
446 case SHAPE_T::ARC:
447 MIRROR( m_start, aCentre, aFlipDirection );
448 MIRROR( m_end, aCentre, aFlipDirection );
449 MIRROR( m_arcCenter, aCentre, aFlipDirection );
450
451 std::swap( m_start, m_end );
452 break;
453
454 case SHAPE_T::POLY:
455 m_poly.Mirror( aCentre, aFlipDirection );
456 break;
457
458 case SHAPE_T::BEZIER:
459 MIRROR( m_start, aCentre, aFlipDirection );
460 MIRROR( m_end, aCentre, aFlipDirection );
461 MIRROR( m_bezierC1, aCentre, aFlipDirection );
462 MIRROR( m_bezierC2, aCentre, aFlipDirection );
463
465 break;
466
467 default:
469 break;
470 }
471}
472
473
475{
476 // Has meaning only for SHAPE_T::BEZIER
477 if( m_shape != SHAPE_T::BEZIER )
478 {
479 m_bezierPoints.clear();
480 return;
481 }
482
483 // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
485}
486
487
488const std::vector<VECTOR2I> EDA_SHAPE::buildBezierToSegmentsPointsList( int aMaxError ) const
489{
490 std::vector<VECTOR2I> bezierPoints;
491
492 // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
493 std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
494 BEZIER_POLY converter( ctrlPoints );
495 converter.GetPoly( bezierPoints, aMaxError );
496
497 return bezierPoints;
498}
499
500
502{
503 switch( m_shape )
504 {
505 case SHAPE_T::ARC:
506 return m_arcCenter;
507
508 case SHAPE_T::CIRCLE:
509 return m_start;
510
511 case SHAPE_T::SEGMENT:
512 // Midpoint of the line
513 return ( m_start + m_end ) / 2;
514
515 case SHAPE_T::POLY:
516 case SHAPE_T::RECTANGLE:
517 case SHAPE_T::BEZIER:
518 return getBoundingBox().Centre();
519
520 default:
522 return VECTOR2I();
523 }
524}
525
526
527void EDA_SHAPE::SetCenter( const VECTOR2I& aCenter )
528{
529 switch( m_shape )
530 {
531 case SHAPE_T::ARC:
532 m_arcCenter = aCenter;
533 break;
534
535 case SHAPE_T::CIRCLE:
536 m_start = aCenter;
537 break;
538
539 default:
541 }
542}
543
544
546{
547 // If none of the input data have changed since we loaded the arc,
548 // keep the original mid point data to minimize churn
551 return m_arcMidData.mid;
552
553 VECTOR2I mid = m_start;
554 RotatePoint( mid, m_arcCenter, -GetArcAngle() / 2.0 );
555 return mid;
556}
557
558
559void EDA_SHAPE::CalcArcAngles( EDA_ANGLE& aStartAngle, EDA_ANGLE& aEndAngle ) const
560{
561 VECTOR2D startRadial( GetStart() - getCenter() );
562 VECTOR2D endRadial( GetEnd() - getCenter() );
563
564 aStartAngle = EDA_ANGLE( startRadial );
565 aEndAngle = EDA_ANGLE( endRadial );
566
567 if( aEndAngle == aStartAngle )
568 aEndAngle = aStartAngle + ANGLE_360; // ring, not null
569
570 while( aEndAngle < aStartAngle )
571 aEndAngle += ANGLE_360;
572}
573
574
576{
577 double radius = 0.0;
578
579 switch( m_shape )
580 {
581 case SHAPE_T::ARC:
582 radius = m_arcCenter.Distance( m_start );
583 break;
584
585 case SHAPE_T::CIRCLE:
586 radius = m_start.Distance( m_end );
587 break;
588
589 default:
591 }
592
593 // don't allow degenerate circles/arcs
594 return std::max( 1, KiROUND( radius ) );
595}
596
597
598void EDA_SHAPE::SetCachedArcData( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCenter )
599{
600 m_arcMidData.start = aStart;
601 m_arcMidData.end = aEnd;
602 m_arcMidData.center = aCenter;
603 m_arcMidData.mid = aMid;
604}
605
606
607void EDA_SHAPE::SetArcGeometry( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
608{
609 m_arcMidData = {};
610 m_start = aStart;
611 m_end = aEnd;
612 m_arcCenter = CalcArcCenter( aStart, aMid, aEnd );
613 VECTOR2I new_mid = GetArcMid();
614
615 m_endsSwapped = false;
616
617 // Watch the ordering here. GetArcMid above needs to be called prior to initializing the
618 // m_arcMidData structure in order to ensure we get the calculated variant, not the cached
619 SetCachedArcData( aStart, aMid, aEnd, m_arcCenter );
620
621 /*
622 * If the input winding doesn't match our internal winding, the calculated midpoint will end
623 * up on the other side of the arc. In this case, we need to flip the start/end points and
624 * flag this change for the system.
625 */
626 VECTOR2D dist( new_mid - aMid );
627 VECTOR2D dist2( new_mid - m_arcCenter );
628
629 if( dist.SquaredEuclideanNorm() > dist2.SquaredEuclideanNorm() )
630 {
631 std::swap( m_start, m_end );
632 m_endsSwapped = true;
633 }
634}
635
637{
638 EDA_ANGLE angle( atan2( static_cast<double>( GetStart().y - GetEnd().y ),
639 static_cast<double>( GetEnd().x - GetStart().x ) ), RADIANS_T );
640
641 return angle;
642}
643
644
646{
647 EDA_ANGLE startAngle;
648 EDA_ANGLE endAngle;
649
650 CalcArcAngles( startAngle, endAngle );
651
652 return endAngle - startAngle;
653}
654
655
657{
658 if( m_shape == SHAPE_T::ARC )
659 {
660 VECTOR2D mid = GetArcMid();
661
662 double orient = ( mid.x - m_start.x ) * ( m_end.y - m_start.y )
663 - ( mid.y - m_start.y ) * ( m_end.x - m_start.x );
664
665 return orient < 0;
666 }
667
669 return false;
670}
671
672
673void EDA_SHAPE::SetArcAngleAndEnd( const EDA_ANGLE& aAngle, bool aCheckNegativeAngle )
674{
675 EDA_ANGLE angle( aAngle );
676
677 m_end = m_start;
679
680 if( aCheckNegativeAngle && aAngle < ANGLE_0 )
681 {
682 std::swap( m_start, m_end );
683 m_endsSwapped = true;
684 }
685}
686
687
689{
690 if( IsProxyItem() )
691 {
692 switch( m_shape )
693 {
694 case SHAPE_T::RECTANGLE: return _( "Pad Number Box" );
695 case SHAPE_T::SEGMENT: return _( "Thermal Spoke Template" );
696 default: return _( "Unrecognized" );
697 }
698 }
699 else
700 {
701 switch( m_shape )
702 {
703 case SHAPE_T::CIRCLE: return _( "Circle" );
704 case SHAPE_T::ARC: return _( "Arc" );
705 case SHAPE_T::BEZIER: return _( "Curve" );
706 case SHAPE_T::POLY: return _( "Polygon" );
707 case SHAPE_T::RECTANGLE: return _( "Rectangle" );
708 case SHAPE_T::SEGMENT: return _( "Segment" );
709 default: return _( "Unrecognized" );
710 }
711 }
712}
713
714
715void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
716{
717 wxString msg;
718
719 wxString shape = _( "Shape" );
720 aList.emplace_back( shape, getFriendlyName() );
721
722 switch( m_shape )
723 {
724 case SHAPE_T::CIRCLE:
725 aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
726 break;
727
728 case SHAPE_T::ARC:
730 aList.emplace_back( _( "Angle" ), msg );
731
732 aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
733 break;
734
735 case SHAPE_T::BEZIER:
736 aList.emplace_back( _( "Length" ), aFrame->MessageTextFromValue( GetLength() ) );
737 break;
738
739 case SHAPE_T::POLY:
740 msg.Printf( wxS( "%d" ), GetPolyShape().Outline(0).PointCount() );
741 aList.emplace_back( _( "Points" ), msg );
742 break;
743
744 case SHAPE_T::RECTANGLE:
745 aList.emplace_back( _( "Width" ),
746 aFrame->MessageTextFromValue( std::abs( GetEnd().x - GetStart().x ) ) );
747
748 aList.emplace_back( _( "Height" ),
749 aFrame->MessageTextFromValue( std::abs( GetEnd().y - GetStart().y ) ) );
750 break;
751
752 case SHAPE_T::SEGMENT:
753 {
754 aList.emplace_back( _( "Length" ),
755 aFrame->MessageTextFromValue( GetStart().Distance( GetEnd() ) ));
756
757 // angle counter-clockwise from 3'o-clock
758 EDA_ANGLE angle( atan2( (double)( GetStart().y - GetEnd().y ),
759 (double)( GetEnd().x - GetStart().x ) ), RADIANS_T );
760 aList.emplace_back( _( "Angle" ), EDA_UNIT_UTILS::UI::MessageTextFromValue( angle ) );
761 break;
762 }
763
764 default:
765 break;
766 }
767
768 m_stroke.GetMsgPanelInfo( aFrame, aList );
769}
770
771
773{
774 BOX2I bbox;
775
776 switch( m_shape )
777 {
778 case SHAPE_T::RECTANGLE:
779 for( VECTOR2I& pt : GetRectCorners() )
780 bbox.Merge( pt );
781
782 break;
783
784 case SHAPE_T::SEGMENT:
785 bbox.SetOrigin( GetStart() );
786 bbox.SetEnd( GetEnd() );
787 break;
788
789 case SHAPE_T::CIRCLE:
790 bbox.SetOrigin( GetStart() );
791 bbox.Inflate( GetRadius() );
792 break;
793
794 case SHAPE_T::ARC:
795 computeArcBBox( bbox );
796 break;
797
798 case SHAPE_T::POLY:
799 if( m_poly.IsEmpty() )
800 break;
801
802 for( auto iter = m_poly.CIterate(); iter; iter++ )
803 bbox.Merge( *iter );
804
805 break;
806
807 case SHAPE_T::BEZIER:
808 // Bezier BBoxes are not trivial to compute, so we approximate it by
809 // using the bounding box of the curve (not control!) points.
810 for( const VECTOR2I& pt : m_bezierPoints )
811 bbox.Merge( pt );
812 break;
813
814 default:
816 break;
817 }
818
819 bbox.Inflate( std::max( 0, GetWidth() ) / 2 );
820 bbox.Normalize();
821
822 return bbox;
823}
824
825
826bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
827{
828 double maxdist = aAccuracy;
829
830 if( GetWidth() > 0 )
831 maxdist += GetWidth() / 2.0;
832
833 switch( m_shape )
834 {
835 case SHAPE_T::CIRCLE:
836 {
837 double radius = GetRadius();
838 double dist = aPosition.Distance( getCenter() );
839
841 return dist <= radius + maxdist; // Filled circle hit-test
842 else
843 return abs( radius - dist ) <= maxdist; // Ring hit-test
844 }
845
846 case SHAPE_T::ARC:
847 {
848 if( aPosition.Distance( m_start ) <= maxdist )
849 return true;
850
851 if( aPosition.Distance( m_end ) <= maxdist )
852 return true;
853
854 double radius = GetRadius();
855 VECTOR2D relPos( VECTOR2D( aPosition ) - getCenter() );
856 double dist = relPos.EuclideanNorm();
857
859 {
860 // Check distance from arc center
861 if( dist > radius + maxdist )
862 return false;
863 }
864 else
865 {
866 // Check distance from arc circumference
867 if( abs( radius - dist ) > maxdist )
868 return false;
869 }
870
871 // Finally, check to see if it's within arc's swept angle.
872 EDA_ANGLE startAngle;
873 EDA_ANGLE endAngle;
874 CalcArcAngles( startAngle, endAngle );
875
876 EDA_ANGLE relPosAngle( relPos );
877
878 startAngle.Normalize();
879 endAngle.Normalize();
880 relPosAngle.Normalize();
881
882 if( endAngle > startAngle )
883 return relPosAngle >= startAngle && relPosAngle <= endAngle;
884 else
885 return relPosAngle >= startAngle || relPosAngle <= endAngle;
886 }
887
888 case SHAPE_T::BEZIER:
889 {
890 const std::vector<VECTOR2I>* pts = &m_bezierPoints;
891 std::vector<VECTOR2I> updatedBezierPoints;
892
893 if( m_bezierPoints.empty() )
894 {
896 converter.GetPoly( updatedBezierPoints, aAccuracy / 2 );
897 pts = &updatedBezierPoints;
898 }
899
900 for( unsigned int i = 1; i < pts->size(); i++ )
901 {
902 if( TestSegmentHit( aPosition, ( *pts )[i - 1], ( *pts )[i], maxdist ) )
903 return true;
904 }
905
906 return false;
907 }
908 case SHAPE_T::SEGMENT:
909 return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
910
911 case SHAPE_T::RECTANGLE:
912 if( IsProxyItem() || IsFilledForHitTesting() ) // Filled rect hit-test
913 {
914 SHAPE_POLY_SET poly;
915 poly.NewOutline();
916
917 for( const VECTOR2I& pt : GetRectCorners() )
918 poly.Append( pt );
919
920 return poly.Collide( aPosition, maxdist );
921 }
922 else // Open rect hit-test
923 {
924 std::vector<VECTOR2I> pts = GetRectCorners();
925
926 return TestSegmentHit( aPosition, pts[0], pts[1], maxdist )
927 || TestSegmentHit( aPosition, pts[1], pts[2], maxdist )
928 || TestSegmentHit( aPosition, pts[2], pts[3], maxdist )
929 || TestSegmentHit( aPosition, pts[3], pts[0], maxdist );
930 }
931
932 case SHAPE_T::POLY:
934 {
935 if( !m_poly.COutline( 0 ).IsClosed() )
936 {
938 copy.Outline( 0 ).Append( copy.Outline( 0 ).CPoint( 0 ) );
939 return copy.Collide( aPosition, maxdist );
940 }
941 else
942 {
943 return m_poly.Collide( aPosition, maxdist );
944 }
945 }
946 else
947 {
948 return m_poly.CollideEdge( aPosition, nullptr, maxdist );
949 }
950
951 default:
953 return false;
954 }
955}
956
957
958bool EDA_SHAPE::hitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
959{
960 BOX2I arect = aRect;
961 arect.Normalize();
962 arect.Inflate( aAccuracy );
963
964 BOX2I bbox = getBoundingBox();
965
966 switch( m_shape )
967 {
968 case SHAPE_T::CIRCLE:
969 // Test if area intersects or contains the circle:
970 if( aContained )
971 {
972 return arect.Contains( bbox );
973 }
974 else
975 {
976 // If the rectangle does not intersect the bounding box, this is a much quicker test
977 if( !arect.Intersects( bbox ) )
978 return false;
979 else
980 return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
981 }
982
983 case SHAPE_T::ARC:
984 // Test for full containment of this arc in the rect
985 if( aContained )
986 {
987 return arect.Contains( bbox );
988 }
989 // Test if the rect crosses the arc
990 else
991 {
992 if( !arect.Intersects( bbox ) )
993 return false;
994
995 if( IsFilled() )
996 {
997 return ( arect.Intersects( getCenter(), GetStart() )
998 || arect.Intersects( getCenter(), GetEnd() )
999 || arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() ) );
1000 }
1001 else
1002 {
1003 return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
1004 }
1005 }
1006
1007 case SHAPE_T::RECTANGLE:
1008 if( aContained )
1009 {
1010 return arect.Contains( bbox );
1011 }
1012 else
1013 {
1014 std::vector<VECTOR2I> pts = GetRectCorners();
1015
1016 // Account for the width of the lines
1017 arect.Inflate( GetWidth() / 2 );
1018 return ( arect.Intersects( pts[0], pts[1] )
1019 || arect.Intersects( pts[1], pts[2] )
1020 || arect.Intersects( pts[2], pts[3] )
1021 || arect.Intersects( pts[3], pts[0] ) );
1022 }
1023
1024 case SHAPE_T::SEGMENT:
1025 if( aContained )
1026 {
1027 return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
1028 }
1029 else
1030 {
1031 // Account for the width of the line
1032 arect.Inflate( GetWidth() / 2 );
1033 return arect.Intersects( GetStart(), GetEnd() );
1034 }
1035
1036 case SHAPE_T::POLY:
1037 if( aContained )
1038 {
1039 return arect.Contains( bbox );
1040 }
1041 else
1042 {
1043 // Fast test: if aRect is outside the polygon bounding box,
1044 // rectangles cannot intersect
1045 if( !arect.Intersects( bbox ) )
1046 return false;
1047
1048 // Account for the width of the line
1049 arect.Inflate( GetWidth() / 2 );
1050
1051 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1052 {
1053 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
1054 int count = poly.GetPointCount();
1055
1056 for( int jj = 0; jj < count; jj++ )
1057 {
1058 VECTOR2I vertex = poly.GetPoint( jj );
1059
1060 // Test if the point is within aRect
1061 if( arect.Contains( vertex ) )
1062 return true;
1063
1064 if( jj + 1 < count )
1065 {
1066 VECTOR2I vertexNext = poly.GetPoint( jj + 1 );
1067
1068 // Test if this edge intersects aRect
1069 if( arect.Intersects( vertex, vertexNext ) )
1070 return true;
1071 }
1072 else if( poly.IsClosed() )
1073 {
1074 VECTOR2I vertexNext = poly.GetPoint( 0 );
1075
1076 // Test if this edge intersects aRect
1077 if( arect.Intersects( vertex, vertexNext ) )
1078 return true;
1079 }
1080 }
1081 }
1082
1083 return false;
1084 }
1085
1086 case SHAPE_T::BEZIER:
1087 if( aContained )
1088 {
1089 return arect.Contains( bbox );
1090 }
1091 else
1092 {
1093 // Fast test: if aRect is outside the polygon bounding box,
1094 // rectangles cannot intersect
1095 if( !arect.Intersects( bbox ) )
1096 return false;
1097
1098 // Account for the width of the line
1099 arect.Inflate( GetWidth() / 2 );
1100 const std::vector<VECTOR2I>* pts = &m_bezierPoints;
1101 std::vector<VECTOR2I> updatedBezierPoints;
1102
1103 if( m_bezierPoints.empty() )
1104 {
1106 converter.GetPoly( updatedBezierPoints, aAccuracy / 2 );
1107 pts = &updatedBezierPoints;
1108 }
1109
1110 for( unsigned ii = 1; ii < pts->size(); ii++ )
1111 {
1112 VECTOR2I vertex = ( *pts )[ii - 1];
1113 VECTOR2I vertexNext = ( *pts )[ii];
1114
1115 // Test if the point is within aRect
1116 if( arect.Contains( vertex ) )
1117 return true;
1118
1119 // Test if this edge intersects aRect
1120 if( arect.Intersects( vertex, vertexNext ) )
1121 return true;
1122 }
1123
1124 return false;
1125 }
1126
1127 default:
1129 return false;
1130 }
1131}
1132
1133
1134std::vector<VECTOR2I> EDA_SHAPE::GetRectCorners() const
1135{
1136 std::vector<VECTOR2I> pts;
1137 VECTOR2I topLeft = GetStart();
1138 VECTOR2I botRight = GetEnd();
1139
1140 pts.emplace_back( topLeft );
1141 pts.emplace_back( botRight.x, topLeft.y );
1142 pts.emplace_back( botRight );
1143 pts.emplace_back( topLeft.x, botRight.y );
1144
1145 return pts;
1146}
1147
1148
1150{
1151 // Start, end, and each inflection point the arc crosses will enclose the entire arc.
1152 // Only include the center when filled; it's not necessarily inside the BB of an unfilled
1153 // arc with a small included angle.
1154 aBBox.SetOrigin( m_start );
1155 aBBox.Merge( m_end );
1156
1157 if( IsFilled() )
1158 aBBox.Merge( m_arcCenter );
1159
1160 int radius = GetRadius();
1161 EDA_ANGLE t1, t2;
1162
1163 CalcArcAngles( t1, t2 );
1164
1165 t1.Normalize();
1166 t2.Normalize();
1167
1168 if( t2 > t1 )
1169 {
1170 if( t1 < ANGLE_0 && t2 > ANGLE_0 )
1171 aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
1172
1173 if( t1 < ANGLE_90 && t2 > ANGLE_90 )
1174 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
1175
1176 if( t1 < ANGLE_180 && t2 > ANGLE_180 )
1177 aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
1178
1179 if( t1 < ANGLE_270 && t2 > ANGLE_270 )
1180 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
1181 }
1182 else
1183 {
1184 if( t1 < ANGLE_0 || t2 > ANGLE_0 )
1185 aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
1186
1187 if( t1 < ANGLE_90 || t2 > ANGLE_90 )
1188 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
1189
1190 if( t1 < ANGLE_180 || t2 > ANGLE_180 )
1191 aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
1192
1193 if( t1 < ANGLE_270 || t2 > ANGLE_270 )
1194 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
1195 }
1196}
1197
1198
1199void EDA_SHAPE::SetPolyPoints( const std::vector<VECTOR2I>& aPoints )
1200{
1203
1204 for( const VECTOR2I& p : aPoints )
1205 m_poly.Append( p.x, p.y );
1206}
1207
1208
1209std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineChainOnly ) const
1210{
1211 std::vector<SHAPE*> effectiveShapes;
1212 int width = GetEffectiveWidth();
1213
1214 switch( m_shape )
1215 {
1216 case SHAPE_T::ARC:
1217 effectiveShapes.emplace_back( new SHAPE_ARC( m_arcCenter, m_start, GetArcAngle(), width ) );
1218 break;
1219
1220 case SHAPE_T::SEGMENT:
1221 effectiveShapes.emplace_back( new SHAPE_SEGMENT( m_start, m_end, width ) );
1222 break;
1223
1224 case SHAPE_T::RECTANGLE:
1225 {
1226 std::vector<VECTOR2I> pts = GetRectCorners();
1227
1228 if( ( IsFilled() || IsProxyItem() ) && !aEdgeOnly )
1229 effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
1230
1231 if( width > 0 || !IsFilled() || aEdgeOnly )
1232 {
1233 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], width ) );
1234 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], width ) );
1235 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], width ) );
1236 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], width ) );
1237 }
1238 }
1239 break;
1240
1241 case SHAPE_T::CIRCLE:
1242 {
1243 if( IsFilled() && !aEdgeOnly )
1244 effectiveShapes.emplace_back( new SHAPE_CIRCLE( getCenter(), GetRadius() ) );
1245
1246 if( width > 0 || !IsFilled() || aEdgeOnly )
1247 effectiveShapes.emplace_back( new SHAPE_ARC( getCenter(), GetEnd(), ANGLE_360, width ) );
1248
1249 break;
1250 }
1251
1252 case SHAPE_T::BEZIER:
1253 {
1254 std::vector<VECTOR2I> bezierPoints = buildBezierToSegmentsPointsList( width / 2 );
1255 VECTOR2I start_pt = bezierPoints[0];
1256
1257 for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ )
1258 {
1259 VECTOR2I end_pt = bezierPoints[jj];
1260 effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, width ) );
1261 start_pt = end_pt;
1262 }
1263
1264 break;
1265 }
1266
1267 case SHAPE_T::POLY:
1268 {
1269 if( GetPolyShape().OutlineCount() == 0 ) // malformed/empty polygon
1270 break;
1271
1272 for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
1273 {
1274 const SHAPE_LINE_CHAIN& l = GetPolyShape().COutline( ii );
1275
1276 if( IsFilled() && !aEdgeOnly )
1277 effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) );
1278
1279 if( width > 0 || !IsFilled() || aEdgeOnly )
1280 {
1281 int segCount = l.SegmentCount();
1282
1283 if( aLineChainOnly && l.IsClosed() )
1284 segCount--; // Treat closed chain as open
1285
1286 for( int jj = 0; jj < segCount; jj++ )
1287 effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.CSegment( jj ), width ) );
1288 }
1289 }
1290 }
1291 break;
1292
1293 default:
1295 break;
1296 }
1297
1298 return effectiveShapes;
1299}
1300
1301
1302void EDA_SHAPE::DupPolyPointsList( std::vector<VECTOR2I>& aBuffer ) const
1303{
1304 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1305 {
1306 int pointCount = m_poly.COutline( ii ).PointCount();
1307
1308 if( pointCount )
1309 {
1310 aBuffer.reserve( pointCount );
1311
1312 for ( auto iter = m_poly.CIterate(); iter; iter++ )
1313 aBuffer.emplace_back( iter->x, iter->y );
1314 }
1315 }
1316}
1317
1318
1320{
1321 // return true if the polygonal shape is valid (has more than 2 points)
1322 return GetPolyShape().OutlineCount() > 0 && GetPolyShape().Outline( 0 ).PointCount() > 2;
1323}
1324
1325
1327{
1328 // return the number of corners of the polygonal shape
1329 // this shape is expected to be only one polygon without hole
1330 return GetPolyShape().OutlineCount() ? GetPolyShape().VertexCount( 0 ) : 0;
1331}
1332
1333
1334void EDA_SHAPE::beginEdit( const VECTOR2I& aPosition )
1335{
1336 switch( GetShape() )
1337 {
1338 case SHAPE_T::SEGMENT:
1339 case SHAPE_T::CIRCLE:
1340 case SHAPE_T::RECTANGLE:
1341 SetStart( aPosition );
1342 SetEnd( aPosition );
1343 break;
1344
1345 case SHAPE_T::ARC:
1346 SetArcGeometry( aPosition, aPosition, aPosition );
1347 m_editState = 1;
1348 break;
1349
1350 case SHAPE_T::BEZIER:
1351 SetStart( aPosition );
1352 SetEnd( aPosition );
1353 SetBezierC1( aPosition );
1354 SetBezierC2( aPosition );
1355 m_editState = 1;
1356
1358 break;
1359
1360 case SHAPE_T::POLY:
1362 m_poly.Outline( 0 ).SetClosed( false );
1363
1364 // Start and end of the first segment (co-located for now)
1365 m_poly.Outline( 0 ).Append( aPosition );
1366 m_poly.Outline( 0 ).Append( aPosition, true );
1367 break;
1368
1369 default:
1371 }
1372}
1373
1374
1375bool EDA_SHAPE::continueEdit( const VECTOR2I& aPosition )
1376{
1377 switch( GetShape() )
1378 {
1379 case SHAPE_T::ARC:
1380 case SHAPE_T::SEGMENT:
1381 case SHAPE_T::CIRCLE:
1382 case SHAPE_T::RECTANGLE:
1383 return false;
1384
1385 case SHAPE_T::BEZIER:
1386 if( m_editState == 3 )
1387 return false;
1388
1389 m_editState++;
1390 return true;
1391
1392 case SHAPE_T::POLY:
1393 {
1394 SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1395
1396 // do not add zero-length segments
1397 if( poly.CPoint( poly.GetPointCount() - 2 ) != poly.CLastPoint() )
1398 poly.Append( aPosition, true );
1399 }
1400 return true;
1401
1402 default:
1404 return false;
1405 }
1406}
1407
1408
1409void EDA_SHAPE::calcEdit( const VECTOR2I& aPosition )
1410{
1411#define sq( x ) pow( x, 2 )
1412
1413 switch( GetShape() )
1414 {
1415 case SHAPE_T::SEGMENT:
1416 case SHAPE_T::CIRCLE:
1417 case SHAPE_T::RECTANGLE:
1418 SetEnd( aPosition );
1419 break;
1420
1421 case SHAPE_T::BEZIER:
1422 {
1423 switch( m_editState )
1424 {
1425 case 0:
1426 SetStart( aPosition );
1427 SetEnd( aPosition );
1428 SetBezierC1( aPosition );
1429 SetBezierC2( aPosition );
1430 break;
1431 case 1:
1432 SetBezierC2( aPosition );
1433 SetEnd( aPosition );
1434 break;
1435 case 2: SetBezierC1( aPosition ); break;
1436 case 3: SetBezierC2( aPosition ); break;
1437 }
1438
1440 }
1441 break;
1442
1443 case SHAPE_T::ARC:
1444 {
1445 double radius = GetRadius();
1446 EDA_ANGLE lastAngle = GetArcAngle();
1447
1448 // Edit state 0: drawing: place start
1449 // Edit state 1: drawing: place end (center calculated for 90-degree subtended angle)
1450 // Edit state 2: point edit: move start (center calculated for invariant subtended angle)
1451 // Edit state 3: point edit: move end (center calculated for invariant subtended angle)
1452 // Edit state 4: point edit: move center
1453 // Edit state 5: point edit: move arc-mid-point
1454
1455 switch( m_editState )
1456 {
1457 case 0:
1458 SetArcGeometry( aPosition, aPosition, aPosition );
1459 return;
1460
1461 case 1:
1462 m_end = aPosition;
1463 radius = m_start.Distance( m_end ) * M_SQRT1_2;
1464 break;
1465
1466 case 2:
1467 case 3:
1468 {
1469 VECTOR2I v = m_start - m_end;
1470 double chordBefore = v.SquaredEuclideanNorm();
1471
1472 if( m_editState == 2 )
1473 m_start = aPosition;
1474 else
1475 m_end = aPosition;
1476
1477 v = m_start - m_end;
1478
1479 double chordAfter = v.SquaredEuclideanNorm();
1480 double ratio = 0.0;
1481
1482 if( chordBefore > 0 )
1483 ratio = chordAfter / chordBefore;
1484
1485 if( ratio != 0 )
1486 radius = std::max( sqrt( sq( radius ) * ratio ), sqrt( chordAfter ) / 2 );
1487 }
1488 break;
1489
1490 case 4:
1491 {
1492 double radialA = m_start.Distance( aPosition );
1493 double radialB = m_end.Distance( aPosition );
1494 radius = ( radialA + radialB ) / 2.0;
1495 }
1496 break;
1497
1498 case 5:
1499 SetArcGeometry( GetStart(), aPosition, GetEnd() );
1500 return;
1501 }
1502
1503 // Calculate center based on start, end, and radius
1504 //
1505 // Let 'l' be the length of the chord and 'm' the middle point of the chord
1506 double l = m_start.Distance( m_end );
1507 VECTOR2D m = ( m_start + m_end ) / 2;
1508 double sqRadDiff = ( radius * radius ) - ( l * l ) / 4.0;
1509
1510 // Calculate 'd', the vector from the chord midpoint to the center
1511 VECTOR2D d;
1512
1513 if( l > 0 && sqRadDiff >= 0 )
1514 {
1515 d.x = sqrt( sqRadDiff ) * ( m_start.y - m_end.y ) / l;
1516 d.y = sqrt( sqRadDiff ) * ( m_end.x - m_start.x ) / l;
1517 }
1518
1519 VECTOR2I c1 = KiROUND( m + d );
1520 VECTOR2I c2 = KiROUND( m - d );
1521
1522 // Solution gives us 2 centers; we need to pick one:
1523 switch( m_editState )
1524 {
1525 case 1:
1526 // Keep arc clockwise while drawing i.e. arc angle = 90 deg.
1527 // it can be 90 or 270 deg depending on the arc center choice (c1 or c2)
1528 m_arcCenter = c1; // first trial
1529
1530 if( GetArcAngle() > ANGLE_180 )
1531 m_arcCenter = c2;
1532
1533 break;
1534
1535 case 2:
1536 case 3:
1537 // Pick the one of c1, c2 to keep arc on the same side
1538 m_arcCenter = c1; // first trial
1539
1540 if( ( lastAngle < ANGLE_180 ) != ( GetArcAngle() < ANGLE_180 ) )
1541 m_arcCenter = c2;
1542
1543 break;
1544
1545 case 4:
1546 // Pick the one closer to the mouse position
1547 m_arcCenter = c1.Distance( aPosition ) < c2.Distance( aPosition ) ? c1 : c2;
1548 break;
1549 }
1550 }
1551 break;
1552
1553 case SHAPE_T::POLY:
1554 m_poly.Outline( 0 ).SetPoint( m_poly.Outline( 0 ).GetPointCount() - 1, aPosition );
1555 break;
1556
1557 default:
1559 }
1560}
1561
1562
1563void EDA_SHAPE::endEdit( bool aClosed )
1564{
1565 switch( GetShape() )
1566 {
1567 case SHAPE_T::ARC:
1568 case SHAPE_T::SEGMENT:
1569 case SHAPE_T::CIRCLE:
1570 case SHAPE_T::RECTANGLE:
1571 case SHAPE_T::BEZIER:
1572 break;
1573
1574 case SHAPE_T::POLY:
1575 {
1576 SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1577
1578 // do not include last point twice
1579 if( poly.GetPointCount() > 2 )
1580 {
1581 if( poly.CPoint( poly.GetPointCount() - 2 ) == poly.CLastPoint() )
1582 {
1583 poly.SetClosed( aClosed );
1584 }
1585 else
1586 {
1587 poly.SetClosed( false );
1588 poly.Remove( poly.GetPointCount() - 1 );
1589 }
1590 }
1591 }
1592 break;
1593
1594 default:
1596 }
1597}
1598
1599
1601{
1602 EDA_SHAPE* image = dynamic_cast<EDA_SHAPE*>( aImage );
1603 assert( image );
1604
1605 #define SWAPITEM( x ) std::swap( x, image->x )
1606 SWAPITEM( m_stroke );
1607 SWAPITEM( m_start );
1608 SWAPITEM( m_end );
1610 SWAPITEM( m_shape );
1614 SWAPITEM( m_poly );
1615 SWAPITEM( m_fill );
1619 #undef SWAPITEM
1620}
1621
1622
1623int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const
1624{
1625#define EPSILON 2 // Should be enough for rounding errors on calculated items
1626
1627#define TEST( a, b ) { if( a != b ) return a - b; }
1628#define TEST_E( a, b ) { if( abs( a - b ) > EPSILON ) return a - b; }
1629#define TEST_PT( a, b ) { TEST_E( a.x, b.x ); TEST_E( a.y, b.y ); }
1630
1631 TEST_PT( m_start, aOther->m_start );
1632 TEST_PT( m_end, aOther->m_end );
1633
1634 TEST( (int) m_shape, (int) aOther->m_shape );
1635
1636 if( m_shape == SHAPE_T::ARC )
1637 {
1638 TEST_PT( m_arcCenter, aOther->m_arcCenter );
1639 }
1640 else if( m_shape == SHAPE_T::BEZIER )
1641 {
1642 TEST_PT( m_bezierC1, aOther->m_bezierC1 );
1643 TEST_PT( m_bezierC2, aOther->m_bezierC2 );
1644 }
1645 else if( m_shape == SHAPE_T::POLY )
1646 {
1648 }
1649
1650 for( size_t ii = 0; ii < m_bezierPoints.size(); ++ii )
1651 TEST_PT( m_bezierPoints[ii], aOther->m_bezierPoints[ii] );
1652
1653 for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
1654 TEST_PT( m_poly.CVertex( ii ), aOther->m_poly.CVertex( ii ) );
1655
1656 TEST_E( m_stroke.GetWidth(), aOther->m_stroke.GetWidth() );
1657 TEST( (int) m_stroke.GetLineStyle(), (int) aOther->m_stroke.GetLineStyle() );
1658 TEST( (int) m_fill, (int) aOther->m_fill );
1659
1660 return 0;
1661}
1662
1663
1664void EDA_SHAPE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, int aClearance, int aError,
1665 ERROR_LOC aErrorLoc, bool ignoreLineWidth ) const
1666{
1667 int width = ignoreLineWidth ? 0 : GetWidth();
1668
1669 width += 2 * aClearance;
1670
1671 switch( m_shape )
1672 {
1673 case SHAPE_T::CIRCLE:
1674 {
1675 int r = GetRadius();
1676
1677 if( IsFilled() )
1678 TransformCircleToPolygon( aBuffer, getCenter(), r + width / 2, aError, aErrorLoc );
1679 else
1680 TransformRingToPolygon( aBuffer, getCenter(), r, width, aError, aErrorLoc );
1681
1682 break;
1683 }
1684
1685 case SHAPE_T::RECTANGLE:
1686 {
1687 std::vector<VECTOR2I> pts = GetRectCorners();
1688
1689 if( IsFilled() || IsProxyItem() )
1690 {
1691 aBuffer.NewOutline();
1692
1693 for( const VECTOR2I& pt : pts )
1694 aBuffer.Append( pt );
1695 }
1696
1697 if( width > 0 || !IsFilled() )
1698 {
1699 // Add in segments
1700 TransformOvalToPolygon( aBuffer, pts[0], pts[1], width, aError, aErrorLoc );
1701 TransformOvalToPolygon( aBuffer, pts[1], pts[2], width, aError, aErrorLoc );
1702 TransformOvalToPolygon( aBuffer, pts[2], pts[3], width, aError, aErrorLoc );
1703 TransformOvalToPolygon( aBuffer, pts[3], pts[0], width, aError, aErrorLoc );
1704 }
1705
1706 break;
1707 }
1708
1709 case SHAPE_T::ARC:
1710 TransformArcToPolygon( aBuffer, GetStart(), GetArcMid(), GetEnd(), width, aError, aErrorLoc );
1711 break;
1712
1713 case SHAPE_T::SEGMENT:
1714 TransformOvalToPolygon( aBuffer, GetStart(), GetEnd(), width, aError, aErrorLoc );
1715 break;
1716
1717 case SHAPE_T::POLY:
1718 {
1719 if( !IsPolyShapeValid() )
1720 break;
1721
1722 if( IsFilled() )
1723 {
1724 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1725 {
1726 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
1727 SHAPE_POLY_SET tmp;
1728 tmp.NewOutline();
1729
1730 for( int jj = 0; jj < (int) poly.GetPointCount(); ++jj )
1731 tmp.Append( poly.GetPoint( jj ) );
1732
1733 if( width > 0 )
1734 {
1735 int inflate = width / 2;
1736
1737 if( aErrorLoc == ERROR_OUTSIDE )
1738 inflate += aError;
1739
1740 tmp.Inflate( inflate, CORNER_STRATEGY::ROUND_ALL_CORNERS, aError );
1741 }
1742
1743 aBuffer.Append( tmp );
1744 }
1745 }
1746 else
1747 {
1748 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1749 {
1750 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
1751
1752 for( int jj = 0; jj < (int) poly.SegmentCount(); ++jj )
1753 {
1754 const SEG& seg = poly.GetSegment( jj );
1755 TransformOvalToPolygon( aBuffer, seg.A, seg.B, width, aError, aErrorLoc );
1756 }
1757 }
1758 }
1759
1760 break;
1761 }
1762
1763 case SHAPE_T::BEZIER:
1764 {
1765 std::vector<VECTOR2I> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
1766 BEZIER_POLY converter( ctrlPts );
1767 std::vector<VECTOR2I> poly;
1768 converter.GetPoly( poly, aError );
1769
1770 for( unsigned ii = 1; ii < poly.size(); ii++ )
1771 TransformOvalToPolygon( aBuffer, poly[ii - 1], poly[ii], width, aError, aErrorLoc );
1772
1773 break;
1774 }
1775
1776 default:
1778 break;
1779 }
1780}
1781
1782
1784{
1785 m_stroke.SetLineStyle( aStyle );
1786}
1787
1788
1790{
1791 if( m_stroke.GetLineStyle() != LINE_STYLE::DEFAULT )
1792 return m_stroke.GetLineStyle();
1793
1794 return LINE_STYLE::SOLID;
1795}
1796
1797
1798bool EDA_SHAPE::operator==( const EDA_SHAPE& aOther ) const
1799{
1800 if( GetShape() != aOther.GetShape() )
1801 return false;
1802
1803 if( m_fill != aOther.m_fill )
1804 return false;
1805
1806 if( m_stroke.GetWidth() != aOther.m_stroke.GetWidth() )
1807 return false;
1808
1809 if( m_stroke.GetLineStyle() != aOther.m_stroke.GetLineStyle() )
1810 return false;
1811
1812 if( m_fillColor != aOther.m_fillColor )
1813 return false;
1814
1815 if( m_start != aOther.m_start )
1816 return false;
1817
1818 if( m_end != aOther.m_end )
1819 return false;
1820
1821 if( m_arcCenter != aOther.m_arcCenter )
1822 return false;
1823
1824 if( m_bezierC1 != aOther.m_bezierC1 )
1825 return false;
1826
1827 if( m_bezierC2 != aOther.m_bezierC2 )
1828 return false;
1829
1830 if( m_bezierPoints != aOther.m_bezierPoints )
1831 return false;
1832
1833 for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
1834 {
1835 if( m_poly.CVertex( ii ) != aOther.m_poly.CVertex( ii ) )
1836 return false;
1837 }
1838
1839 return true;
1840}
1841
1842
1843double EDA_SHAPE::Similarity( const EDA_SHAPE& aOther ) const
1844{
1845 if( GetShape() != aOther.GetShape() )
1846 return 0.0;
1847
1848 double similarity = 1.0;
1849
1850 if( m_fill != aOther.m_fill )
1851 similarity *= 0.9;
1852
1853 if( m_stroke.GetWidth() != aOther.m_stroke.GetWidth() )
1854 similarity *= 0.9;
1855
1856 if( m_stroke.GetLineStyle() != aOther.m_stroke.GetLineStyle() )
1857 similarity *= 0.9;
1858
1859 if( m_fillColor != aOther.m_fillColor )
1860 similarity *= 0.9;
1861
1862 if( m_start != aOther.m_start )
1863 similarity *= 0.9;
1864
1865 if( m_end != aOther.m_end )
1866 similarity *= 0.9;
1867
1868 if( m_arcCenter != aOther.m_arcCenter )
1869 similarity *= 0.9;
1870
1871 if( m_bezierC1 != aOther.m_bezierC1 )
1872 similarity *= 0.9;
1873
1874 if( m_bezierC2 != aOther.m_bezierC2 )
1875 similarity *= 0.9;
1876
1877 {
1878 int m = m_bezierPoints.size();
1879 int n = aOther.m_bezierPoints.size();
1880
1881 size_t longest = alg::longest_common_subset( m_bezierPoints, aOther.m_bezierPoints );
1882
1883 similarity *= std::pow( 0.9, m + n - 2 * longest );
1884 }
1885
1886 {
1887 int m = m_poly.TotalVertices();
1888 int n = aOther.m_poly.TotalVertices();
1889 std::vector<VECTOR2I> poly;
1890 std::vector<VECTOR2I> otherPoly;
1891 VECTOR2I lastPt( 0, 0 );
1892
1893 // We look for the longest common subset of the two polygons, but we need to
1894 // offset each point because we're actually looking for overall similarity, not just
1895 // exact matches. So if the zone is moved by 1IU, we only want one point to be
1896 // considered "moved" rather than the entire polygon. In this case, the first point
1897 // will not be a match but the rest of the sequence will.
1898 for( int ii = 0; ii < m; ++ii )
1899 {
1900 poly.emplace_back( lastPt - m_poly.CVertex( ii ) );
1901 lastPt = m_poly.CVertex( ii );
1902 }
1903
1904 lastPt = VECTOR2I( 0, 0 );
1905
1906 for( int ii = 0; ii < n; ++ii )
1907 {
1908 otherPoly.emplace_back( lastPt - aOther.m_poly.CVertex( ii ) );
1909 lastPt = aOther.m_poly.CVertex( ii );
1910 }
1911
1912 size_t longest = alg::longest_common_subset( poly, otherPoly );
1913
1914 similarity *= std::pow( 0.9, m + n - 2 * longest );
1915 }
1916
1917 return similarity;
1918}
1919
1920
1923
1924
1925static struct EDA_SHAPE_DESC
1926{
1928 {
1930 .Map( SHAPE_T::SEGMENT, _HKI( "Segment" ) )
1931 .Map( SHAPE_T::RECTANGLE, _HKI( "Rectangle" ) )
1932 .Map( SHAPE_T::ARC, _HKI( "Arc" ) )
1933 .Map( SHAPE_T::CIRCLE, _HKI( "Circle" ) )
1934 .Map( SHAPE_T::POLY, _HKI( "Polygon" ) )
1935 .Map( SHAPE_T::BEZIER, _HKI( "Bezier" ) );
1936
1937 auto& plotDashTypeEnum = ENUM_MAP<LINE_STYLE>::Instance();
1938
1939 if( plotDashTypeEnum.Choices().GetCount() == 0 )
1940 {
1941 plotDashTypeEnum.Map( LINE_STYLE::DEFAULT, _HKI( "Default" ) )
1942 .Map( LINE_STYLE::SOLID, _HKI( "Solid" ) )
1943 .Map( LINE_STYLE::DASH, _HKI( "Dashed" ) )
1944 .Map( LINE_STYLE::DOT, _HKI( "Dotted" ) )
1945 .Map( LINE_STYLE::DASHDOT, _HKI( "Dash-Dot" ) )
1946 .Map( LINE_STYLE::DASHDOTDOT, _HKI( "Dash-Dot-Dot" ) );
1947 }
1948
1951
1952 auto isNotPolygonOrCircle = []( INSPECTABLE* aItem ) -> bool
1953 {
1954 // Polygons, unlike other shapes, have no meaningful start or end coordinates
1955 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
1956 return shape->GetShape() != SHAPE_T::POLY && shape->GetShape() != SHAPE_T::CIRCLE;
1957
1958 return false;
1959 };
1960
1961 auto isCircle = []( INSPECTABLE* aItem ) -> bool
1962 {
1963 // Polygons, unlike other shapes, have no meaningful start or end coordinates
1964 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
1965 return shape->GetShape() == SHAPE_T::CIRCLE;
1966
1967 return false;
1968 };
1969
1970 auto isRectangle = []( INSPECTABLE* aItem ) -> bool
1971 {
1972 // Polygons, unlike other shapes, have no meaningful start or end coordinates
1973 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
1974 return shape->GetShape() == SHAPE_T::RECTANGLE;
1975
1976 return false;
1977 };
1978
1979 const wxString shapeProps = _HKI( "Shape Properties" );
1980
1981 auto shape = new PROPERTY_ENUM<EDA_SHAPE, SHAPE_T>( _HKI( "Shape" ),
1983 propMgr.AddProperty( shape, shapeProps );
1984
1985 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start X" ),
1988 shapeProps )
1989 .SetAvailableFunc( isNotPolygonOrCircle );
1990 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start Y" ),
1993 shapeProps )
1994 .SetAvailableFunc( isNotPolygonOrCircle );
1995
1996 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Center X" ),
1999 shapeProps )
2000 .SetAvailableFunc( isCircle );
2001
2002 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Center Y" ),
2005 shapeProps )
2006 .SetAvailableFunc( isCircle );
2007
2008 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Radius" ),
2011 shapeProps )
2012 .SetAvailableFunc( isCircle );
2013
2014 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End X" ),
2017 shapeProps )
2018 .SetAvailableFunc( isNotPolygonOrCircle );
2019
2020 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End Y" ),
2023 shapeProps )
2024 .SetAvailableFunc( isNotPolygonOrCircle );
2025
2026 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Width" ),
2029 shapeProps )
2030 .SetAvailableFunc( isRectangle );
2031
2032 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Height" ),
2035 shapeProps )
2036 .SetAvailableFunc( isRectangle );
2037
2038 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Line Width" ),
2040 shapeProps );
2041
2042 propMgr.AddProperty( new PROPERTY_ENUM<EDA_SHAPE, LINE_STYLE>( _HKI( "Line Style" ),
2044 shapeProps );
2045
2046 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, COLOR4D>( _HKI( "Line Color" ),
2048 shapeProps )
2050
2051 auto angle = new PROPERTY<EDA_SHAPE, EDA_ANGLE>( _HKI( "Angle" ),
2054 angle->SetAvailableFunc(
2055 [=]( INSPECTABLE* aItem ) -> bool
2056 {
2057 if( EDA_SHAPE* curr_shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2058 return curr_shape->GetShape() == SHAPE_T::ARC;
2059
2060 return false;
2061 } );
2062 propMgr.AddProperty( angle, shapeProps );
2063
2064 auto fillAvailable =
2065 [=]( INSPECTABLE* aItem ) -> bool
2066 {
2067 if( EDA_ITEM* edaItem = dynamic_cast<EDA_ITEM*>( aItem ) )
2068 {
2069 // For some reason masking "Filled" and "Fill Color" at the
2070 // PCB_TABLECELL level doesn't work.
2071 if( edaItem->Type() == PCB_TABLECELL_T )
2072 return false;
2073 }
2074
2075 if( EDA_SHAPE* edaShape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2076 {
2077 switch( edaShape->GetShape() )
2078 {
2079 case SHAPE_T::POLY:
2080 case SHAPE_T::RECTANGLE:
2081 case SHAPE_T::CIRCLE:
2082 case SHAPE_T::BEZIER:
2083 return true;
2084
2085 default:
2086 return false;
2087 }
2088 }
2089
2090 return false;
2091 };
2092
2093 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, bool>( _HKI( "Filled" ),
2095 shapeProps )
2096 .SetAvailableFunc( fillAvailable );
2097
2098 auto fillColor = new PROPERTY<EDA_SHAPE, COLOR4D>( _HKI( "Fill Color" ),
2100 fillColor->SetWriteableFunc(
2101 [=]( INSPECTABLE* aItem ) -> bool
2102 {
2103 if( EDA_SHAPE* edaShape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2104 return edaShape->GetFillMode() == FILL_T::FILLED_WITH_COLOR;
2105
2106 return true;
2107 } );
2108 propMgr.AddProperty( fillColor, shapeProps )
2109 .SetAvailableFunc( fillAvailable )
2111 }
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
Definition: approximation.h:32
@ ERROR_OUTSIDE
Definition: approximation.h:33
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
Bezier curves to polygon converter.
Definition: bezier_curves.h:38
void GetPoly(std::vector< VECTOR2I > &aOutput, int aMaxError=10)
Convert a Bezier curve to a polygon.
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:558
constexpr void SetOrigin(const Vec &pos)
Definition: box2.h:237
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition: box2.h:146
constexpr Vec Centre() const
Definition: box2.h:97
bool IntersectsCircleEdge(const Vec &aCenter, const int aRadius, const int aWidth) const
Definition: box2.h:523
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:658
constexpr bool Contains(const Vec &aPoint) const
Definition: box2.h:168
constexpr void SetEnd(coord_type x, coord_type y)
Definition: box2.h:297
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:311
EDA_ANGLE Normalize()
Definition: eda_angle.h:221
bool IsCardinal() const
Definition: eda_angle.cpp:40
EDA_ANGLE Normalize720()
Definition: eda_angle.h:271
double AsRadians() const
Definition: eda_angle.h:117
The base class for create windows for drawing purpose.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:645
SHAPE_T m_shape
Definition: eda_shape.h:414
void SetStartX(int x)
Definition: eda_shape.h:146
bool m_endsSwapped
Definition: eda_shape.h:413
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:206
void SetBezierC2(const VECTOR2I &aPt)
Definition: eda_shape.h:205
void move(const VECTOR2I &aMoveVector)
Definition: eda_shape.cpp:279
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:527
VECTOR2I getCenter() const
Definition: eda_shape.cpp:501
int GetStartY() const
Definition: eda_shape.h:131
void SetLength(const double &aLength)
Definition: eda_shape.cpp:179
int m_editState
Definition: eda_shape.h:437
void rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle)
Definition: eda_shape.cpp:374
const std::vector< VECTOR2I > buildBezierToSegmentsPointsList(int aMaxError) const
Definition: eda_shape.cpp:488
virtual ~EDA_SHAPE()
Definition: eda_shape.cpp:57
long long int m_rectangleHeight
Definition: eda_shape.h:419
void SetEndY(int aY)
Definition: eda_shape.h:177
virtual int GetEffectiveWidth() const
Definition: eda_shape.h:116
COLOR4D GetLineColor() const
Definition: eda_shape.h:122
int GetEndX() const
Definition: eda_shape.h:169
int GetRectangleWidth() const
Definition: eda_shape.cpp:166
void SetLineStyle(const LINE_STYLE aStyle)
Definition: eda_shape.cpp:1783
void calcEdit(const VECTOR2I &aPosition)
Definition: eda_shape.cpp:1409
void SetStartY(int y)
Definition: eda_shape.h:140
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:279
bool IsFilled() const
Definition: eda_shape.h:91
void SetCenterY(int y)
Definition: eda_shape.h:152
void CalcArcAngles(EDA_ANGLE &aStartAngle, EDA_ANGLE &aEndAngle) const
Calc arc start and end angles such that aStartAngle < aEndAngle.
Definition: eda_shape.cpp:559
void ShapeGetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList)
Definition: eda_shape.cpp:715
void SetFilled(bool aFlag)
Definition: eda_shape.h:101
bool operator==(const EDA_SHAPE &aOther) const
Definition: eda_shape.cpp:1798
int GetRadius() const
Definition: eda_shape.cpp:575
SHAPE_T GetShape() const
Definition: eda_shape.h:125
void SetRectangleHeight(const int &aHeight)
Definition: eda_shape.cpp:191
VECTOR2I m_arcCenter
Definition: eda_shape.h:428
double m_segmentLength
Definition: eda_shape.h:422
void SetCenterX(int x)
Definition: eda_shape.h:158
virtual bool IsFilledForHitTesting() const
Definition: eda_shape.h:96
bool continueEdit(const VECTOR2I &aPosition)
Definition: eda_shape.cpp:1375
wxString ShowShape() const
Definition: eda_shape.cpp:62
ARC_MID m_arcMidData
Definition: eda_shape.h:429
void SetFillColor(const COLOR4D &aColor)
Definition: eda_shape.h:112
int GetEndY() const
Definition: eda_shape.h:168
std::vector< SHAPE * > makeEffectiveShapes(bool aEdgeOnly, bool aLineChainOnly=false) const
Make a set of SHAPE objects representing the EDA_SHAPE.
Definition: eda_shape.cpp:1209
bool hitTest(const VECTOR2I &aPosition, int aAccuracy=0) const
Definition: eda_shape.cpp:826
void SetCachedArcData(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, const VECTOR2I &aCenter)
Set the data used for mid point caching.
Definition: eda_shape.cpp:598
void SetEndX(int aX)
Definition: eda_shape.h:183
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:474
void flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection)
Definition: eda_shape.cpp:431
EDA_SHAPE(SHAPE_T aType, int aLineWidth, FILL_T aFill)
Definition: eda_shape.cpp:42
void beginEdit(const VECTOR2I &aStartPoint)
Definition: eda_shape.cpp:1334
VECTOR2I m_start
Definition: eda_shape.h:425
int GetPointCount() const
Definition: eda_shape.cpp:1326
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:167
bool IsClosed() const
Definition: eda_shape.cpp:247
void SetRadius(int aX)
Definition: eda_shape.h:189
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:134
void DupPolyPointsList(std::vector< VECTOR2I > &aBuffer) const
Duplicate the list of corners in a std::vector<VECTOR2I>
Definition: eda_shape.cpp:1302
LINE_STYLE GetLineStyle() const
Definition: eda_shape.cpp:1789
void endEdit(bool aClosed=true)
Finishes editing the shape.
Definition: eda_shape.cpp:1563
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:130
void SetLineColor(const COLOR4D &aColor)
Definition: eda_shape.h:121
COLOR4D GetFillColor() const
Definition: eda_shape.h:111
void SetRectangle(const long long int &aHeight, const long long int &aWidth)
Definition: eda_shape.cpp:219
void SwapShape(EDA_SHAPE *aImage)
Definition: eda_shape.cpp:1600
std::vector< VECTOR2I > GetRectCorners() const
Definition: eda_shape.cpp:1134
void SetSegmentAngle(const EDA_ANGLE &aAngle)
Definition: eda_shape.cpp:234
std::vector< VECTOR2I > m_bezierPoints
Definition: eda_shape.h:434
void setPosition(const VECTOR2I &aPos)
Definition: eda_shape.cpp:106
virtual bool IsProxyItem() const
Definition: eda_shape.h:88
void computeArcBBox(BOX2I &aBBox) const
Definition: eda_shape.cpp:1149
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:171
void SetRectangleWidth(const int &aWidth)
Definition: eda_shape.cpp:205
void SetBezierC1(const VECTOR2I &aPt)
Definition: eda_shape.h:202
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:607
double GetLength() const
Definition: eda_shape.cpp:123
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:89
void scale(double aScale)
Definition: eda_shape.cpp:316
int GetStartX() const
Definition: eda_shape.h:132
double Similarity(const EDA_SHAPE &aOther) const
Definition: eda_shape.cpp:1843
const VECTOR2I & GetBezierC1() const
Definition: eda_shape.h:203
VECTOR2I m_end
Definition: eda_shape.h:426
const BOX2I getBoundingBox() const
Definition: eda_shape.cpp:772
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:673
SHAPE_POLY_SET m_poly
Definition: eda_shape.h:435
int GetRectangleHeight() const
Definition: eda_shape.cpp:153
virtual int GetWidth() const
Definition: eda_shape.h:115
VECTOR2I getPosition() const
Definition: eda_shape.cpp:112
bool IsClockwiseArc() const
Definition: eda_shape.cpp:656
STROKE_PARAMS m_stroke
Definition: eda_shape.h:415
EDA_ANGLE m_segmentAngle
Definition: eda_shape.h:423
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
Definition: eda_shape.cpp:1199
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const
Convert the shape to a closed polygon.
Definition: eda_shape.cpp:1664
wxString getFriendlyName() const
Definition: eda_shape.cpp:688
VECTOR2I m_bezierC1
Definition: eda_shape.h:431
FILL_T m_fill
Definition: eda_shape.h:416
COLOR4D m_fillColor
Definition: eda_shape.h:417
void SetWidth(int aWidth)
Definition: eda_shape.h:114
EDA_ANGLE GetSegmentAngle() const
Definition: eda_shape.cpp:636
long long int m_rectangleWidth
Definition: eda_shape.h:420
VECTOR2I m_bezierC2
Definition: eda_shape.h:432
bool IsPolyShapeValid() const
Definition: eda_shape.cpp:1319
int Compare(const EDA_SHAPE *aOther) const
Definition: eda_shape.cpp:1623
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:545
static ENUM_MAP< T > & Instance()
Definition: property.h:663
Class that other classes need to inherit from, in order to be inspectable.
Definition: inspectable.h:36
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
PROPERTY_BASE & SetAvailableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Set a callback function to determine whether an object provides this property.
Definition: property.h:257
PROPERTY_BASE & SetIsHiddenFromRulesEditor(bool aHide=true)
Definition: property.h:307
Provide class metadata.Helper macro to map type hashes to names.
Definition: property_mgr.h:85
static PROPERTY_MANAGER & Instance()
Definition: property_mgr.h:87
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
int Length() const
Return the length (this).
Definition: seg.h:333
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsClosed() const override
virtual const VECTOR2I GetPoint(int aIndex) const override
void SetPoint(int aIndex, const VECTOR2I &aPos)
Move a point to a specific location.
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.
virtual size_t GetPointCount() const override
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
virtual const SEG GetSegment(int aIndex) const override
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
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.
const std::vector< VECTOR2I > & CPoints() const
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
bool CollideEdge(const VECTOR2I &aPoint, VERTEX_INDEX *aClosestVertex=nullptr, int aClearance=0) const
Check whether aPoint collides with any edge of any of the contours of the polygon.
int VertexCount(int aOutline=-1, int aHole=-1) const
Return the number of vertices in a given outline/hole.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
CONST_ITERATOR CIterate(int aFirst, int aLast, bool aIterateHoles=false) const
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
int TotalVertices() const
Return total number of vertices stored in the set.
void Inflate(int aAmount, CORNER_STRATEGY aCornerStrategy, int aMaxError, bool aSimplify=false)
Perform outline inflation/deflation.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Appends a vertex at the end of the given outline/hole (default: the last outline)
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int NewOutline()
Creates a new empty polygon in the set and returns its index.
void Mirror(const VECTOR2I &aRef, FLIP_DIRECTION aFlipDirection)
Mirror the line points about y or x (or both)
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the index-th vertex in a given hole outline within a given outline.
int OutlineCount() const
Return the number of outlines in the set.
void Move(const VECTOR2I &aVector) override
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Represent a simple polygon consisting of a zero-thickness closed chain of connected line segments.
Definition: shape_simple.h:42
int GetWidth() const
Definition: stroke_params.h:89
void SetLineStyle(LINE_STYLE aLineStyle)
Definition: stroke_params.h:93
void GetMsgPanelInfo(UNITS_PROVIDER *aUnitsProvider, std::vector< MSG_PANEL_ITEM > &aList, bool aIncludeStyle=true, bool aIncludeWidth=true)
LINE_STYLE GetLineStyle() const
Definition: stroke_params.h:92
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition: vector2d.h:557
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition: vector2d.h:307
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:283
void TransformRingToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aCentre, int aRadius, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arcs to multiple straight segments.
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
void TransformArcToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arc to multiple straight segments.
void TransformOvalToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a oblong shape to a polygon, using multiple segments.
#define _HKI(x)
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:401
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
@ RADIANS_T
Definition: eda_angle.h:32
static constexpr EDA_ANGLE ANGLE_270
Definition: eda_angle.h:406
static constexpr EDA_ANGLE ANGLE_360
Definition: eda_angle.h:407
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:405
#define TEST_PT(a, b)
#define TEST(a, b)
#define TEST_E(a, b)
static struct EDA_SHAPE_DESC _EDA_SHAPE_DESC
#define SWAPITEM(x)
#define sq(x)
SHAPE_T
Definition: eda_shape.h:42
@ ARC
use RECTANGLE instead of RECT to avoid collision in a Windows header
FILL_T
Definition: eda_shape.h:55
@ FILLED_WITH_COLOR
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:96
constexpr void MIRROR(T &aPoint, const T &aMirrorRef)
Updates aPoint with the mirror of aPoint relative to the aMirrorRef.
Definition: mirror.h:45
FLIP_DIRECTION
Definition: mirror.h:27
KICOMMON_API wxString MessageTextFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A helper to convert the double length aValue to a string in inches, millimeters, or unscaled units.
Definition: eda_units.cpp:408
size_t longest_common_subset(const _Container &__c1, const _Container &__c2)
Returns the length of the longest common subset of values between two containers.
Definition: kicad_algo.h:208
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:390
#define IMPLEMENT_ENUM_TO_WXANY(type)
Definition: property.h:763
#define NO_SETTER(owner, type)
Definition: property.h:774
@ PT_COORD
Coordinate expressed in distance units (mm/inch)
Definition: property.h:64
@ PT_DECIDEGREE
Angle expressed in decidegrees.
Definition: property.h:66
@ PT_SIZE
Size expressed in distance units (mm/inch)
Definition: property.h:62
#define REGISTER_TYPE(x)
Definition: property_mgr.h:371
LINE_STYLE
Dashed line types.
Definition: stroke_params.h:46
VECTOR2I end
Definition: eda_shape.h:68
VECTOR2I center
Definition: eda_shape.h:69
VECTOR2I start
Definition: eda_shape.h:67
VECTOR2I mid
Definition: eda_shape.h:66
bool TestSegmentHit(const VECTOR2I &aRefPoint, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aDist)
Test if aRefPoint is with aDistance on the line defined by aStart and aEnd.
Definition: trigo.cpp:175
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
const VECTOR2I CalcArcCenter(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Determine the center of an arc or circle given three points on its circumference.
Definition: trigo.cpp:521
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition: typeinfo.h:95
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691
VECTOR2< double > VECTOR2D
Definition: vector2d.h:690