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