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 The 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>
42#include <tools/pcb_actions.h>
48#include <board_commit.h>
49#include <pcb_edit_frame.h>
50#include <pcb_reference_image.h>
51#include <pcb_generator.h>
52#include <pcb_dimension.h>
53#include <pcb_textbox.h>
54#include <pcb_tablecell.h>
55#include <pcb_table.h>
56#include <pad.h>
57#include <zone.h>
58#include <footprint.h>
61#include <progress_reporter.h>
62
63const unsigned int PCB_POINT_EDITOR::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
64
65// Few constants to avoid using bare numbers for point indices
67{
73
74 RECT_MAX_POINTS, // Must be last
75};
76
77
79{
81};
82
83
85{
92
97};
98
99
102{
105};
106
107
109{
110public:
112 {
113 wxASSERT( m_rectangle.GetShape() == SHAPE_T::RECTANGLE );
114 }
115
120 static void MakePoints( const PCB_SHAPE& aRectangle, EDIT_POINTS& aPoints )
121 {
122 wxCHECK( aRectangle.GetShape() == SHAPE_T::RECTANGLE, /* void */ );
123
124 VECTOR2I topLeft = aRectangle.GetTopLeft();
125 VECTOR2I botRight = aRectangle.GetBotRight();
126
127 aPoints.SetSwapX( topLeft.x > botRight.x );
128 aPoints.SetSwapY( topLeft.y > botRight.y );
129
130 if( aPoints.SwapX() )
131 std::swap( topLeft.x, botRight.x );
132
133 if( aPoints.SwapY() )
134 std::swap( topLeft.y, botRight.y );
135
136 aPoints.AddPoint( topLeft );
137 aPoints.AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
138 aPoints.AddPoint( botRight );
139 aPoints.AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
140 aPoints.AddPoint( aRectangle.GetCenter() );
141
142 aPoints.AddLine( aPoints.Point( RECT_TOP_LEFT ), aPoints.Point( RECT_TOP_RIGHT ) );
143 aPoints.Line( RECT_TOP ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_TOP ) ) );
144 aPoints.AddLine( aPoints.Point( RECT_TOP_RIGHT ), aPoints.Point( RECT_BOT_RIGHT ) );
145 aPoints.Line( RECT_RIGHT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_RIGHT ) ) );
146 aPoints.AddLine( aPoints.Point( RECT_BOT_RIGHT ), aPoints.Point( RECT_BOT_LEFT ) );
147 aPoints.Line( RECT_BOT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_BOT ) ) );
148 aPoints.AddLine( aPoints.Point( RECT_BOT_LEFT ), aPoints.Point( RECT_TOP_LEFT ) );
149 aPoints.Line( RECT_LEFT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_LEFT ) ) );
150 }
151
152 static void UpdateItem( PCB_SHAPE& aRectangle, const EDIT_POINT& aEditedPoint,
153 EDIT_POINTS& aPoints )
154 {
155 // You can have more points if your item wants to have more points
156 // (this class assumes the rect points come first, but that can be changed)
158
159 auto setLeft =
160 [&]( int left )
161 {
162 aPoints.SwapX() ? aRectangle.SetRight( left ) : aRectangle.SetLeft( left );
163 };
164 auto setRight =
165 [&]( int right )
166 {
167 aPoints.SwapX() ? aRectangle.SetLeft( right ) : aRectangle.SetRight( right );
168 };
169 auto setTop =
170 [&]( int top )
171 {
172 aPoints.SwapY() ? aRectangle.SetBottom( top ) : aRectangle.SetTop( top );
173 };
174 auto setBottom =
175 [&]( int bottom )
176 {
177 aPoints.SwapY() ? aRectangle.SetTop( bottom ) : aRectangle.SetBottom( bottom );
178 };
179
180 VECTOR2I topLeft = aPoints.Point( RECT_TOP_LEFT ).GetPosition();
181 VECTOR2I topRight = aPoints.Point( RECT_TOP_RIGHT ).GetPosition();
182 VECTOR2I botLeft = aPoints.Point( RECT_BOT_LEFT ).GetPosition();
183 VECTOR2I botRight = aPoints.Point( RECT_BOT_RIGHT ).GetPosition();
184
185 PinEditedCorner( aEditedPoint, aPoints, topLeft, topRight, botLeft, botRight );
186
187 if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_LEFT ) )
188 || isModified( aEditedPoint, aPoints.Point( RECT_TOP_RIGHT ) )
189 || isModified( aEditedPoint, aPoints.Point( RECT_BOT_RIGHT ) )
190 || isModified( aEditedPoint, aPoints.Point( RECT_BOT_LEFT ) ) )
191 {
192 setTop( topLeft.y );
193 setLeft( topLeft.x );
194 setRight( botRight.x );
195 setBottom( botRight.y );
196 }
197 else if( isModified( aEditedPoint, aPoints.Point( RECT_CENTER ) ) )
198 {
199 const VECTOR2I moveVector =
200 aPoints.Point( RECT_CENTER ).GetPosition() - aRectangle.GetCenter();
201 aRectangle.Move( moveVector );
202 }
203 else if( isModified( aEditedPoint, aPoints.Line( RECT_TOP ) ) )
204 {
205 setTop( topLeft.y );
206 }
207 else if( isModified( aEditedPoint, aPoints.Line( RECT_LEFT ) ) )
208 {
209 setLeft( topLeft.x );
210 }
211 else if( isModified( aEditedPoint, aPoints.Line( RECT_BOT ) ) )
212 {
213 setBottom( botRight.y );
214 }
215 else if( isModified( aEditedPoint, aPoints.Line( RECT_RIGHT ) ) )
216 {
217 setRight( botRight.x );
218 }
219
220 for( unsigned i = 0; i < aPoints.LinesSize(); ++i )
221 {
222 if( !isModified( aEditedPoint, aPoints.Line( i ) ) )
223 {
224 aPoints.Line( i ).SetConstraint( new EC_PERPLINE( aPoints.Line( i ) ) );
225 }
226 }
227 }
228
229 static void UpdatePoints( PCB_SHAPE& aRectangle, EDIT_POINTS& aPoints )
230 {
231 wxCHECK( aPoints.PointsSize() >= RECT_MAX_POINTS, /* void */ );
232
233 VECTOR2I topLeft = aRectangle.GetTopLeft();
234 VECTOR2I botRight = aRectangle.GetBotRight();
235
236 aPoints.SetSwapX( topLeft.x > botRight.x );
237 aPoints.SetSwapY( topLeft.y > botRight.y );
238
239 if( aPoints.SwapX() )
240 std::swap( topLeft.x, botRight.x );
241
242 if( aPoints.SwapY() )
243 std::swap( topLeft.y, botRight.y );
244
245 aPoints.Point( RECT_TOP_LEFT ).SetPosition( topLeft );
246 aPoints.Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y );
247 aPoints.Point( RECT_BOT_RIGHT ).SetPosition( botRight );
248 aPoints.Point( RECT_BOT_LEFT ).SetPosition( topLeft.x, botRight.y );
249 aPoints.Point( RECT_CENTER ).SetPosition( aRectangle.GetCenter() );
250 }
251
252 void MakePoints( EDIT_POINTS& aPoints ) override
253 {
254 // Just call the static helper
255 MakePoints( m_rectangle, aPoints );
256 }
257
258 void UpdatePoints( EDIT_POINTS& aPoints ) override
259 {
260 UpdatePoints( m_rectangle, aPoints );
261 }
262
263 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
264 std::vector<EDA_ITEM*>& aUpdatedItems ) override
265 {
266 UpdateItem( m_rectangle, aEditedPoint, aPoints );
267 }
268
282 static void PinEditedCorner( const EDIT_POINT& aEditedPoint, const EDIT_POINTS& aEditPoints,
283 VECTOR2I& aTopLeft, VECTOR2I& aTopRight, VECTOR2I& aBotLeft,
284 VECTOR2I& aBotRight, const VECTOR2I& aHole = { 0, 0 },
285 const VECTOR2I& aHoleSize = { 0, 0 } )
286 {
287 int minWidth = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
288 int minHeight = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
289
290 if( isModified( aEditedPoint, aEditPoints.Point( RECT_TOP_LEFT ) ) )
291 {
292 if( aHoleSize.x )
293 {
294 // pin edited point to the top/left of the hole
295 aTopLeft.x = std::min( aTopLeft.x, aHole.x - aHoleSize.x / 2 - minWidth );
296 aTopLeft.y = std::min( aTopLeft.y, aHole.y - aHoleSize.y / 2 - minHeight );
297 }
298 else
299 {
300 // pin edited point within opposite corner
301 aTopLeft.x = std::min( aTopLeft.x, aBotRight.x - minWidth );
302 aTopLeft.y = std::min( aTopLeft.y, aBotRight.y - minHeight );
303 }
304
305 // push edited point edges to adjacent corners
306 aTopRight.y = aTopLeft.y;
307 aBotLeft.x = aTopLeft.x;
308 }
309 else if( isModified( aEditedPoint, aEditPoints.Point( RECT_TOP_RIGHT ) ) )
310 {
311 if( aHoleSize.x )
312 {
313 // pin edited point to the top/right of the hole
314 aTopRight.x = std::max( aTopRight.x, aHole.x + aHoleSize.x / 2 + minWidth );
315 aTopRight.y = std::min( aTopRight.y, aHole.y - aHoleSize.y / 2 - minHeight );
316 }
317 else
318 {
319 // pin edited point within opposite corner
320 aTopRight.x = std::max( aTopRight.x, aBotLeft.x + minWidth );
321 aTopRight.y = std::min( aTopRight.y, aBotLeft.y - minHeight );
322 }
323
324 // push edited point edges to adjacent corners
325 aTopLeft.y = aTopRight.y;
326 aBotRight.x = aTopRight.x;
327 }
328 else if( isModified( aEditedPoint, aEditPoints.Point( RECT_BOT_LEFT ) ) )
329 {
330 if( aHoleSize.x )
331 {
332 // pin edited point to the bottom/left of the hole
333 aBotLeft.x = std::min( aBotLeft.x, aHole.x - aHoleSize.x / 2 - minWidth );
334 aBotLeft.y = std::max( aBotLeft.y, aHole.y + aHoleSize.y / 2 + minHeight );
335 }
336 else
337 {
338 // pin edited point within opposite corner
339 aBotLeft.x = std::min( aBotLeft.x, aTopRight.x - minWidth );
340 aBotLeft.y = std::max( aBotLeft.y, aTopRight.y + minHeight );
341 }
342
343 // push edited point edges to adjacent corners
344 aBotRight.y = aBotLeft.y;
345 aTopLeft.x = aBotLeft.x;
346 }
347 else if( isModified( aEditedPoint, aEditPoints.Point( RECT_BOT_RIGHT ) ) )
348 {
349 if( aHoleSize.x )
350 {
351 // pin edited point to the bottom/right of the hole
352 aBotRight.x = std::max( aBotRight.x, aHole.x + aHoleSize.x / 2 + minWidth );
353 aBotRight.y = std::max( aBotRight.y, aHole.y + aHoleSize.y / 2 + minHeight );
354 }
355 else
356 {
357 // pin edited point within opposite corner
358 aBotRight.x = std::max( aBotRight.x, aTopLeft.x + minWidth );
359 aBotRight.y = std::max( aBotRight.y, aTopLeft.y + minHeight );
360 }
361
362 // push edited point edges to adjacent corners
363 aBotLeft.y = aBotRight.y;
364 aTopRight.x = aBotRight.x;
365 }
366 else if( isModified( aEditedPoint, aEditPoints.Line( RECT_TOP ) ) )
367 {
368 aTopLeft.y = std::min( aTopLeft.y, aBotRight.y - minHeight );
369 }
370 else if( isModified( aEditedPoint, aEditPoints.Line( RECT_LEFT ) ) )
371 {
372 aTopLeft.x = std::min( aTopLeft.x, aBotRight.x - minWidth );
373 }
374 else if( isModified( aEditedPoint, aEditPoints.Line( RECT_BOT ) ) )
375 {
376 aBotRight.y = std::max( aBotRight.y, aTopLeft.y + minHeight );
377 }
378 else if( isModified( aEditedPoint, aEditPoints.Line( RECT_RIGHT ) ) )
379 {
380 aBotRight.x = std::max( aBotRight.x, aTopLeft.x + minWidth );
381 }
382 }
383
384private:
386};
387
388
390{
392 {
397 };
398
399public:
401 KIGFX::VIEW_CONTROLS& aViewContols ) :
402 m_arc( aArc ), m_arcEditMode( aArcEditMode ), m_viewControls( aViewContols )
403 {
404 wxASSERT( m_arc.GetShape() == SHAPE_T::ARC );
405 }
406
407 void MakePoints( EDIT_POINTS& aPoints ) override
408 {
409 aPoints.AddPoint( m_arc.GetStart() );
410 aPoints.AddPoint( m_arc.GetArcMid() );
411 aPoints.AddPoint( m_arc.GetEnd() );
412 aPoints.AddPoint( m_arc.GetCenter() );
413
414 aPoints.AddIndicatorLine( aPoints.Point( ARC_CENTER ), aPoints.Point( ARC_START ) );
415 aPoints.AddIndicatorLine( aPoints.Point( ARC_CENTER ), aPoints.Point( ARC_END ) );
416 }
417
418 void UpdatePoints( EDIT_POINTS& aPoints ) override
419 {
420 CHECK_POINT_COUNT( aPoints, 4 );
421
422 aPoints.Point( ARC_START ).SetPosition( m_arc.GetStart() );
423 aPoints.Point( ARC_MID ).SetPosition( m_arc.GetArcMid() );
424 aPoints.Point( ARC_END ).SetPosition( m_arc.GetEnd() );
426 }
427
428 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
429 std::vector<EDA_ITEM*>& aUpdatedItems ) override
430 {
431 CHECK_POINT_COUNT( aPoints, 4 );
432
434 VECTOR2I mid = aPoints.Point( ARC_MID ).GetPosition();
435 VECTOR2I start = aPoints.Point( ARC_START ).GetPosition();
436 VECTOR2I end = aPoints.Point( ARC_END ).GetPosition();
437
438 if( isModified( aEditedPoint, aPoints.Point( ARC_CENTER ) ) )
439 {
440 if( m_arcEditMode == ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION )
441 {
443 }
444 else
445 {
446 VECTOR2I moveVector = VECTOR2I( center.x, center.y ) - m_arc.GetCenter();
447 m_arc.Move( moveVector );
448 }
449 }
450 else if( isModified( aEditedPoint, aPoints.Point( ARC_MID ) ) )
451 {
452 const VECTOR2I& cursorPos = m_viewControls.GetCursorPosition( false );
453
454 if( m_arcEditMode == ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION )
455 editArcMidKeepEndpoints( m_arc, start, end, cursorPos );
456 else
457 editArcMidKeepCenter( m_arc, center, start, mid, end, cursorPos );
458 }
459 else if( isModified( aEditedPoint, aPoints.Point( ARC_START ) )
460 || isModified( aEditedPoint, aPoints.Point( ARC_END ) ) )
461 {
462 const VECTOR2I& cursorPos = m_viewControls.GetCursorPosition();
463
464 if( m_arcEditMode == ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION )
465 editArcEndpointKeepTangent( m_arc, center, start, mid, end, cursorPos );
466 else
467 editArcEndpointKeepCenter( m_arc, center, start, mid, end, cursorPos );
468 }
469 }
470
472 EDIT_POINTS& aPoints ) const override
473 {
474 return aPoints.Point( ARC_CENTER ).GetPosition();
475 }
476
477private:
478 // Note: these static arc functions don't have to be in here - we could ship them out
479 // to a utils area for use by other behaviours (e.g. in eeschema, or for polygons with
480 // radiused corners). For now this is the smaller conceptual delta with the old code.
481
485 static void editArcEndpointKeepTangent( PCB_SHAPE& aArc, const VECTOR2I& aCenter,
486 const VECTOR2I& aStart, const VECTOR2I& aMid,
487 const VECTOR2I& aEnd, const VECTOR2I& aCursor )
488 {
489 VECTOR2I center = aCenter;
490 bool movingStart;
491 bool arcValid = true;
492
493 VECTOR2I p1, p2, p3;
494 // p1 does not move, p2 does.
495
496 if( aStart != aArc.GetStart() )
497 {
498 p1 = aEnd;
499 p2 = aStart;
500 p3 = aMid;
501 movingStart = true;
502 }
503 else if( aEnd != aArc.GetEnd() )
504 {
505 p1 = aStart;
506 p2 = aEnd;
507 p3 = aMid;
508 movingStart = false;
509 }
510 else
511 {
512 return;
513 }
514
515 VECTOR2D v1, v2, v3, v4;
516
517 // Move the coordinate system
518 v1 = p1 - aCenter;
519 v2 = p2 - aCenter;
520 v3 = p3 - aCenter;
521
522 VECTOR2D u1, u2;
523
524 // A point cannot be both the center and on the arc.
525 if( ( v1.EuclideanNorm() == 0 ) || ( v2.EuclideanNorm() == 0 ) )
526 return;
527
528 u1 = v1 / v1.EuclideanNorm();
529 u2 = v3 - ( u1.x * v3.x + u1.y * v3.y ) * u1;
530 u2 = u2 / u2.EuclideanNorm();
531
532 // [ u1, u3 ] is a base centered on the circle with:
533 // u1 : unit vector toward the point that does not move
534 // u2 : unit vector toward the mid point.
535
536 // Get vectors v1, and v2 in that coordinate system.
537
538 double det = u1.x * u2.y - u2.x * u1.y;
539
540 // u1 and u2 are unit vectors, and perpendicular.
541 // det should not be 0. In case it is, do not change the arc.
542 if( det == 0 )
543 return;
544
545 double tmpx = v1.x * u2.y - v1.y * u2.x;
546 double tmpy = -v1.x * u1.y + v1.y * u1.x;
547 v1.x = tmpx;
548 v1.y = tmpy;
549 v1 = v1 / det;
550
551 tmpx = v2.x * u2.y - v2.y * u2.x;
552 tmpy = -v2.x * u1.y + v2.y * u1.x;
553 v2.x = tmpx;
554 v2.y = tmpy;
555 v2 = v2 / det;
556
557 double R = v1.EuclideanNorm();
558 bool transformCircle = false;
559
560 /* p2
561 * X***
562 * ** <---- This is the arc
563 * y ^ **
564 * | R *
565 * | <-----------> *
566 * x------x------>--------x p1
567 * C' <----> C x
568 * delta
569 *
570 * p1 does not move, and the tangent at p1 remains the same.
571 * => The new center, C', will be on the C-p1 axis.
572 * p2 moves
573 *
574 * The radius of the new circle is delta + R
575 *
576 * || C' p2 || = || C' P1 ||
577 * is the same as :
578 * ( delta + p2.x ) ^ 2 + p2.y ^ 2 = ( R + delta ) ^ 2
579 *
580 * delta = ( R^2 - p2.x ^ 2 - p2.y ^2 ) / ( 2 * p2.x - 2 * R )
581 *
582 * We can use this equation for any point p2 with p2.x < R
583 */
584
585 if( v2.x == R )
586 {
587 // Straight line, do nothing
588 }
589 else
590 {
591 if( v2.x > R )
592 {
593 // If we need to invert the curvature.
594 // We modify the input so we can use the same equation
595 transformCircle = true;
596 v2.x = 2 * R - v2.x;
597 }
598
599 // We can keep the tangent constraint.
600 double delta = ( R * R - v2.x * v2.x - v2.y * v2.y ) / ( 2 * v2.x - 2 * R );
601
602 // This is just to limit the radius, so nothing overflows later when drawing.
603 if( abs( v2.y / ( R - v2.x ) ) > ADVANCED_CFG::GetCfg().m_DrawArcCenterMaxAngle )
604 arcValid = false;
605
606 // Never recorded a problem, but still checking.
607 if( !std::isfinite( delta ) )
608 arcValid = false;
609
610 // v4 is the new center
611 v4 = ( !transformCircle ) ? VECTOR2D( -delta, 0 ) : VECTOR2D( 2 * R + delta, 0 );
612
613 tmpx = v4.x * u1.x + v4.y * u2.x;
614 tmpy = v4.x * u1.y + v4.y * u2.y;
615 v4.x = tmpx;
616 v4.y = tmpy;
617
618 center = v4 + aCenter;
619
620 if( arcValid )
621 {
622 aArc.SetCenter( center );
623
624 if( movingStart )
625 aArc.SetStart( aStart );
626 else
627 aArc.SetEnd( aEnd );
628 }
629 }
630 }
631
635 static void editArcCenterKeepEndpoints( PCB_SHAPE& aArc, const VECTOR2I& aCenter,
636 const VECTOR2I& aStart, const VECTOR2I& aMid,
637 const VECTOR2I& aEnd )
638 {
639 const int c_snapEpsilon_sq = 4;
640
641 VECTOR2I m = ( aStart / 2 + aEnd / 2 );
642 VECTOR2I perp = ( aEnd - aStart ).Perpendicular().Resize( INT_MAX / 2 );
643
644 SEG legal( m - perp, m + perp );
645
646 const SEG testSegments[] = { SEG( aCenter, aCenter + VECTOR2( 1, 0 ) ),
647 SEG( aCenter, aCenter + VECTOR2( 0, 1 ) ) };
648
649 std::vector<VECTOR2I> points = { legal.A, legal.B };
650
651 for( const SEG& seg : testSegments )
652 {
653 OPT_VECTOR2I vec = legal.IntersectLines( seg );
654
655 if( vec && legal.SquaredDistance( *vec ) <= c_snapEpsilon_sq )
656 points.push_back( *vec );
657 }
658
659 OPT_VECTOR2I nearest;
661
662 // Snap by distance between cursor and intersections
663 for( const VECTOR2I& pt : points )
664 {
665 SEG::ecoord d_sq = ( pt - aCenter ).SquaredEuclideanNorm();
666
667 if( d_sq < min_d_sq - c_snapEpsilon_sq )
668 {
669 min_d_sq = d_sq;
670 nearest = pt;
671 }
672 }
673
674 if( nearest )
675 aArc.SetCenter( *nearest );
676 }
677
681 static void editArcEndpointKeepCenter( PCB_SHAPE& aArc, const VECTOR2I& aCenter,
682 const VECTOR2I& aStart, const VECTOR2I& aMid,
683 const VECTOR2I& aEnd, const VECTOR2I& aCursor )
684 {
685 int minRadius = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
686 bool movingStart;
687
688 VECTOR2I p1, p2, prev_p1;
689
690 // user is moving p1, we want to move p2 to the new radius.
691
692 if( aStart != aArc.GetStart() )
693 {
694 prev_p1 = aArc.GetStart();
695 p1 = aStart;
696 p2 = aEnd;
697 movingStart = true;
698 }
699 else
700 {
701 prev_p1 = aArc.GetEnd();
702 p1 = aEnd;
703 p2 = aStart;
704 movingStart = false;
705 }
706
707 p1 = p1 - aCenter;
708 p2 = p2 - aCenter;
709
710 if( p1.x == 0 && p1.y == 0 )
711 p1 = prev_p1 - aCenter;
712
713 if( p2.x == 0 && p2.y == 0 )
714 p2 = { 1, 0 };
715
716 double radius = p1.EuclideanNorm();
717
718 if( radius < minRadius )
719 radius = minRadius;
720
721 p1 = aCenter + p1.Resize( radius );
722 p2 = aCenter + p2.Resize( radius );
723
724 aArc.SetCenter( aCenter );
725
726 if( movingStart )
727 {
728 aArc.SetStart( p1 );
729 aArc.SetEnd( p2 );
730 }
731 else
732 {
733 aArc.SetStart( p2 );
734 aArc.SetEnd( p1 );
735 }
736 }
737
741 void editArcMidKeepCenter( PCB_SHAPE& aArc, const VECTOR2I& aCenter, const VECTOR2I& aStart,
742 const VECTOR2I& aMid, const VECTOR2I& aEnd, const VECTOR2I& aCursor )
743 {
744 int minRadius = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
745
746 SEG chord( aStart, aEnd );
747
748 // Now, update the edit point position
749 // Express the point in a circle-centered coordinate system.
750 VECTOR2I start = aStart - aCenter;
751 VECTOR2I end = aEnd - aCenter;
752
753 double radius = ( aCursor - aCenter ).EuclideanNorm();
754
755 if( radius < minRadius )
756 radius = minRadius;
757
758 start = start.Resize( radius );
759 end = end.Resize( radius );
760
761 start = start + aCenter;
762 end = end + aCenter;
763
764 aArc.SetStart( start );
765 aArc.SetEnd( end );
766 }
767
771 static void editArcMidKeepEndpoints( PCB_SHAPE& aArc, const VECTOR2I& aStart,
772 const VECTOR2I& aEnd, const VECTOR2I& aCursor )
773 {
774 // Let 'm' be the middle point of the chord between the start and end points
775 VECTOR2I m = ( aStart + aEnd ) / 2;
776
777 // Legal midpoints lie on a vector starting just off the chord midpoint and extending out
778 // past the existing midpoint. We do not allow arc inflection while point editing.
779 const int JUST_OFF = ( aStart - aEnd ).EuclideanNorm() / 100;
780 VECTOR2I v = (VECTOR2I) aArc.GetArcMid() - m;
781 SEG legal( m + v.Resize( JUST_OFF ), m + v.Resize( INT_MAX / 2 ) );
782 VECTOR2I mid = legal.NearestPoint( aCursor );
783
784 aArc.SetArcGeometry( aStart, mid, aEnd );
785 }
786
788 // The arc edit mode, which is injected from the editor
791};
792
793
795{
796public:
798 POLYGON_POINT_EDIT_BEHAVIOR( *aZone.Outline() ), m_zone( aZone )
799 {
800 }
801
802 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
803 std::vector<EDA_ITEM*>& aUpdatedItems ) override
804 {
805 m_zone.UnFill();
806
807 // Defer to the base class to update the polygon
808 POLYGON_POINT_EDIT_BEHAVIOR::UpdateItem( aEditedPoint, aPoints, aCommit, aUpdatedItems );
809
811 }
812
813private:
815};
816
817
819{
821 {
822 REFIMG_ORIGIN = RECT_CENTER, // Reuse the center point fo rthe transform origin
823
825 };
826
827public:
829 {
830 }
831
832 void MakePoints( EDIT_POINTS& aPoints ) override
833 {
835
836 const VECTOR2I topLeft = refImage.GetPosition() - refImage.GetSize() / 2;
837 const VECTOR2I botRight = refImage.GetPosition() + refImage.GetSize() / 2;
838
839 aPoints.AddPoint( topLeft );
840 aPoints.AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
841 aPoints.AddPoint( botRight );
842 aPoints.AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
843
844 aPoints.AddPoint( refImage.GetPosition() + refImage.GetTransformOriginOffset() );
845 }
846
847 void UpdatePoints( EDIT_POINTS& aPoints ) override
848 {
850
852
853 const VECTOR2I topLeft = refImage.GetPosition() - refImage.GetSize() / 2;
854 const VECTOR2I botRight = refImage.GetPosition() + refImage.GetSize() / 2;
855
856 aPoints.Point( RECT_TOP_LEFT ).SetPosition( topLeft );
857 aPoints.Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y );
858 aPoints.Point( RECT_BOT_RIGHT ).SetPosition( botRight );
859 aPoints.Point( RECT_BOT_LEFT ).SetPosition( topLeft.x, botRight.y );
860
861 aPoints.Point( REFIMG_ORIGIN )
862 .SetPosition( refImage.GetPosition() + refImage.GetTransformOriginOffset() );
863 }
864
865 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
866 std::vector<EDA_ITEM*>& aUpdatedItems ) override
867 {
869
871
872 const VECTOR2I topLeft = aPoints.Point( RECT_TOP_LEFT ).GetPosition();
873 const VECTOR2I topRight = aPoints.Point( RECT_TOP_RIGHT ).GetPosition();
874 const VECTOR2I botRight = aPoints.Point( RECT_BOT_RIGHT ).GetPosition();
875 const VECTOR2I botLeft = aPoints.Point( RECT_BOT_LEFT ).GetPosition();
876 const VECTOR2I xfrmOrigin = aPoints.Point( REFIMG_ORIGIN ).GetPosition();
877
878 if( isModified( aEditedPoint, aPoints.Point( REFIMG_ORIGIN ) ) )
879 {
880 // Moving the transform origin
881 // As the other points didn't move, we can get the image extent from them
882 const VECTOR2I newOffset = xfrmOrigin - ( topLeft + botRight ) / 2;
883 refImage.SetTransformOriginOffset( newOffset );
884 }
885 else
886 {
887 const VECTOR2I oldOrigin =
889 const VECTOR2I oldSize = refImage.GetSize();
890 const VECTOR2I pos = refImage.GetPosition();
891
892 OPT_VECTOR2I newCorner;
893 VECTOR2I oldCorner = pos;
894 if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_LEFT ) ) )
895 {
896 newCorner = topLeft;
897 oldCorner -= oldSize / 2;
898 }
899 else if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_RIGHT ) ) )
900 {
901 newCorner = topRight;
902 oldCorner -= VECTOR2I( -oldSize.x, oldSize.y ) / 2;
903 }
904 else if( isModified( aEditedPoint, aPoints.Point( RECT_BOT_LEFT ) ) )
905 {
906 newCorner = botLeft;
907 oldCorner -= VECTOR2I( oldSize.x, -oldSize.y ) / 2;
908 }
909 else if( isModified( aEditedPoint, aPoints.Point( RECT_BOT_RIGHT ) ) )
910 {
911 newCorner = botRight;
912 oldCorner += oldSize / 2;
913 }
914
915 if( newCorner )
916 {
917 // Turn in the respective vectors from the origin
918 *newCorner -= xfrmOrigin;
919 oldCorner -= oldOrigin;
920
921 // If we tried to cross the origin, clamp it to stop it
922 if( sign( newCorner->x ) != sign( oldCorner.x )
923 || sign( newCorner->y ) != sign( oldCorner.y ) )
924 {
925 *newCorner = VECTOR2I( 0, 0 );
926 }
927
928 const double newLength = newCorner->EuclideanNorm();
929 const double oldLength = oldCorner.EuclideanNorm();
930
931 double ratio = oldLength > 0 ? ( newLength / oldLength ) : 1.0;
932
933 // Clamp the scaling to a minimum of 50 mils
934 VECTOR2I newSize = oldSize * ratio;
935 double newWidth = std::max( newSize.x, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
936 double newHeight = std::max( newSize.y, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
937 ratio = std::min( newWidth / oldSize.x, newHeight / oldSize.y );
938
939 // Also handles the origin offset
940 refImage.SetImageScale( refImage.GetImageScale() * ratio );
941 }
942 }
943 }
944
945private:
947};
948
949
951{
952public:
955 {
956 }
957
958 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
959 std::vector<EDA_ITEM*>& aUpdatedItems ) override
960 {
962
963 PCB_TABLE& table = static_cast<PCB_TABLE&>( *m_cell.GetParent() );
964 aCommit.Modify( &table );
965 aUpdatedItems.push_back( &table );
966
968 {
969 if( isModified( aEditedPoint, aPoints.Point( ROW_HEIGHT ) ) )
970 {
971 m_cell.SetEnd( VECTOR2I( m_cell.GetEndX(), aPoints.Point( ROW_HEIGHT ).GetY() ) );
972
973 int colWidth = std::abs( m_cell.GetRectangleHeight() );
974
975 for( int ii = 0; ii < m_cell.GetColSpan() - 1; ++ii )
976 colWidth -= table.GetColWidth( m_cell.GetColumn() + ii );
977
978 table.SetColWidth( m_cell.GetColumn() + m_cell.GetColSpan() - 1, colWidth );
979 }
980 else if( isModified( aEditedPoint, aPoints.Point( COL_WIDTH ) ) )
981 {
982 m_cell.SetEnd( VECTOR2I( aPoints.Point( COL_WIDTH ).GetX(), m_cell.GetEndY() ) );
983
984 int rowHeight = m_cell.GetRectangleWidth();
985
986 for( int ii = 0; ii < m_cell.GetRowSpan() - 1; ++ii )
987 rowHeight -= table.GetRowHeight( m_cell.GetRow() + ii );
988
989 table.SetRowHeight( m_cell.GetRow() + m_cell.GetRowSpan() - 1, rowHeight );
990 }
991 }
992 else
993 {
994 if( isModified( aEditedPoint, aPoints.Point( COL_WIDTH ) ) )
995 {
996 m_cell.SetEnd( VECTOR2I( aPoints.Point( COL_WIDTH ).GetX(), m_cell.GetEndY() ) );
997
998 int colWidth = m_cell.GetRectangleWidth();
999
1000 for( int ii = 0; ii < m_cell.GetColSpan() - 1; ++ii )
1001 colWidth -= table.GetColWidth( m_cell.GetColumn() + ii );
1002
1003 table.SetColWidth( m_cell.GetColumn() + m_cell.GetColSpan() - 1, colWidth );
1004 }
1005 else if( isModified( aEditedPoint, aPoints.Point( ROW_HEIGHT ) ) )
1006 {
1007 m_cell.SetEnd( VECTOR2I( m_cell.GetEndX(), aPoints.Point( ROW_HEIGHT ).GetY() ) );
1008
1009 int rowHeight = m_cell.GetRectangleHeight();
1010
1011 for( int ii = 0; ii < m_cell.GetRowSpan() - 1; ++ii )
1012 rowHeight -= table.GetRowHeight( m_cell.GetRow() + ii );
1013
1014 table.SetRowHeight( m_cell.GetRow() + m_cell.GetRowSpan() - 1, rowHeight );
1015 }
1016 }
1017
1018 table.Normalize();
1019 }
1020
1021private:
1023};
1024
1025
1027{
1028public:
1030 m_pad( aPad ),
1031 m_layer( aLayer )
1032 {}
1033
1034 void MakePoints( EDIT_POINTS& aPoints ) override
1035 {
1036 VECTOR2I shapePos = m_pad.ShapePos( m_layer );
1037 VECTOR2I halfSize( m_pad.GetSize( m_layer ).x / 2,
1038 m_pad.GetSize( m_layer ).y / 2 );
1039
1040 if( m_pad.IsLocked() )
1041 return;
1042
1043 switch( m_pad.GetShape( m_layer ) )
1044 {
1045 case PAD_SHAPE::CIRCLE:
1046 aPoints.AddPoint( VECTOR2I( shapePos.x + halfSize.x, shapePos.y ) );
1047 break;
1048
1049 case PAD_SHAPE::OVAL:
1050 case PAD_SHAPE::TRAPEZOID:
1051 case PAD_SHAPE::RECTANGLE:
1052 case PAD_SHAPE::ROUNDRECT:
1053 case PAD_SHAPE::CHAMFERED_RECT:
1054 {
1056 break;
1057
1059 std::swap( halfSize.x, halfSize.y );
1060
1061 // It's important to fill these according to the RECT indices
1062 aPoints.AddPoint( shapePos - halfSize );
1063 aPoints.AddPoint( VECTOR2I( shapePos.x + halfSize.x, shapePos.y - halfSize.y ) );
1064 aPoints.AddPoint( shapePos + halfSize );
1065 aPoints.AddPoint( VECTOR2I( shapePos.x - halfSize.x, shapePos.y + halfSize.y ) );
1066 }
1067 break;
1068
1069 default: // suppress warnings
1070 break;
1071 }
1072 }
1073
1074 void UpdatePoints( EDIT_POINTS& aPoints ) override
1075 {
1076 bool locked = m_pad.GetParent() && m_pad.IsLocked();
1077 VECTOR2I shapePos = m_pad.ShapePos( m_layer );
1078 VECTOR2I halfSize( m_pad.GetSize( m_layer ).x / 2,
1079 m_pad.GetSize( m_layer ).y / 2 );
1080
1081 switch( m_pad.GetShape( m_layer ) )
1082 {
1083 case PAD_SHAPE::CIRCLE:
1084 {
1085 int target = locked ? 0 : 1;
1086
1087 // Careful; pad shape is mutable...
1088 if( int( aPoints.PointsSize() ) != target )
1089 {
1090 aPoints.Clear();
1091 MakePoints( aPoints );
1092 }
1093 else if( target == 1 )
1094 {
1095 shapePos.x += halfSize.x;
1096 aPoints.Point( 0 ).SetPosition( shapePos );
1097 }
1098 }
1099 break;
1100
1101 case PAD_SHAPE::OVAL:
1102 case PAD_SHAPE::TRAPEZOID:
1103 case PAD_SHAPE::RECTANGLE:
1104 case PAD_SHAPE::ROUNDRECT:
1105 case PAD_SHAPE::CHAMFERED_RECT:
1106 {
1107 // Careful; pad shape and orientation are mutable...
1108 int target = locked || !m_pad.GetOrientation().IsCardinal() ? 0 : 4;
1109
1110 if( int( aPoints.PointsSize() ) != target )
1111 {
1112 aPoints.Clear();
1113 MakePoints( aPoints );
1114 }
1115 else if( target == 4 )
1116 {
1118 std::swap( halfSize.x, halfSize.y );
1119
1120 aPoints.Point( RECT_TOP_LEFT ).SetPosition( shapePos - halfSize );
1121 aPoints.Point( RECT_TOP_RIGHT )
1122 .SetPosition(
1123 VECTOR2I( shapePos.x + halfSize.x, shapePos.y - halfSize.y ) );
1124 aPoints.Point( RECT_BOT_RIGHT ).SetPosition( shapePos + halfSize );
1125 aPoints.Point( RECT_BOT_LEFT )
1126 .SetPosition(
1127 VECTOR2I( shapePos.x - halfSize.x, shapePos.y + halfSize.y ) );
1128 }
1129
1130 break;
1131 }
1132
1133 default: // suppress warnings
1134 break;
1135 }
1136 }
1137
1138 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1139 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1140 {
1141 switch( m_pad.GetShape( m_layer ) )
1142 {
1143 case PAD_SHAPE::CIRCLE:
1144 {
1145 VECTOR2I end = aPoints.Point( 0 ).GetPosition();
1146 int diameter = 2 * ( end - m_pad.GetPosition() ).EuclideanNorm();
1147
1148 m_pad.SetSize( m_layer, VECTOR2I( diameter, diameter ) );
1149 break;
1150 }
1151
1152 case PAD_SHAPE::OVAL:
1153 case PAD_SHAPE::TRAPEZOID:
1154 case PAD_SHAPE::RECTANGLE:
1155 case PAD_SHAPE::ROUNDRECT:
1156 case PAD_SHAPE::CHAMFERED_RECT:
1157 {
1158 VECTOR2I topLeft = aPoints.Point( RECT_TOP_LEFT ).GetPosition();
1159 VECTOR2I topRight = aPoints.Point( RECT_TOP_RIGHT ).GetPosition();
1160 VECTOR2I botLeft = aPoints.Point( RECT_BOT_LEFT ).GetPosition();
1161 VECTOR2I botRight = aPoints.Point( RECT_BOT_RIGHT ).GetPosition();
1162 VECTOR2I holeCenter = m_pad.GetPosition();
1163 VECTOR2I holeSize = m_pad.GetDrillSize();
1164
1165 RECTANGLE_POINT_EDIT_BEHAVIOR::PinEditedCorner( aEditedPoint, aPoints, topLeft,
1166 topRight, botLeft, botRight, holeCenter,
1167 holeSize );
1168
1169 if( ( m_pad.GetOffset( m_layer ).x
1170 || m_pad.GetOffset( m_layer ).y )
1171 || ( m_pad.GetDrillSize().x && m_pad.GetDrillSize().y ) )
1172 {
1173 // Keep hole pinned at the current location; adjust the pad around the hole
1174
1176 int dist[4];
1177
1178 if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_LEFT ) )
1179 || isModified( aEditedPoint, aPoints.Point( RECT_BOT_RIGHT ) ) )
1180 {
1181 dist[0] = center.x - topLeft.x;
1182 dist[1] = center.y - topLeft.y;
1183 dist[2] = botRight.x - center.x;
1184 dist[3] = botRight.y - center.y;
1185 }
1186 else
1187 {
1188 dist[0] = center.x - botLeft.x;
1189 dist[1] = center.y - topRight.y;
1190 dist[2] = topRight.x - center.x;
1191 dist[3] = botLeft.y - center.y;
1192 }
1193
1194 VECTOR2I padSize( dist[0] + dist[2], dist[1] + dist[3] );
1195 VECTOR2I deltaOffset( padSize.x / 2 - dist[2], padSize.y / 2 - dist[3] );
1196
1198 std::swap( padSize.x, padSize.y );
1199
1200 RotatePoint( deltaOffset, -m_pad.GetOrientation() );
1201
1202 m_pad.SetSize( m_layer, padSize );
1203 m_pad.SetOffset( m_layer, -deltaOffset );
1204 }
1205 else
1206 {
1207 // Keep pad position at the center of the pad shape
1208
1209 int left, top, right, bottom;
1210
1211 if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_LEFT ) )
1212 || isModified( aEditedPoint, aPoints.Point( RECT_BOT_RIGHT ) ) )
1213 {
1214 left = topLeft.x;
1215 top = topLeft.y;
1216 right = botRight.x;
1217 bottom = botRight.y;
1218 }
1219 else
1220 {
1221 left = botLeft.x;
1222 top = topRight.y;
1223 right = topRight.x;
1224 bottom = botLeft.y;
1225 }
1226
1227 VECTOR2I padSize( abs( right - left ), abs( bottom - top ) );
1228
1230 std::swap( padSize.x, padSize.y );
1231
1232 m_pad.SetSize( m_layer, padSize );
1233 m_pad.SetPosition( VECTOR2I( ( left + right ) / 2, ( top + bottom ) / 2 ) );
1234 }
1235 break;
1236 }
1237 default: // suppress warnings
1238 break;
1239 }
1240 }
1241
1242private:
1245};
1246
1247
1254{
1255public:
1257
1258 void MakePoints( EDIT_POINTS& aPoints ) override
1259 {
1260 m_generator.MakeEditPoints( aPoints );
1261 }
1262
1263 void UpdatePoints( EDIT_POINTS& aPoints ) override
1264 {
1265 m_generator.UpdateEditPoints( aPoints );
1266 }
1267
1268 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1269 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1270 {
1272 }
1273
1274private:
1276};
1277
1278
1288{
1289public:
1291 m_dimension( aDimension ), m_originalTextPos( aDimension.GetTextPos() ),
1292 m_oldCrossBar( SEG{ aDimension.GetCrossbarStart(), aDimension.GetCrossbarEnd() } )
1293 {
1294 }
1295
1297 {
1298 const SEG newCrossBar{ m_dimension.GetCrossbarStart(), m_dimension.GetCrossbarEnd() };
1299
1300 if( newCrossBar == m_oldCrossBar )
1301 {
1302 // Crossbar didn't change, text doesn't need to change
1303 return;
1304 }
1305
1306 const VECTOR2I newTextPos = getDimensionNewTextPosition();
1307 m_dimension.SetTextPos( newTextPos );
1308
1309 const GR_TEXT_H_ALIGN_T oldJustify = m_dimension.GetHorizJustify();
1310
1311 // We may need to update the justification if we go past vertical.
1312 if( oldJustify == GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_LEFT
1313 || oldJustify == GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_RIGHT )
1314 {
1316 const VECTOR2I newProject = newCrossBar.LineProject( newTextPos );
1317
1318 const VECTOR2I oldProjectedOffset =
1319 oldProject - m_oldCrossBar.NearestPoint( oldProject );
1320 const VECTOR2I newProjectedOffset = newProject - newCrossBar.NearestPoint( newProject );
1321
1322 const bool textWasLeftOf = oldProjectedOffset.x < 0
1323 || ( oldProjectedOffset.x == 0 && oldProjectedOffset.y > 0 );
1324 const bool textIsLeftOf = newProjectedOffset.x < 0
1325 || ( newProjectedOffset.x == 0 && newProjectedOffset.y > 0 );
1326
1327 if( textWasLeftOf != textIsLeftOf )
1328 {
1329 // Flip whatever the user had set
1331 ( oldJustify == GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_LEFT )
1332 ? GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_RIGHT
1333 : GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_LEFT );
1334 }
1335 }
1336
1337 // Update the dimension (again) to ensure the text knockouts are correct
1339 }
1340
1341private:
1343 {
1344 const SEG newCrossBar{ m_dimension.GetCrossbarStart(), m_dimension.GetCrossbarEnd() };
1345
1346 const EDA_ANGLE oldAngle = EDA_ANGLE( m_oldCrossBar.B - m_oldCrossBar.A );
1347 const EDA_ANGLE newAngle = EDA_ANGLE( newCrossBar.B - newCrossBar.A );
1348 const EDA_ANGLE rotation = oldAngle - newAngle;
1349
1350 // There are two modes - when the text is between the crossbar points, and when it's not.
1352 {
1353 const VECTOR2I cbNearestEndToText =
1355 const VECTOR2I rotTextOffsetFromCbCenter =
1357 const VECTOR2I rotTextOffsetFromCbEnd =
1358 GetRotated( m_originalTextPos - cbNearestEndToText, rotation );
1359
1360 // Which of the two crossbar points is now in the right direction? They could be swapped over now.
1361 // If zero-length, doesn't matter, they're the same thing
1362 const bool startIsInOffsetDirection =
1364 rotTextOffsetFromCbCenter, newCrossBar.Center() );
1365
1366 const VECTOR2I& newCbRefPt = startIsInOffsetDirection ? m_dimension.GetCrossbarStart()
1368
1369 // Apply the new offset to the correct crossbar point
1370 return newCbRefPt + rotTextOffsetFromCbEnd;
1371 }
1372
1373 // If the text was between the crossbar points, it should stay there, but we need to find a
1374 // good place for it. Keep it the same distance from the crossbar line, but rotated as needed.
1375
1376 const VECTOR2I origTextPointProjected = m_oldCrossBar.NearestPoint( m_originalTextPos );
1377 const double oldRatio =
1378 KIGEOM::GetLengthRatioFromStart( origTextPointProjected, m_oldCrossBar );
1379
1380 // Perpendicular from the crossbar line to the text position
1381 // We need to keep this length constant
1382 const VECTOR2I rotCbNormalToText =
1383 GetRotated( m_originalTextPos - origTextPointProjected, rotation );
1384
1385 const VECTOR2I newProjected = newCrossBar.A + ( newCrossBar.B - newCrossBar.A ) * oldRatio;
1386 return newProjected + rotCbNormalToText;
1387 }
1388
1392};
1393
1394
1399{
1400public:
1402 {
1403 }
1404
1405 void MakePoints( EDIT_POINTS& aPoints ) override
1406 {
1407 aPoints.AddPoint( m_dimension.GetStart() );
1408 aPoints.AddPoint( m_dimension.GetEnd() );
1409 aPoints.AddPoint( m_dimension.GetTextPos() );
1411 aPoints.AddPoint( m_dimension.GetCrossbarEnd() );
1412
1415
1417 {
1418 // Dimension height setting - edit points should move only along the feature lines
1419 aPoints.Point( DIM_CROSSBARSTART )
1420 .SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBARSTART ),
1421 aPoints.Point( DIM_START ) ) );
1422 aPoints.Point( DIM_CROSSBAREND )
1423 .SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBAREND ),
1424 aPoints.Point( DIM_END ) ) );
1425 }
1426 }
1427
1428 void UpdatePoints( EDIT_POINTS& aPoints ) override
1429 {
1431
1433 aPoints.Point( DIM_END ).SetPosition( m_dimension.GetEnd() );
1437 }
1438
1439 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1440 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1441 {
1443
1445 updateAlignedDimension( aEditedPoint, aPoints );
1446 else
1447 updateOrthogonalDimension( aEditedPoint, aPoints );
1448 }
1449
1451 EDIT_POINTS& aPoints ) const override
1452 {
1453 // Constraint for crossbar
1454 if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1455 return aPoints.Point( DIM_END ).GetPosition();
1456
1457 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1458 return aPoints.Point( DIM_START ).GetPosition();
1459
1460 // No constraint
1461 return aEditedPoint.GetPosition();
1462 }
1463
1464private:
1468 void updateAlignedDimension( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints )
1469 {
1470 DIM_ALIGNED_TEXT_UPDATER textPositionUpdater( m_dimension );
1471
1472 // Check which point is currently modified and updated dimension's points respectively
1473 if( isModified( aEditedPoint, aPoints.Point( DIM_CROSSBARSTART ) ) )
1474 {
1475 VECTOR2D featureLine( aEditedPoint.GetPosition() - m_dimension.GetStart() );
1477
1478 if( featureLine.Cross( crossBar ) > 0 )
1479 m_dimension.SetHeight( -featureLine.EuclideanNorm() );
1480 else
1481 m_dimension.SetHeight( featureLine.EuclideanNorm() );
1482
1484 }
1485 else if( isModified( aEditedPoint, aPoints.Point( DIM_CROSSBAREND ) ) )
1486 {
1487 VECTOR2D featureLine( aEditedPoint.GetPosition() - m_dimension.GetEnd() );
1489
1490 if( featureLine.Cross( crossBar ) > 0 )
1491 m_dimension.SetHeight( -featureLine.EuclideanNorm() );
1492 else
1493 m_dimension.SetHeight( featureLine.EuclideanNorm() );
1494
1496 }
1497 else if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1498 {
1499 m_dimension.SetStart( aEditedPoint.GetPosition() );
1501
1502 aPoints.Point( DIM_CROSSBARSTART )
1503 .SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBARSTART ),
1504 aPoints.Point( DIM_START ) ) );
1505 aPoints.Point( DIM_CROSSBAREND )
1506 .SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBAREND ),
1507 aPoints.Point( DIM_END ) ) );
1508 }
1509 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1510 {
1511 m_dimension.SetEnd( aEditedPoint.GetPosition() );
1513
1514 aPoints.Point( DIM_CROSSBARSTART )
1515 .SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBARSTART ),
1516 aPoints.Point( DIM_START ) ) );
1517 aPoints.Point( DIM_CROSSBAREND )
1518 .SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBAREND ),
1519 aPoints.Point( DIM_END ) ) );
1520 }
1521 else if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1522 {
1523 // Force manual mode if we weren't already in it
1524 m_dimension.SetTextPositionMode( DIM_TEXT_POSITION::MANUAL );
1525 m_dimension.SetTextPos( aEditedPoint.GetPosition() );
1527 }
1528
1529 textPositionUpdater.UpdateTextAfterChange();
1530 }
1531
1535 void updateOrthogonalDimension( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints )
1536 {
1537 DIM_ALIGNED_TEXT_UPDATER textPositionUpdater( m_dimension );
1538 PCB_DIM_ORTHOGONAL& orthDimension = static_cast<PCB_DIM_ORTHOGONAL&>( m_dimension );
1539
1540 if( isModified( aEditedPoint, aPoints.Point( DIM_CROSSBARSTART ) )
1541 || isModified( aEditedPoint, aPoints.Point( DIM_CROSSBAREND ) ) )
1542 {
1544
1545 const VECTOR2I& cursorPos = aEditedPoint.GetPosition();
1546
1547 // Find vector from nearest dimension point to edit position
1548 VECTOR2I directionA( cursorPos - m_dimension.GetStart() );
1549 VECTOR2I directionB( cursorPos - m_dimension.GetEnd() );
1550 VECTOR2I direction = ( directionA < directionB ) ? directionA : directionB;
1551
1552 bool vert;
1553 VECTOR2D featureLine( cursorPos - m_dimension.GetStart() );
1554
1555 // Only change the orientation when we move outside the bounds
1556 if( !bounds.Contains( cursorPos ) )
1557 {
1558 // If the dimension is horizontal or vertical, set correct orientation
1559 // otherwise, test if we're left/right of the bounding box or above/below it
1560 if( bounds.GetWidth() == 0 )
1561 vert = true;
1562 else if( bounds.GetHeight() == 0 )
1563 vert = false;
1564 else if( cursorPos.x > bounds.GetLeft() && cursorPos.x < bounds.GetRight() )
1565 vert = false;
1566 else if( cursorPos.y > bounds.GetTop() && cursorPos.y < bounds.GetBottom() )
1567 vert = true;
1568 else
1569 vert = std::abs( direction.y ) < std::abs( direction.x );
1570
1573 }
1574 else
1575 {
1576 vert = orthDimension.GetOrientation() == PCB_DIM_ORTHOGONAL::DIR::VERTICAL;
1577 }
1578
1579 m_dimension.SetHeight( vert ? featureLine.x : featureLine.y );
1580 }
1581 else if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1582 {
1583 m_dimension.SetStart( aEditedPoint.GetPosition() );
1584 }
1585 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1586 {
1587 m_dimension.SetEnd( aEditedPoint.GetPosition() );
1588 }
1589 else if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1590 {
1591 // Force manual mode if we weren't already in it
1592 m_dimension.SetTextPositionMode( DIM_TEXT_POSITION::MANUAL );
1593 m_dimension.SetTextPos( VECTOR2I( aEditedPoint.GetPosition() ) );
1594 }
1595
1597
1598 // After recompute, find the new text position
1599 textPositionUpdater.UpdateTextAfterChange();
1600 }
1601
1603};
1604
1605
1607{
1608public:
1610
1611 void MakePoints( EDIT_POINTS& aPoints ) override
1612 {
1613 aPoints.AddPoint( m_dimension.GetStart() );
1614 aPoints.AddPoint( m_dimension.GetEnd() );
1615
1617
1618 aPoints.Point( DIM_END ).SetConstraint(
1619 new EC_45DEGREE( aPoints.Point( DIM_END ), aPoints.Point( DIM_START ) ) );
1621 }
1622
1623 void UpdatePoints( EDIT_POINTS& aPoints ) override
1624 {
1626
1628 aPoints.Point( DIM_END ).SetPosition( m_dimension.GetEnd() );
1629 }
1630
1631 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1632 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1633 {
1635
1636 if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1637 m_dimension.SetStart( aEditedPoint.GetPosition() );
1638 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1639 m_dimension.SetEnd( aEditedPoint.GetPosition() );
1640
1642 }
1643
1645 EDIT_POINTS& aPoints ) const override
1646 {
1647 if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1648 return aPoints.Point( DIM_START ).GetPosition();
1649
1650 return std::nullopt;
1651 }
1652
1653private:
1655};
1656
1657
1659{
1660public:
1662
1663 void MakePoints( EDIT_POINTS& aPoints ) override
1664 {
1665 aPoints.AddPoint( m_dimension.GetStart() );
1666 aPoints.AddPoint( m_dimension.GetEnd() );
1667 aPoints.AddPoint( m_dimension.GetTextPos() );
1668 aPoints.AddPoint( m_dimension.GetKnee() );
1669
1672
1673 aPoints.Point( DIM_KNEE )
1675 new EC_LINE( aPoints.Point( DIM_START ), aPoints.Point( DIM_END ) ) );
1677
1678 aPoints.Point( DIM_TEXT )
1680 new EC_45DEGREE( aPoints.Point( DIM_TEXT ), aPoints.Point( DIM_KNEE ) ) );
1682 }
1683
1684 void UpdatePoints( EDIT_POINTS& aPoints ) override
1685 {
1687
1689 aPoints.Point( DIM_END ).SetPosition( m_dimension.GetEnd() );
1692 }
1693
1694 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1695 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1696 {
1698
1699 if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1700 {
1701 m_dimension.SetStart( aEditedPoint.GetPosition() );
1703
1704 aPoints.Point( DIM_KNEE )
1706 new EC_LINE( aPoints.Point( DIM_START ), aPoints.Point( DIM_END ) ) );
1707 }
1708 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1709 {
1710 VECTOR2I oldKnee = m_dimension.GetKnee();
1711
1712 m_dimension.SetEnd( aEditedPoint.GetPosition() );
1714
1715 VECTOR2I kneeDelta = m_dimension.GetKnee() - oldKnee;
1718
1719 aPoints.Point( DIM_KNEE )
1721 new EC_LINE( aPoints.Point( DIM_START ), aPoints.Point( DIM_END ) ) );
1722 }
1723 else if( isModified( aEditedPoint, aPoints.Point( DIM_KNEE ) ) )
1724 {
1725 VECTOR2I oldKnee = m_dimension.GetKnee();
1726 VECTOR2I arrowVec = aPoints.Point( DIM_KNEE ).GetPosition()
1727 - aPoints.Point( DIM_END ).GetPosition();
1728
1731
1732 VECTOR2I kneeDelta = m_dimension.GetKnee() - oldKnee;
1735 }
1736 else if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1737 {
1738 m_dimension.SetTextPos( aEditedPoint.GetPosition() );
1740 }
1741 }
1742
1744 EDIT_POINTS& aPoints ) const override
1745 {
1746 if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1747 return aPoints.Point( DIM_KNEE ).GetPosition();
1748
1749 return std::nullopt;
1750 }
1751
1752private:
1754};
1755
1756
1758{
1759public:
1761
1762 void MakePoints( EDIT_POINTS& aPoints ) override
1763 {
1764 aPoints.AddPoint( m_dimension.GetStart() );
1765 aPoints.AddPoint( m_dimension.GetEnd() );
1766 aPoints.AddPoint( m_dimension.GetTextPos() );
1767
1770
1771 aPoints.Point( DIM_TEXT )
1773 new EC_45DEGREE( aPoints.Point( DIM_TEXT ), aPoints.Point( DIM_END ) ) );
1775 }
1776
1777 void UpdatePoints( EDIT_POINTS& aPoints ) override
1778 {
1780
1782 aPoints.Point( DIM_END ).SetPosition( m_dimension.GetEnd() );
1784 }
1785
1786 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1787 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1788 {
1790
1791 if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1792 {
1793 m_dimension.SetStart( aEditedPoint.GetPosition() );
1794 }
1795 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1796 {
1797 const VECTOR2I newPoint( aEditedPoint.GetPosition() );
1798 const VECTOR2I delta = newPoint - m_dimension.GetEnd();
1799
1800 m_dimension.SetEnd( newPoint );
1802 }
1803 else if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1804 {
1805 m_dimension.SetTextPos( aEditedPoint.GetPosition() );
1806 }
1807
1809 }
1810
1811private:
1813};
1814
1815
1820{
1821public:
1823
1824 void MakePoints( EDIT_POINTS& aPoints ) override
1825 {
1826 if( m_textbox.GetShape() == SHAPE_T::RECTANGLE )
1827 {
1829 }
1830 else
1831 {
1832 // Rotated textboxes are implemented as polygons and these
1833 // aren't currently editable.
1834 }
1835 }
1836
1837 void UpdatePoints( EDIT_POINTS& aPoints ) override
1838 {
1839 // When textboxes are rotated, they act as polygons, not rectangles
1840 const unsigned target = m_textbox.GetShape() == SHAPE_T::RECTANGLE
1841 ? TEXTBOX_POINT_COUNT::WHEN_RECTANGLE
1842 : TEXTBOX_POINT_COUNT::WHEN_POLYGON;
1843
1844 // Careful; textbox shape is mutable between cardinal and non-cardinal rotations...
1845 if( aPoints.PointsSize() != target )
1846 {
1847 aPoints.Clear();
1848 MakePoints( aPoints );
1849 return;
1850 }
1851
1852 if( m_textbox.GetShape() == SHAPE_T::RECTANGLE )
1853 {
1854 // Dispatch to the rectangle behavior
1856 }
1857 else if( m_textbox.GetShape() == SHAPE_T::POLY )
1858 {
1859 // Not currently editable while rotated.
1860 }
1861 }
1862
1863 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1864 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1865 {
1866 if( m_textbox.GetShape() == SHAPE_T::RECTANGLE )
1867 {
1868 RECTANGLE_POINT_EDIT_BEHAVIOR::UpdateItem( m_textbox, aEditedPoint, aPoints );
1869 }
1870 }
1871
1872private:
1874};
1875
1877 PCB_TOOL_BASE( "pcbnew.PointEditor" ),
1878 m_frame( nullptr ),
1879 m_selectionTool( nullptr ), m_editedPoint( nullptr ),
1880 m_hoveredPoint( nullptr ), m_original( VECTOR2I( 0, 0 ) ),
1882 m_altConstrainer( VECTOR2I( 0, 0 ) ), m_inPointEditorTool( false )
1883{
1884}
1885
1886
1888{
1889 m_frame = getEditFrame<PCB_BASE_FRAME>();
1890 m_editPoints.reset();
1891 m_altConstraint.reset();
1892 getViewControls()->SetAutoPan( false );
1893}
1894
1895
1899static bool canAddCorner( const EDA_ITEM& aItem )
1900{
1901 const auto type = aItem.Type();
1902
1903 if( type == PCB_ZONE_T )
1904 return true;
1905
1906 if( type == PCB_SHAPE_T )
1907 {
1908 const PCB_SHAPE& shape = static_cast<const PCB_SHAPE&>( aItem );
1909 const SHAPE_T shapeType = shape.GetShape();
1910 return shapeType == SHAPE_T::SEGMENT || shapeType == SHAPE_T::POLY
1911 || shapeType == SHAPE_T::ARC;
1912 }
1913
1914 return false;
1915}
1916
1921static bool canChamferCorner( const EDA_ITEM& aItem )
1922{
1923 const auto type = aItem.Type();
1924
1925 // Works only for zones and polygons
1926 if( type == PCB_ZONE_T )
1927 return true;
1928
1929 if( type == PCB_SHAPE_T )
1930 {
1931 const PCB_SHAPE& shape = static_cast<const PCB_SHAPE&>( aItem );
1932 const SHAPE_T shapeType = shape.GetShape();
1933 return shapeType == SHAPE_T::POLY;
1934 }
1935
1936 return false;
1937}
1938
1939
1941{
1942 // Find the selection tool, so they can cooperate
1944
1945 wxASSERT_MSG( m_selectionTool, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
1946
1947 const auto addCornerCondition = [&]( const SELECTION& aSelection ) -> bool
1948 {
1949 const EDA_ITEM* item = aSelection.Front();
1950 return ( item != nullptr ) && canAddCorner( *item );
1951 };
1952
1953 const auto addChamferCondition = [&]( const SELECTION& aSelection ) -> bool
1954 {
1955 const EDA_ITEM* item = aSelection.Front();
1956 return ( item != nullptr ) && canChamferCorner( *item );
1957 };
1958
1959 const auto removeCornerCondition = [&]( const SELECTION& aSelection ) -> bool
1960 {
1961 return PCB_POINT_EDITOR::removeCornerCondition( aSelection );
1962 };
1963
1964 using S_C = SELECTION_CONDITIONS;
1965
1966 auto& menu = m_selectionTool->GetToolMenu().GetMenu();
1967
1968 // clang-format off
1969 menu.AddItem( PCB_ACTIONS::pointEditorAddCorner, S_C::Count( 1 ) && addCornerCondition );
1971 menu.AddItem( PCB_ACTIONS::pointEditorChamferCorner, S_C::Count( 1 ) && addChamferCondition );
1972 // clang-format on
1973
1974 return true;
1975}
1976
1977
1978std::shared_ptr<EDIT_POINTS> PCB_POINT_EDITOR::makePoints( EDA_ITEM* aItem )
1979{
1980 std::shared_ptr<EDIT_POINTS> points = std::make_shared<EDIT_POINTS>( aItem );
1981
1982 if( !aItem )
1983 return points;
1984
1985 // Reset the behaviour and we'll make a new one
1986 m_editorBehavior = nullptr;
1987
1988 switch( aItem->Type() )
1989 {
1991 {
1992 PCB_REFERENCE_IMAGE& refImage = static_cast<PCB_REFERENCE_IMAGE&>( *aItem );
1993 m_editorBehavior = std::make_unique<REFERENCE_IMAGE_POINT_EDIT_BEHAVIOR>( refImage );
1994 break;
1995 }
1996 case PCB_TEXTBOX_T:
1997 {
1998 PCB_TEXTBOX& textbox = static_cast<PCB_TEXTBOX&>( *aItem );
1999 m_editorBehavior = std::make_unique<TEXTBOX_POINT_EDIT_BEHAVIOR>( textbox );
2000 break;
2001 }
2002 case PCB_SHAPE_T:
2003 {
2004 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
2005
2006 switch( shape->GetShape() )
2007 {
2008 case SHAPE_T::SEGMENT:
2009 m_editorBehavior = std::make_unique<EDA_SEGMENT_POINT_EDIT_BEHAVIOR>( *shape );
2010 break;
2011 case SHAPE_T::RECTANGLE:
2012 m_editorBehavior = std::make_unique<RECTANGLE_POINT_EDIT_BEHAVIOR>( *shape );
2013 break;
2014 case SHAPE_T::ARC:
2015 m_editorBehavior = std::make_unique<ARC_POINT_EDIT_BEHAVIOR>( *shape, m_arcEditMode,
2016 *getViewControls() );
2017 break;
2018
2019 case SHAPE_T::CIRCLE:
2020 m_editorBehavior = std::make_unique<EDA_CIRCLE_POINT_EDIT_BEHAVIOR>( *shape );
2021 break;
2022
2023 case SHAPE_T::POLY:
2024 m_editorBehavior = std::make_unique<EDA_POLYGON_POINT_EDIT_BEHAVIOR>( *shape );
2025 break;
2026
2027 case SHAPE_T::BEZIER:
2028 m_editorBehavior = std::make_unique<EDA_BEZIER_POINT_EDIT_BEHAVIOR>( *shape );
2029 break;
2030
2031 default: // suppress warnings
2032 break;
2033 }
2034
2035 break;
2036 }
2037
2038 case PCB_TABLECELL_T:
2039 {
2040 PCB_TABLECELL* cell = static_cast<PCB_TABLECELL*>( aItem );
2041 m_editorBehavior = std::make_unique<PCB_TABLECELL_POINT_EDIT_BEHAVIOR>( *cell );
2042 break;
2043 }
2044
2045 case PCB_PAD_T:
2046 {
2047 // Pad edit only for the footprint editor
2049 {
2050 PAD& pad = static_cast<PAD&>( *aItem );
2052
2053 // Point editor only handles copper shape changes
2054 if( !IsCopperLayer( activeLayer ) )
2055 activeLayer = IsFrontLayer( activeLayer ) ? F_Cu : B_Cu;
2056
2057 m_editorBehavior = std::make_unique<PAD_POINT_EDIT_BEHAVIOR>( pad, activeLayer );
2058 }
2059 break;
2060 }
2061
2062 case PCB_ZONE_T:
2063 {
2064 ZONE& zone = static_cast<ZONE&>( *aItem );
2065 m_editorBehavior = std::make_unique<ZONE_POINT_EDIT_BEHAVIOR>( zone );
2066 break;
2067 }
2068
2069 case PCB_GENERATOR_T:
2070 {
2071 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( aItem );
2072 m_editorBehavior = std::make_unique<GENERATOR_POINT_EDIT_BEHAVIOR>( *generator );
2073 break;
2074 }
2075
2076 case PCB_DIM_ALIGNED_T:
2078 {
2079 PCB_DIM_ALIGNED& dimension = static_cast<PCB_DIM_ALIGNED&>( *aItem );
2080 m_editorBehavior = std::make_unique<ALIGNED_DIMENSION_POINT_EDIT_BEHAVIOR>( dimension );
2081 break;
2082 }
2083
2084 case PCB_DIM_CENTER_T:
2085 {
2086 PCB_DIM_CENTER& dimension = static_cast<PCB_DIM_CENTER&>( *aItem );
2087 m_editorBehavior = std::make_unique<DIM_CENTER_POINT_EDIT_BEHAVIOR>( dimension );
2088 break;
2089 }
2090
2091 case PCB_DIM_RADIAL_T:
2092 {
2093 PCB_DIM_RADIAL& dimension = static_cast<PCB_DIM_RADIAL&>( *aItem );
2094 m_editorBehavior = std::make_unique<DIM_RADIAL_POINT_EDIT_BEHAVIOR>( dimension );
2095 break;
2096 }
2097
2098 case PCB_DIM_LEADER_T:
2099 {
2100 PCB_DIM_LEADER& dimension = static_cast<PCB_DIM_LEADER&>( *aItem );
2101 m_editorBehavior = std::make_unique<DIM_LEADER_POINT_EDIT_BEHAVIOR>( dimension );
2102 break;
2103 }
2104
2105 default:
2106 points.reset();
2107 break;
2108 }
2109
2110 if( m_editorBehavior )
2111 m_editorBehavior->MakePoints( *points );
2112
2113 return points;
2114}
2115
2116
2118{
2119 EDIT_POINT* point;
2120 EDIT_POINT* hovered = nullptr;
2121
2122 if( aEvent.IsMotion() )
2123 {
2124 point = m_editPoints->FindPoint( aEvent.Position(), getView() );
2125 hovered = point;
2126 }
2127 else if( aEvent.IsDrag( BUT_LEFT ) )
2128 {
2129 point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
2130 }
2131 else
2132 {
2133 point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
2134 }
2135
2136 if( hovered )
2137 {
2138 if( m_hoveredPoint != hovered )
2139 {
2140 if( m_hoveredPoint )
2141 m_hoveredPoint->SetHover( false );
2142
2143 m_hoveredPoint = hovered;
2145 }
2146 }
2147 else if( m_hoveredPoint )
2148 {
2149 m_hoveredPoint->SetHover( false );
2150 m_hoveredPoint = nullptr;
2151 }
2152
2153 if( m_editedPoint != point )
2154 setEditedPoint( point );
2155}
2156
2157
2159{
2161 return 0;
2162
2164 return 0;
2165
2167
2168 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2170
2171 if( selection.Size() != 1 || selection.Front()->GetEditFlags() )
2172 return 0;
2173
2174 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
2175
2176 if( !item || item->IsLocked() )
2177 return 0;
2178
2179 Activate();
2180 // Must be done after Activate() so that it gets set into the correct context
2181 getViewControls()->ShowCursor( true );
2182
2184
2185 // Use the original object as a construction item
2186 std::vector<std::unique_ptr<BOARD_ITEM>> clones;
2187
2188 m_editorBehavior.reset();
2189 // Will also make the edit behavior if supported
2190 m_editPoints = makePoints( item );
2191
2192 if( !m_editPoints )
2193 return 0;
2194
2196 getView()->Add( &m_preview );
2197
2198 getView()->Add( m_editPoints.get() );
2199 setEditedPoint( nullptr );
2200 updateEditedPoint( aEvent );
2201 bool inDrag = false;
2202
2203 BOARD_COMMIT commit( editFrame );
2204
2205 // Main loop: keep receiving events
2206 while( TOOL_EVENT* evt = Wait() )
2207 {
2208 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2209 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2210
2211 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
2212 {
2214 }
2215 else
2216 {
2218 }
2219
2220 if( !m_editPoints || evt->IsSelectionEvent() ||
2221 evt->Matches( EVENTS::InhibitSelectionEditing ) )
2222 {
2223 break;
2224 }
2225
2226 EDIT_POINT* prevHover = m_hoveredPoint;
2227
2228 if( !inDrag )
2229 updateEditedPoint( *evt );
2230
2231 if( prevHover != m_hoveredPoint )
2232 getView()->Update( m_editPoints.get() );
2233
2234 if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
2235 {
2236 if( !inDrag )
2237 {
2238 frame()->UndoRedoBlock( true );
2239
2240 if( item->Type() == PCB_GENERATOR_T )
2241 {
2243 static_cast<PCB_GENERATOR*>( item ) );
2244 }
2245
2247 m_original = *m_editedPoint; // Save the original position
2248 getViewControls()->SetAutoPan( true );
2249 inDrag = true;
2250
2252 grid.SetAuxAxes( true, m_original.GetPosition() );
2253
2254 setAltConstraint( true );
2256
2257 for( size_t ii = 0; ii < m_editPoints->PointsSize(); ++ii )
2258 {
2259 EDIT_POINT& point = m_editPoints->Point( ii );
2260
2261 if( &point != m_editedPoint )
2262 point.SetActive( false );
2263 }
2264
2265 // When we start dragging, create a clone of the item to use as the original
2266 // reference geometry (e.g. for intersections and extensions)
2267 BOARD_ITEM* clone = static_cast<BOARD_ITEM*>( item->Clone() );
2268 clone->SetParent( nullptr );
2269 clone->SetParentGroup( nullptr );
2270 clones.emplace_back( clone );
2271 grid.AddConstructionItems( { clone }, false, true );
2272 }
2273
2274 // Keep point inside of limits with some padding
2275 VECTOR2I pos = GetClampedCoords<double, int>( evt->Position(), COORDS_PADDING );
2276 LSET snapLayers;
2277
2278 switch( m_editedPoint->GetSnapConstraint() )
2279 {
2280 case IGNORE_SNAPS: break;
2281 case OBJECT_LAYERS: snapLayers = item->GetLayerSet(); break;
2282 case ALL_LAYERS: snapLayers = LSET::AllLayersMask(); break;
2283 }
2284
2286 {
2287 if( grid.GetUseGrid() )
2288 {
2289 VECTOR2I gridPt = grid.BestSnapAnchor( pos, {}, grid.GetItemGrid( item ),
2290 { item } );
2291
2293 VECTOR2I delta = pos - last;
2294 VECTOR2I deltaGrid = gridPt - grid.BestSnapAnchor( last, {},
2295 grid.GetItemGrid( item ),
2296 { item } );
2297
2298 if( abs( delta.x ) > grid.GetGrid().x / 2 )
2299 pos.x = last.x + deltaGrid.x;
2300 else
2301 pos.x = last.x;
2302
2303 if( abs( delta.y ) > grid.GetGrid().y / 2 )
2304 pos.y = last.y + deltaGrid.y;
2305 else
2306 pos.y = last.y;
2307 }
2308 }
2309
2310 m_editedPoint->SetPosition( pos );
2311
2312 // Constrain edited line midpoints to move normal to themselves
2313 if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
2314 {
2315 m_altConstraint->Apply( grid );
2316 }
2317 else if( m_editedPoint->IsConstrained() )
2318 {
2320 }
2322 {
2324 snapLayers,
2325 grid.GetItemGrid( item ),
2326 { item } ) );
2327 }
2328
2329 updateItem( commit );
2331 updatePoints();
2332 }
2333 else if( m_editedPoint && evt->Action() == TA_MOUSE_DOWN && evt->Buttons() == BUT_LEFT )
2334 {
2336
2337 for( size_t ii = 0; ii < m_editPoints->PointsSize(); ++ii )
2338 {
2339 EDIT_POINT& point = m_editPoints->Point( ii );
2340
2341 if( &point != m_editedPoint )
2342 point.SetActive( false );
2343 }
2344
2345 getView()->Update( m_editPoints.get() );
2346 }
2347 else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
2348 {
2349 if( m_editedPoint )
2350 {
2351 m_editedPoint->SetActive( false );
2352 getView()->Update( m_editPoints.get() );
2353 }
2354
2355 getViewControls()->SetAutoPan( false );
2356 setAltConstraint( false );
2357
2358 if( item->Type() == PCB_GENERATOR_T )
2359 {
2362 static_cast<PCB_GENERATOR*>( item ) );
2363 }
2364 else if( item->Type() == PCB_TABLECELL_T )
2365 {
2366 commit.Push( _( "Resize Table Cells" ) );
2367 }
2368 else
2369 {
2370 commit.Push( _( "Move Point" ) );
2371 }
2372
2373 inDrag = false;
2374 frame()->UndoRedoBlock( false );
2375
2377 item ); // FIXME: Needed for generators
2378 }
2379 else if( evt->IsCancelInteractive() || evt->IsActivate() )
2380 {
2381 if( inDrag ) // Restore the last change
2382 {
2383 if( item->Type() == PCB_GENERATOR_T )
2384 {
2386 static_cast<PCB_GENERATOR*>( item ) );
2387 }
2388 commit.Revert();
2389
2390 inDrag = false;
2391 frame()->UndoRedoBlock( false );
2392 }
2393
2394 // Only cancel point editor when activating a new tool
2395 // Otherwise, allow the points to persist when moving up the
2396 // tool stack
2397 if( evt->IsActivate() && !evt->IsMoveTool() )
2398 break;
2399 }
2400 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2401 {
2402 // Re-create the points for items which can have different behavior on different layers
2403 if( item->Type() == PCB_PAD_T && m_isFootprintEditor )
2404 {
2405 getView()->Remove( m_editPoints.get() );
2406 m_editPoints = makePoints( item );
2407 getView()->Add( m_editPoints.get() );
2408 }
2409 }
2410 else if( evt->Action() == TA_UNDO_REDO_POST )
2411 {
2412 break;
2413 }
2414 else
2415 {
2416 evt->SetPassEvent();
2417 }
2418 }
2419
2421 getView()->Remove( &m_preview );
2422
2423 if( m_editPoints )
2424 {
2425 getView()->Remove( m_editPoints.get() );
2426 m_editPoints.reset();
2427 }
2428
2429 m_editedPoint = nullptr;
2430
2431 return 0;
2432}
2433
2434
2436{
2437 if( !m_editPoints || !m_editPoints->GetParent() || !HasPoint() )
2438 return 0;
2439
2440 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2441
2442 BOARD_COMMIT commit( editFrame );
2443 commit.Stage( m_editPoints->GetParent(), CHT_MODIFY );
2444
2446 wxString title;
2447 wxString msg;
2448
2449 if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
2450 {
2451 title = _( "Move Midpoint to Location" );
2452 msg = _( "Move Midpoint" );
2453 }
2454 else
2455 {
2456 title = _( "Move Corner to Location" );
2457 msg = _( "Move Corner" );
2458 }
2459
2460 WX_PT_ENTRY_DIALOG dlg( editFrame, title, _( "X:" ), _( "Y:" ), pt );
2461
2462 if( dlg.ShowModal() == wxID_OK )
2463 {
2465 updateItem( commit );
2466 commit.Push( msg );
2467 }
2468
2469 return 0;
2470}
2471
2472
2474{
2475 wxCHECK( m_editPoints, /* void */ );
2476 EDA_ITEM* item = m_editPoints->GetParent();
2477
2478 if( !item )
2479 return;
2480
2481 // item is always updated
2482 std::vector<EDA_ITEM*> updatedItems = { item };
2483 aCommit.Modify( item );
2484
2485 if( m_editorBehavior )
2486 {
2487 wxCHECK( m_editedPoint, /* void */ );
2488 m_editorBehavior->UpdateItem( *m_editedPoint, *m_editPoints, aCommit, updatedItems );
2489 }
2490
2491 // Perform any post-edit actions that the item may require
2492
2493 switch( item->Type() )
2494 {
2495 case PCB_TEXTBOX_T:
2496 case PCB_SHAPE_T:
2497 {
2498 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2499
2500 if( shape->IsProxyItem() )
2501 {
2502 for( PAD* pad : shape->GetParentFootprint()->Pads() )
2503 {
2504 if( pad->IsEntered() )
2505 view()->Update( pad );
2506 }
2507 }
2508
2509 // Nuke outline font render caches
2510 if( PCB_TEXTBOX* textBox = dynamic_cast<PCB_TEXTBOX*>( item ) )
2511 textBox->ClearRenderCache();
2512
2513 break;
2514 }
2515 case PCB_GENERATOR_T:
2516 {
2517 GENERATOR_TOOL* generatorTool = m_toolMgr->GetTool<GENERATOR_TOOL>();
2518 PCB_GENERATOR* generatorItem = static_cast<PCB_GENERATOR*>( item );
2519
2520 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genUpdateEdit, &aCommit, generatorItem );
2521
2522 // Note: POINT_EDITOR::m_preview holds only the canvas-draw status "popup"; the meanders
2523 // themselves (ROUTER_PREVIEW_ITEMs) are owned by the router.
2524
2526
2527 for( EDA_ITEM* previewItem : generatorItem->GetPreviewItems( generatorTool, frame(),
2529 {
2530 m_preview.Add( previewItem );
2531 }
2532
2533 getView()->Update( &m_preview );
2534 break;
2535 }
2536 default:
2537 break;
2538 }
2539
2540 // Update the item and any affected items
2541 for( EDA_ITEM* updatedItem : updatedItems )
2542 {
2543 getView()->Update( updatedItem );
2544 }
2545
2546 frame()->SetMsgPanel( item );
2547}
2548
2549
2551{
2552 if( !m_editPoints )
2553 return;
2554
2555 EDA_ITEM* item = m_editPoints->GetParent();
2556
2557 if( !item )
2558 return;
2559
2560 if( !m_editorBehavior )
2561 return;
2562
2563 m_editorBehavior->UpdatePoints( *m_editPoints );
2564 getView()->Update( m_editPoints.get() );
2565}
2566
2567
2569{
2571
2572 if( aPoint )
2573 {
2574 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2575 controls->ForceCursorPosition( true, aPoint->GetPosition() );
2576 controls->ShowCursor( true );
2577 }
2578 else
2579 {
2580 if( frame()->ToolStackIsEmpty() )
2581 controls->ShowCursor( false );
2582
2583 controls->ForceCursorPosition( false );
2584 }
2585
2586 m_editedPoint = aPoint;
2587}
2588
2589
2591{
2592 if( aEnabled )
2593 {
2594 EDA_ITEM* parent = m_editPoints->GetParent();
2595 EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
2596 bool isPoly;
2597
2598 switch( parent->Type() )
2599 {
2600 case PCB_ZONE_T:
2601 isPoly = true;
2602 break;
2603
2604 case PCB_SHAPE_T:
2605 isPoly = static_cast<PCB_SHAPE*>( parent )->GetShape() == SHAPE_T::POLY;
2606 break;
2607
2608 default:
2609 isPoly = false;
2610 break;
2611 }
2612
2613 if( line && isPoly )
2614 {
2615 EC_CONVERGING* altConstraint = new EC_CONVERGING( *line, *m_editPoints );
2616 m_altConstraint.reset( (EDIT_CONSTRAINT<EDIT_POINT>*) altConstraint );
2617 }
2618 else
2619 {
2620 // Find a proper constraining point for 45 degrees mode
2623 }
2624 }
2625 else
2626 {
2627 m_altConstraint.reset();
2628 }
2629}
2630
2631
2633{
2634 // If there's a behaviour and it provides a constrainer, use that
2635 if( m_editorBehavior )
2636 {
2637 const OPT_VECTOR2I constrainer =
2638 m_editorBehavior->Get45DegreeConstrainer( *m_editedPoint, *m_editPoints );
2639 if( constrainer )
2640 return EDIT_POINT( *constrainer );
2641 }
2642
2643 // In any other case we may align item to its original position
2644 return m_original;
2645}
2646
2647
2648// Finds a corresponding vertex in a polygon set
2649static std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX>
2650findVertex( SHAPE_POLY_SET& aPolySet, const EDIT_POINT& aPoint )
2651{
2652 for( auto it = aPolySet.IterateWithHoles(); it; ++it )
2653 {
2654 auto vertexIdx = it.GetIndex();
2655
2656 if( aPolySet.CVertex( vertexIdx ) == aPoint.GetPosition() )
2657 return std::make_pair( true, vertexIdx );
2658 }
2659
2660 return std::make_pair( false, SHAPE_POLY_SET::VERTEX_INDEX() );
2661}
2662
2663
2665{
2666 if( !m_editPoints || !m_editedPoint )
2667 return false;
2668
2669 EDA_ITEM* item = m_editPoints->GetParent();
2670 SHAPE_POLY_SET* polyset = nullptr;
2671
2672 if( !item )
2673 return false;
2674
2675 switch( item->Type() )
2676 {
2677 case PCB_ZONE_T:
2678 polyset = static_cast<ZONE*>( item )->Outline();
2679 break;
2680
2681 case PCB_SHAPE_T:
2682 if( static_cast<PCB_SHAPE*>( item )->GetShape() == SHAPE_T::POLY )
2683 polyset = &static_cast<PCB_SHAPE*>( item )->GetPolyShape();
2684 else
2685 return false;
2686
2687 break;
2688
2689 default:
2690 return false;
2691 }
2692
2693 std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX> vertex = findVertex( *polyset, *m_editedPoint );
2694
2695 if( !vertex.first )
2696 return false;
2697
2698 const SHAPE_POLY_SET::VERTEX_INDEX& vertexIdx = vertex.second;
2699
2700 // Check if there are enough vertices so one can be removed without
2701 // degenerating the polygon.
2702 // The first condition allows one to remove all corners from holes (when
2703 // there are only 2 vertices left, a hole is removed).
2704 if( vertexIdx.m_contour == 0 &&
2705 polyset->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour].PointCount() <= 3 )
2706 {
2707 return false;
2708 }
2709
2710 // Remove corner does not work with lines
2711 if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
2712 return false;
2713
2714 return m_editedPoint != nullptr;
2715}
2716
2717
2719{
2720 if( !m_editPoints )
2721 return 0;
2722
2723 EDA_ITEM* item = m_editPoints->GetParent();
2724 PCB_BASE_EDIT_FRAME* frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2725 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
2726
2727 // called without an active edited polygon
2728 if( !item || !canAddCorner( *item ) )
2729 return 0;
2730
2731 PCB_SHAPE* graphicItem = dynamic_cast<PCB_SHAPE*>( item );
2732 BOARD_COMMIT commit( frame );
2733
2734 if( item->Type() == PCB_ZONE_T || ( graphicItem && graphicItem->GetShape() == SHAPE_T::POLY ) )
2735 {
2736 unsigned int nearestIdx = 0;
2737 unsigned int nextNearestIdx = 0;
2738 unsigned int nearestDist = INT_MAX;
2739 unsigned int firstPointInContour = 0;
2740 SHAPE_POLY_SET* zoneOutline;
2741
2742 if( item->Type() == PCB_ZONE_T )
2743 {
2744 ZONE* zone = static_cast<ZONE*>( item );
2745 zoneOutline = zone->Outline();
2746 zone->SetNeedRefill( true );
2747 }
2748 else
2749 {
2750 zoneOutline = &( graphicItem->GetPolyShape() );
2751 }
2752
2753 commit.Modify( item );
2754
2755 // Search the best outline segment to add a new corner
2756 // and therefore break this segment into two segments
2757
2758 // Object to iterate through the corners of the outlines (main contour and its holes)
2759 SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0, zoneOutline->OutlineCount()-1,
2760 /* IterateHoles */ true );
2761 int curr_idx = 0;
2762
2763 // Iterate through all the corners of the outlines and search the best segment
2764 for( ; iterator; iterator++, curr_idx++ )
2765 {
2766 int jj = curr_idx+1;
2767
2768 if( iterator.IsEndContour() )
2769 { // We reach the last point of the current contour (main or hole)
2770 jj = firstPointInContour;
2771 firstPointInContour = curr_idx+1; // Prepare next contour analysis
2772 }
2773
2774 SEG curr_segment( zoneOutline->CVertex( curr_idx ), zoneOutline->CVertex( jj ) );
2775
2776 unsigned int distance = curr_segment.Distance( cursorPos );
2777
2778 if( distance < nearestDist )
2779 {
2780 nearestDist = distance;
2781 nearestIdx = curr_idx;
2782 nextNearestIdx = jj;
2783 }
2784 }
2785
2786 // Find the point on the closest segment
2787 const VECTOR2I& sideOrigin = zoneOutline->CVertex( nearestIdx );
2788 const VECTOR2I& sideEnd = zoneOutline->CVertex( nextNearestIdx );
2789 SEG nearestSide( sideOrigin, sideEnd );
2790 VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
2791
2792 // Do not add points that have the same coordinates as ones that already belong to polygon
2793 // instead, add a point in the middle of the side
2794 if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
2795 nearestPoint = ( sideOrigin + sideEnd ) / 2;
2796
2797 zoneOutline->InsertVertex( nextNearestIdx, nearestPoint );
2798
2799 // We re-hatch the filled zones but not polygons
2800 if( item->Type() == PCB_ZONE_T )
2801 static_cast<ZONE*>( item )->HatchBorder();
2802
2803 commit.Push( _( "Add Zone Corner" ) );
2804 }
2805 else if( graphicItem )
2806 {
2807 switch( graphicItem->GetShape() )
2808 {
2809 case SHAPE_T::SEGMENT:
2810 {
2811 commit.Modify( graphicItem );
2812
2813 SEG seg( graphicItem->GetStart(), graphicItem->GetEnd() );
2814 VECTOR2I nearestPoint = seg.NearestPoint( cursorPos );
2815
2816 // Move the end of the line to the break point..
2817 graphicItem->SetEnd( nearestPoint );
2818
2819 // and add another one starting from the break point
2820 PCB_SHAPE* newSegment = static_cast<PCB_SHAPE*>( graphicItem->Duplicate() );
2821
2822 newSegment->ClearSelected();
2823 newSegment->SetStart( nearestPoint );
2824 newSegment->SetEnd( VECTOR2I( seg.B.x, seg.B.y ) );
2825
2826 commit.Add( newSegment );
2827 commit.Push( _( "Split Segment" ) );
2828 break;
2829 }
2830 case SHAPE_T::ARC:
2831 {
2832 commit.Modify( graphicItem );
2833
2834 const SHAPE_ARC arc( graphicItem->GetStart(), graphicItem->GetArcMid(),
2835 graphicItem->GetEnd(), 0 );
2836 const VECTOR2I nearestPoint = arc.NearestPoint( cursorPos );
2837
2838 // Move the end of the arc to the break point..
2839 graphicItem->SetEnd( nearestPoint );
2840
2841 // and add another one starting from the break point
2842 PCB_SHAPE* newArc = static_cast<PCB_SHAPE*>( graphicItem->Duplicate() );
2843
2844 newArc->ClearSelected();
2845 newArc->SetEnd( arc.GetP1() );
2846 newArc->SetStart( nearestPoint );
2847
2848 commit.Add( newArc );
2849 commit.Push( _( "Split Arc" ) );
2850 break;
2851 }
2852 default:
2853 // No split implemented for other shapes
2854 break;
2855 }
2856 }
2857
2858 updatePoints();
2859 return 0;
2860}
2861
2862
2864{
2865 if( !m_editPoints || !m_editedPoint )
2866 return 0;
2867
2868 EDA_ITEM* item = m_editPoints->GetParent();
2869
2870 if( !item )
2871 return 0;
2872
2873 SHAPE_POLY_SET* polygon = nullptr;
2874
2875 if( item->Type() == PCB_ZONE_T )
2876 {
2877 ZONE* zone = static_cast<ZONE*>( item );
2878 polygon = zone->Outline();
2879 zone->SetNeedRefill( true );
2880 }
2881 else if( item->Type() == PCB_SHAPE_T )
2882 {
2883 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2884
2885 if( shape->GetShape() == SHAPE_T::POLY )
2886 polygon = &shape->GetPolyShape();
2887 }
2888
2889 if( !polygon )
2890 return 0;
2891
2892 PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
2893 BOARD_COMMIT commit( frame );
2894 auto vertex = findVertex( *polygon, *m_editedPoint );
2895
2896 if( vertex.first )
2897 {
2898 const auto& vertexIdx = vertex.second;
2899 auto& outline = polygon->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour];
2900
2901 if( outline.PointCount() > 3 )
2902 {
2903 // the usual case: remove just the corner when there are >3 vertices
2904 commit.Modify( item );
2905 polygon->RemoveVertex( vertexIdx );
2906 }
2907 else
2908 {
2909 // either remove a hole or the polygon when there are <= 3 corners
2910 if( vertexIdx.m_contour > 0 )
2911 {
2912 // remove hole
2913 commit.Modify( item );
2914 polygon->RemoveContour( vertexIdx.m_contour );
2915 }
2916 else
2917 {
2919 commit.Remove( item );
2920 }
2921 }
2922
2923 setEditedPoint( nullptr );
2924
2925 if( item->Type() == PCB_ZONE_T )
2926 commit.Push( _( "Remove Zone Corner" ) );
2927 else
2928 commit.Push( _( "Remove Polygon Corner" ) );
2929
2930 // Refresh zone hatching
2931 if( item->Type() == PCB_ZONE_T )
2932 static_cast<ZONE*>( item )->HatchBorder();
2933
2934 updatePoints();
2935 }
2936
2937 return 0;
2938}
2939
2940
2942{
2943 if( !m_editPoints || !m_editedPoint )
2944 return 0;
2945
2946 EDA_ITEM* item = m_editPoints->GetParent();
2947
2948 if( !item )
2949 return 0;
2950
2951 SHAPE_POLY_SET* polygon = nullptr;
2952
2953 if( item->Type() == PCB_ZONE_T )
2954 {
2955 ZONE* zone = static_cast<ZONE*>( item );
2956 polygon = zone->Outline();
2957 zone->SetNeedRefill( true );
2958 }
2959 else if( item->Type() == PCB_SHAPE_T )
2960 {
2961 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2962
2963 if( shape->GetShape() == SHAPE_T::POLY )
2964 polygon = &shape->GetPolyShape();
2965 }
2966
2967 if( !polygon )
2968 return 0;
2969
2970 // Search the best outline corner to break
2971
2972 PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
2973 BOARD_COMMIT commit( frame );
2974 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
2975
2976 unsigned int nearestIdx = 0;
2977 unsigned int nearestDist = INT_MAX;
2978
2979 int curr_idx = 0;
2980 // Object to iterate through the corners of the outlines (main contour and its holes)
2981 SHAPE_POLY_SET::ITERATOR iterator = polygon->Iterate( 0, polygon->OutlineCount() - 1,
2982 /* IterateHoles */ true );
2983
2984 // Iterate through all the corners of the outlines and search the best segment
2985 for( ; iterator; iterator++, curr_idx++ )
2986 {
2987 unsigned int distance = polygon->CVertex( curr_idx ).Distance( cursorPos );
2988
2989 if( distance < nearestDist )
2990 {
2991 nearestDist = distance;
2992 nearestIdx = curr_idx;
2993 }
2994 }
2995
2996 int prevIdx, nextIdx;
2997 if( polygon->GetNeighbourIndexes( nearestIdx, &prevIdx, &nextIdx ) )
2998 {
2999 const SEG segA{ polygon->CVertex( prevIdx ), polygon->CVertex( nearestIdx ) };
3000 const SEG segB{ polygon->CVertex( nextIdx ), polygon->CVertex( nearestIdx ) };
3001
3002 // A plausible setback that won't consume a whole edge
3003 int setback = pcbIUScale.mmToIU( 5 );
3004 setback = std::min( setback, (int) ( segA.Length() * 0.25 ) );
3005 setback = std::min( setback, (int) ( segB.Length() * 0.25 ) );
3006
3007 CHAMFER_PARAMS chamferParams{ setback, setback };
3008
3009 std::optional<CHAMFER_RESULT> chamferResult =
3010 ComputeChamferPoints( segA, segB, chamferParams );
3011
3012 if( chamferResult && chamferResult->m_updated_seg_a && chamferResult->m_updated_seg_b )
3013 {
3014 commit.Modify( item );
3015 polygon->RemoveVertex( nearestIdx );
3016
3017 // The two end points of the chamfer are the new corners
3018 polygon->InsertVertex( nearestIdx, chamferResult->m_updated_seg_b->B );
3019 polygon->InsertVertex( nearestIdx, chamferResult->m_updated_seg_a->B );
3020 }
3021 }
3022
3023 setEditedPoint( nullptr );
3024
3025 if( item->Type() == PCB_ZONE_T )
3026 commit.Push( _( "Break Zone Corner" ) );
3027 else
3028 commit.Push( _( "Break Polygon Corner" ) );
3029
3030 // Refresh zone hatching
3031 if( item->Type() == PCB_ZONE_T )
3032 static_cast<ZONE*>( item )->HatchBorder();
3033
3034 updatePoints();
3035
3036 return 0;
3037}
3038
3039
3041{
3042 updatePoints();
3043 return 0;
3044}
3045
3046
3048{
3049 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
3050
3052 {
3053 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
3055 else
3057
3058 switch( m_arcEditMode )
3059 {
3060 case ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS:
3061 m_arcEditMode = ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION;
3062 break;
3063 case ARC_EDIT_MODE::KEEP_ENDPOINTS_OR_START_DIRECTION:
3064 m_arcEditMode = ARC_EDIT_MODE::KEEP_CENTER_ADJUST_ANGLE_RADIUS;
3065 break;
3066 }
3067 }
3068 else
3069 {
3071 }
3072
3073 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
3075 else
3077
3078 return 0;
3079}
3080
3081
3083{
3100}
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
Execute the changes.
COMMIT & Stage(EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen=nullptr) override
Add a change of the item aItem of type aChangeType to the change list.
virtual void Revert() override
Revert the commit by restoring the modified items state.
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:243
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:298
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:259
virtual bool IsLocked() const
Definition: board_item.cpp:75
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:217
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)
Remove a new item from the model.
Definition: commit.h:92
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Modify a given item in the model.
Definition: commit.h:108
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
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 IsHorizontal() const
Definition: eda_angle.h:138
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:792
virtual VECTOR2I GetTopLeft() const
Definition: eda_shape.h:201
int GetEndX() const
Definition: eda_shape.h:176
int GetRectangleWidth() const
Definition: eda_shape.cpp:419
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:291
SHAPE_T GetShape() const
Definition: eda_shape.h:132
virtual VECTOR2I GetBotRight() const
Definition: eda_shape.h:202
virtual void SetBottom(int val)
Definition: eda_shape.h:207
virtual void SetTop(int val)
Definition: eda_shape.h:204
int GetEndY() const
Definition: eda_shape.h:175
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:174
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:141
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:137
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:178
void SetArcGeometry(const VECTOR2I &aStart, const VECTOR2I &aMid, const VECTOR2I &aEnd)
Set the three controlling points for an arc.
Definition: eda_shape.cpp:873
virtual void SetLeft(int val)
Definition: eda_shape.h:205
int GetRectangleHeight() const
Definition: eda_shape.cpp:405
virtual void SetRight(int val)
Definition: eda_shape.h:206
VECTOR2I GetArcMid() const
Definition: eda_shape.cpp:810
"Standard" table-cell editing behavior.
const VECTOR2I & GetTextPos() const
Definition: eda_text.h:260
const EDA_ANGLE & GetTextAngle() const
Definition: eda_text.h:134
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:569
GR_TEXT_H_ALIGN_T GetHorizJustify() const
Definition: eda_text.h:187
void SetHorizJustify(GR_TEXT_H_ALIGN_T aType)
Definition: eda_text.cpp:400
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:579
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:575
bool SwapY() const
Definition: edit_points.h:578
void SetSwapX(bool aSwap)
Definition: edit_points.h:576
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:204
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:211
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:297
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:332
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:1673
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
static LSET AllLayersMask()
Definition: lset.cpp:593
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:303
bool IsLocked() const override
Definition: pad.cpp:231
VECTOR2I GetPosition() const override
Definition: pad.h:206
PAD_SHAPE GetShape(PCB_LAYER_ID aLayer) const
Definition: pad.h:193
void SetOffset(PCB_LAYER_ID aLayer, const VECTOR2I &aOffset)
Definition: pad.h:309
void SetPosition(const VECTOR2I &aPos) override
Definition: pad.h:200
const VECTOR2I & GetOffset(PCB_LAYER_ID aLayer) const
Definition: pad.h:315
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition: pad.h:406
void SetSize(PCB_LAYER_ID aLayer, const VECTOR2I &aSize)
Definition: pad.h:257
VECTOR2I ShapePos(PCB_LAYER_ID aLayer) const
Definition: pad.cpp:1016
const VECTOR2I & GetSize(PCB_LAYER_ID aLayer) const
Definition: pad.h:262
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.
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 "Remove 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)
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)
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:387
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:120
void Normalize() override
Perform any normalization required after a user rotate and/or flip.
Definition: pcb_table.cpp:130
int GetColWidth(int aCol) const
Definition: pcb_table.h:122
void SetRowHeight(int aRow, int aHeight)
Definition: pcb_table.h:130
int GetRowHeight(int aRow) const
Definition: pcb_table.h:132
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:80
VECTOR2I GetCenter() const
Definition: sch_shape.h:75
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
Class that groups generic conditions for selected items.
static SELECTION_CONDITION Count(int aNumber)
Create a functor that tests if the number of selected items is equal to the value given as parameter.
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:427
const VECTOR2I & GetP1() const
Definition: shape_arc.h:117
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:44
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:38
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
Generic, UI-independent tool event.
Definition: tool_event.h:168
bool Matches(const TOOL_EVENT &aEvent) const
Test whether two events match in terms of category & action or command.
Definition: tool_event.h:389
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition: tool_event.h:290
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition: tool_event.h:312
const VECTOR2D DragOrigin() const
Return the point where dragging has started.
Definition: tool_event.h:296
T Parameter() const
Return a parameter assigned to the event.
Definition: tool_event.h:465
bool IsMotion() const
Definition: tool_event.h:327
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:546
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition: vector2d.h:561
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:153
T x
Definition: vector3.h:63
VECTOR2I GetValue()
Return 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:275
bool UnFill()
Removes the zone filling.
Definition: zone.cpp:418
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:1143
SHAPE_POLY_SET * Outline()
Definition: zone.h:347
@ 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:43
@ 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:719
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:617
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:159
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)
static bool canAddCorner(const EDA_ITEM &aItem)
Condition to check if a point editor can add a corner to the given item.
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
static bool canChamferCorner(const EDA_ITEM &aItem)
Condition to check if a point editor can add a chamfer to a corner of the given item.
#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
Holding struct to keep originating midpoint.
Definition: eda_shape.h:66
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 center
int radius
VECTOR2I end
VECTOR2I v2(1, 0)
VECTOR2I v4(1, 1)
VECTOR2I v3(-2, 1)
constexpr int delta
GR_TEXT_H_ALIGN_T
This is API surface mapped to common.types.HorizontalAlignment.
@ TA_MOUSE_DOWN
Definition: tool_event.h:70
@ TA_UNDO_REDO_POST
This event is sent after undo/redo command is performed.
Definition: tool_event.h:109
@ MD_SHIFT
Definition: tool_event.h:143
@ BUT_LEFT
Definition: tool_event.h:132
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:695
VECTOR2< double > VECTOR2D
Definition: vector2d.h:694
Supplemental functions for working with vectors and simple objects that interact with vectors.