opennurbs_mesh.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 
00017 #include "pcl/surface/3rdparty/opennurbs/opennurbs.h"
00018 
00019 
00020 // NEVER COPY OR MOVE THE NEXT 2 LINES
00021 #define ON_BOZO_VACCINE_17F24E7521BE4a7b9F3D7F85225247E3
00022 #define ON_BOZO_VACCINE_B5628CA982C44CAE9883487B3E4AB28B
00023 // NEVER COPY OR MOVE THE PREVIOUS 2 LINES
00024 
00025 
00026 
00027 
00029 //
00030 // Double precision vertices user data
00031 //
00032 
00033 class /* DO NOT copy, move, or export this class */ ON_MeshDoubleVertices : public ON_UserData
00034 {
00035   ON_OBJECT_DECLARE(ON_MeshDoubleVertices);
00036 
00037 public:
00038   ON_MeshDoubleVertices();
00039   ~ON_MeshDoubleVertices();  
00040 
00041   // default copy constructor and operator= work fine.
00042 
00043   /*
00044     If the mesh has ON_MeshDoubleVertices user data, then return
00045     a pointer to it.
00046   */
00047   static ON_MeshDoubleVertices* Get(const ON_Mesh* mesh);
00048 
00049   /*
00050     Attach new ON_MeshDoubleVertices user data to the mesh.
00051     This will fail and return NULL if the mesh already has
00052     ON_MeshDoubleVertices user data.
00053   */
00054   static ON_MeshDoubleVertices* Attach(const ON_Mesh* mesh);
00055 
00056   // virtual ON_Object overrides
00057   ON_BOOL32 IsValid( ON_TextLog* = NULL ) const;
00058   void Dump( ON_TextLog& ) const;
00059   unsigned int SizeOf() const;
00060   ON__UINT32 DataCRC(ON__UINT32) const;
00061   ON_BOOL32 Write(ON_BinaryArchive&) const;
00062   ON_BOOL32 Read(ON_BinaryArchive&);
00063 
00064   // virtual ON_UserData overrides
00065   ON_BOOL32 GetDescription( ON_wString& );
00066   ON_BOOL32 Archive() const; 
00067   ON_BOOL32 Transform( const ON_Xform& ); 
00068 
00069 #if !defined(ON_BOZO_VACCINE_17F24E7521BE4a7b9F3D7F85225247E3)
00070 #error DO NOT copy, move or export the definition of ON_MeshDoubleVertices
00071 #endif
00072 #undef ON_BOZO_VACCINE_17F24E7521BE4a7b9F3D7F85225247E3
00073 
00074   ON__UINT32 DoubleCRC() const;
00075   static ON__UINT32 FloatCRC( const ON_3fPointArray& );
00076 
00077   // If m_dV.Count() != m_dcount or
00078   // m_dCRC != ON_CRC32(0,m_dV.Count()*sizeof(ON_3dPoint),m_dV.Array()),
00079   // then somebody has changed m_dV and not called
00080   // SetDoublePrecisionVerticesAsValid() to mark the
00081   // values as valid.
00082   //
00083   // If ON_Mesh.M_V.Count() != m_fcount or
00084   // m_fCRC != ON_CRC32(0,m_V.Count()*sizeof(ON_3fPoint),m_V.Array()),
00085   // then somebody has changed ON_Mesh.m_V and not called
00086   // SetSinglePrecisionVerticesAsValid() to mark the
00087   // values as valid.
00088   //
00089   // Whenever there is a question about which values are valid,
00090   // it is assumed the m_V array is valid and the double precision
00091   // informtion should be destroyed.
00092   int m_fcount;  // single precision vertex count
00093   int m_dcount;  // double precision vertex count
00094   ON__UINT32 m_fCRC; // crc of float vertex array
00095   ON__UINT32 m_dCRC; // crc of double vertex array
00096 
00097   ON_3dPointArray m_dV; // double precision mesh vertices
00098 };
00099 
00100 static const ON_3dPoint* Mesh_dV(const ON_Mesh& mesh)
00101 {
00102   if ( mesh.HasDoublePrecisionVertices() && mesh.DoublePrecisionVerticesAreValid() )
00103   {
00104     const ON_3dPointArray& a = mesh.DoublePrecisionVertices();
00105     if ( a.Count() == mesh.m_V.Count() )
00106       return a.Array();
00107   }
00108   return 0;
00109 }
00110 
00111 ON_MeshCurveParameters::ON_MeshCurveParameters()
00112 {
00113   memset(this,0,sizeof(*this));
00114 }
00115 
00116 ON_OBJECT_IMPLEMENT(ON_Mesh,ON_Geometry,"4ED7D4E4-E947-11d3-BFE5-0010830122F0");
00117 
00118 /*
00119 ON_MeshEdge& ON_MeshEdge::operator=( const ON_MeshEdge& src )
00120 {
00121   int fc = src.m_fcount+(src.m_fcount%1);
00122   if ( fc <= 2 ) {
00123     if ( m_fi && m_fi != m_private_fi ) onfree((void*)m_fi);
00124     m_fi = m_private_fi;
00125     fc = 2;
00126   }
00127   else {
00128     if ( (m_fcount+(m_fcount%1)) != fc ) {
00129       if ( m_fi == m_private_fi )
00130         m_fi = 0;
00131       m_fi = (int*)onrealloc(m_fi,fc*sizeof(*m_fi));
00132     }
00133   }
00134   memcpy(m_fi,src.m_fi,fc*sizeof(*m_fi));
00135   m_fcount=src.m_fcount;
00136   return *this;
00137 }
00138 
00139 void ON_MeshEdge::AppendFaceIndex(int face_index)
00140 {
00141   if ( m_fcount>0 && !(m_fcount%1) ) {
00142     if ( m_fi == m_private_fi ) {
00143       m_fi = (int*)onmalloc((m_fcount+2)*sizeof(*m_fi));
00144       m_fi[0] = m_private_fi[0];
00145       m_fi[1] = m_private_fi[1];
00146     }
00147     else {
00148       m_fi = (int*)onrealloc(m_fi,(m_fcount+2)*sizeof(*m_fi));
00149     }
00150   }
00151   m_fi[m_fcount++] = face_index;
00152 }
00153 */
00154 
00155 bool
00156 ON_MeshFace::IsValid(int mesh_vertex_count) const
00157 {
00158   return (    vi[0] >= 0 && vi[0] < mesh_vertex_count
00159            && vi[1] >= 0 && vi[1] < mesh_vertex_count
00160            && vi[2] >= 0 && vi[2] < mesh_vertex_count
00161            && vi[3] >= 0 && vi[3] < mesh_vertex_count
00162            && vi[0] != vi[1] && vi[1] != vi[2] && vi[2] != vi[0] 
00163            && (vi[2]==vi[3]||(vi[0] != vi[3] && vi[1] != vi[3])) );
00164 }
00165 
00166 
00167 bool
00168 ON_MeshFace::IsValid(int mesh_vertex_count, const ON_3fPoint* V ) const
00169 {
00170   if ( !IsValid(mesh_vertex_count) )
00171     return false;
00172   if ( !(V[vi[0]] != V[vi[1]]) )
00173     return false;
00174   if ( !(V[vi[0]] != V[vi[2]]) )
00175     return false;
00176   if ( !(V[vi[1]] != V[vi[2]]) )
00177     return false;
00178   if ( vi[2] != vi[3] )
00179   {
00180   if ( !(V[vi[0]] != V[vi[3]]) )
00181     return false;
00182   if ( !(V[vi[1]] != V[vi[3]]) )
00183     return false;
00184   if ( !(V[vi[2]] != V[vi[3]]) )
00185     return false;
00186   }
00187   return true;
00188 }
00189 
00190 
00191 bool
00192 ON_MeshFace::IsValid(int mesh_vertex_count, const ON_3dPoint* V ) const
00193 {
00194   if ( !IsValid(mesh_vertex_count) )
00195     return false;
00196   if ( !(V[vi[0]] != V[vi[1]]) )
00197     return false;
00198   if ( !(V[vi[0]] != V[vi[2]]) )
00199     return false;
00200   if ( !(V[vi[1]] != V[vi[2]]) )
00201     return false;
00202   if ( vi[2] != vi[3] )
00203   {
00204   if ( !(V[vi[0]] != V[vi[3]]) )
00205     return false;
00206   if ( !(V[vi[1]] != V[vi[3]]) )
00207     return false;
00208   if ( !(V[vi[2]] != V[vi[3]]) )
00209     return false;
00210   }
00211   return true;
00212 }
00213 
00214 
00215 bool ON_MeshFace::Repair(
00216   int mesh_vertex_count
00217   )
00218 {
00219   ON_MeshFace f;
00220   int fvi_count = 0;
00221   f.vi[0] = f.vi[1] = f.vi[2] = f.vi[3] = -1;
00222   
00223   if ( vi[0] >= 0 && vi[0] < mesh_vertex_count )
00224     f.vi[fvi_count++] = vi[0];
00225 
00226   if ( vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] )
00227     f.vi[fvi_count++] = vi[1];
00228 
00229   if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] )
00230     f.vi[fvi_count++] = vi[2];
00231 
00232   if ( vi[3] >= 0 && vi[3] < mesh_vertex_count && f.vi[0] != vi[3] && f.vi[1] != vi[3] && f.vi[2] != vi[3] )
00233     f.vi[fvi_count++] = vi[3];
00234   
00235   if ( fvi_count < 3 )
00236     return false;
00237  
00238   if ( 3 == fvi_count )
00239     f.vi[3] = f.vi[2];
00240   
00241   if ( !f.IsValid(mesh_vertex_count) )
00242     return false;
00243 
00244   vi[0] = f.vi[0];
00245   vi[1] = f.vi[1];
00246   vi[2] = f.vi[2];
00247   vi[3] = f.vi[3];
00248 
00249   return true;
00250 }
00251 
00252 bool ON_MeshFace::Repair(
00253   int mesh_vertex_count,
00254   const ON_3fPoint* V
00255   )
00256 {
00257   ON_MeshFace f;
00258   int fvi_count = 0;
00259   f.vi[0] = f.vi[1] = f.vi[2] = f.vi[3] = -1;
00260   
00261   if ( vi[0] >= 0 && vi[0] < mesh_vertex_count )
00262     f.vi[fvi_count++] = vi[0];
00263 
00264   if ( vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] )
00265   {
00266     if ( 0 == fvi_count || V[f.vi[0]] != V[vi[1]] )
00267       f.vi[fvi_count++] = vi[1];
00268   }
00269 
00270   if ( fvi_count < 1 )
00271     return false;
00272 
00273   if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] && V[f.vi[0]] != V[vi[2]] )
00274   {
00275     if ( 1 == fvi_count || V[f.vi[1]] != V[vi[2]] )
00276       f.vi[fvi_count++] = vi[2];
00277   }
00278 
00279   if ( fvi_count < 2 )
00280     return false;
00281 
00282   if ( vi[3] >= 0 && vi[3] < mesh_vertex_count && f.vi[0] != vi[3] && f.vi[1] != vi[3] && f.vi[2] != vi[3] && V[f.vi[0]] != V[vi[3]] && V[f.vi[1]] != V[vi[3]] )
00283   {
00284     if ( 2 == fvi_count || V[f.vi[2]] != V[vi[3]] )
00285       f.vi[fvi_count++] = vi[3];
00286   }
00287   
00288   if ( fvi_count < 3 )
00289     return false;
00290  
00291   if ( 3 == fvi_count )
00292     f.vi[3] = f.vi[2];
00293   
00294   if ( !f.IsValid(mesh_vertex_count) )
00295     return false;
00296 
00297   vi[0] = f.vi[0];
00298   vi[1] = f.vi[1];
00299   vi[2] = f.vi[2];
00300   vi[3] = f.vi[3];
00301 
00302   return true;
00303 }
00304 
00305 
00306 bool ON_MeshFace::Repair(
00307   int mesh_vertex_count,
00308   const ON_3dPoint* V
00309   )
00310 {
00311   ON_MeshFace f;
00312   int fvi_count = 0;
00313   f.vi[0] = f.vi[1] = f.vi[2] = f.vi[3] = -1;
00314   
00315   if ( vi[0] >= 0 && vi[0] < mesh_vertex_count )
00316     f.vi[fvi_count++] = vi[0];
00317 
00318   if ( vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] )
00319   {
00320     if ( 0 == fvi_count || V[f.vi[0]] != V[vi[1]] )
00321       f.vi[fvi_count++] = vi[1];
00322   }
00323 
00324   if ( fvi_count < 1 )
00325     return false;
00326 
00327   if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] && V[f.vi[0]] != V[vi[2]] )
00328   {
00329     if ( 1 == fvi_count || V[f.vi[1]] != V[vi[2]] )
00330       f.vi[fvi_count++] = vi[2];
00331   }
00332 
00333   if ( fvi_count < 2 )
00334     return false;
00335 
00336   if ( vi[3] >= 0 && vi[3] < mesh_vertex_count && f.vi[0] != vi[3] && f.vi[1] != vi[3] && f.vi[2] != vi[3] && V[f.vi[0]] != V[vi[3]] && V[f.vi[1]] != V[vi[3]] )
00337   {
00338     if ( 2 == fvi_count || V[f.vi[2]] != V[vi[3]] )
00339       f.vi[fvi_count++] = vi[3];
00340   }
00341   
00342   if ( fvi_count < 3 )
00343     return false;
00344  
00345   if ( 3 == fvi_count )
00346     f.vi[3] = f.vi[2];
00347   
00348   if ( !f.IsValid(mesh_vertex_count) )
00349     return false;
00350 
00351   vi[0] = f.vi[0];
00352   vi[1] = f.vi[1];
00353   vi[2] = f.vi[2];
00354   vi[3] = f.vi[3];
00355 
00356   return true;
00357 }
00358 
00359 ON_Mesh::ON_Mesh() 
00360 : m_packed_tex_rotate(0)
00361 , m_parent(0)
00362 , m_mesh_parameters(0) 
00363 , m_invalid_count(0) 
00364 , m_quad_count(0) 
00365 , m_triangle_count(0)
00366 , m_mesh_is_closed(0)
00367 , m_mesh_is_manifold(0)
00368 , m_mesh_is_oriented(0)
00369 , m_mesh_is_solid(0)
00370 {
00371   m_top.m_mesh = this;
00372   m_srf_scale[0] = 0.0;
00373   m_srf_scale[1] = 0.0;
00374   m_kstat[0] = 0;
00375   m_kstat[1] = 0;
00376   m_kstat[2] = 0;
00377   m_kstat[3] = 0;
00378   InvalidateBoundingBoxes();
00379   m_partition = 0;
00380   m_hidden_count = 0;
00381 }
00382 
00383 
00384 ON_Mesh::ON_Mesh(
00385     int initial_facet_capacity,  // initial facet array capacity
00386     int initial_vertex_capacity,  // initial vertex array capacity
00387     bool bHasVertexNormals, // true if mesh has unit vertex normals
00388     bool bHasTextureCoordinates // true if mesh has texture coordinates
00389     )
00390 : m_V(initial_vertex_capacity)
00391 , m_F(initial_facet_capacity)
00392 , m_N(bHasVertexNormals?initial_vertex_capacity:0)
00393 , m_T(bHasTextureCoordinates?initial_vertex_capacity:0)
00394 , m_packed_tex_rotate(0)
00395 , m_parent(0) 
00396 , m_mesh_parameters(0)
00397 , m_invalid_count(0) 
00398 , m_quad_count(0) 
00399 , m_triangle_count(0)
00400 , m_mesh_is_closed(0)
00401 , m_mesh_is_manifold(0)
00402 , m_mesh_is_oriented(0)
00403 , m_mesh_is_solid(0)
00404 {
00405   m_top.m_mesh = this;
00406   m_srf_scale[0] = 0.0;
00407   m_srf_scale[1] = 0.0;
00408   m_kstat[0] = 0;
00409   m_kstat[1] = 0;
00410   m_kstat[2] = 0;
00411   m_kstat[3] = 0;
00412   InvalidateBoundingBoxes();
00413   m_partition = 0;
00414   m_hidden_count = 0;
00415 }
00416 
00417 ON_Mesh::ON_Mesh( const ON_Mesh& src ) 
00418 : m_packed_tex_rotate(0)
00419 , m_parent(0) 
00420 , m_mesh_parameters(0) 
00421 , m_invalid_count(0) 
00422 , m_quad_count(0) 
00423 , m_triangle_count(0)
00424 , m_mesh_is_closed(0)
00425 , m_mesh_is_manifold(0)
00426 , m_mesh_is_oriented(0)
00427 , m_mesh_is_solid(0)
00428 {
00429   // Do not copy m_mesh_cache. Cached information will
00430   // be recalculated if it is needed.
00431   m_top.m_mesh = this;
00432   m_srf_scale[0] = 0.0;
00433   m_srf_scale[1] = 0.0;
00434 
00435   m_kstat[0] = 0;
00436   m_kstat[1] = 0;
00437   m_kstat[2] = 0;
00438   m_kstat[3] = 0;
00439   InvalidateBoundingBoxes();
00440   m_partition = 0;
00441   m_hidden_count = 0;
00442   ON_Mesh::operator=(src);
00443 }
00444 
00445 
00446 unsigned int ON_Mesh::SizeOf() const
00447 {
00448   unsigned int sz = ON_Geometry::SizeOf();
00449   sz += m_V.SizeOfArray();
00450   sz += m_F.SizeOfArray();
00451   sz += m_N.SizeOfArray();
00452   sz += m_FN.SizeOfArray();
00453   sz += m_T.SizeOfArray();
00454   sz += m_S.SizeOfArray();
00455   sz += m_K.SizeOfArray();
00456   sz += m_C.SizeOfArray();  
00457   sz += m_top.m_topv_map.SizeOfArray();
00458   sz += m_top.m_topv.SizeOfArray();
00459   sz += m_top.m_tope.SizeOfArray();
00460   sz += m_top.m_topf.SizeOfArray();
00461   return sz;
00462 }
00463 
00464 ON__UINT32 ON_Mesh::DataCRC(ON__UINT32 current_remainder) const
00465 {
00466   const ON_3fPoint* p = m_V.Array();
00467   current_remainder = ON_CRC32(current_remainder,m_V.Count()*sizeof(p[0]),p);
00468   current_remainder = ON_CRC32(current_remainder,m_F.Count()*sizeof(ON_MeshFace),m_F.Array());
00469   const ON_3fVector* v = m_N.Array();
00470   current_remainder = ON_CRC32(current_remainder,m_N.Count()*sizeof(v[0]),v);
00471   return current_remainder;
00472 }
00473 
00474 ON_Mesh& ON_Mesh::operator=( const ON_Mesh& src )
00475 {
00476   if ( this != &src ) 
00477   {
00478     Destroy();
00479     ON_Geometry::operator=(src);
00480 
00481     m_V  = src.m_V;
00482     m_F  = src.m_F;
00483     m_N  = src.m_N;
00484     m_FN = src.m_FN;
00485     m_T  = src.m_T;
00486     m_TC = src.m_TC;
00487     m_S  = src.m_S;
00488     m_H  = src.m_H;
00489     m_hidden_count = src.m_hidden_count;
00490 
00491     m_Ctag = src.m_Ctag;
00492     m_Ttag = src.m_Ttag;
00493     m_packed_tex_domain[0] = src.m_packed_tex_domain[0];
00494     m_packed_tex_domain[1] = src.m_packed_tex_domain[1];
00495     m_srf_domain[0] = src.m_srf_domain[0];
00496     m_srf_domain[1] = src.m_srf_domain[1];
00497     m_srf_scale[0] = src.m_srf_scale[0];
00498     m_srf_scale[1] = src.m_srf_scale[1];
00499     m_packed_tex_rotate = src.m_packed_tex_rotate;
00500 
00501     m_K = src.m_K;
00502     m_C = src.m_C;
00503 
00504     m_parent         = src.m_parent;
00505     //m_material_index = src.m_material_index;
00506 
00507     if ( m_mesh_parameters ) {
00508       delete m_mesh_parameters;
00509       m_mesh_parameters = 0;
00510     }
00511     if ( src.m_mesh_parameters )
00512       m_mesh_parameters = new ON_MeshParameters(*src.m_mesh_parameters);
00513 
00514     m_invalid_count = src.m_invalid_count;
00515     m_quad_count = src.m_quad_count;
00516     m_triangle_count = src.m_triangle_count;
00517 
00518     m_mesh_is_closed = src.m_mesh_is_closed;
00519     m_mesh_is_manifold = src.m_mesh_is_manifold;
00520     m_mesh_is_oriented = src.m_mesh_is_oriented;
00521     m_mesh_is_solid    = src.m_mesh_is_solid;
00522 
00523     memcpy(m_vbox,src.m_vbox,sizeof(m_vbox));
00524     memcpy(m_nbox,src.m_nbox,sizeof(m_nbox));
00525     memcpy(m_tbox,src.m_tbox,sizeof(m_tbox));
00526 
00527     int i;
00528     for ( i = 0; i < 4; i++ ) {
00529       if ( m_kstat[i] ) 
00530       {
00531         delete m_kstat[i];
00532         m_kstat[i] = 0;
00533       }
00534       if ( src.m_kstat[i] )
00535       {
00536         m_kstat[i] = new ON_MeshCurvatureStats(*src.m_kstat[i]);
00537       }
00538     }
00539 
00540     // do not copy m_top
00541 
00542     // Do not copy m_mesh_cache. Cached information will
00543     // be recalculated if it is needed.
00544   }
00545   return *this;
00546 }
00547 
00548 ON_Mesh::~ON_Mesh()
00549 {
00550   Destroy();
00551   m_top.m_mesh = 0;
00552 }
00553 
00554 void ON_Mesh::MemoryRelocate()
00555 {
00556   // the back pointer on m_top needs to be updated.
00557   m_top.m_mesh = this;
00558 }
00559 
00560 void ON_Mesh::Destroy()
00561 {
00562   PurgeUserData();
00563   DestroyRuntimeCache( true );
00564   m_Ttag.Default();
00565   m_Ctag.Default();
00566   m_V.Destroy();
00567   m_F.Destroy();
00568   m_N.Destroy();
00569   m_FN.Destroy();
00570   m_T.Destroy();
00571   m_TC.Destroy();
00572   m_S.Destroy();
00573   m_K.Destroy();
00574   m_C.Destroy();
00575 }
00576 
00577 void ON_Mesh::EmergencyDestroy()
00578 {
00579   DestroyRuntimeCache( false );
00580   m_V.EmergencyDestroy();
00581   m_F.EmergencyDestroy();
00582   m_N.EmergencyDestroy();
00583   m_FN.EmergencyDestroy();
00584   m_T.EmergencyDestroy();
00585   m_TC.EmergencyDestroy();
00586   m_S.EmergencyDestroy();
00587   m_K.EmergencyDestroy();
00588   m_C.EmergencyDestroy();
00589 }
00590 
00591 static bool ON_MeshIsNotValid(bool bSilentError)
00592 {
00593   return bSilentError ? false : ON_IsNotValid(); // good place for a breakpoint;
00594 }
00595 
00596 ON_BOOL32 ON_Mesh::IsValid( ON_TextLog* text_logx ) const
00597 {
00598   // If low bit of text_log pointer is 1, then ON_Error is not called when the
00599   // knot vector is invalid.
00600   const ON__INT_PTR lowbit = 1;
00601   const ON__INT_PTR hightbits = ~lowbit;
00602   bool bSilentError = ( 0 != (lowbit & ((ON__INT_PTR)text_logx)) );
00603   ON_TextLog* text_log = (ON_TextLog*)(((ON__INT_PTR)text_logx) & hightbits);
00604 
00605   const int facet_count = FaceCount();
00606   const int vertex_count = VertexCount();
00607   int fi, vi;
00608 
00609   if (facet_count < 1) 
00610   {
00611     if ( text_log )
00612     {
00613       text_log->Print("ON_Mesh.m_F.Count() < 1 (should be at least 1).\n");
00614     }
00615     return ON_MeshIsNotValid(bSilentError);
00616   }
00617 
00618   if ( vertex_count < 3 )
00619   {
00620     if ( text_log )
00621     {
00622       text_log->Print("ON_Mesh.m_V.Count() < 3 (should be at least 3).\n");
00623     }
00624     return ON_MeshIsNotValid(bSilentError);
00625   }
00626 
00627   if ( m_N.Count() > 0 && m_N.Count() != vertex_count )
00628   {
00629     if ( text_log )
00630     {
00631       text_log->Print("ON_Mesh.m_N.Count() = %d (should be 0 or %d=vertex_count).\n",
00632                       m_N.Count(),vertex_count);
00633     }
00634     return ON_MeshIsNotValid(bSilentError);
00635   }
00636 
00637   if ( m_T.Count() > 0 && m_T.Count() != vertex_count )
00638   {
00639     if ( text_log )
00640     {
00641       text_log->Print("ON_Mesh.m_T.Count() = %d (should be 0 or %d=vertex_count).\n",
00642                       m_T.Count(),vertex_count);
00643     }
00644     return ON_MeshIsNotValid(bSilentError);
00645   }
00646 
00647   if ( m_S.Count() > 0 && m_S.Count() != vertex_count )
00648   {
00649     if ( text_log )
00650     {
00651       text_log->Print("ON_Mesh.m_S.Count() = %d (should be 0 or %d=vertex_count).\n",
00652                       m_S.Count(),vertex_count);
00653     }
00654     return ON_MeshIsNotValid(bSilentError);
00655   }
00656 
00657   if ( HasVertexNormals() ) 
00658   {
00659     float x;
00660     for ( vi = 0; vi < vertex_count; vi++ ) {
00661       x = m_N[vi][0]*m_N[vi][0] + m_N[vi][1]*m_N[vi][1] + m_N[vi][2]*m_N[vi][2];
00662       if ( x < 0.985 || x > 1.015 )
00663       {
00664         if ( text_log )
00665         {
00666           text_log->Print("ON_Mesh.m_N[%d] is not a unit vector (length = %g).\n",vi,sqrt(x));
00667         }
00668         return ON_MeshIsNotValid(bSilentError);
00669       }
00670     }
00671   }
00672 
00673         // Greg Arden 9 May 2003. Fixes TRR#10604.  Attempt to detect meshes with non-finite vertices
00674         // by testing the bounding box.
00675   int i;
00676   for ( i = 0; i < 3; i++ )
00677   {
00678     if ( !ON_IsValid( m_vbox[0][i] ) || !ON_IsValid( m_vbox[1][i] ) )
00679     {
00680       if ( text_log )
00681       {
00682         text_log->Print("ON_Mesh.m_vbox is not finite.  Check for invalid vertices\n");
00683       }         
00684             return ON_MeshIsNotValid(bSilentError);
00685     }   
00686   }
00687 
00688   const ON_3dPoint* dV = 0;
00689   while ( HasDoublePrecisionVertices() )
00690   {
00691     bool bValidDoubles = DoublePrecisionVerticesAreValid();
00692     if ( bValidDoubles )
00693       dV = DoublePrecisionVertices().Array();
00694     bool bValidFloats  = SinglePrecisionVerticesAreValid();
00695     bool bSynchronized = HasSynchronizedDoubleAndSinglePrecisionVertices();
00696     if ( bSynchronized && bValidDoubles && bValidFloats )
00697       break;
00698 
00699     if ( !bSynchronized )
00700     {
00701       if ( text_log )
00702       {
00703         text_log->Print("Single and double precision vertices are not synchronized.\n");
00704       }         
00705             return ON_MeshIsNotValid(bSilentError);
00706     }
00707 
00708     if ( !bValidDoubles )
00709     {
00710       if ( text_log )
00711       {
00712         text_log->Print("Double precision vertices appear to be ok but are not marked as valid\n");
00713       }         
00714             return ON_MeshIsNotValid(bSilentError);
00715     }
00716 
00717     if ( !bValidFloats )
00718     {
00719       if ( text_log )
00720       {
00721         text_log->Print("Single precision vertices appear to be ok but are not marked as valid\n");
00722       }         
00723             return ON_MeshIsNotValid(bSilentError);
00724     }
00725 
00726 
00727     break;
00728   }
00729 
00730   if ( 0 != dV )
00731   {
00732     for ( fi = 0; fi < facet_count; fi++ ) 
00733     {
00734       if ( !m_F[fi].IsValid( vertex_count, dV ) ) 
00735       {
00736         if ( text_log )
00737         {
00738           if ( !m_F[fi].IsValid( vertex_count) )
00739             text_log->Print("ON_Mesh.m_F[%d].vi[] has invalid vertex indices.\n",fi);
00740           else
00741             text_log->Print("ON_Mesh.m_F[%d] has degenerate double precision vertex locations.\n",fi);
00742         }
00743         return ON_MeshIsNotValid(bSilentError);
00744       }
00745     }
00746   }
00747   else
00748   {
00749     //const ON_3fPoint* fV = m_V.Array();
00750     for ( fi = 0; fi < facet_count; fi++ ) 
00751     {
00752       // This test was too harsh for float precision meshes
00753       // with nearly degnerate faces after they are transformed
00754       // by a transform with a reasonable sized translation 
00755       // component.
00756       // See bug http://dev.mcneel.com/bugtrack/?q=87465
00757 
00758       //if ( !m_F[fi].IsValid( vertex_count, fV ) ) 
00759       //{
00760       //  if ( text_log )
00761       //  {
00762       //    if ( !m_F[fi].IsValid( vertex_count) )
00763       //      text_log->Print("ON_Mesh.m_F[%d].vi[] has invalid vertex indices.\n",fi);
00764       //    else
00765       //      text_log->Print("ON_Mesh.m_F[%d] has degenerate float precision vertex locations.\n",fi);
00766       //  }
00767       //  return ON_MeshIsNotValid(bSilentError);
00768       //}
00769 
00770       if ( !m_F[fi].IsValid( vertex_count ) ) 
00771       {
00772         if ( text_log )
00773           text_log->Print("ON_Mesh.m_F[%d].vi[] has invalid vertex indices.\n",fi);
00774         return ON_MeshIsNotValid(bSilentError);
00775       }
00776     }
00777   }
00778 
00779 
00780   return true;
00781 }
00782 
00783 void ON_Mesh::Dump( ON_TextLog& dump ) const
00784 {
00785   const int half_max = 8;
00786 
00787   const int fcount = m_F.Count();
00788   int i;
00789   const int vcount = m_V.Count();
00790   ON_3dPoint p, q;
00791 
00792   bool bDoubles =    vcount > 0 
00793                   && HasDoublePrecisionVertices()
00794                   && HasSynchronizedDoubleAndSinglePrecisionVertices();
00795 
00796   dump.Print("ON_Mesh: vertex count = %d  facet count = %d\n", m_V.Count(), m_F.Count() );
00797   dump.Print("double precision: %s\n",bDoubles?"true":"false");
00798   dump.Print("vertex normals:   %s\n",HasVertexNormals()?"true":"false");
00799   dump.Print("face normals:     %s\n",HasFaceNormals()?"true":"false");
00800   dump.Print("srf parameters:   %s\n",HasSurfaceParameters()?"true":"false");
00801   dump.Print("tex coords:       %s\n",HasTextureCoordinates()?"true":"false");
00802   dump.Print("vertex kappa:     %s\n",HasPrincipalCurvatures()?"true":"false");
00803   dump.Print("vertex colors:    %s\n",HasVertexColors()?"true":"false");
00804   dump.Print("m_Ctag:\n"); dump.PushIndent(); m_Ctag.Dump(dump); dump.PopIndent();
00805   dump.Print("m_packed_tex_rotate: %s\n",m_packed_tex_rotate?"true":"false");
00806   dump.Print("m_packed_tex_domain: (%g,%g)x(%g,%g)\n",
00807              m_packed_tex_domain[0][0],m_packed_tex_domain[0][1],
00808              m_packed_tex_domain[1][0],m_packed_tex_domain[1][1]);
00809   dump.Print("m_srf_domain: (%g,%g)x(%g,%g)\n",m_srf_domain[0][0],m_srf_domain[0][1],m_srf_domain[1][0],m_srf_domain[1][1]);
00810   dump.Print("m_srf_scale: %g,%g\n",m_srf_scale[0],m_srf_scale[0]);
00811   dump.Print("m_Ttag:\n"); dump.PushIndent(); m_Ttag.Dump(dump); dump.PopIndent();
00812 
00813   dump.PushIndent();
00814 
00815   dump.Print("%d mesh vertices:\n",m_V.Count());
00816   {
00817     dump.PushIndent();
00818     const ON_3dPoint* D = 0;
00819     if ( bDoubles )
00820     {
00821       D = DoublePrecisionVertices().Array();
00822     }
00823     for (i = 0; i < vcount; i++)
00824     {
00825       if ( i == half_max && 2*half_max < vcount )
00826       {
00827         dump.Print("...\n");
00828         i = vcount-half_max;
00829       }
00830       else
00831       {
00832         p = m_V[i];
00833         if ( 0 != D )
00834         {
00835           q = D[i];
00836           dump.Print("m_V[%d] = (%.17g,%.17g,%.17g) D = (%.17g,%.17g,%.17g)\n",
00837                      i,
00838                      p.x,p.y,p.z,
00839                      q.x,q.y,q.z
00840                      );
00841         }
00842         else
00843         {
00844           dump.Print("m_V[%d] = (%g,%g,%g)\n",i,p.x,p.y,p.z);
00845         }
00846       }
00847     }
00848     dump.PopIndent();
00849   }
00850 
00851   if ( HasVertexNormals() )
00852   {
00853     dump.Print("%d mesh vertex normals:\n",m_N.Count());
00854     {
00855       dump.PushIndent();
00856       for (i = 0; i < vcount; i++)
00857       {
00858         if ( i == half_max && 2*half_max < vcount )
00859         {
00860           dump.Print("...\n");
00861           i = vcount-half_max;
00862         }
00863         else
00864         {
00865           p = m_N[i];
00866           dump.Print("m_N[%d] = (%g,%g,%g)\n",i,p.x,p.y,p.z);
00867         }
00868       }
00869       dump.PopIndent();
00870     }
00871   }
00872 
00873   if ( HasTextureCoordinates() )
00874   {
00875     dump.Print("%d mesh vertex texture coordinates:\n",m_T.Count());
00876     {
00877       dump.PushIndent();
00878       for (i = 0; i < vcount; i++)
00879       {
00880         if ( i == half_max && 2*half_max < vcount )
00881         {
00882           dump.Print("...\n");
00883           i = vcount-half_max;
00884         }
00885         else
00886         {
00887           ON_2fPoint tp = m_T[i];
00888           p.x = tp.x;
00889           p.y = tp.y;
00890           dump.Print("m_T[%d] = (%g,%g)\n",i,p.x,p.y);
00891         }
00892       }
00893       dump.PopIndent();
00894     }
00895   }
00896 
00897 
00898   if ( HasSurfaceParameters() )
00899   {
00900     dump.Print("%d mesh vertex surface parameters:\n",m_S.Count());
00901     {
00902       dump.PushIndent();
00903       for (i = 0; i < vcount; i++)
00904       {
00905         if ( i == half_max && 2*half_max < vcount )
00906         {
00907           dump.Print("...\n");
00908           i = vcount-half_max;
00909         }
00910         else
00911         {
00912           ON_2dPoint srfuv = m_S[i];
00913           dump.Print("m_S[%d] = (%g,%g)\n",i,srfuv.x,srfuv.y);
00914         }
00915       }
00916       dump.PopIndent();
00917     }
00918   }
00919 
00920   dump.Print("%d mesh faces:\n",m_F.Count());
00921   {
00922     dump.PushIndent();
00923     for (i = 0; i < fcount; i++)
00924     {
00925       if ( i == half_max && 2*half_max < fcount )
00926       {
00927         dump.Print("...\n");
00928         i = fcount-half_max;
00929       }
00930       else if ( m_F[i].vi[2] == m_F[i].vi[3] )
00931         dump.Print("m_F[%d].vi = (%d,%d,%d)\n",i,m_F[i].vi[0],m_F[i].vi[1],m_F[i].vi[2]);
00932       else
00933         dump.Print("m_F[%d].vi = (%d,%d,%d,%d)\n",i,m_F[i].vi[0],m_F[i].vi[1],m_F[i].vi[2],m_F[i].vi[3]);
00934     }
00935     dump.PopIndent();
00936   }
00937 
00938   if ( HasFaceNormals() )
00939   {
00940     dump.Print("%d mesh face normals:\n",m_FN.Count());
00941     {
00942       dump.PushIndent();
00943       for (i = 0; i < fcount; i++)
00944       {
00945         if ( i == half_max && 2*half_max < fcount )
00946         {
00947           dump.Print("...\n");
00948           i = fcount-half_max;
00949         }
00950         else
00951         {
00952           p = m_FN[i];
00953           dump.Print("m_FN[%d] = (%g,%g,%g)\n",i,p.x,p.y,p.z);
00954         }
00955       }
00956       dump.PopIndent();
00957     }
00958   }
00959 
00960 
00961   dump.PopIndent();
00962 }
00963 
00964 
00965 bool ON_Mesh::WriteFaceArray( int vcount, int fcount, ON_BinaryArchive& file ) const
00966 {
00967   unsigned char  cvi[4];
00968   unsigned short svi[4];
00969   const int* vi;
00970   int i_size = 0;
00971   if ( vcount < 256 ) {
00972     i_size = 1; // unsigned chars
00973   }
00974   else if (vcount < 65536 ) {
00975     i_size = 2; // unsigned shorts
00976   }
00977   else {
00978     i_size = 4; // 4 byte ints
00979   }
00980 
00981   bool rc = file.WriteInt( i_size );
00982   int i;
00983   switch(i_size) {
00984   case 1:
00985     for ( i = 0; i < fcount && rc ; i++ ) {
00986       vi = m_F[i].vi;
00987       cvi[0] = (unsigned char)vi[0];
00988       cvi[1] = (unsigned char)vi[1];
00989       cvi[2] = (unsigned char)vi[2];
00990       cvi[3] = (unsigned char)vi[3];
00991       rc = file.WriteChar( 4, cvi );
00992     }
00993     break;
00994   case 2:
00995     for ( i = 0; i < fcount && rc ; i++ ) {
00996       vi = m_F[i].vi;
00997       svi[0] = (unsigned short)vi[0];
00998       svi[1] = (unsigned short)vi[1];
00999       svi[2] = (unsigned short)vi[2];
01000       svi[3] = (unsigned short)vi[3];
01001       rc = file.WriteShort( 4, svi );
01002     }
01003     break;
01004   case 4:
01005     for ( i = 0; i < fcount && rc ; i++ ) {
01006       rc = file.WriteInt( 4, m_F[i].vi );
01007     }
01008     break;
01009   }
01010 
01011   return rc;
01012 }
01013 
01014 bool ON_Mesh::ReadFaceArray( int vcount, int fcount, ON_BinaryArchive& file )
01015 {
01016   unsigned char  cvi[4];
01017   unsigned short svi[4];
01018   unsigned int* vi;
01019   int i_size = 0;
01020 
01021   if ( m_F.Capacity() < fcount )
01022     m_F.SetCapacity(fcount);
01023   bool rc = file.ReadInt( &i_size );
01024   int i = 0;
01025   switch(i_size) {
01026   case 1:
01027     for ( i = 0; i < fcount && rc ; i++ ) {
01028       rc = file.ReadChar( 4, cvi );
01029       vi = (unsigned int*)m_F[i].vi;
01030       vi[0] = cvi[0];
01031       vi[1] = cvi[1];
01032       vi[2] = cvi[2];
01033       vi[3] = cvi[3];
01034     }
01035     break;
01036   case 2:
01037     for ( i = 0; i < fcount && rc ; i++ ) {
01038       rc = file.ReadShort( 4, svi );
01039       vi = (unsigned int*)m_F[i].vi;
01040       vi[0] = svi[0];
01041       vi[1] = svi[1];
01042       vi[2] = svi[2];
01043       vi[3] = svi[3];
01044     }
01045     break;
01046   case 4:
01047     for ( i = 0; i < fcount && rc ; i++ ) {
01048       rc = file.ReadInt( 4, m_F[i].vi );
01049     }
01050     break;
01051   }
01052   m_F.SetCount(i);
01053 
01054   return rc;
01055 }
01056 
01057 
01058 bool ON_Mesh::Write_1( ON_BinaryArchive& file ) const
01059 {
01060   // ver 1.0 uncompressed format
01061 
01062   bool rc = file.WriteArray( m_V );
01063   if (rc) rc = file.WriteArray( m_N );
01064   if (rc) rc = file.WriteArray( m_T );
01065   if (rc) rc = file.WriteArray( m_K );
01066   if (rc) rc = file.WriteArray( m_C );
01067 
01068   return rc;
01069 }
01070 
01071 bool ON_Mesh::Read_1( ON_BinaryArchive& file )
01072 {
01073   // common to all 1.x formats (uncompressed)
01074 
01075   bool rc = file.ReadArray( m_V );
01076   if (rc) rc = file.ReadArray( m_N );
01077   if (rc) rc = file.ReadArray( m_T );
01078   if (rc) rc = file.ReadArray( m_K );
01079   if (rc) rc = file.ReadArray( m_C );
01080 
01081   return rc;
01082 }
01083 
01084 bool ON_Mesh::Write_2( int Vcount, ON_BinaryArchive& file ) const
01085 {
01086   // ver 2.0 compressed format
01087   const ON::endian e = file.Endian();
01088 
01089   bool rc = true;
01090 
01091   if ( Vcount > m_V.Count() )
01092     return false;
01093 
01094   if ( Vcount > 0 ) 
01095   {
01096     const int Ncount = (m_V.Count() == m_N.Count()) ? Vcount : 0;
01097     const int Tcount = (m_V.Count() == m_T.Count()) ? Vcount : 0;
01098     const int Kcount = (m_V.Count() == m_K.Count()) ? Vcount : 0;
01099     const int Ccount = (m_V.Count() == m_C.Count()) ? Vcount : 0;
01100 
01101     if ( e == ON::big_endian ) 
01102     {
01103       // These calls temporarily put the m_V[], m_N[], m_T[], m_K[]
01104       // and m_C[] arrays in little endian byte order because 3dm archives
01105       // are always in little endian byte order.
01106       //
01107       // This code assumes sizeof(ON_Color)=4, sizeof(float)=4 
01108       // and sizeof(double)=8.
01109       // If this is not the case, then changing the 4's and 8's below
01110       // will not work.  You will have to copy the mesh definition
01111       // into temporary arrays of 4 byte floats/8 byte doubles
01112       // and them compress the temporary arrays.  If you do this,
01113       // then remove the "restore" byte order calls below.
01114       file.ToggleByteOrder( Vcount*3, 4, m_V.Array(), (void*)m_V.Array() );
01115       file.ToggleByteOrder( Ncount*3, 4, m_N.Array(), (void*)m_N.Array() );
01116       file.ToggleByteOrder( Tcount*2, 4, m_T.Array(), (void*)m_T.Array() );
01117       file.ToggleByteOrder( Kcount*2, 8, m_K.Array(), (void*)m_K.Array() );
01118       file.ToggleByteOrder( Ccount,   4, m_C.Array(), (void*)m_C.Array() );
01119     }
01120     if (rc) rc = file.WriteCompressedBuffer( Vcount*sizeof(ON_3fPoint),         m_V.Array() );
01121     if (rc) rc = file.WriteCompressedBuffer( Ncount*sizeof(ON_3fVector),        m_N.Array() );
01122     if (rc) rc = file.WriteCompressedBuffer( Tcount*sizeof(ON_2fPoint),         m_T.Array() );
01123     if (rc) rc = file.WriteCompressedBuffer( Kcount*sizeof(ON_SurfaceCurvature),m_K.Array() );
01124     if (rc) rc = file.WriteCompressedBuffer( Ccount*sizeof(ON_Color),           m_C.Array() );
01125     if ( e == ON::big_endian ) 
01126     {
01127       // These calls restore the m_V[], m_N[], m_T[], m_K[] and m_C[] arrays
01128       // to the correct big endian runtime byte order.  This must be done even
01129       // if rc is false.
01130       file.ToggleByteOrder( Vcount*3, 4, m_V.Array(), (void*)m_V.Array() );
01131       file.ToggleByteOrder( Ncount*3, 4, m_N.Array(), (void*)m_N.Array() );
01132       file.ToggleByteOrder( Tcount*2, 4, m_T.Array(), (void*)m_T.Array() );
01133       file.ToggleByteOrder( Kcount*2, 8, m_K.Array(), (void*)m_K.Array() );
01134       file.ToggleByteOrder( Ccount,   4, m_C.Array(), (void*)m_C.Array() );
01135     }
01136   }
01137 
01138   return rc;
01139 }
01140 
01141 bool ON_Mesh::Read_2( int vcount, ON_BinaryArchive& file )
01142 {
01143   // common to all 2.x formats (compressed)
01144   const ON::endian e = file.Endian();
01145 
01146   bool rc = true;
01147 
01148 
01149   if ( vcount > 0 ) 
01150   {
01151     size_t sz = 0;
01152     int bFailedCRC;
01153 
01154     sz = 0;
01155     if (rc) rc = file.ReadCompressedBufferSize( &sz );
01156     if (rc && sz) 
01157     {
01158       if ( sz == vcount*sizeof(m_V[0]) )
01159       {
01160         m_V.SetCapacity(vcount);
01161         if (rc) rc = file.ReadCompressedBuffer( sz,m_V.Array(),&bFailedCRC);
01162         if (rc) m_V.SetCount(vcount);
01163       }
01164       else
01165       {
01166         ON_ERROR("ON_Mesh::Read - compressed vertex point buffer size is wrong.");
01167         rc = false; // buffer is wrong size
01168       }
01169     }
01170 
01171     sz = 0;
01172     if (rc) rc = file.ReadCompressedBufferSize( &sz );
01173     if (rc && sz) 
01174     {
01175       if ( sz == vcount*sizeof(m_N[0]) )
01176       {
01177         m_N.SetCapacity(vcount);
01178         if (rc) rc = file.ReadCompressedBuffer( sz,m_N.Array(),&bFailedCRC );
01179         if (rc) m_N.SetCount(vcount);
01180       }
01181       else
01182       {
01183         ON_ERROR("ON_Mesh::Read - compressed vertex normal buffer size is wrong.");
01184         rc = false; // buffer is wrong size
01185       }
01186     }
01187     
01188     sz = 0;
01189     if (rc) rc = file.ReadCompressedBufferSize( &sz );
01190     if (rc && sz) 
01191     {
01192       if ( sz == vcount*sizeof(m_T[0]) )
01193       {
01194         m_T.SetCapacity(vcount);
01195         if (rc) rc = file.ReadCompressedBuffer( sz,m_T.Array(),&bFailedCRC );
01196         if (rc) m_T.SetCount(vcount);
01197       }
01198       else
01199       {
01200         ON_ERROR("ON_Mesh::Read - compressed texture coordinate buffer size is wrong.");
01201         rc = false; // buffer is wrong size
01202       }
01203     }
01204     
01205     sz = 0;
01206     if (rc) rc = file.ReadCompressedBufferSize( &sz );
01207     if (rc && sz) 
01208     {
01209       if ( sz == vcount*sizeof(m_K[0]) )
01210       {
01211         m_K.SetCapacity(vcount);
01212         if (rc) rc = file.ReadCompressedBuffer( sz,m_K.Array(),&bFailedCRC );
01213         if (rc) m_K.SetCount(vcount);
01214       }
01215       else
01216       {
01217         ON_ERROR("ON_Mesh::Read - compressed vertex curvature buffer size is wrong.");
01218         rc = false; // buffer is wrong size
01219       }
01220     }
01221     
01222     sz = 0;
01223     if (rc) rc = file.ReadCompressedBufferSize( &sz );
01224     if (rc && sz) 
01225     {
01226       if ( sz == vcount*sizeof(m_C[0]) )
01227       {
01228         m_C.SetCapacity(vcount);
01229         if (rc) rc = file.ReadCompressedBuffer( sz,m_C.Array(),&bFailedCRC );
01230         if (rc) m_C.SetCount(vcount);
01231       }
01232       else
01233       {
01234         ON_ERROR("ON_Mesh::Read - compressed vertex color buffer size is wrong.");
01235         rc = false; // buffer is wrong size
01236       }
01237     }
01238     
01239     if ( e == ON::big_endian ) 
01240     {
01241       // This code assumes sizeof(ON_Color)=4, sizeof(float)=4 
01242       // and sizeof(double)=8.
01243       // If this is not the case, then changing the 4's and 8's below
01244       // will not work.  You will have to read the compressed
01245       // information into temporary arrays of 4 byte floats/8 byte doubles
01246       // and then convert those numbers to whatever is stored in the
01247       // m_V[], m_N[], m_T[], m_K[] and m_C[] arrays/
01248       file.ToggleByteOrder( m_V.Count()*3, 4, m_V.Array(), (void*)m_V.Array() );
01249       file.ToggleByteOrder( m_N.Count()*3, 4, m_N.Array(), (void*)m_N.Array() );
01250       file.ToggleByteOrder( m_T.Count()*2, 4, m_T.Array(), (void*)m_T.Array() );
01251       file.ToggleByteOrder( m_K.Count()*2, 8, m_K.Array(), (void*)m_K.Array() );
01252       file.ToggleByteOrder( m_C.Count()*3, 4, m_C.Array(), (void*)m_C.Array() );
01253     }
01254   }
01255 
01256   return rc;
01257 }
01258 
01259 
01260 ON_BOOL32 ON_Mesh::Write( ON_BinaryArchive& file ) const
01261 {
01262   int i;
01263   //const int major_version = 1; // uncompressed
01264   //const int major_version = 2; // beta format (never used)
01265   const int major_version = 3; // compressed
01266   bool rc = file.Write3dmChunkVersion(major_version,5);
01267 
01268   const int vcount = VertexCount();
01269   const int fcount = FaceCount();
01270 
01271   if (rc) rc = file.WriteInt( vcount );
01272   if (rc) rc = file.WriteInt( fcount );
01273   if (rc) rc = file.WriteInterval( m_packed_tex_domain[0] );
01274   if (rc) rc = file.WriteInterval( m_packed_tex_domain[1] );
01275   if (rc) rc = file.WriteInterval( m_srf_domain[0] );
01276   if (rc) rc = file.WriteInterval( m_srf_domain[1] );
01277   if (rc) rc = file.WriteDouble( 2, m_srf_scale );
01278   if (rc) rc = file.WriteFloat( 6, &m_vbox[0][0] );
01279   if (rc) rc = file.WriteFloat( 6, &m_nbox[0][0] );
01280   if (rc) rc = file.WriteFloat( 4, &m_tbox[0][0] );
01281 
01282   // archive int value meaning: -1 = unknown 0 = mesh is not closed, 1 = mesh is closed
01283   i = -1;
01284   switch( m_mesh_is_closed )
01285   {
01286   case 0: // unset
01287     i = -1;
01288     break;
01289   case 1: // closed
01290     i = 1;
01291     break;
01292   case 2: // not closed
01293     i = 0;
01294     break;
01295   }
01296   if (rc) rc = file.WriteInt( i );
01297 
01298   unsigned char b = m_mesh_parameters ? 1 : 0;
01299   if (rc) rc = file.WriteChar(b);
01300   if (rc && b) {
01301     if (rc) rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 );
01302     if (rc) {
01303       rc = m_mesh_parameters->Write(file);
01304       if ( !file.EndWrite3dmChunk() )
01305         rc = false;
01306     }
01307   }
01308 
01309   for ( i = 0; rc && i < 4; i++ ) {
01310     b = m_kstat[i] ? 1 : 0;
01311     rc = file.WriteChar(b);
01312     if (b) {
01313       rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 );
01314       if (rc) {
01315         rc = m_kstat[i]->Write(file);
01316         if ( !file.EndWrite3dmChunk() )
01317           rc = false;
01318       }
01319     }
01320   }
01321 
01322   if (rc) rc = WriteFaceArray( vcount, fcount, file );
01323 
01324   if (rc) {
01325     // major version is a hard coded 3
01326 
01327     //if ( major_version == 1 )
01328     //  rc = Write_1(file);
01329     //else if ( major_version == 3 )
01330       rc = Write_2(vcount,file);
01331     //else
01332     //  rc = false;
01333   }
01334 
01335   // added for minor version 1.2 and 3.2
01336   i = m_packed_tex_rotate ? 1 : 0;
01337   if (rc) rc = file.WriteInt( i );
01338 
01339   // added for minor version 3.3
01340   if (rc) rc = file.WriteUuid( m_Ttag.m_mapping_id );
01341  
01342   // compressed m_S[]
01343   if ( rc && vcount > 0 ) 
01344   {
01345     // Before 201011049 there was a bug that let m_S[] arrays
01346     // with the wrong size get saved in files. 
01347     const int Scount = (vcount == m_S.Count()) ? m_S.Count() : 0;
01348     const ON::endian e = file.Endian();
01349     if ( e == ON::big_endian ) 
01350     {
01351       file.ToggleByteOrder( Scount*2, 8, m_S.Array(), (void*)m_S.Array() );
01352     }
01353     if (rc) rc = file.WriteCompressedBuffer( Scount*sizeof(ON_2dPoint),m_S.Array() );
01354     if ( e == ON::big_endian ) 
01355     {
01356       file.ToggleByteOrder( Scount*2, 8, m_S.Array(), (void*)m_S.Array() );
01357     }
01358   }
01359 
01360   // added for minor version 3.4
01361   if (rc) rc = m_Ttag.Write(file);
01362 
01363   // added for minor version 3.5
01364   if (rc) rc = file.WriteChar( m_mesh_is_manifold );
01365   if (rc) rc = file.WriteChar( m_mesh_is_oriented );
01366   if (rc) rc = file.WriteChar( m_mesh_is_solid );
01367 
01368 
01369   return rc;
01370 }
01371 
01377 //
01379 static const ON_UUID obsolete_default_srfp_mapping_id = { 0xb988a6c2, 0x61a6, 0x45a7, { 0xaa, 0xee, 0x9a, 0xed, 0x7e, 0xf4, 0xe3, 0x16 } };
01380 
01381 // overrides virtual ON_Object::Write
01382 ON_BOOL32 ON_TextureMapping::Write(
01383         ON_BinaryArchive& file
01384       ) const
01385 {
01386   bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1);
01387   if (rc)
01388   {
01389 
01390     for(;;)
01391     {
01392       rc = file.WriteUuid( m_mapping_id);
01393       if (!rc) break;
01394 
01395       rc = file.WriteInt( m_type );
01396       if (!rc) break;
01397 
01398       rc = file.WriteInt( m_projection );
01399       if (!rc) break;
01400 
01401       rc = file.WriteXform( m_Pxyz );
01402       if (!rc) break;
01403 
01404       // Do not write m_Nxyz - it is calculated from m_Pxyz.
01405       rc = file.WriteXform( m_uvw );
01406       if (!rc) break;
01407 
01408       rc = file.WriteString(m_mapping_name);
01409       if (!rc) break;
01410 
01411       rc = file.WriteObject(m_mapping_primitive);
01412       if (!rc) break;
01413 
01414       // 13 October 2006 ver 1.1 fields
01415       rc = file.WriteInt(m_texture_space);
01416       if (!rc) break;
01417 
01418       rc = file.WriteBool(m_bCapped);
01419       if (!rc) break;
01420 
01421       break;
01422     }
01423 
01424     if ( !file.EndWrite3dmChunk() )
01425       rc = false;
01426   }
01427 
01428   return rc;
01429 }
01430 
01431 // overrides virtual ON_Object::Read
01432 ON_BOOL32 ON_TextureMapping::Read(
01433         ON_BinaryArchive& file
01434       )
01435 {
01436   Default();
01437 
01438   int major_version = 0;
01439   int minor_version = 0;
01440   int i;
01441 
01442   bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
01443   if (rc)
01444   {
01445     if ( 1 == major_version )
01446     {
01447       // DO NOT SAVE m_mapping_index in archive.
01448       // 1.0 fields
01449       for(;;)
01450       {
01451         rc = file.ReadUuid( m_mapping_id );
01452         if (!rc) break;
01453         if ( 0 == ON_UuidCompare(&obsolete_default_srfp_mapping_id,&m_mapping_id) )
01454           m_mapping_id = ON_nil_uuid;
01455 
01456         rc = file.ReadInt( &i );
01457         if (!rc) break;
01458         m_type = TypeFromInt(i);
01459 
01460         rc = file.ReadInt( &i );
01461         if (!rc) break;
01462         m_projection = ProjectionFromInt(i);
01463 
01464         rc = file.ReadXform( m_Pxyz );
01465         if (!rc) break;
01466 
01467         m_Pxyz.GetSurfaceNormalXform(m_Nxyz);
01468 
01469         rc = file.ReadXform( m_uvw );
01470         if (!rc) break;
01471 
01472         rc = file.ReadString(m_mapping_name);
01473         if (!rc) break;
01474 
01475         rc = (file.ReadObject(&m_mapping_primitive) >= 0);
01476         if (!rc) break;
01477 
01478         if ( minor_version >= 1 )
01479         {
01480           rc = file.ReadInt(&i);
01481           if (!rc) break;
01482           m_texture_space = TextureSpaceFromInt(i);
01483 
01484           rc = file.ReadBool(&m_bCapped);
01485           if (!rc) break;
01486         }
01487 
01488         break;
01489       }
01490     }
01491 
01492     if ( !file.EndRead3dmChunk() )
01493       rc = false;
01494   }
01495 
01496   return rc;
01497 }
01498 
01499 static
01500 void GetSurfaceParametersHelper( const ON_Mesh& mesh,
01501                                  double tex_x, double tex_y, 
01502                                  double* srf_s, double* srf_t )
01503 {
01504   // convert texture coordinates to surface parameters
01505   // Used to reconstruct m_S[] when old files are read.
01506   double tex_s, tex_t;
01507 
01508   if ( mesh.m_packed_tex_rotate ) 
01509   {
01510     // undo rotation and normalize
01511     tex_s =  mesh.m_packed_tex_domain[1].NormalizedParameterAt( tex_y );
01512     tex_t = 1.0 -  mesh.m_packed_tex_domain[0].NormalizedParameterAt( tex_x );
01513   }
01514   else 
01515   {
01516     // normalize
01517     tex_s =  mesh.m_packed_tex_domain[0].NormalizedParameterAt( tex_x );
01518     tex_t =  mesh.m_packed_tex_domain[1].NormalizedParameterAt( tex_y );
01519   }
01520   *srf_s =  mesh.m_srf_domain[0].ParameterAt(tex_s);
01521   *srf_t =  mesh.m_srf_domain[1].ParameterAt(tex_t);
01522 }
01523 
01524 
01525 ON_BOOL32 ON_Mesh::Read( ON_BinaryArchive& file )
01526 {
01527   Destroy();
01528 
01529   int major_version = 0;
01530   int minor_version = 0;
01531   int i;
01532   bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
01533   
01534   if (rc && (1 == major_version || 3 == major_version) ) 
01535   {
01536     int vcount = 0;
01537     int fcount = 0;
01538 
01539     if (rc) rc = file.ReadInt( &vcount );
01540     if (rc) rc = file.ReadInt( &fcount );
01541     if (rc) rc = file.ReadInterval( m_packed_tex_domain[0] );
01542     if (rc) rc = file.ReadInterval( m_packed_tex_domain[1] );
01543     if (rc) rc = file.ReadInterval( m_srf_domain[0] );
01544     if (rc) rc = file.ReadInterval( m_srf_domain[1] );
01545     if (rc) rc = file.ReadDouble( 2, m_srf_scale );
01546     if (rc) rc = file.ReadFloat( 6, &m_vbox[0][0] );
01547     if (rc) rc = file.ReadFloat( 6, &m_nbox[0][0] );
01548     if (rc) rc = file.ReadFloat( 4, &m_tbox[0][0] );
01549 
01550     // int value meaning: -1 = unknown 0 = mesh is not closed, 1 = mesh is closed
01551     i = -1;
01552     if (rc) rc = file.ReadInt( &i );
01553     if (rc)
01554     {
01555       switch(i)
01556       {
01557       case 0: // not closed;
01558         SetClosed(0);
01559         break;
01560       case 1: // closed;
01561         SetClosed(1);
01562         break;
01563       case 2: // 13 April 2010 Dale Lear - "2" value is obsolete but appears in old files
01564         SetClosed(1);
01565         break;
01566       }
01567     }
01568 
01569     unsigned char b = 0;
01570     ON__UINT32 tcode=0;
01571     ON__INT64 big_value=0;
01572     if (rc) rc = file.ReadChar(&b);
01573     if (rc && b) 
01574     {
01575       // mesh parameters are in an anonymous chunk
01576       rc = file.BeginRead3dmBigChunk(&tcode,&big_value);
01577       if (rc) 
01578       {
01579         if ( TCODE_ANONYMOUS_CHUNK == tcode )
01580         {
01581           m_mesh_parameters = new ON_MeshParameters();
01582           rc = m_mesh_parameters->Read( file );
01583         }
01584         else
01585           rc = false;
01586         if (!file.EndRead3dmChunk())
01587           rc = false;
01588       }
01589     }
01590 
01591     for ( i = 0; rc && i < 4; i++ ) 
01592     {
01593       rc = file.ReadChar(&b);
01594       if (rc && b) 
01595       {
01596         // m_kstat[i] curvature stats are in an anonymous chunk
01597         tcode = 0;
01598         big_value = 0;
01599         rc = file.BeginRead3dmBigChunk( &tcode, &big_value );
01600         if (rc) 
01601         {
01602           if ( TCODE_ANONYMOUS_CHUNK == tcode )
01603           {
01604             m_kstat[i] = new ON_MeshCurvatureStats();
01605             rc = m_kstat[i]->Read(file);
01606           }
01607           else
01608             rc = false;
01609           if ( !file.EndRead3dmChunk() )
01610             rc = false;
01611         }
01612       }
01613     }
01614 
01615     if (rc) rc = ReadFaceArray( vcount, fcount, file );
01616 
01617     if (rc) {
01618       if ( major_version==1) {
01619         rc = Read_1(file);
01620       }
01621       else if ( major_version == 3 ) {
01622         rc = Read_2(vcount,file);
01623       }
01624       else
01625         rc = false;
01626     }
01627 
01628     if ( minor_version >= 2 ) 
01629     {
01630       int b = m_packed_tex_rotate;
01631       if (rc) rc = file.ReadInt( &b );
01632       m_packed_tex_rotate = b?true:false;
01633     }
01634 
01635     if ( 3 == major_version )
01636     {
01637       if ( minor_version >= 3 )
01638       {
01639         // added for minor version 3.3
01640         if (rc) rc = file.ReadUuid( m_Ttag.m_mapping_id );
01641 
01642         // compressed m_S[]
01643         if ( rc && vcount > 0 ) 
01644         {
01645           size_t sz = 0;
01646           ON_BOOL32 bFailedCRC=false;
01647           if (rc) rc = file.ReadCompressedBufferSize( &sz );
01648           if (rc && sz) 
01649           {
01650             if ( sz == vcount*sizeof(ON_2dPoint) )
01651             {
01652               m_S.SetCapacity(vcount);
01653               if (rc) rc = file.ReadCompressedBuffer( sz, m_S.Array(), &bFailedCRC );
01654               if (rc) m_S.SetCount(vcount);
01655               if ( ON::big_endian == file.Endian() ) 
01656               {
01657                 file.ToggleByteOrder( m_S.Count()*2, 8, m_S.Array(), (void*)m_S.Array() );
01658               }
01659             }
01660             else
01661             {
01662               ON_ERROR("ON_Mesh::Read - surface parameter buffer size is wrong.");
01663               if (    rc
01664                    && file.ArchiveOpenNURBSVersion() <= 201011049 
01665                    && 0 == (sz % sizeof(ON_2dPoint))
01666                    && sz >= sizeof(ON_2dPoint)
01667                  )
01668               {
01669                 // Before 201011049 there was a bug that let m_S[] arrays with
01670                 // the wrong size get saved in files. There was also a bug in 
01671                 // the Rhino .OBJ file reader that created meshes with m_S[] 
01672                 // arrays that had the wrong size.  The next 4 lines of code
01673                 // let us read the junk, discard it and then successfully read
01674                 // the rest of the file.
01675                 int Scount = (int)(sz / sizeof(ON_2dPoint));
01676                 m_S.SetCapacity(Scount);
01677                 rc = file.ReadCompressedBuffer( sz, m_S.Array(), &bFailedCRC );
01678                 m_S.Destroy();
01679               }
01680               else
01681               {
01682                 rc = false; // buffer is wrong size
01683               }
01684             }
01685           }
01686         }
01687         if ( minor_version >= 4 && file.ArchiveOpenNURBSVersion() >= 200606010 )
01688         {
01689           if (rc) rc = m_Ttag.Read(file);
01690           if ( minor_version >= 5 )
01691           {
01692             if (rc) rc = file.ReadChar( &m_mesh_is_manifold );
01693             if (rc) rc = file.ReadChar( &m_mesh_is_oriented );
01694             if (rc) rc = file.ReadChar( &m_mesh_is_solid );
01695           }
01696         }
01697       }
01698     }
01699 
01700     if (    0 == m_S.Count()
01701          && m_V.Count() > 0
01702          && HasTextureCoordinates()
01703          && m_srf_domain[0].IsIncreasing() 
01704          && m_srf_domain[1].IsIncreasing() 
01705          && m_packed_tex_domain[0].IsInterval()
01706          && m_packed_tex_domain[1].IsInterval()
01707          && 0 == m_Ttag.m_mapping_crc
01708          && ON_UuidIsNil(m_Ttag.m_mapping_id)
01709           ) 
01710     {
01711       // This is a mesh from an old file - but there is enough 
01712       // information to calculate the m_S[] values from the 
01713       // m_T[] values.
01714       m_S.SetCapacity(vcount);
01715       m_S.SetCount(0);
01716       ON_2dPoint sp;
01717       ON_2fPoint tc;
01718       for ( i = 0; i < vcount; i++)
01719       {
01720         tc = m_T[i];
01721         sp.x = tc.x;
01722         sp.y = tc.y;
01723         GetSurfaceParametersHelper(*this,sp.x,sp.y,&sp.x,&sp.y);
01724         m_S.Append(sp);
01725       }
01726       m_Ttag.SetDefaultSurfaceParameterMappingTag();
01727     }
01728   }
01729 
01730   return rc;
01731 }
01732 
01733 ON::object_type ON_Mesh::ObjectType() const
01734 {
01735   return ON::mesh_object;
01736 }
01737 
01738 int ON_Mesh::Dimension() const
01739 {
01740   return 3;
01741 }
01742 
01743 #if defined(ON_COMPILER_MSC)
01744 // Disable the MSC /W4 warning
01745 //   C4189: 'breakpoint_here_for_bad_vbox' : local variable is initialized but not referenced
01746 // on the line
01747 //   int breakpoint_here_for_bad_vbox = 0.  
01748 // The warning disable is here because MS is ignoring it
01749 // if I put it inside of the function.
01750 #pragma warning( push )
01751 #pragma warning( disable : 4189 ) 
01752 #endif
01753 
01754 float ON_FloatFloor(double x)
01755 {
01756   // If x is a NaN, you get what you deserve.
01757   //
01758   // If x is a finite valid double in the range -3.402823466e+38
01759   // to +3.402823466e+38, then returned value of f is the largest
01760   // float value that is mathematically less than or equal to the
01761   // value of x.
01762   //
01763   // If x is not in the float range or x is a NaN, then you get
01764   // what you deserve.
01765   //
01766   // ON_FLOAT_EPSILON = 1.192092896e-07 is the smallest number such that
01767   // 1.0f + 1.192092896e-07f > 1.0f.  
01768   //
01769   // If x < 0, then (1.0 + 0.5*1.192092896e-07)*x rounds x down so
01770   // that converting the double precision mantissa cannot create a
01771   // float value that is mathematically larger than the value of x.  
01772   //
01773   // If x > 0, then (1.0 - 0.5*1.192092896e-07)*x rounds x down so
01774   // that converting the double precision mantissa cannot create a
01775   // float value that is mathematically larger than the value of x.  
01776   // 
01777   const double e = (x < 0.0) ? (1.0 + 0.5*ON_FLOAT_EPSILON) : (1.0 - 0.5*ON_FLOAT_EPSILON);
01778   float f;
01779   f = (float)(e*x);
01780   return f;
01781 }
01782 
01783 float ON_FloatCeil(double x)
01784 {
01785   float f = (x != 0.0) ? (-ON_FloatFloor(-x)) : ((float)x);
01786   return f;
01787 }
01788 
01789 ON_BOOL32 ON_Mesh::GetBBox( // returns true if successful
01790        double* boxmin, // minimum
01791        double* boxmax, // maximum
01792        ON_BOOL32 bGrowBox
01793        ) const
01794 {
01795   ON_BOOL32 rc = false;
01796   const int facet_count  = FaceCount();
01797   const int vertex_count = VertexCount();
01798   if ( facet_count >= 1 && vertex_count >= 3 ) 
01799   {
01800     ON_BoundingBox vbox;
01801     if ( m_vbox[0][0] > m_vbox[1][0] ) 
01802     {
01803       // const lie - cache mesh bounding box
01804       float* fbbox[2] = {const_cast<float*>(&m_vbox[0][0]),const_cast<float*>(&m_vbox[1][0])};
01805       while ( HasDoublePrecisionVertices() && DoublePrecisionVerticesAreValid() )
01806       {
01807         double dbbox[2][3];
01808         const ON_3dPointArray& dV = DoublePrecisionVertices();
01809         rc = ON_GetPointListBoundingBox(
01810                 3, 0, vertex_count, 3, &dV[0].x,
01811                 &dbbox[0][0], &dbbox[1][0], 
01812                 false 
01813                 );
01814         if (!rc)
01815           break;
01816         // make sure we round min doubles down to the nearest float
01817         // and max doubles up to the nearest float.
01818         fbbox[0][0] = ON_FloatFloor(dbbox[0][0]);
01819         fbbox[0][1] = ON_FloatFloor(dbbox[0][1]);
01820         fbbox[0][2] = ON_FloatFloor(dbbox[0][2]);
01821         fbbox[1][0] = ON_FloatCeil(dbbox[1][0]);
01822         fbbox[1][1] = ON_FloatCeil(dbbox[1][1]);
01823         fbbox[1][2] = ON_FloatCeil(dbbox[1][2]);
01824 
01825         // depending on how
01826         if ( SinglePrecisionVerticesAreValid() )
01827         {
01828           ON_GetPointListBoundingBox(
01829                 3, 0, vertex_count, 3, &m_V[0].x,
01830                 &fbbox[0][0], &fbbox[1][0], 
01831                 true 
01832               );
01833         }
01834         break;
01835       }
01836 
01837       if (!rc)
01838       {
01839         rc = ON_GetPointListBoundingBox( 3, 0, 
01840                 vertex_count, 3, &m_V[0].x,
01841                 fbbox[0],fbbox[1],
01842                 false 
01843                 );
01844       }
01845     }
01846     else
01847     {
01848       rc = true;
01849     }
01850 
01851     if ( rc ) 
01852     {
01853       vbox.m_min.x = m_vbox[0][0];
01854       vbox.m_min.y = m_vbox[0][1];
01855       vbox.m_min.z = m_vbox[0][2];
01856       vbox.m_max.x = m_vbox[1][0];
01857       vbox.m_max.y = m_vbox[1][1];
01858       vbox.m_max.z = m_vbox[1][2];
01859       rc = vbox.IsValid();
01860       if (rc)
01861       {
01862         if ( bGrowBox )
01863         {
01864           vbox.Union( ON_BoundingBox(boxmin,boxmax) );
01865         }
01866 
01867         boxmin[0] = vbox.m_min.x;
01868         boxmin[1] = vbox.m_min.y;
01869         boxmin[2] = vbox.m_min.z;
01870 
01871         boxmax[0] = vbox.m_max.x;
01872         boxmax[1] = vbox.m_max.y;
01873         boxmax[2] = vbox.m_max.z;
01874       }
01875 #if defined(ON_DEBUG) && !defined(ON_COMPILER_GNU)
01876       // generates gcc unused variable warning
01877       else
01878       {
01879         int breakpoint_here_for_bad_vbox=0;
01880       }
01881 #endif
01882     }
01883   }
01884   return rc;
01885 }
01886 
01887 #if defined(ON_COMPILER_MSC)
01888 #pragma warning( pop )
01889 #endif
01890 
01891 bool ON_Mesh::IsDeformable() const
01892 {
01893   return true;
01894 }
01895 
01896 bool ON_Mesh::MakeDeformable()
01897 {
01898   return true;
01899 }
01900 
01901 ON_BOOL32 ON_Mesh::Transform( 
01902        const ON_Xform& xform
01903        )
01904 {
01905   const bool bIsValid_fV = SinglePrecisionVerticesAreValid();
01906   const bool bIsValid_dV = DoublePrecisionVerticesAreValid();
01907   const bool bSyncheddV = bIsValid_fV && bIsValid_dV && HasSynchronizedDoubleAndSinglePrecisionVertices();
01908   TransformUserData(xform);
01909         DestroyTree();
01910 
01911   double d = xform.Determinant();
01912   const int vertex_count = VertexCount();
01913   bool rc = false;
01914   if ( bSyncheddV )
01915   {
01916     // transforming the double precision vertices is the 
01917     // best way to set the floats.
01918     UpdateSinglePrecisionVertices();
01919     rc = true;
01920   }
01921   else
01922   {
01923     rc = ON_TransformPointList( 3, false, vertex_count, 3, &m_V[0][0], xform );
01924   }
01925 
01926   if ( rc )
01927   {
01928     m_Ctag.Transform(xform);
01929     m_Ttag.Transform(xform);
01930     int tci, tccnt = m_TC.Count();
01931     for ( tci = 0; tci < tccnt; tci++ )
01932     {
01933       m_TC[tci].m_tag.Transform(xform);
01934     }
01935   }
01936 
01937   if ( rc && 0.0 == d )
01938   {
01939     // mesh has been squashed to a plane (or worse)
01940     if ( HasVertexNormals() )
01941     {
01942       ComputeFaceNormals();
01943       ComputeVertexNormals();
01944     }
01945     else if ( HasFaceNormals() )
01946     {
01947       ComputeFaceNormals();
01948     }
01949   }
01950   else if ( rc )
01951   {
01952     if ( HasVertexNormals() ) 
01953     {
01954       // See http://www.gignews.com/realtime020100.htm or these
01955       // references.
01956       //
01957       // 1. Hanrahan, Pat, 
01958       //    "A Survey of Ray-Surface Intersection Algorithms", 
01959       //     chapter 3 in Andrew Glassner (editor), 
01960       //     An Introduction to Ray Tracing, 
01961       //     Academic Press Inc., London, 1989.
01962       //
01963       // 2. Turkowski, Ken, 
01964       //    "Properties of Surface-Normal Transformations", 
01965       //     in Andrew Glassner (editor), 
01966       //     Graphics Gems, Academic Press, Inc., 
01967       //     pp. 539-547, 1990. 
01968       ON_Xform N_xform;
01969       double d = xform.GetSurfaceNormalXform(N_xform);
01970       rc = ON_TransformVectorList( 3, vertex_count, 3, &m_N[0][0], N_xform )?true:false;
01971       if ( d < 0.0 )
01972       {
01973         FlipVertexNormals();
01974       }
01975       UnitizeVertexNormals();
01976     }
01977 
01978     if ( rc && HasFaceNormals() ) 
01979     {
01980       ComputeFaceNormals();
01981     }
01982   }
01983 
01984   if ( rc && HasPrincipalCurvatures() ) 
01985   {
01986     if ( fabs(fabs(d) - 1.0) > ON_SQRT_EPSILON ) 
01987     {
01988       // If it's a uniform scale, handle it, otherwise we can't do it.
01989       double scale = xform.m_xform[0][0];
01990       if ( 0.0 != scale && 0.0 != d
01991            && scale == xform.m_xform[1][1] 
01992            && scale == xform.m_xform[2][2] 
01993            && fabs(d - scale*scale*scale) <= d*ON_SQRT_EPSILON )
01994       {
01995         // uniform scale
01996         const double ks = 1.0/scale;
01997         ON_SurfaceCurvature* sc = m_K.Array();
01998         int ki = m_K.Count();
01999         while ( ki-- )
02000         {
02001           sc->k1 *= ks;
02002           sc->k2 *= ks;
02003           sc++;
02004         }
02005 
02006         // update curvature stats.
02007         for ( int j = 0; j < 4; j++ )
02008         {
02009           if ( m_kstat[j] )
02010             m_kstat[j]->Set( m_kstat[j]->m_style,m_K.Count(),m_K.Array(),m_N.Array() );
02011         }
02012       }
02013       else
02014       {
02015         ON_ERROR("ON_Mesh::Transform() cannot apply this transform to curvatures.\n");
02016         rc = false;
02017       }
02018     }
02019   }
02020 
02021   InvalidateVertexBoundingBox();
02022   InvalidateVertexNormalBoundingBox();
02023   if ( fabs(d) <= ON_ZERO_TOLERANCE )
02024     DestroyTopology(); // transform may not be one-to-one on vertices
02025 
02026   if ( bIsValid_fV )
02027     SetSinglePrecisionVerticesAsValid();
02028   if ( bIsValid_dV )
02029     SetDoublePrecisionVerticesAsValid();
02030 
02031   return rc;
02032 }
02033 
02034 void ON_Mesh::DestroyRuntimeCache( bool bDelete )
02035 {
02036   int i;
02037 
02038   DestroyTree(bDelete);
02039 
02040   if (bDelete )
02041   {    
02042     DestroyPartition();
02043     m_top.Destroy();
02044     DeleteMeshParameters();
02045     InvalidateCurvatureStats();
02046   }
02047   else
02048   {
02049     // do not free any memory
02050     m_top.EmergencyDestroy();
02051   }
02052 
02053   InvalidateBoundingBoxes();
02054   m_partition = 0;
02055   m_mesh_parameters = 0;
02056   m_top.m_mesh = this;
02057   m_parent = 0;
02058   //m_material_index = -1;
02059   m_mesh_is_closed = 0;
02060   m_mesh_is_manifold = 0;
02061   m_mesh_is_oriented = 0;
02062   m_mesh_is_solid = 0;
02063   for ( i = 0; i < 4; i++ ) 
02064   {
02065     m_kstat[i] = 0;
02066   }
02067 }
02068 
02069 ON_BOOL32 ON_Mesh::SwapCoordinates(
02070       int i, int j        // indices of coords to swap
02071       )
02072 {
02073   if ( i == j )
02074     return true;
02075 
02076   const bool bIsValid_fV = SinglePrecisionVerticesAreValid();
02077   const bool bIsValid_dV = DoublePrecisionVerticesAreValid();
02078 
02079   const int vertex_count = VertexCount();
02080   ON_BOOL32 rc = ON_SwapPointListCoordinates( vertex_count, 3, &m_V[0][0], i, j );
02081   if ( rc && HasVertexNormals() ) {
02082     rc = ON_SwapPointListCoordinates( vertex_count, 3, &m_N[0][0], i, j );
02083   }
02084   if ( rc )
02085   {
02086     float x;
02087     if ( m_vbox[0][0] <= m_vbox[1][0] ) {
02088       x = m_vbox[0][i]; m_vbox[0][i] = m_vbox[0][j]; m_vbox[0][j] = x;
02089       x = m_vbox[1][i]; m_vbox[1][i] = m_vbox[1][j]; m_vbox[1][j] = x;
02090     }
02091     if ( m_nbox[0][0] <= m_nbox[1][0] ) {
02092       x = m_nbox[0][i]; m_nbox[0][i] = m_nbox[0][j]; m_nbox[0][j] = x;
02093       x = m_nbox[1][i]; m_nbox[1][i] = m_nbox[1][j]; m_nbox[1][j] = x;
02094     }
02095   }
02096 
02097   if ( HasDoublePrecisionVertices() )
02098   {
02099     DoublePrecisionVertices().SwapCoordinates(i,j);
02100     if ( bIsValid_fV )
02101       SetSinglePrecisionVerticesAsValid();
02102     if ( bIsValid_dV )
02103       SetDoublePrecisionVerticesAsValid();
02104   }
02105 
02106   return rc;
02107 }
02108 
02109 void ON_Mesh::SetClosed(int b)
02110 {
02111   // 6 Novermber 2003 Dale Lear - let expert user set m_mesh_is_closed
02112   char mesh_is_closed = 0;
02113   switch(b)
02114   {
02115   case 0: // not closed - at least one boundary edge
02116     mesh_is_closed = 2;
02117     SetSolidOrientation(0);
02118     break;
02119   case 1: // all edges are shared 
02120     // DO NOT SET m_mesh_is_solid here.
02121     // Meshes can be closed but not solid
02122     mesh_is_closed = 1; 
02123     break;
02124   case 2: // 31 April 2010 Dale Lear - 2 is obsolete - it's either 0 or 1 now.
02125     mesh_is_closed = 1; 
02126     // DO NOT SET m_mesh_is_solid here.
02127     // Meshes can be closed but not solid
02128     break;
02129   default:
02130     mesh_is_closed = 0; // unset
02131     break;
02132   }
02133   if ( 0 == mesh_is_closed || m_mesh_is_closed != mesh_is_closed )
02134   {
02135     m_mesh_is_closed = mesh_is_closed;
02136     m_mesh_is_manifold = 0; // unset - will be reevaluated when needed
02137     m_mesh_is_oriented = 0; // unset - will be reevaluated when needed
02138   }
02139 }
02140 
02141 void ON_Mesh::SetSolidOrientation(int solid_orientation)
02142 {
02143   switch(solid_orientation)
02144   {
02145   case -1: // closed oriented manifold solid with inward face normals
02146     SetClosed(1);
02147     m_mesh_is_manifold = 1;
02148     m_mesh_is_oriented = 1;
02149     m_mesh_is_solid = 2;
02150     break;
02151 
02152   case  0: // not solid
02153     m_mesh_is_solid = 3;
02154     // DO NOT SET m_mesh_is_closed here.
02155     // Meshes can be closed but not solid
02156     break;
02157 
02158   case  1: // closed oriented manifold solid with outward face normals
02159     SetClosed(1);
02160     m_mesh_is_manifold = 1;
02161     m_mesh_is_oriented = 1;
02162     m_mesh_is_solid = 1;
02163     break;
02164 
02165   default:
02166     m_mesh_is_solid = 0;
02167     break;
02168   }
02169 }
02170 
02171 
02172 static 
02173 int ON_MeshIsManifold_CompareV( const void* a, const void* b )
02174 {
02175   return memcmp(a,b,sizeof(ON_3fPoint));
02176   /*
02177   float d;
02178   const float* fa = (const float*)a;
02179   const float* fb = (const float*)b;
02180   if ( 0.0f == (d = (*fa++ - *fb++)) )
02181   {
02182     if ( 0.0f == (d = (*fa++ - *fb++)) )
02183     {
02184       if ( 0.0f == (d = (*fa++ - *fb++)) )
02185         return 0;
02186     }
02187   }
02188   return ( d < 0.0f ) ? -1 : 1;
02189   */
02190 }
02191 
02192 static 
02193 int ON_MeshGetVertexEdges_Compare2dex( const void* a, const void* b )
02194 {
02195   return ON_Compare2dex((const ON_2dex*)a,(const ON_2dex*)b);
02196 }
02197 
02198 static 
02199 int ON_MeshIsManifold_Compare3dex( const void* a, const void* b )
02200 {
02201   return ON_Compare3dex((const ON_3dex*)a,(const ON_3dex*)b);
02202 }
02203 
02204 //static 
02205 //int ON_MeshGetVertexEdges_CompareInt( const int* a, const int* b )
02206 //{
02207 //  return (*a-*b);
02208 //}
02209 
02210 
02211 int ON_Mesh::GetVertexEdges( 
02212   int vertex_index_count,
02213   const int* vertex_index, 
02214   bool bNoDuplicates,
02215   ON_SimpleArray<ON_2dex>& edges
02216   ) const
02217 {
02218   // Get edges connected to vertices in vertex_index[] array.
02219   const int edges_count0 = edges.Count();
02220 
02221   const int mesh_vcount = m_V.Count();
02222 
02223   //03/12/2007 TimH. The line below appears to be a typo.  Using the following line works better.
02224   //const int mesh_fcount = m_V.Count();
02225   const int mesh_fcount = m_F.Count();
02226 
02227   if (   vertex_index_count <= 0 || 0 == vertex_index 
02228       || mesh_fcount <= 0 || mesh_vcount < 3 )
02229   {
02230     return 0;
02231   }
02232 
02233   int vei, efi, fvi, ei, fi, j, n, vi;
02234   const int* f_vi;
02235   ON_2dex edge_ends;
02236   const ON_MeshFace* f = m_F.Array();
02237 
02238   if (   TopologyExists()
02239        && mesh_vcount == m_top.m_topv_map.Count()
02240        && m_top.m_tope.Count() > 0 )
02241   {
02242     // Topology looks good; use it to speed up the search.
02243     const int* topv_map = m_top.m_topv_map;
02244     const int top_vcount = m_top.m_topv.Count();
02245     const int top_ecount = m_top.m_tope.Count();
02246     int top_vi;
02247     for ( n = 0; n < vertex_index_count; n++ )
02248     {
02249       vi = vertex_index[n];
02250       if ( vi < 0 || vi >= mesh_vcount )
02251         continue;
02252       top_vi = topv_map[vi];
02253       if ( top_vi < 0 || top_vi > top_vcount )
02254         continue;
02255       edge_ends.i = vi;
02256       const ON_MeshTopologyVertex& v = m_top.m_topv[top_vi];
02257       for ( vei = 0; vei < v.m_tope_count; vei++ )
02258       {
02259         ei = v.m_topei[vei];
02260         if ( ei < 0 || ei >= top_ecount )
02261           continue;
02262         const ON_MeshTopologyEdge& e = m_top.m_tope[ei];
02263         for ( efi = 0; efi < e.m_topf_count; efi++ )
02264         {
02265           fi = e.m_topfi[efi];
02266           if ( fi < 0 || fi >= mesh_fcount )
02267             continue;
02268           f_vi = f[fi].vi;
02269           for ( fvi = 0; fvi < 4; fvi++ )
02270           {
02271             if ( f_vi[fvi] == vi )
02272             {
02273               j = f_vi[(fvi+3)%4];
02274               if ( j >= 0 && j < mesh_vcount && vi != j )
02275               {
02276                 edge_ends.i = j;
02277                 edge_ends.j = vi;
02278                 edges.Append(edge_ends);
02279               }
02280               j = f_vi[ (2==fvi && f_vi[2]==f_vi[3]) ? 0 : ((fvi+1)%4) ];
02281               if ( j >= 0 && j < mesh_vcount && vi != j )
02282               {
02283                 edge_ends.i = vi;
02284                 edge_ends.j = j;
02285                 edges.Append(edge_ends);
02286               }
02287               break; // done with this face
02288             }
02289           }
02290         }
02291       }
02292     }
02293   }
02294   else
02295   {
02296     // slow-n-stupid search through all the faces
02297 
02298     // Sort vertex_index[] array so we can use a quick
02299     // binary search to see if a face is using one of 
02300     // the vertices in the list.
02301     ON_Workspace ws;
02302     for ( vi = 1; vi < vertex_index_count; vi++ )
02303     {
02304       if ( vertex_index[vi] < vertex_index[vi-1] )
02305       {
02306         // need to sort vertex_index[] array
02307         int* tmp = ws.GetIntMemory(vertex_index_count);
02308         memcpy(tmp,vertex_index,vertex_index_count*sizeof(tmp[0]));
02309         ON_SortIntArray(ON::quick_sort,tmp,vertex_index_count);
02310         vertex_index = tmp;
02311         break;
02312       }
02313     }
02314 
02315     // Find all the faces that use a vertex in the vertex_index[] array.
02316     for ( fi = 0; fi < mesh_fcount; fi++ )
02317     {
02318       f_vi = f[fi].vi;
02319       for ( fvi = 0; fvi < 4; fvi++ )
02320       {
02321         vi = f_vi[fvi];
02322         if ( ON_BinarySearchIntArray(vi,vertex_index,vertex_index_count) )
02323         {
02324           // vi is in the vertex_index[] array.  Add the edges
02325           // of this face that begin and end at this vertex.
02326           j = f_vi[(fvi+3)%4];
02327           if ( j >= 0 && j < mesh_vcount && vi != j )
02328           {
02329             edge_ends.i = j;
02330             edge_ends.j = vi;
02331             edges.Append(edge_ends);
02332           }
02333           j = f_vi[ (2==fvi && f_vi[2]==f_vi[3]) ? 0 : ((fvi+1)%4) ];
02334           if ( j >= 0 && j < mesh_vcount && vi != j )
02335           {
02336             edge_ends.i = vi;
02337             edge_ends.j = j;
02338             edges.Append(edge_ends);
02339           }
02340         }
02341       }
02342     }
02343   }
02344 
02345   if ( bNoDuplicates && edges.Count() > edges_count0 )
02346   {
02347     for ( ei = edges_count0; ei < edges.Count(); ei++ )
02348     {
02349       edge_ends = edges[ei];
02350       if ( edge_ends.i > edge_ends.j )
02351       {
02352         j = edge_ends.i; edge_ends.i = edge_ends.j; edge_ends.j = j;
02353       }
02354     }
02355     ON_qsort( edges.Array() + edges_count0,
02356               edges.Count() - edges_count0,
02357               sizeof(edge_ends), 
02358               ON_MeshGetVertexEdges_Compare2dex);
02359     edge_ends = edges[edges_count0];
02360     for ( ei = j = edges_count0+1; ei < edges.Count(); ei++ )
02361     {
02362       if ( ON_Compare2dex(&edge_ends,&edges[ei]) )
02363       {
02364         edge_ends = edges[ei];
02365         if ( j != ei )
02366           edges[j] = edge_ends;
02367         j++;
02368       }
02369     }
02370     edges.SetCount(j);
02371   }
02372 
02373   return (edges.Count() - edges_count0);
02374 }
02375 
02376 int ON_Mesh::GetMeshEdges( 
02377   ON_SimpleArray<ON_2dex>& edges
02378   ) const
02379 {
02380   const int edges_count0 = edges.Count();
02381   int fi, ei, j, fvi;
02382   const int* f_vi;
02383   const ON_MeshFace* f = m_F.Array();
02384   const int mesh_vcount = m_V.Count();
02385   const int mesh_fcount = m_F.Count();
02386   edges.Reserve( edges_count0 + 4*mesh_fcount );
02387   ON_2dex e;
02388 
02389   // Find all the faces that use a vertex in the vertex_index[] array.
02390   for ( fi = 0; fi < mesh_fcount; fi++ )
02391   {
02392     f_vi = f[fi].vi;
02393     ei = f_vi[3];
02394     for ( fvi = 0; fvi < 4; fvi++ )
02395     {
02396       e.i = ei;
02397       ei = *f_vi++;
02398       e.j = ei;
02399       if ( e.i > e.j )
02400       {
02401         j = e.i; e.i = e.j; e.j = j;
02402       }
02403       if ( e.i != e.j && e.i >= 0 && e.j < mesh_vcount )
02404       {
02405         edges.Append(e);
02406       }
02407     }
02408   }
02409   
02410 
02411   if ( edges.Count() > edges_count0 )
02412   {
02413     ON_qsort( edges.Array() + edges_count0,
02414               edges.Count() - edges_count0,
02415               sizeof(e), 
02416               ON_MeshGetVertexEdges_Compare2dex);
02417     e = edges[edges_count0];
02418     for ( ei = j = edges_count0+1; ei < edges.Count(); ei++ )
02419     {
02420       if ( ON_Compare2dex(&e,&edges[ei]) )
02421       {
02422         e = edges[ei];
02423         if ( j != ei )
02424           edges[j] = e;
02425         j++;
02426       }
02427     }
02428     edges.SetCount(j);
02429   }
02430 
02431   return edges.Count() - edges_count0;
02432 }
02433 
02434 
02435 int ON_Mesh::SolidOrientation() const
02436 {
02437 
02438   if ( m_mesh_is_solid <= 0 || m_mesh_is_solid > 3 )
02439   {
02440     // NOTE: calling IsSolid() will set m_mesh_is_solid
02441     //       to 3 if mes is non-manifold
02442     IsSolid();
02443   }
02444 
02445   switch(m_mesh_is_solid)
02446   {
02447   case 1:
02448     return 1;
02449     break;
02450 
02451   case 2:
02452     return -1;
02453     break;
02454 
02455   case 3:
02456     return 0;
02457     break;
02458   }
02459 
02460   return 0; // answer "no" if we don't know.
02461 }
02462 
02463 
02464 bool ON_Mesh::IsSolid() const
02465 {
02466   return ( IsClosed() && IsManifold() && IsOriented() );
02467 }
02468 
02469 
02470 bool ON_Mesh::IsManifold(
02471   bool bTopologicalTest,
02472   bool* pbIsOriented,
02473   bool* pbHasBoundary
02474   ) const
02475 {
02476   bool bIsManifold = false;
02477   if ( pbIsOriented )
02478     *pbIsOriented = false;
02479   if ( pbHasBoundary )
02480     *pbHasBoundary = false;
02481   const int vcount = m_V.Count();
02482   const int fcount = m_F.Count();
02483   if ( vcount > 0 && fcount > 0 )
02484   {
02485     ON_Workspace ws;
02486     ON_3dex e;
02487     int i, j, ecount;
02488     const int* fvi;
02489     ON_3fPoint v0;
02490     const ON_3fPoint* v;
02491     const ON_MeshFace* f;
02492     int* vid = ws.GetIntMemory(vcount);
02493     ON_3dex* edge = (ON_3dex*)ws.GetMemory(4*fcount*sizeof(*edge));
02494 
02495     if ( bTopologicalTest )
02496     {
02497       // coincident vertices are assigned the same vertex id
02498       ON_Sort(ON::quick_sort,vid,m_V.Array(),vcount,sizeof(m_V[0]),ON_MeshIsManifold_CompareV);
02499       ecount = 0;
02500       v = m_V.Array();
02501       ecount = 0;
02502       j = vcount;
02503       for ( i = 0; i < vcount; i = j)
02504       {
02505         v0 = v[vid[i]];
02506         vid[i] = ecount;
02507         for ( j = i+1; j < vcount; j++ )
02508         {
02509           if ( ON_MeshIsManifold_CompareV(&v,v+vid[j]) )
02510           {
02511             ecount++;
02512             break;
02513           }
02514           vid[j] = ecount;
02515         }
02516       }
02517     }
02518     else
02519     {
02520       // each vertex gets a unique id.
02521       for ( i = 0; i < vcount; i++ )
02522         vid[i] = i;
02523     }
02524 
02525     // build a list of edges
02526     f = m_F.Array();
02527     ecount = 0;
02528     for ( i = 0; i < fcount; i++ )
02529     {
02530       fvi = (f++)->vi;
02531       if (   fvi[0] >= 0 && fvi[0] < vcount 
02532           && fvi[1] >= 0 && fvi[1] < vcount
02533           && fvi[2] >= 0 && fvi[2] < vcount
02534           && fvi[3] >= 0 && fvi[3] < vcount )
02535       {
02536         // loop unrolled for speed
02537         j = ecount;
02538         e.i = vid[fvi[0]];  e.j = vid[fvi[1]];
02539         if ( 0 != (e.k = e.j - e.i) )
02540         {
02541           if ( e.k < 0 ) {e.k = e.i; e.i = e.j; e.j = e.k; e.k = 1;} else e.k = 0;
02542           edge[ecount++] = e;
02543         }
02544         e.i = vid[fvi[1]];  e.j = vid[fvi[2]];
02545         if ( 0 != (e.k = e.j - e.i) )
02546         {
02547           if ( e.k < 0 ) {e.k = e.i; e.i = e.j; e.j = e.k; e.k = 1;} else e.k = 0;
02548           edge[ecount++] = e;
02549         }
02550         e.i = vid[fvi[2]];  e.j = vid[fvi[3]];
02551         if ( 0 != (e.k = e.j - e.i) )
02552         {
02553           if ( e.k < 0 ) {e.k = e.i; e.i = e.j; e.j = e.k; e.k = 1;} else e.k = 0;
02554           edge[ecount++] = e;
02555         }
02556         e.i = vid[fvi[3]];  e.j = vid[fvi[0]];
02557         if ( 0 != (e.k = e.j - e.i) )
02558         {
02559           if ( e.k < 0 ) {e.k = e.i; e.i = e.j; e.j = e.k; e.k = 1;} else e.k = 0;
02560           edge[ecount++] = e;
02561         }
02562         if ( ecount < j+3 )
02563           ecount = j;
02564       }
02565     }
02566 
02567     if ( ecount >= 4 )
02568     {
02569       bIsManifold = true;
02570       bool bIsOriented  = (pbIsOriented)  ? true  : false;
02571       bool bHasBoundary = (pbHasBoundary) ? false : true;
02572       ON_qsort(edge,ecount,sizeof(edge[0]),ON_MeshIsManifold_Compare3dex);
02573 
02574       i = 0;
02575       e = *edge;
02576       while ( --ecount )
02577       {
02578         edge++;
02579         if ( memcmp(&e,edge,2*sizeof(int)) )
02580         {
02581           if (!i)
02582             bHasBoundary = true;
02583           e = *edge;
02584           i = 0;
02585         }
02586         else
02587         {
02588           if ( i++ )
02589           {
02590             bIsManifold = false;
02591             break;
02592           }
02593           if ( e.k == edge->k )
02594             bIsOriented = false;
02595         }
02596       }
02597 
02598       if ( bIsManifold )
02599       {
02600         if ( pbIsOriented )
02601           *pbIsOriented = bIsOriented;
02602         if ( pbHasBoundary )
02603           *pbHasBoundary = bHasBoundary;
02604       }
02605     }
02606   }
02607 
02608   return bIsManifold;
02609 }
02610 
02611 static void ON_hsort_3dex(ON_3dex *e, size_t nel)
02612 {
02613   // dictionary sort e[]
02614   size_t i_end,k,i,j;
02615   ON_3dex e_tmp;
02616 
02617   if (nel < 2) return;
02618   k = nel >> 1;
02619   i_end = nel-1;
02620   for (;;)
02621   {
02622     if (k)
02623     {
02624       --k;
02625       e_tmp = e[k];
02626     }
02627     else 
02628     {
02629       e_tmp = e[i_end];
02630       e[i_end] = e[0];
02631       if (!(--i_end))
02632       {
02633         e[0] = e_tmp;
02634         break;
02635       }
02636     }
02637     i = k;
02638     j = (k<<1) + 1;
02639     while (j <= i_end) 
02640     {
02641       if ( j < i_end && ( e[j].i < e[j + 1].i || (e[j].i == e[j + 1].i && (e[j].j < e[j + 1].j || ( e[j].j == e[j + 1].j && e[j].k < e[j + 1].k)) ) ) ) 
02642         j++;
02643 
02644       if ( e_tmp.i < e[j].i || (e_tmp.i == e[j].i && (e_tmp.j < e[j].j || ( e_tmp.j == e[j].j && e_tmp.k < e[j].k) ) ) )
02645       {
02646         e[i] = e[j];
02647         i = j;
02648         j = (j<<1) + 1;
02649       }
02650       else
02651         j = i_end + 1;
02652     }
02653     e[i] = e_tmp;
02654   }
02655 }
02656 
02657 static void ON_Mesh_SetClosedHelper( 
02658           bool bClosedOnly,
02659           const ON_Mesh& mesh,
02660           const char& m_mesh_is_manifold,
02661           const char& m_mesh_is_oriented
02662           )
02663 {
02664   // thread safe lazy evaluation for mesh's m_mesh_is_... flags
02665   // Sets: m_mesh_is_closed.
02666   //       If bClosedOnly is false, also sets m_mesh_is_manifold and m_mesh_is_oriented
02667   int is_closed = 0;
02668   char is_manifold = 2;
02669   char is_oriented = 2;
02670   for (;;)
02671   {
02672     const int Vcount = mesh.m_V.Count();
02673     const int Fcount = mesh.m_F.Count();
02674     if ( Vcount < 3 || Fcount < 1 )
02675     {
02676       ON_ERROR("Mesh is not valid.");
02677       break;
02678     }
02679     if ( bClosedOnly && (Vcount < 4 || Fcount < 4) )
02680     {
02681       // not closed - don't waste any more time.
02682       break;
02683     }
02684 
02685     int i, j;
02686     int Vidbuffer[256];
02687     int* Vid = mesh.GetVertexLocationIds( 
02688                      1,
02689                      (Vcount*sizeof(*Vid) <= sizeof(Vidbuffer) ? &Vidbuffer[0] : 0),
02690                      0
02691                     );
02692     if ( 0 == Vid )
02693     {
02694       ON_ERROR("Mesh has corrupt vertex information.");
02695       bClosedOnly = false;
02696       break;
02697     }
02698 
02699     // build an edge list where the "vertex" indices identify unique 3d locations
02700     ON_3dex* E_list = (ON_3dex*)onmalloc(4*Fcount*sizeof(E_list[0]));
02701     ON_3dex E;
02702     int Vid0;
02703     const int* fvi;
02704     int E_count = 0;
02705     const ON_MeshFace* F = mesh.m_F.Array();
02706     for ( j = 0; j < Fcount; j++ )
02707     {
02708       fvi = F[j].vi;
02709       E.i = Vid[fvi[0]];
02710       Vid0 = E.j = Vid[fvi[1]];
02711       if ( E.i == E.j )
02712         break;
02713       if ( E.i > E.j )
02714       {
02715         i = E.i; E.i = E.j; E.j = i;
02716         E.k = 1;
02717       }
02718       else
02719       {
02720         E.k = 0;
02721       }
02722       E_list[E_count++] = E;
02723 
02724       E.i = Vid0;
02725       Vid0 = E.j = Vid[fvi[2]];
02726       if ( E.i == E.j )
02727         break;
02728       if ( E.i > E.j )
02729       {
02730         i = E.i; E.i = E.j; E.j = i;
02731         E.k = 1;
02732       }
02733       else
02734       {
02735         E.k = 0;
02736       }
02737       E_list[E_count++] = E;
02738 
02739       if ( fvi[2] != fvi[3] )
02740       {
02741         // quad
02742         E.i = Vid0;
02743         Vid0 = E.j = Vid[fvi[3]];
02744         if ( E.i == E.j )
02745           break;
02746         if ( E.i > E.j )
02747         {
02748           i = E.i; E.i = E.j; E.j = i;
02749           E.k = 1;
02750         }
02751         else
02752         {
02753           E.k = 0;
02754         }
02755         E_list[E_count++] = E;
02756       }
02757 
02758       E.i = Vid0;
02759       E.j = Vid[fvi[0]];
02760       if ( E.i == E.j )
02761         break;
02762       if ( E.i > E.j )
02763       {
02764         i = E.i; E.i = E.j; E.j = i;
02765         E.k = 1;
02766       }
02767       else
02768       {
02769         E.k = 0;
02770       }
02771       E_list[E_count++] = E;
02772     }
02773     if ( Vid != &Vidbuffer[0] )
02774       onfree(Vid);
02775 
02776     if ( E_count < 3 || j != Fcount )
02777     {
02778       ON_ERROR("Mesh is corrupt or collapsed");
02779       bClosedOnly = false;
02780       break;
02781     }
02782 
02783     // sort the the edges
02784     ON_hsort_3dex(E_list,E_count);
02785 
02786     // Look for duplicate edges.  If we find an edge with no duplicate,
02787     // then the mesh is open.  It is possible that degenerate meshes,
02788     // like a flattened box, will be flagged as closed.
02789     is_closed = (Fcount >= 4 && E_count >= 6) ? 1 : 0;
02790     is_oriented = 1;
02791     is_manifold = 1;
02792     i = -1;
02793     if ( !bClosedOnly || 1 == is_closed ) for ( i = 0; i < E_count; /*empty iterator*/ )
02794     {
02795       E = E_list[i];
02796       if ( ++i >= E_count )
02797       {
02798         // boundary edge (and the last edge in our list)
02799         is_closed = 0;
02800         break;
02801       }
02802 
02803       if ( E.i != E_list[i].i || E.j != E_list[i].j )
02804       {
02805         // boundary edge
02806         is_closed = 0;
02807         if ( 2 == is_oriented && 2 == is_manifold )
02808         {
02809           bClosedOnly = false;
02810           break;
02811         }
02812         if ( bClosedOnly )
02813           break; // don't spend time with further testing
02814         continue;
02815       }
02816 
02817       if ( E.k == E_list[i].k )
02818       {
02819         // opposite face normals along this edge - mesh is not oriented
02820         is_oriented = 2; 
02821       }
02822 
02823       if ( ++i >= E_count || E.i != E_list[i].i || E.j != E_list[i].j )
02824       {
02825         // two faces share this edge
02826         continue;
02827       }
02828 
02829       // three or more faces share this edge - mesh is not oriented manifold
02830       is_oriented = 2; 
02831       is_manifold = 2; 
02832       if ( 0 == is_closed )
02833       {
02834         bClosedOnly = false;
02835         break;
02836       }
02837 
02838       // Check for more faces sharing this edge.
02839       for ( i++; i < E_count; i++ )
02840       {
02841         if ( E.i != E_list[i].i || E.j != E_list[i].j )
02842         {
02843           // the edges E and Eid_list[i] are in different locations
02844           break;
02845         }
02846       }
02847     }
02848     if ( i >= E_count )
02849     {
02850       // is_manifold and is_oriented are set correctly
02851       bClosedOnly = false;
02852     }
02853 
02854     onfree(E_list);
02855 
02856     break;
02857   }
02858 
02859   const_cast<ON_Mesh&>(mesh).SetClosed(is_closed);
02860   if ( !bClosedOnly )
02861   {
02862     // is_manifold and is_oriented are set correctly
02863     if ( 2 == is_manifold )
02864       is_oriented = 2;
02865     const_cast<char&>(m_mesh_is_manifold) = is_manifold;
02866     const_cast<char&>(m_mesh_is_oriented) = is_oriented;
02867   }
02868 }
02869 
02870 bool ON_Mesh::IsClosed() const
02871 {
02872   if ( m_mesh_is_closed <= 0 || m_mesh_is_closed > 2) 
02873   {
02874     // thread safe lazy evaluation
02875     ON_Mesh_SetClosedHelper( true, *this, m_mesh_is_manifold, m_mesh_is_oriented );
02876   }
02877 
02878   return (1 == m_mesh_is_closed);
02879 }
02880 
02881 bool ON_Mesh::IsManifold() const
02882 {
02883   if ( m_mesh_is_manifold <= 0 || m_mesh_is_manifold > 2 )
02884   {
02885     // thread safe lazy evaluation
02886     ON_Mesh_SetClosedHelper( false, *this, m_mesh_is_manifold, m_mesh_is_oriented );
02887   }
02888   return (1 == m_mesh_is_manifold);
02889 }
02890 
02891 bool ON_Mesh::IsOriented() const
02892 {
02893   if ( m_mesh_is_oriented <= 0 || m_mesh_is_oriented > 2 )
02894   {
02895     // thread safe lazy evaluation
02896     ON_Mesh_SetClosedHelper( false, *this, m_mesh_is_manifold, m_mesh_is_oriented );
02897   }
02898   return (1 == m_mesh_is_oriented);
02899 }
02900 
02901 
02902 bool ON_Mesh::SetVertex(
02903        int vertex_index,
02904        const ON_3dPoint& vertex_location
02905        )
02906 {
02907   bool rc = false;
02908   int vertex_count = m_V.Count();
02909   if ( vertex_index >= 0 && vertex_index <= vertex_count )
02910   {
02911     bool bIsValid_fV = false;
02912     if ( HasDoublePrecisionVertices() )
02913     {
02914       bIsValid_fV = SinglePrecisionVerticesAreValid();
02915       ON_3dPointArray& dV = DoublePrecisionVertices();
02916       if ( vertex_count == dV.Count() )
02917       {
02918         bool bIsValid_dV = DoublePrecisionVerticesAreValid();
02919         if ( vertex_index < vertex_count ) 
02920           dV[vertex_index] = vertex_location;
02921         else
02922           dV.Append(vertex_location);
02923         if ( bIsValid_dV )
02924           SetDoublePrecisionVerticesAsValid();
02925       }
02926     }
02927     if ( vertex_index < vertex_count ) 
02928       m_V[vertex_index] = vertex_location;
02929     else
02930       m_V.Append(vertex_location);
02931     if ( bIsValid_fV )
02932       SetSinglePrecisionVerticesAsValid();
02933     rc = true;
02934   }
02935   return rc;
02936 }
02937 
02938 bool ON_Mesh::SetVertex(
02939        int vertex_index,
02940        const ON_3fPoint& vertex_location
02941        )
02942 {
02943   bool rc = false;
02944   int vertex_count = m_V.Count();
02945   if ( vertex_index >= 0 && vertex_index <= vertex_count )
02946   {
02947     bool bIsValid_fV = false;
02948     if ( HasDoublePrecisionVertices() )
02949     {
02950       bIsValid_fV = SinglePrecisionVerticesAreValid();
02951       ON_3dPointArray& dV = DoublePrecisionVertices();
02952       if ( vertex_count == dV.Count() )
02953       {
02954         bool bIsValid_dV = DoublePrecisionVerticesAreValid();
02955         if ( vertex_index < vertex_count ) 
02956           dV[vertex_index] = vertex_location;
02957         else
02958           dV.Append(vertex_location);
02959         if ( bIsValid_dV )
02960           SetDoublePrecisionVerticesAsValid();
02961       }
02962     }
02963     if ( vertex_index < vertex_count ) 
02964       m_V[vertex_index] = vertex_location;
02965     else
02966       m_V.Append(vertex_location);
02967     if ( bIsValid_fV )
02968       SetSinglePrecisionVerticesAsValid();
02969     rc = true;
02970   }
02971   return rc;
02972 }
02973 
02974 bool ON_Mesh::SetVertexNormal(
02975        int vertex_index,
02976        const ON_3dVector& normal
02977        )
02978 {
02979   bool rc = false;
02980   // use double precision for unitizing normal
02981   ON_3dVector unit_vector = normal;
02982   const bool bUnitVector = unit_vector.Unitize();
02983   ON_3fVector v((float)unit_vector.x, (float)unit_vector.y, (float)unit_vector.z);
02984   int normal_count = m_N.Count();
02985   if ( vertex_index >= 0 ) {
02986     if ( vertex_index < normal_count ) {
02987       m_N[vertex_index] = v;
02988       rc = bUnitVector;
02989     }
02990     else if ( vertex_index == normal_count ) {
02991       m_N.Append(v);
02992       rc = bUnitVector;
02993     }
02994   }
02995   return rc;
02996 }
02997 
02998 bool ON_Mesh::SetVertexNormal(
02999        int vertex_index,
03000        const ON_3fVector& normal
03001        )
03002 {
03003   ON_3dVector v(normal.x,normal.y,normal.z);
03004   return SetVertexNormal(vertex_index,v);
03005 }
03006 
03007 bool ON_Mesh::SetTextureCoord(
03008        int vertex_index,
03009        double s, double t    // texture coordinates
03010        )
03011 {
03012   ON_2fPoint tc((float)s,(float)t);
03013   bool rc = false;
03014   int vertex_count = m_T.Count();
03015   if ( vertex_index >= 0 ) {
03016     if ( vertex_index < vertex_count ) {
03017       m_T[vertex_index] = tc;
03018       rc = true;
03019     }
03020     else if ( vertex_index == vertex_count ) {
03021       m_T.Append(tc);
03022       rc = true;
03023     }
03024   }
03025   return rc;
03026 }
03027 
03028 
03029 bool ON_Mesh::SetTriangle(
03030        int face_index,
03031        int a,int b ,int c // vertex indices
03032        )
03033 {
03034   return SetQuad( face_index, a,b,c,c );
03035 }
03036 
03037 bool ON_Mesh::SetQuad(
03038        int face_index,
03039        int a, int b, int c, int d // vertex indices
03040        )
03041 {
03042   bool rc = false;
03043   int face_count = m_F.Count();
03044   if ( face_index >= 0 ) {
03045     ON_MeshFace f;
03046     f.vi[0] = a;
03047     f.vi[1] = b;
03048     f.vi[2] = c;
03049     f.vi[3] = d;
03050     if ( face_index < face_count ) {
03051       m_F[face_index] = f;
03052       rc = true;
03053     }
03054     else if ( face_index == face_count ) {
03055       m_F.Append(f);
03056       rc = true;
03057     }
03058     if ( rc )
03059       rc = f.IsValid(m_V.Count());
03060   }
03061   return rc;
03062 }
03063 
03064 
03065 
03066 int ON_Mesh::FaceCount() const
03067 {
03068   return m_F.Count();
03069 }
03070 
03071 int ON_Mesh::QuadCount() const
03072 {
03073   // number of faces that are quads
03074   if (   m_quad_count     < 0
03075       || m_triangle_count < 0 
03076       || m_invalid_count  < 0 
03077       || m_quad_count + m_triangle_count + m_invalid_count != FaceCount() ) 
03078   {
03079     const_cast<ON_Mesh*>(this)->CountQuads();
03080   }
03081   return m_quad_count;
03082 }
03083 
03084 int ON_Mesh::TriangleCount() const
03085 {
03086   // number of faces that are triangles
03087   QuadCount(); // makes sure counts are valid
03088   return m_triangle_count;
03089 }
03090 
03091 int ON_Mesh::InvalidFaceCount() const
03092 {
03093   // number of faces that are invalid
03094   QuadCount(); // makes sure counts are valid
03095   return m_invalid_count;
03096 }
03097 
03098 
03099 int ON_Mesh::VertexCount() const
03100 {
03101   return m_V.Count();
03102 }
03103 
03104 bool ON_Mesh::HasVertexNormals() const
03105 {
03106   const int vertex_count = VertexCount();
03107   return ( vertex_count > 0 && m_N.Count() == vertex_count ) ? true : false;
03108 }
03109 
03110 bool ON_Mesh::HasFaceNormals() const
03111 {
03112   const int face_count = FaceCount();
03113   return ( face_count > 0 && m_FN.Count() == face_count ) ? true : false;
03114 }
03115 
03116 bool ON_Mesh::HasTextureCoordinates() const
03117 {
03118   const int vertex_count = VertexCount();
03119   return ( vertex_count > 0 && m_T.Count() == vertex_count ) ? true : false;
03120 }
03121 
03122 bool ON_Mesh::HasCachedTextureCoordinates() const
03123 {
03124   const int vertex_count = VertexCount();
03125   if (vertex_count > 0 )
03126   {
03127     int tci, tccount = m_TC.Count();
03128     for ( tci = 0; tci < tccount; tci++ )
03129     {
03130       if ( vertex_count == m_TC[tci].m_T.Count() )
03131         return true;
03132     }
03133   }
03134   return false;
03135 }
03136 
03137 const ON_TextureCoordinates* 
03138 ON_Mesh::CachedTextureCoordinates( const ON_UUID& mapping_id ) const
03139 {
03140   const int vertex_count = VertexCount();
03141   if (vertex_count > 0 )
03142   {
03143     const ON_TextureCoordinates* TC = m_TC.Array();
03144     int tci, tccount = m_TC.Count();
03145     for ( tci = 0; tci < tccount; tci++ )
03146     {
03147       if (   vertex_count == TC->m_T.Count() 
03148           && mapping_id == TC->m_tag.m_mapping_id )
03149       {
03150         return TC;
03151       }
03152     }
03153   }
03154   return 0;
03155 }
03156 
03157 bool ON_Mesh::HasSurfaceParameters() const
03158 {
03159   const int vertex_count = VertexCount();
03160   return ( vertex_count > 0 && m_S.Count() == vertex_count ) ? true : false;
03161 }
03162 
03163 bool ON_Mesh::HasPrincipalCurvatures() const
03164 {
03165   const int vertex_count = VertexCount();
03166   return ( vertex_count > 0 && m_K.Count() == vertex_count ) ? true : false;
03167 }
03168 
03169 bool ON_Mesh::HasVertexColors() const
03170 {
03171   const int vertex_count = VertexCount();
03172   return ( vertex_count > 0 && m_C.Count() == vertex_count ) ? true : false;
03173 }
03174 
03175 void ON_Mesh::InvalidateBoundingBoxes()
03176 {
03177   InvalidateVertexBoundingBox();
03178   InvalidateVertexNormalBoundingBox();
03179   InvalidateTextureCoordinateBoundingBox();
03180   InvalidateCurvatureStats();
03181 }
03182 
03183 void ON_Mesh::InvalidateVertexBoundingBox()
03184 {
03185   m_vbox[0][0] = m_vbox[0][1] = m_vbox[0][2] =  1.0;
03186   m_vbox[1][0] = m_vbox[1][1] = m_vbox[1][2] = -1.0;
03187 }
03188 
03189 void ON_Mesh::InvalidateVertexNormalBoundingBox()
03190 {
03191   m_nbox[0][0] = m_nbox[0][1] = m_nbox[0][2] =  1.0;
03192   m_nbox[1][0] = m_nbox[1][1] = m_nbox[1][2] = -1.0;
03193 }
03194 
03195 void ON_Mesh::InvalidateTextureCoordinateBoundingBox()
03196 {
03197   m_tbox[0][0] = m_tbox[0][1] =  1.0;
03198   m_tbox[1][0] = m_tbox[1][1] = -1.0;
03199 }
03200 
03201 void ON_Mesh::InvalidateCurvatureStats()
03202 {
03203   int i;
03204   for ( i = 0; i < 4; i++ ) {
03205     if ( m_kstat[i] ) {
03206       delete m_kstat[i];
03207       m_kstat[i] = 0;
03208     }
03209   }
03210 }
03211 
03212 bool ON_Mesh::UnitizeVertexNormals()
03213 {
03214   bool rc = HasVertexNormals();
03215   if ( rc ) {
03216     const int vertex_count = VertexCount();
03217     float* n = &m_N[0][0];
03218     int i;
03219     ON_3dVector N;
03220     for ( i = 0; i < vertex_count; i++ ) {
03221       N.x = n[0];
03222       N.y = n[1];
03223       N.z = n[2];
03224       if ( !N.Unitize() )
03225         rc = false;
03226       *n++ = (float)N.x;
03227       *n++ = (float)N.y;
03228       *n++ = (float)N.z;
03229     }
03230   }
03231   return rc;
03232 }
03233 
03234 bool ON_Mesh::UnitizeFaceNormals()
03235 {
03236   bool rc = HasFaceNormals();
03237   if ( rc ) {
03238     const int face_count = FaceCount();
03239     float* n = &m_FN[0][0];
03240     int i;
03241     ON_3dVector N;
03242     for ( i = 0; i < face_count; i++ ) {
03243       N.x = n[0];
03244       N.y = n[1];
03245       N.z = n[2];
03246       if ( !N.Unitize() )
03247         rc = false;
03248       *n++ = (float)N.x;
03249       *n++ = (float)N.y;
03250       *n++ = (float)N.z;
03251     }
03252   }
03253   return rc;
03254 }
03255 
03256 
03257 bool ON_Mesh::GetCurvatureStats( // returns true if successful
03258        ON::curvature_style kappa_style,
03259        ON_MeshCurvatureStats& stats
03260        ) const
03261 {
03262   bool rc = false;
03263   stats.Destroy();
03264   int ksi;
03265   switch ( kappa_style ) {
03266     case ON::gaussian_curvature:
03267       ksi = 0;
03268       break;
03269     case ON::mean_curvature:
03270       ksi = 1;
03271       break;
03272     case ON::min_curvature: // minimum unsigned radius of curvature
03273       ksi = 2;
03274       break;
03275     case ON::max_curvature: // maximum unsigned radius of curvature
03276       ksi = 3;
03277       break;
03278     //case ON::section_curvature_x:
03279     //  ksi = 4;
03280     //  break;
03281     //case ON::section_curvature_y:
03282     //  ksi = 5;
03283     //  break;
03284     //case ON::section_curvature_z:
03285     //  ksi = 6;
03286     //  break;
03287     default:
03288       ksi = -1;
03289       break;
03290   }
03291   if ( ksi >= 0 && ksi <= 3 && HasPrincipalCurvatures() ) {
03292     ON_Mesh* p = (ON_Mesh*)this; // const lie 
03293     if ( !m_kstat[ksi] ) {
03294       p->m_kstat[ksi] = new ON_MeshCurvatureStats();
03295       p->m_kstat[ksi]->Set( kappa_style, m_K.Count(), m_K.Array(), m_N.Array() );
03296     }
03297     if ( p->m_kstat[ksi] ) {
03298       stats = *p->m_kstat[ksi];
03299       rc = true;
03300     }
03301   }
03302   return rc;
03303 }
03304 
03305 int ON_MeshTopology::WaitUntilReady(int sleep_value) const
03306 {
03307   return m_b32IsValid;
03308 }
03309 
03310 
03311 bool ON_Mesh::TopologyExists() const
03312 {
03313   return (1 == m_top.WaitUntilReady(0));
03314 }
03315 
03316 const ON_MeshTopology& ON_Mesh::Topology() const
03317 {
03318   int top_b32IsValid =  m_top.WaitUntilReady(-1);
03319 
03320   if ( 0 == top_b32IsValid ) 
03321   {
03322     ON_MeshTopology& top = const_cast<ON_MeshTopology&>(m_top);
03323     top.m_mesh = this;
03324     top_b32IsValid = top.Create() ? 1 : 0;
03325     top.m_b32IsValid = top_b32IsValid;
03326   }
03327 
03328   return m_top;
03329 }
03330 
03331 void ON_Mesh::DestroyTopology()
03332 {
03333   m_top.Destroy();
03334 }
03335 
03336 bool
03337 ON_MeshFace::IsTriangle() const 
03338 {
03339   return vi[2]==vi[3];
03340 }
03341 
03342 bool
03343 ON_MeshFace::IsQuad() const 
03344 {
03345   return vi[2]!=vi[3];
03346 }
03347 
03348 void
03349 ON_MeshFace::Flip()
03350 {
03351   int i;
03352   if ( vi[2] == vi[3] ) {
03353     i = vi[1];
03354     vi[1] = vi[2];
03355     vi[2] = i;
03356     vi[3] = i;
03357   }
03358   else {
03359     i = vi[1];
03360     vi[1] = vi[3];
03361     vi[3] = i;
03362   }
03363 }
03364 
03365 /*
03366 ON_MeshEdge::ON_MeshEdge() : m_fcount(0)
03367 {
03368   m_fi = m_private_fi;
03369   m_private_fi[0] = 0;
03370   m_private_fi[0] = 0;
03371 }
03372 
03373 ON_MeshEdge::~ON_MeshEdge()
03374 {
03375   if ( m_fi && m_fi != m_private_fi ) onfree((void*)m_fi);
03376 }
03377 
03378 ON_MeshEdge::ON_MeshEdge( const ON_MeshEdge& src ) : m_fcount(0), m_fi(0)
03379 {
03380   *this = src;
03381 }
03382 
03383 int ON_MeshEdge::FaceIndexCount() const
03384 {
03385   return m_fcount;
03386 }
03387 
03388 const int* ON_MeshEdge::FaceIndexArray() const
03389 {
03390   return m_fi;
03391 }
03392 
03393 int& ON_MeshEdge::operator[](int i)
03394 {
03395   return (i>=0&&i<m_fcount) ? m_fi[i] : m_private_fi[0];
03396 }
03397 
03398 int ON_MeshEdge::operator[](int i) const
03399 {
03400   return (i>=0&&i<m_fcount) ? m_fi[i] : m_private_fi[0];
03401 }
03402 */
03403 
03404 void 
03405 ON_Mesh::Flip()
03406 {
03407   FlipFaceOrientation();
03408   FlipFaceNormals();
03409   FlipVertexNormals();
03410 
03411   // Do not modify m_S[] or m_T[]
03412   // values here.
03413 }
03414 
03415 void 
03416 ON_Mesh::FlipVertexNormals()
03417 {
03418   int i;
03419   const int vcount = VertexCount();
03420   if ( HasVertexNormals() ) {
03421     for ( i = 0; i < vcount; i++ ) {
03422       m_N[i].Reverse();
03423     }
03424   }
03425 }
03426 
03427 void 
03428 ON_Mesh::FlipFaceNormals()
03429 {
03430   int i;
03431   const int fcount = FaceCount();
03432   if ( HasFaceNormals() ) {
03433     for( i = 0; i < fcount; i++ ) {
03434       m_FN[i].Reverse();
03435     }
03436   }
03437 }
03438 
03439 void 
03440 ON_Mesh::FlipFaceOrientation()
03441 {
03442   int i;
03443   const int fcount = FaceCount();
03444   for( i = 0; i < fcount; i++ ) {
03445     m_F[i].Flip();
03446   }
03447   if ( fcount > 0 )
03448     DestroyTopology(); // flipping changes order of face corners
03449 }
03450 
03451 bool ON_MeshFace::ComputeFaceNormal( const ON_3dPoint* dV, ON_3dVector& FN ) const
03452 {
03453   if ( 0 != dV )
03454   {
03455     ON_3dVector a = dV[vi[2]] - dV[vi[0]];
03456     ON_3dVector b = dV[vi[3]] - dV[vi[1]];
03457     FN = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads
03458     if ( FN.Unitize() )
03459       return true;
03460   }
03461 
03462   FN.Zero();
03463   return false;
03464 }
03465 
03466 bool ON_MeshFace::ComputeFaceNormal( const ON_3fPoint* fV, ON_3dVector& FN ) const
03467 {
03468   if ( 0 != fV )
03469   {
03470     ON_3dVector a = fV[vi[2]] - fV[vi[0]];
03471     ON_3dVector b = fV[vi[3]] - fV[vi[1]];
03472     FN = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads
03473     if ( FN.Unitize() )
03474       return true;
03475   }
03476 
03477   FN.Zero();
03478   return false;
03479 }
03480 
03481 bool
03482 ON_Mesh::ComputeFaceNormal( int fi )
03483 {
03484   if ( fi < 0 )
03485     return false;
03486   if ( fi >= m_F.Count() )
03487     return false;
03488   if ( m_FN.Count() != m_F.Count() )
03489     return false;
03490   
03491   ON_3dVector FN;
03492   bool rc = ( HasDoublePrecisionVertices() )
03493           ? m_F[fi].ComputeFaceNormal(DoublePrecisionVertices().Array(),FN)
03494           : m_F[fi].ComputeFaceNormal(m_V.Array(),FN);
03495   
03496   m_FN[fi] = FN;
03497 
03498   return rc;
03499 }
03500 
03501 bool
03502 ON_Mesh::ComputeFaceNormals()
03503 {
03504   bool rc = false;
03505   const int fcount = FaceCount();
03506   if ( fcount > 0 )
03507   {
03508     ON_3dVector a, b, n;
03509     int fi;
03510     const int* vi;
03511     if ( m_FN.Capacity() < fcount )
03512       m_FN.SetCapacity(fcount);
03513     m_FN.SetCount(0);
03514     rc = true;
03515     if ( HasDoublePrecisionVertices() && DoublePrecisionVerticesAreValid() )
03516     {      
03517       const ON_3dPointArray& dV = DoublePrecisionVertices();
03518       for ( fi = 0; fi < fcount; fi++ ) {
03519         vi = m_F[fi].vi;
03520         a = dV[vi[2]] - dV[vi[0]];
03521         b = dV[vi[3]] - dV[vi[1]];
03522         n = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads
03523         n.Unitize();
03524         m_FN.Append(n);
03525       }
03526     }
03527     else
03528     {
03529       for ( fi = 0; fi < fcount; fi++ ) {
03530         vi = m_F[fi].vi;
03531         a = m_V[vi[2]] - m_V[vi[0]];
03532         b = m_V[vi[3]] - m_V[vi[1]];
03533         n = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads
03534         n.Unitize();
03535         m_FN.Append(n);
03536       }
03537     }
03538   }
03539   else 
03540   {
03541     m_FN.Destroy();
03542   }
03543   return rc;
03544 }
03545 
03546 
03547 //static int compareRadial3fPoint( const ON_3fPoint* a, const ON_3fPoint* b )
03548 //{
03549 //  double ar = a->x+a->y+a->z;
03550 //  double br = b->x+b->y+b->z;
03551 //  if ( ar < br )
03552 //    return -1;
03553 //  if ( ar > br )
03554 //    return 1;
03555 //  return 0;
03556 //}
03557 
03558 bool ON_Mesh::CombineCoincidentVertices( 
03559         const ON_3fVector tolerance,
03560         double cos_normal_angle // = -1.0  // cosine(break angle) -1.0 will merge all coincident vertices
03561         )
03562 {
03563   // TODO - If you need this function, please ask Dale Lear to finish it.
03564   //bool rc = false;
03565   //const int vcount = VertexCount();
03566   //if ( vcount > 0 && rc ) {
03567   //  ON_Workspace ws;
03568   //  int* index = ws.GetIntMemory(vcount);
03569   //  rc = m_V.Sort( ON::quick_sort, index, compareRadial3fPoint );
03570   //  int i, j;
03571   //  ON_3fPoint p0, p1, pmin, pmax;
03572   //  for ( i = 0; i < vcount; i++ ) {
03573   //    p0 = m_V[i];
03574   //    pmin = p0 - tolerance;
03575   //    pmax = p0 + tolerance;
03576   //    for ( j = i+1; j < vcount; j++ ) {
03577   //      p1 = m_V[j];
03578   //      // TODO        
03579   //    }
03580   //  }
03581   //}
03582   return false;
03583 }
03584 
03585 
03586 struct tagMESHPOINTS
03587 {
03588   // p0 = bogus pointer - never dereferenced - that is used
03589   //      to calculate vertex index in CompareMeshPoint().
03590   const char* p0;
03591   ON_3fPoint*  V;
03592   ON_2fPoint*  T;
03593   ON_3fVector* N;
03594   ON_SurfaceCurvature* K;
03595   ON_Color* C;
03596 };
03597 
03598 static int CompareMeshPoint(const void* a,const void* b,void* ptr)
03599 {
03600   float d;
03601   const struct tagMESHPOINTS * mp = (const struct tagMESHPOINTS *)ptr;
03602 
03603   // use bogus pointer to convert a,b into vertex indices
03604   int i = (int)(((const char*)a) - mp->p0); // the (int) is for 64 bit size_t conversion
03605   int j = (int)(((const char*)b) - mp->p0);
03606 
03607   d = mp->V[j].x - mp->V[i].x;
03608   if ( d == 0.0f )
03609   {
03610     d = mp->V[j].y - mp->V[i].y;
03611     if ( d == 0.0f )
03612     {
03613       d = mp->V[j].z - mp->V[i].z;
03614 
03615       //if ( d == 0.0f )
03616       //  return 0;
03617 
03618       if ( d == 0.0f && 0 != mp->N)
03619       {
03620         d = mp->N[j].x - mp->N[i].x;
03621         if ( d == 0.0f )
03622         {
03623           d = mp->N[j].y - mp->N[i].y;
03624           if ( d == 0.0f )
03625           {
03626             d = mp->N[j].z - mp->N[i].z;
03627           }
03628         }
03629       }
03630 
03631       if ( d == 0.0f && 0 != mp->T)
03632       {
03633         d = mp->T[j].x - mp->T[i].x;
03634         if ( d == 0.0f )
03635         {
03636           d = mp->T[j].y - mp->T[i].y;
03637         }
03638       }
03639 
03640       if ( d == 0.0f && 0 != mp->C )
03641       {
03642         int u = ((int)mp->C[j])-((int)mp->C[i]);
03643         if ( u < 0 )
03644           d = -1.0f;
03645         else if ( u > 0 )
03646           d = 1.0f;
03647       }
03648 
03649       if ( d == 0.0f && 0 != mp->K )
03650       {
03651         double dk = mp->K[j].k1 - mp->K[i].k1;
03652         if ( dk < 0.0 )
03653           d = -1.0;
03654         else if ( dk > 0.0 )
03655           d = 1.0;
03656         else
03657         {
03658           dk = mp->K[j].k2 - mp->K[i].k2;
03659           if ( dk < 0.0 )
03660             d = -1.0;
03661           else if ( dk > 0.0 )
03662             d = 1.0;
03663         }
03664       }
03665     }
03666   }
03667   
03668   if ( d < 0.0f )
03669     return -1;
03670   if ( d > 0.0f )
03671     return 1;
03672   return 0;
03673 }
03674 
03675 bool ON_Mesh::CombineIdenticalVertices(
03676                                 bool bIgnoreVertexNormals,
03677                                 bool bIgnoreTextureCoordinates
03678                                 )
03679 {
03680   // 11 June 2003 - added and tested.
03681   bool rc = false;
03682   ON_Mesh& mesh = *this;
03683 
03684   int vertex_count = mesh.VertexCount();
03685   if ( vertex_count > 0 )
03686   {
03687     ON_SimpleArray<int> index_array(vertex_count);
03688     ON_SimpleArray<int> remap_array(vertex_count);
03689 
03690     int remap_vertex_count = 0;
03691     int merge_count = 0;
03692     int i0, i1, k;
03693 
03694     struct tagMESHPOINTS mp;
03695     memset(&mp,0,sizeof(mp));
03696     mp.p0 = (const char*)&mp; // bogus pointer - never dereferenced
03697     mp.V = mesh.m_V.Array();
03698     mp.N = mesh.HasVertexNormals()       ? mesh.m_N.Array() : 0;
03699     mp.T = mesh.HasTextureCoordinates()  ? mesh.m_T.Array() : 0;
03700     mp.C = mesh.HasVertexColors()        ? mesh.m_C.Array() : 0;
03701     mp.K = mesh.HasPrincipalCurvatures() ? mesh.m_K.Array() : 0;
03702 
03703     if ( bIgnoreVertexNormals )
03704     {
03705       mp.N = 0;
03706     }
03707 
03708     if ( bIgnoreTextureCoordinates )
03709     {
03710       mp.T = 0;
03711       mp.C = 0;
03712       mp.K = 0;
03713     }
03714 
03715     index_array.SetCount(vertex_count);
03716     index_array.Zero();
03717     int* index = index_array.Array();
03718 
03719     remap_array.SetCount(vertex_count);
03720     int* remap = remap_array.Array();
03721     for ( k = 0; k < vertex_count; k++ )
03722       remap[k] = -1;
03723 
03724     ON_Sort( 
03725           ON::quick_sort,
03726           index,
03727           mp.p0,                 // data buffer
03728           vertex_count,
03729           sizeof(*mp.p0),
03730           CompareMeshPoint,
03731           &mp
03732          );
03733 
03734     for ( i0 = 0; i0 < vertex_count; i0 = i1 )
03735     {
03736       for ( i1 = i0+1; i1 < vertex_count; i1++ )
03737       {
03738         if ( CompareMeshPoint( mp.p0+index[i0], mp.p0+index[i1], &mp ) )
03739           break;
03740         else
03741           merge_count++;
03742       }
03743       for ( /*empty*/; i0 < i1; i0++ )
03744       {
03745         remap[index[i0]] = remap_vertex_count;
03746       }
03747       remap_vertex_count++;
03748     }
03749 
03750     if ( bIgnoreVertexNormals )
03751     {
03752       mp.N = mesh.HasVertexNormals() ? mesh.m_N.Array() : 0;
03753     }
03754 
03755     if ( bIgnoreTextureCoordinates )
03756     {
03757       mp.T = mesh.HasTextureCoordinates()  ? mesh.m_T.Array() : 0;
03758       mp.C = mesh.HasVertexColors()        ? mesh.m_C.Array() : 0;
03759       mp.K = mesh.HasPrincipalCurvatures() ? mesh.m_K.Array() : 0;
03760     }
03761 
03762     if ( remap_vertex_count > 0 && remap_vertex_count < vertex_count )
03763     {
03764       ON_SimpleArray<ON_3fPoint> p_array(remap_vertex_count);
03765       p_array.SetCount(remap_vertex_count);
03766       ON_3fPoint* p = p_array.Array();
03767       ON_3fVector* v = (ON_3fVector*)p;
03768 
03769       for ( k = 0; k < vertex_count; k++ )
03770       {
03771         p[remap[k]] = mp.V[k];
03772       }
03773       for ( k = 0; k < remap_vertex_count; k++ )
03774         mp.V[k] = p[k];
03775       mesh.m_V.SetCount(remap_vertex_count);
03776 
03777       if ( 0 != mp.N )
03778       {
03779         if ( bIgnoreVertexNormals )
03780         {
03781           // average vertex normals of combined vertices
03782           p_array.Zero();
03783           for ( k = 0; k < vertex_count; k++ )
03784           {
03785             v[remap[k]] += mp.N[k];
03786           }
03787           for ( k = 0; k < remap_vertex_count; k++ )
03788           {
03789             v[k].Unitize();
03790           }
03791         }
03792         else
03793         {
03794           for ( k = 0; k < vertex_count; k++ )
03795           {
03796             v[remap[k]] = mp.N[k];
03797           }
03798         }
03799         for ( k = 0; k < remap_vertex_count; k++ )
03800           mp.N[k] = v[k];
03801         mesh.m_N.SetCount(remap_vertex_count);
03802       }
03803       else
03804         mesh.m_N.SetCount(0);
03805 
03806       if ( 0 != mp.T && !bIgnoreTextureCoordinates )
03807       {
03808         for ( k = 0; k < vertex_count; k++ )
03809         {
03810           p[remap[k]] = mp.T[k];
03811         }
03812         for ( k = 0; k < remap_vertex_count; k++ )
03813           mp.T[k] = p[k];
03814         mesh.m_T.SetCount(remap_vertex_count);
03815       }
03816       else
03817         mesh.m_T.SetCount(0);
03818 
03819       if ( 0 != mp.C && !bIgnoreTextureCoordinates )
03820       {
03821         ON_SimpleArray<ON_Color> c_array(remap_vertex_count);
03822         c_array.SetCount(remap_vertex_count);
03823         ON_Color* c = c_array.Array();
03824         for ( k = 0; k < vertex_count; k++ )
03825         {
03826           c[remap[k]] = mp.C[k];
03827         }
03828         for ( k = 0; k < remap_vertex_count; k++ )
03829           mp.C[k] = c[k];
03830         mesh.m_C.SetCount(remap_vertex_count);
03831       }
03832       else
03833         mesh.m_C.SetCount(0);
03834 
03835       if ( 0 != mp.K && !bIgnoreTextureCoordinates )
03836       {
03837         ON_SimpleArray<ON_SurfaceCurvature> s_array(remap_vertex_count);
03838         s_array.SetCount(remap_vertex_count);
03839         ON_SurfaceCurvature* s = s_array.Array();
03840         for ( k = 0; k < vertex_count; k++ )
03841         {
03842           s[remap[k]] = mp.K[k];
03843         }
03844         for ( k = 0; k < remap_vertex_count; k++ )
03845           mp.K[k] = s[k];
03846         mesh.m_K.SetCount(remap_vertex_count);
03847       }
03848       else
03849         mesh.m_K.SetCount(0);
03850 
03851       const int face_count = mesh.m_F.Count();
03852       ON_MeshFace* f = mesh.m_F.Array();
03853       int* fvi;
03854       for ( k = 0; k < face_count; k++ )
03855       {
03856         fvi = f[k].vi;
03857         fvi[0] = remap[fvi[0]];
03858         fvi[1] = remap[fvi[1]];
03859         fvi[2] = remap[fvi[2]];
03860         fvi[3] = remap[fvi[3]];
03861       }
03862 
03863       if (0 != NgonList())
03864       {
03865         //This mesh has an ngon list.
03866         //Modify the vertex indexes in the ngons as 
03867         //we did the faces above
03868         ON_MeshNgonList* ngonlist = ModifyNgonList();
03869         int kk, kkct = ngonlist->NgonCount();
03870         for (kk = 0; kk < kkct; kk++)
03871         {
03872           ON_MeshNgon* ngon = ngonlist->Ngon(kk);
03873           if (0 == ngon)
03874             continue;
03875           for ( k = 0; k < ngon->N; k++ )
03876             ngon->vi[k] = remap[ngon->vi[k]];
03877         }
03878       }
03879 
03880       mesh.DestroyPartition();
03881       mesh.DestroyTopology();
03882 
03883       if ( mesh.m_V.Capacity() > 4*mesh.m_V.Count() && mesh.m_V.Capacity() > 50 )
03884       {
03885         // There is lots of unused memory in the dynamic arrays.
03886         // Release what we can.
03887         mesh.Compact();
03888       }
03889       rc = true;
03890     }
03891   }
03892   return rc;
03893 }
03894 
03895 void ON_Mesh::Append( int mesh_count, const ON_Mesh* const* meshes )
03896 {
03897   if ( mesh_count <= 0 || 0 == meshes )
03898     return;
03899 
03900   int mi;
03901   int vcount0 = VertexCount();
03902   if ( vcount0 <= 0 )
03903     m_F.SetCount(0);
03904   int fcount0 = FaceCount();
03905   int* vi;
03906   int j;
03907   const ON_Mesh* m;
03908 
03909   // The calls to Has*() must happen before the m_V[] and m_F[] arrays get enlarged
03910   // Allow the appendage of VertexNormals, TextureCoordinates, PrincipalCurvatures to empty meshes
03911   // by checking for 0 == vcount0 && 0 == fcount0
03912   bool bHasVertexNormals       = (0 == vcount0 || HasVertexNormals());
03913   bool bHasFaceNormals         = (0 == vcount0 || 0 == fcount0 || HasFaceNormals());
03914   bool bHasTextureCoordinates  = (0 == vcount0 || HasTextureCoordinates());
03915   bool bHasPrincipalCurvatures = (0 == vcount0 || HasPrincipalCurvatures());
03916   bool bHasVertexColors        = (0 == vcount0 || HasVertexColors());
03917   bool bHasSurfaceParameters   = (0 == vcount0 || HasSurfaceParameters());
03918   bool bHasDoubles             = (0 == vcount0 || HasSynchronizedDoubleAndSinglePrecisionVertices());
03919 
03920   int fcount = fcount0;
03921   int vcount = vcount0;
03922   for ( mi = 0; mi < mesh_count; mi++ )
03923   {
03924     m = meshes[mi];
03925     if ( 0 == m )
03926       continue;
03927     int vcount1 = m->m_V.Count();
03928     if ( vcount1 <= 0 )
03929       continue;
03930     int fcount1 = m->m_F.Count();
03931     if ( fcount1 > 0 )
03932       fcount += fcount1;
03933     vcount += vcount1;
03934     if ( bHasVertexNormals && !m->HasVertexNormals() )
03935       bHasVertexNormals = false;
03936     if ( bHasTextureCoordinates && !m->HasTextureCoordinates())
03937       bHasTextureCoordinates = false;
03938     if ( bHasPrincipalCurvatures && !m->HasPrincipalCurvatures())
03939       bHasPrincipalCurvatures = false;
03940     if ( bHasVertexColors && !m->HasVertexColors())
03941       bHasVertexColors = false;
03942     if ( bHasSurfaceParameters && !m->HasSurfaceParameters())
03943       bHasSurfaceParameters = false;
03944     if ( bHasDoubles && !m->HasSynchronizedDoubleAndSinglePrecisionVertices())
03945       bHasDoubles = false;
03946     if ( bHasFaceNormals && fcount1 > 0 && !m->HasFaceNormals() )
03947       bHasFaceNormals = false;
03948   }
03949 
03950   if ( vcount <= vcount0 && fcount <= fcount0 )
03951     return;
03952 
03953   m_top.Destroy();
03954 
03955   // It is critical to call DoublePrecisionVertices() before 
03956   // we modify m_V[] because DoublePrecisionVertices() will
03957   // attempt to update the double precision information
03958   // when it notices that m_V has new vertices added.
03959   ON_MeshDoubleVertices* dv = 0;
03960   if ( bHasDoubles )
03961   {
03962     dv = ON_MeshDoubleVertices::Get(this);
03963     if ( 0 == dv && 0 == m_V.Count() )
03964     {
03965       DoublePrecisionVertices(); // creates ON_MeshDoubleVertices info on "this"
03966       dv = ON_MeshDoubleVertices::Get(this);
03967     }
03968     if ( 0 == dv )
03969       bHasDoubles = false;
03970   }
03971 
03972   m_V.Reserve(vcount);
03973   m_F.Reserve(fcount);
03974   for (mi = 0; mi < mesh_count; mi++ )
03975   {
03976     m = meshes[mi];
03977     if ( 0 == m )
03978       continue;
03979     int vcount1 = m->m_V.Count();
03980     if ( vcount1 <= 0 )
03981       continue;
03982     int fcount1 = m->m_F.Count();
03983     if ( fcount1 > 0 )
03984     {
03985       int vc0 = m_V.Count();
03986       j = m_F.Count();
03987       m_F.Append(fcount1,m->m_F.Array());
03988       fcount1 += j;
03989       while (j < fcount1)
03990       {
03991         vi = m_F[j].vi;
03992         vi[0] += vc0;
03993         vi[1] += vc0;
03994         vi[2] += vc0;
03995         vi[3] += vc0;
03996         j++;
03997       }
03998     }
03999     m_V.Append(vcount1,m->m_V.Array());
04000   }
04001 
04002   if ( bHasDoubles)
04003   {
04004     // Now update the double precision vertex locations.
04005     dv->m_dV.Reserve(vcount);
04006     for (mi = 0; mi < mesh_count; mi++ )
04007     {
04008       m = meshes[mi];
04009       if ( 0 == m || m->m_V.Count() <= 0 )
04010         continue;
04011       ON_MeshDoubleVertices* dv1 = ON_MeshDoubleVertices::Get(m);
04012       if ( 0 == dv1 )
04013         break;
04014       dv->m_dV.Append(dv1->m_dV.Count(),dv1->m_dV.Array());
04015     }
04016     if ( dv->m_dV.Count() == vcount )
04017     {
04018       // The calculation of the CRCs in the following calls
04019       // is what slows down appending meshes one-by-one
04020       // when thousands of meshes are being appended.
04021       // That's why the code is structured to do the calculation
04022       // a single time here.
04023       SetSinglePrecisionVerticesAsValid();
04024       SetDoublePrecisionVerticesAsValid();
04025     }
04026     else
04027     {
04028       DestroyDoublePrecisionVertices();
04029     }
04030   }
04031   else
04032   {
04033     DestroyDoublePrecisionVertices();
04034   }
04035 
04036 
04037   if ( bHasVertexNormals ) 
04038   {
04039     m_N.Reserve(vcount);
04040     for (mi = 0; mi < mesh_count; mi++ )
04041     {
04042       m = meshes[mi];
04043       if ( 0 == m )
04044         continue;
04045       m_N.Append(m->m_N.Count(), m->m_N.Array());
04046     }
04047   }
04048   else
04049   {
04050     m_N.Destroy();
04051   }
04052 
04053 
04054   if ( bHasFaceNormals ) 
04055   {
04056     m_FN.Reserve(fcount);
04057     for (mi = 0; mi < mesh_count; mi++ )
04058     {
04059       m = meshes[mi];
04060       if ( 0 == m || m->m_V.Count() <= 0 )
04061         continue;
04062       m_FN.Append(m->m_FN.Count(), m->m_FN.Array());
04063     }
04064   }
04065   else
04066   {
04067     m_FN.Destroy();
04068   }
04069 
04070   if ( bHasTextureCoordinates ) 
04071   {
04072     m_T.Reserve(vcount);
04073     for (mi = 0; mi < mesh_count; mi++ )
04074     {
04075       m = meshes[mi];
04076       if ( 0 == m )
04077         continue;
04078       m_T.Append(m->m_T.Count(), m->m_T.Array());
04079     }
04080   }
04081   else 
04082   {
04083     m_T.Destroy();
04084   }
04085 
04086 
04087   if ( bHasSurfaceParameters )
04088   {
04089     m_S.Reserve(vcount);
04090     for (mi = 0; mi < mesh_count; mi++ )
04091     {
04092       m = meshes[mi];
04093       if ( 0 == m )
04094         continue;
04095       m_S.Append(m->m_S.Count(), m->m_S.Array());
04096     }
04097   }
04098   else 
04099   {
04100     m_S.Destroy();
04101   }
04102 
04103   if ( bHasPrincipalCurvatures )
04104   {
04105     m_K.Reserve(vcount);
04106     for (mi = 0; mi < mesh_count; mi++ )
04107     {
04108       m = meshes[mi];
04109       if ( 0 == m )
04110         continue;
04111       m_K.Append(m->m_K.Count(), m->m_K.Array());
04112     }
04113   }
04114   else
04115   {
04116     m_K.Destroy();
04117   }
04118 
04119   if ( bHasVertexColors ) 
04120   {
04121     m_C.Reserve(vcount);
04122     for (mi = 0; mi < mesh_count; mi++ )
04123     {
04124       m = meshes[mi];
04125       if ( 0 == m )
04126         continue;
04127       m_C.Append(m->m_C.Count(), m->m_C.Array());
04128     }
04129   }
04130   else 
04131   {
04132     m_C.Destroy();
04133   }
04134 
04135   if ( 0 !=  m_mesh_parameters )
04136   {
04137     for (mi = 0; mi < mesh_count; mi++ )
04138     {
04139       m = meshes[mi];
04140       if ( 0 == m )
04141         continue;
04142       if ( 0 == m->m_mesh_parameters || *m_mesh_parameters != *m->m_mesh_parameters )
04143       {
04144         delete m_mesh_parameters;
04145         m_mesh_parameters = 0;
04146         break;
04147       }
04148     }
04149   }
04150 
04151   for ( j = 0; j < 4; j++ ) 
04152   {
04153     if ( m_kstat[j] ) 
04154     {
04155       // will be recomputed if required
04156       delete m_kstat[j];
04157       m_kstat[j] = 0;
04158     }
04159   }
04160 
04161   SetClosed(-99);
04162   SetSolidOrientation(-99);
04163   InvalidateBoundingBoxes();
04164 }
04165 
04166 void ON_Mesh::Append( const ON_Mesh& m )
04167 { 
04168   const ON_Mesh* meshes[1];
04169   meshes[0] = &m;
04170   Append(1,meshes);
04171 }
04172 
04173 ON_MeshParameters::ON_MeshParameters()
04174 {
04175   Default();
04176 }
04177 
04178 ON_MeshParameters::~ON_MeshParameters()
04179 {
04180 }
04181 
04182 double ON_MeshParameters::MinEdgeLength( double max_edge_length, double tolerance )
04183 {
04184   // The 1.0e-4 is a guess.  1.0e-12 was too small to help with objects
04185   // like the one in 67885.
04186   double min_edge_length = 1.0e-4;
04187   if ( max_edge_length > 0.0 && min_edge_length > 1.0e-3*max_edge_length )
04188     min_edge_length =  1.0e-3*max_edge_length;
04189   if ( tolerance > 0.0 && min_edge_length > 1.0e-2*tolerance )
04190     min_edge_length =  1.0e-2*tolerance;
04191   return min_edge_length;
04192 }
04193 
04194 double ON_MeshParameters::Tolerance( double density, double actual_size )
04195 {
04196   // min_rel_tol creates the most facets
04197   //const double min_rel_tol = 5.0e-5;
04198 
04199   //max rel tol creates the fewest facets
04200   //const double max_rel_tol = 0.1;
04201   //m_relative_tolerance = 0.0;
04202   //if ( density <= 0.0 )
04203   //  m_relative_tolerance = max_rel_tol;
04204   //else if ( density >= 1.0 )
04205   //  m_relative_tolerance = min_rel_tol;
04206   //else
04207   //{
04208   //  ON_Interval e(log10(max_rel_tol),log10(min_rel_tol));
04209   //  m_relative_tolerance = pow(10.0,e.ParameterAt(density));
04210   //}
04211 
04212   double tol = 0.0;
04213   double x, e;
04214   if ( ON_IsValid(density) && ON_IsValid(actual_size)
04215        && density > 0.0 && actual_size > 0.0 )
04216   {
04217     if ( density > 1.0 )
04218       density = 1.0;
04219 
04220     //e = (density*(12.0 + density*(2.0*density - 9.0)));
04221     //e = (1.0 + density*(8.0 - 4.0*density));
04222     //e = 1.0 + density*4.0;
04223     //x = 5.0*pow(10.0,-e);
04224 
04225     e = (density < 0.5) 
04226       ? 1.0 + density*(6.0 - 4.0*density) // 1.0 + 4.0*density
04227       : 2.0 + 2.0*density;
04228     x = pow(10.0,-e);
04229 
04230     tol = actual_size*x;
04231   }
04232   return tol;
04233 }
04234 
04235 static ON_MeshParameters FastRenderMeshParameters()
04236 {
04237   // If you change these, put your name, the date, and the reasons for
04238   // the change in this comment so we can keep track of what we are
04239   // trying to accomplish;
04240   ON_MeshParameters mp;
04241 
04242   // Added 27 April 2006 for the Meshkateers
04243   //   Attempting to make jagged and faster render meshes a little
04244   //   more dense.
04245   //
04246   // Turn off everything ...
04247   mp.m_bComputeCurvature = false;
04248   mp.m_tolerance = 0.0;
04249   mp.m_bJaggedSeams  = false;
04250   mp.m_max_edge_length = 0.0;
04251   mp.m_grid_aspect_ratio = 0.0;
04252   mp.m_grid_max_count = 0;
04253   mp.m_grid_angle = 0.0;
04254   mp.m_grid_amplification = 0.0;
04255   mp.m_refine_angle = 0.0;
04256 
04257   // ... except ...
04258   // The m_relative_tolerance setting must be set so that
04259   // 0.0005 = ON_MeshParameters::Tolerance(m_relative_tolerance,1.0).
04260   mp.m_relative_tolerance = 0.65;
04261   //double x = Tolerance(m_relative_tolerance,1.0);
04262 
04263   mp.m_grid_min_count     = 16;
04264   mp.m_min_edge_length    = 0.0001;
04265   mp.m_bRefine            = true;
04266   mp.m_bSimplePlanes      = true;
04267 
04268   mp.m_texture_range = 2; // Don't change this without speaking to Dale Lear
04269 
04270   //{
04271   //  // 16 July, 2002 - copied V2 hard coded "jagged and faster" render mesh settings
04272   //  //
04273   //  // Settings used in V2, V3 and early V4 beta
04274   //  mp.m_refine_angle       = 20.0*ON_PI/180.0;
04275   //  mp.m_grid_angle         = 20.0*ON_PI/180.0;
04276   //  mp.m_grid_aspect_ratio  = 0.0;
04277   //  mp.m_min_edge_length    = 0.0001;
04278   //  mp.m_max_edge_length    = 0.0;
04279   //  mp.m_tolerance          = 0.0;
04280   //  mp.m_grid_min_count     = 16;
04281   //  mp.m_bRefine            = 1;
04282   //  mp.m_bJaggedSeams       = 0;
04283   //  mp.m_bSimplePlanes      = 0;
04284   //}
04285 
04286   return mp;
04287 }
04288 
04289 static ON_MeshParameters QualityRenderMeshParameters()
04290 {
04291   // If you change these, put your name, the date, and the reasons for
04292   // the change in this comment so we can keep track of what we are
04293   // trying to accomplish;
04294   ON_MeshParameters mp;
04295 
04296   // Added 27 April 2006 for the Meshkateers
04297   //   Attempting to make smooth and slower render meshing a little
04298   //   faster.
04299   //
04300   // Turn off everything ...
04301   mp.m_bComputeCurvature = false;
04302   mp.m_tolerance = 0.0;
04303   mp.m_bJaggedSeams  = false;
04304   mp.m_max_edge_length = 0.0;
04305   mp.m_grid_aspect_ratio = 0.0;
04306   mp.m_grid_max_count = 0;
04307   mp.m_grid_angle = 0.0;
04308   mp.m_grid_amplification = 0.0;
04309 
04310   // ... except ...
04311   // The m_relative_tolerance setting must be set so that
04312   // 0.00025 = ON_MeshParameters::Tolerance(m_relative_tolerance,1.0).
04313   mp.m_relative_tolerance = 0.8;
04314   //double x = Tolerance(m_relative_tolerance,1.0);
04315 
04316   mp.m_grid_min_count     = 16;
04317   mp.m_min_edge_length    = 0.0001;
04318   mp.m_bRefine            = true;
04319   mp.m_bSimplePlanes      = true;
04320   mp.m_refine_angle       = 20.0*ON_PI/180.0;
04321 
04322   mp.m_texture_range = 2; // Don't change this without speaking to Dale Lear
04323 
04324 
04328   //mp.m_refine_angle       = 15.0*ON_PI/180.0;
04329   //mp.m_grid_angle         = 15.0*ON_PI/180.0;
04330   //mp.m_grid_aspect_ratio  = 6.0;
04331   //mp.m_min_edge_length    = 0.0001;
04332   //mp.m_max_edge_length    = 0.0;
04333   //mp.m_tolerance          = 0.0;
04334   //mp.m_grid_min_count     = 16;
04335   //mp.m_bRefine            = 1;
04336   //mp.m_bJaggedSeams       = 0;
04337   //mp.m_bSimplePlanes      = 0;
04338 
04339   return mp;
04340 }
04341 
04342 const ON_MeshParameters ON_MeshParameters::FastRenderMesh(FastRenderMeshParameters());
04343 
04344 const ON_MeshParameters ON_MeshParameters::QualityRenderMesh(QualityRenderMeshParameters());
04345 
04346 void ON_MeshParameters::JaggedAndFasterMeshParameters()
04347 {
04348   *this = ON_MeshParameters::FastRenderMesh;
04349 }
04350 
04351 void ON_MeshParameters::SmoothAndSlowerMeshParameters()
04352 {
04353   *this = ON_MeshParameters::QualityRenderMesh;
04354 }
04355 
04356 void ON_MeshParameters::DefaultAnalysisMeshParameters()
04357 {
04358   Default();
04359 
04360   // 7 July 2006 Dale Lear
04361   //    I added this line for RR 10330
04362   Set( 0.5, m_min_edge_length );
04363 
04364   m_texture_range = 1; // THIS IS REQUIRED - DO NOT CHANGE IT
04365 
04366   // Meshkateers 
04367   //   Add your stuff below this line.
04368   //   Do not change m_texture_range.
04369 }
04370 
04371 bool ON_MeshParameters::operator==(const ON_MeshParameters& m2) const
04372 {
04373   return (Compare(m2)==0) ? true : false;
04374 }
04375 
04376 bool ON_MeshParameters::operator!=(const ON_MeshParameters& m2) const
04377 {
04378   return (Compare(m2)) ? true : false;
04379 }
04380 
04381 bool ON_MeshParameters::operator==(const ON_Mesh& mesh) const
04382 {
04383   const ON_MeshParameters* mp1 = mesh.MeshParameters();
04384   return mp1 ? (*this == *mp1) : false;
04385 }
04386 
04387 bool ON_MeshParameters::operator!=(const ON_Mesh& mesh) const
04388 {
04389   const ON_MeshParameters* mp1 = mesh.MeshParameters();
04390   return mp1 ? (*this != *mp1) : true;
04391 }
04392 
04393 void ON_MeshParameters::Dump( ON_TextLog& text_log ) const
04394 {
04395   text_log.Print("Gridding:\n");
04396   text_log.PushIndent();
04397   text_log.Print("Min grid count = %d\n",m_grid_min_count);
04398   text_log.Print("Max grid count = %d\n",m_grid_max_count);
04399   text_log.Print("Gridding angle = %g radians (%g degrees)\n",m_grid_angle,180.0*m_grid_angle/ON_PI);
04400   text_log.Print("Aspect ratio = %g\n",m_grid_aspect_ratio);
04401   text_log.Print("Amplification = %g\n",m_grid_amplification);
04402   text_log.PopIndent();
04403 
04404   text_log.Print("Refining:\n");
04405   text_log.PushIndent();
04406   text_log.Print("Refine = %s\n",m_bRefine?"true":"false");
04407   text_log.Print("Refine angle = %g radians (%g degrees)\n",m_refine_angle,180.0*m_refine_angle/ON_PI);
04408   text_log.PopIndent();
04409 
04410   text_log.Print("Metrics:\n");
04411   text_log.PushIndent();
04412   text_log.Print("Density = %g (relative tolerance = %g)\n",m_relative_tolerance,ON_MeshParameters::Tolerance(m_relative_tolerance,1.0));
04413   text_log.Print("Minimum tolerance = %g\n",m_min_tolerance);
04414   text_log.Print("Tolerance = %g\n",m_tolerance);
04415   text_log.Print("Min edge length = %g\n",m_min_edge_length);
04416   text_log.Print("Max edge length = %g\n",m_max_edge_length);
04417   text_log.PopIndent();
04418 
04419   text_log.Print("Misceleanous:\n");
04420   text_log.PushIndent();
04421   text_log.Print("Face type = %d\n",m_face_type );
04422   text_log.Print("Compute curvature = %s\n",m_bComputeCurvature?"true":"false");
04423   text_log.Print("Texture range = %d\n",m_texture_range);
04424   text_log.Print("Simple planes = %s\n",m_bSimplePlanes?"true":"false");
04425   text_log.Print("Jagged Seams = %s\n",m_bJaggedSeams?"true":"false");
04426   text_log.Print("Double Precision = %s\n",m_bDoublePrecision?"true":"false");
04427   text_log.Print("Custom settings = %s\n",m_bCustomSettings?"true":"false");
04428   text_log.PopIndent();
04429 }
04430 
04431 void ON_MeshParameters::Default()
04432 {
04433   memset(this,0,sizeof(*this));
04434 
04435   m_bCustomSettings = false;
04436   m_bComputeCurvature = false;
04437   m_bSimplePlanes = false;
04438   m_bRefine = true;
04439   m_bJaggedSeams = false;
04440   m_bDoublePrecision = false;
04441   m_bCustomSettingsEnabled = true;
04442   m_mesher = 0;
04443   m_texture_range = 2; // packed normalized textures
04444   m_reserved2 = 0;
04445   m_tolerance = 0.0;
04446   m_relative_tolerance = 0.0;
04447   m_min_tolerance = 0.0;
04448   m_min_edge_length = 0.0001;
04449   m_max_edge_length = 0.0;
04450   m_grid_aspect_ratio = 6.0;
04451   m_grid_min_count = 0;
04452   m_grid_max_count = 0;
04453   m_grid_angle = 20.0*ON_PI/180.0;
04454   m_grid_amplification = 1.0;
04455   m_refine_angle = 20.0*ON_PI/180.0;
04456   m_face_type = 0;
04457   m_reserved3 = 0;
04458 }
04459 
04460 void ON_MeshParameters::Set( double density, double min_edge_length )
04461 {
04462   bool bRelTolTest = true;
04463 
04464   Default();
04465 
04466   // These 4 settings should not be changed
04467   m_bComputeCurvature = false;
04468   m_min_edge_length = min_edge_length;
04469   m_texture_range = 0;
04470   m_tolerance = 0.0;
04471 
04472   if ( bRelTolTest )
04473   {
04474     // Added 27 April 2005
04475     m_relative_tolerance = density;
04476 
04477     // The other meshing parameters are disabled.
04478     //
04479     //   Dale Lear's remark:
04480     //     I think it is a mistake to leave m_grid_aspect_ratio = 0
04481     //     at all settings.  This will result in long skinny triangles
04482     //     in areas where the principal curvatures are dramatically
04483     //     different.  
04484     //
04485     m_bRefine = (density < 0.65);
04486     m_bSimplePlanes = (density <= 0.0);
04487     m_bJaggedSeams  = false;
04488     m_max_edge_length = 0.0;
04489     m_grid_aspect_ratio = 0.0;
04490     m_grid_min_count = 0;
04491     m_grid_max_count = 0;
04492     m_grid_angle = 0.0;
04493     m_grid_amplification = 0.0;
04494     m_refine_angle = 0.0;
04495   }
04496   else
04497   {
04498     // Used in V3 and prior to 27 April 2005
04499     double t0, t1;
04500 
04501     m_bRefine = true;
04502     m_bSimplePlanes = false;
04503     m_bJaggedSeams  = false;
04504     m_max_edge_length = 0.0;
04505     m_grid_aspect_ratio = 0.0;
04506     m_grid_min_count = 16;
04507     m_grid_max_count = 0;
04508     m_grid_angle = 20.0*ON_PI/180.0;
04509     m_grid_amplification = 1.0;
04510     m_refine_angle = 20.0*ON_PI/180.0;
04511  
04512     if ( density <= 0.0) 
04513     {
04514       m_bSimplePlanes = true;
04515       m_bRefine       = false;
04516 
04517       m_grid_min_count     = 0;
04518       m_grid_aspect_ratio  = 0.0;
04519       m_grid_angle         = 0.0;
04520       m_grid_amplification = 1.0;
04521 
04522       m_refine_angle       = m_grid_angle;
04523     }
04524     else if ( density < 0.5)
04525     {
04526       t1 = 2.0*density;
04527       t0 = 1.0-t1;
04528       
04529       m_bRefine = density < 0.10 ? false : true;
04530 
04531       m_grid_min_count = 0;
04532       m_grid_aspect_ratio  = 6.0;
04533       m_grid_angle         = 20.0*ON_PI/180.0;
04534       m_grid_amplification = (t1 > 0.0 ) ? t1 : 1.0;
04535 
04536       m_refine_angle = (t0*65.0 + t1*20.0)*ON_PI/180.0;
04537     }
04538     else if ( density == 0.5 ) 
04539     {
04540       m_bRefine       = true;
04541 
04542       m_grid_min_count     = 0;
04543       m_grid_aspect_ratio  = 6.0;
04544       m_grid_angle         = 20.0*ON_PI/180.0;
04545       m_grid_amplification = 1.0;
04546 
04547       m_refine_angle        = m_grid_angle;
04548     }
04549     else
04550     {
04551       t1 = 2.0*(density - 0.5);
04552       if ( t1 > 1.0 )
04553         t1 = 1.0;
04554       t0 = 1.0-t1;
04555       
04556       m_bRefine = true;
04557 
04558       m_grid_min_count     = 0;
04559       m_grid_aspect_ratio  = t0*6.0 + t1;
04560       m_grid_angle         = (t0*20.0 + t1*5.0)*ON_PI/180.0;
04561       m_grid_amplification = 1.0 + t1;
04562 
04563       m_refine_angle       = m_grid_angle;
04564     }
04565   }
04566 }
04567 
04568 
04569 int ON_MeshParameters::Compare( const ON_MeshParameters& src ) const
04570 {
04571   // Discuss any changes with Dale Lear.
04572   if ( !m_bCustomSettings && src.m_bCustomSettings )
04573     return -1;
04574   if ( m_bCustomSettings && !src.m_bCustomSettings )
04575     return 1;
04576   if ( m_texture_range < src.m_texture_range )
04577     return -1;
04578   if ( m_texture_range > src.m_texture_range )
04579     return 1;
04580   return CompareGeometrySettings(src);
04581 }
04582 
04583 static int ON_MeshParameters_CompareDouble(double t0, double t1, double default_value)
04584 {
04585   if ( !ON_IsValid(t0) || t0 <= 0.0 )
04586     t0 = default_value;
04587   if ( !ON_IsValid(t1) || t1 <= 0.0 )
04588     t1 = default_value;
04589   if ( t0 < t1 )
04590     return 1;
04591   if ( t1 < t0 )
04592     return -1;
04593   return 0;
04594 }
04595 
04596 static int ON_MeshParameters_CompareInt(int i0, int i1)
04597 {
04598   if ( i0 <= 0 )
04599     i0 = 0;
04600   if ( i1 <= 0 )
04601     i1 = 0;
04602   return i1-i0;
04603 }
04604 
04605 static int ON_MeshParameters_CompareBool(bool b0, bool b1)
04606 {
04607   const int i0 = b0 ? 1 : 0; 
04608   const int i1 = b1 ? 1 : 0;
04609   return i1-i0;
04610 }
04611 
04612 int ON_MeshParameters::CompareGeometrySettings( const ON_MeshParameters& src ) const
04613 {
04614   int rc;
04615 
04616   // Discuss any changes with Dale Lear.
04617   rc = ON_MeshParameters_CompareBool(m_bSimplePlanes,src.m_bSimplePlanes);
04618   if (rc)
04619     return rc;
04620   rc = ON_MeshParameters_CompareBool(m_bRefine,src.m_bRefine);
04621   if (rc)
04622     return rc;
04623   rc = ON_MeshParameters_CompareBool(m_bJaggedSeams,src.m_bJaggedSeams);
04624   if (rc)
04625     return rc;
04626   if ( m_mesher < src.m_mesher )
04627     return -1;
04628   if ( m_mesher > src.m_mesher )
04629     return 1;
04630 
04631   rc = ON_MeshParameters_CompareDouble(m_tolerance,src.m_tolerance,0.0);
04632   if (rc)
04633     return rc;
04634 
04635   rc = ON_MeshParameters_CompareDouble(m_relative_tolerance,src.m_relative_tolerance,0.0);
04636   if (rc)
04637     return rc;
04638 
04639   // DO NOT COMPARE m_min_tolerance - it is a runtime lower bound clamp.
04640   //                If it is included here, Rhino will remesh everytime
04641   //                model tolerance changes.
04642 
04643   rc = ON_MeshParameters_CompareDouble(m_min_edge_length,src.m_min_edge_length,0.0);
04644   if (rc)
04645     return rc;
04646 
04647   rc = ON_MeshParameters_CompareDouble(m_max_edge_length,src.m_max_edge_length,0.0);
04648   if (rc)
04649     return rc;
04650 
04651   rc = ON_MeshParameters_CompareDouble(m_grid_aspect_ratio,src.m_grid_aspect_ratio,0.0);
04652   if (rc)
04653     return rc;
04654 
04655   rc = ON_MeshParameters_CompareInt(m_grid_min_count,src.m_grid_min_count);
04656   if (rc)
04657     return rc;
04658 
04659   rc = ON_MeshParameters_CompareInt(m_grid_max_count,src.m_grid_max_count);
04660   if (rc)
04661     return rc;
04662 
04663   rc = ON_MeshParameters_CompareDouble(m_grid_angle,src.m_grid_angle,ON_PI);
04664   if (rc)
04665     return rc;
04666 
04667   rc = ON_MeshParameters_CompareDouble(m_refine_angle,src.m_refine_angle,0.0);
04668   if (rc)
04669     return rc;
04670 
04671   rc = ON_MeshParameters_CompareDouble(m_grid_amplification,src.m_grid_amplification,1.0);
04672   if (rc)
04673     return rc;
04674 
04675   if ( m_face_type < src.m_face_type )
04676     return -1;
04677   if ( m_face_type > src.m_face_type )
04678     return 1;
04679 
04680   return 0;
04681 }
04682 
04683 ON__UINT32 ON_MeshParameters::DataCRC(ON__UINT32 current_remainder) const
04684 {
04685   ON__UINT32 crc = ON_CRC32(current_remainder,sizeof(m_bComputeCurvature),&m_bComputeCurvature);
04686   crc = ON_CRC32(crc,sizeof(m_bSimplePlanes),&m_bSimplePlanes);
04687   crc = ON_CRC32(crc,sizeof(m_bRefine),&m_bRefine);
04688   crc = ON_CRC32(crc,sizeof(m_bJaggedSeams),&m_bJaggedSeams);
04689   crc = ON_CRC32(crc,sizeof(m_tolerance),&m_tolerance);
04690   crc = ON_CRC32(crc,sizeof(m_min_edge_length),&m_min_edge_length);
04691   crc = ON_CRC32(crc,sizeof(m_max_edge_length),&m_max_edge_length);
04692   crc = ON_CRC32(crc,sizeof(m_grid_aspect_ratio),&m_grid_aspect_ratio);
04693   crc = ON_CRC32(crc,sizeof(m_grid_min_count),&m_grid_min_count);
04694   crc = ON_CRC32(crc,sizeof(m_grid_max_count),&m_grid_max_count);
04695   crc = ON_CRC32(crc,sizeof(m_grid_angle),&m_grid_angle);
04696   crc = ON_CRC32(crc,sizeof(m_grid_amplification),&m_grid_amplification);
04697   crc = ON_CRC32(crc,sizeof(m_refine_angle),&m_refine_angle);
04698   crc = ON_CRC32(crc,sizeof(m_face_type),&m_face_type);
04699   crc = ON_CRC32(crc,sizeof(m_texture_range),&m_texture_range);
04700   crc = ON_CRC32(crc,sizeof(m_bCustomSettings),&m_bCustomSettings);
04701   crc = ON_CRC32(crc,sizeof(m_relative_tolerance),&m_relative_tolerance);
04702   crc = ON_CRC32(crc,sizeof(m_mesher),&m_mesher);
04703   return crc;
04704 }
04705 
04706 bool ON_MeshParameters::Write( ON_BinaryArchive& file ) const
04707 {
04708   bool rc = file.Write3dmChunkVersion(1,4);
04709   if (rc) {
04710     if (rc) rc = file.WriteInt(m_bComputeCurvature);
04711     if (rc) rc = file.WriteInt(m_bSimplePlanes);
04712     if (rc) rc = file.WriteInt(m_bRefine);
04713     if (rc) rc = file.WriteInt(m_bJaggedSeams);
04714     if (rc) rc = file.WriteInt(0); // obsolete m_bWeld field
04715     if (rc) rc = file.WriteDouble(m_tolerance);
04716     if (rc) rc = file.WriteDouble(m_min_edge_length);
04717     if (rc) rc = file.WriteDouble(m_max_edge_length);
04718     if (rc) rc = file.WriteDouble(m_grid_aspect_ratio);
04719     if (rc) rc = file.WriteInt(m_grid_min_count);
04720     if (rc) rc = file.WriteInt(m_grid_max_count);
04721     if (rc) rc = file.WriteDouble(m_grid_angle);
04722     if (rc) rc = file.WriteDouble(m_grid_amplification);
04723     if (rc) rc = file.WriteDouble(m_refine_angle);
04724     if (rc) rc = file.WriteDouble(5.0*ON_PI/180.0); // obsolete m_combine_angle field
04725     int mft = m_face_type;
04726     if ( mft < 0 || mft > 2 ) 
04727     {
04728       ON_ERROR("ON_MeshParameters::Read() - m_face_type out of bounds.");
04729       mft = 0;
04730     }
04731     if (rc) rc = file.WriteInt(mft);
04732 
04733     // added for chunk version 1.1
04734     if (rc) rc = file.WriteInt( m_texture_range );
04735 
04736     // added for chunk version 1.2 - 14 October 2005
04737     if (rc) rc = file.WriteBool( m_bCustomSettings );
04738     if (rc) rc = file.WriteDouble( m_relative_tolerance );
04739     // DO NOT SAVE m_min_tolerance - yet ??
04740 
04741     // added for chunk version 1.3 - 20 February 2006
04742     if (rc) rc = file.WriteChar(m_mesher);
04743 
04744     // added for chunk version 1.4 - 3 March 2011
04745     if (rc) rc = file.WriteBool( m_bCustomSettingsEnabled );
04746   }
04747   return rc;
04748 }
04749 
04750 bool ON_MeshParameters::Read( ON_BinaryArchive& file )
04751 {
04752   Default();
04753   int major_version = 0;
04754   int minor_version = 0;
04755   bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
04756   if ( rc && major_version == 1 ) 
04757   {
04758     int i;
04759 
04760     i = m_bComputeCurvature;
04761     if (rc) rc = file.ReadInt(&i);
04762     m_bComputeCurvature = i?true:false;
04763 
04764     i = m_bSimplePlanes;
04765     if (rc) rc = file.ReadInt(&i);
04766     m_bSimplePlanes = i?true:false;
04767 
04768     i = m_bRefine;
04769     if (rc) rc = file.ReadInt(&i);
04770     m_bRefine = i?true:false;
04771 
04772     i = m_bJaggedSeams;
04773     if (rc) rc = file.ReadInt(&i);
04774     m_bJaggedSeams = i?true:false;
04775 
04776     int obsolete_m_bWeld;
04777     if (rc) rc = file.ReadInt(&obsolete_m_bWeld);
04778 
04779     if (rc) rc = file.ReadDouble(&m_tolerance);
04780     if (rc) rc = file.ReadDouble(&m_min_edge_length);
04781     if (rc) rc = file.ReadDouble(&m_max_edge_length);
04782     if (rc) rc = file.ReadDouble(&m_grid_aspect_ratio);
04783     if (rc) rc = file.ReadInt(&m_grid_min_count);
04784     if (rc) rc = file.ReadInt(&m_grid_max_count);
04785     if (rc) rc = file.ReadDouble(&m_grid_angle);
04786     if (rc) rc = file.ReadDouble(&m_grid_amplification);
04787     if (rc) rc = file.ReadDouble(&m_refine_angle);
04788     double obsolete_m_combine_angle;
04789     if (rc) rc = file.ReadDouble(&obsolete_m_combine_angle);
04790     if (rc) rc = file.ReadInt(&m_face_type);
04791     if ( m_face_type < 0 || m_face_type > 2 ) 
04792     {
04793       ON_ERROR("ON_MeshParameters::Read() - m_face_type out of bounds.");
04794       m_face_type = 0;
04795     }
04796 
04797     if ( rc && minor_version >= 1 ) 
04798     {
04799       rc = file.ReadInt( &m_texture_range );
04800       if ( rc && minor_version >= 2 )
04801       {
04802         rc = file.ReadBool(&m_bCustomSettings);
04803         if (rc) rc = file.ReadDouble(&m_relative_tolerance);
04804         if ( rc && minor_version >= 3 )
04805         {
04806           rc = file.ReadChar(&m_mesher);
04807           if ( rc && minor_version >= 4 )
04808           {
04809             rc = file.ReadBool(&m_bCustomSettingsEnabled);
04810           }
04811         }
04812       }
04813     }
04814   }
04815   return rc;
04816 }
04817 
04818 
04819 
04820 ON_MeshCurvatureStats::ON_MeshCurvatureStats()
04821 {
04822   Destroy(); // initializes defaults
04823 }
04824 
04825 ON_MeshCurvatureStats::~ON_MeshCurvatureStats()
04826 {}
04827 
04828 void ON_MeshCurvatureStats::Destroy()
04829 {
04830   m_style = ON::unknown_curvature_style;
04831   m_infinity = 0.0;
04832   m_count_infinite = 0;
04833   m_count = 0;
04834   m_mode = 0.0;
04835   m_average = 0.0;
04836   m_adev = 0.0;
04837   m_range.Set(0.0,0.0);
04838 }
04839 
04840 void ON_MeshCurvatureStats::EmergencyDestroy()
04841 {
04842   Destroy(); // - no memory is freed so Destroy() will work
04843 }
04844 
04845 ON_MeshCurvatureStats::ON_MeshCurvatureStats(const ON_MeshCurvatureStats& src)
04846 {
04847   *this = src;
04848 }
04849 
04850 ON_MeshCurvatureStats& ON_MeshCurvatureStats::operator=(const ON_MeshCurvatureStats& src)
04851 {
04852   if ( this != &src ) {
04853     m_style          = src.m_style;
04854     m_infinity       = src.m_infinity;
04855     m_count_infinite = src.m_count_infinite;
04856     m_count          = src.m_count;
04857     m_mode           = src.m_mode;
04858     m_average        = src.m_average;
04859     m_adev           = src.m_adev;
04860     m_range          = src.m_range;
04861   }
04862   return *this;
04863 }
04864 
04865 bool ON_MeshCurvatureStats::Write( ON_BinaryArchive& file ) const
04866 {
04867   int i;
04868   bool rc = file.Write3dmChunkVersion(1,1);
04869   if (rc) {
04870     i = m_style;
04871     if (rc) rc = file.WriteInt(i);
04872     if (rc) rc = file.WriteDouble(m_infinity);
04873     if (rc) rc = file.WriteInt(m_count_infinite);
04874     if (rc) rc = file.WriteInt(m_count);
04875     if (rc) rc = file.WriteDouble(m_mode);
04876     if (rc) rc = file.WriteDouble(m_average);
04877     if (rc) rc = file.WriteDouble(m_adev);
04878     if (rc) rc = file.WriteInterval(m_range);
04879   }
04880   return rc;
04881 }
04882 
04883 
04884 bool ON_MeshCurvatureStats::Read( ON_BinaryArchive& file )
04885 {
04886   int major_version = 0;
04887   int minor_version = 0;
04888   Destroy();
04889   bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
04890   if (rc && major_version == 1) {
04891     int i=0;
04892     if (rc) rc = file.ReadInt(&i);
04893     if (rc) m_style = ON::CurvatureStyle(i);
04894     if (rc) rc = file.ReadDouble(&m_infinity);
04895     if (rc) rc = file.ReadInt(&m_count_infinite);
04896     if (rc) rc = file.ReadInt(&m_count);
04897     if (rc) rc = file.ReadDouble(&m_mode);
04898     if (rc) rc = file.ReadDouble(&m_average);
04899     if (rc) rc = file.ReadDouble(&m_adev);
04900     if (rc) rc = file.ReadInterval(m_range);
04901   }
04902   return rc;
04903 }
04904 
04905 bool ON_MeshCurvatureStats::Set( ON::curvature_style kappa_style,
04906                                  int Kcount,
04907                                  const ON_SurfaceCurvature* K,
04908                                  const ON_3fVector* N, // needed for normal sectional curvatures
04909                                  double infinity
04910                                  )
04911 {
04912   bool rc = (Kcount > 0 && K != NULL);
04913 
04914   Destroy();
04915 
04916   if (rc) {
04917     ON_Workspace ws;
04918     //ON_3dVector tangent;
04919     double k;
04920     double* kappa = ws.GetDoubleMemory(Kcount);
04921     int i;
04922 
04923     switch( kappa_style ) {
04924     case ON::gaussian_curvature:
04925       m_style = kappa_style;
04926       m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e20;
04927       break;
04928     case ON::mean_curvature: // unsigned mean
04929       m_style = kappa_style;
04930       m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
04931       break;
04932     case ON::min_curvature: // minimum unsigned radius of curvature
04933       m_style = kappa_style;
04934       m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
04935       break;
04936     case ON::max_curvature: // maximum unsigned radius of curvature
04937       m_style = kappa_style;
04938       m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
04939       break;
04940     //case ON::section_curvature_x:
04941     //  if ( !N )
04942     //    kappa_style = ON::unknown_curvature_style;
04943     //  m_style = kappa_style;
04944     //  m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
04945     //  break;
04946     //case ON::section_curvature_y:
04947     //  if ( !N )
04948     //    kappa_style = ON::unknown_curvature_style;
04949     //  m_style = kappa_style;
04950     //  m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
04951     //  break;
04952     //case ON::section_curvature_z:
04953     //  if ( !N )
04954     //    kappa_style = ON::unknown_curvature_style;
04955     //  m_style = kappa_style;
04956     //  m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
04957     //  break;
04958     default:
04959       rc = false;
04960       break;
04961     }
04962 
04963     for ( i = 0; i < Kcount; i++ ) {
04964       switch( kappa_style ) {
04965       case ON::gaussian_curvature:
04966         k = K[i].GaussianCurvature();
04967         break;
04968       case ON::mean_curvature: // unsigned mean
04969         k = fabs(K[i].MeanCurvature());
04970         break;
04971       case ON::min_curvature: // minimum unsigned radius of curvature
04972         k = fabs(K[i].MinimumRadius());
04973         break;
04974       case ON::max_curvature: // maximum unsigned radius of curvature
04975         k = fabs(K[i].MaximumRadius());
04976         break;
04977       //case ON::section_curvature_x:
04978       //  tangent.x = 0.0; tangent.y = -N[i].z; tangent.z = N[i].y;
04979       //  if ( fabs(tangent.y) <= ON_SQRT_EPSILON && fabs(tangent.z) <= ON_SQRT_EPSILON )
04980       //    tangent.Zero();
04981       //  else
04982       //    tangent.Unitize();
04983       //  k = fabs(K[i].NormalCurvature(tangent));
04984       //  break;
04985       //case ON::section_curvature_y:
04986       //  tangent.x = N[i].z; tangent.y = 0.0; tangent.z = -N[i].x;
04987       //  if ( fabs(tangent.x) <= ON_SQRT_EPSILON && fabs(tangent.z) <= ON_SQRT_EPSILON )
04988       //    tangent.Zero();
04989       //  else
04990       //    tangent.Unitize();
04991       //  k = fabs(K[i].NormalCurvature(tangent));
04992       //  break;
04993       //case ON::section_curvature_z:
04994       //  tangent.x = -N[i].y; tangent.y = N[i].x; tangent.z = 0.0;
04995       //  if ( fabs(tangent.x) <= ON_SQRT_EPSILON && fabs(tangent.y) <= ON_SQRT_EPSILON )
04996       //    tangent.Zero();
04997       //  else
04998       //    tangent.Unitize();
04999       //  k = fabs(K[i].NormalCurvature(tangent));
05000       //  break;
05001       default:
05002         k=0.0;
05003         break;
05004       }
05005       if ( fabs(k) >= m_infinity ) {
05006         m_count_infinite++;
05007         continue;
05008       }
05009       if ( m_count ) {
05010         if ( k < m_range.m_t[0] )
05011           m_range.m_t[0] = k;
05012         else if ( k > m_range.m_t[1] )
05013           m_range.m_t[1] = k;
05014       }
05015       else {
05016         m_range.m_t[0] = m_range.m_t[1] = k;
05017       }
05018       kappa[m_count++] = k;
05019     }
05020 
05021 
05022     if ( m_count == 0 )
05023       rc = false;
05024     else {
05025       // sum curvatures
05026       ON_SortDoubleArray( ON::quick_sort, kappa, m_count );
05027 
05028       // mode
05029       m_mode = kappa[m_count/2];
05030       if ( 0 == (m_count % 2) ) {
05031         m_mode += kappa[(m_count/2)-1];
05032         m_mode *= 0.5;
05033       }
05034 
05035       // average
05036       for ( i = 0; i < m_count; i++ ) {
05037         m_average += kappa[i];
05038       }
05039       m_average = m_average/m_count;
05040       
05041       // average deviation
05042       for ( i = 0; i < m_count; i++ ) {
05043         m_adev += fabs(kappa[i] - m_average);
05044       }
05045       m_adev = m_adev/m_count;
05046     }  
05047   }
05048   
05049   return rc;  
05050 }
05051 
05052 struct EDGEINFO
05053 {
05054   int fi[2]; // m_F[] triangles on either side of the edge
05055   int vi[4]; // potential m_V[] quad indices
05056   int flag; // 0 = available, 
05057             // 1 = side of quad, 
05058             // 2 = bndry or nonmanifold edge,
05059             // 3 = edge crease angle > angle_tol_radians
05060             // 4 = edge is not longest side of triangle
05061             // 5 = edge length is zero
05062             // 6 = edge would be a boundary if mesh were exploded
05063             // 16 = edge will be removed to make a quad
05064   double length;
05065 };
05066 
05067 bool ON_Mesh::ConvertTrianglesToQuads( 
05068               double angle_tol_radians,
05069               double min_diagonal_length_ratio
05070               )
05071 {
05072   ON_Workspace ws;
05073 
05074   double d;
05075 
05076   int i, ii, jj;
05077   int diagonal_count;
05078   const int* f0vi;
05079   const int* f1vi;
05080   const int* fei;
05081 
05082   diagonal_count = 0;
05083 
05084   if ( angle_tol_radians < 0.0 || !ON_IsValid(angle_tol_radians) )
05085   {
05086     // 2.5 degrees
05087     angle_tol_radians = 0.043633231299858239423092269212215;
05088   }
05089   else if ( angle_tol_radians < ON_ZERO_TOLERANCE )
05090   {
05091     angle_tol_radians = ON_ZERO_TOLERANCE;
05092   }
05093 
05094   angle_tol_radians = cos(angle_tol_radians);
05095   if ( angle_tol_radians < 0.5 )
05096     angle_tol_radians = 0.5;
05097   else if ( angle_tol_radians > 1.0-ON_SQRT_EPSILON )
05098     angle_tol_radians = 1.0-ON_SQRT_EPSILON;
05099 
05100   const ON_MeshTopology& top = Topology();
05101 
05102   if ( !HasFaceNormals() )
05103     ComputeFaceNormals();
05104 
05105   if ( min_diagonal_length_ratio < ON_ZERO_TOLERANCE )
05106     min_diagonal_length_ratio = ON_ZERO_TOLERANCE;
05107 
05108   double max_diagonal_length_ratio = 1.0/min_diagonal_length_ratio;
05109 
05110   if( min_diagonal_length_ratio > max_diagonal_length_ratio )
05111   {
05112     d = min_diagonal_length_ratio;
05113     min_diagonal_length_ratio = max_diagonal_length_ratio;
05114     max_diagonal_length_ratio = d;
05115   }
05116 
05117   //double rel_tol = ON_SQRT_EPSILON;
05118   double rel_tol = 8.0*ON_SQRT_EPSILON; // to fix RR 51543
05119   if ( min_diagonal_length_ratio > 1.0-rel_tol )
05120     min_diagonal_length_ratio = 1.0-rel_tol;
05121   if ( max_diagonal_length_ratio < 1.0+rel_tol )
05122     max_diagonal_length_ratio = 1.0+rel_tol;
05123 
05124 
05125   const int face_count = m_F.Count();
05126   int* face_flag = ws.GetIntMemory(face_count);  
05127   for ( i = 0; i < face_count; i++ )
05128   {
05129     f0vi = m_F[i].vi;
05130     face_flag[i] = ( f0vi[2] == f0vi[3] ) ? 0 : 1;
05131   }
05132 
05133   const int edge_count = top.m_tope.Count();
05134   struct EDGEINFO* EI = (struct EDGEINFO*)ws.GetMemory(edge_count*sizeof(*EI));
05135   for ( i = 0; i < edge_count; i++ )
05136   {
05137     struct EDGEINFO& ei = EI[i];
05138     const ON_MeshTopologyEdge& tope = top.m_tope[i];
05139     ei.flag = 0;
05140     ei.vi[0] = top.m_topv[tope.m_topvi[0]].m_vi[0];
05141     ei.vi[2] = top.m_topv[tope.m_topvi[1]].m_vi[0];
05142     ei.length = m_V[ei.vi[0]].DistanceTo(m_V[ei.vi[2]]);
05143     if ( ei.length <= 0.0 || !ON_IsValid(ei.length) )
05144     {
05145       ei.flag = 5;
05146     }
05147     else if ( tope.m_topf_count != 2 )
05148     {
05149       ei.flag = 2;
05150     }
05151     else
05152     {
05153       ei.fi[0] = tope.m_topfi[0];
05154       ei.fi[1] = tope.m_topfi[1];
05155       if (face_flag[ei.fi[0]] || face_flag[ei.fi[1]] )
05156       {
05157         ei.flag = 1;
05158       }
05159       else if ( m_FN[ei.fi[0]] * m_FN[ei.fi[1]] < angle_tol_radians )
05160       {
05161         ei.flag = 3;
05162       }
05163       else 
05164       {
05165         f0vi = m_F[ei.fi[0]].vi;
05166         f1vi = m_F[ei.fi[1]].vi;
05167         ei.flag = 6;
05168         for ( ii = 0; ii < 3 && ei.flag; ii++ )
05169         {
05170           for (jj = 0; jj < 3; jj++)
05171           {
05172             if (    f0vi[ii] == f1vi[jj] 
05173                  && f0vi[(ii+1)%3] == f1vi[(jj+2)%3]
05174                  && f0vi[(ii+2)%3] != f1vi[(jj+1)%3] )
05175             {
05176               if ( ei.fi[0] > ei.fi[1] )
05177               {
05178                 jj = ei.fi[0]; ei.fi[0] = ei.fi[1]; ei.fi[1] = jj;
05179               }
05180               ei.vi[0] = f0vi[ii];
05181               ei.vi[1] = f1vi[(jj+1)%3];
05182               ei.vi[2] = f0vi[(ii+1)%3];
05183               ei.vi[3] = f0vi[(ii+2)%3];
05184               ei.flag = 0;
05185               break;
05186             }
05187           }
05188         }
05189       }
05190     }
05191   }
05192 
05193   for ( i = 0; i < edge_count; i++ )
05194   {
05195     struct EDGEINFO& ei = EI[i];
05196     if ( ei.flag )
05197       continue;
05198 
05199     ei.flag = 16;
05200 
05201     // It is CRITICAL that the length compare use >=.
05202     // Otherwise tesselations of equailateral triangles
05203     // will not work right in this fuction.
05204     fei = top.m_topf[ei.fi[0]].m_topei;
05205     if (    ( i != fei[0] && EI[fei[0]].length >= ei.length )
05206          || ( i != fei[1] && EI[fei[1]].length >= ei.length )
05207          || ( i != fei[2] && EI[fei[2]].length >= ei.length )
05208          )
05209     {
05210       // diagonal is not strictly longest in this triangle
05211       continue;
05212     }
05213 
05214     // It is CRITICAL that the length compare use >=.
05215     // Otherwise tesselations of equailateral triangles
05216     // will not work right in this fuction.
05217     fei = top.m_topf[ei.fi[1]].m_topei;
05218     if (    ( i != fei[0] && EI[fei[0]].length >= ei.length )
05219          || ( i != fei[1] && EI[fei[1]].length >= ei.length )
05220          || ( i != fei[2] && EI[fei[2]].length >= ei.length )
05221          )
05222     {
05223       // diagonal is not strictly longest in this triangle
05224       continue;
05225     }
05226 
05227     d = m_V[ei.vi[1]].DistanceTo(m_V[ei.vi[3]]);
05228     d /= ei.length;
05229     if ( d < min_diagonal_length_ratio || d > max_diagonal_length_ratio )
05230     {
05231       // quad wouldn't be square enough
05232       continue;
05233     }
05234 
05235     ei.flag = 0;
05236 
05237     diagonal_count++;
05238   }
05239 
05240   if ( diagonal_count > 0 )
05241   {
05242     DestroyTree();
05243     DestroyPartition();
05244     m_top.Destroy();
05245     for ( i = 0; i < edge_count; i++ )
05246     {
05247       struct EDGEINFO& ei = EI[i];
05248       if ( ei.flag )
05249         continue;
05250       memcpy( m_F[ei.fi[0]].vi, ei.vi, 4*sizeof(ei.vi[0]) );
05251       memset( m_F[ei.fi[1]].vi, 0xFF,  4*sizeof(ei.vi[0]) );
05252       m_triangle_count--;
05253       m_quad_count++;
05254     }    
05255     CullDegenerateFaces();
05256   }
05257 
05258   return (diagonal_count > 0 );
05259 }
05260 
05261 bool ON_Mesh::ConvertQuadsToTriangles()
05262 {
05263   const bool bHasFaceNormals = HasFaceNormals();
05264   const int fcount = FaceCount();
05265   const int vcount = VertexCount();
05266   int fi, idmin;;
05267   double d0, d1, dmin, d;
05268   if ( fcount > 0 && QuadCount() > 0 ) 
05269   {
05270     // use SetCapacity() instead of Reserve() because it's unlikely
05271     // this mesh will have anything else added and we don't want to
05272     // waste memory.
05273     if ( m_F.Capacity() < fcount + m_quad_count )
05274       m_F.SetCapacity( fcount + m_quad_count );
05275     if ( bHasFaceNormals && m_FN.Capacity() < fcount + m_quad_count )
05276       m_FN.SetCapacity( fcount + m_quad_count );
05277 
05278     const ON_3dPoint* dV = ( vcount > 0 && HasDoublePrecisionVertices() 
05279                              && ( HasSynchronizedDoubleAndSinglePrecisionVertices() || DoublePrecisionVerticesAreValid() ) 
05280                            )
05281                    ? DoublePrecisionVertices().Array()
05282                    : 0;
05283 
05284     const ON_3fPoint* fV = m_V.Array();
05285 
05286     const double rel_tol = 8.0*( (0 != dV) ? ON_EPSILON : ON_FLOAT_EPSILON );
05287 
05288     ON_3dVector FN;
05289 
05290     for ( fi = 0; fi < fcount; fi++ ) 
05291     {
05292       ON_MeshFace& f0 = m_F[fi];
05293       if ( f0.IsValid(vcount) && f0.IsQuad() )
05294       {
05295         if ( 0 != dV )
05296         {
05297           d0 = dV[f0.vi[0]].DistanceTo(dV[f0.vi[2]]);
05298           d1 = dV[f0.vi[1]].DistanceTo(dV[f0.vi[3]]);
05299 
05300           // if quad is degenerate, just turn it into a triangle
05301           idmin = -1;
05302           dmin = ((d0<=d1)?d0:d1)*rel_tol;
05303           if ( dmin > ON_ZERO_TOLERANCE )
05304             dmin = ON_ZERO_TOLERANCE;
05305           d = dV[f0.vi[0]].DistanceTo(dV[f0.vi[1]]);
05306           if ( d < dmin )
05307           {
05308             idmin = 0;
05309             dmin = d;
05310           }
05311           d = dV[f0.vi[1]].DistanceTo(dV[f0.vi[2]]);
05312           if ( d < dmin )
05313           {
05314             idmin = 1;
05315             dmin = d;
05316           }
05317           d = dV[f0.vi[2]].DistanceTo(dV[f0.vi[3]]);
05318           if ( d < dmin )
05319           {
05320             idmin = 2;
05321             dmin = d;
05322           }
05323           d = dV[f0.vi[3]].DistanceTo(dV[f0.vi[0]]);
05324           if ( d < dmin )
05325           {
05326             idmin = 3;
05327             dmin = d;
05328           }
05329         }
05330         else
05331         {
05332           d0 = m_V[f0.vi[0]].DistanceTo(m_V[f0.vi[2]]);
05333           d1 = m_V[f0.vi[1]].DistanceTo(m_V[f0.vi[3]]);
05334 
05335           // if quad is degenerate, just turn it into a triangle
05336           idmin = -1;
05337           dmin = ((d0<=d1)?d0:d1)*rel_tol;
05338           if ( dmin > ON_ZERO_TOLERANCE )
05339             dmin = ON_ZERO_TOLERANCE;
05340           d = m_V[f0.vi[0]].DistanceTo(m_V[f0.vi[1]]);
05341           if ( d < dmin )
05342           {
05343             idmin = 0;
05344             dmin = d;
05345           }
05346           d = m_V[f0.vi[1]].DistanceTo(m_V[f0.vi[2]]);
05347           if ( d < dmin )
05348           {
05349             idmin = 1;
05350             dmin = d;
05351           }
05352           d = m_V[f0.vi[2]].DistanceTo(m_V[f0.vi[3]]);
05353           if ( d < dmin )
05354           {
05355             idmin = 2;
05356             dmin = d;
05357           }
05358           d = m_V[f0.vi[3]].DistanceTo(m_V[f0.vi[0]]);
05359           if ( d < dmin )
05360           {
05361             idmin = 3;
05362             dmin = d;
05363           }
05364         }
05365 
05366         if ( !(d0 > 0.0) )
05367         {
05368           if ( !(d1 > 0.0) )
05369             continue;
05370           // d0 = 0 or is invalid and d1 > 0
05371           // force split along v[1],v[3]
05372           idmin = -1;
05373           d0 = 1.0;
05374           d1 = 0.0;
05375         }
05376         else if ( !(d1 > 0.0) )
05377         {
05378           // d1 = 0 or is invalid and d0 > 0
05379           // force split along v[0],v[1]
05380           idmin = -1;
05381           d1 = 1.0;
05382           d0 = 0.0;
05383         }
05384 
05385         m_quad_count--;
05386         m_triangle_count++;
05387         if ( 0 == idmin ) // m_V[f0.vi[0]] == m_V[f0.vi[1]] (nearly)
05388         {
05389           // degenerate quad - remove duplicate vertex
05390           f0.vi[0] = f0.vi[1];
05391           f0.vi[1] = f0.vi[2];
05392           f0.vi[2] = f0.vi[3];
05393         }
05394         else if ( 1 == idmin ) // m_V[f0.vi[1]] == m_V[f0.vi[2]] (nearly)
05395         {
05396           // degenerate quad - remove duplicate vertex
05397           int vi0 = f0.vi[0]; 
05398           f0.vi[0] = f0.vi[2];
05399           f0.vi[1] = f0.vi[3];
05400           f0.vi[2] = vi0;
05401           f0.vi[3] = vi0;
05402         }
05403         else if ( 2 == idmin ) // m_V[f0.vi[2]] == m_V[f0.vi[3]] (nearly)
05404         {
05405           // degenerate quad - remove duplicate vertex
05406           f0.vi[2] = f0.vi[1];
05407           f0.vi[1] = f0.vi[0];
05408           f0.vi[0] = f0.vi[3];
05409           f0.vi[3] = f0.vi[2];
05410         }
05411         else if ( 3 == idmin ) // m_V[f0.vi[3]] == m_V[f0.vi[0]] (nearly)
05412         {
05413           // degenerate quad - remove duplicate vertex
05414           f0.vi[3] = f0.vi[2];
05415         }
05416         else
05417         {
05418           // split non-degenerate quad into two triangles
05419           ON_MeshFace& f1 = m_F.AppendNew();
05420           if  ( d0 <= d1 ) 
05421           {
05422             f1.vi[0] = f0.vi[0];
05423             f1.vi[1] = f0.vi[2];
05424             f1.vi[2] = f0.vi[3];
05425             f1.vi[3] = f1.vi[2];
05426             f0.vi[3] = f0.vi[2];
05427           }
05428           else 
05429           {
05430             f1.vi[0] = f0.vi[1];
05431             f1.vi[1] = f0.vi[2];
05432             f1.vi[2] = f0.vi[3];
05433             f1.vi[3] = f1.vi[2];
05434             f0.vi[2] = f0.vi[3];
05435           }
05436           if ( bHasFaceNormals ) 
05437           {
05438             if ( 0 != dV )
05439             {
05440               m_F[fi].ComputeFaceNormal(dV,FN);
05441               m_FN[fi] = FN;
05442               m_F[m_F.Count()-1].ComputeFaceNormal(dV,FN);
05443             }
05444             else
05445             {
05446               m_F[fi].ComputeFaceNormal(fV,FN);
05447               m_FN[fi] = FN;
05448               m_F[m_F.Count()-1].ComputeFaceNormal(fV,FN);
05449             }
05450             m_FN.AppendNew() = FN;
05451           }
05452         }
05453       }
05454     }
05455     if ( fcount != m_F.Count() )
05456       DestroyTopology(); // we added some triangles
05457   }
05458   return ( QuadCount() == 0 && TriangleCount() == FaceCount() ) ? true : false;
05459 }
05460 
05461 bool ON_Mesh::CountQuads()
05462 {
05463   const int fcount = FaceCount();
05464   const int vcount = VertexCount();
05465   int fi;
05466   m_quad_count = 0;
05467   m_triangle_count = 0;
05468   m_invalid_count = 0;
05469   for ( fi = 0; fi < fcount; fi++ ) {
05470     const ON_MeshFace& f = m_F[fi];
05471     if ( f.IsValid(vcount) ) {
05472       if ( f.IsTriangle() )
05473         m_triangle_count++;
05474       else
05475         m_quad_count++;
05476     }
05477     else
05478       m_invalid_count++;
05479   }
05480   return true;
05481 }
05482 
05483 bool ON_Mesh::ComputeVertexNormals()
05484 {
05485   bool rc = false;
05486   const int fcount = FaceCount();
05487   const int vcount = VertexCount();
05488   int vi, fi, j;
05489   ON_3fVector n;
05490 
05491   if ( fcount > 0 && vcount > 0 ) {
05492     rc = HasFaceNormals();
05493     if ( !rc )
05494       rc = ComputeFaceNormals();
05495     if ( rc ) {
05496       ON_Workspace ws;
05497       //double* face_area = ws.GetDoubleMemory(fcount);
05498       int* vfcount = ws.GetIntMemory( vcount );
05499       memset ( vfcount, 0, vcount*sizeof(*vfcount) );
05500 
05501       // count number of faces that use each vertex
05502       for ( fi = 0; fi < fcount; fi++ ) {
05503         ON_MeshFace& f = m_F[fi];
05504         if ( f.IsValid(vcount) ) {
05505           vfcount[f.vi[0]]++;
05506           vfcount[f.vi[1]]++;
05507           vfcount[f.vi[2]]++;
05508           if ( f.IsQuad() )
05509             vfcount[f.vi[3]]++;
05510         }
05511       }
05512 
05513       // set vfi[vi][] = array of face indices that use vertex vi
05514       int** vfi = (int**)ws.GetMemory( vcount*sizeof(vfi[0] ) );
05515       {
05516         int scratch_sz = 0;
05517         for ( vi = 0; vi < vcount; vi++ ) {
05518           scratch_sz += vfcount[vi];
05519         }
05520         int* scratch = ws.GetIntMemory(scratch_sz);
05521         for ( vi = 0; vi < vcount; vi++ ) {
05522           if ( vfcount[vi] ) {
05523             vfi[vi] = scratch;
05524             scratch += vfcount[vi];
05525           }
05526           vfcount[vi] = 0;
05527         }
05528       }
05529       for ( fi = 0; fi < fcount; fi++ ) {
05530         ON_MeshFace& f = m_F[fi];
05531         if ( f.IsValid(vcount) ) {
05532           vi = f.vi[0]; vfi[vi][vfcount[vi]++] = fi;
05533           vi = f.vi[1]; vfi[vi][vfcount[vi]++] = fi;
05534           vi = f.vi[2]; vfi[vi][vfcount[vi]++] = fi;
05535           if ( f.IsQuad() ) {
05536             vi = f.vi[3]; vfi[vi][vfcount[vi]++] = fi;
05537           }
05538         }
05539       }
05540 
05541       // average face normals to get an estimate for a vertex normal
05542       m_N.SetCapacity(vcount);
05543       m_N.SetCount(0);
05544       for ( vi = 0; vi < vcount; vi++ ) {
05545         n.Zero();
05546         for ( j = vfcount[vi]-1; j >= 0; j-- ) {
05547           n += m_FN[vfi[vi][j]];
05548         }
05549         if ( !n.Unitize() )
05550         {
05551           // this vertex is not used by a face or the face normals cancel out.
05552           // set a unit z normal and press on.
05553           n.Set(0,0,1);
05554         }
05555         m_N.Append(n);
05556       }
05557     }
05558   }
05559   return rc;
05560 }
05561 
05562 bool ON_Mesh::NormalizeTextureCoordinates()
05563 {
05564   ON_2fPoint t0;//, t1;
05565   int ti;
05566   const int vertex_count = m_V.Count();
05567   bool rc = HasSurfaceParameters();
05568   if ( rc )
05569   {
05570     const ON_2dPoint* S = m_S.Array();
05571     ON_Interval udom = m_srf_domain[0];
05572     ON_Interval vdom = m_srf_domain[1];
05573     rc = udom.IsIncreasing() && vdom.IsIncreasing();
05574     if ( !rc )
05575     {
05576       udom.Set(S[0].x,S[0].x);
05577       vdom.Set(S[0].y,S[0].y);
05578       for ( ti = 1; ti < vertex_count; ti++ )
05579       {
05580         if      ( S[ti].x < udom.m_t[0] ) udom.m_t[0] = S[ti].x;
05581         else if ( S[ti].x > udom.m_t[1] ) udom.m_t[1] = S[ti].x;
05582         if      ( S[ti].y < vdom.m_t[0] ) vdom.m_t[0] = S[ti].y;
05583         else if ( S[ti].y > vdom.m_t[1] ) vdom.m_t[1] = S[ti].y;
05584       }
05585       rc = udom.IsIncreasing() && vdom.IsIncreasing();
05586     }
05587 
05588     if (rc)
05589     {
05590       m_T.Reserve(vertex_count);
05591       m_T.SetCount(0);
05592       for (ti = 0; ti < vertex_count; ti++ )
05593       {
05594         t0.x = (float)udom.NormalizedParameterAt(S[ti].x);
05595         t0.y = (float)vdom.NormalizedParameterAt(S[ti].y);
05596         m_T.Append(t0);
05597       }
05598       m_packed_tex_domain[0].Set(0.0,1.0);
05599       m_packed_tex_domain[1].Set(0.0,1.0);
05600       m_packed_tex_rotate = false;
05601       m_Ttag.SetDefaultSurfaceParameterMappingTag();
05602       if ( m_mesh_parameters )
05603         m_mesh_parameters->m_texture_range = 1;
05604     }
05605   }
05606 
05607   return rc;
05608 }
05609 
05610 bool ON_Mesh::TransposeSurfaceParameters()
05611 {
05612         // swap m_srf_domain 
05613         ON_Interval temp = m_srf_domain[0];
05614         m_srf_domain[0]  = m_srf_domain[1];
05615         m_srf_domain[1]  = temp;
05616 
05617         double t = m_srf_scale[0];
05618         m_srf_scale[0] = m_srf_scale[1];
05619   m_srf_scale[1] = t;
05620 
05621   int S_count = m_S.Count();
05622   ON_2dPoint* S_array = m_S.Array();
05623   for (int i = 0; i < S_count; i++ )
05624   {
05625     ON_2dPoint& S = S_array[i];
05626     t = S.x; S.x = S.y; S.y = t;
05627   }
05628   return true;
05629 }
05630 
05631 bool ON_Mesh::HasPackedTextureRegion() const
05632 {
05633   return (    ON_IsValid(m_srf_scale[0])
05634            && m_srf_scale[0] > 0.0
05635            && ON_IsValid(m_srf_scale[1])
05636            && m_srf_scale[1] > 0.0
05637            && m_packed_tex_domain[0].IsInterval()
05638            && m_packed_tex_domain[1].IsInterval()
05639            && 0.0 <= m_packed_tex_domain[0].Min()
05640            && m_packed_tex_domain[0].Max() <= 1.0
05641            && 0.0 <= m_packed_tex_domain[1].Min()
05642            && m_packed_tex_domain[1].Max() <= 1.0
05643            && (   fabs(m_packed_tex_domain[0].Length()) < 1.0
05644                || fabs(m_packed_tex_domain[1].Length()) < 1.0)
05645          );
05646 }
05647 
05648 
05649 bool ON_Mesh::TransposeTextureCoordinates()
05650 {
05651   if ( !HasTextureCoordinates() )
05652     return false;
05653 
05654   const int vcnt = m_T.Count();
05655   int i;
05656 
05657   bool bPackedRegion =  HasPackedTextureRegion();
05658 
05659   bool bSrfParamTag = (!m_Ttag.IsSet() || m_Ttag.IsDefaultSurfaceParameterMapping());
05660 
05661   if ( bPackedRegion && bSrfParamTag )
05662   {
05663     // The region of the bitmap the texture uses 
05664     // cannot change.  The texture coordinates 
05665     // themselves get reflected in the subrectangle
05666     // about either the lowerleft to upperright
05667     // diagonal (llur = true) or the lowerright
05668     // to upperleft diagonal (llur = false).
05669     bool bRevU = m_packed_tex_domain[0].IsDecreasing();
05670     bool bRevV = m_packed_tex_domain[1].IsDecreasing();
05671     bool llur = (bRevU == bRevV) ? true : false;
05672     if ( m_packed_tex_rotate )
05673       llur = !llur;
05674 
05675     ON_Interval TD[2];
05676         TD[0] = m_packed_tex_domain[0];
05677           TD[1] = m_packed_tex_domain[1];
05678           TD[0].MakeIncreasing(); 
05679           TD[1].MakeIncreasing(); 
05680     for( i=0; i<vcnt; i++)
05681     {
05682             ON_2fPoint tc = m_T[i];
05683             double x = TD[0].NormalizedParameterAt(tc[0]);
05684             double y = TD[1].NormalizedParameterAt(tc[1]);
05685             if(!llur)
05686       {
05687                     x = 1.0-x;
05688                     y = 1.0-y;
05689             }   
05690             double s = TD[0].ParameterAt(y);
05691             double t = TD[1].ParameterAt(x);
05692             m_T[i].Set((float)s,(float)t);
05693     }
05694   }
05695   else
05696   {
05697     float f;
05698           for(i=0; i<vcnt; i++)
05699     {
05700                   ON_2fPoint& tc = m_T[i];
05701       f = tc.x; tc.x = tc.y; tc.y = f;
05702           }
05703   }
05704         return true;
05705 }
05706 
05707 bool ON_Mesh::ReverseTextureCoordinates( int dir )
05708 {
05709   if ( dir < 0 || dir > 1 || !HasTextureCoordinates() )
05710     return false;
05711 
05712   bool bPackedRegion =  HasPackedTextureRegion();
05713 
05714   bool bSrfParamTag = (!m_Ttag.IsSet() || m_Ttag.IsDefaultSurfaceParameterMapping());
05715 
05716   const int vcount = m_T.Count();
05717   int i;
05718   if ( bPackedRegion && bSrfParamTag )
05719   {
05720     // The region of the bitmap the texture uses 
05721     // cannot change.  The texture coordinates 
05722     // themselves get reflected in the subrectangle
05723     // about either the lowerleft to upperright
05724     // diagonal (llur = true) or the lowerright
05725     // to upperleft diagonal (llur = false).
05726     if ( m_packed_tex_rotate )
05727       dir = 1-dir;
05728     const ON_Interval tex_dom = m_packed_tex_domain[dir];
05729     double s;
05730     m_packed_tex_domain[dir].Swap(); // Swap() is correct - Reverse() is wrong.
05731     for (i = 0; i < vcount; i++ )
05732     {
05733       ON_2fPoint& tc = m_T[i];
05734       s = 1.0 - tex_dom.NormalizedParameterAt(tc[dir]);
05735       if ( dir )
05736         tc.y = (float)tex_dom.ParameterAt(s);
05737       else
05738         tc.x = (float)tex_dom.ParameterAt(s);
05739     }
05740   }
05741   else
05742   {
05743     for (i = 0; i < vcount; i++ )
05744     {
05745       ON_2fPoint& tc = m_T[i];
05746       if ( dir )
05747         tc.y = 1.0f-tc.y;
05748       else
05749         tc.x = 1.0f-tc.x;
05750     }
05751   }
05752         return true;
05753 }
05754 
05755 bool ON_Mesh::ReverseSurfaceParameters( int dir )
05756 {
05757   if ( dir < 0 || dir > 1 || !HasSurfaceParameters() )
05758     return false;
05759   if ( m_srf_domain[dir].IsIncreasing() )
05760     m_srf_domain[dir].Reverse();
05761   int i, vcount = m_S.Count();
05762   for (i = 0; i < vcount; i++)
05763   {
05764     ON_2dPoint& S = m_S[i];
05765     if ( dir )
05766       S.y = -S.y;
05767     else
05768       S.x = -S.x;
05769   }
05770   return true;
05771 }
05772 
05773 
05774 bool ON_Mesh::EvaluateMeshGeometry( const ON_Surface& srf )
05775 {
05776   bool rc = false;
05777   const int vcount = VertexCount();
05778   const bool bHasSurfaceParameters = HasSurfaceParameters();
05779   if ( bHasSurfaceParameters )
05780   {
05781     const bool bHasVertexNormals = HasVertexNormals();
05782     m_N.SetCapacity(vcount);
05783     int vi, side, hint[2];
05784     ON_3dPoint point;
05785     ON_3dVector normal, Ds, Dt, Dss, Dst, Dtt, K1, K2;
05786     const ON_2dPoint* srf_st;
05787     double s, t, kgauss, kmean;
05788     side = 0;
05789     hint[0] = 0;
05790     hint[1] = 0;
05791     const double smax = srf.Domain(0)[1];
05792     const double tmax = srf.Domain(1)[1];
05793     if ( HasPrincipalCurvatures() ) 
05794     {
05795       for ( vi = 0; vi < vcount; vi++ ) 
05796       {
05797         //ON_2fPoint& tex = m_T[vi];
05798         // convert texture coordinates to normalizied texture coordinates
05799         srf_st = &m_S[vi];
05800         s = srf_st->x;
05801         t = srf_st->y;
05802         // 19 April 2006 Dale Lear - added side setter so singular normals
05803         //                           are correctly evaluated RR 12482
05804         side = ( smax == s ) ? ((tmax == t) ? 3 : 2) : ((tmax == t) ? 4 : 1);
05805         srf.Ev2Der( s, t, point, Ds, Dt, Dss, Dst, Dtt, side, hint );
05806         ON_EvNormal( side, Ds, Dt, Dss, Dst, Dtt, normal );
05807         ON_EvPrincipalCurvatures( Ds, Dt, Dss, Dst, Dtt, normal,
05808                                   &kgauss, &kmean, 
05809                                   &m_K[vi].k1, &m_K[vi].k2, 
05810                                   K1, K2 ); //m_K[vi].e1, m_K[vi].e2 );
05811         m_V[vi] = &point.x; // use ON_3fPoint double* conversion (quiets gcc)
05812         if ( bHasVertexNormals )
05813           m_N[vi] = &normal.x; // use ON_3fVector double* conversion (quiets gcc)
05814       }
05815       InvalidateCurvatureStats();
05816     }
05817     else if ( bHasVertexNormals ) 
05818     {
05819       for ( vi = 0; vi < vcount; vi++ ) 
05820       {
05821         //ON_2fPoint& tex = m_T[vi];
05822         srf_st = &m_S[vi];
05823         s = srf_st->x;
05824         t = srf_st->y;
05825         // 19 April 2006 Dale Lear - added side setter so singular normals
05826         //                           are correctly evaluated RR 12482
05827         side = ( smax == s ) ? ((tmax == t) ? 3 : 2) : ((tmax == t) ? 4 : 1);
05828         srf.EvNormal( s, t, point, normal, side, hint );
05829         m_V[vi] = &point.x; // use ON_3fPoint double* conversion (quiets gcc)
05830         m_N[vi] = &normal.x; // use ON_3fVector double* conversion (quiets gcc)
05831       }
05832     }
05833     else 
05834     {
05835       for ( vi = 0; vi < vcount; vi++ ) 
05836       {
05837         //ON_2fPoint& tex = m_T[vi];
05838         srf_st = &m_S[vi];
05839         s = srf_st->x;
05840         t = srf_st->y;
05841         srf.EvPoint( s, t, point, side, hint );
05842         m_V[vi] = &point.x;
05843       }
05844     }
05845     if ( HasFaceNormals() )
05846     {
05847       ComputeFaceNormals();
05848     }
05849     rc = true;
05850 
05851     m_Ctag.Default();
05852     InvalidateVertexBoundingBox();
05853     InvalidateVertexNormalBoundingBox();
05854     DeleteMeshParameters();
05855     DestroyTree();
05856   }
05857   return rc;
05858 }
05859 
05860 void ON_Mesh::SetMeshParameters( const ON_MeshParameters& mp )
05861 {
05862   DeleteMeshParameters();
05863   m_mesh_parameters = new ON_MeshParameters(mp);
05864 }
05865 
05866 const ON_MeshParameters* ON_Mesh::MeshParameters() const
05867 {
05868   return m_mesh_parameters;
05869 }
05870 
05871 void ON_Mesh::DeleteMeshParameters()
05872 {
05873   if ( m_mesh_parameters ) {
05874     delete m_mesh_parameters;
05875     m_mesh_parameters = 0;
05876   }
05877 }
05878 
05879 static int compare3fPoint( const ON_3fPoint* a, const ON_3fPoint* b )
05880 {
05881   if ( a->x < b->x ) return -1;
05882   if ( a->x > b->x ) return  1;
05883   if ( a->y < b->y ) return -1;
05884   if ( a->y > b->y ) return  1;
05885   if ( a->z < b->z ) return -1;
05886   if ( a->z > b->z ) return  1;
05887   return 0;
05888 }
05889 
05890 static int compare3dPoint( const ON_3dPoint* a, const ON_3dPoint* b )
05891 {
05892   if ( a->x < b->x ) return -1;
05893   if ( a->x > b->x ) return  1;
05894   if ( a->y < b->y ) return -1;
05895   if ( a->y > b->y ) return  1;
05896   if ( a->z < b->z ) return -1;
05897   if ( a->z > b->z ) return  1;
05898   return 0;
05899 }
05900 
05901 typedef int (*ON_COMPAR_LPVOID_LPVOID)(const void*,const void*);
05902 
05903 static 
05904 int GetPointMap( int pt_count, const ON_3fPoint* fV, const ON_3dPoint* dV, ON_SimpleArray<int>& pt_map )
05905 {
05906   // Builds a mapping array, pt_map[], such that the length of pt_map[] 
05907   // is pt_count and pt_map[i] == pt_map[j] if and only if pt[i] == pt[j]
05908   // as 3d points.  The values in map[] run from 0 to max_pt_index.
05909   int vt0, vt1;
05910   ON_3fPoint fp0;
05911   ON_3dPoint dp0;
05912   int* map;
05913   int* index;
05914   int max_pt_index = 0;
05915   if ( pt_count > 0 && (dV || fV) )
05916   {
05917     index = (int*)onmalloc(pt_count*sizeof(*index));
05918     
05919     if ( dV )
05920       ON_Sort( ON::quick_sort, index, dV, pt_count, sizeof(*dV), (ON_COMPAR_LPVOID_LPVOID)compare3dPoint );
05921     else
05922       ON_Sort( ON::quick_sort, index, fV, pt_count, sizeof(*fV), (ON_COMPAR_LPVOID_LPVOID)compare3fPoint );
05923     
05924     pt_map.SetCapacity( pt_count );
05925     pt_map.SetCount( pt_count );
05926     map = pt_map.Array();
05927     for ( vt0 = 0; vt0 < pt_count; vt0++ )
05928       map[vt0] = -1;
05929 
05930     if ( dV )
05931     {
05932       for (vt0 = 0; vt0 < pt_count; vt0 = vt1, max_pt_index++) 
05933       {
05934         dp0 = dV[index[vt0]];
05935         for ( vt1=vt0+1; vt1<pt_count && 0==compare3dPoint(&dp0,dV+index[vt1]); vt1++ ) {
05936           // empty
05937         }
05938         while ( vt0 < vt1 ) {
05939           map[index[vt0++]] = max_pt_index;
05940         }
05941       }
05942     }
05943     else
05944     {
05945       for (vt0 = 0; vt0 < pt_count; vt0 = vt1, max_pt_index++) 
05946       {
05947         fp0 = fV[index[vt0]];
05948         for ( vt1=vt0+1; vt1<pt_count && 0==compare3fPoint(&fp0,fV+index[vt1]); vt1++ ) {
05949           // empty
05950         }
05951         while ( vt0 < vt1 ) {
05952           map[index[vt0++]] = max_pt_index;
05953         }
05954       }
05955     }
05956     onfree(index);
05957   }
05958   if ( max_pt_index == 0 )
05959     pt_map.Destroy();
05960   return max_pt_index;
05961 }
05962 
05963 int ON_Mesh::CullDegenerateFaces()
05964 {
05965   // 30 December 2003 Dale Lear - fixed bug that was not culling
05966   //     topologically degenerate faces.
05967 
05968   int bad_count = 0;
05969   int degenerate_count = 0;
05970   const int fcount = m_F.Count();
05971   const int vcount = m_V.Count();
05972   ON_MeshFace f;
05973   int fi;
05974 
05975   if ( fcount > 0 ) 
05976   {
05977     // use GetTopologyVertexMap() instead of calling Topology() because this mesh
05978     // may have lots of bogus faces that need to be culled.
05979     ON_SimpleArray<int> topv_map;
05980     const ON_3dPoint* dV = Mesh_dV(*this);
05981     const ON_3fPoint* fV = (0 == dV) ? m_V.Array() : 0;
05982     const int topv_count = GetPointMap( m_V.Count(), fV, dV, topv_map );
05983     if ( topv_count > 0 && topv_map.Count() == m_V.Count() ) 
05984     {
05985       ON_Workspace ws;
05986       const int* vtop = topv_map.Array();
05987       unsigned char* bBadFace = (unsigned char*)ws.GetMemory(fcount*sizeof(*bBadFace));
05988       memset( bBadFace, 0, fcount*sizeof(*bBadFace) );
05989       for ( fi = 0; fi < fcount; fi++ ) 
05990       {
05991         ON_MeshFace& f0 = m_F[fi];
05992         // set f.vi[] to values of topoligical indices
05993         {
05994           int f0vi = f0.vi[0];
05995           f.vi[0] = (f0vi < 0 || f0vi >= vcount) ? -1 : vtop[f0vi];
05996 
05997           f0vi = f0.vi[1];
05998           f.vi[1] = (f0vi < 0 || f0vi >= vcount) ? -1 : vtop[f0vi];
05999 
06000           f0vi = f0.vi[2];
06001           f.vi[2] = (f0vi < 0 || f0vi >= vcount) ? -1 : vtop[f0vi];
06002 
06003           f0vi = f0.vi[3];
06004           f.vi[3] = (f0vi < 0 || f0vi >= vcount) ? -1 : vtop[f0vi];
06005         }
06006 
06007         if ( !f.IsValid(topv_count) ) 
06008         {
06009           degenerate_count++;
06010           //f = m_F[fi];
06011           if ( f.vi[0] == f.vi[1] || f.vi[0] < 0 || f.vi[0] >= topv_count )
06012           {
06013             f0.vi[0] = f0.vi[1];
06014             f0.vi[1] = f0.vi[2];
06015             f0.vi[2] = f0.vi[3];
06016             f.vi[0] = f.vi[1];
06017             f.vi[1] = f.vi[2];
06018             f.vi[2] = f.vi[3];
06019           }
06020 
06021           if ( f.vi[1] == f.vi[2] || f.vi[1] < 0 || f.vi[1] >= topv_count )
06022           {
06023             f0.vi[1] = f0.vi[2];
06024             f0.vi[2] = f0.vi[3];
06025             f.vi[1] = f.vi[2];
06026             f.vi[2] = f.vi[3];
06027           }
06028 
06029           if ( f.vi[2] < 0 || f.vi[2] >= topv_count )
06030           {
06031             f0.vi[2] = f0.vi[3];
06032             f.vi[2] = f.vi[3];
06033           }
06034 
06035           if ( f.vi[3] < 0 || f.vi[3] >= topv_count )
06036           {
06037             f0.vi[3] = f0.vi[2];
06038             f.vi[3] = f.vi[2];
06039           }
06040           else if ( f.vi[0] == f.vi[3] && f.vi[2] != f.vi[3] )
06041           {
06042             f0.vi[0] = f0.vi[1];
06043             f0.vi[1] = f0.vi[2];
06044             f0.vi[2] = f0.vi[3];
06045             f.vi[0] = f.vi[1];
06046             f.vi[1] = f.vi[2];
06047             f.vi[2] = f.vi[3];
06048           }
06049 
06050           if ( !f0.IsValid(vcount) || !f.IsValid(topv_count) ) 
06051           {
06052             // face cannot be repaired by juggling vertex indices
06053             bBadFace[fi] = 1;
06054             bad_count++;
06055           }
06056         }
06057       }
06058 
06059       if ( bad_count > 0 ) 
06060       {
06061         //  remove bad faces.
06062         bool bHasFN = (m_FN.Count() == m_F.Count());
06063         if ( !bHasFN )
06064           m_FN.SetCount(0);
06065 
06066         int fcnt = 0;
06067         for ( fi = fcnt = 0; fi < fcount; fi++ )
06068         {
06069           if ( !bBadFace[fi] )
06070           {
06071             if ( fcnt < fi )
06072             {
06073               m_F[fcnt] = m_F[fi];
06074               if ( bHasFN )
06075                 m_FN[fcnt] = m_FN[fi];
06076             }
06077             fcnt++;
06078           }
06079         }
06080 
06081         m_F.SetCount(fcnt);
06082         if ( bHasFN )
06083           m_FN.SetCount(fcnt);
06084       }
06085 
06086       if ( degenerate_count > 0 )
06087       {
06088         // mesh was modified - destroy information that was
06089         // calculated using the old mesh
06090         DestroyTree();
06091         DestroyPartition();
06092         DestroyTopology();
06093         m_invalid_count = 0;
06094         m_quad_count = 0;
06095         m_triangle_count = 0;
06096       }
06097     }
06098   }
06099   return degenerate_count;
06100 }
06101 
06102 int ON_Mesh::CullUnusedVertices()
06103 {
06104   //int cullcount = 0;
06105   int vi, fi;
06106   ON_Workspace ws;
06107   CullDegenerateFaces();
06108   int fcnt = m_F.Count();
06109   int vcnt = m_V.Count();
06110   int * vmap = ws.GetIntMemory( vcnt );
06111   memset ( vmap, 0, vcnt*sizeof(vmap[0]) );
06112   for ( fi = 0; fi < fcnt; fi++ )
06113   {
06114     const ON_MeshFace& f = m_F[fi];
06115     vmap[f.vi[0]] = 1;
06116     vmap[f.vi[1]] = 1;
06117     vmap[f.vi[2]] = 1;
06118     vmap[f.vi[3]] = 1;
06119   }
06120 
06121   int newvcnt = 0;
06122   for ( vi = 0; vi < vcnt; vi++ )
06123   {
06124     if ( vmap[vi] )
06125       vmap[vi] = newvcnt++;
06126     else {
06127       vmap[vi] = -1;
06128     }
06129   }
06130 
06131   if ( newvcnt == 0 )
06132     Destroy();
06133   else if ( newvcnt < vcnt )
06134   {
06135     DestroyTopology();
06136 
06137     // buffer will hold up to vcnt 3d points
06138     void* buffer = ws.GetMemory(vcnt*9*sizeof(double));
06139 
06140     if ( HasSurfaceParameters() ) {
06141       ON_2dPoint* s = (ON_2dPoint*)buffer;
06142       for ( vi = 0; vi < vcnt; vi++ )
06143       {
06144         if ( vmap[vi]>=0 )
06145           s[vmap[vi]] = m_S[vi];
06146       }
06147       memcpy( m_S.Array(), s, newvcnt*sizeof(s[0]) );
06148       m_S.SetCount(newvcnt);
06149     }
06150 
06151     if ( HasDoublePrecisionVertices() )
06152     {
06153       ON_3dPointArray& D = DoublePrecisionVertices();
06154       if ( vcnt == D.Count() )
06155       {
06156         bool bValidDoubles = DoublePrecisionVerticesAreValid();
06157         ON_3dPoint* s = (ON_3dPoint*)buffer;
06158         for ( vi = 0; vi < vcnt; vi++ )
06159         {
06160           if ( vmap[vi]>=0 )
06161             s[vmap[vi]] = D[vi];
06162         }
06163         memcpy( D.Array(), s, newvcnt*sizeof(s[0]) );
06164         D.SetCount(newvcnt);
06165         if ( bValidDoubles )
06166           SetDoublePrecisionVerticesAsValid();
06167       }
06168       else
06169       {
06170         DestroyDoublePrecisionVertices();
06171       }
06172     }
06173 
06174     if ( HasVertexNormals() ) {
06175       ON_3fVector* v = (ON_3fVector*)buffer;
06176       for ( vi = 0; vi < vcnt; vi++ )
06177       {
06178         if ( vmap[vi]>=0 )
06179           v[vmap[vi]] = m_N[vi];
06180       }
06181       memcpy( m_N.Array(), v, newvcnt*sizeof(v[0]) );
06182       m_N.SetCount(newvcnt);
06183     }
06184 
06185     if ( HasTextureCoordinates() ) {
06186       ON_2fPoint* t = (ON_2fPoint*)buffer;
06187       for ( vi = 0; vi < vcnt; vi++ )
06188       {
06189         if ( vmap[vi]>=0 )
06190           t[vmap[vi]] = m_T[vi];
06191       }
06192       memcpy( m_T.Array(), t, newvcnt*sizeof(t[0]) );
06193       m_T.SetCount(newvcnt);
06194     }
06195 
06196     if ( HasPrincipalCurvatures() ) {
06197       ON_SurfaceCurvature* k = (ON_SurfaceCurvature*)buffer;
06198       for ( vi = 0; vi < vcnt; vi++ )
06199       {
06200         if ( vmap[vi]>=0 )
06201           k[vmap[vi]] = m_K[vi];
06202       }
06203       memcpy( m_K.Array(), k, newvcnt*sizeof(k[0]) );
06204       m_K.SetCount(newvcnt);
06205     }
06206 
06207     if ( HasVertexColors() ) {
06208       ON_Color* c = (ON_Color*)buffer;
06209       for ( vi = 0; vi < vcnt; vi++ )
06210       {
06211         if ( vmap[vi]>=0 )
06212           c[vmap[vi]] = m_C[vi];
06213       }
06214       memcpy( m_C.Array(), c, newvcnt*sizeof(c[0]) );
06215       m_C.SetCount(newvcnt);
06216     }
06217 
06218     {
06219       bool bValidSingles = SinglePrecisionVerticesAreValid();
06220       ON_3fPoint* p = (ON_3fPoint*)buffer;
06221       for ( vi = 0; vi < vcnt; vi++ )
06222       {
06223         if ( vmap[vi]>=0 )
06224           p[vmap[vi]] = m_V[vi];
06225       }
06226       memcpy( m_V.Array(), p, newvcnt*sizeof(p[0]) );
06227       m_V.SetCount(newvcnt);
06228       if ( bValidSingles )
06229         SetSinglePrecisionVerticesAsValid();
06230     }
06231 
06232     for ( fi = 0; fi < fcnt; fi++ )
06233     {
06234       ON_MeshFace& f = m_F[fi];
06235       f.vi[0] = vmap[f.vi[0]];
06236       f.vi[1] = vmap[f.vi[1]];
06237       f.vi[2] = vmap[f.vi[2]];
06238       f.vi[3] = vmap[f.vi[3]];
06239     }
06240 
06241   }
06242   return vcnt - newvcnt;
06243 }
06244 
06245 bool ON_Mesh::Compact()
06246 {
06247   // CullDegenerateFaces(); // CullUnusedVertices() does this
06248   CullUnusedVertices();
06249   m_V.Shrink();
06250   m_F.Shrink();
06251   m_N.Shrink();
06252   m_FN.Shrink();
06253   m_K.Shrink();
06254   m_C.Shrink();
06255   m_S.Shrink();
06256   m_T.Shrink();
06257   return true;
06258 }
06259 
06261 //
06262 //   ON_SurfaceCurvature
06263 //
06264 double ON_SurfaceCurvature::GaussianCurvature() const
06265 {
06266   return k1*k2;
06267 }
06268 
06269 double ON_SurfaceCurvature::MeanCurvature() const
06270 {
06271   return 0.5*(k1+k2);
06272 }
06273 
06274 double ON_SurfaceCurvature::MinimumRadius() const
06275 {
06276   double k;
06277   k = (fabs(k1)>=fabs(k2)) ? fabs(k1) : fabs(k2); // k = maximum directional curvature
06278   k = ( k > 1.0e-300 ) ? 1.0/k : 1.0e300;         // 1/k = minimum radius of curvature
06279   return k;
06280 }
06281 
06282 double ON_SurfaceCurvature::MaximumRadius() const
06283 {
06284   double k;
06285   if ( k1*k2 <= 0.0 ) {
06286     // if principal curvatures have opposite signs, there
06287     // is a direction with zero directional curvature
06288     k = 0.0;
06289   }
06290   else {
06291     k = (fabs(k1)<=fabs(k2)) ? fabs(k1) : fabs(k2);
06292   }
06293   // k = minimum directional curvature
06294   k = ( k > 1.0e-300 ) ? 1.0/k : 1.0e300; // 1/k = maximum radius of curvature
06295   return k;
06296 }
06297 
06298 //double ON_SurfaceCurvature::NormalCurvature(const ON_3dVector& tangent) const
06299 //{
06300 //  double c = tangent*e1;
06301 //  double s = tangent*e2;
06302 //  return k1*c*c + k2*s*s;
06303 //}
06304 
06305 //double ON_SurfaceCurvature::NormalSectionCurvature( const ON_3dVector& section_normal, const ON_3dVector& surface_normal ) const
06306 //{
06307 //  ON_3dVector tangent = ON_CrossProduct( section_normal, surface_normal );
06308 //  if ( fabs(tangent.x) <= ON_SQRT_EPSILON && fabs(tangent.y) <= ON_SQRT_EPSILON && fabs(tangent.z) <= ON_SQRT_EPSILON )
06309 //    tangent.Zero();
06310 //  else
06311 //    tangent.Unitize();
06312 //  return NormalCurvature(tangent);
06313 //}
06314 
06315 ON_MeshTopology::ON_MeshTopology() 
06316 : m_mesh(0)
06317 , m_memchunk(0)
06318 , m_b32IsValid(0)
06319 {
06320 }
06321 
06322 ON_MeshTopology::~ON_MeshTopology()
06323 {
06324   Destroy();
06325 }
06326 
06327 void ON_MeshTopology::Destroy()
06328 {
06329   m_topv_map.Destroy();
06330   m_topv.Destroy();
06331   m_tope.Destroy();
06332   m_topf.Destroy();
06333   struct memchunk *p, *n;
06334   n = m_memchunk;
06335   while(n)
06336   {
06337     p = n;
06338     n = n->next;
06339     onfree(p);
06340   }
06341   m_memchunk = 0;
06342   if ( -1 != m_b32IsValid)
06343     m_b32IsValid = 0;
06344 }
06345 
06346 void ON_MeshTopology::EmergencyDestroy()
06347 {
06348   m_mesh = 0;
06349   m_topv_map.EmergencyDestroy();
06350   m_topv.EmergencyDestroy();
06351   m_tope.EmergencyDestroy();
06352   m_topf.EmergencyDestroy();
06353   m_memchunk = 0;
06354   m_b32IsValid = 0;
06355 }
06356 
06357 int ON_MeshTopology::TopVertexCount() const
06358 {
06359   return m_topv.Count();
06360 }
06361 
06363 // number of topoligical edges
06364 int ON_MeshTopology::TopEdgeCount() const
06365 {
06366   return m_tope.Count();
06367 }
06368 
06370 // number of topoligical faces (same as m_mesh.FaceCount())
06371 int ON_MeshTopology::TopFaceCount() const
06372 {
06373   return m_topf.Count();
06374 }
06375 
06376 ON_3fPoint ON_MeshTopology::TopVertexPoint( int vtopi ) const
06377 {
06378   return m_mesh->m_V[m_topv[vtopi].m_vi[0]];
06379 }
06380 
06381 ON_Line ON_MeshTopology::TopEdgeLine( int tope_index ) const
06382 {
06383   ON_Line L(ON_UNSET_POINT,ON_UNSET_POINT);
06384   if ( m_mesh && tope_index >= 0 && tope_index < m_tope.Count() )
06385   {
06386     const int* topvi = m_tope[tope_index].m_topvi;
06387     if (   topvi[0] >= 0 && topvi[0] < m_topv.Count() 
06388         && topvi[1] >= 0 && topvi[1] < m_topv.Count() )
06389     {
06390       const ON_MeshTopologyVertex& v0 = m_topv[topvi[0]];
06391       const ON_MeshTopologyVertex& v1 = m_topv[topvi[1]];
06392       if ( v0.m_v_count > 0 && v0.m_vi && v1.m_v_count > 0 && v1.m_vi )
06393       {
06394         int vi0 = v0.m_vi[0];
06395         int vi1 = v1.m_vi[0];
06396         int vcount = m_mesh->m_V.Count();
06397         if ( vi0 >= 0 && vi0 < vcount && vi1 >= 0 && vi1 < vcount )
06398         {
06399           L.from = m_mesh->m_V[vi0];
06400           L.to   = m_mesh->m_V[vi1];
06401         }
06402       }
06403     }
06404   }
06405   return L;
06406 }
06407 
06409 // returns index of edge that connects topological vertices
06410 // returns -1 if no edge is found.
06411 int ON_MeshTopology::TopEdge( int vtopi0, int vtopi1 ) const
06412 {
06413   int i0, i1, ei, vi0;
06414   if ( vtopi0 > vtopi1 ) {vi0 = vtopi0; vtopi0 = vtopi1; vtopi1 = vi0;}
06415   if ( vtopi0 < vtopi1 ) {
06416     const int tope_count = TopEdgeCount();
06417     const ON_MeshTopologyEdge* tope = m_tope.Array(); // to speed array access
06418     i0 = 0;
06419     i1 = tope_count;
06420     while ( i0 < i1 )
06421     {
06422       ei = (i0+i1)/2;
06423       vi0 = tope[ei].m_topvi[0];
06424       if ( vi0 < vtopi0 ) {
06425         if ( i0 == ei )
06426           break;
06427         i0 = ei;
06428       }
06429       else if ( vi0 > vtopi0 ) {
06430         if ( i1 == ei )
06431           break;
06432         i1 = ei;
06433       }
06434       else {
06435         while (ei > 0 && tope[ei-1].m_topvi[0] == vtopi0 )
06436           ei--;
06437         while ( ei < tope_count && tope[ei].m_topvi[0] == vtopi0 ) {
06438           if ( tope[ei].m_topvi[1] == vtopi1 )
06439             return ei;
06440           ei++;
06441         }
06442         break;
06443       }
06444     }
06445   }
06446   return -1;
06447 }
06448 
06449 bool ON_MeshTopology::GetTopFaceVertices( int fi, int topvi[4] ) const
06450 {
06451   if ( fi >= 0 && fi < m_mesh->m_F.Count() ) {
06452     const int* fvi = m_mesh->m_F[fi].vi;
06453     topvi[0] = m_topv_map[fvi[0]];
06454     topvi[1] = m_topv_map[fvi[1]];
06455     topvi[2] = m_topv_map[fvi[2]];
06456     topvi[3] = m_topv_map[fvi[3]];
06457   }
06458   return true;
06459 }
06460 
06461 bool ON_MeshTopology::SortVertexEdges() const
06462 {
06463   bool rc = true;
06464   int topvi, topv_count = m_topv.Count();
06465   for ( topvi = 0; topvi < topv_count; topvi++ )
06466   {
06467     if ( !SortVertexEdges(topvi) )
06468       rc = false;
06469   }
06470   return rc;
06471 }
06472 
06473 // TODO make public and add to header file new ON_SortIntArray
06474 static
06475 void ON_ReverseIntArray(
06476         int* e,    // array of ints
06477         size_t  nel   // length of array
06478         )
06479 {
06480   int ei;
06481   size_t i;
06482   nel--;
06483   for ( i = 0; i<nel; i++, nel--)
06484   {
06485     ei = e[i];
06486     e[i] = e[nel];
06487     e[nel] = ei;
06488   }
06489 }
06490 
06491 bool ON_MeshTopology::SortVertexEdges(int topvi) const
06492 {
06493   if ( topvi < 0 || topvi >= m_topv.Count() )
06494     return false;
06495 
06496   const ON_MeshTopologyVertex& topv = m_topv[topvi];
06497   if ( topv.m_tope_count < 2 )
06498     return true;
06499 
06500   // Divide the edges that use this vertex into two sets:
06501   //   e1f[] = indices of edges that bound 1 face, 3 or 
06502   //           more faces, or no faces (in that order).
06503   //   e2f[] = indices of edges that bound exactly 2 faces.
06504   int i, j;
06505   int topei;
06506   int vei;
06507   int efcnt;
06508   int* new_tope = (int*)alloca(5*topv.m_tope_count*sizeof(new_tope[0]));
06509   int* e2f  = new_tope + topv.m_tope_count;
06510   int* e1f  = e2f + topv.m_tope_count;
06511   int e1fcnt = 0;
06512   int e2fcnt = 0;
06513   {
06514     int* e3f  = e1f + topv.m_tope_count; // e3f[] = edges with 3 or more faces
06515     int* e0f  = e3f + topv.m_tope_count; // e0f[] = edges with no faces
06516     int e3fcnt = 0;
06517     int e0fcnt = 0;
06518 
06519     for ( vei = 0; vei < topv.m_tope_count; vei++ )
06520     {
06521       topei = topv.m_topei[vei];
06522       if ( topei >= 0 && topei < m_tope.Count() )
06523       {
06524         const ON_MeshTopologyEdge& tope = m_tope[topei];
06525         if ( tope.m_topvi[0] == topvi || tope.m_topvi[1] == topvi )
06526         {
06527           efcnt = m_tope[topei].m_topf_count;
06528           if ( efcnt < 0 )
06529           {
06530             ON_ERROR("ON_MeshTopology::SortVertexEdges(int topvi) - m_tope[topei].m_topf_count < 0"); 
06531             return false;
06532           }
06533           switch(efcnt)
06534           {
06535           case 0: // edge has no faces 
06536             // (never happens if topology is from a valid ON_Mesh.)
06537             e0f[e0fcnt++] = topei;                                   
06538             break;
06539 
06540           case 1: // edge has exactly one face
06541             e1f[e1fcnt++] = topei; 
06542             break;
06543 
06544           case 2: // edge has exactly two faces
06545             e2f[e2fcnt++] = topei;
06546             break;
06547 
06548           default: // edge has 3 or more faces
06549             // (happens when mesh is non-manifold)
06550             e3f[e3fcnt++] = topei;
06551             break;
06552           }
06553         }
06554       }
06555     }
06556   
06557     // append list of non-manifold edges (3 or more faces) to e1f[]
06558     for ( i = 0; i < e3fcnt; i++ )
06559     {
06560       e1f[e1fcnt++] = e3f[i];
06561     }
06562 
06563     // append list of wire edges (0 faces) to e1f[]
06564     for ( i = 0; i < e0fcnt; i++ )
06565     {
06566       e1f[e1fcnt++] = e0f[i];
06567     }
06568 
06569     e0fcnt = 0;
06570     e3fcnt = 0;
06571     e0f = 0;
06572     e3f = 0;
06573   }
06574 
06575   // Put the edge indices in new_tope[] in radial order.
06576   // NOTE: The code below works for non-oriented meshes and
06577   //       non-manifold meshes.  If you change the code, you
06578   //       must make sure that it still works in these cases.
06579   if ( e1fcnt + e2fcnt != topv.m_tope_count )
06580   {
06581     ON_ERROR("ON_MeshTopology::SortVertexEdges() input vertex had bogus m_topei[]");
06582     return false;
06583   }
06584   int fi = -1;
06585   int next_topei = -1;
06586   int efi, fecnt, fei, next_fei;
06587   vei = 0;
06588   int vei0 = 0;
06589   int vei1 = 0;
06590   int elist_dir = 0;
06591   while(vei < topv.m_tope_count)
06592   {
06593     if ( next_topei >= 0 )
06594     {
06595       // continue with this group of edges
06596       topei = next_topei;
06597       next_topei = -1;
06598     }
06599     else if ( e1fcnt > 0 )
06600     {
06601       // start a new group of edges
06602       topei = *e1f++;
06603       e1fcnt--;
06604       vei1 = vei;
06605     }
06606     else if ( e2fcnt > 0 )
06607     {
06608       // start a new group of edges
06609       // (this only happens in non-manifold cases)
06610       topei = *e2f++;
06611       e2fcnt--;
06612       vei1 = vei;
06613     }
06614     else
06615     {
06616       ON_ERROR("ON_MeshTopology::SortVertexEdges() input vertex had bogus topology information.");
06617       return false;
06618     }
06619 
06620     if ( vei0 < vei1 )
06621     {
06622       if ( 1 == elist_dir )
06623       {
06624         // 30 December 2003 Dale Lear added this feature
06625         //   edges new_tope[vei0],...,new_tope[vei1-1] are radially sorted
06626         //   but the order is not counterclockwise with respect to
06627         //   the normal of the face attached to the first edge.
06628         //   So, this group of edges in new_tope[] needs to
06629         //   be reversed.
06630         ON_ReverseIntArray( new_tope+vei0, vei1-vei0 );
06631       }
06632       elist_dir = 0;
06633       vei0 = vei1;
06634     }
06635 
06636     new_tope[vei++] = topei;
06637 
06638     // search faces connected to tope for the next edge
06639     const ON_MeshTopologyEdge& tope = m_tope[topei];
06640     for ( efi = 0; next_topei < 0 && efi < tope.m_topf_count; efi++ )
06641     {
06642       fi = tope.m_topfi[efi];
06643       if ( fi < 0 || fi >= m_topf.Count() )
06644       {
06645         // bogus face index into m_topf[] array
06646         continue;
06647       }
06648 
06649       // find fei so that topf.m_topei[fei] = topei
06650       const ON_MeshTopologyFace& topf = m_topf[fi];
06651       fecnt = topf.IsQuad() ? 4 : 3;
06652       for ( fei = 0; fei < fecnt; fei++ )
06653       {
06654         if ( topf.m_topei[fei] != topei )
06655           continue;
06656 
06657         if ( tope.m_topvi[0] == topvi )
06658         {
06659           next_fei = ( topf.m_reve[fei] ) ?  1 : -1;
06660         }
06661         else
06662         {
06663           next_fei = ( topf.m_reve[fei] ) ? -1 :  1;
06664         }
06665 
06666         if ( 0 == elist_dir )
06667           elist_dir = next_fei;
06668 
06669         next_fei = (fei+next_fei+fecnt)%fecnt;
06670         next_topei = topf.m_topei[next_fei];
06671 
06672         // next_topei = candidate for the next edge
06673         //  Check to see that it is available by
06674         //  finding it in the e1f[] or e2f[] arrays.
06675         j = 0;
06676         for ( i = 0; i < e1fcnt; i++ )
06677         {
06678           if ( next_topei == e1f[i] )
06679           {
06680             // found it in the e1f list.
06681             for ( j = i+1; j < e1fcnt; j++ )
06682               e1f[j-1] = e1f[j];
06683             e1fcnt--;
06684             break;
06685           }
06686         }
06687         if ( 0 == j )
06688         {
06689           // search the e2f[] list
06690           for ( i = 0; i < e2fcnt; i++ )
06691           {
06692             if ( next_topei == e2f[i] )
06693             {
06694               for ( j = i+1; j < e2fcnt; j++ )
06695                 e2f[j-1] = e2f[j];
06696               e2fcnt--;
06697               break;
06698             }
06699           }
06700         }
06701         if ( 0 == j )
06702         {
06703           // the candidate was already used, check the next
06704           // face attached to this edge.
06705           next_topei = -1;
06706         }
06707         else
06708         {
06709           break;
06710         }
06711       }
06712     }
06713   }
06714 
06715   if ( topv.m_tope_count != vei )
06716   {
06717     ON_ERROR("ON_MeshTopology::SortVertexEdges() edge sorting error.");
06718     return false;
06719   }
06720 
06721   vei1 = vei;
06722   if ( vei0 < vei1 )
06723   {
06724     if ( 1 == elist_dir )
06725     {
06726       // 30 December 2003 Dale Lear added this feature
06727       //   edges new_tope[vei0],...,new_tope[vei1-1] are radially sorted
06728       //   but the order is not counterclockwise with respect to
06729       //   the normal of the face attached to the first edge.
06730       //   So, this group of edges in new_tope[] needs to
06731       //   be reversed.
06732       ON_ReverseIntArray( new_tope+vei0, vei1-vei0 );
06733     }
06734     elist_dir = 0;
06735     vei0 = vei1;
06736   }
06737 
06738   int* topv_m_topei = const_cast<int*>(topv.m_topei);
06739   for ( vei = 0; vei < topv.m_tope_count; vei++ )
06740   {
06741     topv_m_topei[vei] = new_tope[vei];
06742   }
06743 
06744   return true;
06745 }
06746 
06747 int* ON_MeshTopology::GetIntArray(int length)
06748 {
06749   int* a = 0;
06750   if ( length > 0 ) {
06751     struct memchunk* pm;
06752     pm = (struct memchunk*)onmalloc(length*sizeof(*a) + sizeof(*pm));
06753     if ( pm ) {
06754       pm->next = m_memchunk;
06755       m_memchunk = pm++;
06756       a = (int*)pm;
06757     }
06758   }
06759   return a;
06760 }
06761 
06762 bool ON_MeshTopologyFace::IsTriangle() const
06763 {
06764   return ( m_topei[2] == m_topei[3] && m_topei[0] != m_topei[1] )
06765          ? true : false;
06766 }
06767 
06768 bool ON_MeshTopologyFace::IsQuad() const
06769 {
06770   return ( m_topei[2] != m_topei[3] ) ? true : false;
06771 }
06772 
06773 bool ON_MeshTopologyFace::IsValid() const
06774 {
06775   return ( m_topei[0] != m_topei[1] ) ? true : false;
06776 }
06777 
06778 
06779 bool ON_MeshTopology::IsValid() const
06780 {
06781   ON_Workspace ws;
06782   int topvi, topei, topfi, vi, fi, j, jmax, k, tfvi[4];
06783   ON_3fPoint p;
06784 
06785   WaitUntilReady(0);
06786 
06787   // simple checks
06788   if ( 1 != m_b32IsValid )
06789     return false;
06790   if ( !m_mesh )
06791     return false;
06792   if ( this != &(m_mesh->Topology()) )
06793     return false;
06794   const int v_count = m_mesh->VertexCount();
06795   const int f_count = m_mesh->FaceCount();
06796   const int topv_count = TopVertexCount();
06797   const int tope_count = TopEdgeCount();
06798   const int topf_count = TopFaceCount();
06799   if ( topv_count > v_count || topv_count < 0 )
06800     return false;
06801   if ( topv_count == 0 && v_count > 0 )
06802     return false;
06803   if ( topf_count != f_count )
06804     return false;
06805   if ( f_count > 0 && tope_count < 3 )
06806     return false;
06807   if ( m_topv_map.Count() != v_count )
06808     return false;
06809 
06810   // check vertex information
06811   for ( vi = 0; vi < v_count; vi++ ) {
06812     topvi = m_topv_map[vi];
06813     if ( topvi < 0 || topvi >= topv_count )
06814       return false;
06815   }
06816 
06817   char* vCheck = (char*)ws.GetMemory( v_count*sizeof(*vCheck) );
06818   memset( vCheck, 0, v_count*sizeof(*vCheck) );
06819   for ( topvi = 0; topvi < topv_count; topvi++ )
06820   {
06821     const ON_MeshTopologyVertex& topv = m_topv[topvi];
06822     if ( topv.m_v_count <= 0 )
06823       return false;
06824     if ( !topv.m_vi )
06825       return false;
06826     p = TopVertexPoint(topvi);
06827     for ( j = 0; j < topv.m_v_count; j++ ) {
06828       vi = topv.m_vi[j];
06829       if ( vi < 0 )
06830         return false;
06831       if ( vi >= v_count )
06832         return false;
06833       if ( vCheck[vi] != 0 )
06834         return false; // mesh.m_V[vi] is referenced multiple times
06835       if ( compare3fPoint( &p, &m_mesh->m_V[vi] ) )
06836         return false; // mesh.m_V[vi] not at same location as topv
06837       if ( m_topv_map[vi] != topvi )
06838         return false; 
06839       vCheck[vi] = 1;
06840     }
06841 
06842     // check that edges in m_topei[] list have topvi has a start/end
06843     if ( topv.m_tope_count < 0 )
06844       return false;
06845     if ( topv.m_tope_count == 0 && topv.m_topei )
06846       return false; // array should be NULL
06847     if ( topv.m_tope_count > 0 && !topv.m_topei )
06848       return false; // array should not be NULL
06849     for ( j = 0; j < topv.m_tope_count; j++ ) {
06850       topei = topv.m_topei[j];
06851       if ( topei < 0 )
06852         return false;
06853       if ( topei >= tope_count )
06854         return false;
06855       const ON_MeshTopologyEdge& tope = m_tope[topei];
06856       if ( tope.m_topvi[0] != topvi && tope.m_topvi[1] != topvi )
06857         return false; // edge doesn't reference this top vtx
06858       for ( k = 0; k < j; k++ ) {
06859         if ( topv.m_topei[k] == topei )
06860           return false; // edge listed multiple times
06861       }
06862     }
06863   }
06864 
06865   // make sure every mesh.m_V[] maps to a topoligical vertex
06866   for ( vi = 0; vi < v_count; vi++ ) {
06867     if ( vCheck[vi] != 1 )
06868       return false; // mesh.m_V[vi] is not referenced
06869     topvi = m_topv_map[vi];
06870     if ( topvi < 0 )
06871       return false;
06872     if ( topvi >= topv_count )
06873       return false;
06874     const ON_MeshTopologyVertex& topv = m_topv[topvi];
06875     for ( j = 0; j < topv.m_v_count; j++ ) {
06876       if ( topv.m_vi[j] == vi )
06877         break;
06878     }
06879     if ( j >= topv.m_v_count )
06880       return false; // bogus m_topv_map[] array
06881   }
06882 
06883   // check edges
06884   for ( topei = 0; topei < tope_count; topei++ ) {
06885     const ON_MeshTopologyEdge& tope = m_tope[topei];
06886     if ( tope.m_topvi[0] < 0 || tope.m_topvi[0] >= topv_count )
06887       return false;
06888     if ( tope.m_topvi[1] < 0 || tope.m_topvi[1] >= topv_count )
06889       return false;
06890     if ( tope.m_topvi[0] == tope.m_topvi[1] )
06891       return false;
06892 
06893     const ON_MeshTopologyVertex& topv0 = m_topv[tope.m_topvi[0]];
06894     for ( j = 0; j < topv0.m_tope_count; j++ ) {
06895       if ( topv0.m_topei[j] == topei )
06896         break;
06897     }
06898     if ( j >= topv0.m_tope_count )
06899       return false; // start vtx not linked to edge
06900 
06901     const ON_MeshTopologyVertex& topv1 = m_topv[tope.m_topvi[1]];
06902     for ( j = 0; j < topv1.m_tope_count; j++ ) {
06903       if ( topv1.m_topei[j] == topei )
06904         break;
06905     }
06906     if ( j >= topv1.m_tope_count )
06907       return false; // end vtx not linked to edge
06908 
06909     if ( tope.m_topf_count < 0 )
06910       return false;
06911     if ( tope.m_topf_count == 0 && tope.m_topfi )
06912       return false; // array should be NULL
06913     if ( tope.m_topf_count > 0 && !tope.m_topfi )
06914       return false; // array should not be NULL
06915     for ( j = 0; j < tope.m_topf_count; j++ ) {
06916       fi = tope.m_topfi[j];
06917       if ( fi < 0 || fi >= f_count )
06918         return false;
06919       const ON_MeshTopologyFace& topf = m_topf[fi];
06920       for ( k = 0; k < 4; k++ ) {
06921         if ( topf.m_topei[k] == topei )
06922           break;
06923       }
06924       if ( k >= 4 )
06925         return false; // edge not in face's list
06926     }
06927   }
06928 
06929   // check faces
06930   for ( fi = 0; fi < f_count; fi++ ) {
06931     topfi = fi;
06932     const ON_MeshTopologyFace& topf = m_topf[topfi];
06933     const ON_MeshFace& f = m_mesh->m_F[fi];
06934     if ( topf.m_topei[0] < 0 || topf.m_topei[0] >= tope_count )
06935       return false;
06936     if ( topf.m_topei[1] < 0 || topf.m_topei[1] >= tope_count )
06937       return false;
06938     if ( topf.m_topei[2] < 0 || topf.m_topei[2] >= tope_count )
06939       return false;
06940     if ( topf.m_topei[3] < 0 || topf.m_topei[3] >= tope_count )
06941       return false;
06942     tfvi[0] = m_topv_map[f.vi[0]];
06943     tfvi[1] = m_topv_map[f.vi[1]];
06944     tfvi[2] = m_topv_map[f.vi[2]];
06945     tfvi[3] = m_topv_map[f.vi[3]];
06946     if (    topf.m_topei[0] != 0 || topf.m_topei[1] != 0 
06947          || topf.m_topei[2] != 0 || topf.m_topei[3] != 0 ) {
06948       if ( !f.IsValid(v_count) )
06949         return false;
06950       if ( f.IsTriangle() ) {
06951         if (topf.m_topei[2] != topf.m_topei[3] )
06952           return false;
06953         jmax = 3;
06954       }
06955       else {
06956         if (topf.m_topei[2] == topf.m_topei[3] )
06957           return false;
06958         jmax = 4;
06959       }
06960       for ( j = 0; j < jmax; j++ ) {
06961         const ON_MeshTopologyEdge& tope = m_tope[topf.m_topei[j]];
06962         for ( k = 0; k < tope.m_topf_count; k++ ) {
06963           if ( tope.m_topfi[k] == topfi )
06964             break;
06965         }
06966         if ( k >= tope.m_topf_count )
06967           return false;
06968 
06969         // topedge m_tope[topf.m_topei[j]] ENDS at topvtx m_topv[tfvi[j]]
06970         if ( topf.m_reve[j] ) {
06971           if ( tope.m_topvi[1] != tfvi[(j+3)%4] )
06972             return false;
06973           if ( tope.m_topvi[0] != tfvi[j] )
06974             return false;
06975         }
06976         else {
06977           if ( tope.m_topvi[0] != tfvi[(j+3)%4] )
06978             return false;
06979           if ( tope.m_topvi[1] != tfvi[j] )
06980             return false;
06981         }
06982       }      
06983     }
06984     else {
06985       // all topf.m_topei[] must be zeros
06986       if (    topf.m_topei[0] != 0 || topf.m_topei[1] != 0 
06987            || topf.m_topei[2] != 0 || topf.m_topei[3] != 0 )
06988         return false;
06989     }
06990   }
06991   return true;
06992 }
06993 
06994 void ON_MeshTopology::Dump( ON_TextLog& dump ) const
06995 {
06996   ON_3fPoint p;
06997   int vi, ei, fi, j;
06998   const int topv_count = m_topv.Count();
06999   const int tope_count = m_tope.Count();
07000   const int topf_count = m_topf.Count();
07001 
07002   // topological vertex information
07003   for ( vi = 0; vi < topv_count; vi++ ) {
07004     const ON_MeshTopologyVertex& v = m_topv[vi];
07005     dump.Print("topv %d: ",vi);
07006     if (m_mesh) {
07007       // dump geometric location of this vertex
07008       p = m_mesh->m_V[v.m_vi[0]];
07009       dump.Print("{%g,%g,%g} ", p.x, p.y, p.z);
07010     }
07011 
07012     // list all mesh geometry viertices that are coincident with this
07013     // topological vertex
07014     dump.Print("(");
07015     for ( j = 0; j < v.m_v_count; j++ ) {
07016       if ( j )
07017         dump.Print(",");
07018       dump.Print("m_V[%d]",v.m_vi[j]);
07019     }
07020 
07021     // list all toplogical edges that begin/end at this topological vertex
07022     dump.Print(") (");
07023     for ( j = 0; j < v.m_tope_count; j++ ) {
07024       if ( j )
07025         dump.Print(",");
07026       dump.Print("%d",v.m_topei[j]);
07027     }
07028     dump.Print(")\n");
07029   }
07030 
07031   // topological edge information
07032   for ( ei = 0; ei < tope_count; ei++ ) {
07033     const ON_MeshTopologyEdge& e = m_tope[ei];
07034     dump.Print("tope %d: topv%d to topvv%d (", ei, e.m_topvi[0], e.m_topvi[1] );
07035     // list all mesh topolical faces attached to this topolical edge
07036     for ( j = 0; j < e.m_topf_count; j++ ) {
07037       if (j)
07038         dump.Print(",");
07039       dump.Print("f%d",e.m_topfi[j]);
07040     }
07041     dump.Print(")\n");
07042   }
07043 
07044   // topological face information
07045   // mesh m_F[] index = mesh topology m_topf[] index
07046   for ( fi = 0; fi < topf_count; fi++ ) {
07047     const ON_MeshTopologyFace& f = m_topf[fi];
07048     dump.Print("topf %d: (",fi);
07049     for ( j = 0; j < 4; j++ ) {
07050       if ( j == 3 && f.m_topei[3] == f.m_topei[2] )
07051         break; // triangular face
07052       if (j)
07053         dump.Print(",");
07054       dump.Print("%ce%d",f.m_reve[j]?'-':'+',f.m_topei[j]);
07055     }
07056     dump.Print(")\n");
07057   }
07058 }
07059 
07060 
07061 bool ON_MeshTopology::Create()
07062 {
07063   // When -1 == m_b32IsValid, this ON_MeshTopology
07064   // is the m_top field on an ON_Mesh and is being
07065   // created in an ON_Mesh::Topology() call and a
07066   // sleep lock is used to keep ON_Mesh::Topology()
07067   // thread safe.
07068   //
07069   // When 0 == m_b32IsValid, this ON_MeshTopology
07070   // is being created stand alone.
07071   //
07072   // When 1 == m_b32IsValid, this ON_MeshTopology
07073   // is already created and valid.
07074 
07075   if ( 1 == m_b32IsValid )
07076     return true;
07077 
07078   const int b32IsValid0 = m_b32IsValid;
07079 
07080   if ( 0 == b32IsValid0 )
07081   {
07082     // no sleep lock is being used
07083     m_b32IsValid = -1;
07084   }
07085   int b32IsValid = b32IsValid0;
07086 
07087   while ( 0 == b32IsValid || -1 == b32IsValid ) 
07088   {
07089     // while() is for flow control - this is a while() {... break;} statment.
07090     Destroy();
07091     b32IsValid = 0;
07092 
07093     // build vertex topology information
07094     const int fcount = m_mesh->FaceCount();
07095     const int vcount = m_mesh->VertexCount();
07096     if ( 0 == vcount )
07097       break;
07098 
07099     int* vindex = GetIntArray(vcount);
07100     m_topv_map.SetCapacity( vcount );
07101     m_topv_map.SetCount( vcount );
07102     if (  0 == m_mesh->GetVertexLocationIds( 0, m_topv_map.Array(), vindex ) )
07103     {
07104       Destroy();
07105       break;
07106     }
07107 
07108     {
07109       int topv_count = m_topv_map[vindex[vcount-1]]+1;
07110       m_topv_map.SetCapacity( vcount );
07111       m_topv_map.SetCount( vcount );
07112       m_topv.SetCapacity( topv_count );
07113       int vt0, vt1, topvi;
07114       ON_3fPoint p0;
07115       for (vt0 = 0; vt0 < vcount; vt0 = vt1)
07116       {
07117         topvi = m_topv.Count();
07118 #if defined(ON_DEBUG)
07119         if ( topvi != m_topv_map[vindex[vt0]] )
07120         {
07121           // 20 April 2010 Dale Lear:
07122           //  If you get this error, save the mesh and tell Dale Lear.
07123           ON_ERROR("ON_MeshTopology::Create() - topvi != vertex id from GetVertexLocationIds()");
07124         }
07125 #endif
07126         ON_MeshTopologyVertex& topv = m_topv.AppendNew();
07127         topv.m_vi = vindex+vt0;
07128         for ( vt1=vt0+1; vt1<vcount && topvi == m_topv_map[vindex[vt1]]; vt1++ ) {
07129           // empty
07130         }
07131         topv.m_v_count = vt1-vt0;
07132       }
07133     }
07134 
07135     // build edge topology information
07136     const int topv_count = m_topv.Count();
07137     if ( topv_count >= 2 ) 
07138     {
07139       bool rc = false;
07140       int ei, ecnt, fi, vi0, vi1, efi, topfvi[4];
07141       ON_MeshFace f;
07142 
07143       if ( fcount > 0 && vcount > 0 ) 
07144       {
07145         // When working on this code be sure to test bug# 9271 and 9254 and file fsv_r4.3dm
07146         ON_Workspace ws;
07147         struct ON_MeshFaceSide* e = (struct ON_MeshFaceSide*)ws.GetMemory( 4*fcount*sizeof(*e) );
07148         ecnt = m_mesh->GetMeshFaceSideList( m_topv_map.Array(), e );
07149         
07150         if ( ecnt > 0 ) 
07151         {
07152           rc = true;
07153           ON_SortMeshFaceSidesByVertexIndex( ecnt, e );
07154 
07155           // count number of topological edges and allocate storage
07156           int etop_count = 0;
07157           ei = 0;
07158           while( ei < ecnt ) 
07159           {
07160             vi0 = e[ei].vi[0];
07161             vi1 = e[ei].vi[1];
07162             ei++;
07163             while (ei < ecnt && e[ei].vi[0] == vi0 && e[ei].vi[1] == vi1 )
07164               ei++;
07165             etop_count++;
07166           }
07167           m_tope.SetCapacity(etop_count);
07168 
07169           // fill in the m_tope[] array information
07170           int* efindex = GetIntArray(ecnt);
07171           for ( ei = 0; ei < ecnt; /*empty*/ )
07172           {
07173             ON_MeshTopologyEdge& tope = m_tope.AppendNew();
07174             tope.m_topvi[0] = vi0 = e[ei].vi[0];
07175             tope.m_topvi[1] = vi1 = e[ei].vi[1];
07176             tope.m_topfi = efindex;
07177             tope.m_topf_count = 0;
07178             *efindex++ = e[ei].fi;
07179             tope.m_topf_count++;
07180             ei++;
07181             while( ei < ecnt && e[ei].vi[0] == vi0 && e[ei].vi[1] == vi1 ) 
07182             {
07183               *efindex++ = e[ei].fi;
07184               tope.m_topf_count++;
07185               ei++;
07186             }
07187           }
07188           efindex = 0; // memory deallocated by ~ON_MeshTopology()
07189 
07190           // connect vertices to edges;
07191           ecnt = m_tope.Count();
07192           int* ve_count = (int*)onmalloc(topv_count*sizeof(*ve_count));
07193           // set ve_count[topvi] = number of edges that begin or end at m_topv[topvi]
07194           memset( ve_count, 0, topv_count*sizeof(*ve_count));
07195           for ( ei = 0; ei < ecnt; ei++ ) {
07196             const ON_MeshTopologyEdge& tope = m_tope[ei];
07197             ve_count[tope.m_topvi[0]]++;
07198             ve_count[tope.m_topvi[1]]++;
07199           }
07200           // allocate and distribute storage for the mopv.m_topei[] array
07201           int* vei = GetIntArray(2*ecnt);
07202           for ( vi0 = 0; vi0 < topv_count; vi0++ ) {
07203             ON_MeshTopologyVertex& topv = m_topv[vi0];
07204             if ( ve_count[vi0] > 0 ) {
07205               topv.m_topei = vei;
07206               vei += ve_count[vi0];
07207             }
07208           }
07209           onfree(ve_count); ve_count = 0;
07210           vei = 0; // memory deallocated by ~ON_MeshTopology()
07211 
07212           // fill in the m_topv[].m_topei[] values
07213           for ( ei = 0; ei < ecnt; ei++ ) {
07214             ON_MeshTopologyEdge& tope = m_tope[ei];
07215             ON_MeshTopologyVertex& topv0 = m_topv[tope.m_topvi[0]];
07216             ON_MeshTopologyVertex& topv1 = m_topv[tope.m_topvi[1]];
07217             vei = const_cast<int*>(topv0.m_topei);
07218             vei[topv0.m_tope_count++] = ei;
07219             vei = const_cast<int*>(topv1.m_topei);
07220             vei[topv1.m_tope_count++] = ei;
07221           }
07222 
07223           // build face topology information
07224           m_topf.SetCapacity(fcount);
07225           m_topf.SetCount(fcount);
07226           memset( m_topf.Array(), 0, fcount*sizeof(ON_MeshTopologyFace) );
07227           for ( fi = 0; fi < fcount; fi++ ) {
07228             ON_MeshTopologyFace& f = m_topf[fi];
07229             f.m_topei[0] = -1;
07230             f.m_topei[1] = -1;
07231             f.m_topei[2] = -1;
07232             f.m_topei[3] = -1;
07233           }
07234           for ( ei = 0; ei < ecnt; ei++ ) {
07235             ON_MeshTopologyEdge& tope = m_tope[ei];
07236             for (efi = 0; efi < tope.m_topf_count; efi++ ) {
07237               // Because ON_MeshFace.vi[2] == ON_MeshFace.vi[3] for triangles,
07238               // we have topf.m_topei[j] BEGIN at ON_MeshFace.vi[(j+3)%4] and END at ON_MeshFace.vi[j]
07239               fi = tope.m_topfi[efi];
07240               f = m_mesh->m_F[fi];
07241               topfvi[0] = m_topv_map[f.vi[0]];
07242               topfvi[1] = m_topv_map[f.vi[1]];
07243               topfvi[2] = m_topv_map[f.vi[2]];
07244               topfvi[3] = m_topv_map[f.vi[3]];
07245               ON_MeshTopologyFace& topf = m_topf[fi];
07246               vi0 = tope.m_topvi[0];
07247               vi1 = tope.m_topvi[1];
07248               // unroll loop for speed
07249               if      ( vi0 == topfvi[3] && vi1 == topfvi[0] ) {
07250                 topf.m_topei[0] = ei;
07251                 topf.m_reve[0] = 0;
07252               }
07253               else if ( vi0 == topfvi[0] && vi1 == topfvi[1] ) {
07254                 topf.m_topei[1] = ei;
07255                 topf.m_reve[1] = 0;
07256               }
07257               else if ( vi0 == topfvi[1] && vi1 == topfvi[2] ) {
07258                 topf.m_topei[2] = ei;
07259                 topf.m_reve[2] = 0;
07260               }
07261               else if ( vi0 == topfvi[2] && vi1 == topfvi[3] ) {
07262                 topf.m_topei[3] = ei;
07263                 topf.m_reve[3] = 0;
07264               }
07265               else if ( vi1 == topfvi[3] && vi0 == topfvi[0] ) {
07266                 topf.m_topei[0] = ei;
07267                 topf.m_reve[0] = 1;
07268               }
07269               else if ( vi1 == topfvi[0] && vi0 == topfvi[1] ) {
07270                 topf.m_topei[1] = ei;
07271                 topf.m_reve[1] = 1;
07272               }
07273               else if ( vi1 == topfvi[1] && vi0 == topfvi[2] ) {
07274                 topf.m_topei[2] = ei;
07275                 topf.m_reve[2] = 1;
07276               }
07277               else if ( vi1 == topfvi[2] && vi0 == topfvi[3] ) {
07278                 topf.m_topei[3] = ei;
07279                 topf.m_reve[3] = 1;
07280               }
07281             }
07282           }
07283           for ( fi = 0; fi < fcount; fi++ ) {
07284             ON_MeshTopologyFace& f = m_topf[fi];
07285             bool bIsGood = false;
07286             if (    f.m_topei[0] >= 0 && f.m_topei[1] >= 0 && f.m_topei[2] >=0 
07287                  && f.m_topei[0] != f.m_topei[1] 
07288                  && f.m_topei[1] != f.m_topei[2] 
07289                  && f.m_topei[2] != f.m_topei[0] 
07290                  ) {
07291               if ( m_mesh->m_F[fi].IsTriangle() ) {
07292                 bIsGood = true;
07293                 f.m_topei[3] = f.m_topei[2];
07294               }
07295               else if (   f.m_topei[3] >= 0 
07296                        && f.m_topei[0] != f.m_topei[3] 
07297                        && f.m_topei[1] != f.m_topei[3] 
07298                        && f.m_topei[2] != f.m_topei[3] ) {
07299                 bIsGood = true;
07300               }
07301             }
07302             if ( !bIsGood ) {
07303               f.m_topei[0] = 0;
07304               f.m_topei[1] = 0;
07305               f.m_topei[2] = 0;
07306               f.m_topei[3] = 0;
07307               f.m_reve[0] = 0;
07308               f.m_reve[1] = 0;
07309               f.m_reve[2] = 0;
07310               f.m_reve[3] = 0;
07311             }
07312           }
07313 
07314         }
07315       }
07316     }
07317 
07318     b32IsValid = 1;
07319     break;
07320   }
07321 
07322   if ( -1 != b32IsValid0 )
07323   {
07324     // no sleep lock is in use
07325     m_b32IsValid = b32IsValid;
07326   }
07327 
07328   if ( 1 != b32IsValid )
07329   {
07330     Destroy();
07331   }
07332 
07333   return (1 == b32IsValid);
07334 }
07335 
07336 struct tagFPT
07337 {
07338   int x, y, z;
07339 };
07340 
07341 //static int compare_fpt( const struct tagFPT* a, const struct tagFPT* b )
07342 //{
07343 //  if ( a->x < b->x )
07344 //    return -1;
07345 //  if ( a->x > b->x )
07346 //    return 1;
07347 //  if ( a->y < b->y )
07348 //    return -1;
07349 //  if ( a->y > b->y )
07350 //    return 1;
07351 //  if ( a->z < b->z )
07352 //    return -1;
07353 //  if ( a->z > b->z )
07354 //    return 1;
07355 //  return 0;
07356 //}
07357 
07358 static int compare_pmark( const int* a, const int* b )
07359 {
07360   if ( *a < *b )
07361     return -1;
07362   if ( *a > *b )
07363     return 1;
07364   if ( a < b )
07365     return -1;
07366   if ( a > b )
07367     return 1;
07368   return 0;
07369 }
07370 
07371 static int compare_vmap( const void* a, const void* b )
07372 {
07373   int i = *((int*)a);
07374   int j = *((int*)b);
07375   if ( i < j )
07376     return -1;
07377   if ( i > j )
07378     return 1;
07379   return 0;
07380 }
07381 
07382 static int DupVertex( ON_Mesh* mesh, int vi )
07383 {
07384   int vertex_count = mesh->m_V.Count();
07385   ON_3fVector n;
07386   ON_3fPoint v;
07387   ON_Color c;
07388   ON_2fPoint t;
07389   ON_SurfaceCurvature k;
07390   v = mesh->m_V[vi];
07391   mesh->m_V.Append(v);
07392   if (mesh->m_N.Count() == vertex_count) {
07393     n = mesh->m_N[vi];
07394     mesh->m_N.Append(n);
07395   }
07396   if (mesh->m_T.Count() == vertex_count) {
07397     t = mesh->m_T[vi];
07398     mesh->m_T.Append(t);
07399   }
07400   if (mesh->m_K.Count() == vertex_count) {
07401     k = mesh->m_K[vi];
07402     mesh->m_K.Append(k);
07403   }
07404   if (mesh->m_C.Count() == vertex_count) {
07405     c = mesh->m_C[vi];
07406     mesh->m_C.Append(c);
07407   }
07408   return vertex_count;
07409 }
07410 
07411 
07412 static int AddToPartition( ON_Mesh* mesh, ON_SimpleArray<int>& pmark, int vi, int partition_mark, int fi0 )
07413 {
07414   bool b = true;
07415   int i, fi, new_vi, face_count, *fvi;
07416   i = pmark[vi];
07417   if ( !i ) {
07418     pmark[vi] = partition_mark;
07419   }
07420   else if ( i != partition_mark && i != partition_mark-1 ) {
07421     if ( i == partition_mark-2 )
07422       pmark[vi] = partition_mark-1; // vertex vi shared between two partitions
07423     else {
07424       new_vi = DupVertex(mesh,vi);
07425       face_count = mesh->m_F.Count();
07426       for ( fi = fi0; fi < face_count; fi++ ) {
07427         fvi = mesh->m_F[fi].vi;
07428         if ( fvi[0] == vi )
07429           fvi[0] = new_vi;
07430         if ( fvi[1] == vi )
07431           fvi[1] = new_vi;
07432         if ( fvi[2] == vi )
07433           fvi[2] = new_vi;
07434         if ( fvi[3] == vi )
07435           fvi[3] = new_vi;
07436       }
07437       pmark.Append(partition_mark);
07438     }
07439   }
07440   else
07441     b = false; // vertex already in this partition
07442   return b;
07443 }
07444 
07445 bool ON_MeshPartition_IsValid( const ON_MeshPartition& p, const ON_Mesh& mesh )
07446 {
07447   bool rc = false;
07448   const int* fvi;
07449   int j, tcnt, fi, parti, partcount;
07450   partcount = p.m_part.Count();
07451   rc = ( partcount > 0 );
07452   if ( p.m_partition_max_triangle_count < 1 )
07453     rc = false;
07454   if ( p.m_partition_max_vertex_count < 3 )
07455     rc = false;
07456   for ( parti = 0; parti < partcount && rc; parti++ ) {
07457     const ON_MeshPart& part = p.m_part[parti];
07458     if ( part.triangle_count < 1 )
07459       rc = false;
07460     if ( part.vertex_count < 1 )
07461       rc = false;
07462     if ( part.vertex_count != part.vi[1] - part.vi[0] )
07463       rc = false;
07464     tcnt = 0;
07465     for ( fi = part.fi[0]; fi < part.fi[1]; fi++ ) {
07466       fvi = mesh.m_F[fi].vi;
07467       tcnt++;
07468       if ( fvi[2] != fvi[3] )
07469         tcnt++;
07470       for ( j = 0; j < 4; j++ ) {
07471         if ( fvi[j] < part.vi[0] || fvi[j] >= part.vi[1] )
07472           rc = false;
07473       }
07474     }
07475     if ( tcnt != part.triangle_count )
07476       rc = false;
07477     if ( parti ) {
07478       if ( part.fi[0] != p.m_part[parti-1].fi[1] )
07479         rc = false;
07480       if ( part.vi[0] > p.m_part[parti-1].vi[1] )
07481         rc = false;
07482     }
07483   }
07484   if ( partcount ) {
07485     if ( p.m_part[0].fi[0] != 0 || p.m_part[partcount-1].fi[1] != mesh.m_F.Count() )
07486       rc = false;
07487   }
07488   return rc;
07489 }
07490 
07491 static
07492 bool ON_Mesh_CreatePartition_SortFaces(const ON_Mesh& mesh, int* fmap )
07493 {
07494   ON_RTree rt;
07495   if ( !rt.CreateMeshFaceTree(&mesh) )
07496     return false;
07497 
07498   const int mesh_F_count = mesh.m_F.Count();
07499   int fmap_count = 0;
07500 
07501   const ON_RTreeBranch* branch;
07502   ON_RTreeIterator rit(rt);
07503   for ( rit.First(); 0 != (branch = rit.Value()); rit.Next() )
07504   {
07505     if ( fmap_count > mesh_F_count )
07506       break;
07507     fmap[fmap_count++] = (int)(branch->m_id);
07508   }  
07509 
07510   if ( fmap_count != mesh_F_count )
07511   {
07512     ON_ERROR("ON_Mesh::CreatePartition unable to get fmap[]");
07513     return false;
07514   }
07515 
07516   return true;
07517 }
07518 
07519 const ON_MeshPartition* ON_Mesh::CreatePartition( 
07520               int partition_max_vertex_count, // maximum number of vertices in a partition
07521               int partition_max_triangle_count // maximum number of triangles in a partition
07522               )
07523 {
07524   ON_Workspace ws;
07525   bool bNeedFaceSort = true;
07526   if ( m_partition ) 
07527   {
07528     bNeedFaceSort = false;
07529     if (   m_partition->m_partition_max_triangle_count > partition_max_triangle_count
07530         || m_partition->m_partition_max_vertex_count > partition_max_vertex_count )
07531         DestroyPartition();
07532   }
07533   if ( !m_partition ) 
07534   {
07535     // TODO: create one
07536     struct ON_MeshPart p;
07537     int vertex_count = VertexCount();
07538     const int face_count = FaceCount();
07539     const int triangle_count = TriangleCount() + 2*QuadCount();
07540     m_partition = new ON_MeshPartition();
07541     int k = triangle_count/partition_max_triangle_count;
07542     if ( k < vertex_count/partition_max_vertex_count )
07543       k = vertex_count/partition_max_vertex_count;
07544     k++;
07545     m_partition->m_part.Reserve(k);
07546     if ( vertex_count   <= partition_max_vertex_count && 
07547          triangle_count <= partition_max_triangle_count ) 
07548     {
07549       m_partition->m_partition_max_vertex_count = vertex_count;
07550       m_partition->m_partition_max_triangle_count = triangle_count;
07551       memset(&p,0,sizeof(p));
07552       p.vi[0] = 0;
07553       p.vi[1] = vertex_count;
07554       p.fi[0] = 0;
07555       p.fi[1] = face_count;
07556       p.vertex_count = vertex_count;
07557       p.triangle_count = triangle_count;
07558       m_partition->m_part.Append(p);
07559     }
07560     else 
07561     {
07562       int fi;
07563       int* fvi;
07564       DestroyTopology();
07565 
07566       // sort faces
07567       int* fmap = (int*)ws.GetMemory( face_count*sizeof(fmap[0]) );
07568       if ( !ON_Mesh_CreatePartition_SortFaces(*this,fmap) )
07569       {
07570         for ( fi = 0; fi < face_count; fi++ )
07571           fmap[fi] = fi;
07572       }
07573 
07574       //ON_SimpleArray<struct tagFPT> fpt(face_count);
07575       //fpt.SetCount(face_count);
07576       //if ( HasTextureCoordinates() )
07577       //{
07578       //  ON_2fPoint fcenter;
07579       //  ON_BoundingBox bbox = ON_PointListBoundingBox(2,0,m_T.Count(),2,&m_T[0].x);
07580       //  const ON_Interval txdom(bbox.m_min.x,bbox.m_max.x);
07581       //  const ON_Interval tydom(bbox.m_min.y,bbox.m_max.y);
07582       //  for ( fi = 0; fi < face_count; fi++ ) {
07583       //    fvi = m_F[fi].vi;
07584       //    if ( fvi[2] == fvi[3] ) {
07585       //      fcenter = 0.333333333333333333f*(m_T[fvi[0]] + m_T[fvi[1]] + m_T[fvi[2]]);
07586       //    }
07587       //    else {
07588       //      fcenter = 0.25f*(m_T[fvi[0]] + m_T[fvi[1]] + m_T[fvi[2]] + m_T[fvi[3]]);
07589       //    }
07590       //    fpt[fi].x = (int)floor(txdom.NormalizedParameterAt(fcenter.x)*100);
07591       //    fpt[fi].y = (int)floor(tydom.NormalizedParameterAt(fcenter.y)*100);
07592       //    fpt[fi].z = 0;
07593       //  }
07594       //}
07595       //else
07596       //{
07597       //  ON_3dPoint fcenter;
07598       //  ON_BoundingBox bbox = BoundingBox();
07599       //  const ON_Interval vxdom(bbox.m_min.x,bbox.m_max.x);
07600       //  const ON_Interval vydom(bbox.m_min.y,bbox.m_max.y);
07601       //  const ON_Interval vzdom(bbox.m_min.z,bbox.m_max.z);
07602       //  for ( fi = 0; fi < face_count; fi++ ) {
07603       //    fvi = m_F[fi].vi;
07604       //    if ( fvi[2] == fvi[3] ) {
07605       //      fcenter = 0.333333333333333333f*(m_V[fvi[0]] + m_V[fvi[1]] + m_V[fvi[2]]);
07606       //    }
07607       //    else {
07608       //      fcenter = 0.25f*(m_V[fvi[0]] + m_V[fvi[1]] + m_V[fvi[2]] + m_V[fvi[3]]);
07609       //    }
07610       //    fpt[fi].x = (int)floor(vxdom.NormalizedParameterAt(fcenter.x)*100);
07611       //    fpt[fi].y = (int)floor(vydom.NormalizedParameterAt(fcenter.y)*100);
07612       //    fpt[fi].z = (int)floor(vzdom.NormalizedParameterAt(fcenter.z)*100);
07613       //  }
07614       //}
07615       //fpt.Sort( ON::quick_sort, fmap, compare_fpt ); 
07616       m_F.Permute( fmap );
07617       if ( m_FN.Count() == face_count )
07618         m_FN.Permute( fmap );
07619 
07620       // sort vertices
07621       ON_SimpleArray<int>pmark(2*vertex_count);
07622       pmark.SetCount(vertex_count);
07623       pmark.Zero();
07624       int fi0, fi1, partition_mark, partition_vertex_count, partition_triangle_count;
07625       fi1 = 0;
07626       fi0 = 0;
07627       for ( partition_mark = 3, fi0 = 0; fi0 < face_count; fi0 = fi1, partition_mark += 2 ) 
07628       {
07629         partition_vertex_count = 0;
07630         partition_triangle_count = 0;
07631         for ( fi1 = fi0; 
07632               fi1 < face_count
07633               && partition_triangle_count+2 <= partition_max_triangle_count
07634               && partition_vertex_count+4 <= partition_max_vertex_count;
07635               fi1++ ) 
07636         {
07637           fvi = m_F[fi1].vi;
07638           partition_triangle_count++;
07639           if ( AddToPartition( this, pmark, fvi[0], partition_mark, fi0 ) )
07640             partition_vertex_count++;
07641           if ( AddToPartition( this, pmark, fvi[1], partition_mark, fi0 ) )
07642             partition_vertex_count++;
07643           if ( AddToPartition( this, pmark, fvi[2], partition_mark, fi0 ) )
07644             partition_vertex_count++;
07645           if ( fvi[2] != fvi[3] ) {
07646             partition_triangle_count++; // quads = 2 triangles
07647             if ( AddToPartition( this, pmark, fvi[3], partition_mark, fi0 ) )
07648               partition_vertex_count++;
07649           }
07650         }
07651         if ( fi0 < fi1 ) {
07652           struct ON_MeshPart p;
07653           memset(&p,0,sizeof(p));
07654           p.fi[0] = fi0;
07655           p.fi[1] = fi1;
07656           p.vertex_count = partition_vertex_count;
07657           p.triangle_count = partition_triangle_count;
07658           m_partition->m_part.Append(p);
07659         }
07660         if ( partition_triangle_count > m_partition->m_partition_max_triangle_count )
07661           m_partition->m_partition_max_triangle_count = partition_triangle_count;
07662         if ( partition_vertex_count > m_partition->m_partition_max_vertex_count )
07663           m_partition->m_partition_max_vertex_count = partition_vertex_count;
07664       }
07665 
07666       // the calls to AddToPartition() may have increased vertex count
07667       vertex_count = m_V.Count();
07668 
07669       // sort vertices
07670       int* vmap = (int*)ws.GetMemory( vertex_count*sizeof(vmap[0]) );
07671       pmark.Sort( ON::quick_sort, vmap, compare_pmark );
07672       m_V.Permute( vmap );
07673       if ( m_N.Count() == vertex_count )
07674         m_N.Permute( vmap );
07675       if ( m_T.Count() == vertex_count )
07676         m_T.Permute( vmap );
07677       if ( m_K.Count() == vertex_count )
07678         m_K.Permute( vmap );
07679       if ( m_C.Count() == vertex_count )
07680         m_C.Permute( vmap );
07681       pmark.Permute( vmap );
07682       // pamv[] = inverse of mapv permutation
07683       int* pamv = (int*)ws.GetMemory( vertex_count*sizeof(pamv[0]) );
07684       ON_Sort( ON::quick_sort, pamv, vmap, vertex_count, sizeof(vmap[0]), compare_vmap );
07685       for ( fi = 0; fi < face_count; fi++ ) {
07686         fvi = m_F[fi].vi;
07687         fvi[0] = pamv[fvi[0]];
07688         fvi[1] = pamv[fvi[1]];
07689         fvi[2] = pamv[fvi[2]];
07690         fvi[3] = pamv[fvi[3]];
07691       }
07692 
07693       // fill in m_part.vi[]
07694       int m, pi, partition_count = m_partition->m_part.Count();
07695       int vi0, vi1, vi2, vi3;
07696       for (vi2 = 0; vi2 < vertex_count && pmark[vi2]<2; vi2++)
07697       {/*empty for body*/}
07698       vi3=vi2;
07699       for ( pi = 0; pi < partition_count; pi++ ) {
07700         vi0 = vi2;
07701         vi1 = vi3;
07702         m = 2*pi + 4;
07703         for ( vi2 = vi3; vi2 < vertex_count && pmark[vi2] <  m; vi2++) 
07704         {/*empty for body*/}
07705         for ( vi3 = vi2; vi3 < vertex_count && pmark[vi3] <= m; vi3++) 
07706         {/*empty for body*/}
07707         m_partition->m_part[pi].vi[0] = vi0;
07708         m_partition->m_part[pi].vi[1] = vi3;
07709       }
07710     }
07711     // debugging - test partition
07712     if ( m_partition && !ON_MeshPartition_IsValid( *m_partition, *this ) ) {
07713       delete m_partition;
07714       m_partition = 0;
07715     }
07716   }
07717 
07718   return m_partition;
07719 }
07720 
07721 const ON_MeshPartition* ON_Mesh::Partition() const
07722 {
07723   return m_partition;
07724 }
07725 
07726 void ON_Mesh::DestroyPartition()
07727 {
07728   if ( m_partition ) {
07729     delete m_partition;
07730     m_partition = 0;
07731   }
07732 }
07733 
07734 ON_MeshPartition::ON_MeshPartition()
07735 {
07736   m_partition_max_vertex_count = 0;
07737   m_partition_max_triangle_count = 0;
07738   m_part = 0;
07739 }
07740 
07741 ON_MeshPartition::~ON_MeshPartition()
07742 {
07743   m_part.Destroy();
07744 }
07745 
07746 
07747 ON_Mesh* ON_Mesh::MeshPart( 
07748   const ON_MeshPart& mesh_part,
07749   ON_Mesh* mesh 
07750   ) const
07751 {
07752   if ( this == mesh )
07753   {
07754     ON_ERROR("ON_Mesh::MeshPart this == mesh");
07755     return 0;
07756   }
07757 
07758   if ( mesh )
07759     mesh->Destroy();
07760 
07761   if (    mesh_part.fi[0] < 0 
07762        || mesh_part.fi[1] > m_F.Count()
07763        || mesh_part.fi[0] > mesh_part.fi[1]
07764        )
07765   {
07766     ON_ERROR("ON_Mesh::MeshPart mesh_part.fi[] is not valid");
07767     return 0;
07768   }
07769 
07770   if (    mesh_part.vi[0] < 0
07771        || mesh_part.vi[1] > m_V.Count()
07772        || mesh_part.vi[0] >= mesh_part.vi[1]
07773        )
07774   {
07775     ON_ERROR("ON_Mesh::MeshPart mesh_part.vi[] is not valid");
07776     return 0;
07777   }
07778 
07779   const int submesh_V_count = mesh_part.vi[1] - mesh_part.vi[0];
07780   const int submesh_F_count = mesh_part.fi[1] - mesh_part.fi[0];
07781 
07782   const bool bHasVertexNormals = HasVertexNormals();
07783   const bool bHasTextureCoordinates = HasTextureCoordinates();
07784   const bool bHasVertexColors = HasVertexColors();
07785   const bool bHasFaceNormals = HasFaceNormals();
07786   const bool bHasSurfaceParameters = HasSurfaceParameters();
07787   const bool bHasPrincipalCurvatures = HasPrincipalCurvatures();
07788   const bool bHasHiddenVertices = HiddenVertexCount() > 0;
07789 
07790   ON_Mesh* submesh = (0 != mesh)
07791                    ? mesh
07792                    : new ON_Mesh(mesh_part.triangle_count,
07793                                  mesh_part.vertex_count,
07794                                  bHasVertexNormals,
07795                                  bHasTextureCoordinates
07796                                  );
07797 
07798   if ( bHasVertexColors )
07799     submesh->m_C.Reserve(submesh_V_count);
07800   if ( bHasSurfaceParameters )
07801     submesh->m_S.Reserve(submesh_V_count);
07802   if ( bHasPrincipalCurvatures )
07803     submesh->m_K.Reserve(submesh_V_count);
07804   if ( bHasHiddenVertices )
07805     submesh->m_H.Reserve(submesh_V_count);
07806   if ( bHasFaceNormals )
07807     submesh->m_FN.Reserve(submesh_F_count);
07808 
07809   // put vertex information into submesh
07810   const int vi0 = mesh_part.vi[0];
07811   const int vi1 = mesh_part.vi[1];
07812   for ( int vi = vi0; vi < vi1; vi++ )
07813   {
07814     submesh->m_V.Append(m_V[vi]);
07815     if ( bHasVertexNormals )
07816       submesh->m_N.Append(m_N[vi]);
07817     if ( bHasTextureCoordinates )
07818       submesh->m_T.Append(m_T[vi]);
07819     if ( bHasVertexColors )
07820       submesh->m_C.Append(m_C[vi]);
07821     if ( bHasSurfaceParameters )
07822       submesh->m_S.Append(m_S[vi]);
07823     if ( bHasPrincipalCurvatures )
07824       submesh->m_K.Append(m_K[vi]);
07825     if ( bHasHiddenVertices )
07826     {
07827       bool bHidden = m_H[vi];
07828       submesh->m_H.Append(bHidden);
07829       if ( bHidden )
07830         submesh->m_hidden_count++;
07831     }
07832   }
07833   if ( submesh->m_hidden_count <= 0 )
07834   {
07835     submesh->m_H.Destroy();
07836     submesh->m_hidden_count = 0;
07837   }
07838 
07839   // put face information into submesh
07840   int bad_face_count = 0;
07841   const int fi0 = mesh_part.fi[0];
07842   const int fi1 = mesh_part.fi[1];
07843   for ( int fi = fi0; fi < fi1; fi++ )
07844   {
07845     ON_MeshFace f = m_F[fi];
07846     f.vi[0] -= vi0;
07847     f.vi[1] -= vi0;
07848     f.vi[2] -= vi0;
07849     f.vi[3] -= vi0;
07850     if (    f.vi[0] >= submesh_V_count || f.vi[0] < 0 
07851          || f.vi[1] >= submesh_V_count || f.vi[1] < 0 
07852          || f.vi[2] >= submesh_V_count || f.vi[2] < 0 
07853          || f.vi[3] >= submesh_V_count || f.vi[3] < 0 
07854          )
07855     {
07856       bad_face_count++;
07857       ON_ERROR("ON_Mesh::MeshPart Invalid face in partition");
07858       continue;
07859     }
07860     submesh->m_F.Append(f);
07861     if ( bHasFaceNormals )
07862       submesh->m_FN.Append(m_FN[fi]);
07863   }
07864 
07865   if ( submesh->m_F.Count() < 1 && bad_face_count > 0 )
07866   {
07867     if ( submesh != mesh )
07868       delete submesh;
07869     else
07870       mesh->Destroy();
07871 
07872     submesh = 0;
07873   }
07874 
07875   return submesh;
07876 }
07877 
07878 ON_Mesh* ON_Mesh::DuplicateFace( int face_index, ON_Mesh* mesh ) const
07879 {
07880   if ( mesh == this )
07881     return 0;
07882   if ( 0 != mesh )
07883     mesh->Destroy();
07884   if ( face_index < 0 || face_index >= m_F.Count() )
07885     return 0;
07886   const int vcnt = m_V.Count();
07887   if ( vcnt < 3 )
07888     return 0;
07889 
07890   const ON_3dPoint* dV = ( HasDoublePrecisionVertices() && DoublePrecisionVerticesAreValid() )
07891                         ? DoublePrecisionVertices().Array()
07892                         : 0;
07893   const ON_3fPoint* fV = (0 == dV) ? m_V.Array() : 0;
07894   bool bHasFaceNormals = HasFaceNormals();
07895   bool bHasVertexNormals = HasVertexNormals();
07896   bool bHasVertexColors = HasVertexColors();
07897   bool bHasTextureCoordinates = HasTextureCoordinates();
07898   bool bHasSurfaceParameters = HasSurfaceParameters();
07899   bool bHasPrincipalCurvatures = HasPrincipalCurvatures();
07900 
07901   ON_MeshFace f = m_F[face_index];
07902   if ( dV )
07903   {
07904     if ( !f.IsValid(vcnt,dV) )
07905     {
07906       // invalid vertex indices - see if it can be fixed
07907       if ( !f.Repair(vcnt,dV) )
07908         return 0;
07909     }
07910   }
07911   else
07912   {
07913     if ( !f.IsValid(vcnt,fV) )
07914     {
07915       // invalid vertex indices - see if it can be fixed
07916       if ( !f.Repair(vcnt,fV) )
07917         return 0;
07918     }
07919   }
07920   const int newvcnt = f.IsTriangle() ? 3 : 4;
07921   if ( 0 == mesh )
07922     mesh = new ON_Mesh();
07923   ON_3dPointArray* newdV = 0;
07924   if ( dV )
07925   {
07926     newdV = &mesh->DoublePrecisionVertices();
07927     newdV->Reserve(newvcnt);
07928   }
07929   mesh->m_V.Reserve(newvcnt);
07930   mesh->m_F.Reserve(1);
07931   ON_MeshFace& newface = mesh->m_F.AppendNew();
07932   newface.vi[0] = 0;
07933   newface.vi[1] = 1;
07934   newface.vi[2] = 2;
07935   newface.vi[3] = (4 == newvcnt) ? 3 : newface.vi[2];
07936 
07937   if ( bHasFaceNormals )
07938   {
07939     mesh->m_FN.Reserve(1);
07940     mesh->m_FN.Append(m_FN[face_index]);
07941   }
07942 
07943   if ( bHasVertexNormals )
07944     mesh->m_N.Reserve(newvcnt);
07945   if ( bHasTextureCoordinates )
07946     mesh->m_T.Reserve(newvcnt);
07947   if ( bHasVertexColors )
07948     mesh->m_C.Reserve(newvcnt);
07949   if ( bHasSurfaceParameters )
07950     mesh->m_S.Reserve(newvcnt);
07951   if ( bHasPrincipalCurvatures )
07952     mesh->m_K.Reserve(newvcnt);
07953   for ( int vi = 0; vi < newvcnt; vi++ )
07954   {
07955     if ( dV )
07956       newdV->Append(dV[f.vi[vi]]);
07957     else
07958       mesh->m_V.Append(fV[f.vi[vi]]);
07959     if ( bHasVertexNormals )
07960       mesh->m_N.Append(m_N[f.vi[vi]]);
07961     if ( bHasTextureCoordinates )
07962       mesh->m_T.Append(m_T[f.vi[vi]]);
07963     if ( bHasVertexColors )
07964       mesh->m_C.Append(m_C[f.vi[vi]]);
07965     if ( bHasSurfaceParameters )
07966       mesh->m_S.Append(m_S[f.vi[vi]]);
07967     if ( bHasPrincipalCurvatures )
07968       mesh->m_K.Append(m_K[f.vi[vi]]);
07969   }
07970   if ( dV )
07971   {
07972     mesh->SetDoublePrecisionVerticesAsValid();
07973     mesh->UpdateSinglePrecisionVertices();
07974   }
07975 
07976   return mesh;
07977 }
07978 
07979 
07980 ON_OBJECT_IMPLEMENT(ON_MeshVertexRef,ON_Geometry,"C547B4BD-BDCD-49b6-A983-0C4A7F02E31A");
07981 
07982 ON_OBJECT_IMPLEMENT(ON_MeshEdgeRef,ON_Geometry,"ED727872-463A-4424-851F-9EC02CB0F155");
07983 
07984 ON_OBJECT_IMPLEMENT(ON_MeshFaceRef,ON_Geometry,"4F529AA5-EF8D-4c25-BCBB-162D510AA280");
07985 
07986 ON_MeshVertexRef::ON_MeshVertexRef()
07987 {
07988   m_mesh = 0;
07989   m_mesh_vi = -1;
07990   m_top_vi = -1;
07991 }
07992 
07993 ON_MeshVertexRef::~ON_MeshVertexRef()
07994 {
07995   m_mesh = 0;
07996   m_mesh_vi = -1;
07997   m_top_vi = -1;
07998 }
07999 
08000 ON_MeshVertexRef& ON_MeshVertexRef::operator=(const ON_MeshVertexRef& src)
08001 {
08002   if ( this != &src )
08003   {
08004     ON_Geometry::operator=(src);
08005     m_mesh = src.m_mesh;
08006     m_mesh_vi = src.m_mesh_vi;
08007     m_top_vi = src.m_top_vi;
08008   }
08009   return *this;
08010 }
08011 
08012 
08013 ON_BOOL32 ON_MeshVertexRef::IsValid( ON_TextLog* text_log ) const
08014 {
08015   if ( 0 == m_mesh )
08016   {
08017     if ( 0 != text_log )
08018     {
08019       text_log->Print("m_mesh = NULL\n");
08020     }
08021     return false;
08022   }
08023 
08024   if ( -1 != m_mesh_vi )
08025   {
08026     if ( m_mesh_vi < 0 || m_mesh_vi >= m_mesh->m_V.Count() )
08027     {
08028       if ( 0 != text_log )
08029       {
08030         text_log->Print("m_mesh_vi = %d (should have 0 <= m_mesh_vi < %d)\n",m_mesh_vi,m_mesh->m_V.Count());
08031       }
08032       return false;
08033     }
08034   }
08035   else if ( -1 == m_top_vi )
08036   {
08037     if ( 0 != text_log )
08038     {
08039       text_log->Print("m_mesh_vi = -1 and m_top_vi = -1\n");
08040     }
08041     return false;
08042   }
08043 
08044   if ( -1 != m_top_vi )
08045   {
08046     const ON_MeshTopology* top = MeshTopology();
08047     if ( 0 == top )
08048     {
08049       if ( 0 != text_log )
08050       {
08051         text_log->Print("m_top_vi = %d and MeshTopology()=NULL\n",m_top_vi);
08052       }
08053       return false;
08054     }
08055     if ( m_top_vi < 0 || m_top_vi >= top->m_tope.Count() )
08056     {
08057       if ( 0 != text_log )
08058       {
08059         text_log->Print("m_top_vi = %d (should have 0 <= m_top_vi < %d)\n",m_top_vi,top->m_topv.Count());
08060       }
08061       return false;
08062     }
08063 
08064     if ( -1 != m_mesh_vi )
08065     {
08066       const ON_MeshTopologyVertex& topv = top->m_topv[m_top_vi];
08067       int i;
08068       for ( i = 0; i < topv.m_v_count; i++ )
08069       {
08070         if ( topv.m_vi[i] == m_mesh_vi )
08071           break;
08072       }
08073       if ( i >= topv.m_v_count )
08074       {
08075         if ( 0 != text_log )
08076         {
08077           text_log->Print("m_mesh_vi=%d is not in m_top->m_topv[m_top_vi=%d].m_vi[] array.\n",
08078                           m_mesh_vi,m_top_vi);
08079           
08080         }
08081         return false;
08082       }
08083     }
08084   }
08085 
08086   return true;
08087 }
08088 
08089 void ON_MeshVertexRef::Dump( ON_TextLog& text_log ) const
08090 {
08091   text_log.Print("m_mesh=%08x m_mesh_vi=%d m_top_vi=%d\n",
08092                   m_mesh,m_mesh_vi,m_top_vi);
08093   ON_3dPoint v = Point();
08094   if ( v.IsValid() )
08095   {
08096     text_log.PushIndent();
08097     text_log.Print("Location: ");
08098     text_log.Print(v);
08099     text_log.Print("\n");
08100     text_log.PopIndent();
08101   }
08102 
08103 }
08104 unsigned int ON_MeshVertexRef::SizeOf() const
08105 {
08106   unsigned int sz = sizeof(*this) - sizeof(ON_Geometry);
08107   sz += ON_Geometry::SizeOf();
08108   return sz;
08109 }
08110 
08111 ON::object_type ON_MeshVertexRef::ObjectType() const
08112 {
08113   return ON::meshvertex_object;
08114 }
08115 
08116 // overrides of virtual ON_Geometry functions
08117 int ON_MeshVertexRef::Dimension() const
08118 {
08119   return 3;
08120 }
08121 
08122 ON_BOOL32 ON_MeshVertexRef::GetBBox(
08123        double* boxmin,
08124        double* boxmax,
08125        ON_BOOL32 bGrowBox
08126        ) const
08127 {
08128   bool rc = false;
08129   ON_3dPoint v = Point();
08130   if ( v.IsValid() )
08131   {
08132     rc = ON_GetPointListBoundingBox( 3, 0, 1, 3, &v.x, boxmin, boxmax, bGrowBox?true:false );
08133   }
08134   return rc;
08135 }
08136 
08137 ON_BOOL32 ON_MeshVertexRef::Transform( 
08138        const ON_Xform& xform
08139        )
08140 {
08141   return false;
08142 }
08143 
08144 const ON_MeshTopology* ON_MeshVertexRef::MeshTopology() const
08145 {
08146   return (0 != m_mesh) ? &m_mesh->m_top : 0;
08147 }
08148 
08149 ON_3dPoint ON_MeshVertexRef::Point() const
08150 {
08151   ON_3dPoint v = ON_UNSET_POINT;
08152   if ( 0 != m_mesh )
08153   {
08154     int vi = m_mesh_vi;
08155     if ( -1 == vi && m_top_vi >= 0 && m_top_vi < m_mesh->m_top.m_topv.Count() )
08156     {
08157       const ON_MeshTopologyVertex& topv = m_mesh->m_top.m_topv[m_top_vi];
08158       if ( topv.m_v_count > 0 )
08159         vi = topv.m_vi[0];
08160     }
08161     if ( vi >= 0 && vi < m_mesh->m_V.Count() )
08162       v = m_mesh->m_V[vi];
08163   }
08164   return v;
08165 }
08166 
08167 const ON_MeshTopologyVertex* ON_MeshVertexRef::MeshTopologyVertex() const
08168 {
08169   const ON_MeshTopologyVertex* topv = 0;
08170   if ( 0 != m_mesh && m_top_vi >= 0 && m_top_vi < m_mesh->m_top.m_topv.Count() )
08171   {
08172     topv = &m_mesh->m_top.m_topv[m_top_vi];
08173   }
08174   return topv;
08175 }
08176 
08177 ON_MeshEdgeRef::ON_MeshEdgeRef()
08178 {
08179   m_mesh = 0;
08180   m_top_ei = -1;
08181 }
08182 
08183 ON_MeshEdgeRef::~ON_MeshEdgeRef()
08184 {
08185   m_mesh = 0;
08186   m_top_ei = -1;
08187 }
08188 
08189 ON_MeshEdgeRef& ON_MeshEdgeRef::operator=(const ON_MeshEdgeRef& src)
08190 {
08191   if ( this != &src )
08192   {
08193     ON_Geometry::operator=(src);
08194     m_mesh = src.m_mesh;
08195     m_top_ei = src.m_top_ei;
08196   }
08197   return *this;
08198 }
08199 
08200 
08201 ON_BOOL32 ON_MeshEdgeRef::IsValid( ON_TextLog* text_log ) const
08202 {
08203   if ( 0 == m_mesh)
08204   {
08205     if ( 0 != text_log )
08206     {
08207       text_log->Print("m_mesh = NULL\n");
08208     }
08209     return false;
08210   }
08211 
08212   if ( m_top_ei < 0 || m_top_ei >= m_mesh->m_top.m_tope.Count() )
08213   {
08214     if ( 0 != text_log )
08215     {
08216       text_log->Print("m_top_ei = %d (should have 0 <= m_top_ei < %d)\n",m_top_ei,m_mesh->m_top.m_tope.Count());
08217     }
08218     return false;
08219   }
08220 
08221   return true;
08222 }
08223 
08224 void ON_MeshEdgeRef::Dump( ON_TextLog& text_log ) const
08225 {
08226   text_log.Print("m_mesh=%08x, m_top_ei=%d\n",m_mesh,m_top_ei);
08227   ON_Line line = Line();
08228   if ( line.from.IsValid() )
08229   {
08230     text_log.PushIndent();
08231     text_log.Print("Location: ");
08232     text_log.Print(line.from);
08233     text_log.Print(" to ");
08234     text_log.Print(line.to);
08235     text_log.Print("\n");
08236     text_log.PopIndent();
08237   }
08238 
08239 }
08240 unsigned int ON_MeshEdgeRef::SizeOf() const
08241 {
08242   unsigned int sz = sizeof(*this) - sizeof(ON_Geometry);
08243   sz += ON_Geometry::SizeOf();
08244   return sz;
08245 }
08246 
08247 ON::object_type ON_MeshEdgeRef::ObjectType() const
08248 {
08249   return ON::meshedge_object;
08250 }
08251 
08252 // overrides of virtual ON_Geometry functions
08253 int ON_MeshEdgeRef::Dimension() const
08254 {
08255   return 3;
08256 }
08257 
08258 ON_BOOL32 ON_MeshEdgeRef::GetBBox(
08259        double* boxmin,
08260        double* boxmax,
08261        ON_BOOL32 bGrowBox
08262        ) const
08263 {
08264   bool rc = false;
08265   ON_Line line = Line();
08266   if ( line.from.IsValid() && line.to.IsValid() )
08267   {
08268     rc = ON_GetPointListBoundingBox( 3, 0, 2, 3, &line.from.x, boxmin, boxmax, bGrowBox?true:false );
08269   }
08270   return rc;
08271 }
08272 
08273 ON_BOOL32 ON_MeshEdgeRef::Transform( 
08274        const ON_Xform& xform
08275        )
08276 {
08277   return false;
08278 }
08279 
08280 const ON_MeshTopology* ON_MeshEdgeRef::MeshTopology() const
08281 {
08282   return (0 != m_mesh) ? &m_mesh->m_top : 0;
08283 }
08284 
08285 ON_Line ON_MeshEdgeRef::Line() const
08286 {
08287   ON_Line line(ON_UNSET_POINT,ON_UNSET_POINT);
08288   const ON_MeshTopologyEdge* tope = MeshTopologyEdge();
08289   if ( 0 != tope )
08290   {
08291     ON_MeshVertexRef</