KiCad PCB EDA Suite
Loading...
Searching...
No Matches
drawing_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) 2014-2017 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 * @author Maciej Suminski <[email protected]>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include "drawing_tool.h"
27#include "geometry/shape_rect.h"
28#include "dialog_table_properties.h"
29
30#include <pgm_base.h>
32#include <pcbnew_settings.h>
34#include <dialogs/dialog_text_properties.h>
45#include <router/router_tool.h>
46#include <status_popup.h>
47#include <tool/tool_manager.h>
48#include <tools/pcb_actions.h>
54#include <view/view.h>
56#include <widgets/wx_infobar.h>
57#include <wx/filedlg.h>
58#include <wx/msgdlg.h>
59
60#include <bitmaps.h>
61#include <board.h>
62#include <board_commit.h>
64#include <confirm.h>
65#include <footprint.h>
66#include <macros.h>
67#include <gal/painter.h>
68#include <pcb_edit_frame.h>
69#include <pcb_group.h>
70#include <pcb_reference_image.h>
71#include <pcb_text.h>
72#include <pcb_textbox.h>
73#include <pcb_table.h>
74#include <pcb_tablecell.h>
75#include <pcb_dimension.h>
76#include <pcbnew_id.h>
77#include <scoped_set_reset.h>
78#include <string_utils.h>
79#include <zone.h>
80#include <fix_board_shape.h>
81#include <view/view_controls.h>
82
83const unsigned int DRAWING_TOOL::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
84
86
87
89{
90public:
92 ACTION_MENU( true )
93 {
94 SetIcon( BITMAPS::width_track_via );
95 SetTitle( _( "Select Via Size" ) );
96 }
97
98protected:
99 ACTION_MENU* create() const override
100 {
101 return new VIA_SIZE_MENU();
102 }
103
104 void update() override
105 {
108 bool useIndex = !bds.m_UseConnectedTrackWidth
109 && !bds.UseCustomTrackViaSize();
110 wxString msg;
111
112 Clear();
113
114 Append( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, _( "Use Custom Values..." ),
115 _( "Specify custom track and via sizes" ), wxITEM_CHECK );
117
118 AppendSeparator();
119
120 for( unsigned i = 1; i < bds.m_ViasDimensionsList.size(); i++ )
121 {
123
124 if( via.m_Drill > 0 )
125 {
126 msg.Printf( _("Via %s, hole %s" ),
127 frame->MessageTextFromValue( via.m_Diameter ),
128 frame->MessageTextFromValue( via.m_Drill ) );
129 }
130 else
131 {
132 msg.Printf( _( "Via %s" ),
133 frame->MessageTextFromValue( via.m_Diameter ) );
134 }
135
136 int menuIdx = ID_POPUP_PCB_SELECT_VIASIZE1 + i;
137 Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
138 Check( menuIdx, useIndex && bds.GetViaSizeIndex() == i );
139 }
140 }
141
142 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
143 {
146 int id = aEvent.GetId();
147
148 // On Windows, this handler can be called with an event ID not existing in any
149 // menuitem, so only set flags when we have an ID match.
150
152 {
153 DIALOG_TRACK_VIA_SIZE sizeDlg( frame, bds );
154
155 if( sizeDlg.ShowModal() == wxID_OK )
156 {
157 bds.UseCustomTrackViaSize( true );
158 bds.m_UseConnectedTrackWidth = false;
159 }
160 }
162 {
163 bds.UseCustomTrackViaSize( false );
164 bds.m_UseConnectedTrackWidth = false;
166 }
167
169 }
170};
171
172
174 PCB_TOOL_BASE( "pcbnew.InteractiveDrawing" ),
175 m_view( nullptr ),
176 m_controls( nullptr ),
177 m_board( nullptr ),
178 m_frame( nullptr ),
179 m_mode( MODE::NONE ),
180 m_inDrawingTool( false ),
181 m_layer( UNDEFINED_LAYER ),
182 m_stroke( 1, LINE_STYLE::DEFAULT, COLOR4D::UNSPECIFIED ),
183 m_pickerItem( nullptr ),
184 m_tuningPattern( nullptr )
185{
186}
187
188
190{
191}
192
193
195{
196 auto haveHighlight =
197 [&]( const SELECTION& sel )
198 {
200
201 return !cfg->GetHighlightNetCodes().empty();
202 };
203
204 auto activeToolFunctor =
205 [this]( const SELECTION& aSel )
206 {
207 return m_mode != MODE::NONE;
208 };
209
210 // some interactive drawing tools can undo the last point
211 auto canUndoPoint =
212 [this]( const SELECTION& aSel )
213 {
214 return ( m_mode == MODE::ARC
215 || m_mode == MODE::ZONE
218 || m_mode == MODE::BEZIER
219 || m_mode == MODE::LINE );
220 };
221
222 // functor for tools that can automatically close the outline
223 auto canCloseOutline =
224 [this]( const SELECTION& aSel )
225 {
226 return ( m_mode == MODE::ZONE
229 };
230
231 auto arcToolActive =
232 [this]( const SELECTION& aSel )
233 {
234 return m_mode == MODE::ARC;
235 };
236
237 auto viaToolActive =
238 [this]( const SELECTION& aSel )
239 {
240 return m_mode == MODE::VIA;
241 };
242
243 auto tuningToolActive =
244 [this]( const SELECTION& aSel )
245 {
246 return m_mode == MODE::TUNING;
247 };
248
249 auto dimensionToolActive =
250 [this]( const SELECTION& aSel )
251 {
252 return m_mode == MODE::DIMENSION;
253 };
254
255 CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu();
256
257 // cancel current tool goes in main context menu at the top if present
258 ctxMenu.AddItem( ACTIONS::cancelInteractive, activeToolFunctor, 1 );
259 ctxMenu.AddSeparator( 1 );
260
261 ctxMenu.AddItem( PCB_ACTIONS::clearHighlight, haveHighlight, 2 );
262 ctxMenu.AddSeparator( haveHighlight, 2 );
263
264 // tool-specific actions
265 ctxMenu.AddItem( PCB_ACTIONS::closeOutline, canCloseOutline, 200 );
266 ctxMenu.AddItem( PCB_ACTIONS::deleteLastPoint, canUndoPoint, 200 );
267 ctxMenu.AddItem( PCB_ACTIONS::arcPosture, arcToolActive, 200 );
268 ctxMenu.AddItem( PCB_ACTIONS::spacingIncrease, tuningToolActive, 200 );
269 ctxMenu.AddItem( PCB_ACTIONS::spacingDecrease, tuningToolActive, 200 );
270 ctxMenu.AddItem( PCB_ACTIONS::amplIncrease, tuningToolActive, 200 );
271 ctxMenu.AddItem( PCB_ACTIONS::amplDecrease, tuningToolActive, 200 );
272 ctxMenu.AddItem( PCB_ACTIONS::lengthTunerSettings, tuningToolActive, 200 );
273 ctxMenu.AddItem( PCB_ACTIONS::changeDimensionArrows, dimensionToolActive, 200 );
274
275 ctxMenu.AddCheckItem( PCB_ACTIONS::toggleHV45Mode, !tuningToolActive, 250 );
276 ctxMenu.AddSeparator( 500 );
277
278 std::shared_ptr<VIA_SIZE_MENU> viaSizeMenu = std::make_shared<VIA_SIZE_MENU>();
279 viaSizeMenu->SetTool( this );
280 m_menu->RegisterSubMenu( viaSizeMenu );
281 ctxMenu.AddMenu( viaSizeMenu.get(), viaToolActive, 500 );
282
283 ctxMenu.AddSeparator( 500 );
284
285 // Type-specific sub-menus will be added for us by other tools
286 // For example, zone fill/unfill is provided by the PCB control tool
287
288 // Finally, add the standard zoom/grid items
289 getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( *m_menu.get() );
290
291 return true;
292}
293
294
296{
297 // Init variables used by every drawing tool
298 m_view = getView();
300 m_board = getModel<BOARD>();
301 m_frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
302
303 // Re-initialize session attributes
305
306 if( aReason == RESET_REASON::SHUTDOWN )
307 return;
308
311 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
312 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
313
322
324}
325
326
328{
329 return m_mode;
330}
331
332
334{
335 if( m_frame )
336 m_frame->DisplayConstraintsMsg( Is45Limited() ? _( "Constrain to H, V, 45" ) : wxString( "" ) );
337}
338
339
341{
343 return 0;
344
345 if( m_inDrawingTool )
346 return 0;
347
349
350 BOARD_ITEM* parent = m_frame->GetModel();
351 PCB_SHAPE* line = new PCB_SHAPE( parent );
352 BOARD_COMMIT commit( m_frame );
353 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::LINE );
354 std::optional<VECTOR2D> startingPoint;
355 std::stack<PCB_SHAPE*> committedLines;
356
357 line->SetShape( SHAPE_T::SEGMENT );
358 line->SetFlags( IS_NEW );
359
360 if( aEvent.HasPosition() )
361 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
362
363 m_frame->PushTool( aEvent );
364 Activate();
365
366 while( drawShape( aEvent, &line, startingPoint, &committedLines ) )
367 {
368 if( line )
369 {
370 commit.Add( line );
371 commit.Push( _( "Draw Line" ) );
372 startingPoint = VECTOR2D( line->GetEnd() );
373 committedLines.push( line );
374 }
375 else
376 {
377 startingPoint = std::nullopt;
378 }
379
380 line = new PCB_SHAPE( parent );
381 line->SetShape( SHAPE_T::SEGMENT );
382 line->SetFlags( IS_NEW );
383 }
384
385 return 0;
386}
387
388
390{
392 return 0;
393
394 if( m_inDrawingTool )
395 return 0;
396
398
399 bool isTextBox = aEvent.IsAction( &PCB_ACTIONS::drawTextBox );
400 PCB_SHAPE* rect = nullptr;
401 BOARD_COMMIT commit( m_frame );
402 BOARD_ITEM* parent = m_frame->GetModel();
403 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::RECTANGLE );
404 std::optional<VECTOR2D> startingPoint;
405
406 rect = isTextBox ? new PCB_TEXTBOX( parent ) : new PCB_SHAPE( parent );
407 rect->SetShape( SHAPE_T::RECTANGLE );
408 rect->SetFilled( false );
409 rect->SetFlags( IS_NEW );
410
411 if( aEvent.HasPosition() )
412 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
413
414 m_frame->PushTool( aEvent );
415 Activate();
416
417 while( drawShape( aEvent, &rect, startingPoint, nullptr ) )
418 {
419 if( rect )
420 {
421 bool cancelled = false;
422
423 if( PCB_TEXTBOX* textbox = dynamic_cast<PCB_TEXTBOX*>( rect ) )
424 cancelled = m_frame->ShowTextBoxPropertiesDialog( textbox ) != wxID_OK;
425
426 if( cancelled )
427 {
428 delete rect;
429 rect = nullptr;
430 }
431 else
432 {
433 rect->Normalize();
434 commit.Add( rect );
435 commit.Push( isTextBox ? _( "Draw Text Box" ) : _( "Draw Rectangle" ) );
436
438 }
439 }
440
441 rect = isTextBox ? new PCB_TEXTBOX( parent ) : new PCB_SHAPE( parent );
442 rect->SetShape( SHAPE_T::RECTANGLE );
443 rect->SetFilled( false );
444 rect->SetFlags( IS_NEW );
445 startingPoint = std::nullopt;
446 }
447
448 return 0;
449}
450
451
453{
455 return 0;
456
457 if( m_inDrawingTool )
458 return 0;
459
461
462 BOARD_ITEM* parent = m_frame->GetModel();
463 PCB_SHAPE* circle = new PCB_SHAPE( parent );
464 BOARD_COMMIT commit( m_frame );
465 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::CIRCLE );
466 std::optional<VECTOR2D> startingPoint;
467
468 circle->SetShape( SHAPE_T::CIRCLE );
469 circle->SetFilled( false );
470 circle->SetFlags( IS_NEW );
471
472 if( aEvent.HasPosition() )
473 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
474
475 m_frame->PushTool( aEvent );
476 Activate();
477
478 while( drawShape( aEvent, &circle, startingPoint, nullptr ) )
479 {
480 if( circle )
481 {
482 commit.Add( circle );
483 commit.Push( _( "Draw Circle" ) );
484
486 }
487
488 circle = new PCB_SHAPE( parent );
489 circle->SetShape( SHAPE_T::CIRCLE );
490 circle->SetFilled( false );
491 circle->SetFlags( IS_NEW );
492
493 startingPoint = std::nullopt;
494 }
495
496 return 0;
497}
498
499
501{
503 return 0;
504
505 if( m_inDrawingTool )
506 return 0;
507
509
510 BOARD_ITEM* parent = m_frame->GetModel();
511 PCB_SHAPE* arc = new PCB_SHAPE( parent );
512 BOARD_COMMIT commit( m_frame );
513 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ARC );
514 std::optional<VECTOR2D> startingPoint;
515
516 arc->SetShape( SHAPE_T::ARC );
517 arc->SetFlags( IS_NEW );
518
519 m_frame->PushTool( aEvent );
520 Activate();
521
522 if( aEvent.HasPosition() )
523 startingPoint = aEvent.Position();
524
525 while( drawArc( aEvent, &arc, startingPoint ) )
526 {
527 if( arc )
528 {
529 commit.Add( arc );
530 commit.Push( _( "Draw Arc" ) );
531
533 }
534
535 arc = new PCB_SHAPE( parent );
536 arc->SetShape( SHAPE_T::ARC );
537 arc->SetFlags( IS_NEW );
538
539 startingPoint = std::nullopt;
540 }
541
542 return 0;
543}
544
545
547{
549 return 0;
550
551 if( m_inDrawingTool )
552 return 0;
553
555
556 BOARD_COMMIT commit( m_frame );
557 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::BEZIER );
558 OPT_VECTOR2I startingPoint, startingC1;
559
560 m_frame->PushTool( aEvent );
561 Activate();
562
563 if( aEvent.HasPosition() )
564 startingPoint = aEvent.Position();
565
567 while( result != DRAW_ONE_RESULT::CANCELLED )
568 {
569 std::unique_ptr<PCB_SHAPE> bezier =
570 drawOneBezier( aEvent, startingPoint, startingC1, result );
571
572 // Anyting other than accepted means no chaining
573 startingPoint = std::nullopt;
574 startingC1 = std::nullopt;
575
576 // If a bezier was created, add it and go again
577 if( bezier )
578 {
579 PCB_SHAPE& bezierRef = *bezier;
580 commit.Add( bezier.release() );
581 commit.Push( _( "Draw Bezier" ) );
582
583 // Don't chain if reset (or accepted and reset)
584 if( result == DRAW_ONE_RESULT::ACCEPTED )
585 {
586 startingPoint = bezierRef.GetEnd();
587
588 // If the last bezier has a zero C2 control arm, allow the user to
589 // define a new C1 control arm for the next one.
590 if( bezierRef.GetEnd() != bezierRef.GetBezierC2() )
591 {
592 // Mirror the control point across the end point to get
593 // a tangent control point
594 startingC1 =
595 bezierRef.GetEnd() - ( bezierRef.GetBezierC2() - bezierRef.GetEnd() );
596 }
597 }
598 }
599 }
600
601 return 0;
602}
603
604
606{
607 if( m_inDrawingTool )
608 return 0;
609
611
613 bool immediateMode = image != nullptr;
615 bool ignorePrimePosition = false;
616 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
617
620 BOARD_COMMIT commit( m_frame );
621
623
624 // Add all the drawable symbols to preview
625 if( image )
626 {
627 image->SetPosition( cursorPos );
629 m_view->AddToPreview( image, false ); // Add, but not give ownership
630 }
631
632 m_frame->PushTool( aEvent );
633
634 auto setCursor =
635 [&]()
636 {
637 if( image )
638 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
639 else
640 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
641 };
642
643 auto cleanup =
644 [&] ()
645 {
649 delete image;
650 image = nullptr;
651 };
652
653 Activate();
654
655 // Must be done after Activate() so that it gets set into the correct context
656 getViewControls()->ShowCursor( true );
657
658 // Set initial cursor
659 setCursor();
660
661 // Prime the pump
662 if( image )
663 {
665 }
666 else if( aEvent.HasPosition() )
667 {
668 m_toolMgr->PrimeTool( aEvent.Position() );
669 }
670 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
671 {
672 m_toolMgr->PrimeTool( { 0, 0 } );
673 ignorePrimePosition = true;
674 }
675
676 // Main loop: keep receiving events
677 while( TOOL_EVENT* evt = Wait() )
678 {
679 setCursor();
680
681 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
682 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
683 cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(),
684 { m_frame->GetActiveLayer() },
687 m_controls->ForceCursorPosition( true, cursorPos );
688
689 if( evt->IsCancelInteractive() || ( image && evt->IsAction( &ACTIONS::undo ) ) )
690 {
691 if( image )
692 {
693 cleanup();
694 }
695 else
696 {
697 m_frame->PopTool( aEvent );
698 break;
699 }
700
701 if( immediateMode )
702 {
703 m_frame->PopTool( aEvent );
704 break;
705 }
706 }
707 else if( evt->IsActivate() )
708 {
709 if( image && evt->IsMoveTool() )
710 {
711 // We're already moving our own item; ignore the move tool
712 evt->SetPassEvent( false );
713 continue;
714 }
715
716 if( image )
717 {
718 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel image creation." ) );
719 evt->SetPassEvent( false );
720 continue;
721 }
722
723 if( evt->IsMoveTool() )
724 {
725 // Leave ourselves on the stack so we come back after the move
726 break;
727 }
728 else
729 {
730 m_frame->PopTool( aEvent );
731 break;
732 }
733 }
734 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
735 {
736 if( !image )
737 {
739
740 wxFileDialog dlg( m_frame, _( "Choose Image" ), wxEmptyString, wxEmptyString,
741 _( "Image Files" ) + wxS( " " ) + wxImage::GetImageExtWildcard(),
742 wxFD_OPEN );
743
744 if( dlg.ShowModal() != wxID_OK )
745 continue;
746
747 // If we started with a hotkey which has a position then warp back to that.
748 // Otherwise update to the current mouse position pinned inside the autoscroll
749 // boundaries.
750 if( evt->IsPrime() && !ignorePrimePosition )
751 {
752 cursorPos = grid.Align( evt->Position() );
753 getViewControls()->WarpMouseCursor( cursorPos, true );
754 }
755 else
756 {
758 cursorPos = getViewControls()->GetMousePosition();
759 }
760
761 cursorPos = getViewControls()->GetMousePosition( true );
762
763 wxString fullFilename = dlg.GetPath();
764
765 if( wxFileExists( fullFilename ) )
766 image = new PCB_REFERENCE_IMAGE( m_frame->GetModel(), cursorPos );
767
768 if( !image || !image->GetReferenceImage().ReadImageFile( fullFilename ) )
769 {
770 wxMessageBox( wxString::Format(_( "Could not load image from '%s'." ), fullFilename ) );
771 delete image;
772 image = nullptr;
773 continue;
774 }
775
776 image->SetFlags( IS_NEW | IS_MOVING );
777 image->SetLayer( m_frame->GetActiveLayer() );
778
780 m_view->AddToPreview( image, false ); // Add, but not give ownership
781 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
782 selectionTool->AddItemToSel( image, false );
783
784 getViewControls()->SetCursorPosition( cursorPos, false );
785 setCursor();
786 m_view->ShowPreview( true );
787 }
788 else
789 {
790 commit.Add( image );
791 commit.Push( _( "Place Image" ) );
792
794
795 image = nullptr;
797
799
800 if( immediateMode )
801 {
802 m_frame->PopTool( aEvent );
803 break;
804 }
805 }
806 }
807 else if( evt->IsClick( BUT_RIGHT ) )
808 {
809 // Warp after context menu only if dragging...
810 if( !image )
812
813 m_menu->ShowContextMenu( selectionTool->GetSelection() );
814 }
815 else if( image && ( evt->IsAction( &ACTIONS::refreshPreview )
816 || evt->IsMotion() ) )
817 {
818 image->SetPosition( cursorPos );
820 m_view->AddToPreview( image, false ); // Add, but not give ownership
821 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
822 }
823 else if( image && evt->IsAction( &ACTIONS::doDelete ) )
824 {
825 cleanup();
826 }
827 else if( image && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
828 || evt->IsAction( &ACTIONS::redo ) ) )
829 {
830 wxBell();
831 }
832 else
833 {
834 evt->SetPassEvent();
835 }
836
837 // Enable autopanning and cursor capture only when there is an image to be placed
838 getViewControls()->SetAutoPan( image != nullptr );
839 getViewControls()->CaptureCursor( image != nullptr );
840 }
841
842 getViewControls()->SetAutoPan( false );
843 getViewControls()->CaptureCursor( false );
844 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
845
846 return 0;
847}
848
849
851{
853 return 0;
854
855 if( m_inDrawingTool )
856 return 0;
857
859
860 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
861 PCB_TEXT* text = nullptr;
862 bool ignorePrimePosition = false;
864 BOARD_COMMIT commit( m_frame );
865 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TEXT );
867
868 auto setCursor =
869 [&]()
870 {
871 if( text )
872 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
873 else
874 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
875 };
876
877 auto cleanup =
878 [&]()
879 {
882 m_controls->ShowCursor( true );
883 m_controls->SetAutoPan( false );
884 m_controls->CaptureCursor( false );
885 delete text;
886 text = nullptr;
887 };
888
890
891 m_frame->PushTool( aEvent );
892
893 Activate();
894 // Must be done after Activate() so that it gets set into the correct context
895 m_controls->ShowCursor( true );
897 // do not capture or auto-pan until we start placing some text
898 // Set initial cursor
899 setCursor();
900
901 if( aEvent.HasPosition() )
902 {
903 m_toolMgr->PrimeTool( aEvent.Position() );
904 }
905 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
906 {
907 m_toolMgr->PrimeTool( { 0, 0 } );
908 ignorePrimePosition = true;
909 }
910
911 // Main loop: keep receiving events
912 while( TOOL_EVENT* evt = Wait() )
913 {
914 setCursor();
915
916 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
917 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
918 VECTOR2I cursorPos =
920 { m_frame->GetActiveLayer() }, GRID_TEXT ),
922 m_controls->ForceCursorPosition( true, cursorPos );
923
924 if( evt->IsDrag() )
925 {
926 continue;
927 }
928 else if( evt->IsCancelInteractive() || ( text && evt->IsAction( &ACTIONS::undo ) ) )
929 {
930 if( text )
931 {
932 cleanup();
933 }
934 else
935 {
936 m_frame->PopTool( aEvent );
937 break;
938 }
939 }
940 else if( evt->IsActivate() )
941 {
942 if( text )
943 cleanup();
944
945 if( evt->IsMoveTool() )
946 {
947 // leave ourselves on the stack so we come back after the move
948 break;
949 }
950 else
951 {
952 m_frame->PopTool( aEvent );
953 break;
954 }
955 }
956 else if( evt->IsClick( BUT_RIGHT ) )
957 {
958 if( !text )
960
961 m_menu->ShowContextMenu( selection() );
962 }
963 else if( evt->IsClick( BUT_LEFT ) )
964 {
965 bool placing = text != nullptr;
966
967 if( !text )
968 {
970
972
974 TEXT_ATTRIBUTES textAttrs;
975
976 textAttrs.m_Size = bds.GetTextSize( layer );
977 textAttrs.m_StrokeWidth = bds.GetTextThickness( layer );
978 InferBold( &textAttrs );
979 textAttrs.m_Italic = bds.GetTextItalic( layer );
980 textAttrs.m_KeepUpright = bds.GetTextUpright( layer );
981 textAttrs.m_Mirrored = IsBackLayer( layer );
982 textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
984
986 text = new PCB_TEXT( static_cast<FOOTPRINT*>( m_frame->GetModel() ) );
987 else
988 text = new PCB_TEXT( m_frame->GetModel() );
989
990 text->SetLayer( layer );
991 text->SetAttributes( textAttrs );
992 text->SetTextPos( cursorPos );
993 text->SetFlags( IS_NEW ); // Prevent double undo commits
994
995 DIALOG_TEXT_PROPERTIES textDialog( m_frame, text );
996 bool cancelled;
997
999 [&]()
1000 {
1001 // QuasiModal required for Scintilla auto-complete
1002 cancelled = !textDialog.ShowQuasiModal();
1003 } );
1004
1005 if( cancelled || NoPrintableChars( text->GetText() ) )
1006 {
1007 delete text;
1008 text = nullptr;
1009 }
1010 else if( text->GetTextPos() != cursorPos )
1011 {
1012 // If the user modified the location then go ahead and place it there.
1013 // Otherwise we'll drag.
1014 placing = true;
1015 }
1016
1017 if( text )
1018 {
1019 if( !m_view->IsLayerVisible( text->GetLayer() ) )
1020 {
1021 m_frame->GetAppearancePanel()->SetLayerVisible( text->GetLayer(), true );
1023 }
1024
1026 m_view->Update( &selection() );
1027
1028 // update the cursor so it looks correct before another event
1029 setCursor();
1030 }
1031 }
1032
1033 if( placing )
1034 {
1035 text->ClearFlags();
1037
1038 commit.Add( text );
1039 commit.Push( _( "Draw Text" ) );
1040
1042
1043 text = nullptr;
1044 }
1045
1047
1048 // If we started with a hotkey which has a position then warp back to that.
1049 // Otherwise update to the current mouse position pinned inside the autoscroll
1050 // boundaries.
1051 if( evt->IsPrime() && !ignorePrimePosition )
1052 {
1053 cursorPos = evt->Position();
1054 m_controls->WarpMouseCursor( cursorPos, true );
1055 }
1056 else
1057 {
1059 cursorPos = m_controls->GetMousePosition();
1060 }
1061
1063
1064 m_controls->ShowCursor( true );
1065 m_controls->CaptureCursor( text != nullptr );
1066 m_controls->SetAutoPan( text != nullptr );
1067 }
1068 else if( text && ( evt->IsMotion() || evt->IsAction( &PCB_ACTIONS::refreshPreview ) ) )
1069 {
1070 text->SetPosition( cursorPos );
1071 selection().SetReferencePoint( cursorPos );
1072 m_view->Update( &selection() );
1073 }
1074 else if( text
1076 || evt->IsAction( &ACTIONS::redo ) ) )
1077 {
1078 wxBell();
1079 }
1080 else if( text && evt->IsAction( &PCB_ACTIONS::properties ) )
1081 {
1082 frame()->OnEditItemRequest( text );
1083 m_view->Update( &selection() );
1084 frame()->SetMsgPanel( text );
1085 }
1086 else
1087 {
1088 evt->SetPassEvent();
1089 }
1090 }
1091
1092 m_controls->SetAutoPan( false );
1093 m_controls->CaptureCursor( false );
1095 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1096
1097 if( selection().Empty() )
1099
1100 return 0;
1101}
1102
1103
1105{
1106 if( m_inDrawingTool )
1107 return 0;
1108
1110
1111 PCB_TABLE* table = nullptr;
1113 BOARD_COMMIT commit( m_frame );
1115
1116 // We might be running as the same shape in another co-routine. Make sure that one
1117 // gets whacked.
1119
1120 auto setCursor =
1121 [&]()
1122 {
1123 if( table )
1124 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
1125 else
1126 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1127 };
1128
1129 auto cleanup =
1130 [&] ()
1131 {
1134 m_controls->ShowCursor( true );
1135 m_controls->SetAutoPan( false );
1136 m_controls->CaptureCursor( false );
1137 delete table;
1138 table = nullptr;
1139 };
1140
1142
1143 m_frame->PushTool( aEvent );
1144
1145 Activate();
1146 // Must be done after Activate() so that it gets set into the correct context
1147 getViewControls()->ShowCursor( true );
1149 // Set initial cursor
1150 setCursor();
1151
1152 if( aEvent.HasPosition() )
1153 m_toolMgr->PrimeTool( aEvent.Position() );
1154
1155 // Main loop: keep receiving events
1156 while( TOOL_EVENT* evt = Wait() )
1157 {
1158 setCursor();
1159 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1160 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1161 VECTOR2I cursorPos =
1163 { m_frame->GetActiveLayer() }, GRID_TEXT ),
1165 m_controls->ForceCursorPosition( true, cursorPos );
1166
1167 if( evt->IsCancelInteractive() || ( table && evt->IsAction( &ACTIONS::undo ) )
1168 || evt->IsDrag() )
1169 {
1170 if( table )
1171 {
1172 cleanup();
1173 }
1174 else
1175 {
1176 m_frame->PopTool( aEvent );
1177 break;
1178 }
1179 }
1180 else if( evt->IsActivate() )
1181 {
1182 if( table )
1183 cleanup();
1184
1185 if( evt->IsMoveTool() )
1186 {
1187 // leave ourselves on the stack so we come back after the move
1188 break;
1189 }
1190 else
1191 {
1192 m_frame->PopTool( aEvent );
1193 break;
1194 }
1195 }
1196 else if( evt->IsClick( BUT_RIGHT ) )
1197 {
1198 // Warp after context menu only if dragging...
1199 if( !table )
1201
1202 m_menu->ShowContextMenu( selection() );
1203 }
1204 else if( evt->IsClick( BUT_LEFT ) )
1205 {
1206 if( !table )
1207 {
1209
1211
1212 table = new PCB_TABLE( m_frame->GetModel(), bds.GetLineThickness( layer ) );
1213 table->SetFlags( IS_NEW );
1214 table->SetLayer( layer );
1215 table->SetColCount( 1 );
1216 table->AddCell( new PCB_TABLECELL( table ) );
1217
1218 table->SetLayer( layer );
1219 table->SetPosition( cursorPos );
1220
1221 if( !m_view->IsLayerVisible( layer ) )
1222 {
1223 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1225 }
1226
1228 m_view->Update( &selection() );
1229
1230 // update the cursor so it looks correct before another event
1231 setCursor();
1232 }
1233 else
1234 {
1236
1237 table->Normalize();
1238
1240
1241 // QuasiModal required for Scintilla auto-complete
1242 if( dlg.ShowQuasiModal() == wxID_OK )
1243 {
1244 commit.Add( table, m_frame->GetScreen() );
1245 commit.Push( _( "Draw Table" ) );
1246
1249 }
1250 else
1251 {
1252 delete table;
1253 }
1254
1255 table = nullptr;
1256 }
1257 }
1258 else if( table && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
1259 {
1260 VECTOR2I fontSize = bds.GetTextSize( table->GetLayer() );
1261 VECTOR2I gridSize = grid.GetGridSize( grid.GetItemGrid( table ) );
1262 VECTOR2I origin( table->GetPosition() );
1263 VECTOR2I requestedSize( cursorPos - origin );
1264
1265 int colCount = std::max( 1, requestedSize.x / ( fontSize.x * 15 ) );
1266 int rowCount = std::max( 1, requestedSize.y / ( fontSize.y * 3 ) );
1267
1268 VECTOR2I cellSize( std::max( fontSize.x * 5, requestedSize.x / colCount ),
1269 std::max( fontSize.y * 3, requestedSize.y / rowCount ) );
1270
1271 cellSize.x = KiROUND( (double) cellSize.x / gridSize.x ) * gridSize.x;
1272 cellSize.y = KiROUND( (double) cellSize.y / gridSize.y ) * gridSize.y;
1273
1274 table->ClearCells();
1275 table->SetColCount( colCount );
1276
1277 for( int col = 0; col < colCount; ++col )
1278 table->SetColWidth( col, cellSize.x );
1279
1280 for( int row = 0; row < rowCount; ++row )
1281 {
1282 table->SetRowHeight( row, cellSize.y );
1283
1284 for( int col = 0; col < colCount; ++col )
1285 {
1286 PCB_TABLECELL* cell = new PCB_TABLECELL( table );
1287 cell->SetPosition( origin + VECTOR2I( col * cellSize.x, row * cellSize.y ) );
1288 cell->SetEnd( cell->GetPosition() + cellSize );
1289 table->AddCell( cell );
1290 }
1291 }
1292
1293 selection().SetReferencePoint( cursorPos );
1294 m_view->Update( &selection() );
1296 }
1297 else if( table && evt->IsAction( &PCB_ACTIONS::properties ) )
1298 {
1299 frame()->OnEditItemRequest( table );
1300 m_view->Update( &selection() );
1301 frame()->SetMsgPanel( table );
1302 }
1303 else if( table && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
1304 || evt->IsAction( &ACTIONS::redo ) ) )
1305 {
1306 wxBell();
1307 }
1308 else
1309 {
1310 evt->SetPassEvent();
1311 }
1312
1313 // Enable autopanning and cursor capture only when there is a shape being drawn
1314 getViewControls()->SetAutoPan( table != nullptr );
1315 getViewControls()->CaptureCursor( table != nullptr );
1316 }
1317
1318 getViewControls()->SetAutoPan( false );
1319 getViewControls()->CaptureCursor( false );
1320 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1321 return 0;
1322}
1323
1324
1326{
1327 const VECTOR2I lineVector{ aDim->GetEnd() - aDim->GetStart() };
1328
1329 aDim->SetEnd( aDim->GetStart() + GetVectorSnapped45( lineVector ) );
1330 aDim->Update();
1331}
1332
1333
1335{
1337 return 0;
1338
1339 if( m_inDrawingTool )
1340 return 0;
1341
1343
1344 enum DIMENSION_STEPS
1345 {
1346 SET_ORIGIN = 0,
1347 SET_END,
1348 SET_HEIGHT,
1349 FINISHED
1350 };
1351
1352 TOOL_EVENT originalEvent = aEvent;
1353 PCB_DIMENSION_BASE* dimension = nullptr;
1354 BOARD_COMMIT commit( m_frame );
1357 PCB_SELECTION preview; // A VIEW_GROUP that serves as a preview for the new item(s)
1358 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DIMENSION );
1359 int step = SET_ORIGIN;
1361
1362 m_view->Add( &preview );
1363
1364 auto cleanup =
1365 [&]()
1366 {
1367 m_controls->SetAutoPan( false );
1368 m_controls->CaptureCursor( false );
1370
1371 preview.Clear();
1372 m_view->Update( &preview );
1373
1374 delete dimension;
1375 dimension = nullptr;
1376 step = SET_ORIGIN;
1377 };
1378
1379 auto setCursor =
1380 [&]()
1381 {
1382 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
1383 };
1384
1386
1387 m_frame->PushTool( aEvent );
1388
1389 Activate();
1390 // Must be done after Activate() so that it gets set into the correct context
1391 m_controls->ShowCursor( true );
1393 // Set initial cursor
1394 setCursor();
1395
1397
1398 if( aEvent.HasPosition() )
1399 m_toolMgr->PrimeTool( aEvent.Position() );
1400
1401 // Main loop: keep receiving events
1402 while( TOOL_EVENT* evt = Wait() )
1403 {
1404 if( step > SET_ORIGIN )
1405 frame()->SetMsgPanel( dimension );
1406
1407 setCursor();
1408
1409 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1410 bool is45Limited = Is45Limited() && !evt->Modifier( MD_CTRL );
1411 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1412
1413 if( step == SET_HEIGHT && t != PCB_DIM_ORTHOGONAL_T )
1414 {
1415 if( dimension->GetStart().x != dimension->GetEnd().x
1416 && dimension->GetStart().y != dimension->GetEnd().y )
1417 {
1418 // Not cardinal. Grid snapping doesn't make sense for height.
1419 grid.SetUseGrid( false );
1420 }
1421 }
1422
1423 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
1424 cursorPos = GetClampedCoords( grid.BestSnapAnchor( cursorPos, nullptr, GRID_GRAPHICS ),
1426
1427 m_controls->ForceCursorPosition( true, cursorPos );
1428
1429 if( evt->IsCancelInteractive() || ( dimension && evt->IsAction( &ACTIONS::undo ) ) )
1430 {
1431 m_controls->SetAutoPan( false );
1432
1433 if( step != SET_ORIGIN ) // start from the beginning
1434 {
1435 cleanup();
1436 }
1437 else
1438 {
1439 m_frame->PopTool( aEvent );
1440 break;
1441 }
1442 }
1443 else if( evt->IsActivate() )
1444 {
1445 if( step != SET_ORIGIN )
1446 cleanup();
1447
1448 if( evt->IsPointEditor() )
1449 {
1450 // don't exit (the point editor runs in the background)
1451 }
1452 else if( evt->IsMoveTool() )
1453 {
1454 // leave ourselves on the stack so we come back after the move
1455 break;
1456 }
1457 else
1458 {
1459 m_frame->PopTool( aEvent );
1460 break;
1461 }
1462 }
1463 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) && step != SET_ORIGIN )
1464 {
1466 dimension->SetLineThickness( m_stroke.GetWidth() );
1467 m_view->Update( &preview );
1468 frame()->SetMsgPanel( dimension );
1469 }
1470 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && step != SET_ORIGIN )
1471 {
1472 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
1473 {
1475 dimension->SetLineThickness( m_stroke.GetWidth() );
1476 m_view->Update( &preview );
1477 frame()->SetMsgPanel( dimension );
1478 }
1479 }
1480 else if( evt->IsClick( BUT_RIGHT ) )
1481 {
1482 if( !dimension )
1484
1485 m_menu->ShowContextMenu( selection() );
1486 }
1487 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1488 {
1489 switch( step )
1490 {
1491 case SET_ORIGIN:
1492 {
1494
1496
1497 // Init the new item attributes
1498 auto setMeasurementAttributes =
1499 [&]( PCB_DIMENSION_BASE* aDim )
1500 {
1501 aDim->SetUnitsMode( boardSettings.m_DimensionUnitsMode );
1502 aDim->SetUnitsFormat( boardSettings.m_DimensionUnitsFormat );
1503 aDim->SetPrecision( boardSettings.m_DimensionPrecision );
1504 aDim->SetSuppressZeroes( boardSettings.m_DimensionSuppressZeroes );
1505 aDim->SetTextPositionMode( boardSettings.m_DimensionTextPosition );
1506 aDim->SetKeepTextAligned( boardSettings.m_DimensionKeepTextAligned );
1507 };
1508
1509 if( originalEvent.IsAction( &PCB_ACTIONS::drawAlignedDimension ) )
1510 {
1511 dimension = new PCB_DIM_ALIGNED( m_frame->GetModel() );
1512 setMeasurementAttributes( dimension );
1513 }
1514 else if( originalEvent.IsAction( &PCB_ACTIONS::drawOrthogonalDimension ) )
1515 {
1516 dimension = new PCB_DIM_ORTHOGONAL( m_frame->GetModel() );
1517 setMeasurementAttributes( dimension );
1518 }
1519 else if( originalEvent.IsAction( &PCB_ACTIONS::drawCenterDimension ) )
1520 {
1521 dimension = new PCB_DIM_CENTER( m_frame->GetModel() );
1522 }
1523 else if( originalEvent.IsAction( &PCB_ACTIONS::drawRadialDimension ) )
1524 {
1525 dimension = new PCB_DIM_RADIAL( m_frame->GetModel() );
1526 setMeasurementAttributes( dimension );
1527 }
1528 else if( originalEvent.IsAction( &PCB_ACTIONS::drawLeader ) )
1529 {
1530 dimension = new PCB_DIM_LEADER( m_frame->GetModel() );
1531 dimension->SetTextPos( cursorPos );
1532 }
1533 else
1534 {
1535 wxFAIL_MSG( wxT( "Unhandled action in DRAWING_TOOL::DrawDimension" ) );
1536 }
1537
1538 t = dimension->Type();
1539
1540 dimension->SetLayer( layer );
1541 dimension->SetMirrored( IsBackLayer( layer ) );
1542 dimension->SetTextSize( boardSettings.GetTextSize( layer ) );
1543 dimension->SetTextThickness( boardSettings.GetTextThickness( layer ) );
1544 dimension->SetItalic( boardSettings.GetTextItalic( layer ) );
1545 dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
1546 dimension->SetArrowLength( boardSettings.m_DimensionArrowLength );
1547 dimension->SetExtensionOffset( boardSettings.m_DimensionExtensionOffset );
1548 dimension->SetStart( cursorPos );
1549 dimension->SetEnd( cursorPos );
1550 dimension->Update();
1551
1552 if( !m_view->IsLayerVisible( layer ) )
1553 {
1554 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1556 }
1557
1558 preview.Add( dimension );
1559 frame()->SetMsgPanel( dimension );
1560
1561 m_controls->SetAutoPan( true );
1562 m_controls->CaptureCursor( true );
1563 break;
1564 }
1565
1566 case SET_END:
1567 // Dimensions that have origin and end in the same spot are not valid
1568 if( dimension->GetStart() == dimension->GetEnd() )
1569 {
1570 --step;
1571 break;
1572 }
1573
1574 if( t != PCB_DIM_CENTER_T && t != PCB_DIM_RADIAL_T && t != PCB_DIM_LEADER_T )
1575 {
1576 break;
1577 }
1578
1579 ++step;
1581 case SET_HEIGHT:
1582 assert( dimension->GetStart() != dimension->GetEnd() );
1583 assert( dimension->GetLineThickness() > 0 );
1584
1585 preview.Remove( dimension );
1586
1587 commit.Add( dimension );
1588 commit.Push( _( "Draw Dimension" ) );
1589
1590 // Run the edit immediately to set the leader text
1591 if( t == PCB_DIM_LEADER_T )
1592 frame()->OnEditItemRequest( dimension );
1593
1595
1596 break;
1597 }
1598
1599 if( ++step >= FINISHED )
1600 {
1601 dimension = nullptr;
1602 step = SET_ORIGIN;
1603 m_controls->SetAutoPan( false );
1604 m_controls->CaptureCursor( false );
1605 }
1606 else if( evt->IsDblClick( BUT_LEFT ) )
1607 {
1609 }
1610 }
1611 else if( evt->IsMotion() )
1612 {
1613 switch( step )
1614 {
1615 case SET_END:
1616 dimension->SetEnd( cursorPos );
1617
1618 if( is45Limited || t == PCB_DIM_CENTER_T )
1619 constrainDimension( dimension );
1620
1621 if( t == PCB_DIM_ORTHOGONAL_T )
1622 {
1623 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
1624
1625 BOX2I bounds( dimension->GetStart(),
1626 dimension->GetEnd() - dimension->GetStart() );
1627
1628 // Create a nice preview by measuring the longer dimension
1629 bool vert = bounds.GetWidth() < bounds.GetHeight();
1630
1631 ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
1633 }
1634 else if( t == PCB_DIM_RADIAL_T )
1635 {
1636 PCB_DIM_RADIAL* radialDim = static_cast<PCB_DIM_RADIAL*>( dimension );
1637 VECTOR2I textOffset( radialDim->GetArrowLength() * 10, 0 );
1638
1639 if( radialDim->GetEnd().x < radialDim->GetStart().x )
1640 textOffset = -textOffset;
1641
1642 radialDim->SetTextPos( radialDim->GetKnee() + textOffset );
1643 }
1644 else if( t == PCB_DIM_LEADER_T )
1645 {
1646 VECTOR2I textOffset( dimension->GetArrowLength() * 10, 0 );
1647
1648 if( dimension->GetEnd().x < dimension->GetStart().x )
1649 textOffset = -textOffset;
1650
1651 dimension->SetTextPos( dimension->GetEnd() + textOffset );
1652 }
1653
1654 dimension->Update();
1655 break;
1656
1657 case SET_HEIGHT:
1658 if( t == PCB_DIM_ALIGNED_T )
1659 {
1660 PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dimension );
1661
1662 // Calculating the direction of travel perpendicular to the selected axis
1663 double angle = aligned->GetAngle() + ( M_PI / 2 );
1664
1665 VECTOR2I delta( (VECTOR2I) cursorPos - dimension->GetEnd() );
1666 double height = ( delta.x * cos( angle ) ) + ( delta.y * sin( angle ) );
1667 aligned->SetHeight( height );
1668 aligned->Update();
1669 }
1670 else if( t == PCB_DIM_ORTHOGONAL_T )
1671 {
1672 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
1673
1674 BOX2I bbox( dimension->GetStart(),
1675 dimension->GetEnd() - dimension->GetStart() );
1676 VECTOR2I direction( cursorPos - bbox.Centre() );
1677 bool vert;
1678
1679 // Only change the orientation when we move outside the bbox
1680 if( !bbox.Contains( cursorPos ) )
1681 {
1682 // If the dimension is horizontal or vertical, set correct orientation
1683 // otherwise, test if we're left/right of the bounding box or above/below it
1684 if( bbox.GetWidth() == 0 )
1685 vert = true;
1686 else if( bbox.GetHeight() == 0 )
1687 vert = false;
1688 else if( cursorPos.x > bbox.GetLeft() && cursorPos.x < bbox.GetRight() )
1689 vert = false;
1690 else if( cursorPos.y > bbox.GetTop() && cursorPos.y < bbox.GetBottom() )
1691 vert = true;
1692 else
1693 vert = std::abs( direction.y ) < std::abs( direction.x );
1694
1695 ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
1697 }
1698 else
1699 {
1700 vert = ortho->GetOrientation() == PCB_DIM_ORTHOGONAL::DIR::VERTICAL;
1701 }
1702
1703 VECTOR2I heightVector( cursorPos - dimension->GetStart() );
1704 ortho->SetHeight( vert ? heightVector.x : heightVector.y );
1705 ortho->Update();
1706 }
1707
1708 break;
1709 }
1710
1711 // Show a preview of the item
1712 m_view->Update( &preview );
1713 }
1714 else if( dimension && evt->IsAction( &PCB_ACTIONS::layerChanged ) )
1715 {
1717
1718 if( !m_view->IsLayerVisible( layer ) )
1719 {
1720 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1722 }
1723
1724 dimension->SetLayer( layer );
1725 dimension->SetTextSize( boardSettings.GetTextSize( layer ) );
1726 dimension->SetTextThickness( boardSettings.GetTextThickness( layer ) );
1727 dimension->SetItalic( boardSettings.GetTextItalic( layer ) );
1728 dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
1729 dimension->Update();
1730
1731 m_view->Update( &preview );
1732 frame()->SetMsgPanel( dimension );
1733 }
1734 else if( dimension && evt->IsAction( &PCB_ACTIONS::properties ) )
1735 {
1736 if( step == SET_END || step == SET_HEIGHT )
1737 {
1738 frame()->OnEditItemRequest( dimension );
1739 dimension->Update();
1740 frame()->SetMsgPanel( dimension );
1741 break;
1742 }
1743 else
1744 {
1745 wxBell();
1746 }
1747 }
1748 else if( dimension && evt->IsAction( &PCB_ACTIONS::changeDimensionArrows ) )
1749 {
1750 switch( dimension->Type() )
1751 {
1752 case PCB_DIM_ALIGNED_T:
1754 case PCB_DIM_RADIAL_T:
1755 if( dimension->GetArrowDirection() == DIM_ARROW_DIRECTION::INWARD )
1756 dimension->SetArrowDirection( DIM_ARROW_DIRECTION::OUTWARD );
1757 else
1758 dimension->SetArrowDirection( DIM_ARROW_DIRECTION::INWARD );
1759 break;
1760 default:
1761 // Other dimension types don't have arrows that can swap
1762 wxBell();
1763 }
1764
1765 m_view->Update( &preview );
1766 }
1767 else if( dimension && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
1768 || evt->IsAction( &ACTIONS::redo ) ) )
1769 {
1770 wxBell();
1771 }
1772 else
1773 {
1774 evt->SetPassEvent();
1775 }
1776 }
1777
1778 if( step != SET_ORIGIN )
1779 delete dimension;
1780
1781 m_controls->SetAutoPan( false );
1783 m_controls->CaptureCursor( false );
1784 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1785
1786 m_view->Remove( &preview );
1787
1788 if( selection().Empty() )
1790
1791 return 0;
1792}
1793
1794
1796{
1797 if( !m_frame->GetModel() )
1798 return 0;
1799
1800 if( m_inDrawingTool )
1801 return 0;
1802
1804
1806 int dlgResult = dlg.ShowModal();
1807
1808 std::list<std::unique_ptr<EDA_ITEM>>& list = dlg.GetImportedItems();
1809
1810 if( dlgResult != wxID_OK )
1811 return 0;
1812
1813 // Ensure the list is not empty:
1814 if( list.empty() )
1815 {
1816 wxMessageBox( _( "No graphic items found in file.") );
1817 return 0;
1818 }
1819
1821
1822 std::vector<BOARD_ITEM*> newItems; // all new items, including group
1823 std::vector<BOARD_ITEM*> selectedItems; // the group, or newItems if no group
1824 PCB_SELECTION preview;
1825 BOARD_COMMIT commit( m_frame );
1826 PCB_GROUP* group = nullptr;
1827 PCB_LAYER_ID layer = F_Cu;
1828
1829 if( dlg.ShouldGroupItems() )
1830 {
1831 group = new PCB_GROUP( m_frame->GetModel() );
1832
1833 newItems.push_back( group );
1834 selectedItems.push_back( group );
1835 preview.Add( group );
1836 }
1837
1838 if( dlg.ShouldFixDiscontinuities() )
1839 {
1840 std::vector<PCB_SHAPE*> shapeList;
1841 std::vector<std::unique_ptr<PCB_SHAPE>> newShapes;
1842
1843 for( const std::unique_ptr<EDA_ITEM>& ptr : list )
1844 {
1845 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( ptr.get() ) )
1846 shapeList.push_back( shape );
1847 }
1848
1849 ConnectBoardShapes( shapeList, newShapes, dlg.GetTolerance() );
1850
1851 for( std::unique_ptr<PCB_SHAPE>& ptr : newShapes )
1852 {
1853 ptr->SetParent( m_frame->GetBoard() );
1854 list.push_back( std::move( ptr ) );
1855 }
1856 }
1857
1858 for( std::unique_ptr<EDA_ITEM>& ptr : list )
1859 {
1860 EDA_ITEM* eda_item = ptr.release();
1861
1862 if( eda_item->IsBOARD_ITEM() )
1863 {
1864 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( eda_item );
1865
1866 newItems.push_back( item );
1867
1868 if( group )
1869 group->AddItem( item );
1870 else
1871 selectedItems.push_back( item );
1872
1873 layer = item->GetLayer();
1874 }
1875
1876 preview.Add( eda_item );
1877 }
1878
1879 // Clear the current selection then select the drawings so that edit tools work on them
1881
1882 EDA_ITEMS selItems( selectedItems.begin(), selectedItems.end() );
1884
1885 if( !dlg.IsPlacementInteractive() )
1886 {
1887 for( BOARD_ITEM* item : newItems )
1888 commit.Add( item );
1889
1890 commit.Push( _( "Import Graphics" ) );
1891
1892 return 0;
1893 }
1894
1895 // Turn shapes on if they are off, so that the created object will be visible after completion
1897
1898 if( !m_view->IsLayerVisible( layer ) )
1899 {
1900 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1902 }
1903
1904 m_view->Add( &preview );
1905
1906 m_frame->PushTool( aEvent );
1907
1908 auto setCursor =
1909 [&]()
1910 {
1911 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
1912 };
1913
1914 Activate();
1915 // Must be done after Activate() so that it gets set into the correct context
1916 m_controls->ShowCursor( true );
1918 // Set initial cursor
1919 setCursor();
1920
1921 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );
1923
1924 // Now move the new items to the current cursor position:
1925 VECTOR2I cursorPos = m_controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
1926 VECTOR2I delta = cursorPos - static_cast<BOARD_ITEM*>( preview.GetTopLeftItem() )->GetPosition();
1927
1928 for( BOARD_ITEM* item : selectedItems )
1929 item->Move( delta );
1930
1931 m_view->Update( &preview );
1932
1933 // Main loop: keep receiving events
1934 while( TOOL_EVENT* evt = Wait() )
1935 {
1936 setCursor();
1937
1938 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1939 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1940 cursorPos = GetClampedCoords(
1941 grid.BestSnapAnchor( m_controls->GetMousePosition(), { layer }, GRID_GRAPHICS ),
1943 m_controls->ForceCursorPosition( true, cursorPos );
1944
1945 if( evt->IsCancelInteractive() || evt->IsActivate() )
1946 {
1948
1949 if( group )
1950 preview.Remove( group );
1951
1952 for( BOARD_ITEM* item : newItems )
1953 delete item;
1954
1955 break;
1956 }
1957 else if( evt->IsMotion() )
1958 {
1959 delta = cursorPos - static_cast<BOARD_ITEM*>( preview.GetTopLeftItem() )->GetPosition();
1960
1961 for( BOARD_ITEM* item : selectedItems )
1962 item->Move( delta );
1963
1964 m_view->Update( &preview );
1965 }
1966 else if( evt->IsClick( BUT_RIGHT ) )
1967 {
1968 m_menu->ShowContextMenu( selection() );
1969 }
1970 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1971 {
1972 // Place the imported drawings
1973 for( BOARD_ITEM* item : newItems )
1974 commit.Add( item );
1975
1976 commit.Push( _( "Import Graphics" ) );
1977
1978 break; // This is a one-shot command, not a tool
1979 }
1980 else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
1981 {
1982 wxBell();
1983 }
1984 else
1985 {
1986 evt->SetPassEvent();
1987 }
1988 }
1989
1990 preview.Clear();
1991 m_view->Remove( &preview );
1992
1993 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1995
1996 m_frame->PopTool( aEvent );
1997
1998 return 0;
1999}
2000
2001
2003{
2004 // Make sense only in FP editor
2005 if( !m_isFootprintEditor )
2006 return 0;
2007
2008 if( !m_frame->GetModel() )
2009 return 0;
2010
2011 if( m_inDrawingTool )
2012 return 0;
2013
2015
2016 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ANCHOR );
2018
2020
2021 m_frame->PushTool( aEvent );
2022
2023 auto setCursor =
2024 [&]()
2025 {
2026 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
2027 };
2028
2029 Activate();
2030 // Must be done after Activate() so that it gets set into the correct context
2031 m_controls->ShowCursor( true );
2032 m_controls->SetAutoPan( true );
2033 m_controls->CaptureCursor( false );
2035 // Set initial cursor
2036 setCursor();
2037
2038 while( TOOL_EVENT* evt = Wait() )
2039 {
2040 setCursor();
2041
2042 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2043 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2044 VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(),
2046 m_controls->ForceCursorPosition( true, cursorPos );
2047
2048 if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
2049 {
2051 BOARD_COMMIT commit( m_frame );
2052 commit.Modify( footprint );
2053
2054 // set the new relative internal local coordinates of footprint items
2055 VECTOR2I moveVector = footprint->GetPosition() - cursorPos;
2056 footprint->MoveAnchorPosition( moveVector );
2057
2058 commit.Push( _( "Move Footprint Anchor" ) );
2059
2060 // Usually, we do not need to change twice the anchor position,
2061 // so deselect the active tool
2062 m_frame->PopTool( aEvent );
2063 break;
2064 }
2065 else if( evt->IsClick( BUT_RIGHT ) )
2066 {
2067 m_menu->ShowContextMenu( selection() );
2068 }
2069 else if( evt->IsCancelInteractive() || evt->IsActivate() )
2070 {
2071 m_frame->PopTool( aEvent );
2072 break;
2073 }
2074 else
2075 {
2076 evt->SetPassEvent();
2077 }
2078 }
2079
2080 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2082
2083 return 0;
2084}
2085
2086
2091 PCB_SHAPE* aGraphic )
2092{
2093 if( !aMgr.IsReset() )
2094 {
2095 aGraphic->SetStart( aMgr.GetOrigin() );
2096 aGraphic->SetEnd( aMgr.GetEnd() );
2097 }
2098}
2099
2100
2101bool DRAWING_TOOL::drawShape( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
2102 std::optional<VECTOR2D> aStartingPoint,
2103 std::stack<PCB_SHAPE*>* aCommittedGraphics )
2104{
2105 SHAPE_T shape = ( *aGraphic )->GetShape();
2106
2107 // Only three shapes are currently supported
2108 wxASSERT( shape == SHAPE_T::SEGMENT || shape == SHAPE_T::CIRCLE || shape == SHAPE_T::RECTANGLE );
2109
2111 EDA_UNITS userUnits = m_frame->GetUserUnits();
2113 PCB_SHAPE*& graphic = *aGraphic;
2114
2115 if( m_layer != m_frame->GetActiveLayer() )
2116 {
2119 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2120 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2121
2130 }
2131
2132 // Turn shapes on if they are off, so that the created object will be visible after completion
2134
2135 // geometric construction manager
2137
2138 // drawing assistant overlay
2139 // TODO: workaround because EDA_SHAPE_TYPE_T is not visible from commons.
2140 KIGFX::PREVIEW::GEOM_SHAPE geomShape( static_cast<KIGFX::PREVIEW::GEOM_SHAPE>( shape ) );
2141 KIGFX::PREVIEW::TWO_POINT_ASSISTANT twoPointAsst( twoPointMgr, pcbIUScale, userUnits, geomShape );
2142
2143 // Add a VIEW_GROUP that serves as a preview for the new item
2144 m_preview.Clear();
2145 m_view->Add( &m_preview );
2146 m_view->Add( &twoPointAsst );
2147
2148 bool started = false;
2149 bool cancelled = false;
2150 bool isLocalOriginSet = ( m_frame->GetScreen()->m_LocalOrigin != VECTOR2D( 0, 0 ) );
2151 VECTOR2I cursorPos = m_controls->GetMousePosition();
2152
2153 auto setCursor =
2154 [&]()
2155 {
2156 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2157 };
2158
2159 auto cleanup =
2160 [&]()
2161 {
2162 m_preview.Clear();
2163 m_view->Update( &m_preview );
2164 delete graphic;
2165 graphic = nullptr;
2166
2167 if( !isLocalOriginSet )
2169 };
2170
2171 m_controls->ShowCursor( true );
2173 // Set initial cursor
2174 setCursor();
2175
2177
2178 if( aStartingPoint )
2179 m_toolMgr->PrimeTool( *aStartingPoint );
2180
2181 // Main loop: keep receiving events
2182 while( TOOL_EVENT* evt = Wait() )
2183 {
2184 setCursor();
2185
2186 if( started )
2187 m_frame->SetMsgPanel( graphic );
2188
2189 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2190 bool is45Limited = Is45Limited() && !evt->Modifier( MD_CTRL );
2191
2192 // Rectangular shapes never get 45-degree snapping
2193 if( shape == SHAPE_T::RECTANGLE )
2194 is45Limited = false;
2195
2196 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2197 cursorPos = GetClampedCoords(
2198 grid.BestSnapAnchor( m_controls->GetMousePosition(), { m_layer }, GRID_GRAPHICS ),
2200 m_controls->ForceCursorPosition( true, cursorPos );
2201
2202 if( evt->IsCancelInteractive() || ( started && evt->IsAction( &ACTIONS::undo ) ) )
2203 {
2204 cleanup();
2205
2206 if( !started )
2207 {
2208 // We've handled the cancel event. Don't cancel other tools
2209 evt->SetPassEvent( false );
2210 m_frame->PopTool( aTool );
2211 cancelled = true;
2212 }
2213
2214 break;
2215 }
2216 else if( evt->IsActivate() )
2217 {
2218 if( evt->IsPointEditor() )
2219 {
2220 // don't exit (the point editor runs in the background)
2221 }
2222 else if( evt->IsMoveTool() )
2223 {
2224 cleanup();
2225 // leave ourselves on the stack so we come back after the move
2226 cancelled = true;
2227 break;
2228 }
2229 else
2230 {
2231 cleanup();
2232 m_frame->PopTool( aTool );
2233 cancelled = true;
2234 break;
2235 }
2236 }
2237 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2238 {
2239 if( m_layer != m_frame->GetActiveLayer() )
2240 {
2243 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2244 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2245
2254 }
2255
2256 if( graphic )
2257 {
2258 if( !m_view->IsLayerVisible( m_layer ) )
2259 {
2262 }
2263
2264 graphic->SetLayer( m_layer );
2265 graphic->SetStroke( m_stroke );
2266
2267 if( PCB_TEXTBOX* pcb_textbox = dynamic_cast<PCB_TEXTBOX*>( graphic ) )
2268 pcb_textbox->SetAttributes( m_textAttrs );
2269
2270 m_view->Update( &m_preview );
2271 frame()->SetMsgPanel( graphic );
2272 }
2273 else
2274 {
2275 evt->SetPassEvent();
2276 }
2277 }
2278 else if( evt->IsClick( BUT_RIGHT ) )
2279 {
2280 if( !graphic )
2282
2283 m_menu->ShowContextMenu( selection() );
2284 }
2285 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
2286 {
2287 if( !graphic )
2288 break;
2289
2290 if( !started )
2291 {
2293
2294 if( aStartingPoint )
2295 {
2296 cursorPos = *aStartingPoint;
2297 aStartingPoint = std::nullopt;
2298 }
2299
2300 // Init the new item attributes
2301 if( graphic ) // always true, but Coverity can't seem to figure that out
2302 {
2303 graphic->SetShape( static_cast<SHAPE_T>( shape ) );
2304 graphic->SetFilled( false );
2305 graphic->SetStroke( m_stroke );
2306 graphic->SetLayer( m_layer );
2307 }
2308
2309 if( PCB_TEXTBOX* pcb_textbox = dynamic_cast<PCB_TEXTBOX*>( graphic ) )
2310 pcb_textbox->SetAttributes( m_textAttrs );
2311
2312 grid.SetSkipPoint( cursorPos );
2313
2314 twoPointMgr.SetOrigin( cursorPos );
2315 twoPointMgr.SetEnd( cursorPos );
2316
2317 if( !isLocalOriginSet )
2318 m_frame->GetScreen()->m_LocalOrigin = cursorPos;
2319
2320 m_preview.Add( graphic );
2321 frame()->SetMsgPanel( graphic );
2322 m_controls->SetAutoPan( true );
2323 m_controls->CaptureCursor( true );
2324
2325 if( !m_view->IsLayerVisible( m_layer ) )
2326 {
2329 }
2330
2331 updateSegmentFromGeometryMgr( twoPointMgr, graphic );
2332
2333 started = true;
2334 }
2335 else
2336 {
2337 PCB_SHAPE* snapItem = dynamic_cast<PCB_SHAPE*>( grid.GetSnapped() );
2338
2339 if( shape == SHAPE_T::SEGMENT && snapItem && graphic->GetLength() > 0 )
2340 {
2341 // User has clicked on the end of an existing segment, closing a path
2342 BOARD_COMMIT commit( m_frame );
2343
2344 commit.Add( graphic );
2345 commit.Push( _( "Draw Line" ) );
2347
2348 graphic = nullptr;
2349 }
2350 else if( twoPointMgr.IsEmpty() || evt->IsDblClick( BUT_LEFT ) )
2351 {
2352 // User has clicked twice in the same spot, meaning we're finished
2353 delete graphic;
2354 graphic = nullptr;
2355 }
2356
2357 m_preview.Clear();
2358 twoPointMgr.Reset();
2359 break;
2360 }
2361
2362 twoPointMgr.SetEnd( GetClampedCoords( cursorPos ) );
2363 }
2364 else if( evt->IsMotion() )
2365 {
2366 VECTOR2I clampedCursorPos = cursorPos;
2367
2368 if( shape == SHAPE_T::CIRCLE || shape == SHAPE_T::ARC )
2369 clampedCursorPos = getClampedRadiusEnd( twoPointMgr.GetOrigin(), cursorPos );
2370 else
2371 clampedCursorPos = getClampedDifferenceEnd( twoPointMgr.GetOrigin(), cursorPos );
2372
2373 // 45 degree lines
2374 if( started && is45Limited )
2375 {
2376 const VECTOR2I lineVector( clampedCursorPos - VECTOR2I( twoPointMgr.GetOrigin() ) );
2377
2378 // get a restricted 45/H/V line from the last fixed point to the cursor
2379 VECTOR2I newEnd = GetVectorSnapped45( lineVector, ( shape == SHAPE_T::RECTANGLE ) );
2380 m_controls->ForceCursorPosition( true, VECTOR2I( twoPointMgr.GetEnd() ) );
2381 twoPointMgr.SetEnd( twoPointMgr.GetOrigin() + newEnd );
2382 twoPointMgr.SetAngleSnap( true );
2383 }
2384 else
2385 {
2386 twoPointMgr.SetEnd( clampedCursorPos );
2387 twoPointMgr.SetAngleSnap( false );
2388 }
2389
2390 updateSegmentFromGeometryMgr( twoPointMgr, graphic );
2391 m_view->Update( &m_preview );
2392 m_view->Update( &twoPointAsst );
2393 }
2394 else if( started && ( evt->IsAction( &PCB_ACTIONS::doDelete )
2395 || evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) ) )
2396 {
2397 if( aCommittedGraphics && !aCommittedGraphics->empty() )
2398 {
2399 twoPointMgr.SetOrigin( aCommittedGraphics->top()->GetStart() );
2400 twoPointMgr.SetEnd( aCommittedGraphics->top()->GetEnd() );
2401 aCommittedGraphics->pop();
2402
2403 getViewControls()->WarpMouseCursor( twoPointMgr.GetEnd(), true );
2404
2406 {
2409 delete undo;
2410 }
2411
2412 updateSegmentFromGeometryMgr( twoPointMgr, graphic );
2413 m_view->Update( &m_preview );
2414 m_view->Update( &twoPointAsst );
2415 }
2416 else
2417 {
2418 cleanup();
2419 break;
2420 }
2421 }
2422 else if( graphic && evt->IsAction( &PCB_ACTIONS::incWidth ) )
2423 {
2425 graphic->SetStroke( m_stroke );
2426 m_view->Update( &m_preview );
2427 frame()->SetMsgPanel( graphic );
2428 }
2429 else if( graphic && evt->IsAction( &PCB_ACTIONS::decWidth ) )
2430 {
2431 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
2432 {
2434 graphic->SetStroke( m_stroke );
2435 m_view->Update( &m_preview );
2436 frame()->SetMsgPanel( graphic );
2437 }
2438 }
2439 else if( started && evt->IsAction( &PCB_ACTIONS::properties ) )
2440 {
2441 frame()->OnEditItemRequest( graphic );
2442 m_view->Update( &m_preview );
2443 frame()->SetMsgPanel( graphic );
2444 }
2445 else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
2446 || evt->IsAction( &ACTIONS::redo ) ) )
2447 {
2448 wxBell();
2449 }
2450 else if( evt->IsAction( &ACTIONS::resetLocalCoords ) )
2451 {
2452 isLocalOriginSet = true;
2453 evt->SetPassEvent();
2454 }
2455 else if( evt->IsAction( &ACTIONS::updateUnits ) )
2456 {
2457 if( frame()->GetUserUnits() != userUnits )
2458 {
2459 userUnits = frame()->GetUserUnits();
2460 twoPointAsst.SetUnits( userUnits );
2461 m_view->Update( &twoPointAsst );
2462 }
2463 evt->SetPassEvent();
2464 }
2465 else
2466 {
2467 evt->SetPassEvent();
2468 }
2469 }
2470
2471 if( !isLocalOriginSet ) // reset the relative coordinate if it was not set before
2473
2474 m_view->Remove( &twoPointAsst );
2475 m_view->Remove( &m_preview );
2476
2477 if( selection().Empty() )
2479
2480 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2481 m_controls->SetAutoPan( false );
2482 m_controls->CaptureCursor( false );
2484
2485 return !cancelled;
2486}
2487
2488
2493 PCB_SHAPE& aArc )
2494{
2495 VECTOR2I vec = aMgr.GetOrigin();
2496
2497 aArc.SetCenter( vec );
2498
2499 if( aMgr.GetSubtended() < ANGLE_0 )
2500 {
2501 vec = aMgr.GetStartRadiusEnd();
2502 aArc.SetStart( vec );
2503 vec = aMgr.GetEndRadiusEnd();
2504 aArc.SetEnd( vec );
2505 }
2506 else
2507 {
2508 vec = aMgr.GetEndRadiusEnd();
2509 aArc.SetStart( vec );
2510 vec = aMgr.GetStartRadiusEnd();
2511 aArc.SetEnd( vec );
2512 }
2513}
2514
2515
2516bool DRAWING_TOOL::drawArc( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
2517 std::optional<VECTOR2D> aStartingPoint )
2518{
2519 wxCHECK( aGraphic, false );
2520
2521 PCB_SHAPE*& graphic = *aGraphic;
2522
2523 wxCHECK( graphic, false );
2524
2525 if( m_layer != m_frame->GetActiveLayer() )
2526 {
2529 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2530 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2531 }
2532
2533 // Arc geometric construction manager
2535
2536 // Arc drawing assistant overlay
2538
2539 // Add a VIEW_GROUP that serves as a preview for the new item
2540 PCB_SELECTION preview;
2541 m_view->Add( &preview );
2542 m_view->Add( &arcAsst );
2544
2545 auto setCursor =
2546 [&]()
2547 {
2548 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2549 };
2550
2551 auto cleanup =
2552 [&] ()
2553 {
2554 preview.Clear();
2555 delete *aGraphic;
2556 *aGraphic = nullptr;
2557 };
2558
2559 m_controls->ShowCursor( true );
2561 // Set initial cursor
2562 setCursor();
2563
2564 bool started = false;
2565 bool cancelled = false;
2566
2568
2569 if( aStartingPoint )
2570 m_toolMgr->PrimeTool( *aStartingPoint );
2571
2572 // Main loop: keep receiving events
2573 while( TOOL_EVENT* evt = Wait() )
2574 {
2575 if( started )
2576 m_frame->SetMsgPanel( graphic );
2577
2578 setCursor();
2579
2580 graphic->SetLayer( m_layer );
2581
2582 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2583 bool is45Limited = Is45Limited() && !evt->Modifier( MD_CTRL );
2584 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2585 VECTOR2I cursorPos = GetClampedCoords(
2586 grid.BestSnapAnchor( m_controls->GetMousePosition(), graphic, GRID_GRAPHICS ),
2588 m_controls->ForceCursorPosition( true, cursorPos );
2589
2590 if( evt->IsCancelInteractive() || ( started && evt->IsAction( &ACTIONS::undo ) ) )
2591 {
2592 cleanup();
2593
2594 if( !started )
2595 {
2596 // We've handled the cancel event. Don't cancel other tools
2597 evt->SetPassEvent( false );
2598 m_frame->PopTool( aTool );
2599 cancelled = true;
2600 }
2601
2602 break;
2603 }
2604 else if( evt->IsActivate() )
2605 {
2606 if( evt->IsPointEditor() )
2607 {
2608 // don't exit (the point editor runs in the background)
2609 }
2610 else if( evt->IsMoveTool() )
2611 {
2612 cleanup();
2613 // leave ourselves on the stack so we come back after the move
2614 cancelled = true;
2615 break;
2616 }
2617 else
2618 {
2619 cleanup();
2620 m_frame->PopTool( aTool );
2621 cancelled = true;
2622 break;
2623 }
2624 }
2625 else if( evt->IsClick( BUT_LEFT ) )
2626 {
2627 if( !started )
2628 {
2630
2631 m_controls->SetAutoPan( true );
2632 m_controls->CaptureCursor( true );
2633
2634 // Init the new item attributes
2635 // (non-geometric, those are handled by the manager)
2636 graphic->SetShape( SHAPE_T::ARC );
2637 graphic->SetStroke( m_stroke );
2638
2639 if( !m_view->IsLayerVisible( m_layer ) )
2640 {
2643 }
2644
2645 preview.Add( graphic );
2646 frame()->SetMsgPanel( graphic );
2647 started = true;
2648 }
2649
2650 arcManager.AddPoint( cursorPos, true );
2651 }
2652 else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
2653 {
2654 arcManager.RemoveLastPoint();
2655 }
2656 else if( evt->IsMotion() )
2657 {
2658 // set angle snap
2659 arcManager.SetAngleSnap( is45Limited );
2660
2661 // update, but don't step the manager state
2662 arcManager.AddPoint( cursorPos, false );
2663 }
2664 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2665 {
2666 if( m_layer != m_frame->GetActiveLayer() )
2667 {
2670 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2671 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2672 }
2673
2674 if( graphic )
2675 {
2676 if( !m_view->IsLayerVisible( m_layer ) )
2677 {
2680 }
2681
2682 graphic->SetLayer( m_layer );
2683 graphic->SetStroke( m_stroke );
2684 m_view->Update( &preview );
2685 frame()->SetMsgPanel( graphic );
2686 }
2687 else
2688 {
2689 evt->SetPassEvent();
2690 }
2691 }
2692 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
2693 {
2695 {
2696 graphic->SetArcAngleAndEnd( ANGLE_90 );
2697 frame()->OnEditItemRequest( graphic );
2698 m_view->Update( &preview );
2699 frame()->SetMsgPanel( graphic );
2700 break;
2701 }
2702 // Don't show the edit panel if we can't represent the arc with it
2703 else if( ( arcManager.GetStep() == KIGFX::PREVIEW::ARC_GEOM_MANAGER::SET_ANGLE )
2704 && ( arcManager.GetStartRadiusEnd() != arcManager.GetEndRadiusEnd() ) )
2705 {
2706 frame()->OnEditItemRequest( graphic );
2707 m_view->Update( &preview );
2708 frame()->SetMsgPanel( graphic );
2709 break;
2710 }
2711 else
2712 {
2713 evt->SetPassEvent();
2714 }
2715 }
2716 else if( evt->IsClick( BUT_RIGHT ) )
2717 {
2718 if( !graphic )
2720
2721 m_menu->ShowContextMenu( selection() );
2722 }
2723 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
2724 {
2726
2727 if( graphic )
2728 {
2729 graphic->SetStroke( m_stroke );
2730 m_view->Update( &preview );
2731 frame()->SetMsgPanel( graphic );
2732 }
2733 }
2734 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
2735 {
2736 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
2737 {
2739
2740 if( graphic )
2741 {
2742 graphic->SetStroke( m_stroke );
2743 m_view->Update( &preview );
2744 frame()->SetMsgPanel( graphic );
2745 }
2746 }
2747 }
2748 else if( evt->IsAction( &PCB_ACTIONS::arcPosture ) )
2749 {
2750 arcManager.ToggleClockwise();
2751 }
2752 else if( evt->IsAction( &ACTIONS::updateUnits ) )
2753 {
2754 arcAsst.SetUnits( frame()->GetUserUnits() );
2755 m_view->Update( &arcAsst );
2756 evt->SetPassEvent();
2757 }
2758 else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
2759 || evt->IsAction( &ACTIONS::redo ) ) )
2760 {
2761 wxBell();
2762 }
2763 else
2764 {
2765 evt->SetPassEvent();
2766 }
2767
2768 if( arcManager.IsComplete() )
2769 {
2770 break;
2771 }
2772 else if( arcManager.HasGeometryChanged() )
2773 {
2774 updateArcFromConstructionMgr( arcManager, *graphic );
2775 m_view->Update( &preview );
2776 m_view->Update( &arcAsst );
2777
2778 if( started )
2779 frame()->SetMsgPanel( graphic );
2780 else
2781 frame()->SetMsgPanel( board() );
2782 }
2783 }
2784
2785 preview.Remove( graphic );
2786 m_view->Remove( &arcAsst );
2787 m_view->Remove( &preview );
2788
2789 if( selection().Empty() )
2791
2792 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2793 m_controls->SetAutoPan( false );
2794 m_controls->CaptureCursor( false );
2796
2797 return !cancelled;
2798}
2799
2800
2801std::unique_ptr<PCB_SHAPE> DRAWING_TOOL::drawOneBezier( const TOOL_EVENT& aTool,
2802 const OPT_VECTOR2I& aStartingPoint,
2803 const OPT_VECTOR2I& aStartingControl1Point,
2804 DRAW_ONE_RESULT& aResult )
2805{
2806 int maxError = board()->GetDesignSettings().m_MaxError;
2807
2808 std::unique_ptr<PCB_SHAPE> bezier = std::make_unique<PCB_SHAPE>( m_frame->GetModel() );
2809 bezier->SetShape( SHAPE_T::BEZIER );
2810 bezier->SetFlags( IS_NEW );
2811
2812 if( m_layer != m_frame->GetActiveLayer() )
2813 {
2816 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2817 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2818 }
2819
2820 // Arc geometric construction manager
2822
2823 // Arc drawing assistant overlay
2824 KIGFX::PREVIEW::BEZIER_ASSISTANT bezierAsst( bezierManager, pcbIUScale,
2825 m_frame->GetUserUnits() );
2826
2827 // Add a VIEW_GROUP that serves as a preview for the new item
2828 PCB_SELECTION preview;
2829 m_view->Add( &preview );
2830 m_view->Add( &bezierAsst );
2832
2833 const auto setCursor =
2834 [&]()
2835 {
2836 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2837 };
2838
2839 const auto resetProgress =
2840 [&]()
2841 {
2842 preview.Clear();
2843 bezier.reset();
2844 };
2845
2846 m_controls->ShowCursor( true );
2848 // Set initial cursor
2849 setCursor();
2850
2851 const auto started =
2852 [&]()
2853 {
2855 };
2856
2857 aResult = DRAW_ONE_RESULT::ACCEPTED;
2858 bool priming = false;
2859
2861
2862 // Load in one or two points if they were passed in
2863 if( aStartingPoint )
2864 {
2865 priming = true;
2866
2867 if( aStartingControl1Point )
2868 {
2869 bezierManager.AddPoint( *aStartingPoint, true );
2870 bezierManager.AddPoint( *aStartingControl1Point, true );
2871 m_toolMgr->PrimeTool( *aStartingControl1Point );
2872 }
2873 else
2874 {
2875 bezierManager.AddPoint( *aStartingPoint, true );
2876 m_toolMgr->PrimeTool( *aStartingPoint );
2877 }
2878 }
2879
2880 // Main loop: keep receiving events
2881 while( TOOL_EVENT* evt = Wait() )
2882 {
2883 if( started() )
2884 m_frame->SetMsgPanel( bezier.get() );
2885
2886 setCursor();
2887
2888 // Init the new item attributes
2889 // (non-geometric, those are handled by the manager)
2890 bezier->SetShape( SHAPE_T::BEZIER );
2891 bezier->SetStroke( m_stroke );
2892 bezier->SetLayer( m_layer );
2893
2894 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2895 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2896 VECTOR2I cursorPos = GetClampedCoords(
2897 grid.BestSnapAnchor( m_controls->GetMousePosition(), bezier.get(), GRID_GRAPHICS ),
2899 m_controls->ForceCursorPosition( true, cursorPos );
2900
2901 if( evt->IsCancelInteractive() || ( started() && evt->IsAction( &ACTIONS::undo ) ) )
2902 {
2903 resetProgress();
2904
2905 if( !started() )
2906 {
2907 // We've handled the cancel event. Don't cancel other tools
2908 evt->SetPassEvent( false );
2909 m_frame->PopTool( aTool );
2911 }
2912 else
2913 {
2914 // We're not cancelling, but we're also not returning a finished bezier
2915 // So we'll be called again.
2916 aResult = DRAW_ONE_RESULT::RESET;
2917 }
2918
2919 break;
2920 }
2921 else if( evt->IsActivate() )
2922 {
2923 if( evt->IsPointEditor() )
2924 {
2925 // don't exit (the point editor runs in the background)
2926 }
2927 else if( evt->IsMoveTool() )
2928 {
2929 resetProgress();
2930 // leave ourselves on the stack so we come back after the move
2932 break;
2933 }
2934 else
2935 {
2936 resetProgress();
2937 m_frame->PopTool( aTool );
2939 break;
2940 }
2941 }
2942 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
2943 {
2944 if( !started() )
2945 {
2947
2948 m_controls->SetAutoPan( true );
2949 m_controls->CaptureCursor( true );
2950
2951 if( !m_view->IsLayerVisible( m_layer ) )
2952 {
2955 }
2956
2957 frame()->SetMsgPanel( bezier.get() );
2958 }
2959
2960 if( !priming )
2961 bezierManager.AddPoint( cursorPos, true );
2962 else
2963 priming = false;
2964
2965 const bool doubleClick = evt->IsDblClick( BUT_LEFT );
2966
2967 if( doubleClick )
2968 {
2969 // Use the current point for all remaining points
2970 while( bezierManager.GetStep() < KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END )
2971 bezierManager.AddPoint( cursorPos, true );
2972 }
2973
2975 preview.Add( bezier.get() );
2976
2977 // Return to the caller for a reset
2978 if( doubleClick )
2979 {
2980 // Don't chain to this one
2982 break;
2983 }
2984 }
2985 else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
2986 {
2987 bezierManager.RemoveLastPoint();
2988
2990 preview.Remove( bezier.get() );
2991 }
2992 else if( evt->IsMotion() )
2993 {
2994 // set angle snap
2995 // bezierManager.SetAngleSnap( Is45Limited() );
2996
2997 // update, but don't step the manager state
2998 bezierManager.AddPoint( cursorPos, false );
2999 }
3000 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
3001 {
3002 if( m_layer != m_frame->GetActiveLayer() )
3003 {
3006 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
3007 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
3008 }
3009
3010 if( !m_view->IsLayerVisible( m_layer ) )
3011 {
3014 }
3015
3016 bezier->SetLayer( m_layer );
3017 bezier->SetStroke( m_stroke );
3018 m_view->Update( &preview );
3019 frame()->SetMsgPanel( bezier.get() );
3020 }
3021 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
3022 {
3023 // Don't show the edit panel if we can't represent the arc with it
3024 if( ( bezierManager.GetStep() >= KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END ) )
3025 {
3026 frame()->OnEditItemRequest( bezier.get() );
3027 m_view->Update( &preview );
3028 frame()->SetMsgPanel( bezier.get() );
3029 break;
3030 }
3031 else
3032 {
3033 evt->SetPassEvent();
3034 }
3035 }
3036 else if( evt->IsClick( BUT_RIGHT ) )
3037 {
3038 m_menu->ShowContextMenu( selection() );
3039 }
3040 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
3041 {
3043
3044 bezier->SetStroke( m_stroke );
3045 m_view->Update( &preview );
3046 frame()->SetMsgPanel( bezier.get() );
3047 }
3048 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
3049 {
3050 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
3051 {
3053
3054 bezier->SetStroke( m_stroke );
3055 m_view->Update( &preview );
3056 frame()->SetMsgPanel( bezier.get() );
3057 }
3058 }
3059 else if( evt->IsAction( &ACTIONS::updateUnits ) )
3060 {
3061 bezierAsst.SetUnits( frame()->GetUserUnits() );
3062 m_view->Update( &bezierAsst );
3063 evt->SetPassEvent();
3064 }
3065 else if( started() && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
3066 || evt->IsAction( &ACTIONS::redo ) ) )
3067 {
3068 wxBell();
3069 }
3070 else
3071 {
3072 evt->SetPassEvent();
3073 }
3074
3075 if( bezierManager.IsComplete() )
3076 {
3077 break;
3078 }
3079 else if( bezierManager.HasGeometryChanged() )
3080 {
3081 bezier->SetStart( bezierManager.GetStart() );
3082 bezier->SetBezierC1( bezierManager.GetControlC1() );
3083 bezier->SetEnd( bezierManager.GetEnd() );
3084 bezier->SetBezierC2( bezierManager.GetControlC2() );
3085 bezier->RebuildBezierToSegmentsPointsList( maxError );
3086
3087 m_view->Update( &preview );
3088 m_view->Update( &bezierAsst );
3089
3090 // Once we are receiving end points, we can show the bezier in the preview
3092 frame()->SetMsgPanel( bezier.get() );
3093 else
3094 frame()->SetMsgPanel( board() );
3095 }
3096 }
3097
3098 preview.Remove( bezier.get() );
3099 m_view->Remove( &bezierAsst );
3100 m_view->Remove( &preview );
3101
3102 if( selection().Empty() )
3104
3105 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3106 m_controls->SetAutoPan( false );
3107 m_controls->CaptureCursor( false );
3109
3110 return bezier;
3111};
3112
3114{
3115 bool clearSelection = false;
3116 *aZone = nullptr;
3117
3118 // not an action that needs a source zone
3119 if( aMode == ZONE_MODE::ADD || aMode == ZONE_MODE::GRAPHIC_POLYGON )
3120 return true;
3121
3123 const PCB_SELECTION& selection = selTool->GetSelection();
3124
3125 if( selection.Empty() )
3126 {
3127 clearSelection = true;
3129 }
3130
3131 // we want a single zone
3132 if( selection.Size() == 1 && selection[0]->Type() == PCB_ZONE_T )
3133 *aZone = static_cast<ZONE*>( selection[0] );
3134
3135 // expected a zone, but didn't get one
3136 if( !*aZone )
3137 {
3138 if( clearSelection )
3140
3141 return false;
3142 }
3143
3144 return true;
3145}
3146
3148{
3150 return 0;
3151
3152 ZONE_MODE zoneMode = aEvent.Parameter<ZONE_MODE>();
3153 MODE drawMode = MODE::ZONE;
3154
3155 if( aEvent.IsAction( &PCB_ACTIONS::drawRuleArea ) )
3156 drawMode = MODE::KEEPOUT;
3157
3158 if( aEvent.IsAction( &PCB_ACTIONS::drawPolygon ) )
3159 drawMode = MODE::GRAPHIC_POLYGON;
3160
3161 SCOPED_DRAW_MODE scopedDrawMode( m_mode, drawMode );
3162
3163 // get a source zone, if we need one. We need it for:
3164 // ZONE_MODE::CUTOUT (adding a hole to the source zone)
3165 // ZONE_MODE::SIMILAR (creating a new zone using settings of source zone
3166 ZONE* sourceZone = nullptr;
3167
3168 if( !getSourceZoneForAction( zoneMode, &sourceZone ) )
3169 return 0;
3170
3171 // Turn zones on if they are off, so that the created object will be visible after completion
3173
3175
3176 params.m_keepout = drawMode == MODE::KEEPOUT;
3177 params.m_mode = zoneMode;
3178 params.m_sourceZone = sourceZone;
3179 params.m_layer = m_frame->GetActiveLayer();
3180
3181 if( zoneMode == ZONE_MODE::SIMILAR && !sourceZone->IsOnLayer( params.m_layer ) )
3182 params.m_layer = sourceZone->GetFirstLayer();
3183
3184 ZONE_CREATE_HELPER zoneTool( *this, params );
3185 // the geometry manager which handles the zone geometry, and hands the calculated points
3186 // over to the zone creator tool
3187 POLYGON_GEOM_MANAGER polyGeomMgr( zoneTool );
3188 bool started = false;
3190
3191 m_frame->PushTool( aEvent );
3192
3193 auto setCursor =
3194 [&]()
3195 {
3196 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
3197 };
3198
3199 auto cleanup =
3200 [&] ()
3201 {
3202 polyGeomMgr.Reset();
3203 started = false;
3204 grid.ClearSkipPoint();
3205 m_controls->SetAutoPan( false );
3206 m_controls->CaptureCursor( false );
3207 };
3208
3209 Activate();
3210 // Must be done after Activate() so that it gets set into the correct context
3211 m_controls->ShowCursor( true );
3213 // Set initial cursor
3214 setCursor();
3215
3216 if( aEvent.HasPosition() )
3217 m_toolMgr->PrimeTool( aEvent.Position() );
3218
3219 // Main loop: keep receiving events
3220 while( TOOL_EVENT* evt = Wait() )
3221 {
3222 setCursor();
3223
3224 LSET layers( { m_frame->GetActiveLayer() } );
3225 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
3226 bool is45Limited = Is45Limited() && !evt->Modifier( MD_CTRL );
3227 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
3228
3229 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
3230 cursorPos = GetClampedCoords( grid.BestSnapAnchor( cursorPos, layers, GRID_GRAPHICS ),
3232
3233 m_controls->ForceCursorPosition( true, cursorPos );
3234
3237
3238 if( evt->IsCancelInteractive() )
3239 {
3240 if( started )
3241 {
3242 cleanup();
3243 }
3244 else
3245 {
3246 m_frame->PopTool( aEvent );
3247
3248 // We've handled the cancel event. Don't cancel other tools
3249 evt->SetPassEvent( false );
3250 break;
3251 }
3252 }
3253 else if( evt->IsActivate() )
3254 {
3255 if( started )
3256 cleanup();
3257
3258 if( evt->IsPointEditor() )
3259 {
3260 // don't exit (the point editor runs in the background)
3261 }
3262 else if( evt->IsMoveTool() )
3263 {
3264 // leave ourselves on the stack so we come back after the move
3265 break;
3266 }
3267 else
3268 {
3269 m_frame->PopTool( aEvent );
3270 break;
3271 }
3272 }
3273 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
3274 {
3275 if( zoneMode != ZONE_MODE::SIMILAR )
3276 params.m_layer = frame()->GetActiveLayer();
3277
3278 if( !m_view->IsLayerVisible( params.m_layer ) )
3279 {
3282 }
3283 }
3284 else if( evt->IsClick( BUT_RIGHT ) )
3285 {
3286 if( !started )
3288
3289 m_menu->ShowContextMenu( selection() );
3290 }
3291 // events that lock in nodes
3292 else if( evt->IsClick( BUT_LEFT )
3293 || evt->IsDblClick( BUT_LEFT )
3294 || evt->IsAction( &PCB_ACTIONS::closeOutline ) )
3295 {
3296 // Check if it is double click / closing line (so we have to finish the zone)
3297 const bool endPolygon = evt->IsDblClick( BUT_LEFT )
3298 || evt->IsAction( &PCB_ACTIONS::closeOutline )
3299 || polyGeomMgr.NewPointClosesOutline( cursorPos );
3300
3301 if( endPolygon )
3302 {
3303 polyGeomMgr.SetFinished();
3304 polyGeomMgr.Reset();
3305
3306 started = false;
3307 m_controls->SetAutoPan( false );
3308 m_controls->CaptureCursor( false );
3309 }
3310 // adding a corner
3311 else if( polyGeomMgr.AddPoint( cursorPos ) )
3312 {
3313 if( !started )
3314 {
3315 started = true;
3316
3317 m_controls->SetAutoPan( true );
3318 m_controls->CaptureCursor( true );
3319
3320 if( !m_view->IsLayerVisible( params.m_layer ) )
3321 {
3324 }
3325 }
3326 }
3327 }
3328 else if( started && ( evt->IsAction( &PCB_ACTIONS::deleteLastPoint )
3329 || evt->IsAction( &ACTIONS::doDelete )
3330 || evt->IsAction( &ACTIONS::undo ) ) )
3331 {
3332 if( std::optional<VECTOR2I> last = polyGeomMgr.DeleteLastCorner() )
3333 {
3334 cursorPos = last.value();
3335 getViewControls()->WarpMouseCursor( cursorPos, true );
3336 m_controls->ForceCursorPosition( true, cursorPos );
3337 polyGeomMgr.SetCursorPosition( cursorPos );
3338 }
3339 else
3340 {
3341 cleanup();
3342 }
3343 }
3344 else if( started && ( evt->IsMotion()
3345 || evt->IsDrag( BUT_LEFT ) ) )
3346 {
3347 polyGeomMgr.SetCursorPosition( cursorPos );
3348 }
3349 else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
3350 || evt->IsAction( &ACTIONS::redo ) ) )
3351 {
3352 wxBell();
3353 }
3354 else if( started && evt->IsAction( &PCB_ACTIONS::properties ) )
3355 {
3356 frame()->OnEditItemRequest( zoneTool.GetZone() );
3357 zoneTool.OnGeometryChange( polyGeomMgr );
3358 frame()->SetMsgPanel( zoneTool.GetZone() );
3359 }
3360 /*else if( evt->IsAction( &ACTIONS::updateUnits ) )
3361 {
3362 // If we ever have an assistant here that reports dimensions, we'll want to
3363 // update its units here....
3364 // zoneAsst.SetUnits( frame()->GetUserUnits() );
3365 // m_view->Update( &zoneAsst );
3366 evt->SetPassEvent();
3367 }*/
3368 else
3369 {
3370 evt->SetPassEvent();
3371 }
3372
3373 } // end while
3374
3375 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3377 controls()->SetAutoPan( false );
3378 m_controls->CaptureCursor( false );
3379 return 0;
3380}
3381
3382
3384{
3386 return 0;
3387
3388 struct VIA_PLACER : public INTERACTIVE_PLACER_BASE
3389 {
3390 PCB_BASE_EDIT_FRAME* m_frame;
3391 PCB_GRID_HELPER m_gridHelper;
3392 std::shared_ptr<DRC_ENGINE> m_drcEngine;
3393 int m_drcEpsilon;
3394 int m_worstClearance;
3395 bool m_allowDRCViolations;
3396
3397 VIA_PLACER( PCB_BASE_EDIT_FRAME* aFrame ) :
3398 m_frame( aFrame ),
3399 m_gridHelper( aFrame->GetToolManager(), aFrame->GetMagneticItemsSettings() ),
3400 m_drcEngine( aFrame->GetBoard()->GetDesignSettings().m_DRCEngine ),
3401 m_drcEpsilon( aFrame->GetBoard()->GetDesignSettings().GetDRCEpsilon() ),
3402 m_worstClearance( 0 )
3403 {
3404 ROUTER_TOOL* router = m_frame->GetToolManager()->GetTool<ROUTER_TOOL>();
3405
3406 if( router )
3407 m_allowDRCViolations = router->Router()->Settings().AllowDRCViolations();
3408
3409 try
3410 {
3411 if( aFrame )
3412 m_drcEngine->InitEngine( aFrame->GetDesignRulesPath() );
3413
3414 DRC_CONSTRAINT constraint;
3415
3416 if( m_drcEngine->QueryWorstConstraint( CLEARANCE_CONSTRAINT, constraint ) )
3417 m_worstClearance = constraint.GetValue().Min();
3418
3419 if( m_drcEngine->QueryWorstConstraint( HOLE_CLEARANCE_CONSTRAINT, constraint ) )
3420 m_worstClearance = std::max( m_worstClearance, constraint.GetValue().Min() );
3421
3422 for( FOOTPRINT* footprint : aFrame->GetBoard()->Footprints() )
3423 {
3424 for( PAD* pad : footprint->Pads() )
3425 {
3426 std::optional<int> padOverride = pad->GetClearanceOverrides( nullptr );
3427
3428 if( padOverride.has_value() )
3429 m_worstClearance = std::max( m_worstClearance, padOverride.value() );
3430 }
3431 }
3432 }
3433 catch( PARSE_ERROR& )
3434 {
3435 }
3436 }
3437
3438 virtual ~VIA_PLACER()
3439 {
3440 }
3441
3442 PCB_TRACK* findTrack( PCB_VIA* aVia )
3443 {
3444 const LSET lset = aVia->GetLayerSet();
3445 VECTOR2I position = aVia->GetPosition();
3446 BOX2I bbox = aVia->GetBoundingBox();
3447
3448 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
3449 KIGFX::PCB_VIEW* view = m_frame->GetCanvas()->GetView();
3450 std::vector<PCB_TRACK*> possible_tracks;
3451
3452 wxCHECK( view, nullptr );
3453
3454 view->Query( bbox, items );
3455
3456 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
3457 {
3458 if( !it.first->IsBOARD_ITEM() )
3459 continue;
3460
3461 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
3462
3463 if( !( item->GetLayerSet() & lset ).any() )
3464 continue;
3465
3466 if( item->Type() == PCB_TRACE_T )
3467 {
3468 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
3469
3470 if( TestSegmentHit( position, track->GetStart(), track->GetEnd(),
3471 ( track->GetWidth()
3472 + aVia->GetWidth( track->GetLayer() ) ) / 2 ) )
3473 {
3474 possible_tracks.push_back( track );
3475 }
3476 }
3477 else if( item->Type() == PCB_ARC_T )
3478 {
3479 PCB_ARC* arc = static_cast<PCB_ARC*>( item );
3480
3481 if( arc->HitTest( position, aVia->GetWidth( arc->GetLayer() ) / 2 ) )
3482 possible_tracks.push_back( arc );
3483 }
3484 }
3485
3486 PCB_TRACK* return_track = nullptr;
3487 int min_d = std::numeric_limits<int>::max();
3488
3489 for( PCB_TRACK* track : possible_tracks )
3490 {
3491 SEG test( track->GetStart(), track->GetEnd() );
3492 int dist = ( test.NearestPoint( position ) - position ).EuclideanNorm();
3493
3494 if( dist < min_d )
3495 {
3496 min_d = dist;
3497 return_track = track;
3498 }
3499 }
3500
3501 return return_track;
3502 }
3503
3504 bool hasDRCViolation( PCB_VIA* aVia, BOARD_ITEM* aOther )
3505 {
3506 DRC_CONSTRAINT constraint;
3507 int clearance;
3508 BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aOther );
3509 ZONE* zone = dynamic_cast<ZONE*>( aOther );
3510
3511 if( zone && zone->GetIsRuleArea() )
3512 {
3513 if( zone->GetDoNotAllowVias() )
3514 {
3515 bool hit = false;
3516
3518 [&]( PCB_LAYER_ID aLayer )
3519 {
3520 if( hit )
3521 return;
3522
3523 if( zone->Outline()->Collide( aVia->GetPosition(),
3524 aVia->GetWidth( aLayer ) / 2 ) )
3525 {
3526 hit = true;
3527 }
3528 } );
3529
3530 return hit;
3531 }
3532
3533 return false;
3534 }
3535
3536 if( connectedItem )
3537 {
3538 int connectedItemNet = connectedItem->GetNetCode();
3539
3540 if( connectedItemNet == 0 || connectedItemNet == aVia->GetNetCode() )
3541 return false;
3542 }
3543
3544 for( PCB_LAYER_ID layer : aOther->GetLayerSet().Seq() )
3545 {
3546 // Reference images are "on" a copper layer but are not actually part of it
3547 if( !IsCopperLayer( layer ) || aOther->Type() == PCB_REFERENCE_IMAGE_T )
3548 continue;
3549
3550 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aVia, aOther, layer );
3551 clearance = constraint.GetValue().Min();
3552
3553 if( clearance >= 0 )
3554 {
3555 std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( layer );
3556 std::shared_ptr<SHAPE> otherShape = aOther->GetEffectiveShape( layer );
3557
3558 if( viaShape->Collide( otherShape.get(), clearance - m_drcEpsilon ) )
3559 return true;
3560 }
3561 }
3562
3563 if( aOther->HasHole() )
3564 {
3565 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aVia, aOther,
3567 clearance = constraint.GetValue().Min();
3568
3569 if( clearance >= 0 )
3570 {
3571 std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( UNDEFINED_LAYER );
3572
3573 if( viaShape->Collide( aOther->GetEffectiveHoleShape().get(),
3574 clearance - m_drcEpsilon ) )
3575 {
3576 return true;
3577 }
3578 }
3579 }
3580
3581 return false;
3582 }
3583
3584 bool checkDRCViolation( PCB_VIA* aVia )
3585 {
3586 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
3587 std::set<BOARD_ITEM*> checkedItems;
3588 BOX2I bbox = aVia->GetBoundingBox();
3589
3590 bbox.Inflate( m_worstClearance );
3591 m_frame->GetCanvas()->GetView()->Query( bbox, items );
3592
3593 for( std::pair<KIGFX::VIEW_ITEM*, int> it : items )
3594 {
3595 if( !it.first->IsBOARD_ITEM() )
3596 continue;
3597
3598 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
3599
3600 if( item->Type() == PCB_ZONE_T && !static_cast<ZONE*>( item )->GetIsRuleArea() )
3601 {
3602 continue; // stitching vias bind to zones, so ignore them
3603 }
3604 else if( item->Type() == PCB_FOOTPRINT_T || item->Type() == PCB_GROUP_T )
3605 {
3606 continue; // check against children, but not against footprint itself
3607 }
3608 else if( ( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T )
3609 && !static_cast<PCB_TEXT*>( item )->IsVisible() )
3610 {
3611 continue; // ignore hidden items
3612 }
3613 else if( checkedItems.count( item ) )
3614 {
3615 continue; // already checked
3616 }
3617
3618 if( hasDRCViolation( aVia, item ) )
3619 return true;
3620
3621 checkedItems.insert( item );
3622 }
3623
3624 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( DISALLOW_CONSTRAINT, aVia, nullptr,
3626
3627 if( constraint.m_DisallowFlags && constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
3628 return true;
3629
3630 return false;
3631 }
3632
3633 PAD* findPad( PCB_VIA* aVia )
3634 {
3635 const VECTOR2I position = aVia->GetPosition();
3636 const LSET lset = aVia->GetLayerSet();
3637
3638 for( FOOTPRINT* fp : m_board->Footprints() )
3639 {
3640 for( PAD* pad : fp->Pads() )
3641 {
3642 if( pad->HitTest( position ) && ( pad->GetLayerSet() & lset ).any() )
3643 {
3644 return pad;
3645 }
3646 }
3647 }
3648
3649 return nullptr;
3650 }
3651
3652 PCB_SHAPE* findGraphic( const PCB_VIA* aVia ) const
3653 {
3654 const LSET lset = aVia->GetLayerSet() & LSET::AllCuMask();
3655 VECTOR2I position = aVia->GetPosition();
3656 BOX2I bbox = aVia->GetBoundingBox();
3657
3658 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
3659 KIGFX::PCB_VIEW* view = m_frame->GetCanvas()->GetView();
3660 PCB_LAYER_ID activeLayer = m_frame->GetActiveLayer();
3661 std::vector<PCB_SHAPE*> possible_shapes;
3662
3663 view->Query( bbox, items );
3664
3665 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
3666 {
3667 if( !it.first->IsBOARD_ITEM() )
3668 continue;
3669
3670 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
3671
3672 if( !( item->GetLayerSet() & lset ).any() )
3673 continue;
3674
3675 if( item->Type() == PCB_SHAPE_T )
3676 {
3677 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
3678
3679 if( shape->HitTest( position, aVia->GetWidth( activeLayer ) / 2 ) )
3680 possible_shapes.push_back( shape );
3681 }
3682 }
3683
3684 PCB_SHAPE* return_shape = nullptr;
3685 int min_d = std::numeric_limits<int>::max();
3686
3687 for( PCB_SHAPE* shape : possible_shapes )
3688 {
3689 int dist = ( shape->GetPosition() - position ).EuclideanNorm();
3690
3691 if( dist < min_d )
3692 {
3693 min_d = dist;
3694 return_shape = shape;
3695 }
3696 }
3697
3698 return return_shape;
3699 }
3700
3701 std::optional<int> selectPossibleNetsByPopupMenu( std::set<int>& aNetcodeList )
3702 {
3703 ACTION_MENU menu( true );
3704 const NETINFO_LIST& netInfo = m_board->GetNetInfo();
3705 std::map<int, int> menuIDNetCodeMap;
3706 int menuID = 1;
3707
3708 for( int netcode : aNetcodeList )
3709 {
3710 wxString menuText;
3711 if( menuID < 10 )
3712 {
3713#ifdef __WXMAC__
3714 menuText = wxString::Format( "%s\t",
3715 netInfo.GetNetItem( netcode )->GetNetname() );
3716#else
3717 menuText = wxString::Format( "&%d %s\t",
3718 menuID,
3719 netInfo.GetNetItem( netcode )->GetNetname() );
3720#endif
3721 }
3722 else
3723 {
3724 menuText = netInfo.GetNetItem( netcode )->GetNetname();
3725 }
3726
3727 menu.Add( menuText, menuID, BITMAPS::INVALID_BITMAP );
3728 menuIDNetCodeMap[ menuID ] = netcode;
3729 menuID++;
3730 }
3731
3732 menu.SetTitle( _( "Select Net:" ) );
3733 menu.DisplayTitle( true );
3734
3735 DRAWING_TOOL* drawingTool = m_frame->GetToolManager()->GetTool<DRAWING_TOOL>();
3736 drawingTool->SetContextMenu( &menu, CMENU_NOW );
3737
3738 int selectedNetCode = -1;
3739 bool cancelled = false;
3740
3741 while( TOOL_EVENT* evt = drawingTool->Wait() )
3742 {
3743 if( evt->Action() == TA_CHOICE_MENU_UPDATE )
3744 {
3745 evt->SetPassEvent();
3746 }
3747 else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
3748 {
3749 std::optional<int> id = evt->GetCommandId();
3750
3751 // User has selected an item, so this one will be returned
3752 if( id && ( *id > 0 ) && ( *id < menuID ) )
3753 {
3754 selectedNetCode = menuIDNetCodeMap.at( *id );
3755 }
3756 // User has cancelled the menu (either by <esc> or clicking out of it),
3757 else
3758 {
3759 cancelled = true;
3760 }
3761 }
3762 else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
3763 {
3764 break;
3765 }
3766 }
3767
3768 if( cancelled )
3769 return std::optional<int>();
3770 else
3771 return selectedNetCode;
3772 }
3773
3774 std::optional<int> findStitchedZoneNet( PCB_VIA* aVia )
3775 {
3776 const VECTOR2I position = aVia->GetPosition();
3777 PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
3778 std::set<int> netcodeList;
3779
3780 // See if there are any connections available on a high-contrast layer
3781 if( opts.m_ContrastModeDisplay == HIGH_CONTRAST_MODE::DIMMED
3782 || opts.m_ContrastModeDisplay == HIGH_CONTRAST_MODE::HIDDEN )
3783 {
3784 if( aVia->GetLayerSet().test( m_frame->GetActiveLayer() ) )
3785 {
3786 for( ZONE* z : m_board->Zones() )
3787 {
3788 if( z->IsOnLayer( m_frame->GetActiveLayer() ) )
3789 {
3790 if( z->HitTestFilledArea( m_frame->GetActiveLayer(), position ) )
3791 netcodeList.insert( z->GetNetCode() );
3792 }
3793 }
3794 }
3795 }
3796
3797 // If there's only one, return it.
3798 if( netcodeList.size() == 1 )
3799 return *netcodeList.begin();
3800
3801 // See if there are any connections available on a visible layer
3802 LSET lset = LSET( m_board->GetVisibleLayers() & aVia->GetLayerSet() );
3803
3804 for( ZONE* z : m_board->Zones() )
3805 {
3806 for( PCB_LAYER_ID layer : lset.Seq() )
3807 {
3808 if( z->IsOnLayer( layer ) )
3809 {
3810 if( z->HitTestFilledArea( layer, position ) )
3811 netcodeList.insert( z->GetNetCode() );
3812 }
3813 }
3814 }
3815
3816 // If there's only one, return it.
3817 if( netcodeList.size() == 1 )
3818 return *netcodeList.begin();
3819
3820 if( netcodeList.size() > 1 )
3821 {
3822 // The net assignment is ambiguous. Let the user decide.
3823 return selectPossibleNetsByPopupMenu( netcodeList );
3824 }
3825 else
3826 {
3828 }
3829 }
3830
3831 void SnapItem( BOARD_ITEM *aItem ) override
3832 {
3833 m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
3834
3835 MAGNETIC_SETTINGS* settings = m_frame->GetMagneticItemsSettings();
3836 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
3837 VECTOR2I position = via->GetPosition();
3838
3839 if( settings->tracks != MAGNETIC_OPTIONS::NO_EFFECT && m_gridHelper.GetSnap() )
3840 {
3841 if( PCB_TRACK* track = findTrack( via ) )
3842 {
3843 SEG trackSeg( track->GetStart(), track->GetEnd() );
3844 VECTOR2I snap = m_gridHelper.AlignToSegment( position, trackSeg );
3845
3846 aItem->SetPosition( snap );
3847 return;
3848 }
3849 }
3850
3851 if( settings->pads != MAGNETIC_OPTIONS::NO_EFFECT && m_gridHelper.GetSnap() )
3852 {
3853 if( PAD* pad = findPad( via ) )
3854 {
3855 aItem->SetPosition( pad->GetPosition() );
3856 return;
3857 }
3858 }
3859
3860 if( settings->graphics && m_gridHelper.GetSnap() )
3861 {
3862 if( PCB_SHAPE* shape = findGraphic( via ) )
3863 {
3864 if( shape->IsAnyFill() )
3865 {
3866 aItem->SetPosition( shape->GetPosition() );
3867 }
3868 else
3869 {
3870 switch( shape->GetShape() )
3871 {
3872 case SHAPE_T::SEGMENT:
3873 {
3874 SEG seg( shape->GetStart(), shape->GetEnd() );
3875 VECTOR2I snap = m_gridHelper.AlignToSegment( position, seg );
3876 aItem->SetPosition( snap );
3877 break;
3878 }
3879
3880 case SHAPE_T::ARC:
3881 {
3882 if( ( shape->GetEnd() - position ).SquaredEuclideanNorm() <
3883 ( shape->GetStart() - position ).SquaredEuclideanNorm() )
3884 {
3885 aItem->SetPosition( shape->GetEnd() );
3886 }
3887 else
3888 {
3889 aItem->SetPosition( shape->GetStart() );
3890 }
3891
3892 break;
3893 }
3894
3895 case SHAPE_T::POLY:
3896 {
3897 if( !shape->IsPolyShapeValid() )
3898 {
3899 aItem->SetPosition( shape->GetPosition() );
3900 break;
3901 }
3902
3903 const SHAPE_POLY_SET& polySet = shape->GetPolyShape();
3904 std::optional<SEG> nearestSeg;
3905 int minDist = std::numeric_limits<int>::max();
3906
3907 for( int ii = 0; ii < polySet.OutlineCount(); ++ii )
3908 {
3909 const SHAPE_LINE_CHAIN& poly = polySet.Outline( ii );
3910
3911 for( int jj = 0; jj < poly.SegmentCount(); ++jj )
3912 {
3913 const SEG& seg = poly.GetSegment( jj );
3914 int dist = seg.Distance( position );
3915
3916 if( dist < minDist )
3917 {
3918 minDist = dist;
3919 nearestSeg = seg;
3920 }
3921 }
3922 }
3923
3924 if( nearestSeg )
3925 {
3926 VECTOR2I snap = m_gridHelper.AlignToSegment( position, *nearestSeg );
3927 aItem->SetPosition( snap );
3928 }
3929
3930 break;
3931 }
3932
3933 default:
3934 aItem->SetPosition( shape->GetPosition() );
3935 }
3936
3937 }
3938 }
3939
3940 }
3941 }
3942
3943 bool PlaceItem( BOARD_ITEM* aItem, BOARD_COMMIT& aCommit ) override
3944 {
3945 WX_INFOBAR* infobar = m_frame->GetInfoBar();
3946 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
3947 VECTOR2I viaPos = via->GetPosition();
3948 PCB_TRACK* track = findTrack( via );
3949 PAD* pad = findPad( via );
3950
3951 if( track )
3952 {
3953 via->SetNetCode( track->GetNetCode() );
3954 via->SetIsFree( false );
3955 }
3956 else if( pad )
3957 {
3958 via->SetNetCode( pad->GetNetCode() );
3959 via->SetIsFree( false );
3960 }
3961 else
3962 {
3963 std::optional<int> netcode = findStitchedZoneNet( via );
3964
3965 if( !netcode.has_value() ) // user cancelled net disambiguation menu
3966 return false;
3967
3968 via->SetNetCode( netcode.value() );
3969 via->SetIsFree( via->GetNetCode() > 0 );
3970 }
3971
3972 if( checkDRCViolation( via ) )
3973 {
3974 m_frame->ShowInfoBarError( _( "Via location violates DRC." ), true,
3976
3977 if( !m_allowDRCViolations )
3978 return false;
3979 }
3980 else
3981 {
3983 infobar->Dismiss();
3984 }
3985
3986 aCommit.Add( via );
3987
3988 // If the user explicitly disables snap (using shift), then don't break the tracks.
3989 // This will prevent PNS from being able to connect the via and track but
3990 // it is explicitly requested by the user
3991 if( track && m_gridHelper.GetSnap() )
3992 {
3993 VECTOR2I trackStart = track->GetStart();
3994 VECTOR2I trackEnd = track->GetEnd();
3995 SEG trackSeg( trackStart, trackEnd );
3996
3997 if( viaPos == trackStart || viaPos == trackEnd )
3998 return true;
3999
4000 if( !trackSeg.Contains( viaPos ) )
4001 return true;
4002
4003 aCommit.Modify( track );
4004 track->SetStart( trackStart );
4005 track->SetEnd( viaPos );
4006
4007 PCB_TRACK* newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
4008 const_cast<KIID&>( newTrack->m_Uuid ) = KIID();
4009
4010 newTrack->SetStart( viaPos );
4011 newTrack->SetEnd( trackEnd );
4012 aCommit.Add( newTrack );
4013 }
4014
4015 return true;
4016 }
4017
4018 std::unique_ptr<BOARD_ITEM> CreateItem() override
4019 {
4020 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
4021 PCB_VIA* via = new PCB_VIA( m_board );
4022
4023 via->SetNetCode( 0 );
4024 via->SetViaType( bds.m_CurrentViaType );
4025
4026 if( via->GetViaType() == VIATYPE::THROUGH )
4027 {
4028 via->SetLayerPair( B_Cu, F_Cu );
4029 }
4030 else
4031 {
4032 PCB_LAYER_ID first_layer = m_frame->GetActiveLayer();
4033 PCB_LAYER_ID last_layer;
4034
4035 // prepare switch to new active layer:
4036 if( first_layer != m_frame->GetScreen()->m_Route_Layer_TOP )
4037 last_layer = m_frame->GetScreen()->m_Route_Layer_TOP;
4038 else
4039 last_layer = m_frame->GetScreen()->m_Route_Layer_BOTTOM;
4040
4041 via->SetLayerPair( first_layer, last_layer );
4042 }
4043
4044 if( via->GetViaType() == VIATYPE::MICROVIA )
4045 {
4046 via->SetWidth( PADSTACK::ALL_LAYERS,
4047 via->GetEffectiveNetClass()->GetuViaDiameter() );
4048 via->SetDrill( via->GetEffectiveNetClass()->GetuViaDrill() );
4049 }
4050 else
4051 {
4052 via->SetWidth( PADSTACK::ALL_LAYERS, bds.GetCurrentViaSize() );
4053 via->SetDrill( bds.GetCurrentViaDrill() );
4054 }
4055
4056 return std::unique_ptr<BOARD_ITEM>( via );
4057 }
4058 };
4059
4060 VIA_PLACER placer( frame() );
4061
4062 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::VIA );
4063
4064 doInteractiveItemPlacement( aEvent, &placer, _( "Place via" ), IPO_REPEAT | IPO_SINGLE_CLICK );
4065
4066 return 0;
4067}
4068
4069
4070const unsigned int DRAWING_TOOL::WIDTH_STEP = pcbIUScale.mmToIU( 0.1 );
4071
4072
4074{
4075 // clang-format off
4098
4102 // clang-format on
4103}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:112
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition: box2.h:990
static TOOL_ACTION cancelInteractive
Definition: actions.h:72
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition: actions.h:220
static TOOL_ACTION selectionCursor
Select a single item under the cursor position.
Definition: actions.h:214
static TOOL_ACTION updateUnits
Definition: actions.h:204
static TOOL_ACTION undo
Definition: actions.h:75
static TOOL_ACTION activatePointEditor
Definition: actions.h:264
static TOOL_ACTION doDelete
Definition: actions.h:85
static TOOL_ACTION cursorClick
Definition: actions.h:177
static TOOL_ACTION redo
Definition: actions.h:76
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: actions.h:217
static TOOL_ACTION refreshPreview
Definition: actions.h:156
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition: actions.h:225
static TOOL_ACTION resetLocalCoords
Definition: actions.h:207
Define the structure of a menu based on ACTIONs.
Definition: action_menu.h:49
TOOL_MANAGER * getToolManager() const
Return an instance of TOOL_MANAGER class.
void DisplayTitle(bool aDisplay=true)
Decide whether a title for a pop up menu should be displayed.
void Clear()
Remove all the entries from the menu (as well as its title).
void SetTitle(const wxString &aTitle) override
Set title for the menu.
Definition: action_menu.cpp:92
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
Definition: action_menu.cpp:78
wxMenuItem * Add(const wxString &aLabel, int aId, BITMAPS aIcon)
Add a wxWidgets-style entry to the menu.
void SetLayerVisible(int aLayer, bool isVisible)
VECTOR2D m_LocalOrigin
Relative Screen cursor coordinate (on grid) in user units.
Definition: base_screen.h:90
virtual void Push(const wxString &aMessage=wxEmptyString, int aCommitFlags=0) override
Execute the changes.
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
Container for design settings for a BOARD object.
DIM_PRECISION m_DimensionPrecision
Number of digits after the decimal.
void UseCustomTrackViaSize(bool aEnabled)
Enables/disables custom track/via size settings.
VIATYPE m_CurrentViaType
(VIA_BLIND_BURIED, VIA_THROUGH, VIA_MICROVIA)
DIM_UNITS_FORMAT m_DimensionUnitsFormat
bool GetTextUpright(PCB_LAYER_ID aLayer) const
int GetTextThickness(PCB_LAYER_ID aLayer) const
Return the default text thickness from the layer class for the given layer.
bool GetTextItalic(PCB_LAYER_ID aLayer) const
std::shared_ptr< DRC_ENGINE > m_DRCEngine
void SetViaSizeIndex(unsigned aIndex)
Set the current via size list index to aIndex.
int GetDRCEpsilon() const
Return an epsilon which accounts for rounding errors, etc.
unsigned GetViaSizeIndex() const
VECTOR2I GetTextSize(PCB_LAYER_ID aLayer) const
Return the default text size from the layer class for the given layer.
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
DIM_TEXT_POSITION m_DimensionTextPosition
DIM_UNITS_MODE m_DimensionUnitsMode
std::vector< VIA_DIMENSION > m_ViasDimensionsList
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:232
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:283
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition: board_item.cpp:314
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:252
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
Definition: board_item.cpp:324
virtual bool HasHole() const
Definition: board_item.h:156
const FOOTPRINTS & Footprints() const
Definition: board.h:355
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:943
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:558
constexpr size_type GetWidth() const
Definition: box2.h:214
constexpr Vec Centre() const
Definition: box2.h:97
constexpr size_type GetHeight() const
Definition: box2.h:215
constexpr coord_type GetLeft() const
Definition: box2.h:228
constexpr bool Contains(const Vec &aPoint) const
Definition: box2.h:168
constexpr coord_type GetRight() const
Definition: box2.h:217
constexpr coord_type GetTop() const
Definition: box2.h:229
constexpr coord_type GetBottom() const
Definition: box2.h:222
COMMIT & Modify(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr, RECURSE_MODE aRecurse=RECURSE_MODE::NO_RECURSE)
Modify a given item in the model.
Definition: commit.h:107
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition: commit.h:79
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 AddCheckItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a checked menu entry to run a TOOL_ACTION on selected items.
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
std::list< std::unique_ptr< EDA_ITEM > > & GetImportedItems()
int ShowQuasiModal()
int ShowModal() override
Implementing DIALOG_TRACK_VIA_SIZE_BASE.
Tool responsible for drawing graphical elements like lines, arcs, circles, etc.
Definition: drawing_tool.h:55
TEXT_ATTRIBUTES m_textAttrs
Definition: drawing_tool.h:393
MODE GetDrawingMode() const
Return the current drawing mode of the DRAWING_TOOL or MODE::NONE if not currently in any drawing mod...
PCB_SELECTION m_preview
Definition: drawing_tool.h:395
PCB_LAYER_ID m_layer
Definition: drawing_tool.h:391
int DrawTable(const TOOL_EVENT &aEvent)
int SetAnchor(const TOOL_EVENT &aEvent)
Place the footprint anchor (only in footprint editor).
int DrawDimension(const TOOL_EVENT &aEvent)
Start interactively drawing a dimension.
int DrawVia(const TOOL_EVENT &aEvent)
KIGFX::VIEW_CONTROLS * m_controls
Definition: drawing_tool.h:385
KIGFX::VIEW * m_view
Definition: drawing_tool.h:384
STROKE_PARAMS m_stroke
Definition: drawing_tool.h:392
int DrawZone(const TOOL_EVENT &aEvent)
Start interactively drawing a zone.
int DrawBezier(const TOOL_EVENT &aEvent)
Start interactively drawing a bezier curve.
int DrawLine(const TOOL_EVENT &aEvent)
Start interactively drawing a line.
int DrawArc(const TOOL_EVENT &aEvent)
Start interactively drawing an arc.
VECTOR2I getClampedDifferenceEnd(const VECTOR2I &aOrigin, const VECTOR2I &aEnd)
Clamps the end vector to respect numeric limits of difference representation.
Definition: drawing_tool.h:332
bool drawShape(const TOOL_EVENT &aTool, PCB_SHAPE **aGraphic, std::optional< VECTOR2D > aStartingPoint, std::stack< PCB_SHAPE * > *aCommittedGraphics)
Start drawing a selected shape (i.e.
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
int PlaceImportedGraphics(const TOOL_EVENT &aEvent)
Place a drawing imported from a DXF or SVG file in footprint editor.
VECTOR2I getClampedRadiusEnd(const VECTOR2I &aOrigin, const VECTOR2I &aEnd)
Clamps the end vector to respect numeric limits of radius representation.
Definition: drawing_tool.h:362
bool m_inDrawingTool
Definition: drawing_tool.h:389
static const unsigned int WIDTH_STEP
Definition: drawing_tool.h:399
static const unsigned int COORDS_PADDING
Definition: drawing_tool.h:400
bool drawArc(const TOOL_EVENT &aTool, PCB_SHAPE **aGraphic, std::optional< VECTOR2D > aStartingPoint)
Start drawing an arc.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
int DrawCircle(const TOOL_EVENT &aEvent)
Start interactively drawing a circle.
int PlaceTuningPattern(const TOOL_EVENT &aEvent)
BOARD * m_board
Definition: drawing_tool.h:386
int DrawRectangle(const TOOL_EVENT &aEvent)
Start interactively drawing a rectangle.
bool Init() override
Init() is called once upon a registration of the tool.
void constrainDimension(PCB_DIMENSION_BASE *aDim)
Force the dimension lime to be drawn on multiple of 45 degrees.
int PlaceText(const TOOL_EVENT &aEvent)
Display a dialog that allows one to input text and its settings and then lets the user decide where t...
PCB_BASE_EDIT_FRAME * m_frame
Definition: drawing_tool.h:387
void UpdateStatusBar() const
int PlaceReferenceImage(const TOOL_EVENT &aEvent)
Display a dialog that allows one to select a reference image and then decide where to place the image...
bool getSourceZoneForAction(ZONE_MODE aMode, ZONE **aZone)
Draw a polygon, that is added as a zone or a keepout area.
std::unique_ptr< PCB_SHAPE > drawOneBezier(const TOOL_EVENT &aTool, const OPT_VECTOR2I &aStartingPoint, const OPT_VECTOR2I &aStartingControl1Point, DRAW_ONE_RESULT &aResult)
Draw a bezier curve.
int m_DisallowFlags
Definition: drc_rule.h:203
SEVERITY GetSeverity() const
Definition: drc_rule.h:181
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:160
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...
virtual PICKED_ITEMS_LIST * PopCommandFromUndoList()
Return the last command to undo and remove it from list, nothing is deleted.
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...
WX_INFOBAR * GetInfoBar()
void SetMsgPanel(const std::vector< MSG_PANEL_ITEM > &aList)
Clear the message panel and populates it with the contents of aList.
void DisplayConstraintsMsg(const wxString &msg)
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:97
virtual void SetPosition(const VECTOR2I &aPos)
Definition: eda_item.h:260
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:141
const KIID m_Uuid
Definition: eda_item.h:507
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:109
const VECTOR2I & GetBezierC2() const
Definition: eda_shape.h:258
void SetCenter(const VECTOR2I &aCenter)
Definition: eda_shape.cpp:955
virtual void SetFilled(bool aFlag)
Definition: eda_shape.h:136
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:215
void SetStart(const VECTOR2I &aStart)
Definition: eda_shape.h:177
void SetShape(SHAPE_T aShape)
Definition: eda_shape.h:167
void SetEnd(const VECTOR2I &aEnd)
Definition: eda_shape.h:219
double GetLength() const
Definition: eda_shape.cpp:376
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Set the end point from the angle center and start.
Definition: eda_shape.cpp:1104
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true)
Definition: eda_text.cpp:533
virtual bool IsVisible() const
Definition: eda_text.h:184
void SetTextPos(const VECTOR2I &aPoint)
Definition: eda_text.cpp:578
void SetMirrored(bool isMirrored)
Definition: eda_text.cpp:393
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.cpp:284
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition: eda_text.cpp:307
void MoveAnchorPosition(const VECTOR2I &aMoveVector)
Move the reference point of the footprint.
Definition: footprint.cpp:2488
VECTOR2I GetPosition() const override
Definition: footprint.h:227
bool GetSnap() const
Definition: grid_helper.h:110
void SetSnap(bool aSnap)
Definition: grid_helper.h:109
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
virtual RENDER_SETTINGS * GetSettings()=0
Return a pointer to current settings that are going to be used when drawing items.
Represents an assistant draw when interactively drawing an arc on a canvas.
Definition: arc_assistant.h:41
void SetUnits(EDA_UNITS aUnits)
Definition: arc_assistant.h:71
Manage the construction of a circular arc though sequential setting of critical points: center,...
VECTOR2I GetOrigin() const
< Get the center point of the arc (valid when state > SET_ORIGIN)
ARC_STEPS GetStep() const
Get the current step the manager is on (useful when drawing something depends on the current state)
void ToggleClockwise()
Set angle snapping (for the next point)
VECTOR2I GetStartRadiusEnd() const
Get the coordinates of the arc end point.
VECTOR2I GetEndRadiusEnd() const
Get the radius of the arc (valid if step >= SET_START)
@ SET_ANGLE
Waiting to lock in the arc end point.
@ SET_START
Waiting to lock in the arc start point.
Represents an assistant draw when interactively drawing a bezier on a canvas.
void SetUnits(EDA_UNITS aUnits)
Manage the construction of a bezier through a series of steps.
VECTOR2I GetControlC2() const
Get the coordinates of the arc end point.
VECTOR2I GetStart() const
< Get the center point of the arc (valid when state > SET_ORIGIN)
@ SET_END
Waiting to lock in the end point.
@ SET_START
Waiting to lock in the start point.
BEZIER_STEPS GetStep() const
Get the current step the manager is on (useful when drawing something depends on the current state)
void AddPoint(const VECTOR2I &aPt, bool aLockIn)
Add a point to the construction manager.
void RemoveLastPoint()
Undo the last point, and move the manager back to the previous step.
Represents an assistant draw when interactively drawing a line or circle on a canvas.
Represent a very simple geometry manager for items that have a start and end point.
void SetOrigin(const VECTOR2I &aOrigin)
< Set the origin of the ruler (the fixed end)
void Reset()
Reset the manager to the initial state.
void SetEnd(const VECTOR2I &aEnd)
Set the current end of the rectangle (the end that moves with the cursor.
Container for all the knowledge about how graphical objects are drawn on any output surface/device.
const std::set< int > & GetHighlightNetCodes() const
Return the netcode of currently highlighted net.
virtual void CaptureCursor(bool aEnabled)
Force the cursor to stay within the drawing panel area.
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.
virtual void WarpMouseCursor(const VECTOR2D &aPosition, bool aWorldCoordinates=false, bool aWarpView=false)=0
If enabled (.
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 SetCursorPosition(const VECTOR2D &aPosition, bool aWarpView=true, bool aTriggeredByArrows=false, long aArrowCommand=0)=0
Move cursor to the requested position expressed in world coordinates.
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
virtual void PinCursorInsideNonAutoscrollArea(bool aWarpMouseCursor)=0
bool IsBOARD_ITEM() const
Definition: view_item.h:102
void ShowPreview(bool aShow=true)
Definition: view.cpp:1733
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:297
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:332
int Query(const BOX2I &aRect, std::vector< LAYER_ITEM_PAIR > &aResult) const
Find all visible items that touch or are within the rectangle aRect.
Definition: view.cpp:410
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:1675
void ClearPreview()
Definition: view.cpp:1697
void RecacheAllItems()
Rebuild GAL display lists.
Definition: view.cpp:1441
std::pair< VIEW_ITEM *, int > LAYER_ITEM_PAIR
Definition: view.h:71
void AddToPreview(VIEW_ITEM *aItem, bool aTakeOwnership=true)
Definition: view.cpp:1719
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition: view.h:418
PAINTER * GetPainter() const
Return the painter object used by the view for drawing #VIEW_ITEMS.
Definition: view.h:216
Definition: kiid.h:49
LSET is a set of PCB_LAYER_IDs.
Definition: lset.h:37
static LSET AllCuMask(int aCuLayerCount=MAX_CU_LAYERS)
Return a mask holding the requested number of Cu PCB_LAYER_IDs.
Definition: lset.cpp:583
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:297
static const LSET & AllLayersMask()
Definition: lset.cpp:627
T Min() const
Definition: minoptmax.h:33
const wxString & GetNetname() const
Definition: netinfo.h:114
Container for NETINFO_ITEM elements, which are the nets.
Definition: netinfo.h:346
static const int ORPHANED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition: netinfo.h:385
NETINFO_ITEM * GetNetItem(int aNetCode) const
void ForEachUniqueLayer(const std::function< void(PCB_LAYER_ID)> &aMethod) const
Runs the given callable for each active unique copper layer in this padstack, meaning F_Cu for MODE::...
Definition: padstack.cpp:881
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition: padstack.h:144
Definition: pad.h:54
static TOOL_ACTION deleteLastPoint
Definition: pcb_actions.h:219
static TOOL_ACTION toggleHV45Mode
Definition: pcb_actions.h:540
static TOOL_ACTION drawRuleArea
Definition: pcb_actions.h:211
static TOOL_ACTION changeDimensionArrows
Switch between dimension arrow directions.
Definition: pcb_actions.h:232
static TOOL_ACTION drawBezier
Definition: pcb_actions.h:194
static TOOL_ACTION placeText
Definition: pcb_actions.h:196
static TOOL_ACTION drawOrthogonalDimension
Definition: pcb_actions.h:207
static TOOL_ACTION drawRectangle
Definition: pcb_actions.h:191
static TOOL_ACTION setAnchor
Definition: pcb_actions.h:218
static TOOL_ACTION placeReferenceImage
Definition: pcb_actions.h:195
static TOOL_ACTION drawCircle
Definition: pcb_actions.h:192
static TOOL_ACTION tuneDiffPair
Definition: pcb_actions.h:248
static TOOL_ACTION trackViaSizeChanged
Definition: pcb_actions.h:386
static TOOL_ACTION layerChanged
Definition: pcb_actions.h:372
static TOOL_ACTION drawTable
Definition: pcb_actions.h:198
static TOOL_ACTION drawTextBox
Definition: pcb_actions.h:197
static TOOL_ACTION drawZoneCutout
Definition: pcb_actions.h:212
static TOOL_ACTION drawPolygon
Definition: pcb_actions.h:190
static TOOL_ACTION drawRadialDimension
Definition: pcb_actions.h:206
static TOOL_ACTION tuneSingleTrack
Definition: pcb_actions.h:247
static TOOL_ACTION properties
Activation of the edit tool.
Definition: pcb_actions.h:168
static TOOL_ACTION drawLeader
Definition: pcb_actions.h:208
static TOOL_ACTION tuneSkew
Definition: pcb_actions.h:249
static TOOL_ACTION incWidth
Increase width of currently drawn line.
Definition: pcb_actions.h:223
static TOOL_ACTION clearHighlight
Definition: pcb_actions.h:565
static TOOL_ACTION spacingDecrease
Definition: pcb_actions.h:200
static TOOL_ACTION placeImportedGraphics
Definition: pcb_actions.h:217
static TOOL_ACTION drawVia
Definition: pcb_actions.h:210
static TOOL_ACTION drawArc
Definition: pcb_actions.h:193
static TOOL_ACTION drawSimilarZone
Definition: pcb_actions.h:213
static TOOL_ACTION decWidth
Decrease width of currently drawn line.
Definition: pcb_actions.h:226
static TOOL_ACTION drawCenterDimension
Definition: pcb_actions.h:205
static TOOL_ACTION arcPosture
Switch posture when drawing arc.
Definition: pcb_actions.h:229
static TOOL_ACTION closeOutline
Definition: pcb_actions.h:220
static TOOL_ACTION amplIncrease
Definition: pcb_actions.h:201
static TOOL_ACTION amplDecrease
Definition: pcb_actions.h:202
static TOOL_ACTION lengthTunerSettings
Definition: pcb_actions.h:203
static TOOL_ACTION spacingIncrease
Definition: pcb_actions.h:199
static TOOL_ACTION drawLine
Definition: pcb_actions.h:189
static TOOL_ACTION drawAlignedDimension
Definition: pcb_actions.h:204
static TOOL_ACTION drawZone
Definition: pcb_actions.h:209
virtual bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
Definition: pcb_track.cpp:1968
Common, abstract interface for edit frames.
wxString GetDesignRulesPath()
Return the absolute path to the design rules file for the currently-loaded board.
void ClearListAndDeleteItems(PICKED_ITEMS_LIST *aList)
Definition: undo_redo.cpp:653
int ShowTextBoxPropertiesDialog(PCB_TEXTBOX *aTextBox)
APPEARANCE_CONTROLS * GetAppearancePanel()
void PutDataInPreviousState(PICKED_ITEMS_LIST *aList)
Used in undo or redo command.
Definition: undo_redo.cpp:258
void SetObjectVisible(GAL_LAYER_ID aLayer, bool aVisible=true)
const PCB_DISPLAY_OPTIONS & GetDisplayOptions() const
Display options control the way tracks, vias, outlines and other things are shown (for instance solid...
virtual PCB_LAYER_ID GetActiveLayer() const
virtual MAGNETIC_SETTINGS * GetMagneticItemsSettings()
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
PCB_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
BOARD * GetBoard() const
virtual BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Return the BOARD_DESIGN_SETTINGS for the open project.
virtual BOARD_ITEM_CONTAINER * GetModel() const =0
Abstract dimension API.
void Update()
Update the dimension's cached text and geometry.
int GetLineThickness() const
void SetExtensionOffset(int aOffset)
void SetLineThickness(int aWidth)
void SetArrowLength(int aLength)
virtual const VECTOR2I & GetStart() const
The dimension's origin is the first feature point for the dimension.
DIM_ARROW_DIRECTION GetArrowDirection() const
virtual void SetEnd(const VECTOR2I &aPoint)
virtual void SetStart(const VECTOR2I &aPoint)
void SetArrowDirection(const DIM_ARROW_DIRECTION &aDirection)
int GetArrowLength() const
virtual const VECTOR2I & GetEnd() const
For better understanding of the points that make a dimension:
double GetAngle() const
Return the angle of the crossbar.
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
Mark the center of a circle or arc with a cross shape.
A leader is a dimension-like object pointing to a specific point.
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
A radial dimension indicates either the radius or diameter of an arc or circle.
VECTOR2I GetKnee() const
HIGH_CONTRAST_MODE m_ContrastModeDisplay
How inactive layers are displayed.
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
The main frame for Pcbnew.
VECTOR2I AlignToSegment(const VECTOR2I &aPoint, const SEG &aSeg)
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:53
Object to handle a bitmap image that can be inserted in a PCB.
PCB_LAYER_ID m_Route_Layer_TOP
Definition: pcb_screen.h:43
PCB_LAYER_ID m_Route_Layer_BOTTOM
Definition: pcb_screen.h:44
The selection tool: currently supports:
PCB_SELECTION & GetSelection()
EDA_ITEM * GetTopLeftItem(bool aFootprintsOnly=false) const override
bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
Definition: pcb_shape.h:123
void SetPosition(const VECTOR2I &aPos) override
Definition: pcb_shape.h:78
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
Definition: pcb_shape.cpp:176
void SetStroke(const STROKE_PARAMS &aStroke) override
Definition: pcb_shape.h:92
void Normalize() override
Perform any normalization required after a user rotate and/or flip.
Definition: pcb_shape.cpp:483
VECTOR2I GetPosition() const override
Definition: pcb_shape.h:79
T * frame() const
virtual bool Is45Limited() const
Should the tool use its 45° mode option?
KIGFX::VIEW_CONTROLS * controls() const
BOARD * board() const
@ IPO_SINGLE_CLICK
Create an item immediately on placement starting, otherwise show the pencil cursor until the item is ...
@ IPO_REPEAT
Allow repeat placement of the item.
bool m_isFootprintEditor
void doInteractiveItemPlacement(const TOOL_EVENT &aTool, INTERACTIVE_PLACER_BASE *aPlacer, const wxString &aCommitMessage, int aOptions=IPO_ROTATE|IPO_FLIP|IPO_REPEAT)
Helper function for performing a common interactive idiom: wait for a left click, place an item there...
const PCB_SELECTION & selection() const
FOOTPRINT * footprint() const
void SetEnd(const VECTOR2I &aEnd)
Definition: pcb_track.h:148
void SetStart(const VECTOR2I &aStart)
Definition: pcb_track.h:151
virtual EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition: pcb_track.cpp:69
const VECTOR2I & GetStart() const
Definition: pcb_track.h:152
const VECTOR2I & GetEnd() const
Definition: pcb_track.h:149
virtual int GetWidth() const
Definition: pcb_track.h:146
VECTOR2I GetPosition() const override
Definition: pcb_track.h:551
std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT) const override
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
Definition: pcb_track.cpp:2203
const PADSTACK & Padstack() const
Definition: pcb_track.h:453
int GetWidth() const override
Definition: pcb_track.cpp:381
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pcb_track.cpp:1263
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: pcb_track.cpp:724
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition: pgm_base.cpp:556
A holder to handle information on schematic or board items.
ROUTING_SETTINGS & Settings()
Definition: pns_router.h:206
ROUTER * Router() const
Class that handles the drawing of a polygon, including management of last corner deletion and drawing...
bool AddPoint(const VECTOR2I &aPt)
Lock in a polygon point.
@ DIRECT
Unconstrained point-to-point.
void SetCursorPosition(const VECTOR2I &aPos)
Set the current cursor position.
bool NewPointClosesOutline(const VECTOR2I &aPt) const
std::optional< VECTOR2I > DeleteLastCorner()
Remove the last-added point from the polygon.
void SetFinished()
Mark the polygon finished and update the client.
void SetLeaderMode(LEADER_MODE aMode)
Set the leader mode to use when calculating the leader/returner lines.
void Reset()
Clear the manager state and start again.
RAII class that sets an value at construction and resets it to the original value at destruction.
Definition: seg.h:42
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition: seg.cpp:439
bool Contains(const SEG &aSeg) const
Definition: seg.h:314
int AddItemToSel(const TOOL_EVENT &aEvent)
virtual void Add(EDA_ITEM *aItem)
Definition: selection.cpp:42
virtual void Remove(EDA_ITEM *aItem)
Definition: selection.cpp:60
virtual void Clear() override
Remove all the stored items from the group.
Definition: selection.h:93
int Size() const
Returns the number of selected parts.
Definition: selection.h:116
void SetReferencePoint(const VECTOR2I &aP)
Definition: selection.cpp:178
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:110
Represent a polyline containing arcs as well as line segments: A chain of connected line and/or arc s...
virtual const SEG GetSegment(int aIndex) const override
int SegmentCount() const
Return the number of segments in this line chain.
Represent a set of closed polygons.
bool Collide(const SHAPE *aShape, int aClearance=0, int *aActual=nullptr, VECTOR2I *aLocation=nullptr) const override
Check if the boundary of shape (this) lies closer to the shape aShape than aClearance,...
SHAPE_LINE_CHAIN & Outline(int aIndex)
Return the reference to aIndex-th outline in the set.
int OutlineCount() const
Return the number of outlines in the set.
int GetWidth() const
void SetLineStyle(LINE_STYLE aLineStyle)
void SetWidth(int aWidth)
void SetColor(const KIGFX::COLOR4D &aColor)
GR_TEXT_H_ALIGN_T m_Halign
GR_TEXT_V_ALIGN_T m_Valign
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.
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:55
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:44
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:220
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:38
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:78
@ SHUTDOWN
Tool is being shut down.
Definition: tool_base.h:84
Generic, UI-independent tool event.
Definition: tool_event.h:168
bool HasPosition() const
Returns if it this event has a valid position (true for mouse events and context-menu or hotkey-based...
Definition: tool_event.h:257
bool DisableGridSnapping() const
Definition: tool_event.h:368
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition: tool_event.h:290
bool IsReactivate() const
Control whether the tool is first being pushed to the stack or being reactivated after a pause.
Definition: tool_event.h:270
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:465
void SetContextMenu(ACTION_MENU *aMenu, CONTEXT_MENU_TRIGGER aTrigger=CMENU_BUTTON)
Assign a context menu and tells when it should be activated.
void RunMainStack(std::function< void()> aFunc)
Call a function using the main stack.
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).
std::unique_ptr< TOOL_MENU > m_menu
The functions below are not yet implemented - their interface may change.
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.
void DeactivateTool()
Deactivate the currently active tool.
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:150
void PrimeTool(const VECTOR2D &aPosition)
"Prime" a tool by sending a cursor left-click event with the mouse position set to the passed in posi...
TOOLS_HOLDER * GetToolHolder() const
Definition: tool_manager.h:406
bool PostAction(const std::string &aActionName, T aParam)
Run the specified action after the current action (coroutine) ends.
Definition: tool_manager.h:235
void VetoContextMenuMouseWarp()
Disable mouse warping after the current context menu is closed.
Definition: tool_manager.h:515
KIGFX::VIEW * GetView() const
Definition: tool_manager.h:395
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
EDA_UNITS GetUserUnits() const
void update() override
Update menu state stub.
OPT_TOOL_EVENT eventHandler(const wxMenuEvent &aEvent) override
Event handler stub.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
A modified version of the wxInfoBar class that allows us to:
Definition: wx_infobar.h:76
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
Definition: wx_infobar.cpp:190
MESSAGE_TYPE GetMessageType() const
Definition: wx_infobar.h:101
An adjunct helper to the DRAWING_TOOL interactive tool, which handles incoming geometry changes from ...
void OnGeometryChange(const POLYGON_GEOM_MANAGER &aMgr) override
Called when the polygon is complete.
ZONE * GetZone() const
static bool IsZoneFillAction(const TOOL_EVENT *aEvent)
Handle a list of polygons defining a copper zone.
Definition: zone.h:74
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition: zone.h:699
bool GetDoNotAllowVias() const
Definition: zone.h:715
SHAPE_POLY_SET * Outline()
Definition: zone.h:335
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition: zone.cpp:616
PCB_LAYER_ID GetFirstLayer() const
Definition: zone.cpp:482
This file is part of the common library.
static void updateArcFromConstructionMgr(const KIGFX::PREVIEW::ARC_GEOM_MANAGER &aMgr, PCB_SHAPE &aArc)
Update an arc PCB_SHAPE from the current state of an Arc Geometry Manager.
static void updateSegmentFromGeometryMgr(const KIGFX::PREVIEW::TWO_POINT_GEOMETRY_MANAGER &aMgr, PCB_SHAPE *aGraphic)
Update a PCB_SHAPE from the current state of a #TWO_POINT_GEOMETRY_MANAGER.
@ DISALLOW_CONSTRAINT
Definition: drc_rule.h:69
@ CLEARANCE_CONSTRAINT
Definition: drc_rule.h:49
@ HOLE_CLEARANCE_CONSTRAINT
Definition: drc_rule.h:51
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition: eda_angle.h:404
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:406
std::vector< EDA_ITEM * > EDA_ITEMS
Define list of drawing items for screens.
Definition: eda_item.h:557
#define IS_NEW
New item, just created.
#define IS_MOVING
Item being moved.
SHAPE_T
Definition: eda_shape.h:43
@ NONE
Definition: eda_shape.h:69
EDA_UNITS
Definition: eda_units.h:48
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)...
a few functions useful in geometry calculations.
VECTOR2< T > GetVectorSnapped45(const VECTOR2< T > &aVec, bool only45=false)
Snap a vector onto the nearest 0, 45 or 90 degree line.
VECTOR2< ret_type > GetClampedCoords(const VECTOR2< in_type > &aCoords, pad_type aPadding=1u)
Clamps a vector to values that can be negated, respecting numeric limits of coordinates data type wit...
void InferBold(TEXT_ATTRIBUTES *aAttrs)
Definition: gr_text.h:82
@ GRID_TEXT
Definition: grid_helper.h:49
@ GRID_GRAPHICS
Definition: grid_helper.h:50
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition: layer_ids.h:788
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition: layer_ids.h:663
@ LAYER_FILLED_SHAPES
Copper graphic shape opacity/visibility (color ignored).
Definition: layer_ids.h:312
@ LAYER_ZONES
Control for copper zone opacity/visibility (color ignored).
Definition: layer_ids.h:294
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:60
@ B_Cu
Definition: layer_ids.h:65
@ UNDEFINED_LAYER
Definition: layer_ids.h:61
@ F_Cu
Definition: layer_ids.h:64
This file contains miscellaneous commonly used macros and functions.
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition: eda_angle.h:393
ZONE_MODE
Definition: pcb_actions.h:35
Class to handle a set of BOARD_ITEMs.
@ ID_POPUP_PCB_SELECT_CUSTOM_WIDTH
Definition: pcbnew_id.h:20
@ ID_POPUP_PCB_SELECT_VIASIZE1
Definition: pcbnew_id.h:39
@ ID_POPUP_PCB_SELECT_VIASIZE16
Definition: pcbnew_id.h:54
int GetUserUnits()
Return the currently selected user unit value for the interface.
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:893
see class PGM_BASE
Class that computes missing connections on a PCB.
@ RPT_SEVERITY_IGNORE
std::optional< VECTOR2I > OPT_VECTOR2I
Definition: seg.h:39
bool NoPrintableChars(const wxString &aString)
Return true if the string is empty or contains only whitespace.
LINE_STYLE
Dashed line types.
Definition: stroke_params.h:46
constexpr int mmToIU(double mm) const
Definition: base_units.h:92
MAGNETIC_OPTIONS tracks
MAGNETIC_OPTIONS pads
A filename or source description, a problem input line, a line number, a byte offset,...
Definition: ki_exception.h:120
Container to handle a stock of specific vias each with unique diameter and drill sizes in the BOARD c...
Parameters used to fully describe a zone creation process.
ZONE_MODE m_mode
Zone settings source (for similar and cutout zones)
bool m_keepout
< Should create a keepout zone?
ZONE * m_sourceZone
Zone leader mode.
PCB_LAYER_ID m_layer
The zone mode to operate in.
SHAPE_CIRCLE circle(c.m_circle_center, c.m_circle_radius)
int clearance
constexpr int delta
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_TOP
@ CMENU_NOW
Right now (after TOOL_INTERACTIVE::SetContextMenu).
Definition: tool_event.h:153
std::optional< TOOL_EVENT > OPT_TOOL_EVENT
Definition: tool_event.h:633
@ TA_CHOICE_MENU_CHOICE
Context menu choice.
Definition: tool_event.h:98
@ TA_CHOICE_MENU_UPDATE
Context menu update.
Definition: tool_event.h:94
@ TA_CHOICE_MENU_CLOSED
Context menu is closed, no matter whether anything has been chosen or not.
Definition: tool_event.h:101
@ MD_CTRL
Definition: tool_event.h:144
@ MD_SHIFT
Definition: tool_event.h:143
@ BUT_LEFT
Definition: tool_event.h:132
@ BUT_RIGHT
Definition: tool_event.h:133
bool TestSegmentHit(const VECTOR2I &aRefPoint, const VECTOR2I &aStart, const VECTOR2I &aEnd, int aDist)
Test if aRefPoint is with aDistance on the line defined by aStart and aEnd.
Definition: trigo.cpp:175
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition: typeinfo.h:78
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:88
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:105
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:102
@ 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_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_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_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_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
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:695
VECTOR2< double > VECTOR2D
Definition: vector2d.h:694