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, bool aFlipLeftRight )
432{
433 switch ( m_shape )
434 {
435 case SHAPE_T::SEGMENT:
436 case SHAPE_T::RECTANGLE:
437 if( aFlipLeftRight )
438 {
439 m_start.x = aCentre.x - ( m_start.x - aCentre.x );
440 m_end.x = aCentre.x - ( m_end.x - aCentre.x );
441 }
442 else
443 {
444 m_start.y = aCentre.y - ( m_start.y - aCentre.y );
445 m_end.y = aCentre.y - ( m_end.y - aCentre.y );
446 }
447 break;
448
449 case SHAPE_T::CIRCLE:
450 if( aFlipLeftRight )
451 {
452 m_start.x = aCentre.x - ( m_start.x - aCentre.x );
453 m_end.x = aCentre.x - ( m_end.x - aCentre.x );
454 }
455 else
456 {
457 m_start.y = aCentre.y - ( m_start.y - aCentre.y );
458 m_end.y = aCentre.y - ( m_end.y - aCentre.y );
459 }
460 break;
461
462 case SHAPE_T::ARC:
463 if( aFlipLeftRight )
464 {
465 m_start.x = aCentre.x - ( m_start.x - aCentre.x );
466 m_end.x = aCentre.x - ( m_end.x - aCentre.x );
467 m_arcCenter.x = aCentre.x - ( m_arcCenter.x - aCentre.x );
468 }
469 else
470 {
471 m_start.y = aCentre.y - ( m_start.y - aCentre.y );
472 m_end.y = aCentre.y - ( m_end.y - aCentre.y );
473 m_arcCenter.y = aCentre.y - ( m_arcCenter.y - aCentre.y );
474 }
475
476 std::swap( m_start, m_end );
477 break;
478
479 case SHAPE_T::POLY:
480 m_poly.Mirror( aFlipLeftRight, !aFlipLeftRight, aCentre );
481 break;
482
483 case SHAPE_T::BEZIER:
484 if( aFlipLeftRight )
485 {
486 m_start.x = aCentre.x - ( m_start.x - aCentre.x );
487 m_end.x = aCentre.x - ( m_end.x - aCentre.x );
488 m_bezierC1.x = aCentre.x - ( m_bezierC1.x - aCentre.x );
489 m_bezierC2.x = aCentre.x - ( m_bezierC2.x - aCentre.x );
490 }
491 else
492 {
493 m_start.y = aCentre.y - ( m_start.y - aCentre.y );
494 m_end.y = aCentre.y - ( m_end.y - aCentre.y );
495 m_bezierC1.y = aCentre.y - ( m_bezierC1.y - aCentre.y );
496 m_bezierC2.y = aCentre.y - ( m_bezierC2.y - aCentre.y );
497 }
498
500 break;
501
502 default:
504 break;
505 }
506}
507
508
510{
511 // Has meaning only for SHAPE_T::BEZIER
512 if( m_shape != SHAPE_T::BEZIER )
513 {
514 m_bezierPoints.clear();
515 return;
516 }
517
518 // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
520}
521
522
523const std::vector<VECTOR2I> EDA_SHAPE::buildBezierToSegmentsPointsList( int aMaxError ) const
524{
525 std::vector<VECTOR2I> bezierPoints;
526
527 // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
528 std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
529 BEZIER_POLY converter( ctrlPoints );
530 converter.GetPoly( bezierPoints, aMaxError );
531
532 return bezierPoints;
533}
534
535
537{
538 switch( m_shape )
539 {
540 case SHAPE_T::ARC:
541 return m_arcCenter;
542
543 case SHAPE_T::CIRCLE:
544 return m_start;
545
546 case SHAPE_T::SEGMENT:
547 // Midpoint of the line
548 return ( m_start + m_end ) / 2;
549
550 case SHAPE_T::POLY:
551 case SHAPE_T::RECTANGLE:
552 case SHAPE_T::BEZIER:
553 return getBoundingBox().Centre();
554
555 default:
557 return VECTOR2I();
558 }
559}
560
561
562void EDA_SHAPE::SetCenter( const VECTOR2I& aCenter )
563{
564 switch( m_shape )
565 {
566 case SHAPE_T::ARC:
567 m_arcCenter = aCenter;
568 break;
569
570 case SHAPE_T::CIRCLE:
571 m_start = aCenter;
572 break;
573
574 default:
576 }
577}
578
579
581{
582 // If none of the input data have changed since we loaded the arc,
583 // keep the original mid point data to minimize churn
586 return m_arcMidData.mid;
587
588 VECTOR2I mid = m_start;
589 RotatePoint( mid, m_arcCenter, -GetArcAngle() / 2.0 );
590 return mid;
591}
592
593
594void EDA_SHAPE::CalcArcAngles( EDA_ANGLE& aStartAngle, EDA_ANGLE& aEndAngle ) const
595{
596 VECTOR2D startRadial( GetStart() - getCenter() );
597 VECTOR2D endRadial( GetEnd() - getCenter() );
598
599 aStartAngle = EDA_ANGLE( startRadial );
600 aEndAngle = EDA_ANGLE( endRadial );
601
602 if( aEndAngle == aStartAngle )
603 aEndAngle = aStartAngle + ANGLE_360; // ring, not null
604
605 while( aEndAngle < aStartAngle )
606 aEndAngle += ANGLE_360;
607}
608
609
611{
612 double radius = 0.0;
613
614 switch( m_shape )
615 {
616 case SHAPE_T::ARC:
617 radius = m_arcCenter.Distance( m_start );
618 break;
619
620 case SHAPE_T::CIRCLE:
621 radius = m_start.Distance( m_end );
622 break;
623
624 default:
626 }
627
628 // don't allow degenerate circles/arcs
629 return std::max( 1, KiROUND( radius ) );
630}
631
632
633void EDA_SHAPE::SetCachedArcData( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCenter )
634{
635 m_arcMidData.start = aStart;
636 m_arcMidData.end = aEnd;
637 m_arcMidData.center = aCenter;
638 m_arcMidData.mid = aMid;
639}
640
641
642void EDA_SHAPE::SetArcGeometry( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
643{
644 m_arcMidData = {};
645 m_start = aStart;
646 m_end = aEnd;
647 m_arcCenter = CalcArcCenter( aStart, aMid, aEnd );
648 VECTOR2I new_mid = GetArcMid();
649
650 m_endsSwapped = false;
651
652 // Watch the ordering here. GetArcMid above needs to be called prior to initializing the
653 // m_arcMidData structure in order to ensure we get the calculated variant, not the cached
654 SetCachedArcData( aStart, aMid, aEnd, m_arcCenter );
655
656 /*
657 * If the input winding doesn't match our internal winding, the calculated midpoint will end
658 * up on the other side of the arc. In this case, we need to flip the start/end points and
659 * flag this change for the system.
660 */
661 VECTOR2D dist( new_mid - aMid );
662 VECTOR2D dist2( new_mid - m_arcCenter );
663
664 if( dist.SquaredEuclideanNorm() > dist2.SquaredEuclideanNorm() )
665 {
666 std::swap( m_start, m_end );
667 m_endsSwapped = true;
668 }
669}
670
672{
673 EDA_ANGLE angle( atan2( static_cast<double>( GetStart().y - GetEnd().y ),
674 static_cast<double>( GetEnd().x - GetStart().x ) ), RADIANS_T );
675
676 return angle;
677}
678
679
681{
682 EDA_ANGLE startAngle;
683 EDA_ANGLE endAngle;
684
685 CalcArcAngles( startAngle, endAngle );
686
687 return endAngle - startAngle;
688}
689
690
692{
693 if( m_shape == SHAPE_T::ARC )
694 {
695 VECTOR2D mid = GetArcMid();
696
697 double orient = ( mid.x - m_start.x ) * ( m_end.y - m_start.y )
698 - ( mid.y - m_start.y ) * ( m_end.x - m_start.x );
699
700 return orient < 0;
701 }
702
704 return false;
705}
706
707
708void EDA_SHAPE::SetArcAngleAndEnd( const EDA_ANGLE& aAngle, bool aCheckNegativeAngle )
709{
710 EDA_ANGLE angle( aAngle );
711
712 m_end = m_start;
714
715 if( aCheckNegativeAngle && aAngle < ANGLE_0 )
716 {
717 std::swap( m_start, m_end );
718 m_endsSwapped = true;
719 }
720}
721
722
724{
725 if( IsProxyItem() )
726 {
727 switch( m_shape )
728 {
729 case SHAPE_T::RECTANGLE: return _( "Pad Number Box" );
730 case SHAPE_T::SEGMENT: return _( "Thermal Spoke Template" );
731 default: return _( "Unrecognized" );
732 }
733 }
734 else
735 {
736 switch( m_shape )
737 {
738 case SHAPE_T::CIRCLE: return _( "Circle" );
739 case SHAPE_T::ARC: return _( "Arc" );
740 case SHAPE_T::BEZIER: return _( "Curve" );
741 case SHAPE_T::POLY: return _( "Polygon" );
742 case SHAPE_T::RECTANGLE: return _( "Rectangle" );
743 case SHAPE_T::SEGMENT: return _( "Segment" );
744 default: return _( "Unrecognized" );
745 }
746 }
747}
748
749
750void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
751{
752 wxString msg;
753
754 wxString shape = _( "Shape" );
755 aList.emplace_back( shape, getFriendlyName() );
756
757 switch( m_shape )
758 {
759 case SHAPE_T::CIRCLE:
760 aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
761 break;
762
763 case SHAPE_T::ARC:
765 aList.emplace_back( _( "Angle" ), msg );
766
767 aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
768 break;
769
770 case SHAPE_T::BEZIER:
771 aList.emplace_back( _( "Length" ), aFrame->MessageTextFromValue( GetLength() ) );
772 break;
773
774 case SHAPE_T::POLY:
775 msg.Printf( wxS( "%d" ), GetPolyShape().Outline(0).PointCount() );
776 aList.emplace_back( _( "Points" ), msg );
777 break;
778
779 case SHAPE_T::RECTANGLE:
780 aList.emplace_back( _( "Width" ),
781 aFrame->MessageTextFromValue( std::abs( GetEnd().x - GetStart().x ) ) );
782
783 aList.emplace_back( _( "Height" ),
784 aFrame->MessageTextFromValue( std::abs( GetEnd().y - GetStart().y ) ) );
785 break;
786
787 case SHAPE_T::SEGMENT:
788 {
789 aList.emplace_back( _( "Length" ),
790 aFrame->MessageTextFromValue( GetStart().Distance( GetEnd() ) ));
791
792 // angle counter-clockwise from 3'o-clock
793 EDA_ANGLE angle( atan2( (double)( GetStart().y - GetEnd().y ),
794 (double)( GetEnd().x - GetStart().x ) ), RADIANS_T );
795 aList.emplace_back( _( "Angle" ), EDA_UNIT_UTILS::UI::MessageTextFromValue( angle ) );
796 break;
797 }
798
799 default:
800 break;
801 }
802
803 m_stroke.GetMsgPanelInfo( aFrame, aList );
804}
805
806
808{
809 BOX2I bbox;
810
811 switch( m_shape )
812 {
813 case SHAPE_T::RECTANGLE:
814 for( VECTOR2I& pt : GetRectCorners() )
815 bbox.Merge( pt );
816
817 break;
818
819 case SHAPE_T::SEGMENT:
820 bbox.SetOrigin( GetStart() );
821 bbox.SetEnd( GetEnd() );
822 break;
823
824 case SHAPE_T::CIRCLE:
825 bbox.SetOrigin( GetStart() );
826 bbox.Inflate( GetRadius() );
827 break;
828
829 case SHAPE_T::ARC:
830 computeArcBBox( bbox );
831 break;
832
833 case SHAPE_T::POLY:
834 if( m_poly.IsEmpty() )
835 break;
836
837 for( auto iter = m_poly.CIterate(); iter; iter++ )
838 bbox.Merge( *iter );
839
840 break;
841
842 case SHAPE_T::BEZIER:
843 bbox.SetOrigin( GetStart() );
844 bbox.Merge( GetBezierC1() );
845 bbox.Merge( GetBezierC2() );
846 bbox.Merge( GetEnd() );
847 break;
848
849 default:
851 break;
852 }
853
854 bbox.Inflate( std::max( 0, GetWidth() ) / 2 );
855 bbox.Normalize();
856
857 return bbox;
858}
859
860
861bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
862{
863 double maxdist = aAccuracy;
864
865 if( GetWidth() > 0 )
866 maxdist += GetWidth() / 2.0;
867
868 switch( m_shape )
869 {
870 case SHAPE_T::CIRCLE:
871 {
872 double radius = GetRadius();
873 double dist = aPosition.Distance( getCenter() );
874
876 return dist <= radius + maxdist; // Filled circle hit-test
877 else
878 return abs( radius - dist ) <= maxdist; // Ring hit-test
879 }
880
881 case SHAPE_T::ARC:
882 {
883 if( aPosition.Distance( m_start ) <= maxdist )
884 return true;
885
886 if( aPosition.Distance( m_end ) <= maxdist )
887 return true;
888
889 double radius = GetRadius();
890 VECTOR2D relPos( VECTOR2D( aPosition ) - getCenter() );
891 double dist = relPos.EuclideanNorm();
892
894 {
895 // Check distance from arc center
896 if( dist > radius + maxdist )
897 return false;
898 }
899 else
900 {
901 // Check distance from arc circumference
902 if( abs( radius - dist ) > maxdist )
903 return false;
904 }
905
906 // Finally, check to see if it's within arc's swept angle.
907 EDA_ANGLE startAngle;
908 EDA_ANGLE endAngle;
909 CalcArcAngles( startAngle, endAngle );
910
911 EDA_ANGLE relPosAngle( relPos );
912
913 startAngle.Normalize();
914 endAngle.Normalize();
915 relPosAngle.Normalize();
916
917 if( endAngle > startAngle )
918 return relPosAngle >= startAngle && relPosAngle <= endAngle;
919 else
920 return relPosAngle >= startAngle || relPosAngle <= endAngle;
921 }
922
923 case SHAPE_T::BEZIER:
924 {
925 const std::vector<VECTOR2I>* pts = &m_bezierPoints;
926 std::vector<VECTOR2I> updatedBezierPoints;
927
928 if( m_bezierPoints.empty() )
929 {
931 converter.GetPoly( updatedBezierPoints, aAccuracy / 2 );
932 pts = &updatedBezierPoints;
933 }
934
935 for( unsigned int i = 1; i < pts->size(); i++ )
936 {
937 if( TestSegmentHit( aPosition, ( *pts )[i - 1], ( *pts )[i], maxdist ) )
938 return true;
939 }
940
941 return false;
942 }
943 case SHAPE_T::SEGMENT:
944 return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
945
946 case SHAPE_T::RECTANGLE:
947 if( IsProxyItem() || IsFilledForHitTesting() ) // Filled rect hit-test
948 {
949 SHAPE_POLY_SET poly;
950 poly.NewOutline();
951
952 for( const VECTOR2I& pt : GetRectCorners() )
953 poly.Append( pt );
954
955 return poly.Collide( aPosition, maxdist );
956 }
957 else // Open rect hit-test
958 {
959 std::vector<VECTOR2I> pts = GetRectCorners();
960
961 return TestSegmentHit( aPosition, pts[0], pts[1], maxdist )
962 || TestSegmentHit( aPosition, pts[1], pts[2], maxdist )
963 || TestSegmentHit( aPosition, pts[2], pts[3], maxdist )
964 || TestSegmentHit( aPosition, pts[3], pts[0], maxdist );
965 }
966
967 case SHAPE_T::POLY:
969 {
970 if( !m_poly.COutline( 0 ).IsClosed() )
971 {
973 copy.Outline( 0 ).Append( copy.Outline( 0 ).CPoint( 0 ) );
974 return copy.Collide( aPosition, maxdist );
975 }
976 else
977 {
978 return m_poly.Collide( aPosition, maxdist );
979 }
980 }
981 else
982 {
983 return m_poly.CollideEdge( aPosition, nullptr, maxdist );
984 }
985
986 default:
988 return false;
989 }
990}
991
992
993bool EDA_SHAPE::hitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
994{
995 BOX2I arect = aRect;
996 arect.Normalize();
997 arect.Inflate( aAccuracy );
998
999 BOX2I bbox = getBoundingBox();
1000
1001 switch( m_shape )
1002 {
1003 case SHAPE_T::CIRCLE:
1004 // Test if area intersects or contains the circle:
1005 if( aContained )
1006 {
1007 return arect.Contains( bbox );
1008 }
1009 else
1010 {
1011 // If the rectangle does not intersect the bounding box, this is a much quicker test
1012 if( !arect.Intersects( bbox ) )
1013 return false;
1014 else
1015 return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
1016 }
1017
1018 case SHAPE_T::ARC:
1019 // Test for full containment of this arc in the rect
1020 if( aContained )
1021 {
1022 return arect.Contains( bbox );
1023 }
1024 // Test if the rect crosses the arc
1025 else
1026 {
1027 if( !arect.Intersects( bbox ) )
1028 return false;
1029
1030 if( IsFilled() )
1031 {
1032 return ( arect.Intersects( getCenter(), GetStart() )
1033 || arect.Intersects( getCenter(), GetEnd() )
1034 || arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() ) );
1035 }
1036 else
1037 {
1038 return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
1039 }
1040 }
1041
1042 case SHAPE_T::RECTANGLE:
1043 if( aContained )
1044 {
1045 return arect.Contains( bbox );
1046 }
1047 else
1048 {
1049 std::vector<VECTOR2I> pts = GetRectCorners();
1050
1051 // Account for the width of the lines
1052 arect.Inflate( GetWidth() / 2 );
1053 return ( arect.Intersects( pts[0], pts[1] )
1054 || arect.Intersects( pts[1], pts[2] )
1055 || arect.Intersects( pts[2], pts[3] )
1056 || arect.Intersects( pts[3], pts[0] ) );
1057 }
1058
1059 case SHAPE_T::SEGMENT:
1060 if( aContained )
1061 {
1062 return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
1063 }
1064 else
1065 {
1066 // Account for the width of the line
1067 arect.Inflate( GetWidth() / 2 );
1068 return arect.Intersects( GetStart(), GetEnd() );
1069 }
1070
1071 case SHAPE_T::POLY:
1072 if( aContained )
1073 {
1074 return arect.Contains( bbox );
1075 }
1076 else
1077 {
1078 // Fast test: if aRect is outside the polygon bounding box,
1079 // rectangles cannot intersect
1080 if( !arect.Intersects( bbox ) )
1081 return false;
1082
1083 // Account for the width of the line
1084 arect.Inflate( GetWidth() / 2 );
1085
1086 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1087 {
1088 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
1089 int count = poly.GetPointCount();
1090
1091 for( int jj = 0; jj < count; jj++ )
1092 {
1093 VECTOR2I vertex = poly.GetPoint( jj );
1094
1095 // Test if the point is within aRect
1096 if( arect.Contains( vertex ) )
1097 return true;
1098
1099 if( jj + 1 < count )
1100 {
1101 VECTOR2I vertexNext = poly.GetPoint( jj + 1 );
1102
1103 // Test if this edge intersects aRect
1104 if( arect.Intersects( vertex, vertexNext ) )
1105 return true;
1106 }
1107 else if( poly.IsClosed() )
1108 {
1109 VECTOR2I vertexNext = poly.GetPoint( 0 );
1110
1111 // Test if this edge intersects aRect
1112 if( arect.Intersects( vertex, vertexNext ) )
1113 return true;
1114 }
1115 }
1116 }
1117
1118 return false;
1119 }
1120
1121 case SHAPE_T::BEZIER:
1122 if( aContained )
1123 {
1124 return arect.Contains( bbox );
1125 }
1126 else
1127 {
1128 // Fast test: if aRect is outside the polygon bounding box,
1129 // rectangles cannot intersect
1130 if( !arect.Intersects( bbox ) )
1131 return false;
1132
1133 // Account for the width of the line
1134 arect.Inflate( GetWidth() / 2 );
1135 const std::vector<VECTOR2I>* pts = &m_bezierPoints;
1136 std::vector<VECTOR2I> updatedBezierPoints;
1137
1138 if( m_bezierPoints.empty() )
1139 {
1141 converter.GetPoly( updatedBezierPoints, aAccuracy / 2 );
1142 pts = &updatedBezierPoints;
1143 }
1144
1145 for( unsigned ii = 1; ii < pts->size(); ii++ )
1146 {
1147 VECTOR2I vertex = ( *pts )[ii - 1];
1148 VECTOR2I vertexNext = ( *pts )[ii];
1149
1150 // Test if the point is within aRect
1151 if( arect.Contains( vertex ) )
1152 return true;
1153
1154 // Test if this edge intersects aRect
1155 if( arect.Intersects( vertex, vertexNext ) )
1156 return true;
1157 }
1158
1159 return false;
1160 }
1161
1162 default:
1164 return false;
1165 }
1166}
1167
1168
1169std::vector<VECTOR2I> EDA_SHAPE::GetRectCorners() const
1170{
1171 std::vector<VECTOR2I> pts;
1172 VECTOR2I topLeft = GetStart();
1173 VECTOR2I botRight = GetEnd();
1174
1175 pts.emplace_back( topLeft );
1176 pts.emplace_back( botRight.x, topLeft.y );
1177 pts.emplace_back( botRight );
1178 pts.emplace_back( topLeft.x, botRight.y );
1179
1180 return pts;
1181}
1182
1183
1185{
1186 // Start, end, and each inflection point the arc crosses will enclose the entire arc.
1187 // Only include the center when filled; it's not necessarily inside the BB of an unfilled
1188 // arc with a small included angle.
1189 aBBox.SetOrigin( m_start );
1190 aBBox.Merge( m_end );
1191
1192 if( IsFilled() )
1193 aBBox.Merge( m_arcCenter );
1194
1195 int radius = GetRadius();
1196 EDA_ANGLE t1, t2;
1197
1198 CalcArcAngles( t1, t2 );
1199
1200 t1.Normalize();
1201 t2.Normalize();
1202
1203 if( t2 > t1 )
1204 {
1205 if( t1 < ANGLE_0 && t2 > ANGLE_0 )
1206 aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
1207
1208 if( t1 < ANGLE_90 && t2 > ANGLE_90 )
1209 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
1210
1211 if( t1 < ANGLE_180 && t2 > ANGLE_180 )
1212 aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
1213
1214 if( t1 < ANGLE_270 && t2 > ANGLE_270 )
1215 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
1216 }
1217 else
1218 {
1219 if( t1 < ANGLE_0 || t2 > ANGLE_0 )
1220 aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
1221
1222 if( t1 < ANGLE_90 || t2 > ANGLE_90 )
1223 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
1224
1225 if( t1 < ANGLE_180 || t2 > ANGLE_180 )
1226 aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
1227
1228 if( t1 < ANGLE_270 || t2 > ANGLE_270 )
1229 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
1230 }
1231}
1232
1233
1234void EDA_SHAPE::SetPolyPoints( const std::vector<VECTOR2I>& aPoints )
1235{
1238
1239 for( const VECTOR2I& p : aPoints )
1240 m_poly.Append( p.x, p.y );
1241}
1242
1243
1244std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineChainOnly ) const
1245{
1246 std::vector<SHAPE*> effectiveShapes;
1247 int width = GetEffectiveWidth();
1248
1249 switch( m_shape )
1250 {
1251 case SHAPE_T::ARC:
1252 effectiveShapes.emplace_back( new SHAPE_ARC( m_arcCenter, m_start, GetArcAngle(), width ) );
1253 break;
1254
1255 case SHAPE_T::SEGMENT:
1256 effectiveShapes.emplace_back( new SHAPE_SEGMENT( m_start, m_end, width ) );
1257 break;
1258
1259 case SHAPE_T::RECTANGLE:
1260 {
1261 std::vector<VECTOR2I> pts = GetRectCorners();
1262
1263 if( ( IsFilled() || IsProxyItem() ) && !aEdgeOnly )
1264 effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
1265
1266 if( width > 0 || !IsFilled() || aEdgeOnly )
1267 {
1268 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], width ) );
1269 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], width ) );
1270 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], width ) );
1271 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], width ) );
1272 }
1273 }
1274 break;
1275
1276 case SHAPE_T::CIRCLE:
1277 {
1278 if( IsFilled() && !aEdgeOnly )
1279 effectiveShapes.emplace_back( new SHAPE_CIRCLE( getCenter(), GetRadius() ) );
1280
1281 if( width > 0 || !IsFilled() || aEdgeOnly )
1282 effectiveShapes.emplace_back( new SHAPE_ARC( getCenter(), GetEnd(), ANGLE_360, width ) );
1283
1284 break;
1285 }
1286
1287 case SHAPE_T::BEZIER:
1288 {
1289 std::vector<VECTOR2I> bezierPoints = buildBezierToSegmentsPointsList( width / 2 );
1290 VECTOR2I start_pt = bezierPoints[0];
1291
1292 for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ )
1293 {
1294 VECTOR2I end_pt = bezierPoints[jj];
1295 effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, width ) );
1296 start_pt = end_pt;
1297 }
1298
1299 break;
1300 }
1301
1302 case SHAPE_T::POLY:
1303 {
1304 if( GetPolyShape().OutlineCount() == 0 ) // malformed/empty polygon
1305 break;
1306
1307 for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
1308 {
1309 const SHAPE_LINE_CHAIN& l = GetPolyShape().COutline( ii );
1310
1311 if( IsFilled() && !aEdgeOnly )
1312 effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) );
1313
1314 if( width > 0 || !IsFilled() || aEdgeOnly )
1315 {
1316 int segCount = l.SegmentCount();
1317
1318 if( aLineChainOnly && l.IsClosed() )
1319 segCount--; // Treat closed chain as open
1320
1321 for( int jj = 0; jj < segCount; jj++ )
1322 effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.CSegment( jj ), width ) );
1323 }
1324 }
1325 }
1326 break;
1327
1328 default:
1330 break;
1331 }
1332
1333 return effectiveShapes;
1334}
1335
1336
1337void EDA_SHAPE::DupPolyPointsList( std::vector<VECTOR2I>& aBuffer ) const
1338{
1339 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1340 {
1341 int pointCount = m_poly.COutline( ii ).PointCount();
1342
1343 if( pointCount )
1344 {
1345 aBuffer.reserve( pointCount );
1346
1347 for ( auto iter = m_poly.CIterate(); iter; iter++ )
1348 aBuffer.emplace_back( iter->x, iter->y );
1349 }
1350 }
1351}
1352
1353
1355{
1356 // return true if the polygonal shape is valid (has more than 2 points)
1357 return GetPolyShape().OutlineCount() > 0 && GetPolyShape().Outline( 0 ).PointCount() > 2;
1358}
1359
1360
1362{
1363 // return the number of corners of the polygonal shape
1364 // this shape is expected to be only one polygon without hole
1365 return GetPolyShape().OutlineCount() ? GetPolyShape().VertexCount( 0 ) : 0;
1366}
1367
1368
1369void EDA_SHAPE::beginEdit( const VECTOR2I& aPosition )
1370{
1371 switch( GetShape() )
1372 {
1373 case SHAPE_T::SEGMENT:
1374 case SHAPE_T::CIRCLE:
1375 case SHAPE_T::RECTANGLE:
1376 SetStart( aPosition );
1377 SetEnd( aPosition );
1378 break;
1379
1380 case SHAPE_T::ARC:
1381 SetArcGeometry( aPosition, aPosition, aPosition );
1382 m_editState = 1;
1383 break;
1384
1385 case SHAPE_T::BEZIER:
1386 SetStart( aPosition );
1387 SetEnd( aPosition );
1388 SetBezierC1( aPosition );
1389 SetBezierC2( aPosition );
1390 m_editState = 1;
1391
1393 break;
1394
1395 case SHAPE_T::POLY:
1397 m_poly.Outline( 0 ).SetClosed( false );
1398
1399 // Start and end of the first segment (co-located for now)
1400 m_poly.Outline( 0 ).Append( aPosition );
1401 m_poly.Outline( 0 ).Append( aPosition, true );
1402 break;
1403
1404 default:
1406 }
1407}
1408
1409
1410bool EDA_SHAPE::continueEdit( const VECTOR2I& aPosition )
1411{
1412 switch( GetShape() )
1413 {
1414 case SHAPE_T::ARC:
1415 case SHAPE_T::SEGMENT:
1416 case SHAPE_T::CIRCLE:
1417 case SHAPE_T::RECTANGLE:
1418 return false;
1419
1420 case SHAPE_T::BEZIER:
1421 if( m_editState == 3 )
1422 return false;
1423
1424 m_editState++;
1425 return true;
1426
1427 case SHAPE_T::POLY:
1428 {
1429 SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1430
1431 // do not add zero-length segments
1432 if( poly.CPoint( poly.GetPointCount() - 2 ) != poly.CLastPoint() )
1433 poly.Append( aPosition, true );
1434 }
1435 return true;
1436
1437 default:
1439 return false;
1440 }
1441}
1442
1443
1444void EDA_SHAPE::calcEdit( const VECTOR2I& aPosition )
1445{
1446#define sq( x ) pow( x, 2 )
1447
1448 switch( GetShape() )
1449 {
1450 case SHAPE_T::SEGMENT:
1451 case SHAPE_T::CIRCLE:
1452 case SHAPE_T::RECTANGLE:
1453 SetEnd( aPosition );
1454 break;
1455
1456 case SHAPE_T::BEZIER:
1457 {
1458 switch( m_editState )
1459 {
1460 case 0:
1461 SetStart( aPosition );
1462 SetEnd( aPosition );
1463 SetBezierC1( aPosition );
1464 SetBezierC2( aPosition );
1465 break;
1466 case 1:
1467 SetBezierC2( aPosition );
1468 SetEnd( aPosition );
1469 break;
1470 case 2: SetBezierC1( aPosition ); break;
1471 case 3: SetBezierC2( aPosition ); break;
1472 }
1473
1475 }
1476 break;
1477
1478 case SHAPE_T::ARC:
1479 {
1480 double radius = GetRadius();
1481 EDA_ANGLE lastAngle = GetArcAngle();
1482
1483 // Edit state 0: drawing: place start
1484 // Edit state 1: drawing: place end (center calculated for 90-degree subtended angle)
1485 // Edit state 2: point edit: move start (center calculated for invariant subtended angle)
1486 // Edit state 3: point edit: move end (center calculated for invariant subtended angle)
1487 // Edit state 4: point edit: move center
1488 // Edit state 5: point edit: move arc-mid-point
1489
1490 switch( m_editState )
1491 {
1492 case 0:
1493 SetArcGeometry( aPosition, aPosition, aPosition );
1494 return;
1495
1496 case 1:
1497 m_end = aPosition;
1498 radius = m_start.Distance( m_end ) * M_SQRT1_2;
1499 break;
1500
1501 case 2:
1502 case 3:
1503 {
1504 VECTOR2I v = m_start - m_end;
1505 double chordBefore = v.SquaredEuclideanNorm();
1506
1507 if( m_editState == 2 )
1508 m_start = aPosition;
1509 else
1510 m_end = aPosition;
1511
1512 v = m_start - m_end;
1513
1514 double chordAfter = v.SquaredEuclideanNorm();
1515 double ratio = 0.0;
1516
1517 if( chordBefore > 0 )
1518 ratio = chordAfter / chordBefore;
1519
1520 if( ratio != 0 )
1521 radius = std::max( sqrt( sq( radius ) * ratio ), sqrt( chordAfter ) / 2 );
1522 }
1523 break;
1524
1525 case 4:
1526 {
1527 double radialA = m_start.Distance( aPosition );
1528 double radialB = m_end.Distance( aPosition );
1529 radius = ( radialA + radialB ) / 2.0;
1530 }
1531 break;
1532
1533 case 5:
1534 SetArcGeometry( GetStart(), aPosition, GetEnd() );
1535 return;
1536 }
1537
1538 // Calculate center based on start, end, and radius
1539 //
1540 // Let 'l' be the length of the chord and 'm' the middle point of the chord
1541 double l = m_start.Distance( m_end );
1542 VECTOR2D m = ( m_start + m_end ) / 2;
1543 double sqRadDiff = ( radius * radius ) - ( l * l ) / 4.0;
1544
1545 // Calculate 'd', the vector from the chord midpoint to the center
1546 VECTOR2D d;
1547
1548 if( l > 0 && sqRadDiff >= 0 )
1549 {
1550 d.x = sqrt( sqRadDiff ) * ( m_start.y - m_end.y ) / l;
1551 d.y = sqrt( sqRadDiff ) * ( m_end.x - m_start.x ) / l;
1552 }
1553
1554 VECTOR2I c1 = KiROUND( m + d );
1555 VECTOR2I c2 = KiROUND( m - d );
1556
1557 // Solution gives us 2 centers; we need to pick one:
1558 switch( m_editState )
1559 {
1560 case 1:
1561 // Keep arc clockwise while drawing i.e. arc angle = 90 deg.
1562 // it can be 90 or 270 deg depending on the arc center choice (c1 or c2)
1563 m_arcCenter = c1; // first trial
1564
1565 if( GetArcAngle() > ANGLE_180 )
1566 m_arcCenter = c2;
1567
1568 break;
1569
1570 case 2:
1571 case 3:
1572 // Pick the one of c1, c2 to keep arc on the same side
1573 m_arcCenter = c1; // first trial
1574
1575 if( ( lastAngle < ANGLE_180 ) != ( GetArcAngle() < ANGLE_180 ) )
1576 m_arcCenter = c2;
1577
1578 break;
1579
1580 case 4:
1581 // Pick the one closer to the mouse position
1582 m_arcCenter = c1.Distance( aPosition ) < c2.Distance( aPosition ) ? c1 : c2;
1583 break;
1584 }
1585 }
1586 break;
1587
1588 case SHAPE_T::POLY:
1589 m_poly.Outline( 0 ).SetPoint( m_poly.Outline( 0 ).GetPointCount() - 1, aPosition );
1590 break;
1591
1592 default:
1594 }
1595}
1596
1597
1598void EDA_SHAPE::endEdit( bool aClosed )
1599{
1600 switch( GetShape() )
1601 {
1602 case SHAPE_T::ARC:
1603 case SHAPE_T::SEGMENT:
1604 case SHAPE_T::CIRCLE:
1605 case SHAPE_T::RECTANGLE:
1606 case SHAPE_T::BEZIER:
1607 break;
1608
1609 case SHAPE_T::POLY:
1610 {
1611 SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1612
1613 // do not include last point twice
1614 if( poly.GetPointCount() > 2 )
1615 {
1616 if( poly.CPoint( poly.GetPointCount() - 2 ) == poly.CLastPoint() )
1617 {
1618 poly.SetClosed( aClosed );
1619 }
1620 else
1621 {
1622 poly.SetClosed( false );
1623 poly.Remove( poly.GetPointCount() - 1 );
1624 }
1625 }
1626 }
1627 break;
1628
1629 default:
1631 }
1632}
1633
1634
1636{
1637 EDA_SHAPE* image = dynamic_cast<EDA_SHAPE*>( aImage );
1638 assert( image );
1639
1640 #define SWAPITEM( x ) std::swap( x, image->x )
1641 SWAPITEM( m_stroke );
1642 SWAPITEM( m_start );
1643 SWAPITEM( m_end );
1645 SWAPITEM( m_shape );
1649 SWAPITEM( m_poly );
1650 SWAPITEM( m_fill );
1654 #undef SWAPITEM
1655}
1656
1657
1658int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const
1659{
1660#define EPSILON 2 // Should be enough for rounding errors on calculated items
1661
1662#define TEST( a, b ) { if( a != b ) return a - b; }
1663#define TEST_E( a, b ) { if( abs( a - b ) > EPSILON ) return a - b; }
1664#define TEST_PT( a, b ) { TEST_E( a.x, b.x ); TEST_E( a.y, b.y ); }
1665
1666 TEST_PT( m_start, aOther->m_start );
1667 TEST_PT( m_end, aOther->m_end );
1668
1669 TEST( (int) m_shape, (int) aOther->m_shape );
1670
1671 if( m_shape == SHAPE_T::ARC )
1672 {
1673 TEST_PT( m_arcCenter, aOther->m_arcCenter );
1674 }
1675 else if( m_shape == SHAPE_T::BEZIER )
1676 {
1677 TEST_PT( m_bezierC1, aOther->m_bezierC1 );
1678 TEST_PT( m_bezierC2, aOther->m_bezierC2 );
1679 }
1680 else if( m_shape == SHAPE_T::POLY )
1681 {
1683 }
1684
1685 for( size_t ii = 0; ii < m_bezierPoints.size(); ++ii )
1686 TEST_PT( m_bezierPoints[ii], aOther->m_bezierPoints[ii] );
1687
1688 for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
1689 TEST_PT( m_poly.CVertex( ii ), aOther->m_poly.CVertex( ii ) );
1690
1691 TEST_E( m_stroke.GetWidth(), aOther->m_stroke.GetWidth() );
1692 TEST( (int) m_stroke.GetLineStyle(), (int) aOther->m_stroke.GetLineStyle() );
1693 TEST( (int) m_fill, (int) aOther->m_fill );
1694
1695 return 0;
1696}
1697
1698
1699void EDA_SHAPE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, int aClearance, int aError,
1700 ERROR_LOC aErrorLoc, bool ignoreLineWidth ) const
1701{
1702 int width = ignoreLineWidth ? 0 : GetWidth();
1703
1704 width += 2 * aClearance;
1705
1706 switch( m_shape )
1707 {
1708 case SHAPE_T::CIRCLE:
1709 {
1710 int r = GetRadius();
1711
1712 if( IsFilled() )
1713 TransformCircleToPolygon( aBuffer, getCenter(), r + width / 2, aError, aErrorLoc );
1714 else
1715 TransformRingToPolygon( aBuffer, getCenter(), r, width, aError, aErrorLoc );
1716
1717 break;
1718 }
1719
1720 case SHAPE_T::RECTANGLE:
1721 {
1722 std::vector<VECTOR2I> pts = GetRectCorners();
1723
1724 if( IsFilled() || IsProxyItem() )
1725 {
1726 aBuffer.NewOutline();
1727
1728 for( const VECTOR2I& pt : pts )
1729 aBuffer.Append( pt );
1730 }
1731
1732 if( width > 0 || !IsFilled() )
1733 {
1734 // Add in segments
1735 TransformOvalToPolygon( aBuffer, pts[0], pts[1], width, aError, aErrorLoc );
1736 TransformOvalToPolygon( aBuffer, pts[1], pts[2], width, aError, aErrorLoc );
1737 TransformOvalToPolygon( aBuffer, pts[2], pts[3], width, aError, aErrorLoc );
1738 TransformOvalToPolygon( aBuffer, pts[3], pts[0], width, aError, aErrorLoc );
1739 }
1740
1741 break;
1742 }
1743
1744 case SHAPE_T::ARC:
1745 TransformArcToPolygon( aBuffer, GetStart(), GetArcMid(), GetEnd(), width, aError, aErrorLoc );
1746 break;
1747
1748 case SHAPE_T::SEGMENT:
1749 TransformOvalToPolygon( aBuffer, GetStart(), GetEnd(), width, aError, aErrorLoc );
1750 break;
1751
1752 case SHAPE_T::POLY:
1753 {
1754 if( !IsPolyShapeValid() )
1755 break;
1756
1757 if( IsFilled() )
1758 {
1759 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1760 {
1761 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
1762 SHAPE_POLY_SET tmp;
1763 tmp.NewOutline();
1764
1765 for( int jj = 0; jj < (int) poly.GetPointCount(); ++jj )
1766 tmp.Append( poly.GetPoint( jj ) );
1767
1768 if( width > 0 )
1769 {
1770 int inflate = width / 2;
1771
1772 if( aErrorLoc == ERROR_OUTSIDE )
1773 inflate += aError;
1774
1775 tmp.Inflate( inflate, CORNER_STRATEGY::ROUND_ALL_CORNERS, aError );
1776 }
1777
1778 aBuffer.Append( tmp );
1779 }
1780 }
1781 else
1782 {
1783 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1784 {
1785 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
1786
1787 for( int jj = 0; jj < (int) poly.SegmentCount(); ++jj )
1788 {
1789 const SEG& seg = poly.GetSegment( jj );
1790 TransformOvalToPolygon( aBuffer, seg.A, seg.B, width, aError, aErrorLoc );
1791 }
1792 }
1793 }
1794
1795 break;
1796 }
1797
1798 case SHAPE_T::BEZIER:
1799 {
1800 std::vector<VECTOR2I> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
1801 BEZIER_POLY converter( ctrlPts );
1802 std::vector<VECTOR2I> poly;
1803 converter.GetPoly( poly, aError );
1804
1805 for( unsigned ii = 1; ii < poly.size(); ii++ )
1806 TransformOvalToPolygon( aBuffer, poly[ii - 1], poly[ii], width, aError, aErrorLoc );
1807
1808 break;
1809 }
1810
1811 default:
1813 break;
1814 }
1815}
1816
1817
1819{
1820 m_stroke.SetLineStyle( aStyle );
1821}
1822
1823
1825{
1826 if( m_stroke.GetLineStyle() != LINE_STYLE::DEFAULT )
1827 return m_stroke.GetLineStyle();
1828
1829 return LINE_STYLE::SOLID;
1830}
1831
1832
1833bool EDA_SHAPE::operator==( const EDA_SHAPE& aOther ) const
1834{
1835 if( GetShape() != aOther.GetShape() )
1836 return false;
1837
1838 if( m_fill != aOther.m_fill )
1839 return false;
1840
1841 if( m_stroke.GetWidth() != aOther.m_stroke.GetWidth() )
1842 return false;
1843
1844 if( m_stroke.GetLineStyle() != aOther.m_stroke.GetLineStyle() )
1845 return false;
1846
1847 if( m_fillColor != aOther.m_fillColor )
1848 return false;
1849
1850 if( m_start != aOther.m_start )
1851 return false;
1852
1853 if( m_end != aOther.m_end )
1854 return false;
1855
1856 if( m_arcCenter != aOther.m_arcCenter )
1857 return false;
1858
1859 if( m_bezierC1 != aOther.m_bezierC1 )
1860 return false;
1861
1862 if( m_bezierC2 != aOther.m_bezierC2 )
1863 return false;
1864
1865 if( m_bezierPoints != aOther.m_bezierPoints )
1866 return false;
1867
1868 for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
1869 {
1870 if( m_poly.CVertex( ii ) != aOther.m_poly.CVertex( ii ) )
1871 return false;
1872 }
1873
1874 return true;
1875}
1876
1877
1878double EDA_SHAPE::Similarity( const EDA_SHAPE& aOther ) const
1879{
1880 if( GetShape() != aOther.GetShape() )
1881 return 0.0;
1882
1883 double similarity = 1.0;
1884
1885 if( m_fill != aOther.m_fill )
1886 similarity *= 0.9;
1887
1888 if( m_stroke.GetWidth() != aOther.m_stroke.GetWidth() )
1889 similarity *= 0.9;
1890
1891 if( m_stroke.GetLineStyle() != aOther.m_stroke.GetLineStyle() )
1892 similarity *= 0.9;
1893
1894 if( m_fillColor != aOther.m_fillColor )
1895 similarity *= 0.9;
1896
1897 if( m_start != aOther.m_start )
1898 similarity *= 0.9;
1899
1900 if( m_end != aOther.m_end )
1901 similarity *= 0.9;
1902
1903 if( m_arcCenter != aOther.m_arcCenter )
1904 similarity *= 0.9;
1905
1906 if( m_bezierC1 != aOther.m_bezierC1 )
1907 similarity *= 0.9;
1908
1909 if( m_bezierC2 != aOther.m_bezierC2 )
1910 similarity *= 0.9;
1911
1912 {
1913 int m = m_bezierPoints.size();
1914 int n = aOther.m_bezierPoints.size();
1915
1916 size_t longest = alg::longest_common_subset( m_bezierPoints, aOther.m_bezierPoints );
1917
1918 similarity *= std::pow( 0.9, m + n - 2 * longest );
1919 }
1920
1921 {
1922 int m = m_poly.TotalVertices();
1923 int n = aOther.m_poly.TotalVertices();
1924 std::vector<VECTOR2I> poly;
1925 std::vector<VECTOR2I> otherPoly;
1926 VECTOR2I lastPt( 0, 0 );
1927
1928 // We look for the longest common subset of the two polygons, but we need to
1929 // offset each point because we're actually looking for overall similarity, not just
1930 // exact matches. So if the zone is moved by 1IU, we only want one point to be
1931 // considered "moved" rather than the entire polygon. In this case, the first point
1932 // will not be a match but the rest of the sequence will.
1933 for( int ii = 0; ii < m; ++ii )
1934 {
1935 poly.emplace_back( lastPt - m_poly.CVertex( ii ) );
1936 lastPt = m_poly.CVertex( ii );
1937 }
1938
1939 lastPt = VECTOR2I( 0, 0 );
1940
1941 for( int ii = 0; ii < n; ++ii )
1942 {
1943 otherPoly.emplace_back( lastPt - aOther.m_poly.CVertex( ii ) );
1944 lastPt = aOther.m_poly.CVertex( ii );
1945 }
1946
1947 size_t longest = alg::longest_common_subset( poly, otherPoly );
1948
1949 similarity *= std::pow( 0.9, m + n - 2 * longest );
1950 }
1951
1952 return similarity;
1953}
1954
1955
1958
1959
1960static struct EDA_SHAPE_DESC
1961{
1963 {
1965 .Map( SHAPE_T::SEGMENT, _HKI( "Segment" ) )
1966 .Map( SHAPE_T::RECTANGLE, _HKI( "Rectangle" ) )
1967 .Map( SHAPE_T::ARC, _HKI( "Arc" ) )
1968 .Map( SHAPE_T::CIRCLE, _HKI( "Circle" ) )
1969 .Map( SHAPE_T::POLY, _HKI( "Polygon" ) )
1970 .Map( SHAPE_T::BEZIER, _HKI( "Bezier" ) );
1971
1972 auto& plotDashTypeEnum = ENUM_MAP<LINE_STYLE>::Instance();
1973
1974 if( plotDashTypeEnum.Choices().GetCount() == 0 )
1975 {
1976 plotDashTypeEnum.Map( LINE_STYLE::DEFAULT, _HKI( "Default" ) )
1977 .Map( LINE_STYLE::SOLID, _HKI( "Solid" ) )
1978 .Map( LINE_STYLE::DASH, _HKI( "Dashed" ) )
1979 .Map( LINE_STYLE::DOT, _HKI( "Dotted" ) )
1980 .Map( LINE_STYLE::DASHDOT, _HKI( "Dash-Dot" ) )
1981 .Map( LINE_STYLE::DASHDOTDOT, _HKI( "Dash-Dot-Dot" ) );
1982 }
1983
1986
1987 auto isNotPolygonOrCircle = []( INSPECTABLE* aItem ) -> bool
1988 {
1989 // Polygons, unlike other shapes, have no meaningful start or end coordinates
1990 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
1991 return shape->GetShape() != SHAPE_T::POLY && shape->GetShape() != SHAPE_T::CIRCLE;
1992
1993 return false;
1994 };
1995
1996 auto isCircle = []( INSPECTABLE* aItem ) -> bool
1997 {
1998 // Polygons, unlike other shapes, have no meaningful start or end coordinates
1999 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2000 return shape->GetShape() == SHAPE_T::CIRCLE;
2001
2002 return false;
2003 };
2004
2005 auto isRectangle = []( INSPECTABLE* aItem ) -> bool
2006 {
2007 // Polygons, unlike other shapes, have no meaningful start or end coordinates
2008 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2009 return shape->GetShape() == SHAPE_T::RECTANGLE;
2010
2011 return false;
2012 };
2013
2014 const wxString shapeProps = _HKI( "Shape Properties" );
2015
2016 auto shape = new PROPERTY_ENUM<EDA_SHAPE, SHAPE_T>( _HKI( "Shape" ),
2018 propMgr.AddProperty( shape, shapeProps );
2019
2020 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start X" ),
2023 shapeProps )
2024 .SetAvailableFunc( isNotPolygonOrCircle );
2025 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start Y" ),
2028 shapeProps )
2029 .SetAvailableFunc( isNotPolygonOrCircle );
2030
2031 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Center X" ),
2034 shapeProps )
2035 .SetAvailableFunc( isCircle );
2036
2037 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Center Y" ),
2040 shapeProps )
2041 .SetAvailableFunc( isCircle );
2042
2043 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Radius" ),
2046 shapeProps )
2047 .SetAvailableFunc( isCircle );
2048
2049 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End X" ),
2052 shapeProps )
2053 .SetAvailableFunc( isNotPolygonOrCircle );
2054
2055 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End Y" ),
2058 shapeProps )
2059 .SetAvailableFunc( isNotPolygonOrCircle );
2060
2061 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Width" ),
2064 shapeProps )
2065 .SetAvailableFunc( isRectangle );
2066
2067 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Height" ),
2070 shapeProps )
2071 .SetAvailableFunc( isRectangle );
2072
2073 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Line Width" ),
2075 shapeProps );
2076
2077 void ( EDA_SHAPE::*lineStyleSetter )( LINE_STYLE ) = &EDA_SHAPE::SetLineStyle;
2078 propMgr.AddProperty( new PROPERTY_ENUM<EDA_SHAPE, LINE_STYLE>( _HKI( "Line Style" ),
2079 lineStyleSetter, &EDA_SHAPE::GetLineStyle ),
2080 shapeProps );
2081
2082 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, COLOR4D>( _HKI( "Line Color" ),
2084 shapeProps )
2085 .SetIsHiddenFromRulesEditor();
2086
2087 auto angle = new PROPERTY<EDA_SHAPE, EDA_ANGLE>( _HKI( "Angle" ),
2090 angle->SetAvailableFunc(
2091 [=]( INSPECTABLE* aItem ) -> bool
2092 {
2093 if( EDA_SHAPE* curr_shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2094 return curr_shape->GetShape() == SHAPE_T::ARC;
2095
2096 return false;
2097 } );
2098 propMgr.AddProperty( angle, shapeProps );
2099
2100 auto fillAvailable =
2101 [=]( INSPECTABLE* aItem ) -> bool
2102 {
2103 if( EDA_ITEM* edaItem = dynamic_cast<EDA_ITEM*>( aItem ) )
2104 {
2105 // For some reason masking "Filled" and "Fill Color" at the
2106 // PCB_TABLECELL level doesn't work.
2107 if( edaItem->Type() == PCB_TABLECELL_T )
2108 return false;
2109 }
2110
2111 if( EDA_SHAPE* edaShape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2112 {
2113 switch( edaShape->GetShape() )
2114 {
2115 case SHAPE_T::POLY:
2116 case SHAPE_T::RECTANGLE:
2117 case SHAPE_T::CIRCLE:
2118 return true;
2119
2120 default:
2121 return false;
2122 }
2123 }
2124
2125 return false;
2126 };
2127
2128 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, bool>( _HKI( "Filled" ),
2130 shapeProps )
2131 .SetAvailableFunc( fillAvailable );
2132
2133 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, COLOR4D>( _HKI( "Fill Color" ),
2135 shapeProps )
2136 .SetAvailableFunc( fillAvailable )
2138 }
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.
void SetOrigin(const Vec &pos)
Definition: box2.h:227
BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition: box2.h:136
bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:294
bool IntersectsCircleEdge(const Vec &aCenter, const int aRadius, const int aWidth) const
Definition: box2.h:506
bool Contains(const Vec &aPoint) const
Definition: box2.h:158
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:541
Vec Centre() const
Definition: box2.h:87
void SetEnd(coord_type x, coord_type y)
Definition: box2.h:280
BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:623
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:680
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:562
VECTOR2I getCenter() const
Definition: eda_shape.cpp:536
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:523
void flip(const VECTOR2I &aCentre, bool aFlipLeftRight)
Definition: eda_shape.cpp:431
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:1818
void calcEdit(const VECTOR2I &aPosition)
Definition: eda_shape.cpp:1444
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:594
void ShapeGetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList)
Definition: eda_shape.cpp:750
void SetFilled(bool aFlag)
Definition: eda_shape.h:101
bool operator==(const EDA_SHAPE &aOther) const
Definition: eda_shape.cpp:1833
int GetRadius() const
Definition: eda_shape.cpp:610
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:1410
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:1244
bool hitTest(const VECTOR2I &aPosition, int aAccuracy=0) const
Definition: eda_shape.cpp:861
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:633
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:509
EDA_SHAPE(SHAPE_T aType, int aLineWidth, FILL_T aFill)
Definition: eda_shape.cpp:42
void beginEdit(const VECTOR2I &aStartPoint)
Definition: eda_shape.cpp:1369
VECTOR2I m_start
Definition: eda_shape.h:425
int GetPointCount() const
Definition: eda_shape.cpp:1361
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:1337
LINE_STYLE GetLineStyle() const
Definition: eda_shape.cpp:1824
void endEdit(bool aClosed=true)
Finishes editing the shape.
Definition: eda_shape.cpp:1598
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:1635
std::vector< VECTOR2I > GetRectCorners() const
Definition: eda_shape.cpp:1169
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:1184
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:642
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:1878
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:807
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:708
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:691
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:1234
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:1699
wxString getFriendlyName() const
Definition: eda_shape.cpp:723
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:671
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:1354
int Compare(const EDA_SHAPE *aOther) const
Definition: eda_shape.cpp:1658
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:580
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)
void Mirror(bool aX=true, bool aY=false, const VECTOR2I &aRef={ 0, 0 })
Mirror the line points about y or x (or both)
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.
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:91
void SetLineStyle(LINE_STYLE aLineStyle)
Definition: stroke_params.h:95
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:94
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition: vector2d.h:305
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition: vector2d.h:553
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:281
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
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
@ ERROR_OUTSIDE
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
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:214
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:48
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
constexpr ret_type KiROUND(fp_type v, bool aQuiet=false)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:100
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:676
VECTOR2< double > VECTOR2D
Definition: vector2d.h:675