You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1892 lines
61 KiB

12 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
9 years ago
  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-2019 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. #include "drawing_tool.h"
  26. #include "pcb_actions.h"
  27. #include <pcb_edit_frame.h>
  28. #include <class_draw_panel_gal.h>
  29. #include <project.h>
  30. #include <id.h>
  31. #include <pcbnew_id.h>
  32. #include <confirm.h>
  33. #include <import_gfx/dialog_import_gfx.h>
  34. #include <view/view_group.h>
  35. #include <view/view_controls.h>
  36. #include <view/view.h>
  37. #include <gal/graphics_abstraction_layer.h>
  38. #include <tool/tool_manager.h>
  39. #include <geometry/geometry_utils.h>
  40. #include <ratsnest_data.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>
  46. #include "grid_helper.h"
  47. #include <dialogs/dialog_text_properties.h>
  48. #include <preview_items/arc_assistant.h>
  49. #include <class_board.h>
  50. #include <class_edge_mod.h>
  51. #include <class_pcb_text.h>
  52. #include <class_dimension.h>
  53. #include <class_zone.h>
  54. #include <class_module.h>
  55. #include <tools/selection_tool.h>
  56. #include <tools/tool_event_utils.h>
  57. #include <tools/zone_create_helper.h>
  58. using SCOPED_DRAW_MODE = SCOPED_SET_RESET<DRAWING_TOOL::MODE>;
  59. // Drawing tool actions
  60. TOOL_ACTION PCB_ACTIONS::drawLine( "pcbnew.InteractiveDrawing.line",
  61. AS_GLOBAL,
  62. MD_SHIFT + MD_CTRL + 'L', LEGACY_HK_NAME( "Draw Line" ),
  63. _( "Draw Line" ), _( "Draw a line" ),
  64. add_graphical_segments_xpm, AF_ACTIVATE );
  65. TOOL_ACTION PCB_ACTIONS::drawPolygon( "pcbnew.InteractiveDrawing.graphicPolygon",
  66. AS_GLOBAL,
  67. MD_SHIFT + MD_CTRL + 'P', LEGACY_HK_NAME( "Draw Graphic Polygon" ),
  68. _( "Draw Graphic Polygon" ), _( "Draw a graphic polygon" ),
  69. add_graphical_polygon_xpm, AF_ACTIVATE );
  70. TOOL_ACTION PCB_ACTIONS::drawCircle( "pcbnew.InteractiveDrawing.circle",
  71. AS_GLOBAL,
  72. MD_SHIFT + MD_CTRL + 'C', LEGACY_HK_NAME( "Draw Circle" ),
  73. _( "Draw Circle" ), _( "Draw a circle" ),
  74. add_circle_xpm, AF_ACTIVATE );
  75. TOOL_ACTION PCB_ACTIONS::drawArc( "pcbnew.InteractiveDrawing.arc",
  76. AS_GLOBAL,
  77. MD_SHIFT + MD_CTRL + 'A', LEGACY_HK_NAME( "Draw Arc" ),
  78. _( "Draw Arc" ), _( "Draw an arc" ),
  79. add_arc_xpm, AF_ACTIVATE );
  80. TOOL_ACTION PCB_ACTIONS::placeText( "pcbnew.InteractiveDrawing.text",
  81. AS_GLOBAL,
  82. MD_SHIFT + MD_CTRL + 'T', LEGACY_HK_NAME( "Add Text" ),
  83. _( "Add Text" ), _( "Add a text item" ),
  84. text_xpm, AF_ACTIVATE );
  85. TOOL_ACTION PCB_ACTIONS::drawDimension( "pcbnew.InteractiveDrawing.dimension",
  86. AS_GLOBAL,
  87. MD_SHIFT + MD_CTRL + 'H', LEGACY_HK_NAME( "Add Dimension" ),
  88. _( "Add Dimension" ), _( "Add a dimension" ),
  89. add_dimension_xpm, AF_ACTIVATE );
  90. TOOL_ACTION PCB_ACTIONS::drawZone( "pcbnew.InteractiveDrawing.zone",
  91. AS_GLOBAL,
  92. #ifdef __WXOSX_MAC__
  93. MD_ALT + 'Z',
  94. #else
  95. MD_SHIFT + MD_CTRL + 'Z',
  96. #endif
  97. LEGACY_HK_NAME( "Add Filled Zone" ),
  98. _( "Add Filled Zone" ), _( "Add a filled zone" ),
  99. add_zone_xpm, AF_ACTIVATE );
  100. TOOL_ACTION PCB_ACTIONS::drawVia( "pcbnew.InteractiveDrawing.via",
  101. AS_GLOBAL,
  102. MD_SHIFT + MD_CTRL + 'V', LEGACY_HK_NAME( "Add Vias" ),
  103. _( "Add Vias" ), _( "Add free-standing vias" ),
  104. add_via_xpm, AF_ACTIVATE );
  105. TOOL_ACTION PCB_ACTIONS::drawZoneKeepout( "pcbnew.InteractiveDrawing.keepout",
  106. AS_GLOBAL,
  107. MD_SHIFT + MD_CTRL + 'K', LEGACY_HK_NAME( "Add Keepout Area" ),
  108. _( "Add Keepout Area" ), _( "Add a keepout area" ),
  109. add_keepout_area_xpm, AF_ACTIVATE );
  110. TOOL_ACTION PCB_ACTIONS::drawZoneCutout( "pcbnew.InteractiveDrawing.zoneCutout",
  111. AS_GLOBAL,
  112. MD_SHIFT + 'C', LEGACY_HK_NAME( "Add a Zone Cutout" ),
  113. _( "Add a Zone Cutout" ), _( "Add a cutout area of an existing zone" ),
  114. add_zone_cutout_xpm, AF_ACTIVATE );
  115. TOOL_ACTION PCB_ACTIONS::drawSimilarZone( "pcbnew.InteractiveDrawing.similarZone",
  116. AS_GLOBAL,
  117. MD_SHIFT + MD_CTRL + '.', LEGACY_HK_NAME( "Add a Similar Zone" ),
  118. _( "Add a Similar Zone" ), _( "Add a zone with the same settings as an existing zone" ),
  119. add_zone_xpm, AF_ACTIVATE );
  120. TOOL_ACTION PCB_ACTIONS::placeImportedGraphics( "pcbnew.InteractiveDrawing.placeImportedGraphics",
  121. AS_GLOBAL,
  122. MD_SHIFT + MD_CTRL + 'F', LEGACY_HK_NAME( "Place DXF" ),
  123. _( "Place Imported Graphics" ), "",
  124. import_vector_xpm, AF_ACTIVATE );
  125. TOOL_ACTION PCB_ACTIONS::setAnchor( "pcbnew.InteractiveDrawing.setAnchor",
  126. AS_GLOBAL,
  127. MD_SHIFT + MD_CTRL + 'N', LEGACY_HK_NAME( "Place the Footprint Anchor" ),
  128. _( "Place the Footprint Anchor" ), "",
  129. anchor_xpm, AF_ACTIVATE );
  130. TOOL_ACTION PCB_ACTIONS::incWidth( "pcbnew.InteractiveDrawing.incWidth",
  131. AS_CONTEXT,
  132. MD_CTRL + '+', LEGACY_HK_NAME( "Increase Line Width" ),
  133. _( "Increase Line Width" ), _( "Increase the line width" ) );
  134. TOOL_ACTION PCB_ACTIONS::decWidth( "pcbnew.InteractiveDrawing.decWidth",
  135. AS_CONTEXT,
  136. MD_CTRL + '-', LEGACY_HK_NAME( "Decrease Line Width" ),
  137. _( "Decrease Line Width" ), _( "Decrease the line width" ) );
  138. TOOL_ACTION PCB_ACTIONS::arcPosture( "pcbnew.InteractiveDrawing.arcPosture",
  139. AS_CONTEXT,
  140. '/', LEGACY_HK_NAME( "Switch Track Posture" ),
  141. _( "Switch Arc Posture" ), _( "Switch the arc posture" ) );
  142. /*
  143. * Contextual actions
  144. */
  145. static TOOL_ACTION deleteLastPoint( "pcbnew.InteractiveDrawing.deleteLastPoint",
  146. AS_CONTEXT,
  147. WXK_BACK, "",
  148. _( "Delete Last Point" ), _( "Delete the last point added to the current item" ),
  149. undo_xpm );
  150. static TOOL_ACTION closeZoneOutline( "pcbnew.InteractiveDrawing.closeZoneOutline",
  151. AS_CONTEXT, 0, "",
  152. _( "Close Zone Outline" ), _( "Close the outline of a zone in progress" ),
  153. checked_ok_xpm );
  154. DRAWING_TOOL::DRAWING_TOOL() :
  155. PCB_TOOL_BASE( "pcbnew.InteractiveDrawing" ),
  156. m_view( nullptr ), m_controls( nullptr ),
  157. m_board( nullptr ), m_frame( nullptr ), m_mode( MODE::NONE ),
  158. m_lineWidth( 1 )
  159. {
  160. }
  161. DRAWING_TOOL::~DRAWING_TOOL()
  162. {
  163. }
  164. bool DRAWING_TOOL::Init()
  165. {
  166. auto activeToolFunctor = [ this ] ( const SELECTION& aSel ) {
  167. return m_mode != MODE::NONE;
  168. };
  169. // some interactive drawing tools can undo the last point
  170. auto canUndoPoint = [ this ] ( const SELECTION& aSel ) {
  171. return m_mode == MODE::ARC || m_mode == MODE::ZONE;
  172. };
  173. // functor for zone-only actions
  174. auto zoneActiveFunctor = [this ] ( const SELECTION& aSel ) {
  175. return m_mode == MODE::ZONE;
  176. };
  177. auto& ctxMenu = m_menu.GetMenu();
  178. // cancel current tool goes in main context menu at the top if present
  179. ctxMenu.AddItem( ACTIONS::cancelInteractive, activeToolFunctor, 1 );
  180. ctxMenu.AddSeparator( 1 );
  181. // tool-specific actions
  182. ctxMenu.AddItem( closeZoneOutline, zoneActiveFunctor, 200 );
  183. ctxMenu.AddItem( deleteLastPoint, canUndoPoint, 200 );
  184. ctxMenu.AddSeparator( 500 );
  185. // Type-specific sub-menus will be added for us by other tools
  186. // For example, zone fill/unfill is provided by the PCB control tool
  187. // Finally, add the standard zoom/grid items
  188. getEditFrame<PCB_BASE_FRAME>()->AddStandardSubMenus( m_menu );
  189. return true;
  190. }
  191. void DRAWING_TOOL::Reset( RESET_REASON aReason )
  192. {
  193. // Init variables used by every drawing tool
  194. m_view = getView();
  195. m_controls = getViewControls();
  196. m_board = getModel<BOARD>();
  197. m_frame = getEditFrame<PCB_BASE_EDIT_FRAME>();
  198. }
  199. DRAWING_TOOL::MODE DRAWING_TOOL::GetDrawingMode() const
  200. {
  201. return m_mode;
  202. }
  203. int DRAWING_TOOL::DrawLine( const TOOL_EVENT& aEvent )
  204. {
  205. if( m_editModules && !m_frame->GetModel() )
  206. return 0;
  207. BOARD_ITEM_CONTAINER* parent = m_frame->GetModel();
  208. DRAWSEGMENT* line = m_editModules ? new EDGE_MODULE( (MODULE*) parent ) : new DRAWSEGMENT;
  209. line->SetFlags( IS_NEW );
  210. auto startingPoint = boost::make_optional<VECTOR2D>( false, VECTOR2D( 0, 0 ) );
  211. BOARD_COMMIT commit( m_frame );
  212. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::LINE );
  213. m_frame->SetToolID( m_editModules ? ID_MODEDIT_LINE_TOOL : ID_PCB_ADD_LINE_BUTT,
  214. wxCURSOR_PENCIL, _( "Add graphic line" ) );
  215. while( drawSegment( S_SEGMENT, line, startingPoint ) )
  216. {
  217. // This can be reset by some actions (e.g. Save Board), so ensure it stays set.
  218. m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
  219. if( line )
  220. {
  221. if( m_editModules )
  222. static_cast<EDGE_MODULE*>( line )->SetLocalCoord();
  223. commit.Add( line );
  224. commit.Push( _( "Draw a line segment" ) );
  225. startingPoint = VECTOR2D( line->GetEnd() );
  226. }
  227. else
  228. {
  229. startingPoint = NULLOPT;
  230. }
  231. line = m_editModules ? new EDGE_MODULE( (MODULE*) parent ) : new DRAWSEGMENT;
  232. line->SetFlags( IS_NEW );
  233. }
  234. m_frame->SetNoToolSelected();
  235. return 0;
  236. }
  237. int DRAWING_TOOL::DrawCircle( const TOOL_EVENT& aEvent )
  238. {
  239. if( m_editModules && !m_frame->GetModel() )
  240. return 0;
  241. BOARD_ITEM_CONTAINER* parent = m_frame->GetModel();
  242. DRAWSEGMENT* circle = m_editModules ? new EDGE_MODULE( (MODULE*) parent ) : new DRAWSEGMENT;
  243. BOARD_COMMIT commit( m_frame );
  244. circle->SetFlags( IS_NEW );
  245. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::CIRCLE );
  246. m_frame->SetToolID( m_editModules ? ID_MODEDIT_CIRCLE_TOOL : ID_PCB_CIRCLE_BUTT,
  247. wxCURSOR_PENCIL, _( "Add graphic circle" ) );
  248. while( drawSegment( S_CIRCLE, circle ) )
  249. {
  250. // This can be reset by some actions (e.g. Save Board), so ensure it stays set.
  251. m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
  252. if( circle )
  253. {
  254. if( m_editModules )
  255. static_cast<EDGE_MODULE*>( circle )->SetLocalCoord();
  256. commit.Add( circle );
  257. commit.Push( _( "Draw a circle" ) );
  258. }
  259. circle = m_editModules ? new EDGE_MODULE( (MODULE*) parent ) : new DRAWSEGMENT;
  260. circle->SetFlags( IS_NEW );
  261. }
  262. m_frame->SetNoToolSelected();
  263. return 0;
  264. }
  265. int DRAWING_TOOL::DrawArc( const TOOL_EVENT& aEvent )
  266. {
  267. if( m_editModules && !m_frame->GetModel() )
  268. return 0;
  269. BOARD_ITEM_CONTAINER* parent = m_frame->GetModel();
  270. DRAWSEGMENT* arc = m_editModules ? new EDGE_MODULE( (MODULE*) parent ) : new DRAWSEGMENT;
  271. BOARD_COMMIT commit( m_frame );
  272. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ARC );
  273. arc->SetFlags( IS_NEW );
  274. m_frame->SetToolID( m_editModules ? ID_MODEDIT_ARC_TOOL : ID_PCB_ARC_BUTT,
  275. wxCURSOR_PENCIL, _( "Add graphic arc" ) );
  276. while( drawArc( arc ) )
  277. {
  278. // This can be reset by some actions (e.g. Save Board), so ensure it stays set.
  279. m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
  280. if( arc )
  281. {
  282. if( m_editModules )
  283. static_cast<EDGE_MODULE*>( arc )->SetLocalCoord();
  284. commit.Add( arc );
  285. commit.Push( _( "Draw an arc" ) );
  286. }
  287. arc = m_editModules ? new EDGE_MODULE( (MODULE*) parent ) : new DRAWSEGMENT;
  288. arc->SetFlags( IS_NEW );
  289. }
  290. m_frame->SetNoToolSelected();
  291. return 0;
  292. }
  293. int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
  294. {
  295. if( m_editModules && !m_frame->GetModel() )
  296. return 0;
  297. BOARD_ITEM* text = NULL;
  298. const BOARD_DESIGN_SETTINGS& dsnSettings = m_frame->GetDesignSettings();
  299. BOARD_COMMIT commit( m_frame );
  300. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  301. m_controls->ShowCursor( true );
  302. m_controls->SetSnapping( true );
  303. // do not capture or auto-pan until we start placing some text
  304. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TEXT );
  305. Activate();
  306. m_frame->SetToolID( m_editModules ? ID_MODEDIT_TEXT_TOOL : ID_PCB_ADD_TEXT_BUTT,
  307. wxCURSOR_PENCIL, _( "Add text" ) );
  308. bool reselect = false;
  309. // Main loop: keep receiving events
  310. while( OPT_TOOL_EVENT evt = Wait() )
  311. {
  312. // This can be reset by some actions (e.g. Save Board), so ensure it stays set.
  313. m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
  314. VECTOR2I cursorPos = m_controls->GetCursorPosition();
  315. if( reselect && text )
  316. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, text );
  317. if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  318. {
  319. if( text )
  320. {
  321. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  322. // Delete the old text and have another try
  323. delete text;
  324. text = NULL;
  325. m_controls->SetAutoPan( false );
  326. m_controls->CaptureCursor( false );
  327. m_controls->ShowCursor( true );
  328. }
  329. else
  330. break;
  331. if( evt->IsActivate() ) // now finish unconditionally
  332. break;
  333. }
  334. else if( evt->IsClick( BUT_RIGHT ) )
  335. {
  336. m_menu.ShowContextMenu( selection() );
  337. }
  338. else if( evt->IsClick( BUT_LEFT ) )
  339. {
  340. bool placing = text != nullptr;
  341. if( !text )
  342. {
  343. m_controls->ForceCursorPosition( true, m_controls->GetCursorPosition() );
  344. PCB_LAYER_ID layer = m_frame->GetActiveLayer();
  345. // Init the new item attributes
  346. if( m_editModules )
  347. {
  348. TEXTE_MODULE* textMod = new TEXTE_MODULE( (MODULE*) m_frame->GetModel() );
  349. textMod->SetLayer( layer );
  350. textMod->SetTextSize( dsnSettings.GetTextSize( layer ) );
  351. textMod->SetThickness( dsnSettings.GetTextThickness( layer ) );
  352. textMod->SetItalic( dsnSettings.GetTextItalic( layer ) );
  353. textMod->SetKeepUpright( dsnSettings.GetTextUpright( layer ) );
  354. textMod->SetTextPos( (wxPoint) cursorPos );
  355. text = textMod;
  356. DIALOG_TEXT_PROPERTIES textDialog( m_frame, textMod );
  357. bool cancelled;
  358. RunMainStack([&]() {
  359. cancelled = !textDialog.ShowModal() || textMod->GetText().IsEmpty();
  360. } );
  361. if( cancelled )
  362. {
  363. delete text;
  364. text = nullptr;
  365. }
  366. else if( textMod->GetTextPos() != (wxPoint) cursorPos )
  367. {
  368. // If the user modified the location then go ahead and place it there.
  369. // Otherwise we'll drag.
  370. placing = true;
  371. }
  372. }
  373. else
  374. {
  375. TEXTE_PCB* textPcb = new TEXTE_PCB( m_frame->GetModel() );
  376. // TODO we have to set IS_NEW, otherwise InstallTextPCB.. creates an undo entry :| LEGACY_CLEANUP
  377. textPcb->SetFlags( IS_NEW );
  378. textPcb->SetLayer( layer );
  379. // Set the mirrored option for layers on the BACK side of the board
  380. if( IsBackLayer( layer ) )
  381. textPcb->SetMirrored( true );
  382. textPcb->SetTextSize( dsnSettings.GetTextSize( layer ) );
  383. textPcb->SetThickness( dsnSettings.GetTextThickness( layer ) );
  384. textPcb->SetItalic( dsnSettings.GetTextItalic( layer ) );
  385. textPcb->SetTextPos( (wxPoint) cursorPos );
  386. RunMainStack([&]() {
  387. m_frame->InstallTextOptionsFrame( textPcb );
  388. } );
  389. if( textPcb->GetText().IsEmpty() )
  390. {
  391. m_controls->ForceCursorPosition( false );
  392. delete textPcb;
  393. }
  394. else
  395. text = textPcb;
  396. }
  397. if( text == NULL )
  398. continue;
  399. m_controls->WarpCursor( text->GetPosition(), true );
  400. m_controls->ForceCursorPosition( false );
  401. m_controls->CaptureCursor( true );
  402. m_controls->SetAutoPan( true );
  403. m_toolMgr->RunAction( PCB_ACTIONS::selectItem, true, text );
  404. }
  405. if( placing )
  406. {
  407. text->ClearFlags();
  408. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  409. commit.Add( text );
  410. commit.Push( _( "Place a text" ) );
  411. m_controls->CaptureCursor( false );
  412. m_controls->SetAutoPan( false );
  413. m_controls->ShowCursor( true );
  414. text = NULL;
  415. }
  416. }
  417. else if( text && evt->IsMotion() )
  418. {
  419. text->SetPosition( (wxPoint) cursorPos );
  420. selection().SetReferencePoint( cursorPos );
  421. m_view->Update( &selection() );
  422. frame()->SetMsgPanel( text );
  423. }
  424. else if( text && evt->IsAction( &PCB_ACTIONS::properties ) )
  425. {
  426. // Calling 'Properties' action clears the selection, so we need to restore it
  427. reselect = true;
  428. }
  429. }
  430. frame()->SetMsgPanel( board() );
  431. frame()->SetNoToolSelected();
  432. return 0;
  433. }
  434. void DRAWING_TOOL::constrainDimension( DIMENSION* dimension )
  435. {
  436. const VECTOR2I lineVector{ dimension->GetEnd() - dimension->GetOrigin() };
  437. dimension->SetEnd( wxPoint(
  438. VECTOR2I( dimension->GetOrigin() ) + GetVectorSnapped45( lineVector ) ) );
  439. }
  440. int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
  441. {
  442. if( m_editModules && !m_frame->GetModel() )
  443. return 0;
  444. DIMENSION* dimension = NULL;
  445. BOARD_COMMIT commit( m_frame );
  446. GRID_HELPER grid( m_frame );
  447. // Add a VIEW_GROUP that serves as a preview for the new item
  448. PCBNEW_SELECTION preview;
  449. m_view->Add( &preview );
  450. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  451. m_controls->ShowCursor( true );
  452. m_controls->SetSnapping( true );
  453. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DIMENSION );
  454. Activate();
  455. m_frame->SetToolID( ID_PCB_DIMENSION_BUTT, wxCURSOR_PENCIL, _( "Add dimension" ) );
  456. enum DIMENSION_STEPS
  457. {
  458. SET_ORIGIN = 0,
  459. SET_END,
  460. SET_HEIGHT,
  461. FINISHED
  462. };
  463. int step = SET_ORIGIN;
  464. // Main loop: keep receiving events
  465. while( OPT_TOOL_EVENT evt = Wait() )
  466. {
  467. // This can be reset by some actions (e.g. Save Board), so ensure it stays set.
  468. m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
  469. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  470. grid.SetUseGrid( !evt->Modifier( MD_ALT ) );
  471. m_controls->SetSnapping( !evt->Modifier( MD_ALT ) );
  472. VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), nullptr );
  473. m_controls->ForceCursorPosition( true, cursorPos );
  474. if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  475. {
  476. m_controls->SetAutoPan( false );
  477. if( step != SET_ORIGIN ) // start from the beginning
  478. {
  479. preview.Clear();
  480. delete dimension;
  481. step = SET_ORIGIN;
  482. }
  483. else
  484. break;
  485. if( evt->IsActivate() ) // now finish unconditionally
  486. break;
  487. }
  488. else if( evt->IsAction( &PCB_ACTIONS::incWidth ) && step != SET_ORIGIN )
  489. {
  490. m_lineWidth += WIDTH_STEP;
  491. dimension->SetWidth( m_lineWidth );
  492. m_view->Update( &preview );
  493. frame()->SetMsgPanel( dimension );
  494. }
  495. else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && step != SET_ORIGIN )
  496. {
  497. if( m_lineWidth > WIDTH_STEP )
  498. {
  499. m_lineWidth -= WIDTH_STEP;
  500. dimension->SetWidth( m_lineWidth );
  501. m_view->Update( &preview );
  502. frame()->SetMsgPanel( dimension );
  503. }
  504. }
  505. else if( evt->IsClick( BUT_RIGHT ) )
  506. {
  507. m_menu.ShowContextMenu( selection() );
  508. }
  509. else if( evt->IsClick( BUT_LEFT ) )
  510. {
  511. switch( step )
  512. {
  513. case SET_ORIGIN:
  514. {
  515. PCB_LAYER_ID layer = getDrawingLayer();
  516. const BOARD_DESIGN_SETTINGS& boardSettings = m_board->GetDesignSettings();
  517. if( layer == Edge_Cuts ) // dimensions are not allowed on EdgeCuts
  518. layer = Dwgs_User;
  519. // Init the new item attributes
  520. dimension = new DIMENSION( m_board );
  521. dimension->SetLayer( layer );
  522. dimension->SetOrigin( (wxPoint) cursorPos );
  523. dimension->SetEnd( (wxPoint) cursorPos );
  524. dimension->Text().SetTextSize( boardSettings.GetTextSize( layer ) );
  525. dimension->Text().SetThickness( boardSettings.GetTextThickness( layer ) );
  526. dimension->Text().SetItalic( boardSettings.GetTextItalic( layer ) );
  527. dimension->SetWidth( boardSettings.GetLineThickness( layer ) );
  528. dimension->SetUnits( m_frame->GetUserUnits(), false );
  529. dimension->AdjustDimensionDetails();
  530. preview.Add( dimension );
  531. frame()->SetMsgPanel( dimension );
  532. m_controls->SetAutoPan( true );
  533. m_controls->CaptureCursor( true );
  534. }
  535. break;
  536. case SET_END:
  537. dimension->SetEnd( (wxPoint) cursorPos );
  538. if( !!evt->Modifier( MD_CTRL ) )
  539. constrainDimension( dimension );
  540. // Dimensions that have origin and end in the same spot are not valid
  541. if( dimension->GetOrigin() == dimension->GetEnd() )
  542. --step;
  543. break;
  544. case SET_HEIGHT:
  545. {
  546. if( (wxPoint) cursorPos != dimension->GetPosition() )
  547. {
  548. assert( dimension->GetOrigin() != dimension->GetEnd() );
  549. assert( dimension->GetWidth() > 0 );
  550. preview.Remove( dimension );
  551. commit.Add( dimension );
  552. commit.Push( _( "Draw a dimension" ) );
  553. }
  554. }
  555. break;
  556. }
  557. if( ++step == FINISHED )
  558. {
  559. step = SET_ORIGIN;
  560. m_controls->SetAutoPan( false );
  561. m_controls->CaptureCursor( false );
  562. }
  563. }
  564. else if( evt->IsMotion() )
  565. {
  566. switch( step )
  567. {
  568. case SET_END:
  569. dimension->SetEnd( (wxPoint) cursorPos );
  570. if( !!evt->Modifier( MD_CTRL ) )
  571. constrainDimension( dimension );
  572. break;
  573. case SET_HEIGHT:
  574. {
  575. // Calculating the direction of travel perpendicular to the selected axis
  576. double angle = dimension->GetAngle() + ( M_PI / 2 );
  577. wxPoint delta( (wxPoint) cursorPos - dimension->m_featureLineDO );
  578. double height = ( delta.x * cos( angle ) ) + ( delta.y * sin( angle ) );
  579. dimension->SetHeight( height );
  580. }
  581. break;
  582. }
  583. // Show a preview of the item
  584. m_view->Update( &preview );
  585. if( step )
  586. frame()->SetMsgPanel( dimension );
  587. else
  588. frame()->SetMsgPanel( board() );
  589. }
  590. }
  591. if( step != SET_ORIGIN )
  592. delete dimension;
  593. m_controls->SetAutoPan( false );
  594. m_controls->ForceCursorPosition( false );
  595. m_view->Remove( &preview );
  596. frame()->SetMsgPanel( board() );
  597. m_frame->SetNoToolSelected();
  598. return 0;
  599. }
  600. int DRAWING_TOOL::DrawZone( const TOOL_EVENT& aEvent )
  601. {
  602. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ZONE );
  603. m_frame->SetToolID( ID_PCB_ZONES_BUTT, wxCURSOR_PENCIL, _( "Add zones" ) );
  604. return drawZone( false, ZONE_MODE::ADD );
  605. }
  606. int DRAWING_TOOL::DrawZoneKeepout( const TOOL_EVENT& aEvent )
  607. {
  608. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::KEEPOUT );
  609. m_frame->SetToolID( ID_PCB_KEEPOUT_BUTT, wxCURSOR_PENCIL, _( "Add keepout" ) );
  610. return drawZone( true, ZONE_MODE::ADD );
  611. }
  612. int DRAWING_TOOL::DrawZoneCutout( const TOOL_EVENT& aEvent )
  613. {
  614. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ZONE );
  615. m_frame->SetToolID( ID_PCB_ZONES_BUTT, wxCURSOR_PENCIL, _( "Add zone cutout" ) );
  616. return drawZone( false, ZONE_MODE::CUTOUT );
  617. }
  618. int DRAWING_TOOL::DrawGraphicPolygon( const TOOL_EVENT& aEvent )
  619. {
  620. if( m_editModules && !m_frame->GetModel() )
  621. return 0;
  622. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::GRAPHIC_POLYGON );
  623. m_frame->SetToolID( m_editModules ? ID_MODEDIT_POLYGON_TOOL : ID_PCB_ADD_POLYGON_BUTT,
  624. wxCURSOR_PENCIL, _( "Add graphic polygon" ) );
  625. return drawZone( false, ZONE_MODE::GRAPHIC_POLYGON );
  626. }
  627. int DRAWING_TOOL::DrawSimilarZone( const TOOL_EVENT& aEvent )
  628. {
  629. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ZONE );
  630. m_frame->SetToolID( ID_PCB_ZONES_BUTT, wxCURSOR_PENCIL, _( "Add similar zone" ) );
  631. return drawZone( false, ZONE_MODE::SIMILAR );
  632. }
  633. int DRAWING_TOOL::PlaceImportedGraphics( const TOOL_EVENT& aEvent )
  634. {
  635. if( !m_frame->GetModel() )
  636. return 0;
  637. // Note: PlaceImportedGraphics() will convert PCB_LINE_T and PCB_TEXT_T to module graphic
  638. // items if needed
  639. DIALOG_IMPORT_GFX dlg( m_frame, m_editModules );
  640. int dlgResult = dlg.ShowModal();
  641. auto& list = dlg.GetImportedItems();
  642. if( dlgResult != wxID_OK )
  643. return 0;
  644. // Ensure the list is not empty:
  645. if( list.empty() )
  646. {
  647. wxMessageBox( _( "No graphic items found in file to import") );
  648. return 0;
  649. }
  650. m_frame->SetNoToolSelected();
  651. // Add a VIEW_GROUP that serves as a preview for the new item
  652. PCBNEW_SELECTION preview;
  653. BOARD_COMMIT commit( m_frame );
  654. // Build the undo list & add items to the current view
  655. for( auto& ptr : list)
  656. {
  657. EDA_ITEM* item = ptr.get();
  658. if( m_editModules )
  659. wxASSERT( item->Type() == PCB_MODULE_EDGE_T || item->Type() == PCB_MODULE_TEXT_T );
  660. else
  661. wxASSERT( item->Type() == PCB_LINE_T || item->Type() == PCB_TEXT_T );
  662. if( dlg.IsPlacementInteractive() )
  663. preview.Add( item );
  664. else
  665. commit.Add( item );
  666. ptr.release();
  667. }
  668. if( !dlg.IsPlacementInteractive() )
  669. {
  670. commit.Push( _( "Place a DXF_SVG drawing" ) );
  671. return 0;
  672. }
  673. BOARD_ITEM* firstItem = static_cast<BOARD_ITEM*>( preview.Front() );
  674. m_view->Add( &preview );
  675. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  676. m_controls->ShowCursor( true );
  677. m_controls->SetSnapping( true );
  678. m_controls->ForceCursorPosition( false );
  679. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::DXF );
  680. // Now move the new items to the current cursor position:
  681. VECTOR2I cursorPos = m_controls->GetCursorPosition();
  682. VECTOR2I delta = cursorPos - firstItem->GetPosition();
  683. for( EDA_ITEM* item : preview )
  684. static_cast<BOARD_ITEM*>( item )->Move( (wxPoint) delta );
  685. m_view->Update( &preview );
  686. Activate();
  687. // Main loop: keep receiving events
  688. while( OPT_TOOL_EVENT evt = Wait() )
  689. {
  690. cursorPos = m_controls->GetCursorPosition();
  691. if( evt->IsMotion() )
  692. {
  693. delta = cursorPos - firstItem->GetPosition();
  694. for( auto item : preview )
  695. static_cast<BOARD_ITEM*>( item )->Move( (wxPoint) delta );
  696. m_view->Update( &preview );
  697. }
  698. else if( evt->Category() == TC_COMMAND )
  699. {
  700. // TODO it should be handled by EDIT_TOOL, so add items and select?
  701. if( TOOL_EVT_UTILS::IsRotateToolEvt( *evt ) )
  702. {
  703. const auto rotationPoint = (wxPoint) cursorPos;
  704. const auto rotationAngle = TOOL_EVT_UTILS::GetEventRotationAngle( *m_frame, *evt );
  705. for( auto item : preview )
  706. static_cast<BOARD_ITEM*>( item )->Rotate( rotationPoint, rotationAngle );
  707. m_view->Update( &preview );
  708. }
  709. else if( evt->IsAction( &PCB_ACTIONS::flip ) )
  710. {
  711. for( auto item : preview )
  712. static_cast<BOARD_ITEM*>( item )->Flip( (wxPoint) cursorPos );
  713. m_view->Update( &preview );
  714. }
  715. else if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  716. {
  717. preview.FreeItems();
  718. break;
  719. }
  720. }
  721. else if( evt->IsClick( BUT_RIGHT ) )
  722. {
  723. m_menu.ShowContextMenu( selection() );
  724. }
  725. else if( evt->IsClick( BUT_LEFT ) )
  726. {
  727. // Place the imported drawings
  728. for( auto item : preview )
  729. commit.Add( item );
  730. commit.Push( _( "Place a DXF_SVG drawing" ) );
  731. break;
  732. }
  733. }
  734. preview.Clear();
  735. m_view->Remove( &preview );
  736. return 0;
  737. }
  738. int DRAWING_TOOL::SetAnchor( const TOOL_EVENT& aEvent )
  739. {
  740. assert( m_editModules );
  741. if( !m_frame->GetModel() )
  742. return 0;
  743. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::ANCHOR );
  744. Activate();
  745. m_frame->SetToolID( ID_MODEDIT_ANCHOR_TOOL, wxCURSOR_PENCIL, _( "Place the footprint anchor" ) );
  746. m_controls->ShowCursor( true );
  747. m_controls->SetSnapping( true );
  748. m_controls->SetAutoPan( true );
  749. m_controls->CaptureCursor( false );
  750. while( OPT_TOOL_EVENT evt = Wait() )
  751. {
  752. // This can be reset by some actions (e.g. Save Board), so ensure it stays set.
  753. m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
  754. if( evt->IsClick( BUT_LEFT ) )
  755. {
  756. MODULE* module = (MODULE*) m_frame->GetModel();
  757. BOARD_COMMIT commit( m_frame );
  758. commit.Modify( module );
  759. // set the new relative internal local coordinates of footprint items
  760. VECTOR2I cursorPos = m_controls->GetCursorPosition();
  761. wxPoint moveVector = module->GetPosition() - (wxPoint) cursorPos;
  762. module->MoveAnchorPosition( moveVector );
  763. commit.Push( _( "Move the footprint reference anchor" ) );
  764. // Usually, we do not need to change twice the anchor position,
  765. // so deselect the active tool
  766. break;
  767. }
  768. else if( evt->IsClick( BUT_RIGHT ) )
  769. {
  770. m_menu.ShowContextMenu( selection() );
  771. }
  772. else if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  773. break;
  774. }
  775. m_frame->SetNoToolSelected();
  776. return 0;
  777. }
  778. bool DRAWING_TOOL::drawSegment( int aShape, DRAWSEGMENT*& aGraphic, OPT<VECTOR2D> aStartingPoint )
  779. {
  780. // Only two shapes are currently supported
  781. assert( aShape == S_SEGMENT || aShape == S_CIRCLE );
  782. GRID_HELPER grid( m_frame );
  783. m_lineWidth = getSegmentWidth( getDrawingLayer() );
  784. m_frame->SetActiveLayer( getDrawingLayer() );
  785. // Add a VIEW_GROUP that serves as a preview for the new item
  786. PCBNEW_SELECTION preview;
  787. m_view->Add( &preview );
  788. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  789. m_controls->ShowCursor( true );
  790. Activate();
  791. bool direction45 = false; // 45 degrees only mode
  792. bool started = false;
  793. bool isLocalOriginSet = ( m_frame->GetScreen()->m_LocalOrigin != VECTOR2D( 0, 0 ) );
  794. VECTOR2I cursorPos = m_controls->GetMousePosition();
  795. if( aStartingPoint )
  796. {
  797. // Init the new item attributes
  798. aGraphic->SetShape( (STROKE_T) aShape );
  799. aGraphic->SetWidth( m_lineWidth );
  800. aGraphic->SetLayer( getDrawingLayer() );
  801. aGraphic->SetStart( (wxPoint) aStartingPoint.get() );
  802. cursorPos = grid.BestSnapAnchor( cursorPos, aGraphic );
  803. m_controls->ForceCursorPosition( true, cursorPos );
  804. aGraphic->SetEnd( wxPoint( cursorPos.x, cursorPos.y ) );
  805. preview.Add( aGraphic );
  806. m_controls->SetAutoPan( true );
  807. m_controls->CaptureCursor( true );
  808. if( !isLocalOriginSet )
  809. m_frame->GetScreen()->m_LocalOrigin = aStartingPoint.get();
  810. started = true;
  811. }
  812. // Main loop: keep receiving events
  813. while( OPT_TOOL_EVENT evt = Wait() )
  814. {
  815. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  816. grid.SetUseGrid( !evt->Modifier( MD_ALT ) );
  817. m_controls->SetSnapping( !evt->Modifier( MD_ALT ) );
  818. cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), getDrawingLayer() );
  819. m_controls->ForceCursorPosition( true, cursorPos );
  820. // 45 degree angle constraint enabled with an option and toggled with Ctrl
  821. const bool limit45 = ( frame()->Settings().g_Use45DegreeGraphicSegments != !!( evt->Modifier( MD_CTRL ) ) );
  822. if( direction45 != limit45 && started && aShape == S_SEGMENT )
  823. {
  824. direction45 = limit45;
  825. if( direction45 )
  826. {
  827. const VECTOR2I lineVector( cursorPos - VECTOR2I( aGraphic->GetStart() ) );
  828. // get a restricted 45/H/V line from the last fixed point to the cursor
  829. auto newEnd = GetVectorSnapped45( lineVector );
  830. aGraphic->SetEnd( aGraphic->GetStart() + (wxPoint) newEnd );
  831. m_controls->ForceCursorPosition( true, VECTOR2I( aGraphic->GetEnd() ) );
  832. }
  833. else
  834. {
  835. aGraphic->SetEnd( (wxPoint) cursorPos );
  836. }
  837. m_view->Update( &preview );
  838. frame()->SetMsgPanel( aGraphic );
  839. }
  840. if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  841. {
  842. preview.Clear();
  843. m_view->Update( &preview );
  844. delete aGraphic;
  845. aGraphic = nullptr;
  846. if( !isLocalOriginSet )
  847. m_frame->GetScreen()->m_LocalOrigin = VECTOR2D( 0, 0 );
  848. break;
  849. }
  850. else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
  851. {
  852. m_lineWidth = getSegmentWidth( getDrawingLayer() );
  853. aGraphic->SetLayer( getDrawingLayer() );
  854. aGraphic->SetWidth( m_lineWidth );
  855. m_view->Update( &preview );
  856. frame()->SetMsgPanel( aGraphic );
  857. }
  858. else if( evt->IsClick( BUT_RIGHT ) )
  859. {
  860. m_menu.ShowContextMenu( selection() );
  861. }
  862. else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
  863. {
  864. if( !started )
  865. {
  866. m_lineWidth = getSegmentWidth( getDrawingLayer() );
  867. // Init the new item attributes
  868. aGraphic->SetShape( (STROKE_T) aShape );
  869. aGraphic->SetWidth( m_lineWidth );
  870. aGraphic->SetStart( (wxPoint) cursorPos );
  871. aGraphic->SetEnd( (wxPoint) cursorPos );
  872. aGraphic->SetLayer( getDrawingLayer() );
  873. if( !isLocalOriginSet )
  874. m_frame->GetScreen()->m_LocalOrigin = cursorPos;
  875. preview.Add( aGraphic );
  876. frame()->SetMsgPanel( aGraphic );
  877. m_controls->SetAutoPan( true );
  878. m_controls->CaptureCursor( true );
  879. started = true;
  880. }
  881. else
  882. {
  883. auto snapItem = dyn_cast<DRAWSEGMENT*>( grid.GetSnapped() );
  884. auto mod = dyn_cast<MODULE*>( m_frame->GetModel() );
  885. if( aGraphic->GetEnd() == aGraphic->GetStart()
  886. || ( evt->IsDblClick( BUT_LEFT ) && aShape == S_SEGMENT )
  887. || snapItem )
  888. // User has clicked twice in the same spot
  889. // or clicked on the end of an existing segment (closing a path)
  890. {
  891. BOARD_COMMIT commit( m_frame );
  892. // If the user clicks on an existing snap point from a drawsegment
  893. // we finish the segment as they are likely closing a path
  894. if( snapItem && aGraphic->GetLength() > 0.0 )
  895. {
  896. DRAWSEGMENT* l = m_editModules ? new EDGE_MODULE( mod ) : new DRAWSEGMENT;
  897. l->SetFlags( IS_NEW );
  898. *l = *aGraphic;
  899. commit.Add( l );
  900. }
  901. if( !commit.Empty() )
  902. commit.Push( _( "Draw a line" ) );
  903. delete aGraphic;
  904. aGraphic = nullptr;
  905. }
  906. preview.Clear();
  907. break;
  908. }
  909. }
  910. else if( evt->IsMotion() )
  911. {
  912. // 45 degree lines
  913. if( direction45 && aShape == S_SEGMENT )
  914. {
  915. const VECTOR2I lineVector( cursorPos - VECTOR2I( aGraphic->GetStart() ) );
  916. // get a restricted 45/H/V line from the last fixed point to the cursor
  917. auto newEnd = GetVectorSnapped45( lineVector );
  918. aGraphic->SetEnd( aGraphic->GetStart() + (wxPoint) newEnd );
  919. m_controls->ForceCursorPosition( true, VECTOR2I( aGraphic->GetEnd() ) );
  920. }
  921. else
  922. aGraphic->SetEnd( (wxPoint) cursorPos );
  923. m_view->Update( &preview );
  924. if( started )
  925. frame()->SetMsgPanel( aGraphic );
  926. else
  927. frame()->SetMsgPanel( board() );
  928. }
  929. else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
  930. {
  931. m_lineWidth += WIDTH_STEP;
  932. aGraphic->SetWidth( m_lineWidth );
  933. m_view->Update( &preview );
  934. frame()->SetMsgPanel( aGraphic );
  935. }
  936. else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && ( m_lineWidth > WIDTH_STEP ) )
  937. {
  938. m_lineWidth -= WIDTH_STEP;
  939. aGraphic->SetWidth( m_lineWidth );
  940. m_view->Update( &preview );
  941. frame()->SetMsgPanel( aGraphic );
  942. }
  943. else if( evt->IsAction( &ACTIONS::resetLocalCoords ) )
  944. {
  945. isLocalOriginSet = true;
  946. }
  947. }
  948. if( !isLocalOriginSet ) // reset the relative coordinte if it was not set before
  949. m_frame->GetScreen()->m_LocalOrigin = VECTOR2D( 0, 0 );
  950. m_view->Remove( &preview );
  951. frame()->SetMsgPanel( board() );
  952. m_controls->SetAutoPan( false );
  953. m_controls->CaptureCursor( false );
  954. m_controls->ForceCursorPosition( false );
  955. return started;
  956. }
  957. /**
  958. * Update an arc DRAWSEGMENT from the current state
  959. * of an Arc Geometry Manager
  960. */
  961. static void updateArcFromConstructionMgr( const KIGFX::PREVIEW::ARC_GEOM_MANAGER& aMgr,
  962. DRAWSEGMENT& aArc )
  963. {
  964. auto vec = aMgr.GetOrigin();
  965. aArc.SetCenter( { vec.x, vec.y } );
  966. vec = aMgr.GetStartRadiusEnd();
  967. aArc.SetArcStart( { vec.x, vec.y } );
  968. aArc.SetAngle( RAD2DECIDEG( -aMgr.GetSubtended() ) );
  969. }
  970. bool DRAWING_TOOL::drawArc( DRAWSEGMENT*& aGraphic )
  971. {
  972. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  973. m_lineWidth = getSegmentWidth( getDrawingLayer() );
  974. // Arc geometric construction manager
  975. KIGFX::PREVIEW::ARC_GEOM_MANAGER arcManager;
  976. // Arc drawing assistant overlay
  977. KIGFX::PREVIEW::ARC_ASSISTANT arcAsst( arcManager, m_frame->GetUserUnits() );
  978. // Add a VIEW_GROUP that serves as a preview for the new item
  979. PCBNEW_SELECTION preview;
  980. m_view->Add( &preview );
  981. m_view->Add( &arcAsst );
  982. GRID_HELPER grid( m_frame );
  983. m_controls->ShowCursor( true );
  984. m_controls->SetSnapping( true );
  985. Activate();
  986. bool firstPoint = false;
  987. // Main loop: keep receiving events
  988. while( OPT_TOOL_EVENT evt = Wait() )
  989. {
  990. PCB_LAYER_ID layer = getDrawingLayer();
  991. aGraphic->SetLayer( layer );
  992. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  993. grid.SetUseGrid( !evt->Modifier( MD_ALT ) );
  994. m_controls->SetSnapping( !evt->Modifier( MD_ALT ) );
  995. VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), aGraphic );
  996. m_controls->ForceCursorPosition( true, cursorPos );
  997. if( evt->IsClick( BUT_LEFT ) )
  998. {
  999. if( !firstPoint )
  1000. {
  1001. m_controls->SetAutoPan( true );
  1002. m_controls->CaptureCursor( true );
  1003. m_lineWidth = getSegmentWidth( getDrawingLayer() );
  1004. // Init the new item attributes
  1005. // (non-geometric, those are handled by the manager)
  1006. aGraphic->SetShape( S_ARC );
  1007. aGraphic->SetWidth( m_lineWidth );
  1008. preview.Add( aGraphic );
  1009. firstPoint = true;
  1010. }
  1011. arcManager.AddPoint( cursorPos, true );
  1012. }
  1013. else if( evt->IsAction( &deleteLastPoint ) )
  1014. {
  1015. arcManager.RemoveLastPoint();
  1016. }
  1017. else if( evt->IsMotion() )
  1018. {
  1019. // set angle snap
  1020. arcManager.SetAngleSnap( evt->Modifier( MD_CTRL ) );
  1021. // update, but don't step the manager state
  1022. arcManager.AddPoint( cursorPos, false );
  1023. }
  1024. else if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  1025. {
  1026. preview.Clear();
  1027. delete aGraphic;
  1028. aGraphic = nullptr;
  1029. break;
  1030. }
  1031. else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
  1032. {
  1033. m_lineWidth = getSegmentWidth( getDrawingLayer() );
  1034. aGraphic->SetLayer( getDrawingLayer() );
  1035. aGraphic->SetWidth( m_lineWidth );
  1036. m_view->Update( &preview );
  1037. frame()->SetMsgPanel( aGraphic );
  1038. }
  1039. else if( evt->IsClick( BUT_RIGHT ) )
  1040. {
  1041. m_menu.ShowContextMenu( selection() );
  1042. }
  1043. else if( evt->IsAction( &PCB_ACTIONS::incWidth ) )
  1044. {
  1045. m_lineWidth += WIDTH_STEP;
  1046. aGraphic->SetWidth( m_lineWidth );
  1047. m_view->Update( &preview );
  1048. frame()->SetMsgPanel( aGraphic );
  1049. }
  1050. else if( evt->IsAction( &PCB_ACTIONS::decWidth ) && m_lineWidth > WIDTH_STEP )
  1051. {
  1052. m_lineWidth -= WIDTH_STEP;
  1053. aGraphic->SetWidth( m_lineWidth );
  1054. m_view->Update( &preview );
  1055. frame()->SetMsgPanel( aGraphic );
  1056. }
  1057. else if( evt->IsAction( &PCB_ACTIONS::arcPosture ) )
  1058. {
  1059. arcManager.ToggleClockwise();
  1060. }
  1061. if( arcManager.IsComplete() )
  1062. {
  1063. break;
  1064. }
  1065. else if( arcManager.HasGeometryChanged() )
  1066. {
  1067. updateArcFromConstructionMgr( arcManager, *aGraphic );
  1068. m_view->Update( &preview );
  1069. m_view->Update( &arcAsst );
  1070. if(firstPoint)
  1071. frame()->SetMsgPanel( aGraphic );
  1072. else
  1073. frame()->SetMsgPanel( board() );
  1074. }
  1075. }
  1076. preview.Remove( aGraphic );
  1077. m_view->Remove( &arcAsst );
  1078. m_view->Remove( &preview );
  1079. frame()->SetMsgPanel( board() );
  1080. m_controls->SetAutoPan( false );
  1081. m_controls->CaptureCursor( false );
  1082. m_controls->ForceCursorPosition( false );
  1083. return !arcManager.IsReset();
  1084. }
  1085. bool DRAWING_TOOL::getSourceZoneForAction( ZONE_MODE aMode, ZONE_CONTAINER*& aZone )
  1086. {
  1087. bool clearSelection = false;
  1088. aZone = nullptr;
  1089. // not an action that needs a source zone
  1090. if( aMode == ZONE_MODE::ADD || aMode == ZONE_MODE::GRAPHIC_POLYGON )
  1091. return true;
  1092. SELECTION_TOOL* selTool = m_toolMgr->GetTool<SELECTION_TOOL>();
  1093. const PCBNEW_SELECTION& selection = selTool->GetSelection();
  1094. if( selection.Empty() )
  1095. {
  1096. clearSelection = true;
  1097. m_toolMgr->RunAction( PCB_ACTIONS::selectionCursor, true );
  1098. }
  1099. // we want a single zone
  1100. if( selection.Size() == 1 )
  1101. aZone = dyn_cast<ZONE_CONTAINER*>( selection[0] );
  1102. // expected a zone, but didn't get one
  1103. if( !aZone )
  1104. {
  1105. if( clearSelection )
  1106. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  1107. return false;
  1108. }
  1109. return true;
  1110. }
  1111. int DRAWING_TOOL::drawZone( bool aKeepout, ZONE_MODE aMode )
  1112. {
  1113. // get a source zone, if we need one. We need it for:
  1114. // ZONE_MODE::CUTOUT (adding a hole to the source zone)
  1115. // ZONE_MODE::SIMILAR (creating a new zone using settings of source zone
  1116. ZONE_CONTAINER* sourceZone = nullptr;
  1117. if( !getSourceZoneForAction( aMode, sourceZone ) )
  1118. {
  1119. m_frame->SetNoToolSelected();
  1120. return 0;
  1121. }
  1122. ZONE_CREATE_HELPER::PARAMS params;
  1123. params.m_keepout = aKeepout;
  1124. params.m_mode = aMode;
  1125. params.m_sourceZone = sourceZone;
  1126. if( aMode == ZONE_MODE::GRAPHIC_POLYGON )
  1127. params.m_layer = getDrawingLayer();
  1128. else if( aMode == ZONE_MODE::SIMILAR )
  1129. params.m_layer = sourceZone->GetLayer();
  1130. else
  1131. params.m_layer = m_frame->GetActiveLayer();
  1132. ZONE_CREATE_HELPER zoneTool( *this, params );
  1133. // the geometry manager which handles the zone geometry, and
  1134. // hands the calculated points over to the zone creator tool
  1135. POLYGON_GEOM_MANAGER polyGeomMgr( zoneTool );
  1136. Activate(); // register for events
  1137. m_controls->ShowCursor( true );
  1138. m_controls->SetSnapping( true );
  1139. bool started = false;
  1140. GRID_HELPER grid( m_frame );
  1141. STATUS_TEXT_POPUP status( m_frame );
  1142. status.SetTextColor( wxColour( 255, 0, 0 ) );
  1143. status.SetText( _( "Self-intersecting polygons are not allowed" ) );
  1144. while( OPT_TOOL_EVENT evt = Wait() )
  1145. {
  1146. // This can be reset by some actions (e.g. Save Board), so ensure it stays set.
  1147. m_frame->GetCanvas()->SetCurrentCursor( wxCURSOR_PENCIL );
  1148. LSET layers( m_frame->GetActiveLayer() );
  1149. grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
  1150. grid.SetUseGrid( !evt->Modifier( MD_ALT ) );
  1151. m_controls->SetSnapping( !evt->Modifier( MD_ALT ) );
  1152. VECTOR2I cursorPos = grid.BestSnapAnchor( m_controls->GetMousePosition(), layers );
  1153. m_controls->ForceCursorPosition( true, cursorPos );
  1154. if( TOOL_EVT_UTILS::IsCancelInteractive( *evt ) )
  1155. {
  1156. // pre-empted by another tool, give up
  1157. // cancelled without an inprogress polygon, give up
  1158. if( !polyGeomMgr.IsPolygonInProgress() || evt->IsActivate() )
  1159. {
  1160. break;
  1161. }
  1162. polyGeomMgr.Reset();
  1163. // start again
  1164. started = false;
  1165. m_controls->SetAutoPan( false );
  1166. m_controls->CaptureCursor( false );
  1167. }
  1168. else if( evt->IsAction( &PCB_ACTIONS::layerChanged ) )
  1169. {
  1170. if( aMode == ZONE_MODE::GRAPHIC_POLYGON )
  1171. params.m_layer = getDrawingLayer();
  1172. else if( aMode == ZONE_MODE::ADD || aMode == ZONE_MODE::CUTOUT )
  1173. params.m_layer = frame()->GetActiveLayer();
  1174. }
  1175. else if( evt->IsClick( BUT_RIGHT ) )
  1176. {
  1177. m_menu.ShowContextMenu( selection() );
  1178. }
  1179. // events that lock in nodes
  1180. else if( evt->IsClick( BUT_LEFT )
  1181. || evt->IsDblClick( BUT_LEFT )
  1182. || evt->IsAction( &closeZoneOutline ) )
  1183. {
  1184. // Check if it is double click / closing line (so we have to finish the zone)
  1185. const bool endPolygon = evt->IsDblClick( BUT_LEFT )
  1186. || evt->IsAction( &closeZoneOutline )
  1187. || polyGeomMgr.NewPointClosesOutline( cursorPos );
  1188. if( endPolygon )
  1189. {
  1190. polyGeomMgr.SetFinished();
  1191. polyGeomMgr.Reset();
  1192. started = false;
  1193. m_controls->SetAutoPan( false );
  1194. m_controls->CaptureCursor( false );
  1195. }
  1196. // adding a corner
  1197. else if( polyGeomMgr.AddPoint( cursorPos ) )
  1198. {
  1199. if( !started )
  1200. {
  1201. started = true;
  1202. m_controls->SetAutoPan( true );
  1203. m_controls->CaptureCursor( true );
  1204. }
  1205. }
  1206. }
  1207. else if( evt->IsAction( &deleteLastPoint ) )
  1208. {
  1209. polyGeomMgr.DeleteLastCorner();
  1210. if( !polyGeomMgr.IsPolygonInProgress() )
  1211. {
  1212. // report finished as an empty shape
  1213. polyGeomMgr.SetFinished();
  1214. // start again
  1215. started = false;
  1216. m_controls->SetAutoPan( false );
  1217. m_controls->CaptureCursor( false );
  1218. }
  1219. }
  1220. else if( polyGeomMgr.IsPolygonInProgress()
  1221. && ( evt->IsMotion() || evt->IsDrag( BUT_LEFT ) ) )
  1222. {
  1223. polyGeomMgr.SetCursorPosition( cursorPos, evt->Modifier( MD_CTRL )
  1224. ? POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45
  1225. : POLYGON_GEOM_MANAGER::LEADER_MODE::DIRECT );
  1226. if( polyGeomMgr.IsSelfIntersecting( true ) )
  1227. {
  1228. wxPoint p = wxGetMousePosition() + wxPoint( 20, 20 );
  1229. status.Move( p );
  1230. status.PopupFor( 1500 );
  1231. }
  1232. else
  1233. {
  1234. status.Hide();
  1235. }
  1236. }
  1237. } // end while
  1238. m_toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  1239. m_frame->SetNoToolSelected();
  1240. m_controls->ForceCursorPosition( false );
  1241. return 0;
  1242. }
  1243. int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
  1244. {
  1245. struct VIA_PLACER : public INTERACTIVE_PLACER_BASE
  1246. {
  1247. GRID_HELPER m_gridHelper;
  1248. VIA_PLACER( PCB_BASE_EDIT_FRAME* aFrame ) : m_gridHelper( aFrame )
  1249. {}
  1250. TRACK* findTrack( VIA* aVia )
  1251. {
  1252. const LSET lset = aVia->GetLayerSet();
  1253. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
  1254. BOX2I bbox = aVia->GetBoundingBox();
  1255. auto view = m_frame->GetCanvas()->GetView();
  1256. std::vector<TRACK*> possible_tracks;
  1257. view->Query( bbox, items );
  1258. for( auto it : items )
  1259. {
  1260. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
  1261. if( !(item->GetLayerSet() & lset ).any() )
  1262. continue;
  1263. if( auto track = dyn_cast<TRACK*>( item ) )
  1264. {
  1265. if( TestSegmentHit( aVia->GetPosition(), track->GetStart(), track->GetEnd(),
  1266. ( track->GetWidth() + aVia->GetWidth() ) / 2 ) )
  1267. possible_tracks.push_back( track );
  1268. }
  1269. }
  1270. TRACK* return_track = nullptr;
  1271. int min_d = std::numeric_limits<int>::max();
  1272. for( auto track : possible_tracks )
  1273. {
  1274. SEG test( track->GetStart(), track->GetEnd() );
  1275. auto dist = ( test.NearestPoint( aVia->GetPosition() ) -
  1276. VECTOR2I( aVia->GetPosition() ) ).EuclideanNorm();
  1277. if( dist < min_d )
  1278. {
  1279. min_d = dist;
  1280. return_track = track;
  1281. }
  1282. }
  1283. return return_track;
  1284. }
  1285. bool hasDRCViolation( VIA* aVia )
  1286. {
  1287. const LSET lset = aVia->GetLayerSet();
  1288. std::vector<KIGFX::VIEW::LAYER_ITEM_PAIR> items;
  1289. int net = 0;
  1290. int clearance = 0;
  1291. BOX2I bbox = aVia->GetBoundingBox();
  1292. auto view = m_frame->GetCanvas()->GetView();
  1293. view->Query( bbox, items );
  1294. for( auto it : items )
  1295. {
  1296. BOARD_ITEM* item = static_cast<BOARD_ITEM*>( it.first );
  1297. if( !(item->GetLayerSet() & lset ).any() )
  1298. continue;
  1299. if( auto track = dyn_cast<TRACK*>( item ) )
  1300. {
  1301. int max_clearance = std::max( clearance, track->GetClearance() );
  1302. if( TestSegmentHit( aVia->GetPosition(), track->GetStart(), track->GetEnd(),
  1303. ( track->GetWidth() + aVia->GetWidth() ) / 2 + max_clearance ) )
  1304. {
  1305. if( net && track->GetNetCode() != net )
  1306. return true;
  1307. net = track->GetNetCode();
  1308. clearance = track->GetClearance();
  1309. }
  1310. }
  1311. if( auto mod = dyn_cast<MODULE*>( item ) )
  1312. {
  1313. for( auto pad : mod->Pads() )
  1314. {
  1315. int max_clearance = std::max( clearance, pad->GetClearance() );
  1316. if( pad->HitTest( aVia->GetBoundingBox(), false, max_clearance ) )
  1317. {
  1318. if( net && pad->GetNetCode() != net )
  1319. return true;
  1320. net = pad->GetNetCode();
  1321. clearance = pad->GetClearance();
  1322. }
  1323. }
  1324. }
  1325. }
  1326. return false;
  1327. }
  1328. int findStitchedZoneNet( VIA* aVia )
  1329. {
  1330. const auto pos = aVia->GetPosition();
  1331. const auto lset = aVia->GetLayerSet();
  1332. for( auto mod : m_board->Modules() )
  1333. {
  1334. for( D_PAD* pad : mod->Pads() )
  1335. {
  1336. if( pad->HitTest( pos ) && ( pad->GetLayerSet() & lset ).any() )
  1337. return -1;
  1338. }
  1339. }
  1340. std::vector<ZONE_CONTAINER*> foundZones;
  1341. for( auto zone : m_board->Zones() )
  1342. {
  1343. if( zone->HitTestFilledArea( pos ) )
  1344. {
  1345. foundZones.push_back( zone );
  1346. }
  1347. }
  1348. std::sort( foundZones.begin(), foundZones.end(),
  1349. [] ( const ZONE_CONTAINER* a, const ZONE_CONTAINER* b ) {
  1350. return a->GetLayer() < b->GetLayer();
  1351. } );
  1352. // first take the net of the active layer
  1353. for( auto z : foundZones )
  1354. {
  1355. if( m_frame->GetActiveLayer() == z->GetLayer() )
  1356. return z->GetNetCode();
  1357. }
  1358. // none? take the topmost visible layer
  1359. for( auto z : foundZones )
  1360. {
  1361. if( m_board->IsLayerVisible( z->GetLayer() ) )
  1362. return z->GetNetCode();
  1363. }
  1364. return -1;
  1365. }
  1366. void SnapItem( BOARD_ITEM *aItem ) override
  1367. {
  1368. // If you place a Via on a track but not on its centerline, the current
  1369. // connectivity algorithm will require us to put a kink in the track when
  1370. // we break it (so that each of the two segments ends on the via center).
  1371. // That's not ideal, and is in fact probably worse than forcing snap in
  1372. // this situation.
  1373. m_gridHelper.SetSnap( !( m_modifiers & MD_SHIFT ) );
  1374. auto via = static_cast<VIA*>( aItem );
  1375. wxPoint pos = via->GetPosition();
  1376. TRACK* track = findTrack( via );
  1377. if( track )
  1378. {
  1379. SEG trackSeg( track->GetStart(), track->GetEnd() );
  1380. VECTOR2I snap = m_gridHelper.AlignToSegment( pos, trackSeg );
  1381. aItem->SetPosition( (wxPoint) snap );
  1382. }
  1383. }
  1384. bool PlaceItem( BOARD_ITEM* aItem, BOARD_COMMIT& aCommit ) override
  1385. {
  1386. auto via = static_cast<VIA*>( aItem );
  1387. int newNet;
  1388. TRACK* track = findTrack( via );
  1389. if( hasDRCViolation( via ) )
  1390. return false;
  1391. if( track )
  1392. {
  1393. aCommit.Modify( track );
  1394. TRACK* newTrack = dynamic_cast<TRACK*>( track->Clone() );
  1395. track->SetEnd( via->GetPosition() );
  1396. newTrack->SetStart( via->GetPosition() );
  1397. aCommit.Add( newTrack );
  1398. newNet = track->GetNetCode();
  1399. }
  1400. else
  1401. newNet = findStitchedZoneNet( via );
  1402. if( newNet > 0 )
  1403. via->SetNetCode( newNet );
  1404. aCommit.Add( aItem );
  1405. return true;
  1406. }
  1407. std::unique_ptr<BOARD_ITEM> CreateItem() override
  1408. {
  1409. auto& ds = m_board->GetDesignSettings();
  1410. VIA* via = new VIA( m_board );
  1411. via->SetNetCode( 0 );
  1412. via->SetViaType( ds.m_CurrentViaType );
  1413. // for microvias, the size and hole will be changed later.
  1414. via->SetWidth( ds.GetCurrentViaSize() );
  1415. via->SetDrill( ds.GetCurrentViaDrill() );
  1416. // Usual via is from copper to component.
  1417. // layer pair is B_Cu and F_Cu.
  1418. via->SetLayerPair( B_Cu, F_Cu );
  1419. PCB_LAYER_ID first_layer = m_frame->GetActiveLayer();
  1420. PCB_LAYER_ID last_layer;
  1421. // prepare switch to new active layer:
  1422. if( first_layer != m_frame->GetScreen()->m_Route_Layer_TOP )
  1423. last_layer = m_frame->GetScreen()->m_Route_Layer_TOP;
  1424. else
  1425. last_layer = m_frame->GetScreen()->m_Route_Layer_BOTTOM;
  1426. // Adjust the actual via layer pair
  1427. switch( via->GetViaType() )
  1428. {
  1429. case VIA_BLIND_BURIED:
  1430. via->SetLayerPair( first_layer, last_layer );
  1431. break;
  1432. case VIA_MICROVIA: // from external to the near neighbor inner layer
  1433. {
  1434. PCB_LAYER_ID last_inner_layer =
  1435. ToLAYER_ID( ( m_board->GetCopperLayerCount() - 2 ) );
  1436. if( first_layer == B_Cu )
  1437. last_layer = last_inner_layer;
  1438. else if( first_layer == F_Cu )
  1439. last_layer = In1_Cu;
  1440. else if( first_layer == last_inner_layer )
  1441. last_layer = B_Cu;
  1442. else if( first_layer == In1_Cu )
  1443. last_layer = F_Cu;
  1444. // else error: will be removed later
  1445. via->SetLayerPair( first_layer, last_layer );
  1446. // Update diameter and hole size, which where set previously
  1447. // for normal vias
  1448. NETINFO_ITEM* net = via->GetNet();
  1449. if( net )
  1450. {
  1451. via->SetWidth( net->GetMicroViaSize() );
  1452. via->SetDrill( net->GetMicroViaDrillSize() );
  1453. }
  1454. }
  1455. break;
  1456. default:
  1457. break;
  1458. }
  1459. return std::unique_ptr<BOARD_ITEM>( via );
  1460. }
  1461. };
  1462. VIA_PLACER placer( frame() );
  1463. SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::VIA );
  1464. frame()->SetToolID( ID_PCB_DRAW_VIA_BUTT, wxCURSOR_PENCIL, _( "Add vias" ) );
  1465. doInteractiveItemPlacement( &placer, _( "Place via" ),
  1466. IPO_REPEAT | IPO_SINGLE_CLICK | IPO_ROTATE | IPO_FLIP );
  1467. frame()->SetToolID( ID_NO_TOOL_SELECTED, wxCURSOR_DEFAULT, wxEmptyString );
  1468. return 0;
  1469. }
  1470. void DRAWING_TOOL::setTransitions()
  1471. {
  1472. Go( &DRAWING_TOOL::DrawLine, PCB_ACTIONS::drawLine.MakeEvent() );
  1473. Go( &DRAWING_TOOL::DrawGraphicPolygon, PCB_ACTIONS::drawPolygon.MakeEvent() );
  1474. Go( &DRAWING_TOOL::DrawCircle, PCB_ACTIONS::drawCircle.MakeEvent() );
  1475. Go( &DRAWING_TOOL::DrawArc, PCB_ACTIONS::drawArc.MakeEvent() );
  1476. Go( &DRAWING_TOOL::DrawDimension, PCB_ACTIONS::drawDimension.MakeEvent() );
  1477. Go( &DRAWING_TOOL::DrawZone, PCB_ACTIONS::drawZone.MakeEvent() );
  1478. Go( &DRAWING_TOOL::DrawZoneKeepout, PCB_ACTIONS::drawZoneKeepout.MakeEvent() );
  1479. Go( &DRAWING_TOOL::DrawZoneCutout, PCB_ACTIONS::drawZoneCutout.MakeEvent() );
  1480. Go( &DRAWING_TOOL::DrawSimilarZone, PCB_ACTIONS::drawSimilarZone.MakeEvent() );
  1481. Go( &DRAWING_TOOL::DrawVia, PCB_ACTIONS::drawVia.MakeEvent() );
  1482. Go( &DRAWING_TOOL::PlaceText, PCB_ACTIONS::placeText.MakeEvent() );
  1483. Go( &DRAWING_TOOL::PlaceImportedGraphics, PCB_ACTIONS::placeImportedGraphics.MakeEvent() );
  1484. Go( &DRAWING_TOOL::SetAnchor, PCB_ACTIONS::setAnchor.MakeEvent() );
  1485. }
  1486. int DRAWING_TOOL::getSegmentWidth( PCB_LAYER_ID aLayer ) const
  1487. {
  1488. assert( m_board );
  1489. return m_board->GetDesignSettings().GetLineThickness( aLayer );
  1490. }
  1491. PCB_LAYER_ID DRAWING_TOOL::getDrawingLayer() const
  1492. {
  1493. PCB_LAYER_ID layer = m_frame->GetActiveLayer();
  1494. if( ( GetDrawingMode() == MODE::DIMENSION || GetDrawingMode() == MODE::GRAPHIC_POLYGON )
  1495. && IsCopperLayer( layer ) )
  1496. {
  1497. if( layer == F_Cu )
  1498. layer = F_SilkS;
  1499. else if( layer == B_Cu )
  1500. layer = B_SilkS;
  1501. else
  1502. layer = Dwgs_User;
  1503. m_frame->SetActiveLayer( layer );
  1504. }
  1505. return layer;
  1506. }
  1507. const unsigned int DRAWING_TOOL::WIDTH_STEP = Millimeter2iu( 0.1 );