KiCad PCB EDA Suite
Loading...
Searching...
No Matches
point_editor_behavior.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21
22#include <algorithm>
23
24#include <advanced_config.h>
25#include <commit.h>
27#include <math/util.h>
28#include <reference_image.h>
29
30
32 const SHAPE_POLY_SET& aOutline )
33{
34 const int cornersCount = aOutline.TotalVertices();
35
36 if( cornersCount == 0 )
37 return;
38
39 for( auto iterator = aOutline.CIterateWithHoles(); iterator; iterator++ )
40 {
41 aPoints.AddPoint( *iterator );
42
43 if( iterator.IsEndContour() )
44 aPoints.AddBreak();
45 }
46
47 // Lines have to be added after creating edit points, as they use EDIT_POINT references
48 for( int i = 0; i < cornersCount - 1; ++i )
49 {
50 if( aPoints.IsContourEnd( i ) )
51 aPoints.AddLine( aPoints.Point( i ), aPoints.Point( aPoints.GetContourStartIdx( i ) ) );
52 else
53 aPoints.AddLine( aPoints.Point( i ), aPoints.Point( i + 1 ) );
54
55 aPoints.Line( i ).SetDragPolicy(
56 std::make_unique<POLYGON_EDGE_DRAG_POLICY>( aPoints.Line( i ), aPoints ) );
57 }
58
59 // The last missing line, connecting the last and the first polygon point
60 aPoints.AddLine( aPoints.Point( cornersCount - 1 ),
61 aPoints.Point( aPoints.GetContourStartIdx( cornersCount - 1 ) ) );
62
63 aPoints.Line( aPoints.LinesSize() - 1 )
64 .SetDragPolicy( std::make_unique<POLYGON_EDGE_DRAG_POLICY>(
65 aPoints.Line( aPoints.LinesSize() - 1 ), aPoints ) );
66}
67
68
70 EDIT_POINTS& aPoints )
71{
72 // No size check here, as we can and will rebuild if that fails
73 if( aPoints.PointsSize() != (unsigned) aOutline.TotalVertices() )
74 {
75 // Rebuild the points list
76 aPoints.Clear();
77 BuildForPolyOutline( aPoints, aOutline );
78 }
79 else
80 {
81 for( int i = 0; i < aOutline.TotalVertices(); ++i )
82 aPoints.Point( i ).SetPosition( aOutline.CVertex( i ) );
83 }
84}
85
86
88 const EDIT_POINT& aEditedPoint,
89 EDIT_POINTS& aPoints )
90{
91 CHECK_POINT_COUNT_GE( aPoints, (unsigned) aOutline.TotalVertices() );
92
93 for( int i = 0; i < aOutline.TotalVertices(); ++i )
94 aOutline.SetVertex( i, aPoints.Point( i ).GetPosition() );
95
96 for( unsigned i = 0; i < aPoints.LinesSize(); ++i )
97 {
98 if( !isModified( aEditedPoint, aPoints.Line( i ) ) )
99 aPoints.Line( i ).SetDragPolicy(
100 std::make_unique<POLYGON_EDGE_DRAG_POLICY>( aPoints.Line( i ), aPoints ) );
101 }
102}
103
104
106{
107 SHAPE_POLY_SET cleaned = aOutline;
108
109 cleaned.RemoveNullSegments();
110
111 if( cleaned.TotalVertices() >= 3 )
112 aOutline = cleaned;
113}
114
115
120
121
123{
124 aPoints.AddPoint( m_segment.GetStart() );
125 aPoints.AddPoint( m_segment.GetEnd() );
126}
127
128
130{
131 wxCHECK( aPoints.PointsSize() == SEGMENT_MAX_POINTS, false );
132
133 aPoints.Point( SEGMENT_START ) = m_segment.GetStart();
134 aPoints.Point( SEGMENT_END ) = m_segment.GetEnd();
135 return true;
136}
137
138
140 EDIT_POINTS& aPoints, COMMIT& aCommit,
141 std::vector<EDA_ITEM*>& aUpdatedItems )
142{
144
145 if( isModified( aEditedPoint, aPoints.Point( SEGMENT_START ) ) )
146 m_segment.SetStart( aPoints.Point( SEGMENT_START ).GetPosition() );
147
148 else if( isModified( aEditedPoint, aPoints.Point( SEGMENT_END ) ) )
149 m_segment.SetEnd( aPoints.Point( SEGMENT_END ).GetPosition() );
150}
151
152
154{
155 const ELLIPSE<int>& ellipse = m_ellipse.GetEllipse();
156 return ellipse.GetPointAtAngle( aTheta );
157}
158
159
161{
162 const ELLIPSE<int>& ellipse = m_ellipse.GetEllipse();
163 return ellipse.GetAngleAtPoint( aWorldPt );
164}
165
166
168{
169 const ELLIPSE<int>& ellipse = m_ellipse.GetEllipse();
170
171 aPoints.AddPoint( ellipse.Center );
172 aPoints.AddPoint( ellipse.GetPointAtAngle( ANGLE_0 ) );
173 aPoints.AddPoint( ellipse.GetPointAtAngle( ANGLE_90 ) );
174
175 if( m_ellipse.GetShape() == SHAPE_T::ELLIPSE_ARC )
176 {
177 aPoints.AddPoint( evaluateAt( m_ellipse.GetEllipseStartAngle() ) );
178 aPoints.AddPoint( evaluateAt( m_ellipse.GetEllipseEndAngle() ) );
179
180 aPoints.AddIndicatorLine( aPoints.Point( ELLIPSE_CENTER ), aPoints.Point( ELLIPSE_ARC_START ) );
181 aPoints.AddIndicatorLine( aPoints.Point( ELLIPSE_CENTER ), aPoints.Point( ELLIPSE_ARC_END ) );
182 }
183}
184
185
187{
188 const bool isArc = ( m_ellipse.GetShape() == SHAPE_T::ELLIPSE_ARC );
189 const size_t expected = isArc ? ELLIPSE_ARC_POINTS : ELLIPSE_CLOSED_POINTS;
190
191 wxCHECK( aPoints.PointsSize() == expected, false );
192
193 const ELLIPSE<int>& ellipse = m_ellipse.GetEllipse();
194
195 aPoints.Point( ELLIPSE_CENTER ).SetPosition( ellipse.Center );
198
199 if( isArc )
200 {
201 aPoints.Point( ELLIPSE_ARC_START ).SetPosition( evaluateAt( m_ellipse.GetEllipseStartAngle() ) );
202 aPoints.Point( ELLIPSE_ARC_END ).SetPosition( evaluateAt( m_ellipse.GetEllipseEndAngle() ) );
203 }
204
205 return true;
206}
207
208
209void EDA_ELLIPSE_POINT_EDIT_BEHAVIOR::UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
210 std::vector<EDA_ITEM*>& aUpdatedItems )
211{
212 const bool isArc = ( m_ellipse.GetShape() == SHAPE_T::ELLIPSE_ARC );
213 const size_t expected = isArc ? ELLIPSE_ARC_POINTS : ELLIPSE_CLOSED_POINTS;
214
215 CHECK_POINT_COUNT( aPoints, expected );
216
217 if( isModified( aEditedPoint, aPoints.Point( ELLIPSE_CENTER ) ) )
218 {
219 m_ellipse.SetEllipseCenter( aPoints.Point( ELLIPSE_CENTER ).GetPosition() );
220 }
221 else if( isModified( aEditedPoint, aPoints.Point( ELLIPSE_MAJOR_END ) ) )
222 {
223 const VECTOR2I center = m_ellipse.GetEllipseCenter();
224 const VECTOR2I v = aPoints.Point( ELLIPSE_MAJOR_END ).GetPosition() - center;
225 const double len = std::sqrt( double( v.x ) * v.x + double( v.y ) * v.y );
226
227 m_ellipse.SetEllipseMajorRadius( std::max( 1, KiROUND( len ) ) );
228 m_ellipse.SetEllipseRotation( EDA_ANGLE( std::atan2( (double) v.y, (double) v.x ), RADIANS_T ) );
229 }
230 else if( isModified( aEditedPoint, aPoints.Point( ELLIPSE_MINOR_END ) ) )
231 {
232 const VECTOR2I center = m_ellipse.GetEllipseCenter();
233 const VECTOR2I v = aPoints.Point( ELLIPSE_MINOR_END ).GetPosition() - center;
234 const EDA_ANGLE rotation = m_ellipse.GetEllipseRotation();
235
236 // Absolute value so dragging past the rotation axis keeps radius positive.
237 const double proj = -v.x * rotation.Sin() + v.y * rotation.Cos();
238
239 m_ellipse.SetEllipseMinorRadius( std::max( 1, KiROUND( std::abs( proj ) ) ) );
240 }
241 else if( isArc && isModified( aEditedPoint, aPoints.Point( ELLIPSE_ARC_START ) ) )
242 {
244 EDA_ANGLE prevStart = m_ellipse.GetEllipseStartAngle();
245 EDA_ANGLE sweep = m_ellipse.GetEllipseEndAngle() - prevStart;
246
247 while( ( rawAngle - prevStart ).AsDegrees() > 180.0 )
248 rawAngle = rawAngle - ANGLE_360;
249 while( ( prevStart - rawAngle ).AsDegrees() > 180.0 )
250 rawAngle = rawAngle + ANGLE_360;
251
252 m_ellipse.SetEllipseStartAngle( rawAngle );
253 m_ellipse.SetEllipseEndAngle( rawAngle + sweep );
254 }
255 else if( isArc && isModified( aEditedPoint, aPoints.Point( ELLIPSE_ARC_END ) ) )
256 {
258 EDA_ANGLE prevEnd = m_ellipse.GetEllipseEndAngle();
259
260 while( ( rawAngle - prevEnd ).AsDegrees() > 180.0 )
261 rawAngle = rawAngle - ANGLE_360;
262 while( ( prevEnd - rawAngle ).AsDegrees() > 180.0 )
263 rawAngle = rawAngle + ANGLE_360;
264
265 m_ellipse.SetEllipseEndAngle( rawAngle );
266 }
267}
268
269
271 EDIT_POINTS& aPoints ) const
272{
273 // Select the other end of line
274 return aPoints.Next( aEditedPoint )->GetPosition();
275}
276
277
279{
280 aPoints.AddPoint( m_circle.getCenter() );
281 aPoints.AddPoint( m_circle.GetEnd() );
282}
283
284
286{
287 wxCHECK( aPoints.PointsSize() == CIRC_MAX_POINTS, false );
288
289 aPoints.Point( CIRC_CENTER ).SetPosition( m_circle.getCenter() );
290 aPoints.Point( CIRC_END ).SetPosition( m_circle.GetEnd() );
291 return true;
292}
293
294
296 EDIT_POINTS& aPoints, COMMIT& aCommit,
297 std::vector<EDA_ITEM*>& aUpdatedItems )
298{
300
301 const VECTOR2I& center = aPoints.Point( CIRC_CENTER ).GetPosition();
302 const VECTOR2I& end = aPoints.Point( CIRC_END ).GetPosition();
303
304 if( isModified( aEditedPoint, aPoints.Point( CIRC_CENTER ) ) )
305 m_circle.SetCenter( center );
306 else
307 m_circle.SetEnd( VECTOR2I( end.x, end.y ) );
308}
309
310
312 EDIT_POINTS& aPoints ) const
313{
314 return aPoints.Point( CIRC_CENTER ).GetPosition();
315}
316
317
319{
320 aPoints.AddPoint( m_bezier.GetStart() );
321 aPoints.AddPoint( m_bezier.GetBezierC1() );
322 aPoints.AddPoint( m_bezier.GetBezierC2() );
323 aPoints.AddPoint( m_bezier.GetEnd() );
324
325 aPoints.AddIndicatorLine( aPoints.Point( BEZIER_START ), aPoints.Point( BEZIER_CTRL_PT1 ) );
326 aPoints.AddIndicatorLine( aPoints.Point( BEZIER_CTRL_PT2 ), aPoints.Point( BEZIER_END ) );
327}
328
329
331{
332 wxCHECK( aPoints.PointsSize() == BEZIER_MAX_POINTS, false );
333
334 aPoints.Point( BEZIER_START ).SetPosition( m_bezier.GetStart() );
335 aPoints.Point( BEZIER_CTRL_PT1 ).SetPosition( m_bezier.GetBezierC1() );
336 aPoints.Point( BEZIER_CTRL_PT2 ).SetPosition( m_bezier.GetBezierC2() );
337 aPoints.Point( BEZIER_END ).SetPosition( m_bezier.GetEnd() );
338 return true;
339}
340
341
343 COMMIT& aCommit, std::vector<EDA_ITEM*>& aUpdatedItems )
344{
346
347 if( isModified( aEditedPoint, aPoints.Point( BEZIER_START ) ) )
348 {
349 m_bezier.SetStart( aPoints.Point( BEZIER_START ).GetPosition() );
350 }
351 else if( isModified( aEditedPoint, aPoints.Point( BEZIER_CTRL_PT1 ) ) )
352 {
353 m_bezier.SetBezierC1( aPoints.Point( BEZIER_CTRL_PT1 ).GetPosition() );
354 }
355 else if( isModified( aEditedPoint, aPoints.Point( BEZIER_CTRL_PT2 ) ) )
356 {
357 m_bezier.SetBezierC2( aPoints.Point( BEZIER_CTRL_PT2 ).GetPosition() );
358 }
359 else if( isModified( aEditedPoint, aPoints.Point( BEZIER_END ) ) )
360 {
361 m_bezier.SetEnd( aPoints.Point( BEZIER_END ).GetPosition() );
362 }
363
364 m_bezier.RebuildBezierToSegmentsPointsList( m_maxError );
365}
366
367
368// Note: these static arc functions don't have to be in here - we could ship them out
369// to a utils area for use by other code (e.g. polygon fillet editing).
370
374static void editArcEndpointKeepTangent( EDA_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
375 const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCursor )
376{
377 VECTOR2I center = aCenter;
378 bool movingStart;
379 bool arcValid = true;
380
381 VECTOR2I p1, p2, p3;
382 // p1 does not move, p2 does.
383
384 if( aStart != aArc.GetStart() )
385 {
386 p1 = aEnd;
387 p2 = aStart;
388 p3 = aMid;
389 movingStart = true;
390 }
391 else if( aEnd != aArc.GetEnd() )
392 {
393 p1 = aStart;
394 p2 = aEnd;
395 p3 = aMid;
396 movingStart = false;
397 }
398 else
399 {
400 return;
401 }
402
403 VECTOR2D v1, v2, v3, v4;
404
405 // Move the coordinate system
406 v1 = p1 - aCenter;
407 v2 = p2 - aCenter;
408 v3 = p3 - aCenter;
409
410 VECTOR2D u1, u2;
411
412 // A point cannot be both the center and on the arc.
413 if( ( v1.EuclideanNorm() == 0 ) || ( v2.EuclideanNorm() == 0 ) )
414 return;
415
416 u1 = v1 / v1.EuclideanNorm();
417 u2 = v3 - ( u1.x * v3.x + u1.y * v3.y ) * u1;
418 u2 = u2 / u2.EuclideanNorm();
419
420 // [ u1, u3 ] is a base centered on the circle with:
421 // u1 : unit vector toward the point that does not move
422 // u2 : unit vector toward the mid point.
423
424 // Get vectors v1, and v2 in that coordinate system.
425
426 double det = u1.x * u2.y - u2.x * u1.y;
427
428 // u1 and u2 are unit vectors, and perpendicular.
429 // det should not be 0. In case it is, do not change the arc.
430 if( det == 0 )
431 return;
432
433 double tmpx = v1.x * u2.y - v1.y * u2.x;
434 double tmpy = -v1.x * u1.y + v1.y * u1.x;
435 v1.x = tmpx;
436 v1.y = tmpy;
437 v1 = v1 / det;
438
439 tmpx = v2.x * u2.y - v2.y * u2.x;
440 tmpy = -v2.x * u1.y + v2.y * u1.x;
441 v2.x = tmpx;
442 v2.y = tmpy;
443 v2 = v2 / det;
444
445 double R = v1.EuclideanNorm();
446 bool transformCircle = false;
447
448 /* p2
449 * X***
450 * ** <---- This is the arc
451 * y ^ **
452 * | R *
453 * | <-----------> *
454 * x------x------>--------x p1
455 * C' <----> C x
456 * delta
457 *
458 * p1 does not move, and the tangent at p1 remains the same.
459 * => The new center, C', will be on the C-p1 axis.
460 * p2 moves
461 *
462 * The radius of the new circle is delta + R
463 *
464 * || C' p2 || = || C' P1 ||
465 * is the same as :
466 * ( delta + p2.x ) ^ 2 + p2.y ^ 2 = ( R + delta ) ^ 2
467 *
468 * delta = ( R^2 - p2.x ^ 2 - p2.y ^2 ) / ( 2 * p2.x - 2 * R )
469 *
470 * We can use this equation for any point p2 with p2.x < R
471 */
472
473 if( v2.x == R )
474 {
475 // Straight line, do nothing
476 }
477 else
478 {
479 if( v2.x > R )
480 {
481 // If we need to invert the curvature.
482 // We modify the input so we can use the same equation
483 transformCircle = true;
484 v2.x = 2 * R - v2.x;
485 }
486
487 // We can keep the tangent constraint.
488 double delta = ( R * R - v2.x * v2.x - v2.y * v2.y ) / ( 2 * v2.x - 2 * R );
489
490 // This is just to limit the radius, so nothing overflows later when drawing.
491 if( abs( v2.y / ( R - v2.x ) ) > ADVANCED_CFG::GetCfg().m_DrawArcCenterMaxAngle )
492 arcValid = false;
493
494 // Never recorded a problem, but still checking.
495 if( !std::isfinite( delta ) )
496 arcValid = false;
497
498 // v4 is the new center
499 v4 = ( !transformCircle ) ? VECTOR2D( -delta, 0 ) : VECTOR2D( 2 * R + delta, 0 );
500
501 tmpx = v4.x * u1.x + v4.y * u2.x;
502 tmpy = v4.x * u1.y + v4.y * u2.y;
503 v4.x = tmpx;
504 v4.y = tmpy;
505
506 center = v4 + aCenter;
507
508 if( arcValid )
509 {
510 aArc.SetCenter( center );
511
512 if( movingStart )
513 aArc.SetStart( aStart );
514 else
515 aArc.SetEnd( aEnd );
516 }
517 }
518}
519
520
524static void editArcCenterKeepEndpoints( EDA_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
525 const VECTOR2I& aMid, const VECTOR2I& aEnd )
526{
527 const int c_snapEpsilon_sq = 4;
528
529 VECTOR2I m = ( aStart / 2 + aEnd / 2 );
530 VECTOR2I perp = ( aEnd - aStart ).Perpendicular().Resize( INT_MAX / 2 );
531
532 SEG legal( m - perp, m + perp );
533
534 const SEG testSegments[] = {
535 SEG( aCenter, aCenter + VECTOR2( 1, 0 ) ),
536 SEG( aCenter, aCenter + VECTOR2( 0, 1 ) ),
537 };
538
539 std::vector<VECTOR2I> points = { legal.A, legal.B };
540
541 for( const SEG& seg : testSegments )
542 {
543 OPT_VECTOR2I vec = legal.IntersectLines( seg );
544
545 if( vec && legal.SquaredDistance( *vec ) <= c_snapEpsilon_sq )
546 points.push_back( *vec );
547 }
548
549 OPT_VECTOR2I nearest;
551
552 // Snap by distance between cursor and intersections
553 for( const VECTOR2I& pt : points )
554 {
555 SEG::ecoord d_sq = ( pt - aCenter ).SquaredEuclideanNorm();
556
557 if( d_sq < min_d_sq - c_snapEpsilon_sq )
558 {
559 min_d_sq = d_sq;
560 nearest = pt;
561 }
562 }
563
564 if( nearest )
565 aArc.SetCenter( *nearest );
566}
567
568
573 const VECTOR2I& aStart, const VECTOR2I& aMid,
574 const VECTOR2I& aEnd, const VECTOR2I& aCursor,
575 const EDA_IU_SCALE& aIuScale )
576{
577 // 1 mil floor in the caller's units keeps the arc non-degenerate without
578 // snapping small eeschema arcs that are legitimately under 100 mils.
579 int minRadius = EDA_UNIT_UTILS::Mils2IU( aIuScale, 1 );
580 bool movingStart;
581
582 VECTOR2I p1, p2, prev_p1;
583
584 // user is moving p1, we want to move p2 to the new radius.
585
586 if( aStart != aArc.GetStart() )
587 {
588 prev_p1 = aArc.GetStart();
589 p1 = aStart;
590 p2 = aEnd;
591 movingStart = true;
592 }
593 else
594 {
595 prev_p1 = aArc.GetEnd();
596 p1 = aEnd;
597 p2 = aStart;
598 movingStart = false;
599 }
600
601 p1 = p1 - aCenter;
602 p2 = p2 - aCenter;
603
604 if( p1.x == 0 && p1.y == 0 )
605 p1 = prev_p1 - aCenter;
606
607 if( p2.x == 0 && p2.y == 0 )
608 p2 = { 1, 0 };
609
610 double radius = p1.EuclideanNorm();
611
612 if( radius < minRadius )
613 radius = minRadius;
614
615 p1 = aCenter + p1.Resize( KiROUND( radius ) );
616 p2 = aCenter + p2.Resize( KiROUND( radius ) );
617
618 aArc.SetCenter( aCenter );
619
620 if( movingStart )
621 {
622 aArc.SetStart( p1 );
623 aArc.SetEnd( p2 );
624 }
625 else
626 {
627 aArc.SetStart( p2 );
628 aArc.SetEnd( p1 );
629 }
630}
631
632
633static void editArcEndpointKeepCenterAndRadius( EDA_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
634 const VECTOR2I& aMid, const VECTOR2I& aEnd )
635{
636 VECTOR2I p1;
637 bool movingStart = false;
638
639 // User is moving p1, we need to update whichever end that is
640 // The other end won't move.
641
642 if( aStart != aArc.GetStart() )
643 {
644 p1 = aStart;
645 movingStart = true;
646 }
647 else
648 {
649 p1 = aEnd;
650 movingStart = false;
651 }
652
653 // Do not change the radius
654 p1 = p1 - aCenter;
655 p1 = aCenter + p1.Resize( aArc.GetRadius() );
656
657 if( movingStart )
658 {
659 aArc.SetStart( p1 );
660 }
661 else
662 {
663 aArc.SetEnd( p1 );
664 }
665}
666
667
672 const VECTOR2I& aStart, const VECTOR2I& aMid,
673 const VECTOR2I& aEnd, const VECTOR2I& aCursor,
674 const EDA_IU_SCALE& aIuScale )
675{
676 // See EditArcEndpointKeepCenter for why we use the caller's IU scale.
677 int minRadius = EDA_UNIT_UTILS::Mils2IU( aIuScale, 1 );
678
679 // Now, update the edit point position
680 // Express the point in a circle-centered coordinate system.
681 VECTOR2I start = aStart - aCenter;
682 VECTOR2I end = aEnd - aCenter;
683
684 double radius = ( aCursor - aCenter ).EuclideanNorm();
685
686 if( radius < minRadius )
687 radius = minRadius;
688
689 start = start.Resize( KiROUND( radius ) );
690 end = end.Resize( KiROUND( radius ) );
691
692 start = start + aCenter;
693 end = end + aCenter;
694
695 aArc.SetStart( start );
696 aArc.SetEnd( end );
697}
698
699
703static void editArcMidKeepEndpoints( EDA_SHAPE& aArc, const VECTOR2I& aStart, const VECTOR2I& aEnd,
704 const VECTOR2I& aCursor )
705{
706 // Let 'm' be the middle point of the chord between the start and end points
707 VECTOR2I m = ( aStart + aEnd ) / 2;
708
709 // Legal midpoints lie on a vector starting just off the chord midpoint and extending out
710 // past the existing midpoint. We do not allow arc inflection while point editing.
711 const int JUST_OFF = ( aStart - aEnd ).EuclideanNorm() / 100;
712 VECTOR2I v = (VECTOR2I) aArc.GetArcMid() - m;
713 SEG legal( m + v.Resize( JUST_OFF ), m + v.Resize( INT_MAX / 2 ) );
714 VECTOR2I mid = legal.NearestPoint( aCursor );
715
716 if( !IsStraightArc( aStart, mid, aEnd ) )
717 aArc.SetArcGeometry( aStart, mid, aEnd );
718}
719
720
722 KIGFX::VIEW_CONTROLS& aViewContols,
723 const EDA_IU_SCALE& aIuScale ) :
724 m_arc( aArc ),
725 m_arcEditMode( aArcEditMode ),
726 m_viewControls( aViewContols ),
727 m_iuScale( aIuScale )
728{
729 wxASSERT( m_arc.GetShape() == SHAPE_T::ARC );
730}
731
732
734{
735 aPoints.AddPoint( m_arc.GetStart() );
736 aPoints.AddPoint( m_arc.GetArcMid() );
737 aPoints.AddPoint( m_arc.GetEnd() );
738 aPoints.AddPoint( m_arc.getCenter() );
739
740 aPoints.AddIndicatorLine( aPoints.Point( ARC_CENTER ), aPoints.Point( ARC_START ) );
741 aPoints.AddIndicatorLine( aPoints.Point( ARC_CENTER ), aPoints.Point( ARC_END ) );
742}
743
744
746{
747 wxCHECK( aPoints.PointsSize() == 4, false );
748
749 aPoints.Point( ARC_START ).SetPosition( m_arc.GetStart() );
750 aPoints.Point( ARC_MID ).SetPosition( m_arc.GetArcMid() );
751 aPoints.Point( ARC_END ).SetPosition( m_arc.GetEnd() );
752 aPoints.Point( ARC_CENTER ).SetPosition( m_arc.getCenter() );
753 return true;
754}
755
756
757void EDA_ARC_POINT_EDIT_BEHAVIOR::UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
758 std::vector<EDA_ITEM*>& aUpdatedItems )
759{
760 CHECK_POINT_COUNT( aPoints, 4 );
761
763 VECTOR2I mid = aPoints.Point( ARC_MID ).GetPosition();
764 VECTOR2I start = aPoints.Point( ARC_START ).GetPosition();
765 VECTOR2I end = aPoints.Point( ARC_END ).GetPosition();
766
767 if( isModified( aEditedPoint, aPoints.Point( ARC_CENTER ) ) )
768 {
769 switch( m_arcEditMode )
770 {
773 break;
774
777 {
778 // Both these modes just move the arc
779 VECTOR2I moveVector = center - m_arc.getCenter();
780
781 m_arc.SetArcGeometry( m_arc.GetStart() + moveVector, m_arc.GetArcMid() + moveVector,
782 m_arc.GetEnd() + moveVector );
783 break;
784 }
785 }
786 }
787 else if( isModified( aEditedPoint, aPoints.Point( ARC_MID ) ) )
788 {
789 const VECTOR2I& cursorPos = m_viewControls.GetCursorPosition( false );
790
791 switch( m_arcEditMode )
792 {
794 editArcMidKeepEndpoints( m_arc, start, end, cursorPos );
795 break;
798 KI_ARC_EDIT::EditArcMidKeepCenter( m_arc, center, start, mid, end, cursorPos, m_iuScale );
799 break;
800 }
801 }
802 else if( isModified( aEditedPoint, aPoints.Point( ARC_START ) )
803 || isModified( aEditedPoint, aPoints.Point( ARC_END ) ) )
804 {
805 const VECTOR2I& cursorPos = m_viewControls.GetCursorPosition();
806
807 switch( m_arcEditMode )
808 {
811 break;
814 break;
816 editArcEndpointKeepTangent( m_arc, center, start, mid, end, cursorPos );
817 break;
818 }
819 }
820}
821
822
824 EDIT_POINTS& aPoints ) const
825{
826 return aPoints.Point( ARC_CENTER ).GetPosition();
827}
828
829
831{
832 aPoints.AddPoint( m_cell.GetEnd() - VECTOR2I( 0, m_cell.GetRectangleHeight() / 2 ) );
833 aPoints.AddPoint( m_cell.GetEnd() - VECTOR2I( m_cell.GetRectangleWidth() / 2, 0 ) );
834}
835
836
838{
839 aPoints.Point( COL_WIDTH ).SetPosition( m_cell.GetEnd() - VECTOR2I( 0, m_cell.GetRectangleHeight() / 2 ) );
840 aPoints.Point( ROW_HEIGHT ).SetPosition( m_cell.GetEnd() - VECTOR2I( m_cell.GetRectangleWidth() / 2, 0 ) );
841 return true;
842}
843
844
846{
847 for( int i = 0; i < REFIMG_MAX_POINTS; ++i )
848 aPoints.AddPoint( {} );
849
850 // Four lines to mark the corners of the bounding box.
851 aPoints.AddIndicatorLine( aPoints.Point( REFIMG_TOP_LEFT ), aPoints.Point( REFIMG_TOP_RIGHT ) );
852 aPoints.AddIndicatorLine( aPoints.Point( REFIMG_TOP_RIGHT ), aPoints.Point( REFIMG_BOT_RIGHT ) );
853 aPoints.AddIndicatorLine( aPoints.Point( REFIMG_BOT_RIGHT ), aPoints.Point( REFIMG_BOT_LEFT ) );
854 aPoints.AddIndicatorLine( aPoints.Point( REFIMG_BOT_LEFT ), aPoints.Point( REFIMG_TOP_LEFT ) );
855
856 aPoints.AddIndicatorLine( aPoints.Point( REFIMG_ORIGIN ), aPoints.Point( REFIMG_ROTATION ) );
857
858 UpdatePoints( aPoints );
859}
860
861
863{
864 wxCHECK( aPoints.PointsSize() == REFIMG_MAX_POINTS, false );
865
866 const VECTOR2I size = m_image.GetBoundingBox().GetSize();
867
868 const VECTOR2I topLeft = m_image.GetPosition() - size / 2;
869 const VECTOR2I botRight = m_image.GetPosition() + size / 2;
870
871 aPoints.Point( REFIMG_TOP_LEFT ).SetPosition( topLeft );
872 aPoints.Point( REFIMG_TOP_RIGHT ).SetPosition( VECTOR2I( botRight.x, topLeft.y ) );
873 aPoints.Point( REFIMG_BOT_RIGHT ).SetPosition( botRight );
874 aPoints.Point( REFIMG_BOT_LEFT ).SetPosition( VECTOR2I( topLeft.x, botRight.y ) );
875
876 const VECTOR2I origin = m_image.GetPosition() + m_image.GetTransformOriginOffset();
877 aPoints.Point( REFIMG_ORIGIN ).SetPosition( origin );
878
879 // The rotation handle extends above the image by a short distance (20%), anchored at the
880 // transform origin.
881 const EDA_ANGLE angle = m_image.GetOrientation();
882 const VECTOR2I localOffset = GetRotated( m_image.GetTransformOriginOffset(), -angle );
883 const int handleLength = localOffset.y + KiROUND( m_image.GetSize().y * 0.7 );
884
885 // Now we have the length of the handle, construct the vector
886 const VECTOR2I originToRot = GetRotated( VECTOR2I( 0, -handleLength ), angle );
887
888 aPoints.Point( REFIMG_ROTATION ).SetPosition( origin + originToRot );
889
890 return true;
891}
892
893
895 COMMIT& aCommit, std::vector<EDA_ITEM*>& aUpdatedItems )
896{
898
899 const VECTOR2I topLeft = aPoints.Point( REFIMG_TOP_LEFT ).GetPosition();
900 const VECTOR2I topRight = aPoints.Point( REFIMG_TOP_RIGHT ).GetPosition();
901 const VECTOR2I botRight = aPoints.Point( REFIMG_BOT_RIGHT ).GetPosition();
902 const VECTOR2I botLeft = aPoints.Point( REFIMG_BOT_LEFT ).GetPosition();
903 const VECTOR2I xfrmOrigin = aPoints.Point( REFIMG_ORIGIN ).GetPosition();
904
905 if( isModified( aEditedPoint, aPoints.Point( REFIMG_ORIGIN ) ) )
906 {
907 // Moving the transform origin
908 // As the other points didn't move, we can get the image extent from them
909 m_image.SetTransformOriginOffset( xfrmOrigin - ( topLeft + botRight ) / 2 );
910 return;
911 }
912
913 if( isModified( aEditedPoint, aPoints.Point( REFIMG_ROTATION ) ) )
914 {
915 // Rotating the image around the transform origin
916 const VECTOR2I origin = m_image.GetPosition() + m_image.GetTransformOriginOffset();
917 const VECTOR2I cursorVec = aEditedPoint.GetPosition() - origin;
918 const double angle = std::atan2( -cursorVec.y, cursorVec.x );
919
920 // The angle handle is vertical, offset by 90 degrees from the horizontal.
921 const EDA_ANGLE target = EDA_ANGLE( angle, RADIANS_T ) - ANGLE_90;
922
923 // Rotate the image around the transform origin so that the handle
924 // aligns with the target angle.
925 m_image.Rotate( origin, target - m_image.GetOrientation() );
926 return;
927 }
928
929 const VECTOR2I oldOrigin = m_image.GetPosition() + m_image.GetTransformOriginOffset();
930 const VECTOR2I oldSize = m_image.GetBoundingBox().GetSize();
931
932 OPT_VECTOR2I newCorner;
933 VECTOR2I oldCorner = m_image.GetPosition();
934
935 if( isModified( aEditedPoint, aPoints.Point( REFIMG_TOP_LEFT ) ) )
936 {
937 newCorner = topLeft;
938 oldCorner -= oldSize / 2;
939 }
940 else if( isModified( aEditedPoint, aPoints.Point( REFIMG_TOP_RIGHT ) ) )
941 {
942 newCorner = topRight;
943 oldCorner -= VECTOR2I( -oldSize.x, oldSize.y ) / 2;
944 }
945 else if( isModified( aEditedPoint, aPoints.Point( REFIMG_BOT_LEFT ) ) )
946 {
947 newCorner = botLeft;
948 oldCorner -= VECTOR2I( oldSize.x, -oldSize.y ) / 2;
949 }
950 else if( isModified( aEditedPoint, aPoints.Point( REFIMG_BOT_RIGHT ) ) )
951 {
952 newCorner = botRight;
953 oldCorner += oldSize / 2;
954 }
955
956 if( !newCorner )
957 return;
958
959 // Turn in the respective vectors from the origin
960 *newCorner -= xfrmOrigin;
961 oldCorner -= oldOrigin;
962
963 // If we tried to cross the origin, clamp it to stop it
964 if( sign( newCorner->x ) != sign( oldCorner.x ) || sign( newCorner->y ) != sign( oldCorner.y ) )
965 *newCorner = VECTOR2I( 0, 0 );
966
967 const double newLength = newCorner->EuclideanNorm();
968 const double oldLength = oldCorner.EuclideanNorm();
969
970 double ratio = oldLength > 0 ? ( newLength / oldLength ) : 1.0;
971
972 // Clamp the scaling to the minimum image size the editor allows
973 const VECTOR2I newSize = oldSize * ratio;
974 const double newWidth = std::max( newSize.x, m_minSize );
975 const double newHeight = std::max( newSize.y, m_minSize );
976 ratio = std::min( newWidth / oldSize.x, newHeight / oldSize.y );
977
978 // SetImageScale keeps the transform origin in place, which is what the origin handle pins.
979 m_image.SetImageScale( m_image.GetImageScale() * ratio );
980}
981
982
ARC_EDIT_MODE
Settings for arc editing.
@ KEEP_ENDPOINTS_OR_START_DIRECTION
Whe editing endpoints, the other end remains in place.
@ KEEP_CENTER_ENDS_ADJUST_ANGLE
When editing endpoints, only the angle is adjusted.
@ KEEP_CENTER_ADJUST_ANGLE_RADIUS
When editing endpoints, the angle and radius are adjusted.
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition commit.h:68
double Sin() const
Definition eda_angle.h:177
double Cos() const
Definition eda_angle.h:196
EDA_ARC_POINT_EDIT_BEHAVIOR(EDA_SHAPE &aArc, const ARC_EDIT_MODE &aArcEditMode, KIGFX::VIEW_CONTROLS &aViewContols, const EDA_IU_SCALE &aIuScale)
const ARC_EDIT_MODE & m_arcEditMode
void UpdateItem(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) override
Update the item with the new positions of the edit points.
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
OPT_VECTOR2I Get45DegreeConstrainer(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints) const override
Get the 45-degree constrainer for the item, when the given point is moved.
KIGFX::VIEW_CONTROLS & m_viewControls
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
void UpdateItem(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) override
Update the item with the new positions of the edit points.
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
OPT_VECTOR2I Get45DegreeConstrainer(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints) const override
Get the 45-degree constrainer for the item, when the given point is moved.
void UpdateItem(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) override
Update the item with the new positions of the edit points.
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
EDA_ANGLE parametricAngleOf(const VECTOR2I &aWorldPt) const
Inverse: the parametric angle of a world-space point relative to this ellipse.
VECTOR2I evaluateAt(const EDA_ANGLE &aTheta) const
World-space point on the ellipse at the given parametric angle.
void UpdateItem(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) override
Update the item with the new positions of the edit points.
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
OPT_VECTOR2I Get45DegreeConstrainer(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints) const override
Get the 45-degree constrainer for the item, when the given point is moved.
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
void UpdateItem(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) override
Update the item with the new positions of the edit points.
virtual void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:337
virtual void SetCenter(const VECTOR2I &aCenter)
int GetRadius() const
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:333
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:283
virtual void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
virtual void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:287
VECTOR2I GetArcMid() const
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
void SetDragPolicy(std::unique_ptr< POLYGON_EDGE_DRAG_POLICY > aPolicy)
EDIT_POINTS is a VIEW_ITEM that manages EDIT_POINTs and EDIT_LINEs and draws them.
unsigned int PointsSize() const
Return number of stored EDIT_POINTs.
void AddPoint(const EDIT_POINT &aPoint)
Add an EDIT_POINT.
void Clear()
Clear all stored EDIT_POINTs and EDIT_LINEs.
EDIT_POINT * Next(const EDIT_POINT &aPoint, bool aTraverseContours=true)
Return the point that is before the given point in the list.
EDIT_LINE & Line(unsigned int aIndex)
bool IsContourEnd(int aPointIdx) const
Check is a point with given index is a contour finish.
void AddIndicatorLine(EDIT_POINT &aOrigin, EDIT_POINT &aEnd)
Adds an EDIT_LINE that is shown as an indicator, rather than an editable line (no center point drag,...
void AddBreak()
Adds a break, indicating the end of a contour.
int GetContourStartIdx(int aPointIdx) const
Return index of the contour origin for a point with given index.
unsigned int LinesSize() const
Return number of stored EDIT_LINEs.
EDIT_POINT & Point(unsigned int aIndex)
void AddLine(const EDIT_LINE &aLine)
Adds an EDIT_LINE.
Represent a single point that can be used for modifying items.
Definition edit_points.h:44
virtual void SetPosition(const VECTOR2I &aPosition)
Set new coordinates for an EDIT_POINT.
virtual VECTOR2I GetPosition() const
Return coordinates of an EDIT_POINT.
Definition edit_points.h:69
Plain ellipse / elliptical-arc data.
Definition ellipse.h:32
VECTOR2< NumericType > GetPointAtAngle(EDA_ANGLE angle) const
Get the point on the ellipse at a given angle.
Definition ellipse.cpp:90
EDA_ANGLE GetAngleAtPoint(const VECTOR2< NumericType > &aPt) const
Get the parametric angle of a point on the ellipse.
Definition ellipse.cpp:109
VECTOR2< NumericType > Center
Definition ellipse.h:101
An interface for classes handling user events controlling the view behavior such as zooming,...
static bool isModified(const EDIT_POINT &aEditedPoint, const EDIT_POINT &aPoint)
Checks if two points are the same instance - which means the point is being edited.
static void UpdateOutlineFromPoints(SHAPE_POLY_SET &aOutline, const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints)
Update the polygon outline with the new positions of the edit points.
static void UpdatePointsFromOutline(const SHAPE_POLY_SET &aOutline, EDIT_POINTS &aPoints)
Update the edit points with the current polygon outline.
void FinalizeItem(EDIT_POINTS &aPoints, COMMIT &aCommit) override
Finalize the edit operation.
static void BuildForPolyOutline(EDIT_POINTS &aPoints, const SHAPE_POLY_SET &aOutline)
Build the edit points for the given polygon outline.
static void RemoveNullSegmentsIfPolygonSurvives(SHAPE_POLY_SET &aOutline)
Remove the zero length segments left by an edit, unless that would strip the outline down to somethin...
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
void UpdateItem(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) override
Update the item with the new positions of the edit points.
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
ecoord SquaredDistance(const SEG &aSeg) const
Definition seg.cpp:35
VECTOR2I::extended_type ecoord
Definition seg.h:40
VECTOR2I B
Definition seg.h:46
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition seg.cpp:599
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition seg.h:217
Represent a set of closed polygons.
void SetVertex(const VERTEX_INDEX &aIndex, const VECTOR2I &aPos)
Accessor function to set the position of a specific point.
int TotalVertices() const
Return total number of vertices stored in the set.
int RemoveNullSegments()
Look for null segments; ie, segments whose ends are exactly the same and deletes them.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the index-th vertex in a given hole outline within a given outline.
CONST_ITERATOR CIterateWithHoles(int aOutline) const
Define a general 2D-vector/point.
Definition vector2d.h:67
static constexpr extended_type ECOORD_MAX
Definition vector2d.h:72
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:281
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:406
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:450
@ RADIANS_T
Definition eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:454
@ ELLIPSE_ARC
Definition eda_shape.h:63
constexpr int Mils2IU(const EDA_IU_SCALE &aIuScale, int mils)
Definition eda_units.h:172
void EditArcEndpointKeepCenter(EDA_SHAPE &aArc, const VECTOR2I &aCenter, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, const VECTOR2I &aCursor, const EDA_IU_SCALE &aIuScale)
Move an arc endpoint around the existing center, pulling the opposite endpoint along to keep the radi...
void EditArcMidKeepCenter(EDA_SHAPE &aArc, const VECTOR2I &aCenter, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, const VECTOR2I &aCursor, const EDA_IU_SCALE &aIuScale)
Move the mid point of an arc while keeping the center, rotating the endpoints onto the new radius.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
static void editArcEndpointKeepCenterAndRadius(EDA_SHAPE &aArc, const VECTOR2I &aCenter, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
ARC_EDIT_MODE IncrementArcEditMode(ARC_EDIT_MODE aMode)
static void editArcCenterKeepEndpoints(EDA_SHAPE &aArc, const VECTOR2I &aCenter, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Move the arc center but keep endpoint locations.
static void editArcMidKeepEndpoints(EDA_SHAPE &aArc, const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCursor)
Move the mid point of the arc, while keeping the angle.
static void editArcEndpointKeepTangent(EDA_SHAPE &aArc, const VECTOR2I &aCenter, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, const VECTOR2I &aCursor)
Move an end point of the arc, while keeping the tangent at the other endpoint.
#define CHECK_POINT_COUNT(aPoints, aExpected)
#define CHECK_POINT_COUNT_GE(aPoints, aExpected)
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:35
VECTOR3I expected(15, 30, 45)
VECTOR3I v1(5, 5, 5)
VECTOR2I center
int radius
VECTOR2I end
static double sweep(float aStart, float aEnd)
VECTOR2I v2(1, 0)
VECTOR2I v4(1, 1)
VECTOR2I v3(-2, 1)
int delta
VECTOR2I GetRotated(const VECTOR2I &aVector, const EDA_ANGLE &aAngle)
Return a new VECTOR2I that is the result of rotating aVector by aAngle.
Definition trigo.h:73
bool IsStraightArc(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Definition trigo.cpp:549
constexpr int sign(T val)
Definition util.h:167
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707