KiCad PCB EDA Suite
export_idf.cpp File Reference
#include <list>
#include <locale_io.h>
#include <macros.h>
#include <pcb_edit_frame.h>
#include <board.h>
#include <board_design_settings.h>
#include <footprint.h>
#include <fp_shape.h>
#include <idf_parser.h>
#include <pad.h>
#include <build_version.h>
#include <wx/msgdlg.h>
#include "project.h"
#include "kiway.h"
#include "3d_cache/3d_cache.h"
#include "filename_resolver.h"
#include <convert_to_biu.h>

Go to the source code of this file.

Macros

#define PCBNEW
 
#define LINE_WIDTH   (Millimeter2iu( 0.1 ))
 

Functions

static void idf_export_outline (BOARD *aPcb, IDF3_BOARD &aIDFBoard)
 Retrieve line segment information from the edge layer and compiles the data into a form which can be output as an IDFv3 compliant #BOARD_OUTLINE section. More...
 
static void idf_export_footprint (BOARD *aPcb, FOOTPRINT *aFootprint, IDF3_BOARD &aIDFBoard)
 Retrieve information from all board footprints, adds drill holes to the DRILLED_HOLES or BOARD_OUTLINE section as appropriate, Compiles data for the PLACEMENT section and compiles data for the library ELECTRICAL section. More...
 

Variables

static FILENAME_RESOLVERresolver
 

Macro Definition Documentation

◆ LINE_WIDTH

#define LINE_WIDTH   (Millimeter2iu( 0.1 ))

Definition at line 54 of file export_idf.cpp.

◆ PCBNEW

#define PCBNEW

Definition at line 48 of file export_idf.cpp.

Function Documentation

◆ idf_export_footprint()

static void idf_export_footprint ( BOARD aPcb,
FOOTPRINT aFootprint,
IDF3_BOARD &  aIDFBoard 
)
static

Retrieve information from all board footprints, adds drill holes to the DRILLED_HOLES or BOARD_OUTLINE section as appropriate, Compiles data for the PLACEMENT section and compiles data for the library ELECTRICAL section.

Definition at line 279 of file export_idf.cpp.

280 {
281  // Reference Designator
282  std::string crefdes = TO_UTF8( aFootprint->Reference().GetShownText() );
283 
284  if( crefdes.empty() || !crefdes.compare( "~" ) )
285  {
286  std::string cvalue = TO_UTF8( aFootprint->Value().GetShownText() );
287 
288  // if both the RefDes and Value are empty or set to '~' the board owns the part,
289  // otherwise associated parts of the footprint must be marked NOREFDES.
290  if( cvalue.empty() || !cvalue.compare( "~" ) )
291  crefdes = "BOARD";
292  else
293  crefdes = "NOREFDES";
294  }
295 
296  // TODO: If footprint cutouts are supported we must add code here
297  // for( EDA_ITEM* item = aFootprint->GraphicalItems(); item != NULL; item = item->Next() )
298  // {
299  // if( item->Type() != PCB_FP_SHAPE_T || item->GetLayer() != Edge_Cuts )
300  // continue;
301  // code to export cutouts
302  // }
303 
304  // Export pads
305  double drill, x, y;
306  double scale = aIDFBoard.GetUserScale();
307  IDF3::KEY_PLATING kplate;
308  std::string pintype;
309  std::string tstr;
310 
311  double dx, dy;
312 
313  aIDFBoard.GetUserOffset( dx, dy );
314 
315  for( auto pad : aFootprint->Pads() )
316  {
317  drill = (double) pad->GetDrillSize().x * scale;
318  x = pad->GetPosition().x * scale + dx;
319  y = -pad->GetPosition().y * scale + dy;
320 
321  // Export the hole on the edge layer
322  if( drill > 0.0 )
323  {
324  // plating
325  if( pad->GetAttribute() == PAD_ATTRIB::NPTH )
326  kplate = IDF3::NPTH;
327  else
328  kplate = IDF3::PTH;
329 
330  // hole type
331  tstr = TO_UTF8( pad->GetNumber() );
332 
333  if( tstr.empty() || !tstr.compare( "0" ) || !tstr.compare( "~" )
334  || ( kplate == IDF3::NPTH )
335  || ( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ) )
336  pintype = "MTG";
337  else
338  pintype = "PIN";
339 
340  // fields:
341  // 1. hole dia. : float
342  // 2. X coord : float
343  // 3. Y coord : float
344  // 4. plating : PTH | NPTH
345  // 5. Assoc. part : BOARD | NOREFDES | PANEL | {"refdes"}
346  // 6. type : PIN | VIA | MTG | TOOL | { "other" }
347  // 7. owner : MCAD | ECAD | UNOWNED
348  if( ( pad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG )
349  && ( pad->GetDrillSize().x != pad->GetDrillSize().y ) )
350  {
351  // NOTE: IDF does not have direct support for slots;
352  // slots are implemented as a board cutout and we
353  // cannot represent plating or reference designators
354 
355  double dlength = pad->GetDrillSize().y * scale;
356 
357  // NOTE: The orientation of footprints and pads have
358  // the opposite sense due to KiCad drawing on a
359  // screen with a LH coordinate system
360  double angle = pad->GetOrientation() / 10.0;
361 
362  // NOTE: Since this code assumes the scenario where
363  // GetDrillSize().y is the length but idf_parser.cpp
364  // assumes a length along the X axis, the orientation
365  // must be shifted +90 deg when GetDrillSize().y is
366  // the major axis.
367 
368  if( dlength < drill )
369  {
370  std::swap( drill, dlength );
371  }
372  else
373  {
374  angle += 90.0;
375  }
376 
377  // NOTE: KiCad measures a slot's length from end to end
378  // rather than between the centers of the arcs
379  dlength -= drill;
380 
381  aIDFBoard.AddSlot( drill, dlength, angle, x, y );
382  }
383  else
384  {
385  IDF_DRILL_DATA *dp = new IDF_DRILL_DATA( drill, x, y, kplate, crefdes,
386  pintype, IDF3::ECAD );
387 
388  if( !aIDFBoard.AddDrill( dp ) )
389  {
390  delete dp;
391 
392  std::ostringstream ostr;
393  ostr << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__;
394  ostr << "(): could not add drill";
395 
396  throw std::runtime_error( ostr.str() );
397  }
398  }
399  }
400  }
401 
402  // add any valid models to the library item list
403  std::string refdes;
404 
405  IDF3_COMPONENT* comp = nullptr;
406 
407  auto sM = aFootprint->Models().begin();
408  auto eM = aFootprint->Models().end();
409  wxFileName idfFile;
410  wxString idfExt;
411 
412  while( sM != eM )
413  {
414  if( !sM->m_Show )
415  {
416  ++sM;
417  continue;
418  }
419 
420  idfFile.Assign( resolver->ResolvePath( sM->m_Filename ) );
421  idfExt = idfFile.GetExt();
422 
423  if( idfExt.Cmp( wxT( "idf" ) ) && idfExt.Cmp( wxT( "IDF" ) ) )
424  {
425  ++sM;
426  continue;
427  }
428 
429  if( refdes.empty() )
430  {
431  refdes = TO_UTF8( aFootprint->Reference().GetShownText() );
432 
433  // NOREFDES cannot be used or else the software gets confused
434  // when writing out the placement data due to conflicting
435  // placement and layer specifications; to work around this we
436  // create a (hopefully) unique refdes for our exported part.
437  if( refdes.empty() || !refdes.compare( "~" ) )
438  refdes = aIDFBoard.GetNewRefDes();
439  }
440 
441  IDF3_COMP_OUTLINE* outline;
442 
443  outline = aIDFBoard.GetComponentOutline( idfFile.GetFullPath() );
444 
445  if( !outline )
446  throw( std::runtime_error( aIDFBoard.GetError() ) );
447 
448  double rotz = aFootprint->GetOrientation() / 10.0;
449  double locx = sM->m_Offset.x * 25.4; // part offsets are in inches
450  double locy = sM->m_Offset.y * 25.4;
451  double locz = sM->m_Offset.z * 25.4;
452  double lrot = sM->m_Rotation.z;
453 
454  bool top = ( aFootprint->GetLayer() == B_Cu ) ? false : true;
455 
456  if( top )
457  {
458  locy = -locy;
459  RotatePoint( &locx, &locy, aFootprint->GetOrientation() );
460  locy = -locy;
461  }
462 
463  if( !top )
464  {
465  lrot = -lrot;
466  RotatePoint( &locx, &locy, aFootprint->GetOrientation() );
467  locy = -locy;
468 
469  rotz = 180.0 - rotz;
470 
471  if( rotz >= 360.0 )
472  while( rotz >= 360.0 ) rotz -= 360.0;
473 
474  if( rotz <= -360.0 )
475  while( rotz <= -360.0 ) rotz += 360.0;
476  }
477 
478  if( comp == nullptr )
479  comp = aIDFBoard.FindComponent( refdes );
480 
481  if( comp == nullptr )
482  {
483  comp = new IDF3_COMPONENT( &aIDFBoard );
484 
485  if( comp == nullptr )
486  throw( std::runtime_error( aIDFBoard.GetError() ) );
487 
488  comp->SetRefDes( refdes );
489 
490  if( top )
491  {
492  comp->SetPosition( aFootprint->GetPosition().x * scale + dx,
493  -aFootprint->GetPosition().y * scale + dy,
494  rotz, IDF3::LYR_TOP );
495  }
496  else
497  {
498  comp->SetPosition( aFootprint->GetPosition().x * scale + dx,
499  -aFootprint->GetPosition().y * scale + dy,
500  rotz, IDF3::LYR_BOTTOM );
501  }
502 
503  comp->SetPlacement( IDF3::PS_ECAD );
504 
505  aIDFBoard.AddComponent( comp );
506  }
507  else
508  {
509  double refX, refY, refA;
510  IDF3::IDF_LAYER side;
511 
512  if( ! comp->GetPosition( refX, refY, refA, side ) )
513  {
514  // place the item
515  if( top )
516  {
517  comp->SetPosition( aFootprint->GetPosition().x * scale + dx,
518  -aFootprint->GetPosition().y * scale + dy,
519  rotz, IDF3::LYR_TOP );
520  }
521  else
522  {
523  comp->SetPosition( aFootprint->GetPosition().x * scale + dx,
524  -aFootprint->GetPosition().y * scale + dy,
525  rotz, IDF3::LYR_BOTTOM );
526  }
527 
528  comp->SetPlacement( IDF3::PS_ECAD );
529 
530  }
531  else
532  {
533  // check that the retrieved component matches this one
534  refX = refX - ( aFootprint->GetPosition().x * scale + dx );
535  refY = refY - ( -aFootprint->GetPosition().y * scale + dy );
536  refA = refA - rotz;
537  refA *= refA;
538  refX *= refX;
539  refY *= refY;
540  refX += refY;
541 
542  // conditions: same side, X,Y coordinates within 10 microns,
543  // angle within 0.01 degree
544  if( ( top && side == IDF3::LYR_BOTTOM ) || ( !top && side == IDF3::LYR_TOP )
545  || ( refA > 0.0001 ) || ( refX > 0.0001 ) )
546  {
547  comp->GetPosition( refX, refY, refA, side );
548 
549  std::ostringstream ostr;
550  ostr << "* " << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << "():\n";
551  ostr << "* conflicting Reference Designator '" << refdes << "'\n";
552  ostr << "* X loc: " << ( aFootprint->GetPosition().x * scale + dx);
553  ostr << " vs. " << refX << "\n";
554  ostr << "* Y loc: " << ( -aFootprint->GetPosition().y * scale + dy);
555  ostr << " vs. " << refY << "\n";
556  ostr << "* angle: " << rotz;
557  ostr << " vs. " << refA << "\n";
558 
559  if( top )
560  ostr << "* TOP vs. ";
561  else
562  ostr << "* BOTTOM vs. ";
563 
564  if( side == IDF3::LYR_TOP )
565  ostr << "TOP";
566  else
567  ostr << "BOTTOM";
568 
569  throw( std::runtime_error( ostr.str() ) );
570  }
571  }
572  }
573 
574  // create the local data ...
575  IDF3_COMP_OUTLINE_DATA* data = new IDF3_COMP_OUTLINE_DATA( comp, outline );
576 
577  data->SetOffsets( locx, locy, locz, lrot );
578  comp->AddOutlineData( data );
579  ++sM;
580  }
581 }
std::list< FP_3DMODEL > & Models()
Definition: footprint.h:182
double GetOrientation() const
Definition: footprint.h:190
void RotatePoint(int *pX, int *pY, double angle)
Definition: trigo.cpp:229
PADS & Pads()
Definition: footprint.h:168
FP_TEXT & Value()
read/write accessors:
Definition: footprint.h:499
FP_TEXT & Reference()
Definition: footprint.h:500
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: macros.h:96
like PAD_PTH, but not plated
wxString ResolvePath(const wxString &aFileName)
Determines the full path of the given file name.
const int scale
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
wxPoint GetPosition() const override
Definition: footprint.h:186
static FILENAME_RESOLVER * resolver
Definition: export_idf.cpp:57
virtual wxString GetShownText(int aDepth=0) const override
Return the string actually shown after processing of the base text.
Definition: fp_text.cpp:416
virtual PCB_LAYER_ID GetLayer() const
Return the primary layer this item is on.
Definition: board_item.h:142

References PNS::angle(), B_Cu, BOARD_ITEM::GetLayer(), FOOTPRINT::GetOrientation(), FOOTPRINT::GetPosition(), FP_TEXT::GetShownText(), FOOTPRINT::Models(), NPTH, pad, PAD_DRILL_SHAPE_OBLONG, FOOTPRINT::Pads(), FOOTPRINT::Reference(), FILENAME_RESOLVER::ResolvePath(), resolver, RotatePoint(), scale, TO_UTF8, and FOOTPRINT::Value().

Referenced by PCB_EDIT_FRAME::Export_IDF3().

◆ idf_export_outline()

static void idf_export_outline ( BOARD aPcb,
IDF3_BOARD &  aIDFBoard 
)
static

Retrieve line segment information from the edge layer and compiles the data into a form which can be output as an IDFv3 compliant #BOARD_OUTLINE section.

Definition at line 64 of file export_idf.cpp.

65 {
66  double scale = aIDFBoard.GetUserScale();
67 
68  PCB_SHAPE* graphic; // KiCad graphical item
69  IDF_POINT sp, ep; // start and end points from KiCad item
70 
71  std::list< IDF_SEGMENT* > lines; // IDF intermediate form of KiCad graphical item
72  IDF_OUTLINE* outline = nullptr; // graphical items forming an outline or cutout
73 
74  // NOTE: IMPLEMENTATION
75  // If/when component cutouts are allowed, we must implement them separately. Cutouts
76  // must be added to the board outline section and not to the Other Outline section.
77  // The footprint cutouts should be handled via the idf_export_footprint() routine.
78 
79  double offX, offY;
80  aIDFBoard.GetUserOffset( offX, offY );
81 
82  // Retrieve segments and arcs from the board
83  for( BOARD_ITEM* item : aPcb->Drawings() )
84  {
85  if( item->Type() != PCB_SHAPE_T || item->GetLayer() != Edge_Cuts )
86  continue;
87 
88  graphic = (PCB_SHAPE*) item;
89 
90  switch( graphic->GetShape() )
91  {
92  case SHAPE_T::SEGMENT:
93  {
94  if( graphic->GetStart() == graphic->GetEnd() )
95  break;
96 
97  sp.x = graphic->GetStart().x * scale + offX;
98  sp.y = -graphic->GetStart().y * scale + offY;
99  ep.x = graphic->GetEnd().x * scale + offX;
100  ep.y = -graphic->GetEnd().y * scale + offY;
101  IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep );
102 
103  if( seg )
104  lines.push_back( seg );
105 
106  break;
107  }
108 
109  case SHAPE_T::RECT:
110  {
111  if( graphic->GetStart() == graphic->GetEnd() )
112  break;
113 
114  double top = graphic->GetStart().y * scale + offY;
115  double left = graphic->GetStart().x * scale + offX;
116  double bottom = graphic->GetEnd().y * scale + offY;
117  double right = graphic->GetEnd().x * scale + offX;
118 
119  IDF_POINT corners[4];
120  corners[0] = IDF_POINT( left, top );
121  corners[1] = IDF_POINT( right, top );
122  corners[2] = IDF_POINT( right, bottom );
123  corners[3] = IDF_POINT( left, bottom );
124 
125  lines.push_back( new IDF_SEGMENT( corners[0], corners[1] ) );
126  lines.push_back( new IDF_SEGMENT( corners[1], corners[2] ) );
127  lines.push_back( new IDF_SEGMENT( corners[2], corners[3] ) );
128  lines.push_back( new IDF_SEGMENT( corners[3], corners[0] ) );
129  break;
130  }
131 
132  case SHAPE_T::ARC:
133  {
134  if( graphic->GetCenter() == graphic->GetStart() )
135  break;
136 
137  sp.x = graphic->GetCenter().x * scale + offX;
138  sp.y = -graphic->GetCenter().y * scale + offY;
139  ep.x = graphic->GetStart().x * scale + offX;
140  ep.y = -graphic->GetStart().y * scale + offY;
141  IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep, -graphic->GetArcAngle() / 10.0, true );
142 
143  if( seg )
144  lines.push_back( seg );
145 
146  break;
147  }
148 
149  case SHAPE_T::CIRCLE:
150  {
151  if( graphic->GetRadius() == 0 )
152  break;
153 
154  sp.x = graphic->GetCenter().x * scale + offX;
155  sp.y = -graphic->GetCenter().y * scale + offY;
156  ep.x = sp.x - graphic->GetRadius() * scale;
157  ep.y = sp.y;
158 
159  // Circles must always have an angle of +360 deg. to appease
160  // quirky MCAD implementations of IDF.
161  IDF_SEGMENT* seg = new IDF_SEGMENT( sp, ep, 360.0, true );
162 
163  if( seg )
164  lines.push_back( seg );
165 
166  break;
167  }
168 
169  default:
170  break;
171  }
172  }
173 
174  // if there is no outline then use the bounding box
175  if( lines.empty() )
176  {
177  goto UseBoundingBox;
178  }
179 
180  // get the board outline and write it out
181  // note: we do not use a try/catch block here since we intend
182  // to simply ignore unclosed loops and continue processing
183  // until we're out of segments to process
184  outline = new IDF_OUTLINE;
185  IDF3::GetOutline( lines, *outline );
186 
187  if( outline->empty() )
188  goto UseBoundingBox;
189 
190  aIDFBoard.AddBoardOutline( outline );
191  outline = nullptr;
192 
193  // get all cutouts and write them out
194  while( !lines.empty() )
195  {
196  if( !outline )
197  outline = new IDF_OUTLINE;
198 
199  IDF3::GetOutline( lines, *outline );
200 
201  if( outline->empty() )
202  {
203  outline->Clear();
204  continue;
205  }
206 
207  aIDFBoard.AddBoardOutline( outline );
208  outline = nullptr;
209  }
210 
211  return;
212 
213 UseBoundingBox:
214 
215  // clean up if necessary
216  while( !lines.empty() )
217  {
218  delete lines.front();
219  lines.pop_front();
220  }
221 
222  if( outline )
223  outline->Clear();
224  else
225  outline = new IDF_OUTLINE;
226 
227  // Fetch a rectangular bounding box for the board; there is always some uncertainty in the
228  // board dimensions computed via ComputeBoundingBox() since this depends on the individual
229  // footprint entities.
230  EDA_RECT bbbox = aPcb->GetBoardEdgesBoundingBox();
231 
232  // convert to mm and compensate for an assumed LINE_WIDTH line thickness
233  double x = ( bbbox.GetOrigin().x + LINE_WIDTH / 2 ) * scale + offX;
234  double y = ( bbbox.GetOrigin().y + LINE_WIDTH / 2 ) * scale + offY;
235  double dx = ( bbbox.GetSize().x - LINE_WIDTH ) * scale;
236  double dy = ( bbbox.GetSize().y - LINE_WIDTH ) * scale;
237 
238  double px[4], py[4];
239  px[0] = x;
240  py[0] = y;
241 
242  px[1] = x;
243  py[1] = y + dy;
244 
245  px[2] = x + dx;
246  py[2] = y + dy;
247 
248  px[3] = x + dx;
249  py[3] = y;
250 
251  IDF_POINT p1, p2;
252 
253  p1.x = px[3];
254  p1.y = py[3];
255  p2.x = px[0];
256  p2.y = py[0];
257 
258  outline->push( new IDF_SEGMENT( p1, p2 ) );
259 
260  for( int i = 1; i < 4; ++i )
261  {
262  p1.x = px[i - 1];
263  p1.y = py[i - 1];
264  p2.x = px[i];
265  p2.y = py[i];
266 
267  outline->push( new IDF_SEGMENT( p1, p2 ) );
268  }
269 
270  aIDFBoard.AddBoardOutline( outline );
271 }
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition: board_item.h:49
const wxPoint & GetStart() const
Return the starting point of the graphic.
Definition: eda_shape.h:97
const EDA_RECT GetBoardEdgesBoundingBox() const
Return the board bounding box calculated using exclusively the board edges (graphics on Edge....
Definition: board.h:738
double GetArcAngle() const
Definition: eda_shape.cpp:498
const wxPoint & GetEnd() const
Return the ending point of the graphic.
Definition: eda_shape.h:122
const wxPoint GetOrigin() const
Definition: eda_rect.h:101
wxPoint GetCenter() const override
This defaults to the center of the bounding box if not overridden.
Definition: pcb_shape.h:79
const int scale
Handle the component boundary box.
Definition: eda_rect.h:42
SHAPE_T GetShape() const
Definition: eda_shape.h:92
int GetRadius() const
Definition: eda_shape.cpp:466
class PCB_SHAPE, a segment not on copper layers
Definition: typeinfo.h:90
DRAWINGS & Drawings()
Definition: board.h:237
const wxSize GetSize() const
Definition: eda_rect.h:91
#define LINE_WIDTH
Definition: export_idf.cpp:54

References ARC, CIRCLE, BOARD::Drawings(), Edge_Cuts, EDA_SHAPE::GetArcAngle(), BOARD::GetBoardEdgesBoundingBox(), PCB_SHAPE::GetCenter(), EDA_SHAPE::GetEnd(), EDA_RECT::GetOrigin(), EDA_SHAPE::GetRadius(), EDA_SHAPE::GetShape(), EDA_RECT::GetSize(), EDA_SHAPE::GetStart(), left, LINE_WIDTH, PCB_SHAPE_T, RECT, right, scale, and SEGMENT.

Referenced by PCB_EDIT_FRAME::Export_IDF3().

Variable Documentation

◆ resolver