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.

378 lines
13 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2013 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr
  5. * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  6. * Copyright (C) 2013 Wayne Stambaugh <stambaughw@verizon.net>
  7. *
  8. * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
  9. *
  10. * This program is free software; you can redistribute it and/or
  11. * modify it under the terms of the GNU General Public License
  12. * as published by the Free Software Foundation; either version 2
  13. * of the License, or (at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program; if not, you may find one here:
  22. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  23. * or you may search the http://www.gnu.org website for the version 2 license,
  24. * or you may write to the Free Software Foundation, Inc.,
  25. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  26. */
  27. /**
  28. * @file spread_footprints.cpp
  29. * @brief functions to spread footprints on free areas outside a board.
  30. * this is usefull after reading a netlist, when new footprints are loaded
  31. * and stacked at 0,0 coordinate.
  32. * Often, spread them on a free area near the board being edited make more easy
  33. * their selection.
  34. */
  35. #include <algorithm>
  36. #include <fctsys.h>
  37. #include <convert_to_biu.h>
  38. #include <class_drawpanel.h>
  39. #include <confirm.h>
  40. #include <pcbnew.h>
  41. #include <pcb_edit_frame.h>
  42. #include <class_board.h>
  43. #include <class_module.h>
  44. #include <rect_placement/rect_placement.h>
  45. struct TSubRect : public CRectPlacement::TRect
  46. {
  47. int n; // Original index of this subrect, before sorting
  48. TSubRect() : TRect(),
  49. n( 0 )
  50. {
  51. }
  52. TSubRect( int _w, int _h, int _n ) :
  53. TRect( 0, 0, _w, _h ), n( _n ) { }
  54. };
  55. typedef std::vector<TSubRect> CSubRectArray;
  56. // Use 0.01 mm units to calculate placement, to avoid long calculation time
  57. const int scale = (int)(0.01 * IU_PER_MM);
  58. // Populates a list of rectangles, from a list of modules
  59. void fillRectList( CSubRectArray& vecSubRects, std::vector <MODULE*>& aModuleList )
  60. {
  61. vecSubRects.clear();
  62. for( unsigned ii = 0; ii < aModuleList.size(); ii++ )
  63. {
  64. EDA_RECT fpBox = aModuleList[ii]->GetFootprintRect();
  65. TSubRect fpRect( fpBox.GetWidth()/scale, fpBox.GetHeight()/scale, ii );
  66. vecSubRects.push_back( fpRect );
  67. }
  68. }
  69. // Populates a list of rectangles, from a list of EDA_RECT
  70. void fillRectList( CSubRectArray& vecSubRects, std::vector <EDA_RECT>& aRectList )
  71. {
  72. vecSubRects.clear();
  73. for( unsigned ii = 0; ii < aRectList.size(); ii++ )
  74. {
  75. EDA_RECT& rect = aRectList[ii];
  76. TSubRect fpRect( rect.GetWidth()/scale, rect.GetHeight()/scale, ii );
  77. vecSubRects.push_back( fpRect );
  78. }
  79. }
  80. // Spread a list of rectangles inside a placement area
  81. void spreadRectangles( CRectPlacement& aPlacementArea,
  82. CSubRectArray& vecSubRects,
  83. int areaSizeX, int areaSizeY )
  84. {
  85. areaSizeX/= scale;
  86. areaSizeY/= scale;
  87. // Sort the subRects based on dimensions, larger dimension goes first.
  88. std::sort( vecSubRects.begin(), vecSubRects.end(), CRectPlacement::TRect::Greater );
  89. // gives the initial size to the area
  90. aPlacementArea.Init( areaSizeX, areaSizeY );
  91. // Add all subrects
  92. CSubRectArray::iterator it;
  93. for( it = vecSubRects.begin(); it != vecSubRects.end(); )
  94. {
  95. CRectPlacement::TRect r( 0, 0, it->w, it->h );
  96. bool bPlaced = aPlacementArea.AddAtEmptySpotAutoGrow( &r, areaSizeX, areaSizeY );
  97. if( !bPlaced ) // No room to place the rectangle: enlarge area and retry
  98. {
  99. areaSizeX = ceil(areaSizeX * 1.1);
  100. areaSizeY = ceil(areaSizeY * 1.1);
  101. aPlacementArea.Init( areaSizeX, areaSizeY );
  102. it = vecSubRects.begin();
  103. continue;
  104. }
  105. // When correctly placed in a placement area, the coords are returned in r.x and r.y
  106. // Store them.
  107. it->x = r.x;
  108. it->y = r.y;
  109. it++;
  110. }
  111. }
  112. void moveFootprintsInArea( CRectPlacement& aPlacementArea,
  113. std::vector <MODULE*>& aModuleList,
  114. EDA_RECT& aFreeArea,
  115. bool aFindAreaOnly )
  116. {
  117. CSubRectArray vecSubRects;
  118. fillRectList( vecSubRects, aModuleList );
  119. spreadRectangles( aPlacementArea, vecSubRects,
  120. aFreeArea.GetWidth(), aFreeArea.GetHeight() );
  121. if( aFindAreaOnly )
  122. return;
  123. for( unsigned it = 0; it < vecSubRects.size(); ++it )
  124. {
  125. wxPoint pos( vecSubRects[it].x, vecSubRects[it].y );
  126. pos.x *= scale;
  127. pos.y *= scale;
  128. MODULE * module = aModuleList[vecSubRects[it].n];
  129. EDA_RECT fpBBox = module->GetFootprintRect();
  130. wxPoint mod_pos = pos + ( module->GetPosition() - fpBBox.GetOrigin() )
  131. + aFreeArea.GetOrigin();
  132. module->Move( mod_pos - module->GetPosition() );
  133. }
  134. }
  135. static bool sortFootprintsbySheetPath( MODULE* ref, MODULE* compare );
  136. /* Function to move components in a rectangular area format 4 / 3,
  137. * starting from the mouse cursor.
  138. * Footprints are grouped by sheet.
  139. * Components with the LOCKED status set are not moved
  140. */
  141. void PCB_EDIT_FRAME::SpreadFootprints( std::vector<MODULE*>* aFootprints,
  142. bool aMoveFootprintsOutsideBoardOnly,
  143. bool aCheckForBoardEdges,
  144. wxPoint aSpreadAreaPosition,
  145. bool aPrepareUndoCommand )
  146. {
  147. EDA_RECT bbox = GetBoard()->GetBoardEdgesBoundingBox();
  148. bool edgesExist = bbox.GetWidth() || bbox.GetHeight();
  149. // if aFootprintsOutsideBoardOnly is true, and if board outline exists,
  150. // we have to filter footprints to move:
  151. bool outsideBrdFilter = aMoveFootprintsOutsideBoardOnly && edgesExist;
  152. // no edges exist
  153. if( aMoveFootprintsOutsideBoardOnly && !edgesExist )
  154. {
  155. DisplayError( this,
  156. _( "Could not automatically place footprints. No board outlines detected." ) );
  157. return;
  158. }
  159. // Build candidate list
  160. // calculate also the area needed by these footprints
  161. std::vector <MODULE*> footprintList;
  162. for( MODULE* footprint : *aFootprints )
  163. {
  164. footprint->CalculateBoundingBox();
  165. if( outsideBrdFilter )
  166. {
  167. if( bbox.Contains( footprint->GetPosition() ) )
  168. continue;
  169. }
  170. if( footprint->IsLocked() )
  171. continue;
  172. footprintList.push_back( footprint );
  173. }
  174. if( footprintList.empty() )
  175. return;
  176. // sort footprints by sheet path. we group them later by sheet
  177. sort( footprintList.begin(), footprintList.end(), sortFootprintsbySheetPath );
  178. // Undo command: init undo list. If aPrepareUndoCommand == false
  179. // no undo command will be initialized.
  180. // Useful when a undo command is already initialized by the caller
  181. PICKED_ITEMS_LIST undoList;
  182. if( aPrepareUndoCommand )
  183. {
  184. undoList.m_Status = UR_CHANGED;
  185. ITEM_PICKER picker( NULL, UR_CHANGED );
  186. for( MODULE* footprint : footprintList )
  187. {
  188. // Undo: add copy of the footprint to undo list
  189. picker.SetItem( footprint );
  190. picker.SetLink( footprint->Clone() );
  191. undoList.PushItem( picker );
  192. }
  193. }
  194. // Extract and place footprints by sheet
  195. std::vector <MODULE*> footprintListBySheet;
  196. std::vector <EDA_RECT> placementSheetAreas;
  197. double subsurface;
  198. double placementsurface = 0.0;
  199. // put the placement area position on mouse cursor.
  200. // this position will be adjusted later
  201. wxPoint placementAreaPosition = aSpreadAreaPosition;
  202. // We sometimes do not want to move footprints inside an existing board.
  203. // Therefore, move the placement area position outside the board bounding box
  204. // to the left of the board
  205. if( aCheckForBoardEdges && edgesExist )
  206. {
  207. if( placementAreaPosition.x < bbox.GetEnd().x &&
  208. placementAreaPosition.y < bbox.GetEnd().y )
  209. {
  210. // the placement area could overlap the board
  211. // move its position to a safe location
  212. placementAreaPosition.x = bbox.GetEnd().x;
  213. placementAreaPosition.y = bbox.GetOrigin().y;
  214. }
  215. }
  216. // The placement uses 2 passes:
  217. // the first pass creates the rectangular areas to place footprints
  218. // each sheet in schematic creates one rectangular area.
  219. // the second pass moves footprints inside these areas
  220. MODULE* footprint;
  221. for( int pass = 0; pass < 2; pass++ )
  222. {
  223. int subareaIdx = 0;
  224. footprintListBySheet.clear();
  225. subsurface = 0.0;
  226. for( unsigned ii = 0; ii < footprintList.size(); ii++ )
  227. {
  228. footprint = footprintList[ii];
  229. bool islastItem = false;
  230. if( ii == footprintList.size() - 1 ||
  231. ( footprintList[ii]->GetPath().BeforeLast( '/' ) !=
  232. footprintList[ii+1]->GetPath().BeforeLast( '/' ) ) )
  233. islastItem = true;
  234. footprintListBySheet.push_back( footprint );
  235. subsurface += footprint->GetArea();
  236. if( islastItem )
  237. {
  238. // end of the footprint sublist relative to the same sheet path
  239. // calculate placement of the current sublist
  240. EDA_RECT freeArea;
  241. int Xsize_allowed = (int) ( sqrt( subsurface ) * 4.0 / 3.0 );
  242. int Ysize_allowed = (int) ( subsurface / Xsize_allowed );
  243. freeArea.SetWidth( Xsize_allowed );
  244. freeArea.SetHeight( Ysize_allowed );
  245. CRectPlacement placementArea;
  246. if( pass == 1 )
  247. {
  248. wxPoint areapos = placementSheetAreas[subareaIdx].GetOrigin()
  249. + placementAreaPosition;
  250. freeArea.SetOrigin( areapos );
  251. }
  252. bool findAreaOnly = pass == 0;
  253. moveFootprintsInArea( placementArea, footprintListBySheet,
  254. freeArea, findAreaOnly );
  255. if( pass == 0 )
  256. {
  257. // Populate sheet placement areas list
  258. EDA_RECT sub_area;
  259. sub_area.SetWidth( placementArea.GetW()*scale );
  260. sub_area.SetHeight( placementArea.GetH()*scale );
  261. // Add a margin around the sheet placement area:
  262. sub_area.Inflate( Millimeter2iu( 1.5 ) );
  263. placementSheetAreas.push_back( sub_area );
  264. placementsurface += (double) sub_area.GetWidth()*
  265. sub_area.GetHeight();
  266. }
  267. // Prepare buffers for next sheet
  268. subsurface = 0.0;
  269. footprintListBySheet.clear();
  270. subareaIdx++;
  271. }
  272. }
  273. // End of pass:
  274. // At the end of the first pass, we have to find position of each sheet
  275. // placement area
  276. if( pass == 0 )
  277. {
  278. int Xsize_allowed = (int) ( sqrt( placementsurface ) * 4.0 / 3.0 );
  279. int Ysize_allowed = (int) ( placementsurface / Xsize_allowed );
  280. CRectPlacement placementArea;
  281. CSubRectArray vecSubRects;
  282. fillRectList( vecSubRects, placementSheetAreas );
  283. spreadRectangles( placementArea, vecSubRects, Xsize_allowed, Ysize_allowed );
  284. for( unsigned it = 0; it < vecSubRects.size(); ++it )
  285. {
  286. TSubRect& srect = vecSubRects[it];
  287. wxPoint pos( srect.x*scale, srect.y*scale );
  288. wxSize size( srect.w*scale, srect.h*scale );
  289. placementSheetAreas[srect.n].SetOrigin( pos );
  290. placementSheetAreas[srect.n].SetSize( size );
  291. }
  292. }
  293. } // End pass
  294. // Undo: commit list
  295. if( aPrepareUndoCommand )
  296. SaveCopyInUndoList( undoList, UR_CHANGED );
  297. OnModify();
  298. m_canvas->Refresh();
  299. }
  300. // Sort function, used to group footprints by sheet.
  301. // Footprints are sorted by their sheet path.
  302. // (the full sheet path restricted to the time stamp of the sheet itself,
  303. // without the time stamp of the footprint ).
  304. static bool sortFootprintsbySheetPath( MODULE* ref, MODULE* compare )
  305. {
  306. if( ref->GetPath().Length() == compare->GetPath().Length() )
  307. return ref->GetPath().BeforeLast( '/' ).Cmp( compare->GetPath().BeforeLast( '/' ) ) < 0;
  308. return ref->GetPath().Length() < compare->GetPath().Length();
  309. }