KiCad PCB EDA Suite
sg_faceset.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) 2015-2017 Cirilo Bernardo <cirilo.bernardo@gmail.com>
5  * Copyright (C) 2020 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 
26 #include <iostream>
27 #include <sstream>
28 #include <wx/log.h>
29 
30 #include "3d_cache/sg/sg_faceset.h"
31 #include "3d_cache/sg/sg_colors.h"
32 #include "3d_cache/sg/sg_coords.h"
33 #include "3d_cache/sg/sg_normals.h"
35 #include "3d_cache/sg/sg_helpers.h"
36 
37 
38 SGFACESET::SGFACESET( SGNODE* aParent ) : SGNODE( aParent )
39 {
41  m_Colors = nullptr;
42  m_Coords = nullptr;
43  m_CoordIndices = nullptr;
44  m_Normals = nullptr;
45  m_RColors = nullptr;
46  m_RCoords = nullptr;
47  m_RNormals = nullptr;
48  valid = false;
49  validated = false;
50 
51  if( nullptr != aParent && S3D::SGTYPE_SHAPE != aParent->GetNodeType() )
52  {
53  m_Parent = nullptr;
54 
55  wxLogTrace( MASK_3D_SG, "%s:%s:%d * [BUG] inappropriate parent to SGFACESET (type %s)",
56  __FILE__, __FUNCTION__, __LINE__, aParent->GetNodeType() );
57  }
58  else if( nullptr != aParent && S3D::SGTYPE_SHAPE == aParent->GetNodeType() )
59  {
60  m_Parent->AddChildNode( this );
61  }
62 }
63 
64 
66 {
67  // drop references
68  if( m_RColors )
69  {
70  m_RColors->delNodeRef( this );
71  m_RColors = nullptr;
72  }
73 
74  if( m_RCoords )
75  {
76  m_RCoords->delNodeRef( this );
77  m_RCoords = nullptr;
78  }
79 
80  if( m_RNormals )
81  {
82  m_RNormals->delNodeRef( this );
83  m_RNormals = nullptr;
84  }
85 
86  // delete owned objects
87  if( m_Colors )
88  {
89  m_Colors->SetParent( nullptr, false );
90  delete m_Colors;
91  m_Colors = nullptr;
92  }
93 
94  if( m_Coords )
95  {
96  m_Coords->SetParent( nullptr, false );
97  delete m_Coords;
98  m_Coords = nullptr;
99  }
100 
101  if( m_Normals )
102  {
103  m_Normals->SetParent( nullptr, false );
104  delete m_Normals;
105  m_Normals = nullptr;
106  }
107 
108  if( m_CoordIndices )
109  {
110  m_CoordIndices->SetParent( nullptr, false );
111  delete m_CoordIndices;
112  m_CoordIndices = nullptr;
113  }
114 }
115 
116 
117 bool SGFACESET::SetParent( SGNODE* aParent, bool notify )
118 {
119  if( nullptr != m_Parent )
120  {
121  if( aParent == m_Parent )
122  return true;
123 
124  // handle the change in parents
125  if( notify )
126  m_Parent->unlinkChildNode( this );
127 
128  m_Parent = nullptr;
129 
130  if( nullptr == aParent )
131  return true;
132  }
133 
134  // only a SGSHAPE may be parent to a SGFACESET
135  if( nullptr != aParent && S3D::SGTYPE_SHAPE != aParent->GetNodeType() )
136  return false;
137 
138  m_Parent = aParent;
139 
140  if( m_Parent )
141  m_Parent->AddChildNode( this );
142 
143  return true;
144 }
145 
146 
147 SGNODE* SGFACESET::FindNode(const char *aNodeName, const SGNODE *aCaller)
148 {
149  if( nullptr == aNodeName || 0 == aNodeName[0] )
150  return nullptr;
151 
152  if( !m_Name.compare( aNodeName ) )
153  return this;
154 
155  SGNODE* np = nullptr;
156 
157  if( m_Colors )
158  {
159  np = m_Colors->FindNode( aNodeName, this );
160 
161  if( np )
162  return np;
163  }
164 
165  if( m_Coords )
166  {
167  np = m_Coords->FindNode( aNodeName, this );
168 
169  if( np )
170  return np;
171  }
172 
173  if( m_CoordIndices )
174  {
175  np = m_CoordIndices->FindNode( aNodeName, this );
176 
177  if( np )
178  return np;
179  }
180 
181  if( m_Normals )
182  {
183  np = m_Normals->FindNode( aNodeName, this );
184 
185  if( np )
186  return np;
187  }
188 
189  // query the parent if appropriate
190  if( aCaller == m_Parent || nullptr == m_Parent )
191  return nullptr;
192 
193  return m_Parent->FindNode( aNodeName, this );
194 }
195 
196 
197 void SGFACESET::unlinkNode( const SGNODE* aNode, bool isChild )
198 {
199  if( nullptr == aNode )
200  return;
201 
202  valid = false;
203  validated = false;
204 
205  if( isChild )
206  {
207  if( aNode == m_Colors )
208  {
209  m_Colors = nullptr;
210  return;
211  }
212 
213  if( aNode == m_Coords )
214  {
215  m_Coords = nullptr;
216  return;
217  }
218 
219  if( aNode == m_Normals )
220  {
221  m_Normals = nullptr;
222  return;
223  }
224 
225  if( aNode == m_CoordIndices )
226  {
227  m_CoordIndices = nullptr;
228  return;
229  }
230  }
231  else
232  {
233  if( aNode == m_RColors )
234  {
235  delNodeRef( this );
236  m_RColors = nullptr;
237  return;
238  }
239 
240  if( aNode == m_RCoords )
241  {
242  delNodeRef( this );
243  m_RCoords = nullptr;
244  return;
245  }
246 
247  if( aNode == m_RNormals )
248  {
249  delNodeRef( this );
250  m_RNormals = nullptr;
251  return;
252  }
253  }
254 
255  wxLogTrace( MASK_3D_SG, "%s:%s:%d * [BUG] unlinkNode() did not find its target",
256  __FILE__, __FUNCTION__, __LINE__ );
257 }
258 
259 
260 void SGFACESET::unlinkChildNode( const SGNODE* aNode )
261 {
262  unlinkNode( aNode, true );
263 }
264 
265 
266 void SGFACESET::unlinkRefNode( const SGNODE* aNode )
267 {
268  unlinkNode( aNode, false );
269 }
270 
271 
272 
273 bool SGFACESET::addNode( SGNODE* aNode, bool isChild )
274 {
275  wxCHECK( aNode, false );
276 
277  valid = false;
278  validated = false;
279 
280  if( S3D::SGTYPE_COLORS == aNode->GetNodeType() )
281  {
282  if( m_Colors || m_RColors )
283  {
284  if( aNode != m_Colors && aNode != m_RColors )
285  {
286  wxLogTrace( MASK_3D_SG, "%s:%s:%d * [BUG] assigning multiple Colors nodes",
287  __FILE__, __FUNCTION__, __LINE__ );
288 
289  return false;
290  }
291 
292  return true;
293  }
294 
295  if( isChild )
296  {
297  m_Colors = (SGCOLORS*)aNode;
298  m_Colors->SetParent( this );
299  }
300  else
301  {
302  m_RColors = (SGCOLORS*)aNode;
303  m_RColors->addNodeRef( this );
304  }
305 
306  return true;
307  }
308 
309  if( S3D::SGTYPE_COORDS == aNode->GetNodeType() )
310  {
311  if( m_Coords || m_RCoords )
312  {
313  if( aNode != m_Coords && aNode != m_RCoords )
314  {
315  wxLogTrace( MASK_3D_SG, "%s:%s:%d * [BUG] assigning multiple Colors nodes",
316  __FILE__, __FUNCTION__, __LINE__ );
317 
318  return false;
319  }
320 
321  return true;
322  }
323 
324  if( isChild )
325  {
326  m_Coords = (SGCOORDS*)aNode;
327  m_Coords->SetParent( this );
328  }
329  else
330  {
331  m_RCoords = (SGCOORDS*)aNode;
332  m_RCoords->addNodeRef( this );
333  }
334 
335  return true;
336  }
337 
338  if( S3D::SGTYPE_NORMALS == aNode->GetNodeType() )
339  {
340  if( m_Normals || m_RNormals )
341  {
342  if( aNode != m_Normals && aNode != m_RNormals )
343  {
344  wxLogTrace( MASK_3D_SG, "%s:%s:%d * [BUG] assigning multiple Normals nodes",
345  __FILE__, __FUNCTION__, __LINE__ );
346 
347  return false;
348  }
349 
350  return true;
351  }
352 
353  if( isChild )
354  {
355  m_Normals = (SGNORMALS*)aNode;
356  m_Normals->SetParent( this );
357  }
358  else
359  {
360  m_RNormals = (SGNORMALS*)aNode;
361  m_RNormals->addNodeRef( this );
362  }
363 
364  return true;
365  }
366 
367  if( S3D::SGTYPE_COORDINDEX == aNode->GetNodeType() )
368  {
369  if( m_CoordIndices )
370  {
371  if( aNode != m_CoordIndices )
372  {
373  wxLogTrace( MASK_3D_SG, "%s:%s:%d * [BUG] assigning multiple CoordIndex nodes",
374  __FILE__, __FUNCTION__, __LINE__ );
375 
376  return false;
377  }
378 
379  return true;
380  }
381 
382  m_CoordIndices = (SGCOORDINDEX*)aNode;
383  m_CoordIndices->SetParent( this );
384 
385  return true;
386  }
387 
388  wxLogTrace( MASK_3D_SG, "%s:%s:%d * [BUG] object type '%s' is not a valid type for "
389  "this object '%d'", __FILE__, __FUNCTION__, __LINE__, aNode->GetName(),
390  aNode->GetNodeType() );
391 
392  return false;
393 }
394 
395 
397 {
398  return addNode( aNode, false );
399 }
400 
401 
403 {
404  return addNode( aNode, true );
405 }
406 
407 
409 {
410  m_written = false;
411 
412  // rename this node
413  m_Name.clear();
414  GetName();
415 
416  // rename all Colors and Indices
417  if( m_Colors )
419 
420  // rename all Coordinates and Indices
421  if( m_Coords )
423 
424  if( m_CoordIndices )
426 
427  // rename all Normals and Indices
428  if( m_Normals )
430 }
431 
432 
433 bool SGFACESET::WriteVRML( std::ostream& aFile, bool aReuseFlag )
434 {
435  if( ( nullptr == m_Coords && nullptr == m_RCoords ) || ( nullptr == m_CoordIndices ) )
436  {
437  return false;
438  }
439 
440  if( aReuseFlag )
441  {
442  if( !m_written )
443  {
444  aFile << " geometry DEF " << GetName() << " IndexedFaceSet {\n";
445  m_written = true;
446  }
447  else
448  {
449  aFile << "USE " << GetName() << "\n";
450  return true;
451  }
452  }
453  else
454  {
455  aFile << " geometry IndexedFaceSet {\n";
456  }
457 
458  if( m_Coords )
459  m_Coords->WriteVRML( aFile, aReuseFlag );
460 
461  if( m_RCoords )
462  m_RCoords->WriteVRML( aFile, aReuseFlag );
463 
464  if( m_CoordIndices )
465  m_CoordIndices->WriteVRML( aFile, aReuseFlag );
466 
467  if( m_Normals || m_RNormals )
468  aFile << " normalPerVertex TRUE\n";
469 
470  if( m_Normals )
471  m_Normals->WriteVRML( aFile, aReuseFlag );
472 
473  if( m_RNormals )
474  m_RNormals->WriteVRML( aFile, aReuseFlag );
475 
476  if( m_Colors )
477  m_Colors->WriteVRML( aFile, aReuseFlag );
478 
479  if( m_RColors )
480  m_RColors->WriteVRML( aFile, aReuseFlag );
481 
482  aFile << "}\n";
483 
484  return true;
485 }
486 
487 
488 bool SGFACESET::WriteCache( std::ostream& aFile, SGNODE* parentNode )
489 {
490  if( nullptr == parentNode )
491  {
492  wxCHECK( m_Parent, false );
493 
494  SGNODE* np = m_Parent;
495 
496  while( nullptr != np->GetParent() )
497  np = np->GetParent();
498 
499  if( np->WriteCache( aFile, nullptr ) )
500  {
501  m_written = true;
502  return true;
503  }
504 
505  return false;
506  }
507 
508  wxCHECK( parentNode == m_Parent, false );
509 
510  if( !aFile.good() )
511  {
512  wxLogTrace( MASK_3D_SG, "%s:%s:%d * [INFO] bad stream", __FILE__, __FUNCTION__, __LINE__ );
513 
514  return false;
515  }
516 
517  // check if any references are unwritten and swap parents if so
518  if( nullptr != m_RCoords && !m_RCoords->isWritten() )
519  m_RCoords->SwapParent( this );
520 
521  if( nullptr != m_RNormals && !m_RNormals->isWritten() )
522  m_RNormals->SwapParent( this );
523 
524  if( nullptr != m_RColors && !m_RColors->isWritten() )
525  m_RColors->SwapParent( this );
526 
527  aFile << "[" << GetName() << "]";
528  #define NITEMS 7
529  bool items[NITEMS];
530  int i;
531 
532  for( i = 0; i < NITEMS; ++i )
533  items[i] = 0;
534 
535  i = 0;
536  if( nullptr != m_Coords )
537  items[i] = true;
538 
539  ++i;
540  if( nullptr != m_RCoords )
541  items[i] = true;
542 
543  ++i;
544  if( nullptr != m_CoordIndices )
545  items[i] = true;
546 
547  ++i;
548  if( nullptr != m_Normals )
549  items[i] = true;
550 
551  ++i;
552  if( nullptr != m_RNormals )
553  items[i] = true;
554 
555  ++i;
556  if( nullptr != m_Colors )
557  items[i] = true;
558 
559  ++i;
560  if( nullptr != m_RColors )
561  items[i] = true;
562 
563  for( int jj = 0; jj < NITEMS; ++jj )
564  aFile.write( (char*) &items[jj], sizeof( bool ) );
565 
566  if( items[0] )
567  m_Coords->WriteCache( aFile, this );
568 
569  if( items[1] )
570  aFile << "[" << m_RCoords->GetName() << "]";
571 
572  if( items[2] )
573  m_CoordIndices->WriteCache( aFile, this );
574 
575  if( items[3] )
576  m_Normals->WriteCache( aFile, this );
577 
578  if( items[4] )
579  aFile << "[" << m_RNormals->GetName() << "]";
580 
581  if( items[5] )
582  m_Colors->WriteCache( aFile, this );
583 
584  if( items[6] )
585  aFile << "[" << m_RColors->GetName() << "]";
586 
587  if( aFile.fail() )
588  return false;
589 
590  m_written = true;
591  return true;
592 }
593 
594 
595 bool SGFACESET::ReadCache( std::istream& aFile, SGNODE* parentNode )
596 {
598  || m_RNormals )
599  {
600  wxLogTrace( MASK_3D_SG, "%s:%s:%d * [BUG] non-empty node",
601  __FILE__, __FUNCTION__, __LINE__ );
602 
603  return false;
604  }
605 
606  #define NITEMS 7
607  bool items[NITEMS];
608 
609  for( int i = 0; i < NITEMS; ++i )
610  aFile.read( (char*) &items[i], sizeof( bool ) );
611 
612  if( ( items[0] && items[1] ) || ( items[3] && items[4] ) || ( items[5] && items[6] ) )
613  {
614  wxLogTrace( MASK_3D_SG,
615  "%s:%s:%d * [INFO] corrupt data; multiple item definitions at position %d",
616  __FILE__, __FUNCTION__, __LINE__, static_cast<int>( aFile.tellg() ) );
617 
618  return false;
619  }
620 
621  std::string name;
622 
623  if( items[0] )
624  {
625  if( S3D::SGTYPE_COORDS != S3D::ReadTag( aFile, name ) )
626  {
627  wxLogTrace( MASK_3D_SG,
628  "%s:%s:%d * [INFO] corrupt data; bad child coords tag at position %d",
629  __FILE__, __FUNCTION__, __LINE__, static_cast<int>( aFile.tellg() ) );
630 
631  return false;
632  }
633 
634  m_Coords = new SGCOORDS( this );
635  m_Coords->SetName( name.c_str() );
636 
637  if( !m_Coords->ReadCache( aFile, this ) )
638  {
639  wxLogTrace( MASK_3D_SG,
640  "%s:%s:%d * [INFO] corrupt data; corrupt data while reading coords '%s'",
641  __FILE__, __FUNCTION__, __LINE__, name );
642 
643  return false;
644  }
645  }
646 
647  if( items[1] )
648  {
649  if( S3D::SGTYPE_COORDS != S3D::ReadTag( aFile, name ) )
650  {
651  wxLogTrace( MASK_3D_SG,
652  "%s:%s:%d * [INFO] corrupt data; bad ref coords tag at position %d",
653  __FILE__, __FUNCTION__, __LINE__, static_cast<int>( aFile.tellg() ) );
654 
655  return false;
656  }
657 
658  SGNODE* np = FindNode( name.c_str(), this );
659 
660  if( !np )
661  {
662  wxLogTrace( MASK_3D_SG,
663  "%s:%s:%d * [INFO] corrupt data; cannot find ref coords '%s'",
664  __FILE__, __FUNCTION__, __LINE__, name );
665 
666  return false;
667  }
668 
669  if( S3D::SGTYPE_COORDS != np->GetNodeType() )
670  {
671  wxLogTrace( MASK_3D_SG,
672  "%s:%s:%d * [INFO] corrupt data; type is not SGCOORDS '%s'",
673  __FILE__, __FUNCTION__, __LINE__, name );
674 
675  return false;
676  }
677 
678  m_RCoords = (SGCOORDS*)np;
679  m_RCoords->addNodeRef( this );
680  }
681 
682  if( items[2] )
683  {
684  if( S3D::SGTYPE_COORDINDEX != S3D::ReadTag( aFile, name ) )
685  {
686  wxLogTrace( MASK_3D_SG,
687  "%s:%s:%d * [INFO] corrupt data; bad coord index tag at position %d",
688  __FILE__, __FUNCTION__, __LINE__, static_cast<int>( aFile.tellg() ) );
689 
690  return false;
691  }
692 
693  m_CoordIndices = new SGCOORDINDEX( this );
694  m_CoordIndices->SetName( name.c_str() );
695 
696  if( !m_CoordIndices->ReadCache( aFile, this ) )
697  {
698  wxLogTrace( MASK_3D_SG,
699  "%s:%s:%d * [INFO] corrupt data while reading coord index '%s'",
700  __FILE__, __FUNCTION__, __LINE__, name );
701 
702  return false;
703  }
704  }
705 
706  if( items[3] )
707  {
708  if( S3D::SGTYPE_NORMALS != S3D::ReadTag( aFile, name ) )
709  {
710  wxLogTrace( MASK_3D_SG,
711  "%s:%s:%d * [INFO] corrupt data; bad child normals tag at position %d",
712  __FILE__, __FUNCTION__, __LINE__, static_cast<int>( aFile.tellg() ) );
713 
714  return false;
715  }
716 
717  m_Normals = new SGNORMALS( this );
718  m_Normals->SetName( name.c_str() );
719 
720  if( !m_Normals->ReadCache( aFile, this ) )
721  {
722  wxLogTrace( MASK_3D_SG,
723  "%s:%s:%d * [INFO] corrupt data while reading normals '%s'",
724  __FILE__, __FUNCTION__, __LINE__, name );
725 
726  return false;
727  }
728  }
729 
730  if( items[4] )
731  {
732  if( S3D::SGTYPE_NORMALS != S3D::ReadTag( aFile, name ) )
733  {
734  wxLogTrace( MASK_3D_SG,
735  "%s:%s:%d * [INFO] corrupt data; bad ref normals tag at position %d",
736  __FILE__, __FUNCTION__, __LINE__, static_cast<int>( aFile.tellg() ) );
737 
738  return false;
739  }
740 
741  SGNODE* np = FindNode( name.c_str(), this );
742 
743  if( !np )
744  {
745  wxLogTrace( MASK_3D_SG,
746  "%s:%s:%d * [INFO] corrupt: cannot find ref normals '%s'",
747  __FILE__, __FUNCTION__, __LINE__, name );
748 
749  return false;
750  }
751 
752  if( S3D::SGTYPE_NORMALS != np->GetNodeType() )
753  {
754  wxLogTrace( MASK_3D_SG,
755  "%s:%s:%d * [INFO] corrupt: type is not SGNORMALS '%s'",
756  __FILE__, __FUNCTION__, __LINE__, name );
757 
758  return false;
759  }
760 
761  m_RNormals = (SGNORMALS*)np;
762  m_RNormals->addNodeRef( this );
763  }
764 
765  if( items[5] )
766  {
767  if( S3D::SGTYPE_COLORS != S3D::ReadTag( aFile, name ) )
768  {
769  wxLogTrace( MASK_3D_SG,
770  "%s:%s:%d * [INFO] corrupt data; bad child colors tag at position %d",
771  __FILE__, __FUNCTION__, __LINE__, static_cast<int>( aFile.tellg() ) );
772 
773  return false;
774  }
775 
776  m_Colors = new SGCOLORS( this );
777  m_Colors->SetName( name.c_str() );
778 
779  if( !m_Colors->ReadCache( aFile, this ) )
780  {
781  wxLogTrace( MASK_3D_SG,
782  "%s:%s:%d * [INFO] corrupt data while reading colors '%s'",
783  __FILE__, __FUNCTION__, __LINE__, name );
784 
785  return false;
786  }
787  }
788 
789  if( items[6] )
790  {
791  if( S3D::SGTYPE_COLORS != S3D::ReadTag( aFile, name ) )
792  {
793  wxLogTrace( MASK_3D_SG,
794  "%s:%s:%d * [INFO] corrupt data; bad ref colors tag at position %d",
795  __FILE__, __FUNCTION__, __LINE__, static_cast<int>( aFile.tellg() ) );
796 
797  return false;
798  }
799 
800  SGNODE* np = FindNode( name.c_str(), this );
801 
802  if( !np )
803  {
804  wxLogTrace( MASK_3D_SG,
805  "%s:%s:%d * [INFO] corrupt data: cannot find ref colors '%s'",
806  __FILE__, __FUNCTION__, __LINE__, name );
807 
808  return false;
809  }
810 
811  if( S3D::SGTYPE_COLORS != np->GetNodeType() )
812  {
813  wxLogTrace( MASK_3D_SG,
814  "%s:%s:%d * [INFO] corrupt data: type is not SGCOLORS '%s'",
815  __FILE__, __FUNCTION__, __LINE__, name );
816 
817  return false;
818  }
819 
820  m_RColors = (SGCOLORS*)np;
821  m_RColors->addNodeRef( this );
822  }
823 
824  if( aFile.fail() )
825  return false;
826 
827  return true;
828 }
829 
830 
832 {
833  // verify the integrity of this object's data
834  if( validated )
835  return valid;
836 
837  // ensure we have at least coordinates and their normals
838  if( ( nullptr == m_Coords && nullptr == m_RCoords )
839  || ( nullptr == m_Normals && nullptr == m_RNormals )
840  || ( nullptr == m_CoordIndices ) )
841  {
842  wxLogTrace( MASK_3D_SG,
843  "%s:%s:%d * [INFO] bad model; no vertices, vertex indices, or normals",
844  __FILE__, __FUNCTION__, __LINE__ );
845 
846  validated = true;
847  valid = false;
848  return false;
849  }
850 
851  // check that there are >3 vertices
852  SGCOORDS* coords = m_Coords;
853 
854  if( nullptr == coords )
855  coords = m_RCoords;
856 
857  size_t nCoords = 0;
858  SGPOINT* lCoords = nullptr;
859  coords->GetCoordsList( nCoords, lCoords );
860 
861  if( nCoords < 3 )
862  {
863  wxLogTrace( MASK_3D_SG, "%s:%s:%d * [INFO] bad model; fewer than 3 vertices",
864  __FILE__, __FUNCTION__, __LINE__ );
865 
866  validated = true;
867  valid = false;
868  return false;
869  }
870 
871  // check that nVertices is divisible by 3 (facets are triangles)
872  size_t nCIdx = 0;
873  int* lCIdx = nullptr;
874  m_CoordIndices->GetIndices( nCIdx, lCIdx );
875 
876  if( nCIdx < 3 || ( nCIdx % 3 > 0 ) )
877  {
878  wxLogTrace( MASK_3D_SG,
879  "%s:%s:%d * [INFO] bad model; no vertex indices or not multiple of 3",
880  __FILE__, __FUNCTION__, __LINE__ );
881 
882  validated = true;
883  valid = false;
884  return false;
885  }
886 
887  // check that vertex[n] >= 0 and < nVertices
888  for( size_t i = 0; i < nCIdx; ++i )
889  {
890  if( lCIdx[i] < 0 || lCIdx[i] >= (int)nCoords )
891  {
892  wxLogTrace( MASK_3D_SG, "%s:%s:%d * [INFO] bad model; vertex index out of bounds",
893  __FILE__, __FUNCTION__, __LINE__ );
894 
895  validated = true;
896  valid = false;
897  return false;
898  }
899  }
900 
901  // check that there are as many normals as vertices
902  size_t nNorms = 0;
903  SGVECTOR* lNorms = nullptr;
904  SGNORMALS* pNorms = m_Normals;
905 
906  if( nullptr == pNorms )
907  pNorms = m_RNormals;
908 
909  pNorms->GetNormalList( nNorms, lNorms );
910 
911  if( nNorms != nCoords )
912  {
913  wxLogTrace( MASK_3D_SG,
914  "%s:%s:%d * [INFO] bad model; number of normals (%ul) does not match "
915  "number of vertices (%ul)", __FILE__, __FUNCTION__, __LINE__,
916  static_cast<unsigned long>( nNorms ), static_cast<unsigned long>( nCoords ) );
917 
918  validated = true;
919  valid = false;
920  return false;
921  }
922 
923  // if there are colors then ensure there are as many colors as vertices
924  SGCOLORS* pColors = m_Colors;
925 
926  if( nullptr == pColors )
927  pColors = m_RColors;
928 
929  if( nullptr != pColors )
930  {
931  // we must have at least as many colors as vertices
932  size_t nColor = 0;
933  SGCOLOR* pColor = nullptr;
934  pColors->GetColorList( nColor, pColor );
935  }
936 
937  validated = true;
938  valid = true;
939  return true;
940 }
941 
942 
943 void SGFACESET::GatherCoordIndices( std::vector< int >& aIndexList )
944 {
945  if( m_CoordIndices )
946  m_CoordIndices->GatherCoordIndices( aIndexList );
947 }
948 
949 
951 {
952  SGCOORDS* coords = m_Coords;
953 
954  if( m_RCoords )
955  coords = m_RCoords;
956 
957  if( nullptr == coords || coords->coords.empty() )
958  return false;
959 
960  if( m_Normals && !m_Normals->norms.empty( ) )
961  return true;
962 
963  if( m_RNormals && !m_RNormals->norms.empty( ) )
964  return true;
965 
966  return coords->CalcNormals( this, aPtr );
967 }
virtual bool SetParent(SGNODE *aParent, bool notify=true) override
Set the parent SGNODE of this object.
Definition: sg_colors.cpp:57
Define an RGB color set for a scenegraph object.
Definition: sg_colors.h:38
void addNodeRef(SGNODE *aNode)
Add a pointer to a node which references this node, but does not own.
Definition: sg_node.cpp:170
void ReNameNodes(void) override
Rename a node and all its child nodes in preparation for write operations.
Definition: sg_normals.cpp:166
SGCOORDINDEX * m_CoordIndices
Definition: sg_faceset.h:81
void ReNameNodes(void) override
Rename a node and all its child nodes in preparation for write operations.
Definition: sg_coords.cpp:168
bool WriteCache(std::ostream &aFile, SGNODE *parentNode) override
Write this node's data to a binary cache file.
Definition: sg_normals.cpp:232
bool CalcNormals(SGNODE **aPtr)
Definition: sg_faceset.cpp:950
virtual ~SGFACESET()
Definition: sg_faceset.cpp:65
SGNODE * m_Parent
Pointer to parent node; may be NULL for top level transform.
Definition: sg_node.h:227
An object to maintain a coordinate index list.
Definition: sg_coordindex.h:42
SGFACESET(SGNODE *aParent)
Definition: sg_faceset.cpp:38
bool validated
Definition: sg_faceset.h:91
virtual bool SetParent(SGNODE *aParent, bool notify=true) override
Set the parent SGNODE of this object.
Definition: sg_index.cpp:50
virtual bool SetParent(SGNODE *aParent, bool notify=true) override
Set the parent SGNODE of this object.
Definition: sg_normals.cpp:57
bool WriteVRML(std::ostream &aFile, bool aReuseFlag) override
Writes this node's data to a VRML file.
Definition: sg_coords.cpp:178
bool GetColorList(size_t &aListSize, SGCOLOR *&aColorList)
Definition: sg_colors.cpp:127
SGNODE * FindNode(const char *aNodeName, const SGNODE *aCaller) noexcept override
Search the tree of linked nodes and return a reference to the first node found with the given name.
Definition: sg_coords.cpp:89
bool ReadCache(std::istream &aFile, SGNODE *parentNode) override
Reads binary format data from a cache file.
Definition: sg_colors.cpp:282
bool ReadCache(std::istream &aFile, SGNODE *parentNode) override
Reads binary format data from a cache file.
Definition: sg_coords.cpp:284
bool WriteVRML(std::ostream &aFile, bool aReuseFlag) override
Writes this node's data to a VRML file.
Definition: sg_index.cpp:169
SGCOLORS * m_Colors
Definition: sg_faceset.h:79
Define a vertex coordinate set for a scenegraph object.
Definition: sg_coords.h:40
bool WriteCache(std::ostream &aFile, SGNODE *parentNode) override
Write this node's data to a binary cache file.
Definition: sg_faceset.cpp:488
bool GetCoordsList(size_t &aListSize, SGPOINT *&aCoordsList)
Definition: sg_coords.cpp:129
The base class of all Scene Graph nodes.
Definition: sg_node.h:74
bool valid
Definition: sg_faceset.h:90
void delNodeRef(const SGNODE *aNode)
Remove a pointer to a node which references this node, but does not own.
Definition: sg_node.cpp:185
bool ReadCache(std::istream &aFile, SGNODE *parentNode) override
Reads binary format data from a cache file.
Definition: sg_normals.cpp:277
bool WriteCache(std::ostream &aFile, SGNODE *parentNode) override
Write this node's data to a binary cache file.
Definition: sg_index.cpp:260
SGCOLORS * m_RColors
Definition: sg_faceset.h:85
bool ReadCache(std::istream &aFile, SGNODE *parentNode) override
Reads binary format data from a cache file.
Definition: sg_faceset.cpp:595
void ReNameNodes(void) override
Rename a node and all its child nodes in preparation for write operations.
Definition: sg_index.cpp:159
Define a set of vertex normals for a scene graph object.
Definition: sg_normals.h:38
bool WriteVRML(std::ostream &aFile, bool aReuseFlag) override
Writes this node's data to a VRML file.
Definition: sg_normals.cpp:176
void unlinkNode(const SGNODE *aNode, bool isChild)
Definition: sg_faceset.cpp:197
void ReNameNodes(void) override
Rename a node and all its child nodes in preparation for write operations.
Definition: sg_faceset.cpp:408
void ReNameNodes(void) override
Rename a node and all its child nodes in preparation for write operations.
Definition: sg_colors.cpp:170
SGNODE * FindNode(const char *aNodeName, const SGNODE *aCaller) override
Search the tree of linked nodes and return a reference to the first node found with the given name.
Definition: sg_faceset.cpp:147
bool CalcNormals(SGFACESET *callingNode, SGNODE **aPtr=nullptr)
Calculate normals for this coordinate list and sets the normals list in the parent SGFACESET.
Definition: sg_coords.cpp:307
const char * GetName(void)
Definition: sg_node.cpp:146
SGCOORDS * m_RCoords
Definition: sg_faceset.h:86
bool m_written
Set to true when the object has been written after a ReNameNodes().
Definition: sg_node.h:230
S3D::SGTYPES ReadTag(std::istream &aFile, std::string &aName)
Read the text tag of a binary cache file which is the NodeTag and unique ID number combined.
Definition: sg_helpers.cpp:195
virtual void unlinkChildNode(const SGNODE *aNode)=0
Remove references to an owned child.
bool GetIndices(size_t &nIndices, int *&aIndexList)
Retrieve the number of indices and a pointer to the list.
Definition: sg_index.cpp:124
bool WriteVRML(std::ostream &aFile, bool aReuseFlag) override
Writes this node's data to a VRML file.
Definition: sg_colors.cpp:180
bool SwapParent(SGNODE *aNewParent)
Swap the ownership with the given parent.
Definition: sg_node.cpp:116
std::vector< SGVECTOR > norms
Definition: sg_normals.h:64
bool validate(void)
Definition: sg_faceset.cpp:831
bool AddChildNode(SGNODE *aNode) override
Definition: sg_faceset.cpp:402
virtual bool SetParent(SGNODE *aParent, bool notify=true) override
Set the parent SGNODE of this object.
Definition: sg_coords.cpp:59
void GatherCoordIndices(std::vector< int > &aIndexList)
Add all coordinate indices to the given list in preparation for a normals calculation.
virtual SGNODE * FindNode(const char *aNodeName, const SGNODE *aCaller)=0
Search the tree of linked nodes and return a reference to the first node found with the given name.
SGNODE * GetParent(void) const noexcept
Returns a pointer to the parent SGNODE of this object or NULL if the object has no parent (ie.
Definition: sg_node.cpp:110
void SetName(const char *aName)
Definition: sg_node.cpp:155
std::string m_Name
name to use for referencing the entity by name.
Definition: sg_node.h:229
bool AddRefNode(SGNODE *aNode) override
Definition: sg_faceset.cpp:396
virtual bool SetParent(SGNODE *aParent, bool notify=true) override
Set the parent SGNODE of this object.
Definition: sg_faceset.cpp:117
virtual bool WriteCache(std::ostream &aFile, SGNODE *parentNode)=0
Write this node's data to a binary cache file.
SGNODE * FindNode(const char *aNodeName, const SGNODE *aCaller) noexcept override
Search the tree of linked nodes and return a reference to the first node found with the given name.
Definition: sg_colors.cpp:87
bool addNode(SGNODE *aNode, bool isChild)
Definition: sg_faceset.cpp:273
S3D::SGTYPES m_SGtype
Type of Scene Graph node.
Definition: sg_node.h:228
SGCOORDS * m_Coords
Definition: sg_faceset.h:80
void unlinkChildNode(const SGNODE *aNode) override
Remove references to an owned child.
Definition: sg_faceset.cpp:260
bool WriteCache(std::ostream &aFile, SGNODE *parentNode) override
Write this node's data to a binary cache file.
Definition: sg_colors.cpp:238
bool ReadCache(std::istream &aFile, SGNODE *parentNode) override
Reads binary format data from a cache file.
Definition: sg_index.cpp:305
const char * name
Definition: DXF_plotter.cpp:59
void GatherCoordIndices(std::vector< int > &aIndexList)
Add all internal coordinate indices to the given list in preparation for a normals calculation.
Definition: sg_faceset.cpp:943
std::vector< SGPOINT > coords
Definition: sg_coords.h:72
bool WriteVRML(std::ostream &aFile, bool aReuseFlag) override
Writes this node's data to a VRML file.
Definition: sg_faceset.cpp:433
bool isWritten(void) noexcept
Return true if the object had already been written to a cache file or VRML file.
Definition: sg_node.h:220
virtual bool AddChildNode(SGNODE *aNode)=0
void unlinkRefNode(const SGNODE *aNode) override
Remove pointers to a referenced node.
Definition: sg_faceset.cpp:266
SGNODE * FindNode(const char *aNodeName, const SGNODE *aCaller) noexcept override
Search the tree of linked nodes and return a reference to the first node found with the given name.
Definition: sg_index.cpp:80
SGNORMALS * m_Normals
Definition: sg_faceset.h:82
Define a number of macros to aid in repetitious code which is probably best expressed as a preprocess...
#define NITEMS
SGNORMALS * m_RNormals
Definition: sg_faceset.h:87
bool GetNormalList(size_t &aListSize, SGVECTOR *&aNormalList)
Definition: sg_normals.cpp:127
S3D::SGTYPES GetNodeType(void) const noexcept
Return the type of this node instance.
Definition: sg_node.cpp:104
SGNODE * FindNode(const char *aNodeName, const SGNODE *aCaller) noexcept override
Search the tree of linked nodes and return a reference to the first node found with the given name.
Definition: sg_normals.cpp:87
bool WriteCache(std::ostream &aFile, SGNODE *parentNode) override
Write this node's data to a binary cache file.
Definition: sg_coords.cpp:240