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