KiCad PCB EDA Suite
pcbnew/exporters/export_idf.cpp File Reference
#include <list>
#include <pcb_edit_frame.h>
#include <macros.h>
#include <pcbnew.h>
#include <class_board.h>
#include <class_module.h>
#include <class_edge_mod.h>
#include <idf_parser.h>
#include <3d_cache/3d_info.h>
#include <build_version.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)
 Function idf_export_outline retrieves 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_module (BOARD *aPcb, MODULE *aModule, IDF3_BOARD &aIDFBoard)
 Function idf_export_module retrieves information from all board modules, 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 50 of file pcbnew/exporters/export_idf.cpp.

◆ PCBNEW

#define PCBNEW

Definition at line 45 of file pcbnew/exporters/export_idf.cpp.

Function Documentation

◆ idf_export_module()

static void idf_export_module ( BOARD aPcb,
MODULE aModule,
IDF3_BOARD &  aIDFBoard 
)
static

Function idf_export_module retrieves information from all board modules, 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 277 of file pcbnew/exporters/export_idf.cpp.

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

References PNS::angle(), B_Cu, BOARD_ITEM::GetLayer(), MODULE::GetOrientation(), MODULE::GetPosition(), MODULE::GetReference(), MODULE::GetValue(), MODULE::Models(), NULL, PAD_ATTRIB_HOLE_NOT_PLATED, PAD_DRILL_SHAPE_OBLONG, MODULE::Pads(), FILENAME_RESOLVER::ResolvePath(), resolver, RotatePoint(), scale, TO_UTF8, wxPoint::x, and wxPoint::y.

Referenced by PCB_EDIT_FRAME::Export_IDF3().

◆ idf_export_outline()

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

Function idf_export_outline retrieves 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 60 of file pcbnew/exporters/export_idf.cpp.

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

References BOARD::Drawings(), Edge_Cuts, DRAWSEGMENT::GetAngle(), DRAWSEGMENT::GetArcStart(), BOARD::GetBoardEdgesBoundingBox(), DRAWSEGMENT::GetCenter(), DRAWSEGMENT::GetEnd(), EDA_RECT::GetOrigin(), DRAWSEGMENT::GetRadius(), DRAWSEGMENT::GetShape(), EDA_RECT::GetSize(), DRAWSEGMENT::GetStart(), LINE_WIDTH, NULL, PCB_LINE_T, S_ARC, S_CIRCLE, S_RECT, S_SEGMENT, scale, wxPoint::x, and wxPoint::y.

Referenced by PCB_EDIT_FRAME::Export_IDF3().

Variable Documentation

◆ resolver

FILENAME_RESOLVER* resolver
static