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.

477 lines
16 KiB

  1. /**
  2. * @file gerber_file_image.cpp
  3. * a GERBER class handle for a given layer info about used D_CODES and how the layer is drawn
  4. */
  5. /*
  6. * This program source code file is part of KiCad, a free EDA CAD application.
  7. *
  8. * Copyright (C) 1992-2016 Jean-Pierre Charras jp.charras at wanadoo.fr
  9. * Copyright (C) 1992-2021 KiCad Developers, see AUTHORS.txt for contributors.
  10. *
  11. * This program is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU General Public License
  13. * as published by the Free Software Foundation; either version 2
  14. * of the License, or (at your option) any later version.
  15. *
  16. * This program is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  19. * GNU General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU General Public License
  22. * along with this program; if not, you may find one here:
  23. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  24. * or you may search the http://www.gnu.org website for the version 2 license,
  25. * or you may write to the Free Software Foundation, Inc.,
  26. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  27. */
  28. #include <gerbview.h>
  29. #include <gerbview_frame.h>
  30. #include <gerber_file_image.h>
  31. #include <gerber_file_image_list.h>
  32. #include <X2_gerber_attributes.h>
  33. #include <map>
  34. // The global image list:
  35. GERBER_FILE_IMAGE_LIST s_GERBER_List;
  36. GERBER_FILE_IMAGE_LIST::GERBER_FILE_IMAGE_LIST()
  37. {
  38. m_GERBER_List.reserve( GERBER_DRAWLAYERS_COUNT );
  39. for( unsigned layer = 0; layer < GERBER_DRAWLAYERS_COUNT; ++layer )
  40. m_GERBER_List.push_back( nullptr );
  41. }
  42. GERBER_FILE_IMAGE_LIST::~GERBER_FILE_IMAGE_LIST()
  43. {
  44. DeleteAllImages();
  45. }
  46. GERBER_FILE_IMAGE_LIST& GERBER_FILE_IMAGE_LIST::GetImagesList()
  47. {
  48. return s_GERBER_List;
  49. }
  50. GERBER_FILE_IMAGE* GERBER_FILE_IMAGE_LIST::GetGbrImage( int aIdx )
  51. {
  52. if( (unsigned)aIdx < m_GERBER_List.size() )
  53. return m_GERBER_List[aIdx];
  54. return nullptr;
  55. }
  56. unsigned GERBER_FILE_IMAGE_LIST::GetLoadedImageCount()
  57. {
  58. auto notNull = []( GERBER_FILE_IMAGE* image )
  59. {
  60. return image != nullptr;
  61. };
  62. return std::count_if( m_GERBER_List.begin(), m_GERBER_List.end(), notNull );
  63. }
  64. int GERBER_FILE_IMAGE_LIST::AddGbrImage( GERBER_FILE_IMAGE* aGbrImage, int aIdx )
  65. {
  66. int idx = aIdx;
  67. if( idx < 0 )
  68. {
  69. for( idx = 0; idx < (int)m_GERBER_List.size(); idx++ )
  70. {
  71. if( m_GERBER_List[idx] == nullptr )
  72. break;
  73. }
  74. }
  75. if( idx >= (int)m_GERBER_List.size() )
  76. return -1; // No room
  77. m_GERBER_List[idx] = aGbrImage;
  78. return idx;
  79. }
  80. void GERBER_FILE_IMAGE_LIST::DeleteAllImages()
  81. {
  82. for( unsigned idx = 0; idx < m_GERBER_List.size(); ++idx )
  83. DeleteImage( idx );
  84. }
  85. void GERBER_FILE_IMAGE_LIST::DeleteImage( unsigned int aIdx )
  86. {
  87. // Ensure the index is valid:
  88. if( aIdx >= m_GERBER_List.size() )
  89. return;
  90. // delete image aIdx
  91. GERBER_FILE_IMAGE* gbr_image = GetGbrImage( static_cast<int>( aIdx ) );
  92. delete gbr_image;
  93. m_GERBER_List[ aIdx ] = nullptr;
  94. }
  95. const wxString GERBER_FILE_IMAGE_LIST::GetDisplayName( int aIdx, bool aNameOnly, bool aFullName )
  96. {
  97. wxString name;
  98. GERBER_FILE_IMAGE* gerber = nullptr;
  99. if( aIdx >= 0 && aIdx < (int)m_GERBER_List.size() )
  100. gerber = m_GERBER_List[aIdx];
  101. // if a file is loaded, build the name:
  102. // <id> <short filename> <X2 FileFunction info> if a X2 FileFunction info is found
  103. // or (if no FileFunction info)
  104. // <id> <short filename> *
  105. if( gerber )
  106. {
  107. wxFileName fn( gerber->m_FileName );
  108. wxString filename = fn.GetFullName();
  109. // If the filename is too long, display a shortened name if requested
  110. const int maxlen = 30;
  111. if( !aFullName && filename.Length() > maxlen )
  112. {
  113. wxString shortenedfn = filename.Left(2) + wxT( "..." ) + filename.Right(maxlen-5);
  114. filename = shortenedfn;
  115. }
  116. if( gerber->m_FileFunction )
  117. {
  118. if( gerber->m_FileFunction->IsCopper() )
  119. {
  120. name.Printf( wxT( "%s (%s, %s, %s)" ),
  121. filename.GetData(),
  122. gerber->m_FileFunction->GetFileType(),
  123. gerber->m_FileFunction->GetBrdLayerId(),
  124. gerber->m_FileFunction->GetBrdLayerSide() );
  125. }
  126. if( gerber->m_FileFunction->IsDrillFile() )
  127. {
  128. name.Printf( wxT( "%s (%s,%s,%s,%s)" ),
  129. filename.GetData(),
  130. gerber->m_FileFunction->GetFileType(),
  131. gerber->m_FileFunction->GetDrillLayerPair(),
  132. gerber->m_FileFunction->GetLPType(),
  133. gerber->m_FileFunction->GetRouteType() );
  134. }
  135. else
  136. {
  137. name.Printf( wxT( "%s (%s, %s)" ),
  138. filename.GetData(),
  139. gerber->m_FileFunction->GetFileType(),
  140. gerber->m_FileFunction->GetBrdLayerId() );
  141. }
  142. }
  143. else
  144. {
  145. name = filename;
  146. }
  147. if( aNameOnly )
  148. return name;
  149. wxString fullname;
  150. fullname.Printf( wxT( "%d " ), aIdx + 1 );
  151. fullname << name;
  152. return fullname;
  153. }
  154. else
  155. {
  156. name.Printf( _( "Graphic layer %d" ), aIdx + 1 );
  157. }
  158. return name;
  159. }
  160. struct GERBER_ORDER
  161. {
  162. std::string m_FilenameMask;
  163. GERBER_ORDER_ENUM m_Order;
  164. };
  165. // clang-format off
  166. static struct GERBER_ORDER gerberFileExtensionOrder[] =
  167. {
  168. { ".GM1", GERBER_ORDER_ENUM::GERBER_BOARD_OUTLINE },
  169. { ".GM3", GERBER_ORDER_ENUM::GERBER_BOARD_OUTLINE },
  170. { ".GBR", GERBER_ORDER_ENUM::GERBER_BOARD_OUTLINE },
  171. { ".DIM", GERBER_ORDER_ENUM::GERBER_BOARD_OUTLINE },
  172. { ".MIL", GERBER_ORDER_ENUM::GERBER_BOARD_OUTLINE },
  173. { ".GML", GERBER_ORDER_ENUM::GERBER_BOARD_OUTLINE },
  174. { "EDGE.CUTS", GERBER_ORDER_ENUM::GERBER_BOARD_OUTLINE },
  175. { ".FAB", GERBER_ORDER_ENUM::GERBER_BOARD_OUTLINE },
  176. { ".GKO", GERBER_ORDER_ENUM::GERBER_KEEP_OUT },
  177. { ".GM?", GERBER_ORDER_ENUM::GERBER_MECHANICAL },
  178. { ".GM??", GERBER_ORDER_ENUM::GERBER_MECHANICAL },
  179. { ".TXT", GERBER_ORDER_ENUM::GERBER_DRILL },
  180. { ".XLN", GERBER_ORDER_ENUM::GERBER_DRILL },
  181. { ".TAP", GERBER_ORDER_ENUM::GERBER_DRILL },
  182. { ".DRD", GERBER_ORDER_ENUM::GERBER_DRILL },
  183. { ".DRL", GERBER_ORDER_ENUM::GERBER_DRILL },
  184. { ".NC", GERBER_ORDER_ENUM::GERBER_DRILL },
  185. { ".XNC", GERBER_ORDER_ENUM::GERBER_DRILL },
  186. { ".GTP", GERBER_ORDER_ENUM::GERBER_TOP_PASTE },
  187. { ".CRC", GERBER_ORDER_ENUM::GERBER_TOP_PASTE },
  188. { ".TSP", GERBER_ORDER_ENUM::GERBER_TOP_PASTE },
  189. { "F.PASTE", GERBER_ORDER_ENUM::GERBER_TOP_PASTE },
  190. { ".SPT", GERBER_ORDER_ENUM::GERBER_TOP_PASTE },
  191. { "PT.PHO", GERBER_ORDER_ENUM::GERBER_TOP_PASTE },
  192. { ".GTO", GERBER_ORDER_ENUM::GERBER_TOP_SILK_SCREEN },
  193. { ".PLC", GERBER_ORDER_ENUM::GERBER_TOP_SILK_SCREEN },
  194. { ".TSK", GERBER_ORDER_ENUM::GERBER_TOP_SILK_SCREEN },
  195. { "F.SILKS", GERBER_ORDER_ENUM::GERBER_TOP_SILK_SCREEN },
  196. { ".SST", GERBER_ORDER_ENUM::GERBER_TOP_SILK_SCREEN },
  197. { "ST.PHO", GERBER_ORDER_ENUM::GERBER_TOP_SILK_SCREEN },
  198. { ".GTS", GERBER_ORDER_ENUM::GERBER_TOP_SOLDER_MASK },
  199. { ".STC", GERBER_ORDER_ENUM::GERBER_TOP_SOLDER_MASK },
  200. { ".TSM", GERBER_ORDER_ENUM::GERBER_TOP_SOLDER_MASK },
  201. { "F.MASK", GERBER_ORDER_ENUM::GERBER_TOP_SOLDER_MASK },
  202. { ".SMT", GERBER_ORDER_ENUM::GERBER_TOP_SOLDER_MASK },
  203. { "MT.PHO", GERBER_ORDER_ENUM::GERBER_TOP_SOLDER_MASK },
  204. { ".GTL", GERBER_ORDER_ENUM::GERBER_TOP_COPPER },
  205. { ".CMP", GERBER_ORDER_ENUM::GERBER_TOP_COPPER },
  206. { ".TOP", GERBER_ORDER_ENUM::GERBER_TOP_COPPER },
  207. { "F.CU", GERBER_ORDER_ENUM::GERBER_TOP_COPPER },
  208. { "L1.PHO", GERBER_ORDER_ENUM::GERBER_TOP_COPPER },
  209. { ".PHD", GERBER_ORDER_ENUM::GERBER_TOP_COPPER },
  210. { ".ART", GERBER_ORDER_ENUM::GERBER_TOP_COPPER },
  211. { ".GBL", GERBER_ORDER_ENUM::GERBER_BOTTOM_COPPER },
  212. { ".SOL", GERBER_ORDER_ENUM::GERBER_BOTTOM_COPPER },
  213. { ".BOT", GERBER_ORDER_ENUM::GERBER_BOTTOM_COPPER },
  214. { "B.CU", GERBER_ORDER_ENUM::GERBER_BOTTOM_COPPER },
  215. { ".BOT", GERBER_ORDER_ENUM::GERBER_BOTTOM_COPPER },
  216. { ".GBS", GERBER_ORDER_ENUM::GERBER_BOTTOM_SOLDER_MASK },
  217. { ".STS", GERBER_ORDER_ENUM::GERBER_BOTTOM_SOLDER_MASK },
  218. { ".BSM", GERBER_ORDER_ENUM::GERBER_BOTTOM_SOLDER_MASK },
  219. { "B.MASK", GERBER_ORDER_ENUM::GERBER_BOTTOM_SOLDER_MASK },
  220. { ".SMB", GERBER_ORDER_ENUM::GERBER_BOTTOM_SOLDER_MASK },
  221. { "MB.PHO", GERBER_ORDER_ENUM::GERBER_BOTTOM_SOLDER_MASK },
  222. { ".GBO", GERBER_ORDER_ENUM::GERBER_BOTTOM_SILK_SCREEN },
  223. { ".PLS", GERBER_ORDER_ENUM::GERBER_BOTTOM_SILK_SCREEN },
  224. { ".BSK", GERBER_ORDER_ENUM::GERBER_BOTTOM_SILK_SCREEN },
  225. { "B.SILK", GERBER_ORDER_ENUM::GERBER_BOTTOM_SILK_SCREEN },
  226. { ".SSB", GERBER_ORDER_ENUM::GERBER_BOTTOM_SILK_SCREEN },
  227. { "SB.PHO", GERBER_ORDER_ENUM::GERBER_BOTTOM_SILK_SCREEN },
  228. { ".GBP", GERBER_ORDER_ENUM::GERBER_BOTTOM_PASTE },
  229. { ".CRS", GERBER_ORDER_ENUM::GERBER_BOTTOM_PASTE },
  230. { ".BSP", GERBER_ORDER_ENUM::GERBER_BOTTOM_PASTE },
  231. { "B.PASTE", GERBER_ORDER_ENUM::GERBER_BOTTOM_PASTE },
  232. { ".SMB", GERBER_ORDER_ENUM::GERBER_BOTTOM_PASTE },
  233. { "MB.PHO", GERBER_ORDER_ENUM::GERBER_BOTTOM_PASTE },
  234. // EAGLE CAD file to explicitly ignore that can match some other
  235. // layers otherwise
  236. { ".GPI", GERBER_ORDER_ENUM::GERBER_LAYER_UNKNOWN },
  237. // Inner copper layers need to come last so the wildcard
  238. // number matching doesn't pick up other specific layer names.
  239. { ".GI?", GERBER_ORDER_ENUM::GERBER_INNER },
  240. { ".GI??", GERBER_ORDER_ENUM::GERBER_INNER },
  241. { ".G?", GERBER_ORDER_ENUM::GERBER_INNER },
  242. { ".G??", GERBER_ORDER_ENUM::GERBER_INNER },
  243. { ".G?L", GERBER_ORDER_ENUM::GERBER_INNER },
  244. { ".G??L", GERBER_ORDER_ENUM::GERBER_INNER },
  245. };
  246. // clang-format on
  247. void GERBER_FILE_IMAGE_LIST::GetGerberLayerFromFilename( const wxString& filename,
  248. enum GERBER_ORDER_ENUM& order,
  249. wxString& matchedExtension )
  250. {
  251. order = GERBER_ORDER_ENUM::GERBER_LAYER_UNKNOWN;
  252. matchedExtension = "";
  253. for( struct GERBER_ORDER o : gerberFileExtensionOrder )
  254. {
  255. wxString ext = filename.Right( o.m_FilenameMask.length() ).Upper();
  256. if( ext.Matches( o.m_FilenameMask ) )
  257. {
  258. order = o.m_Order;
  259. matchedExtension = ext;
  260. return;
  261. }
  262. }
  263. }
  264. static bool sortFileExtension( const GERBER_FILE_IMAGE* const& ref,
  265. const GERBER_FILE_IMAGE* const& test )
  266. {
  267. // Do not change order: no criteria to sort items
  268. if( !ref && !test )
  269. return false;
  270. // Not used: ref ordered after
  271. if( !ref || !ref->m_InUse )
  272. return false;
  273. // Not used: ref ordered before
  274. if( !test || !test->m_InUse )
  275. return true;
  276. enum GERBER_ORDER_ENUM ref_layer;
  277. enum GERBER_ORDER_ENUM test_layer;
  278. wxString ref_extension;
  279. wxString test_extension;
  280. GERBER_FILE_IMAGE_LIST::GetGerberLayerFromFilename( ref->m_FileName, ref_layer,
  281. ref_extension );
  282. GERBER_FILE_IMAGE_LIST::GetGerberLayerFromFilename( test->m_FileName, test_layer,
  283. test_extension );
  284. // Inner layers have a numeric code that we can compare against
  285. if( ref_layer == GERBER_ORDER_ENUM::GERBER_INNER
  286. && test_layer == GERBER_ORDER_ENUM::GERBER_INNER )
  287. {
  288. unsigned long ref_layer_number = 0;
  289. unsigned long test_layer_number = 0;
  290. // Strip extensions down to only the numbers in it. Later conversion to int will
  291. // automatically skip the spaces
  292. for( wxString::iterator it = ref_extension.begin(); it != ref_extension.end(); ++it )
  293. {
  294. if( !isdigit( *it ) )
  295. *it = ' ';
  296. }
  297. for( wxString::iterator it = test_extension.begin(); it != test_extension.end(); ++it )
  298. {
  299. if( !isdigit( *it ) )
  300. *it = ' ';
  301. }
  302. ref_extension.ToULong( &ref_layer_number );
  303. test_extension.ToULong( &test_layer_number );
  304. return ref_layer_number < test_layer_number;
  305. }
  306. return (int) ref_layer < (int) test_layer;
  307. }
  308. // Helper function, for std::sort.
  309. // Sort loaded images by Z order priority, if they have the X2 FileFormat info
  310. // returns true if the first argument (ref) is ordered before the second (test).
  311. static bool sortZorder( const GERBER_FILE_IMAGE* const& ref, const GERBER_FILE_IMAGE* const& test )
  312. {
  313. if( !ref && !test )
  314. return false; // do not change order: no criteria to sort items
  315. if( !ref || !ref->m_InUse )
  316. return false; // Not used: ref ordered after
  317. if( !test || !test->m_InUse )
  318. return true; // Not used: ref ordered before
  319. if( !ref->m_FileFunction && !test->m_FileFunction )
  320. return false; // do not change order: no criteria to sort items
  321. if( !ref->m_FileFunction )
  322. return false;
  323. if( !test->m_FileFunction )
  324. return true;
  325. if( ref->m_FileFunction->GetZOrder() != test->m_FileFunction->GetZOrder() )
  326. return ref->m_FileFunction->GetZOrder() > test->m_FileFunction->GetZOrder();
  327. return ref->m_FileFunction->GetZSubOrder() > test->m_FileFunction->GetZSubOrder();
  328. }
  329. std::unordered_map<int, int> GERBER_FILE_IMAGE_LIST::GetLayerRemap()
  330. {
  331. // The image order has changed.
  332. // Graphic layer numbering must be updated to match the widgets layer order
  333. // Store the old/new graphic layer info:
  334. std::unordered_map<int, int> tab_lyr;
  335. for( unsigned layer = 0; layer < m_GERBER_List.size(); ++layer )
  336. {
  337. GERBER_FILE_IMAGE* gerber = m_GERBER_List[layer];
  338. if( !gerber )
  339. continue;
  340. tab_lyr[gerber->m_GraphicLayer] = layer;
  341. gerber->m_GraphicLayer = layer ;
  342. }
  343. return tab_lyr;
  344. }
  345. std::unordered_map<int, int>
  346. GERBER_FILE_IMAGE_LIST::SortImagesByFunction( LayerSortFunction sortFunction )
  347. {
  348. std::sort( m_GERBER_List.begin(), m_GERBER_List.end(), sortFunction );
  349. return GetLayerRemap();
  350. }
  351. std::unordered_map<int, int> GERBER_FILE_IMAGE_LIST::SortImagesByFileExtension()
  352. {
  353. return SortImagesByFunction( sortFileExtension );
  354. }
  355. std::unordered_map<int, int> GERBER_FILE_IMAGE_LIST::SortImagesByZOrder()
  356. {
  357. return SortImagesByFunction( sortZorder );
  358. }
  359. std::unordered_map<int, int> GERBER_FILE_IMAGE_LIST::SwapImages( unsigned int layer1,
  360. unsigned int layer2 )
  361. {
  362. if( ( layer1 >= m_GERBER_List.size() ) || ( layer2 >= m_GERBER_List.size() ) )
  363. return std::unordered_map<int, int>();
  364. std::swap( m_GERBER_List[layer1], m_GERBER_List[layer2] );
  365. return GetLayerRemap();
  366. }
  367. std::unordered_map<int, int> GERBER_FILE_IMAGE_LIST::RemoveImage( unsigned int layer )
  368. {
  369. if( layer >= m_GERBER_List.size() )
  370. return std::unordered_map<int, int>();
  371. DeleteImage( layer );
  372. // Move deleted image to end of list, move all other images up
  373. std::rotate( m_GERBER_List.begin() + layer, m_GERBER_List.begin() + layer + 1,
  374. m_GERBER_List.end() );
  375. return GetLayerRemap();
  376. }