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) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
27#include <bezier_curves.h>
28#include <base_units.h>
30#include <eda_draw_frame.h>
34#include <macros.h>
35#include <math/util.h> // for KiROUND
36#include <eda_shape.h>
37#include <plotters/plotter.h>
38
39
40EDA_SHAPE::EDA_SHAPE( SHAPE_T aType, int aLineWidth, FILL_T aFill ) :
41 m_endsSwapped( false ),
42 m_shape( aType ),
43 m_stroke( aLineWidth, PLOT_DASH_TYPE::DEFAULT, COLOR4D::UNSPECIFIED ),
44 m_fill( aFill ),
45 m_fillColor( COLOR4D::UNSPECIFIED ),
46 m_editState( 0 ),
47 m_annotationProxy( false )
48{
49}
50
51
53{
54}
55
56
57wxString EDA_SHAPE::ShowShape() const
58{
59 if( IsAnnotationProxy() )
60 return _( "Number Box" );
61
62 switch( m_shape )
63 {
64 case SHAPE_T::SEGMENT: return _( "Line" );
65 case SHAPE_T::RECT: return _( "Rect" );
66 case SHAPE_T::ARC: return _( "Arc" );
67 case SHAPE_T::CIRCLE: return _( "Circle" );
68 case SHAPE_T::BEZIER: return _( "Bezier Curve" );
69 case SHAPE_T::POLY: return _( "Polygon" );
70 default: return wxT( "??" );
71 }
72}
73
74
76{
77 switch( m_shape )
78 {
79 case SHAPE_T::SEGMENT: return wxS( "S_SEGMENT" );
80 case SHAPE_T::RECT: return wxS( "S_RECT" );
81 case SHAPE_T::ARC: return wxS( "S_ARC" );
82 case SHAPE_T::CIRCLE: return wxS( "S_CIRCLE" );
83 case SHAPE_T::POLY: return wxS( "S_POLYGON" );
84 case SHAPE_T::BEZIER: return wxS( "S_CURVE" );
85 case SHAPE_T::LAST: return wxS( "!S_LAST!" ); // Synthetic value, but if we come across it then
86 // we're going to want to know.
87 }
88
89 return wxEmptyString; // Just to quiet GCC.
90}
91
92
94{
95 move( aPos - getPosition() );
96}
97
98
100{
101 if( m_shape == SHAPE_T::ARC )
102 return getCenter();
103 else if( m_shape == SHAPE_T::POLY )
104 return m_poly.CVertex( 0 );
105 else
106 return m_start;
107}
108
109
111{
112 double length = 0.0;
113
114 switch( m_shape )
115 {
116 case SHAPE_T::BEZIER:
117 for( size_t ii = 1; ii < m_bezierPoints.size(); ++ii )
118 length += GetLineLength( m_bezierPoints[ ii - 1], m_bezierPoints[ii] );
119
120 return length;
121
122 case SHAPE_T::SEGMENT:
123 return GetLineLength( GetStart(), GetEnd() );
124
125 case SHAPE_T::POLY:
126 for( int ii = 0; ii < m_poly.COutline( 0 ).SegmentCount(); ii++ )
127 length += m_poly.COutline( 0 ).CSegment( ii ).Length();
128
129 return length;
130
131 case SHAPE_T::ARC:
132 return GetRadius() * GetArcAngle().AsRadians();
133
134 default:
136 return 0.0;
137 }
138}
139
140
142{
143 switch( m_shape )
144 {
145 case SHAPE_T::CIRCLE:
146 case SHAPE_T::RECT:
147 return true;
148
149 case SHAPE_T::ARC:
150 case SHAPE_T::SEGMENT:
151 return false;
152
153 case SHAPE_T::POLY:
154 if( m_poly.IsEmpty() )
155 return false;
156 else
157 return m_poly.Outline( 0 ).IsClosed();
158
159 case SHAPE_T::BEZIER:
160 if( m_bezierPoints.size() < 3 )
161 return false;
162 else
163 return m_bezierPoints[0] == m_bezierPoints[ m_bezierPoints.size() - 1 ];
164
165 default:
167 return false;
168 }
169}
170
171
172
173void EDA_SHAPE::move( const VECTOR2I& aMoveVector )
174{
175 switch ( m_shape )
176 {
177 case SHAPE_T::ARC:
178 case SHAPE_T::SEGMENT:
179 case SHAPE_T::RECT:
180 case SHAPE_T::CIRCLE:
181 m_start += aMoveVector;
182 m_end += aMoveVector;
183 m_arcCenter += aMoveVector;
184 break;
185
186 case SHAPE_T::POLY:
187 m_poly.Move( aMoveVector );
188 break;
189
190 case SHAPE_T::BEZIER:
191 m_start += aMoveVector;
192 m_end += aMoveVector;
193 m_bezierC1 += aMoveVector;
194 m_bezierC2 += aMoveVector;
195
196 for( VECTOR2I& pt : m_bezierPoints )
197 pt += aMoveVector;
198
199 break;
200
201 default:
203 break;
204 }
205}
206
207
208void EDA_SHAPE::scale( double aScale )
209{
210 auto scalePt = [&]( VECTOR2I& pt )
211 {
212 pt.x = KiROUND( pt.x * aScale );
213 pt.y = KiROUND( pt.y * aScale );
214 };
215
216 switch( m_shape )
217 {
218 case SHAPE_T::ARC:
219 case SHAPE_T::SEGMENT:
220 case SHAPE_T::RECT:
221 scalePt( m_start );
222 scalePt( m_end );
223 scalePt( m_arcCenter );
224 break;
225
226 case SHAPE_T::CIRCLE: // ring or circle
227 scalePt( m_start );
228 m_end.x = m_start.x + KiROUND( GetRadius() * aScale );
229 m_end.y = m_start.y;
230 break;
231
232 case SHAPE_T::POLY: // polygon
233 {
234 std::vector<VECTOR2I> pts;
235
236 for( int ii = 0; ii < m_poly.OutlineCount(); ++ ii )
237 {
238 for( const VECTOR2I& pt : m_poly.Outline( ii ).CPoints() )
239 {
240 pts.emplace_back( pt );
241 scalePt( pts.back() );
242 }
243 }
244
245 SetPolyPoints( pts );
246 }
247 break;
248
249 case SHAPE_T::BEZIER:
250 scalePt( m_start );
251 scalePt( m_end );
252 scalePt( m_bezierC1 );
253 scalePt( m_bezierC2 );
254 break;
255
256 default:
258 break;
259 }
260}
261
262
263void EDA_SHAPE::rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
264{
265 switch( m_shape )
266 {
267 case SHAPE_T::SEGMENT:
268 case SHAPE_T::CIRCLE:
269 RotatePoint( m_start, aRotCentre, aAngle );
270 RotatePoint( m_end, aRotCentre, aAngle );
271 break;
272
273 case SHAPE_T::ARC:
274 RotatePoint( m_start, aRotCentre, aAngle );
275 RotatePoint( m_end, aRotCentre, aAngle );
276 RotatePoint( m_arcCenter, aRotCentre, aAngle );
277 break;
278
279 case SHAPE_T::RECT:
280 if( aAngle.IsCardinal() )
281 {
282 RotatePoint( m_start, aRotCentre, aAngle );
283 RotatePoint( m_end, aRotCentre, aAngle );
284 break;
285 }
286
287 // Convert non-cardinally-rotated rect to a diamond
288 m_shape = SHAPE_T::POLY;
295
297
298 case SHAPE_T::POLY:
299 m_poly.Rotate( aAngle, aRotCentre );
300 break;
301
302 case SHAPE_T::BEZIER:
303 RotatePoint( m_start, aRotCentre, aAngle );
304 RotatePoint( m_end, aRotCentre, aAngle );
305 RotatePoint( m_bezierC1, aRotCentre, aAngle );
306 RotatePoint( m_bezierC2, aRotCentre, aAngle );
307
308 for( VECTOR2I& pt : m_bezierPoints )
309 RotatePoint( pt, aRotCentre, aAngle);
310
311 break;
312
313 default:
315 break;
316 }
317}
318
319
320void EDA_SHAPE::flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
321{
322 switch ( m_shape )
323 {
324 case SHAPE_T::SEGMENT:
325 case SHAPE_T::RECT:
326 if( aFlipLeftRight )
327 {
328 m_start.x = aCentre.x - ( m_start.x - aCentre.x );
329 m_end.x = aCentre.x - ( m_end.x - aCentre.x );
330 }
331 else
332 {
333 m_start.y = aCentre.y - ( m_start.y - aCentre.y );
334 m_end.y = aCentre.y - ( m_end.y - aCentre.y );
335 }
336
337 std::swap( m_start, m_end );
338 break;
339
340 case SHAPE_T::CIRCLE:
341 if( aFlipLeftRight )
342 {
343 m_start.x = aCentre.x - ( m_start.x - aCentre.x );
344 m_end.x = aCentre.x - ( m_end.x - aCentre.x );
345 }
346 else
347 {
348 m_start.y = aCentre.y - ( m_start.y - aCentre.y );
349 m_end.y = aCentre.y - ( m_end.y - aCentre.y );
350 }
351 break;
352
353 case SHAPE_T::ARC:
354 if( aFlipLeftRight )
355 {
356 m_start.x = aCentre.x - ( m_start.x - aCentre.x );
357 m_end.x = aCentre.x - ( m_end.x - aCentre.x );
358 m_arcCenter.x = aCentre.x - ( m_arcCenter.x - aCentre.x );
359 }
360 else
361 {
362 m_start.y = aCentre.y - ( m_start.y - aCentre.y );
363 m_end.y = aCentre.y - ( m_end.y - aCentre.y );
364 m_arcCenter.y = aCentre.y - ( m_arcCenter.y - aCentre.y );
365 }
366
367 std::swap( m_start, m_end );
368 break;
369
370 case SHAPE_T::POLY:
371 m_poly.Mirror( aFlipLeftRight, !aFlipLeftRight, aCentre );
372 break;
373
374 case SHAPE_T::BEZIER:
375 if( aFlipLeftRight )
376 {
377 m_start.x = aCentre.x - ( m_start.x - aCentre.x );
378 m_end.x = aCentre.x - ( m_end.x - aCentre.x );
379 m_bezierC1.x = aCentre.x - ( m_bezierC1.x - aCentre.x );
380 m_bezierC2.x = aCentre.x - ( m_bezierC2.x - aCentre.x );
381 }
382 else
383 {
384 m_start.y = aCentre.y - ( m_start.y - aCentre.y );
385 m_end.y = aCentre.y - ( m_end.y - aCentre.y );
386 m_bezierC1.y = aCentre.y - ( m_bezierC1.y - aCentre.y );
387 m_bezierC2.y = aCentre.y - ( m_bezierC2.y - aCentre.y );
388 }
389
390 // Rebuild the poly points shape
391 {
392 std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
393 BEZIER_POLY converter( ctrlPoints );
394 converter.GetPoly( m_bezierPoints, m_stroke.GetWidth() );
395 }
396 break;
397
398 default:
400 break;
401 }
402}
403
404
406{
407 // Has meaning only for SHAPE_T::BEZIER
408 if( m_shape != SHAPE_T::BEZIER )
409 {
410 m_bezierPoints.clear();
411 return;
412 }
413
414 // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
416
417 // Ensure last point respects aMinSegLen parameter
418 if( m_bezierPoints.size() > 2 )
419 {
420 int idx = m_bezierPoints.size()-1;
421
422 if( VECTOR2I( m_bezierPoints[idx] - m_bezierPoints[idx]-1 ).EuclideanNorm() < aMinSegLen )
423 {
424 m_bezierPoints[idx]-1 = m_bezierPoints[idx];
425 m_bezierPoints.pop_back();
426 }
427 }
428}
429
430
431const std::vector<VECTOR2I> EDA_SHAPE::buildBezierToSegmentsPointsList( int aMinSegLen ) const
432{
433 std::vector<VECTOR2I> bezierPoints;
434
435 // Rebuild the m_BezierPoints vertex list that approximate the Bezier curve
436 std::vector<VECTOR2I> ctrlPoints = { m_start, m_bezierC1, m_bezierC2, m_end };
437 BEZIER_POLY converter( ctrlPoints );
438 converter.GetPoly( bezierPoints, aMinSegLen );
439
440 return bezierPoints;
441}
442
443
445{
446 switch( m_shape )
447 {
448 case SHAPE_T::ARC:
449 return m_arcCenter;
450
451 case SHAPE_T::CIRCLE:
452 return m_start;
453
454 case SHAPE_T::SEGMENT:
455 // Midpoint of the line
456 return ( m_start + m_end ) / 2;
457
458 case SHAPE_T::POLY:
459 case SHAPE_T::RECT:
460 case SHAPE_T::BEZIER:
461 return getBoundingBox().Centre();
462
463 default:
465 return VECTOR2I();
466 }
467}
468
469
470void EDA_SHAPE::SetCenter( const VECTOR2I& aCenter )
471{
472 switch( m_shape )
473 {
474 case SHAPE_T::ARC:
475 m_arcCenter = aCenter;
476 break;
477
478 case SHAPE_T::CIRCLE:
479 m_start = aCenter;
480 break;
481
482 default:
484 }
485}
486
487
489{
490 // If none of the input data have changed since we loaded the arc,
491 // keep the original mid point data to minimize churn
494 return m_arcMidData.mid;
495
496 VECTOR2I mid = m_start;
497 RotatePoint( mid, m_arcCenter, -GetArcAngle() / 2.0 );
498 return mid;
499}
500
501
502void EDA_SHAPE::CalcArcAngles( EDA_ANGLE& aStartAngle, EDA_ANGLE& aEndAngle ) const
503{
504 VECTOR2D startRadial( GetStart() - getCenter() );
505 VECTOR2D endRadial( GetEnd() - getCenter() );
506
507 aStartAngle = EDA_ANGLE( startRadial );
508 aEndAngle = EDA_ANGLE( endRadial );
509
510 if( aEndAngle == aStartAngle )
511 aEndAngle = aStartAngle + ANGLE_360; // ring, not null
512
513 if( aStartAngle > aEndAngle )
514 {
515 if( aEndAngle < ANGLE_0 )
516 aEndAngle.Normalize();
517 else
518 aStartAngle = aStartAngle.Normalize() - ANGLE_360;
519 }
520}
521
522
524{
525 double radius = 0.0;
526
527 switch( m_shape )
528 {
529 case SHAPE_T::ARC:
530 radius = GetLineLength( m_arcCenter, m_start );
531 break;
532
533 case SHAPE_T::CIRCLE:
534 radius = GetLineLength( m_start, m_end );
535 break;
536
537 default:
539 }
540
541 // don't allow degenerate circles/arcs
542 return std::max( 1, KiROUND( radius ) );
543}
544
545
546void EDA_SHAPE::SetCachedArcData( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCenter )
547{
548 m_arcMidData.start = aStart;
549 m_arcMidData.end = aEnd;
550 m_arcMidData.center = aCenter;
551 m_arcMidData.mid = aMid;
552}
553
554
555void EDA_SHAPE::SetArcGeometry( const VECTOR2I& aStart, const VECTOR2I& aMid, const VECTOR2I& aEnd )
556{
557 m_arcMidData = {};
558 m_start = aStart;
559 m_end = aEnd;
560 m_arcCenter = CalcArcCenter( aStart, aMid, aEnd );
561 VECTOR2I new_mid = GetArcMid();
562
563 m_endsSwapped = false;
564
565 // Watch the ordering here. GetArcMid above needs to be called prior to initializing the
566 // m_arcMidData structure in order to ensure we get the calculated variant, not the cached
567 SetCachedArcData( aStart, aMid, aEnd, m_arcCenter );
568
569 /*
570 * If the input winding doesn't match our internal winding, the calculated midpoint will end
571 * up on the other side of the arc. In this case, we need to flip the start/end points and
572 * flag this change for the system.
573 */
574 VECTOR2D dist( new_mid - aMid );
575 VECTOR2D dist2( new_mid - m_arcCenter );
576
577 if( dist.SquaredEuclideanNorm() > dist2.SquaredEuclideanNorm() )
578 {
579 std::swap( m_start, m_end );
580 m_endsSwapped = true;
581 }
582}
583
584
586{
587 EDA_ANGLE startAngle;
588 EDA_ANGLE endAngle;
589
590 CalcArcAngles( startAngle, endAngle );
591
592 return endAngle - startAngle;
593}
594
595
596void EDA_SHAPE::SetArcAngleAndEnd( const EDA_ANGLE& aAngle, bool aCheckNegativeAngle )
597{
598 EDA_ANGLE angle( aAngle );
599
600 m_end = m_start;
602
603 if( aCheckNegativeAngle && aAngle < ANGLE_0 )
604 {
605 std::swap( m_start, m_end );
606 m_endsSwapped = true;
607 }
608}
609
610
612{
613 switch( m_shape )
614 {
615 case SHAPE_T::CIRCLE: return _( "Circle" );
616 case SHAPE_T::ARC: return _( "Arc" );
617 case SHAPE_T::BEZIER: return _( "Curve" );
618 case SHAPE_T::POLY: return _( "Polygon" );
619 case SHAPE_T::RECT: return IsAnnotationProxy() ? _( "Pad Number Box" ) : _( "Rectangle" );
620 case SHAPE_T::SEGMENT: return _( "Segment" );
621 default: return _( "Unrecognized" );
622 }
623}
624
625
626void EDA_SHAPE::ShapeGetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
627{
628 ORIGIN_TRANSFORMS originTransforms = aFrame->GetOriginTransforms();
629 wxString msg;
630
631 wxString shape = _( "Shape" );
632 aList.emplace_back( shape, GetFriendlyName() );
633
634 switch( m_shape )
635 {
636 case SHAPE_T::CIRCLE:
637 aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
638 break;
639
640 case SHAPE_T::ARC:
642 aList.emplace_back( _( "Angle" ), msg );
643
644 aList.emplace_back( _( "Radius" ), aFrame->MessageTextFromValue( GetRadius() ) );
645 break;
646
647 case SHAPE_T::BEZIER:
648 aList.emplace_back( _( "Length" ), aFrame->MessageTextFromValue( GetLength() ) );
649 break;
650
651 case SHAPE_T::POLY:
652 msg.Printf( wxS( "%d" ), GetPolyShape().Outline(0).PointCount() );
653 aList.emplace_back( _( "Points" ), msg );
654 break;
655
656 case SHAPE_T::RECT:
657 aList.emplace_back( _( "Width" ),
658 aFrame->MessageTextFromValue( std::abs( GetEnd().x - GetStart().x ) ) );
659
660 aList.emplace_back( _( "Height" ),
661 aFrame->MessageTextFromValue( std::abs( GetEnd().y - GetStart().y ) ) );
662 break;
663
664 case SHAPE_T::SEGMENT:
665 {
666 aList.emplace_back( _( "Length" ),
668
669 // angle counter-clockwise from 3'o-clock
670 EDA_ANGLE angle( atan2( (double)( GetStart().y - GetEnd().y ),
671 (double)( GetEnd().x - GetStart().x ) ), RADIANS_T );
672 aList.emplace_back( _( "Angle" ), EDA_UNIT_UTILS::UI::MessageTextFromValue( angle ) );
673 break;
674 }
675
676 default:
677 break;
678 }
679
680 m_stroke.GetMsgPanelInfo( aFrame, aList );
681}
682
683
685{
686 BOX2I bbox;
687
688 switch( m_shape )
689 {
690 case SHAPE_T::RECT:
691 for( VECTOR2I& pt : GetRectCorners() )
692 bbox.Merge( pt );
693
694 break;
695
696 case SHAPE_T::SEGMENT:
697 bbox.SetOrigin( GetStart() );
698 bbox.SetEnd( GetEnd() );
699 break;
700
701 case SHAPE_T::CIRCLE:
702 bbox.SetOrigin( GetStart() );
703 bbox.Inflate( GetRadius() );
704 break;
705
706 case SHAPE_T::ARC:
707 computeArcBBox( bbox );
708 break;
709
710 case SHAPE_T::POLY:
711 if( m_poly.IsEmpty() )
712 break;
713
714 for( auto iter = m_poly.CIterate(); iter; iter++ )
715 bbox.Merge( *iter );
716
717 break;
718
719 case SHAPE_T::BEZIER:
720 bbox.SetOrigin( GetStart() );
721 bbox.Merge( GetBezierC1() );
722 bbox.Merge( GetBezierC2() );
723 bbox.Merge( GetEnd() );
724 break;
725
726 default:
728 break;
729 }
730
731 bbox.Inflate( std::max( 0, GetWidth() ) / 2 );
732 bbox.Normalize();
733
734 return bbox;
735}
736
737
738bool EDA_SHAPE::hitTest( const VECTOR2I& aPosition, int aAccuracy ) const
739{
740 int maxdist = aAccuracy;
741
742 if( GetWidth() > 0 )
743 maxdist += GetWidth() / 2;
744
745 switch( m_shape )
746 {
747 case SHAPE_T::CIRCLE:
748 {
749 int radius = GetRadius();
750
751 VECTOR2I::extended_type dist = KiROUND<double, VECTOR2I::extended_type>(
752 EuclideanNorm( aPosition - getCenter() ) );
753
754 if( IsFilled() )
755 return dist <= radius + maxdist; // Filled circle hit-test
756 else
757 return abs( radius - dist ) <= maxdist; // Ring hit-test
758 }
759
760 case SHAPE_T::ARC:
761 {
762 if( EuclideanNorm( aPosition - m_start ) <= maxdist )
763 return true;
764
765 if( EuclideanNorm( aPosition - m_end ) <= maxdist )
766 return true;
767
768 VECTOR2I relPos = aPosition - getCenter();
769 int radius = GetRadius();
770
772 KiROUND<double, VECTOR2I::extended_type>( EuclideanNorm( relPos ) );
773
774 if( IsFilled() )
775 {
776 // Check distance from arc center
777 if( dist > radius + maxdist )
778 return false;
779 }
780 else
781 {
782 // Check distance from arc circumference
783 if( abs( radius - dist ) > maxdist )
784 return false;
785 }
786
787 // Finally, check to see if it's within arc's swept angle.
788 EDA_ANGLE startAngle;
789 EDA_ANGLE endAngle;
790 CalcArcAngles( startAngle, endAngle );
791
792 EDA_ANGLE relPosAngle( relPos );
793
794 startAngle.Normalize();
795 endAngle.Normalize();
796 relPosAngle.Normalize();
797
798 if( endAngle > startAngle )
799 return relPosAngle >= startAngle && relPosAngle <= endAngle;
800 else
801 return relPosAngle >= startAngle || relPosAngle <= endAngle;
802 }
803
804 case SHAPE_T::BEZIER:
805 const_cast<EDA_SHAPE*>( this )->RebuildBezierToSegmentsPointsList( GetWidth() );
806
807 for( unsigned int i= 1; i < m_bezierPoints.size(); i++)
808 {
809 if( TestSegmentHit( aPosition, m_bezierPoints[ i - 1], m_bezierPoints[i], maxdist ) )
810 return true;
811 }
812
813 return false;
814
815 case SHAPE_T::SEGMENT:
816 return TestSegmentHit( aPosition, GetStart(), GetEnd(), maxdist );
817
818 case SHAPE_T::RECT:
819 if( IsAnnotationProxy() || IsFilled() ) // Filled rect hit-test
820 {
821 SHAPE_POLY_SET poly;
822 poly.NewOutline();
823
824 for( const VECTOR2I& pt : GetRectCorners() )
825 poly.Append( pt );
826
827 return poly.Collide( aPosition, maxdist );
828 }
829 else // Open rect hit-test
830 {
831 std::vector<VECTOR2I> pts = GetRectCorners();
832
833 return TestSegmentHit( aPosition, pts[0], pts[1], maxdist )
834 || TestSegmentHit( aPosition, pts[1], pts[2], maxdist )
835 || TestSegmentHit( aPosition, pts[2], pts[3], maxdist )
836 || TestSegmentHit( aPosition, pts[3], pts[0], maxdist );
837 }
838
839 case SHAPE_T::POLY:
840 if( IsFilled() )
841 return m_poly.Collide( aPosition, maxdist );
842 else
843 return m_poly.CollideEdge( aPosition, nullptr, maxdist );
844
845 default:
847 return false;
848 }
849}
850
851
852bool EDA_SHAPE::hitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
853{
854 BOX2I arect = aRect;
855 arect.Normalize();
856 arect.Inflate( aAccuracy );
857
858 BOX2I bbox = getBoundingBox();
859
860 switch( m_shape )
861 {
862 case SHAPE_T::CIRCLE:
863 // Test if area intersects or contains the circle:
864 if( aContained )
865 {
866 return arect.Contains( bbox );
867 }
868 else
869 {
870 // If the rectangle does not intersect the bounding box, this is a much quicker test
871 if( !arect.Intersects( bbox ) )
872 return false;
873 else
874 return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
875 }
876
877 case SHAPE_T::ARC:
878 // Test for full containment of this arc in the rect
879 if( aContained )
880 {
881 return arect.Contains( bbox );
882 }
883 // Test if the rect crosses the arc
884 else
885 {
886 if( !arect.Intersects( bbox ) )
887 return false;
888
889 if( IsFilled() )
890 {
891 return ( arect.Intersects( getCenter(), GetStart() )
892 || arect.Intersects( getCenter(), GetEnd() )
893 || arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() ) );
894 }
895 else
896 {
897 return arect.IntersectsCircleEdge( getCenter(), GetRadius(), GetWidth() );
898 }
899 }
900
901 case SHAPE_T::RECT:
902 if( aContained )
903 {
904 return arect.Contains( bbox );
905 }
906 else
907 {
908 std::vector<VECTOR2I> pts = GetRectCorners();
909
910 // Account for the width of the lines
911 arect.Inflate( GetWidth() / 2 );
912 return ( arect.Intersects( pts[0], pts[1] )
913 || arect.Intersects( pts[1], pts[2] )
914 || arect.Intersects( pts[2], pts[3] )
915 || arect.Intersects( pts[3], pts[0] ) );
916 }
917
918 case SHAPE_T::SEGMENT:
919 if( aContained )
920 {
921 return arect.Contains( GetStart() ) && aRect.Contains( GetEnd() );
922 }
923 else
924 {
925 // Account for the width of the line
926 arect.Inflate( GetWidth() / 2 );
927 return arect.Intersects( GetStart(), GetEnd() );
928 }
929
930 case SHAPE_T::POLY:
931 if( aContained )
932 {
933 return arect.Contains( bbox );
934 }
935 else
936 {
937 // Fast test: if aRect is outside the polygon bounding box,
938 // rectangles cannot intersect
939 if( !arect.Intersects( bbox ) )
940 return false;
941
942 // Account for the width of the line
943 arect.Inflate( GetWidth() / 2 );
944
945 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
946 {
947 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
948 int count = poly.GetPointCount();
949
950 for( int jj = 0; jj < count; jj++ )
951 {
952 VECTOR2I vertex = poly.GetPoint( jj );
953
954 // Test if the point is within aRect
955 if( arect.Contains( vertex ) )
956 return true;
957
958 if( jj + 1 < count )
959 {
960 VECTOR2I vertexNext = poly.GetPoint( jj + 1 );
961
962 // Test if this edge intersects aRect
963 if( arect.Intersects( vertex, vertexNext ) )
964 return true;
965 }
966 else if( poly.IsClosed() )
967 {
968 VECTOR2I vertexNext = poly.GetPoint( 0 );
969
970 // Test if this edge intersects aRect
971 if( arect.Intersects( vertex, vertexNext ) )
972 return true;
973 }
974 }
975 }
976
977 return false;
978 }
979
980 case SHAPE_T::BEZIER:
981 if( aContained )
982 {
983 return arect.Contains( bbox );
984 }
985 else
986 {
987 // Fast test: if aRect is outside the polygon bounding box,
988 // rectangles cannot intersect
989 if( !arect.Intersects( bbox ) )
990 return false;
991
992 // Account for the width of the line
993 arect.Inflate( GetWidth() / 2 );
994 unsigned count = m_bezierPoints.size();
995
996 for( unsigned ii = 1; ii < count; ii++ )
997 {
998 VECTOR2I vertex = m_bezierPoints[ii - 1];
999 VECTOR2I vertexNext = m_bezierPoints[ii];
1000
1001 // Test if the point is within aRect
1002 if( arect.Contains( vertex ) )
1003 return true;
1004
1005 // Test if this edge intersects aRect
1006 if( arect.Intersects( vertex, vertexNext ) )
1007 return true;
1008 }
1009
1010 return false;
1011 }
1012
1013 default:
1015 return false;
1016 }
1017}
1018
1019
1020std::vector<VECTOR2I> EDA_SHAPE::GetRectCorners() const
1021{
1022 std::vector<VECTOR2I> pts;
1023 VECTOR2I topLeft = GetStart();
1024 VECTOR2I botRight = GetEnd();
1025
1026 pts.emplace_back( topLeft );
1027 pts.emplace_back( botRight.x, topLeft.y );
1028 pts.emplace_back( botRight );
1029 pts.emplace_back( topLeft.x, botRight.y );
1030
1031 return pts;
1032}
1033
1034
1036{
1037 // Start, end, and each inflection point the arc crosses will enclose the entire arc.
1038 // Only include the center when filled; it's not necessarily inside the BB of an unfilled
1039 // arc with a small included angle.
1040 aBBox.SetOrigin( m_start );
1041 aBBox.Merge( m_end );
1042
1043 if( IsFilled() )
1044 aBBox.Merge( m_arcCenter );
1045
1046 int radius = GetRadius();
1047 EDA_ANGLE t1, t2;
1048
1049 CalcArcAngles( t1, t2 );
1050
1051 t1.Normalize();
1052 t2.Normalize();
1053
1054 if( t2 > t1 )
1055 {
1056 if( t1 < ANGLE_0 && t2 > ANGLE_0 )
1057 aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
1058
1059 if( t1 < ANGLE_90 && t2 > ANGLE_90 )
1060 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
1061
1062 if( t1 < ANGLE_180 && t2 > ANGLE_180 )
1063 aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
1064
1065 if( t1 < ANGLE_270 && t2 > ANGLE_270 )
1066 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
1067 }
1068 else
1069 {
1070 if( t1 < ANGLE_0 || t2 > ANGLE_0 )
1071 aBBox.Merge( VECTOR2I( m_arcCenter.x + radius, m_arcCenter.y ) ); // right
1072
1073 if( t1 < ANGLE_90 || t2 > ANGLE_90 )
1074 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y + radius ) ); // down
1075
1076 if( t1 < ANGLE_180 || t2 > ANGLE_180 )
1077 aBBox.Merge( VECTOR2I( m_arcCenter.x - radius, m_arcCenter.y ) ); // left
1078
1079 if( t1 < ANGLE_270 || t2 > ANGLE_270 )
1080 aBBox.Merge( VECTOR2I( m_arcCenter.x, m_arcCenter.y - radius ) ); // up
1081 }
1082}
1083
1084
1085void EDA_SHAPE::SetPolyPoints( const std::vector<VECTOR2I>& aPoints )
1086{
1089
1090 for( const VECTOR2I& p : aPoints )
1091 m_poly.Append( p.x, p.y );
1092}
1093
1094
1095std::vector<SHAPE*> EDA_SHAPE::makeEffectiveShapes( bool aEdgeOnly, bool aLineChainOnly ) const
1096{
1097 std::vector<SHAPE*> effectiveShapes;
1098 int width = GetEffectiveWidth();
1099
1100 switch( m_shape )
1101 {
1102 case SHAPE_T::ARC:
1103 effectiveShapes.emplace_back( new SHAPE_ARC( m_arcCenter, m_start, GetArcAngle(), width ) );
1104 break;
1105
1106 case SHAPE_T::SEGMENT:
1107 effectiveShapes.emplace_back( new SHAPE_SEGMENT( m_start, m_end, width ) );
1108 break;
1109
1110 case SHAPE_T::RECT:
1111 {
1112 std::vector<VECTOR2I> pts = GetRectCorners();
1113
1114 if( ( IsFilled() || IsAnnotationProxy() ) && !aEdgeOnly )
1115 effectiveShapes.emplace_back( new SHAPE_SIMPLE( pts ) );
1116
1117 if( width > 0 || !IsFilled() || aEdgeOnly )
1118 {
1119 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[0], pts[1], width ) );
1120 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[1], pts[2], width ) );
1121 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[2], pts[3], width ) );
1122 effectiveShapes.emplace_back( new SHAPE_SEGMENT( pts[3], pts[0], width ) );
1123 }
1124 }
1125 break;
1126
1127 case SHAPE_T::CIRCLE:
1128 {
1129 if( IsFilled() && !aEdgeOnly )
1130 effectiveShapes.emplace_back( new SHAPE_CIRCLE( getCenter(), GetRadius() ) );
1131
1132 if( width > 0 || !IsFilled() || aEdgeOnly )
1133 effectiveShapes.emplace_back( new SHAPE_ARC( getCenter(), GetEnd(), ANGLE_360, width ) );
1134
1135 break;
1136 }
1137
1138 case SHAPE_T::BEZIER:
1139 {
1140 std::vector<VECTOR2I> bezierPoints = buildBezierToSegmentsPointsList( width );
1141 VECTOR2I start_pt = bezierPoints[0];
1142
1143 for( unsigned int jj = 1; jj < bezierPoints.size(); jj++ )
1144 {
1145 VECTOR2I end_pt = bezierPoints[jj];
1146 effectiveShapes.emplace_back( new SHAPE_SEGMENT( start_pt, end_pt, width ) );
1147 start_pt = end_pt;
1148 }
1149
1150 break;
1151 }
1152
1153 case SHAPE_T::POLY:
1154 {
1155 if( GetPolyShape().OutlineCount() == 0 ) // malformed/empty polygon
1156 break;
1157
1158 for( int ii = 0; ii < GetPolyShape().OutlineCount(); ++ii )
1159 {
1161
1162 if( aLineChainOnly )
1163 l.SetClosed( false );
1164
1165 if( IsFilled() && !aEdgeOnly )
1166 effectiveShapes.emplace_back( new SHAPE_SIMPLE( l ) );
1167
1168 if( width > 0 || !IsFilled() || aEdgeOnly )
1169 {
1170 for( int jj = 0; jj < l.SegmentCount(); jj++ )
1171 effectiveShapes.emplace_back( new SHAPE_SEGMENT( l.Segment( jj ), width ) );
1172 }
1173 }
1174 }
1175 break;
1176
1177 default:
1179 break;
1180 }
1181
1182 return effectiveShapes;
1183}
1184
1185
1186void EDA_SHAPE::DupPolyPointsList( std::vector<VECTOR2I>& aBuffer ) const
1187{
1188 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1189 {
1190 int pointCount = m_poly.COutline( ii ).PointCount();
1191
1192 if( pointCount )
1193 {
1194 aBuffer.reserve( pointCount );
1195
1196 for ( auto iter = m_poly.CIterate(); iter; iter++ )
1197 aBuffer.emplace_back( iter->x, iter->y );
1198 }
1199 }
1200}
1201
1202
1204{
1205 // return true if the polygonal shape is valid (has more than 2 points)
1206 if( GetPolyShape().OutlineCount() == 0 )
1207 return false;
1208
1209 const SHAPE_LINE_CHAIN& outline = static_cast<const SHAPE_POLY_SET&>( GetPolyShape() ).Outline( 0 );
1210
1211 return outline.PointCount() > 2;
1212}
1213
1214
1216{
1217 // return the number of corners of the polygonal shape
1218 // this shape is expected to be only one polygon without hole
1219 if( GetPolyShape().OutlineCount() )
1220 return GetPolyShape().VertexCount( 0 );
1221
1222 return 0;
1223}
1224
1225
1226void EDA_SHAPE::beginEdit( const VECTOR2I& aPosition )
1227{
1228 switch( GetShape() )
1229 {
1230 case SHAPE_T::SEGMENT:
1231 case SHAPE_T::CIRCLE:
1232 case SHAPE_T::RECT:
1233 SetStart( aPosition );
1234 SetEnd( aPosition );
1235 break;
1236
1237 case SHAPE_T::ARC:
1238 SetArcGeometry( aPosition, aPosition, aPosition );
1239 m_editState = 1;
1240 break;
1241
1242 case SHAPE_T::POLY:
1244 m_poly.Outline( 0 ).SetClosed( false );
1245
1246 // Start and end of the first segment (co-located for now)
1247 m_poly.Outline( 0 ).Append( aPosition );
1248 m_poly.Outline( 0 ).Append( aPosition, true );
1249 break;
1250
1251 default:
1253 }
1254}
1255
1256
1257bool EDA_SHAPE::continueEdit( const VECTOR2I& aPosition )
1258{
1259 switch( GetShape() )
1260 {
1261 case SHAPE_T::ARC:
1262 case SHAPE_T::SEGMENT:
1263 case SHAPE_T::CIRCLE:
1264 case SHAPE_T::RECT:
1265 return false;
1266
1267 case SHAPE_T::POLY:
1268 {
1269 SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1270
1271 // do not add zero-length segments
1272 if( poly.CPoint( poly.GetPointCount() - 2 ) != poly.CLastPoint() )
1273 poly.Append( aPosition, true );
1274 }
1275 return true;
1276
1277 default:
1279 return false;
1280 }
1281}
1282
1283
1284void EDA_SHAPE::calcEdit( const VECTOR2I& aPosition )
1285{
1286#define sq( x ) pow( x, 2 )
1287
1288 switch( GetShape() )
1289 {
1290 case SHAPE_T::SEGMENT:
1291 case SHAPE_T::CIRCLE:
1292 case SHAPE_T::RECT:
1293 SetEnd( aPosition );
1294 break;
1295
1296 case SHAPE_T::ARC:
1297 {
1298 int radius = GetRadius();
1299
1300 // Edit state 0: drawing: place start
1301 // Edit state 1: drawing: place end (center calculated for 90-degree subtended angle)
1302 // Edit state 2: point edit: move start (center calculated for invariant subtended angle)
1303 // Edit state 3: point edit: move end (center calculated for invariant subtended angle)
1304 // Edit state 4: point edit: move center
1305 // Edit state 5: point edit: move arc-mid-point
1306
1307 switch( m_editState )
1308 {
1309 case 0:
1310 SetArcGeometry( aPosition, aPosition, aPosition );
1311 return;
1312
1313 case 1:
1314 m_end = aPosition;
1315 radius = KiROUND( sqrt( sq( GetLineLength( m_start, m_end ) ) / 2.0 ) );
1316 break;
1317
1318 case 2:
1319 case 3:
1320 {
1321 VECTOR2I v = m_start - m_end;
1322 double chordBefore = sq( v.x ) + sq( v.y );
1323
1324 if( m_editState == 2 )
1325 m_start = aPosition;
1326 else
1327 m_end = aPosition;
1328
1329 v = m_start - m_end;
1330 double chordAfter = sq( v.x ) + sq( v.y );
1331 double ratio = chordAfter / chordBefore;
1332
1333 if( ratio != 0 )
1334 {
1335 radius = std::max( int( sqrt( sq( radius ) * ratio ) ) + 1,
1336 int( sqrt( chordAfter ) / 2 ) + 1 );
1337 }
1338 }
1339 break;
1340
1341 case 4:
1342 {
1343 double radialA = GetLineLength( m_start, aPosition );
1344 double radialB = GetLineLength( m_end, aPosition );
1345 radius = int( ( radialA + radialB ) / 2.0 ) + 1;
1346 }
1347 break;
1348
1349 case 5:
1350 SetArcGeometry( GetStart(), aPosition, GetEnd() );
1351 return;
1352 }
1353
1354 // Calculate center based on start, end, and radius
1355 //
1356 // Let 'l' be the length of the chord and 'm' the middle point of the chord
1357 double l = GetLineLength( m_start, m_end );
1358 VECTOR2I m = ( m_start + m_end ) / 2;
1359
1360 // Calculate 'd', the vector from the chord midpoint to the center
1361 VECTOR2I d;
1362 d.x = KiROUND( sqrt( sq( radius ) - sq( l/2 ) ) * ( m_start.y - m_end.y ) / l );
1363 d.y = KiROUND( sqrt( sq( radius ) - sq( l/2 ) ) * ( m_end.x - m_start.x ) / l );
1364
1365 VECTOR2I c1 = m + d;
1366 VECTOR2I c2 = m - d;
1367
1368 // Solution gives us 2 centers; we need to pick one:
1369 switch( m_editState )
1370 {
1371 case 1:
1372 // Keep arc clockwise while drawing i.e. arc angle = 90 deg.
1373 // it can be 90 or 270 deg depending on the arc center choice (c1 or c2)
1374 m_arcCenter = c1; // first trial
1375
1376 if( GetArcAngle() > ANGLE_180 )
1377 m_arcCenter = c2;
1378
1379 break;
1380
1381 case 2:
1382 case 3:
1383 // Pick the one of c1, c2 to keep arc <= 180 deg
1384 m_arcCenter = c1; // first trial
1385
1386 if( GetArcAngle() > ANGLE_180 )
1387 m_arcCenter = c2;
1388
1389 break;
1390
1391 case 4:
1392 // Pick the one closer to the mouse position
1393 m_arcCenter = GetLineLength( c1, aPosition ) < GetLineLength( c2, aPosition ) ? c1 : c2;
1394
1395 // keep arc angle <= 180 deg
1396 if( GetArcAngle() > ANGLE_180 )
1397 std::swap( m_start, m_end );
1398
1399 break;
1400 }
1401 }
1402 break;
1403
1404 case SHAPE_T::POLY:
1405 m_poly.Outline( 0 ).SetPoint( m_poly.Outline( 0 ).GetPointCount() - 1, aPosition );
1406 break;
1407
1408 default:
1410 }
1411}
1412
1413
1414void EDA_SHAPE::endEdit( bool aClosed )
1415{
1416 switch( GetShape() )
1417 {
1418 case SHAPE_T::ARC:
1419 case SHAPE_T::SEGMENT:
1420 case SHAPE_T::CIRCLE:
1421 case SHAPE_T::RECT:
1422 break;
1423
1424 case SHAPE_T::POLY:
1425 {
1426 SHAPE_LINE_CHAIN& poly = m_poly.Outline( 0 );
1427
1428 // do not include last point twice
1429 if( poly.GetPointCount() > 2 )
1430 {
1431 if( poly.CPoint( poly.GetPointCount() - 2 ) == poly.CLastPoint() )
1432 {
1433 poly.SetClosed( aClosed );
1434 poly.Remove( poly.GetPointCount() - 1 );
1435 }
1436 }
1437 }
1438 break;
1439
1440 default:
1442 }
1443}
1444
1445
1447{
1448 EDA_SHAPE* image = dynamic_cast<EDA_SHAPE*>( aImage );
1449 assert( image );
1450
1451 #define SWAPITEM( x ) std::swap( x, image->x )
1452 SWAPITEM( m_stroke );
1453 SWAPITEM( m_start );
1454 SWAPITEM( m_end );
1456 SWAPITEM( m_shape );
1460 SWAPITEM( m_poly );
1461 SWAPITEM( m_fill );
1465 #undef SWAPITEM
1466}
1467
1468
1469int EDA_SHAPE::Compare( const EDA_SHAPE* aOther ) const
1470{
1471#define EPSILON 2 // Should be enough for rounding errors on calculated items
1472
1473#define TEST( a, b ) { if( a != b ) return a - b; }
1474#define TEST_E( a, b ) { if( abs( a - b ) > EPSILON ) return a - b; }
1475#define TEST_PT( a, b ) { TEST_E( a.x, b.x ); TEST_E( a.y, b.y ); }
1476
1477 TEST_PT( m_start, aOther->m_start );
1478 TEST_PT( m_end, aOther->m_end );
1479
1480 TEST( (int) m_shape, (int) aOther->m_shape );
1481
1482 if( m_shape == SHAPE_T::ARC )
1483 {
1484 TEST_PT( m_arcCenter, aOther->m_arcCenter );
1485 }
1486 else if( m_shape == SHAPE_T::BEZIER )
1487 {
1488 TEST_PT( m_bezierC1, aOther->m_bezierC1 );
1489 TEST_PT( m_bezierC2, aOther->m_bezierC2 );
1490 }
1491 else if( m_shape == SHAPE_T::POLY )
1492 {
1494 }
1495
1496 for( size_t ii = 0; ii < m_bezierPoints.size(); ++ii )
1497 TEST_PT( m_bezierPoints[ii], aOther->m_bezierPoints[ii] );
1498
1499 for( int ii = 0; ii < m_poly.TotalVertices(); ++ii )
1500 TEST_PT( m_poly.CVertex( ii ), aOther->m_poly.CVertex( ii ) );
1501
1502 TEST_E( m_stroke.GetWidth(), aOther->m_stroke.GetWidth() );
1503 TEST( (int) m_stroke.GetPlotStyle(), (int) aOther->m_stroke.GetPlotStyle() );
1504 TEST( (int) m_fill, (int) aOther->m_fill );
1505
1506 return 0;
1507}
1508
1509
1510void EDA_SHAPE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, int aClearance, int aError,
1511 ERROR_LOC aErrorLoc, bool ignoreLineWidth ) const
1512{
1513 int width = ignoreLineWidth ? 0 : GetWidth();
1514
1515 width += 2 * aClearance;
1516
1517 switch( m_shape )
1518 {
1519 case SHAPE_T::CIRCLE:
1520 {
1521 int r = GetRadius();
1522
1523 if( IsFilled() )
1524 TransformCircleToPolygon( aBuffer, getCenter(), r + width / 2, aError, aErrorLoc );
1525 else
1526 TransformRingToPolygon( aBuffer, getCenter(), r, width, aError, aErrorLoc );
1527
1528 break;
1529 }
1530
1531 case SHAPE_T::RECT:
1532 {
1533 std::vector<VECTOR2I> pts = GetRectCorners();
1534
1535 if( IsFilled() || IsAnnotationProxy() )
1536 {
1537 aBuffer.NewOutline();
1538
1539 for( const VECTOR2I& pt : pts )
1540 aBuffer.Append( pt );
1541 }
1542
1543 if( width > 0 || !IsFilled() )
1544 {
1545 // Add in segments
1546 TransformOvalToPolygon( aBuffer, pts[0], pts[1], width, aError, aErrorLoc );
1547 TransformOvalToPolygon( aBuffer, pts[1], pts[2], width, aError, aErrorLoc );
1548 TransformOvalToPolygon( aBuffer, pts[2], pts[3], width, aError, aErrorLoc );
1549 TransformOvalToPolygon( aBuffer, pts[3], pts[0], width, aError, aErrorLoc );
1550 }
1551
1552 break;
1553 }
1554
1555 case SHAPE_T::ARC:
1556 TransformArcToPolygon( aBuffer, GetStart(), GetArcMid(), GetEnd(), width, aError, aErrorLoc );
1557 break;
1558
1559 case SHAPE_T::SEGMENT:
1560 TransformOvalToPolygon( aBuffer, GetStart(), GetEnd(), width, aError, aErrorLoc );
1561 break;
1562
1563 case SHAPE_T::POLY:
1564 {
1565 if( !IsPolyShapeValid() )
1566 break;
1567
1568 if( IsFilled() )
1569 {
1570 aBuffer.NewOutline();
1571
1572 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1573 {
1574 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
1575
1576 for( int jj = 0; jj < (int) poly.GetPointCount(); ++jj )
1577 aBuffer.Append( poly.GetPoint( jj ) );
1578 }
1579 }
1580
1581 if( width > 0 || !IsFilled() )
1582 {
1583 for( int ii = 0; ii < m_poly.OutlineCount(); ++ii )
1584 {
1585 const SHAPE_LINE_CHAIN& poly = m_poly.Outline( ii );
1586
1587 for( int jj = 0; jj < (int) poly.SegmentCount(); ++jj )
1588 {
1589 const SEG& seg = poly.GetSegment( jj );
1590 TransformOvalToPolygon( aBuffer, seg.A, seg.B, width, aError, aErrorLoc );
1591 }
1592 }
1593 }
1594
1595 break;
1596 }
1597
1598 case SHAPE_T::BEZIER:
1599 {
1600 std::vector<VECTOR2I> ctrlPts = { GetStart(), GetBezierC1(), GetBezierC2(), GetEnd() };
1601 BEZIER_POLY converter( ctrlPts );
1602 std::vector<VECTOR2I> poly;
1603 converter.GetPoly( poly, GetWidth() );
1604
1605 for( unsigned ii = 1; ii < poly.size(); ii++ )
1606 TransformOvalToPolygon( aBuffer, poly[ii - 1], poly[ii], width, aError, aErrorLoc );
1607
1608 break;
1609 }
1610
1611 default:
1613 break;
1614 }
1615}
1616
1617
1620
1621
1622static struct EDA_SHAPE_DESC
1623{
1625 {
1627 .Map( SHAPE_T::SEGMENT, _HKI( "Segment" ) )
1628 .Map( SHAPE_T::RECT, _HKI( "Rectangle" ) )
1629 .Map( SHAPE_T::ARC, _HKI( "Arc" ) )
1630 .Map( SHAPE_T::CIRCLE, _HKI( "Circle" ) )
1631 .Map( SHAPE_T::POLY, _HKI( "Polygon" ) )
1632 .Map( SHAPE_T::BEZIER, _HKI( "Bezier" ) );
1634 .Map( PLOT_DASH_TYPE::DEFAULT, _HKI( "Default" ) )
1635 .Map( PLOT_DASH_TYPE::SOLID, _HKI( "Solid" ) )
1636 .Map( PLOT_DASH_TYPE::DASH, _HKI( "Dashed" ) )
1637 .Map( PLOT_DASH_TYPE::DOT, _HKI( "Dotted" ) )
1638 .Map( PLOT_DASH_TYPE::DASHDOT, _HKI( "Dash-Dot" ) )
1639 .Map( PLOT_DASH_TYPE::DASHDOTDOT, _HKI( "Dash-Dot-Dot" ) );
1640
1643
1644 auto isNotPolygon =
1645 []( INSPECTABLE* aItem ) -> bool
1646 {
1647 // Polygons, unlike other shapes, have no meaningful start or end coordinates
1648 if( EDA_SHAPE* shape = dynamic_cast<EDA_SHAPE*>( aItem ) )
1649 return shape->GetShape() != SHAPE_T::POLY;
1650
1651 return false;
1652 };
1653
1654 auto shape = new PROPERTY_ENUM<EDA_SHAPE, SHAPE_T>( _HKI( "Shape" ),
1656 propMgr.AddProperty( shape );
1657 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start X" ),
1660 .SetAvailableFunc( isNotPolygon );
1661 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Start Y" ),
1664 .SetAvailableFunc( isNotPolygon );
1665 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End X" ),
1668 .SetAvailableFunc( isNotPolygon );
1669 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "End Y" ),
1672 .SetAvailableFunc( isNotPolygon );
1673 // TODO: m_arcCenter, m_bezierC1, m_bezierC2, m_poly
1674 propMgr.AddProperty( new PROPERTY<EDA_SHAPE, int>( _HKI( "Line Width" ),
1676
1677 auto angle = new PROPERTY<EDA_SHAPE, EDA_ANGLE>( _HKI( "Angle" ),
1680 angle->SetAvailableFunc(
1681 [=]( INSPECTABLE* aItem ) -> bool
1682 {
1683 return aItem->Get<SHAPE_T>( shape ) == SHAPE_T::ARC;
1684 } );
1685 propMgr.AddProperty( angle );
1686
1687 auto filled = new PROPERTY<EDA_SHAPE, bool>( _HKI( "Filled" ),
1689 filled->SetAvailableFunc(
1690 [=]( INSPECTABLE* aItem ) -> bool
1691 {
1692 SHAPE_T itemShape;
1693
1694 try
1695 {
1696 itemShape = aItem->Get<SHAPE_T>( shape );
1697 }
1698 catch( std::runtime_error& err )
1699 {
1700 wxFAIL_MSG( err.what() );
1701 return false;
1702 }
1703
1704 switch( itemShape )
1705 {
1706 case SHAPE_T::POLY:
1707 case SHAPE_T::RECT:
1708 case SHAPE_T::CIRCLE:
1709 return true;
1710
1711 default:
1712 return false;
1713 }
1714 } );
1715
1716 propMgr.AddProperty( filled );
1717 }
Bezier curves to polygon converter.
Definition: bezier_curves.h:38
void GetPoly(std::vector< VECTOR2I > &aOutput, int aMinSegLen=0, int aMaxSegCount=32)
Convert a Bezier curve to a polygon.
void SetOrigin(const Vec &pos)
Definition: box2.h:202
BOX2< Vec > & Normalize()
Ensure that the height and width are positive.
Definition: box2.h:119
bool Intersects(const BOX2< Vec > &aRect) const
Definition: box2.h:269
bool IntersectsCircleEdge(const Vec &aCenter, const int aRadius, const int aWidth) const
Definition: box2.h:471
bool Contains(const Vec &aPoint) const
Definition: box2.h:141
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:506
Vec Centre() const
Definition: box2.h:70
void SetEnd(coord_type x, coord_type y)
Definition: box2.h:255
BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:588
EDA_ANGLE Normalize()
Definition: eda_angle.h:249
bool IsCardinal() const
Definition: eda_angle.cpp:49
EDA_ANGLE Normalize720()
Definition: eda_angle.h:299
double AsRadians() const
Definition: eda_angle.h:153
ORIGIN_TRANSFORMS & GetOriginTransforms() override
Return a reference to the default ORIGIN_TRANSFORMS object.
The base class for create windows for drawing purpose.
EDA_ANGLE GetArcAngle() const
Definition: eda_shape.cpp:585
SHAPE_T m_shape
Definition: eda_shape.h:360
wxString GetFriendlyName() const
Definition: eda_shape.cpp:611
void SetStartX(int x)
Definition: eda_shape.h:136
bool m_endsSwapped
Definition: eda_shape.h:359
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:179
void move(const VECTOR2I &aMoveVector)
Definition: eda_shape.cpp:173
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:470
VECTOR2I getCenter() const
Definition: eda_shape.cpp:444
int GetStartY() const
Definition: eda_shape.h:121
int m_editState
Definition: eda_shape.h:377
void rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle)
Definition: eda_shape.cpp:263
void flip(const VECTOR2I &aCentre, bool aFlipLeftRight)
Definition: eda_shape.cpp:320
virtual ~EDA_SHAPE()
Definition: eda_shape.cpp:52
void SetEndY(int y)
Definition: eda_shape.h:155
void RebuildBezierToSegmentsPointsList(int aMinSegLen)
Rebuild the m_bezierPoints vertex list that approximate the Bezier curve by a list of segments.
Definition: eda_shape.cpp:405
virtual int GetEffectiveWidth() const
Definition: eda_shape.h:110
int GetEndX() const
Definition: eda_shape.h:147
const std::vector< VECTOR2I > buildBezierToSegmentsPointsList(int aMinSegLen) const
Definition: eda_shape.cpp:431
void calcEdit(const VECTOR2I &aPosition)
Definition: eda_shape.cpp:1284
void SetStartY(int y)
Definition: eda_shape.h:130
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:247
bool IsFilled() const
Definition: eda_shape.h:90
void CalcArcAngles(EDA_ANGLE &aStartAngle, EDA_ANGLE &aEndAngle) const
Calc arc start and end angles such that aStartAngle < aEndAngle.
Definition: eda_shape.cpp:502
void ShapeGetMsgPanelInfo(EDA_DRAW_FRAME *aFrame, std::vector< MSG_PANEL_ITEM > &aList)
Definition: eda_shape.cpp:626
void SetFilled(bool aFlag)
Definition: eda_shape.h:95
int GetRadius() const
Definition: eda_shape.cpp:523
SHAPE_T GetShape() const
Definition: eda_shape.h:113
VECTOR2I m_arcCenter
Definition: eda_shape.h:368
bool continueEdit(const VECTOR2I &aPosition)
Definition: eda_shape.cpp:1257
wxString ShowShape() const
Definition: eda_shape.cpp:57
ARC_MID m_arcMidData
Definition: eda_shape.h:369
int GetEndY() const
Definition: eda_shape.h:146
std::vector< SHAPE * > makeEffectiveShapes(bool aEdgeOnly, bool aLineChainOnly=false) const
Make a set of SHAPE objects representing the EDA_SHAPE.
Definition: eda_shape.cpp:1095
bool hitTest(const VECTOR2I &aPosition, int aAccuracy=0) const
Definition: eda_shape.cpp:738
void SetCachedArcData(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, const VECTOR2I &aCenter)
Set the data used for mid point caching.
Definition: eda_shape.cpp:546
EDA_SHAPE(SHAPE_T aType, int aLineWidth, FILL_T aFill)
Definition: eda_shape.cpp:40
void beginEdit(const VECTOR2I &aStartPoint)
Definition: eda_shape.cpp:1226
void SetEndX(int x)
Definition: eda_shape.h:161
VECTOR2I m_start
Definition: eda_shape.h:365
int GetPointCount() const
Definition: eda_shape.cpp:1215
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:145
bool IsClosed() const
Definition: eda_shape.cpp:141
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:124
void DupPolyPointsList(std::vector< VECTOR2I > &aBuffer) const
Duplicate the list of corners in a std::vector<VECTOR2I>
Definition: eda_shape.cpp:1186
void endEdit(bool aClosed=true)
Finishes editing the shape.
Definition: eda_shape.cpp:1414
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:120
void SwapShape(EDA_SHAPE *aImage)
Definition: eda_shape.cpp:1446
std::vector< VECTOR2I > GetRectCorners() const
Definition: eda_shape.cpp:1020
std::vector< VECTOR2I > m_bezierPoints
Definition: eda_shape.h:374
int GetWidth() const
Definition: eda_shape.h:109
void setPosition(const VECTOR2I &aPos)
Definition: eda_shape.cpp:93
void computeArcBBox(BOX2I &aBBox) const
Definition: eda_shape.cpp:1035
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:149
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:555
double GetLength() const
Return the length of the track using the hypotenuse calculation.
Definition: eda_shape.cpp:110
wxString SHAPE_T_asString() const
Definition: eda_shape.cpp:75
void scale(double aScale)
Definition: eda_shape.cpp:208
int GetStartX() const
Definition: eda_shape.h:122
const VECTOR2I & GetBezierC1() const
Definition: eda_shape.h:176
VECTOR2I m_end
Definition: eda_shape.h:366
const BOX2I getBoundingBox() const
Definition: eda_shape.cpp:684
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:596
SHAPE_POLY_SET m_poly
Definition: eda_shape.h:375
VECTOR2I getPosition() const
Definition: eda_shape.cpp:99
STROKE_PARAMS m_stroke
Definition: eda_shape.h:361
bool IsAnnotationProxy() const
Definition: eda_shape.h:87
void SetPolyPoints(const std::vector< VECTOR2I > &aPoints)
Definition: eda_shape.cpp:1085
void TransformShapeToPolygon(SHAPE_POLY_SET &aBuffer, int aClearance, int aError, ERROR_LOC aErrorLoc, bool ignoreLineWidth=false) const
Convert the shape to a closed polygon.
Definition: eda_shape.cpp:1510
VECTOR2I m_bezierC1
Definition: eda_shape.h:371
FILL_T m_fill
Definition: eda_shape.h:362
COLOR4D m_fillColor
Definition: eda_shape.h:363
void SetWidth(int aWidth)
Definition: eda_shape.h:108
VECTOR2I m_bezierC2
Definition: eda_shape.h:372
bool IsPolyShapeValid() const
Definition: eda_shape.cpp:1203
int Compare(const EDA_SHAPE *aOther) const
Definition: eda_shape.cpp:1469
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:488
static ENUM_MAP< T > & Instance()
Definition: property.h:627
Class that other classes need to inherit from, in order to be inspectable.
Definition: inspectable.h:36
wxAny Get(PROPERTY_BASE *aProperty) const
Definition: inspectable.h:84
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103
A class to perform either relative or absolute display origin transforms for a single axis of a point...
PROPERTY_BASE & SetAvailableFunc(std::function< bool(INSPECTABLE *)> aFunc)
Set a callback function to determine whether an object provides this property.
Definition: property.h:241
Provide class metadata.Helper macro to map type hashes to names.
Definition: property_mgr.h:74
static PROPERTY_MANAGER & Instance()
Definition: property_mgr.h:76
PROPERTY_BASE & AddProperty(PROPERTY_BASE *aProperty, const wxString &aGroup=wxEmptyString)
Register a property.
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
int Length() const
Return the length (this).
Definition: seg.h:326
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
bool IsClosed() const override
virtual const VECTOR2I GetPoint(int aIndex) const override
void SetPoint(int aIndex, const VECTOR2I &aPos)
Move a point to a specific location.
void SetClosed(bool aClosed)
Mark the line chain as closed (i.e.
int PointCount() const
Return the number of points (vertices) in this line chain.
virtual size_t GetPointCount() const override
void Append(int aX, int aY, bool aAllowDuplication=false)
Append a new point at the end of the line chain.
virtual const SEG GetSegment(int aIndex) const override
const VECTOR2I & CPoint(int aIndex) const
Return a reference to a given point in the line chain.
int SegmentCount() const
Return the number of segments in this line chain.
const VECTOR2I & CLastPoint() const
Return the last point in the line chain.
void Remove(int aStartIndex, int aEndIndex)
Remove the range of points [start_index, end_index] from the line chain.
const SEG CSegment(int aIndex) const
Return a constant copy of the aIndex segment in the line chain.
const std::vector< VECTOR2I > & CPoints() const
SEG Segment(int aIndex)
Return a copy of the aIndex-th segment in the line chain.
Represent a set of closed polygons.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
void RemoveAllContours()
Remove all outlines & holes (clears) the polygon set.
bool CollideEdge(const VECTOR2I &aPoint, VERTEX_INDEX *aClosestVertex=nullptr, int aClearance=0) const
Check whether aPoint collides with any edge of any of the contours of the polygon.
int VertexCount(int aOutline=-1, int aHole=-1) const
Return the number of vertices in a given outline/hole.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
CONST_ITERATOR CIterate(int aFirst, int aLast, bool aIterateHoles=false) const
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
int TotalVertices() const
Return total number of vertices stored in the set.
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)
void Mirror(bool aX=true, bool aY=false, const VECTOR2I &aRef={ 0, 0 })
Mirror the line points about y or x (or both)
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.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the index-th vertex in a given hole outline within a given outline.
int OutlineCount() const
Return the number of outlines in the set.
void Move(const VECTOR2I &aVector) override
const SHAPE_LINE_CHAIN & COutline(int aIndex) const
Represent a simple polygon consisting of a zero-thickness closed chain of connected line segments.
Definition: shape_simple.h:42
int GetWidth() const
Definition: stroke_params.h:98
void GetMsgPanelInfo(UNITS_PROVIDER *aUnitsProvider, std::vector< MSG_PANEL_ITEM > &aList, bool aIncludeStyle=true, bool aIncludeWidth=true)
PLOT_DASH_TYPE GetPlotStyle() const
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A lower-precision version of StringFromValue().
extended_type SquaredEuclideanNorm() const
Compute the squared euclidean norm of the vector, which is defined as (x ** 2 + y ** 2).
Definition: vector2d.h:272
VECTOR2_TRAITS< int >::extended_type extended_type
Definition: vector2d.h:72
void TransformRingToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aCentre, int aRadius, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arcs to multiple straight segments.
void TransformCircleToPolygon(SHAPE_LINE_CHAIN &aBuffer, const VECTOR2I &aCenter, int aRadius, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a circle to a polygon, using multiple straight lines.
void TransformArcToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc)
Convert arc to multiple straight segments.
void TransformOvalToPolygon(SHAPE_POLY_SET &aBuffer, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aWidth, int aError, ERROR_LOC aErrorLoc, int aMinSegCount=0)
Convert a oblong shape to a polygon, using multiple segments.
#define _HKI(x)
#define _(s)
static constexpr EDA_ANGLE & ANGLE_180
Definition: eda_angle.h:433
@ RADIANS_T
Definition: eda_angle.h:32
static constexpr EDA_ANGLE & ANGLE_360
Definition: eda_angle.h:435
static constexpr EDA_ANGLE & ANGLE_90
Definition: eda_angle.h:431
static constexpr EDA_ANGLE & ANGLE_0
Definition: eda_angle.h:429
static constexpr EDA_ANGLE & ANGLE_270
Definition: eda_angle.h:434
#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:41
FILL_T
Definition: eda_shape.h:54
ERROR_LOC
When approximating an arc or circle, should the error be placed on the outside or inside of the curve...
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:120
wxString MessageTextFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A helper to convert the double length aValue to a string in inches, millimeters, or unscaled units.
Definition: eda_units.cpp:315
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:418
Plot settings, and plotting engines (PostScript, Gerber, HPGL and DXF)
#define NO_SETTER(owner, type)
Definition: property.h:738
#define ENUM_TO_WXANY(type)
Macro to define read-only fields (no setter method available)
Definition: property.h:729
@ PT_COORD
Coordinate expressed in distance units (mm/inch)
Definition: property.h:57
@ PT_DECIDEGREE
Angle expressed in decidegrees.
Definition: property.h:59
@ PT_SIZE
Size expressed in distance units (mm/inch)
Definition: property.h:56
#define REGISTER_TYPE(x)
Definition: property_mgr.h:328
PLOT_DASH_TYPE
Dashed line types.
Definition: stroke_params.h:48
VECTOR2I end
Definition: eda_shape.h:67
VECTOR2I center
Definition: eda_shape.h:68
VECTOR2I start
Definition: eda_shape.h:66
VECTOR2I mid
Definition: eda_shape.h:65
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:129
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Definition: trigo.cpp:183
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:458
double GetLineLength(const VECTOR2I &aPointA, const VECTOR2I &aPointB)
Return the length of a line segment defined by aPointA and aPointB.
Definition: trigo.h:188
double EuclideanNorm(const VECTOR2I &vector)
Definition: trigo.h:129
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:85
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588