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.

954 lines
30 KiB

17 years ago
17 years ago
16 years ago
16 years ago
16 years ago
16 years ago
17 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
18 years ago
16 years ago
16 years ago
16 years ago
16 years ago
18 years ago
18 years ago
16 years ago
18 years ago
16 years ago
16 years ago
16 years ago
18 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
18 years ago
18 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
16 years ago
17 years ago
16 years ago
17 years ago
16 years ago
16 years ago
17 years ago
16 years ago
17 years ago
16 years ago
16 years ago
17 years ago
17 years ago
16 years ago
16 years ago
18 years ago
16 years ago
18 years ago
16 years ago
16 years ago
16 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004 Jean-Pierre Charras, jaen-pierre.charras@gipsa-lab.inpg.com
  5. * Copyright (C) 2008-2011 Wayne Stambaugh <stambaughw@verizon.net>
  6. * Copyright (C) 2004-2011 KiCad Developers, see change_log.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. /**
  26. * @file export_gencad.cpp
  27. * @brief Export GenCAD 1.4 format.
  28. */
  29. #include "fctsys.h"
  30. #include "class_drawpanel.h"
  31. #include "confirm.h"
  32. #include "gestfich.h"
  33. #include "appl_wxstruct.h"
  34. #include "wxPcbStruct.h"
  35. #include "trigo.h"
  36. #include "build_version.h"
  37. #include "macros.h"
  38. #include "pcbnew.h"
  39. #include "class_board.h"
  40. #include "class_module.h"
  41. #include "class_track.h"
  42. #include "class_edge_mod.h"
  43. bool CreateHeaderInfoData( FILE* file, PCB_EDIT_FRAME* frame );
  44. static void CreateTracksInfoData( FILE* file, BOARD* pcb );
  45. static void CreateBoardSection( FILE* file, BOARD* pcb );
  46. static void CreateComponentsSection( FILE* file, BOARD* pcb );
  47. static void CreateDevicesSection( FILE* file, BOARD* pcb );
  48. static void CreateRoutesSection( FILE* file, BOARD* pcb );
  49. static void CreateSignalsSection( FILE* file, BOARD* pcb );
  50. static void CreateShapesSection( FILE* file, BOARD* pcb );
  51. static void CreatePadsShapesSection( FILE* file, BOARD* pcb );
  52. static void CreatePadsStacksSection( FILE* file, BOARD* pcb );
  53. static void FootprintWriteShape( FILE* File, MODULE* module );
  54. // layer name for Gencad export
  55. static const wxString GenCAD_Layer_Name[32] =
  56. {
  57. wxT( "BOTTOM" ), wxT( "INNER1" ), wxT( "INNER2" ),
  58. wxT( "INNER3" ), wxT( "INNER4" ), wxT( "INNER5" ),
  59. wxT( "INNER6" ), wxT( "INNER7" ), wxT( "INNER8" ),
  60. wxT( "INNER9" ), wxT( "INNER10" ), wxT( "INNER11" ),
  61. wxT( "INNER12" ), wxT( "INNER13" ), wxT( "INNER14" ),
  62. wxT( "TOP" ), wxT( "adhecu" ), wxT( "adhecmp" ),
  63. wxT( "SOLDERPASTE_BOTTOM" ), wxT( "SOLDERPASTE_TOP" ),
  64. wxT( "SILKSCREEN_BOTTOM" ), wxT( "SILKSCREEN_TOP" ),
  65. wxT( "SOLDERMASK_BOTTOM" ), wxT( "SOLDERMASK_TOP" ), wxT( "drawings" ),
  66. wxT( "comments" ), wxT( "eco1" ), wxT( "eco2" ),
  67. wxT( "edges" ), wxT( "--" ), wxT( "--" ),
  68. wxT( "--" )
  69. };
  70. int offsetX, offsetY;
  71. D_PAD* PadList;
  72. /* 2 helper functions to calculate coordinates of modules in gencad values (
  73. * GenCAD Y axis from bottom to top)
  74. */
  75. static int mapXto( int x )
  76. {
  77. return x - offsetX;
  78. }
  79. static int mapYto( int y )
  80. {
  81. return offsetY - y;
  82. }
  83. void PCB_EDIT_FRAME::ExportToGenCAD( wxCommandEvent& event )
  84. {
  85. wxFileName fn = GetScreen()->GetFileName();
  86. wxString msg, ext, wildcard;
  87. FILE* file;
  88. ext = wxT( "cad" );
  89. wildcard = _( "GenCAD 1.4 board files (.cad)|*.cad" );
  90. fn.SetExt( ext );
  91. wxFileDialog dlg( this, _( "Save GenCAD Board File" ), wxGetCwd(),
  92. fn.GetFullName(), wildcard,
  93. wxFD_SAVE | wxFD_OVERWRITE_PROMPT );
  94. if( dlg.ShowModal() == wxID_CANCEL )
  95. return;
  96. if( ( file = wxFopen( dlg.GetPath(), wxT( "wt" ) ) ) == NULL )
  97. {
  98. msg = _( "Unable to create " ) + dlg.GetPath();
  99. DisplayError( this, msg ); return;
  100. }
  101. /* Update some board data, to ensure a reliable gencad export: */
  102. GetBoard()->ComputeBoundingBox();
  103. offsetX = m_Auxiliary_Axis_Position.x;
  104. offsetY = m_Auxiliary_Axis_Position.y;
  105. Compile_Ratsnest( NULL, true );
  106. /* Temporary modification of footprints that are flipped (i.e. on bottom
  107. * layer) to convert them to non flipped footprints.
  108. * This is necessary to easily export shapes to GenCAD,
  109. * that are given as normal orientation (non flipped, rotation = 0))
  110. * these changes will be undone later
  111. */
  112. MODULE* module;
  113. for( module = GetBoard()->m_Modules; module != NULL; module = module->Next() )
  114. {
  115. module->flag = 0;
  116. if( module->GetLayer() == LAYER_N_BACK )
  117. {
  118. module->Flip( module->m_Pos );
  119. module->flag = 1;
  120. }
  121. }
  122. // Create file header:
  123. CreateHeaderInfoData( file, this );
  124. CreateBoardSection( file, GetBoard() );
  125. /* Create TRACKS list
  126. * This is the section $TRACK) (track width sizes) */
  127. CreateTracksInfoData( file, GetBoard() );
  128. /* Create the shapes list
  129. * (shapes of pads and footprints */
  130. CreatePadsShapesSection( file, GetBoard() ); /* Must be called
  131. * before
  132. * CreatePadsStacksSection
  133. * and
  134. * CreateShapesSection()
  135. */
  136. CreatePadsStacksSection( file, GetBoard() );
  137. CreateShapesSection( file, GetBoard() );
  138. CreateDevicesSection( file, GetBoard() );
  139. CreateComponentsSection( file, GetBoard() );
  140. /* Create the list of Nets: */
  141. CreateSignalsSection( file, GetBoard() );
  142. // Creates the Routes section (i.e. the list of board tracks)
  143. CreateRoutesSection( file, GetBoard() );
  144. fclose( file );
  145. /* Undo the footprints modifications (flipped footprints) */
  146. for( module = GetBoard()->m_Modules; module != NULL; module = module->Next() )
  147. {
  148. if( module->flag )
  149. {
  150. module->Flip( module->m_Pos );
  151. module->flag = 0;
  152. }
  153. }
  154. }
  155. static int Pad_list_Sort_by_Shapes( const void* refptr, const void* objptr )
  156. {
  157. const D_PAD* padref = *(D_PAD**) refptr;
  158. const D_PAD* padcmp = *(D_PAD**) objptr;
  159. return D_PAD::Compare( padref, padcmp );
  160. }
  161. /* Creates the pads shapes list ( 1 shape per pad )
  162. * Uses .GetSubRatsnest member of class D_PAD, to handle the shape id (value 1
  163. * ..n) for pads shapes PAD1 to PADn
  164. *
  165. * The PADS section is used to describe the shape of all the pads used on the
  166. * printed circuit board. The PADS section must be included, even if only a
  167. * default pad is described and used for all pads.
  168. * The keywords used in the PADS section are:
  169. * $PADS
  170. * PAD <pad_name> <pad_type> <drill_size>
  171. * LINE <line_ref>
  172. * ARC <arc_ref>
  173. * CIRCLE <circle_ref>
  174. * RECTANGLE <rectangle_ref>
  175. * ATTRIBUTE <attrib_ref>
  176. * $ENDPADS
  177. * $PADS and $ENDPADS mark the PADS section of the GenCAD file. Each pad
  178. * description must start with a PAD keyword.
  179. * The layer in which a pad lies is defined in the SHAPE section of the GenCAD
  180. * specification.
  181. * The pad is always placed on a shape at the pad origin, or in a pad stack at
  182. * the pad stack origin.
  183. */
  184. void CreatePadsShapesSection( FILE* file, BOARD* pcb )
  185. {
  186. std::vector<D_PAD*> pads;
  187. const char* pad_type;
  188. fputs( "$PADS\n", file );
  189. if( pcb->GetPadsCount() > 0 )
  190. {
  191. pads.insert( pads.end(),
  192. pcb->m_NetInfo->m_PadsFullList.begin(),
  193. pcb->m_NetInfo->m_PadsFullList.end() );
  194. qsort( &pads[0], pcb->GetPadsCount(), sizeof( D_PAD* ), Pad_list_Sort_by_Shapes );
  195. }
  196. D_PAD* old_pad = NULL;
  197. int pad_name_number = 0;
  198. for( unsigned i = 0; i<pads.size(); ++i )
  199. {
  200. D_PAD* pad = pads[i];
  201. pad->SetSubRatsnest( pad_name_number );
  202. if( old_pad && 0==D_PAD::Compare( old_pad, pad ) )
  203. continue; // already created
  204. old_pad = pad;
  205. pad_name_number++;
  206. pad->SetSubRatsnest( pad_name_number );
  207. fprintf( file, "PAD PAD%d", pad->GetSubRatsnest() );
  208. int dx = pad->m_Size.x / 2;
  209. int dy = pad->m_Size.y / 2;
  210. switch( pad->m_PadShape )
  211. {
  212. default:
  213. case PAD_CIRCLE:
  214. pad_type = "ROUND";
  215. fprintf( file, " %s %d\n", pad_type, pad->m_Drill.x );
  216. fprintf( file, "CIRCLE %d %d %d\n",
  217. pad->m_Offset.x, -pad->m_Offset.y, dx );
  218. break;
  219. case PAD_RECT:
  220. pad_type = "RECTANGULAR";
  221. fprintf( file, " %s %d\n", pad_type, pad->m_Drill.x );
  222. fprintf( file, "RECTANGLE %d %d %d %d\n",
  223. pad->m_Offset.x - dx, -pad->m_Offset.y - dy,
  224. pad->m_Size.x, pad->m_Size.y );
  225. break;
  226. case PAD_OVAL: /* Create outline by 2 lines and 2 arcs */
  227. {
  228. pad_type = "FINGER";
  229. fprintf( file, " %s %d\n", pad_type, pad->m_Drill.x );
  230. int dr = dx - dy;
  231. if( dr >= 0 ) // Horizontal oval
  232. {
  233. int radius = dy;
  234. fprintf( file, "LINE %d %d %d %d\n",
  235. -dr + pad->m_Offset.x, -pad->m_Offset.y - radius,
  236. dr + pad->m_Offset.x, -pad->m_Offset.y - radius );
  237. fprintf( file, "ARC %d %d %d %d %d %d\n",
  238. dr + pad->m_Offset.x, -pad->m_Offset.y - radius,
  239. dr + pad->m_Offset.x, -pad->m_Offset.y + radius,
  240. dr + pad->m_Offset.x, -pad->m_Offset.y );
  241. fprintf( file, "LINE %d %d %d %d\n",
  242. dr + pad->m_Offset.x, -pad->m_Offset.y + radius,
  243. -dr + pad->m_Offset.x, -pad->m_Offset.y + radius );
  244. fprintf( file, "ARC %d %d %d %d %d %d\n",
  245. -dr + pad->m_Offset.x, -pad->m_Offset.y + radius,
  246. -dr + pad->m_Offset.x, -pad->m_Offset.y - radius,
  247. -dr + pad->m_Offset.x, -pad->m_Offset.y );
  248. }
  249. else // Vertical oval
  250. {
  251. dr = -dr;
  252. int radius = dx;
  253. fprintf( file, "LINE %d %d %d %d\n",
  254. -radius + pad->m_Offset.x, -pad->m_Offset.y - dr,
  255. -radius + pad->m_Offset.x, -pad->m_Offset.y + dr );
  256. fprintf( file, "ARC %d %d %d %d %d %d\n",
  257. -radius + pad->m_Offset.x, -pad->m_Offset.y + dr,
  258. radius + pad->m_Offset.x, -pad->m_Offset.y + dr,
  259. pad->m_Offset.x, -pad->m_Offset.y + dr );
  260. fprintf( file, "LINE %d %d %d %d\n",
  261. radius + pad->m_Offset.x, -pad->m_Offset.y + dr,
  262. radius + pad->m_Offset.x, -pad->m_Offset.y - dr );
  263. fprintf( file, "ARC %d %d %d %d %d %d\n",
  264. radius + pad->m_Offset.x, -pad->m_Offset.y - dr,
  265. -radius + pad->m_Offset.x, -pad->m_Offset.y - dr,
  266. pad->m_Offset.x, -pad->m_Offset.y - dr );
  267. }
  268. break;
  269. }
  270. case PAD_TRAPEZOID:
  271. pad_type = "POLYGON";
  272. break;
  273. }
  274. }
  275. fputs( "$ENDPADS\n\n", file );
  276. }
  277. /*The PADSTACKS section is optional, and is used to describe how a group of
  278. * pads are
  279. * arranged. The keywords used in the PADSTACKS section are:
  280. * $PADSTACKS
  281. * PADSTACK <pad_name> <drill_size>
  282. * PAD <pad_name> <layer> <rot> <mirror>
  283. * ATTRIBUTE <attrib_ref>
  284. * $ENDPADSTACKS
  285. * $PADSTACKS and $ENDPADSTACKS mark the PADSTACKS section of the GenCAD file.
  286. */
  287. void CreatePadsStacksSection( FILE* file, BOARD* pcb )
  288. {
  289. fputs( "$PADSTACKS\n", file );
  290. fputs( "$ENDPADSTACKS\n\n", file );
  291. }
  292. /* Creates the footprint shape list.
  293. * We must use one shape for identical footprint (i.e. come from the same
  294. * footprint in lib)
  295. * But because pads shapes and positions can be easily modified on board,
  296. * a shape is created by footprint found.
  297. * (todo : compare footprints shapes and creates only one shape for all
  298. * footprints found having the same shape)
  299. * The shape is always given in orientation 0, position 0 not flipped
  300. *
  301. * Syntax:
  302. * $SHAPES
  303. * SHAPE <shape_name>
  304. * INSERT <string> here <string> = "TH"
  305. * shape_descr (line, arc ..)
  306. * PIN <pin_name> <pad_name> <x_y_ref> <layer> <rot> <mirror>
  307. *
  308. * SHAPE <shape_name>
  309. * ..
  310. * $ENDSHAPES
  311. */
  312. void CreateShapesSection( FILE* file, BOARD* pcb )
  313. {
  314. MODULE* module;
  315. D_PAD* pad;
  316. const char* layer;
  317. int orient;
  318. wxString pinname;
  319. const char* mirror = "0";
  320. fputs( "$SHAPES\n", file );
  321. for( module = pcb->m_Modules; module != NULL; module = module->Next() )
  322. {
  323. FootprintWriteShape( file, module );
  324. for( pad = module->m_Pads; pad != NULL; pad = pad->Next() )
  325. {
  326. layer = "ALL";
  327. if( ( pad->m_layerMask & ALL_CU_LAYERS ) == LAYER_BACK )
  328. {
  329. if( module->GetLayer() == LAYER_N_FRONT )
  330. layer = "BOTTOM";
  331. else
  332. layer = "TOP";
  333. }
  334. else if( ( pad->m_layerMask & ALL_CU_LAYERS ) == LAYER_FRONT )
  335. {
  336. if( module->GetLayer() == LAYER_N_FRONT )
  337. layer = "TOP";
  338. else
  339. layer = "BOTTOM";
  340. }
  341. pad->ReturnStringPadName( pinname );
  342. if( pinname.IsEmpty() )
  343. pinname = wxT( "noname" );
  344. orient = pad->m_Orient - module->m_Orient;
  345. NORMALIZE_ANGLE_POS( orient );
  346. fprintf( file, "PIN %s PAD%d %d %d %s %d %s",
  347. TO_UTF8( pinname ), pad->GetSubRatsnest(),
  348. pad->m_Pos0.x, -pad->m_Pos0.y,
  349. layer, orient / 10, mirror );
  350. if( orient % 10 )
  351. fprintf( file, " .%d", orient % 10 );
  352. fprintf( file, "\n" );
  353. }
  354. }
  355. fputs( "$ENDSHAPES\n\n", file );
  356. }
  357. /* Creates the section $COMPONENTS (Footprints placement)
  358. * When a footprint is on bottom side of the board::
  359. * shapes are given with option "FLIP" and "MIRRORX".
  360. * - But shapes remain given like component not mirrored and not flipped
  361. * - orientation is given like if where not mirrored and not flipped.
  362. */
  363. void CreateComponentsSection( FILE* file, BOARD* pcb )
  364. {
  365. MODULE* module = pcb->m_Modules;
  366. TEXTE_MODULE* PtTexte;
  367. const char* mirror;
  368. const char* flip;
  369. int ii;
  370. fputs( "$COMPONENTS\n", file );
  371. for( ; module != NULL; module = module->Next() )
  372. {
  373. int orient = module->m_Orient;
  374. if( module->flag )
  375. {
  376. mirror = "MIRRORX"; // Mirrored relative to X axis
  377. flip = "FLIP"; // Normal shape description ( gencad
  378. // viewer must show it flipped and
  379. // mirrored)
  380. NEGATE_AND_NORMALIZE_ANGLE_POS( orient );
  381. }
  382. else
  383. {
  384. mirror = "0";
  385. flip = "0";
  386. }
  387. fprintf( file, "COMPONENT %s\n", TO_UTF8( module->m_Reference->m_Text ) );
  388. fprintf( file, "DEVICE %s\n", TO_UTF8( module->m_Reference->m_Text ) );
  389. fprintf( file, "PLACE %d %d\n", mapXto( module->m_Pos.x ), mapYto( module->m_Pos.y ) );
  390. fprintf( file, "LAYER %s\n", (module->flag) ? "BOTTOM" : "TOP" );
  391. fprintf( file, "ROTATION %d", orient / 10 );
  392. if( orient % 10 )
  393. fprintf( file, ".%d", orient % 10 );
  394. fputs( "\n", file );
  395. fprintf( file, "SHAPE %s %s %s\n", TO_UTF8( module->m_Reference->m_Text ), mirror, flip );
  396. /* creates texts (ref and value) */
  397. PtTexte = module->m_Reference;
  398. for( ii = 0; ii < 2; ii++ )
  399. {
  400. int orient = PtTexte->m_Orient;
  401. wxString layer = GenCAD_Layer_Name[SILKSCREEN_N_FRONT];
  402. fprintf( file, "TEXT %d %d %d %d.%d %s %s \"%s\"",
  403. PtTexte->m_Pos0.x, -PtTexte->m_Pos0.y,
  404. PtTexte->m_Size.x,
  405. orient / 10, orient % 10,
  406. mirror,
  407. TO_UTF8( layer ),
  408. TO_UTF8( PtTexte->m_Text )
  409. );
  410. fprintf( file, " 0 0 %d %d\n",
  411. (int) ( PtTexte->m_Size.x * PtTexte->m_Text.Len() ),
  412. (int) PtTexte->m_Size.y );
  413. PtTexte = module->m_Value;
  414. }
  415. //put a comment:
  416. fprintf( file, "SHEET Part %s %s\n",
  417. TO_UTF8( module->m_Reference->m_Text ),
  418. TO_UTF8( module->m_Value->m_Text ) );
  419. }
  420. fputs( "$ENDCOMPONENTS\n\n", file );
  421. }
  422. /* Creates the list of Nets:
  423. * $SIGNALS
  424. * SIGNAL <net name>
  425. * NODE <component name> <pin name>
  426. * ...
  427. * NODE <component name> <pin name>
  428. * $ENDSIGNALS
  429. */
  430. void CreateSignalsSection( FILE* file, BOARD* pcb )
  431. {
  432. wxString msg;
  433. NETINFO_ITEM* net;
  434. D_PAD* pad;
  435. MODULE* module;
  436. int NbNoConn = 1;
  437. fputs( "$SIGNALS\n", file );
  438. for( unsigned ii = 0; ii < pcb->m_NetInfo->GetCount(); ii++ )
  439. {
  440. net = pcb->m_NetInfo->GetNetItem( ii );
  441. if( net->GetNetname() == wxEmptyString ) // dummy netlist (no connection)
  442. {
  443. wxString msg; msg << wxT( "NoConnection" ) << NbNoConn++;
  444. net->SetNetname( msg );
  445. }
  446. if( net->GetNet() <= 0 ) // dummy netlist (no connection)
  447. continue;
  448. msg = wxT( "SIGNAL " ) + net->GetNetname();
  449. fputs( TO_UTF8( msg ), file );
  450. fputs( "\n", file );
  451. for( module = pcb->m_Modules; module != NULL; module = module->Next() )
  452. {
  453. for( pad = module->m_Pads; pad != NULL; pad = pad->Next() )
  454. {
  455. wxString padname;
  456. if( pad->GetNet() != net->GetNet() )
  457. continue;
  458. pad->ReturnStringPadName( padname );
  459. msg.Printf( wxT( "NODE %s %.4s" ),
  460. GetChars( module->m_Reference->m_Text ),
  461. GetChars( padname ) );
  462. fputs( TO_UTF8( msg ), file );
  463. fputs( "\n", file );
  464. }
  465. }
  466. }
  467. fputs( "$ENDSIGNALS\n\n", file );
  468. }
  469. /* Creates the section $HEADER ... $ENDHEADER
  470. */
  471. bool CreateHeaderInfoData( FILE* file, PCB_EDIT_FRAME* frame )
  472. {
  473. wxString msg;
  474. PCB_SCREEN* screen = (PCB_SCREEN*) ( frame->GetScreen() );
  475. fputs( "$HEADER\n", file );
  476. fputs( "GENCAD 1.4\n", file );
  477. msg = wxT( "USER " ) + wxGetApp().GetAppName() + wxT( " " ) + GetBuildVersion();
  478. fputs( TO_UTF8( msg ), file ); fputs( "\n", file );
  479. msg = wxT( "DRAWING " ) + screen->GetFileName();
  480. fputs( TO_UTF8( msg ), file ); fputs( "\n", file );
  481. msg = wxT( "REVISION " ) + screen->m_Revision + wxT( " " ) + screen->m_Date;
  482. fputs( TO_UTF8( msg ), file ); fputs( "\n", file );
  483. msg.Printf( wxT( "UNITS USER %d" ), PCB_INTERNAL_UNIT );
  484. fputs( TO_UTF8( msg ), file ); fputs( "\n", file );
  485. msg.Printf( wxT( "ORIGIN %d %d" ),
  486. mapXto( frame->m_Auxiliary_Axis_Position.x ),
  487. mapYto( frame->m_Auxiliary_Axis_Position.y ) );
  488. fputs( TO_UTF8( msg ), file ); fputs( "\n", file );
  489. fputs( "INTERTRACK 0\n", file );
  490. fputs( "$ENDHEADER\n\n", file );
  491. return true;
  492. }
  493. /*
  494. * Sort function used to sort tracks segments:
  495. * items are sorted by netcode, then by width then by layer
  496. */
  497. static int Track_list_Sort_by_Netcode( const void* refptr, const void* objptr )
  498. {
  499. const TRACK* ref, * cmp;
  500. int diff;
  501. ref = *( (TRACK**) refptr );
  502. cmp = *( (TRACK**) objptr );
  503. if( ( diff = ref->GetNet() - cmp->GetNet() ) )
  504. return diff;
  505. if( ( diff = ref->m_Width - cmp->m_Width ) )
  506. return diff;
  507. if( ( diff = ref->GetLayer() - cmp->GetLayer() ) )
  508. return diff;
  509. return 0;
  510. }
  511. /* Creates the section ROUTES
  512. * that handles tracks, vias
  513. * TODO: add zones
  514. * section:
  515. * $ROUTE
  516. * ...
  517. * $ENROUTE
  518. * Track segments must be sorted by nets
  519. */
  520. void CreateRoutesSection( FILE* file, BOARD* pcb )
  521. {
  522. TRACK* track, ** tracklist;
  523. int vianum = 1;
  524. int old_netcode, old_width, old_layer;
  525. int nbitems, ii;
  526. // Count items
  527. nbitems = 0;
  528. for( track = pcb->m_Track; track != NULL; track = track->Next() )
  529. nbitems++;
  530. for( track = pcb->m_Zone; track != NULL; track = track->Next() )
  531. {
  532. if( track->Type() == PCB_ZONE_T )
  533. nbitems++;
  534. }
  535. tracklist = (TRACK**) operator new( (nbitems + 1) * sizeof( TRACK* ) );
  536. nbitems = 0;
  537. for( track = pcb->m_Track; track != NULL; track = track->Next() )
  538. tracklist[nbitems++] = track;
  539. for( track = pcb->m_Zone; track != NULL; track = track->Next() )
  540. {
  541. if( track->Type() == PCB_ZONE_T )
  542. tracklist[nbitems++] = track;
  543. }
  544. tracklist[nbitems] = NULL;
  545. qsort( tracklist, nbitems, sizeof(TRACK*), Track_list_Sort_by_Netcode );
  546. fputs( "$ROUTES\n", file );
  547. old_netcode = -1; old_width = -1; old_layer = -1;
  548. for( ii = 0; ii < nbitems; ii++ )
  549. {
  550. track = tracklist[ii];
  551. if( old_netcode != track->GetNet() )
  552. {
  553. old_netcode = track->GetNet();
  554. NETINFO_ITEM* net = pcb->FindNet( track->GetNet() );
  555. wxString netname;
  556. if( net && (net->GetNetname() != wxEmptyString) )
  557. netname = net->GetNetname();
  558. else
  559. netname = wxT( "_noname_" );
  560. fprintf( file, "ROUTE %s\n", TO_UTF8( netname ) );
  561. }
  562. if( old_width != track->m_Width )
  563. {
  564. old_width = track->m_Width;
  565. fprintf( file, "TRACK TRACK%d\n", track->m_Width );
  566. }
  567. if( (track->Type() == PCB_TRACE_T) || (track->Type() == PCB_ZONE_T) )
  568. {
  569. if( old_layer != track->GetLayer() )
  570. {
  571. old_layer = track->GetLayer();
  572. fprintf( file, "LAYER %s\n",
  573. TO_UTF8( GenCAD_Layer_Name[track->GetLayer() & 0x1F] ) );
  574. }
  575. fprintf( file, "LINE %d %d %d %d\n",
  576. mapXto( track->m_Start.x ), mapYto( track->m_Start.y ),
  577. mapXto( track->m_End.x ), mapYto( track->m_End.y ) );
  578. }
  579. if( track->Type() == PCB_VIA_T )
  580. {
  581. fprintf( file, "VIA viapad%d %d %d ALL %d via%d\n",
  582. track->m_Width,
  583. mapXto( track->m_Start.x ), mapYto( track->m_Start.y ),
  584. track->GetDrillValue(), vianum++ );
  585. }
  586. }
  587. fputs( "$ENDROUTES\n\n", file );
  588. delete tracklist;
  589. }
  590. /* Creates the section $DEVICES
  591. * This is a list of footprints properties
  592. * ( Shapes are in section $SHAPE )
  593. */
  594. void CreateDevicesSection( FILE* file, BOARD* pcb )
  595. {
  596. MODULE* module;
  597. D_PAD* pad;
  598. fputs( "$DEVICES\n", file );
  599. for( module = pcb->m_Modules; module != NULL; module = module->Next() )
  600. {
  601. fprintf( file, "DEVICE %s\n", TO_UTF8( module->m_Reference->m_Text ) );
  602. fprintf( file, "PART %s\n", TO_UTF8( module->m_LibRef ) );
  603. fprintf( file, "TYPE %s\n", "UNKNOWN" );
  604. for( pad = module->m_Pads; pad != NULL; pad = pad->Next() )
  605. {
  606. fprintf( file, "PINDESCR %.4s", pad->m_Padname );
  607. if( pad->GetNetname() == wxEmptyString )
  608. fputs( " NoConn\n", file );
  609. else
  610. fprintf( file, " %.4s\n", pad->m_Padname );
  611. }
  612. fprintf( file, "ATTRIBUTE %s\n", TO_UTF8( module->m_Value->m_Text ) );
  613. }
  614. fputs( "$ENDDEVICES\n\n", file );
  615. }
  616. /* Creates the section $BOARD.
  617. * We output here only the board boundary box
  618. */
  619. void CreateBoardSection( FILE* file, BOARD* pcb )
  620. {
  621. fputs( "$BOARD\n", file );
  622. fprintf( file, "LINE %d %d %d %d\n",
  623. mapXto( pcb->m_BoundaryBox.m_Pos.x ),
  624. mapYto( pcb->m_BoundaryBox.m_Pos.y ),
  625. mapXto( pcb->m_BoundaryBox.GetRight() ),
  626. mapYto( pcb->m_BoundaryBox.m_Pos.y ) );
  627. fprintf( file, "LINE %d %d %d %d\n",
  628. mapXto( pcb->m_BoundaryBox.GetRight() ),
  629. mapYto( pcb->m_BoundaryBox.m_Pos.y ),
  630. mapXto( pcb->m_BoundaryBox.GetRight() ),
  631. mapYto( pcb->m_BoundaryBox.GetBottom() ) );
  632. fprintf( file, "LINE %d %d %d %d\n",
  633. mapXto( pcb->m_BoundaryBox.GetRight() ),
  634. mapYto( pcb->m_BoundaryBox.GetBottom() ),
  635. mapXto( pcb->m_BoundaryBox.m_Pos.x ),
  636. mapYto( pcb->m_BoundaryBox.GetBottom() ) );
  637. fprintf( file, "LINE %d %d %d %d\n",
  638. mapXto( pcb->m_BoundaryBox.m_Pos.x ),
  639. mapYto( pcb->m_BoundaryBox.GetBottom() ),
  640. mapXto( pcb->m_BoundaryBox.m_Pos.x ),
  641. mapYto( pcb->m_BoundaryBox.m_Pos.y ) );
  642. fputs( "$ENDBOARD\n\n", file );
  643. }
  644. /* Creates the section "$TRACKS"
  645. * This sections give the list of widths (tools) used in tracks and vias
  646. * format:
  647. * $TRACK
  648. * TRACK <name> <width>
  649. * $ENDTRACK
  650. *
  651. * Each tool name is build like this: "TRACK" + track width.
  652. * For instance for a width = 120 : name = "TRACK120".
  653. */
  654. void CreateTracksInfoData( FILE* file, BOARD* pcb )
  655. {
  656. TRACK* track;
  657. int last_width = -1;
  658. /* Find thickness used for traces. */
  659. std::vector <int> trackinfo;
  660. unsigned ii;
  661. for( track = pcb->m_Track; track != NULL; track = track->Next() )
  662. {
  663. if( last_width != track->m_Width ) // Find a thickness already used.
  664. {
  665. for( ii = 0; ii < trackinfo.size(); ii++ )
  666. {
  667. if( trackinfo[ii] == track->m_Width )
  668. break;
  669. }
  670. if( ii == trackinfo.size() ) // not found
  671. trackinfo.push_back( track->m_Width );
  672. last_width = track->m_Width;
  673. }
  674. }
  675. for( track = pcb->m_Zone; track != NULL; track = track->Next() )
  676. {
  677. if( last_width != track->m_Width ) // Find a thickness already used.
  678. {
  679. for( ii = 0; ii < trackinfo.size(); ii++ )
  680. {
  681. if( trackinfo[ii] == track->m_Width )
  682. break;
  683. }
  684. if( ii == trackinfo.size() ) // not found
  685. trackinfo.push_back( track->m_Width );
  686. last_width = track->m_Width;
  687. }
  688. }
  689. // Write data
  690. fputs( "$TRACKS\n", file );
  691. for( ii = 0; ii < trackinfo.size(); ii++ )
  692. {
  693. fprintf( file, "TRACK TRACK%d %d\n", trackinfo[ii], trackinfo[ii] );
  694. }
  695. fputs( "$ENDTRACKS\n\n", file );
  696. }
  697. /* Creates the shape of a footprint (section SHAPE)
  698. * The shape is always given "normal" (Orient 0, not mirrored)
  699. * Syntax:
  700. * SHAPE <shape_name>
  701. * INSERT <string> here <string> = "TH"
  702. * shape_descr (line, arc ..):
  703. * LINE startX startY endX endY
  704. * ARC startX startY endX endY centreX centreY
  705. * PAD_CIRCLE centreX scentreY radius
  706. */
  707. void FootprintWriteShape( FILE* file, MODULE* module )
  708. {
  709. EDGE_MODULE* edge;
  710. EDA_ITEM* item;
  711. int y_axis_sign = -1; // Control Y axis change sign (as normal
  712. // module / mirror axis and conventions)
  713. /* creates header: */
  714. fprintf( file, "SHAPE %s\n", TO_UTF8( module->m_Reference->m_Text ) );
  715. fprintf( file, "INSERT %s\n", (module->m_Attributs & MOD_CMS) ? "SMD" : "TH" );
  716. /* creates Attributes */
  717. if( module->m_Attributs != MOD_DEFAULT )
  718. {
  719. fprintf( file, "ATTRIBUTE" );
  720. if( module->m_Attributs & MOD_CMS )
  721. fprintf( file, " PAD_SMD" );
  722. if( module->m_Attributs & MOD_VIRTUAL )
  723. fprintf( file, " VIRTUAL" );
  724. fprintf( file, "\n" );
  725. }
  726. /* creates Drawing */
  727. item = module->m_Drawings;
  728. for( ; item != NULL; item = item->Next() )
  729. {
  730. switch( item->Type() )
  731. {
  732. case PCB_MODULE_TEXT_T:
  733. break;
  734. case PCB_MODULE_EDGE_T:
  735. edge = (EDGE_MODULE*) item;
  736. switch( edge->m_Shape )
  737. {
  738. case S_SEGMENT:
  739. fprintf( file, "LINE %d %d %d %d\n",
  740. edge->m_Start0.x, y_axis_sign * edge->m_Start0.y,
  741. edge->m_End0.x, y_axis_sign * edge->m_End0.y );
  742. break;
  743. case S_CIRCLE:
  744. {
  745. int radius = (int) hypot( (double) ( edge->m_End0.x - edge->m_Start0.x ),
  746. (double) ( edge->m_End0.y - edge->m_Start0.y ) );
  747. fprintf( file, "CIRCLE %d %d %d\n",
  748. edge->m_Start0.x, y_axis_sign * edge->m_Start0.y, radius );
  749. break;
  750. }
  751. case S_ARC: /* print ARC x,y start x,y end x,y center */
  752. { // Arcs are defined counter clockwise (positive trigonometric)
  753. // from the start point to the end point (0 to 360 degrees)
  754. wxPoint arcStart, arcEnd;
  755. // edge->m_Start0 is the arc center relative to the shape position
  756. // edge->m_End0 is the arc start point relative to the shape position
  757. arcStart = edge->m_End0;
  758. // calculate arcEnd arc end point relative to the shape position, in Pcbnew
  759. // coordinates
  760. arcEnd = arcStart;
  761. RotatePoint( &arcEnd, edge->m_Start0, -edge->m_Angle );
  762. // due to difference between Pcbnew and gencad, swap arc start and arc end
  763. EXCHG(arcEnd, arcStart);
  764. // print arc shape:
  765. fprintf( file, "ARC %d %d %d %d %d %d\n",
  766. arcStart.x, y_axis_sign * arcStart.y, // Start point
  767. arcEnd.x, y_axis_sign * arcEnd.y, // End point
  768. edge->m_Start0.x, y_axis_sign * edge->m_Start0.y );
  769. break;
  770. }
  771. default:
  772. DisplayError( NULL, wxT( "Type Edge Module invalid." ) );
  773. break;
  774. } /* end switch PtEdge->m_Shape */
  775. break;
  776. default:
  777. break;
  778. } /* End switch Items type */
  779. }
  780. }