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