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, see <https://www.gnu.org/licenses/>.
20 */
21
22#include "drawing_tool.h"
23#include "geometry/shape_rect.h"
24#include "dialog_table_properties.h"
25
26#include <pgm_base.h>
28#include <pcbnew_settings.h>
31#include <dialogs/dialog_text_properties.h>
42#include <router/router_tool.h>
43#include <status_popup.h>
44#include <tool/tool_manager.h>
45#include <tools/pcb_actions.h>
47#include <kiplatform/ui.h>
49#include <pcb_barcode.h>
53#include <view/view.h>
55#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 <drc/drc_engine.h>
65#include <drc/drc_rule.h>
66#include <confirm.h>
67#include <footprint.h>
68#include <macros.h>
69#include <gal/painter.h>
70#include <pad.h>
71#include <pcb_edit_frame.h>
72#include <pcb_group.h>
73#include <pcb_point.h>
74#include <pcb_reference_image.h>
75#include <pcb_text.h>
76#include <pcb_textbox.h>
77#include <pcb_table.h>
78#include <pcb_tablecell.h>
79#include <pcb_track.h>
80#include <pcb_dimension.h>
81#include <pcbnew_id.h>
82#include <scoped_set_reset.h>
83#include <string_utils.h>
84#include <zone.h>
85#include <fix_board_shape.h>
86#include <view/view_controls.h>
87
88const unsigned int DRAWING_TOOL::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
89
91
92
94{
95public:
97 ACTION_MENU( true )
98 {
100 SetTitle( _( "Select Via Size" ) );
101 }
102
103protected:
104 ACTION_MENU* create() const override
105 {
106 return new VIA_SIZE_MENU();
107 }
108
109 void update() override
110 {
113 bool useIndex = !bds.m_UseConnectedTrackWidth && !bds.UseCustomTrackViaSize();
114 wxString msg;
115
116 Clear();
117
118 Append( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, _( "Use Custom Values..." ),
119 _( "Specify custom track and via sizes" ), wxITEM_CHECK );
121
122 AppendSeparator();
123
124 for( int i = 1; i < (int) bds.m_ViasDimensionsList.size(); i++ )
125 {
127
128 if( via.m_Drill > 0 )
129 {
130 msg.Printf( _("Via %s, hole %s" ),
131 frame->MessageTextFromValue( via.m_Diameter ),
132 frame->MessageTextFromValue( via.m_Drill ) );
133 }
134 else
135 {
136 msg.Printf( _( "Via %s" ),
137 frame->MessageTextFromValue( via.m_Diameter ) );
138 }
139
140 int menuIdx = ID_POPUP_PCB_SELECT_VIASIZE1 + i;
141 Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
142 Check( menuIdx, useIndex && bds.GetViaSizeIndex() == i );
143 }
144 }
145
146 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
147 {
150 int id = aEvent.GetId();
151
152 // On Windows, this handler can be called with an event ID not existing in any
153 // menuitem, so only set flags when we have an ID match.
154
156 {
157 DIALOG_TRACK_VIA_SIZE sizeDlg( frame, bds );
158
159 if( sizeDlg.ShowModal() == wxID_OK )
160 {
161 bds.UseCustomTrackViaSize( true );
162 bds.m_UseConnectedTrackWidth = false;
163 }
164 }
166 {
167 bds.UseCustomTrackViaSize( false );
168 bds.m_UseConnectedTrackWidth = false;
170 }
171
173 }
174};
175
176
178 PCB_TOOL_BASE( "pcbnew.InteractiveDrawing" ),
179 m_view( nullptr ),
180 m_controls( nullptr ),
181 m_board( nullptr ),
182 m_frame( nullptr ),
183 m_mode( MODE::NONE ),
184 m_inDrawingTool( false ),
187 m_pickerItem( nullptr ),
188 m_tuningPattern( nullptr )
189{
190}
191
192
196
197
199{
200 auto haveHighlight =
201 [this]( const SELECTION& sel )
202 {
203 KIGFX::RENDER_SETTINGS* cfg = m_toolMgr->GetView()->GetPainter()->GetSettings();
204
205 return !cfg->GetHighlightNetCodes().empty();
206 };
207
208 auto activeToolFunctor =
209 [this]( const SELECTION& aSel )
210 {
211 return m_mode != MODE::NONE;
212 };
213
214 // some interactive drawing tools can undo the last point
215 auto canUndoPoint =
216 [this]( const SELECTION& aSel )
217 {
218 return ( m_mode == MODE::ARC
219 || m_mode == MODE::ZONE
222 || m_mode == MODE::BEZIER
223 || m_mode == MODE::LINE );
224 };
225
226 // functor for tools that can automatically close the outline
227 auto canCloseOutline =
228 [this]( const SELECTION& aSel )
229 {
230 return ( m_mode == MODE::ZONE
233 };
234
235 auto arcToolActive =
236 [this]( const SELECTION& aSel )
237 {
238 return m_mode == MODE::ARC;
239 };
240
241 auto viaToolActive =
242 [this]( const SELECTION& aSel )
243 {
244 return m_mode == MODE::VIA;
245 };
246
247 auto tuningToolActive =
248 [this]( const SELECTION& aSel )
249 {
250 return m_mode == MODE::TUNING;
251 };
252
253 auto dimensionToolActive =
254 [this]( const SELECTION& aSel )
255 {
256 return m_mode == MODE::DIMENSION;
257 };
258
259 CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu();
260
261 // cancel current tool goes in main context menu at the top if present
262 ctxMenu.AddItem( ACTIONS::cancelInteractive, activeToolFunctor, 1 );
263 ctxMenu.AddSeparator( 1 );
264
265 ctxMenu.AddItem( PCB_ACTIONS::clearHighlight, haveHighlight, 2 );
266 ctxMenu.AddSeparator( haveHighlight, 2 );
267
268 // tool-specific actions
269 ctxMenu.AddItem( PCB_ACTIONS::closeOutline, canCloseOutline, 200 );
270 ctxMenu.AddItem( PCB_ACTIONS::deleteLastPoint, canUndoPoint, 200 );
271 ctxMenu.AddItem( PCB_ACTIONS::arcPosture, arcToolActive, 200 );
272 ctxMenu.AddItem( PCB_ACTIONS::spacingIncrease, tuningToolActive, 200 );
273 ctxMenu.AddItem( PCB_ACTIONS::spacingDecrease, tuningToolActive, 200 );
274 ctxMenu.AddItem( PCB_ACTIONS::amplIncrease, tuningToolActive, 200 );
275 ctxMenu.AddItem( PCB_ACTIONS::amplDecrease, tuningToolActive, 200 );
276 ctxMenu.AddItem( PCB_ACTIONS::lengthTunerSettings, tuningToolActive, 200 );
277 ctxMenu.AddItem( PCB_ACTIONS::changeDimensionArrows, dimensionToolActive, 200 );
278
279 ctxMenu.AddSeparator( 500 );
280
281 std::shared_ptr<VIA_SIZE_MENU> viaSizeMenu = std::make_shared<VIA_SIZE_MENU>();
282 viaSizeMenu->SetTool( this );
283 m_menu->RegisterSubMenu( viaSizeMenu );
284 ctxMenu.AddMenu( viaSizeMenu.get(), viaToolActive, 500 );
285
286 ctxMenu.AddSeparator( 500 );
287
288 // Type-specific sub-menus will be added for us by other tools
289 // For example, zone fill/unfill is provided by the PCB control tool
290
291 // Finally, add the standard zoom/grid items
292 getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( *m_menu.get() );
293
294 return true;
295}
296
297
299{
300 // Init variables used by every drawing tool
301 m_view = getView();
305
306 // Re-initialize session attributes
307 const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
308
309 if( aReason == RESET_REASON::SHUTDOWN )
310 return;
311
312 m_layer = m_frame->GetActiveLayer();
313 m_stroke.SetWidth( bds.GetLineThickness( m_layer ) );
314 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
315 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
316
317 m_textAttrs.m_Size = bds.GetTextSize( m_layer );
318 m_textAttrs.m_StrokeWidth = bds.GetTextThickness( m_layer );
320 m_textAttrs.m_Italic = bds.GetTextItalic( m_layer );
321 m_textAttrs.m_KeepUpright = bds.GetTextUpright( m_layer );
322 m_textAttrs.m_Mirrored = m_board->IsBackLayer( m_layer );
325
327}
328
329
334
335
337{
338 if( m_frame )
339 {
340 switch( GetAngleSnapMode() )
341 {
343 m_frame->DisplayConstraintsMsg( _( "Constrain to H, V, 45" ) );
344 break;
346 m_frame->DisplayConstraintsMsg( _( "Constrain to H, V" ) );
347 break;
348 default:
349 m_frame->DisplayConstraintsMsg( wxString( "" ) );
350 break;
351 }
352 }
353}
354
355
357{
358 if( m_isFootprintEditor && !m_frame->GetModel() )
359 return 0;
360
361 if( m_inDrawingTool )
362 return 0;
363
365
366 BOARD_ITEM* parent = m_frame->GetModel();
367 PCB_SHAPE* line = new PCB_SHAPE( parent );
368 BOARD_COMMIT commit( m_frame );
369 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::LINE );
370 std::optional<VECTOR2D> startingPoint;
371 std::stack<PCB_SHAPE*> committedLines;
372
373 line->SetShape( SHAPE_T::SEGMENT );
374 line->SetFlags( IS_NEW );
375
376 if( aEvent.HasPosition() )
377 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
378
379 m_frame->PushTool( aEvent );
380 Activate();
381
382 while( drawShape( aEvent, &line, startingPoint, &committedLines ) )
383 {
384 if( line )
385 {
386 commit.Add( line );
387 commit.Push( _( "Draw Line" ) );
388 startingPoint = VECTOR2D( line->GetEnd() );
389 committedLines.push( line );
390 }
391 else
392 {
393 startingPoint = std::nullopt;
394 }
395
396 line = new PCB_SHAPE( parent );
397 line->SetShape( SHAPE_T::SEGMENT );
398 line->SetFlags( IS_NEW );
399 }
400
401 return 0;
402}
403
404
406{
407 if( m_isFootprintEditor && !m_frame->GetModel() )
408 return 0;
409
410 if( m_inDrawingTool )
411 return 0;
412
414
415 bool isTextBox = aEvent.IsAction( &PCB_ACTIONS::drawTextBox );
416 PCB_SHAPE* rect = nullptr;
417 BOARD_COMMIT commit( m_frame );
418 BOARD_ITEM* parent = m_frame->GetModel();
419 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::RECTANGLE );
420 std::optional<VECTOR2D> startingPoint;
421
422 rect = isTextBox ? new PCB_TEXTBOX( parent ) : new PCB_SHAPE( parent );
424 rect->SetFilled( false );
425 rect->SetFlags( IS_NEW );
426
427 if( aEvent.HasPosition() )
428 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
429
430 m_frame->PushTool( aEvent );
431 Activate();
432
433 while( drawShape( aEvent, &rect, startingPoint, nullptr ) )
434 {
435 if( rect )
436 {
437 bool cancelled = false;
438
439 if( PCB_TEXTBOX* textbox = dynamic_cast<PCB_TEXTBOX*>( rect ) )
440 cancelled = m_frame->ShowTextBoxPropertiesDialog( textbox ) != wxID_OK;
441
442 if( cancelled )
443 {
444 delete rect;
445 rect = nullptr;
446 }
447 else
448 {
449 rect->Normalize();
450 commit.Add( rect );
451 commit.Push( isTextBox ? _( "Draw Text Box" ) : _( "Draw Rectangle" ) );
452
453 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, rect );
454 }
455 }
456
457 rect = isTextBox ? new PCB_TEXTBOX( parent ) : new PCB_SHAPE( parent );
459 rect->SetFilled( false );
460 rect->SetFlags( IS_NEW );
461 startingPoint = std::nullopt;
462 }
463
464 return 0;
465}
466
467
469{
470 return runSimpleShapeDraw( aEvent, SHAPE_T::CIRCLE, MODE::CIRCLE, _( "Draw Circle" ),
471 [this]( const TOOL_EVENT& e, PCB_SHAPE** s, std::optional<VECTOR2D> sp )
472 {
473 return drawShape( e, s, sp, nullptr );
474 } );
475}
476
477
479{
480 return runSimpleShapeDraw( aEvent, SHAPE_T::ELLIPSE, MODE::ELLIPSE, _( "Draw Ellipse" ),
481 [this]( const TOOL_EVENT& e, PCB_SHAPE** s, std::optional<VECTOR2D> sp )
482 {
483 return drawShape( e, s, sp, nullptr );
484 } );
485}
486
488{
489 return runSimpleShapeDraw( aEvent, SHAPE_T::ELLIPSE_ARC, MODE::ELLIPSE_ARC, _( "Draw Elliptical Arc" ),
490 [this]( const TOOL_EVENT& e, PCB_SHAPE** s, std::optional<VECTOR2D> sp )
491 {
492 return drawShape( e, s, sp, nullptr );
493 } );
494}
495
496
498{
499 if( m_isFootprintEditor && !m_frame->GetModel() )
500 return 0;
501
502 if( m_inDrawingTool )
503 return 0;
504
506
507 BOARD_ITEM* parent = m_frame->GetModel();
508 PCB_SHAPE* arc = new PCB_SHAPE( parent );
509 BOARD_COMMIT commit( m_frame );
510 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ARC );
511 std::optional<VECTOR2D> startingPoint;
512
513 arc->SetShape( SHAPE_T::ARC );
514 arc->SetFlags( IS_NEW );
515
516 m_frame->PushTool( aEvent );
517 Activate();
518
519 if( aEvent.HasPosition() )
520 startingPoint = aEvent.Position();
521
522 while( drawArc( aEvent, &arc, startingPoint ) )
523 {
524 if( arc )
525 {
526 commit.Add( arc );
527 commit.Push( _( "Draw Arc" ) );
528
529 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, arc );
530 }
531
532 arc = new PCB_SHAPE( parent );
533 arc->SetShape( SHAPE_T::ARC );
534 arc->SetFlags( IS_NEW );
535
536 startingPoint = std::nullopt;
537 }
538
539 return 0;
540}
541
542
544{
545 if( m_isFootprintEditor && !m_frame->GetModel() )
546 return 0;
547
548 if( m_inDrawingTool )
549 return 0;
550
552
553 BOARD_COMMIT commit( m_frame );
554 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::BEZIER );
555 OPT_VECTOR2I startingPoint, startingC1;
556
557 m_frame->PushTool( aEvent );
558 Activate();
559
560 if( aEvent.HasPosition() )
561 startingPoint = aEvent.Position();
562
565 {
566 std::unique_ptr<PCB_SHAPE> bezier =
567 drawOneBezier( aEvent, startingPoint, startingC1, result );
568
569 // Anyting other than accepted means no chaining
570 startingPoint = std::nullopt;
571 startingC1 = std::nullopt;
572
573 // If a bezier was created, add it and go again
574 if( bezier )
575 {
576 PCB_SHAPE& bezierRef = *bezier;
577 commit.Add( bezier.release() );
578 commit.Push( _( "Draw Bezier" ) );
579
580 // Don't chain if reset (or accepted and reset)
582 {
583 startingPoint = bezierRef.GetEnd();
584
585 // If the last bezier has a zero C2 control arm, allow the user to define a new C1
586 // control arm for the next one.
587 if( bezierRef.GetEnd() != bezierRef.GetBezierC2() )
588 {
589 // Mirror the control point across the end point to get a tangent control point
590 startingC1 = bezierRef.GetEnd() - ( bezierRef.GetBezierC2() - bezierRef.GetEnd() );
591 }
592 }
593 }
594 }
595
596 return 0;
597}
598
599
601{
602 if( m_inDrawingTool )
603 return 0;
604
606
608 bool immediateMode = image != nullptr;
609 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
610 bool ignorePrimePosition = false;
611 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
612
614 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
615 BOARD_COMMIT commit( m_frame );
616 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::IMAGE );
617
619
620 // Add all the drawable symbols to preview
621 if( image )
622 {
623 image->SetPosition( cursorPos );
624 m_view->ClearPreview();
625 m_view->AddToPreview( image, false ); // Add, but not give ownership
626 }
627
628 m_frame->PushTool( aEvent );
629
630 auto setCursor =
631 [&]()
632 {
633 if( image )
634 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
635 else
636 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
637 };
638
639 auto cleanup =
640 [&] ()
641 {
643 m_view->ClearPreview();
644 m_view->RecacheAllItems();
645 delete image;
646 image = nullptr;
647 };
648
649 Activate();
650
651 // Must be done after Activate() so that it gets set into the correct context
652 getViewControls()->ShowCursor( true );
653
654 // Set initial cursor
655 setCursor();
656
657 // Prime the pump
658 if( image )
659 {
660 m_toolMgr->PostAction( ACTIONS::refreshPreview );
661 }
662 else if( aEvent.HasPosition() )
663 {
664 m_toolMgr->PrimeTool( aEvent.Position() );
665 }
666 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
667 {
668 m_toolMgr->PrimeTool( { 0, 0 } );
669 ignorePrimePosition = true;
670 }
671
672 // Main loop: keep receiving events
673 while( TOOL_EVENT* evt = Wait() )
674 {
675 setCursor();
676
677 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
678 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
679 cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(),
680 { m_frame->GetActiveLayer() }, GRID_GRAPHICS ),
682 m_controls->ForceCursorPosition( true, cursorPos );
683
684 if( evt->IsCancelInteractive() || ( image && evt->IsAction( &ACTIONS::undo ) ) )
685 {
686 if( image )
687 {
688 cleanup();
689 }
690 else
691 {
692 m_frame->PopTool( aEvent );
693 break;
694 }
695
696 if( immediateMode )
697 {
698 m_frame->PopTool( aEvent );
699 break;
700 }
701 }
702 else if( evt->IsActivate() )
703 {
704 if( image && evt->IsMoveTool() )
705 {
706 // We're already moving our own item; ignore the move tool
707 evt->SetPassEvent( false );
708 continue;
709 }
710
711 if( image )
712 {
713 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel image creation." ) );
714 evt->SetPassEvent( false );
715 continue;
716 }
717
718 if( evt->IsMoveTool() )
719 {
720 // Leave ourselves on the stack so we come back after the move
721 break;
722 }
723 else
724 {
725 m_frame->PopTool( aEvent );
726 break;
727 }
728 }
729 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
730 {
731 if( !image )
732 {
734
735 wxFileDialog dlg( m_frame, _( "Choose Image" ), wxEmptyString, wxEmptyString,
736 FILEEXT::ImageFileWildcard(), wxFD_OPEN );
737
739
740 bool cancelled = false;
741
743 [&]()
744 {
745 cancelled = dlg.ShowModal() != wxID_OK;
746 } );
747
748 if( cancelled )
749 continue;
750
751 // If we started with a hotkey which has a position then warp back to that.
752 // Otherwise update to the current mouse position pinned inside the autoscroll
753 // boundaries.
754 if( evt->IsPrime() && !ignorePrimePosition )
755 {
756 cursorPos = grid.Align( evt->Position() );
757 getViewControls()->WarpMouseCursor( cursorPos, true );
758 }
759 else
760 {
762 cursorPos = getViewControls()->GetMousePosition();
763 }
764
765 cursorPos = getViewControls()->GetMousePosition( true );
766
767 wxString fullFilename = dlg.GetPath();
768
769 if( wxFileExists( fullFilename ) )
770 image = new PCB_REFERENCE_IMAGE( m_frame->GetModel(), cursorPos );
771
772 if( !image || !image->GetReferenceImage().ReadImageFile( fullFilename ) )
773 {
774 wxMessageBox( wxString::Format(_( "Could not load image from '%s'." ), fullFilename ) );
775 delete image;
776 image = nullptr;
777 continue;
778 }
779
780 image->SetFlags( IS_NEW | IS_MOVING );
781 image->SetLayer( m_frame->GetActiveLayer() );
782
783 m_view->ClearPreview();
784 m_view->AddToPreview( image, false ); // Add, but not give ownership
785 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
786 selectionTool->AddItemToSel( image, false );
787
788 getViewControls()->SetCursorPosition( cursorPos, false );
789 setCursor();
790 m_view->ShowPreview( true );
791 }
792 else
793 {
794 commit.Add( image );
795 commit.Push( _( "Place Image" ) );
796
798
799 image = nullptr;
801
802 m_view->ClearPreview();
803
804 if( immediateMode )
805 {
806 m_frame->PopTool( aEvent );
807 break;
808 }
809 }
810 }
811 else if( evt->IsClick( BUT_RIGHT ) )
812 {
813 // Warp after context menu only if dragging...
814 if( !image )
815 m_toolMgr->VetoContextMenuMouseWarp();
816
817 m_menu->ShowContextMenu( selectionTool->GetSelection() );
818 }
819 else if( image && ( evt->IsAction( &ACTIONS::refreshPreview )
820 || evt->IsMotion() ) )
821 {
822 image->SetPosition( cursorPos );
823 m_view->ClearPreview();
824 m_view->AddToPreview( image, false ); // Add, but not give ownership
825 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
826 }
827 else if( image && evt->IsAction( &ACTIONS::doDelete ) )
828 {
829 cleanup();
830 }
831 else if( image && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
832 || evt->IsAction( &ACTIONS::redo ) ) )
833 {
834 wxBell();
835 }
836 else
837 {
838 evt->SetPassEvent();
839 }
840
841 // Enable autopanning and cursor capture only when there is an image to be placed
842 getViewControls()->SetAutoPan( image != nullptr );
843 getViewControls()->CaptureCursor( image != nullptr );
844 }
845
846 getViewControls()->SetAutoPan( false );
847 getViewControls()->CaptureCursor( false );
848 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
849
850 return 0;
851}
852
854{
856 m_drawingTool( aDrawingTool ),
857 m_frame( aFrame ),
858 m_gridHelper( aDrawingTool.GetManager(), aFrame.GetMagneticItemsSettings() )
859 {
860 }
861
862 std::unique_ptr<BOARD_ITEM> CreateItem() override
863 {
864 std::unique_ptr<PCB_POINT> new_point = std::make_unique<PCB_POINT>( m_frame.GetModel() );
865
866 PCB_LAYER_ID layer = m_frame.GetActiveLayer();
867 new_point->SetLayer( layer );
868
869 return new_point;
870 }
871
872 void SnapItem( BOARD_ITEM* aItem ) override
873 {
874 m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
875 m_gridHelper.SetUseGrid( !( m_modifiers & MD_CTRL ) );
876
877 KIGFX::VIEW_CONTROLS& viewControls = *m_drawingTool.GetManager()->GetViewControls();
878 const VECTOR2I position = viewControls.GetMousePosition();
879
880 VECTOR2I cursorPos = m_gridHelper.BestSnapAnchor( position, aItem->GetLayerSet() );
881 viewControls.ForceCursorPosition( true, cursorPos );
882 aItem->SetPosition( cursorPos );
883 }
884
888};
889
890
892{
893 if( m_isFootprintEditor && !m_frame->GetModel() )
894 return 0;
895
896 if( m_inDrawingTool )
897 return 0;
898
900
901 POINT_PLACER placer( *this, *frame() );
902 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::MD_POINT );
903
904 doInteractiveItemPlacement( aEvent, &placer, _( "Place point" ), IPO_REPEAT | IPO_SINGLE_CLICK );
905
906 return 0;
907}
908
909
911{
912 if( m_isFootprintEditor && !m_frame->GetModel() )
913 return 0;
914
915 if( m_inDrawingTool )
916 return 0;
917
919
920 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
921 PCB_TEXT* text = nullptr;
922 bool ignorePrimePosition = false;
923 const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
924 BOARD_COMMIT commit( m_frame );
925 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TEXT );
926 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
927
928 auto setCursor =
929 [&]()
930 {
931 if( text )
932 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
933 else
934 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
935 };
936
937 auto cleanup =
938 [&]()
939 {
941 m_controls->ForceCursorPosition( false );
942 m_controls->ShowCursor( true );
943 m_controls->SetAutoPan( false );
944 m_controls->CaptureCursor( false );
945 delete text;
946 text = nullptr;
947 };
948
950
951 m_frame->PushTool( aEvent );
952
953 Activate();
954 // Must be done after Activate() so that it gets set into the correct context
955 m_controls->ShowCursor( true );
956 m_controls->ForceCursorPosition( false );
957 // do not capture or auto-pan until we start placing some text
958 // Set initial cursor
959 setCursor();
960
961 if( aEvent.HasPosition() )
962 {
963 m_toolMgr->PrimeTool( aEvent.Position() );
964 }
965 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
966 {
967 m_toolMgr->PrimeTool( { 0, 0 } );
968 ignorePrimePosition = true;
969 }
970
971 // Main loop: keep receiving events
972 while( TOOL_EVENT* evt = Wait() )
973 {
974 setCursor();
975
976 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
977 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
978 VECTOR2I cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(),
979 { m_frame->GetActiveLayer() }, GRID_TEXT ),
981 m_controls->ForceCursorPosition( true, cursorPos );
982
983 if( evt->IsDrag() )
984 {
985 continue;
986 }
987 else if( evt->IsCancelInteractive() || ( text && evt->IsAction( &ACTIONS::undo ) ) )
988 {
989 if( text )
990 {
991 cleanup();
992 }
993 else
994 {
995 m_frame->PopTool( aEvent );
996 break;
997 }
998 }
999 else if( evt->IsActivate() )
1000 {
1001 if( text )
1002 cleanup();
1003
1004 if( evt->IsMoveTool() )
1005 {
1006 // leave ourselves on the stack so we come back after the move
1007 break;
1008 }
1009 else
1010 {
1011 m_frame->PopTool( aEvent );
1012 break;
1013 }
1014 }
1015 else if( evt->IsClick( BUT_RIGHT ) )
1016 {
1017 if( !text )
1018 m_toolMgr->VetoContextMenuMouseWarp();
1019
1020 m_menu->ShowContextMenu( selection() );
1021 }
1022 else if( evt->IsClick( BUT_LEFT ) )
1023 {
1024 bool placing = text != nullptr;
1025
1026 if( !text )
1027 {
1028 m_toolMgr->RunAction( ACTIONS::selectionClear );
1029
1030 m_controls->ForceCursorPosition( true, m_controls->GetCursorPosition() );
1031
1032 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
1033 TEXT_ATTRIBUTES textAttrs;
1034
1035 textAttrs.m_Size = bds.GetTextSize( layer );
1036 textAttrs.m_StrokeWidth = bds.GetTextThickness( layer );
1037 InferBold( &textAttrs );
1038 textAttrs.m_Italic = bds.GetTextItalic( layer );
1039 textAttrs.m_KeepUpright = bds.GetTextUpright( layer );
1040 textAttrs.m_Mirrored = m_board->IsBackLayer( layer );
1041 textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
1042 textAttrs.m_Valign = GR_TEXT_V_ALIGN_BOTTOM;
1043
1045 text = new PCB_TEXT( static_cast<FOOTPRINT*>( m_frame->GetModel() ) );
1046 else
1047 text = new PCB_TEXT( m_frame->GetModel() );
1048
1049 text->SetLayer( layer );
1050 text->SetAttributes( textAttrs );
1051 text->SetTextPos( cursorPos );
1052 text->SetFlags( IS_NEW ); // Prevent double undo commits
1053
1054 DIALOG_TEXT_PROPERTIES textDialog( m_frame, text );
1055 bool cancelled;
1056
1058 [&]()
1059 {
1060 // QuasiModal required for Scintilla auto-complete
1061 cancelled = textDialog.ShowQuasiModal() != wxID_OK;
1062 } );
1063
1064 if( cancelled || NoPrintableChars( text->GetText() ) )
1065 {
1066 delete text;
1067 text = nullptr;
1068 }
1069 else if( text->GetTextPos() != cursorPos )
1070 {
1071 // If the user modified the location then go ahead and place it there.
1072 // Otherwise we'll drag.
1073 placing = true;
1074 }
1075
1076 if( text )
1077 {
1078 if( !m_view->IsLayerVisible( text->GetLayer() ) )
1079 {
1080 m_frame->GetAppearancePanel()->SetLayerVisible( text->GetLayer(), true );
1081 m_frame->GetCanvas()->Refresh();
1082 }
1083
1084 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, text );
1085 m_view->Update( &selection() );
1086
1087 // update the cursor so it looks correct before another event
1088 setCursor();
1089 }
1090 }
1091
1092 if( placing )
1093 {
1094 text->ClearFlags();
1095 m_toolMgr->RunAction( ACTIONS::selectionClear );
1096
1097 commit.Add( text );
1098 commit.Push( _( "Draw Text" ) );
1099
1100 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, text );
1101
1102 text = nullptr;
1103 }
1104
1105 m_controls->ForceCursorPosition( false );
1106
1107 // If we started with a hotkey which has a position then warp back to that.
1108 // Otherwise update to the current mouse position pinned inside the autoscroll
1109 // boundaries.
1110 if( evt->IsPrime() && !ignorePrimePosition )
1111 {
1112 cursorPos = evt->Position();
1113 m_controls->WarpMouseCursor( cursorPos, true );
1114 }
1115 else
1116 {
1117 m_controls->PinCursorInsideNonAutoscrollArea( true );
1118 cursorPos = m_controls->GetMousePosition();
1119 }
1120
1122
1123 m_controls->ShowCursor( true );
1124 m_controls->CaptureCursor( text != nullptr );
1125 m_controls->SetAutoPan( text != nullptr );
1126 }
1127 else if( text && ( evt->IsMotion() || evt->IsAction( &PCB_ACTIONS::refreshPreview ) ) )
1128 {
1129 text->SetPosition( cursorPos );
1130 selection().SetReferencePoint( cursorPos );
1131 m_view->Update( &selection() );
1132 }
1133 else if( text
1135 || evt->IsAction( &ACTIONS::redo ) ) )
1136 {
1137 wxBell();
1138 }
1139 else if( text && evt->IsAction( &PCB_ACTIONS::properties ) )
1140 {
1141 frame()->OnEditItemRequest( text );
1142 m_view->Update( &selection() );
1143 frame()->SetMsgPanel( text );
1144 }
1145 else
1146 {
1147 evt->SetPassEvent();
1148 }
1149 }
1150
1151 m_controls->SetAutoPan( false );
1152 m_controls->CaptureCursor( false );
1153 m_controls->ForceCursorPosition( false );
1154 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1155
1156 if( selection().Empty() )
1157 m_frame->SetMsgPanel( board() );
1158
1159 return 0;
1160}
1161
1162
1164{
1165 if( m_inDrawingTool )
1166 return 0;
1167
1169
1170 PCB_TABLE* table = nullptr;
1171 const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
1172 BOARD_COMMIT commit( m_frame );
1173 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TABLE );
1174 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
1175
1176 // We might be running as the same shape in another co-routine. Make sure that one
1177 // gets whacked.
1178 m_toolMgr->DeactivateTool();
1179
1180 auto setCursor =
1181 [&]()
1182 {
1183 if( table )
1184 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
1185 else
1186 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1187 };
1188
1189 auto cleanup =
1190 [&] ()
1191 {
1192 m_toolMgr->RunAction( ACTIONS::selectionClear );
1193 m_controls->ForceCursorPosition( false );
1194 m_controls->ShowCursor( true );
1195 m_controls->SetAutoPan( false );
1196 m_controls->CaptureCursor( false );
1197 delete table;
1198 table = nullptr;
1199 };
1200
1201 m_toolMgr->RunAction( ACTIONS::selectionClear );
1202
1203 m_frame->PushTool( aEvent );
1204
1205 Activate();
1206 // Must be done after Activate() so that it gets set into the correct context
1207 getViewControls()->ShowCursor( true );
1208 m_controls->ForceCursorPosition( false );
1209 // Set initial cursor
1210 setCursor();
1211
1212 if( aEvent.HasPosition() )
1213 m_toolMgr->PrimeTool( aEvent.Position() );
1214
1215 // Main loop: keep receiving events
1216 while( TOOL_EVENT* evt = Wait() )
1217 {
1218 setCursor();
1219 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1220 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1221 VECTOR2I cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(),
1222 { m_frame->GetActiveLayer() }, GRID_TEXT ),
1224 m_controls->ForceCursorPosition( true, cursorPos );
1225
1226 if( evt->IsDrag() )
1227 {
1228 continue;
1229 }
1230 else if( evt->IsCancelInteractive() || ( table && evt->IsAction( &ACTIONS::undo ) ) )
1231 {
1232 if( table )
1233 {
1234 cleanup();
1235 }
1236 else
1237 {
1238 m_frame->PopTool( aEvent );
1239 break;
1240 }
1241 }
1242 else if( evt->IsActivate() )
1243 {
1244 if( table )
1245 cleanup();
1246
1247 if( evt->IsMoveTool() )
1248 {
1249 // leave ourselves on the stack so we come back after the move
1250 break;
1251 }
1252 else
1253 {
1254 m_frame->PopTool( aEvent );
1255 break;
1256 }
1257 }
1258 else if( evt->IsClick( BUT_RIGHT ) )
1259 {
1260 // Warp after context menu only if dragging...
1261 if( !table )
1262 m_toolMgr->VetoContextMenuMouseWarp();
1263
1264 m_menu->ShowContextMenu( selection() );
1265 }
1266 else if( evt->IsClick( BUT_LEFT ) )
1267 {
1268 if( !table )
1269 {
1270 m_toolMgr->RunAction( ACTIONS::selectionClear );
1271
1272 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
1273
1274 table = new PCB_TABLE( m_frame->GetModel(), bds.GetLineThickness( layer ) );
1275 table->SetFlags( IS_NEW );
1276 table->SetLayer( layer );
1277 table->SetColCount( 1 );
1278 table->AddCell( new PCB_TABLECELL( table ) );
1279
1280 table->SetLayer( layer );
1281 table->SetPosition( cursorPos );
1282
1283 if( !m_view->IsLayerVisible( layer ) )
1284 {
1285 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1286 m_frame->GetCanvas()->Refresh();
1287 }
1288
1290 m_view->Update( &selection() );
1291
1292 // update the cursor so it looks correct before another event
1293 setCursor();
1294 }
1295 else
1296 {
1297 m_toolMgr->RunAction( ACTIONS::selectionClear );
1298
1299 table->Normalize();
1300
1302 bool cancelled;
1303
1305 [&]()
1306 {
1307 // QuasiModal required for Scintilla auto-complete
1308 cancelled = dlg.ShowQuasiModal() != wxID_OK;
1309 } );
1310
1311 if( cancelled )
1312 {
1313 delete table;
1314 }
1315 else
1316 {
1317 commit.Add( table, m_frame->GetScreen() );
1318 commit.Push( _( "Draw Table" ) );
1319
1322 }
1323
1324 table = nullptr;
1325 }
1326 }
1327 else if( table && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
1328 {
1329 VECTOR2I fontSize = bds.GetTextSize( table->GetLayer() );
1330 VECTOR2I gridSize = grid.GetGridSize( grid.GetItemGrid( table ) );
1331 VECTOR2I origin( table->GetPosition() );
1332 VECTOR2I requestedSize( cursorPos - origin );
1333
1334 int colCount = std::max( 1, requestedSize.x / ( fontSize.x * 15 ) );
1335 int rowCount = std::max( 1, requestedSize.y / ( fontSize.y * 3 ) );
1336
1337 VECTOR2I cellSize( std::max( fontSize.x * 5, requestedSize.x / colCount ),
1338 std::max( fontSize.y * 3, requestedSize.y / rowCount ) );
1339
1340 cellSize.x = KiROUND( (double) cellSize.x / gridSize.x ) * gridSize.x;
1341 cellSize.y = KiROUND( (double) cellSize.y / gridSize.y ) * gridSize.y;
1342
1343 table->ClearCells();
1344 table->SetColCount( colCount );
1345
1346 for( int col = 0; col < colCount; ++col )
1347 table->SetColWidth( col, cellSize.x );
1348
1349 for( int row = 0; row < rowCount; ++row )
1350 {
1351 table->SetRowHeight( row, cellSize.y );
1352
1353 for( int col = 0; col < colCount; ++col )
1354 {
1355 PCB_TABLECELL* cell = new PCB_TABLECELL( table );
1356 cell->SetPosition( origin + VECTOR2I( col * cellSize.x, row * cellSize.y ) );
1357 cell->SetEnd( cell->GetPosition() + cellSize );
1358 table->AddCell( cell );
1359 }
1360 }
1361
1362 selection().SetReferencePoint( cursorPos );
1363 m_view->Update( &selection() );
1364 m_frame->SetMsgPanel( table );
1365 }
1366 else if( table && evt->IsAction( &PCB_ACTIONS::properties ) )
1367 {
1368 frame()->OnEditItemRequest( table );
1369 m_view->Update( &selection() );
1370 frame()->SetMsgPanel( table );
1371 }
1372 else if( table && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
1373 || evt->IsAction( &ACTIONS::redo ) ) )
1374 {
1375 wxBell();
1376 }
1377 else
1378 {
1379 evt->SetPassEvent();
1380 }
1381
1382 // Enable autopanning and cursor capture only when there is a shape being drawn
1383 getViewControls()->SetAutoPan( table != nullptr );
1384 getViewControls()->CaptureCursor( table != nullptr );
1385 }
1386
1387 getViewControls()->SetAutoPan( false );
1388 getViewControls()->CaptureCursor( false );
1389 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1390 return 0;
1391}
1392
1393
1395{
1396 const VECTOR2I lineVector{ aDim->GetEnd() - aDim->GetStart() };
1397
1398 aDim->SetEnd( aDim->GetStart() + GetVectorSnapped45( lineVector ) );
1399 aDim->Update();
1400}
1401
1403{
1404 if( m_inDrawingTool )
1405 return 0;
1406
1408
1409 PCB_BARCODE* barcode = nullptr;
1410 BOARD_COMMIT commit( m_frame );
1411 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::BARCODE );
1412 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
1413 const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
1414
1415 auto setCursor =
1416 [&]()
1417 {
1418 if( barcode )
1419 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
1420 else
1421 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1422 };
1423
1424 auto cleanup =
1425 [&]()
1426 {
1427 m_toolMgr->RunAction( ACTIONS::selectionClear );
1428 m_controls->ForceCursorPosition( false );
1429 m_controls->ShowCursor( true );
1430 m_controls->SetAutoPan( false );
1431 m_controls->CaptureCursor( false );
1432 delete barcode;
1433 barcode = nullptr;
1434 };
1435
1436 m_toolMgr->RunAction( ACTIONS::selectionClear );
1437
1438 m_frame->PushTool( aEvent );
1439
1440 Activate();
1441 // Must be done after Activate() so that it gets set into the correct context
1442 getViewControls()->ShowCursor( true );
1443 m_controls->ForceCursorPosition( false );
1444 setCursor();
1445
1446 if( aEvent.HasPosition() )
1447 m_toolMgr->PrimeTool( aEvent.Position() );
1448
1449 while( TOOL_EVENT* evt = Wait() )
1450 {
1451 setCursor();
1452
1453 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1454 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1455 VECTOR2I cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(),
1456 { m_frame->GetActiveLayer() }, GRID_TEXT ),
1458 m_controls->ForceCursorPosition( true, cursorPos );
1459
1460 if( evt->IsDrag() )
1461 {
1462 continue;
1463 }
1464 else if( evt->IsCancelInteractive() || ( barcode && evt->IsAction( &ACTIONS::undo ) ) )
1465 {
1466 if( barcode )
1467 {
1468 cleanup();
1469 }
1470 else
1471 {
1472 m_frame->PopTool( aEvent );
1473 break;
1474 }
1475 }
1476 else if( evt->IsActivate() )
1477 {
1478 if( barcode )
1479 cleanup();
1480
1481 if( evt->IsMoveTool() )
1482 {
1483 // leave ourselves on the stack so we come back after the move
1484 break;
1485 }
1486 else
1487 {
1488 m_frame->PopTool( aEvent );
1489 break;
1490 }
1491 }
1492 else if( evt->IsClick( BUT_RIGHT ) )
1493 {
1494 if( !barcode )
1495 m_toolMgr->VetoContextMenuMouseWarp();
1496
1497 m_menu->ShowContextMenu( selection() );
1498 }
1499 else if( evt->IsClick( BUT_LEFT ) )
1500 {
1501 m_toolMgr->RunAction( ACTIONS::selectionClear );
1502
1503 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
1504
1505 barcode = new PCB_BARCODE( m_frame->GetModel() );
1506 barcode->SetFlags( IS_NEW );
1507 barcode->SetLayer( layer );
1508 barcode->SetPosition( cursorPos );
1509 barcode->SetTextSize( bds.GetTextSize( layer ).y );
1510
1511 DIALOG_BARCODE_PROPERTIES dlg( m_frame, barcode );
1512 bool cancelled;
1513
1515 [&]()
1516 {
1517 cancelled = dlg.ShowModal() != wxID_OK;
1518 } );
1519
1520 if( cancelled )
1521 {
1522 delete barcode;
1523 }
1524 else
1525 {
1526 if( !m_view->IsLayerVisible( layer ) )
1527 {
1528 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1529 m_frame->GetCanvas()->Refresh();
1530 }
1531
1532 commit.Add( barcode );
1533 commit.Push( _( "Draw Barcode" ) );
1534
1535 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, barcode );
1536 m_view->Update( &selection() );
1537 }
1538
1539 barcode = nullptr;
1540 }
1541 else
1542 {
1543 evt->SetPassEvent();
1544 }
1545
1546 getViewControls()->SetAutoPan( false );
1547 getViewControls()->CaptureCursor( false );
1548 }
1549
1550 getViewControls()->SetAutoPan( false );
1551 getViewControls()->CaptureCursor( false );
1552 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1553 return 0;
1554}
1555
1556
1558{
1559 if( m_isFootprintEditor && !m_frame->GetModel() )
1560 return 0;
1561
1562 if( m_inDrawingTool )
1563 return 0;
1564
1566
1567 enum DIMENSION_STEPS
1568 {
1569 SET_ORIGIN = 0,
1570 SET_END,
1571 SET_HEIGHT,
1572 FINISHED
1573 };
1574
1575 TOOL_EVENT originalEvent = aEvent;
1576 PCB_DIMENSION_BASE* dimension = nullptr;
1577 BOARD_COMMIT commit( m_frame );
1578 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
1579 BOARD_DESIGN_SETTINGS& boardSettings = m_board->GetDesignSettings();
1580 PCB_SELECTION preview; // A VIEW_GROUP that serves as a preview for the new item(s)
1581 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DIMENSION );
1582 int step = SET_ORIGIN;
1584
1585 m_view->Add( &preview );
1586
1587 auto cleanup =
1588 [&]()
1589 {
1590 m_controls->SetAutoPan( false );
1591 m_controls->CaptureCursor( false );
1592 m_controls->ForceCursorPosition( false );
1593
1594 preview.Clear();
1595 m_view->Update( &preview );
1596
1597 // Snap guides persist in the grid helper until the tool exits, so abandoning the
1598 // dimension mid-draw must clear them or they linger on screen.
1599 grid.FullReset();
1600
1601 delete dimension;
1602 dimension = nullptr;
1603 step = SET_ORIGIN;
1604 };
1605
1606 auto setCursor =
1607 [&]()
1608 {
1609 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
1610 };
1611
1612 m_toolMgr->RunAction( ACTIONS::selectionClear );
1613
1614 m_frame->PushTool( aEvent );
1615
1616 Activate();
1617 // Must be done after Activate() so that it gets set into the correct context
1618 m_controls->ShowCursor( true );
1619 m_controls->ForceCursorPosition( false );
1620 // Set initial cursor
1621 setCursor();
1622
1623 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1624
1625 if( aEvent.HasPosition() )
1626 m_toolMgr->PrimeTool( aEvent.Position() );
1627
1628 // Main loop: keep receiving events
1629 while( TOOL_EVENT* evt = Wait() )
1630 {
1631 if( step > SET_ORIGIN )
1632 frame()->SetMsgPanel( dimension );
1633
1634 setCursor();
1635
1636 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1637 auto angleSnap = GetAngleSnapMode();
1638 if( evt->Modifier( MD_CTRL ) )
1639 angleSnap = LEADER_MODE::DIRECT;
1640 bool constrained = angleSnap != LEADER_MODE::DIRECT;
1641 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1642
1643 if( step == SET_HEIGHT && t != PCB_DIM_ORTHOGONAL_T )
1644 {
1645 if( dimension->GetStart().x != dimension->GetEnd().x
1646 && dimension->GetStart().y != dimension->GetEnd().y )
1647 {
1648 // Not cardinal. Grid snapping doesn't make sense for height.
1649 grid.SetUseGrid( false );
1650 }
1651 }
1652
1653 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
1654 cursorPos = GetClampedCoords( grid.BestSnapAnchor( cursorPos, nullptr, GRID_GRAPHICS ),
1656
1657 m_controls->ForceCursorPosition( true, cursorPos );
1658
1659 if( evt->IsCancelInteractive() || ( dimension && evt->IsAction( &ACTIONS::undo ) ) )
1660 {
1661 m_controls->SetAutoPan( false );
1662
1663 if( step != SET_ORIGIN ) // start from the beginning
1664 {
1665 cleanup();
1666 }
1667 else
1668 {
1669 m_frame->PopTool( aEvent );
1670 break;
1671 }
1672 }
1673 else if( evt->IsActivate() )
1674 {
1675 if( step != SET_ORIGIN )
1676 cleanup();
1677
1678 if( evt->IsPointEditor() )
1679 {
1680 // don't exit (the point editor runs in the background)
1681 }
1682 else if( evt->IsMoveTool() )
1683 {
1684 // leave ourselves on the stack so we come back after the move
1685 break;
1686 }
1687 else
1688 {
1689 m_frame->PopTool( aEvent );
1690 break;
1691 }
1692 }
1693 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) && step != SET_ORIGIN )
1694 {
1695 m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
1696 dimension->SetLineThickness( m_stroke.GetWidth() );
1697 m_view->Update( &preview );
1698 frame()->SetMsgPanel( dimension );
1699 }
1700 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && step != SET_ORIGIN )
1701 {
1702 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
1703 {
1704 m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
1705 dimension->SetLineThickness( m_stroke.GetWidth() );
1706 m_view->Update( &preview );
1707 frame()->SetMsgPanel( dimension );
1708 }
1709 }
1710 else if( evt->IsClick( BUT_RIGHT ) )
1711 {
1712 if( !dimension )
1713 m_toolMgr->VetoContextMenuMouseWarp();
1714
1715 m_menu->ShowContextMenu( selection() );
1716 }
1717 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1718 {
1719 switch( step )
1720 {
1721 case SET_ORIGIN:
1722 {
1723 m_toolMgr->RunAction( ACTIONS::selectionClear );
1724
1725 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
1726
1727 // Init the new item attributes
1728 auto setMeasurementAttributes =
1729 [&]( PCB_DIMENSION_BASE* aDim )
1730 {
1731 aDim->SetUnitsMode( boardSettings.m_DimensionUnitsMode );
1732 aDim->SetUnitsFormat( boardSettings.m_DimensionUnitsFormat );
1733 aDim->SetPrecision( boardSettings.m_DimensionPrecision );
1734 aDim->SetSuppressZeroes( boardSettings.m_DimensionSuppressZeroes );
1735 aDim->SetTextPositionMode( boardSettings.m_DimensionTextPosition );
1736 aDim->SetKeepTextAligned( boardSettings.m_DimensionKeepTextAligned );
1737 };
1738
1739 if( originalEvent.IsAction( &PCB_ACTIONS::drawAlignedDimension ) )
1740 {
1741 dimension = new PCB_DIM_ALIGNED( m_frame->GetModel() );
1742 setMeasurementAttributes( dimension );
1743 }
1744 else if( originalEvent.IsAction( &PCB_ACTIONS::drawOrthogonalDimension ) )
1745 {
1746 dimension = new PCB_DIM_ORTHOGONAL( m_frame->GetModel() );
1747 setMeasurementAttributes( dimension );
1748 }
1749 else if( originalEvent.IsAction( &PCB_ACTIONS::drawCenterDimension ) )
1750 {
1751 dimension = new PCB_DIM_CENTER( m_frame->GetModel() );
1752 }
1753 else if( originalEvent.IsAction( &PCB_ACTIONS::drawRadialDimension ) )
1754 {
1755 dimension = new PCB_DIM_RADIAL( m_frame->GetModel() );
1756 setMeasurementAttributes( dimension );
1757 }
1758 else if( originalEvent.IsAction( &PCB_ACTIONS::drawLeader ) )
1759 {
1760 dimension = new PCB_DIM_LEADER( m_frame->GetModel() );
1761 dimension->SetTextPos( cursorPos );
1762 }
1763 else
1764 {
1765 wxFAIL_MSG( wxT( "Unhandled action in DRAWING_TOOL::DrawDimension" ) );
1766 }
1767
1768 t = dimension->Type();
1769
1770 dimension->SetLayer( layer );
1771 dimension->SetMirrored( m_board->IsBackLayer( layer ) );
1772 dimension->SetTextSize( boardSettings.GetTextSize( layer ) );
1773 dimension->SetTextThickness( boardSettings.GetTextThickness( layer ) );
1774 dimension->SetItalic( boardSettings.GetTextItalic( layer ) );
1775 dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
1776 dimension->SetArrowLength( boardSettings.m_DimensionArrowLength );
1777 dimension->SetExtensionOffset( boardSettings.m_DimensionExtensionOffset );
1778 dimension->SetStart( cursorPos );
1779 dimension->SetEnd( cursorPos );
1780 dimension->Update();
1781
1782 if( !m_view->IsLayerVisible( layer ) )
1783 {
1784 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1785 m_frame->GetCanvas()->Refresh();
1786 }
1787
1788 preview.Add( dimension );
1789 frame()->SetMsgPanel( dimension );
1790
1791 m_controls->SetAutoPan( true );
1792 m_controls->CaptureCursor( true );
1793 break;
1794 }
1795
1796 case SET_END:
1797 // Dimensions that have origin and end in the same spot are not valid
1798 if( dimension->GetStart() == dimension->GetEnd() )
1799 {
1800 --step;
1801 break;
1802 }
1803
1804 if( t != PCB_DIM_CENTER_T && t != PCB_DIM_RADIAL_T && t != PCB_DIM_LEADER_T )
1805 {
1806 break;
1807 }
1808
1809 ++step;
1811 case SET_HEIGHT:
1812 assert( dimension->GetStart() != dimension->GetEnd() );
1813 assert( dimension->GetLineThickness() > 0 );
1814
1815 preview.Remove( dimension );
1816
1817 commit.Add( dimension );
1818 commit.Push( _( "Draw Dimension" ) );
1819
1820 // Run the edit immediately to set the leader text
1821 if( t == PCB_DIM_LEADER_T )
1822 frame()->OnEditItemRequest( dimension );
1823
1824 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, dimension );
1825
1826 break;
1827 }
1828
1829 if( ++step >= FINISHED )
1830 {
1831 dimension = nullptr;
1832 step = SET_ORIGIN;
1833 m_controls->SetAutoPan( false );
1834 m_controls->CaptureCursor( false );
1835 }
1836 else if( evt->IsDblClick( BUT_LEFT ) )
1837 {
1838 m_toolMgr->PostAction( PCB_ACTIONS::cursorClick );
1839 }
1840 }
1841 else if( evt->IsMotion() )
1842 {
1843 switch( step )
1844 {
1845 case SET_END:
1846 dimension->SetEnd( cursorPos );
1847
1848 if( constrained || t == PCB_DIM_CENTER_T )
1849 constrainDimension( dimension );
1850
1851 if( t == PCB_DIM_ORTHOGONAL_T )
1852 {
1853 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
1854
1855 BOX2I bounds( dimension->GetStart(),
1856 dimension->GetEnd() - dimension->GetStart() );
1857
1858 // Create a nice preview by measuring the longer dimension
1859 bool vert = bounds.GetWidth() < bounds.GetHeight();
1860
1861 ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
1863 }
1864 else if( t == PCB_DIM_RADIAL_T )
1865 {
1866 PCB_DIM_RADIAL* radialDim = static_cast<PCB_DIM_RADIAL*>( dimension );
1867 VECTOR2I textOffset( radialDim->GetArrowLength() * 10, 0 );
1868
1869 if( radialDim->GetEnd().x < radialDim->GetStart().x )
1870 textOffset = -textOffset;
1871
1872 radialDim->SetTextPos( radialDim->GetKnee() + textOffset );
1873 }
1874 else if( t == PCB_DIM_LEADER_T )
1875 {
1876 VECTOR2I textOffset( dimension->GetArrowLength() * 10, 0 );
1877
1878 if( dimension->GetEnd().x < dimension->GetStart().x )
1879 textOffset = -textOffset;
1880
1881 dimension->SetTextPos( dimension->GetEnd() + textOffset );
1882 }
1883
1884 dimension->Update();
1885 break;
1886
1887 case SET_HEIGHT:
1888 if( t == PCB_DIM_ALIGNED_T )
1889 {
1890 PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dimension );
1891
1892 // Calculating the direction of travel perpendicular to the selected axis
1893 double angle = aligned->GetAngle() + ( M_PI / 2 );
1894
1895 VECTOR2I delta( (VECTOR2I) cursorPos - dimension->GetEnd() );
1896 double height = ( delta.x * cos( angle ) ) + ( delta.y * sin( angle ) );
1897 aligned->SetHeight( height );
1898 aligned->Update();
1899 }
1900 else if( t == PCB_DIM_ORTHOGONAL_T )
1901 {
1902 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
1903
1904 BOX2I bbox( dimension->GetStart(),
1905 dimension->GetEnd() - dimension->GetStart() );
1906 VECTOR2I direction( cursorPos - bbox.Centre() );
1907 bool vert;
1908
1909 // Only change the orientation when we move outside the bbox
1910 if( !bbox.Contains( cursorPos ) )
1911 {
1912 // If the dimension is horizontal or vertical, set correct orientation
1913 // otherwise, test if we're left/right of the bounding box or above/below it
1914 if( bbox.GetWidth() == 0 )
1915 vert = true;
1916 else if( bbox.GetHeight() == 0 )
1917 vert = false;
1918 else if( cursorPos.x > bbox.GetLeft() && cursorPos.x < bbox.GetRight() )
1919 vert = false;
1920 else if( cursorPos.y > bbox.GetTop() && cursorPos.y < bbox.GetBottom() )
1921 vert = true;
1922 else
1923 vert = std::abs( direction.y ) < std::abs( direction.x );
1924
1925 ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
1927 }
1928 else
1929 {
1930 vert = ortho->GetOrientation() == PCB_DIM_ORTHOGONAL::DIR::VERTICAL;
1931 }
1932
1933 VECTOR2I heightVector( cursorPos - dimension->GetStart() );
1934 ortho->SetHeight( vert ? heightVector.x : heightVector.y );
1935 ortho->Update();
1936 }
1937
1938 break;
1939 }
1940
1941 // Show a preview of the item
1942 m_view->Update( &preview );
1943 }
1944 else if( dimension && evt->IsAction( &PCB_ACTIONS::layerChanged ) )
1945 {
1946 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
1947
1948 if( !m_view->IsLayerVisible( layer ) )
1949 {
1950 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1951 m_frame->GetCanvas()->Refresh();
1952 }
1953
1954 dimension->SetLayer( layer );
1955 dimension->SetTextSize( boardSettings.GetTextSize( layer ) );
1956 dimension->SetTextThickness( boardSettings.GetTextThickness( layer ) );
1957 dimension->SetItalic( boardSettings.GetTextItalic( layer ) );
1958 dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
1959 dimension->Update();
1960
1961 m_view->Update( &preview );
1962 frame()->SetMsgPanel( dimension );
1963 }
1964 else if( dimension && evt->IsAction( &PCB_ACTIONS::properties ) )
1965 {
1966 if( step == SET_END || step == SET_HEIGHT )
1967 {
1968 frame()->OnEditItemRequest( dimension );
1969 dimension->Update();
1970 frame()->SetMsgPanel( dimension );
1971 break;
1972 }
1973 else
1974 {
1975 wxBell();
1976 }
1977 }
1978 else if( dimension && evt->IsAction( &PCB_ACTIONS::changeDimensionArrows ) )
1979 {
1980 switch( dimension->Type() )
1981 {
1982 case PCB_DIM_ALIGNED_T:
1984 case PCB_DIM_RADIAL_T:
1987 else
1989 break;
1990 default:
1991 // Other dimension types don't have arrows that can swap
1992 wxBell();
1993 }
1994
1995 m_view->Update( &preview );
1996 }
1997 else if( dimension && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
1998 || evt->IsAction( &ACTIONS::redo ) ) )
1999 {
2000 wxBell();
2001 }
2002 else
2003 {
2004 evt->SetPassEvent();
2005 }
2006 }
2007
2008 if( step != SET_ORIGIN )
2009 delete dimension;
2010
2011 m_controls->SetAutoPan( false );
2012 m_controls->ForceCursorPosition( false );
2013 m_controls->CaptureCursor( false );
2014 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2015
2016 m_view->Remove( &preview );
2017
2018 if( selection().Empty() )
2019 m_frame->SetMsgPanel( board() );
2020
2021 return 0;
2022}
2023
2024
2026{
2027 if( !m_frame->GetModel() )
2028 return 0;
2029
2030 if( m_inDrawingTool )
2031 return 0;
2032
2034
2036
2037 // Set filename on drag-and-drop
2038 if( aEvent.HasParameter() )
2039 dlg.SetFilenameOverride( *aEvent.Parameter<wxString*>() );
2040
2041 int dlgResult = dlg.ShowModal();
2042
2043 std::list<std::unique_ptr<EDA_ITEM>>& list = dlg.GetImportedItems();
2044
2045 if( dlgResult != wxID_OK )
2046 return 0;
2047
2048 // Ensure the list is not empty:
2049 if( list.empty() )
2050 {
2051 wxMessageBox( _( "No graphic items found in file.") );
2052 return 0;
2053 }
2054
2056
2057 std::vector<BOARD_ITEM*> newItems; // all new items, including group
2058 std::vector<BOARD_ITEM*> selectedItems; // the group, or newItems if no group
2059 PCB_SELECTION preview;
2060 BOARD_COMMIT commit( m_frame );
2061 PCB_GROUP* group = nullptr;
2062 PCB_LAYER_ID layer = F_Cu;
2063
2064 if( dlg.ShouldGroupItems() )
2065 {
2066 size_t boardItemCount = std::count_if( list.begin(), list.end(),
2067 []( const std::unique_ptr<EDA_ITEM>& ptr )
2068 {
2069 return ptr->IsBOARD_ITEM();
2070 } );
2071
2072 if( boardItemCount >= 2 )
2073 {
2074 group = new PCB_GROUP( m_frame->GetModel() );
2075
2076 newItems.push_back( group );
2077 selectedItems.push_back( group );
2078 preview.Add( group );
2079 }
2080 }
2081
2082 if( dlg.ShouldFixDiscontinuities() )
2083 {
2084 std::vector<PCB_SHAPE*> shapeList;
2085
2086 for( const std::unique_ptr<EDA_ITEM>& ptr : list )
2087 {
2088 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( ptr.get() ) )
2089 shapeList.push_back( shape );
2090 }
2091
2092 ConnectBoardShapes( shapeList, dlg.GetTolerance() );
2093 }
2094
2095 for( std::unique_ptr<EDA_ITEM>& ptr : list )
2096 {
2097 EDA_ITEM* eda_item = ptr.release();
2098
2099 if( eda_item->IsBOARD_ITEM() )
2100 {
2101 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( eda_item );
2102
2103 newItems.push_back( item );
2104
2105 if( group )
2106 group->AddItem( item );
2107 else
2108 selectedItems.push_back( item );
2109
2110 layer = item->GetLayer();
2111 }
2112
2113 preview.Add( eda_item );
2114 }
2115
2116 // Clear the current selection then select the drawings so that edit tools work on them
2117 m_toolMgr->RunAction( ACTIONS::selectionClear );
2118
2119 EDA_ITEMS selItems( selectedItems.begin(), selectedItems.end() );
2120 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &selItems );
2121
2122 if( !dlg.IsPlacementInteractive() )
2123 {
2124 for( BOARD_ITEM* item : newItems )
2125 commit.Add( item );
2126
2127 commit.Push( _( "Import Graphics" ) );
2128
2129 return 0;
2130 }
2131
2132 // Turn shapes on if they are off, so that the created object will be visible after completion
2133 m_frame->SetObjectVisible( LAYER_FILLED_SHAPES );
2134
2135 if( !m_view->IsLayerVisible( layer ) )
2136 {
2137 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
2138 m_frame->GetCanvas()->Refresh();
2139 }
2140
2141 m_view->Add( &preview );
2142
2143 m_frame->PushTool( aEvent );
2144
2145 auto setCursor =
2146 [&]()
2147 {
2148 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
2149 };
2150
2151 Activate();
2152 // Must be done after Activate() so that it gets set into the correct context
2153 m_controls->ShowCursor( true );
2154 m_controls->ForceCursorPosition( false );
2155 // Set initial cursor
2156 setCursor();
2157
2158 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );
2159 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
2160
2161 // Now move the new items to the current cursor position:
2162 VECTOR2I cursorPos = m_controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
2163 VECTOR2I delta = cursorPos - static_cast<BOARD_ITEM*>( preview.GetTopLeftItem() )->GetPosition();
2164
2165 for( BOARD_ITEM* item : selectedItems )
2166 item->Move( delta );
2167
2168 m_view->Update( &preview );
2169
2170 // Main loop: keep receiving events
2171 while( TOOL_EVENT* evt = Wait() )
2172 {
2173 setCursor();
2174
2175 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2176 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2177 cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(), { layer }, GRID_GRAPHICS ),
2179 m_controls->ForceCursorPosition( true, cursorPos );
2180
2181 if( evt->IsCancelInteractive() || evt->IsActivate() )
2182 {
2183 m_toolMgr->RunAction( ACTIONS::selectionClear );
2184
2185 if( group )
2186 preview.Remove( group );
2187
2188 for( BOARD_ITEM* item : newItems )
2189 delete item;
2190
2191 break;
2192 }
2193 else if( evt->IsMotion() )
2194 {
2195 delta = cursorPos - static_cast<BOARD_ITEM*>( preview.GetTopLeftItem() )->GetPosition();
2196
2197 for( BOARD_ITEM* item : selectedItems )
2198 item->Move( delta );
2199
2200 m_view->Update( &preview );
2201 }
2202 else if( evt->IsClick( BUT_RIGHT ) )
2203 {
2204 m_menu->ShowContextMenu( selection() );
2205 }
2206 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
2207 {
2208 // Place the imported drawings
2209 for( BOARD_ITEM* item : newItems )
2210 commit.Add( item );
2211
2212 commit.Push( _( "Import Graphics" ) );
2213
2214 break; // This is a one-shot command, not a tool
2215 }
2216 else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
2217 {
2218 wxBell();
2219 }
2220 else
2221 {
2222 evt->SetPassEvent();
2223 }
2224 }
2225
2226 preview.Clear();
2227 m_view->Remove( &preview );
2228
2229 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2230 m_controls->ForceCursorPosition( false );
2231
2232 m_frame->PopTool( aEvent );
2233
2234 return 0;
2235}
2236
2237
2239{
2240 // Make sense only in FP editor
2241 if( !m_isFootprintEditor )
2242 return 0;
2243
2244 if( !m_frame->GetModel() )
2245 return 0;
2246
2247 if( m_inDrawingTool )
2248 return 0;
2249
2251
2252 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ANCHOR );
2253 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
2254
2255 m_toolMgr->RunAction( ACTIONS::selectionClear );
2256
2257 m_frame->PushTool( aEvent );
2258
2259 auto setCursor =
2260 [&]()
2261 {
2262 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
2263 };
2264
2265 Activate();
2266 // Must be done after Activate() so that it gets set into the correct context
2267 m_controls->ShowCursor( true );
2268 m_controls->SetAutoPan( true );
2269 m_controls->CaptureCursor( false );
2270 m_controls->ForceCursorPosition( false );
2271 // Set initial cursor
2272 setCursor();
2273
2274 while( TOOL_EVENT* evt = Wait() )
2275 {
2276 setCursor();
2277
2278 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2279 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2280 VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), LSET::AllLayersMask() );
2281 m_controls->ForceCursorPosition( true, cursorPos );
2282
2283 if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
2284 {
2285 FOOTPRINT* footprint = (FOOTPRINT*) m_frame->GetModel();
2286 BOARD_COMMIT commit( m_frame );
2287 commit.Modify( footprint );
2288
2289 // set the new relative internal local coordinates of footprint items
2290 VECTOR2I moveVector = footprint->GetPosition() - cursorPos;
2291 footprint->MoveAnchorPosition( moveVector );
2292
2293 commit.Push( _( "Move Footprint Anchor" ) );
2294
2295 // Usually, we do not need to change twice the anchor position,
2296 // so deselect the active tool
2297 m_frame->PopTool( aEvent );
2298 break;
2299 }
2300 else if( evt->IsClick( BUT_RIGHT ) )
2301 {
2302 m_menu->ShowContextMenu( selection() );
2303 }
2304 else if( evt->IsCancelInteractive() || evt->IsActivate() )
2305 {
2306 m_frame->PopTool( aEvent );
2307 break;
2308 }
2309 else
2310 {
2311 evt->SetPassEvent();
2312 }
2313 }
2314
2315 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2316 m_controls->ForceCursorPosition( false );
2317
2318 return 0;
2319}
2320
2321
2322static VECTOR2I evalEllipsePoint( const PCB_SHAPE* aGraphic, const VECTOR2I& aCursorPos )
2323{
2324 const VECTOR2I center = aGraphic->GetEllipseCenter();
2325 const double a = std::max( 1, aGraphic->GetEllipseMajorRadius() );
2326 const double b = std::max( 1, aGraphic->GetEllipseMinorRadius() );
2327 const EDA_ANGLE rot = aGraphic->GetEllipseRotation();
2328 const double cosRot = rot.Cos();
2329 const double sinRot = rot.Sin();
2330
2331 const double dx = aCursorPos.x - center.x;
2332 const double dy = aCursorPos.y - center.y;
2333 const double lx = dx * cosRot + dy * sinRot;
2334 const double ly = -dx * sinRot + dy * cosRot;
2335
2336 const EDA_ANGLE t( std::atan2( ly / b, lx / a ), RADIANS_T );
2337 const double px = a * t.Cos();
2338 const double py = b * t.Sin();
2339
2340 return center + VECTOR2I( KiROUND( px * cosRot - py * sinRot ), KiROUND( px * sinRot + py * cosRot ) );
2341}
2342
2343
2345{
2346 if( aMgr.IsReset() )
2347 return;
2348
2349 if( aGraphic->GetShape() == SHAPE_T::ELLIPSE || aGraphic->GetShape() == SHAPE_T::ELLIPSE_ARC )
2350 {
2351 const VECTOR2I origin = aMgr.GetOrigin();
2352 const VECTOR2I end = aMgr.GetEnd();
2353 const VECTOR2I center = ( origin + end ) / 2;
2354 const int halfW = std::abs( end.x - origin.x ) / 2;
2355 const int halfH = std::abs( end.y - origin.y ) / 2;
2356
2357 int majorRadius;
2358 int minorRadius;
2359 EDA_ANGLE rotation;
2360
2361 if( halfW >= halfH )
2362 {
2363 majorRadius = std::max( halfW, 1 );
2364 minorRadius = std::max( halfH, 1 );
2365 rotation = ANGLE_0;
2366 }
2367 else
2368 {
2369 majorRadius = std::max( halfH, 1 );
2370 minorRadius = std::max( halfW, 1 );
2371 rotation = ANGLE_90;
2372 }
2373
2374 aGraphic->SetStart( origin );
2375 aGraphic->SetEllipseCenter( center );
2376 aGraphic->SetEllipseMajorRadius( majorRadius );
2377 aGraphic->SetEllipseMinorRadius( minorRadius );
2378 aGraphic->SetEllipseRotation( rotation );
2379
2380 if( aGraphic->GetShape() == SHAPE_T::ELLIPSE_ARC )
2381 {
2382 aGraphic->SetEllipseStartAngle( ANGLE_0 );
2383 aGraphic->SetEllipseEndAngle( ANGLE_360 );
2384 }
2385 }
2386 else
2387 {
2388 aGraphic->SetStart( aMgr.GetOrigin() );
2389 aGraphic->SetEnd( aMgr.GetEnd() );
2390 }
2391}
2392
2393
2394bool DRAWING_TOOL::drawShape( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
2395 std::optional<VECTOR2D> aStartingPoint,
2396 std::stack<PCB_SHAPE*>* aCommittedGraphics )
2397{
2398 SHAPE_T shape = ( *aGraphic )->GetShape();
2399
2400 wxASSERT( shape == SHAPE_T::SEGMENT || shape == SHAPE_T::CIRCLE || shape == SHAPE_T::RECTANGLE
2401 || shape == SHAPE_T::ELLIPSE || shape == SHAPE_T::ELLIPSE_ARC );
2402
2403 const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
2404 EDA_UNITS userUnits = m_frame->GetUserUnits();
2405 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
2406 PCB_SHAPE*& graphic = *aGraphic;
2407
2408 if( m_layer != m_frame->GetActiveLayer() )
2409 {
2410 m_layer = m_frame->GetActiveLayer();
2411 m_stroke.SetWidth( bds.GetLineThickness( m_layer ) );
2412 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2413 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2414
2415 m_textAttrs.m_Size = bds.GetTextSize( m_layer );
2416 m_textAttrs.m_StrokeWidth = bds.GetTextThickness( m_layer );
2418 m_textAttrs.m_Italic = bds.GetTextItalic( m_layer );
2419 m_textAttrs.m_KeepUpright = bds.GetTextUpright( m_layer );
2420 m_textAttrs.m_Mirrored = m_board->IsBackLayer( m_layer );
2423 }
2424
2425 // Turn shapes on if they are off, so that the created object will be visible after completion
2426 m_frame->SetObjectVisible( LAYER_FILLED_SHAPES );
2427
2428 // geometric construction manager
2430
2431 // drawing assistant overlay
2432 // TODO: workaround because EDA_SHAPE_TYPE_T is not visible from commons.
2433 KIGFX::PREVIEW::GEOM_SHAPE geomShape = ( shape == SHAPE_T::ELLIPSE || shape == SHAPE_T::ELLIPSE_ARC )
2435 : static_cast<KIGFX::PREVIEW::GEOM_SHAPE>( shape );
2436 KIGFX::PREVIEW::TWO_POINT_ASSISTANT twoPointAsst( twoPointMgr, pcbIUScale, userUnits, geomShape );
2437
2438 // Add a VIEW_GROUP that serves as a preview for the new item
2439 m_preview.Clear();
2440 m_view->Add( &m_preview );
2441 m_view->Add( &twoPointAsst );
2442
2443 bool started = false;
2444 bool cancelled = false;
2445 bool multiPhase = false;
2446 PCB_SHAPE* marker = nullptr;
2447 bool isLocalOriginSet = ( m_frame->GetScreen()->m_LocalOrigin != VECTOR2D( 0, 0 ) );
2448 VECTOR2I cursorPos = m_controls->GetMousePosition();
2449
2450 auto setCursor =
2451 [&]()
2452 {
2453 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2454 };
2455
2456 auto cleanup =
2457 [&]()
2458 {
2459 m_preview.Clear();
2460 m_view->Update( &m_preview );
2461 delete graphic;
2462 graphic = nullptr;
2463
2464 delete marker;
2465 marker = nullptr;
2466
2467 if( !isLocalOriginSet )
2468 m_frame->GetScreen()->m_LocalOrigin = VECTOR2D( 0, 0 );
2469 };
2470
2471 m_controls->ShowCursor( true );
2472 m_controls->ForceCursorPosition( false );
2473 // Set initial cursor
2474 setCursor();
2475
2476 m_toolMgr->PostAction( ACTIONS::refreshPreview );
2477
2478 if( aStartingPoint )
2479 m_toolMgr->PrimeTool( *aStartingPoint );
2480
2481 // Main loop: keep receiving events
2482 while( TOOL_EVENT* evt = Wait() )
2483 {
2484 setCursor();
2485
2486 if( started )
2487 m_frame->SetMsgPanel( graphic );
2488
2489 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2490 auto angleSnap = GetAngleSnapMode();
2491
2492 // Drawing rectangles and circles ignore the snap behavior by default, but constrains
2493 // when the modifier key is pressed
2494 if( shape == SHAPE_T::RECTANGLE || shape == SHAPE_T::CIRCLE || shape == SHAPE_T::ELLIPSE
2495 || shape == SHAPE_T::ELLIPSE_ARC )
2496 {
2497 if( evt->Modifier( MD_CTRL ) )
2498 angleSnap = LEADER_MODE::DEG45;
2499 else
2500 angleSnap = LEADER_MODE::DIRECT;
2501 }
2502 else {
2503 // All other drawing uses the snap mode, except that is disabled with the modifier key
2504 if( evt->Modifier( MD_CTRL ) )
2505 angleSnap = LEADER_MODE::DIRECT;
2506 }
2507
2508 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2509 cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(), { m_layer }, GRID_GRAPHICS ),
2511 m_controls->ForceCursorPosition( true, cursorPos );
2512
2513 if( evt->IsCancelInteractive() || ( started && evt->IsAction( &ACTIONS::undo ) ) )
2514 {
2515 cleanup();
2516
2517 if( !started )
2518 {
2519 // We've handled the cancel event. Don't cancel other tools
2520 evt->SetPassEvent( false );
2521 m_frame->PopTool( aTool );
2522 cancelled = true;
2523 }
2524
2525 break;
2526 }
2527 else if( evt->IsActivate() )
2528 {
2529 if( evt->IsPointEditor() )
2530 {
2531 // don't exit (the point editor runs in the background)
2532 }
2533 else if( evt->IsMoveTool() )
2534 {
2535 cleanup();
2536 // leave ourselves on the stack so we come back after the move
2537 cancelled = true;
2538 break;
2539 }
2540 else
2541 {
2542 cleanup();
2543 m_frame->PopTool( aTool );
2544 cancelled = true;
2545 break;
2546 }
2547 }
2548 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2549 {
2550 if( m_layer != m_frame->GetActiveLayer() )
2551 {
2552 m_layer = m_frame->GetActiveLayer();
2553 m_stroke.SetWidth( bds.GetLineThickness( m_layer ) );
2554 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2555 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2556
2557 m_textAttrs.m_Size = bds.GetTextSize( m_layer );
2558 m_textAttrs.m_StrokeWidth = bds.GetTextThickness( m_layer );
2560 m_textAttrs.m_Italic = bds.GetTextItalic( m_layer );
2561 m_textAttrs.m_KeepUpright = bds.GetTextUpright( m_layer );
2562 m_textAttrs.m_Mirrored = m_board->IsBackLayer( m_layer );
2565 }
2566
2567 if( graphic )
2568 {
2569 if( !m_view->IsLayerVisible( m_layer ) )
2570 {
2571 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
2572 m_frame->GetCanvas()->Refresh();
2573 }
2574
2575 graphic->SetLayer( m_layer );
2576 graphic->SetStroke( m_stroke );
2577
2578 if( PCB_TEXTBOX* pcb_textbox = dynamic_cast<PCB_TEXTBOX*>( graphic ) )
2579 pcb_textbox->SetAttributes( m_textAttrs );
2580
2581 m_view->Update( &m_preview );
2582 frame()->SetMsgPanel( graphic );
2583 }
2584 else
2585 {
2586 evt->SetPassEvent();
2587 }
2588 }
2589 else if( evt->IsClick( BUT_RIGHT ) )
2590 {
2591 if( !graphic )
2592 m_toolMgr->VetoContextMenuMouseWarp();
2593
2594 m_menu->ShowContextMenu( selection() );
2595 }
2596 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
2597 {
2598 if( !graphic )
2599 break;
2600
2601 if( !started )
2602 {
2603 m_toolMgr->RunAction( ACTIONS::selectionClear );
2604
2605 if( aStartingPoint )
2606 {
2607 cursorPos = *aStartingPoint;
2608 aStartingPoint = std::nullopt;
2609 }
2610
2611 // Init the new item attributes
2612 if( graphic ) // always true, but Coverity can't seem to figure that out
2613 {
2614 graphic->SetShape( static_cast<SHAPE_T>( shape ) );
2615 graphic->SetFilled( false );
2616 graphic->SetStroke( m_stroke );
2617 graphic->SetLayer( m_layer );
2618 }
2619
2620 if( PCB_TEXTBOX* pcb_textbox = dynamic_cast<PCB_TEXTBOX*>( graphic ) )
2621 pcb_textbox->SetAttributes( m_textAttrs );
2622
2623 grid.SetSkipPoint( cursorPos );
2624
2625 twoPointMgr.SetOrigin( cursorPos );
2626 twoPointMgr.SetEnd( cursorPos );
2627
2628 if( !isLocalOriginSet )
2629 m_frame->GetScreen()->m_LocalOrigin = cursorPos;
2630
2631 m_preview.Add( graphic );
2632 frame()->SetMsgPanel( graphic );
2633 m_controls->SetAutoPan( true );
2634 m_controls->CaptureCursor( true );
2635
2636 if( !m_view->IsLayerVisible( m_layer ) )
2637 {
2638 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
2639 m_frame->GetCanvas()->Refresh();
2640 }
2641
2642 updateSegmentFromGeometryMgr( twoPointMgr, graphic );
2643
2644 if( shape == SHAPE_T::ELLIPSE_ARC )
2645 graphic->SetEditState( 1 );
2646
2647 started = true;
2648 }
2649 else if( multiPhase )
2650 {
2651 if( !graphic->ContinueEdit( cursorPos ) )
2652 {
2653 // Multi-phase shape is complete
2654 if( marker )
2655 {
2656 m_preview.Remove( marker );
2657 delete marker;
2658 marker = nullptr;
2659 }
2660
2661 graphic->EndEdit();
2662 graphic->ClearEditFlags();
2663 graphic->SetFlags( IS_NEW );
2664 m_preview.Clear();
2665 break;
2666 }
2667 }
2668 else
2669 {
2670 // Check if the shape needs more clicks
2671 if( graphic->ContinueEdit( cursorPos ) )
2672 {
2673 multiPhase = true;
2674 m_view->Remove( &twoPointAsst );
2675 m_view->Update( &m_preview );
2676 }
2677 else
2678 {
2679 PCB_SHAPE* snapItem = dynamic_cast<PCB_SHAPE*>( grid.GetSnapped() );
2680
2681 if( shape == SHAPE_T::SEGMENT && snapItem && graphic->GetLength() > 0 )
2682 {
2683 // User has clicked on the end of an existing segment, closing a path
2684 BOARD_COMMIT commit( m_frame );
2685
2686 commit.Add( graphic );
2687 commit.Push( _( "Draw Line" ) );
2688 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, graphic );
2689
2690 graphic = nullptr;
2691 }
2692 else if( twoPointMgr.IsEmpty() || evt->IsDblClick( BUT_LEFT ) )
2693 {
2694 // User has clicked twice in the same spot, meaning we're finished
2695 delete graphic;
2696 graphic = nullptr;
2697 }
2698
2699 m_preview.Clear();
2700 twoPointMgr.Reset();
2701 break;
2702 }
2703 }
2704
2705 twoPointMgr.SetEnd( GetClampedCoords( cursorPos ) );
2706 }
2707 else if( evt->IsMotion() )
2708 {
2709 if( multiPhase )
2710 {
2711 graphic->CalcEdit( GetClampedCoords( cursorPos ) );
2712
2713 if( shape == SHAPE_T::ELLIPSE_ARC )
2714 {
2715 VECTOR2I markerPos = evalEllipsePoint( graphic, cursorPos );
2716
2717 if( !marker )
2718 {
2719 marker = new PCB_SHAPE( static_cast<BOARD_ITEM*>( m_frame->GetModel() ) );
2720 marker->SetShape( SHAPE_T::CIRCLE );
2721 marker->SetFilled( true );
2722 marker->SetLayer( m_layer );
2723 marker->SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
2724 m_preview.Add( marker );
2725 }
2726
2727 int radius = KiROUND( m_view->ToWorld( 4 ) );
2728 marker->SetStart( markerPos );
2729 marker->SetEnd( markerPos + VECTOR2I( radius, 0 ) );
2730 }
2731
2732 m_view->Update( &m_preview );
2733 frame()->SetMsgPanel( graphic );
2734 }
2735 else
2736 {
2737 VECTOR2I clampedCursorPos = cursorPos;
2738
2739 if( shape == SHAPE_T::CIRCLE || shape == SHAPE_T::ARC )
2740 clampedCursorPos = getClampedRadiusEnd( twoPointMgr.GetOrigin(), cursorPos );
2741 else
2742 clampedCursorPos = getClampedDifferenceEnd( twoPointMgr.GetOrigin(), cursorPos );
2743
2744 // constrained lines
2745 if( started && angleSnap != LEADER_MODE::DIRECT )
2746 {
2747 const VECTOR2I lineVector( clampedCursorPos - VECTOR2I( twoPointMgr.GetOrigin() ) );
2748
2749 VECTOR2I newEnd;
2750 if( angleSnap == LEADER_MODE::DEG90 )
2751 newEnd = GetVectorSnapped90( lineVector );
2752 else
2753 newEnd = GetVectorSnapped45( lineVector, ( shape == SHAPE_T::RECTANGLE ) );
2754
2755 m_controls->ForceCursorPosition( true, VECTOR2I( twoPointMgr.GetEnd() ) );
2756 twoPointMgr.SetEnd( twoPointMgr.GetOrigin() + newEnd );
2757 twoPointMgr.SetAngleSnap( angleSnap );
2758 }
2759 else
2760 {
2761 twoPointMgr.SetEnd( clampedCursorPos );
2762 twoPointMgr.SetAngleSnap( LEADER_MODE::DIRECT );
2763 }
2764
2765 updateSegmentFromGeometryMgr( twoPointMgr, graphic );
2766 m_view->Update( &m_preview );
2767 m_view->Update( &twoPointAsst );
2768 }
2769 }
2770 else if( started && ( evt->IsAction( &PCB_ACTIONS::doDelete )
2771 || evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) ) )
2772 {
2773 if( aCommittedGraphics && !aCommittedGraphics->empty() )
2774 {
2775 twoPointMgr.SetOrigin( aCommittedGraphics->top()->GetStart() );
2776 twoPointMgr.SetEnd( aCommittedGraphics->top()->GetEnd() );
2777 aCommittedGraphics->pop();
2778
2779 // Snap guides persist in the grid helper until the tool exits, so a mid-draw
2780 // backup must clear them or they linger on screen.
2781 grid.FullReset();
2782
2783 getViewControls()->WarpMouseCursor( twoPointMgr.GetEnd(), true );
2784
2785 if( PICKED_ITEMS_LIST* undo = m_frame->PopCommandFromUndoList() )
2786 {
2787 m_frame->PutDataInPreviousState( undo );
2788 m_frame->ClearListAndDeleteItems( undo );
2789 delete undo;
2790 }
2791
2792 updateSegmentFromGeometryMgr( twoPointMgr, graphic );
2793 m_view->Update( &m_preview );
2794 m_view->Update( &twoPointAsst );
2795 }
2796 else
2797 {
2798 cleanup();
2799 break;
2800 }
2801 }
2802 else if( graphic && evt->IsAction( &PCB_ACTIONS::incWidth ) )
2803 {
2804 m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
2805 graphic->SetStroke( m_stroke );
2806 m_view->Update( &m_preview );
2807 frame()->SetMsgPanel( graphic );
2808 }
2809 else if( graphic && evt->IsAction( &PCB_ACTIONS::decWidth ) )
2810 {
2811 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
2812 {
2813 m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
2814 graphic->SetStroke( m_stroke );
2815 m_view->Update( &m_preview );
2816 frame()->SetMsgPanel( graphic );
2817 }
2818 }
2819 else if( started && evt->IsAction( &PCB_ACTIONS::properties ) )
2820 {
2821 frame()->OnEditItemRequest( graphic );
2822 m_view->Update( &m_preview );
2823 frame()->SetMsgPanel( graphic );
2824 }
2825 else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
2826 || evt->IsAction( &ACTIONS::redo ) ) )
2827 {
2828 wxBell();
2829 }
2830 else if( evt->IsAction( &ACTIONS::resetLocalCoords ) )
2831 {
2832 isLocalOriginSet = true;
2833 evt->SetPassEvent();
2834 }
2835 else if( evt->IsAction( &ACTIONS::updateUnits ) )
2836 {
2837 if( frame()->GetUserUnits() != userUnits )
2838 {
2839 userUnits = frame()->GetUserUnits();
2840 twoPointAsst.SetUnits( userUnits );
2841 m_view->Update( &twoPointAsst );
2842 }
2843 evt->SetPassEvent();
2844 }
2845 else
2846 {
2847 evt->SetPassEvent();
2848 }
2849 }
2850
2851 if( !isLocalOriginSet ) // reset the relative coordinate if it was not set before
2852 m_frame->GetScreen()->m_LocalOrigin = VECTOR2D( 0, 0 );
2853
2854 if( !multiPhase )
2855 m_view->Remove( &twoPointAsst );
2856
2857 m_view->Remove( &m_preview );
2858
2859 if( selection().Empty() )
2860 m_frame->SetMsgPanel( board() );
2861
2862 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2863 m_controls->SetAutoPan( false );
2864 m_controls->CaptureCursor( false );
2865 m_controls->ForceCursorPosition( false );
2866
2867 return !cancelled;
2868}
2869
2870
2875 PCB_SHAPE& aArc )
2876{
2877 VECTOR2I vec = aMgr.GetOrigin();
2878
2879 aArc.SetCenter( vec );
2880
2881 if( aMgr.GetSubtended() < ANGLE_0 )
2882 {
2883 vec = aMgr.GetStartRadiusEnd();
2884 aArc.SetStart( vec );
2885 vec = aMgr.GetEndRadiusEnd();
2886 aArc.SetEnd( vec );
2887 }
2888 else
2889 {
2890 vec = aMgr.GetEndRadiusEnd();
2891 aArc.SetStart( vec );
2892 vec = aMgr.GetStartRadiusEnd();
2893 aArc.SetEnd( vec );
2894 }
2895}
2896
2897
2898bool DRAWING_TOOL::drawArc( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic,
2899 std::optional<VECTOR2D> aStartingPoint )
2900{
2901 wxCHECK( aGraphic, false );
2902
2903 PCB_SHAPE*& graphic = *aGraphic;
2904
2905 wxCHECK( graphic, false );
2906
2907 if( m_layer != m_frame->GetActiveLayer() )
2908 {
2909 m_layer = m_frame->GetActiveLayer();
2910 m_stroke.SetWidth( m_frame->GetDesignSettings().GetLineThickness( m_layer ) );
2911 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2912 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2913 }
2914
2915 // Arc geometric construction manager
2917
2918 // Arc drawing assistant overlay
2919 KIGFX::PREVIEW::ARC_ASSISTANT arcAsst( arcManager, pcbIUScale, m_frame->GetUserUnits() );
2920
2921 // Add a VIEW_GROUP that serves as a preview for the new item
2922 PCB_SELECTION preview;
2923 m_view->Add( &preview );
2924 m_view->Add( &arcAsst );
2925 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
2926
2927 auto setCursor =
2928 [&]()
2929 {
2930 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2931 };
2932
2933 auto cleanup =
2934 [&] ()
2935 {
2936 preview.Clear();
2937 delete *aGraphic;
2938 *aGraphic = nullptr;
2939 };
2940
2941 m_controls->ShowCursor( true );
2942 m_controls->ForceCursorPosition( false );
2943 // Set initial cursor
2944 setCursor();
2945
2946 bool started = false;
2947 bool cancelled = false;
2948
2949 m_toolMgr->PostAction( ACTIONS::refreshPreview );
2950
2951 if( aStartingPoint )
2952 m_toolMgr->PrimeTool( *aStartingPoint );
2953
2954 // Main loop: keep receiving events
2955 while( TOOL_EVENT* evt = Wait() )
2956 {
2957 if( started )
2958 m_frame->SetMsgPanel( graphic );
2959
2960 setCursor();
2961
2962 graphic->SetLayer( m_layer );
2963
2964 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2965 LEADER_MODE angleSnap = GetAngleSnapMode();
2966
2967 if( evt->Modifier( MD_CTRL ) )
2968 angleSnap = LEADER_MODE::DIRECT;
2969
2970 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2971 VECTOR2I cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(), graphic,
2972 GRID_GRAPHICS ),
2974 m_controls->ForceCursorPosition( true, cursorPos );
2975
2976 if( evt->IsCancelInteractive() || ( started && evt->IsAction( &ACTIONS::undo ) ) )
2977 {
2978 cleanup();
2979
2980 if( !started )
2981 {
2982 // We've handled the cancel event. Don't cancel other tools
2983 evt->SetPassEvent( false );
2984 m_frame->PopTool( aTool );
2985 cancelled = true;
2986 }
2987
2988 break;
2989 }
2990 else if( evt->IsActivate() )
2991 {
2992 if( evt->IsPointEditor() )
2993 {
2994 // don't exit (the point editor runs in the background)
2995 }
2996 else if( evt->IsMoveTool() )
2997 {
2998 cleanup();
2999 // leave ourselves on the stack so we come back after the move
3000 cancelled = true;
3001 break;
3002 }
3003 else
3004 {
3005 cleanup();
3006 m_frame->PopTool( aTool );
3007 cancelled = true;
3008 break;
3009 }
3010 }
3011 else if( evt->IsClick( BUT_LEFT ) )
3012 {
3013 if( !started )
3014 {
3015 m_toolMgr->RunAction( ACTIONS::selectionClear );
3016
3017 m_controls->SetAutoPan( true );
3018 m_controls->CaptureCursor( true );
3019
3020 // Init the new item attributes
3021 // (non-geometric, those are handled by the manager)
3022 graphic->SetShape( SHAPE_T::ARC );
3023 graphic->SetStroke( m_stroke );
3024
3025 if( !m_view->IsLayerVisible( m_layer ) )
3026 {
3027 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
3028 m_frame->GetCanvas()->Refresh();
3029 }
3030
3031 preview.Add( graphic );
3032 frame()->SetMsgPanel( graphic );
3033 started = true;
3034 }
3035
3036 arcManager.AddPoint( cursorPos, true );
3037 }
3038 else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
3039 {
3040 // Snap guides persist in the grid helper until the tool exits, so a mid-draw backup
3041 // must clear them or they linger on screen.
3042 grid.FullReset();
3043 arcManager.RemoveLastPoint();
3044 }
3045 else if( evt->IsMotion() )
3046 {
3047 // set angle snap
3048 arcManager.SetAngleSnap( angleSnap != LEADER_MODE::DIRECT );
3049
3050 // update, but don't step the manager state
3051 arcManager.AddPoint( cursorPos, false );
3052 }
3053 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
3054 {
3055 if( m_layer != m_frame->GetActiveLayer() )
3056 {
3057 m_layer = m_frame->GetActiveLayer();
3058 m_stroke.SetWidth( m_frame->GetDesignSettings().GetLineThickness( m_layer ) );
3059 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
3060 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
3061 }
3062
3063 if( graphic )
3064 {
3065 if( !m_view->IsLayerVisible( m_layer ) )
3066 {
3067 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
3068 m_frame->GetCanvas()->Refresh();
3069 }
3070
3071 graphic->SetLayer( m_layer );
3072 graphic->SetStroke( m_stroke );
3073 m_view->Update( &preview );
3074 frame()->SetMsgPanel( graphic );
3075 }
3076 else
3077 {
3078 evt->SetPassEvent();
3079 }
3080 }
3081 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
3082 {
3084 {
3085 graphic->SetArcAngleAndEnd( ANGLE_90 );
3086 frame()->OnEditItemRequest( graphic );
3087 m_view->Update( &preview );
3088 frame()->SetMsgPanel( graphic );
3089 break;
3090 }
3091 // Don't show the edit panel if we can't represent the arc with it
3092 else if( ( arcManager.GetStep() == KIGFX::PREVIEW::ARC_GEOM_MANAGER::SET_ANGLE )
3093 && ( arcManager.GetStartRadiusEnd() != arcManager.GetEndRadiusEnd() ) )
3094 {
3095 frame()->OnEditItemRequest( graphic );
3096 m_view->Update( &preview );
3097 frame()->SetMsgPanel( graphic );
3098 break;
3099 }
3100 else
3101 {
3102 evt->SetPassEvent();
3103 }
3104 }
3105 else if( evt->IsClick( BUT_RIGHT ) )
3106 {
3107 if( !graphic )
3108 m_toolMgr->VetoContextMenuMouseWarp();
3109
3110 m_menu->ShowContextMenu( selection() );
3111 }
3112 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
3113 {
3114 m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
3115
3116 if( graphic )
3117 {
3118 graphic->SetStroke( m_stroke );
3119 m_view->Update( &preview );
3120 frame()->SetMsgPanel( graphic );
3121 }
3122 }
3123 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
3124 {
3125 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
3126 {
3127 m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
3128
3129 if( graphic )
3130 {
3131 graphic->SetStroke( m_stroke );
3132 m_view->Update( &preview );
3133 frame()->SetMsgPanel( graphic );
3134 }
3135 }
3136 }
3137 else if( evt->IsAction( &PCB_ACTIONS::arcPosture ) )
3138 {
3139 arcManager.ToggleClockwise();
3140 }
3141 else if( evt->IsAction( &ACTIONS::updateUnits ) )
3142 {
3143 arcAsst.SetUnits( frame()->GetUserUnits() );
3144 m_view->Update( &arcAsst );
3145 evt->SetPassEvent();
3146 }
3147 else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
3148 || evt->IsAction( &ACTIONS::redo ) ) )
3149 {
3150 wxBell();
3151 }
3152 else
3153 {
3154 evt->SetPassEvent();
3155 }
3156
3157 if( arcManager.IsComplete() )
3158 {
3159 break;
3160 }
3161 else if( arcManager.HasGeometryChanged() )
3162 {
3163 updateArcFromConstructionMgr( arcManager, *graphic );
3164 m_view->Update( &preview );
3165 m_view->Update( &arcAsst );
3166
3167 if( started )
3168 frame()->SetMsgPanel( graphic );
3169 else
3170 frame()->SetMsgPanel( board() );
3171 }
3172 }
3173
3174 preview.Remove( graphic );
3175 m_view->Remove( &arcAsst );
3176 m_view->Remove( &preview );
3177
3178 if( selection().Empty() )
3179 m_frame->SetMsgPanel( board() );
3180
3181 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3182 m_controls->SetAutoPan( false );
3183 m_controls->CaptureCursor( false );
3184 m_controls->ForceCursorPosition( false );
3185
3186 return !cancelled;
3187}
3188
3189
3190std::unique_ptr<PCB_SHAPE> DRAWING_TOOL::drawOneBezier( const TOOL_EVENT& aTool,
3191 const OPT_VECTOR2I& aStartingPoint,
3192 const OPT_VECTOR2I& aStartingControl1Point,
3193 DRAW_ONE_RESULT& aResult )
3194{
3195 int maxError = board()->GetDesignSettings().m_MaxError;
3196
3197 std::unique_ptr<PCB_SHAPE> bezier = std::make_unique<PCB_SHAPE>( m_frame->GetModel() );
3198 bezier->SetShape( SHAPE_T::BEZIER );
3199 bezier->SetFlags( IS_NEW );
3200
3201 if( m_layer != m_frame->GetActiveLayer() )
3202 {
3203 m_layer = m_frame->GetActiveLayer();
3204 m_stroke.SetWidth( m_frame->GetDesignSettings().GetLineThickness( m_layer ) );
3205 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
3206 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
3207 }
3208
3209 // Arc geometric construction manager
3211
3212 // Arc drawing assistant overlay
3213 KIGFX::PREVIEW::BEZIER_ASSISTANT bezierAsst( bezierManager, pcbIUScale, m_frame->GetUserUnits() );
3214
3215 // Add a VIEW_GROUP that serves as a preview for the new item
3216 PCB_SELECTION preview;
3217 m_view->Add( &preview );
3218 m_view->Add( &bezierAsst );
3219 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
3220
3221 const auto setCursor =
3222 [&]()
3223 {
3224 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
3225 };
3226
3227 const auto resetProgress =
3228 [&]()
3229 {
3230 preview.Clear();
3231 bezier.reset();
3232 };
3233
3234 m_controls->ShowCursor( true );
3235 m_controls->ForceCursorPosition( false );
3236 // Set initial cursor
3237 setCursor();
3238
3239 const auto started =
3240 [&]()
3241 {
3243 };
3244
3245 aResult = DRAW_ONE_RESULT::ACCEPTED;
3246 bool priming = false;
3247
3248 m_toolMgr->PostAction( ACTIONS::refreshPreview );
3249
3250 // Load in one or two points if they were passed in
3251 if( aStartingPoint )
3252 {
3253 priming = true;
3254
3255 if( aStartingControl1Point )
3256 {
3257 bezierManager.AddPoint( *aStartingPoint, true );
3258 bezierManager.AddPoint( *aStartingControl1Point, true );
3259 m_toolMgr->PrimeTool( *aStartingControl1Point );
3260 }
3261 else
3262 {
3263 bezierManager.AddPoint( *aStartingPoint, true );
3264 m_toolMgr->PrimeTool( *aStartingPoint );
3265 }
3266 }
3267
3268 // Main loop: keep receiving events
3269 while( TOOL_EVENT* evt = Wait() )
3270 {
3271 if( started() )
3272 m_frame->SetMsgPanel( bezier.get() );
3273
3274 setCursor();
3275
3276 // Init the new item attributes
3277 // (non-geometric, those are handled by the manager)
3278 bezier->SetShape( SHAPE_T::BEZIER );
3279 bezier->SetStroke( m_stroke );
3280 bezier->SetLayer( m_layer );
3281
3282 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
3283 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
3284 VECTOR2I cursorPos = GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(), bezier.get(),
3285 GRID_GRAPHICS ),
3287 m_controls->ForceCursorPosition( true, cursorPos );
3288
3289 if( evt->IsCancelInteractive() || ( started() && evt->IsAction( &ACTIONS::undo ) ) )
3290 {
3291 resetProgress();
3292
3293 if( !started() )
3294 {
3295 // We've handled the cancel event. Don't cancel other tools
3296 evt->SetPassEvent( false );
3297 m_frame->PopTool( aTool );
3299 }
3300 else
3301 {
3302 // We're not cancelling, but we're also not returning a finished bezier
3303 // So we'll be called again.
3304 aResult = DRAW_ONE_RESULT::RESET;
3305 }
3306
3307 break;
3308 }
3309 else if( evt->IsActivate() )
3310 {
3311 if( evt->IsPointEditor() )
3312 {
3313 // don't exit (the point editor runs in the background)
3314 }
3315 else if( evt->IsMoveTool() )
3316 {
3317 resetProgress();
3318 // leave ourselves on the stack so we come back after the move
3320 break;
3321 }
3322 else
3323 {
3324 resetProgress();
3325 m_frame->PopTool( aTool );
3327 break;
3328 }
3329 }
3330 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
3331 {
3332 if( !started() )
3333 {
3334 m_toolMgr->RunAction( ACTIONS::selectionClear );
3335
3336 m_controls->SetAutoPan( true );
3337 m_controls->CaptureCursor( true );
3338
3339 if( !m_view->IsLayerVisible( m_layer ) )
3340 {
3341 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
3342 m_frame->GetCanvas()->Refresh();
3343 }
3344
3345 frame()->SetMsgPanel( bezier.get() );
3346 }
3347
3348 if( !priming )
3349 bezierManager.AddPoint( cursorPos, true );
3350 else
3351 priming = false;
3352
3353 const bool doubleClick = evt->IsDblClick( BUT_LEFT );
3354
3355 if( doubleClick )
3356 {
3357 // Use the current point for all remaining points
3358 while( bezierManager.GetStep() < KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END )
3359 bezierManager.AddPoint( cursorPos, true );
3360 }
3361
3363 preview.Add( bezier.get() );
3364
3365 // Return to the caller for a reset
3366 if( doubleClick )
3367 {
3368 // Don't chain to this one
3370 break;
3371 }
3372 }
3373 else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
3374 {
3375 // Snap guides persist in the grid helper until the tool exits, so a mid-draw backup
3376 // must clear them or they linger on screen.
3377 grid.FullReset();
3378 bezierManager.RemoveLastPoint();
3379
3381 preview.Remove( bezier.get() );
3382 }
3383 else if( evt->IsMotion() )
3384 {
3385 // set angle snap
3386 // bezierManager.SetAngleSnap( Is45Limited() );
3387
3388 // update, but don't step the manager state
3389 bezierManager.AddPoint( cursorPos, false );
3390 }
3391 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
3392 {
3393 if( m_layer != m_frame->GetActiveLayer() )
3394 {
3395 m_layer = m_frame->GetActiveLayer();
3396 m_stroke.SetWidth( m_frame->GetDesignSettings().GetLineThickness( m_layer ) );
3397 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
3398 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
3399 }
3400
3401 if( !m_view->IsLayerVisible( m_layer ) )
3402 {
3403 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
3404 m_frame->GetCanvas()->Refresh();
3405 }
3406
3407 bezier->SetLayer( m_layer );
3408 bezier->SetStroke( m_stroke );
3409 m_view->Update( &preview );
3410 frame()->SetMsgPanel( bezier.get() );
3411 }
3412 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
3413 {
3414 // Don't show the edit panel if we can't represent the arc with it
3415 if( ( bezierManager.GetStep() >= KIGFX::PREVIEW::BEZIER_GEOM_MANAGER::SET_END ) )
3416 {
3417 frame()->OnEditItemRequest( bezier.get() );
3418 m_view->Update( &preview );
3419 frame()->SetMsgPanel( bezier.get() );
3420 break;
3421 }
3422 else
3423 {
3424 evt->SetPassEvent();
3425 }
3426 }
3427 else if( evt->IsClick( BUT_RIGHT ) )
3428 {
3429 m_menu->ShowContextMenu( selection() );
3430 }
3431 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
3432 {
3433 m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
3434
3435 bezier->SetStroke( m_stroke );
3436 m_view->Update( &preview );
3437 frame()->SetMsgPanel( bezier.get() );
3438 }
3439 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
3440 {
3441 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
3442 {
3443 m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
3444
3445 bezier->SetStroke( m_stroke );
3446 m_view->Update( &preview );
3447 frame()->SetMsgPanel( bezier.get() );
3448 }
3449 }
3450 else if( evt->IsAction( &ACTIONS::updateUnits ) )
3451 {
3452 bezierAsst.SetUnits( frame()->GetUserUnits() );
3453 m_view->Update( &bezierAsst );
3454 evt->SetPassEvent();
3455 }
3456 else if( started() && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
3457 || evt->IsAction( &ACTIONS::redo ) ) )
3458 {
3459 wxBell();
3460 }
3461 else
3462 {
3463 evt->SetPassEvent();
3464 }
3465
3466 if( bezierManager.IsComplete() )
3467 {
3468 break;
3469 }
3470 else if( bezierManager.HasGeometryChanged() )
3471 {
3472 bezier->SetStart( bezierManager.GetStart() );
3473 bezier->SetBezierC1( bezierManager.GetControlC1() );
3474 bezier->SetEnd( bezierManager.GetEnd() );
3475 bezier->SetBezierC2( bezierManager.GetControlC2() );
3476 bezier->RebuildBezierToSegmentsPointsList( maxError );
3477
3478 m_view->Update( &preview );
3479 m_view->Update( &bezierAsst );
3480
3481 // Once we are receiving end points, we can show the bezier in the preview
3483 frame()->SetMsgPanel( bezier.get() );
3484 else
3485 frame()->SetMsgPanel( board() );
3486 }
3487 }
3488
3489 preview.Remove( bezier.get() );
3490 m_view->Remove( &bezierAsst );
3491 m_view->Remove( &preview );
3492
3493 if( selection().Empty() )
3494 m_frame->SetMsgPanel( board() );
3495
3496 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3497 m_controls->SetAutoPan( false );
3498 m_controls->CaptureCursor( false );
3499 m_controls->ForceCursorPosition( false );
3500
3501 return bezier;
3502};
3503
3504
3506{
3507 bool clearSelection = false;
3508 *aZone = nullptr;
3509
3510 // not an action that needs a source zone
3511 if( aMode == ZONE_MODE::ADD || aMode == ZONE_MODE::GRAPHIC_POLYGON )
3512 return true;
3513
3514 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
3515 const PCB_SELECTION& selection = selTool->GetSelection();
3516
3517 if( selection.Empty() )
3518 {
3519 clearSelection = true;
3520 m_toolMgr->RunAction( ACTIONS::selectionCursor );
3521 }
3522
3523 // we want a single zone
3524 if( selection.Size() == 1 && selection[0]->Type() == PCB_ZONE_T )
3525 *aZone = static_cast<ZONE*>( selection[0] );
3526
3527 // expected a zone, but didn't get one
3528 if( !*aZone )
3529 {
3530 if( clearSelection )
3531 m_toolMgr->RunAction( ACTIONS::selectionClear );
3532
3533 return false;
3534 }
3535
3536 return true;
3537}
3538
3539
3541{
3542 if( m_isFootprintEditor && !m_frame->GetModel() )
3543 return 0;
3544
3545 if( m_inDrawingTool )
3546 return 0;
3547
3549
3550 ZONE_MODE zoneMode = aEvent.Parameter<ZONE_MODE>();
3551 MODE drawMode = MODE::ZONE;
3552
3553 if( aEvent.IsAction( &PCB_ACTIONS::drawRuleArea ) )
3554 drawMode = MODE::KEEPOUT;
3555
3556 if( aEvent.IsAction( &PCB_ACTIONS::drawPolygon ) )
3557 drawMode = MODE::GRAPHIC_POLYGON;
3558
3559 const bool drawingThieving = aEvent.IsAction( &PCB_ACTIONS::drawCopperThievingZone );
3560
3561 SCOPED_DRAW_MODE scopedDrawMode( m_mode, drawMode );
3562
3563 // get a source zone, if we need one. We need it for:
3564 // ZONE_MODE::CUTOUT (adding a hole to the source zone)
3565 // ZONE_MODE::SIMILAR (creating a new zone using settings of source zone
3566 ZONE* sourceZone = nullptr;
3567
3568 if( !getSourceZoneForAction( zoneMode, &sourceZone ) )
3569 return 0;
3570
3571 // Turn zones on if they are off, so that the created object will be visible after completion
3572 m_frame->SetObjectVisible( LAYER_ZONES );
3573
3575
3576 params.m_keepout = drawMode == MODE::KEEPOUT;
3577 params.m_thieving = drawingThieving;
3578 params.m_mode = zoneMode;
3579 params.m_sourceZone = sourceZone;
3580 params.m_layer = m_frame->GetActiveLayer();
3581
3582 if( zoneMode == ZONE_MODE::SIMILAR && !sourceZone->IsOnLayer( params.m_layer ) )
3583 params.m_layer = sourceZone->GetFirstLayer();
3584
3585 ZONE_CREATE_HELPER zoneTool( *this, params );
3586 // the geometry manager which handles the zone geometry, and hands the calculated points
3587 // over to the zone creator tool
3588 POLYGON_GEOM_MANAGER polyGeomMgr( zoneTool );
3589 bool started = false;
3590 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
3591
3592 m_frame->PushTool( aEvent );
3593
3594 auto setCursor =
3595 [&]()
3596 {
3597 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
3598 };
3599
3600 auto cleanup =
3601 [&] ()
3602 {
3603 polyGeomMgr.Reset();
3604 started = false;
3605 grid.ClearSkipPoint();
3606
3607 // Snap guides persist in the grid helper until the tool exits, so abandoning the
3608 // outline mid-draw must clear them or they linger on screen.
3609 grid.FullReset();
3610
3611 m_controls->SetAutoPan( false );
3612 m_controls->CaptureCursor( false );
3613 };
3614
3615 Activate();
3616 // Must be done after Activate() so that it gets set into the correct context
3617 m_controls->ShowCursor( true );
3618 m_controls->ForceCursorPosition( false );
3619 // Set initial cursor
3620 setCursor();
3621
3622 if( aEvent.HasPosition() )
3623 m_toolMgr->PrimeTool( aEvent.Position() );
3624
3625 // Main loop: keep receiving events
3626 while( TOOL_EVENT* evt = Wait() )
3627 {
3628 setCursor();
3629
3630 LSET layers( { m_frame->GetActiveLayer() } );
3631 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
3632 LEADER_MODE angleSnap = GetAngleSnapMode();
3633
3634 if( evt->Modifier( MD_CTRL ) )
3635 angleSnap = LEADER_MODE::DIRECT;
3636
3637 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
3638
3639 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
3640 cursorPos = GetClampedCoords( grid.BestSnapAnchor( cursorPos, layers, GRID_GRAPHICS ), COORDS_PADDING );
3641
3642 m_controls->ForceCursorPosition( true, cursorPos );
3643
3644 polyGeomMgr.SetLeaderMode( angleSnap );
3645
3646 if( evt->IsCancelInteractive() )
3647 {
3648 if( started )
3649 {
3650 cleanup();
3651 }
3652 else
3653 {
3654 m_frame->PopTool( aEvent );
3655
3656 // We've handled the cancel event. Don't cancel other tools
3657 evt->SetPassEvent( false );
3658 break;
3659 }
3660 }
3661 else if( evt->IsActivate() )
3662 {
3663 if( started )
3664 cleanup();
3665
3666 if( evt->IsPointEditor() )
3667 {
3668 // don't exit (the point editor runs in the background)
3669 }
3670 else if( evt->IsMoveTool() )
3671 {
3672 // leave ourselves on the stack so we come back after the move
3673 break;
3674 }
3675 else
3676 {
3677 m_frame->PopTool( aEvent );
3678 break;
3679 }
3680 }
3681 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
3682 {
3683 if( zoneMode != ZONE_MODE::SIMILAR )
3684 params.m_layer = frame()->GetActiveLayer();
3685
3686 if( !m_view->IsLayerVisible( params.m_layer ) )
3687 {
3688 m_frame->GetAppearancePanel()->SetLayerVisible( params.m_layer, true );
3689 m_frame->GetCanvas()->Refresh();
3690 }
3691 }
3692 else if( evt->IsClick( BUT_RIGHT ) )
3693 {
3694 if( !started )
3695 m_toolMgr->VetoContextMenuMouseWarp();
3696
3697 m_menu->ShowContextMenu( selection() );
3698 }
3699 // events that lock in nodes
3700 else if( evt->IsClick( BUT_LEFT )
3701 || evt->IsDblClick( BUT_LEFT )
3702 || evt->IsAction( &PCB_ACTIONS::closeOutline ) )
3703 {
3704 // Check if it is double click / closing line (so we have to finish the zone)
3705 const bool endPolygon = evt->IsDblClick( BUT_LEFT )
3706 || evt->IsAction( &PCB_ACTIONS::closeOutline )
3707 || polyGeomMgr.NewPointClosesOutline( cursorPos );
3708
3709 if( endPolygon )
3710 {
3711 polyGeomMgr.SetFinished();
3712 polyGeomMgr.Reset();
3713
3714 cleanup();
3715 m_frame->PopTool( aEvent );
3716 break;
3717 }
3718 // adding a corner
3719 else if( polyGeomMgr.AddPoint( cursorPos ) )
3720 {
3721 if( !started )
3722 {
3723 started = true;
3724
3725 m_controls->SetAutoPan( true );
3726 m_controls->CaptureCursor( true );
3727
3728 if( !m_view->IsLayerVisible( params.m_layer ) )
3729 {
3730 m_frame->GetAppearancePanel()->SetLayerVisible( params.m_layer, true );
3731 m_frame->GetCanvas()->Refresh();
3732 }
3733 }
3734 }
3735 }
3736 else if( started && ( evt->IsAction( &PCB_ACTIONS::deleteLastPoint )
3737 || evt->IsAction( &ACTIONS::doDelete )
3738 || evt->IsAction( &ACTIONS::undo ) ) )
3739 {
3740 // Snap guides persist in the grid helper until the tool exits, so dropping a corner
3741 // must clear them or they linger on screen.
3742 grid.FullReset();
3743
3744 if( std::optional<VECTOR2I> last = polyGeomMgr.DeleteLastCorner() )
3745 {
3746 cursorPos = last.value();
3747 getViewControls()->WarpMouseCursor( cursorPos, true );
3748 m_controls->ForceCursorPosition( true, cursorPos );
3749 polyGeomMgr.SetCursorPosition( cursorPos );
3750 }
3751 else
3752 {
3753 cleanup();
3754 }
3755 }
3756 else if( started && ( evt->IsMotion()
3757 || evt->IsDrag( BUT_LEFT ) ) )
3758 {
3759 polyGeomMgr.SetCursorPosition( cursorPos );
3760 }
3761 else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
3762 || evt->IsAction( &ACTIONS::redo ) ) )
3763 {
3764 wxBell();
3765 }
3766 else if( started && evt->IsAction( &PCB_ACTIONS::properties ) )
3767 {
3768 frame()->OnEditItemRequest( zoneTool.GetZone() );
3769 zoneTool.OnGeometryChange( polyGeomMgr );
3770 frame()->SetMsgPanel( zoneTool.GetZone() );
3771 }
3772 /*else if( evt->IsAction( &ACTIONS::updateUnits ) )
3773 {
3774 // If we ever have an assistant here that reports dimensions, we'll want to
3775 // update its units here....
3776 // zoneAsst.SetUnits( frame()->GetUserUnits() );
3777 // m_view->Update( &zoneAsst );
3778 evt->SetPassEvent();
3779 }*/
3780 else
3781 {
3782 evt->SetPassEvent();
3783 }
3784
3785 } // end while
3786
3787 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3788 m_controls->ForceCursorPosition( false );
3789 controls()->SetAutoPan( false );
3790 m_controls->CaptureCursor( false );
3791 return 0;
3792}
3793
3794
3796{
3798 return 0;
3799
3800 if( m_inDrawingTool )
3801 return 0;
3802
3804
3805 struct VIA_PLACER : public INTERACTIVE_PLACER_BASE
3806 {
3808 PCB_GRID_HELPER m_gridHelper;
3809 std::shared_ptr<DRC_ENGINE> m_drcEngine;
3810 int m_drcEpsilon;
3811 int m_worstClearance;
3812 bool m_allowDRCViolations;
3813
3814 int sub_e( int aClearance )
3815 {
3816 return std::max( 0, aClearance - m_drcEpsilon );
3817 };
3818
3819 VIA_PLACER( PCB_BASE_EDIT_FRAME* aFrame ) :
3820 m_frame( aFrame ),
3821 m_gridHelper( aFrame->GetToolManager(), aFrame->GetMagneticItemsSettings() ),
3822 m_drcEngine( aFrame->GetBoard()->GetDesignSettings().m_DRCEngine ),
3823 m_drcEpsilon( aFrame->GetBoard()->GetDesignSettings().GetDRCEpsilon() ),
3824 m_worstClearance( 0 )
3825 {
3826 ROUTER_TOOL* router = m_frame->GetToolManager()->GetTool<ROUTER_TOOL>();
3827
3828 if( router )
3829 m_allowDRCViolations = router->Router()->Settings().AllowDRCViolations();
3830
3831 try
3832 {
3833 if( aFrame )
3834 m_drcEngine->InitEngine( aFrame->GetBoard()->GetDesignRulesPath() );
3835
3836 DRC_CONSTRAINT constraint;
3837
3838 if( m_drcEngine->QueryWorstConstraint( CLEARANCE_CONSTRAINT, constraint ) )
3839 m_worstClearance = constraint.GetValue().Min();
3840
3841 if( m_drcEngine->QueryWorstConstraint( HOLE_CLEARANCE_CONSTRAINT, constraint ) )
3842 m_worstClearance = std::max( m_worstClearance, constraint.GetValue().Min() );
3843
3844 for( FOOTPRINT* footprint : aFrame->GetBoard()->Footprints() )
3845 {
3846 for( PAD* pad : footprint->Pads() )
3847 {
3848 std::optional<int> padOverride = pad->GetClearanceOverrides( nullptr );
3849
3850 if( padOverride.has_value() )
3851 m_worstClearance = std::max( m_worstClearance, padOverride.value() );
3852 }
3853 }
3854 }
3855 catch( PARSE_ERROR& )
3856 {
3857 }
3858 }
3859
3860 virtual ~VIA_PLACER()
3861 {
3862 }
3863
3868 static BOX2I getEffectiveBoundingBox( const PCB_VIA& aVia, const VECTOR2I& aPosition )
3869 {
3870 BOX2I bbox = aVia.GetBoundingBox();
3871 bbox.Move( aPosition - aVia.GetPosition() );
3872 return bbox;
3873 }
3874
3875 PCB_TRACK* findTrack( const PCB_VIA* aVia, const VECTOR2I& aPosition ) const
3876 {
3877 const LSET lset = aVia->GetLayerSet();
3878 const BOX2I bbox = getEffectiveBoundingBox( *aVia, aPosition );
3879
3880 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
3881 KIGFX::PCB_VIEW* view = m_frame->GetCanvas()->GetView();
3882 std::vector<PCB_TRACK*> possible_tracks;
3883
3884 wxCHECK( view, nullptr );
3885
3886 view->Query( bbox, items );
3887
3888 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
3889 {
3890 if( !it.first->IsBOARD_ITEM() )
3891 continue;
3892
3893 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
3894
3895 if( !( item->GetLayerSet() & lset ).any() )
3896 continue;
3897
3898 if( item->Type() == PCB_TRACE_T )
3899 {
3900 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
3901
3902 if( TestSegmentHit( aPosition, track->GetStart(), track->GetEnd(),
3903 ( track->GetWidth() + aVia->GetWidth( track->GetLayer() ) ) / 2 ) )
3904 {
3905 possible_tracks.push_back( track );
3906 }
3907 }
3908 else if( item->Type() == PCB_ARC_T )
3909 {
3910 PCB_ARC* arc = static_cast<PCB_ARC*>( item );
3911
3912 if( arc->HitTest( aPosition, aVia->GetWidth( arc->GetLayer() ) / 2 ) )
3913 possible_tracks.push_back( arc );
3914 }
3915 }
3916
3917 PCB_TRACK* return_track = nullptr;
3918 int min_d = std::numeric_limits<int>::max();
3919
3920 for( PCB_TRACK* track : possible_tracks )
3921 {
3922 SEG test( track->GetStart(), track->GetEnd() );
3923 int dist = ( test.NearestPoint( aPosition ) - aPosition ).EuclideanNorm();
3924
3925 if( dist < min_d )
3926 {
3927 min_d = dist;
3928 return_track = track;
3929 }
3930 }
3931
3932 return return_track;
3933 }
3934
3935 bool hasDRCViolation( PCB_VIA* aVia, BOARD_ITEM* aOther )
3936 {
3937 DRC_CONSTRAINT constraint;
3938 int clearance;
3939 BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aOther );
3940 ZONE* zone = dynamic_cast<ZONE*>( aOther );
3941
3942 if( zone && zone->GetIsRuleArea() )
3943 {
3944 if( zone->GetDoNotAllowVias() )
3945 {
3946 bool hit = false;
3947
3949 [&]( PCB_LAYER_ID aLayer )
3950 {
3951 if( hit )
3952 return;
3953
3954 SHAPE_POLY_SET zoneOutline = zone->GetBoardOutline();
3955
3956 if( zoneOutline.Collide( aVia->GetPosition(), aVia->GetWidth( aLayer ) / 2 ) )
3957 hit = true;
3958 } );
3959
3960 return hit;
3961 }
3962
3963 return false;
3964 }
3965
3966 if( connectedItem )
3967 {
3968 int connectedItemNet = connectedItem->GetNetCode();
3969
3970 if( connectedItemNet == 0 || connectedItemNet == aVia->GetNetCode() )
3971 return false;
3972 }
3973
3974 for( PCB_LAYER_ID layer : aOther->GetLayerSet() )
3975 {
3976 // Reference images are "on" a copper layer but are not actually part of it
3977 if( !IsCopperLayer( layer ) || aOther->Type() == PCB_REFERENCE_IMAGE_T )
3978 continue;
3979
3980 constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aVia, aOther, layer );
3981 clearance = constraint.GetValue().Min();
3982
3983 if( clearance >= 0 )
3984 {
3985 std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( layer );
3986 std::shared_ptr<SHAPE> otherShape = aOther->GetEffectiveShape( layer );
3987
3988 if( viaShape->Collide( otherShape.get(), sub_e( clearance ) ) )
3989 return true;
3990 }
3991 }
3992
3993 if( aOther->HasHole() )
3994 {
3995 constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aVia, aOther, UNDEFINED_LAYER );
3996 clearance = constraint.GetValue().Min();
3997
3998 if( clearance >= 0 )
3999 {
4000 std::shared_ptr<SHAPE> viaShape = aVia->GetEffectiveShape( UNDEFINED_LAYER );
4001
4002 if( viaShape->Collide( aOther->GetEffectiveHoleShape().get(), sub_e( clearance ) ) )
4003 return true;
4004 }
4005 }
4006
4007 return false;
4008 }
4009
4010 bool checkDRCViolation( PCB_VIA* aVia )
4011 {
4012 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
4013 std::set<BOARD_ITEM*> checkedItems;
4014 BOX2I bbox = aVia->GetBoundingBox();
4015
4016 bbox.Inflate( m_worstClearance );
4017 m_frame->GetCanvas()->GetView()->Query( bbox, items );
4018
4019 for( std::pair<KIGFX::VIEW_ITEM*, int> it : items )
4020 {
4021 if( !it.first->IsBOARD_ITEM() )
4022 continue;
4023
4024 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
4025
4026 if( item->Type() == PCB_ZONE_T && !static_cast<ZONE*>( item )->GetIsRuleArea() )
4027 {
4028 continue; // stitching vias bind to zones, so ignore them
4029 }
4030 else if( item->Type() == PCB_FOOTPRINT_T || item->Type() == PCB_GROUP_T )
4031 {
4032 continue; // check against children, but not against footprint itself
4033 }
4034 else if( ( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T )
4035 && !static_cast<PCB_TEXT*>( item )->IsVisible() )
4036 {
4037 continue; // ignore hidden items
4038 }
4039 else if( checkedItems.count( item ) )
4040 {
4041 continue; // already checked
4042 }
4043
4044 if( hasDRCViolation( aVia, item ) )
4045 return true;
4046
4047 checkedItems.insert( item );
4048 }
4049
4050 DRC_CONSTRAINT constraint = m_drcEngine->EvalRules( DISALLOW_CONSTRAINT, aVia, nullptr,
4052
4053 if( constraint.m_DisallowFlags && constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
4054 return true;
4055
4056 return false;
4057 }
4058
4059 PAD* findPad( const PCB_VIA* aVia, const VECTOR2I& aPosition ) const
4060 {
4061 const LSET lset = aVia->GetLayerSet();
4062 const BOX2I bbox = getEffectiveBoundingBox( *aVia, aPosition );
4063
4064 const KIGFX::PCB_VIEW& view = *m_frame->GetCanvas()->GetView();
4065 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
4066
4067 view.Query( bbox, items );
4068
4069 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
4070 {
4071 if( !it.first->IsBOARD_ITEM() )
4072 continue;
4073
4074 BOARD_ITEM& item = static_cast<BOARD_ITEM&>( *it.first );
4075
4076 if( item.Type() == PCB_PAD_T && ( item.GetLayerSet() & lset ).any() )
4077 {
4078 PAD& pad = static_cast<PAD&>( item );
4079
4080 if( pad.HitTest( aPosition ) )
4081 return &pad;
4082 }
4083 }
4084
4085 return nullptr;
4086 }
4087
4088 PCB_SHAPE* findGraphic( const PCB_VIA* aVia, const VECTOR2I& aPosition ) const
4089 {
4090 const LSET lset = aVia->GetLayerSet() & LSET::AllCuMask();
4091 BOX2I bbox = getEffectiveBoundingBox( *aVia, aPosition );
4092
4093 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
4094 KIGFX::PCB_VIEW* view = m_frame->GetCanvas()->GetView();
4095 PCB_LAYER_ID activeLayer = m_frame->GetActiveLayer();
4096 std::vector<PCB_SHAPE*> possible_shapes;
4097
4098 view->Query( bbox, items );
4099
4100 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
4101 {
4102 if( !it.first->IsBOARD_ITEM() )
4103 continue;
4104
4105 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
4106
4107 if( !( item->GetLayerSet() & lset ).any() )
4108 continue;
4109
4110 if( item->Type() == PCB_SHAPE_T )
4111 {
4112 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
4113
4114 if( shape->HitTest( aPosition, aVia->GetWidth( activeLayer ) / 2 ) )
4115 possible_shapes.push_back( shape );
4116 }
4117 }
4118
4119 PCB_SHAPE* return_shape = nullptr;
4120 int min_d = std::numeric_limits<int>::max();
4121
4122 for( PCB_SHAPE* shape : possible_shapes )
4123 {
4124 int dist = ( shape->GetPosition() - aPosition ).EuclideanNorm();
4125
4126 if( dist < min_d )
4127 {
4128 min_d = dist;
4129 return_shape = shape;
4130 }
4131 }
4132
4133 return return_shape;
4134 }
4135
4136 std::optional<int> selectPossibleNetsByPopupMenu( std::set<int>& aNetcodeList )
4137 {
4138 ACTION_MENU menu( true );
4139 const NETINFO_LIST& netInfo = m_board->GetNetInfo();
4140 std::map<int, int> menuIDNetCodeMap;
4141 int menuID = 1;
4142
4143 for( int netcode : aNetcodeList )
4144 {
4145 wxString menuText;
4146 if( menuID < 10 )
4147 {
4148#ifdef __WXMAC__
4149 menuText = wxString::Format( "%s\t",
4150 netInfo.GetNetItem( netcode )->GetNetname() );
4151#else
4152 menuText = wxString::Format( "&%d %s\t",
4153 menuID,
4154 netInfo.GetNetItem( netcode )->GetNetname() );
4155#endif
4156 }
4157 else
4158 {
4159 menuText = netInfo.GetNetItem( netcode )->GetNetname();
4160 }
4161
4162 menu.Add( menuText, menuID, BITMAPS::INVALID_BITMAP );
4163 menuIDNetCodeMap[ menuID ] = netcode;
4164 menuID++;
4165 }
4166
4167 menu.SetTitle( _( "Select Net:" ) );
4168 menu.DisplayTitle( true );
4169
4170 DRAWING_TOOL* drawingTool = m_frame->GetToolManager()->GetTool<DRAWING_TOOL>();
4171 drawingTool->SetContextMenu( &menu, CMENU_NOW );
4172
4173 int selectedNetCode = -1;
4174 bool cancelled = false;
4175
4176 while( TOOL_EVENT* evt = drawingTool->Wait() )
4177 {
4178 if( evt->Action() == TA_CHOICE_MENU_UPDATE )
4179 {
4180 evt->SetPassEvent();
4181 }
4182 else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
4183 {
4184 std::optional<int> id = evt->GetCommandId();
4185
4186 // User has selected an item, so this one will be returned
4187 if( id && ( *id > 0 ) && ( *id < menuID ) )
4188 {
4189 selectedNetCode = menuIDNetCodeMap.at( *id );
4190 }
4191 // User has cancelled the menu (either by <esc> or clicking out of it),
4192 else
4193 {
4194 cancelled = true;
4195 }
4196 }
4197 else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
4198 {
4199 break;
4200 }
4201 }
4202
4203 if( cancelled )
4204 return std::optional<int>();
4205 else
4206 return selectedNetCode;
4207 }
4208
4209 std::optional<int> findStitchedZoneNet( PCB_VIA* aVia )
4210 {
4211 const VECTOR2I position = aVia->GetPosition();
4212 PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
4213 std::set<int> netcodeList;
4214
4215 // See if there are any connections available on a high-contrast layer
4218 {
4219 if( aVia->GetLayerSet().test( m_frame->GetActiveLayer() ) )
4220 {
4221 for( ZONE* z : m_board->Zones() )
4222 {
4223 if( z->IsOnLayer( m_frame->GetActiveLayer() ) )
4224 {
4225 if( z->HitTestFilledArea( m_frame->GetActiveLayer(), position ) )
4226 netcodeList.insert( z->GetNetCode() );
4227 }
4228 }
4229 }
4230 }
4231
4232 // If there's only one, return it.
4233 if( netcodeList.size() == 1 )
4234 return *netcodeList.begin();
4235
4236 // See if there are any connections available on a visible layer
4237 LSET lset = LSET( m_board->GetVisibleLayers() & aVia->GetLayerSet() );
4238
4239 for( ZONE* z : m_board->Zones() )
4240 {
4241 if( z->GetIsRuleArea() )
4242 continue; // ignore rule areas
4243
4244 for( PCB_LAYER_ID layer : lset )
4245 {
4246 if( z->IsOnLayer( layer ) )
4247 {
4248 if( z->HitTestFilledArea( layer, position ) )
4249 netcodeList.insert( z->GetNetCode() );
4250 }
4251 }
4252 }
4253
4254 // If there's only one, return it.
4255 if( netcodeList.size() == 1 )
4256 return *netcodeList.begin();
4257
4258 if( netcodeList.size() > 1 )
4259 {
4260 // The net assignment is ambiguous. Let the user decide.
4261 return selectPossibleNetsByPopupMenu( netcodeList );
4262 }
4263 else
4264 {
4266 }
4267 }
4268
4269 void SnapItem( BOARD_ITEM *aItem ) override
4270 {
4271 m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
4272
4273 MAGNETIC_SETTINGS* settings = m_frame->GetMagneticItemsSettings();
4274 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
4275
4276 // When snapping, use the mouse position, not the item position, which may be
4277 // grid-snapped, so that we can get the cursor within snap-range of snap points.
4278 // If we don't get a snap, the via will be left as it is (i.e. maybe grid-snapped).
4279 KIGFX::VIEW_CONTROLS& viewControls = *m_frame->GetCanvas()->GetViewControls();
4280 const VECTOR2I position = viewControls.GetMousePosition();
4281
4282 if( settings->tracks != MAGNETIC_OPTIONS::NO_EFFECT && m_gridHelper.GetSnap() )
4283 {
4284 if( PCB_TRACK* track = findTrack( via, position ) )
4285 {
4286 SEG trackSeg( track->GetStart(), track->GetEnd() );
4287 VECTOR2I snap = m_gridHelper.AlignToSegment( position, trackSeg );
4288
4289 aItem->SetPosition( snap );
4290 return;
4291 }
4292 }
4293
4294 if( settings->pads != MAGNETIC_OPTIONS::NO_EFFECT && m_gridHelper.GetSnap() )
4295 {
4296 if( PAD* pad = findPad( via, position ) )
4297 {
4298 aItem->SetPosition( pad->GetPosition() );
4299 return;
4300 }
4301 }
4302
4303 if( settings->graphics && m_gridHelper.GetSnap() )
4304 {
4305 if( PCB_SHAPE* shape = findGraphic( via, position ) )
4306 {
4307 if( shape->IsAnyFill() )
4308 {
4309 // Is this shape something to be replaced by the via, or something to be
4310 // stitched by multiple vias? Use an area-based test to make a guess.
4311 SHAPE_POLY_SET poly;
4312 shape->TransformShapeToPolygon( poly, shape->GetLayer(), 0, ARC_LOW_DEF, ERROR_INSIDE );
4313 double shapeArea = poly.Area();
4314
4315 int R = via->GetWidth( shape->GetLayer() ) / 2;
4316 double viaArea = M_PI * R * R;
4317
4318 if( viaArea * 4 > shapeArea )
4319 aItem->SetPosition( shape->GetPosition() );
4320 }
4321 else
4322 {
4323 switch( shape->GetShape() )
4324 {
4325 case SHAPE_T::SEGMENT:
4326 {
4327 SEG seg( shape->GetStart(), shape->GetEnd() );
4328 VECTOR2I snap = m_gridHelper.AlignToSegment( position, seg );
4329 aItem->SetPosition( snap );
4330 break;
4331 }
4332
4333 case SHAPE_T::ARC:
4334 {
4335 if( ( shape->GetEnd() - position ).SquaredEuclideanNorm() <
4336 ( shape->GetStart() - position ).SquaredEuclideanNorm() )
4337 {
4338 aItem->SetPosition( shape->GetEnd() );
4339 }
4340 else
4341 {
4342 aItem->SetPosition( shape->GetStart() );
4343 }
4344
4345 break;
4346 }
4347
4348 case SHAPE_T::POLY:
4349 {
4350 if( !shape->IsPolyShapeValid() )
4351 {
4352 aItem->SetPosition( shape->GetPosition() );
4353 break;
4354 }
4355
4356 const SHAPE_POLY_SET& polySet = shape->GetPolyShape();
4357 std::optional<SEG> nearestSeg;
4358 int minDist = std::numeric_limits<int>::max();
4359
4360 for( int ii = 0; ii < polySet.OutlineCount(); ++ii )
4361 {
4362 const SHAPE_LINE_CHAIN& poly = polySet.Outline( ii );
4363
4364 for( int jj = 0; jj < poly.SegmentCount(); ++jj )
4365 {
4366 const SEG& seg = poly.GetSegment( jj );
4367 int dist = seg.Distance( position );
4368
4369 if( dist < minDist )
4370 {
4371 minDist = dist;
4372 nearestSeg = seg;
4373 }
4374 }
4375 }
4376
4377 if( nearestSeg )
4378 {
4379 VECTOR2I snap = m_gridHelper.AlignToSegment( position, *nearestSeg );
4380 aItem->SetPosition( snap );
4381 }
4382
4383 break;
4384 }
4385
4386 default:
4387 aItem->SetPosition( shape->GetPosition() );
4388 }
4389
4390 }
4391 }
4392
4393 }
4394 }
4395
4396 bool PlaceItem( BOARD_ITEM* aItem, BOARD_COMMIT& aCommit ) override
4397 {
4398 WX_INFOBAR* infobar = m_frame->GetInfoBar();
4399 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
4400 VECTOR2I viaPos = via->GetPosition();
4401 PCB_TRACK* track = findTrack( via, via->GetPosition() );
4402 PAD* pad = findPad( via, via->GetPosition() );
4403 PCB_SHAPE* shape = findGraphic( via, via->GetPosition() );
4404
4405 if( track )
4406 {
4407 via->SetNetCode( track->GetNetCode() );
4408 via->SetIsFree( false );
4409 }
4410 else if( pad )
4411 {
4412 via->SetNetCode( pad->GetNetCode() );
4413 via->SetIsFree( false );
4414 }
4415 else if( shape && shape->GetNetCode() > 0 )
4416 {
4417 via->SetNetCode( shape->GetNetCode() );
4418 via->SetIsFree( false );
4419 }
4420 else
4421 {
4422 std::optional<int> netcode = findStitchedZoneNet( via );
4423
4424 if( !netcode.has_value() ) // user cancelled net disambiguation menu
4425 return false;
4426
4427 via->SetNetCode( netcode.value() );
4428 via->SetIsFree( via->GetNetCode() > 0 );
4429 }
4430
4431 if( checkDRCViolation( via ) )
4432 {
4433 m_frame->ShowInfoBarError( _( "Via location violates DRC." ), true,
4434 WX_INFOBAR::MESSAGE_TYPE::DRC_VIOLATION );
4435
4436 if( !m_allowDRCViolations )
4437 return false;
4438 }
4439 else
4440 {
4441 if( infobar->GetMessageType() == WX_INFOBAR::MESSAGE_TYPE::DRC_VIOLATION )
4442 infobar->Dismiss();
4443 }
4444
4445 aCommit.Add( via );
4446
4447 // If the user explicitly disables snap (using shift), then don't break the tracks.
4448 // This will prevent PNS from being able to connect the via and track but
4449 // it is explicitly requested by the user
4450 if( track && m_gridHelper.GetSnap() )
4451 {
4452 VECTOR2I trackStart = track->GetStart();
4453 VECTOR2I trackEnd = track->GetEnd();
4454 SEG trackSeg( trackStart, trackEnd );
4455
4456 if( viaPos == trackStart || viaPos == trackEnd )
4457 return true;
4458
4459 if( !trackSeg.Contains( viaPos ) )
4460 return true;
4461
4462 aCommit.Modify( track );
4463 track->SetStart( trackStart );
4464 track->SetEnd( viaPos );
4465
4466 PCB_TRACK* newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
4467 newTrack->ResetUuidDirect();
4468
4469 newTrack->SetStart( viaPos );
4470 newTrack->SetEnd( trackEnd );
4471 aCommit.Add( newTrack );
4472 }
4473
4474 return true;
4475 }
4476
4477 std::unique_ptr<BOARD_ITEM> CreateItem() override
4478 {
4479 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
4480 PCB_VIA* via = new PCB_VIA( m_board );
4481
4482 via->SetNetCode( 0 );
4483 via->SetViaType( bds.m_CurrentViaType );
4484
4485 if( via->GetViaType() == VIATYPE::THROUGH )
4486 {
4487 via->SetLayerPair( B_Cu, F_Cu );
4488 }
4489 else
4490 {
4491 PCB_LAYER_ID first_layer = m_frame->GetActiveLayer();
4492 PCB_LAYER_ID last_layer;
4493
4494 // prepare switch to new active layer:
4495 if( first_layer != m_frame->GetScreen()->m_Route_Layer_TOP )
4496 last_layer = m_frame->GetScreen()->m_Route_Layer_TOP;
4497 else
4498 last_layer = m_frame->GetScreen()->m_Route_Layer_BOTTOM;
4499
4500 via->SetLayerPair( first_layer, last_layer );
4501 }
4502
4503 if( via->GetViaType() == VIATYPE::MICROVIA )
4504 {
4505 via->SetWidth( PADSTACK::ALL_LAYERS,
4506 via->GetEffectiveNetClass()->GetuViaDiameter() );
4507 via->SetDrill( via->GetEffectiveNetClass()->GetuViaDrill() );
4508 }
4509 else
4510 {
4511 via->SetWidth( PADSTACK::ALL_LAYERS, bds.GetCurrentViaSize() );
4512 via->SetDrill( bds.GetCurrentViaDrill() );
4513 }
4514
4515 return std::unique_ptr<BOARD_ITEM>( via );
4516 }
4517 };
4518
4519 VIA_PLACER placer( frame() );
4520
4521 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::VIA );
4522
4523 doInteractiveItemPlacement( aEvent, &placer, _( "Place via" ), IPO_REPEAT | IPO_SINGLE_CLICK );
4524
4525 return 0;
4526}
4527
4528
4530 const TOOL_EVENT& aEvent, SHAPE_T aShapeType, MODE aMode, const wxString& aCommitLabel,
4531 std::function<bool( const TOOL_EVENT&, PCB_SHAPE**, std::optional<VECTOR2D> )> aDrawer )
4532{
4533 if( m_isFootprintEditor && !m_frame->GetModel() )
4534 return 0;
4535
4536 if( m_inDrawingTool )
4537 return 0;
4538
4540
4541 BOARD_ITEM* parent = m_frame->GetModel();
4542 BOARD_COMMIT commit( m_frame );
4543 SCOPED_DRAW_MODE scopedDrawMode( m_mode, aMode );
4544 std::optional<VECTOR2D> startingPoint;
4545
4546 auto makeShape = [&]()
4547 {
4548 PCB_SHAPE* s = new PCB_SHAPE( parent );
4549 s->SetShape( aShapeType );
4550 s->SetFilled( false );
4551 s->SetFlags( IS_NEW );
4552 return s;
4553 };
4554
4555 PCB_SHAPE* shape = makeShape();
4556
4557 if( aEvent.HasPosition() )
4558 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
4559
4560 m_frame->PushTool( aEvent );
4561 Activate();
4562
4563 while( aDrawer( aEvent, &shape, startingPoint ) )
4564 {
4565 if( shape )
4566 {
4567 commit.Add( shape );
4568 commit.Push( aCommitLabel );
4569 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, shape );
4570 }
4571
4572 shape = makeShape();
4573 startingPoint = std::nullopt;
4574 }
4575
4576 return 0;
4577}
4578
4579
4580const unsigned int DRAWING_TOOL::WIDTH_STEP = pcbIUScale.mmToIU( 0.1 );
4581
4582
4584{
4585 // clang-format off
4614
4618 // clang-format on
4619}
@ ERROR_INSIDE
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
constexpr int ARC_LOW_DEF
Definition base_units.h:136
@ width_track_via
@ INVALID_BITMAP
@ HIDDEN
Inactive layers are hidden.
@ DIMMED
Inactive layers are dimmed (old high-contrast mode)
BOX2< VECTOR2I > BOX2I
Definition box2.h:918
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:986
static TOOL_ACTION cancelInteractive
Definition actions.h:68
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition actions.h:223
static TOOL_ACTION selectionCursor
Select a single item under the cursor position.
Definition actions.h:213
static TOOL_ACTION updateUnits
Definition actions.h:203
static TOOL_ACTION undo
Definition actions.h:71
static TOOL_ACTION activatePointEditor
Definition actions.h:267
static TOOL_ACTION doDelete
Definition actions.h:81
static TOOL_ACTION cursorClick
Definition actions.h:176
static TOOL_ACTION redo
Definition actions.h:72
static TOOL_ACTION selectionClear
Clear the current selection.
Definition actions.h:220
static TOOL_ACTION refreshPreview
Definition actions.h:155
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition actions.h:228
static TOOL_ACTION resetLocalCoords
Definition actions.h:206
Define the structure of a menu based on ACTIONs.
Definition action_menu.h:43
ACTION_MENU(bool isContextMenu, TOOL_INTERACTIVE *aTool=nullptr)
Default constructor.
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.
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
wxMenuItem * Add(const wxString &aLabel, int aId, BITMAPS aIcon)
Add a wxWidgets-style entry to the menu.
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,...
PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
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
void SetViaSizeIndex(int aIndex)
Set the current via size list index to aIndex.
std::shared_ptr< DRC_ENGINE > m_DRCEngine
int GetDRCEpsilon() const
Return an epsilon which accounts for rounding errors, etc.
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
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:81
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition board_item.h:265
void ResetUuidDirect()
Definition board_item.h:242
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:313
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.
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:285
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape() const
virtual bool HasHole() const
Definition board_item.h:177
const FOOTPRINTS & Footprints() const
Definition board.h:420
wxString GetDesignRulesPath() const
Return the absolute path to the design rules file for this board.
Definition board.cpp:272
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1149
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:554
constexpr size_type GetWidth() const
Definition box2.h:210
constexpr Vec Centre() const
Definition box2.h:93
constexpr size_type GetHeight() const
Definition box2.h:211
constexpr coord_type GetLeft() const
Definition box2.h:224
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:164
constexpr void Move(const Vec &aMoveVector)
Move the rectangle by the aMoveVector.
Definition box2.h:134
constexpr coord_type GetRight() const
Definition box2.h:213
constexpr coord_type GetTop() const
Definition box2.h:225
constexpr coord_type GetBottom() const
Definition box2.h:218
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:398
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:102
COMMIT & Add(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Add a new item to the model.
Definition commit.h:74
void AddItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a menu entry to run a TOOL_ACTION on selected items.
void AddSeparator(int aOrder=ANY_ORDER)
Add a separator to the menu.
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
DIALOG_BARCODE_PROPERTIES, derived from DIALOG_BARCODE_PROPERTIES_BASE, created by wxFormBuilder.
std::list< std::unique_ptr< EDA_ITEM > > & GetImportedItems()
void SetFilenameOverride(const wxString &aFilenameOverride)
Set the filename override to be applied in TransferDataToWindow.
int ShowModal() override
Implementing DIALOG_TRACK_VIA_SIZE_BASE.
Tool responsible for drawing graphical elements like lines, arcs, circles, etc.
TEXT_ATTRIBUTES m_textAttrs
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
int DrawEllipseArc(const TOOL_EVENT &aEvent)
PCB_LAYER_ID m_layer
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 PlacePoint(const TOOL_EVENT &aEvent)
Place a reference 0D point.
int DrawVia(const TOOL_EVENT &aEvent)
KIGFX::VIEW_CONTROLS * m_controls
friend class ZONE_CREATE_HELPER
KIGFX::VIEW * m_view
STROKE_PARAMS m_stroke
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.
BOARD_CONNECTED_ITEM * m_pickerItem
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.
VECTOR2I getClampedRadiusEnd(const VECTOR2I &aOrigin, const VECTOR2I &aEnd)
Clamps the end vector to respect numeric limits of radius representation.
static const unsigned int WIDTH_STEP
int runSimpleShapeDraw(const TOOL_EVENT &aEvent, SHAPE_T aShapeType, MODE aMode, const wxString &aCommitLabel, std::function< bool(const TOOL_EVENT &, PCB_SHAPE **, std::optional< VECTOR2D >)> aDrawer)
static const unsigned int COORDS_PADDING
bool drawArc(const TOOL_EVENT &aTool, PCB_SHAPE **aGraphic, std::optional< VECTOR2D > aStartingPoint)
Start drawing an arc.
int DrawBarcode(const TOOL_EVENT &aEvent)
Starts interactively drawing a barcode.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
int DrawEllipse(const TOOL_EVENT &aEvent)
int DrawCircle(const TOOL_EVENT &aEvent)
Start interactively drawing a circle.
int PlaceTuningPattern(const TOOL_EVENT &aEvent)
BOARD * m_board
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
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...
PCB_TUNING_PATTERN * m_tuningPattern
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:241
SEVERITY GetSeverity() const
Definition drc_rule.h:217
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:196
double Sin() const
Definition eda_angle.h:178
double Cos() const
Definition eda_angle.h:197
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:96
virtual VECTOR2I GetPosition() const
Definition eda_item.h:282
virtual void ClearEditFlags()
Definition eda_item.h:166
virtual void SetPosition(const VECTOR2I &aPos)
Definition eda_item.h:283
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:152
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:108
int GetEllipseMinorRadius() const
Definition eda_shape.h:310
const VECTOR2I & GetBezierC2() const
Definition eda_shape.h:283
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:292
void SetCenter(const VECTOR2I &aCenter)
int GetEllipseMajorRadius() const
Definition eda_shape.h:301
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:319
SHAPE_T GetShape() const
Definition eda_shape.h:185
virtual void SetFilled(bool aFlag)
Definition eda_shape.h:152
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:240
double GetLength() const
virtual bool IsVisible() const
Definition eda_text.h:208
virtual void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:576
void SetMirrored(bool isMirrored)
Definition eda_text.cpp:388
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:302
bool GetSnap() const
void SetSnap(bool aSnap)
A color representation with 4 components: red, green, blue, alpha.
Definition color4d.h:101
Represents an assistant draw when interactively drawing an arc on a canvas.
void SetUnits(EDA_UNITS aUnits)
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.
An interface for classes handling user events controlling the view behavior such as zooming,...
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:98
std::pair< VIEW_ITEM *, int > LAYER_ITEM_PAIR
Definition view.h:67
LSET is a set of PCB_LAYER_IDs.
Definition lset.h:37
static const LSET & AllCuMask()
return AllCuMask( MAX_CU_LAYERS );
Definition lset.cpp:604
static const LSET & AllLayersMask()
Definition lset.cpp:637
T Min() const
Definition minoptmax.h:29
const wxString & GetNetname() const
Definition netinfo.h:100
Container for NETINFO_ITEM elements, which are the nets.
Definition netinfo.h:221
static const int ORPHANED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition netinfo.h:260
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::...
static constexpr PCB_LAYER_ID ALL_LAYERS
! Temporary layer identifier to identify code that is not padstack-aware
Definition padstack.h:177
Definition pad.h:61
static TOOL_ACTION deleteLastPoint
static TOOL_ACTION drawRuleArea
static TOOL_ACTION changeDimensionArrows
Switch between dimension arrow directions.
static TOOL_ACTION drawBezier
static TOOL_ACTION placeText
static TOOL_ACTION drawOrthogonalDimension
static TOOL_ACTION drawRectangle
static TOOL_ACTION setAnchor
static TOOL_ACTION placeReferenceImage
static TOOL_ACTION drawCircle
static TOOL_ACTION tuneDiffPair
static TOOL_ACTION trackViaSizeChanged
static TOOL_ACTION layerChanged
static TOOL_ACTION drawEllipseArc
static TOOL_ACTION drawTable
static TOOL_ACTION drawTextBox
static TOOL_ACTION drawZoneCutout
static TOOL_ACTION drawPolygon
static TOOL_ACTION drawRadialDimension
static TOOL_ACTION tuneSingleTrack
static TOOL_ACTION properties
Activation of the edit tool.
static TOOL_ACTION drawLeader
static TOOL_ACTION drawCopperThievingZone
static TOOL_ACTION tuneSkew
static TOOL_ACTION incWidth
Increase width of currently drawn line.
static TOOL_ACTION drawEllipse
static TOOL_ACTION clearHighlight
static TOOL_ACTION spacingDecrease
static TOOL_ACTION placeImportedGraphics
static TOOL_ACTION drawVia
static TOOL_ACTION drawArc
static TOOL_ACTION drawSimilarZone
static TOOL_ACTION decWidth
Decrease width of currently drawn line.
static TOOL_ACTION drawCenterDimension
static TOOL_ACTION arcPosture
Switch posture when drawing arc.
static TOOL_ACTION ddImportGraphics
static TOOL_ACTION placeBarcode
static TOOL_ACTION placePoint
static TOOL_ACTION closeOutline
static TOOL_ACTION amplIncrease
static TOOL_ACTION amplDecrease
static TOOL_ACTION lengthTunerSettings
static TOOL_ACTION spacingIncrease
static TOOL_ACTION drawLine
static TOOL_ACTION drawAlignedDimension
static TOOL_ACTION drawZone
virtual bool HitTest(const VECTOR2I &aPosition, int aAccuracy=0) const override
Test if aPosition is inside or on the boundary of this item.
void SetTextSize(int aTextSize)
Change the height of the human-readable text displayed below the barcode.
void SetPosition(const VECTOR2I &aPos) override
void SetLayer(PCB_LAYER_ID aLayer) override
Set the drawing layer for the barcode and its text.
Common, abstract interface for edit frames.
virtual MAGNETIC_SETTINGS * GetMagneticItemsSettings()
BOARD * GetBoard() const
Abstract dimension API.
void Update()
Update the dimension's cached text and geometry.
virtual void SetEnd(const VECTOR2I &aPoint)
int GetLineThickness() const
virtual void SetStart(const VECTOR2I &aPoint)
void SetExtensionOffset(int aOffset)
void SetLineThickness(int aWidth)
void SetArrowLength(int aLength)
DIM_ARROW_DIRECTION GetArrowDirection() const
virtual VECTOR2I GetEnd() const
void SetArrowDirection(const DIM_ARROW_DIRECTION &aDirection)
virtual VECTOR2I GetStart() const
The dimension's origin is the first feature point for the dimension.
int GetArrowLength() 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.
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:49
Object to handle a bitmap image that can be inserted in a PCB.
The selection tool: currently supports:
PCB_SELECTION & GetSelection()
EDA_ITEM * GetTopLeftItem(bool aFootprintsOnly=false) const override
void SetEllipseCenter(const VECTOR2I &aPt) override
void EndEdit(bool aClosed=true)
Definition pcb_shape.h:99
void CalcEdit(const VECTOR2I &aPosition)
Definition pcb_shape.h:93
void SetArcAngleAndEnd(const EDA_ANGLE &aAngle, bool aCheckNegativeAngle=false)
Definition pcb_shape.h:107
void SetEditState(int aState)
Definition pcb_shape.h:105
void SetEllipseStartAngle(const EDA_ANGLE &aA) 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:153
void SetShape(SHAPE_T aShape) override
Definition pcb_shape.h:200
void SetPosition(const VECTOR2I &aPos) override
Definition pcb_shape.h:75
void SetEllipseEndAngle(const EDA_ANGLE &aA) override
void SetEnd(const VECTOR2I &aEnd) override
void SetEllipseRotation(const EDA_ANGLE &aA) override
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetEllipseMinorRadius(int aR) override
void SetStart(const VECTOR2I &aStart) override
bool ContinueEdit(const VECTOR2I &aPosition)
Definition pcb_shape.h:86
void SetStroke(const STROKE_PARAMS &aStroke) override
void Normalize() override
Perform any normalization required after a user rotate and/or flip.
VECTOR2I GetPosition() const override
Definition pcb_shape.h:76
void SetEllipseMajorRadius(int aR) override
void SetTextThickness(int aWidth) override
The TextThickness is that set by the user.
Definition pcb_text.cpp:495
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true) override
Definition pcb_text.cpp:467
T * frame() const
KIGFX::PCB_VIEW * view() const
LEADER_MODE GetAngleSnapMode() const
Get the current angle snapping mode.
KIGFX::VIEW_CONTROLS * controls() const
PCB_TOOL_BASE(TOOL_ID aId, const std::string &aName)
Constructor.
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.
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:89
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:92
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:93
const VECTOR2I & GetEnd() const
Definition pcb_track.h:90
virtual int GetWidth() const
Definition pcb_track.h:87
VECTOR2I GetPosition() const override
Definition pcb_track.h:553
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.
const PADSTACK & Padstack() const
Definition pcb_track.h:402
int GetWidth() const override
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
const BOX2I GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
virtual COMMON_SETTINGS * GetCommonSettings() const
Definition pgm_base.cpp:528
A holder to handle information on schematic or board items.
ROUTING_SETTINGS & Settings()
Definition pns_router.h:214
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.
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:38
int Distance(const SEG &aSeg) const
Compute minimum Euclidean distance to segment aSeg.
Definition seg.cpp:698
bool Contains(const SEG &aSeg) const
Definition seg.h:320
int AddItemToSel(const TOOL_EVENT &aEvent)
virtual void Add(EDA_ITEM *aItem)
Definition selection.cpp:38
virtual void Remove(EDA_ITEM *aItem)
Definition selection.cpp:56
virtual void Clear() override
Remove all the stored items from the group.
Definition selection.h:94
void SetReferencePoint(const VECTOR2I &aP)
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.
double Area()
Return the area of this poly set.
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.
Simple container to manage line stroke parameters.
GR_TEXT_H_ALIGN_T m_Halign
GR_TEXT_V_ALIGN_T m_Valign
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
T * getEditFrame() const
Return the application window object, casted to requested user type.
Definition tool_base.h:182
T * getModel() const
Return the model object if it matches the requested type.
Definition tool_base.h:195
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition tool_base.cpp:40
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:34
RESET_REASON
Determine the reason of reset for a tool.
Definition tool_base.h:74
@ SHUTDOWN
Tool is being shut down.
Definition tool_base.h:80
Generic, UI-independent tool event.
Definition tool_event.h:167
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:256
bool DisableGridSnapping() const
Definition tool_event.h:367
bool HasParameter() const
Definition tool_event.h:460
const VECTOR2D Position() const
Return mouse cursor position in world coordinates.
Definition tool_event.h:289
bool IsReactivate() const
Control whether the tool is first being pushed to the stack or being reactivated after a pause.
Definition tool_event.h:269
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
T Parameter() const
Return a parameter assigned to the event.
Definition tool_event.h:469
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.
TOOLS_HOLDER * GetToolHolder() const
wxString MessageTextFromValue(double aValue, bool aAddUnitLabel=true, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE) const
A lower-precision version of StringFromValue().
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:77
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
MESSAGE_TYPE GetMessageType() const
Definition wx_infobar.h:93
void OnGeometryChange(const POLYGON_GEOM_MANAGER &aMgr) override
Called when the polygon is complete.
static bool IsZoneFillAction(const TOOL_EVENT *aEvent)
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:811
bool GetDoNotAllowVias() const
Definition zone.h:822
SHAPE_POLY_SET GetBoardOutline() const
Definition zone.cpp:835
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition zone.cpp:730
PCB_LAYER_ID GetFirstLayer() const
Definition zone.cpp:554
This file is part of the common library.
@ MEASURE
Definition cursors.h:64
@ MOVING
Definition cursors.h:44
@ ARROW
Definition cursors.h:42
@ BULLSEYE
Definition cursors.h:54
@ PENCIL
Definition cursors.h:48
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)
static VECTOR2I evalEllipsePoint(const PCB_SHAPE *aGraphic, const VECTOR2I &aCursorPos)
@ DISALLOW_CONSTRAINT
Definition drc_rule.h:71
@ CLEARANCE_CONSTRAINT
Definition drc_rule.h:51
@ HOLE_CLEARANCE_CONSTRAINT
Definition drc_rule.h:53
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:411
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:413
@ RADIANS_T
Definition eda_angle.h:32
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:417
#define IS_NEW
New item, just created.
#define IS_MOVING
Item being moved.
SHAPE_T
Definition eda_shape.h:44
@ ELLIPSE
Definition eda_shape.h:52
@ SEGMENT
Definition eda_shape.h:46
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:47
@ ELLIPSE_ARC
Definition eda_shape.h:53
EDA_UNITS
Definition eda_units.h:44
void ConnectBoardShapes(std::vector< PCB_SHAPE * > &aShapeList, 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...
LEADER_MODE
The kind of the leader line.
@ DEG45
45 Degree only
@ DIRECT
Unconstrained point-to-point.
@ DEG90
90 Degree only
VECTOR2< T > GetVectorSnapped90(const VECTOR2< T > &aVec)
Snap a vector onto the nearest horizontal or vertical line.
void InferBold(TEXT_ATTRIBUTES *aAttrs)
Definition gr_text.h:78
@ GRID_TEXT
Definition grid_helper.h:47
@ GRID_GRAPHICS
Definition grid_helper.h:48
static wxString ImageFileWildcard()
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:675
@ LAYER_FILLED_SHAPES
Copper graphic shape opacity/visibility (color ignored).
Definition layer_ids.h:309
@ LAYER_ZONES
Control for copper zone opacity/visibility (color ignored).
Definition layer_ids.h:291
PCB_LAYER_ID
A quick note on layer IDs:
Definition layer_ids.h:56
@ B_Cu
Definition layer_ids.h:61
@ UNDEFINED_LAYER
Definition layer_ids.h:57
@ F_Cu
Definition layer_ids.h:60
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:79
void AllowNetworkFileSystems(wxDialog *aDialog)
Configure a file dialog to show network and virtual file systems.
Definition wxgtk/ui.cpp:448
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:400
ZONE_MODE
Definition pcb_actions.h:31
@ SIMILAR
Add a new zone with the same settings as an existing one.
Definition pcb_actions.h:34
@ GRAPHIC_POLYGON
Definition pcb_actions.h:35
@ ADD
Add a new zone/keepout with fresh settings.
Definition pcb_actions.h:32
BARCODE class definition.
Class to handle a set of BOARD_ITEMs.
SCOPED_SET_RESET< DRAWING_TOOL::MODE > SCOPED_DRAW_MODE
@ ID_POPUP_PCB_SELECT_CUSTOM_WIDTH
Definition pcbnew_id.h:21
@ ID_POPUP_PCB_SELECT_VIASIZE1
Definition pcbnew_id.h:40
@ ID_POPUP_PCB_SELECT_VIASIZE16
Definition pcbnew_id.h:55
PGM_BASE & Pgm()
The global program "get" accessor.
see class PGM_BASE
Class that computes missing connections on a PCB.
@ RPT_SEVERITY_IGNORE
std::vector< EDA_ITEM * > EDA_ITEMS
std::optional< VECTOR2I > OPT_VECTOR2I
Definition seg.h:35
bool NoPrintableChars(const wxString &aString)
Return true if the string is empty or contains only whitespace.
LINE_STYLE
Dashed line types.
MAGNETIC_OPTIONS tracks
MAGNETIC_OPTIONS pads
A filename or source description, a problem input line, a line number, a byte offset,...
std::unique_ptr< BOARD_ITEM > CreateItem() override
DRAWING_TOOL & m_drawingTool
PCB_BASE_EDIT_FRAME & m_frame
PCB_GRID_HELPER m_gridHelper
void SnapItem(BOARD_ITEM *aItem) override
POINT_PLACER(DRAWING_TOOL &aDrawingTool, PCB_BASE_EDIT_FRAME &aFrame)
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.
bool m_thieving
Layer to begin drawing.
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.
std::vector< std::vector< std::string > > table
VECTOR2I center
int radius
VECTOR2I end
int clearance
wxString result
Test unit parsing edge cases and error handling.
int delta
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_TOP
#define M_PI
@ TA_CHOICE_MENU_CHOICE
Context menu choice.
Definition tool_event.h:94
@ TA_CHOICE_MENU_UPDATE
Context menu update.
Definition tool_event.h:90
@ TA_CHOICE_MENU_CLOSED
Context menu is closed, no matter whether anything has been chosen or not.
Definition tool_event.h:97
@ CMENU_NOW
Right now (after TOOL_INTERACTIVE::SetContextMenu).
Definition tool_event.h:152
std::optional< TOOL_EVENT > OPT_TOOL_EVENT
Definition tool_event.h:637
@ MD_CTRL
Definition tool_event.h:140
@ MD_SHIFT
Definition tool_event.h:139
@ BUT_LEFT
Definition tool_event.h:128
@ BUT_RIGHT
Definition tool_event.h:129
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:171
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:71
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:81
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:99
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:96
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:97
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:104
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:101
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:85
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:82
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:83
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:79
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:95
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:80
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:91
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:93
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:89
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:98
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:683
VECTOR2< double > VECTOR2D
Definition vector2d.h:682
Definition of file extensions used in Kicad.