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 += GetLineLength( m_bezierPoints[ ii - 1], m_bezierPoints[ii] );
132
133 return length;
134
135 case SHAPE_T::SEGMENT:
136 return GetLineLength( GetStart(), 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: return GetEndY() - GetStartY();
158
159 default:
161 return 0;
162 }
163}
164
166{
167 switch( m_shape )
168 {
169 case SHAPE_T::RECTANGLE: return GetEndX() - GetStartX();
170
171 default:
173 return 0;
174 }
175}
176
177void EDA_SHAPE::SetLength( const double& aLength )
178{
179 switch( m_shape )
180 {
181 case SHAPE_T::SEGMENT: m_segmentLength = aLength; break;
182
183 default:
185 }
186}
187
188void EDA_SHAPE::SetRectangle( const long long int& aHeight, const long long int& aWidth )
189{
190 switch ( m_shape )
191 {
192 case SHAPE_T::RECTANGLE:
193 m_rectangleHeight = aHeight;
194 m_rectangleWidth = aWidth;
195 break;
196
198 }
199}
200
201
203{
204 switch( m_shape )
205 {
206 case SHAPE_T::SEGMENT: m_segmentAngle = aAngle; break;
207
209 }
210}
211
213{
214 switch( m_shape )
215 {
216 case SHAPE_T::CIRCLE:
217 case SHAPE_T::RECTANGLE:
218 return true;
219
220 case SHAPE_T::ARC:
221 case SHAPE_T::SEGMENT:
222 return false;
223
224 case SHAPE_T::POLY:
225 if( m_poly.IsEmpty() )
226 return false;
227 else
228 return m_poly.Outline( 0 ).IsClosed();
229
230 case SHAPE_T::BEZIER:
231 if( m_bezierPoints.size() < 3 )
232 return false;
233 else
234 return m_bezierPoints[0] == m_bezierPoints[ m_bezierPoints.size() - 1 ];
235
236 default:
238 return false;
239 }
240}
241
242
243
244void EDA_SHAPE::move( const VECTOR2I& aMoveVector )
245{
246 switch ( m_shape )
247 {
248 case SHAPE_T::ARC:
249 m_arcCenter += aMoveVector;
251
252 case SHAPE_T::SEGMENT:
253 case SHAPE_T::RECTANGLE:
254 case SHAPE_T::CIRCLE:
255 m_start += aMoveVector;
256 m_end += aMoveVector;
257 break;
258
259 case SHAPE_T::POLY:
260 m_poly.Move( aMoveVector );
261 break;
262
263 case SHAPE_T::BEZIER:
264 m_start += aMoveVector;
265 m_end += aMoveVector;
266 m_bezierC1 += aMoveVector;
267 m_bezierC2 += aMoveVector;
268
269 for( VECTOR2I& pt : m_bezierPoints )
270 pt += aMoveVector;
271
272 break;
273
274 default:
276 break;
277 }
278}
279
280
281void EDA_SHAPE::scale( double aScale )
282{
283 auto scalePt = [&]( VECTOR2I& pt )
284 {
285 pt.x = KiROUND( pt.x * aScale );
286 pt.y = KiROUND( pt.y * aScale );
287 };
288
289 switch( m_shape )
290 {
291 case SHAPE_T::ARC:
292 scalePt( m_arcCenter );
294
295 case SHAPE_T::SEGMENT:
296 case SHAPE_T::RECTANGLE:
297 scalePt( m_start );
298 scalePt( m_end );
299 break;
300
301 case SHAPE_T::CIRCLE: // ring or circle
302 scalePt( m_start );
303 m_end.x = m_start.x + KiROUND( GetRadius() * aScale );
304 m_end.y = m_start.y;
305 break;
306
307 case SHAPE_T::POLY: // polygon
308 {
309 std::vector<VECTOR2I> pts;
310
311 for( int ii = 0; ii < m_poly.OutlineCount(); ++ ii )
312 {
313 for( const VECTOR2I& pt : m_poly.Outline( ii ).CPoints() )
314 {
315 pts.emplace_back( pt );
316 scalePt( pts.back() );
317 }
318 }
319
320 SetPolyPoints( pts );
321 }
322 break;
323
324 case SHAPE_T::BEZIER:
325 scalePt( m_start );
326 scalePt( m_end );
327 scalePt( m_bezierC1 );
328 scalePt( m_bezierC2 );
329 break;
330
331 default:
333 break;
334 }
335}
336
337
338void EDA_SHAPE::rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
339{
340 switch( m_shape )
341 {
342 case SHAPE_T::SEGMENT:
343 case SHAPE_T::CIRCLE:
344 RotatePoint( m_start, aRotCentre, aAngle );
345 RotatePoint( m_end, aRotCentre, aAngle );
346 break;
347
348 case SHAPE_T::ARC:
349 RotatePoint( m_start, aRotCentre, aAngle );
350 RotatePoint( m_end, aRotCentre, aAngle );
351 RotatePoint( m_arcCenter, aRotCentre, aAngle );
352 break;
353
354 case SHAPE_T::RECTANGLE:
355 if( aAngle.IsCardinal() )
356 {
357 RotatePoint( m_start, aRotCentre, aAngle );
358 RotatePoint( m_end, aRotCentre, aAngle );
359 break;
360 }
361
362 // Convert non-cardinally-rotated rect to a diamond
363 m_shape = SHAPE_T::POLY;
370
372
373 case SHAPE_T::POLY:
374 m_poly.Rotate( aAngle, aRotCentre );
375 break;
376
377 case SHAPE_T::BEZIER:
378 RotatePoint( m_start, aRotCentre, aAngle );
379 RotatePoint( m_end, aRotCentre, aAngle );
380 RotatePoint( m_bezierC1, aRotCentre, aAngle );
381 RotatePoint( m_bezierC2, aRotCentre, aAngle );
382
383 for( VECTOR2I& pt : m_bezierPoints )
384 RotatePoint( pt, aRotCentre, aAngle);
385
386 break;
387
388 default:
390 break;
391 }
392}
393
394
395void EDA_SHAPE::flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
396{
397 switch ( m_shape )
398 {
399 case SHAPE_T::SEGMENT:
400 case SHAPE_T::RECTANGLE:
401 if( aFlipLeftRight )
402 {
403 m_start.x = aCentre.x - ( m_start.x - aCentre.x );
404 m_end.x = aCentre.x - ( m_end.x - aCentre.x );
405 }
406 else
407 {
408 m_start.y = aCentre.y - ( m_start.y - aCentre.y );
409 m_end.y = aCentre.y - ( m_end.y - aCentre.y );
410 }
411
412 std::swap( m_start, m_end );
413 break;
414
415 case SHAPE_T::CIRCLE:
416 if( aFlipLeftRight )
417 {
418 m_start.x = aCentre.x - ( m_start.x - aCentre.x );
419 m_end.x = aCentre.x - ( m_end.x - aCentre.x );
420 }
421 else
422 {
423 m_start.y = aCentre.y - ( m_start.y - aCentre.y );
424 m_end.y = aCentre.y - ( m_end.y - aCentre.y );
425 }
426 break;
427
428 case SHAPE_T::ARC:
429 if( aFlipLeftRight )
430 {
431 m_start.x = aCentre.x - ( m_start.x - aCentre.x );
432 m_end.x = aCentre.x - ( m_end.x - aCentre.x );
433 m_arcCenter.x = aCentre.x - ( m_arcCenter.x - aCentre.x );
434 }
435 else
436 {
437 m_start.y = aCentre.y - ( m_start.y - aCentre.y );
438 m_end.y = aCentre.y - ( m_end.y - aCentre.y );
439 m_arcCenter.y = aCentre.y - ( m_arcCenter.y - aCentre.y );
440 }
441
442 std::swap( m_start, m_end );
443 break;
444
445 case SHAPE_T::POLY:
446 m_poly.Mirror( aFlipLeftRight, !aFlipLeftRight, aCentre );
447 break;
448
449 case SHAPE_T::BEZIER:
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 m_bezierC1.x = aCentre.x - ( m_bezierC1.x - aCentre.x );
455 m_bezierC2.x = aCentre.x - ( m_bezierC2.x - aCentre.x );
456 }
457 else
458 {
459 m_start.y = aCentre.y - ( m_start.y - aCentre.y );
460 m_end.y = aCentre.y - ( m_end.y - aCentre.y );
461 m_bezierC1.y = aCentre.y - ( m_bezierC1.y - aCentre.y );
462 m_bezierC2.y = aCentre.y - ( m_bezierC2.y - aCentre.y );
463 }
464
465 // Rebuild the poly points shape
466 {
467 std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
468 BEZIER_POLY converter( ctrlPoints );
469 converter.GetPoly( m_bezierPoints, m_stroke.GetWidth() );
470 }
471 break;
472
473 default:
475 break;
476 }
477}
478
479
481{
482 // Has meaning only for SHAPE_T::BEZIER
483 if( m_shape != SHAPE_T::BEZIER )
484 {
485 m_bezierPoints.clear();
486 return;
487 }
488
489 // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
491
492 // Ensure last point respects aMinSegLen parameter
493 if( m_bezierPoints.size() > 2 )
494 {
495 int idx = m_bezierPoints.size() - 1;
496
497 if( VECTOR2I( m_bezierPoints[idx] - m_bezierPoints[idx] - 1 ).EuclideanNorm() < aMinSegLen )
498 {
499 m_bezierPoints[idx - 1] = m_bezierPoints[idx];
500 m_bezierPoints.pop_back();
501 }
502 }
503}
504
505
506const std::vector<VECTOR2I> EDA_SHAPE::buildBezierToSegmentsPointsList( int aMinSegLen ) const
507{
508 std::vector<VECTOR2I> bezierPoints;
509
510 // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
511 std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
512 BEZIER_POLY converter( ctrlPoints );
513 converter.GetPoly( bezierPoints, aMinSegLen );
514
515 return bezierPoints;
516}
517
518
520{
521 switch( m_shape )
522 {
523 case SHAPE_T::ARC:
524 return m_arcCenter;
525
526 case SHAPE_T::CIRCLE:
527 return m_start;
528
529 case SHAPE_T::SEGMENT:
530 // Midpoint of the line
531 return ( m_start + m_end ) / 2;
532
533 case SHAPE_T::POLY:
534 case SHAPE_T::RECTANGLE:
535 case SHAPE_T::BEZIER:
536 return getBoundingBox().Centre();
537
538 default:
540 return VECTOR2I();
541 }
542}
543
544
545void EDA_SHAPE::SetCenter( const VECTOR2I& aCenter )
546{
547 switch( m_shape )
548 {
549 case SHAPE_T::ARC:
550 m_arcCenter = aCenter;
551 break;
552
553 case SHAPE_T::CIRCLE:
554 m_start = aCenter;
555 break;
556
557 default:
559 }
560}
561
562
564{
565 // If none of the input data have changed since we loaded the arc,
566 // keep the original mid point data to minimize churn
569 return m_arcMidData.mid;
570
571 VECTOR2I mid = m_start;
572 RotatePoint( mid, m_arcCenter, -GetArcAngle() / 2.0 );
573 return mid;
574}
575
576
577void EDA_SHAPE::CalcArcAngles( EDA_ANGLE& aStartAngle, EDA_ANGLE& aEndAngle ) const
578{
579 VECTOR2D startRadial( GetStart() - getCenter() );
580 VECTOR2D endRadial( GetEnd() - getCenter() );
581
582 aStartAngle = EDA_ANGLE( startRadial );
583 aEndAngle = EDA_ANGLE( endRadial );
584
585 if( aEndAngle == aStartAngle )
586 aEndAngle = aStartAngle + ANGLE_360; // ring, not null
587
588 while( aEndAngle < aStartAngle )
589 aEndAngle += ANGLE_360;
590}
591
592
594{
595 double radius = 0.0;
596
597 switch( m_shape )
598 {
599 case SHAPE_T::ARC:
600 radius = GetLineLength( m_arcCenter, m_start );
601 break;
602
603 case SHAPE_T::CIRCLE:
604 radius = GetLineLength( m_start, m_end );
605 break;
606
607 default:
609 }
610
611 // don't allow degenerate circles/arcs
612 return std::max( 1, KiROUND( radius ) );
613}
614
615
616void EDA_SHAPE::SetCachedArcData( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCenter )
617{
618 m_arcMidData.start = aStart;
619 m_arcMidData.end = aEnd;
620 m_arcMidData.center = aCenter;
621 m_arcMidData.mid = aMid;
622}
623
624
625void EDA_SHAPE::SetArcGeometry( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
626{
627 m_arcMidData = {};
628 m_start = aStart;
629 m_end = aEnd;
630 m_arcCenter = CalcArcCenter( aStart, aMid, aEnd );
631 VECTOR2I new_mid = GetArcMid();
632
633 m_endsSwapped = false;
634
635 // Watch the ordering here. GetArcMid above needs to be called prior to initializing the
636 // m_arcMidData structure in order to ensure we get the calculated variant, not the cached
637 SetCachedArcData( aStart, aMid, aEnd, m_arcCenter );
638
639 /*
640 * If the input winding doesn't match our internal winding, the calculated midpoint will end
641 * up on the other side of the arc. In this case, we need to flip the start/end points and
642 * flag this change for the system.
643 */
644 VECTOR2D dist( new_mid - aMid );
645 VECTOR2D dist2( new_mid - m_arcCenter );
646
647 if( dist.SquaredEuclideanNorm() > dist2.SquaredEuclideanNorm() )
648 {
649 std::swap( m_start, m_end );
650 m_endsSwapped = true;
651 }
652}
653
655{
656 EDA_ANGLE angle( atan2( static_cast<double>( GetStart().y - GetEnd().y ),
657 static_cast<double>( GetEnd().x - GetStart().x ) ), RADIANS_T );
658
659 return angle;
660}
661
662
664{
665 EDA_ANGLE startAngle;
666 EDA_ANGLE endAngle;
667
668 CalcArcAngles( startAngle, endAngle );
669
670 return endAngle - startAngle;
671}
672
673
675{
676 if( m_shape == SHAPE_T::ARC )
677 {
678 VECTOR2D mid = GetArcMid();
679
680 double orient = ( mid.x - m_start.x ) * ( m_end.y - m_start.y )
681 - ( mid.y - m_start.y ) * ( m_end.x - m_start.x );
682
683 return orient < 0;
684 }
685
687 return false;
688}
689
690
691void EDA_SHAPE::SetArcAngleAndEnd( const EDA_ANGLE& aAngle, bool aCheckNegativeAngle )
692{
693 EDA_ANGLE angle( aAngle );
694
695 m_end = m_start;
697
698 if( aCheckNegativeAngle && aAngle < ANGLE_0 )
699 {
700 std::swap( m_start, m_end );
701 m_endsSwapped = true;
702 }
703}
704
705
707{
708 if( IsProxyItem() )
709 {
710 switch( m_shape )
711 {
712 case SHAPE_T::RECTANGLE: return _( "Pad Number Box" );
713 case SHAPE_T::SEGMENT: return _( "Thermal Spoke Template" );
714 default: return _( "Unrecognized" );
715 }
716 }
717 else
718 {
719 switch( m_shape )
720 {
721 case SHAPE_T::CIRCLE: return _( "Circle" );
722 case SHAPE_T::ARC: return _( "Arc" );
723 case SHAPE_T::BEZIER: return _( "Curve" );
724 case SHAPE_T::POLY: return _( "Polygon" );
725 case SHAPE_T::RECTANGLE: return _( "Rectangle" );
726 case SHAPE_T::SEGMENT: return _( "Segment" );
727 default: return _( "Unrecognized" );
728 }
729 }
730}
731
732
733void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
734{
735 wxString msg;
736
737 wxString shape = _( "Shape" );
738 aList.emplace_back( shape, getFriendlyName() );
739
740 switch( m_shape )
741 {
742 case SHAPE_T::CIRCLE:
743 aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
744 break;
745
746 case SHAPE_T::ARC:
748 aList.emplace_back( _( "Angle" ), msg );
749
750 aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
751 break;
752
753 case SHAPE_T::BEZIER:
754 aList.emplace_back( _( "Length" ), aFrame->MessageTextFromValue( GetLength() ) );
755 break;
756
757 case SHAPE_T::POLY:
758 msg.Printf( wxS( "%d" ), GetPolyShape().Outline(0).PointCount() );
759 aList.emplace_back( _( "Points" ), msg );
760 break;
761
762 case SHAPE_T::RECTANGLE:
763 aList.emplace_back( _( "Width" ),
764 aFrame->MessageTextFromValue( std::abs( GetEnd().x - GetStart().x ) ) );
765
766 aList.emplace_back( _( "Height" ),
767 aFrame->MessageTextFromValue( std::abs( GetEnd().y - GetStart().y ) ) );
768 break;
769
770 case SHAPE_T::SEGMENT:
771 {
772 aList.emplace_back( _( "Length" ),
774
775 // angle counter-clockwise from 3'o-clock
776 EDA_ANGLE angle( atan2( (double)( GetStart().y - GetEnd().y ),
777 (double)( GetEnd().x - GetStart().x ) ), RADIANS_T );
778 aList.emplace_back( _( "Angle" ), EDA_UNIT_UTILS::UI::MessageTextFromValue( angle ) );
779 break;
780 }
781
782 default:
783 break;
784 }
785
786 m_stroke.GetMsgPanelInfo( aFrame, aList );
787}
788
789
791{
792 BOX2I bbox;
793
794 switch( m_shape )
795 {
796 case SHAPE_T::RECTANGLE:
797 for( VECTOR2I& pt : GetRectCorners() )
798 bbox.Merge( pt );
799
800 break;
801
802 case SHAPE_T::SEGMENT:
803 bbox.SetOrigin( GetStart() );
804 bbox.SetEnd( GetEnd() );
805 break;
806
807 case SHAPE_T::CIRCLE:
808 bbox.SetOrigin( GetStart() );
809 bbox.Inflate( GetRadius() );
810 break;
811
812 case SHAPE_T::ARC:
813 computeArcBBox( bbox );
814 break;
815
816 case SHAPE_T::POLY:
817 if( m_poly.IsEmpty() )
818 break;
819
820 for( auto iter = m_poly.CIterate(); iter; iter++ )
821 bbox.Merge( *iter );
822
823 break;
824
825 case SHAPE_T::BEZIER:
826 bbox.SetOrigin( GetStart() );
827 bbox.Merge( GetBezierC1() );
828 bbox.Merge( GetBezierC2() );
829 bbox.Merge( GetEnd() );
830 break;
831
832 default:
834 break;
835 }
836
837 bbox.Inflate( std::max( 0, GetWidth() ) / 2 );
838 bbox.Normalize();
839
840 return bbox;
841}
842
843
844bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
845{
846 int maxdist = aAccuracy;
847
848 if( GetWidth() > 0 )
849 maxdist += GetWidth() / 2;
850
851 switch( m_shape )
852 {
853 case SHAPE_T::CIRCLE:
854 {
855 int radius = GetRadius();
856
857 VECTOR2I::extended_type dist = KiROUND<double, VECTOR2I::extended_type>(
858 EuclideanNorm( aPosition - getCenter() ) );
859
860 if( IsFilled() )
861 return dist <= radius + maxdist; // Filled circle hit-test
862 else
863 return abs( radius - dist ) <= maxdist; // Ring hit-test
864 }
865
866 case SHAPE_T::ARC:
867 {
868 if( EuclideanNorm( aPosition - m_start ) <= maxdist )
869 return true;
870
871 if( EuclideanNorm( aPosition - m_end ) <= maxdist )
872 return true;
873
874 VECTOR2I relPos = aPosition - getCenter();
875 int radius = GetRadius();
876
878 KiROUND<double, VECTOR2I::extended_type>( EuclideanNorm( relPos ) );
879
880 if( IsFilled() )
881 {
882 // Check distance from arc center
883 if( dist > radius + maxdist )
884 return false;
885 }
886 else
887 {
888 // Check distance from arc circumference
889 if( abs( radius - dist ) > maxdist )
890 return false;
891 }
892
893 // Finally, check to see if it's within arc's swept angle.
894 EDA_ANGLE startAngle;
895 EDA_ANGLE endAngle;
896 CalcArcAngles( startAngle, endAngle );
897
898 EDA_ANGLE relPosAngle( relPos );
899
900 startAngle.Normalize();
901 endAngle.Normalize();
902 relPosAngle.Normalize();
903
904 if( endAngle > startAngle )
905 return relPosAngle >= startAngle && relPosAngle <= endAngle;
906 else
907 return relPosAngle >= startAngle || relPosAngle <= endAngle;
908 }
909
910 case SHAPE_T::BEZIER:
911 const_cast<EDA_SHAPE*>( this )->RebuildBezierToSegmentsPointsList( GetWidth() );
912
913 for( unsigned int i= 1; i < m_bezierPoints.size(); i++)
914 {
915 if( TestSegmentHit( aPosition, m_bezierPoints[ i - 1], m_bezierPoints[i], maxdist ) )
916 return true;
917 }
918
919 return false;
920
921 case SHAPE_T::SEGMENT:
922 return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
923
924 case SHAPE_T::RECTANGLE:
925 if( IsProxyItem() || IsFilled() ) // Filled rect hit-test
926 {
927 SHAPE_POLY_SET poly;
928 poly.NewOutline();
929
930 for( const VECTOR2I& pt : GetRectCorners() )
931 poly.Append( pt );
932
933 return poly.Collide( aPosition, maxdist );
934 }
935 else // Open rect hit-test
936 {
937 std::vector<VECTOR2I> pts = GetRectCorners();
938
939 return TestSegmentHit( aPosition, pts[0], pts[1], maxdist )
940 || TestSegmentHit( aPosition, pts[1], pts[2], maxdist )
941 || TestSegmentHit( aPosition, pts[2], pts[3], maxdist )
942 || TestSegmentHit( aPosition, pts[3], pts[0], maxdist );
943 }
944
945 case SHAPE_T::POLY:
946 if( IsFilled() )
947 {
948 if( !m_poly.COutline( 0 ).IsClosed() )
949 {
951 copy.Outline( 0 ).Append( copy.Outline( 0 ).CPoint( 0 ) );
952 return copy.Collide( aPosition, maxdist );
953 }
954 else
955 {
956 return m_poly.Collide( aPosition, maxdist );
957 }
958 }
959 else
960 {
961 return m_poly.CollideEdge( aPosition, nullptr, maxdist );
962 }
963
964 default:
966 return false;
967 }
968}
969
970
971bool EDA_SHAPE::hitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
972{
973 BOX2I arect = aRect;
974 arect.Normalize();
975 arect.Inflate( aAccuracy );
976
977 BOX2I bbox = getBoundingBox();
978
979 switch( m_shape )
980 {
981 case SHAPE_T::CIRCLE:
982 // Test if area intersects or contains the circle:
983 if( aContained )
984 {
985 return arect.Contains( bbox );
986 }
987 else
988 {
989 // If the rectangle does not intersect the bounding box, this is a much quicker test
990 if( !arect.Intersects( bbox ) )
991 return false;
992 else
993 return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
994 }
995
996 case SHAPE_T::ARC:
997 // Test for full containment of this arc in the rect
998 if( aContained )
999 {
1000 return arect.Contains( bbox );
1001 }
1002 // Test if the rect crosses the arc
1003 else
1004 {
1005 if( !arect.Intersects( bbox ) )
1006 return false;
1007
1008 if( IsFilled() )
1009 {
1010 return ( arect.Intersects( getCenter(), GetStart() )
1011 || arect.Intersects( getCenter(), GetEnd() )
1012 || arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() ) );
1013 }
1014 else
1015 {
1016 return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
1017 }
1018 }
1019
1020 case SHAPE_T::RECTANGLE:
1021 if( aContained )
1022 {
1023 return arect.Contains( bbox );
1024 }
1025 else
1026 {
1027 std::vector<VECTOR2I> pts = GetRectCorners();
1028
1029 // Account for the width of the lines
1030 arect.Inflate( GetWidth() / 2 );
1031 return ( arect.Intersects( pts[0], pts[1] )
1032 || arect.Intersects( pts[1], pts[2] )
1033 || arect.Intersects( pts[2], pts[3] )
1034 || arect.Intersects( pts[3], pts[0] ) );
1035 }
1036
1037 case SHAPE_T::SEGMENT:
1038 if( aContained )
1039 {
1040 return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
1041 }
1042 else
1043 {
1044 // Account for the width of the line
1045 arect.Inflate( GetWidth() / 2 );
1046 return arect.Intersects( GetStart(), GetEnd() );
1047 }
1048
1049 case SHAPE_T::POLY:
1050 if( aContained )
1051 {
1052 return arect.Contains( bbox );
1053 }
1054 else
1055 {
1056 // Fast test: if aRect is outside the polygon bounding box,
1057 // rectangles cannot intersect
1058 if( !arect.Intersects( bbox ) )
1059 return false;
1060
1061 // Account for the width of the line
1062 arect.Inflate( GetWidth() / 2 );
1063
1064 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1065 {
1066 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
1067 int count = poly.GetPointCount();
1068
1069 for( int jj = 0; jj < count; jj++ )
1070 {
1071 VECTOR2I vertex = poly.GetPoint( jj );
1072
1073 // Test if the point is within aRect
1074 if( arect.Contains( vertex ) )
1075 return true;
1076
1077 if( jj + 1 < count )
1078 {
1079 VECTOR2I vertexNext = poly.GetPoint( jj + 1 );
1080
1081 // Test if this edge intersects aRect
1082 if( arect.Intersects( vertex, vertexNext ) )
1083 return true;
1084 }
1085 else if( poly.IsClosed() )
1086 {
1087 VECTOR2I vertexNext = poly.GetPoint( 0 );
1088
1089 // Test if this edge intersects aRect
1090 if( arect.Intersects( vertex, vertexNext ) )
1091 return true;
1092 }
1093 }
1094 }
1095
1096 return false;
1097 }
1098
1099 case SHAPE_T::BEZIER:
1100 if( aContained )
1101 {
1102 return arect.Contains( bbox );
1103 }
1104 else
1105 {
1106 // Fast test: if aRect is outside the polygon bounding box,
1107 // rectangles cannot intersect
1108 if( !arect.Intersects( bbox ) )
1109 return false;
1110
1111 // Account for the width of the line
1112 arect.Inflate( GetWidth() / 2 );
1113 unsigned count = m_bezierPoints.size();
1114
1115 for( unsigned ii = 1; ii < count; ii++ )
1116 {
1117 VECTOR2I vertex = m_bezierPoints[ii - 1];
1118 VECTOR2I vertexNext = m_bezierPoints[ii];
1119
1120 // Test if the point is within aRect
1121 if( arect.Contains( vertex ) )
1122 return true;
1123
1124 // Test if this edge intersects aRect
1125 if( arect.Intersects( vertex, vertexNext ) )
1126 return true;
1127 }
1128
1129 return false;
1130 }
1131
1132 default:
1134 return false;
1135 }
1136}
1137
1138
1139std::vector<VECTOR2I> EDA_SHAPE::GetRectCorners() const
1140{
1141 std::vector<VECTOR2I> pts;
1142 VECTOR2I topLeft = GetStart();
1143 VECTOR2I botRight = GetEnd();
1144
1145 pts.emplace_back( topLeft );
1146 pts.emplace_back( botRight.x, topLeft.y );
1147 pts.emplace_back( botRight );
1148 pts.emplace_back( topLeft.x, botRight.y );
1149
1150 return pts;
1151}
1152
1153
1155{
1156 // Start, end, and each inflection point the arc crosses will enclose the entire arc.
1157 // Only include the center when filled; it's not necessarily inside the BB of an unfilled
1158 // arc with a small included angle.
1159 aBBox.SetOrigin( m_start );
1160 aBBox.Merge( m_end );
1161
1162 if( IsFilled() )
1163 aBBox.Merge( m_arcCenter );
1164
1165 int radius = GetRadius();
1166 EDA_ANGLE t1, t2;
1167
1168 CalcArcAngles( t1, t2 );
1169
1170 t1.Normalize();
1171 t2.Normalize();
1172
1173 if( t2 > t1 )
1174 {
1175 if( t1 < ANGLE_0 && t2 > ANGLE_0 )
1176 aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
1177
1178 if( t1 < ANGLE_90 && t2 > ANGLE_90 )
1179 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
1180
1181 if( t1 < ANGLE_180 && t2 > ANGLE_180 )
1182 aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
1183
1184 if( t1 < ANGLE_270 && t2 > ANGLE_270 )
1185 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
1186 }
1187 else
1188 {
1189 if( t1 < ANGLE_0 || t2 > ANGLE_0 )
1190 aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
1191
1192 if( t1 < ANGLE_90 || t2 > ANGLE_90 )
1193 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
1194
1195 if( t1 < ANGLE_180 || t2 > ANGLE_180 )
1196 aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
1197
1198 if( t1 < ANGLE_270 || t2 > ANGLE_270 )
1199 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
1200 }
1201}
1202
1203
1204void EDA_SHAPE::SetPolyPoints( const std::vector<VECTOR2I>& aPoints )
1205{
1208
1209 for( const VECTOR2I& p : aPoints )
1210 m_poly.Append( p.x, p.y );
1211}
1212
1213
1214std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineChainOnly ) const
1215{
1216 std::vector<SHAPE*> effectiveShapes;
1217 int width = GetEffectiveWidth();
1218
1219 switch( m_shape )
1220 {
1221 case SHAPE_T::ARC:
1222 effectiveShapes.emplace_back( new SHAPE_ARC( m_arcCenter, m_start, GetArcAngle(), width ) );
1223 break;
1224
1225 case SHAPE_T::SEGMENT:
1226 effectiveShapes.emplace_back( new SHAPE_SEGMENT( m_start, m_end, width ) );
1227 break;
1228
1229 case SHAPE_T::RECTANGLE:
1230 {
1231 std::vector<VECTOR2I> pts = GetRectCorners();
1232
1233 if( ( IsFilled() || IsProxyItem() ) && !aEdgeOnly )
1234 effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
1235
1236 if( width > 0 || !IsFilled() || aEdgeOnly )
1237 {
1238 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], width ) );
1239 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], width ) );
1240 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], width ) );
1241 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], width ) );
1242 }
1243 }
1244 break;
1245
1246 case SHAPE_T::CIRCLE:
1247 {
1248 if( IsFilled() && !aEdgeOnly )
1249 effectiveShapes.emplace_back( new SHAPE_CIRCLE( getCenter(), GetRadius() ) );
1250
1251 if( width > 0 || !IsFilled() || aEdgeOnly )
1252 effectiveShapes.emplace_back( new SHAPE_ARC( getCenter(), GetEnd(), ANGLE_360, width ) );
1253
1254 break;
1255 }
1256
1257 case SHAPE_T::BEZIER:
1258 {
1259 std::vector<VECTOR2I> bezierPoints = buildBezierToSegmentsPointsList( width );
1260 VECTOR2I start_pt = bezierPoints[0];
1261
1262 for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ )
1263 {
1264 VECTOR2I end_pt = bezierPoints[jj];
1265 effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, width ) );
1266 start_pt = end_pt;
1267 }
1268
1269 break;
1270 }
1271
1272 case SHAPE_T::POLY:
1273 {
1274 if( GetPolyShape().OutlineCount() == 0 ) // malformed/empty polygon
1275 break;
1276
1277 for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
1278 {
1279 const SHAPE_LINE_CHAIN& l = GetPolyShape().COutline( ii );
1280
1281 if( IsFilled() && !aEdgeOnly )
1282 effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) );
1283
1284 if( width > 0 || !IsFilled() || aEdgeOnly )
1285 {
1286 int segCount = l.SegmentCount();
1287
1288 if( aLineChainOnly && l.IsClosed() )
1289 segCount--; // Treat closed chain as open
1290
1291 for( int jj = 0; jj < segCount; jj++ )
1292 effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.CSegment( jj ), width ) );
1293 }
1294 }
1295 }
1296 break;
1297
1298 default:
1300 break;
1301 }
1302
1303 return effectiveShapes;
1304}
1305
1306
1307void EDA_SHAPE::DupPolyPointsList( std::vector<VECTOR2I>& aBuffer ) const
1308{
1309 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1310 {
1311 int pointCount = m_poly.COutline( ii ).PointCount();
1312
1313 if( pointCount )
1314 {
1315 aBuffer.reserve( pointCount );
1316
1317 for ( auto iter = m_poly.CIterate(); iter; iter++ )
1318 aBuffer.emplace_back( iter->x, iter->y );
1319 }
1320 }
1321}
1322
1323
1325{
1326 // return true if the polygonal shape is valid (has more than 2 points)
1327 return GetPolyShape().OutlineCount() > 0 && GetPolyShape().Outline( 0 ).PointCount() > 2;
1328}
1329
1330
1332{
1333 // return the number of corners of the polygonal shape
1334 // this shape is expected to be only one polygon without hole
1335 return GetPolyShape().OutlineCount() ? GetPolyShape().VertexCount( 0 ) : 0;
1336}
1337
1338
1339void EDA_SHAPE::beginEdit( const VECTOR2I& aPosition )
1340{
1341 switch( GetShape() )
1342 {
1343 case SHAPE_T::SEGMENT:
1344 case SHAPE_T::CIRCLE:
1345 case SHAPE_T::RECTANGLE:
1346 SetStart( aPosition );
1347 SetEnd( aPosition );
1348 break;
1349
1350 case SHAPE_T::ARC:
1351 SetArcGeometry( aPosition, aPosition, aPosition );
1352 m_editState = 1;
1353 break;
1354
1355 case SHAPE_T::BEZIER:
1356 SetStart( aPosition );
1357 SetEnd( aPosition );
1358 SetBezierC1( aPosition );
1359 SetBezierC2( aPosition );
1360 m_editState = 1;
1361
1363 break;
1364
1365 case SHAPE_T::POLY:
1367 m_poly.Outline( 0 ).SetClosed( false );
1368
1369 // Start and end of the first segment (co-located for now)
1370 m_poly.Outline( 0 ).Append( aPosition );
1371 m_poly.Outline( 0 ).Append( aPosition, true );
1372 break;
1373
1374 default:
1376 }
1377}
1378
1379
1380bool EDA_SHAPE::continueEdit( const VECTOR2I& aPosition )
1381{
1382 switch( GetShape() )
1383 {
1384 case SHAPE_T::ARC:
1385 case SHAPE_T::SEGMENT:
1386 case SHAPE_T::CIRCLE:
1387 case SHAPE_T::RECTANGLE:
1388 return false;
1389
1390 case SHAPE_T::BEZIER:
1391 if( m_editState == 3 )
1392 return false;
1393
1394 m_editState++;
1395 return true;
1396
1397 case SHAPE_T::POLY:
1398 {
1399 SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1400
1401 // do not add zero-length segments
1402 if( poly.CPoint( poly.GetPointCount() - 2 ) != poly.CLastPoint() )
1403 poly.Append( aPosition, true );
1404 }
1405 return true;
1406
1407 default:
1409 return false;
1410 }
1411}
1412
1413
1414void EDA_SHAPE::calcEdit( const VECTOR2I& aPosition )
1415{
1416#define sq( x ) pow( x, 2 )
1417
1418 switch( GetShape() )
1419 {
1420 case SHAPE_T::SEGMENT:
1421 case SHAPE_T::CIRCLE:
1422 case SHAPE_T::RECTANGLE:
1423 SetEnd( aPosition );
1424 break;
1425
1426 case SHAPE_T::BEZIER:
1427 {
1428 switch( m_editState )
1429 {
1430 case 0:
1431 SetStart( aPosition );
1432 SetEnd( aPosition );
1433 SetBezierC1( aPosition );
1434 SetBezierC2( aPosition );
1435 break;
1436 case 1:
1437 SetBezierC2( aPosition );
1438 SetEnd( aPosition );
1439 break;
1440 case 2: SetBezierC1( aPosition ); break;
1441 case 3: SetBezierC2( aPosition ); break;
1442 }
1443
1445 }
1446 break;
1447
1448 case SHAPE_T::ARC:
1449 {
1450 double radius = GetRadius();
1451 EDA_ANGLE lastAngle = GetArcAngle();
1452
1453 // Edit state 0: drawing: place start
1454 // Edit state 1: drawing: place end (center calculated for 90-degree subtended angle)
1455 // Edit state 2: point edit: move start (center calculated for invariant subtended angle)
1456 // Edit state 3: point edit: move end (center calculated for invariant subtended angle)
1457 // Edit state 4: point edit: move center
1458 // Edit state 5: point edit: move arc-mid-point
1459
1460 switch( m_editState )
1461 {
1462 case 0:
1463 SetArcGeometry( aPosition, aPosition, aPosition );
1464 return;
1465
1466 case 1:
1467 m_end = aPosition;
1468 radius = sqrt( sq( GetLineLength( m_start, m_end ) ) / 2.0 );
1469 break;
1470
1471 case 2:
1472 case 3:
1473 {
1474 VECTOR2I v = m_start - m_end;
1475 double chordBefore = sq( v.x ) + sq( v.y );
1476
1477 if( m_editState == 2 )
1478 m_start = aPosition;
1479 else
1480 m_end = aPosition;
1481
1482 v = m_start - m_end;
1483
1484 double chordAfter = sq( v.x ) + sq( v.y );
1485 double ratio = 0.0;
1486
1487 if( chordBefore > 0 )
1488 ratio = chordAfter / chordBefore;
1489
1490 if( ratio != 0 )
1491 radius = std::max( sqrt( sq( radius ) * ratio ), sqrt( chordAfter ) / 2 );
1492 }
1493 break;
1494
1495 case 4:
1496 {
1497 double radialA = GetLineLength( m_start, aPosition );
1498 double radialB = GetLineLength( m_end, aPosition );
1499 radius = ( radialA + radialB ) / 2.0;
1500 }
1501 break;
1502
1503 case 5:
1504 SetArcGeometry( GetStart(), aPosition, GetEnd() );
1505 return;
1506 }
1507
1508 // Calculate center based on start, end, and radius
1509 //
1510 // Let 'l' be the length of the chord and 'm' the middle point of the chord
1511 double l = GetLineLength( m_start, m_end );
1512 VECTOR2D m = ( m_start + m_end ) / 2;
1513 double sqRadDiff = sq( radius ) - sq( l / 2 );
1514
1515 // Calculate 'd', the vector from the chord midpoint to the center
1516 VECTOR2D d;
1517
1518 if( l > 0 && sqRadDiff >= 0 )
1519 {
1520 d.x = sqrt( sqRadDiff ) * ( m_start.y - m_end.y ) / l;
1521 d.y = sqrt( sqRadDiff ) * ( m_end.x - m_start.x ) / l;
1522 }
1523
1524 VECTOR2I c1 = KiROUND( m + d );
1525 VECTOR2I c2 = KiROUND( m - d );
1526
1527 // Solution gives us 2 centers; we need to pick one:
1528 switch( m_editState )
1529 {
1530 case 1:
1531 // Keep arc clockwise while drawing i.e. arc angle = 90 deg.
1532 // it can be 90 or 270 deg depending on the arc center choice (c1 or c2)
1533 m_arcCenter = c1; // first trial
1534
1535 if( GetArcAngle() > ANGLE_180 )
1536 m_arcCenter = c2;
1537
1538 break;
1539
1540 case 2:
1541 case 3:
1542 // Pick the one of c1, c2 to keep arc on the same side
1543 m_arcCenter = c1; // first trial
1544
1545 if( ( lastAngle < ANGLE_180 ) != ( GetArcAngle() < ANGLE_180 ) )
1546 m_arcCenter = c2;
1547
1548 break;
1549
1550 case 4:
1551 // Pick the one closer to the mouse position
1552 m_arcCenter = GetLineLength( c1, aPosition ) < GetLineLength( c2, aPosition ) ? c1 : c2;
1553 break;
1554 }
1555 }
1556 break;
1557
1558 case SHAPE_T::POLY:
1559 m_poly.Outline( 0 ).SetPoint( m_poly.Outline( 0 ).GetPointCount() - 1, aPosition );
1560 break;
1561
1562 default:
1564 }
1565}
1566
1567
1568void EDA_SHAPE::endEdit( bool aClosed )
1569{
1570 switch( GetShape() )
1571 {
1572 case SHAPE_T::ARC:
1573 case SHAPE_T::SEGMENT:
1574 case SHAPE_T::CIRCLE:
1575 case SHAPE_T::RECTANGLE:
1576 case SHAPE_T::BEZIER:
1577 break;
1578
1579 case SHAPE_T::POLY:
1580 {
1581 SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1582
1583 // do not include last point twice
1584 if( poly.GetPointCount() > 2 )
1585 {
1586 if( poly.CPoint( poly.GetPointCount() - 2 ) == poly.CLastPoint() )
1587 {
1588 poly.SetClosed( aClosed );
1589 }
1590 else
1591 {
1592 poly.SetClosed( false );
1593 poly.Remove( poly.GetPointCount() - 1 );
1594 }
1595 }
1596 }
1597 break;
1598
1599 default:
1601 }
1602}
1603
1604
1606{
1607 EDA_SHAPE* image = dynamic_cast<EDA_SHAPE*>( aImage );
1608 assert( image );
1609
1610 #define SWAPITEM( x ) std::swap( x, image->x )
1611 SWAPITEM( m_stroke );
1612 SWAPITEM( m_start );
1613 SWAPITEM( m_end );
1615 SWAPITEM( m_shape );
1619 SWAPITEM( m_poly );
1620 SWAPITEM( m_fill );
1624 #undef SWAPITEM
1625}
1626
1627
1628int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const
1629{
1630#define EPSILON 2 // Should be enough for rounding errors on calculated items
1631
1632#define TEST( a, b ) { if( a != b ) return a - b; }
1633#define TEST_E( a, b ) { if( abs( a - b ) > EPSILON ) return a - b; }
1634#define TEST_PT( a, b ) { TEST_E( a.x, b.x ); TEST_E( a.y, b.y ); }
1635
1636 TEST_PT( m_start, aOther->m_start );
1637 TEST_PT( m_end, aOther->m_end );
1638
1639 TEST( (int) m_shape, (int) aOther->m_shape );
1640
1641 if( m_shape == SHAPE_T::ARC )
1642 {
1643 TEST_PT( m_arcCenter, aOther->m_arcCenter );
1644 }
1645 else if( m_shape == SHAPE_T::BEZIER )
1646 {
1647 TEST_PT( m_bezierC1, aOther->m_bezierC1 );
1648 TEST_PT( m_bezierC2, aOther->m_bezierC2 );
1649 }
1650 else if( m_shape == SHAPE_T::POLY )
1651 {
1653 }
1654
1655 for( size_t ii = 0; ii < m_bezierPoints.size(); ++ii )
1656 TEST_PT( m_bezierPoints[ii], aOther->m_bezierPoints[ii] );
1657
1658 for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
1659 TEST_PT( m_poly.CVertex( ii ), aOther->m_poly.CVertex( ii ) );
1660
1661 TEST_E( m_stroke.GetWidth(), aOther->m_stroke.GetWidth() );
1662 TEST( (int) m_stroke.GetLineStyle(), (int) aOther->m_stroke.GetLineStyle() );
1663 TEST( (int) m_fill, (int) aOther->m_fill );
1664
1665 return 0;
1666}
1667
1668
1669void EDA_SHAPE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, int aClearance, int aError,
1670 ERROR_LOC aErrorLoc, bool ignoreLineWidth ) const
1671{
1672 int width = ignoreLineWidth ? 0 : GetWidth();
1673
1674 width += 2 * aClearance;
1675
1676 switch( m_shape )
1677 {
1678 case SHAPE_T::CIRCLE:
1679 {
1680 int r = GetRadius();
1681
1682 if( IsFilled() )
1683 TransformCircleToPolygon( aBuffer, getCenter(), r + width / 2, aError, aErrorLoc );
1684 else
1685 TransformRingToPolygon( aBuffer, getCenter(), r, width, aError, aErrorLoc );
1686
1687 break;
1688 }
1689
1690 case SHAPE_T::RECTANGLE:
1691 {
1692 std::vector<VECTOR2I> pts = GetRectCorners();
1693
1694 if( IsFilled() || IsProxyItem() )
1695 {
1696 aBuffer.NewOutline();
1697
1698 for( const VECTOR2I& pt : pts )
1699 aBuffer.Append( pt );
1700 }
1701
1702 if( width > 0 || !IsFilled() )
1703 {
1704 // Add in segments
1705 TransformOvalToPolygon( aBuffer, pts[0], pts[1], width, aError, aErrorLoc );
1706 TransformOvalToPolygon( aBuffer, pts[1], pts[2], width, aError, aErrorLoc );
1707 TransformOvalToPolygon( aBuffer, pts[2], pts[3], width, aError, aErrorLoc );
1708 TransformOvalToPolygon( aBuffer, pts[3], pts[0], width, aError, aErrorLoc );
1709 }
1710
1711 break;
1712 }
1713
1714 case SHAPE_T::ARC:
1715 TransformArcToPolygon( aBuffer, GetStart(), GetArcMid(), GetEnd(), width, aError, aErrorLoc );
1716 break;
1717
1718 case SHAPE_T::SEGMENT:
1719 TransformOvalToPolygon( aBuffer, GetStart(), GetEnd(), width, aError, aErrorLoc );
1720 break;
1721
1722 case SHAPE_T::POLY:
1723 {
1724 if( !IsPolyShapeValid() )
1725 break;
1726
1727 if( IsFilled() )
1728 {
1729 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1730 {
1731 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
1732 SHAPE_POLY_SET tmp;
1733 tmp.NewOutline();
1734
1735 for( int jj = 0; jj < (int) poly.GetPointCount(); ++jj )
1736 tmp.Append( poly.GetPoint( jj ) );
1737
1738 if( width > 0 )
1739 {
1740 int inflate = width / 2;
1741
1742 if( aErrorLoc == ERROR_OUTSIDE )
1743 inflate += aError;
1744
1745 tmp.Inflate( inflate, CORNER_STRATEGY::ROUND_ALL_CORNERS, aError );
1746 }
1747
1748 aBuffer.Append( tmp );
1749 }
1750 }
1751 else
1752 {
1753 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1754 {
1755 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
1756
1757 for( int jj = 0; jj < (int) poly.SegmentCount(); ++jj )
1758 {
1759 const SEG& seg = poly.GetSegment( jj );
1760 TransformOvalToPolygon( aBuffer, seg.A, seg.B, width, aError, aErrorLoc );
1761 }
1762 }
1763 }
1764
1765 break;
1766 }
1767
1768 case SHAPE_T::BEZIER:
1769 {
1770 std::vector<VECTOR2I> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
1771 BEZIER_POLY converter( ctrlPts );
1772 std::vector<VECTOR2I> poly;
1773 converter.GetPoly( poly, GetWidth() );
1774
1775 for( unsigned ii = 1; ii < poly.size(); ii++ )
1776 TransformOvalToPolygon( aBuffer, poly[ii - 1], poly[ii], width, aError, aErrorLoc );
1777
1778 break;
1779 }
1780
1781 default:
1783 break;
1784 }
1785}
1786
1787
1789{
1790 m_stroke.SetLineStyle( aStyle );
1791}
1792
1793
1795{
1796 if( m_stroke.GetLineStyle() != LINE_STYLE::DEFAULT )
1797 return m_stroke.GetLineStyle();
1798
1799 return LINE_STYLE::SOLID;
1800}
1801
1802
1803bool EDA_SHAPE::operator==( const EDA_SHAPE& aOther ) const
1804{
1805 if( GetShape() != aOther.GetShape() )
1806 return false;
1807
1808 if( m_fill != aOther.m_fill )
1809 return false;
1810
1811 if( m_stroke.GetWidth() != aOther.m_stroke.GetWidth() )
1812 return false;
1813
1814 if( m_stroke.GetLineStyle() != aOther.m_stroke.GetLineStyle() )
1815 return false;
1816
1817 if( m_fillColor != aOther.m_fillColor )
1818 return false;
1819
1820 if( m_start != aOther.m_start )
1821 return false;
1822
1823 if( m_end != aOther.m_end )
1824 return false;
1825
1826 if( m_arcCenter != aOther.m_arcCenter )
1827 return false;
1828
1829 if( m_bezierC1 != aOther.m_bezierC1 )
1830 return false;
1831
1832 if( m_bezierC2 != aOther.m_bezierC2 )
1833 return false;
1834
1835 if( m_bezierPoints != aOther.m_bezierPoints )
1836 return false;
1837
1838 for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
1839 {
1840 if( m_poly.CVertex( ii ) != aOther.m_poly.CVertex( ii ) )
1841 return false;
1842 }
1843
1844 return true;
1845}
1846
1847
1848double EDA_SHAPE::Similarity( const EDA_SHAPE& aOther ) const
1849{
1850 if( GetShape() != aOther.GetShape() )
1851 return 0.0;
1852
1853 double similarity = 1.0;
1854
1855 if( m_fill != aOther.m_fill )
1856 similarity *= 0.9;
1857
1858 if( m_stroke.GetWidth() != aOther.m_stroke.GetWidth() )
1859 similarity *= 0.9;
1860
1861 if( m_stroke.GetLineStyle() != aOther.m_stroke.GetLineStyle() )
1862 similarity *= 0.9;
1863
1864 if( m_fillColor != aOther.m_fillColor )
1865 similarity *= 0.9;
1866
1867 if( m_start != aOther.m_start )
1868 similarity *= 0.9;
1869
1870 if( m_end != aOther.m_end )
1871 similarity *= 0.9;
1872
1873 if( m_arcCenter != aOther.m_arcCenter )
1874 similarity *= 0.9;
1875
1876 if( m_bezierC1 != aOther.m_bezierC1 )
1877 similarity *= 0.9;
1878
1879 if( m_bezierC2 != aOther.m_bezierC2 )
1880 similarity *= 0.9;
1881
1882 {
1883 int m = m_bezierPoints.size();
1884 int n = aOther.m_bezierPoints.size();
1885
1886 size_t longest = alg::longest_common_subset( m_bezierPoints, aOther.m_bezierPoints );
1887
1888 similarity *= std::pow( 0.9, m + n - 2 * longest );
1889 }
1890
1891 {
1892 int m = m_poly.TotalVertices();
1893 int n = aOther.m_poly.TotalVertices();
1894 std::vector<VECTOR2I> poly;
1895 std::vector<VECTOR2I> otherPoly;
1896 VECTOR2I lastPt( 0, 0 );
1897
1898 // We look for the longest common subset of the two polygons, but we need to
1899 // offset each point because we're actually looking for overall similarity, not just
1900 // exact matches. So if the zone is moved by 1IU, we only want one point to be
1901 // considered "moved" rather than the entire polygon. In this case, the first point
1902 // will not be a match but the rest of the sequence will.
1903 for( int ii = 0; ii < m; ++ii )
1904 {
1905 poly.emplace_back( lastPt - m_poly.CVertex( ii ) );
1906 lastPt = m_poly.CVertex( ii );
1907 }
1908
1909 lastPt = VECTOR2I( 0, 0 );
1910
1911 for( int ii = 0; ii < n; ++ii )
1912 {
1913 otherPoly.emplace_back( lastPt - aOther.m_poly.CVertex( ii ) );
1914 lastPt = aOther.m_poly.CVertex( ii );
1915 }
1916
1917 size_t longest = alg::longest_common_subset( poly, otherPoly );
1918
1919 similarity *= std::pow( 0.9, m + n - 2 * longest );
1920 }
1921
1922 return similarity;
1923}
1924
1925
1928
1929
1930static struct EDA_SHAPE_DESC
1931{
1933 {
1935 .Map( SHAPE_T::SEGMENT, _HKI( "Segment" ) )
1936 .Map( SHAPE_T::RECTANGLE, _HKI( "Rectangle" ) )
1937 .Map( SHAPE_T::ARC, _HKI( "Arc" ) )
1938 .Map( SHAPE_T::CIRCLE, _HKI( "Circle" ) )
1939 .Map( SHAPE_T::POLY, _HKI( "Polygon" ) )
1940 .Map( SHAPE_T::BEZIER, _HKI( "Bezier" ) );
1941
1942 auto& plotDashTypeEnum = ENUM_MAP<LINE_STYLE>::Instance();
1943
1944 if( plotDashTypeEnum.Choices().GetCount() == 0 )
1945 {
1946 plotDashTypeEnum.Map( LINE_STYLE::DEFAULT, _HKI( "Default" ) )
1947 .Map( LINE_STYLE::SOLID, _HKI( "Solid" ) )
1948 .Map( LINE_STYLE::DASH, _HKI( "Dashed" ) )
1949 .Map( LINE_STYLE::DOT, _HKI( "Dotted" ) )
1950 .Map( LINE_STYLE::DASHDOT, _HKI( "Dash-Dot" ) )
1951 .Map( LINE_STYLE::DASHDOTDOT, _HKI( "Dash-Dot-Dot" ) );
1952 }
1953
1956
1957 auto isNotPolygonOrCircle = []( INSPECTABLE* aItem ) -> bool
1958 {
1959 // Polygons, unlike other shapes, have no meaningful start or end coordinates
1960 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
1961 return shape->GetShape() != SHAPE_T::POLY && shape->GetShape() != SHAPE_T::CIRCLE;
1962
1963 return false;
1964 };
1965
1966 auto isCircle = []( INSPECTABLE* aItem ) -> bool
1967 {
1968 // Polygons, unlike other shapes, have no meaningful start or end coordinates
1969 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
1970 return shape->GetShape() == SHAPE_T::CIRCLE;
1971
1972 return false;
1973 };
1974
1975 const wxString shapeProps = _HKI( "Shape Properties" );
1976
1977 auto shape = new PROPERTY_ENUM<EDA_SHAPE, SHAPE_T>( _HKI( "Shape" ),
1979 propMgr.AddProperty( shape, shapeProps );
1980
1981 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start X" ),
1984 shapeProps )
1985 .SetAvailableFunc( isNotPolygonOrCircle );
1986 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start Y" ),
1989 shapeProps )
1990 .SetAvailableFunc( isNotPolygonOrCircle );
1991
1992 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Center X" ),
1995 shapeProps )
1996 .SetAvailableFunc( isCircle );
1997
1998 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Center Y" ),
2001 shapeProps )
2002 .SetAvailableFunc( isCircle );
2003
2004 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Radius" ),
2007 shapeProps )
2008 .SetAvailableFunc( isCircle );
2009
2010 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End X" ),
2013 shapeProps )
2014 .SetAvailableFunc( isNotPolygonOrCircle );
2015
2016 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End Y" ),
2019 shapeProps )
2020 .SetAvailableFunc( isNotPolygonOrCircle );
2021
2022 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Line Width" ),
2024 shapeProps );
2025
2026 void ( EDA_SHAPE::*lineStyleSetter )( LINE_STYLE ) = &EDA_SHAPE::SetLineStyle;
2027 propMgr.AddProperty( new PROPERTY_ENUM<EDA_SHAPE, LINE_STYLE>( _HKI( "Line Style" ),
2028 lineStyleSetter, &EDA_SHAPE::GetLineStyle ),
2029 shapeProps );
2030
2031 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, COLOR4D>( _HKI( "Line Color" ),
2033 shapeProps )
2034 .SetIsHiddenFromRulesEditor();
2035
2036 auto angle = new PROPERTY<EDA_SHAPE, EDA_ANGLE>( _HKI( "Angle" ),
2039 angle->SetAvailableFunc(
2040 [=]( INSPECTABLE* aItem ) -> bool
2041 {
2042 if( EDA_SHAPE* curr_shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2043 return curr_shape->GetShape() == SHAPE_T::ARC;
2044
2045 return false;
2046 } );
2047 propMgr.AddProperty( angle, shapeProps );
2048
2049 auto fillAvailable =
2050 [=]( INSPECTABLE* aItem ) -> bool
2051 {
2052 if( EDA_ITEM* edaItem = dynamic_cast<EDA_ITEM*>( aItem ) )
2053 {
2054 // For some reason masking "Filled" and "Fill Color" at the
2055 // PCB_TABLECELL level doesn't work.
2056 if( edaItem->Type() == PCB_TABLECELL_T )
2057 return false;
2058 }
2059
2060 if( EDA_SHAPE* edaShape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2061 {
2062 switch( edaShape->GetShape() )
2063 {
2064 case SHAPE_T::POLY:
2065 case SHAPE_T::RECTANGLE:
2066 case SHAPE_T::CIRCLE:
2067 return true;
2068
2069 default:
2070 return false;
2071 }
2072 }
2073
2074 return false;
2075 };
2076
2077 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, bool>( _HKI( "Filled" ),
2079 shapeProps )
2080 .SetAvailableFunc( fillAvailable );
2081
2082 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, COLOR4D>( _HKI( "Fill Color" ),
2084 shapeProps )
2085 .SetAvailableFunc( fillAvailable )
2087 }
Bezier curves to polygon converter.
Definition: bezier_curves.h:38
void GetPoly(std::vector< VECTOR2I > &aOutput, int aMinSegLen=0, int aMaxSegCount=32)
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:255
bool IsCardinal() const
Definition: eda_angle.cpp:40
EDA_ANGLE Normalize720()
Definition: eda_angle.h:305
double AsRadians() const
Definition: eda_angle.h:159
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:88
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:663
SHAPE_T m_shape
Definition: eda_shape.h:407
void SetStartX(int x)
Definition: eda_shape.h:141
bool m_endsSwapped
Definition: eda_shape.h:406
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:201
void SetBezierC2(const VECTOR2I &aPt)
Definition: eda_shape.h:200
void move(const VECTOR2I &aMoveVector)
Definition: eda_shape.cpp:244
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:545
VECTOR2I getCenter() const
Definition: eda_shape.cpp:519
int GetStartY() const
Definition: eda_shape.h:126
void SetLength(const double &aLength)
Definition: eda_shape.cpp:177
int m_editState
Definition: eda_shape.h:430
void rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle)
Definition: eda_shape.cpp:338
void flip(const VECTOR2I &aCentre, bool aFlipLeftRight)
Definition: eda_shape.cpp:395
virtual ~EDA_SHAPE()
Definition: eda_shape.cpp:57
long long int m_rectangleHeight
Definition: eda_shape.h:412
void SetEndY(int aY)
Definition: eda_shape.h:172
void RebuildBezierToSegmentsPointsList(int aMinSegLen)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:480
virtual int GetEffectiveWidth() const
Definition: eda_shape.h:111
COLOR4D GetLineColor() const
Definition: eda_shape.h:117
int GetEndX() const
Definition: eda_shape.h:164
int GetRectangleWidth() const
Definition: eda_shape.cpp:165
void SetLineStyle(const LINE_STYLE aStyle)
Definition: eda_shape.cpp:1788
const std::vector< VECTOR2I > buildBezierToSegmentsPointsList(int aMinSegLen) const
Definition: eda_shape.cpp:506
void calcEdit(const VECTOR2I &aPosition)
Definition: eda_shape.cpp:1414
void SetStartY(int y)
Definition: eda_shape.h:135
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:274
bool IsFilled() const
Definition: eda_shape.h:91
void SetCenterY(int y)
Definition: eda_shape.h:147
void CalcArcAngles(EDA_ANGLE &aStartAngle, EDA_ANGLE &aEndAngle) const
Calc arc start and end angles such that aStartAngle < aEndAngle.
Definition: eda_shape.cpp:577
void ShapeGetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList)
Definition: eda_shape.cpp:733
void SetFilled(bool aFlag)
Definition: eda_shape.h:96
bool operator==(const EDA_SHAPE &aOther) const
Definition: eda_shape.cpp:1803
int GetRadius() const
Definition: eda_shape.cpp:593
SHAPE_T GetShape() const
Definition: eda_shape.h:120
VECTOR2I m_arcCenter
Definition: eda_shape.h:421
double m_segmentLength
Definition: eda_shape.h:415
void SetCenterX(int x)
Definition: eda_shape.h:153
bool continueEdit(const VECTOR2I &aPosition)
Definition: eda_shape.cpp:1380
wxString ShowShape() const
Definition: eda_shape.cpp:62
ARC_MID m_arcMidData
Definition: eda_shape.h:422
void SetFillColor(const COLOR4D &aColor)
Definition: eda_shape.h:107
int GetEndY() const
Definition: eda_shape.h:163
std::vector< SHAPE * > makeEffectiveShapes(bool aEdgeOnly, bool aLineChainOnly=false) const
Make a set of SHAPE objects representing the EDA_SHAPE.
Definition: eda_shape.cpp:1214
bool hitTest(const VECTOR2I &aPosition, int aAccuracy=0) const
Definition: eda_shape.cpp:844
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:616
void SetEndX(int aX)
Definition: eda_shape.h:178
EDA_SHAPE(SHAPE_T aType, int aLineWidth, FILL_T aFill)
Definition: eda_shape.cpp:42
void beginEdit(const VECTOR2I &aStartPoint)
Definition: eda_shape.cpp:1339
VECTOR2I m_start
Definition: eda_shape.h:418
int GetPointCount() const
Definition: eda_shape.cpp:1331
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:162
bool IsClosed() const
Definition: eda_shape.cpp:212
void SetRadius(int aX)
Definition: eda_shape.h:184
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:129
void DupPolyPointsList(std::vector< VECTOR2I > &aBuffer) const
Duplicate the list of corners in a std::vector<VECTOR2I>
Definition: eda_shape.cpp:1307
LINE_STYLE GetLineStyle() const
Definition: eda_shape.cpp:1794
void endEdit(bool aClosed=true)
Finishes editing the shape.
Definition: eda_shape.cpp:1568
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:125
void SetLineColor(const COLOR4D &aColor)
Definition: eda_shape.h:116
COLOR4D GetFillColor() const
Definition: eda_shape.h:106
void SetRectangle(const long long int &aHeight, const long long int &aWidth)
Definition: eda_shape.cpp:188
void SwapShape(EDA_SHAPE *aImage)
Definition: eda_shape.cpp:1605
std::vector< VECTOR2I > GetRectCorners() const
Definition: eda_shape.cpp:1139
void SetSegmentAngle(const EDA_ANGLE &aAngle)
Definition: eda_shape.cpp:202
std::vector< VECTOR2I > m_bezierPoints
Definition: eda_shape.h:427
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:1154
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:166
void SetBezierC1(const VECTOR2I &aPt)
Definition: eda_shape.h:197
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:625
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:281
int GetStartX() const
Definition: eda_shape.h:127
double Similarity(const EDA_SHAPE &aOther) const
Definition: eda_shape.cpp:1848
const VECTOR2I & GetBezierC1() const
Definition: eda_shape.h:198
VECTOR2I m_end
Definition: eda_shape.h:419
const BOX2I getBoundingBox() const
Definition: eda_shape.cpp:790
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:691
SHAPE_POLY_SET m_poly
Definition: eda_shape.h:428
int GetRectangleHeight() const
Definition: eda_shape.cpp:153
virtual int GetWidth() const
Definition: eda_shape.h:110
VECTOR2I getPosition() const
Definition: eda_shape.cpp:112
bool IsClockwiseArc() const
Definition: eda_shape.cpp:674
STROKE_PARAMS m_stroke
Definition: eda_shape.h:408
EDA_ANGLE m_segmentAngle
Definition: eda_shape.h:416
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
Definition: eda_shape.cpp:1204
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:1669
wxString getFriendlyName() const
Definition: eda_shape.cpp:706
VECTOR2I m_bezierC1
Definition: eda_shape.h:424
FILL_T m_fill
Definition: eda_shape.h:409
COLOR4D m_fillColor
Definition: eda_shape.h:410
void SetWidth(int aWidth)
Definition: eda_shape.h:109
EDA_ANGLE GetSegmentAngle() const
Definition: eda_shape.cpp:654
long long int m_rectangleWidth
Definition: eda_shape.h:413
VECTOR2I m_bezierC2
Definition: eda_shape.h:425
bool IsPolyShapeValid() const
Definition: eda_shape.cpp:1324
int Compare(const EDA_SHAPE *aOther) const
Definition: eda_shape.cpp:1628
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:563
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:326
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:276
VECTOR2_TRAITS< int >::extended_type extended_type
Definition: vector2d.h:72
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:435
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:437
@ RADIANS_T
Definition: eda_angle.h:32
static constexpr EDA_ANGLE ANGLE_270
Definition: eda_angle.h:440
static constexpr EDA_ANGLE ANGLE_360
Definition: eda_angle.h:441
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:439
#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:424
#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:174
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:228
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:520
double GetLineLength(const VECTOR2I &aPointA, const VECTOR2I &aPointB)
Return the length of a line segment defined by aPointA and aPointB.
Definition: trigo.h:194
double EuclideanNorm(const VECTOR2I &vector)
Definition: trigo.h:128
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition: typeinfo.h:95
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:118
VECTOR2< int > VECTOR2I
Definition: vector2d.h:602