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 <advanced_config.h>
23#include <commit.h>
25
26
28 const SHAPE_POLY_SET& aOutline )
29{
30 const int cornersCount = aOutline.TotalVertices();
31
32 if( cornersCount == 0 )
33 return;
34
35 for( auto iterator = aOutline.CIterateWithHoles(); iterator; iterator++ )
36 {
37 aPoints.AddPoint( *iterator );
38
39 if( iterator.IsEndContour() )
40 aPoints.AddBreak();
41 }
42
43 // Lines have to be added after creating edit points, as they use EDIT_POINT references
44 for( int i = 0; i < cornersCount - 1; ++i )
45 {
46 if( aPoints.IsContourEnd( i ) )
47 aPoints.AddLine( aPoints.Point( i ), aPoints.Point( aPoints.GetContourStartIdx( i ) ) );
48 else
49 aPoints.AddLine( aPoints.Point( i ), aPoints.Point( i + 1 ) );
50
51 aPoints.Line( i ).SetConstraint( new EC_CONVERGING( aPoints.Line( i ), aPoints ) );
52 }
53
54 // The last missing line, connecting the last and the first polygon point
55 aPoints.AddLine( aPoints.Point( cornersCount - 1 ),
56 aPoints.Point( aPoints.GetContourStartIdx( cornersCount - 1 ) ) );
57
58 aPoints.Line( aPoints.LinesSize() - 1 )
59 .SetConstraint( new EC_CONVERGING( aPoints.Line( aPoints.LinesSize() - 1 ), aPoints ) );
60}
61
62
64 EDIT_POINTS& aPoints )
65{
66 // No size check here, as we can and will rebuild if that fails
67 if( aPoints.PointsSize() != (unsigned) aOutline.TotalVertices() )
68 {
69 // Rebuild the points list
70 aPoints.Clear();
71 BuildForPolyOutline( aPoints, aOutline );
72 }
73 else
74 {
75 for( int i = 0; i < aOutline.TotalVertices(); ++i )
76 aPoints.Point( i ).SetPosition( aOutline.CVertex( i ) );
77 }
78}
79
80
82 const EDIT_POINT& aEditedPoint,
83 EDIT_POINTS& aPoints )
84{
85 CHECK_POINT_COUNT_GE( aPoints, (unsigned) aOutline.TotalVertices() );
86
87 for( int i = 0; i < aOutline.TotalVertices(); ++i )
88 aOutline.SetVertex( i, aPoints.Point( i ).GetPosition() );
89
90 for( unsigned i = 0; i < aPoints.LinesSize(); ++i )
91 {
92 if( !isModified( aEditedPoint, aPoints.Line( i ) ) )
93 aPoints.Line( i ).SetConstraint( new EC_CONVERGING( aPoints.Line( i ), aPoints ) );
94 }
95}
96
97
99{
100 m_polygon.RemoveNullSegments();
101}
102
103
105{
106 aPoints.AddPoint( m_segment.GetStart() );
107 aPoints.AddPoint( m_segment.GetEnd() );
108}
109
110
112{
113 wxCHECK( aPoints.PointsSize() == SEGMENT_MAX_POINTS, false );
114
115 aPoints.Point( SEGMENT_START ) = m_segment.GetStart();
116 aPoints.Point( SEGMENT_END ) = m_segment.GetEnd();
117 return true;
118}
119
120
122 EDIT_POINTS& aPoints, COMMIT& aCommit,
123 std::vector<EDA_ITEM*>& aUpdatedItems )
124{
126
127 if( isModified( aEditedPoint, aPoints.Point( SEGMENT_START ) ) )
128 m_segment.SetStart( aPoints.Point( SEGMENT_START ).GetPosition() );
129
130 else if( isModified( aEditedPoint, aPoints.Point( SEGMENT_END ) ) )
131 m_segment.SetEnd( aPoints.Point( SEGMENT_END ).GetPosition() );
132}
133
134
136{
137 const VECTOR2I center = m_ellipse.GetEllipseCenter();
138 const double a = m_ellipse.GetEllipseMajorRadius();
139 const double b = m_ellipse.GetEllipseMinorRadius();
140 const EDA_ANGLE rotation = m_ellipse.GetEllipseRotation();
141
142 const double cosTheta = aTheta.Cos();
143 const double sinTheta = aTheta.Sin();
144 const double cosRot = rotation.Cos();
145 const double sinRot = rotation.Sin();
146
147 const double lx = a * cosTheta;
148 const double ly = b * sinTheta;
149
150 return center + VECTOR2I( KiROUND( lx * cosRot - ly * sinRot ), KiROUND( lx * sinRot + ly * cosRot ) );
151}
152
153
155{
156 const VECTOR2I center = m_ellipse.GetEllipseCenter();
157 const double a = std::max( 1, m_ellipse.GetEllipseMajorRadius() );
158 const double b = std::max( 1, m_ellipse.GetEllipseMinorRadius() );
159 const EDA_ANGLE rotation = m_ellipse.GetEllipseRotation();
160
161 const double dx = aWorldPt.x - center.x;
162 const double dy = aWorldPt.y - center.y;
163
164 const double cosRot = rotation.Cos();
165 const double sinRot = rotation.Sin();
166 const double lx = dx * cosRot + dy * sinRot;
167 const double ly = -dx * sinRot + dy * cosRot;
168
169 return EDA_ANGLE( atan2( ly / b, lx / a ), RADIANS_T );
170}
171
172
174{
175 const VECTOR2I center = m_ellipse.GetEllipseCenter();
176 const int a = m_ellipse.GetEllipseMajorRadius();
177 const int b = m_ellipse.GetEllipseMinorRadius();
178 const EDA_ANGLE rotation = m_ellipse.GetEllipseRotation();
179
180 const double cosRot = rotation.Cos();
181 const double sinRot = rotation.Sin();
182
183 aPoints.AddPoint( center );
184 aPoints.AddPoint( center + VECTOR2I( KiROUND( a * cosRot ), KiROUND( a * sinRot ) ) );
185 aPoints.AddPoint( center + VECTOR2I( KiROUND( -b * sinRot ), KiROUND( b * cosRot ) ) );
186
187 if( m_ellipse.GetShape() == SHAPE_T::ELLIPSE_ARC )
188 {
189 aPoints.AddPoint( evaluateAt( m_ellipse.GetEllipseStartAngle() ) );
190 aPoints.AddPoint( evaluateAt( m_ellipse.GetEllipseEndAngle() ) );
191 }
192}
193
194
196{
197 const bool isArc = ( m_ellipse.GetShape() == SHAPE_T::ELLIPSE_ARC );
198 const size_t expected = isArc ? ELLIPSE_ARC_POINTS : ELLIPSE_CLOSED_POINTS;
199
200 wxCHECK( aPoints.PointsSize() == expected, false );
201
202 const VECTOR2I center = m_ellipse.GetEllipseCenter();
203 const int a = m_ellipse.GetEllipseMajorRadius();
204 const int b = m_ellipse.GetEllipseMinorRadius();
205 const EDA_ANGLE rotation = m_ellipse.GetEllipseRotation();
206
207 const double cosRot = rotation.Cos();
208 const double sinRot = rotation.Sin();
209
211 aPoints.Point( ELLIPSE_MAJOR_END ).SetPosition( center + VECTOR2I( KiROUND( a * cosRot ), KiROUND( a * sinRot ) ) );
212 aPoints.Point( ELLIPSE_MINOR_END )
213 .SetPosition( center + VECTOR2I( KiROUND( -b * sinRot ), KiROUND( b * cosRot ) ) );
214
215 if( isArc )
216 {
217 aPoints.Point( ELLIPSE_ARC_START ).SetPosition( evaluateAt( m_ellipse.GetEllipseStartAngle() ) );
218 aPoints.Point( ELLIPSE_ARC_END ).SetPosition( evaluateAt( m_ellipse.GetEllipseEndAngle() ) );
219 }
220
221 return true;
222}
223
224
225void EDA_ELLIPSE_POINT_EDIT_BEHAVIOR::UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
226 std::vector<EDA_ITEM*>& aUpdatedItems )
227{
228 const bool isArc = ( m_ellipse.GetShape() == SHAPE_T::ELLIPSE_ARC );
229 const size_t expected = isArc ? ELLIPSE_ARC_POINTS : ELLIPSE_CLOSED_POINTS;
230
231 CHECK_POINT_COUNT( aPoints, expected );
232
233 if( isModified( aEditedPoint, aPoints.Point( ELLIPSE_CENTER ) ) )
234 {
235 m_ellipse.SetEllipseCenter( aPoints.Point( ELLIPSE_CENTER ).GetPosition() );
236 }
237 else if( isModified( aEditedPoint, aPoints.Point( ELLIPSE_MAJOR_END ) ) )
238 {
239 const VECTOR2I center = m_ellipse.GetEllipseCenter();
240 const VECTOR2I v = aPoints.Point( ELLIPSE_MAJOR_END ).GetPosition() - center;
241 const double len = std::sqrt( double( v.x ) * v.x + double( v.y ) * v.y );
242
243 m_ellipse.SetEllipseMajorRadius( std::max( 1, KiROUND( len ) ) );
244 m_ellipse.SetEllipseRotation( EDA_ANGLE( std::atan2( (double) v.y, (double) v.x ), RADIANS_T ) );
245 }
246 else if( isModified( aEditedPoint, aPoints.Point( ELLIPSE_MINOR_END ) ) )
247 {
248 const VECTOR2I center = m_ellipse.GetEllipseCenter();
249 const VECTOR2I v = aPoints.Point( ELLIPSE_MINOR_END ).GetPosition() - center;
250 const EDA_ANGLE rotation = m_ellipse.GetEllipseRotation();
251
252 // Absolute value so dragging past the rotation axis keeps radius positive.
253 const double proj = -v.x * rotation.Sin() + v.y * rotation.Cos();
254
255 m_ellipse.SetEllipseMinorRadius( std::max( 1, KiROUND( std::abs( proj ) ) ) );
256 }
257 else if( isArc && isModified( aEditedPoint, aPoints.Point( ELLIPSE_ARC_START ) ) )
258 {
260 EDA_ANGLE prevStart = m_ellipse.GetEllipseStartAngle();
261 EDA_ANGLE sweep = m_ellipse.GetEllipseEndAngle() - prevStart;
262
263 while( ( rawAngle - prevStart ).AsDegrees() > 180.0 )
264 rawAngle = rawAngle - ANGLE_360;
265 while( ( prevStart - rawAngle ).AsDegrees() > 180.0 )
266 rawAngle = rawAngle + ANGLE_360;
267
268 m_ellipse.SetEllipseStartAngle( rawAngle );
269 m_ellipse.SetEllipseEndAngle( rawAngle + sweep );
270 }
271 else if( isArc && isModified( aEditedPoint, aPoints.Point( ELLIPSE_ARC_END ) ) )
272 {
274 EDA_ANGLE prevEnd = m_ellipse.GetEllipseEndAngle();
275
276 while( ( rawAngle - prevEnd ).AsDegrees() > 180.0 )
277 rawAngle = rawAngle - ANGLE_360;
278 while( ( prevEnd - rawAngle ).AsDegrees() > 180.0 )
279 rawAngle = rawAngle + ANGLE_360;
280
281 m_ellipse.SetEllipseEndAngle( rawAngle );
282 }
283}
284
285
287 EDIT_POINTS& aPoints ) const
288{
289 // Select the other end of line
290 return aPoints.Next( aEditedPoint )->GetPosition();
291}
292
293
295{
296 aPoints.AddPoint( m_circle.getCenter() );
297 aPoints.AddPoint( m_circle.GetEnd() );
298}
299
300
302{
303 wxCHECK( aPoints.PointsSize() == CIRC_MAX_POINTS, false );
304
305 aPoints.Point( CIRC_CENTER ).SetPosition( m_circle.getCenter() );
306 aPoints.Point( CIRC_END ).SetPosition( m_circle.GetEnd() );
307 return true;
308}
309
310
312 EDIT_POINTS& aPoints, COMMIT& aCommit,
313 std::vector<EDA_ITEM*>& aUpdatedItems )
314{
316
317 const VECTOR2I& center = aPoints.Point( CIRC_CENTER ).GetPosition();
318 const VECTOR2I& end = aPoints.Point( CIRC_END ).GetPosition();
319
320 if( isModified( aEditedPoint, aPoints.Point( CIRC_CENTER ) ) )
321 m_circle.SetCenter( center );
322 else
323 m_circle.SetEnd( VECTOR2I( end.x, end.y ) );
324}
325
326
328 EDIT_POINTS& aPoints ) const
329{
330 return aPoints.Point( CIRC_CENTER ).GetPosition();
331}
332
333
335{
336 aPoints.AddPoint( m_bezier.GetStart() );
337 aPoints.AddPoint( m_bezier.GetBezierC1() );
338 aPoints.AddPoint( m_bezier.GetBezierC2() );
339 aPoints.AddPoint( m_bezier.GetEnd() );
340
341 aPoints.AddIndicatorLine( aPoints.Point( BEZIER_START ), aPoints.Point( BEZIER_CTRL_PT1 ) );
342 aPoints.AddIndicatorLine( aPoints.Point( BEZIER_CTRL_PT2 ), aPoints.Point( BEZIER_END ) );
343}
344
345
347{
348 wxCHECK( aPoints.PointsSize() == BEZIER_MAX_POINTS, false );
349
350 aPoints.Point( BEZIER_START ).SetPosition( m_bezier.GetStart() );
351 aPoints.Point( BEZIER_CTRL_PT1 ).SetPosition( m_bezier.GetBezierC1() );
352 aPoints.Point( BEZIER_CTRL_PT2 ).SetPosition( m_bezier.GetBezierC2() );
353 aPoints.Point( BEZIER_END ).SetPosition( m_bezier.GetEnd() );
354 return true;
355}
356
357
359 COMMIT& aCommit, std::vector<EDA_ITEM*>& aUpdatedItems )
360{
362
363 if( isModified( aEditedPoint, aPoints.Point( BEZIER_START ) ) )
364 {
365 m_bezier.SetStart( aPoints.Point( BEZIER_START ).GetPosition() );
366 }
367 else if( isModified( aEditedPoint, aPoints.Point( BEZIER_CTRL_PT1 ) ) )
368 {
369 m_bezier.SetBezierC1( aPoints.Point( BEZIER_CTRL_PT1 ).GetPosition() );
370 }
371 else if( isModified( aEditedPoint, aPoints.Point( BEZIER_CTRL_PT2 ) ) )
372 {
373 m_bezier.SetBezierC2( aPoints.Point( BEZIER_CTRL_PT2 ).GetPosition() );
374 }
375 else if( isModified( aEditedPoint, aPoints.Point( BEZIER_END ) ) )
376 {
377 m_bezier.SetEnd( aPoints.Point( BEZIER_END ).GetPosition() );
378 }
379
380 m_bezier.RebuildBezierToSegmentsPointsList( m_maxError );
381}
382
383
384// Note: these static arc functions don't have to be in here - we could ship them out
385// to a utils area for use by other code (e.g. polygon fillet editing).
386
390static void editArcEndpointKeepTangent( EDA_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
391 const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCursor )
392{
393 VECTOR2I center = aCenter;
394 bool movingStart;
395 bool arcValid = true;
396
397 VECTOR2I p1, p2, p3;
398 // p1 does not move, p2 does.
399
400 if( aStart != aArc.GetStart() )
401 {
402 p1 = aEnd;
403 p2 = aStart;
404 p3 = aMid;
405 movingStart = true;
406 }
407 else if( aEnd != aArc.GetEnd() )
408 {
409 p1 = aStart;
410 p2 = aEnd;
411 p3 = aMid;
412 movingStart = false;
413 }
414 else
415 {
416 return;
417 }
418
419 VECTOR2D v1, v2, v3, v4;
420
421 // Move the coordinate system
422 v1 = p1 - aCenter;
423 v2 = p2 - aCenter;
424 v3 = p3 - aCenter;
425
426 VECTOR2D u1, u2;
427
428 // A point cannot be both the center and on the arc.
429 if( ( v1.EuclideanNorm() == 0 ) || ( v2.EuclideanNorm() == 0 ) )
430 return;
431
432 u1 = v1 / v1.EuclideanNorm();
433 u2 = v3 - ( u1.x * v3.x + u1.y * v3.y ) * u1;
434 u2 = u2 / u2.EuclideanNorm();
435
436 // [ u1, u3 ] is a base centered on the circle with:
437 // u1 : unit vector toward the point that does not move
438 // u2 : unit vector toward the mid point.
439
440 // Get vectors v1, and v2 in that coordinate system.
441
442 double det = u1.x * u2.y - u2.x * u1.y;
443
444 // u1 and u2 are unit vectors, and perpendicular.
445 // det should not be 0. In case it is, do not change the arc.
446 if( det == 0 )
447 return;
448
449 double tmpx = v1.x * u2.y - v1.y * u2.x;
450 double tmpy = -v1.x * u1.y + v1.y * u1.x;
451 v1.x = tmpx;
452 v1.y = tmpy;
453 v1 = v1 / det;
454
455 tmpx = v2.x * u2.y - v2.y * u2.x;
456 tmpy = -v2.x * u1.y + v2.y * u1.x;
457 v2.x = tmpx;
458 v2.y = tmpy;
459 v2 = v2 / det;
460
461 double R = v1.EuclideanNorm();
462 bool transformCircle = false;
463
464 /* p2
465 * X***
466 * ** <---- This is the arc
467 * y ^ **
468 * | R *
469 * | <-----------> *
470 * x------x------>--------x p1
471 * C' <----> C x
472 * delta
473 *
474 * p1 does not move, and the tangent at p1 remains the same.
475 * => The new center, C', will be on the C-p1 axis.
476 * p2 moves
477 *
478 * The radius of the new circle is delta + R
479 *
480 * || C' p2 || = || C' P1 ||
481 * is the same as :
482 * ( delta + p2.x ) ^ 2 + p2.y ^ 2 = ( R + delta ) ^ 2
483 *
484 * delta = ( R^2 - p2.x ^ 2 - p2.y ^2 ) / ( 2 * p2.x - 2 * R )
485 *
486 * We can use this equation for any point p2 with p2.x < R
487 */
488
489 if( v2.x == R )
490 {
491 // Straight line, do nothing
492 }
493 else
494 {
495 if( v2.x > R )
496 {
497 // If we need to invert the curvature.
498 // We modify the input so we can use the same equation
499 transformCircle = true;
500 v2.x = 2 * R - v2.x;
501 }
502
503 // We can keep the tangent constraint.
504 double delta = ( R * R - v2.x * v2.x - v2.y * v2.y ) / ( 2 * v2.x - 2 * R );
505
506 // This is just to limit the radius, so nothing overflows later when drawing.
507 if( abs( v2.y / ( R - v2.x ) ) > ADVANCED_CFG::GetCfg().m_DrawArcCenterMaxAngle )
508 arcValid = false;
509
510 // Never recorded a problem, but still checking.
511 if( !std::isfinite( delta ) )
512 arcValid = false;
513
514 // v4 is the new center
515 v4 = ( !transformCircle ) ? VECTOR2D( -delta, 0 ) : VECTOR2D( 2 * R + delta, 0 );
516
517 tmpx = v4.x * u1.x + v4.y * u2.x;
518 tmpy = v4.x * u1.y + v4.y * u2.y;
519 v4.x = tmpx;
520 v4.y = tmpy;
521
522 center = v4 + aCenter;
523
524 if( arcValid )
525 {
526 aArc.SetCenter( center );
527
528 if( movingStart )
529 aArc.SetStart( aStart );
530 else
531 aArc.SetEnd( aEnd );
532 }
533 }
534}
535
536
540static void editArcCenterKeepEndpoints( EDA_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
541 const VECTOR2I& aMid, const VECTOR2I& aEnd )
542{
543 const int c_snapEpsilon_sq = 4;
544
545 VECTOR2I m = ( aStart / 2 + aEnd / 2 );
546 VECTOR2I perp = ( aEnd - aStart ).Perpendicular().Resize( INT_MAX / 2 );
547
548 SEG legal( m - perp, m + perp );
549
550 const SEG testSegments[] = {
551 SEG( aCenter, aCenter + VECTOR2( 1, 0 ) ),
552 SEG( aCenter, aCenter + VECTOR2( 0, 1 ) ),
553 };
554
555 std::vector<VECTOR2I> points = { legal.A, legal.B };
556
557 for( const SEG& seg : testSegments )
558 {
559 OPT_VECTOR2I vec = legal.IntersectLines( seg );
560
561 if( vec && legal.SquaredDistance( *vec ) <= c_snapEpsilon_sq )
562 points.push_back( *vec );
563 }
564
565 OPT_VECTOR2I nearest;
567
568 // Snap by distance between cursor and intersections
569 for( const VECTOR2I& pt : points )
570 {
571 SEG::ecoord d_sq = ( pt - aCenter ).SquaredEuclideanNorm();
572
573 if( d_sq < min_d_sq - c_snapEpsilon_sq )
574 {
575 min_d_sq = d_sq;
576 nearest = pt;
577 }
578 }
579
580 if( nearest )
581 aArc.SetCenter( *nearest );
582}
583
584
589 const VECTOR2I& aStart, const VECTOR2I& aMid,
590 const VECTOR2I& aEnd, const VECTOR2I& aCursor,
591 const EDA_IU_SCALE& aIuScale )
592{
593 // 1 mil floor in the caller's units keeps the arc non-degenerate without
594 // snapping small eeschema arcs that are legitimately under 100 mils.
595 int minRadius = EDA_UNIT_UTILS::Mils2IU( aIuScale, 1 );
596 bool movingStart;
597
598 VECTOR2I p1, p2, prev_p1;
599
600 // user is moving p1, we want to move p2 to the new radius.
601
602 if( aStart != aArc.GetStart() )
603 {
604 prev_p1 = aArc.GetStart();
605 p1 = aStart;
606 p2 = aEnd;
607 movingStart = true;
608 }
609 else
610 {
611 prev_p1 = aArc.GetEnd();
612 p1 = aEnd;
613 p2 = aStart;
614 movingStart = false;
615 }
616
617 p1 = p1 - aCenter;
618 p2 = p2 - aCenter;
619
620 if( p1.x == 0 && p1.y == 0 )
621 p1 = prev_p1 - aCenter;
622
623 if( p2.x == 0 && p2.y == 0 )
624 p2 = { 1, 0 };
625
626 double radius = p1.EuclideanNorm();
627
628 if( radius < minRadius )
629 radius = minRadius;
630
631 p1 = aCenter + p1.Resize( KiROUND( radius ) );
632 p2 = aCenter + p2.Resize( KiROUND( radius ) );
633
634 aArc.SetCenter( aCenter );
635
636 if( movingStart )
637 {
638 aArc.SetStart( p1 );
639 aArc.SetEnd( p2 );
640 }
641 else
642 {
643 aArc.SetStart( p2 );
644 aArc.SetEnd( p1 );
645 }
646}
647
648
649static void editArcEndpointKeepCenterAndRadius( EDA_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
650 const VECTOR2I& aMid, const VECTOR2I& aEnd )
651{
652 VECTOR2I p1;
653 bool movingStart = false;
654
655 // User is moving p1, we need to update whichever end that is
656 // The other end won't move.
657
658 if( aStart != aArc.GetStart() )
659 {
660 p1 = aStart;
661 movingStart = true;
662 }
663 else
664 {
665 p1 = aEnd;
666 movingStart = false;
667 }
668
669 // Do not change the radius
670 p1 = p1 - aCenter;
671 p1 = aCenter + p1.Resize( aArc.GetRadius() );
672
673 if( movingStart )
674 {
675 aArc.SetStart( p1 );
676 }
677 else
678 {
679 aArc.SetEnd( p1 );
680 }
681}
682
683
688 const VECTOR2I& aStart, const VECTOR2I& aMid,
689 const VECTOR2I& aEnd, const VECTOR2I& aCursor,
690 const EDA_IU_SCALE& aIuScale )
691{
692 // See EditArcEndpointKeepCenter for why we use the caller's IU scale.
693 int minRadius = EDA_UNIT_UTILS::Mils2IU( aIuScale, 1 );
694
695 // Now, update the edit point position
696 // Express the point in a circle-centered coordinate system.
697 VECTOR2I start = aStart - aCenter;
698 VECTOR2I end = aEnd - aCenter;
699
700 double radius = ( aCursor - aCenter ).EuclideanNorm();
701
702 if( radius < minRadius )
703 radius = minRadius;
704
705 start = start.Resize( KiROUND( radius ) );
706 end = end.Resize( KiROUND( radius ) );
707
708 start = start + aCenter;
709 end = end + aCenter;
710
711 aArc.SetStart( start );
712 aArc.SetEnd( end );
713}
714
715
719static void editArcMidKeepEndpoints( EDA_SHAPE& aArc, const VECTOR2I& aStart, const VECTOR2I& aEnd,
720 const VECTOR2I& aCursor )
721{
722 // Let 'm' be the middle point of the chord between the start and end points
723 VECTOR2I m = ( aStart + aEnd ) / 2;
724
725 // Legal midpoints lie on a vector starting just off the chord midpoint and extending out
726 // past the existing midpoint. We do not allow arc inflection while point editing.
727 const int JUST_OFF = ( aStart - aEnd ).EuclideanNorm() / 100;
728 VECTOR2I v = (VECTOR2I) aArc.GetArcMid() - m;
729 SEG legal( m + v.Resize( JUST_OFF ), m + v.Resize( INT_MAX / 2 ) );
730 VECTOR2I mid = legal.NearestPoint( aCursor );
731
732 aArc.SetArcGeometry( aStart, mid, aEnd );
733}
734
735
737 KIGFX::VIEW_CONTROLS& aViewContols,
738 const EDA_IU_SCALE& aIuScale ) :
739 m_arc( aArc ),
740 m_arcEditMode( aArcEditMode ),
741 m_viewControls( aViewContols ),
742 m_iuScale( aIuScale )
743{
744 wxASSERT( m_arc.GetShape() == SHAPE_T::ARC );
745}
746
747
749{
750 aPoints.AddPoint( m_arc.GetStart() );
751 aPoints.AddPoint( m_arc.GetArcMid() );
752 aPoints.AddPoint( m_arc.GetEnd() );
753 aPoints.AddPoint( m_arc.getCenter() );
754
755 aPoints.AddIndicatorLine( aPoints.Point( ARC_CENTER ), aPoints.Point( ARC_START ) );
756 aPoints.AddIndicatorLine( aPoints.Point( ARC_CENTER ), aPoints.Point( ARC_END ) );
757}
758
759
761{
762 wxCHECK( aPoints.PointsSize() == 4, false );
763
764 aPoints.Point( ARC_START ).SetPosition( m_arc.GetStart() );
765 aPoints.Point( ARC_MID ).SetPosition( m_arc.GetArcMid() );
766 aPoints.Point( ARC_END ).SetPosition( m_arc.GetEnd() );
767 aPoints.Point( ARC_CENTER ).SetPosition( m_arc.getCenter() );
768 return true;
769}
770
771
772void EDA_ARC_POINT_EDIT_BEHAVIOR::UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
773 std::vector<EDA_ITEM*>& aUpdatedItems )
774{
775 CHECK_POINT_COUNT( aPoints, 4 );
776
778 VECTOR2I mid = aPoints.Point( ARC_MID ).GetPosition();
779 VECTOR2I start = aPoints.Point( ARC_START ).GetPosition();
780 VECTOR2I end = aPoints.Point( ARC_END ).GetPosition();
781
782 if( isModified( aEditedPoint, aPoints.Point( ARC_CENTER ) ) )
783 {
784 switch( m_arcEditMode )
785 {
788 break;
789
792 {
793 // Both these modes just move the arc
794 VECTOR2I moveVector = center - m_arc.getCenter();
795
796 m_arc.SetArcGeometry( m_arc.GetStart() + moveVector, m_arc.GetArcMid() + moveVector,
797 m_arc.GetEnd() + moveVector );
798 break;
799 }
800 }
801 }
802 else if( isModified( aEditedPoint, aPoints.Point( ARC_MID ) ) )
803 {
804 const VECTOR2I& cursorPos = m_viewControls.GetCursorPosition( false );
805
806 switch( m_arcEditMode )
807 {
809 editArcMidKeepEndpoints( m_arc, start, end, cursorPos );
810 break;
813 KI_ARC_EDIT::EditArcMidKeepCenter( m_arc, center, start, mid, end, cursorPos, m_iuScale );
814 break;
815 }
816 }
817 else if( isModified( aEditedPoint, aPoints.Point( ARC_START ) )
818 || isModified( aEditedPoint, aPoints.Point( ARC_END ) ) )
819 {
820 const VECTOR2I& cursorPos = m_viewControls.GetCursorPosition();
821
822 switch( m_arcEditMode )
823 {
826 break;
829 break;
831 editArcEndpointKeepTangent( m_arc, center, start, mid, end, cursorPos );
832 break;
833 }
834 }
835}
836
837
839 EDIT_POINTS& aPoints ) const
840{
841 return aPoints.Point( ARC_CENTER ).GetPosition();
842}
843
844
846{
847 aPoints.AddPoint( m_cell.GetEnd() - VECTOR2I( 0, m_cell.GetRectangleHeight() / 2 ) );
848 aPoints.AddPoint( m_cell.GetEnd() - VECTOR2I( m_cell.GetRectangleWidth() / 2, 0 ) );
849}
850
851
853{
854 aPoints.Point( COL_WIDTH ).SetPosition( m_cell.GetEnd() - VECTOR2I( 0, m_cell.GetRectangleHeight() / 2 ) );
855 aPoints.Point( ROW_HEIGHT ).SetPosition( m_cell.GetEnd() - VECTOR2I( m_cell.GetRectangleWidth() / 2, 0 ) );
856 return true;
857}
858
859
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:986
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
EDIT_CONSTRAINT for polygon line dragging.
double Sin() const
Definition eda_angle.h:178
double Cos() const
Definition eda_angle.h:197
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:244
void SetCenter(const VECTOR2I &aCenter)
int GetRadius() const
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:240
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:190
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:194
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 SetConstraint(EDIT_CONSTRAINT< EDIT_LINE > *aConstraint)
Set a constraint for and EDIT_POINT.
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:68
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.
Definition seg.h:38
VECTOR2I A
Definition seg.h:45
ecoord SquaredDistance(const SEG &aSeg) const
Definition seg.cpp:76
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:629
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition seg.h:216
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.
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:279
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:381
@ RADIANS_T
Definition eda_angle.h:32
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:417
@ ELLIPSE_ARC
Definition eda_shape.h:53
constexpr int Mils2IU(const EDA_IU_SCALE &aIuScale, int mils)
Definition eda_units.h:171
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:400
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
VECTOR2I v2(1, 0)
VECTOR2I v4(1, 1)
VECTOR2I v3(-2, 1)
int delta
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682