KiCad PCB EDA Suite
Loading...
Searching...
No Matches
eda_shape.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
6 * Copyright (C) 2011 Wayne Stambaugh <[email protected]>
7 * Copyright (C) 2023 CERN
8 * Copyright (C) 1992-2024 KiCad Developers, see AUTHORS.txt for contributors.
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, you may find one here:
22 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
23 * or you may search the http://www.gnu.org website for the version 2 license,
24 * or you may write to the Free Software Foundation, Inc.,
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
26 */
27
28#include <eda_shape.h>
29
30#include <bezier_curves.h>
32#include <eda_draw_frame.h>
33#include <geometry/shape_arc.h>
37#include <geometry/shape_rect.h>
38#include <macros.h>
39#include <math/util.h> // for KiROUND
40#include <eda_item.h>
41#include <plotters/plotter.h>
42#include <api/api_enums.h>
43#include <api/api_utils.h>
44#include <api/common/types/base_types.pb.h>
45
46
47EDA_SHAPE::EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill ) :
48 m_endsSwapped( false ),
49 m_shape( aType ),
50 m_stroke( aLineWidth, LINE_STYLE::DEFAULT, COLOR4D::UNSPECIFIED ),
51 m_fill( aFill ),
52 m_fillColor( COLOR4D::UNSPECIFIED ),
53 m_rectangleHeight( 0 ),
54 m_rectangleWidth( 0 ),
55 m_segmentLength( 0 ),
56 m_editState( 0 ),
57 m_proxyItem( false )
58{
59}
60
61
63{
64}
65
66
67EDA_SHAPE::EDA_SHAPE( const SHAPE& aShape ) :
68 m_endsSwapped( false ),
69 m_stroke( 0, LINE_STYLE::DEFAULT, COLOR4D::UNSPECIFIED ),
70 m_fill(),
71 m_rectangleHeight( 0 ),
72 m_rectangleWidth( 0 ),
73 m_segmentLength( 0 ),
74 m_editState( 0 ),
75 m_proxyItem( false )
76{
77 switch( aShape.Type() )
78 {
79 case SH_RECT:
80 {
81 auto rect = static_cast<const SHAPE_RECT&>( aShape );
82 m_shape = SHAPE_T::RECTANGLE;
83 SetStart( rect.GetPosition() );
84 SetEnd( rect.GetPosition() + rect.GetSize() );
85 break;
86 }
87
88 case SH_SEGMENT:
89 {
90 auto seg = static_cast<const SHAPE_SEGMENT&>( aShape );
91 m_shape = SHAPE_T::SEGMENT;
92 SetStart( seg.GetSeg().A );
93 SetEnd( seg.GetSeg().B );
94 SetWidth( seg.GetWidth() );
95 break;
96 }
97
98 case SH_LINE_CHAIN:
99 {
100 auto line = static_cast<const SHAPE_LINE_CHAIN&>( aShape );
101 m_shape = SHAPE_T::POLY;
103 m_poly.AddOutline( line );
104 SetWidth( line.Width() );
105 break;
106 }
107
108 case SH_CIRCLE:
109 {
110 auto circle = static_cast<const SHAPE_CIRCLE&>( aShape );
111 m_shape = SHAPE_T::CIRCLE;
112 SetStart( circle.GetCenter() );
113 SetEnd( circle.GetCenter() + circle.GetRadius() );
114 break;
115 }
116
117 case SH_ARC:
118 {
119 auto arc = static_cast<const SHAPE_ARC&>( aShape );
120 m_shape = SHAPE_T::ARC;
121 SetArcGeometry( arc.GetP0(), arc.GetArcMid(), arc.GetP1() );
122 SetWidth( arc.GetWidth() );
123 break;
124 }
125
126 case SH_SIMPLE:
127 {
128 auto poly = static_cast<const SHAPE_SIMPLE&>( aShape );
129 m_shape = SHAPE_T::POLY;
130 poly.TransformToPolygon( m_poly, 0, ERROR_INSIDE );
131 break;
132 }
133
134 // currently unhandled
135 case SH_POLY_SET:
136 case SH_COMPOUND:
137 case SH_NULL:
139 default:
140 m_shape = SHAPE_T::UNDEFINED;
141 break;
142 }
143}
144
145
146void EDA_SHAPE::Serialize( google::protobuf::Any &aContainer ) const
147{
148 using namespace kiapi::common;
149 types::GraphicShape shape;
150
151 types::StrokeAttributes* stroke = shape.mutable_attributes()->mutable_stroke();
152 types::GraphicFillAttributes* fill = shape.mutable_attributes()->mutable_fill();
153
154 stroke->mutable_width()->set_value_nm( GetWidth() );
155
156 switch( GetLineStyle() )
157 {
158 case LINE_STYLE::DEFAULT: stroke->set_style( types::SLS_DEFAULT ); break;
159 case LINE_STYLE::SOLID: stroke->set_style( types::SLS_SOLID ); break;
160 case LINE_STYLE::DASH: stroke->set_style( types::SLS_DASH ); break;
161 case LINE_STYLE::DOT: stroke->set_style( types::SLS_DOT ); break;
162 case LINE_STYLE::DASHDOT: stroke->set_style( types::SLS_DASHDOT ); break;
163 case LINE_STYLE::DASHDOTDOT: stroke->set_style( types::SLS_DASHDOTDOT ); break;
164 default: break;
165 }
166
167 switch( GetFillMode() )
168 {
169 case FILL_T::FILLED_SHAPE: fill->set_fill_type( types::GFT_FILLED ); break;
170 default: fill->set_fill_type( types::GFT_UNFILLED ); break;
171 }
172
173 switch( GetShape() )
174 {
175 case SHAPE_T::SEGMENT:
176 {
177 types::GraphicSegmentAttributes* segment = shape.mutable_segment();
178 PackVector2( *segment->mutable_start(), GetStart() );
179 PackVector2( *segment->mutable_end(), GetEnd() );
180 break;
181 }
182
183 case SHAPE_T::RECTANGLE:
184 {
185 types::GraphicRectangleAttributes* rectangle = shape.mutable_rectangle();
186 PackVector2( *rectangle->mutable_top_left(), GetStart() );
187 PackVector2( *rectangle->mutable_bottom_right(), GetEnd() );
188 break;
189 }
190
191 case SHAPE_T::ARC:
192 {
193 types::GraphicArcAttributes* arc = shape.mutable_arc();
194 PackVector2( *arc->mutable_start(), GetStart() );
195 PackVector2( *arc->mutable_mid(), GetArcMid() );
196 PackVector2( *arc->mutable_end(), GetEnd() );
197 break;
198 }
199
200 case SHAPE_T::CIRCLE:
201 {
202 types::GraphicCircleAttributes* circle = shape.mutable_circle();
203 PackVector2( *circle->mutable_center(), GetStart() );
204 PackVector2( *circle->mutable_radius_point(), GetEnd() );
205 break;
206 }
207
208 case SHAPE_T::POLY:
209 {
210 PackPolySet( *shape.mutable_polygon(), GetPolyShape() );
211 break;
212 }
213
214 case SHAPE_T::BEZIER:
215 {
216 types::GraphicBezierAttributes* bezier = shape.mutable_bezier();
217 PackVector2( *bezier->mutable_start(), GetStart() );
218 PackVector2( *bezier->mutable_control1(), GetBezierC1() );
219 PackVector2( *bezier->mutable_control2(), GetBezierC2() );
220 PackVector2( *bezier->mutable_end(), GetEnd() );
221 break;
222 }
223
224 default:
225 wxASSERT_MSG( false, "Unhandled shape in PCB_SHAPE::Serialize" );
226 }
227
228 // TODO m_hasSolderMask and m_solderMaskMargin
229
230 aContainer.PackFrom( shape );
231}
232
233
234bool EDA_SHAPE::Deserialize( const google::protobuf::Any &aContainer )
235{
236 using namespace kiapi::common;
237
238 types::GraphicShape shape;
239
240 if( !aContainer.UnpackTo( &shape ) )
241 return false;
242
243 // Initialize everything to a known state that doesn't get touched by every
244 // codepath below, to make sure the equality operator is consistent
245 m_start = {};
246 m_end = {};
247 m_arcCenter = {};
248 m_arcMidData = {};
249 m_bezierC1 = {};
250 m_bezierC2 = {};
251 m_editState = 0;
252 m_proxyItem = false;
253 m_endsSwapped = false;
254
255 SetFilled( shape.attributes().fill().fill_type() == types::GFT_FILLED );
256 SetWidth( shape.attributes().stroke().width().value_nm() );
257
258 switch( shape.attributes().stroke().style() )
259 {
260 case types::SLS_DEFAULT: SetLineStyle( LINE_STYLE::DEFAULT ); break;
261 case types::SLS_SOLID: SetLineStyle( LINE_STYLE::SOLID ); break;
262 case types::SLS_DASH: SetLineStyle( LINE_STYLE::DASH ); break;
263 case types::SLS_DOT: SetLineStyle( LINE_STYLE::DOT ); break;
264 case types::SLS_DASHDOT: SetLineStyle( LINE_STYLE::DASHDOT ); break;
265 case types::SLS_DASHDOTDOT: SetLineStyle( LINE_STYLE::DASHDOTDOT ); break;
266 default: break;
267 }
268
269 if( shape.has_segment() )
270 {
271 SetShape( SHAPE_T::SEGMENT );
272 SetStart( UnpackVector2( shape.segment().start() ) );
273 SetEnd( UnpackVector2( shape.segment().end() ) );
274 }
275 else if( shape.has_rectangle() )
276 {
277 SetShape( SHAPE_T::RECTANGLE );
278 SetStart( UnpackVector2( shape.rectangle().top_left() ) );
279 SetEnd( UnpackVector2( shape.rectangle().bottom_right() ) );
280 }
281 else if( shape.has_arc() )
282 {
283 SetShape( SHAPE_T::ARC );
284 SetArcGeometry( UnpackVector2( shape.arc().start() ),
285 UnpackVector2( shape.arc().mid() ),
286 UnpackVector2( shape.arc().end() ) );
287 }
288 else if( shape.has_circle() )
289 {
290 SetShape( SHAPE_T::CIRCLE );
291 SetStart( UnpackVector2( shape.circle().center() ) );
292 SetEnd( UnpackVector2( shape.circle().radius_point() ) );
293 }
294 else if( shape.has_polygon() )
295 {
296 SetShape( SHAPE_T::POLY );
297 SetPolyShape( UnpackPolySet( shape.polygon() ) );
298 }
299 else if( shape.has_bezier() )
300 {
301 SetShape( SHAPE_T::BEZIER );
302 SetStart( UnpackVector2( shape.bezier().start() ) );
303 SetBezierC1( UnpackVector2( shape.bezier().control1() ) );
304 SetBezierC2( UnpackVector2( shape.bezier().control2() ) );
305 SetEnd( UnpackVector2( shape.bezier().end() ) );
307 }
308
309 return true;
310}
311
312
313wxString EDA_SHAPE::ShowShape() const
314{
315 if( IsProxyItem() )
316 {
317 switch( m_shape )
318 {
319 case SHAPE_T::SEGMENT: return _( "Thermal Spoke" );
320 case SHAPE_T::RECTANGLE: return _( "Number Box" );
321 default: return wxT( "??" );
322 }
323 }
324 else
325 {
326 switch( m_shape )
327 {
328 case SHAPE_T::SEGMENT: return _( "Line" );
329 case SHAPE_T::RECTANGLE: return _( "Rect" );
330 case SHAPE_T::ARC: return _( "Arc" );
331 case SHAPE_T::CIRCLE: return _( "Circle" );
332 case SHAPE_T::BEZIER: return _( "Bezier Curve" );
333 case SHAPE_T::POLY: return _( "Polygon" );
334 default: return wxT( "??" );
335 }
336 }
337}
338
339
341{
342 switch( m_shape )
343 {
344 case SHAPE_T::SEGMENT: return wxS( "S_SEGMENT" );
345 case SHAPE_T::RECTANGLE: return wxS( "S_RECT" );
346 case SHAPE_T::ARC: return wxS( "S_ARC" );
347 case SHAPE_T::CIRCLE: return wxS( "S_CIRCLE" );
348 case SHAPE_T::POLY: return wxS( "S_POLYGON" );
349 case SHAPE_T::BEZIER: return wxS( "S_CURVE" );
350 case SHAPE_T::UNDEFINED: return wxS( "UNDEFINED" );
351 }
352
353 return wxEmptyString; // Just to quiet GCC.
354}
355
356
358{
359 move( aPos - getPosition() );
360}
361
362
364{
365 if( m_shape == SHAPE_T::ARC )
366 return getCenter();
367 else if( m_shape == SHAPE_T::POLY )
368 return m_poly.CVertex( 0 );
369 else
370 return m_start;
371}
372
373
375{
376 double length = 0.0;
377
378 switch( m_shape )
379 {
380 case SHAPE_T::BEZIER:
381 for( size_t ii = 1; ii < m_bezierPoints.size(); ++ii )
382 length += m_bezierPoints[ ii - 1].Distance( m_bezierPoints[ii] );
383
384 return length;
385
386 case SHAPE_T::SEGMENT:
387 return GetStart().Distance( GetEnd() );
388
389 case SHAPE_T::POLY:
390 for( int ii = 0; ii < m_poly.COutline( 0 ).SegmentCount(); ii++ )
391 length += m_poly.COutline( 0 ).CSegment( ii ).Length();
392
393 return length;
394
395 case SHAPE_T::ARC:
396 return GetRadius() * GetArcAngle().AsRadians();
397
398 default:
400 return 0.0;
401 }
402}
403
405{
406 switch( m_shape )
407 {
408 case SHAPE_T::RECTANGLE:
409 return GetEndY() - GetStartY();
410
411 default:
413 return 0;
414 }
415}
416
418{
419 switch( m_shape )
420 {
421 case SHAPE_T::RECTANGLE:
422 return GetEndX() - GetStartX();
423
424 default:
426 return 0;
427 }
428}
429
430void EDA_SHAPE::SetLength( const double& aLength )
431{
432 switch( m_shape )
433 {
434 case SHAPE_T::SEGMENT:
435 m_segmentLength = aLength; break;
436
437 default:
439 }
440}
441
442void EDA_SHAPE::SetRectangleHeight( const int& aHeight )
443{
444 switch ( m_shape )
445 {
446 case SHAPE_T::RECTANGLE:
447 m_rectangleHeight = aHeight;
449 break;
450
451 default:
453 }
454}
455
456void EDA_SHAPE::SetRectangleWidth( const int& aWidth )
457{
458 switch ( m_shape )
459 {
460 case SHAPE_T::RECTANGLE:
461 m_rectangleWidth = aWidth;
463 break;
464
465 default:
467 }
468}
469
470void EDA_SHAPE::SetRectangle( const long long int& aHeight, const long long int& aWidth )
471{
472 switch ( m_shape )
473 {
474 case SHAPE_T::RECTANGLE:
475 m_rectangleHeight = aHeight;
476 m_rectangleWidth = aWidth;
477 break;
478
479 default:
481 }
482}
483
484
486{
487 switch( m_shape )
488 {
489 case SHAPE_T::SEGMENT:
490 m_segmentAngle = aAngle;
491 break;
492
493 default:
495 }
496}
497
499{
500 switch( m_shape )
501 {
502 case SHAPE_T::CIRCLE:
503 case SHAPE_T::RECTANGLE:
504 return true;
505
506 case SHAPE_T::ARC:
507 case SHAPE_T::SEGMENT:
508 return false;
509
510 case SHAPE_T::POLY:
511 if( m_poly.IsEmpty() )
512 return false;
513 else
514 return m_poly.Outline( 0 ).IsClosed();
515
516 case SHAPE_T::BEZIER:
517 if( m_bezierPoints.size() < 3 )
518 return false;
519 else
520 return m_bezierPoints[0] == m_bezierPoints[ m_bezierPoints.size() - 1 ];
521
522 default:
524 return false;
525 }
526}
527
528
529
530void EDA_SHAPE::move( const VECTOR2I& aMoveVector )
531{
532 switch ( m_shape )
533 {
534 case SHAPE_T::ARC:
535 m_arcCenter += aMoveVector;
537
538 case SHAPE_T::SEGMENT:
539 case SHAPE_T::RECTANGLE:
540 case SHAPE_T::CIRCLE:
541 m_start += aMoveVector;
542 m_end += aMoveVector;
543 break;
544
545 case SHAPE_T::POLY:
546 m_poly.Move( aMoveVector );
547 break;
548
549 case SHAPE_T::BEZIER:
550 m_start += aMoveVector;
551 m_end += aMoveVector;
552 m_bezierC1 += aMoveVector;
553 m_bezierC2 += aMoveVector;
554
555 for( VECTOR2I& pt : m_bezierPoints )
556 pt += aMoveVector;
557
558 break;
559
560 default:
562 break;
563 }
564}
565
566
567void EDA_SHAPE::scale( double aScale )
568{
569 auto scalePt = [&]( VECTOR2I& pt )
570 {
571 pt.x = KiROUND( pt.x * aScale );
572 pt.y = KiROUND( pt.y * aScale );
573 };
574
575 switch( m_shape )
576 {
577 case SHAPE_T::ARC:
578 scalePt( m_arcCenter );
580
581 case SHAPE_T::SEGMENT:
582 case SHAPE_T::RECTANGLE:
583 scalePt( m_start );
584 scalePt( m_end );
585 break;
586
587 case SHAPE_T::CIRCLE: // ring or circle
588 scalePt( m_start );
589 m_end.x = m_start.x + KiROUND( GetRadius() * aScale );
590 m_end.y = m_start.y;
591 break;
592
593 case SHAPE_T::POLY: // polygon
594 {
595 std::vector<VECTOR2I> pts;
596
597 for( int ii = 0; ii < m_poly.OutlineCount(); ++ ii )
598 {
599 for( const VECTOR2I& pt : m_poly.Outline( ii ).CPoints() )
600 {
601 pts.emplace_back( pt );
602 scalePt( pts.back() );
603 }
604 }
605
606 SetPolyPoints( pts );
607 }
608 break;
609
610 case SHAPE_T::BEZIER:
611 scalePt( m_start );
612 scalePt( m_end );
613 scalePt( m_bezierC1 );
614 scalePt( m_bezierC2 );
616 break;
617
618 default:
620 break;
621 }
622}
623
624
625void EDA_SHAPE::rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
626{
627 switch( m_shape )
628 {
629 case SHAPE_T::SEGMENT:
630 case SHAPE_T::CIRCLE:
631 RotatePoint( m_start, aRotCentre, aAngle );
632 RotatePoint( m_end, aRotCentre, aAngle );
633 break;
634
635 case SHAPE_T::ARC:
636 RotatePoint( m_start, aRotCentre, aAngle );
637 RotatePoint( m_end, aRotCentre, aAngle );
638 RotatePoint( m_arcCenter, aRotCentre, aAngle );
639 break;
640
641 case SHAPE_T::RECTANGLE:
642 if( aAngle.IsCardinal() )
643 {
644 RotatePoint( m_start, aRotCentre, aAngle );
645 RotatePoint( m_end, aRotCentre, aAngle );
646 break;
647 }
648
649 // Convert non-cardinally-rotated rect to a diamond
650 m_shape = SHAPE_T::POLY;
657
659
660 case SHAPE_T::POLY:
661 m_poly.Rotate( aAngle, aRotCentre );
662 break;
663
664 case SHAPE_T::BEZIER:
665 RotatePoint( m_start, aRotCentre, aAngle );
666 RotatePoint( m_end, aRotCentre, aAngle );
667 RotatePoint( m_bezierC1, aRotCentre, aAngle );
668 RotatePoint( m_bezierC2, aRotCentre, aAngle );
669
670 for( VECTOR2I& pt : m_bezierPoints )
671 RotatePoint( pt, aRotCentre, aAngle);
672
673 break;
674
675 default:
677 break;
678 }
679}
680
681
682void EDA_SHAPE::flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
683{
684 switch ( m_shape )
685 {
686 case SHAPE_T::SEGMENT:
687 case SHAPE_T::RECTANGLE:
688 MIRROR( m_start, aCentre, aFlipDirection );
689 MIRROR( m_end, aCentre, aFlipDirection );
690 break;
691
692 case SHAPE_T::CIRCLE:
693 MIRROR( m_start, aCentre, aFlipDirection );
694 MIRROR( m_end, aCentre, aFlipDirection );
695 break;
696
697 case SHAPE_T::ARC:
698 MIRROR( m_start, aCentre, aFlipDirection );
699 MIRROR( m_end, aCentre, aFlipDirection );
700 MIRROR( m_arcCenter, aCentre, aFlipDirection );
701
702 std::swap( m_start, m_end );
703 break;
704
705 case SHAPE_T::POLY:
706 m_poly.Mirror( aCentre, aFlipDirection );
707 break;
708
709 case SHAPE_T::BEZIER:
710 MIRROR( m_start, aCentre, aFlipDirection );
711 MIRROR( m_end, aCentre, aFlipDirection );
712 MIRROR( m_bezierC1, aCentre, aFlipDirection );
713 MIRROR( m_bezierC2, aCentre, aFlipDirection );
714
716 break;
717
718 default:
720 break;
721 }
722}
723
724
726{
727 // Has meaning only for SHAPE_T::BEZIER
728 if( m_shape != SHAPE_T::BEZIER )
729 {
730 m_bezierPoints.clear();
731 return;
732 }
733
734 // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
736}
737
738
739const std::vector<VECTOR2I> EDA_SHAPE::buildBezierToSegmentsPointsList( int aMaxError ) const
740{
741 std::vector<VECTOR2I> bezierPoints;
742
743 // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
744 std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
745 BEZIER_POLY converter( ctrlPoints );
746 converter.GetPoly( bezierPoints, aMaxError );
747
748 return bezierPoints;
749}
750
751
753{
754 switch( m_shape )
755 {
756 case SHAPE_T::ARC:
757 return m_arcCenter;
758
759 case SHAPE_T::CIRCLE:
760 return m_start;
761
762 case SHAPE_T::SEGMENT:
763 // Midpoint of the line
764 return ( m_start + m_end ) / 2;
765
766 case SHAPE_T::POLY:
767 case SHAPE_T::RECTANGLE:
768 case SHAPE_T::BEZIER:
769 return getBoundingBox().Centre();
770
771 default:
773 return VECTOR2I();
774 }
775}
776
777
778void EDA_SHAPE::SetCenter( const VECTOR2I& aCenter )
779{
780 switch( m_shape )
781 {
782 case SHAPE_T::ARC:
783 m_arcCenter = aCenter;
784 break;
785
786 case SHAPE_T::CIRCLE:
787 m_start = aCenter;
788 break;
789
790 default:
792 }
793}
794
795
797{
798 // If none of the input data have changed since we loaded the arc,
799 // keep the original mid point data to minimize churn
802 return m_arcMidData.mid;
803
804 VECTOR2I mid = m_start;
805 RotatePoint( mid, m_arcCenter, -GetArcAngle() / 2.0 );
806 return mid;
807}
808
809
810void EDA_SHAPE::CalcArcAngles( EDA_ANGLE& aStartAngle, EDA_ANGLE& aEndAngle ) const
811{
812 VECTOR2D startRadial( GetStart() - getCenter() );
813 VECTOR2D endRadial( GetEnd() - getCenter() );
814
815 aStartAngle = EDA_ANGLE( startRadial );
816 aEndAngle = EDA_ANGLE( endRadial );
817
818 if( aEndAngle == aStartAngle )
819 aEndAngle = aStartAngle + ANGLE_360; // ring, not null
820
821 while( aEndAngle < aStartAngle )
822 aEndAngle += ANGLE_360;
823}
824
825
827{
828 double radius = 0.0;
829
830 switch( m_shape )
831 {
832 case SHAPE_T::ARC:
833 radius = m_arcCenter.Distance( m_start );
834 break;
835
836 case SHAPE_T::CIRCLE:
837 radius = m_start.Distance( m_end );
838 break;
839
840 default:
842 }
843
844 // don't allow degenerate circles/arcs
845 return std::max( 1, KiROUND( radius ) );
846}
847
848
849void EDA_SHAPE::SetCachedArcData( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCenter )
850{
851 m_arcMidData.start = aStart;
852 m_arcMidData.end = aEnd;
853 m_arcMidData.center = aCenter;
854 m_arcMidData.mid = aMid;
855}
856
857
858void EDA_SHAPE::SetArcGeometry( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
859{
860 m_arcMidData = {};
861 m_start = aStart;
862 m_end = aEnd;
863 m_arcCenter = CalcArcCenter( aStart, aMid, aEnd );
864 VECTOR2I new_mid = GetArcMid();
865
866 m_endsSwapped = false;
867
868 // Watch the ordering here. GetArcMid above needs to be called prior to initializing the
869 // m_arcMidData structure in order to ensure we get the calculated variant, not the cached
870 SetCachedArcData( aStart, aMid, aEnd, m_arcCenter );
871
872 /*
873 * If the input winding doesn't match our internal winding, the calculated midpoint will end
874 * up on the other side of the arc. In this case, we need to flip the start/end points and
875 * flag this change for the system.
876 */
877 VECTOR2D dist( new_mid - aMid );
878 VECTOR2D dist2( new_mid - m_arcCenter );
879
880 if( dist.SquaredEuclideanNorm() > dist2.SquaredEuclideanNorm() )
881 {
882 std::swap( m_start, m_end );
883 m_endsSwapped = true;
884 }
885}
886
888{
889 EDA_ANGLE angle( atan2( static_cast<double>( GetStart().y - GetEnd().y ),
890 static_cast<double>( GetEnd().x - GetStart().x ) ), RADIANS_T );
891
892 return angle;
893}
894
895
897{
898 EDA_ANGLE startAngle;
899 EDA_ANGLE endAngle;
900
901 CalcArcAngles( startAngle, endAngle );
902
903 return endAngle - startAngle;
904}
905
906
908{
909 if( m_shape == SHAPE_T::ARC )
910 {
911 VECTOR2D mid = GetArcMid();
912
913 double orient = ( mid.x - m_start.x ) * ( m_end.y - m_start.y )
914 - ( mid.y - m_start.y ) * ( m_end.x - m_start.x );
915
916 return orient < 0;
917 }
918
920 return false;
921}
922
923
924void EDA_SHAPE::SetArcAngleAndEnd( const EDA_ANGLE& aAngle, bool aCheckNegativeAngle )
925{
926 EDA_ANGLE angle( aAngle );
927
928 m_end = m_start;
930
931 if( aCheckNegativeAngle && aAngle < ANGLE_0 )
932 {
933 std::swap( m_start, m_end );
934 m_endsSwapped = true;
935 }
936}
937
938
940{
941 if( IsProxyItem() )
942 {
943 switch( m_shape )
944 {
945 case SHAPE_T::RECTANGLE: return _( "Pad Number Box" );
946 case SHAPE_T::SEGMENT: return _( "Thermal Spoke Template" );
947 default: return _( "Unrecognized" );
948 }
949 }
950 else
951 {
952 switch( m_shape )
953 {
954 case SHAPE_T::CIRCLE: return _( "Circle" );
955 case SHAPE_T::ARC: return _( "Arc" );
956 case SHAPE_T::BEZIER: return _( "Curve" );
957 case SHAPE_T::POLY: return _( "Polygon" );
958 case SHAPE_T::RECTANGLE: return _( "Rectangle" );
959 case SHAPE_T::SEGMENT: return _( "Segment" );
960 default: return _( "Unrecognized" );
961 }
962 }
963}
964
965
966void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
967{
968 wxString msg;
969
970 wxString shape = _( "Shape" );
971 aList.emplace_back( shape, getFriendlyName() );
972
973 switch( m_shape )
974 {
975 case SHAPE_T::CIRCLE:
976 aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
977 break;
978
979 case SHAPE_T::ARC:
981 aList.emplace_back( _( "Angle" ), msg );
982
983 aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
984 break;
985
986 case SHAPE_T::BEZIER:
987 aList.emplace_back( _( "Length" ), aFrame->MessageTextFromValue( GetLength() ) );
988 break;
989
990 case SHAPE_T::POLY:
991 msg.Printf( wxS( "%d" ), GetPolyShape().Outline(0).PointCount() );
992 aList.emplace_back( _( "Points" ), msg );
993 break;
994
995 case SHAPE_T::RECTANGLE:
996 aList.emplace_back( _( "Width" ),
997 aFrame->MessageTextFromValue( std::abs( GetEnd().x - GetStart().x ) ) );
998
999 aList.emplace_back( _( "Height" ),
1000 aFrame->MessageTextFromValue( std::abs( GetEnd().y - GetStart().y ) ) );
1001 break;
1002
1003 case SHAPE_T::SEGMENT:
1004 {
1005 aList.emplace_back( _( "Length" ),
1006 aFrame->MessageTextFromValue( GetStart().Distance( GetEnd() ) ));
1007
1008 // angle counter-clockwise from 3'o-clock
1009 EDA_ANGLE angle( atan2( (double)( GetStart().y - GetEnd().y ),
1010 (double)( GetEnd().x - GetStart().x ) ), RADIANS_T );
1011 aList.emplace_back( _( "Angle" ), EDA_UNIT_UTILS::UI::MessageTextFromValue( angle ) );
1012 break;
1013 }
1014
1015 default:
1016 break;
1017 }
1018
1019 m_stroke.GetMsgPanelInfo( aFrame, aList );
1020}
1021
1022
1024{
1025 BOX2I bbox;
1026
1027 switch( m_shape )
1028 {
1029 case SHAPE_T::RECTANGLE:
1030 for( VECTOR2I& pt : GetRectCorners() )
1031 bbox.Merge( pt );
1032
1033 break;
1034
1035 case SHAPE_T::SEGMENT:
1036 bbox.SetOrigin( GetStart() );
1037 bbox.SetEnd( GetEnd() );
1038 break;
1039
1040 case SHAPE_T::CIRCLE:
1041 bbox.SetOrigin( GetStart() );
1042 bbox.Inflate( GetRadius() );
1043 break;
1044
1045 case SHAPE_T::ARC:
1046 computeArcBBox( bbox );
1047 break;
1048
1049 case SHAPE_T::POLY:
1050 if( m_poly.IsEmpty() )
1051 break;
1052
1053 for( auto iter = m_poly.CIterate(); iter; iter++ )
1054 bbox.Merge( *iter );
1055
1056 break;
1057
1058 case SHAPE_T::BEZIER:
1059 // Bezier BBoxes are not trivial to compute, so we approximate it by
1060 // using the bounding box of the curve (not control!) points.
1061 for( const VECTOR2I& pt : m_bezierPoints )
1062 bbox.Merge( pt );
1063 break;
1064
1065 default:
1067 break;
1068 }
1069
1070 bbox.Inflate( std::max( 0, GetWidth() ) / 2 );
1071 bbox.Normalize();
1072
1073 return bbox;
1074}
1075
1076
1077bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
1078{
1079 double maxdist = aAccuracy;
1080
1081 if( GetWidth() > 0 )
1082 maxdist += GetWidth() / 2.0;
1083
1084 switch( m_shape )
1085 {
1086 case SHAPE_T::CIRCLE:
1087 {
1088 double radius = GetRadius();
1089 double dist = aPosition.Distance( getCenter() );
1090
1091 if( IsFilledForHitTesting() )
1092 return dist <= radius + maxdist; // Filled circle hit-test
1093 else
1094 return abs( radius - dist ) <= maxdist; // Ring hit-test
1095 }
1096
1097 case SHAPE_T::ARC:
1098 {
1099 if( aPosition.Distance( m_start ) <= maxdist )
1100 return true;
1101
1102 if( aPosition.Distance( m_end ) <= maxdist )
1103 return true;
1104
1105 double radius = GetRadius();
1106 VECTOR2D relPos( VECTOR2D( aPosition ) - getCenter() );
1107 double dist = relPos.EuclideanNorm();
1108
1109 if( IsFilledForHitTesting() )
1110 {
1111 // Check distance from arc center
1112 if( dist > radius + maxdist )
1113 return false;
1114 }
1115 else
1116 {
1117 // Check distance from arc circumference
1118 if( abs( radius - dist ) > maxdist )
1119 return false;
1120 }
1121
1122 // Finally, check to see if it's within arc's swept angle.
1123 EDA_ANGLE startAngle;
1124 EDA_ANGLE endAngle;
1125 CalcArcAngles( startAngle, endAngle );
1126
1127 EDA_ANGLE relPosAngle( relPos );
1128
1129 startAngle.Normalize();
1130 endAngle.Normalize();
1131 relPosAngle.Normalize();
1132
1133 if( endAngle > startAngle )
1134 return relPosAngle >= startAngle && relPosAngle <= endAngle;
1135 else
1136 return relPosAngle >= startAngle || relPosAngle <= endAngle;
1137 }
1138
1139 case SHAPE_T::BEZIER:
1140 {
1141 const std::vector<VECTOR2I>* pts = &m_bezierPoints;
1142 std::vector<VECTOR2I> updatedBezierPoints;
1143
1144 if( m_bezierPoints.empty() )
1145 {
1147 converter.GetPoly( updatedBezierPoints, aAccuracy / 2 );
1148 pts = &updatedBezierPoints;
1149 }
1150
1151 for( unsigned int i = 1; i < pts->size(); i++ )
1152 {
1153 if( TestSegmentHit( aPosition, ( *pts )[i - 1], ( *pts )[i], maxdist ) )
1154 return true;
1155 }
1156
1157 return false;
1158 }
1159 case SHAPE_T::SEGMENT:
1160 return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
1161
1162 case SHAPE_T::RECTANGLE:
1163 if( IsProxyItem() || IsFilledForHitTesting() ) // Filled rect hit-test
1164 {
1165 SHAPE_POLY_SET poly;
1166 poly.NewOutline();
1167
1168 for( const VECTOR2I& pt : GetRectCorners() )
1169 poly.Append( pt );
1170
1171 return poly.Collide( aPosition, maxdist );
1172 }
1173 else // Open rect hit-test
1174 {
1175 std::vector<VECTOR2I> pts = GetRectCorners();
1176
1177 return TestSegmentHit( aPosition, pts[0], pts[1], maxdist )
1178 || TestSegmentHit( aPosition, pts[1], pts[2], maxdist )
1179 || TestSegmentHit( aPosition, pts[2], pts[3], maxdist )
1180 || TestSegmentHit( aPosition, pts[3], pts[0], maxdist );
1181 }
1182
1183 case SHAPE_T::POLY:
1184 if( IsFilledForHitTesting() )
1185 {
1186 if( !m_poly.COutline( 0 ).IsClosed() )
1187 {
1188 // Only one outline is expected
1190 copy.SetClosed( true );
1191 return copy.Collide( aPosition, maxdist );
1192 }
1193 else
1194 {
1195 return m_poly.Collide( aPosition, maxdist );
1196 }
1197 }
1198 else
1199 {
1200 return m_poly.CollideEdge( aPosition, nullptr, maxdist );
1201 }
1202
1203 default:
1205 return false;
1206 }
1207}
1208
1209
1210bool EDA_SHAPE::hitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
1211{
1212 BOX2I arect = aRect;
1213 arect.Normalize();
1214 arect.Inflate( aAccuracy );
1215
1216 BOX2I bbox = getBoundingBox();
1217
1218 switch( m_shape )
1219 {
1220 case SHAPE_T::CIRCLE:
1221 // Test if area intersects or contains the circle:
1222 if( aContained )
1223 {
1224 return arect.Contains( bbox );
1225 }
1226 else
1227 {
1228 // If the rectangle does not intersect the bounding box, this is a much quicker test
1229 if( !arect.Intersects( bbox ) )
1230 return false;
1231 else
1232 return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
1233 }
1234
1235 case SHAPE_T::ARC:
1236 // Test for full containment of this arc in the rect
1237 if( aContained )
1238 {
1239 return arect.Contains( bbox );
1240 }
1241 // Test if the rect crosses the arc
1242 else
1243 {
1244 if( !arect.Intersects( bbox ) )
1245 return false;
1246
1247 if( IsFilled() )
1248 {
1249 return ( arect.Intersects( getCenter(), GetStart() )
1250 || arect.Intersects( getCenter(), GetEnd() )
1251 || arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() ) );
1252 }
1253 else
1254 {
1255 return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
1256 }
1257 }
1258
1259 case SHAPE_T::RECTANGLE:
1260 if( aContained )
1261 {
1262 return arect.Contains( bbox );
1263 }
1264 else
1265 {
1266 std::vector<VECTOR2I> pts = GetRectCorners();
1267
1268 // Account for the width of the lines
1269 arect.Inflate( GetWidth() / 2 );
1270 return ( arect.Intersects( pts[0], pts[1] )
1271 || arect.Intersects( pts[1], pts[2] )
1272 || arect.Intersects( pts[2], pts[3] )
1273 || arect.Intersects( pts[3], pts[0] ) );
1274 }
1275
1276 case SHAPE_T::SEGMENT:
1277 if( aContained )
1278 {
1279 return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
1280 }
1281 else
1282 {
1283 // Account for the width of the line
1284 arect.Inflate( GetWidth() / 2 );
1285 return arect.Intersects( GetStart(), GetEnd() );
1286 }
1287
1288 case SHAPE_T::POLY:
1289 if( aContained )
1290 {
1291 return arect.Contains( bbox );
1292 }
1293 else
1294 {
1295 // Fast test: if aRect is outside the polygon bounding box,
1296 // rectangles cannot intersect
1297 if( !arect.Intersects( bbox ) )
1298 return false;
1299
1300 // Account for the width of the line
1301 arect.Inflate( GetWidth() / 2 );
1302
1303 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1304 {
1305 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
1306 int count = poly.GetPointCount();
1307
1308 for( int jj = 0; jj < count; jj++ )
1309 {
1310 VECTOR2I vertex = poly.GetPoint( jj );
1311
1312 // Test if the point is within aRect
1313 if( arect.Contains( vertex ) )
1314 return true;
1315
1316 if( jj + 1 < count )
1317 {
1318 VECTOR2I vertexNext = poly.GetPoint( jj + 1 );
1319
1320 // Test if this edge intersects aRect
1321 if( arect.Intersects( vertex, vertexNext ) )
1322 return true;
1323 }
1324 else if( poly.IsClosed() )
1325 {
1326 VECTOR2I vertexNext = poly.GetPoint( 0 );
1327
1328 // Test if this edge intersects aRect
1329 if( arect.Intersects( vertex, vertexNext ) )
1330 return true;
1331 }
1332 }
1333 }
1334
1335 return false;
1336 }
1337
1338 case SHAPE_T::BEZIER:
1339 if( aContained )
1340 {
1341 return arect.Contains( bbox );
1342 }
1343 else
1344 {
1345 // Fast test: if aRect is outside the polygon bounding box,
1346 // rectangles cannot intersect
1347 if( !arect.Intersects( bbox ) )
1348 return false;
1349
1350 // Account for the width of the line
1351 arect.Inflate( GetWidth() / 2 );
1352 const std::vector<VECTOR2I>* pts = &m_bezierPoints;
1353 std::vector<VECTOR2I> updatedBezierPoints;
1354
1355 if( m_bezierPoints.empty() )
1356 {
1358 converter.GetPoly( updatedBezierPoints, aAccuracy / 2 );
1359 pts = &updatedBezierPoints;
1360 }
1361
1362 for( unsigned ii = 1; ii < pts->size(); ii++ )
1363 {
1364 VECTOR2I vertex = ( *pts )[ii - 1];
1365 VECTOR2I vertexNext = ( *pts )[ii];
1366
1367 // Test if the point is within aRect
1368 if( arect.Contains( vertex ) )
1369 return true;
1370
1371 // Test if this edge intersects aRect
1372 if( arect.Intersects( vertex, vertexNext ) )
1373 return true;
1374 }
1375
1376 return false;
1377 }
1378
1379 default:
1381 return false;
1382 }
1383}
1384
1385
1386std::vector<VECTOR2I> EDA_SHAPE::GetRectCorners() const
1387{
1388 std::vector<VECTOR2I> pts;
1389 VECTOR2I topLeft = GetStart();
1390 VECTOR2I botRight = GetEnd();
1391
1392 pts.emplace_back( topLeft );
1393 pts.emplace_back( botRight.x, topLeft.y );
1394 pts.emplace_back( botRight );
1395 pts.emplace_back( topLeft.x, botRight.y );
1396
1397 return pts;
1398}
1399
1400
1402{
1403 // Start, end, and each inflection point the arc crosses will enclose the entire arc.
1404 // Only include the center when filled; it's not necessarily inside the BB of an unfilled
1405 // arc with a small included angle.
1406 aBBox.SetOrigin( m_start );
1407 aBBox.Merge( m_end );
1408
1409 if( IsFilled() )
1410 aBBox.Merge( m_arcCenter );
1411
1412 int radius = GetRadius();
1413 EDA_ANGLE t1, t2;
1414
1415 CalcArcAngles( t1, t2 );
1416
1417 t1.Normalize();
1418 t2.Normalize();
1419
1420 if( t2 > t1 )
1421 {
1422 if( t1 < ANGLE_0 && t2 > ANGLE_0 )
1423 aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
1424
1425 if( t1 < ANGLE_90 && t2 > ANGLE_90 )
1426 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
1427
1428 if( t1 < ANGLE_180 && t2 > ANGLE_180 )
1429 aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
1430
1431 if( t1 < ANGLE_270 && t2 > ANGLE_270 )
1432 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
1433 }
1434 else
1435 {
1436 if( t1 < ANGLE_0 || t2 > ANGLE_0 )
1437 aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
1438
1439 if( t1 < ANGLE_90 || t2 > ANGLE_90 )
1440 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
1441
1442 if( t1 < ANGLE_180 || t2 > ANGLE_180 )
1443 aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
1444
1445 if( t1 < ANGLE_270 || t2 > ANGLE_270 )
1446 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
1447 }
1448}
1449
1450
1451void EDA_SHAPE::SetPolyPoints( const std::vector<VECTOR2I>& aPoints )
1452{
1455
1456 for( const VECTOR2I& p : aPoints )
1457 m_poly.Append( p.x, p.y );
1458}
1459
1460
1461std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineChainOnly ) const
1462{
1463 std::vector<SHAPE*> effectiveShapes;
1464 int width = GetEffectiveWidth();
1465
1466 switch( m_shape )
1467 {
1468 case SHAPE_T::ARC:
1469 effectiveShapes.emplace_back( new SHAPE_ARC( m_arcCenter, m_start, GetArcAngle(), width ) );
1470 break;
1471
1472 case SHAPE_T::SEGMENT:
1473 effectiveShapes.emplace_back( new SHAPE_SEGMENT( m_start, m_end, width ) );
1474 break;
1475
1476 case SHAPE_T::RECTANGLE:
1477 {
1478 std::vector<VECTOR2I> pts = GetRectCorners();
1479
1480 if( ( IsFilled() || IsProxyItem() ) && !aEdgeOnly )
1481 effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
1482
1483 if( width > 0 || !IsFilled() || aEdgeOnly )
1484 {
1485 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], width ) );
1486 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], width ) );
1487 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], width ) );
1488 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], width ) );
1489 }
1490 }
1491 break;
1492
1493 case SHAPE_T::CIRCLE:
1494 {
1495 if( IsFilled() && !aEdgeOnly )
1496 effectiveShapes.emplace_back( new SHAPE_CIRCLE( getCenter(), GetRadius() ) );
1497
1498 if( width > 0 || !IsFilled() || aEdgeOnly )
1499 effectiveShapes.emplace_back( new SHAPE_ARC( getCenter(), GetEnd(), ANGLE_360, width ) );
1500
1501 break;
1502 }
1503
1504 case SHAPE_T::BEZIER:
1505 {
1506 std::vector<VECTOR2I> bezierPoints = buildBezierToSegmentsPointsList( width / 2 );
1507 VECTOR2I start_pt = bezierPoints[0];
1508
1509 for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ )
1510 {
1511 VECTOR2I end_pt = bezierPoints[jj];
1512 effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, width ) );
1513 start_pt = end_pt;
1514 }
1515
1516 break;
1517 }
1518
1519 case SHAPE_T::POLY:
1520 {
1521 if( GetPolyShape().OutlineCount() == 0 ) // malformed/empty polygon
1522 break;
1523
1524 for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
1525 {
1526 const SHAPE_LINE_CHAIN& l = GetPolyShape().COutline( ii );
1527
1528 if( IsFilled() && !aEdgeOnly )
1529 effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) );
1530
1531 if( width > 0 || !IsFilled() || aEdgeOnly )
1532 {
1533 int segCount = l.SegmentCount();
1534
1535 if( aLineChainOnly && l.IsClosed() )
1536 segCount--; // Treat closed chain as open
1537
1538 for( int jj = 0; jj < segCount; jj++ )
1539 effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.CSegment( jj ), width ) );
1540 }
1541 }
1542 }
1543 break;
1544
1545 default:
1547 break;
1548 }
1549
1550 return effectiveShapes;
1551}
1552
1553
1554void EDA_SHAPE::DupPolyPointsList( std::vector<VECTOR2I>& aBuffer ) const
1555{
1556 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1557 {
1558 int pointCount = m_poly.COutline( ii ).PointCount();
1559
1560 if( pointCount )
1561 {
1562 aBuffer.reserve( pointCount );
1563
1564 for ( auto iter = m_poly.CIterate(); iter; iter++ )
1565 aBuffer.emplace_back( iter->x, iter->y );
1566 }
1567 }
1568}
1569
1570
1572{
1573 // return true if the polygonal shape is valid (has more than 2 points)
1574 return GetPolyShape().OutlineCount() > 0 && GetPolyShape().Outline( 0 ).PointCount() > 2;
1575}
1576
1577
1579{
1580 // return the number of corners of the polygonal shape
1581 // this shape is expected to be only one polygon without hole
1582 return GetPolyShape().OutlineCount() ? GetPolyShape().VertexCount( 0 ) : 0;
1583}
1584
1585
1586void EDA_SHAPE::beginEdit( const VECTOR2I& aPosition )
1587{
1588 switch( GetShape() )
1589 {
1590 case SHAPE_T::SEGMENT:
1591 case SHAPE_T::CIRCLE:
1592 case SHAPE_T::RECTANGLE:
1593 SetStart( aPosition );
1594 SetEnd( aPosition );
1595 break;
1596
1597 case SHAPE_T::ARC:
1598 SetArcGeometry( aPosition, aPosition, aPosition );
1599 m_editState = 1;
1600 break;
1601
1602 case SHAPE_T::BEZIER:
1603 SetStart( aPosition );
1604 SetEnd( aPosition );
1605 SetBezierC1( aPosition );
1606 SetBezierC2( aPosition );
1607 m_editState = 1;
1608
1610 break;
1611
1612 case SHAPE_T::POLY:
1614 m_poly.Outline( 0 ).SetClosed( false );
1615
1616 // Start and end of the first segment (co-located for now)
1617 m_poly.Outline( 0 ).Append( aPosition );
1618 m_poly.Outline( 0 ).Append( aPosition, true );
1619 break;
1620
1621 default:
1623 }
1624}
1625
1626
1627bool EDA_SHAPE::continueEdit( const VECTOR2I& aPosition )
1628{
1629 switch( GetShape() )
1630 {
1631 case SHAPE_T::ARC:
1632 case SHAPE_T::SEGMENT:
1633 case SHAPE_T::CIRCLE:
1634 case SHAPE_T::RECTANGLE:
1635 return false;
1636
1637 case SHAPE_T::BEZIER:
1638 if( m_editState == 3 )
1639 return false;
1640
1641 m_editState++;
1642 return true;
1643
1644 case SHAPE_T::POLY:
1645 {
1646 SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1647
1648 // do not add zero-length segments
1649 if( poly.CPoint( poly.GetPointCount() - 2 ) != poly.CLastPoint() )
1650 poly.Append( aPosition, true );
1651 }
1652 return true;
1653
1654 default:
1656 return false;
1657 }
1658}
1659
1660
1661void EDA_SHAPE::calcEdit( const VECTOR2I& aPosition )
1662{
1663#define sq( x ) pow( x, 2 )
1664
1665 switch( GetShape() )
1666 {
1667 case SHAPE_T::SEGMENT:
1668 case SHAPE_T::CIRCLE:
1669 case SHAPE_T::RECTANGLE:
1670 SetEnd( aPosition );
1671 break;
1672
1673 case SHAPE_T::BEZIER:
1674 {
1675 switch( m_editState )
1676 {
1677 case 0:
1678 SetStart( aPosition );
1679 SetEnd( aPosition );
1680 SetBezierC1( aPosition );
1681 SetBezierC2( aPosition );
1682 break;
1683 case 1:
1684 SetBezierC2( aPosition );
1685 SetEnd( aPosition );
1686 break;
1687 case 2: SetBezierC1( aPosition ); break;
1688 case 3: SetBezierC2( aPosition ); break;
1689 }
1690
1692 }
1693 break;
1694
1695 case SHAPE_T::ARC:
1696 {
1697 double radius = GetRadius();
1698 EDA_ANGLE lastAngle = GetArcAngle();
1699
1700 // Edit state 0: drawing: place start
1701 // Edit state 1: drawing: place end (center calculated for 90-degree subtended angle)
1702 // Edit state 2: point edit: move start (center calculated for invariant subtended angle)
1703 // Edit state 3: point edit: move end (center calculated for invariant subtended angle)
1704 // Edit state 4: point edit: move center
1705 // Edit state 5: point edit: move arc-mid-point
1706
1707 switch( m_editState )
1708 {
1709 case 0:
1710 SetArcGeometry( aPosition, aPosition, aPosition );
1711 return;
1712
1713 case 1:
1714 m_end = aPosition;
1715 radius = m_start.Distance( m_end ) * M_SQRT1_2;
1716 break;
1717
1718 case 2:
1719 case 3:
1720 {
1721 VECTOR2I v = m_start - m_end;
1722 double chordBefore = v.SquaredEuclideanNorm();
1723
1724 if( m_editState == 2 )
1725 m_start = aPosition;
1726 else
1727 m_end = aPosition;
1728
1729 v = m_start - m_end;
1730
1731 double chordAfter = v.SquaredEuclideanNorm();
1732 double ratio = 0.0;
1733
1734 if( chordBefore > 0 )
1735 ratio = chordAfter / chordBefore;
1736
1737 if( ratio != 0 )
1738 radius = std::max( sqrt( sq( radius ) * ratio ), sqrt( chordAfter ) / 2 );
1739 }
1740 break;
1741
1742 case 4:
1743 {
1744 double radialA = m_start.Distance( aPosition );
1745 double radialB = m_end.Distance( aPosition );
1746 radius = ( radialA + radialB ) / 2.0;
1747 }
1748 break;
1749
1750 case 5:
1751 SetArcGeometry( GetStart(), aPosition, GetEnd() );
1752 return;
1753 }
1754
1755 // Calculate center based on start, end, and radius
1756 //
1757 // Let 'l' be the length of the chord and 'm' the middle point of the chord
1758 double l = m_start.Distance( m_end );
1759 VECTOR2D m = ( m_start + m_end ) / 2;
1760 double sqRadDiff = ( radius * radius ) - ( l * l ) / 4.0;
1761
1762 // Calculate 'd', the vector from the chord midpoint to the center
1763 VECTOR2D d;
1764
1765 if( l > 0 && sqRadDiff >= 0 )
1766 {
1767 d.x = sqrt( sqRadDiff ) * ( m_start.y - m_end.y ) / l;
1768 d.y = sqrt( sqRadDiff ) * ( m_end.x - m_start.x ) / l;
1769 }
1770
1771 VECTOR2I c1 = KiROUND( m + d );
1772 VECTOR2I c2 = KiROUND( m - d );
1773
1774 // Solution gives us 2 centers; we need to pick one:
1775 switch( m_editState )
1776 {
1777 case 1:
1778 // Keep arc clockwise while drawing i.e. arc angle = 90 deg.
1779 // it can be 90 or 270 deg depending on the arc center choice (c1 or c2)
1780 m_arcCenter = c1; // first trial
1781
1782 if( GetArcAngle() > ANGLE_180 )
1783 m_arcCenter = c2;
1784
1785 break;
1786
1787 case 2:
1788 case 3:
1789 // Pick the one of c1, c2 to keep arc on the same side
1790 m_arcCenter = c1; // first trial
1791
1792 if( ( lastAngle < ANGLE_180 ) != ( GetArcAngle() < ANGLE_180 ) )
1793 m_arcCenter = c2;
1794
1795 break;
1796
1797 case 4:
1798 // Pick the one closer to the mouse position
1799 m_arcCenter = c1.Distance( aPosition ) < c2.Distance( aPosition ) ? c1 : c2;
1800 break;
1801 }
1802 }
1803 break;
1804
1805 case SHAPE_T::POLY:
1806 m_poly.Outline( 0 ).SetPoint( m_poly.Outline( 0 ).GetPointCount() - 1, aPosition );
1807 break;
1808
1809 default:
1811 }
1812}
1813
1814
1815void EDA_SHAPE::endEdit( bool aClosed )
1816{
1817 switch( GetShape() )
1818 {
1819 case SHAPE_T::ARC:
1820 case SHAPE_T::SEGMENT:
1821 case SHAPE_T::CIRCLE:
1822 case SHAPE_T::RECTANGLE:
1823 case SHAPE_T::BEZIER:
1824 break;
1825
1826 case SHAPE_T::POLY:
1827 {
1828 SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1829
1830 // do not include last point twice
1831 if( poly.GetPointCount() > 2 )
1832 {
1833 if( poly.CPoint( poly.GetPointCount() - 2 ) == poly.CLastPoint() )
1834 {
1835 poly.SetClosed( aClosed );
1836 }
1837 else
1838 {
1839 poly.SetClosed( false );
1840 poly.Remove( poly.GetPointCount() - 1 );
1841 }
1842 }
1843 }
1844 break;
1845
1846 default:
1848 }
1849}
1850
1851
1853{
1854 EDA_SHAPE* image = dynamic_cast<EDA_SHAPE*>( aImage );
1855 assert( image );
1856
1857 #define SWAPITEM( x ) std::swap( x, image->x )
1858 SWAPITEM( m_stroke );
1859 SWAPITEM( m_start );
1860 SWAPITEM( m_end );
1862 SWAPITEM( m_shape );
1866 SWAPITEM( m_poly );
1867 SWAPITEM( m_fill );
1871 #undef SWAPITEM
1872}
1873
1874
1875int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const
1876{
1877#define EPSILON 2 // Should be enough for rounding errors on calculated items
1878
1879#define TEST( a, b ) { if( a != b ) return a - b; }
1880#define TEST_E( a, b ) { if( abs( a - b ) > EPSILON ) return a - b; }
1881#define TEST_PT( a, b ) { TEST_E( a.x, b.x ); TEST_E( a.y, b.y ); }
1882
1883 TEST_PT( m_start, aOther->m_start );
1884 TEST_PT( m_end, aOther->m_end );
1885
1886 TEST( (int) m_shape, (int) aOther->m_shape );
1887
1888 if( m_shape == SHAPE_T::ARC )
1889 {
1890 TEST_PT( m_arcCenter, aOther->m_arcCenter );
1891 }
1892 else if( m_shape == SHAPE_T::BEZIER )
1893 {
1894 TEST_PT( m_bezierC1, aOther->m_bezierC1 );
1895 TEST_PT( m_bezierC2, aOther->m_bezierC2 );
1896 }
1897 else if( m_shape == SHAPE_T::POLY )
1898 {
1900 }
1901
1902 for( size_t ii = 0; ii < m_bezierPoints.size(); ++ii )
1903 TEST_PT( m_bezierPoints[ii], aOther->m_bezierPoints[ii] );
1904
1905 for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
1906 TEST_PT( m_poly.CVertex( ii ), aOther->m_poly.CVertex( ii ) );
1907
1908 TEST_E( m_stroke.GetWidth(), aOther->m_stroke.GetWidth() );
1909 TEST( (int) m_stroke.GetLineStyle(), (int) aOther->m_stroke.GetLineStyle() );
1910 TEST( (int) m_fill, (int) aOther->m_fill );
1911
1912 return 0;
1913}
1914
1915
1916void EDA_SHAPE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, int aClearance, int aError,
1917 ERROR_LOC aErrorLoc, bool ignoreLineWidth ) const
1918{
1919 int width = ignoreLineWidth ? 0 : GetWidth();
1920
1921 width += 2 * aClearance;
1922
1923 switch( m_shape )
1924 {
1925 case SHAPE_T::CIRCLE:
1926 {
1927 int r = GetRadius();
1928
1929 if( IsFilled() )
1930 TransformCircleToPolygon( aBuffer, getCenter(), r + width / 2, aError, aErrorLoc );
1931 else
1932 TransformRingToPolygon( aBuffer, getCenter(), r, width, aError, aErrorLoc );
1933
1934 break;
1935 }
1936
1937 case SHAPE_T::RECTANGLE:
1938 {
1939 std::vector<VECTOR2I> pts = GetRectCorners();
1940
1941 if( IsFilled() || IsProxyItem() )
1942 {
1943 aBuffer.NewOutline();
1944
1945 for( const VECTOR2I& pt : pts )
1946 aBuffer.Append( pt );
1947 }
1948
1949 if( width > 0 || !IsFilled() )
1950 {
1951 // Add in segments
1952 TransformOvalToPolygon( aBuffer, pts[0], pts[1], width, aError, aErrorLoc );
1953 TransformOvalToPolygon( aBuffer, pts[1], pts[2], width, aError, aErrorLoc );
1954 TransformOvalToPolygon( aBuffer, pts[2], pts[3], width, aError, aErrorLoc );
1955 TransformOvalToPolygon( aBuffer, pts[3], pts[0], width, aError, aErrorLoc );
1956 }
1957
1958 break;
1959 }
1960
1961 case SHAPE_T::ARC:
1962 TransformArcToPolygon( aBuffer, GetStart(), GetArcMid(), GetEnd(), width, aError, aErrorLoc );
1963 break;
1964
1965 case SHAPE_T::SEGMENT:
1966 TransformOvalToPolygon( aBuffer, GetStart(), GetEnd(), width, aError, aErrorLoc );
1967 break;
1968
1969 case SHAPE_T::POLY:
1970 {
1971 if( !IsPolyShapeValid() )
1972 break;
1973
1974 if( IsFilled() )
1975 {
1976 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1977 {
1978 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
1979 SHAPE_POLY_SET tmp;
1980 tmp.NewOutline();
1981
1982 for( int jj = 0; jj < (int) poly.GetPointCount(); ++jj )
1983 tmp.Append( poly.GetPoint( jj ) );
1984
1985 if( width > 0 )
1986 {
1987 int inflate = width / 2;
1988
1989 if( aErrorLoc == ERROR_OUTSIDE )
1990 inflate += aError;
1991
1992 tmp.Inflate( inflate, CORNER_STRATEGY::ROUND_ALL_CORNERS, aError );
1993 }
1994
1995 aBuffer.Append( tmp );
1996 }
1997 }
1998 else
1999 {
2000 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
2001 {
2002 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
2003
2004 for( int jj = 0; jj < (int) poly.SegmentCount(); ++jj )
2005 {
2006 const SEG& seg = poly.GetSegment( jj );
2007 TransformOvalToPolygon( aBuffer, seg.A, seg.B, width, aError, aErrorLoc );
2008 }
2009 }
2010 }
2011
2012 break;
2013 }
2014
2015 case SHAPE_T::BEZIER:
2016 {
2017 std::vector<VECTOR2I> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
2018 BEZIER_POLY converter( ctrlPts );
2019 std::vector<VECTOR2I> poly;
2020 converter.GetPoly( poly, aError );
2021
2022 for( unsigned ii = 1; ii < poly.size(); ii++ )
2023 TransformOvalToPolygon( aBuffer, poly[ii - 1], poly[ii], width, aError, aErrorLoc );
2024
2025 break;
2026 }
2027
2028 default:
2030 break;
2031 }
2032}
2033
2034
2036{
2037 m_stroke.SetLineStyle( aStyle );
2038}
2039
2040
2042{
2043 if( m_stroke.GetLineStyle() != LINE_STYLE::DEFAULT )
2044 return m_stroke.GetLineStyle();
2045
2046 return LINE_STYLE::SOLID;
2047}
2048
2049
2050bool EDA_SHAPE::operator==( const EDA_SHAPE& aOther ) const
2051{
2052 if( GetShape() != aOther.GetShape() )
2053 return false;
2054
2055 if( m_fill != aOther.m_fill )
2056 return false;
2057
2058 if( m_stroke.GetWidth() != aOther.m_stroke.GetWidth() )
2059 return false;
2060
2061 if( m_stroke.GetLineStyle() != aOther.m_stroke.GetLineStyle() )
2062 return false;
2063
2064 if( m_fillColor != aOther.m_fillColor )
2065 return false;
2066
2067 if( m_start != aOther.m_start )
2068 return false;
2069
2070 if( m_end != aOther.m_end )
2071 return false;
2072
2073 if( m_arcCenter != aOther.m_arcCenter )
2074 return false;
2075
2076 if( m_bezierC1 != aOther.m_bezierC1 )
2077 return false;
2078
2079 if( m_bezierC2 != aOther.m_bezierC2 )
2080 return false;
2081
2082 if( m_bezierPoints != aOther.m_bezierPoints )
2083 return false;
2084
2085 for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
2086 {
2087 if( m_poly.CVertex( ii ) != aOther.m_poly.CVertex( ii ) )
2088 return false;
2089 }
2090
2091 return true;
2092}
2093
2094
2095double EDA_SHAPE::Similarity( const EDA_SHAPE& aOther ) const
2096{
2097 if( GetShape() != aOther.GetShape() )
2098 return 0.0;
2099
2100 double similarity = 1.0;
2101
2102 if( m_fill != aOther.m_fill )
2103 similarity *= 0.9;
2104
2105 if( m_stroke.GetWidth() != aOther.m_stroke.GetWidth() )
2106 similarity *= 0.9;
2107
2108 if( m_stroke.GetLineStyle() != aOther.m_stroke.GetLineStyle() )
2109 similarity *= 0.9;
2110
2111 if( m_fillColor != aOther.m_fillColor )
2112 similarity *= 0.9;
2113
2114 if( m_start != aOther.m_start )
2115 similarity *= 0.9;
2116
2117 if( m_end != aOther.m_end )
2118 similarity *= 0.9;
2119
2120 if( m_arcCenter != aOther.m_arcCenter )
2121 similarity *= 0.9;
2122
2123 if( m_bezierC1 != aOther.m_bezierC1 )
2124 similarity *= 0.9;
2125
2126 if( m_bezierC2 != aOther.m_bezierC2 )
2127 similarity *= 0.9;
2128
2129 {
2130 int m = m_bezierPoints.size();
2131 int n = aOther.m_bezierPoints.size();
2132
2133 size_t longest = alg::longest_common_subset( m_bezierPoints, aOther.m_bezierPoints );
2134
2135 similarity *= std::pow( 0.9, m + n - 2 * longest );
2136 }
2137
2138 {
2139 int m = m_poly.TotalVertices();
2140 int n = aOther.m_poly.TotalVertices();
2141 std::vector<VECTOR2I> poly;
2142 std::vector<VECTOR2I> otherPoly;
2143 VECTOR2I lastPt( 0, 0 );
2144
2145 // We look for the longest common subset of the two polygons, but we need to
2146 // offset each point because we're actually looking for overall similarity, not just
2147 // exact matches. So if the zone is moved by 1IU, we only want one point to be
2148 // considered "moved" rather than the entire polygon. In this case, the first point
2149 // will not be a match but the rest of the sequence will.
2150 for( int ii = 0; ii < m; ++ii )
2151 {
2152 poly.emplace_back( lastPt - m_poly.CVertex( ii ) );
2153 lastPt = m_poly.CVertex( ii );
2154 }
2155
2156 lastPt = VECTOR2I( 0, 0 );
2157
2158 for( int ii = 0; ii < n; ++ii )
2159 {
2160 otherPoly.emplace_back( lastPt - aOther.m_poly.CVertex( ii ) );
2161 lastPt = aOther.m_poly.CVertex( ii );
2162 }
2163
2164 size_t longest = alg::longest_common_subset( poly, otherPoly );
2165
2166 similarity *= std::pow( 0.9, m + n - 2 * longest );
2167 }
2168
2169 return similarity;
2170}
2171
2172
2175
2176
2177static struct EDA_SHAPE_DESC
2178{
2180 {
2182 .Map( SHAPE_T::SEGMENT, _HKI( "Segment" ) )
2183 .Map( SHAPE_T::RECTANGLE, _HKI( "Rectangle" ) )
2184 .Map( SHAPE_T::ARC, _HKI( "Arc" ) )
2185 .Map( SHAPE_T::CIRCLE, _HKI( "Circle" ) )
2186 .Map( SHAPE_T::POLY, _HKI( "Polygon" ) )
2187 .Map( SHAPE_T::BEZIER, _HKI( "Bezier" ) );
2188
2189 auto& plotDashTypeEnum = ENUM_MAP<LINE_STYLE>::Instance();
2190
2191 if( plotDashTypeEnum.Choices().GetCount() == 0 )
2192 {
2193 plotDashTypeEnum.Map( LINE_STYLE::DEFAULT, _HKI( "Default" ) )
2194 .Map( LINE_STYLE::SOLID, _HKI( "Solid" ) )
2195 .Map( LINE_STYLE::DASH, _HKI( "Dashed" ) )
2196 .Map( LINE_STYLE::DOT, _HKI( "Dotted" ) )
2197 .Map( LINE_STYLE::DASHDOT, _HKI( "Dash-Dot" ) )
2198 .Map( LINE_STYLE::DASHDOTDOT, _HKI( "Dash-Dot-Dot" ) );
2199 }
2200
2203
2204 auto isNotPolygonOrCircle = []( INSPECTABLE* aItem ) -> bool
2205 {
2206 // Polygons, unlike other shapes, have no meaningful start or end coordinates
2207 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2208 return shape->GetShape() != SHAPE_T::POLY && shape->GetShape() != SHAPE_T::CIRCLE;
2209
2210 return false;
2211 };
2212
2213 auto isCircle = []( INSPECTABLE* aItem ) -> bool
2214 {
2215 // Polygons, unlike other shapes, have no meaningful start or end coordinates
2216 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2217 return shape->GetShape() == SHAPE_T::CIRCLE;
2218
2219 return false;
2220 };
2221
2222 auto isRectangle = []( INSPECTABLE* aItem ) -> bool
2223 {
2224 // Polygons, unlike other shapes, have no meaningful start or end coordinates
2225 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2226 return shape->GetShape() == SHAPE_T::RECTANGLE;
2227
2228 return false;
2229 };
2230
2231 const wxString shapeProps = _HKI( "Shape Properties" );
2232
2233 auto shape = new PROPERTY_ENUM<EDA_SHAPE, SHAPE_T>( _HKI( "Shape" ),
2235 propMgr.AddProperty( shape, shapeProps );
2236
2237 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start X" ),
2240 shapeProps )
2241 .SetAvailableFunc( isNotPolygonOrCircle );
2242 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start Y" ),
2245 shapeProps )
2246 .SetAvailableFunc( isNotPolygonOrCircle );
2247
2248 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Center X" ),
2251 shapeProps )
2252 .SetAvailableFunc( isCircle );
2253
2254 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Center Y" ),
2257 shapeProps )
2258 .SetAvailableFunc( isCircle );
2259
2260 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Radius" ),
2263 shapeProps )
2264 .SetAvailableFunc( isCircle );
2265
2266 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End X" ),
2269 shapeProps )
2270 .SetAvailableFunc( isNotPolygonOrCircle );
2271
2272 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End Y" ),
2275 shapeProps )
2276 .SetAvailableFunc( isNotPolygonOrCircle );
2277
2278 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Width" ),
2281 shapeProps )
2282 .SetAvailableFunc( isRectangle );
2283
2284 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Height" ),
2287 shapeProps )
2288 .SetAvailableFunc( isRectangle );
2289
2290 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Line Width" ),
2292 shapeProps );
2293
2294 propMgr.AddProperty( new PROPERTY_ENUM<EDA_SHAPE, LINE_STYLE>( _HKI( "Line Style" ),
2296 shapeProps );
2297
2298 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, COLOR4D>( _HKI( "Line Color" ),
2300 shapeProps )
2302
2303 auto angle = new PROPERTY<EDA_SHAPE, EDA_ANGLE>( _HKI( "Angle" ),
2306 angle->SetAvailableFunc(
2307 [=]( INSPECTABLE* aItem ) -> bool
2308 {
2309 if( EDA_SHAPE* curr_shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2310 return curr_shape->GetShape() == SHAPE_T::ARC;
2311
2312 return false;
2313 } );
2314 propMgr.AddProperty( angle, shapeProps );
2315
2316 auto fillAvailable =
2317 [=]( INSPECTABLE* aItem ) -> bool
2318 {
2319 if( EDA_ITEM* edaItem = dynamic_cast<EDA_ITEM*>( aItem ) )
2320 {
2321 // For some reason masking "Filled" and "Fill Color" at the
2322 // PCB_TABLECELL level doesn't work.
2323 if( edaItem->Type() == PCB_TABLECELL_T )
2324 return false;
2325 }
2326
2327 if( EDA_SHAPE* edaShape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2328 {
2329 switch( edaShape->GetShape() )
2330 {
2331 case SHAPE_T::POLY:
2332 case SHAPE_T::RECTANGLE:
2333 case SHAPE_T::CIRCLE:
2334 case SHAPE_T::BEZIER:
2335 return true;
2336
2337 default:
2338 return false;
2339 }
2340 }
2341
2342 return false;
2343 };
2344
2345 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, bool>( _HKI( "Filled" ),
2347 shapeProps )
2348 .SetAvailableFunc( fillAvailable );
2349
2350 auto fillColor = new PROPERTY<EDA_SHAPE, COLOR4D>( _HKI( "Fill Color" ),
2352 fillColor->SetWriteableFunc(
2353 [=]( INSPECTABLE* aItem ) -> bool
2354 {
2355 if( EDA_SHAPE* edaShape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2356 return edaShape->GetFillMode() == FILL_T::FILLED_WITH_COLOR;
2357
2358 return true;
2359 } );
2360 propMgr.AddProperty( fillColor, shapeProps )
2361 .SetAvailableFunc( fillAvailable )
2363 }
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
Definition: approximation.h:32
@ ERROR_OUTSIDE
Definition: approximation.h:33
@ ERROR_INSIDE
Definition: approximation.h:34
constexpr int ARC_HIGH_DEF
Definition: base_units.h:120
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
Bezier curves to polygon converter.
Definition: bezier_curves.h:38
void GetPoly(std::vector< VECTOR2I > &aOutput, int aMaxError=10)
Convert a Bezier curve to a polygon.
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:558
constexpr void SetOrigin(const Vec &pos)
Definition: box2.h:237
constexpr BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition: box2.h:146
constexpr Vec Centre() const
Definition: box2.h:97
bool IntersectsCircleEdge(const Vec &aCenter, const int aRadius, const int aWidth) const
Definition: box2.h:523
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:658
constexpr bool Contains(const Vec &aPoint) const
Definition: box2.h:168
constexpr void SetEnd(coord_type x, coord_type y)
Definition: box2.h:297
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:311
EDA_ANGLE Normalize()
Definition: eda_angle.h:221
bool IsCardinal() const
Definition: eda_angle.cpp:40
EDA_ANGLE Normalize720()
Definition: eda_angle.h:271
double AsRadians() const
Definition: eda_angle.h:117
The base class for create windows for drawing purpose.
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:896
SHAPE_T m_shape
Definition: eda_shape.h:421
void SetStartX(int x)
Definition: eda_shape.h:153
bool m_proxyItem
Definition: eda_shape.h:445
bool m_endsSwapped
Definition: eda_shape.h:420
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:213
void SetBezierC2(const VECTOR2I &aPt)
Definition: eda_shape.h:212
void move(const VECTOR2I &aMoveVector)
Definition: eda_shape.cpp:530
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:778
VECTOR2I getCenter() const
Definition: eda_shape.cpp:752
int GetStartY() const
Definition: eda_shape.h:138
void SetLength(const double &aLength)
Definition: eda_shape.cpp:430
int m_editState
Definition: eda_shape.h:444
void rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle)
Definition: eda_shape.cpp:625
const std::vector< VECTOR2I > buildBezierToSegmentsPointsList(int aMaxError) const
Definition: eda_shape.cpp:739
FILL_T GetFillMode() const
Definition: eda_shape.h:114
virtual ~EDA_SHAPE()
Definition: eda_shape.cpp:62
long long int m_rectangleHeight
Definition: eda_shape.h:426
void SetEndY(int aY)
Definition: eda_shape.h:184
virtual int GetEffectiveWidth() const
Definition: eda_shape.h:123
COLOR4D GetLineColor() const
Definition: eda_shape.h:129
int GetEndX() const
Definition: eda_shape.h:176
int GetRectangleWidth() const
Definition: eda_shape.cpp:417
void SetLineStyle(const LINE_STYLE aStyle)
Definition: eda_shape.cpp:2035
void calcEdit(const VECTOR2I &aPosition)
Definition: eda_shape.cpp:1661
void SetStartY(int y)
Definition: eda_shape.h:147
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:286
bool IsFilled() const
Definition: eda_shape.h:98
void SetCenterY(int y)
Definition: eda_shape.h:159
void CalcArcAngles(EDA_ANGLE &aStartAngle, EDA_ANGLE &aEndAngle) const
Calc arc start and end angles such that aStartAngle < aEndAngle.
Definition: eda_shape.cpp:810
void ShapeGetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList)
Definition: eda_shape.cpp:966
void SetFilled(bool aFlag)
Definition: eda_shape.h:108
bool operator==(const EDA_SHAPE &aOther) const
Definition: eda_shape.cpp:2050
int GetRadius() const
Definition: eda_shape.cpp:826
SHAPE_T GetShape() const
Definition: eda_shape.h:132
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:294
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
Definition: eda_shape.cpp:234
void SetRectangleHeight(const int &aHeight)
Definition: eda_shape.cpp:442
VECTOR2I m_arcCenter
Definition: eda_shape.h:435
double m_segmentLength
Definition: eda_shape.h:429
void SetCenterX(int x)
Definition: eda_shape.h:165
virtual bool IsFilledForHitTesting() const
Definition: eda_shape.h:103
bool continueEdit(const VECTOR2I &aPosition)
Definition: eda_shape.cpp:1627
wxString ShowShape() const
Definition: eda_shape.cpp:313
ARC_MID m_arcMidData
Definition: eda_shape.h:436
void SetFillColor(const COLOR4D &aColor)
Definition: eda_shape.h:119
int GetEndY() const
Definition: eda_shape.h:175
std::vector< SHAPE * > makeEffectiveShapes(bool aEdgeOnly, bool aLineChainOnly=false) const
Make a set of SHAPE objects representing the EDA_SHAPE.
Definition: eda_shape.cpp:1461
bool hitTest(const VECTOR2I &aPosition, int aAccuracy=0) const
Definition: eda_shape.cpp:1077
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:849
void SetEndX(int aX)
Definition: eda_shape.h:190
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:725
void flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection)
Definition: eda_shape.cpp:682
EDA_SHAPE(SHAPE_T aType, int aLineWidth, FILL_T aFill)
Definition: eda_shape.cpp:47
void beginEdit(const VECTOR2I &aStartPoint)
Definition: eda_shape.cpp:1586
VECTOR2I m_start
Definition: eda_shape.h:432
int GetPointCount() const
Definition: eda_shape.cpp:1578
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:174
bool IsClosed() const
Definition: eda_shape.cpp:498
void SetRadius(int aX)
Definition: eda_shape.h:196
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:141
void DupPolyPointsList(std::vector< VECTOR2I > &aBuffer) const
Duplicate the list of corners in a std::vector<VECTOR2I>
Definition: eda_shape.cpp:1554
LINE_STYLE GetLineStyle() const
Definition: eda_shape.cpp:2041
void endEdit(bool aClosed=true)
Finishes editing the shape.
Definition: eda_shape.cpp:1815
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:137
void SetLineColor(const COLOR4D &aColor)
Definition: eda_shape.h:128
COLOR4D GetFillColor() const
Definition: eda_shape.h:118
void SetRectangle(const long long int &aHeight, const long long int &aWidth)
Definition: eda_shape.cpp:470
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:131
void SwapShape(EDA_SHAPE *aImage)
Definition: eda_shape.cpp:1852
std::vector< VECTOR2I > GetRectCorners() const
Definition: eda_shape.cpp:1386
void SetSegmentAngle(const EDA_ANGLE &aAngle)
Definition: eda_shape.cpp:485
std::vector< VECTOR2I > m_bezierPoints
Definition: eda_shape.h:441
void setPosition(const VECTOR2I &aPos)
Definition: eda_shape.cpp:357
virtual bool IsProxyItem() const
Definition: eda_shape.h:95
void computeArcBBox(BOX2I &aBBox) const
Definition: eda_shape.cpp:1401
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:178
void SetRectangleWidth(const int &aWidth)
Definition: eda_shape.cpp:456
void SetBezierC1(const VECTOR2I &aPt)
Definition: eda_shape.h:209
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:858
double GetLength() const
Definition: eda_shape.cpp:374
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:340
void scale(double aScale)
Definition: eda_shape.cpp:567
int GetStartX() const
Definition: eda_shape.h:139
double Similarity(const EDA_SHAPE &aOther) const
Definition: eda_shape.cpp:2095
const VECTOR2I & GetBezierC1() const
Definition: eda_shape.h:210
VECTOR2I m_end
Definition: eda_shape.h:433
const BOX2I getBoundingBox() const
Definition: eda_shape.cpp:1023
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:924
SHAPE_POLY_SET m_poly
Definition: eda_shape.h:442
int GetRectangleHeight() const
Definition: eda_shape.cpp:404
virtual int GetWidth() const
Definition: eda_shape.h:122
VECTOR2I getPosition() const
Definition: eda_shape.cpp:363
bool IsClockwiseArc() const
Definition: eda_shape.cpp:907
STROKE_PARAMS m_stroke
Definition: eda_shape.h:422
EDA_ANGLE m_segmentAngle
Definition: eda_shape.h:430
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
Definition: eda_shape.cpp:1451
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:1916
wxString getFriendlyName() const
Definition: eda_shape.cpp:939
VECTOR2I m_bezierC1
Definition: eda_shape.h:438
FILL_T m_fill
Definition: eda_shape.h:423
COLOR4D m_fillColor
Definition: eda_shape.h:424
void SetWidth(int aWidth)
Definition: eda_shape.h:121
EDA_ANGLE GetSegmentAngle() const
Definition: eda_shape.cpp:887
long long int m_rectangleWidth
Definition: eda_shape.h:427
VECTOR2I m_bezierC2
Definition: eda_shape.h:439
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition: eda_shape.cpp:146
bool IsPolyShapeValid() const
Definition: eda_shape.cpp:1571
int Compare(const EDA_SHAPE *aOther) const
Definition: eda_shape.cpp:1875
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:796
static ENUM_MAP< T > & Instance()
Definition: property.h:663
Class that other classes need to inherit from, in order to be inspectable.
Definition: inspectable.h:36
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
PROPERTY_BASE & SetAvailableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Set a callback function to determine whether an object provides this property.
Definition: property.h:257
PROPERTY_BASE & SetIsHiddenFromRulesEditor(bool aHide=true)
Definition: property.h:307
Provide class metadata.Helper macro to map type hashes to names.
Definition: property_mgr.h:85
static PROPERTY_MANAGER & Instance()
Definition: property_mgr.h:87
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
int Length() const
Return the length (this).
Definition: seg.h:333
SHAPE_TYPE Type() const
Return the type of the shape.
Definition: shape.h:98
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 AddOutline(const SHAPE_LINE_CHAIN &aOutline)
Adds a new outline to the set and returns its index.
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)
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.
void Mirror(const VECTOR2I &aRef, FLIP_DIRECTION aFlipDirection)
Mirror the line points about y or x (or both)
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
An abstract shape on 2D plane.
Definition: shape.h:126
int GetWidth() const
Definition: stroke_params.h:89
void SetLineStyle(LINE_STYLE aLineStyle)
Definition: stroke_params.h:93
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:92
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition: vector2d.h:557
constexpr extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition: vector2d.h:307
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:283
void TransformRingToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aCentre, int aRadius, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arcs to multiple straight segments.
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
void TransformArcToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arc to multiple straight segments.
void TransformOvalToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a oblong shape to a polygon, using multiple segments.
#define _HKI(x)
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:401
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
@ RADIANS_T
Definition: eda_angle.h:32
static constexpr EDA_ANGLE ANGLE_270
Definition: eda_angle.h:406
static constexpr EDA_ANGLE ANGLE_360
Definition: eda_angle.h:407
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:405
#define TEST_PT(a, b)
#define TEST(a, b)
#define TEST_E(a, b)
static struct EDA_SHAPE_DESC _EDA_SHAPE_DESC
#define SWAPITEM(x)
#define sq(x)
SHAPE_T
Definition: eda_shape.h:43
@ ARC
use RECTANGLE instead of RECT to avoid collision in a Windows header
FILL_T
Definition: eda_shape.h:56
@ FILLED_WITH_COLOR
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
constexpr void MIRROR(T &aPoint, const T &aMirrorRef)
Updates aPoint with the mirror of aPoint relative to the aMirrorRef.
Definition: mirror.h:45
FLIP_DIRECTION
Definition: mirror.h:27
KICOMMON_API wxString MessageTextFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A helper to convert the double length aValue to a string in inches, millimeters, or unscaled units.
Definition: eda_units.cpp:408
size_t longest_common_subset(const _Container &__c1, const _Container &__c2)
Returns the length of the longest common subset of values between two containers.
Definition: kicad_algo.h:208
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:390
#define IMPLEMENT_ENUM_TO_WXANY(type)
Definition: property.h:763
#define NO_SETTER(owner, type)
Definition: property.h:774
@ PT_COORD
Coordinate expressed in distance units (mm/inch)
Definition: property.h:64
@ PT_DECIDEGREE
Angle expressed in decidegrees.
Definition: property.h:66
@ PT_SIZE
Size expressed in distance units (mm/inch)
Definition: property.h:62
#define REGISTER_TYPE(x)
Definition: property_mgr.h:371
@ SH_POLY_SET
set of polygons (with holes, etc.)
Definition: shape.h:52
@ SH_RECT
axis-aligned rectangle
Definition: shape.h:47
@ SH_CIRCLE
circle
Definition: shape.h:50
@ SH_SIMPLE
simple polygon
Definition: shape.h:51
@ SH_NULL
empty shape (no shape...),
Definition: shape.h:55
@ SH_SEGMENT
line segment
Definition: shape.h:48
@ SH_ARC
circular arc
Definition: shape.h:54
@ SH_POLY_SET_TRIANGLE
a single triangle belonging to a POLY_SET triangulation
Definition: shape.h:56
@ SH_LINE_CHAIN
line chain (polyline)
Definition: shape.h:49
@ SH_COMPOUND
compound shape, consisting of multiple simple shapes
Definition: shape.h:53
LINE_STYLE
Dashed line types.
Definition: stroke_params.h:46
VECTOR2I end
Definition: eda_shape.h:69
VECTOR2I center
Definition: eda_shape.h:70
VECTOR2I start
Definition: eda_shape.h:68
VECTOR2I mid
Definition: eda_shape.h:67
bool TestSegmentHit(const VECTOR2I &aRefPoint, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aDist)
Test if aRefPoint is with aDistance on the line defined by aStart and aEnd.
Definition: trigo.cpp:175
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition: trigo.cpp:229
const VECTOR2I CalcArcCenter(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Determine the center of an arc or circle given three points on its circumference.
Definition: trigo.cpp:521
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition: typeinfo.h:95
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691
VECTOR2< double > VECTOR2D
Definition: vector2d.h:690