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.

342 lines
12 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2019 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-2019 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 <convert_to_biu.h>
  37. #include <confirm.h>
  38. #include <pcb_edit_frame.h>
  39. #include <board.h>
  40. #include <footprint.h>
  41. #include <rect_placement/rect_placement.h>
  42. struct TSubRect : public CRectPlacement::TRect
  43. {
  44. int n; // Original index of this subrect, before sorting
  45. TSubRect() : TRect(),
  46. n( 0 )
  47. {
  48. }
  49. TSubRect( int _w, int _h, int _n ) :
  50. TRect( 0, 0, _w, _h ), n( _n ) { }
  51. };
  52. typedef std::vector<TSubRect> CSubRectArray;
  53. // Use 0.01 mm units to calculate placement, to avoid long calculation time
  54. const int scale = (int)(0.01 * IU_PER_MM);
  55. const int PADDING = (int)(1 * IU_PER_MM);
  56. // Populates a list of rectangles, from a list of footprints
  57. void fillRectList( CSubRectArray& vecSubRects, std::vector <FOOTPRINT*>& aFootprintList )
  58. {
  59. vecSubRects.clear();
  60. for( unsigned ii = 0; ii < aFootprintList.size(); ii++ )
  61. {
  62. EDA_RECT fpBox = aFootprintList[ii]->GetBoundingBox( false, false );
  63. TSubRect fpRect( ( fpBox.GetWidth() + PADDING ) / scale,
  64. ( fpBox.GetHeight() + PADDING ) / scale, ii );
  65. vecSubRects.push_back( fpRect );
  66. }
  67. }
  68. // Populates a list of rectangles, from a list of EDA_RECT
  69. void fillRectList( CSubRectArray& vecSubRects, std::vector <EDA_RECT>& aRectList )
  70. {
  71. vecSubRects.clear();
  72. for( unsigned ii = 0; ii < aRectList.size(); ii++ )
  73. {
  74. EDA_RECT& rect = aRectList[ii];
  75. TSubRect fpRect( rect.GetWidth()/scale, rect.GetHeight()/scale, ii );
  76. vecSubRects.push_back( fpRect );
  77. }
  78. }
  79. // Spread a list of rectangles inside a placement area
  80. void spreadRectangles( CRectPlacement& aPlacementArea,
  81. CSubRectArray& vecSubRects,
  82. int areaSizeX, int areaSizeY )
  83. {
  84. areaSizeX/= scale;
  85. areaSizeY/= scale;
  86. // Sort the subRects based on dimensions, larger dimension goes first.
  87. std::sort( vecSubRects.begin(), vecSubRects.end(), CRectPlacement::TRect::Greater );
  88. // gives the initial size to the area
  89. aPlacementArea.Init( areaSizeX, areaSizeY );
  90. // Add all subrects
  91. CSubRectArray::iterator it;
  92. for( it = vecSubRects.begin(); it != vecSubRects.end(); )
  93. {
  94. CRectPlacement::TRect r( 0, 0, it->w, it->h );
  95. bool bPlaced = aPlacementArea.AddAtEmptySpotAutoGrow( &r, areaSizeX, areaSizeY );
  96. if( !bPlaced ) // No room to place the rectangle: enlarge area and retry
  97. {
  98. bool retry = false;
  99. if( areaSizeX < INT_MAX/2 )
  100. {
  101. retry = true;
  102. areaSizeX = areaSizeX * 1.2;
  103. }
  104. if( areaSizeX < INT_MAX/2 )
  105. {
  106. retry = true;
  107. areaSizeY = areaSizeY * 1.2;
  108. }
  109. if( retry )
  110. {
  111. aPlacementArea.Init( areaSizeX, areaSizeY );
  112. it = vecSubRects.begin();
  113. continue;
  114. }
  115. }
  116. // When correctly placed in a placement area, the coords are returned in r.x and r.y
  117. // Store them.
  118. it->x = r.x;
  119. it->y = r.y;
  120. it++;
  121. }
  122. }
  123. void moveFootprintsInArea( CRectPlacement& aPlacementArea, std::vector <FOOTPRINT*>& aFootprintList,
  124. EDA_RECT& aFreeArea, bool aFindAreaOnly )
  125. {
  126. CSubRectArray vecSubRects;
  127. fillRectList( vecSubRects, aFootprintList );
  128. spreadRectangles( aPlacementArea, vecSubRects, aFreeArea.GetWidth(), aFreeArea.GetHeight() );
  129. if( aFindAreaOnly )
  130. return;
  131. for( unsigned it = 0; it < vecSubRects.size(); ++it )
  132. {
  133. wxPoint pos( vecSubRects[it].x, vecSubRects[it].y );
  134. pos.x *= scale;
  135. pos.y *= scale;
  136. FOOTPRINT* footprint = aFootprintList[vecSubRects[it].n];
  137. EDA_RECT fpBBox = footprint->GetBoundingBox( false, false );
  138. wxPoint mod_pos = pos + ( footprint->GetPosition() - fpBBox.GetOrigin() )
  139. + aFreeArea.GetOrigin();
  140. footprint->Move( mod_pos - footprint->GetPosition() );
  141. }
  142. }
  143. static bool sortFootprintsbySheetPath( FOOTPRINT* ref, FOOTPRINT* compare );
  144. /**
  145. * Footprints (after loaded by reading a netlist for instance) are moved
  146. * to be in a small free area (outside the current board) without overlapping.
  147. * @param aBoard is the board to edit.
  148. * @param aFootprints: a list of footprints to be spread out.
  149. * @param aSpreadAreaPosition the position of the upper left corner of the
  150. * area allowed to spread footprints
  151. */
  152. void SpreadFootprints( std::vector<FOOTPRINT*>* aFootprints, wxPoint aSpreadAreaPosition )
  153. {
  154. // Build candidate list
  155. // calculate also the area needed by these footprints
  156. std::vector <FOOTPRINT*> footprintList;
  157. for( FOOTPRINT* footprint : *aFootprints )
  158. {
  159. if( footprint->IsLocked() )
  160. continue;
  161. footprintList.push_back( footprint );
  162. }
  163. if( footprintList.empty() )
  164. return;
  165. // sort footprints by sheet path. we group them later by sheet
  166. sort( footprintList.begin(), footprintList.end(), sortFootprintsbySheetPath );
  167. // Extract and place footprints by sheet
  168. std::vector <FOOTPRINT*> footprintListBySheet;
  169. std::vector <EDA_RECT> placementSheetAreas;
  170. double subsurface;
  171. double placementsurface = 0.0;
  172. // The placement uses 2 passes:
  173. // the first pass creates the rectangular areas to place footprints
  174. // each sheet in schematic creates one rectangular area.
  175. // the second pass moves footprints inside these areas
  176. for( int pass = 0; pass < 2; pass++ )
  177. {
  178. int subareaIdx = 0;
  179. footprintListBySheet.clear();
  180. subsurface = 0.0;
  181. int fp_max_width = 0;
  182. int fp_max_height = 0;
  183. for( unsigned ii = 0; ii < footprintList.size(); ii++ )
  184. {
  185. FOOTPRINT* footprint = footprintList[ii];
  186. bool islastItem = false;
  187. if( ii == footprintList.size() - 1 ||
  188. ( footprintList[ii]->GetPath().AsString().BeforeLast( '/' ) !=
  189. footprintList[ii+1]->GetPath().AsString().BeforeLast( '/' ) ) )
  190. islastItem = true;
  191. footprintListBySheet.push_back( footprint );
  192. subsurface += footprint->GetArea( PADDING );
  193. // Calculate min size of placement area:
  194. EDA_RECT bbox = footprint->GetBoundingBox( false, false );
  195. fp_max_width = std::max( fp_max_width, bbox.GetWidth() );
  196. fp_max_height = std::max( fp_max_height, bbox.GetHeight() );
  197. if( islastItem )
  198. {
  199. // end of the footprint sublist relative to the same sheet path
  200. // calculate placement of the current sublist
  201. EDA_RECT freeArea;
  202. int Xsize_allowed = (int) ( sqrt( subsurface ) * 4.0 / 3.0 );
  203. Xsize_allowed = std::max( fp_max_width, Xsize_allowed );
  204. int Ysize_allowed = (int) ( subsurface / Xsize_allowed );
  205. Ysize_allowed = std::max( fp_max_height, Ysize_allowed );
  206. freeArea.SetWidth( Xsize_allowed );
  207. freeArea.SetHeight( Ysize_allowed );
  208. CRectPlacement placementArea;
  209. if( pass == 1 )
  210. {
  211. wxPoint areapos = placementSheetAreas[subareaIdx].GetOrigin()
  212. + aSpreadAreaPosition;
  213. freeArea.SetOrigin( areapos );
  214. }
  215. bool findAreaOnly = pass == 0;
  216. moveFootprintsInArea( placementArea, footprintListBySheet, freeArea, findAreaOnly );
  217. if( pass == 0 )
  218. {
  219. // Populate sheet placement areas list
  220. EDA_RECT sub_area;
  221. sub_area.SetWidth( placementArea.GetW()*scale );
  222. sub_area.SetHeight( placementArea.GetH()*scale );
  223. // Add a margin around the sheet placement area:
  224. sub_area.Inflate( Millimeter2iu( 1.5 ) );
  225. placementSheetAreas.push_back( sub_area );
  226. placementsurface += (double) sub_area.GetWidth()*
  227. sub_area.GetHeight();
  228. }
  229. // Prepare buffers for next sheet
  230. subsurface = 0.0;
  231. footprintListBySheet.clear();
  232. subareaIdx++;
  233. }
  234. }
  235. // End of pass:
  236. // At the end of the first pass, we have to find position of each sheet
  237. // placement area
  238. if( pass == 0 )
  239. {
  240. int Xsize_allowed = (int) ( sqrt( placementsurface ) * 4.0 / 3.0 );
  241. if( Xsize_allowed < 0 || Xsize_allowed > INT_MAX/2 )
  242. Xsize_allowed = INT_MAX/2;
  243. int Ysize_allowed = (int) ( placementsurface / Xsize_allowed );
  244. if( Ysize_allowed < 0 || Ysize_allowed > INT_MAX/2 )
  245. Ysize_allowed = INT_MAX/2;
  246. CRectPlacement placementArea;
  247. CSubRectArray vecSubRects;
  248. fillRectList( vecSubRects, placementSheetAreas );
  249. spreadRectangles( placementArea, vecSubRects, Xsize_allowed, Ysize_allowed );
  250. for( unsigned it = 0; it < vecSubRects.size(); ++it )
  251. {
  252. TSubRect& srect = vecSubRects[it];
  253. wxPoint pos( srect.x*scale, srect.y*scale );
  254. wxSize size( srect.w*scale, srect.h*scale );
  255. // Avoid too large coordinates: Overlapping components
  256. // are better than out of screen components
  257. if( (uint64_t)pos.x + (uint64_t)size.x > INT_MAX/2 )
  258. pos.x = 0;
  259. if( (uint64_t)pos.y + (uint64_t)size.y > INT_MAX/2 )
  260. pos.y = 0;
  261. placementSheetAreas[srect.n].SetOrigin( pos );
  262. placementSheetAreas[srect.n].SetSize( size );
  263. }
  264. }
  265. } // End pass
  266. }
  267. // Sort function, used to group footprints by sheet.
  268. // Footprints are sorted by their sheet path.
  269. // (the full sheet path restricted to the time stamp of the sheet itself,
  270. // without the time stamp of the footprint ).
  271. static bool sortFootprintsbySheetPath( FOOTPRINT* ref, FOOTPRINT* compare )
  272. {
  273. return ref->GetPath() < compare->GetPath();
  274. }