KiCad PCB EDA Suite
Loading...
Searching...
No Matches
loadmodel.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) 2016 Cirilo Bernardo <[email protected]>
5 * Copyright The 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, see <https://www.gnu.org/licenses/>.
19 *
20 *
21 * Some code lifted from FreeCAD, copyright (c) 2018 Zheng, Lei (realthunder) under GPLv2
22 */
23
24#include <iostream>
25#include <fstream>
26#include <sstream>
27#include <string>
28#include <cstring>
29#include <map>
30#include <vector>
31#include <wx/filename.h>
32#include <wx/log.h>
33#include <wx/stdpaths.h>
34#include <wx/string.h>
35#include <wx/utils.h>
36#include <wx/wfstream.h>
37#include <wx/zipstrm.h>
38
39#include <advanced_config.h>
40#include <decompress.hpp>
41
42#include <TDocStd_Document.hxx>
43#include <TopoDS.hxx>
44#include <TopoDS_Shape.hxx>
45#include <Quantity_Color.hxx>
46#include <XCAFApp_Application.hxx>
47
48#include <AIS_Shape.hxx>
49
50#include <IGESControl_Reader.hxx>
51#include <IGESCAFControl_Reader.hxx>
52#include <Interface_Static.hxx>
53
54#include <STEPControl_Reader.hxx>
55#include <STEPCAFControl_Reader.hxx>
56
57#include <XCAFDoc_DocumentTool.hxx>
58#include <XCAFDoc_ColorTool.hxx>
59#include <XCAFDoc_ShapeTool.hxx>
60
61#include <BRep_Tool.hxx>
62#include <BRepMesh_IncrementalMesh.hxx>
63
64#include <TopoDS.hxx>
65#include <TopoDS_Shape.hxx>
66#include <TopoDS_Face.hxx>
67#include <TopoDS_Compound.hxx>
68#include <TopExp_Explorer.hxx>
69
70#include <Quantity_Color.hxx>
71#include <Poly_Triangulation.hxx>
72#include <Poly_PolygonOnTriangulation.hxx>
73#include <Precision.hxx>
74
75#include <NCollection_Sequence.hxx>
76#include <TDF_Label.hxx>
77#include <TDF_ChildIterator.hxx>
78#include <TDF_Tool.hxx>
79#include <TDataStd_Name.hxx>
80#include <Standard_Version.hxx>
81
83
84
85// log mask for wxLogTrace
86#define MASK_OCE wxT( "PLUGIN_OCE" )
87#define MASK_OCE_EXTRA wxT( "PLUGIN_OCE_EXTRA" )
88
89typedef std::map<std::size_t, SGNODE*> COLORMAP;
90typedef std::map<std::string, SGNODE*> FACEMAP;
91typedef std::map<std::string, std::vector<SGNODE*>> NODEMAP;
92typedef std::pair<std::string, std::vector<SGNODE*>> NODEITEM;
93
94struct DATA;
95
96bool processLabel( const TDF_Label& aLabel, DATA& aData, SGNODE* aParent,
97 std::vector< SGNODE* >* aItems );
98
99
100bool processFace( const TopoDS_Face& face, DATA& data, SGNODE* parent,
101 std::vector< SGNODE* >* items, Quantity_ColorRGBA* color );
102
103#if OCC_VERSION_HEX >= 0x070500
104#define OCC_COLOR_SPACE Quantity_TOC_sRGB
105#else
106#define OCC_COLOR_SPACE Quantity_TOC_RGB
107#endif
108
109struct DATA
110{
111 Handle( TDocStd_Document ) m_doc;
112 Handle( XCAFDoc_ColorTool ) m_color;
113 Handle( XCAFDoc_ShapeTool ) m_assy;
116 Quantity_Color refColor;
117 NODEMAP shapes; // SGNODE lists representing a TopoDS_SOLID / COMPOUND
118 COLORMAP colors; // SGAPPEARANCE nodes
119 FACEMAP faces; // SGSHAPE items representing a TopoDS_FACE
120 bool renderBoth; // set TRUE if we're processing IGES
121 bool hasSolid; // set TRUE if there is no parent SOLID
122
124 {
125 scene = nullptr;
126 defaultColor = nullptr;
127 refColor.SetValues( Quantity_NOC_BLACK );
128 renderBoth = false;
129 hasSolid = false;
130 }
131
133 {
134 // destroy any colors with no parent
135 if( !colors.empty() )
136 {
137 COLORMAP::iterator sC = colors.begin();
138 COLORMAP::iterator eC = colors.end();
139
140 while( sC != eC )
141 {
142 if( nullptr == S3D::GetSGNodeParent( sC->second ) )
143 S3D::DestroyNode( sC->second );
144
145 ++sC;
146 }
147
148 colors.clear();
149 }
150
151 if( defaultColor && nullptr == S3D::GetSGNodeParent( defaultColor ) )
153
154 // destroy any faces with no parent
155 if( !faces.empty() )
156 {
157 FACEMAP::iterator sF = faces.begin();
158 FACEMAP::iterator eF = faces.end();
159
160 while( sF != eF )
161 {
162 if( nullptr == S3D::GetSGNodeParent( sF->second ) )
163 S3D::DestroyNode( sF->second );
164
165 ++sF;
166 }
167
168 faces.clear();
169 }
170
171 // destroy any shapes with no parent
172 if( !shapes.empty() )
173 {
174 NODEMAP::iterator sS = shapes.begin();
175 NODEMAP::iterator eS = shapes.end();
176
177 while( sS != eS )
178 {
179 std::vector< SGNODE* >::iterator sV = sS->second.begin();
180 std::vector< SGNODE* >::iterator eV = sS->second.end();
181
182 while( sV != eV )
183 {
184 if( nullptr == S3D::GetSGNodeParent( *sV ) )
185 S3D::DestroyNode( *sV );
186
187 ++sV;
188 }
189
190 sS->second.clear();
191 ++sS;
192 }
193
194 shapes.clear();
195 }
196
197 if( scene )
199 }
200
201 // find collection of tagged nodes
202 bool GetShape( const std::string& id, std::vector< SGNODE* >*& listPtr )
203 {
204 listPtr = nullptr;
205 NODEMAP::iterator item;
206 item = shapes.find( id );
207
208 if( item == shapes.end() )
209 return false;
210
211 listPtr = &item->second;
212 return true;
213 }
214
215 // find collection of tagged nodes
216 SGNODE* GetFace( const std::string& id )
217 {
218 FACEMAP::iterator item;
219 item = faces.find( id );
220
221 if( item == faces.end() )
222 return nullptr;
223
224 return item->second;
225 }
226
227 // return color if found; if not found, create SGAPPEARANCE
228 SGNODE* GetColor( Quantity_ColorRGBA* colorObj )
229 {
230 if( nullptr == colorObj )
231 {
232 if( defaultColor )
233 return defaultColor;
234
235 IFSG_APPEARANCE app( true );
236 app.SetShininess( 0.05f );
237 app.SetSpecular( 0.04f, 0.04f, 0.04f );
238 app.SetAmbient( 0.1f, 0.1f, 0.1f );
239 app.SetDiffuse( 0.6f, 0.6f, 0.6f );
240
241 defaultColor = app.GetRawPtr();
242 return defaultColor;
243 }
244
245 Quantity_Color colorRgb = colorObj->GetRGB();
246
247 double r, g, b;
248 colorObj->GetRGB().Values( r, g, b, OCC_COLOR_SPACE );
249
250 std::size_t hash = std::hash<double>{}( colorRgb.Distance( refColor ) )
251 ^ ( std::hash<float>{}( colorObj->Alpha() ) << 1 );
252
253 std::map<std::size_t, SGNODE*>::iterator item;
254 item = colors.find( hash );
255
256 if( item != colors.end() )
257 return item->second;
258
259 IFSG_APPEARANCE app( true );
260 app.SetShininess( 0.1f );
261 app.SetSpecular( 0.12f, 0.12f, 0.12f );
262 app.SetAmbient( 0.1f, 0.1f, 0.1f );
263 app.SetDiffuse( r, g, b );
264 app.SetTransparency( 1.0f - colorObj->Alpha() );
265 colors.emplace( hash, app.GetRawPtr() );
266
267 return app.GetRawPtr();
268 }
269};
270
271
279
280
281FormatType fileType( const char* aFileName )
282{
283 wxFileName fname( wxString::FromUTF8Unchecked( aFileName ) );
284 wxFFileInputStream ifile( fname.GetFullPath() );
285
286 if( !ifile.IsOk() )
287 return FMT_NONE;
288
289 if( fname.GetExt().MakeUpper().EndsWith( wxT( "STPZ" ) )
290 || fname.GetExt().MakeUpper().EndsWith( wxT( "GZ" ) ) )
291 {
292 return FMT_STPZ;
293 }
294
295 char iline[82];
296
297 // The expected header should be the first line.
298 // However some files can have a comment at the beginning of the file
299 // So read up to max_line_count lines to try to find the actual header
300 const int max_line_count = 3;
301
302 for( int ii = 0; ii < max_line_count; ii++ )
303 {
304 memset( iline, 0, 82 );
305 ifile.Read( iline, 82 );
306 iline[81] = 0; // ensure NULL termination when string is too long
307
308 // check for STEP in Part 21 format
309 // (this can give false positives since Part 21 is not exclusively STEP)
310 if( !strncmp( iline, "ISO-10303-21;", 13 ) )
311 {
312 return FMT_STEP;
313 break;
314 }
315
316 std::string fstr = iline;
317
318 // check for STEP in XML format
319 // (this can give both false positive and false negatives)
320 if( fstr.find( "urn:oid:1.0.10303." ) != std::string::npos )
321 return FMT_STEP;
322
323 // Note: this is a very simple test which can yield false positives; the only
324 // sure method for determining if a file *not* an IGES model is to attempt
325 // to load it.
326 if( iline[72] == 'S' && ( iline[80] == 0 || iline[80] == 13 || iline[80] == 10 ) )
327 return FMT_IGES;
328
329 // Only a comment (starting by "/*") is allowed as header
330 if( strncmp( iline, "/*", 2 ) != 0 ) // not a comment
331 break;
332 }
333
334 return FMT_NONE;
335}
336
337
344void getTag( const TDF_Label& aLabel, std::string& aTag )
345{
346 std::ostringstream ostr;
347
348 if( aLabel.IsNull() )
349 {
350 wxLogTrace( MASK_OCE, wxT( "Null label passed to getTag" ) );
351 return;
352 }
353
354 NCollection_List<int> tagList;
355 TDF_Tool::TagList( aLabel, tagList );
356
357 for( const int& tag : tagList )
358 {
359 ostr << tag;
360 ostr << ":";
361 }
362
363 aTag = ostr.str();
364 aTag.pop_back(); // kill the last colon
365}
366
367
368static wxString getLabelName( const TDF_Label& aLabel )
369{
370 wxString txt;
371 Handle( TDataStd_Name ) name;
372 if( !aLabel.IsNull() && aLabel.FindAttribute( TDataStd_Name::GetID(), name ) )
373 {
374 TCollection_ExtendedString extstr = name->Get();
375 char* str = new char[extstr.LengthOfCString() + 1];
376 extstr.ToUTF8CString( str );
377
378 txt = wxString::FromUTF8( str );
379 delete[] str;
380 txt = txt.Trim();
381 }
382 return txt;
383}
384
385
391std::string getShapeName( TopAbs_ShapeEnum aShape )
392{
393 switch( aShape )
394 {
395 case TopAbs_COMPOUND: return "COMPOUND";
396 case TopAbs_COMPSOLID: return "COMPSOLID";
397 case TopAbs_SOLID: return "SOLID";
398 case TopAbs_SHELL: return "SHELL";
399 case TopAbs_FACE: return "FACE";
400 case TopAbs_WIRE: return "WIRE";
401 case TopAbs_EDGE: return "EDGE";
402 case TopAbs_VERTEX: return "VERTEX";
403 case TopAbs_SHAPE: return "SHAPE";
404 }
405
406 return "UNKNOWN";
407}
408
409static int colorFloatToDecimal( float aVal )
410{
411 return aVal * 255;
412}
413
414
415static inline std::ostream& operator<<( std::ostream& aOStream, const Quantity_ColorRGBA& aColor )
416{
417 Quantity_Color rgb = aColor.GetRGB();
418
419 return aOStream << "rgba(" << colorFloatToDecimal( rgb.Red() ) << ","
420 << colorFloatToDecimal( rgb.Green() ) << ","
421 << colorFloatToDecimal( rgb.Blue() ) << ","
422 << colorFloatToDecimal( aColor.Alpha() )
423 << ")";
424}
425
426
435static void printLabel( TDF_Label aLabel, Handle( XCAFDoc_ShapeTool ) aShapeTool,
436 Handle( XCAFDoc_ColorTool ) aColorTool, const char* aPreMsg = nullptr )
437{
438 if( aLabel.IsNull() )
439 return;
440
441 if( !aPreMsg )
442 aPreMsg = "Label: ";
443
444 TCollection_AsciiString entry;
445 TDF_Tool::Entry( aLabel, entry );
446 std::ostringstream ss;
447 ss << aPreMsg << entry << ", " << getLabelName( aLabel )
448 << ( aShapeTool->IsShape( aLabel ) ? ", shape" : "" )
449 << ( aShapeTool->IsTopLevel( aLabel ) ? ", topLevel" : "" )
450 << ( aShapeTool->IsFree( aLabel ) ? ", free" : "" )
451 << ( aShapeTool->IsAssembly( aLabel ) ? ", assembly" : "" )
452 << ( aShapeTool->IsSimpleShape( aLabel ) ? ", simple" : "" )
453 << ( aShapeTool->IsCompound( aLabel ) ? ", compound" : "" )
454 << ( aShapeTool->IsReference( aLabel ) ? ", reference" : "" )
455 << ( aShapeTool->IsComponent( aLabel ) ? ", component" : "" )
456 << ( aShapeTool->IsSubShape( aLabel ) ? ", subshape" : "" );
457
458 if( aShapeTool->IsSubShape( aLabel ) )
459 {
460 auto shape = aShapeTool->GetShape( aLabel );
461 if( !shape.IsNull() )
462 ss << ", " << getShapeName( shape.ShapeType() );
463 }
464
465 if( aShapeTool->IsShape( aLabel ) )
466 {
467 Quantity_ColorRGBA c;
468 if( aColorTool->GetColor( aLabel, XCAFDoc_ColorGen, c ) )
469 ss << ", gc: " << c;
470 if( aColorTool->GetColor( aLabel, XCAFDoc_ColorSurf, c ) )
471 ss << ", sc: " << c;
472 if( aColorTool->GetColor( aLabel, XCAFDoc_ColorCurv, c ) )
473 ss << ", cc: " << c;
474 }
475
476 wxLogTrace( MASK_OCE, ss.str().c_str() );
477}
478
479
488static void dumpLabels( TDF_Label aLabel, Handle( XCAFDoc_ShapeTool ) aShapeTool,
489 Handle( XCAFDoc_ColorTool ) aColorTool, int aDepth = 0 )
490{
491 std::string indent( aDepth * 2, ' ' );
492 printLabel( aLabel, aShapeTool, aColorTool, indent.c_str() );
493 TDF_ChildIterator it;
494 for( it.Initialize( aLabel ); it.More(); it.Next() )
495 dumpLabels( it.Value(), aShapeTool, aColorTool, aDepth + 1 );
496}
497
498
499bool getColor( DATA& data, TDF_Label label, Quantity_ColorRGBA& color )
500{
501 while( true )
502 {
503 if( data.m_color->GetColor( label, XCAFDoc_ColorSurf, color ) )
504 return true;
505 else if( data.m_color->GetColor( label, XCAFDoc_ColorCurv, color ) )
506 return true;
507 else if( data.m_color->GetColor( label, XCAFDoc_ColorGen, color ) )
508 return true;
509
510 label = label.Father();
511
512 if( label.IsNull() )
513 break;
514 };
515
516 return false;
517}
518
519
520void addItems( SGNODE* parent, std::vector< SGNODE* >* lp )
521{
522 if( nullptr == lp )
523 return;
524
525 std::vector< SGNODE* >::iterator sL = lp->begin();
526 std::vector< SGNODE* >::iterator eL = lp->end();
527 SGNODE* item;
528
529 while( sL != eL )
530 {
531 item = *sL;
532
533 if( nullptr == S3D::GetSGNodeParent( item ) )
534 S3D::AddSGNodeChild( parent, item );
535 else
536 S3D::AddSGNodeRef( parent, item );
537
538 ++sL;
539 }
540}
541
542
543bool readIGES( Handle( TDocStd_Document ) & m_doc, const char* fname )
544{
545 IGESCAFControl_Reader reader;
546 IFSelect_ReturnStatus stat = reader.ReadFile( fname );
547 reader.PrintCheckLoad( false, IFSelect_ItemsByEntity );
548
549 if( stat != IFSelect_RetDone )
550 return false;
551
552 // Enable file-defined shape precision
553 if( !Interface_Static::SetIVal( "read.precision.mode", 0 ) )
554 return false;
555
556 // set other translation options
557 reader.SetColorMode(true); // use model colors
558 reader.SetNameMode(false); // don't use IGES label names
559 reader.SetLayerMode(false); // ignore LAYER data
560
561 if( !reader.Transfer( m_doc ) )
562 {
563 if( m_doc->CanClose() == CDM_CCS_OK )
564 m_doc->Close();
565
566 return false;
567 }
568
569 // are there any shapes to translate?
570 if( reader.NbShapes() < 1 )
571 {
572 if( m_doc->CanClose() == CDM_CCS_OK )
573 m_doc->Close();
574
575 return false;
576 }
577
578 return true;
579}
580
581
582bool readSTEP( Handle(TDocStd_Document)& m_doc, const char* fname )
583{
584 wxLogTrace( MASK_OCE, wxT( "Reading step file %s" ), fname );
585
586 STEPCAFControl_Reader reader;
587 IFSelect_ReturnStatus stat = reader.ReadFile( fname );
588
589 if( stat != IFSelect_RetDone )
590 return false;
591
592 // Enable user-defined shape precision
593 if( !Interface_Static::SetIVal( "read.precision.mode", 1 ) )
594 return false;
595
596 // Set the shape conversion precision (default 0.0001 has too many triangles)
597 if( !Interface_Static::SetRVal( "read.precision.val", ADVANCED_CFG::GetCfg().m_OcePluginLinearDeflection ) )
598 return false;
599
600 // set other translation options
601 reader.SetColorMode( true ); // use model colors
602 reader.SetNameMode( false ); // don't use label names
603 reader.SetLayerMode( false ); // ignore LAYER data
604
605 if( !reader.Transfer( m_doc ) )
606 {
607 if( m_doc->CanClose() == CDM_CCS_OK )
608 m_doc->Close();
609
610 return false;
611 }
612
613 // are there any shapes to translate?
614 if( reader.NbRootsForTransfer() < 1 )
615 {
616 if( m_doc->CanClose() == CDM_CCS_OK )
617 m_doc->Close();
618
619 return false;
620 }
621
622 return true;
623}
624
625
626bool readSTEPZ( Handle(TDocStd_Document)& m_doc, const char* aFileName )
627{
628 wxFileName fname( wxString::FromUTF8Unchecked( aFileName ) );
629 wxFFileInputStream ifile( fname.GetFullPath() );
630
631 wxFileName outFile( fname );
632
633 outFile.SetPath( wxStandardPaths::Get().GetTempDir() );
634 outFile.SetExt( wxT( "STEP" ) );
635
636 wxFileOffset size = ifile.GetLength();
637 wxBusyCursor busycursor;
638
639 if( size == wxInvalidOffset )
640 return false;
641
642 {
643 bool success = false;
644 wxFFileOutputStream ofile( outFile.GetFullPath() );
645
646 if( !ofile.IsOk() )
647 return false;
648
649 char *buffer = new char[size];
650
651 ifile.Read( buffer, size);
652 std::string expanded;
653
654 try
655 {
656 expanded = gzip::decompress( buffer, size );
657 success = true;
658 }
659 catch(...)
660 {}
661
662 if( expanded.empty() )
663 {
664 ifile.Reset();
665 ifile.SeekI( 0 );
666 wxZipInputStream izipfile( ifile );
667 std::unique_ptr<wxZipEntry> zip_file( izipfile.GetNextEntry() );
668
669 if( zip_file && !zip_file->IsDir() && izipfile.CanRead() )
670 {
671 izipfile.Read( ofile );
672 success = true;
673 }
674 }
675 else
676 {
677 ofile.Write( expanded.data(), expanded.size() );
678 }
679
680 delete[] buffer;
681 ofile.Close();
682
683 if( !success )
684 return false;
685 }
686
687 bool retval = readSTEP( m_doc, outFile.GetFullPath().mb_str() );
688
689 // Cleanup our temporary file
690 wxRemoveFile( outFile.GetFullPath() );
691
692 return retval;
693}
694
695
696SCENEGRAPH* LoadModel( char const* filename )
697{
698 DATA data;
699
700 Handle(XCAFApp_Application) m_app = XCAFApp_Application::GetApplication();
701 m_app->NewDocument( "MDTV-XCAF", data.m_doc );
702 FormatType modelFmt = fileType( filename );
703
704 switch( modelFmt )
705 {
706 case FMT_IGES:
707 data.renderBoth = true;
708
709 if( !readIGES( data.m_doc, filename ) )
710 return nullptr;
711
712 break;
713
714 case FMT_STEP:
715 if( !readSTEP( data.m_doc, filename ) )
716 return nullptr;
717
718 break;
719
720 case FMT_STPZ:
721 if( !readSTEPZ( data.m_doc, filename ) )
722 return nullptr;
723
724 break;
725
726
727 default:
728 if( m_app->CanClose( data.m_doc ) == CDM_CCS_OK )
729 m_app->Close( data.m_doc );
730
731 return nullptr;
732 break;
733 }
734
735 data.m_assy = XCAFDoc_DocumentTool::ShapeTool( data.m_doc->Main() );
736 data.m_color = XCAFDoc_DocumentTool::ColorTool( data.m_doc->Main() );
737
738 // Check if the log mask is enabled otherwise the dump routine may be expensive before the wxLog call
739 if( wxLog::IsAllowedTraceMask( MASK_OCE ) )
740 {
741 dumpLabels( data.m_doc->Main(), data.m_assy, data.m_color );
742 }
743
744 // retrieve all free shapes
745 NCollection_Sequence<TDF_Label> frshapes;
746 data.m_assy->GetFreeShapes( frshapes );
747
748 bool ret = false;
749
750 // create the top level SG node
751 IFSG_TRANSFORM topNode( true );
752 data.scene = topNode.GetRawPtr();
753
754 for( int i = 1; i <= frshapes.Length(); i++ )
755 {
756 const TDF_Label& label = frshapes.Value( i );
757
758 if( data.m_color->IsVisible( label ) )
759 {
760 if( processLabel( label, data, data.scene, nullptr ) )
761 ret = true;
762 }
763 }
764
765 if( !ret )
766 {
767 if( m_app->CanClose( data.m_doc ) == CDM_CCS_OK )
768 m_app->Close( data.m_doc );
769
770 return nullptr;
771 }
772
773 SCENEGRAPH* scene = (SCENEGRAPH*)data.scene;
774
775 // DEBUG: WRITE OUT VRML2 FILE TO CONFIRM STRUCTURE
776#if ( defined( DEBUG_OCE ) && DEBUG_OCE > 3 )
777 if( data.scene )
778 {
779 wxFileName fn( wxString::FromUTF8Unchecked( filename ) );
780 wxString output;
781
782 if( FMT_STEP == modelFmt )
783 output = wxT( "_step-" );
784 else
785 output = wxT( "_iges-" );
786
787 output.append( fn.GetName() );
788 output.append( wxT( ".wrl" ) );
789 S3D::WriteVRML( output.ToUTF8(), true, data.scene, true, true );
790 }
791#endif
792
793 // set to NULL to prevent automatic destruction of the scene data
794 data.scene = nullptr;
795
796 if( m_app->CanClose( data.m_doc ) == CDM_CCS_OK )
797 m_app->Close( data.m_doc );
798
799 return scene;
800}
801
802
803bool processShell( const TopoDS_Shape& shape, DATA& data, SGNODE* parent,
804 std::vector<SGNODE*>* items, Quantity_ColorRGBA* color )
805{
806 TopoDS_Iterator it;
807 bool ret = false;
808
809 wxLogTrace( MASK_OCE, wxT( "Processing shell" ) );
810 for( it.Initialize( shape, false, false ); it.More(); it.Next() )
811 {
812 const TopoDS_Face& face = TopoDS::Face( it.Value() );
813
814 if( processFace( face, data, parent, items, color ) )
815 ret = true;
816 }
817
818 return ret;
819}
820
821
822bool processSolidOrShell( const TopoDS_Shape& shape, DATA& data, SGNODE* parent,
823 std::vector< SGNODE* >* items )
824{
825 TDF_Label label;
826 std::string partID;
827 Quantity_ColorRGBA col;
828 Quantity_ColorRGBA* lcolor = nullptr;
829
830 data.hasSolid = shape.ShapeType() == TopAbs_SOLID;
831
832 wxLogTrace( MASK_OCE, wxT( "Processing solid" ) );
833
834 // Search the whole model first to make sure something exists (may or may not have color)
835 if( !data.m_assy->Search( shape, label ) )
836 {
837 static int i = 0;
838 std::ostringstream ostr;
839 ostr << "KMISC_" << i++;
840 partID = ostr.str();
841 }
842 else
843 {
844 bool found_color = false;
845
846 if( getColor( data, label, col ) )
847 {
848 found_color = true;
849 lcolor = &col;
850 }
851
852 // If the top-level label doesn't have the color information, search components
853 if( !found_color )
854 {
855 if( data.m_assy->Search( shape, label, false, true, true ) &&
856 getColor( data, label, col ) )
857 {
858 found_color = true;
859 lcolor = &col;
860 }
861 }
862
863 // If the components do not have color information, search all components without location
864 if( !found_color )
865 {
866 if( data.m_assy->Search( shape, label, false, false,
867 true ) &&
868 getColor( data, label, col ) )
869 {
870 found_color = true;
871 lcolor = &col;
872 }
873 }
874
875 // Our last chance to find the color looks for color as a subshape of top-level simple
876 // shapes.
877 if( !found_color )
878 {
879 if( data.m_assy->Search( shape, label, false, false,
880 false ) &&
881 getColor( data, label, col ) )
882 {
883 found_color = true;
884 lcolor = &col;
885 }
886 }
887
888 getTag( label, partID );
889 }
890
891 TopoDS_Iterator it;
892 IFSG_TRANSFORM childNode( parent );
893 SGNODE* pptr = childNode.GetRawPtr();
894 bool ret = false;
895
896
897 std::vector< SGNODE* >* component = nullptr;
898
899 if( !partID.empty() )
900 data.GetShape( partID, component );
901
902 if( component )
903 {
904 addItems( pptr, component );
905
906 if( nullptr != items )
907 items->push_back( pptr );
908 }
909
910 // instantiate resulting object
911 std::vector<SGNODE*> itemList;
912
913 TopAbs_ShapeEnum stype = shape.ShapeType();
914 if( stype == TopAbs_SHELL )
915 {
916 if( processShell( shape, data, pptr, &itemList, lcolor ) )
917 ret = true;
918 }
919 else
920 {
921
922 for( it.Initialize( shape, false, false ); it.More(); it.Next() )
923 {
924 const TopoDS_Shape& subShape = it.Value();
925
926 if( subShape.ShapeType() == TopAbs_SHELL )
927 {
928 if( processShell( subShape, data, pptr, &itemList, lcolor ) )
929 ret = true;
930 }
931 else
932 {
933 wxLogTrace( MASK_OCE, wxT( "Unsupported subshape in solid" ) );
934 }
935 }
936 }
937
938 if( !ret )
939 childNode.Destroy();
940 else if( nullptr != items )
941 items->push_back( pptr );
942
943 return ret;
944}
945
946
947bool processLabel( const TDF_Label& aLabel, DATA& aData, SGNODE* aParent,
948 std::vector<SGNODE*>* aItems )
949{
950 std::string labelTag;
951
952 if( wxLog::IsAllowedTraceMask( MASK_OCE ) )
953 {
954 // can be expensive, guard it if we aren't logging
955 getTag( aLabel, labelTag );
956 }
957
958 wxLogTrace( MASK_OCE, wxT( "Processing label %s" ), labelTag );
959
960 TopoDS_Shape originalShape;
961 TDF_Label shapeLabel = aLabel;
962
963 if( !aData.m_assy->GetShape( shapeLabel, originalShape ) )
964 {
965 return false;
966 }
967
968 TopoDS_Shape shape = originalShape;
969
970 if( aData.m_assy->IsReference( aLabel ) )
971 {
972 wxLogTrace( MASK_OCE, wxT( "Label %s is ref, trying to pull up referred label" ),
973 labelTag );
974
975 if( !aData.m_assy->GetReferredShape( aLabel, shapeLabel ) )
976 {
977 return false;
978 }
979
980 labelTag = static_cast<int>( shapeLabel.Tag() );
981 // wxLogTrace( MASK_OCE, wxT( "Label %s referred" ), labelTag );
982
983 if( !aData.m_assy->GetShape( shapeLabel, shape ) )
984 {
985 return false;
986 }
987 }
988
989 // Now let's see if the original label has a location
990 // Labels can be used to place copies of other labels at a specific location
991 IFSG_TRANSFORM childNode( aParent );
992 SGNODE* pptr = childNode.GetRawPtr();
993 const TopLoc_Location& loc = originalShape.Location();
994
995 if( !loc.IsIdentity() )
996 {
997 wxLogTrace( MASK_OCE, wxT( "Label %d has location" ), static_cast<int>( aLabel.Tag() ) );
998 gp_Trsf T = loc.Transformation();
999 gp_XYZ coord = T.TranslationPart();
1000 childNode.SetTranslation( SGPOINT( coord.X(), coord.Y(), coord.Z() ) );
1001 wxLogTrace( MASK_OCE, wxT( "Translation %f, %f, %f" ), coord.X(), coord.Y(), coord.Z() );
1002 gp_XYZ axis;
1003 double angle;
1004
1005 if( T.GetRotation( axis, angle ) )
1006 {
1007 childNode.SetRotation( SGVECTOR( axis.X(), axis.Y(), axis.Z() ), angle );
1008 wxLogTrace( MASK_OCE, wxT( "Rotation %f, %f, %f, angle %f" ),
1009 axis.X(), axis.Y(), axis.Z(), angle );
1010 }
1011 }
1012
1013 TopAbs_ShapeEnum stype = shape.ShapeType();
1014 bool ret = false;
1015 aData.hasSolid = false;
1016
1017 switch( stype )
1018 {
1019 case TopAbs_COMPOUND:
1020 {
1021 // assemblies will report a shape type of compound which isn't what we are after
1022 // we will still process the children of assemblies but they should just be label references to the actual shapes
1023 if( !aData.m_assy->IsAssembly( shapeLabel ) )
1024 {
1025 TopExp_Explorer xp;
1026
1027 for( xp.Init( shape, TopAbs_SOLID ); xp.More(); xp.Next() )
1028 {
1029 processSolidOrShell( xp.Current(), aData, pptr, aItems );
1030 ret = true;
1031 }
1032
1033 // Get all shells, avoid those that may be attached to solids
1034 for( xp.Init( shape, TopAbs_SHELL, TopAbs_SOLID ); xp.More(); xp.Next() )
1035 {
1036 processSolidOrShell( xp.Current(), aData, pptr, aItems );
1037 ret = true;
1038 }
1039
1040 // Get all faces, avoid those that may be attached to shells
1041 for( xp.Init( shape, TopAbs_FACE, TopAbs_SHELL ); xp.More(); xp.Next() )
1042 {
1043 const TopoDS_Face& face = TopoDS::Face( xp.Current() );
1044 processFace( face, aData, pptr, aItems, nullptr );
1045 ret = true;
1046 }
1047 }
1048 }
1049 break;
1050
1051 case TopAbs_SOLID:
1052 if( processSolidOrShell( shape, aData, pptr, aItems ) )
1053 ret = true;
1054
1055 break;
1056
1057 case TopAbs_SHELL:
1058 if( processSolidOrShell( shape, aData, pptr, aItems ) )
1059 ret = true;
1060
1061 break;
1062
1063 case TopAbs_FACE:
1064 if( processFace( TopoDS::Face( shape ), aData, pptr, aItems, nullptr ) )
1065 ret = true;
1066
1067 break;
1068
1069 default:
1070 break;
1071 }
1072
1073 if( nullptr != aItems )
1074 aItems->push_back( pptr );
1075
1076 if( !aData.m_assy->IsSimpleShape( shapeLabel ) && shapeLabel.HasChild() )
1077 {
1078 wxLogTrace( MASK_OCE, wxT( "Label %s has children" ), labelTag );
1079 TDF_ChildIterator it;
1080
1081 for( it.Initialize( shapeLabel ); it.More(); it.Next() )
1082 {
1083 if( processLabel( it.Value(), aData, pptr, aItems ) )
1084 ret = true;
1085 }
1086 }
1087
1088 return ret;
1089}
1090
1091
1092bool processFace( const TopoDS_Face& face, DATA& data, SGNODE* parent, std::vector<SGNODE*>* items,
1093 Quantity_ColorRGBA* color )
1094{
1095 if( true == face.IsNull() )
1096 return false;
1097
1098 bool reverse = ( face.Orientation() == TopAbs_REVERSED );
1099 SGNODE* ashape = nullptr;
1100 std::string partID;
1101 TDF_Label label;
1102
1103 bool useBothSides = false;
1104
1105 // for IGES renderBoth = TRUE; for STEP if a shell or face is not a descendant
1106 // of a SOLID then hasSolid = false and we must render both sides
1107 if( data.renderBoth || !data.hasSolid )
1108 useBothSides = true;
1109
1110 if( data.m_assy->FindShape( face, label, false ) )
1111 getTag( label, partID );
1112
1113 if( !partID.empty() )
1114 ashape = data.GetFace( partID );
1115
1116 if( ashape )
1117 {
1118 if( nullptr == S3D::GetSGNodeParent( ashape ) )
1119 S3D::AddSGNodeChild( parent, ashape );
1120 else
1121 S3D::AddSGNodeRef( parent, ashape );
1122
1123 if( nullptr != items )
1124 items->push_back( ashape );
1125
1126 if( useBothSides )
1127 {
1128 std::string id2 = partID;
1129 id2.append( "b" );
1130 SGNODE* shapeB = data.GetFace( id2 );
1131
1132 if( nullptr == S3D::GetSGNodeParent( shapeB ) )
1133 S3D::AddSGNodeChild( parent, shapeB );
1134 else
1135 S3D::AddSGNodeRef( parent, shapeB );
1136
1137 if( nullptr != items )
1138 items->push_back( shapeB );
1139 }
1140
1141 return true;
1142 }
1143
1144 TopLoc_Location loc;
1145 bool isTessellate (false);
1146 Handle( Poly_Triangulation ) triangulation = BRep_Tool::Triangulation( face, loc );
1147 const double linDeflection = ADVANCED_CFG::GetCfg().m_OcePluginLinearDeflection;
1148
1149 if( triangulation.IsNull() || triangulation->Deflection() > linDeflection + Precision::Confusion() )
1150 isTessellate = true;
1151
1152 if( isTessellate )
1153 {
1154 BRepMesh_IncrementalMesh IM(face, linDeflection, false,
1155 glm::radians(ADVANCED_CFG::GetCfg().m_OcePluginAngularDeflection) );
1156 triangulation = BRep_Tool::Triangulation( face, loc );
1157 }
1158
1159 if( triangulation.IsNull() == true )
1160 return false;
1161
1162 Quantity_ColorRGBA lcolor;
1163
1164 // check for a face color; this has precedence over SOLID colors
1165 if( data.m_color->GetColor( face, XCAFDoc_ColorSurf, lcolor )
1166 || data.m_color->GetColor( face, XCAFDoc_ColorCurv, lcolor )
1167 || data.m_color->GetColor( face, XCAFDoc_ColorGen, lcolor ) )
1168 {
1169 color = &lcolor;
1170 }
1171
1172 SGNODE* ocolor = data.GetColor( color );
1173
1174 // create a SHAPE and attach the color and data,
1175 // then attach the shape to the parent and return TRUE
1176 IFSG_SHAPE vshape( true );
1177 IFSG_FACESET vface( vshape );
1178 IFSG_COORDS vcoords( vface );
1179 IFSG_COORDINDEX coordIdx( vface );
1180
1181 if( nullptr == S3D::GetSGNodeParent( ocolor ) )
1182 S3D::AddSGNodeChild( vshape.GetRawPtr(), ocolor );
1183 else
1184 S3D::AddSGNodeRef( vshape.GetRawPtr(), ocolor );
1185
1186 std::vector< SGPOINT > vertices;
1187 std::vector< int > indices;
1188 std::vector< int > indices2;
1189
1190 // The triangulation location transform converts local triangulation coordinates to face
1191 // coordinates. This must be applied to get correct vertex positions. Skip identity transforms.
1192 bool applyTransform = !loc.IsIdentity();
1193 gp_Trsf tx = applyTransform ? loc.Transformation() : gp_Trsf();
1194
1195 for( int i = 1; i <= triangulation->NbNodes(); i++ )
1196 {
1197 gp_XYZ v = applyTransform ? triangulation->Node( i ).Transformed( tx ).Coord()
1198 : triangulation->Node( i ).Coord();
1199 vertices.emplace_back( v.X(), v.Y(), v.Z() );
1200 }
1201
1202 for( int i = 1; i <= triangulation->NbTriangles(); i++ )
1203 {
1204 int a, b, c;
1205 triangulation->Triangle(i).Get(a, b, c);
1206 a--;
1207
1208 if( reverse )
1209 {
1210 int tmp = b - 1;
1211 b = c - 1;
1212 c = tmp;
1213 }
1214 else
1215 {
1216 b--;
1217 c--;
1218 }
1219
1220 indices.push_back( a );
1221 indices.push_back( b );
1222 indices.push_back( c );
1223
1224 if( useBothSides )
1225 {
1226 indices2.push_back( b );
1227 indices2.push_back( a );
1228 indices2.push_back( c );
1229 }
1230 }
1231
1232 vcoords.SetCoordsList( vertices.size(), &vertices[0] );
1233 coordIdx.SetIndices( indices.size(), &indices[0] );
1234 vface.CalcNormals( nullptr );
1235 vshape.SetParent( parent );
1236
1237 if( !partID.empty() )
1238 data.faces.emplace( partID, vshape.GetRawPtr() );
1239
1240 // The outer surface of an IGES model is indeterminate so
1241 // we must render both sides of a surface.
1242 if( useBothSides )
1243 {
1244 std::string id2 = partID;
1245 id2.append( "b" );
1246 IFSG_SHAPE vshape2( true );
1247 IFSG_FACESET vface2( vshape2 );
1248 IFSG_COORDS vcoords2( vface2 );
1249 IFSG_COORDINDEX coordIdx2( vface2 );
1250 S3D::AddSGNodeRef( vshape2.GetRawPtr(), ocolor );
1251
1252 vcoords2.SetCoordsList( vertices.size(), &vertices[0] );
1253 coordIdx2.SetIndices( indices2.size(), &indices2[0] );
1254 vface2.CalcNormals( nullptr );
1255 vshape2.SetParent( parent );
1256
1257 if( !partID.empty() )
1258 data.faces.emplace( id2, vshape2.GetRawPtr() );
1259 }
1260
1261 return true;
1262}
const char * name
static const ADVANCED_CFG & GetCfg()
Get the singleton instance's config, which is shared by all consumers.
bool SetDiffuse(float aRVal, float aGVal, float aBVal)
bool SetSpecular(float aRVal, float aGVal, float aBVal)
bool SetShininess(float aShininess) noexcept
bool SetAmbient(float aRVal, float aGVal, float aBVal)
bool SetTransparency(float aTransparency) noexcept
The wrapper for SGCOORDINDEX.
The wrapper for SGCOORDS.
Definition ifsg_coords.h:36
bool SetCoordsList(size_t aListSize, const SGPOINT *aCoordsList)
The wrapper for the SGFACESET class.
bool CalcNormals(SGNODE **aPtr)
bool SetIndices(size_t nIndices, int *aIndexList)
Set the number of indices and creates a copy of the given index data.
bool SetParent(SGNODE *aParent)
Set the parent SGNODE of this object.
Definition ifsg_node.cpp:83
SGNODE * GetRawPtr(void) noexcept
Return the raw internal SGNODE pointer.
Definition ifsg_node.cpp:61
void Destroy(void)
Delete the object held by this wrapper.
Definition ifsg_node.cpp:51
The wrapper for the SGSHAPE class.
Definition ifsg_shape.h:36
The wrapper for the VRML compatible TRANSFORM block class SCENEGRAPH.
bool SetTranslation(const SGPOINT &aTranslation) noexcept
bool SetRotation(const SGVECTOR &aRotationAxis, double aAngle)
Define the basic data set required to represent a 3D model.
Definition scenegraph.h:41
The base class of all Scene Graph nodes.
Definition sg_node.h:71
double m_OcePluginLinearDeflection
OCE (STEP/IGES) 3D Plugin Tesselation Linear Deflection.
collects header files for all SG* wrappers and the API
Handle(KICAD3D_INFO) KICAD3D_INFO
bool processShell(const TopoDS_Shape &shape, DATA &data, SGNODE *parent, std::vector< SGNODE * > *items, Quantity_ColorRGBA *color)
std::map< std::string, std::vector< SGNODE * > > NODEMAP
Definition loadmodel.cpp:91
std::map< std::size_t, SGNODE * > COLORMAP
Definition loadmodel.cpp:89
bool processFace(const TopoDS_Face &face, DATA &data, SGNODE *parent, std::vector< SGNODE * > *items, Quantity_ColorRGBA *color)
std::pair< std::string, std::vector< SGNODE * > > NODEITEM
Definition loadmodel.cpp:92
bool getColor(DATA &data, TDF_Label label, Quantity_ColorRGBA &color)
FormatType
@ FMT_STPZ
static int colorFloatToDecimal(float aVal)
#define MASK_OCE
Definition loadmodel.cpp:86
void getTag(const TDF_Label &aLabel, std::string &aTag)
Gets the absolute tag string for a given label in the form of ##:##:##:##.
void addItems(SGNODE *parent, std::vector< SGNODE * > *lp)
std::string getShapeName(TopAbs_ShapeEnum aShape)
Gets a string for a given TopAbs_ShapeEnum element.
static void printLabel(TDF_Label aLabel, Handle(XCAFDoc_ShapeTool) aShapeTool, Handle(XCAFDoc_ColorTool) aColorTool, const char *aPreMsg=nullptr)
Gets a string for a given TopAbs_ShapeEnum element.
SCENEGRAPH * LoadModel(char const *filename)
static void dumpLabels(TDF_Label aLabel, Handle(XCAFDoc_ShapeTool) aShapeTool, Handle(XCAFDoc_ColorTool) aColorTool, int aDepth=0)
Dumps a label and the entire tree underneath it.
FormatType fileType(const char *aFileName)
bool readSTEP(Handle(TDocStd_Document)&m_doc, const char *fname)
static std::ostream & operator<<(std::ostream &aOStream, const Quantity_ColorRGBA &aColor)
#define OCC_COLOR_SPACE
bool processSolidOrShell(const TopoDS_Shape &shape, DATA &data, SGNODE *parent, std::vector< SGNODE * > *items)
static wxString getLabelName(const TDF_Label &aLabel)
bool processLabel(const TDF_Label &aLabel, DATA &aData, SGNODE *aParent, std::vector< SGNODE * > *aItems)
std::map< std::string, SGNODE * > FACEMAP
Definition loadmodel.cpp:90
bool readSTEPZ(Handle(TDocStd_Document)&m_doc, const char *aFileName)
bool readIGES(Handle(TDocStd_Document) &m_doc, const char *fname)
SGLIB_API bool WriteVRML(const char *filename, bool overwrite, SGNODE *aTopNode, bool reuse, bool renameNodes)
Write out the given node and its subnodes to a VRML2 file.
Definition ifsg_api.cpp:73
SGLIB_API SGNODE * GetSGNodeParent(SGNODE *aNode)
Definition ifsg_api.cpp:490
SGLIB_API void DestroyNode(SGNODE *aNode) noexcept
Delete the given SG* class node.
Definition ifsg_api.cpp:145
SGLIB_API bool AddSGNodeChild(SGNODE *aParent, SGNODE *aChild)
Definition ifsg_api.cpp:508
SGLIB_API bool AddSGNodeRef(SGNODE *aParent, SGNODE *aChild)
Definition ifsg_api.cpp:499
static SGNODE * getColor(IFSG_SHAPE &shape, int colorIdx)
@ FMT_STEP
@ FMT_IGES
@ FMT_NONE
SGNODE * defaultColor
COLORMAP colors
Quantity_Color refColor
Handle(XCAFDoc_ColorTool) m_color
bool renderBoth
SGNODE * GetFace(const std::string &id)
Handle(TDocStd_Document) m_doc
bool GetShape(const std::string &id, std::vector< SGNODE * > *&listPtr)
Handle(XCAFDoc_ShapeTool) m_assy
NODEMAP shapes
SGNODE * scene
bool hasSolid
FACEMAP faces
SGNODE * GetColor(Quantity_ColorRGBA *colorObj)
nlohmann::json output