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 * 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
50 /**
51 * Flag to enable IDF plugin trace output.
52 *
53 * @ingroup trace_env_vars
54 */
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
97 class LOCALESWITCH
98 {
99 public:
LOCALESWITCH()100 LOCALESWITCH()
101 {
102 setlocale( LC_NUMERIC, "C" );
103 }
104
~LOCALESWITCH()105 ~LOCALESWITCH()
106 {
107 setlocale( LC_NUMERIC, "" );
108 }
109 };
110
111
getColor(IFSG_SHAPE & shape,int colorIdx)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
GetKicadPluginName(void)197 const char* GetKicadPluginName( void )
198 {
199 return "PLUGIN_3D_IDF";
200 }
201
202
GetPluginVersion(unsigned char * Major,unsigned char * Minor,unsigned char * Patch,unsigned char * Revision)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
FILE_DATAFILE_DATA251 FILE_DATA()
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
GetNExtensions(void)267 int GetNExtensions( void )
268 {
269 return NEXTS;
270 }
271
272
GetModelExtension(int aIndex)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
GetNFilters(void)282 int GetNFilters( void )
283 {
284 return NFILS;
285 }
286
287
GetFileFilter(int aIndex)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
CanRender(void)297 bool CanRender( void )
298 {
299 // this plugin supports rendering of IDF component outlines
300 return true;
301 }
302
303
Load(char const * aFileName)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
getOutlineModel(VRML_LAYER & model,const std::list<IDF_OUTLINE * > * items)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
addSegment(VRML_LAYER & model,IDF_SEGMENT * seg,int icont,int iseg)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
vrmlToSG(VRML_LAYER & vpcb,int idxColor,SGNODE * aParent,double top,double bottom)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
addOutline(IDF3_COMP_OUTLINE * outline,int idxColor,SGNODE * aParent)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
loadIDFOutline(const wxString & aFileName)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
loadIDFBoard(const wxString & aFileName)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
makeBoard(IDF3_BOARD & brd,SGNODE * aParent)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
makeComponents(IDF3_BOARD & brd,SGNODE * aParent)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
makeOtherOutlines(IDF3_BOARD & brd,SGNODE * aParent)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 }
919