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 )
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 {
1653 // Store original line widths as doubles for better scaling precision
1654 for( BOARD_ITEM* item : m_group.GetBoardItems() )
1655 {
1656 if( item->Type() == PCB_SHAPE_T )
1657 {
1658 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
1659 m_originalWidths[shape] = static_cast<double>( shape->GetWidth() );
1660 }
1661 }
1662 }
1663
1664 void MakePoints( EDIT_POINTS& aPoints ) override
1665 {
1666 BOX2I bbox = m_group.GetBoundingBox();
1667 VECTOR2I tl = bbox.GetOrigin();
1668 VECTOR2I br = bbox.GetEnd();
1669
1670 aPoints.AddPoint( tl );
1671 aPoints.AddPoint( VECTOR2I( br.x, tl.y ) );
1672 aPoints.AddPoint( br );
1673 aPoints.AddPoint( VECTOR2I( tl.x, br.y ) );
1674 aPoints.AddPoint( bbox.Centre() );
1675
1676 aPoints.AddIndicatorLine( aPoints.Point( RECT_TOP_LEFT ), aPoints.Point( RECT_TOP_RIGHT ) );
1677 aPoints.AddIndicatorLine( aPoints.Point( RECT_TOP_RIGHT ), aPoints.Point( RECT_BOT_RIGHT ) );
1678 aPoints.AddIndicatorLine( aPoints.Point( RECT_BOT_RIGHT ), aPoints.Point( RECT_BOT_LEFT ) );
1679 aPoints.AddIndicatorLine( aPoints.Point( RECT_BOT_LEFT ), aPoints.Point( RECT_TOP_LEFT ) );
1680 }
1681
1682 bool UpdatePoints( EDIT_POINTS& aPoints ) override
1683 {
1684 BOX2I bbox = m_group.GetBoundingBox();
1685 VECTOR2I tl = bbox.GetOrigin();
1686 VECTOR2I br = bbox.GetEnd();
1687
1688 aPoints.Point( RECT_TOP_LEFT ).SetPosition( tl );
1689 aPoints.Point( RECT_TOP_RIGHT ).SetPosition( br.x, tl.y );
1690 aPoints.Point( RECT_BOT_RIGHT ).SetPosition( br );
1691 aPoints.Point( RECT_BOT_LEFT ).SetPosition( tl.x, br.y );
1692 aPoints.Point( RECT_CENTER ).SetPosition( bbox.Centre() );
1693 return true;
1694 }
1695
1696 void UpdateItem( const EDIT_POINT& aEditedPoint, EDIT_POINTS& aPoints, COMMIT& aCommit,
1697 std::vector<EDA_ITEM*>& aUpdatedItems ) override
1698 {
1699 BOX2I oldBox = m_group.GetBoundingBox();
1700 VECTOR2I oldCenter = oldBox.Centre();
1701
1702 if( isModified( aEditedPoint, aPoints.Point( RECT_CENTER ) ) )
1703 {
1704 VECTOR2I delta = aPoints.Point( RECT_CENTER ).GetPosition() - oldCenter;
1705
1706 aCommit.Modify( &m_group, nullptr, RECURSE_MODE::RECURSE );
1707 m_group.Move( delta );
1708
1709 for( BOARD_ITEM* item : m_group.GetBoardItems() )
1710 aUpdatedItems.push_back( item );
1711
1712 UpdatePoints( aPoints );
1713 return;
1714 }
1715
1716 VECTOR2I tl = aPoints.Point( RECT_TOP_LEFT ).GetPosition();
1717 VECTOR2I tr = aPoints.Point( RECT_TOP_RIGHT ).GetPosition();
1718 VECTOR2I bl = aPoints.Point( RECT_BOT_LEFT ).GetPosition();
1719 VECTOR2I br = aPoints.Point( RECT_BOT_RIGHT ).GetPosition();
1720
1721 RECTANGLE_POINT_EDIT_BEHAVIOR::PinEditedCorner( aEditedPoint, aPoints, tl, tr, bl, br );
1722
1723 double sx = static_cast<double>( br.x - tl.x ) / static_cast<double>( oldBox.GetWidth() );
1724 double sy = static_cast<double>( br.y - tl.y ) / static_cast<double>( oldBox.GetHeight() );
1725 double scale = ( sx + sy ) / 2.0;
1726
1727 for( BOARD_ITEM* item : m_group.GetBoardItems() )
1728 {
1729 if( item->Type() != PCB_SHAPE_T )
1730 continue;
1731
1732 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
1733 aCommit.Modify( shape );
1734 shape->Move( -oldCenter );
1735 shape->Scale( scale );
1736 shape->Move( oldCenter );
1737
1738 if( auto shapeIt = m_originalWidths.find( shape ); shapeIt != m_originalWidths.end() )
1739 {
1740 shapeIt->second = shapeIt->second * scale;
1741 shape->SetWidth( KiROUND( shapeIt->second ) );
1742 }
1743 else
1744 {
1745 // Shouldn't happen, but just in case
1746 shape->SetWidth( KiROUND( shape->GetWidth() * scale ) );
1747 }
1748
1749 aUpdatedItems.push_back( shape );
1750 }
1751
1752 UpdatePoints( aPoints );
1753 }
1754
1755private:
1757 std::unordered_map<PCB_SHAPE*, double> m_originalWidths;
1758};
1759
1760
1762 PCB_TOOL_BASE( "pcbnew.PointEditor" ),
1763 m_frame( nullptr ),
1764 m_selectionTool( nullptr ),
1765 m_editedPoint( nullptr ),
1766 m_hoveredPoint( nullptr ),
1767 m_original( VECTOR2I( 0, 0 ) ),
1769 m_altConstrainer( VECTOR2I( 0, 0 ) ),
1770 m_inPointEditorTool( false ),
1771 m_angleSnapPos( VECTOR2I( 0, 0 ) ),
1772 m_stickyDisplacement( VECTOR2I( 0, 0 ) ),
1773 m_angleSnapActive( false )
1774{}
1775
1776
1778{
1780 m_editPoints.reset();
1781 m_altConstraint.reset();
1782 getViewControls()->SetAutoPan( false );
1783 m_angleSnapActive = false;
1785}
1786
1787
1791static bool canAddCorner( const EDA_ITEM& aItem )
1792{
1793 const KICAD_T type = aItem.Type();
1794
1795 if( type == PCB_ZONE_T )
1796 return true;
1797
1798 if( type == PCB_SHAPE_T )
1799 {
1800 const PCB_SHAPE& shape = static_cast<const PCB_SHAPE&>( aItem );
1801 const SHAPE_T shapeType = shape.GetShape();
1802 return shapeType == SHAPE_T::SEGMENT || shapeType == SHAPE_T::POLY || shapeType == SHAPE_T::ARC;
1803 }
1804
1805 return false;
1806}
1807
1808
1812static bool canChamferCorner( const EDA_ITEM& aItem )
1813{
1814 const auto type = aItem.Type();
1815
1816 // Works only for zones and polygons
1817 if( type == PCB_ZONE_T )
1818 return true;
1819
1820 if( type == PCB_SHAPE_T )
1821 {
1822 const PCB_SHAPE& shape = static_cast<const PCB_SHAPE&>( aItem );
1823 const SHAPE_T shapeType = shape.GetShape();
1824 return shapeType == SHAPE_T::POLY;
1825 }
1826
1827 return false;
1828}
1829
1830
1831static VECTOR2I snapCorner( const VECTOR2I& aPrev, const VECTOR2I& aNext, const VECTOR2I& aGuess,
1832 double aAngleDeg )
1833{
1834 double angleRad = aAngleDeg * M_PI / 180.0;
1835 VECTOR2D prev( aPrev );
1836 VECTOR2D next( aNext );
1837 double chord = ( next - prev ).EuclideanNorm();
1838 double sinA = sin( angleRad );
1839
1840 if( chord == 0.0 || fabs( sinA ) < 1e-9 )
1841 return aGuess;
1842
1843 double radius = chord / ( 2.0 * sinA );
1844 VECTOR2D mid = ( prev + next ) / 2.0;
1845 VECTOR2D dir = next - prev;
1846 VECTOR2D normal( -dir.y, dir.x );
1847 normal = normal.Resize( 1 );
1848 double h_sq = radius * radius - ( chord * chord ) / 4.0;
1849 double h = h_sq > 0.0 ? sqrt( h_sq ) : 0.0;
1850
1851 VECTOR2D center1 = mid + normal * h;
1852 VECTOR2D center2 = mid - normal * h;
1853
1854 auto project =
1855 [&]( const VECTOR2D& center )
1856 {
1857 VECTOR2D v = VECTOR2D( aGuess ) - center;
1858
1859 if( v.EuclideanNorm() == 0.0 )
1860 v = prev - center;
1861
1862 v = v.Resize( 1 );
1863 VECTOR2D p = center + v * radius;
1864 return VECTOR2I( KiROUND( p.x ), KiROUND( p.y ) );
1865 };
1866
1867 VECTOR2I p1 = project( center1 );
1868 VECTOR2I p2 = project( center2 );
1869
1870 double d1 = ( VECTOR2D( aGuess ) - VECTOR2D( p1 ) ).EuclideanNorm();
1871 double d2 = ( VECTOR2D( aGuess ) - VECTOR2D( p2 ) ).EuclideanNorm();
1872
1873 return d1 < d2 ? p1 : p2;
1874}
1875
1876
1878{
1879 // Find the selection tool, so they can cooperate
1881
1882 wxASSERT_MSG( m_selectionTool, wxT( "pcbnew.InteractiveSelection tool is not available" ) );
1883
1884 const auto addCornerCondition =
1885 []( const SELECTION& aSelection ) -> bool
1886 {
1887 const EDA_ITEM* item = aSelection.Front();
1888 return ( item != nullptr ) && canAddCorner( *item );
1889 };
1890
1891 const auto addChamferCondition =
1892 []( const SELECTION& aSelection ) -> bool
1893 {
1894 const EDA_ITEM* item = aSelection.Front();
1895 return ( item != nullptr ) && canChamferCorner( *item );
1896 };
1897
1898 const auto removeCornerCondition =
1899 [this]( const SELECTION& aSelection ) -> bool
1900 {
1901 return PCB_POINT_EDITOR::removeCornerCondition( aSelection );
1902 };
1903
1904 const auto arcIsEdited =
1905 []( const SELECTION& aSelection ) -> bool
1906 {
1907 const EDA_ITEM* item = aSelection.Front();
1908 return ( item != nullptr ) && ( item->Type() == PCB_SHAPE_T )
1909 && static_cast<const PCB_SHAPE*>( item )->GetShape() == SHAPE_T::ARC;
1910 };
1911
1912 using S_C = SELECTION_CONDITIONS;
1913
1914 auto& menu = m_selectionTool->GetToolMenu().GetMenu();
1915
1916 // clang-format off
1917 menu.AddItem( PCB_ACTIONS::pointEditorAddCorner, S_C::Count( 1 ) && addCornerCondition );
1919 menu.AddItem( PCB_ACTIONS::pointEditorChamferCorner, S_C::Count( 1 ) && addChamferCondition );
1920 menu.AddItem( PCB_ACTIONS::cycleArcEditMode, S_C::Count( 1 ) && arcIsEdited );
1921 // clang-format on
1922
1923 return true;
1924}
1925
1926
1927std::shared_ptr<EDIT_POINTS> PCB_POINT_EDITOR::makePoints( EDA_ITEM* aItem )
1928{
1929 std::shared_ptr<EDIT_POINTS> points = std::make_shared<EDIT_POINTS>( aItem );
1930
1931 if( !aItem )
1932 return points;
1933
1934 // Reset the behaviour and we'll make a new one
1935 m_editorBehavior = nullptr;
1936
1937 switch( aItem->Type() )
1938 {
1940 {
1941 PCB_REFERENCE_IMAGE& refImage = static_cast<PCB_REFERENCE_IMAGE&>( *aItem );
1942 m_editorBehavior = std::make_unique<REFERENCE_IMAGE_POINT_EDIT_BEHAVIOR>( refImage );
1943 break;
1944 }
1945 case PCB_BARCODE_T:
1946 {
1947 PCB_BARCODE& barcode = static_cast<PCB_BARCODE&>( *aItem );
1948 m_editorBehavior = std::make_unique<BARCODE_POINT_EDIT_BEHAVIOR>( barcode );
1949 break;
1950 }
1951 case PCB_TEXTBOX_T:
1952 {
1953 PCB_TEXTBOX& textbox = static_cast<PCB_TEXTBOX&>( *aItem );
1954 m_editorBehavior = std::make_unique<TEXTBOX_POINT_EDIT_BEHAVIOR>( textbox );
1955 break;
1956 }
1957 case PCB_SHAPE_T:
1958 {
1959 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( aItem );
1960
1961 switch( shape->GetShape() )
1962 {
1963 case SHAPE_T::SEGMENT:
1964 m_editorBehavior = std::make_unique<EDA_SEGMENT_POINT_EDIT_BEHAVIOR>( *shape );
1965 break;
1966
1967 case SHAPE_T::RECTANGLE:
1968 m_editorBehavior = std::make_unique<RECTANGLE_POINT_EDIT_BEHAVIOR>( *shape );
1969 break;
1970
1971 case SHAPE_T::ARC:
1972 m_editorBehavior = std::make_unique<EDA_ARC_POINT_EDIT_BEHAVIOR>( *shape, m_arcEditMode,
1973 *getViewControls() );
1974 break;
1975
1976 case SHAPE_T::CIRCLE:
1977 m_editorBehavior = std::make_unique<EDA_CIRCLE_POINT_EDIT_BEHAVIOR>( *shape );
1978 break;
1979
1980 case SHAPE_T::POLY:
1981 m_editorBehavior = std::make_unique<EDA_POLYGON_POINT_EDIT_BEHAVIOR>( *shape );
1982 break;
1983
1984 case SHAPE_T::BEZIER:
1985 m_editorBehavior = std::make_unique<EDA_BEZIER_POINT_EDIT_BEHAVIOR>( *shape,
1986 shape->GetMaxError() );
1987 break;
1988
1989 default: // suppress warnings
1990 break;
1991 }
1992
1993 break;
1994 }
1995
1996 case PCB_GROUP_T:
1997 {
1998 PCB_GROUP* group = static_cast<PCB_GROUP*>( aItem );
1999 bool shapesOnly = true;
2000
2001 for( BOARD_ITEM* child : group->GetBoardItems() )
2002 {
2003 if( child->Type() != PCB_SHAPE_T )
2004 {
2005 shapesOnly = false;
2006 break;
2007 }
2008 }
2009
2010 if( shapesOnly )
2011 m_editorBehavior = std::make_unique<SHAPE_GROUP_POINT_EDIT_BEHAVIOR>( *group );
2012 else
2013 points.reset();
2014
2015 break;
2016 }
2017
2018 case PCB_TABLECELL_T:
2019 {
2020 PCB_TABLECELL* cell = static_cast<PCB_TABLECELL*>( aItem );
2021
2022 // No support for point-editing of a rotated table
2023 if( cell->GetShape() == SHAPE_T::RECTANGLE )
2024 m_editorBehavior = std::make_unique<PCB_TABLECELL_POINT_EDIT_BEHAVIOR>( *cell );
2025
2026 break;
2027 }
2028
2029 case PCB_PAD_T:
2030 {
2031 // Pad edit only for the footprint editor
2033 {
2034 PAD& pad = static_cast<PAD&>( *aItem );
2035 PCB_LAYER_ID activeLayer = m_frame ? m_frame->GetActiveLayer() : PADSTACK::ALL_LAYERS;
2036
2037 // Point editor only handles copper shape changes
2038 if( !IsCopperLayer( activeLayer ) )
2039 activeLayer = IsFrontLayer( activeLayer ) ? F_Cu : B_Cu;
2040
2041 m_editorBehavior = std::make_unique<PAD_POINT_EDIT_BEHAVIOR>( pad, activeLayer );
2042 }
2043 break;
2044 }
2045
2046 case PCB_ZONE_T:
2047 {
2048 ZONE& zone = static_cast<ZONE&>( *aItem );
2049 m_editorBehavior = std::make_unique<ZONE_POINT_EDIT_BEHAVIOR>( zone );
2050 break;
2051 }
2052
2053 case PCB_GENERATOR_T:
2054 {
2055 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( aItem );
2056 m_editorBehavior = std::make_unique<GENERATOR_POINT_EDIT_BEHAVIOR>( *generator );
2057 break;
2058 }
2059
2060 case PCB_DIM_ALIGNED_T:
2062 {
2063 PCB_DIM_ALIGNED& dimension = static_cast<PCB_DIM_ALIGNED&>( *aItem );
2064 m_editorBehavior = std::make_unique<ALIGNED_DIMENSION_POINT_EDIT_BEHAVIOR>( dimension );
2065 break;
2066 }
2067
2068 case PCB_DIM_CENTER_T:
2069 {
2070 PCB_DIM_CENTER& dimension = static_cast<PCB_DIM_CENTER&>( *aItem );
2071 m_editorBehavior = std::make_unique<DIM_CENTER_POINT_EDIT_BEHAVIOR>( dimension );
2072 break;
2073 }
2074
2075 case PCB_DIM_RADIAL_T:
2076 {
2077 PCB_DIM_RADIAL& dimension = static_cast<PCB_DIM_RADIAL&>( *aItem );
2078 m_editorBehavior = std::make_unique<DIM_RADIAL_POINT_EDIT_BEHAVIOR>( dimension );
2079 break;
2080 }
2081
2082 case PCB_DIM_LEADER_T:
2083 {
2084 PCB_DIM_LEADER& dimension = static_cast<PCB_DIM_LEADER&>( *aItem );
2085 m_editorBehavior = std::make_unique<DIM_LEADER_POINT_EDIT_BEHAVIOR>( dimension );
2086 break;
2087 }
2088
2089 default:
2090 points.reset();
2091 break;
2092 }
2093
2094 if( m_editorBehavior )
2095 m_editorBehavior->MakePoints( *points );
2096
2097 return points;
2098}
2099
2100
2102{
2103 EDIT_POINT* point;
2104 EDIT_POINT* hovered = nullptr;
2105
2106 if( aEvent.IsMotion() )
2107 {
2108 point = m_editPoints->FindPoint( aEvent.Position(), getView() );
2109 hovered = point;
2110 }
2111 else if( aEvent.IsDrag( BUT_LEFT ) )
2112 {
2113 point = m_editPoints->FindPoint( aEvent.DragOrigin(), getView() );
2114 }
2115 else
2116 {
2117 point = m_editPoints->FindPoint( getViewControls()->GetCursorPosition(), getView() );
2118 }
2119
2120 if( hovered )
2121 {
2122 if( m_hoveredPoint != hovered )
2123 {
2124 if( m_hoveredPoint )
2125 m_hoveredPoint->SetHover( false );
2126
2127 m_hoveredPoint = hovered;
2128 m_hoveredPoint->SetHover();
2129 }
2130 }
2131 else if( m_hoveredPoint )
2132 {
2133 m_hoveredPoint->SetHover( false );
2134 m_hoveredPoint = nullptr;
2135 }
2136
2137 if( m_editedPoint != point )
2138 setEditedPoint( point );
2139}
2140
2141
2143{
2145 return 0;
2146
2148 return 0;
2149
2151
2153 const PCB_SELECTION& selection = m_selectionTool->GetSelection();
2154
2155 if( selection.Size() != 1 || selection.Front()->GetEditFlags() || !selection.Front()->IsBOARD_ITEM() )
2156 return 0;
2157
2158 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( selection.Front() );
2159
2160 if( !item || item->IsLocked() )
2161 return 0;
2162
2163 Activate();
2164 // Must be done after Activate() so that it gets set into the correct context
2165 getViewControls()->ShowCursor( true );
2166
2168
2169 // Use the original object as a construction item
2170 std::vector<std::unique_ptr<BOARD_ITEM>> clones;
2171
2172 m_editorBehavior.reset();
2173 // Will also make the edit behavior if supported
2174 m_editPoints = makePoints( item );
2175
2176 if( !m_editPoints )
2177 return 0;
2178
2179 PCB_SHAPE* graphicItem = dynamic_cast<PCB_SHAPE*>( item );
2180
2181 // Only add the angle_item if we are editing a polygon or zone
2182 if( item->Type() == PCB_ZONE_T || ( graphicItem && graphicItem->GetShape() == SHAPE_T::POLY ) )
2183 {
2184 m_angleItem = std::make_unique<KIGFX::PREVIEW::ANGLE_ITEM>( m_editPoints.get() );
2185 }
2186
2187 m_preview.FreeItems();
2188 getView()->Add( &m_preview );
2189
2190 RECT_RADIUS_TEXT_ITEM* radiusHelper = new RECT_RADIUS_TEXT_ITEM( pcbIUScale, editFrame->GetUserUnits() );
2191 m_preview.Add( radiusHelper );
2192
2193 getView()->Add( m_editPoints.get() );
2194
2195 if( m_angleItem )
2196 getView()->Add( m_angleItem.get() );
2197
2198 setEditedPoint( nullptr );
2199 updateEditedPoint( aEvent );
2200 bool inDrag = false;
2201 bool isConstrained = false;
2202 bool haveSnapLineDirections = false;
2203
2204 auto updateSnapLineDirections =
2205 [&]()
2206 {
2207 std::vector<VECTOR2I> directions;
2208
2209 if( inDrag && m_editedPoint )
2210 {
2211 EDIT_CONSTRAINT<EDIT_POINT>* constraint = nullptr;
2212
2213 if( m_altConstraint )
2214 constraint = m_altConstraint.get();
2215 else if( m_editedPoint->IsConstrained() )
2216 constraint = m_editedPoint->GetConstraint();
2217
2218 directions = getConstraintDirections( constraint );
2219 }
2220
2221 if( directions.empty() )
2222 {
2223 grid.SetSnapLineDirections( {} );
2224 grid.SetSnapLineEnd( std::nullopt );
2225 haveSnapLineDirections = false;
2226 }
2227 else
2228 {
2229 grid.SetSnapLineDirections( directions );
2230 grid.SetSnapLineOrigin( m_original.GetPosition() );
2231 grid.SetSnapLineEnd( std::nullopt );
2232 haveSnapLineDirections = true;
2233 }
2234 };
2235
2236 BOARD_COMMIT commit( editFrame );
2237
2238 // Main loop: keep receiving events
2239 while( TOOL_EVENT* evt = Wait() )
2240 {
2241 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2242 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2243
2244 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
2246 else
2248
2249 if( !m_editPoints || evt->IsSelectionEvent() || evt->Matches( EVENTS::InhibitSelectionEditing ) )
2250 {
2251 break;
2252 }
2253
2254 EDIT_POINT* prevHover = m_hoveredPoint;
2255
2256 if( !inDrag )
2257 updateEditedPoint( *evt );
2258
2259 if( prevHover != m_hoveredPoint )
2260 {
2261 getView()->Update( m_editPoints.get() );
2262
2263 if( m_angleItem )
2264 getView()->Update( m_angleItem.get() );
2265 }
2266
2267 if( evt->IsDrag( BUT_LEFT ) && m_editedPoint )
2268 {
2269 if( !inDrag )
2270 {
2271 frame()->UndoRedoBlock( true );
2272
2273 if( item->Type() == PCB_GENERATOR_T )
2274 {
2275 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genStartEdit, &commit,
2276 static_cast<PCB_GENERATOR*>( item ) );
2277 }
2278
2280 m_original = *m_editedPoint; // Save the original position
2281 getViewControls()->SetAutoPan( true );
2282 inDrag = true;
2283
2284 if( m_editedPoint->GetGridConstraint() != SNAP_BY_GRID )
2285 grid.SetAuxAxes( true, m_original.GetPosition() );
2286
2287 m_editedPoint->SetActive();
2288
2289 for( size_t ii = 0; ii < m_editPoints->PointsSize(); ++ii )
2290 {
2291 EDIT_POINT& point = m_editPoints->Point( ii );
2292
2293 if( &point != m_editedPoint )
2294 point.SetActive( false );
2295 }
2296
2297 // When we start dragging, create a clone of the item to use as the original
2298 // reference geometry (e.g. for intersections and extensions)
2299 BOARD_ITEM* clone = static_cast<BOARD_ITEM*>( item->Clone() );
2300 clone->SetParent( nullptr );
2301
2302 if( PCB_SHAPE* shape= dynamic_cast<PCB_SHAPE*>( item ) )
2303 {
2304 shape->SetFlags( IS_MOVING );
2305 shape->UpdateHatching();
2306
2307 static_cast<PCB_SHAPE*>( clone )->SetFillMode( FILL_T::NO_FILL );
2308 }
2309
2310 clones.emplace_back( clone );
2311 grid.AddConstructionItems( { clone }, false, true );
2312
2313 updateSnapLineDirections();
2314 }
2315
2316 bool need_constraint = Is45Limited() || Is90Limited();
2317
2318 if( isConstrained != need_constraint )
2319 {
2320 setAltConstraint( need_constraint );
2321 isConstrained = need_constraint;
2322 updateSnapLineDirections();
2323 }
2324
2325 // Keep point inside of limits with some padding
2326 VECTOR2I pos = GetClampedCoords<double, int>( evt->Position(), COORDS_PADDING );
2327 LSET snapLayers;
2328
2329 switch( m_editedPoint->GetSnapConstraint() )
2330 {
2331 case IGNORE_SNAPS: break;
2332 case OBJECT_LAYERS: snapLayers = item->GetLayerSet(); break;
2333 case ALL_LAYERS: snapLayers = LSET::AllLayersMask(); break;
2334 }
2335
2336 if( m_editedPoint->GetGridConstraint() == SNAP_BY_GRID )
2337 {
2338 if( grid.GetUseGrid() )
2339 {
2340 VECTOR2I gridPt = grid.BestSnapAnchor( pos, {}, grid.GetItemGrid( item ), { item } );
2341
2342 VECTOR2I last = m_editedPoint->GetPosition();
2343 VECTOR2I delta = pos - last;
2344 VECTOR2I deltaGrid = gridPt - grid.BestSnapAnchor( last, {}, grid.GetItemGrid( item ),
2345 { item } );
2346
2347 if( abs( delta.x ) > grid.GetGrid().x / 2 )
2348 pos.x = last.x + deltaGrid.x;
2349 else
2350 pos.x = last.x;
2351
2352 if( abs( delta.y ) > grid.GetGrid().y / 2 )
2353 pos.y = last.y + deltaGrid.y;
2354 else
2355 pos.y = last.y;
2356 }
2357 }
2358
2359 if( m_angleSnapActive )
2360 {
2361 m_stickyDisplacement = evt->Position() - m_angleSnapPos;
2362 int stickyLimit = KiROUND( getView()->ToWorld( 5 ) );
2363
2364 if( m_stickyDisplacement.EuclideanNorm() > stickyLimit || evt->Modifier( MD_SHIFT ) )
2365 {
2366 m_angleSnapActive = false;
2367 }
2368 else
2369 {
2370 pos = m_angleSnapPos;
2371 }
2372 }
2373
2374 if( !m_angleSnapActive && m_editPoints->PointsSize() > 2 && !evt->Modifier( MD_SHIFT ) )
2375 {
2376 int idx = getEditedPointIndex();
2377
2378 if( idx != wxNOT_FOUND )
2379 {
2380 int prevIdx = ( idx + m_editPoints->PointsSize() - 1 ) % m_editPoints->PointsSize();
2381 int nextIdx = ( idx + 1 ) % m_editPoints->PointsSize();
2382 VECTOR2I prev = m_editPoints->Point( prevIdx ).GetPosition();
2383 VECTOR2I next = m_editPoints->Point( nextIdx ).GetPosition();
2384 SEG segA( pos, prev );
2385 SEG segB( pos, next );
2386 double ang = segA.Angle( segB ).AsDegrees();
2387 double snapAng = 45.0 * std::round( ang / 45.0 );
2388
2389 if( std::abs( ang - snapAng ) < 2.0 )
2390 {
2391 m_angleSnapPos = snapCorner( prev, next, pos, snapAng );
2392 m_angleSnapActive = true;
2393 m_stickyDisplacement = evt->Position() - m_angleSnapPos;
2394 pos = m_angleSnapPos;
2395 }
2396 }
2397 }
2398
2399 bool constraintSnapped = false;
2400
2401 // Apply 45 degree or other constraints
2403 {
2404 m_editedPoint->SetPosition( pos );
2405 m_altConstraint->Apply( grid );
2406 constraintSnapped = true;
2407 }
2408 else if( !m_angleSnapActive && m_editedPoint->IsConstrained() )
2409 {
2410 m_editedPoint->SetPosition( pos );
2411 m_editedPoint->ApplyConstraint( grid );
2412 constraintSnapped = true;
2413 }
2414 else if( !m_angleSnapActive && m_editedPoint->GetGridConstraint() == SNAP_TO_GRID )
2415 {
2416 m_editedPoint->SetPosition( grid.BestSnapAnchor( pos, snapLayers, grid.GetItemGrid( item ),
2417 { item } ) );
2418 }
2419 else
2420 {
2421 m_editedPoint->SetPosition( pos );
2422 }
2423
2424 if( haveSnapLineDirections )
2425 {
2426 if( constraintSnapped )
2427 grid.SetSnapLineEnd( m_editedPoint->GetPosition() );
2428 else
2429 grid.SetSnapLineEnd( std::nullopt );
2430 }
2431
2432 updateItem( commit );
2433 getViewControls()->ForceCursorPosition( true, m_editedPoint->GetPosition() );
2434 updatePoints();
2435
2436 if( m_editPoints->PointsSize() > RECT_RADIUS
2437 && m_editedPoint == &m_editPoints->Point( RECT_RADIUS ) )
2438 {
2439 if( PCB_SHAPE* rect = dynamic_cast<PCB_SHAPE*>( item ) )
2440 {
2441 int radius = rect->GetCornerRadius();
2442 int offset = radius - M_SQRT1_2 * radius;
2443 VECTOR2I topLeft = rect->GetTopLeft();
2444 VECTOR2I botRight = rect->GetBotRight();
2445 VECTOR2I topRight( botRight.x, topLeft.y );
2446 VECTOR2I center( topRight.x - offset, topRight.y + offset );
2447 radiusHelper->Set( radius, center, VECTOR2I( 1, -1 ), editFrame->GetUserUnits() );
2448 }
2449 }
2450 else
2451 {
2452 radiusHelper->Hide();
2453 }
2454
2455 getView()->Update( &m_preview );
2456 }
2457 else if( m_editedPoint && evt->Action() == TA_MOUSE_DOWN && evt->Buttons() == BUT_LEFT )
2458 {
2459 m_editedPoint->SetActive();
2460
2461 for( size_t ii = 0; ii < m_editPoints->PointsSize(); ++ii )
2462 {
2463 EDIT_POINT& point = m_editPoints->Point( ii );
2464
2465 if( &point != m_editedPoint )
2466 point.SetActive( false );
2467 }
2468
2469 getView()->Update( m_editPoints.get() );
2470
2471 if( m_angleItem )
2472 getView()->Update( m_angleItem.get() );
2473 }
2474 else if( inDrag && evt->IsMouseUp( BUT_LEFT ) )
2475 {
2476 if( m_editedPoint )
2477 {
2478 m_editedPoint->SetActive( false );
2479 getView()->Update( m_editPoints.get() );
2480
2481 if( m_angleItem )
2482 getView()->Update( m_angleItem.get() );
2483 }
2484
2485 radiusHelper->Hide();
2486 getView()->Update( &m_preview );
2487
2488 getViewControls()->SetAutoPan( false );
2489 setAltConstraint( false );
2490 updateSnapLineDirections();
2491
2492 if( m_editorBehavior )
2493 m_editorBehavior->FinalizeItem( *m_editPoints, commit );
2494
2495 if( item->Type() == PCB_GENERATOR_T )
2496 {
2497 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( item );
2498
2499 m_preview.FreeItems();
2500 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genFinishEdit, &commit, generator );
2501
2502 commit.Push( generator->GetCommitMessage() );
2503 }
2504 else if( item->Type() == PCB_TABLECELL_T )
2505 {
2506 commit.Push( _( "Resize Table Cells" ) );
2507 }
2508 else
2509 {
2510 commit.Push( _( "Move Point" ) );
2511 }
2512
2513 if( PCB_SHAPE* shape= dynamic_cast<PCB_SHAPE*>( item ) )
2514 {
2515 shape->ClearFlags( IS_MOVING );
2516 shape->UpdateHatching();
2517 }
2518
2519 inDrag = false;
2520 frame()->UndoRedoBlock( false );
2521 updateSnapLineDirections();
2522
2523 m_toolMgr->PostAction<EDA_ITEM*>( ACTIONS::reselectItem, item ); // FIXME: Needed for generators
2524 }
2525 else if( evt->IsCancelInteractive() || evt->IsActivate() )
2526 {
2527 if( inDrag ) // Restore the last change
2528 {
2529 if( item->Type() == PCB_GENERATOR_T )
2530 {
2531 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genCancelEdit, &commit,
2532 static_cast<PCB_GENERATOR*>( item ) );
2533 }
2534
2535 commit.Revert();
2536
2537 if( PCB_SHAPE* shape= dynamic_cast<PCB_SHAPE*>( item ) )
2538 {
2539 shape->ClearFlags( IS_MOVING );
2540 shape->UpdateHatching();
2541 }
2542
2543 inDrag = false;
2544 frame()->UndoRedoBlock( false );
2545 updateSnapLineDirections();
2546 }
2547
2548 // Only cancel point editor when activating a new tool
2549 // Otherwise, allow the points to persist when moving up the
2550 // tool stack
2551 if( evt->IsActivate() && !evt->IsMoveTool() )
2552 break;
2553 }
2554 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2555 {
2556 // Re-create the points for items which can have different behavior on different layers
2557 if( item->Type() == PCB_PAD_T && m_isFootprintEditor )
2558 {
2559 getView()->Remove( m_editPoints.get() );
2560
2561 if( m_angleItem )
2562 getView()->Remove( m_angleItem.get() );
2563
2564 m_editPoints = makePoints( item );
2565
2566 if( m_angleItem )
2567 {
2568 m_angleItem->SetEditPoints( m_editPoints.get() );
2569 getView()->Add( m_angleItem.get() );
2570 }
2571
2572 getView()->Add( m_editPoints.get() );
2573 }
2574 }
2575 else if( evt->Action() == TA_UNDO_REDO_POST )
2576 {
2577 break;
2578 }
2579 else
2580 {
2581 evt->SetPassEvent();
2582 }
2583 }
2584
2585 if( PCB_SHAPE* shape= dynamic_cast<PCB_SHAPE*>( item ) )
2586 {
2587 shape->ClearFlags( IS_MOVING );
2588 shape->UpdateHatching();
2589 }
2590
2591 m_preview.FreeItems();
2592 getView()->Remove( &m_preview );
2593
2594 if( m_editPoints )
2595 {
2596 getView()->Remove( m_editPoints.get() );
2597
2598 if( m_angleItem )
2599 getView()->Remove( m_angleItem.get() );
2600
2601 m_editPoints.reset();
2602 m_angleItem.reset();
2603 }
2604
2605 m_editedPoint = nullptr;
2606 grid.SetSnapLineDirections( {} );
2607
2608 return 0;
2609}
2610
2611
2613{
2614 if( !m_editPoints || !m_editPoints->GetParent() || !HasPoint() )
2615 return 0;
2616
2618
2619 BOARD_COMMIT commit( editFrame );
2620 commit.Stage( m_editPoints->GetParent(), CHT_MODIFY );
2621
2622 VECTOR2I pt = m_editedPoint->GetPosition();
2623 wxString title;
2624 wxString msg;
2625
2626 if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
2627 {
2628 title = _( "Move Midpoint to Location" );
2629 msg = _( "Move Midpoint" );
2630 }
2631 else
2632 {
2633 title = _( "Move Corner to Location" );
2634 msg = _( "Move Corner" );
2635 }
2636
2637 WX_PT_ENTRY_DIALOG dlg( editFrame, title, _( "X:" ), _( "Y:" ), pt, false );
2638
2639 if( dlg.ShowModal() == wxID_OK )
2640 {
2641 m_editedPoint->SetPosition( dlg.GetValue() );
2642 updateItem( commit );
2643 commit.Push( msg );
2644 }
2645
2646 return 0;
2647}
2648
2649
2651{
2652 wxCHECK( m_editPoints, /* void */ );
2653 EDA_ITEM* item = m_editPoints->GetParent();
2654
2655 if( !item )
2656 return;
2657
2658 // item is always updated
2659 std::vector<EDA_ITEM*> updatedItems = { item };
2660 aCommit.Modify( item );
2661
2662 if( m_editorBehavior )
2663 {
2664 wxCHECK( m_editedPoint, /* void */ );
2665 m_editorBehavior->UpdateItem( *m_editedPoint, *m_editPoints, aCommit, updatedItems );
2666 }
2667
2668 // Perform any post-edit actions that the item may require
2669
2670 switch( item->Type() )
2671 {
2672 case PCB_TEXTBOX_T:
2673 case PCB_SHAPE_T:
2674 {
2675 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
2676
2677 if( shape->IsProxyItem() )
2678 {
2679 for( PAD* pad : shape->GetParentFootprint()->Pads() )
2680 {
2681 if( pad->IsEntered() )
2682 view()->Update( pad );
2683 }
2684 }
2685
2686 // Nuke outline font render caches
2687 if( PCB_TEXTBOX* textBox = dynamic_cast<PCB_TEXTBOX*>( item ) )
2688 textBox->ClearRenderCache();
2689
2690 break;
2691 }
2692 case PCB_GENERATOR_T:
2693 {
2694 GENERATOR_TOOL* generatorTool = m_toolMgr->GetTool<GENERATOR_TOOL>();
2695 PCB_GENERATOR* generatorItem = static_cast<PCB_GENERATOR*>( item );
2696
2697 m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genUpdateEdit, &aCommit, generatorItem );
2698
2699 // Note: POINT_EDITOR::m_preview holds only the canvas-draw status "popup"; the meanders
2700 // themselves (ROUTER_PREVIEW_ITEMs) are owned by the router.
2701
2702 m_preview.FreeItems();
2703
2704 for( EDA_ITEM* previewItem : generatorItem->GetPreviewItems( generatorTool, frame(), STATUS_ITEMS_ONLY ) )
2705 m_preview.Add( previewItem );
2706
2707 getView()->Update( &m_preview );
2708 break;
2709 }
2710 default:
2711 break;
2712 }
2713
2714 // Update the item and any affected items
2715 for( EDA_ITEM* updatedItem : updatedItems )
2716 getView()->Update( updatedItem );
2717
2718 frame()->SetMsgPanel( item );
2719}
2720
2721
2723{
2724 if( !m_editPoints )
2725 return;
2726
2727 EDA_ITEM* item = m_editPoints->GetParent();
2728
2729 if( !item )
2730 return;
2731
2732 if( !m_editorBehavior )
2733 return;
2734
2735 int editedIndex = -1;
2736 bool editingLine = false;
2737
2738 if( m_editedPoint )
2739 {
2740 // Check if we're editing a point (vertex)
2741 for( unsigned ii = 0; ii < m_editPoints->PointsSize(); ++ii )
2742 {
2743 if( &m_editPoints->Point( ii ) == m_editedPoint )
2744 {
2745 editedIndex = ii;
2746 break;
2747 }
2748 }
2749
2750 // If not found in points, check if we're editing a line (midpoint)
2751 if( editedIndex == -1 )
2752 {
2753 for( unsigned ii = 0; ii < m_editPoints->LinesSize(); ++ii )
2754 {
2755 if( &m_editPoints->Line( ii ) == m_editedPoint )
2756 {
2757 editedIndex = ii;
2758 editingLine = true;
2759 break;
2760 }
2761 }
2762 }
2763 }
2764
2765 if( !m_editorBehavior->UpdatePoints( *m_editPoints ) )
2766 {
2767 getView()->Remove( m_editPoints.get() );
2768 m_editPoints = makePoints( item );
2769 getView()->Add( m_editPoints.get() );
2770 }
2771
2772 if( editedIndex >= 0 )
2773 {
2774 if( editingLine && editedIndex < (int) m_editPoints->LinesSize() )
2775 m_editedPoint = &m_editPoints->Line( editedIndex );
2776 else if( !editingLine && editedIndex < (int) m_editPoints->PointsSize() )
2777 m_editedPoint = &m_editPoints->Point( editedIndex );
2778 else
2779 m_editedPoint = nullptr;
2780 }
2781 else
2782 {
2783 m_editedPoint = nullptr;
2784 }
2785
2786 getView()->Update( m_editPoints.get() );
2787
2788 if( m_angleItem )
2789 getView()->Update( m_angleItem.get() );
2790}
2791
2792
2794{
2796
2797 if( aPoint )
2798 {
2799 frame()->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2800 controls->ForceCursorPosition( true, aPoint->GetPosition() );
2801 controls->ShowCursor( true );
2802 }
2803 else
2804 {
2805 if( frame()->ToolStackIsEmpty() )
2806 controls->ShowCursor( false );
2807
2808 controls->ForceCursorPosition( false );
2809 }
2810
2811 m_editedPoint = aPoint;
2812}
2813
2814
2816{
2817 if( aEnabled )
2818 {
2819 EDA_ITEM* parent = m_editPoints->GetParent();
2820 EDIT_LINE* line = dynamic_cast<EDIT_LINE*>( m_editedPoint );
2821 bool isPoly;
2822
2823 switch( parent->Type() )
2824 {
2825 case PCB_ZONE_T:
2826 isPoly = true;
2827 break;
2828
2829 case PCB_SHAPE_T:
2830 isPoly = static_cast<PCB_SHAPE*>( parent )->GetShape() == SHAPE_T::POLY;
2831 break;
2832
2833 default:
2834 isPoly = false;
2835 break;
2836 }
2837
2838 if( line && isPoly )
2839 {
2840 EC_CONVERGING* altConstraint = new EC_CONVERGING( *line, *m_editPoints );
2841 m_altConstraint.reset( (EDIT_CONSTRAINT<EDIT_POINT>*) altConstraint );
2842 }
2843 else
2844 {
2845 // Find a proper constraining point for angle snapping mode
2847
2848 if( Is90Limited() )
2850 else
2852 }
2853 }
2854 else
2855 {
2856 m_altConstraint.reset();
2857 }
2858}
2859
2860
2862{
2863 // If there's a behaviour and it provides a constrainer, use that
2864 if( m_editorBehavior )
2865 {
2866 const OPT_VECTOR2I constrainer = m_editorBehavior->Get45DegreeConstrainer( *m_editedPoint, *m_editPoints );
2867
2868 if( constrainer )
2869 return EDIT_POINT( *constrainer );
2870 }
2871
2872 // In any other case we may align item to its original position
2873 return m_original;
2874}
2875
2876
2877// Finds a corresponding vertex in a polygon set
2878static std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX> findVertex( SHAPE_POLY_SET& aPolySet, const EDIT_POINT& aPoint )
2879{
2880 for( auto it = aPolySet.IterateWithHoles(); it; ++it )
2881 {
2882 auto vertexIdx = it.GetIndex();
2883
2884 if( aPolySet.CVertex( vertexIdx ) == aPoint.GetPosition() )
2885 return std::make_pair( true, vertexIdx );
2886 }
2887
2888 return std::make_pair( false, SHAPE_POLY_SET::VERTEX_INDEX() );
2889}
2890
2891
2893{
2894 if( !m_editPoints || !m_editedPoint )
2895 return false;
2896
2897 EDA_ITEM* item = m_editPoints->GetParent();
2898 SHAPE_POLY_SET* polyset = nullptr;
2899
2900 if( !item )
2901 return false;
2902
2903 switch( item->Type() )
2904 {
2905 case PCB_ZONE_T:
2906 polyset = static_cast<ZONE*>( item )->Outline();
2907 break;
2908
2909 case PCB_SHAPE_T:
2910 if( static_cast<PCB_SHAPE*>( item )->GetShape() == SHAPE_T::POLY )
2911 polyset = &static_cast<PCB_SHAPE*>( item )->GetPolyShape();
2912 else
2913 return false;
2914
2915 break;
2916
2917 default:
2918 return false;
2919 }
2920
2921 std::pair<bool, SHAPE_POLY_SET::VERTEX_INDEX> vertex = findVertex( *polyset, *m_editedPoint );
2922
2923 if( !vertex.first )
2924 return false;
2925
2926 const SHAPE_POLY_SET::VERTEX_INDEX& vertexIdx = vertex.second;
2927
2928 // Check if there are enough vertices so one can be removed without
2929 // degenerating the polygon.
2930 // The first condition allows one to remove all corners from holes (when
2931 // there are only 2 vertices left, a hole is removed).
2932 if( vertexIdx.m_contour == 0
2933 && polyset->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour].PointCount() <= 3 )
2934 {
2935 return false;
2936 }
2937
2938 // Remove corner does not work with lines
2939 if( dynamic_cast<EDIT_LINE*>( m_editedPoint ) )
2940 return false;
2941
2942 return m_editedPoint != nullptr;
2943}
2944
2945
2947{
2948 if( !m_editPoints )
2949 return 0;
2950
2951 EDA_ITEM* item = m_editPoints->GetParent();
2953 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
2954
2955 // called without an active edited polygon
2956 if( !item || !canAddCorner( *item ) )
2957 return 0;
2958
2959 PCB_SHAPE* graphicItem = dynamic_cast<PCB_SHAPE*>( item );
2960 BOARD_COMMIT commit( frame );
2961
2962 if( item->Type() == PCB_ZONE_T || ( graphicItem && graphicItem->GetShape() == SHAPE_T::POLY ) )
2963 {
2964 unsigned int nearestIdx = 0;
2965 unsigned int nextNearestIdx = 0;
2966 unsigned int nearestDist = INT_MAX;
2967 unsigned int firstPointInContour = 0;
2968 SHAPE_POLY_SET* zoneOutline;
2969
2970 if( item->Type() == PCB_ZONE_T )
2971 {
2972 ZONE* zone = static_cast<ZONE*>( item );
2973 zoneOutline = zone->Outline();
2974 zone->SetNeedRefill( true );
2975 }
2976 else
2977 {
2978 zoneOutline = &( graphicItem->GetPolyShape() );
2979 }
2980
2981 commit.Modify( item );
2982
2983 // Search the best outline segment to add a new corner
2984 // and therefore break this segment into two segments
2985
2986 // Object to iterate through the corners of the outlines (main contour and its holes)
2987 SHAPE_POLY_SET::ITERATOR iterator = zoneOutline->Iterate( 0, zoneOutline->OutlineCount()-1,
2988 /* IterateHoles */ true );
2989 int curr_idx = 0;
2990
2991 // Iterate through all the corners of the outlines and search the best segment
2992 for( ; iterator; iterator++, curr_idx++ )
2993 {
2994 int jj = curr_idx+1;
2995
2996 if( iterator.IsEndContour() )
2997 { // We reach the last point of the current contour (main or hole)
2998 jj = firstPointInContour;
2999 firstPointInContour = curr_idx+1; // Prepare next contour analysis
3000 }
3001
3002 SEG curr_segment( zoneOutline->CVertex( curr_idx ), zoneOutline->CVertex( jj ) );
3003
3004 unsigned int distance = curr_segment.Distance( cursorPos );
3005
3006 if( distance < nearestDist )
3007 {
3008 nearestDist = distance;
3009 nearestIdx = curr_idx;
3010 nextNearestIdx = jj;
3011 }
3012 }
3013
3014 // Find the point on the closest segment
3015 const VECTOR2I& sideOrigin = zoneOutline->CVertex( nearestIdx );
3016 const VECTOR2I& sideEnd = zoneOutline->CVertex( nextNearestIdx );
3017 SEG nearestSide( sideOrigin, sideEnd );
3018 VECTOR2I nearestPoint = nearestSide.NearestPoint( cursorPos );
3019
3020 // Do not add points that have the same coordinates as ones that already belong to polygon
3021 // instead, add a point in the middle of the side
3022 if( nearestPoint == sideOrigin || nearestPoint == sideEnd )
3023 nearestPoint = ( sideOrigin + sideEnd ) / 2;
3024
3025 zoneOutline->InsertVertex( nextNearestIdx, nearestPoint );
3026
3027 if( item->Type() == PCB_ZONE_T )
3028 static_cast<ZONE*>( item )->HatchBorder();
3029
3030 commit.Push( _( "Add Zone Corner" ) );
3031 }
3032 else if( graphicItem )
3033 {
3034 switch( graphicItem->GetShape() )
3035 {
3036 case SHAPE_T::SEGMENT:
3037 {
3038 commit.Modify( graphicItem );
3039
3040 SEG seg( graphicItem->GetStart(), graphicItem->GetEnd() );
3041 VECTOR2I nearestPoint = seg.NearestPoint( cursorPos );
3042
3043 // Move the end of the line to the break point..
3044 graphicItem->SetEnd( nearestPoint );
3045
3046 // and add another one starting from the break point
3047 PCB_SHAPE* newSegment = static_cast<PCB_SHAPE*>( graphicItem->Duplicate( true, &commit ) );
3048 newSegment->ClearSelected();
3049 newSegment->SetStart( nearestPoint );
3050 newSegment->SetEnd( VECTOR2I( seg.B.x, seg.B.y ) );
3051
3052 commit.Add( newSegment );
3053 commit.Push( _( "Split Segment" ) );
3054 break;
3055 }
3056 case SHAPE_T::ARC:
3057 {
3058 commit.Modify( graphicItem );
3059
3060 const SHAPE_ARC arc( graphicItem->GetStart(), graphicItem->GetArcMid(), graphicItem->GetEnd(), 0 );
3061 const VECTOR2I nearestPoint = arc.NearestPoint( cursorPos );
3062
3063 // Move the end of the arc to the break point..
3064 graphicItem->SetEnd( nearestPoint );
3065
3066 // and add another one starting from the break point
3067 PCB_SHAPE* newArc = static_cast<PCB_SHAPE*>( graphicItem->Duplicate( true, &commit ) );
3068
3069 newArc->ClearSelected();
3070 newArc->SetEnd( arc.GetP1() );
3071 newArc->SetStart( nearestPoint );
3072
3073 commit.Add( newArc );
3074 commit.Push( _( "Split Arc" ) );
3075 break;
3076 }
3077 default:
3078 // No split implemented for other shapes
3079 break;
3080 }
3081 }
3082
3083 updatePoints();
3084 return 0;
3085}
3086
3087
3089{
3090 if( !m_editPoints || !m_editedPoint )
3091 return 0;
3092
3093 EDA_ITEM* item = m_editPoints->GetParent();
3094
3095 if( !item )
3096 return 0;
3097
3098 SHAPE_POLY_SET* polygon = nullptr;
3099
3100 if( item->Type() == PCB_ZONE_T )
3101 {
3102 ZONE* zone = static_cast<ZONE*>( item );
3103 polygon = zone->Outline();
3104 zone->SetNeedRefill( true );
3105 }
3106 else if( item->Type() == PCB_SHAPE_T )
3107 {
3108 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
3109
3110 if( shape->GetShape() == SHAPE_T::POLY )
3111 polygon = &shape->GetPolyShape();
3112 }
3113
3114 if( !polygon )
3115 return 0;
3116
3118 BOARD_COMMIT commit( frame );
3119 auto vertex = findVertex( *polygon, *m_editedPoint );
3120
3121 if( vertex.first )
3122 {
3123 const auto& vertexIdx = vertex.second;
3124 auto& outline = polygon->Polygon( vertexIdx.m_polygon )[vertexIdx.m_contour];
3125
3126 if( outline.PointCount() > 3 )
3127 {
3128 // the usual case: remove just the corner when there are >3 vertices
3129 commit.Modify( item );
3130 polygon->RemoveVertex( vertexIdx );
3131 }
3132 else
3133 {
3134 // either remove a hole or the polygon when there are <= 3 corners
3135 if( vertexIdx.m_contour > 0 )
3136 {
3137 // remove hole
3138 commit.Modify( item );
3139 polygon->RemoveContour( vertexIdx.m_contour );
3140 }
3141 else
3142 {
3143 m_toolMgr->RunAction( ACTIONS::selectionClear );
3144 commit.Remove( item );
3145 }
3146 }
3147
3148 setEditedPoint( nullptr );
3149
3150 if( item->Type() == PCB_ZONE_T )
3151 commit.Push( _( "Remove Zone Corner" ) );
3152 else
3153 commit.Push( _( "Remove Polygon Corner" ) );
3154
3155 if( item->Type() == PCB_ZONE_T )
3156 static_cast<ZONE*>( item )->HatchBorder();
3157
3158 updatePoints();
3159 }
3160
3161 return 0;
3162}
3163
3164
3166{
3167 if( !m_editPoints || !m_editedPoint )
3168 return 0;
3169
3170 EDA_ITEM* item = m_editPoints->GetParent();
3171
3172 if( !item )
3173 return 0;
3174
3175 SHAPE_POLY_SET* polygon = nullptr;
3176
3177 if( item->Type() == PCB_ZONE_T )
3178 {
3179 ZONE* zone = static_cast<ZONE*>( item );
3180 polygon = zone->Outline();
3181 zone->SetNeedRefill( true );
3182 }
3183 else if( item->Type() == PCB_SHAPE_T )
3184 {
3185 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
3186
3187 if( shape->GetShape() == SHAPE_T::POLY )
3188 polygon = &shape->GetPolyShape();
3189 }
3190
3191 if( !polygon )
3192 return 0;
3193
3194 // Search the best outline corner to break
3195
3197 BOARD_COMMIT commit( frame );
3198 const VECTOR2I& cursorPos = getViewControls()->GetCursorPosition();
3199
3200 unsigned int nearestIdx = 0;
3201 unsigned int nearestDist = INT_MAX;
3202
3203 int curr_idx = 0;
3204 // Object to iterate through the corners of the outlines (main contour and its holes)
3205 SHAPE_POLY_SET::ITERATOR iterator = polygon->Iterate( 0, polygon->OutlineCount() - 1,
3206 /* IterateHoles */ true );
3207
3208 // Iterate through all the corners of the outlines and search the best segment
3209 for( ; iterator; iterator++, curr_idx++ )
3210 {
3211 unsigned int distance = polygon->CVertex( curr_idx ).Distance( cursorPos );
3212
3213 if( distance < nearestDist )
3214 {
3215 nearestDist = distance;
3216 nearestIdx = curr_idx;
3217 }
3218 }
3219
3220 int prevIdx, nextIdx;
3221 if( polygon->GetNeighbourIndexes( nearestIdx, &prevIdx, &nextIdx ) )
3222 {
3223 const SEG segA{ polygon->CVertex( prevIdx ), polygon->CVertex( nearestIdx ) };
3224 const SEG segB{ polygon->CVertex( nextIdx ), polygon->CVertex( nearestIdx ) };
3225
3226 // A plausible setback that won't consume a whole edge
3227 int setback = pcbIUScale.mmToIU( 5 );
3228 setback = std::min( setback, (int) ( segA.Length() * 0.25 ) );
3229 setback = std::min( setback, (int) ( segB.Length() * 0.25 ) );
3230
3231 CHAMFER_PARAMS chamferParams{ setback, setback };
3232
3233 std::optional<CHAMFER_RESULT> chamferResult = ComputeChamferPoints( segA, segB, chamferParams );
3234
3235 if( chamferResult && chamferResult->m_updated_seg_a && chamferResult->m_updated_seg_b )
3236 {
3237 commit.Modify( item );
3238 polygon->RemoveVertex( nearestIdx );
3239
3240 // The two end points of the chamfer are the new corners
3241 polygon->InsertVertex( nearestIdx, chamferResult->m_updated_seg_b->B );
3242 polygon->InsertVertex( nearestIdx, chamferResult->m_updated_seg_a->B );
3243 }
3244 }
3245
3246 setEditedPoint( nullptr );
3247
3248 if( item->Type() == PCB_ZONE_T )
3249 commit.Push( _( "Break Zone Corner" ) );
3250 else
3251 commit.Push( _( "Break Polygon Corner" ) );
3252
3253 if( item->Type() == PCB_ZONE_T )
3254 static_cast<ZONE*>( item )->HatchBorder();
3255
3256 updatePoints();
3257
3258 return 0;
3259}
3260
3261
3263{
3264 updatePoints();
3265 return 0;
3266}
3267
3268
3270{
3272
3273 if( aEvent.Matches( ACTIONS::cycleArcEditMode.MakeEvent() ) )
3274 {
3275 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
3277 else
3279
3281 }
3282 else
3283 {
3285 }
3286
3287 if( editFrame->IsType( FRAME_PCB_EDITOR ) )
3289 else
3291
3292 return 0;
3293}
3294
3295
3297{
3315}
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:273
static TOOL_ACTION pointEditorArcKeepCenter
Definition actions.h:274
static TOOL_ACTION pointEditorArcKeepRadius
Definition actions.h:276
static TOOL_ACTION reselectItem
Definition actions.h:228
static TOOL_ACTION activatePointEditor
Definition actions.h:272
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:223
static TOOL_ACTION pointEditorArcKeepEndpoint
Definition actions.h:275
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:79
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:252
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 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 3 segments: dragged and two adjacent ones, enforcing to keep their slopes and all...
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:137
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:337
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
void SetWidth(int aWidth)
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_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:359
static const TOOL_EVENT SelectedEvent
Definition actions.h:346
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition actions.h:353
static const TOOL_EVENT UninhibitSelectionEditing
Used to inform tool that it should display the disambiguation menu.
Definition actions.h:360
static const TOOL_EVENT PointSelectedEvent
Definition actions.h:345
static const TOOL_EVENT SelectedItemsMoved
Used to inform tools that the selection should temporarily be non-editable.
Definition actions.h:356
static const TOOL_EVENT UnselectedEvent
Definition actions.h:347
std::deque< PAD * > & Pads()
Definition footprint.h:224
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:145
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:54
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
int changeArcEditMode(const TOOL_EVENT &aEvent)
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
bool removeCornerCondition(const SELECTION &aSelection)
EDIT_POINT * m_hoveredPoint
int addCorner(const TOOL_EVENT &aEvent)
bool HasPoint()
Indicate the cursor is over an edit point.
EDIT_POINT m_original
Original pos for the current drag point.
EDIT_POINT get45DegConstrainer() const
Condition to display "Remove corner" context menu entry.
int modifiedSelection(const TOOL_EVENT &aEvent)
Change the edit method for arcs.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
std::unique_ptr< POINT_EDIT_BEHAVIOR > m_editorBehavior
int removeCorner(const TOOL_EVENT &aEvent)
bool Init() override
Init() is called once upon a registration of the tool.
PCB_SELECTION m_preview
int movePoint(const TOOL_EVENT &aEvent)
TOOL_ACTION handlers.
void setEditedPoint(EDIT_POINT *aPoint)
EDIT_POINT m_altConstrainer
ARC_EDIT_MODE m_arcEditMode
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
int GetWidth() const override
bool IsProxyItem() const override
Definition pcb_shape.h:116
void Move(const VECTOR2I &aMoveVector) override
Move this object.
void Scale(double aScale)
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:604
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:673
EDA_ANGLE Angle(const SEG &aOther) const
Determine the smallest angle between two segments.
Definition seg.cpp:102
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:117
SHAPE_GROUP_POINT_EDIT_BEHAVIOR(PCB_GROUP &aGroup)
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
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:296
SHAPE_POLY_SET * Outline()
Definition zone.h:335
@ 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
@ 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:779
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:676
@ 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:166
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...
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)
static bool canAddCorner(const EDA_ITEM &aItem)
Condition to check if a point editor can add a corner to the given item.
TEXTBOX_POINT_COUNT
@ WHEN_POLYGON
@ WHEN_RECTANGLE
@ RECT_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)
static bool canChamferCorner(const EDA_ITEM &aItem)
Condition to check if a point editor can add a chamfer to a corner of the given item.
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...
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_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.