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