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