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 <base_units.h>
31#include <bezier_curves.h>
33#include <eda_draw_frame.h>
34#include <geometry/shape_arc.h>
38#include <geometry/shape_rect.h>
39#include <geometry/roundrect.h>
41#include <geometry/roundrect.h>
42#include <macros.h>
43#include <algorithm>
45#include <properties/property.h>
47#include <math/util.h> // for KiROUND
48#include <eda_item.h>
49#include <plotters/plotter.h>
50#include <api/api_enums.h>
51#include <api/api_utils.h>
52#include <api/common/types/base_types.pb.h>
53
54
55EDA_SHAPE::EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill ) :
56 m_endsSwapped( false ),
57 m_shape( aType ),
59 m_fill( aFill ),
61 m_hatchingDirty( true ),
64 m_cornerRadius( 0 ),
65 m_editState( 0 ),
66 m_proxyItem( false )
67{
68}
69
70
74
75
76EDA_SHAPE::EDA_SHAPE( const SHAPE& aShape ) :
77 m_endsSwapped( false ),
79 m_fill(),
80 m_hatchingDirty( true ),
83 m_cornerRadius( 0 ),
84 m_editState( 0 ),
85 m_proxyItem( false )
86{
87 switch( aShape.Type() )
88 {
89 case SH_RECT:
90 {
91 auto rect = static_cast<const SHAPE_RECT&>( aShape );
92 m_shape = SHAPE_T::RECTANGLE;
93 SetStart( rect.GetPosition() );
94 SetEnd( rect.GetPosition() + rect.GetSize() );
95 break;
96 }
97
98 case SH_SEGMENT:
99 {
100 auto seg = static_cast<const SHAPE_SEGMENT&>( aShape );
101 m_shape = SHAPE_T::SEGMENT;
102 SetStart( seg.GetSeg().A );
103 SetEnd( seg.GetSeg().B );
104 SetWidth( seg.GetWidth() );
105 break;
106 }
107
108 case SH_LINE_CHAIN:
109 {
110 auto line = static_cast<const SHAPE_LINE_CHAIN&>( aShape );
111 m_shape = SHAPE_T::POLY;
112 GetPolyShape() = SHAPE_POLY_SET();
113 GetPolyShape().AddOutline( line );
114 SetWidth( line.Width() );
115 break;
116 }
117
118 case SH_CIRCLE:
119 {
120 auto circle = static_cast<const SHAPE_CIRCLE&>( aShape );
121 m_shape = SHAPE_T::CIRCLE;
122 SetStart( circle.GetCenter() );
123 SetEnd( circle.GetCenter() + circle.GetRadius() );
124 break;
125 }
126
127 case SH_ARC:
128 {
129 auto arc = static_cast<const SHAPE_ARC&>( aShape );
130 m_shape = SHAPE_T::ARC;
131 SetArcGeometry( arc.GetP0(), arc.GetArcMid(), arc.GetP1() );
132 SetWidth( arc.GetWidth() );
133 break;
134 }
135
136 case SH_SIMPLE:
137 {
138 auto poly = static_cast<const SHAPE_SIMPLE&>( aShape );
139 m_shape = SHAPE_T::POLY;
140 poly.TransformToPolygon( GetPolyShape(), 0, ERROR_INSIDE );
141 break;
142 }
143
144 // currently unhandled
145 case SH_POLY_SET:
146 case SH_COMPOUND:
147 case SH_NULL:
149 default:
151 break;
152 }
153}
154
155
158 m_shape( aOther.m_shape ),
159 m_stroke( aOther.m_stroke ),
160 m_fill( aOther.m_fill ),
161 m_fillColor( aOther.m_fillColor ),
162 m_hatchingDirty( true ),
166 m_start( aOther.m_start ),
167 m_end( aOther.m_end ),
168 m_arcCenter( aOther.m_arcCenter ),
169 m_arcMidData( aOther.m_arcMidData ),
170 m_bezierC1( aOther.m_bezierC1 ),
171 m_bezierC2( aOther.m_bezierC2 ),
173 m_editState( aOther.m_editState ),
174 m_proxyItem( aOther.m_proxyItem )
175{
176 if( aOther.m_poly )
177 m_poly = std::make_unique<SHAPE_POLY_SET>( *aOther.m_poly );
178}
179
180
182{
183 if( this == &aOther )
184 return *this;
185
187 m_shape = aOther.m_shape;
188 m_stroke = aOther.m_stroke;
189 m_fill = aOther.m_fill;
190 m_fillColor = aOther.m_fillColor;
191 m_hatchingCache.reset();
192 m_hatchingDirty = true;
196 m_start = aOther.m_start;
197 m_end = aOther.m_end;
198 m_arcCenter = aOther.m_arcCenter;
199 m_arcMidData = aOther.m_arcMidData;
200 m_bezierC1 = aOther.m_bezierC1;
201 m_bezierC2 = aOther.m_bezierC2;
203 if( aOther.m_poly )
204 m_poly = std::make_unique<SHAPE_POLY_SET>( *aOther.m_poly );
205 else
206 m_poly.reset();
207 m_editState = aOther.m_editState;
208 m_proxyItem = aOther.m_proxyItem;
209
210 return *this;
211}
212
213
214void EDA_SHAPE::Serialize( google::protobuf::Any &aContainer ) const
215{
216 Serialize( aContainer, pcbIUScale );
217}
218
219
220void EDA_SHAPE::Serialize( google::protobuf::Any &aContainer, const EDA_IU_SCALE &aScale ) const
221{
222 using namespace kiapi::common;
223 types::GraphicShape shape;
224
225 types::StrokeAttributes* stroke = shape.mutable_attributes()->mutable_stroke();
226 types::GraphicFillAttributes* fill = shape.mutable_attributes()->mutable_fill();
227
228 PackDistance( *stroke->mutable_width(), GetWidth(), aScale );
229 stroke->set_style( ToProtoEnum<LINE_STYLE, types::StrokeLineStyle>( m_stroke.GetLineStyle() ) );
230
231 if( m_stroke.GetColor() != COLOR4D::UNSPECIFIED )
232 PackColor( *stroke->mutable_color(), m_stroke.GetColor() );
233
235
237 PackColor( *fill->mutable_color(), m_fillColor );
238
239 switch( GetShape() )
240 {
241 case SHAPE_T::SEGMENT:
242 {
243 types::GraphicSegmentAttributes* segment = shape.mutable_segment();
244 PackVector2( *segment->mutable_start(), GetStart(), aScale );
245 PackVector2( *segment->mutable_end(), GetEnd(), aScale );
246 break;
247 }
248
250 {
251 types::GraphicRectangleAttributes* rectangle = shape.mutable_rectangle();
252 PackVector2( *rectangle->mutable_top_left(), GetStart(), aScale );
253 PackVector2( *rectangle->mutable_bottom_right(), GetEnd(), aScale );
254 PackDistance( *rectangle->mutable_corner_radius(), GetCornerRadius(), aScale );
255 break;
256 }
257
258 case SHAPE_T::ARC:
259 {
260 types::GraphicArcAttributes* arc = shape.mutable_arc();
261 PackVector2( *arc->mutable_start(), GetStart(), aScale );
262 PackVector2( *arc->mutable_mid(), GetArcMid(), aScale );
263 PackVector2( *arc->mutable_end(), GetEnd(), aScale );
264 break;
265 }
266
267 case SHAPE_T::CIRCLE:
268 {
269 types::GraphicCircleAttributes* circle = shape.mutable_circle();
270 PackVector2( *circle->mutable_center(), GetStart(), aScale );
271 PackVector2( *circle->mutable_radius_point(), GetEnd(), aScale );
272 break;
273 }
274
275 case SHAPE_T::POLY:
276 {
277 PackPolySet( *shape.mutable_polygon(), GetPolyShape(), aScale );
278 break;
279 }
280
281 case SHAPE_T::BEZIER:
282 {
283 types::GraphicBezierAttributes* bezier = shape.mutable_bezier();
284 PackVector2( *bezier->mutable_start(), GetStart(), aScale );
285 PackVector2( *bezier->mutable_control1(), GetBezierC1(), aScale );
286 PackVector2( *bezier->mutable_control2(), GetBezierC2(), aScale );
287 PackVector2( *bezier->mutable_end(), GetEnd(), aScale );
288 break;
289 }
290
291 default:
292 wxASSERT_MSG( false, "Unhandled shape in EDA_SHAPE::Serialize" );
293 }
294
295 // TODO m_hasSolderMask and m_solderMaskMargin
296
297 aContainer.PackFrom( shape );
298}
299
300
301bool EDA_SHAPE::Deserialize( const google::protobuf::Any &aContainer )
302{
303 return Deserialize( aContainer, pcbIUScale );
304}
305
306
307bool EDA_SHAPE::Deserialize( const google::protobuf::Any &aContainer, const EDA_IU_SCALE &aScale )
308{
309 using namespace kiapi::common;
310
311 types::GraphicShape shape;
312
313 if( !aContainer.UnpackTo( &shape ) )
314 return false;
315
316 // Initialize everything to a known state that doesn't get touched by every
317 // codepath below, to make sure the equality operator is consistent
318 m_start = {};
319 m_end = {};
320 m_arcCenter = {};
321 m_arcMidData = {};
322 m_bezierC1 = {};
323 m_bezierC2 = {};
324 m_editState = 0;
325 m_proxyItem = false;
326 m_endsSwapped = false;
328
329 if( shape.attributes().stroke().has_color() )
330 m_stroke.SetColor( UnpackColor( shape.attributes().stroke().color() ) );
331 else
332 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
333
334 if( shape.attributes().fill().has_color() )
335 SetFillColor( UnpackColor( shape.attributes().fill().color() ) );
336
337 if( shape.attributes().has_stroke() )
338 {
339 SetWidth( UnpackDistance( shape.attributes().stroke().width(), aScale ) );
340 SetLineStyle( FromProtoEnum<LINE_STYLE, types::StrokeLineStyle>( shape.attributes().stroke().style() ) );
341 }
342
343 if( shape.attributes().has_fill() )
344 SetFillMode( FromProtoEnum<FILL_T, types::GraphicFillType>( shape.attributes().fill().fill_type() ) );
345
346 if( shape.has_segment() )
347 {
349 SetStart( UnpackVector2( shape.segment().start(), aScale ) );
350 SetEnd( UnpackVector2( shape.segment().end(), aScale ) );
351 }
352 else if( shape.has_rectangle() )
353 {
355 SetStart( UnpackVector2( shape.rectangle().top_left(), aScale ) );
356 SetEnd( UnpackVector2( shape.rectangle().bottom_right(), aScale ) );
357 SetCornerRadius( UnpackDistance( shape.rectangle().corner_radius(), aScale ) );
358 }
359 else if( shape.has_arc() )
360 {
362 SetArcGeometry( UnpackVector2( shape.arc().start(), aScale ),
363 UnpackVector2( shape.arc().mid(), aScale ),
364 UnpackVector2( shape.arc().end(), aScale ) );
365 }
366 else if( shape.has_circle() )
367 {
369 SetStart( UnpackVector2( shape.circle().center(), aScale ) );
370 SetEnd( UnpackVector2( shape.circle().radius_point(), aScale ) );
371 }
372 else if( shape.has_polygon() )
373 {
375 SetPolyShape( UnpackPolySet( shape.polygon(), aScale ) );
376 }
377 else if( shape.has_bezier() )
378 {
380 SetStart( UnpackVector2( shape.bezier().start(), aScale ) );
381 SetBezierC1( UnpackVector2( shape.bezier().control1(), aScale ) );
382 SetBezierC2( UnpackVector2( shape.bezier().control2(), aScale ) );
383 SetEnd( UnpackVector2( shape.bezier().end(), aScale ) );
385 }
386
387 return true;
388}
389
390
391wxString EDA_SHAPE::ShowShape() const
392{
393 if( IsProxyItem() )
394 {
395 switch( m_shape )
396 {
397 case SHAPE_T::SEGMENT: return _( "Thermal Spoke" );
398 case SHAPE_T::RECTANGLE: return _( "Number Box" );
399 default: return wxT( "??" );
400 }
401 }
402 else
403 {
404 switch( m_shape )
405 {
406 case SHAPE_T::SEGMENT: return _( "Line" );
407 case SHAPE_T::RECTANGLE: return _( "Rect" );
408 case SHAPE_T::ARC: return _( "Arc" );
409 case SHAPE_T::CIRCLE: return _( "Circle" );
410 case SHAPE_T::BEZIER: return _( "Bezier Curve" );
411 case SHAPE_T::POLY: return _( "Polygon" );
412 default: return wxT( "??" );
413 }
414 }
415}
416
417
419{
420 switch( m_shape )
421 {
422 case SHAPE_T::SEGMENT: return wxS( "S_SEGMENT" );
423 case SHAPE_T::RECTANGLE: return wxS( "S_RECT" );
424 case SHAPE_T::ARC: return wxS( "S_ARC" );
425 case SHAPE_T::CIRCLE: return wxS( "S_CIRCLE" );
426 case SHAPE_T::POLY: return wxS( "S_POLYGON" );
427 case SHAPE_T::BEZIER: return wxS( "S_CURVE" );
428 case SHAPE_T::UNDEFINED: return wxS( "UNDEFINED" );
429 }
430
431 return wxEmptyString; // Just to quiet GCC.
432}
433
434
436{
437 move( aPos - getPosition() );
438}
439
440
442{
443 if( m_shape == SHAPE_T::ARC )
444 return getCenter();
445 else if( m_shape == SHAPE_T::POLY )
446 return GetPolyShape().CVertex( 0 );
447 else
448 return m_start;
449}
450
451
453{
454 double length = 0.0;
455
456 switch( m_shape )
457 {
458 case SHAPE_T::BEZIER:
459 for( size_t ii = 1; ii < m_bezierPoints.size(); ++ii )
460 length += m_bezierPoints[ ii - 1].Distance( m_bezierPoints[ii] );
461
462 return length;
463
464 case SHAPE_T::SEGMENT:
465 return GetStart().Distance( GetEnd() );
466
467 case SHAPE_T::POLY:
468 for( int ii = 0; ii < GetPolyShape().COutline( 0 ).SegmentCount(); ii++ )
469 length += GetPolyShape().COutline( 0 ).CSegment( ii ).Length();
470
471 return length;
472
473 case SHAPE_T::ARC:
474 return GetRadius() * GetArcAngle().AsRadians();
475
476 default:
478 return 0.0;
479 }
480}
481
482
484{
485 switch( m_shape )
486 {
488 return GetEndY() - GetStartY();
489
490 default:
492 return 0;
493 }
494}
495
496
498{
499 switch( m_shape )
500 {
502 return GetEndX() - GetStartX();
503
504 default:
506 return 0;
507 }
508}
509
510
512{
513 return m_cornerRadius;
514}
515
516
517void EDA_SHAPE::SetCornerRadius( int aRadius )
518{
520 {
521 int width = std::abs( GetRectangleWidth() );
522 int height = std::abs( GetRectangleHeight() );
523 int maxRadius = std::min( width, height ) / 2;
524
525 m_cornerRadius = std::clamp( aRadius, 0, maxRadius );
526 }
527 else
528 {
529 m_cornerRadius = aRadius;
530 }
531}
532
533
534void EDA_SHAPE::SetRectangleHeight( const int& aHeight )
535{
536 switch ( m_shape )
537 {
539 m_rectangleHeight = aHeight;
541 break;
542
543 default:
545 }
546}
547
548
549void EDA_SHAPE::SetRectangleWidth( const int& aWidth )
550{
551 switch ( m_shape )
552 {
554 m_rectangleWidth = aWidth;
556 break;
557
558 default:
560 }
561}
562
563
564void EDA_SHAPE::SetRectangle( const long long int& aHeight, const long long int& aWidth )
565{
566 switch ( m_shape )
567 {
569 m_rectangleHeight = aHeight;
570 m_rectangleWidth = aWidth;
571 break;
572
573 default:
575 }
576}
577
578
580{
581 switch( m_shape )
582 {
583 case SHAPE_T::CIRCLE:
585 return true;
586
587 case SHAPE_T::ARC:
588 case SHAPE_T::SEGMENT:
589 return false;
590
591 case SHAPE_T::POLY:
592 if( GetPolyShape().IsEmpty() )
593 return false;
594 else
595 return GetPolyShape().Outline( 0 ).IsClosed();
596
597 case SHAPE_T::BEZIER:
598 if( m_bezierPoints.size() < 3 )
599 return false;
600 else
601 return m_bezierPoints[0] == m_bezierPoints[ m_bezierPoints.size() - 1 ];
602
603 default:
605 return false;
606 }
607}
608
609
611{
612 m_fill = aFill;
613 m_hatchingDirty = true;
614}
615
616
618{
619 switch( aFill )
620 {
625 default: SetFilled( true ); break;
626 }
627}
628
629
641
642
644{
645 if( !m_hatchingCache )
646 m_hatchingCache = std::make_unique<EDA_SHAPE_HATCH_CACHE_DATA>();
647
648 return m_hatchingCache->hatching;
649}
650
651
652const std::vector<SEG>& EDA_SHAPE::GetHatchLines() const
653{
654 if( !m_hatchingCache )
655 m_hatchingCache = std::make_unique<EDA_SHAPE_HATCH_CACHE_DATA>();
656
657 return m_hatchingCache->hatchLines;
658}
659
660
662{
663 if( !m_hatchingCache )
664 m_hatchingCache = std::make_unique<EDA_SHAPE_HATCH_CACHE_DATA>();
665
666 return m_hatchingCache->hatching;
667}
668
669
670std::vector<SEG>& EDA_SHAPE::hatchLines() const
671{
672 if( !m_hatchingCache )
673 m_hatchingCache = std::make_unique<EDA_SHAPE_HATCH_CACHE_DATA>();
674
675 return m_hatchingCache->hatchLines;
676}
677
678
680{
681 if( !m_hatchingDirty )
682 return;
683
684 std::vector<double> slopes;
685 int lineWidth = GetHatchLineWidth();
686 int spacing = GetHatchLineSpacing();
687 SHAPE_POLY_SET shapeBuffer;
688
689 // Validate state before clearing cached hatching. If we can't regenerate, keep existing cache.
690 if( isMoving() )
691 return;
692
694 slopes = { 1.0, -1.0 };
695 else if( GetFillMode() == FILL_T::HATCH )
696 slopes = { -1.0 };
697 else if( GetFillMode() == FILL_T::REVERSE_HATCH )
698 slopes = { 1.0 };
699 else
700 return;
701
702 if( spacing == 0 )
703 return;
704
705 switch( m_shape )
706 {
707 case SHAPE_T::ARC:
708 case SHAPE_T::SEGMENT:
709 case SHAPE_T::BEZIER:
710 return;
711
713 {
715 rr.TransformToPolygon( shapeBuffer, getMaxError() );
716 }
717 break;
718
719 case SHAPE_T::CIRCLE:
721 break;
722
723 case SHAPE_T::POLY:
724 if( !IsClosed() )
725 return;
726
727 shapeBuffer = GetPolyShape().CloneDropTriangulation();
728 break;
729
730 default:
732 return;
733 }
734
735 shapeBuffer.ClearArcs();
736
737 // Clear cached hatching only after all validation passes.
738 // This prevents flickering when early returns would otherwise leave empty hatching.
740 hatchLines().clear();
741
742 BOX2I extents = shapeBuffer.BBox();
743 int majorAxis = std::max( extents.GetWidth(), extents.GetHeight() );
744
745 if( majorAxis / spacing > 100 )
746 spacing = majorAxis / 100;
747
749
750 if( !knockouts.IsEmpty() )
751 {
752 shapeBuffer.BooleanSubtract( knockouts );
753 shapeBuffer.Fracture();
754 }
755
756 // Generate hatch lines for stroke-based rendering. All hatch types use line segments.
757 std::vector<SEG> hatchSegs = shapeBuffer.GenerateHatchLines( slopes, spacing, -1 );
758 hatchLines() = hatchSegs;
759
760 // Also generate polygon representation for exports, 3D viewer, and hit testing
762 {
763 for( const SEG& seg : hatchSegs )
764 {
765 // We don't really need the rounded ends at all, so don't spend any extra time on them
766 int maxError = lineWidth;
767
768 TransformOvalToPolygon( hatching(), seg.A, seg.B, lineWidth, maxError,
769 ERROR_INSIDE );
770 }
771
772 hatching().Fracture();
773 m_hatchingDirty = false;
774 }
775 else
776 {
777 // Generate a grid of holes for a cross-hatch polygon representation.
778 // This is used for exports, 3D viewer, and hit testing.
779
780 int gridsize = spacing;
781 int hole_size = gridsize - GetHatchLineWidth();
782
783 hatching() = shapeBuffer.CloneDropTriangulation();
785
786 // Build hole shape
787 SHAPE_LINE_CHAIN hole_base;
788 VECTOR2I corner( 0, 0 );;
789 hole_base.Append( corner );
790 corner.x += hole_size;
791 hole_base.Append( corner );
792 corner.y += hole_size;
793 hole_base.Append( corner );
794 corner.x = 0;
795 hole_base.Append( corner );
796 hole_base.SetClosed( true );
797
798 // Build holes
799 BOX2I bbox = GetHatching().BBox( 0 );
800 SHAPE_POLY_SET holes;
801
802 int x_offset = bbox.GetX() - ( bbox.GetX() ) % gridsize - gridsize;
803 int y_offset = bbox.GetY() - ( bbox.GetY() ) % gridsize - gridsize;
804
805 for( int xx = x_offset; xx <= bbox.GetRight(); xx += gridsize )
806 {
807 for( int yy = y_offset; yy <= bbox.GetBottom(); yy += gridsize )
808 {
809 SHAPE_LINE_CHAIN hole( hole_base );
810 hole.Move( VECTOR2I( xx, yy ) );
811 holes.AddOutline( hole );
812 }
813 }
814
815 hatching().BooleanSubtract( holes );
816 hatching().Fracture();
817
818 // Must re-rotate after Fracture(). Clipper struggles mightily with fracturing
819 // 45-degree holes.
821
822 if( !knockouts.IsEmpty() )
823 {
824 hatching().BooleanSubtract( knockouts );
825 hatching().Fracture();
826 }
827
828 m_hatchingDirty = false;
829 }
830}
831
832
833void EDA_SHAPE::move( const VECTOR2I& aMoveVector )
834{
835 switch ( m_shape )
836 {
837 case SHAPE_T::ARC:
838 m_arcCenter += aMoveVector;
839 m_arcMidData.center += aMoveVector;
840 m_arcMidData.start += aMoveVector;
841 m_arcMidData.end += aMoveVector;
842 m_arcMidData.mid += aMoveVector;
844
845 case SHAPE_T::SEGMENT:
847 case SHAPE_T::CIRCLE:
848 m_start += aMoveVector;
849 m_end += aMoveVector;
850 break;
851
852 case SHAPE_T::POLY:
853 GetPolyShape().Move( aMoveVector );
854 break;
855
856 case SHAPE_T::BEZIER:
857 m_start += aMoveVector;
858 m_end += aMoveVector;
859 m_bezierC1 += aMoveVector;
860 m_bezierC2 += aMoveVector;
861
862 for( VECTOR2I& pt : m_bezierPoints )
863 pt += aMoveVector;
864
865 break;
866
867 default:
869 break;
870 }
871
872 // Translate the cached hatch geometry instead of leaving it stale. The hatch pattern is
873 // invariant under translation, so shifting line endpoints is sufficient and keeps the
874 // display correct during interactive moves without hitting GenerateHatchLines().
875 if( m_hatchingCache )
876 {
877 for( SEG& seg : m_hatchingCache->hatchLines )
878 {
879 seg.A += aMoveVector;
880 seg.B += aMoveVector;
881 }
882
883 m_hatchingCache->hatching.Move( aMoveVector );
884 }
885
886 m_hatchingDirty = true;
887}
888
889
890void EDA_SHAPE::scale( double aScale )
891{
892 auto scalePt =
893 [&]( VECTOR2I& pt )
894 {
895 pt.x = KiROUND( pt.x * aScale );
896 pt.y = KiROUND( pt.y * aScale );
897 };
898
899 switch( m_shape )
900 {
901 case SHAPE_T::ARC:
902 scalePt( m_arcCenter );
904
905 case SHAPE_T::SEGMENT:
907 case SHAPE_T::CIRCLE:
908 scalePt( m_start );
909 scalePt( m_end );
910 break;
911
912 case SHAPE_T::POLY: // polygon
913 {
914 std::vector<VECTOR2I> pts;
915
916 for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ ii )
917 {
918 for( const VECTOR2I& pt : GetPolyShape().Outline( ii ).CPoints() )
919 {
920 pts.emplace_back( pt );
921 scalePt( pts.back() );
922 }
923 }
924
925 SetPolyPoints( pts );
926 }
927 break;
928
929 case SHAPE_T::BEZIER:
930 scalePt( m_start );
931 scalePt( m_end );
932 scalePt( m_bezierC1 );
933 scalePt( m_bezierC2 );
935 break;
936
937 default:
939 break;
940 }
941
942 m_hatchingDirty = true;
943}
944
945
946void EDA_SHAPE::rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
947{
948 switch( m_shape )
949 {
950 case SHAPE_T::SEGMENT:
951 case SHAPE_T::CIRCLE:
952 RotatePoint( m_start, aRotCentre, aAngle );
953 RotatePoint( m_end, aRotCentre, aAngle );
954 break;
955
956 case SHAPE_T::ARC:
957 RotatePoint( m_start, aRotCentre, aAngle );
958 RotatePoint( m_end, aRotCentre, aAngle );
959 RotatePoint( m_arcCenter, aRotCentre, aAngle );
960 RotatePoint( m_arcMidData.start, aRotCentre, aAngle );
961 RotatePoint( m_arcMidData.end, aRotCentre, aAngle );
962 RotatePoint( m_arcMidData.mid, aRotCentre, aAngle );
963 RotatePoint( m_arcMidData.center, aRotCentre, aAngle );
964 break;
965
967 if( aAngle.IsCardinal() )
968 {
969 RotatePoint( m_start, aRotCentre, aAngle );
970 RotatePoint( m_end, aRotCentre, aAngle );
971 }
972 else
973 {
974 // Convert non-cardinally-rotated rect to a diamond
978 GetPolyShape().Rotate( aAngle, aRotCentre );
979 }
980
981 break;
982
983 case SHAPE_T::POLY:
984 GetPolyShape().Rotate( aAngle, aRotCentre );
985 break;
986
987 case SHAPE_T::BEZIER:
988 RotatePoint( m_start, aRotCentre, aAngle );
989 RotatePoint( m_end, aRotCentre, aAngle );
990 RotatePoint( m_bezierC1, aRotCentre, aAngle );
991 RotatePoint( m_bezierC2, aRotCentre, aAngle );
992
993 for( VECTOR2I& pt : m_bezierPoints )
994 RotatePoint( pt, aRotCentre, aAngle);
995
996 break;
997
998 default:
1000 break;
1001 }
1002
1003 m_hatchingDirty = true;
1004}
1005
1006
1007void EDA_SHAPE::flip( const VECTOR2I& aCentre, FLIP_DIRECTION aFlipDirection )
1008{
1009 switch ( m_shape )
1010 {
1011 case SHAPE_T::SEGMENT:
1012 case SHAPE_T::RECTANGLE:
1013 MIRROR( m_start, aCentre, aFlipDirection );
1014 MIRROR( m_end, aCentre, aFlipDirection );
1015 break;
1016
1017 case SHAPE_T::CIRCLE:
1018 MIRROR( m_start, aCentre, aFlipDirection );
1019 MIRROR( m_end, aCentre, aFlipDirection );
1020 break;
1021
1022 case SHAPE_T::ARC:
1023 MIRROR( m_start, aCentre, aFlipDirection );
1024 MIRROR( m_end, aCentre, aFlipDirection );
1025 MIRROR( m_arcCenter, aCentre, aFlipDirection );
1026
1027 std::swap( m_start, m_end );
1028 break;
1029
1030 case SHAPE_T::POLY:
1031 GetPolyShape().Mirror( aCentre, aFlipDirection );
1032 break;
1033
1034 case SHAPE_T::BEZIER:
1035 MIRROR( m_start, aCentre, aFlipDirection );
1036 MIRROR( m_end, aCentre, aFlipDirection );
1037 MIRROR( m_bezierC1, aCentre, aFlipDirection );
1038 MIRROR( m_bezierC2, aCentre, aFlipDirection );
1039
1041 break;
1042
1043 default:
1045 break;
1046 }
1047
1048 m_hatchingDirty = true;
1049}
1050
1051
1053{
1054 // Has meaning only for SHAPE_T::BEZIER
1055 if( m_shape != SHAPE_T::BEZIER )
1056 {
1057 m_bezierPoints.clear();
1058 return;
1059 }
1060
1061 // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
1063}
1064
1065
1066const std::vector<VECTOR2I> EDA_SHAPE::buildBezierToSegmentsPointsList( int aMaxError ) const
1067{
1068 std::vector<VECTOR2I> bezierPoints;
1069
1070 // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
1071 std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
1072 BEZIER_POLY converter( ctrlPoints );
1073 converter.GetPoly( bezierPoints, aMaxError );
1074
1075 return bezierPoints;
1076}
1077
1078
1080{
1081 switch( m_shape )
1082 {
1083 case SHAPE_T::ARC:
1084 return m_arcCenter;
1085
1086 case SHAPE_T::CIRCLE:
1087 return m_start;
1088
1089 case SHAPE_T::SEGMENT:
1090 // Midpoint of the line
1091 return ( m_start + m_end ) / 2;
1092
1093 case SHAPE_T::POLY:
1094 case SHAPE_T::RECTANGLE:
1095 case SHAPE_T::BEZIER:
1096 return getBoundingBox().Centre();
1097
1098 default:
1100 return VECTOR2I();
1101 }
1102}
1103
1104
1105void EDA_SHAPE::SetCenter( const VECTOR2I& aCenter )
1106{
1107 switch( m_shape )
1108 {
1109 case SHAPE_T::ARC:
1110 m_arcCenter = aCenter;
1111 break;
1112
1113 case SHAPE_T::CIRCLE:
1114 m_start = aCenter;
1115 m_hatchingDirty = true;
1116 break;
1117
1118 default:
1120 }
1121}
1122
1123
1125{
1126 // If none of the input data have changed since we loaded the arc, keep the original mid point data
1127 // to minimize churn
1128 if( m_arcMidData.start == m_start && m_arcMidData.end == m_end && m_arcMidData.center == m_arcCenter )
1129 return m_arcMidData.mid;
1130
1131 VECTOR2I mid = m_start;
1132 RotatePoint( mid, m_arcCenter, -GetArcAngle() / 2.0 );
1133 return mid;
1134}
1135
1136
1137void EDA_SHAPE::CalcArcAngles( EDA_ANGLE& aStartAngle, EDA_ANGLE& aEndAngle ) const
1138{
1139 VECTOR2D startRadial( GetStart() - getCenter() );
1140 VECTOR2D endRadial( GetEnd() - getCenter() );
1141
1142 aStartAngle = EDA_ANGLE( startRadial );
1143 aEndAngle = EDA_ANGLE( endRadial );
1144
1145 if( aEndAngle == aStartAngle )
1146 aEndAngle = aStartAngle + ANGLE_360; // ring, not null
1147
1148 while( aEndAngle < aStartAngle )
1149 aEndAngle += ANGLE_360;
1150}
1151
1152
1154{
1155 double radius = 0.0;
1156
1157 switch( m_shape )
1158 {
1159 case SHAPE_T::ARC:
1160 radius = m_arcCenter.Distance( m_start );
1161 break;
1162
1163 case SHAPE_T::CIRCLE:
1164 radius = m_start.Distance( m_end );
1165 break;
1166
1167 default:
1169 }
1170
1171 // don't allow degenerate circles/arcs
1172 if( radius > (double) INT_MAX / 2.0 )
1173 radius = (double) INT_MAX / 2.0;
1174
1175 return std::max( 1, KiROUND( radius ) );
1176}
1177
1178
1179void EDA_SHAPE::SetCachedArcData( const VECTOR2I& aStart, const VECTOR2I& aMid,
1180 const VECTOR2I& aEnd, const VECTOR2I& aCenter )
1181{
1182 m_arcMidData.start = aStart;
1183 m_arcMidData.end = aEnd;
1184 m_arcMidData.center = aCenter;
1185 m_arcMidData.mid = aMid;
1186}
1187
1188
1189void EDA_SHAPE::SetArcGeometry( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
1190{
1191 m_arcMidData = {};
1192 m_start = aStart;
1193 m_end = aEnd;
1194 m_arcCenter = CalcArcCenter( aStart, aMid, aEnd );
1195 VECTOR2I new_mid = GetArcMid();
1196
1197 m_endsSwapped = false;
1198
1199 // Watch the ordering here. GetArcMid above needs to be called prior to initializing the
1200 // m_arcMidData structure in order to ensure we get the calculated variant, not the cached
1201 SetCachedArcData( aStart, aMid, aEnd, m_arcCenter );
1202
1203 /*
1204 * If the input winding doesn't match our internal winding, the calculated midpoint will end
1205 * up on the other side of the arc. In this case, we need to flip the start/end points and
1206 * flag this change for the system.
1207 */
1208 VECTOR2D dist( new_mid - aMid );
1209 VECTOR2D dist2( new_mid - m_arcCenter );
1210
1211 if( dist.SquaredEuclideanNorm() > dist2.SquaredEuclideanNorm() )
1212 {
1213 std::swap( m_start, m_end );
1214 m_endsSwapped = true;
1215 }
1216}
1217
1218
1220{
1221 EDA_ANGLE angle( atan2( static_cast<double>( GetStart().y - GetEnd().y ),
1222 static_cast<double>( GetEnd().x - GetStart().x ) ), RADIANS_T );
1223
1224 return angle;
1225}
1226
1227
1229{
1230 EDA_ANGLE startAngle;
1231 EDA_ANGLE endAngle;
1232
1233 CalcArcAngles( startAngle, endAngle );
1234
1235 return endAngle - startAngle;
1236}
1237
1238
1240{
1241 if( m_shape == SHAPE_T::ARC )
1242 {
1243 VECTOR2D mid = GetArcMid();
1244
1245 double orient = ( mid.x - m_start.x ) * ( m_end.y - m_start.y )
1246 - ( mid.y - m_start.y ) * ( m_end.x - m_start.x );
1247
1248 return orient < 0;
1249 }
1250
1252 return false;
1253}
1254
1255
1256void EDA_SHAPE::SetArcAngleAndEnd( const EDA_ANGLE& aAngle, bool aCheckNegativeAngle )
1257{
1258 EDA_ANGLE angle( aAngle );
1259
1260 m_end = m_start;
1262
1263 if( aCheckNegativeAngle && aAngle < ANGLE_0 )
1264 {
1265 std::swap( m_start, m_end );
1266 m_endsSwapped = true;
1267 }
1268}
1269
1270
1272{
1273 if( IsProxyItem() )
1274 {
1275 switch( m_shape )
1276 {
1277 case SHAPE_T::RECTANGLE: return _( "Pad Number Box" );
1278 case SHAPE_T::SEGMENT: return _( "Thermal Spoke Template" );
1279 default: return _( "Unrecognized" );
1280 }
1281 }
1282 else
1283 {
1284 switch( m_shape )
1285 {
1286 case SHAPE_T::CIRCLE: return _( "Circle" );
1287 case SHAPE_T::ARC: return _( "Arc" );
1288 case SHAPE_T::BEZIER: return _( "Curve" );
1289 case SHAPE_T::POLY: return _( "Polygon" );
1290 case SHAPE_T::RECTANGLE: return _( "Rectangle" );
1291 case SHAPE_T::SEGMENT: return _( "Segment" );
1292 default: return _( "Unrecognized" );
1293 }
1294 }
1295}
1296
1297
1298void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
1299{
1300 wxString msg;
1301
1302 wxString shape = _( "Shape" );
1303 aList.emplace_back( shape, getFriendlyName() );
1304
1305 switch( m_shape )
1306 {
1307 case SHAPE_T::CIRCLE:
1308 aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
1309 break;
1310
1311 case SHAPE_T::ARC:
1312 aList.emplace_back( _( "Length" ), aFrame->MessageTextFromValue( GetLength() ) );
1313
1315 aList.emplace_back( _( "Angle" ), msg );
1316
1317 aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
1318 break;
1319
1320 case SHAPE_T::BEZIER:
1321 aList.emplace_back( _( "Length" ), aFrame->MessageTextFromValue( GetLength() ) );
1322 break;
1323
1324 case SHAPE_T::POLY:
1325 msg.Printf( wxS( "%d" ), GetPolyShape().Outline(0).PointCount() );
1326 aList.emplace_back( _( "Points" ), msg );
1327 break;
1328
1329 case SHAPE_T::RECTANGLE:
1330 aList.emplace_back( _( "Width" ), aFrame->MessageTextFromValue( std::abs( GetEnd().x - GetStart().x ) ) );
1331 aList.emplace_back( _( "Height" ), aFrame->MessageTextFromValue( std::abs( GetEnd().y - GetStart().y ) ) );
1332 break;
1333
1334 case SHAPE_T::SEGMENT:
1335 {
1336 aList.emplace_back( _( "Length" ), aFrame->MessageTextFromValue( GetStart().Distance( GetEnd() ) ));
1337
1338 // angle counter-clockwise from 3'o-clock
1339 EDA_ANGLE angle( atan2( (double)( GetStart().y - GetEnd().y ), (double)( GetEnd().x - GetStart().x ) ),
1340 RADIANS_T );
1341 aList.emplace_back( _( "Angle" ), EDA_UNIT_UTILS::UI::MessageTextFromValue( angle ) );
1342 break;
1343 }
1344
1345 default:
1346 break;
1347 }
1348
1349 m_stroke.GetMsgPanelInfo( aFrame, aList );
1350}
1351
1352
1354{
1355 BOX2I bbox;
1356
1357 switch( m_shape )
1358 {
1359 case SHAPE_T::RECTANGLE:
1360 for( VECTOR2I& pt : GetRectCorners() )
1361 bbox.Merge( pt );
1362
1363 break;
1364
1365 case SHAPE_T::SEGMENT:
1366 bbox.SetOrigin( GetStart() );
1367 bbox.SetEnd( GetEnd() );
1368 break;
1369
1370 case SHAPE_T::CIRCLE:
1371 bbox.SetOrigin( GetStart() );
1372 bbox.Inflate( GetRadius() );
1373 break;
1374
1375 case SHAPE_T::ARC:
1376 computeArcBBox( bbox );
1377 break;
1378
1379 case SHAPE_T::POLY:
1380 if( GetPolyShape().IsEmpty() )
1381 break;
1382
1383 for( auto iter = GetPolyShape().CIterate(); iter; iter++ )
1384 bbox.Merge( *iter );
1385
1386 break;
1387
1388 case SHAPE_T::BEZIER:
1389 // Bezier BBoxes are not trivial to compute, so we approximate it by
1390 // using the bounding box of the curve (not control!) points.
1391 for( const VECTOR2I& pt : m_bezierPoints )
1392 bbox.Merge( pt );
1393
1394 break;
1395
1396 default:
1398 break;
1399 }
1400
1401 bbox.Inflate( std::max( 0, GetWidth() ) / 2 );
1402 bbox.Normalize();
1403
1404 return bbox;
1405}
1406
1407
1408bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
1409{
1410 double maxdist = aAccuracy;
1411
1412 if( GetWidth() > 0 )
1413 maxdist += GetWidth() / 2.0;
1414
1415 switch( m_shape )
1416 {
1417 case SHAPE_T::CIRCLE:
1418 {
1419 double radius = GetRadius();
1420 double dist = aPosition.Distance( getCenter() );
1421
1422 if( IsFilledForHitTesting() )
1423 return dist <= radius + maxdist; // Filled circle hit-test
1424 else if( abs( radius - dist ) <= maxdist ) // Ring hit-test
1425 return true;
1426
1427 if( IsHatchedFill() && GetHatching().Collide( aPosition, maxdist ) )
1428 return true;
1429
1430 return false;
1431 }
1432
1433 case SHAPE_T::ARC:
1434 {
1435 if( aPosition.Distance( m_start ) <= maxdist )
1436 return true;
1437
1438 if( aPosition.Distance( m_end ) <= maxdist )
1439 return true;
1440
1441 double radius = GetRadius();
1442 VECTOR2D relPos( VECTOR2D( aPosition ) - getCenter() );
1443 double dist = relPos.EuclideanNorm();
1444
1445 if( IsFilledForHitTesting() )
1446 {
1447 // Check distance from arc center
1448 if( dist > radius + maxdist )
1449 return false;
1450 }
1451 else
1452 {
1453 // Check distance from arc circumference
1454 if( abs( radius - dist ) > maxdist )
1455 return false;
1456 }
1457
1458 // Finally, check to see if it's within arc's swept angle.
1459 EDA_ANGLE startAngle;
1460 EDA_ANGLE endAngle;
1461 CalcArcAngles( startAngle, endAngle );
1462
1463 EDA_ANGLE relPosAngle( relPos );
1464
1465 startAngle.Normalize();
1466 endAngle.Normalize();
1467 relPosAngle.Normalize();
1468
1469 if( endAngle > startAngle )
1470 return relPosAngle >= startAngle && relPosAngle <= endAngle;
1471 else
1472 return relPosAngle >= startAngle || relPosAngle <= endAngle;
1473 }
1474
1475 case SHAPE_T::BEZIER:
1476 {
1477 const std::vector<VECTOR2I>* pts = &m_bezierPoints;
1478 std::vector<VECTOR2I> updatedBezierPoints;
1479
1480 if( m_bezierPoints.empty() )
1481 {
1483 converter.GetPoly( updatedBezierPoints, aAccuracy / 2 );
1484 pts = &updatedBezierPoints;
1485 }
1486
1487 for( unsigned int i = 1; i < pts->size(); i++ )
1488 {
1489 if( TestSegmentHit( aPosition, ( *pts )[i - 1], ( *pts )[i], maxdist ) )
1490 return true;
1491 }
1492
1493 return false;
1494 }
1495 case SHAPE_T::SEGMENT:
1496 return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
1497
1498 case SHAPE_T::RECTANGLE:
1499 if( IsProxyItem() || IsFilledForHitTesting() ) // Filled rect hit-test
1500 {
1501 SHAPE_POLY_SET poly;
1502 poly.NewOutline();
1503
1504 for( const VECTOR2I& pt : GetRectCorners() )
1505 poly.Append( pt );
1506
1507 return poly.Collide( aPosition, maxdist );
1508 }
1509 else if( m_cornerRadius > 0 )
1510 {
1512 SHAPE_POLY_SET poly;
1513 rr.TransformToPolygon( poly, getMaxError() );
1514
1515 if( poly.CollideEdge( aPosition, nullptr, maxdist ) )
1516 return true;
1517 }
1518 else
1519 {
1520 std::vector<VECTOR2I> pts = GetRectCorners();
1521
1522 if( TestSegmentHit( aPosition, pts[0], pts[1], maxdist )
1523 || TestSegmentHit( aPosition, pts[1], pts[2], maxdist )
1524 || TestSegmentHit( aPosition, pts[2], pts[3], maxdist )
1525 || TestSegmentHit( aPosition, pts[3], pts[0], maxdist ) )
1526 {
1527 return true;
1528 }
1529 }
1530
1531 if( IsHatchedFill() && GetHatching().Collide( aPosition, maxdist ) )
1532 return true;
1533
1534 return false;
1535
1536 case SHAPE_T::POLY:
1537 if( GetPolyShape().OutlineCount() < 1 ) // empty poly
1538 return false;
1539
1540 if( IsFilledForHitTesting() )
1541 {
1542 if( !GetPolyShape().COutline( 0 ).IsClosed() )
1543 {
1544 // Only one outline is expected
1545 SHAPE_LINE_CHAIN copy( GetPolyShape().COutline( 0 ) );
1546 copy.SetClosed( true );
1547 return copy.Collide( aPosition, maxdist );
1548 }
1549 else
1550 {
1551 return GetPolyShape().Collide( aPosition, maxdist );
1552 }
1553 }
1554 else
1555 {
1556 if( GetPolyShape().CollideEdge( aPosition, nullptr, maxdist ) )
1557 return true;
1558
1559 if( IsHatchedFill() && GetHatching().Collide( aPosition, maxdist ) )
1560 return true;
1561
1562 return false;
1563 }
1564
1565 default:
1567 return false;
1568 }
1569}
1570
1571
1572bool EDA_SHAPE::hitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
1573{
1574 BOX2I arect = aRect;
1575 arect.Normalize();
1576 arect.Inflate( aAccuracy );
1577
1578 BOX2I bbox = getBoundingBox();
1579
1580 auto checkOutline =
1581 [&]( const SHAPE_LINE_CHAIN& outline )
1582 {
1583 int count = (int) outline.GetPointCount();
1584
1585 for( int ii = 0; ii < count; ii++ )
1586 {
1587 VECTOR2I vertex = outline.GetPoint( ii );
1588
1589 // Test if the point is within aRect
1590 if( arect.Contains( vertex ) )
1591 return true;
1592
1593 if( ii + 1 < count )
1594 {
1595 VECTOR2I vertexNext = outline.GetPoint( ii + 1 );
1596
1597 // Test if this edge intersects aRect
1598 if( arect.Intersects( vertex, vertexNext ) )
1599 return true;
1600 }
1601 else if( outline.IsClosed() )
1602 {
1603 VECTOR2I vertexNext = outline.GetPoint( 0 );
1604
1605 // Test if this edge intersects aRect
1606 if( arect.Intersects( vertex, vertexNext ) )
1607 return true;
1608 }
1609 }
1610
1611 return false;
1612 };
1613
1614 switch( m_shape )
1615 {
1616 case SHAPE_T::CIRCLE:
1617 // Test if area intersects or contains the circle:
1618 if( aContained )
1619 {
1620 return arect.Contains( bbox );
1621 }
1622 else
1623 {
1624 // If the rectangle does not intersect the bounding box, this is a much quicker test
1625 if( !arect.Intersects( bbox ) )
1626 return false;
1627 else
1628 return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
1629 }
1630
1631 case SHAPE_T::ARC:
1632 // Test for full containment of this arc in the rect
1633 if( aContained )
1634 {
1635 return arect.Contains( bbox );
1636 }
1637 // Test if the rect crosses the arc
1638 else
1639 {
1640 if( !arect.Intersects( bbox ) )
1641 return false;
1642
1643 if( IsAnyFill() )
1644 {
1645 return ( arect.Intersects( getCenter(), GetStart() )
1646 || arect.Intersects( getCenter(), GetEnd() )
1647 || arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() ) );
1648 }
1649 else
1650 {
1651 return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
1652 }
1653 }
1654
1655 case SHAPE_T::RECTANGLE:
1656 if( aContained )
1657 {
1658 return arect.Contains( bbox );
1659 }
1660 else if( m_cornerRadius > 0 )
1661 {
1663 SHAPE_POLY_SET poly;
1664 rr.TransformToPolygon( poly, getMaxError() );
1665
1666 // Account for the width of the line
1667 arect.Inflate( GetWidth() / 2 );
1668
1669 return checkOutline( poly.Outline( 0 ) );
1670 }
1671 else
1672 {
1673 std::vector<VECTOR2I> pts = GetRectCorners();
1674
1675 // Account for the width of the lines
1676 arect.Inflate( GetWidth() / 2 );
1677 return ( arect.Intersects( pts[0], pts[1] )
1678 || arect.Intersects( pts[1], pts[2] )
1679 || arect.Intersects( pts[2], pts[3] )
1680 || arect.Intersects( pts[3], pts[0] ) );
1681 }
1682
1683 case SHAPE_T::SEGMENT:
1684 if( aContained )
1685 {
1686 return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
1687 }
1688 else
1689 {
1690 // Account for the width of the line
1691 arect.Inflate( GetWidth() / 2 );
1692 return arect.Intersects( GetStart(), GetEnd() );
1693 }
1694
1695 case SHAPE_T::POLY:
1696 if( aContained )
1697 {
1698 return arect.Contains( bbox );
1699 }
1700 else
1701 {
1702 // Fast test: if aRect is outside the polygon bounding box,
1703 // rectangles cannot intersect
1704 if( !arect.Intersects( bbox ) )
1705 return false;
1706
1707 // Account for the width of the line
1708 arect.Inflate( GetWidth() / 2 );
1709
1710 for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
1711 {
1712 if( checkOutline( GetPolyShape().Outline( ii ) ) )
1713 return true;
1714 }
1715
1716 return false;
1717 }
1718
1719 case SHAPE_T::BEZIER:
1720 if( aContained )
1721 {
1722 return arect.Contains( bbox );
1723 }
1724 else
1725 {
1726 // Fast test: if aRect is outside the polygon bounding box,
1727 // rectangles cannot intersect
1728 if( !arect.Intersects( bbox ) )
1729 return false;
1730
1731 // Account for the width of the line
1732 arect.Inflate( GetWidth() / 2 );
1733 const std::vector<VECTOR2I>* pts = &m_bezierPoints;
1734 std::vector<VECTOR2I> updatedBezierPoints;
1735
1736 if( m_bezierPoints.empty() )
1737 {
1739 converter.GetPoly( updatedBezierPoints, aAccuracy / 2 );
1740 pts = &updatedBezierPoints;
1741 }
1742
1743 for( unsigned ii = 1; ii < pts->size(); ii++ )
1744 {
1745 VECTOR2I vertex = ( *pts )[ii - 1];
1746 VECTOR2I vertexNext = ( *pts )[ii];
1747
1748 // Test if the point is within aRect
1749 if( arect.Contains( vertex ) )
1750 return true;
1751
1752 // Test if this edge intersects aRect
1753 if( arect.Intersects( vertex, vertexNext ) )
1754 return true;
1755 }
1756
1757 return false;
1758 }
1759
1760 default:
1762 return false;
1763 }
1764}
1765
1766
1767bool EDA_SHAPE::hitTest( const SHAPE_LINE_CHAIN& aPoly, bool aContained ) const
1768{
1770
1771 return KIGEOM::ShapeHitTest( aPoly, shape, aContained );
1772}
1773
1774
1775std::vector<VECTOR2I> EDA_SHAPE::GetRectCorners() const
1776{
1777 std::vector<VECTOR2I> pts;
1778 VECTOR2I topLeft = GetStart();
1779 VECTOR2I botRight = GetEnd();
1780
1781 pts.emplace_back( topLeft );
1782 pts.emplace_back( botRight.x, topLeft.y );
1783 pts.emplace_back( botRight );
1784 pts.emplace_back( topLeft.x, botRight.y );
1785
1786 return pts;
1787}
1788
1789
1790std::vector<VECTOR2I> EDA_SHAPE::GetCornersInSequence( EDA_ANGLE angle ) const
1791{
1792 std::vector<VECTOR2I> pts;
1793
1794 angle.Normalize();
1795
1796 BOX2I bbox = getBoundingBox();
1797 bbox.Normalize();
1798
1799 if( angle.IsCardinal() )
1800 {
1801 if( angle == ANGLE_0 )
1802 {
1803 pts.emplace_back( VECTOR2I( bbox.GetLeft(), bbox.GetTop() ) );
1804 pts.emplace_back( VECTOR2I( bbox.GetRight(), bbox.GetTop() ) );
1805 pts.emplace_back( VECTOR2I( bbox.GetRight(), bbox.GetBottom() ) );
1806 pts.emplace_back( VECTOR2I( bbox.GetLeft(), bbox.GetBottom() ) );
1807 }
1808 else if( angle == ANGLE_90 )
1809 {
1810 pts.emplace_back( VECTOR2I( bbox.GetLeft(), bbox.GetBottom() ) );
1811 pts.emplace_back( VECTOR2I( bbox.GetLeft(), bbox.GetTop() ) );
1812 pts.emplace_back( VECTOR2I( bbox.GetRight(), bbox.GetTop() ) );
1813 pts.emplace_back( VECTOR2I( bbox.GetRight(), bbox.GetBottom() ) );
1814 }
1815 else if( angle == ANGLE_180 )
1816 {
1817 pts.emplace_back( VECTOR2I( bbox.GetRight(), bbox.GetBottom() ) );
1818 pts.emplace_back( VECTOR2I( bbox.GetLeft(), bbox.GetBottom() ) );
1819 pts.emplace_back( VECTOR2I( bbox.GetLeft(), bbox.GetTop() ) );
1820 pts.emplace_back( VECTOR2I( bbox.GetRight(), bbox.GetTop() ) );
1821 }
1822 else if( angle == ANGLE_270 )
1823 {
1824 pts.emplace_back( VECTOR2I( bbox.GetRight(), bbox.GetTop() ) );
1825 pts.emplace_back( VECTOR2I( bbox.GetRight(), bbox.GetBottom() ) );
1826 pts.emplace_back( VECTOR2I( bbox.GetLeft(), bbox.GetBottom() ) );
1827 pts.emplace_back( VECTOR2I( bbox.GetLeft(), bbox.GetTop() ) );
1828 }
1829 }
1830 else
1831 {
1832 // This function was originally located in pcb_textbox.cpp and was later moved to eda_shape.cpp.
1833 // As a result of this move, access to getCorners was lost, since it is defined in the PCB_SHAPE
1834 // class within pcb_shape.cpp and is not available in the current context.
1835 //
1836 // Additionally, GetRectCorners() cannot be used here, as it assumes the rectangle is rotated by
1837 // a cardinal angle. In non-cardinal cases, it returns incorrect values (e.g., (0, 0)).
1838 //
1839 // To address this, a portion of the getCorners implementation for SHAPE_T::POLY elements
1840 // has been replicated here to restore the correct behavior.
1841 std::vector<VECTOR2I> corners;
1842
1843 for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
1844 {
1845 for( const VECTOR2I& pt : GetPolyShape().Outline( ii ).CPoints() )
1846 corners.emplace_back( pt );
1847 }
1848
1849 while( corners.size() < 4 )
1850 corners.emplace_back( corners.back() + VECTOR2I( 10, 10 ) );
1851
1852 VECTOR2I minX = corners[0];
1853 VECTOR2I maxX = corners[0];
1854 VECTOR2I minY = corners[0];
1855 VECTOR2I maxY = corners[0];
1856
1857 for( const VECTOR2I& corner : corners )
1858 {
1859 if( corner.x < minX.x )
1860 minX = corner;
1861
1862 if( corner.x > maxX.x )
1863 maxX = corner;
1864
1865 if( corner.y < minY.y )
1866 minY = corner;
1867
1868 if( corner.y > maxY.y )
1869 maxY = corner;
1870 }
1871
1872 if( angle < ANGLE_90 )
1873 {
1874 pts.emplace_back( minX );
1875 pts.emplace_back( minY );
1876 pts.emplace_back( maxX );
1877 pts.emplace_back( maxY );
1878 }
1879 else if( angle < ANGLE_180 )
1880 {
1881 pts.emplace_back( maxY );
1882 pts.emplace_back( minX );
1883 pts.emplace_back( minY );
1884 pts.emplace_back( maxX );
1885 }
1886 else if( angle < ANGLE_270 )
1887 {
1888 pts.emplace_back( maxX );
1889 pts.emplace_back( maxY );
1890 pts.emplace_back( minX );
1891 pts.emplace_back( minY );
1892 }
1893 else
1894 {
1895 pts.emplace_back( minY );
1896 pts.emplace_back( maxX );
1897 pts.emplace_back( maxY );
1898 pts.emplace_back( minX );
1899 }
1900 }
1901
1902 return pts;
1903}
1904
1905
1907{
1908 // Start, end, and each inflection point the arc crosses will enclose the entire arc.
1909 // Only include the center when filled; it's not necessarily inside the BB of an unfilled
1910 // arc with a small included angle.
1911 aBBox.SetOrigin( m_start );
1912 aBBox.Merge( m_end );
1913
1914 if( IsAnyFill() )
1915 aBBox.Merge( m_arcCenter );
1916
1917 int radius = GetRadius();
1918 EDA_ANGLE t1, t2;
1919
1920 CalcArcAngles( t1, t2 );
1921
1922 t1.Normalize();
1923 t2.Normalize();
1924
1925 if( t2 > t1 )
1926 {
1927 if( t1 < ANGLE_0 && t2 > ANGLE_0 )
1928 aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
1929
1930 if( t1 < ANGLE_90 && t2 > ANGLE_90 )
1931 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
1932
1933 if( t1 < ANGLE_180 && t2 > ANGLE_180 )
1934 aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
1935
1936 if( t1 < ANGLE_270 && t2 > ANGLE_270 )
1937 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
1938 }
1939 else
1940 {
1941 if( t1 < ANGLE_0 || t2 > ANGLE_0 )
1942 aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
1943
1944 if( t1 < ANGLE_90 || t2 > ANGLE_90 )
1945 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
1946
1947 if( t1 < ANGLE_180 || t2 > ANGLE_180 )
1948 aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
1949
1950 if( t1 < ANGLE_270 || t2 > ANGLE_270 )
1951 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
1952 }
1953}
1954
1955
1956void EDA_SHAPE::SetPolyPoints( const std::vector<VECTOR2I>& aPoints )
1957{
1960
1961 for( const VECTOR2I& p : aPoints )
1962 GetPolyShape().Append( p.x, p.y );
1963}
1964
1965
1966std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineChainOnly, bool aHittesting ) const
1967{
1968 std::vector<SHAPE*> effectiveShapes;
1969 int width = GetEffectiveWidth();
1970 bool solidFill = IsSolidFill()
1971 || IsHatchedFill()
1972 || IsProxyItem()
1973 || ( aHittesting && IsFilledForHitTesting() );
1974
1975 if( aEdgeOnly )
1976 solidFill = false;
1977
1978 switch( m_shape )
1979 {
1980 case SHAPE_T::ARC:
1981 effectiveShapes.emplace_back( new SHAPE_ARC( m_arcCenter, m_start, GetArcAngle(), width ) );
1982 break;
1983
1984 case SHAPE_T::SEGMENT:
1985 effectiveShapes.emplace_back( new SHAPE_SEGMENT( m_start, m_end, width ) );
1986 break;
1987
1988 case SHAPE_T::RECTANGLE:
1989 {
1990 if( m_cornerRadius > 0 )
1991 {
1993 SHAPE_POLY_SET poly;
1994 rr.TransformToPolygon( poly, getMaxError() );
1995 SHAPE_LINE_CHAIN outline = poly.Outline( 0 );
1996
1997 if( solidFill )
1998 effectiveShapes.emplace_back( new SHAPE_SIMPLE( outline ) );
1999
2000 if( width > 0 || !solidFill )
2001 {
2002 std::set<size_t> arcsHandled;
2003
2004 for( int ii = 0; ii < outline.SegmentCount(); ++ii )
2005 {
2006 if( outline.IsArcSegment( ii ) )
2007 {
2008 size_t arcIndex = outline.ArcIndex( ii );
2009
2010 if( !arcsHandled.contains( arcIndex ) )
2011 {
2012 arcsHandled.insert( arcIndex );
2013 effectiveShapes.emplace_back( new SHAPE_ARC( outline.Arc( arcIndex ), width ) );
2014 }
2015 }
2016 else
2017 {
2018 effectiveShapes.emplace_back( new SHAPE_SEGMENT( outline.Segment( ii ), width ) );
2019 }
2020 }
2021 }
2022 }
2023 else
2024 {
2025 std::vector<VECTOR2I> pts = GetRectCorners();
2026
2027 if( solidFill )
2028 effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
2029
2030 if( width > 0 || !solidFill )
2031 {
2032 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], width ) );
2033 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], width ) );
2034 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], width ) );
2035 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], width ) );
2036 }
2037 }
2038 break;
2039 }
2040
2041 case SHAPE_T::CIRCLE:
2042 {
2043 if( solidFill )
2044 effectiveShapes.emplace_back( new SHAPE_CIRCLE( getCenter(), GetRadius() ) );
2045
2046 if( width > 0 || !solidFill )
2047 effectiveShapes.emplace_back( new SHAPE_ARC( getCenter(), GetEnd(), ANGLE_360, width ) );
2048
2049 break;
2050 }
2051
2052 case SHAPE_T::BEZIER:
2053 {
2054 std::vector<VECTOR2I> bezierPoints = buildBezierToSegmentsPointsList( getMaxError() );
2055 VECTOR2I start_pt = bezierPoints[0];
2056
2057 for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ )
2058 {
2059 VECTOR2I end_pt = bezierPoints[jj];
2060 effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, width ) );
2061 start_pt = end_pt;
2062 }
2063
2064 break;
2065 }
2066
2067 case SHAPE_T::POLY:
2068 {
2069 if( GetPolyShape().OutlineCount() == 0 ) // malformed/empty polygon
2070 break;
2071
2072 for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
2073 {
2074 const SHAPE_LINE_CHAIN& l = GetPolyShape().COutline( ii );
2075
2076 if( solidFill )
2077 effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) );
2078
2079 if( width > 0 || !IsSolidFill() || aEdgeOnly )
2080 {
2081 int segCount = l.SegmentCount();
2082
2083 if( aLineChainOnly && l.IsClosed() )
2084 segCount--; // Treat closed chain as open
2085
2086 for( int jj = 0; jj < segCount; jj++ )
2087 effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.CSegment( jj ), width ) );
2088 }
2089 }
2090 }
2091 break;
2092
2093 default:
2095 break;
2096 }
2097
2098 return effectiveShapes;
2099}
2100
2101
2102std::vector<VECTOR2I> EDA_SHAPE::GetPolyPoints() const
2103{
2104 std::vector<VECTOR2I> points;
2105
2106 for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
2107 {
2108 const SHAPE_LINE_CHAIN& outline = GetPolyShape().COutline( ii );
2109 int pointCount = outline.PointCount();
2110
2111 if( pointCount )
2112 {
2113 points.reserve( points.size() + pointCount );
2114
2115 for( const VECTOR2I& pt : outline.CPoints() )
2116 points.emplace_back( pt );
2117 }
2118 }
2119
2120 return points;
2121}
2122
2123
2125{
2126 if( !m_poly )
2127 m_poly = std::make_unique<SHAPE_POLY_SET>();
2128
2129 return *m_poly;
2130}
2131
2133{
2134 if( !m_poly )
2135 m_poly = std::make_unique<SHAPE_POLY_SET>();
2136
2137 return *m_poly;
2138}
2139
2140
2142{
2143 // return true if the polygonal shape is valid (has more than 2 points)
2144 return GetPolyShape().OutlineCount() > 0 && GetPolyShape().Outline( 0 ).PointCount() > 2;
2145}
2146
2147
2149{
2150 // return the number of corners of the polygonal shape
2151 // this shape is expected to be only one polygon without hole
2152 return GetPolyShape().OutlineCount() ? GetPolyShape().VertexCount( 0 ) : 0;
2153}
2154
2155
2156void EDA_SHAPE::beginEdit( const VECTOR2I& aPosition )
2157{
2158 switch( GetShape() )
2159 {
2160 case SHAPE_T::SEGMENT:
2161 case SHAPE_T::CIRCLE:
2162 case SHAPE_T::RECTANGLE:
2163 SetStart( aPosition );
2164 SetEnd( aPosition );
2165 break;
2166
2167 case SHAPE_T::ARC:
2168 SetArcGeometry( aPosition, aPosition, aPosition );
2169 m_editState = 1;
2170 break;
2171
2172 case SHAPE_T::BEZIER:
2173 SetStart( aPosition );
2174 SetEnd( aPosition );
2175 SetBezierC1( aPosition );
2176 SetBezierC2( aPosition );
2177 m_editState = 1;
2178
2180 break;
2181
2182 case SHAPE_T::POLY:
2184 GetPolyShape().Outline( 0 ).SetClosed( false );
2185
2186 // Start and end of the first segment (co-located for now)
2187 GetPolyShape().Outline( 0 ).Append( aPosition );
2188 GetPolyShape().Outline( 0 ).Append( aPosition, true );
2189 break;
2190
2191 default:
2193 }
2194}
2195
2196
2197bool EDA_SHAPE::continueEdit( const VECTOR2I& aPosition )
2198{
2199 switch( GetShape() )
2200 {
2201 case SHAPE_T::ARC:
2202 case SHAPE_T::SEGMENT:
2203 case SHAPE_T::CIRCLE:
2204 case SHAPE_T::RECTANGLE:
2205 return false;
2206
2207 case SHAPE_T::BEZIER:
2208 if( m_editState == 3 )
2209 return false;
2210
2211 m_editState++;
2212 return true;
2213
2214 case SHAPE_T::POLY:
2215 {
2216 SHAPE_LINE_CHAIN& poly = GetPolyShape().Outline( 0 );
2217
2218 // do not add zero-length segments
2219 if( poly.CPoint( (int) poly.GetPointCount() - 2 ) != poly.CLastPoint() )
2220 poly.Append( aPosition, true );
2221 }
2222 return true;
2223
2224 default:
2226 return false;
2227 }
2228}
2229
2230
2231void EDA_SHAPE::calcEdit( const VECTOR2I& aPosition )
2232{
2233#define sq( x ) pow( x, 2 )
2234
2235 switch( GetShape() )
2236 {
2237 case SHAPE_T::SEGMENT:
2238 case SHAPE_T::CIRCLE:
2239 case SHAPE_T::RECTANGLE:
2240 SetEnd( aPosition );
2241 break;
2242
2243 case SHAPE_T::BEZIER:
2244 {
2245 switch( m_editState )
2246 {
2247 case 0:
2248 SetStart( aPosition );
2249 SetEnd( aPosition );
2250 SetBezierC1( aPosition );
2251 SetBezierC2( aPosition );
2252 break;
2253
2254 case 1:
2255 SetBezierC2( aPosition );
2256 SetEnd( aPosition );
2257 break;
2258
2259 case 2:
2260 SetBezierC1( aPosition );
2261 break;
2262
2263 case 3:
2264 SetBezierC2( aPosition );
2265 break;
2266 }
2267
2269 }
2270 break;
2271
2272 case SHAPE_T::ARC:
2273 {
2274 double radius = GetRadius();
2275 EDA_ANGLE lastAngle = GetArcAngle();
2276
2277 // Edit state 0: drawing: place start
2278 // Edit state 1: drawing: place end (center calculated for 90-degree subtended angle)
2279 // Edit state 2: point edit: move start (center calculated for invariant subtended angle)
2280 // Edit state 3: point edit: move end (center calculated for invariant subtended angle)
2281 // Edit state 4: point edit: move center
2282 // Edit state 5: point edit: move arc-mid-point
2283
2284 switch( m_editState )
2285 {
2286 case 0:
2287 SetArcGeometry( aPosition, aPosition, aPosition );
2288 return;
2289
2290 case 1:
2291 m_end = aPosition;
2292 radius = m_start.Distance( m_end ) * M_SQRT1_2;
2293 break;
2294
2295 case 2:
2296 case 3:
2297 {
2298 VECTOR2I v = m_start - m_end;
2299 double chordBefore = v.SquaredEuclideanNorm();
2300
2301 if( m_editState == 2 )
2302 m_start = aPosition;
2303 else
2304 m_end = aPosition;
2305
2306 v = m_start - m_end;
2307
2308 double chordAfter = v.SquaredEuclideanNorm();
2309 double ratio = 0.0;
2310
2311 if( chordBefore > 0 )
2312 ratio = chordAfter / chordBefore;
2313
2314 if( ratio != 0 )
2315 radius = std::max( sqrt( sq( radius ) * ratio ), sqrt( chordAfter ) / 2 );
2316 break;
2317 }
2318
2319 case 4:
2320 {
2321 double radialA = m_start.Distance( aPosition );
2322 double radialB = m_end.Distance( aPosition );
2323 radius = ( radialA + radialB ) / 2.0;
2324 break;
2325 }
2326
2327 case 5:
2328 SetArcGeometry( GetStart(), aPosition, GetEnd() );
2329 return;
2330 }
2331
2332 // Calculate center based on start, end, and radius
2333 //
2334 // Let 'l' be the length of the chord and 'm' the middle point of the chord
2335 double l = m_start.Distance( m_end );
2336 VECTOR2D m = ( m_start + m_end ) / 2;
2337 double sqRadDiff = ( radius * radius ) - ( l * l ) / 4.0;
2338
2339 // Calculate 'd', the vector from the chord midpoint to the center
2340 VECTOR2D d;
2341
2342 if( l > 0 && sqRadDiff >= 0 )
2343 {
2344 d.x = sqrt( sqRadDiff ) * ( m_start.y - m_end.y ) / l;
2345 d.y = sqrt( sqRadDiff ) * ( m_end.x - m_start.x ) / l;
2346 }
2347
2348 VECTOR2I c1 = KiROUND( m + d );
2349 VECTOR2I c2 = KiROUND( m - d );
2350
2351 // Solution gives us 2 centers; we need to pick one:
2352 switch( m_editState )
2353 {
2354 case 1:
2355 // Keep arc clockwise while drawing i.e. arc angle = 90 deg.
2356 // it can be 90 or 270 deg depending on the arc center choice (c1 or c2)
2357 m_arcCenter = c1; // first trial
2358
2359 if( GetArcAngle() > ANGLE_180 )
2360 m_arcCenter = c2;
2361
2362 break;
2363
2364 case 2:
2365 case 3:
2366 // Pick the one of c1, c2 to keep arc on the same side
2367 m_arcCenter = c1; // first trial
2368
2369 if( ( lastAngle < ANGLE_180 ) != ( GetArcAngle() < ANGLE_180 ) )
2370 m_arcCenter = c2;
2371
2372 break;
2373
2374 case 4:
2375 // Pick the one closer to the mouse position
2376 m_arcCenter = c1.Distance( aPosition ) < c2.Distance( aPosition ) ? c1 : c2;
2377 break;
2378 }
2379
2380 break;
2381 }
2382
2383 case SHAPE_T::POLY:
2384 GetPolyShape().Outline( 0 ).SetPoint( GetPolyShape().Outline( 0 ).GetPointCount() - 1,
2385 aPosition );
2386 break;
2387
2388 default:
2390 }
2391}
2392
2393
2394void EDA_SHAPE::endEdit( bool aClosed )
2395{
2396 switch( GetShape() )
2397 {
2398 case SHAPE_T::ARC:
2399 case SHAPE_T::SEGMENT:
2400 case SHAPE_T::CIRCLE:
2401 case SHAPE_T::RECTANGLE:
2402 case SHAPE_T::BEZIER:
2403 break;
2404
2405 case SHAPE_T::POLY:
2406 {
2407 SHAPE_LINE_CHAIN& poly = GetPolyShape().Outline( 0 );
2408
2409 // do not include last point twice
2410 if( poly.GetPointCount() > 2 )
2411 {
2412 if( poly.CPoint( poly.GetPointCount() - 2 ) == poly.CLastPoint() )
2413 {
2414 poly.SetClosed( aClosed );
2415 }
2416 else
2417 {
2418 poly.SetClosed( false );
2419 poly.Remove( poly.GetPointCount() - 1 );
2420 }
2421 }
2422
2423 break;
2424 }
2425
2426 default:
2428 }
2429}
2430
2431
2433{
2434 EDA_SHAPE* image = dynamic_cast<EDA_SHAPE*>( aImage );
2435 assert( image );
2436
2437 #define SWAPITEM( x ) std::swap( x, image->x )
2438 SWAPITEM( m_stroke );
2439 SWAPITEM( m_start );
2440 SWAPITEM( m_end );
2442 SWAPITEM( m_shape );
2446 SWAPITEM( m_poly );
2448 SWAPITEM( m_fill );
2452 #undef SWAPITEM
2453
2454 m_hatchingDirty = true;
2455}
2456
2457
2458int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const
2459{
2460#define EPSILON 2 // Should be enough for rounding errors on calculated items
2461
2462#define TEST( a, b ) { if( a != b ) return a - b; }
2463#define TEST_E( a, b ) { if( abs( a - b ) > EPSILON ) return a - b; }
2464#define TEST_PT( a, b ) { TEST_E( a.x, b.x ); TEST_E( a.y, b.y ); }
2465
2466 TEST_PT( m_start, aOther->m_start );
2467 TEST_PT( m_end, aOther->m_end );
2468
2469 TEST( (int) m_shape, (int) aOther->m_shape );
2470
2472 {
2474 }
2475 else if( m_shape == SHAPE_T::ARC )
2476 {
2477 TEST_PT( GetArcMid(), aOther->GetArcMid() );
2478 }
2479 else if( m_shape == SHAPE_T::BEZIER )
2480 {
2481 TEST_PT( m_bezierC1, aOther->m_bezierC1 );
2482 TEST_PT( m_bezierC2, aOther->m_bezierC2 );
2483 }
2484 else if( m_shape == SHAPE_T::POLY )
2485 {
2486 TEST( GetPolyShape().TotalVertices(), aOther->GetPolyShape().TotalVertices() );
2487 }
2488
2489 for( size_t ii = 0; ii < m_bezierPoints.size(); ++ii )
2490 TEST_PT( m_bezierPoints[ii], aOther->m_bezierPoints[ii] );
2491
2492 for( int ii = 0; ii < GetPolyShape().TotalVertices(); ++ii )
2493 TEST_PT( GetPolyShape().CVertex( ii ), aOther->GetPolyShape().CVertex( ii ) );
2494
2495 TEST_E( m_stroke.GetWidth(), aOther->m_stroke.GetWidth() );
2496 TEST( (int) m_stroke.GetLineStyle(), (int) aOther->m_stroke.GetLineStyle() );
2497 TEST( (int) m_fill, (int) aOther->m_fill );
2498
2499 return 0;
2500}
2501
2502
2503void EDA_SHAPE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, int aClearance, int aError,
2504 ERROR_LOC aErrorLoc, bool ignoreLineWidth, bool includeFill ) const
2505{
2506 bool solidFill = IsSolidFill() || ( IsHatchedFill() && !includeFill ) || IsProxyItem();
2507 int width = ignoreLineWidth ? 0 : GetWidth();
2508
2509 width += 2 * aClearance;
2510
2511 switch( m_shape )
2512 {
2513 case SHAPE_T::CIRCLE:
2514 {
2515 int r = GetRadius();
2516
2517 if( solidFill )
2518 TransformCircleToPolygon( aBuffer, getCenter(), r + width / 2, aError, aErrorLoc );
2519 else
2520 TransformRingToPolygon( aBuffer, getCenter(), r, width, aError, aErrorLoc );
2521
2522 break;
2523 }
2524
2525 case SHAPE_T::RECTANGLE:
2526 {
2527 if( GetCornerRadius() > 0 )
2528 {
2530 BOX2I bbox = getBoundingBox();
2531 VECTOR2I position = bbox.GetCenter();
2532
2533 if( solidFill )
2534 {
2536 0.0, 0, width / 2, aError, aErrorLoc );
2537 }
2538 else
2539 {
2541 SHAPE_POLY_SET poly;
2542 rr.TransformToPolygon( poly, aError );
2543 SHAPE_LINE_CHAIN& outline = poly.Outline( 0 );
2544 outline.SetClosed( true );
2545
2546 std::set<size_t> arcsHandled;
2547
2548 for( int ii = 0; ii < outline.SegmentCount(); ++ii )
2549 {
2550 if( outline.IsArcSegment( ii ) )
2551 {
2552 size_t arcIndex = outline.ArcIndex( ii );
2553
2554 if( arcsHandled.contains( arcIndex ) )
2555 continue;
2556
2557 arcsHandled.insert( arcIndex );
2558
2559 const SHAPE_ARC& arc = outline.Arc( arcIndex );
2560 TransformArcToPolygon( aBuffer, arc.GetP0(), arc.GetArcMid(), arc.GetP1(), width, aError,
2561 aErrorLoc );
2562 }
2563 else
2564 {
2565 const SEG& seg = outline.GetSegment( ii );
2566 TransformOvalToPolygon( aBuffer, seg.A, seg.B, width, aError, aErrorLoc );
2567 }
2568 }
2569 }
2570 }
2571 else
2572 {
2573 std::vector<VECTOR2I> pts = GetRectCorners();
2574
2575 if( solidFill )
2576 {
2577 aBuffer.NewOutline();
2578
2579 for( const VECTOR2I& pt : pts )
2580 aBuffer.Append( pt );
2581 }
2582
2583 if( width > 0 || !solidFill )
2584 {
2585 // Add in segments
2586 TransformOvalToPolygon( aBuffer, pts[0], pts[1], width, aError, aErrorLoc );
2587 TransformOvalToPolygon( aBuffer, pts[1], pts[2], width, aError, aErrorLoc );
2588 TransformOvalToPolygon( aBuffer, pts[2], pts[3], width, aError, aErrorLoc );
2589 TransformOvalToPolygon( aBuffer, pts[3], pts[0], width, aError, aErrorLoc );
2590 }
2591 }
2592
2593 break;
2594 }
2595
2596 case SHAPE_T::ARC:
2597 TransformArcToPolygon( aBuffer, GetStart(), GetArcMid(), GetEnd(), width, aError, aErrorLoc );
2598 break;
2599
2600 case SHAPE_T::SEGMENT:
2601 TransformOvalToPolygon( aBuffer, GetStart(), GetEnd(), width, aError, aErrorLoc );
2602 break;
2603
2604 case SHAPE_T::POLY:
2605 {
2606 if( !IsPolyShapeValid() )
2607 break;
2608
2609 if( solidFill )
2610 {
2611 for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
2612 {
2613 const SHAPE_LINE_CHAIN& poly = GetPolyShape().Outline( ii );
2614 SHAPE_POLY_SET tmp;
2615 tmp.NewOutline();
2616
2617 for( int jj = 0; jj < (int) poly.GetPointCount(); ++jj )
2618 tmp.Append( poly.GetPoint( jj ) );
2619
2620 if( width > 0 )
2621 {
2622 int inflate = width / 2;
2623
2624 if( aErrorLoc == ERROR_OUTSIDE )
2625 inflate += aError;
2626
2627 tmp.Inflate( inflate, CORNER_STRATEGY::ROUND_ALL_CORNERS, aError );
2628 }
2629
2630 aBuffer.Append( tmp );
2631 }
2632 }
2633 else
2634 {
2635 for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
2636 {
2637 const SHAPE_LINE_CHAIN& poly = GetPolyShape().Outline( ii );
2638
2639 for( int jj = 0; jj < (int) poly.SegmentCount(); ++jj )
2640 {
2641 const SEG& seg = poly.GetSegment( jj );
2642 TransformOvalToPolygon( aBuffer, seg.A, seg.B, width, aError, aErrorLoc );
2643 }
2644 }
2645 }
2646
2647 break;
2648 }
2649
2650 case SHAPE_T::BEZIER:
2651 {
2652 std::vector<VECTOR2I> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
2653 BEZIER_POLY converter( ctrlPts );
2654 std::vector<VECTOR2I> poly;
2655 converter.GetPoly( poly, aError );
2656
2657 for( unsigned ii = 1; ii < poly.size(); ii++ )
2658 TransformOvalToPolygon( aBuffer, poly[ii - 1], poly[ii], width, aError, aErrorLoc );
2659
2660 break;
2661 }
2662
2663 default:
2665 break;
2666 }
2667
2668 if( IsHatchedFill() && includeFill )
2669 {
2670 for( int ii = 0; ii < GetHatching().OutlineCount(); ++ii )
2671 aBuffer.AddOutline( GetHatching().COutline( ii ) );
2672 }
2673}
2674
2675
2676void EDA_SHAPE::SetWidth( int aWidth )
2677{
2678 m_stroke.SetWidth( aWidth );
2679 m_hatchingDirty = true;
2680}
2681
2682
2684{
2685 m_stroke.SetLineStyle( aStyle );
2686}
2687
2688
2690{
2691 if( m_stroke.GetLineStyle() != LINE_STYLE::DEFAULT )
2692 return m_stroke.GetLineStyle();
2693
2694 return LINE_STYLE::SOLID;
2695}
2696
2697
2698bool EDA_SHAPE::operator==( const EDA_SHAPE& aOther ) const
2699{
2700 if( GetShape() != aOther.GetShape() )
2701 return false;
2702
2703 if( m_fill != aOther.m_fill )
2704 return false;
2705
2706 if( m_stroke.GetWidth() != aOther.m_stroke.GetWidth() )
2707 return false;
2708
2709 if( m_stroke.GetLineStyle() != aOther.m_stroke.GetLineStyle() )
2710 return false;
2711
2712 if( m_fillColor != aOther.m_fillColor )
2713 return false;
2714
2715 switch( GetShape() )
2716 {
2717 case SHAPE_T::SEGMENT:
2718 case SHAPE_T::RECTANGLE:
2719 case SHAPE_T::CIRCLE:
2720 if( m_start != aOther.m_start )
2721 return false;
2722
2723 if( m_end != aOther.m_end )
2724 return false;
2725
2726 break;
2727
2728 case SHAPE_T::ARC:
2729 if( m_start != aOther.m_start )
2730 return false;
2731
2732 if( m_end != aOther.m_end )
2733 return false;
2734
2735 if( m_arcCenter != aOther.m_arcCenter )
2736 return false;
2737
2738 break;
2739
2740 case SHAPE_T::POLY:
2741 if( GetPolyShape().TotalVertices() != aOther.GetPolyShape().TotalVertices() )
2742 return false;
2743
2744 for( int ii = 0; ii < GetPolyShape().TotalVertices(); ++ii )
2745 {
2746 if( GetPolyShape().CVertex( ii ) != aOther.GetPolyShape().CVertex( ii ) )
2747 return false;
2748 }
2749
2750 break;
2751
2752 case SHAPE_T::BEZIER:
2753 if( m_start != aOther.m_start )
2754 return false;
2755
2756 if( m_end != aOther.m_end )
2757 return false;
2758
2759 if( m_bezierC1 != aOther.m_bezierC1 )
2760 return false;
2761
2762 if( m_bezierC2 != aOther.m_bezierC2 )
2763 return false;
2764
2765 if( m_bezierPoints != aOther.m_bezierPoints )
2766 return false;
2767
2768 break;
2769
2770 default:
2771 return false;
2772 }
2773
2774 return true;
2775}
2776
2777
2778double EDA_SHAPE::Similarity( const EDA_SHAPE& aOther ) const
2779{
2780 if( GetShape() != aOther.GetShape() )
2781 return 0.0;
2782
2783 double similarity = 1.0;
2784
2785 if( m_fill != aOther.m_fill )
2786 similarity *= 0.9;
2787
2788 if( m_stroke.GetWidth() != aOther.m_stroke.GetWidth() )
2789 similarity *= 0.9;
2790
2791 if( m_stroke.GetLineStyle() != aOther.m_stroke.GetLineStyle() )
2792 similarity *= 0.9;
2793
2794 if( m_fillColor != aOther.m_fillColor )
2795 similarity *= 0.9;
2796
2797 if( m_start != aOther.m_start )
2798 similarity *= 0.9;
2799
2800 if( m_end != aOther.m_end )
2801 similarity *= 0.9;
2802
2803 if( m_arcCenter != aOther.m_arcCenter )
2804 similarity *= 0.9;
2805
2806 if( m_bezierC1 != aOther.m_bezierC1 )
2807 similarity *= 0.9;
2808
2809 if( m_bezierC2 != aOther.m_bezierC2 )
2810 similarity *= 0.9;
2811
2812 {
2813 int m = m_bezierPoints.size();
2814 int n = aOther.m_bezierPoints.size();
2815
2816 size_t longest = alg::longest_common_subset( m_bezierPoints, aOther.m_bezierPoints );
2817
2818 similarity *= std::pow( 0.9, m + n - 2 * longest );
2819 }
2820
2821 {
2822 int m = GetPolyShape().TotalVertices();
2823 int n = aOther.GetPolyShape().TotalVertices();
2824 std::vector<VECTOR2I> poly;
2825 std::vector<VECTOR2I> otherPoly;
2826 VECTOR2I lastPt( 0, 0 );
2827
2828 // We look for the longest common subset of the two polygons, but we need to
2829 // offset each point because we're actually looking for overall similarity, not just
2830 // exact matches. So if the zone is moved by 1IU, we only want one point to be
2831 // considered "moved" rather than the entire polygon. In this case, the first point
2832 // will not be a match but the rest of the sequence will.
2833 for( int ii = 0; ii < m; ++ii )
2834 {
2835 poly.emplace_back( lastPt - GetPolyShape().CVertex( ii ) );
2836 lastPt = GetPolyShape().CVertex( ii );
2837 }
2838
2839 lastPt = VECTOR2I( 0, 0 );
2840
2841 for( int ii = 0; ii < n; ++ii )
2842 {
2843 otherPoly.emplace_back( lastPt - aOther.GetPolyShape().CVertex( ii ) );
2844 lastPt = aOther.GetPolyShape().CVertex( ii );
2845 }
2846
2847 size_t longest = alg::longest_common_subset( poly, otherPoly );
2848
2849 similarity *= std::pow( 0.9, m + n - 2 * longest );
2850 }
2851
2852 return similarity;
2853}
2854
2855
2859
2860
2861static struct EDA_SHAPE_DESC
2862{
2864 {
2866 .Map( SHAPE_T::SEGMENT, _HKI( "Segment" ) )
2867 .Map( SHAPE_T::RECTANGLE, _HKI( "Rectangle" ) )
2868 .Map( SHAPE_T::ARC, _HKI( "Arc" ) )
2869 .Map( SHAPE_T::CIRCLE, _HKI( "Circle" ) )
2870 .Map( SHAPE_T::POLY, _HKI( "Polygon" ) )
2871 .Map( SHAPE_T::BEZIER, _HKI( "Bezier" ) );
2872
2874
2875 if( lineStyleEnum.Choices().GetCount() == 0 )
2876 {
2877 lineStyleEnum.Map( LINE_STYLE::SOLID, _HKI( "Solid" ) )
2878 .Map( LINE_STYLE::DASH, _HKI( "Dashed" ) )
2879 .Map( LINE_STYLE::DOT, _HKI( "Dotted" ) )
2880 .Map( LINE_STYLE::DASHDOT, _HKI( "Dash-Dot" ) )
2881 .Map( LINE_STYLE::DASHDOTDOT, _HKI( "Dash-Dot-Dot" ) );
2882 }
2883
2885
2886 if( hatchModeEnum.Choices().GetCount() == 0 )
2887 {
2888 hatchModeEnum.Map( UI_FILL_MODE::NONE, _HKI( "None" ) );
2889 hatchModeEnum.Map( UI_FILL_MODE::SOLID, _HKI( "Solid" ) );
2890 hatchModeEnum.Map( UI_FILL_MODE::HATCH, _HKI( "Hatch" ) );
2891 hatchModeEnum.Map( UI_FILL_MODE::REVERSE_HATCH, _HKI( "Reverse Hatch" ) );
2892 hatchModeEnum.Map( UI_FILL_MODE::CROSS_HATCH, _HKI( "Cross-hatch" ) );
2893 }
2894
2897
2898 auto isNotPolygonOrCircle =
2899 []( INSPECTABLE* aItem ) -> bool
2900 {
2901 // Polygons, unlike other shapes, have no meaningful start or end coordinates
2902 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2903 return shape->GetShape() != SHAPE_T::POLY && shape->GetShape() != SHAPE_T::CIRCLE;
2904
2905 return false;
2906 };
2907
2908 auto isCircle =
2909 []( INSPECTABLE* aItem ) -> bool
2910 {
2911 // Polygons, unlike other shapes, have no meaningful start or end coordinates
2912 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2913 return shape->GetShape() == SHAPE_T::CIRCLE;
2914
2915 return false;
2916 };
2917
2918 auto isRectangle =
2919 []( INSPECTABLE* aItem ) -> bool
2920 {
2921 // Polygons, unlike other shapes, have no meaningful start or end coordinates
2922 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
2923 return shape->GetShape() == SHAPE_T::RECTANGLE;
2924
2925 return false;
2926 };
2927
2928 const wxString shapeProps = _HKI( "Shape Properties" );
2929
2930 auto shape = new PROPERTY_ENUM<EDA_SHAPE, SHAPE_T>( _HKI( "Shape" ),
2932 propMgr.AddProperty( shape, shapeProps );
2933
2934 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start X" ),
2937 shapeProps )
2938 .SetAvailableFunc( isNotPolygonOrCircle );
2939 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start Y" ),
2942 shapeProps )
2943 .SetAvailableFunc( isNotPolygonOrCircle );
2944
2945 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Center X" ),
2948 shapeProps )
2949 .SetAvailableFunc( isCircle );
2950
2951 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Center Y" ),
2954 shapeProps )
2955 .SetAvailableFunc( isCircle );
2956
2957 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Radius" ),
2960 shapeProps )
2961 .SetAvailableFunc( isCircle );
2962
2963 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End X" ),
2966 shapeProps )
2967 .SetAvailableFunc( isNotPolygonOrCircle );
2968
2969 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End Y" ),
2972 shapeProps )
2973 .SetAvailableFunc( isNotPolygonOrCircle );
2974
2975 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Width" ),
2978 shapeProps )
2979 .SetAvailableFunc( isRectangle );
2980
2981 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Height" ),
2984 shapeProps )
2985 .SetAvailableFunc( isRectangle );
2986
2987 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Corner Radius" ),
2990 shapeProps )
2991 .SetAvailableFunc( isRectangle )
2992 .SetValidator( []( const wxAny&& aValue, EDA_ITEM* aItem ) -> VALIDATOR_RESULT
2993 {
2994 wxASSERT_MSG( aValue.CheckType<int>(),
2995 "Expecting int-containing value" );
2996
2997 int radius = aValue.As<int>();
2998
2999 EDA_SHAPE* prop_shape = dynamic_cast<EDA_SHAPE*>( aItem );
3000
3001 if( !prop_shape )
3002 return std::nullopt;
3003
3004 int maxRadius = std::min( prop_shape->GetRectangleWidth(),
3005 prop_shape->GetRectangleHeight() ) / 2;
3006
3007 if( radius > maxRadius )
3008 return std::make_unique<VALIDATION_ERROR_TOO_LARGE<int>>( radius, maxRadius );
3009 else if( radius < 0 )
3010 return std::make_unique<VALIDATION_ERROR_TOO_SMALL<int>>( radius, 0 );
3011
3012 return std::nullopt;
3013 } );
3014
3015 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Line Width" ),
3017 shapeProps );
3018
3019 propMgr.AddProperty( new PROPERTY_ENUM<EDA_SHAPE, LINE_STYLE>( _HKI( "Line Style" ),
3021 shapeProps );
3022
3023 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, COLOR4D>( _HKI( "Line Color" ),
3025 shapeProps )
3027
3028 auto angle = new PROPERTY<EDA_SHAPE, EDA_ANGLE>( _HKI( "Angle" ),
3031 angle->SetAvailableFunc(
3032 [=]( INSPECTABLE* aItem ) -> bool
3033 {
3034 if( EDA_SHAPE* curr_shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
3035 return curr_shape->GetShape() == SHAPE_T::ARC;
3036
3037 return false;
3038 } );
3039 propMgr.AddProperty( angle, shapeProps );
3040
3041 auto fillAvailable =
3042 [=]( INSPECTABLE* aItem ) -> bool
3043 {
3044 if( EDA_ITEM* edaItem = dynamic_cast<EDA_ITEM*>( aItem ) )
3045 {
3046 // For some reason masking "Filled" and "Fill Color" at the
3047 // PCB_TABLECELL level doesn't work.
3048 if( edaItem->Type() == PCB_TABLECELL_T || edaItem->Type() == PCB_TEXTBOX_T )
3049 return false;
3050 }
3051
3052 if( EDA_SHAPE* edaShape = dynamic_cast<EDA_SHAPE*>( aItem ) )
3053 {
3054 switch( edaShape->GetShape() )
3055 {
3056 case SHAPE_T::POLY:
3057 case SHAPE_T::RECTANGLE:
3058 case SHAPE_T::CIRCLE:
3059 case SHAPE_T::BEZIER:
3060 return true;
3061
3062 default:
3063 return false;
3064 }
3065 }
3066
3067 return false;
3068 };
3069
3072 shapeProps )
3073 .SetAvailableFunc( fillAvailable );
3074
3075 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, COLOR4D>( _HKI( "Fill Color" ),
3077 shapeProps )
3078 .SetAvailableFunc( fillAvailable )
3080 }
types::KiCadObjectType ToProtoEnum(KICAD_T aValue)
KICAD_T FromProtoEnum(types::KiCadObjectType aValue)
Definition api_enums.cpp:44
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
@ ERROR_OUTSIDE
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:125
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
Bezier curves to polygon converter.
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 size_type GetWidth() const
Definition box2.h:214
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 const Vec GetCenter() const
Definition box2.h:230
constexpr size_type GetHeight() const
Definition box2.h:215
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
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:402
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:100
UI_FILL_MODE GetFillModeProp() const
virtual int GetHatchLineSpacing() const
Definition eda_shape.h:176
EDA_ANGLE GetArcAngle() const
SHAPE_T m_shape
Definition eda_shape.h:518
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.
void SetStartX(int x)
Definition eda_shape.h:208
bool m_proxyItem
Definition eda_shape.h:543
int m_cornerRadius
Definition eda_shape.h:528
bool m_hatchingDirty
Definition eda_shape.h:524
bool m_endsSwapped
Definition eda_shape.h:517
const VECTOR2I & GetBezierC2() const
Definition eda_shape.h:275
void SetBezierC2(const VECTOR2I &aPt)
Definition eda_shape.h:274
void move(const VECTOR2I &aMoveVector)
void SetCenter(const VECTOR2I &aCenter)
VECTOR2I getCenter() const
int GetStartY() const
Definition eda_shape.h:191
void SetFillModeProp(UI_FILL_MODE)
int m_editState
Definition eda_shape.h:542
virtual int getMaxError() const
Definition eda_shape.h:510
void rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle)
const std::vector< VECTOR2I > buildBezierToSegmentsPointsList(int aMaxError) const
const SHAPE_POLY_SET & GetHatching() const
FILL_T GetFillMode() const
Definition eda_shape.h:158
virtual ~EDA_SHAPE()
Definition eda_shape.cpp:71
void SetCornerRadius(int aRadius)
long long int m_rectangleHeight
Definition eda_shape.h:526
std::unique_ptr< EDA_SHAPE_HATCH_CACHE_DATA > m_hatchingCache
Definition eda_shape.h:523
void SetEndY(int aY)
Definition eda_shape.h:243
virtual int GetEffectiveWidth() const
Definition eda_shape.h:174
std::vector< VECTOR2I > GetPolyPoints() const
Duplicate the polygon outlines into a flat list of VECTOR2I points.
COLOR4D GetLineColor() const
Definition eda_shape.h:182
int GetEndX() const
Definition eda_shape.h:234
std::vector< SHAPE * > makeEffectiveShapes(bool aEdgeOnly, bool aLineChainOnly=false, bool aHittesting=false) const
Make a set of SHAPE objects representing the EDA_SHAPE.
int GetRectangleWidth() const
void SetLineStyle(const LINE_STYLE aStyle)
void calcEdit(const VECTOR2I &aPosition)
void SetStartY(int y)
Definition eda_shape.h:201
virtual std::vector< SHAPE * > MakeEffectiveShapes(bool aEdgeOnly=false) const
Make a set of SHAPE objects representing the EDA_SHAPE.
Definition eda_shape.h:394
SHAPE_POLY_SET & GetPolyShape()
void SetCenterY(int y)
Definition eda_shape.h:215
void CalcArcAngles(EDA_ANGLE &aStartAngle, EDA_ANGLE &aEndAngle) const
Calc arc start and end angles such that aStartAngle < aEndAngle.
std::vector< VECTOR2I > GetCornersInSequence(EDA_ANGLE angle) const
void ShapeGetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList)
virtual bool isMoving() const
Definition eda_shape.h:494
bool operator==(const EDA_SHAPE &aOther) const
int GetRadius() const
SHAPE_T GetShape() const
Definition eda_shape.h:185
void SetPolyShape(const SHAPE_POLY_SET &aShape)
Definition eda_shape.h:360
bool Deserialize(const google::protobuf::Any &aContainer) override
Deserializes the given protobuf message into this object.
const std::vector< SEG > & GetHatchLines() const
void SetRectangleHeight(const int &aHeight)
SHAPE_POLY_SET & hatching() const
bool IsHatchedFill() const
Definition eda_shape.h:140
virtual SHAPE_POLY_SET getHatchingKnockouts() const
Definition eda_shape.h:460
VECTOR2I m_arcCenter
Definition eda_shape.h:533
void SetCenterX(int x)
Definition eda_shape.h:222
virtual void SetFilled(bool aFlag)
Definition eda_shape.h:152
virtual bool IsFilledForHitTesting() const
Definition eda_shape.h:147
bool continueEdit(const VECTOR2I &aPosition)
wxString ShowShape() const
ARC_MID m_arcMidData
Definition eda_shape.h:534
void SetFillColor(const COLOR4D &aColor)
Definition eda_shape.h:170
int GetEndY() const
Definition eda_shape.h:233
bool hitTest(const VECTOR2I &aPosition, int aAccuracy=0) const
void SetCachedArcData(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, const VECTOR2I &aCenter)
Set the data used for mid point caching.
void SetEndX(int aX)
Definition eda_shape.h:250
void RebuildBezierToSegmentsPointsList(int aMaxError)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
virtual int GetHatchLineWidth() const
Definition eda_shape.h:175
bool IsSolidFill() const
Definition eda_shape.h:133
void flip(const VECTOR2I &aCentre, FLIP_DIRECTION aFlipDirection)
EDA_SHAPE(SHAPE_T aType, int aLineWidth, FILL_T aFill)
Definition eda_shape.cpp:55
std::vector< SEG > & hatchLines() const
void beginEdit(const VECTOR2I &aStartPoint)
VECTOR2I m_start
Definition eda_shape.h:530
int GetPointCount() const
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:232
bool IsClosed() const
void SetRadius(int aX)
Definition eda_shape.h:257
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:194
LINE_STYLE GetLineStyle() const
void endEdit(bool aClosed=true)
Finish editing the shape.
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:190
void SetLineColor(const COLOR4D &aColor)
Definition eda_shape.h:181
COLOR4D GetFillColor() const
Definition eda_shape.h:169
void SetRectangle(const long long int &aHeight, const long long int &aWidth)
void SetShape(SHAPE_T aShape)
Definition eda_shape.h:184
void SwapShape(EDA_SHAPE *aImage)
std::vector< VECTOR2I > GetRectCorners() const
std::vector< VECTOR2I > m_bezierPoints
Definition eda_shape.h:539
bool IsAnyFill() const
Definition eda_shape.h:128
void setPosition(const VECTOR2I &aPos)
virtual bool IsProxyItem() const
Definition eda_shape.h:125
void computeArcBBox(BOX2I &aBBox) const
virtual void UpdateHatching() const
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:236
void SetRectangleWidth(const int &aWidth)
void SetBezierC1(const VECTOR2I &aPt)
Definition eda_shape.h:271
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
double GetLength() const
wxString SHAPE_T_asString() const
void scale(double aScale)
int GetStartX() const
Definition eda_shape.h:192
double Similarity(const EDA_SHAPE &aOther) const
const VECTOR2I & GetBezierC1() const
Definition eda_shape.h:272
VECTOR2I m_end
Definition eda_shape.h:531
const BOX2I getBoundingBox() const
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
int GetRectangleHeight() const
virtual int GetWidth() const
Definition eda_shape.h:173
VECTOR2I getPosition() const
bool IsClockwiseArc() const
STROKE_PARAMS m_stroke
Definition eda_shape.h:519
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
wxString getFriendlyName() const
EDA_SHAPE & operator=(const EDA_SHAPE &aOther)
VECTOR2I m_bezierC1
Definition eda_shape.h:536
FILL_T m_fill
Definition eda_shape.h:520
COLOR4D m_fillColor
Definition eda_shape.h:521
void SetWidth(int aWidth)
EDA_ANGLE GetSegmentAngle() const
int GetCornerRadius() const
void SetFillMode(FILL_T aFill)
std::unique_ptr< SHAPE_POLY_SET > m_poly
Definition eda_shape.h:540
long long int m_rectangleWidth
Definition eda_shape.h:527
VECTOR2I m_bezierC2
Definition eda_shape.h:537
void Serialize(google::protobuf::Any &aContainer) const override
Serializes this object to the given Any message.
bool IsPolyShapeValid() const
int Compare(const EDA_SHAPE *aOther) const
VECTOR2I GetArcMid() const
ENUM_MAP & Map(T aValue, const wxString &aName)
Definition property.h:727
static ENUM_MAP< T > & Instance()
Definition property.h:721
wxPGChoices & Choices()
Definition property.h:770
Class that other classes need to inherit from, in order to be inspectable.
Definition inspectable.h:38
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:105
PROPERTY_BASE & SetAvailableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Set a callback function to determine whether an object provides this property.
Definition property.h:262
PROPERTY_BASE & SetValidator(PROPERTY_VALIDATOR_FN &&aValidator)
Definition property.h:349
PROPERTY_BASE & SetIsHiddenFromRulesEditor(bool aHide=true)
Definition property.h:326
Provide class metadata.Helper macro to map type hashes to names.
static PROPERTY_MANAGER & Instance()
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
A round rectangle shape, based on a rectangle and a radius.
Definition roundrect.h:36
void TransformToPolygon(SHAPE_POLY_SET &aBuffer, int aMaxError) const
Get the polygonal representation of the roundrect.
Definition roundrect.cpp:83
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
const VECTOR2I & GetArcMid() const
Definition shape_arc.h:120
const VECTOR2I & GetP1() const
Definition shape_arc.h:119
const VECTOR2I & GetP0() const
Definition shape_arc.h:118
SHAPE_TYPE Type() const
Return the type of the shape.
Definition shape.h:98
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
void Move(const VECTOR2I &aVector) override
const SHAPE_ARC & Arc(size_t aArc) const
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.
ssize_t ArcIndex(size_t aSegment) const
Return the arc index for the given segment index.
SEG Segment(int aIndex) const
Return a copy of the aIndex-th segment in the 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.
bool IsArcSegment(size_t aSegment) const
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.
void ClearArcs()
Removes all arc references from all the outlines and holes in the polyset.
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)
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
void Fracture(bool aSimplify=true)
Convert a set of polygons with holes to a single outline with "slits"/"fractures" connecting the oute...
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.
An abstract shape on 2D plane.
Definition shape.h:126
int GetWidth() const
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:553
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 TransformRoundChamferedRectToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aPosition, const VECTOR2I &aSize, const EDA_ANGLE &aRotation, int aCornerRadius, double aChamferRatio, int aChamferCorners, int aInflate, int aError, ERROR_LOC aErrorLoc)
Convert a rectangle with rounded corners and/or chamfered corners to a polygon.
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.
@ ROUND_ALL_CORNERS
All angles are rounded.
#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:46
@ UNDEFINED
Definition eda_shape.h:47
@ SEGMENT
Definition eda_shape.h:48
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:49
UI_FILL_MODE
Definition eda_shape.h:71
@ REVERSE_HATCH
Definition eda_shape.h:75
@ SOLID
Definition eda_shape.h:73
@ HATCH
Definition eda_shape.h:74
@ NONE
Definition eda_shape.h:72
@ CROSS_HATCH
Definition eda_shape.h:76
FILL_T
Definition eda_shape.h:59
@ NO_FILL
Definition eda_shape.h:60
@ REVERSE_HATCH
Definition eda_shape.h:65
@ HATCH
Definition eda_shape.h:64
@ CROSS_HATCH
Definition eda_shape.h:66
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.
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
KICOMMON_API void PackColor(types::Color &aOutput, const KIGFX::COLOR4D &aInput)
KICOMMON_API int UnpackDistance(const types::Distance &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void PackPolySet(types::PolySet &aOutput, const SHAPE_POLY_SET &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API KIGFX::COLOR4D UnpackColor(const types::Color &aInput)
KICOMMON_API VECTOR2I UnpackVector2(const types::Vector2 &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void PackDistance(types::Distance &aOutput, int aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API void PackVector2(types::Vector2 &aOutput, const VECTOR2I &aInput, const EDA_IU_SCALE &aScale)
KICOMMON_API SHAPE_POLY_SET UnpackPolySet(const types::PolySet &aInput, const EDA_IU_SCALE &aScale)
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
#define _HKI(x)
Definition page_info.cpp:44
#define IMPLEMENT_ENUM_TO_WXANY(type)
Definition property.h:821
#define NO_SETTER(owner, type)
Definition property.h:828
@ PT_COORD
Coordinate expressed in distance units (mm/inch)
Definition property.h:65
@ PT_DECIDEGREE
Angle expressed in decidegrees.
Definition property.h:67
@ PT_SIZE
Size expressed in distance units (mm/inch)
Definition property.h:63
#define REGISTER_TYPE(x)
std::optional< std::unique_ptr< VALIDATION_ERROR > > VALIDATOR_RESULT
Null optional means validation succeeded.
@ SH_POLY_SET
set of polygons (with holes, etc.)
Definition shape.h:52
@ 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.
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:534
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition typeinfo.h:90
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition typeinfo.h:92
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:687
VECTOR2< double > VECTOR2D
Definition vector2d.h:686