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#include <algorithm>
29#include <limits>
30
31using namespace std::placeholders;
32#include <advanced_config.h>
33#include <kiplatform/ui.h>
34#include <view/view_controls.h>
38#include <geometry/seg.h>
40#include <math/util.h>
41#include <confirm.h>
42#include <tool/tool_manager.h>
46#include <tools/pcb_actions.h>
52#include <board_commit.h>
53#include <pcb_edit_frame.h>
54#include <pcb_reference_image.h>
55#include <pcb_generator.h>
56#include <pcb_group.h>
57#include <pcb_dimension.h>
58#include <pcb_barcode.h>
59#include <pcb_textbox.h>
60#include <pcb_tablecell.h>
61#include <pcb_table.h>
62#include <pad.h>
63#include <zone.h>
64#include <footprint.h>
67#include <progress_reporter.h>
68#include <layer_ids.h>
70
71const unsigned int PCB_POINT_EDITOR::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
72
73static void appendDirection( std::vector<VECTOR2I>& aDirections, const VECTOR2I& aDirection )
74{
75 if( aDirection.x != 0 || aDirection.y != 0 )
76 aDirections.push_back( aDirection );
77}
78
79static std::vector<VECTOR2I> getConstraintDirections( EDIT_CONSTRAINT<EDIT_POINT>* aConstraint )
80{
81 std::vector<VECTOR2I> directions;
82
83 if( !aConstraint )
84 return directions;
85
86 if( dynamic_cast<EC_90DEGREE*>( aConstraint ) )
87 {
88 appendDirection( directions, VECTOR2I( 1, 0 ) );
89 appendDirection( directions, VECTOR2I( 0, 1 ) );
90 }
91 else if( dynamic_cast<EC_45DEGREE*>( aConstraint ) )
92 {
93 appendDirection( directions, VECTOR2I( 1, 0 ) );
94 appendDirection( directions, VECTOR2I( 0, 1 ) );
95 appendDirection( directions, VECTOR2I( 1, 1 ) );
96 appendDirection( directions, VECTOR2I( 1, -1 ) );
97 }
98 else if( dynamic_cast<EC_VERTICAL*>( aConstraint ) )
99 {
100 appendDirection( directions, VECTOR2I( 0, 1 ) );
101 }
102 else if( dynamic_cast<EC_HORIZONTAL*>( aConstraint ) )
103 {
104 appendDirection( directions, VECTOR2I( 1, 0 ) );
105 }
106 else if( EC_LINE* lineConstraint = dynamic_cast<EC_LINE*>( aConstraint ) )
107 {
108 appendDirection( directions, lineConstraint->GetLineVector() );
109 }
110
111 return directions;
112}
113
114// Few constants to avoid using bare numbers for point indices
126
127
132
133
148
149
156
157
159{
160public:
161 RECT_RADIUS_TEXT_ITEM( const EDA_IU_SCALE& aIuScale, EDA_UNITS aUnits ) :
163 m_iuScale( aIuScale ),
164 m_units( aUnits ),
165 m_radius( 0 ),
166 m_corner(),
167 m_quadrant( -1, 1 ),
168 m_visible( false )
169 {
170 }
171
172 const BOX2I ViewBBox() const override
173 {
174 BOX2I tmp;
175 tmp.SetMaximum();
176 return tmp;
177 }
178
179 std::vector<int> ViewGetLayers() const override
180 {
182 }
183
184 void ViewDraw( int aLayer, KIGFX::VIEW* aView ) const override
185 {
186 if( !m_visible )
187 return;
188
189 wxArrayString strings;
190 strings.push_back( KIGFX::PREVIEW::DimensionLabel( "r", m_radius, m_iuScale, m_units ) );
192 aLayer == LAYER_SELECT_OVERLAY );
193 }
194
195 void Set( int aRadius, const VECTOR2I& aCorner, const VECTOR2I& aQuadrant, EDA_UNITS aUnits )
196 {
197 m_radius = aRadius;
198 m_corner = aCorner;
199 m_quadrant = aQuadrant;
200 m_units = aUnits;
201 m_visible = true;
202 }
203
204 void Hide()
205 {
206 m_visible = false;
207 }
208
209 wxString GetClass() const override
210 {
211 return wxT( "RECT_RADIUS_TEXT_ITEM" );
212 }
213
214private:
221};
222
223
225{
226public:
228 m_rectangle( aRectangle )
229 {
230 wxASSERT( m_rectangle.GetShape() == SHAPE_T::RECTANGLE );
231 }
232
237 static void MakePoints( const PCB_SHAPE& aRectangle, EDIT_POINTS& aPoints )
238 {
239 wxCHECK( aRectangle.GetShape() == SHAPE_T::RECTANGLE, /* void */ );
240
241 VECTOR2I topLeft = aRectangle.GetTopLeft();
242 VECTOR2I botRight = aRectangle.GetBotRight();
243
244 aPoints.SetSwapX( topLeft.x > botRight.x );
245 aPoints.SetSwapY( topLeft.y > botRight.y );
246
247 if( aPoints.SwapX() )
248 std::swap( topLeft.x, botRight.x );
249
250 if( aPoints.SwapY() )
251 std::swap( topLeft.y, botRight.y );
252
253 aPoints.AddPoint( topLeft );
254 aPoints.AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
255 aPoints.AddPoint( botRight );
256 aPoints.AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
257 aPoints.AddPoint( aRectangle.GetCenter() );
258 aPoints.AddPoint( VECTOR2I( botRight.x - aRectangle.GetCornerRadius(), topLeft.y ) );
259 aPoints.Point( RECT_RADIUS ).SetDrawCircle();
260
261 aPoints.AddLine( aPoints.Point( RECT_TOP_LEFT ), aPoints.Point( RECT_TOP_RIGHT ) );
262 aPoints.Line( RECT_TOP ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_TOP ) ) );
263 aPoints.AddLine( aPoints.Point( RECT_TOP_RIGHT ), aPoints.Point( RECT_BOT_RIGHT ) );
264 aPoints.Line( RECT_RIGHT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_RIGHT ) ) );
265 aPoints.AddLine( aPoints.Point( RECT_BOT_RIGHT ), aPoints.Point( RECT_BOT_LEFT ) );
266 aPoints.Line( RECT_BOT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_BOT ) ) );
267 aPoints.AddLine( aPoints.Point( RECT_BOT_LEFT ), aPoints.Point( RECT_TOP_LEFT ) );
268 aPoints.Line( RECT_LEFT ).SetConstraint( new EC_PERPLINE( aPoints.Line( RECT_LEFT ) ) );
269 }
270
271 static void UpdateItem( PCB_SHAPE& aRectangle, const EDIT_POINT& aEditedPoint,
272 EDIT_POINTS& aPoints )
273 {
274 // You can have more points if your item wants to have more points
275 // (this class assumes the rect points come first, but that can be changed)
277
278 auto setLeft =
279 [&]( int left )
280 {
281 aPoints.SwapX() ? aRectangle.SetRight( left ) : aRectangle.SetLeft( left );
282 };
283 auto setRight =
284 [&]( int right )
285 {
286 aPoints.SwapX() ? aRectangle.SetLeft( right ) : aRectangle.SetRight( right );
287 };
288 auto setTop =
289 [&]( int top )
290 {
291 aPoints.SwapY() ? aRectangle.SetBottom( top ) : aRectangle.SetTop( top );
292 };
293 auto setBottom =
294 [&]( int bottom )
295 {
296 aPoints.SwapY() ? aRectangle.SetTop( bottom ) : aRectangle.SetBottom( bottom );
297 };
298
299 VECTOR2I topLeft = aPoints.Point( RECT_TOP_LEFT ).GetPosition();
300 VECTOR2I topRight = aPoints.Point( RECT_TOP_RIGHT ).GetPosition();
301 VECTOR2I botLeft = aPoints.Point( RECT_BOT_LEFT ).GetPosition();
302 VECTOR2I botRight = aPoints.Point( RECT_BOT_RIGHT ).GetPosition();
303
304 PinEditedCorner( aEditedPoint, aPoints, topLeft, topRight, botLeft, botRight );
305
306 if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_LEFT ) )
307 || isModified( aEditedPoint, aPoints.Point( RECT_TOP_RIGHT ) )
308 || isModified( aEditedPoint, aPoints.Point( RECT_BOT_RIGHT ) )
309 || isModified( aEditedPoint, aPoints.Point( RECT_BOT_LEFT ) ) )
310 {
311 setTop( topLeft.y );
312 setLeft( topLeft.x );
313 setRight( botRight.x );
314 setBottom( botRight.y );
315 }
316 else if( isModified( aEditedPoint, aPoints.Point( RECT_CENTER ) ) )
317 {
318 const VECTOR2I moveVector = aPoints.Point( RECT_CENTER ).GetPosition() - aRectangle.GetCenter();
319 aRectangle.Move( moveVector );
320 }
321 else if( isModified( aEditedPoint, aPoints.Point( RECT_RADIUS ) ) )
322 {
323 int width = std::abs( botRight.x - topLeft.x );
324 int height = std::abs( botRight.y - topLeft.y );
325 int maxRadius = std::min( width, height ) / 2;
326 int x = aPoints.Point( RECT_RADIUS ).GetX();
327 x = std::clamp( x, botRight.x - maxRadius, botRight.x );
328 aPoints.Point( RECT_RADIUS ).SetPosition( x, topLeft.y );
329 aRectangle.SetCornerRadius( botRight.x - x );
330 }
331 else if( isModified( aEditedPoint, aPoints.Line( RECT_TOP ) ) )
332 {
333 // Only top changes; keep others from previous full-local bbox
334 setTop( topLeft.y );
335 }
336 else if( isModified( aEditedPoint, aPoints.Line( RECT_LEFT ) ) )
337 {
338 // Only left changes; keep others from previous full-local bbox
339 setLeft( topLeft.x );
340 }
341 else if( isModified( aEditedPoint, aPoints.Line( RECT_BOT ) ) )
342 {
343 // Only bottom changes; keep others from previous full-local bbox
344 setBottom( botRight.y );
345 }
346 else if( isModified( aEditedPoint, aPoints.Line( RECT_RIGHT ) ) )
347 {
348 // Only right changes; keep others from previous full-local bbox
349 setRight( botRight.x );
350 }
351
352 for( unsigned i = 0; i < aPoints.LinesSize(); ++i )
353 {
354 if( !isModified( aEditedPoint, aPoints.Line( i ) ) )
355 aPoints.Line( i ).SetConstraint( new EC_PERPLINE( aPoints.Line( i ) ) );
356 }
357 }
358
359 static void UpdatePoints( const PCB_SHAPE& aRectangle, EDIT_POINTS& aPoints )
360 {
361 wxCHECK( aPoints.PointsSize() >= RECT_MAX_POINTS, /* void */ );
362
363 VECTOR2I topLeft = aRectangle.GetTopLeft();
364 VECTOR2I botRight = aRectangle.GetBotRight();
365
366 aPoints.SetSwapX( topLeft.x > botRight.x );
367 aPoints.SetSwapY( topLeft.y > botRight.y );
368
369 if( aPoints.SwapX() )
370 std::swap( topLeft.x, botRight.x );
371
372 if( aPoints.SwapY() )
373 std::swap( topLeft.y, botRight.y );
374
375 aPoints.Point( RECT_TOP_LEFT ).SetPosition( topLeft );
376 aPoints.Point( RECT_RADIUS ).SetPosition( botRight.x - aRectangle.GetCornerRadius(), topLeft.y );
377 aPoints.Point( RECT_TOP_RIGHT ).SetPosition( botRight.x, topLeft.y );
378 aPoints.Point( RECT_BOT_RIGHT ).SetPosition( botRight );
379 aPoints.Point( RECT_BOT_LEFT ).SetPosition( topLeft.x, botRight.y );
380 aPoints.Point( RECT_CENTER ).SetPosition( aRectangle.GetCenter() );
381 }
382
383 void MakePoints( EDIT_POINTS& aPoints ) override
384 {
385 // Just call the static helper
386 MakePoints( m_rectangle, aPoints );
387 }
388
389 bool UpdatePoints( EDIT_POINTS& aPoints ) override
390 {
391 // Careful; rectangle shape is mutable between cardinal and non-cardinal rotations...
392 if( m_rectangle.GetShape() != SHAPE_T::RECTANGLE || aPoints.PointsSize() == 0 )
393 return false;
394
395 UpdatePoints( m_rectangle, aPoints );
396 return true;
397 }
398
399 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
400 std::vector<EDA_ITEM*>& aUpdatedItems ) override
401 {
402 UpdateItem( m_rectangle, aEditedPoint, aPoints );
403 }
404
418 static void PinEditedCorner( const EDIT_POINT& aEditedPoint, const EDIT_POINTS& aEditPoints,
419 VECTOR2I& aTopLeft, VECTOR2I& aTopRight, VECTOR2I& aBotLeft, VECTOR2I& aBotRight,
420 const VECTOR2I& aHole = { 0, 0 }, const VECTOR2I& aHoleSize = { 0, 0 } )
421 {
422 int minWidth = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
423 int minHeight = EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 1 );
424
425 if( isModified( aEditedPoint, aEditPoints.Point( RECT_TOP_LEFT ) ) )
426 {
427 if( aHoleSize.x )
428 {
429 // pin edited point to the top/left of the hole
430 aTopLeft.x = std::min( aTopLeft.x, aHole.x - aHoleSize.x / 2 - minWidth );
431 aTopLeft.y = std::min( aTopLeft.y, aHole.y - aHoleSize.y / 2 - minHeight );
432 }
433 else
434 {
435 // pin edited point within opposite corner
436 aTopLeft.x = std::min( aTopLeft.x, aBotRight.x - minWidth );
437 aTopLeft.y = std::min( aTopLeft.y, aBotRight.y - minHeight );
438 }
439
440 // push edited point edges to adjacent corners
441 aTopRight.y = aTopLeft.y;
442 aBotLeft.x = aTopLeft.x;
443 }
444 else if( isModified( aEditedPoint, aEditPoints.Point( RECT_TOP_RIGHT ) ) )
445 {
446 if( aHoleSize.x )
447 {
448 // pin edited point to the top/right of the hole
449 aTopRight.x = std::max( aTopRight.x, aHole.x + aHoleSize.x / 2 + minWidth );
450 aTopRight.y = std::min( aTopRight.y, aHole.y - aHoleSize.y / 2 - minHeight );
451 }
452 else
453 {
454 // pin edited point within opposite corner
455 aTopRight.x = std::max( aTopRight.x, aBotLeft.x + minWidth );
456 aTopRight.y = std::min( aTopRight.y, aBotLeft.y - minHeight );
457 }
458
459 // push edited point edges to adjacent corners
460 aTopLeft.y = aTopRight.y;
461 aBotRight.x = aTopRight.x;
462 }
463 else if( isModified( aEditedPoint, aEditPoints.Point( RECT_BOT_LEFT ) ) )
464 {
465 if( aHoleSize.x )
466 {
467 // pin edited point to the bottom/left of the hole
468 aBotLeft.x = std::min( aBotLeft.x, aHole.x - aHoleSize.x / 2 - minWidth );
469 aBotLeft.y = std::max( aBotLeft.y, aHole.y + aHoleSize.y / 2 + minHeight );
470 }
471 else
472 {
473 // pin edited point within opposite corner
474 aBotLeft.x = std::min( aBotLeft.x, aTopRight.x - minWidth );
475 aBotLeft.y = std::max( aBotLeft.y, aTopRight.y + minHeight );
476 }
477
478 // push edited point edges to adjacent corners
479 aBotRight.y = aBotLeft.y;
480 aTopLeft.x = aBotLeft.x;
481 }
482 else if( isModified( aEditedPoint, aEditPoints.Point( RECT_BOT_RIGHT ) ) )
483 {
484 if( aHoleSize.x )
485 {
486 // pin edited point to the bottom/right of the hole
487 aBotRight.x = std::max( aBotRight.x, aHole.x + aHoleSize.x / 2 + minWidth );
488 aBotRight.y = std::max( aBotRight.y, aHole.y + aHoleSize.y / 2 + minHeight );
489 }
490 else
491 {
492 // pin edited point within opposite corner
493 aBotRight.x = std::max( aBotRight.x, aTopLeft.x + minWidth );
494 aBotRight.y = std::max( aBotRight.y, aTopLeft.y + minHeight );
495 }
496
497 // push edited point edges to adjacent corners
498 aBotLeft.y = aBotRight.y;
499 aTopRight.x = aBotRight.x;
500 }
501 else if( isModified( aEditedPoint, aEditPoints.Line( RECT_TOP ) ) )
502 {
503 aTopLeft.y = std::min( aTopLeft.y, aBotRight.y - minHeight );
504 }
505 else if( isModified( aEditedPoint, aEditPoints.Line( RECT_LEFT ) ) )
506 {
507 aTopLeft.x = std::min( aTopLeft.x, aBotRight.x - minWidth );
508 }
509 else if( isModified( aEditedPoint, aEditPoints.Line( RECT_BOT ) ) )
510 {
511 aBotRight.y = std::max( aBotRight.y, aTopLeft.y + minHeight );
512 }
513 else if( isModified( aEditedPoint, aEditPoints.Line( RECT_RIGHT ) ) )
514 {
515 aBotRight.x = std::max( aBotRight.x, aTopLeft.x + minWidth );
516 }
517 }
518
519private:
521};
522
523
525{
526public:
528 POLYGON_POINT_EDIT_BEHAVIOR( *aZone.Outline() ),
529 m_zone( aZone )
530 {}
531
532 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
533 std::vector<EDA_ITEM*>& aUpdatedItems ) override
534 {
535 m_zone.UnFill();
536
537 // Defer to the base class to update the polygon
538 POLYGON_POINT_EDIT_BEHAVIOR::UpdateItem( aEditedPoint, aPoints, aCommit, aUpdatedItems );
539
540 m_zone.HatchBorder();
541 }
542
543private:
545};
546
547
549{
551 {
552 REFIMG_ORIGIN = RECT_CENTER, // Reuse the center point fo rthe transform origin
553
555 };
556
557public:
561
562 void MakePoints( EDIT_POINTS& aPoints ) override
563 {
564 REFERENCE_IMAGE& refImage = m_refImage.GetReferenceImage();
565
566 const VECTOR2I topLeft = refImage.GetPosition() - refImage.GetSize() / 2;
567 const VECTOR2I botRight = refImage.GetPosition() + refImage.GetSize() / 2;
568
569 aPoints.AddPoint( topLeft );
570 aPoints.AddPoint( VECTOR2I( botRight.x, topLeft.y ) );
571 aPoints.AddPoint( botRight );
572 aPoints.AddPoint( VECTOR2I( topLeft.x, botRight.y ) );
573
574 aPoints.AddPoint( refImage.GetPosition() + refImage.GetTransformOriginOffset() );
575 }
576
577 bool UpdatePoints( EDIT_POINTS& aPoints ) override
578 {
579 wxCHECK( aPoints.PointsSize() == REFIMG_MAX_POINTS, false );
580
581 REFERENCE_IMAGE& refImage = m_refImage.GetReferenceImage();
582
583 const VECTOR2I topLeft = refImage.GetPosition() - refImage.GetSize() / 2;
584 const VECTOR2I botRight = refImage.GetPosition() + refImage.GetSize() / 2;
585
586 aPoints.Point( RECT_TOP_LEFT ).SetPosition( topLeft );
587 aPoints.Point( RECT_TOP_RIGHT ).SetPosition( VECTOR2I( botRight.x, topLeft.y ) );
588 aPoints.Point( RECT_BOT_RIGHT ).SetPosition( botRight );
589 aPoints.Point( RECT_BOT_LEFT ).SetPosition( VECTOR2I( topLeft.x, botRight.y ) );
590 aPoints.Point( REFIMG_ORIGIN ).SetPosition( refImage.GetPosition() + refImage.GetTransformOriginOffset() );
591 return true;
592 }
593
594 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
595 std::vector<EDA_ITEM*>& aUpdatedItems ) override
596 {
598
599 REFERENCE_IMAGE& refImage = m_refImage.GetReferenceImage();
600
601 const VECTOR2I topLeft = aPoints.Point( RECT_TOP_LEFT ).GetPosition();
602 const VECTOR2I topRight = aPoints.Point( RECT_TOP_RIGHT ).GetPosition();
603 const VECTOR2I botRight = aPoints.Point( RECT_BOT_RIGHT ).GetPosition();
604 const VECTOR2I botLeft = aPoints.Point( RECT_BOT_LEFT ).GetPosition();
605 const VECTOR2I xfrmOrigin = aPoints.Point( REFIMG_ORIGIN ).GetPosition();
606
607 if( isModified( aEditedPoint, aPoints.Point( REFIMG_ORIGIN ) ) )
608 {
609 // Moving the transform origin
610 // As the other points didn't move, we can get the image extent from them
611 const VECTOR2I newOffset = xfrmOrigin - ( topLeft + botRight ) / 2;
612 refImage.SetTransformOriginOffset( newOffset );
613 }
614 else
615 {
616 const VECTOR2I oldOrigin = m_refImage.GetPosition() + refImage.GetTransformOriginOffset();
617 const VECTOR2I oldSize = refImage.GetSize();
618 const VECTOR2I pos = refImage.GetPosition();
619
620 OPT_VECTOR2I newCorner;
621 VECTOR2I oldCorner = pos;
622
623 if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_LEFT ) ) )
624 {
625 newCorner = topLeft;
626 oldCorner -= oldSize / 2;
627 }
628 else if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_RIGHT ) ) )
629 {
630 newCorner = topRight;
631 oldCorner -= VECTOR2I( -oldSize.x, oldSize.y ) / 2;
632 }
633 else if( isModified( aEditedPoint, aPoints.Point( RECT_BOT_LEFT ) ) )
634 {
635 newCorner = botLeft;
636 oldCorner -= VECTOR2I( oldSize.x, -oldSize.y ) / 2;
637 }
638 else if( isModified( aEditedPoint, aPoints.Point( RECT_BOT_RIGHT ) ) )
639 {
640 newCorner = botRight;
641 oldCorner += oldSize / 2;
642 }
643
644 if( newCorner )
645 {
646 // Turn in the respective vectors from the origin
647 *newCorner -= xfrmOrigin;
648 oldCorner -= oldOrigin;
649
650 // If we tried to cross the origin, clamp it to stop it
651 if( sign( newCorner->x ) != sign( oldCorner.x ) || sign( newCorner->y ) != sign( oldCorner.y ) )
652 {
653 *newCorner = VECTOR2I( 0, 0 );
654 }
655
656 const double newLength = newCorner->EuclideanNorm();
657 const double oldLength = oldCorner.EuclideanNorm();
658
659 double ratio = oldLength > 0 ? ( newLength / oldLength ) : 1.0;
660
661 // Clamp the scaling to a minimum of 50 mils
662 VECTOR2I newSize = oldSize * ratio;
663 double newWidth = std::max( newSize.x, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
664 double newHeight = std::max( newSize.y, EDA_UNIT_UTILS::Mils2IU( pcbIUScale, 50 ) );
665 ratio = std::min( newWidth / oldSize.x, newHeight / oldSize.y );
666
667 // Also handles the origin offset
668 refImage.SetImageScale( refImage.GetImageScale() * ratio );
669 }
670 }
671 }
672
673private:
675};
676
677
679{
680public:
682 m_barcode( aBarcode )
683 {}
684
686 {
688 dummy.SetStart( m_barcode.GetCenter() - VECTOR2I( m_barcode.GetWidth() / 2, m_barcode.GetHeight() / 2 ) );
689 dummy.SetEnd( dummy.GetStart() + VECTOR2I( m_barcode.GetWidth(), m_barcode.GetHeight() ) );
690 dummy.Rotate( m_barcode.GetPosition(), m_barcode.GetAngle() );
691 return dummy;
692 }
693
694 void MakePoints( EDIT_POINTS& aPoints ) override
695 {
696 if( !m_barcode.GetAngle().IsCardinal() )
697 {
698 // Non-cardinal barcode point-editing isn't useful enough to support.
699 return;
700 }
701
702 auto set45Constraint =
703 [&]( int a, int b )
704 {
705 aPoints.Point( a ).SetConstraint( new EC_45DEGREE( aPoints.Point( a ), aPoints.Point( b ) ) );
706 };
707
709
710 if( m_barcode.KeepSquare() )
711 {
712 set45Constraint( RECT_TOP_LEFT, RECT_BOT_RIGHT );
713 set45Constraint( RECT_TOP_RIGHT, RECT_BOT_LEFT );
714 set45Constraint( RECT_BOT_RIGHT, RECT_TOP_LEFT );
715 set45Constraint( RECT_BOT_LEFT, RECT_TOP_RIGHT );
716 }
717 }
718
719 bool UpdatePoints( EDIT_POINTS& aPoints ) override
720 {
721 const unsigned target = m_barcode.GetAngle().IsCardinal() ? RECT_MAX_POINTS : 0;
722
723 if( aPoints.PointsSize() != target )
724 return false;
725
727 return true;
728 }
729
730 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
731 std::vector<EDA_ITEM*>& aUpdatedItems ) override
732 {
733 if( m_barcode.GetAngle().IsCardinal() )
734 {
736 RECTANGLE_POINT_EDIT_BEHAVIOR::UpdateItem( dummy, aEditedPoint, aPoints );
737 dummy.Rotate( dummy.GetCenter(), -m_barcode.GetAngle() );
738
739 m_barcode.SetPosition( dummy.GetCenter() );
740 m_barcode.SetWidth( dummy.GetRectangleWidth() );
741 m_barcode.SetHeight( dummy.GetRectangleHeight() );
742 m_barcode.AssembleBarcode();
743 }
744 }
745
746private:
748};
749
750
752{
753public:
758
759 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
760 std::vector<EDA_ITEM*>& aUpdatedItems ) override
761 {
763
764 PCB_TABLE& table = static_cast<PCB_TABLE&>( *m_cell.GetParent() );
765 aCommit.Modify( &table );
766 aUpdatedItems.push_back( &table );
767
768 if( !m_cell.GetTextAngle().IsHorizontal() )
769 {
770 if( isModified( aEditedPoint, aPoints.Point( ROW_HEIGHT ) ) )
771 {
772 m_cell.SetEnd( VECTOR2I( m_cell.GetEndX(), aPoints.Point( ROW_HEIGHT ).GetY() ) );
773
774 int colWidth = std::abs( m_cell.GetRectangleHeight() );
775
776 for( int ii = 0; ii < m_cell.GetColSpan() - 1; ++ii )
777 colWidth -= table.GetColWidth( m_cell.GetColumn() + ii );
778
779 table.SetColWidth( m_cell.GetColumn() + m_cell.GetColSpan() - 1, colWidth );
780 }
781 else if( isModified( aEditedPoint, aPoints.Point( COL_WIDTH ) ) )
782 {
783 m_cell.SetEnd( VECTOR2I( aPoints.Point( COL_WIDTH ).GetX(), m_cell.GetEndY() ) );
784
785 int rowHeight = m_cell.GetRectangleWidth();
786
787 for( int ii = 0; ii < m_cell.GetRowSpan() - 1; ++ii )
788 rowHeight -= table.GetRowHeight( m_cell.GetRow() + ii );
789
790 table.SetRowHeight( m_cell.GetRow() + m_cell.GetRowSpan() - 1, rowHeight );
791 }
792 }
793 else
794 {
795 if( isModified( aEditedPoint, aPoints.Point( COL_WIDTH ) ) )
796 {
797 m_cell.SetEnd( VECTOR2I( aPoints.Point( COL_WIDTH ).GetX(), m_cell.GetEndY() ) );
798
799 int colWidth = m_cell.GetRectangleWidth();
800
801 for( int ii = 0; ii < m_cell.GetColSpan() - 1; ++ii )
802 colWidth -= table.GetColWidth( m_cell.GetColumn() + ii );
803
804 table.SetColWidth( m_cell.GetColumn() + m_cell.GetColSpan() - 1, colWidth );
805 }
806 else if( isModified( aEditedPoint, aPoints.Point( ROW_HEIGHT ) ) )
807 {
808 m_cell.SetEnd( VECTOR2I( m_cell.GetEndX(), aPoints.Point( ROW_HEIGHT ).GetY() ) );
809
810 int rowHeight = m_cell.GetRectangleHeight();
811
812 for( int ii = 0; ii < m_cell.GetRowSpan() - 1; ++ii )
813 rowHeight -= table.GetRowHeight( m_cell.GetRow() + ii );
814
815 table.SetRowHeight( m_cell.GetRow() + m_cell.GetRowSpan() - 1, rowHeight );
816 }
817 }
818
819 table.Normalize();
820 }
821
822private:
824};
825
826
828{
829public:
831 m_pad( aPad ),
832 m_layer( aLayer )
833 {}
834
835 void MakePoints( EDIT_POINTS& aPoints ) override
836 {
837 VECTOR2I shapePos = m_pad.ShapePos( m_layer );
838 VECTOR2I halfSize( m_pad.GetSize( m_layer ).x / 2, m_pad.GetSize( m_layer ).y / 2 );
839
840 if( m_pad.IsLocked() )
841 return;
842
843 switch( m_pad.GetShape( m_layer ) )
844 {
846 aPoints.AddPoint( VECTOR2I( shapePos.x + halfSize.x, shapePos.y ) );
847 break;
848
849 case PAD_SHAPE::OVAL:
854 {
855 if( !m_pad.GetOrientation().IsCardinal() )
856 break;
857
858 if( m_pad.GetOrientation().IsVertical() )
859 std::swap( halfSize.x, halfSize.y );
860
861 // It's important to fill these according to the RECT indices
862 aPoints.AddPoint( shapePos - halfSize );
863 aPoints.AddPoint( VECTOR2I( shapePos.x + halfSize.x, shapePos.y - halfSize.y ) );
864 aPoints.AddPoint( shapePos + halfSize );
865 aPoints.AddPoint( VECTOR2I( shapePos.x - halfSize.x, shapePos.y + halfSize.y ) );
866 }
867 break;
868
869 default: // suppress warnings
870 break;
871 }
872 }
873
874 bool UpdatePoints( EDIT_POINTS& aPoints ) override
875 {
876 bool locked = m_pad.GetParent() && m_pad.IsLocked();
877 VECTOR2I shapePos = m_pad.ShapePos( m_layer );
878 VECTOR2I halfSize( m_pad.GetSize( m_layer ).x / 2, m_pad.GetSize( m_layer ).y / 2 );
879
880 switch( m_pad.GetShape( m_layer ) )
881 {
883 {
884 int target = locked ? 0 : 1;
885
886 // Careful; pad shape is mutable...
887 if( int( aPoints.PointsSize() ) != target )
888 {
889 aPoints.Clear();
890 MakePoints( aPoints );
891 }
892 else if( target == 1 )
893 {
894 shapePos.x += halfSize.x;
895 aPoints.Point( 0 ).SetPosition( shapePos );
896 }
897 }
898 break;
899
900 case PAD_SHAPE::OVAL:
905 {
906 // Careful; pad shape and orientation are mutable...
907 int target = locked || !m_pad.GetOrientation().IsCardinal() ? 0 : 4;
908
909 if( int( aPoints.PointsSize() ) != target )
910 {
911 aPoints.Clear();
912 MakePoints( aPoints );
913 }
914 else if( target == 4 )
915 {
916 if( m_pad.GetOrientation().IsVertical() )
917 std::swap( halfSize.x, halfSize.y );
918
919 aPoints.Point( RECT_TOP_LEFT ).SetPosition( shapePos - halfSize );
920 aPoints.Point( RECT_TOP_RIGHT ).SetPosition( VECTOR2I( shapePos.x + halfSize.x,
921 shapePos.y - halfSize.y ) );
922 aPoints.Point( RECT_BOT_RIGHT ).SetPosition( shapePos + halfSize );
923 aPoints.Point( RECT_BOT_LEFT ).SetPosition( VECTOR2I( shapePos.x - halfSize.x,
924 shapePos.y + halfSize.y ) );
925 }
926
927 break;
928 }
929
930 default: // suppress warnings
931 break;
932 }
933
934 return true;
935 }
936
937 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
938 std::vector<EDA_ITEM*>& aUpdatedItems ) override
939 {
940 switch( m_pad.GetShape( m_layer ) )
941 {
943 {
944 VECTOR2I end = aPoints.Point( 0 ).GetPosition();
945 int diameter = 2 * ( end - m_pad.GetPosition() ).EuclideanNorm();
946
947 m_pad.SetSize( m_layer, VECTOR2I( diameter, diameter ) );
948 break;
949 }
950
951 case PAD_SHAPE::OVAL:
956 {
957 VECTOR2I topLeft = aPoints.Point( RECT_TOP_LEFT ).GetPosition();
958 VECTOR2I topRight = aPoints.Point( RECT_TOP_RIGHT ).GetPosition();
959 VECTOR2I botLeft = aPoints.Point( RECT_BOT_LEFT ).GetPosition();
960 VECTOR2I botRight = aPoints.Point( RECT_BOT_RIGHT ).GetPosition();
961 VECTOR2I holeCenter = m_pad.GetPosition();
962 VECTOR2I holeSize = m_pad.GetDrillSize();
963
964 RECTANGLE_POINT_EDIT_BEHAVIOR::PinEditedCorner( aEditedPoint, aPoints, topLeft, topRight,
965 botLeft, botRight, holeCenter, holeSize );
966
967 if( ( m_pad.GetOffset( m_layer ).x || m_pad.GetOffset( m_layer ).y )
968 || ( m_pad.GetDrillSize().x && m_pad.GetDrillSize().y ) )
969 {
970 // Keep hole pinned at the current location; adjust the pad around the hole
971
972 VECTOR2I center = m_pad.GetPosition();
973 int dist[4];
974
975 if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_LEFT ) )
976 || isModified( aEditedPoint, aPoints.Point( RECT_BOT_RIGHT ) ) )
977 {
978 dist[0] = center.x - topLeft.x;
979 dist[1] = center.y - topLeft.y;
980 dist[2] = botRight.x - center.x;
981 dist[3] = botRight.y - center.y;
982 }
983 else
984 {
985 dist[0] = center.x - botLeft.x;
986 dist[1] = center.y - topRight.y;
987 dist[2] = topRight.x - center.x;
988 dist[3] = botLeft.y - center.y;
989 }
990
991 VECTOR2I padSize( dist[0] + dist[2], dist[1] + dist[3] );
992 VECTOR2I deltaOffset( padSize.x / 2 - dist[2], padSize.y / 2 - dist[3] );
993
994 if( m_pad.GetOrientation().IsVertical() )
995 std::swap( padSize.x, padSize.y );
996
997 RotatePoint( deltaOffset, -m_pad.GetOrientation() );
998
999 m_pad.SetSize( m_layer, padSize );
1000 m_pad.SetOffset( m_layer, -deltaOffset );
1001 }
1002 else
1003 {
1004 // Keep pad position at the center of the pad shape
1005
1006 int left, top, right, bottom;
1007
1008 if( isModified( aEditedPoint, aPoints.Point( RECT_TOP_LEFT ) )
1009 || isModified( aEditedPoint, aPoints.Point( RECT_BOT_RIGHT ) ) )
1010 {
1011 left = topLeft.x;
1012 top = topLeft.y;
1013 right = botRight.x;
1014 bottom = botRight.y;
1015 }
1016 else
1017 {
1018 left = botLeft.x;
1019 top = topRight.y;
1020 right = topRight.x;
1021 bottom = botLeft.y;
1022 }
1023
1024 VECTOR2I padSize( abs( right - left ), abs( bottom - top ) );
1025
1026 if( m_pad.GetOrientation().IsVertical() )
1027 std::swap( padSize.x, padSize.y );
1028
1029 m_pad.SetSize( m_layer, padSize );
1030 m_pad.SetPosition( VECTOR2I( ( left + right ) / 2, ( top + bottom ) / 2 ) );
1031 }
1032 break;
1033 }
1034 default: // suppress warnings
1035 break;
1036 }
1037 }
1038
1039private:
1042};
1043
1044
1051{
1052public:
1054 m_generator( aGenerator )
1055 {}
1056
1057 void MakePoints( EDIT_POINTS& aPoints ) override
1058 {
1059 m_generator.MakeEditPoints( aPoints );
1060 }
1061
1062 bool UpdatePoints( EDIT_POINTS& aPoints ) override
1063 {
1064 m_generator.UpdateEditPoints( aPoints );
1065 return true;
1066 }
1067
1068 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1069 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1070 {
1071 m_generator.UpdateFromEditPoints( aPoints );
1072 }
1073
1074private:
1076};
1077
1078
1088{
1089public:
1091 m_dimension( aDimension ),
1092 m_originalTextPos( aDimension.GetTextPos() ),
1093 m_oldCrossBar( SEG{ aDimension.GetCrossbarStart(), aDimension.GetCrossbarEnd() } )
1094 {}
1095
1097 {
1098 const SEG newCrossBar{ m_dimension.GetCrossbarStart(), m_dimension.GetCrossbarEnd() };
1099
1100 if( newCrossBar == m_oldCrossBar )
1101 {
1102 // Crossbar didn't change, text doesn't need to change
1103 return;
1104 }
1105
1106 const VECTOR2I newTextPos = getDimensionNewTextPosition();
1107 m_dimension.SetTextPos( newTextPos );
1108
1109 const GR_TEXT_H_ALIGN_T oldJustify = m_dimension.GetHorizJustify();
1110
1111 // We may need to update the justification if we go past vertical.
1114 {
1115 const VECTOR2I oldProject = m_oldCrossBar.LineProject( m_originalTextPos );
1116 const VECTOR2I newProject = newCrossBar.LineProject( newTextPos );
1117
1118 const VECTOR2I oldProjectedOffset =
1119 oldProject - m_oldCrossBar.NearestPoint( oldProject );
1120 const VECTOR2I newProjectedOffset = newProject - newCrossBar.NearestPoint( newProject );
1121
1122 const bool textWasLeftOf = oldProjectedOffset.x < 0
1123 || ( oldProjectedOffset.x == 0 && oldProjectedOffset.y > 0 );
1124 const bool textIsLeftOf = newProjectedOffset.x < 0
1125 || ( newProjectedOffset.x == 0 && newProjectedOffset.y > 0 );
1126
1127 if( textWasLeftOf != textIsLeftOf )
1128 {
1129 // Flip whatever the user had set
1130 m_dimension.SetHorizJustify( ( oldJustify == GR_TEXT_H_ALIGN_T::GR_TEXT_H_ALIGN_LEFT )
1133 }
1134 }
1135
1136 // Update the dimension (again) to ensure the text knockouts are correct
1137 m_dimension.Update();
1138 }
1139
1140private:
1142 {
1143 const SEG newCrossBar{ m_dimension.GetCrossbarStart(), m_dimension.GetCrossbarEnd() };
1144
1145 const EDA_ANGLE oldAngle = EDA_ANGLE( m_oldCrossBar.B - m_oldCrossBar.A );
1146 const EDA_ANGLE newAngle = EDA_ANGLE( newCrossBar.B - newCrossBar.A );
1147 const EDA_ANGLE rotation = oldAngle - newAngle;
1148
1149 // There are two modes - when the text is between the crossbar points, and when it's not.
1151 {
1153 const VECTOR2I rotTextOffsetFromCbCenter = GetRotated( m_originalTextPos - m_oldCrossBar.Center(),
1154 rotation );
1155 const VECTOR2I rotTextOffsetFromCbEnd = GetRotated( m_originalTextPos - cbNearestEndToText, rotation );
1156
1157 // Which of the two crossbar points is now in the right direction? They could be swapped over now.
1158 // If zero-length, doesn't matter, they're the same thing
1159 const bool startIsInOffsetDirection = KIGEOM::PointIsInDirection( m_dimension.GetCrossbarStart(),
1160 rotTextOffsetFromCbCenter,
1161 newCrossBar.Center() );
1162
1163 const VECTOR2I& newCbRefPt = startIsInOffsetDirection ? m_dimension.GetCrossbarStart()
1164 : m_dimension.GetCrossbarEnd();
1165
1166 // Apply the new offset to the correct crossbar point
1167 return newCbRefPt + rotTextOffsetFromCbEnd;
1168 }
1169
1170 // If the text was between the crossbar points, it should stay there, but we need to find a
1171 // good place for it. Keep it the same distance from the crossbar line, but rotated as needed.
1172
1173 const VECTOR2I origTextPointProjected = m_oldCrossBar.NearestPoint( m_originalTextPos );
1174 const double oldRatio = KIGEOM::GetLengthRatioFromStart( origTextPointProjected, m_oldCrossBar );
1175
1176 // Perpendicular from the crossbar line to the text position
1177 // We need to keep this length constant
1178 const VECTOR2I rotCbNormalToText = GetRotated( m_originalTextPos - origTextPointProjected, rotation );
1179
1180 const VECTOR2I newProjected = newCrossBar.A + ( newCrossBar.B - newCrossBar.A ) * oldRatio;
1181 return newProjected + rotCbNormalToText;
1182 }
1183
1187};
1188
1189
1194{
1195public:
1197 m_dimension( aDimension )
1198 {}
1199
1200 void MakePoints( EDIT_POINTS& aPoints ) override
1201 {
1202 aPoints.AddPoint( m_dimension.GetStart() );
1203 aPoints.AddPoint( m_dimension.GetEnd() );
1204 aPoints.AddPoint( m_dimension.GetTextPos() );
1205 aPoints.AddPoint( m_dimension.GetCrossbarStart() );
1206 aPoints.AddPoint( m_dimension.GetCrossbarEnd() );
1207
1210
1211 if( m_dimension.Type() == PCB_DIM_ALIGNED_T )
1212 {
1213 // Dimension height setting - edit points should move only along the feature lines
1215 aPoints.Point( DIM_START ) ) );
1216 aPoints.Point( DIM_CROSSBAREND ).SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBAREND ),
1217 aPoints.Point( DIM_END ) ) );
1218 }
1219 }
1220
1221 bool UpdatePoints( EDIT_POINTS& aPoints ) override
1222 {
1223 wxCHECK( aPoints.PointsSize() == DIM_ALIGNED_MAX, false );
1224
1225 aPoints.Point( DIM_START ).SetPosition( m_dimension.GetStart() );
1226 aPoints.Point( DIM_END ).SetPosition( m_dimension.GetEnd() );
1227 aPoints.Point( DIM_TEXT ).SetPosition( m_dimension.GetTextPos() );
1228 aPoints.Point( DIM_CROSSBARSTART ).SetPosition( m_dimension.GetCrossbarStart() );
1229 aPoints.Point( DIM_CROSSBAREND ).SetPosition( m_dimension.GetCrossbarEnd() );
1230 return true;
1231 }
1232
1233 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1234 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1235 {
1237
1238 if( m_dimension.Type() == PCB_DIM_ALIGNED_T )
1239 updateAlignedDimension( aEditedPoint, aPoints );
1240 else
1241 updateOrthogonalDimension( aEditedPoint, aPoints );
1242 }
1243
1244 OPT_VECTOR2I Get45DegreeConstrainer( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints ) const override
1245 {
1246 // Constraint for crossbar
1247 if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1248 return aPoints.Point( DIM_END ).GetPosition();
1249
1250 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1251 return aPoints.Point( DIM_START ).GetPosition();
1252
1253 // No constraint
1254 return aEditedPoint.GetPosition();
1255 }
1256
1257private:
1261 void updateAlignedDimension( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints )
1262 {
1263 DIM_ALIGNED_TEXT_UPDATER textPositionUpdater( m_dimension );
1264
1265 // Check which point is currently modified and updated dimension's points respectively
1266 if( isModified( aEditedPoint, aPoints.Point( DIM_CROSSBARSTART ) ) )
1267 {
1268 VECTOR2D featureLine( aEditedPoint.GetPosition() - m_dimension.GetStart() );
1269 VECTOR2D crossBar( m_dimension.GetEnd() - m_dimension.GetStart() );
1270
1271 if( featureLine.Cross( crossBar ) > 0 )
1272 m_dimension.SetHeight( -featureLine.EuclideanNorm() );
1273 else
1274 m_dimension.SetHeight( featureLine.EuclideanNorm() );
1275
1276 m_dimension.Update();
1277 }
1278 else if( isModified( aEditedPoint, aPoints.Point( DIM_CROSSBAREND ) ) )
1279 {
1280 VECTOR2D featureLine( aEditedPoint.GetPosition() - m_dimension.GetEnd() );
1281 VECTOR2D crossBar( m_dimension.GetEnd() - m_dimension.GetStart() );
1282
1283 if( featureLine.Cross( crossBar ) > 0 )
1284 m_dimension.SetHeight( -featureLine.EuclideanNorm() );
1285 else
1286 m_dimension.SetHeight( featureLine.EuclideanNorm() );
1287
1288 m_dimension.Update();
1289 }
1290 else if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1291 {
1292 m_dimension.SetStart( aEditedPoint.GetPosition() );
1293 m_dimension.Update();
1294
1296 aPoints.Point( DIM_START ) ) );
1297 aPoints.Point( DIM_CROSSBAREND ).SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBAREND ),
1298 aPoints.Point( DIM_END ) ) );
1299 }
1300 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1301 {
1302 m_dimension.SetEnd( aEditedPoint.GetPosition() );
1303 m_dimension.Update();
1304
1306 aPoints.Point( DIM_START ) ) );
1307 aPoints.Point( DIM_CROSSBAREND ).SetConstraint( new EC_LINE( aPoints.Point( DIM_CROSSBAREND ),
1308 aPoints.Point( DIM_END ) ) );
1309 }
1310 else if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1311 {
1312 // Force manual mode if we weren't already in it
1313 m_dimension.SetTextPositionMode( DIM_TEXT_POSITION::MANUAL );
1314 m_dimension.SetTextPos( aEditedPoint.GetPosition() );
1315 m_dimension.Update();
1316 }
1317
1318 textPositionUpdater.UpdateTextAfterChange();
1319 }
1320
1324 void updateOrthogonalDimension( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints )
1325 {
1326 DIM_ALIGNED_TEXT_UPDATER textPositionUpdater( m_dimension );
1327 PCB_DIM_ORTHOGONAL& orthDimension = static_cast<PCB_DIM_ORTHOGONAL&>( m_dimension );
1328
1329 if( isModified( aEditedPoint, aPoints.Point( DIM_CROSSBARSTART ) )
1330 || isModified( aEditedPoint, aPoints.Point( DIM_CROSSBAREND ) ) )
1331 {
1332 BOX2I bounds( m_dimension.GetStart(), m_dimension.GetEnd() - m_dimension.GetStart() );
1333
1334 const VECTOR2I& cursorPos = aEditedPoint.GetPosition();
1335
1336 // Find vector from nearest dimension point to edit position
1337 VECTOR2I directionA( cursorPos - m_dimension.GetStart() );
1338 VECTOR2I directionB( cursorPos - m_dimension.GetEnd() );
1339 VECTOR2I direction = ( directionA < directionB ) ? directionA : directionB;
1340
1341 bool vert;
1342 VECTOR2D featureLine( cursorPos - m_dimension.GetStart() );
1343
1344 // Only change the orientation when we move outside the bounds
1345 if( !bounds.Contains( cursorPos ) )
1346 {
1347 // If the dimension is horizontal or vertical, set correct orientation
1348 // otherwise, test if we're left/right of the bounding box or above/below it
1349 if( bounds.GetWidth() == 0 )
1350 vert = true;
1351 else if( bounds.GetHeight() == 0 )
1352 vert = false;
1353 else if( cursorPos.x > bounds.GetLeft() && cursorPos.x < bounds.GetRight() )
1354 vert = false;
1355 else if( cursorPos.y > bounds.GetTop() && cursorPos.y < bounds.GetBottom() )
1356 vert = true;
1357 else
1358 vert = std::abs( direction.y ) < std::abs( direction.x );
1359
1362 }
1363 else
1364 {
1365 vert = orthDimension.GetOrientation() == PCB_DIM_ORTHOGONAL::DIR::VERTICAL;
1366 }
1367
1368 m_dimension.SetHeight( vert ? featureLine.x : featureLine.y );
1369 }
1370 else if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1371 {
1372 m_dimension.SetStart( aEditedPoint.GetPosition() );
1373 }
1374 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1375 {
1376 m_dimension.SetEnd( aEditedPoint.GetPosition() );
1377 }
1378 else if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1379 {
1380 // Force manual mode if we weren't already in it
1381 m_dimension.SetTextPositionMode( DIM_TEXT_POSITION::MANUAL );
1382 m_dimension.SetTextPos( VECTOR2I( aEditedPoint.GetPosition() ) );
1383 }
1384
1385 m_dimension.Update();
1386
1387 // After recompute, find the new text position
1388 textPositionUpdater.UpdateTextAfterChange();
1389 }
1390
1392};
1393
1394
1396{
1397public:
1399 m_dimension( aDimension )
1400 {}
1401
1402 void MakePoints( EDIT_POINTS& aPoints ) override
1403 {
1404 aPoints.AddPoint( m_dimension.GetStart() );
1405 aPoints.AddPoint( m_dimension.GetEnd() );
1406
1408
1409 aPoints.Point( DIM_END ).SetConstraint(new EC_45DEGREE( aPoints.Point( DIM_END ),
1410 aPoints.Point( DIM_START ) ) );
1412 }
1413
1414 bool UpdatePoints( EDIT_POINTS& aPoints ) override
1415 {
1416 wxCHECK( aPoints.PointsSize() == DIM_CENTER_MAX, false );
1417
1418 aPoints.Point( DIM_START ).SetPosition( m_dimension.GetStart() );
1419 aPoints.Point( DIM_END ).SetPosition( m_dimension.GetEnd() );
1420 return true;
1421 }
1422
1423 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1424 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1425 {
1427
1428 if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1429 m_dimension.SetStart( aEditedPoint.GetPosition() );
1430 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1431 m_dimension.SetEnd( aEditedPoint.GetPosition() );
1432
1433 m_dimension.Update();
1434 }
1435
1436 OPT_VECTOR2I Get45DegreeConstrainer( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints ) const override
1437 {
1438 if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1439 return aPoints.Point( DIM_START ).GetPosition();
1440
1441 return std::nullopt;
1442 }
1443
1444private:
1446};
1447
1448
1450{
1451public:
1453 m_dimension( aDimension )
1454 {}
1455
1456 void MakePoints( EDIT_POINTS& aPoints ) override
1457 {
1458 aPoints.AddPoint( m_dimension.GetStart() );
1459 aPoints.AddPoint( m_dimension.GetEnd() );
1460 aPoints.AddPoint( m_dimension.GetTextPos() );
1461 aPoints.AddPoint( m_dimension.GetKnee() );
1462
1465
1466 aPoints.Point( DIM_KNEE ).SetConstraint( new EC_LINE( aPoints.Point( DIM_START ),
1467 aPoints.Point( DIM_END ) ) );
1469
1470 aPoints.Point( DIM_TEXT ).SetConstraint( new EC_45DEGREE( aPoints.Point( DIM_TEXT ),
1471 aPoints.Point( DIM_KNEE ) ) );
1473 }
1474
1475 bool UpdatePoints( EDIT_POINTS& aPoints ) override
1476 {
1477 wxCHECK( aPoints.PointsSize() == DIM_RADIAL_MAX, false );
1478
1479 aPoints.Point( DIM_START ).SetPosition( m_dimension.GetStart() );
1480 aPoints.Point( DIM_END ).SetPosition( m_dimension.GetEnd() );
1481 aPoints.Point( DIM_TEXT ).SetPosition( m_dimension.GetTextPos() );
1482 aPoints.Point( DIM_KNEE ).SetPosition( m_dimension.GetKnee() );
1483 return true;
1484 }
1485
1486 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1487 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1488 {
1490
1491 if( isModified( aEditedPoint, aPoints.Point( DIM_START ) ) )
1492 {
1493 m_dimension.SetStart( aEditedPoint.GetPosition() );
1494 m_dimension.Update();
1495
1496 aPoints.Point( DIM_KNEE ).SetConstraint( new EC_LINE( aPoints.Point( DIM_START ),
1497 aPoints.Point( DIM_END ) ) );
1498 }
1499 else if( isModified( aEditedPoint, aPoints.Point( DIM_END ) ) )
1500 {
1501 VECTOR2I oldKnee = m_dimension.GetKnee();
1502
1503 m_dimension.SetEnd( aEditedPoint.GetPosition() );
1504 m_dimension.Update();
1505
1506 VECTOR2I kneeDelta = m_dimension.GetKnee() - oldKnee;
1507 m_dimension.SetTextPos( m_dimension.GetTextPos() + kneeDelta );
1508 m_dimension.Update();
1509
1510 aPoints.Point( DIM_KNEE ).SetConstraint( new EC_LINE( aPoints.Point( DIM_START ),
1511 aPoints.Point( DIM_END ) ) );
1512 }
1513 else if( isModified( aEditedPoint, aPoints.Point( DIM_KNEE ) ) )
1514 {
1515 VECTOR2I oldKnee = m_dimension.GetKnee();
1516 VECTOR2I arrowVec = aPoints.Point( DIM_KNEE ).GetPosition() - aPoints.Point( DIM_END ).GetPosition();
1517
1518 m_dimension.SetLeaderLength( arrowVec.EuclideanNorm() );
1519 m_dimension.Update();
1520
1521 VECTOR2I kneeDelta = m_dimension.GetKnee() - oldKnee;
1522 m_dimension.SetTextPos( m_dimension.GetTextPos() + kneeDelta );
1523 m_dimension.Update();
1524 }
1525 else if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1526 {
1527 m_dimension.SetTextPos( aEditedPoint.GetPosition() );
1528 m_dimension.Update();
1529 }
1530 }
1531
1532 OPT_VECTOR2I Get45DegreeConstrainer( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints ) const override
1533 {
1534 if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1535 return aPoints.Point( DIM_KNEE ).GetPosition();
1536
1537 return std::nullopt;
1538 }
1539
1540private:
1542};
1543
1544
1546{
1547public:
1549 m_dimension( aDimension )
1550 {}
1551
1552 void MakePoints( EDIT_POINTS& aPoints ) override
1553 {
1554 aPoints.AddPoint( m_dimension.GetStart() );
1555 aPoints.AddPoint( m_dimension.GetEnd() );
1556 aPoints.AddPoint( m_dimension.GetTextPos() );
1557
1560
1561 aPoints.Point( DIM_TEXT ).SetConstraint( new EC_45DEGREE( aPoints.Point( DIM_TEXT ),
1562 aPoints.Point( DIM_END ) ) );
1564 }
1565
1566 bool UpdatePoints( EDIT_POINTS& aPoints ) override
1567 {
1568 wxCHECK( aPoints.PointsSize() == DIM_LEADER_MAX, false );
1569
1570 aPoints.Point( DIM_START ).SetPosition( m_dimension.GetStart() );
1571 aPoints.Point( DIM_END ).SetPosition( m_dimension.GetEnd() );
1572 aPoints.Point( DIM_TEXT ).SetPosition( m_dimension.GetTextPos() );
1573 return true;
1574 }
1575
1576 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1577 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1578 {
1580
1581 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 const VECTOR2I newPoint( aEditedPoint.GetPosition() );
1588 const VECTOR2I delta = newPoint - m_dimension.GetEnd();
1589
1590 m_dimension.SetEnd( newPoint );
1591 m_dimension.SetTextPos( m_dimension.GetTextPos() + delta );
1592 }
1593 else if( isModified( aEditedPoint, aPoints.Point( DIM_TEXT ) ) )
1594 {
1595 m_dimension.SetTextPos( aEditedPoint.GetPosition() );
1596 }
1597
1598 m_dimension.Update();
1599 }
1600
1601private:
1603};
1604
1605
1610{
1611public:
1613 m_textbox( aTextbox )
1614 {}
1615
1616 void MakePoints( EDIT_POINTS& aPoints ) override
1617 {
1618 if( m_textbox.GetShape() == SHAPE_T::RECTANGLE )
1620
1621 // Rotated textboxes are implemented as polygons and these aren't currently editable.
1622 }
1623
1624 bool UpdatePoints( EDIT_POINTS& aPoints ) override
1625 {
1626 // Careful; textbox shape is mutable between cardinal and non-cardinal rotations...
1627 const unsigned target = m_textbox.GetShape() == SHAPE_T::RECTANGLE ? RECT_MAX_POINTS : 0;
1628
1629 if( aPoints.PointsSize() != target )
1630 return false;
1631
1633 return true;
1634 }
1635
1636 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1637 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1638 {
1639 if( m_textbox.GetShape() == SHAPE_T::RECTANGLE )
1640 RECTANGLE_POINT_EDIT_BEHAVIOR::UpdateItem( m_textbox, aEditedPoint, aPoints );
1641 }
1642
1643private:
1645};
1646
1648{
1649public:
1651 m_group( &aGroup ),
1652 m_parent( &aGroup )
1653 {
1654 for( BOARD_ITEM* item : aGroup.GetBoardItems() )
1655 {
1656 if( item->Type() == PCB_SHAPE_T )
1657 {
1658 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
1659 m_shapes.push_back( shape );
1660 m_originalWidths[shape] = static_cast<double>( shape->GetWidth() );
1661 }
1662 }
1663 }
1664
1665 SHAPE_GROUP_POINT_EDIT_BEHAVIOR( std::vector<PCB_SHAPE*> aShapes, BOARD_ITEM* aParent ) :
1666 m_group( nullptr ),
1667 m_shapes( std::move( aShapes ) ),
1668 m_parent( aParent )
1669 {
1670 for( PCB_SHAPE* shape : m_shapes )
1671 m_originalWidths[shape] = static_cast<double>( shape->GetWidth() );
1672 }
1673
1674 void MakePoints( EDIT_POINTS& aPoints ) override
1675 {
1676 BOX2I bbox = getBoundingBox();
1677 VECTOR2I tl = bbox.GetOrigin();
1678 VECTOR2I br = bbox.GetEnd();
1679
1680 aPoints.AddPoint( tl );
1681 aPoints.AddPoint( VECTOR2I( br.x, tl.y ) );
1682 aPoints.AddPoint( br );
1683 aPoints.AddPoint( VECTOR2I( tl.x, br.y ) );
1684 aPoints.AddPoint( bbox.Centre() );
1685
1686 aPoints.AddIndicatorLine( aPoints.Point( RECT_TOP_LEFT ), aPoints.Point( RECT_TOP_RIGHT ) );
1687 aPoints.AddIndicatorLine( aPoints.Point( RECT_TOP_RIGHT ), aPoints.Point( RECT_BOT_RIGHT ) );
1688 aPoints.AddIndicatorLine( aPoints.Point( RECT_BOT_RIGHT ), aPoints.Point( RECT_BOT_LEFT ) );
1689 aPoints.AddIndicatorLine( aPoints.Point( RECT_BOT_LEFT ), aPoints.Point( RECT_TOP_LEFT ) );
1690 }
1691
1692 bool UpdatePoints( EDIT_POINTS& aPoints ) override
1693 {
1694 BOX2I bbox = getBoundingBox();
1695 VECTOR2I tl = bbox.GetOrigin();
1696 VECTOR2I br = bbox.GetEnd();
1697
1698 aPoints.Point( RECT_TOP_LEFT ).SetPosition( tl );
1699 aPoints.Point( RECT_TOP_RIGHT ).SetPosition( br.x, tl.y );
1700 aPoints.Point( RECT_BOT_RIGHT ).SetPosition( br );
1701 aPoints.Point( RECT_BOT_LEFT ).SetPosition( tl.x, br.y );
1702 aPoints.Point( RECT_CENTER ).SetPosition( bbox.Centre() );
1703 return true;
1704 }
1705
1706 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1707 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1708 {
1709 BOX2I oldBox = getBoundingBox();
1710 VECTOR2I oldCenter = oldBox.Centre();
1711
1712 if( isModified( aEditedPoint, aPoints.Point( RECT_CENTER ) ) )
1713 {
1714 VECTOR2I delta = aPoints.Point( RECT_CENTER ).GetPosition() - oldCenter;
1715
1716 if( m_group )
1717 {
1718 aCommit.Modify( m_group, nullptr, RECURSE_MODE::RECURSE );
1719 m_group->Move( delta );
1720 }
1721 else
1722 {
1723 for( PCB_SHAPE* shape : m_shapes )
1724 {
1725 aCommit.Modify( shape );
1726 shape->Move( delta );
1727 }
1728 }
1729
1730 for( PCB_SHAPE* shape : m_shapes )
1731 aUpdatedItems.push_back( shape );
1732
1733 UpdatePoints( aPoints );
1734 return;
1735 }
1736
1737 VECTOR2I tl = aPoints.Point( RECT_TOP_LEFT ).GetPosition();
1738 VECTOR2I tr = aPoints.Point( RECT_TOP_RIGHT ).GetPosition();
1739 VECTOR2I bl = aPoints.Point( RECT_BOT_LEFT ).GetPosition();
1740 VECTOR2I br = aPoints.Point( RECT_BOT_RIGHT ).GetPosition();
1741
1742 RECTANGLE_POINT_EDIT_BEHAVIOR::PinEditedCorner( aEditedPoint, aPoints, tl, tr, bl, br );
1743
1744 double sx = static_cast<double>( br.x - tl.x ) / static_cast<double>( oldBox.GetWidth() );
1745 double sy = static_cast<double>( br.y - tl.y ) / static_cast<double>( oldBox.GetHeight() );
1746 double scale = ( sx + sy ) / 2.0;
1747
1748 for( PCB_SHAPE* shape : m_shapes )
1749 {
1750 aCommit.Modify( shape );
1751 shape->Move( -oldCenter );
1752 shape->Scale( scale );
1753 shape->Move( oldCenter );
1754
1755 if( auto shapeIt = m_originalWidths.find( shape ); shapeIt != m_originalWidths.end() )
1756 {
1757 shapeIt->second = shapeIt->second * scale;
1758 shape->SetWidth( KiROUND( shapeIt->second ) );
1759 }
1760 else
1761 {
1762 shape->SetWidth( KiROUND( shape->GetWidth() * scale ) );
1763 }
1764
1765 aUpdatedItems.push_back( shape );
1766 }
1767
1768 UpdatePoints( aPoints );
1769 }
1770
1771 BOARD_ITEM* GetParent() const { return m_parent; }
1772
1773private:
1775 {
1776 BOX2I bbox;
1777
1778 for( const PCB_SHAPE* shape : m_shapes )
1779 bbox.Merge( shape->GetBoundingBox() );
1780
1781 return bbox;
1782 }
1783
1784private:
1786 std::vector<PCB_SHAPE*> m_shapes;
1788 std::unordered_map<PCB_SHAPE*, double> m_originalWidths;
1789};
1790
1791
1793 PCB_TOOL_BASE( "pcbnew.PointEditor" ),
1794 m_frame( nullptr ),
1795 m_selectionTool( nullptr ),
1796 m_editedPoint( nullptr ),
1797 m_hoveredPoint( nullptr ),
1798 m_original( VECTOR2I( 0, 0 ) ),
1800 m_altConstrainer( VECTOR2I( 0, 0 ) ),
1801 m_inPointEditorTool( false ),
1802 m_angleSnapPos( VECTOR2I( 0, 0 ) ),
1803 m_stickyDisplacement( VECTOR2I( 0, 0 ) ),
1804 m_angleSnapActive( false )
1805{}
1806
1807
1809{
1811 m_editPoints.reset();
1812 m_altConstraint.reset();
1813 getViewControls()->SetAutoPan( false );
1814 m_angleSnapActive = false;
1816}
1817
1818
1820{
1821 const KICAD_T type = aItem.Type();
1822
1823 if( type == PCB_ZONE_T )
1824 return true;
1825
1826 if( type == PCB_SHAPE_T )
1827 {
1828 const PCB_SHAPE& shape = static_cast<const PCB_SHAPE&>( aItem );
1829 const SHAPE_T shapeType = shape.GetShape();
1830 return shapeType == SHAPE_T::SEGMENT || shapeType == SHAPE_T::POLY || shapeType == SHAPE_T::ARC;
1831 }
1832
1833 return false;
1834}
1835
1836
1838{
1839 const auto type = aItem.Type();
1840
1841 if( type == PCB_ZONE_T )
1842 return true;
1843
1844 if( type == PCB_SHAPE_T )
1845 {
1846 const PCB_SHAPE& shape = static_cast<const PCB_SHAPE&>( aItem );
1847 const SHAPE_T shapeType = shape.GetShape();
1848 return shapeType == SHAPE_T::POLY;
1849 }
1850
1851 return false;
1852}
1853
1854
1855static VECTOR2I snapCorner( const VECTOR2I& aPrev, const VECTOR2I& aNext, const VECTOR2I& aGuess,
1856 double aAngleDeg )
1857{
1858 double angleRad = aAngleDeg * M_PI / 180.0;
1859 VECTOR2D prev( aPrev );
1860 VECTOR2D next( aNext );
1861 double chord = ( next - prev ).EuclideanNorm();
1862 double sinA = sin( angleRad );
1863
1864 if( chord == 0.0 || fabs( sinA ) < 1e-9 )
1865 return aGuess;
1866
1867 double radius = chord / ( 2.0 * sinA );
1868 VECTOR2D mid = ( prev + next ) / 2.0;
1869 VECTOR2D dir = next - prev;
1870 VECTOR2D normal( -dir.y, dir.x );
1871 normal = normal.Resize( 1 );
1872 double h_sq = radius * radius - ( chord * chord ) / 4.0;
1873 double h = h_sq > 0.0 ? sqrt( h_sq ) : 0.0;
1874
1875 VECTOR2D center1 = mid + normal * h;
1876 VECTOR2D center2 = mid - normal * h;
1877
1878 auto project =
1879 [&]( const VECTOR2D& center )
1880 {
1881 VECTOR2D v = VECTOR2D( aGuess ) - center;
1882
1883 if( v.EuclideanNorm() == 0.0 )
1884 v = prev - center;
1885
1886 v = v.Resize( 1 );
1887 VECTOR2D p = center + v * radius;
1888 return KiROUND( p );
1889 };
1890
1891 VECTOR2I p1 = project( center1 );
1892 VECTOR2I p2 = project( center2 );
1893
1894 double d1 = ( VECTOR2D( aGuess ) - VECTOR2D( p1 ) ).EuclideanNorm();
1895 double d2 = ( VECTOR2D( aGuess ) - VECTOR2D( p2 ) ).EuclideanNorm();
1896
1897 return d1 < d2 ? p1 : p2;
1898}
1899
1900
1902{
1903 // Find the selection tool, so they can cooperate
1905
1906 wxASSERT_MSG( m_selectionTool, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
1907
1908 const auto arcIsEdited =
1909 []( const SELECTION& aSelection ) -> bool
1910 {
1911 const EDA_ITEM* item = aSelection.Front();
1912 return ( item != nullptr ) && ( item->Type() == PCB_SHAPE_T )
1913 && static_cast<const PCB_SHAPE*>( item )->GetShape() == SHAPE_T::ARC;
1914 };
1915
1916 using S_C = SELECTION_CONDITIONS;
1917
1918 auto& menu = m_selectionTool->GetToolMenu().GetMenu();
1919
1920 menu.AddItem( PCB_ACTIONS::cycleArcEditMode, S_C::Count( 1 ) && arcIsEdited );
1921
1922 return true;
1923}
1924
1925
1926std::shared_ptr<EDIT_POINTS> PCB_POINT_EDITOR::makePoints( EDA_ITEM* aItem )
1927{
1928 std::shared_ptr<EDIT_POINTS> points = std::make_shared<EDIT_POINTS>( aItem );
1929
1930 if( !aItem )
1931 return points;
1932
1933 // Reset the behaviour and we'll make a new one
1934 m_editorBehavior = nullptr;
1935
1936 switch( aItem->Type() )
1937 {
1939 {
1940 PCB_REFERENCE_IMAGE& refImage = static_cast<PCB_REFERENCE_IMAGE&>( *aItem );
1941 m_editorBehavior = std::make_unique<REFERENCE_IMAGE_POINT_EDIT_BEHAVIOR>( refImage );
1942 break;
1943 }
1944 case PCB_BARCODE_T:
1945 {
1946 PCB_BARCODE& barcode = static_cast<PCB_BARCODE&>( *aItem );
1947 m_editorBehavior = std::make_unique<BARCODE_POINT_EDIT_BEHAVIOR>( barcode );
1948 break;
1949 }
1950 case PCB_TEXTBOX_T:
1951 {
1952 PCB_TEXTBOX& textbox = static_cast<PCB_TEXTBOX&>( *aItem );
1953 m_editorBehavior = std::make_unique<TEXTBOX_POINT_EDIT_BEHAVIOR>( textbox );
1954 break;
1955 }
1956 case PCB_SHAPE_T:
1957 {
1958 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
1959
1960 switch( shape->GetShape() )
1961 {
1962 case SHAPE_T::SEGMENT:
1963 m_editorBehavior = std::make_unique<EDA_SEGMENT_POINT_EDIT_BEHAVIOR>( *shape );
1964 break;
1965
1966 case SHAPE_T::RECTANGLE:
1967 m_editorBehavior = std::make_unique<RECTANGLE_POINT_EDIT_BEHAVIOR>( *shape );
1968 break;
1969
1970 case SHAPE_T::ARC:
1971 m_editorBehavior = std::make_unique<EDA_ARC_POINT_EDIT_BEHAVIOR>( *shape, m_arcEditMode,
1972 *getViewControls() );
1973 break;
1974
1975 case SHAPE_T::CIRCLE:
1976 m_editorBehavior = std::make_unique<EDA_CIRCLE_POINT_EDIT_BEHAVIOR>( *shape );
1977 break;
1978
1979 case SHAPE_T::POLY:
1980 m_editorBehavior = std::make_unique<EDA_POLYGON_POINT_EDIT_BEHAVIOR>( *shape );
1981 break;
1982
1983 case SHAPE_T::BEZIER:
1984 m_editorBehavior = std::make_unique<EDA_BEZIER_POINT_EDIT_BEHAVIOR>( *shape,
1985 shape->GetMaxError() );
1986 break;
1987
1988 default: // suppress warnings
1989 break;
1990 }
1991
1992 break;
1993 }
1994
1995 case PCB_GROUP_T:
1996 {
1997 PCB_GROUP* group = static_cast<PCB_GROUP*>( aItem );
1998 bool shapesOnly = true;
1999
2000 for( BOARD_ITEM* child : group->GetBoardItems() )
2001 {
2002 if( child->Type() != PCB_SHAPE_T )
2003 {
2004 shapesOnly = false;
2005 break;
2006 }
2007 }
2008
2009 if( shapesOnly )
2010 m_editorBehavior = std::make_unique<SHAPE_GROUP_POINT_EDIT_BEHAVIOR>( *group );
2011 else
2012 points.reset();
2013
2014 break;
2015 }
2016
2017 case PCB_TABLECELL_T:
2018 {
2019 PCB_TABLECELL* cell = static_cast<PCB_TABLECELL*>( aItem );
2020
2021 // No support for point-editing of a rotated table
2022 if( cell->GetShape() == SHAPE_T::RECTANGLE )
2023 m_editorBehavior = std::make_unique<PCB_TABLECELL_POINT_EDIT_BEHAVIOR>( *cell );
2024
2025 break;
2026 }
2027
2028 case PCB_PAD_T:
2029 {
2030 // Pad edit only for the footprint editor
2032 {
2033 PAD& pad = static_cast<PAD&>( *aItem );
2034 PCB_LAYER_ID activeLayer = m_frame ? m_frame->GetActiveLayer() : PADSTACK::ALL_LAYERS;
2035
2036 // Point editor only handles copper shape changes
2037 if( !IsCopperLayer( activeLayer ) )
2038 activeLayer = IsFrontLayer( activeLayer ) ? F_Cu : B_Cu;
2039
2040 m_editorBehavior = std::make_unique<PAD_POINT_EDIT_BEHAVIOR>( pad, activeLayer );
2041 }
2042 break;
2043 }
2044
2045 case PCB_ZONE_T:
2046 {
2047 ZONE& zone = static_cast<ZONE&>( *aItem );
2048 m_editorBehavior = std::make_unique<ZONE_POINT_EDIT_BEHAVIOR>( zone );
2049 break;
2050 }
2051
2052 case PCB_GENERATOR_T:
2053 {
2054 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( aItem );
2055 m_editorBehavior = std::make_unique<GENERATOR_POINT_EDIT_BEHAVIOR>( *generator );
2056 break;
2057 }
2058
2059 case PCB_DIM_ALIGNED_T:
2061 {
2062 PCB_DIM_ALIGNED& dimension = static_cast<PCB_DIM_ALIGNED&>( *aItem );
2063 m_editorBehavior = std::make_unique<ALIGNED_DIMENSION_POINT_EDIT_BEHAVIOR>( dimension );
2064 break;
2065 }
2066
2067 case PCB_DIM_CENTER_T:
2068 {
2069 PCB_DIM_CENTER& dimension = static_cast<PCB_DIM_CENTER&>( *aItem );
2070 m_editorBehavior = std::make_unique<DIM_CENTER_POINT_EDIT_BEHAVIOR>( dimension );
2071 break;
2072 }
2073
2074 case PCB_DIM_RADIAL_T:
2075 {
2076 PCB_DIM_RADIAL& dimension = static_cast<PCB_DIM_RADIAL&>( *aItem );
2077 m_editorBehavior = std::make_unique<DIM_RADIAL_POINT_EDIT_BEHAVIOR>( dimension );
2078 break;
2079 }
2080
2081 case PCB_DIM_LEADER_T:
2082 {
2083 PCB_DIM_LEADER& dimension = static_cast<PCB_DIM_LEADER&>( *aItem );
2084 m_editorBehavior = std::make_unique<DIM_LEADER_POINT_EDIT_BEHAVIOR>( dimension );
2085 break;
2086 }
2087
2088 default:
2089 points.reset();
2090 break;
2091 }
2092
2093 if( m_editorBehavior )
2094 m_editorBehavior->MakePoints( *points );
2095
2096 return points;
2097}
2098
2099
2101{
2102 EDIT_POINT* point;
2103 EDIT_POINT* hovered = nullptr;
2104
2105 if( aEvent.IsMotion() )
2106 {
2107 point = m_editPoints->FindPoint( aEvent.Position(), getView() );
2108 hovered = point;
2109 }
2110 else if( aEvent.IsDrag( BUT_LEFT ) )
2111 {
2112 point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
2113 }
2114 else
2115 {
2116 point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
2117 }
2118
2119 if( hovered )
2120 {
2121 if( m_hoveredPoint != hovered )
2122 {
2123 if( m_hoveredPoint )
2124 m_hoveredPoint->SetHover( false );
2125
2126 m_hoveredPoint = hovered;
2127 m_hoveredPoint->SetHover();
2128 }
2129 }
2130 else if( m_hoveredPoint )
2131 {
2132 m_hoveredPoint->SetHover( false );
2133 m_hoveredPoint = nullptr;
2134 }
2135
2136 if( m_editedPoint != point )
2137 setEditedPoint( point );
2138}
2139
2140
2142{
2144 return 0;
2145
2147 return 0;
2148
2150
2152 const PCB_SELECTION& selection = m_selectionTool->GetSelection();
2153
2154 if( selection.Size() == 0 )
2155 return 0;
2156
2157 for( EDA_ITEM* selItem : selection )
2158 {
2159 if( selItem->GetEditFlags() || !selItem->IsBOARD_ITEM() )
2160 return 0;
2161 }
2162
2163 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
2164
2165 if( !item || item->IsLocked() )
2166 return 0;
2167
2168 Activate();
2169 // Must be done after Activate() so that it gets set into the correct context
2170 getViewControls()->ShowCursor( true );
2171
2173
2174 // Use the original object as a construction item
2175 std::vector<std::unique_ptr<BOARD_ITEM>> clones;
2176
2177 m_editorBehavior.reset();
2178
2179 if( selection.Size() > 1 )
2180 {
2181 // Multi-selection: check if all items are shapes
2182 std::vector<PCB_SHAPE*> shapes;
2183 bool allShapes = true;
2184 bool anyLocked = false;
2185
2186 for( EDA_ITEM* selItem : selection )
2187 {
2188 if( selItem->Type() == PCB_SHAPE_T )
2189 {
2190 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( selItem );
2191 shapes.push_back( shape );
2192
2193 if( shape->IsLocked() )
2194 anyLocked = true;
2195 }
2196 else
2197 {
2198 allShapes = false;
2199 }
2200 }
2201
2202 if( allShapes && shapes.size() > 1 && !anyLocked )
2203 {
2204 m_editorBehavior = std::make_unique<SHAPE_GROUP_POINT_EDIT_BEHAVIOR>(
2205 std::move( shapes ), item );
2206 m_editPoints = std::make_shared<EDIT_POINTS>( item );
2207 m_editorBehavior->MakePoints( *m_editPoints );
2208 }
2209 else
2210 {
2211 return 0;
2212 }
2213 }
2214 else
2215 {
2216 // Single selection: use existing makePoints logic
2217 m_editPoints = makePoints( item );
2218 }
2219
2220 if( !m_editPoints )
2221 return 0;
2222
2223 PCB_SHAPE* graphicItem = dynamic_cast<PCB_SHAPE*>( item );
2224
2225 // Only add the angle_item if we are editing a polygon or zone
2226 if( item->Type() == PCB_ZONE_T || ( graphicItem && graphicItem->GetShape() == SHAPE_T::POLY ) )
2227 {
2228 m_angleItem = std::make_unique<KIGFX::PREVIEW::ANGLE_ITEM>( m_editPoints.get() );
2229 }
2230
2231 m_preview.FreeItems();
2232 getView()->Add( &m_preview );
2233
2234 RECT_RADIUS_TEXT_ITEM* radiusHelper = new RECT_RADIUS_TEXT_ITEM( pcbIUScale, editFrame->GetUserUnits() );
2235 m_preview.Add( radiusHelper );
2236
2237 getView()->Add( m_editPoints.get() );
2238
2239 if( m_angleItem )
2240 getView()->Add( m_angleItem.get() );
2241
2242 setEditedPoint( nullptr );
2243 updateEditedPoint( aEvent );
2244 bool inDrag = false;
2245 bool isConstrained = false;
2246 bool haveSnapLineDirections = false;
2247
2248 auto updateSnapLineDirections =
2249 [&]()
2250 {
2251 std::vector<VECTOR2I> directions;
2252
2253 if( inDrag && m_editedPoint )
2254 {
2255 EDIT_CONSTRAINT<EDIT_POINT>* constraint = nullptr;
2256
2257 if( m_altConstraint )
2258 constraint = m_altConstraint.get();
2259 else if( m_editedPoint->IsConstrained() )
2260 constraint = m_editedPoint->GetConstraint();
2261
2262 directions = getConstraintDirections( constraint );
2263 }
2264
2265 if( directions.empty() )
2266 {
2267 grid.SetSnapLineDirections( {} );
2268 grid.SetSnapLineEnd( std::nullopt );
2269 haveSnapLineDirections = false;
2270 }
2271 else
2272 {
2273 grid.SetSnapLineDirections( directions );
2274 grid.SetSnapLineOrigin( m_original.GetPosition() );
2275 grid.SetSnapLineEnd( std::nullopt );
2276 haveSnapLineDirections = true;
2277 }
2278 };
2279
2280 BOARD_COMMIT commit( editFrame );
2281
2282 // Main loop: keep receiving events
2283 while( TOOL_EVENT* evt = Wait() )
2284 {
2285 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2286 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2287
2288 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
2290 else
2292
2293 if( !m_editPoints || evt->IsSelectionEvent() || evt->Matches( EVENTS::InhibitSelectionEditing ) )
2294 {
2295 break;
2296 }
2297
2298 EDIT_POINT* prevHover = m_hoveredPoint;
2299
2300 if( !inDrag )
2301 updateEditedPoint( *evt );
2302
2303 if( prevHover != m_hoveredPoint )
2304 {
2305 getView()->Update( m_editPoints.get() );
2306
2307 if( m_angleItem )
2308 getView()->Update( m_angleItem.get() );
2309 }
2310
2311 if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
2312 {
2313 if( !inDrag )
2314 {
2315 frame()->UndoRedoBlock( true );
2316
2317 if( item->Type() == PCB_GENERATOR_T )
2318 {
2319 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genStartEdit, &commit,
2320 static_cast<PCB_GENERATOR*>( item ) );
2321 }
2322
2324 m_original = *m_editedPoint; // Save the original position
2325 getViewControls()->SetAutoPan( true );
2326 inDrag = true;
2327
2328 if( m_editedPoint->GetGridConstraint() != SNAP_BY_GRID )
2329 grid.SetAuxAxes( true, m_original.GetPosition() );
2330
2331 m_editedPoint->SetActive();
2332
2333 for( size_t ii = 0; ii < m_editPoints->PointsSize(); ++ii )
2334 {
2335 EDIT_POINT& point = m_editPoints->Point( ii );
2336
2337 if( &point != m_editedPoint )
2338 point.SetActive( false );
2339 }
2340
2341 // When we start dragging, create a clone of the item to use as the original
2342 // reference geometry (e.g. for intersections and extensions)
2343 BOARD_ITEM* clone = static_cast<BOARD_ITEM*>( item->Clone() );
2344 clone->SetParent( nullptr );
2345
2346 if( PCB_SHAPE* shape= dynamic_cast<PCB_SHAPE*>( item ) )
2347 {
2348 shape->SetFlags( IS_MOVING );
2349 shape->UpdateHatching();
2350
2351 static_cast<PCB_SHAPE*>( clone )->SetFillMode( FILL_T::NO_FILL );
2352 }
2353
2354 clones.emplace_back( clone );
2355 grid.AddConstructionItems( { clone }, false, true );
2356
2357 updateSnapLineDirections();
2358 }
2359
2360 bool need_constraint = Is45Limited() || Is90Limited();
2361
2362 if( isConstrained != need_constraint )
2363 {
2364 setAltConstraint( need_constraint );
2365 isConstrained = need_constraint;
2366 updateSnapLineDirections();
2367 }
2368
2369 // For polygon lines, Ctrl temporarily toggles between CONVERGING and FIXED_LENGTH modes
2370 EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
2371 bool ctrlHeld = evt->Modifier( MD_CTRL );
2372
2373 if( line )
2374 {
2375 bool isPoly = false;
2376
2377 switch( item->Type() )
2378 {
2379 case PCB_ZONE_T:
2380 isPoly = true;
2381 break;
2382
2383 case PCB_SHAPE_T:
2384 isPoly = static_cast<PCB_SHAPE*>( item )->GetShape() == SHAPE_T::POLY;
2385 break;
2386
2387 default:
2388 break;
2389 }
2390
2391 if( isPoly )
2392 {
2393 EC_CONVERGING* constraint =
2394 dynamic_cast<EC_CONVERGING*>( line->GetConstraint() );
2395
2396 if( constraint )
2397 {
2400
2401 if( constraint->GetMode() != targetMode )
2402 constraint->SetMode( targetMode );
2403 }
2404 }
2405 }
2406
2407 // Keep point inside of limits with some padding
2408 VECTOR2I pos = GetClampedCoords<double, int>( evt->Position(), COORDS_PADDING );
2409 LSET snapLayers;
2410
2411 switch( m_editedPoint->GetSnapConstraint() )
2412 {
2413 case IGNORE_SNAPS: break;
2414 case OBJECT_LAYERS: snapLayers = item->GetLayerSet(); break;
2415 case ALL_LAYERS: snapLayers = LSET::AllLayersMask(); break;
2416 }
2417
2418 if( m_editedPoint->GetGridConstraint() == SNAP_BY_GRID )
2419 {
2420 if( grid.GetUseGrid() )
2421 {
2422 VECTOR2I gridPt = grid.BestSnapAnchor( pos, {}, grid.GetItemGrid( item ), { item } );
2423
2424 VECTOR2I last = m_editedPoint->GetPosition();
2425 VECTOR2I delta = pos - last;
2426 VECTOR2I deltaGrid = gridPt - grid.BestSnapAnchor( last, {}, grid.GetItemGrid( item ),
2427 { item } );
2428
2429 if( abs( delta.x ) > grid.GetGrid().x / 2 )
2430 pos.x = last.x + deltaGrid.x;
2431 else
2432 pos.x = last.x;
2433
2434 if( abs( delta.y ) > grid.GetGrid().y / 2 )
2435 pos.y = last.y + deltaGrid.y;
2436 else
2437 pos.y = last.y;
2438 }
2439 }
2440
2441 if( m_angleSnapActive )
2442 {
2443 m_stickyDisplacement = evt->Position() - m_angleSnapPos;
2444 int stickyLimit = KiROUND( getView()->ToWorld( 5 ) );
2445
2446 if( m_stickyDisplacement.EuclideanNorm() > stickyLimit || evt->Modifier( MD_SHIFT ) )
2447 {
2448 m_angleSnapActive = false;
2449 }
2450 else
2451 {
2452 pos = m_angleSnapPos;
2453 }
2454 }
2455
2456 if( !m_angleSnapActive && m_editPoints->PointsSize() > 2 && !evt->Modifier( MD_SHIFT ) )
2457 {
2458 int idx = getEditedPointIndex();
2459
2460 if( idx != wxNOT_FOUND )
2461 {
2462 int prevIdx = ( idx + m_editPoints->PointsSize() - 1 ) % m_editPoints->PointsSize();
2463 int nextIdx = ( idx + 1 ) % m_editPoints->PointsSize();
2464 VECTOR2I prev = m_editPoints->Point( prevIdx ).GetPosition();
2465 VECTOR2I next = m_editPoints->Point( nextIdx ).GetPosition();
2466 SEG segA( pos, prev );
2467 SEG segB( pos, next );
2468 double ang = segA.Angle( segB ).AsDegrees();
2469 double snapAng = 45.0 * std::round( ang / 45.0 );
2470
2471 if( std::abs( ang - snapAng ) < 2.0 )
2472 {
2473 m_angleSnapPos = snapCorner( prev, next, pos, snapAng );
2474 m_angleSnapActive = true;
2475 m_stickyDisplacement = evt->Position() - m_angleSnapPos;
2476 pos = m_angleSnapPos;
2477 }
2478 }
2479 }
2480
2481 bool constraintSnapped = false;
2482
2483 // Apply 45 degree or other constraints
2485 {
2486 m_editedPoint->SetPosition( pos );
2487 m_altConstraint->Apply( grid );
2488 constraintSnapped = true;
2489
2490 // For constrained lines (like zone edges), try to snap to nearby anchors
2491 // that lie on the constraint line
2492 if( grid.GetSnap() && !snapLayers.empty() )
2493 {
2494 VECTOR2I constrainedPos = m_editedPoint->GetPosition();
2495 VECTOR2I snapPos = grid.BestSnapAnchor( constrainedPos, snapLayers,
2496 grid.GetItemGrid( item ), { item } );
2497
2498 if( snapPos != constrainedPos )
2499 {
2500 m_editedPoint->SetPosition( snapPos );
2501 m_altConstraint->Apply( grid );
2502 VECTOR2I projectedPos = m_editedPoint->GetPosition();
2503 const int snapTolerance = KiROUND( getView()->ToWorld( 5 ) );
2504
2505 if( ( projectedPos - snapPos ).EuclideanNorm() > snapTolerance )
2506 m_editedPoint->SetPosition( constrainedPos );
2507 }
2508 }
2509 }
2510 else if( !m_angleSnapActive && m_editedPoint->IsConstrained() )
2511 {
2512 m_editedPoint->SetPosition( pos );
2513 m_editedPoint->ApplyConstraint( grid );
2514 constraintSnapped = true;
2515
2516 // For constrained lines (like zone edges), try to snap to nearby anchors
2517 // that lie on the constraint line. First get the constrained position, then
2518 // look for snap anchors and verify they're on the constraint line.
2519 if( grid.GetSnap() && !snapLayers.empty() )
2520 {
2521 VECTOR2I constrainedPos = m_editedPoint->GetPosition();
2522 VECTOR2I snapPos = grid.BestSnapAnchor( constrainedPos, snapLayers,
2523 grid.GetItemGrid( item ), { item } );
2524
2525 // If we found a snap anchor different from the constrained position,
2526 // check if setting the point there and reapplying the constraint
2527 // results in a position close to the snap point
2528 if( snapPos != constrainedPos )
2529 {
2530 m_editedPoint->SetPosition( snapPos );
2531 m_editedPoint->ApplyConstraint( grid );
2532 VECTOR2I projectedPos = m_editedPoint->GetPosition();
2533
2534 // If the projection is close to the snap anchor, use it
2535 // Otherwise revert to the original constrained position
2536 const int snapTolerance = KiROUND( getView()->ToWorld( 5 ) );
2537
2538 if( ( projectedPos - snapPos ).EuclideanNorm() > snapTolerance )
2539 m_editedPoint->SetPosition( constrainedPos );
2540 }
2541 }
2542 }
2543 else if( !m_angleSnapActive && m_editedPoint->GetGridConstraint() == SNAP_TO_GRID )
2544 {
2545 m_editedPoint->SetPosition( grid.BestSnapAnchor( pos, snapLayers, grid.GetItemGrid( item ),
2546 { item } ) );
2547 }
2548 else
2549 {
2550 m_editedPoint->SetPosition( pos );
2551 }
2552
2553 if( haveSnapLineDirections )
2554 {
2555 if( constraintSnapped )
2556 grid.SetSnapLineEnd( m_editedPoint->GetPosition() );
2557 else
2558 grid.SetSnapLineEnd( std::nullopt );
2559 }
2560
2561 updateItem( commit );
2562 getViewControls()->ForceCursorPosition( true, m_editedPoint->GetPosition() );
2563 updatePoints();
2564
2565 if( m_editPoints->PointsSize() > RECT_RADIUS
2566 && m_editedPoint == &m_editPoints->Point( RECT_RADIUS ) )
2567 {
2568 if( PCB_SHAPE* rect = dynamic_cast<PCB_SHAPE*>( item ) )
2569 {
2570 int radius = rect->GetCornerRadius();
2571 int offset = radius - M_SQRT1_2 * radius;
2572 VECTOR2I topLeft = rect->GetTopLeft();
2573 VECTOR2I botRight = rect->GetBotRight();
2574 VECTOR2I topRight( botRight.x, topLeft.y );
2575 VECTOR2I center( topRight.x - offset, topRight.y + offset );
2576 radiusHelper->Set( radius, center, VECTOR2I( 1, -1 ), editFrame->GetUserUnits() );
2577 }
2578 }
2579 else
2580 {
2581 radiusHelper->Hide();
2582 }
2583
2584 getView()->Update( &m_preview );
2585 }
2586 else if( m_editedPoint && evt->Action() == TA_MOUSE_DOWN && evt->Buttons() == BUT_LEFT )
2587 {
2588 m_editedPoint->SetActive();
2589
2590 for( size_t ii = 0; ii < m_editPoints->PointsSize(); ++ii )
2591 {
2592 EDIT_POINT& point = m_editPoints->Point( ii );
2593
2594 if( &point != m_editedPoint )
2595 point.SetActive( false );
2596 }
2597
2598 getView()->Update( m_editPoints.get() );
2599
2600 if( m_angleItem )
2601 getView()->Update( m_angleItem.get() );
2602 }
2603 else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
2604 {
2605 if( m_editedPoint )
2606 {
2607 m_editedPoint->SetActive( false );
2608 getView()->Update( m_editPoints.get() );
2609
2610 if( m_angleItem )
2611 getView()->Update( m_angleItem.get() );
2612 }
2613
2614 radiusHelper->Hide();
2615 getView()->Update( &m_preview );
2616
2617 getViewControls()->SetAutoPan( false );
2618 setAltConstraint( false );
2619 updateSnapLineDirections();
2620
2621 if( m_editorBehavior )
2622 m_editorBehavior->FinalizeItem( *m_editPoints, commit );
2623
2624 if( item->Type() == PCB_GENERATOR_T )
2625 {
2626 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( item );
2627
2628 m_preview.FreeItems();
2629 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genFinishEdit, &commit, generator );
2630
2631 commit.Push( generator->GetCommitMessage() );
2632 }
2633 else if( item->Type() == PCB_TABLECELL_T )
2634 {
2635 commit.Push( _( "Resize Table Cells" ) );
2636 }
2637 else
2638 {
2639 commit.Push( _( "Move Point" ) );
2640 }
2641
2642 if( PCB_SHAPE* shape= dynamic_cast<PCB_SHAPE*>( item ) )
2643 {
2644 shape->ClearFlags( IS_MOVING );
2645 shape->UpdateHatching();
2646 }
2647
2648 inDrag = false;
2649 frame()->UndoRedoBlock( false );
2650 updateSnapLineDirections();
2651
2652 m_toolMgr->PostAction<EDA_ITEM*>( ACTIONS::reselectItem, item ); // FIXME: Needed for generators
2653 }
2654 else if( evt->IsCancelInteractive() || evt->IsActivate() )
2655 {
2656 if( inDrag ) // Restore the last change
2657 {
2658 if( item->Type() == PCB_GENERATOR_T )
2659 {
2660 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genCancelEdit, &commit,
2661 static_cast<PCB_GENERATOR*>( item ) );
2662 }
2663
2664 commit.Revert();
2665
2666 if( PCB_SHAPE* shape= dynamic_cast<PCB_SHAPE*>( item ) )
2667 {
2668 shape->ClearFlags( IS_MOVING );
2669 shape->UpdateHatching();
2670 }
2671
2672 inDrag = false;
2673 frame()->UndoRedoBlock( false );
2674 updateSnapLineDirections();
2675 }
2676
2677 // Only cancel point editor when activating a new tool
2678 // Otherwise, allow the points to persist when moving up the
2679 // tool stack
2680 if( evt->IsActivate() && !evt->IsMoveTool() )
2681 break;
2682 }
2683 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2684 {
2685 // Re-create the points for items which can have different behavior on different layers
2686 if( item->Type() == PCB_PAD_T && m_isFootprintEditor )
2687 {
2688 getView()->Remove( m_editPoints.get() );
2689
2690 if( m_angleItem )
2691 getView()->Remove( m_angleItem.get() );
2692
2693 m_editPoints = makePoints( item );
2694
2695 if( m_angleItem )
2696 {
2697 m_angleItem->SetEditPoints( m_editPoints.get() );
2698 getView()->Add( m_angleItem.get() );
2699 }
2700
2701 getView()->Add( m_editPoints.get() );
2702 }
2703 }
2704 else if( evt->Action() == TA_UNDO_REDO_POST )
2705 {
2706 break;
2707 }
2708 else
2709 {
2710 evt->SetPassEvent();
2711 }
2712 }
2713
2714 if( PCB_SHAPE* shape= dynamic_cast<PCB_SHAPE*>( item ) )
2715 {
2716 shape->ClearFlags( IS_MOVING );
2717 shape->UpdateHatching();
2718 }
2719
2720 m_preview.FreeItems();
2721 getView()->Remove( &m_preview );
2722
2723 if( m_editPoints )
2724 {
2725 getView()->Remove( m_editPoints.get() );
2726
2727 if( m_angleItem )
2728 getView()->Remove( m_angleItem.get() );
2729
2730 m_editPoints.reset();
2731 m_angleItem.reset();
2732 }
2733
2734 m_editedPoint = nullptr;
2735 grid.SetSnapLineDirections( {} );
2736
2737 return 0;
2738}
2739
2740
2742{
2743 if( !m_editPoints || !m_editPoints->GetParent() || !HasPoint() )
2744 return 0;
2745
2747
2748 BOARD_COMMIT commit( editFrame );
2749 commit.Stage( m_editPoints->GetParent(), CHT_MODIFY );
2750
2751 VECTOR2I pt = m_editedPoint->GetPosition();
2752 wxString title;
2753 wxString msg;
2754
2755 if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
2756 {
2757 title = _( "Move Midpoint to Location" );
2758 msg = _( "Move Midpoint" );
2759 }
2760 else
2761 {
2762 title = _( "Move Corner to Location" );
2763 msg = _( "Move Corner" );
2764 }
2765
2766 WX_PT_ENTRY_DIALOG dlg( editFrame, title, _( "X:" ), _( "Y:" ), pt, false );
2767
2768 if( dlg.ShowModal() == wxID_OK )
2769 {
2770 m_editedPoint->SetPosition( dlg.GetValue() );
2771 updateItem( commit );
2772 commit.Push( msg );
2773 }
2774
2775 return 0;
2776}
2777
2778
2780{
2781 wxCHECK( m_editPoints, /* void */ );
2782 EDA_ITEM* item = m_editPoints->GetParent();
2783
2784 if( !item )
2785 return;
2786
2787 // item is always updated
2788 std::vector<EDA_ITEM*> updatedItems = { item };
2789 aCommit.Modify( item );
2790
2791 if( m_editorBehavior )
2792 {
2793 wxCHECK( m_editedPoint, /* void */ );
2794 m_editorBehavior->UpdateItem( *m_editedPoint, *m_editPoints, aCommit, updatedItems );
2795 }
2796
2797 // Perform any post-edit actions that the item may require
2798
2799 switch( item->Type() )
2800 {
2801 case PCB_TEXTBOX_T:
2802 case PCB_SHAPE_T:
2803 {
2804 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2805
2806 if( shape->IsProxyItem() )
2807 {
2808 for( PAD* pad : shape->GetParentFootprint()->Pads() )
2809 {
2810 if( pad->IsEntered() )
2811 view()->Update( pad );
2812 }
2813 }
2814
2815 // Nuke outline font render caches
2816 if( PCB_TEXTBOX* textBox = dynamic_cast<PCB_TEXTBOX*>( item ) )
2817 textBox->ClearRenderCache();
2818
2819 break;
2820 }
2821 case PCB_GENERATOR_T:
2822 {
2823 GENERATOR_TOOL* generatorTool = m_toolMgr->GetTool<GENERATOR_TOOL>();
2824 PCB_GENERATOR* generatorItem = static_cast<PCB_GENERATOR*>( item );
2825
2826 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genUpdateEdit, &aCommit, generatorItem );
2827
2828 // Note: POINT_EDITOR::m_preview holds only the canvas-draw status "popup"; the meanders
2829 // themselves (ROUTER_PREVIEW_ITEMs) are owned by the router.
2830
2831 m_preview.FreeItems();
2832
2833 for( EDA_ITEM* previewItem : generatorItem->GetPreviewItems( generatorTool, frame(), STATUS_ITEMS_ONLY ) )
2834 m_preview.Add( previewItem );
2835
2836 getView()->Update( &m_preview );
2837 break;
2838 }
2839 default:
2840 break;
2841 }
2842
2843 // Update the item and any affected items
2844 for( EDA_ITEM* updatedItem : updatedItems )
2845 getView()->Update( updatedItem );
2846
2847 frame()->SetMsgPanel( item );
2848}
2849
2850
2852{
2853 if( !m_editPoints )
2854 return;
2855
2856 EDA_ITEM* item = m_editPoints->GetParent();
2857
2858 if( !item )
2859 return;
2860
2861 if( !m_editorBehavior )
2862 return;
2863
2864 int editedIndex = -1;
2865 bool editingLine = false;
2866
2867 if( m_editedPoint )
2868 {
2869 // Check if we're editing a point (vertex)
2870 for( unsigned ii = 0; ii < m_editPoints->PointsSize(); ++ii )
2871 {
2872 if( &m_editPoints->Point( ii ) == m_editedPoint )
2873 {
2874 editedIndex = ii;
2875 break;
2876 }
2877 }
2878
2879 // If not found in points, check if we're editing a line (midpoint)
2880 if( editedIndex == -1 )
2881 {
2882 for( unsigned ii = 0; ii < m_editPoints->LinesSize(); ++ii )
2883 {
2884 if( &m_editPoints->Line( ii ) == m_editedPoint )
2885 {
2886 editedIndex = ii;
2887 editingLine = true;
2888 break;
2889 }
2890 }
2891 }
2892 }
2893
2894 if( !m_editorBehavior->UpdatePoints( *m_editPoints ) )
2895 {
2896 getView()->Remove( m_editPoints.get() );
2897 m_editPoints = makePoints( item );
2898 getView()->Add( m_editPoints.get() );
2899 }
2900
2901 if( editedIndex >= 0 )
2902 {
2903 if( editingLine && editedIndex < (int) m_editPoints->LinesSize() )
2904 m_editedPoint = &m_editPoints->Line( editedIndex );
2905 else if( !editingLine && editedIndex < (int) m_editPoints->PointsSize() )
2906 m_editedPoint = &m_editPoints->Point( editedIndex );
2907 else
2908 m_editedPoint = nullptr;
2909 }
2910 else
2911 {
2912 m_editedPoint = nullptr;
2913 }
2914
2915 getView()->Update( m_editPoints.get() );
2916
2917 if( m_angleItem )
2918 getView()->Update( m_angleItem.get() );
2919}
2920
2921
2923{
2925
2926 if( aPoint )
2927 {
2928 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2929 controls->ForceCursorPosition( true, aPoint->GetPosition() );
2930 controls->ShowCursor( true );
2931 }
2932 else
2933 {
2934 if( frame()->ToolStackIsEmpty() )
2935 controls->ShowCursor( false );
2936
2937 controls->ForceCursorPosition( false );
2938 }
2939
2940 m_editedPoint = aPoint;
2941}
2942
2943
2945{
2946 EDA_ITEM* parent = m_editPoints ? m_editPoints->GetParent() : nullptr;
2947 EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
2948 bool isPoly = false;
2949
2950 if( parent )
2951 {
2952 switch( parent->Type() )
2953 {
2954 case PCB_ZONE_T:
2955 isPoly = true;
2956 break;
2957
2958 case PCB_SHAPE_T:
2959 isPoly = static_cast<PCB_SHAPE*>( parent )->GetShape() == SHAPE_T::POLY;
2960 break;
2961
2962 default:
2963 break;
2964 }
2965 }
2966
2967 if( aEnabled )
2968 {
2969 if( line && isPoly )
2970 {
2971 // For polygon lines, toggle the mode on the existing constraint rather than
2972 // creating a new one. This preserves the original reference positions.
2973 EC_CONVERGING* constraint = dynamic_cast<EC_CONVERGING*>( line->GetConstraint() );
2974
2975 if( constraint )
2977
2978 // Don't set m_altConstraint - we're modifying the line's own constraint
2979 }
2980 else
2981 {
2982 // Find a proper constraining point for angle snapping mode
2984
2985 if( Is90Limited() )
2987 else
2989 }
2990 }
2991 else
2992 {
2993 if( line && isPoly )
2994 {
2995 // Restore the line's constraint to CONVERGING mode
2996 EC_CONVERGING* constraint = dynamic_cast<EC_CONVERGING*>( line->GetConstraint() );
2997
2998 if( constraint )
3000 }
3001
3002 m_altConstraint.reset();
3003 }
3004}
3005
3006
3008{
3009 // If there's a behaviour and it provides a constrainer, use that
3010 if( m_editorBehavior )
3011 {
3012 const OPT_VECTOR2I constrainer = m_editorBehavior->Get45DegreeConstrainer( *m_editedPoint, *m_editPoints );
3013
3014 if( constrainer )
3015 return EDIT_POINT( *constrainer );
3016 }
3017
3018 // In any other case we may align item to its original position
3019 return m_original;
3020}
3021
3022
3023// Finds a corresponding vertex in a polygon set
3024static std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX> findVertex( SHAPE_POLY_SET& aPolySet, const EDIT_POINT& aPoint )
3025{
3026 for( auto it = aPolySet.IterateWithHoles(); it; ++it )
3027 {
3028 auto vertexIdx = it.GetIndex();
3029
3030 if( aPolySet.CVertex( vertexIdx ) == aPoint.GetPosition() )
3031 return std::make_pair( true, vertexIdx );
3032 }
3033
3034 return std::make_pair( false, SHAPE_POLY_SET::VERTEX_INDEX() );
3035}
3036
3037
3039{
3040 if( !m_editPoints || !m_editedPoint )
3041 return false;
3042
3043 EDA_ITEM* item = m_editPoints->GetParent();
3044 SHAPE_POLY_SET* polyset = nullptr;
3045
3046 if( !item )
3047 return false;
3048
3049 switch( item->Type() )
3050 {
3051 case PCB_ZONE_T:
3052 polyset = static_cast<ZONE*>( item )->Outline();
3053 break;
3054
3055 case PCB_SHAPE_T:
3056 if( static_cast<PCB_SHAPE*>( item )->GetShape() == SHAPE_T::POLY )
3057 polyset = &static_cast<PCB_SHAPE*>( item )->GetPolyShape();
3058 else
3059 return false;
3060
3061 break;
3062
3063 default:
3064 return false;
3065 }
3066
3067 std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX> vertex = findVertex( *polyset, *m_editedPoint );
3068
3069 if( !vertex.first )
3070 return false;
3071
3072 const SHAPE_POLY_SET::VERTEX_INDEX& vertexIdx = vertex.second;
3073
3074 // Check if there are enough vertices so one can be removed without
3075 // degenerating the polygon.
3076 // The first condition allows one to remove all corners from holes (when
3077 // there are only 2 vertices left, a hole is removed).
3078 if( vertexIdx.m_contour == 0
3079 && polyset->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour].PointCount() <= 3 )
3080 {
3081 return false;
3082 }
3083
3084 // Remove corner does not work with lines
3085 if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
3086 return false;
3087
3088 return m_editedPoint != nullptr;
3089}
3090
3091
3093{
3094 if( !m_editPoints )
3095 return 0;
3096
3097 EDA_ITEM* item = m_editPoints->GetParent();
3099 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
3100
3101 // called without an active edited polygon
3102 if( !item || !CanAddCorner( *item ) )
3103 return 0;
3104
3105 PCB_SHAPE* graphicItem = dynamic_cast<PCB_SHAPE*>( item );
3106 BOARD_COMMIT commit( frame );
3107
3108 if( item->Type() == PCB_ZONE_T || ( graphicItem && graphicItem->GetShape() == SHAPE_T::POLY ) )
3109 {
3110 unsigned int nearestIdx = 0;
3111 unsigned int nextNearestIdx = 0;
3112 unsigned int nearestDist = INT_MAX;
3113 unsigned int firstPointInContour = 0;
3114 SHAPE_POLY_SET* zoneOutline;
3115
3116 if( item->Type() == PCB_ZONE_T )
3117 {
3118 ZONE* zone = static_cast<ZONE*>( item );
3119 zoneOutline = zone->Outline();
3120 zone->SetNeedRefill( true );
3121 }
3122 else
3123 {
3124 zoneOutline = &( graphicItem->GetPolyShape() );
3125 }
3126
3127 commit.Modify( item );
3128
3129 // Search the best outline segment to add a new corner
3130 // and therefore break this segment into two segments
3131
3132 // Object to iterate through the corners of the outlines (main contour and its holes)
3133 SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0, zoneOutline->OutlineCount()-1,
3134 /* IterateHoles */ true );
3135 int curr_idx = 0;
3136
3137 // Iterate through all the corners of the outlines and search the best segment
3138 for( ; iterator; iterator++, curr_idx++ )
3139 {
3140 int jj = curr_idx+1;
3141
3142 if( iterator.IsEndContour() )
3143 { // We reach the last point of the current contour (main or hole)
3144 jj = firstPointInContour;
3145 firstPointInContour = curr_idx+1; // Prepare next contour analysis
3146 }
3147
3148 SEG curr_segment( zoneOutline->CVertex( curr_idx ), zoneOutline->CVertex( jj ) );
3149
3150 unsigned int distance = curr_segment.Distance( cursorPos );
3151
3152 if( distance < nearestDist )
3153 {
3154 nearestDist = distance;
3155 nearestIdx = curr_idx;
3156 nextNearestIdx = jj;
3157 }
3158 }
3159
3160 // Find the point on the closest segment
3161 const VECTOR2I& sideOrigin = zoneOutline->CVertex( nearestIdx );
3162 const VECTOR2I& sideEnd = zoneOutline->CVertex( nextNearestIdx );
3163 SEG nearestSide( sideOrigin, sideEnd );
3164 VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
3165
3166 // Do not add points that have the same coordinates as ones that already belong to polygon
3167 // instead, add a point in the middle of the side
3168 if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
3169 nearestPoint = ( sideOrigin + sideEnd ) / 2;
3170
3171 zoneOutline->InsertVertex( nextNearestIdx, nearestPoint );
3172
3173 if( item->Type() == PCB_ZONE_T )
3174 static_cast<ZONE*>( item )->HatchBorder();
3175
3176 commit.Push( _( "Add Zone Corner" ) );
3177 }
3178 else if( graphicItem )
3179 {
3180 switch( graphicItem->GetShape() )
3181 {
3182 case SHAPE_T::SEGMENT:
3183 {
3184 commit.Modify( graphicItem );
3185
3186 SEG seg( graphicItem->GetStart(), graphicItem->GetEnd() );
3187 VECTOR2I nearestPoint = seg.NearestPoint( cursorPos );
3188
3189 // Move the end of the line to the break point..
3190 graphicItem->SetEnd( nearestPoint );
3191
3192 // and add another one starting from the break point
3193 PCB_SHAPE* newSegment = static_cast<PCB_SHAPE*>( graphicItem->Duplicate( true, &commit ) );
3194 newSegment->ClearSelected();
3195 newSegment->SetStart( nearestPoint );
3196 newSegment->SetEnd( VECTOR2I( seg.B.x, seg.B.y ) );
3197
3198 commit.Add( newSegment );
3199 commit.Push( _( "Split Segment" ) );
3200 break;
3201 }
3202 case SHAPE_T::ARC:
3203 {
3204 commit.Modify( graphicItem );
3205
3206 const SHAPE_ARC arc( graphicItem->GetStart(), graphicItem->GetArcMid(), graphicItem->GetEnd(), 0 );
3207 const VECTOR2I nearestPoint = arc.NearestPoint( cursorPos );
3208
3209 // Move the end of the arc to the break point..
3210 graphicItem->SetEnd( nearestPoint );
3211
3212 // and add another one starting from the break point
3213 PCB_SHAPE* newArc = static_cast<PCB_SHAPE*>( graphicItem->Duplicate( true, &commit ) );
3214
3215 newArc->ClearSelected();
3216 newArc->SetEnd( arc.GetP1() );
3217 newArc->SetStart( nearestPoint );
3218
3219 commit.Add( newArc );
3220 commit.Push( _( "Split Arc" ) );
3221 break;
3222 }
3223 default:
3224 // No split implemented for other shapes
3225 break;
3226 }
3227 }
3228
3229 updatePoints();
3230 return 0;
3231}
3232
3233
3235{
3236 if( !m_editPoints || !m_editedPoint )
3237 return 0;
3238
3239 EDA_ITEM* item = m_editPoints->GetParent();
3240
3241 if( !item )
3242 return 0;
3243
3244 SHAPE_POLY_SET* polygon = nullptr;
3245
3246 if( item->Type() == PCB_ZONE_T )
3247 {
3248 ZONE* zone = static_cast<ZONE*>( item );
3249 polygon = zone->Outline();
3250 zone->SetNeedRefill( true );
3251 }
3252 else if( item->Type() == PCB_SHAPE_T )
3253 {
3254 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
3255
3256 if( shape->GetShape() == SHAPE_T::POLY )
3257 polygon = &shape->GetPolyShape();
3258 }
3259
3260 if( !polygon )
3261 return 0;
3262
3264 BOARD_COMMIT commit( frame );
3265 auto vertex = findVertex( *polygon, *m_editedPoint );
3266
3267 if( vertex.first )
3268 {
3269 const auto& vertexIdx = vertex.second;
3270 auto& outline = polygon->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour];
3271
3272 if( outline.PointCount() > 3 )
3273 {
3274 // the usual case: remove just the corner when there are >3 vertices
3275 commit.Modify( item );
3276 polygon->RemoveVertex( vertexIdx );
3277 }
3278 else
3279 {
3280 // either remove a hole or the polygon when there are <= 3 corners
3281 if( vertexIdx.m_contour > 0 )
3282 {
3283 // remove hole
3284 commit.Modify( item );
3285 polygon->RemoveContour( vertexIdx.m_contour );
3286 }
3287 else
3288 {
3289 m_toolMgr->RunAction( ACTIONS::selectionClear );
3290 commit.Remove( item );
3291 }
3292 }
3293
3294 setEditedPoint( nullptr );
3295
3296 if( item->Type() == PCB_ZONE_T )
3297 commit.Push( _( "Remove Zone Corner" ) );
3298 else
3299 commit.Push( _( "Remove Polygon Corner" ) );
3300
3301 if( item->Type() == PCB_ZONE_T )
3302 static_cast<ZONE*>( item )->HatchBorder();
3303
3304 updatePoints();
3305 }
3306
3307 return 0;
3308}
3309
3310
3312{
3313 if( !m_editPoints || !m_editedPoint )
3314 return 0;
3315
3316 EDA_ITEM* item = m_editPoints->GetParent();
3317
3318 if( !item )
3319 return 0;
3320
3321 SHAPE_POLY_SET* polygon = nullptr;
3322
3323 if( item->Type() == PCB_ZONE_T )
3324 {
3325 ZONE* zone = static_cast<ZONE*>( item );
3326 polygon = zone->Outline();
3327 zone->SetNeedRefill( true );
3328 }
3329 else if( item->Type() == PCB_SHAPE_T )
3330 {
3331 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
3332
3333 if( shape->GetShape() == SHAPE_T::POLY )
3334 polygon = &shape->GetPolyShape();
3335 }
3336
3337 if( !polygon )
3338 return 0;
3339
3340 // Search the best outline corner to break
3341
3343 BOARD_COMMIT commit( frame );
3344 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
3345
3346 unsigned int nearestIdx = 0;
3347 unsigned int nearestDist = INT_MAX;
3348
3349 int curr_idx = 0;
3350 // Object to iterate through the corners of the outlines (main contour and its holes)
3351 SHAPE_POLY_SET::ITERATOR iterator = polygon->Iterate( 0, polygon->OutlineCount() - 1,
3352 /* IterateHoles */ true );
3353
3354 // Iterate through all the corners of the outlines and search the best segment
3355 for( ; iterator; iterator++, curr_idx++ )
3356 {
3357 unsigned int distance = polygon->CVertex( curr_idx ).Distance( cursorPos );
3358
3359 if( distance < nearestDist )
3360 {
3361 nearestDist = distance;
3362 nearestIdx = curr_idx;
3363 }
3364 }
3365
3366 int prevIdx, nextIdx;
3367 if( polygon->GetNeighbourIndexes( nearestIdx, &prevIdx, &nextIdx ) )
3368 {
3369 const SEG segA{ polygon->CVertex( prevIdx ), polygon->CVertex( nearestIdx ) };
3370 const SEG segB{ polygon->CVertex( nextIdx ), polygon->CVertex( nearestIdx ) };
3371
3372 // A plausible setback that won't consume a whole edge
3373 int setback = pcbIUScale.mmToIU( 5 );
3374 setback = std::min( setback, (int) ( segA.Length() * 0.25 ) );
3375 setback = std::min( setback, (int) ( segB.Length() * 0.25 ) );
3376
3377 CHAMFER_PARAMS chamferParams{ setback, setback };
3378
3379 std::optional<CHAMFER_RESULT> chamferResult = ComputeChamferPoints( segA, segB, chamferParams );
3380
3381 if( chamferResult && chamferResult->m_updated_seg_a && chamferResult->m_updated_seg_b )
3382 {
3383 commit.Modify( item );
3384 polygon->RemoveVertex( nearestIdx );
3385
3386 // The two end points of the chamfer are the new corners
3387 polygon->InsertVertex( nearestIdx, chamferResult->m_updated_seg_b->B );
3388 polygon->InsertVertex( nearestIdx, chamferResult->m_updated_seg_a->B );
3389 }
3390 }
3391
3392 setEditedPoint( nullptr );
3393
3394 if( item->Type() == PCB_ZONE_T )
3395 commit.Push( _( "Break Zone Corner" ) );
3396 else
3397 commit.Push( _( "Break Polygon Corner" ) );
3398
3399 if( item->Type() == PCB_ZONE_T )
3400 static_cast<ZONE*>( item )->HatchBorder();
3401
3402 updatePoints();
3403
3404 return 0;
3405}
3406
3407
3409{
3410 updatePoints();
3411 return 0;
3412}
3413
3414
3416{
3418
3419 if( aEvent.Matches( ACTIONS::cycleArcEditMode.MakeEvent() ) )
3420 {
3421 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
3423 else
3425
3427 }
3428 else
3429 {
3431 }
3432
3433 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
3435 else
3437
3438 return 0;
3439}
3440
3441
3443{
3461}
BOX2I getBoundingBox(BOARD_ITEM *aItem)
ARC_EDIT_MODE
Settings for arc editing.
@ KEEP_CENTER_ADJUST_ANGLE_RADIUS
When editing endpoints, the angle and radius are adjusted.
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:112
BOX2< VECTOR2I > BOX2I
Definition box2.h:922
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:990
static TOOL_ACTION cycleArcEditMode
Definition actions.h:272
static TOOL_ACTION pointEditorArcKeepCenter
Definition actions.h:273
static TOOL_ACTION pointEditorArcKeepRadius
Definition actions.h:275
static TOOL_ACTION reselectItem
Definition actions.h:229
static TOOL_ACTION activatePointEditor
Definition actions.h:271
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:224
static TOOL_ACTION pointEditorArcKeepEndpoint
Definition actions.h:274
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.
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.
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
BARCODE_POINT_EDIT_BEHAVIOR(PCB_BARCODE &aBarcode)
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.
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, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE) 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:83
virtual BOARD_ITEM * Duplicate(bool addToParentGroup, BOARD_COMMIT *aCommit=nullptr) const
Create a copy of this BOARD_ITEM.
bool IsLocked() const override
FOOTPRINT * GetParentFootprint() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:256
int GetMaxError() const
constexpr void SetMaximum()
Definition box2.h:80
constexpr const Vec GetEnd() const
Definition box2.h:212
constexpr size_type GetWidth() const
Definition box2.h:214
constexpr Vec Centre() const
Definition box2.h:97
constexpr BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition box2.h:658
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 const Vec & GetOrigin() const
Definition box2.h:210
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:72
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:90
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition commit.h:106
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:78
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.
bool 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.
bool 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.
bool 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 that imposes a constraint that a point has to be located at angle of 90 degree multip...
EDIT_CONSTRAINT for polygon line dragging.
POLYGON_LINE_MODE GetMode() const
Get the current constraint mode.
void SetMode(POLYGON_LINE_MODE aMode)
Set the constraint mode (allows switching between converging and fixed-length)
EDIT_CONSTRAINT that imposes a constraint that two points have to have the same Y coordinate.
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.
EDIT_CONSTRAINT that imposes a constraint that two points have to have the same X coordinate.
double AsDegrees() const
Definition eda_angle.h:116
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:98
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearSelected()
Definition eda_item.h:143
virtual void SetParent(EDA_ITEM *aParent)
Definition eda_item.h:113
virtual EDA_ITEM * Clone() const
Create a duplicate of this item with linked list members set to NULL.
Definition eda_item.cpp:118
EDA_ITEM(EDA_ITEM *parent, KICAD_T idType, bool isSCH_ITEM=false, bool isBOARD_ITEM=false)
Definition eda_item.cpp:39
virtual VECTOR2I GetTopLeft() const
Definition eda_shape.h:246
void SetCornerRadius(int aRadius)
SHAPE_POLY_SET & GetPolyShape()
Definition eda_shape.h:336
SHAPE_T GetShape() const
Definition eda_shape.h:168
virtual VECTOR2I GetBotRight() const
Definition eda_shape.h:247
virtual void SetBottom(int val)
Definition eda_shape.h:252
virtual void SetTop(int val)
Definition eda_shape.h:249
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:215
void SetStart(const VECTOR2I &aStart)
Definition eda_shape.h:177
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition eda_shape.h:173
void SetEnd(const VECTOR2I &aEnd)
Definition eda_shape.h:219
virtual void SetLeft(int val)
Definition eda_shape.h:250
virtual void SetRight(int val)
Definition eda_shape.h:251
int GetCornerRadius() const
VECTOR2I GetArcMid() const
Describe constraints between two edit handles.
Represent a line connecting two EDIT_POINTs.
void SetConstraint(EDIT_CONSTRAINT< EDIT_LINE > *aConstraint)
Set a constraint for and EDIT_POINT.
EDIT_CONSTRAINT< EDIT_LINE > * GetConstraint() const
Return the constraint imposed on an EDIT_POINT.
EDIT_POINTS is a VIEW_ITEM that manages EDIT_POINTs and EDIT_LINEs and draws them.
unsigned int PointsSize() const
Return number of stored EDIT_POINTs.
void AddPoint(const EDIT_POINT &aPoint)
Add an EDIT_POINT.
void SetSwapY(bool aSwap)
void Clear()
Clear all stored EDIT_POINTs and EDIT_LINEs.
EDIT_LINE & Line(unsigned int aIndex)
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,...
bool SwapX() const
bool SwapY() const
void SetSwapX(bool aSwap)
unsigned int LinesSize() const
Return number of stored EDIT_LINEs.
EDIT_POINT & Point(unsigned int aIndex)
void AddLine(const EDIT_LINE &aLine)
Adds an EDIT_LINE.
Represent a single point that can be used for modifying items.
Definition edit_points.h:48
int GetY() const
Return Y coordinate of an EDIT_POINT.
Definition edit_points.h:96
virtual void SetPosition(const VECTOR2I &aPosition)
Set new coordinates for an EDIT_POINT.
virtual VECTOR2I GetPosition() const
Return coordinates of an EDIT_POINT.
Definition edit_points.h:72
void SetSnapConstraint(SNAP_CONSTRAINT_TYPE aConstraint)
void SetConstraint(EDIT_CONSTRAINT< EDIT_POINT > *aConstraint)
Set a constraint for an EDIT_POINT.
int GetX() const
Return X coordinate of an EDIT_POINT.
Definition edit_points.h:88
void SetActive(bool aActive=true)
void SetDrawCircle(bool aDrawCircle=true)
static const TOOL_EVENT InhibitSelectionEditing
Definition actions.h:358
static const TOOL_EVENT SelectedEvent
Definition actions.h:345
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:352
static const TOOL_EVENT UninhibitSelectionEditing
Used to inform tool that it should display the disambiguation menu.
Definition actions.h:359
static const TOOL_EVENT PointSelectedEvent
Definition actions.h:344
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition actions.h:355
static const TOOL_EVENT UnselectedEvent
Definition actions.h:346
std::deque< PAD * > & Pads()
Definition footprint.h:304
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
void UpdateItem(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) override
Update the item with the new positions of the edit points.
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.
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:66
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition view.cpp:298
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition view.cpp:341
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:1685
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllLayersMask()
Definition lset.cpp:624
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
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.
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
PAD_POINT_EDIT_BEHAVIOR(PAD &aPad, PCB_LAYER_ID aLayer)
Definition pad.h:55
ARC_EDIT_MODE m_ArcEditMode
static TOOL_ACTION layerChanged
static TOOL_ACTION pointEditorMoveMidpoint
static TOOL_ACTION genFinishEdit
static TOOL_ACTION genStartEdit
static TOOL_ACTION pointEditorMoveCorner
static TOOL_ACTION genCancelEdit
static TOOL_ACTION genUpdateEdit
static TOOL_ACTION pointEditorChamferCorner
static TOOL_ACTION pointEditorRemoveCorner
static TOOL_ACTION pointEditorAddCorner
Common, abstract interface for edit frames.
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
PCBNEW_SETTINGS * GetPcbNewSettings() const
virtual MAGNETIC_SETTINGS * GetMagneticItemsSettings()
FOOTPRINT_EDITOR_SETTINGS * GetFootprintEditorSettings() const
For better understanding of the points that make a dimension:
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.
virtual wxString GetCommitMessage() const =0
A set of BOARD_ITEMs (i.e., without duplicates).
Definition pcb_group.h:53
std::unordered_set< BOARD_ITEM * > GetBoardItems() const
int changeArcEditMode(const TOOL_EVENT &aEvent)
bool CanRemoveCorner(const SELECTION &aSelection)
Condition to display "Remove Corner" context menu entry.
void updateItem(BOARD_COMMIT &aCommit)
Update edit points with item's points.
VECTOR2I m_stickyDisplacement
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
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.
static bool CanChamferCorner(const EDA_ITEM &aItem)
Check if a corner of the given item can be chamfered (zones, polys only).
EDIT_POINT get45DegConstrainer() const
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.
static bool CanAddCorner(const EDA_ITEM &aItem)
Check if a corner can be added to the given item (zones, polys, segments, arcs).
void setEditedPoint(EDIT_POINT *aPoint)
EDIT_POINT m_altConstrainer
ARC_EDIT_MODE m_arcEditMode
std::unique_ptr< KIGFX::PREVIEW::ANGLE_ITEM > m_angleItem
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
int getEditedPointIndex() const
Return true if aPoint is the currently modified point.
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.
The selection tool: currently supports:
VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition pcb_shape.h:81
bool IsProxyItem() const override
Definition pcb_shape.h:116
void Move(const VECTOR2I &aMoveVector) override
Move this object.
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.
T * frame() const
KIGFX::PCB_VIEW * view() const
virtual bool Is45Limited() const
Should the tool use its 45° mode option?
KIGFX::VIEW_CONTROLS * controls() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
virtual bool Is90Limited() const
Should the tool limit drawing to horizontal and vertical only?
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.
POLYGON_POINT_EDIT_BEHAVIOR(SHAPE_POLY_SET &aPolygon)
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)
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
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 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 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 UpdatePoints(const PCB_SHAPE &aRectangle, EDIT_POINTS &aPoints)
static void UpdateItem(PCB_SHAPE &aRectangle, const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints)
RECTANGLE_POINT_EDIT_BEHAVIOR(PCB_SHAPE &aRectangle)
const EDA_IU_SCALE & m_iuScale
wxString GetClass() const override
Return the class name.
std::vector< int > ViewGetLayers() const override
Return the all the layers within the VIEW the object is painted on.
const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
void Set(int aRadius, const VECTOR2I &aCorner, const VECTOR2I &aQuadrant, EDA_UNITS aUnits)
RECT_RADIUS_TEXT_ITEM(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits)
void ViewDraw(int aLayer, KIGFX::VIEW *aView) const override
Draw the parts of the object belonging to layer aLayer.
REFERENCE_IMAGE_POINT_EDIT_BEHAVIOR(PCB_REFERENCE_IMAGE &aRefImage)
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
void UpdateItem(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) override
Update the item with the new positions of the edit points.
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.
Definition seg.h:42
VECTOR2I B
Definition seg.h:50
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition seg.cpp:633
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:702
EDA_ANGLE Angle(const SEG &aOther) const
Determine the smallest angle between two segments.
Definition seg.cpp:111
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.
VECTOR2I NearestPoint(const VECTOR2I &aP) const
const VECTOR2I & GetP1() const
Definition shape_arc.h:119
SHAPE_GROUP_POINT_EDIT_BEHAVIOR(PCB_GROUP &aGroup)
SHAPE_GROUP_POINT_EDIT_BEHAVIOR(std::vector< PCB_SHAPE * > aShapes, BOARD_ITEM *aParent)
void MakePoints(EDIT_POINTS &aPoints) override
Construct the initial set of edit points for the item and append to the given list.
std::unordered_map< PCB_SHAPE *, double > m_originalWidths
std::vector< PCB_SHAPE * > m_shapes
void UpdateItem(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) override
Update the item with the new positions of the edit points.
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
Represent a set of closed polygons.
ITERATOR_TEMPLATE< VECTOR2I > ITERATOR
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.
bool UpdatePoints(EDIT_POINTS &aPoints) override
Update the list of the edit points for the item.
void UpdateItem(const EDIT_POINT &aEditedPoint, EDIT_POINTS &aPoints, COMMIT &aCommit, std::vector< EDA_ITEM * > &aUpdatedItems) override
Update the item with the new positions of the edit points.
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:186
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:171
bool Matches(const TOOL_EVENT &aEvent) const
Test whether two events match in terms of category & action or command.
Definition tool_event.h:392
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:293
bool IsDrag(int aButtonMask=BUT_ANY) const
Definition tool_event.h:315
const VECTOR2D DragOrigin() const
Return the point where dragging has started.
Definition tool_event.h:299
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:473
bool IsMotion() const
Definition tool_event.h:330
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_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.
EDA_UNITS GetUserUnits() const
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
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
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:74
void SetNeedRefill(bool aNeedRefill)
Definition zone.h:292
SHAPE_POLY_SET * Outline()
Definition zone.h:331
@ 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.
@ ARROW
Definition cursors.h:46
#define _(s)
@ RECURSE
Definition eda_item.h:51
#define IS_MOVING
Item being moved.
SHAPE_T
Definition eda_shape.h:43
@ SEGMENT
Definition eda_shape.h:45
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:46
@ NO_FILL
Definition eda_shape.h:57
EDA_UNITS
Definition eda_units.h:48
@ ALL_LAYERS
@ OBJECT_LAYERS
@ IGNORE_SNAPS
POLYGON_LINE_MODE
Mode for polygon line edge constraints.
@ CONVERGING
Adjacent lines converge/diverge, dragged line length changes.
@ FIXED_LENGTH
Dragged line maintains its length, adjacent lines adjust angles.
@ SNAP_BY_GRID
@ SNAP_TO_GRID
@ FRAME_PCB_EDITOR
Definition frame_type.h:42
a few functions useful in geometry calculations.
VECTOR2< ret_type > GetClampedCoords(const VECTOR2< in_type > &aCoords, pad_type aPadding=1u)
Clamps a vector to values that can be negated, respecting numeric limits of coordinates data type wit...
bool IsFrontLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a front layer.
Definition layer_ids.h:780
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:677
@ LAYER_GP_OVERLAY
General purpose overlay.
Definition layer_ids.h:279
@ LAYER_SELECT_OVERLAY
Selected items overlay.
Definition layer_ids.h:280
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:175
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)
void DrawTextNextToCursor(KIGFX::VIEW *aView, const VECTOR2D &aCursorPos, const VECTOR2D &aTextQuadrant, const wxArrayString &aStrings, bool aDrawingDropShadows)
Draw strings next to the cursor.
wxString DimensionLabel(const wxString &prefix, double aVal, const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, bool aIncludeUnits=true)
Get a formatted string showing a dimension to a sane precision with an optional prefix and unit suffi...
STL namespace.
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
@ CHAMFERED_RECT
Definition padstack.h:60
@ ROUNDRECT
Definition padstack.h:57
@ TRAPEZOID
Definition padstack.h:56
@ RECTANGLE
Definition padstack.h:54
BARCODE class definition.
@ MANUAL
Text placement is manually set by the user.
#define STATUS_ITEMS_ONLY
Class to handle a set of BOARD_ITEMs.
static VECTOR2I snapCorner(const VECTOR2I &aPrev, const VECTOR2I &aNext, const VECTOR2I &aGuess, double aAngleDeg)
@ RECT_RIGHT
@ RECT_LEFT
static std::pair< bool, SHAPE_POLY_SET::VERTEX_INDEX > findVertex(SHAPE_POLY_SET &aPolySet, const EDIT_POINT &aPoint)
static std::vector< VECTOR2I > getConstraintDirections(EDIT_CONSTRAINT< EDIT_POINT > *aConstraint)
TEXTBOX_POINT_COUNT
@ WHEN_POLYGON
@ WHEN_RECTANGLE
@ RECT_MAX_POINTS
@ RECT_BOT_LEFT
@ RECT_BOT_RIGHT
@ RECT_CENTER
@ RECT_TOP_RIGHT
@ RECT_RADIUS
@ RECT_TOP_LEFT
DIMENSION_POINTS
@ DIM_LEADER_MAX
@ DIM_RADIAL_MAX
@ DIM_CROSSBAREND
@ DIM_START
@ DIM_ALIGNED_MAX
@ DIM_CENTER_MAX
@ DIM_CROSSBARSTART
static void appendDirection(std::vector< VECTOR2I > &aDirections, const VECTOR2I &aDirection)
ARC_EDIT_MODE IncrementArcEditMode(ARC_EDIT_MODE aMode)
#define CHECK_POINT_COUNT(aPoints, aExpected)
#define CHECK_POINT_COUNT_GE(aPoints, aExpected)
CITER next(CITER it)
Definition ptree.cpp:124
static float distance(const SFVEC2UI &a, const SFVEC2UI &b)
SCH_CONDITIONS S_C
@ RECT_CENTER
@ RECT_RADIUS
@ RECT_RIGHT
@ RECT_LEFT
@ RECT_BOT
@ RECT_TOP
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:39
const int scale
std::vector< FAB_LAYER_COLOR > dummy
Parameters that define a simple chamfer operation.
Structure to hold the necessary information in order to index a vertex on a SHAPE_POLY_SET object: th...
KIBIS top(path, &reporter)
VECTOR2I center
int radius
VECTOR2I end
int delta
GR_TEXT_H_ALIGN_T
This is API surface mapped to common.types.HorizontalAlignment.
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
#define M_PI
@ 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_CTRL
Definition tool_event.h:144
@ 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
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:78
@ 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:106
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:103
@ 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:104
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:111
@ 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:108
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:89
@ NOT_USED
the 3d code uses this value
Definition typeinfo.h:79
@ PCB_BARCODE_T
class PCB_BARCODE, a barcode (graphic item)
Definition typeinfo.h:101
@ 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:102
@ 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:105
constexpr int sign(T val)
Definition util.h:145
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.