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