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, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
25
26#include <advanced_config.h>
27#include <commit.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 ).SetConstraint( new EC_CONVERGING( aPoints.Line( i ), aPoints ) );
56 }
57
58 // The last missing line, connecting the last and the first polygon point
59 aPoints.AddLine( aPoints.Point( cornersCount - 1 ),
60 aPoints.Point( aPoints.GetContourStartIdx( cornersCount - 1 ) ) );
61
62 aPoints.Line( aPoints.LinesSize() - 1 )
63 .SetConstraint( new EC_CONVERGING( aPoints.Line( aPoints.LinesSize() - 1 ), aPoints ) );
64}
65
66
68 EDIT_POINTS& aPoints )
69{
70 // No size check here, as we can and will rebuild if that fails
71 if( aPoints.PointsSize() != (unsigned) aOutline.TotalVertices() )
72 {
73 // Rebuild the points list
74 aPoints.Clear();
75 BuildForPolyOutline( aPoints, aOutline );
76 }
77 else
78 {
79 for( int i = 0; i < aOutline.TotalVertices(); ++i )
80 aPoints.Point( i ).SetPosition( aOutline.CVertex( i ) );
81 }
82}
83
84
86 const EDIT_POINT& aEditedPoint,
87 EDIT_POINTS& aPoints )
88{
89 CHECK_POINT_COUNT_GE( aPoints, (unsigned) aOutline.TotalVertices() );
90
91 for( int i = 0; i < aOutline.TotalVertices(); ++i )
92 aOutline.SetVertex( i, aPoints.Point( i ).GetPosition() );
93
94 for( unsigned i = 0; i < aPoints.LinesSize(); ++i )
95 {
96 if( !isModified( aEditedPoint, aPoints.Line( i ) ) )
97 aPoints.Line( i ).SetConstraint( new EC_CONVERGING( aPoints.Line( i ), aPoints ) );
98 }
99}
100
101
103{
104 m_polygon.RemoveNullSegments();
105}
106
107
109{
110 aPoints.AddPoint( m_segment.GetStart() );
111 aPoints.AddPoint( m_segment.GetEnd() );
112}
113
114
116{
117 wxCHECK( aPoints.PointsSize() == SEGMENT_MAX_POINTS, false );
118
119 aPoints.Point( SEGMENT_START ) = m_segment.GetStart();
120 aPoints.Point( SEGMENT_END ) = m_segment.GetEnd();
121 return true;
122}
123
124
126 EDIT_POINTS& aPoints, COMMIT& aCommit,
127 std::vector<EDA_ITEM*>& aUpdatedItems )
128{
130
131 if( isModified( aEditedPoint, aPoints.Point( SEGMENT_START ) ) )
132 m_segment.SetStart( aPoints.Point( SEGMENT_START ).GetPosition() );
133
134 else if( isModified( aEditedPoint, aPoints.Point( SEGMENT_END ) ) )
135 m_segment.SetEnd( aPoints.Point( SEGMENT_END ).GetPosition() );
136}
137
138
140{
141 const VECTOR2I center = m_ellipse.GetEllipseCenter();
142 const double a = m_ellipse.GetEllipseMajorRadius();
143 const double b = m_ellipse.GetEllipseMinorRadius();
144 const EDA_ANGLE rotation = m_ellipse.GetEllipseRotation();
145
146 const double cosTheta = aTheta.Cos();
147 const double sinTheta = aTheta.Sin();
148 const double cosRot = rotation.Cos();
149 const double sinRot = rotation.Sin();
150
151 const double lx = a * cosTheta;
152 const double ly = b * sinTheta;
153
154 return center + VECTOR2I( KiROUND( lx * cosRot - ly * sinRot ), KiROUND( lx * sinRot + ly * cosRot ) );
155}
156
157
159{
160 const VECTOR2I center = m_ellipse.GetEllipseCenter();
161 const double a = std::max( 1, m_ellipse.GetEllipseMajorRadius() );
162 const double b = std::max( 1, m_ellipse.GetEllipseMinorRadius() );
163 const EDA_ANGLE rotation = m_ellipse.GetEllipseRotation();
164
165 const double dx = aWorldPt.x - center.x;
166 const double dy = aWorldPt.y - center.y;
167
168 const double cosRot = rotation.Cos();
169 const double sinRot = rotation.Sin();
170 const double lx = dx * cosRot + dy * sinRot;
171 const double ly = -dx * sinRot + dy * cosRot;
172
173 return EDA_ANGLE( atan2( ly / b, lx / a ), RADIANS_T );
174}
175
176
178{
179 const VECTOR2I center = m_ellipse.GetEllipseCenter();
180 const int a = m_ellipse.GetEllipseMajorRadius();
181 const int b = m_ellipse.GetEllipseMinorRadius();
182 const EDA_ANGLE rotation = m_ellipse.GetEllipseRotation();
183
184 const double cosRot = rotation.Cos();
185 const double sinRot = rotation.Sin();
186
187 aPoints.AddPoint( center );
188 aPoints.AddPoint( center + VECTOR2I( KiROUND( a * cosRot ), KiROUND( a * sinRot ) ) );
189 aPoints.AddPoint( center + VECTOR2I( KiROUND( -b * sinRot ), KiROUND( b * cosRot ) ) );
190
191 if( m_ellipse.GetShape() == SHAPE_T::ELLIPSE_ARC )
192 {
193 aPoints.AddPoint( evaluateAt( m_ellipse.GetEllipseStartAngle() ) );
194 aPoints.AddPoint( evaluateAt( m_ellipse.GetEllipseEndAngle() ) );
195 }
196}
197
198
200{
201 const bool isArc = ( m_ellipse.GetShape() == SHAPE_T::ELLIPSE_ARC );
202 const size_t expected = isArc ? ELLIPSE_ARC_POINTS : ELLIPSE_CLOSED_POINTS;
203
204 wxCHECK( aPoints.PointsSize() == expected, false );
205
206 const VECTOR2I center = m_ellipse.GetEllipseCenter();
207 const int a = m_ellipse.GetEllipseMajorRadius();
208 const int b = m_ellipse.GetEllipseMinorRadius();
209 const EDA_ANGLE rotation = m_ellipse.GetEllipseRotation();
210
211 const double cosRot = rotation.Cos();
212 const double sinRot = rotation.Sin();
213
215 aPoints.Point( ELLIPSE_MAJOR_END ).SetPosition( center + VECTOR2I( KiROUND( a * cosRot ), KiROUND( a * sinRot ) ) );
216 aPoints.Point( ELLIPSE_MINOR_END )
217 .SetPosition( center + VECTOR2I( KiROUND( -b * sinRot ), KiROUND( b * cosRot ) ) );
218
219 if( isArc )
220 {
221 aPoints.Point( ELLIPSE_ARC_START ).SetPosition( evaluateAt( m_ellipse.GetEllipseStartAngle() ) );
222 aPoints.Point( ELLIPSE_ARC_END ).SetPosition( evaluateAt( m_ellipse.GetEllipseEndAngle() ) );
223 }
224
225 return true;
226}
227
228
229void EDA_ELLIPSE_POINT_EDIT_BEHAVIOR::UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
230 std::vector<EDA_ITEM*>& aUpdatedItems )
231{
232 const bool isArc = ( m_ellipse.GetShape() == SHAPE_T::ELLIPSE_ARC );
233 const size_t expected = isArc ? ELLIPSE_ARC_POINTS : ELLIPSE_CLOSED_POINTS;
234
235 CHECK_POINT_COUNT( aPoints, expected );
236
237 if( isModified( aEditedPoint, aPoints.Point( ELLIPSE_CENTER ) ) )
238 {
239 m_ellipse.SetEllipseCenter( aPoints.Point( ELLIPSE_CENTER ).GetPosition() );
240 }
241 else if( isModified( aEditedPoint, aPoints.Point( ELLIPSE_MAJOR_END ) ) )
242 {
243 const VECTOR2I center = m_ellipse.GetEllipseCenter();
244 const VECTOR2I v = aPoints.Point( ELLIPSE_MAJOR_END ).GetPosition() - center;
245 const double len = std::sqrt( double( v.x ) * v.x + double( v.y ) * v.y );
246
247 m_ellipse.SetEllipseMajorRadius( std::max( 1, KiROUND( len ) ) );
248 m_ellipse.SetEllipseRotation( EDA_ANGLE( std::atan2( (double) v.y, (double) v.x ), RADIANS_T ) );
249 }
250 else if( isModified( aEditedPoint, aPoints.Point( ELLIPSE_MINOR_END ) ) )
251 {
252 const VECTOR2I center = m_ellipse.GetEllipseCenter();
253 const VECTOR2I v = aPoints.Point( ELLIPSE_MINOR_END ).GetPosition() - center;
254 const EDA_ANGLE rotation = m_ellipse.GetEllipseRotation();
255
256 // Absolute value so dragging past the rotation axis keeps radius positive.
257 const double proj = -v.x * rotation.Sin() + v.y * rotation.Cos();
258
259 m_ellipse.SetEllipseMinorRadius( std::max( 1, KiROUND( std::abs( proj ) ) ) );
260 }
261 else if( isArc && isModified( aEditedPoint, aPoints.Point( ELLIPSE_ARC_START ) ) )
262 {
264 EDA_ANGLE prevStart = m_ellipse.GetEllipseStartAngle();
265 EDA_ANGLE sweep = m_ellipse.GetEllipseEndAngle() - prevStart;
266
267 while( ( rawAngle - prevStart ).AsDegrees() > 180.0 )
268 rawAngle = rawAngle - ANGLE_360;
269 while( ( prevStart - rawAngle ).AsDegrees() > 180.0 )
270 rawAngle = rawAngle + ANGLE_360;
271
272 m_ellipse.SetEllipseStartAngle( rawAngle );
273 m_ellipse.SetEllipseEndAngle( rawAngle + sweep );
274 }
275 else if( isArc && isModified( aEditedPoint, aPoints.Point( ELLIPSE_ARC_END ) ) )
276 {
278 EDA_ANGLE prevEnd = m_ellipse.GetEllipseEndAngle();
279
280 while( ( rawAngle - prevEnd ).AsDegrees() > 180.0 )
281 rawAngle = rawAngle - ANGLE_360;
282 while( ( prevEnd - rawAngle ).AsDegrees() > 180.0 )
283 rawAngle = rawAngle + ANGLE_360;
284
285 m_ellipse.SetEllipseEndAngle( rawAngle );
286 }
287}
288
289
291 EDIT_POINTS& aPoints ) const
292{
293 // Select the other end of line
294 return aPoints.Next( aEditedPoint )->GetPosition();
295}
296
297
299{
300 aPoints.AddPoint( m_circle.getCenter() );
301 aPoints.AddPoint( m_circle.GetEnd() );
302}
303
304
306{
307 wxCHECK( aPoints.PointsSize() == CIRC_MAX_POINTS, false );
308
309 aPoints.Point( CIRC_CENTER ).SetPosition( m_circle.getCenter() );
310 aPoints.Point( CIRC_END ).SetPosition( m_circle.GetEnd() );
311 return true;
312}
313
314
316 EDIT_POINTS& aPoints, COMMIT& aCommit,
317 std::vector<EDA_ITEM*>& aUpdatedItems )
318{
320
321 const VECTOR2I& center = aPoints.Point( CIRC_CENTER ).GetPosition();
322 const VECTOR2I& end = aPoints.Point( CIRC_END ).GetPosition();
323
324 if( isModified( aEditedPoint, aPoints.Point( CIRC_CENTER ) ) )
325 m_circle.SetCenter( center );
326 else
327 m_circle.SetEnd( VECTOR2I( end.x, end.y ) );
328}
329
330
332 EDIT_POINTS& aPoints ) const
333{
334 return aPoints.Point( CIRC_CENTER ).GetPosition();
335}
336
337
339{
340 aPoints.AddPoint( m_bezier.GetStart() );
341 aPoints.AddPoint( m_bezier.GetBezierC1() );
342 aPoints.AddPoint( m_bezier.GetBezierC2() );
343 aPoints.AddPoint( m_bezier.GetEnd() );
344
345 aPoints.AddIndicatorLine( aPoints.Point( BEZIER_START ), aPoints.Point( BEZIER_CTRL_PT1 ) );
346 aPoints.AddIndicatorLine( aPoints.Point( BEZIER_CTRL_PT2 ), aPoints.Point( BEZIER_END ) );
347}
348
349
351{
352 wxCHECK( aPoints.PointsSize() == BEZIER_MAX_POINTS, false );
353
354 aPoints.Point( BEZIER_START ).SetPosition( m_bezier.GetStart() );
355 aPoints.Point( BEZIER_CTRL_PT1 ).SetPosition( m_bezier.GetBezierC1() );
356 aPoints.Point( BEZIER_CTRL_PT2 ).SetPosition( m_bezier.GetBezierC2() );
357 aPoints.Point( BEZIER_END ).SetPosition( m_bezier.GetEnd() );
358 return true;
359}
360
361
363 COMMIT& aCommit, std::vector<EDA_ITEM*>& aUpdatedItems )
364{
366
367 if( isModified( aEditedPoint, aPoints.Point( BEZIER_START ) ) )
368 {
369 m_bezier.SetStart( aPoints.Point( BEZIER_START ).GetPosition() );
370 }
371 else if( isModified( aEditedPoint, aPoints.Point( BEZIER_CTRL_PT1 ) ) )
372 {
373 m_bezier.SetBezierC1( aPoints.Point( BEZIER_CTRL_PT1 ).GetPosition() );
374 }
375 else if( isModified( aEditedPoint, aPoints.Point( BEZIER_CTRL_PT2 ) ) )
376 {
377 m_bezier.SetBezierC2( aPoints.Point( BEZIER_CTRL_PT2 ).GetPosition() );
378 }
379 else if( isModified( aEditedPoint, aPoints.Point( BEZIER_END ) ) )
380 {
381 m_bezier.SetEnd( aPoints.Point( BEZIER_END ).GetPosition() );
382 }
383
384 m_bezier.RebuildBezierToSegmentsPointsList( m_maxError );
385}
386
387
388// Note: these static arc functions don't have to be in here - we could ship them out
389// to a utils area for use by other code (e.g. polygon fillet editing).
390
394static void editArcEndpointKeepTangent( EDA_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
395 const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCursor )
396{
397 VECTOR2I center = aCenter;
398 bool movingStart;
399 bool arcValid = true;
400
401 VECTOR2I p1, p2, p3;
402 // p1 does not move, p2 does.
403
404 if( aStart != aArc.GetStart() )
405 {
406 p1 = aEnd;
407 p2 = aStart;
408 p3 = aMid;
409 movingStart = true;
410 }
411 else if( aEnd != aArc.GetEnd() )
412 {
413 p1 = aStart;
414 p2 = aEnd;
415 p3 = aMid;
416 movingStart = false;
417 }
418 else
419 {
420 return;
421 }
422
423 VECTOR2D v1, v2, v3, v4;
424
425 // Move the coordinate system
426 v1 = p1 - aCenter;
427 v2 = p2 - aCenter;
428 v3 = p3 - aCenter;
429
430 VECTOR2D u1, u2;
431
432 // A point cannot be both the center and on the arc.
433 if( ( v1.EuclideanNorm() == 0 ) || ( v2.EuclideanNorm() == 0 ) )
434 return;
435
436 u1 = v1 / v1.EuclideanNorm();
437 u2 = v3 - ( u1.x * v3.x + u1.y * v3.y ) * u1;
438 u2 = u2 / u2.EuclideanNorm();
439
440 // [ u1, u3 ] is a base centered on the circle with:
441 // u1 : unit vector toward the point that does not move
442 // u2 : unit vector toward the mid point.
443
444 // Get vectors v1, and v2 in that coordinate system.
445
446 double det = u1.x * u2.y - u2.x * u1.y;
447
448 // u1 and u2 are unit vectors, and perpendicular.
449 // det should not be 0. In case it is, do not change the arc.
450 if( det == 0 )
451 return;
452
453 double tmpx = v1.x * u2.y - v1.y * u2.x;
454 double tmpy = -v1.x * u1.y + v1.y * u1.x;
455 v1.x = tmpx;
456 v1.y = tmpy;
457 v1 = v1 / det;
458
459 tmpx = v2.x * u2.y - v2.y * u2.x;
460 tmpy = -v2.x * u1.y + v2.y * u1.x;
461 v2.x = tmpx;
462 v2.y = tmpy;
463 v2 = v2 / det;
464
465 double R = v1.EuclideanNorm();
466 bool transformCircle = false;
467
468 /* p2
469 * X***
470 * ** <---- This is the arc
471 * y ^ **
472 * | R *
473 * | <-----------> *
474 * x------x------>--------x p1
475 * C' <----> C x
476 * delta
477 *
478 * p1 does not move, and the tangent at p1 remains the same.
479 * => The new center, C', will be on the C-p1 axis.
480 * p2 moves
481 *
482 * The radius of the new circle is delta + R
483 *
484 * || C' p2 || = || C' P1 ||
485 * is the same as :
486 * ( delta + p2.x ) ^ 2 + p2.y ^ 2 = ( R + delta ) ^ 2
487 *
488 * delta = ( R^2 - p2.x ^ 2 - p2.y ^2 ) / ( 2 * p2.x - 2 * R )
489 *
490 * We can use this equation for any point p2 with p2.x < R
491 */
492
493 if( v2.x == R )
494 {
495 // Straight line, do nothing
496 }
497 else
498 {
499 if( v2.x > R )
500 {
501 // If we need to invert the curvature.
502 // We modify the input so we can use the same equation
503 transformCircle = true;
504 v2.x = 2 * R - v2.x;
505 }
506
507 // We can keep the tangent constraint.
508 double delta = ( R * R - v2.x * v2.x - v2.y * v2.y ) / ( 2 * v2.x - 2 * R );
509
510 // This is just to limit the radius, so nothing overflows later when drawing.
511 if( abs( v2.y / ( R - v2.x ) ) > ADVANCED_CFG::GetCfg().m_DrawArcCenterMaxAngle )
512 arcValid = false;
513
514 // Never recorded a problem, but still checking.
515 if( !std::isfinite( delta ) )
516 arcValid = false;
517
518 // v4 is the new center
519 v4 = ( !transformCircle ) ? VECTOR2D( -delta, 0 ) : VECTOR2D( 2 * R + delta, 0 );
520
521 tmpx = v4.x * u1.x + v4.y * u2.x;
522 tmpy = v4.x * u1.y + v4.y * u2.y;
523 v4.x = tmpx;
524 v4.y = tmpy;
525
526 center = v4 + aCenter;
527
528 if( arcValid )
529 {
530 aArc.SetCenter( center );
531
532 if( movingStart )
533 aArc.SetStart( aStart );
534 else
535 aArc.SetEnd( aEnd );
536 }
537 }
538}
539
540
544static void editArcCenterKeepEndpoints( EDA_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
545 const VECTOR2I& aMid, const VECTOR2I& aEnd )
546{
547 const int c_snapEpsilon_sq = 4;
548
549 VECTOR2I m = ( aStart / 2 + aEnd / 2 );
550 VECTOR2I perp = ( aEnd - aStart ).Perpendicular().Resize( INT_MAX / 2 );
551
552 SEG legal( m - perp, m + perp );
553
554 const SEG testSegments[] = {
555 SEG( aCenter, aCenter + VECTOR2( 1, 0 ) ),
556 SEG( aCenter, aCenter + VECTOR2( 0, 1 ) ),
557 };
558
559 std::vector<VECTOR2I> points = { legal.A, legal.B };
560
561 for( const SEG& seg : testSegments )
562 {
563 OPT_VECTOR2I vec = legal.IntersectLines( seg );
564
565 if( vec && legal.SquaredDistance( *vec ) <= c_snapEpsilon_sq )
566 points.push_back( *vec );
567 }
568
569 OPT_VECTOR2I nearest;
571
572 // Snap by distance between cursor and intersections
573 for( const VECTOR2I& pt : points )
574 {
575 SEG::ecoord d_sq = ( pt - aCenter ).SquaredEuclideanNorm();
576
577 if( d_sq < min_d_sq - c_snapEpsilon_sq )
578 {
579 min_d_sq = d_sq;
580 nearest = pt;
581 }
582 }
583
584 if( nearest )
585 aArc.SetCenter( *nearest );
586}
587
588
592static void editArcEndpointKeepCenter( EDA_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
593 const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCursor )
594{
595 int minRadius = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 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
687static void editArcMidKeepCenter( EDA_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
688 const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCursor )
689{
690 int minRadius = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
691
692 // Now, update the edit point position
693 // Express the point in a circle-centered coordinate system.
694 VECTOR2I start = aStart - aCenter;
695 VECTOR2I end = aEnd - aCenter;
696
697 double radius = ( aCursor - aCenter ).EuclideanNorm();
698
699 if( radius < minRadius )
700 radius = minRadius;
701
702 start = start.Resize( KiROUND( radius ) );
703 end = end.Resize( KiROUND( radius ) );
704
705 start = start + aCenter;
706 end = end + aCenter;
707
708 aArc.SetStart( start );
709 aArc.SetEnd( end );
710}
711
712
716static void editArcMidKeepEndpoints( EDA_SHAPE& aArc, const VECTOR2I& aStart, const VECTOR2I& aEnd,
717 const VECTOR2I& aCursor )
718{
719 // Let 'm' be the middle point of the chord between the start and end points
720 VECTOR2I m = ( aStart + aEnd ) / 2;
721
722 // Legal midpoints lie on a vector starting just off the chord midpoint and extending out
723 // past the existing midpoint. We do not allow arc inflection while point editing.
724 const int JUST_OFF = ( aStart - aEnd ).EuclideanNorm() / 100;
725 VECTOR2I v = (VECTOR2I) aArc.GetArcMid() - m;
726 SEG legal( m + v.Resize( JUST_OFF ), m + v.Resize( INT_MAX / 2 ) );
727 VECTOR2I mid = legal.NearestPoint( aCursor );
728
729 aArc.SetArcGeometry( aStart, mid, aEnd );
730}
731
732
734 KIGFX::VIEW_CONTROLS& aViewContols ) :
735 m_arc( aArc ),
736 m_arcEditMode( aArcEditMode ),
737 m_viewControls( aViewContols )
738{
739 wxASSERT( m_arc.GetShape() == SHAPE_T::ARC );
740}
741
742
744{
745 aPoints.AddPoint( m_arc.GetStart() );
746 aPoints.AddPoint( m_arc.GetArcMid() );
747 aPoints.AddPoint( m_arc.GetEnd() );
748 aPoints.AddPoint( m_arc.getCenter() );
749
750 aPoints.AddIndicatorLine( aPoints.Point( ARC_CENTER ), aPoints.Point( ARC_START ) );
751 aPoints.AddIndicatorLine( aPoints.Point( ARC_CENTER ), aPoints.Point( ARC_END ) );
752}
753
754
756{
757 wxCHECK( aPoints.PointsSize() == 4, false );
758
759 aPoints.Point( ARC_START ).SetPosition( m_arc.GetStart() );
760 aPoints.Point( ARC_MID ).SetPosition( m_arc.GetArcMid() );
761 aPoints.Point( ARC_END ).SetPosition( m_arc.GetEnd() );
762 aPoints.Point( ARC_CENTER ).SetPosition( m_arc.getCenter() );
763 return true;
764}
765
766
767void EDA_ARC_POINT_EDIT_BEHAVIOR::UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
768 std::vector<EDA_ITEM*>& aUpdatedItems )
769{
770 CHECK_POINT_COUNT( aPoints, 4 );
771
773 VECTOR2I mid = aPoints.Point( ARC_MID ).GetPosition();
774 VECTOR2I start = aPoints.Point( ARC_START ).GetPosition();
775 VECTOR2I end = aPoints.Point( ARC_END ).GetPosition();
776
777 if( isModified( aEditedPoint, aPoints.Point( ARC_CENTER ) ) )
778 {
779 switch( m_arcEditMode )
780 {
783 break;
784
787 {
788 // Both these modes just move the arc
789 VECTOR2I moveVector = center - m_arc.getCenter();
790
791 m_arc.SetArcGeometry( m_arc.GetStart() + moveVector, m_arc.GetArcMid() + moveVector,
792 m_arc.GetEnd() + moveVector );
793 break;
794 }
795 }
796 }
797 else if( isModified( aEditedPoint, aPoints.Point( ARC_MID ) ) )
798 {
799 const VECTOR2I& cursorPos = m_viewControls.GetCursorPosition( false );
800
801 switch( m_arcEditMode )
802 {
804 editArcMidKeepEndpoints( m_arc, start, end, cursorPos );
805 break;
808 editArcMidKeepCenter( m_arc, center, start, mid, end, cursorPos );
809 break;
810 }
811 }
812 else if( isModified( aEditedPoint, aPoints.Point( ARC_START ) )
813 || isModified( aEditedPoint, aPoints.Point( ARC_END ) ) )
814 {
815 const VECTOR2I& cursorPos = m_viewControls.GetCursorPosition();
816
817 switch( m_arcEditMode )
818 {
820 editArcEndpointKeepCenter( m_arc, center, start, mid, end, cursorPos );
821 break;
824 break;
826 editArcEndpointKeepTangent( m_arc, center, start, mid, end, cursorPos );
827 break;
828 }
829 }
830}
831
832
834 EDIT_POINTS& aPoints ) const
835{
836 return aPoints.Point( ARC_CENTER ).GetPosition();
837}
838
839
841{
842 aPoints.AddPoint( m_cell.GetEnd() - VECTOR2I( 0, m_cell.GetRectangleHeight() / 2 ) );
843 aPoints.AddPoint( m_cell.GetEnd() - VECTOR2I( m_cell.GetRectangleWidth() / 2, 0 ) );
844}
845
846
848{
849 aPoints.Point( COL_WIDTH ).SetPosition( m_cell.GetEnd() - VECTOR2I( 0, m_cell.GetRectangleHeight() / 2 ) );
850 aPoints.Point( ROW_HEIGHT ).SetPosition( m_cell.GetEnd() - VECTOR2I( m_cell.GetRectangleWidth() / 2, 0 ) );
851 return true;
852}
853
854
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 EDA_IU_SCALE pcbIUScale
Definition base_units.h:125
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
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:72
EDIT_CONSTRAINT for polygon line dragging.
double Sin() const
Definition eda_angle.h:178
double Cos() const
Definition eda_angle.h:197
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
EDA_ARC_POINT_EDIT_BEHAVIOR(EDA_SHAPE &aArc, const ARC_EDIT_MODE &aArcEditMode, KIGFX::VIEW_CONTROLS &aViewContols)
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.
void SetCenter(const VECTOR2I &aCenter)
int GetRadius() const
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:236
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:198
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:194
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:240
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
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:48
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:72
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:42
VECTOR2I A
Definition seg.h:49
ecoord SquaredDistance(const SEG &aSeg) const
Definition seg.cpp:80
VECTOR2I::extended_type ecoord
Definition seg.h:44
VECTOR2I B
Definition seg.h:50
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition seg.cpp:633
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition seg.h:220
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:71
static constexpr extended_type ECOORD_MAX
Definition vector2d.h:76
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:283
VECTOR2< T > Resize(T aNewLength) const
Return a vector of the same direction, but length specified in aNewLength.
Definition vector2d.h:385
@ RADIANS_T
Definition eda_angle.h:32
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:417
@ ELLIPSE_ARC
Definition eda_shape.h:57
constexpr int Mils2IU(const EDA_IU_SCALE &aIuScale, int mils)
Definition eda_units.h:175
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)
static void editArcEndpointKeepCenter(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 around the circumference.
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 editArcMidKeepCenter(EDA_SHAPE &aArc, const VECTOR2I &aCenter, const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd, const VECTOR2I &aCursor)
Move the mid point of the arc, while keeping the two endpoints.
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:39
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:687
VECTOR2< double > VECTOR2D
Definition vector2d.h:686