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.

3345 lines
109 KiB

4 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
9 months ago
9 months ago
4 years ago
Clean up arc/circle polygonization. 1) For a while now we've been using a calculated seg count from a given maxError, and a correction factor to push the radius out so that all the error is outside the arc/circle. However, the second calculation (which pre-dates the first) is pretty much just the inverse of the first (and yields nothing more than maxError back). This is particularly sub-optimal given the cost of trig functions. 2) There are a lot of old optimizations to reduce segcounts in certain situations, someting that our error-based calculation compensates for anyway. (Smaller radii need fewer segments to meet the maxError condition.) But perhaps more importantly we now surface maxError in the UI and we don't really want to call it "Max deviation except when it's not". 3) We were also clamping the segCount twice: once in the calculation routine and once in most of it's callers. Furthermore, the caller clamping was inconsistent (both in being done and in the clamping value). We now clamp only in the calculation routine. 4) There's no reason to use the correction factors in the 3Dviewer; it's just a visualization and whether the polygonization error is inside or outside the shape isn't really material. 5) The arc-correction-disabling stuff (used for solder mask layer) was somewhat fragile in that it depended on the caller to turn it back on afterwards. It's now only exposed as a RAII object which automatically cleans up when it goes out of scope. 6) There were also bugs in a couple of the polygonization routines where we'd accumulate round-off error in adding up the segments and end up with an overly long last segment (which of course would voilate the error max). This was the cause of the linked bug and also some issues with vias that we had fudged in the past with extra clearance. Fixes https://gitlab.com/kicad/code/kicad/issues/5567
5 years ago
13 years ago
13 years ago
13 years ago
4 years ago
9 months ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
  5. * Copyright The KiCad Developers, see AUTHORS.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. /*
  25. Pcbnew PLUGIN for Eagle 6.x XML *.brd and footprint format.
  26. XML parsing and converting:
  27. Getting line numbers and byte offsets from the source XML file is not
  28. possible using currently available XML libraries within KiCad project:
  29. wxXmlDocument and boost::property_tree.
  30. property_tree will give line numbers but no byte offsets, and only during
  31. document loading. This means that if we have a problem after the document is
  32. successfully loaded, there is no way to correlate back to line number and byte
  33. offset of the problem. So a different approach is taken, one which relies on the
  34. XML elements themselves using an XPATH type of reporting mechanism. The path to
  35. the problem is reported in the error messages. This means keeping track of that
  36. path as we traverse the XML document for the sole purpose of accurate error
  37. reporting.
  38. User can load the source XML file into firefox or other xml browser and follow
  39. our error message.
  40. Load() TODO's
  41. *) verify zone fill clearances are correct
  42. */
  43. #include <cerrno>
  44. #include <wx/string.h>
  45. #include <wx/xml/xml.h>
  46. #include <wx/filename.h>
  47. #include <wx/log.h>
  48. #include <wx/wfstream.h>
  49. #include <wx/txtstrm.h>
  50. #include <wx/window.h>
  51. #include <convert_basic_shapes_to_polygon.h>
  52. #include <font/fontconfig.h>
  53. #include <geometry/geometry_utils.h>
  54. #include <string_utils.h>
  55. #include <locale_io.h>
  56. #include <trigo.h>
  57. #include <progress_reporter.h>
  58. #include <project.h>
  59. #include <board.h>
  60. #include <board_design_settings.h>
  61. #include <footprint.h>
  62. #include <pad.h>
  63. #include <pcb_track.h>
  64. #include <pcb_shape.h>
  65. #include <zone.h>
  66. #include <padstack.h>
  67. #include <pcb_text.h>
  68. #include <pcb_dimension.h>
  69. #include <reporter.h>
  70. #include <pcb_io/pcb_io.h>
  71. #include <pcb_io/eagle/pcb_io_eagle.h>
  72. using namespace std;
  73. /// Parse an eagle distance which is either mm, or mils if there is "mil" suffix.
  74. /// Return is in BIU.
  75. static int parseEagle( const wxString& aDistance )
  76. {
  77. ECOORD::EAGLE_UNIT unit = ( aDistance.npos != aDistance.find( "mil" ) )
  78. ? ECOORD::EAGLE_UNIT::EU_MIL
  79. : ECOORD::EAGLE_UNIT::EU_MM;
  80. ECOORD coord( aDistance, unit );
  81. return coord.ToPcbUnits();
  82. }
  83. // In Eagle one can specify DRC rules where min value > max value,
  84. // in such case the max value has the priority
  85. template<typename T>
  86. static T eagleClamp( T aMin, T aValue, T aMax )
  87. {
  88. T ret = std::max( aMin, aValue );
  89. return std::min( aMax, ret );
  90. }
  91. /// Assemble a two part key as a simple concatenation of aFirst and aSecond parts,
  92. /// using a separator.
  93. static wxString makeKey( const wxString& aFirst, const wxString& aSecond )
  94. {
  95. wxString key = aFirst + '\x02' + aSecond;
  96. return key;
  97. }
  98. void PCB_IO_EAGLE::setKeepoutSettingsToZone( ZONE* aZone, int aLayer ) const
  99. {
  100. if( aLayer == EAGLE_LAYER::TRESTRICT || aLayer == EAGLE_LAYER::BRESTRICT )
  101. {
  102. aZone->SetIsRuleArea( true );
  103. aZone->SetDoNotAllowVias( true );
  104. aZone->SetDoNotAllowTracks( true );
  105. aZone->SetDoNotAllowCopperPour( true );
  106. aZone->SetDoNotAllowPads( true );
  107. aZone->SetDoNotAllowFootprints( false );
  108. if( aLayer == EAGLE_LAYER::TRESTRICT ) // front layer keepout
  109. aZone->SetLayer( F_Cu );
  110. else // bottom layer keepout
  111. aZone->SetLayer( B_Cu );
  112. }
  113. else if( aLayer == EAGLE_LAYER::VRESTRICT )
  114. {
  115. aZone->SetIsRuleArea( true );
  116. aZone->SetDoNotAllowVias( true );
  117. aZone->SetDoNotAllowTracks( false );
  118. aZone->SetDoNotAllowCopperPour( false );
  119. aZone->SetDoNotAllowPads( false );
  120. aZone->SetDoNotAllowFootprints( false );
  121. aZone->SetLayerSet( LSET::AllCuMask() );
  122. }
  123. else // copper pour cutout
  124. {
  125. aZone->SetIsRuleArea( true );
  126. aZone->SetDoNotAllowVias( false );
  127. aZone->SetDoNotAllowTracks( false );
  128. aZone->SetDoNotAllowCopperPour( true );
  129. aZone->SetDoNotAllowPads( false );
  130. aZone->SetDoNotAllowFootprints( false );
  131. aZone->SetLayerSet( { kicad_layer( aLayer ) } );
  132. }
  133. }
  134. void ERULES::parse( wxXmlNode* aRules, std::function<void()> aCheckpoint )
  135. {
  136. wxXmlNode* child = aRules->GetChildren();
  137. while( child )
  138. {
  139. aCheckpoint();
  140. if( child->GetName() == wxT( "param" ) )
  141. {
  142. const wxString& name = child->GetAttribute( wxT( "name" ) );
  143. const wxString& value = child->GetAttribute( wxT( "value" ) );
  144. if( name == wxT( "psElongationLong" ) )
  145. psElongationLong = wxAtoi( value );
  146. else if( name == wxT( "psElongationOffset" ) )
  147. psElongationOffset = wxAtoi( value );
  148. else if( name == wxT( "mvStopFrame" ) )
  149. value.ToCDouble( &mvStopFrame );
  150. else if( name == wxT( "mvCreamFrame" ) )
  151. value.ToCDouble( &mvCreamFrame );
  152. else if( name == wxT( "mlMinStopFrame" ) )
  153. mlMinStopFrame = parseEagle( value );
  154. else if( name == wxT( "mlMaxStopFrame" ) )
  155. mlMaxStopFrame = parseEagle( value );
  156. else if( name == wxT( "mlMinCreamFrame" ) )
  157. mlMinCreamFrame = parseEagle( value );
  158. else if( name == wxT( "mlMaxCreamFrame" ) )
  159. mlMaxCreamFrame = parseEagle( value );
  160. else if( name == wxT( "srRoundness" ) )
  161. value.ToCDouble( &srRoundness );
  162. else if( name == wxT( "srMinRoundness" ) )
  163. srMinRoundness = parseEagle( value );
  164. else if( name == wxT( "srMaxRoundness" ) )
  165. srMaxRoundness = parseEagle( value );
  166. else if( name == wxT( "psTop" ) )
  167. psTop = wxAtoi( value );
  168. else if( name == wxT( "psBottom" ) )
  169. psBottom = wxAtoi( value );
  170. else if( name == wxT( "psFirst" ) )
  171. psFirst = wxAtoi( value );
  172. else if( name == wxT( "rvPadTop" ) )
  173. value.ToCDouble( &rvPadTop );
  174. else if( name == wxT( "rlMinPadTop" ) )
  175. rlMinPadTop = parseEagle( value );
  176. else if( name == wxT( "rlMaxPadTop" ) )
  177. rlMaxPadTop = parseEagle( value );
  178. else if( name == wxT( "rvViaOuter" ) )
  179. value.ToCDouble( &rvViaOuter );
  180. else if( name == wxT( "rlMinViaOuter" ) )
  181. rlMinViaOuter = parseEagle( value );
  182. else if( name == wxT( "rlMaxViaOuter" ) )
  183. rlMaxViaOuter = parseEagle( value );
  184. else if( name == wxT( "mdWireWire" ) )
  185. mdWireWire = parseEagle( value );
  186. }
  187. child = child->GetNext();
  188. }
  189. }
  190. PCB_IO_EAGLE::PCB_IO_EAGLE() :
  191. PCB_IO( wxS( "Eagle" ) ),
  192. m_rules( new ERULES() ),
  193. m_xpath( new XPATH() ),
  194. m_progressReporter( nullptr ),
  195. m_doneCount( 0 ),
  196. m_lastProgressCount( 0 ),
  197. m_totalCount( 0 ),
  198. m_mod_time( wxDateTime::Now() )
  199. {
  200. using namespace std::placeholders;
  201. init( nullptr );
  202. clear_cu_map();
  203. RegisterCallback( std::bind( &PCB_IO_EAGLE::DefaultLayerMappingCallback, this, _1 ) );
  204. }
  205. PCB_IO_EAGLE::~PCB_IO_EAGLE()
  206. {
  207. deleteTemplates();
  208. delete m_rules;
  209. delete m_xpath;
  210. }
  211. bool PCB_IO_EAGLE::CanReadBoard( const wxString& aFileName ) const
  212. {
  213. if( !PCB_IO::CanReadBoard( aFileName ) )
  214. return false;
  215. return checkHeader( aFileName );
  216. }
  217. bool PCB_IO_EAGLE::CanReadLibrary( const wxString& aFileName ) const
  218. {
  219. if( !PCB_IO::CanReadLibrary( aFileName ) )
  220. return false;
  221. return checkHeader( aFileName );
  222. }
  223. bool PCB_IO_EAGLE::CanReadFootprint( const wxString& aFileName ) const
  224. {
  225. return CanReadLibrary( aFileName );
  226. }
  227. bool PCB_IO_EAGLE::checkHeader(const wxString& aFileName) const
  228. {
  229. wxFileInputStream input( aFileName );
  230. if( !input.IsOk() )
  231. return false;
  232. wxTextInputStream text( input );
  233. for( int i = 0; i < 8; i++ )
  234. {
  235. if( input.Eof() )
  236. return false;
  237. if( text.ReadLine().Contains( wxS( "<eagle" ) ) )
  238. return true;
  239. }
  240. return false;
  241. }
  242. void PCB_IO_EAGLE::checkpoint()
  243. {
  244. const unsigned PROGRESS_DELTA = 50;
  245. if( m_progressReporter )
  246. {
  247. if( ++m_doneCount > m_lastProgressCount + PROGRESS_DELTA )
  248. {
  249. m_progressReporter->SetCurrentProgress( ( (double) m_doneCount )
  250. / std::max( 1U, m_totalCount ) );
  251. if( !m_progressReporter->KeepRefreshing() )
  252. THROW_IO_ERROR( _( "Open cancelled by user." ) );
  253. m_lastProgressCount = m_doneCount;
  254. }
  255. }
  256. }
  257. VECTOR2I inline PCB_IO_EAGLE::kicad_fontsize( const ECOORD& d, int aTextThickness ) const
  258. {
  259. // Eagle includes stroke thickness in the text size, KiCAD does not
  260. int kz = d.ToPcbUnits();
  261. return VECTOR2I( kz - aTextThickness, kz - aTextThickness );
  262. }
  263. BOARD* PCB_IO_EAGLE::LoadBoard( const wxString& aFileName, BOARD* aAppendToMe,
  264. const std::map<std::string, UTF8>* aProperties, PROJECT* aProject )
  265. {
  266. LOCALE_IO toggle; // toggles on, then off, the C locale.
  267. wxXmlNode* doc;
  268. fontconfig::FONTCONFIG::SetReporter( &WXLOG_REPORTER::GetInstance() );
  269. init( aProperties );
  270. m_board = aAppendToMe ? aAppendToMe : new BOARD();
  271. // Give the filename to the board if it's new
  272. if( !aAppendToMe )
  273. m_board->SetFileName( aFileName );
  274. // delete on exception, if I own m_board, according to aAppendToMe
  275. unique_ptr<BOARD> deleter( aAppendToMe ? nullptr : m_board );
  276. try
  277. {
  278. if( m_progressReporter )
  279. {
  280. m_progressReporter->Report( wxString::Format( _( "Loading %s..." ), aFileName ) );
  281. if( !m_progressReporter->KeepRefreshing() )
  282. THROW_IO_ERROR( _( "Open cancelled by user." ) );
  283. }
  284. wxFileName fn = aFileName;
  285. // Load the document
  286. wxFFileInputStream stream( fn.GetFullPath() );
  287. wxXmlDocument xmlDocument;
  288. if( !stream.IsOk() || !xmlDocument.Load( stream ) )
  289. {
  290. THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'" ),
  291. fn.GetFullPath() ) );
  292. }
  293. doc = xmlDocument.GetRoot();
  294. m_min_trace = INT_MAX;
  295. m_min_hole = INT_MAX;
  296. m_min_via = INT_MAX;
  297. m_min_annulus = INT_MAX;
  298. loadAllSections( doc );
  299. BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
  300. if( m_min_trace < bds.m_TrackMinWidth )
  301. bds.m_TrackMinWidth = m_min_trace;
  302. if( m_min_via < bds.m_ViasMinSize )
  303. bds.m_ViasMinSize = m_min_via;
  304. if( m_min_hole < bds.m_MinThroughDrill )
  305. bds.m_MinThroughDrill = m_min_hole;
  306. if( m_min_annulus < bds.m_ViasMinAnnularWidth )
  307. bds.m_ViasMinAnnularWidth = m_min_annulus;
  308. if( m_rules->mdWireWire )
  309. bds.m_MinClearance = KiROUND( m_rules->mdWireWire );
  310. NETCLASS defaults( wxT( "dummy" ) );
  311. auto finishNetclass =
  312. [&]( std::shared_ptr<NETCLASS> netclass )
  313. {
  314. // If Eagle has a clearance matrix then we'll build custom rules from that.
  315. // Netclasses should just be the board minimum clearance.
  316. netclass->SetClearance( KiROUND( bds.m_MinClearance ) );
  317. if( netclass->GetTrackWidth() == INT_MAX )
  318. netclass->SetTrackWidth( defaults.GetTrackWidth() );
  319. if( netclass->GetViaDiameter() == INT_MAX )
  320. netclass->SetViaDiameter( defaults.GetViaDiameter() );
  321. if( netclass->GetViaDrill() == INT_MAX )
  322. netclass->SetViaDrill( defaults.GetViaDrill() );
  323. };
  324. std::shared_ptr<NET_SETTINGS>& netSettings = bds.m_NetSettings;
  325. finishNetclass( netSettings->GetDefaultNetclass() );
  326. for( const auto& [name, netclass] : netSettings->GetNetclasses() )
  327. finishNetclass( netclass );
  328. m_board->m_LegacyNetclassesLoaded = true;
  329. m_board->m_LegacyDesignSettingsLoaded = true;
  330. fn.SetExt( wxT( "kicad_dru" ) );
  331. wxFile rulesFile( fn.GetFullPath(), wxFile::write );
  332. rulesFile.Write( m_customRules );
  333. // should be empty, else missing m_xpath->pop()
  334. wxASSERT( m_xpath->Contents().size() == 0 );
  335. }
  336. catch( const XML_PARSER_ERROR &exc )
  337. {
  338. wxString errmsg = exc.what();
  339. errmsg += wxT( "\n@ " );
  340. errmsg += m_xpath->Contents();
  341. THROW_IO_ERROR( errmsg );
  342. }
  343. // IO_ERROR exceptions are left uncaught, they pass upwards from here.
  344. m_board->SetCopperLayerCount( getMinimumCopperLayerCount() );
  345. centerBoard();
  346. deleter.release();
  347. return m_board;
  348. }
  349. std::vector<FOOTPRINT*> PCB_IO_EAGLE::GetImportedCachedLibraryFootprints()
  350. {
  351. std::vector<FOOTPRINT*> retval;
  352. for( const auto& [ name, footprint ] : m_templates )
  353. retval.push_back( static_cast<FOOTPRINT*>( footprint->Clone() ) );
  354. return retval;
  355. }
  356. void PCB_IO_EAGLE::init( const std::map<std::string, UTF8>* aProperties )
  357. {
  358. m_hole_count = 0;
  359. m_min_trace = 0;
  360. m_min_hole = 0;
  361. m_min_via = 0;
  362. m_min_annulus = 0;
  363. m_xpath->clear();
  364. m_pads_to_nets.clear();
  365. m_board = nullptr;
  366. m_props = aProperties;
  367. delete m_rules;
  368. m_rules = new ERULES();
  369. }
  370. void PCB_IO_EAGLE::clear_cu_map()
  371. {
  372. // All cu layers are invalid until we see them in the <layers> section while
  373. // loading either a board or library. See loadLayerDefs().
  374. for( unsigned i = 0; i < arrayDim(m_cu_map); ++i )
  375. m_cu_map[i] = -1;
  376. }
  377. void PCB_IO_EAGLE::loadAllSections( wxXmlNode* aDoc )
  378. {
  379. wxXmlNode* drawing = MapChildren( aDoc )["drawing"];
  380. NODE_MAP drawingChildren = MapChildren( drawing );
  381. wxXmlNode* board = drawingChildren["board"];
  382. NODE_MAP boardChildren = MapChildren( board );
  383. auto count_children = [this]( wxXmlNode* aNode )
  384. {
  385. if( aNode )
  386. {
  387. wxXmlNode* child = aNode->GetChildren();
  388. while( child )
  389. {
  390. m_totalCount++;
  391. child = child->GetNext();
  392. }
  393. }
  394. };
  395. wxXmlNode* designrules = boardChildren["designrules"];
  396. wxXmlNode* layers = drawingChildren["layers"];
  397. wxXmlNode* plain = boardChildren["plain"];
  398. wxXmlNode* classes = boardChildren["classes"];
  399. wxXmlNode* signals = boardChildren["signals"];
  400. wxXmlNode* libs = boardChildren["libraries"];
  401. wxXmlNode* elems = boardChildren["elements"];
  402. if( m_progressReporter )
  403. {
  404. m_totalCount = 0;
  405. m_doneCount = 0;
  406. count_children( designrules );
  407. count_children( layers );
  408. count_children( plain );
  409. count_children( signals );
  410. count_children( elems );
  411. while( libs )
  412. {
  413. count_children( MapChildren( libs )["packages"] );
  414. libs = libs->GetNext();
  415. }
  416. // Rewind
  417. libs = boardChildren["libraries"];
  418. }
  419. m_xpath->push( "eagle.drawing" );
  420. {
  421. m_xpath->push( "board" );
  422. loadDesignRules( designrules );
  423. m_xpath->pop();
  424. }
  425. {
  426. m_xpath->push( "layers" );
  427. loadLayerDefs( layers );
  428. mapEagleLayersToKicad();
  429. m_xpath->pop();
  430. }
  431. {
  432. m_xpath->push( "board" );
  433. loadPlain( plain );
  434. loadClasses( classes );
  435. loadSignals( signals );
  436. loadLibraries( libs );
  437. loadElements( elems );
  438. m_xpath->pop();
  439. }
  440. m_xpath->pop(); // "eagle.drawing"
  441. }
  442. void PCB_IO_EAGLE::loadDesignRules( wxXmlNode* aDesignRules )
  443. {
  444. if( aDesignRules )
  445. {
  446. m_xpath->push( "designrules" );
  447. m_rules->parse( aDesignRules, [this](){ checkpoint(); } );
  448. m_xpath->pop(); // "designrules"
  449. }
  450. }
  451. void PCB_IO_EAGLE::loadLayerDefs( wxXmlNode* aLayers )
  452. {
  453. if( !aLayers )
  454. return;
  455. ELAYERS cu; // copper layers
  456. // Get the first layer and iterate
  457. wxXmlNode* layerNode = aLayers->GetChildren();
  458. m_eagleLayers.clear();
  459. m_eagleLayersIds.clear();
  460. while( layerNode )
  461. {
  462. ELAYER elayer( layerNode );
  463. m_eagleLayers.insert( std::make_pair( elayer.number, elayer ) );
  464. m_eagleLayersIds.insert( std::make_pair( elayer.name, elayer.number ) );
  465. // find the subset of layers that are copper and active
  466. if( elayer.number >= 1 && elayer.number <= 16 && ( !elayer.active || *elayer.active ) )
  467. cu.push_back( elayer );
  468. layerNode = layerNode->GetNext();
  469. }
  470. // establish cu layer map:
  471. int ki_layer_count = 0;
  472. for( EITER it = cu.begin(); it != cu.end(); ++it, ++ki_layer_count )
  473. {
  474. if( ki_layer_count == 0 )
  475. {
  476. m_cu_map[it->number] = F_Cu;
  477. }
  478. else if( ki_layer_count == int( cu.size()-1 ) )
  479. {
  480. m_cu_map[it->number] = B_Cu;
  481. }
  482. else
  483. {
  484. // some eagle boards do not have contiguous layer number sequences.
  485. m_cu_map[it->number] = ki_layer_count;
  486. }
  487. }
  488. // Set the layer names and cu count if we're loading a board.
  489. if( m_board )
  490. {
  491. m_board->SetCopperLayerCount( cu.size() );
  492. for( EITER it = cu.begin(); it != cu.end(); ++it )
  493. {
  494. PCB_LAYER_ID layer = kicad_layer( it->number );
  495. // these function provide their own protection against non enabled layers:
  496. if( layer >= 0 && layer < PCB_LAYER_ID_COUNT ) // layer should be valid
  497. {
  498. m_board->SetLayerName( layer, it->name );
  499. m_board->SetLayerType( layer, LT_SIGNAL );
  500. }
  501. // could map the colors here
  502. }
  503. }
  504. }
  505. #define DIMENSION_PRECISION DIM_PRECISION::X_XX // 0.01 mm
  506. void PCB_IO_EAGLE::loadPlain( wxXmlNode* aGraphics )
  507. {
  508. if( !aGraphics )
  509. return;
  510. m_xpath->push( "plain" );
  511. // Get the first graphic and iterate
  512. wxXmlNode* gr = aGraphics->GetChildren();
  513. // (polygon | wire | text | circle | rectangle | frame | hole)*
  514. while( gr )
  515. {
  516. checkpoint();
  517. wxString grName = gr->GetName();
  518. if( grName == wxT( "wire" ) )
  519. {
  520. m_xpath->push( "wire" );
  521. EWIRE w( gr );
  522. PCB_LAYER_ID layer = kicad_layer( w.layer );
  523. VECTOR2I start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
  524. VECTOR2I end( kicad_x( w.x2 ), kicad_y( w.y2 ) );
  525. if( layer != UNDEFINED_LAYER )
  526. {
  527. PCB_SHAPE* shape = new PCB_SHAPE( m_board );
  528. int width = w.width.ToPcbUnits();
  529. // KiCad cannot handle zero or negative line widths
  530. if( width <= 0 )
  531. width = m_board->GetDesignSettings().GetLineThickness( layer );
  532. m_board->Add( shape, ADD_MODE::APPEND );
  533. if( !w.curve )
  534. {
  535. shape->SetShape( SHAPE_T::SEGMENT );
  536. shape->SetStart( start );
  537. shape->SetEnd( end );
  538. }
  539. else
  540. {
  541. VECTOR2I center = ConvertArcCenter( start, end, *w.curve );
  542. shape->SetShape( SHAPE_T::ARC );
  543. shape->SetCenter( center );
  544. shape->SetStart( start );
  545. shape->SetArcAngleAndEnd( -EDA_ANGLE( *w.curve, DEGREES_T ), true ); // KiCad rotates the other way
  546. }
  547. shape->SetLayer( layer );
  548. shape->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
  549. }
  550. m_xpath->pop();
  551. }
  552. else if( grName == wxT( "text" ) )
  553. {
  554. m_xpath->push( "text" );
  555. ETEXT t( gr );
  556. PCB_LAYER_ID layer = kicad_layer( t.layer );
  557. if( layer != UNDEFINED_LAYER )
  558. {
  559. PCB_TEXT* pcbtxt = new PCB_TEXT( m_board );
  560. m_board->Add( pcbtxt, ADD_MODE::APPEND );
  561. pcbtxt->SetLayer( layer );
  562. wxString kicadText = interpretText( t.text );
  563. pcbtxt->SetText( kicadText );
  564. double ratio = t.ratio ? *t.ratio : 8; // DTD says 8 is default
  565. int textThickness = KiROUND( t.size.ToPcbUnits() * ratio / 100 );
  566. pcbtxt->SetTextThickness( textThickness );
  567. pcbtxt->SetTextSize( kicad_fontsize( t.size, textThickness ) );
  568. pcbtxt->SetKeepUpright( false );
  569. // Eagle's anchor is independent of text justification; KiCad's is not.
  570. VECTOR2I eagleAnchor( kicad_x( t.x ), kicad_y( t.y ) );
  571. int align = t.align ? *t.align : ETEXT::BOTTOM_LEFT;
  572. BOX2I textbox = pcbtxt->GetBoundingBox();
  573. VECTOR2I offset( 0, 0 );
  574. double degrees = 0;
  575. int signX = 0;
  576. int signY = 0;
  577. if( t.rot )
  578. {
  579. if( !t.rot->spin )
  580. degrees = t.rot->mirror ? -t.rot->degrees : t.rot->degrees;
  581. if( t.rot->mirror )
  582. pcbtxt->SetMirrored( t.rot->mirror );
  583. if( degrees > 90 && degrees <= 270 )
  584. {
  585. if( degrees == 270 && t.rot->mirror )
  586. degrees = 270; // an odd special-case
  587. else
  588. degrees -= 180;
  589. signX = t.rot->mirror ? 1 : -1;
  590. signY = 1;
  591. }
  592. pcbtxt->SetTextAngle( EDA_ANGLE( degrees, DEGREES_T ) );
  593. }
  594. switch( align )
  595. {
  596. case ETEXT::BOTTOM_CENTER:
  597. case ETEXT::BOTTOM_LEFT:
  598. case ETEXT::BOTTOM_RIGHT:
  599. offset.y = signY * (int) textbox.GetHeight();
  600. break;
  601. case ETEXT::TOP_CENTER:
  602. case ETEXT::TOP_LEFT:
  603. case ETEXT::TOP_RIGHT:
  604. offset.y = -signY * (int) textbox.GetHeight();
  605. break;
  606. default:
  607. break;
  608. }
  609. switch( align )
  610. {
  611. case ETEXT::TOP_LEFT:
  612. case ETEXT::CENTER_LEFT:
  613. case ETEXT::BOTTOM_LEFT:
  614. offset.x = signX * (int) textbox.GetWidth();
  615. break;
  616. case ETEXT::TOP_RIGHT:
  617. case ETEXT::CENTER_RIGHT:
  618. case ETEXT::BOTTOM_RIGHT:
  619. offset.x = -signX * (int) textbox.GetWidth();
  620. break;
  621. default:
  622. break;
  623. }
  624. RotatePoint( offset, EDA_ANGLE( degrees, DEGREES_T ) );
  625. pcbtxt->SetTextPos( eagleAnchor + offset );
  626. switch( align )
  627. {
  628. case ETEXT::CENTER:
  629. pcbtxt->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
  630. pcbtxt->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  631. break;
  632. case ETEXT::CENTER_LEFT:
  633. pcbtxt->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  634. pcbtxt->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  635. break;
  636. case ETEXT::CENTER_RIGHT:
  637. pcbtxt->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  638. pcbtxt->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  639. break;
  640. case ETEXT::TOP_CENTER:
  641. pcbtxt->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
  642. pcbtxt->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  643. break;
  644. case ETEXT::TOP_LEFT:
  645. pcbtxt->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  646. pcbtxt->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  647. break;
  648. case ETEXT::TOP_RIGHT:
  649. pcbtxt->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  650. pcbtxt->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  651. break;
  652. case ETEXT::BOTTOM_CENTER:
  653. pcbtxt->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
  654. pcbtxt->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  655. break;
  656. case ETEXT::BOTTOM_LEFT:
  657. pcbtxt->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  658. pcbtxt->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  659. break;
  660. case ETEXT::BOTTOM_RIGHT:
  661. pcbtxt->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  662. pcbtxt->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  663. break;
  664. }
  665. // Refine justification and rotation for mirrored texts
  666. if( pcbtxt->IsMirrored() && degrees < -90 && degrees >= -270 )
  667. {
  668. pcbtxt->SetTextAngle( EDA_ANGLE( 180+degrees, DEGREES_T ) );
  669. if( pcbtxt->GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
  670. pcbtxt->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  671. else if( pcbtxt->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  672. pcbtxt->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  673. if( pcbtxt->GetVertJustify() == GR_TEXT_V_ALIGN_BOTTOM )
  674. pcbtxt->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  675. else if( pcbtxt->GetVertJustify() == GR_TEXT_V_ALIGN_TOP )
  676. pcbtxt->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  677. }
  678. }
  679. m_xpath->pop();
  680. }
  681. else if( grName == wxT( "circle" ) )
  682. {
  683. m_xpath->push( "circle" );
  684. ECIRCLE c( gr );
  685. int width = c.width.ToPcbUnits();
  686. int radius = c.radius.ToPcbUnits();
  687. if( c.layer == EAGLE_LAYER::TRESTRICT || c.layer == EAGLE_LAYER::BRESTRICT
  688. || c.layer == EAGLE_LAYER::VRESTRICT )
  689. {
  690. ZONE* zone = new ZONE( m_board );
  691. m_board->Add( zone, ADD_MODE::APPEND );
  692. setKeepoutSettingsToZone( zone, c.layer );
  693. // approximate circle as polygon
  694. VECTOR2I center( kicad_x( c.x ), kicad_y( c.y ) );
  695. int outlineRadius = radius + ( width / 2 );
  696. int segsInCircle = GetArcToSegmentCount( outlineRadius, ARC_HIGH_DEF, FULL_CIRCLE );
  697. EDA_ANGLE delta = ANGLE_360 / segsInCircle;
  698. for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += delta )
  699. {
  700. VECTOR2I rotatedPoint( outlineRadius, 0 );
  701. RotatePoint( rotatedPoint, angle );
  702. zone->AppendCorner( center + rotatedPoint, -1 );
  703. }
  704. if( width > 0 )
  705. {
  706. zone->NewHole();
  707. int innerRadius = radius - ( width / 2 );
  708. segsInCircle = GetArcToSegmentCount( innerRadius, ARC_HIGH_DEF, FULL_CIRCLE );
  709. delta = ANGLE_360 / segsInCircle;
  710. for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += delta )
  711. {
  712. VECTOR2I rotatedPoint( innerRadius, 0 );
  713. RotatePoint( rotatedPoint, angle );
  714. zone->AppendCorner( center + rotatedPoint, 0 );
  715. }
  716. }
  717. zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
  718. ZONE::GetDefaultHatchPitch(), true );
  719. }
  720. else
  721. {
  722. PCB_LAYER_ID layer = kicad_layer( c.layer );
  723. if( layer != UNDEFINED_LAYER ) // unsupported layer
  724. {
  725. PCB_SHAPE* shape = new PCB_SHAPE( m_board, SHAPE_T::CIRCLE );
  726. m_board->Add( shape, ADD_MODE::APPEND );
  727. shape->SetFilled( false );
  728. shape->SetLayer( layer );
  729. shape->SetStart( VECTOR2I( kicad_x( c.x ), kicad_y( c.y ) ) );
  730. shape->SetEnd( VECTOR2I( kicad_x( c.x ) + radius, kicad_y( c.y ) ) );
  731. shape->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
  732. }
  733. }
  734. m_xpath->pop();
  735. }
  736. else if( grName == wxT( "rectangle" ) )
  737. {
  738. // This seems to be a simplified rectangular [copper] zone, cannot find any
  739. // net related info on it from the DTD.
  740. m_xpath->push( "rectangle" );
  741. ERECT r( gr );
  742. PCB_LAYER_ID layer = kicad_layer( r.layer );
  743. if( layer != UNDEFINED_LAYER )
  744. {
  745. ZONE* zone = new ZONE( m_board );
  746. m_board->Add( zone, ADD_MODE::APPEND );
  747. zone->SetLayer( layer );
  748. zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
  749. ZONE_BORDER_DISPLAY_STYLE outline_hatch = ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE;
  750. const int outlineIdx = -1; // this is the id of the copper zone main outline
  751. zone->AppendCorner( VECTOR2I( kicad_x( r.x1 ), kicad_y( r.y1 ) ), outlineIdx );
  752. zone->AppendCorner( VECTOR2I( kicad_x( r.x2 ), kicad_y( r.y1 ) ), outlineIdx );
  753. zone->AppendCorner( VECTOR2I( kicad_x( r.x2 ), kicad_y( r.y2 ) ), outlineIdx );
  754. zone->AppendCorner( VECTOR2I( kicad_x( r.x1 ), kicad_y( r.y2 ) ), outlineIdx );
  755. if( r.rot )
  756. {
  757. VECTOR2I center( ( kicad_x( r.x1 ) + kicad_x( r.x2 ) ) / 2,
  758. ( kicad_y( r.y1 ) + kicad_y( r.y2 ) ) / 2 );
  759. zone->Rotate( center, EDA_ANGLE( r.rot->degrees, DEGREES_T ) );
  760. }
  761. // this is not my fault:
  762. zone->SetBorderDisplayStyle( outline_hatch, ZONE::GetDefaultHatchPitch(), true );
  763. }
  764. m_xpath->pop();
  765. }
  766. else if( grName == wxT( "hole" ) )
  767. {
  768. m_xpath->push( "hole" );
  769. // Fabricate a FOOTPRINT with a single PAD_ATTRIB::NPTH pad.
  770. // Use m_hole_count to gen up a unique reference designator.
  771. FOOTPRINT* footprint = new FOOTPRINT( m_board );
  772. m_board->Add( footprint, ADD_MODE::APPEND );
  773. int hole_count = m_hole_count++;
  774. footprint->SetReference( wxString::Format( wxT( "UNK_HOLE_%d" ), hole_count ) );
  775. footprint->Reference().SetVisible( false );
  776. // Mandatory: gives a dummy but valid LIB_ID
  777. LIB_ID fpid( wxEmptyString, wxString::Format( wxT( "dummyfp%d" ), hole_count ) );
  778. footprint->SetFPID( fpid );
  779. packageHole( footprint, gr, true );
  780. m_xpath->pop();
  781. }
  782. else if( grName == wxT( "frame" ) )
  783. {
  784. // picture this
  785. }
  786. else if( grName == wxT( "polygon" ) )
  787. {
  788. m_xpath->push( "polygon" );
  789. loadPolygon( gr );
  790. m_xpath->pop(); // "polygon"
  791. }
  792. else if( grName == wxT( "dimension" ) )
  793. {
  794. const BOARD_DESIGN_SETTINGS& designSettings = m_board->GetDesignSettings();
  795. EDIMENSION d( gr );
  796. PCB_LAYER_ID layer = kicad_layer( d.layer );
  797. VECTOR2I pt1( kicad_x( d.x1 ), kicad_y( d.y1 ) );
  798. VECTOR2I pt2( kicad_x( d.x2 ), kicad_y( d.y2 ) );
  799. VECTOR2I pt3( kicad_x( d.x3 ), kicad_y( d.y3 ) );
  800. VECTOR2I textSize = designSettings.GetTextSize( layer );
  801. int textThickness = designSettings.GetLineThickness( layer );
  802. if( d.textsize )
  803. {
  804. double ratio = 8; // DTD says 8 is default
  805. textThickness = KiROUND( d.textsize->ToPcbUnits() * ratio / 100 );
  806. textSize = kicad_fontsize( *d.textsize, textThickness );
  807. }
  808. if( layer != UNDEFINED_LAYER )
  809. {
  810. if( d.dimensionType == wxT( "angle" ) )
  811. {
  812. // Kicad doesn't (at present) support angle dimensions
  813. }
  814. else if( d.dimensionType == wxT( "radius" ) )
  815. {
  816. PCB_DIM_RADIAL* dimension = new PCB_DIM_RADIAL( m_board );
  817. m_board->Add( dimension, ADD_MODE::APPEND );
  818. dimension->SetLayer( layer );
  819. dimension->SetPrecision( DIMENSION_PRECISION );
  820. dimension->SetStart( pt1 );
  821. dimension->SetEnd( pt2 );
  822. dimension->SetTextPos( pt3 );
  823. dimension->SetTextSize( textSize );
  824. dimension->SetTextThickness( textThickness );
  825. dimension->SetLineThickness( designSettings.GetLineThickness( layer ) );
  826. dimension->SetUnits( EDA_UNITS::MM );
  827. }
  828. else if( d.dimensionType == wxT( "leader" ) )
  829. {
  830. PCB_DIM_LEADER* leader = new PCB_DIM_LEADER( m_board );
  831. m_board->Add( leader, ADD_MODE::APPEND );
  832. leader->SetLayer( layer );
  833. leader->SetPrecision( DIMENSION_PRECISION );
  834. leader->SetStart( pt1 );
  835. leader->SetEnd( pt2 );
  836. leader->SetTextPos( pt3 );
  837. leader->SetTextSize( textSize );
  838. leader->SetTextThickness( textThickness );
  839. leader->SetOverrideText( wxEmptyString );
  840. leader->SetLineThickness( designSettings.GetLineThickness( layer ) );
  841. }
  842. else // horizontal, vertical, <default>, diameter
  843. {
  844. PCB_DIM_ALIGNED* dimension = new PCB_DIM_ALIGNED( m_board, PCB_DIM_ALIGNED_T );
  845. m_board->Add( dimension, ADD_MODE::APPEND );
  846. if( d.dimensionType )
  847. {
  848. // Eagle dimension graphic arms may have different lengths, but they look
  849. // incorrect in KiCad (the graphic is tilted). Make them even length in
  850. // such case.
  851. if( *d.dimensionType == wxT( "horizontal" ) )
  852. {
  853. int newY = ( pt1.y + pt2.y ) / 2;
  854. pt1.y = newY;
  855. pt2.y = newY;
  856. }
  857. else if( *d.dimensionType == wxT( "vertical" ) )
  858. {
  859. int newX = ( pt1.x + pt2.x ) / 2;
  860. pt1.x = newX;
  861. pt2.x = newX;
  862. }
  863. }
  864. dimension->SetLayer( layer );
  865. dimension->SetPrecision( DIMENSION_PRECISION );
  866. // The origin and end are assumed to always be in this order from eagle
  867. dimension->SetStart( pt1 );
  868. dimension->SetEnd( pt2 );
  869. dimension->SetTextSize( textSize );
  870. dimension->SetTextThickness( textThickness );
  871. dimension->SetLineThickness( designSettings.GetLineThickness( layer ) );
  872. dimension->SetUnits( EDA_UNITS::MM );
  873. // check which axis the dimension runs in
  874. // because the "height" of the dimension is perpendicular to that axis
  875. // Note the check is just if two axes are close enough to each other
  876. // Eagle appears to have some rounding errors
  877. if( abs( pt1.x - pt2.x ) < 50000 ) // 50000 nm = 0.05 mm
  878. {
  879. int offset = pt3.x - pt1.x;
  880. if( pt1.y > pt2.y )
  881. dimension->SetHeight( offset );
  882. else
  883. dimension->SetHeight( -offset );
  884. }
  885. else if( abs( pt1.y - pt2.y ) < 50000 )
  886. {
  887. int offset = pt3.y - pt1.y;
  888. if( pt1.x > pt2.x )
  889. dimension->SetHeight( -offset );
  890. else
  891. dimension->SetHeight( offset );
  892. }
  893. else
  894. {
  895. int offset = KiROUND( pt3.Distance( pt1 ) );
  896. if( pt1.y > pt2.y )
  897. dimension->SetHeight( offset );
  898. else
  899. dimension->SetHeight( -offset );
  900. }
  901. }
  902. }
  903. }
  904. // Get next graphic
  905. gr = gr->GetNext();
  906. }
  907. m_xpath->pop();
  908. }
  909. void PCB_IO_EAGLE::loadLibrary( wxXmlNode* aLib, const wxString* aLibName )
  910. {
  911. if( !aLib )
  912. return;
  913. wxString urn = aLib->GetAttribute( "urn" );
  914. wxString urnOrdinal;
  915. if( !urn.IsEmpty() )
  916. {
  917. urnOrdinal = urn.AfterLast( ':' );
  918. }
  919. // library will have <xmlattr> node, skip that and get the single packages node
  920. wxXmlNode* packages = MapChildren( aLib )["packages"];
  921. if( !packages )
  922. return;
  923. m_xpath->push( "packages" );
  924. // Create a FOOTPRINT for all the eagle packages, for use later via a copy constructor
  925. // to instantiate needed footprints in our BOARD. Save the FOOTPRINT templates in
  926. // a FOOTPRINT_MAP using a single lookup key consisting of libname+pkgname.
  927. // Get the first package and iterate
  928. wxXmlNode* package = packages->GetChildren();
  929. while( package )
  930. {
  931. checkpoint();
  932. m_xpath->push( "package", "name" );
  933. wxString pack_ref = package->GetAttribute( "name" );
  934. if( !urnOrdinal.IsEmpty() )
  935. pack_ref += wxS( "_" ) + urnOrdinal;
  936. ReplaceIllegalFileNameChars( pack_ref, '_' );
  937. m_xpath->Value( pack_ref.ToUTF8() );
  938. wxString key = aLibName ? makeKey( *aLibName, pack_ref ) : pack_ref;
  939. FOOTPRINT* footprint = makeFootprint( package, pack_ref );
  940. // add the templating FOOTPRINT to the FOOTPRINT template factory "m_templates"
  941. auto r = m_templates.insert( { key, footprint } );
  942. if( !r.second /* && !( m_props && m_props->Value( "ignore_duplicates" ) ) */ )
  943. {
  944. wxString lib = aLibName ? *aLibName : m_lib_path;
  945. const wxString& pkg = pack_ref;
  946. wxString emsg = wxString::Format( _( "<package> '%s' duplicated in <library> '%s'" ),
  947. pkg,
  948. lib );
  949. THROW_IO_ERROR( emsg );
  950. }
  951. m_xpath->pop();
  952. package = package->GetNext();
  953. }
  954. m_xpath->pop(); // "packages"
  955. }
  956. void PCB_IO_EAGLE::loadLibraries( wxXmlNode* aLibs )
  957. {
  958. if( !aLibs )
  959. return;
  960. m_xpath->push( "libraries.library", "name" );
  961. // Get the first library and iterate
  962. wxXmlNode* library = aLibs->GetChildren();
  963. while( library )
  964. {
  965. const wxString& lib_name = library->GetAttribute( "name" );
  966. m_xpath->Value( lib_name.c_str() );
  967. loadLibrary( library, &lib_name );
  968. library = library->GetNext();
  969. }
  970. m_xpath->pop();
  971. }
  972. void PCB_IO_EAGLE::loadElements( wxXmlNode* aElements )
  973. {
  974. if( !aElements )
  975. return;
  976. m_xpath->push( "elements.element", "name" );
  977. EATTR name;
  978. EATTR value;
  979. bool refanceNamePresetInPackageLayout;
  980. bool valueNamePresetInPackageLayout;
  981. // Get the first element and iterate
  982. wxXmlNode* element = aElements->GetChildren();
  983. while( element )
  984. {
  985. checkpoint();
  986. if( element->GetName() != wxT( "element" ) )
  987. {
  988. // Get next item
  989. element = element->GetNext();
  990. continue;
  991. }
  992. EELEMENT e( element );
  993. // use "NULL-ness" as an indication of presence of the attribute:
  994. EATTR* nameAttr = nullptr;
  995. EATTR* valueAttr = nullptr;
  996. m_xpath->Value( e.name.c_str() );
  997. wxString packageName = e.package;
  998. if( e.library_urn )
  999. packageName = e.package + wxS( "_" ) + e.library_urn->assetId;
  1000. wxString pkg_key = makeKey( e.library, packageName );
  1001. auto it = m_templates.find( pkg_key );
  1002. if( it == m_templates.end() )
  1003. {
  1004. wxString emsg = wxString::Format( _( "No '%s' package in library '%s'." ),
  1005. packageName, e.library );
  1006. THROW_IO_ERROR( emsg );
  1007. }
  1008. FOOTPRINT* footprint = static_cast<FOOTPRINT*>( it->second->Duplicate() );
  1009. m_board->Add( footprint, ADD_MODE::APPEND );
  1010. // update the nets within the pads of the clone
  1011. for( PAD* pad : footprint->Pads() )
  1012. {
  1013. wxString pn_key = makeKey( e.name, pad->GetNumber() );
  1014. NET_MAP_CITER ni = m_pads_to_nets.find( pn_key );
  1015. if( ni != m_pads_to_nets.end() )
  1016. {
  1017. const ENET* enet = &ni->second;
  1018. pad->SetNetCode( enet->netcode );
  1019. }
  1020. }
  1021. refanceNamePresetInPackageLayout = true;
  1022. valueNamePresetInPackageLayout = true;
  1023. footprint->SetPosition( VECTOR2I( kicad_x( e.x ), kicad_y( e.y ) ) );
  1024. // Is >NAME field set in package layout ?
  1025. if( footprint->GetReference().size() == 0 )
  1026. {
  1027. footprint->Reference().SetVisible( false ); // No so no show
  1028. refanceNamePresetInPackageLayout = false;
  1029. }
  1030. // Is >VALUE field set in package layout
  1031. if( footprint->GetValue().size() == 0 )
  1032. {
  1033. footprint->Value().SetVisible( false ); // No so no show
  1034. valueNamePresetInPackageLayout = false;
  1035. }
  1036. wxString reference = e.name;
  1037. // EAGLE allows references to be single digits. This breaks KiCad
  1038. // netlisting, which requires parts to have non-digit + digit
  1039. // annotation. If the reference begins with a number, we prepend
  1040. // 'UNK' (unknown) for the symbol designator.
  1041. if( reference.find_first_not_of( "0123456789" ) != 0 )
  1042. reference.Prepend( "UNK" );
  1043. // EAGLE allows designator to start with # but that is used in KiCad
  1044. // for symbols which do not have a footprint
  1045. if( reference.find_first_not_of( "#" ) != 0 )
  1046. reference.Prepend( "UNK" );
  1047. // reference must end with a number but EAGLE does not enforce this
  1048. if( reference.find_last_not_of( "0123456789" ) == (reference.Length()-1) )
  1049. reference.Append( "0" );
  1050. footprint->SetReference( reference );
  1051. footprint->SetValue( e.value );
  1052. if( !e.smashed )
  1053. {
  1054. // Not smashed so show NAME & VALUE
  1055. if( valueNamePresetInPackageLayout )
  1056. footprint->Value().SetVisible( true ); // Only if place holder in package layout
  1057. if( refanceNamePresetInPackageLayout )
  1058. footprint->Reference().SetVisible( true ); // Only if place holder in package layout
  1059. }
  1060. else if( *e.smashed == true )
  1061. {
  1062. // Smashed so set default to no show for NAME and VALUE
  1063. footprint->Value().SetVisible( false );
  1064. footprint->Reference().SetVisible( false );
  1065. // initialize these to default values in case the <attribute> elements are not present.
  1066. m_xpath->push( "attribute", "name" );
  1067. // VALUE and NAME can have something like our text "effects" overrides
  1068. // in SWEET and new schematic. Eagle calls these XML elements "attribute".
  1069. // There can be one for NAME and/or VALUE both. Features present in the
  1070. // EATTR override the ones established in the package only if they are
  1071. // present here (except for rot, which if not present means angle zero).
  1072. // So the logic is a bit different than in packageText() and in plain text.
  1073. // Get the first attribute and iterate
  1074. wxXmlNode* attribute = element->GetChildren();
  1075. while( attribute )
  1076. {
  1077. if( attribute->GetName() != wxT( "attribute" ) )
  1078. {
  1079. attribute = attribute->GetNext();
  1080. continue;
  1081. }
  1082. EATTR a( attribute );
  1083. if( a.name == wxT( "NAME" ) )
  1084. {
  1085. name = a;
  1086. nameAttr = &name;
  1087. // do we have a display attribute ?
  1088. if( a.display )
  1089. {
  1090. // Yes!
  1091. switch( *a.display )
  1092. {
  1093. case EATTR::VALUE :
  1094. {
  1095. nameAttr->name = reference;
  1096. if( refanceNamePresetInPackageLayout )
  1097. footprint->Reference().SetVisible( true );
  1098. break;
  1099. }
  1100. case EATTR::NAME :
  1101. if( refanceNamePresetInPackageLayout )
  1102. {
  1103. footprint->SetReference( "NAME" );
  1104. footprint->Reference().SetVisible( true );
  1105. }
  1106. break;
  1107. case EATTR::BOTH :
  1108. if( refanceNamePresetInPackageLayout )
  1109. footprint->Reference().SetVisible( true );
  1110. nameAttr->name = nameAttr->name + wxT( " = " ) + e.name;
  1111. footprint->SetReference( wxT( "NAME = " ) + e.name );
  1112. break;
  1113. case EATTR::Off :
  1114. footprint->Reference().SetVisible( false );
  1115. break;
  1116. default:
  1117. nameAttr->name = e.name;
  1118. if( refanceNamePresetInPackageLayout )
  1119. footprint->Reference().SetVisible( true );
  1120. }
  1121. }
  1122. else
  1123. {
  1124. // No display, so default is visible, and show value of NAME
  1125. footprint->Reference().SetVisible( true );
  1126. }
  1127. }
  1128. else if( a.name == wxT( "VALUE" ) )
  1129. {
  1130. value = a;
  1131. valueAttr = &value;
  1132. if( a.display )
  1133. {
  1134. // Yes!
  1135. switch( *a.display )
  1136. {
  1137. case EATTR::VALUE :
  1138. valueAttr->value = opt_wxString( e.value );
  1139. footprint->SetValue( e.value );
  1140. if( valueNamePresetInPackageLayout )
  1141. footprint->Value().SetVisible( true );
  1142. break;
  1143. case EATTR::NAME :
  1144. if( valueNamePresetInPackageLayout )
  1145. footprint->Value().SetVisible( true );
  1146. footprint->SetValue( wxT( "VALUE" ) );
  1147. break;
  1148. case EATTR::BOTH :
  1149. if( valueNamePresetInPackageLayout )
  1150. footprint->Value().SetVisible( true );
  1151. valueAttr->value = opt_wxString( wxT( "VALUE = " ) + e.value );
  1152. footprint->SetValue( wxT( "VALUE = " ) + e.value );
  1153. break;
  1154. case EATTR::Off :
  1155. footprint->Value().SetVisible( false );
  1156. break;
  1157. default:
  1158. valueAttr->value = opt_wxString( e.value );
  1159. if( valueNamePresetInPackageLayout )
  1160. footprint->Value().SetVisible( true );
  1161. }
  1162. }
  1163. else
  1164. {
  1165. // No display, so default is visible, and show value of NAME
  1166. footprint->Value().SetVisible( true );
  1167. }
  1168. }
  1169. attribute = attribute->GetNext();
  1170. }
  1171. m_xpath->pop(); // "attribute"
  1172. }
  1173. orientFootprintAndText( footprint, e, nameAttr, valueAttr );
  1174. // Get next element
  1175. element = element->GetNext();
  1176. }
  1177. m_xpath->pop(); // "elements.element"
  1178. }
  1179. ZONE* PCB_IO_EAGLE::loadPolygon( wxXmlNode* aPolyNode )
  1180. {
  1181. EPOLYGON p( aPolyNode );
  1182. PCB_LAYER_ID layer = kicad_layer( p.layer );
  1183. bool keepout = ( p.layer == EAGLE_LAYER::TRESTRICT
  1184. || p.layer == EAGLE_LAYER::BRESTRICT
  1185. || p.layer == EAGLE_LAYER::VRESTRICT );
  1186. if( layer == UNDEFINED_LAYER )
  1187. {
  1188. wxLogMessage( wxString::Format( _( "Ignoring a polygon since Eagle layer '%s' (%d) "
  1189. "was not mapped" ),
  1190. eagle_layer_name( p.layer ), p.layer ) );
  1191. return nullptr;
  1192. }
  1193. // use a "netcode = 0" type ZONE:
  1194. std::unique_ptr<ZONE> zone = std::make_unique<ZONE>( m_board );
  1195. if( !keepout )
  1196. zone->SetLayer( layer );
  1197. else
  1198. setKeepoutSettingsToZone( zone.get(), p.layer );
  1199. // Get the first vertex and iterate
  1200. wxXmlNode* vertex = aPolyNode->GetChildren();
  1201. std::vector<EVERTEX> vertices;
  1202. // Create a circular vector of vertices
  1203. // The "curve" parameter indicates a curve from the current
  1204. // to the next vertex, so we keep the first at the end as well
  1205. // to allow the curve to link back
  1206. while( vertex )
  1207. {
  1208. if( vertex->GetName() == wxT( "vertex" ) )
  1209. vertices.emplace_back( vertex );
  1210. vertex = vertex->GetNext();
  1211. }
  1212. // According to Eagle's doc, by default, the orphans (islands in KiCad parlance)
  1213. // are always removed
  1214. if( !p.orphans || !p.orphans.Get() )
  1215. zone->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::ALWAYS );
  1216. else
  1217. zone->SetIslandRemovalMode( ISLAND_REMOVAL_MODE::NEVER );
  1218. vertices.push_back( vertices[0] );
  1219. SHAPE_POLY_SET polygon;
  1220. polygon.NewOutline();
  1221. for( size_t i = 0; i < vertices.size() - 1; i++ )
  1222. {
  1223. EVERTEX v1 = vertices[i];
  1224. // Append the corner
  1225. polygon.Append( kicad_x( v1.x ), kicad_y( v1.y ) );
  1226. if( v1.curve )
  1227. {
  1228. EVERTEX v2 = vertices[i + 1];
  1229. VECTOR2I center =
  1230. ConvertArcCenter( VECTOR2I( kicad_x( v1.x ), kicad_y( v1.y ) ),
  1231. VECTOR2I( kicad_x( v2.x ), kicad_y( v2.y ) ), *v1.curve );
  1232. double angle = DEG2RAD( *v1.curve );
  1233. double end_angle = atan2( kicad_y( v2.y ) - center.y, kicad_x( v2.x ) - center.x );
  1234. double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 )
  1235. + pow( center.y - kicad_y( v1.y ), 2 ) );
  1236. int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF,
  1237. EDA_ANGLE( *v1.curve, DEGREES_T ) );
  1238. double delta_angle = angle / segCount;
  1239. for( double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta_angle );
  1240. a -= delta_angle )
  1241. {
  1242. polygon.Append( KiROUND( radius * cos( a ) ) + center.x,
  1243. KiROUND( radius * sin( a ) ) + center.y );
  1244. }
  1245. }
  1246. }
  1247. // Eagle traces the zone such that half of the pen width is outside the polygon.
  1248. // We trace the zone such that the copper is completely inside.
  1249. if( p.width.ToPcbUnits() > 0 )
  1250. {
  1251. polygon.Inflate( p.width.ToPcbUnits() / 2, CORNER_STRATEGY::ALLOW_ACUTE_CORNERS,
  1252. ARC_HIGH_DEF, true );
  1253. }
  1254. if( polygon.OutlineCount() != 1 )
  1255. {
  1256. wxLogMessage( wxString::Format(
  1257. _( "Skipping a polygon on layer '%s' (%d): outline count is not 1" ),
  1258. eagle_layer_name( p.layer ), p.layer ) );
  1259. return nullptr;
  1260. }
  1261. zone->AddPolygon( polygon.COutline( 0 ) );
  1262. // If the pour is a cutout it needs to be set to a keepout
  1263. if( p.pour == EPOLYGON::ECUTOUT )
  1264. {
  1265. zone->SetIsRuleArea( true );
  1266. zone->SetDoNotAllowVias( false );
  1267. zone->SetDoNotAllowTracks( false );
  1268. zone->SetDoNotAllowPads( false );
  1269. zone->SetDoNotAllowFootprints( false );
  1270. zone->SetDoNotAllowCopperPour( true );
  1271. zone->SetHatchStyle( ZONE_BORDER_DISPLAY_STYLE::NO_HATCH );
  1272. }
  1273. else if( p.pour == EPOLYGON::EHATCH )
  1274. {
  1275. int spacing = p.spacing ? p.spacing->ToPcbUnits() : 50 * pcbIUScale.IU_PER_MILS;
  1276. zone->SetFillMode( ZONE_FILL_MODE::HATCH_PATTERN );
  1277. zone->SetHatchThickness( p.width.ToPcbUnits() );
  1278. zone->SetHatchGap( spacing - p.width.ToPcbUnits() );
  1279. zone->SetHatchOrientation( ANGLE_0 );
  1280. }
  1281. // We divide the thickness by half because we are tracing _inside_ the zone outline
  1282. // This means the radius of curvature will be twice the size for an equivalent EAGLE zone
  1283. zone->SetMinThickness( std::max<int>( ZONE_THICKNESS_MIN_VALUE_MM * pcbIUScale.IU_PER_MM,
  1284. p.width.ToPcbUnits() / 2 ) );
  1285. if( p.isolate )
  1286. zone->SetLocalClearance( p.isolate->ToPcbUnits() );
  1287. else
  1288. zone->SetLocalClearance( 1 ); // @todo: set minimum clearance value based on board settings
  1289. // missing == yes per DTD.
  1290. bool thermals = !p.thermals || *p.thermals;
  1291. zone->SetPadConnection( thermals ? ZONE_CONNECTION::THERMAL : ZONE_CONNECTION::FULL );
  1292. if( thermals )
  1293. {
  1294. // FIXME: eagle calculates dimensions for thermal spokes
  1295. // based on what the zone is connecting to.
  1296. // (i.e. width of spoke is half of the smaller side of an smd pad)
  1297. // This is a basic workaround
  1298. zone->SetThermalReliefGap( p.width.ToPcbUnits() + 50000 ); // 50000nm == 0.05mm
  1299. zone->SetThermalReliefSpokeWidth( p.width.ToPcbUnits() + 50000 );
  1300. }
  1301. int rank = p.rank ? (p.max_priority - *p.rank) : p.max_priority;
  1302. zone->SetAssignedPriority( rank );
  1303. ZONE* zonePtr = zone.release();
  1304. m_board->Add( zonePtr, ADD_MODE::APPEND );
  1305. return zonePtr;
  1306. }
  1307. void PCB_IO_EAGLE::orientFootprintAndText( FOOTPRINT* aFootprint, const EELEMENT& e,
  1308. const EATTR* aNameAttr, const EATTR* aValueAttr )
  1309. {
  1310. if( e.rot )
  1311. {
  1312. if( e.rot->mirror )
  1313. {
  1314. aFootprint->SetOrientation( EDA_ANGLE( e.rot->degrees + 180.0, DEGREES_T ) );
  1315. aFootprint->Flip( aFootprint->GetPosition(), FLIP_DIRECTION::TOP_BOTTOM );
  1316. }
  1317. else
  1318. {
  1319. aFootprint->SetOrientation( EDA_ANGLE( e.rot->degrees, DEGREES_T ) );
  1320. }
  1321. }
  1322. orientFPText( aFootprint, e, &aFootprint->Reference(), aNameAttr );
  1323. orientFPText( aFootprint, e, &aFootprint->Value(), aValueAttr );
  1324. }
  1325. void PCB_IO_EAGLE::orientFPText( FOOTPRINT* aFootprint, const EELEMENT& e, PCB_TEXT* aFPText,
  1326. const EATTR* aAttr )
  1327. {
  1328. // Smashed part ?
  1329. if( aAttr )
  1330. {
  1331. // Yes
  1332. const EATTR& a = *aAttr;
  1333. if( a.value )
  1334. {
  1335. aFPText->SetText( *a.value );
  1336. }
  1337. if( a.x && a.y ) // std::optional
  1338. {
  1339. VECTOR2I pos( kicad_x( *a.x ), kicad_y( *a.y ) );
  1340. aFPText->SetTextPos( pos );
  1341. }
  1342. // Even though size and ratio are both optional, I am not seeing
  1343. // a case where ratio is present but size is not.
  1344. double ratio = 8;
  1345. if( a.ratio )
  1346. ratio = *a.ratio;
  1347. VECTOR2I fontz = aFPText->GetTextSize();
  1348. int textThickness = KiROUND( fontz.y * ratio / 100 );
  1349. aFPText->SetTextThickness( textThickness );
  1350. if( a.size )
  1351. {
  1352. fontz = kicad_fontsize( *a.size, textThickness );
  1353. aFPText->SetTextSize( fontz );
  1354. }
  1355. int align = ETEXT::BOTTOM_LEFT; // bottom-left is eagle default
  1356. if( a.align )
  1357. align = *a.align;
  1358. // The "rot" in a EATTR seems to be assumed to be zero if it is not
  1359. // present, and this zero rotation becomes an override to the
  1360. // package's text field. If they did not want zero, they specify
  1361. // what they want explicitly.
  1362. double degrees = a.rot ? a.rot->degrees : 0.0;
  1363. int sign = 1;
  1364. bool spin = false;
  1365. if( a.rot )
  1366. {
  1367. spin = a.rot->spin;
  1368. sign = a.rot->mirror ? -1 : 1;
  1369. aFPText->SetMirrored( a.rot->mirror );
  1370. }
  1371. if( degrees == 90 || degrees == 0 || spin )
  1372. {
  1373. degrees *= sign;
  1374. }
  1375. else if( degrees == 180 )
  1376. {
  1377. degrees = 0;
  1378. align = -align;
  1379. }
  1380. else if( degrees == 270 )
  1381. {
  1382. align = -align;
  1383. degrees = sign * 90;
  1384. }
  1385. else
  1386. {
  1387. degrees = 90 - (sign * degrees);
  1388. }
  1389. aFPText->SetTextAngle( EDA_ANGLE( degrees, DEGREES_T ) );
  1390. switch( align )
  1391. {
  1392. case ETEXT::TOP_RIGHT:
  1393. aFPText->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  1394. aFPText->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  1395. break;
  1396. case ETEXT::BOTTOM_LEFT:
  1397. aFPText->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1398. aFPText->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  1399. break;
  1400. case ETEXT::TOP_LEFT:
  1401. aFPText->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1402. aFPText->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  1403. break;
  1404. case ETEXT::BOTTOM_RIGHT:
  1405. aFPText->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  1406. aFPText->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  1407. break;
  1408. case ETEXT::TOP_CENTER:
  1409. aFPText->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
  1410. aFPText->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  1411. break;
  1412. case ETEXT::BOTTOM_CENTER:
  1413. aFPText->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
  1414. aFPText->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  1415. break;
  1416. case ETEXT::CENTER:
  1417. aFPText->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
  1418. aFPText->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1419. break;
  1420. case ETEXT::CENTER_LEFT:
  1421. aFPText->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1422. aFPText->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1423. break;
  1424. case ETEXT::CENTER_RIGHT:
  1425. aFPText->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  1426. aFPText->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1427. break;
  1428. default:
  1429. ;
  1430. }
  1431. // Refine justification and rotation for mirrored texts
  1432. if( aFPText->IsMirrored() && degrees < -90 && degrees >= -270 )
  1433. {
  1434. aFPText->SetTextAngle( EDA_ANGLE( 180+degrees, DEGREES_T ) );
  1435. if( aFPText->GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
  1436. aFPText->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  1437. else if( aFPText->GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  1438. aFPText->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1439. if( aFPText->GetVertJustify() == GR_TEXT_V_ALIGN_BOTTOM )
  1440. aFPText->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  1441. else if( aFPText->GetVertJustify() == GR_TEXT_V_ALIGN_TOP )
  1442. aFPText->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  1443. }
  1444. }
  1445. else
  1446. {
  1447. // Part is not smash so use Lib default for NAME/VALUE
  1448. // the text is per the original package, sans <attribute>.
  1449. double degrees = aFPText->GetTextAngle().AsDegrees()
  1450. + aFootprint->GetOrientation().AsDegrees();
  1451. // bottom-left is eagle default
  1452. aFPText->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1453. aFPText->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  1454. if( !aFPText->IsMirrored() && abs( degrees ) <= -180 )
  1455. {
  1456. aFPText->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  1457. aFPText->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  1458. }
  1459. }
  1460. }
  1461. FOOTPRINT* PCB_IO_EAGLE::makeFootprint( wxXmlNode* aPackage, const wxString& aPkgName )
  1462. {
  1463. std::unique_ptr<FOOTPRINT> m = std::make_unique<FOOTPRINT>( m_board );
  1464. LIB_ID fpID;
  1465. fpID.Parse( aPkgName, true );
  1466. m->SetFPID( fpID );
  1467. // Get the first package item and iterate
  1468. wxXmlNode* packageItem = aPackage->GetChildren();
  1469. // layer 27 is default layer for tValues
  1470. // set default layer for created footprint
  1471. PCB_LAYER_ID layer = kicad_layer( 27 );
  1472. m.get()->Value().SetLayer( layer );
  1473. while( packageItem )
  1474. {
  1475. const wxString& itemName = packageItem->GetName();
  1476. if( itemName == wxT( "description" ) )
  1477. {
  1478. wxString descr = convertDescription( UnescapeHTML( packageItem->GetNodeContent() ) );
  1479. m->SetLibDescription( descr );
  1480. }
  1481. else if( itemName == wxT( "wire" ) )
  1482. packageWire( m.get(), packageItem );
  1483. else if( itemName == wxT( "pad" ) )
  1484. packagePad( m.get(), packageItem );
  1485. else if( itemName == wxT( "text" ) )
  1486. packageText( m.get(), packageItem );
  1487. else if( itemName == wxT( "rectangle" ) )
  1488. packageRectangle( m.get(), packageItem );
  1489. else if( itemName == wxT( "polygon" ) )
  1490. packagePolygon( m.get(), packageItem );
  1491. else if( itemName == wxT( "circle" ) )
  1492. packageCircle( m.get(), packageItem );
  1493. else if( itemName == wxT( "hole" ) )
  1494. packageHole( m.get(), packageItem, false );
  1495. else if( itemName == wxT( "smd" ) )
  1496. packageSMD( m.get(), packageItem );
  1497. packageItem = packageItem->GetNext();
  1498. }
  1499. return m.release();
  1500. }
  1501. void PCB_IO_EAGLE::packageWire( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
  1502. {
  1503. EWIRE w( aTree );
  1504. PCB_LAYER_ID layer = kicad_layer( w.layer );
  1505. VECTOR2I start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
  1506. VECTOR2I end( kicad_x( w.x2 ), kicad_y( w.y2 ) );
  1507. int width = w.width.ToPcbUnits();
  1508. if( layer == UNDEFINED_LAYER )
  1509. {
  1510. wxLogMessage( wxString::Format( _( "Ignoring a wire since Eagle layer '%s' (%d) "
  1511. "was not mapped" ),
  1512. eagle_layer_name( w.layer ), w.layer ) );
  1513. return;
  1514. }
  1515. // KiCad cannot handle zero or negative line widths which apparently have meaning in Eagle.
  1516. if( width <= 0 )
  1517. {
  1518. BOARD* board = aFootprint->GetBoard();
  1519. if( board )
  1520. {
  1521. width = board->GetDesignSettings().GetLineThickness( layer );
  1522. }
  1523. else
  1524. {
  1525. // When loading footprint libraries, there is no board so use the default KiCad
  1526. // line widths.
  1527. switch( layer )
  1528. {
  1529. case Edge_Cuts: width = pcbIUScale.mmToIU( DEFAULT_EDGE_WIDTH ); break;
  1530. case F_SilkS:
  1531. case B_SilkS: width = pcbIUScale.mmToIU( DEFAULT_SILK_LINE_WIDTH ); break;
  1532. case F_CrtYd:
  1533. case B_CrtYd: width = pcbIUScale.mmToIU( DEFAULT_COURTYARD_WIDTH ); break;
  1534. default: width = pcbIUScale.mmToIU( DEFAULT_LINE_WIDTH ); break;
  1535. }
  1536. }
  1537. }
  1538. // FIXME: the cap attribute is ignored because KiCad can't create lines with flat ends.
  1539. PCB_SHAPE* dwg;
  1540. if( !w.curve )
  1541. {
  1542. dwg = new PCB_SHAPE( aFootprint, SHAPE_T::SEGMENT );
  1543. dwg->SetStart( start );
  1544. dwg->SetEnd( end );
  1545. }
  1546. else
  1547. {
  1548. dwg = new PCB_SHAPE( aFootprint, SHAPE_T::ARC );
  1549. VECTOR2I center = ConvertArcCenter( start, end, *w.curve );
  1550. dwg->SetCenter( center );
  1551. dwg->SetStart( start );
  1552. dwg->SetArcAngleAndEnd( -EDA_ANGLE( *w.curve, DEGREES_T ), true ); // KiCad rotates the other way
  1553. }
  1554. dwg->SetLayer( layer );
  1555. dwg->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
  1556. dwg->Rotate( { 0, 0 }, aFootprint->GetOrientation() );
  1557. dwg->Move( aFootprint->GetPosition() );
  1558. aFootprint->Add( dwg );
  1559. }
  1560. void PCB_IO_EAGLE::packagePad( FOOTPRINT* aFootprint, wxXmlNode* aTree )
  1561. {
  1562. // this is thru hole technology here, no SMDs
  1563. EPAD e( aTree );
  1564. int shape = EPAD::UNDEF;
  1565. int eagleDrillz = e.drill ? e.drill->ToPcbUnits() : 0;
  1566. std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
  1567. transferPad( e, pad.get() );
  1568. if( e.first && *e.first && m_rules->psFirst != EPAD::UNDEF )
  1569. shape = m_rules->psFirst;
  1570. else if( aFootprint->GetLayer() == F_Cu && m_rules->psTop != EPAD::UNDEF )
  1571. shape = m_rules->psTop;
  1572. else if( aFootprint->GetLayer() == B_Cu && m_rules->psBottom != EPAD::UNDEF )
  1573. shape = m_rules->psBottom;
  1574. pad->SetDrillSize( VECTOR2I( eagleDrillz, eagleDrillz ) );
  1575. pad->SetLayerSet( LSET::AllCuMask() );
  1576. if( eagleDrillz < m_min_hole )
  1577. m_min_hole = eagleDrillz;
  1578. // Solder mask
  1579. if( !e.stop || *e.stop == true ) // enabled by default
  1580. pad->SetLayerSet( pad->GetLayerSet().set( B_Mask ).set( F_Mask ) );
  1581. if( shape == EPAD::ROUND || shape == EPAD::SQUARE || shape == EPAD::OCTAGON )
  1582. e.shape = shape;
  1583. if( e.shape )
  1584. {
  1585. switch( *e.shape )
  1586. {
  1587. case EPAD::ROUND:
  1588. pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
  1589. break;
  1590. case EPAD::OCTAGON:
  1591. pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CHAMFERED_RECT );
  1592. pad->SetChamferPositions( PADSTACK::ALL_LAYERS, RECT_CHAMFER_ALL );
  1593. pad->SetChamferRectRatio( PADSTACK::ALL_LAYERS, 1 - M_SQRT1_2 ); // Regular polygon
  1594. break;
  1595. case EPAD::LONG:
  1596. pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::OVAL );
  1597. break;
  1598. case EPAD::SQUARE:
  1599. pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::RECTANGLE );
  1600. break;
  1601. case EPAD::OFFSET:
  1602. pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::OVAL );
  1603. break;
  1604. }
  1605. }
  1606. else
  1607. {
  1608. // if shape is not present, our default is circle and that matches their default "round"
  1609. }
  1610. if( e.diameter && e.diameter->value > 0 )
  1611. {
  1612. int diameter = e.diameter->ToPcbUnits();
  1613. pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( diameter, diameter ) );
  1614. }
  1615. else
  1616. {
  1617. double drillz = pad->GetDrillSize().x;
  1618. double annulus = drillz * m_rules->rvPadTop; // copper annulus, eagle "restring"
  1619. annulus = eagleClamp( m_rules->rlMinPadTop, annulus, m_rules->rlMaxPadTop );
  1620. int diameter = KiROUND( drillz + 2 * annulus );
  1621. pad->SetSize( PADSTACK::ALL_LAYERS, VECTOR2I( KiROUND( diameter ), KiROUND( diameter ) ) );
  1622. }
  1623. if( pad->GetShape( PADSTACK::ALL_LAYERS ) == PAD_SHAPE::OVAL )
  1624. {
  1625. // The Eagle "long" pad is wider than it is tall; m_elongation is percent elongation
  1626. VECTOR2I sz = pad->GetSize( PADSTACK::ALL_LAYERS );
  1627. sz.x = ( sz.x * ( 100 + m_rules->psElongationLong ) ) / 100;
  1628. pad->SetSize( PADSTACK::ALL_LAYERS, sz );
  1629. if( e.shape && *e.shape == EPAD::OFFSET )
  1630. {
  1631. int offset = KiROUND( ( sz.x - sz.y ) / 2.0 );
  1632. pad->SetOffset( PADSTACK::ALL_LAYERS, VECTOR2I( offset, 0 ) );
  1633. }
  1634. }
  1635. if( e.rot )
  1636. pad->SetOrientation( EDA_ANGLE( e.rot->degrees, DEGREES_T ) );
  1637. // Eagle spokes are always '+'
  1638. pad->SetThermalSpokeAngle( ANGLE_0 );
  1639. if( pad->GetSizeX() > 0 && pad->GetSizeY() > 0 && pad->HasHole() )
  1640. {
  1641. aFootprint->Add( pad.release() );
  1642. }
  1643. else
  1644. {
  1645. wxFileName fileName( m_lib_path );
  1646. if( m_board)
  1647. wxLogError( _( "Invalid zero-sized pad ignored in\nfile: %s" ), m_board->GetFileName() );
  1648. else
  1649. wxLogError( _( "Invalid zero-sized pad ignored in\nfile: %s" ), fileName.GetFullName() );
  1650. }
  1651. }
  1652. void PCB_IO_EAGLE::packageText( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
  1653. {
  1654. ETEXT t( aTree );
  1655. PCB_LAYER_ID layer = kicad_layer( t.layer );
  1656. if( layer == UNDEFINED_LAYER )
  1657. {
  1658. wxLogMessage( wxString::Format( _( "Ignoring a text since Eagle layer '%s' (%d) "
  1659. "was not mapped" ),
  1660. eagle_layer_name( t.layer ), t.layer ) );
  1661. return;
  1662. }
  1663. PCB_TEXT* textItem;
  1664. if( t.text.Upper() == wxT( ">NAME" ) && aFootprint->GetReference().IsEmpty() )
  1665. {
  1666. textItem = &aFootprint->Reference();
  1667. textItem->SetText( wxT( "REF**" ) );
  1668. }
  1669. else if( t.text.Upper() == wxT( ">VALUE" ) && aFootprint->GetValue().IsEmpty() )
  1670. {
  1671. textItem = &aFootprint->Value();
  1672. textItem->SetText( aFootprint->GetFPID().GetLibItemName() );
  1673. }
  1674. else
  1675. {
  1676. textItem = new PCB_TEXT( aFootprint );
  1677. aFootprint->Add( textItem );
  1678. textItem->SetText( interpretText( t.text ) );
  1679. }
  1680. VECTOR2I pos( kicad_x( t.x ), kicad_y( t.y ) );
  1681. textItem->SetPosition( pos );
  1682. textItem->SetLayer( layer );
  1683. double ratio = t.ratio ? *t.ratio : 8; // DTD says 8 is default
  1684. int textThickness = KiROUND( t.size.ToPcbUnits() * ratio / 100 );
  1685. textItem->SetTextThickness( textThickness );
  1686. textItem->SetTextSize( kicad_fontsize( t.size, textThickness ) );
  1687. textItem->SetKeepUpright( false );
  1688. int align = t.align ? *t.align : ETEXT::BOTTOM_LEFT; // bottom-left is eagle default
  1689. // An eagle package is never rotated, the DTD does not allow it.
  1690. // angle -= aFootprint->GetOrienation();
  1691. if( t.rot )
  1692. {
  1693. int sign = t.rot->mirror ? -1 : 1;
  1694. textItem->SetMirrored( t.rot->mirror );
  1695. double degrees = t.rot->degrees;
  1696. textItem->SetTextAngle( EDA_ANGLE( sign * degrees, DEGREES_T ) );
  1697. }
  1698. switch( align )
  1699. {
  1700. case ETEXT::CENTER:
  1701. textItem->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
  1702. textItem->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1703. break;
  1704. case ETEXT::CENTER_LEFT:
  1705. textItem->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1706. textItem->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1707. break;
  1708. case ETEXT::CENTER_RIGHT:
  1709. textItem->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  1710. textItem->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1711. break;
  1712. case ETEXT::TOP_CENTER:
  1713. textItem->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
  1714. textItem->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  1715. break;
  1716. case ETEXT::TOP_LEFT:
  1717. textItem->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1718. textItem->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  1719. break;
  1720. case ETEXT::TOP_RIGHT:
  1721. textItem->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  1722. textItem->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  1723. break;
  1724. case ETEXT::BOTTOM_CENTER:
  1725. textItem->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
  1726. textItem->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  1727. break;
  1728. case ETEXT::BOTTOM_LEFT:
  1729. textItem->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1730. textItem->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  1731. break;
  1732. case ETEXT::BOTTOM_RIGHT:
  1733. textItem->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  1734. textItem->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  1735. break;
  1736. }
  1737. }
  1738. void PCB_IO_EAGLE::packageRectangle( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
  1739. {
  1740. ERECT r( aTree );
  1741. if( r.layer == EAGLE_LAYER::TRESTRICT || r.layer == EAGLE_LAYER::BRESTRICT
  1742. || r.layer == EAGLE_LAYER::VRESTRICT )
  1743. {
  1744. ZONE* zone = new ZONE( aFootprint );
  1745. aFootprint->Add( zone, ADD_MODE::APPEND );
  1746. setKeepoutSettingsToZone( zone, r.layer );
  1747. const int outlineIdx = -1; // this is the id of the copper zone main outline
  1748. zone->AppendCorner( VECTOR2I( kicad_x( r.x1 ), kicad_y( r.y1 ) ), outlineIdx );
  1749. zone->AppendCorner( VECTOR2I( kicad_x( r.x2 ), kicad_y( r.y1 ) ), outlineIdx );
  1750. zone->AppendCorner( VECTOR2I( kicad_x( r.x2 ), kicad_y( r.y2 ) ), outlineIdx );
  1751. zone->AppendCorner( VECTOR2I( kicad_x( r.x1 ), kicad_y( r.y2 ) ), outlineIdx );
  1752. if( r.rot )
  1753. {
  1754. VECTOR2I center( ( kicad_x( r.x1 ) + kicad_x( r.x2 ) ) / 2,
  1755. ( kicad_y( r.y1 ) + kicad_y( r.y2 ) ) / 2 );
  1756. zone->Rotate( center, EDA_ANGLE( r.rot->degrees, DEGREES_T ) );
  1757. }
  1758. zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
  1759. ZONE::GetDefaultHatchPitch(), true );
  1760. }
  1761. else
  1762. {
  1763. PCB_LAYER_ID layer = kicad_layer( r.layer );
  1764. if( layer == UNDEFINED_LAYER )
  1765. {
  1766. wxLogMessage( wxString::Format( _( "Ignoring a rectangle since Eagle layer '%s' (%d) "
  1767. "was not mapped" ),
  1768. eagle_layer_name( r.layer ), r.layer ) );
  1769. return;
  1770. }
  1771. PCB_SHAPE* dwg = new PCB_SHAPE( aFootprint, SHAPE_T::POLY );
  1772. aFootprint->Add( dwg );
  1773. dwg->SetLayer( layer );
  1774. dwg->SetStroke( STROKE_PARAMS( 0 ) );
  1775. dwg->SetFilled( true );
  1776. std::vector<VECTOR2I> pts;
  1777. VECTOR2I start( VECTOR2I( kicad_x( r.x1 ), kicad_y( r.y1 ) ) );
  1778. VECTOR2I end( VECTOR2I( kicad_x( r.x1 ), kicad_y( r.y2 ) ) );
  1779. pts.push_back( start );
  1780. pts.emplace_back( kicad_x( r.x2 ), kicad_y( r.y1 ) );
  1781. pts.emplace_back( kicad_x( r.x2 ), kicad_y( r.y2 ) );
  1782. pts.push_back( end );
  1783. dwg->SetPolyPoints( pts );
  1784. if( r.rot )
  1785. dwg->Rotate( dwg->GetCenter(), EDA_ANGLE( r.rot->degrees, DEGREES_T ) );
  1786. dwg->Rotate( { 0, 0 }, aFootprint->GetOrientation() );
  1787. dwg->Move( aFootprint->GetPosition() );
  1788. }
  1789. }
  1790. void PCB_IO_EAGLE::packagePolygon( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
  1791. {
  1792. EPOLYGON p( aTree );
  1793. std::vector<VECTOR2I> pts;
  1794. // Get the first vertex and iterate
  1795. wxXmlNode* vertex = aTree->GetChildren();
  1796. std::vector<EVERTEX> vertices;
  1797. // Create a circular vector of vertices
  1798. // The "curve" parameter indicates a curve from the current
  1799. // to the next vertex, so we keep the first at the end as well
  1800. // to allow the curve to link back
  1801. while( vertex )
  1802. {
  1803. if( vertex->GetName() == wxT( "vertex" ) )
  1804. vertices.emplace_back( vertex );
  1805. vertex = vertex->GetNext();
  1806. }
  1807. vertices.push_back( vertices[0] );
  1808. for( size_t i = 0; i < vertices.size() - 1; i++ )
  1809. {
  1810. EVERTEX v1 = vertices[i];
  1811. // Append the corner
  1812. pts.emplace_back( kicad_x( v1.x ), kicad_y( v1.y ) );
  1813. if( v1.curve )
  1814. {
  1815. EVERTEX v2 = vertices[i + 1];
  1816. VECTOR2I center = ConvertArcCenter( VECTOR2I( kicad_x( v1.x ), kicad_y( v1.y ) ),
  1817. VECTOR2I( kicad_x( v2.x ), kicad_y( v2.y ) ),
  1818. *v1.curve );
  1819. double angle = DEG2RAD( *v1.curve );
  1820. double end_angle = atan2( kicad_y( v2.y ) - center.y, kicad_x( v2.x ) - center.x );
  1821. double radius = sqrt( pow( center.x - kicad_x( v1.x ), 2 )
  1822. + pow( center.y - kicad_y( v1.y ), 2 ) );
  1823. // Don't allow a zero-radius curve
  1824. if( KiROUND( radius ) == 0 )
  1825. radius = 1.0;
  1826. int segCount = GetArcToSegmentCount( KiROUND( radius ), ARC_HIGH_DEF,
  1827. EDA_ANGLE( *v1.curve, DEGREES_T ) );
  1828. double delta = angle / segCount;
  1829. for( double a = end_angle + angle; fabs( a - end_angle ) > fabs( delta ); a -= delta )
  1830. {
  1831. pts.push_back( VECTOR2I( KiROUND( radius * cos( a ) ),
  1832. KiROUND( radius * sin( a ) ) ) + center );
  1833. }
  1834. }
  1835. }
  1836. PCB_LAYER_ID layer = kicad_layer( p.layer );
  1837. if( ( p.pour == EPOLYGON::ECUTOUT && layer != UNDEFINED_LAYER )
  1838. || p.layer == EAGLE_LAYER::TRESTRICT
  1839. || p.layer == EAGLE_LAYER::BRESTRICT
  1840. || p.layer == EAGLE_LAYER::VRESTRICT )
  1841. {
  1842. ZONE* zone = new ZONE( aFootprint );
  1843. aFootprint->Add( zone, ADD_MODE::APPEND );
  1844. setKeepoutSettingsToZone( zone, p.layer );
  1845. SHAPE_LINE_CHAIN outline( pts );
  1846. outline.SetClosed( true );
  1847. zone->Outline()->AddOutline( outline );
  1848. zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
  1849. ZONE::GetDefaultHatchPitch(), true );
  1850. }
  1851. else
  1852. {
  1853. if( layer == UNDEFINED_LAYER )
  1854. {
  1855. wxLogMessage( wxString::Format( _( "Ignoring a polygon since Eagle layer '%s' (%d) "
  1856. "was not mapped" ),
  1857. eagle_layer_name( p.layer ),
  1858. p.layer ) );
  1859. return;
  1860. }
  1861. PCB_SHAPE* dwg = new PCB_SHAPE( aFootprint, SHAPE_T::POLY );
  1862. aFootprint->Add( dwg );
  1863. dwg->SetStroke( STROKE_PARAMS( 0 ) );
  1864. dwg->SetFilled( true );
  1865. dwg->SetLayer( layer );
  1866. dwg->SetPolyPoints( pts );
  1867. dwg->Rotate( { 0, 0 }, aFootprint->GetOrientation() );
  1868. dwg->Move( aFootprint->GetPosition() );
  1869. dwg->GetPolyShape().Inflate( p.width.ToPcbUnits() / 2, CORNER_STRATEGY::ALLOW_ACUTE_CORNERS,
  1870. ARC_HIGH_DEF );
  1871. }
  1872. }
  1873. void PCB_IO_EAGLE::packageCircle( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
  1874. {
  1875. ECIRCLE e( aTree );
  1876. int width = e.width.ToPcbUnits();
  1877. int radius = e.radius.ToPcbUnits();
  1878. if( e.layer == EAGLE_LAYER::TRESTRICT
  1879. || e.layer == EAGLE_LAYER::BRESTRICT
  1880. || e.layer == EAGLE_LAYER::VRESTRICT )
  1881. {
  1882. ZONE* zone = new ZONE( aFootprint );
  1883. aFootprint->Add( zone, ADD_MODE::APPEND );
  1884. setKeepoutSettingsToZone( zone, e.layer );
  1885. // approximate circle as polygon
  1886. VECTOR2I center( kicad_x( e.x ), kicad_y( e.y ) );
  1887. int outlineRadius = radius + ( width / 2 );
  1888. int segsInCircle = GetArcToSegmentCount( outlineRadius, ARC_HIGH_DEF, FULL_CIRCLE );
  1889. EDA_ANGLE delta = ANGLE_360 / segsInCircle;
  1890. for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += delta )
  1891. {
  1892. VECTOR2I rotatedPoint( outlineRadius, 0 );
  1893. RotatePoint( rotatedPoint, angle );
  1894. zone->AppendCorner( center + rotatedPoint, -1 );
  1895. }
  1896. if( width > 0 )
  1897. {
  1898. zone->NewHole();
  1899. int innerRadius = radius - ( width / 2 );
  1900. segsInCircle = GetArcToSegmentCount( innerRadius, ARC_HIGH_DEF, FULL_CIRCLE );
  1901. delta = ANGLE_360 / segsInCircle;
  1902. for( EDA_ANGLE angle = ANGLE_0; angle < ANGLE_360; angle += delta )
  1903. {
  1904. VECTOR2I rotatedPoint( innerRadius, 0 );
  1905. RotatePoint( rotatedPoint, angle );
  1906. zone->AppendCorner( center + rotatedPoint, 0 );
  1907. }
  1908. }
  1909. zone->SetBorderDisplayStyle( ZONE_BORDER_DISPLAY_STYLE::DIAGONAL_EDGE,
  1910. ZONE::GetDefaultHatchPitch(), true );
  1911. }
  1912. else
  1913. {
  1914. PCB_LAYER_ID layer = kicad_layer( e.layer );
  1915. if( layer == UNDEFINED_LAYER )
  1916. {
  1917. wxLogMessage( wxString::Format( _( "Ignoring a circle since Eagle layer '%s' (%d) "
  1918. "was not mapped" ),
  1919. eagle_layer_name( e.layer ),
  1920. e.layer ) );
  1921. return;
  1922. }
  1923. PCB_SHAPE* gr = new PCB_SHAPE( aFootprint, SHAPE_T::CIRCLE );
  1924. // width == 0 means filled circle
  1925. if( width <= 0 )
  1926. {
  1927. width = radius;
  1928. radius = radius / 2;
  1929. gr->SetFilled( true );
  1930. }
  1931. aFootprint->Add( gr );
  1932. gr->SetStroke( STROKE_PARAMS( width, LINE_STYLE::SOLID ) );
  1933. switch( (int) layer )
  1934. {
  1935. case UNDEFINED_LAYER:
  1936. layer = Cmts_User;
  1937. break;
  1938. default:
  1939. break;
  1940. }
  1941. gr->SetLayer( layer );
  1942. gr->SetStart( VECTOR2I( kicad_x( e.x ), kicad_y( e.y ) ) );
  1943. gr->SetEnd( VECTOR2I( kicad_x( e.x ) + radius, kicad_y( e.y ) ) );
  1944. gr->Rotate( { 0, 0 }, aFootprint->GetOrientation() );
  1945. gr->Move( aFootprint->GetPosition() );
  1946. }
  1947. }
  1948. void PCB_IO_EAGLE::packageHole( FOOTPRINT* aFootprint, wxXmlNode* aTree, bool aCenter ) const
  1949. {
  1950. EHOLE e( aTree );
  1951. if( e.drill.value == 0 )
  1952. return;
  1953. // we add a PAD_ATTRIB::NPTH pad to this footprint.
  1954. PAD* pad = new PAD( aFootprint );
  1955. aFootprint->Add( pad );
  1956. pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::CIRCLE );
  1957. pad->SetAttribute( PAD_ATTRIB::NPTH );
  1958. // Mechanical purpose only:
  1959. // no offset, no net name, no pad name allowed
  1960. // pad->SetOffset( VECTOR2I( 0, 0 ) );
  1961. // pad->SetNumber( wxEmptyString );
  1962. VECTOR2I padpos( kicad_x( e.x ), kicad_y( e.y ) );
  1963. if( aCenter )
  1964. {
  1965. aFootprint->SetPosition( padpos );
  1966. pad->SetPosition( padpos );
  1967. }
  1968. else
  1969. {
  1970. pad->SetPosition( padpos + aFootprint->GetPosition() );
  1971. }
  1972. VECTOR2I sz( e.drill.ToPcbUnits(), e.drill.ToPcbUnits() );
  1973. pad->SetDrillSize( sz );
  1974. pad->SetSize( PADSTACK::ALL_LAYERS, sz );
  1975. pad->SetLayerSet( LSET::AllCuMask().set( B_Mask ).set( F_Mask ) );
  1976. }
  1977. void PCB_IO_EAGLE::packageSMD( FOOTPRINT* aFootprint, wxXmlNode* aTree ) const
  1978. {
  1979. ESMD e( aTree );
  1980. PCB_LAYER_ID layer = kicad_layer( e.layer );
  1981. if( !IsCopperLayer( layer ) || e.dx.value == 0 || e.dy.value == 0 )
  1982. return;
  1983. PAD* pad = new PAD( aFootprint );
  1984. aFootprint->Add( pad );
  1985. transferPad( e, pad );
  1986. pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::RECTANGLE );
  1987. pad->SetAttribute( PAD_ATTRIB::SMD );
  1988. VECTOR2I padSize( e.dx.ToPcbUnits(), e.dy.ToPcbUnits() );
  1989. pad->SetSize( PADSTACK::ALL_LAYERS, padSize );
  1990. pad->SetLayer( layer );
  1991. const LSET front( { F_Cu, F_Paste, F_Mask } );
  1992. const LSET back( { B_Cu, B_Paste, B_Mask } );
  1993. if( layer == F_Cu )
  1994. pad->SetLayerSet( front );
  1995. else if( layer == B_Cu )
  1996. pad->SetLayerSet( back );
  1997. int minPadSize = std::min( padSize.x, padSize.y );
  1998. // Rounded rectangle pads
  1999. int roundRadius = eagleClamp( m_rules->srMinRoundness * 2,
  2000. (int) ( minPadSize * m_rules->srRoundness ),
  2001. m_rules->srMaxRoundness * 2 );
  2002. if( e.roundness || roundRadius > 0 )
  2003. {
  2004. double roundRatio = (double) roundRadius / minPadSize / 2.0;
  2005. // Eagle uses a different definition of roundness, hence division by 200
  2006. if( e.roundness )
  2007. roundRatio = std::fmax( *e.roundness / 200.0, roundRatio );
  2008. pad->SetShape( PADSTACK::ALL_LAYERS, PAD_SHAPE::ROUNDRECT );
  2009. pad->SetRoundRectRadiusRatio( PADSTACK::ALL_LAYERS, roundRatio );
  2010. }
  2011. if( e.rot )
  2012. pad->SetOrientation( EDA_ANGLE( e.rot->degrees, DEGREES_T ) );
  2013. // Eagle spokes are always '+'
  2014. pad->SetThermalSpokeAngle( ANGLE_0 );
  2015. pad->SetLocalSolderPasteMargin( -eagleClamp( m_rules->mlMinCreamFrame,
  2016. (int) ( m_rules->mvCreamFrame * minPadSize ),
  2017. m_rules->mlMaxCreamFrame ) );
  2018. // Solder mask
  2019. if( e.stop && *e.stop == false ) // enabled by default
  2020. {
  2021. if( layer == F_Cu )
  2022. pad->SetLayerSet( pad->GetLayerSet().set( F_Mask, false ) );
  2023. else if( layer == B_Cu )
  2024. pad->SetLayerSet( pad->GetLayerSet().set( B_Mask, false ) );
  2025. }
  2026. // Solder paste (only for SMD pads)
  2027. if( e.cream && *e.cream == false ) // enabled by default
  2028. {
  2029. if( layer == F_Cu )
  2030. pad->SetLayerSet( pad->GetLayerSet().set( F_Paste, false ) );
  2031. else if( layer == B_Cu )
  2032. pad->SetLayerSet( pad->GetLayerSet().set( B_Paste, false ) );
  2033. }
  2034. }
  2035. void PCB_IO_EAGLE::transferPad( const EPAD_COMMON& aEaglePad, PAD* aPad ) const
  2036. {
  2037. aPad->SetNumber( aEaglePad.name );
  2038. VECTOR2I padPos( kicad_x( aEaglePad.x ), kicad_y( aEaglePad.y ) );
  2039. // Solder mask
  2040. const VECTOR2I& padSize( aPad->GetSize( PADSTACK::ALL_LAYERS ) );
  2041. aPad->SetLocalSolderMaskMargin(
  2042. eagleClamp( m_rules->mlMinStopFrame,
  2043. (int) ( m_rules->mvStopFrame * std::min( padSize.x, padSize.y ) ),
  2044. m_rules->mlMaxStopFrame ) );
  2045. // Solid connection to copper zones
  2046. if( aEaglePad.thermals && !*aEaglePad.thermals )
  2047. aPad->SetLocalZoneConnection( ZONE_CONNECTION::FULL );
  2048. FOOTPRINT* footprint = aPad->GetParentFootprint();
  2049. wxCHECK( footprint, /* void */ );
  2050. RotatePoint( padPos, footprint->GetOrientation() );
  2051. aPad->SetPosition( padPos + footprint->GetPosition() );
  2052. }
  2053. void PCB_IO_EAGLE::deleteTemplates()
  2054. {
  2055. for( const auto& [ name, footprint ] : m_templates )
  2056. {
  2057. footprint->SetParent( nullptr );
  2058. delete footprint;
  2059. }
  2060. m_templates.clear();
  2061. }
  2062. void PCB_IO_EAGLE::loadClasses( wxXmlNode* aClasses )
  2063. {
  2064. // Eagle board DTD defines the "classes" element as 0 or 1.
  2065. if( !aClasses )
  2066. return;
  2067. BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
  2068. m_xpath->push( "classes.class", "number" );
  2069. std::vector<ECLASS> eClasses;
  2070. wxXmlNode* classNode = aClasses->GetChildren();
  2071. while( classNode )
  2072. {
  2073. checkpoint();
  2074. ECLASS eClass( classNode );
  2075. std::shared_ptr<NETCLASS> netclass;
  2076. if( eClass.name.CmpNoCase( wxT( "default" ) ) == 0 )
  2077. {
  2078. netclass = bds.m_NetSettings->GetDefaultNetclass();
  2079. }
  2080. else
  2081. {
  2082. netclass.reset( new NETCLASS( eClass.name ) );
  2083. bds.m_NetSettings->SetNetclass( eClass.name, netclass );
  2084. }
  2085. netclass->SetTrackWidth( INT_MAX );
  2086. netclass->SetViaDiameter( INT_MAX );
  2087. netclass->SetViaDrill( INT_MAX );
  2088. eClasses.emplace_back( eClass );
  2089. m_classMap[ eClass.number ] = netclass;
  2090. // Get next class
  2091. classNode = classNode->GetNext();
  2092. }
  2093. m_customRules = wxT( "(version 1)" );
  2094. for( ECLASS& eClass : eClasses )
  2095. {
  2096. for( std::pair<const wxString&, ECOORD> entry : eClass.clearanceMap )
  2097. {
  2098. if( m_classMap[ entry.first ] != nullptr )
  2099. {
  2100. wxString rule;
  2101. rule.Printf( wxT( "(rule \"class %s:%s\"\n"
  2102. " (condition \"A.NetClass == '%s' && B.NetClass == '%s'\")\n"
  2103. " (constraint clearance (min %smm)))\n" ),
  2104. eClass.number,
  2105. entry.first,
  2106. eClass.name,
  2107. m_classMap[ entry.first ]->GetName(),
  2108. EDA_UNIT_UTILS::UI::StringFromValue( pcbIUScale, EDA_UNITS::MM, entry.second.ToPcbUnits() ) );
  2109. m_customRules += wxT( "\n" ) + rule;
  2110. }
  2111. }
  2112. }
  2113. m_xpath->pop(); // "classes.class"
  2114. }
  2115. void PCB_IO_EAGLE::loadSignals( wxXmlNode* aSignals )
  2116. {
  2117. // Eagle board DTD defines the "signals" element as 0 or 1.
  2118. if( !aSignals )
  2119. return;
  2120. ZONES zones; // per net
  2121. int netCode = 1;
  2122. m_xpath->push( "signals.signal", "name" );
  2123. // Get the first signal and iterate
  2124. wxXmlNode* net = aSignals->GetChildren();
  2125. while( net )
  2126. {
  2127. checkpoint();
  2128. bool sawPad = false;
  2129. zones.clear();
  2130. const wxString& netName = escapeName( net->GetAttribute( "name" ) );
  2131. NETINFO_ITEM* netInfo = new NETINFO_ITEM( m_board, netName, netCode );
  2132. std::shared_ptr<NETCLASS> netclass;
  2133. if( net->HasAttribute( "class" ) )
  2134. {
  2135. auto netclassIt = m_classMap.find( net->GetAttribute( "class" ) );
  2136. if( netclassIt != m_classMap.end() )
  2137. {
  2138. m_board->GetDesignSettings().m_NetSettings->SetNetclassPatternAssignment(
  2139. netName, netclassIt->second->GetName() );
  2140. netInfo->SetNetClass( netclassIt->second );
  2141. netclass = netclassIt->second;
  2142. }
  2143. }
  2144. m_board->Add( netInfo );
  2145. m_xpath->Value( netName.c_str() );
  2146. // Get the first net item and iterate
  2147. wxXmlNode* netItem = net->GetChildren();
  2148. // (contactref | polygon | wire | via)*
  2149. while( netItem )
  2150. {
  2151. const wxString& itemName = netItem->GetName();
  2152. if( itemName == wxT( "wire" ) )
  2153. {
  2154. m_xpath->push( "wire" );
  2155. EWIRE w( netItem );
  2156. PCB_LAYER_ID layer = kicad_layer( w.layer );
  2157. if( IsCopperLayer( layer ) )
  2158. {
  2159. VECTOR2I start( kicad_x( w.x1 ), kicad_y( w.y1 ) );
  2160. VECTOR2I end( kicad_x( w.x2 ), kicad_y( w.y2 ) );
  2161. int width = w.width.ToPcbUnits();
  2162. if( width < m_min_trace )
  2163. m_min_trace = width;
  2164. if( netclass && width < netclass->GetTrackWidth() )
  2165. netclass->SetTrackWidth( width );
  2166. if( w.curve )
  2167. {
  2168. VECTOR2I center = ConvertArcCenter( start, end, *w.curve );
  2169. double radius = sqrt( pow( center.x - kicad_x( w.x1 ), 2 ) +
  2170. pow( center.y - kicad_y( w.y1 ), 2 ) );
  2171. VECTOR2I mid = CalcArcMid( start, end, center, true );
  2172. VECTOR2I otherMid = CalcArcMid( start, end, center, false );
  2173. double radiusA = ( mid - center ).EuclideanNorm();
  2174. double radiusB = ( otherMid - center ).EuclideanNorm();
  2175. if( abs( radiusA - radius ) > abs( radiusB - radius ) )
  2176. std::swap( mid, otherMid );
  2177. PCB_ARC* arc = new PCB_ARC( m_board );
  2178. arc->SetPosition( start );
  2179. arc->SetMid( mid );
  2180. arc->SetEnd( end );
  2181. arc->SetWidth( width );
  2182. arc->SetLayer( layer );
  2183. arc->SetNetCode( netCode );
  2184. m_board->Add( arc );
  2185. }
  2186. else
  2187. {
  2188. PCB_TRACK* track = new PCB_TRACK( m_board );
  2189. track->SetPosition( start );
  2190. track->SetEnd( VECTOR2I( kicad_x( w.x2 ), kicad_y( w.y2 ) ) );
  2191. track->SetWidth( width );
  2192. track->SetLayer( layer );
  2193. track->SetNetCode( netCode );
  2194. m_board->Add( track );
  2195. }
  2196. }
  2197. else
  2198. {
  2199. // put non copper wires where the sun don't shine.
  2200. }
  2201. m_xpath->pop();
  2202. }
  2203. else if( itemName == wxT( "via" ) )
  2204. {
  2205. m_xpath->push( "via" );
  2206. EVIA v( netItem );
  2207. if( v.layer_front_most > v.layer_back_most )
  2208. std::swap( v.layer_front_most, v.layer_back_most );
  2209. PCB_LAYER_ID layer_front_most = kicad_layer( v.layer_front_most );
  2210. PCB_LAYER_ID layer_back_most = kicad_layer( v.layer_back_most );
  2211. if( IsCopperLayer( layer_front_most ) && IsCopperLayer( layer_back_most )
  2212. && layer_front_most != layer_back_most )
  2213. {
  2214. int kidiam;
  2215. int drillz = v.drill.ToPcbUnits();
  2216. PCB_VIA* via = new PCB_VIA( m_board );
  2217. m_board->Add( via );
  2218. if( v.diam )
  2219. {
  2220. kidiam = v.diam->ToPcbUnits();
  2221. via->SetWidth( PADSTACK::ALL_LAYERS, kidiam );
  2222. }
  2223. else
  2224. {
  2225. double annulus = drillz * m_rules->rvViaOuter; // eagle "restring"
  2226. annulus = eagleClamp( m_rules->rlMinViaOuter, annulus,
  2227. m_rules->rlMaxViaOuter );
  2228. kidiam = KiROUND( drillz + 2 * annulus );
  2229. via->SetWidth( PADSTACK::ALL_LAYERS, kidiam );
  2230. }
  2231. via->SetDrill( drillz );
  2232. // make sure the via diameter respects the restring rules
  2233. int via_width = via->GetWidth( PADSTACK::ALL_LAYERS );
  2234. if( !v.diam || via_width <= via->GetDrill() )
  2235. {
  2236. double annular_width = ( via_width - via->GetDrill() ) / 2.0;
  2237. double clamped_annular_width = eagleClamp( m_rules->rlMinViaOuter,
  2238. annular_width,
  2239. m_rules->rlMaxViaOuter );
  2240. via->SetWidth( PADSTACK::ALL_LAYERS, drillz + 2 * clamped_annular_width );
  2241. }
  2242. if( kidiam < m_min_via )
  2243. m_min_via = kidiam;
  2244. if( netclass && kidiam < netclass->GetViaDiameter() )
  2245. netclass->SetViaDiameter( kidiam );
  2246. if( drillz < m_min_hole )
  2247. m_min_hole = drillz;
  2248. if( netclass && drillz < netclass->GetViaDrill() )
  2249. netclass->SetViaDrill( drillz );
  2250. if( ( kidiam - drillz ) / 2 < m_min_annulus )
  2251. m_min_annulus = ( kidiam - drillz ) / 2;
  2252. if( layer_front_most == F_Cu && layer_back_most == B_Cu )
  2253. {
  2254. via->SetViaType( VIATYPE::THROUGH );
  2255. }
  2256. /// This is, at best, a guess. Eagle doesn't seem to differentiate
  2257. /// between blind/buried vias that only go one layer and micro vias
  2258. /// so the user will need to clean up a bit
  2259. else if( v.layer_back_most - v.layer_front_most == 1 )
  2260. {
  2261. via->SetViaType( VIATYPE::MICROVIA );
  2262. }
  2263. else
  2264. {
  2265. via->SetViaType( VIATYPE::BLIND_BURIED );
  2266. }
  2267. VECTOR2I pos( kicad_x( v.x ), kicad_y( v.y ) );
  2268. via->SetLayerPair( layer_front_most, layer_back_most );
  2269. via->SetPosition( pos );
  2270. via->SetEnd( pos );
  2271. via->SetNetCode( netCode );
  2272. }
  2273. m_xpath->pop();
  2274. }
  2275. else if( itemName == wxT( "contactref" ) )
  2276. {
  2277. m_xpath->push( "contactref" );
  2278. // <contactref element="RN1" pad="7"/>
  2279. const wxString& reference = netItem->GetAttribute( "element" );
  2280. const wxString& pad = netItem->GetAttribute( "pad" );
  2281. wxString key = makeKey( reference, pad ) ;
  2282. m_pads_to_nets[ key ] = ENET( netCode, netName );
  2283. m_xpath->pop();
  2284. sawPad = true;
  2285. }
  2286. else if( itemName == wxT( "polygon" ) )
  2287. {
  2288. m_xpath->push( "polygon" );
  2289. auto* zone = loadPolygon( netItem );
  2290. if( zone )
  2291. {
  2292. zones.push_back( zone );
  2293. if( !zone->GetIsRuleArea() )
  2294. zone->SetNetCode( netCode );
  2295. }
  2296. m_xpath->pop(); // "polygon"
  2297. }
  2298. netItem = netItem->GetNext();
  2299. }
  2300. if( zones.size() && !sawPad )
  2301. {
  2302. // KiCad does not support an unconnected zone with its own non-zero netcode,
  2303. // but only when assigned netcode = 0 w/o a name...
  2304. for( ZONE* zone : zones )
  2305. zone->SetNetCode( NETINFO_LIST::UNCONNECTED );
  2306. // therefore omit this signal/net.
  2307. }
  2308. else
  2309. {
  2310. netCode++;
  2311. }
  2312. // Get next signal
  2313. net = net->GetNext();
  2314. }
  2315. m_xpath->pop(); // "signals.signal"
  2316. }
  2317. std::map<wxString, PCB_LAYER_ID> PCB_IO_EAGLE::DefaultLayerMappingCallback(
  2318. const std::vector<INPUT_LAYER_DESC>& aInputLayerDescriptionVector )
  2319. {
  2320. std::map<wxString, PCB_LAYER_ID> layer_map;
  2321. for ( const INPUT_LAYER_DESC& layer : aInputLayerDescriptionVector )
  2322. {
  2323. PCB_LAYER_ID layerId = std::get<0>( defaultKicadLayer( eagle_layer_id( layer.Name ) ) );
  2324. layer_map.emplace( layer.Name, layerId );
  2325. }
  2326. return layer_map;
  2327. }
  2328. void PCB_IO_EAGLE::mapEagleLayersToKicad( bool aIsLibraryCache )
  2329. {
  2330. std::vector<INPUT_LAYER_DESC> inputDescs;
  2331. for ( const std::pair<const int, ELAYER>& layerPair : m_eagleLayers )
  2332. {
  2333. const ELAYER& eLayer = layerPair.second;
  2334. INPUT_LAYER_DESC layerDesc;
  2335. std::tie( layerDesc.AutoMapLayer, layerDesc.PermittedLayers, layerDesc.Required ) =
  2336. defaultKicadLayer( eLayer.number, aIsLibraryCache );
  2337. if( layerDesc.AutoMapLayer == UNDEFINED_LAYER )
  2338. continue; // Ignore unused copper layers
  2339. layerDesc.Name = eLayer.name;
  2340. inputDescs.push_back( layerDesc );
  2341. }
  2342. if( m_progressReporter && dynamic_cast<wxWindow*>( m_progressReporter ) )
  2343. dynamic_cast<wxWindow*>( m_progressReporter )->Hide();
  2344. m_layer_map = m_layer_mapping_handler( inputDescs );
  2345. if( m_progressReporter && dynamic_cast<wxWindow*>( m_progressReporter ))
  2346. dynamic_cast<wxWindow*>( m_progressReporter )->Show();
  2347. }
  2348. PCB_LAYER_ID PCB_IO_EAGLE::kicad_layer( int aEagleLayer ) const
  2349. {
  2350. auto result = m_layer_map.find( eagle_layer_name( aEagleLayer ) );
  2351. return result == m_layer_map.end() ? UNDEFINED_LAYER : result->second;
  2352. }
  2353. std::tuple<PCB_LAYER_ID, LSET, bool> PCB_IO_EAGLE::defaultKicadLayer( int aEagleLayer,
  2354. bool aIsLibraryCache ) const
  2355. {
  2356. // eagle copper layer:
  2357. if( aEagleLayer >= 1 && aEagleLayer < int( arrayDim( m_cu_map ) ) )
  2358. {
  2359. LSET copperLayers;
  2360. for( int copperLayer : m_cu_map )
  2361. {
  2362. if( copperLayer >= 0 )
  2363. copperLayers[copperLayer] = true;
  2364. }
  2365. return { PCB_LAYER_ID( m_cu_map[aEagleLayer] ), copperLayers, true };
  2366. }
  2367. int kiLayer = UNSELECTED_LAYER;
  2368. bool required = false;
  2369. LSET permittedLayers;
  2370. permittedLayers.set();
  2371. // translate non-copper eagle layer to pcbnew layer
  2372. switch( aEagleLayer )
  2373. {
  2374. // Eagle says "Dimension" layer, but it's for board perimeter
  2375. case EAGLE_LAYER::DIMENSION:
  2376. kiLayer = Edge_Cuts;
  2377. required = true;
  2378. permittedLayers = LSET( { Edge_Cuts } );
  2379. break;
  2380. case EAGLE_LAYER::TPLACE:
  2381. kiLayer = F_SilkS;
  2382. break;
  2383. case EAGLE_LAYER::BPLACE:
  2384. kiLayer = B_SilkS;
  2385. break;
  2386. case EAGLE_LAYER::TNAMES:
  2387. kiLayer = F_SilkS;
  2388. break;
  2389. case EAGLE_LAYER::BNAMES:
  2390. kiLayer = B_SilkS;
  2391. break;
  2392. case EAGLE_LAYER::TVALUES:
  2393. kiLayer = F_Fab;
  2394. break;
  2395. case EAGLE_LAYER::BVALUES:
  2396. kiLayer = B_Fab;
  2397. break;
  2398. case EAGLE_LAYER::TSTOP:
  2399. kiLayer = F_Mask;
  2400. break;
  2401. case EAGLE_LAYER::BSTOP:
  2402. kiLayer = B_Mask;
  2403. break;
  2404. case EAGLE_LAYER::TCREAM:
  2405. kiLayer = F_Paste;
  2406. break;
  2407. case EAGLE_LAYER::BCREAM:
  2408. kiLayer = B_Paste;
  2409. break;
  2410. case EAGLE_LAYER::TFINISH:
  2411. kiLayer = F_Mask;
  2412. break;
  2413. case EAGLE_LAYER::BFINISH:
  2414. kiLayer = B_Mask;
  2415. break;
  2416. case EAGLE_LAYER::TGLUE:
  2417. kiLayer = F_Adhes;
  2418. break;
  2419. case EAGLE_LAYER::BGLUE:
  2420. kiLayer = B_Adhes;
  2421. break;
  2422. case EAGLE_LAYER::DOCUMENT:
  2423. kiLayer = Cmts_User;
  2424. break;
  2425. case EAGLE_LAYER::REFERENCELC:
  2426. kiLayer = Cmts_User;
  2427. break;
  2428. case EAGLE_LAYER::REFERENCELS:
  2429. kiLayer = Cmts_User;
  2430. break;
  2431. // Packages show the future chip pins on SMD parts using layer 51.
  2432. // This is an area slightly smaller than the PAD/SMD copper area.
  2433. // Carry those visual aids into the FOOTPRINT on the fabrication layer,
  2434. // not silkscreen. This is perhaps not perfect, but there is not a lot
  2435. // of other suitable paired layers
  2436. case EAGLE_LAYER::TDOCU:
  2437. kiLayer = F_Fab;
  2438. break;
  2439. case EAGLE_LAYER::BDOCU:
  2440. kiLayer = B_Fab;
  2441. break;
  2442. // these layers are defined as user layers. put them on ECO layers
  2443. case EAGLE_LAYER::USERLAYER1:
  2444. kiLayer = Eco1_User;
  2445. break;
  2446. case EAGLE_LAYER::USERLAYER2:
  2447. kiLayer = Eco2_User;
  2448. break;
  2449. // these will also appear in the ratsnest, so there's no need for a warning
  2450. case EAGLE_LAYER::UNROUTED:
  2451. kiLayer = Dwgs_User;
  2452. break;
  2453. case EAGLE_LAYER::TKEEPOUT:
  2454. kiLayer = F_CrtYd;
  2455. break;
  2456. case EAGLE_LAYER::BKEEPOUT:
  2457. kiLayer = B_CrtYd;
  2458. break;
  2459. case EAGLE_LAYER::MILLING:
  2460. case EAGLE_LAYER::TTEST:
  2461. case EAGLE_LAYER::BTEST:
  2462. case EAGLE_LAYER::HOLES:
  2463. default:
  2464. if( aIsLibraryCache )
  2465. kiLayer = UNDEFINED_LAYER;
  2466. else
  2467. kiLayer = UNSELECTED_LAYER;
  2468. break;
  2469. }
  2470. return { PCB_LAYER_ID( kiLayer ), permittedLayers, required };
  2471. }
  2472. const wxString& PCB_IO_EAGLE::eagle_layer_name( int aLayer ) const
  2473. {
  2474. static const wxString unknown( "unknown" );
  2475. auto it = m_eagleLayers.find( aLayer );
  2476. return it == m_eagleLayers.end() ? unknown : it->second.name;
  2477. }
  2478. int PCB_IO_EAGLE::eagle_layer_id( const wxString& aLayerName ) const
  2479. {
  2480. static const int unknown = -1;
  2481. auto it = m_eagleLayersIds.find( aLayerName );
  2482. return it == m_eagleLayersIds.end() ? unknown : it->second;
  2483. }
  2484. void PCB_IO_EAGLE::centerBoard()
  2485. {
  2486. if( m_props )
  2487. {
  2488. UTF8 page_width;
  2489. UTF8 page_height;
  2490. if( auto it = m_props->find( "page_width" ); it != m_props->end() )
  2491. page_width = it->second;
  2492. if( auto it = m_props->find( "page_height" ); it != m_props->end() )
  2493. page_height = it->second;
  2494. if( !page_width.empty() && !page_height.empty() )
  2495. {
  2496. BOX2I bbbox = m_board->GetBoardEdgesBoundingBox();
  2497. int w = atoi( page_width.c_str() );
  2498. int h = atoi( page_height.c_str() );
  2499. int desired_x = ( w - bbbox.GetWidth() ) / 2;
  2500. int desired_y = ( h - bbbox.GetHeight() ) / 2;
  2501. m_board->Move( VECTOR2I( desired_x - bbbox.GetX(), desired_y - bbbox.GetY() ) );
  2502. }
  2503. }
  2504. }
  2505. wxDateTime PCB_IO_EAGLE::getModificationTime( const wxString& aPath )
  2506. {
  2507. // File hasn't been loaded yet.
  2508. if( aPath.IsEmpty() )
  2509. return wxDateTime::Now();
  2510. wxFileName fn( aPath );
  2511. if( fn.IsFileReadable() )
  2512. return fn.GetModificationTime();
  2513. else
  2514. return wxDateTime( 0.0 );
  2515. }
  2516. void PCB_IO_EAGLE::cacheLib( const wxString& aLibPath )
  2517. {
  2518. fontconfig::FONTCONFIG::SetReporter( nullptr );
  2519. try
  2520. {
  2521. wxDateTime modtime = getModificationTime( aLibPath );
  2522. // Fixes assertions in wxWidgets debug builds for the wxDateTime object. Refresh the
  2523. // cache if either of the wxDateTime objects are invalid or the last file modification
  2524. // time differs from the current file modification time.
  2525. bool load = !m_mod_time.IsValid() || !modtime.IsValid() || m_mod_time != modtime;
  2526. if( aLibPath != m_lib_path || load )
  2527. {
  2528. wxXmlNode* doc;
  2529. LOCALE_IO toggle; // toggles on, then off, the C locale.
  2530. deleteTemplates();
  2531. // Set this before completion of loading, since we rely on it for
  2532. // text of an exception. Delay setting m_mod_time until after successful load
  2533. // however.
  2534. m_lib_path = aLibPath;
  2535. // 8 bit "filename" should be encoded according to disk filename encoding,
  2536. // (maybe this is current locale, maybe not, its a filesystem issue),
  2537. // and is not necessarily utf8.
  2538. string filename = (const char*) aLibPath.char_str( wxConvFile );
  2539. // Load the document
  2540. wxFileName fn( filename );
  2541. wxFFileInputStream stream( fn.GetFullPath() );
  2542. wxXmlDocument xmlDocument;
  2543. if( !stream.IsOk() || !xmlDocument.Load( stream ) )
  2544. {
  2545. THROW_IO_ERROR( wxString::Format( _( "Unable to read file '%s'." ),
  2546. fn.GetFullPath() ) );
  2547. }
  2548. doc = xmlDocument.GetRoot();
  2549. wxXmlNode* drawing = MapChildren( doc )["drawing"];
  2550. NODE_MAP drawingChildren = MapChildren( drawing );
  2551. // clear the cu map and then rebuild it.
  2552. clear_cu_map();
  2553. m_xpath->push( "eagle.drawing.layers" );
  2554. wxXmlNode* layers = drawingChildren["layers"];
  2555. loadLayerDefs( layers );
  2556. mapEagleLayersToKicad( true );
  2557. m_xpath->pop();
  2558. m_xpath->push( "eagle.drawing.library" );
  2559. wxXmlNode* library = drawingChildren["library"];
  2560. loadLibrary( library, nullptr );
  2561. m_xpath->pop();
  2562. m_mod_time = modtime;
  2563. }
  2564. }
  2565. catch(...){}
  2566. // TODO: Handle exceptions
  2567. // catch( file_parser_error fpe )
  2568. // {
  2569. // // for xml_parser_error, what() has the line number in it,
  2570. // // but no byte offset. That should be an adequate error message.
  2571. // THROW_IO_ERROR( fpe.what() );
  2572. // }
  2573. //
  2574. // // Class ptree_error is a base class for xml_parser_error & file_parser_error,
  2575. // // so one catch should be OK for all errors.
  2576. // catch( ptree_error pte )
  2577. // {
  2578. // string errmsg = pte.what();
  2579. //
  2580. // errmsg += " @\n";
  2581. // errmsg += m_xpath->Contents();
  2582. //
  2583. // THROW_IO_ERROR( errmsg );
  2584. // }
  2585. }
  2586. void PCB_IO_EAGLE::FootprintEnumerate( wxArrayString& aFootprintNames, const wxString& aLibraryPath,
  2587. bool aBestEfforts, const std::map<std::string, UTF8>* aProperties )
  2588. {
  2589. wxString errorMsg;
  2590. init( aProperties );
  2591. try
  2592. {
  2593. cacheLib( aLibraryPath );
  2594. }
  2595. catch( const IO_ERROR& ioe )
  2596. {
  2597. errorMsg = ioe.What();
  2598. }
  2599. // Some of the files may have been parsed correctly so we want to add the valid files to
  2600. // the library.
  2601. for( const auto& [ name, footprint ] : m_templates )
  2602. aFootprintNames.Add( name );
  2603. if( !errorMsg.IsEmpty() && !aBestEfforts )
  2604. THROW_IO_ERROR( errorMsg );
  2605. }
  2606. FOOTPRINT* PCB_IO_EAGLE::FootprintLoad( const wxString& aLibraryPath,
  2607. const wxString& aFootprintName, bool aKeepUUID,
  2608. const std::map<std::string, UTF8>* aProperties )
  2609. {
  2610. init( aProperties );
  2611. cacheLib( aLibraryPath );
  2612. auto it = m_templates.find( aFootprintName );
  2613. if( it == m_templates.end() )
  2614. return nullptr;
  2615. // Return a copy of the template
  2616. FOOTPRINT* copy = (FOOTPRINT*) it->second->Duplicate();
  2617. copy->SetParent( nullptr );
  2618. return copy;
  2619. }
  2620. int PCB_IO_EAGLE::getMinimumCopperLayerCount() const
  2621. {
  2622. int minLayerCount = 2;
  2623. std::map<wxString, PCB_LAYER_ID>::const_iterator it;
  2624. for( it = m_layer_map.begin(); it != m_layer_map.end(); ++it )
  2625. {
  2626. PCB_LAYER_ID layerId = it->second;
  2627. if( IsCopperLayer( layerId ) && layerId != F_Cu && layerId != B_Cu
  2628. && ( layerId + 2 ) > minLayerCount )
  2629. minLayerCount = layerId + 2;
  2630. }
  2631. // Ensure the copper layers count is a multiple of 2
  2632. // Pcbnew does not like boards with odd layers count
  2633. // (these boards cannot exist. they actually have a even layers count)
  2634. if( ( minLayerCount % 2 ) != 0 )
  2635. minLayerCount++;
  2636. return minLayerCount;
  2637. }