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 "dialog_table_properties.h"
24
25#include <set>
26
27#include <pgm_base.h>
29#include <pcbnew_settings.h>
31#include <bitmap_tracing.h>
45#include <router/router_tool.h>
46#include <status_popup.h>
47#include <tool/tool_manager.h>
48#include <tools/pcb_actions.h>
50#include <kiplatform/ui.h>
52#include <pcb_barcode.h>
61#include <view/view.h>
63#include <widgets/wx_infobar.h>
65#include <wx/filedlg.h>
66#include <wx/msgdlg.h>
67
68#include <bitmaps.h>
69#include <board.h>
70#include <board_commit.h>
72#include <drc/drc_engine.h>
73#include <drc/drc_rule.h>
74#include <confirm.h>
75#include <footprint.h>
76#include <macros.h>
77#include <gal/painter.h>
78#include <pad.h>
79#include <pcb_edit_frame.h>
80#include <pcb_group.h>
81#include <pcb_point.h>
82#include <pcb_reference_image.h>
83#include <pcb_text.h>
84#include <pcb_textbox.h>
85#include <pcb_table.h>
86#include <pcb_tablecell.h>
87#include <pcb_track.h>
88#include <pcb_dimension.h>
89#include <pcb_grid_item.h>
93#include <pcbnew_id.h>
94#include <scoped_set_reset.h>
95#include <string_utils.h>
96#include <zone.h>
97#include <fix_board_shape.h>
98#include <view/view_controls.h>
99
100const unsigned int DRAWING_TOOL::COORDS_PADDING = pcbIUScale.mmToIU( 20 );
101
103
104
105// Bind dimension feature points coincident to measured geometry so it follows that geometry
106// Bindings ride @p aCommit with dimension for single undo interactive draw only paste and duplicate remap constraints
107static void bindDimensionEndpoints( BOARD* aBoard, PCB_DIMENSION_BASE* aDimension, BOARD_ITEM* aParent,
108 BOARD_COMMIT& aCommit )
109{
110 if( !aBoard || !aDimension )
111 return;
112
113 const double tol = pcbIUScale.mmToIU( 0.01 );
114
115 // Radial dimension binds only when centre and rim share one circle or arc
116 // uses coincident centre plus point on circumference rim not generic both ends coincidence
117 if( aDimension->Type() == PCB_DIM_RADIAL_T )
118 {
119 std::optional<KIID> arc = SelectRadialDimensionTarget( aBoard, aDimension->m_Uuid,
120 aDimension->GetStart(),
121 aDimension->GetEnd(), tol );
122
123 if( !arc )
124 return;
125
126 auto addBinding = [&]( PCB_CONSTRAINT_TYPE aType, CONSTRAINT_ANCHOR aDimAnchor,
127 CONSTRAINT_ANCHOR aTargetAnchor )
128 {
129 auto constraint = std::make_unique<PCB_CONSTRAINT>( aParent, aType );
130 constraint->AddMember( aDimension->m_Uuid, aDimAnchor );
131 constraint->AddMember( *arc, aTargetAnchor );
132
133 if( !ConstraintIsDuplicateOnBoard( aBoard, constraint.get() ) )
134 aCommit.Add( constraint.release() );
135 };
136
139 return;
140 }
141
142 // Only aligned/orthogonal dimensions measure a second feature point; the rest bind START.
143 std::optional<VECTOR2I> end;
144
145 switch( aDimension->Type() )
146 {
149 end = aDimension->GetEnd();
150 break;
151
152 default:
153 break;
154 }
155
156 std::vector<ENDPOINT_BINDING> bindings =
157 SelectEndpointBindings( aBoard, aDimension->m_Uuid, aDimension->GetStart(), end, tol );
158
159 for( const ENDPOINT_BINDING& binding : bindings )
160 {
161 auto constraint = std::make_unique<PCB_CONSTRAINT>( aParent, PCB_CONSTRAINT_TYPE::COINCIDENT );
162 constraint->AddMember( aDimension->m_Uuid, binding.sourceAnchor );
163 constraint->AddMember( binding.target.m_item, binding.target.m_anchor, binding.target.m_index );
164
165 if( ConstraintIsDuplicateOnBoard( aBoard, constraint.get() ) )
166 continue;
167
168 aCommit.Add( constraint.release() );
169 }
170}
171
172
173// Stage the auto constraints for a freshly drawn shape on the same commit as the shape
174// Returns the ones the caller must solve after the push to snap the drawn geometry
175static std::vector<PCB_CONSTRAINT*> stageAutoConstraints( BOARD* aBoard, PCB_SHAPE* aShape, BOARD_ITEM* aParent,
176 BOARD_COMMIT& aCommit, bool aAxisConstraint )
177{
178 std::vector<PCB_CONSTRAINT*> snaps;
179
180 for( AUTO_CONSTRAINT& entry : SelectShapeAutoConstraints( aBoard, aShape, aParent, aAxisConstraint ) )
181 {
182 PCB_CONSTRAINT* added = entry.constraint.get();
183 aCommit.Add( entry.constraint.release() );
184
185 if( entry.needsSolve )
186 snaps.push_back( added );
187 }
188
189 return snaps;
190}
191
192
193// Solve after the push since a constraint only goes live at push time
194// Append so the draw the bindings and the snap undo as one action
195static void snapAutoConstraints( PCB_BASE_EDIT_FRAME* aFrame, BOARD* aBoard,
196 const std::vector<PCB_CONSTRAINT*>& aConstraints )
197{
198 if( aConstraints.empty() )
199 return;
200
201 BOARD_COMMIT commit( aFrame );
202
203 for( PCB_CONSTRAINT* constraint : aConstraints )
204 {
205 ApplyConstraintImmediately( aBoard, constraint, nullptr,
206 [&]( BOARD_ITEM* aItem )
207 {
208 commit.Modify( aItem );
209 } );
210 }
211
212 if( !commit.Empty() )
213 commit.Push( _( "Apply Geometric Constraint" ), APPEND_UNDO );
214}
215
216
218{
219public:
221 ACTION_MENU( true )
222 {
224 SetTitle( _( "Select Via Size" ) );
225 }
226
227protected:
228 ACTION_MENU* create() const override
229 {
230 return new VIA_SIZE_MENU();
231 }
232
233 void update() override
234 {
237 bool useIndex = !bds.m_UseConnectedTrackWidth && !bds.UseCustomTrackViaSize();
238 wxString msg;
239
240 Clear();
241
242 Append( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, _( "Use Custom Values..." ),
243 _( "Specify custom track and via sizes" ), wxITEM_CHECK );
245
246 AppendSeparator();
247
248 for( int i = 1; i < (int) bds.m_ViasDimensionsList.size(); i++ )
249 {
251
252 if( via.m_Drill > 0 )
253 {
254 msg.Printf( _("Via %s, hole %s" ),
255 frame->MessageTextFromValue( via.m_Diameter ),
256 frame->MessageTextFromValue( via.m_Drill ) );
257 }
258 else
259 {
260 msg.Printf( _( "Via %s" ),
261 frame->MessageTextFromValue( via.m_Diameter ) );
262 }
263
264 int menuIdx = ID_POPUP_PCB_SELECT_VIASIZE1 + i;
265 Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
266 Check( menuIdx, useIndex && bds.GetViaSizeIndex() == i );
267 }
268 }
269
270 OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
271 {
274 int id = aEvent.GetId();
275
276 // On Windows, this handler can be called with an event ID not existing in any
277 // menuitem, so only set flags when we have an ID match.
278
280 {
281 DIALOG_TRACK_VIA_SIZE sizeDlg( frame, bds );
282
283 if( sizeDlg.ShowModal() == wxID_OK )
284 {
285 bds.UseCustomTrackViaSize( true );
286 bds.m_UseConnectedTrackWidth = false;
287 }
288 }
290 {
291 bds.UseCustomTrackViaSize( false );
292 bds.m_UseConnectedTrackWidth = false;
294 }
295
297 }
298};
299
300
302 PCB_TOOL_BASE( "pcbnew.InteractiveDrawing" ),
303 m_view( nullptr ),
304 m_controls( nullptr ),
305 m_board( nullptr ),
306 m_frame( nullptr ),
307 m_mode( MODE::NONE ),
308 m_inDrawingTool( false ),
311 m_pickerItem( nullptr ),
312 m_tuningPattern( nullptr )
313{
314}
315
316
320
321
323{
324 auto haveHighlight =
325 [this]( const SELECTION& sel )
326 {
327 KIGFX::RENDER_SETTINGS* cfg = m_toolMgr->GetView()->GetPainter()->GetSettings();
328
329 return !cfg->GetHighlightNetCodes().empty();
330 };
331
332 auto activeToolFunctor =
333 [this]( const SELECTION& aSel )
334 {
335 return m_mode != MODE::NONE;
336 };
337
338 // some interactive drawing tools can undo the last point
339 auto canUndoPoint =
340 [this]( const SELECTION& aSel )
341 {
342 return ( m_mode == MODE::ARC
344 || m_mode == MODE::ZONE
347 || m_mode == MODE::BEZIER
348 || m_mode == MODE::LINE );
349 };
350
351 // functor for tools that can automatically close the outline
352 auto canCloseOutline =
353 [this]( const SELECTION& aSel )
354 {
355 return ( m_mode == MODE::ZONE
358 };
359
360 auto arcToolActive =
361 [this]( const SELECTION& aSel )
362 {
363 return m_mode == MODE::ARC;
364 };
365
366 // managed shape loops (arc, ellipse arc, bezier) can be finished early
367 auto canFinishShape =
368 [this]( const SELECTION& aSel )
369 {
370 return ( m_mode == MODE::ARC
372 || m_mode == MODE::BEZIER );
373 };
374
375 auto viaToolActive =
376 [this]( const SELECTION& aSel )
377 {
378 return m_mode == MODE::VIA;
379 };
380
381 auto tuningToolActive =
382 [this]( const SELECTION& aSel )
383 {
384 return m_mode == MODE::TUNING;
385 };
386
387 auto dimensionToolActive =
388 [this]( const SELECTION& aSel )
389 {
390 return m_mode == MODE::DIMENSION;
391 };
392
393 CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu();
394
395 // cancel current tool goes in main context menu at the top if present
396 ctxMenu.AddItem( ACTIONS::cancelInteractive, activeToolFunctor, 1 );
397 ctxMenu.AddSeparator( 1 );
398
399 ctxMenu.AddItem( PCB_ACTIONS::clearHighlight, haveHighlight, 2 );
400 ctxMenu.AddSeparator( haveHighlight, 2 );
401
402 // tool-specific actions
403 ctxMenu.AddItem( PCB_ACTIONS::closeOutline, canCloseOutline, 200 );
404 ctxMenu.AddItem( ACTIONS::deleteLastPoint, canUndoPoint, 200 );
405 ctxMenu.AddItem( ACTIONS::finishInteractive, canFinishShape, 200 );
406 ctxMenu.AddItem( ACTIONS::arcPosture, arcToolActive, 200 );
407 ctxMenu.AddCheckItem( PCB_ACTIONS::drawArcCenter, arcToolActive, 200 );
408 ctxMenu.AddCheckItem( PCB_ACTIONS::drawArcStartEndMid, arcToolActive, 200 );
409 ctxMenu.AddCheckItem( PCB_ACTIONS::drawArcStartEndCenter, arcToolActive, 200 );
410 ctxMenu.AddCheckItem( PCB_ACTIONS::drawArcTangent, arcToolActive, 200 );
411 ctxMenu.AddCheckItem( PCB_ACTIONS::drawArcStartDirEnd, arcToolActive, 200 );
412 ctxMenu.AddItem( ACTIONS::cycleArcDrawMode, arcToolActive, 200 );
413 ctxMenu.AddItem( PCB_ACTIONS::spacingIncrease, tuningToolActive, 200 );
414 ctxMenu.AddItem( PCB_ACTIONS::spacingDecrease, tuningToolActive, 200 );
415 ctxMenu.AddItem( PCB_ACTIONS::amplIncrease, tuningToolActive, 200 );
416 ctxMenu.AddItem( PCB_ACTIONS::amplDecrease, tuningToolActive, 200 );
417 ctxMenu.AddItem( PCB_ACTIONS::lengthTunerSettings, tuningToolActive, 200 );
418 ctxMenu.AddItem( PCB_ACTIONS::changeDimensionArrows, dimensionToolActive, 200 );
419
420 ctxMenu.AddSeparator( 500 );
421
422 std::shared_ptr<VIA_SIZE_MENU> viaSizeMenu = std::make_shared<VIA_SIZE_MENU>();
423 viaSizeMenu->SetTool( this );
424 m_menu->RegisterSubMenu( viaSizeMenu );
425 ctxMenu.AddMenu( viaSizeMenu.get(), viaToolActive, 500 );
426
427 ctxMenu.AddSeparator( 500 );
428
429 // Type-specific sub-menus will be added for us by other tools
430 // For example, zone fill/unfill is provided by the PCB control tool
431
432 // Finally, add the standard zoom/grid items
433 getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( *m_menu.get() );
434
435 return true;
436}
437
438
440{
441 // Init variables used by every drawing tool
442 m_view = getView();
446
447 // Re-initialize session attributes
448 const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
449
450 if( aReason == RESET_REASON::SHUTDOWN )
451 return;
452
453 m_layer = m_frame->GetActiveLayer();
454 m_stroke.SetWidth( bds.GetLineThickness( m_layer ) );
455 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
456 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
457
458 m_textAttrs.m_Size = bds.GetTextSize( m_layer );
459 m_textAttrs.m_StrokeWidth = bds.GetTextThickness( m_layer );
460 m_textAttrs.m_Italic = bds.GetTextItalic( m_layer );
461 m_textAttrs.m_KeepUpright = bds.GetTextUpright( m_layer );
462 m_textAttrs.m_Mirrored = m_board->IsBackLayer( m_layer );
465
467}
468
469
474
475
477{
478 if( m_frame )
479 {
480 switch( GetAngleSnapMode() )
481 {
483 m_frame->DisplayConstraintsMsg( _( "Constrain to H, V, 45" ) );
484 break;
486 m_frame->DisplayConstraintsMsg( _( "Constrain to H, V" ) );
487 break;
488 default:
489 m_frame->DisplayConstraintsMsg( wxString( "" ) );
490 break;
491 }
492 }
493}
494
495
497{
498 if( m_isFootprintEditor && !m_frame->GetModel() )
499 return 0;
500
501 if( m_inDrawingTool )
502 return 0;
503
505
506 BOARD_ITEM* parent = m_frame->GetModel();
507 PCB_SHAPE* line = new PCB_SHAPE( parent );
508 BOARD_COMMIT commit( m_frame );
509 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::LINE );
510 std::optional<VECTOR2D> startingPoint;
511 std::stack<PCB_SHAPE*> committedLines;
512
513 line->SetShape( SHAPE_T::SEGMENT );
514 line->SetFlags( IS_NEW );
515
516 if( aEvent.HasPosition() )
517 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
518
519 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
520 SCOPED_TOOL_PUSHER raii( m_frame, aEvent );
521
522 Activate();
523
524 while( drawShape( originalEvent, &line, startingPoint, &committedLines ) )
525 {
526 if( line )
527 {
528 commit.Add( line );
529
530 std::vector<PCB_CONSTRAINT*> snaps;
531
532 if( GetAutoConstraints() )
533 {
534 snaps = stageAutoConstraints( m_board, line, parent, commit,
536 }
537
538 commit.Push( _( "Draw Line" ) );
540 startingPoint = VECTOR2D( line->GetEnd() );
541 committedLines.push( line );
542 }
543 else
544 {
545 startingPoint = std::nullopt;
546 }
547
548 line = new PCB_SHAPE( parent );
549 line->SetShape( SHAPE_T::SEGMENT );
550 line->SetFlags( IS_NEW );
551 }
552
553 return 0;
554}
555
556
558{
559 if( m_isFootprintEditor && !m_frame->GetModel() )
560 return 0;
561
562 if( m_inDrawingTool )
563 return 0;
564
566
567 bool isTextBox = aEvent.IsAction( &PCB_ACTIONS::drawTextBox );
568 PCB_SHAPE* rect = nullptr;
569 BOARD_COMMIT commit( m_frame );
570 BOARD_ITEM* parent = m_frame->GetModel();
571 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::RECTANGLE );
572 std::optional<VECTOR2D> startingPoint;
573
574 rect = isTextBox ? new PCB_TEXTBOX( parent ) : new PCB_SHAPE( parent );
576 rect->SetFilled( false );
577 rect->SetFlags( IS_NEW );
578
579 if( aEvent.HasPosition() )
580 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
581
582 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
583 SCOPED_TOOL_PUSHER raii( m_frame, aEvent );
584
585 Activate();
586
587 while( drawShape( originalEvent, &rect, startingPoint, nullptr ) )
588 {
589 if( rect )
590 {
591 bool cancelled = false;
592
593 if( PCB_TEXTBOX* textbox = dynamic_cast<PCB_TEXTBOX*>( rect ) )
594 cancelled = m_frame->ShowTextBoxPropertiesDialog( textbox ) != wxID_OK;
595
596 if( cancelled )
597 {
598 delete rect;
599 rect = nullptr;
600 }
601 else
602 {
603 rect->Normalize();
604 commit.Add( rect );
605 commit.Push( isTextBox ? _( "Draw Text Box" ) : _( "Draw Rectangle" ) );
606
607 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, rect );
608 }
609 }
610
611 rect = isTextBox ? new PCB_TEXTBOX( parent ) : new PCB_SHAPE( parent );
613 rect->SetFilled( false );
614 rect->SetFlags( IS_NEW );
615 startingPoint = std::nullopt;
616 }
617
618 return 0;
619}
620
621
623{
624 return runSimpleShapeDraw( aEvent, SHAPE_T::CIRCLE, MODE::CIRCLE, _( "Draw Circle" ),
625 [this]( const TOOL_EVENT& e, PCB_SHAPE** s, std::optional<VECTOR2D> sp )
626 {
627 return drawShape( e, s, sp, nullptr );
628 } );
629}
630
631
633{
634 return runSimpleShapeDraw( aEvent, SHAPE_T::ELLIPSE, MODE::ELLIPSE, _( "Draw Ellipse" ),
635 [this]( const TOOL_EVENT& e, PCB_SHAPE** s, std::optional<VECTOR2D> sp )
636 {
637 return drawShape( e, s, sp, nullptr );
638 } );
639}
640
641
643{
644 if( m_isFootprintEditor && !m_frame->GetModel() )
645 return 0;
646
647 if( m_inDrawingTool )
648 return 0;
649
651
652 BOARD_ITEM* parent = m_frame->GetModel();
653 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( parent );
654 BOARD_COMMIT commit( m_frame );
655 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ARC );
656 std::vector<VECTOR2D> initialPts;
657
658 arc->SetShape( SHAPE_T::ARC );
659 arc->SetFlags( IS_NEW );
660
661 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
662 SCOPED_TOOL_PUSHER raii( m_frame, aEvent );
663
664 Activate();
665
666 if( aEvent.HasPosition() )
667 initialPts.push_back( snapEventPosition( aEvent ) );
668
669 ARC_DRAW_BEHAVIOR arcBehavior( pcbIUScale, m_frame->GetUserUnits() );
670
671 APP_SETTINGS_BASE* cfg = m_frame->config();
672
673 m_frame->SelectToolbarAction( PCB_ACTIONS::DrawArcForMode( StartArcDrawMode( aEvent, cfg, arcBehavior ) ) );
674
675 std::optional<ARC_TANGENT_SEED> tangentSeed;
676 VECTOR2I lastClick;
677
679
681 {
682 result = drawManagedShape( originalEvent, arc, arcBehavior, initialPts, tangentSeed, &lastClick );
683
684 tangentSeed.reset();
685
686 if( arc )
687 {
688 PCB_SHAPE* committedArc = arc.get();
689
690 // The mode can change mid-draw so chain from whatever the settings hold now
692 tangentSeed = ArcTangentSeedNear( *committedArc, lastClick );
693
694 commit.Add( arc.release() );
695
696 std::vector<PCB_CONSTRAINT*> snaps;
697
698 if( GetAutoConstraints() )
699 snaps = stageAutoConstraints( m_board, committedArc, parent, commit, false );
700
701 commit.Push( _( "Draw Arc" ) );
703
704 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, committedArc );
705 }
706
707 arc = std::make_unique<PCB_SHAPE>( parent );
708 arc->SetShape( SHAPE_T::ARC );
709 arc->SetFlags( IS_NEW );
710
711 initialPts.clear();
712 }
713
714 return 0;
715}
716
717
719{
720 if( m_isFootprintEditor && !m_frame->GetModel() )
721 return 0;
722
723 if( m_inDrawingTool )
724 return 0;
725
727
728 BOARD_ITEM* parent = m_frame->GetModel();
729 std::unique_ptr<PCB_SHAPE> arc = std::make_unique<PCB_SHAPE>( parent );
730 BOARD_COMMIT commit( m_frame );
731 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ELLIPSE_ARC );
732 std::vector<VECTOR2D> initialPts;
733
734 arc->SetShape( SHAPE_T::ELLIPSE_ARC );
735 arc->SetFlags( IS_NEW );
736
737 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
738 SCOPED_TOOL_PUSHER raii( m_frame, aEvent );
739
740 Activate();
741
742 if( aEvent.HasPosition() )
743 initialPts.push_back( snapEventPosition( aEvent ) );
744
745 ELLIPSE_ARC_DRAW_BEHAVIOR ellipseBehavior( pcbIUScale, m_frame->GetUserUnits() );
746
748
750 {
751 result = drawManagedShape( originalEvent, arc, ellipseBehavior, initialPts );
752
753 if( arc )
754 {
755 PCB_SHAPE* committedArc = arc.get();
756 commit.Add( arc.release() );
757
758 std::vector<PCB_CONSTRAINT*> snaps;
759
760 if( GetAutoConstraints() )
761 snaps = stageAutoConstraints( m_board, committedArc, parent, commit, false );
762
763 commit.Push( _( "Draw Elliptical Arc" ) );
765
766 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, committedArc );
767 }
768
769 arc = std::make_unique<PCB_SHAPE>( parent );
770 arc->SetShape( SHAPE_T::ELLIPSE_ARC );
771 arc->SetFlags( IS_NEW );
772
773 initialPts.clear();
774 }
775
776 return 0;
777}
778
779
781{
782 if( m_isFootprintEditor && !m_frame->GetModel() )
783 return 0;
784
785 if( m_inDrawingTool )
786 return 0;
787
789
790 BOARD_ITEM* parent = m_frame->GetModel();
791 std::unique_ptr<PCB_SHAPE> bezier = std::make_unique<PCB_SHAPE>( parent );
792 BOARD_COMMIT commit( m_frame );
793 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::BEZIER );
794 std::vector<VECTOR2D> initialPts;
795
796 bezier->SetShape( SHAPE_T::BEZIER );
797 bezier->SetFlags( IS_NEW );
798
799 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
800 SCOPED_TOOL_PUSHER raii( m_frame, aEvent );
801
802 Activate();
803
804 if( aEvent.HasPosition() )
805 initialPts.push_back( snapEventPosition( aEvent ) );
806
807 BEZIER_DRAW_BEHAVIOR bezierBehavior( pcbIUScale, m_frame->GetUserUnits() );
808
810
812 {
813 result = drawManagedShape( originalEvent, bezier, bezierBehavior, initialPts );
814
815 if( bezier )
816 {
817 // Chain: next bezier starts at the end of this one
818 initialPts.clear();
819 initialPts.push_back( bezier->GetEnd() );
820
821 // If the last control arm is non-zero, mirror it for tangent continuity
822 if( bezier->GetEnd() != bezier->GetBezierC2() )
823 {
824 VECTOR2D mirroredC1 = bezier->GetEnd()
825 - ( bezier->GetBezierC2() - bezier->GetEnd() );
826 initialPts.push_back( mirroredC1 );
827 }
828
829 PCB_SHAPE* committedBezier = bezier.get();
830 commit.Add( bezier.release() );
831
832 std::vector<PCB_CONSTRAINT*> snaps;
833
834 if( GetAutoConstraints() )
835 snaps = stageAutoConstraints( m_board, committedBezier, parent, commit, false );
836
837 commit.Push( _( "Draw Bezier" ) );
839
840 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, committedBezier );
841 }
842 else
843 {
844 initialPts.clear();
845 }
846
847 bezier = std::make_unique<PCB_SHAPE>( parent );
848 bezier->SetShape( SHAPE_T::BEZIER );
849 bezier->SetFlags( IS_NEW );
850 }
851
852 return 0;
853}
854
855
857{
858 if( m_inDrawingTool )
859 return 0;
860
862
863 PCB_REFERENCE_IMAGE* image = nullptr;
864
865 // A dropped file arrives as its name.
867 {
868 wxString* filename = aEvent.Parameter<wxString*>();
869
870 if( !filename )
871 return 0;
872
873 image = new PCB_REFERENCE_IMAGE( m_frame->GetModel() );
874
875 if( !image->GetReferenceImage().ReadImageFile( *filename ) )
876 {
877 wxMessageBox( wxString::Format( _( "Could not load image from '%s'." ), *filename ) );
878 delete image;
879 return 0;
880 }
881 }
882 else
883 {
885 }
886
887 bool immediateMode = image != nullptr;
888 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
889 bool ignorePrimePosition = false;
890 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
891
893 PCB_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
894 BOARD_COMMIT commit( m_frame );
895 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::IMAGE );
896 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
897 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
898
899
901
902 // Add all the drawable symbols to preview
903 if( image )
904 {
905 image->SetPosition( cursorPos );
906 image->SetFlags( IS_NEW | IS_MOVING );
907 image->SetLayer( m_frame->GetActiveLayer() );
908
909 m_view->ClearPreview();
910 m_view->AddToPreview( image, false ); // Add, but not give ownership
911 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
912 selectionTool->AddItemToSel( image, false );
913 }
914
915 auto setCursor =
916 [&]()
917 {
918 if( image )
919 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
920 else
921 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
922 };
923
924 auto cleanup =
925 [&] ()
926 {
928 m_view->ClearPreview();
929 m_view->RecacheAllItems();
930 delete image;
931 image = nullptr;
932 };
933
934 Activate();
935
936 // Must be done after Activate() so that it gets set into the correct context
937 getViewControls()->ShowCursor( true );
938
939 // Set initial cursor
940 setCursor();
941
942 // Prime the pump
943 if( image )
944 {
945 m_toolMgr->PostAction( ACTIONS::refreshPreview );
946 }
947 else if( aEvent.HasPosition() )
948 {
949 m_toolMgr->PrimeTool( aEvent.Position() );
950 }
951 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
952 {
953 m_toolMgr->PrimeTool( { 0, 0 } );
954 ignorePrimePosition = true;
955 }
956
957 // Main loop: keep receiving events
958 while( TOOL_EVENT* evt = Wait() )
959 {
960 setCursor();
961
962 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
963 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
964 cursorPos = m_controls->GetMousePosition();
965
966 // Re-evaluate every iteration: the active layer can be changed mid-tool
967 const LSET snapLayers = { m_frame->GetActiveLayer() };
968
969 cursorPos = GetClampedCoords( grid.ResolveSnap( cursorPos, snapLayers, GRID_GRAPHICS ).position,
971 m_controls->ForceCursorPosition( true, cursorPos );
972
973 if( evt->IsCancelInteractive() || ( image && evt->IsAction( &ACTIONS::undo ) ) )
974 {
975 if( image )
976 cleanup();
977 else
978 break;
979
980 if( immediateMode )
981 break;
982 }
983 else if( evt->IsActivate() )
984 {
985 if( image && evt->IsMoveTool() )
986 {
987 // We're already moving our own item; ignore the move tool
988 evt->SetPassEvent( false );
989 continue;
990 }
991
992 if( image )
993 {
994 m_frame->ShowInfoBarMsg( _( "Press <ESC> to cancel image creation." ) );
995 evt->SetPassEvent( false );
996 continue;
997 }
998
999 if( evt->IsMoveTool() )
1000 {
1001 // Make sure we come back after the move tool is done
1002 m_frame->PushTool( originalEvent );
1003 }
1004
1005 break;
1006 }
1007 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
1008 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
1009 {
1010 if( !image )
1011 {
1012 m_toolMgr->RunAction( ACTIONS::selectionClear );
1013
1014 wxFileDialog dlg( m_frame, _( "Choose Image" ), wxEmptyString, wxEmptyString,
1015 FILEEXT::ImageFileWildcard(), wxFD_OPEN );
1016
1018
1019 bool cancelled = false;
1020
1022 [&]()
1023 {
1024 cancelled = dlg.ShowModal() != wxID_OK;
1025 } );
1026
1027 if( cancelled )
1028 continue;
1029
1030 // If we started with a hotkey which has a position then warp back to that.
1031 // Otherwise update to the current mouse position pinned inside the autoscroll
1032 // boundaries.
1033 if( evt->IsPrime() && !ignorePrimePosition )
1034 {
1035 cursorPos = grid.Align( evt->Position() );
1036 getViewControls()->WarpMouseCursor( cursorPos, true );
1037 }
1038 else
1039 {
1041 cursorPos = getViewControls()->GetMousePosition();
1042 }
1043
1044 cursorPos = getViewControls()->GetMousePosition( true );
1045
1046 wxString fullFilename = dlg.GetPath();
1047
1048 if( wxFileExists( fullFilename ) )
1049 image = new PCB_REFERENCE_IMAGE( m_frame->GetModel(), cursorPos );
1050
1051 if( !image || !image->GetReferenceImage().ReadImageFile( fullFilename ) )
1052 {
1053 wxMessageBox( wxString::Format(_( "Could not load image from '%s'." ), fullFilename ) );
1054 delete image;
1055 image = nullptr;
1056 continue;
1057 }
1058
1059 image->SetFlags( IS_NEW | IS_MOVING );
1060 image->SetLayer( m_frame->GetActiveLayer() );
1061
1062 m_view->ClearPreview();
1063 m_view->AddToPreview( image, false ); // Add, but not give ownership
1064 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
1065 selectionTool->AddItemToSel( image, false );
1066
1067 getViewControls()->SetCursorPosition( cursorPos, false );
1068 setCursor();
1069 m_view->ShowPreview( true );
1070 }
1071 else
1072 {
1073 commit.Add( image );
1074 commit.Push( _( "Place Image" ) );
1075
1077
1078 image = nullptr;
1080
1081 m_view->ClearPreview();
1082
1083 if( immediateMode )
1084 break;
1085 }
1086 }
1087 else if( evt->IsClick( BUT_RIGHT ) )
1088 {
1089 // Warp after context menu only if dragging...
1090 if( !image )
1091 m_toolMgr->VetoContextMenuMouseWarp();
1092
1093 m_menu->ShowContextMenu( selectionTool->GetSelection() );
1094 }
1095 else if( image && ( evt->IsAction( &ACTIONS::refreshPreview )
1096 || evt->IsMotion() ) )
1097 {
1098 image->SetPosition( cursorPos );
1099 m_view->ClearPreview();
1100 m_view->AddToPreview( image, false ); // Add, but not give ownership
1101 m_view->RecacheAllItems(); // Bitmaps are cached in Opengl
1102 }
1103 else if( image && evt->IsAction( &ACTIONS::doDelete ) )
1104 {
1105 cleanup();
1106 }
1107 else if( image && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
1108 || evt->IsAction( &ACTIONS::redo ) ) )
1109 {
1110 wxBell();
1111 }
1112 else
1113 {
1114 evt->SetPassEvent();
1115 }
1116
1117 // Enable autopanning and cursor capture only when there is an image to be placed
1118 getViewControls()->SetAutoPan( image != nullptr );
1119 getViewControls()->CaptureCursor( image != nullptr );
1120 }
1121
1122 getViewControls()->SetAutoPan( false );
1123 getViewControls()->CaptureCursor( false );
1124 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1125
1126 return 0;
1127}
1128
1130{
1132 m_drawingTool( aDrawingTool ),
1133 m_frame( aFrame ),
1134 m_gridHelper( aDrawingTool.GetManager(), aFrame.GetMagneticItemsSettings() )
1135 {
1136 }
1137
1138 std::unique_ptr<BOARD_ITEM> CreateItem() override
1139 {
1140 std::unique_ptr<PCB_POINT> new_point = std::make_unique<PCB_POINT>( m_frame.GetModel() );
1141
1142 PCB_LAYER_ID layer = m_frame.GetActiveLayer();
1143 new_point->SetLayer( layer );
1144
1145 return new_point;
1146 }
1147
1148 void SnapItem( BOARD_ITEM* aItem ) override
1149 {
1150 m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
1151 m_gridHelper.SetUseGrid( !( m_modifiers & MD_CTRL ) );
1152
1153 KIGFX::VIEW_CONTROLS& viewControls = *m_drawingTool.GetManager()->GetViewControls();
1154 const VECTOR2I position = viewControls.GetMousePosition();
1155 VECTOR2I cursorPos = m_gridHelper.ResolveSnap( position, aItem->GetLayerSet() ).position;
1156
1157 viewControls.ForceCursorPosition( true, cursorPos );
1158 aItem->SetPosition( cursorPos );
1159 }
1160
1164};
1165
1166
1168{
1169 if( m_isFootprintEditor && !m_frame->GetModel() )
1170 return 0;
1171
1172 if( m_inDrawingTool )
1173 return 0;
1174
1176
1177 POINT_PLACER placer( *this, *frame() );
1178 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::MD_POINT );
1179
1180 doInteractiveItemPlacement( aEvent, &placer, _( "Place point" ), IPO_REPEAT | IPO_SINGLE_CLICK );
1181
1182 return 0;
1183}
1184
1185
1187{
1188 if( m_isFootprintEditor && !m_frame->GetModel() )
1189 return 0;
1190
1191 if( m_inDrawingTool )
1192 return 0;
1193
1195
1196 COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
1197 PCB_TEXT* text = nullptr;
1198 bool ignorePrimePosition = false;
1199 const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
1200 BOARD_COMMIT commit( m_frame );
1201 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TEXT );
1202 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
1203
1204 auto setCursor =
1205 [&]()
1206 {
1207 if( text )
1208 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
1209 else
1210 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
1211 };
1212
1213 auto cleanup =
1214 [&]()
1215 {
1216 m_toolMgr->RunAction( ACTIONS::selectionClear );
1217 m_controls->ForceCursorPosition( false );
1218 m_controls->ShowCursor( true );
1219 m_controls->SetAutoPan( false );
1220 m_controls->CaptureCursor( false );
1221 delete text;
1222 text = nullptr;
1223 };
1224
1225 m_toolMgr->RunAction( ACTIONS::selectionClear );
1226
1227 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
1228 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
1229
1230 Activate();
1231 // Must be done after Activate() so that it gets set into the correct context
1232 m_controls->ShowCursor( true );
1233 m_controls->ForceCursorPosition( false );
1234 // do not capture or auto-pan until we start placing some text
1235 // Set initial cursor
1236 setCursor();
1237
1238 if( aEvent.HasPosition() )
1239 {
1240 m_toolMgr->PrimeTool( aEvent.Position() );
1241 }
1242 else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
1243 {
1244 m_toolMgr->PrimeTool( { 0, 0 } );
1245 ignorePrimePosition = true;
1246 }
1247
1248 // Main loop: keep receiving events
1249 while( TOOL_EVENT* evt = Wait() )
1250 {
1251 setCursor();
1252
1253 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1254 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1255 VECTOR2I cursorPos = m_controls->GetMousePosition();
1256
1257 // Re-evaluate every iteration: the active layer can be changed mid-tool
1258 const LSET snapLayers = { m_frame->GetActiveLayer() };
1259
1260 cursorPos = GetClampedCoords( grid.ResolveSnap( cursorPos, snapLayers, GRID_TEXT ).position, COORDS_PADDING );
1261 m_controls->ForceCursorPosition( true, cursorPos );
1262
1263 if( evt->IsDrag() )
1264 {
1265 continue;
1266 }
1267 else if( evt->IsCancelInteractive() || ( text && evt->IsAction( &ACTIONS::undo ) ) )
1268 {
1269 if( text )
1270 cleanup();
1271 else
1272 break;
1273 }
1274 else if( evt->IsActivate() )
1275 {
1276 if( text )
1277 cleanup();
1278
1279 if( evt->IsMoveTool() )
1280 {
1281 // Make sure we come back after the move tool is done
1282 m_frame->PushTool( originalEvent );
1283 }
1284
1285 break;
1286 }
1287 else if( evt->IsClick( BUT_RIGHT ) )
1288 {
1289 if( !text )
1290 m_toolMgr->VetoContextMenuMouseWarp();
1291
1292 m_menu->ShowContextMenu( selection() );
1293 }
1294 else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACTIONS::cursorClick ) )
1295 {
1296 bool placing = text != nullptr;
1297
1298 if( !text )
1299 {
1300 m_toolMgr->RunAction( ACTIONS::selectionClear );
1301
1302 m_controls->ForceCursorPosition( true, m_controls->GetCursorPosition() );
1303
1304 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
1305 TEXT_ATTRIBUTES textAttrs;
1306
1307 textAttrs.m_Size = bds.GetTextSize( layer );
1308 textAttrs.m_StrokeWidth = bds.GetTextThickness( layer );
1309 textAttrs.m_Italic = bds.GetTextItalic( layer );
1310 textAttrs.m_KeepUpright = bds.GetTextUpright( layer );
1311 textAttrs.m_Mirrored = m_board->IsBackLayer( layer );
1312 textAttrs.m_Halign = GR_TEXT_H_ALIGN_LEFT;
1313 textAttrs.m_Valign = GR_TEXT_V_ALIGN_BOTTOM;
1314
1316 text = new PCB_TEXT( static_cast<FOOTPRINT*>( m_frame->GetModel() ) );
1317 else
1318 text = new PCB_TEXT( m_frame->GetModel() );
1319
1320 text->SetLayer( layer );
1321 text->SetAttributes( textAttrs );
1322 text->SetTextPos( cursorPos );
1323 text->SetFlags( IS_NEW ); // Prevent double undo commits
1324
1325 DIALOG_TEXT_PROPERTIES textDialog( m_frame, text );
1326 bool cancelled;
1327
1329 [&]()
1330 {
1331 // QuasiModal required for Scintilla auto-complete
1332 cancelled = textDialog.ShowQuasiModal() != wxID_OK;
1333 } );
1334
1335 if( cancelled || NoPrintableChars( text->GetText() ) )
1336 {
1337 delete text;
1338 text = nullptr;
1339 }
1340 else if( text->GetTextPos() != cursorPos )
1341 {
1342 // If the user modified the location then go ahead and place it there.
1343 // Otherwise we'll drag.
1344 placing = true;
1345 }
1346
1347 if( text )
1348 {
1349 if( !m_view->IsLayerVisible( text->GetLayer() ) )
1350 {
1351 m_frame->GetAppearancePanel()->SetLayerVisible( text->GetLayer(), true );
1352 m_frame->GetCanvas()->Refresh();
1353 }
1354
1355 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, text );
1356 m_view->Update( &selection() );
1357
1358 // update the cursor so it looks correct before another event
1359 setCursor();
1360 }
1361 }
1362
1363 if( placing )
1364 {
1365 text->ClearFlags();
1366 m_toolMgr->RunAction( ACTIONS::selectionClear );
1367
1368 commit.Add( text );
1369 commit.Push( _( "Draw Text" ) );
1370
1371 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, text );
1372
1373 text = nullptr;
1374 }
1375
1376 m_controls->ForceCursorPosition( false );
1377
1378 // If we started with a hotkey which has a position then warp back to that.
1379 // Otherwise update to the current mouse position pinned inside the autoscroll
1380 // boundaries.
1381 if( evt->IsPrime() && !ignorePrimePosition )
1382 {
1383 cursorPos = evt->Position();
1384 m_controls->WarpMouseCursor( cursorPos, true );
1385 }
1386 else
1387 {
1388 m_controls->PinCursorInsideNonAutoscrollArea( true );
1389 cursorPos = m_controls->GetMousePosition();
1390 }
1391
1393
1394 m_controls->ShowCursor( true );
1395 m_controls->CaptureCursor( text != nullptr );
1396 m_controls->SetAutoPan( text != nullptr );
1397 }
1398 else if( text && ( evt->IsMotion() || evt->IsAction( &PCB_ACTIONS::refreshPreview ) ) )
1399 {
1400 text->SetPosition( cursorPos );
1401 selection().SetReferencePoint( cursorPos );
1402 m_view->Update( &selection() );
1403 }
1404 else if( text && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
1405 || evt->IsAction( &ACTIONS::redo ) ) )
1406 {
1407 wxBell();
1408 }
1409 else if( text && evt->IsAction( &PCB_ACTIONS::properties ) )
1410 {
1411 frame()->OnEditItemRequest( text );
1412 m_view->Update( &selection() );
1413 frame()->SetMsgPanel( text );
1414 }
1415 else
1416 {
1417 evt->SetPassEvent();
1418 }
1419 }
1420
1421 m_controls->SetAutoPan( false );
1422 m_controls->CaptureCursor( false );
1423 m_controls->ForceCursorPosition( false );
1424 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1425
1426 if( selection().Empty() )
1427 m_frame->SetMsgPanel( board() );
1428
1429 return 0;
1430}
1431
1432
1434{
1435 if( m_inDrawingTool )
1436 return 0;
1437
1439
1440 PCB_TABLE* table = nullptr;
1441 const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
1442 BOARD_COMMIT commit( m_frame );
1443 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TABLE );
1444 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
1445
1446 // We might be running as the same shape in another co-routine. Make sure that one
1447 // gets whacked.
1448 m_toolMgr->DeactivateTool();
1449
1450 auto setCursor =
1451 [&]()
1452 {
1453 if( table )
1454 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
1455 else
1456 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1457 };
1458
1459 auto cleanup =
1460 [&] ()
1461 {
1462 m_toolMgr->RunAction( ACTIONS::selectionClear );
1463 m_controls->ForceCursorPosition( false );
1464 m_controls->ShowCursor( true );
1465 m_controls->SetAutoPan( false );
1466 m_controls->CaptureCursor( false );
1467 delete table;
1468 table = nullptr;
1469 };
1470
1471 m_toolMgr->RunAction( ACTIONS::selectionClear );
1472
1473
1474 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
1475 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
1476
1477 Activate();
1478 // Must be done after Activate() so that it gets set into the correct context
1479 getViewControls()->ShowCursor( true );
1480 m_controls->ForceCursorPosition( false );
1481 // Set initial cursor
1482 setCursor();
1483
1484 if( aEvent.HasPosition() )
1485 m_toolMgr->PrimeTool( aEvent.Position() );
1486
1487 // Main loop: keep receiving events
1488 while( TOOL_EVENT* evt = Wait() )
1489 {
1490 setCursor();
1491 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1492 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1493 VECTOR2I cursorPos = m_controls->GetMousePosition();
1494
1495 // Re-evaluate every iteration: the active layer can be changed mid-tool
1496 const LSET snapLayers = { m_frame->GetActiveLayer() };
1497
1498 cursorPos = GetClampedCoords( grid.ResolveSnap( cursorPos, snapLayers, GRID_TEXT ).position, COORDS_PADDING );
1499 m_controls->ForceCursorPosition( true, cursorPos );
1500
1501 if( evt->IsDrag() )
1502 {
1503 continue;
1504 }
1505 else if( evt->IsCancelInteractive() || ( table && evt->IsAction( &ACTIONS::undo ) ) )
1506 {
1507 if( table )
1508 cleanup();
1509 else
1510 break;
1511 }
1512 else if( evt->IsActivate() )
1513 {
1514 if( table )
1515 cleanup();
1516
1517 if( evt->IsMoveTool() )
1518 {
1519 // Make sure we come back after the move tool is done
1520 m_frame->PushTool( originalEvent );
1521 }
1522
1523 break;
1524 }
1525 else if( evt->IsClick( BUT_RIGHT ) )
1526 {
1527 // Warp after context menu only if dragging...
1528 if( !table )
1529 m_toolMgr->VetoContextMenuMouseWarp();
1530
1531 m_menu->ShowContextMenu( selection() );
1532 }
1533 else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACTIONS::cursorClick ) )
1534 {
1535 if( !table )
1536 {
1537 m_toolMgr->RunAction( ACTIONS::selectionClear );
1538
1539 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
1540
1541 table = new PCB_TABLE( m_frame->GetModel(), bds.GetLineThickness( layer ) );
1542 table->SetFlags( IS_NEW );
1543 table->SetLayer( layer );
1544 table->SetColCount( 1 );
1545 table->AddCell( new PCB_TABLECELL( table ) );
1546
1547 table->SetLayer( layer );
1548 table->SetPosition( cursorPos );
1549
1550 if( !m_view->IsLayerVisible( layer ) )
1551 {
1552 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1553 m_frame->GetCanvas()->Refresh();
1554 }
1555
1557 m_view->Update( &selection() );
1558
1559 // update the cursor so it looks correct before another event
1560 setCursor();
1561 }
1562 else
1563 {
1564 m_toolMgr->RunAction( ACTIONS::selectionClear );
1565
1566 table->Normalize();
1567
1569 bool cancelled;
1570
1572 [&]()
1573 {
1574 // QuasiModal required for Scintilla auto-complete
1575 cancelled = dlg.ShowQuasiModal() != wxID_OK;
1576 } );
1577
1578 if( cancelled )
1579 {
1580 delete table;
1581 }
1582 else
1583 {
1584 commit.Add( table, m_frame->GetScreen() );
1585 commit.Push( _( "Draw Table" ) );
1586
1589 }
1590
1591 table = nullptr;
1592 }
1593 }
1594 else if( table && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
1595 {
1596 VECTOR2I fontSize = bds.GetTextSize( table->GetLayer() );
1597 VECTOR2I gridSize = grid.GetGridSize( grid.GetItemGrid( table ) );
1598 VECTOR2I origin( table->GetPosition() );
1599 VECTOR2I requestedSize( cursorPos - origin );
1600
1601 int colCount = std::max( 1, requestedSize.x / ( fontSize.x * 15 ) );
1602 int rowCount = std::max( 1, requestedSize.y / ( fontSize.y * 3 ) );
1603
1604 VECTOR2I cellSize( std::max( fontSize.x * 5, requestedSize.x / colCount ),
1605 std::max( fontSize.y * 3, requestedSize.y / rowCount ) );
1606
1607 cellSize.x = KiROUND( (double) cellSize.x / gridSize.x ) * gridSize.x;
1608 cellSize.y = KiROUND( (double) cellSize.y / gridSize.y ) * gridSize.y;
1609
1610 table->ClearCells();
1611 table->SetColCount( colCount );
1612
1613 for( int col = 0; col < colCount; ++col )
1614 table->SetColWidth( col, cellSize.x );
1615
1616 for( int row = 0; row < rowCount; ++row )
1617 {
1618 table->SetRowHeight( row, cellSize.y );
1619
1620 for( int col = 0; col < colCount; ++col )
1621 {
1622 PCB_TABLECELL* cell = new PCB_TABLECELL( table );
1623 cell->SetPosition( origin + VECTOR2I( col * cellSize.x, row * cellSize.y ) );
1624 cell->SetEnd( cell->GetPosition() + cellSize );
1625 table->AddCell( cell );
1626 }
1627 }
1628
1629 selection().SetReferencePoint( cursorPos );
1630 m_view->Update( &selection() );
1631 m_frame->SetMsgPanel( table );
1632 }
1633 else if( table && evt->IsAction( &PCB_ACTIONS::properties ) )
1634 {
1635 frame()->OnEditItemRequest( table );
1636 m_view->Update( &selection() );
1637 frame()->SetMsgPanel( table );
1638 }
1639 else if( table && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
1640 || evt->IsAction( &ACTIONS::redo ) ) )
1641 {
1642 wxBell();
1643 }
1644 else
1645 {
1646 evt->SetPassEvent();
1647 }
1648
1649 // Enable autopanning and cursor capture only when there is a shape being drawn
1650 getViewControls()->SetAutoPan( table != nullptr );
1651 getViewControls()->CaptureCursor( table != nullptr );
1652 }
1653
1654 getViewControls()->SetAutoPan( false );
1655 getViewControls()->CaptureCursor( false );
1656 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1657 return 0;
1658}
1659
1660
1662{
1663 const VECTOR2I lineVector{ aDim->GetEnd() - aDim->GetStart() };
1664
1665 aDim->SetEnd( aDim->GetStart() + GetVectorSnapped45( lineVector ) );
1666 aDim->Update();
1667}
1668
1670{
1671 if( m_inDrawingTool )
1672 return 0;
1673
1675
1676 PCB_BARCODE* barcode = nullptr;
1677 BOARD_COMMIT commit( m_frame );
1678 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::BARCODE );
1679 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
1680 const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
1681
1682 auto setCursor =
1683 [&]()
1684 {
1685 if( barcode )
1686 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
1687 else
1688 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
1689 };
1690
1691 auto cleanup =
1692 [&]()
1693 {
1694 m_toolMgr->RunAction( ACTIONS::selectionClear );
1695 m_controls->ForceCursorPosition( false );
1696 m_controls->ShowCursor( true );
1697 m_controls->SetAutoPan( false );
1698 m_controls->CaptureCursor( false );
1699 delete barcode;
1700 barcode = nullptr;
1701 };
1702
1703 m_toolMgr->RunAction( ACTIONS::selectionClear );
1704
1705 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
1706 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
1707
1708 Activate();
1709 // Must be done after Activate() so that it gets set into the correct context
1710 getViewControls()->ShowCursor( true );
1711 m_controls->ForceCursorPosition( false );
1712 setCursor();
1713
1714 if( aEvent.HasPosition() )
1715 m_toolMgr->PrimeTool( aEvent.Position() );
1716
1717 while( TOOL_EVENT* evt = Wait() )
1718 {
1719 setCursor();
1720
1721 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1722 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1723 VECTOR2I cursorPos = m_controls->GetMousePosition();
1724
1725 // Re-evaluate every iteration: the active layer can be changed mid-tool
1726 const LSET snapLayers = { m_frame->GetActiveLayer() };
1727
1728 cursorPos = GetClampedCoords( grid.ResolveSnap( cursorPos, snapLayers, GRID_TEXT ).position, COORDS_PADDING );
1729 m_controls->ForceCursorPosition( true, cursorPos );
1730
1731 if( evt->IsDrag() )
1732 {
1733 continue;
1734 }
1735 else if( evt->IsCancelInteractive() || ( barcode && evt->IsAction( &ACTIONS::undo ) ) )
1736 {
1737 if( barcode )
1738 cleanup();
1739 else
1740 break;
1741 }
1742 else if( evt->IsActivate() )
1743 {
1744 if( barcode )
1745 cleanup();
1746
1747 if( evt->IsMoveTool() )
1748 {
1749 // Make sure we come back after the move tool is done
1750 m_frame->PushTool( originalEvent );
1751 }
1752
1753 break;
1754 }
1755 else if( evt->IsClick( BUT_RIGHT ) )
1756 {
1757 if( !barcode )
1758 m_toolMgr->VetoContextMenuMouseWarp();
1759
1760 m_menu->ShowContextMenu( selection() );
1761 }
1762 else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACTIONS::cursorClick ) )
1763 {
1764 m_toolMgr->RunAction( ACTIONS::selectionClear );
1765
1766 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
1767
1768 barcode = new PCB_BARCODE( m_frame->GetModel() );
1769 barcode->SetFlags( IS_NEW );
1770 barcode->SetLayer( layer );
1771 barcode->SetPosition( cursorPos );
1772 barcode->SetTextSize( bds.GetTextSize( layer ).y );
1773
1774 DIALOG_BARCODE_PROPERTIES dlg( m_frame, barcode );
1775 bool cancelled;
1776
1778 [&]()
1779 {
1780 cancelled = dlg.ShowModal() != wxID_OK;
1781 } );
1782
1783 if( cancelled )
1784 {
1785 delete barcode;
1786 }
1787 else
1788 {
1789 if( !m_view->IsLayerVisible( layer ) )
1790 {
1791 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1792 m_frame->GetCanvas()->Refresh();
1793 }
1794
1795 commit.Add( barcode );
1796 commit.Push( _( "Draw Barcode" ) );
1797
1798 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, barcode );
1799 m_view->Update( &selection() );
1800 }
1801
1802 barcode = nullptr;
1803 }
1804 else
1805 {
1806 evt->SetPassEvent();
1807 }
1808
1809 getViewControls()->SetAutoPan( false );
1810 getViewControls()->CaptureCursor( false );
1811 }
1812
1813 getViewControls()->SetAutoPan( false );
1814 getViewControls()->CaptureCursor( false );
1815 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
1816 return 0;
1817}
1818
1819
1821{
1822 if( m_isFootprintEditor && !m_frame->GetModel() )
1823 return 0;
1824
1825 if( m_inDrawingTool )
1826 return 0;
1827
1829
1830 enum DIMENSION_STEPS
1831 {
1832 SET_ORIGIN = 0,
1833 SET_END,
1834 SET_HEIGHT,
1835 FINISHED
1836 };
1837
1838 PCB_DIMENSION_BASE* dimension = nullptr;
1839 BOARD_COMMIT commit( m_frame );
1840 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
1841 BOARD_DESIGN_SETTINGS& boardSettings = m_board->GetDesignSettings();
1842 PCB_SELECTION preview; // A VIEW_GROUP that serves as a preview for the new item(s)
1843 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DIMENSION );
1844 int step = SET_ORIGIN;
1846
1847 m_view->Add( &preview );
1848
1849 auto cleanup =
1850 [&]()
1851 {
1852 m_controls->SetAutoPan( false );
1853 m_controls->CaptureCursor( false );
1854 m_controls->ForceCursorPosition( false );
1855
1856 preview.Clear();
1857 m_view->Update( &preview );
1858
1859 // Snap guides persist in the grid helper until the tool exits, so abandoning the
1860 // dimension mid-draw must clear them or they linger on screen.
1861 grid.FullReset();
1862
1863 delete dimension;
1864 dimension = nullptr;
1865 step = SET_ORIGIN;
1866 };
1867
1868 auto setCursor =
1869 [&]()
1870 {
1871 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MEASURE );
1872 };
1873
1874 m_toolMgr->RunAction( ACTIONS::selectionClear );
1875
1876 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
1877 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
1878
1879 Activate();
1880 // Must be done after Activate() so that it gets set into the correct context
1881 m_controls->ShowCursor( true );
1882 m_controls->ForceCursorPosition( false );
1883 // Set initial cursor
1884 setCursor();
1885
1886 m_toolMgr->PostAction( ACTIONS::refreshPreview );
1887
1888 if( aEvent.HasPosition() )
1889 m_toolMgr->PrimeTool( aEvent.Position() );
1890
1891 // Main loop: keep receiving events
1892 while( TOOL_EVENT* evt = Wait() )
1893 {
1894 if( step > SET_ORIGIN )
1895 frame()->SetMsgPanel( dimension );
1896
1897 setCursor();
1898
1899 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1900 LEADER_MODE angleSnap = GetAngleSnapMode();
1901
1902 if( evt->Modifier( MD_CTRL ) )
1903 angleSnap = LEADER_MODE::DIRECT;
1904
1905 bool constrained = angleSnap != LEADER_MODE::DIRECT;
1906 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1907
1908 if( step == SET_HEIGHT && t != PCB_DIM_ORTHOGONAL_T )
1909 {
1910 if( dimension->GetStart().x != dimension->GetEnd().x
1911 && dimension->GetStart().y != dimension->GetEnd().y )
1912 {
1913 // Not cardinal. Grid snapping doesn't make sense for height.
1914 grid.SetUseGrid( false );
1915 }
1916 }
1917
1918 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
1919 cursorPos = GetClampedCoords( grid.ResolveSnap( cursorPos, nullptr, GRID_GRAPHICS ).position, COORDS_PADDING );
1920
1921 m_controls->ForceCursorPosition( true, cursorPos );
1922
1923 if( evt->IsCancelInteractive() || ( dimension && evt->IsAction( &ACTIONS::undo ) ) )
1924 {
1925 m_controls->SetAutoPan( false );
1926
1927 if( step != SET_ORIGIN ) // start from the beginning
1928 cleanup();
1929 else
1930 break;
1931 }
1932 else if( evt->IsActivate() )
1933 {
1934 if( step != SET_ORIGIN )
1935 cleanup();
1936
1937 if( evt->IsPointEditor() )
1938 {
1939 // don't exit (the point editor runs in the background)
1940 continue;
1941 }
1942
1943 if( evt->IsMoveTool() )
1944 {
1945 // Make sure we come back after the move tool is done
1946 m_frame->PushTool( originalEvent );
1947 }
1948
1949 break;
1950 }
1951 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) && step != SET_ORIGIN )
1952 {
1953 m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
1954 dimension->SetLineThickness( m_stroke.GetWidth() );
1955 m_view->Update( &preview );
1956 frame()->SetMsgPanel( dimension );
1957 }
1958 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && step != SET_ORIGIN )
1959 {
1960 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
1961 {
1962 m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
1963 dimension->SetLineThickness( m_stroke.GetWidth() );
1964 m_view->Update( &preview );
1965 frame()->SetMsgPanel( dimension );
1966 }
1967 }
1968 else if( evt->IsClick( BUT_RIGHT ) )
1969 {
1970 if( !dimension )
1971 m_toolMgr->VetoContextMenuMouseWarp();
1972
1973 m_menu->ShowContextMenu( selection() );
1974 }
1975 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
1976 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
1977 {
1978 switch( step )
1979 {
1980 case SET_ORIGIN:
1981 {
1982 m_toolMgr->RunAction( ACTIONS::selectionClear );
1983
1984 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
1985
1986 // Init the new item attributes
1987 auto setMeasurementAttributes =
1988 [&]( PCB_DIMENSION_BASE* aDim )
1989 {
1990 aDim->SetUnitsMode( boardSettings.m_DimensionUnitsMode );
1991 aDim->SetUnitsFormat( boardSettings.m_DimensionUnitsFormat );
1992 aDim->SetPrecision( boardSettings.m_DimensionPrecision );
1993 aDim->SetSuppressZeroes( boardSettings.m_DimensionSuppressZeroes );
1994 aDim->SetTextPositionMode( boardSettings.m_DimensionTextPosition );
1995 aDim->SetKeepTextAligned( boardSettings.m_DimensionKeepTextAligned );
1996 };
1997
1998 if( originalEvent.IsAction( &PCB_ACTIONS::drawAlignedDimension ) )
1999 {
2000 dimension = new PCB_DIM_ALIGNED( m_frame->GetModel() );
2001 setMeasurementAttributes( dimension );
2002 }
2003 else if( originalEvent.IsAction( &PCB_ACTIONS::drawOrthogonalDimension ) )
2004 {
2005 dimension = new PCB_DIM_ORTHOGONAL( m_frame->GetModel() );
2006 setMeasurementAttributes( dimension );
2007 }
2008 else if( originalEvent.IsAction( &PCB_ACTIONS::drawCenterDimension ) )
2009 {
2010 dimension = new PCB_DIM_CENTER( m_frame->GetModel() );
2011 }
2012 else if( originalEvent.IsAction( &PCB_ACTIONS::drawRadialDimension ) )
2013 {
2014 dimension = new PCB_DIM_RADIAL( m_frame->GetModel() );
2015 setMeasurementAttributes( dimension );
2016 }
2017 else if( originalEvent.IsAction( &PCB_ACTIONS::drawLeader ) )
2018 {
2019 dimension = new PCB_DIM_LEADER( m_frame->GetModel() );
2020 dimension->SetTextPos( cursorPos );
2021 }
2022 else
2023 {
2024 wxFAIL_MSG( wxT( "Unhandled action in DRAWING_TOOL::DrawDimension" ) );
2025 }
2026
2027 t = dimension->Type();
2028
2029 dimension->SetLayer( layer );
2030 dimension->SetMirrored( m_board->IsBackLayer( layer ) );
2031 dimension->SetTextSize( boardSettings.GetTextSize( layer ) );
2032 dimension->SetTextThickness( boardSettings.GetTextThickness( layer ) );
2033 dimension->SetItalic( boardSettings.GetTextItalic( layer ) );
2034 dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
2035 dimension->SetArrowLength( boardSettings.m_DimensionArrowLength );
2036 dimension->SetExtensionOffset( boardSettings.m_DimensionExtensionOffset );
2037 dimension->SetStart( cursorPos );
2038 dimension->SetEnd( cursorPos );
2039 dimension->Update();
2040
2041 if( !m_view->IsLayerVisible( layer ) )
2042 {
2043 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
2044 m_frame->GetCanvas()->Refresh();
2045 }
2046
2047 preview.Add( dimension );
2048 frame()->SetMsgPanel( dimension );
2049
2050 m_controls->SetAutoPan( true );
2051 m_controls->CaptureCursor( true );
2052 break;
2053 }
2054
2055 case SET_END:
2056 // Dimensions that have origin and end in the same spot are not valid
2057 if( dimension->GetStart() == dimension->GetEnd() )
2058 {
2059 --step;
2060 break;
2061 }
2062
2063 if( t != PCB_DIM_CENTER_T && t != PCB_DIM_RADIAL_T && t != PCB_DIM_LEADER_T )
2064 {
2065 break;
2066 }
2067
2068 ++step;
2070 case SET_HEIGHT:
2071 assert( dimension->GetStart() != dimension->GetEnd() );
2072 assert( dimension->GetLineThickness() > 0 );
2073
2074 preview.Remove( dimension );
2075
2076 commit.Add( dimension );
2077
2078 // Bind feature points to measured geometry so it tracks that geometry interactive draw only
2079 if( GetAutoConstraints() )
2080 bindDimensionEndpoints( m_board, dimension, m_frame->GetModel(), commit );
2081
2082 commit.Push( _( "Draw Dimension" ) );
2083
2084 // Run the edit immediately to set the leader text
2085 if( t == PCB_DIM_LEADER_T )
2086 frame()->OnEditItemRequest( dimension );
2087
2088 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, dimension );
2089
2090 break;
2091 }
2092
2093 if( ++step >= FINISHED )
2094 {
2095 dimension = nullptr;
2096 step = SET_ORIGIN;
2097 m_controls->SetAutoPan( false );
2098 m_controls->CaptureCursor( false );
2099 }
2100 else if( evt->IsDblClick( BUT_LEFT ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
2101 {
2102 m_toolMgr->PostAction( PCB_ACTIONS::cursorClick );
2103 }
2104 }
2105 else if( evt->IsMotion() || evt->IsAction( &ACTIONS::refreshPreview ) )
2106 {
2107 switch( step )
2108 {
2109 case SET_END:
2110 dimension->SetEnd( cursorPos );
2111
2112 if( constrained || t == PCB_DIM_CENTER_T )
2113 constrainDimension( dimension );
2114
2115 if( t == PCB_DIM_ORTHOGONAL_T )
2116 {
2117 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
2118
2119 BOX2I bounds( dimension->GetStart(),
2120 dimension->GetEnd() - dimension->GetStart() );
2121
2122 // Create a nice preview by measuring the longer dimension
2123 bool vert = bounds.GetWidth() < bounds.GetHeight();
2124
2125 ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
2127 }
2128 else if( t == PCB_DIM_RADIAL_T )
2129 {
2130 PCB_DIM_RADIAL* radialDim = static_cast<PCB_DIM_RADIAL*>( dimension );
2131 VECTOR2I textOffset( radialDim->GetArrowLength() * 10, 0 );
2132
2133 if( radialDim->GetEnd().x < radialDim->GetStart().x )
2134 textOffset = -textOffset;
2135
2136 radialDim->SetTextPos( radialDim->GetKnee() + textOffset );
2137 }
2138 else if( t == PCB_DIM_LEADER_T )
2139 {
2140 VECTOR2I textOffset( dimension->GetArrowLength() * 10, 0 );
2141
2142 if( dimension->GetEnd().x < dimension->GetStart().x )
2143 textOffset = -textOffset;
2144
2145 dimension->SetTextPos( dimension->GetEnd() + textOffset );
2146 }
2147
2148 dimension->Update();
2149 break;
2150
2151 case SET_HEIGHT:
2152 if( t == PCB_DIM_ALIGNED_T )
2153 {
2154 PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dimension );
2155
2156 // Calculating the direction of travel perpendicular to the selected axis
2157 double angle = aligned->GetAngle() + ( M_PI / 2 );
2158
2159 VECTOR2I delta( (VECTOR2I) cursorPos - dimension->GetEnd() );
2160 double height = ( delta.x * cos( angle ) ) + ( delta.y * sin( angle ) );
2161 aligned->SetHeight( height );
2162 aligned->Update();
2163 }
2164 else if( t == PCB_DIM_ORTHOGONAL_T )
2165 {
2166 PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
2167
2168 BOX2I bbox( dimension->GetStart(),
2169 dimension->GetEnd() - dimension->GetStart() );
2170 VECTOR2I direction( cursorPos - bbox.Centre() );
2171 bool vert;
2172
2173 // Only change the orientation when we move outside the bbox
2174 if( !bbox.Contains( cursorPos ) )
2175 {
2176 // If the dimension is horizontal or vertical, set correct orientation
2177 // otherwise, test if we're left/right of the bounding box or above/below it
2178 if( bbox.GetWidth() == 0 )
2179 vert = true;
2180 else if( bbox.GetHeight() == 0 )
2181 vert = false;
2182 else if( cursorPos.x > bbox.GetLeft() && cursorPos.x < bbox.GetRight() )
2183 vert = false;
2184 else if( cursorPos.y > bbox.GetTop() && cursorPos.y < bbox.GetBottom() )
2185 vert = true;
2186 else
2187 vert = std::abs( direction.y ) < std::abs( direction.x );
2188
2189 ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
2191 }
2192 else
2193 {
2194 vert = ortho->GetOrientation() == PCB_DIM_ORTHOGONAL::DIR::VERTICAL;
2195 }
2196
2197 VECTOR2I heightVector( cursorPos - dimension->GetStart() );
2198 ortho->SetHeight( vert ? heightVector.x : heightVector.y );
2199 ortho->Update();
2200 }
2201
2202 break;
2203 }
2204
2205 // Show a preview of the item
2206 m_view->Update( &preview );
2207 }
2208 else if( dimension && evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2209 {
2210 PCB_LAYER_ID layer = m_frame->GetActiveLayer();
2211
2212 if( !m_view->IsLayerVisible( layer ) )
2213 {
2214 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
2215 m_frame->GetCanvas()->Refresh();
2216 }
2217
2218 dimension->SetLayer( layer );
2219 dimension->SetTextSize( boardSettings.GetTextSize( layer ) );
2220 dimension->SetTextThickness( boardSettings.GetTextThickness( layer ) );
2221 dimension->SetItalic( boardSettings.GetTextItalic( layer ) );
2222 dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
2223 dimension->Update();
2224
2225 m_view->Update( &preview );
2226 frame()->SetMsgPanel( dimension );
2227 }
2228 else if( dimension && evt->IsAction( &PCB_ACTIONS::properties ) )
2229 {
2230 if( step == SET_END || step == SET_HEIGHT )
2231 {
2232 frame()->OnEditItemRequest( dimension );
2233 dimension->Update();
2234 frame()->SetMsgPanel( dimension );
2235 break;
2236 }
2237 else
2238 {
2239 wxBell();
2240 }
2241 }
2242 else if( dimension && evt->IsAction( &PCB_ACTIONS::changeDimensionArrows ) )
2243 {
2244 switch( dimension->Type() )
2245 {
2246 case PCB_DIM_ALIGNED_T:
2248 case PCB_DIM_RADIAL_T:
2251 else
2253 break;
2254 default:
2255 // Other dimension types don't have arrows that can swap
2256 wxBell();
2257 }
2258
2259 m_view->Update( &preview );
2260 }
2261 else if( dimension && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
2262 || evt->IsAction( &ACTIONS::redo ) ) )
2263 {
2264 wxBell();
2265 }
2266 else
2267 {
2268 evt->SetPassEvent();
2269 }
2270 }
2271
2272 if( step != SET_ORIGIN )
2273 delete dimension;
2274
2275 m_controls->SetAutoPan( false );
2276 m_controls->ForceCursorPosition( false );
2277 m_controls->CaptureCursor( false );
2278 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2279
2280 m_view->Remove( &preview );
2281
2282 if( selection().Empty() )
2283 m_frame->SetMsgPanel( board() );
2284
2285 return 0;
2286}
2287
2288
2290{
2291 if( m_inDrawingTool )
2292 return 0;
2293
2295
2296 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
2297 const PCB_SELECTION& selection = selTool->GetSelection();
2298
2299 if( selection.Size() != 1 || selection[0]->Type() != PCB_REFERENCE_IMAGE_T )
2300 return 0;
2301
2303 REFERENCE_IMAGE& refImage = image->GetReferenceImage();
2304 BITMAP_BASE& bitmap = refImage.MutableImage();
2305
2306 if( !bitmap.GetImageData() )
2307 return 0;
2308
2309 const wxImage source = *bitmap.GetImageData();
2310
2311 std::vector<PCB_LAYER_ID> layerIds;
2312 wxArrayString layerNames;
2313 int selectedLayer = 0;
2314
2315 for( PCB_LAYER_ID layer : m_board->GetEnabledLayers().UIOrder() )
2316 {
2317 if( layer == image->GetLayer() )
2318 selectedLayer = (int) layerIds.size();
2319
2320 layerIds.push_back( layer );
2321 layerNames.Add( m_board->GetLayerName( layer ) );
2322 }
2323
2324 auto showPreview = [&]( double aThreshold, bool aInvert )
2325 {
2326 bitmap.BeginPreviewImage( ConvertImageToBilevel( source, aThreshold, aInvert ) );
2327 m_view->Update( image );
2328 m_frame->GetCanvas()->ForceRefresh();
2329 };
2330
2331 DIALOG_CONVERT_IMAGE_TO_OUTLINES dialog( m_frame, layerNames, selectedLayer, showPreview );
2332
2333 showPreview( dialog.GetThreshold(), dialog.GetInvert() );
2334
2335 int result = dialog.ShowModal();
2336
2337 bitmap.EndPreviewImage();
2338 m_view->Update( image );
2339
2340 if( result != wxID_OK )
2341 {
2342 m_frame->GetCanvas()->ForceRefresh();
2343 return 0;
2344 }
2345
2346 wxImage bilevel = ConvertImageToBilevel( source, dialog.GetThreshold(), dialog.GetInvert() );
2347
2348 VECTOR2I size = refImage.GetSize();
2349 double scaleX = (double) size.x / bilevel.GetWidth();
2350 double scaleY = (double) size.y / bilevel.GetHeight();
2351
2353 SHAPE_POLY_SET traced = TraceBilevelImage( bilevel, scaleX, scaleY, reporter );
2354 wxString warnings = reporter.GetMessages();
2355
2356 warnings.Trim();
2357
2358 const int icon = warnings.IsEmpty() ? wxICON_INFORMATION : wxICON_WARNING;
2359
2360 if( traced.IsEmpty() )
2361 {
2362 wxString message = warnings.IsEmpty() ? _( "No shapes to trace at this threshold." ) : warnings;
2363
2364 m_frame->GetCanvas()->ForceRefresh();
2365 m_frame->GetInfoBar()->ShowMessageFor( message, 4000, icon );
2366 return 0;
2367 }
2368
2369 // Images are drawn rotated, so the traced outlines need to be moved and rotated accordingly.
2370 VECTOR2I origin = refImage.GetPosition() - size / 2;
2371
2372 traced.Move( origin );
2373 traced.Rotate( refImage.GetOrientation(), refImage.GetPosition() );
2374
2375 PCB_LAYER_ID layer = layerIds[dialog.GetLayerSelection()];
2376
2377 BOARD_COMMIT commit( m_frame );
2378 PCB_GROUP* group = new PCB_GROUP( m_frame->GetModel() );
2379
2380 for( int ii = 0; ii < traced.OutlineCount(); ii++ )
2381 {
2382 SHAPE_LINE_CHAIN outline = traced.Outline( ii );
2383
2384 PCB_SHAPE* shape = new PCB_SHAPE( m_frame->GetModel() );
2385
2386 shape->SetShape( SHAPE_T::POLY );
2387 shape->SetFilled( true );
2388 shape->SetLayer( layer );
2389 shape->SetPolyShape( outline );
2390 shape->SetStroke( STROKE_PARAMS( 0 ) );
2391
2392 group->AddItem( shape );
2393 commit.Add( shape );
2394 }
2395
2396 commit.Remove( image );
2397 commit.Add( group );
2398 commit.Push( _( "Convert Image to Outlines" ) );
2399
2400 wxString message = wxString::Format( wxPLURAL( "Added %d shape.", "Added %d shapes.", traced.OutlineCount() ),
2401 traced.OutlineCount() );
2402
2403 if( !warnings.IsEmpty() )
2404 message << wxS( " " ) << warnings;
2405
2406 m_frame->GetInfoBar()->ShowMessageFor( message, 4000, icon );
2407
2408 return 0;
2409}
2410
2411
2413{
2414 if( !m_frame->GetModel() )
2415 return 0;
2416
2417 if( m_inDrawingTool )
2418 return 0;
2419
2421
2423
2424 // Set filename on drag-and-drop
2425 if( aEvent.HasParameter() )
2426 dlg.SetFilenameOverride( *aEvent.Parameter<wxString*>() );
2427
2428 int dlgResult = dlg.ShowModal();
2429
2430 std::list<std::unique_ptr<EDA_ITEM>>& list = dlg.GetImportedItems();
2431
2432 if( dlgResult != wxID_OK )
2433 return 0;
2434
2435 // Ensure the list is not empty:
2436 if( list.empty() )
2437 {
2438 wxMessageBox( _( "No graphic items found in file.") );
2439 return 0;
2440 }
2441
2443
2444 std::vector<BOARD_ITEM*> newItems; // all new items, including group
2445 std::vector<BOARD_ITEM*> selectedItems; // the group, or newItems if no group
2446 PCB_SELECTION preview;
2447 BOARD_COMMIT commit( m_frame );
2448 PCB_GROUP* group = nullptr;
2449 PCB_LAYER_ID layer = F_Cu;
2450
2451 if( dlg.ShouldGroupItems() )
2452 {
2453 size_t boardItemCount = std::count_if( list.begin(), list.end(),
2454 []( const std::unique_ptr<EDA_ITEM>& ptr )
2455 {
2456 return ptr->IsBOARD_ITEM();
2457 } );
2458
2459 if( boardItemCount >= 2 )
2460 {
2461 group = new PCB_GROUP( m_frame->GetModel() );
2462
2463 newItems.push_back( group );
2464 selectedItems.push_back( group );
2465 preview.Add( group );
2466 }
2467 }
2468
2469 if( dlg.ShouldFixDiscontinuities() )
2470 {
2471 std::vector<PCB_SHAPE*> shapeList;
2472
2473 for( const std::unique_ptr<EDA_ITEM>& ptr : list )
2474 {
2475 if( PCB_SHAPE* shape = dynamic_cast<PCB_SHAPE*>( ptr.get() ) )
2476 shapeList.push_back( shape );
2477 }
2478
2479 ConnectBoardShapes( shapeList, dlg.GetTolerance() );
2480 }
2481
2482 for( std::unique_ptr<EDA_ITEM>& ptr : list )
2483 {
2484 EDA_ITEM* eda_item = ptr.release();
2485
2486 if( eda_item->IsBOARD_ITEM() )
2487 {
2488 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( eda_item );
2489
2490 newItems.push_back( item );
2491
2492 if( group )
2493 group->AddItem( item );
2494 else
2495 selectedItems.push_back( item );
2496
2497 layer = item->GetLayer();
2498 }
2499
2500 preview.Add( eda_item );
2501 }
2502
2503 // Clear the current selection then select the drawings so that edit tools work on them
2504 m_toolMgr->RunAction( ACTIONS::selectionClear );
2505
2506 EDA_ITEMS selItems( selectedItems.begin(), selectedItems.end() );
2507 m_toolMgr->RunAction<EDA_ITEMS*>( ACTIONS::selectItems, &selItems );
2508
2509 if( !dlg.IsPlacementInteractive() )
2510 {
2511 for( BOARD_ITEM* item : newItems )
2512 commit.Add( item );
2513
2514 commit.Push( _( "Import Graphics" ) );
2515
2516 return 0;
2517 }
2518
2519 // Turn shapes on if they are off, so that the created object will be visible after completion
2520 m_frame->SetObjectVisible( LAYER_FILLED_SHAPES );
2521
2522 if( !m_view->IsLayerVisible( layer ) )
2523 {
2524 m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
2525 m_frame->GetCanvas()->Refresh();
2526 }
2527
2528 m_view->Add( &preview );
2529
2530 SCOPED_TOOL_PUSHER raii( m_frame, aEvent );
2531
2532 auto setCursor =
2533 [&]()
2534 {
2535 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
2536 };
2537
2538 Activate();
2539 // Must be done after Activate() so that it gets set into the correct context
2540 m_controls->ShowCursor( true );
2541 m_controls->ForceCursorPosition( false );
2542 // Set initial cursor
2543 setCursor();
2544
2545 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );
2546 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
2547
2548 // Now move the new items to the current cursor position:
2549 VECTOR2I cursorPos = m_controls->GetCursorPosition( !aEvent.DisableGridSnapping() );
2550 VECTOR2I delta = cursorPos - static_cast<BOARD_ITEM*>( preview.GetTopLeftItem() )->GetPosition();
2551
2552 for( BOARD_ITEM* item : selectedItems )
2553 item->Move( delta );
2554
2555 m_view->Update( &preview );
2556
2557 // Main loop: keep receiving events
2558 while( TOOL_EVENT* evt = Wait() )
2559 {
2560 setCursor();
2561
2562 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2563 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2564 cursorPos = m_controls->GetMousePosition();
2565 cursorPos = GetClampedCoords( grid.ResolveSnap( cursorPos, { layer }, GRID_GRAPHICS ).position, COORDS_PADDING );
2566 m_controls->ForceCursorPosition( true, cursorPos );
2567
2568 if( evt->IsCancelInteractive() || evt->IsActivate() )
2569 {
2570 m_toolMgr->RunAction( ACTIONS::selectionClear );
2571
2572 if( group )
2573 preview.Remove( group );
2574
2575 for( BOARD_ITEM* item : newItems )
2576 delete item;
2577
2578 break;
2579 }
2580 else if( evt->IsMotion() || evt->IsAction( &ACTIONS::refreshPreview ) )
2581 {
2582 delta = cursorPos - preview.GetTopLeftItem()->GetPosition();
2583
2584 for( BOARD_ITEM* item : selectedItems )
2585 item->Move( delta );
2586
2587 m_view->Update( &preview );
2588 }
2589 else if( evt->IsClick( BUT_RIGHT ) )
2590 {
2591 m_menu->ShowContextMenu( selection() );
2592 }
2593 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
2594 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
2595 {
2596 // Place the imported drawings
2597 for( BOARD_ITEM* item : newItems )
2598 commit.Add( item );
2599
2600 commit.Push( _( "Import Graphics" ) );
2601
2602 break; // This is a one-shot command, not a tool
2603 }
2604 else if( ZONE_FILLER_TOOL::IsZoneFillAction( evt ) )
2605 {
2606 wxBell();
2607 }
2608 else
2609 {
2610 evt->SetPassEvent();
2611 }
2612 }
2613
2614 preview.Clear();
2615 m_view->Remove( &preview );
2616
2617 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2618 m_controls->ForceCursorPosition( false );
2619
2620 return 0;
2621}
2622
2623
2625{
2626 // Make sense only in FP editor
2627 if( !m_isFootprintEditor )
2628 return 0;
2629
2630 if( !m_frame->GetModel() )
2631 return 0;
2632
2633 if( m_inDrawingTool )
2634 return 0;
2635
2637
2638 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ANCHOR );
2639 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
2640
2641 m_toolMgr->RunAction( ACTIONS::selectionClear );
2642
2643 SCOPED_TOOL_PUSHER raii( m_frame, aEvent );
2644
2645 auto setCursor =
2646 [&]()
2647 {
2648 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
2649 };
2650
2651 Activate();
2652 // Must be done after Activate() so that it gets set into the correct context
2653 m_controls->ShowCursor( true );
2654 m_controls->SetAutoPan( true );
2655 m_controls->CaptureCursor( false );
2656 m_controls->ForceCursorPosition( false );
2657 // Set initial cursor
2658 setCursor();
2659
2660 while( TOOL_EVENT* evt = Wait() )
2661 {
2662 setCursor();
2663
2664 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2665 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2666 VECTOR2I cursorPos = grid.ResolveSnap( m_controls->GetMousePosition(), LSET::AllLayersMask() ).position;
2667 m_controls->ForceCursorPosition( true, cursorPos );
2668
2669 if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
2670 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
2671 {
2672 FOOTPRINT* footprint = (FOOTPRINT*) m_frame->GetModel();
2673 BOARD_COMMIT commit( m_frame );
2674 commit.Modify( footprint );
2675
2676 // set the new relative internal local coordinates of footprint items
2677 VECTOR2I moveVector = footprint->GetPosition() - cursorPos;
2678 footprint->MoveAnchorPosition( moveVector );
2679
2680 commit.Push( _( "Move Footprint Anchor" ) );
2681
2682 // Usually, we do not need to change twice the anchor position, so exit the tool
2683 break;
2684 }
2685 else if( evt->IsClick( BUT_RIGHT ) )
2686 {
2687 m_menu->ShowContextMenu( selection() );
2688 }
2689 else if( evt->IsCancelInteractive() || evt->IsActivate() )
2690 {
2691 break;
2692 }
2693 else
2694 {
2695 evt->SetPassEvent();
2696 }
2697 }
2698
2699 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2700 m_controls->ForceCursorPosition( false );
2701
2702 return 0;
2703}
2704
2705
2707{
2708 if( !m_frame->GetModel() )
2709 return 0;
2710
2711 if( m_inDrawingTool )
2712 return 0;
2713
2715
2716 enum GRIDITEM_STEPS
2717 {
2718 SET_CENTER = 0,
2719 SET_EXTENT
2720 };
2721
2722 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ANCHOR );
2723 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
2724 int step = SET_CENTER;
2725
2726 m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
2727
2728 // Turn grid items on if they are off, so that the created object will be visible after
2729 // completion
2730 m_frame->SetObjectVisible( LAYER_SUBGRIDS );
2731
2732 SCOPED_TOOL_PUSHER raii( m_frame, aEvent );
2733
2734 auto setCursor =
2735 [&]()
2736 {
2737 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::BULLSEYE );
2738 };
2739
2740 Activate();
2741 // Must be done after Activate() so that it gets set into the correct context
2742 m_controls->ShowCursor( true );
2743 m_controls->SetAutoPan( true );
2744 m_controls->CaptureCursor( false );
2745 m_controls->ForceCursorPosition( false );
2746 // Set initial cursor
2747 setCursor();
2748
2750 PCB_GRID_ITEM* griditem = nullptr;
2751
2752 auto sizeToCursor =
2753 [&]( const VECTOR2I& aCursor )
2754 {
2755 VECTOR2I v = aCursor - griditem->GetPosition();
2756 const int len = KiROUND( v.EuclideanNorm() );
2757
2758 griditem->SetOrientation( -EDA_ANGLE( VECTOR2D( v ) ) );
2759 griditem->SetExtent( VECTOR2I( len, len ) );
2760 };
2761
2762 while( TOOL_EVENT* evt = Wait() )
2763 {
2764 setCursor();
2765
2766 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2767 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2768 VECTOR2I cursorPos = grid.ResolveSnap( m_controls->GetMousePosition(), LSET::AllLayersMask() ).position;
2769 m_controls->ForceCursorPosition( true, cursorPos );
2770
2771 if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
2772 || evt->IsAction( &ACTIONS::cursorClick ) || evt->IsAction( &ACTIONS::cursorDblClick ) )
2773 {
2774 if( step == SET_CENTER )
2775 {
2776 // Only items on the board are picked up trough a GRID_SOURCE
2777 // SetSelected highlights the grid and avoids snapping to itself
2778 griditem = new PCB_GRID_ITEM( board );
2779 griditem->SetPosition( cursorPos );
2780 griditem->SetExtent( VECTOR2I( 0, 0 ) );
2781 griditem->SetSelected();
2782
2783 board->Add( griditem );
2784 view()->Add( griditem );
2785
2786 step = SET_EXTENT;
2787 }
2788 else
2789 {
2790 sizeToCursor( cursorPos );
2791 griditem->ClearSelected();
2792
2793 // Remove the item from the board to add it properly through a commit again
2794 view()->Remove( griditem );
2795 board->Remove( griditem );
2796
2797 BOARD_COMMIT commit( m_frame );
2798 commit.Add( griditem );
2799 commit.Push( _( "Place Sub-grid" ) );
2800
2801 griditem = nullptr;
2802 step = SET_CENTER;
2803 }
2804 }
2805 else if( evt->IsClick( BUT_RIGHT ) )
2806 {
2807 m_menu->ShowContextMenu( selection() );
2808 }
2809 else if( evt->IsCancelInteractive() || evt->IsActivate() || ( griditem && evt->IsAction( &ACTIONS::undo ) ) )
2810 {
2811 bool restart = griditem && !evt->IsActivate();
2812
2813 if( griditem )
2814 {
2815 view()->Remove( griditem );
2816 board->Remove( griditem );
2817 delete griditem;
2818 griditem = nullptr;
2819 }
2820
2821 if( restart )
2822 step = SET_CENTER;
2823 else
2824 break;
2825 }
2826 else if( griditem && ( evt->IsMotion()
2827 || evt->IsAction( &ACTIONS::refreshPreview ) ) )
2828 {
2829 sizeToCursor( cursorPos );
2830 view()->Update( griditem );
2831 }
2832 else
2833 {
2834 evt->SetPassEvent();
2835 }
2836 }
2837
2838 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
2839 m_controls->ForceCursorPosition( false );
2840
2841 return 0;
2842}
2843
2844
2845static VECTOR2I evalEllipsePoint( const PCB_SHAPE* aGraphic, const VECTOR2I& aCursorPos )
2846{
2847 const VECTOR2I center = aGraphic->GetEllipseCenter();
2848 const double a = std::max( 1, aGraphic->GetEllipseMajorRadius() );
2849 const double b = std::max( 1, aGraphic->GetEllipseMinorRadius() );
2850 const EDA_ANGLE rot = aGraphic->GetEllipseRotation();
2851 const double cosRot = rot.Cos();
2852 const double sinRot = rot.Sin();
2853
2854 const double dx = aCursorPos.x - center.x;
2855 const double dy = aCursorPos.y - center.y;
2856 const double lx = dx * cosRot + dy * sinRot;
2857 const double ly = -dx * sinRot + dy * cosRot;
2858
2859 const EDA_ANGLE t( std::atan2( ly / b, lx / a ), RADIANS_T );
2860 const double px = a * t.Cos();
2861 const double py = b * t.Sin();
2862
2863 return center + VECTOR2I( KiROUND( px * cosRot - py * sinRot ), KiROUND( px * sinRot + py * cosRot ) );
2864}
2865
2866
2868{
2869 if( aMgr.IsReset() )
2870 return;
2871
2872 if( aGraphic->GetShape() == SHAPE_T::ELLIPSE || aGraphic->GetShape() == SHAPE_T::ELLIPSE_ARC )
2873 {
2874 const VECTOR2I origin = aMgr.GetOrigin();
2875 const VECTOR2I end = aMgr.GetEnd();
2876 const VECTOR2I center = ( origin + end ) / 2;
2877 const int halfW = std::abs( end.x - origin.x ) / 2;
2878 const int halfH = std::abs( end.y - origin.y ) / 2;
2879
2880 int majorRadius;
2881 int minorRadius;
2882 EDA_ANGLE rotation;
2883
2884 if( halfW >= halfH )
2885 {
2886 majorRadius = std::max( halfW, 1 );
2887 minorRadius = std::max( halfH, 1 );
2888 rotation = ANGLE_0;
2889 }
2890 else
2891 {
2892 majorRadius = std::max( halfH, 1 );
2893 minorRadius = std::max( halfW, 1 );
2894 rotation = ANGLE_90;
2895 }
2896
2897 aGraphic->SetStart( origin );
2898 aGraphic->SetEllipseCenter( center );
2899 aGraphic->SetEllipseMajorRadius( majorRadius );
2900 aGraphic->SetEllipseMinorRadius( minorRadius );
2901 aGraphic->SetEllipseRotation( rotation );
2902
2903 if( aGraphic->GetShape() == SHAPE_T::ELLIPSE_ARC )
2904 {
2905 aGraphic->SetEllipseStartAngle( ANGLE_0 );
2906 aGraphic->SetEllipseEndAngle( ANGLE_360 );
2907 }
2908 }
2909 else
2910 {
2911 aGraphic->SetStart( aMgr.GetOrigin() );
2912 aGraphic->SetEnd( aMgr.GetEnd() );
2913 }
2914}
2915
2916
2917bool DRAWING_TOOL::drawShape( const TOOL_EVENT& aTool, PCB_SHAPE** aGraphic, std::optional<VECTOR2D> aStartingPoint,
2918 std::stack<PCB_SHAPE*>* aCommittedGraphics )
2919{
2920 SHAPE_T shape = ( *aGraphic )->GetShape();
2921
2922 wxASSERT( shape == SHAPE_T::SEGMENT || shape == SHAPE_T::CIRCLE || shape == SHAPE_T::RECTANGLE
2923 || shape == SHAPE_T::ELLIPSE || shape == SHAPE_T::ELLIPSE_ARC );
2924
2925 const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
2926 EDA_UNITS userUnits = m_frame->GetUserUnits();
2927 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
2928 PCB_SHAPE*& graphic = *aGraphic;
2929
2930 if( m_layer != m_frame->GetActiveLayer() )
2931 {
2932 m_layer = m_frame->GetActiveLayer();
2933 m_stroke.SetWidth( bds.GetLineThickness( m_layer ) );
2934 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
2935 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
2936
2937 m_textAttrs.m_Size = bds.GetTextSize( m_layer );
2938 m_textAttrs.m_StrokeWidth = bds.GetTextThickness( m_layer );
2939 m_textAttrs.m_Italic = bds.GetTextItalic( m_layer );
2940 m_textAttrs.m_KeepUpright = bds.GetTextUpright( m_layer );
2941 m_textAttrs.m_Mirrored = m_board->IsBackLayer( m_layer );
2944 }
2945
2946 // Turn shapes on if they are off, so that the created object will be visible after completion
2947 m_frame->SetObjectVisible( LAYER_FILLED_SHAPES );
2948
2949 // geometric construction manager
2951
2952 // drawing assistant overlay
2953 // TODO: workaround because EDA_SHAPE_TYPE_T is not visible from commons.
2954 KIGFX::PREVIEW::GEOM_SHAPE geomShape = ( shape == SHAPE_T::ELLIPSE || shape == SHAPE_T::ELLIPSE_ARC )
2956 : static_cast<KIGFX::PREVIEW::GEOM_SHAPE>( shape );
2957 KIGFX::PREVIEW::TWO_POINT_ASSISTANT twoPointAsst( twoPointMgr, pcbIUScale, userUnits, geomShape );
2958
2959 // Add a VIEW_GROUP that serves as a preview for the new item
2960 m_preview.Clear();
2961 m_view->Add( &m_preview );
2962 m_view->Add( &twoPointAsst );
2963
2964 bool started = false;
2965 bool cancelled = false;
2966 bool multiPhase = false;
2967 PCB_SHAPE* marker = nullptr;
2968 bool isLocalOriginSet = ( m_frame->GetScreen()->m_LocalOrigin != VECTOR2D( 0, 0 ) );
2969 VECTOR2I cursorPos = m_controls->GetMousePosition();
2970
2971 auto setCursor =
2972 [&]()
2973 {
2974 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
2975 };
2976
2977 auto cleanup =
2978 [&]()
2979 {
2980 m_preview.Clear();
2981 m_view->Update( &m_preview );
2982 delete graphic;
2983 graphic = nullptr;
2984
2985 delete marker;
2986 marker = nullptr;
2987
2988 if( !isLocalOriginSet )
2989 m_frame->GetScreen()->m_LocalOrigin = VECTOR2D( 0, 0 );
2990 };
2991
2992 auto updateGraphicToCursor = [&]( const VECTOR2I& aCursorPos, LEADER_MODE aAngleSnap )
2993 {
2994 VECTOR2I clampedCursorPos = aCursorPos;
2995
2996 if( shape == SHAPE_T::CIRCLE || shape == SHAPE_T::ARC )
2997 clampedCursorPos = getClampedRadiusEnd( twoPointMgr.GetOrigin(), aCursorPos );
2998 else
2999 clampedCursorPos = getClampedDifferenceEnd( twoPointMgr.GetOrigin(), aCursorPos );
3000
3001 if( started && aAngleSnap != LEADER_MODE::DIRECT )
3002 {
3003 const VECTOR2I lineVector( clampedCursorPos - VECTOR2I( twoPointMgr.GetOrigin() ) );
3004
3005 VECTOR2I newEnd;
3006
3007 if( aAngleSnap == LEADER_MODE::DEG90 )
3008 newEnd = GetVectorSnapped90( lineVector );
3009 else
3010 newEnd = GetVectorSnapped45( lineVector, ( shape == SHAPE_T::RECTANGLE ) );
3011
3012 m_controls->ForceCursorPosition( true, VECTOR2I( twoPointMgr.GetEnd() ) );
3013 twoPointMgr.SetEnd( twoPointMgr.GetOrigin() + newEnd );
3014 twoPointMgr.SetAngleSnap( aAngleSnap );
3015 }
3016 else
3017 {
3018 twoPointMgr.SetEnd( clampedCursorPos );
3019 twoPointMgr.SetAngleSnap( LEADER_MODE::DIRECT );
3020 }
3021
3022 updateSegmentFromGeometryMgr( twoPointMgr, graphic );
3023 };
3024
3025 m_controls->ShowCursor( true );
3026 m_controls->ForceCursorPosition( false );
3027 // Set initial cursor
3028 setCursor();
3029
3030 m_toolMgr->PostAction( ACTIONS::refreshPreview );
3031
3032 if( aStartingPoint )
3033 m_toolMgr->PrimeTool( *aStartingPoint );
3034
3035 TOOL_EVENT* evt = nullptr;
3036
3037 // Main loop: keep receiving events
3038 while( ( evt = Wait() ) )
3039 {
3040 setCursor();
3041
3042 if( started )
3043 m_frame->SetMsgPanel( graphic );
3044
3045 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
3046 auto angleSnap = GetAngleSnapMode();
3047
3048 // Drawing rectangles and circles ignore the snap behavior by default, but constrains
3049 // when the modifier key is pressed
3050 if( shape == SHAPE_T::RECTANGLE
3051 || shape == SHAPE_T::CIRCLE
3052 || shape == SHAPE_T::ELLIPSE
3053 || shape == SHAPE_T::ELLIPSE_ARC )
3054 {
3055 if( evt->Modifier( MD_CTRL ) )
3056 angleSnap = LEADER_MODE::DEG45;
3057 else
3058 angleSnap = LEADER_MODE::DIRECT;
3059 }
3060 else
3061 {
3062 // All other drawing uses the snap mode, except that is disabled with the modifier key
3063 if( evt->Modifier( MD_CTRL ) )
3064 angleSnap = LEADER_MODE::DIRECT;
3065 }
3066
3067 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
3068 cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
3069 cursorPos = GetClampedCoords( grid.ResolveSnap( cursorPos, { m_layer }, GRID_GRAPHICS ).position,
3071 m_controls->ForceCursorPosition( true, cursorPos );
3072
3073 if( evt->IsCancelInteractive() || ( started && evt->IsAction( &ACTIONS::undo ) ) )
3074 {
3075 cleanup();
3076
3077 if( !started )
3078 {
3079 // We've handled the cancel event. Don't cancel other tools
3080 evt->SetPassEvent( false );
3081 cancelled = true;
3082 }
3083
3084 break;
3085 }
3086 else if( evt->IsActivate() )
3087 {
3088 if( evt->IsPointEditor() )
3089 {
3090 // don't exit (the point editor runs in the background)
3091 continue;
3092 }
3093
3094 if( evt->IsMoveTool() )
3095 {
3096 // Make sure we come back after the move tool is done
3097 m_frame->PushTool( aTool );
3098 }
3099
3100 cleanup();
3101 cancelled = true;
3102 break;
3103 }
3104 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
3105 {
3106 if( m_layer != m_frame->GetActiveLayer() )
3107 {
3108 m_layer = m_frame->GetActiveLayer();
3109 m_stroke.SetWidth( bds.GetLineThickness( m_layer ) );
3110 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
3111 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
3112
3113 m_textAttrs.m_Size = bds.GetTextSize( m_layer );
3114 m_textAttrs.m_StrokeWidth = bds.GetTextThickness( m_layer );
3115 m_textAttrs.m_Italic = bds.GetTextItalic( m_layer );
3116 m_textAttrs.m_KeepUpright = bds.GetTextUpright( m_layer );
3117 m_textAttrs.m_Mirrored = m_board->IsBackLayer( m_layer );
3120 }
3121
3122 if( graphic )
3123 {
3124 if( !m_view->IsLayerVisible( m_layer ) )
3125 {
3126 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
3127 m_frame->GetCanvas()->Refresh();
3128 }
3129
3130 graphic->SetLayer( m_layer );
3131 graphic->SetStroke( m_stroke );
3132
3133 if( PCB_TEXTBOX* pcb_textbox = dynamic_cast<PCB_TEXTBOX*>( graphic ) )
3134 pcb_textbox->SetAttributes( m_textAttrs );
3135
3136 m_view->Update( &m_preview );
3137 frame()->SetMsgPanel( graphic );
3138 }
3139 else
3140 {
3141 evt->SetPassEvent();
3142 }
3143 }
3144 else if( evt->IsClick( BUT_RIGHT ) )
3145 {
3146 if( !graphic )
3147 m_toolMgr->VetoContextMenuMouseWarp();
3148
3149 m_menu->ShowContextMenu( selection() );
3150 }
3151 else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT )
3153 {
3154 if( !graphic )
3155 break;
3156
3157 if( !started )
3158 {
3159 m_toolMgr->RunAction( ACTIONS::selectionClear );
3160
3161 if( aStartingPoint )
3162 {
3163 cursorPos = *aStartingPoint;
3164 aStartingPoint = std::nullopt;
3165 }
3166
3167 // Init the new item attributes
3168 if( graphic ) // always true, but Coverity can't seem to figure that out
3169 {
3170 graphic->SetShape( static_cast<SHAPE_T>( shape ) );
3171 graphic->SetFilled( false );
3172 graphic->SetStroke( m_stroke );
3173 graphic->SetLayer( m_layer );
3174 }
3175
3176 if( PCB_TEXTBOX* pcb_textbox = dynamic_cast<PCB_TEXTBOX*>( graphic ) )
3177 pcb_textbox->SetAttributes( m_textAttrs );
3178
3179 grid.SetSkipPoint( cursorPos );
3180
3181 twoPointMgr.SetOrigin( cursorPos );
3182 twoPointMgr.SetEnd( cursorPos );
3183
3184 if( !isLocalOriginSet )
3185 m_frame->GetScreen()->m_LocalOrigin = cursorPos;
3186
3187 m_preview.Add( graphic );
3188 frame()->SetMsgPanel( graphic );
3189 m_controls->SetAutoPan( true );
3190 m_controls->CaptureCursor( true );
3191
3192 if( !m_view->IsLayerVisible( m_layer ) )
3193 {
3194 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
3195 m_frame->GetCanvas()->Refresh();
3196 }
3197
3198 updateSegmentFromGeometryMgr( twoPointMgr, graphic );
3199
3200 if( shape == SHAPE_T::ELLIPSE_ARC )
3201 graphic->SetEditState( 1 );
3202
3203 started = true;
3204 }
3205 else if( multiPhase )
3206 {
3207 if( !graphic->ContinueEdit( cursorPos ) )
3208 {
3209 // Multi-phase shape is complete
3210 if( marker )
3211 {
3212 m_preview.Remove( marker );
3213 delete marker;
3214 marker = nullptr;
3215 }
3216
3217 graphic->EndEdit();
3218 graphic->ClearEditFlags();
3219 graphic->SetFlags( IS_NEW );
3220 m_preview.Clear();
3221 break;
3222 }
3223 }
3224 else
3225 {
3226 // The click decides the shape, wherever the last motion event left it
3227 updateGraphicToCursor( cursorPos, angleSnap );
3228
3229 // Check if the shape needs more clicks
3230 if( graphic->ContinueEdit( cursorPos ) )
3231 {
3232 multiPhase = true;
3233 m_view->Remove( &twoPointAsst );
3234 m_view->Update( &m_preview );
3235 }
3236 else
3237 {
3238 PCB_SHAPE* snapItem = dynamic_cast<PCB_SHAPE*>( grid.GetSnapped() );
3239
3240 if( shape == SHAPE_T::SEGMENT && snapItem && graphic->GetLength() > 0 )
3241 {
3242 // User has clicked on the end of an existing segment, closing a path
3243 BOARD_COMMIT commit( m_frame );
3244
3245 commit.Add( graphic );
3246
3247 std::vector<PCB_CONSTRAINT*> snaps;
3248
3249 if( GetAutoConstraints() )
3250 {
3251 snaps = stageAutoConstraints( m_board, graphic, m_frame->GetModel(), commit,
3253 }
3254
3255 commit.Push( _( "Draw Line" ) );
3257 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, graphic );
3258
3259 graphic = nullptr;
3260 }
3261 else if( twoPointMgr.IsEmpty()
3262 || evt->IsDblClick( BUT_LEFT )
3263 || evt->IsAction( &ACTIONS::cursorDblClick ) )
3264 {
3265 // User has clicked twice in the same spot, meaning we're finished
3266 delete graphic;
3267 graphic = nullptr;
3268 }
3269
3270 m_preview.Clear();
3271 twoPointMgr.Reset();
3272 break;
3273 }
3274 }
3275
3276 twoPointMgr.SetEnd( GetClampedCoords( cursorPos ) );
3277 }
3278 else if( evt->IsMotion() || evt->IsAction( &ACTIONS::refreshPreview ) )
3279 {
3280 if( multiPhase )
3281 {
3282 graphic->CalcEdit( GetClampedCoords( cursorPos ) );
3283
3284 if( shape == SHAPE_T::ELLIPSE_ARC )
3285 {
3286 VECTOR2I markerPos = evalEllipsePoint( graphic, cursorPos );
3287
3288 if( !marker )
3289 {
3290 marker = new PCB_SHAPE( static_cast<BOARD_ITEM*>( m_frame->GetModel() ) );
3291 marker->SetShape( SHAPE_T::CIRCLE );
3292 marker->SetFilled( true );
3293 marker->SetLayer( m_layer );
3294 marker->SetStroke( STROKE_PARAMS( 0, LINE_STYLE::SOLID ) );
3295 m_preview.Add( marker );
3296 }
3297
3298 int radius = KiROUND( m_view->ToWorld( 4 ) );
3299 marker->SetStart( markerPos );
3300 marker->SetEnd( markerPos + VECTOR2I( radius, 0 ) );
3301 }
3302
3303 m_view->Update( &m_preview );
3304 frame()->SetMsgPanel( graphic );
3305 }
3306 else
3307 {
3308 updateGraphicToCursor( cursorPos, angleSnap );
3309 m_view->Update( &m_preview );
3310 m_view->Update( &twoPointAsst );
3311 }
3312 }
3313 else if( started && ( evt->IsAction( &PCB_ACTIONS::doDelete )
3314 || evt->IsAction( &ACTIONS::deleteLastPoint ) ) )
3315 {
3316 if( aCommittedGraphics && !aCommittedGraphics->empty() )
3317 {
3318 twoPointMgr.SetOrigin( aCommittedGraphics->top()->GetStart() );
3319 twoPointMgr.SetEnd( aCommittedGraphics->top()->GetEnd() );
3320 aCommittedGraphics->pop();
3321
3322 // Snap guides persist in the grid helper until the tool exits, so a mid-draw
3323 // backup must clear them or they linger on screen.
3324 grid.FullReset();
3325
3326 getViewControls()->WarpMouseCursor( twoPointMgr.GetEnd(), true );
3327
3328 if( PICKED_ITEMS_LIST* undo = m_frame->PopCommandFromUndoList() )
3329 {
3330 m_frame->PutDataInPreviousState( undo );
3331 m_frame->ClearListAndDeleteItems( undo );
3332 delete undo;
3333 }
3334
3335 updateSegmentFromGeometryMgr( twoPointMgr, graphic );
3336 m_view->Update( &m_preview );
3337 m_view->Update( &twoPointAsst );
3338 }
3339 else
3340 {
3341 cleanup();
3342 break;
3343 }
3344 }
3345 else if( graphic && evt->IsAction( &PCB_ACTIONS::incWidth ) )
3346 {
3347 m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
3348 graphic->SetStroke( m_stroke );
3349 m_view->Update( &m_preview );
3350 frame()->SetMsgPanel( graphic );
3351 }
3352 else if( graphic && evt->IsAction( &PCB_ACTIONS::decWidth ) )
3353 {
3354 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
3355 {
3356 m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
3357 graphic->SetStroke( m_stroke );
3358 m_view->Update( &m_preview );
3359 frame()->SetMsgPanel( graphic );
3360 }
3361 }
3362 else if( started && evt->IsAction( &PCB_ACTIONS::properties ) )
3363 {
3364 frame()->OnEditItemRequest( graphic );
3365 m_view->Update( &m_preview );
3366 frame()->SetMsgPanel( graphic );
3367 }
3368 else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
3369 || evt->IsAction( &ACTIONS::redo ) ) )
3370 {
3371 wxBell();
3372 }
3373 else if( evt->IsAction( &ACTIONS::resetLocalCoords ) )
3374 {
3375 isLocalOriginSet = true;
3376 evt->SetPassEvent();
3377 }
3378 else if( evt->IsAction( &ACTIONS::updateUnits ) )
3379 {
3380 if( frame()->GetUserUnits() != userUnits )
3381 {
3382 userUnits = frame()->GetUserUnits();
3383 twoPointAsst.SetUnits( userUnits );
3384 m_view->Update( &twoPointAsst );
3385 }
3386 evt->SetPassEvent();
3387 }
3388 else
3389 {
3390 evt->SetPassEvent();
3391 }
3392 }
3393
3394 // A shutdown must end the caller's loop rather than start another shape
3395 if( !evt )
3396 {
3397 cleanup();
3398 cancelled = true;
3399 }
3400
3401 if( !isLocalOriginSet ) // reset the relative coordinate if it was not set before
3402 m_frame->GetScreen()->m_LocalOrigin = VECTOR2D( 0, 0 );
3403
3404 if( !multiPhase )
3405 m_view->Remove( &twoPointAsst );
3406
3407 m_view->Remove( &m_preview );
3408
3409 if( selection().Empty() )
3410 m_frame->SetMsgPanel( board() );
3411
3412 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3413 m_controls->SetAutoPan( false );
3414 m_controls->CaptureCursor( false );
3415 m_controls->ForceCursorPosition( false );
3416
3417 return !cancelled;
3418}
3419
3420
3422{
3423 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
3424 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() );
3425
3426 SNAP_RESULT snap = grid.ResolveSnap( aEvent.Position(), LSET{ m_frame->GetActiveLayer() }, GRID_GRAPHICS );
3427
3429}
3430
3431
3432SHAPE_DRAW_RESULT DRAWING_TOOL::drawManagedShape( const TOOL_EVENT& aTool, std::unique_ptr<PCB_SHAPE>& aGraphic,
3433 SHAPE_DRAW_BEHAVIOR& aBehavior,
3434 const std::vector<VECTOR2D>& aInitialPts,
3435 const std::optional<ARC_TANGENT_SEED>& aTangentSeed,
3436 VECTOR2I* aLastClick )
3437{
3438 if( !aGraphic )
3440
3441 PCB_SHAPE* graphic = aGraphic.get();
3442
3443 aBehavior.Reset();
3444
3445 if( m_layer != m_frame->GetActiveLayer() )
3446 {
3447 m_layer = m_frame->GetActiveLayer();
3448 m_stroke.SetWidth( m_frame->GetDesignSettings().GetLineThickness( m_layer ) );
3449 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
3450 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
3451 }
3452
3453 // Add a VIEW_GROUP that serves as a preview for the new item
3454 PCB_SELECTION preview;
3455 m_view->Add( &preview );
3456 m_view->Add( &aBehavior.GetAssistant() );
3457 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
3458
3459 auto setCursor =
3460 [&]()
3461 {
3462 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
3463 };
3464
3465 auto cleanup =
3466 [&] ()
3467 {
3468 preview.Clear();
3469 aGraphic.reset();
3470 };
3471
3472 // Clicking a graphic on the active layer starts a tangent arc from it instead of placing a start point
3473 auto findTangentSeed =
3474 [&]( const VECTOR2I&, const VECTOR2I& aPos ) -> std::optional<ARC_TANGENT_SEED>
3475 {
3476 const int tolerance = KiROUND( m_view->ToWorld( ARC_SEED_PICK_PIXELS ) );
3477
3478 auto seedFrom =
3479 [&]( const PCB_SHAPE* aShape ) -> std::optional<ARC_TANGENT_SEED>
3480 {
3481 if( !aShape || aShape->GetLayer() != m_layer )
3482 return std::nullopt;
3483
3484 BOX2I reach = aShape->GetBoundingBox();
3485
3486 if( !reach.Inflate( tolerance ).Contains( aPos ) )
3487 return std::nullopt;
3488
3489 return ArcTangentSeedAt( *aShape, aPos, tolerance );
3490 };
3491
3492 if( std::optional<ARC_TANGENT_SEED> seed = seedFrom( dynamic_cast<PCB_SHAPE*>( grid.GetSnapped() ) ) )
3493 return seed;
3494
3495 for( PCB_SHAPE* shape : CollectConstraintShapes( board(), m_layer ) )
3496 {
3497 if( std::optional<ARC_TANGENT_SEED> seed = seedFrom( shape ) )
3498 return seed;
3499 }
3500
3501 return std::nullopt;
3502 };
3503
3504 ARC_MODE_SESSION arcSession( aBehavior, m_frame->config(), m_mode == MODE::ARC, aTangentSeed, findTangentSeed,
3505 [&]( ARC_DRAW_MODE aMode )
3506 {
3507 m_frame->SelectToolbarAction( PCB_ACTIONS::DrawArcForMode( aMode ) );
3508 m_toolMgr->PostAction( ACTIONS::refreshPreview );
3509 } );
3510
3511 m_controls->ShowCursor( true );
3512 m_controls->ForceCursorPosition( false );
3513 // Set initial cursor
3514 setCursor();
3515
3516 bool started = false;
3517 bool cancelled = false;
3518 bool finished = false;
3519
3520 m_toolMgr->PostAction( ACTIONS::refreshPreview );
3521
3522 // Pre-load any initial points into the behaviour, advancing the construction
3523 // state machine so that the next user click adds the subsequent point.
3524 for( const VECTOR2D& pt : aInitialPts )
3525 arcSession.PlacePoint( pt, pt );
3526
3527 if( !aInitialPts.empty() || aTangentSeed )
3528 {
3529 m_toolMgr->RunAction( ACTIONS::selectionClear );
3530
3531 m_controls->SetAutoPan( true );
3532 m_controls->CaptureCursor( true );
3533
3534 // Init the new item attributes
3535 // (non-geometric, those are handled by the manager)
3536 graphic->SetStroke( m_stroke );
3537
3538 if( !m_view->IsLayerVisible( m_layer ) )
3539 {
3540 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
3541 m_frame->GetCanvas()->Refresh();
3542 }
3543
3544 preview.Add( graphic );
3545 frame()->SetMsgPanel( graphic );
3546
3547 started = true;
3548 }
3549
3550 TOOL_EVENT* evt = nullptr;
3551
3552 // Main loop: keep receiving events
3553 while( ( evt = Wait() ) )
3554 {
3555 if( started )
3556 m_frame->SetMsgPanel( graphic );
3557
3558 setCursor();
3559
3560 graphic->SetLayer( m_layer );
3561
3562 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
3563 LEADER_MODE angleSnap = GetAngleSnapMode();
3564
3565 if( evt->Modifier( MD_CTRL ) )
3566 angleSnap = LEADER_MODE::DIRECT;
3567
3568 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
3569 VECTOR2I cursorPos = m_controls->GetMousePosition();
3570 cursorPos = GetClampedCoords( grid.ResolveSnap( cursorPos, graphic, GRID_GRAPHICS ).position, COORDS_PADDING );
3571 m_controls->ForceCursorPosition( true, cursorPos );
3572
3573 if( evt->IsCancelInteractive() )
3574 {
3575 cleanup();
3576
3577 if( !started )
3578 {
3579 // We've handled the cancel event. Don't cancel other tools
3580 evt->SetPassEvent( false );
3581 cancelled = true;
3582 }
3583
3584 break;
3585 }
3586 else if( started && evt->IsAction( &ACTIONS::undo ) )
3587 {
3588 cleanup();
3589 break;
3590 }
3591 else if( arcSession.HandleModeEvent( *evt ) )
3592 {
3593 // A mode switch keeps the tool running rather than restarting it
3594 }
3595 else if( evt->IsActivate() )
3596 {
3597 if( evt->IsPointEditor() )
3598 {
3599 // don't exit (the point editor runs in the background)
3600 continue;
3601 }
3602
3603 if( evt->IsMoveTool() )
3604 {
3605 // Make sure we come back after the move tool is done
3606 m_frame->PushTool( aTool );
3607 }
3608
3609 cleanup();
3610 cancelled = true;
3611 break;
3612 }
3613 else if( evt->IsClick( BUT_LEFT ) || evt->IsAction( &ACTIONS::cursorClick ) )
3614 {
3615 if( !started )
3616 {
3617 m_toolMgr->RunAction( ACTIONS::selectionClear );
3618
3619 m_controls->SetAutoPan( true );
3620 m_controls->CaptureCursor( true );
3621
3622 // Init the new item attributes
3623 // (non-geometric, those are handled by the manager)
3624 graphic->SetStroke( m_stroke );
3625
3626 if( !m_view->IsLayerVisible( m_layer ) )
3627 {
3628 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
3629 m_frame->GetCanvas()->Refresh();
3630 }
3631
3632 preview.Add( graphic );
3633 frame()->SetMsgPanel( graphic );
3634 started = true;
3635 }
3636
3637 arcSession.PlacePoint( cursorPos, cursorPos );
3638
3639 if( aLastClick )
3640 *aLastClick = cursorPos;
3641 }
3642 else if( evt->IsDblClick( BUT_LEFT )
3645 {
3646 // Keep whatever we have so far, and bail. The caller commits it, but does
3647 // not start another shape in the chain.
3648 if( !started )
3649 cleanup();
3650
3651 finished = true;
3652 break;
3653 }
3654 else if( evt->IsAction( &ACTIONS::deleteLastPoint ) || ( started && evt->IsAction( &ACTIONS::doDelete ) ) )
3655 {
3656 // Backspace is also doDelete's alternate hotkey, so a backup can arrive as either action
3657 // Snap guides persist in the grid helper until the tool exits, so a mid-draw backup
3658 // must clear them or they linger on screen.
3659 grid.FullReset();
3660 arcSession.RemoveLastPoint();
3661 }
3662 else if( evt->IsMotion() || evt->IsAction( &ACTIONS::refreshPreview ) )
3663 {
3664 // set angle snap
3665 aBehavior.SetAngleSnap( angleSnap != LEADER_MODE::DIRECT );
3666
3667 // update, but don't step the manager state
3668 aBehavior.SetCursorPosition( cursorPos );
3669 }
3670 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
3671 {
3672 if( m_layer != m_frame->GetActiveLayer() )
3673 {
3674 m_layer = m_frame->GetActiveLayer();
3675 m_stroke.SetWidth( m_frame->GetDesignSettings().GetLineThickness( m_layer ) );
3676 m_stroke.SetLineStyle( LINE_STYLE::DEFAULT );
3677 m_stroke.SetColor( COLOR4D::UNSPECIFIED );
3678 }
3679
3680 if( graphic )
3681 {
3682 if( !m_view->IsLayerVisible( m_layer ) )
3683 {
3684 m_frame->GetAppearancePanel()->SetLayerVisible( m_layer, true );
3685 m_frame->GetCanvas()->Refresh();
3686 }
3687
3688 graphic->SetLayer( m_layer );
3689 graphic->SetStroke( m_stroke );
3690 m_view->Update( &preview );
3691 frame()->SetMsgPanel( graphic );
3692 }
3693 else
3694 {
3695 evt->SetPassEvent();
3696 }
3697 }
3698 else if( evt->IsAction( &PCB_ACTIONS::properties ) )
3699 {
3700 if( aBehavior.OnProperties( *graphic ) )
3701 {
3702 frame()->OnEditItemRequest( graphic );
3703 m_view->Update( &preview );
3704 frame()->SetMsgPanel( graphic );
3705 break;
3706 }
3707 else
3708 {
3709 evt->SetPassEvent();
3710 }
3711 }
3712 else if( evt->IsClick( BUT_RIGHT ) )
3713 {
3714 if( !graphic )
3715 m_toolMgr->VetoContextMenuMouseWarp();
3716
3717 m_menu->ShowContextMenu( selection() );
3718 }
3719 else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
3720 {
3721 m_stroke.SetWidth( m_stroke.GetWidth() + WIDTH_STEP );
3722
3723 if( graphic )
3724 {
3725 graphic->SetStroke( m_stroke );
3726 m_view->Update( &preview );
3727 frame()->SetMsgPanel( graphic );
3728 }
3729 }
3730 else if( evt->IsAction( &PCB_ACTIONS::decWidth ) )
3731 {
3732 if( (unsigned) m_stroke.GetWidth() > WIDTH_STEP )
3733 {
3734 m_stroke.SetWidth( m_stroke.GetWidth() - WIDTH_STEP );
3735
3736 if( graphic )
3737 {
3738 graphic->SetStroke( m_stroke );
3739 m_view->Update( &preview );
3740 frame()->SetMsgPanel( graphic );
3741 }
3742 }
3743 }
3744 else if( evt->IsAction( &ACTIONS::arcPosture ) )
3745 {
3746 aBehavior.ToggleClockwise();
3747 }
3748 else if( evt->IsAction( &ACTIONS::updateUnits ) )
3749 {
3750 aBehavior.SetUnits( frame()->GetUserUnits() );
3751 m_view->Update( &aBehavior.GetAssistant() );
3752 evt->SetPassEvent();
3753 }
3754 else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
3755 || evt->IsAction( &ACTIONS::redo ) ) )
3756 {
3757 wxBell();
3758 }
3759 else
3760 {
3761 evt->SetPassEvent();
3762 }
3763
3764 if( aBehavior.IsComplete() )
3765 {
3766 break;
3767 }
3768 else if( aBehavior.HasGeometryChanged() )
3769 {
3770 aBehavior.ApplyToShape( *graphic );
3771 m_view->Update( &preview );
3772 m_view->Update( &aBehavior.GetAssistant() );
3773 aBehavior.ClearGeometryChanged();
3774
3775 if( started )
3776 frame()->SetMsgPanel( graphic );
3777 else
3778 frame()->SetMsgPanel( board() );
3779 }
3780 }
3781
3782 // A shutdown must end the caller's loop rather than start another shape
3783 if( !evt )
3784 {
3785 cleanup();
3786 cancelled = true;
3787 }
3788
3789 preview.Remove( graphic );
3790 m_view->Remove( &aBehavior.GetAssistant() );
3791 m_view->Remove( &preview );
3792
3793 if( selection().Empty() )
3794 m_frame->SetMsgPanel( board() );
3795
3796 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
3797 m_controls->SetAutoPan( false );
3798 m_controls->CaptureCursor( false );
3799 m_controls->ForceCursorPosition( false );
3800
3801 if( cancelled )
3802 {
3803 aGraphic.reset();
3805 }
3806
3808}
3809
3810
3812{
3813 bool clearSelection = false;
3814 *aZone = nullptr;
3815
3816 // not an action that needs a source zone
3817 if( aMode == ZONE_MODE::ADD || aMode == ZONE_MODE::GRAPHIC_POLYGON || aMode == ZONE_MODE::STITCH )
3818 return true;
3819
3820 PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
3821 const PCB_SELECTION& selection = selTool->GetSelection();
3822
3823 if( selection.Empty() )
3824 {
3825 clearSelection = true;
3826 m_toolMgr->RunAction( ACTIONS::selectionCursor );
3827 }
3828
3829 // we want a single zone
3830 if( selection.Size() == 1 && selection[0]->Type() == PCB_ZONE_T )
3831 *aZone = static_cast<ZONE*>( selection[0] );
3832
3833 // expected a zone, but didn't get one
3834 if( !*aZone )
3835 {
3836 if( clearSelection )
3837 m_toolMgr->RunAction( ACTIONS::selectionClear );
3838
3839 return false;
3840 }
3841
3842 return true;
3843}
3844
3845
3847{
3848 if( m_isFootprintEditor && !m_frame->GetModel() )
3849 return 0;
3850
3851 if( m_inDrawingTool )
3852 return 0;
3853
3855
3856 ZONE_MODE zoneMode = aEvent.Parameter<ZONE_MODE>();
3857 MODE drawMode = MODE::ZONE;
3858
3859 if( aEvent.IsAction( &PCB_ACTIONS::drawRuleArea ) )
3860 drawMode = MODE::KEEPOUT;
3861
3862 if( aEvent.IsAction( &PCB_ACTIONS::drawPolygon ) )
3863 drawMode = MODE::GRAPHIC_POLYGON;
3864
3865 const bool drawingThieving = aEvent.IsAction( &PCB_ACTIONS::drawCopperThievingZone );
3866
3868 drawMode = MODE::STITCH;
3869
3870 SCOPED_DRAW_MODE scopedDrawMode( m_mode, drawMode );
3871
3872 // get a source zone, if we need one. We need it for:
3873 // ZONE_MODE::CUTOUT (adding a hole to the source zone)
3874 // ZONE_MODE::SIMILAR (creating a new zone using settings of source zone
3875 ZONE* sourceZone = nullptr;
3876
3877 if( !getSourceZoneForAction( zoneMode, &sourceZone ) )
3878 return 0;
3879
3880 // Turn zones on if they are off, so that the created object will be visible after completion
3881 m_frame->SetObjectVisible( LAYER_ZONES );
3882
3884
3885 params.m_keepout = drawMode == MODE::KEEPOUT;
3886 params.m_thieving = drawingThieving;
3887 params.m_mode = zoneMode;
3888 params.m_sourceZone = sourceZone;
3889 params.m_layer = m_frame->GetActiveLayer();
3890
3891 if( zoneMode == ZONE_MODE::SIMILAR && !sourceZone->IsOnLayer( params.m_layer ) )
3892 params.m_layer = sourceZone->GetFirstLayer();
3893
3894 ZONE_CREATE_HELPER zoneTool( *this, params );
3895 // the geometry manager which handles the zone geometry, and hands the calculated points
3896 // over to the zone creator tool
3897 POLYGON_GEOM_MANAGER polyGeomMgr( zoneTool );
3898 bool started = false;
3899 PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
3900 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
3901 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
3902
3903 auto setCursor =
3904 [&]()
3905 {
3906 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
3907 };
3908
3909 auto cleanup =
3910 [&] ()
3911 {
3912 polyGeomMgr.Reset();
3913 started = false;
3914 grid.ClearSkipPoint();
3915
3916 // Snap guides persist in the grid helper until the tool exits, so abandoning the
3917 // outline mid-draw must clear them or they linger on screen.
3918 grid.FullReset();
3919
3920 m_controls->SetAutoPan( false );
3921 m_controls->CaptureCursor( false );
3922 };
3923
3924 Activate();
3925 // Must be done after Activate() so that it gets set into the correct context
3926 m_controls->ShowCursor( true );
3927 m_controls->ForceCursorPosition( false );
3928 // Set initial cursor
3929 setCursor();
3930
3931 if( aEvent.HasPosition() )
3932 m_toolMgr->PrimeTool( aEvent.Position() );
3933
3934 // Main loop: keep receiving events
3935 while( TOOL_EVENT* evt = Wait() )
3936 {
3937 setCursor();
3938
3939 LSET layers( { m_frame->GetActiveLayer() } );
3940 grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
3941 LEADER_MODE angleSnap = GetAngleSnapMode();
3942
3943 if( evt->Modifier( MD_CTRL ) )
3944 angleSnap = LEADER_MODE::DIRECT;
3945
3946 grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
3947
3948 VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
3949 cursorPos = GetClampedCoords( grid.ResolveSnap( cursorPos, layers, GRID_GRAPHICS ).position, COORDS_PADDING );
3950
3951 m_controls->ForceCursorPosition( true, cursorPos );
3952
3953 polyGeomMgr.SetLeaderMode( angleSnap );
3954
3955 if( evt->IsCancelInteractive() )
3956 {
3957 if( started )
3958 {
3959 cleanup();
3960 }
3961 else
3962 {
3963 // We've handled the cancel event. Don't cancel other tools
3964 evt->SetPassEvent( false );
3965 break;
3966 }
3967 }
3968 else if( evt->IsActivate() )
3969 {
3970 if( started )
3971 cleanup();
3972
3973 if( evt->IsPointEditor() )
3974 {
3975 // don't exit (the point editor runs in the background)
3976 continue;
3977 }
3978
3979 if( evt->IsMoveTool() )
3980 {
3981 // Make sure we come back after the move tool is done
3982 m_frame->PushTool( originalEvent );
3983 }
3984
3985 break;
3986 }
3987 else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
3988 {
3989 if( zoneMode != ZONE_MODE::SIMILAR )
3990 params.m_layer = frame()->GetActiveLayer();
3991
3992 if( !m_view->IsLayerVisible( params.m_layer ) )
3993 {
3994 m_frame->GetAppearancePanel()->SetLayerVisible( params.m_layer, true );
3995 m_frame->GetCanvas()->Refresh();
3996 }
3997 }
3998 else if( evt->IsClick( BUT_RIGHT ) )
3999 {
4000 if( !started )
4001 m_toolMgr->VetoContextMenuMouseWarp();
4002
4003 m_menu->ShowContextMenu( selection() );
4004 }
4005 // events that lock in nodes
4006 else if( evt->IsClick( BUT_LEFT )
4007 || evt->IsAction( &ACTIONS::cursorClick )
4008 || evt->IsDblClick( BUT_LEFT )
4009 || evt->IsAction( &ACTIONS::cursorDblClick )
4010 || evt->IsAction( &PCB_ACTIONS::closeOutline ) )
4011 {
4012 // Check if it is double click / closing line (so we have to finish the zone)
4013 const bool endPolygon = evt->IsDblClick( BUT_LEFT )
4014 || evt->IsAction( &PCB_ACTIONS::cursorDblClick )
4015 || evt->IsAction( &PCB_ACTIONS::closeOutline )
4016 || polyGeomMgr.NewPointClosesOutline( cursorPos );
4017
4018 if( endPolygon )
4019 {
4020 polyGeomMgr.SetFinished();
4021 polyGeomMgr.Reset();
4022
4023 cleanup();
4024 break;
4025 }
4026 // adding a corner
4027 else if( polyGeomMgr.AddPoint( cursorPos ) )
4028 {
4029 if( !started )
4030 {
4031 started = true;
4032
4033 m_controls->SetAutoPan( true );
4034 m_controls->CaptureCursor( true );
4035
4036 if( !m_view->IsLayerVisible( params.m_layer ) )
4037 {
4038 m_frame->GetAppearancePanel()->SetLayerVisible( params.m_layer, true );
4039 m_frame->GetCanvas()->Refresh();
4040 }
4041 }
4042 }
4043 }
4044 else if( started && ( evt->IsAction( &ACTIONS::deleteLastPoint )
4045 || evt->IsAction( &ACTIONS::doDelete )
4046 || evt->IsAction( &ACTIONS::undo ) ) )
4047 {
4048 // Snap guides persist in the grid helper until the tool exits, so dropping a corner
4049 // must clear them or they linger on screen.
4050 grid.FullReset();
4051
4052 if( std::optional<VECTOR2I> last = polyGeomMgr.DeleteLastCorner() )
4053 {
4054 cursorPos = last.value();
4055 getViewControls()->WarpMouseCursor( cursorPos, true );
4056 m_controls->ForceCursorPosition( true, cursorPos );
4057 polyGeomMgr.SetCursorPosition( cursorPos );
4058 }
4059 else
4060 {
4061 cleanup();
4062 }
4063 }
4064 else if( started && ( evt->IsMotion()
4065 || evt->IsAction( &ACTIONS::refreshPreview )
4066 || evt->IsDrag( BUT_LEFT ) ) )
4067 {
4068 polyGeomMgr.SetCursorPosition( cursorPos );
4069 }
4070 else if( started && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
4071 || evt->IsAction( &ACTIONS::redo ) ) )
4072 {
4073 wxBell();
4074 }
4075 else if( started && evt->IsAction( &PCB_ACTIONS::properties ) )
4076 {
4077 frame()->OnEditItemRequest( zoneTool.GetZone() );
4078 zoneTool.OnGeometryChange( polyGeomMgr );
4079 frame()->SetMsgPanel( zoneTool.GetZone() );
4080 }
4081 /*else if( evt->IsAction( &ACTIONS::updateUnits ) )
4082 {
4083 // If we ever have an assistant here that reports dimensions, we'll want to
4084 // update its units here....
4085 // zoneAsst.SetUnits( frame()->GetUserUnits() );
4086 // m_view->Update( &zoneAsst );
4087 evt->SetPassEvent();
4088 }*/
4089 else
4090 {
4091 evt->SetPassEvent();
4092 }
4093
4094 } // end while
4095
4096 m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
4097 m_controls->ForceCursorPosition( false );
4098 controls()->SetAutoPan( false );
4099 m_controls->CaptureCursor( false );
4100 return 0;
4101}
4102
4103
4105{
4106 int worst = 0;
4107
4108 try
4109 {
4110 if( aFrame )
4111 aEngine->InitEngine( aFrame->GetBoard()->GetDesignRulesPath() );
4112
4113 DRC_CONSTRAINT constraint;
4114
4115 if( aEngine->QueryWorstConstraint( CLEARANCE_CONSTRAINT, constraint ) )
4116 worst = constraint.GetValue().Min();
4117
4118 if( aEngine->QueryWorstConstraint( HOLE_CLEARANCE_CONSTRAINT, constraint ) )
4119 worst = std::max( worst, constraint.GetValue().Min() );
4120
4121 for( FOOTPRINT* footprint : aFrame->GetBoard()->Footprints() )
4122 {
4123 for( PAD* pad : footprint->Pads() )
4124 {
4125 std::optional<int> padOverride = pad->GetClearanceOverrides( nullptr );
4126
4127 if( padOverride.has_value() )
4128 worst = std::max( worst, padOverride.value() );
4129 }
4130 }
4131 }
4132 catch( PARSE_ERROR& )
4133 {
4134 }
4135
4136 return worst;
4137}
4138
4139
4140static bool ItemHasDRCViolation( BOARD_CONNECTED_ITEM* aItem, BOARD_ITEM* aOther, DRC_ENGINE* aEngine, int aEpsilon )
4141{
4142 auto sub_e = [&]( int aClearance )
4143 {
4144 return std::max( 0, aClearance - aEpsilon );
4145 };
4146
4147 DRC_CONSTRAINT constraint;
4148 int clearance;
4149 BOARD_CONNECTED_ITEM* connectedItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aOther );
4150 ZONE* zone = dynamic_cast<ZONE*>( aOther );
4151
4152 PCB_VIA* via = dynamic_cast<PCB_VIA*>( aItem );
4153
4154 if( zone && zone->GetIsRuleArea() )
4155 {
4156 if( via ? zone->GetDoNotAllowVias() : zone->GetDoNotAllowTracks() )
4157 {
4158 SHAPE_POLY_SET zoneOutlineStorage;
4159 SHAPE_POLY_SET* zoneOutline = &zoneOutlineStorage;
4160
4161 if( zone->GetParentFootprint() )
4162 zoneOutlineStorage = zone->GetBoardOutline();
4163 else
4164 zoneOutline = zone->Outline();
4165
4166 if( !via )
4167 return zoneOutline->Collide( aItem->GetEffectiveShape().get() );
4168
4169 bool hit = false;
4170 LSET common = via->GetLayerSet() & zone->GetLayerSet();
4171
4172 for( PCB_LAYER_ID layer : common )
4173 {
4174 if( !IsCopperLayer( layer ) )
4175 continue;
4176
4177 if( zoneOutline->Collide( via->GetPosition(), via->GetWidth( layer ) / 2 ) )
4178 {
4179 hit = true;
4180 break;
4181 }
4182 }
4183
4184 return hit;
4185 }
4186
4187 return false;
4188 }
4189
4190 bool skipCopper = false;
4191
4192 if( connectedItem )
4193 {
4194 int connectedItemNet = connectedItem->GetNetCode();
4195
4196 if( connectedItemNet != 0 && connectedItemNet == aItem->GetNetCode() )
4197 return false;
4198
4199 // A netless item can take the via's net, but its hole still keeps the via out.
4200 if( connectedItemNet == 0 )
4201 skipCopper = true;
4202 }
4203
4204 if( !skipCopper )
4205 {
4206 for( PCB_LAYER_ID layer : aOther->GetLayerSet() )
4207 {
4208 // Reference images are "on" a copper layer but are not actually part of it
4209 if( !IsCopperLayer( layer ) || aOther->Type() == PCB_REFERENCE_IMAGE_T )
4210 continue;
4211
4212 constraint = aEngine->EvalRules( CLEARANCE_CONSTRAINT, aItem, aOther, layer );
4213 clearance = constraint.GetValue().Min();
4214
4215 if( clearance >= 0 )
4216 {
4217 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( layer );
4218 std::shared_ptr<SHAPE> otherShape = aOther->GetEffectiveShape( layer );
4219
4220 if( itemShape->Collide( otherShape.get(), sub_e( clearance ) ) )
4221 return true;
4222 }
4223 }
4224 }
4225
4226 if( aOther->HasHole() )
4227 {
4228 constraint = aEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aItem, aOther, UNDEFINED_LAYER );
4229 clearance = constraint.GetValue().Min();
4230
4231 if( clearance >= 0 )
4232 {
4233 std::shared_ptr<SHAPE> itemShape = aItem->GetEffectiveShape( UNDEFINED_LAYER );
4234
4235 if( itemShape->Collide( aOther->GetEffectiveHoleShape().get(), sub_e( clearance ) ) )
4236 return true;
4237 }
4238 }
4239
4240 return false;
4241}
4242
4243
4245 int aWorstClearance, int aEpsilon )
4246{
4247 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
4248 std::set<BOARD_ITEM*> checkedItems;
4249 BOX2I bbox = aItem->GetBoundingBox();
4250
4251 bbox.Inflate( aWorstClearance );
4252 aFrame->GetCanvas()->GetView()->Query( bbox, items );
4253
4254 for( std::pair<KIGFX::VIEW_ITEM*, int> it : items )
4255 {
4256 if( !it.first->IsBOARD_ITEM() )
4257 continue;
4258
4259 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
4260
4261 if( item->Type() == PCB_ZONE_T && !static_cast<ZONE*>( item )->GetIsRuleArea() )
4262 {
4263 continue; // the pour is not refilled yet, so it still looks solid where the via is going
4264 }
4265 else if( item->Type() == PCB_FOOTPRINT_T || item->Type() == PCB_GROUP_T )
4266 {
4267 continue; // check against children, but not against footprint itself
4268 }
4269 else if( ( item->Type() == PCB_FIELD_T || item->Type() == PCB_TEXT_T )
4270 && !static_cast<PCB_TEXT*>( item )->IsVisible() )
4271 {
4272 continue; // ignore hidden items
4273 }
4274 else if( checkedItems.count( item ) )
4275 {
4276 continue; // already checked
4277 }
4278
4279 if( ItemHasDRCViolation( aItem, item, aEngine, aEpsilon ) )
4280 return true;
4281
4282 checkedItems.insert( item );
4283 }
4284
4285 DRC_CONSTRAINT constraint = aEngine->EvalRules( DISALLOW_CONSTRAINT, aItem, nullptr, UNDEFINED_LAYER );
4286
4287 if( constraint.m_DisallowFlags && constraint.GetSeverity() != RPT_SEVERITY_IGNORE )
4288 return true;
4289
4290 return false;
4291}
4292
4293
4295{
4296 KIGFX::VIEW* view = aFrame->GetCanvas()->GetView();
4297
4298 // The visible grid can exceed INT_MAX, so clamp in double as GRID_HELPER does.
4299 double scale = view->ToWorld( 25.0 );
4300
4301 return KiROUND( std::min( scale, view->GetGAL()->GetVisibleGridSize().x ) );
4302}
4303
4304
4309static BOX2I ViaSnapBoundingBox( const PCB_VIA& aVia, const VECTOR2I& aPosition )
4310{
4311 BOX2I bbox = aVia.GetBoundingBox();
4312 bbox.Move( aPosition - aVia.GetPosition() );
4313 return bbox;
4314}
4315
4316
4317PCB_TRACK* FindSnapTrack( PCB_BASE_EDIT_FRAME* aFrame, const PCB_VIA* aVia, const VECTOR2I& aPosition,
4318 const LSET& aLayers, int aSnapRange )
4319{
4320 const LSET lset = aLayers;
4321 const BOX2I bbox = ViaSnapBoundingBox( *aVia, aPosition );
4322
4323 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
4324 KIGFX::PCB_VIEW* view = aFrame->GetCanvas()->GetView();
4325 std::vector<PCB_TRACK*> possible_tracks;
4326
4327 wxCHECK( view, nullptr );
4328
4329 view->Query( bbox, items );
4330
4331 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
4332 {
4333 if( !it.first->IsBOARD_ITEM() )
4334 continue;
4335
4336 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
4337
4338 if( !( item->GetLayerSet() & lset ).any() )
4339 continue;
4340
4341 if( item->Type() == PCB_TRACE_T )
4342 {
4343 PCB_TRACK* track = static_cast<PCB_TRACK*>( item );
4344
4345 if( TestSegmentHit(
4346 aPosition, track->GetStart(), track->GetEnd(),
4347 std::max( ( track->GetWidth() + aVia->GetWidth( track->GetLayer() ) ) / 2, aSnapRange ) ) )
4348 {
4349 possible_tracks.push_back( track );
4350 }
4351 }
4352 else if( item->Type() == PCB_ARC_T )
4353 {
4354 PCB_ARC* arc = static_cast<PCB_ARC*>( item );
4355
4356 if( arc->HitTest( aPosition, std::max( aVia->GetWidth( arc->GetLayer() ) / 2, aSnapRange ) ) )
4357 possible_tracks.push_back( arc );
4358 }
4359 }
4360
4361 PCB_TRACK* return_track = nullptr;
4362 int min_d = std::numeric_limits<int>::max();
4363
4364 for( PCB_TRACK* track : possible_tracks )
4365 {
4366 SEG test( track->GetStart(), track->GetEnd() );
4367 int dist = ( test.NearestPoint( aPosition ) - aPosition ).EuclideanNorm();
4368
4369 if( dist < min_d )
4370 {
4371 min_d = dist;
4372 return_track = track;
4373 }
4374 }
4375
4376 return return_track;
4377}
4378
4379
4380PAD* FindSnapPad( PCB_BASE_EDIT_FRAME* aFrame, const PCB_VIA* aVia, const VECTOR2I& aPosition, const LSET& aLayers,
4381 int aSnapRange )
4382{
4383 const LSET lset = aLayers;
4384 const BOX2I bbox = ViaSnapBoundingBox( *aVia, aPosition );
4385
4386 const KIGFX::PCB_VIEW& view = *aFrame->GetCanvas()->GetView();
4387 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
4388
4389 view.Query( bbox, items );
4390
4391 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
4392 {
4393 if( !it.first->IsBOARD_ITEM() )
4394 continue;
4395
4396 BOARD_ITEM& item = static_cast<BOARD_ITEM&>( *it.first );
4397
4398 if( item.Type() == PCB_PAD_T && ( item.GetLayerSet() & lset ).any() )
4399 {
4400 PAD& pad = static_cast<PAD&>( item );
4401
4402 if( pad.HitTest( aPosition, aSnapRange ) )
4403 return &pad;
4404 }
4405 }
4406
4407 return nullptr;
4408}
4409
4410
4411static PCB_SHAPE* FindSnapGraphic( PCB_BASE_EDIT_FRAME* aFrame, const PCB_VIA* aVia, const VECTOR2I& aPosition,
4412 const LSET& aLayers, int aSnapRange = 0 )
4413{
4414 const LSET lset = aLayers & LSET::AllCuMask();
4415 BOX2I bbox = ViaSnapBoundingBox( *aVia, aPosition );
4416
4417 std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
4418 KIGFX::PCB_VIEW* view = aFrame->GetCanvas()->GetView();
4419 PCB_LAYER_ID activeLayer = aFrame->GetActiveLayer();
4420 std::vector<PCB_SHAPE*> possible_shapes;
4421
4422 view->Query( bbox, items );
4423
4424 for( const KIGFX::VIEW::LAYER_ITEM_PAIR& it : items )
4425 {
4426 if( !it.first->IsBOARD_ITEM() )
4427 continue;
4428
4429 BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
4430
4431 if( !( item->GetLayerSet() & lset ).any() )
4432 continue;
4433
4434 if( item->Type() == PCB_SHAPE_T )
4435 {
4436 PCB_SHAPE* shape = static_cast<PCB_SHAPE*>( item );
4437
4438 if( shape->HitTest( aPosition, std::max( aVia->GetWidth( activeLayer ) / 2, aSnapRange ) ) )
4439 possible_shapes.push_back( shape );
4440 }
4441 }
4442
4443 PCB_SHAPE* return_shape = nullptr;
4444 int min_d = std::numeric_limits<int>::max();
4445
4446 for( PCB_SHAPE* shape : possible_shapes )
4447 {
4448 int dist = ( shape->GetPosition() - aPosition ).EuclideanNorm();
4449
4450 if( dist < min_d )
4451 {
4452 min_d = dist;
4453 return_shape = shape;
4454 }
4455 }
4456
4457 return return_shape;
4458}
4459
4460
4462{
4464 return 0;
4465
4466 if( m_inDrawingTool )
4467 return 0;
4468
4470
4471 struct VIA_PLACER : public INTERACTIVE_PLACER_BASE
4472 {
4474 PCB_GRID_HELPER m_gridHelper;
4475 std::shared_ptr<DRC_ENGINE> m_drcEngine;
4476 int m_drcEpsilon;
4477 int m_worstClearance;
4478 bool m_allowDRCViolations;
4479
4480 int sub_e( int aClearance )
4481 {
4482 return std::max( 0, aClearance - m_drcEpsilon );
4483 };
4484
4485 VIA_PLACER( PCB_BASE_EDIT_FRAME* aFrame ) :
4486 m_frame( aFrame ),
4487 m_gridHelper( aFrame->GetToolManager(), aFrame->GetMagneticItemsSettings() ),
4488 m_drcEngine( aFrame->GetBoard()->GetDesignSettings().m_DRCEngine ),
4489 m_drcEpsilon( aFrame->GetBoard()->GetDesignSettings().GetDRCEpsilon() ),
4490 m_worstClearance( 0 )
4491 {
4492 ROUTER_TOOL* router = m_frame->GetToolManager()->GetTool<ROUTER_TOOL>();
4493
4494 if( router )
4495 m_allowDRCViolations = router->Router()->Settings().AllowDRCViolations();
4496
4497 m_worstClearance = ComputeWorstViaClearance( aFrame, m_drcEngine.get() );
4498 }
4499
4500 virtual ~VIA_PLACER()
4501 {
4502 }
4503
4504 void sizeMicrovia( PCB_VIA* aVia )
4505 {
4506 NETCLASS* netClass = aVia->GetEffectiveNetClass();
4507
4508 aVia->SetSizeFromRules( netClass->GetuViaDiameter(), netClass->GetuViaDrill() );
4509 }
4510
4511 bool checkDRCViolation( PCB_VIA* aVia )
4512 {
4513 return CheckItemDRCViolation( aVia, m_frame, m_drcEngine.get(), m_worstClearance, m_drcEpsilon );
4514 }
4515
4516 PCB_TRACK* findTrack( const PCB_VIA* aVia, const VECTOR2I& aPosition ) const
4517 {
4518 return FindSnapTrack( m_frame, aVia, aPosition, aVia->GetLayerSet() );
4519 }
4520
4521 PAD* findPad( const PCB_VIA* aVia, const VECTOR2I& aPosition ) const
4522 {
4523 return FindSnapPad( m_frame, aVia, aPosition, aVia->GetLayerSet() );
4524 }
4525
4526 PCB_SHAPE* findGraphic( const PCB_VIA* aVia, const VECTOR2I& aPosition ) const
4527 {
4528 return FindSnapGraphic( m_frame, aVia, aPosition, aVia->GetLayerSet() );
4529 }
4530
4531 std::optional<int> selectPossibleNetsByPopupMenu( std::set<int>& aNetcodeList )
4532 {
4533 ACTION_MENU menu( true );
4534 const NETINFO_LIST& netInfo = m_board->GetNetInfo();
4535 std::map<int, int> menuIDNetCodeMap;
4536 int menuID = 1;
4537
4538 for( int netcode : aNetcodeList )
4539 {
4540 wxString menuText = netcode ? netInfo.GetNetItem( netcode )->GetNetname() : NO_NET;
4541
4542 if( menuID < 10 )
4543 {
4544#ifdef __WXMAC__
4545 menuText = wxString::Format( "%s\t",
4546 menuText );
4547#else
4548 menuText = wxString::Format( "&%d %s\t",
4549 menuID,
4550 menuText );
4551#endif
4552 }
4553 else
4554 {
4555 menuText = netInfo.GetNetItem( netcode )->GetNetname();
4556 }
4557
4558 menu.Add( menuText, menuID, BITMAPS::INVALID_BITMAP );
4559 menuIDNetCodeMap[ menuID ] = netcode;
4560 menuID++;
4561 }
4562
4563 menu.SetTitle( _( "Select Net:" ) );
4564 menu.DisplayTitle( true );
4565
4566 DRAWING_TOOL* drawingTool = m_frame->GetToolManager()->GetTool<DRAWING_TOOL>();
4567 drawingTool->SetContextMenu( &menu, CMENU_NOW );
4568
4569 int selectedNetCode = -1;
4570 bool cancelled = false;
4571 TOOL_EVENT* evt = nullptr;
4572
4573 while( ( evt = drawingTool->Wait() ) )
4574 {
4575 if( evt->Action() == TA_CHOICE_MENU_UPDATE )
4576 {
4577 evt->SetPassEvent();
4578 }
4579 else if( evt->Action() == TA_CHOICE_MENU_CHOICE )
4580 {
4581 std::optional<int> id = evt->GetCommandId();
4582
4583 // User has selected an item, so this one will be returned
4584 if( id && ( *id > 0 ) && ( *id < menuID ) )
4585 {
4586 selectedNetCode = menuIDNetCodeMap.at( *id );
4587 }
4588 // User has cancelled the menu (either by <esc> or clicking out of it),
4589 else
4590 {
4591 cancelled = true;
4592 }
4593 }
4594 else if( evt->Action() == TA_CHOICE_MENU_CLOSED )
4595 {
4596 break;
4597 }
4598 }
4599
4600 // A shutdown is not a net choice
4601 if( cancelled || !evt )
4602 return std::optional<int>();
4603 else
4604 return selectedNetCode;
4605 }
4606
4607 std::optional<int> findStitchedZoneNet( PCB_VIA* aVia )
4608 {
4609 const VECTOR2I position = aVia->GetPosition();
4610 PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
4611 std::set<int> netcodeList;
4612
4613 // See if there are any connections available on a high-contrast layer
4616 {
4617 if( aVia->GetLayerSet().test( m_frame->GetActiveLayer() ) )
4618 {
4619 for( ZONE* z : m_board->Zones() )
4620 {
4621 if( z->IsOnLayer( m_frame->GetActiveLayer() ) )
4622 {
4623 if( z->HitTestFilledArea( m_frame->GetActiveLayer(), position ) )
4624 netcodeList.insert( z->GetNetCode() );
4625 }
4626 }
4627 }
4628 }
4629
4630 // If there's only one, return it.
4631 if( netcodeList.size() == 1 )
4632 return *netcodeList.begin();
4633
4634 // See if there are any connections available on a visible layer
4635 LSET lset = LSET( m_board->GetVisibleLayers() & aVia->GetLayerSet() );
4636
4637 for( ZONE* z : m_board->Zones() )
4638 {
4639 if( z->GetIsRuleArea() )
4640 continue; // ignore rule areas
4641
4642 for( PCB_LAYER_ID layer : lset )
4643 {
4644 if( z->IsOnLayer( layer ) )
4645 {
4646 if( z->HitTestFilledArea( layer, position ) )
4647 netcodeList.insert( z->GetNetCode() );
4648 }
4649 }
4650 }
4651
4652 // If there's only one, return it.
4653 if( netcodeList.size() == 1 )
4654 return *netcodeList.begin();
4655
4656 if( netcodeList.size() > 1 )
4657 {
4658 // The net assignment is ambiguous. Let the user decide.
4659 return selectPossibleNetsByPopupMenu( netcodeList );
4660 }
4661 else
4662 {
4664 }
4665 }
4666
4667 void SnapItem( BOARD_ITEM *aItem ) override
4668 {
4669 m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
4670
4671 MAGNETIC_SETTINGS* settings = m_frame->GetMagneticItemsSettings();
4672 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
4673
4674 // When snapping, use the mouse position, not the item position, which may be
4675 // grid-snapped, so that we can get the cursor within snap-range of snap points.
4676 // If we don't get a snap, the via will be left as it is (i.e. maybe grid-snapped).
4677 KIGFX::VIEW_CONTROLS& viewControls = *m_frame->GetCanvas()->GetViewControls();
4678 const VECTOR2I position = viewControls.GetMousePosition();
4679
4680 if( settings->tracks != MAGNETIC_OPTIONS::NO_EFFECT && m_gridHelper.GetSnap() )
4681 {
4682 if( PCB_TRACK* track = findTrack( via, position ) )
4683 {
4684 SEG trackSeg( track->GetStart(), track->GetEnd() );
4685 VECTOR2I snap = m_gridHelper.AlignToSegment( position, trackSeg );
4686
4687 aItem->SetPosition( snap );
4688 return;
4689 }
4690 }
4691
4692 if( settings->pads != MAGNETIC_OPTIONS::NO_EFFECT && m_gridHelper.GetSnap() )
4693 {
4694 if( PAD* pad = findPad( via, position ) )
4695 {
4696 aItem->SetPosition( pad->GetPosition() );
4697 return;
4698 }
4699 }
4700
4701 if( settings->graphics && m_gridHelper.GetSnap() )
4702 {
4703 if( PCB_SHAPE* shape = findGraphic( via, position ) )
4704 {
4705 if( shape->IsAnyFill() )
4706 {
4707 // Is this shape something to be replaced by the via, or something to be
4708 // stitched by multiple vias? Use an area-based test to make a guess.
4709 SHAPE_POLY_SET poly;
4710 shape->TransformShapeToPolygon( poly, shape->GetLayer(), 0, ARC_LOW_DEF, ERROR_INSIDE );
4711 double shapeArea = poly.Area();
4712
4713 int R = via->GetWidth( shape->GetLayer() ) / 2;
4714 double viaArea = M_PI * R * R;
4715
4716 if( viaArea * 4 > shapeArea )
4717 aItem->SetPosition( shape->GetPosition() );
4718 }
4719 else
4720 {
4721 switch( shape->GetShape() )
4722 {
4723 case SHAPE_T::SEGMENT:
4724 {
4725 SEG seg( shape->GetStart(), shape->GetEnd() );
4726 VECTOR2I snap = m_gridHelper.AlignToSegment( position, seg );
4727 aItem->SetPosition( snap );
4728 break;
4729 }
4730
4731 case SHAPE_T::ARC:
4732 {
4733 if( ( shape->GetEnd() - position ).SquaredEuclideanNorm() <
4734 ( shape->GetStart() - position ).SquaredEuclideanNorm() )
4735 {
4736 aItem->SetPosition( shape->GetEnd() );
4737 }
4738 else
4739 {
4740 aItem->SetPosition( shape->GetStart() );
4741 }
4742
4743 break;
4744 }
4745
4746 case SHAPE_T::POLY:
4747 {
4748 if( !shape->IsPolyShapeValid() )
4749 {
4750 aItem->SetPosition( shape->GetPosition() );
4751 break;
4752 }
4753
4754 const SHAPE_POLY_SET& polySet = shape->GetPolyShape();
4755 std::optional<SEG> nearestSeg;
4756 int minDist = std::numeric_limits<int>::max();
4757
4758 for( int ii = 0; ii < polySet.OutlineCount(); ++ii )
4759 {
4760 const SHAPE_LINE_CHAIN& poly = polySet.Outline( ii );
4761
4762 for( int jj = 0; jj < poly.SegmentCount(); ++jj )
4763 {
4764 const SEG& seg = poly.GetSegment( jj );
4765 int dist = seg.Distance( position );
4766
4767 if( dist < minDist )
4768 {
4769 minDist = dist;
4770 nearestSeg = seg;
4771 }
4772 }
4773 }
4774
4775 if( nearestSeg )
4776 {
4777 VECTOR2I snap = m_gridHelper.AlignToSegment( position, *nearestSeg );
4778 aItem->SetPosition( snap );
4779 }
4780
4781 break;
4782 }
4783
4784 default:
4785 aItem->SetPosition( shape->GetPosition() );
4786 }
4787
4788 }
4789 }
4790
4791 }
4792 }
4793
4794 bool PlaceItem( BOARD_ITEM* aItem, BOARD_COMMIT& aCommit ) override
4795 {
4796 WX_INFOBAR* infobar = m_frame->GetInfoBar();
4797 PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
4798 VECTOR2I viaPos = via->GetPosition();
4799 PCB_TRACK* track = findTrack( via, via->GetPosition() );
4800 PAD* pad = findPad( via, via->GetPosition() );
4801 PCB_SHAPE* shape = findGraphic( via, via->GetPosition() );
4802
4803 if( track )
4804 {
4805 via->SetNetCode( track->GetNetCode() );
4806 via->SetIsFree( false );
4807 }
4808 else if( pad )
4809 {
4810 via->SetNetCode( pad->GetNetCode() );
4811 via->SetIsFree( false );
4812 }
4813 else if( shape && shape->GetNetCode() > 0 )
4814 {
4815 via->SetNetCode( shape->GetNetCode() );
4816 via->SetIsFree( false );
4817 }
4818 else
4819 {
4820 std::optional<int> netcode = findStitchedZoneNet( via );
4821
4822 if( !netcode.has_value() ) // user cancelled net disambiguation menu
4823 return false;
4824
4825 via->SetNetCode( netcode.value() );
4826 via->SetIsFree( via->GetNetCode() > 0 );
4827 }
4828
4829 // A rejected via moves on, so keep it out of the pointer-keyed rule caches
4830 via->SetFlags( ROUTER_TRANSIENT );
4831
4832 // Rules can depend on the net and position, which are only final here
4833 if( via->GetViaType() == VIATYPE::MICROVIA )
4834 sizeMicrovia( via );
4835
4836 bool violatesDRC = checkDRCViolation( via );
4837 via->ClearFlags( ROUTER_TRANSIENT );
4838
4839 if( violatesDRC )
4840 {
4841 m_frame->ShowInfoBarError( _( "Via location violates DRC." ), true,
4842 WX_INFOBAR::MESSAGE_TYPE::DRC_VIOLATION );
4843
4844 if( !m_allowDRCViolations )
4845 return false;
4846 }
4847 else
4848 {
4849 if( infobar->GetMessageType() == WX_INFOBAR::MESSAGE_TYPE::DRC_VIOLATION )
4850 infobar->Dismiss();
4851 }
4852
4853 aCommit.Add( via );
4854
4855 // If the user explicitly disables snap (using shift), then don't break the tracks.
4856 // This will prevent PNS from being able to connect the via and track but
4857 // it is explicitly requested by the user
4858 if( track && m_gridHelper.GetSnap() )
4859 {
4860 VECTOR2I trackStart = track->GetStart();
4861 VECTOR2I trackEnd = track->GetEnd();
4862 SEG trackSeg( trackStart, trackEnd );
4863
4864 if( viaPos == trackStart || viaPos == trackEnd )
4865 return true;
4866
4867 if( !trackSeg.Contains( viaPos ) )
4868 return true;
4869
4870 aCommit.Modify( track );
4871 track->SetStart( trackStart );
4872 track->SetEnd( viaPos );
4873
4874 PCB_TRACK* newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
4875 newTrack->ResetUuidDirect();
4876
4877 newTrack->SetStart( viaPos );
4878 newTrack->SetEnd( trackEnd );
4879 aCommit.Add( newTrack );
4880 }
4881
4882 return true;
4883 }
4884
4885 std::unique_ptr<BOARD_ITEM> CreateItem() override
4886 {
4887 BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
4888 PCB_VIA* via = new PCB_VIA( m_board );
4889
4890 via->SetNetCode( 0 );
4891 via->SetViaType( bds.m_CurrentViaType );
4892 via->SetPadstackMode( PADSTACK::MODE::NORMAL );
4893
4894 if( via->GetViaType() == VIATYPE::THROUGH )
4895 {
4896 via->SetLayerPair( B_Cu, F_Cu );
4897 }
4898 else
4899 {
4900 PCB_LAYER_ID first_layer = m_frame->GetActiveLayer();
4901 PCB_LAYER_ID last_layer;
4902
4903 // prepare switch to new active layer:
4904 if( first_layer != m_frame->GetScreen()->m_Route_Layer_TOP )
4905 last_layer = m_frame->GetScreen()->m_Route_Layer_TOP;
4906 else
4907 last_layer = m_frame->GetScreen()->m_Route_Layer_BOTTOM;
4908
4909 via->SetLayerPair( first_layer, last_layer );
4910 }
4911
4912 if( via->GetViaType() == VIATYPE::MICROVIA )
4913 {
4914 sizeMicrovia( via );
4915 }
4916 else
4917 {
4918 via->SetWidth( PADSTACK::ALL_LAYERS, bds.GetCurrentViaSize() );
4919 via->SetDrill( bds.GetCurrentViaDrill() );
4920 }
4921
4922 return std::unique_ptr<BOARD_ITEM>( via );
4923 }
4924 };
4925
4926 VIA_PLACER placer( frame() );
4927
4928 SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::VIA );
4929
4930 doInteractiveItemPlacement( aEvent, &placer, _( "Place via" ), IPO_REPEAT | IPO_SINGLE_CLICK );
4931
4932 return 0;
4933}
4934
4935
4936int DRAWING_TOOL::runSimpleShapeDraw( const TOOL_EVENT& aEvent, SHAPE_T aShapeType, MODE aMode,
4937 const wxString& aCommitLabel,
4938 std::function<bool( const TOOL_EVENT&, PCB_SHAPE**, std::optional<VECTOR2D> )> aDrawer )
4939{
4940 if( m_isFootprintEditor && !m_frame->GetModel() )
4941 return 0;
4942
4943 if( m_inDrawingTool )
4944 return 0;
4945
4947
4948 BOARD_ITEM* parent = m_frame->GetModel();
4949 BOARD_COMMIT commit( m_frame );
4950 SCOPED_DRAW_MODE scopedDrawMode( m_mode, aMode );
4951 std::optional<VECTOR2D> startingPoint;
4952
4953 auto makeShape =
4954 [&]()
4955 {
4956 PCB_SHAPE* s = new PCB_SHAPE( parent );
4957 s->SetShape( aShapeType );
4958 s->SetFilled( false );
4959 s->SetFlags( IS_NEW );
4960 return s;
4961 };
4962
4963 PCB_SHAPE* shape = makeShape();
4964
4965 if( aEvent.HasPosition() )
4966 startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
4967
4968 TOOL_EVENT originalEvent = aEvent; // This can change out from under us when the event loop runs
4969 SCOPED_TOOL_PUSHER raii( m_frame, originalEvent );
4970
4971 Activate();
4972
4973 while( aDrawer( originalEvent, &shape, startingPoint ) )
4974 {
4975 if( shape )
4976 {
4977 commit.Add( shape );
4978
4979 std::vector<PCB_CONSTRAINT*> snaps;
4980
4981 if( GetAutoConstraints() )
4982 snaps = stageAutoConstraints( m_board, shape, parent, commit, false );
4983
4984 commit.Push( aCommitLabel );
4986 m_toolMgr->RunAction<EDA_ITEM*>( ACTIONS::selectItem, shape );
4987 }
4988
4989 shape = makeShape();
4990 startingPoint = std::nullopt;
4991 }
4992
4993 return 0;
4994}
4995
4996
4997const unsigned int DRAWING_TOOL::WIDTH_STEP = pcbIUScale.mmToIU( 0.1 );
4998
4999
5001{
5002 // clang-format off
5040
5045 // clang-format on
5046}
@ ERROR_INSIDE
ARC_DRAW_MODE
Construction sequence used when interactively drawing a circular arc.
@ TANGENT
End only; start and departure direction come from a tangent seed.
ARC_DRAW_MODE StartArcDrawMode(const TOOL_EVENT &aEvent, APP_SETTINGS_BASE *aSettings, SHAPE_DRAW_BEHAVIOR &aBehavior)
Pick the mode a Draw Arc action starts in and apply it to aBehavior.
std::optional< ARC_TANGENT_SEED > ArcTangentSeedNear(const EDA_SHAPE &aShape, const VECTOR2I &aNear)
Seed the next tangent arc from whichever endpoint of aShape lies nearest aNear.
std::optional< ARC_TANGENT_SEED > ArcTangentSeedAt(const SEG &aSegment, const VECTOR2I &aPoint, int aTolerance)
Tangent seed for a point on or near a segment, arc or Bezier shape.
constexpr int ARC_SEED_PICK_PIXELS
Screen distance in pixels within which a click picks a graphic to start a tangent arc from.
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
constexpr int ARC_LOW_DEF
Definition base_units.h:143
wxImage ConvertImageToBilevel(const wxImage &aImage, double aThreshold, bool aInvert)
Reduce an image to pure black and white.
SHAPE_POLY_SET TraceBilevelImage(const wxImage &aBilevelImage, double aScaleX, double aScaleY, REPORTER &aReporter)
Trace the black regions of a black and white image into filled polygons.
@ 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:914
constexpr BOX2I KiROUND(const BOX2D &aBoxD)
Definition box2.h:982
static TOOL_ACTION cancelInteractive
Definition actions.h:68
static TOOL_ACTION arcPosture
Definition actions.h:269
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:271
static TOOL_ACTION cursorDblClick
Definition actions.h:177
static TOOL_ACTION undo
Definition actions.h:71
static TOOL_ACTION cycleArcDrawMode
Definition actions.h:270
static TOOL_ACTION activatePointEditor
Definition actions.h:268
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 finishInteractive
Definition actions.h:69
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.
APP_SETTINGS_BASE is a settings class that should be derived for each standalone KiCad application.
ARC_DRAW_MODE m_ArcDrawMode
Construction sequence last used by the arc drawing tool in this application.
Interactive arc drawing behaviour.
Arc drawing mode and tangent seed bookkeeping for one pass of an editor's managed-shape draw loop.
void PlacePoint(const VECTOR2I &aMouse, const VECTOR2I &aPos)
Lock in a point, or seed a tangent arc from the graphic under it when it would be the arc's first poi...
void RemoveLastPoint()
Undo the last point. A seed taken from a click is undone too, one from a chain is kept.
bool HandleModeEvent(const TOOL_EVENT &aEvent)
Handle a mode or cycle action; return true if aEvent was one.
Interactive bezier drawing behaviour: start -> control1 -> end -> control2.
This class handle bitmap images in KiCad.
Definition bitmap_base.h:47
void BeginPreviewImage(const wxImage &aImage)
Show finished pixels in place of the image, without disturbing the scale or the resolution.
void EndPreviewImage()
Put back the image a preview stood in for, and do nothing if none is showing.
wxImage * GetImageData()
Definition bitmap_base.h:66
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,...
virtual NETCLASS * GetEffectiveNetClass() const
Return the NETCLASS for this item.
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:85
void ResetUuidDirect()
Definition board_item.h:278
virtual std::shared_ptr< SHAPE_SEGMENT > GetEffectiveHoleShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, DRC_CONSTRAINT_T aUsage=NULL_CONSTRAINT) const
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition board_item.h:383
FOOTPRINT * GetParentFootprint() const
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition board_item.h:355
virtual std::shared_ptr< SHAPE > GetEffectiveShape(PCB_LAYER_ID aLayer=UNDEFINED_LAYER, FLASHING aFlash=FLASHING::DEFAULT, DRC_CONSTRAINT_T aUsage=NULL_CONSTRAINT) const
Some pad shapes can be complex (rounded/chamfered rectangle), even without considering custom shapes.
virtual bool HasHole() const
Definition board_item.h:208
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:411
const FOOTPRINTS & Footprints() const
Definition board.h:465
wxString GetDesignRulesPath() const
Return the absolute path to the design rules file for this board.
Definition board.cpp:437
BOARD_DESIGN_SETTINGS & GetDesignSettings()
Definition board.cpp:1290
constexpr BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition box2.h:553
constexpr size_type GetWidth() const
Definition box2.h:211
constexpr Vec Centre() const
Definition box2.h:94
constexpr size_type GetHeight() const
Definition box2.h:212
constexpr coord_type GetLeft() const
Definition box2.h:225
constexpr bool Contains(const Vec &aPoint) const
Definition box2.h:165
constexpr void Move(const Vec &aMoveVector)
Move the rectangle by the aMoveVector.
Definition box2.h:135
constexpr coord_type GetRight() const
Definition box2.h:214
constexpr coord_type GetTop() const
Definition box2.h:226
constexpr coord_type GetBottom() const
Definition box2.h:219
static const COLOR4D UNSPECIFIED
For legacy support; used as a value to indicate color hasn't been set yet.
Definition color4d.h:400
COMMIT & Remove(EDA_ITEM *aItem, BASE_SCREEN *aScreen=nullptr)
Remove a new item from the model.
Definition commit.h:86
bool Empty() const
Definition commit.h:145
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 AddCheckItem(const TOOL_ACTION &aAction, const SELECTION_CONDITION &aCondition, int aOrder=ANY_ORDER)
Add a checked menu entry to run a TOOL_ACTION on selected items.
void AddMenu(ACTION_MENU *aMenu, const SELECTION_CONDITION &aCondition=SELECTION_CONDITIONS::ShowAlways, int aOrder=ANY_ORDER)
Add a submenu to the menu.
DIALOG_BARCODE_PROPERTIES, derived from DIALOG_BARCODE_PROPERTIES_BASE, created by wxFormBuilder.
Ask for the settings used to trace a placed image into outlines.
double GetThreshold() const
Brightness cut deciding which pixels are traced, from 0 to 1.
int GetLayerSelection() const
Index into the layer names given to the constructor, or -1 when there were none.
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 NONE if not currently in any drawing mode.
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)
int PlaceSubGrid(const TOOL_EVENT &aEvent)
Place a grid item on the board.
KIGFX::VIEW_CONTROLS * m_controls
friend class ZONE_CREATE_HELPER
KIGFX::VIEW * m_view
STROKE_PARAMS m_stroke
int DrawZone(const TOOL_EVENT &aEvent)
Start interactively drawing a zone.
int DrawBezier(const TOOL_EVENT &aEvent)
Start interactively drawing a bezier curve.
int DrawLine(const TOOL_EVENT &aEvent)
Start interactively drawing a line.
VECTOR2I snapEventPosition(const TOOL_EVENT &aEvent)
Snap a tool event's position the way a click is snapped.
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
MODE
The possible drawing modes of DRAWING_TOOL.
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
Set up handlers for various 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)
Clamp 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.
SHAPE_DRAW_RESULT drawManagedShape(const TOOL_EVENT &aTool, std::unique_ptr< PCB_SHAPE > &aGraphic, SHAPE_DRAW_BEHAVIOR &aBehavior, const std::vector< VECTOR2D > &aInitialPts, const std::optional< ARC_TANGENT_SEED > &aTangentSeed=std::nullopt, VECTOR2I *aLastClick=nullptr)
Run the interactive drawing event loop for a shape, driven by a SHAPE_DRAW_BEHAVIOR.
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
int DrawEllipse(const TOOL_EVENT &aEvent)
int PlaceMicroviaStack(const TOOL_EVENT &aEvent)
Place a stacked or staggered microvia stack.
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 ConvertImageToOutlines(const TOOL_EVENT &aEvent)
Trace a placed reference image into filled graphics and replace it with them.
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)
Get a source zone item for an action that takes an existing zone into account (for example a cutout o...
int m_DisallowFlags
Definition drc_rule.h:205
SEVERITY GetSeverity() const
Definition drc_rule.h:181
const MINOPTMAX< int > & GetValue() const
Definition drc_rule.h:129
Design Rule Checker object that performs all the DRC tests.
Definition drc_engine.h:129
DRC_CONSTRAINT EvalRules(DRC_CONSTRAINT_T aConstraintType, const BOARD_ITEM *a, const BOARD_ITEM *b, PCB_LAYER_ID aLayer, REPORTER *aReporter=nullptr)
bool QueryWorstConstraint(DRC_CONSTRAINT_T aRuleId, DRC_CONSTRAINT &aConstraint, bool aUnconditionalOnly=false)
void InitEngine(const wxFileName &aRulePath)
Initialize the DRC engine.
double Sin() const
Definition eda_angle.h:177
double Cos() const
Definition eda_angle.h:196
A base class for most all the KiCad significant classes used in schematics and boards.
Definition eda_item.h:98
virtual VECTOR2I GetPosition() const
Definition eda_item.h:348
virtual void ClearEditFlags()
Definition eda_item.h:178
virtual void SetPosition(const VECTOR2I &aPos)
Definition eda_item.h:349
virtual const BOX2I GetBoundingBox() const
Return the orthogonal bounding box of this object for display purposes.
Definition eda_item.cpp:270
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition eda_item.h:158
const KIID m_Uuid
Definition eda_item.h:599
KICAD_T Type() const
Returns the type of object.
Definition eda_item.h:110
void ClearSelected()
Definition eda_item.h:153
void SetSelected()
Definition eda_item.h:150
int GetEllipseMinorRadius() const
Definition eda_shape.h:403
const VECTOR2I & GetEllipseCenter() const
Definition eda_shape.h:385
int GetEllipseMajorRadius() const
Definition eda_shape.h:394
EDA_ANGLE GetEllipseRotation() const
Definition eda_shape.h:412
SHAPE_T GetShape() const
Definition eda_shape.h:177
virtual void SetFilled(bool aFlag)
Definition eda_shape.h:144
const VECTOR2I & GetEnd() const
Return the ending point of the graphic.
Definition eda_shape.h:333
double GetLength() const
virtual bool IsVisible() const
Definition eda_text.h:226
virtual void SetTextPos(const VECTOR2I &aPoint)
Definition eda_text.cpp:556
void SetMirrored(bool isMirrored)
Definition eda_text.cpp:354
void SetItalic(bool aItalic)
Set the text to be italic - this will also update the font if needed.
Definition eda_text.cpp:290
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
VECTOR2D GetVisibleGridSize() const
Return the visible grid size in x and y directions.
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const override
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition pcb_view.cpp:87
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1) override
Add a VIEW_ITEM to the view.
Definition pcb_view.cpp:53
virtual void Remove(VIEW_ITEM *aItem) override
Remove a VIEW_ITEM from the view.
Definition pcb_view.cpp:70
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
Hold a (potentially large) number of VIEW_ITEMs and renders them on a graphics device provided by the...
Definition view.h:68
int Query(const BOX2I &aRect, std::vector< LAYER_ITEM_PAIR > &aResult) const
Find all visible items that touch or are within the rectangle aRect.
Definition view.cpp:744
GAL * GetGAL() const
Return the GAL this view is using to draw graphical primitives.
Definition view.h:224
VECTOR2D ToWorld(const VECTOR2D &aCoord, bool aAbsolute=true) const
Converts a screen space point/vector to a point/vector in world space coordinates.
Definition view.cpp:791
std::pair< VIEW_ITEM *, int > LAYER_ITEM_PAIR
Definition view.h:72
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
A collection of nets and the parameters used to route or test these nets.
Definition netclass.h:44
int GetuViaDrill() const
Definition netclass.h:182
int GetuViaDiameter() const
Definition netclass.h:174
const wxString & GetNetname() const
Definition netinfo.h:110
Container for NETINFO_ITEM elements, which are the nets.
Definition netinfo.h:232
static const int ORPHANED
Constant that forces initialization of a netinfo item to the NETINFO_ITEM ORPHANED (typically -1) whe...
Definition netinfo.h:285
NETINFO_ITEM * GetNetItem(int aNetCode) const
@ NORMAL
Shape is the same on all layers.
Definition padstack.h:170
static constexpr PCB_LAYER_ID ALL_LAYERS
! The layer identifier to use for the single defintion on normal padstacks
Definition padstack.h:179
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 drawArcStartEndCenter
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 drawViaStitchArea
static TOOL_ACTION drawArcStartDirEnd
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 placeSubGrid
Grid Item.
static TOOL_ACTION clearHighlight
static TOOL_ACTION spacingDecrease
static TOOL_ACTION convertImageToOutlines
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 drawArcCenter
static TOOL_ACTION drawArcTangent
static TOOL_ACTION ddImportGraphics
static TOOL_ACTION placeBarcode
static TOOL_ACTION placePoint
static TOOL_ACTION closeOutline
static TOOL_ACTION amplIncrease
static const TOOL_ACTION & DrawArcForMode(ARC_DRAW_MODE aMode)
Tool action that starts the arc tool in the given mode.
static TOOL_ACTION amplDecrease
static TOOL_ACTION lengthTunerSettings
static TOOL_ACTION spacingIncrease
static TOOL_ACTION ddPlaceReferenceImage
static TOOL_ACTION drawLine
static TOOL_ACTION placeViaStack
static TOOL_ACTION drawAlignedDimension
static TOOL_ACTION drawArcStartEndMid
static TOOL_ACTION drawZone
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()
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
virtual PCB_LAYER_ID GetActiveLayer() const
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.
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the KIGFX::VIEW instance used in the panel.
The main frame for Pcbnew.
VECTOR2I AlignToSegment(const VECTOR2I &aPoint, const SEG &aSeg)
void SetPosition(const VECTOR2I &aPos) override
void SetOrientation(const EDA_ANGLE &aAngle)
void SetExtent(const VECTOR2I &aExtent)
VECTOR2I GetPosition() const override
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:101
void CalcEdit(const VECTOR2I &aPosition)
Definition pcb_shape.h:95
void SetEditState(int aState)
Definition pcb_shape.h:107
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:162
void SetShape(SHAPE_T aShape) override
Definition pcb_shape.h:212
void SetPosition(const VECTOR2I &aPos) override
Definition pcb_shape.h:77
void SetEllipseEndAngle(const EDA_ANGLE &aA) override
void SetEnd(const VECTOR2I &aEnd) override
void SetPolyShape(const SHAPE_POLY_SET &aShape) 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:88
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:78
void SetEllipseMajorRadius(int aR) override
void SetTextThickness(int aWidth) override
The TextThickness is that set by the user.
Definition pcb_text.cpp:512
void SetTextSize(VECTOR2I aNewSize, bool aEnforceMinTextSize=true) override
Definition pcb_text.cpp:484
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:94
void SetStart(const VECTOR2I &aStart)
Definition pcb_track.h:97
EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition pcb_track.cpp:73
const VECTOR2I & GetStart() const
Definition pcb_track.h:98
const VECTOR2I & GetEnd() const
Definition pcb_track.h:95
virtual int GetWidth() const
Definition pcb_track.h:92
VECTOR2I GetPosition() const override
Definition pcb_track.h:603
void SetSizeFromRules(int aDiameter, int aDrill)
Give the via a uniform padstack sized from its diameter and hole rules.
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:239
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.
A REFERENCE_IMAGE is a wrapper around a BITMAP_IMAGE that is displayed in an editor as a reference fo...
BITMAP_BASE & MutableImage() const
Only use this if you really need to modify the underlying image.
EDA_ANGLE GetOrientation() const
VECTOR2I GetPosition() const
VECTOR2I GetSize() const
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:668
bool Contains(const SEG &aSeg) const
Definition seg.h:321
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 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 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.
void Rotate(const EDA_ANGLE &aAngle, const VECTOR2I &aCenter={ 0, 0 }) override
Rotate all vertices by a given angle.
double Area()
Return the area of this poly set.
bool IsEmpty() const
Return true if the set is empty (no polygons at all)
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.
void Move(const VECTOR2I &aVector) override
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
Return the instance of KIGFX::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
bool IsCancelInteractive() const
Indicate the event should restart/end an ongoing interactive tool's event loop (eg esc key,...
TOOL_ACTIONS Action() const
Returns more specific information about the type of an event.
Definition tool_event.h:246
bool IsActivate() const
Definition tool_event.h:341
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 IsClick(int aButtonMask=BUT_ANY) const
int Modifier(int aMask=MD_MODIFIER_MASK) const
Return information about key modifiers state (Ctrl, Alt, etc.).
Definition tool_event.h:362
bool IsMoveTool() const
Indicate if the event is from one of the move tools.
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:484
bool IsDblClick(int aButtonMask=BUT_ANY) const
bool IsPointEditor() const
Indicate if the event is from one of the point editors.
std::optional< int > GetCommandId() const
Definition tool_event.h:544
void SetPassEvent(bool aPass=true)
Definition tool_event.h:252
bool IsMotion() const
Definition tool_event.h:326
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().
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition vector2d.h:281
void update() override
Update menu state stub.
OPT_TOOL_EVENT eventHandler(const wxMenuEvent &aEvent) override
Event handler stub.
ACTION_MENU * create() const override
Return an instance of this class. It has to be overridden in inheriting classes.
A modified version of the wxInfoBar class that allows us to:
Definition wx_infobar.h:76
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
MESSAGE_TYPE GetMessageType() const
Definition wx_infobar.h:99
A wrapper for reporting to a wxString object.
Definition reporter.h:242
void OnGeometryChange(const POLYGON_GEOM_MANAGER &aMgr) override
Interface for receiving POLYGON_GEOM_MANAGER update.
static bool IsZoneFillAction(const TOOL_EVENT *aEvent)
Handle a list of polygons defining a copper zone.
Definition zone.h:70
bool GetIsRuleArea() const
Accessors to parameters used in Rule Area zones:
Definition zone.h:832
bool GetDoNotAllowVias() const
Definition zone.h:850
bool GetDoNotAllowTracks() const
Definition zone.h:851
SHAPE_POLY_SET * Outline()
Definition zone.h:421
SHAPE_POLY_SET GetBoardOutline() const
Definition zone.cpp:951
virtual bool IsOnLayer(PCB_LAYER_ID) const override
Test to see if this object is on the given layer.
Definition zone.cpp:806
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition zone.h:133
PCB_LAYER_ID GetFirstLayer() const
Definition zone.cpp:630
This file is part of the common library.
std::vector< ENDPOINT_BINDING > SelectEndpointBindings(const BOARD *aBoard, const KIID &aItem, const VECTOR2I &aStart, const std::optional< VECTOR2I > &aEnd, double aMaxDist, PCB_LAYER_ID aLayer)
Choose the coincident bindings a freshly drawn item endpoints should take so it tracks the geometry i...
std::vector< AUTO_CONSTRAINT > SelectShapeAutoConstraints(const 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.
bool ConstraintIsDuplicateOnBoard(const BOARD *aBoard, const PCB_CONSTRAINT *aConstraint)
True if the board or one of its footprints already carries an equal constraint.
std::optional< KIID > SelectRadialDimensionTarget(const 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.
std::vector< PCB_SHAPE * > CollectConstraintShapes(const BOARD *aBoard, PCB_LAYER_ID aLayer)
Every PCB_SHAPE on a visible layer (drawings plus footprint graphics) – the candidates constraints ca...
@ MEASURE
Definition cursors.h:65
@ MOVING
Definition cursors.h:45
@ ARROW
Definition cursors.h:43
@ BULLSEYE
Definition cursors.h:55
@ PENCIL
Definition cursors.h:49
#define UNSPECIFIED
PAD * FindSnapPad(PCB_BASE_EDIT_FRAME *aFrame, const PCB_VIA *aVia, const VECTOR2I &aPosition, const LSET &aLayers, int aSnapRange)
int ComputeWorstViaClearance(PCB_BASE_EDIT_FRAME *aFrame, DRC_ENGINE *aEngine)
Shared placement DRC checks, used by the plain via tool and the microvia stack tool so both block pla...
static void snapAutoConstraints(PCB_BASE_EDIT_FRAME *aFrame, BOARD *aBoard, const std::vector< PCB_CONSTRAINT * > &aConstraints)
static bool ItemHasDRCViolation(BOARD_CONNECTED_ITEM *aItem, BOARD_ITEM *aOther, DRC_ENGINE *aEngine, int aEpsilon)
static PCB_SHAPE * FindSnapGraphic(PCB_BASE_EDIT_FRAME *aFrame, const PCB_VIA *aVia, const VECTOR2I &aPosition, const LSET &aLayers, int aSnapRange=0)
static void updateSegmentFromGeometryMgr(const KIGFX::PREVIEW::TWO_POINT_GEOMETRY_MANAGER &aMgr, PCB_SHAPE *aGraphic)
int ViaSnapRange(PCB_BASE_EDIT_FRAME *aFrame)
Shared placement snapping, used by the plain via tool and the microvia stack tool.
static std::vector< PCB_CONSTRAINT * > stageAutoConstraints(BOARD *aBoard, PCB_SHAPE *aShape, BOARD_ITEM *aParent, BOARD_COMMIT &aCommit, bool aAxisConstraint)
static BOX2I ViaSnapBoundingBox(const PCB_VIA &aVia, const VECTOR2I &aPosition)
Get the bounding box the via would have if placed at the given position (the via's bounding box is re...
bool CheckItemDRCViolation(BOARD_CONNECTED_ITEM *aItem, PCB_BASE_EDIT_FRAME *aFrame, DRC_ENGINE *aEngine, int aWorstClearance, int aEpsilon)
PCB_TRACK * FindSnapTrack(PCB_BASE_EDIT_FRAME *aFrame, const PCB_VIA *aVia, const VECTOR2I &aPosition, const LSET &aLayers, int aSnapRange)
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
@ CLEARANCE_CONSTRAINT
@ HOLE_CLEARANCE_CONSTRAINT
#define _(s)
static constexpr EDA_ANGLE ANGLE_0
Definition eda_angle.h:448
static constexpr EDA_ANGLE ANGLE_90
Definition eda_angle.h:450
@ RADIANS_T
Definition eda_angle.h:31
static constexpr EDA_ANGLE ANGLE_360
Definition eda_angle.h:454
#define IS_NEW
New item, just created.
#define ROUTER_TRANSIENT
transient items that should NOT be cached
#define IS_MOVING
Item being moved.
SHAPE_T
Definition eda_shape.h:54
@ ELLIPSE
Definition eda_shape.h:62
@ SEGMENT
Definition eda_shape.h:56
@ RECTANGLE
Use RECTANGLE instead of RECT to avoid collision in a Windows header.
Definition eda_shape.h:57
@ ELLIPSE_ARC
Definition eda_shape.h:63
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)
Clamp a vector to values that can be negated, respecting numeric limits of coordinates data type with...
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.
@ GRID_TEXT
Definition grid_helper.h:62
@ GRID_GRAPHICS
Definition grid_helper.h:63
static wxString ImageFileWildcard()
std::string source
std::string message
bool IsCopperLayer(int aLayerId)
Test whether a layer is a copper layer.
Definition layer_ids.h:741
@ 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
@ LAYER_SUBGRIDS
Routing/placement subgrids (PCB_GRID_ITEM) visibility and color.
Definition layer_ids.h:323
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:640
EDA_ANGLE abs(const EDA_ANGLE &aAngle)
Definition eda_angle.h:437
#define NO_NET
Definition netinfo.h:42
ZONE_MODE
Definition pcb_actions.h:34
@ SIMILAR
Add a new zone with the same settings as an existing one.
Definition pcb_actions.h:37
@ GRAPHIC_POLYGON
Definition pcb_actions.h:38
@ ADD
Add a new zone/keepout with fresh settings.
Definition pcb_actions.h:35
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:22
@ ID_POPUP_PCB_SELECT_VIASIZE1
Definition pcbnew_id.h:41
@ ID_POPUP_PCB_SELECT_VIASIZE16
Definition pcbnew_id.h:56
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:39
std::vector< EDA_ITEM * > EDA_ITEMS
SHAPE_DRAW_RESULT
Outcome of an interactive shape drawing loop, telling the caller how to continue.
@ NEXT_SHAPE
Commit the shape, if any, and start drawing another.
@ FINISHED
The user finished; commit the shape, if any, and stop drawing.
@ CANCELLED
The user cancelled; nothing to commit, stop drawing.
const int scale
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)
VECTOR2I position
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
Are we creating copper theiving zone?
ZONE_MODE m_mode
The zone mode to operate in.
bool m_keepout
Should create a keepout zone?
ZONE * m_sourceZone
Zone settings source (for similar and cutout zones)
PCB_LAYER_ID m_layer
Layer to begin drawing.
IbisParser parser & reporter
VECTOR2I center
int radius
VECTOR2I end
int clearance
wxString result
Test unit parsing edge cases and error handling.
int delta
@ GR_TEXT_H_ALIGN_LEFT
@ GR_TEXT_V_ALIGN_BOTTOM
@ GR_TEXT_V_ALIGN_TOP
#define M_PI
@ TA_CHOICE_MENU_CHOICE
Context menu choice.
Definition tool_event.h:94
@ TA_CHOICE_MENU_UPDATE
Context menu update.
Definition tool_event.h:90
@ TA_CHOICE_MENU_CLOSED
Context menu is closed, no matter whether anything has been chosen or not.
Definition tool_event.h:97
@ CMENU_NOW
Right now (after TOOL_INTERACTIVE::SetContextMenu).
Definition tool_event.h:152
std::optional< TOOL_EVENT > OPT_TOOL_EVENT
Definition tool_event.h:652
@ 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:173
KICAD_T
The set of class identification values stored in EDA_ITEM::m_structType.
Definition typeinfo.h:70
@ PCB_SHAPE_T
class PCB_SHAPE, a segment not on copper layers
Definition typeinfo.h:80
@ PCB_DIM_ORTHOGONAL_T
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition typeinfo.h:98
@ PCB_DIM_LEADER_T
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition typeinfo.h:95
@ PCB_DIM_CENTER_T
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition typeinfo.h:96
@ PCB_GROUP_T
class PCB_GROUP, a set of BOARD_ITEMs
Definition typeinfo.h:103
@ PCB_ZONE_T
class ZONE, a copper pour area
Definition typeinfo.h:100
@ PCB_TEXT_T
class PCB_TEXT, text on a layer
Definition typeinfo.h:84
@ PCB_REFERENCE_IMAGE_T
class PCB_REFERENCE_IMAGE, bitmap on a layer
Definition typeinfo.h:81
@ PCB_FIELD_T
class PCB_FIELD, text associated with a footprint property
Definition typeinfo.h:82
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint
Definition typeinfo.h:78
@ PCB_DIM_ALIGNED_T
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition typeinfo.h:94
@ PCB_PAD_T
class PAD, a pad in a footprint
Definition typeinfo.h:79
@ PCB_ARC_T
class PCB_ARC, an arc track segment on a copper layer
Definition typeinfo.h:90
@ PCB_DIMENSION_T
class PCB_DIMENSION_BASE: abstract dimension meta-type
Definition typeinfo.h:92
@ PCB_TRACE_T
class PCB_TRACK, a track segment (segment on a copper layer)
Definition typeinfo.h:88
@ PCB_DIM_RADIAL_T
class PCB_DIM_RADIAL, a radius or diameter dimension
Definition typeinfo.h:97
VECTOR2< int32_t > VECTOR2I
Definition vector2d.h:708
VECTOR2< double > VECTOR2D
Definition vector2d.h:707
Definition of file extensions used in Kicad.