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.

1554 lines
50 KiB

18 years ago
18 years ago
17 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 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 "specctra.h"
  31. #include "collectors.h"
  32. #include "wxPcbStruct.h"
  33. #include "pcbstruct.h" // HISTORY_NUMBER
  34. #include "confirm.h" // DisplayError()
  35. #include "gestfich.h" // EDA_FileSelector()
  36. #include "autorout.h" // NET_CODES_OK
  37. #include "class_board_design_settings.h"
  38. #include "trigo.h" // RotatePoint()
  39. #include <set> // std::set
  40. #include <map> // std::map
  41. #include <boost/utility.hpp> // boost::addressof()
  42. using namespace DSN;
  43. // Add .1 mil to the requested clearances as a safety margin.
  44. // There has been disagreement about interpretation of clearance in the past
  45. // between Kicad and Freerouter, so keep this safetyMargin until the
  46. // disagreement is resolved and stable. Freerouter seems to be moving
  47. // (protected) traces upon loading the DSN file, and even though it seems to sometimes
  48. // add its own 0.1 to the clearances, I believe this is happening after
  49. // the load process (and moving traces) so I am of the opinion this is
  50. // still needed.
  51. static const double safetyMargin = 0.1;
  52. // see wxPcbStruct.h
  53. void WinEDA_PcbFrame::ExportToSpecctra( wxCommandEvent& event )
  54. {
  55. wxString fullFileName = GetScreen()->m_FileName;
  56. wxString path;
  57. wxString name;
  58. wxString ext;
  59. wxString dsn_ext = wxT( ".dsn" );
  60. wxString mask = wxT( "*" ) + dsn_ext;
  61. wxFileName::SplitPath( fullFileName, &path, &name, &ext );
  62. name += dsn_ext;
  63. fullFileName = EDA_FileSelector( _( "Specctra DSN file:" ),
  64. path,
  65. name, // name.ext without path!
  66. dsn_ext,
  67. mask,
  68. this,
  69. wxFD_SAVE,
  70. FALSE
  71. );
  72. if( fullFileName == wxEmptyString )
  73. return;
  74. SPECCTRA_DB db;
  75. bool ok = true;
  76. wxString errorText;
  77. BASE_SCREEN* screen = GetScreen();
  78. bool wasModified = screen->IsModify() && !screen->IsSave();
  79. db.SetPCB( SPECCTRA_DB::MakePCB() );
  80. SetLocaleTo_C_standard( ); // Switch the locale to standard C
  81. // DSN Images (=Kicad MODULES and pads) must be presented from the
  82. // top view. So we temporarily flip any modules which are on the back
  83. // side of the board to the front, and record this in the MODULE's flag field.
  84. db.FlipMODULEs( GetBoard() );
  85. try
  86. {
  87. GetBoard()->SynchronizeNetsAndNetClasses();
  88. db.FromBOARD( GetBoard() );
  89. db.ExportPCB( fullFileName, true );
  90. // if an exception is thrown by FromBOARD or ExportPCB(), then
  91. // ~SPECCTRA_DB() will close the file.
  92. }
  93. catch( IOError ioe )
  94. {
  95. ok = false;
  96. // copy the error string to safe place, ioe is in this scope only.
  97. errorText = ioe.errorText;
  98. }
  99. SetLocaleTo_Default( ); // revert to the current locale
  100. // done assuredly, even if an exception was thrown and caught.
  101. db.RevertMODULEs( GetBoard() );
  102. // The two calls below to MODULE::Flip(), both set the
  103. // modified flag, yet their actions cancel each other out, so it should
  104. // be ok to clear the modify flag.
  105. if( !wasModified )
  106. screen->ClrModify();
  107. if( ok )
  108. {
  109. Affiche_Message( wxString( _("BOARD exported OK.")) );
  110. }
  111. else
  112. {
  113. errorText += '\n';
  114. errorText += _("Unable to export, please fix and try again.");
  115. DisplayError( this, errorText );
  116. }
  117. }
  118. namespace DSN {
  119. const KICAD_T SPECCTRA_DB::scanPADs[] = { TYPE_PAD, EOT };
  120. /**
  121. * Function scale
  122. * converts a distance from kicad units to our reported specctra dsn units:
  123. * 1/10000 inches (deci-mils) to mils. So the factor of 10 comes in.
  124. */
  125. static inline double scale( int kicadDist )
  126. {
  127. return kicadDist/10.0;
  128. }
  129. static inline double mapX( int x )
  130. {
  131. return scale(x);
  132. }
  133. static inline double mapY( int y )
  134. {
  135. return -scale(y); // make y negative, since it is increasing going down.
  136. }
  137. /**
  138. * Function mapPt
  139. * converts a Kicad point into a DSN file point. Kicad's BOARD coordinates
  140. * are in deci-mils (i.e. 1/10,000th of an inch) and we are exporting in units
  141. * of mils, so we have to divide by 10.
  142. */
  143. static POINT mapPt( const wxPoint& pt )
  144. {
  145. POINT ret;
  146. ret.x = mapX( pt.x );
  147. ret.y = mapY( pt.y );
  148. ret.FixNegativeZero();
  149. return ret;
  150. }
  151. /**
  152. * Function findPoint
  153. * searches for a DRAWSEGMENT with an end point or start point of aPoint, and
  154. * if found, removes it from the TYPE_COLLECTOR and returns it, else returns NULL.
  155. * @param aPoint The starting or ending point to search for.
  156. * @return DRAWSEGMENT* - The first DRAWSEGMENT that has a start or end point matching
  157. * aPoint, otherwise NULL if none.
  158. */
  159. static DRAWSEGMENT* findPoint( const wxPoint& aPoint, TYPE_COLLECTOR* items )
  160. {
  161. for( int i=0; i<items->GetCount(); ++i )
  162. {
  163. DRAWSEGMENT* graphic = (DRAWSEGMENT*) (*items)[i];
  164. wxASSERT( graphic->Type() == TYPE_DRAWSEGMENT );
  165. if( aPoint == graphic->GetStart() || aPoint == graphic->GetEnd() )
  166. {
  167. items->Remove(i);
  168. return graphic;
  169. }
  170. }
  171. #if defined(DEBUG)
  172. printf("Unable to find segment matching point (%d,%d)\n",
  173. aPoint.x, aPoint.y );
  174. for( int i=0; i<items->GetCount(); ++i )
  175. {
  176. DRAWSEGMENT* graphic = (DRAWSEGMENT*) (*items)[i];
  177. printf( "type=%s, GetStart()=%d,%d GetEnd()=%d,%d\n",
  178. CONV_TO_UTF8( BOARD_ITEM::ShowShape( (Track_Shapes)graphic->m_Shape ) ),
  179. graphic->GetStart().x,
  180. graphic->GetStart().y,
  181. graphic->GetEnd().x,
  182. graphic->GetEnd().y );
  183. }
  184. #endif
  185. return NULL;
  186. }
  187. /**
  188. * Function isRoundKeepout
  189. * decides if the pad is a copper-less through hole which needs to be made into
  190. * a round keepout.
  191. */
  192. static bool isRoundKeepout( D_PAD* aPad )
  193. {
  194. if( aPad->m_PadShape==PAD_CIRCLE )
  195. {
  196. if( aPad->m_Drill.x >= aPad->m_Size.x )
  197. return true;
  198. if( (aPad->m_Masque_Layer & ALL_CU_LAYERS) == 0 )
  199. return true;
  200. }
  201. return false;
  202. }
  203. /**
  204. * Function makePath
  205. * creates a PATH element with a single straight line, a pair of vertices.
  206. */
  207. static PATH* makePath( const POINT& aStart, const POINT& aEnd, const std::string& aLayerName )
  208. {
  209. PATH* path = new PATH( 0, T_path );
  210. path->AppendPoint( aStart );
  211. path->AppendPoint( aEnd );
  212. path->SetLayerId( aLayerName.c_str() );
  213. return path;
  214. }
  215. /**
  216. * Struct wxString_less_than
  217. * is used by std:set<> and std::map<> instantiations which use wxString as their key.
  218. struct wxString_less_than
  219. {
  220. // a "less than" test on two wxStrings
  221. bool operator()( const wxString& s1, const wxString& s2) const
  222. {
  223. return s1.Cmp( s2 ) < 0; // case specific wxString compare
  224. }
  225. };
  226. */
  227. /**
  228. * Function makePADSTACK
  229. * creates a PADSTACK which matches the given pad. Only pads which do not
  230. * satisfy the function isKeepout() should be passed to this function.
  231. * @param aPad The D_PAD which needs to be made into a PADSTACK.
  232. * @return PADSTACK* - The created padstack, including its padstack_id.
  233. */
  234. PADSTACK* SPECCTRA_DB::makePADSTACK( BOARD* aBoard, D_PAD* aPad )
  235. {
  236. char name[80]; // padstack name builder
  237. std::string uniqifier;
  238. // caller must do these checks before calling here.
  239. wxASSERT( !isRoundKeepout( aPad ) );
  240. PADSTACK* padstack = new PADSTACK();
  241. int reportedLayers = 0; // how many in reported padstack
  242. const char* layerName[NB_COPPER_LAYERS];
  243. uniqifier = '[';
  244. bool onAllCopperLayers = ( (aPad->m_Masque_Layer & ALL_CU_LAYERS) == ALL_CU_LAYERS );
  245. if( onAllCopperLayers )
  246. uniqifier += 'A'; // A for all layers
  247. const int copperCount = aBoard->GetCopperLayerCount();
  248. for( int layer=0; layer<copperCount; ++layer )
  249. {
  250. int kilayer = pcbLayer2kicad[layer];
  251. if( onAllCopperLayers || aPad->IsOnLayer( kilayer ) )
  252. {
  253. layerName[reportedLayers++] = layerIds[layer].c_str();
  254. if( !onAllCopperLayers )
  255. {
  256. if( layer == 0 )
  257. uniqifier += 'T';
  258. else if( layer == copperCount-1 )
  259. uniqifier += 'B';
  260. else
  261. uniqifier += char('0' + layer); // layer index char
  262. }
  263. }
  264. }
  265. uniqifier += ']';
  266. POINT dsnOffset;
  267. if( aPad->m_Offset.x || aPad->m_Offset.y )
  268. {
  269. char offsetTxt[32];
  270. wxPoint offset( aPad->m_Offset.x, aPad->m_Offset.y );
  271. dsnOffset = mapPt( offset );
  272. // using '(' or ')' would cause padstack name to be quote wrapped,
  273. // so use other brackets, and {} locks freerouter.
  274. sprintf( offsetTxt, "[%.6g,%.6g]", dsnOffset.x, dsnOffset.y );
  275. uniqifier += offsetTxt;
  276. }
  277. switch( aPad->m_PadShape )
  278. {
  279. default:
  280. case PAD_CIRCLE:
  281. {
  282. double diameter = scale(aPad->m_Size.x);
  283. for( int ndx=0; ndx<reportedLayers; ++ndx )
  284. {
  285. SHAPE* shape = new SHAPE( padstack );
  286. padstack->Append( shape );
  287. CIRCLE* circle = new CIRCLE( shape );
  288. shape->SetShape( circle );
  289. circle->SetLayerId( layerName[ndx] );
  290. circle->SetDiameter( diameter );
  291. circle->SetVertex( dsnOffset );
  292. }
  293. snprintf( name, sizeof(name), "Round%sPad_%.6g_mil",
  294. uniqifier.c_str(), scale(aPad->m_Size.x) );
  295. name[ sizeof(name)-1 ] = 0;
  296. padstack->SetPadstackId( name );
  297. }
  298. break;
  299. case PAD_RECT:
  300. {
  301. double dx = scale( aPad->m_Size.x ) / 2.0;
  302. double dy = scale( aPad->m_Size.y ) / 2.0;
  303. POINT lowerLeft( -dx, -dy );
  304. POINT upperRight( dx, dy );
  305. lowerLeft += dsnOffset;
  306. upperRight += dsnOffset;
  307. for( int ndx=0; ndx<reportedLayers; ++ndx )
  308. {
  309. SHAPE* shape = new SHAPE( padstack );
  310. padstack->Append( shape );
  311. RECTANGLE* rect = new RECTANGLE( shape );
  312. shape->SetShape( rect );
  313. rect->SetLayerId( layerName[ndx] );
  314. rect->SetCorners( lowerLeft, upperRight );
  315. }
  316. snprintf( name, sizeof(name), "Rect%sPad_%.6gx%.6g_mil",
  317. uniqifier.c_str(), scale(aPad->m_Size.x), scale(aPad->m_Size.y) );
  318. name[ sizeof(name)-1 ] = 0;
  319. padstack->SetPadstackId( name );
  320. }
  321. break;
  322. case PAD_OVAL:
  323. {
  324. double dx = scale( aPad->m_Size.x ) / 2.0;
  325. double dy = scale( aPad->m_Size.y ) / 2.0;
  326. double dr = dx - dy;
  327. double radius;
  328. POINT start;
  329. POINT stop;
  330. if( dr >= 0 ) // oval is horizontal
  331. {
  332. radius = dy;
  333. start = POINT( -dr, 0.0 );
  334. stop = POINT( dr, 0.0 );
  335. }
  336. else // oval is vertical
  337. {
  338. radius = dx;
  339. dr = -dr;
  340. start = POINT( 0.0, -dr );
  341. stop = POINT( 0.0, dr );
  342. }
  343. start += dsnOffset;
  344. stop += dsnOffset;
  345. for( int ndx=0; ndx<reportedLayers; ++ndx )
  346. {
  347. SHAPE* shape;
  348. PATH* path;
  349. // see http://www.freerouting.net/usren/viewtopic.php?f=3&t=317#p408
  350. shape = new SHAPE( padstack );
  351. padstack->Append( shape );
  352. path = makePath( start, stop, layerName[ndx] );
  353. shape->SetShape( path );
  354. path->aperture_width = 2.0 * radius;
  355. }
  356. snprintf( name, sizeof(name), "Oval%sPad_%.6gx%.6g_mil",
  357. uniqifier.c_str(), scale(aPad->m_Size.x), scale(aPad->m_Size.y) );
  358. name[ sizeof(name)-1 ] = 0;
  359. padstack->SetPadstackId( name );
  360. }
  361. break;
  362. /*
  363. case PAD_TRAPEZOID:
  364. break;
  365. */
  366. }
  367. return padstack;
  368. }
  369. /// data type used to ensure unique-ness of pin names, holding (wxString and int)
  370. //typedef std::map<wxString, int, wxString_less_than> PINMAP;
  371. typedef std::map<wxString, int> PINMAP;
  372. IMAGE* SPECCTRA_DB::makeIMAGE( BOARD* aBoard, MODULE* aModule )
  373. {
  374. PINMAP pinmap;
  375. TYPE_COLLECTOR moduleItems;
  376. wxString padName;
  377. // get all the MODULE's pads.
  378. moduleItems.Collect( aModule, scanPADs );
  379. IMAGE* image = new IMAGE(0);
  380. image->image_id = CONV_TO_UTF8( aModule->m_LibRef );
  381. // from the pads, and make an IMAGE using collated padstacks.
  382. for( int p=0; p<moduleItems.GetCount(); ++p )
  383. {
  384. D_PAD* pad = (D_PAD*) moduleItems[p];
  385. // see if this pad is a through hole with no copper on its perimeter
  386. if( isRoundKeepout( pad ) )
  387. {
  388. double diameter = scale( pad->m_Drill.x );
  389. POINT vertex = mapPt( pad->m_Pos0 );
  390. int layerCount = aBoard->GetCopperLayerCount();
  391. for( int layer=0; layer<layerCount; ++layer )
  392. {
  393. KEEPOUT* keepout = new KEEPOUT(image, T_keepout);
  394. image->keepouts.push_back( keepout );
  395. CIRCLE* circle = new CIRCLE( keepout );
  396. keepout->SetShape( circle );
  397. circle->SetDiameter( diameter );
  398. circle->SetVertex( vertex );
  399. circle->SetLayerId( layerIds[layer].c_str() );
  400. }
  401. }
  402. // else if() could there be a square keepout here?
  403. else
  404. {
  405. PADSTACK* padstack = makePADSTACK( aBoard, pad );
  406. PADSTACKSET::iterator iter = padstackset.find( *padstack );
  407. if( iter != padstackset.end() )
  408. {
  409. // padstack is a duplicate, delete it and use the original
  410. delete padstack;
  411. padstack = (PADSTACK*) *iter.base(); // folklore, be careful here
  412. }
  413. else
  414. {
  415. padstackset.insert( padstack );
  416. }
  417. PIN* pin = new PIN(image);
  418. padName = pad->ReturnStringPadName();
  419. pin->pin_id = CONV_TO_UTF8( padName );
  420. if( padName!=wxEmptyString && pinmap.find( padName )==pinmap.end() )
  421. {
  422. pinmap[ padName ] = 0;
  423. }
  424. else // pad name is a duplicate within this module
  425. {
  426. char buf[32];
  427. int duplicates = ++pinmap[ padName ];
  428. sprintf( buf, "@%d", duplicates );
  429. pin->pin_id += buf; // append "@1" or "@2", etc. to pin name
  430. }
  431. pin->kiNetCode = pad->GetNet();
  432. image->pins.push_back( pin );
  433. pin->padstack_id = padstack->padstack_id;
  434. int angle = pad->m_Orient - aModule->m_Orient; // tenths of degrees
  435. if( angle )
  436. {
  437. NORMALIZE_ANGLE_POS(angle);
  438. pin->SetRotation( angle / 10.0 );
  439. }
  440. wxPoint pos( pad->m_Pos0 );
  441. pin->SetVertex( mapPt( pos ) );
  442. }
  443. }
  444. #if 1 // enable image (outline) scopes.
  445. static const KICAD_T scanEDGEs[] = { TYPE_EDGE_MODULE, EOT };
  446. // get all the MODULE's EDGE_MODULEs and convert those to DSN outlines.
  447. moduleItems.Collect( aModule, scanEDGEs );
  448. for( int i=0; i<moduleItems.GetCount(); ++i )
  449. {
  450. EDGE_MODULE* graphic = (EDGE_MODULE*) moduleItems[i];
  451. SHAPE* outline;
  452. PATH* path;
  453. switch( graphic->m_Shape )
  454. {
  455. case S_SEGMENT:
  456. outline = new SHAPE( image, T_outline );
  457. image->Append( outline );
  458. path = new PATH( outline );
  459. outline->SetShape( path );
  460. path->SetAperture( scale( graphic->m_Width ) );
  461. path->SetLayerId( "signal" );
  462. path->AppendPoint( mapPt( graphic->m_Start0 ) );
  463. path->AppendPoint( mapPt( graphic->m_End0 ) );
  464. break;
  465. case S_CIRCLE:
  466. {
  467. // this is best done by 4 QARC's but freerouter does not yet support QARCs.
  468. // for now, support by using line segments.
  469. outline = new SHAPE( image, T_outline );
  470. image->Append( outline );
  471. path = new PATH( outline );
  472. outline->SetShape( path );
  473. path->SetAperture( scale( graphic->m_Width ) );
  474. path->SetLayerId( "signal" );
  475. // Do the math using Kicad units, that way we stay out of the
  476. // scientific notation range of floating point numbers in the
  477. // DSN file. We do not parse scientific notation in our own
  478. // lexer/beautifier, and the spec is not clear that this is
  479. // required. Fixed point floats are all that should be needed.
  480. double radius = hypot( double( graphic->m_Start.x - graphic->m_End.x ),
  481. double( graphic->m_Start.y - graphic->m_End.y ) );
  482. // better if evenly divisible into 360
  483. const int DEGREE_INTERVAL = 18; // 18 means 20 line segments
  484. for( double radians = 0.0; radians < 2*M_PI; radians += DEGREE_INTERVAL * M_PI / 180.0 )
  485. {
  486. wxPoint point( int( radius * cos( radians ) ),
  487. int( radius * sin( radians ) ) );
  488. point += graphic->m_Start0; // an offset
  489. path->AppendPoint( mapPt(point) );
  490. }
  491. }
  492. break;
  493. case S_RECT:
  494. case S_ARC:
  495. default:
  496. D( printf("makeIMAGE(): unsupported shape %s\n",
  497. CONV_TO_UTF8( BOARD_ITEM::ShowShape( (Track_Shapes)graphic->m_Shape)) );)
  498. continue;
  499. }
  500. }
  501. #endif
  502. return image;
  503. }
  504. PADSTACK* SPECCTRA_DB::makeVia( int aCopperDiameter, int aDrillDiameter,
  505. int aTopLayer, int aBotLayer )
  506. {
  507. char name[48];
  508. PADSTACK* padstack = new PADSTACK();
  509. double dsnDiameter = scale( aCopperDiameter );
  510. for( int layer=aTopLayer; layer<=aBotLayer; ++layer )
  511. {
  512. SHAPE* shape = new SHAPE( padstack );
  513. padstack->Append( shape );
  514. CIRCLE* circle = new CIRCLE( shape );
  515. shape->SetShape( circle );
  516. circle->SetDiameter( dsnDiameter );
  517. circle->SetLayerId( layerIds[layer].c_str() );
  518. }
  519. snprintf( name, sizeof(name), "Via[%d-%d]_%.6g:%.6g_mil",
  520. aTopLayer, aBotLayer, dsnDiameter,
  521. // encode the drill value into the name for later import
  522. scale( aDrillDiameter )
  523. );
  524. name[ sizeof(name)-1 ] = 0;
  525. padstack->SetPadstackId( name );
  526. return padstack;
  527. }
  528. PADSTACK* SPECCTRA_DB::makeVia( const SEGVIA* aVia )
  529. {
  530. int topLayer;
  531. int botLayer;
  532. aVia->ReturnLayerPair( &topLayer, &botLayer );
  533. topLayer = kicadLayer2pcb[topLayer];
  534. botLayer = kicadLayer2pcb[botLayer];
  535. if( topLayer > botLayer )
  536. EXCHG( topLayer, botLayer );
  537. return makeVia( aVia->m_Width, aVia->GetDrillValue(), topLayer, botLayer );
  538. }
  539. void SPECCTRA_DB::fillBOUNDARY( BOARD* aBoard, BOUNDARY* boundary ) throw( IOError )
  540. {
  541. TYPE_COLLECTOR items;
  542. // get all the DRAWSEGMENTS into 'items', then look for layer == EDGE_N,
  543. // and those segments comprise the board's perimeter.
  544. static const KICAD_T scanDRAWSEGMENTS[] = { TYPE_DRAWSEGMENT, EOT };
  545. items.Collect( aBoard, scanDRAWSEGMENTS );
  546. bool haveEdges = false;
  547. for( int i=0; i<items.GetCount(); )
  548. {
  549. DRAWSEGMENT* item = (DRAWSEGMENT*) items[i];
  550. wxASSERT( item->Type() == TYPE_DRAWSEGMENT );
  551. if( item->GetLayer() != EDGE_N )
  552. {
  553. items.Remove( i );
  554. }
  555. else
  556. {
  557. haveEdges = true;
  558. ++i;
  559. //D( item->Show( 0, std::cout );)
  560. }
  561. }
  562. if( haveEdges )
  563. {
  564. PATH* path = new PATH( boundary );
  565. boundary->paths.push_back( path );
  566. path->layer_id = "pcb";
  567. wxPoint prevPt;
  568. DRAWSEGMENT* graphic = (DRAWSEGMENT*) items[0];
  569. // the first DRAWSEGMENT is in 'graphic*', ok to remove it from 'items'
  570. items.Remove( 0 );
  571. prevPt = graphic->GetEnd();
  572. path->AppendPoint( mapPt( graphic->GetEnd() ) );
  573. // do not append the other end point yet, this first edge item might be an arc
  574. for(;;)
  575. {
  576. switch( graphic->m_Shape )
  577. {
  578. case S_ARC:
  579. // freerouter does not yet understand arcs, so approximate
  580. // an arc with a series of short lines and put those
  581. // line segments into the !same! PATH.
  582. {
  583. const int STEPS = 9; // in an arc of 90 degrees
  584. wxPoint start = graphic->GetStart();
  585. wxPoint end = graphic->GetEnd();
  586. wxPoint center = graphic->m_Start;
  587. int angle = -graphic->m_Angle;
  588. if( prevPt != start )
  589. {
  590. wxASSERT( prevPt == graphic->GetEnd() );
  591. angle = -angle;
  592. EXCHG( start, end );
  593. }
  594. wxPoint nextPt;
  595. for( int step=1; step<=STEPS; ++step )
  596. {
  597. int rotation = ( angle * step )/STEPS;
  598. nextPt = start;
  599. RotatePoint( &nextPt.x, &nextPt.y, center.x, center.y, rotation );
  600. path->AppendPoint( mapPt( nextPt ) );
  601. }
  602. prevPt = nextPt;
  603. }
  604. break;
  605. case S_CIRCLE:
  606. #if 0
  607. // do not output a circle, freerouter does not understand it.
  608. // this might be a mounting hole or something, ignore it without error
  609. // because some of our demo boards have used the edges pcb layer to
  610. // hold islanded circles, rather than simply using holes.
  611. break;
  612. #else
  613. // Do not output a circle, freerouter does not understand it.
  614. // tell user his board has a problem, this is better than silently
  615. // ignoring the error. "edges pcb" layer should not be used
  616. // to hold islanded circles which could or should better be done
  617. // as simple holes. (Some of our demo boards have this problem.)
  618. // fall thru here to report the error.
  619. #endif
  620. default:
  621. {
  622. wxString error;
  623. error.Printf( _("Unsupported DRAWSEGMENT type %s"),
  624. GetChars( BOARD_ITEM::ShowShape( (Track_Shapes) graphic->m_Shape ) ) );
  625. ThrowIOError( error );
  626. }
  627. break;
  628. case S_SEGMENT:
  629. {
  630. POINT nextPt;
  631. if( prevPt != graphic->GetStart() )
  632. {
  633. wxASSERT( prevPt == graphic->GetEnd() );
  634. nextPt = mapPt( graphic->GetStart() );
  635. prevPt = graphic->GetStart();
  636. }
  637. else
  638. {
  639. nextPt = mapPt( graphic->GetStart() );
  640. prevPt = graphic->GetEnd();
  641. }
  642. path->AppendPoint( nextPt );
  643. }
  644. }
  645. if( items.GetCount() == 0 )
  646. break;
  647. graphic = findPoint( prevPt, &items );
  648. if( !graphic )
  649. {
  650. wxString error;
  651. error << _("Unable to find the next segment with an endpoint of ");
  652. error << prevPt;
  653. error << wxChar('\n');
  654. error << _("Edit Edges_Pcb segments, making them contiguous.");
  655. ThrowIOError( error );
  656. }
  657. }
  658. }
  659. else
  660. {
  661. aBoard->ComputeBoundaryBox();
  662. RECTANGLE* rect = new RECTANGLE( boundary );
  663. boundary->rectangle = rect;
  664. rect->layer_id = "pcb";
  665. // opposite corners
  666. wxPoint bottomRight;
  667. bottomRight.x = aBoard->m_BoundaryBox.GetRight();
  668. bottomRight.y = aBoard->m_BoundaryBox.GetBottom();
  669. rect->SetCorners( mapPt( aBoard->m_BoundaryBox.GetOrigin() ),
  670. mapPt( bottomRight ) );
  671. }
  672. }
  673. typedef std::set<std::string> STRINGSET;
  674. typedef std::pair<STRINGSET::iterator, bool> STRINGSET_PAIR;
  675. void SPECCTRA_DB::FromBOARD( BOARD* aBoard ) throw( IOError )
  676. {
  677. TYPE_COLLECTOR items;
  678. static const KICAD_T scanMODULEs[] = { TYPE_MODULE, EOT };
  679. // Not all boards are exportable. Check that all reference Ids are unique.
  680. // Unless they are unique, we cannot import the session file which comes
  681. // back to us later from the router.
  682. {
  683. TYPE_COLLECTOR padItems;
  684. items.Collect( aBoard, scanMODULEs );
  685. STRINGSET refs; // holds module reference designators
  686. for( int i=0; i<items.GetCount(); ++i )
  687. {
  688. MODULE* module = (MODULE*) items[i];
  689. if( module->GetReference() == wxEmptyString )
  690. {
  691. ThrowIOError( _("Component with value of \"%s\" has empty reference id."),
  692. GetChars( module->GetValue() ) );
  693. }
  694. // if we cannot insert OK, that means the reference has been seen before.
  695. STRINGSET_PAIR refpair = refs.insert( CONV_TO_UTF8( module->GetReference() ) );
  696. if( !refpair.second ) // insert failed
  697. {
  698. ThrowIOError( _("Multiple components have identical reference IDs of \"%s\"."),
  699. GetChars( module->GetReference() ) );
  700. }
  701. }
  702. }
  703. if( !pcb )
  704. pcb = SPECCTRA_DB::MakePCB();
  705. //-----<layer_descriptor>-----------------------------------------------
  706. {
  707. // specctra wants top physical layer first, then going down to the
  708. // bottom most physical layer in physical sequence.
  709. // @question : why does Kicad not display layers in that order?
  710. buildLayerMaps( aBoard );
  711. int layerCount = aBoard->GetCopperLayerCount();
  712. for( int pcbNdx=0; pcbNdx<layerCount; ++pcbNdx )
  713. {
  714. LAYER* layer = new LAYER( pcb->structure );
  715. pcb->structure->layers.push_back( layer );
  716. layer->name = layerIds[pcbNdx];
  717. DSN_T layerType;
  718. switch( aBoard->GetLayerType( pcbLayer2kicad[pcbNdx] ) )
  719. {
  720. default:
  721. case LT_SIGNAL: layerType = T_signal; break;
  722. case LT_POWER: layerType = T_power; break;
  723. case LT_MIXED: layerType = T_mixed; break;
  724. case LT_JUMPER: layerType = T_jumper; break;
  725. }
  726. layer->layer_type = layerType;
  727. layer->properties.push_back( PROPERTY() );
  728. PROPERTY* property = &layer->properties.back();
  729. property->name = "index";
  730. char temp[32];
  731. sprintf( temp, "%d", pcbNdx );
  732. property->value = temp;
  733. }
  734. }
  735. // a space in a quoted token is NOT a terminator, true establishes this.
  736. pcb->parser->space_in_quoted_tokens = true;
  737. //-----<unit_descriptor> & <resolution_descriptor>--------------------
  738. {
  739. pcb->unit->units = T_mil;
  740. pcb->resolution->units = T_mil;
  741. // Kicad only supports 1/10th of mil internal coordinates. So to avoid
  742. // having the router give us back 1/100th of mil coordinates which we
  743. // will have to round and thereby cause error, we declare our maximum
  744. // resolution precisely at 1/10th for now. For more on this, see:
  745. // http://www.freerouting.net/usren/viewtopic.php?f=3&t=354
  746. pcb->resolution->value = 10;
  747. }
  748. //-----<boundary_descriptor>------------------------------------------
  749. {
  750. // Because fillBOUNDARY() can throw an exception, we link in an
  751. // empty boundary so the BOUNDARY does not get lost in the event of
  752. // of an exception.
  753. BOUNDARY* boundary = new BOUNDARY(0);
  754. pcb->structure->SetBOUNDARY( boundary );
  755. fillBOUNDARY( aBoard, boundary );
  756. }
  757. //-----<rules>--------------------------------------------------------
  758. {
  759. char rule[80];
  760. int defaultTrackWidth = aBoard->m_NetClasses.GetDefault()->GetTrackWidth();
  761. int defaultClearance = aBoard->m_NetClasses.GetDefault()->GetClearance();
  762. double clearance = scale( defaultClearance );
  763. STRINGS& rules = pcb->structure->rules->rules;
  764. sprintf( rule, "(width %.6g)", scale( defaultTrackWidth ) );
  765. rules.push_back( rule );
  766. sprintf( rule, "(clearance %.6g)", clearance+safetyMargin );
  767. rules.push_back( rule );
  768. // On a high density board (a board with 4 mil tracks, 4 mil spacing)
  769. // a typical solder mask clearance will be 2-3 mils.
  770. // This exposes 2 to 3 mils of bare board around each pad, and would
  771. // leave only 1 to 2 mils of solder mask between the solder mask's boundary
  772. // to the edge of any trace within "clearance" of the pad. So we need at least
  773. // 2 mils *extra* clearance for traces which would come near a pad on
  774. // a different net. So if the baseline trace to trace clearance was say 4 mils, then
  775. // the SMD to trace clearance should be at least 6 mils.
  776. double default_smd = clearance + safetyMargin;
  777. if( default_smd <= 6.0 )
  778. default_smd = 6.0;
  779. sprintf( rule, "(clearance %.6g (type default_smd))", default_smd );
  780. rules.push_back( rule );
  781. /* see: http://www.freerouting.net/usren/viewtopic.php?f=5&t=339#p474
  782. sprintf( rule, "(clearance %.6g (type pad_to_turn_gap))", clearance + safetyMargin );
  783. rules.push_back( rule );
  784. sprintf( rule, "(clearance %.6g (type smd_to_turn_gap))", clearance + safetyMargin );
  785. rules.push_back( rule );
  786. sprintf( rule, "(clearance %.6g (type via_via))", clearance + safetyMargin );
  787. rules.push_back( rule );
  788. sprintf( rule, "(clearance %.6g (type via_smd))", clearance + safetyMargin );
  789. rules.push_back( rule );
  790. sprintf( rule, "(clearance %.6g (type via_pin))", clearance + safetyMargin );
  791. rules.push_back( rule );
  792. sprintf( rule, "(clearance %.6g (type pin_pin))", clearance + safetyMargin );
  793. rules.push_back( rule );
  794. sprintf( rule, "(clearance %.6g (type smd_pin))", clearance + safetyMargin );
  795. rules.push_back( rule );
  796. */
  797. // well, the user is going to text edit these in the DSN file anyway,
  798. // at least until we have an export dialog.
  799. // Pad to pad spacing on a single SMT part can be closer than our
  800. // clearance, we don't want freerouter complaining about that, so
  801. // output a significantly smaller pad to pad clearance to freerouter.
  802. clearance = scale( defaultClearance )/4;
  803. sprintf( rule, "(clearance %.6g (type smd_smd))", clearance );
  804. rules.push_back( rule );
  805. }
  806. //-----<zone containers become planes>--------------------------------
  807. {
  808. int netlessZones = 0;
  809. static const KICAD_T scanZONEs[] = { TYPE_ZONE_CONTAINER, EOT };
  810. items.Collect( aBoard, scanZONEs );
  811. for( int i=0; i<items.GetCount(); ++i )
  812. {
  813. ZONE_CONTAINER* item = (ZONE_CONTAINER*) items[i];
  814. COPPER_PLANE* plane = new COPPER_PLANE( pcb->structure );
  815. pcb->structure->planes.push_back( plane );
  816. PATH* mainPolygon = new PATH( plane, T_polygon );
  817. plane->SetShape( mainPolygon );
  818. plane->name = CONV_TO_UTF8( item->m_Netname );
  819. if( plane->name.size() == 0 )
  820. {
  821. char name[32];
  822. // This is one of those no connection zones, netcode=0, and it has no name.
  823. // Create a unique, bogus netname.
  824. NET* no_net = new NET( pcb->network );
  825. sprintf( name, "@:no_net_%d", netlessZones++ );
  826. no_net->net_id = name;
  827. // add the bogus net name to network->nets.
  828. pcb->network->nets.push_back( no_net );
  829. // use the bogus net name in the netless zone.
  830. plane->name = no_net->net_id;
  831. }
  832. mainPolygon->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
  833. int count = item->m_Poly->corner.size();
  834. int ndx = 0; // used in 2 for() loops below
  835. for( ; ndx<count; ++ndx )
  836. {
  837. wxPoint point( item->m_Poly->corner[ndx].x,
  838. item->m_Poly->corner[ndx].y );
  839. mainPolygon->AppendPoint( mapPt(point) );
  840. // this was the end of the main polygon
  841. if( item->m_Poly->corner[ndx].end_contour )
  842. break;
  843. }
  844. WINDOW* window = 0;
  845. PATH* cutout = 0;
  846. // handle the cutouts
  847. for( ++ndx; ndx<count; ++ndx )
  848. {
  849. if( item->m_Poly->corner[ndx-1].end_contour )
  850. {
  851. window = new WINDOW( plane );
  852. plane->AddWindow( window );
  853. cutout = new PATH( window, T_polygon );
  854. window->SetShape( cutout );
  855. cutout->layer_id = layerIds[ kicadLayer2pcb[ item->GetLayer() ] ];
  856. }
  857. wxASSERT( window );
  858. wxASSERT( cutout );
  859. wxPoint point(item->m_Poly->corner[ndx].x,
  860. item->m_Poly->corner[ndx].y );
  861. cutout->AppendPoint( mapPt(point) );
  862. }
  863. }
  864. }
  865. // keepouts could go here, there are none in Kicad at this time.
  866. //-----<build the images, components, and netlist>-----------------------
  867. {
  868. PIN_REF empty( pcb->network );
  869. std::string componentId;
  870. // find the highest numbered netCode within the board.
  871. int highestNetCode = aBoard->m_NetInfo->GetCount() - 1;
  872. deleteNETs();
  873. // expand the net vector to highestNetCode+1, setting empty to NULL
  874. nets.resize( highestNetCode+1, NULL );
  875. // skip netcode = 0
  876. for( unsigned i=1; i<nets.size(); ++i )
  877. nets[i] = new NET( pcb->network );
  878. for( unsigned ii = 0; ii < aBoard->m_NetInfo->GetCount(); ii++ )
  879. {
  880. NETINFO_ITEM* net = aBoard->m_NetInfo->GetNetItem(ii);
  881. int netcode = net->GetNet();
  882. if( netcode > 0 )
  883. nets[ netcode ]->net_id = CONV_TO_UTF8( net->GetNetname() );
  884. }
  885. items.Collect( aBoard, scanMODULEs );
  886. padstackset.clear();
  887. for( int m=0; m<items.GetCount(); ++m )
  888. {
  889. MODULE* module = (MODULE*) items[m];
  890. IMAGE* image = makeIMAGE( aBoard, module );
  891. componentId = CONV_TO_UTF8( module->GetReference() );
  892. // create a net list entry for all the actual pins in the image
  893. // for the current module. location of this code is critical
  894. // because we fabricated some pin names to ensure unique-ness
  895. // of pin names within a module, do not move this code because
  896. // the life of this 'IMAGE* image' is not necessarily long. The
  897. // exported netlist will have some fabricated pin names in it.
  898. // If you don't like fabricated pin names, then make sure all pads
  899. // within your MODULEs are uniquely named!
  900. for( unsigned p=0; p<image->pins.size(); ++p )
  901. {
  902. PIN* pin = &image->pins[p];
  903. int netcode = pin->kiNetCode;
  904. if( netcode > 0 )
  905. {
  906. NET* net = nets[netcode];
  907. net->pins.push_back( empty );
  908. PIN_REF& pin_ref = net->pins.back();
  909. pin_ref.component_id = componentId;
  910. pin_ref.pin_id = pin->pin_id;
  911. }
  912. }
  913. IMAGE* registered = pcb->library->LookupIMAGE( image );
  914. if( registered != image )
  915. {
  916. // If our new 'image' is not a unique IMAGE, delete it.
  917. // and use the registered one, known as 'image' after this.
  918. delete image;
  919. image = registered;
  920. }
  921. COMPONENT* comp = pcb->placement->LookupCOMPONENT( image->GetImageId() );
  922. PLACE* place = new PLACE( comp );
  923. comp->places.push_back( place );
  924. place->SetRotation( module->m_Orient/10.0 );
  925. place->SetVertex( mapPt( module->m_Pos ) );
  926. place->component_id = componentId;
  927. place->part_number = CONV_TO_UTF8( module->GetValue() );
  928. // module is flipped from bottom side, set side to T_back
  929. if( module->flag )
  930. {
  931. int angle = 1800 - module->m_Orient;
  932. NORMALIZE_ANGLE_POS(angle);
  933. place->SetRotation( angle/10.0 );
  934. place->side = T_back;
  935. }
  936. }
  937. // copy the SPECCTRA_DB::padstackset to the LIBRARY. Since we are
  938. // removing, do not increment the iterator
  939. for( PADSTACKSET::iterator i=padstackset.begin(); i!=padstackset.end();
  940. i=padstackset.begin() )
  941. {
  942. PADSTACKSET::auto_type ps = padstackset.release( i );
  943. PADSTACK* padstack = ps.release();
  944. pcb->library->AddPadstack( padstack );
  945. }
  946. // copy our SPECCTRA_DB::nets to the pcb->network
  947. for( unsigned n=1; n<nets.size(); ++n )
  948. {
  949. NET* net = nets[n];
  950. if( net->pins.size() )
  951. {
  952. // give ownership to pcb->network
  953. pcb->network->nets.push_back( net );
  954. nets[n] = 0;
  955. }
  956. }
  957. }
  958. //-----< output vias used in netclasses and as stock >---------------------
  959. {
  960. NETCLASSES& nclasses = aBoard->m_NetClasses;
  961. // Add the via from the Default netclass first. The via container
  962. // in pcb->library preserves the sequence of addition.
  963. NETCLASS* netclass = nclasses.GetDefault();
  964. PADSTACK* via = makeVia( netclass->GetViaDiameter(), netclass->GetViaDrill(),
  965. 0, aBoard->GetCopperLayerCount()-1 );
  966. // we AppendVia() this first one, there is no way it can be a duplicate,
  967. // the pcb->library via container is empty at this point. After this,
  968. // we'll have to use LookupVia().
  969. wxASSERT( pcb->library->vias.size() == 0 );
  970. pcb->library->AppendVia( via );
  971. // output the stock vias, but preserve uniqueness in the via container by
  972. // using LookupVia().
  973. for( unsigned i=0; i < aBoard->m_ViasDimensionsList.size(); ++i )
  974. {
  975. int viaSize = aBoard->m_ViasDimensionsList[i].m_Diameter;
  976. int viaDrill = aBoard->m_ViasDimensionsList[i].m_Drill;
  977. via = makeVia( viaSize, viaDrill,
  978. 0, aBoard->GetCopperLayerCount()-1 );
  979. // maybe add 'via' to the library, but only if unique.
  980. PADSTACK* registered = pcb->library->LookupVia( via );
  981. if( registered != via )
  982. delete via;
  983. }
  984. // set the "spare via" index at the start of the
  985. // pcb->library->spareViaIndex = pcb->library->vias.size();
  986. // output the non-Default netclass vias
  987. for( NETCLASSES::iterator nc = nclasses.begin(); nc != nclasses.end(); ++nc )
  988. {
  989. netclass = nc->second;
  990. via = makeVia( netclass->GetViaDiameter(), netclass->GetViaDrill(),
  991. 0, aBoard->GetCopperLayerCount()-1 );
  992. // maybe add 'via' to the library, but only if unique.
  993. PADSTACK* registered = pcb->library->LookupVia( via );
  994. if( registered != via )
  995. delete via;
  996. }
  997. }
  998. #if 1 // do existing wires and vias
  999. //-----<create the wires from tracks>-----------------------------------
  1000. {
  1001. // export all of them for now, later we'll decide what controls we need
  1002. // on this.
  1003. static const KICAD_T scanTRACKs[] = { TYPE_TRACK, EOT };
  1004. items.Collect( aBoard, scanTRACKs );
  1005. std::string netname;
  1006. WIRING* wiring = pcb->wiring;
  1007. PATH* path = 0;
  1008. int old_netcode = -1;
  1009. int old_width = -1;
  1010. int old_layer = -1;
  1011. for( int i=0; i<items.GetCount(); ++i )
  1012. {
  1013. TRACK* track = (TRACK*) items[i];
  1014. int netcode = track->GetNet();
  1015. if( netcode == 0 )
  1016. continue;
  1017. if( old_netcode != netcode
  1018. || old_width != track->m_Width
  1019. || old_layer != track->GetLayer()
  1020. || (path && path->points.back() != mapPt(track->m_Start) )
  1021. )
  1022. {
  1023. old_width = track->m_Width;
  1024. old_layer = track->GetLayer();
  1025. if( old_netcode != netcode )
  1026. {
  1027. old_netcode = netcode;
  1028. NETINFO_ITEM* net = aBoard->FindNet( netcode );
  1029. wxASSERT( net );
  1030. netname = CONV_TO_UTF8( net->GetNetname() );
  1031. }
  1032. WIRE* wire = new WIRE( wiring );
  1033. wiring->wires.push_back( wire );
  1034. wire->net_id = netname;
  1035. wire->wire_type = T_protect; // @todo, this should be configurable
  1036. int kiLayer = track->GetLayer();
  1037. int pcbLayer = kicadLayer2pcb[kiLayer];
  1038. path = new PATH( wire );
  1039. wire->SetShape( path );
  1040. path->layer_id = layerIds[pcbLayer];
  1041. path->aperture_width = scale( old_width );
  1042. path->AppendPoint( mapPt( track->m_Start ) );
  1043. }
  1044. path->AppendPoint( mapPt( track->m_End ) );
  1045. }
  1046. }
  1047. //-----<export the existing real BOARD instantiated vias>-----------------
  1048. {
  1049. // export all of them for now, later we'll decide what controls we need
  1050. // on this.
  1051. static const KICAD_T scanVIAs[] = { TYPE_VIA, EOT };
  1052. items.Collect( aBoard, scanVIAs );
  1053. for( int i=0; i<items.GetCount(); ++i )
  1054. {
  1055. SEGVIA* via = (SEGVIA*) items[i];
  1056. wxASSERT( via->Type() == TYPE_VIA );
  1057. int netcode = via->GetNet();
  1058. if( netcode == 0 )
  1059. continue;
  1060. PADSTACK* padstack = makeVia( via );
  1061. PADSTACK* registered = pcb->library->LookupVia( padstack );
  1062. // if the one looked up is not our padstack, then delete our padstack
  1063. // since it was a duplicate of one already registered.
  1064. if( padstack != registered )
  1065. {
  1066. delete padstack;
  1067. }
  1068. WIRE_VIA* dsnVia = new WIRE_VIA( pcb->wiring );
  1069. pcb->wiring->wire_vias.push_back( dsnVia );
  1070. dsnVia->padstack_id = registered->padstack_id;
  1071. dsnVia->vertexes.push_back( mapPt( via->GetPosition() ) );
  1072. NETINFO_ITEM* net = aBoard->FindNet( netcode );
  1073. wxASSERT( net );
  1074. dsnVia->net_id = CONV_TO_UTF8( net->GetNetname() );
  1075. dsnVia->via_type = T_protect; // @todo, this should be configurable
  1076. }
  1077. }
  1078. #endif // do existing wires and vias
  1079. //-----<via_descriptor>-------------------------------------------------
  1080. {
  1081. // The pcb->library will output <padstack_descriptors> which is a combined
  1082. // list of part padstacks and via padstacks. specctra dsn uses the
  1083. // <via_descriptors> to say which of those padstacks are vias.
  1084. // Output the vias in the padstack list here, by name only. This must
  1085. // be done after exporting existing vias as WIRE_VIAs.
  1086. VIA* vias = pcb->structure->via;
  1087. for( unsigned viaNdx=0; viaNdx < pcb->library->vias.size(); ++viaNdx )
  1088. {
  1089. vias->AppendVia( pcb->library->vias[viaNdx].padstack_id.c_str() );
  1090. }
  1091. }
  1092. //-----<output NETCLASSs>----------------------------------------------------
  1093. NETCLASSES& nclasses = aBoard->m_NetClasses;
  1094. exportNETCLASS( nclasses.GetDefault(), aBoard );
  1095. for( NETCLASSES::iterator nc = nclasses.begin(); nc != nclasses.end(); ++nc )
  1096. {
  1097. NETCLASS* netclass = nc->second;
  1098. exportNETCLASS( netclass, aBoard );
  1099. }
  1100. }
  1101. void SPECCTRA_DB::exportNETCLASS( NETCLASS* aNetClass, BOARD* aBoard )
  1102. {
  1103. /* From page 11 of specctra spec:
  1104. Routing and Placement Rule Hierarchies
  1105. Routing and placement rules can be defined at multiple levels of design
  1106. specification. When a routing or placement rule is defined for an object at
  1107. multiple levels, a predefined routing or placement precedence order
  1108. automatically determines which rule to apply to the object. The routing rule
  1109. precedence order is
  1110. pcb < layer < class < class layer < group_set < group_set layer < net <
  1111. net layer < group < group layer < fromto < fromto layer < class_class <
  1112. class_class layer < padstack < region < class region < net region <
  1113. class_class region
  1114. A pcb rule (global rule for the PCB design) has the lowest precedence in the
  1115. hierarchy. A class-to-class region rule has the highest precedence. Rules
  1116. set at one level of the hierarchy override conflicting rules set at lower
  1117. levels. The placement rule precedence order is
  1118. pcb < image_set < image < component < super cluster < room <
  1119. room_image_set < family_family < image_image
  1120. A pcb rule (global rule for the PCB design) has the lowest precedence in the
  1121. hierarchy. An image-to-image rule has the highest precedence. Rules set at
  1122. one level of the hierarchy override conflicting rules set at lower levels.
  1123. */
  1124. char text[256];
  1125. CLASS* clazz = new CLASS( pcb->network );
  1126. pcb->network->classes.push_back( clazz );
  1127. // freerouter creates a class named 'default' anyway, and if we
  1128. // try and use that, we end up with two 'default' via rules so use
  1129. // something else as the name of our default class.
  1130. clazz->class_id = CONV_TO_UTF8( aNetClass->GetName() );
  1131. for( NETCLASS::iterator net = aNetClass->begin(); net != aNetClass->end(); ++net )
  1132. clazz->net_ids.push_back( CONV_TO_UTF8( *net ) );
  1133. clazz->rules = new RULE( clazz, T_rule );
  1134. // output the track width.
  1135. int trackWidth = aNetClass->GetTrackWidth();
  1136. sprintf( text, "(width %.6g)", scale( trackWidth ) );
  1137. clazz->rules->rules.push_back( text );
  1138. // output the clearance.
  1139. int clearance = aNetClass->GetClearance();
  1140. sprintf( text, "(clearance %.6g)", scale( clearance ) + safetyMargin );
  1141. clazz->rules->rules.push_back( text );
  1142. if( aNetClass->GetName() == NETCLASS::Default )
  1143. {
  1144. clazz->class_id = "kicad_default";
  1145. }
  1146. // the easiest way to get the via name is to create a via (which generates
  1147. // the name internal to the PADSTACK), and then grab the name and then
  1148. // delete the via. There are not that many netclasses so
  1149. // this should never become a performance issue.
  1150. PADSTACK* via = makeVia( aNetClass->GetViaDiameter(), aNetClass->GetViaDrill(),
  1151. 0, aBoard->GetCopperLayerCount()-1 );
  1152. snprintf( text, sizeof(text), "(use_via %s)", via->GetPadstackId().c_str() );
  1153. clazz->circuit.push_back( text );
  1154. delete via;
  1155. }
  1156. void SPECCTRA_DB::FlipMODULEs( BOARD* aBoard )
  1157. {
  1158. for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
  1159. {
  1160. module->flag = 0;
  1161. if( module->GetLayer() == COPPER_LAYER_N )
  1162. {
  1163. module->Flip( module->m_Pos );
  1164. module->flag = 1;
  1165. }
  1166. }
  1167. modulesAreFlipped = true;
  1168. }
  1169. void SPECCTRA_DB::RevertMODULEs( BOARD* aBoard )
  1170. {
  1171. if( !modulesAreFlipped )
  1172. return;
  1173. // DSN Images (=Kicad MODULES and pads) must be presented from the
  1174. // top view. Restore those that were flipped.
  1175. for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
  1176. {
  1177. if( module->flag )
  1178. {
  1179. module->Flip( module->m_Pos );
  1180. module->flag = 0;
  1181. }
  1182. }
  1183. modulesAreFlipped = false;
  1184. }
  1185. } // namespace DSN