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.

350 lines
12 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2017-2019 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include <tools/zone_create_helper.h>
  24. #include <tool/tool_manager.h>
  25. #include <class_zone.h>
  26. #include <pcb_shape.h>
  27. #include <fp_shape.h>
  28. #include <board_commit.h>
  29. #include <pcb_painter.h>
  30. #include <tools/pcb_actions.h>
  31. #include <tools/selection_tool.h>
  32. #include <zone_filler.h>
  33. ZONE_CREATE_HELPER::ZONE_CREATE_HELPER( DRAWING_TOOL& aTool, PARAMS& aParams ):
  34. m_tool( aTool ),
  35. m_params( aParams ),
  36. m_parentView( *aTool.getView() )
  37. {
  38. m_parentView.Add( &m_previewItem );
  39. }
  40. ZONE_CREATE_HELPER::~ZONE_CREATE_HELPER()
  41. {
  42. // remove the preview from the view
  43. m_parentView.SetVisible( &m_previewItem, false );
  44. m_parentView.Remove( &m_previewItem );
  45. }
  46. std::unique_ptr<ZONE_CONTAINER> ZONE_CREATE_HELPER::createNewZone( bool aKeepout )
  47. {
  48. PCB_BASE_EDIT_FRAME* frame = m_tool.getEditFrame<PCB_BASE_EDIT_FRAME>();
  49. BOARD* board = frame->GetBoard();
  50. BOARD_ITEM_CONTAINER* parent = m_tool.m_frame->GetModel();
  51. KIGFX::VIEW_CONTROLS* controls = m_tool.GetManager()->GetViewControls();
  52. std::set<int> highlightedNets = board->GetHighLightNetCodes();
  53. // Get the current default settings for zones
  54. ZONE_SETTINGS zoneInfo = frame->GetZoneSettings();
  55. zoneInfo.m_Layers.reset().set( m_params.m_layer ); // TODO(JE) multilayer defaults?
  56. zoneInfo.m_NetcodeSelection = highlightedNets.empty() ? -1 : *highlightedNets.begin();
  57. zoneInfo.SetIsRuleArea( m_params.m_keepout );
  58. zoneInfo.m_Zone_45_Only = ( m_params.m_leaderMode == POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45 );
  59. // If we don't have a net from highlighing, maybe we can get one from the selection
  60. SELECTION_TOOL* selectionTool = m_tool.GetManager()->GetTool<SELECTION_TOOL>();
  61. if( selectionTool && !selectionTool->GetSelection().Empty()
  62. && zoneInfo.m_NetcodeSelection == -1 )
  63. {
  64. EDA_ITEM* item = *selectionTool->GetSelection().GetItems().begin();
  65. if( BOARD_CONNECTED_ITEM* bci = dynamic_cast<BOARD_CONNECTED_ITEM*>( item ) )
  66. zoneInfo.m_NetcodeSelection = bci->GetNetCode();
  67. }
  68. if( m_params.m_mode != ZONE_MODE::GRAPHIC_POLYGON )
  69. {
  70. // Get the current default settings for zones
  71. // Show options dialog
  72. int dialogResult;
  73. if( m_params.m_keepout )
  74. dialogResult = InvokeRuleAreaEditor( frame, &zoneInfo );
  75. else
  76. {
  77. // TODO(JE) combine these dialogs?
  78. if( ( zoneInfo.m_Layers & LSET::AllCuMask() ).any() )
  79. dialogResult = InvokeCopperZonesEditor( frame, &zoneInfo );
  80. else
  81. dialogResult = InvokeNonCopperZonesEditor( frame, &zoneInfo );
  82. }
  83. if( dialogResult == wxID_CANCEL )
  84. return nullptr;
  85. controls->WarpCursor( controls->GetCursorPosition(), true );
  86. }
  87. // The new zone is a ZONE_CONTAINER if created in the board editor
  88. // and a MODULE_ZONE_CONTAINER if created in the footprint editor
  89. wxASSERT( !m_tool.m_editModules || ( parent->Type() == PCB_MODULE_T ) );
  90. std::unique_ptr<ZONE_CONTAINER> newZone = m_tool.m_editModules ?
  91. std::make_unique<MODULE_ZONE_CONTAINER>( parent ) :
  92. std::make_unique<ZONE_CONTAINER>( parent );
  93. // Apply the selected settings
  94. zoneInfo.ExportSetting( *newZone );
  95. return newZone;
  96. }
  97. std::unique_ptr<ZONE_CONTAINER> ZONE_CREATE_HELPER::createZoneFromExisting(
  98. const ZONE_CONTAINER& aSrcZone )
  99. {
  100. auto& board = *m_tool.getModel<BOARD>();
  101. auto newZone = std::make_unique<ZONE_CONTAINER>( &board );
  102. ZONE_SETTINGS zoneSettings;
  103. zoneSettings << aSrcZone;
  104. zoneSettings.ExportSetting( *newZone );
  105. return newZone;
  106. }
  107. void ZONE_CREATE_HELPER::performZoneCutout( ZONE_CONTAINER& aZone, ZONE_CONTAINER& aCutout )
  108. {
  109. BOARD_COMMIT commit( &m_tool );
  110. BOARD* board = m_tool.getModel<BOARD>();
  111. std::vector<ZONE_CONTAINER*> newZones;
  112. // Clear the selection before removing the old zone
  113. auto toolMgr = m_tool.GetManager();
  114. toolMgr->RunAction( PCB_ACTIONS::selectionClear, true );
  115. SHAPE_POLY_SET originalOutline( *aZone.Outline() );
  116. originalOutline.BooleanSubtract( *aCutout.Outline(), SHAPE_POLY_SET::PM_FAST );
  117. // After substracting the hole, originalOutline can have more than one
  118. // main outline.
  119. // But a zone can have only one main outline, so create as many zones as
  120. // originalOutline contains main outlines:
  121. for( int outline = 0; outline < originalOutline.OutlineCount(); outline++ )
  122. {
  123. auto newZoneOutline = new SHAPE_POLY_SET;
  124. newZoneOutline->AddOutline( originalOutline.Outline( outline ) );
  125. // Add holes (if any) to thez new zone outline:
  126. for (int hole = 0; hole < originalOutline.HoleCount( outline ) ; hole++ )
  127. newZoneOutline->AddHole( originalOutline.CHole( outline, hole ) );
  128. auto newZone = new ZONE_CONTAINER( aZone );
  129. newZone->SetOutline( newZoneOutline );
  130. newZone->SetLocalFlags( 1 );
  131. newZone->HatchBorder();
  132. newZones.push_back( newZone );
  133. commit.Add( newZone );
  134. }
  135. commit.Remove( &aZone );
  136. ZONE_FILLER filler( board, &commit );
  137. if( !filler.Fill( newZones ) )
  138. {
  139. commit.Revert();
  140. return;
  141. }
  142. commit.Push( _( "Add a zone cutout" ) );
  143. // Select the new zone and set it as the source for the next cutout
  144. if( newZones.empty() )
  145. {
  146. m_params.m_sourceZone = nullptr;
  147. }
  148. else
  149. {
  150. m_params.m_sourceZone = newZones[0];
  151. toolMgr->RunAction( PCB_ACTIONS::selectItem, true, newZones[0] );
  152. }
  153. }
  154. void ZONE_CREATE_HELPER::commitZone( std::unique_ptr<ZONE_CONTAINER> aZone )
  155. {
  156. switch ( m_params.m_mode )
  157. {
  158. case ZONE_MODE::CUTOUT:
  159. // For cutouts, subtract from the source
  160. performZoneCutout( *m_params.m_sourceZone, *aZone );
  161. break;
  162. case ZONE_MODE::ADD:
  163. case ZONE_MODE::SIMILAR:
  164. {
  165. BOARD_COMMIT bCommit( &m_tool );
  166. aZone->HatchBorder();
  167. bCommit.Add( aZone.get() );
  168. if( !m_params.m_keepout )
  169. {
  170. ZONE_FILLER filler( m_tool.getModel<BOARD>(), &bCommit );
  171. std::vector<ZONE_CONTAINER*> toFill = { aZone.get() };
  172. if( !filler.Fill( toFill ) )
  173. {
  174. bCommit.Revert();
  175. break;
  176. }
  177. }
  178. bCommit.Push( _( "Add a zone" ) );
  179. m_tool.GetManager()->RunAction( PCB_ACTIONS::selectItem, true, aZone.release() );
  180. break;
  181. }
  182. case ZONE_MODE::GRAPHIC_POLYGON:
  183. {
  184. BOARD_COMMIT bCommit( &m_tool );
  185. BOARD_ITEM_CONTAINER* parent = m_tool.m_frame->GetModel();
  186. LSET graphicPolygonsLayers = LSET::AllLayersMask();
  187. graphicPolygonsLayers.reset( Edge_Cuts ).reset( F_CrtYd ).reset( B_CrtYd );
  188. if( graphicPolygonsLayers.Contains( m_params.m_layer ) )
  189. {
  190. auto poly = m_tool.m_editModules ? new FP_SHAPE((MODULE *) parent )
  191. : new PCB_SHAPE();
  192. poly->SetShape ( S_POLYGON );
  193. poly->SetLayer( m_params.m_layer );
  194. poly->SetPolyShape ( *aZone->Outline() );
  195. bCommit.Add( poly );
  196. m_tool.GetManager()->RunAction( PCB_ACTIONS::selectItem, true, poly );
  197. }
  198. else
  199. {
  200. SHAPE_POLY_SET* outline = aZone->Outline();
  201. for( auto seg = outline->CIterateSegments( 0 ); seg; seg++ )
  202. {
  203. PCB_SHAPE* new_seg = m_tool.m_editModules ? new FP_SHAPE( (MODULE *) parent )
  204. : new PCB_SHAPE();
  205. new_seg->SetShape( S_SEGMENT );
  206. new_seg->SetLayer( m_params.m_layer );
  207. new_seg->SetStart( wxPoint( seg.Get().A.x, seg.Get().A.y ) );
  208. new_seg->SetEnd( wxPoint( seg.Get().B.x, seg.Get().B.y ) );
  209. bCommit.Add( new_seg );
  210. }
  211. }
  212. bCommit.Push( _( "Add a graphical polygon" ) );
  213. break;
  214. }
  215. }
  216. }
  217. bool ZONE_CREATE_HELPER::OnFirstPoint( POLYGON_GEOM_MANAGER& aMgr )
  218. {
  219. // if we don't have a zone, create one
  220. // the user's choice here can affect things like the colour of the preview
  221. if( !m_zone )
  222. {
  223. if( m_params.m_sourceZone )
  224. m_zone = createZoneFromExisting( *m_params.m_sourceZone );
  225. else
  226. m_zone = createNewZone( m_params.m_keepout );
  227. if( m_zone )
  228. {
  229. m_tool.GetManager()->RunAction( PCB_ACTIONS::selectionClear, true );
  230. // set up poperties from zone
  231. const auto& settings = *m_parentView.GetPainter()->GetSettings();
  232. COLOR4D color = settings.GetColor( nullptr, m_zone->GetLayer() );
  233. m_previewItem.SetStrokeColor( COLOR4D::WHITE );
  234. m_previewItem.SetFillColor( color.WithAlpha( 0.2 ) );
  235. m_parentView.SetVisible( &m_previewItem, true );
  236. aMgr.SetLeaderMode( m_zone->GetHV45() ? POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45
  237. : POLYGON_GEOM_MANAGER::LEADER_MODE::DIRECT );
  238. }
  239. }
  240. return m_zone != nullptr;
  241. }
  242. void ZONE_CREATE_HELPER::OnGeometryChange( const POLYGON_GEOM_MANAGER& aMgr )
  243. {
  244. // send the points to the preview item
  245. m_previewItem.SetPoints( aMgr.GetLockedInPoints(), aMgr.GetLeaderLinePoints() );
  246. m_parentView.Update( &m_previewItem, KIGFX::GEOMETRY );
  247. }
  248. void ZONE_CREATE_HELPER::OnComplete( const POLYGON_GEOM_MANAGER& aMgr )
  249. {
  250. auto& finalPoints = aMgr.GetLockedInPoints();
  251. if( finalPoints.PointCount() < 3 )
  252. {
  253. // just scrap the zone in progress
  254. m_zone = nullptr;
  255. }
  256. else
  257. {
  258. // if m_params.m_mode == DRAWING_TOOL::ZONE_MODE::CUTOUT, m_zone
  259. // will be merged to the existing zone as a new hole.
  260. m_zone->Outline()->NewOutline();
  261. auto* outline = m_zone->Outline();
  262. for( int i = 0; i < finalPoints.PointCount(); ++i )
  263. outline->Append( finalPoints.CPoint( i ) );
  264. // In DEG45 mode, we may have intermediate points in the leader that should be
  265. // included as they are shown in the preview. These typically maintain the
  266. // 45 constraint
  267. if( aMgr.GetLeaderMode() == POLYGON_GEOM_MANAGER::LEADER_MODE::DEG45 )
  268. {
  269. const auto& pts = aMgr.GetLeaderLinePoints();
  270. for( int i = 1; i < pts.PointCount(); i++ )
  271. outline->Append( pts.CPoint( i ) );
  272. }
  273. outline->Outline( 0 ).SetClosed( true );
  274. outline->RemoveNullSegments();
  275. outline->Simplify( SHAPE_POLY_SET::PM_FAST );
  276. // hand the zone over to the committer
  277. commitZone( std::move( m_zone ) );
  278. m_zone = nullptr;
  279. }
  280. m_parentView.SetVisible( &m_previewItem, false );
  281. }