KiCad PCB EDA Suite
Loading...
Searching...
No Matches
edit_tool.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-2023 CERN
5 * Copyright (C) 2017-2024 KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
7 * @author Tomasz Wlostowski <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, you may find one here:
21 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
22 * or you may search the http://www.gnu.org website for the version 2 license,
23 * or you may write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 */
26
27#include <macros.h>
28#include <advanced_config.h>
29#include <limits>
30#include <kiplatform/ui.h>
32#include <board.h>
34#include <footprint.h>
35#include <pcb_shape.h>
36#include <pcb_group.h>
37#include <pcb_target.h>
38#include <pcb_textbox.h>
39#include <pcb_table.h>
40#include <pcb_generator.h>
41#include <pcb_edit_frame.h>
43#include <kiway.h>
44#include <array_creator.h>
45#include <status_popup.h>
47#include <tool/tool_manager.h>
48#include <tools/pcb_actions.h>
50#include <tools/edit_tool.h>
56#include <tools/pad_tool.h>
57#include <view/view_controls.h>
59#include <core/kicad_algo.h>
60#include <fix_board_shape.h>
61#include <bitmaps.h>
62#include <functional>
63using namespace std::placeholders;
64#include "kicad_clipboard.h"
65#include <wx/hyperlink.h>
66#include <router/router_tool.h>
69#include <dialogs/dialog_tablecell_properties.h>
70#include <dialogs/dialog_table_properties.h>
72#include <pcb_reference_image.h>
73
74const unsigned int EDIT_TOOL::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
75
76static const std::vector<KICAD_T> padTypes = { PCB_PAD_T };
77
78static const std::vector<KICAD_T> footprintTypes = { PCB_FOOTPRINT_T };
79
80static const std::vector<KICAD_T> groupTypes = { PCB_GROUP_T };
81
82static const std::vector<KICAD_T> trackTypes = { PCB_TRACE_T,
84 PCB_VIA_T };
85
86static const std::vector<KICAD_T> baseConnectedTypes = { PCB_PAD_T,
89 PCB_ARC_T };
90
91static const std::vector<KICAD_T> connectedTypes = { PCB_TRACE_T,
95 PCB_ZONE_T };
96
97static const std::vector<KICAD_T> unroutableTypes = { PCB_TRACE_T,
100 PCB_PAD_T,
102
103
105 PCB_TOOL_BASE( "pcbnew.InteractiveEdit" ),
106 m_selectionTool( nullptr ),
107 m_dragging( false )
108{
109}
110
111
113{
114 m_dragging = false;
115
116 m_statusPopup = std::make_unique<STATUS_TEXT_POPUP>( getEditFrame<PCB_BASE_EDIT_FRAME>() );
117}
118
119
120static std::shared_ptr<CONDITIONAL_MENU> makePositioningToolsMenu( TOOL_INTERACTIVE* aTool )
121{
122 auto menu = std::make_shared<CONDITIONAL_MENU>( aTool );
123
124 menu->SetIcon( BITMAPS::special_tools );
125 menu->SetTitle( _( "Positioning Tools" ) );
126
127 auto notMovingCondition = []( const SELECTION& aSelection )
128 {
129 return aSelection.Empty() || !aSelection.Front()->IsMoving();
130 };
131
132 // clang-format off
133 menu->AddItem( PCB_ACTIONS::moveExact, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
134 menu->AddItem( PCB_ACTIONS::moveWithReference, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
135 menu->AddItem( PCB_ACTIONS::copyWithReference, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
136 menu->AddItem( PCB_ACTIONS::positionRelative, SELECTION_CONDITIONS::NotEmpty && notMovingCondition );
137 // clang-format on
138 return menu;
139};
140
141
142static std::shared_ptr<CONDITIONAL_MENU> makeShapeModificationMenu( TOOL_INTERACTIVE* aTool )
143{
144 auto menu = std::make_shared<CONDITIONAL_MENU>( aTool );
145
146 menu->SetTitle( _( "Shape Modification" ) );
147
148 static const std::vector<KICAD_T> filletChamferTypes = { PCB_SHAPE_LOCATE_POLY_T,
151
152 static const std::vector<KICAD_T> healShapesTypes = { PCB_SHAPE_LOCATE_SEGMENT_T,
155
156 static const std::vector<KICAD_T> lineExtendTypes = { PCB_SHAPE_LOCATE_SEGMENT_T };
157
158 static const std::vector<KICAD_T> polygonBooleanTypes = { PCB_SHAPE_LOCATE_RECT_T,
160
161 static const std::vector<KICAD_T> polygonSimplifyTypes = { PCB_SHAPE_LOCATE_POLY_T,
162 PCB_ZONE_T };
163
164 auto hasCornerCondition =
165 [aTool]( const SELECTION& aSelection )
166 {
167 PCB_POINT_EDITOR *pt_tool = aTool->GetManager()->GetTool<PCB_POINT_EDITOR>();
168
169 return pt_tool && pt_tool->HasCorner();
170 };
171
172 auto hasMidpointCondition =
173 [aTool]( const SELECTION& aSelection )
174 {
175 PCB_POINT_EDITOR *pt_tool = aTool->GetManager()->GetTool<PCB_POINT_EDITOR>();
176
177 return pt_tool && pt_tool->HasMidpoint();
178 };
179
180 // clang-format off
181 menu->AddItem( PCB_ACTIONS::healShapes, SELECTION_CONDITIONS::HasTypes( healShapesTypes ) );
182 menu->AddItem( PCB_ACTIONS::simplifyPolygons, SELECTION_CONDITIONS::HasTypes( polygonSimplifyTypes ) );
183 menu->AddItem( PCB_ACTIONS::filletLines, SELECTION_CONDITIONS::OnlyTypes( filletChamferTypes ) );
184 menu->AddItem( PCB_ACTIONS::chamferLines, SELECTION_CONDITIONS::OnlyTypes( filletChamferTypes ) );
185 menu->AddItem( PCB_ACTIONS::extendLines, SELECTION_CONDITIONS::OnlyTypes( lineExtendTypes )
187 menu->AddItem( PCB_ACTIONS::pointEditorMoveCorner, hasCornerCondition );
188 menu->AddItem( PCB_ACTIONS::pointEditorMoveMidpoint, hasMidpointCondition );
189
190 menu->AddSeparator( SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
192
193 menu->AddItem( PCB_ACTIONS::mergePolygons, SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
195 menu->AddItem( PCB_ACTIONS::subtractPolygons, SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
197 menu->AddItem( PCB_ACTIONS::intersectPolygons, SELECTION_CONDITIONS::OnlyTypes( polygonBooleanTypes )
199 // clang-format on
200
201 return menu;
202};
203
205{
206 // Find the selection tool, so they can cooperate
208
209 std::shared_ptr<CONDITIONAL_MENU> positioningToolsSubMenu = makePositioningToolsMenu( this );
210 m_selectionTool->GetToolMenu().RegisterSubMenu( positioningToolsSubMenu );
211
212 std::shared_ptr<CONDITIONAL_MENU> shapeModificationSubMenu = makeShapeModificationMenu( this );
213 m_selectionTool->GetToolMenu().RegisterSubMenu( shapeModificationSubMenu );
214
215 auto positioningToolsCondition =
216 [&]( const SELECTION& aSel )
217 {
218 std::shared_ptr<CONDITIONAL_MENU> subMenu = makePositioningToolsMenu( this );
219 subMenu->Evaluate( aSel );
220 return subMenu->GetMenuItemCount() > 0;
221 };
222
223 auto shapeModificationCondition =
224 [&]( const SELECTION& aSel )
225 {
226 std::shared_ptr<CONDITIONAL_MENU> subMenu = makeShapeModificationMenu( this );
227 subMenu->Evaluate( aSel );
228 return subMenu->GetMenuItemCount() > 0;
229 };
230
231 auto propertiesCondition =
232 [&]( const SELECTION& aSel )
233 {
234 if( aSel.GetSize() == 0 )
235 {
237 {
240
241 if( ds && ds->HitTestDrawingSheetItems( getView(), cursor ) )
242 return true;
243 }
244
245 return false;
246 }
247
248 if( aSel.GetSize() == 1 )
249 return true;
250
251 for( EDA_ITEM* item : aSel )
252 {
253 if( !dynamic_cast<PCB_TRACK*>( item ) )
254 return false;
255 }
256
257 return true;
258 };
259
260 auto inFootprintEditor =
261 [ this ]( const SELECTION& aSelection )
262 {
263 return m_isFootprintEditor;
264 };
265
266 auto canMirror =
267 [ this ]( const SELECTION& aSelection )
268 {
270 && SELECTION_CONDITIONS::OnlyTypes( padTypes )( aSelection ) )
271 {
272 return false;
273 }
274
275 if( SELECTION_CONDITIONS::HasTypes( groupTypes )( aSelection ) )
276 return true;
277
279 };
280
281 auto singleFootprintCondition = SELECTION_CONDITIONS::OnlyTypes( footprintTypes )
283
284 auto multipleFootprintsCondition =
285 []( const SELECTION& aSelection )
286 {
287 bool foundFirst = false;
288
289 for( EDA_ITEM* item : aSelection )
290 {
291 if( item->Type() == PCB_FOOTPRINT_T )
292 {
293 if( foundFirst )
294 return true;
295 else
296 foundFirst = true;
297 }
298 }
299
300 return false;
301 };
302
303 auto noActiveToolCondition =
304 [ this ]( const SELECTION& aSelection )
305 {
306 return frame()->ToolStackIsEmpty();
307 };
308
309 auto notMovingCondition =
310 []( const SELECTION& aSelection )
311 {
312 return aSelection.Empty() || !aSelection.Front()->IsMoving();
313 };
314
315 auto noItemsCondition =
316 [ this ]( const SELECTION& aSelections ) -> bool
317 {
318 return frame()->GetBoard() && !frame()->GetBoard()->IsEmpty();
319 };
320
321 auto isSkippable =
322 [ this ]( const SELECTION& aSelection )
323 {
325 };
326
327 // Add context menu entries that are displayed when selection tool is active
329
330 // clang-format off
332 && notMovingCondition );
335 && notMovingCondition
336 && !inFootprintEditor );
338 && notMovingCondition );
339 menu.AddItem( PCB_ACTIONS::skip, isSkippable );
351 menu.AddItem( PCB_ACTIONS::mirrorH, canMirror );
352 menu.AddItem( PCB_ACTIONS::mirrorV, canMirror );
356
357 menu.AddItem( PCB_ACTIONS::properties, propertiesCondition );
358
360 && !inFootprintEditor );
362
363 // Footprint actions
364 menu.AddSeparator();
365 menu.AddItem( PCB_ACTIONS::editFpInFpEditor, singleFootprintCondition );
366 menu.AddItem( PCB_ACTIONS::updateFootprint, singleFootprintCondition );
367 menu.AddItem( PCB_ACTIONS::updateFootprints, multipleFootprintsCondition );
368 menu.AddItem( PCB_ACTIONS::changeFootprint, singleFootprintCondition );
369 menu.AddItem( PCB_ACTIONS::changeFootprints, multipleFootprintsCondition );
370
371 // Add the submenu for the special tools: modfiers and positioning tools
372 menu.AddSeparator( 100 );
373 menu.AddMenu( shapeModificationSubMenu.get(), shapeModificationCondition, 100 );
374 menu.AddMenu( positioningToolsSubMenu.get(), positioningToolsCondition, 100 );
375
376 menu.AddSeparator( 150 );
379
380 // Selection tool handles the context menu for some other tools, such as the Picker.
381 // Don't add things like Paste when another tool is active.
382 menu.AddItem( ACTIONS::paste, noActiveToolCondition, 150 );
383 menu.AddItem( ACTIONS::pasteSpecial, noActiveToolCondition && !inFootprintEditor, 150 );
386
387 menu.AddSeparator( 150 );
388 menu.AddItem( ACTIONS::selectAll, noItemsCondition, 150 );
389 menu.AddItem( ACTIONS::unselectAll, noItemsCondition, 150 );
390 // clang-format on
391
392 return true;
393}
394
395
397{
398 // GetAndPlace makes sense only in board editor, although it is also called
399 // in fpeditor, that shares the same EDIT_TOOL list
400 if( !getEditFrame<PCB_BASE_FRAME>()->IsType( FRAME_PCB_EDITOR ) )
401 return 0;
402
404 FOOTPRINT* fp = getEditFrame<PCB_BASE_FRAME>()->GetFootprintFromBoardByReference();
405
406 if( fp )
407 {
410
411 selectionTool->GetSelection().SetReferencePoint( fp->GetPosition() );
413 }
414
415 return 0;
416}
417
418
419bool EDIT_TOOL::invokeInlineRouter( int aDragMode )
420{
421 ROUTER_TOOL* theRouter = m_toolMgr->GetTool<ROUTER_TOOL>();
422
423 if( !theRouter )
424 return false;
425
426 // don't allow switch from moving to dragging
427 if( m_dragging )
428 {
429 wxBell();
430 return false;
431 }
432
433 // make sure we don't accidentally invoke inline routing mode while the router is already
434 // active!
435 if( theRouter->IsToolActive() )
436 return false;
437
438 if( theRouter->CanInlineDrag( aDragMode ) )
439 {
441 return true;
442 }
443
444 return false;
445}
446
447
449{
451
452 return router && router->RoutingInProgress();
453}
454
455
456int EDIT_TOOL::Drag( const TOOL_EVENT& aEvent )
457{
458 if( !m_toolMgr->GetTool<ROUTER_TOOL>() )
459 {
460 wxBell();
461 return false; // don't drag when no router tool (i.e. fp editor)
462 }
463
465 {
466 wxBell();
467 return false; // don't drag when router is already active
468 }
469
470 if( m_dragging )
471 {
472 wxBell();
473 return false; // don't do a router drag when already in an EDIT_TOOL drag
474 }
475
476 int mode = PNS::DM_ANY;
477
478 if( aEvent.IsAction( &PCB_ACTIONS::dragFreeAngle ) )
479 mode |= PNS::DM_FREE_ANGLE;
480
482 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
483 {
484 sTool->FilterCollectorForFreePads( aCollector );
485 sTool->FilterCollectorForHierarchy( aCollector, true );
486
487 std::vector<PCB_TRACK*> tracks;
488 std::vector<PCB_TRACK*> vias;
489 std::vector<FOOTPRINT*> footprints;
490
491 for( EDA_ITEM* item : aCollector )
492 {
493 if( PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item ) )
494 {
495 if( track->Type() == PCB_VIA_T )
496 vias.push_back( track );
497 else
498 tracks.push_back( track );
499 }
500 else if( FOOTPRINT* footprint = dynamic_cast<FOOTPRINT*>( item ) )
501 {
502 footprints.push_back( footprint );
503 }
504 }
505
506 if( footprints.size() == (unsigned) aCollector.GetCount() )
507 {
508 // We can drag multiple footprints; no need to whittle down selection.
509 }
510 else if( tracks.size() || vias.size() )
511 {
512 /*
513 * First trim down selection to active layer, tracks vs zones, etc.
514 */
515 if( aCollector.GetCount() > 1 )
516 sTool->GuessSelectionCandidates( aCollector, aPt );
517
518 /*
519 * If we have a knee between two tracks, or a via attached to two tracks,
520 * then drop the selection to a single item. We don't want a selection
521 * disambiguation menu when it doesn't matter which items is picked.
522 */
523 auto connected = []( PCB_TRACK* track, const VECTOR2I& pt )
524 {
525 return track->GetStart() == pt || track->GetEnd() == pt;
526 };
527
528 if( tracks.size() == 2 && vias.size() == 0 )
529 {
530 if( connected( tracks[0], tracks[1]->GetStart() )
531 || connected( tracks[0], tracks[1]->GetEnd() ) )
532 {
533 aCollector.Remove( tracks[1] );
534 }
535 }
536 else if( tracks.size() == 2 && vias.size() == 1 )
537 {
538 if( connected( tracks[0], vias[0]->GetPosition() )
539 && connected( tracks[1], vias[0]->GetPosition() ) )
540 {
541 aCollector.Remove( tracks[0] );
542 aCollector.Remove( tracks[1] );
543 }
544 }
545 }
546 },
547 true /* prompt user regarding locked items */ );
548
549 if( selection.Empty() )
550 return 0;
551
552 if( selection.Size() == 1 && selection.Front()->Type() == PCB_ARC_T )
553 {
554 // TODO: This really should be done in PNS to ensure DRC is maintained, but for now
555 // it allows interactive editing of an arc track
556 return DragArcTrack( aEvent );
557 }
558 else
559 {
560 invokeInlineRouter( mode );
561 }
562
563 return 0;
564}
565
566
568{
570
571 if( selection.Size() != 1 || selection.Front()->Type() != PCB_ARC_T )
572 return 0;
573
574 PCB_ARC* theArc = static_cast<PCB_ARC*>( selection.Front() );
575 EDA_ANGLE maxTangentDeviation( ADVANCED_CFG::GetCfg().m_MaxTangentAngleDeviation, DEGREES_T );
576
577 if( theArc->GetAngle() + maxTangentDeviation >= ANGLE_180 )
578 {
579 wxString msg = wxString::Format( _( "Unable to resize arc tracks of %s or greater." ),
580 EDA_UNIT_UTILS::UI::MessageTextFromValue( ANGLE_180 - maxTangentDeviation ) );
581 frame()->ShowInfoBarError( msg );
582
583 return 0; // don't bother with > 180 degree arcs
584 }
585
587
588 Activate();
589 // Must be done after Activate() so that it gets set into the correct context
590 controls->ShowCursor( true );
591 controls->SetAutoPan( true );
592
593 BOARD_COMMIT commit( this );
594 bool restore_state = false;
595
596 commit.Modify( theArc );
597
598 VECTOR2I arcCenter = theArc->GetCenter();
599 SEG tanStart = SEG( arcCenter, theArc->GetStart() ).PerpendicularSeg( theArc->GetStart() );
600 SEG tanEnd = SEG( arcCenter, theArc->GetEnd() ).PerpendicularSeg( theArc->GetEnd() );
601
602 //Ensure the tangent segments are in the correct orientation
603 OPT_VECTOR2I tanIntersect = tanStart.IntersectLines( tanEnd );
604
605 if( !tanIntersect )
606 return 0;
607
608 tanStart.A = *tanIntersect;
609 tanStart.B = theArc->GetStart();
610 tanEnd.A = *tanIntersect;
611 tanEnd.B = theArc->GetEnd();
612
613 std::set<PCB_TRACK*> addedTracks;
614
615 auto getUniqueTrackAtAnchorCollinear =
616 [&]( const VECTOR2I& aAnchor, const SEG& aCollinearSeg ) -> PCB_TRACK*
617 {
618 std::shared_ptr<CONNECTIVITY_DATA> conn = board()->GetConnectivity();
619
620 // Allow items at a distance within the width of the arc track
621 int allowedDeviation = theArc->GetWidth();
622
623 std::vector<BOARD_CONNECTED_ITEM*> itemsOnAnchor;
624
625 for( int i = 0; i < 3; i++ )
626 {
627 itemsOnAnchor = conn->GetConnectedItemsAtAnchor( theArc, aAnchor,
629 allowedDeviation );
630 allowedDeviation /= 2;
631
632 if( itemsOnAnchor.size() == 1 )
633 break;
634 }
635
636 PCB_TRACK* track = nullptr;
637
638 if( itemsOnAnchor.size() == 1 && itemsOnAnchor.front()->Type() == PCB_TRACE_T )
639 {
640 track = static_cast<PCB_TRACK*>( itemsOnAnchor.front() );
641 commit.Modify( track );
642
643 SEG trackSeg( track->GetStart(), track->GetEnd() );
644
645 // Allow deviations in colinearity as defined in ADVANCED_CFG
646 if( trackSeg.Angle( aCollinearSeg ) > maxTangentDeviation )
647 track = nullptr;
648 }
649
650 if( !track )
651 {
652 track = new PCB_TRACK( theArc->GetParent() );
653 track->SetStart( aAnchor );
654 track->SetEnd( aAnchor );
655 track->SetNet( theArc->GetNet() );
656 track->SetLayer( theArc->GetLayer() );
657 track->SetWidth( theArc->GetWidth() );
658 track->SetLocked( theArc->IsLocked() );
659 track->SetFlags( IS_NEW );
660 getView()->Add( track );
661 addedTracks.insert( track );
662 }
663
664 return track;
665 };
666
667 PCB_TRACK* trackOnStart = getUniqueTrackAtAnchorCollinear( theArc->GetStart(), tanStart);
668 PCB_TRACK* trackOnEnd = getUniqueTrackAtAnchorCollinear( theArc->GetEnd(), tanEnd );
669
670 if( trackOnStart->GetLength() != 0 )
671 {
672 tanStart.A = trackOnStart->GetStart();
673 tanStart.B = trackOnStart->GetEnd();
674 }
675
676 if( trackOnEnd->GetLength() != 0 )
677 {
678 tanEnd.A = trackOnEnd->GetStart();
679 tanEnd.B = trackOnEnd->GetEnd();
680 }
681
682 // Recalculate intersection point
683 if( tanIntersect = tanStart.IntersectLines( tanEnd ); !tanIntersect )
684 return 0;
685
686 auto isTrackStartClosestToArcStart =
687 [&]( PCB_TRACK* aTrack ) -> bool
688 {
689 double trackStartToArcStart = aTrack->GetStart().Distance( theArc->GetStart() );
690 double trackEndToArcStart = aTrack->GetEnd().Distance( theArc->GetStart() );
691
692 return trackStartToArcStart < trackEndToArcStart;
693 };
694
695 bool isStartTrackOnStartPt = isTrackStartClosestToArcStart( trackOnStart );
696 bool isEndTrackOnStartPt = isTrackStartClosestToArcStart( trackOnEnd );
697
698 // Calculate constraints
699 //======================
700 // maxTanCircle is the circle with maximum radius that is tangent to the two adjacent straight
701 // tracks and whose tangent points are constrained within the original tracks and their
702 // projected intersection points.
703 //
704 // The cursor will be constrained first within the isosceles triangle formed by the segments
705 // cSegTanStart, cSegTanEnd and cSegChord. After that it will be constrained to be outside
706 // maxTanCircle.
707 //
708 //
709 // ____________ <-cSegTanStart
710 // / * . ' *
711 // cSegTanEnd-> / * . ' *
712 // /* . ' <-cSegChord *
713 // /. '
714 // /* *
715 //
716 // * c * <-maxTanCircle
717 //
718 // * *
719 //
720 // * *
721 // * *
722 // * *
723 //
724
725 auto getFurthestPointToTanInterstect =
726 [&]( VECTOR2I& aPointA, VECTOR2I& aPointB ) -> VECTOR2I
727 {
728 if( ( aPointA - *tanIntersect ).EuclideanNorm()
729 > ( aPointB - *tanIntersect ).EuclideanNorm() )
730 {
731 return aPointA;
732 }
733 else
734 {
735 return aPointB;
736 }
737 };
738
739 CIRCLE maxTanCircle;
740 VECTOR2I tanStartPoint = getFurthestPointToTanInterstect( tanStart.A, tanStart.B );
741 VECTOR2I tanEndPoint = getFurthestPointToTanInterstect( tanEnd.A, tanEnd.B );
742 VECTOR2I tempTangentPoint = tanEndPoint;
743
744 if( getFurthestPointToTanInterstect( tanStartPoint, tanEndPoint ) == tanEndPoint )
745 tempTangentPoint = tanStartPoint;
746
747 maxTanCircle.ConstructFromTanTanPt( tanStart, tanEnd, tempTangentPoint );
748 VECTOR2I maxTanPtStart = tanStart.LineProject( maxTanCircle.Center );
749 VECTOR2I maxTanPtEnd = tanEnd.LineProject( maxTanCircle.Center );
750
751 SEG cSegTanStart( maxTanPtStart, *tanIntersect );
752 SEG cSegTanEnd( maxTanPtEnd, *tanIntersect );
753 SEG cSegChord( maxTanPtStart, maxTanPtEnd );
754
755 int cSegTanStartSide = cSegTanStart.Side( theArc->GetMid() );
756 int cSegTanEndSide = cSegTanEnd.Side( theArc->GetMid() );
757 int cSegChordSide = cSegChord.Side( theArc->GetMid() );
758
759 bool eatFirstMouseUp = true;
760
761 // Start the tool loop
762 while( TOOL_EVENT* evt = Wait() )
763 {
764 m_cursor = controls->GetMousePosition();
765
766 // Constrain cursor within the isosceles triangle
767 if( cSegTanStartSide != cSegTanStart.Side( m_cursor )
768 || cSegTanEndSide != cSegTanEnd.Side( m_cursor )
769 || cSegChordSide != cSegChord.Side( m_cursor ) )
770 {
771 std::vector<VECTOR2I> possiblePoints;
772
773 possiblePoints.push_back( cSegTanEnd.NearestPoint( m_cursor ) );
774 possiblePoints.push_back( cSegChord.NearestPoint( m_cursor ) );
775
776 VECTOR2I closest = cSegTanStart.NearestPoint( m_cursor );
777
778 for( const VECTOR2I& candidate : possiblePoints )
779 {
780 if( ( candidate - m_cursor ).SquaredEuclideanNorm()
781 < ( closest - m_cursor ).SquaredEuclideanNorm() )
782 {
783 closest = candidate;
784 }
785 }
786
787 m_cursor = closest;
788 }
789
790 // Constrain cursor to be outside maxTanCircle
791 if( ( m_cursor - maxTanCircle.Center ).EuclideanNorm() < maxTanCircle.Radius )
792 m_cursor = maxTanCircle.NearestPoint( m_cursor );
793
794 controls->ForceCursorPosition( true, m_cursor );
795
796 // Calculate resulting object coordinates
797 CIRCLE circlehelper;
798 circlehelper.ConstructFromTanTanPt( cSegTanStart, cSegTanEnd, m_cursor );
799
800 VECTOR2I newCenter = circlehelper.Center;
801 VECTOR2I newStart = cSegTanStart.LineProject( newCenter );
802 VECTOR2I newEnd = cSegTanEnd.LineProject( newCenter );
803 VECTOR2I newMid = CalcArcMid( newStart, newEnd, newCenter );
804
805 // Update objects
806 theArc->SetStart( newStart );
807 theArc->SetEnd( newEnd );
808 theArc->SetMid( newMid );
809
810 if( isStartTrackOnStartPt )
811 trackOnStart->SetStart( newStart );
812 else
813 trackOnStart->SetEnd( newStart );
814
815 if( isEndTrackOnStartPt )
816 trackOnEnd->SetStart( newEnd );
817 else
818 trackOnEnd->SetEnd( newEnd );
819
820 // Update view
821 getView()->Update( trackOnStart );
822 getView()->Update( trackOnEnd );
823 getView()->Update( theArc );
824
825 // Handle events
826 if( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) )
827 {
828 eatFirstMouseUp = false;
829 }
830 else if( evt->IsCancelInteractive() || evt->IsActivate() )
831 {
832 restore_state = true; // Canceling the tool means that items have to be restored
833 break; // Finish
834 }
835 else if( evt->IsAction( &ACTIONS::undo ) )
836 {
837 restore_state = true; // Perform undo locally
838 break; // Finish
839 }
840 else if( evt->IsMouseUp( BUT_LEFT ) || evt->IsClick( BUT_LEFT )
841 || evt->IsDblClick( BUT_LEFT ) )
842 {
843 // Eat mouse-up/-click events that leaked through from the lock dialog
844 if( eatFirstMouseUp && !evt->IsAction( &ACTIONS::cursorClick ) )
845 {
846 eatFirstMouseUp = false;
847 continue;
848 }
849
850 break; // Finish
851 }
852 }
853
854 // Amend the end points of the arc if we delete the joining tracks
855 VECTOR2I newStart = trackOnStart->GetStart();
856 VECTOR2I newEnd = trackOnEnd->GetStart();
857
858 if( isStartTrackOnStartPt )
859 newStart = trackOnStart->GetEnd();
860
861 if( isEndTrackOnStartPt )
862 newEnd = trackOnEnd->GetEnd();
863
864 int maxLengthIU =
865 KiROUND( ADVANCED_CFG::GetCfg().m_MaxTrackLengthToKeep * pcbIUScale.IU_PER_MM );
866
867 if( trackOnStart->GetLength() <= maxLengthIU )
868 {
869 if( addedTracks.count( trackOnStart ) )
870 {
871 getView()->Remove( trackOnStart );
872 addedTracks.erase( trackOnStart );
873 delete trackOnStart;
874 }
875 else
876 {
877 commit.Remove( trackOnStart );
878 }
879
880 theArc->SetStart( newStart );
881 }
882
883 if( trackOnEnd->GetLength() <= maxLengthIU )
884 {
885 if( addedTracks.count( trackOnEnd ) )
886 {
887 getView()->Remove( trackOnEnd );
888 addedTracks.erase( trackOnEnd );
889 delete trackOnEnd;
890 }
891 else
892 {
893 commit.Remove( trackOnEnd );
894 }
895
896 theArc->SetEnd( newEnd );
897 }
898
899 if( theArc->GetLength() <= 0 )
900 commit.Remove( theArc );
901
902 for( PCB_TRACK* added : addedTracks )
903 {
904 getView()->Remove( added );
905 commit.Add( added );
906 }
907
908 // Should we commit?
909 if( restore_state )
910 commit.Revert();
911 else
912 commit.Push( _( "Drag Arc Track" ) );
913
914 return 0;
915}
916
917
919{
921 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
922 {
923 // Iterate from the back so we don't have to worry about removals.
924 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
925 {
926 BOARD_ITEM* item = aCollector[ i ];
927
928 if( !dynamic_cast<PCB_TRACK*>( item ) )
929 aCollector.Remove( item );
930 }
931 },
932 true /* prompt user regarding locked items */ );
933
934 BOARD_COMMIT commit( this );
935
936 for( EDA_ITEM* item : selection )
937 {
938 if( item->Type() == PCB_VIA_T )
939 {
940 PCB_VIA* via = static_cast<PCB_VIA*>( item );
941
942 commit.Modify( via );
943
944 int new_width;
945 int new_drill;
946
947 if( via->GetViaType() == VIATYPE::MICROVIA )
948 {
949 NETCLASS* netClass = via->GetEffectiveNetClass();
950
951 new_width = netClass->GetuViaDiameter();
952 new_drill = netClass->GetuViaDrill();
953 }
954 else
955 {
956 new_width = board()->GetDesignSettings().GetCurrentViaSize();
957 new_drill = board()->GetDesignSettings().GetCurrentViaDrill();
958 }
959
960 via->SetDrill( new_drill );
961 via->SetWidth( new_width );
962 }
963 else if( item->Type() == PCB_TRACE_T || item->Type() == PCB_ARC_T )
964 {
965 PCB_TRACK* track = dynamic_cast<PCB_TRACK*>( item );
966
967 wxCHECK( track, 0 );
968
969 commit.Modify( track );
970
971 int new_width = board()->GetDesignSettings().GetCurrentTrackWidth();
972 track->SetWidth( new_width );
973 }
974 }
975
976 commit.Push( _( "Edit Track Width/Via Size" ) );
977
978 if( selection.IsHover() )
979 {
981
982 // Notify other tools of the changes -- This updates the visual ratsnest
984 }
985
986 return 0;
987}
988
989
991{
992 // Store last used fillet radius to allow pressing "enter" if repeat fillet is required
993 static int filletRadius = 0;
994
996 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
997 {
998 // Iterate from the back so we don't have to worry about removals.
999 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1000 {
1001 BOARD_ITEM* item = aCollector[i];
1002
1003 if( !dynamic_cast<PCB_TRACK*>( item ) )
1004 aCollector.Remove( item );
1005 }
1006 },
1007 true /* prompt user regarding locked items */ );
1008
1009 if( selection.Size() < 2 )
1010 {
1011 frame()->ShowInfoBarMsg( _( "At least two straight track segments must be selected." ) );
1012 return 0;
1013 }
1014
1015 WX_UNIT_ENTRY_DIALOG dlg( frame(), _( "Fillet Tracks" ), _( "Radius:" ), filletRadius );
1016
1017 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == 0 )
1018 return 0;
1019
1020 filletRadius = dlg.GetValue();
1021
1022 struct FILLET_OP
1023 {
1024 PCB_TRACK* t1;
1025 PCB_TRACK* t2;
1026 // Start point of track is modified after PCB_ARC is added, otherwise the end point:
1027 bool t1Start = true;
1028 bool t2Start = true;
1029 };
1030
1031 std::vector<FILLET_OP> filletOperations;
1032 bool operationPerformedOnAtLeastOne = false;
1033 bool didOneAttemptFail = false;
1034 std::set<PCB_TRACK*> processedTracks;
1035
1036 auto processFilletOp =
1037 [&]( PCB_TRACK* aTrack, bool aStartPoint )
1038 {
1039 std::shared_ptr<CONNECTIVITY_DATA> c = board()->GetConnectivity();
1040 VECTOR2I anchor = aStartPoint ? aTrack->GetStart() : aTrack->GetEnd();
1041 std::vector<BOARD_CONNECTED_ITEM*> itemsOnAnchor;
1042
1043 itemsOnAnchor = c->GetConnectedItemsAtAnchor( aTrack, anchor, baseConnectedTypes );
1044
1045 if( itemsOnAnchor.size() > 0
1046 && selection.Contains( itemsOnAnchor.at( 0 ) )
1047 && itemsOnAnchor.at( 0 )->Type() == PCB_TRACE_T )
1048 {
1049 PCB_TRACK* trackOther = static_cast<PCB_TRACK*>( itemsOnAnchor.at( 0 ) );
1050
1051 // Make sure we don't fillet the same pair of tracks twice
1052 if( processedTracks.find( trackOther ) == processedTracks.end() )
1053 {
1054 if( itemsOnAnchor.size() == 1 )
1055 {
1056 FILLET_OP filletOp;
1057 filletOp.t1 = aTrack;
1058 filletOp.t2 = trackOther;
1059 filletOp.t1Start = aStartPoint;
1060 filletOp.t2Start = aTrack->IsPointOnEnds( filletOp.t2->GetStart() );
1061 filletOperations.push_back( filletOp );
1062 }
1063 else
1064 {
1065 // User requested to fillet these two tracks but not possible as
1066 // there are other elements connected at that point
1067 didOneAttemptFail = true;
1068 }
1069 }
1070 }
1071 };
1072
1073 for( EDA_ITEM* item : selection )
1074 {
1075 if( item->Type() == PCB_TRACE_T )
1076 {
1077 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
1078
1079 if( track->GetLength() > 0 )
1080 {
1081 processFilletOp( track, true ); // on the start point of track
1082 processFilletOp( track, false ); // on the end point of track
1083
1084 processedTracks.insert( track );
1085 }
1086 }
1087 }
1088
1089 BOARD_COMMIT commit( this );
1090 std::vector<BOARD_ITEM*> itemsToAddToSelection;
1091
1092 for( FILLET_OP filletOp : filletOperations )
1093 {
1094 PCB_TRACK* track1 = filletOp.t1;
1095 PCB_TRACK* track2 = filletOp.t2;
1096
1097 bool trackOnStart = track1->IsPointOnEnds( track2->GetStart() );
1098 bool trackOnEnd = track1->IsPointOnEnds( track2->GetEnd() );
1099
1100 if( trackOnStart && trackOnEnd )
1101 continue; // Ignore duplicate tracks
1102
1103 if( ( trackOnStart || trackOnEnd ) && track1->GetLayer() == track2->GetLayer() )
1104 {
1105 SEG t1Seg( track1->GetStart(), track1->GetEnd() );
1106 SEG t2Seg( track2->GetStart(), track2->GetEnd() );
1107
1108 if( t1Seg.ApproxCollinear( t2Seg ) )
1109 continue;
1110
1111 SHAPE_ARC sArc( t1Seg, t2Seg, filletRadius );
1112 VECTOR2I t1newPoint, t2newPoint;
1113
1114 auto setIfPointOnSeg =
1115 []( VECTOR2I& aPointToSet, const SEG& aSegment, const VECTOR2I& aVecToTest )
1116 {
1117 VECTOR2I segToVec = aSegment.NearestPoint( aVecToTest ) - aVecToTest;
1118
1119 // Find out if we are on the segment (minimum precision)
1121 {
1122 aPointToSet.x = aVecToTest.x;
1123 aPointToSet.y = aVecToTest.y;
1124 return true;
1125 }
1126
1127 return false;
1128 };
1129
1130 //Do not draw a fillet if the end points of the arc are not within the track segments
1131 if( !setIfPointOnSeg( t1newPoint, t1Seg, sArc.GetP0() )
1132 && !setIfPointOnSeg( t2newPoint, t2Seg, sArc.GetP0() ) )
1133 {
1134 didOneAttemptFail = true;
1135 continue;
1136 }
1137
1138 if( !setIfPointOnSeg( t1newPoint, t1Seg, sArc.GetP1() )
1139 && !setIfPointOnSeg( t2newPoint, t2Seg, sArc.GetP1() ) )
1140 {
1141 didOneAttemptFail = true;
1142 continue;
1143 }
1144
1145 PCB_ARC* tArc = new PCB_ARC( frame()->GetBoard(), &sArc );
1146 tArc->SetLayer( track1->GetLayer() );
1147 tArc->SetWidth( track1->GetWidth() );
1148 tArc->SetNet( track1->GetNet() );
1149 tArc->SetLocked( track1->IsLocked() );
1150 commit.Add( tArc );
1151 itemsToAddToSelection.push_back( tArc );
1152
1153 commit.Modify( track1 );
1154 commit.Modify( track2 );
1155
1156 if( filletOp.t1Start )
1157 track1->SetStart( t1newPoint );
1158 else
1159 track1->SetEnd( t1newPoint );
1160
1161 if( filletOp.t2Start )
1162 track2->SetStart( t2newPoint );
1163 else
1164 track2->SetEnd( t2newPoint );
1165
1166 operationPerformedOnAtLeastOne = true;
1167 }
1168 }
1169
1170 commit.Push( _( "Fillet Tracks" ) );
1171
1172 //select the newly created arcs
1173 for( BOARD_ITEM* item : itemsToAddToSelection )
1175
1176 if( !operationPerformedOnAtLeastOne )
1177 frame()->ShowInfoBarMsg( _( "Unable to fillet the selected track segments." ) );
1178 else if( didOneAttemptFail )
1179 frame()->ShowInfoBarMsg( _( "Some of the track segments could not be filleted." ) );
1180
1181 return 0;
1182}
1183
1184
1193static std::optional<int> GetFilletParams( PCB_BASE_EDIT_FRAME& aFrame )
1194{
1195 // Store last used fillet radius to allow pressing "enter" if repeat fillet is required
1196 static int filletRadius = 0;
1197
1198 WX_UNIT_ENTRY_DIALOG dlg( &aFrame, _( "Fillet Lines" ), _( "Radius:" ), filletRadius );
1199
1200 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == 0 )
1201 return std::nullopt;
1202
1203 filletRadius = dlg.GetValue();
1204
1205 return filletRadius;
1206}
1207
1208
1217static std::optional<CHAMFER_PARAMS> GetChamferParams( PCB_BASE_EDIT_FRAME& aFrame )
1218{
1219 // Non-zero and the KLC default for Fab layer chamfers
1220 const int default_setback = pcbIUScale.mmToIU( 1 );
1221 // Store last used setback to allow pressing "enter" if repeat chamfer is required
1222 static CHAMFER_PARAMS params{ default_setback, default_setback };
1223
1224 WX_UNIT_ENTRY_DIALOG dlg( &aFrame, _( "Chamfer Lines" ), _( "Chamfer setback:" ),
1225 params.m_chamfer_setback_a );
1226
1227 if( dlg.ShowModal() == wxID_CANCEL || dlg.GetValue() == 0 )
1228 return std::nullopt;
1229
1230 params.m_chamfer_setback_a = dlg.GetValue();
1231 // It's hard to easily specify an asymmetric chamfer (which line gets the longer setback?),
1232 // so we just use the same setback for each
1233 params.m_chamfer_setback_b = params.m_chamfer_setback_a;
1234
1235 return params;
1236}
1237
1238
1240{
1242 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1243 {
1244 std::vector<VECTOR2I> pts;
1245
1246 // Iterate from the back so we don't have to worry about removals.
1247 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1248 {
1249 BOARD_ITEM* item = aCollector[i];
1250
1251 // We've converted the polygon and rectangle to segments, so drop everything
1252 // that isn't a segment at this point
1253 if( !item->IsType( { PCB_SHAPE_LOCATE_SEGMENT_T,
1254 PCB_SHAPE_LOCATE_POLY_T,
1255 PCB_SHAPE_LOCATE_RECT_T } ) )
1256 {
1257 aCollector.Remove( item );
1258 }
1259 }
1260 },
1261 true /* prompt user regarding locked items */ );
1262
1263 std::set<PCB_SHAPE*> lines_to_add;
1264 std::vector<PCB_SHAPE*> items_to_remove;
1265
1266 for( EDA_ITEM* item : selection )
1267 {
1268 std::vector<VECTOR2I> pts;
1269 PCB_SHAPE *graphic = static_cast<PCB_SHAPE*>( item );
1270 PCB_LAYER_ID layer = graphic->GetLayer();
1271 int width = graphic->GetWidth();
1272
1273 if( graphic->GetShape() == SHAPE_T::RECTANGLE )
1274 {
1275 items_to_remove.push_back( graphic );
1276 VECTOR2I start( graphic->GetStart() );
1277 VECTOR2I end( graphic->GetEnd() );
1278 pts.emplace_back( start );
1279 pts.emplace_back( VECTOR2I( end.x, start.y ) );
1280 pts.emplace_back( end );
1281 pts.emplace_back( VECTOR2I( start.x, end.y ) );
1282 }
1283
1284 if( graphic->GetShape() == SHAPE_T::POLY )
1285 {
1286 items_to_remove.push_back( graphic );
1287
1288 for( int jj = 0; jj < graphic->GetPolyShape().VertexCount(); ++jj )
1289 pts.emplace_back( graphic->GetPolyShape().CVertex( jj ) );
1290 }
1291
1292 for( size_t jj = 1; jj < pts.size(); ++jj )
1293 {
1294 PCB_SHAPE *line = new PCB_SHAPE( frame()->GetModel(), SHAPE_T::SEGMENT );
1295
1296 line->SetStart( pts[jj - 1] );
1297 line->SetEnd( pts[jj] );
1298 line->SetWidth( width );
1299 line->SetLayer( layer );
1300 lines_to_add.insert( line );
1301 }
1302
1303 if( pts.size() > 1 )
1304 {
1305 PCB_SHAPE *line = new PCB_SHAPE( frame()->GetModel(), SHAPE_T::SEGMENT );
1306
1307 line->SetStart( pts.back() );
1308 line->SetEnd( pts.front() );
1309 line->SetWidth( width );
1310 line->SetLayer( layer );
1311 lines_to_add.insert( line );
1312 }
1313 }
1314
1315 int segmentCount = selection.CountType( PCB_SHAPE_LOCATE_SEGMENT_T ) + lines_to_add.size();
1316
1317 if( aEvent.IsAction( &PCB_ACTIONS::extendLines ) && segmentCount != 2 )
1318 {
1319 frame()->ShowInfoBarMsg( _( "Exactly two lines must be selected to extend them." ) );
1320
1321 for( PCB_SHAPE* line : lines_to_add )
1322 delete line;
1323
1324 return 0;
1325 }
1326 else if( segmentCount < 2 )
1327 {
1328 frame()->ShowInfoBarMsg( _( "A shape with at least two lines must be selected." ) );
1329
1330 for( PCB_SHAPE* line : lines_to_add )
1331 delete line;
1332
1333 return 0;
1334 }
1335
1336 BOARD_COMMIT commit( this );
1337
1338 // Items created like lines from a rectangle
1339 for( PCB_SHAPE* item : lines_to_add )
1340 {
1341 commit.Add( item );
1342 selection.Add( item );
1343 }
1344
1345 // Remove items like rectangles that we decomposed into lines
1346 for( PCB_SHAPE* item : items_to_remove )
1347 {
1348 selection.Remove( item );
1349 commit.Remove( item );
1350 }
1351
1352 for( EDA_ITEM* item : selection )
1353 item->ClearFlags( STRUCT_DELETED );
1354
1355 // List of thing to select at the end of the operation
1356 // (doing it as we go will invalidate the iterator)
1357 std::vector<PCB_SHAPE*> items_to_select_on_success;
1358
1359 // And same for items to deselect
1360 std::vector<PCB_SHAPE*> items_to_deselect_on_success;
1361
1362 // Handle modifications to existing items by the routine
1363 // How to deal with this depends on whether we're in the footprint editor or not
1364 // and whether the item was conjured up by decomposing a polygon or rectangle
1365 auto item_modification_handler =
1366 [&]( PCB_SHAPE& aItem )
1367 {
1368 // If the item was "conjured up" it will be added later separately
1369 if( !alg::contains( lines_to_add, &aItem ) )
1370 {
1371 commit.Modify( &aItem );
1372 items_to_select_on_success.push_back( &aItem );
1373 }
1374 };
1375
1376 bool any_items_created = !lines_to_add.empty();
1377 auto item_creation_handler =
1378 [&]( std::unique_ptr<PCB_SHAPE> aItem )
1379 {
1380 any_items_created = true;
1381 items_to_select_on_success.push_back( aItem.get() );
1382 commit.Add( aItem.release() );
1383 };
1384
1385 bool any_items_removed = !items_to_remove.empty();
1386 auto item_removal_handler =
1387 [&]( PCB_SHAPE& aItem )
1388 {
1389 aItem.SetFlags( STRUCT_DELETED );
1390 any_items_removed = true;
1391 items_to_deselect_on_success.push_back( &aItem );
1392 commit.Remove( &aItem );
1393 };
1394
1395 // Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
1397 item_creation_handler, item_modification_handler, item_removal_handler );
1398
1399 // Construct an appropriate tool
1400 std::unique_ptr<PAIRWISE_LINE_ROUTINE> pairwise_line_routine;
1401
1402 if( aEvent.IsAction( &PCB_ACTIONS::filletLines ) )
1403 {
1404 std::optional<int> filletRadiusIU = GetFilletParams( *frame() );
1405
1406 if( filletRadiusIU.has_value() )
1407 {
1408 pairwise_line_routine = std::make_unique<LINE_FILLET_ROUTINE>( frame()->GetModel(),
1409 change_handler,
1410 *filletRadiusIU );
1411 }
1412 }
1413 else if( aEvent.IsAction( &PCB_ACTIONS::chamferLines ) )
1414 {
1415 std::optional<CHAMFER_PARAMS> chamfer_params = GetChamferParams( *frame() );
1416
1417 if( chamfer_params.has_value() )
1418 {
1419 pairwise_line_routine = std::make_unique<LINE_CHAMFER_ROUTINE>( frame()->GetModel(),
1420 change_handler,
1421 *chamfer_params );
1422 }
1423 }
1424 else if( aEvent.IsAction( &PCB_ACTIONS::extendLines ) )
1425 {
1426 pairwise_line_routine = std::make_unique<LINE_EXTENSION_ROUTINE>( frame()->GetModel(),
1427 change_handler );
1428 }
1429
1430 if( !pairwise_line_routine )
1431 {
1432 // Didn't construct any mofication routine - user must have cancelled
1433 commit.Revert();
1434 return 0;
1435 }
1436
1437 // Apply the tool to every line pair
1438 alg::for_all_pairs( selection.begin(), selection.end(),
1439 [&]( EDA_ITEM* a, EDA_ITEM* b )
1440 {
1441 if( ( a->GetFlags() & STRUCT_DELETED ) == 0
1442 && ( b->GetFlags() & STRUCT_DELETED ) == 0 )
1443 {
1444 PCB_SHAPE* line_a = static_cast<PCB_SHAPE*>( a );
1445 PCB_SHAPE* line_b = static_cast<PCB_SHAPE*>( b );
1446
1447 pairwise_line_routine->ProcessLinePair( *line_a, *line_b );
1448 }
1449 } );
1450
1451 // Select added and modified items
1452 for( PCB_SHAPE* item : items_to_select_on_success )
1453 m_selectionTool->AddItemToSel( item, true );
1454
1455 // Deselect removed items
1456 for( PCB_SHAPE* item : items_to_deselect_on_success )
1457 m_selectionTool->RemoveItemFromSel( item, true );
1458
1459 if( any_items_removed )
1460 m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
1461
1462 if( any_items_created )
1463 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1464
1465 // Notify other tools of the changes
1466 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1467
1468 commit.Push( pairwise_line_routine->GetCommitDescription() );
1469
1470 if( const std::optional<wxString> msg = pairwise_line_routine->GetStatusMessage() )
1471 frame()->ShowInfoBarMsg( *msg );
1472
1473 return 0;
1474}
1475
1476
1478{
1480 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1481 {
1482 std::vector<VECTOR2I> pts;
1483
1484 // Iterate from the back so we don't have to worry about removals.
1485 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1486 {
1487 BOARD_ITEM* item = aCollector[i];
1488
1489 if( !item->IsType( { PCB_SHAPE_LOCATE_POLY_T, PCB_ZONE_T } ) )
1490 aCollector.Remove( item );
1491
1492 if( ZONE* zone = dyn_cast<ZONE*>( item ) )
1493 {
1494 if( zone->IsTeardropArea() )
1495 aCollector.Remove( item );
1496 }
1497 }
1498 },
1499 true /* prompt user regarding locked items */ );
1500
1501 // Store last used value
1502 static int s_toleranceValue = pcbIUScale.mmToIU( 3 );
1503
1504 WX_UNIT_ENTRY_DIALOG dlg( frame(), _( "Simplify Shapes" ), _( "Tolerance value:" ),
1505 s_toleranceValue );
1506
1507 if( dlg.ShowModal() == wxID_CANCEL )
1508 return 0;
1509
1510 s_toleranceValue = dlg.GetValue();
1511
1512 if( s_toleranceValue <= 0 )
1513 return 0;
1514
1515 BOARD_COMMIT commit{ this };
1516
1517 std::vector<PCB_SHAPE*> shapeList;
1518
1519 for( EDA_ITEM* item : selection )
1520 {
1521 commit.Modify( item );
1522
1523 if( PCB_SHAPE* shape = dyn_cast<PCB_SHAPE*>( item ) )
1524 {
1525 SHAPE_POLY_SET& poly = shape->GetPolyShape();
1526
1527 poly.SimplifyOutlines( s_toleranceValue );
1528 }
1529
1530 if( ZONE* zone = dyn_cast<ZONE*>( item ) )
1531 {
1532 SHAPE_POLY_SET* poly = zone->Outline();
1533
1534 poly->SimplifyOutlines( s_toleranceValue );
1535 }
1536 }
1537
1538 commit.Push( _( "Simplify Polygons" ) );
1539
1540 // Notify other tools of the changes
1541 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1542
1543 return 0;
1544}
1545
1546
1548{
1550 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1551 {
1552 std::vector<VECTOR2I> pts;
1553
1554 // Iterate from the back so we don't have to worry about removals.
1555 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1556 {
1557 BOARD_ITEM* item = aCollector[i];
1558
1559 // We've converted the polygon and rectangle to segments, so drop everything
1560 // that isn't a segment at this point
1561 if( !item->IsType( { PCB_SHAPE_LOCATE_SEGMENT_T, PCB_SHAPE_LOCATE_ARC_T,
1562 PCB_SHAPE_LOCATE_BEZIER_T } ) )
1563 {
1564 aCollector.Remove( item );
1565 }
1566 }
1567 },
1568 true /* prompt user regarding locked items */ );
1569
1570 // Store last used value
1571 static int s_toleranceValue = pcbIUScale.mmToIU( 3 );
1572
1573 WX_UNIT_ENTRY_DIALOG dlg( frame(), _( "Heal Shapes" ), _( "Tolerance value:" ),
1574 s_toleranceValue );
1575
1576 if( dlg.ShowModal() == wxID_CANCEL )
1577 return 0;
1578
1579 s_toleranceValue = dlg.GetValue();
1580
1581 if( s_toleranceValue <= 0 )
1582 return 0;
1583
1584 BOARD_COMMIT commit{ this };
1585
1586 std::vector<PCB_SHAPE*> shapeList;
1587 std::vector<std::unique_ptr<PCB_SHAPE>> newShapes;
1588
1589 for( EDA_ITEM* item : selection )
1590 {
1591 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( item ) )
1592 {
1593 shapeList.push_back( shape );
1594 commit.Modify( shape );
1595 }
1596 }
1597
1598 ConnectBoardShapes( shapeList, newShapes, s_toleranceValue );
1599
1600 std::vector<PCB_SHAPE*> items_to_select;
1601
1602 for( std::unique_ptr<PCB_SHAPE>& ptr : newShapes )
1603 {
1604 PCB_SHAPE* shape = ptr.release();
1605
1606 commit.Add( shape );
1607 items_to_select.push_back( shape );
1608 }
1609
1610 commit.Push( _( "Heal Shapes" ) );
1611
1612 // Select added items
1613 for( PCB_SHAPE* item : items_to_select )
1614 m_selectionTool->AddItemToSel( item, true );
1615
1616 if( items_to_select.size() > 0 )
1617 m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
1618
1619 // Notify other tools of the changes
1620 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1621
1622 return 0;
1623}
1624
1625
1627{
1629 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1630 {
1631 // Iterate from the back so we don't have to worry about removals.
1632 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
1633 {
1634 BOARD_ITEM* item = aCollector[i];
1635
1636 if( !item->IsType( {
1637 PCB_SHAPE_LOCATE_POLY_T,
1638 PCB_SHAPE_LOCATE_RECT_T,
1639 } ) )
1640 {
1641 aCollector.Remove( item );
1642 }
1643 }
1644 },
1645 true /* prompt user regarding locked items */ );
1646
1647 const EDA_ITEM* const last_item = selection.GetLastAddedItem();
1648
1649 // Gather or construct polygon source shapes to merge
1650 std::vector<PCB_SHAPE*> items_to_process;
1651
1652 for( EDA_ITEM* item : selection )
1653 {
1654 items_to_process.push_back( static_cast<PCB_SHAPE*>( item ) );
1655
1656 // put the last one in the selection at the front of the vector
1657 // so it can be used as the property donor and as the basis for the
1658 // boolean operation
1659 if( item == last_item )
1660 std::swap( items_to_process.back(), items_to_process.front() );
1661 }
1662
1663 BOARD_COMMIT commit{ this };
1664
1665 // Handle modifications to existing items by the routine
1666 auto item_modification_handler =
1667 [&]( PCB_SHAPE& aItem )
1668 {
1669 commit.Modify( &aItem );
1670 };
1671
1672 std::vector<PCB_SHAPE*> items_to_select_on_success;
1673
1674 auto item_creation_handler =
1675 [&]( std::unique_ptr<PCB_SHAPE> aItem )
1676 {
1677 items_to_select_on_success.push_back( aItem.get() );
1678 commit.Add( aItem.release() );
1679 };
1680
1681 auto item_removal_handler =
1682 [&]( PCB_SHAPE& aItem )
1683 {
1684 commit.Remove( &aItem );
1685 };
1686
1687 // Combine these callbacks into a CHANGE_HANDLER to inject in the ROUTINE
1689 item_creation_handler, item_modification_handler, item_removal_handler );
1690
1691 // Construct an appropriate routine
1692 std::unique_ptr<POLYGON_BOOLEAN_ROUTINE> boolean_routine;
1693 if( aEvent.IsAction( &PCB_ACTIONS::mergePolygons ) )
1694 {
1695 boolean_routine = std::make_unique<POLYGON_MERGE_ROUTINE>( frame()->GetModel(),
1696 change_handler );
1697 }
1698 else if( aEvent.IsAction( &PCB_ACTIONS::subtractPolygons ) )
1699 {
1700 boolean_routine = std::make_unique<POLYGON_SUBTRACT_ROUTINE>( frame()->GetModel(),
1701 change_handler );
1702 }
1703 else if( aEvent.IsAction( &PCB_ACTIONS::intersectPolygons ) )
1704 {
1705 boolean_routine = std::make_unique<POLYGON_INTERSECT_ROUTINE>( frame()->GetModel(),
1706 change_handler );
1707 }
1708 else
1709 {
1710 wxASSERT_MSG( false, "Could not find a polygon routine for this action" );
1711 return 0;
1712 }
1713
1714 // Perform the operation on each polygon
1715 for( PCB_SHAPE* shape : items_to_process )
1716 boolean_routine->ProcessShape( *shape );
1717
1718 // Select new items
1719 for( PCB_SHAPE* item : items_to_select_on_success )
1720 m_selectionTool->AddItemToSel( item, true );
1721
1722 // Notify other tools of the changes
1723 m_toolMgr->ProcessEvent( EVENTS::SelectedItemsModified );
1724
1725 commit.Push( boolean_routine->GetCommitDescription() );
1726
1727 if( const std::optional<wxString> msg = boolean_routine->GetStatusMessage() )
1728 frame()->ShowInfoBarMsg( *msg );
1729
1730 return 0;
1731}
1732
1733
1735{
1736 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1738 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1739 {
1740 } );
1741
1742 // Tracks & vias are treated in a special way:
1743 if( ( SELECTION_CONDITIONS::OnlyTypes( { PCB_TRACE_T, PCB_ARC_T, PCB_VIA_T } ) )( selection ) )
1744 {
1745 DIALOG_TRACK_VIA_PROPERTIES dlg( editFrame, selection );
1746 dlg.ShowQuasiModal(); // QuasiModal required for NET_SELECTOR
1747 }
1748 else if( ( SELECTION_CONDITIONS::OnlyTypes( { PCB_TABLECELL_T } ) )( selection ) )
1749 {
1750 std::vector<PCB_TABLECELL*> cells;
1751
1752 for( EDA_ITEM* item : selection.Items() )
1753 cells.push_back( static_cast<PCB_TABLECELL*>( item ) );
1754
1755 DIALOG_TABLECELL_PROPERTIES dlg( editFrame, cells );
1756
1757 dlg.ShowModal();
1758
1760 {
1761 PCB_TABLE* table = static_cast<PCB_TABLE*>( cells[0]->GetParent() );
1762 DIALOG_TABLE_PROPERTIES tableDlg( frame(), table );
1763
1764 tableDlg.ShowQuasiModal(); // Scintilla's auto-complete requires quasiModal
1765 }
1766 }
1767 else if( selection.Size() == 1 )
1768 {
1769 // Display properties dialog
1770 if( BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( selection.Front() ) )
1771 {
1772 // Do not handle undo buffer, it is done by the properties dialogs
1773 editFrame->OnEditItemRequest( item );
1774
1775 // Notify other tools of the changes
1777 }
1778 }
1779 else if( selection.Size() == 0 && getView()->IsLayerVisible( LAYER_DRAWINGSHEET ) )
1780 {
1781 DS_PROXY_VIEW_ITEM* ds = editFrame->GetCanvas()->GetDrawingSheet();
1782 VECTOR2D cursorPos = getViewControls()->GetCursorPosition( false );
1783
1784 if( ds && ds->HitTestDrawingSheetItems( getView(), cursorPos ) )
1786 else
1788 }
1789
1790 if( selection.IsHover() )
1791 {
1793 }
1794 else
1795 {
1796 // Check for items becoming invisible and drop them from the selection.
1797
1798 PCB_SELECTION selCopy = selection;
1799 LSET visible = editFrame->GetBoard()->GetVisibleLayers();
1800
1801 for( EDA_ITEM* eda_item : selCopy )
1802 {
1803 if( BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( eda_item ) )
1804 {
1805 if( !( item->GetLayerSet() & visible ).any() )
1807 }
1808 }
1809 }
1810
1811 return 0;
1812}
1813
1814
1815int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
1816{
1817 if( isRouterActive() )
1818 {
1819 wxBell();
1820 return 0;
1821 }
1822
1823 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
1824 BOARD_COMMIT localCommit( this );
1825 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
1826
1827 if( !commit )
1828 commit = &localCommit;
1829
1830 // Be sure that there is at least one item that we can modify. If nothing was selected before,
1831 // try looking for the stuff under mouse cursor (i.e. KiCad old-style hover selection)
1833 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1834 {
1835 sTool->FilterCollectorForHierarchy( aCollector, true );
1836 sTool->FilterCollectorForMarkers( aCollector );
1837 sTool->FilterCollectorForTableCells( aCollector );
1838 },
1839 // Prompt user regarding locked items if in board editor and in free-pad-mode (if
1840 // we're not in free-pad mode we delay this until the second RequestSelection()).
1842
1843 if( selection.Empty() )
1844 return 0;
1845
1846 std::optional<VECTOR2I> oldRefPt;
1847 bool is_hover = selection.IsHover(); // N.B. This must be saved before the second
1848 // call to RequestSelection() below
1849
1850 if( selection.HasReferencePoint() )
1851 oldRefPt = selection.GetReferencePoint();
1852
1853 // Now filter out pads if not in free pads mode. We cannot do this in the first
1854 // RequestSelection() as we need the reference point when a pad is the selection front.
1856 {
1857 selection = m_selectionTool->RequestSelection(
1858 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
1859 {
1860 sTool->FilterCollectorForMarkers( aCollector );
1861 sTool->FilterCollectorForHierarchy( aCollector, true );
1862 sTool->FilterCollectorForFreePads( aCollector );
1863 sTool->FilterCollectorForTableCells( aCollector );
1864 },
1865 true /* prompt user regarding locked items */ );
1866 }
1867
1868 // Did we filter everything out? If so, don't try to operate further
1869 if( selection.Empty() )
1870 return 0;
1871
1872 // A single textbox "walks" if we rotate it around its position, as we keep resetting which
1873 // corner is the origin.
1874 if( selection.Size() == 1 && dynamic_cast<PCB_TEXTBOX*>( selection.Front() ) )
1875 {
1876 selection.SetReferencePoint( static_cast<PCB_TEXTBOX*>( selection.Front() )->GetCenter() );
1877 }
1878 else
1879 {
1880 updateModificationPoint( selection );
1881 }
1882
1883 VECTOR2I refPt = selection.GetReferencePoint();
1884 EDA_ANGLE rotateAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *editFrame, aEvent );
1885
1886 if( frame()->GetCanvas()->GetView()->GetGAL()->IsFlippedX() )
1887 rotateAngle = -rotateAngle;
1888
1889 // Calculate view bounding box
1890 BOX2I viewBBox = selection.Front()->ViewBBox();
1891
1892 for( EDA_ITEM* item : selection )
1893 viewBBox.Merge( item->ViewBBox() );
1894
1895 // Check if the view bounding box will go out of bounds
1896 VECTOR2D rotPos = viewBBox.GetPosition();
1897 VECTOR2D rotEnd = viewBBox.GetEnd();
1898
1899 RotatePoint( &rotPos.x, &rotPos.y, refPt.x, refPt.y, rotateAngle );
1900 RotatePoint( &rotEnd.x, &rotEnd.y, refPt.x, refPt.y, rotateAngle );
1901
1902 typedef std::numeric_limits<int> coord_limits;
1903
1904 int max = coord_limits::max() - COORDS_PADDING;
1905 int min = -max;
1906
1907 bool outOfBounds = rotPos.x < min || rotPos.x > max || rotPos.y < min || rotPos.y > max
1908 || rotEnd.x < min || rotEnd.x > max || rotEnd.y < min || rotEnd.y > max;
1909
1910 if( !outOfBounds )
1911 {
1912 for( EDA_ITEM* item : selection )
1913 {
1914 if( !item->IsNew() && !item->IsMoving() )
1915 commit->Modify( item );
1916
1917 if( BOARD_ITEM* board_item = dynamic_cast<BOARD_ITEM*>( item ) )
1918 {
1919 board_item->Rotate( refPt, rotateAngle );
1920 board_item->Normalize();
1921 }
1922 }
1923
1924 if( !localCommit.Empty() )
1925 localCommit.Push( _( "Rotate" ) );
1926
1927 if( is_hover && !m_dragging )
1929
1931
1932 if( m_dragging )
1934 }
1935
1936 // Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
1937 // to this now invalid reference
1938 if( oldRefPt )
1939 selection.SetReferencePoint( *oldRefPt );
1940 else
1941 selection.ClearReferencePoint();
1942
1943 return 0;
1944}
1945
1946
1950static VECTOR2I mirrorPointX( const VECTOR2I& aPoint, const VECTOR2I& aMirrorPoint )
1951{
1952 VECTOR2I mirrored = aPoint;
1953
1954 mirrored.x -= aMirrorPoint.x;
1955 mirrored.x = -mirrored.x;
1956 mirrored.x += aMirrorPoint.x;
1957
1958 return mirrored;
1959}
1960
1961
1965static VECTOR2I mirrorPointY( const VECTOR2I& aPoint, const VECTOR2I& aMirrorPoint )
1966{
1967 VECTOR2I mirrored = aPoint;
1968
1969 mirrored.y -= aMirrorPoint.y;
1970 mirrored.y = -mirrored.y;
1971 mirrored.y += aMirrorPoint.y;
1972
1973 return mirrored;
1974}
1975
1976
1980static void mirrorPadX( PAD& aPad, const VECTOR2I& aMirrorPoint )
1981{
1982 if( aPad.GetShape() == PAD_SHAPE::CUSTOM )
1983 aPad.FlipPrimitives( true ); // mirror primitives left to right
1984
1985 VECTOR2I tmpPt = mirrorPointX( aPad.GetPosition(), aMirrorPoint );
1986 aPad.SetPosition( tmpPt );
1987
1988 tmpPt = aPad.GetOffset();
1989 tmpPt.x = -tmpPt.x;
1990 aPad.SetOffset( tmpPt );
1991
1992 auto tmpz = aPad.GetDelta();
1993 tmpz.x = -tmpz.x;
1994 aPad.SetDelta( tmpz );
1995
1996 aPad.SetOrientation( -aPad.GetOrientation() );
1997}
1998
1999
2003static void mirrorPadY( PAD& aPad, const VECTOR2I& aMirrorPoint )
2004{
2005 if( aPad.GetShape() == PAD_SHAPE::CUSTOM )
2006 aPad.FlipPrimitives( false ); // mirror primitives top to bottom
2007
2008 VECTOR2I tmpPt = mirrorPointY( aPad.GetPosition(), aMirrorPoint );
2009 aPad.SetPosition( tmpPt );
2010
2011 tmpPt = aPad.GetOffset();
2012 tmpPt.y = -tmpPt.y;
2013 aPad.SetOffset( tmpPt );
2014
2015 auto tmpz = aPad.GetDelta();
2016 tmpz.y = -tmpz.y;
2017 aPad.SetDelta( tmpz );
2018
2019 aPad.SetOrientation( -aPad.GetOrientation() );
2020}
2021
2022
2023const std::vector<KICAD_T> EDIT_TOOL::MirrorableItems = {
2026 PCB_TEXT_T,
2028 PCB_ZONE_T,
2029 PCB_PAD_T,
2031 PCB_ARC_T,
2032 PCB_VIA_T,
2033};
2034
2035int EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
2036{
2037 if( isRouterActive() )
2038 {
2039 wxBell();
2040 return 0;
2041 }
2042
2043 BOARD_COMMIT localCommit( this );
2044 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
2045
2046 if( !commit )
2047 commit = &localCommit;
2048
2050 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2051 {
2052 sTool->FilterCollectorForMarkers( aCollector );
2053 sTool->FilterCollectorForHierarchy( aCollector, true );
2054 sTool->FilterCollectorForFreePads( aCollector );
2055 },
2056 !m_dragging /* prompt user regarding locked items */ );
2057
2058 if( selection.Empty() )
2059 return 0;
2060
2061 updateModificationPoint( selection );
2062 VECTOR2I mirrorPoint = selection.GetReferencePoint();
2063
2064 // Set the mirroring options.
2065 // Unfortunately, the mirror function do not have the same parameter for all items
2066 // So we need these 2 parameters to avoid mistakes
2067 bool mirrorLeftRight = true;
2068 bool mirrorAroundXaxis = false;
2069
2070 if( aEvent.IsAction( &PCB_ACTIONS::mirrorV ) )
2071 {
2072 mirrorLeftRight = false;
2073 mirrorAroundXaxis = true;
2074 }
2075
2076 std::vector<EDA_ITEM*> items;
2077
2078 for( EDA_ITEM* item : selection )
2079 {
2080 if( item->Type() == PCB_GROUP_T )
2081 {
2082 static_cast<PCB_GROUP*>( item )->RunOnDescendants(
2083 [&]( BOARD_ITEM* descendant )
2084 {
2085 items.push_back( descendant );
2086 } );
2087 }
2088 else
2089 {
2090 items.push_back( item );
2091 }
2092 }
2093
2094 for( EDA_ITEM* item : items )
2095 {
2096 if( !item->IsType( MirrorableItems ) )
2097 continue;
2098
2099 if( !item->IsNew() && !item->IsMoving() )
2100 commit->Modify( item );
2101
2102 // modify each object as necessary
2103 switch( item->Type() )
2104 {
2105 case PCB_SHAPE_T:
2106 static_cast<PCB_SHAPE*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
2107 break;
2108
2109 case PCB_ZONE_T:
2110 static_cast<ZONE*>( item )->Mirror( mirrorPoint, mirrorLeftRight );
2111 break;
2112
2113 case PCB_FIELD_T:
2114 case PCB_TEXT_T:
2115 static_cast<PCB_TEXT*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
2116 break;
2117
2118 case PCB_TEXTBOX_T:
2119 static_cast<PCB_TEXTBOX*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
2120 break;
2121
2122 case PCB_TABLE_T:
2123 // JEY TODO: tables
2124 break;
2125
2126 case PCB_PAD_T:
2127 if( mirrorLeftRight )
2128 mirrorPadX( *static_cast<PAD*>( item ), mirrorPoint );
2129 else
2130 mirrorPadY( *static_cast<PAD*>( item ), mirrorPoint );
2131
2132 break;
2133
2134 case PCB_TRACE_T:
2135 case PCB_ARC_T:
2136 case PCB_VIA_T:
2137 static_cast<PCB_TRACK*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
2138 break;
2139
2140 default:
2141 // it's likely the commit object is wrong if you get here
2142 UNIMPLEMENTED_FOR( item->GetClass() );
2143 }
2144 }
2145
2146 if( !localCommit.Empty() )
2147 localCommit.Push( _( "Mirror" ) );
2148
2149 if( selection.IsHover() && !m_dragging )
2151
2153
2154 if( m_dragging )
2156
2157 return 0;
2158}
2159
2160
2162{
2163 if( isRouterActive() )
2164 {
2165 wxBell();
2166 return 0;
2167 }
2168
2169 BOARD_COMMIT localCommit( this );
2170 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
2171
2172 if( !commit )
2173 commit = &localCommit;
2174
2176 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2177 {
2178 sTool->FilterCollectorForHierarchy( aCollector, true );
2179 },
2180 !m_dragging /* prompt user regarding locked items */ );
2181
2182 if( selection.Empty() )
2183 return 0;
2184
2185 auto setJustify =
2186 [&]( EDA_TEXT* aTextItem )
2187 {
2188 if( aEvent.Matches( ACTIONS::leftJustify.MakeEvent() ) )
2189 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
2190 else if( aEvent.Matches( ACTIONS::centerJustify.MakeEvent() ) )
2191 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
2192 else
2193 aTextItem->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
2194 };
2195
2196 for( EDA_ITEM* item : selection )
2197 {
2198 if( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T )
2199 {
2200 if( !item->IsNew() && !item->IsMoving() )
2201 commit->Modify( item );
2202
2203 setJustify( static_cast<PCB_TEXT*>( item ) );
2204 }
2205 else if( item->Type() == PCB_TEXTBOX_T )
2206 {
2207 if( !item->IsNew() && !item->IsMoving() )
2208 commit->Modify( item );
2209
2210 setJustify( static_cast<PCB_TEXTBOX*>( item ) );
2211 }
2212 }
2213
2214 if( !localCommit.Empty() )
2215 {
2216 if( aEvent.Matches( ACTIONS::leftJustify.MakeEvent() ) )
2217 localCommit.Push( _( "Left Justify" ) );
2218 else if( aEvent.Matches( ACTIONS::centerJustify.MakeEvent() ) )
2219 localCommit.Push( _( "Center Justify" ) );
2220 else
2221 localCommit.Push( _( "Right Justify" ) );
2222 }
2223
2224 if( selection.IsHover() && !m_dragging )
2226
2228
2229 if( m_dragging )
2231
2232 return 0;
2233}
2234
2235
2236int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent )
2237{
2238 if( isRouterActive() )
2239 {
2240 wxBell();
2241 return 0;
2242 }
2243
2244 BOARD_COMMIT localCommit( this );
2245 BOARD_COMMIT* commit = dynamic_cast<BOARD_COMMIT*>( aEvent.Commit() );
2246
2247 if( !commit )
2248 commit = &localCommit;
2249
2251 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2252 {
2253 sTool->FilterCollectorForMarkers( aCollector );
2254 sTool->FilterCollectorForHierarchy( aCollector, true );
2255 sTool->FilterCollectorForFreePads( aCollector );
2256 sTool->FilterCollectorForTableCells( aCollector );
2257 },
2258 !m_dragging /* prompt user regarding locked items */ );
2259
2260 if( selection.Empty() )
2261 return 0;
2262
2263 std::optional<VECTOR2I> oldRefPt;
2264
2265 if( selection.HasReferencePoint() )
2266 oldRefPt = selection.GetReferencePoint();
2267
2268 updateModificationPoint( selection );
2269
2270 // Flip around the anchor for footprints, and the bounding box center for board items
2271 VECTOR2I refPt = IsFootprintEditor() ? VECTOR2I( 0, 0 ) : selection.GetCenter();
2272
2273 // If only one item selected, flip around the selection or item anchor point (instead
2274 // of the bounding box center) to avoid moving the item anchor
2275 if( selection.GetSize() == 1 )
2276 refPt = selection.GetReferencePoint();
2277
2278 bool leftRight = frame()->GetPcbNewSettings()->m_FlipLeftRight;
2279
2280 for( EDA_ITEM* item : selection )
2281 {
2282 if( BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( item ) )
2283 {
2284 if( !boardItem->IsNew() && !boardItem->IsMoving() )
2285 commit->Modify( boardItem );
2286
2287 boardItem->Flip( refPt, leftRight );
2288 boardItem->Normalize();
2289 }
2290 }
2291
2292 if( !localCommit.Empty() )
2293 localCommit.Push( _( "Change Side / Flip" ) );
2294
2295 if( selection.IsHover() && !m_dragging )
2297
2299
2300 if( m_dragging )
2302
2303 // Restore the old reference so any mouse dragging that occurs doesn't make the selection jump
2304 // to this now invalid reference
2305 if( oldRefPt )
2306 selection.SetReferencePoint( *oldRefPt );
2307 else
2308 selection.ClearReferencePoint();
2309
2310 return 0;
2311}
2312
2313
2315 std::unordered_set<BOARD_ITEM*>& children )
2316{
2317 wxASSERT( item->Type() == PCB_GROUP_T || item->Type() == PCB_GENERATOR_T );
2318
2319 std::unordered_set<BOARD_ITEM*>& childItems = static_cast<PCB_GROUP*>( item )->GetItems();
2320
2321 for( BOARD_ITEM* childItem : childItems )
2322 {
2323 children.insert( childItem );
2324
2325 if( childItem->Type() == PCB_GROUP_T || childItem->Type() == PCB_GENERATOR_T )
2326 getChildItemsOfGroupsAndGenerators( childItem, children );
2327 }
2328}
2329
2330
2331void EDIT_TOOL::removeNonRootItems( std::unordered_set<EDA_ITEM*>& items )
2332{
2333 auto itr = items.begin();
2334
2335 while( itr != items.end() )
2336 {
2337 BOARD_ITEM* curItem = dynamic_cast<BOARD_ITEM*>( *itr );
2338
2339 if( curItem )
2340 {
2341 if( curItem->Type() == PCB_GROUP_T || curItem->Type() == PCB_GENERATOR_T )
2342 {
2343 std::unordered_set<BOARD_ITEM*> childItems;
2344 getChildItemsOfGroupsAndGenerators( curItem, childItems );
2345
2346 std::for_each( childItems.begin(), childItems.end(),
2347 [&]( auto eraseItem )
2348 {
2349 items.erase( eraseItem );
2350 } );
2351 }
2352 }
2353
2354 ++itr;
2355 }
2356}
2357
2358
2359void EDIT_TOOL::DeleteItems( const PCB_SELECTION& aItems, bool aIsCut )
2360{
2361 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2362 BOARD_COMMIT commit( this );
2363
2364 // As we are about to remove items, they have to be removed from the selection first
2366
2367 // Only delete items that are the root of a selected set (e.g. only delete grouped / generated
2368 // items from the parent item, not individually) - issue #17527
2369 std::unordered_set<EDA_ITEM*> rootItems( aItems.begin(), aItems.end() );
2370 removeNonRootItems( rootItems );
2371
2372 int itemsDeleted = 0;
2373 int fieldsHidden = 0;
2374 int fieldsAlreadyHidden = 0;
2375
2376 for( EDA_ITEM* item : rootItems )
2377 {
2378 BOARD_ITEM* board_item = dynamic_cast<BOARD_ITEM*>( item );
2379 wxCHECK2( board_item, continue );
2380
2381 FOOTPRINT* parentFP = board_item->GetParentFootprint();
2382
2383 if( board_item->GetParentGroup() )
2384 commit.Stage( board_item, CHT_UNGROUP );
2385
2386 switch( item->Type() )
2387 {
2388 case PCB_FIELD_T:
2389 {
2390 PCB_FIELD* field = static_cast<PCB_FIELD*>( board_item );
2391
2392 wxASSERT( parentFP );
2393 commit.Modify( parentFP );
2394
2395 if( field->IsVisible() )
2396 {
2397 field->SetVisible( false );
2398 fieldsHidden++;
2399 }
2400 else
2401 {
2402 fieldsAlreadyHidden++;
2403 }
2404
2405 getView()->Update( board_item );
2406 break;
2407 }
2408
2409 case PCB_TEXT_T:
2410 case PCB_SHAPE_T:
2411 case PCB_TEXTBOX_T:
2412 case PCB_TABLE_T:
2414 case PCB_DIMENSION_T:
2415 case PCB_DIM_ALIGNED_T:
2416 case PCB_DIM_LEADER_T:
2417 case PCB_DIM_CENTER_T:
2418 case PCB_DIM_RADIAL_T:
2420 commit.Remove( board_item );
2421 itemsDeleted++;
2422 break;
2423
2424 case PCB_TABLECELL_T:
2425 // Clear contents of table cell
2426 commit.Modify( board_item );
2427 static_cast<PCB_TABLECELL*>( board_item )->SetText( wxEmptyString );
2428 itemsDeleted++;
2429 break;
2430
2431 case PCB_GROUP_T:
2432 board_item->RunOnDescendants(
2433 [&commit]( BOARD_ITEM* aItem )
2434 {
2435 commit.Stage( aItem, CHT_UNGROUP );
2436 } );
2437
2438 board_item->RunOnDescendants(
2439 [&commit]( BOARD_ITEM* aItem )
2440 {
2441 commit.Remove( aItem );
2442 } );
2443
2444 commit.Remove( board_item );
2445 itemsDeleted++;
2446 break;
2447
2448 case PCB_PAD_T:
2450 {
2451 commit.Modify( parentFP );
2452 getView()->Remove( board_item );
2453 parentFP->Remove( board_item );
2454 itemsDeleted++;
2455 }
2456
2457 break;
2458
2459 case PCB_ZONE_T:
2460 // We process the zones special so that cutouts can be deleted when the delete
2461 // tool is called from inside a cutout when the zone is selected.
2462 // Only interact with cutouts when deleting and a single item is selected
2463 if( !aIsCut && aItems.GetSize() == 1 )
2464 {
2466 ZONE* zone = static_cast<ZONE*>( board_item );
2467
2468 int outlineIdx, holeIdx;
2469
2470 if( zone->HitTestCutout( curPos, &outlineIdx, &holeIdx ) )
2471 {
2472 // Remove the cutout
2473 commit.Modify( zone );
2474 zone->RemoveCutout( outlineIdx, holeIdx );
2475 zone->UnFill();
2476
2477 // TODO Refill zone when KiCad supports auto re-fill
2478
2479 // Update the display
2480 zone->HatchBorder();
2481 canvas()->Refresh();
2482
2483 // Restore the selection on the original zone
2485
2486 break;
2487 }
2488 }
2489
2490 // Remove the entire zone otherwise
2491 commit.Remove( board_item );
2492 itemsDeleted++;
2493 break;
2494
2495 case PCB_GENERATOR_T:
2496 if( rootItems.size() == 1 )
2497 {
2498 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( board_item );
2499
2501 generator );
2502 }
2503 else
2504 {
2505 PCB_GENERATOR* generator = static_cast<PCB_GENERATOR*>( board_item );
2506
2507 for( BOARD_ITEM* member : generator->GetItems() )
2508 commit.Stage( member, CHT_UNGROUP );
2509
2510 for( BOARD_ITEM* member : generator->GetItems() )
2511 commit.Remove( member );
2512
2513 commit.Remove( board_item );
2514 }
2515
2516 itemsDeleted++;
2517 break;
2518
2519 default:
2520 wxASSERT_MSG( parentFP == nullptr, wxT( "Try to delete an item living in a footprint" ) );
2521 commit.Remove( board_item );
2522 itemsDeleted++;
2523 break;
2524 }
2525 }
2526
2527 // If the entered group has been emptied then leave it.
2528 PCB_GROUP* enteredGroup = m_selectionTool->GetEnteredGroup();
2529
2530 if( enteredGroup && enteredGroup->GetItems().empty() )
2532
2533 if( aIsCut )
2534 {
2535 commit.Push( _( "Cut" ) );
2536 }
2537 else if( itemsDeleted == 0 )
2538 {
2539 if( fieldsHidden == 1 )
2540 commit.Push( _( "Hide Field" ) );
2541 else if( fieldsHidden > 1 )
2542 commit.Push( _( "Hide Fields" ) );
2543 else if( fieldsAlreadyHidden > 0 )
2544 editFrame->ShowInfoBarError( _( "Use the Footprint Properties dialog to remove fields." ) );
2545 }
2546 else
2547 {
2548 commit.Push( _( "Delete" ) );
2549 }
2550}
2551
2552
2553int EDIT_TOOL::Remove( const TOOL_EVENT& aEvent )
2554{
2555 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2556
2557 editFrame->PushTool( aEvent );
2558
2559 std::vector<BOARD_ITEM*> lockedItems;
2560 Activate();
2561
2562 // get a copy instead of reference (as we're going to clear the selection before removing items)
2563 PCB_SELECTION selectionCopy;
2566
2567 // If we are in a "Cut" operation, then the copied selection exists already and we want to
2568 // delete exactly that; no more, no fewer. Any filtering for locked items must be done in
2569 // the copyToClipboard() routine.
2570 if( isCut )
2571 {
2572 selectionCopy = m_selectionTool->GetSelection();
2573 }
2574 else
2575 {
2576 // When not in free-pad mode we normally auto-promote selected pads to their parent
2577 // footprints. But this is probably a little too dangerous for a destructive operation,
2578 // so we just do the promotion but not the deletion (allowing for a second delete to do
2579 // it if that's what the user wanted).
2580 selectionCopy = m_selectionTool->RequestSelection(
2581 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2582 {
2583 } );
2584
2585 size_t beforeFPCount = selectionCopy.CountType( PCB_FOOTPRINT_T );
2586
2588 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2589 {
2590 sTool->FilterCollectorForFreePads( aCollector );
2591 } );
2592
2593 if( !selectionCopy.IsHover()
2594 && m_selectionTool->GetSelection().CountType( PCB_FOOTPRINT_T ) > beforeFPCount )
2595 {
2596 wxBell();
2597 canvas()->Refresh();
2598 editFrame->PopTool( aEvent );
2599 return 0;
2600 }
2601
2602 // In "alternative" mode, we expand selected track items to their full connection.
2603 if( isAlt && ( selectionCopy.HasType( PCB_TRACE_T ) || selectionCopy.HasType( PCB_VIA_T ) ) )
2604 {
2606 }
2607
2608 // Finally run RequestSelection() one more time to find out what user wants to do about
2609 // locked objects.
2610 selectionCopy = m_selectionTool->RequestSelection(
2611 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2612 {
2613 sTool->FilterCollectorForFreePads( aCollector );
2614 },
2615 true /* prompt user regarding locked items */ );
2616 }
2617
2618 DeleteItems( selectionCopy, isCut );
2619 canvas()->Refresh();
2620
2621 editFrame->PopTool( aEvent );
2622 return 0;
2623}
2624
2625
2627{
2628 if( isRouterActive() )
2629 {
2630 wxBell();
2631 return 0;
2632 }
2633
2635 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2636 {
2637 sTool->FilterCollectorForMarkers( aCollector );
2638 sTool->FilterCollectorForHierarchy( aCollector, true );
2639 sTool->FilterCollectorForTableCells( aCollector );
2640 },
2641 true /* prompt user regarding locked items */ );
2642
2643 if( selection.Empty() )
2644 return 0;
2645
2646 VECTOR2I translation;
2647 EDA_ANGLE rotation;
2648 ROTATION_ANCHOR rotationAnchor = selection.Size() > 1 ? ROTATE_AROUND_SEL_CENTER
2650
2651 // TODO: Implement a visible bounding border at the edge
2652 BOX2I sel_box = selection.GetBoundingBox();
2653
2654 DIALOG_MOVE_EXACT dialog( frame(), translation, rotation, rotationAnchor, sel_box );
2655 int ret = dialog.ShowModal();
2656
2657 if( ret == wxID_OK )
2658 {
2659 BOARD_COMMIT commit( this );
2660 EDA_ANGLE angle = rotation;
2661 VECTOR2I rp = selection.GetCenter();
2662 VECTOR2I selCenter( rp.x, rp.y );
2663
2664 // Make sure the rotation is from the right reference point
2665 selCenter += translation;
2666
2667 if( !frame()->GetPcbNewSettings()->m_Display.m_DisplayInvertYAxis )
2668 rotation = -rotation;
2669
2670 for( EDA_ITEM* item : selection )
2671 {
2672 BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( item );
2673
2674 wxCHECK2( boardItem, continue );
2675
2676 if( !boardItem->IsNew() )
2677 commit.Modify( boardItem );
2678
2679 if( !boardItem->GetParent() || !boardItem->GetParent()->IsSelected() )
2680 boardItem->Move( translation );
2681
2682 switch( rotationAnchor )
2683 {
2685 boardItem->Rotate( boardItem->GetPosition(), angle );
2686 break;
2688 boardItem->Rotate( selCenter, angle );
2689 break;
2691 boardItem->Rotate( frame()->GetScreen()->m_LocalOrigin, angle );
2692 break;
2694 boardItem->Rotate( board()->GetDesignSettings().GetAuxOrigin(), angle );
2695 break;
2696 }
2697
2698 if( !m_dragging )
2699 getView()->Update( boardItem );
2700 }
2701
2702 commit.Push( _( "Move Exactly" ) );
2703
2704 if( selection.IsHover() )
2706
2708
2709 if( m_dragging )
2711 }
2712
2713 return 0;
2714}
2715
2716
2718{
2719 if( isRouterActive() )
2720 {
2721 wxBell();
2722 return 0;
2723 }
2724
2725 bool increment = aEvent.IsAction( &PCB_ACTIONS::duplicateIncrement );
2726
2727 // Be sure that there is at least one item that we can modify
2729 []( const VECTOR2I&, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2730 {
2731 sTool->FilterCollectorForFreePads( aCollector, true );
2732 sTool->FilterCollectorForMarkers( aCollector );
2733 sTool->FilterCollectorForHierarchy( aCollector, true );
2734 sTool->FilterCollectorForTableCells( aCollector );
2735 } );
2736
2737 if( selection.Empty() )
2738 return 0;
2739
2740 // Duplicating tuhing patterns alone is not supported
2741 if( selection.Size() == 1 && selection.CountType( PCB_GENERATOR_T ) )
2742 return 0;
2743
2744 // we have a selection to work on now, so start the tool process
2745 PCB_BASE_EDIT_FRAME* editFrame = getEditFrame<PCB_BASE_EDIT_FRAME>();
2746 BOARD_COMMIT commit( this );
2747
2748 // If the selection was given a hover, we do not keep the selection after completion
2749 bool is_hover = selection.IsHover();
2750
2751 std::vector<BOARD_ITEM*> new_items;
2752 new_items.reserve( selection.Size() );
2753
2754 // Each selected item is duplicated and pushed to new_items list
2755 // Old selection is cleared, and new items are then selected.
2756 for( EDA_ITEM* item : selection )
2757 {
2758 BOARD_ITEM* dupe_item = nullptr;
2759 BOARD_ITEM* orig_item = dynamic_cast<BOARD_ITEM*>( item );
2760
2761 wxCHECK2( orig_item, continue );
2762
2764 {
2765 FOOTPRINT* parentFootprint = editFrame->GetBoard()->GetFirstFootprint();
2766
2767 // PCB_FIELD items are specific items (not only graphic, but are properies )
2768 // and cannot be duplicated like other footprint items. So skip it:
2769 if( orig_item->Type() == PCB_FIELD_T )
2770 {
2771 orig_item->ClearSelected();
2772 continue;
2773 }
2774
2775 dupe_item = parentFootprint->DuplicateItem( orig_item );
2776
2777 if( increment && dupe_item->Type() == PCB_PAD_T
2778 && static_cast<PAD*>( dupe_item )->CanHaveNumber() )
2779 {
2780 PAD_TOOL* padTool = m_toolMgr->GetTool<PAD_TOOL>();
2781 wxString padNumber = padTool->GetLastPadNumber();
2782 padNumber = parentFootprint->GetNextPadNumber( padNumber );
2783 padTool->SetLastPadNumber( padNumber );
2784 static_cast<PAD*>( dupe_item )->SetNumber( padNumber );
2785 }
2786
2787 dupe_item->ClearSelected();
2788 new_items.push_back( dupe_item );
2789 commit.Add( dupe_item );
2790 }
2791 else if( /*FOOTPRINT* parentFootprint =*/ orig_item->GetParentFootprint() )
2792 {
2793 // No sub-footprint modifications allowed outside of footprint editor
2794 // and parentFootprint is not (yet?) used
2795 }
2796 else
2797 {
2798 switch( orig_item->Type() )
2799 {
2800 case PCB_FOOTPRINT_T:
2801 case PCB_TEXT_T:
2802 case PCB_TEXTBOX_T:
2804 case PCB_SHAPE_T:
2805 case PCB_TRACE_T:
2806 case PCB_ARC_T:
2807 case PCB_VIA_T:
2808 case PCB_ZONE_T:
2809 case PCB_TARGET_T:
2810 case PCB_DIM_ALIGNED_T:
2811 case PCB_DIM_CENTER_T:
2812 case PCB_DIM_RADIAL_T:
2814 case PCB_DIM_LEADER_T:
2815 dupe_item = orig_item->Duplicate();
2816
2817 // Clear the selection flag here, otherwise the PCB_SELECTION_TOOL
2818 // will not properly select it later on
2819 dupe_item->ClearSelected();
2820
2821 new_items.push_back( dupe_item );
2822 commit.Add( dupe_item );
2823 break;
2824
2825 case PCB_TABLE_T:
2826 // JEY TODO: tables
2827 break;
2828
2829 case PCB_GENERATOR_T:
2830 case PCB_GROUP_T:
2831 dupe_item = static_cast<PCB_GROUP*>( orig_item )->DeepDuplicate();
2832
2833 dupe_item->RunOnDescendants(
2834 [&]( BOARD_ITEM* aItem )
2835 {
2836 aItem->ClearSelected();
2837 new_items.push_back( aItem );
2838 commit.Add( aItem );
2839 } );
2840
2841 dupe_item->ClearSelected();
2842 new_items.push_back( dupe_item );
2843 commit.Add( dupe_item );
2844 break;
2845
2846 default:
2847 wxASSERT_MSG( false, wxString::Format( wxT( "Unhandled item type %d" ),
2848 orig_item->Type() ) );
2849 break;
2850 }
2851 }
2852 }
2853
2854 // Clear the old selection first
2856
2857 // Select the new items
2858 EDA_ITEMS nItems( new_items.begin(), new_items.end() );
2860
2861 // record the new items as added
2862 if( !selection.Empty() )
2863 {
2864 editFrame->DisplayToolMsg( wxString::Format( _( "Duplicated %d item(s)" ),
2865 (int) new_items.size() ) );
2866
2867 // If items were duplicated, pick them up
2868 if( doMoveSelection( aEvent, &commit, true ) )
2869 commit.Push( _( "Duplicate" ) );
2870 else
2871 commit.Revert();
2872
2873 // Deselect the duplicated item if we originally started as a hover selection
2874 if( is_hover )
2876 }
2877
2878 return 0;
2879}
2880
2881
2883{
2884 if( isRouterActive() )
2885 {
2886 wxBell();
2887 return 0;
2888 }
2889
2890 // Be sure that there is at least one item that we can modify
2892 []( const VECTOR2I&, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
2893 {
2894 sTool->FilterCollectorForMarkers( aCollector );
2895 sTool->FilterCollectorForHierarchy( aCollector, true );
2896 } );
2897
2898 if( selection.Empty() )
2899 return 0;
2900
2901 // we have a selection to work on now, so start the tool process
2902 PCB_BASE_FRAME* editFrame = getEditFrame<PCB_BASE_FRAME>();
2903 ARRAY_CREATOR array_creator( *editFrame, m_isFootprintEditor, selection, m_toolMgr );
2904 array_creator.Invoke();
2905
2906 return 0;
2907}
2908
2909
2911 PCB_SELECTION_TOOL* sTool )
2912{
2913 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
2914 {
2915 if( aCollector[i]->Type() != PCB_PAD_T )
2916 aCollector.Remove( i );
2917 }
2918}
2919
2920
2922 PCB_SELECTION_TOOL* sTool )
2923{
2924 for( int i = aCollector.GetCount() - 1; i >= 0; i-- )
2925 {
2926 if( aCollector[i]->Type() != PCB_FOOTPRINT_T )
2927 aCollector.Remove( i );
2928 }
2929}
2930
2931
2933{
2934 // Can't modify an empty group
2935 if( aSelection.Empty() )
2936 return false;
2937
2938 if( ( m_dragging || aSelection[0]->IsMoving() ) && aSelection.HasReferencePoint() )
2939 return false;
2940
2941 // When there is only one item selected, the reference point is its position...
2942 if( aSelection.Size() == 1 && aSelection.Front()->Type() != PCB_TABLE_T )
2943 {
2944 if( BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( aSelection.Front() ) )
2945 aSelection.SetReferencePoint( item->GetPosition() );
2946 }
2947 // ...otherwise modify items with regard to the grid-snapped center position
2948 else
2949 {
2950 PCB_GRID_HELPER grid( m_toolMgr, frame()->GetMagneticItemsSettings() );
2951 VECTOR2I refPt = aSelection.GetCenter();
2952
2953 // Exclude text in the footprint editor if there's anything else selected
2955 {
2956 BOX2I nonFieldsBBox;
2957
2958 for( EDA_ITEM* item : aSelection.Items() )
2959 {
2960 if( !item->IsType( { PCB_TEXT_T, PCB_FIELD_T } ) )
2961 nonFieldsBBox.Merge( item->GetBoundingBox() );
2962 }
2963
2964 if( nonFieldsBBox.IsValid() )
2965 refPt = nonFieldsBBox.GetCenter();
2966 }
2967
2968 aSelection.SetReferencePoint( grid.BestSnapAnchor( refPt, nullptr ) );
2969 }
2970
2971 return true;
2972}
2973
2974
2975bool EDIT_TOOL::pickReferencePoint( const wxString& aTooltip, const wxString& aSuccessMessage,
2976 const wxString& aCanceledMessage, VECTOR2I& aReferencePoint )
2977{
2979 std::optional<VECTOR2I> pickedPoint;
2980 bool done = false;
2981
2982 m_statusPopup->SetText( aTooltip );
2983
2985 picker->SetSnapping( true );
2986
2987 picker->SetClickHandler(
2988 [&]( const VECTOR2D& aPoint ) -> bool
2989 {
2990 pickedPoint = aPoint;
2991
2992 if( !aSuccessMessage.empty() )
2993 {
2994 m_statusPopup->SetText( aSuccessMessage );
2995 m_statusPopup->Expire( 800 );
2996 }
2997 else
2998 {
2999 m_statusPopup->Hide();
3000 }
3001
3002 return false; // we don't need any more points
3003 } );
3004
3005 picker->SetMotionHandler(
3006 [&]( const VECTOR2D& aPos )
3007 {
3008 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
3009 } );
3010
3011 picker->SetCancelHandler(
3012 [&]()
3013 {
3014 if( !aCanceledMessage.empty() )
3015 {
3016 m_statusPopup->SetText( aCanceledMessage );
3017 m_statusPopup->Expire( 800 );
3018 }
3019 else
3020 {
3021 m_statusPopup->Hide();
3022 }
3023 } );
3024
3025 picker->SetFinalizeHandler(
3026 [&]( const int& aFinalState )
3027 {
3028 done = true;
3029 } );
3030
3031 m_statusPopup->Move( KIPLATFORM::UI::GetMousePosition() + wxPoint( 20, -50 ) );
3032 m_statusPopup->Popup();
3033 canvas()->SetStatusPopup( m_statusPopup->GetPanel() );
3034
3036
3037 while( !done )
3038 {
3039 // Pass events unless we receive a null event, then we must shut down
3040 if( TOOL_EVENT* evt = Wait() )
3041 evt->SetPassEvent();
3042 else
3043 break;
3044 }
3045
3046 // Ensure statusPopup is hidden after use and before deleting it:
3047 canvas()->SetStatusPopup( nullptr );
3048 m_statusPopup->Hide();
3049
3050 if( pickedPoint )
3051 aReferencePoint = *pickedPoint;
3052
3053 return pickedPoint.has_value();
3054}
3055
3056
3058{
3059 CLIPBOARD_IO io;
3061 getEditFrame<PCB_BASE_EDIT_FRAME>()->GetMagneticItemsSettings() );
3062 TOOL_EVENT selectReferencePoint( aEvent.Category(), aEvent.Action(),
3063 "pcbnew.InteractiveEdit.selectReferencePoint",
3064 TOOL_ACTION_SCOPE::AS_GLOBAL );
3065
3066 frame()->PushTool( selectReferencePoint );
3067 Activate();
3068
3070 []( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
3071 {
3072 for( int i = aCollector.GetCount() - 1; i >= 0; --i )
3073 {
3074 BOARD_ITEM* item = aCollector[i];
3075
3076 // We can't copy both a footprint and its text in the same operation, so if
3077 // both are selected, remove the text
3078 if( ( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T )
3079 && aCollector.HasItem( item->GetParentFootprint() ) )
3080 {
3081 aCollector.Remove( item );
3082 }
3083 else if( item->Type() == PCB_MARKER_T )
3084 {
3085 // Don't allow copying marker objects
3086 aCollector.Remove( item );
3087 }
3088 }
3089 },
3090
3091 // Prompt user regarding locked items.
3092 aEvent.IsAction( &ACTIONS::cut ) && !m_isFootprintEditor );
3093
3094 if( !selection.Empty() )
3095 {
3096 std::vector<BOARD_ITEM*> items;
3097
3098 for( EDA_ITEM* item : selection )
3099 {
3100 if( BOARD_ITEM* boardItem = dynamic_cast<BOARD_ITEM*>( item ) )
3101 items.push_back( boardItem );
3102 }
3103
3104 VECTOR2I refPoint;
3105
3106 if( aEvent.IsAction( &PCB_ACTIONS::copyWithReference ) )
3107 {
3108 if( !pickReferencePoint( _( "Select reference point for the copy..." ),
3109 _( "Selection copied" ),
3110 _( "Copy canceled" ),
3111 refPoint ) )
3112 {
3113 frame()->PopTool( selectReferencePoint );
3114 return 0;
3115 }
3116 }
3117 else
3118 {
3119 refPoint = grid.BestDragOrigin( getViewControls()->GetCursorPosition(), items );
3120 }
3121
3122 selection.SetReferencePoint( refPoint );
3123
3124 io.SetBoard( board() );
3125 io.SaveSelection( selection, m_isFootprintEditor );
3126 frame()->SetStatusText( _( "Selection copied" ) );
3127 }
3128
3129 frame()->PopTool( selectReferencePoint );
3130
3131 if( selection.IsHover() )
3132 m_selectionTool->ClearSelection();
3133
3134 return 0;
3135}
3136
3137
3139{
3140 if( !copyToClipboard( aEvent ) )
3141 {
3142 // N.B. Setting the CUT flag prevents lock filtering as we only want to delete the items
3143 // that were copied to the clipboard, no more, no fewer. Filtering for locked item, if
3144 // any will be done in the copyToClipboard() routine
3145 TOOL_EVENT evt = aEvent;
3147 Remove( evt );
3148 }
3149
3150 return 0;
3151}
3152
3153
3155{
3159}
3160
3161
3163{
3165 Go( &EDIT_TOOL::Move, PCB_ACTIONS::move.MakeEvent() );
3171 Go( &EDIT_TOOL::Flip, PCB_ACTIONS::flip.MakeEvent() );
3172 Go( &EDIT_TOOL::Remove, ACTIONS::doDelete.MakeEvent() );
3180 Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorH.MakeEvent() );
3181 Go( &EDIT_TOOL::Mirror, PCB_ACTIONS::mirrorV.MakeEvent() );
3182 Go( &EDIT_TOOL::Swap, PCB_ACTIONS::swap.MakeEvent() );
3197
3200 Go( &EDIT_TOOL::cutToClipboard, ACTIONS::cut.MakeEvent() );
3201}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
@ special_tools
static TOOL_ACTION paste
Definition: actions.h:72
static TOOL_ACTION pickerSubTool
Definition: actions.h:195
static TOOL_ACTION unselectAll
Definition: actions.h:75
static TOOL_ACTION copy
Definition: actions.h:71
static TOOL_ACTION pasteSpecial
Definition: actions.h:73
static TOOL_ACTION rightJustify
Definition: actions.h:81
static TOOL_ACTION pageSettings
Definition: actions.h:56
static TOOL_ACTION undo
Definition: actions.h:68
static TOOL_ACTION duplicate
Definition: actions.h:76
static TOOL_ACTION doDelete
Definition: actions.h:77
static TOOL_ACTION cursorClick
Definition: actions.h:159
REMOVE_FLAGS
Definition: actions.h:255
static TOOL_ACTION leftJustify
Definition: actions.h:79
static TOOL_ACTION cut
Definition: actions.h:70
static TOOL_ACTION selectAll
Definition: actions.h:74
static TOOL_ACTION centerJustify
Definition: actions.h:80
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Revert the commit by restoring the modified items state.
COMMIT & Stage(EDA_ITEM *aItem, CHANGE_TYPE aChangeType, BASE_SCREEN *aScreen=nullptr) override
virtual void Revert() override
NETINFO_ITEM * GetNet() const
Return #NET_INFO object for a given item.
void SetNet(NETINFO_ITEM *aNetInfo)
Set a NET_INFO object for the item.
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:79
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:240
virtual void SetLocked(bool aLocked)
Definition: board_item.h:316
PCB_GROUP * GetParentGroup() const
Definition: board_item.h:93
virtual void Rotate(const VECTOR2I &aRotCentre, const EDA_ANGLE &aAngle)
Rotate this object.
Definition: board_item.cpp:321
virtual BOARD_ITEM * Duplicate() const
Create a copy of this BOARD_ITEM.
Definition: board_item.cpp:209
virtual void Move(const VECTOR2I &aMoveVector)
Move this object.
Definition: board_item.h:330
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:276
FOOTPRINT * GetParentFootprint() const
Definition: board_item.cpp:264
virtual bool IsLocked() const
Definition: board_item.cpp:75
BOARD_ITEM_CONTAINER * GetParent() const
Definition: board_item.h:218
virtual void RunOnDescendants(const std::function< void(BOARD_ITEM *)> &aFunction, int aDepth=0) const
Invoke a function on all descendants.
Definition: board_item.h:215
LSET GetVisibleLayers() const
A proxy function that calls the correspondent function in m_BoardSettings.
Definition: board.cpp:771
bool BuildConnectivity(PROGRESS_REPORTER *aReporter=nullptr)
Build or rebuild the board connectivity database for the board, especially the list of connected item...
Definition: board.cpp:186
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:447
bool IsEmpty() const
Definition: board.h:385
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:874
std::shared_ptr< CONNECTIVITY_DATA > GetConnectivity() const
Return a list of missing connections between components/tracks.
Definition: board.h:474
const Vec & GetPosition() const
Definition: box2.h:201
const Vec GetCenter() const
Definition: box2.h:220
const Vec GetEnd() const
Definition: box2.h:202
bool IsValid() const
Definition: box2.h:864
BOX2< Vec > & Merge(const BOX2< Vec > &aRect)
Modify the position and size of the rectangle in order to contain aRect.
Definition: box2.h:623
Represent basic circle geometry with utility geometry functions.
Definition: circle.h:33
VECTOR2I Center
Public to make access simpler.
Definition: circle.h:128
int Radius
Public to make access simpler.
Definition: circle.h:127
CIRCLE & ConstructFromTanTanPt(const SEG &aLineA, const SEG &aLineB, const VECTOR2I &aP)
Construct this circle such that it is tangent to the given segments and passes through the given poin...
Definition: circle.cpp:51
VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute the point on the circumference of the circle that is the closest to aP.
Definition: circle.cpp:197
int GetCount() const
Return the number of objects in the list.
Definition: collector.h:81
void Remove(int aIndex)
Remove the item at aIndex (first position is 0).
Definition: collector.h:109
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been removed.
Definition: commit.h:92
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Create an undo entry for an item that has been already modified.
Definition: commit.h:105
bool Empty() const
Returns status of an item.
Definition: commit.h:144
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Notify observers that aItem has been added.
Definition: commit.h:80
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
void AddSeparator(int aOrder=ANY_ORDER)
Add a separator to the menu.
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
int ShowQuasiModal()
bool HitTestDrawingSheetItems(KIGFX::VIEW *aView, const VECTOR2I &aPosition)
void ShowInfoBarMsg(const wxString &aMsg, bool aShowCloseButton=false)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an info icon on the left of...
void ShowInfoBarError(const wxString &aErrorMsg, bool aShowCloseButton=false, WX_INFOBAR::MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an error icon on the left o...
void DisplayToolMsg(const wxString &msg) override
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
void SetStatusPopup(wxWindow *aPopup)
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:243
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:127
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:101
void ClearSelected()
Definition: eda_item.h:122
bool IsSelected() const
Definition: eda_item.h:110
virtual const BOX2I ViewBBox() const override
Return the bounding box of the item covering all its layers.
Definition: eda_item.cpp:273
bool IsNew() const
Definition: eda_item.h:107
SHAPE_POLY_SET & GetPolyShape()
Definition: eda_shape.h:279
SHAPE_T GetShape() const
Definition: eda_shape.h:125
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:167
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:134
const VECTOR2I & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:130
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:171
void SetWidth(int aWidth)
Definition: eda_shape.h:114
A mix-in class (via multiple inheritance) that handles texts such as labels, parts,...
Definition: eda_text.h:79
virtual bool IsVisible() const
Definition: eda_text.h:147
virtual void SetVisible(bool aVisible)
Definition: eda_text.cpp:244
bool isRouterActive() const
Definition: edit_tool.cpp:448
int Duplicate(const TOOL_EVENT &aItem)
Duplicate the current selection and starts a move action.
Definition: edit_tool.cpp:2717
int Drag(const TOOL_EVENT &aEvent)
Invoke the PNS router to drag tracks or do an offline resizing of an arc track if a single arc track ...
Definition: edit_tool.cpp:456
int Flip(const TOOL_EVENT &aEvent)
Rotate currently selected items.
Definition: edit_tool.cpp:2236
bool doMoveSelection(const TOOL_EVENT &aEvent, BOARD_COMMIT *aCommit, bool aAutoStart)
Rebuilds the ratsnest for operations that require it outside the commit rebuild.
int Swap(const TOOL_EVENT &aEvent)
Swap currently selected items' positions.
int PackAndMoveFootprints(const TOOL_EVENT &aEvent)
Try to fit selected footprints inside a minimal area and start movement.
int Mirror(const TOOL_EVENT &aEvent)
Mirror the current selection.
Definition: edit_tool.cpp:2035
int CreateArray(const TOOL_EVENT &aEvent)
Create an array of the selected items, invoking the array editor dialog to set the options.
Definition: edit_tool.cpp:2882
bool pickReferencePoint(const wxString &aTooltip, const wxString &aSuccessMessage, const wxString &aCanceledMessage, VECTOR2I &aReferencePoint)
Definition: edit_tool.cpp:2975
bool Init() override
Init() is called once upon a registration of the tool.
Definition: edit_tool.cpp:204
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
Definition: edit_tool.cpp:112
int ModifyLines(const TOOL_EVENT &aEvent)
"Modify" graphical lines.
Definition: edit_tool.cpp:1239
bool m_dragging
Definition: edit_tool.h:230
int MoveExact(const TOOL_EVENT &aEvent)
Invoke a dialog box to allow moving of the item by an exact amount.
Definition: edit_tool.cpp:2626
int Move(const TOOL_EVENT &aEvent)
Main loop in which events are handled.
static const unsigned int COORDS_PADDING
Definition: edit_tool.h:235
int JustifyText(const TOOL_EVENT &aEvent)
Set the justification on any text items (or fields) in the current selection.
Definition: edit_tool.cpp:2161
void getChildItemsOfGroupsAndGenerators(EDA_ITEM *item, std::unordered_set< BOARD_ITEM * > &children)
Definition: edit_tool.cpp:2314
bool updateModificationPoint(PCB_SELECTION &aSelection)
Definition: edit_tool.cpp:2932
std::unique_ptr< STATUS_TEXT_POPUP > m_statusPopup
Definition: edit_tool.h:233
int DragArcTrack(const TOOL_EVENT &aTrack)
Drag-resize an arc (and change end points of connected straight segments).
Definition: edit_tool.cpp:567
int copyToClipboard(const TOOL_EVENT &aEvent)
Send the current selection to the clipboard by formatting it as a fake pcb see #AppendBoardFromClipbo...
Definition: edit_tool.cpp:3057
int Remove(const TOOL_EVENT &aEvent)
Delete currently selected items.
Definition: edit_tool.cpp:2553
int cutToClipboard(const TOOL_EVENT &aEvent)
Cut the current selection to the clipboard by formatting it as a fake pcb see #AppendBoardFromClipboa...
Definition: edit_tool.cpp:3138
static const std::vector< KICAD_T > MirrorableItems
Definition: edit_tool.h:105
void DeleteItems(const PCB_SELECTION &aItem, bool aIsCut)
Definition: edit_tool.cpp:2359
static void PadFilter(const VECTOR2I &, GENERAL_COLLECTOR &aCollector, PCB_SELECTION_TOOL *sTool)
A selection filter which prunes the selection to contain only items of type PCB_PAD_T.
Definition: edit_tool.cpp:2910
VECTOR2I m_cursor
Definition: edit_tool.h:231
bool invokeInlineRouter(int aDragMode)
Definition: edit_tool.cpp:419
void removeNonRootItems(std::unordered_set< EDA_ITEM * > &items)
Recursively adds any child items of the given item to the set.
Definition: edit_tool.cpp:2331
void rebuildConnectivity()
Removes all items from the set which are children of other PCB_GROUP or PCB_GENERATOR items in the se...
Definition: edit_tool.cpp:3154
void setTransitions() override
< Set up handlers for various events.
Definition: edit_tool.cpp:3162
int HealShapes(const TOOL_EVENT &aEvent)
Make ends of selected shapes meet by extending or cutting them, or adding extra geometry.
Definition: edit_tool.cpp:1547
int ChangeTrackWidth(const TOOL_EVENT &aEvent)
Definition: edit_tool.cpp:918
int BooleanPolygons(const TOOL_EVENT &aEvent)
Modify selected polygons into a single polygon using boolean operations such as merge (union) or subt...
Definition: edit_tool.cpp:1626
int GetAndPlace(const TOOL_EVENT &aEvent)
Definition: edit_tool.cpp:396
int FilletTracks(const TOOL_EVENT &aEvent)
Fillet (i.e.
Definition: edit_tool.cpp:990
PCB_SELECTION_TOOL * m_selectionTool
Definition: edit_tool.h:229
int SimplifyPolygons(const TOOL_EVENT &aEvent)
Simplify the outlines of selected polygon objects.
Definition: edit_tool.cpp:1477
int Properties(const TOOL_EVENT &aEvent)
Display properties window for the selected object.
Definition: edit_tool.cpp:1734
static void FootprintFilter(const VECTOR2I &, GENERAL_COLLECTOR &aCollector, PCB_SELECTION_TOOL *sTool)
A selection filter which prunes the selection to contain only items of type #PCB_MODULE_T.
Definition: edit_tool.cpp:2921
int Rotate(const TOOL_EVENT &aEvent)
Rotate currently selected items.
Definition: edit_tool.cpp:1815
static const TOOL_EVENT SelectedEvent
Definition: actions.h:270
static const TOOL_EVENT SelectedItemsModified
Selected items were moved, this can be very high frequency on the canvas, use with care.
Definition: actions.h:277
static const TOOL_EVENT ConnectivityChangedEvent
Selected item had a property changed (except movement)
Definition: actions.h:274
static const TOOL_EVENT UnselectedEvent
Definition: actions.h:271
void Remove(BOARD_ITEM *aItem, REMOVE_MODE aMode=REMOVE_MODE::NORMAL) override
Removes an item from the container.
Definition: footprint.cpp:1041
BOARD_ITEM * DuplicateItem(const BOARD_ITEM *aItem, bool aAddToFootprint=false)
Duplicate a given item within the footprint, optionally adding it to the board.
Definition: footprint.cpp:2455
wxString GetNextPadNumber(const wxString &aLastPadName) const
Return the next available pad number in the footprint.
Definition: footprint.cpp:2584
Used when the right click button is pressed, or when the select tool is in effect.
Definition: collectors.h:207
static const std::vector< KICAD_T > DraggableItems
A scan list for items that can be dragged.
Definition: collectors.h:268
A handler that is based on a set of callbacks provided by the user of the ITEM_MODIFICATION_ROUTINE.
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 VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const =0
Return the current mouse pointer position.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:317
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:357
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: view.cpp:1687
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition: view.h:418
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:35
A collection of nets and the parameters used to route or test these nets.
Definition: netclass.h:44
int GetuViaDrill() const
Definition: netclass.h:89
int GetuViaDiameter() const
Definition: netclass.h:85
Tool relating to pads and pad settings.
Definition: pad_tool.h:37
void SetLastPadNumber(const wxString &aPadNumber)
Definition: pad_tool.h:69
wxString GetLastPadNumber() const
Definition: pad_tool.h:68
Definition: pad.h:54
bool CanHaveNumber() const
Indicates whether or not the pad can have a number.
Definition: pad.cpp:233
void FlipPrimitives(bool aFlipLeftRight)
Flip (mirror) the primitives left to right or top to bottom, around the anchor position in custom pad...
Definition: pad.cpp:887
VECTOR2I GetPosition() const override
Definition: pad.h:201
void SetOffset(const VECTOR2I &aOffset)
Definition: pad.h:273
const VECTOR2I & GetOffset() const
Definition: pad.h:274
void SetDelta(const VECTOR2I &aSize)
Definition: pad.h:263
void SetPosition(const VECTOR2I &aPos) override
Definition: pad.h:195
const VECTOR2I & GetDelta() const
Definition: pad.h:264
PAD_SHAPE GetShape() const
Definition: pad.h:193
EDA_ANGLE GetOrientation() const
Return the rotation angle of the pad.
Definition: pad.h:359
void SetOrientation(const EDA_ANGLE &aAngle)
Set the rotation angle of the pad.
Definition: pad.cpp:801
static TOOL_ACTION drag45Degree
Definition: pcb_actions.h:193
static TOOL_ACTION duplicateIncrement
Activation of the duplication tool with incrementing (e.g. pad number)
Definition: pcb_actions.h:182
static TOOL_ACTION changeTrackWidth
Update selected tracks & vias to the current track & via dimensions.
Definition: pcb_actions.h:152
static TOOL_ACTION unrouteSelected
Removes all tracks from the selected items to the first pad.
Definition: pcb_actions.h:93
static TOOL_ACTION mirrorH
Mirroring of selected items.
Definition: pcb_actions.h:139
static TOOL_ACTION updateFootprint
Definition: pcb_actions.h:417
static TOOL_ACTION breakTrack
Break a single track into two segments at the cursor.
Definition: pcb_actions.h:191
static TOOL_ACTION pointEditorMoveMidpoint
Definition: pcb_actions.h:297
static TOOL_ACTION getAndPlace
Find an item and start moving.
Definition: pcb_actions.h:563
static TOOL_ACTION properties
Activation of the edit tool.
Definition: pcb_actions.h:176
static TOOL_ACTION editFpInFpEditor
Definition: pcb_actions.h:439
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:68
static TOOL_ACTION moveWithReference
move with a reference point
Definition: pcb_actions.h:126
static TOOL_ACTION swap
Swapping of selected items.
Definition: pcb_actions.h:143
static TOOL_ACTION moveExact
Activation of the exact move tool.
Definition: pcb_actions.h:179
static TOOL_ACTION intersectPolygons
Intersection of multiple polygons.
Definition: pcb_actions.h:173
static TOOL_ACTION pointEditorMoveCorner
Definition: pcb_actions.h:296
static TOOL_ACTION genRemove
Definition: pcb_actions.h:285
static TOOL_ACTION selectConnection
Select tracks between junctions or expands an existing selection to pads or the entire connection.
Definition: pcb_actions.h:90
static TOOL_ACTION assignNetClass
Definition: pcb_actions.h:393
static TOOL_ACTION packAndMoveFootprints
Pack and start moving selected footprints.
Definition: pcb_actions.h:146
static TOOL_ACTION copyWithReference
copy command with manual reference point selection
Definition: pcb_actions.h:129
static TOOL_ACTION healShapes
Connect selected shapes, possibly extending or cutting them, or adding extra geometry.
Definition: pcb_actions.h:162
static TOOL_ACTION dragFreeAngle
Definition: pcb_actions.h:194
static TOOL_ACTION inspectClearance
Definition: pcb_actions.h:539
static TOOL_ACTION updateLocalRatsnest
Definition: pcb_actions.h:557
static TOOL_ACTION updateFootprints
Definition: pcb_actions.h:418
static TOOL_ACTION deleteFull
Definition: pcb_actions.h:186
static TOOL_ACTION moveIndividually
move items one-by-one
Definition: pcb_actions.h:123
static TOOL_ACTION changeFootprints
Definition: pcb_actions.h:420
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition: pcb_actions.h:71
static TOOL_ACTION chamferLines
Chamfer (i.e. adds a straight line) all selected straight lines by a user defined setback.
Definition: pcb_actions.h:160
static TOOL_ACTION filletTracks
Fillet (i.e. adds an arc tangent to) all selected straight tracks by a user defined radius.
Definition: pcb_actions.h:155
static TOOL_ACTION simplifyPolygons
Simplify polygon outlines.
Definition: pcb_actions.h:166
static TOOL_ACTION footprintProperties
Definition: pcb_actions.h:466
static TOOL_ACTION filletLines
Fillet (i.e. adds an arc tangent to) all selected straight lines by a user defined radius.
Definition: pcb_actions.h:158
static TOOL_ACTION changeFootprint
Definition: pcb_actions.h:419
static TOOL_ACTION routerInlineDrag
Activation of the Push and Shove router (inline dragging mode)
Definition: pcb_actions.h:274
static TOOL_ACTION positionRelative
Activation of the position relative tool.
Definition: pcb_actions.h:319
static TOOL_ACTION skip
Definition: pcb_actions.h:149
static TOOL_ACTION move
move or drag an item
Definition: pcb_actions.h:120
static TOOL_ACTION mirrorV
Definition: pcb_actions.h:140
static TOOL_ACTION mergePolygons
Merge multiple polygons into a single polygon.
Definition: pcb_actions.h:169
static TOOL_ACTION subtractPolygons
Subtract polygons from other polygons.
Definition: pcb_actions.h:171
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition: pcb_actions.h:76
static TOOL_ACTION flip
Flipping of selected objects.
Definition: pcb_actions.h:136
static TOOL_ACTION createArray
Tool for creating an array of objects.
Definition: pcb_actions.h:481
static TOOL_ACTION extendLines
Extend selected lines to meet at a point.
Definition: pcb_actions.h:164
static TOOL_ACTION rotateCw
Rotation of selected objects.
Definition: pcb_actions.h:132
static TOOL_ACTION rotateCcw
Definition: pcb_actions.h:133
virtual double GetLength() const override
Return the length of the arc track.
Definition: pcb_track.h:315
void SetMid(const VECTOR2I &aMid)
Definition: pcb_track.h:280
EDA_ANGLE GetAngle() const
Definition: pcb_track.cpp:1594
const VECTOR2I & GetMid() const
Definition: pcb_track.h:281
virtual VECTOR2I GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_track.h:288
Common, abstract interface for edit frames.
virtual void OnEditItemRequest(BOARD_ITEM *aItem)
Install the corresponding dialog editor for the given item.
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
PCBNEW_SETTINGS * GetPcbNewSettings() const
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
BOARD * GetBoard() const
DS_PROXY_VIEW_ITEM * GetDrawingSheet() const
void RedrawRatsnest()
Return the bounding box of the view that should be used if model is not valid.
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:52
std::unordered_set< BOARD_ITEM * > & GetItems()
Definition: pcb_group.h:69
Generic tool for picking an item.
Tool that displays edit points allowing to modify items by dragging the points.
The selection tool: currently supports:
void FilterCollectorForMarkers(GENERAL_COLLECTOR &aCollector) const
Drop any PCB_MARKERs from the collector.
PCB_SELECTION & RequestSelection(CLIENT_SELECTION_FILTER aClientFilter, bool aConfirmLockedItems=false)
Return the current selection, filtered according to aClientFilter.
void FilterCollectorForFreePads(GENERAL_COLLECTOR &aCollector, bool aForcePromotion=false) const
Check the "allow free pads" setting and if disabled, replace any pads in the collector with their par...
PCB_GROUP * GetEnteredGroup()
void FilterCollectorForHierarchy(GENERAL_COLLECTOR &aCollector, bool aMultiselect) const
In general we don't want to select both a parent and any of it's children.
PCB_SELECTION & GetSelection()
void FilterCollectorForTableCells(GENERAL_COLLECTOR &aCollector) const
Promote any table cell selections to the whole table.
void ExitGroup(bool aSelectGroup=false)
Leave the currently-entered group.
BOX2I GetBoundingBox(bool aOnlyVisible=false) const override
int GetWidth() const override
Definition: pcb_shape.cpp:369
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:317
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: pcb_shape.h:70
bool IsFootprintEditor() const
PCB_BASE_EDIT_FRAME * frame() const
KIGFX::VIEW_CONTROLS * controls() const
BOARD * board() const
PCB_DRAW_PANEL_GAL * canvas() const
bool m_isFootprintEditor
const PCB_SELECTION & selection() const
FOOTPRINT * footprint() const
void SetWidth(int aWidth)
Definition: pcb_track.h:115
virtual double GetLength() const
Get the length of the track using the hypotenuse calculation.
Definition: pcb_track.cpp:636
int GetWidth() const
Definition: pcb_track.h:116
void SetEnd(const VECTOR2I &aEnd)
Definition: pcb_track.h:118
void SetStart(const VECTOR2I &aStart)
Definition: pcb_track.h:121
const VECTOR2I & GetStart() const
Definition: pcb_track.h:122
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:119
EDA_ITEM_FLAGS IsPointOnEnds(const VECTOR2I &point, int min_dist=0) const
Return STARTPOINT if point if near (dist = min_dist) start point, ENDPOINT if point if near (dist = m...
Definition: pcb_track.cpp:559
void SetMotionHandler(MOTION_HANDLER aHandler)
Set a handler for mouse motion.
Definition: picker_tool.h:83
void SetClickHandler(CLICK_HANDLER aHandler)
Set a handler for mouse click event.
Definition: picker_tool.h:72
void SetSnapping(bool aSnap)
Definition: picker_tool.h:65
void SetCancelHandler(CANCEL_HANDLER aHandler)
Set a handler for cancel events (ESC or context-menu Cancel).
Definition: picker_tool.h:92
void SetFinalizeHandler(FINALIZE_HANDLER aHandler)
Set a handler for the finalize event.
Definition: picker_tool.h:103
bool RoutingInProgress()
Returns whether routing is currently active.
bool CanInlineDrag(int aDragMode)
Definition: seg.h:42
VECTOR2I A
Definition: seg.h:49
VECTOR2I B
Definition: seg.h:50
const VECTOR2I NearestPoint(const VECTOR2I &aP) const
Compute a point on the segment (this) that is closest to point aP.
Definition: seg.cpp:327
OPT_VECTOR2I IntersectLines(const SEG &aSeg) const
Compute the intersection point of lines passing through ends of (this) and aSeg.
Definition: seg.h:220
bool ApproxCollinear(const SEG &aSeg, int aDistanceThreshold=1) const
Definition: seg.cpp:449
VECTOR2I LineProject(const VECTOR2I &aP) const
Compute the perpendicular projection point of aP on a line passing through ends of the segment.
Definition: seg.cpp:371
SEG PerpendicularSeg(const VECTOR2I &aP) const
Compute a segment perpendicular to this one, passing through point aP.
Definition: seg.cpp:265
int Side(const VECTOR2I &aP) const
Determine on which side of directed line passing via segment ends point aP lies.
Definition: seg.h:143
EDA_ANGLE Angle(const SEG &aOther) const
Determine the smallest angle between two segments.
Definition: seg.cpp:97
static SELECTION_CONDITION HasTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if among the selected items there is at least one of a given types.
static SELECTION_CONDITION HasType(KICAD_T aType)
Create a functor that tests if among the selected items there is at least one of a given type.
static bool NotEmpty(const SELECTION &aSelection)
Test if there are any items selected.
static SELECTION_CONDITION MoreThan(int aNumber)
Create a functor that tests if the number of selected items is greater than the value given as parame...
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.
static SELECTION_CONDITION OnlyTypes(std::vector< KICAD_T > aTypes)
Create a functor that tests if the selected items are only of given types.
int RemoveItemFromSel(const TOOL_EVENT &aEvent)
int AddItemToSel(const TOOL_EVENT &aEvent)
ITER end()
Definition: selection.h:75
ITER begin()
Definition: selection.h:74
VECTOR2I GetReferencePoint() const
Definition: selection.cpp:171
virtual VECTOR2I GetCenter() const
Returns the center point of the selection area bounding box.
Definition: selection.cpp:93
bool IsHover() const
Definition: selection.h:84
virtual unsigned int GetSize() const override
Return the number of stored items.
Definition: selection.h:100
EDA_ITEM * Front() const
Definition: selection.h:172
bool HasType(KICAD_T aType) const
Checks if there is at least one item of requested kind.
Definition: selection.cpp:145
int Size() const
Returns the number of selected parts.
Definition: selection.h:116
std::deque< EDA_ITEM * > & Items()
Definition: selection.h:177
void ClearReferencePoint()
Definition: selection.cpp:186
void SetReferencePoint(const VECTOR2I &aP)
Definition: selection.cpp:180
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:110
bool HasReferencePoint() const
Definition: selection.h:211
size_t CountType(KICAD_T aType) const
Definition: selection.cpp:157
bool Contains(EDA_ITEM *aItem) const
Definition: selection.cpp:84
const VECTOR2I & GetP1() const
Definition: shape_arc.h:114
const VECTOR2I & GetP0() const
Definition: shape_arc.h:113
Represent a set of closed polygons.
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
void SimplifyOutlines(int aMaxError=0)
Simplifies the lines in the polyset.
const VECTOR2I & CVertex(int aIndex, int aOutline, int aHole) const
Return the index-th vertex in a given hole outline within a given outline.
static const int MIN_PRECISION_IU
This is the minimum precision for all the points in a shape.
Definition: shape.h:131
virtual void PopTool(const TOOL_EVENT &aEvent)
Pops a tool from the stack.
virtual void PushTool(const TOOL_EVENT &aEvent)
NB: the definition of "tool" is different at the user level.
bool ToolStackIsEmpty()
Definition: tools_holder.h:125
bool IsCurrentTool(const TOOL_ACTION &aAction) const
TOOL_EVENT MakeEvent() const
Return the event associated with the action (i.e.
TOOL_MANAGER * GetManager() const
Return the instance of TOOL_MANAGER that takes care of the tool.
Definition: tool_base.h:146
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:218
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
bool IsToolActive() const
Definition: tool_base.cpp:31
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
Generic, UI-independent tool event.
Definition: tool_event.h:167
TOOL_ACTIONS Action() const
These give a tool a method of informing the TOOL_MANAGER that a particular event should be passed on ...
Definition: tool_event.h:246
bool Matches(const TOOL_EVENT &aEvent) const
Test whether two events match in terms of category & action or command.
Definition: tool_event.h:384
COMMIT * Commit() const
Returns information about difference between current mouse cursor position and the place where draggi...
Definition: tool_event.h:275
void SetParameter(T aParam)
Set a non-standard parameter assigned to the event.
Definition: tool_event.h:515
TOOL_EVENT_CATEGORY Category() const
Returns more specific information about the type of an event.
Definition: tool_event.h:243
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
Definition: tool_event.cpp:82
T Parameter() const
Return a parameter assigned to the event.
Definition: tool_event.h:460
void Go(int(T::*aStateFunc)(const TOOL_EVENT &), const TOOL_EVENT_LIST &aConditions=TOOL_EVENT(TC_ANY, TA_ANY))
Define which state (aStateFunc) to go when a certain event arrives (aConditions).
TOOL_MENU & GetToolMenu()
TOOL_EVENT * Wait(const TOOL_EVENT_LIST &aEventList=TOOL_EVENT(TC_ANY, TA_ANY))
Suspend execution of the tool until an event specified in aEventList arrives.
void Activate()
Run the tool.
bool ProcessEvent(const TOOL_EVENT &aEvent)
Propagate an event to tools that requested events of matching type(s).
void PostEvent(const TOOL_EVENT &aEvent)
Put an event to the event queue to be processed at the end of event processing cycle.
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:150
bool PostAction(const std::string &aActionName, T aParam)
Run the specified action after the current action (coroutine) ends.
Definition: tool_manager.h:235
bool RunSynchronousAction(const TOOL_ACTION &aAction, COMMIT *aCommit, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:197
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:44
void RegisterSubMenu(std::shared_ptr< ACTION_MENU > aSubMenu)
Store a submenu of this menu model.
Definition: tool_menu.cpp:50
double Distance(const VECTOR2< extended_type > &aVector) const
Compute the distance between two vectors.
Definition: vector2d.h:550
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:278
An extension of WX_TEXT_ENTRY_DIALOG that uses UNIT_BINDER to request a dimension (e....
int GetValue()
Returns the value in internal units.
Handle a list of polygons defining a copper zone.
Definition: zone.h:73
bool UnFill()
Removes the zone filling.
Definition: zone.cpp:209
bool HitTestCutout(const VECTOR2I &aRefPos, int *aOutlineIdx=nullptr, int *aHoleIdx=nullptr) const
Test if the given point is contained within a cutout of the zone.
Definition: zone.cpp:513
void HatchBorder()
Compute the hatch lines depending on the hatch parameters and stores it in the zone's attribute m_bor...
Definition: zone.cpp:913
void RemoveCutout(int aOutlineIdx, int aHoleIdx)
Remove a cutout from the zone.
Definition: zone.cpp:758
@ CHT_UNGROUP
Definition: commit.h:46
ROTATION_ANCHOR
@ ROTATE_AROUND_USER_ORIGIN
@ ROTATE_AROUND_SEL_CENTER
@ ROTATE_AROUND_AUX_ORIGIN
@ ROTATE_AROUND_ITEM_ANCHOR
#define _(s)
@ DEGREES_T
Definition: eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_180
Definition: eda_angle.h:405
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
Definition: eda_item.h:536
#define IS_NEW
New item, just created.
#define STRUCT_DELETED
flag indication structures to be erased
static const std::vector< KICAD_T > baseConnectedTypes
Definition: edit_tool.cpp:86
static VECTOR2I mirrorPointY(const VECTOR2I &aPoint, const VECTOR2I &aMirrorPoint)
Mirror a point about the vertical axis passing through another point.
Definition: edit_tool.cpp:1965
static void mirrorPadX(PAD &aPad, const VECTOR2I &aMirrorPoint)
Mirror a pad in the vertical axis passing through a point (mirror left to right).
Definition: edit_tool.cpp:1980
static std::shared_ptr< CONDITIONAL_MENU > makePositioningToolsMenu(TOOL_INTERACTIVE *aTool)
Definition: edit_tool.cpp:120
static std::shared_ptr< CONDITIONAL_MENU > makeShapeModificationMenu(TOOL_INTERACTIVE *aTool)
Definition: edit_tool.cpp:142
static std::optional< int > GetFilletParams(PCB_BASE_EDIT_FRAME &aFrame)
Prompt the user for the fillet radius and return it.
Definition: edit_tool.cpp:1193
static std::optional< CHAMFER_PARAMS > GetChamferParams(PCB_BASE_EDIT_FRAME &aFrame)
Prompt the user for chamfer parameters.
Definition: edit_tool.cpp:1217
static VECTOR2I mirrorPointX(const VECTOR2I &aPoint, const VECTOR2I &aMirrorPoint)
Mirror a point about the vertical axis passing through another point.
Definition: edit_tool.cpp:1950
static const std::vector< KICAD_T > footprintTypes
Definition: edit_tool.cpp:78
static const std::vector< KICAD_T > connectedTypes
Definition: edit_tool.cpp:91
static void mirrorPadY(PAD &aPad, const VECTOR2I &aMirrorPoint)
Mirror a pad in the vertical axis passing through a point (mirror left to right).
Definition: edit_tool.cpp:2003
static const std::vector< KICAD_T > groupTypes
Definition: edit_tool.cpp:80
static const std::vector< KICAD_T > unroutableTypes
Definition: edit_tool.cpp:97
static const std::vector< KICAD_T > padTypes
Definition: edit_tool.cpp:76
static const std::vector< KICAD_T > trackTypes
Definition: edit_tool.cpp:82
static std::vector< KICAD_T > connectedTypes
void ConnectBoardShapes(std::vector< PCB_SHAPE * > &aShapeList, std::vector< std::unique_ptr< PCB_SHAPE > > &aNewShapes, int aChainingEpsilon)
Connects shapes to each other, making continious contours (adjacent shapes will have a common vertex)...
@ FRAME_PCB_EDITOR
Definition: frame_type.h:42
@ LAYER_DRAWINGSHEET
drawingsheet frame and titleblock
Definition: layer_ids.h:221
@ LAYER_SCHEMATIC_DRAWINGSHEET
Definition: layer_ids.h:398
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
This file contains miscellaneous commonly used macros and functions.
#define UNIMPLEMENTED_FOR(type)
Definition: macros.h:96
KICOMMON_API wxString MessageTextFromValue(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, double aValue, bool aAddUnitsText=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
A helper to convert the double length aValue to a string in inches, millimeters, or unscaled units.
Definition: eda_units.cpp:408
wxPoint GetMousePosition()
Returns the mouse position in screen coordinates.
Definition: wxgtk/ui.cpp:611
@ DM_ANY
Definition: pns_router.h:77
@ DM_FREE_ANGLE
Definition: pns_router.h:75
SGLIB_API S3DMODEL * GetModel(SCENEGRAPH *aNode)
Function GetModel creates an S3DMODEL representation of aNode (raw data, no transforms)
Definition: ifsg_api.cpp:338
EDA_ANGLE GetEventRotationAngle(const PCB_BASE_EDIT_FRAME &aFrame, const TOOL_EVENT &aEvent)
Function getEventRotationAngle()
bool contains(const _Container &__container, _Value __value)
Returns true if the container contains the given value.
Definition: kicad_algo.h:100
void for_all_pairs(_InputIterator __first, _InputIterator __last, _Function __f)
Apply a function to every possible pair of elements of a sequence.
Definition: kicad_algo.h:84
Class to handle a set of BOARD_ITEMs.
BOARD * GetBoard()
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
Parameters that define a simple chamfer operation.
Definition: chamfer.h:37
const double IU_PER_MM
Definition: base_units.h:76
constexpr int mmToIU(double mm) const
Definition: base_units.h:88
@ GR_TEXT_H_ALIGN_CENTER
@ GR_TEXT_H_ALIGN_RIGHT
@ GR_TEXT_H_ALIGN_LEFT
@ BUT_LEFT
Definition: tool_event.h:131
const VECTOR2I CalcArcMid(const VECTOR2I &aStart, const VECTOR2I &aEnd, const VECTOR2I &aCenter, bool aMinArcAngle=true)
Return the middle point of an arc, half-way between aStart and aEnd.
Definition: trigo.cpp:208
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:228
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:105
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:102
@ PCB_GENERATOR_T
class PCB_GENERATOR, generator on a layer
Definition: typeinfo.h:91
@ PCB_VIA_T
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:97
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:103
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:110
@ PCB_TEXTBOX_T
class PCB_TEXTBOX, wrapped text on a layer
Definition: typeinfo.h:93
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition: typeinfo.h:107
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition: typeinfo.h:92
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition: typeinfo.h:89
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition: typeinfo.h:90
@ PCB_MARKER_T
class PCB_MARKER, a marker used to show something
Definition: typeinfo.h:99
@ PCB_TARGET_T
class PCB_TARGET, a target (graphic item)
Definition: typeinfo.h:106
@ PCB_SHAPE_LOCATE_SEGMENT_T
Definition: typeinfo.h:133
@ PCB_SHAPE_LOCATE_RECT_T
Definition: typeinfo.h:134
@ PCB_TABLECELL_T
class PCB_TABLECELL, PCB_TEXTBOX for use in tables
Definition: typeinfo.h:95
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition: typeinfo.h:86
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:101
@ PCB_SHAPE_LOCATE_BEZIER_T
Definition: typeinfo.h:138
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition: typeinfo.h:87
@ PCB_SHAPE_LOCATE_POLY_T
Definition: typeinfo.h:137
@ PCB_SHAPE_LOCATE_ARC_T
Definition: typeinfo.h:136
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition: typeinfo.h:98
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition: typeinfo.h:100
@ PCB_TABLE_T
class PCB_TABLE, table of PCB_TABLECELLs
Definition: typeinfo.h:94
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition: typeinfo.h:96
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition: typeinfo.h:104
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:121
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:673