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