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.

438 lines
15 KiB

4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015-2022 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. /*
  24. * 1 - create ascii/csv files for automatic placement of smd components
  25. * 2 - create a footprint report (pos and footprint descr) (ascii file)
  26. */
  27. #include <string_utils.h>
  28. #include <macros.h>
  29. #include <locale_io.h>
  30. #include <board_design_settings.h>
  31. #include <build_version.h>
  32. #include <exporters/place_file_exporter.h>
  33. #include <pad.h>
  34. #include <wx/dirdlg.h>
  35. class LIST_MOD // An helper class used to build a list of useful footprints.
  36. {
  37. public:
  38. FOOTPRINT* m_Footprint; // Link to the actual footprint
  39. wxString m_Reference; // Its schematic reference
  40. wxString m_Value; // Its schematic value
  41. int m_Layer; // its side (B_Cu, or F_Cu)
  42. };
  43. // Defined values to write coordinates using inches or mm:
  44. static const double conv_unit_inch = 0.001 / pcbIUScale.IU_PER_MILS ; // units = INCHES
  45. static const char unit_text_inch[] = "## Unit = inches, Angle = deg.\n";
  46. static const double conv_unit_mm = 1.0 / pcbIUScale.IU_PER_MM; // units = mm
  47. static const char unit_text_mm[] = "## Unit = mm, Angle = deg.\n";
  48. // Sort function use by GenerefootprintsPosition()
  49. // sort is made by side (layer) top layer first
  50. // then by reference increasing order
  51. static bool sortFPlist( const LIST_MOD& ref, const LIST_MOD& tst )
  52. {
  53. if( ref.m_Layer == tst.m_Layer )
  54. return StrNumCmp( ref.m_Reference, tst.m_Reference ) < 0;
  55. return ref.m_Layer > tst.m_Layer;
  56. }
  57. enum SELECT_SIDE
  58. {
  59. PCB_NO_SIDE,
  60. PCB_BACK_SIDE,
  61. PCB_FRONT_SIDE,
  62. PCB_BOTH_SIDES
  63. };
  64. PLACE_FILE_EXPORTER::PLACE_FILE_EXPORTER( BOARD* aBoard, bool aUnitsMM, bool aOnlySMD,
  65. bool aExcludeAllTH, bool aExcludeDNP, bool aTopSide,
  66. bool aBottomSide, bool aFormatCSV, bool aUseAuxOrigin,
  67. bool aNegateBottomX )
  68. {
  69. m_board = aBoard;
  70. m_unitsMM = aUnitsMM;
  71. m_onlySMD = aOnlySMD;
  72. m_excludeAllTH = aExcludeAllTH;
  73. m_excludeDNP = aExcludeDNP;
  74. m_fpCount = 0;
  75. m_negateBottomX = aNegateBottomX;
  76. if( aTopSide && aBottomSide )
  77. m_side = PCB_BOTH_SIDES;
  78. else if( aTopSide )
  79. m_side = PCB_FRONT_SIDE;
  80. else if( aBottomSide )
  81. m_side = PCB_BACK_SIDE;
  82. else
  83. m_side = PCB_NO_SIDE;
  84. m_formatCSV = aFormatCSV;
  85. if( aUseAuxOrigin )
  86. m_place_Offset = m_board->GetDesignSettings().GetAuxOrigin();
  87. else
  88. m_place_Offset = VECTOR2I( 0, 0 );
  89. }
  90. std::string PLACE_FILE_EXPORTER::GenPositionData()
  91. {
  92. std::string buffer;
  93. char line[1024]; // A line to print intermediate data
  94. // Minimal text lengths:
  95. m_fpCount = 0;
  96. int lenRefText = 8;
  97. int lenValText = 8;
  98. int lenPkgText = 16;
  99. // Calculating the number of useful footprints (CMS attribute, not VIRTUAL)
  100. m_fpCount = 0;
  101. // Select units:
  102. double conv_unit = m_unitsMM ? conv_unit_mm : conv_unit_inch;
  103. const char *unit_text = m_unitsMM ? unit_text_mm : unit_text_inch;
  104. // Build and sort the list of footprints alphabetically
  105. std::vector<LIST_MOD> list;
  106. for( FOOTPRINT* footprint : m_board->Footprints() )
  107. {
  108. if( m_side != PCB_BOTH_SIDES )
  109. {
  110. if( footprint->GetLayer() == B_Cu && m_side != PCB_BACK_SIDE )
  111. continue;
  112. if( footprint->GetLayer() == F_Cu && m_side != PCB_FRONT_SIDE )
  113. continue;
  114. }
  115. if( footprint->GetAttributes() & FP_EXCLUDE_FROM_POS_FILES )
  116. continue;
  117. if( m_onlySMD && !( footprint->GetAttributes() & FP_SMD ) )
  118. continue;
  119. if( m_excludeAllTH && footprint->HasThroughHolePads() )
  120. continue;
  121. if( m_excludeDNP && ( footprint->GetAttributes() & FP_DNP ) )
  122. continue;
  123. m_fpCount++;
  124. LIST_MOD item;
  125. item.m_Footprint = footprint;
  126. item.m_Reference = footprint->Reference().GetShownText( false );
  127. item.m_Value = footprint->Value().GetShownText( false );
  128. item.m_Layer = footprint->GetLayer();
  129. list.push_back( item );
  130. lenRefText = std::max( lenRefText, (int) item.m_Reference.length() );
  131. lenValText = std::max( lenValText, (int) item.m_Value.length() );
  132. lenPkgText = std::max( lenPkgText, (int) item.m_Footprint->GetFPID().GetLibItemName().length() );
  133. }
  134. if( list.size() > 1 )
  135. sort( list.begin(), list.end(), sortFPlist );
  136. // Switch the locale to standard C (needed to print floating point numbers)
  137. LOCALE_IO toggle;
  138. if( m_formatCSV )
  139. {
  140. wxChar csv_sep = ',';
  141. // Set first line:;
  142. snprintf( line, sizeof(line), "Ref%cVal%cPackage%cPosX%cPosY%cRot%cSide\n",
  143. csv_sep, csv_sep, csv_sep, csv_sep, csv_sep, csv_sep );
  144. buffer += line;
  145. for( int ii = 0; ii < m_fpCount; ii++ )
  146. {
  147. VECTOR2I footprint_pos;
  148. footprint_pos = list[ii].m_Footprint->GetPosition();
  149. footprint_pos -= m_place_Offset;
  150. int layer = list[ii].m_Footprint->GetLayer();
  151. wxASSERT( layer == F_Cu || layer == B_Cu );
  152. if( layer == B_Cu && m_negateBottomX )
  153. footprint_pos.x = - footprint_pos.x;
  154. wxString tmp = wxT( "\"" ) + list[ii].m_Reference;
  155. tmp << wxT( "\"" ) << csv_sep;
  156. tmp << wxT( "\"" ) << list[ii].m_Value;
  157. tmp << wxT( "\"" ) << csv_sep;
  158. tmp << wxT( "\"" ) << list[ii].m_Footprint->GetFPID().GetLibItemName().wx_str();
  159. tmp << wxT( "\"" ) << csv_sep;
  160. tmp << wxString::Format( wxT( "%f%c%f%c%f" ),
  161. footprint_pos.x * conv_unit,
  162. csv_sep,
  163. // Keep the Y axis oriented from bottom to top,
  164. // ( change y coordinate sign )
  165. -footprint_pos.y * conv_unit,
  166. csv_sep,
  167. list[ii].m_Footprint->GetOrientation().AsDegrees() );
  168. tmp << csv_sep;
  169. tmp << ( (layer == F_Cu ) ? PLACE_FILE_EXPORTER::GetFrontSideName()
  170. : PLACE_FILE_EXPORTER::GetBackSideName() );
  171. tmp << '\n';
  172. buffer += TO_UTF8( tmp );
  173. }
  174. }
  175. else
  176. {
  177. // Write file header
  178. snprintf( line, sizeof(line), "### Footprint positions - created on %s ###\n", TO_UTF8( GetISO8601CurrentDateTime() ) );
  179. buffer += line;
  180. wxString Title = GetBuildVersion();
  181. snprintf( line, sizeof(line), "### Printed by KiCad version %s\n", TO_UTF8( Title ) );
  182. buffer += line;
  183. buffer += unit_text;
  184. buffer += "## Side : ";
  185. if( m_side == PCB_BACK_SIDE )
  186. buffer += GetBackSideName().c_str();
  187. else if( m_side == PCB_FRONT_SIDE )
  188. buffer += GetFrontSideName().c_str();
  189. else if( m_side == PCB_BOTH_SIDES )
  190. buffer += "All";
  191. else
  192. buffer += "---";
  193. buffer += "\n";
  194. snprintf( line, sizeof(line), "%-*s %-*s %-*s %9.9s %9.9s %8.8s %s\n",
  195. int(lenRefText), "# Ref",
  196. int(lenValText), "Val",
  197. int(lenPkgText), "Package",
  198. "PosX", "PosY", "Rot", "Side" );
  199. buffer += line;
  200. for( int ii = 0; ii < m_fpCount; ii++ )
  201. {
  202. VECTOR2I footprint_pos;
  203. footprint_pos = list[ii].m_Footprint->GetPosition();
  204. footprint_pos -= m_place_Offset;
  205. int layer = list[ii].m_Footprint->GetLayer();
  206. wxASSERT( layer == F_Cu || layer == B_Cu );
  207. if( layer == B_Cu && m_negateBottomX )
  208. footprint_pos.x = - footprint_pos.x;
  209. wxString ref = list[ii].m_Reference;
  210. wxString val = list[ii].m_Value;
  211. wxString pkg = list[ii].m_Footprint->GetFPID().GetLibItemName();
  212. ref.Replace( wxT( " " ), wxT( "_" ) );
  213. val.Replace( wxT( " " ), wxT( "_" ) );
  214. pkg.Replace( wxT( " " ), wxT( "_" ) );
  215. snprintf( line, sizeof(line), "%-*s %-*s %-*s %9.4f %9.4f %8.4f %s\n",
  216. lenRefText, TO_UTF8( ref ),
  217. lenValText, TO_UTF8( val ),
  218. lenPkgText, TO_UTF8( pkg ),
  219. footprint_pos.x * conv_unit,
  220. // Keep the coordinates in the first quadrant,
  221. // (i.e. change y sign
  222. -footprint_pos.y * conv_unit,
  223. list[ii].m_Footprint->GetOrientation().AsDegrees(),
  224. (layer == F_Cu ) ? GetFrontSideName().c_str() : GetBackSideName().c_str() );
  225. buffer += line;
  226. }
  227. // Write EOF
  228. buffer += "## End\n";
  229. }
  230. return buffer;
  231. }
  232. std::string PLACE_FILE_EXPORTER::GenReportData()
  233. {
  234. std::string buffer;
  235. m_place_Offset = VECTOR2I( 0, 0 );
  236. // Select units:
  237. double conv_unit = m_unitsMM ? conv_unit_mm : conv_unit_inch;
  238. const char *unit_text = m_unitsMM ? unit_text_mm : unit_text_inch;
  239. LOCALE_IO toggle;
  240. // Generate header file comments.)
  241. char line[1024];
  242. snprintf( line, sizeof(line), "## Footprint report - date %s\n", TO_UTF8( GetISO8601CurrentDateTime() ) );
  243. buffer += line;
  244. wxString Title = GetBuildVersion();
  245. snprintf( line, sizeof(line), "## Created by KiCad version %s\n", TO_UTF8( Title ) );
  246. buffer += line;
  247. buffer += unit_text;
  248. buffer += "\n$BeginDESCRIPTION\n";
  249. BOX2I bbbox = m_board->ComputeBoundingBox( false, false );
  250. buffer += "\n$BOARD\n";
  251. snprintf( line, sizeof(line), "upper_left_corner %9.6f %9.6f\n",
  252. bbbox.GetX() * conv_unit, bbbox.GetY() * conv_unit );
  253. buffer += line;
  254. snprintf( line, sizeof(line), "lower_right_corner %9.6f %9.6f\n",
  255. bbbox.GetRight() * conv_unit, bbbox.GetBottom() * conv_unit );
  256. buffer += line;
  257. buffer += "$EndBOARD\n\n";
  258. std::vector<FOOTPRINT*> sortedFootprints;
  259. for( FOOTPRINT* footprint : m_board->Footprints() )
  260. sortedFootprints.push_back( footprint );
  261. std::sort( sortedFootprints.begin(), sortedFootprints.end(),
  262. []( FOOTPRINT* a, FOOTPRINT* b ) -> bool
  263. {
  264. return StrNumCmp( a->GetReference(), b->GetReference(), true ) < 0;
  265. });
  266. for( FOOTPRINT* footprint : sortedFootprints )
  267. {
  268. wxString ref = footprint->Reference().GetShownText( false );
  269. wxString value = footprint->Value().GetShownText( false );
  270. snprintf( line, sizeof(line), "$MODULE %s\n", TO_UTF8( ref ) );
  271. buffer += line;
  272. snprintf( line, sizeof(line), "reference %s\n", TO_UTF8( ref ) );
  273. snprintf( line, sizeof(line), "value %s\n", TO_UTF8( value ) );
  274. snprintf( line, sizeof(line), "footprint %s\n", footprint->GetFPID().Format().c_str() );
  275. buffer += line;
  276. buffer += "attribut";
  277. if(( footprint->GetAttributes() & ( FP_THROUGH_HOLE | FP_SMD ) ) == 0 )
  278. buffer += " virtual";
  279. if( footprint->GetAttributes() & FP_SMD )
  280. buffer += " smd";
  281. if( footprint->GetAttributes() & FP_THROUGH_HOLE )
  282. buffer += " none";
  283. buffer += "\n";
  284. VECTOR2I footprint_pos = footprint->GetPosition();
  285. footprint_pos -= m_place_Offset;
  286. snprintf( line, sizeof(line), "position %9.6f %9.6f orientation %.2f\n",
  287. footprint_pos.x * conv_unit,
  288. footprint_pos.y * conv_unit,
  289. footprint->GetOrientation().AsDegrees() );
  290. buffer += line;
  291. if( footprint->GetLayer() == F_Cu )
  292. buffer += "layer front\n";
  293. else if( footprint->GetLayer() == B_Cu )
  294. buffer += "layer back\n";
  295. else
  296. buffer += "layer other\n";
  297. std::vector<PAD*> sortedPads;
  298. for( PAD* pad : footprint->Pads() )
  299. sortedPads.push_back( pad );
  300. std::sort( sortedPads.begin(), sortedPads.end(),
  301. []( PAD* a, PAD* b ) -> bool
  302. {
  303. return StrNumCmp( a->GetNumber(), b->GetNumber(), true ) < 0;
  304. });
  305. for( PAD* pad : sortedPads )
  306. {
  307. snprintf( line, sizeof(line), "$PAD \"%s\"\n", TO_UTF8( pad->GetNumber() ) );
  308. buffer += line;
  309. int layer = 0;
  310. if( pad->GetLayerSet()[B_Cu] )
  311. layer = 1;
  312. if( pad->GetLayerSet()[F_Cu] )
  313. layer |= 2;
  314. static const char* layer_name[4] = { "nocopper", "back", "front", "both" };
  315. snprintf( line, sizeof(line), "Shape %s Layer %s\n",
  316. TO_UTF8( pad->ShowPadShape() ),
  317. layer_name[layer] );
  318. buffer += line;
  319. VECTOR2I padPos = pad->GetFPRelativePosition();
  320. snprintf( line, sizeof(line), "position %9.6f %9.6f size %9.6f %9.6f orientation %.2f\n",
  321. padPos.x * conv_unit,
  322. padPos.y * conv_unit,
  323. pad->GetSize().x * conv_unit,
  324. pad->GetSize().y * conv_unit,
  325. ( pad->GetOrientation() - footprint->GetOrientation() ).AsDegrees() );
  326. buffer += line;
  327. snprintf( line, sizeof(line), "drill %9.6f\n", pad->GetDrillSize().x * conv_unit );
  328. buffer += line;
  329. snprintf( line, sizeof(line), "shape_offset %9.6f %9.6f\n",
  330. pad->GetOffset().x * conv_unit,
  331. pad->GetOffset().y * conv_unit );
  332. buffer += line;
  333. buffer += "$EndPAD\n";
  334. }
  335. snprintf( line, sizeof(line), "$EndMODULE %s\n\n", TO_UTF8( ref ) );
  336. buffer += line;
  337. }
  338. // Generate EOF.
  339. buffer += "$EndDESCRIPTION\n";
  340. return buffer;
  341. }