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 <cirilo.bernardo@gmail.com>
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 #include <iostream>
26 #include <fstream>
27 #include <sstream>
28 #include <string>
29 #include <cstring>
30 #include <map>
31 #include <vector>
32 #include <wx/filename.h>
33 #include <wx/stdpaths.h>
34 #include <wx/string.h>
35 #include <wx/utils.h>
36 #include <wx/wfstream.h>
37 #include <wx/zipstrm.h>
38 
39 #include <decompress.hpp>
40 
41 #include <TDocStd_Document.hxx>
42 #include <TopoDS.hxx>
43 #include <TopoDS_Shape.hxx>
44 #include <Quantity_Color.hxx>
45 #include <XCAFApp_Application.hxx>
46 
47 #include <AIS_Shape.hxx>
48 
49 #include <IGESControl_Reader.hxx>
50 #include <IGESCAFControl_Reader.hxx>
51 #include <Interface_Static.hxx>
52 
53 #include <STEPControl_Reader.hxx>
54 #include <STEPCAFControl_Reader.hxx>
55 
56 #include <XCAFDoc_DocumentTool.hxx>
57 #include <XCAFDoc_ColorTool.hxx>
58 #include <XCAFDoc_ShapeTool.hxx>
59 
60 #include <BRep_Tool.hxx>
61 #include <BRepMesh_IncrementalMesh.hxx>
62 
63 #include <TopoDS.hxx>
64 #include <TopoDS_Shape.hxx>
65 #include <TopoDS_Face.hxx>
66 #include <TopoDS_Compound.hxx>
67 #include <TopExp_Explorer.hxx>
68 
69 #include <Quantity_Color.hxx>
70 #include <Poly_Triangulation.hxx>
71 #include <Poly_PolygonOnTriangulation.hxx>
72 #include <Precision.hxx>
73 
74 #include <TDF_LabelSequence.hxx>
75 #include <TDF_ChildIterator.hxx>
76 
77 #include "plugins/3dapi/ifsg_all.h"
78 
79 
80 // log mask for wxLogTrace
81 #define MASK_OCE "PLUGIN_OCE"
82 
83 // precision for mesh creation; 0.07 should be good enough for ECAD viewing
84 #define USER_PREC (0.14)
85 
86 // angular deflection for meshing
87 // 10 deg (36 faces per circle) = 0.17453293
88 // 20 deg (18 faces per circle) = 0.34906585
89 // 30 deg (12 faces per circle) = 0.52359878
90 #define USER_ANGLE (0.52359878)
91 
92 typedef std::map< Standard_Real, SGNODE* > COLORMAP;
93 typedef std::map< std::string, SGNODE* > FACEMAP;
94 typedef std::map< std::string, std::vector< SGNODE* > > NODEMAP;
95 typedef std::pair< std::string, std::vector< SGNODE* > > NODEITEM;
96 
97 struct DATA;
98 
99 bool processNode( const TopoDS_Shape& shape, DATA& data, SGNODE* parent,
100  std::vector< SGNODE* >* items );
101 
102 
103 bool processComp( const TopoDS_Shape& shape, DATA& data, SGNODE* parent,
104  std::vector< SGNODE* >* items );
105 
106 
107 bool processFace( const TopoDS_Face& face, DATA& data, SGNODE* parent,
108  std::vector< SGNODE* >* items, Quantity_Color* color );
109 
110 
111 struct DATA
112 {
113  Handle( TDocStd_Document ) m_doc;
114  Handle( XCAFDoc_ColorTool ) m_color;
115  Handle( XCAFDoc_ShapeTool ) m_assy;
118  Quantity_Color refColor;
119  NODEMAP shapes; // SGNODE lists representing a TopoDS_SOLID / COMPOUND
120  COLORMAP colors; // SGAPPEARANCE nodes
121  FACEMAP faces; // SGSHAPE items representing a TopoDS_FACE
122  bool renderBoth; // set TRUE if we're processing IGES
123  bool hasSolid; // set TRUE if there is no parent SOLID
124 
126  {
127  scene = nullptr;
128  defaultColor = nullptr;
129  refColor.SetValues( Quantity_NOC_BLACK );
130  renderBoth = false;
131  hasSolid = false;
132  }
133 
135  {
136  // destroy any colors with no parent
137  if( !colors.empty() )
138  {
139  COLORMAP::iterator sC = colors.begin();
140  COLORMAP::iterator eC = colors.end();
141 
142  while( sC != eC )
143  {
144  if( nullptr == S3D::GetSGNodeParent( sC->second ) )
145  S3D::DestroyNode( sC->second );
146 
147  ++sC;
148  }
149 
150  colors.clear();
151  }
152 
153  if( defaultColor && nullptr == S3D::GetSGNodeParent( defaultColor ) )
155 
156  // destroy any faces with no parent
157  if( !faces.empty() )
158  {
159  FACEMAP::iterator sF = faces.begin();
160  FACEMAP::iterator eF = faces.end();
161 
162  while( sF != eF )
163  {
164  if( nullptr == S3D::GetSGNodeParent( sF->second ) )
165  S3D::DestroyNode( sF->second );
166 
167  ++sF;
168  }
169 
170  faces.clear();
171  }
172 
173  // destroy any shapes with no parent
174  if( !shapes.empty() )
175  {
176  NODEMAP::iterator sS = shapes.begin();
177  NODEMAP::iterator eS = shapes.end();
178 
179  while( sS != eS )
180  {
181  std::vector< SGNODE* >::iterator sV = sS->second.begin();
182  std::vector< SGNODE* >::iterator eV = sS->second.end();
183 
184  while( sV != eV )
185  {
186  if( nullptr == S3D::GetSGNodeParent( *sV ) )
187  S3D::DestroyNode( *sV );
188 
189  ++sV;
190  }
191 
192  sS->second.clear();
193  ++sS;
194  }
195 
196  shapes.clear();
197  }
198 
199  if( scene )
201  }
202 
203  // find collection of tagged nodes
204  bool GetShape( const std::string& id, std::vector< SGNODE* >*& listPtr )
205  {
206  listPtr = nullptr;
207  NODEMAP::iterator item;
208  item = shapes.find( id );
209 
210  if( item == shapes.end() )
211  return false;
212 
213  listPtr = &item->second;
214  return true;
215  }
216 
217  // find collection of tagged nodes
218  SGNODE* GetFace( const std::string& id )
219  {
220  FACEMAP::iterator item;
221  item = faces.find( id );
222 
223  if( item == faces.end() )
224  return nullptr;
225 
226  return item->second;
227  }
228 
229  // return color if found; if not found, create SGAPPEARANCE
230  SGNODE* GetColor( Quantity_Color* colorObj )
231  {
232  if( nullptr == colorObj )
233  {
234  if( defaultColor )
235  return defaultColor;
236 
237  IFSG_APPEARANCE app( true );
238  app.SetShininess( 0.05f );
239  app.SetSpecular( 0.04f, 0.04f, 0.04f );
240  app.SetAmbient( 0.1f, 0.1f, 0.1f );
241  app.SetDiffuse( 0.6f, 0.6f, 0.6f );
242 
243  defaultColor = app.GetRawPtr();
244  return defaultColor;
245  }
246 
247  Standard_Real id = colorObj->Distance( refColor );
248  std::map< Standard_Real, SGNODE* >::iterator item;
249  item = colors.find( id );
250 
251  if( item != colors.end() )
252  return item->second;
253 
254  IFSG_APPEARANCE app( true );
255  app.SetShininess( 0.1f );
256  app.SetSpecular( 0.12f, 0.12f, 0.12f );
257  app.SetAmbient( 0.1f, 0.1f, 0.1f );
258  app.SetDiffuse( colorObj->Red(), colorObj->Green(), colorObj->Blue() );
259  colors.insert( std::pair< Standard_Real, SGNODE* >( id, app.GetRawPtr() ) );
260 
261  return app.GetRawPtr();
262  }
263 };
264 
265 
267 {
268  FMT_NONE = 0,
272 };
273 
274 
275 FormatType fileType( const char* aFileName )
276 {
277  wxFileName fname( wxString::FromUTF8Unchecked( aFileName ) );
278  wxFFileInputStream ifile( fname.GetFullPath() );
279 
280  if( !ifile.IsOk() )
281  return FMT_NONE;
282 
283  if( fname.GetExt().MakeUpper().EndsWith( "STPZ" ) ||
284  fname.GetExt().MakeUpper().EndsWith( "GZ" ) )
285  return FMT_STPZ;
286 
287  char iline[82];
288  memset( iline, 0, 82 );
289  ifile.Read( iline, 82 );
290  iline[81] = 0; // ensure NULL termination when string is too long
291 
292  // check for STEP in Part 21 format
293  // (this can give false positives since Part 21 is not exclusively STEP)
294  if( !strncmp( iline, "ISO-10303-21;", 13 ) )
295  return FMT_STEP;
296 
297  std::string fstr = iline;
298 
299  // check for STEP in XML format
300  // (this can give both false positive and false negatives)
301  if( fstr.find( "urn:oid:1.0.10303." ) != std::string::npos )
302  return FMT_STEP;
303 
304  // Note: this is a very simple test which can yield false positives; the only
305  // sure method for determining if a file *not* an IGES model is to attempt
306  // to load it.
307  if( iline[72] == 'S' && ( iline[80] == 0 || iline[80] == 13 || iline[80] == 10 ) )
308  return FMT_IGES;
309 
310  return FMT_NONE;
311 }
312 
313 
314 void getTag( TDF_Label& label, std::string& aTag )
315 {
316  if( label.IsNull() )
317  return;
318 
319  std::string rtag; // tag in reverse
320  aTag.clear();
321  int id = label.Tag();
322  std::ostringstream ostr;
323  ostr << id;
324  rtag = ostr.str();
325  ostr.str( "" );
326  ostr.clear();
327 
328  TDF_Label nlab = label.Father();
329 
330  while( !nlab.IsNull() )
331  {
332  rtag.append( 1, ':' );
333  id = nlab.Tag();
334  ostr << id;
335  rtag.append( ostr.str() );
336  ostr.str( "" );
337  ostr.clear();
338  nlab = nlab.Father();
339  };
340 
341  std::string::reverse_iterator bI = rtag.rbegin();
342  std::string::reverse_iterator eI = rtag.rend();
343 
344  while( bI != eI )
345  {
346  aTag.append( 1, *bI );
347  ++bI;
348  }
349 }
350 
351 
352 bool getColor( DATA& data, TDF_Label label, Quantity_Color& color )
353 {
354  while( true )
355  {
356  if( data.m_color->GetColor( label, XCAFDoc_ColorGen, color ) )
357  return true;
358  else if( data.m_color->GetColor( label, XCAFDoc_ColorSurf, color ) )
359  return true;
360  else if( data.m_color->GetColor( label, XCAFDoc_ColorCurv, color ) )
361  return true;
362 
363  label = label.Father();
364 
365  if( label.IsNull() )
366  break;
367  };
368 
369  return false;
370 }
371 
372 
373 void addItems( SGNODE* parent, std::vector< SGNODE* >* lp )
374 {
375  if( nullptr == lp )
376  return;
377 
378  std::vector< SGNODE* >::iterator sL = lp->begin();
379  std::vector< SGNODE* >::iterator eL = lp->end();
380  SGNODE* item;
381 
382  while( sL != eL )
383  {
384  item = *sL;
385 
386  if( nullptr == S3D::GetSGNodeParent( item ) )
387  S3D::AddSGNodeChild( parent, item );
388  else
389  S3D::AddSGNodeRef( parent, item );
390 
391  ++sL;
392  }
393 }
394 
395 
396 bool readIGES( Handle( TDocStd_Document ) & m_doc, const char* fname )
397 {
398  IGESCAFControl_Reader reader;
399  IFSelect_ReturnStatus stat = reader.ReadFile( fname );
400  reader.PrintCheckLoad( Standard_False, IFSelect_ItemsByEntity );
401 
402  if( stat != IFSelect_RetDone )
403  return false;
404 
405  // Enable file-defined shape precision
406  if( !Interface_Static::SetIVal( "read.precision.mode", 0 ) )
407  return false;
408 
409  // set other translation options
410  reader.SetColorMode(true); // use model colors
411  reader.SetNameMode(false); // don't use IGES label names
412  reader.SetLayerMode(false); // ignore LAYER data
413 
414  if ( !reader.Transfer( m_doc ) )
415  return false;
416 
417  // are there any shapes to translate?
418  if( reader.NbShapes() < 1 )
419  return false;
420 
421  return true;
422 }
423 
424 
425 bool readSTEP( Handle(TDocStd_Document)& m_doc, const char* fname )
426 {
427  STEPCAFControl_Reader reader;
428  IFSelect_ReturnStatus stat = reader.ReadFile( fname );
429 
430  if( stat != IFSelect_RetDone )
431  return false;
432 
433  // Enable user-defined shape precision
434  if( !Interface_Static::SetIVal( "read.precision.mode", 1 ) )
435  return false;
436 
437  // Set the shape conversion precision to USER_PREC (default 0.0001 has too many triangles)
438  if( !Interface_Static::SetRVal( "read.precision.val", USER_PREC ) )
439  return false;
440 
441  // set other translation options
442  reader.SetColorMode( true ); // use model colors
443  reader.SetNameMode( false ); // don't use label names
444  reader.SetLayerMode( false ); // ignore LAYER data
445 
446  if ( !reader.Transfer( m_doc ) )
447  {
448  m_doc->Close();
449  return false;
450  }
451 
452  // are there any shapes to translate?
453  if( reader.NbRootsForTransfer() < 1 )
454  return false;
455 
456  return true;
457 }
458 
459 
460 bool readSTEPZ( Handle(TDocStd_Document)& m_doc, const char* aFileName )
461 {
462  wxFileName fname( wxString::FromUTF8Unchecked( aFileName ) );
463  wxFFileInputStream ifile( fname.GetFullPath() );
464 
465  wxFileName outFile( fname );
466 
467  outFile.SetPath( wxStandardPaths::Get().GetTempDir() );
468  outFile.SetExt( "STEP" );
469 
470  wxFileOffset size = ifile.GetLength();
471  wxBusyCursor busycursor;
472 
473  if( size == wxInvalidOffset )
474  return false;
475 
476  {
477  bool success = false;
478  wxFFileOutputStream ofile( outFile.GetFullPath() );
479 
480  if( !ofile.IsOk() )
481  return false;
482 
483  char *buffer = new char[size];
484 
485  ifile.Read( buffer, size);
486  std::string expanded;
487 
488  try
489  {
490  expanded = gzip::decompress( buffer, size );
491  success = true;
492  }
493  catch(...)
494  {}
495 
496  if( expanded.empty() )
497  {
498  ifile.Reset();
499  ifile.SeekI( 0 );
500  wxZipInputStream izipfile( ifile );
501  std::unique_ptr<wxZipEntry> zip_file( izipfile.GetNextEntry() );
502 
503  if( zip_file && !zip_file->IsDir() && izipfile.CanRead() )
504  {
505  izipfile.Read( ofile );
506  success = true;
507  }
508  }
509  else
510  {
511  ofile.Write( expanded.data(), expanded.size() );
512  }
513 
514  delete[] buffer;
515  ofile.Close();
516 
517  if( !success )
518  return false;
519  }
520 
521  bool retval = readSTEP( m_doc, outFile.GetFullPath().mb_str() );
522 
523  // Cleanup our temporary file
524  wxRemoveFile( outFile.GetFullPath() );
525 
526  return retval;
527 }
528 
529 
530 SCENEGRAPH* LoadModel( char const* filename )
531 {
532  DATA data;
533 
534  Handle(XCAFApp_Application) m_app = XCAFApp_Application::GetApplication();
535  m_app->NewDocument( "MDTV-XCAF", data.m_doc );
536  FormatType modelFmt = fileType( filename );
537 
538  switch( modelFmt )
539  {
540  case FMT_IGES:
541  data.renderBoth = true;
542 
543  if( !readIGES( data.m_doc, filename ) )
544  return nullptr;
545 
546  break;
547 
548  case FMT_STEP:
549  if( !readSTEP( data.m_doc, filename ) )
550  return nullptr;
551 
552  break;
553 
554  case FMT_STPZ:
555  if( !readSTEPZ( data.m_doc, filename ) )
556  return nullptr;
557 
558  break;
559 
560 
561  default:
562  return nullptr;
563  break;
564  }
565 
566  data.m_assy = XCAFDoc_DocumentTool::ShapeTool( data.m_doc->Main() );
567  data.m_color = XCAFDoc_DocumentTool::ColorTool( data.m_doc->Main() );
568 
569  // retrieve all free shapes
570  TDF_LabelSequence frshapes;
571  data.m_assy->GetFreeShapes( frshapes );
572 
573  int nshapes = frshapes.Length();
574  int id = 1;
575  bool ret = false;
576 
577  // create the top level SG node
578  IFSG_TRANSFORM topNode( true );
579  data.scene = topNode.GetRawPtr();
580 
581  while( id <= nshapes )
582  {
583  TopoDS_Shape shape = data.m_assy->GetShape( frshapes.Value( id ) );
584 
585  if ( !shape.IsNull() && processNode( shape, data, data.scene, nullptr ) )
586  ret = true;
587 
588  ++id;
589  };
590 
591  if( !ret )
592  return nullptr;
593 
594  SCENEGRAPH* scene = (SCENEGRAPH*)data.scene;
595 
596  // DEBUG: WRITE OUT VRML2 FILE TO CONFIRM STRUCTURE
597 #if ( defined( DEBUG_OCE ) && DEBUG_OCE > 3 )
598  if( data.scene )
599  {
600  wxFileName fn( wxString::FromUTF8Unchecked( filename ) );
601  wxString output;
602 
603  if( FMT_STEP == modelFmt )
604  output = wxT( "_step-" );
605  else
606  output = wxT( "_iges-" );
607 
608  output.append( fn.GetName() );
609  output.append( wxT( ".wrl" ) );
610  S3D::WriteVRML( output.ToUTF8(), true, data.scene, true, true );
611  }
612 #endif
613 
614  // set to NULL to prevent automatic destruction of the scene data
615  data.scene = nullptr;
616 
617  return scene;
618 }
619 
620 
621 bool processShell( const TopoDS_Shape& shape, DATA& data, SGNODE* parent,
622  std::vector< SGNODE* >* items, Quantity_Color* color )
623 {
624  TopoDS_Iterator it;
625  bool ret = false;
626 
627  for( it.Initialize( shape, false, false ); it.More(); it.Next() )
628  {
629  const TopoDS_Face& face = TopoDS::Face( it.Value() );
630 
631  if( processFace( face, data, parent, items, color ) )
632  ret = true;
633  }
634 
635  return ret;
636 }
637 
638 
639 bool processSolid( const TopoDS_Shape& shape, DATA& data, SGNODE* parent,
640  std::vector< SGNODE* >* items )
641 {
642  TDF_Label label;
643  data.hasSolid = true;
644  std::string partID;
645  Quantity_Color col;
646  Quantity_Color* lcolor = nullptr;
647 
648  // Search the whole model first to make sure something exists (may or may not have color)
649  if( !data.m_assy->Search( shape, label ) )
650  {
651  static int i = 0;
652  std::ostringstream ostr;
653  ostr << "KMISC_" << i++;
654  partID = ostr.str();
655  }
656  else
657  {
658  bool found_color = false;
659 
660  if( getColor( data, label, col ) )
661  {
662  found_color = true;
663  lcolor = &col;
664  }
665 
666  // If the top-level label doesn't have the color information, search components
667  if( !found_color )
668  {
669  if( data.m_assy->Search( shape, label, Standard_False, Standard_True, Standard_True ) &&
670  getColor( data, label, col ) )
671  {
672  found_color = true;
673  lcolor = &col;
674  }
675  }
676 
677  // If the components do not have color information, search all components without location
678  if( !found_color )
679  {
680  if( data.m_assy->Search( shape, label, Standard_False, Standard_False,
681  Standard_True ) &&
682  getColor( data, label, col ) )
683  {
684  found_color = true;
685  lcolor = &col;
686  }
687  }
688 
689  // Our last chance to find the color looks for color as a subshape of top-level simple
690  // shapes.
691  if( !found_color )
692  {
693  if( data.m_assy->Search( shape, label, Standard_False, Standard_False,
694  Standard_False ) &&
695  getColor( data, label, col ) )
696  {
697  found_color = true;
698  lcolor = &col;
699  }
700  }
701 
702  getTag( label, partID );
703  }
704 
705  TopoDS_Iterator it;
706  IFSG_TRANSFORM childNode( parent );
707  SGNODE* pptr = childNode.GetRawPtr();
708  const TopLoc_Location& loc = shape.Location();
709  bool ret = false;
710 
711  if( !loc.IsIdentity() )
712  {
713  gp_Trsf T = loc.Transformation();
714  gp_XYZ coord = T.TranslationPart();
715  childNode.SetTranslation( SGPOINT( coord.X(), coord.Y(), coord.Z() ) );
716  gp_XYZ axis;
717  Standard_Real angle;
718 
719  if( T.GetRotation( axis, angle ) )
720  childNode.SetRotation( SGVECTOR( axis.X(), axis.Y(), axis.Z() ), angle );
721  }
722 
723  std::vector< SGNODE* >* component = nullptr;
724 
725  if( !partID.empty() )
726  data.GetShape( partID, component );
727 
728  if( component )
729  {
730  addItems( pptr, component );
731 
732  if( nullptr != items )
733  items->push_back( pptr );
734  }
735 
736  // instantiate the solid
737  std::vector< SGNODE* > itemList;
738 
739  for( it.Initialize( shape, false, false ); it.More(); it.Next() )
740  {
741  const TopoDS_Shape& subShape = it.Value();
742 
743  if( processShell( subShape, data, pptr, &itemList, lcolor ) )
744  ret = true;
745  }
746 
747  if( !ret )
748  childNode.Destroy();
749  else if( nullptr != items )
750  items->push_back( pptr );
751 
752  return ret;
753 }
754 
755 
756 bool processComp( const TopoDS_Shape& shape, DATA& data, SGNODE* parent,
757  std::vector< SGNODE* >* items )
758 {
759  TopoDS_Iterator it;
760  IFSG_TRANSFORM childNode( parent );
761  SGNODE* pptr = childNode.GetRawPtr();
762  const TopLoc_Location& loc = shape.Location();
763  bool ret = false;
764 
765  if( !loc.IsIdentity() )
766  {
767  gp_Trsf T = loc.Transformation();
768  gp_XYZ coord = T.TranslationPart();
769  childNode.SetTranslation( SGPOINT( coord.X(), coord.Y(), coord.Z() ) );
770  gp_XYZ axis;
771  Standard_Real angle;
772 
773  if( T.GetRotation( axis, angle ) )
774  childNode.SetRotation( SGVECTOR( axis.X(), axis.Y(), axis.Z() ), angle );
775  }
776 
777  for( it.Initialize( shape, false, false ); it.More(); it.Next() )
778  {
779  const TopoDS_Shape& subShape = it.Value();
780  TopAbs_ShapeEnum stype = subShape.ShapeType();
781  data.hasSolid = false;
782 
783  switch( stype )
784  {
785  case TopAbs_COMPOUND:
786  case TopAbs_COMPSOLID:
787  if( processComp( subShape, data, pptr, items ) )
788  ret = true;
789 
790  break;
791 
792  case TopAbs_SOLID:
793  if( processSolid( subShape, data, pptr, items ) )
794  ret = true;
795 
796  break;
797 
798  case TopAbs_SHELL:
799  if( processShell( subShape, data, pptr, items, nullptr ) )
800  ret = true;
801 
802  break;
803 
804  case TopAbs_FACE:
805  if( processFace( TopoDS::Face( subShape ), data, pptr, items, nullptr ) )
806  ret = true;
807 
808  break;
809 
810  default:
811  break;
812  }
813  }
814 
815  if( !ret )
816  childNode.Destroy();
817  else if( nullptr != items )
818  items->push_back( pptr );
819 
820  return ret;
821 }
822 
823 
824 bool processNode( const TopoDS_Shape& shape, DATA& data, SGNODE* parent,
825  std::vector< SGNODE* >* items )
826 {
827  TopAbs_ShapeEnum stype = shape.ShapeType();
828  bool ret = false;
829  data.hasSolid = false;
830 
831  switch( stype )
832  {
833  case TopAbs_COMPOUND:
834  case TopAbs_COMPSOLID:
835  if( processComp( shape, data, parent, items ) )
836  ret = true;
837 
838  break;
839 
840  case TopAbs_SOLID:
841  if( processSolid( shape, data, parent, items ) )
842  ret = true;
843 
844  break;
845 
846  case TopAbs_SHELL:
847  if( processShell( shape, data, parent, items, nullptr ) )
848  ret = true;
849 
850  break;
851 
852  case TopAbs_FACE:
853  if( processFace( TopoDS::Face( shape ), data, parent, items, nullptr ) )
854  ret = true;
855 
856  break;
857 
858  default:
859  break;
860  }
861 
862  return ret;
863 }
864 
865 
866 bool processFace( const TopoDS_Face& face, DATA& data, SGNODE* parent,
867  std::vector< SGNODE* >* items, Quantity_Color* color )
868 {
869  if( Standard_True == face.IsNull() )
870  return false;
871 
872  bool reverse = ( face.Orientation() == TopAbs_REVERSED );
873  SGNODE* ashape = nullptr;
874  std::string partID;
875  TDF_Label label;
876 
877  bool useBothSides = false;
878 
879  // for IGES renderBoth = TRUE; for STEP if a shell or face is not a descendant
880  // of a SOLID then hasSolid = false and we must render both sides
881  if( data.renderBoth || !data.hasSolid )
882  useBothSides = true;
883 
884  if( data.m_assy->FindShape( face, label, Standard_False ) )
885  getTag( label, partID );
886 
887  if( !partID.empty() )
888  ashape = data.GetFace( partID );
889 
890  if( ashape )
891  {
892  if( nullptr == S3D::GetSGNodeParent( ashape ) )
893  S3D::AddSGNodeChild( parent, ashape );
894  else
895  S3D::AddSGNodeRef( parent, ashape );
896 
897  if( nullptr != items )
898  items->push_back( ashape );
899 
900  if( useBothSides )
901  {
902  std::string id2 = partID;
903  id2.append( "b" );
904  SGNODE* shapeB = data.GetFace( id2 );
905 
906  if( nullptr == S3D::GetSGNodeParent( shapeB ) )
907  S3D::AddSGNodeChild( parent, shapeB );
908  else
909  S3D::AddSGNodeRef( parent, shapeB );
910 
911  if( nullptr != items )
912  items->push_back( shapeB );
913  }
914 
915  return true;
916  }
917 
918  TopLoc_Location loc;
919  Standard_Boolean isTessellate (Standard_False);
920  Handle( Poly_Triangulation ) triangulation = BRep_Tool::Triangulation( face, loc );
921 
922  if( triangulation.IsNull() || triangulation->Deflection() > USER_PREC + Precision::Confusion() )
923  isTessellate = Standard_True;
924 
925  if( isTessellate )
926  {
927  BRepMesh_IncrementalMesh IM(face, USER_PREC, Standard_False, USER_ANGLE );
928  triangulation = BRep_Tool::Triangulation( face, loc );
929  }
930 
931  if( triangulation.IsNull() == Standard_True )
932  return false;
933 
934  Quantity_Color lcolor;
935 
936  // check for a face color; this has precedence over SOLID colors
937  do
938  {
939  TDF_Label L;
940 
941  if( data.m_color->ShapeTool()->Search( face, L ) )
942  {
943  if( data.m_color->GetColor( L, XCAFDoc_ColorGen, lcolor )
944  || data.m_color->GetColor( L, XCAFDoc_ColorCurv, lcolor )
945  || data.m_color->GetColor( L, XCAFDoc_ColorSurf, lcolor ) )
946  color = &lcolor;
947  }
948  } while( 0 );
949 
950  SGNODE* ocolor = data.GetColor( color );
951 
952  // create a SHAPE and attach the color and data,
953  // then attach the shape to the parent and return TRUE
954  IFSG_SHAPE vshape( true );
955  IFSG_FACESET vface( vshape );
956  IFSG_COORDS vcoords( vface );
957  IFSG_COORDINDEX coordIdx( vface );
958 
959  if( nullptr == S3D::GetSGNodeParent( ocolor ) )
960  S3D::AddSGNodeChild( vshape.GetRawPtr(), ocolor );
961  else
962  S3D::AddSGNodeRef( vshape.GetRawPtr(), ocolor );
963 
964  const TColgp_Array1OfPnt& arrPolyNodes = triangulation->Nodes();
965  const Poly_Array1OfTriangle& arrTriangles = triangulation->Triangles();
966 
967  std::vector< SGPOINT > vertices;
968  std::vector< int > indices;
969  std::vector< int > indices2;
970  gp_Trsf tx;
971 
972  for( int i = 1; i <= triangulation->NbNodes(); i++ )
973  {
974  gp_XYZ v( arrPolyNodes(i).Coord() );
975  vertices.emplace_back( v.X(), v.Y(), v.Z() );
976  }
977 
978  for( int i = 1; i <= triangulation->NbTriangles(); i++ )
979  {
980  int a, b, c;
981  arrTriangles( i ).Get( a, b, c );
982  a--;
983 
984  if( reverse )
985  {
986  int tmp = b - 1;
987  b = c - 1;
988  c = tmp;
989  }
990  else
991  {
992  b--;
993  c--;
994  }
995 
996  indices.push_back( a );
997  indices.push_back( b );
998  indices.push_back( c );
999 
1000  if( useBothSides )
1001  {
1002  indices2.push_back( b );
1003  indices2.push_back( a );
1004  indices2.push_back( c );
1005  }
1006  }
1007 
1008  vcoords.SetCoordsList( vertices.size(), &vertices[0] );
1009  coordIdx.SetIndices( indices.size(), &indices[0] );
1010  vface.CalcNormals( nullptr );
1011  vshape.SetParent( parent );
1012 
1013  if( !partID.empty() )
1014  data.faces.insert( std::pair< std::string, SGNODE* >( partID, vshape.GetRawPtr() ) );
1015 
1016  // The outer surface of an IGES model is indeterminate so
1017  // we must render both sides of a surface.
1018  if( useBothSides )
1019  {
1020  std::string id2 = partID;
1021  id2.append( "b" );
1022  IFSG_SHAPE vshape2( true );
1023  IFSG_FACESET vface2( vshape2 );
1024  IFSG_COORDS vcoords2( vface2 );
1025  IFSG_COORDINDEX coordIdx2( vface2 );
1026  S3D::AddSGNodeRef( vshape2.GetRawPtr(), ocolor );
1027 
1028  vcoords2.SetCoordsList( vertices.size(), &vertices[0] );
1029  coordIdx2.SetIndices( indices2.size(), &indices2[0] );
1030  vface2.CalcNormals( nullptr );
1031  vshape2.SetParent( parent );
1032 
1033  if( !partID.empty() )
1034  data.faces.insert( std::pair< std::string, SGNODE* >( id2, vshape2.GetRawPtr() ) );
1035  }
1036 
1037  return true;
1038 }
SGNODE * GetColor(Quantity_Color *colorObj)
Definition: loadmodel.cpp:230
bool readSTEPZ(Handle(TDocStd_Document)&m_doc, const char *aFileName)
Definition: loadmodel.cpp:460
DATA()
Definition: loadmodel.cpp:125
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:218
FACEMAP faces
Definition: loadmodel.cpp:121
~DATA()
Definition: loadmodel.cpp:134
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:492
bool processSolid(const TopoDS_Shape &shape, DATA &data, SGNODE *parent, std::vector< SGNODE * > *items)
Definition: loadmodel.cpp:639
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:120
bool readSTEP(Handle(TDocStd_Document)&m_doc, const char *fname)
Definition: loadmodel.cpp:425
bool processNode(const TopoDS_Shape &shape, DATA &data, SGNODE *parent, std::vector< SGNODE * > *items)
Definition: loadmodel.cpp:824
std::pair< std::string, std::vector< SGNODE * > > NODEITEM
Definition: loadmodel.cpp:95
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:501
std::map< std::string, SGNODE * > FACEMAP
Definition: loadmodel.cpp:93
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:76
std::map< std::string, std::vector< SGNODE * > > NODEMAP
Definition: loadmodel.cpp:94
SGLIB_API void DestroyNode(SGNODE *aNode) noexcept
Function DestroyNode deletes the given SG* class node.
Definition: ifsg_api.cpp:148
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:866
void getTag(TDF_Label &label, std::string &aTag)
Definition: loadmodel.cpp:314
Quantity_Color refColor
Definition: loadmodel.cpp:118
#define USER_ANGLE
Definition: loadmodel.cpp:90
SGLIB_API bool AddSGNodeChild(SGNODE *aParent, SGNODE *aChild)
Definition: ifsg_api.cpp:510
bool SetRotation(const SGVECTOR &aRotationAxis, double aAngle)
bool getColor(DATA &data, TDF_Label label, Quantity_Color &color)
Definition: loadmodel.cpp:352
FormatType fileType(const char *aFileName)
Definition: loadmodel.cpp:275
bool SetShininess(float aShininess) noexcept
bool CalcNormals(SGNODE **aPtr)
FormatType
Definition: loadmodel.cpp:266
bool readIGES(Handle(TDocStd_Document) &m_doc, const char *fname)
Definition: loadmodel.cpp:396
#define USER_PREC
Definition: loadmodel.cpp:84
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:530
bool hasSolid
Definition: loadmodel.cpp:123
void addItems(SGNODE *parent, std::vector< SGNODE * > *lp)
Definition: loadmodel.cpp:373
bool SetSpecular(float aRVal, float aGVal, float aBVal)
bool SetCoordsList(size_t aListSize, const SGPOINT *aCoordsList)
SGNODE * scene
Definition: loadmodel.cpp:116
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:117
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:92
NODEMAP shapes
Definition: loadmodel.cpp:119
bool renderBoth
Definition: loadmodel.cpp:122
bool GetShape(const std::string &id, std::vector< SGNODE * > *&listPtr)
Definition: loadmodel.cpp:204
bool processComp(const TopoDS_Shape &shape, DATA &data, SGNODE *parent, std::vector< SGNODE * > *items)
Definition: loadmodel.cpp:756
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:621