1 /* $NoKeywords: $ */
2 /*
3 //
4 // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
5 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
6 // McNeel & Associates.
7 //
8 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
9 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
10 // MERCHANTABILITY ARE HEREBY DISCLAIMED.
11 //
12 // For complete openNURBS copyright information see <http://www.opennurbs.org>.
13 //
14 ////////////////////////////////////////////////////////////////
15 */
16 #include "pcl/surface/3rdparty/opennurbs/opennurbs.h"
17 
18 
19 #if defined(ON_COMPILER_MSC)
20 // Disable the MSC /W4 warning C4127: conditional expression is constant
21 //
22 // This file has a lot of for( i = 0; true; i < 123 )...
23 // loops where the "true" generates a 4127 warning.
24 // This source code has to work on many different
25 // compilers I do not trust all of them to correctly
26 // compile for( i = 0; /* empty condition*/; i < 123) ...
27 #pragma warning( push )
28 #pragma warning( disable : 4127 )
29 #endif
30 
31 
32 #if defined(ON_DLL_EXPORTS)
33 
34 ////////////////////////////////////////////////////////////////////////////////
35 //
36 // When openNURBS is used as a Microsoft Windows DLL, it is possible
37 // for new/delete to allocate memory in one executable and delete
38 // it in another.  Because Microsoft Windows has incompatible memory
39 // managers in its plethora of C libraries and the choice of which
40 // C library actually gets used depends on the code generation
41 // options you choose,  we get lots of support questions asking
42 // about hard to trace crashes.
43 //
44 // If you are using openNURBS as a Windows DLL, you are sure you know
45 // what you are doing, and you promise never to ask for support, then
46 // feel free to delete these overrides.
47 //
48 //
49 #pragma message( " --- OpenNURBS overriding ONX_Model new and delete" )
50 
51 // ONX_Model_UserData new/delete
52 
operator new(std::size_t sz)53 void* ONX_Model_UserData::operator new(std::size_t sz)
54 {
55   // ONX_Model_UserData new
56   return onmalloc(sz);
57 }
58 
operator delete(void * p)59 void ONX_Model_UserData::operator delete(void* p)
60 {
61   // ONX_Model_UserData delete
62   onfree(p);
63 }
64 
operator new[](std::size_t sz)65 void* ONX_Model_UserData::operator new[] (std::size_t sz)
66 {
67   // ONX_Model_UserData array new
68   return onmalloc(sz);
69 }
70 
operator delete[](void * p)71 void ONX_Model_UserData::operator delete[] (void* p)
72 {
73   // ONX_Model_UserData array delete
74   onfree(p);
75 }
76 
operator new(std::size_t,void * p)77 void* ONX_Model_UserData::operator new(std::size_t, void* p)
78 {
79   // ONX_Model_UserData placement new
80   return p;
81 }
82 
operator delete(void *,void *)83 void ONX_Model_UserData::operator delete(void*, void*)
84 {
85   // ONX_Model_UserData placement delete
86   return;
87 }
88 
89 
90 // ONX_Model_Object new/delete
91 
operator new(std::size_t sz)92 void* ONX_Model_Object::operator new(std::size_t sz)
93 {
94   // ONX_Model_Object new
95   return onmalloc(sz);
96 }
97 
operator delete(void * p)98 void ONX_Model_Object::operator delete(void* p)
99 {
100   // ONX_Model_Object delete
101   onfree(p);
102 }
103 
operator new[](std::size_t sz)104 void* ONX_Model_Object::operator new[] (std::size_t sz)
105 {
106   // ONX_Model_Object array new
107   return onmalloc(sz);
108 }
109 
operator delete[](void * p)110 void ONX_Model_Object::operator delete[] (void* p)
111 {
112   // ONX_Model_Object array delete
113   onfree(p);
114 }
115 
operator new(std::size_t,void * p)116 void* ONX_Model_Object::operator new(std::size_t, void* p)
117 {
118   // ONX_Model_Object placement new
119   return p;
120 }
121 
operator delete(void *,void *)122 void ONX_Model_Object::operator delete(void*, void*)
123 {
124   // ONX_Model_Object placement delete
125   return;
126 }
127 
128 
129 // ONX_Model_RenderLight new/delete
130 
operator new(std::size_t sz)131 void* ONX_Model_RenderLight::operator new(std::size_t sz)
132 {
133   // ONX_Model_RenderLight new
134   return onmalloc(sz);
135 }
136 
operator delete(void * p)137 void ONX_Model_RenderLight::operator delete(void* p)
138 {
139   // ONX_Model_RenderLight delete
140   onfree(p);
141 }
142 
operator new[](std::size_t sz)143 void* ONX_Model_RenderLight::operator new[] (std::size_t sz)
144 {
145   // ONX_Model_RenderLight array new
146   return onmalloc(sz);
147 }
148 
operator delete[](void * p)149 void ONX_Model_RenderLight::operator delete[] (void* p)
150 {
151   // ONX_Model_RenderLight array delete
152   onfree(p);
153 }
154 
operator new(std::size_t,void * p)155 void* ONX_Model_RenderLight::operator new(std::size_t, void* p)
156 {
157   // ONX_Model_RenderLight placement new
158   return p;
159 }
160 
operator delete(void *,void *)161 void ONX_Model_RenderLight::operator delete(void*, void*)
162 {
163   // ONX_Model_RenderLight placement delete
164   return;
165 }
166 
167 
168 // ONX_Model new/delete
169 
operator new(std::size_t sz)170 void* ONX_Model::operator new(std::size_t sz)
171 {
172   // ONX_Model new
173   return onmalloc(sz);
174 }
175 
operator delete(void * p)176 void ONX_Model::operator delete(void* p)
177 {
178   // ONX_Model delete
179   onfree(p);
180 }
181 
operator new[](std::size_t sz)182 void* ONX_Model::operator new[] (std::size_t sz)
183 {
184   // ONX_Model array new
185   return onmalloc(sz);
186 }
187 
operator delete[](void * p)188 void ONX_Model::operator delete[] (void* p)
189 {
190   // ONX_Model array delete
191   onfree(p);
192 }
193 
operator new(std::size_t,void * p)194 void* ONX_Model::operator new(std::size_t, void* p)
195 {
196   // ONX_Model placement new
197   return p;
198 }
199 
operator delete(void *,void *)200 void ONX_Model::operator delete(void*, void*)
201 {
202   // ONX_Model placement delete
203   return;
204 }
205 
206 #endif
207 
208 //
209 //
210 ////////////////////////////////////////////////////////////////////////////////
211 
212 
213 static
ONX_IsValidNameFirstChar(wchar_t c)214 bool ONX_IsValidNameFirstChar( wchar_t c )
215 {
216   if ( c > 127 )
217     return true;
218   if ( c < '0' )
219     return false;
220   if ( c <= '9' )
221     return true;
222   if ( c < 'A' )
223     return false;
224   if ( c <= 'Z' )
225     return true;
226   if ( c == '_' )
227     return true;
228   if ( c < 'a' )
229     return false;
230   if ( c <= 'z' )
231     return true;
232   return false;
233 }
234 
235 static
ONX_IsValidNameSecondChar(wchar_t c)236 bool ONX_IsValidNameSecondChar( wchar_t c )
237 {
238   // control characters, double quote, and DEL are
239   // not permited in names
240   return (c >= 32 && c != 34 && c != 127);
241 }
242 
ONX_IsValidName(const wchar_t * name)243 bool ONX_IsValidName(
244           const wchar_t* name
245           )
246 {
247   bool is_valid = (0 != name && ONX_IsValidNameFirstChar(*name));
248   if ( is_valid )
249   {
250     bool is_integer = (*name >= '0' && *name <= '9');
251     name++;
252     while ( ONX_IsValidNameSecondChar(*name) )
253     {
254       if ( *name < '0' || *name >= '9' )
255         is_integer = false;
256       name++;
257     }
258     if ( *name || is_integer )
259       is_valid = false;
260     else if ( name[-1] <= 32 )
261       is_valid = false; // last character cannot be a space
262   }
263   return is_valid;
264 }
265 
266 ////////////////////////////////////////////////////////////////////////
267 ////////////////////////////////////////////////////////////////////////
268 
ONX_Model_RenderLight()269 ONX_Model_RenderLight::ONX_Model_RenderLight()
270 {
271 }
272 
~ONX_Model_RenderLight()273 ONX_Model_RenderLight::~ONX_Model_RenderLight()
274 {
275 }
276 
ONX_Model_RenderLight(const ONX_Model_RenderLight & src)277 ONX_Model_RenderLight::ONX_Model_RenderLight(const ONX_Model_RenderLight& src)
278              : m_light(src.m_light),
279                m_attributes(src.m_attributes)
280 {
281 }
282 
operator =(const ONX_Model_RenderLight & src)283 ONX_Model_RenderLight& ONX_Model_RenderLight::operator=(const ONX_Model_RenderLight& src)
284 {
285   if ( this != &src )
286   {
287     m_light = src.m_light;
288     m_attributes = src.m_attributes;
289   }
290   return *this;
291 }
292 
293 ////////////////////////////////////////////////////////////////////////
294 ////////////////////////////////////////////////////////////////////////
295 
296 
ONX_Model_Object()297 ONX_Model_Object::ONX_Model_Object()
298                  : m_bDeleteObject(0),
299                    m_object(0),
300                    m_ref_count(0)
301 {
302 }
303 
Destroy()304 void ONX_Model_Object::Destroy()
305 {
306   if ( m_ref_count )
307   {
308     if ( *m_ref_count > 0 )
309       (*m_ref_count)--;
310     if ( *m_ref_count <= 0 )
311     {
312       delete m_ref_count;
313       m_ref_count = 0;
314     }
315   }
316   if ( 0 == m_ref_count && 0 != m_object && m_bDeleteObject )
317   {
318     delete m_object;
319   }
320   m_object = 0;
321   m_bDeleteObject = false;
322 }
323 
~ONX_Model_Object()324 ONX_Model_Object::~ONX_Model_Object()
325 {
326   Destroy();
327 }
328 
ONX_Model_Object(const ONX_Model_Object & src)329 ONX_Model_Object::ONX_Model_Object(const ONX_Model_Object& src)
330              : m_bDeleteObject(0),
331                m_object(0),
332                m_ref_count(0)
333 {
334   *this = src;
335 }
336 
operator =(const ONX_Model_Object & src)337 ONX_Model_Object& ONX_Model_Object::operator=(const ONX_Model_Object& src)
338 {
339   if ( this != &src )
340   {
341     Destroy();
342     m_bDeleteObject = src.m_bDeleteObject;
343     m_object = src.m_object;
344     m_attributes = src.m_attributes;
345     m_ref_count = src.m_ref_count;
346     if ( 0 != m_object && m_bDeleteObject )
347     {
348       if ( 0 != m_ref_count )
349         (*m_ref_count)++;
350       else
351       {
352         m_ref_count = new unsigned int;
353         *m_ref_count = 2; // 2 because this and src reference same m_object
354         const_cast<ONX_Model_Object*>(&src)->m_ref_count = m_ref_count; // need to defeat const
355       }
356     }
357   }
358   return *this;
359 }
360 
361 ////////////////////////////////////////////////////////////////////////
362 ////////////////////////////////////////////////////////////////////////
363 
364 
ONX_Model_UserData()365 ONX_Model_UserData::ONX_Model_UserData()
366 : m_uuid(ON_nil_uuid)
367 , m_usertable_3dm_version(0)
368 , m_usertable_opennurbs_version(0)
369 {
370 }
371 
~ONX_Model_UserData()372 ONX_Model_UserData::~ONX_Model_UserData()
373 {
374 }
375 
ONX_Model_UserData(const ONX_Model_UserData & src)376 ONX_Model_UserData::ONX_Model_UserData(const ONX_Model_UserData& src)
377 : m_uuid(src.m_uuid)
378 , m_goo(src.m_goo)
379 , m_usertable_3dm_version(src.m_usertable_3dm_version)
380 , m_usertable_opennurbs_version(src.m_usertable_opennurbs_version)
381 {
382 }
383 
operator =(const ONX_Model_UserData & src)384 ONX_Model_UserData& ONX_Model_UserData::operator=(const ONX_Model_UserData& src)
385 {
386   if ( this != &src )
387   {
388     m_uuid = src.m_uuid;
389     m_goo = src.m_goo;
390     m_usertable_3dm_version = src.m_usertable_3dm_version;
391     m_usertable_opennurbs_version = src.m_usertable_opennurbs_version;
392   }
393   return *this;
394 }
395 
396 ////////////////////////////////////////////////////////////////////////
397 ////////////////////////////////////////////////////////////////////////
398 
ONX_Model()399 ONX_Model::ONX_Model()
400           : m_3dm_file_version(0),
401             m_3dm_opennurbs_version(0),
402             m_file_length(0),
403             m_crc_error_count(0)
404 {
405   m_sStartSectionComments.Empty();
406   m_properties.Default();
407   m_settings.Default();
408 }
409 
~ONX_Model()410 ONX_Model::~ONX_Model()
411 {
412   Destroy();
413 }
414 
Destroy()415 void ONX_Model::Destroy()
416 {
417   int i;
418   m_3dm_file_version = 0;
419   m_3dm_opennurbs_version = 0;
420   m_sStartSectionComments.Empty();
421   m_properties.Default();
422   m_settings.Default();
423 
424   for ( i = 0; i < m_history_record_table.Count(); i++ )
425     delete m_history_record_table[i];
426   m_history_record_table.Zero();
427 
428   for ( i = 0; i < m_bitmap_table.Count(); i++ )
429     delete m_bitmap_table[i];
430   m_bitmap_table.Zero();
431 
432   m_bitmap_table.SetCapacity(0);
433   m_mapping_table.SetCapacity(0);
434   m_material_table.SetCapacity(0);
435   m_linetype_table.SetCapacity(0);
436   m_layer_table.SetCapacity(0);
437   m_group_table.SetCapacity(0);
438   m_font_table.SetCapacity(0);
439   m_dimstyle_table.SetCapacity(0);
440   m_light_table.SetCapacity(0);
441   m_hatch_pattern_table.SetCapacity(0);
442   m_idef_table.SetCapacity(0);
443   m_object_table.SetCapacity(0);
444   m_history_record_table.SetCapacity(0);
445   m_userdata_table.SetCapacity(0);
446 
447   m_file_length = 0;
448   m_crc_error_count = 0;
449 
450   DestroyCache();
451 }
452 
453 
DestroyCache()454 void ONX_Model::DestroyCache()
455 {
456   m_mapping_id_index.Empty();
457   m_material_id_index.Empty();
458   m_idef_id_index.Empty();
459   m_object_id_index.Empty();
460 
461   m__object_table_bbox.Destroy();
462 }
463 
BoundingBox() const464 ON_BoundingBox ONX_Model::BoundingBox() const
465 {
466   if( !m__object_table_bbox.IsValid() && m_object_table.Count() > 0 )
467   {
468     ON_BoundingBox bbox;
469     int i, object_count = m_object_table.Count();
470     for ( i = 0; i < object_count; i++ )
471     {
472       const ON_Geometry* geo = ON_Geometry::Cast(m_object_table[i].m_object);
473       if ( geo )
474         bbox.Union(geo->BoundingBox());
475       const_cast<ONX_Model*>(this)->m__object_table_bbox = bbox;
476     }
477   }
478   return m__object_table_bbox;
479 }
480 
GetRenderMaterial(const ON_3dmObjectAttributes & attributes,ON_Material & material) const481 void ONX_Model::GetRenderMaterial( const ON_3dmObjectAttributes& attributes, ON_Material& material ) const
482 {
483   int material_index = -1;
484 
485   switch ( attributes.MaterialSource() )
486   {
487   case ON::material_from_layer:
488     if ( attributes.m_layer_index >= 0 && attributes.m_layer_index < m_layer_table.Count() )
489       material_index = m_layer_table[attributes.m_layer_index].RenderMaterialIndex();
490     break;
491   case ON::material_from_object:
492     material_index = attributes.m_material_index;
493     break;
494 
495   case ON::material_from_parent:
496     material_index = attributes.m_material_index;
497     // TODO: If object is an idef, get material from iref attributes.
498     break;
499   }
500 
501   if ( material_index < 0 || material_index >= m_material_table.Count() )
502   {
503     material_index = -1;
504     material.Default();
505   }
506   else
507   {
508     material = m_material_table[material_index];
509   }
510 
511   material.SetMaterialIndex(material_index);
512 }
513 
GetRenderMaterial(int object_index,ON_Material & material) const514 void ONX_Model::GetRenderMaterial(
515       int object_index,
516       ON_Material& material
517       ) const
518 {
519   if ( object_index < 0 || object_index >= m_object_table.Count() )
520   {
521     material.Default();
522     material.SetMaterialIndex(-1);
523   }
524   else
525     GetRenderMaterial( m_object_table[object_index].m_attributes, material );
526 }
527 
GetLinetype(const ON_3dmObjectAttributes & attributes,ON_Linetype & linetype) const528 void ONX_Model::GetLinetype( const ON_3dmObjectAttributes& attributes, ON_Linetype& linetype ) const
529 {
530   int linetype_index = -1;
531 
532   switch ( attributes.LinetypeSource() )
533   {
534   case ON::linetype_from_layer:
535     if ( attributes.m_layer_index >= 0 && attributes.m_layer_index < m_layer_table.Count() )
536       linetype_index = m_layer_table[attributes.m_layer_index].LinetypeIndex();
537     break;
538   case ON::linetype_from_object:
539     linetype_index = attributes.m_linetype_index;
540     break;
541   case ON::linetype_from_parent:
542     linetype_index = attributes.m_linetype_index;
543     // TODO: if object is an instance definition, get linetype
544     //       from instance references.
545     break;
546   }
547 
548   if ( linetype_index < 0 || linetype_index >= m_linetype_table.Count() )
549   {
550     linetype_index = -1;
551     linetype.Default();
552   }
553   else
554   {
555     linetype = m_linetype_table[linetype_index];
556   }
557 
558   linetype.SetLinetypeIndex(linetype_index);
559 }
560 
GetLinetype(int object_index,ON_Linetype & linetype) const561 void ONX_Model::GetLinetype(
562       int object_index,
563       ON_Linetype& linetype
564       ) const
565 {
566   if ( object_index < 0 || object_index >= m_object_table.Count() )
567   {
568     linetype.Default();
569     linetype.SetLinetypeIndex(-1);
570   }
571   else
572   {
573     GetLinetype( m_object_table[object_index].m_attributes, linetype );
574   }
575 }
576 
577 
578 
WireframeColor(const ON_3dmObjectAttributes & attributes) const579 ON_Color ONX_Model::WireframeColor( const ON_3dmObjectAttributes& attributes ) const
580 {
581   ON_Color color = ON_UNSET_COLOR;
582 
583   switch ( attributes.ColorSource() )
584   {
585   case ON::color_from_layer:
586     if ( attributes.m_layer_index >= 0 && attributes.m_layer_index < m_layer_table.Count() )
587       color = m_layer_table[attributes.m_layer_index].Color();
588     break;
589 
590   case ON::color_from_object:
591     color = attributes.m_color;
592     break;
593 
594   case ON::color_from_material:
595     {
596       ON_Material mat;
597       GetRenderMaterial( attributes, mat );
598       color = mat.Diffuse();
599     }
600     break;
601 
602   case ON::color_from_parent:
603     color = attributes.m_color;
604     // TODO: if object is an instance definition, get color
605     //       from instance references.
606     break;
607   }
608 
609   if ( color == ON_UNSET_COLOR )
610     color.SetRGB(128,128,128);
611 
612   return color;
613 }
614 
WireframeColor(int object_index) const615 ON_Color ONX_Model::WireframeColor(int object_index) const
616 {
617   ON_Color c;
618   if ( object_index < 0 || object_index >= m_object_table.Count() )
619   {
620     ON_3dmObjectAttributes a;
621     c = a.m_color;
622   }
623   else
624     c = WireframeColor( m_object_table[object_index].m_attributes );
625   return c;
626 }
627 
628 
ONX_DumpView(ON_TextLog & dump,const ON_3dmView & view)629 void ONX_DumpView( ON_TextLog& dump, const ON_3dmView& view )
630 {
631   view.Dump(dump);
632 }
633 
DumpSummary(ON_TextLog & dump) const634 void ONX_Model::DumpSummary( ON_TextLog& dump ) const
635 {
636   dump.Print("File version: %d\n",m_3dm_file_version);
637   dump.Print("File openNURBS version: %d\n",m_3dm_opennurbs_version);
638   if ( m_file_length > 0 )
639     dump.Print("File length: %d bytes\n",m_file_length);
640 
641   if ( m_sStartSectionComments.Length() > 0 )
642   {
643     dump.Print("Start section comments:\n");
644     dump.PushIndent();
645     dump.PrintWrappedText(m_sStartSectionComments);
646     dump.PopIndent();
647     dump.Print("\n");
648   }
649 
650   m_properties.Dump(dump);
651 
652   dump.Print("\n");
653 
654   m_settings.Dump(dump);
655 
656   dump.Print("\n");
657 
658   dump.Print("Contents:\n");
659   dump.PushIndent();
660   dump.Print("%d embedded bitmaps\n",m_bitmap_table.Count());
661   dump.Print("%d render material definitions\n",m_material_table.Count());
662   dump.Print("%d line type definitions\n",m_linetype_table.Count());
663   dump.Print("%d layers\n",m_layer_table.Count());
664   dump.Print("%d render lights\n",m_light_table.Count());
665   dump.Print("%d groups\n",m_group_table.Count());
666   dump.Print("%d objects\n",m_object_table.Count());
667   dump.Print("%d user data objects\n",m_userdata_table.Count());
668   dump.PopIndent();
669 }
670 
DumpBitmapTable(ON_TextLog & dump) const671 void ONX_Model::DumpBitmapTable( ON_TextLog& dump) const
672 {
673   int i;
674   for ( i = 0; i < m_bitmap_table.Count(); i++ )
675   {
676     dump.Print("Bitmap %d:\n",i);
677     dump.PushIndent();
678     m_bitmap_table[i]->Dump(dump);
679     dump.PopIndent();
680   }
681 }
682 
DumpTextureMappingTable(ON_TextLog & dump) const683 void ONX_Model::DumpTextureMappingTable( ON_TextLog& dump) const
684 {
685   int i;
686   for ( i = 0; i < m_mapping_table.Count(); i++ )
687   {
688     dump.Print("Texture Mapping %d:\n",i);
689     dump.PushIndent();
690     m_mapping_table[i].Dump(dump);
691     dump.PopIndent();
692   }
693 }
694 
DumpMaterialTable(ON_TextLog & dump) const695 void ONX_Model::DumpMaterialTable( ON_TextLog& dump) const
696 {
697   int i;
698   for ( i = 0; i < m_material_table.Count(); i++ )
699   {
700     dump.Print("Material %d:\n",i);
701     dump.PushIndent();
702     m_material_table[i].Dump(dump);
703     dump.PopIndent();
704   }
705 }
706 
DumpLinetypeTable(ON_TextLog & dump) const707 void ONX_Model::DumpLinetypeTable( ON_TextLog& dump ) const
708 {
709   int i;
710   for ( i = 0; i < m_linetype_table.Count(); i++ )
711   {
712     dump.Print("Linetype %d:\n",i);
713     dump.PushIndent();
714     m_linetype_table[i].Dump(dump);
715     dump.PopIndent();
716   }
717 }
718 
DumpLayerTable(ON_TextLog & dump) const719 void ONX_Model::DumpLayerTable( ON_TextLog& dump) const
720 {
721   int i;
722   for ( i = 0; i < m_layer_table.Count(); i++ )
723   {
724     dump.Print("Layer %d:\n",i);
725     dump.PushIndent();
726     m_layer_table[i].Dump(dump);
727     dump.PopIndent();
728   }
729 }
730 
DumpLightTable(ON_TextLog & dump) const731 void ONX_Model::DumpLightTable( ON_TextLog& dump) const
732 {
733   int i;
734   for ( i = 0; i < m_light_table.Count(); i++ )
735   {
736     dump.Print("Light %d:\n",i);
737     dump.PushIndent();
738     m_light_table[i].m_attributes.Dump(dump);
739     m_light_table[i].m_light.Dump(dump);
740     dump.PopIndent();
741   }
742 }
743 
DumpGroupTable(ON_TextLog & dump) const744 void ONX_Model::DumpGroupTable( ON_TextLog& dump) const
745 {
746   int i;
747   for ( i = 0; i < m_group_table.Count(); i++ )
748   {
749     dump.Print("Group %d:\n",i);
750     dump.PushIndent();
751     m_group_table[i].Dump(dump);
752     dump.PopIndent();
753   }
754 }
755 
DumpFontTable(ON_TextLog & dump) const756 void ONX_Model::DumpFontTable( ON_TextLog& dump) const
757 {
758   int i;
759   for ( i = 0; i < m_font_table.Count(); i++ )
760   {
761     dump.Print("Font %d:\n",i);
762     dump.PushIndent();
763     m_font_table[i].Dump(dump);
764     dump.PopIndent();
765   }
766 }
767 
DumpDimStyleTable(ON_TextLog & dump) const768 void ONX_Model::DumpDimStyleTable( ON_TextLog& dump) const
769 {
770   int i;
771   for ( i = 0; i < m_dimstyle_table.Count(); i++ )
772   {
773     dump.Print("DimStyle %d:\n",i);
774     dump.PushIndent();
775     m_dimstyle_table[i].Dump(dump);
776     dump.PopIndent();
777   }
778 }
779 
780 
DumpHatchPatternTable(ON_TextLog & dump) const781 void ONX_Model::DumpHatchPatternTable( ON_TextLog& dump) const
782 {
783   int i;
784   for ( i = 0; i < m_hatch_pattern_table.Count(); i++ )
785   {
786     dump.Print("HatchPattern %d:\n",i);
787     dump.PushIndent();
788     m_hatch_pattern_table[i].Dump(dump);
789     dump.PopIndent();
790   }
791 }
792 
DumpIDefTable(ON_TextLog & dump) const793 void ONX_Model::DumpIDefTable( ON_TextLog& dump) const
794 {
795   int i;
796   for ( i = 0; i < m_idef_table.Count(); i++ )
797   {
798     dump.Print("Instance Definition %d:\n",i);
799     dump.PushIndent();
800     m_idef_table[i].Dump(dump);
801     dump.PopIndent();
802   }
803 }
804 
Dump(ON_TextLog & dump) const805 void ONX_Model_Object::Dump( ON_TextLog& dump ) const
806 {
807   if ( 0 != m_object )
808   {
809     m_object->Dump(dump);
810 
811     // user data attached to this object
812     const ON_UserData* ud = m_object->FirstUserData();
813     while(0 != ud)
814     {
815       dump.Print("object user data:\n");
816       dump.PushIndent();
817       ud->Dump(dump);
818       dump.PopIndent();
819       ud = ud->Next();
820     }
821   }
822   else
823   {
824     dump.Print("NULL m_object pointer\n");
825   }
826 
827   // Use Cast() if you need to get at the details.
828   /*
829   const ON_Geometry* pGeometry = ON_Geometry::Cast(m_object);
830   if ( pGeometry )
831   {
832     // m_object is some type of geometric object
833     if ( ON_Extrusion::Cast(m_object) )
834     {
835       // m_object is derived from ON_Extrusion
836       //  Note:
837       //   ON_Extrusion::BrepForm() will return a brep form
838       //   if you don't want to explicitly handle extrusions.
839       const ON_Extrusion* extrusion = ON_Extrusion::Cast(m_object);
840     }
841     else if ( ON_Brep::Cast(m_object) )
842     {
843       // m_object is derived from ON_Brep
844       const ON_Brep* brep = ON_Brep::Cast(m_object);
845     }
846     else if ( pGeometry->HasBrepForm() )
847     {
848       // m_object is note derived from ON_Brep but its geometry can
849       // be represented by an ON_Brep.
850       ON_Brep* brep = pGeometry->BrepForm();
851       // you manage the ON_Brep returned by pGeometry->BrepForm();
852       delete brep;
853     }
854     else if ( ON_Curve::Cast(m_object) )
855     {
856       // curve objects
857       if ( ON_NurbsCurve::Cast(m_object) ) {
858         const ON_NurbsCurve* pCurve = ON_NurbsCurve::Cast(m_object);
859       }
860       else if ( ON_ArcCurve::Cast(m_object) ) {
861         const ON_ArcCurve* pCurve = ON_ArcCurve::Cast(m_object);
862       }
863       else if ( ON_CurveOnSurface::Cast(m_object) ) {
864         const ON_CurveOnSurface* pCurve = ON_CurveOnSurface::Cast(m_object);
865       }
866       else if ( ON_BrepEdge::Cast(m_object) ) {
867         const ON_BrepEdge* pCurve = ON_BrepEdge::Cast(m_object);
868       }
869       else if ( ON_LineCurve::Cast(m_object) ) {
870         const ON_LineCurve* pCurve = ON_LineCurve::Cast(m_object);
871       }
872       else if ( ON_PolyCurve::Cast(m_object) ) {
873         const ON_PolyCurve* pCurve = ON_PolyCurve::Cast(m_object);
874       }
875       else if ( ON_PolylineCurve::Cast(m_object) ) {
876         const ON_PolylineCurve* pCurve = ON_PolylineCurve::Cast(m_object);
877       }
878       else if ( ON_CurveProxy::Cast(m_object) ) {
879         const ON_CurveProxy* pCurve = ON_CurveProxy::Cast(m_object);
880       }
881       else {
882         const ON_Curve* pCurve = ON_Curve::Cast(m_object);
883       }
884     }
885     else if ( ON_Surface::Cast(m_object) )
886     {
887       // surface objects
888       if ( ON_NurbsSurface::Cast(m_object) ) {
889         const ON_NurbsSurface* pSurface = ON_NurbsSurface::Cast(m_object);
890       }
891       else if ( ON_PlaneSurface::Cast(m_object) ) {
892         const ON_PlaneSurface* pSurface = ON_PlaneSurface::Cast(m_object);
893       }
894       else if ( ON_RevSurface::Cast(m_object) ) {
895         const ON_RevSurface* pSurface = ON_RevSurface::Cast(m_object);
896       }
897       else if ( ON_BrepFace::Cast(m_object) ) {
898         const ON_BrepFace* pSurface = ON_BrepFace::Cast(m_object);
899       }
900       else if ( ON_SurfaceProxy::Cast(m_object) ) {
901         const ON_SurfaceProxy* pSurface = ON_SurfaceProxy::Cast(m_object);
902       }
903       else {
904         const ON_Surface* pSurface = ON_Surface::Cast(m_object);
905       }
906     }
907     else if ( ON_Mesh::Cast(m_object) )
908     {
909       const ON_Mesh* pMesh = ON_Mesh::Cast(m_object);
910     }
911   }
912   */
913 }
914 
DumpObjectTable(ON_TextLog & dump) const915 void ONX_Model::DumpObjectTable( ON_TextLog& dump) const
916 {
917   int i;
918   for ( i = 0; i < m_object_table.Count(); i++ )
919   {
920     dump.Print("Object %d:\n",i);
921     dump.PushIndent();
922 
923     // object's attibutes
924     m_object_table[i].m_attributes.Dump(dump);
925 
926     // object definition
927     m_object_table[i].Dump(dump);
928 
929     dump.PopIndent();
930   }
931 }
932 
DumpHistoryRecordTable(ON_TextLog & dump) const933 void ONX_Model::DumpHistoryRecordTable( ON_TextLog& dump) const
934 {
935   int i;
936   for ( i = 0; i < m_history_record_table.Count(); i++ )
937   {
938     dump.Print("History record %d:\n",i);
939     dump.PushIndent();
940 
941     const ON_HistoryRecord* history_record = m_history_record_table[i];
942     if ( history_record )
943     {
944       history_record->Dump(dump);
945     }
946     else
947     {
948       dump.Print("Missing.\n");
949     }
950 
951     dump.PopIndent();
952   }
953 }
954 
DumpUserDataTable(ON_TextLog & dump) const955 void ONX_Model::DumpUserDataTable( ON_TextLog& dump) const
956 {
957   int i;
958   for ( i = 0; i < m_userdata_table.Count(); i++ )
959   {
960     const ONX_Model_UserData& ud = m_userdata_table[i];
961     dump.Print("User Data Table %d:\n",i);
962     dump.PushIndent();
963     dump.Print("uuid = "); dump.Print(ud.m_uuid); dump.Print("\n");
964     ud.m_goo.Dump(dump);
965     dump.PopIndent();
966   }
967 }
968 
Dump(ON_TextLog & dump) const969 void ONX_Model::Dump( ON_TextLog& dump ) const
970 {
971   dump.Print("Model summary:\n");
972   dump.PushIndent();
973   DumpSummary(dump);
974   dump.PopIndent();
975   dump.Print("\n");
976 
977   dump.Print("Bitmap table:\n");
978   dump.PushIndent();
979   DumpBitmapTable(dump);
980   dump.PopIndent();
981   dump.Print("\n");
982 
983   dump.Print("TextureMapping table:\n");
984   dump.PushIndent();
985   DumpTextureMappingTable(dump);
986   dump.PopIndent();
987   dump.Print("\n");
988 
989   dump.Print("Material table:\n");
990   dump.PushIndent();
991   DumpMaterialTable(dump);
992   dump.PopIndent();
993   dump.Print("\n");
994 
995   dump.Print("Line type table:\n");
996   dump.PushIndent();
997   DumpLinetypeTable(dump);
998   dump.PopIndent();
999   dump.Print("\n");
1000 
1001   dump.Print("Layer table:\n");
1002   dump.PushIndent();
1003   DumpLayerTable(dump);
1004   dump.PopIndent();
1005   dump.Print("\n");
1006 
1007   dump.Print("Group table:\n");
1008   dump.PushIndent();
1009   DumpGroupTable(dump);
1010   dump.PopIndent();
1011   dump.Print("\n");
1012 
1013   dump.Print("Font table:\n");
1014   dump.PushIndent();
1015   DumpFontTable(dump);
1016   dump.PopIndent();
1017   dump.Print("\n");
1018 
1019   dump.Print("DimStyle table:\n");
1020   dump.PushIndent();
1021   DumpDimStyleTable(dump);
1022   dump.PopIndent();
1023   dump.Print("\n");
1024 
1025   dump.Print("Light table:\n");
1026   dump.PushIndent();
1027   DumpLightTable(dump);
1028   dump.PopIndent();
1029   dump.Print("\n");
1030 
1031   dump.Print("HatchPattern table:\n");
1032   dump.PushIndent();
1033   DumpHatchPatternTable(dump);
1034   dump.PopIndent();
1035   dump.Print("\n");
1036 
1037   dump.Print("Instance Definition table:\n");
1038   dump.PushIndent();
1039   DumpIDefTable(dump);
1040   dump.PopIndent();
1041   dump.Print("\n");
1042 
1043   dump.Print("Object table:\n");
1044   dump.PushIndent();
1045   DumpObjectTable(dump);
1046   dump.PopIndent();
1047   dump.Print("\n");
1048 
1049   dump.Print("History record table:\n");
1050   dump.PushIndent();
1051   DumpHistoryRecordTable(dump);
1052   dump.PopIndent();
1053   dump.Print("\n");
1054 
1055   dump.Print("User data table:\n");
1056   dump.PushIndent();
1057   DumpUserDataTable(dump);
1058   dump.PopIndent();
1059   dump.Print("\n");
1060 }
1061 
1062 static
CheckForCRCErrors(ON_BinaryArchive & archive,ONX_Model & model,ON_TextLog * error_log,const char * sSection)1063 bool CheckForCRCErrors(
1064           ON_BinaryArchive& archive,
1065           ONX_Model& model,
1066           ON_TextLog* error_log,
1067           const char* sSection
1068           )
1069 {
1070   // returns true if new CRC errors are found
1071   bool rc = false;
1072   int new_crc_count = archive.BadCRCCount();
1073 
1074   if ( model.m_crc_error_count != new_crc_count )
1075   {
1076     if ( error_log )
1077     {
1078       error_log->Print("ERROR: Corrupt %s. (CRC errors).\n",sSection);
1079       error_log->Print("-- Attempting to continue.\n");
1080     }
1081     model.m_crc_error_count = new_crc_count;
1082     rc = true;
1083   }
1084 
1085   return rc;
1086 }
1087 
1088 class ON__CIndexPair
1089 {
1090 public:
1091   static int CompareOldIndex( const ON__CIndexPair* a, const ON__CIndexPair* b );
1092   static int CompareOldAndNewIndex( const ON__CIndexPair* a, const ON__CIndexPair* b );
1093   int m_old_index;  // value in model.m_..._table[m_table_index].m_..._index; (Read from file)
1094   int m_new_index;  // index in model.m_..._table[] array
1095 };
1096 
CompareOldIndex(const ON__CIndexPair * a,const ON__CIndexPair * b)1097 int ON__CIndexPair::CompareOldIndex( const ON__CIndexPair* a, const ON__CIndexPair* b )
1098 {
1099   return (a->m_old_index - b->m_old_index);
1100 }
1101 
CompareOldAndNewIndex(const ON__CIndexPair * a,const ON__CIndexPair * b)1102 int ON__CIndexPair::CompareOldAndNewIndex( const ON__CIndexPair* a, const ON__CIndexPair* b )
1103 {
1104   int i;
1105   if ( 0 == (i = a->m_old_index - b->m_old_index) )
1106     i = a->m_new_index - b->m_new_index;
1107   return i;
1108 }
1109 
1110 class ON__CIndexMaps
1111 {
1112 public:
ON__CIndexMaps(ONX_Model & model)1113   ON__CIndexMaps( ONX_Model& model )
1114     : m_model(model),
1115       m_bRemapLayerIndex(0),
1116       m_bRemapMaterialIndex(0),
1117       m_bRemapLinetypeIndex(0),
1118       m_bRemapGroupIndex(0),
1119       m_bRemapFontIndex(0),
1120       m_bRemapDimstyleIndex(0),
1121       m_bRemapHatchPatternIndex(0),
1122       m_layer_count(0),
1123       m_group_count(0),
1124       m_material_count(0),
1125       m_linetype_count(0),
1126       m_font_count(0),
1127       m_dimstyle_count(0),
1128       m_hatch_pattern_count(0),
1129       m_default_layer_index(0),
1130       m_default_group_index(-1),
1131       m_default_material_index(-1),
1132       m_default_linetype_index(-1),
1133       m_default_font_index(0),
1134       m_default_dimstyle_index(0),
1135       m_default_hatch_pattern_index(-1)
1136   {
1137     CreateHelper();
1138   }
1139 
1140   /*
1141   Description:
1142     Remap all tables in m_model.
1143   Returns:
1144     Number of indices that were changed.
1145   */
1146   int RemapModel();
1147 
1148   ONX_Model& m_model;
1149 
1150   bool m_bRemapLayerIndex;
1151   bool m_bRemapMaterialIndex;
1152   bool m_bRemapLinetypeIndex;
1153   bool m_bRemapGroupIndex;
1154   bool m_bRemapFontIndex;
1155   bool m_bRemapDimstyleIndex;
1156   bool m_bRemapHatchPatternIndex;
1157 
1158   int m_layer_count;
1159   int m_group_count;
1160   int m_material_count;
1161   int m_linetype_count;
1162   int m_font_count;
1163   int m_dimstyle_count;
1164   int m_hatch_pattern_count;
1165 
1166   int m_default_layer_index;
1167   int m_default_group_index;
1168   int m_default_material_index;
1169   int m_default_linetype_index;
1170   int m_default_font_index;
1171   int m_default_dimstyle_index;
1172   int m_default_hatch_pattern_index;
1173 
1174   ON_SimpleArray<ON__CIndexPair> m_layer_map;
1175   ON_SimpleArray<ON__CIndexPair> m_group_map;
1176   ON_SimpleArray<ON__CIndexPair> m_material_map;
1177   ON_SimpleArray<ON__CIndexPair> m_linetype_map;
1178   ON_SimpleArray<ON__CIndexPair> m_font_map;
1179   ON_SimpleArray<ON__CIndexPair> m_dimstyle_map;
1180   ON_SimpleArray<ON__CIndexPair> m_hatch_pattern_map;
1181 
1182 
1183   /*
1184   Description:
1185     Low level tool to convert old_layer_index into a valid
1186     m_layer_table[] index.
1187   Parameters:
1188     old_layer_index - [in]
1189   Returns:
1190     new layer index to use.
1191   */
1192   int RemapLayerIndex( int old_layer_index ) const;
1193 
1194   /*
1195   Description:
1196     Low level tool to convert old_material_index into a valid
1197     m_material_table[] index  or -1 if the default material
1198     should be used.
1199   Parameters:
1200     old_material_index - [in]
1201   Returns:
1202     new material index to use.
1203   */
1204   int RemapMaterialIndex( int old_material_index ) const;
1205 
1206   /*
1207   Description:
1208     Low level tool to convert old_linetype_index into a valid
1209     m_linetype_table[] index or -1 if the default linetype
1210     should be used.
1211   Parameters:
1212     old_linetype_index - [in]
1213   Returns:
1214     new linetype index to use.
1215   */
1216   int RemapLinetypeIndex( int old_linetype_index ) const;
1217 
1218   /*
1219   Description:
1220     Low level tool to convert old_group_index into a valid
1221     m_group_table[] index or -1 if no conversion is possible.
1222   Parameters:
1223     old_group_index - [in]
1224   Returns:
1225     new group index to use or -1 if old_group_index makes no sense.
1226   */
1227   int RemapGroupIndex( int old_group_index ) const;
1228 
1229   /*
1230   Description:
1231     Low level tool to convert old_font_index into a valid
1232     m_font_table[] index or -1 if no conversion is possible.
1233   Parameters:
1234     old_font_index - [in]
1235   Returns:
1236     new font index to use or -1 if old_font_index makes no sense.
1237   */
1238   int RemapFontIndex( int old_font_index ) const;
1239 
1240   /*
1241   Description:
1242     Low level tool to convert old_dimstyle_index into a valid
1243     m_dimstyle_table[] index or -1 if no conversion is possible.
1244   Parameters:
1245     old_dimstyle_index - [in]
1246   Returns:
1247     new dimstyle index to use or -1 if old_dimstyle_index makes no sense.
1248   */
1249   int RemapDimstyleIndex( int old_dimstyle_index ) const;
1250 
1251   /*
1252   Description:
1253     Low level tool to convert old_hatch_pattern_index into a valid
1254     m_hatch_pattern_table[] index or -1 if no conversion is possible.
1255   Parameters:
1256     old_hatch_pattern_index - [in]
1257   Returns:
1258     new hatch pattern index to use or -1 if old_hatch_pattern_index makes no sense.
1259   */
1260   int RemapHatchPatternIndex( int old_hatch_pattern_index ) const;
1261 
1262   /*
1263   Description:
1264     Low level tool to remap table indices used by model objects in
1265     the object attributes class.
1266   */
1267   int RemapGeometryAndObjectAttributes( ONX_Model_Object& );
1268 
1269   /*
1270   Description:
1271     Low level tool to remap table indices used by model objects in
1272     the object attributes class.
1273   Returns:
1274     Number of indices that were changed.
1275   */
1276   int RemapGeometryAttributes( ON_Object* );
1277 
1278   /*
1279   Description:
1280     Low level tool to remap table indices saved in
1281     the object attributes class.
1282   Returns:
1283     Number of indices that were changed.
1284   */
1285   int RemapObjectAttributes( ON_3dmObjectAttributes& );
1286 
1287   /*
1288   Description:
1289     Low level tool to remap table indices saved in
1290     the object attributes class.
1291   Returns:
1292     Number of indices that were changed.
1293   */
1294   int RemapLayerAttributes( ON_Layer& );
1295 
1296   /*
1297   Description:
1298     Low level tool to remap material table indices saved in
1299     the rendering attributes class.
1300   Returns:
1301     Number of indices that were changed.
1302   */
1303   int RemapRenderingAttributes( ON_RenderingAttributes& ra );
1304 
1305 private:
1306   int CreateHelper();
1307 
1308 private:
1309   // no implementation - prohibit use
1310   ON__CIndexMaps(const ON__CIndexMaps&);
1311   ON__CIndexMaps& operator=(const ON__CIndexMaps&);
1312 };
1313 
1314 
CreateHelper()1315 int ON__CIndexMaps::CreateHelper()
1316 {
1317   int change_count = 0;
1318   int i;
1319 
1320   // bitmaps are not referenced by index any place,
1321   // so just set bitmap index to match table index
1322   for ( i = 0; i < m_model.m_bitmap_table.Count(); i++ )
1323   {
1324     ON_Bitmap* bitmap = m_model.m_bitmap_table[i];
1325     if ( !bitmap )
1326     {
1327       change_count++;
1328       m_model.m_bitmap_table.Remove(i);
1329       i--;
1330       continue;
1331     }
1332 
1333     if ( bitmap->m_bitmap_index != i )
1334     {
1335       bitmap->m_bitmap_index = i;
1336       change_count++;
1337     }
1338     if ( ON_nil_uuid == bitmap->m_bitmap_id )
1339     {
1340       ON_CreateUuid(bitmap->m_bitmap_id);
1341       change_count++;
1342     }
1343   }
1344 
1345   // texture maps are not referenced  by index
1346   // so just set texture map index to match table
1347   // index
1348   m_model.m_mapping_id_index.Empty();
1349   m_model.m_mapping_id_index.Reserve(m_model.m_mapping_table.Count());
1350   for ( i = 0; i < m_model.m_mapping_table.Count(); i++ )
1351   {
1352     ON_TextureMapping& mapping = m_model.m_mapping_table[i];
1353     if ( mapping.m_mapping_index != i )
1354     {
1355       mapping.m_mapping_index = i;
1356       change_count++;
1357     }
1358     if ( ON_nil_uuid == mapping.m_mapping_id )
1359     {
1360       ON_CreateUuid(mapping.m_mapping_id);
1361       change_count++;
1362     }
1363     m_model.m_mapping_id_index.AddUuidIndex(mapping.m_mapping_id,i,false);
1364   }
1365 
1366   // make sure material indices are valid
1367   m_model.m_material_id_index.Empty();
1368   m_model.m_material_id_index.Reserve(m_model.m_material_table.Count());
1369   m_bRemapMaterialIndex = false;
1370   m_material_count = m_model.m_material_table.Count();
1371   m_material_map.SetCount(0);
1372   m_material_map.Reserve(m_material_count);
1373   for ( i = 0; i < m_material_count; i++ )
1374   {
1375     ON_Material& material = m_model.m_material_table[i];
1376     ON__CIndexPair& ip = m_material_map.AppendNew();
1377     ip.m_new_index = i;
1378     ip.m_old_index = material.m_material_index;
1379     if ( material.m_material_index != i )
1380     {
1381       material.m_material_index = i;
1382       m_bRemapMaterialIndex = true;
1383       change_count++;
1384     }
1385     if ( ON_nil_uuid == material.m_material_id )
1386     {
1387       ON_CreateUuid(material.m_material_id);
1388       change_count++;
1389     }
1390     m_model.m_material_id_index.AddUuidIndex(material.m_material_id,i,false);
1391   }
1392 
1393   // make sure linetype indices are valid
1394   m_bRemapLinetypeIndex = false;
1395   m_linetype_count = m_model.m_linetype_table.Count();
1396   m_linetype_map.SetCount(0);
1397   m_linetype_map.Reserve(m_linetype_count);
1398   for ( i = 0; i < m_linetype_count; i++ )
1399   {
1400     ON_Linetype& linetype = m_model.m_linetype_table[i];
1401     ON__CIndexPair& ip = m_linetype_map.AppendNew();
1402     ip.m_new_index = i;
1403     ip.m_old_index = linetype.m_linetype_index;
1404     if ( linetype.m_linetype_index != i )
1405     {
1406       linetype.m_linetype_index = i;
1407       m_bRemapLinetypeIndex = true;
1408       change_count++;
1409     }
1410     if ( ON_nil_uuid == linetype.m_linetype_id )
1411     {
1412       ON_CreateUuid(linetype.m_linetype_id);
1413       change_count++;
1414     }
1415   }
1416 
1417   // make sure there is at least one layer
1418   if ( m_model.m_layer_table.Count() < 1 )
1419   {
1420     ON_Layer layer;
1421     layer.Default();
1422     m_model.GetUnusedLayerName(layer.m_name);
1423     if ( !ONX_IsValidName(layer.m_name) )
1424       layer.m_name = L"Default";
1425     layer.m_layer_index = 0;
1426     ON_CreateUuid(layer.m_layer_id);
1427     m_model.m_layer_table.Append(layer);
1428     change_count++;
1429   }
1430 
1431   // make sure layer indices are valid
1432   m_bRemapLayerIndex = false;
1433   m_layer_count = m_model.m_layer_table.Count();
1434   m_layer_map.SetCount(0);
1435   m_layer_map.Reserve(m_layer_count);
1436   for ( i = 0; i < m_layer_count; i++ )
1437   {
1438     ON_Layer& layer = m_model.m_layer_table[i];
1439     ON__CIndexPair& ip = m_layer_map.AppendNew();
1440     ip.m_new_index = i;
1441     ip.m_old_index = layer.m_layer_index;
1442     if ( layer.m_layer_index != i )
1443     {
1444       layer.m_layer_index = i;
1445       m_bRemapLayerIndex = true;
1446       change_count++;
1447     }
1448     if ( ON_nil_uuid == layer.m_layer_id )
1449     {
1450       ON_CreateUuid(layer.m_layer_id);
1451       change_count++;
1452     }
1453   }
1454 
1455   // make sure group indices are valid
1456   m_bRemapGroupIndex = false;
1457   m_group_count = m_model.m_group_table.Count();
1458   m_group_map.SetCount(0);
1459   m_group_map.Reserve(m_group_count);
1460   for ( i = 0; i < m_group_count; i++ )
1461   {
1462     ON_Group& group = m_model.m_group_table[i];
1463     ON__CIndexPair& ip = m_group_map.AppendNew();
1464     ip.m_new_index = i;
1465     ip.m_old_index = group.m_group_index;
1466     if ( group.m_group_index != i )
1467     {
1468       group.m_group_index = i;
1469       m_bRemapGroupIndex = true;
1470       change_count++;
1471     }
1472     if ( ON_nil_uuid == group.m_group_id )
1473     {
1474       ON_CreateUuid(group.m_group_id);
1475       change_count++;
1476     }
1477   }
1478 
1479   // make sure there is at least one font
1480   if ( m_model.m_font_table.Count() < 1 )
1481   {
1482     ON_Font font;
1483     font.Defaults();
1484     if ( !ONX_IsValidName(font.m_font_name) )
1485       font.m_font_name = L"Default";
1486     font.m_font_index = 0;
1487     ON_CreateUuid(font.m_font_id);
1488     m_model.m_font_table.Append(font);
1489     change_count++;
1490   }
1491 
1492   // make sure font indices are valid
1493   m_bRemapFontIndex = false;
1494   m_font_count = m_model.m_font_table.Count();
1495   m_font_map.SetCount(0);
1496   m_font_map.Reserve(m_font_count);
1497   for ( i = 0; i < m_font_count; i++ )
1498   {
1499     ON_Font& font = m_model.m_font_table[i];
1500     ON__CIndexPair& ip = m_font_map.AppendNew();
1501     ip.m_new_index = i;
1502     ip.m_old_index = font.m_font_index;
1503     if ( font.m_font_index != i )
1504     {
1505       font.m_font_index = i;
1506       m_bRemapFontIndex = true;
1507       change_count++;
1508     }
1509     if ( ON_nil_uuid == font.m_font_id )
1510     {
1511       ON_CreateUuid(font.m_font_id);
1512       change_count++;
1513     }
1514   }
1515 
1516   // make sure there is at least one dimstyle
1517   if ( m_model.m_dimstyle_table.Count() < 1 )
1518   {
1519     ON_DimStyle dimstyle;
1520     dimstyle.SetDefaults();
1521     if ( !ONX_IsValidName(dimstyle.m_dimstyle_name) )
1522       dimstyle.m_dimstyle_name = L"Default";
1523     dimstyle.m_dimstyle_index = 0;
1524     ON_CreateUuid(dimstyle.m_dimstyle_id);
1525     dimstyle.m_fontindex = 0;
1526     m_model.m_dimstyle_table.Append(dimstyle);
1527     change_count++;
1528   }
1529 
1530   // make sure dimstyle indices are valid
1531   m_bRemapDimstyleIndex = false;
1532   m_dimstyle_count = m_model.m_dimstyle_table.Count();
1533   m_dimstyle_map.SetCount(0);
1534   m_dimstyle_map.Reserve(m_dimstyle_count);
1535   for ( i = 0; i < m_dimstyle_count; i++ )
1536   {
1537     ON_DimStyle& dimstyle = m_model.m_dimstyle_table[i];
1538     ON__CIndexPair& ip = m_dimstyle_map.AppendNew();
1539     ip.m_new_index = i;
1540     ip.m_old_index = dimstyle.m_dimstyle_index;
1541     if ( dimstyle.m_dimstyle_index != i )
1542     {
1543       dimstyle.m_dimstyle_index = i;
1544       m_bRemapDimstyleIndex = true;
1545       change_count++;
1546     }
1547     if ( ON_nil_uuid == dimstyle.m_dimstyle_id )
1548     {
1549       ON_CreateUuid(dimstyle.m_dimstyle_id);
1550       change_count++;
1551     }
1552   }
1553 
1554   // lights are not referenced by index any place,
1555   // so just set light index to match table index
1556   for ( i = 0; i < m_model.m_light_table.Count(); i++ )
1557   {
1558     ONX_Model_RenderLight& light = m_model.m_light_table[i];
1559     if ( light.m_light.m_light_index != i )
1560     {
1561       light.m_light.m_light_index = i;
1562       change_count++;
1563     }
1564 
1565     if ( light.m_light.m_light_id == light.m_attributes.m_uuid )
1566     {
1567       // ids match - this is good
1568       if ( ON_nil_uuid == light.m_light.m_light_id )
1569       {
1570         // ids not set
1571         ON_CreateUuid(light.m_light.m_light_id);
1572         light.m_attributes.m_uuid = light.m_light.m_light_id;
1573         change_count++;
1574       }
1575     }
1576     else if ( ON_nil_uuid == light.m_light.m_light_id )
1577     {
1578       // id not set on the light object
1579       light.m_light.m_light_id = light.m_attributes.m_uuid;
1580       change_count++;
1581     }
1582     else if ( ON_nil_uuid == light.m_attributes.m_uuid )
1583     {
1584       // id not set on the attributes
1585       light.m_attributes.m_uuid = light.m_light.m_light_id;
1586       change_count++;
1587     }
1588     else
1589     {
1590       // id's are different - the one on the light object wins
1591       light.m_attributes.m_uuid = light.m_light.m_light_id;
1592       change_count++;
1593     }
1594   }
1595 
1596   // make sure hatch pattern indices are valid
1597   m_bRemapHatchPatternIndex = false;
1598   m_hatch_pattern_count = m_model.m_hatch_pattern_table.Count();
1599   m_hatch_pattern_map.SetCount(0);
1600   m_hatch_pattern_map.Reserve(m_hatch_pattern_count);
1601   for ( i = 0; i < m_hatch_pattern_count; i++ )
1602   {
1603     ON_HatchPattern& hatchpattern = m_model.m_hatch_pattern_table[i];
1604     ON__CIndexPair& ip = m_hatch_pattern_map.AppendNew();
1605     ip.m_new_index = i;
1606     ip.m_old_index = hatchpattern.m_hatchpattern_index;
1607     if ( ip.m_new_index != ip.m_old_index )
1608     {
1609       hatchpattern.m_hatchpattern_index = i;
1610       m_bRemapHatchPatternIndex = true;
1611       change_count++;
1612     }
1613     if ( ON_nil_uuid == hatchpattern.m_hatchpattern_id )
1614     {
1615       ON_CreateUuid(hatchpattern.m_hatchpattern_id);
1616       change_count++;
1617     }
1618   }
1619 
1620   // make sure idefs have valid ids
1621   m_model.m_idef_id_index.Empty();
1622   m_model.m_idef_id_index.Reserve(m_model.m_idef_table.Count());
1623   for ( i = 0; i < m_model.m_idef_table.Count(); i++ )
1624   {
1625     ON_InstanceDefinition& idef = m_model.m_idef_table[i];
1626     if ( ON_nil_uuid == idef.m_uuid )
1627     {
1628       ON_CreateUuid(idef.m_uuid);
1629       change_count++;
1630     }
1631     m_model.m_idef_id_index.AddUuidIndex(idef.m_uuid,i,false);
1632   }
1633 
1634   // make sure objects have valid ids
1635   m_model.m_object_id_index.Empty();
1636   m_model.m_object_id_index.Reserve(m_model.m_object_table.Count());
1637   for ( i = 0; i < m_model.m_object_table.Count(); i++ )
1638   {
1639     ONX_Model_Object& mo = m_model.m_object_table[i];
1640     if ( ON_nil_uuid == mo.m_attributes.m_uuid )
1641     {
1642       ON_CreateUuid(mo.m_attributes.m_uuid);
1643       change_count++;
1644     }
1645     m_model.m_object_id_index.AddUuidIndex(mo.m_attributes.m_uuid,i,false);
1646   }
1647 
1648   // make sure history records have valid ids
1649   for ( i = 0; i < m_model.m_history_record_table.Count(); i++ )
1650   {
1651     ON_HistoryRecord* hr = m_model.m_history_record_table[i];
1652     if ( !hr )
1653     {
1654       change_count++;
1655       m_model.m_history_record_table.Remove(i);
1656       i--;
1657       continue;
1658     }
1659     if ( ON_nil_uuid == hr->m_record_id )
1660     {
1661       ON_CreateUuid(hr->m_record_id);
1662       change_count++;
1663     }
1664   }
1665 
1666   // Sort the maps
1667   if ( m_bRemapLayerIndex )
1668     m_layer_map.QuickSort( ON__CIndexPair::CompareOldAndNewIndex );
1669   if ( m_bRemapGroupIndex )
1670     m_group_map.QuickSort( ON__CIndexPair::CompareOldAndNewIndex );
1671   if ( m_bRemapMaterialIndex )
1672     m_material_map.QuickSort( ON__CIndexPair::CompareOldAndNewIndex );
1673   if ( m_bRemapLinetypeIndex )
1674     m_linetype_map.QuickSort( ON__CIndexPair::CompareOldAndNewIndex );
1675   if ( m_bRemapFontIndex )
1676     m_font_map.QuickSort( ON__CIndexPair::CompareOldAndNewIndex );
1677   if ( m_bRemapDimstyleIndex )
1678     m_dimstyle_map.QuickSort( ON__CIndexPair::CompareOldAndNewIndex );
1679   if ( m_bRemapHatchPatternIndex )
1680     m_hatch_pattern_map.QuickSort( ON__CIndexPair::CompareOldAndNewIndex );
1681 
1682   return change_count;
1683 }
1684 
RemapGeometryAttributes(ON_Object * object)1685 int ON__CIndexMaps::RemapGeometryAttributes( ON_Object* object )
1686 {
1687   int change_count = 0;
1688 
1689   switch(object ? object->ObjectType() : ON::unknown_object_type )
1690   {
1691   case ON::layer_object:
1692     {
1693       ON_Layer* layer = ON_Layer::Cast(object);
1694       if ( layer )
1695         change_count += RemapLayerAttributes(*layer);
1696     }
1697     break;
1698 
1699   case ON::annotation_object:
1700     {
1701       ON_Annotation2* ann = ON_Annotation2::Cast(object);
1702       if ( ann )
1703       {
1704         if (ann->IsText() )
1705         {
1706           // ann->m_index is a font index
1707           int old_font_index = ann->m_index;
1708           int new_font_index = RemapFontIndex(old_font_index);
1709           if ( new_font_index != old_font_index )
1710           {
1711             ann->m_index = new_font_index;
1712             change_count++;
1713           }
1714         }
1715         else
1716         {
1717           // ann->m_index is a dimstyle index
1718           int old_dimstyle_index = ann->m_index;
1719           int new_dimstyle_index = RemapDimstyleIndex(old_dimstyle_index);
1720           {
1721             if ( old_dimstyle_index != new_dimstyle_index )
1722             {
1723               ann->m_index = new_dimstyle_index;
1724               change_count++;
1725             }
1726           }
1727         }
1728       }
1729     }
1730     break;
1731 
1732   case ON::hatch_object:
1733     {
1734       ON_Hatch* hatch_object = ON_Hatch::Cast(object);
1735       if ( hatch_object )
1736       {
1737         int old_hatch_pattern_index = hatch_object->PatternIndex();
1738         int new_hatch_pattern_index = RemapHatchPatternIndex(old_hatch_pattern_index);
1739         if ( old_hatch_pattern_index != new_hatch_pattern_index )
1740           hatch_object->SetPatternIndex(new_hatch_pattern_index);
1741       }
1742     }
1743     break;
1744 
1745   default:
1746     // other object types skipped on purpose
1747     break;
1748   }
1749 
1750   return change_count;
1751 }
1752 
RemapGeometryAndObjectAttributes(ONX_Model_Object & model_object)1753 int ON__CIndexMaps::RemapGeometryAndObjectAttributes( ONX_Model_Object& model_object )
1754 {
1755   int geometry_change_count  = RemapGeometryAttributes(const_cast<ON_Object*>(model_object.m_object));
1756   int attribute_change_count = RemapObjectAttributes( model_object.m_attributes );
1757   return (geometry_change_count + attribute_change_count);
1758 }
1759 
RemapModel()1760 int ON__CIndexMaps::RemapModel()
1761 {
1762   int change_count = 0;
1763 
1764   int i, old_index, new_index;
1765 
1766   // make sure current layer is valid and in "normal" mode
1767   old_index =  m_model.m_settings.m_current_layer_index;
1768   new_index = RemapLayerIndex(old_index);
1769   if ( new_index < 0 || new_index >= m_layer_count )
1770   {
1771     new_index = 0;
1772   }
1773   m_model.m_settings.m_current_layer_index = new_index;
1774   if ( !m_model.m_layer_table[new_index].IsVisibleAndNotLocked() )
1775   {
1776     m_model.m_layer_table[new_index].SetVisible( true );
1777     m_model.m_layer_table[new_index].SetLocked( false );
1778   }
1779   m_default_layer_index = m_model.m_settings.m_current_layer_index;
1780 
1781   for ( i = 0; i < m_model.m_layer_table.Count(); i++ )
1782   {
1783     change_count += RemapLayerAttributes(m_model.m_layer_table[i]);
1784   }
1785 
1786   for ( i = 0; i < m_model.m_dimstyle_table.Count(); i++ )
1787   {
1788     old_index = m_model.m_dimstyle_table[i].m_fontindex;
1789     new_index = RemapFontIndex(old_index);
1790     if ( new_index != old_index )
1791     {
1792       m_model.m_dimstyle_table[i].m_fontindex = new_index;
1793       change_count++;
1794     }
1795   }
1796 
1797   for ( i = 0; i < m_model.m_light_table.Count(); i++ )
1798   {
1799     change_count += RemapObjectAttributes( m_model.m_light_table[i].m_attributes );
1800   }
1801 
1802   for ( i = 0; i < m_model.m_object_table.Count(); i++ )
1803   {
1804     change_count += RemapGeometryAndObjectAttributes( m_model.m_object_table[i] );
1805   }
1806 
1807   return change_count;
1808 }
1809 
1810 
1811 
RemapIndexHelper(int old_index,bool bRemapIndex,int count,int default_index,const ON_SimpleArray<ON__CIndexPair> & imap)1812 static int RemapIndexHelper(
1813                 int old_index,
1814                 bool bRemapIndex,
1815                 int count,
1816                 int default_index,
1817                 const ON_SimpleArray<ON__CIndexPair>& imap
1818                 )
1819 {
1820   int new_index = old_index;
1821   if ( bRemapIndex )
1822   {
1823     ON__CIndexPair ip;
1824     memset(&ip,0,sizeof(ip));
1825     ip.m_old_index = old_index;
1826     int j = imap.BinarySearch(&ip,ON__CIndexPair::CompareOldIndex);
1827     if ( j >= 0 )
1828       new_index = imap[j].m_new_index;
1829   }
1830   if ( new_index < 0 || new_index >= count )
1831     new_index = default_index;
1832   return new_index;
1833 }
1834 
RemapLayerIndex(int old_layer_index) const1835 int ON__CIndexMaps::RemapLayerIndex( int old_layer_index ) const
1836 {
1837   return RemapIndexHelper(
1838               old_layer_index,
1839               m_bRemapLayerIndex,
1840               m_layer_count,
1841               m_default_layer_index,
1842               m_layer_map
1843               );
1844 }
1845 
RemapMaterialIndex(int old_material_index) const1846 int ON__CIndexMaps::RemapMaterialIndex( int old_material_index ) const
1847 {
1848   return RemapIndexHelper(
1849               old_material_index,
1850               m_bRemapMaterialIndex,
1851               m_material_count,
1852               m_default_material_index,
1853               m_material_map
1854               );
1855 }
1856 
RemapLinetypeIndex(int old_linetype_index) const1857 int ON__CIndexMaps::RemapLinetypeIndex( int old_linetype_index ) const
1858 {
1859   return RemapIndexHelper(
1860               old_linetype_index,
1861               m_bRemapLinetypeIndex,
1862               m_linetype_count,
1863               m_default_linetype_index,
1864               m_linetype_map
1865               );
1866 }
1867 
RemapGroupIndex(int old_group_index) const1868 int ON__CIndexMaps::RemapGroupIndex( int old_group_index ) const
1869 {
1870   return RemapIndexHelper(
1871               old_group_index,
1872               m_bRemapGroupIndex,
1873               m_group_count,
1874               m_default_group_index,
1875               m_group_map
1876               );
1877 }
1878 
RemapFontIndex(int old_font_index) const1879 int ON__CIndexMaps::RemapFontIndex( int old_font_index ) const
1880 {
1881   return RemapIndexHelper(
1882               old_font_index,
1883               m_bRemapFontIndex,
1884               m_font_count,
1885               m_default_font_index,
1886               m_font_map
1887               );
1888 }
1889 
RemapDimstyleIndex(int old_dimstyle_index) const1890 int ON__CIndexMaps::RemapDimstyleIndex( int old_dimstyle_index ) const
1891 {
1892   return RemapIndexHelper(
1893               old_dimstyle_index,
1894               m_bRemapDimstyleIndex,
1895               m_dimstyle_count,
1896               m_default_dimstyle_index,
1897               m_dimstyle_map
1898               );
1899 }
1900 
RemapHatchPatternIndex(int old_hatch_pattern_index) const1901 int ON__CIndexMaps::RemapHatchPatternIndex( int old_hatch_pattern_index ) const
1902 {
1903   return RemapIndexHelper(
1904               old_hatch_pattern_index,
1905               m_bRemapHatchPatternIndex,
1906               m_hatch_pattern_count,
1907               m_default_hatch_pattern_index,
1908               m_hatch_pattern_map
1909               );
1910 }
1911 
RemapRenderingAttributes(ON_RenderingAttributes & ra)1912 int ON__CIndexMaps::RemapRenderingAttributes( ON_RenderingAttributes& ra )
1913 {
1914   int change_count = 0;
1915   int old_material_index, new_material_index, i;
1916   for ( i = ra.m_materials.Count()-1; i >= 0; i-- )
1917   {
1918     ON_MaterialRef& mr = ra.m_materials[i];
1919 
1920     old_material_index = mr.m_material_index;
1921     if ( old_material_index >= 0 )
1922     {
1923       new_material_index = RemapMaterialIndex(old_material_index);
1924       if ( old_material_index != new_material_index )
1925       {
1926         mr.m_material_index = new_material_index;
1927         change_count++;
1928       }
1929     }
1930     else
1931       mr.m_material_index = -1;
1932 
1933     old_material_index = mr.m_material_backface_index;
1934     if ( old_material_index >= 0 )
1935     {
1936       new_material_index = RemapMaterialIndex(old_material_index);
1937       if ( old_material_index != new_material_index )
1938       {
1939         mr.m_material_backface_index = new_material_index;
1940         change_count++;
1941       }
1942     }
1943     else
1944       mr.m_material_backface_index = -1;
1945 
1946     if ( -1 == mr.m_material_index || mr.m_material_index >= m_material_count )
1947     {
1948       if ( ON_nil_uuid == mr.m_material_id )
1949         mr.m_material_index = -1;
1950       else if ( !m_model.m_material_id_index.FindUuid(mr.m_material_id,&mr.m_material_index) )
1951         mr.m_material_index = -1;
1952     }
1953     else if ( m_model.m_material_table[mr.m_material_index].m_material_id != mr.m_material_id )
1954     {
1955       new_material_index = -1;
1956       if (    !ON_UuidIsNil(mr.m_material_id)
1957            && m_model.m_material_id_index.FindUuid(mr.m_material_id,&new_material_index)
1958            && new_material_index >= 0
1959            && new_material_index < m_material_count
1960          )
1961       {
1962         mr.m_material_index = new_material_index;
1963       }
1964       else
1965       {
1966         mr.m_material_id = m_model.m_material_table[mr.m_material_index].m_material_id;
1967       }
1968     }
1969 
1970     if ( -1 == mr.m_material_backface_index || mr.m_material_backface_index >= m_material_count )
1971     {
1972       if ( ON_nil_uuid == mr.m_material_backface_id )
1973         mr.m_material_backface_index = -1;
1974       else if ( !m_model.m_material_id_index.FindUuid(mr.m_material_backface_id,&mr.m_material_backface_index) )
1975         mr.m_material_backface_index = -1;
1976     }
1977     else if ( m_model.m_material_table[mr.m_material_backface_index].m_material_id != mr.m_material_backface_id )
1978     {
1979       new_material_index = -1;
1980       if (   !ON_UuidIsNil(mr.m_material_backface_id)
1981            && m_model.m_material_id_index.FindUuid(mr.m_material_backface_id,&new_material_index)
1982            && new_material_index >= 0
1983            && new_material_index < m_material_count
1984           )
1985       {
1986         mr.m_material_backface_index = new_material_index;
1987       }
1988       else
1989       {
1990         mr.m_material_backface_id = m_model.m_material_table[mr.m_material_backface_index].m_material_id;
1991       }
1992     }
1993 
1994     if ( mr.m_material_index < 0 && mr.m_material_backface_index < 0 )
1995     {
1996       ra.m_materials.Remove(i);
1997     }
1998   }
1999   return change_count;
2000 }
2001 
RemapLayerAttributes(ON_Layer & layer)2002 int ON__CIndexMaps::RemapLayerAttributes( ON_Layer& layer )
2003 {
2004   int change_count = 0;
2005 
2006   if ( ON_UuidIsNil(layer.m_layer_id) )
2007   {
2008     ON_CreateUuid(layer.m_layer_id);
2009     change_count++;
2010   }
2011 
2012   int old_linetype_index = layer.m_linetype_index;
2013   int new_linetype_index = RemapLinetypeIndex(old_linetype_index);
2014   if ( old_linetype_index != new_linetype_index )
2015   {
2016     layer.m_linetype_index = new_linetype_index;
2017     change_count++;
2018   }
2019 
2020   int old_material_index = layer.m_material_index;
2021   int new_material_index = RemapMaterialIndex(old_material_index);
2022   if ( old_material_index != new_material_index )
2023   {
2024     layer.m_material_index = new_material_index;
2025     change_count++;
2026   }
2027 
2028   change_count += RemapRenderingAttributes(layer.m_rendering_attributes);
2029 
2030   return change_count;
2031 }
2032 
RemapObjectAttributes(ON_3dmObjectAttributes & a)2033 int ON__CIndexMaps::RemapObjectAttributes( ON_3dmObjectAttributes& a )
2034 {
2035   int change_count = 0;
2036 
2037   int i;
2038   if ( ON_UuidIsNil(a.m_uuid) )
2039   {
2040     ON_CreateUuid(a.m_uuid);
2041     change_count++;
2042   }
2043 
2044   int old_layer_index = a.m_layer_index;
2045   int new_layer_index = RemapLayerIndex(old_layer_index);
2046   if ( old_layer_index != new_layer_index )
2047   {
2048     a.m_layer_index = new_layer_index;
2049     change_count++;
2050   }
2051 
2052   int old_linetype_index = a.m_linetype_index;
2053   int new_linetype_index = RemapLinetypeIndex(old_linetype_index);
2054   if ( old_linetype_index != new_linetype_index )
2055   {
2056     a.m_linetype_index = new_linetype_index;
2057     change_count++;
2058   }
2059 
2060   int old_material_index = a.m_material_index;
2061   int new_material_index = RemapMaterialIndex(old_material_index);
2062   if ( old_material_index != new_material_index )
2063   {
2064     a.m_material_index = new_material_index;
2065     change_count++;
2066   }
2067 
2068   if ( a.TopGroup() != -1 )
2069   {
2070     bool bUpdateGroupList = true;
2071     ON_SimpleArray<int> group_list;
2072     a.GetGroupList(group_list);
2073     for ( i = group_list.Count()-1; i >= 0; i-- )
2074     {
2075       int old_group_index = group_list[i];
2076       int new_group_index = RemapGroupIndex(old_group_index);
2077       if ( new_group_index < 0 )
2078       {
2079         group_list.Remove(i);
2080         bUpdateGroupList = true;
2081         change_count++;
2082       }
2083       else if ( old_group_index != new_group_index )
2084       {
2085         group_list[i] = new_group_index;
2086         bUpdateGroupList = true;
2087         change_count++;
2088       }
2089     }
2090 
2091     if ( bUpdateGroupList || group_list.Count() == 0 )
2092     {
2093       a.RemoveFromAllGroups();
2094       for( i = 0; i < group_list.Count(); i++ )
2095         a.AddToGroup(group_list[i]);
2096     }
2097   }
2098 
2099   change_count += RemapRenderingAttributes(a.m_rendering_attributes);
2100 
2101   return change_count;
2102 }
2103 
Polish()2104 void ONX_Model::Polish()
2105 {
2106   DestroyCache();
2107 
2108   // make sure there is a valid revision history
2109   if ( m_properties.m_RevisionHistory.m_revision_count == 0 )
2110     m_properties.m_RevisionHistory.NewRevision();
2111 
2112 
2113   // Get maps sorted so BinarySearch calls in PolishAttributes
2114   // will work.
2115   ON__CIndexMaps imaps(*this);
2116   imaps.RemapModel();
2117 }
2118 
Read(const char * filename,ON_TextLog * error_log)2119 bool ONX_Model::Read(
2120        const char* filename,
2121        ON_TextLog* error_log
2122        )
2123 {
2124   Destroy(); // get rid of any residual stuff
2125   bool rc = false;
2126   if ( 0 != filename )
2127   {
2128     FILE* fp = ON::OpenFile(filename,"rb");
2129     if ( 0 != fp )
2130     {
2131       ON_BinaryFile file(ON::read3dm,fp);
2132       rc = Read(file,error_log);
2133       ON::CloseFile(fp);
2134     }
2135   }
2136   return rc;
2137 }
2138 
Read(const wchar_t * filename,ON_TextLog * error_log)2139 bool ONX_Model::Read(
2140        const wchar_t* filename,
2141        ON_TextLog* error_log
2142        )
2143 {
2144   Destroy(); // get rid of any residual stuff
2145   bool rc = false;
2146   if ( 0 != filename )
2147   {
2148     FILE* fp = ON::OpenFile(filename,L"rb");
2149     if ( 0 != fp )
2150     {
2151       ON_BinaryFile file(ON::read3dm,fp);
2152       rc = Read(file,error_log);
2153       ON::CloseFile(fp);
2154     }
2155   }
2156   return rc;
2157 }
2158 
Read(ON_BinaryArchive & archive,ON_TextLog * error_log)2159 bool ONX_Model::Read(
2160        ON_BinaryArchive& archive,
2161        ON_TextLog* error_log
2162        )
2163 {
2164   const int max_error_count = 2000;
2165   int error_count = 0;
2166   bool return_code = true;
2167   int count, rc;
2168 
2169   Destroy(); // get rid of any residual stuff
2170 
2171   // STEP 1: REQUIRED - Read start section
2172   if ( !archive.Read3dmStartSection( &m_3dm_file_version, m_sStartSectionComments ) )
2173   {
2174     if ( error_log) error_log->Print("ERROR: Unable to read start section. (ON_BinaryArchive::Read3dmStartSection() returned false.)\n");
2175     return false;
2176   }
2177   else if ( CheckForCRCErrors( archive, *this, error_log, "start section" ) )
2178     return_code = false;
2179 
2180   // STEP 2: REQUIRED - Read properties section
2181   if ( !archive.Read3dmProperties( m_properties ) )
2182   {
2183     if ( error_log) error_log->Print("ERROR: Unable to read properties section. (ON_BinaryArchive::Read3dmProperties() returned false.)\n");
2184     return false;
2185   }
2186   else if ( CheckForCRCErrors( archive, *this, error_log, "properties section" ) )
2187     return_code = false;
2188 
2189   // version of opennurbs used to write the file.
2190   m_3dm_opennurbs_version = archive.ArchiveOpenNURBSVersion();
2191 
2192   // STEP 3: REQUIRED - Read properties section
2193   if ( !archive.Read3dmSettings( m_settings ) )
2194   {
2195     if ( error_log) error_log->Print("ERROR: Unable to read settings section. (ON_BinaryArchive::Read3dmSettings() returned false.)\n");
2196     return false;
2197   }
2198   else if ( CheckForCRCErrors( archive, *this, error_log, "settings section" ) )
2199     return_code = false;
2200 
2201   // STEP 4: REQUIRED - Read embedded bitmap table
2202   if ( archive.BeginRead3dmBitmapTable() )
2203   {
2204     // At the moment no bitmaps are embedded so this table is empty
2205     ON_Bitmap* pBitmap = NULL;
2206     for( count = 0; true; count++ )
2207     {
2208       pBitmap = NULL;
2209       rc = archive.Read3dmBitmap(&pBitmap);
2210       if ( rc==0 )
2211         break; // end of bitmap table
2212       if ( rc < 0 )
2213       {
2214         if ( error_log)
2215         {
2216           error_log->Print("ERROR: Corrupt bitmap found. (ON_BinaryArchive::Read3dmBitmap() < 0.)\n");
2217           error_count++;
2218           if ( error_count > max_error_count )
2219             return false;
2220           error_log->Print("-- Attempting to continue.\n");
2221         }
2222         return_code = false;
2223       }
2224       m_bitmap_table.Append(pBitmap);
2225     }
2226 
2227     // If BeginRead3dmBitmapTable() returns true,
2228     // then you MUST call EndRead3dmBitmapTable().
2229     if ( !archive.EndRead3dmBitmapTable() )
2230     {
2231       if ( error_log) error_log->Print("ERROR: Corrupt bitmap table. (ON_BinaryArchive::EndRead3dmBitmapTable() returned false.)\n");
2232       return false;
2233     }
2234     if ( CheckForCRCErrors( archive, *this, error_log, "bitmap table" ) )
2235       return_code = false;
2236   }
2237   else
2238   {
2239     if ( error_log)
2240     {
2241       error_log->Print("WARNING: Missing or corrupt bitmap table. (ON_BinaryArchive::BeginRead3dmBitmapTable() returned false.)\n");
2242       error_log->Print("-- Attempting to continue.\n");
2243     }
2244     return_code = false;
2245   }
2246 
2247 
2248 
2249   // STEP 5: REQUIRED - Read texture mapping table
2250   if ( archive.BeginRead3dmTextureMappingTable() )
2251   {
2252     ON_TextureMapping* pTextureMapping = NULL;
2253     for( count = 0; true; count++ )
2254     {
2255       rc = archive.Read3dmTextureMapping(&pTextureMapping);
2256       if ( rc==0 )
2257         break; // end of texture_mapping table
2258       if ( rc < 0 )
2259       {
2260         if ( error_log)
2261         {
2262           error_log->Print("ERROR: Corrupt render texture_mapping found. (ON_BinaryArchive::Read3dmTextureMapping() < 0.)\n");
2263           error_count++;
2264           if ( error_count > max_error_count )
2265             return false;
2266           error_log->Print("-- Attempting to continue.\n");
2267         }
2268         continue;
2269       }
2270       ON_UserDataHolder ud;
2271       ud.MoveUserDataFrom(*pTextureMapping);
2272       m_mapping_table.Append(*pTextureMapping);
2273       pTextureMapping->m_mapping_index = count;
2274       ud.MoveUserDataTo(*m_mapping_table.Last(),false);
2275       delete pTextureMapping;
2276       pTextureMapping = NULL;
2277     }
2278 
2279     // If BeginRead3dmTextureMappingTable() returns true,
2280     // then you MUST call EndRead3dmTextureMappingTable().
2281     if ( !archive.EndRead3dmTextureMappingTable() )
2282     {
2283       if ( error_log) error_log->Print("ERROR: Corrupt render texture_mapping table. (ON_BinaryArchive::EndRead3dmTextureMappingTable() returned false.)\n");
2284       return false;
2285     }
2286     if ( CheckForCRCErrors( archive, *this, error_log, "render texture_mapping table" ) )
2287       return_code = false;
2288   }
2289   else
2290   {
2291     if ( error_log)
2292     {
2293       error_log->Print("WARNING: Missing or corrupt render texture_mapping table. (ON_BinaryArchive::BeginRead3dmTextureMappingTable() returned false.)\n");
2294       error_log->Print("-- Attempting to continue.\n");
2295     }
2296     return_code = false;
2297   }
2298 
2299 
2300   // STEP 6: REQUIRED - Read render material table
2301   if ( archive.BeginRead3dmMaterialTable() )
2302   {
2303     ON_Material* pMaterial = NULL;
2304     for( count = 0; true; count++ )
2305     {
2306       rc = archive.Read3dmMaterial(&pMaterial);
2307       if ( rc==0 )
2308         break; // end of material table
2309       if ( rc < 0 )
2310       {
2311         if ( error_log)
2312         {
2313           error_log->Print("ERROR: Corrupt render material found. (ON_BinaryArchive::Read3dmMaterial() < 0.)\n");
2314           error_count++;
2315           if ( error_count > max_error_count )
2316             return false;
2317           error_log->Print("-- Attempting to continue.\n");
2318         }
2319         pMaterial = new ON_Material; // use default
2320         pMaterial->m_material_index = count;
2321       }
2322       ON_UserDataHolder ud;
2323       ud.MoveUserDataFrom(*pMaterial);
2324       m_material_table.Append(*pMaterial);
2325       ud.MoveUserDataTo(*m_material_table.Last(),false);
2326       delete pMaterial;
2327       pMaterial = NULL;
2328     }
2329 
2330     // If BeginRead3dmMaterialTable() returns true,
2331     // then you MUST call EndRead3dmMaterialTable().
2332     if ( !archive.EndRead3dmMaterialTable() )
2333     {
2334       if ( error_log) error_log->Print("ERROR: Corrupt render material table. (ON_BinaryArchive::EndRead3dmMaterialTable() returned false.)\n");
2335       return false;
2336     }
2337     if ( CheckForCRCErrors( archive, *this, error_log, "render material table" ) )
2338       return_code = false;
2339   }
2340   else
2341   {
2342     if ( error_log)
2343     {
2344       error_log->Print("WARNING: Missing or corrupt render material table. (ON_BinaryArchive::BeginRead3dmMaterialTable() returned false.)\n");
2345       error_log->Print("-- Attempting to continue.\n");
2346     }
2347     return_code = false;
2348   }
2349 
2350 
2351   // STEP 7: REQUIRED - Read line type table
2352   if ( archive.BeginRead3dmLinetypeTable() )
2353   {
2354     ON_Linetype* pLinetype = NULL;
2355     for( count = 0; true; count++ )
2356     {
2357       rc = archive.Read3dmLinetype(&pLinetype);
2358       if ( rc==0 )
2359         break; // end of linetype table
2360       if ( rc < 0 )
2361       {
2362         if ( error_log)
2363         {
2364           error_log->Print("ERROR: Corrupt render linetype found. (ON_BinaryArchive::Read3dmLinetype() < 0.)\n");
2365           error_count++;
2366           if ( error_count > max_error_count )
2367             return false;
2368           error_log->Print("-- Attempting to continue.\n");
2369         }
2370         pLinetype = new ON_Linetype; // use default
2371         pLinetype->m_linetype_index = count;
2372       }
2373       ON_UserDataHolder ud;
2374       ud.MoveUserDataFrom(*pLinetype);
2375       m_linetype_table.Append(*pLinetype);
2376       ud.MoveUserDataTo(*m_linetype_table.Last(),false);
2377       delete pLinetype;
2378       pLinetype = NULL;
2379     }
2380 
2381     // If BeginRead3dmLinetypeTable() returns true,
2382     // then you MUST call EndRead3dmLinetypeTable().
2383     if ( !archive.EndRead3dmLinetypeTable() )
2384     {
2385       if ( error_log) error_log->Print("ERROR: Corrupt render linetype table. (ON_BinaryArchive::EndRead3dmLinetypeTable() returned false.)\n");
2386       return false;
2387     }
2388     if ( CheckForCRCErrors( archive, *this, error_log, "render linetype table" ) )
2389       return_code = false;
2390   }
2391   else
2392   {
2393     if ( error_log)
2394     {
2395       error_log->Print("WARNING: Missing or corrupt render linetype table. (ON_BinaryArchive::BeginRead3dmLinetypeTable() returned false.)\n");
2396       error_log->Print("-- Attempting to continue.\n");
2397     }
2398     return_code = false;
2399   }
2400 
2401   // STEP 8: REQUIRED - Read layer table
2402   if ( archive.BeginRead3dmLayerTable() )
2403   {
2404     ON_Layer* pLayer = NULL;
2405     for( count = 0; true; count++ )
2406     {
2407       pLayer = NULL;
2408       rc = archive.Read3dmLayer(&pLayer);
2409       if ( rc==0 )
2410         break; // end of layer table
2411       if ( rc < 0 )
2412       {
2413         if ( error_log)
2414         {
2415           error_log->Print("ERROR: Corrupt layer found. (ON_BinaryArchive::Read3dmLayer() < 0.)\n");
2416           error_count++;
2417           if ( error_count > max_error_count )
2418             return false;
2419           error_log->Print("-- Attempting to continue.\n");
2420         }
2421         pLayer = new ON_Layer; // use default
2422         pLayer->m_layer_index = count;
2423       }
2424       ON_UserDataHolder ud;
2425       ud.MoveUserDataFrom(*pLayer);
2426       m_layer_table.Append(*pLayer);
2427       ud.MoveUserDataTo(*m_layer_table.Last(),false);
2428       delete pLayer;
2429       pLayer = NULL;
2430     }
2431 
2432     // If BeginRead3dmLayerTable() returns true,
2433     // then you MUST call EndRead3dmLayerTable().
2434     if ( !archive.EndRead3dmLayerTable() )
2435     {
2436       if ( error_log) error_log->Print("ERROR: Corrupt render layer table. (ON_BinaryArchive::EndRead3dmLayerTable() returned false.)\n");
2437       return false;
2438     }
2439     if ( CheckForCRCErrors( archive, *this, error_log, "layer table" ) )
2440       return_code = false;
2441   }
2442   else
2443   {
2444     if ( error_log)
2445     {
2446       error_log->Print("WARNING: Missing or corrupt layer table. (ON_BinaryArchive::BeginRead3dmLayerTable() returned false.)\n");
2447       error_log->Print("-- Attempting to continue.\n");
2448     }
2449     return_code = false;
2450   }
2451 
2452   // STEP 9: REQUIRED - Read group table
2453   if ( archive.BeginRead3dmGroupTable() )
2454   {
2455     ON_Group* pGroup = NULL;
2456     for( count = 0; true; count++ )
2457     {
2458       rc = archive.Read3dmGroup(&pGroup);
2459       if ( rc==0 )
2460         break; // end of group table
2461       if ( rc < 0 )
2462       {
2463         if ( error_log)
2464         {
2465           error_log->Print("ERROR: Corrupt group found. (ON_BinaryArchive::Read3dmGroup() < 0.)\n");
2466           error_count++;
2467           if ( error_count > max_error_count )
2468             return false;
2469           error_log->Print("-- Attempting to continue.\n");
2470         }
2471         pGroup = new ON_Group; // use default
2472         pGroup->m_group_index = -1;
2473       }
2474       ON_UserDataHolder ud;
2475       ud.MoveUserDataFrom(*pGroup);
2476       m_group_table.Append(*pGroup);
2477       ud.MoveUserDataTo(*m_group_table.Last(),false);
2478       delete pGroup;
2479       pGroup = NULL;
2480     }
2481 
2482     // If BeginRead3dmGroupTable() returns true,
2483     // then you MUST call EndRead3dmGroupTable().
2484     if ( !archive.EndRead3dmGroupTable() )
2485     {
2486       if ( error_log) error_log->Print("ERROR: Corrupt group table. (ON_BinaryArchive::EndRead3dmGroupTable() returned false.)\n");
2487       return false;
2488     }
2489     if ( CheckForCRCErrors( archive, *this, error_log, "group table" ) )
2490       return_code = false;
2491   }
2492   else
2493   {
2494     if ( error_log)
2495     {
2496       error_log->Print("WARNING: Missing or corrupt group table. (ON_BinaryArchive::BeginRead3dmGroupTable() returned false.)\n");
2497       error_log->Print("-- Attempting to continue.\n");
2498     }
2499     return_code = false;
2500   }
2501 
2502   // STEP 10: REQUIRED - Read font table
2503   if ( archive.BeginRead3dmFontTable() )
2504   {
2505     ON_Font* pFont = NULL;
2506     for( count = 0; true; count++ )
2507     {
2508       rc = archive.Read3dmFont(&pFont);
2509       if ( rc==0 )
2510         break; // end of font table
2511       if ( rc < 0 )
2512       {
2513         if ( error_log)
2514         {
2515           error_log->Print("ERROR: Corrupt font found. (ON_BinaryArchive::Read3dmFont() < 0.)\n");
2516           error_count++;
2517           if ( error_count > max_error_count )
2518             return false;
2519           error_log->Print("-- Attempting to continue.\n");
2520         }
2521         pFont = new ON_Font; // use default
2522         pFont->m_font_index = -1;
2523       }
2524       ON_UserDataHolder ud;
2525       ud.MoveUserDataFrom(*pFont);
2526       m_font_table.Append(*pFont);
2527       ud.MoveUserDataTo(*m_font_table.Last(),false);
2528       delete pFont;
2529       pFont = NULL;
2530     }
2531 
2532     // If BeginRead3dmFontTable() returns true,
2533     // then you MUST call EndRead3dmFontTable().
2534     if ( !archive.EndRead3dmFontTable() )
2535     {
2536       if ( error_log) error_log->Print("ERROR: Corrupt font table. (ON_BinaryArchive::EndRead3dmFontTable() returned false.)\n");
2537       return false;
2538     }
2539     if ( CheckForCRCErrors( archive, *this, error_log, "font table" ) )
2540       return_code = false;
2541   }
2542   else
2543   {
2544     if ( error_log)
2545     {
2546       error_log->Print("WARNING: Missing or corrupt font table. (ON_BinaryArchive::BeginRead3dmFontTable() returned false.)\n");
2547       error_log->Print("-- Attempting to continue.\n");
2548     }
2549     return_code = false;
2550   }
2551 
2552   // STEP 11: REQUIRED - Read dimstyle table
2553   if ( archive.BeginRead3dmDimStyleTable() )
2554   {
2555     ON_DimStyle* pDimStyle = NULL;
2556     for( count = 0; true; count++ )
2557     {
2558       rc = archive.Read3dmDimStyle(&pDimStyle);
2559       if ( rc==0 )
2560         break; // end of dimstyle table
2561       if ( rc < 0 )
2562       {
2563         if ( error_log)
2564         {
2565           error_log->Print("ERROR: Corrupt dimstyle found. (ON_BinaryArchive::Read3dmDimStyle() < 0.)\n");
2566           error_count++;
2567           if ( error_count > max_error_count )
2568             return false;
2569           error_log->Print("-- Attempting to continue.\n");
2570         }
2571         pDimStyle = new ON_DimStyle; // use default
2572         pDimStyle->m_dimstyle_index = count;
2573       }
2574       ON_UserDataHolder ud;
2575       ud.MoveUserDataFrom(*pDimStyle);
2576       m_dimstyle_table.Append(*pDimStyle);
2577       ud.MoveUserDataTo(*m_dimstyle_table.Last(),false);
2578       delete pDimStyle;
2579       pDimStyle = NULL;
2580     }
2581 
2582     // If BeginRead3dmDimStyleTable() returns true,
2583     // then you MUST call EndRead3dmDimStyleTable().
2584     if ( !archive.EndRead3dmDimStyleTable() )
2585     {
2586       if ( error_log) error_log->Print("ERROR: Corrupt dimstyle table. (ON_BinaryArchive::EndRead3dmDimStyleTable() returned false.)\n");
2587       return false;
2588     }
2589     if ( CheckForCRCErrors( archive, *this, error_log, "dimstyle table" ) )
2590       return_code = false;
2591   }
2592   else
2593   {
2594     if ( error_log)
2595     {
2596       error_log->Print("WARNING: Missing or corrupt dimstyle table. (ON_BinaryArchive::BeginRead3dmDimStyleTable() returned false.)\n");
2597       error_log->Print("-- Attempting to continue.\n");
2598     }
2599     return_code = false;
2600   }
2601 
2602   // STEP 12: REQUIRED - Read render lights table
2603   if ( archive.BeginRead3dmLightTable() )
2604   {
2605     ON_Light* pLight = NULL;
2606     ON_3dmObjectAttributes object_attributes;
2607     for( count = 0; true; count++ )
2608     {
2609       object_attributes.Default();
2610       rc = archive.Read3dmLight(&pLight,&object_attributes);
2611       if ( rc==0 )
2612         break; // end of light table
2613       if ( rc < 0 )
2614       {
2615         if ( error_log)
2616         {
2617           error_log->Print("ERROR: Corrupt render light found. (ON_BinaryArchive::Read3dmLight() < 0.)\n");
2618           error_count++;
2619           if ( error_count > max_error_count )
2620             return false;
2621           error_log->Print("-- Attempting to continue.\n");
2622         }
2623         continue;
2624       }
2625       ONX_Model_RenderLight& light = m_light_table.AppendNew();
2626       ON_UserDataHolder ud;
2627       ud.MoveUserDataFrom(*pLight);
2628       light.m_light = *pLight;
2629       ud.MoveUserDataTo(light.m_light,false);
2630       light.m_attributes = object_attributes;
2631       delete pLight;
2632       pLight = NULL;
2633     }
2634 
2635     // If BeginRead3dmLightTable() returns true,
2636     // then you MUST call EndRead3dmLightTable().
2637     if ( !archive.EndRead3dmLightTable() )
2638     {
2639       if ( error_log) error_log->Print("ERROR: Corrupt render light table. (ON_BinaryArchive::EndRead3dmLightTable() returned false.)\n");
2640       return false;
2641     }
2642     if ( CheckForCRCErrors( archive, *this, error_log, "render light table" ) )
2643       return_code = false;
2644   }
2645   else
2646   {
2647     if ( error_log)
2648     {
2649       error_log->Print("WARNING: Missing or corrupt render light table. (ON_BinaryArchive::BeginRead3dmLightTable() returned false.)\n");
2650       error_log->Print("-- Attempting to continue.\n");
2651     }
2652     return_code = false;
2653   }
2654 
2655   // STEP 13 - read hatch pattern table
2656   if ( archive.BeginRead3dmHatchPatternTable() )
2657   {
2658     ON_HatchPattern* pHatchPattern = NULL;
2659     for( count = 0; true; count++ )
2660     {
2661       rc = archive.Read3dmHatchPattern(&pHatchPattern);
2662       if ( rc==0 )
2663         break; // end of hatchpattern table
2664       if ( rc < 0 )
2665       {
2666         if ( error_log)
2667         {
2668           error_log->Print("ERROR: Corrupt hatchpattern found. (ON_BinaryArchive::Read3dmHatchPattern() < 0.)\n");
2669           error_count++;
2670           if ( error_count > max_error_count )
2671             return false;
2672           error_log->Print("-- Attempting to continue.\n");
2673         }
2674         pHatchPattern = new ON_HatchPattern; // use default
2675         pHatchPattern->m_hatchpattern_index = count;
2676       }
2677       ON_UserDataHolder ud;
2678       ud.MoveUserDataFrom(*pHatchPattern);
2679       m_hatch_pattern_table.Append(*pHatchPattern);
2680       ud.MoveUserDataTo(*m_hatch_pattern_table.Last(),false);
2681       delete pHatchPattern;
2682       pHatchPattern = NULL;
2683     }
2684 
2685     // If BeginRead3dmHatchPatternTable() returns true,
2686     // then you MUST call EndRead3dmHatchPatternTable().
2687     if ( !archive.EndRead3dmHatchPatternTable() )
2688     {
2689       if ( error_log) error_log->Print("ERROR: Corrupt hatchpattern table. (ON_BinaryArchive::EndRead3dmHatchPatternTable() returned false.)\n");
2690       return false;
2691     }
2692     if ( CheckForCRCErrors( archive, *this, error_log, "hatchpattern table" ) )
2693       return_code = false;
2694   }
2695   else
2696   {
2697     if ( error_log)
2698     {
2699       error_log->Print("WARNING: Missing or corrupt hatchpattern table. (ON_BinaryArchive::BeginRead3dmHatchPatternTable() returned false.)\n");
2700       error_log->Print("-- Attempting to continue.\n");
2701     }
2702     return_code = false;
2703   }
2704 
2705   // STEP 14: REQUIRED - Read instance definition table
2706   if ( archive.BeginRead3dmInstanceDefinitionTable() )
2707   {
2708     ON_InstanceDefinition* pIDef = NULL;
2709     for( count = 0; true; count++ )
2710     {
2711       rc = archive.Read3dmInstanceDefinition(&pIDef);
2712       if ( rc==0 )
2713         break; // end of instance definition table
2714       if ( rc < 0 )
2715       {
2716         if ( error_log)
2717         {
2718           error_log->Print("ERROR: Corrupt instance definition found. (ON_BinaryArchive::Read3dmInstanceDefinition() < 0.)\n");
2719           error_count++;
2720           if ( error_count > max_error_count )
2721             return false;
2722           error_log->Print("-- Attempting to continue.\n");
2723         }
2724         continue;
2725       }
2726       ON_UserDataHolder ud;
2727       ud.MoveUserDataFrom(*pIDef);
2728       m_idef_table.Append(*pIDef);
2729       ud.MoveUserDataTo(*m_idef_table.Last(),false);
2730       delete pIDef;
2731     }
2732 
2733     // If BeginRead3dmInstanceDefinitionTable() returns true,
2734     // then you MUST call EndRead3dmInstanceDefinitionTable().
2735     if ( !archive.EndRead3dmInstanceDefinitionTable() )
2736     {
2737       if ( error_log) error_log->Print("ERROR: Corrupt instance definition table. (ON_BinaryArchive::EndRead3dmInstanceDefinitionTable() returned false.)\n");
2738       return false;
2739     }
2740     if ( CheckForCRCErrors( archive, *this, error_log, "instance definition table" ) )
2741       return_code = false;
2742   }
2743   else
2744   {
2745     if ( error_log)
2746     {
2747       error_log->Print("WARNING: Missing or corrupt instance definition table. (ON_BinaryArchive::BeginRead3dmInstanceDefinitionTable() returned false.)\n");
2748       error_log->Print("-- Attempting to continue.\n");
2749     }
2750     return_code = false;
2751   }
2752 
2753 
2754 
2755   // STEP 15: REQUIRED - Read object (geometry and annotation) table
2756   if ( archive.BeginRead3dmObjectTable() )
2757   {
2758     // optional filter made by setting ON::object_type bits
2759     // For example, if you just wanted to just read points and meshes, you would use
2760     // object_filter = ON::point_object | ON::mesh_object;
2761     int object_filter = 0;
2762 
2763     for( count = 0; true; count++ )
2764     {
2765       ON_Object* pObject = NULL;
2766       ON_3dmObjectAttributes attributes;
2767       rc = archive.Read3dmObject(&pObject,&attributes,object_filter);
2768       if ( rc == 0 )
2769         break; // end of object table
2770       if ( rc < 0 )
2771       {
2772         if ( error_log)
2773         {
2774           error_log->Print("ERROR: Object table entry %d is corrupt. (ON_BinaryArchive::Read3dmObject() < 0.)\n",count);
2775           error_count++;
2776           if ( error_count > max_error_count )
2777             return false;
2778           error_log->Print("-- Attempting to continue.\n");
2779         }
2780         continue;
2781       }
2782       if ( m_crc_error_count != archive.BadCRCCount() )
2783       {
2784         if ( error_log)
2785         {
2786           error_log->Print("ERROR: Object table entry %d is corrupt. (CRC errors).\n",count);
2787           error_log->Print("-- Attempting to continue.\n");
2788         }
2789         m_crc_error_count = archive.BadCRCCount();
2790       }
2791       if ( pObject )
2792       {
2793         ONX_Model_Object& mo = m_object_table.AppendNew();
2794         mo.m_object = pObject;
2795         mo.m_bDeleteObject = true;
2796         mo.m_attributes = attributes;
2797       }
2798       else
2799       {
2800         if ( error_log)
2801         {
2802           if ( rc == 2 )
2803             error_log->Print("WARNING: Skipping object table entry %d because it's filtered.\n",count);
2804           else if ( rc == 3 )
2805             error_log->Print("WARNING: Skipping object table entry %d because it's newer than this code.  Update your OpenNURBS toolkit.\n",count);
2806           else
2807             error_log->Print("WARNING: Skipping object table entry %d for unknown reason.\n",count);
2808         }
2809       }
2810     }
2811 
2812     // If BeginRead3dmObjectTable() returns true,
2813     // then you MUST call EndRead3dmObjectTable().
2814     if ( !archive.EndRead3dmObjectTable() )
2815     {
2816       if ( error_log) error_log->Print("ERROR: Corrupt object light table. (ON_BinaryArchive::EndRead3dmObjectTable() returned false.)\n");
2817       return false;
2818     }
2819     if ( CheckForCRCErrors( archive, *this, error_log, "object table" ) )
2820       return_code = false;
2821   }
2822   else
2823   {
2824     if ( error_log)
2825     {
2826       error_log->Print("WARNING: Missing or corrupt object table. (ON_BinaryArchive::BeginRead3dmObjectTable() returned false.)\n");
2827       error_log->Print("-- Attempting to continue.\n");
2828     }
2829     return_code = false;
2830   }
2831 
2832   // STEP 16: Read history table
2833   if ( archive.BeginRead3dmHistoryRecordTable() )
2834   {
2835     for( count = 0; true; count++ )
2836     {
2837       ON_HistoryRecord* pHistoryRecord = NULL;
2838       rc = archive.Read3dmHistoryRecord(pHistoryRecord);
2839       if ( rc == 0 )
2840         break; // end of history record table
2841       if ( rc < 0 )
2842       {
2843         if ( error_log)
2844         {
2845           error_log->Print("ERROR: History record table entry %d is corrupt. (ON_BinaryArchive::Read3dmHistoryRecord() < 0.)\n",count);
2846           error_count++;
2847           if ( error_count > max_error_count )
2848             return false;
2849           error_log->Print("-- Attempting to continue.\n");
2850         }
2851         continue;
2852       }
2853       if ( m_crc_error_count != archive.BadCRCCount() )
2854       {
2855         if ( error_log)
2856         {
2857           error_log->Print("ERROR: History record table entry %d is corrupt. (CRC errors).\n",count);
2858           error_log->Print("-- Attempting to continue.\n");
2859         }
2860         m_crc_error_count = archive.BadCRCCount();
2861       }
2862       if ( pHistoryRecord )
2863       {
2864         m_history_record_table.Append(pHistoryRecord);
2865       }
2866       else
2867       {
2868         if ( error_log)
2869         {
2870           error_log->Print("WARNING: Skipping history record table entry %d for unknown reason.\n",count);
2871         }
2872       }
2873     }
2874 
2875     // If BeginRead3dmHistoryRecordTable() returns true,
2876     // then you MUST call EndRead3dmHistoryRecordTable().
2877     if ( !archive.EndRead3dmHistoryRecordTable() )
2878     {
2879       if ( error_log) error_log->Print("ERROR: Corrupt object light table. (ON_BinaryArchive::EndRead3dmObjectTable() returned false.)\n");
2880       return false;
2881     }
2882     if ( CheckForCRCErrors( archive, *this, error_log, "history record table" ) )
2883       return_code = false;
2884   }
2885   else
2886   {
2887     if ( error_log)
2888     {
2889       error_log->Print("WARNING: Missing or corrupt history record table. (ON_BinaryArchive::BeginRead3dmHistoryRecordTable() returned false.)\n");
2890       error_log->Print("-- Attempting to continue.\n");
2891     }
2892     return_code = false;
2893   }
2894 
2895   // STEP 17: OPTIONAL - Read user tables as anonymous goo
2896   // If you develop a plug-ins or application that uses OpenNURBS files,
2897   // you can store anything you want in a user table.
2898   for(count=0;true;count++)
2899   {
2900     if ( archive.Archive3dmVersion() <= 1 )
2901     {
2902       // no user tables in version 1 archives.
2903       break;
2904     }
2905 
2906     {
2907       ON__UINT32 tcode = 0;
2908       ON__INT64 big_value = 0;
2909       if ( !archive.PeekAt3dmBigChunkType(&tcode,&big_value) )
2910         break;
2911       if ( TCODE_USER_TABLE != tcode )
2912         break;
2913     }
2914     ON_UUID plugin_id = ON_nil_uuid;
2915     bool bGoo = false;
2916     int usertable_3dm_version = 0;
2917     int usertable_opennurbs_version = 0;
2918     if ( !archive.BeginRead3dmUserTable( plugin_id, &bGoo, &usertable_3dm_version, &usertable_opennurbs_version ) )
2919     {
2920       // attempt to skip bogus user table
2921       const ON__UINT64 pos0 = archive.CurrentPosition();
2922       ON__UINT32 tcode = 0;
2923       ON__INT64 big_value = 0;
2924       if  ( !archive.BeginRead3dmBigChunk(&tcode,&big_value) )
2925         break;
2926       if ( !archive.EndRead3dmChunk() )
2927         break;
2928       const ON__UINT64 pos1 = archive.CurrentPosition();
2929       if (pos1 <= pos0)
2930         break;
2931       if ( TCODE_USER_TABLE != tcode )
2932         break;
2933 
2934       continue; // skip this bogus user table
2935     }
2936 
2937     ONX_Model_UserData& ud = m_userdata_table.AppendNew();
2938     ud.m_uuid = plugin_id;
2939     ud.m_usertable_3dm_version = usertable_3dm_version;
2940     ud.m_usertable_opennurbs_version = usertable_opennurbs_version;
2941 
2942     if ( !archive.Read3dmAnonymousUserTable( usertable_3dm_version, usertable_opennurbs_version, ud.m_goo ) )
2943     {
2944       if ( error_log) error_log->Print("ERROR: User data table entry %d is corrupt. (ON_BinaryArchive::Read3dmAnonymousUserTable() is false.)\n",count);
2945       break;
2946     }
2947 
2948     // If BeginRead3dmObjectTable() returns true,
2949     // then you MUST call EndRead3dmUserTable().
2950     if ( !archive.EndRead3dmUserTable() )
2951     {
2952       if ( error_log) error_log->Print("ERROR: Corrupt user data table. (ON_BinaryArchive::EndRead3dmUserTable() returned false.)\n");
2953       break;
2954     }
2955   }
2956 
2957   // STEP 18: OPTIONAL - check for end mark
2958   if ( !archive.Read3dmEndMark(&m_file_length) )
2959   {
2960     if ( archive.Archive3dmVersion() != 1 )
2961     {
2962       // some v1 files are missing end-of-archive markers
2963       if ( error_log) error_log->Print("ERROR: ON_BinaryArchive::Read3dmEndMark(&m_file_length) returned false.\n");
2964     }
2965   }
2966 
2967   // Remap layer, material, linetype, font, dimstyle, hatch pattern, etc.,
2968   // indices so the correspond to the model's table array index.
2969   //
2970   // Polish also sets revision history information if it is missing.
2971   // In this case, that is not appropriate so the value of
2972   // m_properties.m_RevisionHistory is saved before calling Polish()
2973   // and restored afterwards.
2974   const ON_3dmRevisionHistory saved_revision_history(m_properties.m_RevisionHistory);
2975   Polish();
2976   m_properties.m_RevisionHistory = saved_revision_history;
2977 
2978   return return_code;
2979 }
2980 
2981 static
ONX_Model_WriteHelper(ON_BinaryFile & file)2982 void ONX_Model_WriteHelper(ON_BinaryFile& file)
2983 {
2984   file.EnableSave3dmRenderMeshes(true);
2985   file.EnableSave3dmAnalysisMeshes(true);
2986   file.EnableSaveUserData(true);
2987 }
2988 
Write(const char * filename,int version,const char * sStartSectionComment,ON_TextLog * error_log)2989 bool ONX_Model::Write(
2990        const char* filename,
2991        int version,
2992        const char* sStartSectionComment,
2993        ON_TextLog* error_log
2994        )
2995 {
2996   bool rc = false;
2997   if ( 0 != filename )
2998   {
2999     FILE* fp = ON::OpenFile( filename, "wb" );
3000     if ( 0 != fp )
3001     {
3002       ON_BinaryFile file( ON::write3dm, fp );
3003       ONX_Model_WriteHelper(file);
3004       rc = Write( file, version, sStartSectionComment, error_log );
3005       ON::CloseFile(fp);
3006     }
3007   }
3008   return rc;
3009 }
3010 
Write(const wchar_t * filename,int version,const char * sStartSectionComment,ON_TextLog * error_log)3011 bool ONX_Model::Write(
3012        const wchar_t* filename,
3013        int version,
3014        const char* sStartSectionComment,
3015        ON_TextLog* error_log
3016        )
3017 {
3018   bool rc = false;
3019   if ( 0 != filename )
3020   {
3021     FILE* fp = ON::OpenFile( filename, L"wb" );
3022     if ( 0 != fp )
3023     {
3024       ON_BinaryFile file( ON::write3dm, fp );
3025       ONX_Model_WriteHelper(file);
3026       rc = Write( file, version, sStartSectionComment, error_log );
3027       ON::CloseFile(fp);
3028     }
3029   }
3030   return rc;
3031 }
3032 
3033 
Write(ON_BinaryArchive & archive,int version,const char *,ON_TextLog * error_log)3034 bool ONX_Model::Write(
3035        ON_BinaryArchive& archive,
3036        int version,
3037        const char*,
3038        ON_TextLog* error_log
3039        )
3040 {
3041   int i;
3042 
3043   if ( !IsValid(error_log) )
3044   {
3045     // This model is not valid.  See the error_log for details.
3046     if ( error_log) error_log->Print("ONX_Model::Write Your model is not valid and will not be saved.\n");
3047     return false;
3048   }
3049 
3050   if ( 0 != version )
3051   {
3052     if (    version < 2
3053          || version > ON_BinaryArchive::CurrentArchiveVersion()
3054          || (version >= 50 && 0 != (version%10))
3055          || (version < 50 && version > ON_BinaryArchive::CurrentArchiveVersion()/10)
3056          )
3057     {
3058       // version must be 0, 2, 3, 4, 5 or 50
3059       version = 0;
3060       if ( error_log) error_log->Print("ONX_Model::Write version parameter = %d; it must be 0, or >= 2 and <= %d, or a multiple of 10 >= 50 and <= %d.\n",
3061                       version,ON_BinaryArchive::CurrentArchiveVersion()/10,ON_BinaryArchive::CurrentArchiveVersion());
3062     }
3063   }
3064 
3065   if ( !archive.WriteMode() )
3066   {
3067     // You passed in a bogus archive.  You must pass ON::write3dm to the
3068     // archive constructor.
3069     if ( error_log) error_log->Print("ONX_Model::Write archive.Mode() is not ON::write3dm.\n"
3070                     "See ONX_Model::Write example in the header file.\n");
3071     return false;
3072   }
3073 
3074   bool ok;
3075 
3076   // START SECTION
3077   ok = archive.Write3dmStartSection( version, m_sStartSectionComments );
3078   if ( !ok )
3079   {
3080     // make sure your archive was created with ON::write3dm mode.
3081     if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmStartSection() failed.\n"
3082                     "Your archive is not properly initialized\n"
3083                     "(make sure you passed ON::write3dm to the constructor),\n"
3084                     "a file is locked, a disk is locked, or something along those lines.\n");
3085     return false;
3086   }
3087 
3088   // PROPERTIES SECTION
3089   ok = archive.Write3dmProperties( m_properties );
3090   if ( !ok )
3091   {
3092     // make sure m_properties is valid
3093     if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmProperties() failed.\n"
3094                     "Your m_properties information is not valid or basic file writing failed.\n"
3095                     );
3096     return false;
3097   }
3098 
3099   // SETTINGS SECTION
3100   ok = archive.Write3dmSettings( m_settings );
3101   if ( !ok )
3102   {
3103     // make sure m_settings is valid
3104     if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmSettings() failed.\n"
3105                     "Your m_settings information is not valid or basic file writing failed.\n");
3106     return false;
3107   }
3108 
3109   // BITMAP TABLE
3110   ok = archive.BeginWrite3dmBitmapTable();
3111   if ( !ok )
3112   {
3113     if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmBitmapTable() failed.\n");
3114     return false;
3115   }
3116   for( i = 0; ok && i < m_bitmap_table.Count(); i++ )
3117   {
3118     ok = archive.Write3dmBitmap(*m_bitmap_table[i]);
3119     if ( !ok )
3120     {
3121       if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmBitmap(m_bitmap_table[%d]) failed.\n",i);
3122     }
3123   }
3124   if ( !archive.EndWrite3dmBitmapTable() )
3125   {
3126     if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmBitmapTable() failed.\n");
3127     return false;
3128   }
3129   if (!ok)
3130     return false;
3131 
3132   // RENDER TEXTURE MAPPING TABLE
3133   if ( archive.Archive3dmVersion() >= 4 )
3134   {
3135     ok = archive.BeginWrite3dmTextureMappingTable();
3136     if ( !ok )
3137     {
3138       if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmTextureMappingTable() failed.\n");
3139       return false;
3140     }
3141     for( i = 0; ok && i < m_mapping_table.Count(); i++ )
3142     {
3143       ok = archive.Write3dmTextureMapping(m_mapping_table[i]);
3144       if ( !ok )
3145       {
3146         if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmTextureMapping(m_mapping_table[%d]) failed.\n",i);
3147       }
3148     }
3149     if ( !archive.EndWrite3dmTextureMappingTable() )
3150     {
3151       if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmTextureMappingTable() failed.\n");
3152       return false;
3153     }
3154     if (!ok)
3155       return false;
3156   }
3157 
3158   // RENDER MATERIAL TABLE
3159   ok = archive.BeginWrite3dmMaterialTable();
3160   if ( !ok )
3161   {
3162     if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmMaterialTable() failed.\n");
3163     return false;
3164   }
3165   for( i = 0; ok && i < m_material_table.Count(); i++ )
3166   {
3167     ok = archive.Write3dmMaterial(m_material_table[i]);
3168     if ( !ok )
3169     {
3170       if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmMaterial(m_material_table[%d]) failed.\n",i);
3171     }
3172   }
3173   if ( !archive.EndWrite3dmMaterialTable() )
3174   {
3175     if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmMaterialTable() failed.\n");
3176     return false;
3177   }
3178   if (!ok)
3179     return false;
3180 
3181 
3182   // LINETYPE TABLE
3183   if ( archive.Archive3dmVersion() >= 4 )
3184   {
3185     ok = archive.BeginWrite3dmLinetypeTable();
3186     if ( !ok )
3187     {
3188       if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmLinetypeTable() failed.\n");
3189       return false;
3190     }
3191     for( i = 0; ok && i < m_linetype_table.Count(); i++ )
3192     {
3193       ok = archive.Write3dmLinetype(m_linetype_table[i]);
3194       if ( !ok )
3195       {
3196         if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmLinetype(m_linetype_table[%d]) failed.\n",i);
3197       }
3198     }
3199     if ( !archive.EndWrite3dmLinetypeTable() )
3200     {
3201       if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmLinetypeTable() failed.\n");
3202       return false;
3203     }
3204     if (!ok)
3205       return false;
3206   }
3207 
3208   // LAYER TABLE
3209   ok = archive.BeginWrite3dmLayerTable();
3210   if ( !ok )
3211   {
3212     // make sure m_settings is valid
3213     if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmLayerTable() failed.\n");
3214     return false;
3215   }
3216   for( i = 0; ok && i < m_layer_table.Count(); i++ )
3217   {
3218     ok = archive.Write3dmLayer(m_layer_table[i]);
3219     if ( !ok )
3220     {
3221       if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmLayer(m_layer_table[%d]) failed.\n",i);
3222     }
3223   }
3224   if ( !archive.EndWrite3dmLayerTable() )
3225   {
3226     if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmLayerTable() failed.\n");
3227     return false;
3228   }
3229   if (!ok)
3230     return false;
3231 
3232   // GROUP TABLE
3233   ok = archive.BeginWrite3dmGroupTable();
3234   if ( !ok )
3235   {
3236     // make sure m_settings is valid
3237     if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmGroupTable() failed.\n");
3238     return false;
3239   }
3240   for( i = 0; ok && i < m_group_table.Count(); i++ )
3241   {
3242     ok = archive.Write3dmGroup(m_group_table[i]);
3243     if ( !ok )
3244     {
3245       if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmGroup(m_group_table[%d]) failed.\n",i);
3246     }
3247   }
3248   if ( !archive.EndWrite3dmGroupTable() )
3249   {
3250     if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmGroupTable() failed.\n");
3251     return false;
3252   }
3253   if (!ok)
3254     return false;
3255 
3256 
3257   // FONT TABLE
3258   if ( archive.Archive3dmVersion() >= 3 )
3259   {
3260     ok = archive.BeginWrite3dmFontTable();
3261     if ( !ok )
3262     {
3263       // make sure m_settings is valid
3264       if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmFontTable() failed.\n");
3265       return false;
3266     }
3267     for( i = 0; ok && i < m_font_table.Count(); i++ )
3268     {
3269       ok = archive.Write3dmFont(m_font_table[i]);
3270       if ( !ok )
3271       {
3272         if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmFont(m_font_table[%d]) failed.\n",i);
3273       }
3274     }
3275     if ( !archive.EndWrite3dmFontTable() )
3276     {
3277       if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmFontTable() failed.\n");
3278       return false;
3279     }
3280     if (!ok)
3281       return false;
3282   }
3283 
3284 
3285   // DIMSTYLE TABLE
3286   if ( archive.Archive3dmVersion() >= 3 )
3287   {
3288     ok = archive.BeginWrite3dmDimStyleTable();
3289     if ( !ok )
3290     {
3291       // make sure m_settings is valid
3292       if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmDimStyleTable() failed.\n");
3293       return false;
3294     }
3295     for( i = 0; ok && i < m_dimstyle_table.Count(); i++ )
3296     {
3297       ok = archive.Write3dmDimStyle(m_dimstyle_table[i]);
3298       if ( !ok )
3299       {
3300         if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmDimStyle(m_dimstyle_table[%d]) failed.\n",i);
3301       }
3302     }
3303     if ( !archive.EndWrite3dmDimStyleTable() )
3304     {
3305       if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmDimStyleTable() failed.\n");
3306       return false;
3307     }
3308     if (!ok)
3309       return false;
3310   }
3311 
3312 
3313   // LIGHT TABLE
3314   ok = archive.BeginWrite3dmLightTable();
3315   if ( !ok )
3316   {
3317     // make sure m_settings is valid
3318     if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmLightTable() failed.\n");
3319     return false;
3320   }
3321   for( i = 0; ok && i < m_light_table.Count(); i++ )
3322   {
3323     ok = archive.Write3dmLight(m_light_table[i].m_light,&m_light_table[i].m_attributes);
3324     if ( !ok )
3325     {
3326       if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmLight(m_light_table[%d]) failed.\n",i);
3327     }
3328   }
3329   if ( !archive.EndWrite3dmLightTable() )
3330   {
3331     if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmLightTable() failed.\n");
3332     return false;
3333   }
3334   if (!ok)
3335     return false;
3336 
3337 
3338   // HATCH PATTERN TABLE
3339   if ( archive.Archive3dmVersion() >= 4 )
3340   {
3341     ok = archive.BeginWrite3dmHatchPatternTable();
3342     if ( !ok )
3343     {
3344       if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmHatchPatternTable() failed.\n");
3345       return false;
3346     }
3347     for( i = 0; ok && i < m_hatch_pattern_table.Count(); i++ )
3348     {
3349       ok = archive.Write3dmHatchPattern(m_hatch_pattern_table[i]);
3350       if ( !ok )
3351       {
3352         if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmHatchPattern(m_hatch_pattern_table[%d]) failed.\n",i);
3353       }
3354     }
3355     if ( !archive.EndWrite3dmHatchPatternTable() )
3356     {
3357       if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmHatchPatternTable() failed.\n");
3358       return false;
3359     }
3360     if (!ok)
3361       return false;
3362   }
3363 
3364 
3365   // INSTANCE DEFINITION TABLE
3366   if ( archive.Archive3dmVersion() >= 3 )
3367   {
3368     ok = archive.BeginWrite3dmInstanceDefinitionTable();
3369     if ( !ok )
3370     {
3371       // make sure m_settings is valid
3372       if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmInstanceDefinitionTable() failed.\n");
3373       return false;
3374     }
3375     for( i = 0; ok && i < m_idef_table.Count(); i++ )
3376     {
3377       ok = archive.Write3dmInstanceDefinition(m_idef_table[i]);
3378       if ( !ok )
3379       {
3380         if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmInstanceDefinition(m_IDef_table[%d]) failed.\n",i);
3381       }
3382     }
3383     if ( !archive.EndWrite3dmInstanceDefinitionTable() )
3384     {
3385       if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmInstanceDefinitionTable() failed.\n");
3386       return false;
3387     }
3388     if (!ok)
3389       return false;
3390   }
3391 
3392 
3393   // OBJECT TABLE
3394   ok = archive.BeginWrite3dmObjectTable();
3395   if ( !ok )
3396   {
3397     if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmObjectTable() failed.\n");
3398     return false;
3399   }
3400   for( i = 0; ok && i < m_object_table.Count(); i++ )
3401   {
3402     if ( 0 != m_object_table[i].m_object )
3403     {
3404       ok = archive.Write3dmObject(*m_object_table[i].m_object,&m_object_table[i].m_attributes);
3405       if ( !ok )
3406       {
3407         if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmObject(m_IDef_table[%d]) failed.\n",i);
3408       }
3409     }
3410   }
3411   if ( !archive.EndWrite3dmObjectTable() )
3412   {
3413     if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmObjectTable() failed.\n");
3414     return false;
3415   }
3416   if (!ok)
3417     return false;
3418 
3419 
3420   // HISTORY RECORD TABLE
3421   if ( archive.Archive3dmVersion() >= 4 )
3422   {
3423     ok = archive.BeginWrite3dmHistoryRecordTable();
3424     if ( !ok )
3425     {
3426       if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmHistoryRecordTable() failed.\n");
3427       return false;
3428     }
3429     for ( i = 0; ok && i < m_history_record_table.Count(); i++ )
3430     {
3431       const ON_HistoryRecord* history_record = m_history_record_table[i];
3432       if( history_record)
3433         ok = archive.Write3dmHistoryRecord( *history_record );
3434     }
3435     if( !archive.EndWrite3dmHistoryRecordTable() )
3436     {
3437       if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmHistoryTable() failed.\n");
3438       return false;
3439     }
3440     if (!ok)
3441       return false;
3442   }
3443 
3444   // USER DATA TABLE
3445   for( i = 0; ok && i < m_userdata_table.Count(); i++ )
3446   {
3447     const ONX_Model_UserData& ud = m_userdata_table[i];
3448     if ( ON_UuidIsNotNil(ud.m_uuid) )
3449     {
3450       if ( !archive.Write3dmAnonymousUserTableRecord(
3451         ud.m_uuid,
3452         ud.m_usertable_3dm_version,
3453         ud.m_usertable_opennurbs_version,ud.m_goo
3454         ) )
3455       {
3456         continue;
3457       }
3458     }
3459   }
3460 
3461   if ( !archive.Write3dmEndMark() )
3462   {
3463     ok = false;
3464     if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmEndMark() failed.\n");
3465   }
3466 
3467   return ok;
3468 }
3469 
IsValid(ON_TextLog * text_log) const3470 bool ONX_Model::IsValid( ON_TextLog* text_log ) const
3471 {
3472   // Audit with no repairs will simply complain if it
3473   // finds something wrong;
3474   int i = const_cast<ONX_Model*>(this)->Audit(false,NULL,text_log,NULL);
3475   return (i>=0);
3476 }
3477 
ObjectIndex(ON_UUID object_uuid) const3478 int ONX_Model::ObjectIndex( ON_UUID object_uuid ) const
3479 {
3480   // In a high quality app, you may want to override this
3481   // and do something a little smarter.
3482 
3483   int object_index = -1;
3484   if ( ON_UuidIsNotNil(object_uuid) )
3485   {
3486     int i, object_count = m_object_table.Count();
3487     if ( object_count > 0 )
3488     {
3489       if ( object_count != m_object_id_index.Count() )
3490       {
3491         // rebuild m__object_uuid_index[]
3492         ON_UuidIndexList* p = const_cast< ON_UuidIndexList* >(&m_object_id_index);
3493         p->Empty();
3494         p->Reserve(object_count);
3495         for ( i = 0; i < object_count; i++ )
3496         {
3497           ON_UUID id = m_object_table[i].m_attributes.m_uuid;
3498           if ( ON_UuidIsNil(id) )
3499           {
3500             ON_ERROR("Nil object ids in model");
3501             ON_CreateUuid(id);
3502             *(const_cast<ON_UUID*>(&m_object_table[i].m_attributes.m_uuid)) = id;
3503           }
3504           if ( !p->AddUuidIndex(id,i,true) )
3505           {
3506             ON_ERROR("Duplicate object ids in model");
3507             ON_CreateUuid(id);
3508             *(const_cast<ON_UUID*>(&m_object_table[i].m_attributes.m_uuid)) = id;
3509             p->AddUuidIndex(id,i,false);
3510           }
3511         }
3512       }
3513 
3514       if ( !m_object_id_index.FindUuid(object_uuid,&object_index) )
3515         object_index = -1;
3516     }
3517   }
3518 
3519   return object_index;
3520 }
3521 
IDefIndex(ON_UUID idef_uuid) const3522 int ONX_Model::IDefIndex( ON_UUID idef_uuid ) const
3523 {
3524   // In a high quality app, you may want to override this
3525   // and do something a little smarter.
3526 
3527   int idef_index = -1;
3528   if ( ON_UuidIsNotNil(idef_uuid) )
3529   {
3530     int i, idef_count = m_idef_table.Count();
3531     if ( idef_count > 0 )
3532     {
3533       if ( idef_count != m_idef_id_index.Count() )
3534       {
3535         // rebuild m__idef_uuid_index[]
3536         ON_UuidIndexList* p = const_cast<ON_UuidIndexList*>(&m_idef_id_index);
3537         p->Empty();
3538         p->Reserve(idef_count);
3539         for ( i = 0; i < idef_count; i++ )
3540         {
3541           ON_UUID id = m_idef_table[i].m_uuid;
3542           if ( ON_UuidIsNil(id) )
3543           {
3544             ON_ERROR("Nil idef ids in model");
3545             ON_CreateUuid(id);
3546             (const_cast<ON_InstanceDefinition*>(&m_idef_table[i]))->m_uuid = id;
3547           }
3548           if ( !p->AddUuidIndex(id,i,true) )
3549           {
3550             ON_ERROR("Duplicate idef ids in model");
3551             ON_CreateUuid(id);
3552             (const_cast<ON_InstanceDefinition*>(&m_idef_table[i]))->m_uuid = id;
3553             p->AddUuidIndex(id,i,false);
3554           }
3555         }
3556       }
3557 
3558       if ( !m_idef_id_index.FindUuid(idef_uuid,&idef_index) )
3559         idef_index = -1;
3560     }
3561   }
3562 
3563   return idef_index;
3564 }
3565 
IDefIndex(const wchar_t * idef_name) const3566 int ONX_Model::IDefIndex( const wchar_t* idef_name ) const
3567 {
3568   // slow and stupid search - what do you expect for free?
3569   // In a high quality app, you will want to override this
3570   // and do something a little smarter.
3571   //
3572   int idef_index = -1;
3573   if ( 0 != idef_name && 0 != idef_name[0]  )
3574   {
3575     int i, idef_count = m_idef_table.Count();
3576     for ( i = 0; i < idef_count; i++ )
3577     {
3578       if ( 0 == on_wcsicmp(idef_name, m_idef_table[i].Name() ) )
3579       {
3580         idef_index = i;
3581         break;
3582       }
3583     }
3584   }
3585   return idef_index;
3586 }
3587 
GetUnusedIDefName(ON_wString & idef_name) const3588 void ONX_Model::GetUnusedIDefName( ON_wString& idef_name ) const
3589 {
3590   int i = 1;
3591   for(i = 1; i < 100000; i++ )
3592   {
3593     idef_name.Format("IDef_%02d",i);
3594     if ( IDefIndex(idef_name) < 0 )
3595       return;
3596   }
3597   idef_name = "IDef";
3598   return;
3599 }
3600 
UsesIDef(const ON_InstanceRef & iref,ON_UUID idef_uuid) const3601 int ONX_Model::UsesIDef(
3602         const ON_InstanceRef& iref,
3603         ON_UUID idef_uuid
3604         ) const
3605 {
3606   // get id of idef we are looking for
3607   if ( ON_UuidIsNil(idef_uuid) )
3608     return 0;
3609 
3610   // id of idef that defines iref
3611   ON_UUID iref_idef_uuid = iref.m_instance_definition_uuid;
3612   if ( 0 == ON_UuidCompare( idef_uuid, iref_idef_uuid ) )
3613     return 1;
3614 
3615   const int iref_idef_index = IDefIndex(iref_idef_uuid);
3616   if ( -1 == iref_idef_index )
3617     return -1;
3618   const ON_InstanceDefinition& iref_idef = m_idef_table[iref_idef_index];
3619 
3620 
3621   int i0 = 0;
3622   int i1 = 0;
3623   int i, j, k, depth, obj_index;
3624   const ON_InstanceRef* pNestedIRef;
3625 
3626   // set iref_list[] = list of all nested instance references in iref_idef.
3627   ON_SimpleArray<const ON_InstanceRef*> iref_list(256);
3628   for ( j = 0; j < iref_idef.m_object_uuid.Count(); j++ )
3629   {
3630     obj_index = ObjectIndex(iref_idef.m_object_uuid[j]);
3631     if ( obj_index < 0 )
3632       continue;
3633     const ONX_Model_Object& obj = m_object_table[obj_index];
3634     if ( 0 == obj.m_object )
3635       continue;
3636     if ( obj.m_object->ObjectType() == ON::instance_reference )
3637     {
3638       pNestedIRef = ON_InstanceRef::Cast(obj.m_object);
3639       if ( 0 != pNestedIRef )
3640       {
3641         if ( 0 == ON_UuidCompare( idef_uuid, pNestedIRef->m_instance_definition_uuid ) )
3642           return 2;
3643         iref_list.Append(pNestedIRef);
3644       }
3645     }
3646   }
3647 
3648   // test the nested instance references to see if they use idef_index.
3649   const int max_depth = 1000;
3650   for ( depth=3; depth < max_depth && i1 < iref_list.Count(); depth++ )
3651   {
3652     i0 = i1;
3653     i1 = iref_list.Count();
3654     for ( i = i0; i < i1; i++ )
3655     {
3656       pNestedIRef = iref_list[i];
3657       if ( 0 == pNestedIRef )
3658         continue;
3659       k = IDefIndex( pNestedIRef->m_instance_definition_uuid );
3660       if ( k < 0 )
3661         continue;
3662       const ON_InstanceDefinition& nested_idef = m_idef_table[k];
3663       for ( j = 0; j < nested_idef.m_object_uuid.Count(); j++ )
3664       {
3665         obj_index = ObjectIndex(nested_idef.m_object_uuid[j]);
3666         if ( obj_index < 0 )
3667           continue;
3668         const ONX_Model_Object& obj = m_object_table[obj_index];
3669         if ( 0 == obj.m_object )
3670           continue;
3671         if ( obj.m_object->ObjectType() == ON::instance_reference )
3672         {
3673           pNestedIRef = ON_InstanceRef::Cast(obj.m_object);
3674           if ( 0 != pNestedIRef )
3675           {
3676             if ( 0 == ON_UuidCompare( idef_uuid, pNestedIRef->m_instance_definition_uuid ) )
3677               return depth;
3678             iref_list.Append(pNestedIRef);
3679           }
3680         }
3681       }
3682     }
3683   }
3684 
3685   return (depth < max_depth) ? 0 : -2;
3686 }
3687 
LayerIndex(const wchar_t * layer_name) const3688 int ONX_Model::LayerIndex( const wchar_t* layer_name ) const
3689 {
3690   // slow and stupid search - what do you expect for free?
3691   // In a high quality app, you will want to override this
3692   // and do something a little smarter.
3693 
3694   int layer_index = -1;
3695   if ( 0 != layer_name && 0 != layer_name[0] )
3696   {
3697     int i, layer_count = m_layer_table.Count();
3698     for ( i = 0; i < layer_count; i++ )
3699     {
3700       if ( 0 == on_wcsicmp(layer_name, m_layer_table[i].LayerName() ) )
3701       {
3702         layer_index = i;
3703         break;
3704       }
3705     }
3706   }
3707   return layer_index;
3708 }
3709 
3710 
GetUnusedLayerName(ON_wString & layer_name) const3711 void ONX_Model::GetUnusedLayerName( ON_wString& layer_name ) const
3712 {
3713   int i = 1;
3714   for(i = 1; i < 100000; i++ )
3715   {
3716     layer_name.Format("Layer_%02d",i);
3717     if ( LayerIndex(layer_name) < 0 )
3718       return;
3719   }
3720   layer_name = "Layer";
3721   return;
3722 }
3723 
3724 
3725 
SetDocumentUserString(const wchar_t * key,const wchar_t * string_value)3726 bool ONX_Model::SetDocumentUserString( const wchar_t* key, const wchar_t* string_value )
3727 {
3728   // This is a slow and stupid way to set a single string,
3729   // but I cannot modify the ONX_Model class to transparently
3730   // store document user string information until I can break
3731   // the public SDK in V6.
3732   bool rc = false;
3733   if ( 0 != key && 0 != key[0] )
3734   {
3735     ON_UUID doc_userstring_id = ON_DocumentUserStringList::m_ON_DocumentUserStringList_class_id.Uuid();
3736     for (int i = 0; i < m_userdata_table.Count(); i++ )
3737     {
3738       ONX_Model_UserData& ud = m_userdata_table[i];
3739       if ( ud.m_uuid == doc_userstring_id )
3740       {
3741         if ( TCODE_USER_RECORD == ud.m_goo.m_typecode && ud.m_goo.m_value != 0 )
3742         {
3743           ON_Read3dmBufferArchive ba(
3744             (unsigned int)ud.m_goo.m_value,
3745             ud.m_goo.m_goo,
3746             false,
3747             m_3dm_file_version,
3748             m_3dm_opennurbs_version
3749             );
3750           ON_Object* p = 0;
3751           if ( ba.ReadObject(&p) )
3752           {
3753             ON_DocumentUserStringList* sl = ON_DocumentUserStringList::Cast(p);
3754             if ( 0 != sl )
3755             {
3756               // modify the user string information
3757               rc = sl->SetUserString(key,string_value);
3758               if ( rc )
3759               {
3760                 // write the new informtion to a memory buffer
3761                 ON_Write3dmBufferArchive newgoo(ud.m_goo.m_value+1024,0,m_3dm_file_version,ON::Version());
3762                 if (    newgoo.BeginWrite3dmUserTable(doc_userstring_id,false,0,0)
3763                      && newgoo.WriteObject(sl)
3764                      && newgoo.EndWrite3dmUserTable()
3765                      )
3766                 {
3767                   if (    newgoo.SizeOfArchive() > 0
3768                        && newgoo.SizeOfArchive() <= 0xFFFFFFFF // max goo size if 4GB because we used an unsigned int back in the ice ages
3769                      )
3770                   {
3771                     // update the "goo"
3772                     unsigned char* goo = (unsigned char*)newgoo.HarvestBuffer();
3773                     unsigned int value = (unsigned int)newgoo.SizeOfArchive();
3774                     if ( 0 != goo && value > 0 )
3775                     {
3776                       onfree(ud.m_goo.m_goo); // delete old "goo"
3777                       ud.m_goo.m_value = (int)value;
3778                       ud.m_goo.m_goo = goo;
3779                     }
3780                   }
3781                 }
3782               }
3783             }
3784           }
3785           if ( 0 != p )
3786           {
3787             delete p;
3788             p = 0;
3789           }
3790         }
3791         break;
3792       }
3793     }
3794   }
3795   return rc;
3796 }
3797 
3798 
GetDocumentUserString(const wchar_t * key,ON_wString & string_value) const3799 bool ONX_Model::GetDocumentUserString( const wchar_t* key, ON_wString& string_value ) const
3800 {
3801   const wchar_t* s = 0;
3802   if ( 0 != key && 0 != key[0] )
3803   {
3804     // This is a slow and stupid way to get a single string,
3805     // but I cannot modify the ONX_Model class to transparently
3806     // store document user string information until I can break
3807     // the public SDK in V6.
3808     ON_ClassArray<ON_UserString> user_strings;
3809     GetDocumentUserStrings( user_strings );
3810     for ( int i = 0; i < user_strings.Count(); i++ )
3811     {
3812       if ( !user_strings[i].m_key.CompareNoCase(key) )
3813       {
3814         s = user_strings[i].m_string_value;
3815         break;
3816       }
3817     }
3818   }
3819   string_value = s;
3820   return (0 != s);
3821 }
3822 
3823 
GetDocumentUserStrings(ON_ClassArray<ON_UserString> & user_strings) const3824 int ONX_Model::GetDocumentUserStrings( ON_ClassArray<ON_UserString>& user_strings ) const
3825 {
3826   int rc = 0;
3827   // user strings are stored as ON_Object user strings on
3828   // an ON_DocumentUserStringList object in a user table
3829   // with id = doc_userstring_id.
3830   ON_UUID doc_userstring_id = ON_DocumentUserStringList::m_ON_DocumentUserStringList_class_id.Uuid();
3831   for (int i = 0; i < m_userdata_table.Count(); i++ )
3832   {
3833     const ONX_Model_UserData& ud = m_userdata_table[i];
3834     if ( ud.m_uuid == doc_userstring_id )
3835     {
3836       if ( TCODE_USER_RECORD == ud.m_goo.m_typecode && ud.m_goo.m_value != 0 )
3837       {
3838         ON_Read3dmBufferArchive ba(
3839           (unsigned int)ud.m_goo.m_value,
3840           ud.m_goo.m_goo,
3841           false,
3842           m_3dm_file_version,
3843           m_3dm_opennurbs_version
3844           );
3845 
3846         ON_Object* p = 0;
3847         if ( ba.ReadObject(&p) )
3848         {
3849           const ON_DocumentUserStringList* sl = ON_DocumentUserStringList::Cast(p);
3850           if ( 0 != sl )
3851           {
3852             rc = sl->GetUserStrings(user_strings);
3853           }
3854         }
3855         if ( 0 != p )
3856         {
3857           delete p;
3858           p = 0;
3859         }
3860       }
3861       break;
3862     }
3863   }
3864   return rc;
3865 }
3866 
3867 
AuditTextureMappingTableHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)3868 static int AuditTextureMappingTableHelper(
3869       ONX_Model& model,
3870       bool bAttemptRepair,
3871       int* repair_count,
3872       ON_TextLog* text_log
3873       )
3874 {
3875   if ( repair_count )
3876     *repair_count = 0;
3877   int i, count = model.m_mapping_table.Count();
3878   for ( i = 0; i < count; i++ )
3879   {
3880     ON_TextureMapping& mapping = model.m_mapping_table[i];
3881     if ( mapping.m_mapping_index != i )
3882     {
3883       if ( text_log )
3884       {
3885         text_log->Print("m_mapping_table[%d].m_mapping_index == %d (should be %d)\n",
3886                         i,mapping.m_mapping_index,i);
3887       }
3888       if ( bAttemptRepair )
3889       {
3890         mapping.m_mapping_index = i;
3891         if ( text_log )
3892         {
3893           text_log->PushIndent();
3894           text_log->Print("Repaired.\n");
3895           text_log->PopIndent();
3896         }
3897         if ( repair_count )
3898           *repair_count += 1;
3899       }
3900       else
3901       {
3902         return -1;
3903       }
3904     }
3905     if ( !mapping.IsValid(text_log) )
3906       return 1;
3907   }
3908   return 0;
3909 }
3910 
3911 
AuditGroupTableHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)3912 static int AuditGroupTableHelper(
3913       ONX_Model& model,
3914       bool bAttemptRepair,
3915       int* repair_count,
3916       ON_TextLog* text_log
3917       )
3918 {
3919   if ( repair_count )
3920     *repair_count = 0;
3921   int i, count = model.m_group_table.Count();
3922   for ( i = 0; i < count; i++ )
3923   {
3924     ON_Group& group = model.m_group_table[i];
3925     if ( group.m_group_index != i )
3926     {
3927       if ( text_log )
3928       {
3929         text_log->Print("m_group_table[%d].m_group_index == %d (should be %d)\n",
3930                         i,group.m_group_index,i);
3931       }
3932       if ( bAttemptRepair )
3933       {
3934         group.m_group_index = i;
3935         if ( text_log )
3936         {
3937           text_log->PushIndent();
3938           text_log->Print("Repaired.\n");
3939           text_log->PopIndent();
3940         }
3941         if ( repair_count )
3942           *repair_count += 1;
3943       }
3944       else
3945       {
3946         return -1;
3947       }
3948     }
3949     if ( !group.IsValid(text_log) )
3950       return 1;
3951   }
3952   return 0;
3953 }
3954 
3955 
AuditFontTableHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)3956 static int AuditFontTableHelper(
3957       ONX_Model& model,
3958       bool bAttemptRepair,
3959       int* repair_count,
3960       ON_TextLog* text_log
3961       )
3962 {
3963   if ( repair_count )
3964     *repair_count = 0;
3965   int i, count = model.m_font_table.Count();
3966   for ( i = 0; i < count; i++ )
3967   {
3968     ON_Font& font = model.m_font_table[i];
3969     if ( font.m_font_index != i )
3970     {
3971       if ( text_log )
3972       {
3973         text_log->Print("m_font_table[%d].m_font_index == %d (should be %d)\n",
3974                         i,font.m_font_index,i);
3975       }
3976       if ( bAttemptRepair )
3977       {
3978         font.m_font_index = i;
3979         if ( text_log )
3980         {
3981           text_log->PushIndent();
3982           text_log->Print("Repaired.\n");
3983           text_log->PopIndent();
3984         }
3985         if ( repair_count )
3986           *repair_count += 1;
3987       }
3988       else
3989       {
3990         return -1;
3991       }
3992     }
3993     if ( !font.IsValid(text_log) )
3994       return 1;
3995   }
3996   return 0;
3997 }
3998 
3999 
AuditDimStyleTableHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)4000 static int AuditDimStyleTableHelper(
4001       ONX_Model& model,
4002       bool bAttemptRepair,
4003       int* repair_count,
4004       ON_TextLog* text_log
4005       )
4006 {
4007   if ( repair_count )
4008     *repair_count = 0;
4009   int i, count = model.m_dimstyle_table.Count();
4010   for ( i = 0; i < count; i++ )
4011   {
4012     ON_DimStyle& dimstyle = model.m_dimstyle_table[i];
4013     if ( dimstyle.m_dimstyle_index != i )
4014     {
4015       if ( text_log )
4016       {
4017         text_log->Print("m_dimstyle_table[%d].m_dimstyle_index == %d (should be %d)\n",
4018                         i,dimstyle.m_dimstyle_index,i);
4019       }
4020       if ( bAttemptRepair )
4021       {
4022         dimstyle.m_dimstyle_index = i;
4023         if ( text_log )
4024         {
4025           text_log->PushIndent();
4026           text_log->Print("Repaired.\n");
4027           text_log->PopIndent();
4028         }
4029         if ( repair_count )
4030           *repair_count += 1;
4031       }
4032       else
4033       {
4034         return -1;
4035       }
4036     }
4037     if ( !dimstyle.IsValid(text_log) )
4038       return 1;
4039   }
4040   return 0;
4041 }
4042 
4043 
AuditHatchPatternTableHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)4044 static int AuditHatchPatternTableHelper(
4045       ONX_Model& model,
4046       bool bAttemptRepair,
4047       int* repair_count,
4048       ON_TextLog* text_log
4049       )
4050 {
4051   if ( repair_count )
4052     *repair_count = 0;
4053   int i, count = model.m_hatch_pattern_table.Count();
4054   for ( i = 0; i < count; i++ )
4055   {
4056     ON_HatchPattern& hatchpattern = model.m_hatch_pattern_table[i];
4057     if ( hatchpattern.m_hatchpattern_index != i )
4058     {
4059       if ( text_log )
4060       {
4061         text_log->Print("m_hatch_pattern_table[%d].m_hatchpattern_index == %d (should be %d)\n",
4062                         i,hatchpattern.m_hatchpattern_index,i);
4063       }
4064       if ( bAttemptRepair )
4065       {
4066         hatchpattern.m_hatchpattern_index = i;
4067         if ( text_log )
4068         {
4069           text_log->PushIndent();
4070           text_log->Print("Repaired.\n");
4071           text_log->PopIndent();
4072         }
4073         if ( repair_count )
4074           *repair_count += 1;
4075       }
4076       else
4077       {
4078         return -1;
4079       }
4080     }
4081     if ( !hatchpattern.IsValid(text_log) )
4082       return 1;
4083   }
4084   return 0;
4085 }
4086 
4087 
AuditObjectAttributesHelper(ONX_Model & model,ON_3dmObjectAttributes & attributes,const char * parent_name,int parent_index,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)4088 static int AuditObjectAttributesHelper(
4089       ONX_Model& model,
4090       ON_3dmObjectAttributes& attributes,
4091       const char* parent_name,
4092       int parent_index,
4093       bool bAttemptRepair,
4094       int* repair_count,
4095       ON_TextLog* text_log
4096       )
4097 {
4098   int repcount = 0;
4099   int errcount = 0;
4100 
4101   // validate key attributes.
4102   int layer_index = attributes.m_layer_index;
4103   if ( layer_index < 0 || layer_index >= model.m_layer_table.Count() )
4104   {
4105     errcount++;
4106     if ( text_log )
4107     {
4108       text_log->Print(parent_name,parent_index);
4109       text_log->Print("m_layer_index = %d is not valid.",layer_index);
4110     }
4111 
4112     if ( bAttemptRepair )
4113     {
4114       layer_index = model.m_settings.m_current_layer_index;
4115       if ( layer_index < 0 || layer_index >= model.m_layer_table.Count()
4116          )
4117       {
4118         layer_index = 0;
4119       }
4120       if ( layer_index >= 0 && layer_index < model.m_layer_table.Count() )
4121       {
4122         repcount++;
4123         attributes.m_layer_index = layer_index;
4124         if ( text_log )
4125           text_log->Print(" Repaired.");
4126       }
4127     }
4128     if ( text_log )
4129     {
4130       text_log->Print("\n");
4131     }
4132   }
4133 
4134   int linetype_index = attributes.m_linetype_index;
4135   if ( linetype_index < -1 || linetype_index >= model.m_linetype_table.Count() )
4136   {
4137     errcount++;
4138     if ( text_log )
4139     {
4140       text_log->Print(parent_name,parent_index);
4141       text_log->Print("m_linetype_index = %d is not valid.",linetype_index);
4142     }
4143 
4144     if ( bAttemptRepair )
4145     {
4146       linetype_index = -1;
4147       repcount++;
4148       attributes.m_linetype_index = linetype_index;
4149       if ( text_log )
4150         text_log->Print(" Repaired.");
4151     }
4152     if ( text_log )
4153     {
4154       text_log->Print("\n");
4155     }
4156   }
4157 
4158   int material_index = attributes.m_material_index;
4159   if ( material_index < -1 || material_index >= model.m_material_table.Count() )
4160   {
4161     errcount++;
4162     if ( text_log )
4163     {
4164       text_log->Print(parent_name,parent_index);
4165       text_log->Print("m_material_index = %d is not valid.",material_index);
4166     }
4167 
4168     if ( bAttemptRepair )
4169     {
4170       material_index = -1;
4171       repcount++;
4172       attributes.m_material_index = material_index;
4173       if ( text_log )
4174         text_log->Print(" Repaired.");
4175     }
4176     if ( text_log )
4177     {
4178       text_log->Print("\n");
4179     }
4180   }
4181 
4182   if ( repcount > 0 && repair_count )
4183     *repair_count = *repair_count + repcount;
4184 
4185   return (errcount>0) ? 9 : 0;
4186 }
4187 
4188 
4189 
AuditLightTableHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)4190 static int AuditLightTableHelper(
4191       ONX_Model& model,
4192       bool bAttemptRepair,
4193       int* repair_count,
4194       ON_TextLog* text_log
4195       )
4196 {
4197   int rc = 0;
4198   if ( repair_count )
4199     *repair_count = 0;
4200   int i, count = model.m_light_table.Count();
4201   for ( i = 0; i < count; i++ )
4202   {
4203     ONX_Model_RenderLight& xlight = model.m_light_table[i];
4204     ON_Light& light = xlight.m_light;
4205     if ( light.m_light_index != i )
4206     {
4207       if ( text_log )
4208       {
4209         text_log->Print("m_light_table[%d].m_light_index == %d (should be %d)\n",
4210                         i,light.m_light_index,i);
4211       }
4212       if ( bAttemptRepair )
4213       {
4214         light.m_light_index = i;
4215         if ( text_log )
4216         {
4217           text_log->PushIndent();
4218           text_log->Print("Repaired.\n");
4219           text_log->PopIndent();
4220         }
4221         if ( repair_count )
4222           *repair_count += 1;
4223       }
4224       else
4225       {
4226         return -1;
4227       }
4228     }
4229 
4230     int attrc = AuditObjectAttributesHelper(model,
4231       xlight.m_attributes,
4232       "m_light_table[%d].m_attributes",
4233       i,
4234       bAttemptRepair,
4235       repair_count,
4236       text_log
4237       );
4238 
4239     if ( attrc && 0 == rc )
4240       rc = 9;
4241 
4242     if ( !light.IsValid(text_log) )
4243     {
4244       if ( 0 == rc )
4245         rc = 1;
4246     }
4247 
4248   }
4249   return rc;
4250 }
4251 
AuditMaterialTableHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)4252 static int AuditMaterialTableHelper(
4253       ONX_Model& model,
4254       bool bAttemptRepair,
4255       int* repair_count,
4256       ON_TextLog* text_log
4257       )
4258 {
4259   if ( repair_count )
4260     *repair_count = 0;
4261   int i, count = model.m_material_table.Count();
4262   for ( i = 0; i < count; i++ )
4263   {
4264     ON_Material& mat = model.m_material_table[i];
4265     if ( mat.MaterialIndex() != i )
4266     {
4267       if ( text_log )
4268       {
4269         text_log->Print("m_material_table[%d].MaterialIndex() == %d (should be %d)\n",
4270                         i,mat.MaterialIndex(),i);
4271       }
4272       if ( bAttemptRepair )
4273       {
4274         mat.SetMaterialIndex(i);
4275         if ( text_log )
4276         {
4277           text_log->PushIndent();
4278           text_log->Print("Repaired.\n");
4279           text_log->PopIndent();
4280         }
4281         if ( repair_count )
4282           *repair_count += 1;
4283       }
4284       else
4285       {
4286         return -1;
4287       }
4288     }
4289     if ( !mat.IsValid(text_log) )
4290       return 1;
4291   }
4292   return 0;
4293 }
4294 
AuditLinetypeTableHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)4295 static int AuditLinetypeTableHelper(
4296       ONX_Model& model,
4297       bool bAttemptRepair,
4298       int* repair_count,
4299       ON_TextLog* text_log
4300       )
4301 {
4302   if ( repair_count )
4303     *repair_count = 0;
4304   int i, count = model.m_linetype_table.Count();
4305   for ( i = 0; i < count; i++ )
4306   {
4307     ON_Linetype& lt = model.m_linetype_table[i];
4308     if ( lt.LinetypeIndex() != i )
4309     {
4310       if ( text_log )
4311       {
4312         text_log->Print("m_linetype_table[%d].LinetypeIndex() == %d (should be %d)\n",
4313                         i,lt.LinetypeIndex(),i);
4314       }
4315       if ( bAttemptRepair )
4316       {
4317         lt.SetLinetypeIndex(i);
4318         if ( text_log )
4319         {
4320           text_log->PushIndent();
4321           text_log->Print("Repaired.\n");
4322           text_log->PopIndent();
4323         }
4324         if ( repair_count )
4325           *repair_count += 1;
4326       }
4327       else
4328       {
4329         return -1;
4330       }
4331     }
4332     if ( !lt.IsValid(text_log) )
4333       return 10;
4334   }
4335   return 0;
4336 }
4337 
AuditLayerTableHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)4338 static int AuditLayerTableHelper(
4339       ONX_Model& model,
4340       bool bAttemptRepair,
4341       int* repair_count,
4342       ON_TextLog* text_log
4343       )
4344 {
4345   int rc = 0;
4346   if ( repair_count )
4347     *repair_count = 0;
4348   int i, count = model.m_layer_table.Count();
4349   if ( count == 0 && (model.m_object_table.Count()>0 || model.m_light_table.Count()>0))
4350     rc = 2;
4351   for ( i = 0; i < count; i++ )
4352   {
4353     ON_Layer& layer = model.m_layer_table[i];
4354     if ( layer.LayerIndex() != i )
4355     {
4356       if ( text_log )
4357       {
4358         text_log->Print("m_layer_table[%d].LayerIndex() == %d (should be %d)\n",
4359                         i,layer.LayerIndex(),i);
4360       }
4361       if ( bAttemptRepair )
4362       {
4363         layer.SetLayerIndex(i);
4364         if ( text_log )
4365         {
4366           text_log->PushIndent();
4367           text_log->Print("Repaired.\n");
4368           text_log->PopIndent();
4369         }
4370         if ( repair_count )
4371           *repair_count += 1;
4372       }
4373       else
4374       {
4375         rc = -1;
4376       }
4377     }
4378 
4379     const wchar_t* layer_name = layer.LayerName();
4380 
4381     if ( 0 == layer_name || 0 == layer_name[0] )
4382     {
4383       if ( text_log )
4384       {
4385         text_log->Print("m_layer_table[%d].LayerName() is empty\n",i);
4386       }
4387       if ( bAttemptRepair )
4388       {
4389         ON_wString name;
4390         model.GetUnusedLayerName( name );
4391         layer.SetLayerName( name );
4392         if ( text_log )
4393         {
4394           text_log->PushIndent();
4395           text_log->Print("Repaired.\n");
4396           text_log->PopIndent();
4397         }
4398         if ( repair_count )
4399           *repair_count += 1;
4400         layer_name = layer.LayerName();
4401       }
4402       else if ( rc == 0 )
4403       {
4404         rc = 2;
4405       }
4406     }
4407 
4408     if ( !ONX_IsValidName(layer_name) )
4409     {
4410       if ( text_log )
4411       {
4412         text_log->Print("m_layer_table[%d].LayerName() is not valid\n",i);
4413       }
4414       if ( bAttemptRepair )
4415       {
4416         ON_wString name;
4417         model.GetUnusedLayerName( name );
4418         layer.SetLayerName( name );
4419         if ( text_log )
4420         {
4421           text_log->PushIndent();
4422           text_log->Print("Repaired.\n");
4423           text_log->PopIndent();
4424         }
4425         if ( repair_count )
4426           *repair_count += 1;
4427         layer_name = layer.LayerName();
4428       }
4429       else if ( rc == 0 )
4430       {
4431         rc = 2;
4432       }
4433     }
4434 
4435     int j = model.LayerIndex(layer_name);
4436     if ( i != j )
4437     {
4438       if ( text_log )
4439       {
4440         text_log->Print("m_layer_table[%d] and m_layer_table[%d] have same layer name.\n",i,j);
4441       }
4442       if ( bAttemptRepair )
4443       {
4444         ON_wString name;
4445         model.GetUnusedLayerName( name );
4446         layer.SetLayerName( name );
4447         if ( text_log )
4448         {
4449           text_log->PushIndent();
4450           text_log->Print("Repaired.\n");
4451           text_log->PopIndent();
4452         }
4453         if ( repair_count )
4454           *repair_count += 1;
4455       }
4456       else if ( rc == 0 )
4457       {
4458         rc = 2;
4459       }
4460     }
4461 
4462     if ( rc == 0 && !layer.IsValid(text_log) )
4463       rc = 2;
4464   }
4465   return rc;
4466 }
4467 
AuditIdsHelper(ON_SimpleArray<ON_UuidIndex> & id_list,ON_UuidIndexList * index_list,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log,const char * nil_id_msg,const char * dup_id_msg)4468 static int AuditIdsHelper(
4469       ON_SimpleArray<ON_UuidIndex>& id_list,
4470       ON_UuidIndexList* index_list,
4471       bool bAttemptRepair,
4472       int* repair_count,
4473       ON_TextLog* text_log,
4474       const char* nil_id_msg,
4475       const char* dup_id_msg
4476       )
4477 {
4478   int nil_count = 0;
4479   int dup_count = 0;
4480   int rep_count = 0;
4481 
4482   if ( index_list )
4483     index_list->Empty();
4484   const int count = id_list.Count();
4485   if ( count > 0 )
4486   {
4487     int i;
4488     ON_UUID id;
4489 
4490     // Make sure objects have non-nil ids
4491     for ( i = 0; i < count; i++ )
4492     {
4493       id_list[i].m_i = i;
4494       if ( ON_nil_uuid == id_list[i].m_id )
4495       {
4496         nil_count++;
4497         if ( text_log )
4498           text_log->Print(nil_id_msg,i);
4499 
4500         if ( bAttemptRepair )
4501         {
4502           id = ON_nil_uuid;
4503           if ( ON_CreateUuid(id) && !ON_UuidIsNil(id) )
4504           {
4505             id_list[i].m_id = id;
4506             rep_count++;
4507             if ( text_log )
4508               text_log->Print(" Repaired.");
4509           }
4510         }
4511         if ( text_log )
4512           text_log->Print("\n");
4513       }
4514     }
4515 
4516     if ( count > 1 )
4517     {
4518       // Make sure objects have unique ids
4519       id_list.QuickSort( ON_UuidIndex::CompareIdAndIndex );
4520       ON_UuidIndex id0 = id_list[0];
4521       for ( i = 1; i < count; i++ )
4522       {
4523         if ( ON_nil_uuid == id_list[i].m_id )
4524         {
4525           // nil ids were handled and counted above.
4526           // Either repair is false or we are not running
4527           // on Windows and the user supplied ON_CreateUuid
4528           // is returning nil ids.
4529           continue;
4530         }
4531 
4532         if ( id_list[i].m_id == id0.m_id )
4533         {
4534           dup_count++;
4535           if ( text_log )
4536             text_log->Print(dup_id_msg,id0.m_i,id_list[i].m_i);
4537 
4538           if ( bAttemptRepair )
4539           {
4540             // fix duplicate object id
4541             id = ON_nil_uuid;
4542             if ( ON_CreateUuid(id) && !ON_UuidIsNil(id) )
4543             {
4544               rep_count++;
4545               id_list[i].m_id = id;
4546               if ( text_log )
4547                 text_log->Print(" Repaired.");
4548             }
4549           }
4550           if ( text_log )
4551             text_log->Print("\n");
4552         }
4553         else
4554         {
4555           id0 = id_list[i];
4556         }
4557       }
4558     }
4559 
4560     if ( index_list )
4561     {
4562       // rebuild index_list
4563       index_list->Reserve(count);
4564       for ( i = 0; i < count; i++ )
4565       {
4566         index_list->AddUuidIndex(id_list[i].m_id,id_list[i].m_i,false);
4567       }
4568     }
4569   }
4570 
4571   if ( repair_count )
4572     *repair_count = rep_count;
4573 
4574   return dup_count + nil_count;
4575 }
4576 
AuditObjectIdsHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)4577 static int AuditObjectIdsHelper(
4578       ONX_Model& model,
4579       bool bAttemptRepair,
4580       int* repair_count,
4581       ON_TextLog* text_log
4582       )
4583 {
4584   int rc = 0;
4585   const int count = model.m_object_table.Count();
4586   model.m_object_id_index.Empty();
4587   if ( count > 0 )
4588   {
4589     int i;
4590     ON_SimpleArray<ON_UuidIndex> id_list(count);
4591     for ( i = 0; i < count; i++ )
4592     {
4593       id_list.AppendNew().m_id = model.m_object_table[i].m_attributes.m_uuid;
4594     }
4595     rc = AuditIdsHelper(
4596               id_list,
4597               &model.m_object_id_index,
4598               bAttemptRepair,
4599               repair_count,
4600               text_log,
4601               "m_object_table[%d].m_attributes.m_uuid is nil.",
4602               "m_object_table[%d] and [%d] have the same id."
4603               );
4604     if (rc && bAttemptRepair )
4605     {
4606       for ( i = 0; i < count; i++ )
4607       {
4608         model.m_object_table[id_list[i].m_i].m_attributes.m_uuid = id_list[i].m_id;
4609       }
4610     }
4611   }
4612   return rc;
4613 }
4614 
4615 
AuditLightIdsHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)4616 static int AuditLightIdsHelper(
4617       ONX_Model& model,
4618       bool bAttemptRepair,
4619       int* repair_count,
4620       ON_TextLog* text_log
4621       )
4622 {
4623   int rc = 0;
4624   int mismatch_count = 0;
4625   const int count = model.m_light_table.Count();
4626   if ( count > 0 )
4627   {
4628     int i;
4629     ON_SimpleArray<ON_UuidIndex> id_list(count);
4630     for ( i = 0; i < count; i++ )
4631     {
4632       ONX_Model_RenderLight& light = model.m_light_table[i];
4633       if ( ON_UuidCompare(light.m_light.m_light_id,light.m_attributes.m_uuid) )
4634       {
4635         mismatch_count++;
4636         if ( text_log )
4637         {
4638           text_log->Print("m_light_table[%d] light id and attributes id differ.",i);
4639         }
4640         if ( bAttemptRepair )
4641         {
4642           if ( repair_count )
4643             *repair_count = *repair_count + 1;
4644           if ( ON_nil_uuid == light.m_light.m_light_id )
4645             light.m_light.m_light_id = light.m_attributes.m_uuid;
4646           else
4647             light.m_attributes.m_uuid = light.m_light.m_light_id;
4648           if ( text_log )
4649             text_log->Print(" Repaired.");
4650         }
4651         if ( text_log )
4652           text_log->Print("\n");
4653       }
4654 
4655       if ( ON_nil_uuid == light.m_light.m_light_id )
4656       {
4657         id_list.AppendNew().m_id = light.m_attributes.m_uuid;
4658       }
4659       else
4660       {
4661         id_list.AppendNew().m_id = light.m_light.m_light_id;
4662       }
4663     }
4664 
4665     rc = AuditIdsHelper(
4666               id_list,
4667               0, // no ONX_Model id index for lights
4668               bAttemptRepair,
4669               repair_count,
4670               text_log,
4671               "m_light_table[%d] light id is nil.",
4672               "m_light_table[%d] and[%d] have the same id."
4673               );
4674 
4675     rc += mismatch_count;
4676 
4677     if ( rc && bAttemptRepair )
4678     {
4679       for ( i = 0; i < count; i++ )
4680       {
4681         ONX_Model_RenderLight& light = model.m_light_table[id_list[i].m_i];
4682         light.m_light.m_light_id = id_list[i].m_id;
4683         light.m_attributes.m_uuid = light.m_light.m_light_id;
4684       }
4685     }
4686 
4687     // make sure light ids are not duplicated in object id list
4688     for ( i = 0; i < count; i++ )
4689     {
4690       ONX_Model_RenderLight& light = model.m_light_table[i];
4691       int oi = -1;
4692       if ( model.m_object_id_index.FindUuid(light.m_light.m_light_id,&oi) )
4693       {
4694         rc++;
4695         if ( text_log )
4696         {
4697           text_log->Print("m_light_table[%d] and m_object_table[%d] have same id.",
4698                           i,
4699                           oi
4700                           );
4701         }
4702         if ( bAttemptRepair )
4703         {
4704           ON_UuidIndex light_id;
4705           memset(&light_id,0,sizeof(light_id));
4706           light_id.m_id = ON_nil_uuid;
4707           light_id.m_i = -1;
4708           if ( ON_CreateUuid(light_id.m_id) && ON_nil_uuid != light_id.m_id )
4709           {
4710             if (    !model.m_object_id_index.FindUuid(light_id.m_id)
4711                  && id_list.BinarySearch( &light_id, ON_UuidIndex::CompareId ) < 0
4712                )
4713             {
4714               if ( repair_count )
4715                 *repair_count = *repair_count + 1;
4716               light.m_light.m_light_id = light_id.m_id;
4717               light.m_attributes.m_uuid = light.m_light.m_light_id;
4718               if ( text_log )
4719                 text_log->Print(" Repaired.");
4720             }
4721           }
4722         }
4723         if ( text_log )
4724           text_log->Print("\n");
4725       }
4726     }
4727   }
4728 
4729   return rc;
4730 }
4731 
AuditIDefIdsHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)4732 static int AuditIDefIdsHelper(
4733       ONX_Model& model,
4734       bool bAttemptRepair,
4735       int* repair_count,
4736       ON_TextLog* text_log
4737       )
4738 {
4739   int rc = 0;
4740   const int count = model.m_idef_table.Count();
4741   model.m_idef_id_index.Empty();
4742   if ( count > 0 )
4743   {
4744     int i;
4745     ON_SimpleArray<ON_UuidIndex> id_list(count);
4746     for ( i = 0; i < count; i++ )
4747     {
4748       id_list.AppendNew().m_id = model.m_idef_table[i].m_uuid;
4749     }
4750     rc = AuditIdsHelper(
4751               id_list,
4752               &model.m_idef_id_index,
4753               bAttemptRepair,
4754               repair_count,
4755               text_log,
4756               "m_idef_table[%d].m_attributes.m_uuid is nil.",
4757               "m_idef_table[%d] and[%d] are the same."
4758               );
4759     if (rc && bAttemptRepair )
4760     {
4761       for ( i = 0; i < count; i++ )
4762       {
4763         model.m_idef_table[id_list[i].m_i].m_uuid = id_list[i].m_id;
4764       }
4765     }
4766   }
4767   return rc;
4768 }
4769 
AuditMappingIdsHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)4770 static int AuditMappingIdsHelper(
4771       ONX_Model& model,
4772       bool bAttemptRepair,
4773       int* repair_count,
4774       ON_TextLog* text_log
4775       )
4776 {
4777   int rc = 0;
4778   const int count = model.m_mapping_table.Count();
4779   model.m_mapping_id_index.Empty();
4780   if ( count > 0 )
4781   {
4782     int i;
4783     ON_SimpleArray<ON_UuidIndex> id_list(count);
4784     for ( i = 0; i < count; i++ )
4785     {
4786       id_list.AppendNew().m_id = model.m_mapping_table[i].m_mapping_id;
4787     }
4788     rc = AuditIdsHelper(
4789               id_list,
4790               &model.m_mapping_id_index,
4791               bAttemptRepair,
4792               repair_count,
4793               text_log,
4794               "m_mapping_table[%d].m_mapping_id is nil.",
4795               "m_mapping_table[%d] and[%d] are the same."
4796               );
4797     if (rc && bAttemptRepair )
4798     {
4799       for ( i = 0; i < count; i++ )
4800       {
4801         model.m_mapping_table[id_list[i].m_i].m_mapping_id = id_list[i].m_id;
4802       }
4803     }
4804   }
4805   return rc;
4806 }
4807 
4808 
AuditMaterialIdsHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)4809 static int AuditMaterialIdsHelper(
4810       ONX_Model& model,
4811       bool bAttemptRepair,
4812       int* repair_count,
4813       ON_TextLog* text_log
4814       )
4815 {
4816   int rc = 0;
4817   const int count = model.m_material_table.Count();
4818   model.m_material_id_index.Empty();
4819   if ( count > 0 )
4820   {
4821     int i;
4822     ON_SimpleArray<ON_UuidIndex> id_list(count);
4823     for ( i = 0; i < count; i++ )
4824     {
4825       id_list.AppendNew().m_id = model.m_material_table[i].m_material_id;
4826     }
4827     rc = AuditIdsHelper(
4828               id_list,
4829               &model.m_material_id_index,
4830               bAttemptRepair,
4831               repair_count,
4832               text_log,
4833               "m_material_table[%d].m_material_id is nil.",
4834               "m_material_table[%d] and[%d] are the same."
4835               );
4836     if (rc && bAttemptRepair )
4837     {
4838       for ( i = 0; i < count; i++ )
4839       {
4840         model.m_material_table[id_list[i].m_i].m_material_id = id_list[i].m_id;
4841       }
4842     }
4843   }
4844   return rc;
4845 }
4846 
AuditModelIdsHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log,ON_SimpleArray<int> * warnings)4847 static int AuditModelIdsHelper(
4848       ONX_Model& model,
4849       bool bAttemptRepair,
4850       int* repair_count,
4851       ON_TextLog* text_log,
4852       ON_SimpleArray<int>* warnings
4853       )
4854 {
4855   int warning_count = 0;
4856   int i;
4857 
4858   // object ids
4859   i = AuditObjectIdsHelper(model,bAttemptRepair,repair_count,text_log);
4860   if (i < 0 )
4861     return i;
4862   if (i > 0 )
4863   {
4864     warning_count += i;
4865     if (warnings)
4866       warnings->Append(3);
4867   }
4868 
4869   // light ids
4870   i = AuditLightIdsHelper(model,bAttemptRepair,repair_count,text_log);
4871   if (i < 0 )
4872     return i;
4873   if (i > 0 )
4874   {
4875     warning_count += i;
4876     if (warnings)
4877       warnings->Append(15);
4878   }
4879 
4880   // idef ids
4881   i = AuditIDefIdsHelper(model,bAttemptRepair,repair_count,text_log);
4882   if (i < 0 )
4883     return i;
4884   if (i > 0 )
4885   {
4886     warning_count += i;
4887     if (warnings)
4888       warnings->Append(12);
4889   }
4890 
4891   // mapping ids
4892   i = AuditMappingIdsHelper(model,bAttemptRepair,repair_count,text_log);
4893   if (i < 0 )
4894     return i;
4895   if (i > 0 )
4896   {
4897     warning_count += i;
4898     if (warnings)
4899       warnings->Append(13);
4900   }
4901 
4902   // material ids
4903   i = AuditMaterialIdsHelper(model,bAttemptRepair,repair_count,text_log);
4904   if (i < 0 )
4905     return i;
4906   if (i > 0 )
4907   {
4908     warning_count += i;
4909     if (warnings)
4910       warnings->Append(13);
4911   }
4912 
4913   return warning_count;
4914 }
4915 
AuditIDefTableHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)4916 static int AuditIDefTableHelper(
4917       ONX_Model& model,
4918       bool bAttemptRepair,
4919       int* repair_count,
4920       ON_TextLog* text_log
4921       )
4922 {
4923   int rc = 0;
4924   int i, j, count = model.m_idef_table.Count();
4925 
4926   // AuditInstanceDefinitionIds() are already validated/repaired idef ids.
4927 
4928   // make sure idef names are empty or valid and unique
4929   for ( i = 0; i < count; i++ )
4930   {
4931     bool idef_ok = true;
4932     ON_InstanceDefinition& idef = model.m_idef_table[i];
4933     const wchar_t* idef_name = idef.Name();
4934     if ( 0 == idef_name )
4935       continue;
4936 
4937     if ( !ONX_IsValidName(idef_name) )
4938     {
4939       if ( text_log )
4940         text_log->Print("m_idef_table[%d].Name() = \"%ls\" is not valid.\n",i,idef_name);
4941       idef_ok = false;
4942     }
4943     else
4944     {
4945       j = model.IDefIndex( idef_name );
4946       if ( j != i )
4947       {
4948         if ( text_log )
4949           text_log->Print("m_idef_table[%d].Name() = m_idef_table[%d].Name().\n",i,j);
4950         idef_ok = false;
4951       }
4952     }
4953     if ( bAttemptRepair )
4954     {
4955       ON_wString new_name;
4956       model.GetUnusedIDefName(new_name);
4957       if ( ONX_IsValidName(new_name) && -1 == model.IDefIndex(new_name) )
4958       {
4959         idef.SetName(new_name);
4960         if ( repair_count )
4961           *repair_count = *repair_count + 1;
4962         if ( text_log )
4963           text_log->Print("Repaired.\n");
4964         idef_ok = true;
4965       }
4966     }
4967     if ( !idef_ok && rc == 0 )
4968     {
4969       rc = 5;
4970     }
4971   }
4972 
4973   for ( i = 0; i < count; i++ )
4974   {
4975     bool idef_ok = true;
4976     ON_InstanceDefinition& idef = model.m_idef_table[i];
4977 
4978     // make sure object uuid's are valid
4979     int k;
4980     j = 0;
4981     for ( j = k = 0; j < idef.m_object_uuid.Count(); k++ )
4982     {
4983       ON_UUID obj_uuid = idef.m_object_uuid[j];
4984       int obj_index = model.ObjectIndex(obj_uuid);
4985       if ( obj_index < 0 )
4986       {
4987         if ( text_log )
4988           text_log->Print("m_idef_table[%d].m_object_uuid[%d] is not an object's uuid.\n",i,k);
4989         if ( bAttemptRepair )
4990         {
4991           idef.m_object_uuid.Remove(j);
4992           if ( repair_count )
4993             *repair_count = *repair_count + 1;
4994           if ( text_log )
4995             text_log->Print("Repaired.\n");
4996         }
4997         else
4998         {
4999           j++;
5000           idef_ok = false;
5001         }
5002       }
5003       else
5004       {
5005         ONX_Model_Object& obj = model.m_object_table[obj_index];
5006         if ( ON::idef_object != obj.m_attributes.Mode() )
5007         {
5008           if ( text_log )
5009             text_log->Print("Object with uuid m_idef_table[%d].m_object_uuid[%d] does not have m_attributes.Mode()=ON::idef_object.\n",i,k);
5010           if ( bAttemptRepair )
5011           {
5012             idef.m_object_uuid.Remove(j);
5013             if ( repair_count )
5014               *repair_count = *repair_count + 1;
5015             if ( text_log )
5016               text_log->Print("Repaired.\n");
5017           }
5018           else
5019           {
5020             j++;
5021             idef_ok = false;
5022           }
5023         }
5024         else if ( 0 == obj.m_object )
5025         {
5026           if ( text_log )
5027             text_log->Print("Object with uuid m_idef_table[%d].m_object_uuid[%d] has NULL m_object.\n",i,k);
5028           if ( bAttemptRepair )
5029           {
5030             idef.m_object_uuid.Remove(j);
5031             if ( repair_count )
5032               *repair_count = *repair_count + 1;
5033             if ( text_log )
5034               text_log->Print("Repaired.\n");
5035           }
5036           else
5037           {
5038             j++;
5039             idef_ok = false;
5040           }
5041         }
5042         else if ( obj.m_object->ObjectType() == ON::instance_reference )
5043         {
5044           const ON_InstanceRef* pNestedRef = ON_InstanceRef::Cast(obj.m_object);
5045           if ( pNestedRef )
5046           {
5047             if ( model.UsesIDef( *pNestedRef, idef.Uuid() ) )
5048             {
5049               if ( text_log )
5050                 text_log->Print("m_idef_table[%d].m_object_uuid[%d] is a circular reference.\n",i,k);
5051               if ( bAttemptRepair )
5052               {
5053                 idef.m_object_uuid.Remove(j);
5054                 if ( repair_count )
5055                   *repair_count = *repair_count + 1;
5056                 if ( text_log )
5057                   text_log->Print("Repaired.\n");
5058               }
5059               else
5060               {
5061                 j++;
5062                 idef_ok = false;
5063                 rc = -1; // circular reference
5064               }
5065             }
5066             else
5067               j++;
5068           }
5069           else
5070             j++;
5071         }
5072         else
5073           j++;
5074       }
5075     }
5076     if ( idef.m_object_uuid.Count() <= 0 )
5077     {
5078       if ( text_log )
5079         text_log->Print("m_idef_table[%d].m_object_uuid.Count() = 0.\n",i);
5080       idef_ok = false;
5081     }
5082     if ( !idef_ok && rc == 0 )
5083       rc = 6;
5084 
5085   }
5086 
5087   return rc;
5088 }
5089 
5090 
AuditObjectTableHelper(ONX_Model & model,bool bAttemptRepair,int * repair_count,ON_TextLog * text_log)5091 static int AuditObjectTableHelper(
5092       ONX_Model& model,
5093       bool bAttemptRepair,
5094       int* repair_count,
5095       ON_TextLog* text_log
5096       )
5097 {
5098   // AuditObjectIds() are already validated/repaired object ids.
5099 
5100   int rc = 0;
5101   int i, count = model.m_object_table.Count();
5102 
5103   for ( i = 0; i < count; i++ )
5104   {
5105     ONX_Model_Object& obj = model.m_object_table[i];
5106     if ( 0 == obj.m_object )
5107     {
5108       rc = 7;
5109       if ( text_log )
5110         text_log->Print("m_object_table[%d].m_object is NULL.\n",i);
5111     }
5112     else if ( false == obj.m_object->IsValid(NULL) )
5113     {
5114       rc = 8;
5115       if ( text_log )
5116       {
5117         text_log->Print("m_object_table[%d].m_object->IsValid() = false.\n",i);
5118         text_log->PushIndent();
5119         text_log->PushIndent();
5120         obj.m_object->IsValid(text_log);
5121         text_log->PopIndent();
5122         text_log->PopIndent();
5123       }
5124     }
5125 
5126     int attrc = AuditObjectAttributesHelper(model,
5127                                 obj.m_attributes,
5128                                 "m_object_table[%d].m_attributes",
5129                                 i,
5130                                 bAttemptRepair,
5131                                 repair_count,
5132                                 text_log
5133                                 );
5134     if ( 0 == rc && attrc )
5135       rc = attrc;
5136   }
5137 
5138   return rc;
5139 }
5140 
AuditHistoryRecordTableHelper(ONX_Model &,bool,int *,ON_TextLog *)5141 static int AuditHistoryRecordTableHelper(
5142       ONX_Model&,
5143       bool,
5144       int*,
5145       ON_TextLog*
5146       )
5147 {
5148   // TODO - make sure object id's exist
5149   return 0;
5150 }
5151 
Audit(bool bAttemptRepair,int * repair_count,ON_TextLog * text_log,ON_SimpleArray<int> * warnings)5152 int ONX_Model::Audit(
5153       bool bAttemptRepair,
5154       int* repair_count,
5155       ON_TextLog* text_log,
5156       ON_SimpleArray<int>* warnings
5157       )
5158 {
5159   int warning_count = 0;
5160   int rc = 0, i;
5161   int repcnt;
5162   if ( repair_count )
5163     *repair_count = 0;
5164 
5165   repcnt = 0;
5166   i = AuditModelIdsHelper( *this, bAttemptRepair, &repcnt, text_log, warnings );
5167   if ( repair_count )
5168     *repair_count += repcnt;
5169   if ( i < 0 )
5170     return i;
5171   warning_count += i;
5172 
5173   repcnt = 0;
5174   i = AuditTextureMappingTableHelper( *this, bAttemptRepair, &repcnt, text_log );
5175   if ( repair_count )
5176     *repair_count += repcnt;
5177   if ( i < 0 )
5178     return i;
5179   if ( 0 == rc && 0 != i )
5180     rc = i;
5181 
5182   repcnt = 0;
5183   i = AuditMaterialTableHelper( *this, bAttemptRepair, &repcnt, text_log );
5184   if ( repair_count )
5185     *repair_count += repcnt;
5186   if ( i < 0 )
5187     return i;
5188   if ( 0 == rc && 0 != i )
5189     rc = i;
5190 
5191   repcnt = 0;
5192   i = AuditLinetypeTableHelper( *this, bAttemptRepair, &repcnt, text_log );
5193   if ( repair_count )
5194     *repair_count += repcnt;
5195   if ( i < 0 )
5196     return i;
5197   if ( 0 == rc && 0 != i )
5198     rc = i;
5199 
5200   repcnt = 0;
5201   i = AuditLayerTableHelper( *this, bAttemptRepair, &repcnt, text_log );
5202   if ( repair_count )
5203     *repair_count += repcnt;
5204   if ( i < 0 )
5205     return i;
5206   if ( 0 == rc && 0 != i )
5207     rc = i;
5208 
5209   repcnt = 0;
5210   i = AuditGroupTableHelper( *this, bAttemptRepair, &repcnt, text_log );
5211   if ( repair_count )
5212     *repair_count += repcnt;
5213   if ( i < 0 )
5214     return i;
5215   if ( 0 == rc && 0 != i )
5216     rc = i;
5217 
5218   repcnt = 0;
5219   i = AuditFontTableHelper( *this, bAttemptRepair, &repcnt, text_log );
5220   if ( repair_count )
5221     *repair_count += repcnt;
5222   if ( i < 0 )
5223     return i;
5224   if ( 0 == rc && 0 != i )
5225     rc = i;
5226 
5227   repcnt = 0;
5228   i = AuditDimStyleTableHelper( *this, bAttemptRepair, &repcnt, text_log );
5229   if ( repair_count )
5230     *repair_count += repcnt;
5231   if ( i < 0 )
5232     return i;
5233   if ( 0 == rc && 0 != i )
5234     rc = i;
5235 
5236   repcnt = 0;
5237   i = AuditLightTableHelper( *this, bAttemptRepair, &repcnt, text_log );
5238   if ( repair_count )
5239     *repair_count += repcnt;
5240   if ( i < 0 )
5241     return i;
5242   if ( 0 == rc && 0 != i )
5243     rc = i;
5244 
5245   repcnt = 0;
5246   i = AuditHatchPatternTableHelper( *this, bAttemptRepair, &repcnt, text_log );
5247   if ( repair_count )
5248     *repair_count += repcnt;
5249   if ( i < 0 )
5250     return i;
5251   if ( 0 == rc && 0 != i )
5252     rc = i;
5253 
5254   repcnt = 0;
5255   i = AuditIDefTableHelper( *this, bAttemptRepair, &repcnt, text_log );
5256   if ( repair_count )
5257     *repair_count += repcnt;
5258   if ( i < 0 )
5259     return i;
5260   if ( 0 == rc && 0 != i )
5261     rc = i;
5262 
5263   repcnt = 0;
5264   i = AuditObjectTableHelper( *this, bAttemptRepair, &repcnt, text_log );
5265   if ( repair_count )
5266     *repair_count += repcnt;
5267   if ( i < 0 )
5268     return i;
5269   if ( 0 == rc && 0 != i )
5270     rc = i;
5271 
5272   repcnt = 0;
5273   i = AuditHistoryRecordTableHelper( *this, bAttemptRepair, &repcnt, text_log );
5274   if ( repair_count )
5275     *repair_count += repcnt;
5276   if ( i < 0 )
5277     return i;
5278   if ( 0 == rc && 0 != i )
5279     rc = i;
5280 
5281   return warning_count;
5282 }
5283 
RDKObjectUserDataHelper(const ON_UserData * objectud)5284 static const ON_UnknownUserData* RDKObjectUserDataHelper(const ON_UserData* objectud)
5285 {
5286   // CRhRdkUserData object id: AFA82772-1525-43dd-A63C-C84AC5806911
5287   // CRhRdkUserData::m_userdata_uuid = B63ED079-CF67-416c-800D-22023AE1BE21
5288 
5289   // CRhRdkUserData object id
5290   // {AFA82772-1525-43dd-A63C-C84AC5806911}
5291   static const ON_UUID CRhRdkUserData_object_id =
5292   { 0xAFA82772, 0x1525, 0x43dd, { 0xA6, 0x3C, 0xC8, 0x4A, 0xC5, 0x80, 0x69, 0x11 } };
5293 
5294   // CRhRdkUserData::m_userdata_uuid
5295   // {B63ED079-CF67-416c-800D-22023AE1BE21}
5296   static const ON_UUID CRhRdkUserData_userdata_uuid =
5297   { 0xB63ED079, 0xCF67, 0x416c, { 0x80, 0x0D, 0x22, 0x02, 0x3A, 0xE1, 0xBE, 0x21 } };
5298 
5299   const ON_UnknownUserData* unknown_ud = ON_UnknownUserData::Cast(objectud);
5300 
5301   bool rc = ( 0 != unknown_ud
5302               && unknown_ud->m_sizeof_buffer > 0
5303               && 0 != unknown_ud->m_buffer
5304               && 0 == ON_UuidCompare(CRhRdkUserData_object_id,unknown_ud->m_unknownclass_uuid)
5305               && 0 == ON_UuidCompare(CRhRdkUserData_userdata_uuid,unknown_ud->m_userdata_uuid)
5306             );
5307   return rc ? unknown_ud : 0;
5308 }
5309 
IsRDKObjectInformation(const ON_UserData & objectud)5310 bool ONX_Model::IsRDKObjectInformation(const ON_UserData& objectud)
5311 {
5312   return 0 != RDKObjectUserDataHelper(&objectud);
5313 }
5314 
GetRDKObjectInformation(const ON_Object & object,ON_wString & rdk_xml_object_data)5315 bool ONX_Model::GetRDKObjectInformation(const ON_Object& object,ON_wString& rdk_xml_object_data)
5316 {
5317   rdk_xml_object_data.SetLength(0);
5318   const ON_UnknownUserData* unknown_ud = 0;
5319   const ON_UserData* ud = ON_UserData::Cast(&object);
5320   if ( 0 != ud )
5321   {
5322     unknown_ud = RDKObjectUserDataHelper(ud);
5323   }
5324   else
5325   {
5326     for ( ud = object.FirstUserData(); 0 != ud && 0 == unknown_ud; ud = ud->Next() )
5327     {
5328       unknown_ud = RDKObjectUserDataHelper(ud);
5329     }
5330   }
5331 
5332   if ( 0 == unknown_ud )
5333     return false;
5334 
5335   ON_Read3dmBufferArchive a(unknown_ud->m_sizeof_buffer,unknown_ud->m_buffer,false,unknown_ud->m_3dm_version,unknown_ud->m_3dm_opennurbs_version);
5336   int version = 0;
5337   if (!a.ReadInt(&version) )
5338     return false;
5339 
5340   if ( 1 == version )
5341   {
5342     if ( !a.ReadString(rdk_xml_object_data) )
5343       return false;
5344   }
5345   else if ( 2 == version )
5346   {
5347     // UTF8 string
5348     ON_SimpleArray< char > s;
5349     int slen = 0;
5350     if ( !a.ReadInt(&slen) )
5351       return false;
5352     if ( slen <= 0 )
5353       return false;
5354     if ( slen + 4 > unknown_ud->m_sizeof_buffer )
5355       return false;
5356     s.Reserve(slen+1);
5357     s.SetCount(slen+1);
5358     s[slen] = 0;
5359     if ( !a.ReadChar(slen,s.Array() ) )
5360       return false;
5361     const char* sArray = s.Array();
5362     if ( 0 != sArray && 0 != sArray[0] )
5363     {
5364       unsigned int error_status = 0;
5365       int wLen = ON_ConvertUTF8ToWideChar(sArray,-1,0,0,&error_status,0,0,0);
5366       if ( wLen > 0 && 0 == error_status )
5367       {
5368         rdk_xml_object_data.SetLength(wLen+2);
5369         wLen = ON_ConvertUTF8ToWideChar(sArray,-1,rdk_xml_object_data.Array(),wLen+1,&error_status,0,0,0);
5370         if ( wLen > 0 && 0 == error_status )
5371           rdk_xml_object_data.SetLength(wLen);
5372         else
5373           rdk_xml_object_data.SetLength(0);
5374       }
5375       if ( 0 != error_status )
5376       {
5377         ON_ERROR("RDK xml object information is not a valid UTF-8 string.");
5378       }
5379     }
5380   }
5381 
5382   return rdk_xml_object_data.Length() > 0;
5383 }
5384 
IsRDKDocumentInformation(const ONX_Model_UserData & docud)5385 bool ONX_Model::IsRDKDocumentInformation(const ONX_Model_UserData& docud)
5386 {
5387   // {16592D58-4A2F-401D-BF5E-3B87741C1B1B}
5388   static const ON_UUID rdk_plugin_id =
5389   { 0x16592D58, 0x4A2F, 0x401D, { 0xBF, 0x5E, 0x3B, 0x87, 0x74, 0x1C, 0x1B, 0x1B } };
5390 
5391   return ( 0 == ON_UuidCompare(rdk_plugin_id,docud.m_uuid) && docud.m_goo.m_value >= 4 && 0 != docud.m_goo.m_goo );
5392 }
5393 
5394 
GetRDKDocumentInformation(const ONX_Model_UserData & docud,ON_wString & rdk_xml_document_data)5395 bool ONX_Model::GetRDKDocumentInformation(const ONX_Model_UserData& docud,ON_wString& rdk_xml_document_data)
5396 {
5397   if ( !ONX_Model::IsRDKDocumentInformation(docud) )
5398     return false;
5399 
5400   ON_Read3dmBufferArchive a(docud.m_goo.m_value,docud.m_goo.m_goo,false,docud.m_usertable_3dm_version,docud.m_usertable_opennurbs_version);
5401 
5402   int version = 0;
5403   if (!a.ReadInt(&version) )
5404     return false;
5405 
5406   if ( 1 == version )
5407   {
5408     // UTF-16 string
5409     if ( !a.ReadString(rdk_xml_document_data) )
5410       return false;
5411   }
5412   else if ( 3 == version )
5413   {
5414     // UTF-8 string
5415     int slen = 0;
5416     if ( !a.ReadInt(&slen) )
5417       return 0;
5418     if ( slen <= 0 )
5419       return 0;
5420     if ( slen + 4 > docud.m_goo.m_value )
5421       return 0;
5422     ON_String s;
5423     s.SetLength(slen);
5424     if ( !a.ReadChar(slen,s.Array()) )
5425       return 0;
5426     const char* sArray = s.Array();
5427     if ( 0 != sArray && 0 != sArray[0] )
5428     {
5429       unsigned int error_status = 0;
5430       int wLen = ON_ConvertUTF8ToWideChar(sArray,-1,0,0,&error_status,0,0,0);
5431       if ( wLen > 0 && 0 == error_status )
5432       {
5433         rdk_xml_document_data.SetLength(wLen+2);
5434         wLen = ON_ConvertUTF8ToWideChar(sArray,-1,rdk_xml_document_data.Array(),wLen+1,&error_status,0,0,0);
5435         if ( wLen > 0 && 0 == error_status )
5436           rdk_xml_document_data.SetLength(wLen);
5437         else
5438         {
5439           rdk_xml_document_data.SetLength(0);
5440         }
5441       }
5442       if ( 0 != error_status )
5443       {
5444         ON_ERROR("RDK xml document settings is not a valid UTF-8 string.");
5445       }
5446     }
5447   }
5448 
5449   return rdk_xml_document_data.Length() > 0;
5450 }
5451 
5452 #if defined(ON_COMPILER_MSC)
5453 #pragma warning( pop )
5454 #endif
5455