KiCad PCB EDA Suite
Loading...
Searching...
No Matches
pcb_point_editor.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2013-2021 CERN
5 * Copyright (C) 2018-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <functional>
27#include <memory>
28
29using namespace std::placeholders;
30#include <advanced_config.h>
31#include <kiplatform/ui.h>
32#include <view/view_controls.h>
36#include <geometry/seg.h>
38#include <confirm.h>
39#include <tool/tool_manager.h>
41#include <tools/pcb_actions.h>
47#include <board_commit.h>
48#include <pcb_edit_frame.h>
49#include <pcb_reference_image.h>
50#include <pcb_generator.h>
51#include <pcb_dimension.h>
52#include <pcb_textbox.h>
53#include <pcb_tablecell.h>
54#include <pcb_table.h>
55#include <pad.h>
56#include <zone.h>
57#include <footprint.h>
60#include <progress_reporter.h>
61
62const unsigned int PCB_POINT_EDITOR::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
63
64// Few constants to avoid using bare numbers for point indices
66{
72
73 RECT_MAX_POINTS, // Must be last
74};
75
76
78{
80};
81
82
84{
91
96};
97
98
101{
104};
105
106
108{
109public:
111 {
112 wxASSERT( m_rectangle.GetShape() == SHAPE_T::RECTANGLE );
113 }
114
119 static void MakePoints( const PCB_SHAPE& aRectangle, EDIT_POINTS& aPoints )
120 {
121 wxCHECK( aRectangle.GetShape() == SHAPE_T::RECTANGLE, /* void */ );
122
123 VECTOR2I topLeft = aRectangle.GetTopLeft();
124 VECTOR2I botRight = aRectangle.GetBotRight();
125
126 aPoints.SetSwapX( topLeft.x > botRight.x );
127 aPoints.SetSwapY( topLeft.y > botRight.y );
128
129 if( aPoints.SwapX() )
130 std::swap( topLeft.x, botRight.x );
131
132 if( aPoints.SwapY() )
133 std::swap( topLeft.y, botRight.y );
134
135 aPoints.AddPoint( topLeft );
136 aPoints.AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
137 aPoints.AddPoint( botRight );
138 aPoints.AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
139 aPoints.AddPoint( aRectangle.GetCenter() );
140
141 aPoints.AddLine( aPoints.Point( RECT_TOP_LEFT ), aPoints.Point( RECT_TOP_RIGHT ) );
142 aPoints.Line( RECT_TOP ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_TOP ) ) );
143 aPoints.AddLine( aPoints.Point( RECT_TOP_RIGHT ), aPoints.Point( RECT_BOT_RIGHT ) );
144 aPoints.Line( RECT_RIGHT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_RIGHT ) ) );
145 aPoints.AddLine( aPoints.Point( RECT_BOT_RIGHT ), aPoints.Point( RECT_BOT_LEFT ) );
146 aPoints.Line( RECT_BOT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_BOT ) ) );
147 aPoints.AddLine( aPoints.Point( RECT_BOT_LEFT ), aPoints.Point( RECT_TOP_LEFT ) );
148 aPoints.Line( RECT_LEFT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_LEFT ) ) );
149 }
150
151 static void UpdateItem( PCB_SHAPE& aRectangle, const EDIT_POINT& aEditedPoint,
152 EDIT_POINTS& aPoints )
153 {
154 // You can have more points if your item wants to have more points
155 // (this class assumes the rect points come first, but that can be changed)
157
158 auto setLeft =
159 [&]( int left )
160 {
161 aPoints.SwapX() ? aRectangle.SetRight( left ) : aRectangle.SetLeft( left );
162 };
163 auto setRight =
164 [&]( int right )
165 {
166 aPoints.SwapX() ? aRectangle.SetLeft( right ) : aRectangle.SetRight( right );
167 };
168 auto setTop =
169 [&]( int top )
170 {
171 aPoints.SwapY() ? aRectangle.SetBottom( top ) : aRectangle.SetTop( top );
172 };
173 auto setBottom =
174 [&]( int bottom )
175 {
176 aPoints.SwapY() ? aRectangle.SetTop( bottom ) : aRectangle.SetBottom( bottom );
177 };
178
179 VECTOR2I topLeft = aPoints.Point( RECT_TOP_LEFT ).GetPosition();
180 VECTOR2I topRight = aPoints.Point( RECT_TOP_RIGHT ).GetPosition();
181 VECTOR2I botLeft = aPoints.Point( RECT_BOT_LEFT ).GetPosition();
182 VECTOR2I botRight = aPoints.Point( RECT_BOT_RIGHT ).GetPosition();
183
184 PinEditedCorner( aEditedPoint, aPoints, topLeft, topRight, botLeft, botRight );
185
186 if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_LEFT ) )
187 || isModified( aEditedPoint, aPoints.Point( RECT_TOP_RIGHT ) )
188 || isModified( aEditedPoint, aPoints.Point( RECT_BOT_RIGHT ) )
189 || isModified( aEditedPoint, aPoints.Point( RECT_BOT_LEFT ) ) )
190 {
191 setTop( topLeft.y );
192 setLeft( topLeft.x );
193 setRight( botRight.x );
194 setBottom( botRight.y );
195 }
196 else if( isModified( aEditedPoint, aPoints.Point( RECT_CENTER ) ) )
197 {
198 const VECTOR2I moveVector =
199 aPoints.Point( RECT_CENTER ).GetPosition() - aRectangle.GetCenter();
200 aRectangle.Move( moveVector );
201 }
202 else if( isModified( aEditedPoint, aPoints.Line( RECT_TOP ) ) )
203 {
204 setTop( topLeft.y );
205 }
206 else if( isModified( aEditedPoint, aPoints.Line( RECT_LEFT ) ) )
207 {
208 setLeft( topLeft.x );
209 }
210 else if( isModified( aEditedPoint, aPoints.Line( RECT_BOT ) ) )
211 {
212 setBottom( botRight.y );
213 }
214 else if( isModified( aEditedPoint, aPoints.Line( RECT_RIGHT ) ) )
215 {
216 setRight( botRight.x );
217 }
218
219 for( unsigned i = 0; i < aPoints.LinesSize(); ++i )
220 {
221 if( !isModified( aEditedPoint, aPoints.Line( i ) ) )
222 {
223 aPoints.Line( i ).SetConstraint( new EC_PERPLINE( aPoints.Line( i ) ) );
224 }
225 }
226 }
227
228 static void UpdatePoints( PCB_SHAPE& aRectangle, EDIT_POINTS& aPoints )
229 {
230 wxCHECK( aPoints.PointsSize() >= RECT_MAX_POINTS, /* void */ );
231
232 VECTOR2I topLeft = aRectangle.GetTopLeft();
233 VECTOR2I botRight = aRectangle.GetBotRight();
234
235 aPoints.SetSwapX( topLeft.x > botRight.x );
236 aPoints.SetSwapY( topLeft.y > botRight.y );
237
238 if( aPoints.SwapX() )
239 std::swap( topLeft.x, botRight.x );
240
241 if( aPoints.SwapY() )
242 std::swap( topLeft.y, botRight.y );
243
244 aPoints.Point( RECT_TOP_LEFT ).SetPosition( topLeft );
245 aPoints.Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y );
246 aPoints.Point( RECT_BOT_RIGHT ).SetPosition( botRight );
247 aPoints.Point( RECT_BOT_LEFT ).SetPosition( topLeft.x, botRight.y );
248 aPoints.Point( RECT_CENTER ).SetPosition( aRectangle.GetCenter() );
249 }
250
251 void MakePoints( EDIT_POINTS& aPoints ) override
252 {
253 // Just call the static helper
254 MakePoints( m_rectangle, aPoints );
255 }
256
257 void UpdatePoints( EDIT_POINTS& aPoints ) override
258 {
259 UpdatePoints( m_rectangle, aPoints );
260 }
261
262 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
263 std::vector<EDA_ITEM*>& aUpdatedItems ) override
264 {
265 UpdateItem( m_rectangle, aEditedPoint, aPoints );
266 }
267
281 static void PinEditedCorner( const EDIT_POINT& aEditedPoint, const EDIT_POINTS& aEditPoints,
282 VECTOR2I& aTopLeft, VECTOR2I& aTopRight, VECTOR2I& aBotLeft,
283 VECTOR2I& aBotRight, const VECTOR2I& aHole = { 0, 0 },
284 const VECTOR2I& aHoleSize = { 0, 0 } )
285 {
286 int minWidth = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
287 int minHeight = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
288
289 if( isModified( aEditedPoint, aEditPoints.Point( RECT_TOP_LEFT ) ) )
290 {
291 if( aHoleSize.x )
292 {
293 // pin edited point to the top/left of the hole
294 aTopLeft.x = std::min( aTopLeft.x, aHole.x - aHoleSize.x / 2 - minWidth );
295 aTopLeft.y = std::min( aTopLeft.y, aHole.y - aHoleSize.y / 2 - minHeight );
296 }
297 else
298 {
299 // pin edited point within opposite corner
300 aTopLeft.x = std::min( aTopLeft.x, aBotRight.x - minWidth );
301 aTopLeft.y = std::min( aTopLeft.y, aBotRight.y - minHeight );
302 }
303
304 // push edited point edges to adjacent corners
305 aTopRight.y = aTopLeft.y;
306 aBotLeft.x = aTopLeft.x;
307 }
308 else if( isModified( aEditedPoint, aEditPoints.Point( RECT_TOP_RIGHT ) ) )
309 {
310 if( aHoleSize.x )
311 {
312 // pin edited point to the top/right of the hole
313 aTopRight.x = std::max( aTopRight.x, aHole.x + aHoleSize.x / 2 + minWidth );
314 aTopRight.y = std::min( aTopRight.y, aHole.y - aHoleSize.y / 2 - minHeight );
315 }
316 else
317 {
318 // pin edited point within opposite corner
319 aTopRight.x = std::max( aTopRight.x, aBotLeft.x + minWidth );
320 aTopRight.y = std::min( aTopRight.y, aBotLeft.y - minHeight );
321 }
322
323 // push edited point edges to adjacent corners
324 aTopLeft.y = aTopRight.y;
325 aBotRight.x = aTopRight.x;
326 }
327 else if( isModified( aEditedPoint, aEditPoints.Point( RECT_BOT_LEFT ) ) )
328 {
329 if( aHoleSize.x )
330 {
331 // pin edited point to the bottom/left of the hole
332 aBotLeft.x = std::min( aBotLeft.x, aHole.x - aHoleSize.x / 2 - minWidth );
333 aBotLeft.y = std::max( aBotLeft.y, aHole.y + aHoleSize.y / 2 + minHeight );
334 }
335 else
336 {
337 // pin edited point within opposite corner
338 aBotLeft.x = std::min( aBotLeft.x, aTopRight.x - minWidth );
339 aBotLeft.y = std::max( aBotLeft.y, aTopRight.y + minHeight );
340 }
341
342 // push edited point edges to adjacent corners
343 aBotRight.y = aBotLeft.y;
344 aTopLeft.x = aBotLeft.x;
345 }
346 else if( isModified( aEditedPoint, aEditPoints.Point( RECT_BOT_RIGHT ) ) )
347 {
348 if( aHoleSize.x )
349 {
350 // pin edited point to the bottom/right of the hole
351 aBotRight.x = std::max( aBotRight.x, aHole.x + aHoleSize.x / 2 + minWidth );
352 aBotRight.y = std::max( aBotRight.y, aHole.y + aHoleSize.y / 2 + minHeight );
353 }
354 else
355 {
356 // pin edited point within opposite corner
357 aBotRight.x = std::max( aBotRight.x, aTopLeft.x + minWidth );
358 aBotRight.y = std::max( aBotRight.y, aTopLeft.y + minHeight );
359 }
360
361 // push edited point edges to adjacent corners
362 aBotLeft.y = aBotRight.y;
363 aTopRight.x = aBotRight.x;
364 }
365 else if( isModified( aEditedPoint, aEditPoints.Line( RECT_TOP ) ) )
366 {
367 aTopLeft.y = std::min( aTopLeft.y, aBotRight.y - minHeight );
368 }
369 else if( isModified( aEditedPoint, aEditPoints.Line( RECT_LEFT ) ) )
370 {
371 aTopLeft.x = std::min( aTopLeft.x, aBotRight.x - minWidth );
372 }
373 else if( isModified( aEditedPoint, aEditPoints.Line( RECT_BOT ) ) )
374 {
375 aBotRight.y = std::max( aBotRight.y, aTopLeft.y + minHeight );
376 }
377 else if( isModified( aEditedPoint, aEditPoints.Line( RECT_RIGHT ) ) )
378 {
379 aBotRight.x = std::max( aBotRight.x, aTopLeft.x + minWidth );
380 }
381 }
382
383private:
385};
386
387
389{
391 {
396 };
397
398public:
400 KIGFX::VIEW_CONTROLS& aViewContols ) :
401 m_arc( aArc ), m_arcEditMode( aArcEditMode ), m_viewControls( aViewContols )
402 {
403 wxASSERT( m_arc.GetShape() == SHAPE_T::ARC );
404 }
405
406 void MakePoints( EDIT_POINTS& aPoints ) override
407 {
408 aPoints.AddPoint( m_arc.GetStart() );
409 aPoints.AddPoint( m_arc.GetArcMid() );
410 aPoints.AddPoint( m_arc.GetEnd() );
411 aPoints.AddPoint( m_arc.GetCenter() );
412
413 aPoints.AddIndicatorLine( aPoints.Point( ARC_CENTER ), aPoints.Point( ARC_START ) );
414 aPoints.AddIndicatorLine( aPoints.Point( ARC_CENTER ), aPoints.Point( ARC_END ) );
415 }
416
417 void UpdatePoints( EDIT_POINTS& aPoints ) override
418 {
419 CHECK_POINT_COUNT( aPoints, 4 );
420
421 aPoints.Point( ARC_START ).SetPosition( m_arc.GetStart() );
422 aPoints.Point( ARC_MID ).SetPosition( m_arc.GetArcMid() );
423 aPoints.Point( ARC_END ).SetPosition( m_arc.GetEnd() );
425 }
426
427 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
428 std::vector<EDA_ITEM*>& aUpdatedItems ) override
429 {
430 CHECK_POINT_COUNT( aPoints, 4 );
431
432 VECTOR2I center = aPoints.Point( ARC_CENTER ).GetPosition();
433 VECTOR2I mid = aPoints.Point( ARC_MID ).GetPosition();
434 VECTOR2I start = aPoints.Point( ARC_START ).GetPosition();
435 VECTOR2I end = aPoints.Point( ARC_END ).GetPosition();
436
437 if( isModified( aEditedPoint, aPoints.Point( ARC_CENTER ) ) )
438 {
439 if( m_arcEditMode == ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION )
440 {
441 editArcCenterKeepEndpoints( m_arc, center, start, mid, end );
442 }
443 else
444 {
445 VECTOR2I moveVector = VECTOR2I( center.x, center.y ) - m_arc.GetCenter();
446 m_arc.Move( moveVector );
447 }
448 }
449 else if( isModified( aEditedPoint, aPoints.Point( ARC_MID ) ) )
450 {
451 const VECTOR2I& cursorPos = m_viewControls.GetCursorPosition( false );
452
453 if( m_arcEditMode == ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION )
454 editArcMidKeepEndpoints( m_arc, start, end, cursorPos );
455 else
456 editArcMidKeepCenter( m_arc, center, start, mid, end, cursorPos );
457 }
458 else if( isModified( aEditedPoint, aPoints.Point( ARC_START ) )
459 || isModified( aEditedPoint, aPoints.Point( ARC_END ) ) )
460 {
461 const VECTOR2I& cursorPos = m_viewControls.GetCursorPosition();
462
463 if( m_arcEditMode == ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION )
464 editArcEndpointKeepTangent( m_arc, center, start, mid, end, cursorPos );
465 else
466 editArcEndpointKeepCenter( m_arc, center, start, mid, end, cursorPos );
467 }
468 }
469
471 EDIT_POINTS& aPoints ) const override
472 {
473 return aPoints.Point( ARC_CENTER ).GetPosition();
474 }
475
476private:
477 // Note: these static arc functions don't have to be in here - we could ship them out
478 // to a utils area for use by other behaviours (e.g. in eeschema, or for polygons with
479 // radiused corners). For now this is the smaller conceptual delta with the old code.
480
484 static void editArcEndpointKeepTangent( PCB_SHAPE& aArc, const VECTOR2I& aCenter,
485 const VECTOR2I& aStart, const VECTOR2I& aMid,
486 const VECTOR2I& aEnd, const VECTOR2I& aCursor )
487 {
488 VECTOR2I center = aCenter;
489 bool movingStart;
490 bool arcValid = true;
491
492 VECTOR2I p1, p2, p3;
493 // p1 does not move, p2 does.
494
495 if( aStart != aArc.GetStart() )
496 {
497 p1 = aEnd;
498 p2 = aStart;
499 p3 = aMid;
500 movingStart = true;
501 }
502 else if( aEnd != aArc.GetEnd() )
503 {
504 p1 = aStart;
505 p2 = aEnd;
506 p3 = aMid;
507 movingStart = false;
508 }
509 else
510 {
511 return;
512 }
513
514 VECTOR2D v1, v2, v3, v4;
515
516 // Move the coordinate system
517 v1 = p1 - aCenter;
518 v2 = p2 - aCenter;
519 v3 = p3 - aCenter;
520
521 VECTOR2D u1, u2;
522
523 // A point cannot be both the center and on the arc.
524 if( ( v1.EuclideanNorm() == 0 ) || ( v2.EuclideanNorm() == 0 ) )
525 return;
526
527 u1 = v1 / v1.EuclideanNorm();
528 u2 = v3 - ( u1.x * v3.x + u1.y * v3.y ) * u1;
529 u2 = u2 / u2.EuclideanNorm();
530
531 // [ u1, u3 ] is a base centered on the circle with:
532 // u1 : unit vector toward the point that does not move
533 // u2 : unit vector toward the mid point.
534
535 // Get vectors v1, and v2 in that coordinate system.
536
537 double det = u1.x * u2.y - u2.x * u1.y;
538
539 // u1 and u2 are unit vectors, and perpendicular.
540 // det should not be 0. In case it is, do not change the arc.
541 if( det == 0 )
542 return;
543
544 double tmpx = v1.x * u2.y - v1.y * u2.x;
545 double tmpy = -v1.x * u1.y + v1.y * u1.x;
546 v1.x = tmpx;
547 v1.y = tmpy;
548 v1 = v1 / det;
549
550 tmpx = v2.x * u2.y - v2.y * u2.x;
551 tmpy = -v2.x * u1.y + v2.y * u1.x;
552 v2.x = tmpx;
553 v2.y = tmpy;
554 v2 = v2 / det;
555
556 double R = v1.EuclideanNorm();
557 bool transformCircle = false;
558
559 /* p2
560 * X***
561 * ** <---- This is the arc
562 * y ^ **
563 * | R *
564 * | <-----------> *
565 * x------x------>--------x p1
566 * C' <----> C x
567 * delta
568 *
569 * p1 does not move, and the tangent at p1 remains the same.
570 * => The new center, C', will be on the C-p1 axis.
571 * p2 moves
572 *
573 * The radius of the new circle is delta + R
574 *
575 * || C' p2 || = || C' P1 ||
576 * is the same as :
577 * ( delta + p2.x ) ^ 2 + p2.y ^ 2 = ( R + delta ) ^ 2
578 *
579 * delta = ( R^2 - p2.x ^ 2 - p2.y ^2 ) / ( 2 * p2.x - 2 * R )
580 *
581 * We can use this equation for any point p2 with p2.x < R
582 */
583
584 if( v2.x == R )
585 {
586 // Straight line, do nothing
587 }
588 else
589 {
590 if( v2.x > R )
591 {
592 // If we need to invert the curvature.
593 // We modify the input so we can use the same equation
594 transformCircle = true;
595 v2.x = 2 * R - v2.x;
596 }
597
598 // We can keep the tangent constraint.
599 double delta = ( R * R - v2.x * v2.x - v2.y * v2.y ) / ( 2 * v2.x - 2 * R );
600
601 // This is just to limit the radius, so nothing overflows later when drawing.
602 if( abs( v2.y / ( R - v2.x ) ) > ADVANCED_CFG::GetCfg().m_DrawArcCenterMaxAngle )
603 arcValid = false;
604
605 // Never recorded a problem, but still checking.
606 if( !std::isfinite( delta ) )
607 arcValid = false;
608
609 // v4 is the new center
610 v4 = ( !transformCircle ) ? VECTOR2D( -delta, 0 ) : VECTOR2D( 2 * R + delta, 0 );
611
612 tmpx = v4.x * u1.x + v4.y * u2.x;
613 tmpy = v4.x * u1.y + v4.y * u2.y;
614 v4.x = tmpx;
615 v4.y = tmpy;
616
617 center = v4 + aCenter;
618
619 if( arcValid )
620 {
621 aArc.SetCenter( center );
622
623 if( movingStart )
624 aArc.SetStart( aStart );
625 else
626 aArc.SetEnd( aEnd );
627 }
628 }
629 }
630
634 static void editArcCenterKeepEndpoints( PCB_SHAPE& aArc, const VECTOR2I& aCenter,
635 const VECTOR2I& aStart, const VECTOR2I& aMid,
636 const VECTOR2I& aEnd )
637 {
638 const int c_snapEpsilon_sq = 4;
639
640 VECTOR2I m = ( aStart / 2 + aEnd / 2 );
641 VECTOR2I perp = ( aEnd - aStart ).Perpendicular().Resize( INT_MAX / 2 );
642
643 SEG legal( m - perp, m + perp );
644
645 const SEG testSegments[] = { SEG( aCenter, aCenter + VECTOR2( 1, 0 ) ),
646 SEG( aCenter, aCenter + VECTOR2( 0, 1 ) ) };
647
648 std::vector<VECTOR2I> points = { legal.A, legal.B };
649
650 for( const SEG& seg : testSegments )
651 {
652 OPT_VECTOR2I vec = legal.IntersectLines( seg );
653
654 if( vec && legal.SquaredDistance( *vec ) <= c_snapEpsilon_sq )
655 points.push_back( *vec );
656 }
657
658 OPT_VECTOR2I nearest;
660
661 // Snap by distance between cursor and intersections
662 for( const VECTOR2I& pt : points )
663 {
664 SEG::ecoord d_sq = ( pt - aCenter ).SquaredEuclideanNorm();
665
666 if( d_sq < min_d_sq - c_snapEpsilon_sq )
667 {
668 min_d_sq = d_sq;
669 nearest = pt;
670 }
671 }
672
673 if( nearest )
674 aArc.SetCenter( *nearest );
675 }
676
680 static void editArcEndpointKeepCenter( PCB_SHAPE& aArc, const VECTOR2I& aCenter,
681 const VECTOR2I& aStart, const VECTOR2I& aMid,
682 const VECTOR2I& aEnd, const VECTOR2I& aCursor )
683 {
684 int minRadius = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
685 bool movingStart;
686
687 VECTOR2I p1, p2, prev_p1;
688
689 // user is moving p1, we want to move p2 to the new radius.
690
691 if( aStart != aArc.GetStart() )
692 {
693 prev_p1 = aArc.GetStart();
694 p1 = aStart;
695 p2 = aEnd;
696 movingStart = true;
697 }
698 else
699 {
700 prev_p1 = aArc.GetEnd();
701 p1 = aEnd;
702 p2 = aStart;
703 movingStart = false;
704 }
705
706 p1 = p1 - aCenter;
707 p2 = p2 - aCenter;
708
709 if( p1.x == 0 && p1.y == 0 )
710 p1 = prev_p1 - aCenter;
711
712 if( p2.x == 0 && p2.y == 0 )
713 p2 = { 1, 0 };
714
715 double radius = p1.EuclideanNorm();
716
717 if( radius < minRadius )
718 radius = minRadius;
719
720 p1 = aCenter + p1.Resize( radius );
721 p2 = aCenter + p2.Resize( radius );
722
723 aArc.SetCenter( aCenter );
724
725 if( movingStart )
726 {
727 aArc.SetStart( p1 );
728 aArc.SetEnd( p2 );
729 }
730 else
731 {
732 aArc.SetStart( p2 );
733 aArc.SetEnd( p1 );
734 }
735 }
736
740 void editArcMidKeepCenter( PCB_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
741 const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCursor )
742 {
743 int minRadius = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
744
745 SEG chord( aStart, aEnd );
746
747 // Now, update the edit point position
748 // Express the point in a circle-centered coordinate system.
749 VECTOR2I start = aStart - aCenter;
750 VECTOR2I end = aEnd - aCenter;
751
752 double radius = ( aCursor - aCenter ).EuclideanNorm();
753
754 if( radius < minRadius )
755 radius = minRadius;
756
757 start = start.Resize( radius );
758 end = end.Resize( radius );
759
760 start = start + aCenter;
761 end = end + aCenter;
762
763 aArc.SetStart( start );
764 aArc.SetEnd( end );
765 }
766
770 static void editArcMidKeepEndpoints( PCB_SHAPE& aArc, const VECTOR2I& aStart,
771 const VECTOR2I& aEnd, const VECTOR2I& aCursor )
772 {
773 // Let 'm' be the middle point of the chord between the start and end points
774 VECTOR2I m = ( aStart + aEnd ) / 2;
775
776 // Legal midpoints lie on a vector starting just off the chord midpoint and extending out
777 // past the existing midpoint. We do not allow arc inflection while point editing.
778 const int JUST_OFF = ( aStart - aEnd ).EuclideanNorm() / 100;
779 VECTOR2I v = (VECTOR2I) aArc.GetArcMid() - m;
780 SEG legal( m + v.Resize( JUST_OFF ), m + v.Resize( INT_MAX / 2 ) );
781 VECTOR2I mid = legal.NearestPoint( aCursor );
782
783 aArc.SetArcGeometry( aStart, mid, aEnd );
784 }
785
787 // The arc edit mode, which is injected from the editor
790};
791
792
794{
795public:
797 POLYGON_POINT_EDIT_BEHAVIOR( *aZone.Outline() ), m_zone( aZone )
798 {
799 }
800
801 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
802 std::vector<EDA_ITEM*>& aUpdatedItems ) override
803 {
804 m_zone.UnFill();
805
806 // Defer to the base class to update the polygon
807 POLYGON_POINT_EDIT_BEHAVIOR::UpdateItem( aEditedPoint, aPoints, aCommit, aUpdatedItems );
808
810 }
811
812private:
814};
815
816
818{
820 {
821 REFIMG_ORIGIN = RECT_CENTER, // Reuse the center point fo rthe transform origin
822
824 };
825
826public:
828 {
829 }
830
831 void MakePoints( EDIT_POINTS& aPoints ) override
832 {
834
835 const VECTOR2I topLeft = refImage.GetPosition() - refImage.GetSize() / 2;
836 const VECTOR2I botRight = refImage.GetPosition() + refImage.GetSize() / 2;
837
838 aPoints.AddPoint( topLeft );
839 aPoints.AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
840 aPoints.AddPoint( botRight );
841 aPoints.AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
842
843 aPoints.AddPoint( refImage.GetPosition() + refImage.GetTransformOriginOffset() );
844 }
845
846 void UpdatePoints( EDIT_POINTS& aPoints ) override
847 {
849
851
852 const VECTOR2I topLeft = refImage.GetPosition() - refImage.GetSize() / 2;
853 const VECTOR2I botRight = refImage.GetPosition() + refImage.GetSize() / 2;
854
855 aPoints.Point( RECT_TOP_LEFT ).SetPosition( topLeft );
856 aPoints.Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y );
857 aPoints.Point( RECT_BOT_RIGHT ).SetPosition( botRight );
858 aPoints.Point( RECT_BOT_LEFT ).SetPosition( topLeft.x, botRight.y );
859
860 aPoints.Point( REFIMG_ORIGIN )
861 .SetPosition( refImage.GetPosition() + refImage.GetTransformOriginOffset() );
862 }
863
864 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
865 std::vector<EDA_ITEM*>& aUpdatedItems ) override
866 {
868
870
871 const VECTOR2I topLeft = aPoints.Point( RECT_TOP_LEFT ).GetPosition();
872 const VECTOR2I topRight = aPoints.Point( RECT_TOP_RIGHT ).GetPosition();
873 const VECTOR2I botRight = aPoints.Point( RECT_BOT_RIGHT ).GetPosition();
874 const VECTOR2I botLeft = aPoints.Point( RECT_BOT_LEFT ).GetPosition();
875 const VECTOR2I xfrmOrigin = aPoints.Point( REFIMG_ORIGIN ).GetPosition();
876
877 if( isModified( aEditedPoint, aPoints.Point( REFIMG_ORIGIN ) ) )
878 {
879 // Moving the transform origin
880 // As the other points didn't move, we can get the image extent from them
881 const VECTOR2I newOffset = xfrmOrigin - ( topLeft + botRight ) / 2;
882 refImage.SetTransformOriginOffset( newOffset );
883 }
884 else
885 {
886 const VECTOR2I oldOrigin =
888 const VECTOR2I oldSize = refImage.GetSize();
889 const VECTOR2I pos = refImage.GetPosition();
890
891 OPT_VECTOR2I newCorner;
892 VECTOR2I oldCorner = pos;
893 if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_LEFT ) ) )
894 {
895 newCorner = topLeft;
896 oldCorner -= oldSize / 2;
897 }
898 else if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_RIGHT ) ) )
899 {
900 newCorner = topRight;
901 oldCorner -= VECTOR2I( -oldSize.x, oldSize.y ) / 2;
902 }
903 else if( isModified( aEditedPoint, aPoints.Point( RECT_BOT_LEFT ) ) )
904 {
905 newCorner = botLeft;
906 oldCorner -= VECTOR2I( oldSize.x, -oldSize.y ) / 2;
907 }
908 else if( isModified( aEditedPoint, aPoints.Point( RECT_BOT_RIGHT ) ) )
909 {
910 newCorner = botRight;
911 oldCorner += oldSize / 2;
912 }
913
914 if( newCorner )
915 {
916 // Turn in the respective vectors from the origin
917 *newCorner -= xfrmOrigin;
918 oldCorner -= oldOrigin;
919
920 // If we tried to cross the origin, clamp it to stop it
921 if( sign( newCorner->x ) != sign( oldCorner.x )
922 || sign( newCorner->y ) != sign( oldCorner.y ) )
923 {
924 *newCorner = VECTOR2I( 0, 0 );
925 }
926
927 const double newLength = newCorner->EuclideanNorm();
928 const double oldLength = oldCorner.EuclideanNorm();
929
930 double ratio = oldLength > 0 ? ( newLength / oldLength ) : 1.0;
931
932 // Clamp the scaling to a minimum of 50 mils
933 VECTOR2I newSize = oldSize * ratio;
934 double newWidth = std::max( newSize.x, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
935 double newHeight = std::max( newSize.y, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
936 ratio = std::min( newWidth / oldSize.x, newHeight / oldSize.y );
937
938 // Also handles the origin offset
939 refImage.SetImageScale( refImage.GetImageScale() * ratio );
940 }
941 }
942 }
943
944private:
946};
947
948
950{
951public:
954 {
955 }
956
957 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
958 std::vector<EDA_ITEM*>& aUpdatedItems ) override
959 {
961
962 PCB_TABLE& table = static_cast<PCB_TABLE&>( *m_cell.GetParent() );
963 aCommit.Modify( &table );
964 aUpdatedItems.push_back( &table );
965
966 if( isModified( aEditedPoint, aPoints.Point( COL_WIDTH ) ) )
967 {
968 m_cell.SetEnd( VECTOR2I( aPoints.Point( 0 ).GetX(), m_cell.GetEndY() ) );
969
970 int colWidth = m_cell.GetRectangleWidth();
971
972 for( int ii = 0; ii < m_cell.GetColSpan() - 1; ++ii )
973 colWidth -= table.GetColWidth( m_cell.GetColumn() + ii );
974
975 table.SetColWidth( m_cell.GetColumn() + m_cell.GetColSpan() - 1, colWidth );
976 }
977 else if( isModified( aEditedPoint, aPoints.Point( ROW_HEIGHT ) ) )
978 {
979 m_cell.SetEnd( VECTOR2I( m_cell.GetEndX(), aPoints.Point( 1 ).GetY() ) );
980
981 int rowHeight = m_cell.GetRectangleHeight();
982
983 for( int ii = 0; ii < m_cell.GetRowSpan() - 1; ++ii )
984 rowHeight -= table.GetRowHeight( m_cell.GetRow() + ii );
985
986 table.SetRowHeight( m_cell.GetRow() + m_cell.GetRowSpan() - 1, rowHeight );
987 }
988
989 table.Normalize();
990 }
991
992private:
994};
995
996
998{
999public:
1001 m_pad( aPad ),
1002 m_layer( aLayer )
1003 {}
1004
1005 void MakePoints( EDIT_POINTS& aPoints ) override
1006 {
1007 VECTOR2I shapePos = m_pad.ShapePos( m_layer );
1008 VECTOR2I halfSize( m_pad.GetSize( m_layer ).x / 2,
1009 m_pad.GetSize( m_layer ).y / 2 );
1010
1011 if( m_pad.IsLocked() )
1012 return;
1013
1014 switch( m_pad.GetShape( m_layer ) )
1015 {
1016 case PAD_SHAPE::CIRCLE:
1017 aPoints.AddPoint( VECTOR2I( shapePos.x + halfSize.x, shapePos.y ) );
1018 break;
1019
1020 case PAD_SHAPE::OVAL:
1021 case PAD_SHAPE::TRAPEZOID:
1022 case PAD_SHAPE::RECTANGLE:
1023 case PAD_SHAPE::ROUNDRECT:
1024 case PAD_SHAPE::CHAMFERED_RECT:
1025 {
1027 break;
1028
1030 std::swap( halfSize.x, halfSize.y );
1031
1032 // It's important to fill these according to the RECT indices
1033 aPoints.AddPoint( shapePos - halfSize );
1034 aPoints.AddPoint( VECTOR2I( shapePos.x + halfSize.x, shapePos.y - halfSize.y ) );
1035 aPoints.AddPoint( shapePos + halfSize );
1036 aPoints.AddPoint( VECTOR2I( shapePos.x - halfSize.x, shapePos.y + halfSize.y ) );
1037 }
1038 break;
1039
1040 default: // suppress warnings
1041 break;
1042 }
1043 }
1044
1045 void UpdatePoints( EDIT_POINTS& aPoints ) override
1046 {
1047 bool locked = m_pad.GetParent() && m_pad.IsLocked();
1048 VECTOR2I shapePos = m_pad.ShapePos( m_layer );
1049 VECTOR2I halfSize( m_pad.GetSize( m_layer ).x / 2,
1050 m_pad.GetSize( m_layer ).y / 2 );
1051
1052 switch( m_pad.GetShape( m_layer ) )
1053 {
1054 case PAD_SHAPE::CIRCLE:
1055 {
1056 int target = locked ? 0 : 1;
1057
1058 // Careful; pad shape is mutable...
1059 if( int( aPoints.PointsSize() ) != target )
1060 {
1061 aPoints.Clear();
1062 MakePoints( aPoints );
1063 }
1064 else if( target == 1 )
1065 {
1066 shapePos.x += halfSize.x;
1067 aPoints.Point( 0 ).SetPosition( shapePos );
1068 }
1069 }
1070 break;
1071
1072 case PAD_SHAPE::OVAL:
1073 case PAD_SHAPE::TRAPEZOID:
1074 case PAD_SHAPE::RECTANGLE:
1075 case PAD_SHAPE::ROUNDRECT:
1076 case PAD_SHAPE::CHAMFERED_RECT:
1077 {
1078 // Careful; pad shape and orientation are mutable...
1079 int target = locked || !m_pad.GetOrientation().IsCardinal() ? 0 : 4;
1080
1081 if( int( aPoints.PointsSize() ) != target )
1082 {
1083 aPoints.Clear();
1084 MakePoints( aPoints );
1085 }
1086 else if( target == 4 )
1087 {
1089 std::swap( halfSize.x, halfSize.y );
1090
1091 aPoints.Point( RECT_TOP_LEFT ).SetPosition( shapePos - halfSize );
1092 aPoints.Point( RECT_TOP_RIGHT )
1093 .SetPosition(
1094 VECTOR2I( shapePos.x + halfSize.x, shapePos.y - halfSize.y ) );
1095 aPoints.Point( RECT_BOT_RIGHT ).SetPosition( shapePos + halfSize );
1096 aPoints.Point( RECT_BOT_LEFT )
1097 .SetPosition(
1098 VECTOR2I( shapePos.x - halfSize.x, shapePos.y + halfSize.y ) );
1099 }
1100
1101 break;
1102 }
1103
1104 default: // suppress warnings
1105 break;
1106 }
1107 }
1108
1109 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1110 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1111 {
1112 switch( m_pad.GetShape( m_layer ) )
1113 {
1114 case PAD_SHAPE::CIRCLE:
1115 {
1116 VECTOR2I end = aPoints.Point( 0 ).GetPosition();
1117 int diameter = 2 * ( end - m_pad.GetPosition() ).EuclideanNorm();
1118
1119 m_pad.SetSize( m_layer, VECTOR2I( diameter, diameter ) );
1120 break;
1121 }
1122
1123 case PAD_SHAPE::OVAL:
1124 case PAD_SHAPE::TRAPEZOID:
1125 case PAD_SHAPE::RECTANGLE:
1126 case PAD_SHAPE::ROUNDRECT:
1127 case PAD_SHAPE::CHAMFERED_RECT:
1128 {
1129 VECTOR2I topLeft = aPoints.Point( RECT_TOP_LEFT ).GetPosition();
1130 VECTOR2I topRight = aPoints.Point( RECT_TOP_RIGHT ).GetPosition();
1131 VECTOR2I botLeft = aPoints.Point( RECT_BOT_LEFT ).GetPosition();
1132 VECTOR2I botRight = aPoints.Point( RECT_BOT_RIGHT ).GetPosition();
1133 VECTOR2I holeCenter = m_pad.GetPosition();
1134 VECTOR2I holeSize = m_pad.GetDrillSize();
1135
1136 RECTANGLE_POINT_EDIT_BEHAVIOR::PinEditedCorner( aEditedPoint, aPoints, topLeft,
1137 topRight, botLeft, botRight, holeCenter,
1138 holeSize );
1139
1140 if( ( m_pad.GetOffset( m_layer ).x
1141 || m_pad.GetOffset( m_layer ).y )
1142 || ( m_pad.GetDrillSize().x && m_pad.GetDrillSize().y ) )
1143 {
1144 // Keep hole pinned at the current location; adjust the pad around the hole
1145
1146 VECTOR2I center = m_pad.GetPosition();
1147 int dist[4];
1148
1149 if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_LEFT ) )
1150 || isModified( aEditedPoint, aPoints.Point( RECT_BOT_RIGHT ) ) )
1151 {
1152 dist[0] = center.x - topLeft.x;
1153 dist[1] = center.y - topLeft.y;
1154 dist[2] = botRight.x - center.x;
1155 dist[3] = botRight.y - center.y;
1156 }
1157 else
1158 {
1159 dist[0] = center.x - botLeft.x;
1160 dist[1] = center.y - topRight.y;
1161 dist[2] = topRight.x - center.x;
1162 dist[3] = botLeft.y - center.y;
1163 }
1164
1165 VECTOR2I padSize( dist[0] + dist[2], dist[1] + dist[3] );
1166 VECTOR2I deltaOffset( padSize.x / 2 - dist[2], padSize.y / 2 - dist[3] );
1167
1169 std::swap( padSize.x, padSize.y );
1170
1171 RotatePoint( deltaOffset, -m_pad.GetOrientation() );
1172
1173 m_pad.SetSize( m_layer, padSize );
1174 m_pad.SetOffset( m_layer, -deltaOffset );
1175 }
1176 else
1177 {
1178 // Keep pad position at the center of the pad shape
1179
1180 int left, top, right, bottom;
1181
1182 if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_LEFT ) )
1183 || isModified( aEditedPoint, aPoints.Point( RECT_BOT_RIGHT ) ) )
1184 {
1185 left = topLeft.x;
1186 top = topLeft.y;
1187 right = botRight.x;
1188 bottom = botRight.y;
1189 }
1190 else
1191 {
1192 left = botLeft.x;
1193 top = topRight.y;
1194 right = topRight.x;
1195 bottom = botLeft.y;
1196 }
1197
1198 VECTOR2I padSize( abs( right - left ), abs( bottom - top ) );
1199
1201 std::swap( padSize.x, padSize.y );
1202
1203 m_pad.SetSize( m_layer, padSize );
1204 m_pad.SetPosition( VECTOR2I( ( left + right ) / 2, ( top + bottom ) / 2 ) );
1205 }
1206 break;
1207 }
1208 default: // suppress warnings
1209 break;
1210 }
1211 }
1212
1213private:
1216};
1217
1218
1225{
1226public:
1228
1229 void MakePoints( EDIT_POINTS& aPoints ) override
1230 {
1231 m_generator.MakeEditPoints( aPoints );
1232 }
1233
1234 void UpdatePoints( EDIT_POINTS& aPoints ) override
1235 {
1236 m_generator.UpdateEditPoints( aPoints );
1237 }
1238
1239 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1240 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1241 {
1243 }
1244
1245private:
1247};
1248
1249
1259{
1260public:
1262 m_dimension( aDimension ), m_originalTextPos( aDimension.GetTextPos() ),
1263 m_oldCrossBar( SEG{ aDimension.GetCrossbarStart(), aDimension.GetCrossbarEnd() } )
1264 {
1265 }
1266
1268 {
1269 const SEG newCrossBar{ m_dimension.GetCrossbarStart(), m_dimension.GetCrossbarEnd() };
1270
1271 if( newCrossBar == m_oldCrossBar )
1272 {
1273 // Crossbar didn't change, text doesn't need to change
1274 return;
1275 }
1276
1277 const VECTOR2I newTextPos = getDimensionNewTextPosition();
1278 m_dimension.SetTextPos( newTextPos );
1279
1280 const GR_TEXT_H_ALIGN_T oldJustify = m_dimension.GetHorizJustify();
1281
1282 // We may need to update the justification if we go past vertical.
1283 if( oldJustify == GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_LEFT
1284 || oldJustify == GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_RIGHT )
1285 {
1287 const VECTOR2I newProject = newCrossBar.LineProject( newTextPos );
1288
1289 const VECTOR2I oldProjectedOffset =
1290 oldProject - m_oldCrossBar.NearestPoint( oldProject );
1291 const VECTOR2I newProjectedOffset = newProject - newCrossBar.NearestPoint( newProject );
1292
1293 const bool textWasLeftOf = oldProjectedOffset.x < 0
1294 || ( oldProjectedOffset.x == 0 && oldProjectedOffset.y > 0 );
1295 const bool textIsLeftOf = newProjectedOffset.x < 0
1296 || ( newProjectedOffset.x == 0 && newProjectedOffset.y > 0 );
1297
1298 if( textWasLeftOf != textIsLeftOf )
1299 {
1300 // Flip whatever the user had set
1302 ( oldJustify == GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_LEFT )
1303 ? GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_RIGHT
1304 : GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_LEFT );
1305 }
1306 }
1307
1308 // Update the dimension (again) to ensure the text knockouts are correct
1310 }
1311
1312private:
1314 {
1315 const SEG newCrossBar{ m_dimension.GetCrossbarStart(), m_dimension.GetCrossbarEnd() };
1316
1317 const EDA_ANGLE oldAngle = EDA_ANGLE( m_oldCrossBar.B - m_oldCrossBar.A );
1318 const EDA_ANGLE newAngle = EDA_ANGLE( newCrossBar.B - newCrossBar.A );
1319 const EDA_ANGLE rotation = oldAngle - newAngle;
1320
1321 // There are two modes - when the text is between the crossbar points, and when it's not.
1323 {
1324 const VECTOR2I cbNearestEndToText =
1326 const VECTOR2I rotTextOffsetFromCbCenter =
1328 const VECTOR2I rotTextOffsetFromCbEnd =
1329 GetRotated( m_originalTextPos - cbNearestEndToText, rotation );
1330
1331 // Which of the two crossbar points is now in the right direction? They could be swapped over now.
1332 // If zero-length, doesn't matter, they're the same thing
1333 const bool startIsInOffsetDirection =
1335 rotTextOffsetFromCbCenter, newCrossBar.Center() );
1336
1337 const VECTOR2I& newCbRefPt = startIsInOffsetDirection ? m_dimension.GetCrossbarStart()
1339
1340 // Apply the new offset to the correct crossbar point
1341 return newCbRefPt + rotTextOffsetFromCbEnd;
1342 }
1343
1344 // If the text was between the crossbar points, it should stay there, but we need to find a
1345 // good place for it. Keep it the same distance from the crossbar line, but rotated as needed.
1346
1347 const VECTOR2I origTextPointProjected = m_oldCrossBar.NearestPoint( m_originalTextPos );
1348 const double oldRatio =
1349 KIGEOM::GetLengthRatioFromStart( origTextPointProjected, m_oldCrossBar );
1350
1351 // Perpendicular from the crossbar line to the text position
1352 // We need to keep this length constant
1353 const VECTOR2I rotCbNormalToText =
1354 GetRotated( m_originalTextPos - origTextPointProjected, rotation );
1355
1356 const VECTOR2I newProjected = newCrossBar.A + ( newCrossBar.B - newCrossBar.A ) * oldRatio;
1357 return newProjected + rotCbNormalToText;
1358 }
1359
1363};
1364
1365
1370{
1371public:
1373 {
1374 }
1375
1376 void MakePoints( EDIT_POINTS& aPoints ) override
1377 {
1378 aPoints.AddPoint( m_dimension.GetStart() );
1379 aPoints.AddPoint( m_dimension.GetEnd() );
1380 aPoints.AddPoint( m_dimension.GetTextPos() );
1382 aPoints.AddPoint( m_dimension.GetCrossbarEnd() );
1383
1386
1388 {
1389 // Dimension height setting - edit points should move only along the feature lines
1390 aPoints.Point( DIM_CROSSBARSTART )
1391 .SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBARSTART ),
1392 aPoints.Point( DIM_START ) ) );
1393 aPoints.Point( DIM_CROSSBAREND )
1394 .SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBAREND ),
1395 aPoints.Point( DIM_END ) ) );
1396 }
1397 }
1398
1399 void UpdatePoints( EDIT_POINTS& aPoints ) override
1400 {
1402
1404 aPoints.Point( DIM_END ).SetPosition( m_dimension.GetEnd() );
1408 }
1409
1410 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1411 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1412 {
1414
1416 updateAlignedDimension( aEditedPoint, aPoints );
1417 else
1418 updateOrthogonalDimension( aEditedPoint, aPoints );
1419 }
1420
1422 EDIT_POINTS& aPoints ) const override
1423 {
1424 // Constraint for crossbar
1425 if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1426 return aPoints.Point( DIM_END ).GetPosition();
1427
1428 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1429 return aPoints.Point( DIM_START ).GetPosition();
1430
1431 // No constraint
1432 return aEditedPoint.GetPosition();
1433 }
1434
1435private:
1439 void updateAlignedDimension( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints )
1440 {
1441 DIM_ALIGNED_TEXT_UPDATER textPositionUpdater( m_dimension );
1442
1443 // Check which point is currently modified and updated dimension's points respectively
1444 if( isModified( aEditedPoint, aPoints.Point( DIM_CROSSBARSTART ) ) )
1445 {
1446 VECTOR2D featureLine( aEditedPoint.GetPosition() - m_dimension.GetStart() );
1448
1449 if( featureLine.Cross( crossBar ) > 0 )
1450 m_dimension.SetHeight( -featureLine.EuclideanNorm() );
1451 else
1452 m_dimension.SetHeight( featureLine.EuclideanNorm() );
1453
1455 }
1456 else if( isModified( aEditedPoint, aPoints.Point( DIM_CROSSBAREND ) ) )
1457 {
1458 VECTOR2D featureLine( aEditedPoint.GetPosition() - m_dimension.GetEnd() );
1460
1461 if( featureLine.Cross( crossBar ) > 0 )
1462 m_dimension.SetHeight( -featureLine.EuclideanNorm() );
1463 else
1464 m_dimension.SetHeight( featureLine.EuclideanNorm() );
1465
1467 }
1468 else if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1469 {
1470 m_dimension.SetStart( aEditedPoint.GetPosition() );
1472
1473 aPoints.Point( DIM_CROSSBARSTART )
1474 .SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBARSTART ),
1475 aPoints.Point( DIM_START ) ) );
1476 aPoints.Point( DIM_CROSSBAREND )
1477 .SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBAREND ),
1478 aPoints.Point( DIM_END ) ) );
1479 }
1480 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1481 {
1482 m_dimension.SetEnd( aEditedPoint.GetPosition() );
1484
1485 aPoints.Point( DIM_CROSSBARSTART )
1486 .SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBARSTART ),
1487 aPoints.Point( DIM_START ) ) );
1488 aPoints.Point( DIM_CROSSBAREND )
1489 .SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBAREND ),
1490 aPoints.Point( DIM_END ) ) );
1491 }
1492 else if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1493 {
1494 // Force manual mode if we weren't already in it
1495 m_dimension.SetTextPositionMode( DIM_TEXT_POSITION::MANUAL );
1496 m_dimension.SetTextPos( aEditedPoint.GetPosition() );
1498 }
1499
1500 textPositionUpdater.UpdateTextAfterChange();
1501 }
1502
1506 void updateOrthogonalDimension( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints )
1507 {
1508 DIM_ALIGNED_TEXT_UPDATER textPositionUpdater( m_dimension );
1509 PCB_DIM_ORTHOGONAL& orthDimension = static_cast<PCB_DIM_ORTHOGONAL&>( m_dimension );
1510
1511 if( isModified( aEditedPoint, aPoints.Point( DIM_CROSSBARSTART ) )
1512 || isModified( aEditedPoint, aPoints.Point( DIM_CROSSBAREND ) ) )
1513 {
1515
1516 const VECTOR2I& cursorPos = aEditedPoint.GetPosition();
1517
1518 // Find vector from nearest dimension point to edit position
1519 VECTOR2I directionA( cursorPos - m_dimension.GetStart() );
1520 VECTOR2I directionB( cursorPos - m_dimension.GetEnd() );
1521 VECTOR2I direction = ( directionA < directionB ) ? directionA : directionB;
1522
1523 bool vert;
1524 VECTOR2D featureLine( cursorPos - m_dimension.GetStart() );
1525
1526 // Only change the orientation when we move outside the bounds
1527 if( !bounds.Contains( cursorPos ) )
1528 {
1529 // If the dimension is horizontal or vertical, set correct orientation
1530 // otherwise, test if we're left/right of the bounding box or above/below it
1531 if( bounds.GetWidth() == 0 )
1532 vert = true;
1533 else if( bounds.GetHeight() == 0 )
1534 vert = false;
1535 else if( cursorPos.x > bounds.GetLeft() && cursorPos.x < bounds.GetRight() )
1536 vert = false;
1537 else if( cursorPos.y > bounds.GetTop() && cursorPos.y < bounds.GetBottom() )
1538 vert = true;
1539 else
1540 vert = std::abs( direction.y ) < std::abs( direction.x );
1541
1544 }
1545 else
1546 {
1547 vert = orthDimension.GetOrientation() == PCB_DIM_ORTHOGONAL::DIR::VERTICAL;
1548 }
1549
1550 m_dimension.SetHeight( vert ? featureLine.x : featureLine.y );
1551 }
1552 else if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1553 {
1554 m_dimension.SetStart( aEditedPoint.GetPosition() );
1555 }
1556 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1557 {
1558 m_dimension.SetEnd( aEditedPoint.GetPosition() );
1559 }
1560 else if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1561 {
1562 // Force manual mode if we weren't already in it
1563 m_dimension.SetTextPositionMode( DIM_TEXT_POSITION::MANUAL );
1564 m_dimension.SetTextPos( VECTOR2I( aEditedPoint.GetPosition() ) );
1565 }
1566
1568
1569 // After recompute, find the new text position
1570 textPositionUpdater.UpdateTextAfterChange();
1571 }
1572
1574};
1575
1576
1578{
1579public:
1581
1582 void MakePoints( EDIT_POINTS& aPoints ) override
1583 {
1584 aPoints.AddPoint( m_dimension.GetStart() );
1585 aPoints.AddPoint( m_dimension.GetEnd() );
1586
1588
1589 aPoints.Point( DIM_END ).SetConstraint(
1590 new EC_45DEGREE( aPoints.Point( DIM_END ), aPoints.Point( DIM_START ) ) );
1592 }
1593
1594 void UpdatePoints( EDIT_POINTS& aPoints ) override
1595 {
1597
1599 aPoints.Point( DIM_END ).SetPosition( m_dimension.GetEnd() );
1600 }
1601
1602 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1603 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1604 {
1606
1607 if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1608 m_dimension.SetStart( aEditedPoint.GetPosition() );
1609 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1610 m_dimension.SetEnd( aEditedPoint.GetPosition() );
1611
1613 }
1614
1616 EDIT_POINTS& aPoints ) const override
1617 {
1618 if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1619 return aPoints.Point( DIM_START ).GetPosition();
1620
1621 return std::nullopt;
1622 }
1623
1624private:
1626};
1627
1628
1630{
1631public:
1633
1634 void MakePoints( EDIT_POINTS& aPoints ) override
1635 {
1636 aPoints.AddPoint( m_dimension.GetStart() );
1637 aPoints.AddPoint( m_dimension.GetEnd() );
1638 aPoints.AddPoint( m_dimension.GetTextPos() );
1639 aPoints.AddPoint( m_dimension.GetKnee() );
1640
1643
1644 aPoints.Point( DIM_KNEE )
1646 new EC_LINE( aPoints.Point( DIM_START ), aPoints.Point( DIM_END ) ) );
1648
1649 aPoints.Point( DIM_TEXT )
1651 new EC_45DEGREE( aPoints.Point( DIM_TEXT ), aPoints.Point( DIM_KNEE ) ) );
1653 }
1654
1655 void UpdatePoints( EDIT_POINTS& aPoints ) override
1656 {
1658
1660 aPoints.Point( DIM_END ).SetPosition( m_dimension.GetEnd() );
1663 }
1664
1665 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1666 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1667 {
1669
1670 if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1671 {
1672 m_dimension.SetStart( aEditedPoint.GetPosition() );
1674
1675 aPoints.Point( DIM_KNEE )
1677 new EC_LINE( aPoints.Point( DIM_START ), aPoints.Point( DIM_END ) ) );
1678 }
1679 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1680 {
1681 VECTOR2I oldKnee = m_dimension.GetKnee();
1682
1683 m_dimension.SetEnd( aEditedPoint.GetPosition() );
1685
1686 VECTOR2I kneeDelta = m_dimension.GetKnee() - oldKnee;
1689
1690 aPoints.Point( DIM_KNEE )
1692 new EC_LINE( aPoints.Point( DIM_START ), aPoints.Point( DIM_END ) ) );
1693 }
1694 else if( isModified( aEditedPoint, aPoints.Point( DIM_KNEE ) ) )
1695 {
1696 VECTOR2I oldKnee = m_dimension.GetKnee();
1697 VECTOR2I arrowVec = aPoints.Point( DIM_KNEE ).GetPosition()
1698 - aPoints.Point( DIM_END ).GetPosition();
1699
1702
1703 VECTOR2I kneeDelta = m_dimension.GetKnee() - oldKnee;
1706 }
1707 else if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1708 {
1709 m_dimension.SetTextPos( aEditedPoint.GetPosition() );
1711 }
1712 }
1713
1715 EDIT_POINTS& aPoints ) const override
1716 {
1717 if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1718 return aPoints.Point( DIM_KNEE ).GetPosition();
1719
1720 return std::nullopt;
1721 }
1722
1723private:
1725};
1726
1727
1729{
1730public:
1732
1733 void MakePoints( EDIT_POINTS& aPoints ) override
1734 {
1735 aPoints.AddPoint( m_dimension.GetStart() );
1736 aPoints.AddPoint( m_dimension.GetEnd() );
1737 aPoints.AddPoint( m_dimension.GetTextPos() );
1738
1741
1742 aPoints.Point( DIM_TEXT )
1744 new EC_45DEGREE( aPoints.Point( DIM_TEXT ), aPoints.Point( DIM_END ) ) );
1746 }
1747
1748 void UpdatePoints( EDIT_POINTS& aPoints ) override
1749 {
1751
1753 aPoints.Point( DIM_END ).SetPosition( m_dimension.GetEnd() );
1755 }
1756
1757 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1758 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1759 {
1761
1762 if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1763 {
1764 m_dimension.SetStart( aEditedPoint.GetPosition() );
1765 }
1766 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1767 {
1768 const VECTOR2I newPoint( aEditedPoint.GetPosition() );
1769 const VECTOR2I delta = newPoint - m_dimension.GetEnd();
1770
1771 m_dimension.SetEnd( newPoint );
1773 }
1774 else if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1775 {
1776 m_dimension.SetTextPos( aEditedPoint.GetPosition() );
1777 }
1778
1780 }
1781
1782private:
1784};
1785
1786
1791{
1792public:
1794
1795 void MakePoints( EDIT_POINTS& aPoints ) override
1796 {
1797 if( m_textbox.GetShape() == SHAPE_T::RECTANGLE )
1798 {
1800 }
1801 else
1802 {
1803 // Rotated textboxes are implemented as polygons and these
1804 // aren't currently editable.
1805 }
1806 }
1807
1808 void UpdatePoints( EDIT_POINTS& aPoints ) override
1809 {
1810 // When textboxes are rotated, they act as polygons, not rectangles
1811 const unsigned target = m_textbox.GetShape() == SHAPE_T::RECTANGLE
1812 ? TEXTBOX_POINT_COUNT::WHEN_RECTANGLE
1813 : TEXTBOX_POINT_COUNT::WHEN_POLYGON;
1814
1815 // Careful; textbox shape is mutable between cardinal and non-cardinal rotations...
1816 if( aPoints.PointsSize() != target )
1817 {
1818 aPoints.Clear();
1819 MakePoints( aPoints );
1820 return;
1821 }
1822
1823 if( m_textbox.GetShape() == SHAPE_T::RECTANGLE )
1824 {
1825 // Dispatch to the rectangle behavior
1827 }
1828 else if( m_textbox.GetShape() == SHAPE_T::POLY )
1829 {
1830 // Not currently editable while rotated.
1831 }
1832 }
1833
1834 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1835 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1836 {
1837 if( m_textbox.GetShape() == SHAPE_T::RECTANGLE )
1838 {
1839 RECTANGLE_POINT_EDIT_BEHAVIOR::UpdateItem( m_textbox, aEditedPoint, aPoints );
1840 }
1841 }
1842
1843private:
1845};
1846
1848 PCB_TOOL_BASE( "pcbnew.PointEditor" ),
1849 m_frame( nullptr ),
1850 m_selectionTool( nullptr ), m_editedPoint( nullptr ),
1851 m_hoveredPoint( nullptr ), m_original( VECTOR2I( 0, 0 ) ),
1853 m_altConstrainer( VECTOR2I( 0, 0 ) ), m_inPointEditorTool( false )
1854{
1855}
1856
1857
1859{
1860 m_frame = getEditFrame<PCB_BASE_FRAME>();
1861 m_editPoints.reset();
1862 m_altConstraint.reset();
1863 getViewControls()->SetAutoPan( false );
1864}
1865
1866
1868{
1869 // Find the selection tool, so they can cooperate
1871
1872 wxASSERT_MSG( m_selectionTool, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
1873
1874 auto& menu = m_selectionTool->GetToolMenu().GetMenu();
1877 std::bind( &PCB_POINT_EDITOR::removeCornerCondition, this, _1 ) );
1879
1880 return true;
1881}
1882
1883
1884std::shared_ptr<EDIT_POINTS> PCB_POINT_EDITOR::makePoints( EDA_ITEM* aItem )
1885{
1886 std::shared_ptr<EDIT_POINTS> points = std::make_shared<EDIT_POINTS>( aItem );
1887
1888 if( !aItem )
1889 return points;
1890
1891 // Reset the behaviour and we'll make a new one
1892 m_editorBehavior = nullptr;
1893
1894 switch( aItem->Type() )
1895 {
1897 {
1898 PCB_REFERENCE_IMAGE& refImage = static_cast<PCB_REFERENCE_IMAGE&>( *aItem );
1899 m_editorBehavior = std::make_unique<REFERENCE_IMAGE_POINT_EDIT_BEHAVIOR>( refImage );
1900 break;
1901 }
1902 case PCB_TEXTBOX_T:
1903 {
1904 PCB_TEXTBOX& textbox = static_cast<PCB_TEXTBOX&>( *aItem );
1905 m_editorBehavior = std::make_unique<TEXTBOX_POINT_EDIT_BEHAVIOR>( textbox );
1906 break;
1907 }
1908 case PCB_SHAPE_T:
1909 {
1910 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
1911
1912 switch( shape->GetShape() )
1913 {
1914 case SHAPE_T::SEGMENT:
1915 m_editorBehavior = std::make_unique<EDA_SEGMENT_POINT_EDIT_BEHAVIOR>( *shape );
1916 break;
1917 case SHAPE_T::RECTANGLE:
1918 m_editorBehavior = std::make_unique<RECTANGLE_POINT_EDIT_BEHAVIOR>( *shape );
1919 break;
1920 case SHAPE_T::ARC:
1921 m_editorBehavior = std::make_unique<ARC_POINT_EDIT_BEHAVIOR>( *shape, m_arcEditMode,
1922 *getViewControls() );
1923 break;
1924
1925 case SHAPE_T::CIRCLE:
1926 m_editorBehavior = std::make_unique<EDA_CIRCLE_POINT_EDIT_BEHAVIOR>( *shape );
1927 break;
1928
1929 case SHAPE_T::POLY:
1930 m_editorBehavior = std::make_unique<EDA_POLYGON_POINT_EDIT_BEHAVIOR>( *shape );
1931 break;
1932
1933 case SHAPE_T::BEZIER:
1934 m_editorBehavior = std::make_unique<EDA_BEZIER_POINT_EDIT_BEHAVIOR>( *shape );
1935 break;
1936
1937 default: // suppress warnings
1938 break;
1939 }
1940
1941 break;
1942 }
1943
1944 case PCB_TABLECELL_T:
1945 {
1946 PCB_TABLECELL* cell = static_cast<PCB_TABLECELL*>( aItem );
1947 m_editorBehavior = std::make_unique<PCB_TABLECELL_POINT_EDIT_BEHAVIOR>( *cell );
1948 break;
1949 }
1950
1951 case PCB_PAD_T:
1952 {
1953 // Pad edit only for the footprint editor
1955 {
1956 PAD& pad = static_cast<PAD&>( *aItem );
1958
1959 // Point editor only handles copper shape changes
1960 if( !IsCopperLayer( activeLayer ) )
1961 activeLayer = IsFrontLayer( activeLayer ) ? F_Cu : B_Cu;
1962
1963 m_editorBehavior = std::make_unique<PAD_POINT_EDIT_BEHAVIOR>( pad, activeLayer );
1964 }
1965 break;
1966 }
1967
1968 case PCB_ZONE_T:
1969 {
1970 ZONE& zone = static_cast<ZONE&>( *aItem );
1971 m_editorBehavior = std::make_unique<ZONE_POINT_EDIT_BEHAVIOR>( zone );
1972 break;
1973 }
1974
1975 case PCB_GENERATOR_T:
1976 {
1977 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( aItem );
1978 m_editorBehavior = std::make_unique<GENERATOR_POINT_EDIT_BEHAVIOR>( *generator );
1979 break;
1980 }
1981
1982 case PCB_DIM_ALIGNED_T:
1984 {
1985 PCB_DIM_ALIGNED& dimension = static_cast<PCB_DIM_ALIGNED&>( *aItem );
1986 m_editorBehavior = std::make_unique<ALIGNED_DIMENSION_POINT_EDIT_BEHAVIOR>( dimension );
1987 break;
1988 }
1989
1990 case PCB_DIM_CENTER_T:
1991 {
1992 PCB_DIM_CENTER& dimension = static_cast<PCB_DIM_CENTER&>( *aItem );
1993 m_editorBehavior = std::make_unique<DIM_CENTER_POINT_EDIT_BEHAVIOR>( dimension );
1994 break;
1995 }
1996
1997 case PCB_DIM_RADIAL_T:
1998 {
1999 PCB_DIM_RADIAL& dimension = static_cast<PCB_DIM_RADIAL&>( *aItem );
2000 m_editorBehavior = std::make_unique<DIM_RADIAL_POINT_EDIT_BEHAVIOR>( dimension );
2001 break;
2002 }
2003
2004 case PCB_DIM_LEADER_T:
2005 {
2006 PCB_DIM_LEADER& dimension = static_cast<PCB_DIM_LEADER&>( *aItem );
2007 m_editorBehavior = std::make_unique<DIM_LEADER_POINT_EDIT_BEHAVIOR>( dimension );
2008 break;
2009 }
2010
2011 default:
2012 points.reset();
2013 break;
2014 }
2015
2016 if( m_editorBehavior )
2017 m_editorBehavior->MakePoints( *points );
2018
2019 return points;
2020}
2021
2022
2024{
2025 EDIT_POINT* point;
2026 EDIT_POINT* hovered = nullptr;
2027
2028 if( aEvent.IsMotion() )
2029 {
2030 point = m_editPoints->FindPoint( aEvent.Position(), getView() );
2031 hovered = point;
2032 }
2033 else if( aEvent.IsDrag( BUT_LEFT ) )
2034 {
2035 point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
2036 }
2037 else
2038 {
2039 point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
2040 }
2041
2042 if( hovered )
2043 {
2044 if( m_hoveredPoint != hovered )
2045 {
2046 if( m_hoveredPoint )
2047 m_hoveredPoint->SetHover( false );
2048
2049 m_hoveredPoint = hovered;
2051 }
2052 }
2053 else if( m_hoveredPoint )
2054 {
2055 m_hoveredPoint->SetHover( false );
2056 m_hoveredPoint = nullptr;
2057 }
2058
2059 if( m_editedPoint != point )
2060 setEditedPoint( point );
2061}
2062
2063
2065{
2067 return 0;
2068
2070 return 0;
2071
2073
2074 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2076
2077 if( selection.Size() != 1 || selection.Front()->GetEditFlags() )
2078 return 0;
2079
2080 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
2081
2082 if( !item || item->IsLocked() )
2083 return 0;
2084
2085 Activate();
2086 // Must be done after Activate() so that it gets set into the correct context
2087 getViewControls()->ShowCursor( true );
2088
2090
2091 // Use the original object as a construction item
2092 std::vector<std::unique_ptr<BOARD_ITEM>> clones;
2093
2094 m_editorBehavior.reset();
2095 // Will also make the edit behavior if supported
2096 m_editPoints = makePoints( item );
2097
2098 if( !m_editPoints )
2099 return 0;
2100
2102 getView()->Add( &m_preview );
2103
2104 getView()->Add( m_editPoints.get() );
2105 setEditedPoint( nullptr );
2106 updateEditedPoint( aEvent );
2107 bool inDrag = false;
2108 bool useAltContraint = false;
2109
2110 BOARD_COMMIT commit( editFrame );
2111
2112 // Main loop: keep receiving events
2113 while( TOOL_EVENT* evt = Wait() )
2114 {
2115 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2116 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2117
2118 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
2119 {
2120 useAltContraint = editFrame->GetPcbNewSettings()->m_Use45DegreeLimit;
2122 }
2123 else
2124 {
2125 useAltContraint = editFrame->GetFootprintEditorSettings()->m_Use45Limit;
2127 }
2128
2129 if( !m_editPoints || evt->IsSelectionEvent() ||
2130 evt->Matches( EVENTS::InhibitSelectionEditing ) )
2131 {
2132 break;
2133 }
2134
2135 EDIT_POINT* prevHover = m_hoveredPoint;
2136
2137 if( !inDrag )
2138 updateEditedPoint( *evt );
2139
2140 if( prevHover != m_hoveredPoint )
2141 getView()->Update( m_editPoints.get() );
2142
2143 if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
2144 {
2145 if( !inDrag )
2146 {
2147 frame()->UndoRedoBlock( true );
2148
2149 if( item->Type() == PCB_GENERATOR_T )
2150 {
2152 static_cast<PCB_GENERATOR*>( item ) );
2153 }
2154
2156 m_original = *m_editedPoint; // Save the original position
2157 getViewControls()->SetAutoPan( true );
2158 inDrag = true;
2159
2161 grid.SetAuxAxes( true, m_original.GetPosition() );
2162
2163 setAltConstraint( true );
2165
2166 for( size_t ii = 0; ii < m_editPoints->PointsSize(); ++ii )
2167 {
2168 EDIT_POINT& point = m_editPoints->Point( ii );
2169
2170 if( &point != m_editedPoint )
2171 point.SetActive( false );
2172 }
2173
2174 // When we start dragging, create a clone of the item to use as the original
2175 // reference geometry (e.g. for intersections and extensions)
2176 BOARD_ITEM* clone = static_cast<BOARD_ITEM*>( item->Clone() );
2177 clone->SetParent( nullptr );
2178 clone->SetParentGroup( nullptr );
2179 clones.emplace_back( clone );
2180 grid.AddConstructionItems( { clone }, false, true );
2181 }
2182
2183 // Keep point inside of limits with some padding
2184 VECTOR2I pos = GetClampedCoords<double, int>( evt->Position(), COORDS_PADDING );
2185 LSET snapLayers;
2186
2187 switch( m_editedPoint->GetSnapConstraint() )
2188 {
2189 case IGNORE_SNAPS: break;
2190 case OBJECT_LAYERS: snapLayers = item->GetLayerSet(); break;
2191 case ALL_LAYERS: snapLayers = LSET::AllLayersMask(); break;
2192 }
2193
2195 {
2196 if( grid.GetUseGrid() )
2197 {
2198 VECTOR2I gridPt = grid.BestSnapAnchor( pos, {}, grid.GetItemGrid( item ),
2199 { item } );
2200
2202 VECTOR2I delta = pos - last;
2203 VECTOR2I deltaGrid = gridPt - grid.BestSnapAnchor( last, {},
2204 grid.GetItemGrid( item ),
2205 { item } );
2206
2207 if( abs( delta.x ) > grid.GetGrid().x / 2 )
2208 pos.x = last.x + deltaGrid.x;
2209 else
2210 pos.x = last.x;
2211
2212 if( abs( delta.y ) > grid.GetGrid().y / 2 )
2213 pos.y = last.y + deltaGrid.y;
2214 else
2215 pos.y = last.y;
2216 }
2217 }
2218
2219 m_editedPoint->SetPosition( pos );
2220
2221 // The alternative constraint limits to 45 degrees
2222 if( useAltContraint )
2223 {
2224 m_altConstraint->Apply( grid );
2225 }
2226 else if( m_editedPoint->IsConstrained() )
2227 {
2229 }
2231 {
2233 snapLayers,
2234 grid.GetItemGrid( item ),
2235 { item } ) );
2236 }
2237
2238 updateItem( commit );
2240 updatePoints();
2241 }
2242 else if( m_editedPoint && evt->Action() == TA_MOUSE_DOWN && evt->Buttons() == BUT_LEFT )
2243 {
2245
2246 for( size_t ii = 0; ii < m_editPoints->PointsSize(); ++ii )
2247 {
2248 EDIT_POINT& point = m_editPoints->Point( ii );
2249
2250 if( &point != m_editedPoint )
2251 point.SetActive( false );
2252 }
2253
2254 getView()->Update( m_editPoints.get() );
2255 }
2256 else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
2257 {
2258 if( m_editedPoint )
2259 {
2260 m_editedPoint->SetActive( false );
2261 getView()->Update( m_editPoints.get() );
2262 }
2263
2264 getViewControls()->SetAutoPan( false );
2265 setAltConstraint( false );
2266
2267 if( item->Type() == PCB_GENERATOR_T )
2268 {
2271 static_cast<PCB_GENERATOR*>( item ) );
2272 }
2273 else if( item->Type() == PCB_TABLECELL_T )
2274 {
2275 commit.Push( _( "Resize Table Cells" ) );
2276 }
2277 else
2278 {
2279 commit.Push( _( "Move Point" ) );
2280 }
2281
2282 inDrag = false;
2283 frame()->UndoRedoBlock( false );
2284
2286 item ); // FIXME: Needed for generators
2287 }
2288 else if( evt->IsCancelInteractive() || evt->IsActivate() )
2289 {
2290 if( inDrag ) // Restore the last change
2291 {
2292 if( item->Type() == PCB_GENERATOR_T )
2293 {
2295 static_cast<PCB_GENERATOR*>( item ) );
2296 }
2297 commit.Revert();
2298
2299 inDrag = false;
2300 frame()->UndoRedoBlock( false );
2301 }
2302
2303 // Only cancel point editor when activating a new tool
2304 // Otherwise, allow the points to persist when moving up the
2305 // tool stack
2306 if( evt->IsActivate() && !evt->IsMoveTool() )
2307 break;
2308 }
2309 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2310 {
2311 // Re-create the points for items which can have different behavior on different layers
2312 if( item->Type() == PCB_PAD_T && m_isFootprintEditor )
2313 {
2314 getView()->Remove( m_editPoints.get() );
2315 m_editPoints = makePoints( item );
2316 getView()->Add( m_editPoints.get() );
2317 }
2318 }
2319 else if( evt->Action() == TA_UNDO_REDO_POST )
2320 {
2321 break;
2322 }
2323 else
2324 {
2325 evt->SetPassEvent();
2326 }
2327 }
2328
2330 getView()->Remove( &m_preview );
2331
2332 if( m_editPoints )
2333 {
2334 getView()->Remove( m_editPoints.get() );
2335 m_editPoints.reset();
2336 }
2337
2338 m_editedPoint = nullptr;
2339
2340 return 0;
2341}
2342
2343
2345{
2346 if( !m_editPoints || !m_editPoints->GetParent() || !HasPoint() )
2347 return 0;
2348
2349 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2350
2351 BOARD_COMMIT commit( editFrame );
2352 commit.Stage( m_editPoints->GetParent(), CHT_MODIFY );
2353
2355 wxString title;
2356 wxString msg;
2357
2358 if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
2359 {
2360 title = _( "Move Midpoint to Location" );
2361 msg = _( "Move Midpoint" );
2362 }
2363 else
2364 {
2365 title = _( "Move Corner to Location" );
2366 msg = _( "Move Corner" );
2367 }
2368
2369 WX_PT_ENTRY_DIALOG dlg( editFrame, title, _( "X:" ), _( "Y:" ), pt );
2370
2371 if( dlg.ShowModal() == wxID_OK )
2372 {
2373 pt = editFrame->GetOriginTransforms().FromDisplayAbs( dlg.GetValue() );
2375 updateItem( commit );
2376 commit.Push( msg );
2377 }
2378
2379 return 0;
2380}
2381
2382
2384{
2385 wxCHECK( m_editPoints, /* void */ );
2386 EDA_ITEM* item = m_editPoints->GetParent();
2387
2388 if( !item )
2389 return;
2390
2391 // item is always updated
2392 std::vector<EDA_ITEM*> updatedItems = { item };
2393 aCommit.Modify( item );
2394
2395 if( m_editorBehavior )
2396 {
2397 wxCHECK( m_editedPoint, /* void */ );
2398 m_editorBehavior->UpdateItem( *m_editedPoint, *m_editPoints, aCommit, updatedItems );
2399 }
2400
2401 // Perform any post-edit actions that the item may require
2402
2403 switch( item->Type() )
2404 {
2405 case PCB_TEXTBOX_T:
2406 case PCB_SHAPE_T:
2407 {
2408 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2409
2410 if( shape->IsProxyItem() )
2411 {
2412 for( PAD* pad : shape->GetParentFootprint()->Pads() )
2413 {
2414 if( pad->IsEntered() )
2415 view()->Update( pad );
2416 }
2417 }
2418
2419 // Nuke outline font render caches
2420 if( PCB_TEXTBOX* textBox = dynamic_cast<PCB_TEXTBOX*>( item ) )
2421 textBox->ClearRenderCache();
2422
2423 break;
2424 }
2425 case PCB_GENERATOR_T:
2426 {
2427 GENERATOR_TOOL* generatorTool = m_toolMgr->GetTool<GENERATOR_TOOL>();
2428 PCB_GENERATOR* generatorItem = static_cast<PCB_GENERATOR*>( item );
2429
2430 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genUpdateEdit, &aCommit, generatorItem );
2431
2432 // Note: POINT_EDITOR::m_preview holds only the canvas-draw status "popup"; the meanders
2433 // themselves (ROUTER_PREVIEW_ITEMs) are owned by the router.
2434
2436
2437 for( EDA_ITEM* previewItem : generatorItem->GetPreviewItems( generatorTool, frame(),
2439 {
2440 m_preview.Add( previewItem );
2441 }
2442
2443 getView()->Update( &m_preview );
2444 break;
2445 }
2446 default:
2447 break;
2448 }
2449
2450 // Update the item and any affected items
2451 for( EDA_ITEM* updatedItem : updatedItems )
2452 {
2453 getView()->Update( updatedItem );
2454 }
2455
2456 frame()->SetMsgPanel( item );
2457}
2458
2459
2461{
2462 if( !m_editPoints )
2463 return;
2464
2465 EDA_ITEM* item = m_editPoints->GetParent();
2466
2467 if( !item )
2468 return;
2469
2470 if( !m_editorBehavior )
2471 return;
2472
2473 m_editorBehavior->UpdatePoints( *m_editPoints );
2474 getView()->Update( m_editPoints.get() );
2475}
2476
2477
2479{
2481
2482 if( aPoint )
2483 {
2484 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2485 controls->ForceCursorPosition( true, aPoint->GetPosition() );
2486 controls->ShowCursor( true );
2487 }
2488 else
2489 {
2490 if( frame()->ToolStackIsEmpty() )
2491 controls->ShowCursor( false );
2492
2493 controls->ForceCursorPosition( false );
2494 }
2495
2496 m_editedPoint = aPoint;
2497}
2498
2499
2501{
2502 if( aEnabled )
2503 {
2504 EDA_ITEM* parent = m_editPoints->GetParent();
2505 EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
2506 bool isPoly;
2507
2508 switch( parent->Type() )
2509 {
2510 case PCB_ZONE_T:
2511 isPoly = true;
2512 break;
2513
2514 case PCB_SHAPE_T:
2515 isPoly = static_cast<PCB_SHAPE*>( parent )->GetShape() == SHAPE_T::POLY;
2516 break;
2517
2518 default:
2519 isPoly = false;
2520 break;
2521 }
2522
2523 if( line && isPoly )
2524 {
2525 EC_CONVERGING* altConstraint = new EC_CONVERGING( *line, *m_editPoints );
2526 m_altConstraint.reset( (EDIT_CONSTRAINT<EDIT_POINT>*) altConstraint );
2527 }
2528 else
2529 {
2530 // Find a proper constraining point for 45 degrees mode
2533 }
2534 }
2535 else
2536 {
2537 m_altConstraint.reset();
2538 }
2539}
2540
2541
2543{
2544 // If there's a behaviour and it provides a constrainer, use that
2545 if( m_editorBehavior )
2546 {
2547 const OPT_VECTOR2I constrainer =
2548 m_editorBehavior->Get45DegreeConstrainer( *m_editedPoint, *m_editPoints );
2549 if( constrainer )
2550 return EDIT_POINT( *constrainer );
2551 }
2552
2553 // In any other case we may align item to its original position
2554 return m_original;
2555}
2556
2557
2559{
2560 const auto type = aItem.Type();
2561
2562 // Works only for zones and line segments
2563 if( type == PCB_ZONE_T )
2564 return true;
2565
2566 if( type == PCB_SHAPE_T )
2567 {
2568 const PCB_SHAPE& shape = static_cast<const PCB_SHAPE&>( aItem );
2569 const SHAPE_T shapeType = shape.GetShape();
2570 return shapeType == SHAPE_T::SEGMENT || shapeType == SHAPE_T::POLY
2571 || shapeType == SHAPE_T::ARC;
2572 }
2573
2574 return false;
2575}
2576
2577
2579{
2580 if( aSelection.Size() != 1 )
2581 return false;
2582
2583 const EDA_ITEM* item = aSelection.Front();
2584
2585 return ( item != nullptr ) && canAddCorner( *item );
2586}
2587
2588
2589// Finds a corresponding vertex in a polygon set
2590static std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX>
2591findVertex( SHAPE_POLY_SET& aPolySet, const EDIT_POINT& aPoint )
2592{
2593 for( auto it = aPolySet.IterateWithHoles(); it; ++it )
2594 {
2595 auto vertexIdx = it.GetIndex();
2596
2597 if( aPolySet.CVertex( vertexIdx ) == aPoint.GetPosition() )
2598 return std::make_pair( true, vertexIdx );
2599 }
2600
2601 return std::make_pair( false, SHAPE_POLY_SET::VERTEX_INDEX() );
2602}
2603
2604
2606{
2607 if( !m_editPoints || !m_editedPoint )
2608 return false;
2609
2610 EDA_ITEM* item = m_editPoints->GetParent();
2611 SHAPE_POLY_SET* polyset = nullptr;
2612
2613 if( !item )
2614 return false;
2615
2616 switch( item->Type() )
2617 {
2618 case PCB_ZONE_T:
2619 polyset = static_cast<ZONE*>( item )->Outline();
2620 break;
2621
2622 case PCB_SHAPE_T:
2623 if( static_cast<PCB_SHAPE*>( item )->GetShape() == SHAPE_T::POLY )
2624 polyset = &static_cast<PCB_SHAPE*>( item )->GetPolyShape();
2625 else
2626 return false;
2627
2628 break;
2629
2630 default:
2631 return false;
2632 }
2633
2634 std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX> vertex = findVertex( *polyset, *m_editedPoint );
2635
2636 if( !vertex.first )
2637 return false;
2638
2639 const SHAPE_POLY_SET::VERTEX_INDEX& vertexIdx = vertex.second;
2640
2641 // Check if there are enough vertices so one can be removed without
2642 // degenerating the polygon.
2643 // The first condition allows one to remove all corners from holes (when
2644 // there are only 2 vertices left, a hole is removed).
2645 if( vertexIdx.m_contour == 0 &&
2646 polyset->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour].PointCount() <= 3 )
2647 {
2648 return false;
2649 }
2650
2651 // Remove corner does not work with lines
2652 if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
2653 return false;
2654
2655 return m_editedPoint != nullptr;
2656}
2657
2658
2660{
2661 if( !m_editPoints )
2662 return 0;
2663
2664 EDA_ITEM* item = m_editPoints->GetParent();
2665 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2666 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
2667
2668 // called without an active edited polygon
2669 if( !item || !canAddCorner( *item ) )
2670 return 0;
2671
2672 PCB_SHAPE* graphicItem = dynamic_cast<PCB_SHAPE*>( item );
2673 BOARD_COMMIT commit( frame );
2674
2675 if( item->Type() == PCB_ZONE_T || ( graphicItem && graphicItem->GetShape() == SHAPE_T::POLY ) )
2676 {
2677 unsigned int nearestIdx = 0;
2678 unsigned int nextNearestIdx = 0;
2679 unsigned int nearestDist = INT_MAX;
2680 unsigned int firstPointInContour = 0;
2681 SHAPE_POLY_SET* zoneOutline;
2682
2683 if( item->Type() == PCB_ZONE_T )
2684 {
2685 ZONE* zone = static_cast<ZONE*>( item );
2686 zoneOutline = zone->Outline();
2687 zone->SetNeedRefill( true );
2688 }
2689 else
2690 {
2691 zoneOutline = &( graphicItem->GetPolyShape() );
2692 }
2693
2694 commit.Modify( item );
2695
2696 // Search the best outline segment to add a new corner
2697 // and therefore break this segment into two segments
2698
2699 // Object to iterate through the corners of the outlines (main contour and its holes)
2700 SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0, zoneOutline->OutlineCount()-1,
2701 /* IterateHoles */ true );
2702 int curr_idx = 0;
2703
2704 // Iterate through all the corners of the outlines and search the best segment
2705 for( ; iterator; iterator++, curr_idx++ )
2706 {
2707 int jj = curr_idx+1;
2708
2709 if( iterator.IsEndContour() )
2710 { // We reach the last point of the current contour (main or hole)
2711 jj = firstPointInContour;
2712 firstPointInContour = curr_idx+1; // Prepare next contour analysis
2713 }
2714
2715 SEG curr_segment( zoneOutline->CVertex( curr_idx ), zoneOutline->CVertex( jj ) );
2716
2717 unsigned int distance = curr_segment.Distance( cursorPos );
2718
2719 if( distance < nearestDist )
2720 {
2721 nearestDist = distance;
2722 nearestIdx = curr_idx;
2723 nextNearestIdx = jj;
2724 }
2725 }
2726
2727 // Find the point on the closest segment
2728 const VECTOR2I& sideOrigin = zoneOutline->CVertex( nearestIdx );
2729 const VECTOR2I& sideEnd = zoneOutline->CVertex( nextNearestIdx );
2730 SEG nearestSide( sideOrigin, sideEnd );
2731 VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
2732
2733 // Do not add points that have the same coordinates as ones that already belong to polygon
2734 // instead, add a point in the middle of the side
2735 if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
2736 nearestPoint = ( sideOrigin + sideEnd ) / 2;
2737
2738 zoneOutline->InsertVertex( nextNearestIdx, nearestPoint );
2739
2740 // We re-hatch the filled zones but not polygons
2741 if( item->Type() == PCB_ZONE_T )
2742 static_cast<ZONE*>( item )->HatchBorder();
2743
2744 commit.Push( _( "Add Zone Corner" ) );
2745 }
2746 else if( graphicItem )
2747 {
2748 switch( graphicItem->GetShape() )
2749 {
2750 case SHAPE_T::SEGMENT:
2751 {
2752 commit.Modify( graphicItem );
2753
2754 SEG seg( graphicItem->GetStart(), graphicItem->GetEnd() );
2755 VECTOR2I nearestPoint = seg.NearestPoint( cursorPos );
2756
2757 // Move the end of the line to the break point..
2758 graphicItem->SetEnd( nearestPoint );
2759
2760 // and add another one starting from the break point
2761 PCB_SHAPE* newSegment = static_cast<PCB_SHAPE*>( graphicItem->Duplicate() );
2762
2763 newSegment->ClearSelected();
2764 newSegment->SetStart( nearestPoint );
2765 newSegment->SetEnd( VECTOR2I( seg.B.x, seg.B.y ) );
2766
2767 commit.Add( newSegment );
2768 commit.Push( _( "Split Segment" ) );
2769 break;
2770 }
2771 case SHAPE_T::ARC:
2772 {
2773 commit.Modify( graphicItem );
2774
2775 const SHAPE_ARC arc( graphicItem->GetStart(), graphicItem->GetArcMid(),
2776 graphicItem->GetEnd(), 0 );
2777 const VECTOR2I nearestPoint = arc.NearestPoint( cursorPos );
2778
2779 // Move the end of the arc to the break point..
2780 graphicItem->SetEnd( nearestPoint );
2781
2782 // and add another one starting from the break point
2783 PCB_SHAPE* newArc = static_cast<PCB_SHAPE*>( graphicItem->Duplicate() );
2784
2785 newArc->ClearSelected();
2786 newArc->SetEnd( arc.GetP1() );
2787 newArc->SetStart( nearestPoint );
2788
2789 commit.Add( newArc );
2790 commit.Push( _( "Split Arc" ) );
2791 break;
2792 }
2793 default:
2794 // No split implemented for other shapes
2795 break;
2796 }
2797 }
2798
2799 updatePoints();
2800 return 0;
2801}
2802
2803
2805{
2806 if( !m_editPoints || !m_editedPoint )
2807 return 0;
2808
2809 EDA_ITEM* item = m_editPoints->GetParent();
2810
2811 if( !item )
2812 return 0;
2813
2814 SHAPE_POLY_SET* polygon = nullptr;
2815
2816 if( item->Type() == PCB_ZONE_T )
2817 {
2818 ZONE* zone = static_cast<ZONE*>( item );
2819 polygon = zone->Outline();
2820 zone->SetNeedRefill( true );
2821 }
2822 else if( item->Type() == PCB_SHAPE_T )
2823 {
2824 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2825
2826 if( shape->GetShape() == SHAPE_T::POLY )
2827 polygon = &shape->GetPolyShape();
2828 }
2829
2830 if( !polygon )
2831 return 0;
2832
2833 PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
2834 BOARD_COMMIT commit( frame );
2835 auto vertex = findVertex( *polygon, *m_editedPoint );
2836
2837 if( vertex.first )
2838 {
2839 const auto& vertexIdx = vertex.second;
2840 auto& outline = polygon->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour];
2841
2842 if( outline.PointCount() > 3 )
2843 {
2844 // the usual case: remove just the corner when there are >3 vertices
2845 commit.Modify( item );
2846 polygon->RemoveVertex( vertexIdx );
2847 }
2848 else
2849 {
2850 // either remove a hole or the polygon when there are <= 3 corners
2851 if( vertexIdx.m_contour > 0 )
2852 {
2853 // remove hole
2854 commit.Modify( item );
2855 polygon->RemoveContour( vertexIdx.m_contour );
2856 }
2857 else
2858 {
2860 commit.Remove( item );
2861 }
2862 }
2863
2864 setEditedPoint( nullptr );
2865
2866 if( item->Type() == PCB_ZONE_T )
2867 commit.Push( _( "Remove Zone Corner" ) );
2868 else
2869 commit.Push( _( "Remove Polygon Corner" ) );
2870
2871 // Refresh zone hatching
2872 if( item->Type() == PCB_ZONE_T )
2873 static_cast<ZONE*>( item )->HatchBorder();
2874
2875 updatePoints();
2876 }
2877
2878 return 0;
2879}
2880
2881
2883{
2884 if( !m_editPoints || !m_editedPoint )
2885 return 0;
2886
2887 EDA_ITEM* item = m_editPoints->GetParent();
2888
2889 if( !item )
2890 return 0;
2891
2892 SHAPE_POLY_SET* polygon = nullptr;
2893
2894 if( item->Type() == PCB_ZONE_T )
2895 {
2896 ZONE* zone = static_cast<ZONE*>( item );
2897 polygon = zone->Outline();
2898 zone->SetNeedRefill( true );
2899 }
2900 else if( item->Type() == PCB_SHAPE_T )
2901 {
2902 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2903
2904 if( shape->GetShape() == SHAPE_T::POLY )
2905 polygon = &shape->GetPolyShape();
2906 }
2907
2908 if( !polygon )
2909 return 0;
2910
2911 // Search the best outline corner to break
2912
2913 PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
2914 BOARD_COMMIT commit( frame );
2915 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
2916
2917 unsigned int nearestIdx = 0;
2918 unsigned int nearestDist = INT_MAX;
2919
2920 int curr_idx = 0;
2921 // Object to iterate through the corners of the outlines (main contour and its holes)
2922 SHAPE_POLY_SET::ITERATOR iterator = polygon->Iterate( 0, polygon->OutlineCount() - 1,
2923 /* IterateHoles */ true );
2924
2925 // Iterate through all the corners of the outlines and search the best segment
2926 for( ; iterator; iterator++, curr_idx++ )
2927 {
2928 unsigned int distance = polygon->CVertex( curr_idx ).Distance( cursorPos );
2929
2930 if( distance < nearestDist )
2931 {
2932 nearestDist = distance;
2933 nearestIdx = curr_idx;
2934 }
2935 }
2936
2937 int prevIdx, nextIdx;
2938 if( polygon->GetNeighbourIndexes( nearestIdx, &prevIdx, &nextIdx ) )
2939 {
2940 const SEG segA{ polygon->CVertex( prevIdx ), polygon->CVertex( nearestIdx ) };
2941 const SEG segB{ polygon->CVertex( nextIdx ), polygon->CVertex( nearestIdx ) };
2942
2943 // A plausible setback that won't consume a whole edge
2944 int setback = pcbIUScale.mmToIU( 5 );
2945 setback = std::min( setback, (int) ( segA.Length() * 0.8 ) );
2946 setback = std::min( setback, (int) ( segB.Length() * 0.8 ) );
2947
2948 CHAMFER_PARAMS chamferParams{ setback, setback };
2949
2950 std::optional<CHAMFER_RESULT> chamferResult =
2951 ComputeChamferPoints( segA, segB, chamferParams );
2952
2953 if( chamferResult && chamferResult->m_updated_seg_a && chamferResult->m_updated_seg_b )
2954 {
2955 commit.Modify( item );
2956 polygon->RemoveVertex( nearestIdx );
2957
2958 // The two end points of the chamfer are the new corners
2959 polygon->InsertVertex( nearestIdx, chamferResult->m_updated_seg_b->B );
2960 polygon->InsertVertex( nearestIdx, chamferResult->m_updated_seg_a->B );
2961 }
2962 }
2963
2964 setEditedPoint( nullptr );
2965
2966 if( item->Type() == PCB_ZONE_T )
2967 commit.Push( _( "Break Zone Corner" ) );
2968 else
2969 commit.Push( _( "Break Polygon Corner" ) );
2970
2971 // Refresh zone hatching
2972 if( item->Type() == PCB_ZONE_T )
2973 static_cast<ZONE*>( item )->HatchBorder();
2974
2975 updatePoints();
2976
2977 return 0;
2978}
2979
2980
2982{
2983 updatePoints();
2984 return 0;
2985}
2986
2987
2989{
2990 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2991
2993 {
2994 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
2996 else
2998
2999 switch( m_arcEditMode )
3000 {
3001 case ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS:
3002 m_arcEditMode = ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION;
3003 break;
3004 case ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION:
3005 m_arcEditMode = ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS;
3006 break;
3007 }
3008 }
3009 else
3010 {
3012 }
3013
3014 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
3016 else
3018
3019 return 0;
3020}
3021
3022
3024{
3041}
ARC_EDIT_MODE
Settings for arc editing.
Definition: app_settings.h:52
@ KEEP_CENTER_ADJUST_ANGLE_RADIUS
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
static TOOL_ACTION cycleArcEditMode
Definition: actions.h:222
static TOOL_ACTION activatePointEditor
Definition: actions.h:221
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
This covers both aligned and the orthogonal sub-type.
void updateOrthogonalDimension(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints)
Update orthogonal dimension points.
ALIGNED_DIMENSION_POINT_EDIT_BEHAVIOR(PCB_DIM_ALIGNED &aDimension)
void updateAlignedDimension(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints)
Update non-orthogonal dimension points.
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.
void UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
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 editArcMidKeepCenter(PCB_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.
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
static void editArcEndpointKeepCenter(PCB_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.
static void editArcCenterKeepEndpoints(PCB_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(PCB_SHAPE &aArc, const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCursor)
Move the mid point of the arc, while keeping the angle.
void UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
KIGFX::VIEW_CONTROLS & m_viewControls
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.
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.
ARC_POINT_EDIT_BEHAVIOR(PCB_SHAPE &aArc, const ARC_EDIT_MODE &aArcEditMode, KIGFX::VIEW_CONTROLS &aViewContols)
static void editArcEndpointKeepTangent(PCB_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.
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
COMMIT & Stage(EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen=nullptr) override
virtual void Revert() override
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
void SetParentGroup(PCB_GROUP *aGroup)
Definition: board_item.h:89
virtual BOARD_ITEM * Duplicate() const
Create a copy of this BOARD_ITEM.
Definition: board_item.cpp:244
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:299
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:257
virtual bool IsLocked() const
Definition: board_item.cpp:75
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:215
constexpr size_type GetWidth() const
Definition: box2.h:214
constexpr size_type GetHeight() const
Definition: box2.h:215
constexpr coord_type GetLeft() const
Definition: box2.h:228
constexpr bool Contains(const Vec &aPoint) const
Definition: box2.h:168
constexpr coord_type GetRight() const
Definition: box2.h:217
constexpr coord_type GetTop() const
Definition: box2.h:229
constexpr coord_type GetBottom() const
Definition: box2.h:222
Represent a set of changes (additions, deletions or modifications) of a data model (e....
Definition: commit.h:74
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been removed.
Definition: commit.h:92
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:105
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition: commit.h:80
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
int ShowModal() override
Class to help update the text position of a dimension when the crossbar changes.
DIM_ALIGNED_TEXT_UPDATER(PCB_DIM_ALIGNED &aDimension)
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.
void UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
DIM_CENTER_POINT_EDIT_BEHAVIOR(PCB_DIM_CENTER &aDimension)
DIM_LEADER_POINT_EDIT_BEHAVIOR(PCB_DIM_LEADER &aDimension)
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.
void UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
DIM_RADIAL_POINT_EDIT_BEHAVIOR(PCB_DIM_RADIAL &aDimension)
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.
void UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
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.
EDIT_CONSTRAINT that imposes a constraint that two points have to be located at angle of 45 degree mu...
EDIT_CONSTRAINT for 3 segments: dragged and two adjacent ones, enforcing to keep their slopes and all...
EDIT_CONSTRAINT that imposes a constraint that a point has to lie on a line (determined by 2 points).
EDIT_CONSTRAINT for a EDIT_LINE, that constrains the line to move perpendicular to the line itself.
bool IsCardinal() const
Definition: eda_angle.cpp:40
bool IsType(FRAME_T aType) const
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
EDA_ITEM_FLAGS GetEditFlags() const
Definition: eda_item.h:133
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
void ClearSelected()
Definition: eda_item.h:122
virtual void SetParent(EDA_ITEM *aParent)
Definition: eda_item.h:104
virtual EDA_ITEM * Clone() const
Create a duplicate of this item with linked list members set to NULL.
Definition: eda_item.cpp:85
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:527
virtual VECTOR2I GetTopLeft() const
Definition: eda_shape.h:194
int GetEndX() const
Definition: eda_shape.h:169
int GetRectangleWidth() const
Definition: eda_shape.cpp:166
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:279
SHAPE_T GetShape() const
Definition: eda_shape.h:125
virtual VECTOR2I GetBotRight() const
Definition: eda_shape.h:195
virtual void SetBottom(int val)
Definition: eda_shape.h:200
virtual void SetTop(int val)
Definition: eda_shape.h:197
int GetEndY() const
Definition: eda_shape.h:168
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:167
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:134
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:130
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:171
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:607
virtual void SetLeft(int val)
Definition: eda_shape.h:198
int GetRectangleHeight() const
Definition: eda_shape.cpp:153
virtual void SetRight(int val)
Definition: eda_shape.h:199
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:545
"Standard" table-cell editing behavior.
const VECTOR2I & GetTextPos() const
Definition: eda_text.h:253
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:464
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition: eda_text.h:183
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:313
Describe constraints between two edit handles.
Represent a line connecting two EDIT_POINTs.
Definition: edit_points.h:223
void SetConstraint(EDIT_CONSTRAINT< EDIT_LINE > *aConstraint)
Set a constraint for and EDIT_POINT.
Definition: edit_points.h:267
EDIT_POINTS is a VIEW_ITEM that manages EDIT_POINTs and EDIT_LINEs and draws them.
Definition: edit_points.h:353
unsigned int PointsSize() const
Return number of stored EDIT_POINTs.
Definition: edit_points.h:534
void AddPoint(const EDIT_POINT &aPoint)
Add an EDIT_POINT.
Definition: edit_points.h:390
void SetSwapY(bool aSwap)
Definition: edit_points.h:580
void Clear()
Clear all stored EDIT_POINTs and EDIT_LINEs.
Definition: edit_points.h:378
EDIT_LINE & Line(unsigned int aIndex)
Definition: edit_points.h:521
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,...
Definition: edit_points.h:434
bool SwapX() const
Definition: edit_points.h:576
bool SwapY() const
Definition: edit_points.h:579
void SetSwapX(bool aSwap)
Definition: edit_points.h:577
unsigned int LinesSize() const
Return number of stored EDIT_LINEs.
Definition: edit_points.h:542
EDIT_POINT & Point(unsigned int aIndex)
Definition: edit_points.h:511
void AddLine(const EDIT_LINE &aLine)
Adds an EDIT_LINE.
Definition: edit_points.h:410
Represent a single point that can be used for modifying items.
Definition: edit_points.h:48
int GetY() const
Return Y coordinate of an EDIT_POINT.
Definition: edit_points.h:95
SNAP_CONSTRAINT_TYPE GetSnapConstraint() const
Definition: edit_points.h:181
virtual void SetPosition(const VECTOR2I &aPosition)
Set new coordinates for an EDIT_POINT.
Definition: edit_points.h:107
virtual VECTOR2I GetPosition() const
Return coordinates of an EDIT_POINT.
Definition: edit_points.h:71
void SetSnapConstraint(SNAP_CONSTRAINT_TYPE aConstraint)
Definition: edit_points.h:182
virtual void ApplyConstraint(const GRID_HELPER &aGrid)
Correct coordinates of an EDIT_POINT by applying previously set constraint.
Definition: edit_points.h:166
void SetHover(bool aHover=true)
Definition: edit_points.h:176
bool IsConstrained() const
Check if point is constrained.
Definition: edit_points.h:158
void SetConstraint(EDIT_CONSTRAINT< EDIT_POINT > *aConstraint)
Set a constraint for and EDIT_POINT.
Definition: edit_points.h:131
int GetX() const
Return X coordinate of an EDIT_POINT.
Definition: edit_points.h:87
void SetActive(bool aActive=true)
Definition: edit_points.h:173
GRID_CONSTRAINT_TYPE GetGridConstraint() const
Definition: edit_points.h:178
static const TOOL_EVENT InhibitSelectionEditing
Definition: actions.h:305
static const TOOL_EVENT SelectedEvent
Definition: actions.h:292
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:299
static const TOOL_EVENT UninhibitSelectionEditing
Used to inform tool that it should display the disambiguation menu.
Definition: actions.h:306
static const TOOL_EVENT PointSelectedEvent
Definition: actions.h:291
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition: actions.h:302
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:293
std::deque< PAD * > & Pads()
Definition: footprint.h:206
Point editor behavior for the PCB_GENERATOR class.
void 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.
GENERATOR_POINT_EDIT_BEHAVIOR(PCB_GENERATOR &aGenerator)
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
Handle actions specific to filling copper zones.
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const override
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: pcb_view.cpp:91
An interface for classes handling user events controlling the view behavior such as zooming,...
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
void FreeItems()
Free all the items that were added to the group.
Definition: view_group.cpp:199
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:317
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:357
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: view.cpp:1687
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:36
static LSET AllLayersMask()
Definition: lset.cpp:701
T ToDisplayAbs(const T &aValue) const
T FromDisplayAbs(const T &aValue) const
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition: padstack.h:144
void 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.
PAD_POINT_EDIT_BEHAVIOR(PAD &aPad, PCB_LAYER_ID aLayer)
Definition: pad.h:54
const VECTOR2I & GetDrillSize() const
Definition: pad.h:307
bool IsLocked() const override
Definition: pad.cpp:230
VECTOR2I GetPosition() const override
Definition: pad.h:210
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition: pad.h:193
void SetOffset(PCB_LAYER_ID aLayer, const VECTOR2I &aOffset)
Definition: pad.h:313
void SetPosition(const VECTOR2I &aPos) override
Definition: pad.h:204
const VECTOR2I & GetOffset(PCB_LAYER_ID aLayer) const
Definition: pad.h:319
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition: pad.h:410
void SetSize(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition: pad.h:261
VECTOR2I ShapePos(PCB_LAYER_ID aLayer) const
Definition: pad.cpp:973
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition: pad.h:266
ARC_EDIT_MODE m_ArcEditMode
static TOOL_ACTION pointEditorArcKeepCenter
Definition: pcb_actions.h:302
static TOOL_ACTION layerChanged
Definition: pcb_actions.h:392
static TOOL_ACTION pointEditorMoveMidpoint
Definition: pcb_actions.h:306
static TOOL_ACTION genPushEdit
Definition: pcb_actions.h:291
static TOOL_ACTION genStartEdit
Definition: pcb_actions.h:289
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:68
static TOOL_ACTION pointEditorMoveCorner
Definition: pcb_actions.h:305
static TOOL_ACTION genUpdateEdit
Definition: pcb_actions.h:290
static TOOL_ACTION pointEditorArcKeepEndpoint
Definition: pcb_actions.h:303
static TOOL_ACTION pointEditorChamferCorner
Definition: pcb_actions.h:300
static TOOL_ACTION pointEditorRemoveCorner
Definition: pcb_actions.h:299
static TOOL_ACTION pointEditorAddCorner
Definition: pcb_actions.h:298
static TOOL_ACTION reselectItem
Definition: pcb_actions.h:73
static TOOL_ACTION genRevertEdit
Definition: pcb_actions.h:292
Common, abstract interface for edit frames.
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
ORIGIN_TRANSFORMS & GetOriginTransforms() override
Return a reference to the default ORIGIN_TRANSFORMS object.
PCBNEW_SETTINGS * GetPcbNewSettings() const
virtual PCB_LAYER_ID GetActiveLayer() const
virtual MAGNETIC_SETTINGS * GetMagneticItemsSettings()
FOOTPRINT_EDITOR_SETTINGS * GetFootprintEditorSettings() const
void Update()
Update the dimension's cached text and geometry.
void SetTextPositionMode(DIM_TEXT_POSITION aMode)
virtual const VECTOR2I & GetStart() const
The dimension's origin is the first feature point for the dimension.
virtual void SetEnd(const VECTOR2I &aPoint)
virtual void SetStart(const VECTOR2I &aPoint)
virtual const VECTOR2I & GetEnd() const
For better understanding of the points that make a dimension:
const VECTOR2I & GetCrossbarStart() const
const VECTOR2I & GetCrossbarEnd() const
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
Mark the center of a circle or arc with a cross shape.
A leader is a dimension-like object pointing to a specific point.
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
void SetOrientation(DIR aOrientation)
Set the orientation of the dimension line (so, perpendicular to the feature lines).
DIR GetOrientation() const
A radial dimension indicates either the radius or diameter of an arc or circle.
void SetLeaderLength(int aLength)
VECTOR2I GetKnee() const
virtual bool UpdateFromEditPoints(EDIT_POINTS &aEditPoints)
virtual bool MakeEditPoints(EDIT_POINTS &aEditPoints) const
virtual bool UpdateEditPoints(EDIT_POINTS &aEditPoints)
int changeArcEditMode(const TOOL_EVENT &aEvent)
void updateItem(BOARD_COMMIT &aCommit)
Update edit points with item's points.
int OnSelectionChange(const TOOL_EVENT &aEvent)
Change selection event handler.
void setAltConstraint(bool aEnabled)
Return a point that should be used as a constrainer for 45 degrees mode.
static const unsigned int COORDS_PADDING
bool removeCornerCondition(const SELECTION &aSelection)
EDIT_POINT * m_hoveredPoint
int addCorner(const TOOL_EVENT &aEvent)
bool HasPoint()
Indicate the cursor is over an edit point.
EDIT_POINT m_original
Original pos for the current drag point.
EDIT_POINT get45DegConstrainer() const
Condition to display "Create corner" context menu entry.
int modifiedSelection(const TOOL_EVENT &aEvent)
Change the edit method for arcs.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
std::unique_ptr< POINT_EDIT_BEHAVIOR > m_editorBehavior
int removeCorner(const TOOL_EVENT &aEvent)
static bool canAddCorner(const EDA_ITEM &aItem)
Condition to display "Remove corner" context menu entry.
bool Init() override
Init() is called once upon a registration of the tool.
PCB_SELECTION m_preview
int movePoint(const TOOL_EVENT &aEvent)
TOOL_ACTION handlers.
void setEditedPoint(EDIT_POINT *aPoint)
EDIT_POINT m_altConstrainer
ARC_EDIT_MODE m_arcEditMode
EDIT_POINT * m_editedPoint
void updateEditedPoint(const TOOL_EVENT &aEvent)
Set the current point being edited. NULL means none.
std::shared_ptr< EDIT_CONSTRAINT< EDIT_POINT > > m_altConstraint
void updatePoints()
Update which point is being edited.
int chamferCorner(const TOOL_EVENT &aEvent)
static bool addCornerCondition(const SELECTION &aSelection)
Determine if the tool can currently add a corner to the given item.
void setTransitions() override
< Set up handlers for various events.
std::shared_ptr< EDIT_POINTS > m_editPoints
PCB_BASE_FRAME * m_frame
std::shared_ptr< EDIT_POINTS > makePoints(EDA_ITEM *aItem)
Update item's points with edit points.
PCB_SELECTION_TOOL * m_selectionTool
Object to handle a bitmap image that can be inserted in a PCB.
VECTOR2I GetPosition() const override
Get the position of the image (this is the center of the image).
REFERENCE_IMAGE & GetReferenceImage()
The selection tool: currently supports:
PCB_SELECTION & GetSelection()
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:79
bool IsProxyItem() const override
Definition: pcb_shape.h:114
void Move(const VECTOR2I &aMoveVector) override
Move this object.
Definition: pcb_shape.cpp:543
PCB_TABLECELL_POINT_EDIT_BEHAVIOR(PCB_TABLECELL &aCell)
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.
int GetRowSpan() const
Definition: pcb_tablecell.h:65
int GetColSpan() const
Definition: pcb_tablecell.h:62
int GetRow() const
int GetColumn() const
void SetColWidth(int aCol, int aWidth)
Definition: pcb_table.h:111
void Normalize() override
Perform any normalization required after a user rotate and/or flip.
Definition: pcb_table.cpp:132
int GetColWidth(int aCol) const
Definition: pcb_table.h:113
void SetRowHeight(int aRow, int aHeight)
Definition: pcb_table.h:121
int GetRowHeight(int aRow) const
Definition: pcb_table.h:123
T * frame() const
KIGFX::PCB_VIEW * view() const
KIGFX::VIEW_CONTROLS * controls() const
bool m_isFootprintEditor
const PCB_SELECTION & selection() const
A helper class interface to manage the edit points for a single item.
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.
Class that implements "standard" polygon editing behavior.
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.
static void UpdateItem(SCH_SHAPE &aRect, const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints)
static void UpdatePoints(SCH_SHAPE &aRect, EDIT_POINTS &aPoints)
static void PinEditedCorner(const EDIT_POINT &aEditedPoint, const EDIT_POINTS &aPoints, int minWidth, int minHeight, VECTOR2I &topLeft, VECTOR2I &topRight, VECTOR2I &botLeft, VECTOR2I &botRight)
Update the coordinates of 4 corners of a rectangle, according to constraints and the moved corner.
static void UpdatePoints(PCB_SHAPE &aRectangle, EDIT_POINTS &aPoints)
static void PinEditedCorner(const EDIT_POINT &aEditedPoint, const EDIT_POINTS &aEditPoints, VECTOR2I &aTopLeft, VECTOR2I &aTopRight, VECTOR2I &aBotLeft, VECTOR2I &aBotRight, const VECTOR2I &aHole={ 0, 0 }, const VECTOR2I &aHoleSize={ 0, 0 })
Update the coordinates of 4 corners of a rectangle, according to pad constraints and the moved corner...
static void MakePoints(SCH_SHAPE &aRect, EDIT_POINTS &aPoints)
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.
static void MakePoints(const PCB_SHAPE &aRectangle, EDIT_POINTS &aPoints)
Standard rectangle points construction utility (other shapes may use this as well)
static void UpdateItem(PCB_SHAPE &aRectangle, const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints)
void UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
RECTANGLE_POINT_EDIT_BEHAVIOR(PCB_SHAPE &aRectangle)
REFERENCE_IMAGE_POINT_EDIT_BEHAVIOR(PCB_REFERENCE_IMAGE &aRefImage)
void 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.
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
void SetTransformOriginOffset(const VECTOR2I &aCenter)
VECTOR2I GetTransformOriginOffset() const
Get the center of scaling, etc, relative to the image center (GetPosition()).
VECTOR2I GetPosition() const
VECTOR2I GetSize() const
double GetImageScale() const
void SetImageScale(double aScale)
Set the image "zoom" value.
void Move(const VECTOR2I &aOffset) override
Move the item by aMoveVector to a new position.
Definition: sch_shape.cpp:69
VECTOR2I GetCenter() const
Definition: sch_shape.h:73
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
ecoord SquaredDistance(const SEG &aSeg) const
Definition: seg.cpp:75
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:327
VECTOR2I Center() const
Definition: seg.h:369
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition: seg.h:220
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition: seg.cpp:388
VECTOR2I LineProject(const VECTOR2I &aP) const
Compute the perpendicular projection point of aP on a line passing through ends of the segment.
Definition: seg.cpp:371
virtual void Add(EDA_ITEM *aItem)
Definition: selection.cpp:42
EDA_ITEM * Front() const
Definition: selection.h:172
int Size() const
Returns the number of selected parts.
Definition: selection.h:116
VECTOR2I NearestPoint(const VECTOR2I &aP) const
Definition: shape_arc.cpp:417
const VECTOR2I & GetP1() const
Definition: shape_arc.h:115
Base class for iterating over all vertices in a given SHAPE_POLY_SET.
Represent a set of closed polygons.
ITERATOR IterateWithHoles(int aOutline)
void InsertVertex(int aGlobalIndex, const VECTOR2I &aNewVertex)
Adds a vertex in the globally indexed position aGlobalIndex.
POLYGON & Polygon(int aIndex)
Return the aIndex-th subpolygon in the set.
void RemoveVertex(int aGlobalIndex)
Delete the aGlobalIndex-th vertex.
void RemoveContour(int aContourIdx, int aPolygonIdx=-1)
Delete the aContourIdx-th contour of the aPolygonIdx-th polygon in the set.
bool GetNeighbourIndexes(int aGlobalIndex, int *aPrevious, int *aNext) const
Return the global indexes of the previous and the next corner of the aGlobalIndex-th corner of a cont...
ITERATOR Iterate(int aFirst, int aLast, bool aIterateHoles=false)
Return an object to iterate through the points of the polygons between aFirst and aLast.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the index-th vertex in a given hole outline within a given outline.
int OutlineCount() const
Return the number of outlines in the set.
A textbox is edited as a rectnagle when it is orthogonally aligned.
TEXTBOX_POINT_EDIT_BEHAVIOR(PCB_TEXTBOX &aTextbox)
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.
void UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
TOOL_EVENT MakeEvent() const
Return the event associated with the action (i.e.
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:218
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
Generic, UI-independent tool event.
Definition: tool_event.h:167
bool Matches(const TOOL_EVENT &aEvent) const
Test whether two events match in terms of category & action or command.
Definition: tool_event.h:384
const VECTOR2D Position() const
Returns the point where dragging has started.
Definition: tool_event.h:285
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:307
const VECTOR2D DragOrigin() const
Returns information about mouse buttons state.
Definition: tool_event.h:291
T Parameter() const
Return a parameter assigned to the event.
Definition: tool_event.h:460
bool IsMotion() const
Definition: tool_event.h:322
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
TOOL_MENU & GetToolMenu()
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
void Activate()
Run the tool.
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:150
bool PostAction(const std::string &aActionName, T aParam)
Run the specified action after the current action (coroutine) ends.
Definition: tool_manager.h:235
bool RunSynchronousAction(const TOOL_ACTION &aAction, COMMIT *aCommit, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:197
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:44
constexpr extended_type Cross(const VECTOR2< T > &aVector) const
Compute cross product of self with aVector.
Definition: vector2d.h:542
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition: vector2d.h:557
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
T y
Definition: vector3.h:64
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector3.h:148
T x
Definition: vector3.h:63
VECTOR2I GetValue()
Returns the value in internal units.
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.
Handle a list of polygons defining a copper zone.
Definition: zone.h:73
void SetNeedRefill(bool aNeedRefill)
Definition: zone.h:265
bool UnFill()
Removes the zone filling.
Definition: zone.cpp:230
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:954
SHAPE_POLY_SET * Outline()
Definition: zone.h:337
@ CHT_MODIFY
Definition: commit.h:44
This file is part of the common library.
std::optional< CHAMFER_RESULT > ComputeChamferPoints(const SEG &aSegA, const SEG &aSegB, const CHAMFER_PARAMS &aChamferParams)
Compute the chamfer points for a given line pair and chamfer parameters.
#define _(s)
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
static constexpr EDA_ANGLE ANGLE_270
Definition: eda_angle.h:406
SHAPE_T
Definition: eda_shape.h:42
@ ALL_LAYERS
@ OBJECT_LAYERS
@ IGNORE_SNAPS
@ SNAP_BY_GRID
@ SNAP_TO_GRID
@ FRAME_PCB_EDITOR
Definition: frame_type.h:42
a few functions useful in geometry calculations.
double m_DrawArcCenterMaxAngle
When drawing an arc, the angle ( center - start ) - ( start - end ) can be limited to avoid extremely...
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition: layer_ids.h:621
bool IsCopperLayer(int aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:531
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ B_Cu
Definition: layer_ids.h:65
@ F_Cu
Definition: layer_ids.h:64
constexpr int Mils2IU(const EDA_IU_SCALE &aIuScale, int mils)
Definition: eda_units.h:157
bool PointProjectsOntoSegment(const VECTOR2I &aPoint, const SEG &aSeg)
Determine if a point projects onto a segment.
double GetLengthRatioFromStart(const VECTOR2I &aPoint, const SEG &aSeg)
Get the ratio of the vector to a point from the segment's start, compared to the segment's length.
const VECTOR2I & GetNearestEndpoint(const SEG &aSeg, const VECTOR2I &aPoint)
Get the nearest end of a segment to a point.
bool PointIsInDirection(const VECTOR2< T > &aPoint, const VECTOR2< T > &aDirection, const VECTOR2< T > &aFrom)
Definition: vector_utils.h:57
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:390
#define STATUS_ITEMS_ONLY
Definition: pcb_generator.h:67
RECT_LINES
@ RECT_RIGHT
@ RECT_LEFT
@ RECT_BOT
@ RECT_TOP
static std::pair< bool, SHAPE_POLY_SET::VERTEX_INDEX > findVertex(SHAPE_POLY_SET &aPolySet, const EDIT_POINT &aPoint)
TEXTBOX_POINT_COUNT
@ WHEN_POLYGON
@ WHEN_RECTANGLE
RECT_POINTS
@ RECT_MAX_POINTS
@ RECT_BOT_LEFT
@ RECT_BOT_RIGHT
@ RECT_CENTER
@ RECT_TOP_RIGHT
@ RECT_TOP_LEFT
DIMENSION_POINTS
@ DIM_LEADER_MAX
@ DIM_KNEE
@ DIM_TEXT
@ DIM_RADIAL_MAX
@ DIM_CROSSBAREND
@ DIM_END
@ DIM_START
@ DIM_ALIGNED_MAX
@ DIM_CENTER_MAX
@ DIM_CROSSBARSTART
#define CHECK_POINT_COUNT(aPoints, aExpected)
#define CHECK_POINT_COUNT_GE(aPoints, aExpected)
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
Parameters that define a simple chamfer operation.
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
Structure to hold the necessary information in order to index a vertex on a SHAPE_POLY_SET object: th...
VECTOR3I v1(5, 5, 5)
VECTOR2I v2(1, 0)
VECTOR2I v4(1, 1)
VECTOR2I v3(-2, 1)
constexpr int delta
GR_TEXT_H_ALIGN_T
@ TA_MOUSE_DOWN
Definition: tool_event.h:69
@ TA_UNDO_REDO_POST
Definition: tool_event.h:108
@ MD_SHIFT
Definition: tool_event.h:142
@ BUT_LEFT
Definition: tool_event.h:131
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:77
void RotatePoint(int *pX, int *pY, const EDA_ANGLE &aAngle)
Calculate the new point of coord coord pX, pY, for a rotation center 0, 0.
Definition: trigo.cpp:229
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:105
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:102
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition: typeinfo.h:91
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:103
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition: typeinfo.h:93
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:107
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition: typeinfo.h:89
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition: typeinfo.h:95
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:101
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:104
constexpr int sign(T val)
Definition: util.h:159
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691
VECTOR2< double > VECTOR2D
Definition: vector2d.h:690
Supplemental functions for working with vectors and simple objects that interact with vectors.