KiCad PCB EDA Suite
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 (C) 2018-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  * @author Maciej Suminski <maciej.suminski@cern.ch>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25 
26 #include "drawing_tool.h"
27 
28 
32 #include <geometry/shape_segment.h>
36 #include <ratsnest/ratsnest_data.h>
37 #include <router/router_tool.h>
38 #include <tool/tool_manager.h>
39 #include <tools/pcb_actions.h>
41 #include <tools/pcb_grid_helper.h>
43 #include <tools/tool_event_utils.h>
45 #include <view/view.h>
47 #include <widgets/infobar.h>
48 
49 #include <bitmaps.h>
50 #include <board.h>
51 #include <board_commit.h>
52 #include <board_design_settings.h>
53 #include <confirm.h>
54 #include <footprint.h>
55 #include <fp_shape.h>
56 #include <macros.h>
57 #include <painter.h>
58 #include <pcb_edit_frame.h>
59 #include <pcb_group.h>
60 #include <pcb_text.h>
61 #include <pcb_dimension.h>
62 #include <pcbnew_id.h>
64 #include <scoped_set_reset.h>
65 #include <status_popup.h>
66 #include <string_utils.h>
67 #include <zone.h>
68 
69 
71 
72 
73 class VIA_SIZE_MENU : public ACTION_MENU
74 {
75 public:
77  ACTION_MENU( true )
78  {
80  SetTitle( _( "Select Via Size" ) );
81  }
82 
83 protected:
84  ACTION_MENU* create() const override
85  {
86  return new VIA_SIZE_MENU();
87  }
88 
89  void update() override
90  {
92  EDA_UNITS units = frame->GetUserUnits();
94  bool useIndex = !bds.m_UseConnectedTrackWidth
95  && !bds.UseCustomTrackViaSize();
96  wxString msg;
97 
98  Clear();
99 
100  Append( ID_POPUP_PCB_SELECT_CUSTOM_WIDTH, _( "Use Custom Values..." ),
101  _( "Specify custom track and via sizes" ), wxITEM_CHECK );
103 
104  AppendSeparator();
105 
106  for( unsigned i = 1; i < bds.m_ViasDimensionsList.size(); i++ )
107  {
109 
110  if( via.m_Drill > 0 )
111  {
112  msg.Printf( _("Via %s, drill %s" ),
113  MessageTextFromValue( units, via.m_Diameter ),
114  MessageTextFromValue( units, via.m_Drill ) );
115  }
116  else
117  {
118  msg.Printf( _( "Via %s" ), MessageTextFromValue( units, via.m_Diameter ) );
119  }
120 
121  int menuIdx = ID_POPUP_PCB_SELECT_VIASIZE1 + i;
122  Append( menuIdx, msg, wxEmptyString, wxITEM_CHECK );
123  Check( menuIdx, useIndex && bds.GetViaSizeIndex() == i );
124  }
125  }
126 
127  OPT_TOOL_EVENT eventHandler( const wxMenuEvent& aEvent ) override
128  {
131  int id = aEvent.GetId();
132 
133  // On Windows, this handler can be called with an event ID not existing in any
134  // menuitem, so only set flags when we have an ID match.
135 
137  {
138  DIALOG_TRACK_VIA_SIZE sizeDlg( frame, bds );
139 
140  if( sizeDlg.ShowModal() == wxID_OK )
141  {
142  bds.UseCustomTrackViaSize( true );
143  bds.m_UseConnectedTrackWidth = false;
144  }
145  }
147  {
148  bds.UseCustomTrackViaSize( false );
149  bds.m_UseConnectedTrackWidth = false;
151  }
152 
153  return OPT_TOOL_EVENT( PCB_ACTIONS::trackViaSizeChanged.MakeEvent() );
154  }
155 };
156 
157 
159  PCB_TOOL_BASE( "pcbnew.InteractiveDrawing" ),
160  m_view( nullptr ),
161  m_controls( nullptr ),
162  m_board( nullptr ),
163  m_frame( nullptr ),
164  m_mode( MODE::NONE ),
165  m_inDrawingTool( false ),
166  m_lineWidth( 1 )
167 {
168 }
169 
170 
172 {
173 }
174 
175 
177 {
178  auto activeToolFunctor = [this]( const SELECTION& aSel )
179  {
180  return m_mode != MODE::NONE;
181  };
182 
183  // some interactive drawing tools can undo the last point
184  auto canUndoPoint = [this]( const SELECTION& aSel )
185  {
186  return ( m_mode == MODE::ARC
187  || m_mode == MODE::ZONE
188  || m_mode == MODE::KEEPOUT
190  };
191 
192  // functor for tools that can automatically close the outline
193  auto canCloseOutline = [this]( const SELECTION& aSel )
194  {
195  return ( m_mode == MODE::ZONE
196  || m_mode == MODE::KEEPOUT
198  };
199 
200  auto viaToolActive = [this]( const SELECTION& aSel )
201  {
202  return m_mode == MODE::VIA;
203  };
204 
205  auto& ctxMenu = m_menu.GetMenu();
206 
207  // cancel current tool goes in main context menu at the top if present
208  ctxMenu.AddItem( ACTIONS::cancelInteractive, activeToolFunctor, 1 );
209  ctxMenu.AddSeparator( 1 );
210 
211  // tool-specific actions
212  ctxMenu.AddItem( PCB_ACTIONS::closeOutline, canCloseOutline, 200 );
213  ctxMenu.AddItem( PCB_ACTIONS::deleteLastPoint, canUndoPoint, 200 );
214 
215  ctxMenu.AddCheckItem( PCB_ACTIONS::toggle45, SELECTION_CONDITIONS::ShowAlways, 250 );
216  ctxMenu.AddSeparator( 500 );
217 
218  std::shared_ptr<VIA_SIZE_MENU> viaSizeMenu = std::make_shared<VIA_SIZE_MENU>();
219  viaSizeMenu->SetTool( this );
220  m_menu.AddSubMenu( viaSizeMenu );
221  ctxMenu.AddMenu( viaSizeMenu.get(), viaToolActive, 500 );
222 
223  ctxMenu.AddSeparator( 500 );
224 
225  // Type-specific sub-menus will be added for us by other tools
226  // For example, zone fill/unfill is provided by the PCB control tool
227 
228  // Finally, add the standard zoom/grid items
229  getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( m_menu );
230 
231  return true;
232 }
233 
234 
236 {
237  // Init variables used by every drawing tool
238  m_view = getView();
240  m_board = getModel<BOARD>();
241  m_frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
242 }
243 
244 
246 {
247  return m_mode;
248 }
249 
250 
251 int DRAWING_TOOL::DrawLine( const TOOL_EVENT& aEvent )
252 {
254  return 0;
255 
256  if( m_inDrawingTool )
257  return 0;
258 
260 
261  FOOTPRINT* parentFootprint = dynamic_cast<FOOTPRINT*>( m_frame->GetModel() );
262  PCB_SHAPE* line = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
263  BOARD_COMMIT commit( m_frame );
264  SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::LINE );
265  OPT<VECTOR2D> startingPoint = boost::make_optional<VECTOR2D>( false, VECTOR2D( 0, 0 ) );
266 
267  line->SetShape( SHAPE_T::SEGMENT );
268  line->SetFlags( IS_NEW );
269 
270  if( aEvent.HasPosition() )
271  startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
272 
273  std::string tool = aEvent.GetCommandStr().get();
274  m_frame->PushTool( tool );
275  Activate();
276 
277  while( drawSegment( tool, &line, startingPoint ) )
278  {
279  if( line )
280  {
281  if( m_isFootprintEditor )
282  static_cast<FP_SHAPE*>( line )->SetLocalCoord();
283 
284  commit.Add( line );
285  commit.Push( _( "Draw a line segment" ) );
286  startingPoint = VECTOR2D( line->GetEnd() );
287  }
288  else
289  {
290  startingPoint = NULLOPT;
291  }
292 
293  line = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
294  line->SetShape( SHAPE_T::SEGMENT );
295  line->SetFlags( IS_NEW );
296  }
297 
298  return 0;
299 }
300 
301 
303 {
305  return 0;
306 
307  if( m_inDrawingTool )
308  return 0;
309 
311 
312  FOOTPRINT* parentFootprint = dynamic_cast<FOOTPRINT*>( m_frame->GetModel() );
313  PCB_SHAPE* rect = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
314  BOARD_COMMIT commit( m_frame );
315  SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::RECTANGLE );
316  OPT<VECTOR2D> startingPoint = boost::make_optional<VECTOR2D>( false, VECTOR2D( 0, 0 ) );
317 
318  rect->SetShape( SHAPE_T::RECT );
319  rect->SetFilled( false );
320  rect->SetFlags(IS_NEW );
321 
322  if( aEvent.HasPosition() )
323  startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
324 
325  std::string tool = aEvent.GetCommandStr().get();
326  m_frame->PushTool( tool );
327  Activate();
328 
329  while( drawSegment( tool, &rect, startingPoint ) )
330  {
331  if( rect )
332  {
333  if( m_isFootprintEditor )
334  static_cast<FP_SHAPE*>( rect )->SetLocalCoord();
335 
336  commit.Add( rect );
337  commit.Push( _( "Draw a rectangle" ) );
338 
340  }
341 
342  rect = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
343  rect->SetShape( SHAPE_T::RECT );
344  rect->SetFilled( false );
345  rect->SetFlags(IS_NEW );
346  startingPoint = NULLOPT;
347  }
348 
349  return 0;
350 }
351 
352 
354 {
356  return 0;
357 
358  if( m_inDrawingTool )
359  return 0;
360 
362 
363  FOOTPRINT* parentFootprint = dynamic_cast<FOOTPRINT*>( m_frame->GetModel() );
364  PCB_SHAPE* circle = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
365  BOARD_COMMIT commit( m_frame );
366  SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::CIRCLE );
367  OPT<VECTOR2D> startingPoint = boost::make_optional<VECTOR2D>( false, VECTOR2D( 0, 0 ) );
368 
369  circle->SetShape( SHAPE_T::CIRCLE );
370  circle->SetFilled( false );
371  circle->SetFlags( IS_NEW );
372 
373  if( aEvent.HasPosition() )
374  startingPoint = getViewControls()->GetCursorPosition( !aEvent.DisableGridSnapping() );
375 
376  std::string tool = aEvent.GetCommandStr().get();
377  m_frame->PushTool( tool );
378  Activate();
379 
380  while( drawSegment( tool, &circle, startingPoint ) )
381  {
382  if( circle )
383  {
384  if( m_isFootprintEditor )
385  static_cast<FP_SHAPE*>( circle )->SetLocalCoord();
386 
387  commit.Add( circle );
388  commit.Push( _( "Draw a circle" ) );
389 
390  m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, circle );
391  }
392 
393  circle = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
394  circle->SetShape( SHAPE_T::CIRCLE );
395  circle->SetFilled( false );
396  circle->SetFlags( IS_NEW );
397  startingPoint = NULLOPT;
398  }
399 
400  return 0;
401 }
402 
403 
404 int DRAWING_TOOL::DrawArc( const TOOL_EVENT& aEvent )
405 {
407  return 0;
408 
409  if( m_inDrawingTool )
410  return 0;
411 
413 
414  FOOTPRINT* parentFootprint = dynamic_cast<FOOTPRINT*>( m_frame->GetModel() );
415  PCB_SHAPE* arc = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
416  BOARD_COMMIT commit( m_frame );
417  SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ARC );
418  bool immediateMode = aEvent.HasPosition();
419 
420  arc->SetShape( SHAPE_T::ARC );
421  arc->SetFlags( IS_NEW );
422 
423  std::string tool = aEvent.GetCommandStr().get();
424  m_frame->PushTool( tool );
425  Activate();
426 
427  while( drawArc( tool, &arc, immediateMode ) )
428  {
429  if( arc )
430  {
431  if( m_isFootprintEditor )
432  static_cast<FP_SHAPE*>( arc )->SetLocalCoord();
433 
434  commit.Add( arc );
435  commit.Push( _( "Draw an arc" ) );
436 
438  }
439 
440  arc = m_isFootprintEditor ? new FP_SHAPE( parentFootprint ) : new PCB_SHAPE;
441  arc->SetShape( SHAPE_T::ARC );
442  arc->SetFlags( IS_NEW );
443  immediateMode = false;
444  }
445 
446  return 0;
447 }
448 
449 
451 {
453  return 0;
454 
455  if( m_inDrawingTool )
456  return 0;
457 
459 
460  BOARD_ITEM* text = nullptr;
461  const BOARD_DESIGN_SETTINGS& dsnSettings = m_frame->GetDesignSettings();
462  BOARD_COMMIT commit( m_frame );
463  SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TEXT );
464 
465  auto cleanup =
466  [&]()
467  {
470  m_controls->ShowCursor( true );
471  m_controls->SetAutoPan( false );
472  m_controls->CaptureCursor( false );
473  delete text;
474  text = nullptr;
475  };
476 
477  auto setCursor =
478  [&]()
479  {
480  if( text )
482  else
484  };
485 
487 
488  std::string tool = aEvent.GetCommandStr().get();
489  m_frame->PushTool( tool );
490 
491  Activate();
492  // Must be done after Activate() so that it gets set into the correct context
493  m_controls->ShowCursor( true );
494  // do not capture or auto-pan until we start placing some text
495  // Set initial cursor
496  setCursor();
497 
498  // Prime the pump
499  if( !aEvent.IsReactivate() )
501 
502  // Main loop: keep receiving events
503  while( TOOL_EVENT* evt = Wait() )
504  {
505  setCursor();
506  VECTOR2I cursorPos = m_controls->GetCursorPosition();
507 
508  if( evt->IsCancelInteractive() )
509  {
510  if( text )
511  {
512  cleanup();
513  }
514  else
515  {
516  m_frame->PopTool( tool );
517  break;
518  }
519  }
520  else if( evt->IsActivate() )
521  {
522  if( text )
523  cleanup();
524 
525  if( evt->IsMoveTool() )
526  {
527  // leave ourselves on the stack so we come back after the move
528  break;
529  }
530  else
531  {
532  m_frame->PopTool( tool );
533  break;
534  }
535  }
536  else if( evt->IsClick( BUT_RIGHT ) )
537  {
539  }
540  else if( evt->IsClick( BUT_LEFT ) )
541  {
542  bool placing = text != nullptr;
543 
544  if( !text )
545  {
547 
550 
551  // Init the new item attributes
552  if( m_isFootprintEditor )
553  {
554  FP_TEXT* fpText = new FP_TEXT( (FOOTPRINT*) m_frame->GetModel() );
555 
556  fpText->SetLayer( layer );
557  fpText->SetTextSize( dsnSettings.GetTextSize( layer ) );
558  fpText->SetTextThickness( dsnSettings.GetTextThickness( layer ) );
559  fpText->SetItalic( dsnSettings.GetTextItalic( layer ) );
560  fpText->SetKeepUpright( dsnSettings.GetTextUpright( layer ) );
561  fpText->SetTextPos( (wxPoint) cursorPos );
562 
563  text = fpText;
564 
565  DIALOG_TEXT_PROPERTIES textDialog( m_frame, fpText );
566  bool cancelled;
567 
568  RunMainStack( [&]()
569  {
570  cancelled = !textDialog.ShowModal();
571  } );
572 
573  if( cancelled || NoPrintableChars( fpText->GetText() ) )
574  {
575  delete text;
576  text = nullptr;
577  }
578  else if( fpText->GetTextPos() != (wxPoint) cursorPos )
579  {
580  // If the user modified the location then go ahead and place it there.
581  // Otherwise we'll drag.
582  placing = true;
583  }
584  }
585  else
586  {
587  PCB_TEXT* pcbText = new PCB_TEXT( m_frame->GetModel() );
588  // TODO we have to set IS_NEW, otherwise InstallTextPCB.. creates an
589  // undo entry :| LEGACY_CLEANUP
590  pcbText->SetFlags( IS_NEW );
591 
592  pcbText->SetLayer( layer );
593 
594  // Set the mirrored option for layers on the BACK side of the board
595  if( IsBackLayer( layer ) )
596  pcbText->SetMirrored( true );
597 
598  pcbText->SetTextSize( dsnSettings.GetTextSize( layer ) );
599  pcbText->SetTextThickness( dsnSettings.GetTextThickness( layer ) );
600  pcbText->SetItalic( dsnSettings.GetTextItalic( layer ) );
601  pcbText->SetTextPos( (wxPoint) cursorPos );
602 
603  RunMainStack( [&]()
604  {
605  m_frame->ShowTextPropertiesDialog( pcbText );
606  } );
607 
608  if( NoPrintableChars( pcbText->GetText() ) )
609  delete pcbText;
610  else
611  text = pcbText;
612  }
613 
614  if( text )
615  {
616  if( !m_view->IsLayerVisible( text->GetLayer() ) )
617  {
618  m_frame->GetAppearancePanel()->SetLayerVisible( text->GetLayer(), true );
619  m_frame->GetCanvas()->Refresh();
620  }
621 
622  m_controls->WarpCursor( text->GetPosition(), true );
624  m_view->Update( &selection() );
625 
626  // update the cursor so it looks correct before another event
627  setCursor();
628  }
629  }
630 
631  if( placing )
632  {
633  text->ClearFlags();
635 
636  commit.Add( text );
637  commit.Push( _( "Place a text" ) );
638 
640 
641  text = nullptr;
642  }
643 
645  m_controls->ShowCursor( true );
646  m_controls->CaptureCursor( text != nullptr );
647  m_controls->SetAutoPan( text != nullptr );
648  }
649  else if( text && evt->IsMotion() )
650  {
651  text->SetPosition( (wxPoint) cursorPos );
652  selection().SetReferencePoint( cursorPos );
653  m_view->Update( &selection() );
654  }
655  else if( evt->IsAction( &PCB_ACTIONS::properties ) )
656  {
657  if( text )
658  {
660  m_view->Update( &selection() );
661  frame()->SetMsgPanel( text );
662  }
663  else
664  {
665  evt->SetPassEvent();
666  }
667  }
668  else
669  {
670  evt->SetPassEvent();
671  }
672  }
673 
674  m_controls->SetAutoPan( false );
675  m_controls->CaptureCursor( false );
677  m_frame->SetMsgPanel( board() );
678 
679  return 0;
680 }
681 
682 
684 {
685  const VECTOR2I lineVector{ aDim->GetEnd() - aDim->GetStart() };
686 
687  aDim->SetEnd( wxPoint( VECTOR2I( aDim->GetStart() ) + GetVectorSnapped45( lineVector ) ) );
688  aDim->Update();
689 }
690 
691 
693 {
695  return 0;
696 
697  if( m_inDrawingTool )
698  return 0;
699 
701 
702  enum DIMENSION_STEPS
703  {
704  SET_ORIGIN = 0,
705  SET_END,
706  SET_HEIGHT,
707  FINISHED
708  };
709 
710  TOOL_EVENT originalEvent = aEvent;
711  PCB_DIMENSION_BASE* dimension = nullptr;
712  BOARD_COMMIT commit( m_frame );
714  BOARD_DESIGN_SETTINGS& boardSettings = m_board->GetDesignSettings();
715  PCB_SELECTION preview; // A VIEW_GROUP that serves as a preview for the new item(s)
716  SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DIMENSION );
717  int step = SET_ORIGIN;
718 
719  m_view->Add( &preview );
720 
721  auto cleanup =
722  [&]()
723  {
724  m_controls->SetAutoPan( false );
725  m_controls->CaptureCursor( false );
726 
727  preview.Clear();
728  m_view->Update( &preview );
729 
730  delete dimension;
731  dimension = nullptr;
732  step = SET_ORIGIN;
733  };
734 
735  auto setCursor =
736  [&]()
737  {
739  };
740 
742 
743  std::string tool = aEvent.GetCommandStr().get();
744  m_frame->PushTool( tool );
745 
746  Activate();
747  // Must be done after Activate() so that it gets set into the correct context
748  m_controls->ShowCursor( true );
749  // Set initial cursor
750  setCursor();
751 
752  // Prime the pump
754 
755  if( aEvent.HasPosition() )
756  m_toolMgr->PrimeTool( aEvent.Position() );
757 
758  // Main loop: keep receiving events
759  while( TOOL_EVENT* evt = Wait() )
760  {
761  if( step > SET_ORIGIN )
762  frame()->SetMsgPanel( dimension );
763 
764  setCursor();
765 
766  grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
767  grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
768 
769  VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
770 
771  cursorPos = grid.BestSnapAnchor( cursorPos, nullptr );
772  m_controls->ForceCursorPosition( true, cursorPos );
773 
774  if( evt->IsCancelInteractive() )
775  {
776  m_controls->SetAutoPan( false );
777 
778  if( step != SET_ORIGIN ) // start from the beginning
779  {
780  cleanup();
781  }
782  else
783  {
784  m_frame->PopTool( tool );
785  break;
786  }
787  }
788  else if( evt->IsActivate() )
789  {
790  if( step != SET_ORIGIN )
791  cleanup();
792 
793  if( evt->IsPointEditor() )
794  {
795  // don't exit (the point editor runs in the background)
796  }
797  else if( evt->IsMoveTool() )
798  {
799  // leave ourselves on the stack so we come back after the move
800  break;
801  }
802  else
803  {
804  m_frame->PopTool( tool );
805  break;
806  }
807  }
808  else if( evt->IsAction( &PCB_ACTIONS::incWidth ) && step != SET_ORIGIN )
809  {
811  dimension->SetLineThickness( m_lineWidth );
812  m_view->Update( &preview );
813  frame()->SetMsgPanel( dimension );
814  }
815  else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && step != SET_ORIGIN )
816  {
817  if( m_lineWidth > WIDTH_STEP )
818  {
820  dimension->SetLineThickness( m_lineWidth );
821  m_view->Update( &preview );
822  frame()->SetMsgPanel( dimension );
823  }
824  }
825  else if( evt->IsClick( BUT_RIGHT ) )
826  {
828  }
829  else if( evt->IsClick( BUT_LEFT ) )
830  {
831  switch( step )
832  {
833  case SET_ORIGIN:
834  {
836 
838 
839  // Init the new item attributes
840  auto setMeasurementAttributes =
841  [&]( PCB_DIMENSION_BASE* aDim )
842  {
843  aDim->SetUnitsMode( boardSettings.m_DimensionUnitsMode );
844  aDim->SetUnitsFormat( boardSettings.m_DimensionUnitsFormat );
845  aDim->SetPrecision( boardSettings.m_DimensionPrecision );
846  aDim->SetSuppressZeroes( boardSettings.m_DimensionSuppressZeroes );
847  aDim->SetTextPositionMode( boardSettings.m_DimensionTextPosition );
848  aDim->SetKeepTextAligned( boardSettings.m_DimensionKeepTextAligned );
849 
850  if( boardSettings.m_DimensionUnitsMode == DIM_UNITS_MODE::AUTOMATIC )
851  aDim->SetUnits( m_frame->GetUserUnits() );
852  };
853 
854  if( originalEvent.IsAction( &PCB_ACTIONS::drawAlignedDimension ) )
855  {
856  dimension = new PCB_DIM_ALIGNED( m_board );
857  setMeasurementAttributes( dimension );
858  }
859  else if( originalEvent.IsAction( &PCB_ACTIONS::drawOrthogonalDimension ) )
860  {
861  dimension = new PCB_DIM_ORTHOGONAL( m_board );
862  setMeasurementAttributes( dimension );
863  }
864  else if( originalEvent.IsAction( &PCB_ACTIONS::drawCenterDimension ) )
865  {
866  dimension = new PCB_DIM_CENTER( m_board );
867  }
868  else if( originalEvent.IsAction( &PCB_ACTIONS::drawLeader ) )
869  {
870  dimension = new PCB_DIM_LEADER( m_board );
871  dimension->Text().SetPosition( wxPoint( cursorPos ) );
872  }
873  else
874  {
875  wxFAIL_MSG( "Unhandled action in DRAWING_TOOL::DrawDimension" );
876  }
877 
878  dimension->SetLayer( layer );
879  dimension->Text().SetTextSize( boardSettings.GetTextSize( layer ) );
880  dimension->Text().SetTextThickness( boardSettings.GetTextThickness( layer ) );
881  dimension->Text().SetItalic( boardSettings.GetTextItalic( layer ) );
882  dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
883  dimension->SetArrowLength( boardSettings.m_DimensionArrowLength );
884  dimension->SetExtensionOffset( boardSettings.m_DimensionExtensionOffset );
885  dimension->SetStart( (wxPoint) cursorPos );
886  dimension->SetEnd( (wxPoint) cursorPos );
887  dimension->Update();
888 
889  if( !m_view->IsLayerVisible( layer ) )
890  {
891  m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
892  m_frame->GetCanvas()->Refresh();
893  }
894 
895  preview.Add( dimension );
896  frame()->SetMsgPanel( dimension );
897 
898  m_controls->SetAutoPan( true );
899  m_controls->CaptureCursor( true );
900  break;
901  }
902 
903  case SET_END:
904  {
905  dimension->SetEnd( (wxPoint) cursorPos );
906  dimension->Update();
907 
908  if( Is45Limited() || dimension->Type() == PCB_DIM_CENTER_T )
909  constrainDimension( dimension );
910 
911  // Dimensions that have origin and end in the same spot are not valid
912  if( dimension->GetStart() == dimension->GetEnd() )
913  --step;
914  else if( dimension->Type() == PCB_DIM_LEADER_T )
915  dimension->SetText( wxT( "?" ) );
916 
917  if( dimension->Type() == PCB_DIM_CENTER_T )
918  {
919  // No separate height/text step
920  ++step;
922  }
923  else
924  {
925  break;
926  }
927  }
928 
929  case SET_HEIGHT:
930  if( dimension->Type() == PCB_DIM_LEADER_T )
931  {
932  assert( dimension->GetStart() != dimension->GetEnd() );
933  assert( dimension->GetLineThickness() > 0 );
934 
935  preview.Remove( dimension );
936 
937  commit.Add( dimension );
938  commit.Push( _( "Draw a leader" ) );
939 
940  // Run the edit immediately to set the leader text
941  m_toolMgr->RunAction( PCB_ACTIONS::properties, true, dimension );
942  }
943  else if( (wxPoint) cursorPos != dimension->GetPosition() )
944  {
945  assert( dimension->GetStart() != dimension->GetEnd() );
946  assert( dimension->GetLineThickness() > 0 );
947 
948  preview.Remove( dimension );
949 
950  commit.Add( dimension );
951  commit.Push( _( "Draw a dimension" ) );
952 
953  m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, dimension );
954  }
955 
956  break;
957  }
958 
959  if( ++step == FINISHED )
960  {
961  step = SET_ORIGIN;
962  m_controls->SetAutoPan( false );
963  m_controls->CaptureCursor( false );
964  }
965  }
966  else if( evt->IsMotion() )
967  {
968  switch( step )
969  {
970  case SET_END:
971  dimension->SetEnd( (wxPoint) cursorPos );
972 
973  if( dimension->Type() == PCB_DIM_ORTHOGONAL_T )
974  {
975  PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
976 
977  BOX2I bounds( dimension->GetStart(),
978  dimension->GetEnd() - dimension->GetStart() );
979 
980  // Create a nice preview by measuring the longer dimension
981  bool vert = bounds.GetWidth() < bounds.GetHeight();
982 
983  ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
985  }
986 
987  dimension->Update();
988 
989  if( Is45Limited() || dimension->Type() == PCB_DIM_CENTER_T )
990  constrainDimension( dimension );
991 
992  break;
993 
994  case SET_HEIGHT:
995  {
996  if( dimension->Type() == PCB_DIM_ALIGNED_T )
997  {
998  PCB_DIM_ALIGNED* aligned = static_cast<PCB_DIM_ALIGNED*>( dimension );
999 
1000  // Calculating the direction of travel perpendicular to the selected axis
1001  double angle = aligned->GetAngle() + ( M_PI / 2 );
1002 
1003  wxPoint delta( (wxPoint) cursorPos - dimension->GetEnd() );
1004  double height = ( delta.x * cos( angle ) ) + ( delta.y * sin( angle ) );
1005  aligned->SetHeight( height );
1006  aligned->Update();
1007  }
1008  else if( dimension->Type() == PCB_DIM_ORTHOGONAL_T )
1009  {
1010  PCB_DIM_ORTHOGONAL* ortho = static_cast<PCB_DIM_ORTHOGONAL*>( dimension );
1011 
1012  BOX2I bounds( dimension->GetStart(),
1013  dimension->GetEnd() - dimension->GetStart() );
1014  VECTOR2I direction( cursorPos - bounds.Centre() );
1015  bool vert;
1016 
1017  // Only change the orientation when we move outside the bounds
1018  if( !bounds.Contains( cursorPos ) )
1019  {
1020  // If the dimension is horizontal or vertical, set correct orientation
1021  // otherwise, test if we're left/right of the bounding box or above/below it
1022  if( bounds.GetWidth() == 0 )
1023  {
1024  vert = true;
1025  }
1026  else if( bounds.GetHeight() == 0 )
1027  {
1028  vert = false;
1029  }
1030  else if( cursorPos.x > bounds.GetLeft() && cursorPos.x < bounds.GetRight() )
1031  {
1032  vert = false;
1033  }
1034  else if( cursorPos.y > bounds.GetTop() && cursorPos.y < bounds.GetBottom() )
1035  {
1036  vert = true;
1037  }
1038  else
1039  {
1040  vert = std::abs( direction.y ) < std::abs( direction.x );
1041  }
1042  ortho->SetOrientation( vert ? PCB_DIM_ORTHOGONAL::DIR::VERTICAL
1044  }
1045  else
1046  {
1047  vert = ortho->GetOrientation() == PCB_DIM_ORTHOGONAL::DIR::VERTICAL;
1048  }
1049 
1050  VECTOR2I heightVector( cursorPos - dimension->GetStart() );
1051  ortho->SetHeight( vert ? heightVector.x : heightVector.y );
1052  ortho->Update();
1053  }
1054  else if( dimension->Type() == PCB_DIM_LEADER_T )
1055  {
1056  // Leader: SET_HEIGHT actually sets the text position directly
1057  VECTOR2I lineVector( cursorPos - dimension->GetEnd() );
1058  dimension->Text().SetPosition( wxPoint( VECTOR2I( dimension->GetEnd() ) +
1059  GetVectorSnapped45( lineVector ) ) );
1060  dimension->Update();
1061  }
1062  }
1063  break;
1064  }
1065 
1066  // Show a preview of the item
1067  m_view->Update( &preview );
1068  }
1069  else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
1070  {
1071  if( dimension )
1072  {
1073  PCB_LAYER_ID layer = m_frame->GetActiveLayer();
1074 
1075  if( !m_view->IsLayerVisible( layer ) )
1076  {
1077  m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
1078  m_frame->GetCanvas()->Refresh();
1079  }
1080 
1081  dimension->SetLayer( layer );
1082  dimension->Text().SetTextSize( boardSettings.GetTextSize( layer ) );
1083  dimension->Text().SetTextThickness( boardSettings.GetTextThickness( layer ) );
1084  dimension->Text().SetItalic( boardSettings.GetTextItalic( layer ) );
1085  dimension->SetLineThickness( boardSettings.GetLineThickness( layer ) );
1086  dimension->Update();
1087 
1088  m_view->Update( &preview );
1089  frame()->SetMsgPanel( dimension );
1090  }
1091  else
1092  {
1093  evt->SetPassEvent();
1094  }
1095  }
1096  else if( evt->IsAction( &PCB_ACTIONS::properties ) )
1097  {
1098  if( step == SET_END || step == SET_HEIGHT )
1099  {
1100  frame()->OnEditItemRequest( dimension );
1101  dimension->Update();
1102  frame()->SetMsgPanel( dimension );
1103  break;
1104  }
1105  else
1106  {
1107  evt->SetPassEvent();
1108  }
1109  }
1110  else
1111  {
1112  evt->SetPassEvent();
1113  }
1114  }
1115 
1116  if( step != SET_ORIGIN )
1117  delete dimension;
1118 
1119  m_controls->SetAutoPan( false );
1120  m_controls->ForceCursorPosition( false );
1121  m_controls->CaptureCursor( false );
1123 
1124  m_view->Remove( &preview );
1125  m_frame->SetMsgPanel( board() );
1126 
1127  return 0;
1128 }
1129 
1130 
1132 {
1133  if( !m_frame->GetModel() )
1134  return 0;
1135 
1136  if( m_inDrawingTool )
1137  return 0;
1138 
1140 
1141  // Note: PlaceImportedGraphics() will convert PCB_SHAPE_T and PCB_TEXT_T to footprint
1142  // items if needed
1144  int dlgResult = dlg.ShowModal();
1145 
1146  std::list<std::unique_ptr<EDA_ITEM>>& list = dlg.GetImportedItems();
1147 
1148  if( dlgResult != wxID_OK )
1149  return 0;
1150 
1151  // Ensure the list is not empty:
1152  if( list.empty() )
1153  {
1154  wxMessageBox( _( "No graphic items found in file.") );
1155  return 0;
1156  }
1157 
1159 
1160  std::vector<BOARD_ITEM*> newItems; // all new items, including group
1161  std::vector<BOARD_ITEM*> selectedItems; // the group, or newItems if no group
1162  PCB_SELECTION preview;
1163  BOARD_COMMIT commit( m_frame );
1164  PCB_GROUP* group = nullptr;
1165 
1166  if( dlg.ShouldGroupItems() )
1167  {
1168  if( m_isFootprintEditor )
1170  else
1171  group = new PCB_GROUP( m_frame->GetBoard() );
1172 
1173  newItems.push_back( group );
1174  selectedItems.push_back( group );
1175  preview.Add( group );
1176  }
1177 
1178  for( std::unique_ptr<EDA_ITEM>& ptr : list )
1179  {
1180  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( ptr.get() );
1181 
1182  if( m_isFootprintEditor )
1183  wxASSERT( item->Type() == PCB_FP_SHAPE_T || item->Type() == PCB_FP_TEXT_T );
1184  else
1185  wxASSERT( item->Type() == PCB_SHAPE_T || item->Type() == PCB_TEXT_T );
1186 
1187  newItems.push_back( item );
1188 
1189  if( group )
1190  group->AddItem( item );
1191  else
1192  selectedItems.push_back( item );
1193 
1194  preview.Add( item );
1195 
1196  ptr.release();
1197  }
1198 
1199  if( !dlg.IsPlacementInteractive() )
1200  {
1201  for( BOARD_ITEM* item : newItems )
1202  commit.Add( item );
1203 
1204  commit.Push( _( "Place a DXF_SVG drawing" ) );
1205  return 0;
1206  }
1207 
1208  m_view->Add( &preview );
1209 
1210  // Clear the current selection then select the drawings so that edit tools work on them
1212  m_toolMgr->RunAction( PCB_ACTIONS::selectItems, true, &selectedItems );
1213 
1214  std::string tool = aEvent.GetCommandStr().get();
1215  m_frame->PushTool( tool );
1216 
1217  auto setCursor =
1218  [&]()
1219  {
1221  };
1222 
1223  Activate();
1224  // Must be done after Activate() so that it gets set into the correct context
1225  m_controls->ShowCursor( true );
1226  m_controls->ForceCursorPosition( false );
1227  // Set initial cursor
1228  setCursor();
1229 
1230  SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );
1231 
1232  // Now move the new items to the current cursor position:
1233  VECTOR2I cursorPos = m_controls->GetCursorPosition();
1234  VECTOR2I delta = cursorPos - static_cast<BOARD_ITEM*>( preview.Front() )->GetPosition();
1235 
1236  for( BOARD_ITEM* item : selectedItems )
1237  item->Move( (wxPoint) delta );
1238 
1239  m_view->Update( &preview );
1240 
1241  // Main loop: keep receiving events
1242  while( TOOL_EVENT* evt = Wait() )
1243  {
1244  setCursor();
1245  cursorPos = m_controls->GetCursorPosition();
1246 
1247  if( evt->IsCancelInteractive() || evt->IsActivate() )
1248  {
1250 
1251  for( BOARD_ITEM* item : newItems )
1252  delete item;
1253 
1254  break;
1255  }
1256  else if( evt->IsMotion() )
1257  {
1258  delta = cursorPos - static_cast<BOARD_ITEM*>( preview.Front() )->GetPosition();
1259 
1260  for( BOARD_ITEM* item : selectedItems )
1261  item->Move( (wxPoint) delta );
1262 
1263  m_view->Update( &preview );
1264  }
1265  else if( evt->IsClick( BUT_RIGHT ) )
1266  {
1268  }
1269  else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1270  {
1271  // Place the imported drawings
1272  for( BOARD_ITEM* item : newItems )
1273  commit.Add( item );
1274 
1275  commit.Push( _( "Place a DXF_SVG drawing" ) );
1276  break; // This is a one-shot command, not a tool
1277  }
1278  else
1279  {
1280  evt->SetPassEvent();
1281  }
1282  }
1283 
1284  preview.Clear();
1285  m_view->Remove( &preview );
1286 
1288 
1289  m_frame->PopTool( tool );
1290 
1291  return 0;
1292 }
1293 
1294 
1296 {
1297  wxASSERT( m_isFootprintEditor );
1298 
1299  if( !m_frame->GetModel() )
1300  return 0;
1301 
1302  if( m_inDrawingTool )
1303  return 0;
1304 
1306 
1307  SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ANCHOR );
1309 
1311 
1312  std::string tool = aEvent.GetCommandStr().get();
1313  m_frame->PushTool( tool );
1314 
1315  auto setCursor =
1316  [&]()
1317  {
1319  };
1320 
1321  Activate();
1322  // Must be done after Activate() so that it gets set into the correct context
1323  m_controls->ShowCursor( true );
1324  m_controls->SetAutoPan( true );
1325  m_controls->CaptureCursor( false );
1326  // Set initial cursor
1327  setCursor();
1328 
1329  while( TOOL_EVENT* evt = Wait() )
1330  {
1331  setCursor();
1332 
1333  grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1334  grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1335  VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(),
1336  LSET::AllLayersMask() );
1337  m_controls->ForceCursorPosition( true, cursorPos );
1338 
1339  if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1340  {
1342  BOARD_COMMIT commit( m_frame );
1343  commit.Modify( footprint );
1344 
1345  // set the new relative internal local coordinates of footprint items
1346  wxPoint moveVector = footprint->GetPosition() - (wxPoint) cursorPos;
1347  footprint->MoveAnchorPosition( moveVector );
1348 
1349  commit.Push( _( "Move the footprint reference anchor" ) );
1350 
1351  // Usually, we do not need to change twice the anchor position,
1352  // so deselect the active tool
1353  m_frame->PopTool( tool );
1354  break;
1355  }
1356  else if( evt->IsClick( BUT_RIGHT ) )
1357  {
1359  }
1360  else if( evt->IsCancelInteractive() || evt->IsActivate() )
1361  {
1362  m_frame->PopTool( tool );
1363  break;
1364  }
1365  else
1366  {
1367  evt->SetPassEvent();
1368  }
1369  }
1370 
1372 
1373  return 0;
1374 }
1375 
1376 
1378 {
1381 
1382  return 0;
1383 }
1384 
1385 
1390  PCB_SHAPE* aGraphic )
1391 {
1392  if( !aMgr.IsReset() )
1393  {
1394  aGraphic->SetStart( (wxPoint) aMgr.GetOrigin() );
1395  aGraphic->SetEnd( (wxPoint) aMgr.GetEnd() );
1396  }
1397 }
1398 
1399 
1400 bool DRAWING_TOOL::drawSegment( const std::string& aTool, PCB_SHAPE** aGraphic,
1401  OPT<VECTOR2D> aStartingPoint )
1402 {
1403  SHAPE_T shape = ( *aGraphic )->GetShape();
1404 
1405  // Only three shapes are currently supported
1406  wxASSERT( shape == SHAPE_T::SEGMENT || shape == SHAPE_T::CIRCLE || shape == SHAPE_T::RECT );
1407 
1408  EDA_UNITS userUnits = m_frame->GetUserUnits();
1410  PCB_SHAPE*& graphic = *aGraphic;
1411  PCB_LAYER_ID drawingLayer = m_frame->GetActiveLayer();
1412 
1413  m_lineWidth = getSegmentWidth( drawingLayer );
1414 
1415  // geometric construction manager
1417 
1418  // drawing assistant overlay
1419  // TODO: workaround because PCB_SHAPE_TYPE_T is not visible from commons.
1420  KIGFX::PREVIEW::GEOM_SHAPE geomShape( static_cast<KIGFX::PREVIEW::GEOM_SHAPE>( shape ) );
1421  KIGFX::PREVIEW::TWO_POINT_ASSISTANT twoPointAsst( twoPointManager, userUnits, geomShape );
1422 
1423  // Add a VIEW_GROUP that serves as a preview for the new item
1424  PCB_SELECTION preview;
1425  m_view->Add( &preview );
1426  m_view->Add( &twoPointAsst );
1427 
1428  bool started = false;
1429  bool cancelled = false;
1430  bool isLocalOriginSet = ( m_frame->GetScreen()->m_LocalOrigin != VECTOR2D( 0, 0 ) );
1431  VECTOR2I cursorPos = m_controls->GetMousePosition();
1432 
1433  auto setCursor =
1434  [&]()
1435  {
1437  };
1438 
1439  auto cleanup =
1440  [&]()
1441  {
1442  preview.Clear();
1443  m_view->Update( &preview );
1444  delete graphic;
1445  graphic = nullptr;
1446 
1447  if( !isLocalOriginSet )
1448  m_frame->GetScreen()->m_LocalOrigin = VECTOR2D( 0, 0 );
1449  };
1450 
1451  m_controls->ShowCursor( true );
1452  // Set initial cursor
1453  setCursor();
1454 
1455  // Prime the pump
1457 
1458  if( aStartingPoint )
1460 
1461  // Main loop: keep receiving events
1462  while( TOOL_EVENT* evt = Wait() )
1463  {
1464  setCursor();
1465 
1466  if( started )
1467  m_frame->SetMsgPanel( graphic );
1468 
1469  grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1470  grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1471  cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), drawingLayer );
1472  m_controls->ForceCursorPosition( true, cursorPos );
1473 
1474  if( evt->IsCancelInteractive() )
1475  {
1476  cleanup();
1477 
1478  if( !started )
1479  {
1480  // We've handled the cancel event. Don't cancel other tools
1481  evt->SetPassEvent( false );
1482  m_frame->PopTool( aTool );
1483  cancelled = true;
1484  }
1485 
1486  break;
1487  }
1488  else if( evt->IsActivate() )
1489  {
1490  if( evt->IsPointEditor() )
1491  {
1492  // don't exit (the point editor runs in the background)
1493  }
1494  else if( evt->IsMoveTool() )
1495  {
1496  cleanup();
1497  // leave ourselves on the stack so we come back after the move
1498  cancelled = true;
1499  break;
1500  }
1501  else
1502  {
1503  cleanup();
1504  m_frame->PopTool( aTool );
1505  cancelled = true;
1506  break;
1507  }
1508  }
1509  else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
1510  {
1511  drawingLayer = m_frame->GetActiveLayer();
1512  m_lineWidth = getSegmentWidth( drawingLayer );
1513 
1514  if( graphic )
1515  {
1516  if( !m_view->IsLayerVisible( drawingLayer ) )
1517  {
1518  m_frame->GetAppearancePanel()->SetLayerVisible( drawingLayer, true );
1519  m_frame->GetCanvas()->Refresh();
1520  }
1521 
1522  graphic->SetLayer( drawingLayer );
1523  graphic->SetWidth( m_lineWidth );
1524  m_view->Update( &preview );
1525  frame()->SetMsgPanel( graphic );
1526  }
1527  else
1528  {
1529  evt->SetPassEvent();
1530  }
1531  }
1532  else if( evt->IsAction( &PCB_ACTIONS::properties ) )
1533  {
1534  if( started )
1535  {
1536  frame()->OnEditItemRequest( graphic );
1537  m_view->Update( &preview );
1538  frame()->SetMsgPanel( graphic );
1539  break;
1540  }
1541  else
1542  {
1543  evt->SetPassEvent();
1544  }
1545  }
1546  else if( evt->IsClick( BUT_RIGHT ) )
1547  {
1549  }
1550  else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
1551  {
1552  if( !started )
1553  {
1555 
1556  if( aStartingPoint )
1557  {
1558  cursorPos = aStartingPoint.get();
1559  aStartingPoint = NULLOPT;
1560  }
1561 
1562  m_lineWidth = getSegmentWidth( drawingLayer );
1563 
1564  // Init the new item attributes
1565  graphic->SetShape( static_cast<SHAPE_T>( shape ) );
1566  graphic->SetFilled( false );
1567  graphic->SetWidth( m_lineWidth );
1568  graphic->SetLayer( drawingLayer );
1569  grid.SetSkipPoint( cursorPos );
1570 
1571  twoPointManager.SetOrigin( (wxPoint) cursorPos );
1572  twoPointManager.SetEnd( (wxPoint) cursorPos );
1573 
1574  if( !isLocalOriginSet )
1575  m_frame->GetScreen()->m_LocalOrigin = cursorPos;
1576 
1577  preview.Add( graphic );
1578  frame()->SetMsgPanel( graphic );
1579  m_controls->SetAutoPan( true );
1580  m_controls->CaptureCursor( true );
1581 
1582  if( !m_view->IsLayerVisible( drawingLayer ) )
1583  {
1584  m_frame->GetAppearancePanel()->SetLayerVisible( drawingLayer, true );
1585  m_frame->GetCanvas()->Refresh();
1586  }
1587 
1588  updateSegmentFromGeometryMgr( twoPointManager, graphic );
1589 
1590  started = true;
1591  }
1592  else if( shape == SHAPE_T::CIRCLE )
1593  {
1594  // No clever logic if drawing a circle
1595  preview.Clear();
1596  twoPointManager.Reset();
1597  break;
1598  }
1599  else
1600  {
1601  PCB_SHAPE* snapItem = dyn_cast<PCB_SHAPE*>( grid.GetSnapped() );
1602 
1603  if( twoPointManager.GetOrigin() == twoPointManager.GetEnd()
1604  || ( evt->IsDblClick( BUT_LEFT ) && shape == SHAPE_T::SEGMENT )
1605  || snapItem )
1606  // User has clicked twice in the same spot
1607  // or clicked on the end of an existing segment (closing a path)
1608  {
1609  BOARD_COMMIT commit( m_frame );
1610 
1611  // If the user clicks on an existing snap point from a drawsegment
1612  // we finish the segment as they are likely closing a path
1613  if( snapItem
1614  && ( shape == SHAPE_T::RECT || graphic->GetLength() > 0.0 ) )
1615  {
1616  commit.Add( graphic );
1617  commit.Push( _( "Draw a line segment" ) );
1618  m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, graphic );
1619  }
1620  else
1621  {
1622  delete graphic;
1623  }
1624 
1625  graphic = nullptr;
1626  }
1627 
1628  preview.Clear();
1629  twoPointManager.Reset();
1630  break;
1631  }
1632 
1633  twoPointManager.SetEnd( cursorPos );
1634  }
1635  else if( evt->IsMotion() )
1636  {
1637  // 45 degree lines
1638  if( started && Is45Limited() )
1639  {
1640  const VECTOR2I lineVector( cursorPos - VECTOR2I( twoPointManager.GetOrigin() ) );
1641 
1642  // get a restricted 45/H/V line from the last fixed point to the cursor
1643  auto newEnd = GetVectorSnapped45( lineVector, ( shape == SHAPE_T::RECT ) );
1644  m_controls->ForceCursorPosition( true, VECTOR2I( twoPointManager.GetEnd() ) );
1645  twoPointManager.SetEnd( twoPointManager.GetOrigin() + (wxPoint) newEnd );
1646  twoPointManager.SetAngleSnap( true );
1647  }
1648  else
1649  {
1650  twoPointManager.SetEnd( (wxPoint) cursorPos );
1651  twoPointManager.SetAngleSnap( false );
1652  }
1653 
1654  updateSegmentFromGeometryMgr( twoPointManager, graphic );
1655  m_view->Update( &preview );
1656  m_view->Update( &twoPointAsst );
1657  }
1658  else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
1659  {
1661  graphic->SetWidth( m_lineWidth );
1662  m_view->Update( &preview );
1663  frame()->SetMsgPanel( graphic );
1664  }
1665  else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && ( m_lineWidth > WIDTH_STEP ) )
1666  {
1668  graphic->SetWidth( m_lineWidth );
1669  m_view->Update( &preview );
1670  frame()->SetMsgPanel( graphic );
1671  }
1672  else if( evt->IsAction( &ACTIONS::resetLocalCoords ) )
1673  {
1674  isLocalOriginSet = true;
1675  evt->SetPassEvent();
1676  }
1677  else if( evt->IsAction( &ACTIONS::updateUnits ) )
1678  {
1679  if( frame()->GetUserUnits() != userUnits )
1680  {
1681  userUnits = frame()->GetUserUnits();
1682  twoPointAsst.SetUnits( userUnits );
1683  m_view->Update( &twoPointAsst );
1684  }
1685  evt->SetPassEvent();
1686  }
1687  else
1688  {
1689  evt->SetPassEvent();
1690  }
1691  }
1692 
1693  if( !isLocalOriginSet ) // reset the relative coordinate if it was not set before
1694  m_frame->GetScreen()->m_LocalOrigin = VECTOR2D( 0, 0 );
1695 
1696  m_view->Remove( &twoPointAsst );
1697  m_view->Remove( &preview );
1698  m_frame->SetMsgPanel( board() );
1699 
1701  m_controls->SetAutoPan( false );
1702  m_controls->CaptureCursor( false );
1703  m_controls->ForceCursorPosition( false );
1704 
1705  return !cancelled;
1706 }
1707 
1708 
1713  PCB_SHAPE& aArc )
1714 {
1715  auto vec = aMgr.GetOrigin();
1716 
1717  aArc.SetCenter( { vec.x, vec.y } );
1718 
1719  vec = aMgr.GetStartRadiusEnd();
1720  aArc.SetArcStart( { vec.x, vec.y } );
1721 
1722  aArc.SetAngle( RAD2DECIDEG( -aMgr.GetSubtended() ) );
1723 
1724  vec = aMgr.GetEndRadiusEnd();
1725  aArc.SetArcEnd( { vec.x, vec.y } );
1726 }
1727 
1728 
1729 bool DRAWING_TOOL::drawArc( const std::string& aTool, PCB_SHAPE** aGraphic, bool aImmediateMode )
1730 {
1731  PCB_SHAPE*& graphic = *aGraphic;
1732  PCB_LAYER_ID drawingLayer = m_frame->GetActiveLayer();
1733 
1734  m_lineWidth = getSegmentWidth( drawingLayer );
1735 
1736  // Arc geometric construction manager
1738 
1739  // Arc drawing assistant overlay
1740  KIGFX::PREVIEW::ARC_ASSISTANT arcAsst( arcManager, m_frame->GetUserUnits() );
1741 
1742  // Add a VIEW_GROUP that serves as a preview for the new item
1743  PCB_SELECTION preview;
1744  m_view->Add( &preview );
1745  m_view->Add( &arcAsst );
1747 
1748  auto setCursor =
1749  [&]()
1750  {
1752  };
1753 
1754  auto cleanup =
1755  [&] ()
1756  {
1757  preview.Clear();
1758  delete *aGraphic;
1759  *aGraphic = nullptr;
1760  };
1761 
1762  m_controls->ShowCursor( true );
1763  // Set initial cursor
1764  setCursor();
1765 
1766  bool firstPoint = false;
1767  bool cancelled = false;
1768 
1769  // Prime the pump
1771 
1772  if( aImmediateMode )
1774 
1775  // Main loop: keep receiving events
1776  while( TOOL_EVENT* evt = Wait() )
1777  {
1778  if( firstPoint )
1779  m_frame->SetMsgPanel( graphic );
1780 
1781  setCursor();
1782 
1783  graphic->SetLayer( drawingLayer );
1784 
1785  grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
1786  grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
1787  VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), graphic );
1788  m_controls->ForceCursorPosition( true, cursorPos );
1789 
1790  if( evt->IsCancelInteractive() )
1791  {
1792  cleanup();
1793 
1794  if( !firstPoint )
1795  {
1796  // We've handled the cancel event. Don't cancel other tools
1797  evt->SetPassEvent( false );
1798  m_frame->PopTool( aTool );
1799  cancelled = true;
1800  }
1801 
1802  break;
1803  }
1804  else if( evt->IsActivate() )
1805  {
1806  if( evt->IsPointEditor() )
1807  {
1808  // don't exit (the point editor runs in the background)
1809  }
1810  else if( evt->IsMoveTool() )
1811  {
1812  cleanup();
1813  // leave ourselves on the stack so we come back after the move
1814  cancelled = true;
1815  break;
1816  }
1817  else
1818  {
1819  cleanup();
1820  m_frame->PopTool( aTool );
1821  cancelled = true;
1822  break;
1823  }
1824  }
1825  else if( evt->IsClick( BUT_LEFT ) )
1826  {
1827  if( !firstPoint )
1828  {
1830 
1831  m_controls->SetAutoPan( true );
1832  m_controls->CaptureCursor( true );
1833 
1834  drawingLayer = m_frame->GetActiveLayer();
1835  m_lineWidth = getSegmentWidth( drawingLayer );
1836 
1837  // Init the new item attributes
1838  // (non-geometric, those are handled by the manager)
1839  graphic->SetShape( SHAPE_T::ARC );
1840  graphic->SetWidth( m_lineWidth );
1841 
1842  if( !m_view->IsLayerVisible( drawingLayer ) )
1843  {
1844  m_frame->GetAppearancePanel()->SetLayerVisible( drawingLayer, true );
1845  m_frame->GetCanvas()->Refresh();
1846  }
1847 
1848  preview.Add( graphic );
1849  frame()->SetMsgPanel( graphic );
1850  firstPoint = true;
1851  }
1852 
1853  arcManager.AddPoint( cursorPos, true );
1854  }
1855  else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
1856  {
1857  arcManager.RemoveLastPoint();
1858  }
1859  else if( evt->IsMotion() )
1860  {
1861  // set angle snap
1862  arcManager.SetAngleSnap( Is45Limited() );
1863 
1864  // update, but don't step the manager state
1865  arcManager.AddPoint( cursorPos, false );
1866  }
1867  else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
1868  {
1869  drawingLayer = m_frame->GetActiveLayer();
1870  m_lineWidth = getSegmentWidth( drawingLayer );
1871 
1872  if( graphic )
1873  {
1874  if( !m_view->IsLayerVisible( drawingLayer ) )
1875  {
1876  m_frame->GetAppearancePanel()->SetLayerVisible( drawingLayer, true );
1877  m_frame->GetCanvas()->Refresh();
1878  }
1879 
1880  graphic->SetLayer( drawingLayer );
1881  graphic->SetWidth( m_lineWidth );
1882  m_view->Update( &preview );
1883  frame()->SetMsgPanel( graphic );
1884  }
1885  else
1886  {
1887  evt->SetPassEvent();
1888  }
1889  }
1890  else if( evt->IsAction( &PCB_ACTIONS::properties ) )
1891  {
1893  {
1894  graphic->SetAngle( 900, true );
1895  frame()->OnEditItemRequest( graphic );
1896  m_view->Update( &preview );
1897  frame()->SetMsgPanel( graphic );
1898  break;
1899  }
1900  else if( arcManager.GetStep() == KIGFX::PREVIEW::ARC_GEOM_MANAGER::SET_ANGLE )
1901  {
1902  frame()->OnEditItemRequest( graphic );
1903  m_view->Update( &preview );
1904  frame()->SetMsgPanel( graphic );
1905  break;
1906  }
1907  else
1908  {
1909  evt->SetPassEvent();
1910  }
1911  }
1912  else if( evt->IsClick( BUT_RIGHT ) )
1913  {
1915  }
1916  else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
1917  {
1919  graphic->SetWidth( m_lineWidth );
1920  m_view->Update( &preview );
1921  frame()->SetMsgPanel( graphic );
1922  }
1923  else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && m_lineWidth > WIDTH_STEP )
1924  {
1926  graphic->SetWidth( m_lineWidth );
1927  m_view->Update( &preview );
1928  frame()->SetMsgPanel( graphic );
1929  }
1930  else if( evt->IsAction( &PCB_ACTIONS::arcPosture ) )
1931  {
1932  arcManager.ToggleClockwise();
1933  }
1934  else if( evt->IsAction( &ACTIONS::updateUnits ) )
1935  {
1936  arcAsst.SetUnits( frame()->GetUserUnits() );
1937  m_view->Update( &arcAsst );
1938  evt->SetPassEvent();
1939  }
1940  else
1941  {
1942  evt->SetPassEvent();
1943  }
1944 
1945  if( arcManager.IsComplete() )
1946  {
1947  break;
1948  }
1949  else if( arcManager.HasGeometryChanged() )
1950  {
1951  updateArcFromConstructionMgr( arcManager, *graphic );
1952  m_view->Update( &preview );
1953  m_view->Update( &arcAsst );
1954 
1955  if( firstPoint )
1956  frame()->SetMsgPanel( graphic );
1957  else
1958  frame()->SetMsgPanel( board() );
1959  }
1960  }
1961 
1962  preview.Remove( graphic );
1963  m_view->Remove( &arcAsst );
1964  m_view->Remove( &preview );
1965  m_frame->SetMsgPanel( board() );
1966 
1968  m_controls->SetAutoPan( false );
1969  m_controls->CaptureCursor( false );
1970  m_controls->ForceCursorPosition( false );
1971 
1972  return !cancelled;
1973 }
1974 
1975 
1977 {
1978  bool clearSelection = false;
1979  *aZone = nullptr;
1980 
1981  // not an action that needs a source zone
1982  if( aMode == ZONE_MODE::ADD || aMode == ZONE_MODE::GRAPHIC_POLYGON )
1983  return true;
1984 
1986  const PCB_SELECTION& selection = selTool->GetSelection();
1987 
1988  if( selection.Empty() )
1989  {
1990  clearSelection = true;
1992  }
1993 
1994  // we want a single zone
1995  if( selection.Size() == 1 )
1996  *aZone = dyn_cast<ZONE*>( selection[0] );
1997 
1998  // expected a zone, but didn't get one
1999  if( !*aZone )
2000  {
2001  if( clearSelection )
2003 
2004  return false;
2005  }
2006 
2007  return true;
2008 }
2009 
2011 {
2012  if( m_isFootprintEditor && !m_frame->GetModel() )
2013  return 0;
2014 
2015  ZONE_MODE zoneMode = aEvent.Parameter<ZONE_MODE>();
2016  MODE drawMode = MODE::ZONE;
2017 
2018  if( aEvent.IsAction( &PCB_ACTIONS::drawRuleArea ) )
2019  drawMode = MODE::KEEPOUT;
2020 
2021  if( aEvent.IsAction( &PCB_ACTIONS::drawPolygon ) )
2022  drawMode = MODE::GRAPHIC_POLYGON;
2023 
2024  SCOPED_DRAW_MODE scopedDrawMode( m_mode, drawMode );
2025 
2026  // get a source zone, if we need one. We need it for:
2027  // ZONE_MODE::CUTOUT (adding a hole to the source zone)
2028  // ZONE_MODE::SIMILAR (creating a new zone using settings of source zone
2029  ZONE* sourceZone = nullptr;
2030 
2031  if( !getSourceZoneForAction( zoneMode, &sourceZone ) )
2032  return 0;
2033 
2034  // Turn zones on if they are off, so that the created object will be visible after completion
2036 
2038 
2039  params.m_keepout = drawMode == MODE::KEEPOUT;
2040  params.m_mode = zoneMode;
2041  params.m_sourceZone = sourceZone;
2042 
2043  if( zoneMode == ZONE_MODE::SIMILAR )
2044  params.m_layer = sourceZone->GetLayer();
2045  else
2046  params.m_layer = m_frame->GetActiveLayer();
2047 
2048  ZONE_CREATE_HELPER zoneTool( *this, params );
2049  // the geometry manager which handles the zone geometry, and hands the calculated points
2050  // over to the zone creator tool
2051  POLYGON_GEOM_MANAGER polyGeomMgr( zoneTool );
2052  bool constrainAngle = false;
2053  bool started = false;
2055  STATUS_TEXT_POPUP status( m_frame );
2056 
2057  status.SetTextColor( wxColour( 255, 0, 0 ) );
2058  status.SetText( _( "Self-intersecting polygons are not allowed" ) );
2059 
2060  std::string tool = aEvent.GetCommandStr().get();
2061  m_frame->PushTool( tool );
2062 
2063  auto setCursor =
2064  [&]()
2065  {
2067  };
2068 
2069  auto cleanup =
2070  [&] ()
2071  {
2072  polyGeomMgr.Reset();
2073  started = false;
2074  grid.ClearSkipPoint();
2075  m_controls->SetAutoPan( false );
2076  m_controls->CaptureCursor( false );
2077  };
2078 
2079  Activate();
2080  // Must be done after Activate() so that it gets set into the correct context
2081  m_controls->ShowCursor( true );
2082  // Set initial cursor
2083  setCursor();
2084 
2085  // Prime the pump
2086  if( aEvent.HasPosition() )
2087  m_toolMgr->PrimeTool( aEvent.Position() );
2088 
2089  // Main loop: keep receiving events
2090  while( TOOL_EVENT* evt = Wait() )
2091  {
2092  setCursor();
2093 
2094  LSET layers( m_frame->GetActiveLayer() );
2095  grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
2096  grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
2097 
2098  VECTOR2I cursorPos = evt->HasPosition() ? evt->Position() : m_controls->GetMousePosition();
2099  cursorPos = grid.BestSnapAnchor( cursorPos, layers );
2100 
2101  m_controls->ForceCursorPosition( true, cursorPos );
2102 
2103  if( ( sourceZone && sourceZone->GetHV45() ) || constrainAngle || Is45Limited() )
2105  else
2107 
2108  if( evt->IsCancelInteractive() )
2109  {
2110  if( polyGeomMgr.IsPolygonInProgress() )
2111  {
2112  cleanup();
2113  }
2114  else
2115  {
2116  // We've handled the cancel event. Don't cancel other tools
2117  evt->SetPassEvent( false );
2118  m_frame->PopTool( tool );
2119  break;
2120  }
2121  }
2122  else if( evt->IsActivate() )
2123  {
2124  if( polyGeomMgr.IsPolygonInProgress() )
2125  cleanup();
2126 
2127  if( evt->IsPointEditor() )
2128  {
2129  // don't exit (the point editor runs in the background)
2130  }
2131  else if( evt->IsMoveTool() )
2132  {
2133  // leave ourselves on the stack so we come back after the move
2134  break;
2135  }
2136  else
2137  {
2138  m_frame->PopTool( tool );
2139  break;
2140  }
2141  }
2142  else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
2143  {
2144  if( zoneMode != ZONE_MODE::SIMILAR )
2145  params.m_layer = frame()->GetActiveLayer();
2146 
2147  if( !m_view->IsLayerVisible( params.m_layer ) )
2148  {
2149  m_frame->GetAppearancePanel()->SetLayerVisible( params.m_layer, true );
2150  m_frame->GetCanvas()->Refresh();
2151  }
2152  }
2153  else if( evt->IsClick( BUT_RIGHT ) )
2154  {
2156  }
2157  // events that lock in nodes
2158  else if( evt->IsClick( BUT_LEFT )
2159  || evt->IsDblClick( BUT_LEFT )
2160  || evt->IsAction( &PCB_ACTIONS::closeOutline ) )
2161  {
2162  // Check if it is double click / closing line (so we have to finish the zone)
2163  const bool endPolygon = evt->IsDblClick( BUT_LEFT )
2164  || evt->IsAction( &PCB_ACTIONS::closeOutline )
2165  || polyGeomMgr.NewPointClosesOutline( cursorPos );
2166 
2167  if( endPolygon )
2168  {
2169  polyGeomMgr.SetFinished();
2170  polyGeomMgr.Reset();
2171 
2172  started = false;
2173  m_controls->SetAutoPan( false );
2174  m_controls->CaptureCursor( false );
2175  }
2176  // adding a corner
2177  else if( polyGeomMgr.AddPoint( cursorPos ) )
2178  {
2179  if( !started )
2180  {
2181  started = true;
2182 
2183  POLYGON_GEOM_MANAGER::LEADER_MODE leaderMode = polyGeomMgr.GetLeaderMode();
2184  constrainAngle = ( leaderMode == POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45 );
2185 
2186  m_controls->SetAutoPan( true );
2187  m_controls->CaptureCursor( true );
2188 
2189  if( !m_view->IsLayerVisible( params.m_layer ) )
2190  {
2191  m_frame->GetAppearancePanel()->SetLayerVisible( params.m_layer, true );
2192  m_frame->GetCanvas()->Refresh();
2193  }
2194  }
2195  }
2196  }
2197  else if( evt->IsAction( &PCB_ACTIONS::deleteLastPoint ) )
2198  {
2199  polyGeomMgr.DeleteLastCorner();
2200 
2201  if( !polyGeomMgr.IsPolygonInProgress() )
2202  {
2203  // report finished as an empty shape
2204  polyGeomMgr.SetFinished();
2205 
2206  // start again
2207  started = false;
2208  m_controls->SetAutoPan( false );
2209  m_controls->CaptureCursor( false );
2210  }
2211  }
2212  else if( polyGeomMgr.IsPolygonInProgress()
2213  && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
2214  {
2215  polyGeomMgr.SetCursorPosition( cursorPos );
2216 
2217  if( polyGeomMgr.IsSelfIntersecting( true ) )
2218  {
2219  wxPoint p = wxGetMousePosition() + wxPoint( 20, 20 );
2220  status.Move( p );
2221  status.PopupFor( 1500 );
2222  }
2223  else
2224  {
2225  status.Hide();
2226  }
2227  }
2228  else if( evt->IsAction( &PCB_ACTIONS::properties ) )
2229  {
2230  if( started )
2231  {
2232  frame()->OnEditItemRequest( zoneTool.GetZone() );
2233  zoneTool.OnGeometryChange( polyGeomMgr );
2234  frame()->SetMsgPanel( zoneTool.GetZone() );
2235  }
2236  else
2237  {
2238  evt->SetPassEvent();
2239  }
2240  }
2241  /*else if( evt->IsAction( &ACTIONS::updateUnits ) )
2242  {
2243  // If we ever have an assistant here that reports dimensions, we'll want to
2244  // update its units here....
2245  // zoneAsst.SetUnits( frame()->GetUserUnits() );
2246  // m_view->Update( &zoneAsst );
2247  evt->SetPassEvent();
2248  }*/
2249  else
2250  {
2251  evt->SetPassEvent();
2252  }
2253 
2254  } // end while
2255 
2257  m_controls->ForceCursorPosition( false );
2258  controls()->SetAutoPan( false );
2259  m_controls->CaptureCursor( false );
2260  return 0;
2261 }
2262 
2263 
2264 int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
2265 {
2266  if( m_isFootprintEditor )
2267  return 0;
2268 
2269  struct VIA_PLACER : public INTERACTIVE_PLACER_BASE
2270  {
2272  PCB_GRID_HELPER m_gridHelper;
2273  std::shared_ptr<DRC_ENGINE> m_drcEngine;
2274  int m_drcEpsilon;
2275  int m_worstClearance;
2276  bool m_allowDRCViolations;
2277 
2278  VIA_PLACER( PCB_BASE_EDIT_FRAME* aFrame ) :
2279  m_frame( aFrame ),
2280  m_gridHelper( aFrame->GetToolManager(), aFrame->GetMagneticItemsSettings() ),
2281  m_drcEngine( aFrame->GetBoard()->GetDesignSettings().m_DRCEngine ),
2282  m_drcEpsilon( aFrame->GetBoard()->GetDesignSettings().GetDRCEpsilon() ),
2283  m_worstClearance( 0 )
2284  {
2286 
2287  m_allowDRCViolations = router->Router()->Settings().AllowDRCViolations();
2288 
2289  try
2290  {
2291  m_drcEngine->InitEngine( aFrame->GetDesignRulesPath() );
2292 
2293  DRC_CONSTRAINT constraint;
2294 
2295  if( m_drcEngine->QueryWorstConstraint( CLEARANCE_CONSTRAINT, constraint ) )
2296  m_worstClearance = constraint.GetValue().Min();
2297 
2298  if( m_drcEngine->QueryWorstConstraint( HOLE_CLEARANCE_CONSTRAINT, constraint ) )
2299  m_worstClearance = std::max( m_worstClearance, constraint.GetValue().Min() );
2300 
2301  for( FOOTPRINT* footprint : aFrame->GetBoard()->Footprints() )
2302  {
2303  for( PAD* pad : footprint->Pads() )
2304  m_worstClearance = std::max( m_worstClearance, pad->GetLocalClearance() );
2305  }
2306  }
2307  catch( PARSE_ERROR& )
2308  {
2309  }
2310  }
2311 
2312  virtual ~VIA_PLACER()
2313  {
2314  }
2315 
2316  PCB_TRACK* findTrack( PCB_VIA* aVia )
2317  {
2318  const LSET lset = aVia->GetLayerSet();
2319  wxPoint position = aVia->GetPosition();
2320  BOX2I bbox = aVia->GetBoundingBox();
2321 
2322  std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
2323  auto view = m_frame->GetCanvas()->GetView();
2324  std::vector<PCB_TRACK*> possible_tracks;
2325 
2326  view->Query( bbox, items );
2327 
2328  for( auto it : items )
2329  {
2330  BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
2331 
2332  if( !(item->GetLayerSet() & lset ).any() )
2333  continue;
2334 
2335  if( PCB_TRACK* track = dyn_cast<PCB_TRACK*>( item ) )
2336  {
2337  if( TestSegmentHit( position, track->GetStart(), track->GetEnd(),
2338  ( track->GetWidth() + aVia->GetWidth() ) / 2 ) )
2339  possible_tracks.push_back( track );
2340  }
2341  }
2342 
2343  PCB_TRACK* return_track = nullptr;
2344  int min_d = std::numeric_limits<int>::max();
2345 
2346  for( PCB_TRACK* track : possible_tracks )
2347  {
2348  SEG test( track->GetStart(), track->GetEnd() );
2349  int dist = ( test.NearestPoint( position ) - position ).EuclideanNorm();
2350 
2351  if( dist < min_d )
2352  {
2353  min_d = dist;
2354  return_track = track;
2355  }
2356  }
2357 
2358  return return_track;
2359  }
2360 
2361  bool hasDRCViolation( PCB_VIA* aVia, BOARD_ITEM* aOther )
2362  {
2363  // It would really be better to know what particular nets a nettie should allow,
2364  // but for now it is what it is.
2365  if( DRC_ENGINE::IsNetTie( aOther ) )
2366  return false;
2367 
2368  BOARD_CONNECTED_ITEM* cItem = dynamic_cast<BOARD_CONNECTED_ITEM*>( aOther );
2369 
2370  if( cItem && cItem->GetNetCode() == aVia->GetNetCode() )
2371  return false;
2372 
2373  DRC_CONSTRAINT constraint;
2374  int clearance;
2375 
2376  for( PCB_LAYER_ID layer : aOther->GetLayerSet().Seq() )
2377  {
2378  if( !IsCopperLayer( layer ) )
2379  continue;
2380 
2381  constraint = m_drcEngine->EvalRules( CLEARANCE_CONSTRAINT, aVia, aOther, layer );
2382  clearance = constraint.GetValue().Min();
2383 
2384  if( clearance >= 0 )
2385  {
2386  std::shared_ptr<SHAPE> viaShape = DRC_ENGINE::GetShape( aVia, layer );
2387  std::shared_ptr<SHAPE> otherShape = DRC_ENGINE::GetShape( aOther, layer );
2388 
2389  if( viaShape->Collide( otherShape.get(), clearance - m_drcEpsilon ) )
2390  return true;
2391  }
2392  }
2393 
2394  std::unique_ptr<SHAPE_SEGMENT> holeShape;
2395 
2396  if( aOther->Type() == PCB_VIA_T )
2397  {
2398  PCB_VIA* via = static_cast<PCB_VIA*>( aOther );
2399  wxPoint pos = via->GetPosition();
2400 
2401  holeShape.reset( new SHAPE_SEGMENT( pos, pos, via->GetDrill() ) );
2402  }
2403  else if( aOther->Type() == PCB_PAD_T )
2404  {
2405  PAD* pad = static_cast<PAD*>( aOther );
2406 
2407  if( pad->GetDrillSize().x )
2408  holeShape.reset( new SHAPE_SEGMENT( *pad->GetEffectiveHoleShape() ) );
2409  }
2410 
2411  if( holeShape )
2412  {
2413  constraint = m_drcEngine->EvalRules( HOLE_CLEARANCE_CONSTRAINT, aVia, aOther,
2414  UNDEFINED_LAYER );
2415  clearance = constraint.GetValue().Min();
2416 
2417  if( clearance >= 0 )
2418  {
2419  std::shared_ptr<SHAPE> viaShape = DRC_ENGINE::GetShape( aVia, UNDEFINED_LAYER );
2420 
2421  if( viaShape->Collide( holeShape.get(), clearance - m_drcEpsilon ) )
2422  return true;
2423  }
2424  }
2425 
2426  return false;
2427  }
2428 
2429  bool checkDRCViolation( PCB_VIA* aVia )
2430  {
2431  std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
2432  std::set<BOARD_ITEM*> checkedItems;
2433  BOX2I bbox = aVia->GetBoundingBox();
2434 
2435  bbox.Inflate( m_worstClearance );
2436  m_frame->GetCanvas()->GetView()->Query( bbox, items );
2437 
2438  for( std::pair<KIGFX::VIEW_ITEM*, int> it : items )
2439  {
2440  BOARD_ITEM* item = dynamic_cast<BOARD_ITEM*>( it.first );
2441 
2442  if( !item )
2443  continue;
2444 
2445  if( item->Type() == PCB_ZONE_T || item->Type() == PCB_FP_ZONE_T )
2446  continue; // stitching vias bind to zones, so ignore them
2447 
2448  if( item->Type() == PCB_FOOTPRINT_T || item->Type() == PCB_GROUP_T )
2449  continue; // check against children, but not against footprint itself
2450 
2451  if( item->Type() == PCB_FP_TEXT_T && !static_cast<FP_TEXT*>( item )->IsVisible() )
2452  continue; // ignore hidden items
2453 
2454  if( checkedItems.count( item ) )
2455  continue;
2456 
2457  if( hasDRCViolation( aVia, item ) )
2458  return true;
2459 
2460  checkedItems.insert( item );
2461  }
2462 
2463  return false;
2464  }
2465 
2466  PAD* findPad( PCB_VIA* aVia )
2467  {
2468  const wxPoint position = aVia->GetPosition();
2469  const LSET lset = aVia->GetLayerSet();
2470 
2471  for( FOOTPRINT* fp : m_board->Footprints() )
2472  {
2473  for(PAD* pad : fp->Pads() )
2474  {
2475  if( pad->HitTest( position ) && ( pad->GetLayerSet() & lset ).any() )
2476  if( pad->GetNetCode() > 0 )
2477  return pad;
2478  }
2479  }
2480 
2481  return nullptr;
2482  }
2483 
2484  int findStitchedZoneNet( PCB_VIA* aVia )
2485  {
2486  const wxPoint position = aVia->GetPosition();
2487  const LSET lset = aVia->GetLayerSet();
2488 
2489  std::vector<ZONE*> foundZones;
2490 
2491  for( ZONE* zone : m_board->Zones() )
2492  {
2493  for( PCB_LAYER_ID layer : LSET( zone->GetLayerSet() & lset ).Seq() )
2494  {
2495  if( zone->HitTestFilledArea( layer, position ) )
2496  foundZones.push_back( zone );
2497  }
2498  }
2499 
2500  std::sort( foundZones.begin(), foundZones.end(),
2501  [] ( const ZONE* a, const ZONE* b )
2502  {
2503  return a->GetLayer() < b->GetLayer();
2504  } );
2505 
2506  // first take the net of the active layer
2507  for( ZONE* z : foundZones )
2508  {
2509  if( m_frame->GetActiveLayer() == z->GetLayer() )
2510  return z->GetNetCode();
2511  }
2512 
2513  // none? take the topmost visible layer
2514  for( ZONE* z : foundZones )
2515  {
2516  if( m_board->IsLayerVisible( z->GetLayer() ) )
2517  return z->GetNetCode();
2518  }
2519 
2520  return -1;
2521  }
2522 
2523  void SnapItem( BOARD_ITEM *aItem ) override
2524  {
2525  // If you place a Via on a track but not on its centerline, the current
2526  // connectivity algorithm will require us to put a kink in the track when
2527  // we break it (so that each of the two segments ends on the via center).
2528  // That's not ideal, and is in fact probably worse than forcing snap in
2529  // this situation.
2530 
2531  m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
2532  PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
2533  wxPoint position = via->GetPosition();
2534  PCB_TRACK* track = findTrack( via );
2535 
2536  if( track )
2537  {
2538  SEG trackSeg( track->GetStart(), track->GetEnd() );
2539  VECTOR2I snap = m_gridHelper.AlignToSegment( position, trackSeg );
2540 
2541  aItem->SetPosition( (wxPoint) snap );
2542  }
2543  }
2544 
2545  bool PlaceItem( BOARD_ITEM* aItem, BOARD_COMMIT& aCommit ) override
2546  {
2547  WX_INFOBAR* infobar = m_frame->GetInfoBar();
2548  PCB_VIA* via = static_cast<PCB_VIA*>( aItem );
2549  wxPoint viaPos = via->GetPosition();
2550  PCB_TRACK* track = findTrack( via );
2551  PAD* pad = findPad( via );
2552 
2553  if( track )
2554  via->SetNetCode( track->GetNetCode() );
2555  else if( pad )
2556  via->SetNetCode( pad->GetNetCode() );
2557  else
2558  via->SetNetCode( findStitchedZoneNet( via ) );
2559 
2560  if( !m_allowDRCViolations && checkDRCViolation( via ) )
2561  {
2562  m_frame->ShowInfoBarError( _( "Via location violates DRC." ), true,
2564  via->SetNetCode( 0 );
2565  return false;
2566  }
2567  else
2568  {
2570  infobar->Dismiss();
2571  }
2572 
2573  if( track )
2574  {
2575  if( viaPos != track->GetStart() && viaPos != track->GetEnd() )
2576  {
2577  aCommit.Modify( track );
2578 
2579  PCB_TRACK* newTrack = dynamic_cast<PCB_TRACK*>( track->Clone() );
2580  const_cast<KIID&>( newTrack->m_Uuid ) = KIID();
2581 
2582  track->SetEnd( viaPos );
2583  newTrack->SetStart( viaPos );
2584  aCommit.Add( newTrack );
2585  }
2586  }
2587  else if( !pad )
2588  {
2589  via->SetNetCode( findStitchedZoneNet( via ) );
2590  via->SetIsFree();
2591  }
2592 
2593  aCommit.Add( aItem );
2594  return true;
2595  }
2596 
2597  std::unique_ptr<BOARD_ITEM> CreateItem() override
2598  {
2600  PCB_VIA* via = new PCB_VIA( m_board );
2601 
2602  via->SetNetCode( 0 );
2603  via->SetViaType( bds.m_CurrentViaType );
2604 
2605  // for microvias, the size and hole will be changed later.
2606  via->SetWidth( bds.GetCurrentViaSize() );
2607  via->SetDrill( bds.GetCurrentViaDrill() );
2608 
2609  // Usual via is from copper to component.
2610  // layer pair is B_Cu and F_Cu.
2611  via->SetLayerPair( B_Cu, F_Cu );
2612 
2613  PCB_LAYER_ID first_layer = m_frame->GetActiveLayer();
2614  PCB_LAYER_ID last_layer;
2615 
2616  // prepare switch to new active layer:
2617  if( first_layer != m_frame->GetScreen()->m_Route_Layer_TOP )
2618  last_layer = m_frame->GetScreen()->m_Route_Layer_TOP;
2619  else
2620  last_layer = m_frame->GetScreen()->m_Route_Layer_BOTTOM;
2621 
2622  // Adjust the actual via layer pair
2623  switch( via->GetViaType() )
2624  {
2625  case VIATYPE::BLIND_BURIED:
2626  via->SetLayerPair( first_layer, last_layer );
2627  break;
2628 
2629  case VIATYPE::MICROVIA: // from external to the near neighbor inner layer
2630  {
2631  PCB_LAYER_ID last_inner_layer =
2632  ToLAYER_ID( ( m_board->GetCopperLayerCount() - 2 ) );
2633 
2634  if( first_layer == B_Cu )
2635  last_layer = last_inner_layer;
2636  else if( first_layer == F_Cu )
2637  last_layer = In1_Cu;
2638  else if( first_layer == last_inner_layer )
2639  last_layer = B_Cu;
2640  else if( first_layer == In1_Cu )
2641  last_layer = F_Cu;
2642 
2643  // else error: will be removed later
2644  via->SetLayerPair( first_layer, last_layer );
2645 
2646  // Update diameter and hole size, which where set previously for normal vias
2647  NETCLASS* netClass = via->GetNetClass();
2648 
2649  via->SetWidth( netClass->GetuViaDiameter() );
2650  via->SetDrill( netClass->GetuViaDrill() );
2651  }
2652  break;
2653 
2654  default:
2655  break;
2656  }
2657 
2658  return std::unique_ptr<BOARD_ITEM>( via );
2659  }
2660  };
2661 
2662  VIA_PLACER placer( frame() );
2663 
2664  SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::VIA );
2665 
2666  doInteractiveItemPlacement( aEvent.GetCommandStr().get(), &placer, _( "Place via" ),
2668 
2669  return 0;
2670 }
2671 
2672 
2674 {
2675  assert( m_board );
2676  return m_board->GetDesignSettings().GetLineThickness( aLayer );
2677 }
2678 
2679 
2680 const unsigned int DRAWING_TOOL::WIDTH_STEP = Millimeter2iu( 0.1 );
2681 
2682 
2684 {
2685 
2706 }
void SetMirrored(bool isMirrored)
Definition: eda_text.h:188
double EuclideanNorm(const wxPoint &vector)
Euclidean norm of a 2D vector.
Definition: trigo.h:146
OPT_TOOL_EVENT eventHandler(const wxMenuEvent &aEvent) override
Event handler stub.
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
Definition: pcb_actions.h:66
static TOOL_ACTION selectionClear
Clear the current selection.
Definition: pcb_actions.h:59
virtual void ShowCursor(bool aEnabled)
Enable or disables display of cursor.
static bool ShowAlways(const SELECTION &aSelection)
The default condition function (always returns true).
double GetAngle() const
Return the angle of the crossbar.
static TOOL_ACTION drawLine
Definition: pcb_actions.h:142
static TOOL_ACTION drawCenterDimension
Definition: pcb_actions.h:149
Container to handle a stock of specific vias each with unique diameter and drill sizes in the BOARD c...
wxString MessageTextFromValue(EDA_UNITS aUnits, int aValue, bool aAddUnitLabel, EDA_DATA_TYPE aType)
Convert a value to a string using double notation.
Definition: base_units.cpp:104
const EDA_RECT GetBoundingBox() const override
Return the orthogonal bounding box of this object for display purposes.
Definition: pcb_track.cpp:240
Manage the construction of a circular arc though sequential setting of critical points: center,...
void SetObjectVisible(GAL_LAYER_ID aLayer, bool aVisible=true)
COMMIT & Modify(EDA_ITEM *aItem)
Create an undo entry for an item that has been already modified.
Definition: commit.h:103
Arcs (with rounded ends)
int DrawCircle(const TOOL_EVENT &aEvent)
Start interactively drawing a circle.
VECTOR2< T > GetVectorSnapped45(const VECTOR2< T > &aVec, bool only45=false)
Snap a vector onto the nearest 0, 45 or 90 degree line.
TOOL_MENU m_menu
The functions below are not yet implemented - their interface may change.
static TOOL_ACTION placeImportedGraphics
Definition: pcb_actions.h:161
class PCB_DIM_ALIGNED, a linear dimension (graphic item)
Definition: typeinfo.h:100
virtual void Clear() override
Remove all the stored items from the group.
Definition: selection.h:82
virtual BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Returns the BOARD_DESIGN_SETTINGS for the open project.
class PCB_DIM_LEADER, a leader dimension (graphic item)
Definition: typeinfo.h:101
VECTOR2I GetEndRadiusEnd() const
Get the radius of the arc (valid if step >= SET_START)
void SetCurrentCursor(KICURSOR aCursor)
Set the current cursor shape for this panel.
class FP_TEXT, text in a footprint
Definition: typeinfo.h:92
void SetHeight(int aHeight)
Set the distance from the feature points to the crossbar line.
ZONES & Zones()
Definition: board.h:239
Add a new zone with the same settings as an existing one.
void SetShape(SHAPE_T aShape)
Definition: pcb_shape.h:109
int DrawVia(const TOOL_EVENT &aEvent)
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.
virtual VECTOR2D GetMousePosition(bool aWorldCoordinates=true) const =0
Return the current mouse pointer position.
BOARD * board() const
int DrawZone(const TOOL_EVENT &aEvent)
Start interactively drawing a zone.
BOARD * m_board
Definition: drawing_tool.h:266
LEADER_MODE
The kind of the leader line.
ZONE * m_sourceZone
Zone leader mode.
static TOOL_ACTION incWidth
Increase width of currently drawn line.
Definition: pcb_actions.h:167
wxPoint GetPosition() const override
Definition: pcb_track.h:392
virtual void SetPosition(const wxPoint &aPos) override
Definition: pcb_text.h:81
wxString GetDesignRulesPath()
Return the absolute path to the design rules file for the currently-loaded board.
const wxPoint & GetEnd() const
Definition: pcb_track.h:105
int GetUserUnits()
Return the currently selected user unit value for the interface.
virtual EDA_ITEM * Clone() const override
Create a duplicate of this item with linked list members set to NULL.
Definition: pcb_track.cpp:57
static TOOL_ACTION drawSimilarZone
Definition: pcb_actions.h:156
#define IS_NEW
New item, just created.
virtual void SetLayer(PCB_LAYER_ID aLayer)
Set the layer this item is on.
Definition: board_item.h:192
Defines the structure of a menu based on ACTIONs.
Definition: action_menu.h:48
This file is part of the common library.
virtual void SetPosition(const wxPoint &aPos)
Definition: eda_item.h:253
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:80
virtual void Add(EDA_ITEM *aItem)
Definition: selection.cpp:31
static TOOL_ACTION drawArc
Definition: pcb_actions.h:146
class PCB_GROUP, a set of BOARD_ITEMs
Definition: typeinfo.h:108
void SetIcon(BITMAPS aIcon)
Assign an icon for the entry.
Definition: action_menu.cpp:73
int DrawLine(const TOOL_EVENT &aEvent)
Start interactively drawing a line.
static std::shared_ptr< SHAPE > GetShape(BOARD_ITEM *aItem, PCB_LAYER_ID aLayer)
void SetEnd(const wxPoint &aEnd)
Definition: pcb_track.h:104
COMMIT & Add(EDA_ITEM *aItem)
Notify observers that aItem has been added.
Definition: commit.h:78
Represents an assistant draw when interactively drawing a line or circle on a canvas.
A set of BOARD_ITEMs (i.e., without duplicates).
Definition: pcb_group.h:50
PCB_TEXT & Text()
void RunMainStack(std::function< void()> aFunc)
Call a function using the main stack.
static void updateArcFromConstructionMgr(const KIGFX::PREVIEW::ARC_GEOM_MANAGER &aMgr, PCB_SHAPE &aArc)
Update an arc PCB_SHAPE from the current state of an Arc Geometry Manager.
class PCB_DIM_CENTER, a center point marking (graphic item)
Definition: typeinfo.h:102
Represent a very simple geometry manager for items that have a start and end point.
std::list< std::unique_ptr< EDA_ITEM > > & GetImportedItems()
int DrawRectangle(const TOOL_EVENT &aEvent)
Start interactively drawing a rectangle.
Extension of STATUS_POPUP for displaying a single line text.
Definition: status_popup.h:79
PCB_DRAW_PANEL_GAL * GetCanvas() const override
Return a pointer to GAL-based canvas of given EDA draw frame.
Class that computes missing connections on a PCB.
CONDITIONAL_MENU & GetMenu()
Definition: tool_menu.cpp:46
static const unsigned int WIDTH_STEP
Definition: drawing_tool.h:272
void SetItalic(bool isItalic)
Definition: eda_text.h:179
TOOL_MANAGER * m_toolMgr
Definition: tool_base.h:214
virtual void OnEditItemRequest(BOARD_ITEM *aItem)=0
Install the corresponding dialog editor for the given item.
static TOOL_ACTION drawAlignedDimension
Definition: pcb_actions.h:148
static TOOL_ACTION cancelInteractive
Definition: actions.h:62
virtual void PushTool(const std::string &actionName)
NB: the definition of "tool" is different at the user level.
virtual void Remove(VIEW_ITEM *aItem)
Remove a VIEW_ITEM from the view.
Definition: view.cpp:353
void SetTextPos(const wxPoint &aPoint)
Definition: eda_text.h:246
static TOOL_ACTION properties
Activation of the edit tool.
Definition: pcb_actions.h:117
usual segment : line with rounded ends
static TOOL_ACTION drawOrthogonalDimension
Definition: pcb_actions.h:150
void SetText(const wxString &aNewText)
Set the override text - has no effect if m_overrideValue == false.
void SetFlags(EDA_ITEM_FLAGS aMask)
Definition: eda_item.h:153
class PCB_TEXT, text on a layer
Definition: typeinfo.h:91
PCB_LAYER_ID m_layer
The zone mode to operate in.
bool RunAction(const std::string &aActionName, bool aNow=false, T aParam=NULL)
Run the specified action.
Definition: tool_manager.h:143
static TOOL_ACTION drawPolygon
Definition: pcb_actions.h:143
double RAD2DECIDEG(double rad)
Definition: trigo.h:234
VECTOR2I GetStartRadiusEnd() const
Get the coordinates of the arc end point.
void PrimeTool(const VECTOR2D &aPosition)
"Prime" a tool by sending a cursor left-click event with the mouse position set to the passed in posi...
static TOOL_ACTION trackViaSizeChanged
Definition: pcb_actions.h:299
virtual LSET GetLayerSet() const override
Return a std::bitset of all layers on which the item physically resides.
Definition: pcb_track.cpp:402
void RemoveLastPoint()
Undo the last point, and move the manager back to the previous step.
class FP_SHAPE, a footprint edge
Definition: typeinfo.h:93
class PAD, a pad in a footprint
Definition: typeinfo.h:89
#define KI_FALLTHROUGH
The KI_FALLTHROUGH macro is to be used when switch statement cases should purposely fallthrough from ...
Definition: macros.h:83
Abstract dimension API.
Definition: pcb_dimension.h:95
void SetTextSize(const wxSize &aNewSize)
Definition: eda_text.h:237
int GetWidth() const
Definition: pcb_track.h:102
void Reset()
Reset the manager to the initial state.
virtual MAGNETIC_SETTINGS * GetMagneticItemsSettings()
T Min() const
Definition: minoptmax.h:33
Parameters used to fully describe a zone creation process.
bool NewPointClosesOutline(const VECTOR2I &aPt) const
int GetTextThickness(PCB_LAYER_ID aLayer) const
Return the default text thickness from the layer class for the given layer.
static bool IsNetTie(BOARD_ITEM *aItem)
VECTOR2< int > VECTOR2I
Definition: vector2d.h:623
VIATYPE m_CurrentViaType
(VIA_BLIND_BURIED, VIA_THROUGH, VIA_MICROVIA)
LSEQ Seq(const PCB_LAYER_ID *aWishListSequence, unsigned aCount) const
Return an LSEQ from the union of this LSET and a desired sequence.
Definition: lset.cpp:411
void SetLeaderMode(LEADER_MODE aMode)
Set the leader mode to use when calculating the leader/returner lines.
APPEARANCE_CONTROLS * GetAppearancePanel()
LEADER_MODE GetLeaderMode() const
void SetExtensionOffset(int aOffset)
A base class derived from BOARD_ITEM for items that can be connected and have a net,...
static TOOL_ACTION setAnchor
Definition: pcb_actions.h:162
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition: board.cpp:589
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).
An orthogonal dimension is like an aligned dimension, but the extension lines are locked to the X or ...
static TOOL_ACTION placeText
Definition: pcb_actions.h:147
static void updateSegmentFromGeometryMgr(const KIGFX::PREVIEW::TWO_POINT_GEOMETRY_MANAGER &aMgr, PCB_SHAPE *aGraphic)
Update a PCB_SHAPE from the current state of a #TWO_POINT_GEOMETRY_MANAGER.
void AddPoint(const VECTOR2I &aPt, bool aLockIn)
Add a point to the construction manager.
PADS & Pads()
Definition: footprint.h:168
void ShowTextPropertiesDialog(BOARD_ITEM *aText)
Control for copper zone opacity/visibility (color ignored)
Definition: layer_ids.h:226
static TOOL_ACTION decWidth
Decrease width of currently drawn line.
Definition: pcb_actions.h:170
This file contains miscellaneous commonly used macros and functions.
bool IsAction(const TOOL_ACTION *aAction) const
Test if the event contains an action issued upon activation of the given TOOL_ACTION.
Definition: tool_event.cpp:88
bool m_inDrawingTool
Definition: drawing_tool.h:269
void SetLineThickness(int aWidth)
KIGFX::VIEW * m_view
Definition: drawing_tool.h:264
For better understanding of the points that make a dimension:
bool GetTextUpright(PCB_LAYER_ID aLayer) const
PCB_BASE_EDIT_FRAME * frame() const
bool GetTextItalic(PCB_LAYER_ID aLayer) const
virtual PCB_LAYER_ID GetActiveLayer() const
void ShowInfoBarError(const wxString &aErrorMsg, bool aShowCloseButton=false, WX_INFOBAR::MESSAGE_TYPE aType=WX_INFOBAR::MESSAGE_TYPE::GENERIC)
Show the WX_INFOBAR displayed on the top of the canvas with a message and an error icon on the left o...
int GetLineThickness(PCB_LAYER_ID aLayer) const
Return the default graphic segment thickness from the layer class for the given layer.
void SetLayer(PCB_LAYER_ID aLayer) override
Set the layer this item is on.
void SetViaSizeIndex(unsigned aIndex)
Set the current via size list index to aIndex.
SHAPE_T
The set of shapes for PCB graphics and tracks and footprint graphics in the .m_Shape member.
Definition: board_item.h:46
PCB_SELECTION & GetSelection()
Return the set of currently selected items.
Definition: kiid.h:44
virtual const wxPoint & GetStart() const
The dimension's origin is the first feature point for the dimension.
void SetReferencePoint(const VECTOR2I &aP)
Definition: selection.h:186
virtual void WarpCursor(const VECTOR2D &aPosition, bool aWorldCoordinates=false, bool aWarpView=false)=0
If enabled (.
int PlaceImportedGraphics(const TOOL_EVENT &aEvent)
Place a drawing imported from a DXF or SVG file in footprint editor.
bool drawSegment(const std::string &aTool, PCB_SHAPE **aGraphic, OPT< VECTOR2D > aStartingPoint)
Start drawing a selected shape (i.e.
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...
void Dismiss() override
Dismisses the infobar and updates the containing layout and AUI manager (if one is provided).
Definition: infobar.cpp:175
LSET is a set of PCB_LAYER_IDs.
Definition: layer_ids.h:502
RAII class that sets an value at construction and resets it to the original value at destruction.
const PCB_SELECTION & selection() const
FOOTPRINT * GetFirstFootprint() const
Get the first footprint on the board or nullptr.
Definition: board.h:317
const auto NULLOPT
Definition: optional.h:9
void update() override
Update menu state stub.
VECTOR2< double > VECTOR2D
Definition: vector2d.h:622
bool IsBackLayer(PCB_LAYER_ID aLayerId)
Layer classification: check if it's a back layer.
Definition: layer_ids.h:886
void SetMsgPanel(const std::vector< MSG_PANEL_ITEM > &aList)
Clear the message panel and populates it with the contents of aList.
Waiting to lock in the arc start point.
MESSAGE_TYPE GetMessageType() const
Definition: infobar.h:99
Allow repeat placement of the item.
unsigned GetViaSizeIndex() const
virtual void PopTool(const std::string &actionName)
virtual void CaptureCursor(bool aEnabled)
Force the cursor to stay within the drawing panel area.
virtual void SetEnd(const wxPoint &aPoint)
virtual PCB_LAYER_ID GetLayer() const override
Return the primary layer this item is on.
Definition: zone.cpp:218
T Parameter() const
Return a non-standard parameter assigned to the event.
Definition: tool_event.h:432
void Reset(RESET_REASON aReason) override
Bring the tool to a known, initial state.
coord_type GetWidth() const
Definition: box2.h:180
VECTOR2I GetOrigin() const
< Get the center point of the arc (valid when state > SET_ORIGIN)
wxPoint GetPosition() const override
bool Init() override
Init() is called once upon a registration of the tool.
FOOTPRINTS & Footprints()
Definition: board.h:233
Generic, UI-independent tool event.
Definition: tool_event.h:152
A collection of nets and the parameters used to route or test these nets.
Definition: netclass.h:46
bool TestSegmentHit(const wxPoint &aRefPoint, const wxPoint &aStart, const wxPoint &aEnd, int aDist)
Test if aRefPoint is with aDistance on the line defined by aStart and aEnd.
Definition: trigo.cpp:129
Mark the center of a circle or arc with a cross shape.
unsigned int m_lineWidth
Definition: drawing_tool.h:271
FOOTPRINT * footprint() const
void SetOrigin(const VECTOR2I &aOrigin)
< Set the origin of the ruler (the fixed end)
KIGFX::PCB_VIEW * view() const
void SetArrowLength(int aLength)
static TOOL_ACTION drawRectangle
Definition: pcb_actions.h:144
void SetStart(const wxPoint &aStart)
Definition: pcb_track.h:107
PCB_LAYER_ID m_Route_Layer_BOTTOM
Definition: pcb_screen.h:44
#define _(s)
Add a new zone/keepout with fresh settings.
int PlaceCharacteristics(const TOOL_EVENT &aEvent)
bool drawArc(const std::string &aTool, PCB_SHAPE **aGraphic, bool aImmediateMode)
Start drawing an arc.
bool IsSelfIntersecting(bool aIncludeLeaderPts) const
Check whether the locked points constitute a self-intersecting outline.
virtual void ForceCursorPosition(bool aEnabled, const VECTOR2D &aPosition=VECTOR2D(0, 0))
Place the cursor immediately at a given point.
void MoveAnchorPosition(const wxPoint &aMoveVector)
Move the reference point of the footprint.
Definition: footprint.cpp:1545
a few functions useful in geometry calculations.
static LSET AllLayersMask()
Definition: lset.cpp:787
static TOOL_ACTION drawVia
Definition: pcb_actions.h:153
Handle a list of polygons defining a copper zone.
Definition: zone.h:56
KIGFX::VIEW_CONTROLS * m_controls
Definition: drawing_tool.h:265
class ZONE, a copper pour area
Definition: typeinfo.h:105
Create an item immediately on placement starting, otherwise show the pencil cursor until the item is ...
bool DisableGridSnapping() const
Definition: tool_event.h:341
int GetuViaDiameter() const
Definition: netclass.h:140
void SetCenter(const wxPoint &aCenterPoint)
Definition: pcb_shape.h:198
Unconstrained point-to-point.
bool Is45Limited() const
Should the tool use its 45° mode option?
static TOOL_ACTION drawZoneCutout
Definition: pcb_actions.h:155
bool m_isFootprintEditor
bool GetHV45() const
Definition: zone.h:802
virtual KIGFX::PCB_VIEW * GetView() const override
Return a pointer to the #VIEW instance used in the panel.
int getSegmentWidth(PCB_LAYER_ID aLayer) const
ZONE_MODE
Definition: pcb_actions.h:33
KIGFX::VIEW * getView() const
Returns the instance of #VIEW object used in the application.
Definition: tool_base.cpp:36
virtual void SetAutoPan(bool aEnabled)
Turn on/off auto panning (this feature is used when there is a tool active (eg.
bool IsCopperLayer(LAYER_NUM aLayerId)
Tests whether a layer is a copper layer.
Definition: layer_ids.h:787
bool Empty() const
Checks if there is anything selected.
Definition: selection.h:97
void setTransitions() override
This method is meant to be overridden in order to specify handlers for events.
class FOOTPRINT, a footprint
Definition: typeinfo.h:88
PCB_BASE_EDIT_FRAME * m_frame
Definition: drawing_tool.h:267
void SetSnap(bool aSnap)
Definition: grid_helper.h:64
void DeleteLastCorner()
Remove the last-added point from the polygon.
const KIID m_Uuid
Definition: eda_item.h:475
virtual BOARD_ITEM_CONTAINER * GetModel() const =0
static TOOL_ACTION placeCharacteristics
Definition: pcb_actions.h:157
static TOOL_ACTION drawCircle
Definition: pcb_actions.h:145
void constrainDimension(PCB_DIMENSION_BASE *aDim)
Force the dimension lime to be drawn on multiple of 45 degrees.
Definition: seg.h:40
EDA_UNITS
Definition: eda_units.h:38
No updates are required.
Definition: view_item.h:51
OPT< std::string > GetCommandStr() const
Definition: tool_event.h:460
TOOLS_HOLDER * GetToolHolder() const
Definition: tool_manager.h:294
void SetKeepUpright(bool aKeepUpright)
Definition: fp_text.h:115
A modified version of the wxInfoBar class that allows us to:
Definition: infobar.h:73
void OnGeometryChange(const POLYGON_GEOM_MANAGER &aMgr) override
Called when the polygon is complete.
static TOOL_ACTION arcPosture
Switch posture when drawing arc.
Definition: pcb_actions.h:173
BOX2< Vec > & Inflate(coord_type dx, coord_type dy)
Inflates the rectangle horizontally by dx and vertically by dy.
Definition: box2.h:281
bool IsLayerVisible(PCB_LAYER_ID aLayer) const
A proxy function that calls the correspondent function in m_BoardSettings tests whether a given layer...
Definition: board.cpp:472
static TOOL_ACTION layerChanged
Definition: pcb_actions.h:289
bool getSourceZoneForAction(ZONE_MODE aMode, ZONE **aZone)
Draw a polygon, that is added as a zone or a keepout area.
ACTION_MENU * create() const override
< Return an instance of this class. It has to be overridden in inheriting classes.
static TOOL_ACTION drawLeader
Definition: pcb_actions.h:151
A filename or source description, a problem input line, a line number, a byte offset,...
Definition: ki_exception.h:118
static TOOL_ACTION updateUnits
Definition: actions.h:147
int SetAnchor(const TOOL_EVENT &aEvent)
Place the footprint anchor (only in footprint editor).
static TOOL_ACTION drawRuleArea
Definition: pcb_actions.h:154
void SetTitle(const wxString &aTitle) override
Set title for the menu.
Definition: action_menu.cpp:87
Waiting to lock in the arc end point.
KIGFX::VIEW_CONTROLS * controls() const
const MINOPTMAX< int > & GetValue() const
Definition: drc_rule.h:122
Common, abstract interface for edit frames.
void ToggleClockwise()
Set angle snapping (for the next point)
void SetStart(const wxPoint &aStart)
Definition: pcb_shape.h:127
PCB_LAYER_ID
A quick note on layer IDs:
Definition: layer_ids.h:64
int PlaceStackup(const TOOL_EVENT &aEvent)
TOOL_MANAGER * getToolManager() const
ARC_STEPS GetStep() const
Get the current step the manager is on (useful when drawing something depends on the current state)
static TOOL_ACTION placeStackup
Definition: pcb_actions.h:159
Definition: layer_ids.h:70
void AddSubMenu(std::shared_ptr< ACTION_MENU > aSubMenu)
Store a submenu of this menu model.
Definition: tool_menu.cpp:52
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
virtual void Remove(EDA_ITEM *aItem)
Definition: selection.cpp:43
int GetCopperLayerCount() const
Definition: board.cpp:454
static TOOL_ACTION selectItem
Select an item (specified as the event parameter).
Definition: pcb_actions.h:62
class ZONE, managed by a footprint
Definition: typeinfo.h:94
virtual void Refresh(bool aEraseBackground=true, const wxRect *aRect=nullptr) override
Update the board display after modifying it by a python script (note: it is automatically called by a...
virtual void SetStart(const wxPoint &aPoint)
An adjunct helper to the DRAWING_TOOL interactive tool, which handles incoming geometry changes from ...
The main frame for Pcbnew.
void Clear()
Remove all the entries from the menu (as well as its title).
PCBNEW_SETTINGS & Settings()
int Size() const
Returns the number of selected parts.
Definition: selection.h:103
void SetWidth(int aWidth)
Definition: pcb_shape.h:96
The selection tool: currently supports:
Represents an assistant draw when interactively drawing an arc on a canvas.
Definition: arc_assistant.h:38
static TOOL_ACTION resetLocalCoords
Definition: actions.h:150
void Reset()
Clear the manager state and start again.
virtual void Push(const wxString &aMessage=wxT("A commit"), bool aCreateUndoEntry=true, bool aSetDirtyBit=true) override
Revert the commit by restoring the modified items state.
wxPoint GetPosition() const override
Definition: footprint.h:186
PCB_SCREEN * GetScreen() const override
Return a pointer to a BASE_SCREEN or one of its derivatives.
RESET_REASON
Determine the reason of reset for a tool.
Definition: tool_base.h:77
std::vector< VIA_DIMENSION > m_ViasDimensionsList
static TOOL_ACTION deleteLastPoint
Definition: pcb_actions.h:163
void SetArcEnd(const wxPoint &aArcEndPoint)
Initialize the end arc point.
Definition: pcb_shape.h:193
int ToggleLine45degMode(const TOOL_EVENT &aEvent)
Toggle the 45 degree angle constraint for graphic lines.
boost::optional< T > OPT
Definition: optional.h:7
bool IsReactivate() const
Definition: tool_event.h:252
void Activate()
Run the tool.
ZONE * GetZone() const
const wxPoint & GetTextPos() const
Definition: eda_text.h:247
WX_INFOBAR * GetInfoBar()
A leader is a dimension-like object pointing to a specific point.
constexpr int delta
int GetuViaDrill() const
Definition: netclass.h:144
Implementing DIALOG_TRACK_VIA_SIZE_BASE.
class PCB_DIM_ORTHOGONAL, a linear dimension constrained to x/y
Definition: typeinfo.h:103
class PCB_VIA, a via (like a track segment on a copper layer)
Definition: typeinfo.h:96
void SetTextThickness(int aWidth)
The TextThickness is that set by the user.
Definition: eda_text.h:159
void SetLayerVisible(LAYER_NUM aLayer, bool isVisible)
bool HasPosition() const
Definition: tool_event.h:240
virtual const wxPoint & GetEnd() const
virtual void Add(VIEW_ITEM *aItem, int aDrawPriority=-1)
Add a VIEW_ITEM to the view.
Definition: view.cpp:323
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:54
virtual 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:429
void doInteractiveItemPlacement(const std::string &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...
BOARD * GetBoard() const
KIGFX::VIEW_CONTROLS * getViewControls() const
Return the instance of VIEW_CONTROLS object used in the application.
Definition: tool_base.cpp:42
segment with non rounded ends
ROUTER * Router() const
bool m_keepout
< Should create a keepout zone?
Definition: pad.h:57
MODE GetDrawingMode() const
Return the current drawing mode of the DRAWING_TOOL or #MODE::NONE if not currently in any drawing mo...
bool AddPoint(const VECTOR2I &aPt)
Lock in a polygon point.
void SetCursorPosition(const VECTOR2I &aPos)
Set the current cursor position.
virtual void SetAngle(double aAngle, bool aUpdateEnd=true)
Set the angle for arcs, and normalizes it within the range 0 - 360 degrees.
Definition: pcb_shape.cpp:519
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
static constexpr int Millimeter2iu(double mm)
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 SetFinished()
Mark the polygon finished and update the client.
int DrawArc(const TOOL_EVENT &aEvent)
Start interactively drawing an arc.
static TOOL_ACTION toggle45
Definition: pcb_actions.h:418
void ShowContextMenu(SELECTION &aSelection)
Helper function to set and immediately show a CONDITIONAL_MENU in concert with the given SELECTION.
Definition: tool_menu.cpp:59
ROUTING_SETTINGS & Settings()
Definition: pns_router.h:181
bool NoPrintableChars(const wxString &aString)
Return true if the string is empty or contains only whitespace.
virtual const wxString & GetText() const
Return the string associated with the text object.
Definition: eda_text.h:133
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Definition: lset.cpp:905
int GetLineThickness() const
wxSize GetTextSize(PCB_LAYER_ID aLayer) const
Return the default text size from the layer class for the given layer.
static TOOL_ACTION drawZone
Definition: pcb_actions.h:152
VECTOR2I AlignToSegment(const VECTOR2I &aPoint, const SEG &aSeg)
Class that handles the drawing of a polygon, including management of last corner deletion and drawing...
std::shared_ptr< DRC_ENGINE > m_DRCEngine
const VECTOR2D Position() const
Returns the point where dragging has started.
Definition: tool_event.h:263
static TOOL_ACTION selectionCursor
Select a single item under the cursor position.
Definition: pcb_actions.h:56
EDA_UNITS GetUserUnits() const
Return the user units currently in use.
ZONE_MODE m_mode
Zone settings source (for similar and cutout zones)
PCB_LAYER_ID m_Route_Layer_TOP
Definition: pcb_screen.h:43
const wxPoint & GetStart() const
Definition: pcb_track.h:108
static TOOL_ACTION refreshPreview
Definition: actions.h:106
void UseCustomTrackViaSize(bool aEnabled)
Enables/disables custom track/via size settings.
void SetEnd(const wxPoint &aEnd)
Definition: pcb_shape.h:137
virtual LSET GetLayerSet() const
Return a std::bitset of all layers on which the item physically resides.
Definition: board_item.h:176
VECTOR2D GetCursorPosition() const
Return the current cursor position in world coordinates.
EDA_ITEM * Front() const
Definition: selection.h:144
static TOOL_ACTION cursorClick
Definition: actions.h:123
virtual void Update(const VIEW_ITEM *aItem, int aUpdateFlags) const
For dynamic VIEWs, inform the associated VIEW that the graphical representation of this item has chan...
Definition: view.cpp:1518
VECTOR2D m_LocalOrigin
Relative Screen cursor coordinate (on grid) in user units.
Definition: base_screen.h:90
void SetArcStart(const wxPoint &aArcStartPoint)
Initialize the start arc point.
Definition: pcb_shape.h:183
void SetEnd(const VECTOR2I &aEnd)
Set the current end of the rectangle (the end that moves with the cursor.
OPT< TOOL_EVENT > OPT_TOOL_EVENT
Definition: tool_event.h:548
KICAD_T Type() const
Returns the type of object.
Definition: eda_item.h:113
int DrawDimension(const TOOL_EVENT &aEvent)
Start interactively drawing a dimension.
Container for design settings for a BOARD object.
bool IsLayerVisible(int aLayer) const
Return information about visibility of a particular layer.
Definition: view.h:405
static TOOL_ACTION closeOutline
Definition: pcb_actions.h:164
void Update()
Update the dimension's cached text and geometry.