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