opennurbs_extensions.cpp
Go to the documentation of this file.
00001 /* $NoKeywords: $ */
00002 /*
00003 //
00004 // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
00005 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
00006 // McNeel & Associates.
00007 //
00008 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
00009 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
00010 // MERCHANTABILITY ARE HEREBY DISCLAIMED.
00011 //                              
00012 // For complete openNURBS copyright information see <http://www.opennurbs.org>.
00013 //
00015 */
00016 #include "pcl/surface/3rdparty/opennurbs/opennurbs.h"
00017 
00018 
00019 #if defined(ON_COMPILER_MSC)
00020 // Disable the MSC /W4 warning C4127: conditional expression is constant
00021 //
00022 // This file has a lot of for( i = 0; true; i < 123 )...
00023 // loops where the "true" generates a 4127 warning.
00024 // This source code has to work on many different
00025 // compilers I do not trust all of them to correctly
00026 // compile for( i = 0; /* empty condition*/; i < 123) ...
00027 #pragma warning( push )
00028 #pragma warning( disable : 4127 )
00029 #endif
00030 
00031 
00032 #if defined(ON_DLL_EXPORTS)
00033 
00035 //
00036 // When openNURBS is used as a Microsoft Windows DLL, it is possible 
00037 // for new/delete to allocate memory in one executable and delete
00038 // it in another.  Because Microsoft Windows has incompatible memory 
00039 // managers in its plethora of C libraries and the choice of which
00040 // C library actually gets used depends on the code generation 
00041 // options you choose,  we get lots of support questions asking
00042 // about hard to trace crashes.
00043 //
00044 // If you are using openNURBS as a Windows DLL, you are sure you know
00045 // what you are doing, and you promise never to ask for support, then
00046 // feel free to delete these overrides.
00047 //
00048 //
00049 #pragma message( " --- OpenNURBS overriding ONX_Model new and delete" )
00050 
00051 // ONX_Model_UserData new/delete
00052 
00053 void* ONX_Model_UserData::operator new(size_t sz)
00054 {
00055   // ONX_Model_UserData new
00056   return onmalloc(sz);
00057 }
00058 
00059 void ONX_Model_UserData::operator delete(void* p)
00060 {
00061   // ONX_Model_UserData delete
00062   onfree(p);
00063 }
00064 
00065 void* ONX_Model_UserData::operator new[] (size_t sz)
00066 {
00067   // ONX_Model_UserData array new
00068   return onmalloc(sz);
00069 }
00070 
00071 void ONX_Model_UserData::operator delete[] (void* p)
00072 {
00073   // ONX_Model_UserData array delete
00074   onfree(p);
00075 }
00076 
00077 void* ONX_Model_UserData::operator new(size_t, void* p)
00078 {
00079   // ONX_Model_UserData placement new
00080   return p;
00081 }
00082 
00083 void ONX_Model_UserData::operator delete(void*, void*)
00084 {
00085   // ONX_Model_UserData placement delete
00086   return;
00087 }
00088 
00089 
00090 // ONX_Model_Object new/delete
00091 
00092 void* ONX_Model_Object::operator new(size_t sz)
00093 {
00094   // ONX_Model_Object new
00095   return onmalloc(sz);
00096 }
00097 
00098 void ONX_Model_Object::operator delete(void* p)
00099 {
00100   // ONX_Model_Object delete
00101   onfree(p);
00102 }
00103 
00104 void* ONX_Model_Object::operator new[] (size_t sz)
00105 {
00106   // ONX_Model_Object array new
00107   return onmalloc(sz);
00108 }
00109 
00110 void ONX_Model_Object::operator delete[] (void* p)
00111 {
00112   // ONX_Model_Object array delete
00113   onfree(p);
00114 }
00115 
00116 void* ONX_Model_Object::operator new(size_t, void* p)
00117 {
00118   // ONX_Model_Object placement new
00119   return p;
00120 }
00121 
00122 void ONX_Model_Object::operator delete(void*, void*)
00123 {
00124   // ONX_Model_Object placement delete
00125   return;
00126 }
00127 
00128 
00129 // ONX_Model_RenderLight new/delete
00130 
00131 void* ONX_Model_RenderLight::operator new(size_t sz)
00132 {
00133   // ONX_Model_RenderLight new
00134   return onmalloc(sz);
00135 }
00136 
00137 void ONX_Model_RenderLight::operator delete(void* p)
00138 {
00139   // ONX_Model_RenderLight delete
00140   onfree(p);
00141 }
00142 
00143 void* ONX_Model_RenderLight::operator new[] (size_t sz)
00144 {
00145   // ONX_Model_RenderLight array new
00146   return onmalloc(sz);
00147 }
00148 
00149 void ONX_Model_RenderLight::operator delete[] (void* p)
00150 {
00151   // ONX_Model_RenderLight array delete
00152   onfree(p);
00153 }
00154 
00155 void* ONX_Model_RenderLight::operator new(size_t, void* p)
00156 {
00157   // ONX_Model_RenderLight placement new
00158   return p;
00159 }
00160 
00161 void ONX_Model_RenderLight::operator delete(void*, void*)
00162 {
00163   // ONX_Model_RenderLight placement delete
00164   return;
00165 }
00166 
00167 
00168 // ONX_Model new/delete
00169 
00170 void* ONX_Model::operator new(size_t sz)
00171 {
00172   // ONX_Model new
00173   return onmalloc(sz);
00174 }
00175 
00176 void ONX_Model::operator delete(void* p)
00177 {
00178   // ONX_Model delete
00179   onfree(p);
00180 }
00181 
00182 void* ONX_Model::operator new[] (size_t sz)
00183 {
00184   // ONX_Model array new
00185   return onmalloc(sz);
00186 }
00187 
00188 void ONX_Model::operator delete[] (void* p)
00189 {
00190   // ONX_Model array delete
00191   onfree(p);
00192 }
00193 
00194 void* ONX_Model::operator new(size_t, void* p)
00195 {
00196   // ONX_Model placement new
00197   return p;
00198 }
00199 
00200 void ONX_Model::operator delete(void*, void*)
00201 {
00202   // ONX_Model placement delete
00203   return;
00204 }
00205 
00206 #endif
00207 
00208 //
00209 //
00211 
00212 
00213 static
00214 bool ONX_IsValidNameFirstChar( wchar_t c )
00215 {
00216   if ( c > 127 )
00217     return true;
00218   if ( c < '0' )
00219     return false;
00220   if ( c <= '9' )
00221     return true;
00222   if ( c < 'A' )
00223     return false;
00224   if ( c <= 'Z' )
00225     return true;
00226   if ( c == '_' )
00227     return true;
00228   if ( c < 'a' )
00229     return false;
00230   if ( c <= 'z' )
00231     return true;
00232   return false;
00233 }
00234 
00235 static
00236 bool ONX_IsValidNameSecondChar( wchar_t c )
00237 {
00238   // control characters, double quote, and DEL are 
00239   // not permited in names
00240   return (c >= 32 && c != 34 && c != 127);
00241 }
00242 
00243 bool ONX_IsValidName( 
00244           const wchar_t* name 
00245           )
00246 {
00247   bool is_valid = (0 != name && ONX_IsValidNameFirstChar(*name));
00248   if ( is_valid )
00249   {
00250     bool is_integer = (*name >= '0' && *name <= '9');
00251     name++;
00252     while ( ONX_IsValidNameSecondChar(*name) )
00253     {
00254       if ( *name < '0' || *name >= '9' )
00255         is_integer = false;
00256       name++;
00257     }
00258     if ( *name || is_integer )
00259       is_valid = false;
00260     else if ( name[-1] <= 32 )
00261       is_valid = false; // last character cannot be a space
00262   }
00263   return is_valid;
00264 }
00265 
00268 
00269 ONX_Model_RenderLight::ONX_Model_RenderLight()
00270 {
00271 }
00272 
00273 ONX_Model_RenderLight::~ONX_Model_RenderLight()
00274 {
00275 }
00276 
00277 ONX_Model_RenderLight::ONX_Model_RenderLight(const ONX_Model_RenderLight& src) 
00278              : m_light(src.m_light), 
00279                m_attributes(src.m_attributes)
00280 {
00281 }
00282 
00283 ONX_Model_RenderLight& ONX_Model_RenderLight::operator=(const ONX_Model_RenderLight& src)
00284 {
00285   if ( this != &src )
00286   {
00287     m_light = src.m_light;
00288     m_attributes = src.m_attributes;
00289   }
00290   return *this;
00291 }
00292 
00295 
00296 
00297 ONX_Model_Object::ONX_Model_Object() 
00298                  : m_bDeleteObject(0),
00299                    m_object(0), 
00300                    m_ref_count(0)
00301 {
00302 }
00303 
00304 void ONX_Model_Object::Destroy()
00305 {
00306   if ( m_ref_count ) 
00307   {
00308     if ( *m_ref_count > 0 )
00309       (*m_ref_count)--;
00310     if ( *m_ref_count <= 0 ) 
00311     {
00312       delete m_ref_count;
00313       m_ref_count = 0;
00314     }
00315   }
00316   if ( 0 == m_ref_count && 0 != m_object && m_bDeleteObject )
00317   {
00318     delete m_object;
00319   }
00320   m_object = 0;
00321   m_bDeleteObject = false;
00322 }
00323 
00324 ONX_Model_Object::~ONX_Model_Object()
00325 {
00326   Destroy();
00327 }
00328 
00329 ONX_Model_Object::ONX_Model_Object(const ONX_Model_Object& src) 
00330              : m_bDeleteObject(0),
00331                m_object(0),
00332                m_ref_count(0)
00333 {
00334   *this = src;
00335 }
00336 
00337 ONX_Model_Object& ONX_Model_Object::operator=(const ONX_Model_Object& src)
00338 {
00339   if ( this != &src )
00340   {
00341     Destroy();
00342     m_bDeleteObject = src.m_bDeleteObject;
00343     m_object = src.m_object;
00344     m_attributes = src.m_attributes;
00345     m_ref_count = src.m_ref_count;
00346     if ( 0 != m_object && m_bDeleteObject ) 
00347     {
00348       if ( 0 != m_ref_count )
00349         (*m_ref_count)++;
00350       else 
00351       {
00352         m_ref_count = new unsigned int;
00353         *m_ref_count = 2; // 2 because this and src reference same m_object
00354         const_cast<ONX_Model_Object*>(&src)->m_ref_count = m_ref_count; // need to defeat const
00355       }
00356     }
00357   }
00358   return *this;
00359 }
00360 
00363 
00364 
00365 ONX_Model_UserData::ONX_Model_UserData() 
00366 : m_uuid(ON_nil_uuid)
00367 , m_usertable_3dm_version(0)
00368 , m_usertable_opennurbs_version(0)
00369 {
00370 }
00371 
00372 ONX_Model_UserData::~ONX_Model_UserData()
00373 {
00374 }
00375 
00376 ONX_Model_UserData::ONX_Model_UserData(const ONX_Model_UserData& src) 
00377 : m_uuid(src.m_uuid)
00378 , m_goo(src.m_goo)
00379 , m_usertable_3dm_version(src.m_usertable_3dm_version)
00380 , m_usertable_opennurbs_version(src.m_usertable_opennurbs_version)
00381 {
00382 }
00383 
00384 ONX_Model_UserData& ONX_Model_UserData::operator=(const ONX_Model_UserData& src)
00385 {
00386   if ( this != &src )
00387   {
00388     m_uuid = src.m_uuid;
00389     m_goo = src.m_goo;
00390     m_usertable_3dm_version = src.m_usertable_3dm_version;
00391     m_usertable_opennurbs_version = src.m_usertable_opennurbs_version;
00392   }
00393   return *this;
00394 }
00395 
00398 
00399 ONX_Model::ONX_Model() 
00400           : m_3dm_file_version(0), 
00401             m_3dm_opennurbs_version(0),
00402             m_file_length(0),
00403             m_crc_error_count(0)
00404 {
00405   m_sStartSectionComments.Empty();
00406   m_properties.Default();
00407   m_settings.Default();
00408 }
00409 
00410 ONX_Model::~ONX_Model()
00411 {
00412   Destroy();
00413 }
00414 
00415 void ONX_Model::Destroy()
00416 {
00417   int i;
00418   m_3dm_file_version = 0;
00419   m_3dm_opennurbs_version = 0;
00420   m_sStartSectionComments.Empty();
00421   m_properties.Default();
00422   m_settings.Default();
00423 
00424   for ( i = 0; i < m_history_record_table.Count(); i++ )
00425     delete m_history_record_table[i];
00426   m_history_record_table.Zero();
00427 
00428   for ( i = 0; i < m_bitmap_table.Count(); i++ )
00429     delete m_bitmap_table[i];
00430   m_bitmap_table.Zero();
00431 
00432   m_bitmap_table.SetCapacity(0);
00433   m_mapping_table.SetCapacity(0);
00434   m_material_table.SetCapacity(0);
00435   m_linetype_table.SetCapacity(0);
00436   m_layer_table.SetCapacity(0);
00437   m_group_table.SetCapacity(0);
00438   m_font_table.SetCapacity(0);
00439   m_dimstyle_table.SetCapacity(0);
00440   m_light_table.SetCapacity(0);
00441   m_hatch_pattern_table.SetCapacity(0);
00442   m_idef_table.SetCapacity(0);
00443   m_object_table.SetCapacity(0);
00444   m_history_record_table.SetCapacity(0);  
00445   m_userdata_table.SetCapacity(0);
00446 
00447   m_file_length = 0;
00448   m_crc_error_count = 0;
00449 
00450   DestroyCache();
00451 }
00452 
00453 
00454 void ONX_Model::DestroyCache()
00455 {
00456   m_mapping_id_index.Empty();
00457   m_material_id_index.Empty();
00458   m_idef_id_index.Empty();
00459   m_object_id_index.Empty();
00460 
00461   m__object_table_bbox.Destroy();
00462 }
00463 
00464 ON_BoundingBox ONX_Model::BoundingBox() const
00465 {
00466   if( !m__object_table_bbox.IsValid() && m_object_table.Count() > 0 )
00467   {
00468     ON_BoundingBox bbox;
00469     int i, object_count = m_object_table.Count();
00470     for ( i = 0; i < object_count; i++ )
00471     {
00472       const ON_Geometry* geo = ON_Geometry::Cast(m_object_table[i].m_object);
00473       if ( geo )
00474         bbox.Union(geo->BoundingBox());
00475       const_cast<ONX_Model*>(this)->m__object_table_bbox = bbox;
00476     }
00477   }
00478   return m__object_table_bbox;
00479 }
00480 
00481 void ONX_Model::GetRenderMaterial( const ON_3dmObjectAttributes& attributes, ON_Material& material ) const
00482 {
00483   int material_index = -1;
00484 
00485   switch ( attributes.MaterialSource() )
00486   {
00487   case ON::material_from_layer:
00488     if ( attributes.m_layer_index >= 0 && attributes.m_layer_index < m_layer_table.Count() )
00489       material_index = m_layer_table[attributes.m_layer_index].RenderMaterialIndex();
00490     break;
00491   case ON::material_from_object:
00492     material_index = attributes.m_material_index;
00493     break;
00494 
00495   case ON::material_from_parent:
00496     material_index = attributes.m_material_index;
00497     // TODO: If object is an idef, get material from iref attributes.
00498     break;
00499   }
00500 
00501   if ( material_index < 0 || material_index >= m_material_table.Count() )
00502   {
00503     material_index = -1;
00504     material.Default();
00505   }
00506   else
00507   {
00508     material = m_material_table[material_index];
00509   }
00510 
00511   material.SetMaterialIndex(material_index);
00512 }
00513 
00514 void ONX_Model::GetRenderMaterial( 
00515       int object_index,
00516       ON_Material& material 
00517       ) const
00518 {
00519   if ( object_index < 0 || object_index >= m_object_table.Count() )
00520   {
00521     material.Default();
00522     material.SetMaterialIndex(-1);    
00523   }
00524   else
00525     GetRenderMaterial( m_object_table[object_index].m_attributes, material );
00526 }
00527 
00528 void ONX_Model::GetLinetype( const ON_3dmObjectAttributes& attributes, ON_Linetype& linetype ) const
00529 {
00530   int linetype_index = -1;
00531 
00532   switch ( attributes.LinetypeSource() )
00533   {
00534   case ON::linetype_from_layer:
00535     if ( attributes.m_layer_index >= 0 && attributes.m_layer_index < m_layer_table.Count() )
00536       linetype_index = m_layer_table[attributes.m_layer_index].LinetypeIndex();
00537     break;
00538   case ON::linetype_from_object:
00539     linetype_index = attributes.m_linetype_index;
00540     break;
00541   case ON::linetype_from_parent:
00542     linetype_index = attributes.m_linetype_index;
00543     // TODO: if object is an instance definition, get linetype
00544     //       from instance references.
00545     break;
00546   }
00547 
00548   if ( linetype_index < 0 || linetype_index >= m_linetype_table.Count() )
00549   {
00550     linetype_index = -1;
00551     linetype.Default();
00552   }
00553   else
00554   {
00555     linetype = m_linetype_table[linetype_index];
00556   }
00557 
00558   linetype.SetLinetypeIndex(linetype_index);
00559 }
00560 
00561 void ONX_Model::GetLinetype( 
00562       int object_index,
00563       ON_Linetype& linetype
00564       ) const
00565 {
00566   if ( object_index < 0 || object_index >= m_object_table.Count() )
00567   {
00568     linetype.Default();
00569     linetype.SetLinetypeIndex(-1);    
00570   }
00571   else
00572   {
00573     GetLinetype( m_object_table[object_index].m_attributes, linetype );
00574   }
00575 }
00576 
00577 
00578 
00579 ON_Color ONX_Model::WireframeColor( const ON_3dmObjectAttributes& attributes ) const
00580 {
00581   ON_Color color = ON_UNSET_COLOR;
00582 
00583   switch ( attributes.ColorSource() )
00584   {
00585   case ON::color_from_layer:
00586     if ( attributes.m_layer_index >= 0 && attributes.m_layer_index < m_layer_table.Count() )
00587       color = m_layer_table[attributes.m_layer_index].Color();
00588     break;
00589 
00590   case ON::color_from_object:
00591     color = attributes.m_color;
00592     break;
00593 
00594   case ON::color_from_material:
00595     {
00596       ON_Material mat;
00597       GetRenderMaterial( attributes, mat );
00598       color = mat.Diffuse();
00599     }
00600     break;
00601 
00602   case ON::color_from_parent:
00603     color = attributes.m_color;
00604     // TODO: if object is an instance definition, get color
00605     //       from instance references.
00606     break;
00607   }
00608 
00609   if ( color == ON_UNSET_COLOR )
00610     color.SetRGB(128,128,128);
00611 
00612   return color;
00613 }
00614 
00615 ON_Color ONX_Model::WireframeColor(int object_index) const
00616 {
00617   ON_Color c;
00618   if ( object_index < 0 || object_index >= m_object_table.Count() )
00619   {
00620     ON_3dmObjectAttributes a;
00621     c = a.m_color;
00622   }
00623   else
00624     c = WireframeColor( m_object_table[object_index].m_attributes );
00625   return c;
00626 }
00627 
00628 
00629 void ONX_DumpView( ON_TextLog& dump, const ON_3dmView& view )
00630 {
00631   view.Dump(dump);
00632 }
00633 
00634 void ONX_Model::DumpSummary( ON_TextLog& dump ) const
00635 {
00636   dump.Print("File version: %d\n",m_3dm_file_version);
00637   dump.Print("File openNURBS version: %d\n",m_3dm_opennurbs_version);
00638   if ( m_file_length > 0 )
00639     dump.Print("File length: %d bytes\n",m_file_length);
00640 
00641   if ( m_sStartSectionComments.Length() > 0 ) 
00642   {
00643     dump.Print("Start section comments:\n");
00644     dump.PushIndent();
00645     dump.PrintWrappedText(m_sStartSectionComments);
00646     dump.PopIndent();
00647     dump.Print("\n");
00648   }
00649   
00650   m_properties.Dump(dump);
00651 
00652   dump.Print("\n");
00653 
00654   m_settings.Dump(dump);
00655 
00656   dump.Print("\n");
00657 
00658   dump.Print("Contents:\n");
00659   dump.PushIndent();
00660   dump.Print("%d embedded bitmaps\n",m_bitmap_table.Count());
00661   dump.Print("%d render material definitions\n",m_material_table.Count());
00662   dump.Print("%d line type definitions\n",m_linetype_table.Count());
00663   dump.Print("%d layers\n",m_layer_table.Count());
00664   dump.Print("%d render lights\n",m_light_table.Count());
00665   dump.Print("%d groups\n",m_group_table.Count());
00666   dump.Print("%d objects\n",m_object_table.Count());
00667   dump.Print("%d user data objects\n",m_userdata_table.Count());
00668   dump.PopIndent();
00669 }
00670 
00671 void ONX_Model::DumpBitmapTable( ON_TextLog& dump) const
00672 {
00673   int i;
00674   for ( i = 0; i < m_bitmap_table.Count(); i++ )
00675   {
00676     dump.Print("Bitmap %d:\n",i);
00677     dump.PushIndent();
00678     m_bitmap_table[i]->Dump(dump);
00679     dump.PopIndent();
00680   }
00681 }
00682 
00683 void ONX_Model::DumpTextureMappingTable( ON_TextLog& dump) const
00684 {
00685   int i;
00686   for ( i = 0; i < m_mapping_table.Count(); i++ )
00687   {
00688     dump.Print("Texture Mapping %d:\n",i);
00689     dump.PushIndent();
00690     m_mapping_table[i].Dump(dump);
00691     dump.PopIndent();
00692   }
00693 }
00694 
00695 void ONX_Model::DumpMaterialTable( ON_TextLog& dump) const
00696 {
00697   int i;
00698   for ( i = 0; i < m_material_table.Count(); i++ )
00699   {
00700     dump.Print("Material %d:\n",i);
00701     dump.PushIndent();
00702     m_material_table[i].Dump(dump);
00703     dump.PopIndent();
00704   }
00705 }
00706 
00707 void ONX_Model::DumpLinetypeTable( ON_TextLog& dump ) const
00708 {
00709   int i;
00710   for ( i = 0; i < m_linetype_table.Count(); i++ )
00711   {
00712     dump.Print("Linetype %d:\n",i);
00713     dump.PushIndent();
00714     m_linetype_table[i].Dump(dump);
00715     dump.PopIndent();
00716   }
00717 }
00718 
00719 void ONX_Model::DumpLayerTable( ON_TextLog& dump) const
00720 {
00721   int i;
00722   for ( i = 0; i < m_layer_table.Count(); i++ )
00723   {
00724     dump.Print("Layer %d:\n",i);
00725     dump.PushIndent();
00726     m_layer_table[i].Dump(dump);
00727     dump.PopIndent();
00728   }
00729 }
00730 
00731 void ONX_Model::DumpLightTable( ON_TextLog& dump) const
00732 {
00733   int i;
00734   for ( i = 0; i < m_light_table.Count(); i++ )
00735   {
00736     dump.Print("Light %d:\n",i);
00737     dump.PushIndent();
00738     m_light_table[i].m_attributes.Dump(dump);
00739     m_light_table[i].m_light.Dump(dump);
00740     dump.PopIndent();
00741   }
00742 }
00743 
00744 void ONX_Model::DumpGroupTable( ON_TextLog& dump) const
00745 {
00746   int i;
00747   for ( i = 0; i < m_group_table.Count(); i++ )
00748   {
00749     dump.Print("Group %d:\n",i);
00750     dump.PushIndent();
00751     m_group_table[i].Dump(dump);
00752     dump.PopIndent();
00753   }
00754 }
00755 
00756 void ONX_Model::DumpFontTable( ON_TextLog& dump) const
00757 {
00758   int i;
00759   for ( i = 0; i < m_font_table.Count(); i++ )
00760   {
00761     dump.Print("Font %d:\n",i);
00762     dump.PushIndent();
00763     m_font_table[i].Dump(dump);
00764     dump.PopIndent();
00765   }
00766 }
00767 
00768 void ONX_Model::DumpDimStyleTable( ON_TextLog& dump) const
00769 {
00770   int i;
00771   for ( i = 0; i < m_dimstyle_table.Count(); i++ )
00772   {
00773     dump.Print("DimStyle %d:\n",i);
00774     dump.PushIndent();
00775     m_dimstyle_table[i].Dump(dump);
00776     dump.PopIndent();
00777   }
00778 }
00779 
00780 
00781 void ONX_Model::DumpHatchPatternTable( ON_TextLog& dump) const
00782 {
00783   int i;
00784   for ( i = 0; i < m_hatch_pattern_table.Count(); i++ )
00785   {
00786     dump.Print("HatchPattern %d:\n",i);
00787     dump.PushIndent();
00788     m_hatch_pattern_table[i].Dump(dump);
00789     dump.PopIndent();
00790   }
00791 }
00792 
00793 void ONX_Model::DumpIDefTable( ON_TextLog& dump) const
00794 {
00795   int i;
00796   for ( i = 0; i < m_idef_table.Count(); i++ )
00797   {
00798     dump.Print("Instance Definition %d:\n",i);
00799     dump.PushIndent();
00800     m_idef_table[i].Dump(dump);
00801     dump.PopIndent();
00802   }
00803 }
00804 
00805 void ONX_Model_Object::Dump( ON_TextLog& dump ) const
00806 {
00807   if ( 0 != m_object )
00808   {
00809     m_object->Dump(dump);
00810 
00811     // user data attached to this object
00812     const ON_UserData* ud = m_object->FirstUserData();
00813     while(0 != ud)
00814     {
00815       dump.Print("object user data:\n");
00816       dump.PushIndent();
00817       ud->Dump(dump);
00818       dump.PopIndent();
00819       ud = ud->Next();
00820     }
00821   }
00822   else
00823   {
00824     dump.Print("NULL m_object pointer\n");
00825   }
00826 
00827   // Use Cast() if you need to get at the details.
00828   /*
00829   const ON_Geometry* pGeometry = ON_Geometry::Cast(m_object);
00830   if ( pGeometry ) 
00831   {
00832     // m_object is some type of geometric object
00833     if ( ON_Extrusion::Cast(m_object) ) 
00834     {
00835       // m_object is derived from ON_Extrusion
00836       //  Note: 
00837       //   ON_Extrusion::BrepForm() will return a brep form
00838       //   if you don't want to explicitly handle extrusions. 
00839       const ON_Extrusion* extrusion = ON_Extrusion::Cast(m_object);
00840     }
00841     else if ( ON_Brep::Cast(m_object) ) 
00842     {
00843       // m_object is derived from ON_Brep
00844       const ON_Brep* brep = ON_Brep::Cast(m_object);
00845     }
00846     else if ( pGeometry->HasBrepForm() ) 
00847     {
00848       // m_object is note derived from ON_Brep but its geometry can
00849       // be represented by an ON_Brep.
00850       ON_Brep* brep = pGeometry->BrepForm();
00851       // you manage the ON_Brep returned by pGeometry->BrepForm();
00852       delete brep;
00853     }
00854     else if ( ON_Curve::Cast(m_object) ) 
00855     {
00856       // curve objects
00857       if ( ON_NurbsCurve::Cast(m_object) ) {
00858         const ON_NurbsCurve* pCurve = ON_NurbsCurve::Cast(m_object);
00859       }
00860       else if ( ON_ArcCurve::Cast(m_object) ) {
00861         const ON_ArcCurve* pCurve = ON_ArcCurve::Cast(m_object);
00862       }
00863       else if ( ON_CurveOnSurface::Cast(m_object) ) {
00864         const ON_CurveOnSurface* pCurve = ON_CurveOnSurface::Cast(m_object);
00865       }
00866       else if ( ON_BrepEdge::Cast(m_object) ) {
00867         const ON_BrepEdge* pCurve = ON_BrepEdge::Cast(m_object);
00868       }
00869       else if ( ON_LineCurve::Cast(m_object) ) {
00870         const ON_LineCurve* pCurve = ON_LineCurve::Cast(m_object);
00871       }
00872       else if ( ON_PolyCurve::Cast(m_object) ) {
00873         const ON_PolyCurve* pCurve = ON_PolyCurve::Cast(m_object);
00874       }
00875       else if ( ON_PolylineCurve::Cast(m_object) ) {
00876         const ON_PolylineCurve* pCurve = ON_PolylineCurve::Cast(m_object);
00877       }
00878       else if ( ON_CurveProxy::Cast(m_object) ) {
00879         const ON_CurveProxy* pCurve = ON_CurveProxy::Cast(m_object);
00880       }
00881       else {
00882         const ON_Curve* pCurve = ON_Curve::Cast(m_object);
00883       }
00884     }
00885     else if ( ON_Surface::Cast(m_object) ) 
00886     {
00887       // surface objects
00888       if ( ON_NurbsSurface::Cast(m_object) ) {
00889         const ON_NurbsSurface* pSurface = ON_NurbsSurface::Cast(m_object);
00890       }
00891       else if ( ON_PlaneSurface::Cast(m_object) ) {
00892         const ON_PlaneSurface* pSurface = ON_PlaneSurface::Cast(m_object);
00893       }
00894       else if ( ON_RevSurface::Cast(m_object) ) {
00895         const ON_RevSurface* pSurface = ON_RevSurface::Cast(m_object);
00896       }
00897       else if ( ON_BrepFace::Cast(m_object) ) {
00898         const ON_BrepFace* pSurface = ON_BrepFace::Cast(m_object);
00899       }
00900       else if ( ON_SurfaceProxy::Cast(m_object) ) {
00901         const ON_SurfaceProxy* pSurface = ON_SurfaceProxy::Cast(m_object);
00902       }
00903       else {
00904         const ON_Surface* pSurface = ON_Surface::Cast(m_object);
00905       }
00906     }
00907     else if ( ON_Mesh::Cast(m_object) ) 
00908     {
00909       const ON_Mesh* pMesh = ON_Mesh::Cast(m_object);
00910     }
00911   }
00912   */
00913 }
00914 
00915 void ONX_Model::DumpObjectTable( ON_TextLog& dump) const
00916 {
00917   int i;
00918   for ( i = 0; i < m_object_table.Count(); i++ )
00919   {
00920     dump.Print("Object %d:\n",i);
00921     dump.PushIndent();
00922 
00923     // object's attibutes
00924     m_object_table[i].m_attributes.Dump(dump);
00925 
00926     // object definition
00927     m_object_table[i].Dump(dump);
00928 
00929     dump.PopIndent();
00930   }
00931 }
00932 
00933 void ONX_Model::DumpHistoryRecordTable( ON_TextLog& dump) const
00934 {
00935   int i;
00936   for ( i = 0; i < m_history_record_table.Count(); i++ )
00937   {
00938     dump.Print("History record %d:\n",i);
00939     dump.PushIndent();
00940 
00941     const ON_HistoryRecord* history_record = m_history_record_table[i];
00942     if ( history_record )
00943     {
00944       history_record->Dump(dump);
00945     }
00946     else
00947     {
00948       dump.Print("Missing.\n");
00949     }
00950 
00951     dump.PopIndent();
00952   }
00953 }
00954 
00955 void ONX_Model::DumpUserDataTable( ON_TextLog& dump) const
00956 {
00957   int i;
00958   for ( i = 0; i < m_userdata_table.Count(); i++ )
00959   {
00960     const ONX_Model_UserData& ud = m_userdata_table[i];
00961     dump.Print("User Data Table %d:\n",i);
00962     dump.PushIndent();
00963     dump.Print("uuid = "); dump.Print(ud.m_uuid); dump.Print("\n");
00964     ud.m_goo.Dump(dump);    
00965     dump.PopIndent();
00966   }
00967 }
00968 
00969 void ONX_Model::Dump( ON_TextLog& dump ) const
00970 {
00971   dump.Print("Model summary:\n");
00972   dump.PushIndent();
00973   DumpSummary(dump);
00974   dump.PopIndent();
00975   dump.Print("\n");
00976 
00977   dump.Print("Bitmap table:\n");
00978   dump.PushIndent();
00979   DumpBitmapTable(dump);
00980   dump.PopIndent();
00981   dump.Print("\n");
00982 
00983   dump.Print("TextureMapping table:\n");
00984   dump.PushIndent();
00985   DumpTextureMappingTable(dump);
00986   dump.PopIndent();
00987   dump.Print("\n");
00988 
00989   dump.Print("Material table:\n");
00990   dump.PushIndent();
00991   DumpMaterialTable(dump);
00992   dump.PopIndent();
00993   dump.Print("\n");
00994 
00995   dump.Print("Line type table:\n");
00996   dump.PushIndent();
00997   DumpLinetypeTable(dump);
00998   dump.PopIndent();
00999   dump.Print("\n");
01000 
01001   dump.Print("Layer table:\n");
01002   dump.PushIndent();
01003   DumpLayerTable(dump);
01004   dump.PopIndent();
01005   dump.Print("\n");
01006 
01007   dump.Print("Group table:\n");
01008   dump.PushIndent();
01009   DumpGroupTable(dump);
01010   dump.PopIndent();
01011   dump.Print("\n");
01012 
01013   dump.Print("Font table:\n");
01014   dump.PushIndent();
01015   DumpFontTable(dump);
01016   dump.PopIndent();
01017   dump.Print("\n");
01018 
01019   dump.Print("DimStyle table:\n");
01020   dump.PushIndent();
01021   DumpDimStyleTable(dump);
01022   dump.PopIndent();
01023   dump.Print("\n");
01024 
01025   dump.Print("Light table:\n");
01026   dump.PushIndent();
01027   DumpLightTable(dump);
01028   dump.PopIndent();
01029   dump.Print("\n");
01030 
01031   dump.Print("HatchPattern table:\n");
01032   dump.PushIndent();
01033   DumpHatchPatternTable(dump);
01034   dump.PopIndent();
01035   dump.Print("\n");
01036 
01037   dump.Print("Instance Definition table:\n");
01038   dump.PushIndent();
01039   DumpIDefTable(dump);
01040   dump.PopIndent();
01041   dump.Print("\n");
01042 
01043   dump.Print("Object table:\n");
01044   dump.PushIndent();
01045   DumpObjectTable(dump);
01046   dump.PopIndent();
01047   dump.Print("\n");
01048 
01049   dump.Print("History record table:\n");
01050   dump.PushIndent();
01051   DumpHistoryRecordTable(dump);
01052   dump.PopIndent();
01053   dump.Print("\n");
01054 
01055   dump.Print("User data table:\n");
01056   dump.PushIndent();
01057   DumpUserDataTable(dump);
01058   dump.PopIndent();
01059   dump.Print("\n");
01060 }
01061 
01062 static 
01063 bool CheckForCRCErrors( 
01064           ON_BinaryArchive& archive, 
01065           ONX_Model& model,
01066           ON_TextLog* error_log,
01067           const char* sSection
01068           )
01069 {
01070   // returns true if new CRC errors are found
01071   bool rc = false;
01072   int new_crc_count = archive.BadCRCCount();
01073   
01074   if ( model.m_crc_error_count != new_crc_count ) 
01075   {
01076     if ( error_log )
01077     {
01078       error_log->Print("ERROR: Corrupt %s. (CRC errors).\n",sSection);
01079       error_log->Print("-- Attempting to continue.\n");
01080     }
01081     model.m_crc_error_count = new_crc_count;
01082     rc = true;
01083   }
01084 
01085   return rc;
01086 }
01087 
01088 class ON__CIndexPair
01089 {
01090 public:
01091   static int CompareOldIndex( const ON__CIndexPair* a, const ON__CIndexPair* b );
01092   static int CompareOldAndNewIndex( const ON__CIndexPair* a, const ON__CIndexPair* b );
01093   int m_old_index;  // value in model.m_..._table[m_table_index].m_..._index; (Read from file)
01094   int m_new_index;  // index in model.m_..._table[] array
01095 };
01096 
01097 int ON__CIndexPair::CompareOldIndex( const ON__CIndexPair* a, const ON__CIndexPair* b )
01098 {
01099   return (a->m_old_index - b->m_old_index);
01100 }
01101 
01102 int ON__CIndexPair::CompareOldAndNewIndex( const ON__CIndexPair* a, const ON__CIndexPair* b )
01103 {
01104   int i;
01105   if ( 0 == (i = a->m_old_index - b->m_old_index) )
01106     i = a->m_new_index - b->m_new_index;
01107   return i;
01108 }
01109 
01110 class ON__CIndexMaps
01111 {
01112 public:
01113   ON__CIndexMaps( ONX_Model& model ) 
01114     : m_model(model),
01115       m_bRemapLayerIndex(0), 
01116       m_bRemapMaterialIndex(0), 
01117       m_bRemapLinetypeIndex(0),
01118       m_bRemapGroupIndex(0),
01119       m_bRemapFontIndex(0),
01120       m_bRemapDimstyleIndex(0),
01121       m_bRemapHatchPatternIndex(0),
01122       m_layer_count(0),
01123       m_group_count(0),
01124       m_material_count(0),
01125       m_linetype_count(0),
01126       m_font_count(0),
01127       m_dimstyle_count(0),
01128       m_hatch_pattern_count(0),
01129       m_default_layer_index(0),
01130       m_default_group_index(-1),
01131       m_default_material_index(-1),
01132       m_default_linetype_index(-1),
01133       m_default_font_index(0),
01134       m_default_dimstyle_index(0),
01135       m_default_hatch_pattern_index(-1)
01136   { 
01137     CreateHelper();
01138   }
01139 
01140   /*
01141   Description:
01142     Remap all tables in m_model.
01143   Returns:
01144     Number of indices that were changed.
01145   */
01146   int RemapModel();
01147 
01148   ONX_Model& m_model;
01149 
01150   bool m_bRemapLayerIndex;
01151   bool m_bRemapMaterialIndex;
01152   bool m_bRemapLinetypeIndex;
01153   bool m_bRemapGroupIndex;
01154   bool m_bRemapFontIndex;
01155   bool m_bRemapDimstyleIndex;
01156   bool m_bRemapHatchPatternIndex;
01157 
01158   int m_layer_count;
01159   int m_group_count;
01160   int m_material_count;
01161   int m_linetype_count;
01162   int m_font_count;
01163   int m_dimstyle_count;
01164   int m_hatch_pattern_count;
01165 
01166   int m_default_layer_index;
01167   int m_default_group_index;
01168   int m_default_material_index;
01169   int m_default_linetype_index;
01170   int m_default_font_index;
01171   int m_default_dimstyle_index;
01172   int m_default_hatch_pattern_index;
01173 
01174   ON_SimpleArray<ON__CIndexPair> m_layer_map;
01175   ON_SimpleArray<ON__CIndexPair> m_group_map;
01176   ON_SimpleArray<ON__CIndexPair> m_material_map;
01177   ON_SimpleArray<ON__CIndexPair> m_linetype_map;
01178   ON_SimpleArray<ON__CIndexPair> m_font_map;
01179   ON_SimpleArray<ON__CIndexPair> m_dimstyle_map;
01180   ON_SimpleArray<ON__CIndexPair> m_hatch_pattern_map;
01181 
01182 
01183   /*
01184   Description:
01185     Low level tool to convert old_layer_index into a valid 
01186     m_layer_table[] index.
01187   Parameters:
01188     old_layer_index - [in]
01189   Returns:
01190     new layer index to use.
01191   */
01192   int RemapLayerIndex( int old_layer_index ) const;
01193 
01194   /*
01195   Description:
01196     Low level tool to convert old_material_index into a valid 
01197     m_material_table[] index  or -1 if the default material 
01198     should be used.
01199   Parameters:
01200     old_material_index - [in]
01201   Returns:
01202     new material index to use.
01203   */
01204   int RemapMaterialIndex( int old_material_index ) const;
01205 
01206   /*
01207   Description:
01208     Low level tool to convert old_linetype_index into a valid 
01209     m_linetype_table[] index or -1 if the default linetype 
01210     should be used.
01211   Parameters:
01212     old_linetype_index - [in]
01213   Returns:
01214     new linetype index to use.
01215   */
01216   int RemapLinetypeIndex( int old_linetype_index ) const;
01217 
01218   /*
01219   Description:
01220     Low level tool to convert old_group_index into a valid 
01221     m_group_table[] index or -1 if no conversion is possible.
01222   Parameters:
01223     old_group_index - [in]
01224   Returns:
01225     new group index to use or -1 if old_group_index makes no sense.
01226   */
01227   int RemapGroupIndex( int old_group_index ) const;
01228 
01229   /*
01230   Description:
01231     Low level tool to convert old_font_index into a valid 
01232     m_font_table[] index or -1 if no conversion is possible.
01233   Parameters:
01234     old_font_index - [in]
01235   Returns:
01236     new font index to use or -1 if old_font_index makes no sense.
01237   */
01238   int RemapFontIndex( int old_font_index ) const;
01239 
01240   /*
01241   Description:
01242     Low level tool to convert old_dimstyle_index into a valid 
01243     m_dimstyle_table[] index or -1 if no conversion is possible.
01244   Parameters:
01245     old_dimstyle_index - [in]
01246   Returns:
01247     new dimstyle index to use or -1 if old_dimstyle_index makes no sense.
01248   */
01249   int RemapDimstyleIndex( int old_dimstyle_index ) const;
01250 
01251   /*
01252   Description:
01253     Low level tool to convert old_hatch_pattern_index into a valid 
01254     m_hatch_pattern_table[] index or -1 if no conversion is possible.
01255   Parameters:
01256     old_hatch_pattern_index - [in]
01257   Returns:
01258     new hatch pattern index to use or -1 if old_hatch_pattern_index makes no sense.
01259   */
01260   int RemapHatchPatternIndex( int old_hatch_pattern_index ) const;
01261 
01262   /*
01263   Description:
01264     Low level tool to remap table indices used by model objects in
01265     the object attributes class.
01266   */
01267   int RemapGeometryAndObjectAttributes( ONX_Model_Object& );
01268 
01269   /*
01270   Description:
01271     Low level tool to remap table indices used by model objects in
01272     the object attributes class.
01273   Returns:
01274     Number of indices that were changed.
01275   */
01276   int RemapGeometryAttributes( ON_Object* );
01277 
01278   /*
01279   Description:
01280     Low level tool to remap table indices saved in
01281     the object attributes class.
01282   Returns:
01283     Number of indices that were changed.
01284   */
01285   int RemapObjectAttributes( ON_3dmObjectAttributes& );
01286 
01287   /*
01288   Description:
01289     Low level tool to remap table indices saved in
01290     the object attributes class.
01291   Returns:
01292     Number of indices that were changed.
01293   */
01294   int RemapLayerAttributes( ON_Layer& );
01295 
01296   /*
01297   Description:
01298     Low level tool to remap material table indices saved in
01299     the rendering attributes class.
01300   Returns:
01301     Number of indices that were changed.
01302   */
01303   int RemapRenderingAttributes( ON_RenderingAttributes& ra );
01304 
01305 private:
01306   int CreateHelper();
01307 
01308 private:
01309   // no implementation - prohibit use
01310   ON__CIndexMaps(const ON__CIndexMaps&);
01311   ON__CIndexMaps& operator=(const ON__CIndexMaps&);
01312 };
01313 
01314 
01315 int ON__CIndexMaps::CreateHelper()
01316 {
01317   int change_count = 0;
01318   int i;
01319 
01320   // bitmaps are not referenced by index any place,
01321   // so just set bitmap index to match table index
01322   for ( i = 0; i < m_model.m_bitmap_table.Count(); i++ )
01323   {
01324     ON_Bitmap* bitmap = m_model.m_bitmap_table[i];
01325     if ( !bitmap )
01326     {
01327       change_count++;
01328       m_model.m_bitmap_table.Remove(i);
01329       i--;
01330       continue;
01331     }
01332 
01333     if ( bitmap->m_bitmap_index != i )
01334     {
01335       bitmap->m_bitmap_index = i;
01336       change_count++;
01337     }
01338     if ( ON_nil_uuid == bitmap->m_bitmap_id )
01339     {
01340       ON_CreateUuid(bitmap->m_bitmap_id);
01341       change_count++;
01342     }
01343   }
01344 
01345   // texture maps are not referenced  by index 
01346   // so just set texture map index to match table
01347   // index
01348   m_model.m_mapping_id_index.Empty();
01349   m_model.m_mapping_id_index.Reserve(m_model.m_mapping_table.Count());
01350   for ( i = 0; i < m_model.m_mapping_table.Count(); i++ )
01351   {
01352     ON_TextureMapping& mapping = m_model.m_mapping_table[i];
01353     if ( mapping.m_mapping_index != i )
01354     {
01355       mapping.m_mapping_index = i;
01356       change_count++;
01357     }
01358     if ( ON_nil_uuid == mapping.m_mapping_id )
01359     {
01360       ON_CreateUuid(mapping.m_mapping_id);
01361       change_count++;
01362     }
01363     m_model.m_mapping_id_index.AddUuidIndex(mapping.m_mapping_id,i,false);
01364   }
01365 
01366   // make sure material indices are valid
01367   m_model.m_material_id_index.Empty();
01368   m_model.m_material_id_index.Reserve(m_model.m_material_table.Count());
01369   m_bRemapMaterialIndex = false;
01370   m_material_count = m_model.m_material_table.Count();
01371   m_material_map.SetCount(0);
01372   m_material_map.Reserve(m_material_count);
01373   for ( i = 0; i < m_material_count; i++ )
01374   {
01375     ON_Material& material = m_model.m_material_table[i];
01376     ON__CIndexPair& ip = m_material_map.AppendNew();
01377     ip.m_new_index = i;
01378     ip.m_old_index = material.m_material_index;
01379     if ( material.m_material_index != i )
01380     {
01381       material.m_material_index = i;
01382       m_bRemapMaterialIndex = true;
01383       change_count++;
01384     }
01385     if ( ON_nil_uuid == material.m_material_id )
01386     {
01387       ON_CreateUuid(material.m_material_id);
01388       change_count++;
01389     }
01390     m_model.m_material_id_index.AddUuidIndex(material.m_material_id,i,false);
01391   }
01392 
01393   // make sure linetype indices are valid
01394   m_bRemapLinetypeIndex = false;
01395   m_linetype_count = m_model.m_linetype_table.Count();
01396   m_linetype_map.SetCount(0);
01397   m_linetype_map.Reserve(m_linetype_count);
01398   for ( i = 0; i < m_linetype_count; i++ )
01399   {
01400     ON_Linetype& linetype = m_model.m_linetype_table[i];
01401     ON__CIndexPair& ip = m_linetype_map.AppendNew();
01402     ip.m_new_index = i;
01403     ip.m_old_index = linetype.m_linetype_index;
01404     if ( linetype.m_linetype_index != i )
01405     {
01406       linetype.m_linetype_index = i;
01407       m_bRemapLinetypeIndex = true;
01408       change_count++;
01409     }
01410     if ( ON_nil_uuid == linetype.m_linetype_id )
01411     {
01412       ON_CreateUuid(linetype.m_linetype_id);
01413       change_count++;
01414     }
01415   }
01416 
01417   // make sure there is at least one layer
01418   if ( m_model.m_layer_table.Count() < 1 )
01419   {
01420     ON_Layer layer;
01421     layer.Default();
01422     m_model.GetUnusedLayerName(layer.m_name);
01423     if ( !ONX_IsValidName(layer.m_name) )
01424       layer.m_name = L"Default";
01425     layer.m_layer_index = 0;
01426     ON_CreateUuid(layer.m_layer_id);
01427     m_model.m_layer_table.Append(layer);
01428     change_count++;
01429   }
01430 
01431   // make sure layer indices are valid
01432   m_bRemapLayerIndex = false;
01433   m_layer_count = m_model.m_layer_table.Count();
01434   m_layer_map.SetCount(0);
01435   m_layer_map.Reserve(m_layer_count);
01436   for ( i = 0; i < m_layer_count; i++ )
01437   {
01438     ON_Layer& layer = m_model.m_layer_table[i];
01439     ON__CIndexPair& ip = m_layer_map.AppendNew();
01440     ip.m_new_index = i;
01441     ip.m_old_index = layer.m_layer_index;
01442     if ( layer.m_layer_index != i )
01443     {
01444       layer.m_layer_index = i;
01445       m_bRemapLayerIndex = true;
01446       change_count++;
01447     }
01448     if ( ON_nil_uuid == layer.m_layer_id )
01449     {
01450       ON_CreateUuid(layer.m_layer_id);
01451       change_count++;
01452     }
01453   }
01454 
01455   // make sure group indices are valid
01456   m_bRemapGroupIndex = false;
01457   m_group_count = m_model.m_group_table.Count();
01458   m_group_map.SetCount(0);
01459   m_group_map.Reserve(m_group_count);
01460   for ( i = 0; i < m_group_count; i++ )
01461   {
01462     ON_Group& group = m_model.m_group_table[i];
01463     ON__CIndexPair& ip = m_group_map.AppendNew();
01464     ip.m_new_index = i;
01465     ip.m_old_index = group.m_group_index;
01466     if ( group.m_group_index != i )
01467     {
01468       group.m_group_index = i;
01469       m_bRemapGroupIndex = true;
01470       change_count++;
01471     }
01472     if ( ON_nil_uuid == group.m_group_id )
01473     {
01474       ON_CreateUuid(group.m_group_id);
01475       change_count++;
01476     }
01477   }
01478 
01479   // make sure there is at least one font
01480   if ( m_model.m_font_table.Count() < 1 )
01481   {
01482     ON_Font font;
01483     font.Defaults();
01484     if ( !ONX_IsValidName(font.m_font_name) )
01485       font.m_font_name = L"Default";
01486     font.m_font_index = 0;
01487     ON_CreateUuid(font.m_font_id);
01488     m_model.m_font_table.Append(font);
01489     change_count++;
01490   }
01491 
01492   // make sure font indices are valid
01493   m_bRemapFontIndex = false;
01494   m_font_count = m_model.m_font_table.Count();
01495   m_font_map.SetCount(0);
01496   m_font_map.Reserve(m_font_count);
01497   for ( i = 0; i < m_font_count; i++ )
01498   {
01499     ON_Font& font = m_model.m_font_table[i];
01500     ON__CIndexPair& ip = m_font_map.AppendNew();
01501     ip.m_new_index = i;
01502     ip.m_old_index = font.m_font_index;
01503     if ( font.m_font_index != i )
01504     {
01505       font.m_font_index = i;
01506       m_bRemapFontIndex = true;
01507       change_count++;
01508     }
01509     if ( ON_nil_uuid == font.m_font_id )
01510     {
01511       ON_CreateUuid(font.m_font_id);
01512       change_count++;
01513     }
01514   }
01515 
01516   // make sure there is at least one dimstyle
01517   if ( m_model.m_dimstyle_table.Count() < 1 )
01518   {
01519     ON_DimStyle dimstyle;
01520     dimstyle.SetDefaults();
01521     if ( !ONX_IsValidName(dimstyle.m_dimstyle_name) )
01522       dimstyle.m_dimstyle_name = L"Default";
01523     dimstyle.m_dimstyle_index = 0;
01524     ON_CreateUuid(dimstyle.m_dimstyle_id);
01525     dimstyle.m_fontindex = 0;
01526     m_model.m_dimstyle_table.Append(dimstyle);
01527     change_count++;
01528   }
01529 
01530   // make sure dimstyle indices are valid
01531   m_bRemapDimstyleIndex = false;
01532   m_dimstyle_count = m_model.m_dimstyle_table.Count();
01533   m_dimstyle_map.SetCount(0);
01534   m_dimstyle_map.Reserve(m_dimstyle_count);
01535   for ( i = 0; i < m_dimstyle_count; i++ )
01536   {
01537     ON_DimStyle& dimstyle = m_model.m_dimstyle_table[i];
01538     ON__CIndexPair& ip = m_dimstyle_map.AppendNew();
01539     ip.m_new_index = i;
01540     ip.m_old_index = dimstyle.m_dimstyle_index;
01541     if ( dimstyle.m_dimstyle_index != i )
01542     {
01543       dimstyle.m_dimstyle_index = i;
01544       m_bRemapDimstyleIndex = true;
01545       change_count++;
01546     }
01547     if ( ON_nil_uuid == dimstyle.m_dimstyle_id )
01548     {
01549       ON_CreateUuid(dimstyle.m_dimstyle_id);
01550       change_count++;
01551     }
01552   }
01553 
01554   // lights are not referenced by index any place,
01555   // so just set light index to match table index
01556   for ( i = 0; i < m_model.m_light_table.Count(); i++ )
01557   {
01558     ONX_Model_RenderLight& light = m_model.m_light_table[i];
01559     if ( light.m_light.m_light_index != i )
01560     {
01561       light.m_light.m_light_index = i;
01562       change_count++;
01563     }
01564 
01565     if ( light.m_light.m_light_id == light.m_attributes.m_uuid )
01566     {
01567       // ids match - this is good
01568       if ( ON_nil_uuid == light.m_light.m_light_id )
01569       {
01570         // ids not set
01571         ON_CreateUuid(light.m_light.m_light_id);
01572         light.m_attributes.m_uuid = light.m_light.m_light_id;
01573         change_count++;
01574       }
01575     }
01576     else if ( ON_nil_uuid == light.m_light.m_light_id )
01577     {
01578       // id not set on the light object
01579       light.m_light.m_light_id = light.m_attributes.m_uuid;
01580       change_count++;
01581     }
01582     else if ( ON_nil_uuid == light.m_attributes.m_uuid )
01583     {
01584       // id not set on the attributes
01585       light.m_attributes.m_uuid = light.m_light.m_light_id;
01586       change_count++;
01587     }
01588     else 
01589     {
01590       // id's are different - the one on the light object wins
01591       light.m_attributes.m_uuid = light.m_light.m_light_id;
01592       change_count++;
01593     }
01594   }
01595 
01596   // make sure hatch pattern indices are valid
01597   m_bRemapHatchPatternIndex = false;
01598   m_hatch_pattern_count = m_model.m_hatch_pattern_table.Count();
01599   m_hatch_pattern_map.SetCount(0);
01600   m_hatch_pattern_map.Reserve(m_hatch_pattern_count);
01601   for ( i = 0; i < m_hatch_pattern_count; i++ )
01602   {
01603     ON_HatchPattern& hatchpattern = m_model.m_hatch_pattern_table[i];
01604     ON__CIndexPair& ip = m_hatch_pattern_map.AppendNew();
01605     ip.m_new_index = i;
01606     ip.m_old_index = hatchpattern.m_hatchpattern_index;
01607     if ( ip.m_new_index != ip.m_old_index )
01608     {
01609       hatchpattern.m_hatchpattern_index = i;
01610       m_bRemapHatchPatternIndex = true;
01611       change_count++;
01612     }
01613     if ( ON_nil_uuid == hatchpattern.m_hatchpattern_id )
01614     {
01615       ON_CreateUuid(hatchpattern.m_hatchpattern_id);
01616       change_count++;
01617     }
01618   }
01619 
01620   // make sure idefs have valid ids
01621   m_model.m_idef_id_index.Empty();
01622   m_model.m_idef_id_index.Reserve(m_model.m_idef_table.Count());
01623   for ( i = 0; i < m_model.m_idef_table.Count(); i++ )
01624   {
01625     ON_InstanceDefinition& idef = m_model.m_idef_table[i];
01626     if ( ON_nil_uuid == idef.m_uuid )
01627     {
01628       ON_CreateUuid(idef.m_uuid);
01629       change_count++;
01630     }
01631     m_model.m_idef_id_index.AddUuidIndex(idef.m_uuid,i,false);
01632   }
01633 
01634   // make sure objects have valid ids
01635   m_model.m_object_id_index.Empty();
01636   m_model.m_object_id_index.Reserve(m_model.m_object_table.Count());
01637   for ( i = 0; i < m_model.m_object_table.Count(); i++ )
01638   {
01639     ONX_Model_Object& mo = m_model.m_object_table[i];
01640     if ( ON_nil_uuid == mo.m_attributes.m_uuid )
01641     {
01642       ON_CreateUuid(mo.m_attributes.m_uuid);
01643       change_count++;
01644     }
01645     m_model.m_object_id_index.AddUuidIndex(mo.m_attributes.m_uuid,i,false);
01646   }
01647 
01648   // make sure history records have valid ids
01649   for ( i = 0; i < m_model.m_history_record_table.Count(); i++ )
01650   {
01651     ON_HistoryRecord* hr = m_model.m_history_record_table[i];
01652     if ( !hr )
01653     {
01654       change_count++;
01655       m_model.m_history_record_table.Remove(i);
01656       i--;
01657       continue;
01658     }
01659     if ( ON_nil_uuid == hr->m_record_id )
01660     {
01661       ON_CreateUuid(hr->m_record_id);
01662       change_count++;
01663     }
01664   }
01665 
01666   // Sort the maps
01667   if ( m_bRemapLayerIndex )
01668     m_layer_map.QuickSort( ON__CIndexPair::CompareOldAndNewIndex );
01669   if ( m_bRemapGroupIndex )
01670     m_group_map.QuickSort( ON__CIndexPair::CompareOldAndNewIndex );
01671   if ( m_bRemapMaterialIndex )
01672     m_material_map.QuickSort( ON__CIndexPair::CompareOldAndNewIndex );
01673   if ( m_bRemapLinetypeIndex )
01674     m_linetype_map.QuickSort( ON__CIndexPair::CompareOldAndNewIndex );
01675   if ( m_bRemapFontIndex )
01676     m_font_map.QuickSort( ON__CIndexPair::CompareOldAndNewIndex );
01677   if ( m_bRemapDimstyleIndex )
01678     m_dimstyle_map.QuickSort( ON__CIndexPair::CompareOldAndNewIndex );
01679   if ( m_bRemapHatchPatternIndex )
01680     m_hatch_pattern_map.QuickSort( ON__CIndexPair::CompareOldAndNewIndex );
01681 
01682   return change_count;
01683 }
01684 
01685 int ON__CIndexMaps::RemapGeometryAttributes( ON_Object* object )
01686 {
01687   int change_count = 0;
01688 
01689   switch(object ? object->ObjectType() : ON::unknown_object_type )
01690   {
01691   case ON::layer_object:
01692     {
01693       ON_Layer* layer = ON_Layer::Cast(object);
01694       if ( layer )
01695         change_count += RemapLayerAttributes(*layer);
01696     }
01697     break;
01698 
01699   case ON::annotation_object:
01700     {
01701       ON_Annotation2* ann = ON_Annotation2::Cast(object);
01702       if ( ann )
01703       {
01704         if (ann->IsText() )
01705         {
01706           // ann->m_index is a font index
01707           int old_font_index = ann->m_index;
01708           int new_font_index = RemapFontIndex(old_font_index);
01709           if ( new_font_index != old_font_index )
01710           {
01711             ann->m_index = new_font_index;
01712             change_count++;
01713           }
01714         }
01715         else
01716         {
01717           // ann->m_index is a dimstyle index
01718           int old_dimstyle_index = ann->m_index;
01719           int new_dimstyle_index = RemapDimstyleIndex(old_dimstyle_index);
01720           {
01721             if ( old_dimstyle_index != new_dimstyle_index )
01722             {
01723               ann->m_index = new_dimstyle_index;
01724               change_count++;
01725             }
01726           }
01727         }
01728       }
01729     }
01730     break;
01731 
01732   case ON::hatch_object:
01733     {
01734       ON_Hatch* hatch_object = ON_Hatch::Cast(object);
01735       if ( hatch_object )
01736       {
01737         int old_hatch_pattern_index = hatch_object->PatternIndex();
01738         int new_hatch_pattern_index = RemapHatchPatternIndex(old_hatch_pattern_index);
01739         if ( old_hatch_pattern_index != new_hatch_pattern_index )
01740           hatch_object->SetPatternIndex(new_hatch_pattern_index);
01741       }
01742     }
01743     break;
01744 
01745   default:
01746     // other object types skipped on purpose
01747     break;
01748   }
01749 
01750   return change_count;
01751 }
01752 
01753 int ON__CIndexMaps::RemapGeometryAndObjectAttributes( ONX_Model_Object& model_object )
01754 {
01755   int geometry_change_count  = RemapGeometryAttributes(const_cast<ON_Object*>(model_object.m_object));
01756   int attribute_change_count = RemapObjectAttributes( model_object.m_attributes );
01757   return (geometry_change_count + attribute_change_count);
01758 }
01759 
01760 int ON__CIndexMaps::RemapModel()
01761 {
01762   int change_count = 0;
01763 
01764   int i, old_index, new_index;
01765 
01766   // make sure current layer is valid and in "normal" mode
01767   old_index =  m_model.m_settings.m_current_layer_index;
01768   new_index = RemapLayerIndex(old_index);
01769   if ( new_index < 0 || new_index >= m_layer_count )
01770   {
01771     new_index = 0;
01772   }
01773   m_model.m_settings.m_current_layer_index = new_index;
01774   if ( !m_model.m_layer_table[new_index].IsVisibleAndNotLocked() )
01775   {
01776     m_model.m_layer_table[new_index].SetVisible( true );
01777     m_model.m_layer_table[new_index].SetLocked( false );
01778   }
01779   m_default_layer_index = m_model.m_settings.m_current_layer_index;
01780 
01781   for ( i = 0; i < m_model.m_layer_table.Count(); i++ )
01782   {
01783     change_count += RemapLayerAttributes(m_model.m_layer_table[i]);
01784   }
01785 
01786   for ( i = 0; i < m_model.m_dimstyle_table.Count(); i++ )
01787   {
01788     old_index = m_model.m_dimstyle_table[i].m_fontindex;
01789     new_index = RemapFontIndex(old_index);
01790     if ( new_index != old_index )
01791     {
01792       m_model.m_dimstyle_table[i].m_fontindex = new_index;
01793       change_count++;
01794     }
01795   }
01796 
01797   for ( i = 0; i < m_model.m_light_table.Count(); i++ )
01798   {
01799     change_count += RemapObjectAttributes( m_model.m_light_table[i].m_attributes );
01800   }
01801 
01802   for ( i = 0; i < m_model.m_object_table.Count(); i++ )
01803   {
01804     change_count += RemapGeometryAndObjectAttributes( m_model.m_object_table[i] );
01805   }
01806 
01807   return change_count;
01808 }
01809 
01810 
01811 
01812 static int RemapIndexHelper( 
01813                 int old_index, 
01814                 bool bRemapIndex, 
01815                 int count, 
01816                 int default_index,
01817                 const ON_SimpleArray<ON__CIndexPair>& imap
01818                 )
01819 {
01820   int new_index = old_index;
01821   if ( bRemapIndex )
01822   {
01823     ON__CIndexPair ip;
01824     memset(&ip,0,sizeof(ip));
01825     ip.m_old_index = old_index;
01826     int j = imap.BinarySearch(&ip,ON__CIndexPair::CompareOldIndex);
01827     if ( j >= 0 )
01828       new_index = imap[j].m_new_index;
01829   }
01830   if ( new_index < 0 || new_index >= count )
01831     new_index = default_index;
01832   return new_index;
01833 }
01834 
01835 int ON__CIndexMaps::RemapLayerIndex( int old_layer_index ) const
01836 {
01837   return RemapIndexHelper(
01838               old_layer_index,
01839               m_bRemapLayerIndex,
01840               m_layer_count,
01841               m_default_layer_index,
01842               m_layer_map
01843               );
01844 }
01845 
01846 int ON__CIndexMaps::RemapMaterialIndex( int old_material_index ) const
01847 {
01848   return RemapIndexHelper(
01849               old_material_index,
01850               m_bRemapMaterialIndex,
01851               m_material_count,
01852               m_default_material_index,
01853               m_material_map
01854               );
01855 }
01856 
01857 int ON__CIndexMaps::RemapLinetypeIndex( int old_linetype_index ) const
01858 {
01859   return RemapIndexHelper(
01860               old_linetype_index,
01861               m_bRemapLinetypeIndex,
01862               m_linetype_count,
01863               m_default_linetype_index,
01864               m_linetype_map
01865               );
01866 }
01867 
01868 int ON__CIndexMaps::RemapGroupIndex( int old_group_index ) const
01869 {
01870   return RemapIndexHelper(
01871               old_group_index,
01872               m_bRemapGroupIndex,
01873               m_group_count,
01874               m_default_group_index,
01875               m_group_map
01876               );
01877 }
01878 
01879 int ON__CIndexMaps::RemapFontIndex( int old_font_index ) const
01880 {
01881   return RemapIndexHelper(
01882               old_font_index,
01883               m_bRemapFontIndex,
01884               m_font_count,
01885               m_default_font_index,
01886               m_font_map
01887               );
01888 }
01889 
01890 int ON__CIndexMaps::RemapDimstyleIndex( int old_dimstyle_index ) const
01891 {
01892   return RemapIndexHelper(
01893               old_dimstyle_index,
01894               m_bRemapDimstyleIndex,
01895               m_dimstyle_count,
01896               m_default_dimstyle_index,
01897               m_dimstyle_map
01898               );
01899 }
01900 
01901 int ON__CIndexMaps::RemapHatchPatternIndex( int old_hatch_pattern_index ) const
01902 {
01903   return RemapIndexHelper(
01904               old_hatch_pattern_index,
01905               m_bRemapHatchPatternIndex,
01906               m_hatch_pattern_count,
01907               m_default_hatch_pattern_index,
01908               m_hatch_pattern_map
01909               );
01910 }
01911 
01912 int ON__CIndexMaps::RemapRenderingAttributes( ON_RenderingAttributes& ra )
01913 {
01914   int change_count = 0;
01915   int old_material_index, new_material_index, i;
01916   for ( i = ra.m_materials.Count()-1; i >= 0; i-- )
01917   {
01918     ON_MaterialRef& mr = ra.m_materials[i]; 
01919 
01920     old_material_index = mr.m_material_index;
01921     if ( old_material_index >= 0 )
01922     {
01923       new_material_index = RemapMaterialIndex(old_material_index);
01924       if ( old_material_index != new_material_index )
01925       {
01926         mr.m_material_index = new_material_index;
01927         change_count++;
01928       }
01929     }
01930     else
01931       mr.m_material_index = -1;
01932 
01933     old_material_index = mr.m_material_backface_index;
01934     if ( old_material_index >= 0 )
01935     {
01936       new_material_index = RemapMaterialIndex(old_material_index);
01937       if ( old_material_index != new_material_index )
01938       {
01939         mr.m_material_backface_index = new_material_index;
01940         change_count++;
01941       }
01942     }
01943     else
01944       mr.m_material_backface_index = -1;
01945 
01946     if ( -1 == mr.m_material_index || mr.m_material_index >= m_material_count )
01947     {
01948       if ( ON_nil_uuid == mr.m_material_id )
01949         mr.m_material_index = -1;
01950       else if ( !m_model.m_material_id_index.FindUuid(mr.m_material_id,&mr.m_material_index) )
01951         mr.m_material_index = -1;
01952     }
01953     else if ( m_model.m_material_table[mr.m_material_index].m_material_id != mr.m_material_id )
01954     {
01955       new_material_index = -1;
01956       if (    !ON_UuidIsNil(mr.m_material_id)
01957            && m_model.m_material_id_index.FindUuid(mr.m_material_id,&new_material_index) 
01958            && new_material_index >= 0 
01959            && new_material_index < m_material_count
01960          )
01961       {
01962         mr.m_material_index = new_material_index;
01963       }
01964       else
01965       {
01966         mr.m_material_id = m_model.m_material_table[mr.m_material_index].m_material_id;
01967       }
01968     }
01969 
01970     if ( -1 == mr.m_material_backface_index || mr.m_material_backface_index >= m_material_count )
01971     {
01972       if ( ON_nil_uuid == mr.m_material_backface_id )
01973         mr.m_material_backface_index = -1;
01974       else if ( !m_model.m_material_id_index.FindUuid(mr.m_material_backface_id,&mr.m_material_backface_index) )
01975         mr.m_material_backface_index = -1;
01976     }
01977     else if ( m_model.m_material_table[mr.m_material_backface_index].m_material_id != mr.m_material_backface_id )
01978     {
01979       new_material_index = -1;
01980       if (   !ON_UuidIsNil(mr.m_material_backface_id)
01981            && m_model.m_material_id_index.FindUuid(mr.m_material_backface_id,&new_material_index) 
01982            && new_material_index >= 0 
01983            && new_material_index < m_material_count
01984           )
01985       {
01986         mr.m_material_backface_index = new_material_index;
01987       }
01988       else
01989       {
01990         mr.m_material_backface_id = m_model.m_material_table[mr.m_material_backface_index].m_material_id;
01991       }
01992     }
01993 
01994     if ( mr.m_material_index < 0 && mr.m_material_backface_index < 0 )
01995     {
01996       ra.m_materials.Remove(i);
01997     }
01998   }
01999   return change_count;
02000 }
02001 
02002 int ON__CIndexMaps::RemapLayerAttributes( ON_Layer& layer )
02003 {
02004   int change_count = 0;
02005 
02006   if ( ON_UuidIsNil(layer.m_layer_id) )
02007   {
02008     ON_CreateUuid(layer.m_layer_id);
02009     change_count++;
02010   }
02011 
02012   int old_linetype_index = layer.m_linetype_index;
02013   int new_linetype_index = RemapLinetypeIndex(old_linetype_index);
02014   if ( old_linetype_index != new_linetype_index )
02015   {
02016     layer.m_linetype_index = new_linetype_index;
02017     change_count++;
02018   }
02019 
02020   int old_material_index = layer.m_material_index;
02021   int new_material_index = RemapMaterialIndex(old_material_index);
02022   if ( old_material_index != new_material_index )
02023   {
02024     layer.m_material_index = new_material_index;
02025     change_count++;
02026   }
02027 
02028   change_count += RemapRenderingAttributes(layer.m_rendering_attributes);
02029 
02030   return change_count;
02031 }
02032 
02033 int ON__CIndexMaps::RemapObjectAttributes( ON_3dmObjectAttributes& a )
02034 {
02035   int change_count = 0;
02036 
02037   int i;
02038   if ( ON_UuidIsNil(a.m_uuid) )
02039   {
02040     ON_CreateUuid(a.m_uuid);
02041     change_count++;
02042   }
02043 
02044   int old_layer_index = a.m_layer_index;
02045   int new_layer_index = RemapLayerIndex(old_layer_index);
02046   if ( old_layer_index != new_layer_index )
02047   {
02048     a.m_layer_index = new_layer_index;
02049     change_count++;
02050   }
02051 
02052   int old_linetype_index = a.m_linetype_index;
02053   int new_linetype_index = RemapLinetypeIndex(old_linetype_index);
02054   if ( old_linetype_index != new_linetype_index )
02055   {
02056     a.m_linetype_index = new_linetype_index;
02057     change_count++;
02058   }
02059 
02060   int old_material_index = a.m_material_index;
02061   int new_material_index = RemapMaterialIndex(old_material_index);
02062   if ( old_material_index != new_material_index )
02063   {
02064     a.m_material_index = new_material_index;
02065     change_count++;
02066   }
02067 
02068   if ( a.TopGroup() != -1 )
02069   {
02070     bool bUpdateGroupList = true;
02071     ON_SimpleArray<int> group_list;
02072     a.GetGroupList(group_list);
02073     for ( i = group_list.Count()-1; i >= 0; i-- )
02074     {
02075       int old_group_index = group_list[i];
02076       int new_group_index = RemapGroupIndex(old_group_index);
02077       if ( new_group_index < 0 )
02078       {
02079         group_list.Remove(i);
02080         bUpdateGroupList = true;
02081         change_count++;
02082       }
02083       else if ( old_group_index != new_group_index )
02084       {
02085         group_list[i] = new_group_index;
02086         bUpdateGroupList = true;
02087         change_count++;
02088       }
02089     } 
02090 
02091     if ( bUpdateGroupList || group_list.Count() == 0 )
02092     {
02093       a.RemoveFromAllGroups();
02094       for( i = 0; i < group_list.Count(); i++ )
02095         a.AddToGroup(group_list[i]);
02096     }
02097   }
02098 
02099   change_count += RemapRenderingAttributes(a.m_rendering_attributes);
02100 
02101   return change_count;
02102 }
02103 
02104 void ONX_Model::Polish()
02105 {
02106   DestroyCache();
02107 
02108   // make sure there is a valid revision history
02109   if ( m_properties.m_RevisionHistory.m_revision_count == 0 )
02110     m_properties.m_RevisionHistory.NewRevision();
02111 
02112 
02113   // Get maps sorted so BinarySearch calls in PolishAttributes
02114   // will work.
02115   ON__CIndexMaps imaps(*this);
02116   imaps.RemapModel();
02117 }
02118 
02119 bool ONX_Model::Read( 
02120        const char* filename,
02121        ON_TextLog* error_log
02122        )
02123 {
02124   Destroy(); // get rid of any residual stuff
02125   bool rc = false;
02126   if ( 0 != filename )
02127   {
02128     FILE* fp = ON::OpenFile(filename,"rb");
02129     if ( 0 != fp )
02130     {
02131       ON_BinaryFile file(ON::read3dm,fp);
02132       rc = Read(file,error_log);
02133       ON::CloseFile(fp);
02134     }
02135   }
02136   return rc;
02137 }
02138 
02139 bool ONX_Model::Read( 
02140        const wchar_t* filename,
02141        ON_TextLog* error_log
02142        )
02143 {
02144   Destroy(); // get rid of any residual stuff
02145   bool rc = false;
02146   if ( 0 != filename )
02147   {
02148     FILE* fp = ON::OpenFile(filename,L"rb");
02149     if ( 0 != fp )
02150     {
02151       ON_BinaryFile file(ON::read3dm,fp);
02152       rc = Read(file,error_log);
02153       ON::CloseFile(fp);
02154     }
02155   }
02156   return rc;
02157 }
02158 
02159 bool ONX_Model::Read( 
02160        ON_BinaryArchive& archive,
02161        ON_TextLog* error_log
02162        )
02163 {
02164   const int max_error_count = 2000;
02165   int error_count = 0;
02166   bool return_code = true;
02167   int count, rc;
02168 
02169   Destroy(); // get rid of any residual stuff
02170 
02171   // STEP 1: REQUIRED - Read start section
02172   if ( !archive.Read3dmStartSection( &m_3dm_file_version, m_sStartSectionComments ) )
02173   {
02174     if ( error_log) error_log->Print("ERROR: Unable to read start section. (ON_BinaryArchive::Read3dmStartSection() returned false.)\n");
02175     return false;
02176   }
02177   else if ( CheckForCRCErrors( archive, *this, error_log, "start section" ) )
02178     return_code = false;
02179 
02180   // STEP 2: REQUIRED - Read properties section
02181   if ( !archive.Read3dmProperties( m_properties ) )
02182   {
02183     if ( error_log) error_log->Print("ERROR: Unable to read properties section. (ON_BinaryArchive::Read3dmProperties() returned false.)\n");
02184     return false;
02185   }
02186   else if ( CheckForCRCErrors( archive, *this, error_log, "properties section" ) )
02187     return_code = false;
02188 
02189   // version of opennurbs used to write the file.
02190   m_3dm_opennurbs_version = archive.ArchiveOpenNURBSVersion();
02191 
02192   // STEP 3: REQUIRED - Read properties section
02193   if ( !archive.Read3dmSettings( m_settings ) )
02194   {
02195     if ( error_log) error_log->Print("ERROR: Unable to read settings section. (ON_BinaryArchive::Read3dmSettings() returned false.)\n");
02196     return false;
02197   }
02198   else if ( CheckForCRCErrors( archive, *this, error_log, "settings section" ) )
02199     return_code = false;
02200 
02201   // STEP 4: REQUIRED - Read embedded bitmap table
02202   if ( archive.BeginRead3dmBitmapTable() )
02203   {
02204     // At the moment no bitmaps are embedded so this table is empty
02205     ON_Bitmap* pBitmap = NULL;
02206     for( count = 0; true; count++ ) 
02207     {
02208       pBitmap = NULL;
02209       rc = archive.Read3dmBitmap(&pBitmap);
02210       if ( rc==0 )
02211         break; // end of bitmap table
02212       if ( rc < 0 ) 
02213       {
02214         if ( error_log) 
02215         {
02216           error_log->Print("ERROR: Corrupt bitmap found. (ON_BinaryArchive::Read3dmBitmap() < 0.)\n");
02217           error_count++;
02218           if ( error_count > max_error_count )
02219             return false;
02220           error_log->Print("-- Attempting to continue.\n");
02221         }
02222         return_code = false;
02223       }
02224       m_bitmap_table.Append(pBitmap);
02225     }
02226 
02227     // If BeginRead3dmBitmapTable() returns true, 
02228     // then you MUST call EndRead3dmBitmapTable().
02229     if ( !archive.EndRead3dmBitmapTable() )
02230     {
02231       if ( error_log) error_log->Print("ERROR: Corrupt bitmap table. (ON_BinaryArchive::EndRead3dmBitmapTable() returned false.)\n");
02232       return false;
02233     }
02234     if ( CheckForCRCErrors( archive, *this, error_log, "bitmap table" ) )
02235       return_code = false;
02236   }
02237   else     
02238   {
02239     if ( error_log) 
02240     {
02241       error_log->Print("WARNING: Missing or corrupt bitmap table. (ON_BinaryArchive::BeginRead3dmBitmapTable() returned false.)\n");
02242       error_log->Print("-- Attempting to continue.\n");
02243     }
02244     return_code = false;
02245   }
02246 
02247 
02248 
02249   // STEP 5: REQUIRED - Read texture mapping table
02250   if ( archive.BeginRead3dmTextureMappingTable() )
02251   {
02252     ON_TextureMapping* pTextureMapping = NULL;
02253     for( count = 0; true; count++ ) 
02254     {
02255       rc = archive.Read3dmTextureMapping(&pTextureMapping);
02256       if ( rc==0 )
02257         break; // end of texture_mapping table
02258       if ( rc < 0 ) 
02259       {
02260         if ( error_log) 
02261         {
02262           error_log->Print("ERROR: Corrupt render texture_mapping found. (ON_BinaryArchive::Read3dmTextureMapping() < 0.)\n");
02263           error_count++;
02264           if ( error_count > max_error_count )
02265             return false;
02266           error_log->Print("-- Attempting to continue.\n");
02267         }
02268         continue;
02269       }
02270       ON_UserDataHolder ud;
02271       ud.MoveUserDataFrom(*pTextureMapping);
02272       m_mapping_table.Append(*pTextureMapping);
02273       pTextureMapping->m_mapping_index = count;
02274       ud.MoveUserDataTo(*m_mapping_table.Last(),false);
02275       delete pTextureMapping;
02276       pTextureMapping = NULL;
02277     }
02278     
02279     // If BeginRead3dmTextureMappingTable() returns true, 
02280     // then you MUST call EndRead3dmTextureMappingTable().
02281     if ( !archive.EndRead3dmTextureMappingTable() )
02282     {
02283       if ( error_log) error_log->Print("ERROR: Corrupt render texture_mapping table. (ON_BinaryArchive::EndRead3dmTextureMappingTable() returned false.)\n");
02284       return false;
02285     }
02286     if ( CheckForCRCErrors( archive, *this, error_log, "render texture_mapping table" ) )
02287       return_code = false;
02288   }
02289   else     
02290   {
02291     if ( error_log)
02292     {
02293       error_log->Print("WARNING: Missing or corrupt render texture_mapping table. (ON_BinaryArchive::BeginRead3dmTextureMappingTable() returned false.)\n");
02294       error_log->Print("-- Attempting to continue.\n");
02295     }
02296     return_code = false;
02297   }
02298 
02299 
02300   // STEP 6: REQUIRED - Read render material table
02301   if ( archive.BeginRead3dmMaterialTable() )
02302   {
02303     ON_Material* pMaterial = NULL;
02304     for( count = 0; true; count++ ) 
02305     {
02306       rc = archive.Read3dmMaterial(&pMaterial);
02307       if ( rc==0 )
02308         break; // end of material table
02309       if ( rc < 0 ) 
02310       {
02311         if ( error_log) 
02312         {
02313           error_log->Print("ERROR: Corrupt render material found. (ON_BinaryArchive::Read3dmMaterial() < 0.)\n");
02314           error_count++;
02315           if ( error_count > max_error_count )
02316             return false;
02317           error_log->Print("-- Attempting to continue.\n");
02318         }
02319         pMaterial = new ON_Material; // use default
02320         pMaterial->m_material_index = count;
02321       }
02322       ON_UserDataHolder ud;
02323       ud.MoveUserDataFrom(*pMaterial);
02324       m_material_table.Append(*pMaterial);
02325       ud.MoveUserDataTo(*m_material_table.Last(),false);
02326       delete pMaterial;
02327       pMaterial = NULL;
02328     }
02329     
02330     // If BeginRead3dmMaterialTable() returns true, 
02331     // then you MUST call EndRead3dmMaterialTable().
02332     if ( !archive.EndRead3dmMaterialTable() )
02333     {
02334       if ( error_log) error_log->Print("ERROR: Corrupt render material table. (ON_BinaryArchive::EndRead3dmMaterialTable() returned false.)\n");
02335       return false;
02336     }
02337     if ( CheckForCRCErrors( archive, *this, error_log, "render material table" ) )
02338       return_code = false;
02339   }
02340   else     
02341   {
02342     if ( error_log)
02343     {
02344       error_log->Print("WARNING: Missing or corrupt render material table. (ON_BinaryArchive::BeginRead3dmMaterialTable() returned false.)\n");
02345       error_log->Print("-- Attempting to continue.\n");
02346     }
02347     return_code = false;
02348   }
02349 
02350 
02351   // STEP 7: REQUIRED - Read line type table
02352   if ( archive.BeginRead3dmLinetypeTable() )
02353   {
02354     ON_Linetype* pLinetype = NULL;
02355     for( count = 0; true; count++ ) 
02356     {
02357       rc = archive.Read3dmLinetype(&pLinetype);
02358       if ( rc==0 )
02359         break; // end of linetype table
02360       if ( rc < 0 ) 
02361       {
02362         if ( error_log) 
02363         {
02364           error_log->Print("ERROR: Corrupt render linetype found. (ON_BinaryArchive::Read3dmLinetype() < 0.)\n");
02365           error_count++;
02366           if ( error_count > max_error_count )
02367             return false;
02368           error_log->Print("-- Attempting to continue.\n");
02369         }
02370         pLinetype = new ON_Linetype; // use default
02371         pLinetype->m_linetype_index = count;
02372       }
02373       ON_UserDataHolder ud;
02374       ud.MoveUserDataFrom(*pLinetype);
02375       m_linetype_table.Append(*pLinetype);
02376       ud.MoveUserDataTo(*m_linetype_table.Last(),false);
02377       delete pLinetype;
02378       pLinetype = NULL;
02379     }
02380     
02381     // If BeginRead3dmLinetypeTable() returns true, 
02382     // then you MUST call EndRead3dmLinetypeTable().
02383     if ( !archive.EndRead3dmLinetypeTable() )
02384     {
02385       if ( error_log) error_log->Print("ERROR: Corrupt render linetype table. (ON_BinaryArchive::EndRead3dmLinetypeTable() returned false.)\n");
02386       return false;
02387     }
02388     if ( CheckForCRCErrors( archive, *this, error_log, "render linetype table" ) )
02389       return_code = false;
02390   }
02391   else     
02392   {
02393     if ( error_log)
02394     {
02395       error_log->Print("WARNING: Missing or corrupt render linetype table. (ON_BinaryArchive::BeginRead3dmLinetypeTable() returned false.)\n");
02396       error_log->Print("-- Attempting to continue.\n");
02397     }
02398     return_code = false;
02399   }
02400 
02401   // STEP 8: REQUIRED - Read layer table
02402   if ( archive.BeginRead3dmLayerTable() )
02403   {
02404     ON_Layer* pLayer = NULL;
02405     for( count = 0; true; count++ ) 
02406     {
02407       pLayer = NULL;
02408       rc = archive.Read3dmLayer(&pLayer);
02409       if ( rc==0 )
02410         break; // end of layer table
02411       if ( rc < 0 ) 
02412       {
02413         if ( error_log)
02414         {
02415           error_log->Print("ERROR: Corrupt layer found. (ON_BinaryArchive::Read3dmLayer() < 0.)\n");
02416           error_count++;
02417           if ( error_count > max_error_count )
02418             return false;
02419           error_log->Print("-- Attempting to continue.\n");
02420         }
02421         pLayer = new ON_Layer; // use default
02422         pLayer->m_layer_index = count;
02423       }
02424       ON_UserDataHolder ud;
02425       ud.MoveUserDataFrom(*pLayer);
02426       m_layer_table.Append(*pLayer);
02427       ud.MoveUserDataTo(*m_layer_table.Last(),false);
02428       delete pLayer;
02429       pLayer = NULL;
02430     }
02431     
02432     // If BeginRead3dmLayerTable() returns true, 
02433     // then you MUST call EndRead3dmLayerTable().
02434     if ( !archive.EndRead3dmLayerTable() )
02435     {
02436       if ( error_log) error_log->Print("ERROR: Corrupt render layer table. (ON_BinaryArchive::EndRead3dmLayerTable() returned false.)\n");
02437       return false;
02438     }
02439     if ( CheckForCRCErrors( archive, *this, error_log, "layer table" ) )
02440       return_code = false;
02441   }
02442   else     
02443   {
02444     if ( error_log) 
02445     {
02446       error_log->Print("WARNING: Missing or corrupt layer table. (ON_BinaryArchive::BeginRead3dmLayerTable() returned false.)\n");
02447       error_log->Print("-- Attempting to continue.\n");
02448     }
02449     return_code = false;
02450   }
02451 
02452   // STEP 9: REQUIRED - Read group table
02453   if ( archive.BeginRead3dmGroupTable() )
02454   {
02455     ON_Group* pGroup = NULL;
02456     for( count = 0; true; count++ ) 
02457     {
02458       rc = archive.Read3dmGroup(&pGroup);
02459       if ( rc==0 )
02460         break; // end of group table
02461       if ( rc < 0 ) 
02462       {
02463         if ( error_log)
02464         {
02465           error_log->Print("ERROR: Corrupt group found. (ON_BinaryArchive::Read3dmGroup() < 0.)\n");
02466           error_count++;
02467           if ( error_count > max_error_count )
02468             return false;
02469           error_log->Print("-- Attempting to continue.\n");
02470         }
02471         pGroup = new ON_Group; // use default
02472         pGroup->m_group_index = -1;
02473       }
02474       ON_UserDataHolder ud;
02475       ud.MoveUserDataFrom(*pGroup);
02476       m_group_table.Append(*pGroup);
02477       ud.MoveUserDataTo(*m_group_table.Last(),false);
02478       delete pGroup;
02479       pGroup = NULL;
02480     }
02481     
02482     // If BeginRead3dmGroupTable() returns true, 
02483     // then you MUST call EndRead3dmGroupTable().
02484     if ( !archive.EndRead3dmGroupTable() )
02485     {
02486       if ( error_log) error_log->Print("ERROR: Corrupt group table. (ON_BinaryArchive::EndRead3dmGroupTable() returned false.)\n");
02487       return false;
02488     }
02489     if ( CheckForCRCErrors( archive, *this, error_log, "group table" ) )
02490       return_code = false;
02491   }
02492   else     
02493   {
02494     if ( error_log) 
02495     {
02496       error_log->Print("WARNING: Missing or corrupt group table. (ON_BinaryArchive::BeginRead3dmGroupTable() returned false.)\n");
02497       error_log->Print("-- Attempting to continue.\n");
02498     }
02499     return_code = false;
02500   }
02501 
02502   // STEP 10: REQUIRED - Read font table
02503   if ( archive.BeginRead3dmFontTable() )
02504   {
02505     ON_Font* pFont = NULL;
02506     for( count = 0; true; count++ ) 
02507     {
02508       rc = archive.Read3dmFont(&pFont);
02509       if ( rc==0 )
02510         break; // end of font table
02511       if ( rc < 0 ) 
02512       {
02513         if ( error_log)
02514         {
02515           error_log->Print("ERROR: Corrupt font found. (ON_BinaryArchive::Read3dmFont() < 0.)\n");
02516           error_count++;
02517           if ( error_count > max_error_count )
02518             return false;
02519           error_log->Print("-- Attempting to continue.\n");
02520         }
02521         pFont = new ON_Font; // use default
02522         pFont->m_font_index = -1;
02523       }
02524       ON_UserDataHolder ud;
02525       ud.MoveUserDataFrom(*pFont);
02526       m_font_table.Append(*pFont);
02527       ud.MoveUserDataTo(*m_font_table.Last(),false);
02528       delete pFont;
02529       pFont = NULL;
02530     }
02531     
02532     // If BeginRead3dmFontTable() returns true, 
02533     // then you MUST call EndRead3dmFontTable().
02534     if ( !archive.EndRead3dmFontTable() )
02535     {
02536       if ( error_log) error_log->Print("ERROR: Corrupt font table. (ON_BinaryArchive::EndRead3dmFontTable() returned false.)\n");
02537       return false;
02538     }
02539     if ( CheckForCRCErrors( archive, *this, error_log, "font table" ) )
02540       return_code = false;
02541   }
02542   else     
02543   {
02544     if ( error_log)
02545     {
02546       error_log->Print("WARNING: Missing or corrupt font table. (ON_BinaryArchive::BeginRead3dmFontTable() returned false.)\n");
02547       error_log->Print("-- Attempting to continue.\n");
02548     }
02549     return_code = false;
02550   }
02551 
02552   // STEP 11: REQUIRED - Read dimstyle table
02553   if ( archive.BeginRead3dmDimStyleTable() )
02554   {
02555     ON_DimStyle* pDimStyle = NULL;
02556     for( count = 0; true; count++ ) 
02557     {
02558       rc = archive.Read3dmDimStyle(&pDimStyle);
02559       if ( rc==0 )
02560         break; // end of dimstyle table
02561       if ( rc < 0 ) 
02562       {
02563         if ( error_log)
02564         {
02565           error_log->Print("ERROR: Corrupt dimstyle found. (ON_BinaryArchive::Read3dmDimStyle() < 0.)\n");
02566           error_count++;
02567           if ( error_count > max_error_count )
02568             return false;
02569           error_log->Print("-- Attempting to continue.\n");
02570         }
02571         pDimStyle = new ON_DimStyle; // use default
02572         pDimStyle->m_dimstyle_index = count;
02573       }
02574       ON_UserDataHolder ud;
02575       ud.MoveUserDataFrom(*pDimStyle);
02576       m_dimstyle_table.Append(*pDimStyle);
02577       ud.MoveUserDataTo(*m_dimstyle_table.Last(),false);
02578       delete pDimStyle;
02579       pDimStyle = NULL;
02580     }
02581     
02582     // If BeginRead3dmDimStyleTable() returns true, 
02583     // then you MUST call EndRead3dmDimStyleTable().
02584     if ( !archive.EndRead3dmDimStyleTable() )
02585     {
02586       if ( error_log) error_log->Print("ERROR: Corrupt dimstyle table. (ON_BinaryArchive::EndRead3dmDimStyleTable() returned false.)\n");
02587       return false;
02588     }
02589     if ( CheckForCRCErrors( archive, *this, error_log, "dimstyle table" ) )
02590       return_code = false;
02591   }
02592   else     
02593   {
02594     if ( error_log)
02595     {
02596       error_log->Print("WARNING: Missing or corrupt dimstyle table. (ON_BinaryArchive::BeginRead3dmDimStyleTable() returned false.)\n");
02597       error_log->Print("-- Attempting to continue.\n");
02598     }
02599     return_code = false;
02600   }
02601 
02602   // STEP 12: REQUIRED - Read render lights table
02603   if ( archive.BeginRead3dmLightTable() )
02604   {
02605     ON_Light* pLight = NULL;
02606     ON_3dmObjectAttributes object_attributes;
02607     for( count = 0; true; count++ ) 
02608     {
02609       object_attributes.Default();
02610       rc = archive.Read3dmLight(&pLight,&object_attributes);
02611       if ( rc==0 )
02612         break; // end of light table
02613       if ( rc < 0 ) 
02614       {
02615         if ( error_log)
02616         {
02617           error_log->Print("ERROR: Corrupt render light found. (ON_BinaryArchive::Read3dmLight() < 0.)\n");
02618           error_count++;
02619           if ( error_count > max_error_count )
02620             return false;
02621           error_log->Print("-- Attempting to continue.\n");
02622         }
02623         continue;
02624       }
02625       ONX_Model_RenderLight& light = m_light_table.AppendNew();
02626       ON_UserDataHolder ud;
02627       ud.MoveUserDataFrom(*pLight);
02628       light.m_light = *pLight;
02629       ud.MoveUserDataTo(light.m_light,false);
02630       light.m_attributes = object_attributes;
02631       delete pLight;
02632       pLight = NULL;
02633     }
02634     
02635     // If BeginRead3dmLightTable() returns true, 
02636     // then you MUST call EndRead3dmLightTable().
02637     if ( !archive.EndRead3dmLightTable() )
02638     {
02639       if ( error_log) error_log->Print("ERROR: Corrupt render light table. (ON_BinaryArchive::EndRead3dmLightTable() returned false.)\n");
02640       return false;
02641     }
02642     if ( CheckForCRCErrors( archive, *this, error_log, "render light table" ) )
02643       return_code = false;
02644   }
02645   else     
02646   {
02647     if ( error_log)
02648     {
02649       error_log->Print("WARNING: Missing or corrupt render light table. (ON_BinaryArchive::BeginRead3dmLightTable() returned false.)\n");
02650       error_log->Print("-- Attempting to continue.\n");
02651     }
02652     return_code = false;
02653   }
02654 
02655   // STEP 13 - read hatch pattern table
02656   if ( archive.BeginRead3dmHatchPatternTable() )
02657   {
02658     ON_HatchPattern* pHatchPattern = NULL;
02659     for( count = 0; true; count++ ) 
02660     {
02661       rc = archive.Read3dmHatchPattern(&pHatchPattern);
02662       if ( rc==0 )
02663         break; // end of hatchpattern table
02664       if ( rc < 0 ) 
02665       {
02666         if ( error_log)
02667         {
02668           error_log->Print("ERROR: Corrupt hatchpattern found. (ON_BinaryArchive::Read3dmHatchPattern() < 0.)\n");
02669           error_count++;
02670           if ( error_count > max_error_count )
02671             return false;
02672           error_log->Print("-- Attempting to continue.\n");
02673         }
02674         pHatchPattern = new ON_HatchPattern; // use default
02675         pHatchPattern->m_hatchpattern_index = count;
02676       }
02677       ON_UserDataHolder ud;
02678       ud.MoveUserDataFrom(*pHatchPattern);
02679       m_hatch_pattern_table.Append(*pHatchPattern);
02680       ud.MoveUserDataTo(*m_hatch_pattern_table.Last(),false);
02681       delete pHatchPattern;
02682       pHatchPattern = NULL;
02683     }
02684     
02685     // If BeginRead3dmHatchPatternTable() returns true, 
02686     // then you MUST call EndRead3dmHatchPatternTable().
02687     if ( !archive.EndRead3dmHatchPatternTable() )
02688     {
02689       if ( error_log) error_log->Print("ERROR: Corrupt hatchpattern table. (ON_BinaryArchive::EndRead3dmHatchPatternTable() returned false.)\n");
02690       return false;
02691     }
02692     if ( CheckForCRCErrors( archive, *this, error_log, "hatchpattern table" ) )
02693       return_code = false;
02694   }
02695   else     
02696   {
02697     if ( error_log)
02698     {
02699       error_log->Print("WARNING: Missing or corrupt hatchpattern table. (ON_BinaryArchive::BeginRead3dmHatchPatternTable() returned false.)\n");
02700       error_log->Print("-- Attempting to continue.\n");
02701     }
02702     return_code = false;
02703   }
02704 
02705   // STEP 14: REQUIRED - Read instance definition table
02706   if ( archive.BeginRead3dmInstanceDefinitionTable() )
02707   {
02708     ON_InstanceDefinition* pIDef = NULL;
02709     for( count = 0; true; count++ ) 
02710     {
02711       rc = archive.Read3dmInstanceDefinition(&pIDef);
02712       if ( rc==0 )
02713         break; // end of instance definition table
02714       if ( rc < 0 ) 
02715       {
02716         if ( error_log)
02717         {
02718           error_log->Print("ERROR: Corrupt instance definition found. (ON_BinaryArchive::Read3dmInstanceDefinition() < 0.)\n");
02719           error_count++;
02720           if ( error_count > max_error_count )
02721             return false;
02722           error_log->Print("-- Attempting to continue.\n");
02723         }
02724         continue;
02725       }
02726       ON_UserDataHolder ud;
02727       ud.MoveUserDataFrom(*pIDef);
02728       m_idef_table.Append(*pIDef);
02729       ud.MoveUserDataTo(*m_idef_table.Last(),false);
02730       delete pIDef;
02731     }
02732     
02733     // If BeginRead3dmInstanceDefinitionTable() returns true, 
02734     // then you MUST call EndRead3dmInstanceDefinitionTable().
02735     if ( !archive.EndRead3dmInstanceDefinitionTable() )
02736     {
02737       if ( error_log) error_log->Print("ERROR: Corrupt instance definition table. (ON_BinaryArchive::EndRead3dmInstanceDefinitionTable() returned false.)\n");
02738       return false;
02739     }
02740     if ( CheckForCRCErrors( archive, *this, error_log, "instance definition table" ) )
02741       return_code = false;
02742   }
02743   else     
02744   {
02745     if ( error_log)
02746     {
02747       error_log->Print("WARNING: Missing or corrupt instance definition table. (ON_BinaryArchive::BeginRead3dmInstanceDefinitionTable() returned false.)\n");
02748       error_log->Print("-- Attempting to continue.\n");
02749     }
02750     return_code = false;
02751   }
02752 
02753 
02754 
02755   // STEP 15: REQUIRED - Read object (geometry and annotation) table
02756   if ( archive.BeginRead3dmObjectTable() )
02757   {
02758     // optional filter made by setting ON::object_type bits 
02759     // For example, if you just wanted to just read points and meshes, you would use
02760     // object_filter = ON::point_object | ON::mesh_object;
02761     int object_filter = 0; 
02762 
02763     for( count = 0; true; count++ ) 
02764     {
02765       ON_Object* pObject = NULL;
02766       ON_3dmObjectAttributes attributes;
02767       rc = archive.Read3dmObject(&pObject,&attributes,object_filter);
02768       if ( rc == 0 )
02769         break; // end of object table
02770       if ( rc < 0 ) 
02771       {
02772         if ( error_log)
02773         {
02774           error_log->Print("ERROR: Object table entry %d is corrupt. (ON_BinaryArchive::Read3dmObject() < 0.)\n",count);
02775           error_count++;
02776           if ( error_count > max_error_count )
02777             return false;
02778           error_log->Print("-- Attempting to continue.\n");
02779         }
02780         continue;
02781       }
02782       if ( m_crc_error_count != archive.BadCRCCount() ) 
02783       {
02784         if ( error_log)
02785         {
02786           error_log->Print("ERROR: Object table entry %d is corrupt. (CRC errors).\n",count);
02787           error_log->Print("-- Attempting to continue.\n");
02788         }
02789         m_crc_error_count = archive.BadCRCCount();
02790       }
02791       if ( pObject ) 
02792       {
02793         ONX_Model_Object& mo = m_object_table.AppendNew();
02794         mo.m_object = pObject;
02795         mo.m_bDeleteObject = true;
02796         mo.m_attributes = attributes;
02797       }
02798       else
02799       {
02800         if ( error_log)
02801         {
02802           if ( rc == 2 )
02803             error_log->Print("WARNING: Skipping object table entry %d because it's filtered.\n",count);
02804           else if ( rc == 3 )
02805             error_log->Print("WARNING: Skipping object table entry %d because it's newer than this code.  Update your OpenNURBS toolkit.\n",count);
02806           else
02807             error_log->Print("WARNING: Skipping object table entry %d for unknown reason.\n",count);
02808         }
02809       }
02810     }
02811     
02812     // If BeginRead3dmObjectTable() returns true, 
02813     // then you MUST call EndRead3dmObjectTable().
02814     if ( !archive.EndRead3dmObjectTable() )
02815     {
02816       if ( error_log) error_log->Print("ERROR: Corrupt object light table. (ON_BinaryArchive::EndRead3dmObjectTable() returned false.)\n");
02817       return false;
02818     }
02819     if ( CheckForCRCErrors( archive, *this, error_log, "object table" ) )
02820       return_code = false;
02821   }
02822   else     
02823   {
02824     if ( error_log)
02825     {
02826       error_log->Print("WARNING: Missing or corrupt object table. (ON_BinaryArchive::BeginRead3dmObjectTable() returned false.)\n");
02827       error_log->Print("-- Attempting to continue.\n");
02828     }
02829     return_code = false;
02830   }
02831 
02832   // STEP 16: Read history table
02833   if ( archive.BeginRead3dmHistoryRecordTable() )
02834   {
02835     for( count = 0; true; count++ ) 
02836     {
02837       ON_HistoryRecord* pHistoryRecord = NULL;
02838       rc = archive.Read3dmHistoryRecord(pHistoryRecord);
02839       if ( rc == 0 )
02840         break; // end of history record table
02841       if ( rc < 0 ) 
02842       {
02843         if ( error_log)
02844         {
02845           error_log->Print("ERROR: History record table entry %d is corrupt. (ON_BinaryArchive::Read3dmHistoryRecord() < 0.)\n",count);
02846           error_count++;
02847           if ( error_count > max_error_count )
02848             return false;
02849           error_log->Print("-- Attempting to continue.\n");
02850         }
02851         continue;
02852       }
02853       if ( m_crc_error_count != archive.BadCRCCount() ) 
02854       {
02855         if ( error_log)
02856         {
02857           error_log->Print("ERROR: History record table entry %d is corrupt. (CRC errors).\n",count);
02858           error_log->Print("-- Attempting to continue.\n");
02859         }
02860         m_crc_error_count = archive.BadCRCCount();
02861       }
02862       if ( pHistoryRecord ) 
02863       {
02864         m_history_record_table.Append(pHistoryRecord);
02865       }
02866       else
02867       {
02868         if ( error_log)
02869         {
02870           error_log->Print("WARNING: Skipping history record table entry %d for unknown reason.\n",count);
02871         }
02872       }
02873     }
02874     
02875     // If BeginRead3dmHistoryRecordTable() returns true, 
02876     // then you MUST call EndRead3dmHistoryRecordTable().
02877     if ( !archive.EndRead3dmHistoryRecordTable() )
02878     {
02879       if ( error_log) error_log->Print("ERROR: Corrupt object light table. (ON_BinaryArchive::EndRead3dmObjectTable() returned false.)\n");
02880       return false;
02881     }
02882     if ( CheckForCRCErrors( archive, *this, error_log, "history record table" ) )
02883       return_code = false;
02884   }
02885   else     
02886   {
02887     if ( error_log)
02888     {
02889       error_log->Print("WARNING: Missing or corrupt history record table. (ON_BinaryArchive::BeginRead3dmHistoryRecordTable() returned false.)\n");
02890       error_log->Print("-- Attempting to continue.\n");
02891     }
02892     return_code = false;
02893   }
02894 
02895   // STEP 17: OPTIONAL - Read user tables as anonymous goo
02896   // If you develop a plug-ins or application that uses OpenNURBS files,
02897   // you can store anything you want in a user table.
02898   for(count=0;true;count++)
02899   {
02900     if ( archive.Archive3dmVersion() <= 1 )
02901     {
02902       // no user tables in version 1 archives.
02903       break;
02904     }
02905 
02906     {
02907       ON__UINT32 tcode = 0;
02908       ON__INT64 big_value = 0;
02909       if ( !archive.PeekAt3dmBigChunkType(&tcode,&big_value) )
02910         break;
02911       if ( TCODE_USER_TABLE != tcode )
02912         break;
02913     }
02914     ON_UUID plugin_id = ON_nil_uuid;
02915     bool bGoo = false;
02916     int usertable_3dm_version = 0;
02917     int usertable_opennurbs_version = 0;
02918     if ( !archive.BeginRead3dmUserTable( plugin_id, &bGoo, &usertable_3dm_version, &usertable_opennurbs_version ) )
02919     {
02920       // attempt to skip bogus user table
02921       const ON__UINT64 pos0 = archive.CurrentPosition();
02922       ON__UINT32 tcode = 0;
02923       ON__INT64 big_value = 0;
02924       if  ( !archive.BeginRead3dmBigChunk(&tcode,&big_value) )
02925         break;
02926       if ( !archive.EndRead3dmChunk() )
02927         break;
02928       const ON__UINT64 pos1 = archive.CurrentPosition();
02929       if (pos1 <= pos0)
02930         break;
02931       if ( TCODE_USER_TABLE != tcode )
02932         break;
02933 
02934       continue; // skip this bogus user table
02935     }
02936 
02937     ONX_Model_UserData& ud = m_userdata_table.AppendNew();
02938     ud.m_uuid = plugin_id;
02939     ud.m_usertable_3dm_version = usertable_3dm_version;
02940     ud.m_usertable_opennurbs_version = usertable_opennurbs_version;
02941 
02942     if ( !archive.Read3dmAnonymousUserTable( usertable_3dm_version, usertable_opennurbs_version, ud.m_goo ) )
02943     {
02944       if ( error_log) error_log->Print("ERROR: User data table entry %d is corrupt. (ON_BinaryArchive::Read3dmAnonymousUserTable() is false.)\n",count);
02945       break;
02946     }
02947 
02948     // If BeginRead3dmObjectTable() returns true, 
02949     // then you MUST call EndRead3dmUserTable().
02950     if ( !archive.EndRead3dmUserTable() )
02951     {
02952       if ( error_log) error_log->Print("ERROR: Corrupt user data table. (ON_BinaryArchive::EndRead3dmUserTable() returned false.)\n");
02953       break;
02954     }
02955   }
02956 
02957   // STEP 18: OPTIONAL - check for end mark
02958   if ( !archive.Read3dmEndMark(&m_file_length) )
02959   {
02960     if ( archive.Archive3dmVersion() != 1 ) 
02961     {
02962       // some v1 files are missing end-of-archive markers
02963       if ( error_log) error_log->Print("ERROR: ON_BinaryArchive::Read3dmEndMark(&m_file_length) returned false.\n");
02964     }
02965   }
02966 
02967   // Remap layer, material, linetype, font, dimstyle, hatch pattern, etc., 
02968   // indices so the correspond to the model's table array index.
02969   //
02970   // Polish also sets revision history information if it is missing.
02971   // In this case, that is not appropriate so the value of
02972   // m_properties.m_RevisionHistory is saved before calling Polish()
02973   // and restored afterwards.
02974   const ON_3dmRevisionHistory saved_revision_history(m_properties.m_RevisionHistory);
02975   Polish();
02976   m_properties.m_RevisionHistory = saved_revision_history;
02977 
02978   return return_code;
02979 }
02980 
02981 static 
02982 void ONX_Model_WriteHelper(ON_BinaryFile& file)
02983 {
02984   file.EnableSave3dmRenderMeshes(true);
02985   file.EnableSave3dmAnalysisMeshes(true);
02986   file.EnableSaveUserData(true);
02987 }
02988 
02989 bool ONX_Model::Write( 
02990        const char* filename,
02991        int version,
02992        const char* sStartSectionComment,
02993        ON_TextLog* error_log
02994        )
02995 {
02996   bool rc = false;
02997   if ( 0 != filename )
02998   {
02999     FILE* fp = ON::OpenFile( filename, "wb" );
03000     if ( 0 != fp )
03001     {
03002       ON_BinaryFile file( ON::write3dm, fp );
03003       ONX_Model_WriteHelper(file);
03004       rc = Write( file, version, sStartSectionComment, error_log );
03005       ON::CloseFile(fp);
03006     }
03007   }
03008   return rc;
03009 }
03010 
03011 bool ONX_Model::Write( 
03012        const wchar_t* filename,
03013        int version,
03014        const char* sStartSectionComment,
03015        ON_TextLog* error_log
03016        )
03017 {
03018   bool rc = false;
03019   if ( 0 != filename )
03020   {
03021     FILE* fp = ON::OpenFile( filename, L"wb" );
03022     if ( 0 != fp )
03023     {
03024       ON_BinaryFile file( ON::write3dm, fp );
03025       ONX_Model_WriteHelper(file);
03026       rc = Write( file, version, sStartSectionComment, error_log );
03027       ON::CloseFile(fp);
03028     }
03029   }
03030   return rc;
03031 }
03032 
03033 
03034 bool ONX_Model::Write( 
03035        ON_BinaryArchive& archive,
03036        int version,
03037        const char* sStartSectionComment,
03038        ON_TextLog* error_log
03039        )
03040 {
03041   int i;
03042 
03043   if ( !IsValid(error_log) )
03044   {
03045     // This model is not valid.  See the error_log for details.
03046     if ( error_log) error_log->Print("ONX_Model::Write Your model is not valid and will not be saved.\n");
03047     return false;
03048   }
03049 
03050   if ( 0 != version )
03051   {
03052     if (    version < 2 
03053          || version > ON_BinaryArchive::CurrentArchiveVersion() 
03054          || (version >= 50 && 0 != (version%10))
03055          || (version < 50 && version > ON_BinaryArchive::CurrentArchiveVersion()/10)
03056          )
03057     {
03058       // version must be 0, 2, 3, 4, 5 or 50
03059       version = 0;
03060       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",
03061                       version,ON_BinaryArchive::CurrentArchiveVersion()/10,ON_BinaryArchive::CurrentArchiveVersion());
03062     }
03063   }
03064 
03065   if ( !archive.WriteMode() )
03066   {
03067     // You passed in a bogus archive.  You must pass ON::write3dm to the
03068     // archive constructor.
03069     if ( error_log) error_log->Print("ONX_Model::Write archive.Mode() is not ON::write3dm.\n"
03070                     "See ONX_Model::Write example in the header file.\n");
03071     return false;
03072   }
03073 
03074   bool ok;
03075 
03076   // START SECTION
03077   ok = archive.Write3dmStartSection( version, m_sStartSectionComments );
03078   if ( !ok )
03079   {
03080     // make sure your archive was created with ON::write3dm mode.
03081     if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmStartSection() failed.\n"
03082                     "Your archive is not properly initialized\n"
03083                     "(make sure you passed ON::write3dm to the constructor),\n"
03084                     "a file is locked, a disk is locked, or something along those lines.\n");
03085     return false;
03086   }
03087 
03088   // PROPERTIES SECTION
03089   ok = archive.Write3dmProperties( m_properties );
03090   if ( !ok )
03091   {
03092     // make sure m_properties is valid
03093     if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmProperties() failed.\n"
03094                     "Your m_properties information is not valid or basic file writing failed.\n"
03095                     );
03096     return false;
03097   }
03098 
03099   // SETTINGS SECTION
03100   ok = archive.Write3dmSettings( m_settings );
03101   if ( !ok )
03102   {
03103     // make sure m_settings is valid
03104     if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmSettings() failed.\n"
03105                     "Your m_settings information is not valid or basic file writing failed.\n");
03106     return false;
03107   }
03108 
03109   // BITMAP TABLE
03110   ok = archive.BeginWrite3dmBitmapTable();
03111   if ( !ok )
03112   {
03113     if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmBitmapTable() failed.\n");
03114     return false;
03115   }
03116   for( i = 0; ok && i < m_bitmap_table.Count(); i++ )
03117   {
03118     ok = archive.Write3dmBitmap(*m_bitmap_table[i]);
03119     if ( !ok )
03120     {
03121       if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmBitmap(m_bitmap_table[%d]) failed.\n",i);
03122     }
03123   }
03124   if ( !archive.EndWrite3dmBitmapTable() )
03125   {
03126     if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmBitmapTable() failed.\n");
03127     return false;
03128   }
03129   if (!ok)
03130     return false;
03131 
03132   // RENDER TEXTURE MAPPING TABLE
03133   if ( archive.Archive3dmVersion() >= 4 )
03134   {
03135     ok = archive.BeginWrite3dmTextureMappingTable();
03136     if ( !ok )
03137     {
03138       if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmTextureMappingTable() failed.\n");
03139       return false;
03140     }
03141     for( i = 0; ok && i < m_mapping_table.Count(); i++ )
03142     {
03143       ok = archive.Write3dmTextureMapping(m_mapping_table[i]);
03144       if ( !ok )
03145       {
03146         if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmTextureMapping(m_mapping_table[%d]) failed.\n",i);
03147       }
03148     }
03149     if ( !archive.EndWrite3dmTextureMappingTable() )
03150     {
03151       if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmTextureMappingTable() failed.\n");
03152       return false;
03153     }
03154     if (!ok)
03155       return false;
03156   }
03157 
03158   // RENDER MATERIAL TABLE
03159   ok = archive.BeginWrite3dmMaterialTable();
03160   if ( !ok )
03161   {
03162     if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmMaterialTable() failed.\n");
03163     return false;
03164   }
03165   for( i = 0; ok && i < m_material_table.Count(); i++ )
03166   {
03167     ok = archive.Write3dmMaterial(m_material_table[i]);
03168     if ( !ok )
03169     {
03170       if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmMaterial(m_material_table[%d]) failed.\n",i);
03171     }
03172   }
03173   if ( !archive.EndWrite3dmMaterialTable() )
03174   {
03175     if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmMaterialTable() failed.\n");
03176     return false;
03177   }
03178   if (!ok)
03179     return false;
03180 
03181 
03182   // LINETYPE TABLE
03183   if ( archive.Archive3dmVersion() >= 4 )
03184   {
03185     ok = archive.BeginWrite3dmLinetypeTable();
03186     if ( !ok )
03187     {
03188       if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmLinetypeTable() failed.\n");
03189       return false;
03190     }
03191     for( i = 0; ok && i < m_linetype_table.Count(); i++ )
03192     {
03193       ok = archive.Write3dmLinetype(m_linetype_table[i]);
03194       if ( !ok )
03195       {
03196         if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmLinetype(m_linetype_table[%d]) failed.\n",i);
03197       }
03198     }
03199     if ( !archive.EndWrite3dmLinetypeTable() )
03200     {
03201       if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmLinetypeTable() failed.\n");
03202       return false;
03203     }
03204     if (!ok)
03205       return false;
03206   }
03207 
03208   // LAYER TABLE
03209   ok = archive.BeginWrite3dmLayerTable();
03210   if ( !ok )
03211   {
03212     // make sure m_settings is valid
03213     if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmLayerTable() failed.\n");
03214     return false;
03215   }
03216   for( i = 0; ok && i < m_layer_table.Count(); i++ )
03217   {
03218     ok = archive.Write3dmLayer(m_layer_table[i]);
03219     if ( !ok )
03220     {
03221       if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmLayer(m_layer_table[%d]) failed.\n",i);
03222     }
03223   }
03224   if ( !archive.EndWrite3dmLayerTable() )
03225   {
03226     if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmLayerTable() failed.\n");
03227     return false;
03228   }
03229   if (!ok)
03230     return false;
03231 
03232   // GROUP TABLE
03233   ok = archive.BeginWrite3dmGroupTable();
03234   if ( !ok )
03235   {
03236     // make sure m_settings is valid
03237     if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmGroupTable() failed.\n");
03238     return false;
03239   }
03240   for( i = 0; ok && i < m_group_table.Count(); i++ )
03241   {
03242     ok = archive.Write3dmGroup(m_group_table[i]);
03243     if ( !ok )
03244     {
03245       if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmGroup(m_group_table[%d]) failed.\n",i);
03246     }
03247   }
03248   if ( !archive.EndWrite3dmGroupTable() )
03249   {
03250     if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmGroupTable() failed.\n");
03251     return false;
03252   }
03253   if (!ok)
03254     return false;
03255 
03256 
03257   // FONT TABLE
03258   if ( archive.Archive3dmVersion() >= 3 )
03259   {
03260     ok = archive.BeginWrite3dmFontTable();
03261     if ( !ok )
03262     {
03263       // make sure m_settings is valid
03264       if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmFontTable() failed.\n");
03265       return false;
03266     }
03267     for( i = 0; ok && i < m_font_table.Count(); i++ )
03268     {
03269       ok = archive.Write3dmFont(m_font_table[i]);
03270       if ( !ok )
03271       {
03272         if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmFont(m_font_table[%d]) failed.\n",i);
03273       }
03274     }
03275     if ( !archive.EndWrite3dmFontTable() )
03276     {
03277       if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmFontTable() failed.\n");
03278       return false;
03279     }
03280     if (!ok)
03281       return false;
03282   }
03283 
03284 
03285   // DIMSTYLE TABLE
03286   if ( archive.Archive3dmVersion() >= 3 )
03287   {
03288     ok = archive.BeginWrite3dmDimStyleTable();
03289     if ( !ok )
03290     {
03291       // make sure m_settings is valid
03292       if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmDimStyleTable() failed.\n");
03293       return false;
03294     }
03295     for( i = 0; ok && i < m_dimstyle_table.Count(); i++ )
03296     {
03297       ok = archive.Write3dmDimStyle(m_dimstyle_table[i]);
03298       if ( !ok )
03299       {
03300         if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmDimStyle(m_dimstyle_table[%d]) failed.\n",i);
03301       }
03302     }
03303     if ( !archive.EndWrite3dmDimStyleTable() )
03304     {
03305       if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmDimStyleTable() failed.\n");
03306       return false;
03307     }
03308     if (!ok)
03309       return false;
03310   }
03311 
03312 
03313   // LIGHT TABLE
03314   ok = archive.BeginWrite3dmLightTable();
03315   if ( !ok )
03316   {
03317     // make sure m_settings is valid
03318     if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmLightTable() failed.\n");
03319     return false;
03320   }
03321   for( i = 0; ok && i < m_light_table.Count(); i++ )
03322   {
03323     ok = archive.Write3dmLight(m_light_table[i].m_light,&m_light_table[i].m_attributes);
03324     if ( !ok )
03325     {
03326       if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmLight(m_light_table[%d]) failed.\n",i);
03327     }
03328   }
03329   if ( !archive.EndWrite3dmLightTable() )
03330   {
03331     if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmLightTable() failed.\n");
03332     return false;
03333   }
03334   if (!ok)
03335     return false;
03336 
03337 
03338   // HATCH PATTERN TABLE
03339   if ( archive.Archive3dmVersion() >= 4 )
03340   {
03341     ok = archive.BeginWrite3dmHatchPatternTable();
03342     if ( !ok )
03343     {
03344       if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmHatchPatternTable() failed.\n");
03345       return false;
03346     }
03347     for( i = 0; ok && i < m_hatch_pattern_table.Count(); i++ )
03348     {
03349       ok = archive.Write3dmHatchPattern(m_hatch_pattern_table[i]);
03350       if ( !ok )
03351       {
03352         if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmHatchPattern(m_hatch_pattern_table[%d]) failed.\n",i);
03353       }
03354     }
03355     if ( !archive.EndWrite3dmHatchPatternTable() )
03356     {
03357       if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmHatchPatternTable() failed.\n");
03358       return false;
03359     }
03360     if (!ok)
03361       return false;
03362   }
03363 
03364 
03365   // INSTANCE DEFINITION TABLE
03366   if ( archive.Archive3dmVersion() >= 3 )
03367   {
03368     ok = archive.BeginWrite3dmInstanceDefinitionTable();
03369     if ( !ok )
03370     {
03371       // make sure m_settings is valid
03372       if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmInstanceDefinitionTable() failed.\n");
03373       return false;
03374     }
03375     for( i = 0; ok && i < m_idef_table.Count(); i++ )
03376     {
03377       ok = archive.Write3dmInstanceDefinition(m_idef_table[i]);
03378       if ( !ok )
03379       {
03380         if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmInstanceDefinition(m_IDef_table[%d]) failed.\n",i);
03381       }
03382     }
03383     if ( !archive.EndWrite3dmInstanceDefinitionTable() )
03384     {
03385       if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmInstanceDefinitionTable() failed.\n");
03386       return false;
03387     }
03388     if (!ok)
03389       return false;
03390   }
03391 
03392 
03393   // OBJECT TABLE
03394   ok = archive.BeginWrite3dmObjectTable();
03395   if ( !ok )
03396   {
03397     if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmObjectTable() failed.\n");
03398     return false;
03399   }
03400   for( i = 0; ok && i < m_object_table.Count(); i++ )
03401   {
03402     if ( 0 != m_object_table[i].m_object )
03403     {
03404       ok = archive.Write3dmObject(*m_object_table[i].m_object,&m_object_table[i].m_attributes);
03405       if ( !ok )
03406       {
03407         if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmObject(m_IDef_table[%d]) failed.\n",i);
03408       }
03409     }
03410   }
03411   if ( !archive.EndWrite3dmObjectTable() )
03412   {
03413     if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmObjectTable() failed.\n");
03414     return false;
03415   }
03416   if (!ok)
03417     return false;
03418 
03419 
03420   // HISTORY RECORD TABLE
03421   if ( archive.Archive3dmVersion() >= 4 )
03422   {
03423     ok = archive.BeginWrite3dmHistoryRecordTable();
03424     if ( !ok )
03425     {
03426       if ( error_log) error_log->Print("ONX_Model::Write archive.BeginWrite3dmHistoryRecordTable() failed.\n");
03427       return false;
03428     }
03429     for ( i = 0; ok && i < m_history_record_table.Count(); i++ )
03430     {
03431       const ON_HistoryRecord* history_record = m_history_record_table[i];
03432       if( history_record)
03433         ok = archive.Write3dmHistoryRecord( *history_record );
03434     }
03435     if( !archive.EndWrite3dmHistoryRecordTable() )
03436     {
03437       if ( error_log) error_log->Print("ONX_Model::Write archive.EndWrite3dmHistoryTable() failed.\n");
03438       return false;
03439     }
03440     if (!ok)
03441       return false;
03442   }
03443 
03444   // USER DATA TABLE
03445   for( i = 0; ok && i < m_userdata_table.Count(); i++ )
03446   {
03447     const ONX_Model_UserData& ud = m_userdata_table[i];
03448     if ( ON_UuidIsNotNil(ud.m_uuid) )
03449     {
03450       if ( !archive.Write3dmAnonymousUserTableRecord(
03451         ud.m_uuid,
03452         ud.m_usertable_3dm_version,
03453         ud.m_usertable_opennurbs_version,ud.m_goo
03454         ) )
03455       {
03456         continue;
03457       }
03458     }
03459   }
03460 
03461   if ( !archive.Write3dmEndMark() )
03462   {
03463     ok = false;
03464     if ( error_log) error_log->Print("ONX_Model::Write archive.Write3dmEndMark() failed.\n");
03465   }
03466 
03467   return ok;
03468 }
03469 
03470 bool ONX_Model::IsValid( ON_TextLog* text_log ) const
03471 {
03472   // Audit with no repairs will simply complain if it
03473   // finds something wrong;
03474   int i = const_cast<ONX_Model*>(this)->Audit(false,NULL,text_log,NULL);
03475   return (i>=0);
03476 }
03477 
03478 int ONX_Model::ObjectIndex( ON_UUID object_uuid ) const
03479 {
03480   // In a high quality app, you may want to override this
03481   // and do something a little smarter.
03482 
03483   int object_index = -1;
03484   if ( ON_UuidIsNotNil(object_uuid) )
03485   {
03486     int i, object_count = m_object_table.Count();
03487     if ( object_count > 0 )
03488     {
03489       if ( object_count != m_object_id_index.Count() )
03490       {
03491         // rebuild m__object_uuid_index[]
03492         ON_UuidIndexList* p = const_cast< ON_UuidIndexList* >(&m_object_id_index);
03493         p->Empty();
03494         p->Reserve(object_count);
03495         for ( i = 0; i < object_count; i++ )
03496         {
03497           ON_UUID id = m_object_table[i].m_attributes.m_uuid;
03498           if ( ON_UuidIsNil(id) )
03499           {
03500             ON_ERROR("Nil object ids in model");
03501             ON_CreateUuid(id);
03502             *(const_cast<ON_UUID*>(&m_object_table[i].m_attributes.m_uuid)) = id;
03503           }
03504           if ( !p->AddUuidIndex(id,i,true) )
03505           {
03506             ON_ERROR("Duplicate object ids in model");
03507             ON_CreateUuid(id);
03508             *(const_cast<ON_UUID*>(&m_object_table[i].m_attributes.m_uuid)) = id;
03509             p->AddUuidIndex(id,i,false);
03510           }
03511         }
03512       }
03513 
03514       if ( !m_object_id_index.FindUuid(object_uuid,&object_index) )
03515         object_index = -1;
03516     }
03517   }
03518 
03519   return object_index;
03520 }
03521 
03522 int ONX_Model::IDefIndex( ON_UUID idef_uuid ) const
03523 {
03524   // In a high quality app, you may want to override this
03525   // and do something a little smarter.
03526 
03527   int idef_index = -1;
03528   if ( ON_UuidIsNotNil(idef_uuid) )
03529   {
03530     int i, idef_count = m_idef_table.Count();
03531     if ( idef_count > 0 )
03532     {
03533       if ( idef_count != m_idef_id_index.Count() )      
03534       {
03535         // rebuild m__idef_uuid_index[]
03536         ON_UuidIndexList* p = const_cast<ON_UuidIndexList*>(&m_idef_id_index);
03537         p->Empty();
03538         p->Reserve(idef_count);
03539         for ( i = 0; i < idef_count; i++ )
03540         {
03541           ON_UUID id = m_idef_table[i].m_uuid;
03542           if ( ON_UuidIsNil(id) )
03543           {
03544             ON_ERROR("Nil idef ids in model");
03545             ON_CreateUuid(id);
03546             (const_cast<ON_InstanceDefinition*>(&m_idef_table[i]))->m_uuid = id;
03547           }
03548           if ( !p->AddUuidIndex(id,i,true) )
03549           {
03550             ON_ERROR("Duplicate idef ids in model");
03551             ON_CreateUuid(id);
03552             (const_cast<ON_InstanceDefinition*>(&m_idef_table[i]))->m_uuid = id;
03553             p->AddUuidIndex(id,i,false);
03554           }
03555         }
03556       }
03557 
03558       if ( !m_idef_id_index.FindUuid(idef_uuid,&idef_index) )
03559         idef_index = -1;
03560     }
03561   }
03562 
03563   return idef_index;
03564 }
03565 
03566 int ONX_Model::IDefIndex( const wchar_t* idef_name ) const
03567 {
03568   // slow and stupid search - what do you expect for free?
03569   // In a high quality app, you will want to override this
03570   // and do something a little smarter.
03571   //
03572   int idef_index = -1;
03573   if ( 0 != idef_name && 0 != idef_name[0]  )
03574   {
03575     int i, idef_count = m_idef_table.Count();
03576     for ( i = 0; i < idef_count; i++ )
03577     {
03578       if ( 0 == on_wcsicmp(idef_name, m_idef_table[i].Name() ) )
03579       {
03580         idef_index = i;
03581         break;
03582       }
03583     }
03584   }
03585   return idef_index;
03586 }
03587 
03588 void ONX_Model::GetUnusedIDefName( ON_wString& idef_name ) const
03589 {
03590   int i = 1;
03591   for(i = 1; i < 100000; i++ )
03592   {
03593     idef_name.Format("IDef_%02d",i);
03594     if ( IDefIndex(idef_name) < 0 )
03595       return;
03596   }
03597   idef_name = "IDef";
03598   return;
03599 }
03600 
03601 int ONX_Model::UsesIDef(
03602         const ON_InstanceRef& iref,
03603         ON_UUID idef_uuid
03604         ) const
03605 {
03606   // get id of idef we are looking for
03607   if ( ON_UuidIsNil(idef_uuid) )
03608     return 0;
03609 
03610   // id of idef that defines iref
03611   ON_UUID iref_idef_uuid = iref.m_instance_definition_uuid;
03612   if ( 0 == ON_UuidCompare( idef_uuid, iref_idef_uuid ) )
03613     return 1;
03614 
03615   const int iref_idef_index = IDefIndex(iref_idef_uuid);
03616   if ( -1 == iref_idef_index )
03617     return -1;
03618   const ON_InstanceDefinition& iref_idef = m_idef_table[iref_idef_index];
03619 
03620 
03621   int i0 = 0;
03622   int i1 = 0;
03623   int i, j, k, depth, obj_index;
03624   const ON_InstanceRef* pNestedIRef;
03625 
03626   // set iref_list[] = list of all nested instance references in iref_idef.
03627   ON_SimpleArray<const ON_InstanceRef*> iref_list(256);
03628   for ( j = 0; j < iref_idef.m_object_uuid.Count(); j++ )
03629   {
03630     obj_index = ObjectIndex(iref_idef.m_object_uuid[j]);
03631     if ( obj_index < 0 )
03632       continue;
03633     const ONX_Model_Object& obj = m_object_table[obj_index];
03634     if ( 0 == obj.m_object )
03635       continue;
03636     if ( obj.m_object->ObjectType() == ON::instance_reference )
03637     {
03638       pNestedIRef = ON_InstanceRef::Cast(obj.m_object);
03639       if ( 0 != pNestedIRef )
03640       {
03641         if ( 0 == ON_UuidCompare( idef_uuid, pNestedIRef->m_instance_definition_uuid ) )
03642           return 2;
03643         iref_list.Append(pNestedIRef);
03644       }
03645     }
03646   }
03647 
03648   // test the nested instance references to see if they use idef_index.
03649   const int max_depth = 1000;
03650   for ( depth=3; depth < max_depth && i1 < iref_list.Count(); depth++ )
03651   {
03652     i0 = i1;
03653     i1 = iref_list.Count();
03654     for ( i = i0; i < i1; i++ )
03655     {
03656       pNestedIRef = iref_list[i];
03657       if ( 0 == pNestedIRef )
03658         continue;
03659       k = IDefIndex( pNestedIRef->m_instance_definition_uuid );
03660       if ( k < 0 )
03661         continue;
03662       const ON_InstanceDefinition& nested_idef = m_idef_table[k];
03663       for ( j = 0; j < nested_idef.m_object_uuid.Count(); j++ )
03664       {
03665         obj_index = ObjectIndex(nested_idef.m_object_uuid[j]);
03666         if ( obj_index < 0 )
03667           continue;
03668         const ONX_Model_Object& obj = m_object_table[obj_index];
03669         if ( 0 == obj.m_object )
03670           continue;
03671         if ( obj.m_object->ObjectType() == ON::instance_reference )
03672         {
03673           pNestedIRef = ON_InstanceRef::Cast(obj.m_object);
03674           if ( 0 != pNestedIRef )
03675           {
03676             if ( 0 == ON_UuidCompare( idef_uuid, pNestedIRef->m_instance_definition_uuid ) )
03677               return depth;
03678             iref_list.Append(pNestedIRef);
03679           }
03680         }
03681       }
03682     }
03683   }
03684 
03685   return (depth < max_depth) ? 0 : -2;
03686 }
03687 
03688 int ONX_Model::LayerIndex( const wchar_t* layer_name ) const
03689 {
03690   // slow and stupid search - what do you expect for free?
03691   // In a high quality app, you will want to override this
03692   // and do something a little smarter.
03693 
03694   int layer_index = -1;
03695   if ( 0 != layer_name && 0 != layer_name[0] )
03696   {
03697     int i, layer_count = m_layer_table.Count();
03698     for ( i = 0; i < layer_count; i++ )
03699     {
03700       if ( 0 == on_wcsicmp(layer_name, m_layer_table[i].LayerName() ) )
03701       {
03702         layer_index = i;
03703         break;
03704       }
03705     }
03706   }
03707   return layer_index;
03708 }
03709 
03710 
03711 void ONX_Model::GetUnusedLayerName( ON_wString& layer_name ) const
03712 {
03713   int i = 1;
03714   for(i = 1; i < 100000; i++ )
03715   {
03716     layer_name.Format("Layer_%02d",i);
03717     if ( LayerIndex(layer_name) < 0 )
03718       return;
03719   }
03720   layer_name = "Layer";
03721   return;
03722 }
03723 
03724 
03725 
03726 bool ONX_Model::SetDocumentUserString( const wchar_t* key, const wchar_t* string_value )
03727 {
03728   // This is a slow and stupid way to set a single string,
03729   // but I cannot modify the ONX_Model class to transparently
03730   // store document user string information until I can break
03731   // the public SDK in V6.
03732   bool rc = false;
03733   if ( 0 != key && 0 != key[0] )
03734   {
03735     ON_UUID doc_userstring_id = ON_DocumentUserStringList::m_ON_DocumentUserStringList_class_id.Uuid();
03736     for (int i = 0; i < m_userdata_table.Count(); i++ )
03737     {
03738       ONX_Model_UserData& ud = m_userdata_table[i];
03739       if ( ud.m_uuid == doc_userstring_id )
03740       {
03741         if ( TCODE_USER_RECORD == ud.m_goo.m_typecode && ud.m_goo.m_value != 0 )
03742         {
03743           ON_Read3dmBufferArchive ba( 
03744             (unsigned int)ud.m_goo.m_value,
03745             ud.m_goo.m_goo, 
03746             false,
03747             m_3dm_file_version,
03748             m_3dm_opennurbs_version
03749             );
03750           ON_Object* p = 0;
03751           if ( ba.ReadObject(&p) )
03752           {
03753             ON_DocumentUserStringList* sl = ON_DocumentUserStringList::Cast(p);
03754             if ( 0 != sl )
03755             {
03756               // modify the user string information
03757               rc = sl->SetUserString(key,string_value);
03758               if ( rc )
03759               {
03760                 // write the new informtion to a memory buffer
03761                 ON_Write3dmBufferArchive newgoo(ud.m_goo.m_value+1024,0,m_3dm_file_version,ON::Version());
03762                 if (    newgoo.BeginWrite3dmUserTable(doc_userstring_id,false,0,0)
03763                      && newgoo.WriteObject(sl) 
03764                      && newgoo.EndWrite3dmUserTable()
03765                      )
03766                 {
03767                   if (    newgoo.SizeOfArchive() > 0 
03768                        && newgoo.SizeOfArchive() <= 0xFFFFFFFF // max goo size if 4GB because we used an unsigned int back in the ice ages
03769                      )
03770                   {
03771                     // update the "goo"
03772                     unsigned char* goo = (unsigned char*)newgoo.HarvestBuffer();
03773                     unsigned int value = (unsigned int)newgoo.SizeOfArchive();
03774                     if ( 0 != goo && value > 0 )
03775                     {
03776                       onfree(ud.m_goo.m_goo); // delete old "goo"
03777                       ud.m_goo.m_value = (int)value;
03778                       ud.m_goo.m_goo = goo;
03779                     }
03780                   }
03781                 }
03782               }
03783             }
03784           }
03785           if ( 0 != p )
03786           {
03787             delete p;
03788             p = 0;
03789           }
03790         }
03791         break;
03792       }
03793     }
03794   }
03795   return rc;
03796 }
03797 
03798 
03799 bool ONX_Model::GetDocumentUserString( const wchar_t* key, ON_wString& string_value ) const
03800 {
03801   const wchar_t* s = 0;
03802   if ( 0 != key && 0 != key[0] )
03803   {
03804     // This is a slow and stupid way to get a single string,
03805     // but I cannot modify the ONX_Model class to transparently
03806     // store document user string information until I can break
03807     // the public SDK in V6.
03808     ON_ClassArray<ON_UserString> user_strings;
03809     GetDocumentUserStrings( user_strings );
03810     for ( int i = 0; i < user_strings.Count(); i++ )
03811     {
03812       if ( !user_strings[i].m_key.CompareNoCase(key) )
03813       {
03814         s = user_strings[i].m_string_value;
03815         break;
03816       }
03817     }
03818   }
03819   string_value = s;
03820   return (0 != s);
03821 }
03822 
03823 
03824 int ONX_Model::GetDocumentUserStrings( ON_ClassArray<ON_UserString>& user_strings ) const
03825 {
03826   int rc = 0;
03827   // user strings are stored as ON_Object user strings on
03828   // an ON_DocumentUserStringList object in a user table 
03829   // with id = doc_userstring_id.
03830   ON_UUID doc_userstring_id = ON_DocumentUserStringList::m_ON_DocumentUserStringList_class_id.Uuid();
03831   for (int i = 0; i < m_userdata_table.Count(); i++ )
03832   {
03833     const ONX_Model_UserData& ud = m_userdata_table[i];
03834     if ( ud.m_uuid == doc_userstring_id )
03835     {
03836       if ( TCODE_USER_RECORD == ud.m_goo.m_typecode && ud.m_goo.m_value != 0 )
03837       {
03838         ON_Read3dmBufferArchive ba( 
03839           (unsigned int)ud.m_goo.m_value,
03840           ud.m_goo.m_goo, 
03841           false,
03842           m_3dm_file_version,
03843           m_3dm_opennurbs_version
03844           );
03845 
03846         ON_Object* p = 0;
03847         if ( ba.ReadObject(&p) )
03848         {
03849           const ON_DocumentUserStringList* sl = ON_DocumentUserStringList::Cast(p);
03850           if ( 0 != sl )
03851           {
03852             rc = sl->GetUserStrings(user_strings);
03853           }
03854         }
03855         if ( 0 != p )
03856         {
03857           delete p;
03858           p = 0;
03859         }
03860       }
03861       break;
03862     }
03863   }
03864   return rc;
03865 }
03866 
03867 
03868 static int AuditTextureMappingTableHelper( 
03869       ONX_Model& model,
03870       bool bAttemptRepair,
03871       int* repair_count,
03872       ON_TextLog* text_log 
03873       )
03874 {
03875   if ( repair_count ) 
03876     *repair_count = 0;
03877   int i, count = model.m_mapping_table.Count();
03878   for ( i = 0; i < count; i++ )
03879   {
03880     ON_TextureMapping& mapping = model.m_mapping_table[i];
03881     if ( mapping.m_mapping_index != i )
03882     {
03883       if ( text_log )
03884       {
03885         text_log->Print("m_mapping_table[%d].m_mapping_index == %d (should be %d)\n",
03886                         i,mapping.m_mapping_index,i);
03887       }
03888       if ( bAttemptRepair )
03889       {
03890         mapping.m_mapping_index = i;
03891         if ( text_log )
03892         {
03893           text_log->PushIndent();
03894           text_log->Print("Repaired.\n");
03895           text_log->PopIndent();
03896         }
03897         if ( repair_count )
03898           *repair_count += 1;
03899       }
03900       else
03901       {
03902         return -1;
03903       }
03904     }
03905     if ( !mapping.IsValid(text_log) )
03906       return 1;
03907   }
03908   return 0;
03909 }
03910 
03911 
03912 static int AuditGroupTableHelper( 
03913       ONX_Model& model,
03914       bool bAttemptRepair,
03915       int* repair_count,
03916       ON_TextLog* text_log 
03917       )
03918 {
03919   if ( repair_count ) 
03920     *repair_count = 0;
03921   int i, count = model.m_group_table.Count();
03922   for ( i = 0; i < count; i++ )
03923   {
03924     ON_Group& group = model.m_group_table[i];
03925     if ( group.m_group_index != i )
03926     {
03927       if ( text_log )
03928       {
03929         text_log->Print("m_group_table[%d].m_group_index == %d (should be %d)\n",
03930                         i,group.m_group_index,i);
03931       }
03932       if ( bAttemptRepair )
03933       {
03934         group.m_group_index = i;
03935         if ( text_log )
03936         {
03937           text_log->PushIndent();
03938           text_log->Print("Repaired.\n");
03939           text_log->PopIndent();
03940         }
03941         if ( repair_count )
03942           *repair_count += 1;
03943       }
03944       else
03945       {
03946         return -1;
03947       }
03948     }
03949     if ( !group.IsValid(text_log) )
03950       return 1;
03951   }
03952   return 0;
03953 }
03954 
03955 
03956 static int AuditFontTableHelper( 
03957       ONX_Model& model,
03958       bool bAttemptRepair,
03959       int* repair_count,
03960       ON_TextLog* text_log 
03961       )
03962 {
03963   if ( repair_count ) 
03964     *repair_count = 0;
03965   int i, count = model.m_font_table.Count();
03966   for ( i = 0; i < count; i++ )
03967   {
03968     ON_Font& font = model.m_font_table[i];
03969     if ( font.m_font_index != i )
03970     {
03971       if ( text_log )
03972       {
03973         text_log->Print("m_font_table[%d].m_font_index == %d (should be %d)\n",
03974                         i,font.m_font_index,i);
03975       }
03976       if ( bAttemptRepair )
03977       {
03978         font.m_font_index = i;
03979         if ( text_log )
03980         {
03981           text_log->PushIndent();
03982           text_log->Print("Repaired.\n");
03983           text_log->PopIndent();
03984         }
03985         if ( repair_count )
03986           *repair_count += 1;
03987       }
03988       else
03989       {
03990         return -1;
03991       }
03992     }
03993     if ( !font.IsValid(text_log) )
03994       return 1;
03995   }
03996   return 0;
03997 }
03998 
03999 
04000 static int AuditDimStyleTableHelper( 
04001       ONX_Model& model,
04002       bool bAttemptRepair,
04003       int* repair_count,
04004       ON_TextLog* text_log 
04005       )
04006 {
04007   if ( repair_count ) 
04008     *repair_count = 0;
04009   int i, count = model.m_dimstyle_table.Count();
04010   for ( i = 0; i < count; i++ )
04011   {
04012     ON_DimStyle& dimstyle = model.m_dimstyle_table[i];
04013     if ( dimstyle.m_dimstyle_index != i )
04014     {
04015       if ( text_log )
04016       {
04017         text_log->Print("m_dimstyle_table[%d].m_dimstyle_index == %d (should be %d)\n",
04018                         i,dimstyle.m_dimstyle_index,i);
04019       }
04020       if ( bAttemptRepair )
04021       {
04022         dimstyle.m_dimstyle_index = i;
04023         if ( text_log )
04024         {
04025           text_log->PushIndent();
04026           text_log->Print("Repaired.\n");
04027           text_log->PopIndent();
04028         }
04029         if ( repair_count )
04030           *repair_count += 1;
04031       }
04032       else
04033       {
04034         return -1;
04035       }
04036     }
04037     if ( !dimstyle.IsValid(text_log) )
04038       return 1;
04039   }
04040   return 0;
04041 }
04042 
04043 
04044 static int AuditHatchPatternTableHelper( 
04045       ONX_Model& model,
04046       bool bAttemptRepair,
04047       int* repair_count,
04048       ON_TextLog* text_log 
04049       )
04050 {
04051   if ( repair_count ) 
04052     *repair_count = 0;
04053   int i, count = model.m_hatch_pattern_table.Count();
04054   for ( i = 0; i < count; i++ )
04055   {
04056     ON_HatchPattern& hatchpattern = model.m_hatch_pattern_table[i];
04057     if ( hatchpattern.m_hatchpattern_index != i )
04058     {
04059       if ( text_log )
04060       {
04061         text_log->Print("m_hatch_pattern_table[%d].m_hatchpattern_index == %d (should be %d)\n",
04062                         i,hatchpattern.m_hatchpattern_index,i);
04063       }
04064       if ( bAttemptRepair )
04065       {
04066         hatchpattern.m_hatchpattern_index = i;
04067         if ( text_log )
04068         {
04069           text_log->PushIndent();
04070           text_log->Print("Repaired.\n");
04071           text_log->PopIndent();
04072         }
04073         if ( repair_count )
04074           *repair_count += 1;
04075       }
04076       else
04077       {
04078         return -1;
04079       }
04080     }
04081     if ( !hatchpattern.IsValid(text_log) )
04082       return 1;
04083   }
04084   return 0;
04085 }
04086 
04087 
04088 static int AuditObjectAttributesHelper(
04089       ONX_Model& model,
04090       ON_3dmObjectAttributes& attributes,
04091       const char* parent_name,
04092       int parent_index,
04093       bool bAttemptRepair,
04094       int* repair_count,
04095       ON_TextLog* text_log 
04096       )
04097 {
04098   int repcount = 0;
04099   int errcount = 0;
04100 
04101   // validate key attributes.
04102   int layer_index = attributes.m_layer_index;
04103   if ( layer_index < 0 || layer_index >= model.m_layer_table.Count() )
04104   {
04105     errcount++;
04106     if ( text_log )
04107     {
04108       text_log->Print(parent_name,parent_index);
04109       text_log->Print("m_layer_index = %d is not valid.",layer_index);
04110     }
04111 
04112     if ( bAttemptRepair )
04113     {
04114       layer_index = model.m_settings.m_current_layer_index;
04115       if ( layer_index < 0 || layer_index >= model.m_layer_table.Count() 
04116          )
04117       {
04118         layer_index = 0;
04119       }
04120       if ( layer_index >= 0 && layer_index < model.m_layer_table.Count() )
04121       {
04122         repcount++;
04123         attributes.m_layer_index = layer_index;
04124         if ( text_log )
04125           text_log->Print(" Repaired.");
04126       }
04127     }
04128     if ( text_log )
04129     {
04130       text_log->Print("\n");
04131     }
04132   }
04133 
04134   int linetype_index = attributes.m_linetype_index;
04135   if ( linetype_index < -1 || linetype_index >= model.m_linetype_table.Count() )
04136   {
04137     errcount++;
04138     if ( text_log )
04139     {
04140       text_log->Print(parent_name,parent_index);
04141       text_log->Print("m_linetype_index = %d is not valid.",linetype_index);
04142     }
04143 
04144     if ( bAttemptRepair )
04145     {
04146       linetype_index = -1;
04147       repcount++;
04148       attributes.m_linetype_index = linetype_index;
04149       if ( text_log )
04150         text_log->Print(" Repaired.");
04151     }
04152     if ( text_log )
04153     {
04154       text_log->Print("\n");
04155     }
04156   }
04157 
04158   int material_index = attributes.m_material_index;
04159   if ( material_index < -1 || material_index >= model.m_material_table.Count() )
04160   {
04161     errcount++;
04162     if ( text_log )
04163     {
04164       text_log->Print(parent_name,parent_index);
04165       text_log->Print("m_material_index = %d is not valid.",material_index);
04166     }
04167 
04168     if ( bAttemptRepair )
04169     {
04170       material_index = -1;
04171       repcount++;
04172       attributes.m_material_index = material_index;
04173       if ( text_log )
04174         text_log->Print(" Repaired.");
04175     }
04176     if ( text_log )
04177     {
04178       text_log->Print("\n");
04179     }
04180   }
04181 
04182   if ( repcount > 0 && repair_count )
04183     *repair_count = *repair_count + repcount;
04184 
04185   return (errcount>0) ? 9 : 0;
04186 }
04187 
04188 
04189 
04190 static int AuditLightTableHelper( 
04191       ONX_Model& model,
04192       bool bAttemptRepair,
04193       int* repair_count,
04194       ON_TextLog* text_log 
04195       )
04196 {
04197   int rc = 0;
04198   if ( repair_count ) 
04199     *repair_count = 0;
04200   int i, count = model.m_light_table.Count();
04201   for ( i = 0; i < count; i++ )
04202   {
04203     ONX_Model_RenderLight& xlight = model.m_light_table[i];
04204     ON_Light& light = xlight.m_light;
04205     if ( light.m_light_index != i )
04206     {
04207       if ( text_log )
04208       {
04209         text_log->Print("m_light_table[%d].m_light_index == %d (should be %d)\n",
04210                         i,light.m_light_index,i);
04211       }
04212       if ( bAttemptRepair )
04213       {
04214         light.m_light_index = i;
04215         if ( text_log )
04216         {
04217           text_log->PushIndent();
04218           text_log->Print("Repaired.\n");
04219           text_log->PopIndent();
04220         }
04221         if ( repair_count )
04222           *repair_count += 1;
04223       }
04224       else
04225       {
04226         return -1;
04227       }
04228     }
04229 
04230     int attrc = AuditObjectAttributesHelper(model,
04231       xlight.m_attributes,
04232       "m_light_table[%d].m_attributes",
04233       i,
04234       bAttemptRepair,
04235       repair_count,
04236       text_log 
04237       );
04238 
04239     if ( attrc && 0 == rc )
04240       rc = 9;
04241 
04242     if ( !light.IsValid(text_log) )
04243     {
04244       if ( 0 == rc )
04245         rc = 1;
04246     }
04247 
04248   }
04249   return rc;
04250 }
04251 
04252 static int AuditMaterialTableHelper( 
04253       ONX_Model& model,
04254       bool bAttemptRepair,
04255       int* repair_count,
04256       ON_TextLog* text_log 
04257       )
04258 {
04259   if ( repair_count ) 
04260     *repair_count = 0;
04261   int i, count = model.m_material_table.Count();
04262   for ( i = 0; i < count; i++ )
04263   {
04264     ON_Material& mat = model.m_material_table[i];
04265     if ( mat.MaterialIndex() != i )
04266     {
04267       if ( text_log )
04268       {
04269         text_log->Print("m_material_table[%d].MaterialIndex() == %d (should be %d)\n",
04270                         i,mat.MaterialIndex(),i);
04271       }
04272       if ( bAttemptRepair )
04273       {
04274         mat.SetMaterialIndex(i);
04275         if ( text_log )
04276         {
04277           text_log->PushIndent();
04278           text_log->Print("Repaired.\n");
04279           text_log->PopIndent();
04280         }
04281         if ( repair_count )
04282           *repair_count += 1;
04283       }
04284       else
04285       {
04286         return -1;
04287       }
04288     }
04289     if ( !mat.IsValid(text_log) )
04290       return 1;
04291   }
04292   return 0;
04293 }
04294 
04295 static int AuditLinetypeTableHelper( 
04296       ONX_Model& model,
04297       bool bAttemptRepair,
04298       int* repair_count,
04299       ON_TextLog* text_log 
04300       )
04301 {
04302   if ( repair_count ) 
04303     *repair_count = 0;
04304   int i, count = model.m_linetype_table.Count();
04305   for ( i = 0; i < count; i++ )
04306   {
04307     ON_Linetype& lt = model.m_linetype_table[i];
04308     if ( lt.LinetypeIndex() != i )
04309     {
04310       if ( text_log )
04311       {
04312         text_log->Print("m_linetype_table[%d].LinetypeIndex() == %d (should be %d)\n",
04313                         i,lt.LinetypeIndex(),i);
04314       }
04315       if ( bAttemptRepair )
04316       {
04317         lt.SetLinetypeIndex(i);
04318         if ( text_log )
04319         {
04320           text_log->PushIndent();
04321           text_log->Print("Repaired.\n");
04322           text_log->PopIndent();
04323         }
04324         if ( repair_count )
04325           *repair_count += 1;
04326       }
04327       else
04328       {
04329         return -1;
04330       }
04331     }
04332     if ( !lt.IsValid(text_log) )
04333       return 10;
04334   }
04335   return 0;
04336 }
04337 
04338 static int AuditLayerTableHelper( 
04339       ONX_Model& model,
04340       bool bAttemptRepair,
04341       int* repair_count,
04342       ON_TextLog* text_log 
04343       )
04344 {
04345   int rc = 0;
04346   if ( repair_count ) 
04347     *repair_count = 0;
04348   int i, count = model.m_layer_table.Count();
04349   if ( count == 0 && (model.m_object_table.Count()>0 || model.m_light_table.Count()>0))
04350     rc = 2;
04351   for ( i = 0; i < count; i++ )
04352   {
04353     ON_Layer& layer = model.m_layer_table[i];
04354     if ( layer.LayerIndex() != i )
04355     {
04356       if ( text_log )
04357       {
04358         text_log->Print("m_layer_table[%d].LayerIndex() == %d (should be %d)\n",
04359                         i,layer.LayerIndex(),i);
04360       }
04361       if ( bAttemptRepair )
04362       {
04363         layer.SetLayerIndex(i);
04364         if ( text_log )
04365         {
04366           text_log->PushIndent();
04367           text_log->Print("Repaired.\n");
04368           text_log->PopIndent();
04369         }
04370         if ( repair_count )
04371           *repair_count += 1;
04372       }
04373       else
04374       {
04375         rc = -1;
04376       }
04377     }
04378 
04379     const wchar_t* layer_name = layer.LayerName();
04380 
04381     if ( 0 == layer_name || 0 == layer_name[0] )
04382     {
04383       if ( text_log )
04384       {
04385         text_log->Print("m_layer_table[%d].LayerName() is empty\n",i);
04386       }
04387       if ( bAttemptRepair )
04388       {
04389         ON_wString name;
04390         model.GetUnusedLayerName( name );
04391         layer.SetLayerName( name );
04392         if ( text_log )
04393         {
04394           text_log->PushIndent();
04395           text_log->Print("Repaired.\n");
04396           text_log->PopIndent();
04397         }
04398         if ( repair_count )
04399           *repair_count += 1;
04400         layer_name = layer.LayerName();
04401       }
04402       else if ( rc == 0 )
04403       {
04404         rc = 2;
04405       }
04406     }
04407 
04408     if ( !ONX_IsValidName(layer_name) )
04409     {
04410       if ( text_log )
04411       {
04412         text_log->Print("m_layer_table[%d].LayerName() is not valid\n",i);
04413       }
04414       if ( bAttemptRepair )
04415       {
04416         ON_wString name;
04417         model.GetUnusedLayerName( name );
04418         layer.SetLayerName( name );
04419         if ( text_log )
04420         {
04421           text_log->PushIndent();
04422           text_log->Print("Repaired.\n");
04423           text_log->PopIndent();
04424         }
04425         if ( repair_count )
04426           *repair_count += 1;
04427         layer_name = layer.LayerName();
04428       }
04429       else if ( rc == 0 )
04430       {
04431         rc = 2;
04432       }
04433     }
04434 
04435     int j = model.LayerIndex(layer_name);
04436     if ( i != j )
04437     {
04438       if ( text_log )
04439       {
04440         text_log->Print("m_layer_table[%d] and m_layer_table[%d] have same layer name.\n",i,j);
04441       }
04442       if ( bAttemptRepair )
04443       {
04444         ON_wString name;
04445         model.GetUnusedLayerName( name );
04446         layer.SetLayerName( name );
04447         if ( text_log )
04448         {
04449           text_log->PushIndent();
04450           text_log->Print("Repaired.\n");
04451           text_log->PopIndent();
04452         }
04453         if ( repair_count )
04454           *repair_count += 1;
04455       }
04456       else if ( rc == 0 )
04457       {
04458         rc = 2;
04459       }
04460     }
04461 
04462     if ( rc == 0 && !layer.IsValid(text_log) )
04463       rc = 2;
04464   }
04465   return rc;
04466 }
04467 
04468 static int AuditIdsHelper(
04469       ON_SimpleArray<ON_UuidIndex>& id_list,
04470       ON_UuidIndexList* index_list,
04471       bool bAttemptRepair,
04472       int* repair_count,
04473       ON_TextLog* text_log,
04474       const char* nil_id_msg,
04475       const char* dup_id_msg
04476       )
04477 {
04478   int nil_count = 0;
04479   int dup_count = 0;
04480   int rep_count = 0;
04481 
04482   if ( index_list )
04483     index_list->Empty();
04484   const int count = id_list.Count();
04485   if ( count > 0 )
04486   {
04487     int i;
04488     ON_UUID id;
04489 
04490     // Make sure objects have non-nil ids
04491     for ( i = 0; i < count; i++ )
04492     {
04493       id_list[i].m_i = i;
04494       if ( ON_nil_uuid == id_list[i].m_id )
04495       {
04496         nil_count++;
04497         if ( text_log )
04498           text_log->Print(nil_id_msg,i);
04499 
04500         if ( bAttemptRepair )
04501         {
04502           id = ON_nil_uuid;
04503           if ( ON_CreateUuid(id) && !ON_UuidIsNil(id) )
04504           {
04505             id_list[i].m_id = id;
04506             rep_count++;
04507             if ( text_log )
04508               text_log->Print(" Repaired.");
04509           }
04510         }
04511         if ( text_log )
04512           text_log->Print("\n");
04513       }
04514     }
04515 
04516     if ( count > 1 )
04517     {
04518       // Make sure objects have unique ids
04519       id_list.QuickSort( ON_UuidIndex::CompareIdAndIndex );
04520       ON_UuidIndex id0 = id_list[0];
04521       for ( i = 1; i < count; i++ )
04522       {
04523         if ( ON_nil_uuid == id_list[i].m_id )
04524         {
04525           // nil ids were handled and counted above.
04526           // Either repair is false or we are not running
04527           // on Windows and the user supplied ON_CreateUuid
04528           // is returning nil ids.
04529           continue;
04530         }
04531 
04532         if ( id_list[i].m_id == id0.m_id )
04533         {
04534           dup_count++;
04535           if ( text_log )
04536             text_log->Print(dup_id_msg,id0.m_i,id_list[i].m_i);
04537 
04538           if ( bAttemptRepair )
04539           {
04540             // fix duplicate object id
04541             id = ON_nil_uuid;
04542             if ( ON_CreateUuid(id) && !ON_UuidIsNil(id) )
04543             {
04544               rep_count++;
04545               id_list[i].m_id = id;
04546               if ( text_log )
04547                 text_log->Print(" Repaired.");
04548             }
04549           }
04550           if ( text_log )
04551             text_log->Print("\n");
04552         }
04553         else
04554         {
04555           id0 = id_list[i];
04556         }
04557       }
04558     }
04559 
04560     if ( index_list )
04561     {
04562       // rebuild index_list
04563       index_list->Reserve(count);
04564       for ( i = 0; i < count; i++ )
04565       {
04566         index_list->AddUuidIndex(id_list[i].m_id,id_list[i].m_i,false);
04567       }
04568     }
04569   }
04570 
04571   if ( repair_count )
04572     *repair_count = rep_count;
04573 
04574   return dup_count + nil_count;
04575 }
04576 
04577 static int AuditObjectIdsHelper(
04578       ONX_Model& model, 
04579       bool bAttemptRepair,
04580       int* repair_count,
04581       ON_TextLog* text_log 
04582       )
04583 {
04584   int rc = 0;
04585   const int count = model.m_object_table.Count();
04586   model.m_object_id_index.Empty();
04587   if ( count > 0 )
04588   {
04589     int i;
04590     ON_SimpleArray<ON_UuidIndex> id_list(count);
04591     for ( i = 0; i < count; i++ )
04592     {
04593       id_list.AppendNew().m_id = model.m_object_table[i].m_attributes.m_uuid;
04594     }
04595     rc = AuditIdsHelper(
04596               id_list,
04597               &model.m_object_id_index,
04598               bAttemptRepair,
04599               repair_count,
04600               text_log,
04601               "m_object_table[%d].m_attributes.m_uuid is nil.",
04602               "m_object_table[%d] and [%d] have the same id."
04603               );
04604     if (rc && bAttemptRepair )
04605     {
04606       for ( i = 0; i < count; i++ )
04607       {
04608         model.m_object_table[id_list[i].m_i].m_attributes.m_uuid = id_list[i].m_id;
04609       }
04610     }
04611   }
04612   return rc;
04613 }
04614 
04615 
04616 static int AuditLightIdsHelper(
04617       ONX_Model& model, 
04618       bool bAttemptRepair,
04619       int* repair_count,
04620       ON_TextLog* text_log 
04621       )
04622 {
04623   int rc = 0;
04624   int mismatch_count = 0;
04625   const int count = model.m_light_table.Count();
04626   if ( count > 0 )
04627   {
04628     int i;
04629     ON_SimpleArray<ON_UuidIndex> id_list(count);
04630     for ( i = 0; i < count; i++ )
04631     {
04632       ONX_Model_RenderLight& light = model.m_light_table[i];
04633       if ( ON_UuidCompare(light.m_light.m_light_id,light.m_attributes.m_uuid) )
04634       {
04635         mismatch_count++;
04636         if ( text_log )
04637         {
04638           text_log->Print("m_light_table[%d] light id and attributes id differ.",i);
04639         }
04640         if ( bAttemptRepair )
04641         {
04642           if ( repair_count )
04643             *repair_count = *repair_count + 1;
04644           if ( ON_nil_uuid == light.m_light.m_light_id )
04645             light.m_light.m_light_id = light.m_attributes.m_uuid;
04646           else
04647             light.m_attributes.m_uuid = light.m_light.m_light_id;
04648           if ( text_log )
04649             text_log->Print(" Repaired.");
04650         }
04651         if ( text_log )
04652           text_log->Print("\n");
04653       }
04654 
04655       if ( ON_nil_uuid == light.m_light.m_light_id )
04656       {
04657         id_list.AppendNew().m_id = light.m_attributes.m_uuid;
04658       }
04659       else 
04660       {
04661         id_list.AppendNew().m_id = light.m_light.m_light_id;
04662       }
04663     }
04664 
04665     rc = AuditIdsHelper(
04666               id_list,
04667               0, // no ONX_Model id index for lights
04668               bAttemptRepair,
04669               repair_count,
04670               text_log,
04671               "m_light_table[%d] light id is nil.",
04672               "m_light_table[%d] and[%d] have the same id."
04673               );
04674 
04675     rc += mismatch_count;
04676 
04677     if ( rc && bAttemptRepair )
04678     {
04679       for ( i = 0; i < count; i++ )
04680       {
04681         ONX_Model_RenderLight& light = model.m_light_table[id_list[i].m_i];
04682         light.m_light.m_light_id = id_list[i].m_id;
04683         light.m_attributes.m_uuid = light.m_light.m_light_id;
04684       }
04685     }
04686 
04687     // make sure light ids are not duplicated in object id list
04688     for ( i = 0; i < count; i++ )
04689     {
04690       ONX_Model_RenderLight& light = model.m_light_table[i];
04691       int oi = -1;
04692       if ( model.m_object_id_index.FindUuid(light.m_light.m_light_id,&oi) )
04693       {
04694         rc++;
04695         if ( text_log )
04696         {
04697           text_log->Print("m_light_table[%d] and m_object_table[%d] have same id.",
04698                           i,
04699                           oi
04700                           );
04701         }
04702         if ( bAttemptRepair )
04703         {
04704           ON_UuidIndex light_id;
04705           memset(&light_id,0,sizeof(light_id));
04706           light_id.m_id = ON_nil_uuid;
04707           light_id.m_i = -1;
04708           if ( ON_CreateUuid(light_id.m_id) && ON_nil_uuid != light_id.m_id )
04709           {
04710             if (    !model.m_object_id_index.FindUuid(light_id.m_id) 
04711                  && id_list.BinarySearch( &light_id, ON_UuidIndex::CompareId ) < 0 
04712                )
04713             {
04714               if ( repair_count )
04715                 *repair_count = *repair_count + 1;
04716               light.m_light.m_light_id = light_id.m_id;
04717               light.m_attributes.m_uuid = light.m_light.m_light_id;
04718               if ( text_log )
04719                 text_log->Print(" Repaired.");
04720             }
04721           }
04722         }
04723         if ( text_log )
04724           text_log->Print("\n");
04725       }
04726     }
04727   }
04728 
04729   return rc;
04730 }
04731 
04732 static int AuditIDefIdsHelper(
04733       ONX_Model& model, 
04734       bool bAttemptRepair,
04735       int* repair_count,
04736       ON_TextLog* text_log 
04737       )
04738 {
04739   int rc = 0;
04740   const int count = model.m_idef_table.Count();
04741   model.m_idef_id_index.Empty();
04742   if ( count > 0 )
04743   {
04744     int i;
04745     ON_SimpleArray<ON_UuidIndex> id_list(count);
04746     for ( i = 0; i < count; i++ )
04747     {
04748       id_list.AppendNew().m_id = model.m_idef_table[i].m_uuid;
04749     }
04750     rc = AuditIdsHelper(
04751               id_list,
04752               &model.m_idef_id_index,
04753               bAttemptRepair,
04754               repair_count,
04755               text_log,
04756               "m_idef_table[%d].m_attributes.m_uuid is nil.",
04757               "m_idef_table[%d] and[%d] are the same."
04758               );
04759     if (rc && bAttemptRepair )
04760     {
04761       for ( i = 0; i < count; i++ )
04762       {
04763         model.m_idef_table[id_list[i].m_i].m_uuid = id_list[i].m_id;
04764       }
04765     }
04766   }
04767   return rc;
04768 }
04769 
04770 static int AuditMappingIdsHelper(
04771       ONX_Model& model, 
04772       bool bAttemptRepair,
04773       int* repair_count,
04774       ON_TextLog* text_log 
04775       )
04776 {
04777   int rc = 0;
04778   const int count = model.m_mapping_table.Count();
04779   model.m_mapping_id_index.Empty();
04780   if ( count > 0 )
04781   {
04782     int i;
04783     ON_SimpleArray<ON_UuidIndex> id_list(count);
04784     for ( i = 0; i < count; i++ )
04785     {
04786       id_list.AppendNew().m_id = model.m_mapping_table[i].m_mapping_id;
04787     }
04788     rc = AuditIdsHelper(
04789               id_list,
04790               &model.m_mapping_id_index,
04791               bAttemptRepair,
04792               repair_count,
04793               text_log,
04794               "m_mapping_table[%d].m_mapping_id is nil.",
04795               "m_mapping_table[%d] and[%d] are the same."
04796               );
04797     if (rc && bAttemptRepair )
04798     {
04799       for ( i = 0; i < count; i++ )
04800       {
04801         model.m_mapping_table[id_list[i].m_i].m_mapping_id = id_list[i].m_id;
04802       }
04803     }
04804   }
04805   return rc;
04806 }
04807 
04808 
04809 static int AuditMaterialIdsHelper(
04810       ONX_Model& model, 
04811       bool bAttemptRepair,
04812       int* repair_count,
04813       ON_TextLog* text_log 
04814       )
04815 {
04816   int rc = 0;
04817   const int count = model.m_material_table.Count();
04818   model.m_material_id_index.Empty();
04819   if ( count > 0 )
04820   {
04821     int i;
04822     ON_SimpleArray<ON_UuidIndex> id_list(count);
04823     for ( i = 0; i < count; i++ )
04824     {
04825       id_list.AppendNew().m_id = model.m_material_table[i].m_material_id;
04826     }
04827     rc = AuditIdsHelper(
04828               id_list,
04829               &model.m_material_id_index,
04830               bAttemptRepair,
04831               repair_count,
04832               text_log,
04833               "m_material_table[%d].m_material_id is nil.",
04834               "m_material_table[%d] and[%d] are the same."
04835               );
04836     if (rc && bAttemptRepair )
04837     {
04838       for ( i = 0; i < count; i++ )
04839       {
04840         model.m_material_table[id_list[i].m_i].m_material_id = id_list[i].m_id;
04841       }
04842     }
04843   }
04844   return rc;
04845 }
04846 
04847 static int AuditModelIdsHelper( 
04848       ONX_Model& model, 
04849       bool bAttemptRepair,
04850       int* repair_count,
04851       ON_TextLog* text_log, 
04852       ON_SimpleArray<int>* warnings
04853       )
04854 {
04855   int warning_count = 0;
04856   int i;
04857 
04858   // object ids
04859   i = AuditObjectIdsHelper(model,bAttemptRepair,repair_count,text_log);
04860   if (i < 0 ) 
04861     return i;
04862   if (i > 0 )
04863   {
04864     warning_count += i;
04865     if (warnings)
04866       warnings->Append(3);
04867   }
04868 
04869   // light ids
04870   i = AuditLightIdsHelper(model,bAttemptRepair,repair_count,text_log);
04871   if (i < 0 ) 
04872     return i;
04873   if (i > 0 )
04874   {
04875     warning_count += i;
04876     if (warnings)
04877       warnings->Append(15);
04878   }
04879 
04880   // idef ids
04881   i = AuditIDefIdsHelper(model,bAttemptRepair,repair_count,text_log);
04882   if (i < 0 ) 
04883     return i;
04884   if (i > 0 )
04885   {
04886     warning_count += i;
04887     if (warnings)
04888       warnings->Append(12);
04889   }
04890 
04891   // mapping ids
04892   i = AuditMappingIdsHelper(model,bAttemptRepair,repair_count,text_log);
04893   if (i < 0 ) 
04894     return i;
04895   if (i > 0 )
04896   {
04897     warning_count += i;
04898     if (warnings)
04899       warnings->Append(13);
04900   }
04901 
04902   // material ids
04903   i = AuditMaterialIdsHelper(model,bAttemptRepair,repair_count,text_log);
04904   if (i < 0 ) 
04905     return i;
04906   if (i > 0 )
04907   {
04908     warning_count += i;
04909     if (warnings)
04910       warnings->Append(13);
04911   }
04912 
04913   return warning_count;
04914 }
04915 
04916 static int AuditIDefTableHelper( 
04917       ONX_Model& model,
04918       bool bAttemptRepair,
04919       int* repair_count,
04920       ON_TextLog* text_log 
04921       )
04922 {
04923   int rc = 0;
04924   int i, j, count = model.m_idef_table.Count();
04925 
04926   // AuditInstanceDefinitionIds() are already validated/repaired idef ids.
04927 
04928   // make sure idef names are empty or valid and unique
04929   for ( i = 0; i < count; i++ )
04930   {
04931     bool idef_ok = true;
04932     ON_InstanceDefinition& idef = model.m_idef_table[i];
04933     const wchar_t* idef_name = idef.Name();
04934     if ( 0 == idef_name )
04935       continue;
04936 
04937     if ( !ONX_IsValidName(idef_name) )
04938     {
04939       if ( text_log )
04940         text_log->Print("m_idef_table[%d].Name() = \"%ls\" is not valid.\n",i,idef_name);
04941       idef_ok = false;
04942     }
04943     else
04944     {
04945       j = model.IDefIndex( idef_name );
04946       if ( j != i )
04947       {
04948         if ( text_log )
04949           text_log->Print("m_idef_table[%d].Name() = m_idef_table[%d].Name().\n",i,j);
04950         idef_ok = false;
04951       }
04952     }
04953     if ( bAttemptRepair )
04954     {
04955       ON_wString new_name;
04956       model.GetUnusedIDefName(new_name);
04957       if ( ONX_IsValidName(new_name) && -1 == model.IDefIndex(new_name) )
04958       {
04959         idef.SetName(new_name);
04960         if ( repair_count )
04961           *repair_count = *repair_count + 1;
04962         if ( text_log )
04963           text_log->Print("Repaired.\n");
04964         idef_ok = true;
04965       }
04966     }
04967     if ( !idef_ok && rc == 0 )
04968     {
04969       rc = 5;
04970     }
04971   }
04972 
04973   for ( i = 0; i < count; i++ )
04974   {
04975     bool idef_ok = true;
04976     ON_InstanceDefinition& idef = model.m_idef_table[i];
04977 
04978     // make sure object uuid's are valid
04979     int k;
04980     j = 0;
04981     for ( j = k = 0; j < idef.m_object_uuid.Count(); k++ )
04982     {
04983       ON_UUID obj_uuid = idef.m_object_uuid[j];
04984       int obj_index = model.ObjectIndex(obj_uuid);
04985       if ( obj_index < 0 )
04986       {
04987         if ( text_log )
04988           text_log->Print("m_idef_table[%d].m_object_uuid[%d] is not an object's uuid.\n",i,k);
04989         if ( bAttemptRepair )
04990         {
04991           idef.m_object_uuid.Remove(j);
04992           if ( repair_count )
04993             *repair_count = *repair_count + 1;
04994           if ( text_log )
04995             text_log->Print("Repaired.\n");
04996         }
04997         else
04998         {
04999           j++;
05000           idef_ok = false;
05001         }
05002       }
05003       else
05004       {
05005         ONX_Model_Object& obj = model.m_object_table[obj_index];
05006         if ( ON::idef_object != obj.m_attributes.Mode() )
05007         {
05008           if ( text_log )
05009             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);
05010           if ( bAttemptRepair )
05011           {
05012             idef.m_object_uuid.Remove(j);
05013             if ( repair_count )
05014               *repair_count = *repair_count + 1;
05015             if ( text_log )
05016               text_log->Print("Repaired.\n");
05017           }
05018           else
05019           {
05020             j++;
05021             idef_ok = false;
05022           }
05023         }
05024         else if ( 0 == obj.m_object )
05025         {
05026           if ( text_log )
05027             text_log->Print("Object with uuid m_idef_table[%d].m_object_uuid[%d] has NULL m_object.\n",i,k);
05028           if ( bAttemptRepair )
05029           {
05030             idef.m_object_uuid.Remove(j);
05031             if ( repair_count )
05032               *repair_count = *repair_count + 1;
05033             if ( text_log )
05034               text_log->Print("Repaired.\n");
05035           }
05036           else
05037           {
05038             j++;
05039             idef_ok = false;
05040           }
05041         }
05042         else if ( obj.m_object->ObjectType() == ON::instance_reference )
05043         {
05044           const ON_InstanceRef* pNestedRef = ON_InstanceRef::Cast(obj.m_object);
05045           if ( pNestedRef )
05046           {
05047             if ( model.UsesIDef( *pNestedRef, idef.Uuid() ) )
05048             {
05049               if ( text_log )
05050                 text_log->Print("m_idef_table[%d].m_object_uuid[%d] is a circular reference.\n",i,k);
05051               if ( bAttemptRepair )
05052               {
05053                 idef.m_object_uuid.Remove(j);
05054                 if ( repair_count )
05055                   *repair_count = *repair_count + 1;
05056                 if ( text_log )
05057                   text_log->Print("Repaired.\n");
05058               }
05059               else
05060               {
05061                 j++;
05062                 idef_ok = false;
05063                 rc = -1; // circular reference
05064               }
05065             }
05066             else
05067               j++;
05068           }
05069           else
05070             j++;
05071         }
05072         else
05073           j++;
05074       }
05075     }
05076     if ( idef.m_object_uuid.Count() <= 0 )
05077     {
05078       if ( text_log )
05079         text_log->Print("m_idef_table[%d].m_object_uuid.Count() = 0.\n",i);
05080       idef_ok = false;
05081     }
05082     if ( !idef_ok && rc == 0 )
05083       rc = 6;
05084 
05085   }
05086 
05087   return rc;
05088 }
05089 
05090 
05091 static int AuditObjectTableHelper( 
05092       ONX_Model& model,
05093       bool bAttemptRepair,
05094       int* repair_count,
05095       ON_TextLog* text_log 
05096       )
05097 {
05098   // AuditObjectIds() are already validated/repaired object ids.
05099 
05100   int rc = 0;
05101   int i, count = model.m_object_table.Count();
05102 
05103   for ( i = 0; i < count; i++ )
05104   {
05105     ONX_Model_Object& obj = model.m_object_table[i];
05106     if ( 0 == obj.m_object )
05107     {
05108       rc = 7;
05109       if ( text_log )
05110         text_log->Print("m_object_table[%d].m_object is NULL.\n",i);
05111     }
05112     else if ( false == obj.m_object->IsValid(NULL) )
05113     {
05114       rc = 8;
05115       if ( text_log )
05116       {
05117         text_log->Print("m_object_table[%d].m_object->IsValid() = false.\n",i);
05118         text_log->PushIndent();
05119         text_log->PushIndent();
05120         obj.m_object->IsValid(text_log);
05121         text_log->PopIndent();
05122         text_log->PopIndent();
05123       }
05124     }
05125 
05126     int attrc = AuditObjectAttributesHelper(model,
05127                                 obj.m_attributes,
05128                                 "m_object_table[%d].m_attributes",
05129                                 i,
05130                                 bAttemptRepair,
05131                                 repair_count,
05132                                 text_log
05133                                 );
05134     if ( 0 == rc && attrc )
05135       rc = attrc;
05136   }
05137 
05138   return rc;
05139 }
05140 
05141 static int AuditHistoryRecordTableHelper( 
05142       ONX_Model& model,
05143       bool bAttemptRepair,
05144       int* repair_count,
05145       ON_TextLog* text_log 
05146       )
05147 {
05148   // TODO - make sure object id's exist
05149   return 0;
05150 }
05151 
05152 int ONX_Model::Audit( 
05153       bool bAttemptRepair,
05154       int* repair_count,
05155       ON_TextLog* text_log,
05156       ON_SimpleArray<int>* warnings
05157       )
05158 {
05159   int warning_count = 0;
05160   int rc = 0, i;
05161   int repcnt;
05162   if ( repair_count ) 
05163     *repair_count = 0;
05164 
05165   repcnt = 0;
05166   i = AuditModelIdsHelper( *this, bAttemptRepair, &repcnt, text_log, warnings );
05167   if ( repair_count ) 
05168     *repair_count += repcnt;
05169   if ( i < 0 )
05170     return i;
05171   warning_count += i;
05172 
05173   repcnt = 0;
05174   i = AuditTextureMappingTableHelper( *this, bAttemptRepair, &repcnt, text_log );
05175   if ( repair_count ) 
05176     *repair_count += repcnt;
05177   if ( i < 0 )
05178     return i;
05179   if ( 0 == rc && 0 != i )
05180     rc = i;
05181 
05182   repcnt = 0;
05183   i = AuditMaterialTableHelper( *this, bAttemptRepair, &repcnt, text_log );
05184   if ( repair_count ) 
05185     *repair_count += repcnt;
05186   if ( i < 0 )
05187     return i;
05188   if ( 0 == rc && 0 != i )
05189     rc = i;
05190 
05191   repcnt = 0;
05192   i = AuditLinetypeTableHelper( *this, bAttemptRepair, &repcnt, text_log );
05193   if ( repair_count ) 
05194     *repair_count += repcnt;
05195   if ( i < 0 )
05196     return i;
05197   if ( 0 == rc && 0 != i )
05198     rc = i;
05199 
05200   repcnt = 0;
05201   i = AuditLayerTableHelper( *this, bAttemptRepair, &repcnt, text_log );
05202   if ( repair_count ) 
05203     *repair_count += repcnt;
05204   if ( i < 0 )
05205     return i;
05206   if ( 0 == rc && 0 != i )
05207     rc = i;
05208 
05209   repcnt = 0;
05210   i = AuditGroupTableHelper( *this, bAttemptRepair, &repcnt, text_log );
05211   if ( repair_count ) 
05212     *repair_count += repcnt;
05213   if ( i < 0 )
05214     return i;
05215   if ( 0 == rc && 0 != i )
05216     rc = i;
05217 
05218   repcnt = 0;
05219   i = AuditFontTableHelper( *this, bAttemptRepair, &repcnt, text_log );
05220   if ( repair_count ) 
05221     *repair_count += repcnt;
05222   if ( i < 0 )
05223     return i;
05224   if ( 0 == rc && 0 != i )
05225     rc = i;
05226 
05227   repcnt = 0;
05228   i = AuditDimStyleTableHelper( *this, bAttemptRepair, &repcnt, text_log );
05229   if ( repair_count ) 
05230     *repair_count += repcnt;
05231   if ( i < 0 )
05232     return i;
05233   if ( 0 == rc && 0 != i )
05234     rc = i;
05235 
05236   repcnt = 0;
05237   i = AuditLightTableHelper( *this, bAttemptRepair, &repcnt, text_log );
05238   if ( repair_count ) 
05239     *repair_count += repcnt;
05240   if ( i < 0 )
05241     return i;
05242   if ( 0 == rc && 0 != i )
05243     rc = i;
05244 
05245   repcnt = 0;
05246   i = AuditHatchPatternTableHelper( *this, bAttemptRepair, &repcnt, text_log );
05247   if ( repair_count ) 
05248     *repair_count += repcnt;
05249   if ( i < 0 )
05250     return i;
05251   if ( 0 == rc && 0 != i )
05252     rc = i;
05253 
05254   repcnt = 0;
05255   i = AuditIDefTableHelper( *this, bAttemptRepair, &repcnt, text_log );
05256   if ( repair_count ) 
05257     *repair_count += repcnt;
05258   if ( i < 0 )
05259     return i;
05260   if ( 0 == rc && 0 != i )
05261     rc = i;
05262 
05263   repcnt = 0;
05264   i = AuditObjectTableHelper( *this, bAttemptRepair, &repcnt, text_log );
05265   if ( repair_count ) 
05266     *repair_count += repcnt;
05267   if ( i < 0 )
05268     return i;
05269   if ( 0 == rc && 0 != i )
05270     rc = i;
05271 
05272   repcnt = 0;
05273   i = AuditHistoryRecordTableHelper( *this, bAttemptRepair, &repcnt, text_log );
05274   if ( repair_count ) 
05275     *repair_count += repcnt;
05276   if ( i < 0 )
05277     return i;
05278   if ( 0 == rc && 0 != i )
05279     rc = i;
05280 
05281   return warning_count;
05282 }
05283 
05284 static const ON_UnknownUserData* RDKObjectUserDataHelper(const ON_UserData* objectud)
05285 {
05286   // CRhRdkUserData object id: AFA82772-1525-43dd-A63C-C84AC5806911
05287   // CRhRdkUserData::m_userdata_uuid = B63ED079-CF67-416c-800D-22023AE1BE21
05288 
05289   // CRhRdkUserData object id
05290   // {AFA82772-1525-43dd-A63C-C84AC5806911}
05291   static const ON_UUID CRhRdkUserData_object_id = 
05292   { 0xAFA82772, 0x1525, 0x43dd, { 0xA6, 0x3C, 0xC8, 0x4A, 0xC5, 0x80, 0x69, 0x11 } };
05293 
05294   // CRhRdkUserData::m_userdata_uuid
05295   // {B63ED079-CF67-416c-800D-22023AE1BE21}
05296   static const ON_UUID CRhRdkUserData_userdata_uuid = 
05297   { 0xB63ED079, 0xCF67, 0x416c, { 0x80, 0x0D, 0x22, 0x02, 0x3A, 0xE1, 0xBE, 0x21 } };
05298   
05299   const ON_UnknownUserData* unknown_ud = ON_UnknownUserData::Cast(objectud);
05300   
05301   bool rc = ( 0 != unknown_ud 
05302               && unknown_ud->m_sizeof_buffer > 0
05303               && 0 != unknown_ud->m_buffer
05304               && 0 == ON_UuidCompare(CRhRdkUserData_object_id,unknown_ud->m_unknownclass_uuid)
05305               && 0 == ON_UuidCompare(CRhRdkUserData_userdata_uuid,unknown_ud->m_userdata_uuid)
05306             );
05307   return rc ? unknown_ud : 0;
05308 }
05309 
05310 bool ONX_Model::IsRDKObjectInformation(const ON_UserData& objectud)
05311 {
05312   return 0 != RDKObjectUserDataHelper(&objectud);
05313 }
05314 
05315 bool ONX_Model::GetRDKObjectInformation(const ON_Object& object,ON_wString& rdk_xml_object_data)
05316 {
05317   rdk_xml_object_data.SetLength(0);
05318   const ON_UnknownUserData* unknown_ud = 0;
05319   const ON_UserData* ud = ON_UserData::Cast(&object);
05320   if ( 0 != ud )
05321   {
05322     unknown_ud = RDKObjectUserDataHelper(ud);
05323   }
05324   else
05325   {
05326     for ( ud = object.FirstUserData(); 0 != ud && 0 == unknown_ud; ud = ud->Next() )
05327     {
05328       unknown_ud = RDKObjectUserDataHelper(ud);
05329     }
05330   }
05331 
05332   if ( 0 == unknown_ud )
05333     return false;
05334 
05335   ON_Read3dmBufferArchive a(unknown_ud->m_sizeof_buffer,unknown_ud->m_buffer,false,unknown_ud->m_3dm_version,unknown_ud->m_3dm_opennurbs_version);
05336   int version = 0;
05337   if (!a.ReadInt(&version) )
05338     return false;
05339   
05340   if ( 1 == version )
05341   {
05342     if ( !a.ReadString(rdk_xml_object_data) )
05343       return false;
05344   }
05345   else if ( 2 == version )
05346   {
05347     // UTF8 string
05348     ON_SimpleArray< char > s;
05349     int slen = 0;
05350     if ( !a.ReadInt(&slen) )
05351       return false;
05352     if ( slen <= 0 )
05353       return false;
05354     if ( slen + 4 > unknown_ud->m_sizeof_buffer )
05355       return false;
05356     s.Reserve(slen+1);
05357     s.SetCount(slen+1);
05358     s[slen] = 0;
05359     if ( !a.ReadChar(slen,s.Array() ) ) 
05360       return false;
05361     const char* sArray = s.Array();
05362     if ( 0 != sArray && 0 != sArray[0] )
05363     {
05364       unsigned int error_status = 0;
05365       int wLen = ON_ConvertUTF8ToWideChar(sArray,-1,0,0,&error_status,0,0,0);
05366       if ( wLen > 0 && 0 == error_status )
05367       {
05368         rdk_xml_object_data.SetLength(wLen+2);
05369         wLen = ON_ConvertUTF8ToWideChar(sArray,-1,rdk_xml_object_data.Array(),wLen+1,&error_status,0,0,0);
05370         if ( wLen > 0 && 0 == error_status )
05371           rdk_xml_object_data.SetLength(wLen);
05372         else
05373           rdk_xml_object_data.SetLength(0);
05374       }
05375       if ( 0 != error_status )
05376       {
05377         ON_ERROR("RDK xml object information is not a valid UTF-8 string.");
05378       }
05379     }
05380   }
05381 
05382   return rdk_xml_object_data.Length() > 0;
05383 }
05384 
05385 bool ONX_Model::IsRDKDocumentInformation(const ONX_Model_UserData& docud)
05386 {
05387   // {16592D58-4A2F-401D-BF5E-3B87741C1B1B}
05388   static const ON_UUID rdk_plugin_id = 
05389   { 0x16592D58, 0x4A2F, 0x401D, { 0xBF, 0x5E, 0x3B, 0x87, 0x74, 0x1C, 0x1B, 0x1B } };
05390 
05391   return ( 0 == ON_UuidCompare(rdk_plugin_id,docud.m_uuid) && docud.m_goo.m_value >= 4 && 0 != docud.m_goo.m_goo );
05392 }
05393 
05394 
05395 bool ONX_Model::GetRDKDocumentInformation(const ONX_Model_UserData& docud,ON_wString& rdk_xml_document_data)
05396 {
05397   if ( !ONX_Model::IsRDKDocumentInformation(docud) )
05398     return false;
05399 
05400   ON_Read3dmBufferArchive a(docud.m_goo.m_value,docud.m_goo.m_goo,false,docud.m_usertable_3dm_version,docud.m_usertable_opennurbs_version);
05401 
05402   int version = 0;
05403   if (!a.ReadInt(&version) )
05404     return false;
05405   
05406   if ( 1 == version )
05407   {
05408     // UTF-16 string
05409     if ( !a.ReadString(rdk_xml_document_data) )
05410       return false;
05411   }
05412   else if ( 3 == version )
05413   {
05414     // UTF-8 string
05415     int slen = 0;
05416     if ( !a.ReadInt(&slen) )
05417       return 0;
05418     if ( slen <= 0 )
05419       return 0;
05420     if ( slen + 4 > docud.m_goo.m_value )
05421       return 0;
05422     ON_String s;
05423     s.SetLength(slen);
05424     if ( !a.ReadChar(slen,s.Array()) )
05425       return 0;
05426     const char* sArray = s.Array();
05427     if ( 0 != sArray && 0 != sArray[0] )
05428     {
05429       unsigned int error_status = 0;
05430       int wLen = ON_ConvertUTF8ToWideChar(sArray,-1,0,0,&error_status,0,0,0);
05431       if ( wLen > 0 && 0 == error_status )
05432       {
05433         rdk_xml_document_data.SetLength(wLen+2);
05434         wLen = ON_ConvertUTF8ToWideChar(sArray,-1,rdk_xml_document_data.Array(),wLen+1,&error_status,0,0,0);
05435         if ( wLen > 0 && 0 == error_status )
05436           rdk_xml_document_data.SetLength(wLen);
05437         else
05438         {
05439           rdk_xml_document_data.SetLength(0);
05440         }
05441       }
05442       if ( 0 != error_status )
05443       {
05444         ON_ERROR("RDK xml document settings is not a valid UTF-8 string.");
05445       }
05446     }
05447   }
05448 
05449   return rdk_xml_document_data.Length() > 0;
05450 }
05451 
05452 #if defined(ON_COMPILER_MSC)
05453 #pragma warning( pop )
05454 #endif


pcl
Author(s): Open Perception
autogenerated on Wed Aug 26 2015 15:27:01