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