KiCad PCB EDA Suite
s3d_plugin_idf.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015-2017 Cirilo Bernardo <[email protected]>
5  * Copyright (C) 2021 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 // Note: the board's bottom side is at Z = 0
26 
27 #include <iostream>
28 #include <sstream>
29 #include <cmath>
30 #include <string>
31 #include <map>
32 #include <wx/filename.h>
33 #include <wx/log.h>
34 #include <wx/string.h>
35 
36 #include "plugins/3d/3d_plugin.h"
37 #include "plugins/3dapi/ifsg_all.h"
38 #include "idf_parser.h"
39 #include "vrml_layer.h"
40 
41 #define PLUGIN_3D_IDF_MAJOR 1
42 #define PLUGIN_3D_IDF_MINOR 0
43 #define PLUGIN_3D_IDF_PATCH 0
44 #define PLUGIN_3D_IDF_REVNO 0
45 
46 // number of colors in the palette; cycles from 1..NCOLORS;
47 // number 0 is special (the PCB board color)
48 #define NCOLORS 6
49 
55 const wxChar* const traceIdfPlugin = wxT( "KICAD_IDF_PLUGIN" );
56 
57 
58 // read and instantiate an IDF component outline
59 static SCENEGRAPH* loadIDFOutline( const wxString& aFileName );
60 
61 
62 // read and render an IDF board assembly
63 static SCENEGRAPH* loadIDFBoard( const wxString& aFileName );
64 
65 
66 // model a single extruded outline
67 // idxColor = color index to use
68 // aParent = parent SCENEGRAPH object, if any
69 static SCENEGRAPH* addOutline( IDF3_COMP_OUTLINE* outline, int idxColor, SGNODE* aParent );
70 
71 
72 // model the board extrusion
73 static SCENEGRAPH* makeBoard( IDF3_BOARD& brd, SGNODE* aParent );
74 
75 
76 // model all included components
77 static bool makeComponents( IDF3_BOARD& brd, SGNODE* aParent );
78 
79 
80 // model any .OTHER_OUTLINE items
81 static bool makeOtherOutlines( IDF3_BOARD& brd, SGNODE* aParent );
82 
83 
84 // convert the IDF outline to VRML intermediate data
85 static bool getOutlineModel( VRML_LAYER& model, const std::list< IDF_OUTLINE* >* items );
86 
87 
88 // convert IDF segment data to VRML segment data
89 static bool addSegment( VRML_LAYER& model, IDF_SEGMENT* seg, int icont, int iseg );
90 
91 
92 // convert the VRML intermediate data into SG* data
93 static SCENEGRAPH* vrmlToSG( VRML_LAYER& vpcb, int idxColor, SGNODE* aParent, double top,
94  double bottom );
95 
96 
98 {
99 public:
101  {
102  setlocale( LC_NUMERIC, "C" );
103  }
104 
106  {
107  setlocale( LC_NUMERIC, "" );
108  }
109 };
110 
111 
112 static SGNODE* getColor( IFSG_SHAPE& shape, int colorIdx )
113 {
114  IFSG_APPEARANCE material( shape );
115 
116  static int cidx = 1;
117  int idx;
118 
119  if( colorIdx == -1 )
120  idx = cidx;
121  else
122  idx = colorIdx;
123 
124  switch( idx )
125  {
126  case 0:
127  // green for PCB
128  material.SetSpecular( 0.13f, 0.81f, 0.22f );
129  material.SetDiffuse( 0.13f, 0.81f, 0.22f );
130 
131  // default ambient intensity
132  material.SetShininess( 0.3f );
133  break;
134 
135  case 1:
136  // magenta
137  material.SetSpecular( 0.8f, 0.0f, 0.8f );
138  material.SetDiffuse( 0.6f, 0.0f, 0.6f );
139 
140  // default ambient intensity
141  material.SetShininess( 0.3f );
142  break;
143 
144  case 2:
145  // red
146  material.SetSpecular( 0.69f, 0.14f, 0.14f );
147  material.SetDiffuse( 0.69f, 0.14f, 0.14f );
148 
149  // default ambient intensity
150  material.SetShininess( 0.3f );
151  break;
152 
153  case 3:
154  // orange
155  material.SetSpecular( 1.0f, 0.44f, 0.0f );
156  material.SetDiffuse( 1.0f, 0.44f, 0.0f );
157 
158  // default ambient intensity
159  material.SetShininess( 0.3f );
160  break;
161 
162  case 4:
163  // yellow
164  material.SetSpecular( 0.93f, 0.94f, 0.16f );
165  material.SetDiffuse( 0.93f, 0.94f, 0.16f );
166 
167  // default ambient intensity
168  material.SetShininess( 0.3f );
169  break;
170 
171  case 5:
172  // blue
173  material.SetSpecular( 0.1f, 0.11f, 0.88f );
174  material.SetDiffuse( 0.1f, 0.11f, 0.88f );
175 
176  // default ambient intensity
177  material.SetShininess( 0.3f );
178  break;
179 
180  default:
181  // violet
182  material.SetSpecular( 0.32f, 0.07f, 0.64f );
183  material.SetDiffuse( 0.32f, 0.07f, 0.64f );
184 
185  // default ambient intensity
186  material.SetShininess( 0.3f );
187  break;
188  }
189 
190  if( ( colorIdx == -1 ) && ( ++cidx > NCOLORS ) )
191  cidx = 1;
192 
193  return material.GetRawPtr();
194 }
195 
196 
197 const char* GetKicadPluginName( void )
198 {
199  return "PLUGIN_3D_IDF";
200 }
201 
202 
203 void GetPluginVersion( unsigned char* Major, unsigned char* Minor, unsigned char* Patch,
204  unsigned char* Revision )
205 {
206  if( Major )
207  *Major = PLUGIN_3D_IDF_MAJOR;
208 
209  if( Minor )
210  *Minor = PLUGIN_3D_IDF_MINOR;
211 
212  if( Patch )
213  *Patch = PLUGIN_3D_IDF_PATCH;
214 
215  if( Revision )
216  *Revision = PLUGIN_3D_IDF_REVNO;
217 }
218 
219 
220 // number of extensions supported
221 #ifdef _WIN32
222  #define NEXTS 2
223 #else
224  #define NEXTS 4
225 #endif
226 
227 
228 // number of filter sets supported
229 #define NFILS 2
230 
231 
232 static char ext0[] = "idf";
233 static char ext1[] = "emn";
234 
235 
236 #ifdef _WIN32
237  static char fil0[] = "IDF (*.idf)|*.idf";
238  static char fil1[] = "IDF BRD v2/v3 (*.emn)|*.emn";
239 #else
240  static char ext2[] = "IDF";
241  static char ext3[] = "EMN";
242  static char fil0[] = "IDF (*.idf;*.IDF)|*.idf;*.IDF";
243  static char fil1[] = "IDF BRD (*.emn;*.EMN)|*.emn;*.EMN";
244 #endif
245 
246 static struct FILE_DATA
247 {
248  char const* extensions[NEXTS];
249  char const* filters[NFILS];
250 
252  {
253  extensions[0] = ext0;
254  extensions[1] = ext1;
255  filters[0] = fil0;
256  filters[1] = fil1;
257 
258 #ifndef _WIN32
259  extensions[2] = ext2;
260  extensions[3] = ext3;
261 #endif
262  }
263 
264 } file_data;
265 
266 
267 int GetNExtensions( void )
268 {
269  return NEXTS;
270 }
271 
272 
273 char const* GetModelExtension( int aIndex )
274 {
275  if( aIndex < 0 || aIndex >= NEXTS )
276  return nullptr;
277 
278  return file_data.extensions[aIndex];
279 }
280 
281 
282 int GetNFilters( void )
283 {
284  return NFILS;
285 }
286 
287 
288 char const* GetFileFilter( int aIndex )
289 {
290  if( aIndex < 0 || aIndex >= NFILS )
291  return nullptr;
292 
293  return file_data.filters[aIndex];
294 }
295 
296 
297 bool CanRender( void )
298 {
299  // this plugin supports rendering of IDF component outlines
300  return true;
301 }
302 
303 
304 SCENEGRAPH* Load( char const* aFileName )
305 {
306  if( nullptr == aFileName )
307  return nullptr;
308 
309  wxFileName fname;
310  fname.Assign( wxString::FromUTF8Unchecked( aFileName ) );
311 
312  wxString ext = fname.GetExt();
313 
314  SCENEGRAPH* data = nullptr;
315 
316  if( !ext.Cmp( wxT( "idf" ) ) || !ext.Cmp( wxT( "IDF" ) ) )
317  {
318  data = loadIDFOutline( fname.GetFullPath() );
319  }
320 
321  if( !ext.Cmp( wxT( "emn" ) ) || !ext.Cmp( wxT( "EMN" ) ) )
322  {
323  data = loadIDFBoard( fname.GetFullPath() );
324  }
325 
326  // DEBUG: WRITE OUT IDF FILE TO CONFIRM NORMALS
327 #if defined( DEBUG_IDF ) && DEBUG_IDF > 3
328  if( data )
329  {
330  wxFileName fn( aFileName );
331  wxString output = wxT( "_idf-" );
332  output.append( fn.GetName() );
333  output.append( wxT( ".wrl" ) );
334  S3D::WriteVRML( output.ToUTF8(), true, (SGNODE*) ( data ), true, true );
335  }
336 #endif
337 
338  return data;
339 }
340 
341 
342 static bool getOutlineModel( VRML_LAYER& model, const std::list< IDF_OUTLINE* >* items )
343 {
344  // empty outlines are not unusual so we fail quietly
345  if( items->size() < 1 )
346  return false;
347 
348  int nvcont = 0;
349  int iseg = 0;
350 
351  std::list< IDF_OUTLINE* >::const_iterator scont = items->begin();
352  std::list< IDF_OUTLINE* >::const_iterator econt = items->end();
353  std::list<IDF_SEGMENT*>::iterator sseg;
354  std::list<IDF_SEGMENT*>::iterator eseg;
355 
356  IDF_SEGMENT lseg;
357 
358  while( scont != econt )
359  {
360  nvcont = model.NewContour();
361 
362  if( nvcont < 0 )
363  {
364  wxLogTrace( traceIdfPlugin, wxT( "%s:%s:%s\n"
365  "* [INFO] cannot create an outline" ),
366  __FILE__, __FUNCTION__, __LINE__ );
367 
368  return false;
369  }
370 
371  if( (*scont)->size() < 1 )
372  {
373  wxLogTrace( traceIdfPlugin, wxT( "%s:%s:%s\n "
374  "* [INFO] invalid contour: no vertices" ),
375  __FILE__, __FUNCTION__, __LINE__ );
376 
377  return false;
378  }
379 
380  sseg = (*scont)->begin();
381  eseg = (*scont)->end();
382 
383  iseg = 0;
384 
385  while( sseg != eseg )
386  {
387  lseg = **sseg;
388 
389  if( !addSegment( model, &lseg, nvcont, iseg ) )
390  {
391  wxLogTrace( traceIdfPlugin, wxT( "%s:%s:%s\n"
392  "* [BUG] cannot add segment" ),
393  __FILE__, __FUNCTION__, __LINE__ );
394 
395  return false;
396  }
397 
398  ++iseg;
399  ++sseg;
400  }
401 
402  ++scont;
403  }
404 
405  return true;
406 }
407 
408 
409 static bool addSegment( VRML_LAYER& model, IDF_SEGMENT* seg, int icont, int iseg )
410 {
411  // note: in all cases we must add all but the last point in the segment
412  // to avoid redundant points
413 
414  if( seg->angle != 0.0 )
415  {
416  if( seg->IsCircle() )
417  {
418  if( iseg != 0 )
419  {
420  wxLogTrace( traceIdfPlugin,
421  wxT( "%s:%s:%s\n"
422  "* [INFO] adding a circle to an existing vertex list" ),
423  __FILE__, __FUNCTION__, __LINE__ );
424 
425  return false;
426  }
427 
428  return model.AppendCircle( seg->center.x, seg->center.y, seg->radius, icont );
429  }
430  else
431  {
432  return model.AppendArc( seg->center.x, seg->center.y, seg->radius,
433  seg->offsetAngle, seg->angle, icont );
434  }
435  }
436 
437  if( !model.AddVertex( icont, seg->startPoint.x, seg->startPoint.y ) )
438  return false;
439 
440  return true;
441 }
442 
443 
444 static SCENEGRAPH* vrmlToSG( VRML_LAYER& vpcb, int idxColor, SGNODE* aParent, double top,
445  double bottom )
446 {
447  vpcb.Tesselate( nullptr );
448  std::vector< double > vertices;
449  std::vector< int > idxPlane;
450  std::vector< int > idxSide;
451 
452  if( top < bottom )
453  {
454  double tmp = top;
455  top = bottom;
456  bottom = tmp;
457  }
458 
459  if( !vpcb.Get3DTriangles( vertices, idxPlane, idxSide, top, bottom ) )
460  {
461  wxLogTrace( traceIdfPlugin, wxT( "%s:%s:%s\n"
462  "* [INFO] no vertex data" ),
463  __FILE__, __FUNCTION__, __LINE__ );
464 
465  return nullptr;
466  }
467 
468  if( ( idxPlane.size() % 3 ) || ( idxSide.size() % 3 ) )
469  {
470  wxLogTrace( traceIdfPlugin,
471  wxT( "%s:%s:%s\n"
472  "* [BUG] index lists are not a multiple of 3 (not a triangle list)" ),
473  __FILE__, __FUNCTION__, __LINE__ );
474 
475  return nullptr;
476  }
477 
478  std::vector< SGPOINT > vlist;
479  size_t nvert = vertices.size() / 3;
480  size_t j = 0;
481 
482  for( size_t i = 0; i < nvert; ++i, j+= 3 )
483  vlist.emplace_back( vertices[j], vertices[j+1], vertices[j+2] );
484 
485  // create the intermediate scenegraph
486  IFSG_TRANSFORM* tx0 = new IFSG_TRANSFORM( aParent ); // tx0 = Transform for this outline
487 
488  // shape will hold (a) all vertices and (b) a local list of normals
489  IFSG_SHAPE* shape = new IFSG_SHAPE( *tx0 );
490 
491  // this face shall represent the top and bottom planes
492  IFSG_FACESET* face = new IFSG_FACESET( *shape );
493 
494  // coordinates for all faces
495  IFSG_COORDS* cp = new IFSG_COORDS( *face );
496  cp->SetCoordsList( nvert, &vlist[0] );
497 
498  // coordinate indices for top and bottom planes only.
499  IFSG_COORDINDEX* coordIdx = new IFSG_COORDINDEX( *face );
500  coordIdx->SetIndices( idxPlane.size(), &idxPlane[0] );
501 
502  // normals for the top and bottom planes.
503  IFSG_NORMALS* norms = new IFSG_NORMALS( *face );
504 
505  // number of TOP (and bottom) vertices
506  j = nvert / 2;
507 
508  // set the TOP normals
509  for( size_t i = 0; i < j; ++i )
510  norms->AddNormal( 0.0, 0.0, 1.0 );
511 
512  // set the BOTTOM normals
513  for( size_t i = 0; i < j; ++i )
514  norms->AddNormal( 0.0, 0.0, -1.0 );
515 
516  // assign a color from the palette
517  SGNODE* modelColor = getColor( *shape, idxColor );
518 
519  // create a second shape describing the vertical walls of the IDF extrusion
520  // using per-vertex-per-face-normals
521  shape->NewNode( *tx0 );
522  shape->AddRefNode( modelColor ); // set the color to be the same as the top/bottom
523  face->NewNode( *shape );
524  cp->NewNode( *face ); // new vertex list
525  norms->NewNode( *face ); // new normals list
526  coordIdx->NewNode( *face ); // new index list
527 
528  // populate the new per-face vertex list and its indices and normals
529  std::vector< int >::iterator sI = idxSide.begin();
530  std::vector< int >::iterator eI = idxSide.end();
531 
532  size_t sidx = 0; // index to the new coord set
533  SGPOINT p1, p2, p3;
534  SGVECTOR vnorm;
535 
536  while( sI != eI )
537  {
538  p1 = vlist[*sI];
539  cp->AddCoord( p1 );
540  ++sI;
541 
542  p2 = vlist[*sI];
543  cp->AddCoord( p2 );
544  ++sI;
545 
546  p3 = vlist[*sI];
547  cp->AddCoord( p3 );
548  ++sI;
549 
550  vnorm.SetVector( S3D::CalcTriNorm( p1, p2, p3 ) );
551  norms->AddNormal( vnorm );
552  norms->AddNormal( vnorm );
553  norms->AddNormal( vnorm );
554 
555  coordIdx->AddIndex( (int)sidx );
556  ++sidx;
557  coordIdx->AddIndex( (int)sidx );
558  ++sidx;
559  coordIdx->AddIndex( (int)sidx );
560  ++sidx;
561  }
562 
563  SCENEGRAPH* data = (SCENEGRAPH*)tx0->GetRawPtr();
564 
565  // delete the API wrappers
566  delete shape;
567  delete face;
568  delete coordIdx;
569  delete cp;
570  delete tx0;
571 
572  return data;
573 }
574 
575 
576 static SCENEGRAPH* addOutline( IDF3_COMP_OUTLINE* outline, int idxColor, SGNODE* aParent )
577 {
578  VRML_LAYER vpcb;
579 
580  if( !getOutlineModel( vpcb, outline->GetOutlines() ) )
581  {
582  wxLogTrace( traceIdfPlugin, wxT( "%s:%s:%s\n"
583  "* [INFO] no valid outline data" ),
584  __FILE__, __FUNCTION__, __LINE__ );
585 
586  return nullptr;
587  }
588 
589  vpcb.EnsureWinding( 0, false );
590 
591  double top = outline->GetThickness();
592  double bot = 0.0;
593 
594  // note: some IDF entities permit negative heights
595  if( top < bot )
596  {
597  bot = top;
598  top = 0.0;
599  }
600 
601  SCENEGRAPH* data = vrmlToSG( vpcb, idxColor, aParent, top, bot );
602 
603  return data;
604 }
605 
606 
607 static SCENEGRAPH* loadIDFOutline( const wxString& aFileName )
608 {
609  LOCALESWITCH switcher;
610  IDF3_BOARD brd( IDF3::CAD_ELEC );
611  IDF3_COMP_OUTLINE* outline = nullptr;
612 
613  outline = brd.GetComponentOutline( aFileName );
614 
615  if( nullptr == outline )
616  {
617  wxLogTrace( traceIdfPlugin, wxT( "%s:%s:%s\n"
618  "* [INFO] Failed to read IDF data:\n"
619  "%s\n"
620  "* [INFO] no outline for file '%s'" ),
621  __FILE__, __FUNCTION__, __LINE__,
622  brd.GetError(),
623  aFileName );
624 
625  return nullptr;
626  }
627 
628  SCENEGRAPH* data = addOutline( outline, -1, nullptr );
629 
630  return data;
631 }
632 
633 
634 static SCENEGRAPH* loadIDFBoard( const wxString& aFileName )
635 {
636  LOCALESWITCH switcher;
637  IDF3_BOARD brd( IDF3::CAD_ELEC );
638 
639  // note: if the IDF model is defective no outline substitutes shall be made
640  if( !brd.ReadFile( aFileName, true ) )
641  {
642  wxLogTrace( traceIdfPlugin, wxT( "%s:%s:%s\n"
643  "* [INFO] Error '%s' occurred reading IDF file: %s" ),
644  __FILE__, __FUNCTION__, __LINE__,
645  brd.GetError(),
646  aFileName );
647 
648  return nullptr;
649  }
650 
651  IFSG_TRANSFORM tx0( true );
652  SGNODE* topNode = tx0.GetRawPtr();
653 
654  bool noBoard = false;
655  bool noComp = false;
656  bool noOther = false;
657 
658  if( nullptr == makeBoard( brd, topNode ) )
659  noBoard = true;
660 
661  if( !makeComponents( brd, topNode ) )
662  noComp = true;
663 
664  if( !makeOtherOutlines( brd, topNode ) )
665  noOther = true;
666 
667  if( noBoard && noComp && noOther )
668  {
669  tx0.Destroy();
670  return nullptr;
671  }
672 
673  return (SCENEGRAPH*) topNode;
674 }
675 
676 
677 static SCENEGRAPH* makeBoard( IDF3_BOARD& brd, SGNODE* aParent )
678 {
679  if( nullptr == aParent )
680  return nullptr;
681 
682  VRML_LAYER vpcb;
683 
684  // check if no board outline
685  if( brd.GetBoardOutlinesSize() < 1 )
686  return nullptr;
687 
688 
689  if( !getOutlineModel( vpcb, brd.GetBoardOutline()->GetOutlines() ) )
690  return nullptr;
691 
692  vpcb.EnsureWinding( 0, false );
693 
694  int nvcont = vpcb.GetNContours() - 1;
695 
696  while( nvcont > 0 )
697  vpcb.EnsureWinding( nvcont--, true );
698 
699  // Add the drill holes
700  const std::list<IDF_DRILL_DATA*>* drills = &brd.GetBoardDrills();
701 
702  std::list<IDF_DRILL_DATA*>::const_iterator sd = drills->begin();
703  std::list<IDF_DRILL_DATA*>::const_iterator ed = drills->end();
704 
705  while( sd != ed )
706  {
707  vpcb.AddCircle( (*sd)->GetDrillXPos(), (*sd)->GetDrillYPos(),
708  (*sd)->GetDrillDia() / 2.0, true );
709  ++sd;
710  }
711 
712  std::map< std::string, IDF3_COMPONENT* >*const comp = brd.GetComponents();
713  std::map< std::string, IDF3_COMPONENT* >::const_iterator sc = comp->begin();
714  std::map< std::string, IDF3_COMPONENT* >::const_iterator ec = comp->end();
715 
716  while( sc != ec )
717  {
718  drills = sc->second->GetDrills();
719  sd = drills->begin();
720  ed = drills->end();
721 
722  while( sd != ed )
723  {
724  vpcb.AddCircle( (*sd)->GetDrillXPos(), (*sd)->GetDrillYPos(),
725  (*sd)->GetDrillDia() / 2.0, true );
726  ++sd;
727  }
728 
729  ++sc;
730  }
731 
732  double top = brd.GetBoardThickness();
733 
734  SCENEGRAPH* data = vrmlToSG( vpcb, 0, aParent, top, 0.0 );
735 
736  return data;
737 }
738 
739 
740 static bool makeComponents( IDF3_BOARD& brd, SGNODE* aParent )
741 {
742  if( nullptr == aParent )
743  return false;
744 
745  int ncomponents = 0;
746 
747  double brdTop = brd.GetBoardThickness();
748 
749  // Add the component outlines
750  const std::map< std::string, IDF3_COMPONENT* >*const comp = brd.GetComponents();
751  std::map< std::string, IDF3_COMPONENT* >::const_iterator sc = comp->begin();
752  std::map< std::string, IDF3_COMPONENT* >::const_iterator ec = comp->end();
753 
754  std::list< IDF3_COMP_OUTLINE_DATA* >::const_iterator so;
755  std::list< IDF3_COMP_OUTLINE_DATA* >::const_iterator eo;
756 
757  double vX, vY, vA;
758  double tX, tY, tZ, tA;
759  bool bottom;
760  IDF3::IDF_LAYER lyr;
761 
762  std::map< std::string, SGNODE* > dataMap; // map data by UID
763  std::map< std::string, SGNODE* >::iterator dataItem;
764  IDF3_COMP_OUTLINE* pout;
765 
766  while( sc != ec )
767  {
768  sc->second->GetPosition( vX, vY, vA, lyr );
769 
770  if( lyr == IDF3::LYR_BOTTOM )
771  bottom = true;
772  else
773  bottom = false;
774 
775  so = sc->second->GetOutlinesData()->begin();
776  eo = sc->second->GetOutlinesData()->end();
777 
778  while( so != eo )
779  {
780  if( std::abs( (*so)->GetOutline()->GetThickness() ) < 0.001 )
781  {
782  ++so;
783  continue;
784  }
785 
786  (*so)->GetOffsets( tX, tY, tZ, tA );
787  tX += vX;
788  tY += vY;
789  tA += vA;
790 
791  pout = (IDF3_COMP_OUTLINE*)((*so)->GetOutline());
792 
793  if( nullptr == pout )
794  {
795  ++so;
796  continue;
797  }
798 
799  dataItem = dataMap.find( pout->GetUID() );
800  SCENEGRAPH* sg = nullptr;
801 
802  if( dataItem == dataMap.end() )
803  {
804  sg = addOutline( pout, -1, nullptr );
805 
806  if( nullptr == sg )
807  {
808  ++so;
809  continue;
810  }
811 
812  ++ncomponents;
813  dataMap.insert( std::pair< std::string, SGNODE* >( pout->GetUID(), (SGNODE*)sg ) );
814  }
815  else
816  {
817  sg = (SCENEGRAPH*) dataItem->second;
818  }
819 
820  IFSG_TRANSFORM tx0( aParent );
821  IFSG_TRANSFORM txN( false );
822  txN.Attach( (SGNODE*)sg );
823 
824  if( nullptr == txN.GetParent() )
825  tx0.AddChildNode( txN );
826  else
827  tx0.AddRefNode( txN );
828 
829  if( bottom )
830  {
831  tx0.SetTranslation( SGPOINT( tX, tY, -tZ ) );
832  // for an item on the back of the board we have a compounded rotation,
833  // first a flip on the Y axis as per the IDF spec and then a rotation
834  // of -tA degrees on the Z axis. The resultant rotation axis is an
835  // XY vector equivalent to (0,1) rotated by -(tA/2) degrees
836  //
837  double ang = -tA * M_PI / 360.0;
838  double sinA = sin( ang );
839  double cosA = cos( ang );
840  tx0.SetRotation( SGVECTOR( -sinA, cosA , 0 ), M_PI );
841  }
842  else
843  {
844  tx0.SetTranslation( SGPOINT( tX, tY, tZ + brdTop ) );
845  tx0.SetRotation( SGVECTOR( 0, 0, 1 ), tA * M_PI / 180.0 );
846  }
847 
848  ++so;
849  }
850 
851  ++sc;
852  }
853 
854  if( 0 == ncomponents )
855  return false;
856 
857  return true;
858 }
859 
860 
861 static bool makeOtherOutlines( IDF3_BOARD& brd, SGNODE* aParent )
862 {
863  if( nullptr == aParent )
864  return false;
865 
866  VRML_LAYER vpcb;
867  int ncomponents = 0;
868 
869  double brdTop = brd.GetBoardThickness();
870  double top, bot;
871 
872  // Add the component outlines
873  const std::map< std::string, OTHER_OUTLINE* >*const comp = brd.GetOtherOutlines();
874  std::map< std::string, OTHER_OUTLINE* >::const_iterator sc = comp->begin();
875  std::map< std::string, OTHER_OUTLINE* >::const_iterator ec = comp->end();
876 
877  int nvcont;
878 
879  OTHER_OUTLINE* pout;
880 
881  while( sc != ec )
882  {
883  pout = sc->second;
884 
885  if( std::abs( pout->GetThickness() ) < 0.001 )
886  {
887  ++sc;
888  continue;
889  }
890 
891  if( !getOutlineModel( vpcb, pout->GetOutlines() ) )
892  {
893  vpcb.Clear();
894  ++sc;
895  continue;
896  }
897 
898  vpcb.EnsureWinding( 0, false );
899 
900  nvcont = vpcb.GetNContours() - 1;
901 
902  while( nvcont > 0 )
903  vpcb.EnsureWinding( nvcont--, true );
904 
905  if( pout->GetSide() == IDF3::LYR_BOTTOM )
906  {
907  top = 0.0;
908  bot = -pout->GetThickness();
909  }
910  else
911  {
912  bot = brdTop;
913  top = bot + pout->GetThickness();
914  }
915 
916  if( nullptr == vrmlToSG( vpcb, -1, aParent, top, bot ) )
917  {
918  vpcb.Clear();
919  ++sc;
920  continue;
921  }
922 
923  ++ncomponents;
924 
925  vpcb.Clear();
926  ++sc;
927  }
928 
929  if( 0 == ncomponents )
930  return false;
931 
932  return true;
933 }
#define NCOLORS
static SCENEGRAPH * vrmlToSG(VRML_LAYER &vpcb, int idxColor, SGNODE *aParent, double top, double bottom)
static SCENEGRAPH * loadIDFOutline(const wxString &aFileName)
char const * GetModelExtension(int aIndex)
Function GetModelExtension.
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
static bool makeOtherOutlines(IDF3_BOARD &brd, SGNODE *aParent)
SGNODE * GetParent(void) const
Function GetParent returns a pointer to the parent SGNODE of this object or NULL if the object has no...
Definition: ifsg_node.cpp:79
IFSG_COORDS is the wrapper for SGCOORDS.
Definition: ifsg_coords.h:40
IFSG_COORDINDEX is the wrapper for SGCOORDINDEX.
bool Attach(SGNODE *aNode) override
Function Attach associates a given SGNODE* with this wrapper.
SCENEGRAPH * Load(char const *aFileName)
reads a model file and creates a generic display structure
bool SetDiffuse(float aRVal, float aGVal, float aBVal)
static bool getOutlineModel(VRML_LAYER &model, const std::list< IDF_OUTLINE * > *items)
bool AddRefNode(SGNODE *aNode)
Function AddRefNode adds a reference to an existing node which is not owned by (not a child of) this ...
Definition: ifsg_node.cpp:128
void SetVector(double aXVal, double aYVal, double aZVal)
Definition: sg_base.cpp:233
#define NEXTS
static char ext3[]
int GetNExtensions(void)
Function GetNExtensions.
static SCENEGRAPH * makeBoard(IDF3_BOARD &brd, SGNODE *aParent)
static SCENEGRAPH * addOutline(IDF3_COMP_OUTLINE *outline, int idxColor, SGNODE *aParent)
The base class of all Scene Graph nodes.
Definition: sg_node.h:74
collects header files for all SG* wrappers and the API
describes the runtime-loadable interface to support loading and parsing of 3D models.
SGNODE * GetRawPtr(void) noexcept
Function GetRawPtr() returns the raw internal SGNODE pointer.
Definition: ifsg_node.cpp:65
static char ext0[]
#define PLUGIN_3D_IDF_REVNO
bool AddChildNode(SGNODE *aNode)
Function AddChildNode adds a node as a child owned by this node.
Definition: ifsg_node.cpp:148
bool CanRender(void)
Function CanRender.
bool AddIndex(int aIndex)
Function AddIndex adds a single index to the list.
Definition: ifsg_index.cpp:57
SGLIB_API bool WriteVRML(const char *filename, bool overwrite, SGNODE *aTopNode, bool reuse, bool renameNodes)
Function WriteVRML writes out the given node and its subnodes to a VRML2 file.
Definition: ifsg_api.cpp:77
#define PLUGIN_3D_IDF_MINOR
char const * extensions[NEXTS]
bool AddNormal(double aXValue, double aYValue, double aZValue)
IFSG_NORMALS is the wrapper for the SGNORMALS class.
Definition: ifsg_normals.h:40
static SCENEGRAPH * loadIDFBoard(const wxString &aFileName)
static char fil1[]
bool SetRotation(const SGVECTOR &aRotationAxis, double aAngle)
bool SetShininess(float aShininess) noexcept
#define PLUGIN_3D_IDF_PATCH
char const * GetFileFilter(int aIndex)
Function GetFileFilter.
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
bool SetIndices(size_t nIndices, int *aIndexList)
Function SetIndices sets the number of indices and creates a copy of the given index data.
Definition: ifsg_index.cpp:47
static SGNODE * getColor(IFSG_SHAPE &shape, int colorIdx)
SGLIB_API SGVECTOR CalcTriNorm(const SGPOINT &p1, const SGPOINT &p2, const SGPOINT &p3)
Function CalcTriNorm returns the normal vector of a triangle described by vertices p1,...
Definition: ifsg_api.cpp:464
#define NFILS
static char ext1[]
bool SetSpecular(float aRVal, float aGVal, float aBVal)
bool SetCoordsList(size_t aListSize, const SGPOINT *aCoordsList)
static char ext2[]
#define PLUGIN_3D_IDF_MAJOR
IFSG_FACESET is the wrapper for the SGFACESET class.
Definition: ifsg_faceset.h:40
static char fil0[]
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
IFSG_TRANSFORM is the wrapper for the VRML compatible TRANSFORM block class SCENEGRAPH.
char const * filters[NFILS]
bool SetTranslation(const SGPOINT &aTranslation) noexcept
Define the basic data set required to represent a 3D model.
Definition: scenegraph.h:44
const wxChar *const traceIdfPlugin
Flag to enable IDF plugin trace output.
void Destroy(void)
Function Destroy deletes the object held by this wrapper.
Definition: ifsg_node.cpp:55
const char * GetKicadPluginName(void)
Function GetKicadPluginName returns the name of the plugin instance; for example IDFv3.
void GetPluginVersion(unsigned char *Major, unsigned char *Minor, unsigned char *Patch, unsigned char *Revision)
Function GetPluginVersion retrieves the version of the instantiated plugin for informational purposes...
static struct FILE_DATA file_data
static bool makeComponents(IDF3_BOARD &brd, SGNODE *aParent)
bool AddCoord(double aXValue, double aYValue, double aZValue)
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.
Definition: ifsg_shape.cpp:121
int GetNFilters(void)
Function GetNFilters.
static bool addSegment(VRML_LAYER &model, IDF_SEGMENT *seg, int icont, int iseg)
IFSG_SHAPE is the wrapper for the SGSHAPE class.
Definition: ifsg_shape.h:40
bool NewNode(SGNODE *aParent) override
Function NewNode creates a new node to associate with this wrapper.