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.

2157 lines
70 KiB

18 years ago
18 years ago
18 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
13 years ago
14 years ago
13 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
18 years ago
++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
14 years ago
++PCBNew * Removed Pcb_Frame argument from BOARD() constructor, since it precludes having a BOARD being edited by more than one editor, it was a bad design. And this meant removing m_PcbFrame from BOARD. * removed BOARD::SetWindowFrame(), and BOARD::m_PcbFrame * Removed the global BOARD_DESIGN_SETTINGS which was in class_board.cpp * added BOARD_DESIGN_SETTINGS to the BOARD class, a full instance * a couple dialogs now only change BOARD_DESIGN_SETTINGS when OK is pressed, such as dialog_mask_clearance, dialog_drc, etc. * Removed common/pcbcommon.cpp's int g_CurrentVersionPCB = 1 and replaced it with build_version.h's #define BOARD_FILE_VERSION, although there may be a better place for this constant. * Made the public functions in PARAM_CFG_ARRAY be type const. void SaveParam(..) const and void ReadParam(..) const * PARAM_CFG_BASE now has virtual destructor since we have various way of destroying the derived class and boost::ptr_vector must be told about this. * Pass const PARAM_CFG_ARRAY& instead of PARAM_CFG_ARRAY so that we can use an automatic PARAM_CFG_ARRAY which is on the stack.\ * PCB_EDIT_FRAME::GetProjectFileParameters() may no longer cache the array, since it has to access the current BOARD and the BOARD can change. Remember BOARD_DESIGN_SETTINGS are now in the BOARD. * Made the m_BoundingBox member private, this was a brutally hard task, and indicative of the lack of commitment to accessors and object oriented design on the part of KiCad developers. We must do better. Added BOARD::GetBoundingBox, SetBoundingBox(), ComputeBoundingBox(). * Added PCB_BASE_FRAME::GetBoardBoundingBox() which calls BOARD::ComputeBoundingBox()
14 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
14 years ago
14 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2007-2008 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  5. * Copyright (C) 2007 KiCad Developers, see change_log.txt for contributors.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, you may find one here:
  19. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  20. * or you may search the http://www.gnu.org website for the version 2 license,
  21. * or you may write to the Free Software Foundation, Inc.,
  22. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  23. */
  24. /* This source is a complement to specctra.cpp and implements the export to
  25. specctra dsn file format. The specification for the grammar of the specctra
  26. dsn file used to develop this code is given here:
  27. http://tech.groups.yahoo.com/group/kicad-users/files/ then file "specctra.pdf"
  28. Also see the comments at the top of the specctra.cpp file itself.
  29. */
  30. #include <wxPcbStruct.h>
  31. #include <pcbstruct.h> // HISTORY_NUMBER
  32. #include <confirm.h> // DisplayError()
  33. #include <gestfich.h> // EDA_FileSelector()
  34. #include <trigo.h> // RotatePoint()
  35. #include <macros.h>
  36. #include <set> // std::set
  37. #include <map> // std::map
  38. #include <boost/utility.hpp> // boost::addressof()
  39. #include <class_board.h>
  40. #include <class_module.h>
  41. #include <class_edge_mod.h>
  42. #include <class_track.h>
  43. #include <class_zone.h>
  44. #include <class_drawsegment.h>
  45. #include <base_units.h>
  46. #include <collectors.h>
  47. #include <specctra.h>
  48. using namespace DSN;
  49. // Add .1 mil to the requested clearances as a safety margin.
  50. // There has been disagreement about interpretation of clearance in the past
  51. // between KiCad and Freerouter, so keep this safetyMargin until the
  52. // disagreement is resolved and stable. Freerouter seems to be moving
  53. // (protected) traces upon loading the DSN file, and even though it seems to sometimes
  54. // add its own 0.1 to the clearances, I believe this is happening after
  55. // the load process (and moving traces) so I am of the opinion this is
  56. // still needed.
  57. static const double safetyMargin = 0.1;
  58. /**
  59. * Function close_ness
  60. * is a non-exact distance calculator used to approximate the distance between
  61. * two points. The distance is very in-exact, but can be helpful when used
  62. * to pick between alternative neighboring points.
  63. * @param aLeft is the first point
  64. * @param aRight is the second point
  65. * @return unsigned - a measure of proximity that the caller knows about, in BIU,
  66. * but remember it is only an approximation.
  67. */
  68. static unsigned close_ness( const wxPoint& aLeft, const wxPoint& aRight )
  69. {
  70. // Don't need an accurate distance calculation, just something
  71. // approximating it, for relative orering.
  72. return unsigned( abs( aLeft.x - aRight.x ) + abs( aLeft.y - aRight.y ) );
  73. }
  74. /**
  75. * Function close_enough
  76. * is a local and tunable method of qualifying the proximity of two points.
  77. *
  78. * @param aLeft is the first point
  79. * @param aRight is the second point
  80. * @param aLimit is a measure of proximity that the caller knows about.
  81. * @return bool - true if the two points are close enough, else false.
  82. */
  83. inline bool close_enough( const wxPoint& aLeft, const wxPoint& aRight, unsigned aLimit )
  84. {
  85. // We don't use an accurate distance calculation, just something
  86. // approximating it, since aLimit is non-exact anyway except when zero.
  87. return close_ness( aLeft, aRight ) <= aLimit;
  88. }
  89. // see wxPcbStruct.h
  90. void PCB_EDIT_FRAME::ExportToSpecctra( wxCommandEvent& event )
  91. {
  92. wxString fullFileName = GetBoard()->GetFileName();
  93. wxString path;
  94. wxString name;
  95. wxString ext;
  96. wxString dsn_ext = wxT( ".dsn" );
  97. wxString mask = wxT( "*" ) + dsn_ext;
  98. wxFileName::SplitPath( fullFileName, &path, &name, &ext );
  99. name += dsn_ext;
  100. fullFileName = EDA_FileSelector( _( "Specctra DSN file:" ),
  101. path,
  102. name, // name.ext without path!
  103. dsn_ext,
  104. mask,
  105. this,
  106. wxFD_SAVE,
  107. false
  108. );
  109. if( fullFileName == wxEmptyString )
  110. return;
  111. SPECCTRA_DB db;
  112. bool ok = true;
  113. wxString errorText;
  114. BASE_SCREEN* screen = GetScreen();
  115. bool wasModified = screen->IsModify();
  116. db.SetPCB( SPECCTRA_DB::MakePCB() );
  117. LOCALE_IO toggle; // Switch the locale to standard C
  118. // DSN Images (=KiCad MODULES and pads) must be presented from the
  119. // top view. So we temporarily flip any modules which are on the back
  120. // side of the board to the front, and record this in the MODULE's flag field.
  121. db.FlipMODULEs( GetBoard() );
  122. try
  123. {
  124. GetBoard()->SynchronizeNetsAndNetClasses();
  125. db.FromBOARD( GetBoard() );
  126. db.ExportPCB( fullFileName, true );
  127. // if an exception is thrown by FromBOARD or ExportPCB(), then
  128. // ~SPECCTRA_DB() will close the file.
  129. }
  130. catch( IO_ERROR& ioe )
  131. {
  132. ok = false;
  133. // copy the error string to safe place, ioe is in this scope only.
  134. errorText = ioe.errorText;
  135. }
  136. // done assuredly, even if an exception was thrown and caught.
  137. db.RevertMODULEs( GetBoard() );
  138. // The two calls below to MODULE::Flip(), both set the
  139. // modified flag, yet their actions cancel each other out, so it should
  140. // be ok to clear the modify flag.
  141. if( !wasModified )
  142. screen->ClrModify();
  143. if( ok )
  144. {
  145. SetStatusText( wxString( _( "BOARD exported OK." ) ) );
  146. }
  147. else
  148. {
  149. errorText += '\n';
  150. errorText += _( "Unable to export, please fix and try again." );
  151. DisplayError( this, errorText );
  152. }
  153. }
  154. namespace DSN {
  155. const KICAD_T SPECCTRA_DB::scanPADs[] = { PCB_PAD_T, EOT };
  156. // "specctra reported units" are what we tell the external router that our
  157. // exported lengths are in.
  158. /**
  159. * Function scale
  160. * converts a distance from PCBNEW internal units to the reported specctra dsn units
  161. * in floating point format.
  162. */
  163. static inline double scale( int kicadDist )
  164. {
  165. // nanometers to um
  166. return kicadDist / ( IU_PER_MM / 1000.0 );
  167. }
  168. // / Convert integer internal units to float um
  169. static inline double IU2um( int kicadDist )
  170. {
  171. return kicadDist * (1000.0 / IU_PER_MM);
  172. }
  173. static inline double mapX( int x )
  174. {
  175. return scale( x );
  176. }
  177. static inline double mapY( int y )
  178. {
  179. return -scale( y ); // make y negative, since it is increasing going down.
  180. }
  181. /**
  182. * Function mapPt
  183. * converts a KiCad point into a DSN file point. Kicad's BOARD coordinates
  184. * are in deci-mils (i.e. 1/10,000th of an inch) and we are exporting in units
  185. * of mils, so we have to divide by 10.
  186. */
  187. static POINT mapPt( const wxPoint& pt )
  188. {
  189. POINT ret;
  190. ret.x = mapX( pt.x );
  191. ret.y = mapY( pt.y );
  192. ret.FixNegativeZero();
  193. return ret;
  194. }
  195. /**
  196. * Function findPoint
  197. * searches for a DRAWSEGMENT with an end point or start point of aPoint, and
  198. * if found, removes it from the TYPE_COLLECTOR and returns it, else returns NULL.
  199. * @param aPoint The starting or ending point to search for.
  200. * @param items The list to remove from.
  201. * @param aLimit is the distance from \a aPoint that still constitutes a valid find.
  202. * @return DRAWSEGMENT* - The first DRAWSEGMENT that has a start or end point matching
  203. * aPoint, otherwise NULL if none.
  204. */
  205. static DRAWSEGMENT* findPoint( const wxPoint& aPoint, TYPE_COLLECTOR* items, unsigned aLimit )
  206. {
  207. unsigned min_d = INT_MAX;
  208. int ndx_min = 0;
  209. // find the point closest to aPoint and perhaps exactly matching aPoint.
  210. for( int i = 0; i<items->GetCount(); ++i )
  211. {
  212. DRAWSEGMENT* graphic = (DRAWSEGMENT*) (*items)[i];
  213. unsigned d;
  214. wxASSERT( graphic->Type() == PCB_LINE_T );
  215. switch( graphic->GetShape() )
  216. {
  217. case S_ARC:
  218. if( aPoint == graphic->GetArcStart() || aPoint == graphic->GetArcEnd() )
  219. {
  220. items->Remove( i );
  221. return graphic;
  222. }
  223. d = close_ness( aPoint, graphic->GetArcStart() );
  224. if( d < min_d )
  225. {
  226. min_d = d;
  227. ndx_min = i;
  228. }
  229. d = close_ness( aPoint, graphic->GetArcEnd() );
  230. if( d < min_d )
  231. {
  232. min_d = d;
  233. ndx_min = i;
  234. }
  235. break;
  236. default:
  237. if( aPoint == graphic->GetStart() || aPoint == graphic->GetEnd() )
  238. {
  239. items->Remove( i );
  240. return graphic;
  241. }
  242. d = close_ness( aPoint, graphic->GetStart() );
  243. if( d < min_d )
  244. {
  245. min_d = d;
  246. ndx_min = i;
  247. }
  248. d = close_ness( aPoint, graphic->GetEnd() );
  249. if( d < min_d )
  250. {
  251. min_d = d;
  252. ndx_min = i;
  253. }
  254. }
  255. }
  256. if( min_d <= aLimit )
  257. {
  258. DRAWSEGMENT* graphic = (DRAWSEGMENT*) (*items)[ndx_min];
  259. items->Remove( ndx_min );
  260. return graphic;
  261. }
  262. #if defined(DEBUG)
  263. printf( "Unable to find segment matching point (%d,%d)\n",
  264. aPoint.x, aPoint.y );
  265. for( int i = 0; i< items->GetCount(); ++i )
  266. {
  267. DRAWSEGMENT* graphic = (DRAWSEGMENT*) (*items)[i];
  268. printf( "type=%s, GetStart()=%d,%d GetEnd()=%d,%d\n",
  269. TO_UTF8( BOARD_ITEM::ShowShape( (STROKE_T) graphic->GetShape() ) ),
  270. graphic->GetStart().x,
  271. graphic->GetStart().y,
  272. graphic->GetEnd().x,
  273. graphic->GetEnd().y );
  274. }
  275. #endif
  276. return NULL;
  277. }
  278. /**
  279. * Function isRoundKeepout
  280. * decides if the pad is a copper-less through hole which needs to be made into
  281. * a round keepout.
  282. */
  283. static bool isRoundKeepout( D_PAD* aPad )
  284. {
  285. if( aPad->GetShape()==PAD_CIRCLE )
  286. {
  287. if( aPad->GetDrillSize().x >= aPad->GetSize().x )
  288. return true;
  289. if( (aPad->GetLayerMask() & ALL_CU_LAYERS) == 0 )
  290. return true;
  291. }
  292. return false;
  293. }
  294. /**
  295. * Function makePath
  296. * creates a PATH element with a single straight line, a pair of vertices.
  297. */
  298. static PATH* makePath( const POINT& aStart, const POINT& aEnd, const std::string& aLayerName )
  299. {
  300. PATH* path = new PATH( 0, T_path );
  301. path->AppendPoint( aStart );
  302. path->AppendPoint( aEnd );
  303. path->SetLayerId( aLayerName.c_str() );
  304. return path;
  305. }
  306. PADSTACK* SPECCTRA_DB::makePADSTACK( BOARD* aBoard, D_PAD* aPad )
  307. {
  308. char name[256]; // padstack name builder
  309. std::string uniqifier;
  310. // caller must do these checks before calling here.
  311. wxASSERT( !isRoundKeepout( aPad ) );
  312. PADSTACK* padstack = new PADSTACK();
  313. int reportedLayers = 0; // how many in reported padstack
  314. const char* layerName[NB_COPPER_LAYERS];
  315. uniqifier = '[';
  316. bool onAllCopperLayers = ( (aPad->GetLayerMask() & ALL_CU_LAYERS) == ALL_CU_LAYERS );
  317. if( onAllCopperLayers )
  318. uniqifier += 'A'; // A for all layers
  319. const int copperCount = aBoard->GetCopperLayerCount();
  320. for( int layer=0; layer<copperCount; ++layer )
  321. {
  322. LAYER_NUM kilayer = pcbLayer2kicad[layer];
  323. if( onAllCopperLayers || aPad->IsOnLayer( kilayer ) )
  324. {
  325. layerName[reportedLayers++] = layerIds[layer].c_str();
  326. if( !onAllCopperLayers )
  327. {
  328. if( layer == 0 )
  329. uniqifier += 'T';
  330. else if( layer == copperCount - 1 )
  331. uniqifier += 'B';
  332. else
  333. uniqifier += char('0' + layer); // layer index char
  334. }
  335. }
  336. }
  337. uniqifier += ']';
  338. POINT dsnOffset;
  339. if( aPad->GetOffset().x || aPad->GetOffset().y )
  340. {
  341. char offsetTxt[64];
  342. wxPoint offset( aPad->GetOffset().x, aPad->GetOffset().y );
  343. dsnOffset = mapPt( offset );
  344. // using '(' or ')' would cause padstack name to be quote wrapped,
  345. // so use other brackets, and {} locks freerouter.
  346. sprintf( offsetTxt, "[%.6g,%.6g]", dsnOffset.x, dsnOffset.y );
  347. uniqifier += offsetTxt;
  348. }
  349. switch( aPad->GetShape() )
  350. {
  351. default:
  352. case PAD_CIRCLE:
  353. {
  354. double diameter = scale( aPad->GetSize().x );
  355. for( int ndx=0; ndx<reportedLayers; ++ndx )
  356. {
  357. SHAPE* shape = new SHAPE( padstack );
  358. padstack->Append( shape );
  359. CIRCLE* circle = new CIRCLE( shape );
  360. shape->SetShape( circle );
  361. circle->SetLayerId( layerName[ndx] );
  362. circle->SetDiameter( diameter );
  363. circle->SetVertex( dsnOffset );
  364. }
  365. snprintf( name, sizeof(name), "Round%sPad_%.6g_um",
  366. uniqifier.c_str(), IU2um( aPad->GetSize().x ) );
  367. name[ sizeof(name) - 1 ] = 0;
  368. padstack->SetPadstackId( name );
  369. }
  370. break;
  371. case PAD_RECT:
  372. {
  373. double dx = scale( aPad->GetSize().x ) / 2.0;
  374. double dy = scale( aPad->GetSize().y ) / 2.0;
  375. POINT lowerLeft( -dx, -dy );
  376. POINT upperRight( dx, dy );
  377. lowerLeft += dsnOffset;
  378. upperRight += dsnOffset;
  379. for( int ndx=0; ndx<reportedLayers; ++ndx )
  380. {
  381. SHAPE* shape = new SHAPE( padstack );
  382. padstack->Append( shape );
  383. RECTANGLE* rect = new RECTANGLE( shape );
  384. shape->SetShape( rect );
  385. rect->SetLayerId( layerName[ndx] );
  386. rect->SetCorners( lowerLeft, upperRight );
  387. }
  388. snprintf( name, sizeof(name), "Rect%sPad_%.6gx%.6g_um",
  389. uniqifier.c_str(),
  390. IU2um( aPad->GetSize().x ),
  391. IU2um( aPad->GetSize().y ) );
  392. name[ sizeof(name) - 1 ] = 0;
  393. padstack->SetPadstackId( name );
  394. }
  395. break;
  396. case PAD_OVAL:
  397. {
  398. double dx = scale( aPad->GetSize().x ) / 2.0;
  399. double dy = scale( aPad->GetSize().y ) / 2.0;
  400. double dr = dx - dy;
  401. double radius;
  402. POINT start;
  403. POINT stop;
  404. if( dr >= 0 ) // oval is horizontal
  405. {
  406. radius = dy;
  407. start = POINT( -dr, 0.0 );
  408. stop = POINT( dr, 0.0 );
  409. }
  410. else // oval is vertical
  411. {
  412. radius = dx;
  413. dr = -dr;
  414. start = POINT( 0.0, -dr );
  415. stop = POINT( 0.0, dr );
  416. }
  417. start += dsnOffset;
  418. stop += dsnOffset;
  419. for( int ndx=0; ndx<reportedLayers; ++ndx )
  420. {
  421. SHAPE* shape;
  422. PATH* path;
  423. // see http://www.freerouting.net/usren/viewtopic.php?f=3&t=317#p408
  424. shape = new SHAPE( padstack );
  425. padstack->Append( shape );
  426. path = makePath( start, stop, layerName[ndx] );
  427. shape->SetShape( path );
  428. path->aperture_width = 2.0 * radius;
  429. }
  430. snprintf( name, sizeof(name), "Oval%sPad_%.6gx%.6g_um",
  431. uniqifier.c_str(),
  432. IU2um( aPad->GetSize().x ),
  433. IU2um( aPad->GetSize().y ) );
  434. name[ sizeof(name) - 1 ] = 0;
  435. padstack->SetPadstackId( name );
  436. }
  437. break;
  438. case PAD_TRAPEZOID:
  439. {
  440. double dx = scale( aPad->GetSize().x ) / 2.0;
  441. double dy = scale( aPad->GetSize().y ) / 2.0;
  442. double ddx = scale( aPad->GetDelta().x ) / 2.0;
  443. double ddy = scale( aPad->GetDelta().y ) / 2.0;
  444. // see class_pad_draw_functions.cpp which draws the trapezoid pad
  445. POINT lowerLeft( -dx - ddy, -dy - ddx );
  446. POINT upperLeft( -dx + ddy, +dy + ddx );
  447. POINT upperRight( +dx - ddy, +dy - ddx );
  448. POINT lowerRight( +dx + ddy, -dy + ddx );
  449. lowerLeft += dsnOffset;
  450. upperLeft += dsnOffset;
  451. upperRight += dsnOffset;
  452. lowerRight += dsnOffset;
  453. for( int ndx=0; ndx<reportedLayers; ++ndx )
  454. {
  455. SHAPE* shape = new SHAPE( padstack );
  456. padstack->Append( shape );
  457. // a T_polygon exists as a PATH
  458. PATH* polygon = new PATH( shape, T_polygon );
  459. shape->SetShape( polygon );
  460. polygon->SetLayerId( layerName[ndx] );
  461. polygon->AppendPoint( lowerLeft );
  462. polygon->AppendPoint( upperLeft );
  463. polygon->AppendPoint( upperRight );
  464. polygon->AppendPoint( lowerRight );
  465. }
  466. // this string _must_ be unique for a given physical shape
  467. snprintf( name, sizeof(name), "Trapz%sPad_%.6gx%.6g_%c%.6gx%c%.6g_um",
  468. uniqifier.c_str(), IU2um( aPad->GetSize().x ), IU2um( aPad->GetSize().y ),
  469. aPad->GetDelta().x < 0 ? 'n' : 'p',
  470. std::abs( IU2um( aPad->GetDelta().x )),
  471. aPad->GetDelta().y < 0 ? 'n' : 'p',
  472. std::abs( IU2um( aPad->GetDelta().y ) )
  473. );
  474. name[ sizeof(name)-1 ] = 0;
  475. padstack->SetPadstackId( name );
  476. }
  477. break;
  478. }
  479. return padstack;
  480. }
  481. /// data type used to ensure unique-ness of pin names, holding (wxString and int)
  482. typedef std::map<wxString, int> PINMAP;
  483. IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, MODULE* aModule )
  484. {
  485. PINMAP pinmap;
  486. TYPE_COLLECTOR moduleItems;
  487. wxString padName;
  488. // get all the MODULE's pads.
  489. moduleItems.Collect( aModule, scanPADs );
  490. IMAGE* image = new IMAGE(0);
  491. image->image_id = aModule->GetFPID().Format().c_str();
  492. // from the pads, and make an IMAGE using collated padstacks.
  493. for( int p=0; p<moduleItems.GetCount(); ++p )
  494. {
  495. D_PAD* pad = (D_PAD*) moduleItems[p];
  496. // see if this pad is a through hole with no copper on its perimeter
  497. if( isRoundKeepout( pad ) )
  498. {
  499. double diameter = scale( pad->GetDrillSize().x );
  500. POINT vertex = mapPt( pad->GetPos0() );
  501. int layerCount = aBoard->GetCopperLayerCount();
  502. for( int layer=0; layer<layerCount; ++layer )
  503. {
  504. KEEPOUT* keepout = new KEEPOUT( image, T_keepout );
  505. image->keepouts.push_back( keepout );
  506. CIRCLE* circle = new CIRCLE( keepout );
  507. keepout->SetShape( circle );
  508. circle->SetDiameter( diameter );
  509. circle->SetVertex( vertex );
  510. circle->SetLayerId( layerIds[layer].c_str() );
  511. }
  512. }
  513. // else if() could there be a square keepout here?
  514. else
  515. {
  516. PADSTACK* padstack = makePADSTACK( aBoard, pad );
  517. PADSTACKSET::iterator iter = padstackset.find( *padstack );
  518. if( iter != padstackset.end() )
  519. {
  520. // padstack is a duplicate, delete it and use the original
  521. delete padstack;
  522. padstack = (PADSTACK*) *iter.base(); // folklore, be careful here
  523. }
  524. else
  525. {
  526. padstackset.insert( padstack );
  527. }
  528. PIN* pin = new PIN( image );
  529. padName = pad->GetPadName();
  530. pin->pin_id = TO_UTF8( padName );
  531. if( padName!=wxEmptyString && pinmap.find( padName )==pinmap.end() )
  532. {
  533. pinmap[ padName ] = 0;
  534. }
  535. else // pad name is a duplicate within this module
  536. {
  537. char buf[32];
  538. int duplicates = ++pinmap[ padName ];
  539. sprintf( buf, "@%d", duplicates );
  540. pin->pin_id += buf; // append "@1" or "@2", etc. to pin name
  541. }
  542. pin->kiNetCode = pad->GetNet();
  543. image->pins.push_back( pin );
  544. pin->padstack_id = padstack->padstack_id;
  545. int angle = pad->GetOrientation() - aModule->GetOrientation(); // tenths of degrees
  546. if( angle )
  547. {
  548. NORMALIZE_ANGLE_POS( angle );
  549. pin->SetRotation( angle / 10.0 );
  550. }
  551. wxPoint pos( pad->GetPos0() );
  552. pin->SetVertex( mapPt( pos ) );
  553. }
  554. }
  555. #if 1 // enable image (outline) scopes.
  556. static const KICAD_T scanEDGEs[] = { PCB_MODULE_EDGE_T, EOT };
  557. // get all the MODULE's EDGE_MODULEs and convert those to DSN outlines.
  558. moduleItems.Collect( aModule, scanEDGEs );
  559. for( int i = 0; i<moduleItems.GetCount(); ++i )
  560. {
  561. EDGE_MODULE* graphic = (EDGE_MODULE*) moduleItems[i];
  562. SHAPE* outline;
  563. PATH* path;
  564. switch( graphic->GetShape() )
  565. {
  566. case S_SEGMENT:
  567. outline = new SHAPE( image, T_outline );
  568. image->Append( outline );
  569. path = new PATH( outline );
  570. outline->SetShape( path );
  571. path->SetAperture( scale( graphic->GetWidth() ) );
  572. path->SetLayerId( "signal" );
  573. path->AppendPoint( mapPt( graphic->GetStart0() ) );
  574. path->AppendPoint( mapPt( graphic->GetEnd0() ) );
  575. break;
  576. case S_CIRCLE:
  577. {
  578. // this is best done by 4 QARC's but freerouter does not yet support QARCs.
  579. // for now, support by using line segments.
  580. outline = new SHAPE( image, T_outline );
  581. image->Append( outline );
  582. path = new PATH( outline );
  583. outline->SetShape( path );
  584. path->SetAperture( scale( graphic->GetWidth() ) );
  585. path->SetLayerId( "signal" );
  586. // Do the math using KiCad units, that way we stay out of the
  587. // scientific notation range of floating point numbers in the
  588. // DSN file. We do not parse scientific notation in our own
  589. // lexer/beautifier, and the spec is not clear that this is
  590. // required. Fixed point floats are all that should be needed.
  591. double radius = GetLineLength( graphic->GetStart(), graphic->GetEnd() );
  592. // better if evenly divisible into 360
  593. const int DEGREE_INTERVAL = 18; // 18 means 20 line segments
  594. for( double radians = 0.0;
  595. radians < 2 * M_PI;
  596. radians += DEGREE_INTERVAL * M_PI / 180.0 )
  597. {
  598. wxPoint point( KiROUND( radius * cos( radians ) ),
  599. KiROUND( radius * sin( radians ) ) );
  600. point += graphic->m_Start0; // an offset
  601. path->AppendPoint( mapPt( point ) );
  602. }
  603. }
  604. break;
  605. case S_RECT:
  606. case S_ARC:
  607. default:
  608. DBG( printf( "makeIMAGE(): unsupported shape %s\n",
  609. TO_UTF8( BOARD_ITEM::ShowShape( (STROKE_T) graphic->GetShape() ) ) ); )
  610. continue;
  611. }
  612. }
  613. #endif
  614. return image;
  615. }
  616. PADSTACK* SPECCTRA_DB::makeVia( int aCopperDiameter, int aDrillDiameter,
  617. int aTopLayer, int aBotLayer )
  618. {
  619. char name[48];
  620. PADSTACK* padstack = new PADSTACK();
  621. double dsnDiameter = scale( aCopperDiameter );
  622. for( int layer=aTopLayer; layer<=aBotLayer; ++layer )
  623. {
  624. SHAPE* shape = new SHAPE( padstack );
  625. padstack->Append( shape );
  626. CIRCLE* circle = new CIRCLE( shape );
  627. shape->SetShape( circle );
  628. circle->SetDiameter( dsnDiameter );
  629. circle->SetLayerId( layerIds[layer].c_str() );
  630. }
  631. snprintf( name, sizeof(name), "Via[%d-%d]_%.6g:%.6g_um",
  632. aTopLayer, aBotLayer, dsnDiameter,
  633. // encode the drill value into the name for later import
  634. IU2um( aDrillDiameter )
  635. );
  636. name[ sizeof(name) - 1 ] = 0;
  637. padstack->SetPadstackId( name );
  638. return padstack;
  639. }
  640. PADSTACK* SPECCTRA_DB::makeVia( const SEGVIA* aVia )
  641. {
  642. LAYER_NUM topLayerNum;
  643. LAYER_NUM botLayerNum;
  644. aVia->ReturnLayerPair( &topLayerNum, &botLayerNum );
  645. int topLayer = kicadLayer2pcb[topLayerNum];
  646. int botLayer = kicadLayer2pcb[botLayerNum];
  647. if( topLayer > botLayer )
  648. EXCHG( topLayer, botLayer );
  649. return makeVia( aVia->GetWidth(), aVia->GetDrillValue(), topLayer, botLayer );
  650. }
  651. /**
  652. * Function makeCircle
  653. * does a line segmented circle into aPath.
  654. */
  655. static void makeCircle( PATH* aPath, DRAWSEGMENT* aGraphic )
  656. {
  657. // do a circle segmentation
  658. const int STEPS = 2 * 36;
  659. wxPoint start;
  660. wxPoint center = aGraphic->GetCenter();
  661. int radius = aGraphic->GetRadius();
  662. double angle = 3600.0;
  663. start = center;
  664. start.x += radius;
  665. wxPoint nextPt;
  666. for( int step = 0; step<STEPS; ++step )
  667. {
  668. double rotation = ( angle * step ) / STEPS;
  669. nextPt = start;
  670. RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y, rotation );
  671. aPath->AppendPoint( mapPt( nextPt ) );
  672. }
  673. }
  674. void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary ) throw( IO_ERROR )
  675. {
  676. TYPE_COLLECTOR items;
  677. unsigned prox; // a proximity BIU metric, not an accurate distance
  678. const int STEPS = 36; // for a segmentation of an arc of 360 degrees
  679. // Get all the DRAWSEGMENTS and module graphics into 'items',
  680. // then keep only those on layer == EDGE_N.
  681. static const KICAD_T scan_graphics[] = { PCB_LINE_T, PCB_MODULE_EDGE_T, EOT };
  682. items.Collect( aBoard, scan_graphics );
  683. for( int i = 0; i<items.GetCount(); )
  684. {
  685. if( items[i]->GetLayer() != EDGE_N )
  686. {
  687. items.Remove( i );
  688. }
  689. else // remove graphics not on EDGE_N layer
  690. {
  691. DBG( items[i]->Show( 0, std::cout );)
  692. ++i;
  693. }
  694. }
  695. if( items.GetCount() )
  696. {
  697. PATH* path = new PATH( boundary );
  698. boundary->paths.push_back( path );
  699. path->layer_id = "pcb";
  700. wxPoint prevPt;
  701. DRAWSEGMENT* graphic;
  702. // Find edge point with minimum x, this should be in the outer polygon
  703. // which will define the perimeter Edge.Cuts polygon.
  704. wxPoint xmin = wxPoint( INT_MAX, 0 );
  705. int xmini = 0;
  706. for( int i = 0; i < items.GetCount(); i++ )
  707. {
  708. graphic = (DRAWSEGMENT*) items[i];
  709. switch( graphic->GetShape() )
  710. {
  711. case S_SEGMENT:
  712. {
  713. if( graphic->GetStart().x < xmin.x )
  714. {
  715. xmin = graphic->GetStart();
  716. xmini = i;
  717. }
  718. if( graphic->GetEnd().x < xmin.x )
  719. {
  720. xmin = graphic->GetEnd();
  721. xmini = i;
  722. }
  723. }
  724. break;
  725. case S_ARC:
  726. // freerouter does not yet understand arcs, so approximate
  727. // an arc with a series of short lines and put those
  728. // line segments into the !same! PATH.
  729. {
  730. wxPoint start = graphic->GetArcStart();
  731. wxPoint center = graphic->GetCenter();
  732. double angle = -graphic->GetAngle();
  733. int steps = STEPS * fabs(angle) /3600.0;
  734. if( steps == 0 )
  735. steps = 1;
  736. wxPoint pt;
  737. for( int step = 1; step<=steps; ++step )
  738. {
  739. double rotation = ( angle * step ) / steps;
  740. pt = start;
  741. RotatePoint( &pt.x, &pt.y, center.x, center.y, rotation );
  742. if( pt.x < xmin.x )
  743. {
  744. xmin = pt;
  745. xmini = i;
  746. }
  747. }
  748. }
  749. break;
  750. case S_CIRCLE:
  751. {
  752. wxPoint pt = graphic->GetCenter();
  753. // pt has minimum x point
  754. pt.x -= graphic->GetRadius();
  755. if( pt.x < xmin.x )
  756. {
  757. xmin = pt;
  758. xmini = i;
  759. }
  760. }
  761. break;
  762. default:
  763. {
  764. wxString error = wxString::Format( _( "Unsupported DRAWSEGMENT type %s" ),
  765. GetChars( BOARD_ITEM::ShowShape( (STROKE_T) graphic->GetShape() ) ) );
  766. ThrowIOError( error );
  767. }
  768. break;
  769. }
  770. }
  771. // Grab the left most point, assume its on the board's perimeter, and see if we
  772. // can put enough graphics together by matching endpoints to formulate a cohesive
  773. // polygon.
  774. graphic = (DRAWSEGMENT*) items[xmini];
  775. // The first DRAWSEGMENT is in 'graphic', ok to remove it from 'items'
  776. items.Remove( xmini );
  777. // Set maximum proximity threshold for point to point nearness metric for
  778. // board perimeter only, not interior keepouts yet.
  779. prox = Millimeter2iu( 0.002 ); // should be enough to fix rounding issues
  780. // is arc start and end point calculations
  781. // Output the Edge.Cuts perimeter as circle or polygon.
  782. if( graphic->GetShape() == S_CIRCLE )
  783. {
  784. makeCircle( path, graphic );
  785. }
  786. else
  787. {
  788. wxPoint startPt = wxPoint( graphic->GetEnd() );
  789. prevPt = graphic->GetEnd();
  790. path->AppendPoint( mapPt( prevPt ) );
  791. // Do not append the other end point yet of this 'graphic', this first
  792. // 'graphic' might be an arc.
  793. for(;;)
  794. {
  795. switch( graphic->GetShape() )
  796. {
  797. case S_SEGMENT:
  798. {
  799. wxPoint nextPt;
  800. if( !close_enough( prevPt, graphic->GetStart(), prox ) )
  801. {
  802. wxASSERT( close_enough( prevPt, graphic->GetEnd(), prox ) );
  803. nextPt = graphic->GetStart();
  804. }
  805. else
  806. {
  807. wxASSERT( close_enough( prevPt, graphic->GetStart(), prox ) );
  808. nextPt = graphic->GetEnd();
  809. }
  810. path->AppendPoint( mapPt( nextPt ) );
  811. prevPt = nextPt;
  812. }
  813. break;
  814. case S_ARC:
  815. // Freerouter does not yet understand arcs, so approximate
  816. // an arc with a series of short lines and put those
  817. // line segments into the !same! PATH.
  818. {
  819. wxPoint start = graphic->GetArcStart();
  820. wxPoint end = graphic->GetArcEnd();
  821. wxPoint center = graphic->GetCenter();
  822. double angle = -graphic->GetAngle();
  823. int steps = STEPS * fabs(angle) /3600.0;
  824. if( steps == 0 )
  825. steps = 1;
  826. if( !close_enough( prevPt, start, prox ) )
  827. {
  828. wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), prox ) );
  829. angle = -angle;
  830. EXCHG( start, end );
  831. }
  832. wxPoint nextPt;
  833. for( int step = 1; step<=steps; ++step )
  834. {
  835. double rotation = ( angle * step ) / steps;
  836. nextPt = start;
  837. RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y, rotation );
  838. path->AppendPoint( mapPt( nextPt ) );
  839. }
  840. prevPt = nextPt;
  841. }
  842. break;
  843. default:
  844. {
  845. wxString error = wxString::Format( _( "Unsupported DRAWSEGMENT type %s" ),
  846. GetChars( BOARD_ITEM::ShowShape( (STROKE_T) graphic->GetShape() ) ) );
  847. ThrowIOError( error );
  848. }
  849. break;
  850. }
  851. if( close_enough( startPt, prevPt, prox ) ) // the polygon is closed.
  852. break;
  853. graphic = findPoint( prevPt, &items, prox );
  854. if( !graphic )
  855. {
  856. wxString error = wxString::Format(
  857. _( "Unable to find the next segment with an endpoint of (%s mm, %s mm).\n"
  858. "Edit Edge.Cuts perimeter graphics, making them contiguous polygons each." ),
  859. GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.x ).c_str() ) ),
  860. GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.y ).c_str() ) )
  861. );
  862. ThrowIOError( error );
  863. }
  864. }
  865. }
  866. // Output the interior Edge.Cuts graphics as keepouts, using nearness metric
  867. // for sloppy graphical items.
  868. prox = Millimeter2iu( 0.25 );
  869. while( items.GetCount() )
  870. {
  871. // emit a signal layers keepout for every interior polygon left...
  872. KEEPOUT* keepout = new KEEPOUT( NULL, T_keepout );
  873. PATH* poly_ko = new PATH( NULL, T_polygon );
  874. keepout->SetShape( poly_ko );
  875. poly_ko->SetLayerId( "signal" );
  876. pcb->structure->keepouts.push_back( keepout );
  877. graphic = (DRAWSEGMENT*) items[0];
  878. items.Remove( 0 );
  879. if( graphic->GetShape() == S_CIRCLE )
  880. {
  881. makeCircle( poly_ko, graphic );
  882. }
  883. else
  884. {
  885. wxPoint startPt( graphic->GetEnd() );
  886. prevPt = graphic->GetEnd();
  887. poly_ko->AppendPoint( mapPt( prevPt ) );
  888. // do not append the other end point yet, this first 'graphic' might be an arc
  889. for(;;)
  890. {
  891. switch( graphic->GetShape() )
  892. {
  893. case S_SEGMENT:
  894. {
  895. wxPoint nextPt;
  896. if( !close_enough( prevPt, graphic->GetStart(), prox ) )
  897. {
  898. wxASSERT( close_enough( prevPt, graphic->GetEnd(), prox ) );
  899. nextPt = graphic->GetStart();
  900. }
  901. else
  902. {
  903. wxASSERT( close_enough( prevPt, graphic->GetStart(), prox ) );
  904. nextPt = graphic->GetEnd();
  905. }
  906. prevPt = nextPt;
  907. poly_ko->AppendPoint( mapPt( prevPt ) );
  908. }
  909. break;
  910. case S_ARC:
  911. // freerouter does not yet understand arcs, so approximate
  912. // an arc with a series of short lines and put those
  913. // line segments into the !same! PATH.
  914. {
  915. wxPoint start = graphic->GetArcStart();
  916. wxPoint end = graphic->GetArcEnd();
  917. wxPoint center = graphic->GetCenter();
  918. double angle = -graphic->GetAngle();
  919. int steps = STEPS * fabs(angle) /3600.0;
  920. if( steps == 0 )
  921. steps = 1;
  922. if( !close_enough( prevPt, start, prox ) )
  923. {
  924. wxASSERT( close_enough( prevPt, graphic->GetArcEnd(), prox ) );
  925. angle = -angle;
  926. EXCHG( start, end );
  927. }
  928. wxPoint nextPt;
  929. for( int step = 1; step<=steps; ++step )
  930. {
  931. double rotation = ( angle * step ) / steps;
  932. nextPt = start;
  933. RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y, rotation );
  934. poly_ko->AppendPoint( mapPt( nextPt ) );
  935. }
  936. prevPt = nextPt;
  937. }
  938. break;
  939. default:
  940. {
  941. wxString error = wxString::Format(
  942. _( "Unsupported DRAWSEGMENT type %s" ),
  943. GetChars( BOARD_ITEM::ShowShape( (STROKE_T) graphic->GetShape() ) ) );
  944. ThrowIOError( error );
  945. }
  946. break;
  947. }
  948. if( close_enough( startPt, prevPt, prox ) )
  949. break;
  950. graphic = findPoint( prevPt, &items, prox );
  951. if( !graphic )
  952. {
  953. wxString error = wxString::Format(
  954. _( "Unable to find the next segment with an endpoint of (%s mm, %s mm).\n"
  955. "Edit Edge.Cuts interior graphics, making them contiguous polygons each." ),
  956. GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.x ).c_str() ) ),
  957. GetChars( FROM_UTF8( BOARD_ITEM::FormatInternalUnits( prevPt.y ).c_str() ) )
  958. );
  959. ThrowIOError( error );
  960. }
  961. }
  962. }
  963. }
  964. }
  965. else
  966. {
  967. // User has not defined a board perimeter yet...
  968. EDA_RECT bbbox = aBoard->ComputeBoundingBox();
  969. RECTANGLE* rect = new RECTANGLE( boundary );
  970. boundary->rectangle = rect;
  971. rect->layer_id = "pcb";
  972. // opposite corners
  973. wxPoint bottomRight( bbbox.GetRight(), bbbox.GetBottom() );
  974. rect->SetCorners( mapPt( bbbox.GetOrigin() ),
  975. mapPt( bottomRight ) );
  976. }
  977. }
  978. /* This function is not used in SPECCTRA export,
  979. * but uses a lot of functions from it
  980. * and is used to extract a board outlines (3D view, automatic zones build ...)
  981. * makes the board perimeter for the DSN file by filling the BOUNDARY element.
  982. * Any closed outline inside the main outline is a hole
  983. * All contours should be closed, i.e. valid closed polygon vertices
  984. */
  985. bool SPECCTRA_DB::GetBoardPolygonOutlines( BOARD* aBoard,
  986. CPOLYGONS_LIST& aOutlines,
  987. CPOLYGONS_LIST& aHoles,
  988. wxString* aErrorText )
  989. {
  990. bool success = true;
  991. double specctra2UIfactor = IU_PER_MM / 1000.0; // Specctra unite = micron
  992. if( ! pcb )
  993. {
  994. pcb = new PCB();
  995. pcb->structure = new STRUCTURE( pcb );
  996. }
  997. CPolyPt corner;
  998. BOUNDARY* boundary = new BOUNDARY( 0 );
  999. pcb->structure->SetBOUNDARY( boundary );
  1000. try
  1001. {
  1002. fillBOUNDARY( aBoard, boundary );
  1003. std::vector<double> buffer;
  1004. boundary->GetCorners( buffer );
  1005. for( unsigned ii = 0; ii < buffer.size(); ii+=2 )
  1006. {
  1007. corner.x = buffer[ii] * specctra2UIfactor;
  1008. corner.y = - buffer[ii+1] * specctra2UIfactor;
  1009. aOutlines.Append( corner );
  1010. }
  1011. aOutlines.CloseLastContour();
  1012. // Export holes, stored as keepouts polygonal shapes.
  1013. // by fillBOUNDARY()
  1014. KEEPOUTS& holes = pcb->structure->keepouts;
  1015. for( KEEPOUTS::iterator i=holes.begin(); i!=holes.end(); ++i )
  1016. {
  1017. KEEPOUT& keepout = *i;
  1018. PATH* poly_hole = (PATH*)keepout.shape;
  1019. POINTS& plist = poly_hole->GetPoints();
  1020. for( unsigned ii = 0; ii < plist.size(); ii++ )
  1021. {
  1022. corner.x = plist[ii].x * specctra2UIfactor;
  1023. corner.y = - plist[ii].y * specctra2UIfactor;
  1024. aHoles.Append( corner );
  1025. }
  1026. aHoles.CloseLastContour();
  1027. }
  1028. }
  1029. catch( IO_ERROR ioe )
  1030. {
  1031. // Creates a valid polygon outline is not possible.
  1032. // So uses the board edge cuts bounding box to create a
  1033. // rectangular outline
  1034. // (when no edge cuts items, fillBOUNDARY biuld n outline
  1035. // from global bounding box
  1036. success = false;
  1037. if( aErrorText )
  1038. *aErrorText = ioe.errorText;
  1039. EDA_RECT bbbox = aBoard->ComputeBoundingBox( true );
  1040. corner.x = bbbox.GetOrigin().x;
  1041. corner.y = bbbox.GetOrigin().y;
  1042. aOutlines.Append( corner );
  1043. corner.x = bbbox.GetOrigin().x;
  1044. corner.y = bbbox.GetEnd().y;
  1045. aOutlines.Append( corner );
  1046. corner.x = bbbox.GetEnd().x;
  1047. corner.y = bbbox.GetEnd().y;
  1048. aOutlines.Append( corner );
  1049. corner.x = bbbox.GetEnd().x;
  1050. corner.y = bbbox.GetOrigin().y;
  1051. aOutlines.Append( corner );
  1052. aOutlines.CloseLastContour();
  1053. }
  1054. return success;
  1055. }
  1056. typedef std::set<std::string> STRINGSET;
  1057. typedef std::pair<STRINGSET::iterator, bool> STRINGSET_PAIR;
  1058. void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IO_ERROR )
  1059. {
  1060. TYPE_COLLECTOR items;
  1061. static const KICAD_T scanMODULEs[] = { PCB_MODULE_T, EOT };
  1062. // Not all boards are exportable. Check that all reference Ids are unique.
  1063. // Unless they are unique, we cannot import the session file which comes
  1064. // back to us later from the router.
  1065. {
  1066. TYPE_COLLECTOR padItems;
  1067. items.Collect( aBoard, scanMODULEs );
  1068. STRINGSET refs; // holds module reference designators
  1069. for( int i=0; i<items.GetCount(); ++i )
  1070. {
  1071. MODULE* module = (MODULE*) items[i];
  1072. if( module->GetReference() == wxEmptyString )
  1073. {
  1074. ThrowIOError( _( "Component with value of '%s' has empty reference id." ),
  1075. GetChars( module->GetValue() ) );
  1076. }
  1077. // if we cannot insert OK, that means the reference has been seen before.
  1078. STRINGSET_PAIR refpair = refs.insert( TO_UTF8( module->GetReference() ) );
  1079. if( !refpair.second ) // insert failed
  1080. {
  1081. ThrowIOError( _( "Multiple components have identical reference IDs of '%s'." ),
  1082. GetChars( module->GetReference() ) );
  1083. }
  1084. }
  1085. }
  1086. if( !pcb )
  1087. pcb = SPECCTRA_DB::MakePCB();
  1088. //-----<layer_descriptor>-----------------------------------------------
  1089. {
  1090. // specctra wants top physical layer first, then going down to the
  1091. // bottom most physical layer in physical sequence.
  1092. // @question : why does KiCad not display layers in that order?
  1093. buildLayerMaps( aBoard );
  1094. int layerCount = aBoard->GetCopperLayerCount();
  1095. for( int pcbNdx=0; pcbNdx<layerCount; ++pcbNdx )
  1096. {
  1097. LAYER* layer = new LAYER( pcb->structure );
  1098. pcb->structure->layers.push_back( layer );
  1099. layer->name = layerIds[pcbNdx];
  1100. DSN_T layerType;
  1101. switch( aBoard->GetLayerType( pcbLayer2kicad[pcbNdx] ) )
  1102. {
  1103. default:
  1104. case LT_SIGNAL: layerType = T_signal; break;
  1105. case LT_POWER: layerType = T_power; break;
  1106. #if 1 // Freerouter does not support type "mixed", only signal and power.
  1107. // Remap "mixed" to "signal".
  1108. case LT_MIXED: layerType = T_signal; break;
  1109. #else
  1110. case LT_MIXED: layerType = T_mixed; break;
  1111. #endif
  1112. case LT_JUMPER: layerType = T_jumper; break;
  1113. }
  1114. layer->layer_type = layerType;
  1115. layer->properties.push_back( PROPERTY() );
  1116. PROPERTY* property = &layer->properties.back();
  1117. property->name = "index";
  1118. char temp[32];
  1119. sprintf( temp, "%d", pcbNdx );
  1120. property->value = temp;
  1121. }
  1122. }
  1123. // a space in a quoted token is NOT a terminator, true establishes this.
  1124. pcb->parser->space_in_quoted_tokens = true;
  1125. //-----<unit_descriptor> & <resolution_descriptor>--------------------
  1126. {
  1127. // tell freerouter to use "tenths of micrometers",
  1128. // which is 100 nm resolution. Possibly more resolution is possible
  1129. // in freerouter, but it would need testing.
  1130. pcb->unit->units = T_um;
  1131. pcb->resolution->units = T_um;
  1132. pcb->resolution->value = 10; // tenths of a um
  1133. // pcb->resolution->value = 1000; // "thousandths of a um" (i.e. "nm")
  1134. }
  1135. //-----<boundary_descriptor>------------------------------------------
  1136. {
  1137. // Because fillBOUNDARY() can throw an exception, we link in an
  1138. // empty boundary so the BOUNDARY does not get lost in the event of
  1139. // of an exception.
  1140. BOUNDARY* boundary = new BOUNDARY( 0 );
  1141. pcb->structure->SetBOUNDARY( boundary );
  1142. fillBOUNDARY( aBoard, boundary );
  1143. }
  1144. //-----<rules>--------------------------------------------------------
  1145. {
  1146. char rule[80];
  1147. int defaultTrackWidth = aBoard->m_NetClasses.GetDefault()->GetTrackWidth();
  1148. int defaultClearance = aBoard->m_NetClasses.GetDefault()->GetClearance();
  1149. double clearance = scale( defaultClearance );
  1150. STRINGS& rules = pcb->structure->rules->rules;
  1151. sprintf( rule, "(width %.6g)", scale( defaultTrackWidth ) );
  1152. rules.push_back( rule );
  1153. sprintf( rule, "(clearance %.6g)", clearance + safetyMargin );
  1154. rules.push_back( rule );
  1155. // On a high density board (a board with 4 mil tracks, 4 mil spacing)
  1156. // a typical solder mask clearance will be 2-3 mils.
  1157. // This exposes 2 to 3 mils of bare board around each pad, and would
  1158. // leave only 1 to 2 mils of solder mask between the solder mask's boundary
  1159. // to the edge of any trace within "clearance" of the pad. So we need at least
  1160. // 2 mils *extra* clearance for traces which would come near a pad on
  1161. // a different net. So if the baseline trace to trace clearance was say 4 mils, then
  1162. // the SMD to trace clearance should be at least 6 mils.
  1163. double default_smd = clearance + safetyMargin;
  1164. if( default_smd <= 6.0 )
  1165. default_smd = 6.0;
  1166. sprintf( rule, "(clearance %.6g (type default_smd))", default_smd );
  1167. rules.push_back( rule );
  1168. /* see: http://www.freerouting.net/usren/viewtopic.php?f=5&t=339#p474
  1169. sprintf( rule, "(clearance %.6g (type pad_to_turn_gap))", clearance + safetyMargin );
  1170. rules.push_back( rule );
  1171. sprintf( rule, "(clearance %.6g (type smd_to_turn_gap))", clearance + safetyMargin );
  1172. rules.push_back( rule );
  1173. sprintf( rule, "(clearance %.6g (type via_via))", clearance + safetyMargin );
  1174. rules.push_back( rule );
  1175. sprintf( rule, "(clearance %.6g (type via_smd))", clearance + safetyMargin );
  1176. rules.push_back( rule );
  1177. sprintf( rule, "(clearance %.6g (type via_pin))", clearance + safetyMargin );
  1178. rules.push_back( rule );
  1179. sprintf( rule, "(clearance %.6g (type pin_pin))", clearance + safetyMargin );
  1180. rules.push_back( rule );
  1181. sprintf( rule, "(clearance %.6g (type smd_pin))", clearance + safetyMargin );
  1182. rules.push_back( rule );
  1183. */
  1184. // Pad to pad spacing on a single SMT part can be closer than our
  1185. // clearance, we don't want freerouter complaining about that, so
  1186. // output a significantly smaller pad to pad clearance to freerouter.
  1187. clearance = scale( defaultClearance ) / 4;
  1188. sprintf( rule, "(clearance %.6g (type smd_smd))", clearance );
  1189. rules.push_back( rule );
  1190. }
  1191. //-----<zone containers (not keepout areas) become planes>--------------------------------
  1192. // Note: only zones are output here, keepout areas be be created later
  1193. {
  1194. int netlessZones = 0;
  1195. static const KICAD_T scanZONEs[] = { PCB_ZONE_AREA_T, EOT };
  1196. items.Collect( aBoard, scanZONEs );
  1197. for( int i = 0; i<items.GetCount(); ++i )
  1198. {
  1199. ZONE_CONTAINER* item = (ZONE_CONTAINER*) items[i];
  1200. if( item->GetIsKeepout() )
  1201. continue;
  1202. COPPER_PLANE* plane = new COPPER_PLANE( pcb->structure );
  1203. pcb->structure->planes.push_back( plane );
  1204. PATH* mainPolygon = new PATH( plane, T_polygon );
  1205. plane->SetShape( mainPolygon );
  1206. plane->name = TO_UTF8( item->GetNetName() );
  1207. if( plane->name.size() == 0 )
  1208. {
  1209. char name[32];
  1210. // This is one of those no connection zones, netcode=0, and it has no name.
  1211. // Create a unique, bogus netname.
  1212. NET* no_net = new NET( pcb->network );
  1213. sprintf( name, "@:no_net_%d", netlessZones++ );
  1214. no_net->net_id = name;
  1215. // add the bogus net name to network->nets.
  1216. pcb->network->nets.push_back( no_net );
  1217. // use the bogus net name in the netless zone.
  1218. plane->name = no_net->net_id;
  1219. }
  1220. mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
  1221. int count = item->Outline()->m_CornersList.GetCornersCount();
  1222. int ndx = 0; // used in 2 for() loops below
  1223. for( ; ndx<count; ++ndx )
  1224. {
  1225. wxPoint point( item->Outline()->m_CornersList[ndx].x,
  1226. item->Outline()->m_CornersList[ndx].y );
  1227. mainPolygon->AppendPoint( mapPt(point) );
  1228. // this was the end of the main polygon
  1229. if( item->Outline()->m_CornersList[ndx].end_contour )
  1230. break;
  1231. }
  1232. WINDOW* window = 0;
  1233. PATH* cutout = 0;
  1234. // handle the cutouts
  1235. for( ++ndx; ndx<count; ++ndx )
  1236. {
  1237. if( item->Outline()->m_CornersList[ndx-1].end_contour )
  1238. {
  1239. window = new WINDOW( plane );
  1240. plane->AddWindow( window );
  1241. cutout = new PATH( window, T_polygon );
  1242. window->SetShape( cutout );
  1243. cutout->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
  1244. }
  1245. wxASSERT( window );
  1246. wxASSERT( cutout );
  1247. wxPoint point(item->Outline()->m_CornersList[ndx].x,
  1248. item->Outline()->m_CornersList[ndx].y );
  1249. cutout->AppendPoint( mapPt(point) );
  1250. }
  1251. }
  1252. }
  1253. //-----<zone containers flagged keepout areas become keepout>--------------------------------
  1254. {
  1255. static const KICAD_T scanZONEs[] = { PCB_ZONE_AREA_T, EOT };
  1256. items.Collect( aBoard, scanZONEs );
  1257. for( int i=0; i<items.GetCount(); ++i )
  1258. {
  1259. ZONE_CONTAINER* item = (ZONE_CONTAINER*) items[i];
  1260. if( ! item->GetIsKeepout() )
  1261. continue;
  1262. // keepout areas have a type. types are
  1263. // T_place_keepout, T_via_keepout, T_wire_keepout,
  1264. // T_bend_keepout, T_elongate_keepout, T_keepout.
  1265. // Pcbnew knows only T_keepout, T_via_keepout and T_wire_keepout
  1266. DSN_T keepout_type;
  1267. if( item->GetDoNotAllowVias() && item->GetDoNotAllowTracks() )
  1268. keepout_type = T_keepout;
  1269. else if( item->GetDoNotAllowVias() )
  1270. keepout_type = T_via_keepout;
  1271. else if( item->GetDoNotAllowTracks() )
  1272. keepout_type = T_wire_keepout;
  1273. else
  1274. keepout_type = T_keepout;
  1275. KEEPOUT* keepout = new KEEPOUT( pcb->structure, keepout_type );
  1276. pcb->structure->keepouts.push_back( keepout );
  1277. PATH* mainPolygon = new PATH( keepout, T_polygon );
  1278. keepout->SetShape( mainPolygon );
  1279. mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
  1280. int count = item->Outline()->m_CornersList.GetCornersCount();
  1281. int ndx = 0; // used in 2 for() loops below
  1282. for( ; ndx<count; ++ndx )
  1283. {
  1284. wxPoint point( item->Outline()->m_CornersList[ndx].x,
  1285. item->Outline()->m_CornersList[ndx].y );
  1286. mainPolygon->AppendPoint( mapPt(point) );
  1287. // this was the end of the main polygon
  1288. if( item->Outline()->m_CornersList[ndx].end_contour )
  1289. break;
  1290. }
  1291. WINDOW* window = 0;
  1292. PATH* cutout = 0;
  1293. // handle the cutouts
  1294. for( ++ndx; ndx<count; ++ndx )
  1295. {
  1296. if( item->Outline()->m_CornersList[ndx-1].end_contour )
  1297. {
  1298. window = new WINDOW( keepout );
  1299. keepout->AddWindow( window );
  1300. cutout = new PATH( window, T_polygon );
  1301. window->SetShape( cutout );
  1302. cutout->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
  1303. }
  1304. wxASSERT( window );
  1305. wxASSERT( cutout );
  1306. wxPoint point(item->Outline()->m_CornersList[ndx].x,
  1307. item->Outline()->m_CornersList[ndx].y );
  1308. cutout->AppendPoint( mapPt(point) );
  1309. }
  1310. }
  1311. }
  1312. //-----<build the images, components, and netlist>-----------------------
  1313. {
  1314. PIN_REF empty( pcb->network );
  1315. std::string componentId;
  1316. // find the highest numbered netCode within the board.
  1317. int highestNetCode = aBoard->GetNetCount() - 1;
  1318. deleteNETs();
  1319. // expand the net vector to highestNetCode+1, setting empty to NULL
  1320. nets.resize( highestNetCode + 1, NULL );
  1321. // skip netcode = 0
  1322. for( unsigned i = 1; i<nets.size(); ++i )
  1323. nets[i] = new NET( pcb->network );
  1324. for( unsigned ii = 0; ii < aBoard->GetNetCount(); ii++ )
  1325. {
  1326. NETINFO_ITEM* net = aBoard->FindNet( ii );
  1327. int netcode = net->GetNet();
  1328. if( netcode > 0 )
  1329. nets[ netcode ]->net_id = TO_UTF8( net->GetNetname() );
  1330. }
  1331. items.Collect( aBoard, scanMODULEs );
  1332. padstackset.clear();
  1333. for( int m = 0; m<items.GetCount(); ++m )
  1334. {
  1335. MODULE* module = (MODULE*) items[m];
  1336. IMAGE* image = makeIMAGE( aBoard, module );
  1337. componentId = TO_UTF8( module->GetReference() );
  1338. // create a net list entry for all the actual pins in the image
  1339. // for the current module. location of this code is critical
  1340. // because we fabricated some pin names to ensure unique-ness
  1341. // of pin names within a module, do not move this code because
  1342. // the life of this 'IMAGE* image' is not necessarily long. The
  1343. // exported netlist will have some fabricated pin names in it.
  1344. // If you don't like fabricated pin names, then make sure all pads
  1345. // within your MODULEs are uniquely named!
  1346. for( unsigned p = 0; p<image->pins.size(); ++p )
  1347. {
  1348. PIN* pin = &image->pins[p];
  1349. int netcode = pin->kiNetCode;
  1350. if( netcode > 0 )
  1351. {
  1352. NET* net = nets[netcode];
  1353. net->pins.push_back( empty );
  1354. PIN_REF& pin_ref = net->pins.back();
  1355. pin_ref.component_id = componentId;
  1356. pin_ref.pin_id = pin->pin_id;
  1357. }
  1358. }
  1359. IMAGE* registered = pcb->library->LookupIMAGE( image );
  1360. if( registered != image )
  1361. {
  1362. // If our new 'image' is not a unique IMAGE, delete it.
  1363. // and use the registered one, known as 'image' after this.
  1364. delete image;
  1365. image = registered;
  1366. }
  1367. COMPONENT* comp = pcb->placement->LookupCOMPONENT( image->GetImageId() );
  1368. PLACE* place = new PLACE( comp );
  1369. comp->places.push_back( place );
  1370. place->SetRotation( module->GetOrientation()/10.0 );
  1371. place->SetVertex( mapPt( module->GetPosition() ) );
  1372. place->component_id = componentId;
  1373. place->part_number = TO_UTF8( module->GetValue() );
  1374. // module is flipped from bottom side, set side to T_back
  1375. if( module->GetFlag() )
  1376. {
  1377. int angle = 1800 - module->GetOrientation();
  1378. NORMALIZE_ANGLE_POS( angle );
  1379. place->SetRotation( angle / 10.0 );
  1380. place->side = T_back;
  1381. }
  1382. }
  1383. // copy the SPECCTRA_DB::padstackset to the LIBRARY. Since we are
  1384. // removing, do not increment the iterator
  1385. for( PADSTACKSET::iterator i = padstackset.begin(); i!=padstackset.end();
  1386. i = padstackset.begin() )
  1387. {
  1388. PADSTACKSET::auto_type ps = padstackset.release( i );
  1389. PADSTACK* padstack = ps.release();
  1390. pcb->library->AddPadstack( padstack );
  1391. }
  1392. // copy our SPECCTRA_DB::nets to the pcb->network
  1393. for( unsigned n = 1; n<nets.size(); ++n )
  1394. {
  1395. NET* net = nets[n];
  1396. if( net->pins.size() )
  1397. {
  1398. // give ownership to pcb->network
  1399. pcb->network->nets.push_back( net );
  1400. nets[n] = 0;
  1401. }
  1402. }
  1403. }
  1404. //-----< output vias used in netclasses >-----------------------------------
  1405. {
  1406. NETCLASSES& nclasses = aBoard->m_NetClasses;
  1407. // Assume the netclass vias are all the same kind of thru, blind, or buried vias.
  1408. // This is in lieu of either having each netclass via have its own layer pair in
  1409. // the netclass dialog, or such control in the specctra export dialog.
  1410. // if( aBoard->GetDesignSettings().m_CurrentViaType == VIA_THROUGH )
  1411. {
  1412. m_top_via_layer = 0; // first specctra cu layer is number zero.
  1413. m_bot_via_layer = aBoard->GetCopperLayerCount()-1;
  1414. }
  1415. /*
  1416. else
  1417. {
  1418. // again, should be in the BOARD:
  1419. topLayer = kicadLayer2pcb[ GetScreen()->m_Route_Layer_TOP ];
  1420. botLayer = kicadLayer2pcb[ GetScreen()->m_Route_Layer_BOTTOM ];
  1421. }
  1422. */
  1423. // Add the via from the Default netclass first. The via container
  1424. // in pcb->library preserves the sequence of addition.
  1425. NETCLASS* netclass = nclasses.GetDefault();
  1426. PADSTACK* via = makeVia( netclass->GetViaDiameter(), netclass->GetViaDrill(),
  1427. m_top_via_layer, m_bot_via_layer );
  1428. // we AppendVia() this first one, there is no way it can be a duplicate,
  1429. // the pcb->library via container is empty at this point. After this,
  1430. // we'll have to use LookupVia().
  1431. wxASSERT( pcb->library->vias.size() == 0 );
  1432. pcb->library->AppendVia( via );
  1433. #if 0
  1434. // I've seen no way to make stock vias useable by freerouter. Also the
  1435. // zero based diameter was leading to duplicates in the LookupVia() function.
  1436. // User should use netclass based vias when going to freerouter.
  1437. // Output the stock vias, but preserve uniqueness in the via container by
  1438. // using LookupVia().
  1439. for( unsigned i = 0; i < aBoard->m_ViasDimensionsList.size(); ++i )
  1440. {
  1441. int viaSize = aBoard->m_ViasDimensionsList[i].m_Diameter;
  1442. int viaDrill = aBoard->m_ViasDimensionsList[i].m_Drill;
  1443. via = makeVia( viaSize, viaDrill,
  1444. m_top_via_layer, m_bot_via_layer );
  1445. // maybe add 'via' to the library, but only if unique.
  1446. PADSTACK* registered = pcb->library->LookupVia( via );
  1447. if( registered != via )
  1448. delete via;
  1449. }
  1450. #endif
  1451. // set the "spare via" index at the start of the
  1452. // pcb->library->spareViaIndex = pcb->library->vias.size();
  1453. // output the non-Default netclass vias
  1454. for( NETCLASSES::iterator nc = nclasses.begin(); nc != nclasses.end(); ++nc )
  1455. {
  1456. netclass = nc->second;
  1457. via = makeVia( netclass->GetViaDiameter(), netclass->GetViaDrill(),
  1458. m_top_via_layer, m_bot_via_layer );
  1459. // maybe add 'via' to the library, but only if unique.
  1460. PADSTACK* registered = pcb->library->LookupVia( via );
  1461. if( registered != via )
  1462. delete via;
  1463. }
  1464. }
  1465. #if 1 // do existing wires and vias
  1466. //-----<create the wires from tracks>-----------------------------------
  1467. {
  1468. // export all of them for now, later we'll decide what controls we need
  1469. // on this.
  1470. static const KICAD_T scanTRACKs[] = { PCB_TRACE_T, EOT };
  1471. items.Collect( aBoard, scanTRACKs );
  1472. std::string netname;
  1473. WIRING* wiring = pcb->wiring;
  1474. PATH* path = 0;
  1475. int old_netcode = -1;
  1476. int old_width = -1;
  1477. LAYER_NUM old_layer = UNDEFINED_LAYER;
  1478. for( int i=0; i<items.GetCount(); ++i )
  1479. {
  1480. TRACK* track = (TRACK*) items[i];
  1481. int netcode = track->GetNet();
  1482. if( netcode == 0 )
  1483. continue;
  1484. if( old_netcode != netcode
  1485. || old_width != track->GetWidth()
  1486. || old_layer != track->GetLayer()
  1487. || (path && path->points.back() != mapPt(track->GetStart()) )
  1488. )
  1489. {
  1490. old_width = track->GetWidth();
  1491. old_layer = track->GetLayer();
  1492. if( old_netcode != netcode )
  1493. {
  1494. old_netcode = netcode;
  1495. NETINFO_ITEM* net = aBoard->FindNet( netcode );
  1496. wxASSERT( net );
  1497. netname = TO_UTF8( net->GetNetname() );
  1498. }
  1499. WIRE* wire = new WIRE( wiring );
  1500. wiring->wires.push_back( wire );
  1501. wire->net_id = netname;
  1502. wire->wire_type = T_protect; // @todo, this should be configurable
  1503. LAYER_NUM kiLayer = track->GetLayer();
  1504. int pcbLayer = kicadLayer2pcb[kiLayer];
  1505. path = new PATH( wire );
  1506. wire->SetShape( path );
  1507. path->layer_id = layerIds[pcbLayer];
  1508. path->aperture_width = scale( old_width );
  1509. path->AppendPoint( mapPt( track->GetStart() ) );
  1510. }
  1511. path->AppendPoint( mapPt( track->GetEnd() ) );
  1512. }
  1513. }
  1514. //-----<export the existing real BOARD instantiated vias>-----------------
  1515. {
  1516. // Export all vias, once per unique size and drill diameter combo.
  1517. static const KICAD_T scanVIAs[] = { PCB_VIA_T, EOT };
  1518. items.Collect( aBoard, scanVIAs );
  1519. for( int i = 0; i<items.GetCount(); ++i )
  1520. {
  1521. SEGVIA* via = (SEGVIA*) items[i];
  1522. wxASSERT( via->Type() == PCB_VIA_T );
  1523. int netcode = via->GetNet();
  1524. if( netcode == 0 )
  1525. continue;
  1526. PADSTACK* padstack = makeVia( via );
  1527. PADSTACK* registered = pcb->library->LookupVia( padstack );
  1528. // if the one looked up is not our padstack, then delete our padstack
  1529. // since it was a duplicate of one already registered.
  1530. if( padstack != registered )
  1531. {
  1532. delete padstack;
  1533. }
  1534. WIRE_VIA* dsnVia = new WIRE_VIA( pcb->wiring );
  1535. pcb->wiring->wire_vias.push_back( dsnVia );
  1536. dsnVia->padstack_id = registered->padstack_id;
  1537. dsnVia->vertexes.push_back( mapPt( via->GetPosition() ) );
  1538. NETINFO_ITEM* net = aBoard->FindNet( netcode );
  1539. wxASSERT( net );
  1540. dsnVia->net_id = TO_UTF8( net->GetNetname() );
  1541. dsnVia->via_type = T_protect; // @todo, this should be configurable
  1542. }
  1543. }
  1544. #endif // do existing wires and vias
  1545. //-----<via_descriptor>-------------------------------------------------
  1546. {
  1547. // The pcb->library will output <padstack_descriptors> which is a combined
  1548. // list of part padstacks and via padstacks. specctra dsn uses the
  1549. // <via_descriptors> to say which of those padstacks are vias.
  1550. // Output the vias in the padstack list here, by name only. This must
  1551. // be done after exporting existing vias as WIRE_VIAs.
  1552. VIA* vias = pcb->structure->via;
  1553. for( unsigned viaNdx = 0; viaNdx < pcb->library->vias.size(); ++viaNdx )
  1554. {
  1555. vias->AppendVia( pcb->library->vias[viaNdx].padstack_id.c_str() );
  1556. }
  1557. }
  1558. //-----<output NETCLASSs>----------------------------------------------------
  1559. NETCLASSES& nclasses = aBoard->m_NetClasses;
  1560. exportNETCLASS( nclasses.GetDefault(), aBoard );
  1561. for( NETCLASSES::iterator nc = nclasses.begin(); nc != nclasses.end(); ++nc )
  1562. {
  1563. NETCLASS* netclass = nc->second;
  1564. exportNETCLASS( netclass, aBoard );
  1565. }
  1566. }
  1567. void SPECCTRA_DB::exportNETCLASS( NETCLASS* aNetClass, BOARD* aBoard )
  1568. {
  1569. /* From page 11 of specctra spec:
  1570. *
  1571. * Routing and Placement Rule Hierarchies
  1572. *
  1573. * Routing and placement rules can be defined at multiple levels of design
  1574. * specification. When a routing or placement rule is defined for an object at
  1575. * multiple levels, a predefined routing or placement precedence order
  1576. * automatically determines which rule to apply to the object. The routing rule
  1577. * precedence order is
  1578. *
  1579. * pcb < layer < class < class layer < group_set < group_set layer < net <
  1580. * net layer < group < group layer < fromto < fromto layer < class_class <
  1581. * class_class layer < padstack < region < class region < net region <
  1582. * class_class region
  1583. *
  1584. * A pcb rule (global rule for the PCB design) has the lowest precedence in the
  1585. * hierarchy. A class-to-class region rule has the highest precedence. Rules
  1586. * set at one level of the hierarchy override conflicting rules set at lower
  1587. * levels. The placement rule precedence order is
  1588. *
  1589. * pcb < image_set < image < component < super cluster < room <
  1590. * room_image_set < family_family < image_image
  1591. *
  1592. * A pcb rule (global rule for the PCB design) has the lowest precedence in the
  1593. * hierarchy. An image-to-image rule has the highest precedence. Rules set at
  1594. * one level of the hierarchy override conflicting rules set at lower levels.
  1595. */
  1596. char text[256];
  1597. CLASS* clazz = new CLASS( pcb->network );
  1598. pcb->network->classes.push_back( clazz );
  1599. // freerouter creates a class named 'default' anyway, and if we
  1600. // try and use that, we end up with two 'default' via rules so use
  1601. // something else as the name of our default class.
  1602. clazz->class_id = TO_UTF8( aNetClass->GetName() );
  1603. for( NETCLASS::iterator net = aNetClass->begin(); net != aNetClass->end(); ++net )
  1604. clazz->net_ids.push_back( TO_UTF8( *net ) );
  1605. clazz->rules = new RULE( clazz, T_rule );
  1606. // output the track width.
  1607. int trackWidth = aNetClass->GetTrackWidth();
  1608. sprintf( text, "(width %.6g)", scale( trackWidth ) );
  1609. clazz->rules->rules.push_back( text );
  1610. // output the clearance.
  1611. int clearance = aNetClass->GetClearance();
  1612. sprintf( text, "(clearance %.6g)", scale( clearance ) + safetyMargin );
  1613. clazz->rules->rules.push_back( text );
  1614. if( aNetClass->GetName() == NETCLASS::Default )
  1615. {
  1616. clazz->class_id = "kicad_default";
  1617. }
  1618. // the easiest way to get the via name is to create a via (which generates
  1619. // the name internal to the PADSTACK), and then grab the name and then
  1620. // delete the via. There are not that many netclasses so
  1621. // this should never become a performance issue.
  1622. PADSTACK* via = makeVia( aNetClass->GetViaDiameter(), aNetClass->GetViaDrill(),
  1623. m_top_via_layer, m_bot_via_layer );
  1624. snprintf( text, sizeof(text), "(use_via %s)", via->GetPadstackId().c_str() );
  1625. clazz->circuit.push_back( text );
  1626. delete via;
  1627. }
  1628. void SPECCTRA_DB::FlipMODULEs( BOARD* aBoard )
  1629. {
  1630. for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
  1631. {
  1632. module->SetFlag( 0 );
  1633. if( module->GetLayer() == LAYER_N_BACK )
  1634. {
  1635. module->Flip( module->GetPosition() );
  1636. module->SetFlag( 1 );
  1637. }
  1638. }
  1639. modulesAreFlipped = true;
  1640. }
  1641. void SPECCTRA_DB::RevertMODULEs( BOARD* aBoard )
  1642. {
  1643. if( !modulesAreFlipped )
  1644. return;
  1645. // DSN Images (=KiCad MODULES and pads) must be presented from the
  1646. // top view. Restore those that were flipped.
  1647. for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
  1648. {
  1649. if( module->GetFlag() )
  1650. {
  1651. module->Flip( module->GetPosition() );
  1652. module->SetFlag( 0 );
  1653. }
  1654. }
  1655. modulesAreFlipped = false;
  1656. }
  1657. } // namespace DSN