KiCad PCB EDA Suite
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) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
27#include <bezier_curves.h>
28#include <base_units.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, PLOT_DASH_TYPE::DEFAULT, COLOR4D::UNSPECIFIED ),
44 m_fill( aFill ),
45 m_fillColor( COLOR4D::UNSPECIFIED ),
46 m_editState( 0 ),
47 m_annotationProxy( false )
48{
49}
50
51
53{
54}
55
56
57wxString EDA_SHAPE::ShowShape() const
58{
59 if( IsAnnotationProxy() )
60 return _( "Number Box" );
61
62 switch( m_shape )
63 {
64 case SHAPE_T::SEGMENT: return _( "Line" );
65 case SHAPE_T::RECT: return _( "Rect" );
66 case SHAPE_T::ARC: return _( "Arc" );
67 case SHAPE_T::CIRCLE: return _( "Circle" );
68 case SHAPE_T::BEZIER: return _( "Bezier Curve" );
69 case SHAPE_T::POLY: return _( "Polygon" );
70 default: return wxT( "??" );
71 }
72}
73
74
76{
77 switch( m_shape )
78 {
79 case SHAPE_T::SEGMENT: return "S_SEGMENT";
80 case SHAPE_T::RECT: return "S_RECT";
81 case SHAPE_T::ARC: return "S_ARC";
82 case SHAPE_T::CIRCLE: return "S_CIRCLE";
83 case SHAPE_T::POLY: return "S_POLYGON";
84 case SHAPE_T::BEZIER: return "S_CURVE";
85 case SHAPE_T::LAST: return "!S_LAST!"; // Synthetic value, but if we come across it then
86 // we're going to want to know.
87 }
88
89 return wxEmptyString; // Just to quiet GCC.
90}
91
92
94{
95 move( aPos - getPosition() );
96}
97
98
100{
101 if( m_shape == SHAPE_T::ARC )
102 return getCenter();
103 else if( m_shape == SHAPE_T::POLY )
104 return m_poly.CVertex( 0 );
105 else
106 return m_start;
107}
108
109
111{
112 double length = 0.0;
113
114 switch( m_shape )
115 {
116 case SHAPE_T::BEZIER:
117 for( size_t ii = 1; ii < m_bezierPoints.size(); ++ii )
118 length += GetLineLength( m_bezierPoints[ ii - 1], m_bezierPoints[ii] );
119
120 return length;
121
122 case SHAPE_T::SEGMENT:
123 return GetLineLength( GetStart(), GetEnd() );
124
125 case SHAPE_T::POLY:
126 for( int ii = 0; ii < m_poly.COutline( 0 ).SegmentCount(); ii++ )
127 length += m_poly.COutline( 0 ).CSegment( ii ).Length();
128
129 return length;
130
131 case SHAPE_T::ARC:
132 return GetRadius() * GetArcAngle().AsRadians();
133
134 default:
136 return 0.0;
137 }
138}
139
140
141void EDA_SHAPE::move( const VECTOR2I& aMoveVector )
142{
143 switch ( m_shape )
144 {
145 case SHAPE_T::ARC:
146 case SHAPE_T::SEGMENT:
147 case SHAPE_T::RECT:
148 case SHAPE_T::CIRCLE:
149 m_start += aMoveVector;
150 m_end += aMoveVector;
151 m_arcCenter += aMoveVector;
152 break;
153
154 case SHAPE_T::POLY:
155 m_poly.Move( VECTOR2I( aMoveVector ) );
156 break;
157
158 case SHAPE_T::BEZIER:
159 m_start += aMoveVector;
160 m_end += aMoveVector;
161 m_bezierC1 += aMoveVector;
162 m_bezierC2 += aMoveVector;
163
164 for( VECTOR2I& pt : m_bezierPoints )
165 pt += aMoveVector;
166
167 break;
168
169 default:
171 break;
172 }
173}
174
175
176void EDA_SHAPE::scale( double aScale )
177{
178 auto scalePt = [&]( VECTOR2I& pt )
179 {
180 pt.x = KiROUND( pt.x * aScale );
181 pt.y = KiROUND( pt.y * aScale );
182 };
183
184 switch( m_shape )
185 {
186 case SHAPE_T::ARC:
187 case SHAPE_T::SEGMENT:
188 case SHAPE_T::RECT:
189 scalePt( m_start );
190 scalePt( m_end );
191 scalePt( m_arcCenter );
192 break;
193
194 case SHAPE_T::CIRCLE: // ring or circle
195 scalePt( m_start );
196 m_end.x = m_start.x + KiROUND( GetRadius() * aScale );
197 m_end.y = m_start.y;
198 break;
199
200 case SHAPE_T::POLY: // polygon
201 {
202 std::vector<VECTOR2I> pts;
203
204 for( const VECTOR2I& pt : m_poly.Outline( 0 ).CPoints() )
205 {
206 pts.emplace_back( pt );
207 scalePt( pts.back() );
208 }
209
210 SetPolyPoints( pts );
211 }
212 break;
213
214 case SHAPE_T::BEZIER:
215 scalePt( m_start );
216 scalePt( m_end );
217 scalePt( m_bezierC1 );
218 scalePt( m_bezierC2 );
219 break;
220
221 default:
223 break;
224 }
225}
226
227
228void EDA_SHAPE::rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
229{
230 switch( m_shape )
231 {
232 case SHAPE_T::SEGMENT:
233 case SHAPE_T::CIRCLE:
234 RotatePoint( m_start, aRotCentre, aAngle );
235 RotatePoint( m_end, aRotCentre, aAngle );
236 break;
237
238 case SHAPE_T::ARC:
239 RotatePoint( m_start, aRotCentre, aAngle );
240 RotatePoint( m_end, aRotCentre, aAngle );
241 RotatePoint( m_arcCenter, aRotCentre, aAngle );
242 break;
243
244 case SHAPE_T::RECT:
245 if( aAngle.IsCardinal() )
246 {
247 RotatePoint( m_start, aRotCentre, aAngle );
248 RotatePoint( m_end, aRotCentre, aAngle );
249 break;
250 }
251
252 // Convert non-cardinally-rotated rect to a diamond
260
262
263 case SHAPE_T::POLY:
264 m_poly.Rotate( aAngle, aRotCentre );
265 break;
266
267 case SHAPE_T::BEZIER:
268 RotatePoint( m_start, aRotCentre, aAngle );
269 RotatePoint( m_end, aRotCentre, aAngle );
270 RotatePoint( m_bezierC1, aRotCentre, aAngle );
271 RotatePoint( m_bezierC2, aRotCentre, aAngle );
272
273 for( VECTOR2I& pt : m_bezierPoints )
274 RotatePoint( pt, aRotCentre, aAngle);
275
276 break;
277
278 default:
280 break;
281 }
282}
283
284
285void EDA_SHAPE::flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
286{
287 switch ( m_shape )
288 {
289 case SHAPE_T::SEGMENT:
290 case SHAPE_T::RECT:
291 if( aFlipLeftRight )
292 {
293 m_start.x = aCentre.x - ( m_start.x - aCentre.x );
294 m_end.x = aCentre.x - ( m_end.x - aCentre.x );
295 }
296 else
297 {
298 m_start.y = aCentre.y - ( m_start.y - aCentre.y );
299 m_end.y = aCentre.y - ( m_end.y - aCentre.y );
300 }
301
302 std::swap( m_start, m_end );
303 break;
304
305 case SHAPE_T::CIRCLE:
306 if( aFlipLeftRight )
307 {
308 m_start.x = aCentre.x - ( m_start.x - aCentre.x );
309 m_end.x = aCentre.x - ( m_end.x - aCentre.x );
310 }
311 else
312 {
313 m_start.y = aCentre.y - ( m_start.y - aCentre.y );
314 m_end.y = aCentre.y - ( m_end.y - aCentre.y );
315 }
316 break;
317
318 case SHAPE_T::ARC:
319 if( aFlipLeftRight )
320 {
321 m_start.x = aCentre.x - ( m_start.x - aCentre.x );
322 m_end.x = aCentre.x - ( m_end.x - aCentre.x );
323 m_arcCenter.x = aCentre.x - ( m_arcCenter.x - aCentre.x );
324 }
325 else
326 {
327 m_start.y = aCentre.y - ( m_start.y - aCentre.y );
328 m_end.y = aCentre.y - ( m_end.y - aCentre.y );
329 m_arcCenter.y = aCentre.y - ( m_arcCenter.y - aCentre.y );
330 }
331
332 std::swap( m_start, m_end );
333 break;
334
335 case SHAPE_T::POLY:
336 m_poly.Mirror( aFlipLeftRight, !aFlipLeftRight, aCentre );
337 break;
338
339 case SHAPE_T::BEZIER:
340 if( aFlipLeftRight )
341 {
342 m_start.x = aCentre.x - ( m_start.x - aCentre.x );
343 m_end.x = aCentre.x - ( m_end.x - aCentre.x );
344 m_bezierC1.x = aCentre.x - ( m_bezierC1.x - aCentre.x );
345 m_bezierC2.x = aCentre.x - ( m_bezierC2.x - aCentre.x );
346 }
347 else
348 {
349 m_start.y = aCentre.y - ( m_start.y - aCentre.y );
350 m_end.y = aCentre.y - ( m_end.y - aCentre.y );
351 m_bezierC1.y = aCentre.y - ( m_bezierC1.y - aCentre.y );
352 m_bezierC2.y = aCentre.y - ( m_bezierC2.y - aCentre.y );
353 }
354
355 // Rebuild the poly points shape
356 {
357 std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
358 BEZIER_POLY converter( ctrlPoints );
359 converter.GetPoly( m_bezierPoints, m_stroke.GetWidth() );
360 }
361 break;
362
363 default:
365 break;
366 }
367}
368
369
371{
372 // Has meaning only for SHAPE_T::BEZIER
373 if( m_shape != SHAPE_T::BEZIER )
374 {
375 m_bezierPoints.clear();
376 return;
377 }
378
379 // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
381}
382
383
384const std::vector<VECTOR2I> EDA_SHAPE::buildBezierToSegmentsPointsList( int aMinSegLen ) const
385{
386 std::vector<VECTOR2I> bezierPoints;
387
388 // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
389 std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
390 BEZIER_POLY converter( ctrlPoints );
391 converter.GetPoly( bezierPoints, aMinSegLen );
392
393 return bezierPoints;
394}
395
396
398{
399 switch( m_shape )
400 {
401 case SHAPE_T::ARC:
402 return m_arcCenter;
403
404 case SHAPE_T::CIRCLE:
405 return m_start;
406
407 case SHAPE_T::SEGMENT:
408 // Midpoint of the line
409 return ( m_start + m_end ) / 2;
410
411 case SHAPE_T::POLY:
412 case SHAPE_T::RECT:
413 case SHAPE_T::BEZIER:
414 return getBoundingBox().Centre();
415
416 default:
418 return VECTOR2I();
419 }
420}
421
422
423void EDA_SHAPE::SetCenter( const VECTOR2I& aCenter )
424{
425 switch( m_shape )
426 {
427 case SHAPE_T::ARC:
428 m_arcCenter = aCenter;
429 break;
430
431 case SHAPE_T::CIRCLE:
432 m_start = aCenter;
433 break;
434
435 default:
437 }
438}
439
440
442{
443 // If none of the input data have changed since we loaded the arc,
444 // keep the original mid point data to minimize churn
447 return m_arcMidData.mid;
448
449 VECTOR2I mid = m_start;
450 RotatePoint( mid, m_arcCenter, -GetArcAngle() / 2.0 );
451 return mid;
452}
453
454
455void EDA_SHAPE::CalcArcAngles( EDA_ANGLE& aStartAngle, EDA_ANGLE& aEndAngle ) const
456{
457 VECTOR2D startRadial( GetStart() - getCenter() );
458 VECTOR2D endRadial( GetEnd() - getCenter() );
459
460 aStartAngle = EDA_ANGLE( startRadial );
461 aEndAngle = EDA_ANGLE( endRadial );
462
463 if( aEndAngle == aStartAngle )
464 aEndAngle = aStartAngle + ANGLE_360; // ring, not null
465
466 if( aStartAngle > aEndAngle )
467 {
468 if( aEndAngle < ANGLE_0 )
469 aEndAngle.Normalize();
470 else
471 aStartAngle = aStartAngle.Normalize() - ANGLE_360;
472 }
473}
474
475
477{
478 double radius = 0.0;
479
480 switch( m_shape )
481 {
482 case SHAPE_T::ARC:
483 radius = GetLineLength( m_arcCenter, m_start );
484 break;
485
486 case SHAPE_T::CIRCLE:
487 radius = GetLineLength( m_start, m_end );
488 break;
489
490 default:
492 }
493
494 // don't allow degenerate circles/arcs
495 return std::max( 1, KiROUND( radius ) );
496}
497
498
499void EDA_SHAPE::SetCachedArcData( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCenter )
500{
501 m_arcMidData.start = aStart;
502 m_arcMidData.end = aEnd;
503 m_arcMidData.center = aCenter;
504 m_arcMidData.mid = aMid;
505}
506
507
508void EDA_SHAPE::SetArcGeometry( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
509{
510 m_arcMidData = {};
511 m_start = aStart;
512 m_end = aEnd;
513 m_arcCenter = CalcArcCenter( aStart, aMid, aEnd );
514 VECTOR2I new_mid = GetArcMid();
515
516 m_endsSwapped = false;
517
518 // Watch the ordering here. GetArcMid above needs to be called prior to initializing the
519 // m_arcMidData structure in order to ensure we get the calculated variant, not the cached
520 SetCachedArcData( aStart, aMid, aEnd, m_arcCenter );
521
522 /*
523 * If the input winding doesn't match our internal winding, the calculated midpoint will end
524 * up on the other side of the arc. In this case, we need to flip the start/end points and
525 * flag this change for the system.
526 */
527 VECTOR2D dist( new_mid - aMid );
528 VECTOR2D dist2( new_mid - m_arcCenter );
529
530 if( dist.SquaredEuclideanNorm() > dist2.SquaredEuclideanNorm() )
531 {
532 std::swap( m_start, m_end );
533 m_endsSwapped = true;
534 }
535}
536
537
539{
540 EDA_ANGLE startAngle;
541 EDA_ANGLE endAngle;
542
543 CalcArcAngles( startAngle, endAngle );
544
545 return endAngle - startAngle;
546}
547
548
549void EDA_SHAPE::SetArcAngleAndEnd( const EDA_ANGLE& aAngle, bool aCheckNegativeAngle )
550{
551 EDA_ANGLE angle( aAngle );
552
553 m_end = m_start;
554 RotatePoint( m_end, m_arcCenter, -angle.Normalize720() );
555
556 if( aCheckNegativeAngle && aAngle < ANGLE_0 )
557 {
558 std::swap( m_start, m_end );
559 m_endsSwapped = true;
560 }
561}
562
563
564void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
565{
566 ORIGIN_TRANSFORMS originTransforms = aFrame->GetOriginTransforms();
567 wxString msg;
568
569 wxString shape = _( "Shape" );
570
571 switch( m_shape )
572 {
573 case SHAPE_T::CIRCLE:
574 aList.emplace_back( shape, _( "Circle" ) );
575 aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
576 break;
577
578 case SHAPE_T::ARC:
579 aList.emplace_back( shape, _( "Arc" ) );
580
582 aList.emplace_back( _( "Angle" ), msg );
583
584 aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
585 break;
586
587 case SHAPE_T::BEZIER:
588 aList.emplace_back( shape, _( "Curve" ) );
589 aList.emplace_back( _( "Length" ), aFrame->MessageTextFromValue( GetLength() ) );
590 break;
591
592 case SHAPE_T::POLY:
593 aList.emplace_back( shape, _( "Polygon" ) );
594
595 msg.Printf( "%d", GetPolyShape().Outline(0).PointCount() );
596 aList.emplace_back( _( "Points" ), msg );
597 break;
598
599 case SHAPE_T::RECT:
600 if( IsAnnotationProxy() )
601 aList.emplace_back( shape, _( "Pad Number Box" ) );
602 else
603 aList.emplace_back( shape, _( "Rectangle" ) );
604
605 aList.emplace_back( _( "Width" ),
606 aFrame->MessageTextFromValue( std::abs( GetEnd().x - GetStart().x ) ) );
607
608 aList.emplace_back( _( "Height" ),
609 aFrame->MessageTextFromValue( std::abs( GetEnd().y - GetStart().y ) ) );
610 break;
611
612 case SHAPE_T::SEGMENT:
613 {
614 aList.emplace_back( shape, _( "Segment" ) );
615
616 aList.emplace_back( _( "Length" ),
618
619 // angle counter-clockwise from 3'o-clock
620 EDA_ANGLE angle( atan2( (double)( GetStart().y - GetEnd().y ),
621 (double)( GetEnd().x - GetStart().x ) ), RADIANS_T );
622 aList.emplace_back( _( "Angle" ), EDA_UNIT_UTILS::UI::MessageTextFromValue( angle ) );
623 break;
624 }
625
626 default:
627 aList.emplace_back( shape, _( "Unrecognized" ) );
628 break;
629 }
630
631 m_stroke.GetMsgPanelInfo( aFrame, aList );
632}
633
634
636{
637 BOX2I bbox;
638
639 switch( m_shape )
640 {
641 case SHAPE_T::RECT:
642 for( VECTOR2I& pt : GetRectCorners() )
643 bbox.Merge( pt );
644
645 break;
646
647 case SHAPE_T::SEGMENT:
648 bbox.SetOrigin( GetStart() );
649 bbox.SetEnd( GetEnd() );
650 break;
651
652 case SHAPE_T::CIRCLE:
653 bbox.SetOrigin( GetStart() );
654 bbox.Inflate( GetRadius() );
655 break;
656
657 case SHAPE_T::ARC:
658 computeArcBBox( bbox );
659 break;
660
661 case SHAPE_T::POLY:
662 if( m_poly.IsEmpty() )
663 break;
664
665 for( auto iter = m_poly.CIterate(); iter; iter++ )
666 {
667 VECTOR2I pt( iter->x, iter->y );
668
670 pt += getParentPosition();
671
672 bbox.Merge( pt );
673 }
674
675 break;
676
677 case SHAPE_T::BEZIER:
678 bbox.SetOrigin( GetStart() );
679 bbox.Merge( GetBezierC1() );
680 bbox.Merge( GetBezierC2() );
681 bbox.Merge( GetEnd() );
682 break;
683
684 default:
686 break;
687 }
688
689 bbox.Inflate( std::max( 0, GetWidth() ) / 2 );
690 bbox.Normalize();
691
692 return bbox;
693}
694
695
696bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
697{
698 int maxdist = aAccuracy;
699
700 if( GetWidth() > 0 )
701 maxdist += GetWidth() / 2;
702
703 switch( m_shape )
704 {
705 case SHAPE_T::CIRCLE:
706 {
707 int radius = GetRadius();
708
709 VECTOR2I::extended_type dist = KiROUND<double, VECTOR2I::extended_type>(
710 EuclideanNorm( aPosition - getCenter() ) );
711
712 if( IsFilled() )
713 return dist <= radius + maxdist; // Filled circle hit-test
714 else
715 return abs( radius - dist ) <= maxdist; // Ring hit-test
716 }
717
718 case SHAPE_T::ARC:
719 {
720 if( EuclideanNorm( aPosition - m_start ) <= maxdist )
721 return true;
722
723 if( EuclideanNorm( aPosition - m_end ) <= maxdist )
724 return true;
725
726 VECTOR2I relPos = aPosition - getCenter();
727 int radius = GetRadius();
728
730 KiROUND<double, VECTOR2I::extended_type>( EuclideanNorm( relPos ) );
731
732 if( IsFilled() )
733 {
734 // Check distance from arc center
735 if( dist > radius + maxdist )
736 return false;
737 }
738 else
739 {
740 // Check distance from arc circumference
741 if( abs( radius - dist ) > maxdist )
742 return false;
743 }
744
745 // Finally, check to see if it's within arc's swept angle.
746 EDA_ANGLE startAngle;
747 EDA_ANGLE endAngle;
748 CalcArcAngles( startAngle, endAngle );
749
750 EDA_ANGLE relPosAngle( relPos );
751
752 startAngle.Normalize();
753 endAngle.Normalize();
754 relPosAngle.Normalize();
755
756 if( endAngle > startAngle )
757 return relPosAngle >= startAngle && relPosAngle <= endAngle;
758 else
759 return relPosAngle >= startAngle || relPosAngle <= endAngle;
760 }
761
762 case SHAPE_T::BEZIER:
763 const_cast<EDA_SHAPE*>( this )->RebuildBezierToSegmentsPointsList( GetWidth() );
764
765 for( unsigned int i= 1; i < m_bezierPoints.size(); i++)
766 {
767 if( TestSegmentHit( aPosition, m_bezierPoints[ i - 1], m_bezierPoints[i], maxdist ) )
768 return true;
769 }
770
771 return false;
772
773 case SHAPE_T::SEGMENT:
774 return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
775
776 case SHAPE_T::RECT:
777 if( IsAnnotationProxy() || IsFilled() ) // Filled rect hit-test
778 {
779 SHAPE_POLY_SET poly;
780 poly.NewOutline();
781
782 for( const VECTOR2I& pt : GetRectCorners() )
783 poly.Append( pt );
784
785 return poly.Collide( aPosition, maxdist );
786 }
787 else // Open rect hit-test
788 {
789 std::vector<VECTOR2I> pts = GetRectCorners();
790
791 return TestSegmentHit( aPosition, pts[0], pts[1], maxdist )
792 || TestSegmentHit( aPosition, pts[1], pts[2], maxdist )
793 || TestSegmentHit( aPosition, pts[2], pts[3], maxdist )
794 || TestSegmentHit( aPosition, pts[3], pts[0], maxdist );
795 }
796
797 case SHAPE_T::POLY:
798 if( IsFilled() )
799 return m_poly.Collide( aPosition, maxdist );
800 else
801 return m_poly.CollideEdge( aPosition, nullptr, maxdist );
802
803 default:
805 return false;
806 }
807}
808
809
810bool EDA_SHAPE::hitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
811{
812 BOX2I arect = aRect;
813 arect.Normalize();
814 arect.Inflate( aAccuracy );
815
816 BOX2I bbox = getBoundingBox();
817
818 switch( m_shape )
819 {
820 case SHAPE_T::CIRCLE:
821 // Test if area intersects or contains the circle:
822 if( aContained )
823 {
824 return arect.Contains( bbox );
825 }
826 else
827 {
828 // If the rectangle does not intersect the bounding box, this is a much quicker test
829 if( !arect.Intersects( bbox ) )
830 return false;
831 else
832 return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
833 }
834
835 case SHAPE_T::ARC:
836 // Test for full containment of this arc in the rect
837 if( aContained )
838 {
839 return arect.Contains( bbox );
840 }
841 // Test if the rect crosses the arc
842 else
843 {
844 if( !arect.Intersects( bbox ) )
845 return false;
846
847 if( IsFilled() )
848 {
849 return ( arect.Intersects( getCenter(), GetStart() )
850 || arect.Intersects( getCenter(), GetEnd() )
851 || arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() ) );
852 }
853 else
854 {
855 return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
856 }
857 }
858
859 case SHAPE_T::RECT:
860 if( aContained )
861 {
862 return arect.Contains( bbox );
863 }
864 else
865 {
866 std::vector<VECTOR2I> pts = GetRectCorners();
867
868 // Account for the width of the lines
869 arect.Inflate( GetWidth() / 2 );
870 return ( arect.Intersects( pts[0], pts[1] )
871 || arect.Intersects( pts[1], pts[2] )
872 || arect.Intersects( pts[2], pts[3] )
873 || arect.Intersects( pts[3], pts[0] ) );
874 }
875
876 case SHAPE_T::SEGMENT:
877 if( aContained )
878 {
879 return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
880 }
881 else
882 {
883 // Account for the width of the line
884 arect.Inflate( GetWidth() / 2 );
885 return arect.Intersects( GetStart(), GetEnd() );
886 }
887
888 case SHAPE_T::POLY:
889 if( aContained )
890 {
891 return arect.Contains( bbox );
892 }
893 else
894 {
895 // Fast test: if aRect is outside the polygon bounding box,
896 // rectangles cannot intersect
897 if( !arect.Intersects( bbox ) )
898 return false;
899
900 // Account for the width of the line
901 arect.Inflate( GetWidth() / 2 );
902
903 // Polygons in footprints use coordinates relative to the footprint.
904 // Therefore, instead of using m_poly, we make a copy which is translated
905 // to the actual location in the board.
906 VECTOR2I offset = getParentPosition();
907
908 SHAPE_LINE_CHAIN poly = m_poly.Outline( 0 );
910 poly.Move( offset );
911
912 int count = poly.GetPointCount();
913
914 for( int ii = 0; ii < count; ii++ )
915 {
916 VECTOR2I vertex = poly.GetPoint( ii );
917
918 // Test if the point is within aRect
919 if( arect.Contains( vertex ) )
920 return true;
921
922 if( ii + 1 < count )
923 {
924 VECTOR2I vertexNext = poly.GetPoint( ii + 1 );
925
926 // Test if this edge intersects aRect
927 if( arect.Intersects( vertex, vertexNext ) )
928 return true;
929 }
930 else if( poly.IsClosed() )
931 {
932 VECTOR2I vertexNext = poly.GetPoint( 0 );
933
934 // Test if this edge intersects aRect
935 if( arect.Intersects( vertex, vertexNext ) )
936 return true;
937 }
938 }
939
940 return false;
941 }
942
943 case SHAPE_T::BEZIER:
944 if( aContained )
945 {
946 return arect.Contains( bbox );
947 }
948 else
949 {
950 // Fast test: if aRect is outside the polygon bounding box,
951 // rectangles cannot intersect
952 if( !arect.Intersects( bbox ) )
953 return false;
954
955 // Account for the width of the line
956 arect.Inflate( GetWidth() / 2 );
957 unsigned count = m_bezierPoints.size();
958
959 for( unsigned ii = 1; ii < count; ii++ )
960 {
961 VECTOR2I vertex = m_bezierPoints[ii - 1];
962 VECTOR2I vertexNext = m_bezierPoints[ii];
963
964 // Test if the point is within aRect
965 if( arect.Contains( vertex ) )
966 return true;
967
968 // Test if this edge intersects aRect
969 if( arect.Intersects( vertex, vertexNext ) )
970 return true;
971 }
972
973 return false;
974 }
975
976 default:
978 return false;
979 }
980}
981
982
983std::vector<VECTOR2I> EDA_SHAPE::GetRectCorners() const
984{
985 std::vector<VECTOR2I> pts;
986 VECTOR2I topLeft = GetStart();
987 VECTOR2I botRight = GetEnd();
988
989 // Un-rotate rect topLeft and botRight
990 if( !getParentOrientation().IsCardinal() )
991 {
992 topLeft -= getParentPosition();
993 RotatePoint( topLeft, -getParentOrientation() );
994
995 botRight -= getParentPosition();
996 RotatePoint( botRight, -getParentOrientation() );
997 }
998
999 // Set up the un-rotated 4 corners
1000 pts.emplace_back( topLeft );
1001 pts.emplace_back( botRight.x, topLeft.y );
1002 pts.emplace_back( botRight );
1003 pts.emplace_back( topLeft.x, botRight.y );
1004
1005 // Now re-rotate the 4 corners to get a diamond
1006 if( !getParentOrientation().IsCardinal() )
1007 {
1008 for( VECTOR2I& pt : pts )
1009 {
1011 pt += getParentPosition();
1012 }
1013 }
1014
1015 return pts;
1016}
1017
1018
1020{
1021 // Start, end, and each inflection point the arc crosses will enclose the entire arc.
1022 // Only include the center when filled; it's not necessarily inside the BB of an unfilled
1023 // arc with a small included angle.
1024 aBBox.SetOrigin( m_start );
1025 aBBox.Merge( m_end );
1026
1027 if( IsFilled() )
1028 aBBox.Merge( m_arcCenter );
1029
1030 int radius = GetRadius();
1031 EDA_ANGLE t1, t2;
1032
1033 CalcArcAngles( t1, t2 );
1034
1035 t1.Normalize();
1036 t2.Normalize();
1037
1038 if( t2 > t1 )
1039 {
1040 if( t1 < ANGLE_0 && t2 > ANGLE_0 )
1041 aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
1042
1043 if( t1 < ANGLE_90 && t2 > ANGLE_90 )
1044 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
1045
1046 if( t1 < ANGLE_180 && t2 > ANGLE_180 )
1047 aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
1048
1049 if( t1 < ANGLE_270 && t2 > ANGLE_270 )
1050 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
1051 }
1052 else
1053 {
1054 if( t1 < ANGLE_0 || t2 > ANGLE_0 )
1055 aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
1056
1057 if( t1 < ANGLE_90 || t2 > ANGLE_90 )
1058 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
1059
1060 if( t1 < ANGLE_180 || t2 > ANGLE_180 )
1061 aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
1062
1063 if( t1 < ANGLE_270 || t2 > ANGLE_270 )
1064 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
1065 }
1066}
1067
1068
1069void EDA_SHAPE::SetPolyPoints( const std::vector<VECTOR2I>& aPoints )
1070{
1073
1074 for( const VECTOR2I& p : aPoints )
1075 m_poly.Append( p.x, p.y );
1076}
1077
1078
1079std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineChainOnly ) const
1080{
1081 std::vector<SHAPE*> effectiveShapes;
1082 int width = GetEffectiveWidth();
1083
1084 switch( m_shape )
1085 {
1086 case SHAPE_T::ARC:
1087 effectiveShapes.emplace_back( new SHAPE_ARC( m_arcCenter, m_start, GetArcAngle(), width ) );
1088 break;
1089
1090 case SHAPE_T::SEGMENT:
1091 effectiveShapes.emplace_back( new SHAPE_SEGMENT( m_start, m_end, width ) );
1092 break;
1093
1094 case SHAPE_T::RECT:
1095 {
1096 std::vector<VECTOR2I> pts = GetRectCorners();
1097
1098 if( ( IsFilled() || IsAnnotationProxy() ) && !aEdgeOnly )
1099 effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
1100
1101 if( width > 0 || !IsFilled() || aEdgeOnly )
1102 {
1103 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], width ) );
1104 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], width ) );
1105 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], width ) );
1106 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], width ) );
1107 }
1108 }
1109 break;
1110
1111 case SHAPE_T::CIRCLE:
1112 {
1113 if( IsFilled() && !aEdgeOnly )
1114 effectiveShapes.emplace_back( new SHAPE_CIRCLE( getCenter(), GetRadius() ) );
1115
1116 if( width > 0 || !IsFilled() || aEdgeOnly )
1117 effectiveShapes.emplace_back( new SHAPE_ARC( getCenter(), GetEnd(), ANGLE_360, width ) );
1118
1119 break;
1120 }
1121
1122 case SHAPE_T::BEZIER:
1123 {
1124 std::vector<VECTOR2I> bezierPoints = buildBezierToSegmentsPointsList( width );
1125 VECTOR2I start_pt = bezierPoints[0];
1126
1127 for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ )
1128 {
1129 VECTOR2I end_pt = bezierPoints[jj];
1130 effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, width ) );
1131 start_pt = end_pt;
1132 }
1133
1134 break;
1135 }
1136
1137 case SHAPE_T::POLY:
1138 {
1139 if( GetPolyShape().OutlineCount() == 0 ) // malformed/empty polygon
1140 break;
1141
1143
1144 if( aLineChainOnly )
1145 l.SetClosed( false );
1146
1148 l.Move( getParentPosition() );
1149
1150 if( IsFilled() && !aEdgeOnly )
1151 effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) );
1152
1153 if( width > 0 || !IsFilled() || aEdgeOnly )
1154 {
1155 for( int i = 0; i < l.SegmentCount(); i++ )
1156 effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( i ), width ) );
1157 }
1158 }
1159 break;
1160
1161 default:
1163 break;
1164 }
1165
1166 return effectiveShapes;
1167}
1168
1169
1170void EDA_SHAPE::DupPolyPointsList( std::vector<VECTOR2I>& aBuffer ) const
1171{
1172 if( m_poly.OutlineCount() )
1173 {
1174 int pointCount = m_poly.COutline( 0 ).PointCount();
1175
1176 if( pointCount )
1177 {
1178 aBuffer.reserve( pointCount );
1179
1180 for ( auto iter = m_poly.CIterate(); iter; iter++ )
1181 aBuffer.emplace_back( iter->x, iter->y );
1182 }
1183 }
1184}
1185
1186
1188{
1189 // return true if the polygonal shape is valid (has more than 2 points)
1190 if( GetPolyShape().OutlineCount() == 0 )
1191 return false;
1192
1193 const SHAPE_LINE_CHAIN& outline = static_cast<const SHAPE_POLY_SET&>( GetPolyShape() ).Outline( 0 );
1194
1195 return outline.PointCount() > 2;
1196}
1197
1198
1200{
1201 // return the number of corners of the polygonal shape
1202 // this shape is expected to be only one polygon without hole
1203 if( GetPolyShape().OutlineCount() )
1204 return GetPolyShape().VertexCount( 0 );
1205
1206 return 0;
1207}
1208
1209
1210void EDA_SHAPE::beginEdit( const VECTOR2I& aPosition )
1211{
1212 switch( GetShape() )
1213 {
1214 case SHAPE_T::SEGMENT:
1215 case SHAPE_T::CIRCLE:
1216 case SHAPE_T::RECT:
1217 SetStart( aPosition );
1218 SetEnd( aPosition );
1219 break;
1220
1221 case SHAPE_T::ARC:
1222 SetArcGeometry( aPosition, aPosition, aPosition );
1223 m_editState = 1;
1224 break;
1225
1226 case SHAPE_T::POLY:
1228 m_poly.Outline( 0 ).SetClosed( false );
1229
1230 // Start and end of the first segment (co-located for now)
1231 m_poly.Outline( 0 ).Append( aPosition );
1232 m_poly.Outline( 0 ).Append( aPosition, true );
1233 break;
1234
1235 default:
1237 }
1238}
1239
1240
1241bool EDA_SHAPE::continueEdit( const VECTOR2I& aPosition )
1242{
1243 switch( GetShape() )
1244 {
1245 case SHAPE_T::ARC:
1246 case SHAPE_T::SEGMENT:
1247 case SHAPE_T::CIRCLE:
1248 case SHAPE_T::RECT:
1249 return false;
1250
1251 case SHAPE_T::POLY:
1252 {
1253 SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1254
1255 // do not add zero-length segments
1256 if( poly.CPoint( poly.GetPointCount() - 2 ) != poly.CLastPoint() )
1257 poly.Append( aPosition, true );
1258 }
1259 return true;
1260
1261 default:
1263 return false;
1264 }
1265}
1266
1267
1268void EDA_SHAPE::calcEdit( const VECTOR2I& aPosition )
1269{
1270#define sq( x ) pow( x, 2 )
1271
1272 switch( GetShape() )
1273 {
1274 case SHAPE_T::SEGMENT:
1275 case SHAPE_T::CIRCLE:
1276 case SHAPE_T::RECT:
1277 SetEnd( aPosition );
1278 break;
1279
1280 case SHAPE_T::ARC:
1281 {
1282 int radius = GetRadius();
1283
1284 // Edit state 0: drawing: place start
1285 // Edit state 1: drawing: place end (center calculated for 90-degree subtended angle)
1286 // Edit state 2: point edit: move start (center calculated for invariant subtended angle)
1287 // Edit state 3: point edit: move end (center calculated for invariant subtended angle)
1288 // Edit state 4: point edit: move center
1289 // Edit state 5: point edit: move arc-mid-point
1290
1291 switch( m_editState )
1292 {
1293 case 0:
1294 SetArcGeometry( aPosition, aPosition, aPosition );
1295 return;
1296
1297 case 1:
1298 m_end = aPosition;
1299 radius = KiROUND( sqrt( sq( GetLineLength( m_start, m_end ) ) / 2.0 ) );
1300 break;
1301
1302 case 2:
1303 case 3:
1304 {
1305 VECTOR2I v = m_start - m_end;
1306 double chordBefore = sq( v.x ) + sq( v.y );
1307
1308 if( m_editState == 2 )
1309 m_start = aPosition;
1310 else
1311 m_end = aPosition;
1312
1313 v = m_start - m_end;
1314 double chordAfter = sq( v.x ) + sq( v.y );
1315 double ratio = chordAfter / chordBefore;
1316
1317 if( ratio != 0 )
1318 {
1319 radius = std::max( int( sqrt( sq( radius ) * ratio ) ) + 1,
1320 int( sqrt( chordAfter ) / 2 ) + 1 );
1321 }
1322 }
1323 break;
1324
1325 case 4:
1326 {
1327 double radialA = GetLineLength( m_start, aPosition );
1328 double radialB = GetLineLength( m_end, aPosition );
1329 radius = int( ( radialA + radialB ) / 2.0 ) + 1;
1330 }
1331 break;
1332
1333 case 5:
1334 SetArcGeometry( GetStart(), aPosition, GetEnd() );
1335 return;
1336 }
1337
1338 // Calculate center based on start, end, and radius
1339 //
1340 // Let 'l' be the length of the chord and 'm' the middle point of the chord
1341 double l = GetLineLength( m_start, m_end );
1342 VECTOR2I m = ( m_start + m_end ) / 2;
1343
1344 // Calculate 'd', the vector from the chord midpoint to the center
1345 VECTOR2I d;
1346 d.x = KiROUND( sqrt( sq( radius ) - sq( l/2 ) ) * ( m_start.y - m_end.y ) / l );
1347 d.y = KiROUND( sqrt( sq( radius ) - sq( l/2 ) ) * ( m_end.x - m_start.x ) / l );
1348
1349 VECTOR2I c1 = m + d;
1350 VECTOR2I c2 = m - d;
1351
1352 // Solution gives us 2 centers; we need to pick one:
1353 switch( m_editState )
1354 {
1355 case 1:
1356 {
1357 // Keep center clockwise from chord while drawing
1358 VECTOR2I chordVector = m_end - m_start;
1359 EDA_ANGLE chordAngle( chordVector );
1360
1361 VECTOR2I c1Test = c1;
1362 RotatePoint( c1Test, m_start, -chordAngle.Normalize() );
1363
1364 m_arcCenter = c1Test.x > 0 ? c2 : c1;
1365 }
1366 break;
1367
1368 case 2:
1369 case 3:
1370 // Pick the one closer to the old center
1372 break;
1373
1374 case 4:
1375 // Pick the one closer to the mouse position
1376 m_arcCenter = GetLineLength( c1, aPosition ) < GetLineLength( c2, aPosition ) ? c1 : c2;
1377
1378 if( GetArcAngle() > ANGLE_180 )
1379 std::swap( m_start, m_end );
1380
1381 break;
1382 }
1383 }
1384 break;
1385
1386 case SHAPE_T::POLY:
1387 m_poly.Outline( 0 ).SetPoint( m_poly.Outline( 0 ).GetPointCount() - 1, aPosition );
1388 break;
1389
1390 default:
1392 }
1393}
1394
1395
1396void EDA_SHAPE::endEdit( bool aClosed )
1397{
1398 switch( GetShape() )
1399 {
1400 case SHAPE_T::ARC:
1401 case SHAPE_T::SEGMENT:
1402 case SHAPE_T::CIRCLE:
1403 case SHAPE_T::RECT:
1404 break;
1405
1406 case SHAPE_T::POLY:
1407 {
1408 SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1409
1410 // do not include last point twice
1411 if( poly.GetPointCount() > 2 )
1412 {
1413 if( poly.CPoint( poly.GetPointCount() - 2 ) == poly.CLastPoint() )
1414 {
1415 poly.SetClosed( aClosed );
1416 poly.Remove( poly.GetPointCount() - 1 );
1417 }
1418 }
1419 }
1420 break;
1421
1422 default:
1424 }
1425}
1426
1427
1429{
1430 EDA_SHAPE* image = dynamic_cast<EDA_SHAPE*>( aImage );
1431 assert( image );
1432
1433 #define SWAPITEM( x ) std::swap( x, image->x )
1434 SWAPITEM( m_stroke );
1435 SWAPITEM( m_start );
1436 SWAPITEM( m_end );
1438 SWAPITEM( m_shape );
1442 SWAPITEM( m_poly );
1443 SWAPITEM( m_fill );
1447 #undef SWAPITEM
1448}
1449
1450
1451int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const
1452{
1453#define EPSILON 2 // Should be enough for rounding errors on calculated items
1454
1455#define TEST( a, b ) { if( a != b ) return a - b; }
1456#define TEST_E( a, b ) { if( abs( a - b ) > EPSILON ) return a - b; }
1457#define TEST_PT( a, b ) { TEST_E( a.x, b.x ); TEST_E( a.y, b.y ); }
1458
1459 TEST_PT( m_start, aOther->m_start );
1460 TEST_PT( m_end, aOther->m_end );
1461
1462 TEST( (int) m_shape, (int) aOther->m_shape );
1463
1464 if( m_shape == SHAPE_T::ARC )
1465 {
1466 TEST_PT( m_arcCenter, aOther->m_arcCenter );
1467 }
1468 else if( m_shape == SHAPE_T::BEZIER )
1469 {
1470 TEST_PT( m_bezierC1, aOther->m_bezierC1 );
1471 TEST_PT( m_bezierC2, aOther->m_bezierC2 );
1472 }
1473 else if( m_shape == SHAPE_T::POLY )
1474 {
1476 }
1477
1478 for( size_t ii = 0; ii < m_bezierPoints.size(); ++ii )
1479 TEST_PT( m_bezierPoints[ii], aOther->m_bezierPoints[ii] );
1480
1481 for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
1482 TEST_PT( m_poly.CVertex( ii ), aOther->m_poly.CVertex( ii ) );
1483
1484 TEST_E( m_stroke.GetWidth(), aOther->m_stroke.GetWidth() );
1485 TEST( (int) m_stroke.GetPlotStyle(), (int) aOther->m_stroke.GetPlotStyle() );
1486 TEST( (int) m_fill, (int) aOther->m_fill );
1487
1488 return 0;
1489}
1490
1491
1493 int aClearanceValue,
1494 int aError, ERROR_LOC aErrorLoc,
1495 bool ignoreLineWidth ) const
1496{
1497 int width = ignoreLineWidth ? 0 : GetWidth();
1498
1499 width += 2 * aClearanceValue;
1500
1501 switch( m_shape )
1502 {
1503 case SHAPE_T::CIRCLE:
1504 if( IsFilled() )
1505 {
1506 TransformCircleToPolygon( aCornerBuffer, getCenter(), GetRadius() + width / 2, aError,
1507 aErrorLoc );
1508 }
1509 else
1510 {
1511 TransformRingToPolygon( aCornerBuffer, getCenter(), GetRadius(), width, aError,
1512 aErrorLoc );
1513 }
1514
1515 break;
1516
1517 case SHAPE_T::RECT:
1518 {
1519 std::vector<VECTOR2I> pts = GetRectCorners();
1520
1521 if( IsFilled() || IsAnnotationProxy() )
1522 {
1523 aCornerBuffer.NewOutline();
1524
1525 for( const VECTOR2I& pt : pts )
1526 aCornerBuffer.Append( pt );
1527 }
1528
1529 if( width > 0 || !IsFilled() )
1530 {
1531 // Add in segments
1532 TransformOvalToPolygon( aCornerBuffer, pts[0], pts[1], width, aError, aErrorLoc );
1533 TransformOvalToPolygon( aCornerBuffer, pts[1], pts[2], width, aError, aErrorLoc );
1534 TransformOvalToPolygon( aCornerBuffer, pts[2], pts[3], width, aError, aErrorLoc );
1535 TransformOvalToPolygon( aCornerBuffer, pts[3], pts[0], width, aError, aErrorLoc );
1536 }
1537
1538 break;
1539 }
1540
1541 case SHAPE_T::ARC:
1542 TransformArcToPolygon( aCornerBuffer, GetStart(), GetArcMid(), GetEnd(), width, aError,
1543 aErrorLoc );
1544 break;
1545
1546 case SHAPE_T::SEGMENT:
1547 TransformOvalToPolygon( aCornerBuffer, GetStart(), GetEnd(), width, aError, aErrorLoc );
1548 break;
1549
1550 case SHAPE_T::POLY:
1551 {
1552 if( !IsPolyShapeValid() )
1553 break;
1554
1555 // The polygon is expected to be a simple polygon; not self intersecting, no hole.
1556 EDA_ANGLE orientation = getParentOrientation();
1557 VECTOR2I offset = getParentPosition();
1558
1559 // Build the polygon with the actual position and orientation:
1560 std::vector<VECTOR2I> poly;
1561 DupPolyPointsList( poly );
1562
1563 for( VECTOR2I& point : poly )
1564 {
1565 RotatePoint( point, orientation );
1566 point += offset;
1567 }
1568
1569 if( IsFilled() )
1570 {
1571 aCornerBuffer.NewOutline();
1572
1573 for( const VECTOR2I& point : poly )
1574 aCornerBuffer.Append( point.x, point.y );
1575 }
1576
1577 if( width > 0 || !IsFilled() )
1578 {
1579 VECTOR2I pt1( poly[poly.size() - 1] );
1580
1581 for( const VECTOR2I& pt2 : poly )
1582 {
1583 if( pt2 != pt1 )
1584 TransformOvalToPolygon( aCornerBuffer, pt1, pt2, width, aError, aErrorLoc );
1585
1586 pt1 = pt2;
1587 }
1588 }
1589
1590 break;
1591 }
1592
1593 case SHAPE_T::BEZIER:
1594 {
1595 std::vector<VECTOR2I> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
1596 BEZIER_POLY converter( ctrlPts );
1597 std::vector<VECTOR2I> poly;
1598 converter.GetPoly( poly, GetWidth() );
1599
1600 for( unsigned ii = 1; ii < poly.size(); ii++ )
1601 {
1602 TransformOvalToPolygon( aCornerBuffer, poly[ii - 1], poly[ii], width, aError,
1603 aErrorLoc );
1604 }
1605
1606 break;
1607 }
1608
1609 default:
1611 break;
1612 }
1613}
1614
1615
1618
1619
1620static struct EDA_SHAPE_DESC
1621{
1623 {
1625 .Map( SHAPE_T::SEGMENT, _HKI( "Segment" ) )
1626 .Map( SHAPE_T::RECT, _HKI( "Rectangle" ) )
1627 .Map( SHAPE_T::ARC, _HKI( "Arc" ) )
1628 .Map( SHAPE_T::CIRCLE, _HKI( "Circle" ) )
1629 .Map( SHAPE_T::POLY, _HKI( "Polygon" ) )
1630 .Map( SHAPE_T::BEZIER, _HKI( "Bezier" ) );
1632 .Map( PLOT_DASH_TYPE::DEFAULT, _HKI( "Default" ) )
1633 .Map( PLOT_DASH_TYPE::SOLID, _HKI( "Solid" ) )
1634 .Map( PLOT_DASH_TYPE::DASH, _HKI( "Dashed" ) )
1635 .Map( PLOT_DASH_TYPE::DOT, _HKI( "Dotted" ) )
1636 .Map( PLOT_DASH_TYPE::DASHDOT, _HKI( "Dash-Dot" ) )
1637 .Map( PLOT_DASH_TYPE::DASHDOTDOT, _HKI( "Dash-Dot-Dot" ) );
1638
1641 auto shape = new PROPERTY_ENUM<EDA_SHAPE, SHAPE_T>( _HKI( "Shape" ),
1643 propMgr.AddProperty( shape );
1644 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start X" ),
1646 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start Y" ),
1648 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End X" ),
1650 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End Y" ),
1652 // TODO: m_arcCenter, m_bezierC1, m_bezierC2, m_poly
1653 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Line Width" ),
1655
1656 auto angle = new PROPERTY<EDA_SHAPE, EDA_ANGLE>( _HKI( "Angle" ),
1659 angle->SetAvailableFunc(
1660 [=]( INSPECTABLE* aItem ) -> bool
1661 {
1662 return aItem->Get<SHAPE_T>( shape ) == SHAPE_T::ARC;
1663 } );
1664 propMgr.AddProperty( angle );
1665 }
Bezier curves to polygon converter.
Definition: bezier_curves.h:36
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:202
BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition: box2.h:119
bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:269
bool IntersectsCircleEdge(const Vec &aCenter, const int aRadius, const int aWidth) const
Definition: box2.h:471
bool Contains(const Vec &aPoint) const
Definition: box2.h:141
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:506
Vec Centre() const
Definition: box2.h:70
void SetEnd(coord_type x, coord_type y)
Definition: box2.h:255
BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:588
EDA_ANGLE Normalize()
Definition: eda_angle.h:249
bool IsCardinal() const
Definition: eda_angle.cpp:49
double AsRadians() const
Definition: eda_angle.h:153
virtual ORIGIN_TRANSFORMS & GetOriginTransforms()
Return a reference to the default ORIGIN_TRANSFORMS object.
The base class for create windows for drawing purpose.
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:538
SHAPE_T m_shape
Definition: eda_shape.h:363
virtual VECTOR2I getParentPosition() const =0
void SetStartX(int x)
Definition: eda_shape.h:132
bool m_endsSwapped
Definition: eda_shape.h:362
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:175
virtual EDA_ANGLE getParentOrientation() const =0
void move(const VECTOR2I &aMoveVector)
Definition: eda_shape.cpp:141
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:423
VECTOR2I getCenter() const
Definition: eda_shape.cpp:397
int GetStartY() const
Definition: eda_shape.h:117
int m_editState
Definition: eda_shape.h:380
void rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle)
Definition: eda_shape.cpp:228
void flip(const VECTOR2I &aCentre, bool aFlipLeftRight)
Definition: eda_shape.cpp:285
virtual ~EDA_SHAPE()
Definition: eda_shape.cpp:52
void SetEndY(int y)
Definition: eda_shape.h:151
void RebuildBezierToSegmentsPointsList(int aMinSegLen)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:370
virtual int GetEffectiveWidth() const
Definition: eda_shape.h:108
int GetEndX() const
Definition: eda_shape.h:143
const std::vector< VECTOR2I > buildBezierToSegmentsPointsList(int aMinSegLen) const
Definition: eda_shape.cpp:384
void calcEdit(const VECTOR2I &aPosition)
Definition: eda_shape.cpp:1268
void SetStartY(int y)
Definition: eda_shape.h:126
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:243
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:455
void ShapeGetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList)
Definition: eda_shape.cpp:564
int GetRadius() const
Definition: eda_shape.cpp:476
SHAPE_T GetShape() const
Definition: eda_shape.h:111
VECTOR2I m_arcCenter
Definition: eda_shape.h:371
bool continueEdit(const VECTOR2I &aPosition)
Definition: eda_shape.cpp:1241
wxString ShowShape() const
Definition: eda_shape.cpp:57
ARC_MID m_arcMidData
Definition: eda_shape.h:372
int GetEndY() const
Definition: eda_shape.h:142
std::vector< SHAPE * > makeEffectiveShapes(bool aEdgeOnly, bool aLineChainOnly=false) const
Make a set of SHAPE objects representing the EDA_SHAPE.
Definition: eda_shape.cpp:1079
bool hitTest(const VECTOR2I &aPosition, int aAccuracy=0) const
Definition: eda_shape.cpp:696
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:499
EDA_SHAPE(SHAPE_T aType, int aLineWidth, FILL_T aFill)
Definition: eda_shape.cpp:40
void beginEdit(const VECTOR2I &aStartPoint)
Definition: eda_shape.cpp:1210
void SetEndX(int x)
Definition: eda_shape.h:157
VECTOR2I m_start
Definition: eda_shape.h:368
int GetPointCount() const
Definition: eda_shape.cpp:1199
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:141
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:120
void DupPolyPointsList(std::vector< VECTOR2I > &aBuffer) const
Duplicate the list of corners in a std::vector<VECTOR2I>
Definition: eda_shape.cpp:1170
void endEdit(bool aClosed=true)
Finishes editing the shape.
Definition: eda_shape.cpp:1396
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:116
void SwapShape(EDA_SHAPE *aImage)
Definition: eda_shape.cpp:1428
std::vector< VECTOR2I > GetRectCorners() const
Definition: eda_shape.cpp:983
std::vector< VECTOR2I > m_bezierPoints
Definition: eda_shape.h:377
int GetWidth() const
Definition: eda_shape.h:107
void setPosition(const VECTOR2I &aPos)
Definition: eda_shape.cpp:93
void TransformShapeWithClearanceToPolygon(SHAPE_POLY_SET &aCornerBuffer, int aClearanceValue, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth) const
Convert the shape to a closed polygon.
Definition: eda_shape.cpp:1492
void computeArcBBox(BOX2I &aBBox) const
Definition: eda_shape.cpp:1019
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:145
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:508
double GetLength() const
Return the length of the track using the hypotenuse calculation.
Definition: eda_shape.cpp:110
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:75
void scale(double aScale)
Definition: eda_shape.cpp:176
int GetStartX() const
Definition: eda_shape.h:118
const VECTOR2I & GetBezierC1() const
Definition: eda_shape.h:172
VECTOR2I m_end
Definition: eda_shape.h:369
const BOX2I getBoundingBox() const
Definition: eda_shape.cpp:635
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:549
SHAPE_POLY_SET m_poly
Definition: eda_shape.h:378
VECTOR2I getPosition() const
Definition: eda_shape.cpp:99
STROKE_PARAMS m_stroke
Definition: eda_shape.h:364
bool IsAnnotationProxy() const
Definition: eda_shape.h:87
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
Definition: eda_shape.cpp:1069
VECTOR2I m_bezierC1
Definition: eda_shape.h:374
FILL_T m_fill
Definition: eda_shape.h:365
COLOR4D m_fillColor
Definition: eda_shape.h:366
void SetWidth(int aWidth)
Definition: eda_shape.h:106
VECTOR2I m_bezierC2
Definition: eda_shape.h:375
bool IsPolyShapeValid() const
Definition: eda_shape.cpp:1187
int Compare(const EDA_SHAPE *aOther) const
Definition: eda_shape.cpp:1451
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:441
static ENUM_MAP< T > & Instance()
Definition: property.h:512
Class that other classes need to inherit from, in order to be inspectable.
Definition: inspectable.h:36
wxAny Get(PROPERTY_BASE *aProperty) const
Definition: inspectable.h:84
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
A class to perform either relative or absolute display origin transforms for a single axis of a point...
Provide class metadata.Helper macro to map type hashes to names.
Definition: property_mgr.h:63
void AddProperty(PROPERTY_BASE *aProperty)
Register a property.
static PROPERTY_MANAGER & Instance()
Definition: property_mgr.h:65
int Length() const
Return the length (this).
Definition: seg.h:351
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Move(const VECTOR2I &aVector) override
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.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
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
SEG Segment(int aIndex)
Return a copy of the aIndex-th segment in the line chain.
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.
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 points in the shape poly set.
bool IsEmpty() const
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
Delete aIdx-th polygon from the set.
int Append(int x, int y, int aOutline=-1, int aHole=-1, bool aAllowDuplication=false)
Add a new vertex to the contour indexed by aOutline and aHole (defaults to the outline of the last po...
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)
int NewOutline()
Creates a new hole in a given outline.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the aGlobalIndex-th vertex in the poly set.
int OutlineCount() const
Return the number of vertices in a given outline/hole.
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:98
void GetMsgPanelInfo(UNITS_PROVIDER *aUnitsProvider, std::vector< MSG_PANEL_ITEM > &aList, bool aIncludeStyle=true, bool aIncludeWidth=true)
PLOT_DASH_TYPE GetPlotStyle() const
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
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:300
VECTOR2_TRAITS< int >::extended_type extended_type
Definition: vector2d.h:76
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aCornerBuffer, 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 &aCornerBuffer, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arc to multiple straight segments.
void TransformRingToPolygon(SHAPE_POLY_SET &aCornerBuffer, const VECTOR2I &aCentre, int aRadius, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arcs to multiple straight segments.
void TransformOvalToPolygon(SHAPE_POLY_SET &aCornerBuffer, 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_180
Definition: eda_angle.h:416
@ RADIANS_T
Definition: eda_angle.h:32
static constexpr EDA_ANGLE & ANGLE_360
Definition: eda_angle.h:418
static constexpr EDA_ANGLE & ANGLE_90
Definition: eda_angle.h:414
static constexpr EDA_ANGLE & ANGLE_0
Definition: eda_angle.h:412
static constexpr EDA_ANGLE & ANGLE_270
Definition: eda_angle.h:417
#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:41
@ LAST
marker for list end
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...
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:120
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:323
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:401
Plot settings, and plotting engines (PostScript, Gerber, HPGL and DXF)
#define NO_SETTER(owner, type)
Definition: property.h:623
#define ENUM_TO_WXANY(type)
Macro to define read-only fields (no setter method available)
Definition: property.h:614
@ PT_COORD
Coordinate expressed in distance units (mm/inch)
Definition: property.h:54
@ PT_DECIDEGREE
Angle expressed in decidegrees.
Definition: property.h:56
@ PT_SIZE
Size expressed in distance units (mm/inch)
Definition: property.h:53
#define REGISTER_TYPE(x)
Definition: property_mgr.h:248
PLOT_DASH_TYPE
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:129
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Definition: trigo.cpp:183
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:472
double GetLineLength(const VECTOR2I &aPointA, const VECTOR2I &aPointB)
Return the length of a line segment defined by aPointA and aPointB.
Definition: trigo.h:188
double EuclideanNorm(const VECTOR2I &vector)
Definition: trigo.h:129
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:80
VECTOR2< int > VECTOR2I
Definition: vector2d.h:618