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