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_hatchingDirty( true ),
72 m_rectangleHeight( 0 ),
73 m_rectangleWidth( 0 ),
74 m_segmentLength( 0 ),
75 m_editState( 0 ),
76 m_proxyItem( false )
77{
78 switch( aShape.Type() )
79 {
80 case SH_RECT:
81 {
82 auto rect = static_cast<const SHAPE_RECT&>( aShape );
83 m_shape = SHAPE_T::RECTANGLE;
84 SetStart( rect.GetPosition() );
85 SetEnd( rect.GetPosition() + rect.GetSize() );
86 break;
87 }
88
89 case SH_SEGMENT:
90 {
91 auto seg = static_cast<const SHAPE_SEGMENT&>( aShape );
92 m_shape = SHAPE_T::SEGMENT;
93 SetStart( seg.GetSeg().A );
94 SetEnd( seg.GetSeg().B );
95 SetWidth( seg.GetWidth() );
96 break;
97 }
98
99 case SH_LINE_CHAIN:
100 {
101 auto line = static_cast<const SHAPE_LINE_CHAIN&>( aShape );
102 m_shape = SHAPE_T::POLY;
104 m_poly.AddOutline( line );
105 SetWidth( line.Width() );
106 break;
107 }
108
109 case SH_CIRCLE:
110 {
111 auto circle = static_cast<const SHAPE_CIRCLE&>( aShape );
112 m_shape = SHAPE_T::CIRCLE;
115 break;
116 }
117
118 case SH_ARC:
119 {
120 auto arc = static_cast<const SHAPE_ARC&>( aShape );
121 m_shape = SHAPE_T::ARC;
122 SetArcGeometry( arc.GetP0(), arc.GetArcMid(), arc.GetP1() );
123 SetWidth( arc.GetWidth() );
124 break;
125 }
126
127 case SH_SIMPLE:
128 {
129 auto poly = static_cast<const SHAPE_SIMPLE&>( aShape );
130 m_shape = SHAPE_T::POLY;
131 poly.TransformToPolygon( m_poly, 0, ERROR_INSIDE );
132 break;
133 }
134
135 // currently unhandled
136 case SH_POLY_SET:
137 case SH_COMPOUND:
138 case SH_NULL:
140 default:
141 m_shape = SHAPE_T::UNDEFINED;
142 break;
143 }
144}
145
146
147void EDA_SHAPE::Serialize( google::protobuf::Any &aContainer ) const
148{
149 using namespace kiapi::common;
150 types::GraphicShape shape;
151
152 types::StrokeAttributes* stroke = shape.mutable_attributes()->mutable_stroke();
153 types::GraphicFillAttributes* fill = shape.mutable_attributes()->mutable_fill();
154
155 stroke->mutable_width()->set_value_nm( GetWidth() );
156
157 switch( GetLineStyle() )
158 {
159 case LINE_STYLE::DEFAULT: stroke->set_style( types::SLS_DEFAULT ); break;
160 case LINE_STYLE::SOLID: stroke->set_style( types::SLS_SOLID ); break;
161 case LINE_STYLE::DASH: stroke->set_style( types::SLS_DASH ); break;
162 case LINE_STYLE::DOT: stroke->set_style( types::SLS_DOT ); break;
163 case LINE_STYLE::DASHDOT: stroke->set_style( types::SLS_DASHDOT ); break;
164 case LINE_STYLE::DASHDOTDOT: stroke->set_style( types::SLS_DASHDOTDOT ); break;
165 default: break;
166 }
167
168 switch( GetFillMode() )
169 {
170 case FILL_T::FILLED_SHAPE: fill->set_fill_type( types::GFT_FILLED ); break;
171 default: fill->set_fill_type( types::GFT_UNFILLED ); break;
172 }
173
174 switch( GetShape() )
175 {
176 case SHAPE_T::SEGMENT:
177 {
178 types::GraphicSegmentAttributes* segment = shape.mutable_segment();
179 PackVector2( *segment->mutable_start(), GetStart() );
180 PackVector2( *segment->mutable_end(), GetEnd() );
181 break;
182 }
183
184 case SHAPE_T::RECTANGLE:
185 {
186 types::GraphicRectangleAttributes* rectangle = shape.mutable_rectangle();
187 PackVector2( *rectangle->mutable_top_left(), GetStart() );
188 PackVector2( *rectangle->mutable_bottom_right(), GetEnd() );
189 break;
190 }
191
192 case SHAPE_T::ARC:
193 {
194 types::GraphicArcAttributes* arc = shape.mutable_arc();
195 PackVector2( *arc->mutable_start(), GetStart() );
196 PackVector2( *arc->mutable_mid(), GetArcMid() );
197 PackVector2( *arc->mutable_end(), GetEnd() );
198 break;
199 }
200
201 case SHAPE_T::CIRCLE:
202 {
203 types::GraphicCircleAttributes* circle = shape.mutable_circle();
204 PackVector2( *circle->mutable_center(), GetStart() );
205 PackVector2( *circle->mutable_radius_point(), GetEnd() );
206 break;
207 }
208
209 case SHAPE_T::POLY:
210 {
211 PackPolySet( *shape.mutable_polygon(), GetPolyShape() );
212 break;
213 }
214
215 case SHAPE_T::BEZIER:
216 {
217 types::GraphicBezierAttributes* bezier = shape.mutable_bezier();
218 PackVector2( *bezier->mutable_start(), GetStart() );
219 PackVector2( *bezier->mutable_control1(), GetBezierC1() );
220 PackVector2( *bezier->mutable_control2(), GetBezierC2() );
221 PackVector2( *bezier->mutable_end(), GetEnd() );
222 break;
223 }
224
225 default:
226 wxASSERT_MSG( false, "Unhandled shape in PCB_SHAPE::Serialize" );
227 }
228
229 // TODO m_hasSolderMask and m_solderMaskMargin
230
231 aContainer.PackFrom( shape );
232}
233
234
235bool EDA_SHAPE::Deserialize( const google::protobuf::Any &aContainer )
236{
237 using namespace kiapi::common;
238
239 types::GraphicShape shape;
240
241 if( !aContainer.UnpackTo( &shape ) )
242 return false;
243
244 // Initialize everything to a known state that doesn't get touched by every
245 // codepath below, to make sure the equality operator is consistent
246 m_start = {};
247 m_end = {};
248 m_arcCenter = {};
249 m_arcMidData = {};
250 m_bezierC1 = {};
251 m_bezierC2 = {};
252 m_editState = 0;
253 m_proxyItem = false;
254 m_endsSwapped = false;
255
256 SetFilled( shape.attributes().fill().fill_type() == types::GFT_FILLED );
257 SetWidth( shape.attributes().stroke().width().value_nm() );
258
259 switch( shape.attributes().stroke().style() )
260 {
261 case types::SLS_DEFAULT: SetLineStyle( LINE_STYLE::DEFAULT ); break;
262 case types::SLS_SOLID: SetLineStyle( LINE_STYLE::SOLID ); break;
263 case types::SLS_DASH: SetLineStyle( LINE_STYLE::DASH ); break;
264 case types::SLS_DOT: SetLineStyle( LINE_STYLE::DOT ); break;
265 case types::SLS_DASHDOT: SetLineStyle( LINE_STYLE::DASHDOT ); break;
266 case types::SLS_DASHDOTDOT: SetLineStyle( LINE_STYLE::DASHDOTDOT ); break;
267 default: break;
268 }
269
270 if( shape.has_segment() )
271 {
272 SetShape( SHAPE_T::SEGMENT );
273 SetStart( UnpackVector2( shape.segment().start() ) );
274 SetEnd( UnpackVector2( shape.segment().end() ) );
275 }
276 else if( shape.has_rectangle() )
277 {
278 SetShape( SHAPE_T::RECTANGLE );
279 SetStart( UnpackVector2( shape.rectangle().top_left() ) );
280 SetEnd( UnpackVector2( shape.rectangle().bottom_right() ) );
281 }
282 else if( shape.has_arc() )
283 {
284 SetShape( SHAPE_T::ARC );
285 SetArcGeometry( UnpackVector2( shape.arc().start() ),
286 UnpackVector2( shape.arc().mid() ),
287 UnpackVector2( shape.arc().end() ) );
288 }
289 else if( shape.has_circle() )
290 {
291 SetShape( SHAPE_T::CIRCLE );
292 SetStart( UnpackVector2( shape.circle().center() ) );
293 SetEnd( UnpackVector2( shape.circle().radius_point() ) );
294 }
295 else if( shape.has_polygon() )
296 {
297 SetShape( SHAPE_T::POLY );
298 SetPolyShape( UnpackPolySet( shape.polygon() ) );
299 }
300 else if( shape.has_bezier() )
301 {
302 SetShape( SHAPE_T::BEZIER );
303 SetStart( UnpackVector2( shape.bezier().start() ) );
304 SetBezierC1( UnpackVector2( shape.bezier().control1() ) );
305 SetBezierC2( UnpackVector2( shape.bezier().control2() ) );
306 SetEnd( UnpackVector2( shape.bezier().end() ) );
308 }
309
310 return true;
311}
312
313
314wxString EDA_SHAPE::ShowShape() const
315{
316 if( IsProxyItem() )
317 {
318 switch( m_shape )
319 {
320 case SHAPE_T::SEGMENT: return _( "Thermal Spoke" );
321 case SHAPE_T::RECTANGLE: return _( "Number Box" );
322 default: return wxT( "??" );
323 }
324 }
325 else
326 {
327 switch( m_shape )
328 {
329 case SHAPE_T::SEGMENT: return _( "Line" );
330 case SHAPE_T::RECTANGLE: return _( "Rect" );
331 case SHAPE_T::ARC: return _( "Arc" );
332 case SHAPE_T::CIRCLE: return _( "Circle" );
333 case SHAPE_T::BEZIER: return _( "Bezier Curve" );
334 case SHAPE_T::POLY: return _( "Polygon" );
335 default: return wxT( "??" );
336 }
337 }
338}
339
340
342{
343 switch( m_shape )
344 {
345 case SHAPE_T::SEGMENT: return wxS( "S_SEGMENT" );
346 case SHAPE_T::RECTANGLE: return wxS( "S_RECT" );
347 case SHAPE_T::ARC: return wxS( "S_ARC" );
348 case SHAPE_T::CIRCLE: return wxS( "S_CIRCLE" );
349 case SHAPE_T::POLY: return wxS( "S_POLYGON" );
350 case SHAPE_T::BEZIER: return wxS( "S_CURVE" );
351 case SHAPE_T::UNDEFINED: return wxS( "UNDEFINED" );
352 }
353
354 return wxEmptyString; // Just to quiet GCC.
355}
356
357
359{
360 move( aPos - getPosition() );
361}
362
363
365{
366 if( m_shape == SHAPE_T::ARC )
367 return getCenter();
368 else if( m_shape == SHAPE_T::POLY )
369 return m_poly.CVertex( 0 );
370 else
371 return m_start;
372}
373
374
376{
377 double length = 0.0;
378
379 switch( m_shape )
380 {
381 case SHAPE_T::BEZIER:
382 for( size_t ii = 1; ii < m_bezierPoints.size(); ++ii )
383 length += m_bezierPoints[ ii - 1].Distance( m_bezierPoints[ii] );
384
385 return length;
386
387 case SHAPE_T::SEGMENT:
388 return GetStart().Distance( GetEnd() );
389
390 case SHAPE_T::POLY:
391 for( int ii = 0; ii < m_poly.COutline( 0 ).SegmentCount(); ii++ )
392 length += m_poly.COutline( 0 ).CSegment( ii ).Length();
393
394 return length;
395
396 case SHAPE_T::ARC:
397 return GetRadius() * GetArcAngle().AsRadians();
398
399 default:
401 return 0.0;
402 }
403}
404
405
407{
408 switch( m_shape )
409 {
410 case SHAPE_T::RECTANGLE:
411 return GetEndY() - GetStartY();
412
413 default:
415 return 0;
416 }
417}
418
419
421{
422 switch( m_shape )
423 {
424 case SHAPE_T::RECTANGLE:
425 return GetEndX() - GetStartX();
426
427 default:
429 return 0;
430 }
431}
432
433
434void EDA_SHAPE::SetLength( const double& aLength )
435{
436 switch( m_shape )
437 {
438 case SHAPE_T::SEGMENT:
439 m_segmentLength = aLength; break;
440
441 default:
443 }
444}
445
446
447void EDA_SHAPE::SetRectangleHeight( const int& aHeight )
448{
449 switch ( m_shape )
450 {
451 case SHAPE_T::RECTANGLE:
452 m_rectangleHeight = aHeight;
454 break;
455
456 default:
458 }
459}
460
461
462void EDA_SHAPE::SetRectangleWidth( const int& aWidth )
463{
464 switch ( m_shape )
465 {
466 case SHAPE_T::RECTANGLE:
467 m_rectangleWidth = aWidth;
469 break;
470
471 default:
473 }
474}
475
476
477void EDA_SHAPE::SetRectangle( const long long int& aHeight, const long long int& aWidth )
478{
479 switch ( m_shape )
480 {
481 case SHAPE_T::RECTANGLE:
482 m_rectangleHeight = aHeight;
483 m_rectangleWidth = aWidth;
484 break;
485
486 default:
488 }
489}
490
491
493{
494 switch( m_shape )
495 {
496 case SHAPE_T::SEGMENT:
497 m_segmentAngle = aAngle;
498 break;
499
500 default:
502 }
503}
504
505
507{
508 switch( m_shape )
509 {
510 case SHAPE_T::CIRCLE:
511 case SHAPE_T::RECTANGLE:
512 return true;
513
514 case SHAPE_T::ARC:
515 case SHAPE_T::SEGMENT:
516 return false;
517
518 case SHAPE_T::POLY:
519 if( m_poly.IsEmpty() )
520 return false;
521 else
522 return m_poly.Outline( 0 ).IsClosed();
523
524 case SHAPE_T::BEZIER:
525 if( m_bezierPoints.size() < 3 )
526 return false;
527 else
528 return m_bezierPoints[0] == m_bezierPoints[ m_bezierPoints.size() - 1 ];
529
530 default:
532 return false;
533 }
534}
535
536
538{
539 m_fill = aFill;
540 m_hatchingDirty = true;
541}
542
543
545{
546 switch( aFill )
547 {
548 case UI_FILL_MODE::NONE: SetFillMode( FILL_T::NO_FILL ); break;
549 case UI_FILL_MODE::HATCH: SetFillMode( FILL_T::HATCH ); break;
550 case UI_FILL_MODE::REVERSE_HATCH: SetFillMode( FILL_T::REVERSE_HATCH ); break;
551 case UI_FILL_MODE::CROSS_HATCH: SetFillMode( FILL_T::CROSS_HATCH ); break;
552 default: SetFilled( true ); break;
553 }
554}
555
556
558{
559 switch( m_fill )
560 {
561 case FILL_T::NO_FILL: return UI_FILL_MODE::NONE;
562 case FILL_T::HATCH: return UI_FILL_MODE::HATCH;
563 case FILL_T::REVERSE_HATCH: return UI_FILL_MODE::REVERSE_HATCH;
564 case FILL_T::CROSS_HATCH: return UI_FILL_MODE::CROSS_HATCH;
565 default: return UI_FILL_MODE::SOLID;
566 }
567}
568
569
571{
572 if( !m_hatchingDirty )
573 return;
574
576
577 std::vector<double> slopes;
578 int lineWidth = GetHatchLineWidth();
579 int spacing = GetHatchLineSpacing();
580 SHAPE_POLY_SET shapeBuffer;
581
582 if( isMoving() )
583 return;
584 else if( GetFillMode() == FILL_T::CROSS_HATCH )
585 slopes = { 1.0, -1.0 };
586 else if( GetFillMode() == FILL_T::HATCH )
587 slopes = { -1.0 };
588 else if( GetFillMode() == FILL_T::REVERSE_HATCH )
589 slopes = { 1.0 };
590 else
591 return;
592
593 if( spacing == 0 )
594 return;
595
596 switch( m_shape )
597 {
598 case SHAPE_T::ARC:
599 case SHAPE_T::SEGMENT:
600 case SHAPE_T::BEZIER:
601 return;
602
603 case SHAPE_T::RECTANGLE:
604 shapeBuffer.NewOutline();
605
606 for( const VECTOR2I& pt : GetRectCorners() )
607 shapeBuffer.Append( pt );
608
609 break;
610
611 case SHAPE_T::CIRCLE:
614 break;
615
616 case SHAPE_T::POLY:
617 if( !IsClosed() )
618 return;
619
620 shapeBuffer = m_poly.CloneDropTriangulation();
621 break;
622
623 default:
625 return;
626 }
627
628 if( GetFillMode() == FILL_T::HATCH || GetFillMode() == FILL_T::REVERSE_HATCH )
629 {
630 for( const SEG& seg : shapeBuffer.GenerateHatchLines( slopes, spacing, -1 ) )
631 {
632 // We don't really need the rounded ends at all, so don't spend any extra time on them
633 int maxError = lineWidth;
634
635 TransformOvalToPolygon( m_hatching, seg.A, seg.B, lineWidth, maxError, ERROR_INSIDE );
636 }
637
639 }
640 else
641 {
642 // Generate a grid of holes for a cross-hatch. This is about 3X the speed of the above
643 // algorithm, even when modified for the 45-degree fracture problem.
644
645 int gridsize = GetHatchLineSpacing();
646 int hole_size = gridsize - GetHatchLineWidth();
647
648 m_hatching = shapeBuffer.CloneDropTriangulation();
650
651 // Build hole shape
652 SHAPE_LINE_CHAIN hole_base;
653 VECTOR2I corner( 0, 0 );;
654 hole_base.Append( corner );
655 corner.x += hole_size;
656 hole_base.Append( corner );
657 corner.y += hole_size;
658 hole_base.Append( corner );
659 corner.x = 0;
660 hole_base.Append( corner );
661 hole_base.SetClosed( true );
662
663 // Build holes
664 BOX2I bbox = m_hatching.BBox( 0 );
665 SHAPE_POLY_SET holes;
666
667 int x_offset = bbox.GetX() - ( bbox.GetX() ) % gridsize - gridsize;
668 int y_offset = bbox.GetY() - ( bbox.GetY() ) % gridsize - gridsize;
669
670 for( int xx = x_offset; xx <= bbox.GetRight(); xx += gridsize )
671 {
672 for( int yy = y_offset; yy <= bbox.GetBottom(); yy += gridsize )
673 {
674 SHAPE_LINE_CHAIN hole( hole_base );
675 hole.Move( VECTOR2I( xx, yy ) );
676 holes.AddOutline( hole );
677 }
678 }
679
682
683 // Must re-rotate after Fracture(). Clipper struggles mightily with fracturing
684 // 45-degree holes.
686 }
687}
688
689
690void EDA_SHAPE::move( const VECTOR2I& aMoveVector )
691{
692 switch ( m_shape )
693 {
694 case SHAPE_T::ARC:
695 m_arcCenter += aMoveVector;
696 m_arcMidData.center += aMoveVector;
697 m_arcMidData.start += aMoveVector;
698 m_arcMidData.end += aMoveVector;
699 m_arcMidData.mid += aMoveVector;
701
702 case SHAPE_T::SEGMENT:
703 case SHAPE_T::RECTANGLE:
704 case SHAPE_T::CIRCLE:
705 m_start += aMoveVector;
706 m_end += aMoveVector;
707 break;
708
709 case SHAPE_T::POLY:
710 m_poly.Move( aMoveVector );
711 break;
712
713 case SHAPE_T::BEZIER:
714 m_start += aMoveVector;
715 m_end += aMoveVector;
716 m_bezierC1 += aMoveVector;
717 m_bezierC2 += aMoveVector;
718
719 for( VECTOR2I& pt : m_bezierPoints )
720 pt += aMoveVector;
721
722 break;
723
724 default:
726 break;
727 }
728
729 m_hatchingDirty = true;
730}
731
732
733void EDA_SHAPE::scale( double aScale )
734{
735 auto scalePt = [&]( VECTOR2I& pt )
736 {
737 pt.x = KiROUND( pt.x * aScale );
738 pt.y = KiROUND( pt.y * aScale );
739 };
740
741 switch( m_shape )
742 {
743 case SHAPE_T::ARC:
744 scalePt( m_arcCenter );
746
747 case SHAPE_T::SEGMENT:
748 case SHAPE_T::RECTANGLE:
749 scalePt( m_start );
750 scalePt( m_end );
751 break;
752
753 case SHAPE_T::CIRCLE: // ring or circle
754 scalePt( m_start );
755 m_end.x = m_start.x + KiROUND( GetRadius() * aScale );
756 m_end.y = m_start.y;
757 break;
758
759 case SHAPE_T::POLY: // polygon
760 {
761 std::vector<VECTOR2I> pts;
762
763 for( int ii = 0; ii < m_poly.OutlineCount(); ++ ii )
764 {
765 for( const VECTOR2I& pt : m_poly.Outline( ii ).CPoints() )
766 {
767 pts.emplace_back( pt );
768 scalePt( pts.back() );
769 }
770 }
771
772 SetPolyPoints( pts );
773 }
774 break;
775
776 case SHAPE_T::BEZIER:
777 scalePt( m_start );
778 scalePt( m_end );
779 scalePt( m_bezierC1 );
780 scalePt( m_bezierC2 );
782 break;
783
784 default:
786 break;
787 }
788
789 m_hatchingDirty = true;
790}
791
792
793void EDA_SHAPE::rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
794{
795 switch( m_shape )
796 {
797 case SHAPE_T::SEGMENT:
798 case SHAPE_T::CIRCLE:
799 RotatePoint( m_start, aRotCentre, aAngle );
800 RotatePoint( m_end, aRotCentre, aAngle );
801 break;
802
803 case SHAPE_T::ARC:
804 RotatePoint( m_start, aRotCentre, aAngle );
805 RotatePoint( m_end, aRotCentre, aAngle );
806 RotatePoint( m_arcCenter, aRotCentre, aAngle );
807 RotatePoint( m_arcMidData.start, aRotCentre, aAngle );
808 RotatePoint( m_arcMidData.end, aRotCentre, aAngle );
809 RotatePoint( m_arcMidData.mid, aRotCentre, aAngle );
810 RotatePoint( m_arcMidData.center, aRotCentre, aAngle );
811 break;
812
813 case SHAPE_T::RECTANGLE:
814 if( aAngle.IsCardinal() )
815 {
816 RotatePoint( m_start, aRotCentre, aAngle );
817 RotatePoint( m_end, aRotCentre, aAngle );
818 break;
819 }
820
821 // Convert non-cardinally-rotated rect to a diamond
822 m_shape = SHAPE_T::POLY;
829
831
832 case SHAPE_T::POLY:
833 m_poly.Rotate( aAngle, aRotCentre );
834 break;
835
836 case SHAPE_T::BEZIER:
837 RotatePoint( m_start, aRotCentre, aAngle );
838 RotatePoint( m_end, aRotCentre, aAngle );
839 RotatePoint( m_bezierC1, aRotCentre, aAngle );
840 RotatePoint( m_bezierC2, aRotCentre, aAngle );
841
842 for( VECTOR2I& pt : m_bezierPoints )
843 RotatePoint( pt, aRotCentre, aAngle);
844
845 break;
846
847 default:
849 break;
850 }
851
852 m_hatchingDirty = true;
853}
854
855
856void EDA_SHAPE::flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
857{
858 switch ( m_shape )
859 {
860 case SHAPE_T::SEGMENT:
861 case SHAPE_T::RECTANGLE:
862 MIRROR( m_start, aCentre, aFlipDirection );
863 MIRROR( m_end, aCentre, aFlipDirection );
864 break;
865
866 case SHAPE_T::CIRCLE:
867 MIRROR( m_start, aCentre, aFlipDirection );
868 MIRROR( m_end, aCentre, aFlipDirection );
869 break;
870
871 case SHAPE_T::ARC:
872 MIRROR( m_start, aCentre, aFlipDirection );
873 MIRROR( m_end, aCentre, aFlipDirection );
874 MIRROR( m_arcCenter, aCentre, aFlipDirection );
875
876 std::swap( m_start, m_end );
877 break;
878
879 case SHAPE_T::POLY:
880 m_poly.Mirror( aCentre, aFlipDirection );
881 break;
882
883 case SHAPE_T::BEZIER:
884 MIRROR( m_start, aCentre, aFlipDirection );
885 MIRROR( m_end, aCentre, aFlipDirection );
886 MIRROR( m_bezierC1, aCentre, aFlipDirection );
887 MIRROR( m_bezierC2, aCentre, aFlipDirection );
888
890 break;
891
892 default:
894 break;
895 }
896
897 m_hatchingDirty = true;
898}
899
900
902{
903 // Has meaning only for SHAPE_T::BEZIER
904 if( m_shape != SHAPE_T::BEZIER )
905 {
906 m_bezierPoints.clear();
907 return;
908 }
909
910 // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
912}
913
914
915const std::vector<VECTOR2I> EDA_SHAPE::buildBezierToSegmentsPointsList( int aMaxError ) const
916{
917 std::vector<VECTOR2I> bezierPoints;
918
919 // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
920 std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
921 BEZIER_POLY converter( ctrlPoints );
922 converter.GetPoly( bezierPoints, aMaxError );
923
924 return bezierPoints;
925}
926
927
929{
930 switch( m_shape )
931 {
932 case SHAPE_T::ARC:
933 return m_arcCenter;
934
935 case SHAPE_T::CIRCLE:
936 return m_start;
937
938 case SHAPE_T::SEGMENT:
939 // Midpoint of the line
940 return ( m_start + m_end ) / 2;
941
942 case SHAPE_T::POLY:
943 case SHAPE_T::RECTANGLE:
944 case SHAPE_T::BEZIER:
945 return getBoundingBox().Centre();
946
947 default:
949 return VECTOR2I();
950 }
951}
952
953
954void EDA_SHAPE::SetCenter( const VECTOR2I& aCenter )
955{
956 switch( m_shape )
957 {
958 case SHAPE_T::ARC:
959 m_arcCenter = aCenter;
960 break;
961
962 case SHAPE_T::CIRCLE:
963 m_start = aCenter;
964 m_hatchingDirty = true;
965 break;
966
967 default:
969 }
970}
971
972
974{
975 // If none of the input data have changed since we loaded the arc,
976 // keep the original mid point data to minimize churn
979 return m_arcMidData.mid;
980
981 VECTOR2I mid = m_start;
982 RotatePoint( mid, m_arcCenter, -GetArcAngle() / 2.0 );
983 return mid;
984}
985
986
987void EDA_SHAPE::CalcArcAngles( EDA_ANGLE& aStartAngle, EDA_ANGLE& aEndAngle ) const
988{
989 VECTOR2D startRadial( GetStart() - getCenter() );
990 VECTOR2D endRadial( GetEnd() - getCenter() );
991
992 aStartAngle = EDA_ANGLE( startRadial );
993 aEndAngle = EDA_ANGLE( endRadial );
994
995 if( aEndAngle == aStartAngle )
996 aEndAngle = aStartAngle + ANGLE_360; // ring, not null
997
998 while( aEndAngle < aStartAngle )
999 aEndAngle += ANGLE_360;
1000}
1001
1002
1004{
1005 double radius = 0.0;
1006
1007 switch( m_shape )
1008 {
1009 case SHAPE_T::ARC:
1011 break;
1012
1013 case SHAPE_T::CIRCLE:
1015 break;
1016
1017 default:
1019 }
1020
1021 // don't allow degenerate circles/arcs
1022 return std::max( 1, KiROUND( radius ) );
1023}
1024
1025
1026void EDA_SHAPE::SetCachedArcData( const VECTOR2I& aStart, const VECTOR2I& aMid,
1027 const VECTOR2I& aEnd, const VECTOR2I& aCenter )
1028{
1029 m_arcMidData.start = aStart;
1030 m_arcMidData.end = aEnd;
1031 m_arcMidData.center = aCenter;
1032 m_arcMidData.mid = aMid;
1033}
1034
1035
1036void EDA_SHAPE::SetArcGeometry( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
1037{
1038 m_arcMidData = {};
1039 m_start = aStart;
1040 m_end = aEnd;
1041 m_arcCenter = CalcArcCenter( aStart, aMid, aEnd );
1042 VECTOR2I new_mid = GetArcMid();
1043
1044 m_endsSwapped = false;
1045
1046 // Watch the ordering here. GetArcMid above needs to be called prior to initializing the
1047 // m_arcMidData structure in order to ensure we get the calculated variant, not the cached
1048 SetCachedArcData( aStart, aMid, aEnd, m_arcCenter );
1049
1050 /*
1051 * If the input winding doesn't match our internal winding, the calculated midpoint will end
1052 * up on the other side of the arc. In this case, we need to flip the start/end points and
1053 * flag this change for the system.
1054 */
1055 VECTOR2D dist( new_mid - aMid );
1056 VECTOR2D dist2( new_mid - m_arcCenter );
1057
1058 if( dist.SquaredEuclideanNorm() > dist2.SquaredEuclideanNorm() )
1059 {
1060 std::swap( m_start, m_end );
1061 m_endsSwapped = true;
1062 }
1063}
1064
1065
1067{
1068 EDA_ANGLE angle( atan2( static_cast<double>( GetStart().y - GetEnd().y ),
1069 static_cast<double>( GetEnd().x - GetStart().x ) ), RADIANS_T );
1070
1071 return angle;
1072}
1073
1074
1076{
1077 EDA_ANGLE startAngle;
1078 EDA_ANGLE endAngle;
1079
1080 CalcArcAngles( startAngle, endAngle );
1081
1082 return endAngle - startAngle;
1083}
1084
1085
1087{
1088 if( m_shape == SHAPE_T::ARC )
1089 {
1090 VECTOR2D mid = GetArcMid();
1091
1092 double orient = ( mid.x - m_start.x ) * ( m_end.y - m_start.y )
1093 - ( mid.y - m_start.y ) * ( m_end.x - m_start.x );
1094
1095 return orient < 0;
1096 }
1097
1099 return false;
1100}
1101
1102
1103void EDA_SHAPE::SetArcAngleAndEnd( const EDA_ANGLE& aAngle, bool aCheckNegativeAngle )
1104{
1105 EDA_ANGLE angle( aAngle );
1106
1107 m_end = m_start;
1109
1110 if( aCheckNegativeAngle && aAngle < ANGLE_0 )
1111 {
1112 std::swap( m_start, m_end );
1113 m_endsSwapped = true;
1114 }
1115}
1116
1117
1119{
1120 if( IsProxyItem() )
1121 {
1122 switch( m_shape )
1123 {
1124 case SHAPE_T::RECTANGLE: return _( "Pad Number Box" );
1125 case SHAPE_T::SEGMENT: return _( "Thermal Spoke Template" );
1126 default: return _( "Unrecognized" );
1127 }
1128 }
1129 else
1130 {
1131 switch( m_shape )
1132 {
1133 case SHAPE_T::CIRCLE: return _( "Circle" );
1134 case SHAPE_T::ARC: return _( "Arc" );
1135 case SHAPE_T::BEZIER: return _( "Curve" );
1136 case SHAPE_T::POLY: return _( "Polygon" );
1137 case SHAPE_T::RECTANGLE: return _( "Rectangle" );
1138 case SHAPE_T::SEGMENT: return _( "Segment" );
1139 default: return _( "Unrecognized" );
1140 }
1141 }
1142}
1143
1144
1145void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
1146{
1147 wxString msg;
1148
1149 wxString shape = _( "Shape" );
1150 aList.emplace_back( shape, getFriendlyName() );
1151
1152 switch( m_shape )
1153 {
1154 case SHAPE_T::CIRCLE:
1155 aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
1156 break;
1157
1158 case SHAPE_T::ARC:
1159 aList.emplace_back( _( "Length" ), aFrame->MessageTextFromValue( GetLength() ) );
1160
1162 aList.emplace_back( _( "Angle" ), msg );
1163
1164 aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
1165 break;
1166
1167 case SHAPE_T::BEZIER:
1168 aList.emplace_back( _( "Length" ), aFrame->MessageTextFromValue( GetLength() ) );
1169 break;
1170
1171 case SHAPE_T::POLY:
1172 msg.Printf( wxS( "%d" ), GetPolyShape().Outline(0).PointCount() );
1173 aList.emplace_back( _( "Points" ), msg );
1174 break;
1175
1176 case SHAPE_T::RECTANGLE:
1177 aList.emplace_back( _( "Width" ),
1178 aFrame->MessageTextFromValue( std::abs( GetEnd().x - GetStart().x ) ) );
1179
1180 aList.emplace_back( _( "Height" ),
1181 aFrame->MessageTextFromValue( std::abs( GetEnd().y - GetStart().y ) ) );
1182 break;
1183
1184 case SHAPE_T::SEGMENT:
1185 {
1186 aList.emplace_back( _( "Length" ),
1187 aFrame->MessageTextFromValue( GetStart().Distance( GetEnd() ) ));
1188
1189 // angle counter-clockwise from 3'o-clock
1190 EDA_ANGLE angle( atan2( (double)( GetStart().y - GetEnd().y ),
1191 (double)( GetEnd().x - GetStart().x ) ), RADIANS_T );
1192 aList.emplace_back( _( "Angle" ), EDA_UNIT_UTILS::UI::MessageTextFromValue( angle ) );
1193 break;
1194 }
1195
1196 default:
1197 break;
1198 }
1199
1200 m_stroke.GetMsgPanelInfo( aFrame, aList );
1201}
1202
1203
1205{
1206 BOX2I bbox;
1207
1208 switch( m_shape )
1209 {
1210 case SHAPE_T::RECTANGLE:
1211 for( VECTOR2I& pt : GetRectCorners() )
1212 bbox.Merge( pt );
1213
1214 break;
1215
1216 case SHAPE_T::SEGMENT:
1217 bbox.SetOrigin( GetStart() );
1218 bbox.SetEnd( GetEnd() );
1219 break;
1220
1221 case SHAPE_T::CIRCLE:
1222 bbox.SetOrigin( GetStart() );
1223 bbox.Inflate( GetRadius() );
1224 break;
1225
1226 case SHAPE_T::ARC:
1227 computeArcBBox( bbox );
1228 break;
1229
1230 case SHAPE_T::POLY:
1231 if( m_poly.IsEmpty() )
1232 break;
1233
1234 for( auto iter = m_poly.CIterate(); iter; iter++ )
1235 bbox.Merge( *iter );
1236
1237 break;
1238
1239 case SHAPE_T::BEZIER:
1240 // Bezier BBoxes are not trivial to compute, so we approximate it by
1241 // using the bounding box of the curve (not control!) points.
1242 for( const VECTOR2I& pt : m_bezierPoints )
1243 bbox.Merge( pt );
1244 break;
1245
1246 default:
1248 break;
1249 }
1250
1251 bbox.Inflate( std::max( 0, GetWidth() ) / 2 );
1252 bbox.Normalize();
1253
1254 return bbox;
1255}
1256
1257
1258bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
1259{
1260 double maxdist = aAccuracy;
1261
1262 if( GetWidth() > 0 )
1263 maxdist += GetWidth() / 2.0;
1264
1265 switch( m_shape )
1266 {
1267 case SHAPE_T::CIRCLE:
1268 {
1269 double radius = GetRadius();
1270 double dist = aPosition.Distance( getCenter() );
1271
1272 if( IsFilledForHitTesting() )
1273 return dist <= radius + maxdist; // Filled circle hit-test
1274 else if( abs( radius - dist ) <= maxdist ) // Ring hit-test
1275 return true;
1276
1277 if( IsHatchedFill() && GetHatching().Collide( aPosition, maxdist ) )
1278 return true;
1279
1280 return false;
1281 }
1282
1283 case SHAPE_T::ARC:
1284 {
1285 if( aPosition.Distance( m_start ) <= maxdist )
1286 return true;
1287
1288 if( aPosition.Distance( m_end ) <= maxdist )
1289 return true;
1290
1291 double radius = GetRadius();
1292 VECTOR2D relPos( VECTOR2D( aPosition ) - getCenter() );
1293 double dist = relPos.EuclideanNorm();
1294
1295 if( IsFilledForHitTesting() )
1296 {
1297 // Check distance from arc center
1298 if( dist > radius + maxdist )
1299 return false;
1300 }
1301 else
1302 {
1303 // Check distance from arc circumference
1304 if( abs( radius - dist ) > maxdist )
1305 return false;
1306 }
1307
1308 // Finally, check to see if it's within arc's swept angle.
1309 EDA_ANGLE startAngle;
1310 EDA_ANGLE endAngle;
1311 CalcArcAngles( startAngle, endAngle );
1312
1313 EDA_ANGLE relPosAngle( relPos );
1314
1315 startAngle.Normalize();
1316 endAngle.Normalize();
1317 relPosAngle.Normalize();
1318
1319 if( endAngle > startAngle )
1320 return relPosAngle >= startAngle && relPosAngle <= endAngle;
1321 else
1322 return relPosAngle >= startAngle || relPosAngle <= endAngle;
1323 }
1324
1325 case SHAPE_T::BEZIER:
1326 {
1327 const std::vector<VECTOR2I>* pts = &m_bezierPoints;
1328 std::vector<VECTOR2I> updatedBezierPoints;
1329
1330 if( m_bezierPoints.empty() )
1331 {
1333 converter.GetPoly( updatedBezierPoints, aAccuracy / 2 );
1334 pts = &updatedBezierPoints;
1335 }
1336
1337 for( unsigned int i = 1; i < pts->size(); i++ )
1338 {
1339 if( TestSegmentHit( aPosition, ( *pts )[i - 1], ( *pts )[i], maxdist ) )
1340 return true;
1341 }
1342
1343 return false;
1344 }
1345 case SHAPE_T::SEGMENT:
1346 return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
1347
1348 case SHAPE_T::RECTANGLE:
1349 if( IsProxyItem() || IsFilledForHitTesting() ) // Filled rect hit-test
1350 {
1351 SHAPE_POLY_SET poly;
1352 poly.NewOutline();
1353
1354 for( const VECTOR2I& pt : GetRectCorners() )
1355 poly.Append( pt );
1356
1357 return poly.Collide( aPosition, maxdist );
1358 }
1359 else
1360 {
1361 std::vector<VECTOR2I> pts = GetRectCorners();
1362
1363 if( TestSegmentHit( aPosition, pts[0], pts[1], maxdist )
1364 || TestSegmentHit( aPosition, pts[1], pts[2], maxdist )
1365 || TestSegmentHit( aPosition, pts[2], pts[3], maxdist )
1366 || TestSegmentHit( aPosition, pts[3], pts[0], maxdist ) )
1367 {
1368 return true;
1369 }
1370
1371 if( IsHatchedFill() && GetHatching().Collide( aPosition, maxdist ) )
1372 return true;
1373
1374 return false;
1375 }
1376
1377 case SHAPE_T::POLY:
1378 if( IsFilledForHitTesting() )
1379 {
1380 if( !m_poly.COutline( 0 ).IsClosed() )
1381 {
1382 // Only one outline is expected
1384 copy.SetClosed( true );
1385 return copy.Collide( aPosition, maxdist );
1386 }
1387 else
1388 {
1389 return m_poly.Collide( aPosition, maxdist );
1390 }
1391 }
1392 else
1393 {
1394 if( m_poly.CollideEdge( aPosition, nullptr, maxdist ) )
1395 return true;
1396
1397 if( IsHatchedFill() && GetHatching().Collide( aPosition, maxdist ) )
1398 return true;
1399
1400 return false;
1401 }
1402
1403 default:
1405 return false;
1406 }
1407}
1408
1409
1410bool EDA_SHAPE::hitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
1411{
1412 BOX2I arect = aRect;
1413 arect.Normalize();
1414 arect.Inflate( aAccuracy );
1415
1416 BOX2I bbox = getBoundingBox();
1417
1418 switch( m_shape )
1419 {
1420 case SHAPE_T::CIRCLE:
1421 // Test if area intersects or contains the circle:
1422 if( aContained )
1423 {
1424 return arect.Contains( bbox );
1425 }
1426 else
1427 {
1428 // If the rectangle does not intersect the bounding box, this is a much quicker test
1429 if( !arect.Intersects( bbox ) )
1430 return false;
1431 else
1432 return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
1433 }
1434
1435 case SHAPE_T::ARC:
1436 // Test for full containment of this arc in the rect
1437 if( aContained )
1438 {
1439 return arect.Contains( bbox );
1440 }
1441 // Test if the rect crosses the arc
1442 else
1443 {
1444 if( !arect.Intersects( bbox ) )
1445 return false;
1446
1447 if( IsAnyFill() )
1448 {
1449 return ( arect.Intersects( getCenter(), GetStart() )
1450 || arect.Intersects( getCenter(), GetEnd() )
1451 || arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() ) );
1452 }
1453 else
1454 {
1455 return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
1456 }
1457 }
1458
1459 case SHAPE_T::RECTANGLE:
1460 if( aContained )
1461 {
1462 return arect.Contains( bbox );
1463 }
1464 else
1465 {
1466 std::vector<VECTOR2I> pts = GetRectCorners();
1467
1468 // Account for the width of the lines
1469 arect.Inflate( GetWidth() / 2 );
1470 return ( arect.Intersects( pts[0], pts[1] )
1471 || arect.Intersects( pts[1], pts[2] )
1472 || arect.Intersects( pts[2], pts[3] )
1473 || arect.Intersects( pts[3], pts[0] ) );
1474 }
1475
1476 case SHAPE_T::SEGMENT:
1477 if( aContained )
1478 {
1479 return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
1480 }
1481 else
1482 {
1483 // Account for the width of the line
1484 arect.Inflate( GetWidth() / 2 );
1485 return arect.Intersects( GetStart(), GetEnd() );
1486 }
1487
1488 case SHAPE_T::POLY:
1489 if( aContained )
1490 {
1491 return arect.Contains( bbox );
1492 }
1493 else
1494 {
1495 // Fast test: if aRect is outside the polygon bounding box,
1496 // rectangles cannot intersect
1497 if( !arect.Intersects( bbox ) )
1498 return false;
1499
1500 // Account for the width of the line
1501 arect.Inflate( GetWidth() / 2 );
1502
1503 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1504 {
1505 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
1506 int count = poly.GetPointCount();
1507
1508 for( int jj = 0; jj < count; jj++ )
1509 {
1510 VECTOR2I vertex = poly.GetPoint( jj );
1511
1512 // Test if the point is within aRect
1513 if( arect.Contains( vertex ) )
1514 return true;
1515
1516 if( jj + 1 < count )
1517 {
1518 VECTOR2I vertexNext = poly.GetPoint( jj + 1 );
1519
1520 // Test if this edge intersects aRect
1521 if( arect.Intersects( vertex, vertexNext ) )
1522 return true;
1523 }
1524 else if( poly.IsClosed() )
1525 {
1526 VECTOR2I vertexNext = poly.GetPoint( 0 );
1527
1528 // Test if this edge intersects aRect
1529 if( arect.Intersects( vertex, vertexNext ) )
1530 return true;
1531 }
1532 }
1533 }
1534
1535 return false;
1536 }
1537
1538 case SHAPE_T::BEZIER:
1539 if( aContained )
1540 {
1541 return arect.Contains( bbox );
1542 }
1543 else
1544 {
1545 // Fast test: if aRect is outside the polygon bounding box,
1546 // rectangles cannot intersect
1547 if( !arect.Intersects( bbox ) )
1548 return false;
1549
1550 // Account for the width of the line
1551 arect.Inflate( GetWidth() / 2 );
1552 const std::vector<VECTOR2I>* pts = &m_bezierPoints;
1553 std::vector<VECTOR2I> updatedBezierPoints;
1554
1555 if( m_bezierPoints.empty() )
1556 {
1558 converter.GetPoly( updatedBezierPoints, aAccuracy / 2 );
1559 pts = &updatedBezierPoints;
1560 }
1561
1562 for( unsigned ii = 1; ii < pts->size(); ii++ )
1563 {
1564 VECTOR2I vertex = ( *pts )[ii - 1];
1565 VECTOR2I vertexNext = ( *pts )[ii];
1566
1567 // Test if the point is within aRect
1568 if( arect.Contains( vertex ) )
1569 return true;
1570
1571 // Test if this edge intersects aRect
1572 if( arect.Intersects( vertex, vertexNext ) )
1573 return true;
1574 }
1575
1576 return false;
1577 }
1578
1579 default:
1581 return false;
1582 }
1583}
1584
1585
1586std::vector<VECTOR2I> EDA_SHAPE::GetRectCorners() const
1587{
1588 std::vector<VECTOR2I> pts;
1589 VECTOR2I topLeft = GetStart();
1590 VECTOR2I botRight = GetEnd();
1591
1592 pts.emplace_back( topLeft );
1593 pts.emplace_back( botRight.x, topLeft.y );
1594 pts.emplace_back( botRight );
1595 pts.emplace_back( topLeft.x, botRight.y );
1596
1597 return pts;
1598}
1599
1600
1601std::vector<VECTOR2I> EDA_SHAPE::GetCornersInSequence( EDA_ANGLE angle ) const
1602{
1603 std::vector<VECTOR2I> pts;
1604
1605 angle.Normalize();
1606
1607 BOX2I bbox = getBoundingBox();
1608 bbox.Normalize();
1609
1610 if( angle.IsCardinal() )
1611 {
1612 if( angle == ANGLE_0 )
1613 {
1614 pts.emplace_back( VECTOR2I( bbox.GetLeft(), bbox.GetTop() ) );
1615 pts.emplace_back( VECTOR2I( bbox.GetRight(), bbox.GetTop() ) );
1616 pts.emplace_back( VECTOR2I( bbox.GetRight(), bbox.GetBottom() ) );
1617 pts.emplace_back( VECTOR2I( bbox.GetLeft(), bbox.GetBottom() ) );
1618 }
1619 else if( angle == ANGLE_90 )
1620 {
1621 pts.emplace_back( VECTOR2I( bbox.GetLeft(), bbox.GetBottom() ) );
1622 pts.emplace_back( VECTOR2I( bbox.GetLeft(), bbox.GetTop() ) );
1623 pts.emplace_back( VECTOR2I( bbox.GetRight(), bbox.GetTop() ) );
1624 pts.emplace_back( VECTOR2I( bbox.GetRight(), bbox.GetBottom() ) );
1625 }
1626 else if( angle == ANGLE_180 )
1627 {
1628 pts.emplace_back( VECTOR2I( bbox.GetRight(), bbox.GetBottom() ) );
1629 pts.emplace_back( VECTOR2I( bbox.GetLeft(), bbox.GetBottom() ) );
1630 pts.emplace_back( VECTOR2I( bbox.GetLeft(), bbox.GetTop() ) );
1631 pts.emplace_back( VECTOR2I( bbox.GetRight(), bbox.GetTop() ) );
1632 }
1633 else if( angle == ANGLE_270 )
1634 {
1635 pts.emplace_back( VECTOR2I( bbox.GetRight(), bbox.GetTop() ) );
1636 pts.emplace_back( VECTOR2I( bbox.GetRight(), bbox.GetBottom() ) );
1637 pts.emplace_back( VECTOR2I( bbox.GetLeft(), bbox.GetBottom() ) );
1638 pts.emplace_back( VECTOR2I( bbox.GetLeft(), bbox.GetTop() ) );
1639 }
1640 }
1641 else
1642 {
1643 // This function was originally located in pcb_textbox.cpp and was later moved to eda_shape.cpp.
1644 // As a result of this move, access to getCorners was lost, since it is defined in the PCB_SHAPE
1645 // class within pcb_shape.cpp and is not available in the current context.
1646 //
1647 // Additionally, GetRectCorners() cannot be used here, as it assumes the rectangle is rotated by
1648 // a cardinal angle. In non-cardinal cases, it returns incorrect values (e.g., (0, 0)).
1649 //
1650 // To address this, a portion of the getCorners implementation for SHAPE_T::POLY elements
1651 // has been replicated here to restore the correct behavior.
1652 std::vector<VECTOR2I> corners;
1653
1654 for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
1655 {
1656 for( const VECTOR2I& pt : GetPolyShape().Outline( ii ).CPoints() )
1657 corners.emplace_back( pt );
1658 }
1659
1660 while( corners.size() < 4 )
1661 corners.emplace_back( corners.back() + VECTOR2I( 10, 10 ) );
1662
1663 VECTOR2I minX = corners[0];
1664 VECTOR2I maxX = corners[0];
1665 VECTOR2I minY = corners[0];
1666 VECTOR2I maxY = corners[0];
1667
1668 for( const VECTOR2I& corner : corners )
1669 {
1670 if( corner.x < minX.x )
1671 minX = corner;
1672
1673 if( corner.x > maxX.x )
1674 maxX = corner;
1675
1676 if( corner.y < minY.y )
1677 minY = corner;
1678
1679 if( corner.y > maxY.y )
1680 maxY = corner;
1681 }
1682
1683 if( angle < ANGLE_90 )
1684 {
1685 pts.emplace_back( minX );
1686 pts.emplace_back( minY );
1687 pts.emplace_back( maxX );
1688 pts.emplace_back( maxY );
1689 }
1690 else if( angle < ANGLE_180 )
1691 {
1692 pts.emplace_back( maxY );
1693 pts.emplace_back( minX );
1694 pts.emplace_back( minY );
1695 pts.emplace_back( maxX );
1696 }
1697 else if( angle < ANGLE_270 )
1698 {
1699 pts.emplace_back( maxX );
1700 pts.emplace_back( maxY );
1701 pts.emplace_back( minX );
1702 pts.emplace_back( minY );
1703 }
1704 else
1705 {
1706 pts.emplace_back( minY );
1707 pts.emplace_back( maxX );
1708 pts.emplace_back( maxY );
1709 pts.emplace_back( minX );
1710 }
1711 }
1712
1713 return pts;
1714}
1715
1716
1718{
1719 // Start, end, and each inflection point the arc crosses will enclose the entire arc.
1720 // Only include the center when filled; it's not necessarily inside the BB of an unfilled
1721 // arc with a small included angle.
1722 aBBox.SetOrigin( m_start );
1723 aBBox.Merge( m_end );
1724
1725 if( IsAnyFill() )
1726 aBBox.Merge( m_arcCenter );
1727
1728 int radius = GetRadius();
1729 EDA_ANGLE t1, t2;
1730
1731 CalcArcAngles( t1, t2 );
1732
1733 t1.Normalize();
1734 t2.Normalize();
1735
1736 if( t2 > t1 )
1737 {
1738 if( t1 < ANGLE_0 && t2 > ANGLE_0 )
1739 aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
1740
1741 if( t1 < ANGLE_90 && t2 > ANGLE_90 )
1742 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
1743
1744 if( t1 < ANGLE_180 && t2 > ANGLE_180 )
1745 aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
1746
1747 if( t1 < ANGLE_270 && t2 > ANGLE_270 )
1748 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
1749 }
1750 else
1751 {
1752 if( t1 < ANGLE_0 || t2 > ANGLE_0 )
1753 aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
1754
1755 if( t1 < ANGLE_90 || t2 > ANGLE_90 )
1756 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
1757
1758 if( t1 < ANGLE_180 || t2 > ANGLE_180 )
1759 aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
1760
1761 if( t1 < ANGLE_270 || t2 > ANGLE_270 )
1762 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
1763 }
1764}
1765
1766
1767void EDA_SHAPE::SetPolyPoints( const std::vector<VECTOR2I>& aPoints )
1768{
1771
1772 for( const VECTOR2I& p : aPoints )
1773 m_poly.Append( p.x, p.y );
1774}
1775
1776
1777std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineChainOnly ) const
1778{
1779 std::vector<SHAPE*> effectiveShapes;
1780 int width = GetEffectiveWidth();
1781 bool solidFill = ( IsSolidFill() || IsHatchedFill() || IsProxyItem() ) && !aEdgeOnly;
1782
1783 switch( m_shape )
1784 {
1785 case SHAPE_T::ARC:
1786 effectiveShapes.emplace_back( new SHAPE_ARC( m_arcCenter, m_start, GetArcAngle(), width ) );
1787 break;
1788
1789 case SHAPE_T::SEGMENT:
1790 effectiveShapes.emplace_back( new SHAPE_SEGMENT( m_start, m_end, width ) );
1791 break;
1792
1793 case SHAPE_T::RECTANGLE:
1794 {
1795 std::vector<VECTOR2I> pts = GetRectCorners();
1796
1797 if( solidFill )
1798 effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
1799
1800 if( width > 0 || !solidFill )
1801 {
1802 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], width ) );
1803 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], width ) );
1804 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], width ) );
1805 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], width ) );
1806 }
1807 break;
1808 }
1809
1810 case SHAPE_T::CIRCLE:
1811 {
1812 if( solidFill )
1813 effectiveShapes.emplace_back( new SHAPE_CIRCLE( getCenter(), GetRadius() ) );
1814
1815 if( width > 0 || !solidFill )
1816 effectiveShapes.emplace_back( new SHAPE_ARC( getCenter(), GetEnd(), ANGLE_360, width ) );
1817
1818 break;
1819 }
1820
1821 case SHAPE_T::BEZIER:
1822 {
1823 std::vector<VECTOR2I> bezierPoints = buildBezierToSegmentsPointsList( width / 2 );
1824 VECTOR2I start_pt = bezierPoints[0];
1825
1826 for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ )
1827 {
1828 VECTOR2I end_pt = bezierPoints[jj];
1829 effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, width ) );
1830 start_pt = end_pt;
1831 }
1832
1833 break;
1834 }
1835
1836 case SHAPE_T::POLY:
1837 {
1838 if( GetPolyShape().OutlineCount() == 0 ) // malformed/empty polygon
1839 break;
1840
1841 for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
1842 {
1843 const SHAPE_LINE_CHAIN& l = GetPolyShape().COutline( ii );
1844
1845 if( solidFill )
1846 effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) );
1847
1848 if( width > 0 || !IsSolidFill() || aEdgeOnly )
1849 {
1850 int segCount = l.SegmentCount();
1851
1852 if( aLineChainOnly && l.IsClosed() )
1853 segCount--; // Treat closed chain as open
1854
1855 for( int jj = 0; jj < segCount; jj++ )
1856 effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.CSegment( jj ), width ) );
1857 }
1858 }
1859 }
1860 break;
1861
1862 default:
1864 break;
1865 }
1866
1867 return effectiveShapes;
1868}
1869
1870
1871void EDA_SHAPE::DupPolyPointsList( std::vector<VECTOR2I>& aBuffer ) const
1872{
1873 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1874 {
1875 int pointCount = m_poly.COutline( ii ).PointCount();
1876
1877 if( pointCount )
1878 {
1879 aBuffer.reserve( pointCount );
1880
1881 for ( auto iter = m_poly.CIterate(); iter; iter++ )
1882 aBuffer.emplace_back( iter->x, iter->y );
1883 }
1884 }
1885}
1886
1887
1889{
1890 // return true if the polygonal shape is valid (has more than 2 points)
1891 return GetPolyShape().OutlineCount() > 0 && GetPolyShape().Outline( 0 ).PointCount() > 2;
1892}
1893
1894
1896{
1897 // return the number of corners of the polygonal shape
1898 // this shape is expected to be only one polygon without hole
1899 return GetPolyShape().OutlineCount() ? GetPolyShape().VertexCount( 0 ) : 0;
1900}
1901
1902
1903void EDA_SHAPE::beginEdit( const VECTOR2I& aPosition )
1904{
1905 switch( GetShape() )
1906 {
1907 case SHAPE_T::SEGMENT:
1908 case SHAPE_T::CIRCLE:
1909 case SHAPE_T::RECTANGLE:
1910 SetStart( aPosition );
1911 SetEnd( aPosition );
1912 break;
1913
1914 case SHAPE_T::ARC:
1915 SetArcGeometry( aPosition, aPosition, aPosition );
1916 m_editState = 1;
1917 break;
1918
1919 case SHAPE_T::BEZIER:
1920 SetStart( aPosition );
1921 SetEnd( aPosition );
1922 SetBezierC1( aPosition );
1923 SetBezierC2( aPosition );
1924 m_editState = 1;
1925
1927 break;
1928
1929 case SHAPE_T::POLY:
1931 m_poly.Outline( 0 ).SetClosed( false );
1932
1933 // Start and end of the first segment (co-located for now)
1934 m_poly.Outline( 0 ).Append( aPosition );
1935 m_poly.Outline( 0 ).Append( aPosition, true );
1936 break;
1937
1938 default:
1940 }
1941}
1942
1943
1944bool EDA_SHAPE::continueEdit( const VECTOR2I& aPosition )
1945{
1946 switch( GetShape() )
1947 {
1948 case SHAPE_T::ARC:
1949 case SHAPE_T::SEGMENT:
1950 case SHAPE_T::CIRCLE:
1951 case SHAPE_T::RECTANGLE:
1952 return false;
1953
1954 case SHAPE_T::BEZIER:
1955 if( m_editState == 3 )
1956 return false;
1957
1958 m_editState++;
1959 return true;
1960
1961 case SHAPE_T::POLY:
1962 {
1963 SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1964
1965 // do not add zero-length segments
1966 if( poly.CPoint( poly.GetPointCount() - 2 ) != poly.CLastPoint() )
1967 poly.Append( aPosition, true );
1968 }
1969 return true;
1970
1971 default:
1973 return false;
1974 }
1975}
1976
1977
1978void EDA_SHAPE::calcEdit( const VECTOR2I& aPosition )
1979{
1980#define sq( x ) pow( x, 2 )
1981
1982 switch( GetShape() )
1983 {
1984 case SHAPE_T::SEGMENT:
1985 case SHAPE_T::CIRCLE:
1986 case SHAPE_T::RECTANGLE:
1987 SetEnd( aPosition );
1988 break;
1989
1990 case SHAPE_T::BEZIER:
1991 {
1992 switch( m_editState )
1993 {
1994 case 0:
1995 SetStart( aPosition );
1996 SetEnd( aPosition );
1997 SetBezierC1( aPosition );
1998 SetBezierC2( aPosition );
1999 break;
2000 case 1:
2001 SetBezierC2( aPosition );
2002 SetEnd( aPosition );
2003 break;
2004 case 2: SetBezierC1( aPosition ); break;
2005 case 3: SetBezierC2( aPosition ); break;
2006 }
2007
2009 }
2010 break;
2011
2012 case SHAPE_T::ARC:
2013 {
2014 double radius = GetRadius();
2015 EDA_ANGLE lastAngle = GetArcAngle();
2016
2017 // Edit state 0: drawing: place start
2018 // Edit state 1: drawing: place end (center calculated for 90-degree subtended angle)
2019 // Edit state 2: point edit: move start (center calculated for invariant subtended angle)
2020 // Edit state 3: point edit: move end (center calculated for invariant subtended angle)
2021 // Edit state 4: point edit: move center
2022 // Edit state 5: point edit: move arc-mid-point
2023
2024 switch( m_editState )
2025 {
2026 case 0:
2027 SetArcGeometry( aPosition, aPosition, aPosition );
2028 return;
2029
2030 case 1:
2031 m_end = aPosition;
2032 radius = m_start.Distance( m_end ) * M_SQRT1_2;
2033 break;
2034
2035 case 2:
2036 case 3:
2037 {
2038 VECTOR2I v = m_start - m_end;
2039 double chordBefore = v.SquaredEuclideanNorm();
2040
2041 if( m_editState == 2 )
2042 m_start = aPosition;
2043 else
2044 m_end = aPosition;
2045
2046 v = m_start - m_end;
2047
2048 double chordAfter = v.SquaredEuclideanNorm();
2049 double ratio = 0.0;
2050
2051 if( chordBefore > 0 )
2052 ratio = chordAfter / chordBefore;
2053
2054 if( ratio != 0 )
2055 radius = std::max( sqrt( sq( radius ) * ratio ), sqrt( chordAfter ) / 2 );
2056 }
2057 break;
2058
2059 case 4:
2060 {
2061 double radialA = m_start.Distance( aPosition );
2062 double radialB = m_end.Distance( aPosition );
2063 radius = ( radialA + radialB ) / 2.0;
2064 }
2065 break;
2066
2067 case 5:
2068 SetArcGeometry( GetStart(), aPosition, GetEnd() );
2069 return;
2070 }
2071
2072 // Calculate center based on start, end, and radius
2073 //
2074 // Let 'l' be the length of the chord and 'm' the middle point of the chord
2075 double l = m_start.Distance( m_end );
2076 VECTOR2D m = ( m_start + m_end ) / 2;
2077 double sqRadDiff = ( radius * radius ) - ( l * l ) / 4.0;
2078
2079 // Calculate 'd', the vector from the chord midpoint to the center
2080 VECTOR2D d;
2081
2082 if( l > 0 && sqRadDiff >= 0 )
2083 {
2084 d.x = sqrt( sqRadDiff ) * ( m_start.y - m_end.y ) / l;
2085 d.y = sqrt( sqRadDiff ) * ( m_end.x - m_start.x ) / l;
2086 }
2087
2088 VECTOR2I c1 = KiROUND( m + d );
2089 VECTOR2I c2 = KiROUND( m - d );
2090
2091 // Solution gives us 2 centers; we need to pick one:
2092 switch( m_editState )
2093 {
2094 case 1:
2095 // Keep arc clockwise while drawing i.e. arc angle = 90 deg.
2096 // it can be 90 or 270 deg depending on the arc center choice (c1 or c2)
2097 m_arcCenter = c1; // first trial
2098
2099 if( GetArcAngle() > ANGLE_180 )
2100 m_arcCenter = c2;
2101
2102 break;
2103
2104 case 2:
2105 case 3:
2106 // Pick the one of c1, c2 to keep arc on the same side
2107 m_arcCenter = c1; // first trial
2108
2109 if( ( lastAngle < ANGLE_180 ) != ( GetArcAngle() < ANGLE_180 ) )
2110 m_arcCenter = c2;
2111
2112 break;
2113
2114 case 4:
2115 // Pick the one closer to the mouse position
2116 m_arcCenter = c1.Distance( aPosition ) < c2.Distance( aPosition ) ? c1 : c2;
2117 break;
2118 }
2119 }
2120 break;
2121
2122 case SHAPE_T::POLY:
2123 m_poly.Outline( 0 ).SetPoint( m_poly.Outline( 0 ).GetPointCount() - 1, aPosition );
2124 break;
2125
2126 default:
2128 }
2129}
2130
2131
2132void EDA_SHAPE::endEdit( bool aClosed )
2133{
2134 switch( GetShape() )
2135 {
2136 case SHAPE_T::ARC:
2137 case SHAPE_T::SEGMENT:
2138 case SHAPE_T::CIRCLE:
2139 case SHAPE_T::RECTANGLE:
2140 case SHAPE_T::BEZIER:
2141 break;
2142
2143 case SHAPE_T::POLY:
2144 {
2145 SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
2146
2147 // do not include last point twice
2148 if( poly.GetPointCount() > 2 )
2149 {
2150 if( poly.CPoint( poly.GetPointCount() - 2 ) == poly.CLastPoint() )
2151 {
2152 poly.SetClosed( aClosed );
2153 }
2154 else
2155 {
2156 poly.SetClosed( false );
2157 poly.Remove( poly.GetPointCount() - 1 );
2158 }
2159 }
2160 }
2161 break;
2162
2163 default:
2165 }
2166}
2167
2168
2170{
2171 EDA_SHAPE* image = dynamic_cast<EDA_SHAPE*>( aImage );
2172 assert( image );
2173
2174 #define SWAPITEM( x ) std::swap( x, image->x )
2175 SWAPITEM( m_stroke );
2176 SWAPITEM( m_start );
2177 SWAPITEM( m_end );
2179 SWAPITEM( m_shape );
2183 SWAPITEM( m_poly );
2184 SWAPITEM( m_fill );
2188 #undef SWAPITEM
2189
2190 m_hatchingDirty = true;
2191}
2192
2193
2194int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const
2195{
2196#define EPSILON 2 // Should be enough for rounding errors on calculated items
2197
2198#define TEST( a, b ) { if( a != b ) return a - b; }
2199#define TEST_E( a, b ) { if( abs( a - b ) > EPSILON ) return a - b; }
2200#define TEST_PT( a, b ) { TEST_E( a.x, b.x ); TEST_E( a.y, b.y ); }
2201
2202 TEST_PT( m_start, aOther->m_start );
2203 TEST_PT( m_end, aOther->m_end );
2204
2205 TEST( (int) m_shape, (int) aOther->m_shape );
2206
2207 if( m_shape == SHAPE_T::ARC )
2208 {
2209 TEST_PT( GetArcMid(), aOther->GetArcMid() );
2210 }
2211 else if( m_shape == SHAPE_T::BEZIER )
2212 {
2213 TEST_PT( m_bezierC1, aOther->m_bezierC1 );
2214 TEST_PT( m_bezierC2, aOther->m_bezierC2 );
2215 }
2216 else if( m_shape == SHAPE_T::POLY )
2217 {
2219 }
2220
2221 for( size_t ii = 0; ii < m_bezierPoints.size(); ++ii )
2222 TEST_PT( m_bezierPoints[ii], aOther->m_bezierPoints[ii] );
2223
2224 for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
2225 TEST_PT( m_poly.CVertex( ii ), aOther->m_poly.CVertex( ii ) );
2226
2227 TEST_E( m_stroke.GetWidth(), aOther->m_stroke.GetWidth() );
2228 TEST( (int) m_stroke.GetLineStyle(), (int) aOther->m_stroke.GetLineStyle() );
2229 TEST( (int) m_fill, (int) aOther->m_fill );
2230
2231 return 0;
2232}
2233
2234
2235void EDA_SHAPE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, int aClearance, int aError,
2236 ERROR_LOC aErrorLoc, bool ignoreLineWidth,
2237 bool includeFill ) const
2238{
2239 bool solidFill = IsSolidFill() || ( IsHatchedFill() && !includeFill ) || IsProxyItem();
2240 int width = ignoreLineWidth ? 0 : GetWidth();
2241
2242 width += 2 * aClearance;
2243
2244 switch( m_shape )
2245 {
2246 case SHAPE_T::CIRCLE:
2247 {
2248 int r = GetRadius();
2249
2250 if( solidFill )
2251 TransformCircleToPolygon( aBuffer, getCenter(), r + width / 2, aError, aErrorLoc );
2252 else
2253 TransformRingToPolygon( aBuffer, getCenter(), r, width, aError, aErrorLoc );
2254
2255 break;
2256 }
2257
2258 case SHAPE_T::RECTANGLE:
2259 {
2260 std::vector<VECTOR2I> pts = GetRectCorners();
2261
2262 if( solidFill )
2263 {
2264 aBuffer.NewOutline();
2265
2266 for( const VECTOR2I& pt : pts )
2267 aBuffer.Append( pt );
2268 }
2269
2270 if( width > 0 || !solidFill )
2271 {
2272 // Add in segments
2273 TransformOvalToPolygon( aBuffer, pts[0], pts[1], width, aError, aErrorLoc );
2274 TransformOvalToPolygon( aBuffer, pts[1], pts[2], width, aError, aErrorLoc );
2275 TransformOvalToPolygon( aBuffer, pts[2], pts[3], width, aError, aErrorLoc );
2276 TransformOvalToPolygon( aBuffer, pts[3], pts[0], width, aError, aErrorLoc );
2277 }
2278
2279 break;
2280 }
2281
2282 case SHAPE_T::ARC:
2283 TransformArcToPolygon( aBuffer, GetStart(), GetArcMid(), GetEnd(), width, aError, aErrorLoc );
2284 break;
2285
2286 case SHAPE_T::SEGMENT:
2287 TransformOvalToPolygon( aBuffer, GetStart(), GetEnd(), width, aError, aErrorLoc );
2288 break;
2289
2290 case SHAPE_T::POLY:
2291 {
2292 if( !IsPolyShapeValid() )
2293 break;
2294
2295 if( solidFill )
2296 {
2297 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
2298 {
2299 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
2300 SHAPE_POLY_SET tmp;
2301 tmp.NewOutline();
2302
2303 for( int jj = 0; jj < (int) poly.GetPointCount(); ++jj )
2304 tmp.Append( poly.GetPoint( jj ) );
2305
2306 if( width > 0 )
2307 {
2308 int inflate = width / 2;
2309
2310 if( aErrorLoc == ERROR_OUTSIDE )
2311 inflate += aError;
2312
2313 tmp.Inflate( inflate, CORNER_STRATEGY::ROUND_ALL_CORNERS, aError );
2314 }
2315
2316 aBuffer.Append( tmp );
2317 }
2318 }
2319 else
2320 {
2321 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
2322 {
2323 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
2324
2325 for( int jj = 0; jj < (int) poly.SegmentCount(); ++jj )
2326 {
2327 const SEG& seg = poly.GetSegment( jj );
2328 TransformOvalToPolygon( aBuffer, seg.A, seg.B, width, aError, aErrorLoc );
2329 }
2330 }
2331 }
2332
2333 break;
2334 }
2335
2336 case SHAPE_T::BEZIER:
2337 {
2338 std::vector<VECTOR2I> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
2339 BEZIER_POLY converter( ctrlPts );
2340 std::vector<VECTOR2I> poly;
2341 converter.GetPoly( poly, aError );
2342
2343 for( unsigned ii = 1; ii < poly.size(); ii++ )
2344 TransformOvalToPolygon( aBuffer, poly[ii - 1], poly[ii], width, aError, aErrorLoc );
2345
2346 break;
2347 }
2348
2349 default:
2351 break;
2352 }
2353
2354 if( IsHatchedFill() && includeFill )
2355 {
2356 for( int ii = 0; ii < GetHatching().OutlineCount(); ++ii )
2357 aBuffer.AddOutline( GetHatching().COutline( ii ) );
2358 }
2359}
2360
2361
2362void EDA_SHAPE::SetWidth( int aWidth )
2363{
2364 m_stroke.SetWidth( aWidth );
2365 m_hatchingDirty = true;
2366}
2367
2368
2370{
2371 m_stroke.SetLineStyle( aStyle );
2372}
2373
2374
2376{
2377 if( m_stroke.GetLineStyle() != LINE_STYLE::DEFAULT )
2378 return m_stroke.GetLineStyle();
2379
2380 return LINE_STYLE::SOLID;
2381}
2382
2383
2384bool EDA_SHAPE::operator==( const EDA_SHAPE& aOther ) const
2385{
2386 if( GetShape() != aOther.GetShape() )
2387 return false;
2388
2389 if( m_fill != aOther.m_fill )
2390 return false;
2391
2392 if( m_stroke.GetWidth() != aOther.m_stroke.GetWidth() )
2393 return false;
2394
2395 if( m_stroke.GetLineStyle() != aOther.m_stroke.GetLineStyle() )
2396 return false;
2397
2398 if( m_fillColor != aOther.m_fillColor )
2399 return false;
2400
2401 if( m_start != aOther.m_start )
2402 return false;
2403
2404 if( m_end != aOther.m_end )
2405 return false;
2406
2407 if( m_arcCenter != aOther.m_arcCenter )
2408 return false;
2409
2410 if( m_bezierC1 != aOther.m_bezierC1 )
2411 return false;
2412
2413 if( m_bezierC2 != aOther.m_bezierC2 )
2414 return false;
2415
2416 if( m_bezierPoints != aOther.m_bezierPoints )
2417 return false;
2418
2419 for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
2420 {
2421 if( m_poly.CVertex( ii ) != aOther.m_poly.CVertex( ii ) )
2422 return false;
2423 }
2424
2425 return true;
2426}
2427
2428
2429double EDA_SHAPE::Similarity( const EDA_SHAPE& aOther ) const
2430{
2431 if( GetShape() != aOther.GetShape() )
2432 return 0.0;
2433
2434 double similarity = 1.0;
2435
2436 if( m_fill != aOther.m_fill )
2437 similarity *= 0.9;
2438
2439 if( m_stroke.GetWidth() != aOther.m_stroke.GetWidth() )
2440 similarity *= 0.9;
2441
2442 if( m_stroke.GetLineStyle() != aOther.m_stroke.GetLineStyle() )
2443 similarity *= 0.9;
2444
2445 if( m_fillColor != aOther.m_fillColor )
2446 similarity *= 0.9;
2447
2448 if( m_start != aOther.m_start )
2449 similarity *= 0.9;
2450
2451 if( m_end != aOther.m_end )
2452 similarity *= 0.9;
2453
2454 if( m_arcCenter != aOther.m_arcCenter )
2455 similarity *= 0.9;
2456
2457 if( m_bezierC1 != aOther.m_bezierC1 )
2458 similarity *= 0.9;
2459
2460 if( m_bezierC2 != aOther.m_bezierC2 )
2461 similarity *= 0.9;
2462
2463 {
2464 int m = m_bezierPoints.size();
2465 int n = aOther.m_bezierPoints.size();
2466
2467 size_t longest = alg::longest_common_subset( m_bezierPoints, aOther.m_bezierPoints );
2468
2469 similarity *= std::pow( 0.9, m + n - 2 * longest );
2470 }
2471
2472 {
2473 int m = m_poly.TotalVertices();
2474 int n = aOther.m_poly.TotalVertices();
2475 std::vector<VECTOR2I> poly;
2476 std::vector<VECTOR2I> otherPoly;
2477 VECTOR2I lastPt( 0, 0 );
2478
2479 // We look for the longest common subset of the two polygons, but we need to
2480 // offset each point because we're actually looking for overall similarity, not just
2481 // exact matches. So if the zone is moved by 1IU, we only want one point to be
2482 // considered "moved" rather than the entire polygon. In this case, the first point
2483 // will not be a match but the rest of the sequence will.
2484 for( int ii = 0; ii < m; ++ii )
2485 {
2486 poly.emplace_back( lastPt - m_poly.CVertex( ii ) );
2487 lastPt = m_poly.CVertex( ii );
2488 }
2489
2490 lastPt = VECTOR2I( 0, 0 );
2491
2492 for( int ii = 0; ii < n; ++ii )
2493 {
2494 otherPoly.emplace_back( lastPt - aOther.m_poly.CVertex( ii ) );
2495 lastPt = aOther.m_poly.CVertex( ii );
2496 }
2497
2498 size_t longest = alg::longest_common_subset( poly, otherPoly );
2499
2500 similarity *= std::pow( 0.9, m + n - 2 * longest );
2501 }
2502
2503 return similarity;
2504}
2505
2506
2510
2511
2512static struct EDA_SHAPE_DESC
2513{
2515 {
2517 .Map( SHAPE_T::SEGMENT, _HKI( "Segment" ) )
2518 .Map( SHAPE_T::RECTANGLE, _HKI( "Rectangle" ) )
2519 .Map( SHAPE_T::ARC, _HKI( "Arc" ) )
2520 .Map( SHAPE_T::CIRCLE, _HKI( "Circle" ) )
2521 .Map( SHAPE_T::POLY, _HKI( "Polygon" ) )
2522 .Map( SHAPE_T::BEZIER, _HKI( "Bezier" ) );
2523
2525
2526 if( lineStyleEnum.Choices().GetCount() == 0 )
2527 {
2528 lineStyleEnum.Map( LINE_STYLE::SOLID, _HKI( "Solid" ) )
2529 .Map( LINE_STYLE::DASH, _HKI( "Dashed" ) )
2530 .Map( LINE_STYLE::DOT, _HKI( "Dotted" ) )
2531 .Map( LINE_STYLE::DASHDOT, _HKI( "Dash-Dot" ) )
2532 .Map( LINE_STYLE::DASHDOTDOT, _HKI( "Dash-Dot-Dot" ) );
2533 }
2534
2536
2537 if( hatchModeEnum.Choices().GetCount() == 0 )
2538 {
2539 hatchModeEnum.Map( UI_FILL_MODE::NONE, _HKI( "None" ) );
2540 hatchModeEnum.Map( UI_FILL_MODE::SOLID, _HKI( "Solid" ) );
2541 hatchModeEnum.Map( UI_FILL_MODE::HATCH, _HKI( "Hatch" ) );
2542 hatchModeEnum.Map( UI_FILL_MODE::REVERSE_HATCH, _HKI( "Reverse Hatch" ) );
2543 hatchModeEnum.Map( UI_FILL_MODE::CROSS_HATCH, _HKI( "Cross-hatch" ) );
2544 }
2545
2548
2549 auto isNotPolygonOrCircle =
2550 []( INSPECTABLE* aItem ) -> bool
2551 {
2552 // Polygons, unlike other shapes, have no meaningful start or end coordinates
2553 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2554 return shape->GetShape() != SHAPE_T::POLY && shape->GetShape() != SHAPE_T::CIRCLE;
2555
2556 return false;
2557 };
2558
2559 auto isCircle =
2560 []( INSPECTABLE* aItem ) -> bool
2561 {
2562 // Polygons, unlike other shapes, have no meaningful start or end coordinates
2563 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2564 return shape->GetShape() == SHAPE_T::CIRCLE;
2565
2566 return false;
2567 };
2568
2569 auto isRectangle =
2570 []( INSPECTABLE* aItem ) -> bool
2571 {
2572 // Polygons, unlike other shapes, have no meaningful start or end coordinates
2573 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2574 return shape->GetShape() == SHAPE_T::RECTANGLE;
2575
2576 return false;
2577 };
2578
2579 const wxString shapeProps = _HKI( "Shape Properties" );
2580
2581 auto shape = new PROPERTY_ENUM<EDA_SHAPE, SHAPE_T>( _HKI( "Shape" ),
2583 propMgr.AddProperty( shape, shapeProps );
2584
2585 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start X" ),
2588 shapeProps )
2589 .SetAvailableFunc( isNotPolygonOrCircle );
2590 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start Y" ),
2593 shapeProps )
2594 .SetAvailableFunc( isNotPolygonOrCircle );
2595
2596 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Center X" ),
2599 shapeProps )
2600 .SetAvailableFunc( isCircle );
2601
2602 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Center Y" ),
2605 shapeProps )
2606 .SetAvailableFunc( isCircle );
2607
2608 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Radius" ),
2611 shapeProps )
2612 .SetAvailableFunc( isCircle );
2613
2614 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End X" ),
2617 shapeProps )
2618 .SetAvailableFunc( isNotPolygonOrCircle );
2619
2620 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End Y" ),
2623 shapeProps )
2624 .SetAvailableFunc( isNotPolygonOrCircle );
2625
2626 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Width" ),
2629 shapeProps )
2630 .SetAvailableFunc( isRectangle );
2631
2632 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Height" ),
2635 shapeProps )
2636 .SetAvailableFunc( isRectangle );
2637
2638 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Line Width" ),
2640 shapeProps );
2641
2642 propMgr.AddProperty( new PROPERTY_ENUM<EDA_SHAPE, LINE_STYLE>( _HKI( "Line Style" ),
2644 shapeProps );
2645
2646 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, COLOR4D>( _HKI( "Line Color" ),
2648 shapeProps )
2650
2651 auto angle = new PROPERTY<EDA_SHAPE, EDA_ANGLE>( _HKI( "Angle" ),
2654 angle->SetAvailableFunc(
2655 [=]( INSPECTABLE* aItem ) -> bool
2656 {
2657 if( EDA_SHAPE* curr_shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2658 return curr_shape->GetShape() == SHAPE_T::ARC;
2659
2660 return false;
2661 } );
2662 propMgr.AddProperty( angle, shapeProps );
2663
2664 auto fillAvailable =
2665 [=]( INSPECTABLE* aItem ) -> bool
2666 {
2667 if( EDA_ITEM* edaItem = dynamic_cast<EDA_ITEM*>( aItem ) )
2668 {
2669 // For some reason masking "Filled" and "Fill Color" at the
2670 // PCB_TABLECELL level doesn't work.
2671 if( edaItem->Type() == PCB_TABLECELL_T )
2672 return false;
2673 }
2674
2675 if( EDA_SHAPE* edaShape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2676 {
2677 switch( edaShape->GetShape() )
2678 {
2679 case SHAPE_T::POLY:
2680 case SHAPE_T::RECTANGLE:
2681 case SHAPE_T::CIRCLE:
2682 case SHAPE_T::BEZIER:
2683 return true;
2684
2685 default:
2686 return false;
2687 }
2688 }
2689
2690 return false;
2691 };
2692
2695 shapeProps )
2696 .SetAvailableFunc( fillAvailable );
2697
2698 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, COLOR4D>( _HKI( "Fill Color" ),
2700 shapeProps )
2701 .SetAvailableFunc( fillAvailable )
2703 }
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:122
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 coord_type GetY() const
Definition: box2.h:208
constexpr Vec Centre() const
Definition: box2.h:97
constexpr coord_type GetX() const
Definition: box2.h:207
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 coord_type GetLeft() const
Definition: box2.h:228
constexpr bool Contains(const Vec &aPoint) const
Definition: box2.h:168
constexpr coord_type GetRight() const
Definition: box2.h:217
constexpr void SetEnd(coord_type x, coord_type y)
Definition: box2.h:297
constexpr coord_type GetTop() const
Definition: box2.h:229
constexpr bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:311
constexpr coord_type GetBottom() const
Definition: box2.h:222
EDA_ANGLE Normalize()
Definition: eda_angle.h:224
bool IsCardinal() const
Definition: eda_angle.cpp:40
EDA_ANGLE Normalize720()
Definition: eda_angle.h:274
double AsRadians() const
Definition: eda_angle.h:120
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:96
UI_FILL_MODE GetFillModeProp() const
Definition: eda_shape.cpp:557
virtual int GetHatchLineSpacing() const
Definition: eda_shape.h:159
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:1075
SHAPE_T m_shape
Definition: eda_shape.h:486
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false, bool includeFill=false) const
Convert the shape to a closed polygon.
Definition: eda_shape.cpp:2235
void SetStartX(int x)
Definition: eda_shape.h:191
bool m_proxyItem
Definition: eda_shape.h:513
bool m_hatchingDirty
Definition: eda_shape.h:492
bool m_endsSwapped
Definition: eda_shape.h:485
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:258
void SetBezierC2(const VECTOR2I &aPt)
Definition: eda_shape.h:257
void move(const VECTOR2I &aMoveVector)
Definition: eda_shape.cpp:690
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:954
VECTOR2I getCenter() const
Definition: eda_shape.cpp:928
int GetStartY() const
Definition: eda_shape.h:174
void SetFillModeProp(UI_FILL_MODE)
Definition: eda_shape.cpp:544
void SetLength(const double &aLength)
Definition: eda_shape.cpp:434
int m_editState
Definition: eda_shape.h:512
void rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle)
Definition: eda_shape.cpp:793
const std::vector< VECTOR2I > buildBezierToSegmentsPointsList(int aMaxError) const
Definition: eda_shape.cpp:915
const SHAPE_POLY_SET & GetHatching() const
Definition: eda_shape.h:148
FILL_T GetFillMode() const
Definition: eda_shape.h:142
virtual ~EDA_SHAPE()
Definition: eda_shape.cpp:62
long long int m_rectangleHeight
Definition: eda_shape.h:494
void SetEndY(int aY)
Definition: eda_shape.h:226
virtual int GetEffectiveWidth() const
Definition: eda_shape.h:157
COLOR4D GetLineColor() const
Definition: eda_shape.h:165
int GetEndX() const
Definition: eda_shape.h:217
SHAPE_POLY_SET m_hatching
Definition: eda_shape.h:491
int GetRectangleWidth() const
Definition: eda_shape.cpp:420
void SetLineStyle(const LINE_STYLE aStyle)
Definition: eda_shape.cpp:2369
void calcEdit(const VECTOR2I &aPosition)
Definition: eda_shape.cpp:1978
void SetStartY(int y)
Definition: eda_shape.h:184
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:337
void SetCenterY(int y)
Definition: eda_shape.h:198
void CalcArcAngles(EDA_ANGLE &aStartAngle, EDA_ANGLE &aEndAngle) const
Calc arc start and end angles such that aStartAngle < aEndAngle.
Definition: eda_shape.cpp:987
std::vector< VECTOR2I > GetCornersInSequence(EDA_ANGLE angle) const
Definition: eda_shape.cpp:1601
void ShapeGetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList)
Definition: eda_shape.cpp:1145
virtual bool isMoving() const
Definition: eda_shape.h:469
bool operator==(const EDA_SHAPE &aOther) const
Definition: eda_shape.cpp:2384
int GetRadius() const
Definition: eda_shape.cpp:1003
SHAPE_T GetShape() const
Definition: eda_shape.h:168
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition: eda_shape.h:345
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
Definition: eda_shape.cpp:235
void SetRectangleHeight(const int &aHeight)
Definition: eda_shape.cpp:447
bool IsHatchedFill() const
Definition: eda_shape.h:124
VECTOR2I m_arcCenter
Definition: eda_shape.h:503
double m_segmentLength
Definition: eda_shape.h:497
void SetCenterX(int x)
Definition: eda_shape.h:205
virtual void SetFilled(bool aFlag)
Definition: eda_shape.h:136
virtual bool IsFilledForHitTesting() const
Definition: eda_shape.h:131
bool continueEdit(const VECTOR2I &aPosition)
Definition: eda_shape.cpp:1944
wxString ShowShape() const
Definition: eda_shape.cpp:314
ARC_MID m_arcMidData
Definition: eda_shape.h:504
void SetFillColor(const COLOR4D &aColor)
Definition: eda_shape.h:153
int GetEndY() const
Definition: eda_shape.h:216
std::vector< SHAPE * > makeEffectiveShapes(bool aEdgeOnly, bool aLineChainOnly=false) const
Make a set of SHAPE objects representing the EDA_SHAPE.
Definition: eda_shape.cpp:1777
bool hitTest(const VECTOR2I &aPosition, int aAccuracy=0) const
Definition: eda_shape.cpp:1258
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:1026
void SetEndX(int aX)
Definition: eda_shape.h:233
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:901
virtual int GetHatchLineWidth() const
Definition: eda_shape.h:158
bool IsSolidFill() const
Definition: eda_shape.h:117
void flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection)
Definition: eda_shape.cpp:856
EDA_SHAPE(SHAPE_T aType, int aLineWidth, FILL_T aFill)
Definition: eda_shape.cpp:47
void beginEdit(const VECTOR2I &aStartPoint)
Definition: eda_shape.cpp:1903
VECTOR2I m_start
Definition: eda_shape.h:500
int GetPointCount() const
Definition: eda_shape.cpp:1895
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:215
bool IsClosed() const
Definition: eda_shape.cpp:506
void SetRadius(int aX)
Definition: eda_shape.h:240
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:177
void DupPolyPointsList(std::vector< VECTOR2I > &aBuffer) const
Duplicate the list of corners in a std::vector<VECTOR2I>.
Definition: eda_shape.cpp:1871
LINE_STYLE GetLineStyle() const
Definition: eda_shape.cpp:2375
void endEdit(bool aClosed=true)
Finish editing the shape.
Definition: eda_shape.cpp:2132
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:173
void SetLineColor(const COLOR4D &aColor)
Definition: eda_shape.h:164
COLOR4D GetFillColor() const
Definition: eda_shape.h:152
void SetRectangle(const long long int &aHeight, const long long int &aWidth)
Definition: eda_shape.cpp:477
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:167
void SwapShape(EDA_SHAPE *aImage)
Definition: eda_shape.cpp:2169
std::vector< VECTOR2I > GetRectCorners() const
Definition: eda_shape.cpp:1586
void SetSegmentAngle(const EDA_ANGLE &aAngle)
Definition: eda_shape.cpp:492
std::vector< VECTOR2I > m_bezierPoints
Definition: eda_shape.h:509
bool IsAnyFill() const
Definition: eda_shape.h:112
void setPosition(const VECTOR2I &aPos)
Definition: eda_shape.cpp:358
virtual bool IsProxyItem() const
Definition: eda_shape.h:109
void computeArcBBox(BOX2I &aBBox) const
Definition: eda_shape.cpp:1717
virtual void UpdateHatching() const
Definition: eda_shape.cpp:570
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:219
void SetRectangleWidth(const int &aWidth)
Definition: eda_shape.cpp:462
void SetBezierC1(const VECTOR2I &aPt)
Definition: eda_shape.h:254
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:1036
double GetLength() const
Definition: eda_shape.cpp:375
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:341
void scale(double aScale)
Definition: eda_shape.cpp:733
int GetStartX() const
Definition: eda_shape.h:175
double Similarity(const EDA_SHAPE &aOther) const
Definition: eda_shape.cpp:2429
const VECTOR2I & GetBezierC1() const
Definition: eda_shape.h:255
VECTOR2I m_end
Definition: eda_shape.h:501
const BOX2I getBoundingBox() const
Definition: eda_shape.cpp:1204
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:1103
SHAPE_POLY_SET m_poly
Definition: eda_shape.h:510
int GetRectangleHeight() const
Definition: eda_shape.cpp:406
virtual int GetWidth() const
Definition: eda_shape.h:156
VECTOR2I getPosition() const
Definition: eda_shape.cpp:364
bool IsClockwiseArc() const
Definition: eda_shape.cpp:1086
STROKE_PARAMS m_stroke
Definition: eda_shape.h:487
EDA_ANGLE m_segmentAngle
Definition: eda_shape.h:498
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
Definition: eda_shape.cpp:1767
wxString getFriendlyName() const
Definition: eda_shape.cpp:1118
VECTOR2I m_bezierC1
Definition: eda_shape.h:506
FILL_T m_fill
Definition: eda_shape.h:488
COLOR4D m_fillColor
Definition: eda_shape.h:489
void SetWidth(int aWidth)
Definition: eda_shape.cpp:2362
EDA_ANGLE GetSegmentAngle() const
Definition: eda_shape.cpp:1066
void SetFillMode(FILL_T aFill)
Definition: eda_shape.cpp:537
long long int m_rectangleWidth
Definition: eda_shape.h:495
VECTOR2I m_bezierC2
Definition: eda_shape.h:507
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
Definition: eda_shape.cpp:147
bool IsPolyShapeValid() const
Definition: eda_shape.cpp:1888
int Compare(const EDA_SHAPE *aOther) const
Definition: eda_shape.cpp:2194
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:973
ENUM_MAP & Map(T aValue, const wxString &aName)
Definition: property.h:687
static ENUM_MAP< T > & Instance()
Definition: property.h:681
wxPGChoices & Choices()
Definition: property.h:730
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:258
PROPERTY_BASE & SetIsHiddenFromRulesEditor(bool aHide=true)
Definition: property.h:308
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
int GetRadius() const
Definition: shape_circle.h:118
const VECTOR2I GetCenter() const
Definition: shape_circle.h:123
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Move(const VECTOR2I &aVector) override
bool IsClosed() const override
virtual const VECTOR2I GetPoint(int aIndex) const override
void SetPoint(int aIndex, const VECTOR2I &aPos)
Move a point to a specific location.
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
virtual size_t GetPointCount() const override
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
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
void Fracture()
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
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)
const std::vector< SEG > GenerateHatchLines(const std::vector< double > &aSlopes, int aSpacing, int aLineLength) const
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
SHAPE_POLY_SET CloneDropTriangulation() const
void BooleanSubtract(const SHAPE_POLY_SET &b)
Perform boolean polyset difference.
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
const BOX2I BBox(int aClearance=0) const override
Compute a bounding box of the shape, with a margin of aClearance a collision.
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 SetWidth(int aWidth)
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:404
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:406
@ RADIANS_T
Definition: eda_angle.h:32
static constexpr EDA_ANGLE ANGLE_45
Definition: eda_angle.h:405
static constexpr EDA_ANGLE ANGLE_270
Definition: eda_angle.h:409
static constexpr EDA_ANGLE ANGLE_360
Definition: eda_angle.h:410
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:408
#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.
UI_FILL_MODE
Definition: eda_shape.h:68
@ REVERSE_HATCH
Definition: eda_shape.h:72
@ SOLID
Definition: eda_shape.h:70
@ HATCH
Definition: eda_shape.h:71
@ NONE
Definition: eda_shape.h:69
@ CROSS_HATCH
Definition: eda_shape.h:73
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:404
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:393
#define IMPLEMENT_ENUM_TO_WXANY(type)
Definition: property.h:781
#define NO_SETTER(owner, type)
Definition: property.h:792
@ 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
static bool Collide(const SHAPE_CIRCLE &aA, const SHAPE_CIRCLE &aB, int aClearance, int *aActual, VECTOR2I *aLocation, VECTOR2I *aMTV)
LINE_STYLE
Dashed line types.
Definition: stroke_params.h:46
VECTOR2I end
Definition: eda_shape.h:82
VECTOR2I center
Definition: eda_shape.h:83
VECTOR2I start
Definition: eda_shape.h:81
VECTOR2I mid
Definition: eda_shape.h:80
int radius
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
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