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