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, "%s:%s:%s\n * [INFO] cannot create an outline",
365  __FILE__, __FUNCTION__, __LINE__ );
366 
367  return false;
368  }
369 
370  if( (*scont)->size() < 1 )
371  {
372  wxLogTrace( traceIdfPlugin, "%s:%s:%s\n * [INFO] invalid contour: no vertices",
373  __FILE__, __FUNCTION__, __LINE__ );
374 
375  return false;
376  }
377 
378  sseg = (*scont)->begin();
379  eseg = (*scont)->end();
380 
381  iseg = 0;
382 
383  while( sseg != eseg )
384  {
385  lseg = **sseg;
386 
387  if( !addSegment( model, &lseg, nvcont, iseg ) )
388  {
389  wxLogTrace( traceIdfPlugin, "%s:%s:%s\n * [BUG] cannot add segment",
390  __FILE__, __FUNCTION__, __LINE__ );
391 
392  return false;
393  }
394 
395  ++iseg;
396  ++sseg;
397  }
398 
399  ++scont;
400  }
401 
402  return true;
403 }
404 
405 
406 static bool addSegment( VRML_LAYER& model, IDF_SEGMENT* seg, int icont, int iseg )
407 {
408  // note: in all cases we must add all but the last point in the segment
409  // to avoid redundant points
410 
411  if( seg->angle != 0.0 )
412  {
413  if( seg->IsCircle() )
414  {
415  if( iseg != 0 )
416  {
417  wxLogTrace( traceIdfPlugin, "%s:%s:%s\n * [INFO] adding a circle to an "
418  "existing vertex list", __FILE__, __FUNCTION__, __LINE__ );
419 
420  return false;
421  }
422 
423  return model.AppendCircle( seg->center.x, seg->center.y, seg->radius, icont );
424  }
425  else
426  {
427  return model.AppendArc( seg->center.x, seg->center.y, seg->radius,
428  seg->offsetAngle, seg->angle, icont );
429  }
430  }
431 
432  if( !model.AddVertex( icont, seg->startPoint.x, seg->startPoint.y ) )
433  return false;
434 
435  return true;
436 }
437 
438 
439 static SCENEGRAPH* vrmlToSG( VRML_LAYER& vpcb, int idxColor, SGNODE* aParent, double top,
440  double bottom )
441 {
442  vpcb.Tesselate( nullptr );
443  std::vector< double > vertices;
444  std::vector< int > idxPlane;
445  std::vector< int > idxSide;
446 
447  if( top < bottom )
448  {
449  double tmp = top;
450  top = bottom;
451  bottom = tmp;
452  }
453 
454  if( !vpcb.Get3DTriangles( vertices, idxPlane, idxSide, top, bottom ) )
455  {
456  wxLogTrace( traceIdfPlugin, "%s:%s:%s\n * [INFO] no vertex data",
457  __FILE__, __FUNCTION__, __LINE__ );
458 
459  return nullptr;
460  }
461 
462  if( ( idxPlane.size() % 3 ) || ( idxSide.size() % 3 ) )
463  {
464  wxLogTrace( traceIdfPlugin, "%s:%s:%s\n * [BUG] index lists are not a multiple of 3 "
465  "(not a triangle list)", __FILE__, __FUNCTION__, __LINE__ );
466 
467  return nullptr;
468  }
469 
470  std::vector< SGPOINT > vlist;
471  size_t nvert = vertices.size() / 3;
472  size_t j = 0;
473 
474  for( size_t i = 0; i < nvert; ++i, j+= 3 )
475  vlist.emplace_back( vertices[j], vertices[j+1], vertices[j+2] );
476 
477  // create the intermediate scenegraph
478  IFSG_TRANSFORM* tx0 = new IFSG_TRANSFORM( aParent ); // tx0 = Transform for this outline
479 
480  // shape will hold (a) all vertices and (b) a local list of normals
481  IFSG_SHAPE* shape = new IFSG_SHAPE( *tx0 );
482 
483  // this face shall represent the top and bottom planes
484  IFSG_FACESET* face = new IFSG_FACESET( *shape );
485 
486  // coordinates for all faces
487  IFSG_COORDS* cp = new IFSG_COORDS( *face );
488  cp->SetCoordsList( nvert, &vlist[0] );
489 
490  // coordinate indices for top and bottom planes only.
491  IFSG_COORDINDEX* coordIdx = new IFSG_COORDINDEX( *face );
492  coordIdx->SetIndices( idxPlane.size(), &idxPlane[0] );
493 
494  // normals for the top and bottom planes.
495  IFSG_NORMALS* norms = new IFSG_NORMALS( *face );
496 
497  // number of TOP (and bottom) vertices
498  j = nvert / 2;
499 
500  // set the TOP normals
501  for( size_t i = 0; i < j; ++i )
502  norms->AddNormal( 0.0, 0.0, 1.0 );
503 
504  // set the BOTTOM normals
505  for( size_t i = 0; i < j; ++i )
506  norms->AddNormal( 0.0, 0.0, -1.0 );
507 
508  // assign a color from the palette
509  SGNODE* modelColor = getColor( *shape, idxColor );
510 
511  // create a second shape describing the vertical walls of the IDF extrusion
512  // using per-vertex-per-face-normals
513  shape->NewNode( *tx0 );
514  shape->AddRefNode( modelColor ); // set the color to be the same as the top/bottom
515  face->NewNode( *shape );
516  cp->NewNode( *face ); // new vertex list
517  norms->NewNode( *face ); // new normals list
518  coordIdx->NewNode( *face ); // new index list
519 
520  // populate the new per-face vertex list and its indices and normals
521  std::vector< int >::iterator sI = idxSide.begin();
522  std::vector< int >::iterator eI = idxSide.end();
523 
524  size_t sidx = 0; // index to the new coord set
525  SGPOINT p1, p2, p3;
526  SGVECTOR vnorm;
527 
528  while( sI != eI )
529  {
530  p1 = vlist[*sI];
531  cp->AddCoord( p1 );
532  ++sI;
533 
534  p2 = vlist[*sI];
535  cp->AddCoord( p2 );
536  ++sI;
537 
538  p3 = vlist[*sI];
539  cp->AddCoord( p3 );
540  ++sI;
541 
542  vnorm.SetVector( S3D::CalcTriNorm( p1, p2, p3 ) );
543  norms->AddNormal( vnorm );
544  norms->AddNormal( vnorm );
545  norms->AddNormal( vnorm );
546 
547  coordIdx->AddIndex( (int)sidx );
548  ++sidx;
549  coordIdx->AddIndex( (int)sidx );
550  ++sidx;
551  coordIdx->AddIndex( (int)sidx );
552  ++sidx;
553  }
554 
555  SCENEGRAPH* data = (SCENEGRAPH*)tx0->GetRawPtr();
556 
557  // delete the API wrappers
558  delete shape;
559  delete face;
560  delete coordIdx;
561  delete cp;
562  delete tx0;
563 
564  return data;
565 }
566 
567 
568 static SCENEGRAPH* addOutline( IDF3_COMP_OUTLINE* outline, int idxColor, SGNODE* aParent )
569 {
570  VRML_LAYER vpcb;
571 
572  if( !getOutlineModel( vpcb, outline->GetOutlines() ) )
573  {
574  wxLogTrace( traceIdfPlugin, "%s:%s:%s\n * [INFO] no valid outline data",
575  __FILE__, __FUNCTION__, __LINE__ );
576 
577  return nullptr;
578  }
579 
580  vpcb.EnsureWinding( 0, false );
581 
582  double top = outline->GetThickness();
583  double bot = 0.0;
584 
585  // note: some IDF entities permit negative heights
586  if( top < bot )
587  {
588  bot = top;
589  top = 0.0;
590  }
591 
592  SCENEGRAPH* data = vrmlToSG( vpcb, idxColor, aParent, top, bot );
593 
594  return data;
595 }
596 
597 
598 static SCENEGRAPH* loadIDFOutline( const wxString& aFileName )
599 {
600  LOCALESWITCH switcher;
601  IDF3_BOARD brd( IDF3::CAD_ELEC );
602  IDF3_COMP_OUTLINE* outline = nullptr;
603 
604  outline = brd.GetComponentOutline( aFileName );
605 
606  if( nullptr == outline )
607  {
608  wxLogTrace( traceIdfPlugin, "%s:%s:%s\n * [INFO] Failed to read IDF data:\n%s\n"
609  " * [INFO] no outline for file '%s'", __FILE__, __FUNCTION__, __LINE__,
610  brd.GetError(), aFileName );
611 
612  return nullptr;
613  }
614 
615  SCENEGRAPH* data = addOutline( outline, -1, nullptr );
616 
617  return data;
618 }
619 
620 
621 static SCENEGRAPH* loadIDFBoard( const wxString& aFileName )
622 {
623  LOCALESWITCH switcher;
624  IDF3_BOARD brd( IDF3::CAD_ELEC );
625 
626  // note: if the IDF model is defective no outline substitutes shall be made
627  if( !brd.ReadFile( aFileName, true ) )
628  {
629  wxLogTrace( traceIdfPlugin, "%s:%s:%s\n"
630  "* [INFO] Error '%s' occurred reading IDF file: %s",
631  __FILE__, __FUNCTION__, __LINE__, brd.GetError(), aFileName );
632 
633  return nullptr;
634  }
635 
636  IFSG_TRANSFORM tx0( true );
637  SGNODE* topNode = tx0.GetRawPtr();
638 
639  bool noBoard = false;
640  bool noComp = false;
641  bool noOther = false;
642 
643  if( nullptr == makeBoard( brd, topNode ) )
644  noBoard = true;
645 
646  if( !makeComponents( brd, topNode ) )
647  noComp = true;
648 
649  if( !makeOtherOutlines( brd, topNode ) )
650  noOther = true;
651 
652  if( noBoard && noComp && noOther )
653  {
654  tx0.Destroy();
655  return nullptr;
656  }
657 
658  return (SCENEGRAPH*) topNode;
659 }
660 
661 
662 static SCENEGRAPH* makeBoard( IDF3_BOARD& brd, SGNODE* aParent )
663 {
664  if( nullptr == aParent )
665  return nullptr;
666 
667  VRML_LAYER vpcb;
668 
669  // check if no board outline
670  if( brd.GetBoardOutlinesSize() < 1 )
671  return nullptr;
672 
673 
674  if( !getOutlineModel( vpcb, brd.GetBoardOutline()->GetOutlines() ) )
675  return nullptr;
676 
677  vpcb.EnsureWinding( 0, false );
678 
679  int nvcont = vpcb.GetNContours() - 1;
680 
681  while( nvcont > 0 )
682  vpcb.EnsureWinding( nvcont--, true );
683 
684  // Add the drill holes
685  const std::list<IDF_DRILL_DATA*>* drills = &brd.GetBoardDrills();
686 
687  std::list<IDF_DRILL_DATA*>::const_iterator sd = drills->begin();
688  std::list<IDF_DRILL_DATA*>::const_iterator ed = drills->end();
689 
690  while( sd != ed )
691  {
692  vpcb.AddCircle( (*sd)->GetDrillXPos(), (*sd)->GetDrillYPos(),
693  (*sd)->GetDrillDia() / 2.0, true );
694  ++sd;
695  }
696 
697  std::map< std::string, IDF3_COMPONENT* >*const comp = brd.GetComponents();
698  std::map< std::string, IDF3_COMPONENT* >::const_iterator sc = comp->begin();
699  std::map< std::string, IDF3_COMPONENT* >::const_iterator ec = comp->end();
700 
701  while( sc != ec )
702  {
703  drills = sc->second->GetDrills();
704  sd = drills->begin();
705  ed = drills->end();
706 
707  while( sd != ed )
708  {
709  vpcb.AddCircle( (*sd)->GetDrillXPos(), (*sd)->GetDrillYPos(),
710  (*sd)->GetDrillDia() / 2.0, true );
711  ++sd;
712  }
713 
714  ++sc;
715  }
716 
717  double top = brd.GetBoardThickness();
718 
719  SCENEGRAPH* data = vrmlToSG( vpcb, 0, aParent, top, 0.0 );
720 
721  return data;
722 }
723 
724 
725 static bool makeComponents( IDF3_BOARD& brd, SGNODE* aParent )
726 {
727  if( nullptr == aParent )
728  return false;
729 
730  int ncomponents = 0;
731 
732  double brdTop = brd.GetBoardThickness();
733 
734  // Add the component outlines
735  const std::map< std::string, IDF3_COMPONENT* >*const comp = brd.GetComponents();
736  std::map< std::string, IDF3_COMPONENT* >::const_iterator sc = comp->begin();
737  std::map< std::string, IDF3_COMPONENT* >::const_iterator ec = comp->end();
738 
739  std::list< IDF3_COMP_OUTLINE_DATA* >::const_iterator so;
740  std::list< IDF3_COMP_OUTLINE_DATA* >::const_iterator eo;
741 
742  double vX, vY, vA;
743  double tX, tY, tZ, tA;
744  bool bottom;
745  IDF3::IDF_LAYER lyr;
746 
747  std::map< std::string, SGNODE* > dataMap; // map data by UID
748  std::map< std::string, SGNODE* >::iterator dataItem;
749  IDF3_COMP_OUTLINE* pout;
750 
751  while( sc != ec )
752  {
753  sc->second->GetPosition( vX, vY, vA, lyr );
754 
755  if( lyr == IDF3::LYR_BOTTOM )
756  bottom = true;
757  else
758  bottom = false;
759 
760  so = sc->second->GetOutlinesData()->begin();
761  eo = sc->second->GetOutlinesData()->end();
762 
763  while( so != eo )
764  {
765  if( std::abs( (*so)->GetOutline()->GetThickness() ) < 0.001 )
766  {
767  ++so;
768  continue;
769  }
770 
771  (*so)->GetOffsets( tX, tY, tZ, tA );
772  tX += vX;
773  tY += vY;
774  tA += vA;
775 
776  pout = (IDF3_COMP_OUTLINE*)((*so)->GetOutline());
777 
778  if( nullptr == pout )
779  {
780  ++so;
781  continue;
782  }
783 
784  dataItem = dataMap.find( pout->GetUID() );
785  SCENEGRAPH* sg = nullptr;
786 
787  if( dataItem == dataMap.end() )
788  {
789  sg = addOutline( pout, -1, nullptr );
790 
791  if( nullptr == sg )
792  {
793  ++so;
794  continue;
795  }
796 
797  ++ncomponents;
798  dataMap.insert( std::pair< std::string, SGNODE* >( pout->GetUID(), (SGNODE*)sg ) );
799  }
800  else
801  {
802  sg = (SCENEGRAPH*) dataItem->second;
803  }
804 
805  IFSG_TRANSFORM tx0( aParent );
806  IFSG_TRANSFORM txN( false );
807  txN.Attach( (SGNODE*)sg );
808 
809  if( nullptr == txN.GetParent() )
810  tx0.AddChildNode( txN );
811  else
812  tx0.AddRefNode( txN );
813 
814  if( bottom )
815  {
816  tx0.SetTranslation( SGPOINT( tX, tY, -tZ ) );
817  // for an item on the back of the board we have a compounded rotation,
818  // first a flip on the Y axis as per the IDF spec and then a rotation
819  // of -tA degrees on the Z axis. The resultant rotation axis is an
820  // XY vector equivalent to (0,1) rotated by -(tA/2) degrees
821  //
822  double ang = -tA * M_PI / 360.0;
823  double sinA = sin( ang );
824  double cosA = cos( ang );
825  tx0.SetRotation( SGVECTOR( -sinA, cosA , 0 ), M_PI );
826  }
827  else
828  {
829  tx0.SetTranslation( SGPOINT( tX, tY, tZ + brdTop ) );
830  tx0.SetRotation( SGVECTOR( 0, 0, 1 ), tA * M_PI / 180.0 );
831  }
832 
833  ++so;
834  }
835 
836  ++sc;
837  }
838 
839  if( 0 == ncomponents )
840  return false;
841 
842  return true;
843 }
844 
845 
846 static bool makeOtherOutlines( IDF3_BOARD& brd, SGNODE* aParent )
847 {
848  if( nullptr == aParent )
849  return false;
850 
851  VRML_LAYER vpcb;
852  int ncomponents = 0;
853 
854  double brdTop = brd.GetBoardThickness();
855  double top, bot;
856 
857  // Add the component outlines
858  const std::map< std::string, OTHER_OUTLINE* >*const comp = brd.GetOtherOutlines();
859  std::map< std::string, OTHER_OUTLINE* >::const_iterator sc = comp->begin();
860  std::map< std::string, OTHER_OUTLINE* >::const_iterator ec = comp->end();
861 
862  int nvcont;
863 
864  OTHER_OUTLINE* pout;
865 
866  while( sc != ec )
867  {
868  pout = sc->second;
869 
870  if( std::abs( pout->GetThickness() ) < 0.001 )
871  {
872  ++sc;
873  continue;
874  }
875 
876  if( !getOutlineModel( vpcb, pout->GetOutlines() ) )
877  {
878  vpcb.Clear();
879  ++sc;
880  continue;
881  }
882 
883  vpcb.EnsureWinding( 0, false );
884 
885  nvcont = vpcb.GetNContours() - 1;
886 
887  while( nvcont > 0 )
888  vpcb.EnsureWinding( nvcont--, true );
889 
890  if( pout->GetSide() == IDF3::LYR_BOTTOM )
891  {
892  top = 0.0;
893  bot = -pout->GetThickness();
894  }
895  else
896  {
897  bot = brdTop;
898  top = bot + pout->GetThickness();
899  }
900 
901  if( nullptr == vrmlToSG( vpcb, -1, aParent, top, bot ) )
902  {
903  vpcb.Clear();
904  ++sc;
905  continue;
906  }
907 
908  ++ncomponents;
909 
910  vpcb.Clear();
911  ++sc;
912  }
913 
914  if( 0 == ncomponents )
915  return false;
916 
917  return true;
918 }
#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:76
#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:462
#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:118
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.