1 /* $NoKeywords: $ */
2 /*
3 //
4 // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
5 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
6 // McNeel & Associates.
7 //
8 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
9 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
10 // MERCHANTABILITY ARE HEREBY DISCLAIMED.
11 //
12 // For complete openNURBS copyright information see <http://www.opennurbs.org>.
13 //
14 ////////////////////////////////////////////////////////////////
15 */
16 
17 #include "opennurbs.h"
18 
19 
20 // NEVER COPY OR MOVE THE NEXT 2 LINES
21 #define ON_BOZO_VACCINE_17F24E7521BE4a7b9F3D7F85225247E3
22 #define ON_BOZO_VACCINE_B5628CA982C44CAE9883487B3E4AB28B
23 // NEVER COPY OR MOVE THE PREVIOUS 2 LINES
24 
25 
26 
27 
28 ///////////////////////////////////////////////////////
29 //
30 // Double precision vertices user data
31 //
32 
33 class /* DO NOT copy, move, or export this class */ ON_MeshDoubleVertices : public ON_UserData
34 {
35   ON_OBJECT_DECLARE(ON_MeshDoubleVertices);
36 
37 public:
38   ON_MeshDoubleVertices();
39   ~ON_MeshDoubleVertices();
40 
41   // default copy constructor and operator= work fine.
42 
43   /*
44     If the mesh has ON_MeshDoubleVertices user data, then return
45     a pointer to it.
46   */
47   static ON_MeshDoubleVertices* Get(const ON_Mesh* mesh);
48 
49   /*
50     Attach new ON_MeshDoubleVertices user data to the mesh.
51     This will fail and return NULL if the mesh already has
52     ON_MeshDoubleVertices user data.
53   */
54   static ON_MeshDoubleVertices* Attach(const ON_Mesh* mesh);
55 
56   // virtual ON_Object overrides
57   ON_BOOL32 IsValid( ON_TextLog* = NULL ) const;
58   void Dump( ON_TextLog& ) const;
59   unsigned int SizeOf() const;
60   ON__UINT32 DataCRC(ON__UINT32) const;
61   ON_BOOL32 Write(ON_BinaryArchive&) const;
62   ON_BOOL32 Read(ON_BinaryArchive&);
63 
64   // virtual ON_UserData overrides
65   ON_BOOL32 GetDescription( ON_wString& );
66   ON_BOOL32 Archive() const;
67   ON_BOOL32 Transform( const ON_Xform& );
68 
69 #if !defined(ON_BOZO_VACCINE_17F24E7521BE4a7b9F3D7F85225247E3)
70 #error DO NOT copy, move or export the definition of ON_MeshDoubleVertices
71 #endif
72 #undef ON_BOZO_VACCINE_17F24E7521BE4a7b9F3D7F85225247E3
73 
74   ON__UINT32 DoubleCRC() const;
75   static ON__UINT32 FloatCRC( const ON_3fPointArray& );
76 
77   // If m_dV.Count() != m_dcount or
78   // m_dCRC != ON_CRC32(0,m_dV.Count()*sizeof(ON_3dPoint),m_dV.Array()),
79   // then somebody has changed m_dV and not called
80   // SetDoublePrecisionVerticesAsValid() to mark the
81   // values as valid.
82   //
83   // If ON_Mesh.M_V.Count() != m_fcount or
84   // m_fCRC != ON_CRC32(0,m_V.Count()*sizeof(ON_3fPoint),m_V.Array()),
85   // then somebody has changed ON_Mesh.m_V and not called
86   // SetSinglePrecisionVerticesAsValid() to mark the
87   // values as valid.
88   //
89   // Whenever there is a question about which values are valid,
90   // it is assumed the m_V array is valid and the double precision
91   // informtion should be destroyed.
92   int m_fcount;  // single precision vertex count
93   int m_dcount;  // double precision vertex count
94   ON__UINT32 m_fCRC; // crc of float vertex array
95   ON__UINT32 m_dCRC; // crc of double vertex array
96 
97   ON_3dPointArray m_dV; // double precision mesh vertices
98 };
99 
Mesh_dV(const ON_Mesh & mesh)100 static const ON_3dPoint* Mesh_dV(const ON_Mesh& mesh)
101 {
102   if ( mesh.HasDoublePrecisionVertices() && mesh.DoublePrecisionVerticesAreValid() )
103   {
104     const ON_3dPointArray& a = mesh.DoublePrecisionVertices();
105     if ( a.Count() == mesh.m_V.Count() )
106       return a.Array();
107   }
108   return 0;
109 }
110 
ON_MeshCurveParameters()111 ON_MeshCurveParameters::ON_MeshCurveParameters()
112 {
113   memset(this,0,sizeof(*this));
114 }
115 
116 ON_OBJECT_IMPLEMENT(ON_Mesh,ON_Geometry,"4ED7D4E4-E947-11d3-BFE5-0010830122F0");
117 
118 /*
119 ON_MeshEdge& ON_MeshEdge::operator=( const ON_MeshEdge& src )
120 {
121   int fc = src.m_fcount+(src.m_fcount%1);
122   if ( fc <= 2 ) {
123     if ( m_fi && m_fi != m_private_fi ) onfree((void*)m_fi);
124     m_fi = m_private_fi;
125     fc = 2;
126   }
127   else {
128     if ( (m_fcount+(m_fcount%1)) != fc ) {
129       if ( m_fi == m_private_fi )
130         m_fi = 0;
131       m_fi = (int*)onrealloc(m_fi,fc*sizeof(*m_fi));
132     }
133   }
134   memcpy(m_fi,src.m_fi,fc*sizeof(*m_fi));
135   m_fcount=src.m_fcount;
136   return *this;
137 }
138 
139 void ON_MeshEdge::AppendFaceIndex(int face_index)
140 {
141   if ( m_fcount>0 && !(m_fcount%1) ) {
142     if ( m_fi == m_private_fi ) {
143       m_fi = (int*)onmalloc((m_fcount+2)*sizeof(*m_fi));
144       m_fi[0] = m_private_fi[0];
145       m_fi[1] = m_private_fi[1];
146     }
147     else {
148       m_fi = (int*)onrealloc(m_fi,(m_fcount+2)*sizeof(*m_fi));
149     }
150   }
151   m_fi[m_fcount++] = face_index;
152 }
153 */
154 
155 bool
IsValid(int mesh_vertex_count) const156 ON_MeshFace::IsValid(int mesh_vertex_count) const
157 {
158   return (    vi[0] >= 0 && vi[0] < mesh_vertex_count
159            && vi[1] >= 0 && vi[1] < mesh_vertex_count
160            && vi[2] >= 0 && vi[2] < mesh_vertex_count
161            && vi[3] >= 0 && vi[3] < mesh_vertex_count
162            && vi[0] != vi[1] && vi[1] != vi[2] && vi[2] != vi[0]
163            && (vi[2]==vi[3]||(vi[0] != vi[3] && vi[1] != vi[3])) );
164 }
165 
166 
167 bool
IsValid(int mesh_vertex_count,const ON_3fPoint * V) const168 ON_MeshFace::IsValid(int mesh_vertex_count, const ON_3fPoint* V ) const
169 {
170   if ( !IsValid(mesh_vertex_count) )
171     return false;
172   if ( !(V[vi[0]] != V[vi[1]]) )
173     return false;
174   if ( !(V[vi[0]] != V[vi[2]]) )
175     return false;
176   if ( !(V[vi[1]] != V[vi[2]]) )
177     return false;
178   if ( vi[2] != vi[3] )
179   {
180   if ( !(V[vi[0]] != V[vi[3]]) )
181     return false;
182   if ( !(V[vi[1]] != V[vi[3]]) )
183     return false;
184   if ( !(V[vi[2]] != V[vi[3]]) )
185     return false;
186   }
187   return true;
188 }
189 
190 
191 bool
IsValid(int mesh_vertex_count,const ON_3dPoint * V) const192 ON_MeshFace::IsValid(int mesh_vertex_count, const ON_3dPoint* V ) const
193 {
194   if ( !IsValid(mesh_vertex_count) )
195     return false;
196   if ( !(V[vi[0]] != V[vi[1]]) )
197     return false;
198   if ( !(V[vi[0]] != V[vi[2]]) )
199     return false;
200   if ( !(V[vi[1]] != V[vi[2]]) )
201     return false;
202   if ( vi[2] != vi[3] )
203   {
204   if ( !(V[vi[0]] != V[vi[3]]) )
205     return false;
206   if ( !(V[vi[1]] != V[vi[3]]) )
207     return false;
208   if ( !(V[vi[2]] != V[vi[3]]) )
209     return false;
210   }
211   return true;
212 }
213 
214 
Repair(int mesh_vertex_count)215 bool ON_MeshFace::Repair(
216   int mesh_vertex_count
217   )
218 {
219   ON_MeshFace f;
220   int fvi_count = 0;
221   f.vi[0] = f.vi[1] = f.vi[2] = f.vi[3] = -1;
222 
223   if ( vi[0] >= 0 && vi[0] < mesh_vertex_count )
224     f.vi[fvi_count++] = vi[0];
225 
226   if ( vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] )
227     f.vi[fvi_count++] = vi[1];
228 
229   if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] )
230     f.vi[fvi_count++] = vi[2];
231 
232   if ( vi[3] >= 0 && vi[3] < mesh_vertex_count && f.vi[0] != vi[3] && f.vi[1] != vi[3] && f.vi[2] != vi[3] )
233     f.vi[fvi_count++] = vi[3];
234 
235   if ( fvi_count < 3 )
236     return false;
237 
238   if ( 3 == fvi_count )
239     f.vi[3] = f.vi[2];
240 
241   if ( !f.IsValid(mesh_vertex_count) )
242     return false;
243 
244   vi[0] = f.vi[0];
245   vi[1] = f.vi[1];
246   vi[2] = f.vi[2];
247   vi[3] = f.vi[3];
248 
249   return true;
250 }
251 
Repair(int mesh_vertex_count,const ON_3fPoint * V)252 bool ON_MeshFace::Repair(
253   int mesh_vertex_count,
254   const ON_3fPoint* V
255   )
256 {
257   ON_MeshFace f;
258   int fvi_count = 0;
259   f.vi[0] = f.vi[1] = f.vi[2] = f.vi[3] = -1;
260 
261   if ( vi[0] >= 0 && vi[0] < mesh_vertex_count )
262     f.vi[fvi_count++] = vi[0];
263 
264   if ( vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] )
265   {
266     if ( 0 == fvi_count || V[f.vi[0]] != V[vi[1]] )
267       f.vi[fvi_count++] = vi[1];
268   }
269 
270   if ( fvi_count < 1 )
271     return false;
272 
273   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]] )
274   {
275     if ( 1 == fvi_count || V[f.vi[1]] != V[vi[2]] )
276       f.vi[fvi_count++] = vi[2];
277   }
278 
279   if ( fvi_count < 2 )
280     return false;
281 
282   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]] )
283   {
284     if ( 2 == fvi_count || V[f.vi[2]] != V[vi[3]] )
285       f.vi[fvi_count++] = vi[3];
286   }
287 
288   if ( fvi_count < 3 )
289     return false;
290 
291   if ( 3 == fvi_count )
292     f.vi[3] = f.vi[2];
293 
294   if ( !f.IsValid(mesh_vertex_count) )
295     return false;
296 
297   vi[0] = f.vi[0];
298   vi[1] = f.vi[1];
299   vi[2] = f.vi[2];
300   vi[3] = f.vi[3];
301 
302   return true;
303 }
304 
305 
Repair(int mesh_vertex_count,const ON_3dPoint * V)306 bool ON_MeshFace::Repair(
307   int mesh_vertex_count,
308   const ON_3dPoint* V
309   )
310 {
311   ON_MeshFace f;
312   int fvi_count = 0;
313   f.vi[0] = f.vi[1] = f.vi[2] = f.vi[3] = -1;
314 
315   if ( vi[0] >= 0 && vi[0] < mesh_vertex_count )
316     f.vi[fvi_count++] = vi[0];
317 
318   if ( vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] )
319   {
320     if ( 0 == fvi_count || V[f.vi[0]] != V[vi[1]] )
321       f.vi[fvi_count++] = vi[1];
322   }
323 
324   if ( fvi_count < 1 )
325     return false;
326 
327   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]] )
328   {
329     if ( 1 == fvi_count || V[f.vi[1]] != V[vi[2]] )
330       f.vi[fvi_count++] = vi[2];
331   }
332 
333   if ( fvi_count < 2 )
334     return false;
335 
336   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]] )
337   {
338     if ( 2 == fvi_count || V[f.vi[2]] != V[vi[3]] )
339       f.vi[fvi_count++] = vi[3];
340   }
341 
342   if ( fvi_count < 3 )
343     return false;
344 
345   if ( 3 == fvi_count )
346     f.vi[3] = f.vi[2];
347 
348   if ( !f.IsValid(mesh_vertex_count) )
349     return false;
350 
351   vi[0] = f.vi[0];
352   vi[1] = f.vi[1];
353   vi[2] = f.vi[2];
354   vi[3] = f.vi[3];
355 
356   return true;
357 }
358 
ON_Mesh()359 ON_Mesh::ON_Mesh()
360 : m_packed_tex_rotate(0)
361 , m_parent(0)
362 , m_mesh_parameters(0)
363 , m_invalid_count(0)
364 , m_quad_count(0)
365 , m_triangle_count(0)
366 , m_mesh_is_closed(0)
367 , m_mesh_is_manifold(0)
368 , m_mesh_is_oriented(0)
369 , m_mesh_is_solid(0)
370 {
371   m_top.m_mesh = this;
372   m_srf_scale[0] = 0.0;
373   m_srf_scale[1] = 0.0;
374   m_kstat[0] = 0;
375   m_kstat[1] = 0;
376   m_kstat[2] = 0;
377   m_kstat[3] = 0;
378   InvalidateBoundingBoxes();
379   m_partition = 0;
380   m_hidden_count = 0;
381 }
382 
383 
ON_Mesh(int initial_facet_capacity,int initial_vertex_capacity,bool bHasVertexNormals,bool bHasTextureCoordinates)384 ON_Mesh::ON_Mesh(
385     int initial_facet_capacity,  // initial facet array capacity
386     int initial_vertex_capacity,  // initial vertex array capacity
387     bool bHasVertexNormals, // true if mesh has unit vertex normals
388     bool bHasTextureCoordinates // true if mesh has texture coordinates
389     )
390 : m_V(initial_vertex_capacity)
391 , m_F(initial_facet_capacity)
392 , m_N(bHasVertexNormals?initial_vertex_capacity:0)
393 , m_T(bHasTextureCoordinates?initial_vertex_capacity:0)
394 , m_packed_tex_rotate(0)
395 , m_parent(0)
396 , m_mesh_parameters(0)
397 , m_invalid_count(0)
398 , m_quad_count(0)
399 , m_triangle_count(0)
400 , m_mesh_is_closed(0)
401 , m_mesh_is_manifold(0)
402 , m_mesh_is_oriented(0)
403 , m_mesh_is_solid(0)
404 {
405   m_top.m_mesh = this;
406   m_srf_scale[0] = 0.0;
407   m_srf_scale[1] = 0.0;
408   m_kstat[0] = 0;
409   m_kstat[1] = 0;
410   m_kstat[2] = 0;
411   m_kstat[3] = 0;
412   InvalidateBoundingBoxes();
413   m_partition = 0;
414   m_hidden_count = 0;
415 }
416 
ON_Mesh(const ON_Mesh & src)417 ON_Mesh::ON_Mesh( const ON_Mesh& src )
418 : m_packed_tex_rotate(0)
419 , m_parent(0)
420 , m_mesh_parameters(0)
421 , m_invalid_count(0)
422 , m_quad_count(0)
423 , m_triangle_count(0)
424 , m_mesh_is_closed(0)
425 , m_mesh_is_manifold(0)
426 , m_mesh_is_oriented(0)
427 , m_mesh_is_solid(0)
428 {
429   // Do not copy m_mesh_cache. Cached information will
430   // be recalculated if it is needed.
431   m_top.m_mesh = this;
432   m_srf_scale[0] = 0.0;
433   m_srf_scale[1] = 0.0;
434 
435   m_kstat[0] = 0;
436   m_kstat[1] = 0;
437   m_kstat[2] = 0;
438   m_kstat[3] = 0;
439   InvalidateBoundingBoxes();
440   m_partition = 0;
441   m_hidden_count = 0;
442   ON_Mesh::operator=(src);
443 }
444 
445 
SizeOf() const446 unsigned int ON_Mesh::SizeOf() const
447 {
448   unsigned int sz = ON_Geometry::SizeOf();
449   sz += m_V.SizeOfArray();
450   sz += m_F.SizeOfArray();
451   sz += m_N.SizeOfArray();
452   sz += m_FN.SizeOfArray();
453   sz += m_T.SizeOfArray();
454   sz += m_S.SizeOfArray();
455   sz += m_K.SizeOfArray();
456   sz += m_C.SizeOfArray();
457   sz += m_top.m_topv_map.SizeOfArray();
458   sz += m_top.m_topv.SizeOfArray();
459   sz += m_top.m_tope.SizeOfArray();
460   sz += m_top.m_topf.SizeOfArray();
461   return sz;
462 }
463 
DataCRC(ON__UINT32 current_remainder) const464 ON__UINT32 ON_Mesh::DataCRC(ON__UINT32 current_remainder) const
465 {
466   const ON_3fPoint* p = m_V.Array();
467   current_remainder = ON_CRC32(current_remainder,m_V.Count()*sizeof(p[0]),p);
468   current_remainder = ON_CRC32(current_remainder,m_F.Count()*sizeof(ON_MeshFace),m_F.Array());
469   const ON_3fVector* v = m_N.Array();
470   current_remainder = ON_CRC32(current_remainder,m_N.Count()*sizeof(v[0]),v);
471   return current_remainder;
472 }
473 
operator =(const ON_Mesh & src)474 ON_Mesh& ON_Mesh::operator=( const ON_Mesh& src )
475 {
476   if ( this != &src )
477   {
478     Destroy();
479     ON_Geometry::operator=(src);
480 
481     m_V  = src.m_V;
482     m_F  = src.m_F;
483     m_N  = src.m_N;
484     m_FN = src.m_FN;
485     m_T  = src.m_T;
486     m_TC = src.m_TC;
487     m_S  = src.m_S;
488     m_H  = src.m_H;
489     m_hidden_count = src.m_hidden_count;
490 
491     m_Ctag = src.m_Ctag;
492     m_Ttag = src.m_Ttag;
493     m_packed_tex_domain[0] = src.m_packed_tex_domain[0];
494     m_packed_tex_domain[1] = src.m_packed_tex_domain[1];
495     m_srf_domain[0] = src.m_srf_domain[0];
496     m_srf_domain[1] = src.m_srf_domain[1];
497     m_srf_scale[0] = src.m_srf_scale[0];
498     m_srf_scale[1] = src.m_srf_scale[1];
499     m_packed_tex_rotate = src.m_packed_tex_rotate;
500 
501     m_K = src.m_K;
502     m_C = src.m_C;
503 
504     m_parent         = src.m_parent;
505     //m_material_index = src.m_material_index;
506 
507     if ( m_mesh_parameters ) {
508       delete m_mesh_parameters;
509       m_mesh_parameters = 0;
510     }
511     if ( src.m_mesh_parameters )
512       m_mesh_parameters = new ON_MeshParameters(*src.m_mesh_parameters);
513 
514     m_invalid_count = src.m_invalid_count;
515     m_quad_count = src.m_quad_count;
516     m_triangle_count = src.m_triangle_count;
517 
518     m_mesh_is_closed = src.m_mesh_is_closed;
519     m_mesh_is_manifold = src.m_mesh_is_manifold;
520     m_mesh_is_oriented = src.m_mesh_is_oriented;
521     m_mesh_is_solid    = src.m_mesh_is_solid;
522 
523     memcpy(m_vbox,src.m_vbox,sizeof(m_vbox));
524     memcpy(m_nbox,src.m_nbox,sizeof(m_nbox));
525     memcpy(m_tbox,src.m_tbox,sizeof(m_tbox));
526 
527     int i;
528     for ( i = 0; i < 4; i++ ) {
529       if ( m_kstat[i] )
530       {
531         delete m_kstat[i];
532         m_kstat[i] = 0;
533       }
534       if ( src.m_kstat[i] )
535       {
536         m_kstat[i] = new ON_MeshCurvatureStats(*src.m_kstat[i]);
537       }
538     }
539 
540     // do not copy m_top
541 
542     // Do not copy m_mesh_cache. Cached information will
543     // be recalculated if it is needed.
544   }
545   return *this;
546 }
547 
~ON_Mesh()548 ON_Mesh::~ON_Mesh()
549 {
550   Destroy();
551   m_top.m_mesh = 0;
552 }
553 
MemoryRelocate()554 void ON_Mesh::MemoryRelocate()
555 {
556   // the back pointer on m_top needs to be updated.
557   m_top.m_mesh = this;
558 }
559 
Destroy()560 void ON_Mesh::Destroy()
561 {
562   PurgeUserData();
563   DestroyRuntimeCache( true );
564   m_Ttag.Default();
565   m_Ctag.Default();
566   m_V.Destroy();
567   m_F.Destroy();
568   m_N.Destroy();
569   m_FN.Destroy();
570   m_T.Destroy();
571   m_TC.Destroy();
572   m_S.Destroy();
573   m_K.Destroy();
574   m_C.Destroy();
575 }
576 
EmergencyDestroy()577 void ON_Mesh::EmergencyDestroy()
578 {
579   DestroyRuntimeCache( false );
580   m_V.EmergencyDestroy();
581   m_F.EmergencyDestroy();
582   m_N.EmergencyDestroy();
583   m_FN.EmergencyDestroy();
584   m_T.EmergencyDestroy();
585   m_TC.EmergencyDestroy();
586   m_S.EmergencyDestroy();
587   m_K.EmergencyDestroy();
588   m_C.EmergencyDestroy();
589 }
590 
ON_MeshIsNotValid(bool bSilentError)591 static bool ON_MeshIsNotValid(bool bSilentError)
592 {
593   return bSilentError ? false : ON_IsNotValid(); // good place for a breakpoint;
594 }
595 
IsValid(ON_TextLog * text_logx) const596 ON_BOOL32 ON_Mesh::IsValid( ON_TextLog* text_logx ) const
597 {
598   // If low bit of text_log pointer is 1, then ON_Error is not called when the
599   // knot vector is invalid.
600   const ON__INT_PTR lowbit = 1;
601   const ON__INT_PTR hightbits = ~lowbit;
602   bool bSilentError = ( 0 != (lowbit & ((ON__INT_PTR)text_logx)) );
603   ON_TextLog* text_log = (ON_TextLog*)(((ON__INT_PTR)text_logx) & hightbits);
604 
605   const int facet_count = FaceCount();
606   const int vertex_count = VertexCount();
607   int fi, vi;
608 
609   if (facet_count < 1)
610   {
611     if ( text_log )
612     {
613       text_log->Print("ON_Mesh.m_F.Count() < 1 (should be at least 1).\n");
614     }
615     return ON_MeshIsNotValid(bSilentError);
616   }
617 
618   if ( vertex_count < 3 )
619   {
620     if ( text_log )
621     {
622       text_log->Print("ON_Mesh.m_V.Count() < 3 (should be at least 3).\n");
623     }
624     return ON_MeshIsNotValid(bSilentError);
625   }
626 
627   if ( m_N.Count() > 0 && m_N.Count() != vertex_count )
628   {
629     if ( text_log )
630     {
631       text_log->Print("ON_Mesh.m_N.Count() = %d (should be 0 or %d=vertex_count).\n",
632                       m_N.Count(),vertex_count);
633     }
634     return ON_MeshIsNotValid(bSilentError);
635   }
636 
637   if ( m_T.Count() > 0 && m_T.Count() != vertex_count )
638   {
639     if ( text_log )
640     {
641       text_log->Print("ON_Mesh.m_T.Count() = %d (should be 0 or %d=vertex_count).\n",
642                       m_T.Count(),vertex_count);
643     }
644     return ON_MeshIsNotValid(bSilentError);
645   }
646 
647   if ( m_S.Count() > 0 && m_S.Count() != vertex_count )
648   {
649     if ( text_log )
650     {
651       text_log->Print("ON_Mesh.m_S.Count() = %d (should be 0 or %d=vertex_count).\n",
652                       m_S.Count(),vertex_count);
653     }
654     return ON_MeshIsNotValid(bSilentError);
655   }
656 
657   if ( HasVertexNormals() )
658   {
659     float x;
660     for ( vi = 0; vi < vertex_count; vi++ ) {
661       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];
662       if ( x < 0.985 || x > 1.015 )
663       {
664         if ( text_log )
665         {
666           text_log->Print("ON_Mesh.m_N[%d] is not a unit vector (length = %g).\n",vi,sqrt(x));
667         }
668         return ON_MeshIsNotValid(bSilentError);
669       }
670     }
671   }
672 
673 	// Greg Arden 9 May 2003. Fixes TRR#10604.  Attempt to detect meshes with non-finite vertices
674 	// by testing the bounding box.
675   int i;
676   for ( i = 0; i < 3; i++ )
677   {
678     if ( !ON_IsValid( m_vbox[0][i] ) || !ON_IsValid( m_vbox[1][i] ) )
679     {
680       if ( text_log )
681       {
682         text_log->Print("ON_Mesh.m_vbox is not finite.  Check for invalid vertices\n");
683       }
684 	    return ON_MeshIsNotValid(bSilentError);
685     }
686   }
687 
688   const ON_3dPoint* dV = 0;
689   while ( HasDoublePrecisionVertices() )
690   {
691     bool bValidDoubles = DoublePrecisionVerticesAreValid();
692     if ( bValidDoubles )
693       dV = DoublePrecisionVertices().Array();
694     bool bValidFloats  = SinglePrecisionVerticesAreValid();
695     bool bSynchronized = HasSynchronizedDoubleAndSinglePrecisionVertices();
696     if ( bSynchronized && bValidDoubles && bValidFloats )
697       break;
698 
699     if ( !bSynchronized )
700     {
701       if ( text_log )
702       {
703         text_log->Print("Single and double precision vertices are not synchronized.\n");
704       }
705 	    return ON_MeshIsNotValid(bSilentError);
706     }
707 
708     if ( !bValidDoubles )
709     {
710       if ( text_log )
711       {
712         text_log->Print("Double precision vertices appear to be ok but are not marked as valid\n");
713       }
714 	    return ON_MeshIsNotValid(bSilentError);
715     }
716 
717     if ( !bValidFloats )
718     {
719       if ( text_log )
720       {
721         text_log->Print("Single precision vertices appear to be ok but are not marked as valid\n");
722       }
723 	    return ON_MeshIsNotValid(bSilentError);
724     }
725 
726 
727     break;
728   }
729 
730   if ( 0 != dV )
731   {
732     for ( fi = 0; fi < facet_count; fi++ )
733     {
734       if ( !m_F[fi].IsValid( vertex_count, dV ) )
735       {
736         if ( text_log )
737         {
738           if ( !m_F[fi].IsValid( vertex_count) )
739             text_log->Print("ON_Mesh.m_F[%d].vi[] has invalid vertex indices.\n",fi);
740           else
741             text_log->Print("ON_Mesh.m_F[%d] has degenerate double precision vertex locations.\n",fi);
742         }
743         return ON_MeshIsNotValid(bSilentError);
744       }
745     }
746   }
747   else
748   {
749     //const ON_3fPoint* fV = m_V.Array();
750     for ( fi = 0; fi < facet_count; fi++ )
751     {
752       // This test was too harsh for float precision meshes
753       // with nearly degnerate faces after they are transformed
754       // by a transform with a reasonable sized translation
755       // component.
756       // See bug http://dev.mcneel.com/bugtrack/?q=87465
757 
758       //if ( !m_F[fi].IsValid( vertex_count, fV ) )
759       //{
760       //  if ( text_log )
761       //  {
762       //    if ( !m_F[fi].IsValid( vertex_count) )
763       //      text_log->Print("ON_Mesh.m_F[%d].vi[] has invalid vertex indices.\n",fi);
764       //    else
765       //      text_log->Print("ON_Mesh.m_F[%d] has degenerate float precision vertex locations.\n",fi);
766       //  }
767       //  return ON_MeshIsNotValid(bSilentError);
768       //}
769 
770       if ( !m_F[fi].IsValid( vertex_count ) )
771       {
772         if ( text_log )
773           text_log->Print("ON_Mesh.m_F[%d].vi[] has invalid vertex indices.\n",fi);
774         return ON_MeshIsNotValid(bSilentError);
775       }
776     }
777   }
778 
779 
780   return true;
781 }
782 
Dump(ON_TextLog & dump) const783 void ON_Mesh::Dump( ON_TextLog& dump ) const
784 {
785   const int half_max = 8;
786 
787   const int fcount = m_F.Count();
788   int i;
789   const int vcount = m_V.Count();
790   ON_3dPoint p, q;
791 
792   bool bDoubles =    vcount > 0
793                   && HasDoublePrecisionVertices()
794                   && HasSynchronizedDoubleAndSinglePrecisionVertices();
795 
796   dump.Print("ON_Mesh: vertex count = %d  facet count = %d\n", m_V.Count(), m_F.Count() );
797   dump.Print("double precision: %s\n",bDoubles?"true":"false");
798   dump.Print("vertex normals:   %s\n",HasVertexNormals()?"true":"false");
799   dump.Print("face normals:     %s\n",HasFaceNormals()?"true":"false");
800   dump.Print("srf parameters:   %s\n",HasSurfaceParameters()?"true":"false");
801   dump.Print("tex coords:       %s\n",HasTextureCoordinates()?"true":"false");
802   dump.Print("vertex kappa:     %s\n",HasPrincipalCurvatures()?"true":"false");
803   dump.Print("vertex colors:    %s\n",HasVertexColors()?"true":"false");
804   dump.Print("m_Ctag:\n"); dump.PushIndent(); m_Ctag.Dump(dump); dump.PopIndent();
805   dump.Print("m_packed_tex_rotate: %s\n",m_packed_tex_rotate?"true":"false");
806   dump.Print("m_packed_tex_domain: (%g,%g)x(%g,%g)\n",
807              m_packed_tex_domain[0][0],m_packed_tex_domain[0][1],
808              m_packed_tex_domain[1][0],m_packed_tex_domain[1][1]);
809   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]);
810   dump.Print("m_srf_scale: %g,%g\n",m_srf_scale[0],m_srf_scale[0]);
811   dump.Print("m_Ttag:\n"); dump.PushIndent(); m_Ttag.Dump(dump); dump.PopIndent();
812 
813   dump.PushIndent();
814 
815   dump.Print("%d mesh vertices:\n",m_V.Count());
816   {
817     dump.PushIndent();
818     const ON_3dPoint* D = 0;
819     if ( bDoubles )
820     {
821       D = DoublePrecisionVertices().Array();
822     }
823     for (i = 0; i < vcount; i++)
824     {
825       if ( i == half_max && 2*half_max < vcount )
826       {
827         dump.Print("...\n");
828         i = vcount-half_max;
829       }
830       else
831       {
832         p = m_V[i];
833         if ( 0 != D )
834         {
835           q = D[i];
836           dump.Print("m_V[%d] = (%.17g,%.17g,%.17g) D = (%.17g,%.17g,%.17g)\n",
837                      i,
838                      p.x,p.y,p.z,
839                      q.x,q.y,q.z
840                      );
841         }
842         else
843         {
844           dump.Print("m_V[%d] = (%g,%g,%g)\n",i,p.x,p.y,p.z);
845         }
846       }
847     }
848     dump.PopIndent();
849   }
850 
851   if ( HasVertexNormals() )
852   {
853     dump.Print("%d mesh vertex normals:\n",m_N.Count());
854     {
855       dump.PushIndent();
856       for (i = 0; i < vcount; i++)
857       {
858         if ( i == half_max && 2*half_max < vcount )
859         {
860           dump.Print("...\n");
861           i = vcount-half_max;
862         }
863         else
864         {
865           p = m_N[i];
866           dump.Print("m_N[%d] = (%g,%g,%g)\n",i,p.x,p.y,p.z);
867         }
868       }
869       dump.PopIndent();
870     }
871   }
872 
873   if ( HasTextureCoordinates() )
874   {
875     dump.Print("%d mesh vertex texture coordinates:\n",m_T.Count());
876     {
877       dump.PushIndent();
878       for (i = 0; i < vcount; i++)
879       {
880         if ( i == half_max && 2*half_max < vcount )
881         {
882           dump.Print("...\n");
883           i = vcount-half_max;
884         }
885         else
886         {
887           ON_2fPoint tp = m_T[i];
888           p.x = tp.x;
889           p.y = tp.y;
890           dump.Print("m_T[%d] = (%g,%g)\n",i,p.x,p.y);
891         }
892       }
893       dump.PopIndent();
894     }
895   }
896 
897 
898   if ( HasSurfaceParameters() )
899   {
900     dump.Print("%d mesh vertex surface parameters:\n",m_S.Count());
901     {
902       dump.PushIndent();
903       for (i = 0; i < vcount; i++)
904       {
905         if ( i == half_max && 2*half_max < vcount )
906         {
907           dump.Print("...\n");
908           i = vcount-half_max;
909         }
910         else
911         {
912           ON_2dPoint srfuv = m_S[i];
913           dump.Print("m_S[%d] = (%g,%g)\n",i,srfuv.x,srfuv.y);
914         }
915       }
916       dump.PopIndent();
917     }
918   }
919 
920   dump.Print("%d mesh faces:\n",m_F.Count());
921   {
922     dump.PushIndent();
923     for (i = 0; i < fcount; i++)
924     {
925       if ( i == half_max && 2*half_max < fcount )
926       {
927         dump.Print("...\n");
928         i = fcount-half_max;
929       }
930       else if ( m_F[i].vi[2] == m_F[i].vi[3] )
931         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]);
932       else
933         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]);
934     }
935     dump.PopIndent();
936   }
937 
938   if ( HasFaceNormals() )
939   {
940     dump.Print("%d mesh face normals:\n",m_FN.Count());
941     {
942       dump.PushIndent();
943       for (i = 0; i < fcount; i++)
944       {
945         if ( i == half_max && 2*half_max < fcount )
946         {
947           dump.Print("...\n");
948           i = fcount-half_max;
949         }
950         else
951         {
952           p = m_FN[i];
953           dump.Print("m_FN[%d] = (%g,%g,%g)\n",i,p.x,p.y,p.z);
954         }
955       }
956       dump.PopIndent();
957     }
958   }
959 
960 
961   dump.PopIndent();
962 }
963 
964 
WriteFaceArray(int vcount,int fcount,ON_BinaryArchive & file) const965 bool ON_Mesh::WriteFaceArray( int vcount, int fcount, ON_BinaryArchive& file ) const
966 {
967   unsigned char  cvi[4];
968   unsigned short svi[4];
969   const int* vi;
970   int i_size = 0;
971   if ( vcount < 256 ) {
972     i_size = 1; // unsigned chars
973   }
974   else if (vcount < 65536 ) {
975     i_size = 2; // unsigned shorts
976   }
977   else {
978     i_size = 4; // 4 byte ints
979   }
980 
981   bool rc = file.WriteInt( i_size );
982   int i;
983   switch(i_size) {
984   case 1:
985     for ( i = 0; i < fcount && rc ; i++ ) {
986       vi = m_F[i].vi;
987       cvi[0] = (unsigned char)vi[0];
988       cvi[1] = (unsigned char)vi[1];
989       cvi[2] = (unsigned char)vi[2];
990       cvi[3] = (unsigned char)vi[3];
991       rc = file.WriteChar( 4, cvi );
992     }
993     break;
994   case 2:
995     for ( i = 0; i < fcount && rc ; i++ ) {
996       vi = m_F[i].vi;
997       svi[0] = (unsigned short)vi[0];
998       svi[1] = (unsigned short)vi[1];
999       svi[2] = (unsigned short)vi[2];
1000       svi[3] = (unsigned short)vi[3];
1001       rc = file.WriteShort( 4, svi );
1002     }
1003     break;
1004   case 4:
1005     for ( i = 0; i < fcount && rc ; i++ ) {
1006       rc = file.WriteInt( 4, m_F[i].vi );
1007     }
1008     break;
1009   }
1010 
1011   return rc;
1012 }
1013 
ReadFaceArray(int vcount,int fcount,ON_BinaryArchive & file)1014 bool ON_Mesh::ReadFaceArray( int vcount, int fcount, ON_BinaryArchive& file )
1015 {
1016   unsigned char  cvi[4];
1017   unsigned short svi[4];
1018   unsigned int* vi;
1019   int i_size = 0;
1020 
1021   if ( m_F.Capacity() < fcount )
1022     m_F.SetCapacity(fcount);
1023   bool rc = file.ReadInt( &i_size );
1024   int i = 0;
1025   switch(i_size) {
1026   case 1:
1027     for ( i = 0; i < fcount && rc ; i++ ) {
1028       rc = file.ReadChar( 4, cvi );
1029       vi = (unsigned int*)m_F[i].vi;
1030       vi[0] = cvi[0];
1031       vi[1] = cvi[1];
1032       vi[2] = cvi[2];
1033       vi[3] = cvi[3];
1034     }
1035     break;
1036   case 2:
1037     for ( i = 0; i < fcount && rc ; i++ ) {
1038       rc = file.ReadShort( 4, svi );
1039       vi = (unsigned int*)m_F[i].vi;
1040       vi[0] = svi[0];
1041       vi[1] = svi[1];
1042       vi[2] = svi[2];
1043       vi[3] = svi[3];
1044     }
1045     break;
1046   case 4:
1047     for ( i = 0; i < fcount && rc ; i++ ) {
1048       rc = file.ReadInt( 4, m_F[i].vi );
1049     }
1050     break;
1051   }
1052   m_F.SetCount(i);
1053 
1054   return rc;
1055 }
1056 
1057 
Write_1(ON_BinaryArchive & file) const1058 bool ON_Mesh::Write_1( ON_BinaryArchive& file ) const
1059 {
1060   // ver 1.0 uncompressed format
1061 
1062   bool rc = file.WriteArray( m_V );
1063   if (rc) rc = file.WriteArray( m_N );
1064   if (rc) rc = file.WriteArray( m_T );
1065   if (rc) rc = file.WriteArray( m_K );
1066   if (rc) rc = file.WriteArray( m_C );
1067 
1068   return rc;
1069 }
1070 
Read_1(ON_BinaryArchive & file)1071 bool ON_Mesh::Read_1( ON_BinaryArchive& file )
1072 {
1073   // common to all 1.x formats (uncompressed)
1074 
1075   bool rc = file.ReadArray( m_V );
1076   if (rc) rc = file.ReadArray( m_N );
1077   if (rc) rc = file.ReadArray( m_T );
1078   if (rc) rc = file.ReadArray( m_K );
1079   if (rc) rc = file.ReadArray( m_C );
1080 
1081   return rc;
1082 }
1083 
Write_2(int Vcount,ON_BinaryArchive & file) const1084 bool ON_Mesh::Write_2( int Vcount, ON_BinaryArchive& file ) const
1085 {
1086   // ver 2.0 compressed format
1087   const ON::endian e = file.Endian();
1088 
1089   bool rc = true;
1090 
1091   if ( Vcount > m_V.Count() )
1092     return false;
1093 
1094   if ( Vcount > 0 )
1095   {
1096     const int Ncount = (m_V.Count() == m_N.Count()) ? Vcount : 0;
1097     const int Tcount = (m_V.Count() == m_T.Count()) ? Vcount : 0;
1098     const int Kcount = (m_V.Count() == m_K.Count()) ? Vcount : 0;
1099     const int Ccount = (m_V.Count() == m_C.Count()) ? Vcount : 0;
1100 
1101     if ( e == ON::big_endian )
1102     {
1103       // These calls temporarily put the m_V[], m_N[], m_T[], m_K[]
1104       // and m_C[] arrays in little endian byte order because 3dm archives
1105       // are always in little endian byte order.
1106       //
1107       // This code assumes sizeof(ON_Color)=4, sizeof(float)=4
1108       // and sizeof(double)=8.
1109       // If this is not the case, then changing the 4's and 8's below
1110       // will not work.  You will have to copy the mesh definition
1111       // into temporary arrays of 4 byte floats/8 byte doubles
1112       // and them compress the temporary arrays.  If you do this,
1113       // then remove the "restore" byte order calls below.
1114       file.ToggleByteOrder( Vcount*3, 4, m_V.Array(), (void*)m_V.Array() );
1115       file.ToggleByteOrder( Ncount*3, 4, m_N.Array(), (void*)m_N.Array() );
1116       file.ToggleByteOrder( Tcount*2, 4, m_T.Array(), (void*)m_T.Array() );
1117       file.ToggleByteOrder( Kcount*2, 8, m_K.Array(), (void*)m_K.Array() );
1118       file.ToggleByteOrder( Ccount,   4, m_C.Array(), (void*)m_C.Array() );
1119     }
1120     if (rc) rc = file.WriteCompressedBuffer( Vcount*sizeof(ON_3fPoint),         m_V.Array() );
1121     if (rc) rc = file.WriteCompressedBuffer( Ncount*sizeof(ON_3fVector),        m_N.Array() );
1122     if (rc) rc = file.WriteCompressedBuffer( Tcount*sizeof(ON_2fPoint),         m_T.Array() );
1123     if (rc) rc = file.WriteCompressedBuffer( Kcount*sizeof(ON_SurfaceCurvature),m_K.Array() );
1124     if (rc) rc = file.WriteCompressedBuffer( Ccount*sizeof(ON_Color),           m_C.Array() );
1125     if ( e == ON::big_endian )
1126     {
1127       // These calls restore the m_V[], m_N[], m_T[], m_K[] and m_C[] arrays
1128       // to the correct big endian runtime byte order.  This must be done even
1129       // if rc is false.
1130       file.ToggleByteOrder( Vcount*3, 4, m_V.Array(), (void*)m_V.Array() );
1131       file.ToggleByteOrder( Ncount*3, 4, m_N.Array(), (void*)m_N.Array() );
1132       file.ToggleByteOrder( Tcount*2, 4, m_T.Array(), (void*)m_T.Array() );
1133       file.ToggleByteOrder( Kcount*2, 8, m_K.Array(), (void*)m_K.Array() );
1134       file.ToggleByteOrder( Ccount,   4, m_C.Array(), (void*)m_C.Array() );
1135     }
1136   }
1137 
1138   return rc;
1139 }
1140 
Read_2(int vcount,ON_BinaryArchive & file)1141 bool ON_Mesh::Read_2( int vcount, ON_BinaryArchive& file )
1142 {
1143   // common to all 2.x formats (compressed)
1144   const ON::endian e = file.Endian();
1145 
1146   bool rc = true;
1147 
1148 
1149   if ( vcount > 0 )
1150   {
1151     size_t sz = 0;
1152     int bFailedCRC;
1153 
1154     sz = 0;
1155     if (rc) rc = file.ReadCompressedBufferSize( &sz );
1156     if (rc && sz)
1157     {
1158       if ( sz == vcount*sizeof(m_V[0]) )
1159       {
1160         m_V.SetCapacity(vcount);
1161         if (rc) rc = file.ReadCompressedBuffer( sz,m_V.Array(),&bFailedCRC);
1162         if (rc) m_V.SetCount(vcount);
1163       }
1164       else
1165       {
1166         ON_ERROR("ON_Mesh::Read - compressed vertex point buffer size is wrong.");
1167         rc = false; // buffer is wrong size
1168       }
1169     }
1170 
1171     sz = 0;
1172     if (rc) rc = file.ReadCompressedBufferSize( &sz );
1173     if (rc && sz)
1174     {
1175       if ( sz == vcount*sizeof(m_N[0]) )
1176       {
1177         m_N.SetCapacity(vcount);
1178         if (rc) rc = file.ReadCompressedBuffer( sz,m_N.Array(),&bFailedCRC );
1179         if (rc) m_N.SetCount(vcount);
1180       }
1181       else
1182       {
1183         ON_ERROR("ON_Mesh::Read - compressed vertex normal buffer size is wrong.");
1184         rc = false; // buffer is wrong size
1185       }
1186     }
1187 
1188     sz = 0;
1189     if (rc) rc = file.ReadCompressedBufferSize( &sz );
1190     if (rc && sz)
1191     {
1192       if ( sz == vcount*sizeof(m_T[0]) )
1193       {
1194         m_T.SetCapacity(vcount);
1195         if (rc) rc = file.ReadCompressedBuffer( sz,m_T.Array(),&bFailedCRC );
1196         if (rc) m_T.SetCount(vcount);
1197       }
1198       else
1199       {
1200         ON_ERROR("ON_Mesh::Read - compressed texture coordinate buffer size is wrong.");
1201         rc = false; // buffer is wrong size
1202       }
1203     }
1204 
1205     sz = 0;
1206     if (rc) rc = file.ReadCompressedBufferSize( &sz );
1207     if (rc && sz)
1208     {
1209       if ( sz == vcount*sizeof(m_K[0]) )
1210       {
1211         m_K.SetCapacity(vcount);
1212         if (rc) rc = file.ReadCompressedBuffer( sz,m_K.Array(),&bFailedCRC );
1213         if (rc) m_K.SetCount(vcount);
1214       }
1215       else
1216       {
1217         ON_ERROR("ON_Mesh::Read - compressed vertex curvature buffer size is wrong.");
1218         rc = false; // buffer is wrong size
1219       }
1220     }
1221 
1222     sz = 0;
1223     if (rc) rc = file.ReadCompressedBufferSize( &sz );
1224     if (rc && sz)
1225     {
1226       if ( sz == vcount*sizeof(m_C[0]) )
1227       {
1228         m_C.SetCapacity(vcount);
1229         if (rc) rc = file.ReadCompressedBuffer( sz,m_C.Array(),&bFailedCRC );
1230         if (rc) m_C.SetCount(vcount);
1231       }
1232       else
1233       {
1234         ON_ERROR("ON_Mesh::Read - compressed vertex color buffer size is wrong.");
1235         rc = false; // buffer is wrong size
1236       }
1237     }
1238 
1239     if ( e == ON::big_endian )
1240     {
1241       // This code assumes sizeof(ON_Color)=4, sizeof(float)=4
1242       // and sizeof(double)=8.
1243       // If this is not the case, then changing the 4's and 8's below
1244       // will not work.  You will have to read the compressed
1245       // information into temporary arrays of 4 byte floats/8 byte doubles
1246       // and then convert those numbers to whatever is stored in the
1247       // m_V[], m_N[], m_T[], m_K[] and m_C[] arrays/
1248       file.ToggleByteOrder( m_V.Count()*3, 4, m_V.Array(), (void*)m_V.Array() );
1249       file.ToggleByteOrder( m_N.Count()*3, 4, m_N.Array(), (void*)m_N.Array() );
1250       file.ToggleByteOrder( m_T.Count()*2, 4, m_T.Array(), (void*)m_T.Array() );
1251       file.ToggleByteOrder( m_K.Count()*2, 8, m_K.Array(), (void*)m_K.Array() );
1252       file.ToggleByteOrder( m_C.Count()*3, 4, m_C.Array(), (void*)m_C.Array() );
1253     }
1254   }
1255 
1256   return rc;
1257 }
1258 
1259 
Write(ON_BinaryArchive & file) const1260 ON_BOOL32 ON_Mesh::Write( ON_BinaryArchive& file ) const
1261 {
1262   int i;
1263   //const int major_version = 1; // uncompressed
1264   //const int major_version = 2; // beta format (never used)
1265   const int major_version = 3; // compressed
1266   bool rc = file.Write3dmChunkVersion(major_version,5);
1267 
1268   const int vcount = VertexCount();
1269   const int fcount = FaceCount();
1270 
1271   if (rc) rc = file.WriteInt( vcount );
1272   if (rc) rc = file.WriteInt( fcount );
1273   if (rc) rc = file.WriteInterval( m_packed_tex_domain[0] );
1274   if (rc) rc = file.WriteInterval( m_packed_tex_domain[1] );
1275   if (rc) rc = file.WriteInterval( m_srf_domain[0] );
1276   if (rc) rc = file.WriteInterval( m_srf_domain[1] );
1277   if (rc) rc = file.WriteDouble( 2, m_srf_scale );
1278   if (rc) rc = file.WriteFloat( 6, &m_vbox[0][0] );
1279   if (rc) rc = file.WriteFloat( 6, &m_nbox[0][0] );
1280   if (rc) rc = file.WriteFloat( 4, &m_tbox[0][0] );
1281 
1282   // archive int value meaning: -1 = unknown 0 = mesh is not closed, 1 = mesh is closed
1283   i = -1;
1284   switch( m_mesh_is_closed )
1285   {
1286   case 0: // unset
1287     i = -1;
1288     break;
1289   case 1: // closed
1290     i = 1;
1291     break;
1292   case 2: // not closed
1293     i = 0;
1294     break;
1295   }
1296   if (rc) rc = file.WriteInt( i );
1297 
1298   unsigned char b = m_mesh_parameters ? 1 : 0;
1299   if (rc) rc = file.WriteChar(b);
1300   if (rc && b) {
1301     if (rc) rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 );
1302     if (rc) {
1303       rc = m_mesh_parameters->Write(file);
1304       if ( !file.EndWrite3dmChunk() )
1305         rc = false;
1306     }
1307   }
1308 
1309   for ( i = 0; rc && i < 4; i++ ) {
1310     b = m_kstat[i] ? 1 : 0;
1311     rc = file.WriteChar(b);
1312     if (b) {
1313       rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 );
1314       if (rc) {
1315         rc = m_kstat[i]->Write(file);
1316         if ( !file.EndWrite3dmChunk() )
1317           rc = false;
1318       }
1319     }
1320   }
1321 
1322   if (rc) rc = WriteFaceArray( vcount, fcount, file );
1323 
1324   if (rc) {
1325     // major version is a hard coded 3
1326 
1327     //if ( major_version == 1 )
1328     //  rc = Write_1(file);
1329     //else if ( major_version == 3 )
1330       rc = Write_2(vcount,file);
1331     //else
1332     //  rc = false;
1333   }
1334 
1335   // added for minor version 1.2 and 3.2
1336   i = m_packed_tex_rotate ? 1 : 0;
1337   if (rc) rc = file.WriteInt( i );
1338 
1339   // added for minor version 3.3
1340   if (rc) rc = file.WriteUuid( m_Ttag.m_mapping_id );
1341 
1342   // compressed m_S[]
1343   if ( rc && vcount > 0 )
1344   {
1345     // Before 201011049 there was a bug that let m_S[] arrays
1346     // with the wrong size get saved in files.
1347     const int Scount = (vcount == m_S.Count()) ? m_S.Count() : 0;
1348     const ON::endian e = file.Endian();
1349     if ( e == ON::big_endian )
1350     {
1351       file.ToggleByteOrder( Scount*2, 8, m_S.Array(), (void*)m_S.Array() );
1352     }
1353     if (rc) rc = file.WriteCompressedBuffer( Scount*sizeof(ON_2dPoint),m_S.Array() );
1354     if ( e == ON::big_endian )
1355     {
1356       file.ToggleByteOrder( Scount*2, 8, m_S.Array(), (void*)m_S.Array() );
1357     }
1358   }
1359 
1360   // added for minor version 3.4
1361   if (rc) rc = m_Ttag.Write(file);
1362 
1363   // added for minor version 3.5
1364   if (rc) rc = file.WriteChar( m_mesh_is_manifold );
1365   if (rc) rc = file.WriteChar( m_mesh_is_oriented );
1366   if (rc) rc = file.WriteChar( m_mesh_is_solid );
1367 
1368 
1369   return rc;
1370 }
1371 
1372 //// This id was used in the ON_Mesh::m_mapping_id
1373 //// field to indicate the texture coordinates are the
1374 //// canonical ON_Mesh uv texture coordinates by the
1375 //// OpenNURBS parameteric surface meshers like
1376 //// ON_Surface::CreateMesh() and ON_Brep::CreateMesh().
1377 //
1378 //// {B988A6C2-61A6-45a7-AAEE-9AED7EF4E316}
1379 static const ON_UUID obsolete_default_srfp_mapping_id = { 0xb988a6c2, 0x61a6, 0x45a7, { 0xaa, 0xee, 0x9a, 0xed, 0x7e, 0xf4, 0xe3, 0x16 } };
1380 
1381 // overrides virtual ON_Object::Write
Write(ON_BinaryArchive & file) const1382 ON_BOOL32 ON_TextureMapping::Write(
1383         ON_BinaryArchive& file
1384       ) const
1385 {
1386   bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1);
1387   if (rc)
1388   {
1389 
1390     for(;;)
1391     {
1392       rc = file.WriteUuid( m_mapping_id);
1393       if (!rc) break;
1394 
1395       rc = file.WriteInt( m_type );
1396       if (!rc) break;
1397 
1398       rc = file.WriteInt( m_projection );
1399       if (!rc) break;
1400 
1401       rc = file.WriteXform( m_Pxyz );
1402       if (!rc) break;
1403 
1404       // Do not write m_Nxyz - it is calculated from m_Pxyz.
1405       rc = file.WriteXform( m_uvw );
1406       if (!rc) break;
1407 
1408       rc = file.WriteString(m_mapping_name);
1409       if (!rc) break;
1410 
1411       rc = file.WriteObject(m_mapping_primitive);
1412       if (!rc) break;
1413 
1414       // 13 October 2006 ver 1.1 fields
1415       rc = file.WriteInt(m_texture_space);
1416       if (!rc) break;
1417 
1418       rc = file.WriteBool(m_bCapped);
1419       if (!rc) break;
1420 
1421       break;
1422     }
1423 
1424     if ( !file.EndWrite3dmChunk() )
1425       rc = false;
1426   }
1427 
1428   return rc;
1429 }
1430 
1431 // overrides virtual ON_Object::Read
Read(ON_BinaryArchive & file)1432 ON_BOOL32 ON_TextureMapping::Read(
1433         ON_BinaryArchive& file
1434       )
1435 {
1436   Default();
1437 
1438   int major_version = 0;
1439   int minor_version = 0;
1440   int i;
1441 
1442   bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
1443   if (rc)
1444   {
1445     if ( 1 == major_version )
1446     {
1447       // DO NOT SAVE m_mapping_index in archive.
1448       // 1.0 fields
1449       for(;;)
1450       {
1451         rc = file.ReadUuid( m_mapping_id );
1452         if (!rc) break;
1453         if ( 0 == ON_UuidCompare(&obsolete_default_srfp_mapping_id,&m_mapping_id) )
1454           m_mapping_id = ON_nil_uuid;
1455 
1456         rc = file.ReadInt( &i );
1457         if (!rc) break;
1458         m_type = TypeFromInt(i);
1459 
1460         rc = file.ReadInt( &i );
1461         if (!rc) break;
1462         m_projection = ProjectionFromInt(i);
1463 
1464         rc = file.ReadXform( m_Pxyz );
1465         if (!rc) break;
1466 
1467         m_Pxyz.GetSurfaceNormalXform(m_Nxyz);
1468 
1469         rc = file.ReadXform( m_uvw );
1470         if (!rc) break;
1471 
1472         rc = file.ReadString(m_mapping_name);
1473         if (!rc) break;
1474 
1475         rc = (file.ReadObject(&m_mapping_primitive) >= 0);
1476         if (!rc) break;
1477 
1478         if ( minor_version >= 1 )
1479         {
1480           rc = file.ReadInt(&i);
1481           if (!rc) break;
1482           m_texture_space = TextureSpaceFromInt(i);
1483 
1484           rc = file.ReadBool(&m_bCapped);
1485           if (!rc) break;
1486         }
1487 
1488         break;
1489       }
1490     }
1491 
1492     if ( !file.EndRead3dmChunk() )
1493       rc = false;
1494   }
1495 
1496   return rc;
1497 }
1498 
1499 static
GetSurfaceParametersHelper(const ON_Mesh & mesh,double tex_x,double tex_y,double * srf_s,double * srf_t)1500 void GetSurfaceParametersHelper( const ON_Mesh& mesh,
1501                                  double tex_x, double tex_y,
1502                                  double* srf_s, double* srf_t )
1503 {
1504   // convert texture coordinates to surface parameters
1505   // Used to reconstruct m_S[] when old files are read.
1506   double tex_s, tex_t;
1507 
1508   if ( mesh.m_packed_tex_rotate )
1509   {
1510     // undo rotation and normalize
1511     tex_s =  mesh.m_packed_tex_domain[1].NormalizedParameterAt( tex_y );
1512     tex_t = 1.0 -  mesh.m_packed_tex_domain[0].NormalizedParameterAt( tex_x );
1513   }
1514   else
1515   {
1516     // normalize
1517     tex_s =  mesh.m_packed_tex_domain[0].NormalizedParameterAt( tex_x );
1518     tex_t =  mesh.m_packed_tex_domain[1].NormalizedParameterAt( tex_y );
1519   }
1520   *srf_s =  mesh.m_srf_domain[0].ParameterAt(tex_s);
1521   *srf_t =  mesh.m_srf_domain[1].ParameterAt(tex_t);
1522 }
1523 
1524 
Read(ON_BinaryArchive & file)1525 ON_BOOL32 ON_Mesh::Read( ON_BinaryArchive& file )
1526 {
1527   Destroy();
1528 
1529   int major_version = 0;
1530   int minor_version = 0;
1531   int i;
1532   bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
1533 
1534   if (rc && (1 == major_version || 3 == major_version) )
1535   {
1536     int vcount = 0;
1537     int fcount = 0;
1538 
1539     if (rc) rc = file.ReadInt( &vcount );
1540     if (rc) rc = file.ReadInt( &fcount );
1541     if (rc) rc = file.ReadInterval( m_packed_tex_domain[0] );
1542     if (rc) rc = file.ReadInterval( m_packed_tex_domain[1] );
1543     if (rc) rc = file.ReadInterval( m_srf_domain[0] );
1544     if (rc) rc = file.ReadInterval( m_srf_domain[1] );
1545     if (rc) rc = file.ReadDouble( 2, m_srf_scale );
1546     if (rc) rc = file.ReadFloat( 6, &m_vbox[0][0] );
1547     if (rc) rc = file.ReadFloat( 6, &m_nbox[0][0] );
1548     if (rc) rc = file.ReadFloat( 4, &m_tbox[0][0] );
1549 
1550     // int value meaning: -1 = unknown 0 = mesh is not closed, 1 = mesh is closed
1551     i = -1;
1552     if (rc) rc = file.ReadInt( &i );
1553     if (rc)
1554     {
1555       switch(i)
1556       {
1557       case 0: // not closed;
1558         SetClosed(0);
1559         break;
1560       case 1: // closed;
1561         SetClosed(1);
1562         break;
1563       case 2: // 13 April 2010 Dale Lear - "2" value is obsolete but appears in old files
1564         SetClosed(1);
1565         break;
1566       }
1567     }
1568 
1569     unsigned char b = 0;
1570     ON__UINT32 tcode=0;
1571     ON__INT64 big_value=0;
1572     if (rc) rc = file.ReadChar(&b);
1573     if (rc && b)
1574     {
1575       // mesh parameters are in an anonymous chunk
1576       rc = file.BeginRead3dmBigChunk(&tcode,&big_value);
1577       if (rc)
1578       {
1579         if ( TCODE_ANONYMOUS_CHUNK == tcode )
1580         {
1581           m_mesh_parameters = new ON_MeshParameters();
1582           rc = m_mesh_parameters->Read( file );
1583         }
1584         else
1585           rc = false;
1586         if (!file.EndRead3dmChunk())
1587           rc = false;
1588       }
1589     }
1590 
1591     for ( i = 0; rc && i < 4; i++ )
1592     {
1593       rc = file.ReadChar(&b);
1594       if (rc && b)
1595       {
1596         // m_kstat[i] curvature stats are in an anonymous chunk
1597         tcode = 0;
1598         big_value = 0;
1599         rc = file.BeginRead3dmBigChunk( &tcode, &big_value );
1600         if (rc)
1601         {
1602           if ( TCODE_ANONYMOUS_CHUNK == tcode )
1603           {
1604             m_kstat[i] = new ON_MeshCurvatureStats();
1605             rc = m_kstat[i]->Read(file);
1606           }
1607           else
1608             rc = false;
1609           if ( !file.EndRead3dmChunk() )
1610             rc = false;
1611         }
1612       }
1613     }
1614 
1615     if (rc) rc = ReadFaceArray( vcount, fcount, file );
1616 
1617     if (rc) {
1618       if ( major_version==1) {
1619         rc = Read_1(file);
1620       }
1621       else if ( major_version == 3 ) {
1622         rc = Read_2(vcount,file);
1623       }
1624       else
1625         rc = false;
1626     }
1627 
1628     if ( minor_version >= 2 )
1629     {
1630       int b = m_packed_tex_rotate;
1631       if (rc) rc = file.ReadInt( &b );
1632       m_packed_tex_rotate = b?true:false;
1633     }
1634 
1635     if ( 3 == major_version )
1636     {
1637       if ( minor_version >= 3 )
1638       {
1639         // added for minor version 3.3
1640         if (rc) rc = file.ReadUuid( m_Ttag.m_mapping_id );
1641 
1642         // compressed m_S[]
1643         if ( rc && vcount > 0 )
1644         {
1645           size_t sz = 0;
1646           ON_BOOL32 bFailedCRC=false;
1647           if (rc) rc = file.ReadCompressedBufferSize( &sz );
1648           if (rc && sz)
1649           {
1650             if ( sz == vcount*sizeof(ON_2dPoint) )
1651             {
1652               m_S.SetCapacity(vcount);
1653               if (rc) rc = file.ReadCompressedBuffer( sz, m_S.Array(), &bFailedCRC );
1654               if (rc) m_S.SetCount(vcount);
1655               if ( ON::big_endian == file.Endian() )
1656               {
1657                 file.ToggleByteOrder( m_S.Count()*2, 8, m_S.Array(), (void*)m_S.Array() );
1658               }
1659             }
1660             else
1661             {
1662               ON_ERROR("ON_Mesh::Read - surface parameter buffer size is wrong.");
1663               if (    rc
1664                    && file.ArchiveOpenNURBSVersion() <= 201011049
1665                    && 0 == (sz % sizeof(ON_2dPoint))
1666                    && sz >= sizeof(ON_2dPoint)
1667                  )
1668               {
1669                 // Before 201011049 there was a bug that let m_S[] arrays with
1670                 // the wrong size get saved in files. There was also a bug in
1671                 // the Rhino .OBJ file reader that created meshes with m_S[]
1672                 // arrays that had the wrong size.  The next 4 lines of code
1673                 // let us read the junk, discard it and then successfully read
1674                 // the rest of the file.
1675                 int Scount = (int)(sz / sizeof(ON_2dPoint));
1676                 m_S.SetCapacity(Scount);
1677                 rc = file.ReadCompressedBuffer( sz, m_S.Array(), &bFailedCRC );
1678                 m_S.Destroy();
1679               }
1680               else
1681               {
1682                 rc = false; // buffer is wrong size
1683               }
1684             }
1685           }
1686         }
1687         if ( minor_version >= 4 && file.ArchiveOpenNURBSVersion() >= 200606010 )
1688         {
1689           if (rc) rc = m_Ttag.Read(file);
1690           if ( minor_version >= 5 )
1691           {
1692             if (rc) rc = file.ReadChar( &m_mesh_is_manifold );
1693             if (rc) rc = file.ReadChar( &m_mesh_is_oriented );
1694             if (rc) rc = file.ReadChar( &m_mesh_is_solid );
1695           }
1696         }
1697       }
1698     }
1699 
1700     if (    0 == m_S.Count()
1701          && m_V.Count() > 0
1702          && HasTextureCoordinates()
1703          && m_srf_domain[0].IsIncreasing()
1704          && m_srf_domain[1].IsIncreasing()
1705          && m_packed_tex_domain[0].IsInterval()
1706          && m_packed_tex_domain[1].IsInterval()
1707          && 0 == m_Ttag.m_mapping_crc
1708          && ON_UuidIsNil(m_Ttag.m_mapping_id)
1709           )
1710     {
1711       // This is a mesh from an old file - but there is enough
1712       // information to calculate the m_S[] values from the
1713       // m_T[] values.
1714       m_S.SetCapacity(vcount);
1715       m_S.SetCount(0);
1716       ON_2dPoint sp;
1717       ON_2fPoint tc;
1718       for ( i = 0; i < vcount; i++)
1719       {
1720         tc = m_T[i];
1721         sp.x = tc.x;
1722         sp.y = tc.y;
1723         GetSurfaceParametersHelper(*this,sp.x,sp.y,&sp.x,&sp.y);
1724         m_S.Append(sp);
1725       }
1726       m_Ttag.SetDefaultSurfaceParameterMappingTag();
1727     }
1728   }
1729 
1730   return rc;
1731 }
1732 
ObjectType() const1733 ON::object_type ON_Mesh::ObjectType() const
1734 {
1735   return ON::mesh_object;
1736 }
1737 
Dimension() const1738 int ON_Mesh::Dimension() const
1739 {
1740   return 3;
1741 }
1742 
1743 #if defined(ON_COMPILER_MSC)
1744 // Disable the MSC /W4 warning
1745 //   C4189: 'breakpoint_here_for_bad_vbox' : local variable is initialized but not referenced
1746 // on the line
1747 //   int breakpoint_here_for_bad_vbox = 0.
1748 // The warning disable is here because MS is ignoring it
1749 // if I put it inside of the function.
1750 #pragma warning( push )
1751 #pragma warning( disable : 4189 )
1752 #endif
1753 
ON_FloatFloor(double x)1754 float ON_FloatFloor(double x)
1755 {
1756   // If x is a NaN, you get what you deserve.
1757   //
1758   // If x is a finite valid double in the range -3.402823466e+38
1759   // to +3.402823466e+38, then returned value of f is the largest
1760   // float value that is mathematically less than or equal to the
1761   // value of x.
1762   //
1763   // If x is not in the float range or x is a NaN, then you get
1764   // what you deserve.
1765   //
1766   // ON_FLOAT_EPSILON = 1.192092896e-07 is the smallest number such that
1767   // 1.0f + 1.192092896e-07f > 1.0f.
1768   //
1769   // If x < 0, then (1.0 + 0.5*1.192092896e-07)*x rounds x down so
1770   // that converting the double precision mantissa cannot create a
1771   // float value that is mathematically larger than the value of x.
1772   //
1773   // If x > 0, then (1.0 - 0.5*1.192092896e-07)*x rounds x down so
1774   // that converting the double precision mantissa cannot create a
1775   // float value that is mathematically larger than the value of x.
1776   //
1777   const double e = (x < 0.0) ? (1.0 + 0.5*ON_FLOAT_EPSILON) : (1.0 - 0.5*ON_FLOAT_EPSILON);
1778   float f;
1779   f = (float)(e*x);
1780   return f;
1781 }
1782 
ON_FloatCeil(double x)1783 float ON_FloatCeil(double x)
1784 {
1785   float f = (x != 0.0) ? (-ON_FloatFloor(-x)) : ((float)x);
1786   return f;
1787 }
1788 
GetBBox(double * boxmin,double * boxmax,ON_BOOL32 bGrowBox) const1789 ON_BOOL32 ON_Mesh::GetBBox( // returns true if successful
1790        double* boxmin, // minimum
1791        double* boxmax, // maximum
1792        ON_BOOL32 bGrowBox
1793        ) const
1794 {
1795   ON_BOOL32 rc = false;
1796   const int facet_count  = FaceCount();
1797   const int vertex_count = VertexCount();
1798   if ( facet_count >= 1 && vertex_count >= 3 )
1799   {
1800     ON_BoundingBox vbox;
1801     if ( m_vbox[0][0] > m_vbox[1][0] )
1802     {
1803       // const lie - cache mesh bounding box
1804       float* fbbox[2] = {const_cast<float*>(&m_vbox[0][0]),const_cast<float*>(&m_vbox[1][0])};
1805       while ( HasDoublePrecisionVertices() && DoublePrecisionVerticesAreValid() )
1806       {
1807         double dbbox[2][3];
1808         const ON_3dPointArray& dV = DoublePrecisionVertices();
1809         rc = ON_GetPointListBoundingBox(
1810                 3, 0, vertex_count, 3, &dV[0].x,
1811                 &dbbox[0][0], &dbbox[1][0],
1812                 false
1813                 );
1814         if (!rc)
1815           break;
1816         // make sure we round min doubles down to the nearest float
1817         // and max doubles up to the nearest float.
1818         fbbox[0][0] = ON_FloatFloor(dbbox[0][0]);
1819         fbbox[0][1] = ON_FloatFloor(dbbox[0][1]);
1820         fbbox[0][2] = ON_FloatFloor(dbbox[0][2]);
1821         fbbox[1][0] = ON_FloatCeil(dbbox[1][0]);
1822         fbbox[1][1] = ON_FloatCeil(dbbox[1][1]);
1823         fbbox[1][2] = ON_FloatCeil(dbbox[1][2]);
1824 
1825         // depending on how
1826         if ( SinglePrecisionVerticesAreValid() )
1827         {
1828           ON_GetPointListBoundingBox(
1829                 3, 0, vertex_count, 3, &m_V[0].x,
1830                 &fbbox[0][0], &fbbox[1][0],
1831                 true
1832               );
1833         }
1834         break;
1835       }
1836 
1837       if (!rc)
1838       {
1839         rc = ON_GetPointListBoundingBox( 3, 0,
1840                 vertex_count, 3, &m_V[0].x,
1841                 fbbox[0],fbbox[1],
1842                 false
1843                 );
1844       }
1845     }
1846     else
1847     {
1848       rc = true;
1849     }
1850 
1851     if ( rc )
1852     {
1853       vbox.m_min.x = m_vbox[0][0];
1854       vbox.m_min.y = m_vbox[0][1];
1855       vbox.m_min.z = m_vbox[0][2];
1856       vbox.m_max.x = m_vbox[1][0];
1857       vbox.m_max.y = m_vbox[1][1];
1858       vbox.m_max.z = m_vbox[1][2];
1859       rc = vbox.IsValid();
1860       if (rc)
1861       {
1862         if ( bGrowBox )
1863         {
1864           vbox.Union( ON_BoundingBox(boxmin,boxmax) );
1865         }
1866 
1867         boxmin[0] = vbox.m_min.x;
1868         boxmin[1] = vbox.m_min.y;
1869         boxmin[2] = vbox.m_min.z;
1870 
1871         boxmax[0] = vbox.m_max.x;
1872         boxmax[1] = vbox.m_max.y;
1873         boxmax[2] = vbox.m_max.z;
1874       }
1875 #if defined(ON_DEBUG) && !defined(ON_COMPILER_GNU)
1876       // generates gcc unused variable warning
1877       else
1878       {
1879         int breakpoint_here_for_bad_vbox=0;
1880       }
1881 #endif
1882     }
1883   }
1884   return rc;
1885 }
1886 
1887 #if defined(ON_COMPILER_MSC)
1888 #pragma warning( pop )
1889 #endif
1890 
IsDeformable() const1891 bool ON_Mesh::IsDeformable() const
1892 {
1893   return true;
1894 }
1895 
MakeDeformable()1896 bool ON_Mesh::MakeDeformable()
1897 {
1898   return true;
1899 }
1900 
Transform(const ON_Xform & xform)1901 ON_BOOL32 ON_Mesh::Transform(
1902        const ON_Xform& xform
1903        )
1904 {
1905   const bool bIsValid_fV = SinglePrecisionVerticesAreValid();
1906   const bool bIsValid_dV = DoublePrecisionVerticesAreValid();
1907   const bool bSyncheddV = bIsValid_fV && bIsValid_dV && HasSynchronizedDoubleAndSinglePrecisionVertices();
1908   TransformUserData(xform);
1909 	DestroyTree();
1910 
1911   double d = xform.Determinant();
1912   const int vertex_count = VertexCount();
1913   bool rc = false;
1914   if ( bSyncheddV )
1915   {
1916     // transforming the double precision vertices is the
1917     // best way to set the floats.
1918     UpdateSinglePrecisionVertices();
1919     rc = true;
1920   }
1921   else
1922   {
1923     rc = ON_TransformPointList( 3, false, vertex_count, 3, &m_V[0][0], xform );
1924   }
1925 
1926   if ( rc )
1927   {
1928     m_Ctag.Transform(xform);
1929     m_Ttag.Transform(xform);
1930     int tci, tccnt = m_TC.Count();
1931     for ( tci = 0; tci < tccnt; tci++ )
1932     {
1933       m_TC[tci].m_tag.Transform(xform);
1934     }
1935   }
1936 
1937   if ( rc && 0.0 == d )
1938   {
1939     // mesh has been squashed to a plane (or worse)
1940     if ( HasVertexNormals() )
1941     {
1942       ComputeFaceNormals();
1943       ComputeVertexNormals();
1944     }
1945     else if ( HasFaceNormals() )
1946     {
1947       ComputeFaceNormals();
1948     }
1949   }
1950   else if ( rc )
1951   {
1952     if ( HasVertexNormals() )
1953     {
1954       // See http://www.gignews.com/realtime020100.htm or these
1955       // references.
1956       //
1957       // 1. Hanrahan, Pat,
1958       //    "A Survey of Ray-Surface Intersection Algorithms",
1959       //     chapter 3 in Andrew Glassner (editor),
1960       //     An Introduction to Ray Tracing,
1961       //     Academic Press Inc., London, 1989.
1962       //
1963       // 2. Turkowski, Ken,
1964       //    "Properties of Surface-Normal Transformations",
1965       //     in Andrew Glassner (editor),
1966       //     Graphics Gems, Academic Press, Inc.,
1967       //     pp. 539-547, 1990.
1968       ON_Xform N_xform;
1969       double d = xform.GetSurfaceNormalXform(N_xform);
1970       rc = ON_TransformVectorList( 3, vertex_count, 3, &m_N[0][0], N_xform )?true:false;
1971       if ( d < 0.0 )
1972       {
1973         FlipVertexNormals();
1974       }
1975       UnitizeVertexNormals();
1976     }
1977 
1978     if ( rc && HasFaceNormals() )
1979     {
1980       ComputeFaceNormals();
1981     }
1982   }
1983 
1984   if ( rc && HasPrincipalCurvatures() )
1985   {
1986     if ( fabs(fabs(d) - 1.0) > ON_SQRT_EPSILON )
1987     {
1988       // If it's a uniform scale, handle it, otherwise we can't do it.
1989       double scale = xform.m_xform[0][0];
1990       if ( 0.0 != scale && 0.0 != d
1991            && scale == xform.m_xform[1][1]
1992            && scale == xform.m_xform[2][2]
1993            && fabs(d - scale*scale*scale) <= d*ON_SQRT_EPSILON )
1994       {
1995         // uniform scale
1996         const double ks = 1.0/scale;
1997         ON_SurfaceCurvature* sc = m_K.Array();
1998         int ki = m_K.Count();
1999         while ( ki-- )
2000         {
2001           sc->k1 *= ks;
2002           sc->k2 *= ks;
2003           sc++;
2004         }
2005 
2006         // update curvature stats.
2007         for ( int j = 0; j < 4; j++ )
2008         {
2009           if ( m_kstat[j] )
2010             m_kstat[j]->Set( m_kstat[j]->m_style,m_K.Count(),m_K.Array(),m_N.Array() );
2011         }
2012       }
2013       else
2014       {
2015         ON_ERROR("ON_Mesh::Transform() cannot apply this transform to curvatures.\n");
2016         rc = false;
2017       }
2018     }
2019   }
2020 
2021   InvalidateVertexBoundingBox();
2022   InvalidateVertexNormalBoundingBox();
2023   if ( fabs(d) <= ON_ZERO_TOLERANCE )
2024     DestroyTopology(); // transform may not be one-to-one on vertices
2025 
2026   if ( bIsValid_fV )
2027     SetSinglePrecisionVerticesAsValid();
2028   if ( bIsValid_dV )
2029     SetDoublePrecisionVerticesAsValid();
2030 
2031   return rc;
2032 }
2033 
DestroyRuntimeCache(bool bDelete)2034 void ON_Mesh::DestroyRuntimeCache( bool bDelete )
2035 {
2036   int i;
2037 
2038   DestroyTree(bDelete);
2039 
2040   if (bDelete )
2041   {
2042     DestroyPartition();
2043     m_top.Destroy();
2044     DeleteMeshParameters();
2045     InvalidateCurvatureStats();
2046   }
2047   else
2048   {
2049     // do not free any memory
2050     m_top.EmergencyDestroy();
2051   }
2052 
2053   InvalidateBoundingBoxes();
2054   m_partition = 0;
2055   m_mesh_parameters = 0;
2056   m_top.m_mesh = this;
2057   m_parent = 0;
2058   //m_material_index = -1;
2059   m_mesh_is_closed = 0;
2060   m_mesh_is_manifold = 0;
2061   m_mesh_is_oriented = 0;
2062   m_mesh_is_solid = 0;
2063   for ( i = 0; i < 4; i++ )
2064   {
2065     m_kstat[i] = 0;
2066   }
2067 }
2068 
SwapCoordinates(int i,int j)2069 ON_BOOL32 ON_Mesh::SwapCoordinates(
2070       int i, int j        // indices of coords to swap
2071       )
2072 {
2073   if ( i == j )
2074     return true;
2075 
2076   const bool bIsValid_fV = SinglePrecisionVerticesAreValid();
2077   const bool bIsValid_dV = DoublePrecisionVerticesAreValid();
2078 
2079   const int vertex_count = VertexCount();
2080   ON_BOOL32 rc = ON_SwapPointListCoordinates( vertex_count, 3, &m_V[0][0], i, j );
2081   if ( rc && HasVertexNormals() ) {
2082     rc = ON_SwapPointListCoordinates( vertex_count, 3, &m_N[0][0], i, j );
2083   }
2084   if ( rc )
2085   {
2086     float x;
2087     if ( m_vbox[0][0] <= m_vbox[1][0] ) {
2088       x = m_vbox[0][i]; m_vbox[0][i] = m_vbox[0][j]; m_vbox[0][j] = x;
2089       x = m_vbox[1][i]; m_vbox[1][i] = m_vbox[1][j]; m_vbox[1][j] = x;
2090     }
2091     if ( m_nbox[0][0] <= m_nbox[1][0] ) {
2092       x = m_nbox[0][i]; m_nbox[0][i] = m_nbox[0][j]; m_nbox[0][j] = x;
2093       x = m_nbox[1][i]; m_nbox[1][i] = m_nbox[1][j]; m_nbox[1][j] = x;
2094     }
2095   }
2096 
2097   if ( HasDoublePrecisionVertices() )
2098   {
2099     DoublePrecisionVertices().SwapCoordinates(i,j);
2100     if ( bIsValid_fV )
2101       SetSinglePrecisionVerticesAsValid();
2102     if ( bIsValid_dV )
2103       SetDoublePrecisionVerticesAsValid();
2104   }
2105 
2106   return rc;
2107 }
2108 
SetClosed(int b)2109 void ON_Mesh::SetClosed(int b)
2110 {
2111   // 6 Novermber 2003 Dale Lear - let expert user set m_mesh_is_closed
2112   char mesh_is_closed = 0;
2113   switch(b)
2114   {
2115   case 0: // not closed - at least one boundary edge
2116     mesh_is_closed = 2;
2117     SetSolidOrientation(0);
2118     break;
2119   case 1: // all edges are shared
2120     // DO NOT SET m_mesh_is_solid here.
2121     // Meshes can be closed but not solid
2122     mesh_is_closed = 1;
2123     break;
2124   case 2: // 31 April 2010 Dale Lear - 2 is obsolete - it's either 0 or 1 now.
2125     mesh_is_closed = 1;
2126     // DO NOT SET m_mesh_is_solid here.
2127     // Meshes can be closed but not solid
2128     break;
2129   default:
2130     mesh_is_closed = 0; // unset
2131     break;
2132   }
2133   if ( 0 == mesh_is_closed || m_mesh_is_closed != mesh_is_closed )
2134   {
2135     m_mesh_is_closed = mesh_is_closed;
2136     m_mesh_is_manifold = 0; // unset - will be reevaluated when needed
2137     m_mesh_is_oriented = 0; // unset - will be reevaluated when needed
2138   }
2139 }
2140 
SetSolidOrientation(int solid_orientation)2141 void ON_Mesh::SetSolidOrientation(int solid_orientation)
2142 {
2143   switch(solid_orientation)
2144   {
2145   case -1: // closed oriented manifold solid with inward face normals
2146     SetClosed(1);
2147     m_mesh_is_manifold = 1;
2148     m_mesh_is_oriented = 1;
2149     m_mesh_is_solid = 2;
2150     break;
2151 
2152   case  0: // not solid
2153     m_mesh_is_solid = 3;
2154     // DO NOT SET m_mesh_is_closed here.
2155     // Meshes can be closed but not solid
2156     break;
2157 
2158   case  1: // closed oriented manifold solid with outward face normals
2159     SetClosed(1);
2160     m_mesh_is_manifold = 1;
2161     m_mesh_is_oriented = 1;
2162     m_mesh_is_solid = 1;
2163     break;
2164 
2165   default:
2166     m_mesh_is_solid = 0;
2167     break;
2168   }
2169 }
2170 
2171 
2172 static
ON_MeshIsManifold_CompareV(const void * a,const void * b)2173 int ON_MeshIsManifold_CompareV( const void* a, const void* b )
2174 {
2175   return memcmp(a,b,sizeof(ON_3fPoint));
2176   /*
2177   float d;
2178   const float* fa = (const float*)a;
2179   const float* fb = (const float*)b;
2180   if ( 0.0f == (d = (*fa++ - *fb++)) )
2181   {
2182     if ( 0.0f == (d = (*fa++ - *fb++)) )
2183     {
2184       if ( 0.0f == (d = (*fa++ - *fb++)) )
2185         return 0;
2186     }
2187   }
2188   return ( d < 0.0f ) ? -1 : 1;
2189   */
2190 }
2191 
2192 static
ON_MeshGetVertexEdges_Compare2dex(const void * a,const void * b)2193 int ON_MeshGetVertexEdges_Compare2dex( const void* a, const void* b )
2194 {
2195   return ON_Compare2dex((const ON_2dex*)a,(const ON_2dex*)b);
2196 }
2197 
2198 static
ON_MeshIsManifold_Compare3dex(const void * a,const void * b)2199 int ON_MeshIsManifold_Compare3dex( const void* a, const void* b )
2200 {
2201   return ON_Compare3dex((const ON_3dex*)a,(const ON_3dex*)b);
2202 }
2203 
2204 //static
2205 //int ON_MeshGetVertexEdges_CompareInt( const int* a, const int* b )
2206 //{
2207 //  return (*a-*b);
2208 //}
2209 
2210 
GetVertexEdges(int vertex_index_count,const int * vertex_index,bool bNoDuplicates,ON_SimpleArray<ON_2dex> & edges) const2211 int ON_Mesh::GetVertexEdges(
2212   int vertex_index_count,
2213   const int* vertex_index,
2214   bool bNoDuplicates,
2215   ON_SimpleArray<ON_2dex>& edges
2216   ) const
2217 {
2218   // Get edges connected to vertices in vertex_index[] array.
2219   const int edges_count0 = edges.Count();
2220 
2221   const int mesh_vcount = m_V.Count();
2222 
2223   //03/12/2007 TimH. The line below appears to be a typo.  Using the following line works better.
2224   //const int mesh_fcount = m_V.Count();
2225   const int mesh_fcount = m_F.Count();
2226 
2227   if (   vertex_index_count <= 0 || 0 == vertex_index
2228       || mesh_fcount <= 0 || mesh_vcount < 3 )
2229   {
2230     return 0;
2231   }
2232 
2233   int vei, efi, fvi, ei, fi, j, n, vi;
2234   const int* f_vi;
2235   ON_2dex edge_ends;
2236   const ON_MeshFace* f = m_F.Array();
2237 
2238   if (   TopologyExists()
2239        && mesh_vcount == m_top.m_topv_map.Count()
2240        && m_top.m_tope.Count() > 0 )
2241   {
2242     // Topology looks good; use it to speed up the search.
2243     const int* topv_map = m_top.m_topv_map;
2244     const int top_vcount = m_top.m_topv.Count();
2245     const int top_ecount = m_top.m_tope.Count();
2246     int top_vi;
2247     for ( n = 0; n < vertex_index_count; n++ )
2248     {
2249       vi = vertex_index[n];
2250       if ( vi < 0 || vi >= mesh_vcount )
2251         continue;
2252       top_vi = topv_map[vi];
2253       if ( top_vi < 0 || top_vi > top_vcount )
2254         continue;
2255       edge_ends.i = vi;
2256       const ON_MeshTopologyVertex& v = m_top.m_topv[top_vi];
2257       for ( vei = 0; vei < v.m_tope_count; vei++ )
2258       {
2259         ei = v.m_topei[vei];
2260         if ( ei < 0 || ei >= top_ecount )
2261           continue;
2262         const ON_MeshTopologyEdge& e = m_top.m_tope[ei];
2263         for ( efi = 0; efi < e.m_topf_count; efi++ )
2264         {
2265           fi = e.m_topfi[efi];
2266           if ( fi < 0 || fi >= mesh_fcount )
2267             continue;
2268           f_vi = f[fi].vi;
2269           for ( fvi = 0; fvi < 4; fvi++ )
2270           {
2271             if ( f_vi[fvi] == vi )
2272             {
2273               j = f_vi[(fvi+3)%4];
2274               if ( j >= 0 && j < mesh_vcount && vi != j )
2275               {
2276                 edge_ends.i = j;
2277                 edge_ends.j = vi;
2278                 edges.Append(edge_ends);
2279               }
2280               j = f_vi[ (2==fvi && f_vi[2]==f_vi[3]) ? 0 : ((fvi+1)%4) ];
2281               if ( j >= 0 && j < mesh_vcount && vi != j )
2282               {
2283                 edge_ends.i = vi;
2284                 edge_ends.j = j;
2285                 edges.Append(edge_ends);
2286               }
2287               break; // done with this face
2288             }
2289           }
2290         }
2291       }
2292     }
2293   }
2294   else
2295   {
2296     // slow-n-stupid search through all the faces
2297 
2298     // Sort vertex_index[] array so we can use a quick
2299     // binary search to see if a face is using one of
2300     // the vertices in the list.
2301     ON_Workspace ws;
2302     for ( vi = 1; vi < vertex_index_count; vi++ )
2303     {
2304       if ( vertex_index[vi] < vertex_index[vi-1] )
2305       {
2306         // need to sort vertex_index[] array
2307         int* tmp = ws.GetIntMemory(vertex_index_count);
2308         memcpy(tmp,vertex_index,vertex_index_count*sizeof(tmp[0]));
2309         ON_SortIntArray(ON::quick_sort,tmp,vertex_index_count);
2310         vertex_index = tmp;
2311         break;
2312       }
2313     }
2314 
2315     // Find all the faces that use a vertex in the vertex_index[] array.
2316     for ( fi = 0; fi < mesh_fcount; fi++ )
2317     {
2318       f_vi = f[fi].vi;
2319       for ( fvi = 0; fvi < 4; fvi++ )
2320       {
2321         vi = f_vi[fvi];
2322         if ( ON_BinarySearchIntArray(vi,vertex_index,vertex_index_count) )
2323         {
2324           // vi is in the vertex_index[] array.  Add the edges
2325           // of this face that begin and end at this vertex.
2326           j = f_vi[(fvi+3)%4];
2327           if ( j >= 0 && j < mesh_vcount && vi != j )
2328           {
2329             edge_ends.i = j;
2330             edge_ends.j = vi;
2331             edges.Append(edge_ends);
2332           }
2333           j = f_vi[ (2==fvi && f_vi[2]==f_vi[3]) ? 0 : ((fvi+1)%4) ];
2334           if ( j >= 0 && j < mesh_vcount && vi != j )
2335           {
2336             edge_ends.i = vi;
2337             edge_ends.j = j;
2338             edges.Append(edge_ends);
2339           }
2340         }
2341       }
2342     }
2343   }
2344 
2345   if ( bNoDuplicates && edges.Count() > edges_count0 )
2346   {
2347     for ( ei = edges_count0; ei < edges.Count(); ei++ )
2348     {
2349       edge_ends = edges[ei];
2350       if ( edge_ends.i > edge_ends.j )
2351       {
2352         j = edge_ends.i; edge_ends.i = edge_ends.j; edge_ends.j = j;
2353       }
2354     }
2355     ON_qsort( edges.Array() + edges_count0,
2356               edges.Count() - edges_count0,
2357               sizeof(edge_ends),
2358               ON_MeshGetVertexEdges_Compare2dex);
2359     edge_ends = edges[edges_count0];
2360     for ( ei = j = edges_count0+1; ei < edges.Count(); ei++ )
2361     {
2362       if ( ON_Compare2dex(&edge_ends,&edges[ei]) )
2363       {
2364         edge_ends = edges[ei];
2365         if ( j != ei )
2366           edges[j] = edge_ends;
2367         j++;
2368       }
2369     }
2370     edges.SetCount(j);
2371   }
2372 
2373   return (edges.Count() - edges_count0);
2374 }
2375 
GetMeshEdges(ON_SimpleArray<ON_2dex> & edges) const2376 int ON_Mesh::GetMeshEdges(
2377   ON_SimpleArray<ON_2dex>& edges
2378   ) const
2379 {
2380   const int edges_count0 = edges.Count();
2381   int fi, ei, j, fvi;
2382   const int* f_vi;
2383   const ON_MeshFace* f = m_F.Array();
2384   const int mesh_vcount = m_V.Count();
2385   const int mesh_fcount = m_F.Count();
2386   edges.Reserve( edges_count0 + 4*mesh_fcount );
2387   ON_2dex e;
2388 
2389   // Find all the faces that use a vertex in the vertex_index[] array.
2390   for ( fi = 0; fi < mesh_fcount; fi++ )
2391   {
2392     f_vi = f[fi].vi;
2393     ei = f_vi[3];
2394     for ( fvi = 0; fvi < 4; fvi++ )
2395     {
2396       e.i = ei;
2397       ei = *f_vi++;
2398       e.j = ei;
2399       if ( e.i > e.j )
2400       {
2401         j = e.i; e.i = e.j; e.j = j;
2402       }
2403       if ( e.i != e.j && e.i >= 0 && e.j < mesh_vcount )
2404       {
2405         edges.Append(e);
2406       }
2407     }
2408   }
2409 
2410 
2411   if ( edges.Count() > edges_count0 )
2412   {
2413     ON_qsort( edges.Array() + edges_count0,
2414               edges.Count() - edges_count0,
2415               sizeof(e),
2416               ON_MeshGetVertexEdges_Compare2dex);
2417     e = edges[edges_count0];
2418     for ( ei = j = edges_count0+1; ei < edges.Count(); ei++ )
2419     {
2420       if ( ON_Compare2dex(&e,&edges[ei]) )
2421       {
2422         e = edges[ei];
2423         if ( j != ei )
2424           edges[j] = e;
2425         j++;
2426       }
2427     }
2428     edges.SetCount(j);
2429   }
2430 
2431   return edges.Count() - edges_count0;
2432 }
2433 
2434 
SolidOrientation() const2435 int ON_Mesh::SolidOrientation() const
2436 {
2437 
2438   if ( m_mesh_is_solid <= 0 || m_mesh_is_solid > 3 )
2439   {
2440     // NOTE: calling IsSolid() will set m_mesh_is_solid
2441     //       to 3 if mes is non-manifold
2442     IsSolid();
2443   }
2444 
2445   switch(m_mesh_is_solid)
2446   {
2447   case 1:
2448     return 1;
2449     break;
2450 
2451   case 2:
2452     return -1;
2453     break;
2454 
2455   case 3:
2456     return 0;
2457     break;
2458   }
2459 
2460   return 0; // answer "no" if we don't know.
2461 }
2462 
2463 
IsSolid() const2464 bool ON_Mesh::IsSolid() const
2465 {
2466   return ( IsClosed() && IsManifold() && IsOriented() );
2467 }
2468 
2469 
IsManifold(bool bTopologicalTest,bool * pbIsOriented,bool * pbHasBoundary) const2470 bool ON_Mesh::IsManifold(
2471   bool bTopologicalTest,
2472   bool* pbIsOriented,
2473   bool* pbHasBoundary
2474   ) const
2475 {
2476   bool bIsManifold = false;
2477   if ( pbIsOriented )
2478     *pbIsOriented = false;
2479   if ( pbHasBoundary )
2480     *pbHasBoundary = false;
2481   const int vcount = m_V.Count();
2482   const int fcount = m_F.Count();
2483   if ( vcount > 0 && fcount > 0 )
2484   {
2485     ON_Workspace ws;
2486     ON_3dex e;
2487     int i, j, ecount;
2488     const int* fvi;
2489     ON_3fPoint v0;
2490     const ON_3fPoint* v;
2491     const ON_MeshFace* f;
2492     int* vid = ws.GetIntMemory(vcount);
2493     ON_3dex* edge = (ON_3dex*)ws.GetMemory(4*fcount*sizeof(*edge));
2494 
2495     if ( bTopologicalTest )
2496     {
2497       // coincident vertices are assigned the same vertex id
2498       ON_Sort(ON::quick_sort,vid,m_V.Array(),vcount,sizeof(m_V[0]),ON_MeshIsManifold_CompareV);
2499       ecount = 0;
2500       v = m_V.Array();
2501       ecount = 0;
2502       j = vcount;
2503       for ( i = 0; i < vcount; i = j)
2504       {
2505         v0 = v[vid[i]];
2506         vid[i] = ecount;
2507         for ( j = i+1; j < vcount; j++ )
2508         {
2509           if ( ON_MeshIsManifold_CompareV(&v,v+vid[j]) )
2510           {
2511             ecount++;
2512             break;
2513           }
2514           vid[j] = ecount;
2515         }
2516       }
2517     }
2518     else
2519     {
2520       // each vertex gets a unique id.
2521       for ( i = 0; i < vcount; i++ )
2522         vid[i] = i;
2523     }
2524 
2525     // build a list of edges
2526     f = m_F.Array();
2527     ecount = 0;
2528     for ( i = 0; i < fcount; i++ )
2529     {
2530       fvi = (f++)->vi;
2531       if (   fvi[0] >= 0 && fvi[0] < vcount
2532           && fvi[1] >= 0 && fvi[1] < vcount
2533           && fvi[2] >= 0 && fvi[2] < vcount
2534           && fvi[3] >= 0 && fvi[3] < vcount )
2535       {
2536         // loop unrolled for speed
2537         j = ecount;
2538         e.i = vid[fvi[0]];  e.j = vid[fvi[1]];
2539         if ( 0 != (e.k = e.j - e.i) )
2540         {
2541           if ( e.k < 0 ) {e.k = e.i; e.i = e.j; e.j = e.k; e.k = 1;} else e.k = 0;
2542           edge[ecount++] = e;
2543         }
2544         e.i = vid[fvi[1]];  e.j = vid[fvi[2]];
2545         if ( 0 != (e.k = e.j - e.i) )
2546         {
2547           if ( e.k < 0 ) {e.k = e.i; e.i = e.j; e.j = e.k; e.k = 1;} else e.k = 0;
2548           edge[ecount++] = e;
2549         }
2550         e.i = vid[fvi[2]];  e.j = vid[fvi[3]];
2551         if ( 0 != (e.k = e.j - e.i) )
2552         {
2553           if ( e.k < 0 ) {e.k = e.i; e.i = e.j; e.j = e.k; e.k = 1;} else e.k = 0;
2554           edge[ecount++] = e;
2555         }
2556         e.i = vid[fvi[3]];  e.j = vid[fvi[0]];
2557         if ( 0 != (e.k = e.j - e.i) )
2558         {
2559           if ( e.k < 0 ) {e.k = e.i; e.i = e.j; e.j = e.k; e.k = 1;} else e.k = 0;
2560           edge[ecount++] = e;
2561         }
2562         if ( ecount < j+3 )
2563           ecount = j;
2564       }
2565     }
2566 
2567     if ( ecount >= 4 )
2568     {
2569       bIsManifold = true;
2570       bool bIsOriented  = (pbIsOriented)  ? true  : false;
2571       bool bHasBoundary = (pbHasBoundary) ? false : true;
2572       ON_qsort(edge,ecount,sizeof(edge[0]),ON_MeshIsManifold_Compare3dex);
2573 
2574       i = 0;
2575       e = *edge;
2576       while ( --ecount )
2577       {
2578         edge++;
2579         if ( memcmp(&e,edge,2*sizeof(int)) )
2580         {
2581           if (!i)
2582             bHasBoundary = true;
2583           e = *edge;
2584           i = 0;
2585         }
2586         else
2587         {
2588           if ( i++ )
2589           {
2590             bIsManifold = false;
2591             break;
2592           }
2593           if ( e.k == edge->k )
2594             bIsOriented = false;
2595         }
2596       }
2597 
2598       if ( bIsManifold )
2599       {
2600         if ( pbIsOriented )
2601           *pbIsOriented = bIsOriented;
2602         if ( pbHasBoundary )
2603           *pbHasBoundary = bHasBoundary;
2604       }
2605     }
2606   }
2607 
2608   return bIsManifold;
2609 }
2610 
ON_hsort_3dex(ON_3dex * e,size_t nel)2611 static void ON_hsort_3dex(ON_3dex *e, size_t nel)
2612 {
2613   // dictionary sort e[]
2614   size_t i_end,k,i,j;
2615   ON_3dex e_tmp;
2616 
2617   if (nel < 2) return;
2618   k = nel >> 1;
2619   i_end = nel-1;
2620   for (;;)
2621   {
2622     if (k)
2623     {
2624       --k;
2625       e_tmp = e[k];
2626     }
2627     else
2628     {
2629       e_tmp = e[i_end];
2630       e[i_end] = e[0];
2631       if (!(--i_end))
2632       {
2633         e[0] = e_tmp;
2634         break;
2635       }
2636     }
2637     i = k;
2638     j = (k<<1) + 1;
2639     while (j <= i_end)
2640     {
2641       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)) ) ) )
2642         j++;
2643 
2644       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) ) ) )
2645       {
2646         e[i] = e[j];
2647         i = j;
2648         j = (j<<1) + 1;
2649       }
2650       else
2651         j = i_end + 1;
2652     }
2653     e[i] = e_tmp;
2654   }
2655 }
2656 
ON_Mesh_SetClosedHelper(bool bClosedOnly,const ON_Mesh & mesh,const char & m_mesh_is_manifold,const char & m_mesh_is_oriented)2657 static void ON_Mesh_SetClosedHelper(
2658           bool bClosedOnly,
2659           const ON_Mesh& mesh,
2660           const char& m_mesh_is_manifold,
2661           const char& m_mesh_is_oriented
2662           )
2663 {
2664   // thread safe lazy evaluation for mesh's m_mesh_is_... flags
2665   // Sets: m_mesh_is_closed.
2666   //       If bClosedOnly is false, also sets m_mesh_is_manifold and m_mesh_is_oriented
2667   int is_closed = 0;
2668   char is_manifold = 2;
2669   char is_oriented = 2;
2670   for (;;)
2671   {
2672     const int Vcount = mesh.m_V.Count();
2673     const int Fcount = mesh.m_F.Count();
2674     if ( Vcount < 3 || Fcount < 1 )
2675     {
2676       ON_ERROR("Mesh is not valid.");
2677       break;
2678     }
2679     if ( bClosedOnly && (Vcount < 4 || Fcount < 4) )
2680     {
2681       // not closed - don't waste any more time.
2682       break;
2683     }
2684 
2685     int i, j;
2686     int Vidbuffer[256];
2687     int* Vid = mesh.GetVertexLocationIds(
2688                      1,
2689                      (Vcount*sizeof(*Vid) <= sizeof(Vidbuffer) ? &Vidbuffer[0] : 0),
2690                      0
2691                     );
2692     if ( 0 == Vid )
2693     {
2694       ON_ERROR("Mesh has corrupt vertex information.");
2695       bClosedOnly = false;
2696       break;
2697     }
2698 
2699     // build an edge list where the "vertex" indices identify unique 3d locations
2700     ON_3dex* E_list = (ON_3dex*)onmalloc(4*Fcount*sizeof(E_list[0]));
2701     ON_3dex E;
2702     int Vid0;
2703     const int* fvi;
2704     int E_count = 0;
2705     const ON_MeshFace* F = mesh.m_F.Array();
2706     for ( j = 0; j < Fcount; j++ )
2707     {
2708       fvi = F[j].vi;
2709       E.i = Vid[fvi[0]];
2710       Vid0 = E.j = Vid[fvi[1]];
2711       if ( E.i == E.j )
2712         break;
2713       if ( E.i > E.j )
2714       {
2715         i = E.i; E.i = E.j; E.j = i;
2716         E.k = 1;
2717       }
2718       else
2719       {
2720         E.k = 0;
2721       }
2722       E_list[E_count++] = E;
2723 
2724       E.i = Vid0;
2725       Vid0 = E.j = Vid[fvi[2]];
2726       if ( E.i == E.j )
2727         break;
2728       if ( E.i > E.j )
2729       {
2730         i = E.i; E.i = E.j; E.j = i;
2731         E.k = 1;
2732       }
2733       else
2734       {
2735         E.k = 0;
2736       }
2737       E_list[E_count++] = E;
2738 
2739       if ( fvi[2] != fvi[3] )
2740       {
2741         // quad
2742         E.i = Vid0;
2743         Vid0 = E.j = Vid[fvi[3]];
2744         if ( E.i == E.j )
2745           break;
2746         if ( E.i > E.j )
2747         {
2748           i = E.i; E.i = E.j; E.j = i;
2749           E.k = 1;
2750         }
2751         else
2752         {
2753           E.k = 0;
2754         }
2755         E_list[E_count++] = E;
2756       }
2757 
2758       E.i = Vid0;
2759       E.j = Vid[fvi[0]];
2760       if ( E.i == E.j )
2761         break;
2762       if ( E.i > E.j )
2763       {
2764         i = E.i; E.i = E.j; E.j = i;
2765         E.k = 1;
2766       }
2767       else
2768       {
2769         E.k = 0;
2770       }
2771       E_list[E_count++] = E;
2772     }
2773     if ( Vid != &Vidbuffer[0] )
2774       onfree(Vid);
2775 
2776     if ( E_count < 3 || j != Fcount )
2777     {
2778       ON_ERROR("Mesh is corrupt or collapsed");
2779       bClosedOnly = false;
2780       break;
2781     }
2782 
2783     // sort the the edges
2784     ON_hsort_3dex(E_list,E_count);
2785 
2786     // Look for duplicate edges.  If we find an edge with no duplicate,
2787     // then the mesh is open.  It is possible that degenerate meshes,
2788     // like a flattened box, will be flagged as closed.
2789     is_closed = (Fcount >= 4 && E_count >= 6) ? 1 : 0;
2790     is_oriented = 1;
2791     is_manifold = 1;
2792     i = -1;
2793     if ( !bClosedOnly || 1 == is_closed ) for ( i = 0; i < E_count; /*empty iterator*/ )
2794     {
2795       E = E_list[i];
2796       if ( ++i >= E_count )
2797       {
2798         // boundary edge (and the last edge in our list)
2799         is_closed = 0;
2800         break;
2801       }
2802 
2803       if ( E.i != E_list[i].i || E.j != E_list[i].j )
2804       {
2805         // boundary edge
2806         is_closed = 0;
2807         if ( 2 == is_oriented && 2 == is_manifold )
2808         {
2809           bClosedOnly = false;
2810           break;
2811         }
2812         if ( bClosedOnly )
2813           break; // don't spend time with further testing
2814         continue;
2815       }
2816 
2817       if ( E.k == E_list[i].k )
2818       {
2819         // opposite face normals along this edge - mesh is not oriented
2820         is_oriented = 2;
2821       }
2822 
2823       if ( ++i >= E_count || E.i != E_list[i].i || E.j != E_list[i].j )
2824       {
2825         // two faces share this edge
2826         continue;
2827       }
2828 
2829       // three or more faces share this edge - mesh is not oriented manifold
2830       is_oriented = 2;
2831       is_manifold = 2;
2832       if ( 0 == is_closed )
2833       {
2834         bClosedOnly = false;
2835         break;
2836       }
2837 
2838       // Check for more faces sharing this edge.
2839       for ( i++; i < E_count; i++ )
2840       {
2841         if ( E.i != E_list[i].i || E.j != E_list[i].j )
2842         {
2843           // the edges E and Eid_list[i] are in different locations
2844           break;
2845         }
2846       }
2847     }
2848     if ( i >= E_count )
2849     {
2850       // is_manifold and is_oriented are set correctly
2851       bClosedOnly = false;
2852     }
2853 
2854     onfree(E_list);
2855 
2856     break;
2857   }
2858 
2859   const_cast<ON_Mesh&>(mesh).SetClosed(is_closed);
2860   if ( !bClosedOnly )
2861   {
2862     // is_manifold and is_oriented are set correctly
2863     if ( 2 == is_manifold )
2864       is_oriented = 2;
2865     const_cast<char&>(m_mesh_is_manifold) = is_manifold;
2866     const_cast<char&>(m_mesh_is_oriented) = is_oriented;
2867   }
2868 }
2869 
IsClosed() const2870 bool ON_Mesh::IsClosed() const
2871 {
2872   if ( m_mesh_is_closed <= 0 || m_mesh_is_closed > 2)
2873   {
2874     // thread safe lazy evaluation
2875     ON_Mesh_SetClosedHelper( true, *this, m_mesh_is_manifold, m_mesh_is_oriented );
2876   }
2877 
2878   return (1 == m_mesh_is_closed);
2879 }
2880 
IsManifold() const2881 bool ON_Mesh::IsManifold() const
2882 {
2883   if ( m_mesh_is_manifold <= 0 || m_mesh_is_manifold > 2 )
2884   {
2885     // thread safe lazy evaluation
2886     ON_Mesh_SetClosedHelper( false, *this, m_mesh_is_manifold, m_mesh_is_oriented );
2887   }
2888   return (1 == m_mesh_is_manifold);
2889 }
2890 
IsOriented() const2891 bool ON_Mesh::IsOriented() const
2892 {
2893   if ( m_mesh_is_oriented <= 0 || m_mesh_is_oriented > 2 )
2894   {
2895     // thread safe lazy evaluation
2896     ON_Mesh_SetClosedHelper( false, *this, m_mesh_is_manifold, m_mesh_is_oriented );
2897   }
2898   return (1 == m_mesh_is_oriented);
2899 }
2900 
2901 
SetVertex(int vertex_index,const ON_3dPoint & vertex_location)2902 bool ON_Mesh::SetVertex(
2903        int vertex_index,
2904        const ON_3dPoint& vertex_location
2905        )
2906 {
2907   bool rc = false;
2908   int vertex_count = m_V.Count();
2909   if ( vertex_index >= 0 && vertex_index <= vertex_count )
2910   {
2911     bool bIsValid_fV = false;
2912     if ( HasDoublePrecisionVertices() )
2913     {
2914       bIsValid_fV = SinglePrecisionVerticesAreValid();
2915       ON_3dPointArray& dV = DoublePrecisionVertices();
2916       if ( vertex_count == dV.Count() )
2917       {
2918         bool bIsValid_dV = DoublePrecisionVerticesAreValid();
2919         if ( vertex_index < vertex_count )
2920           dV[vertex_index] = vertex_location;
2921         else
2922           dV.Append(vertex_location);
2923         if ( bIsValid_dV )
2924           SetDoublePrecisionVerticesAsValid();
2925       }
2926     }
2927     if ( vertex_index < vertex_count )
2928       m_V[vertex_index] = vertex_location;
2929     else
2930       m_V.Append(vertex_location);
2931     if ( bIsValid_fV )
2932       SetSinglePrecisionVerticesAsValid();
2933     rc = true;
2934   }
2935   return rc;
2936 }
2937 
SetVertex(int vertex_index,const ON_3fPoint & vertex_location)2938 bool ON_Mesh::SetVertex(
2939        int vertex_index,
2940        const ON_3fPoint& vertex_location
2941        )
2942 {
2943   bool rc = false;
2944   int vertex_count = m_V.Count();
2945   if ( vertex_index >= 0 && vertex_index <= vertex_count )
2946   {
2947     bool bIsValid_fV = false;
2948     if ( HasDoublePrecisionVertices() )
2949     {
2950       bIsValid_fV = SinglePrecisionVerticesAreValid();
2951       ON_3dPointArray& dV = DoublePrecisionVertices();
2952       if ( vertex_count == dV.Count() )
2953       {
2954         bool bIsValid_dV = DoublePrecisionVerticesAreValid();
2955         if ( vertex_index < vertex_count )
2956           dV[vertex_index] = vertex_location;
2957         else
2958           dV.Append(vertex_location);
2959         if ( bIsValid_dV )
2960           SetDoublePrecisionVerticesAsValid();
2961       }
2962     }
2963     if ( vertex_index < vertex_count )
2964       m_V[vertex_index] = vertex_location;
2965     else
2966       m_V.Append(vertex_location);
2967     if ( bIsValid_fV )
2968       SetSinglePrecisionVerticesAsValid();
2969     rc = true;
2970   }
2971   return rc;
2972 }
2973 
SetVertexNormal(int vertex_index,const ON_3dVector & normal)2974 bool ON_Mesh::SetVertexNormal(
2975        int vertex_index,
2976        const ON_3dVector& normal
2977        )
2978 {
2979   bool rc = false;
2980   // use double precision for unitizing normal
2981   ON_3dVector unit_vector = normal;
2982   const bool bUnitVector = unit_vector.Unitize();
2983   ON_3fVector v((float)unit_vector.x, (float)unit_vector.y, (float)unit_vector.z);
2984   int normal_count = m_N.Count();
2985   if ( vertex_index >= 0 ) {
2986     if ( vertex_index < normal_count ) {
2987       m_N[vertex_index] = v;
2988       rc = bUnitVector;
2989     }
2990     else if ( vertex_index == normal_count ) {
2991       m_N.Append(v);
2992       rc = bUnitVector;
2993     }
2994   }
2995   return rc;
2996 }
2997 
SetVertexNormal(int vertex_index,const ON_3fVector & normal)2998 bool ON_Mesh::SetVertexNormal(
2999        int vertex_index,
3000        const ON_3fVector& normal
3001        )
3002 {
3003   ON_3dVector v(normal.x,normal.y,normal.z);
3004   return SetVertexNormal(vertex_index,v);
3005 }
3006 
SetTextureCoord(int vertex_index,double s,double t)3007 bool ON_Mesh::SetTextureCoord(
3008        int vertex_index,
3009        double s, double t    // texture coordinates
3010        )
3011 {
3012   ON_2fPoint tc((float)s,(float)t);
3013   bool rc = false;
3014   int vertex_count = m_T.Count();
3015   if ( vertex_index >= 0 ) {
3016     if ( vertex_index < vertex_count ) {
3017       m_T[vertex_index] = tc;
3018       rc = true;
3019     }
3020     else if ( vertex_index == vertex_count ) {
3021       m_T.Append(tc);
3022       rc = true;
3023     }
3024   }
3025   return rc;
3026 }
3027 
3028 
SetTriangle(int face_index,int a,int b,int c)3029 bool ON_Mesh::SetTriangle(
3030        int face_index,
3031        int a,int b ,int c // vertex indices
3032        )
3033 {
3034   return SetQuad( face_index, a,b,c,c );
3035 }
3036 
SetQuad(int face_index,int a,int b,int c,int d)3037 bool ON_Mesh::SetQuad(
3038        int face_index,
3039        int a, int b, int c, int d // vertex indices
3040        )
3041 {
3042   bool rc = false;
3043   int face_count = m_F.Count();
3044   if ( face_index >= 0 ) {
3045     ON_MeshFace f;
3046     f.vi[0] = a;
3047     f.vi[1] = b;
3048     f.vi[2] = c;
3049     f.vi[3] = d;
3050     if ( face_index < face_count ) {
3051       m_F[face_index] = f;
3052       rc = true;
3053     }
3054     else if ( face_index == face_count ) {
3055       m_F.Append(f);
3056       rc = true;
3057     }
3058     if ( rc )
3059       rc = f.IsValid(m_V.Count());
3060   }
3061   return rc;
3062 }
3063 
3064 
3065 
FaceCount() const3066 int ON_Mesh::FaceCount() const
3067 {
3068   return m_F.Count();
3069 }
3070 
QuadCount() const3071 int ON_Mesh::QuadCount() const
3072 {
3073   // number of faces that are quads
3074   if (   m_quad_count     < 0
3075       || m_triangle_count < 0
3076       || m_invalid_count  < 0
3077       || m_quad_count + m_triangle_count + m_invalid_count != FaceCount() )
3078   {
3079     const_cast<ON_Mesh*>(this)->CountQuads();
3080   }
3081   return m_quad_count;
3082 }
3083 
TriangleCount() const3084 int ON_Mesh::TriangleCount() const
3085 {
3086   // number of faces that are triangles
3087   QuadCount(); // makes sure counts are valid
3088   return m_triangle_count;
3089 }
3090 
InvalidFaceCount() const3091 int ON_Mesh::InvalidFaceCount() const
3092 {
3093   // number of faces that are invalid
3094   QuadCount(); // makes sure counts are valid
3095   return m_invalid_count;
3096 }
3097 
3098 
VertexCount() const3099 int ON_Mesh::VertexCount() const
3100 {
3101   return m_V.Count();
3102 }
3103 
HasVertexNormals() const3104 bool ON_Mesh::HasVertexNormals() const
3105 {
3106   const int vertex_count = VertexCount();
3107   return ( vertex_count > 0 && m_N.Count() == vertex_count ) ? true : false;
3108 }
3109 
HasFaceNormals() const3110 bool ON_Mesh::HasFaceNormals() const
3111 {
3112   const int face_count = FaceCount();
3113   return ( face_count > 0 && m_FN.Count() == face_count ) ? true : false;
3114 }
3115 
HasTextureCoordinates() const3116 bool ON_Mesh::HasTextureCoordinates() const
3117 {
3118   const int vertex_count = VertexCount();
3119   return ( vertex_count > 0 && m_T.Count() == vertex_count ) ? true : false;
3120 }
3121 
HasCachedTextureCoordinates() const3122 bool ON_Mesh::HasCachedTextureCoordinates() const
3123 {
3124   const int vertex_count = VertexCount();
3125   if (vertex_count > 0 )
3126   {
3127     int tci, tccount = m_TC.Count();
3128     for ( tci = 0; tci < tccount; tci++ )
3129     {
3130       if ( vertex_count == m_TC[tci].m_T.Count() )
3131         return true;
3132     }
3133   }
3134   return false;
3135 }
3136 
3137 const ON_TextureCoordinates*
CachedTextureCoordinates(const ON_UUID & mapping_id) const3138 ON_Mesh::CachedTextureCoordinates( const ON_UUID& mapping_id ) const
3139 {
3140   const int vertex_count = VertexCount();
3141   if (vertex_count > 0 )
3142   {
3143     const ON_TextureCoordinates* TC = m_TC.Array();
3144     int tci, tccount = m_TC.Count();
3145     for ( tci = 0; tci < tccount; tci++ )
3146     {
3147       if (   vertex_count == TC->m_T.Count()
3148           && mapping_id == TC->m_tag.m_mapping_id )
3149       {
3150         return TC;
3151       }
3152     }
3153   }
3154   return 0;
3155 }
3156 
HasSurfaceParameters() const3157 bool ON_Mesh::HasSurfaceParameters() const
3158 {
3159   const int vertex_count = VertexCount();
3160   return ( vertex_count > 0 && m_S.Count() == vertex_count ) ? true : false;
3161 }
3162 
HasPrincipalCurvatures() const3163 bool ON_Mesh::HasPrincipalCurvatures() const
3164 {
3165   const int vertex_count = VertexCount();
3166   return ( vertex_count > 0 && m_K.Count() == vertex_count ) ? true : false;
3167 }
3168 
HasVertexColors() const3169 bool ON_Mesh::HasVertexColors() const
3170 {
3171   const int vertex_count = VertexCount();
3172   return ( vertex_count > 0 && m_C.Count() == vertex_count ) ? true : false;
3173 }
3174 
InvalidateBoundingBoxes()3175 void ON_Mesh::InvalidateBoundingBoxes()
3176 {
3177   InvalidateVertexBoundingBox();
3178   InvalidateVertexNormalBoundingBox();
3179   InvalidateTextureCoordinateBoundingBox();
3180   InvalidateCurvatureStats();
3181 }
3182 
InvalidateVertexBoundingBox()3183 void ON_Mesh::InvalidateVertexBoundingBox()
3184 {
3185   m_vbox[0][0] = m_vbox[0][1] = m_vbox[0][2] =  1.0;
3186   m_vbox[1][0] = m_vbox[1][1] = m_vbox[1][2] = -1.0;
3187 }
3188 
InvalidateVertexNormalBoundingBox()3189 void ON_Mesh::InvalidateVertexNormalBoundingBox()
3190 {
3191   m_nbox[0][0] = m_nbox[0][1] = m_nbox[0][2] =  1.0;
3192   m_nbox[1][0] = m_nbox[1][1] = m_nbox[1][2] = -1.0;
3193 }
3194 
InvalidateTextureCoordinateBoundingBox()3195 void ON_Mesh::InvalidateTextureCoordinateBoundingBox()
3196 {
3197   m_tbox[0][0] = m_tbox[0][1] =  1.0;
3198   m_tbox[1][0] = m_tbox[1][1] = -1.0;
3199 }
3200 
InvalidateCurvatureStats()3201 void ON_Mesh::InvalidateCurvatureStats()
3202 {
3203   int i;
3204   for ( i = 0; i < 4; i++ ) {
3205     if ( m_kstat[i] ) {
3206       delete m_kstat[i];
3207       m_kstat[i] = 0;
3208     }
3209   }
3210 }
3211 
UnitizeVertexNormals()3212 bool ON_Mesh::UnitizeVertexNormals()
3213 {
3214   bool rc = HasVertexNormals();
3215   if ( rc ) {
3216     const int vertex_count = VertexCount();
3217     float* n = &m_N[0][0];
3218     int i;
3219     ON_3dVector N;
3220     for ( i = 0; i < vertex_count; i++ ) {
3221       N.x = n[0];
3222       N.y = n[1];
3223       N.z = n[2];
3224       if ( !N.Unitize() )
3225         rc = false;
3226       *n++ = (float)N.x;
3227       *n++ = (float)N.y;
3228       *n++ = (float)N.z;
3229     }
3230   }
3231   return rc;
3232 }
3233 
UnitizeFaceNormals()3234 bool ON_Mesh::UnitizeFaceNormals()
3235 {
3236   bool rc = HasFaceNormals();
3237   if ( rc ) {
3238     const int face_count = FaceCount();
3239     float* n = &m_FN[0][0];
3240     int i;
3241     ON_3dVector N;
3242     for ( i = 0; i < face_count; i++ ) {
3243       N.x = n[0];
3244       N.y = n[1];
3245       N.z = n[2];
3246       if ( !N.Unitize() )
3247         rc = false;
3248       *n++ = (float)N.x;
3249       *n++ = (float)N.y;
3250       *n++ = (float)N.z;
3251     }
3252   }
3253   return rc;
3254 }
3255 
3256 
GetCurvatureStats(ON::curvature_style kappa_style,ON_MeshCurvatureStats & stats) const3257 bool ON_Mesh::GetCurvatureStats( // returns true if successful
3258        ON::curvature_style kappa_style,
3259        ON_MeshCurvatureStats& stats
3260        ) const
3261 {
3262   bool rc = false;
3263   stats.Destroy();
3264   int ksi;
3265   switch ( kappa_style ) {
3266     case ON::gaussian_curvature:
3267       ksi = 0;
3268       break;
3269     case ON::mean_curvature:
3270       ksi = 1;
3271       break;
3272     case ON::min_curvature: // minimum unsigned radius of curvature
3273       ksi = 2;
3274       break;
3275     case ON::max_curvature: // maximum unsigned radius of curvature
3276       ksi = 3;
3277       break;
3278     //case ON::section_curvature_x:
3279     //  ksi = 4;
3280     //  break;
3281     //case ON::section_curvature_y:
3282     //  ksi = 5;
3283     //  break;
3284     //case ON::section_curvature_z:
3285     //  ksi = 6;
3286     //  break;
3287     default:
3288       ksi = -1;
3289       break;
3290   }
3291   if ( ksi >= 0 && ksi <= 3 && HasPrincipalCurvatures() ) {
3292     ON_Mesh* p = (ON_Mesh*)this; // const lie
3293     if ( !m_kstat[ksi] ) {
3294       p->m_kstat[ksi] = new ON_MeshCurvatureStats();
3295       p->m_kstat[ksi]->Set( kappa_style, m_K.Count(), m_K.Array(), m_N.Array() );
3296     }
3297     if ( p->m_kstat[ksi] ) {
3298       stats = *p->m_kstat[ksi];
3299       rc = true;
3300     }
3301   }
3302   return rc;
3303 }
3304 
WaitUntilReady(int sleep_value) const3305 int ON_MeshTopology::WaitUntilReady(int sleep_value) const
3306 {
3307   return m_b32IsValid;
3308 }
3309 
3310 
TopologyExists() const3311 bool ON_Mesh::TopologyExists() const
3312 {
3313   return (1 == m_top.WaitUntilReady(0));
3314 }
3315 
Topology() const3316 const ON_MeshTopology& ON_Mesh::Topology() const
3317 {
3318   int top_b32IsValid =  m_top.WaitUntilReady(-1);
3319 
3320   if ( 0 == top_b32IsValid )
3321   {
3322     ON_MeshTopology& top = const_cast<ON_MeshTopology&>(m_top);
3323     top.m_mesh = this;
3324     top_b32IsValid = top.Create() ? 1 : 0;
3325     top.m_b32IsValid = top_b32IsValid;
3326   }
3327 
3328   return m_top;
3329 }
3330 
DestroyTopology()3331 void ON_Mesh::DestroyTopology()
3332 {
3333   m_top.Destroy();
3334 }
3335 
3336 bool
IsTriangle() const3337 ON_MeshFace::IsTriangle() const
3338 {
3339   return vi[2]==vi[3];
3340 }
3341 
3342 bool
IsQuad() const3343 ON_MeshFace::IsQuad() const
3344 {
3345   return vi[2]!=vi[3];
3346 }
3347 
3348 void
Flip()3349 ON_MeshFace::Flip()
3350 {
3351   int i;
3352   if ( vi[2] == vi[3] ) {
3353     i = vi[1];
3354     vi[1] = vi[2];
3355     vi[2] = i;
3356     vi[3] = i;
3357   }
3358   else {
3359     i = vi[1];
3360     vi[1] = vi[3];
3361     vi[3] = i;
3362   }
3363 }
3364 
3365 /*
3366 ON_MeshEdge::ON_MeshEdge() : m_fcount(0)
3367 {
3368   m_fi = m_private_fi;
3369   m_private_fi[0] = 0;
3370   m_private_fi[0] = 0;
3371 }
3372 
3373 ON_MeshEdge::~ON_MeshEdge()
3374 {
3375   if ( m_fi && m_fi != m_private_fi ) onfree((void*)m_fi);
3376 }
3377 
3378 ON_MeshEdge::ON_MeshEdge( const ON_MeshEdge& src ) : m_fcount(0), m_fi(0)
3379 {
3380   *this = src;
3381 }
3382 
3383 int ON_MeshEdge::FaceIndexCount() const
3384 {
3385   return m_fcount;
3386 }
3387 
3388 const int* ON_MeshEdge::FaceIndexArray() const
3389 {
3390   return m_fi;
3391 }
3392 
3393 int& ON_MeshEdge::operator[](int i)
3394 {
3395   return (i>=0&&i<m_fcount) ? m_fi[i] : m_private_fi[0];
3396 }
3397 
3398 int ON_MeshEdge::operator[](int i) const
3399 {
3400   return (i>=0&&i<m_fcount) ? m_fi[i] : m_private_fi[0];
3401 }
3402 */
3403 
3404 void
Flip()3405 ON_Mesh::Flip()
3406 {
3407   FlipFaceOrientation();
3408   FlipFaceNormals();
3409   FlipVertexNormals();
3410 
3411   // Do not modify m_S[] or m_T[]
3412   // values here.
3413 }
3414 
3415 void
FlipVertexNormals()3416 ON_Mesh::FlipVertexNormals()
3417 {
3418   int i;
3419   const int vcount = VertexCount();
3420   if ( HasVertexNormals() ) {
3421     for ( i = 0; i < vcount; i++ ) {
3422       m_N[i].Reverse();
3423     }
3424   }
3425 }
3426 
3427 void
FlipFaceNormals()3428 ON_Mesh::FlipFaceNormals()
3429 {
3430   int i;
3431   const int fcount = FaceCount();
3432   if ( HasFaceNormals() ) {
3433     for( i = 0; i < fcount; i++ ) {
3434       m_FN[i].Reverse();
3435     }
3436   }
3437 }
3438 
3439 void
FlipFaceOrientation()3440 ON_Mesh::FlipFaceOrientation()
3441 {
3442   int i;
3443   const int fcount = FaceCount();
3444   for( i = 0; i < fcount; i++ ) {
3445     m_F[i].Flip();
3446   }
3447   if ( fcount > 0 )
3448     DestroyTopology(); // flipping changes order of face corners
3449 }
3450 
ComputeFaceNormal(const ON_3dPoint * dV,ON_3dVector & FN) const3451 bool ON_MeshFace::ComputeFaceNormal( const ON_3dPoint* dV, ON_3dVector& FN ) const
3452 {
3453   if ( 0 != dV )
3454   {
3455     ON_3dVector a = dV[vi[2]] - dV[vi[0]];
3456     ON_3dVector b = dV[vi[3]] - dV[vi[1]];
3457     FN = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads
3458     if ( FN.Unitize() )
3459       return true;
3460   }
3461 
3462   FN.Zero();
3463   return false;
3464 }
3465 
ComputeFaceNormal(const ON_3fPoint * fV,ON_3dVector & FN) const3466 bool ON_MeshFace::ComputeFaceNormal( const ON_3fPoint* fV, ON_3dVector& FN ) const
3467 {
3468   if ( 0 != fV )
3469   {
3470     ON_3dVector a = fV[vi[2]] - fV[vi[0]];
3471     ON_3dVector b = fV[vi[3]] - fV[vi[1]];
3472     FN = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads
3473     if ( FN.Unitize() )
3474       return true;
3475   }
3476 
3477   FN.Zero();
3478   return false;
3479 }
3480 
3481 bool
ComputeFaceNormal(int fi)3482 ON_Mesh::ComputeFaceNormal( int fi )
3483 {
3484   if ( fi < 0 )
3485     return false;
3486   if ( fi >= m_F.Count() )
3487     return false;
3488   if ( m_FN.Count() != m_F.Count() )
3489     return false;
3490 
3491   ON_3dVector FN;
3492   bool rc = ( HasDoublePrecisionVertices() )
3493           ? m_F[fi].ComputeFaceNormal(DoublePrecisionVertices().Array(),FN)
3494           : m_F[fi].ComputeFaceNormal(m_V.Array(),FN);
3495 
3496   m_FN[fi] = FN;
3497 
3498   return rc;
3499 }
3500 
3501 bool
ComputeFaceNormals()3502 ON_Mesh::ComputeFaceNormals()
3503 {
3504   bool rc = false;
3505   const int fcount = FaceCount();
3506   if ( fcount > 0 )
3507   {
3508     ON_3dVector a, b, n;
3509     int fi;
3510     const int* vi;
3511     if ( m_FN.Capacity() < fcount )
3512       m_FN.SetCapacity(fcount);
3513     m_FN.SetCount(0);
3514     rc = true;
3515     if ( HasDoublePrecisionVertices() && DoublePrecisionVerticesAreValid() )
3516     {
3517       const ON_3dPointArray& dV = DoublePrecisionVertices();
3518       for ( fi = 0; fi < fcount; fi++ ) {
3519         vi = m_F[fi].vi;
3520         a = dV[vi[2]] - dV[vi[0]];
3521         b = dV[vi[3]] - dV[vi[1]];
3522         n = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads
3523         n.Unitize();
3524         m_FN.Append(n);
3525       }
3526     }
3527     else
3528     {
3529       for ( fi = 0; fi < fcount; fi++ ) {
3530         vi = m_F[fi].vi;
3531         a = m_V[vi[2]] - m_V[vi[0]];
3532         b = m_V[vi[3]] - m_V[vi[1]];
3533         n = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads
3534         n.Unitize();
3535         m_FN.Append(n);
3536       }
3537     }
3538   }
3539   else
3540   {
3541     m_FN.Destroy();
3542   }
3543   return rc;
3544 }
3545 
3546 
3547 //static int compareRadial3fPoint( const ON_3fPoint* a, const ON_3fPoint* b )
3548 //{
3549 //  double ar = a->x+a->y+a->z;
3550 //  double br = b->x+b->y+b->z;
3551 //  if ( ar < br )
3552 //    return -1;
3553 //  if ( ar > br )
3554 //    return 1;
3555 //  return 0;
3556 //}
3557 
CombineCoincidentVertices(const ON_3fVector tolerance,double cos_normal_angle)3558 bool ON_Mesh::CombineCoincidentVertices(
3559         const ON_3fVector tolerance,
3560         double cos_normal_angle // = -1.0  // cosine(break angle) -1.0 will merge all coincident vertices
3561         )
3562 {
3563   // TODO - If you need this function, please ask Dale Lear to finish it.
3564   //bool rc = false;
3565   //const int vcount = VertexCount();
3566   //if ( vcount > 0 && rc ) {
3567   //  ON_Workspace ws;
3568   //  int* index = ws.GetIntMemory(vcount);
3569   //  rc = m_V.Sort( ON::quick_sort, index, compareRadial3fPoint );
3570   //  int i, j;
3571   //  ON_3fPoint p0, p1, pmin, pmax;
3572   //  for ( i = 0; i < vcount; i++ ) {
3573   //    p0 = m_V[i];
3574   //    pmin = p0 - tolerance;
3575   //    pmax = p0 + tolerance;
3576   //    for ( j = i+1; j < vcount; j++ ) {
3577   //      p1 = m_V[j];
3578   //      // TODO
3579   //    }
3580   //  }
3581   //}
3582   return false;
3583 }
3584 
3585 
3586 struct tagMESHPOINTS
3587 {
3588   // p0 = bogus pointer - never dereferenced - that is used
3589   //      to calculate vertex index in CompareMeshPoint().
3590   const char* p0;
3591   ON_3fPoint*  V;
3592   ON_2fPoint*  T;
3593   ON_3fVector* N;
3594   ON_SurfaceCurvature* K;
3595   ON_Color* C;
3596 };
3597 
CompareMeshPoint(const void * a,const void * b,void * ptr)3598 static int CompareMeshPoint(const void* a,const void* b,void* ptr)
3599 {
3600   float d;
3601   const struct tagMESHPOINTS * mp = (const struct tagMESHPOINTS *)ptr;
3602 
3603   // use bogus pointer to convert a,b into vertex indices
3604   int i = (int)(((const char*)a) - mp->p0); // the (int) is for 64 bit size_t conversion
3605   int j = (int)(((const char*)b) - mp->p0);
3606 
3607   d = mp->V[j].x - mp->V[i].x;
3608   if ( d == 0.0f )
3609   {
3610     d = mp->V[j].y - mp->V[i].y;
3611     if ( d == 0.0f )
3612     {
3613       d = mp->V[j].z - mp->V[i].z;
3614 
3615       //if ( d == 0.0f )
3616       //  return 0;
3617 
3618       if ( d == 0.0f && 0 != mp->N)
3619       {
3620         d = mp->N[j].x - mp->N[i].x;
3621         if ( d == 0.0f )
3622         {
3623           d = mp->N[j].y - mp->N[i].y;
3624           if ( d == 0.0f )
3625           {
3626             d = mp->N[j].z - mp->N[i].z;
3627           }
3628         }
3629       }
3630 
3631       if ( d == 0.0f && 0 != mp->T)
3632       {
3633         d = mp->T[j].x - mp->T[i].x;
3634         if ( d == 0.0f )
3635         {
3636           d = mp->T[j].y - mp->T[i].y;
3637         }
3638       }
3639 
3640       if ( d == 0.0f && 0 != mp->C )
3641       {
3642         int u = ((int)mp->C[j])-((int)mp->C[i]);
3643         if ( u < 0 )
3644           d = -1.0f;
3645         else if ( u > 0 )
3646           d = 1.0f;
3647       }
3648 
3649       if ( d == 0.0f && 0 != mp->K )
3650       {
3651         double dk = mp->K[j].k1 - mp->K[i].k1;
3652         if ( dk < 0.0 )
3653           d = -1.0;
3654         else if ( dk > 0.0 )
3655           d = 1.0;
3656         else
3657         {
3658           dk = mp->K[j].k2 - mp->K[i].k2;
3659           if ( dk < 0.0 )
3660             d = -1.0;
3661           else if ( dk > 0.0 )
3662             d = 1.0;
3663         }
3664       }
3665     }
3666   }
3667 
3668   if ( d < 0.0f )
3669     return -1;
3670   if ( d > 0.0f )
3671     return 1;
3672   return 0;
3673 }
3674 
CombineIdenticalVertices(bool bIgnoreVertexNormals,bool bIgnoreTextureCoordinates)3675 bool ON_Mesh::CombineIdenticalVertices(
3676                                 bool bIgnoreVertexNormals,
3677                                 bool bIgnoreTextureCoordinates
3678                                 )
3679 {
3680   // 11 June 2003 - added and tested.
3681   bool rc = false;
3682   ON_Mesh& mesh = *this;
3683 
3684   int vertex_count = mesh.VertexCount();
3685   if ( vertex_count > 0 )
3686   {
3687     ON_SimpleArray<int> index_array(vertex_count);
3688     ON_SimpleArray<int> remap_array(vertex_count);
3689 
3690     int remap_vertex_count = 0;
3691     int merge_count = 0;
3692     int i0, i1, k;
3693 
3694     struct tagMESHPOINTS mp;
3695     memset(&mp,0,sizeof(mp));
3696     mp.p0 = (const char*)&mp; // bogus pointer - never dereferenced
3697     mp.V = mesh.m_V.Array();
3698     mp.N = mesh.HasVertexNormals()       ? mesh.m_N.Array() : 0;
3699     mp.T = mesh.HasTextureCoordinates()  ? mesh.m_T.Array() : 0;
3700     mp.C = mesh.HasVertexColors()        ? mesh.m_C.Array() : 0;
3701     mp.K = mesh.HasPrincipalCurvatures() ? mesh.m_K.Array() : 0;
3702 
3703     if ( bIgnoreVertexNormals )
3704     {
3705       mp.N = 0;
3706     }
3707 
3708     if ( bIgnoreTextureCoordinates )
3709     {
3710       mp.T = 0;
3711       mp.C = 0;
3712       mp.K = 0;
3713     }
3714 
3715     index_array.SetCount(vertex_count);
3716     index_array.Zero();
3717     int* index = index_array.Array();
3718 
3719     remap_array.SetCount(vertex_count);
3720     int* remap = remap_array.Array();
3721     for ( k = 0; k < vertex_count; k++ )
3722       remap[k] = -1;
3723 
3724     ON_Sort(
3725           ON::quick_sort,
3726           index,
3727           mp.p0,                 // data buffer
3728           vertex_count,
3729           sizeof(*mp.p0),
3730           CompareMeshPoint,
3731           &mp
3732          );
3733 
3734     for ( i0 = 0; i0 < vertex_count; i0 = i1 )
3735     {
3736       for ( i1 = i0+1; i1 < vertex_count; i1++ )
3737       {
3738         if ( CompareMeshPoint( mp.p0+index[i0], mp.p0+index[i1], &mp ) )
3739           break;
3740         else
3741           merge_count++;
3742       }
3743       for ( /*empty*/; i0 < i1; i0++ )
3744       {
3745         remap[index[i0]] = remap_vertex_count;
3746       }
3747       remap_vertex_count++;
3748     }
3749 
3750     if ( bIgnoreVertexNormals )
3751     {
3752       mp.N = mesh.HasVertexNormals() ? mesh.m_N.Array() : 0;
3753     }
3754 
3755     if ( bIgnoreTextureCoordinates )
3756     {
3757       mp.T = mesh.HasTextureCoordinates()  ? mesh.m_T.Array() : 0;
3758       mp.C = mesh.HasVertexColors()        ? mesh.m_C.Array() : 0;
3759       mp.K = mesh.HasPrincipalCurvatures() ? mesh.m_K.Array() : 0;
3760     }
3761 
3762     if ( remap_vertex_count > 0 && remap_vertex_count < vertex_count )
3763     {
3764       ON_SimpleArray<ON_3fPoint> p_array(remap_vertex_count);
3765       p_array.SetCount(remap_vertex_count);
3766       ON_3fPoint* p = p_array.Array();
3767       ON_3fVector* v = (ON_3fVector*)p;
3768 
3769       for ( k = 0; k < vertex_count; k++ )
3770       {
3771         p[remap[k]] = mp.V[k];
3772       }
3773       for ( k = 0; k < remap_vertex_count; k++ )
3774         mp.V[k] = p[k];
3775       mesh.m_V.SetCount(remap_vertex_count);
3776 
3777       if ( 0 != mp.N )
3778       {
3779         if ( bIgnoreVertexNormals )
3780         {
3781           // average vertex normals of combined vertices
3782           p_array.Zero();
3783           for ( k = 0; k < vertex_count; k++ )
3784           {
3785             v[remap[k]] += mp.N[k];
3786           }
3787           for ( k = 0; k < remap_vertex_count; k++ )
3788           {
3789             v[k].Unitize();
3790           }
3791         }
3792         else
3793         {
3794           for ( k = 0; k < vertex_count; k++ )
3795           {
3796             v[remap[k]] = mp.N[k];
3797           }
3798         }
3799         for ( k = 0; k < remap_vertex_count; k++ )
3800           mp.N[k] = v[k];
3801         mesh.m_N.SetCount(remap_vertex_count);
3802       }
3803       else
3804         mesh.m_N.SetCount(0);
3805 
3806       if ( 0 != mp.T && !bIgnoreTextureCoordinates )
3807       {
3808         for ( k = 0; k < vertex_count; k++ )
3809         {
3810           p[remap[k]] = mp.T[k];
3811         }
3812         for ( k = 0; k < remap_vertex_count; k++ )
3813           mp.T[k] = p[k];
3814         mesh.m_T.SetCount(remap_vertex_count);
3815       }
3816       else
3817         mesh.m_T.SetCount(0);
3818 
3819       if ( 0 != mp.C && !bIgnoreTextureCoordinates )
3820       {
3821         ON_SimpleArray<ON_Color> c_array(remap_vertex_count);
3822         c_array.SetCount(remap_vertex_count);
3823         ON_Color* c = c_array.Array();
3824         for ( k = 0; k < vertex_count; k++ )
3825         {
3826           c[remap[k]] = mp.C[k];
3827         }
3828         for ( k = 0; k < remap_vertex_count; k++ )
3829           mp.C[k] = c[k];
3830         mesh.m_C.SetCount(remap_vertex_count);
3831       }
3832       else
3833         mesh.m_C.SetCount(0);
3834 
3835       if ( 0 != mp.K && !bIgnoreTextureCoordinates )
3836       {
3837         ON_SimpleArray<ON_SurfaceCurvature> s_array(remap_vertex_count);
3838         s_array.SetCount(remap_vertex_count);
3839         ON_SurfaceCurvature* s = s_array.Array();
3840         for ( k = 0; k < vertex_count; k++ )
3841         {
3842           s[remap[k]] = mp.K[k];
3843         }
3844         for ( k = 0; k < remap_vertex_count; k++ )
3845           mp.K[k] = s[k];
3846         mesh.m_K.SetCount(remap_vertex_count);
3847       }
3848       else
3849         mesh.m_K.SetCount(0);
3850 
3851       const int face_count = mesh.m_F.Count();
3852       ON_MeshFace* f = mesh.m_F.Array();
3853       int* fvi;
3854       for ( k = 0; k < face_count; k++ )
3855       {
3856         fvi = f[k].vi;
3857         fvi[0] = remap[fvi[0]];
3858         fvi[1] = remap[fvi[1]];
3859         fvi[2] = remap[fvi[2]];
3860         fvi[3] = remap[fvi[3]];
3861       }
3862 
3863       if (0 != NgonList())
3864       {
3865         //This mesh has an ngon list.
3866         //Modify the vertex indexes in the ngons as
3867         //we did the faces above
3868         ON_MeshNgonList* ngonlist = ModifyNgonList();
3869         int kk, kkct = ngonlist->NgonCount();
3870         for (kk = 0; kk < kkct; kk++)
3871         {
3872           ON_MeshNgon* ngon = ngonlist->Ngon(kk);
3873           if (0 == ngon)
3874             continue;
3875           for ( k = 0; k < ngon->N; k++ )
3876             ngon->vi[k] = remap[ngon->vi[k]];
3877         }
3878       }
3879 
3880       mesh.DestroyPartition();
3881       mesh.DestroyTopology();
3882 
3883       if ( mesh.m_V.Capacity() > 4*mesh.m_V.Count() && mesh.m_V.Capacity() > 50 )
3884       {
3885         // There is lots of unused memory in the dynamic arrays.
3886         // Release what we can.
3887         mesh.Compact();
3888       }
3889       rc = true;
3890     }
3891   }
3892   return rc;
3893 }
3894 
Append(int mesh_count,const ON_Mesh * const * meshes)3895 void ON_Mesh::Append( int mesh_count, const ON_Mesh* const* meshes )
3896 {
3897   if ( mesh_count <= 0 || 0 == meshes )
3898     return;
3899 
3900   int mi;
3901   int vcount0 = VertexCount();
3902   if ( vcount0 <= 0 )
3903     m_F.SetCount(0);
3904   int fcount0 = FaceCount();
3905   int* vi;
3906   int j;
3907   const ON_Mesh* m;
3908 
3909   // The calls to Has*() must happen before the m_V[] and m_F[] arrays get enlarged
3910   // Allow the appendage of VertexNormals, TextureCoordinates, PrincipalCurvatures to empty meshes
3911   // by checking for 0 == vcount0 && 0 == fcount0
3912   bool bHasVertexNormals       = (0 == vcount0 || HasVertexNormals());
3913   bool bHasFaceNormals         = (0 == vcount0 || 0 == fcount0 || HasFaceNormals());
3914   bool bHasTextureCoordinates  = (0 == vcount0 || HasTextureCoordinates());
3915   bool bHasPrincipalCurvatures = (0 == vcount0 || HasPrincipalCurvatures());
3916   bool bHasVertexColors        = (0 == vcount0 || HasVertexColors());
3917   bool bHasSurfaceParameters   = (0 == vcount0 || HasSurfaceParameters());
3918   bool bHasDoubles             = (0 == vcount0 || HasSynchronizedDoubleAndSinglePrecisionVertices());
3919 
3920   int fcount = fcount0;
3921   int vcount = vcount0;
3922   for ( mi = 0; mi < mesh_count; mi++ )
3923   {
3924     m = meshes[mi];
3925     if ( 0 == m )
3926       continue;
3927     int vcount1 = m->m_V.Count();
3928     if ( vcount1 <= 0 )
3929       continue;
3930     int fcount1 = m->m_F.Count();
3931     if ( fcount1 > 0 )
3932       fcount += fcount1;
3933     vcount += vcount1;
3934     if ( bHasVertexNormals && !m->HasVertexNormals() )
3935       bHasVertexNormals = false;
3936     if ( bHasTextureCoordinates && !m->HasTextureCoordinates())
3937       bHasTextureCoordinates = false;
3938     if ( bHasPrincipalCurvatures && !m->HasPrincipalCurvatures())
3939       bHasPrincipalCurvatures = false;
3940     if ( bHasVertexColors && !m->HasVertexColors())
3941       bHasVertexColors = false;
3942     if ( bHasSurfaceParameters && !m->HasSurfaceParameters())
3943       bHasSurfaceParameters = false;
3944     if ( bHasDoubles && !m->HasSynchronizedDoubleAndSinglePrecisionVertices())
3945       bHasDoubles = false;
3946     if ( bHasFaceNormals && fcount1 > 0 && !m->HasFaceNormals() )
3947       bHasFaceNormals = false;
3948   }
3949 
3950   if ( vcount <= vcount0 && fcount <= fcount0 )
3951     return;
3952 
3953   m_top.Destroy();
3954 
3955   // It is critical to call DoublePrecisionVertices() before
3956   // we modify m_V[] because DoublePrecisionVertices() will
3957   // attempt to update the double precision information
3958   // when it notices that m_V has new vertices added.
3959   ON_MeshDoubleVertices* dv = 0;
3960   if ( bHasDoubles )
3961   {
3962     dv = ON_MeshDoubleVertices::Get(this);
3963     if ( 0 == dv && 0 == m_V.Count() )
3964     {
3965       DoublePrecisionVertices(); // creates ON_MeshDoubleVertices info on "this"
3966       dv = ON_MeshDoubleVertices::Get(this);
3967     }
3968     if ( 0 == dv )
3969       bHasDoubles = false;
3970   }
3971 
3972   m_V.Reserve(vcount);
3973   m_F.Reserve(fcount);
3974   for (mi = 0; mi < mesh_count; mi++ )
3975   {
3976     m = meshes[mi];
3977     if ( 0 == m )
3978       continue;
3979     int vcount1 = m->m_V.Count();
3980     if ( vcount1 <= 0 )
3981       continue;
3982     int fcount1 = m->m_F.Count();
3983     if ( fcount1 > 0 )
3984     {
3985       int vc0 = m_V.Count();
3986       j = m_F.Count();
3987       m_F.Append(fcount1,m->m_F.Array());
3988       fcount1 += j;
3989       while (j < fcount1)
3990       {
3991         vi = m_F[j].vi;
3992         vi[0] += vc0;
3993         vi[1] += vc0;
3994         vi[2] += vc0;
3995         vi[3] += vc0;
3996         j++;
3997       }
3998     }
3999     m_V.Append(vcount1,m->m_V.Array());
4000   }
4001 
4002   if ( bHasDoubles)
4003   {
4004     // Now update the double precision vertex locations.
4005     dv->m_dV.Reserve(vcount);
4006     for (mi = 0; mi < mesh_count; mi++ )
4007     {
4008       m = meshes[mi];
4009       if ( 0 == m || m->m_V.Count() <= 0 )
4010         continue;
4011       ON_MeshDoubleVertices* dv1 = ON_MeshDoubleVertices::Get(m);
4012       if ( 0 == dv1 )
4013         break;
4014       dv->m_dV.Append(dv1->m_dV.Count(),dv1->m_dV.Array());
4015     }
4016     if ( dv->m_dV.Count() == vcount )
4017     {
4018       // The calculation of the CRCs in the following calls
4019       // is what slows down appending meshes one-by-one
4020       // when thousands of meshes are being appended.
4021       // That's why the code is structured to do the calculation
4022       // a single time here.
4023       SetSinglePrecisionVerticesAsValid();
4024       SetDoublePrecisionVerticesAsValid();
4025     }
4026     else
4027     {
4028       DestroyDoublePrecisionVertices();
4029     }
4030   }
4031   else
4032   {
4033     DestroyDoublePrecisionVertices();
4034   }
4035 
4036 
4037   if ( bHasVertexNormals )
4038   {
4039     m_N.Reserve(vcount);
4040     for (mi = 0; mi < mesh_count; mi++ )
4041     {
4042       m = meshes[mi];
4043       if ( 0 == m )
4044         continue;
4045       m_N.Append(m->m_N.Count(), m->m_N.Array());
4046     }
4047   }
4048   else
4049   {
4050     m_N.Destroy();
4051   }
4052 
4053 
4054   if ( bHasFaceNormals )
4055   {
4056     m_FN.Reserve(fcount);
4057     for (mi = 0; mi < mesh_count; mi++ )
4058     {
4059       m = meshes[mi];
4060       if ( 0 == m || m->m_V.Count() <= 0 )
4061         continue;
4062       m_FN.Append(m->m_FN.Count(), m->m_FN.Array());
4063     }
4064   }
4065   else
4066   {
4067     m_FN.Destroy();
4068   }
4069 
4070   if ( bHasTextureCoordinates )
4071   {
4072     m_T.Reserve(vcount);
4073     for (mi = 0; mi < mesh_count; mi++ )
4074     {
4075       m = meshes[mi];
4076       if ( 0 == m )
4077         continue;
4078       m_T.Append(m->m_T.Count(), m->m_T.Array());
4079     }
4080   }
4081   else
4082   {
4083     m_T.Destroy();
4084   }
4085 
4086 
4087   if ( bHasSurfaceParameters )
4088   {
4089     m_S.Reserve(vcount);
4090     for (mi = 0; mi < mesh_count; mi++ )
4091     {
4092       m = meshes[mi];
4093       if ( 0 == m )
4094         continue;
4095       m_S.Append(m->m_S.Count(), m->m_S.Array());
4096     }
4097   }
4098   else
4099   {
4100     m_S.Destroy();
4101   }
4102 
4103   if ( bHasPrincipalCurvatures )
4104   {
4105     m_K.Reserve(vcount);
4106     for (mi = 0; mi < mesh_count; mi++ )
4107     {
4108       m = meshes[mi];
4109       if ( 0 == m )
4110         continue;
4111       m_K.Append(m->m_K.Count(), m->m_K.Array());
4112     }
4113   }
4114   else
4115   {
4116     m_K.Destroy();
4117   }
4118 
4119   if ( bHasVertexColors )
4120   {
4121     m_C.Reserve(vcount);
4122     for (mi = 0; mi < mesh_count; mi++ )
4123     {
4124       m = meshes[mi];
4125       if ( 0 == m )
4126         continue;
4127       m_C.Append(m->m_C.Count(), m->m_C.Array());
4128     }
4129   }
4130   else
4131   {
4132     m_C.Destroy();
4133   }
4134 
4135   if ( 0 !=  m_mesh_parameters )
4136   {
4137     for (mi = 0; mi < mesh_count; mi++ )
4138     {
4139       m = meshes[mi];
4140       if ( 0 == m )
4141         continue;
4142       if ( 0 == m->m_mesh_parameters || *m_mesh_parameters != *m->m_mesh_parameters )
4143       {
4144         delete m_mesh_parameters;
4145         m_mesh_parameters = 0;
4146         break;
4147       }
4148     }
4149   }
4150 
4151   for ( j = 0; j < 4; j++ )
4152   {
4153     if ( m_kstat[j] )
4154     {
4155       // will be recomputed if required
4156       delete m_kstat[j];
4157       m_kstat[j] = 0;
4158     }
4159   }
4160 
4161   SetClosed(-99);
4162   SetSolidOrientation(-99);
4163   InvalidateBoundingBoxes();
4164 }
4165 
Append(const ON_Mesh & m)4166 void ON_Mesh::Append( const ON_Mesh& m )
4167 {
4168   const ON_Mesh* meshes[1];
4169   meshes[0] = &m;
4170   Append(1,meshes);
4171 }
4172 
ON_MeshParameters()4173 ON_MeshParameters::ON_MeshParameters()
4174 {
4175   Default();
4176 }
4177 
~ON_MeshParameters()4178 ON_MeshParameters::~ON_MeshParameters()
4179 {
4180 }
4181 
MinEdgeLength(double max_edge_length,double tolerance)4182 double ON_MeshParameters::MinEdgeLength( double max_edge_length, double tolerance )
4183 {
4184   // The 1.0e-4 is a guess.  1.0e-12 was too small to help with objects
4185   // like the one in 67885.
4186   double min_edge_length = 1.0e-4;
4187   if ( max_edge_length > 0.0 && min_edge_length > 1.0e-3*max_edge_length )
4188     min_edge_length =  1.0e-3*max_edge_length;
4189   if ( tolerance > 0.0 && min_edge_length > 1.0e-2*tolerance )
4190     min_edge_length =  1.0e-2*tolerance;
4191   return min_edge_length;
4192 }
4193 
Tolerance(double density,double actual_size)4194 double ON_MeshParameters::Tolerance( double density, double actual_size )
4195 {
4196   // min_rel_tol creates the most facets
4197   //const double min_rel_tol = 5.0e-5;
4198 
4199   //max rel tol creates the fewest facets
4200   //const double max_rel_tol = 0.1;
4201   //m_relative_tolerance = 0.0;
4202   //if ( density <= 0.0 )
4203   //  m_relative_tolerance = max_rel_tol;
4204   //else if ( density >= 1.0 )
4205   //  m_relative_tolerance = min_rel_tol;
4206   //else
4207   //{
4208   //  ON_Interval e(log10(max_rel_tol),log10(min_rel_tol));
4209   //  m_relative_tolerance = pow(10.0,e.ParameterAt(density));
4210   //}
4211 
4212   double tol = 0.0;
4213   double x, e;
4214   if ( ON_IsValid(density) && ON_IsValid(actual_size)
4215        && density > 0.0 && actual_size > 0.0 )
4216   {
4217     if ( density > 1.0 )
4218       density = 1.0;
4219 
4220     //e = (density*(12.0 + density*(2.0*density - 9.0)));
4221     //e = (1.0 + density*(8.0 - 4.0*density));
4222     //e = 1.0 + density*4.0;
4223     //x = 5.0*pow(10.0,-e);
4224 
4225     e = (density < 0.5)
4226       ? 1.0 + density*(6.0 - 4.0*density) // 1.0 + 4.0*density
4227       : 2.0 + 2.0*density;
4228     x = pow(10.0,-e);
4229 
4230     tol = actual_size*x;
4231   }
4232   return tol;
4233 }
4234 
FastRenderMeshParameters()4235 static ON_MeshParameters FastRenderMeshParameters()
4236 {
4237   // If you change these, put your name, the date, and the reasons for
4238   // the change in this comment so we can keep track of what we are
4239   // trying to accomplish;
4240   ON_MeshParameters mp;
4241 
4242   // Added 27 April 2006 for the Meshkateers
4243   //   Attempting to make jagged and faster render meshes a little
4244   //   more dense.
4245   //
4246   // Turn off everything ...
4247   mp.m_bComputeCurvature = false;
4248   mp.m_tolerance = 0.0;
4249   mp.m_bJaggedSeams  = false;
4250   mp.m_max_edge_length = 0.0;
4251   mp.m_grid_aspect_ratio = 0.0;
4252   mp.m_grid_max_count = 0;
4253   mp.m_grid_angle = 0.0;
4254   mp.m_grid_amplification = 0.0;
4255   mp.m_refine_angle = 0.0;
4256 
4257   // ... except ...
4258   // The m_relative_tolerance setting must be set so that
4259   // 0.0005 = ON_MeshParameters::Tolerance(m_relative_tolerance,1.0).
4260   mp.m_relative_tolerance = 0.65;
4261   //double x = Tolerance(m_relative_tolerance,1.0);
4262 
4263   mp.m_grid_min_count     = 16;
4264   mp.m_min_edge_length    = 0.0001;
4265   mp.m_bRefine            = true;
4266   mp.m_bSimplePlanes      = true;
4267 
4268   mp.m_texture_range = 2; // Don't change this without speaking to Dale Lear
4269 
4270   //{
4271   //  // 16 July, 2002 - copied V2 hard coded "jagged and faster" render mesh settings
4272   //  //
4273   //  // Settings used in V2, V3 and early V4 beta
4274   //  mp.m_refine_angle       = 20.0*ON_PI/180.0;
4275   //  mp.m_grid_angle         = 20.0*ON_PI/180.0;
4276   //  mp.m_grid_aspect_ratio  = 0.0;
4277   //  mp.m_min_edge_length    = 0.0001;
4278   //  mp.m_max_edge_length    = 0.0;
4279   //  mp.m_tolerance          = 0.0;
4280   //  mp.m_grid_min_count     = 16;
4281   //  mp.m_bRefine            = 1;
4282   //  mp.m_bJaggedSeams       = 0;
4283   //  mp.m_bSimplePlanes      = 0;
4284   //}
4285 
4286   return mp;
4287 }
4288 
QualityRenderMeshParameters()4289 static ON_MeshParameters QualityRenderMeshParameters()
4290 {
4291   // If you change these, put your name, the date, and the reasons for
4292   // the change in this comment so we can keep track of what we are
4293   // trying to accomplish;
4294   ON_MeshParameters mp;
4295 
4296   // Added 27 April 2006 for the Meshkateers
4297   //   Attempting to make smooth and slower render meshing a little
4298   //   faster.
4299   //
4300   // Turn off everything ...
4301   mp.m_bComputeCurvature = false;
4302   mp.m_tolerance = 0.0;
4303   mp.m_bJaggedSeams  = false;
4304   mp.m_max_edge_length = 0.0;
4305   mp.m_grid_aspect_ratio = 0.0;
4306   mp.m_grid_max_count = 0;
4307   mp.m_grid_angle = 0.0;
4308   mp.m_grid_amplification = 0.0;
4309 
4310   // ... except ...
4311   // The m_relative_tolerance setting must be set so that
4312   // 0.00025 = ON_MeshParameters::Tolerance(m_relative_tolerance,1.0).
4313   mp.m_relative_tolerance = 0.8;
4314   //double x = Tolerance(m_relative_tolerance,1.0);
4315 
4316   mp.m_grid_min_count     = 16;
4317   mp.m_min_edge_length    = 0.0001;
4318   mp.m_bRefine            = true;
4319   mp.m_bSimplePlanes      = true;
4320   mp.m_refine_angle       = 20.0*ON_PI/180.0;
4321 
4322   mp.m_texture_range = 2; // Don't change this without speaking to Dale Lear
4323 
4324 
4325   //// 16 July, 2002 - copied V2 hard coded "smooth and slower" render mesh settings
4326   ////
4327   //// Settings used in V2, V3 and early V4 beta
4328   //mp.m_refine_angle       = 15.0*ON_PI/180.0;
4329   //mp.m_grid_angle         = 15.0*ON_PI/180.0;
4330   //mp.m_grid_aspect_ratio  = 6.0;
4331   //mp.m_min_edge_length    = 0.0001;
4332   //mp.m_max_edge_length    = 0.0;
4333   //mp.m_tolerance          = 0.0;
4334   //mp.m_grid_min_count     = 16;
4335   //mp.m_bRefine            = 1;
4336   //mp.m_bJaggedSeams       = 0;
4337   //mp.m_bSimplePlanes      = 0;
4338 
4339   return mp;
4340 }
4341 
4342 const ON_MeshParameters ON_MeshParameters::FastRenderMesh(FastRenderMeshParameters());
4343 
4344 const ON_MeshParameters ON_MeshParameters::QualityRenderMesh(QualityRenderMeshParameters());
4345 
JaggedAndFasterMeshParameters()4346 void ON_MeshParameters::JaggedAndFasterMeshParameters()
4347 {
4348   *this = ON_MeshParameters::FastRenderMesh;
4349 }
4350 
SmoothAndSlowerMeshParameters()4351 void ON_MeshParameters::SmoothAndSlowerMeshParameters()
4352 {
4353   *this = ON_MeshParameters::QualityRenderMesh;
4354 }
4355 
DefaultAnalysisMeshParameters()4356 void ON_MeshParameters::DefaultAnalysisMeshParameters()
4357 {
4358   Default();
4359 
4360   // 7 July 2006 Dale Lear
4361   //    I added this line for RR 10330
4362   Set( 0.5, m_min_edge_length );
4363 
4364   m_texture_range = 1; // THIS IS REQUIRED - DO NOT CHANGE IT
4365 
4366   // Meshkateers
4367   //   Add your stuff below this line.
4368   //   Do not change m_texture_range.
4369 }
4370 
operator ==(const ON_MeshParameters & m2) const4371 bool ON_MeshParameters::operator==(const ON_MeshParameters& m2) const
4372 {
4373   return (Compare(m2)==0) ? true : false;
4374 }
4375 
operator !=(const ON_MeshParameters & m2) const4376 bool ON_MeshParameters::operator!=(const ON_MeshParameters& m2) const
4377 {
4378   return (Compare(m2)) ? true : false;
4379 }
4380 
operator ==(const ON_Mesh & mesh) const4381 bool ON_MeshParameters::operator==(const ON_Mesh& mesh) const
4382 {
4383   const ON_MeshParameters* mp1 = mesh.MeshParameters();
4384   return mp1 ? (*this == *mp1) : false;
4385 }
4386 
operator !=(const ON_Mesh & mesh) const4387 bool ON_MeshParameters::operator!=(const ON_Mesh& mesh) const
4388 {
4389   const ON_MeshParameters* mp1 = mesh.MeshParameters();
4390   return mp1 ? (*this != *mp1) : true;
4391 }
4392 
Dump(ON_TextLog & text_log) const4393 void ON_MeshParameters::Dump( ON_TextLog& text_log ) const
4394 {
4395   text_log.Print("Gridding:\n");
4396   text_log.PushIndent();
4397   text_log.Print("Min grid count = %d\n",m_grid_min_count);
4398   text_log.Print("Max grid count = %d\n",m_grid_max_count);
4399   text_log.Print("Gridding angle = %g radians (%g degrees)\n",m_grid_angle,180.0*m_grid_angle/ON_PI);
4400   text_log.Print("Aspect ratio = %g\n",m_grid_aspect_ratio);
4401   text_log.Print("Amplification = %g\n",m_grid_amplification);
4402   text_log.PopIndent();
4403 
4404   text_log.Print("Refining:\n");
4405   text_log.PushIndent();
4406   text_log.Print("Refine = %s\n",m_bRefine?"true":"false");
4407   text_log.Print("Refine angle = %g radians (%g degrees)\n",m_refine_angle,180.0*m_refine_angle/ON_PI);
4408   text_log.PopIndent();
4409 
4410   text_log.Print("Metrics:\n");
4411   text_log.PushIndent();
4412   text_log.Print("Density = %g (relative tolerance = %g)\n",m_relative_tolerance,ON_MeshParameters::Tolerance(m_relative_tolerance,1.0));
4413   text_log.Print("Minimum tolerance = %g\n",m_min_tolerance);
4414   text_log.Print("Tolerance = %g\n",m_tolerance);
4415   text_log.Print("Min edge length = %g\n",m_min_edge_length);
4416   text_log.Print("Max edge length = %g\n",m_max_edge_length);
4417   text_log.PopIndent();
4418 
4419   text_log.Print("Misceleanous:\n");
4420   text_log.PushIndent();
4421   text_log.Print("Face type = %d\n",m_face_type );
4422   text_log.Print("Compute curvature = %s\n",m_bComputeCurvature?"true":"false");
4423   text_log.Print("Texture range = %d\n",m_texture_range);
4424   text_log.Print("Simple planes = %s\n",m_bSimplePlanes?"true":"false");
4425   text_log.Print("Jagged Seams = %s\n",m_bJaggedSeams?"true":"false");
4426   text_log.Print("Double Precision = %s\n",m_bDoublePrecision?"true":"false");
4427   text_log.Print("Custom settings = %s\n",m_bCustomSettings?"true":"false");
4428   text_log.PopIndent();
4429 }
4430 
Default()4431 void ON_MeshParameters::Default()
4432 {
4433   memset(this,0,sizeof(*this));
4434 
4435   m_bCustomSettings = false;
4436   m_bComputeCurvature = false;
4437   m_bSimplePlanes = false;
4438   m_bRefine = true;
4439   m_bJaggedSeams = false;
4440   m_bDoublePrecision = false;
4441   m_bCustomSettingsEnabled = true;
4442   m_mesher = 0;
4443   m_texture_range = 2; // packed normalized textures
4444   m_reserved2 = 0;
4445   m_tolerance = 0.0;
4446   m_relative_tolerance = 0.0;
4447   m_min_tolerance = 0.0;
4448   m_min_edge_length = 0.0001;
4449   m_max_edge_length = 0.0;
4450   m_grid_aspect_ratio = 6.0;
4451   m_grid_min_count = 0;
4452   m_grid_max_count = 0;
4453   m_grid_angle = 20.0*ON_PI/180.0;
4454   m_grid_amplification = 1.0;
4455   m_refine_angle = 20.0*ON_PI/180.0;
4456   m_face_type = 0;
4457   m_reserved3 = 0;
4458 }
4459 
Set(double density,double min_edge_length)4460 void ON_MeshParameters::Set( double density, double min_edge_length )
4461 {
4462   bool bRelTolTest = true;
4463 
4464   Default();
4465 
4466   // These 4 settings should not be changed
4467   m_bComputeCurvature = false;
4468   m_min_edge_length = min_edge_length;
4469   m_texture_range = 0;
4470   m_tolerance = 0.0;
4471 
4472   if ( bRelTolTest )
4473   {
4474     // Added 27 April 2005
4475     m_relative_tolerance = density;
4476 
4477     // The other meshing parameters are disabled.
4478     //
4479     //   Dale Lear's remark:
4480     //     I think it is a mistake to leave m_grid_aspect_ratio = 0
4481     //     at all settings.  This will result in long skinny triangles
4482     //     in areas where the principal curvatures are dramatically
4483     //     different.
4484     //
4485     m_bRefine = (density < 0.65);
4486     m_bSimplePlanes = (density <= 0.0);
4487     m_bJaggedSeams  = false;
4488     m_max_edge_length = 0.0;
4489     m_grid_aspect_ratio = 0.0;
4490     m_grid_min_count = 0;
4491     m_grid_max_count = 0;
4492     m_grid_angle = 0.0;
4493     m_grid_amplification = 0.0;
4494     m_refine_angle = 0.0;
4495   }
4496   else
4497   {
4498     // Used in V3 and prior to 27 April 2005
4499     double t0, t1;
4500 
4501     m_bRefine = true;
4502     m_bSimplePlanes = false;
4503     m_bJaggedSeams  = false;
4504     m_max_edge_length = 0.0;
4505     m_grid_aspect_ratio = 0.0;
4506     m_grid_min_count = 16;
4507     m_grid_max_count = 0;
4508     m_grid_angle = 20.0*ON_PI/180.0;
4509     m_grid_amplification = 1.0;
4510     m_refine_angle = 20.0*ON_PI/180.0;
4511 
4512     if ( density <= 0.0)
4513     {
4514       m_bSimplePlanes = true;
4515       m_bRefine       = false;
4516 
4517       m_grid_min_count     = 0;
4518       m_grid_aspect_ratio  = 0.0;
4519       m_grid_angle         = 0.0;
4520       m_grid_amplification = 1.0;
4521 
4522       m_refine_angle       = m_grid_angle;
4523     }
4524     else if ( density < 0.5)
4525     {
4526       t1 = 2.0*density;
4527       t0 = 1.0-t1;
4528 
4529       m_bRefine = density < 0.10 ? false : true;
4530 
4531       m_grid_min_count = 0;
4532       m_grid_aspect_ratio  = 6.0;
4533       m_grid_angle         = 20.0*ON_PI/180.0;
4534       m_grid_amplification = (t1 > 0.0 ) ? t1 : 1.0;
4535 
4536       m_refine_angle = (t0*65.0 + t1*20.0)*ON_PI/180.0;
4537     }
4538     else if ( density == 0.5 )
4539     {
4540       m_bRefine       = true;
4541 
4542       m_grid_min_count     = 0;
4543       m_grid_aspect_ratio  = 6.0;
4544       m_grid_angle         = 20.0*ON_PI/180.0;
4545       m_grid_amplification = 1.0;
4546 
4547       m_refine_angle        = m_grid_angle;
4548     }
4549     else
4550     {
4551       t1 = 2.0*(density - 0.5);
4552       if ( t1 > 1.0 )
4553         t1 = 1.0;
4554       t0 = 1.0-t1;
4555 
4556       m_bRefine = true;
4557 
4558       m_grid_min_count     = 0;
4559       m_grid_aspect_ratio  = t0*6.0 + t1;
4560       m_grid_angle         = (t0*20.0 + t1*5.0)*ON_PI/180.0;
4561       m_grid_amplification = 1.0 + t1;
4562 
4563       m_refine_angle       = m_grid_angle;
4564     }
4565   }
4566 }
4567 
4568 
Compare(const ON_MeshParameters & src) const4569 int ON_MeshParameters::Compare( const ON_MeshParameters& src ) const
4570 {
4571   // Discuss any changes with Dale Lear.
4572   if ( !m_bCustomSettings && src.m_bCustomSettings )
4573     return -1;
4574   if ( m_bCustomSettings && !src.m_bCustomSettings )
4575     return 1;
4576   if ( m_texture_range < src.m_texture_range )
4577     return -1;
4578   if ( m_texture_range > src.m_texture_range )
4579     return 1;
4580   return CompareGeometrySettings(src);
4581 }
4582 
ON_MeshParameters_CompareDouble(double t0,double t1,double default_value)4583 static int ON_MeshParameters_CompareDouble(double t0, double t1, double default_value)
4584 {
4585   if ( !ON_IsValid(t0) || t0 <= 0.0 )
4586     t0 = default_value;
4587   if ( !ON_IsValid(t1) || t1 <= 0.0 )
4588     t1 = default_value;
4589   if ( t0 < t1 )
4590     return 1;
4591   if ( t1 < t0 )
4592     return -1;
4593   return 0;
4594 }
4595 
ON_MeshParameters_CompareInt(int i0,int i1)4596 static int ON_MeshParameters_CompareInt(int i0, int i1)
4597 {
4598   if ( i0 <= 0 )
4599     i0 = 0;
4600   if ( i1 <= 0 )
4601     i1 = 0;
4602   return i1-i0;
4603 }
4604 
ON_MeshParameters_CompareBool(bool b0,bool b1)4605 static int ON_MeshParameters_CompareBool(bool b0, bool b1)
4606 {
4607   const int i0 = b0 ? 1 : 0;
4608   const int i1 = b1 ? 1 : 0;
4609   return i1-i0;
4610 }
4611 
CompareGeometrySettings(const ON_MeshParameters & src) const4612 int ON_MeshParameters::CompareGeometrySettings( const ON_MeshParameters& src ) const
4613 {
4614   int rc;
4615 
4616   // Discuss any changes with Dale Lear.
4617   rc = ON_MeshParameters_CompareBool(m_bSimplePlanes,src.m_bSimplePlanes);
4618   if (rc)
4619     return rc;
4620   rc = ON_MeshParameters_CompareBool(m_bRefine,src.m_bRefine);
4621   if (rc)
4622     return rc;
4623   rc = ON_MeshParameters_CompareBool(m_bJaggedSeams,src.m_bJaggedSeams);
4624   if (rc)
4625     return rc;
4626   if ( m_mesher < src.m_mesher )
4627     return -1;
4628   if ( m_mesher > src.m_mesher )
4629     return 1;
4630 
4631   rc = ON_MeshParameters_CompareDouble(m_tolerance,src.m_tolerance,0.0);
4632   if (rc)
4633     return rc;
4634 
4635   rc = ON_MeshParameters_CompareDouble(m_relative_tolerance,src.m_relative_tolerance,0.0);
4636   if (rc)
4637     return rc;
4638 
4639   // DO NOT COMPARE m_min_tolerance - it is a runtime lower bound clamp.
4640   //                If it is included here, Rhino will remesh everytime
4641   //                model tolerance changes.
4642 
4643   rc = ON_MeshParameters_CompareDouble(m_min_edge_length,src.m_min_edge_length,0.0);
4644   if (rc)
4645     return rc;
4646 
4647   rc = ON_MeshParameters_CompareDouble(m_max_edge_length,src.m_max_edge_length,0.0);
4648   if (rc)
4649     return rc;
4650 
4651   rc = ON_MeshParameters_CompareDouble(m_grid_aspect_ratio,src.m_grid_aspect_ratio,0.0);
4652   if (rc)
4653     return rc;
4654 
4655   rc = ON_MeshParameters_CompareInt(m_grid_min_count,src.m_grid_min_count);
4656   if (rc)
4657     return rc;
4658 
4659   rc = ON_MeshParameters_CompareInt(m_grid_max_count,src.m_grid_max_count);
4660   if (rc)
4661     return rc;
4662 
4663   rc = ON_MeshParameters_CompareDouble(m_grid_angle,src.m_grid_angle,ON_PI);
4664   if (rc)
4665     return rc;
4666 
4667   rc = ON_MeshParameters_CompareDouble(m_refine_angle,src.m_refine_angle,0.0);
4668   if (rc)
4669     return rc;
4670 
4671   rc = ON_MeshParameters_CompareDouble(m_grid_amplification,src.m_grid_amplification,1.0);
4672   if (rc)
4673     return rc;
4674 
4675   if ( m_face_type < src.m_face_type )
4676     return -1;
4677   if ( m_face_type > src.m_face_type )
4678     return 1;
4679 
4680   return 0;
4681 }
4682 
DataCRC(ON__UINT32 current_remainder) const4683 ON__UINT32 ON_MeshParameters::DataCRC(ON__UINT32 current_remainder) const
4684 {
4685   ON__UINT32 crc = ON_CRC32(current_remainder,sizeof(m_bComputeCurvature),&m_bComputeCurvature);
4686   crc = ON_CRC32(crc,sizeof(m_bSimplePlanes),&m_bSimplePlanes);
4687   crc = ON_CRC32(crc,sizeof(m_bRefine),&m_bRefine);
4688   crc = ON_CRC32(crc,sizeof(m_bJaggedSeams),&m_bJaggedSeams);
4689   crc = ON_CRC32(crc,sizeof(m_tolerance),&m_tolerance);
4690   crc = ON_CRC32(crc,sizeof(m_min_edge_length),&m_min_edge_length);
4691   crc = ON_CRC32(crc,sizeof(m_max_edge_length),&m_max_edge_length);
4692   crc = ON_CRC32(crc,sizeof(m_grid_aspect_ratio),&m_grid_aspect_ratio);
4693   crc = ON_CRC32(crc,sizeof(m_grid_min_count),&m_grid_min_count);
4694   crc = ON_CRC32(crc,sizeof(m_grid_max_count),&m_grid_max_count);
4695   crc = ON_CRC32(crc,sizeof(m_grid_angle),&m_grid_angle);
4696   crc = ON_CRC32(crc,sizeof(m_grid_amplification),&m_grid_amplification);
4697   crc = ON_CRC32(crc,sizeof(m_refine_angle),&m_refine_angle);
4698   crc = ON_CRC32(crc,sizeof(m_face_type),&m_face_type);
4699   crc = ON_CRC32(crc,sizeof(m_texture_range),&m_texture_range);
4700   crc = ON_CRC32(crc,sizeof(m_bCustomSettings),&m_bCustomSettings);
4701   crc = ON_CRC32(crc,sizeof(m_relative_tolerance),&m_relative_tolerance);
4702   crc = ON_CRC32(crc,sizeof(m_mesher),&m_mesher);
4703   return crc;
4704 }
4705 
Write(ON_BinaryArchive & file) const4706 bool ON_MeshParameters::Write( ON_BinaryArchive& file ) const
4707 {
4708   bool rc = file.Write3dmChunkVersion(1,4);
4709   if (rc) {
4710     if (rc) rc = file.WriteInt(m_bComputeCurvature);
4711     if (rc) rc = file.WriteInt(m_bSimplePlanes);
4712     if (rc) rc = file.WriteInt(m_bRefine);
4713     if (rc) rc = file.WriteInt(m_bJaggedSeams);
4714     if (rc) rc = file.WriteInt(0); // obsolete m_bWeld field
4715     if (rc) rc = file.WriteDouble(m_tolerance);
4716     if (rc) rc = file.WriteDouble(m_min_edge_length);
4717     if (rc) rc = file.WriteDouble(m_max_edge_length);
4718     if (rc) rc = file.WriteDouble(m_grid_aspect_ratio);
4719     if (rc) rc = file.WriteInt(m_grid_min_count);
4720     if (rc) rc = file.WriteInt(m_grid_max_count);
4721     if (rc) rc = file.WriteDouble(m_grid_angle);
4722     if (rc) rc = file.WriteDouble(m_grid_amplification);
4723     if (rc) rc = file.WriteDouble(m_refine_angle);
4724     if (rc) rc = file.WriteDouble(5.0*ON_PI/180.0); // obsolete m_combine_angle field
4725     int mft = m_face_type;
4726     if ( mft < 0 || mft > 2 )
4727     {
4728       ON_ERROR("ON_MeshParameters::Read() - m_face_type out of bounds.");
4729       mft = 0;
4730     }
4731     if (rc) rc = file.WriteInt(mft);
4732 
4733     // added for chunk version 1.1
4734     if (rc) rc = file.WriteInt( m_texture_range );
4735 
4736     // added for chunk version 1.2 - 14 October 2005
4737     if (rc) rc = file.WriteBool( m_bCustomSettings );
4738     if (rc) rc = file.WriteDouble( m_relative_tolerance );
4739     // DO NOT SAVE m_min_tolerance - yet ??
4740 
4741     // added for chunk version 1.3 - 20 February 2006
4742     if (rc) rc = file.WriteChar(m_mesher);
4743 
4744     // added for chunk version 1.4 - 3 March 2011
4745     if (rc) rc = file.WriteBool( m_bCustomSettingsEnabled );
4746   }
4747   return rc;
4748 }
4749 
Read(ON_BinaryArchive & file)4750 bool ON_MeshParameters::Read( ON_BinaryArchive& file )
4751 {
4752   Default();
4753   int major_version = 0;
4754   int minor_version = 0;
4755   bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
4756   if ( rc && major_version == 1 )
4757   {
4758     int i;
4759 
4760     i = m_bComputeCurvature;
4761     if (rc) rc = file.ReadInt(&i);
4762     m_bComputeCurvature = i?true:false;
4763 
4764     i = m_bSimplePlanes;
4765     if (rc) rc = file.ReadInt(&i);
4766     m_bSimplePlanes = i?true:false;
4767 
4768     i = m_bRefine;
4769     if (rc) rc = file.ReadInt(&i);
4770     m_bRefine = i?true:false;
4771 
4772     i = m_bJaggedSeams;
4773     if (rc) rc = file.ReadInt(&i);
4774     m_bJaggedSeams = i?true:false;
4775 
4776     int obsolete_m_bWeld;
4777     if (rc) rc = file.ReadInt(&obsolete_m_bWeld);
4778 
4779     if (rc) rc = file.ReadDouble(&m_tolerance);
4780     if (rc) rc = file.ReadDouble(&m_min_edge_length);
4781     if (rc) rc = file.ReadDouble(&m_max_edge_length);
4782     if (rc) rc = file.ReadDouble(&m_grid_aspect_ratio);
4783     if (rc) rc = file.ReadInt(&m_grid_min_count);
4784     if (rc) rc = file.ReadInt(&m_grid_max_count);
4785     if (rc) rc = file.ReadDouble(&m_grid_angle);
4786     if (rc) rc = file.ReadDouble(&m_grid_amplification);
4787     if (rc) rc = file.ReadDouble(&m_refine_angle);
4788     double obsolete_m_combine_angle;
4789     if (rc) rc = file.ReadDouble(&obsolete_m_combine_angle);
4790     if (rc) rc = file.ReadInt(&m_face_type);
4791     if ( m_face_type < 0 || m_face_type > 2 )
4792     {
4793       ON_ERROR("ON_MeshParameters::Read() - m_face_type out of bounds.");
4794       m_face_type = 0;
4795     }
4796 
4797     if ( rc && minor_version >= 1 )
4798     {
4799       rc = file.ReadInt( &m_texture_range );
4800       if ( rc && minor_version >= 2 )
4801       {
4802         rc = file.ReadBool(&m_bCustomSettings);
4803         if (rc) rc = file.ReadDouble(&m_relative_tolerance);
4804         if ( rc && minor_version >= 3 )
4805         {
4806           rc = file.ReadChar(&m_mesher);
4807           if ( rc && minor_version >= 4 )
4808           {
4809             rc = file.ReadBool(&m_bCustomSettingsEnabled);
4810           }
4811         }
4812       }
4813     }
4814   }
4815   return rc;
4816 }
4817 
4818 
4819 
ON_MeshCurvatureStats()4820 ON_MeshCurvatureStats::ON_MeshCurvatureStats()
4821 {
4822   Destroy(); // initializes defaults
4823 }
4824 
~ON_MeshCurvatureStats()4825 ON_MeshCurvatureStats::~ON_MeshCurvatureStats()
4826 {}
4827 
Destroy()4828 void ON_MeshCurvatureStats::Destroy()
4829 {
4830   m_style = ON::unknown_curvature_style;
4831   m_infinity = 0.0;
4832   m_count_infinite = 0;
4833   m_count = 0;
4834   m_mode = 0.0;
4835   m_average = 0.0;
4836   m_adev = 0.0;
4837   m_range.Set(0.0,0.0);
4838 }
4839 
EmergencyDestroy()4840 void ON_MeshCurvatureStats::EmergencyDestroy()
4841 {
4842   Destroy(); // - no memory is freed so Destroy() will work
4843 }
4844 
ON_MeshCurvatureStats(const ON_MeshCurvatureStats & src)4845 ON_MeshCurvatureStats::ON_MeshCurvatureStats(const ON_MeshCurvatureStats& src)
4846 {
4847   *this = src;
4848 }
4849 
operator =(const ON_MeshCurvatureStats & src)4850 ON_MeshCurvatureStats& ON_MeshCurvatureStats::operator=(const ON_MeshCurvatureStats& src)
4851 {
4852   if ( this != &src ) {
4853     m_style          = src.m_style;
4854     m_infinity       = src.m_infinity;
4855     m_count_infinite = src.m_count_infinite;
4856     m_count          = src.m_count;
4857     m_mode           = src.m_mode;
4858     m_average        = src.m_average;
4859     m_adev           = src.m_adev;
4860     m_range          = src.m_range;
4861   }
4862   return *this;
4863 }
4864 
Write(ON_BinaryArchive & file) const4865 bool ON_MeshCurvatureStats::Write( ON_BinaryArchive& file ) const
4866 {
4867   int i;
4868   bool rc = file.Write3dmChunkVersion(1,1);
4869   if (rc) {
4870     i = m_style;
4871     if (rc) rc = file.WriteInt(i);
4872     if (rc) rc = file.WriteDouble(m_infinity);
4873     if (rc) rc = file.WriteInt(m_count_infinite);
4874     if (rc) rc = file.WriteInt(m_count);
4875     if (rc) rc = file.WriteDouble(m_mode);
4876     if (rc) rc = file.WriteDouble(m_average);
4877     if (rc) rc = file.WriteDouble(m_adev);
4878     if (rc) rc = file.WriteInterval(m_range);
4879   }
4880   return rc;
4881 }
4882 
4883 
Read(ON_BinaryArchive & file)4884 bool ON_MeshCurvatureStats::Read( ON_BinaryArchive& file )
4885 {
4886   int major_version = 0;
4887   int minor_version = 0;
4888   Destroy();
4889   bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
4890   if (rc && major_version == 1) {
4891     int i=0;
4892     if (rc) rc = file.ReadInt(&i);
4893     if (rc) m_style = ON::CurvatureStyle(i);
4894     if (rc) rc = file.ReadDouble(&m_infinity);
4895     if (rc) rc = file.ReadInt(&m_count_infinite);
4896     if (rc) rc = file.ReadInt(&m_count);
4897     if (rc) rc = file.ReadDouble(&m_mode);
4898     if (rc) rc = file.ReadDouble(&m_average);
4899     if (rc) rc = file.ReadDouble(&m_adev);
4900     if (rc) rc = file.ReadInterval(m_range);
4901   }
4902   return rc;
4903 }
4904 
Set(ON::curvature_style kappa_style,int Kcount,const ON_SurfaceCurvature * K,const ON_3fVector * N,double infinity)4905 bool ON_MeshCurvatureStats::Set( ON::curvature_style kappa_style,
4906                                  int Kcount,
4907                                  const ON_SurfaceCurvature* K,
4908                                  const ON_3fVector* N, // needed for normal sectional curvatures
4909                                  double infinity
4910                                  )
4911 {
4912   bool rc = (Kcount > 0 && K != NULL);
4913 
4914   Destroy();
4915 
4916   if (rc) {
4917     ON_Workspace ws;
4918     //ON_3dVector tangent;
4919     double k;
4920     double* kappa = ws.GetDoubleMemory(Kcount);
4921     int i;
4922 
4923     switch( kappa_style ) {
4924     case ON::gaussian_curvature:
4925       m_style = kappa_style;
4926       m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e20;
4927       break;
4928     case ON::mean_curvature: // unsigned mean
4929       m_style = kappa_style;
4930       m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
4931       break;
4932     case ON::min_curvature: // minimum unsigned radius of curvature
4933       m_style = kappa_style;
4934       m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
4935       break;
4936     case ON::max_curvature: // maximum unsigned radius of curvature
4937       m_style = kappa_style;
4938       m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
4939       break;
4940     //case ON::section_curvature_x:
4941     //  if ( !N )
4942     //    kappa_style = ON::unknown_curvature_style;
4943     //  m_style = kappa_style;
4944     //  m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
4945     //  break;
4946     //case ON::section_curvature_y:
4947     //  if ( !N )
4948     //    kappa_style = ON::unknown_curvature_style;
4949     //  m_style = kappa_style;
4950     //  m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
4951     //  break;
4952     //case ON::section_curvature_z:
4953     //  if ( !N )
4954     //    kappa_style = ON::unknown_curvature_style;
4955     //  m_style = kappa_style;
4956     //  m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
4957     //  break;
4958     default:
4959       rc = false;
4960       break;
4961     }
4962 
4963     for ( i = 0; i < Kcount; i++ ) {
4964       switch( kappa_style ) {
4965       case ON::gaussian_curvature:
4966         k = K[i].GaussianCurvature();
4967         break;
4968       case ON::mean_curvature: // unsigned mean
4969         k = fabs(K[i].MeanCurvature());
4970         break;
4971       case ON::min_curvature: // minimum unsigned radius of curvature
4972         k = fabs(K[i].MinimumRadius());
4973         break;
4974       case ON::max_curvature: // maximum unsigned radius of curvature
4975         k = fabs(K[i].MaximumRadius());
4976         break;
4977       //case ON::section_curvature_x:
4978       //  tangent.x = 0.0; tangent.y = -N[i].z; tangent.z = N[i].y;
4979       //  if ( fabs(tangent.y) <= ON_SQRT_EPSILON && fabs(tangent.z) <= ON_SQRT_EPSILON )
4980       //    tangent.Zero();
4981       //  else
4982       //    tangent.Unitize();
4983       //  k = fabs(K[i].NormalCurvature(tangent));
4984       //  break;
4985       //case ON::section_curvature_y:
4986       //  tangent.x = N[i].z; tangent.y = 0.0; tangent.z = -N[i].x;
4987       //  if ( fabs(tangent.x) <= ON_SQRT_EPSILON && fabs(tangent.z) <= ON_SQRT_EPSILON )
4988       //    tangent.Zero();
4989       //  else
4990       //    tangent.Unitize();
4991       //  k = fabs(K[i].NormalCurvature(tangent));
4992       //  break;
4993       //case ON::section_curvature_z:
4994       //  tangent.x = -N[i].y; tangent.y = N[i].x; tangent.z = 0.0;
4995       //  if ( fabs(tangent.x) <= ON_SQRT_EPSILON && fabs(tangent.y) <= ON_SQRT_EPSILON )
4996       //    tangent.Zero();
4997       //  else
4998       //    tangent.Unitize();
4999       //  k = fabs(K[i].NormalCurvature(tangent));
5000       //  break;
5001       default:
5002         k=0.0;
5003         break;
5004       }
5005       if ( fabs(k) >= m_infinity ) {
5006         m_count_infinite++;
5007         continue;
5008       }
5009       if ( m_count ) {
5010         if ( k < m_range.m_t[0] )
5011           m_range.m_t[0] = k;
5012         else if ( k > m_range.m_t[1] )
5013           m_range.m_t[1] = k;
5014       }
5015       else {
5016         m_range.m_t[0] = m_range.m_t[1] = k;
5017       }
5018       kappa[m_count++] = k;
5019     }
5020 
5021 
5022     if ( m_count == 0 )
5023       rc = false;
5024     else {
5025       // sum curvatures
5026       ON_SortDoubleArray( ON::quick_sort, kappa, m_count );
5027 
5028       // mode
5029       m_mode = kappa[m_count/2];
5030       if ( 0 == (m_count % 2) ) {
5031         m_mode += kappa[(m_count/2)-1];
5032         m_mode *= 0.5;
5033       }
5034 
5035       // average
5036       for ( i = 0; i < m_count; i++ ) {
5037         m_average += kappa[i];
5038       }
5039       m_average = m_average/m_count;
5040 
5041       // average deviation
5042       for ( i = 0; i < m_count; i++ ) {
5043         m_adev += fabs(kappa[i] - m_average);
5044       }
5045       m_adev = m_adev/m_count;
5046     }
5047   }
5048 
5049   return rc;
5050 }
5051 
5052 struct EDGEINFO
5053 {
5054   int fi[2]; // m_F[] triangles on either side of the edge
5055   int vi[4]; // potential m_V[] quad indices
5056   int flag; // 0 = available,
5057             // 1 = side of quad,
5058             // 2 = bndry or nonmanifold edge,
5059             // 3 = edge crease angle > angle_tol_radians
5060             // 4 = edge is not longest side of triangle
5061             // 5 = edge length is zero
5062             // 6 = edge would be a boundary if mesh were exploded
5063             // 16 = edge will be removed to make a quad
5064   double length;
5065 };
5066 
ConvertTrianglesToQuads(double angle_tol_radians,double min_diagonal_length_ratio)5067 bool ON_Mesh::ConvertTrianglesToQuads(
5068               double angle_tol_radians,
5069               double min_diagonal_length_ratio
5070               )
5071 {
5072   ON_Workspace ws;
5073 
5074   double d;
5075 
5076   int i, ii, jj;
5077   int diagonal_count;
5078   const int* f0vi;
5079   const int* f1vi;
5080   const int* fei;
5081 
5082   diagonal_count = 0;
5083 
5084   if ( angle_tol_radians < 0.0 || !ON_IsValid(angle_tol_radians) )
5085   {
5086     // 2.5 degrees
5087     angle_tol_radians = 0.043633231299858239423092269212215;
5088   }
5089   else if ( angle_tol_radians < ON_ZERO_TOLERANCE )
5090   {
5091     angle_tol_radians = ON_ZERO_TOLERANCE;
5092   }
5093 
5094   angle_tol_radians = cos(angle_tol_radians);
5095   if ( angle_tol_radians < 0.5 )
5096     angle_tol_radians = 0.5;
5097   else if ( angle_tol_radians > 1.0-ON_SQRT_EPSILON )
5098     angle_tol_radians = 1.0-ON_SQRT_EPSILON;
5099 
5100   const ON_MeshTopology& top = Topology();
5101 
5102   if ( !HasFaceNormals() )
5103     ComputeFaceNormals();
5104 
5105   if ( min_diagonal_length_ratio < ON_ZERO_TOLERANCE )
5106     min_diagonal_length_ratio = ON_ZERO_TOLERANCE;
5107 
5108   double max_diagonal_length_ratio = 1.0/min_diagonal_length_ratio;
5109 
5110   if( min_diagonal_length_ratio > max_diagonal_length_ratio )
5111   {
5112     d = min_diagonal_length_ratio;
5113     min_diagonal_length_ratio = max_diagonal_length_ratio;
5114     max_diagonal_length_ratio = d;
5115   }
5116 
5117   //double rel_tol = ON_SQRT_EPSILON;
5118   double rel_tol = 8.0*ON_SQRT_EPSILON; // to fix RR 51543
5119   if ( min_diagonal_length_ratio > 1.0-rel_tol )
5120     min_diagonal_length_ratio = 1.0-rel_tol;
5121   if ( max_diagonal_length_ratio < 1.0+rel_tol )
5122     max_diagonal_length_ratio = 1.0+rel_tol;
5123 
5124 
5125   const int face_count = m_F.Count();
5126   int* face_flag = ws.GetIntMemory(face_count);
5127   for ( i = 0; i < face_count; i++ )
5128   {
5129     f0vi = m_F[i].vi;
5130     face_flag[i] = ( f0vi[2] == f0vi[3] ) ? 0 : 1;
5131   }
5132 
5133   const int edge_count = top.m_tope.Count();
5134   struct EDGEINFO* EI = (struct EDGEINFO*)ws.GetMemory(edge_count*sizeof(*EI));
5135   for ( i = 0; i < edge_count; i++ )
5136   {
5137     struct EDGEINFO& ei = EI[i];
5138     const ON_MeshTopologyEdge& tope = top.m_tope[i];
5139     ei.flag = 0;
5140     ei.vi[0] = top.m_topv[tope.m_topvi[0]].m_vi[0];
5141     ei.vi[2] = top.m_topv[tope.m_topvi[1]].m_vi[0];
5142     ei.length = m_V[ei.vi[0]].DistanceTo(m_V[ei.vi[2]]);
5143     if ( ei.length <= 0.0 || !ON_IsValid(ei.length) )
5144     {
5145       ei.flag = 5;
5146     }
5147     else if ( tope.m_topf_count != 2 )
5148     {
5149       ei.flag = 2;
5150     }
5151     else
5152     {
5153       ei.fi[0] = tope.m_topfi[0];
5154       ei.fi[1] = tope.m_topfi[1];
5155       if (face_flag[ei.fi[0]] || face_flag[ei.fi[1]] )
5156       {
5157         ei.flag = 1;
5158       }
5159       else if ( m_FN[ei.fi[0]] * m_FN[ei.fi[1]] < angle_tol_radians )
5160       {
5161         ei.flag = 3;
5162       }
5163       else
5164       {
5165         f0vi = m_F[ei.fi[0]].vi;
5166         f1vi = m_F[ei.fi[1]].vi;
5167         ei.flag = 6;
5168         for ( ii = 0; ii < 3 && ei.flag; ii++ )
5169         {
5170           for (jj = 0; jj < 3; jj++)
5171           {
5172             if (    f0vi[ii] == f1vi[jj]
5173                  && f0vi[(ii+1)%3] == f1vi[(jj+2)%3]
5174                  && f0vi[(ii+2)%3] != f1vi[(jj+1)%3] )
5175             {
5176               if ( ei.fi[0] > ei.fi[1] )
5177               {
5178                 jj = ei.fi[0]; ei.fi[0] = ei.fi[1]; ei.fi[1] = jj;
5179               }
5180               ei.vi[0] = f0vi[ii];
5181               ei.vi[1] = f1vi[(jj+1)%3];
5182               ei.vi[2] = f0vi[(ii+1)%3];
5183               ei.vi[3] = f0vi[(ii+2)%3];
5184               ei.flag = 0;
5185               break;
5186             }
5187           }
5188         }
5189       }
5190     }
5191   }
5192 
5193   for ( i = 0; i < edge_count; i++ )
5194   {
5195     struct EDGEINFO& ei = EI[i];
5196     if ( ei.flag )
5197       continue;
5198 
5199     ei.flag = 16;
5200 
5201     // It is CRITICAL that the length compare use >=.
5202     // Otherwise tesselations of equailateral triangles
5203     // will not work right in this fuction.
5204     fei = top.m_topf[ei.fi[0]].m_topei;
5205     if (    ( i != fei[0] && EI[fei[0]].length >= ei.length )
5206          || ( i != fei[1] && EI[fei[1]].length >= ei.length )
5207          || ( i != fei[2] && EI[fei[2]].length >= ei.length )
5208          )
5209     {
5210       // diagonal is not strictly longest in this triangle
5211       continue;
5212     }
5213 
5214     // It is CRITICAL that the length compare use >=.
5215     // Otherwise tesselations of equailateral triangles
5216     // will not work right in this fuction.
5217     fei = top.m_topf[ei.fi[1]].m_topei;
5218     if (    ( i != fei[0] && EI[fei[0]].length >= ei.length )
5219          || ( i != fei[1] && EI[fei[1]].length >= ei.length )
5220          || ( i != fei[2] && EI[fei[2]].length >= ei.length )
5221          )
5222     {
5223       // diagonal is not strictly longest in this triangle
5224       continue;
5225     }
5226 
5227     d = m_V[ei.vi[1]].DistanceTo(m_V[ei.vi[3]]);
5228     d /= ei.length;
5229     if ( d < min_diagonal_length_ratio || d > max_diagonal_length_ratio )
5230     {
5231       // quad wouldn't be square enough
5232       continue;
5233     }
5234 
5235     ei.flag = 0;
5236 
5237     diagonal_count++;
5238   }
5239 
5240   if ( diagonal_count > 0 )
5241   {
5242     DestroyTree();
5243     DestroyPartition();
5244     m_top.Destroy();
5245     for ( i = 0; i < edge_count; i++ )
5246     {
5247       struct EDGEINFO& ei = EI[i];
5248       if ( ei.flag )
5249         continue;
5250       memcpy( m_F[ei.fi[0]].vi, ei.vi, 4*sizeof(ei.vi[0]) );
5251       memset( m_F[ei.fi[1]].vi, 0xFF,  4*sizeof(ei.vi[0]) );
5252       m_triangle_count--;
5253       m_quad_count++;
5254     }
5255     CullDegenerateFaces();
5256   }
5257 
5258   return (diagonal_count > 0 );
5259 }
5260 
ConvertQuadsToTriangles()5261 bool ON_Mesh::ConvertQuadsToTriangles()
5262 {
5263   const bool bHasFaceNormals = HasFaceNormals();
5264   const int fcount = FaceCount();
5265   const int vcount = VertexCount();
5266   int fi, idmin;;
5267   double d0, d1, dmin, d;
5268   if ( fcount > 0 && QuadCount() > 0 )
5269   {
5270     // use SetCapacity() instead of Reserve() because it's unlikely
5271     // this mesh will have anything else added and we don't want to
5272     // waste memory.
5273     if ( m_F.Capacity() < fcount + m_quad_count )
5274       m_F.SetCapacity( fcount + m_quad_count );
5275     if ( bHasFaceNormals && m_FN.Capacity() < fcount + m_quad_count )
5276       m_FN.SetCapacity( fcount + m_quad_count );
5277 
5278     const ON_3dPoint* dV = ( vcount > 0 && HasDoublePrecisionVertices()
5279                              && ( HasSynchronizedDoubleAndSinglePrecisionVertices() || DoublePrecisionVerticesAreValid() )
5280                            )
5281                    ? DoublePrecisionVertices().Array()
5282                    : 0;
5283 
5284     const ON_3fPoint* fV = m_V.Array();
5285 
5286     const double rel_tol = 8.0*( (0 != dV) ? ON_EPSILON : ON_FLOAT_EPSILON );
5287 
5288     ON_3dVector FN;
5289 
5290     for ( fi = 0; fi < fcount; fi++ )
5291     {
5292       ON_MeshFace& f0 = m_F[fi];
5293       if ( f0.IsValid(vcount) && f0.IsQuad() )
5294       {
5295         if ( 0 != dV )
5296         {
5297           d0 = dV[f0.vi[0]].DistanceTo(dV[f0.vi[2]]);
5298           d1 = dV[f0.vi[1]].DistanceTo(dV[f0.vi[3]]);
5299 
5300           // if quad is degenerate, just turn it into a triangle
5301           idmin = -1;
5302           dmin = ((d0<=d1)?d0:d1)*rel_tol;
5303           if ( dmin > ON_ZERO_TOLERANCE )
5304             dmin = ON_ZERO_TOLERANCE;
5305           d = dV[f0.vi[0]].DistanceTo(dV[f0.vi[1]]);
5306           if ( d < dmin )
5307           {
5308             idmin = 0;
5309             dmin = d;
5310           }
5311           d = dV[f0.vi[1]].DistanceTo(dV[f0.vi[2]]);
5312           if ( d < dmin )
5313           {
5314             idmin = 1;
5315             dmin = d;
5316           }
5317           d = dV[f0.vi[2]].DistanceTo(dV[f0.vi[3]]);
5318           if ( d < dmin )
5319           {
5320             idmin = 2;
5321             dmin = d;
5322           }
5323           d = dV[f0.vi[3]].DistanceTo(dV[f0.vi[0]]);
5324           if ( d < dmin )
5325           {
5326             idmin = 3;
5327             dmin = d;
5328           }
5329         }
5330         else
5331         {
5332           d0 = m_V[f0.vi[0]].DistanceTo(m_V[f0.vi[2]]);
5333           d1 = m_V[f0.vi[1]].DistanceTo(m_V[f0.vi[3]]);
5334 
5335           // if quad is degenerate, just turn it into a triangle
5336           idmin = -1;
5337           dmin = ((d0<=d1)?d0:d1)*rel_tol;
5338           if ( dmin > ON_ZERO_TOLERANCE )
5339             dmin = ON_ZERO_TOLERANCE;
5340           d = m_V[f0.vi[0]].DistanceTo(m_V[f0.vi[1]]);
5341           if ( d < dmin )
5342           {
5343             idmin = 0;
5344             dmin = d;
5345           }
5346           d = m_V[f0.vi[1]].DistanceTo(m_V[f0.vi[2]]);
5347           if ( d < dmin )
5348           {
5349             idmin = 1;
5350             dmin = d;
5351           }
5352           d = m_V[f0.vi[2]].DistanceTo(m_V[f0.vi[3]]);
5353           if ( d < dmin )
5354           {
5355             idmin = 2;
5356             dmin = d;
5357           }
5358           d = m_V[f0.vi[3]].DistanceTo(m_V[f0.vi[0]]);
5359           if ( d < dmin )
5360           {
5361             idmin = 3;
5362             dmin = d;
5363           }
5364         }
5365 
5366         if ( !(d0 > 0.0) )
5367         {
5368           if ( !(d1 > 0.0) )
5369             continue;
5370           // d0 = 0 or is invalid and d1 > 0
5371           // force split along v[1],v[3]
5372           idmin = -1;
5373           d0 = 1.0;
5374           d1 = 0.0;
5375         }
5376         else if ( !(d1 > 0.0) )
5377         {
5378           // d1 = 0 or is invalid and d0 > 0
5379           // force split along v[0],v[1]
5380           idmin = -1;
5381           d1 = 1.0;
5382           d0 = 0.0;
5383         }
5384 
5385         m_quad_count--;
5386         m_triangle_count++;
5387         if ( 0 == idmin ) // m_V[f0.vi[0]] == m_V[f0.vi[1]] (nearly)
5388         {
5389           // degenerate quad - remove duplicate vertex
5390           f0.vi[0] = f0.vi[1];
5391           f0.vi[1] = f0.vi[2];
5392           f0.vi[2] = f0.vi[3];
5393         }
5394         else if ( 1 == idmin ) // m_V[f0.vi[1]] == m_V[f0.vi[2]] (nearly)
5395         {
5396           // degenerate quad - remove duplicate vertex
5397           int vi0 = f0.vi[0];
5398           f0.vi[0] = f0.vi[2];
5399           f0.vi[1] = f0.vi[3];
5400           f0.vi[2] = vi0;
5401           f0.vi[3] = vi0;
5402         }
5403         else if ( 2 == idmin ) // m_V[f0.vi[2]] == m_V[f0.vi[3]] (nearly)
5404         {
5405           // degenerate quad - remove duplicate vertex
5406           f0.vi[2] = f0.vi[1];
5407           f0.vi[1] = f0.vi[0];
5408           f0.vi[0] = f0.vi[3];
5409           f0.vi[3] = f0.vi[2];
5410         }
5411         else if ( 3 == idmin ) // m_V[f0.vi[3]] == m_V[f0.vi[0]] (nearly)
5412         {
5413           // degenerate quad - remove duplicate vertex
5414           f0.vi[3] = f0.vi[2];
5415         }
5416         else
5417         {
5418           // split non-degenerate quad into two triangles
5419           ON_MeshFace& f1 = m_F.AppendNew();
5420           if  ( d0 <= d1 )
5421           {
5422             f1.vi[0] = f0.vi[0];
5423             f1.vi[1] = f0.vi[2];
5424             f1.vi[2] = f0.vi[3];
5425             f1.vi[3] = f1.vi[2];
5426             f0.vi[3] = f0.vi[2];
5427           }
5428           else
5429           {
5430             f1.vi[0] = f0.vi[1];
5431             f1.vi[1] = f0.vi[2];
5432             f1.vi[2] = f0.vi[3];
5433             f1.vi[3] = f1.vi[2];
5434             f0.vi[2] = f0.vi[3];
5435           }
5436           if ( bHasFaceNormals )
5437           {
5438             if ( 0 != dV )
5439             {
5440               m_F[fi].ComputeFaceNormal(dV,FN);
5441               m_FN[fi] = FN;
5442               m_F[m_F.Count()-1].ComputeFaceNormal(dV,FN);
5443             }
5444             else
5445             {
5446               m_F[fi].ComputeFaceNormal(fV,FN);
5447               m_FN[fi] = FN;
5448               m_F[m_F.Count()-1].ComputeFaceNormal(fV,FN);
5449             }
5450             m_FN.AppendNew() = FN;
5451           }
5452         }
5453       }
5454     }
5455     if ( fcount != m_F.Count() )
5456       DestroyTopology(); // we added some triangles
5457   }
5458   return ( QuadCount() == 0 && TriangleCount() == FaceCount() ) ? true : false;
5459 }
5460 
CountQuads()5461 bool ON_Mesh::CountQuads()
5462 {
5463   const int fcount = FaceCount();
5464   const int vcount = VertexCount();
5465   int fi;
5466   m_quad_count = 0;
5467   m_triangle_count = 0;
5468   m_invalid_count = 0;
5469   for ( fi = 0; fi < fcount; fi++ ) {
5470     const ON_MeshFace& f = m_F[fi];
5471     if ( f.IsValid(vcount) ) {
5472       if ( f.IsTriangle() )
5473         m_triangle_count++;
5474       else
5475         m_quad_count++;
5476     }
5477     else
5478       m_invalid_count++;
5479   }
5480   return true;
5481 }
5482 
ComputeVertexNormals()5483 bool ON_Mesh::ComputeVertexNormals()
5484 {
5485   bool rc = false;
5486   const int fcount = FaceCount();
5487   const int vcount = VertexCount();
5488   int vi, fi, j;
5489   ON_3fVector n;
5490 
5491   if ( fcount > 0 && vcount > 0 ) {
5492     rc = HasFaceNormals();
5493     if ( !rc )
5494       rc = ComputeFaceNormals();
5495     if ( rc ) {
5496       ON_Workspace ws;
5497       //double* face_area = ws.GetDoubleMemory(fcount);
5498       int* vfcount = ws.GetIntMemory( vcount );
5499       memset ( vfcount, 0, vcount*sizeof(*vfcount) );
5500 
5501       // count number of faces that use each vertex
5502       for ( fi = 0; fi < fcount; fi++ ) {
5503         ON_MeshFace& f = m_F[fi];
5504         if ( f.IsValid(vcount) ) {
5505           vfcount[f.vi[0]]++;
5506           vfcount[f.vi[1]]++;
5507           vfcount[f.vi[2]]++;
5508           if ( f.IsQuad() )
5509             vfcount[f.vi[3]]++;
5510         }
5511       }
5512 
5513       // set vfi[vi][] = array of face indices that use vertex vi
5514       int** vfi = (int**)ws.GetMemory( vcount*sizeof(vfi[0] ) );
5515       {
5516         int scratch_sz = 0;
5517         for ( vi = 0; vi < vcount; vi++ ) {
5518           scratch_sz += vfcount[vi];
5519         }
5520         int* scratch = ws.GetIntMemory(scratch_sz);
5521         for ( vi = 0; vi < vcount; vi++ ) {
5522           if ( vfcount[vi] ) {
5523             vfi[vi] = scratch;
5524             scratch += vfcount[vi];
5525           }
5526           vfcount[vi] = 0;
5527         }
5528       }
5529       for ( fi = 0; fi < fcount; fi++ ) {
5530         ON_MeshFace& f = m_F[fi];
5531         if ( f.IsValid(vcount) ) {
5532           vi = f.vi[0]; vfi[vi][vfcount[vi]++] = fi;
5533           vi = f.vi[1]; vfi[vi][vfcount[vi]++] = fi;
5534           vi = f.vi[2]; vfi[vi][vfcount[vi]++] = fi;
5535           if ( f.IsQuad() ) {
5536             vi = f.vi[3]; vfi[vi][vfcount[vi]++] = fi;
5537           }
5538         }
5539       }
5540 
5541       // average face normals to get an estimate for a vertex normal
5542       m_N.SetCapacity(vcount);
5543       m_N.SetCount(0);
5544       for ( vi = 0; vi < vcount; vi++ ) {
5545         n.Zero();
5546         for ( j = vfcount[vi]-1; j >= 0; j-- ) {
5547           n += m_FN[vfi[vi][j]];
5548         }
5549         if ( !n.Unitize() )
5550         {
5551           // this vertex is not used by a face or the face normals cancel out.
5552           // set a unit z normal and press on.
5553           n.Set(0,0,1);
5554         }
5555         m_N.Append(n);
5556       }
5557     }
5558   }
5559   return rc;
5560 }
5561 
NormalizeTextureCoordinates()5562 bool ON_Mesh::NormalizeTextureCoordinates()
5563 {
5564   ON_2fPoint t0;//, t1;
5565   int ti;
5566   const int vertex_count = m_V.Count();
5567   bool rc = HasSurfaceParameters();
5568   if ( rc )
5569   {
5570     const ON_2dPoint* S = m_S.Array();
5571     ON_Interval udom = m_srf_domain[0];
5572     ON_Interval vdom = m_srf_domain[1];
5573     rc = udom.IsIncreasing() && vdom.IsIncreasing();
5574     if ( !rc )
5575     {
5576       udom.Set(S[0].x,S[0].x);
5577       vdom.Set(S[0].y,S[0].y);
5578       for ( ti = 1; ti < vertex_count; ti++ )
5579       {
5580         if      ( S[ti].x < udom.m_t[0] ) udom.m_t[0] = S[ti].x;
5581         else if ( S[ti].x > udom.m_t[1] ) udom.m_t[1] = S[ti].x;
5582         if      ( S[ti].y < vdom.m_t[0] ) vdom.m_t[0] = S[ti].y;
5583         else if ( S[ti].y > vdom.m_t[1] ) vdom.m_t[1] = S[ti].y;
5584       }
5585       rc = udom.IsIncreasing() && vdom.IsIncreasing();
5586     }
5587 
5588     if (rc)
5589     {
5590       m_T.Reserve(vertex_count);
5591       m_T.SetCount(0);
5592       for (ti = 0; ti < vertex_count; ti++ )
5593       {
5594         t0.x = (float)udom.NormalizedParameterAt(S[ti].x);
5595         t0.y = (float)vdom.NormalizedParameterAt(S[ti].y);
5596         m_T.Append(t0);
5597       }
5598       m_packed_tex_domain[0].Set(0.0,1.0);
5599       m_packed_tex_domain[1].Set(0.0,1.0);
5600       m_packed_tex_rotate = false;
5601       m_Ttag.SetDefaultSurfaceParameterMappingTag();
5602       if ( m_mesh_parameters )
5603         m_mesh_parameters->m_texture_range = 1;
5604     }
5605   }
5606 
5607   return rc;
5608 }
5609 
TransposeSurfaceParameters()5610 bool ON_Mesh::TransposeSurfaceParameters()
5611 {
5612 	// swap m_srf_domain
5613 	ON_Interval temp = m_srf_domain[0];
5614 	m_srf_domain[0]  = m_srf_domain[1];
5615 	m_srf_domain[1]  = temp;
5616 
5617 	double t = m_srf_scale[0];
5618 	m_srf_scale[0] = m_srf_scale[1];
5619   m_srf_scale[1] = t;
5620 
5621   int S_count = m_S.Count();
5622   ON_2dPoint* S_array = m_S.Array();
5623   for (int i = 0; i < S_count; i++ )
5624   {
5625     ON_2dPoint& S = S_array[i];
5626     t = S.x; S.x = S.y; S.y = t;
5627   }
5628   return true;
5629 }
5630 
HasPackedTextureRegion() const5631 bool ON_Mesh::HasPackedTextureRegion() const
5632 {
5633   return (    ON_IsValid(m_srf_scale[0])
5634            && m_srf_scale[0] > 0.0
5635            && ON_IsValid(m_srf_scale[1])
5636            && m_srf_scale[1] > 0.0
5637            && m_packed_tex_domain[0].IsInterval()
5638            && m_packed_tex_domain[1].IsInterval()
5639            && 0.0 <= m_packed_tex_domain[0].Min()
5640            && m_packed_tex_domain[0].Max() <= 1.0
5641            && 0.0 <= m_packed_tex_domain[1].Min()
5642            && m_packed_tex_domain[1].Max() <= 1.0
5643            && (   fabs(m_packed_tex_domain[0].Length()) < 1.0
5644                || fabs(m_packed_tex_domain[1].Length()) < 1.0)
5645          );
5646 }
5647 
5648 
TransposeTextureCoordinates()5649 bool ON_Mesh::TransposeTextureCoordinates()
5650 {
5651   if ( !HasTextureCoordinates() )
5652     return false;
5653 
5654   const int vcnt = m_T.Count();
5655   int i;
5656 
5657   bool bPackedRegion =  HasPackedTextureRegion();
5658 
5659   bool bSrfParamTag = (!m_Ttag.IsSet() || m_Ttag.IsDefaultSurfaceParameterMapping());
5660 
5661   if ( bPackedRegion && bSrfParamTag )
5662   {
5663     // The region of the bitmap the texture uses
5664     // cannot change.  The texture coordinates
5665     // themselves get reflected in the subrectangle
5666     // about either the lowerleft to upperright
5667     // diagonal (llur = true) or the lowerright
5668     // to upperleft diagonal (llur = false).
5669     bool bRevU = m_packed_tex_domain[0].IsDecreasing();
5670     bool bRevV = m_packed_tex_domain[1].IsDecreasing();
5671     bool llur = (bRevU == bRevV) ? true : false;
5672     if ( m_packed_tex_rotate )
5673       llur = !llur;
5674 
5675     ON_Interval TD[2];
5676   	TD[0] = m_packed_tex_domain[0];
5677 	  TD[1] = m_packed_tex_domain[1];
5678 	  TD[0].MakeIncreasing();
5679 	  TD[1].MakeIncreasing();
5680     for( i=0; i<vcnt; i++)
5681     {
5682 	    ON_2fPoint tc = m_T[i];
5683 	    double x = TD[0].NormalizedParameterAt(tc[0]);
5684 	    double y = TD[1].NormalizedParameterAt(tc[1]);
5685 	    if(!llur)
5686       {
5687 		    x = 1.0-x;
5688 		    y = 1.0-y;
5689 	    }
5690 	    double s = TD[0].ParameterAt(y);
5691 	    double t = TD[1].ParameterAt(x);
5692 	    m_T[i].Set((float)s,(float)t);
5693     }
5694   }
5695   else
5696   {
5697     float f;
5698 	  for(i=0; i<vcnt; i++)
5699     {
5700 		  ON_2fPoint& tc = m_T[i];
5701       f = tc.x; tc.x = tc.y; tc.y = f;
5702 	  }
5703   }
5704 	return true;
5705 }
5706 
ReverseTextureCoordinates(int dir)5707 bool ON_Mesh::ReverseTextureCoordinates( int dir )
5708 {
5709   if ( dir < 0 || dir > 1 || !HasTextureCoordinates() )
5710     return false;
5711 
5712   bool bPackedRegion =  HasPackedTextureRegion();
5713 
5714   bool bSrfParamTag = (!m_Ttag.IsSet() || m_Ttag.IsDefaultSurfaceParameterMapping());
5715 
5716   const int vcount = m_T.Count();
5717   int i;
5718   if ( bPackedRegion && bSrfParamTag )
5719   {
5720     // The region of the bitmap the texture uses
5721     // cannot change.  The texture coordinates
5722     // themselves get reflected in the subrectangle
5723     // about either the lowerleft to upperright
5724     // diagonal (llur = true) or the lowerright
5725     // to upperleft diagonal (llur = false).
5726     if ( m_packed_tex_rotate )
5727       dir = 1-dir;
5728     const ON_Interval tex_dom = m_packed_tex_domain[dir];
5729     double s;
5730     m_packed_tex_domain[dir].Swap(); // Swap() is correct - Reverse() is wrong.
5731     for (i = 0; i < vcount; i++ )
5732     {
5733       ON_2fPoint& tc = m_T[i];
5734       s = 1.0 - tex_dom.NormalizedParameterAt(tc[dir]);
5735       if ( dir )
5736         tc.y = (float)tex_dom.ParameterAt(s);
5737       else
5738         tc.x = (float)tex_dom.ParameterAt(s);
5739     }
5740   }
5741   else
5742   {
5743     for (i = 0; i < vcount; i++ )
5744     {
5745       ON_2fPoint& tc = m_T[i];
5746       if ( dir )
5747         tc.y = 1.0f-tc.y;
5748       else
5749         tc.x = 1.0f-tc.x;
5750     }
5751   }
5752 	return true;
5753 }
5754 
ReverseSurfaceParameters(int dir)5755 bool ON_Mesh::ReverseSurfaceParameters( int dir )
5756 {
5757   if ( dir < 0 || dir > 1 || !HasSurfaceParameters() )
5758     return false;
5759   if ( m_srf_domain[dir].IsIncreasing() )
5760     m_srf_domain[dir].Reverse();
5761   int i, vcount = m_S.Count();
5762   for (i = 0; i < vcount; i++)
5763   {
5764     ON_2dPoint& S = m_S[i];
5765     if ( dir )
5766       S.y = -S.y;
5767     else
5768       S.x = -S.x;
5769   }
5770   return true;
5771 }
5772 
5773 
EvaluateMeshGeometry(const ON_Surface & srf)5774 bool ON_Mesh::EvaluateMeshGeometry( const ON_Surface& srf )
5775 {
5776   bool rc = false;
5777   const int vcount = VertexCount();
5778   const bool bHasSurfaceParameters = HasSurfaceParameters();
5779   if ( bHasSurfaceParameters )
5780   {
5781     const bool bHasVertexNormals = HasVertexNormals();
5782     m_N.SetCapacity(vcount);
5783     int vi, side, hint[2];
5784     ON_3dPoint point;
5785     ON_3dVector normal, Ds, Dt, Dss, Dst, Dtt, K1, K2;
5786     const ON_2dPoint* srf_st;
5787     double s, t, kgauss, kmean;
5788     side = 0;
5789     hint[0] = 0;
5790     hint[1] = 0;
5791     const double smax = srf.Domain(0)[1];
5792     const double tmax = srf.Domain(1)[1];
5793     if ( HasPrincipalCurvatures() )
5794     {
5795       for ( vi = 0; vi < vcount; vi++ )
5796       {
5797         //ON_2fPoint& tex = m_T[vi];
5798         // convert texture coordinates to normalizied texture coordinates
5799         srf_st = &m_S[vi];
5800         s = srf_st->x;
5801         t = srf_st->y;
5802         // 19 April 2006 Dale Lear - added side setter so singular normals
5803         //                           are correctly evaluated RR 12482
5804         side = ( smax == s ) ? ((tmax == t) ? 3 : 2) : ((tmax == t) ? 4 : 1);
5805         srf.Ev2Der( s, t, point, Ds, Dt, Dss, Dst, Dtt, side, hint );
5806         ON_EvNormal( side, Ds, Dt, Dss, Dst, Dtt, normal );
5807         ON_EvPrincipalCurvatures( Ds, Dt, Dss, Dst, Dtt, normal,
5808                                   &kgauss, &kmean,
5809                                   &m_K[vi].k1, &m_K[vi].k2,
5810                                   K1, K2 ); //m_K[vi].e1, m_K[vi].e2 );
5811         m_V[vi] = &point.x; // use ON_3fPoint double* conversion (quiets gcc)
5812         if ( bHasVertexNormals )
5813           m_N[vi] = &normal.x; // use ON_3fVector double* conversion (quiets gcc)
5814       }
5815       InvalidateCurvatureStats();
5816     }
5817     else if ( bHasVertexNormals )
5818     {
5819       for ( vi = 0; vi < vcount; vi++ )
5820       {
5821         //ON_2fPoint& tex = m_T[vi];
5822         srf_st = &m_S[vi];
5823         s = srf_st->x;
5824         t = srf_st->y;
5825         // 19 April 2006 Dale Lear - added side setter so singular normals
5826         //                           are correctly evaluated RR 12482
5827         side = ( smax == s ) ? ((tmax == t) ? 3 : 2) : ((tmax == t) ? 4 : 1);
5828         srf.EvNormal( s, t, point, normal, side, hint );
5829         m_V[vi] = &point.x; // use ON_3fPoint double* conversion (quiets gcc)
5830         m_N[vi] = &normal.x; // use ON_3fVector double* conversion (quiets gcc)
5831       }
5832     }
5833     else
5834     {
5835       for ( vi = 0; vi < vcount; vi++ )
5836       {
5837         //ON_2fPoint& tex = m_T[vi];
5838         srf_st = &m_S[vi];
5839         s = srf_st->x;
5840         t = srf_st->y;
5841         srf.EvPoint( s, t, point, side, hint );
5842         m_V[vi] = &point.x;
5843       }
5844     }
5845     if ( HasFaceNormals() )
5846     {
5847       ComputeFaceNormals();
5848     }
5849     rc = true;
5850 
5851     m_Ctag.Default();
5852     InvalidateVertexBoundingBox();
5853     InvalidateVertexNormalBoundingBox();
5854     DeleteMeshParameters();
5855     DestroyTree();
5856   }
5857   return rc;
5858 }
5859 
SetMeshParameters(const ON_MeshParameters & mp)5860 void ON_Mesh::SetMeshParameters( const ON_MeshParameters& mp )
5861 {
5862   DeleteMeshParameters();
5863   m_mesh_parameters = new ON_MeshParameters(mp);
5864 }
5865 
MeshParameters() const5866 const ON_MeshParameters* ON_Mesh::MeshParameters() const
5867 {
5868   return m_mesh_parameters;
5869 }
5870 
DeleteMeshParameters()5871 void ON_Mesh::DeleteMeshParameters()
5872 {
5873   if ( m_mesh_parameters ) {
5874     delete m_mesh_parameters;
5875     m_mesh_parameters = 0;
5876   }
5877 }
5878 
compare3fPoint(const ON_3fPoint * a,const ON_3fPoint * b)5879 static int compare3fPoint( const ON_3fPoint* a, const ON_3fPoint* b )
5880 {
5881   if ( a->x < b->x ) return -1;
5882   if ( a->x > b->x ) return  1;
5883   if ( a->y < b->y ) return -1;
5884   if ( a->y > b->y ) return  1;
5885   if ( a->z < b->z ) return -1;
5886   if ( a->z > b->z ) return  1;
5887   return 0;
5888 }
5889 
compare3dPoint(const ON_3dPoint * a,const ON_3dPoint * b)5890 static int compare3dPoint( const ON_3dPoint* a, const ON_3dPoint* b )
5891 {
5892   if ( a->x < b->x ) return -1;
5893   if ( a->x > b->x ) return  1;
5894   if ( a->y < b->y ) return -1;
5895   if ( a->y > b->y ) return  1;
5896   if ( a->z < b->z ) return -1;
5897   if ( a->z > b->z ) return  1;
5898   return 0;
5899 }
5900 
5901 typedef int (*ON_COMPAR_LPVOID_LPVOID)(const void*,const void*);
5902 
5903 static
GetPointMap(int pt_count,const ON_3fPoint * fV,const ON_3dPoint * dV,ON_SimpleArray<int> & pt_map)5904 int GetPointMap( int pt_count, const ON_3fPoint* fV, const ON_3dPoint* dV, ON_SimpleArray<int>& pt_map )
5905 {
5906   // Builds a mapping array, pt_map[], such that the length of pt_map[]
5907   // is pt_count and pt_map[i] == pt_map[j] if and only if pt[i] == pt[j]
5908   // as 3d points.  The values in map[] run from 0 to max_pt_index.
5909   int vt0, vt1;
5910   ON_3fPoint fp0;
5911   ON_3dPoint dp0;
5912   int* map;
5913   int* index;
5914   int max_pt_index = 0;
5915   if ( pt_count > 0 && (dV || fV) )
5916   {
5917     index = (int*)onmalloc(pt_count*sizeof(*index));
5918 
5919     if ( dV )
5920       ON_Sort( ON::quick_sort, index, dV, pt_count, sizeof(*dV), (ON_COMPAR_LPVOID_LPVOID)compare3dPoint );
5921     else
5922       ON_Sort( ON::quick_sort, index, fV, pt_count, sizeof(*fV), (ON_COMPAR_LPVOID_LPVOID)compare3fPoint );
5923 
5924     pt_map.SetCapacity( pt_count );
5925     pt_map.SetCount( pt_count );
5926     map = pt_map.Array();
5927     for ( vt0 = 0; vt0 < pt_count; vt0++ )
5928       map[vt0] = -1;
5929 
5930     if ( dV )
5931     {
5932       for (vt0 = 0; vt0 < pt_count; vt0 = vt1, max_pt_index++)
5933       {
5934         dp0 = dV[index[vt0]];
5935         for ( vt1=vt0+1; vt1<pt_count && 0==compare3dPoint(&dp0,dV+index[vt1]); vt1++ ) {
5936           // empty
5937         }
5938         while ( vt0 < vt1 ) {
5939           map[index[vt0++]] = max_pt_index;
5940         }
5941       }
5942     }
5943     else
5944     {
5945       for (vt0 = 0; vt0 < pt_count; vt0 = vt1, max_pt_index++)
5946       {
5947         fp0 = fV[index[vt0]];
5948         for ( vt1=vt0+1; vt1<pt_count && 0==compare3fPoint(&fp0,fV+index[vt1]); vt1++ ) {
5949           // empty
5950         }
5951         while ( vt0 < vt1 ) {
5952           map[index[vt0++]] = max_pt_index;
5953         }
5954       }
5955     }
5956     onfree(index);
5957   }
5958   if ( max_pt_index == 0 )
5959     pt_map.Destroy();
5960   return max_pt_index;
5961 }
5962 
CullDegenerateFaces()5963 int ON_Mesh::CullDegenerateFaces()
5964 {
5965   // 30 December 2003 Dale Lear - fixed bug that was not culling
5966   //     topologically degenerate faces.
5967 
5968   int bad_count = 0;
5969   int degenerate_count = 0;
5970   const int fcount = m_F.Count();
5971   const int vcount = m_V.Count();
5972   ON_MeshFace f;
5973   int fi;
5974 
5975   if ( fcount > 0 )
5976   {
5977     // use GetTopologyVertexMap() instead of calling Topology() because this mesh
5978     // may have lots of bogus faces that need to be culled.
5979     ON_SimpleArray<int> topv_map;
5980     const ON_3dPoint* dV = Mesh_dV(*this);
5981     const ON_3fPoint* fV = (0 == dV) ? m_V.Array() : 0;
5982     const int topv_count = GetPointMap( m_V.Count(), fV, dV, topv_map );
5983     if ( topv_count > 0 && topv_map.Count() == m_V.Count() )
5984     {
5985       ON_Workspace ws;
5986       const int* vtop = topv_map.Array();
5987       unsigned char* bBadFace = (unsigned char*)ws.GetMemory(fcount*sizeof(*bBadFace));
5988       memset( bBadFace, 0, fcount*sizeof(*bBadFace) );
5989       for ( fi = 0; fi < fcount; fi++ )
5990       {
5991         ON_MeshFace& f0 = m_F[fi];
5992         // set f.vi[] to values of topoligical indices
5993         {
5994           int f0vi = f0.vi[0];
5995           f.vi[0] = (f0vi < 0 || f0vi >= vcount) ? -1 : vtop[f0vi];
5996 
5997           f0vi = f0.vi[1];
5998           f.vi[1] = (f0vi < 0 || f0vi >= vcount) ? -1 : vtop[f0vi];
5999 
6000           f0vi = f0.vi[2];
6001           f.vi[2] = (f0vi < 0 || f0vi >= vcount) ? -1 : vtop[f0vi];
6002 
6003           f0vi = f0.vi[3];
6004           f.vi[3] = (f0vi < 0 || f0vi >= vcount) ? -1 : vtop[f0vi];
6005         }
6006 
6007         if ( !f.IsValid(topv_count) )
6008         {
6009           degenerate_count++;
6010           //f = m_F[fi];
6011           if ( f.vi[0] == f.vi[1] || f.vi[0] < 0 || f.vi[0] >= topv_count )
6012           {
6013             f0.vi[0] = f0.vi[1];
6014             f0.vi[1] = f0.vi[2];
6015             f0.vi[2] = f0.vi[3];
6016             f.vi[0] = f.vi[1];
6017             f.vi[1] = f.vi[2];
6018             f.vi[2] = f.vi[3];
6019           }
6020 
6021           if ( f.vi[1] == f.vi[2] || f.vi[1] < 0 || f.vi[1] >= topv_count )
6022           {
6023             f0.vi[1] = f0.vi[2];
6024             f0.vi[2] = f0.vi[3];
6025             f.vi[1] = f.vi[2];
6026             f.vi[2] = f.vi[3];
6027           }
6028 
6029           if ( f.vi[2] < 0 || f.vi[2] >= topv_count )
6030           {
6031             f0.vi[2] = f0.vi[3];
6032             f.vi[2] = f.vi[3];
6033           }
6034 
6035           if ( f.vi[3] < 0 || f.vi[3] >= topv_count )
6036           {
6037             f0.vi[3] = f0.vi[2];
6038             f.vi[3] = f.vi[2];
6039           }
6040           else if ( f.vi[0] == f.vi[3] && f.vi[2] != f.vi[3] )
6041           {
6042             f0.vi[0] = f0.vi[1];
6043             f0.vi[1] = f0.vi[2];
6044             f0.vi[2] = f0.vi[3];
6045             f.vi[0] = f.vi[1];
6046             f.vi[1] = f.vi[2];
6047             f.vi[2] = f.vi[3];
6048           }
6049 
6050           if ( !f0.IsValid(vcount) || !f.IsValid(topv_count) )
6051           {
6052             // face cannot be repaired by juggling vertex indices
6053             bBadFace[fi] = 1;
6054             bad_count++;
6055           }
6056         }
6057       }
6058 
6059       if ( bad_count > 0 )
6060       {
6061         //  remove bad faces.
6062         bool bHasFN = (m_FN.Count() == m_F.Count());
6063         if ( !bHasFN )
6064           m_FN.SetCount(0);
6065 
6066         int fcnt = 0;
6067         for ( fi = fcnt = 0; fi < fcount; fi++ )
6068         {
6069           if ( !bBadFace[fi] )
6070           {
6071             if ( fcnt < fi )
6072             {
6073               m_F[fcnt] = m_F[fi];
6074               if ( bHasFN )
6075                 m_FN[fcnt] = m_FN[fi];
6076             }
6077             fcnt++;
6078           }
6079         }
6080 
6081         m_F.SetCount(fcnt);
6082         if ( bHasFN )
6083           m_FN.SetCount(fcnt);
6084       }
6085 
6086       if ( degenerate_count > 0 )
6087       {
6088         // mesh was modified - destroy information that was
6089         // calculated using the old mesh
6090         DestroyTree();
6091         DestroyPartition();
6092         DestroyTopology();
6093         m_invalid_count = 0;
6094         m_quad_count = 0;
6095         m_triangle_count = 0;
6096       }
6097     }
6098   }
6099   return degenerate_count;
6100 }
6101 
CullUnusedVertices()6102 int ON_Mesh::CullUnusedVertices()
6103 {
6104   //int cullcount = 0;
6105   int vi, fi;
6106   ON_Workspace ws;
6107   CullDegenerateFaces();
6108   int fcnt = m_F.Count();
6109   int vcnt = m_V.Count();
6110   int * vmap = ws.GetIntMemory( vcnt );
6111   memset ( vmap, 0, vcnt*sizeof(vmap[0]) );
6112   for ( fi = 0; fi < fcnt; fi++ )
6113   {
6114     const ON_MeshFace& f = m_F[fi];
6115     vmap[f.vi[0]] = 1;
6116     vmap[f.vi[1]] = 1;
6117     vmap[f.vi[2]] = 1;
6118     vmap[f.vi[3]] = 1;
6119   }
6120 
6121   int newvcnt = 0;
6122   for ( vi = 0; vi < vcnt; vi++ )
6123   {
6124     if ( vmap[vi] )
6125       vmap[vi] = newvcnt++;
6126     else {
6127       vmap[vi] = -1;
6128     }
6129   }
6130 
6131   if ( newvcnt == 0 )
6132     Destroy();
6133   else if ( newvcnt < vcnt )
6134   {
6135     DestroyTopology();
6136 
6137     // buffer will hold up to vcnt 3d points
6138     void* buffer = ws.GetMemory(vcnt*9*sizeof(double));
6139 
6140     if ( HasSurfaceParameters() ) {
6141       ON_2dPoint* s = (ON_2dPoint*)buffer;
6142       for ( vi = 0; vi < vcnt; vi++ )
6143       {
6144         if ( vmap[vi]>=0 )
6145           s[vmap[vi]] = m_S[vi];
6146       }
6147       memcpy( m_S.Array(), s, newvcnt*sizeof(s[0]) );
6148       m_S.SetCount(newvcnt);
6149     }
6150 
6151     if ( HasDoublePrecisionVertices() )
6152     {
6153       ON_3dPointArray& D = DoublePrecisionVertices();
6154       if ( vcnt == D.Count() )
6155       {
6156         bool bValidDoubles = DoublePrecisionVerticesAreValid();
6157         ON_3dPoint* s = (ON_3dPoint*)buffer;
6158         for ( vi = 0; vi < vcnt; vi++ )
6159         {
6160           if ( vmap[vi]>=0 )
6161             s[vmap[vi]] = D[vi];
6162         }
6163         memcpy( D.Array(), s, newvcnt*sizeof(s[0]) );
6164         D.SetCount(newvcnt);
6165         if ( bValidDoubles )
6166           SetDoublePrecisionVerticesAsValid();
6167       }
6168       else
6169       {
6170         DestroyDoublePrecisionVertices();
6171       }
6172     }
6173 
6174     if ( HasVertexNormals() ) {
6175       ON_3fVector* v = (ON_3fVector*)buffer;
6176       for ( vi = 0; vi < vcnt; vi++ )
6177       {
6178         if ( vmap[vi]>=0 )
6179           v[vmap[vi]] = m_N[vi];
6180       }
6181       memcpy( m_N.Array(), v, newvcnt*sizeof(v[0]) );
6182       m_N.SetCount(newvcnt);
6183     }
6184 
6185     if ( HasTextureCoordinates() ) {
6186       ON_2fPoint* t = (ON_2fPoint*)buffer;
6187       for ( vi = 0; vi < vcnt; vi++ )
6188       {
6189         if ( vmap[vi]>=0 )
6190           t[vmap[vi]] = m_T[vi];
6191       }
6192       memcpy( m_T.Array(), t, newvcnt*sizeof(t[0]) );
6193       m_T.SetCount(newvcnt);
6194     }
6195 
6196     if ( HasPrincipalCurvatures() ) {
6197       ON_SurfaceCurvature* k = (ON_SurfaceCurvature*)buffer;
6198       for ( vi = 0; vi < vcnt; vi++ )
6199       {
6200         if ( vmap[vi]>=0 )
6201           k[vmap[vi]] = m_K[vi];
6202       }
6203       memcpy( m_K.Array(), k, newvcnt*sizeof(k[0]) );
6204       m_K.SetCount(newvcnt);
6205     }
6206 
6207     if ( HasVertexColors() ) {
6208       ON_Color* c = (ON_Color*)buffer;
6209       for ( vi = 0; vi < vcnt; vi++ )
6210       {
6211         if ( vmap[vi]>=0 )
6212           c[vmap[vi]] = m_C[vi];
6213       }
6214       memcpy( m_C.Array(), c, newvcnt*sizeof(c[0]) );
6215       m_C.SetCount(newvcnt);
6216     }
6217 
6218     {
6219       bool bValidSingles = SinglePrecisionVerticesAreValid();
6220       ON_3fPoint* p = (ON_3fPoint*)buffer;
6221       for ( vi = 0; vi < vcnt; vi++ )
6222       {
6223         if ( vmap[vi]>=0 )
6224           p[vmap[vi]] = m_V[vi];
6225       }
6226       memcpy( m_V.Array(), p, newvcnt*sizeof(p[0]) );
6227       m_V.SetCount(newvcnt);
6228       if ( bValidSingles )
6229         SetSinglePrecisionVerticesAsValid();
6230     }
6231 
6232     for ( fi = 0; fi < fcnt; fi++ )
6233     {
6234       ON_MeshFace& f = m_F[fi];
6235       f.vi[0] = vmap[f.vi[0]];
6236       f.vi[1] = vmap[f.vi[1]];
6237       f.vi[2] = vmap[f.vi[2]];
6238       f.vi[3] = vmap[f.vi[3]];
6239     }
6240 
6241   }
6242   return vcnt - newvcnt;
6243 }
6244 
Compact()6245 bool ON_Mesh::Compact()
6246 {
6247   // CullDegenerateFaces(); // CullUnusedVertices() does this
6248   CullUnusedVertices();
6249   m_V.Shrink();
6250   m_F.Shrink();
6251   m_N.Shrink();
6252   m_FN.Shrink();
6253   m_K.Shrink();
6254   m_C.Shrink();
6255   m_S.Shrink();
6256   m_T.Shrink();
6257   return true;
6258 }
6259 
6260 ////////////////////////////////////////////////////////////////
6261 //
6262 //   ON_SurfaceCurvature
6263 //
GaussianCurvature() const6264 double ON_SurfaceCurvature::GaussianCurvature() const
6265 {
6266   return k1*k2;
6267 }
6268 
MeanCurvature() const6269 double ON_SurfaceCurvature::MeanCurvature() const
6270 {
6271   return 0.5*(k1+k2);
6272 }
6273 
MinimumRadius() const6274 double ON_SurfaceCurvature::MinimumRadius() const
6275 {
6276   double k;
6277   k = (fabs(k1)>=fabs(k2)) ? fabs(k1) : fabs(k2); // k = maximum directional curvature
6278   k = ( k > 1.0e-300 ) ? 1.0/k : 1.0e300;         // 1/k = minimum radius of curvature
6279   return k;
6280 }
6281 
MaximumRadius() const6282 double ON_SurfaceCurvature::MaximumRadius() const
6283 {
6284   double k;
6285   if ( k1*k2 <= 0.0 ) {
6286     // if principal curvatures have opposite signs, there
6287     // is a direction with zero directional curvature
6288     k = 0.0;
6289   }
6290   else {
6291     k = (fabs(k1)<=fabs(k2)) ? fabs(k1) : fabs(k2);
6292   }
6293   // k = minimum directional curvature
6294   k = ( k > 1.0e-300 ) ? 1.0/k : 1.0e300; // 1/k = maximum radius of curvature
6295   return k;
6296 }
6297 
6298 //double ON_SurfaceCurvature::NormalCurvature(const ON_3dVector& tangent) const
6299 //{
6300 //  double c = tangent*e1;
6301 //  double s = tangent*e2;
6302 //  return k1*c*c + k2*s*s;
6303 //}
6304 
6305 //double ON_SurfaceCurvature::NormalSectionCurvature( const ON_3dVector& section_normal, const ON_3dVector& surface_normal ) const
6306 //{
6307 //  ON_3dVector tangent = ON_CrossProduct( section_normal, surface_normal );
6308 //  if ( fabs(tangent.x) <= ON_SQRT_EPSILON && fabs(tangent.y) <= ON_SQRT_EPSILON && fabs(tangent.z) <= ON_SQRT_EPSILON )
6309 //    tangent.Zero();
6310 //  else
6311 //    tangent.Unitize();
6312 //  return NormalCurvature(tangent);
6313 //}
6314 
ON_MeshTopology()6315 ON_MeshTopology::ON_MeshTopology()
6316 : m_mesh(0)
6317 , m_memchunk(0)
6318 , m_b32IsValid(0)
6319 {
6320 }
6321 
~ON_MeshTopology()6322 ON_MeshTopology::~ON_MeshTopology()
6323 {
6324   Destroy();
6325 }
6326 
Destroy()6327 void ON_MeshTopology::Destroy()
6328 {
6329   m_topv_map.Destroy();
6330   m_topv.Destroy();
6331   m_tope.Destroy();
6332   m_topf.Destroy();
6333   struct memchunk *p, *n;
6334   n = m_memchunk;
6335   while(n)
6336   {
6337     p = n;
6338     n = n->next;
6339     onfree(p);
6340   }
6341   m_memchunk = 0;
6342   if ( -1 != m_b32IsValid)
6343     m_b32IsValid = 0;
6344 }
6345 
EmergencyDestroy()6346 void ON_MeshTopology::EmergencyDestroy()
6347 {
6348   m_mesh = 0;
6349   m_topv_map.EmergencyDestroy();
6350   m_topv.EmergencyDestroy();
6351   m_tope.EmergencyDestroy();
6352   m_topf.EmergencyDestroy();
6353   m_memchunk = 0;
6354   m_b32IsValid = 0;
6355 }
6356 
TopVertexCount() const6357 int ON_MeshTopology::TopVertexCount() const
6358 {
6359   return m_topv.Count();
6360 }
6361 
6362 //////////
6363 // number of topoligical edges
TopEdgeCount() const6364 int ON_MeshTopology::TopEdgeCount() const
6365 {
6366   return m_tope.Count();
6367 }
6368 
6369 //////////
6370 // number of topoligical faces (same as m_mesh.FaceCount())
TopFaceCount() const6371 int ON_MeshTopology::TopFaceCount() const
6372 {
6373   return m_topf.Count();
6374 }
6375 
TopVertexPoint(int vtopi) const6376 ON_3fPoint ON_MeshTopology::TopVertexPoint( int vtopi ) const
6377 {
6378   return m_mesh->m_V[m_topv[vtopi].m_vi[0]];
6379 }
6380 
TopEdgeLine(int tope_index) const6381 ON_Line ON_MeshTopology::TopEdgeLine( int tope_index ) const
6382 {
6383   ON_Line L(ON_UNSET_POINT,ON_UNSET_POINT);
6384   if ( m_mesh && tope_index >= 0 && tope_index < m_tope.Count() )
6385   {
6386     const int* topvi = m_tope[tope_index].m_topvi;
6387     if (   topvi[0] >= 0 && topvi[0] < m_topv.Count()
6388         && topvi[1] >= 0 && topvi[1] < m_topv.Count() )
6389     {
6390       const ON_MeshTopologyVertex& v0 = m_topv[topvi[0]];
6391       const ON_MeshTopologyVertex& v1 = m_topv[topvi[1]];
6392       if ( v0.m_v_count > 0 && v0.m_vi && v1.m_v_count > 0 && v1.m_vi )
6393       {
6394         int vi0 = v0.m_vi[0];
6395         int vi1 = v1.m_vi[0];
6396         int vcount = m_mesh->m_V.Count();
6397         if ( vi0 >= 0 && vi0 < vcount && vi1 >= 0 && vi1 < vcount )
6398         {
6399           L.from = m_mesh->m_V[vi0];
6400           L.to   = m_mesh->m_V[vi1];
6401         }
6402       }
6403     }
6404   }
6405   return L;
6406 }
6407 
6408 ////////
6409 // returns index of edge that connects topological vertices
6410 // returns -1 if no edge is found.
TopEdge(int vtopi0,int vtopi1) const6411 int ON_MeshTopology::TopEdge( int vtopi0, int vtopi1 ) const
6412 {
6413   int i0, i1, ei, vi0;
6414   if ( vtopi0 > vtopi1 ) {vi0 = vtopi0; vtopi0 = vtopi1; vtopi1 = vi0;}
6415   if ( vtopi0 < vtopi1 ) {
6416     const int tope_count = TopEdgeCount();
6417     const ON_MeshTopologyEdge* tope = m_tope.Array(); // to speed array access
6418     i0 = 0;
6419     i1 = tope_count;
6420     while ( i0 < i1 )
6421     {
6422       ei = (i0+i1)/2;
6423       vi0 = tope[ei].m_topvi[0];
6424       if ( vi0 < vtopi0 ) {
6425         if ( i0 == ei )
6426           break;
6427         i0 = ei;
6428       }
6429       else if ( vi0 > vtopi0 ) {
6430         if ( i1 == ei )
6431           break;
6432         i1 = ei;
6433       }
6434       else {
6435         while (ei > 0 && tope[ei-1].m_topvi[0] == vtopi0 )
6436           ei--;
6437         while ( ei < tope_count && tope[ei].m_topvi[0] == vtopi0 ) {
6438           if ( tope[ei].m_topvi[1] == vtopi1 )
6439             return ei;
6440           ei++;
6441         }
6442         break;
6443       }
6444     }
6445   }
6446   return -1;
6447 }
6448 
GetTopFaceVertices(int fi,int topvi[4]) const6449 bool ON_MeshTopology::GetTopFaceVertices( int fi, int topvi[4] ) const
6450 {
6451   if ( fi >= 0 && fi < m_mesh->m_F.Count() ) {
6452     const int* fvi = m_mesh->m_F[fi].vi;
6453     topvi[0] = m_topv_map[fvi[0]];
6454     topvi[1] = m_topv_map[fvi[1]];
6455     topvi[2] = m_topv_map[fvi[2]];
6456     topvi[3] = m_topv_map[fvi[3]];
6457   }
6458   return true;
6459 }
6460 
SortVertexEdges() const6461 bool ON_MeshTopology::SortVertexEdges() const
6462 {
6463   bool rc = true;
6464   int topvi, topv_count = m_topv.Count();
6465   for ( topvi = 0; topvi < topv_count; topvi++ )
6466   {
6467     if ( !SortVertexEdges(topvi) )
6468       rc = false;
6469   }
6470   return rc;
6471 }
6472 
6473 // TODO make public and add to header file new ON_SortIntArray
6474 static
ON_ReverseIntArray(int * e,size_t nel)6475 void ON_ReverseIntArray(
6476         int* e,    // array of ints
6477         size_t  nel   // length of array
6478         )
6479 {
6480   int ei;
6481   size_t i;
6482   nel--;
6483   for ( i = 0; i<nel; i++, nel--)
6484   {
6485     ei = e[i];
6486     e[i] = e[nel];
6487     e[nel] = ei;
6488   }
6489 }
6490 
SortVertexEdges(int topvi) const6491 bool ON_MeshTopology::SortVertexEdges(int topvi) const
6492 {
6493   if ( topvi < 0 || topvi >= m_topv.Count() )
6494     return false;
6495 
6496   const ON_MeshTopologyVertex& topv = m_topv[topvi];
6497   if ( topv.m_tope_count < 2 )
6498     return true;
6499 
6500   // Divide the edges that use this vertex into two sets:
6501   //   e1f[] = indices of edges that bound 1 face, 3 or
6502   //           more faces, or no faces (in that order).
6503   //   e2f[] = indices of edges that bound exactly 2 faces.
6504   int i, j;
6505   int topei;
6506   int vei;
6507   int efcnt;
6508   int* new_tope = (int*)alloca(5*topv.m_tope_count*sizeof(new_tope[0]));
6509   int* e2f  = new_tope + topv.m_tope_count;
6510   int* e1f  = e2f + topv.m_tope_count;
6511   int e1fcnt = 0;
6512   int e2fcnt = 0;
6513   {
6514     int* e3f  = e1f + topv.m_tope_count; // e3f[] = edges with 3 or more faces
6515     int* e0f  = e3f + topv.m_tope_count; // e0f[] = edges with no faces
6516     int e3fcnt = 0;
6517     int e0fcnt = 0;
6518 
6519     for ( vei = 0; vei < topv.m_tope_count; vei++ )
6520     {
6521       topei = topv.m_topei[vei];
6522       if ( topei >= 0 && topei < m_tope.Count() )
6523       {
6524         const ON_MeshTopologyEdge& tope = m_tope[topei];
6525         if ( tope.m_topvi[0] == topvi || tope.m_topvi[1] == topvi )
6526         {
6527           efcnt = m_tope[topei].m_topf_count;
6528           if ( efcnt < 0 )
6529           {
6530             ON_ERROR("ON_MeshTopology::SortVertexEdges(int topvi) - m_tope[topei].m_topf_count < 0");
6531             return false;
6532           }
6533           switch(efcnt)
6534           {
6535           case 0: // edge has no faces
6536             // (never happens if topology is from a valid ON_Mesh.)
6537             e0f[e0fcnt++] = topei;
6538             break;
6539 
6540           case 1: // edge has exactly one face
6541             e1f[e1fcnt++] = topei;
6542             break;
6543 
6544           case 2: // edge has exactly two faces
6545             e2f[e2fcnt++] = topei;
6546             break;
6547 
6548           default: // edge has 3 or more faces
6549             // (happens when mesh is non-manifold)
6550             e3f[e3fcnt++] = topei;
6551             break;
6552           }
6553         }
6554       }
6555     }
6556 
6557     // append list of non-manifold edges (3 or more faces) to e1f[]
6558     for ( i = 0; i < e3fcnt; i++ )
6559     {
6560       e1f[e1fcnt++] = e3f[i];
6561     }
6562 
6563     // append list of wire edges (0 faces) to e1f[]
6564     for ( i = 0; i < e0fcnt; i++ )
6565     {
6566       e1f[e1fcnt++] = e0f[i];
6567     }
6568 
6569     e0fcnt = 0;
6570     e3fcnt = 0;
6571     e0f = 0;
6572     e3f = 0;
6573   }
6574 
6575   // Put the edge indices in new_tope[] in radial order.
6576   // NOTE: The code below works for non-oriented meshes and
6577   //       non-manifold meshes.  If you change the code, you
6578   //       must make sure that it still works in these cases.
6579   if ( e1fcnt + e2fcnt != topv.m_tope_count )
6580   {
6581     ON_ERROR("ON_MeshTopology::SortVertexEdges() input vertex had bogus m_topei[]");
6582     return false;
6583   }
6584   int fi = -1;
6585   int next_topei = -1;
6586   int efi, fecnt, fei, next_fei;
6587   vei = 0;
6588   int vei0 = 0;
6589   int vei1 = 0;
6590   int elist_dir = 0;
6591   while(vei < topv.m_tope_count)
6592   {
6593     if ( next_topei >= 0 )
6594     {
6595       // continue with this group of edges
6596       topei = next_topei;
6597       next_topei = -1;
6598     }
6599     else if ( e1fcnt > 0 )
6600     {
6601       // start a new group of edges
6602       topei = *e1f++;
6603       e1fcnt--;
6604       vei1 = vei;
6605     }
6606     else if ( e2fcnt > 0 )
6607     {
6608       // start a new group of edges
6609       // (this only happens in non-manifold cases)
6610       topei = *e2f++;
6611       e2fcnt--;
6612       vei1 = vei;
6613     }
6614     else
6615     {
6616       ON_ERROR("ON_MeshTopology::SortVertexEdges() input vertex had bogus topology information.");
6617       return false;
6618     }
6619 
6620     if ( vei0 < vei1 )
6621     {
6622       if ( 1 == elist_dir )
6623       {
6624         // 30 December 2003 Dale Lear added this feature
6625         //   edges new_tope[vei0],...,new_tope[vei1-1] are radially sorted
6626         //   but the order is not counterclockwise with respect to
6627         //   the normal of the face attached to the first edge.
6628         //   So, this group of edges in new_tope[] needs to
6629         //   be reversed.
6630         ON_ReverseIntArray( new_tope+vei0, vei1-vei0 );
6631       }
6632       elist_dir = 0;
6633       vei0 = vei1;
6634     }
6635 
6636     new_tope[vei++] = topei;
6637 
6638     // search faces connected to tope for the next edge
6639     const ON_MeshTopologyEdge& tope = m_tope[topei];
6640     for ( efi = 0; next_topei < 0 && efi < tope.m_topf_count; efi++ )
6641     {
6642       fi = tope.m_topfi[efi];
6643       if ( fi < 0 || fi >= m_topf.Count() )
6644       {
6645         // bogus face index into m_topf[] array
6646         continue;
6647       }
6648 
6649       // find fei so that topf.m_topei[fei] = topei
6650       const ON_MeshTopologyFace& topf = m_topf[fi];
6651       fecnt = topf.IsQuad() ? 4 : 3;
6652       for ( fei = 0; fei < fecnt; fei++ )
6653       {
6654         if ( topf.m_topei[fei] != topei )
6655           continue;
6656 
6657         if ( tope.m_topvi[0] == topvi )
6658         {
6659           next_fei = ( topf.m_reve[fei] ) ?  1 : -1;
6660         }
6661         else
6662         {
6663           next_fei = ( topf.m_reve[fei] ) ? -1 :  1;
6664         }
6665 
6666         if ( 0 == elist_dir )
6667           elist_dir = next_fei;
6668 
6669         next_fei = (fei+next_fei+fecnt)%fecnt;
6670         next_topei = topf.m_topei[next_fei];
6671 
6672         // next_topei = candidate for the next edge
6673         //  Check to see that it is available by
6674         //  finding it in the e1f[] or e2f[] arrays.
6675         j = 0;
6676         for ( i = 0; i < e1fcnt; i++ )
6677         {
6678           if ( next_topei == e1f[i] )
6679           {
6680             // found it in the e1f list.
6681             for ( j = i+1; j < e1fcnt; j++ )
6682               e1f[j-1] = e1f[j];
6683             e1fcnt--;
6684             break;
6685           }
6686         }
6687         if ( 0 == j )
6688         {
6689           // search the e2f[] list
6690           for ( i = 0; i < e2fcnt; i++ )
6691           {
6692             if ( next_topei == e2f[i] )
6693             {
6694               for ( j = i+1; j < e2fcnt; j++ )
6695                 e2f[j-1] = e2f[j];
6696               e2fcnt--;
6697               break;
6698             }
6699           }
6700         }
6701         if ( 0 == j )
6702         {
6703           // the candidate was already used, check the next
6704           // face attached to this edge.
6705           next_topei = -1;
6706         }
6707         else
6708         {
6709           break;
6710         }
6711       }
6712     }
6713   }
6714 
6715   if ( topv.m_tope_count != vei )
6716   {
6717     ON_ERROR("ON_MeshTopology::SortVertexEdges() edge sorting error.");
6718     return false;
6719   }
6720 
6721   vei1 = vei;
6722   if ( vei0 < vei1 )
6723   {
6724     if ( 1 == elist_dir )
6725     {
6726       // 30 December 2003 Dale Lear added this feature
6727       //   edges new_tope[vei0],...,new_tope[vei1-1] are radially sorted
6728       //   but the order is not counterclockwise with respect to
6729       //   the normal of the face attached to the first edge.
6730       //   So, this group of edges in new_tope[] needs to
6731       //   be reversed.
6732       ON_ReverseIntArray( new_tope+vei0, vei1-vei0 );
6733     }
6734     elist_dir = 0;
6735     vei0 = vei1;
6736   }
6737 
6738   int* topv_m_topei = const_cast<int*>(topv.m_topei);
6739   for ( vei = 0; vei < topv.m_tope_count; vei++ )
6740   {
6741     topv_m_topei[vei] = new_tope[vei];
6742   }
6743 
6744   return true;
6745 }
6746 
GetIntArray(int length)6747 int* ON_MeshTopology::GetIntArray(int length)
6748 {
6749   int* a = 0;
6750   if ( length > 0 ) {
6751     struct memchunk* pm;
6752     pm = (struct memchunk*)onmalloc(length*sizeof(*a) + sizeof(*pm));
6753     if ( pm ) {
6754       pm->next = m_memchunk;
6755       m_memchunk = pm++;
6756       a = (int*)pm;
6757     }
6758   }
6759   return a;
6760 }
6761 
IsTriangle() const6762 bool ON_MeshTopologyFace::IsTriangle() const
6763 {
6764   return ( m_topei[2] == m_topei[3] && m_topei[0] != m_topei[1] )
6765          ? true : false;
6766 }
6767 
IsQuad() const6768 bool ON_MeshTopologyFace::IsQuad() const
6769 {
6770   return ( m_topei[2] != m_topei[3] ) ? true : false;
6771 }
6772 
IsValid() const6773 bool ON_MeshTopologyFace::IsValid() const
6774 {
6775   return ( m_topei[0] != m_topei[1] ) ? true : false;
6776 }
6777 
6778 
IsValid() const6779 bool ON_MeshTopology::IsValid() const
6780 {
6781   ON_Workspace ws;
6782   int topvi, topei, topfi, vi, fi, j, jmax, k, tfvi[4];
6783   ON_3fPoint p;
6784 
6785   WaitUntilReady(0);
6786 
6787   // simple checks
6788   if ( 1 != m_b32IsValid )
6789     return false;
6790   if ( !m_mesh )
6791     return false;
6792   if ( this != &(m_mesh->Topology()) )
6793     return false;
6794   const int v_count = m_mesh->VertexCount();
6795   const int f_count = m_mesh->FaceCount();
6796   const int topv_count = TopVertexCount();
6797   const int tope_count = TopEdgeCount();
6798   const int topf_count = TopFaceCount();
6799   if ( topv_count > v_count || topv_count < 0 )
6800     return false;
6801   if ( topv_count == 0 && v_count > 0 )
6802     return false;
6803   if ( topf_count != f_count )
6804     return false;
6805   if ( f_count > 0 && tope_count < 3 )
6806     return false;
6807   if ( m_topv_map.Count() != v_count )
6808     return false;
6809 
6810   // check vertex information
6811   for ( vi = 0; vi < v_count; vi++ ) {
6812     topvi = m_topv_map[vi];
6813     if ( topvi < 0 || topvi >= topv_count )
6814       return false;
6815   }
6816 
6817   char* vCheck = (char*)ws.GetMemory( v_count*sizeof(*vCheck) );
6818   memset( vCheck, 0, v_count*sizeof(*vCheck) );
6819   for ( topvi = 0; topvi < topv_count; topvi++ )
6820   {
6821     const ON_MeshTopologyVertex& topv = m_topv[topvi];
6822     if ( topv.m_v_count <= 0 )
6823       return false;
6824     if ( !topv.m_vi )
6825       return false;
6826     p = TopVertexPoint(topvi);
6827     for ( j = 0; j < topv.m_v_count; j++ ) {
6828       vi = topv.m_vi[j];
6829       if ( vi < 0 )
6830         return false;
6831       if ( vi >= v_count )
6832         return false;
6833       if ( vCheck[vi] != 0 )
6834         return false; // mesh.m_V[vi] is referenced multiple times
6835       if ( compare3fPoint( &p, &m_mesh->m_V[vi] ) )
6836         return false; // mesh.m_V[vi] not at same location as topv
6837       if ( m_topv_map[vi] != topvi )
6838         return false;
6839       vCheck[vi] = 1;
6840     }
6841 
6842     // check that edges in m_topei[] list have topvi has a start/end
6843     if ( topv.m_tope_count < 0 )
6844       return false;
6845     if ( topv.m_tope_count == 0 && topv.m_topei )
6846       return false; // array should be NULL
6847     if ( topv.m_tope_count > 0 && !topv.m_topei )
6848       return false; // array should not be NULL
6849     for ( j = 0; j < topv.m_tope_count; j++ ) {
6850       topei = topv.m_topei[j];
6851       if ( topei < 0 )
6852         return false;
6853       if ( topei >= tope_count )
6854         return false;
6855       const ON_MeshTopologyEdge& tope = m_tope[topei];
6856       if ( tope.m_topvi[0] != topvi && tope.m_topvi[1] != topvi )
6857         return false; // edge doesn't reference this top vtx
6858       for ( k = 0; k < j; k++ ) {
6859         if ( topv.m_topei[k] == topei )
6860           return false; // edge listed multiple times
6861       }
6862     }
6863   }
6864 
6865   // make sure every mesh.m_V[] maps to a topoligical vertex
6866   for ( vi = 0; vi < v_count; vi++ ) {
6867     if ( vCheck[vi] != 1 )
6868       return false; // mesh.m_V[vi] is not referenced
6869     topvi = m_topv_map[vi];
6870     if ( topvi < 0 )
6871       return false;
6872     if ( topvi >= topv_count )
6873       return false;
6874     const ON_MeshTopologyVertex& topv = m_topv[topvi];
6875     for ( j = 0; j < topv.m_v_count; j++ ) {
6876       if ( topv.m_vi[j] == vi )
6877         break;
6878     }
6879     if ( j >= topv.m_v_count )
6880       return false; // bogus m_topv_map[] array
6881   }
6882 
6883   // check edges
6884   for ( topei = 0; topei < tope_count; topei++ ) {
6885     const ON_MeshTopologyEdge& tope = m_tope[topei];
6886     if ( tope.m_topvi[0] < 0 || tope.m_topvi[0] >= topv_count )
6887       return false;
6888     if ( tope.m_topvi[1] < 0 || tope.m_topvi[1] >= topv_count )
6889       return false;
6890     if ( tope.m_topvi[0] == tope.m_topvi[1] )
6891       return false;
6892 
6893     const ON_MeshTopologyVertex& topv0 = m_topv[tope.m_topvi[0]];
6894     for ( j = 0; j < topv0.m_tope_count; j++ ) {
6895       if ( topv0.m_topei[j] == topei )
6896         break;
6897     }
6898     if ( j >= topv0.m_tope_count )
6899       return false; // start vtx not linked to edge
6900 
6901     const ON_MeshTopologyVertex& topv1 = m_topv[tope.m_topvi[1]];
6902     for ( j = 0; j < topv1.m_tope_count; j++ ) {
6903       if ( topv1.m_topei[j] == topei )
6904         break;
6905     }
6906     if ( j >= topv1.m_tope_count )
6907       return false; // end vtx not linked to edge
6908 
6909     if ( tope.m_topf_count < 0 )
6910       return false;
6911     if ( tope.m_topf_count == 0 && tope.m_topfi )
6912       return false; // array should be NULL
6913     if ( tope.m_topf_count > 0 && !tope.m_topfi )
6914       return false; // array should not be NULL
6915     for ( j = 0; j < tope.m_topf_count; j++ ) {
6916       fi = tope.m_topfi[j];
6917       if ( fi < 0 || fi >= f_count )
6918         return false;
6919       const ON_MeshTopologyFace& topf = m_topf[fi];
6920       for ( k = 0; k < 4; k++ ) {
6921         if ( topf.m_topei[k] == topei )
6922           break;
6923       }
6924       if ( k >= 4 )
6925         return false; // edge not in face's list
6926     }
6927   }
6928 
6929   // check faces
6930   for ( fi = 0; fi < f_count; fi++ ) {
6931     topfi = fi;
6932     const ON_MeshTopologyFace& topf = m_topf[topfi];
6933     const ON_MeshFace& f = m_mesh->m_F[fi];
6934     if ( topf.m_topei[0] < 0 || topf.m_topei[0] >= tope_count )
6935       return false;
6936     if ( topf.m_topei[1] < 0 || topf.m_topei[1] >= tope_count )
6937       return false;
6938     if ( topf.m_topei[2] < 0 || topf.m_topei[2] >= tope_count )
6939       return false;
6940     if ( topf.m_topei[3] < 0 || topf.m_topei[3] >= tope_count )
6941       return false;
6942     tfvi[0] = m_topv_map[f.vi[0]];
6943     tfvi[1] = m_topv_map[f.vi[1]];
6944     tfvi[2] = m_topv_map[f.vi[2]];
6945     tfvi[3] = m_topv_map[f.vi[3]];
6946     if (    topf.m_topei[0] != 0 || topf.m_topei[1] != 0
6947          || topf.m_topei[2] != 0 || topf.m_topei[3] != 0 ) {
6948       if ( !f.IsValid(v_count) )
6949         return false;
6950       if ( f.IsTriangle() ) {
6951         if (topf.m_topei[2] != topf.m_topei[3] )
6952           return false;
6953         jmax = 3;
6954       }
6955       else {
6956         if (topf.m_topei[2] == topf.m_topei[3] )
6957           return false;
6958         jmax = 4;
6959       }
6960       for ( j = 0; j < jmax; j++ ) {
6961         const ON_MeshTopologyEdge& tope = m_tope[topf.m_topei[j]];
6962         for ( k = 0; k < tope.m_topf_count; k++ ) {
6963           if ( tope.m_topfi[k] == topfi )
6964             break;
6965         }
6966         if ( k >= tope.m_topf_count )
6967           return false;
6968 
6969         // topedge m_tope[topf.m_topei[j]] ENDS at topvtx m_topv[tfvi[j]]
6970         if ( topf.m_reve[j] ) {
6971           if ( tope.m_topvi[1] != tfvi[(j+3)%4] )
6972             return false;
6973           if ( tope.m_topvi[0] != tfvi[j] )
6974             return false;
6975         }
6976         else {
6977           if ( tope.m_topvi[0] != tfvi[(j+3)%4] )
6978             return false;
6979           if ( tope.m_topvi[1] != tfvi[j] )
6980             return false;
6981         }
6982       }
6983     }
6984     else {
6985       // all topf.m_topei[] must be zeros
6986       if (    topf.m_topei[0] != 0 || topf.m_topei[1] != 0
6987            || topf.m_topei[2] != 0 || topf.m_topei[3] != 0 )
6988         return false;
6989     }
6990   }
6991   return true;
6992 }
6993 
Dump(ON_TextLog & dump) const6994 void ON_MeshTopology::Dump( ON_TextLog& dump ) const
6995 {
6996   ON_3fPoint p;
6997   int vi, ei, fi, j;
6998   const int topv_count = m_topv.Count();
6999   const int tope_count = m_tope.Count();
7000   const int topf_count = m_topf.Count();
7001 
7002   // topological vertex information
7003   for ( vi = 0; vi < topv_count; vi++ ) {
7004     const ON_MeshTopologyVertex& v = m_topv[vi];
7005     dump.Print("topv %d: ",vi);
7006     if (m_mesh) {
7007       // dump geometric location of this vertex
7008       p = m_mesh->m_V[v.m_vi[0]];
7009       dump.Print("{%g,%g,%g} ", p.x, p.y, p.z);
7010     }
7011 
7012     // list all mesh geometry viertices that are coincident with this
7013     // topological vertex
7014     dump.Print("(");
7015     for ( j = 0; j < v.m_v_count; j++ ) {
7016       if ( j )
7017         dump.Print(",");
7018       dump.Print("m_V[%d]",v.m_vi[j]);
7019     }
7020 
7021     // list all toplogical edges that begin/end at this topological vertex
7022     dump.Print(") (");
7023     for ( j = 0; j < v.m_tope_count; j++ ) {
7024       if ( j )
7025         dump.Print(",");
7026       dump.Print("%d",v.m_topei[j]);
7027     }
7028     dump.Print(")\n");
7029   }
7030 
7031   // topological edge information
7032   for ( ei = 0; ei < tope_count; ei++ ) {
7033     const ON_MeshTopologyEdge& e = m_tope[ei];
7034     dump.Print("tope %d: topv%d to topvv%d (", ei, e.m_topvi[0], e.m_topvi[1] );
7035     // list all mesh topolical faces attached to this topolical edge
7036     for ( j = 0; j < e.m_topf_count; j++ ) {
7037       if (j)
7038         dump.Print(",");
7039       dump.Print("f%d",e.m_topfi[j]);
7040     }
7041     dump.Print(")\n");
7042   }
7043 
7044   // topological face information
7045   // mesh m_F[] index = mesh topology m_topf[] index
7046   for ( fi = 0; fi < topf_count; fi++ ) {
7047     const ON_MeshTopologyFace& f = m_topf[fi];
7048     dump.Print("topf %d: (",fi);
7049     for ( j = 0; j < 4; j++ ) {
7050       if ( j == 3 && f.m_topei[3] == f.m_topei[2] )
7051         break; // triangular face
7052       if (j)
7053         dump.Print(",");
7054       dump.Print("%ce%d",f.m_reve[j]?'-':'+',f.m_topei[j]);
7055     }
7056     dump.Print(")\n");
7057   }
7058 }
7059 
7060 
Create()7061 bool ON_MeshTopology::Create()
7062 {
7063   // When -1 == m_b32IsValid, this ON_MeshTopology
7064   // is the m_top field on an ON_Mesh and is being
7065   // created in an ON_Mesh::Topology() call and a
7066   // sleep lock is used to keep ON_Mesh::Topology()
7067   // thread safe.
7068   //
7069   // When 0 == m_b32IsValid, this ON_MeshTopology
7070   // is being created stand alone.
7071   //
7072   // When 1 == m_b32IsValid, this ON_MeshTopology
7073   // is already created and valid.
7074 
7075   if ( 1 == m_b32IsValid )
7076     return true;
7077 
7078   const int b32IsValid0 = m_b32IsValid;
7079 
7080   if ( 0 == b32IsValid0 )
7081   {
7082     // no sleep lock is being used
7083     m_b32IsValid = -1;
7084   }
7085   int b32IsValid = b32IsValid0;
7086 
7087   while ( 0 == b32IsValid || -1 == b32IsValid )
7088   {
7089     // while() is for flow control - this is a while() {... break;} statment.
7090     Destroy();
7091     b32IsValid = 0;
7092 
7093     // build vertex topology information
7094     const int fcount = m_mesh->FaceCount();
7095     const int vcount = m_mesh->VertexCount();
7096     if ( 0 == vcount )
7097       break;
7098 
7099     int* vindex = GetIntArray(vcount);
7100     m_topv_map.SetCapacity( vcount );
7101     m_topv_map.SetCount( vcount );
7102     if (  0 == m_mesh->GetVertexLocationIds( 0, m_topv_map.Array(), vindex ) )
7103     {
7104       Destroy();
7105       break;
7106     }
7107 
7108     {
7109       int topv_count = m_topv_map[vindex[vcount-1]]+1;
7110       m_topv_map.SetCapacity( vcount );
7111       m_topv_map.SetCount( vcount );
7112       m_topv.SetCapacity( topv_count );
7113       int vt0, vt1, topvi;
7114       ON_3fPoint p0;
7115       for (vt0 = 0; vt0 < vcount; vt0 = vt1)
7116       {
7117         topvi = m_topv.Count();
7118 #if defined(ON_DEBUG)
7119         if ( topvi != m_topv_map[vindex[vt0]] )
7120         {
7121           // 20 April 2010 Dale Lear:
7122           //  If you get this error, save the mesh and tell Dale Lear.
7123           ON_ERROR("ON_MeshTopology::Create() - topvi != vertex id from GetVertexLocationIds()");
7124         }
7125 #endif
7126         ON_MeshTopologyVertex& topv = m_topv.AppendNew();
7127         topv.m_vi = vindex+vt0;
7128         for ( vt1=vt0+1; vt1<vcount && topvi == m_topv_map[vindex[vt1]]; vt1++ ) {
7129           // empty
7130         }
7131         topv.m_v_count = vt1-vt0;
7132       }
7133     }
7134 
7135     // build edge topology information
7136     const int topv_count = m_topv.Count();
7137     if ( topv_count >= 2 )
7138     {
7139 //      bool rc = false;
7140       int ei, ecnt, fi, vi0, vi1, efi, topfvi[4];
7141       ON_MeshFace f;
7142 
7143       if ( fcount > 0 && vcount > 0 )
7144       {
7145         // When working on this code be sure to test bug# 9271 and 9254 and file fsv_r4.3dm
7146         ON_Workspace ws;
7147         struct ON_MeshFaceSide* e = (struct ON_MeshFaceSide*)ws.GetMemory( 4*fcount*sizeof(*e) );
7148         ecnt = m_mesh->GetMeshFaceSideList( m_topv_map.Array(), e );
7149 
7150         if ( ecnt > 0 )
7151         {
7152 //          rc = true;
7153           ON_SortMeshFaceSidesByVertexIndex( ecnt, e );
7154 
7155           // count number of topological edges and allocate storage
7156           int etop_count = 0;
7157           ei = 0;
7158           while( ei < ecnt )
7159           {
7160             vi0 = e[ei].vi[0];
7161             vi1 = e[ei].vi[1];
7162             ei++;
7163             while (ei < ecnt && e[ei].vi[0] == vi0 && e[ei].vi[1] == vi1 )
7164               ei++;
7165             etop_count++;
7166           }
7167           m_tope.SetCapacity(etop_count);
7168 
7169           // fill in the m_tope[] array information
7170           int* efindex = GetIntArray(ecnt);
7171           for ( ei = 0; ei < ecnt; /*empty*/ )
7172           {
7173             ON_MeshTopologyEdge& tope = m_tope.AppendNew();
7174             tope.m_topvi[0] = vi0 = e[ei].vi[0];
7175             tope.m_topvi[1] = vi1 = e[ei].vi[1];
7176             tope.m_topfi = efindex;
7177             tope.m_topf_count = 0;
7178             *efindex++ = e[ei].fi;
7179             tope.m_topf_count++;
7180             ei++;
7181             while( ei < ecnt && e[ei].vi[0] == vi0 && e[ei].vi[1] == vi1 )
7182             {
7183               *efindex++ = e[ei].fi;
7184               tope.m_topf_count++;
7185               ei++;
7186             }
7187           }
7188           efindex = 0; // memory deallocated by ~ON_MeshTopology()
7189 
7190           // connect vertices to edges;
7191           ecnt = m_tope.Count();
7192           int* ve_count = (int*)onmalloc(topv_count*sizeof(*ve_count));
7193           // set ve_count[topvi] = number of edges that begin or end at m_topv[topvi]
7194           memset( ve_count, 0, topv_count*sizeof(*ve_count));
7195           for ( ei = 0; ei < ecnt; ei++ ) {
7196             const ON_MeshTopologyEdge& tope = m_tope[ei];
7197             ve_count[tope.m_topvi[0]]++;
7198             ve_count[tope.m_topvi[1]]++;
7199           }
7200           // allocate and distribute storage for the mopv.m_topei[] array
7201           int* vei = GetIntArray(2*ecnt);
7202           for ( vi0 = 0; vi0 < topv_count; vi0++ ) {
7203             ON_MeshTopologyVertex& topv = m_topv[vi0];
7204             if ( ve_count[vi0] > 0 ) {
7205               topv.m_topei = vei;
7206               vei += ve_count[vi0];
7207             }
7208           }
7209           onfree(ve_count); ve_count = 0;
7210           vei = 0; // memory deallocated by ~ON_MeshTopology()
7211 
7212           // fill in the m_topv[].m_topei[] values
7213           for ( ei = 0; ei < ecnt; ei++ ) {
7214             ON_MeshTopologyEdge& tope = m_tope[ei];
7215             ON_MeshTopologyVertex& topv0 = m_topv[tope.m_topvi[0]];
7216             ON_MeshTopologyVertex& topv1 = m_topv[tope.m_topvi[1]];
7217             vei = const_cast<int*>(topv0.m_topei);
7218             vei[topv0.m_tope_count++] = ei;
7219             vei = const_cast<int*>(topv1.m_topei);
7220             vei[topv1.m_tope_count++] = ei;
7221           }
7222 
7223           // build face topology information
7224           m_topf.SetCapacity(fcount);
7225           m_topf.SetCount(fcount);
7226           memset( m_topf.Array(), 0, fcount*sizeof(ON_MeshTopologyFace) );
7227           for ( fi = 0; fi < fcount; fi++ ) {
7228             ON_MeshTopologyFace& f = m_topf[fi];
7229             f.m_topei[0] = -1;
7230             f.m_topei[1] = -1;
7231             f.m_topei[2] = -1;
7232             f.m_topei[3] = -1;
7233           }
7234           for ( ei = 0; ei < ecnt; ei++ ) {
7235             ON_MeshTopologyEdge& tope = m_tope[ei];
7236             for (efi = 0; efi < tope.m_topf_count; efi++ ) {
7237               // Because ON_MeshFace.vi[2] == ON_MeshFace.vi[3] for triangles,
7238               // we have topf.m_topei[j] BEGIN at ON_MeshFace.vi[(j+3)%4] and END at ON_MeshFace.vi[j]
7239               fi = tope.m_topfi[efi];
7240               f = m_mesh->m_F[fi];
7241               topfvi[0] = m_topv_map[f.vi[0]];
7242               topfvi[1] = m_topv_map[f.vi[1]];
7243               topfvi[2] = m_topv_map[f.vi[2]];
7244               topfvi[3] = m_topv_map[f.vi[3]];
7245               ON_MeshTopologyFace& topf = m_topf[fi];
7246               vi0 = tope.m_topvi[0];
7247               vi1 = tope.m_topvi[1];
7248               // unroll loop for speed
7249               if      ( vi0 == topfvi[3] && vi1 == topfvi[0] ) {
7250                 topf.m_topei[0] = ei;
7251                 topf.m_reve[0] = 0;
7252               }
7253               else if ( vi0 == topfvi[0] && vi1 == topfvi[1] ) {
7254                 topf.m_topei[1] = ei;
7255                 topf.m_reve[1] = 0;
7256               }
7257               else if ( vi0 == topfvi[1] && vi1 == topfvi[2] ) {
7258                 topf.m_topei[2] = ei;
7259                 topf.m_reve[2] = 0;
7260               }
7261               else if ( vi0 == topfvi[2] && vi1 == topfvi[3] ) {
7262                 topf.m_topei[3] = ei;
7263                 topf.m_reve[3] = 0;
7264               }
7265               else if ( vi1 == topfvi[3] && vi0 == topfvi[0] ) {
7266                 topf.m_topei[0] = ei;
7267                 topf.m_reve[0] = 1;
7268               }
7269               else if ( vi1 == topfvi[0] && vi0 == topfvi[1] ) {
7270                 topf.m_topei[1] = ei;
7271                 topf.m_reve[1] = 1;
7272               }
7273               else if ( vi1 == topfvi[1] && vi0 == topfvi[2] ) {
7274                 topf.m_topei[2] = ei;
7275                 topf.m_reve[2] = 1;
7276               }
7277               else if ( vi1 == topfvi[2] && vi0 == topfvi[3] ) {
7278                 topf.m_topei[3] = ei;
7279                 topf.m_reve[3] = 1;
7280               }
7281             }
7282           }
7283           for ( fi = 0; fi < fcount; fi++ ) {
7284             ON_MeshTopologyFace& f = m_topf[fi];
7285             bool bIsGood = false;
7286             if (    f.m_topei[0] >= 0 && f.m_topei[1] >= 0 && f.m_topei[2] >=0
7287                  && f.m_topei[0] != f.m_topei[1]
7288                  && f.m_topei[1] != f.m_topei[2]
7289                  && f.m_topei[2] != f.m_topei[0]
7290                  ) {
7291               if ( m_mesh->m_F[fi].IsTriangle() ) {
7292                 bIsGood = true;
7293                 f.m_topei[3] = f.m_topei[2];
7294               }
7295               else if (   f.m_topei[3] >= 0
7296                        && f.m_topei[0] != f.m_topei[3]
7297                        && f.m_topei[1] != f.m_topei[3]
7298                        && f.m_topei[2] != f.m_topei[3] ) {
7299                 bIsGood = true;
7300               }
7301             }
7302             if ( !bIsGood ) {
7303               f.m_topei[0] = 0;
7304               f.m_topei[1] = 0;
7305               f.m_topei[2] = 0;
7306               f.m_topei[3] = 0;
7307               f.m_reve[0] = 0;
7308               f.m_reve[1] = 0;
7309               f.m_reve[2] = 0;
7310               f.m_reve[3] = 0;
7311             }
7312           }
7313 
7314         }
7315       }
7316     }
7317 
7318     b32IsValid = 1;
7319     break;
7320   }
7321 
7322   if ( -1 != b32IsValid0 )
7323   {
7324     // no sleep lock is in use
7325     m_b32IsValid = b32IsValid;
7326   }
7327 
7328   if ( 1 != b32IsValid )
7329   {
7330     Destroy();
7331   }
7332 
7333   return (1 == b32IsValid);
7334 }
7335 
7336 struct tagFPT
7337 {
7338   int x, y, z;
7339 };
7340 
7341 //static int compare_fpt( const struct tagFPT* a, const struct tagFPT* b )
7342 //{
7343 //  if ( a->x < b->x )
7344 //    return -1;
7345 //  if ( a->x > b->x )
7346 //    return 1;
7347 //  if ( a->y < b->y )
7348 //    return -1;
7349 //  if ( a->y > b->y )
7350 //    return 1;
7351 //  if ( a->z < b->z )
7352 //    return -1;
7353 //  if ( a->z > b->z )
7354 //    return 1;
7355 //  return 0;
7356 //}
7357 
compare_pmark(const int * a,const int * b)7358 static int compare_pmark( const int* a, const int* b )
7359 {
7360   if ( *a < *b )
7361     return -1;
7362   if ( *a > *b )
7363     return 1;
7364   if ( a < b )
7365     return -1;
7366   if ( a > b )
7367     return 1;
7368   return 0;
7369 }
7370 
compare_vmap(const void * a,const void * b)7371 static int compare_vmap( const void* a, const void* b )
7372 {
7373   int i = *((int*)a);
7374   int j = *((int*)b);
7375   if ( i < j )
7376     return -1;
7377   if ( i > j )
7378     return 1;
7379   return 0;
7380 }
7381 
DupVertex(ON_Mesh * mesh,int vi)7382 static int DupVertex( ON_Mesh* mesh, int vi )
7383 {
7384   int vertex_count = mesh->m_V.Count();
7385   ON_3fVector n;
7386   ON_3fPoint v;
7387   ON_Color c;
7388   ON_2fPoint t;
7389   ON_SurfaceCurvature k;
7390   v = mesh->m_V[vi];
7391   mesh->m_V.Append(v);
7392   if (mesh->m_N.Count() == vertex_count) {
7393     n = mesh->m_N[vi];
7394     mesh->m_N.Append(n);
7395   }
7396   if (mesh->m_T.Count() == vertex_count) {
7397     t = mesh->m_T[vi];
7398     mesh->m_T.Append(t);
7399   }
7400   if (mesh->m_K.Count() == vertex_count) {
7401     k = mesh->m_K[vi];
7402     mesh->m_K.Append(k);
7403   }
7404   if (mesh->m_C.Count() == vertex_count) {
7405     c = mesh->m_C[vi];
7406     mesh->m_C.Append(c);
7407   }
7408   return vertex_count;
7409 }
7410 
7411 
AddToPartition(ON_Mesh * mesh,ON_SimpleArray<int> & pmark,int vi,int partition_mark,int fi0)7412 static int AddToPartition( ON_Mesh* mesh, ON_SimpleArray<int>& pmark, int vi, int partition_mark, int fi0 )
7413 {
7414   bool b = true;
7415   int i, fi, new_vi, face_count, *fvi;
7416   i = pmark[vi];
7417   if ( !i ) {
7418     pmark[vi] = partition_mark;
7419   }
7420   else if ( i != partition_mark && i != partition_mark-1 ) {
7421     if ( i == partition_mark-2 )
7422       pmark[vi] = partition_mark-1; // vertex vi shared between two partitions
7423     else {
7424       new_vi = DupVertex(mesh,vi);
7425       face_count = mesh->m_F.Count();
7426       for ( fi = fi0; fi < face_count; fi++ ) {
7427         fvi = mesh->m_F[fi].vi;
7428         if ( fvi[0] == vi )
7429           fvi[0] = new_vi;
7430         if ( fvi[1] == vi )
7431           fvi[1] = new_vi;
7432         if ( fvi[2] == vi )
7433           fvi[2] = new_vi;
7434         if ( fvi[3] == vi )
7435           fvi[3] = new_vi;
7436       }
7437       pmark.Append(partition_mark);
7438     }
7439   }
7440   else
7441     b = false; // vertex already in this partition
7442   return b;
7443 }
7444 
ON_MeshPartition_IsValid(const ON_MeshPartition & p,const ON_Mesh & mesh)7445 bool ON_MeshPartition_IsValid( const ON_MeshPartition& p, const ON_Mesh& mesh )
7446 {
7447   bool rc = false;
7448   const int* fvi;
7449   int j, tcnt, fi, parti, partcount;
7450   partcount = p.m_part.Count();
7451   rc = ( partcount > 0 );
7452   if ( p.m_partition_max_triangle_count < 1 )
7453     rc = false;
7454   if ( p.m_partition_max_vertex_count < 3 )
7455     rc = false;
7456   for ( parti = 0; parti < partcount && rc; parti++ ) {
7457     const ON_MeshPart& part = p.m_part[parti];
7458     if ( part.triangle_count < 1 )
7459       rc = false;
7460     if ( part.vertex_count < 1 )
7461       rc = false;
7462     if ( part.vertex_count != part.vi[1] - part.vi[0] )
7463       rc = false;
7464     tcnt = 0;
7465     for ( fi = part.fi[0]; fi < part.fi[1]; fi++ ) {
7466       fvi = mesh.m_F[fi].vi;
7467       tcnt++;
7468       if ( fvi[2] != fvi[3] )
7469         tcnt++;
7470       for ( j = 0; j < 4; j++ ) {
7471         if ( fvi[j] < part.vi[0] || fvi[j] >= part.vi[1] )
7472           rc = false;
7473       }
7474     }
7475     if ( tcnt != part.triangle_count )
7476       rc = false;
7477     if ( parti ) {
7478       if ( part.fi[0] != p.m_part[parti-1].fi[1] )
7479         rc = false;
7480       if ( part.vi[0] > p.m_part[parti-1].vi[1] )
7481         rc = false;
7482     }
7483   }
7484   if ( partcount ) {
7485     if ( p.m_part[0].fi[0] != 0 || p.m_part[partcount-1].fi[1] != mesh.m_F.Count() )
7486       rc = false;
7487   }
7488   return rc;
7489 }
7490 
7491 static
ON_Mesh_CreatePartition_SortFaces(const ON_Mesh & mesh,int * fmap)7492 bool ON_Mesh_CreatePartition_SortFaces(const ON_Mesh& mesh, int* fmap )
7493 {
7494   ON_RTree rt;
7495   if ( !rt.CreateMeshFaceTree(&mesh) )
7496     return false;
7497 
7498   const int mesh_F_count = mesh.m_F.Count();
7499   int fmap_count = 0;
7500 
7501   const ON_RTreeBranch* branch;
7502   ON_RTreeIterator rit(rt);
7503   for ( rit.First(); 0 != (branch = rit.Value()); rit.Next() )
7504   {
7505     if ( fmap_count > mesh_F_count )
7506       break;
7507     fmap[fmap_count++] = (int)(branch->m_id);
7508   }
7509 
7510   if ( fmap_count != mesh_F_count )
7511   {
7512     ON_ERROR("ON_Mesh::CreatePartition unable to get fmap[]");
7513     return false;
7514   }
7515 
7516   return true;
7517 }
7518 
CreatePartition(int partition_max_vertex_count,int partition_max_triangle_count)7519 const ON_MeshPartition* ON_Mesh::CreatePartition(
7520               int partition_max_vertex_count, // maximum number of vertices in a partition
7521               int partition_max_triangle_count // maximum number of triangles in a partition
7522               )
7523 {
7524   ON_Workspace ws;
7525   //bool bNeedFaceSort = true;
7526   if ( m_partition )
7527   {
7528       //bNeedFaceSort = false;
7529     if (   m_partition->m_partition_max_triangle_count > partition_max_triangle_count
7530         || m_partition->m_partition_max_vertex_count > partition_max_vertex_count )
7531         DestroyPartition();
7532   }
7533   if ( !m_partition )
7534   {
7535     // TODO: create one
7536     struct ON_MeshPart p;
7537     int vertex_count = VertexCount();
7538     const int face_count = FaceCount();
7539     const int triangle_count = TriangleCount() + 2*QuadCount();
7540     m_partition = new ON_MeshPartition();
7541     int k = triangle_count/partition_max_triangle_count;
7542     if ( k < vertex_count/partition_max_vertex_count )
7543       k = vertex_count/partition_max_vertex_count;
7544     k++;
7545     m_partition->m_part.Reserve(k);
7546     if ( vertex_count   <= partition_max_vertex_count &&
7547          triangle_count <= partition_max_triangle_count )
7548     {
7549       m_partition->m_partition_max_vertex_count = vertex_count;
7550       m_partition->m_partition_max_triangle_count = triangle_count;
7551       memset(&p,0,sizeof(p));
7552       p.vi[0] = 0;
7553       p.vi[1] = vertex_count;
7554       p.fi[0] = 0;
7555       p.fi[1] = face_count;
7556       p.vertex_count = vertex_count;
7557       p.triangle_count = triangle_count;
7558       m_partition->m_part.Append(p);
7559     }
7560     else
7561     {
7562       int fi;
7563       int* fvi;
7564       DestroyTopology();
7565 
7566       // sort faces
7567       int* fmap = (int*)ws.GetMemory( face_count*sizeof(fmap[0]) );
7568       if ( !ON_Mesh_CreatePartition_SortFaces(*this,fmap) )
7569       {
7570         for ( fi = 0; fi < face_count; fi++ )
7571           fmap[fi] = fi;
7572       }
7573 
7574       //ON_SimpleArray<struct tagFPT> fpt(face_count);
7575       //fpt.SetCount(face_count);
7576       //if ( HasTextureCoordinates() )
7577       //{
7578       //  ON_2fPoint fcenter;
7579       //  ON_BoundingBox bbox = ON_PointListBoundingBox(2,0,m_T.Count(),2,&m_T[0].x);
7580       //  const ON_Interval txdom(bbox.m_min.x,bbox.m_max.x);
7581       //  const ON_Interval tydom(bbox.m_min.y,bbox.m_max.y);
7582       //  for ( fi = 0; fi < face_count; fi++ ) {
7583       //    fvi = m_F[fi].vi;
7584       //    if ( fvi[2] == fvi[3] ) {
7585       //      fcenter = 0.333333333333333333f*(m_T[fvi[0]] + m_T[fvi[1]] + m_T[fvi[2]]);
7586       //    }
7587       //    else {
7588       //      fcenter = 0.25f*(m_T[fvi[0]] + m_T[fvi[1]] + m_T[fvi[2]] + m_T[fvi[3]]);
7589       //    }
7590       //    fpt[fi].x = (int)floor(txdom.NormalizedParameterAt(fcenter.x)*100);
7591       //    fpt[fi].y = (int)floor(tydom.NormalizedParameterAt(fcenter.y)*100);
7592       //    fpt[fi].z = 0;
7593       //  }
7594       //}
7595       //else
7596       //{
7597       //  ON_3dPoint fcenter;
7598       //  ON_BoundingBox bbox = BoundingBox();
7599       //  const ON_Interval vxdom(bbox.m_min.x,bbox.m_max.x);
7600       //  const ON_Interval vydom(bbox.m_min.y,bbox.m_max.y);
7601       //  const ON_Interval vzdom(bbox.m_min.z,bbox.m_max.z);
7602       //  for ( fi = 0; fi < face_count; fi++ ) {
7603       //    fvi = m_F[fi].vi;
7604       //    if ( fvi[2] == fvi[3] ) {
7605       //      fcenter = 0.333333333333333333f*(m_V[fvi[0]] + m_V[fvi[1]] + m_V[fvi[2]]);
7606       //    }
7607       //    else {
7608       //      fcenter = 0.25f*(m_V[fvi[0]] + m_V[fvi[1]] + m_V[fvi[2]] + m_V[fvi[3]]);
7609       //    }
7610       //    fpt[fi].x = (int)floor(vxdom.NormalizedParameterAt(fcenter.x)*100);
7611       //    fpt[fi].y = (int)floor(vydom.NormalizedParameterAt(fcenter.y)*100);
7612       //    fpt[fi].z = (int)floor(vzdom.NormalizedParameterAt(fcenter.z)*100);
7613       //  }
7614       //}
7615       //fpt.Sort( ON::quick_sort, fmap, compare_fpt );
7616       m_F.Permute( fmap );
7617       if ( m_FN.Count() == face_count )
7618         m_FN.Permute( fmap );
7619 
7620       // sort vertices
7621       ON_SimpleArray<int>pmark(2*vertex_count);
7622       pmark.SetCount(vertex_count);
7623       pmark.Zero();
7624       int fi0, fi1, partition_mark, partition_vertex_count, partition_triangle_count;
7625       fi1 = 0;
7626       fi0 = 0;
7627       for ( partition_mark = 3, fi0 = 0; fi0 < face_count; fi0 = fi1, partition_mark += 2 )
7628       {
7629         partition_vertex_count = 0;
7630         partition_triangle_count = 0;
7631         for ( fi1 = fi0;
7632               fi1 < face_count
7633               && partition_triangle_count+2 <= partition_max_triangle_count
7634               && partition_vertex_count+4 <= partition_max_vertex_count;
7635               fi1++ )
7636         {
7637           fvi = m_F[fi1].vi;
7638           partition_triangle_count++;
7639           if ( AddToPartition( this, pmark, fvi[0], partition_mark, fi0 ) )
7640             partition_vertex_count++;
7641           if ( AddToPartition( this, pmark, fvi[1], partition_mark, fi0 ) )
7642             partition_vertex_count++;
7643           if ( AddToPartition( this, pmark, fvi[2], partition_mark, fi0 ) )
7644             partition_vertex_count++;
7645           if ( fvi[2] != fvi[3] ) {
7646             partition_triangle_count++; // quads = 2 triangles
7647             if ( AddToPartition( this, pmark, fvi[3], partition_mark, fi0 ) )
7648               partition_vertex_count++;
7649           }
7650         }
7651         if ( fi0 < fi1 ) {
7652           struct ON_MeshPart p;
7653           memset(&p,0,sizeof(p));
7654           p.fi[0] = fi0;
7655           p.fi[1] = fi1;
7656           p.vertex_count = partition_vertex_count;
7657           p.triangle_count = partition_triangle_count;
7658           m_partition->m_part.Append(p);
7659         }
7660         if ( partition_triangle_count > m_partition->m_partition_max_triangle_count )
7661           m_partition->m_partition_max_triangle_count = partition_triangle_count;
7662         if ( partition_vertex_count > m_partition->m_partition_max_vertex_count )
7663           m_partition->m_partition_max_vertex_count = partition_vertex_count;
7664       }
7665 
7666       // the calls to AddToPartition() may have increased vertex count
7667       vertex_count = m_V.Count();
7668 
7669       // sort vertices
7670       int* vmap = (int*)ws.GetMemory( vertex_count*sizeof(vmap[0]) );
7671       pmark.Sort( ON::quick_sort, vmap, compare_pmark );
7672       m_V.Permute( vmap );
7673       if ( m_N.Count() == vertex_count )
7674         m_N.Permute( vmap );
7675       if ( m_T.Count() == vertex_count )
7676         m_T.Permute( vmap );
7677       if ( m_K.Count() == vertex_count )
7678         m_K.Permute( vmap );
7679       if ( m_C.Count() == vertex_count )
7680         m_C.Permute( vmap );
7681       pmark.Permute( vmap );
7682       // pamv[] = inverse of mapv permutation
7683       int* pamv = (int*)ws.GetMemory( vertex_count*sizeof(pamv[0]) );
7684       ON_Sort( ON::quick_sort, pamv, vmap, vertex_count, sizeof(vmap[0]), compare_vmap );
7685       for ( fi = 0; fi < face_count; fi++ ) {
7686         fvi = m_F[fi].vi;
7687         fvi[0] = pamv[fvi[0]];
7688         fvi[1] = pamv[fvi[1]];
7689         fvi[2] = pamv[fvi[2]];
7690         fvi[3] = pamv[fvi[3]];
7691       }
7692 
7693       // fill in m_part.vi[]
7694       int m, pi, partition_count = m_partition->m_part.Count();
7695       int vi0, vi2, vi3;
7696       for (vi2 = 0; vi2 < vertex_count && pmark[vi2]<2; vi2++)
7697       {/*empty for body*/}
7698       vi3=vi2;
7699       for ( pi = 0; pi < partition_count; pi++ ) {
7700         vi0 = vi2;
7701         //vi1 = vi3;
7702         m = 2*pi + 4;
7703         for ( vi2 = vi3; vi2 < vertex_count && pmark[vi2] <  m; vi2++)
7704         {/*empty for body*/}
7705         for ( vi3 = vi2; vi3 < vertex_count && pmark[vi3] <= m; vi3++)
7706         {/*empty for body*/}
7707         m_partition->m_part[pi].vi[0] = vi0;
7708         m_partition->m_part[pi].vi[1] = vi3;
7709       }
7710     }
7711     // debugging - test partition
7712     if ( m_partition && !ON_MeshPartition_IsValid( *m_partition, *this ) ) {
7713       delete m_partition;
7714       m_partition = 0;
7715     }
7716   }
7717 
7718   return m_partition;
7719 }
7720 
Partition() const7721 const ON_MeshPartition* ON_Mesh::Partition() const
7722 {
7723   return m_partition;
7724 }
7725 
DestroyPartition()7726 void ON_Mesh::DestroyPartition()
7727 {
7728   if ( m_partition ) {
7729     delete m_partition;
7730     m_partition = 0;
7731   }
7732 }
7733 
ON_MeshPartition()7734 ON_MeshPartition::ON_MeshPartition()
7735 {
7736   m_partition_max_vertex_count = 0;
7737   m_partition_max_triangle_count = 0;
7738   m_part = 0;
7739 }
7740 
~ON_MeshPartition()7741 ON_MeshPartition::~ON_MeshPartition()
7742 {
7743   m_part.Destroy();
7744 }
7745 
7746 
MeshPart(const ON_MeshPart & mesh_part,ON_Mesh * mesh) const7747 ON_Mesh* ON_Mesh::MeshPart(
7748   const ON_MeshPart& mesh_part,
7749   ON_Mesh* mesh
7750   ) const
7751 {
7752   if ( this == mesh )
7753   {
7754     ON_ERROR("ON_Mesh::MeshPart this == mesh");
7755     return 0;
7756   }
7757 
7758   if ( mesh )
7759     mesh->Destroy();
7760 
7761   if (    mesh_part.fi[0] < 0
7762        || mesh_part.fi[1] > m_F.Count()
7763        || mesh_part.fi[0] > mesh_part.fi[1]
7764        )
7765   {
7766     ON_ERROR("ON_Mesh::MeshPart mesh_part.fi[] is not valid");
7767     return 0;
7768   }
7769 
7770   if (    mesh_part.vi[0] < 0
7771        || mesh_part.vi[1] > m_V.Count()
7772        || mesh_part.vi[0] >= mesh_part.vi[1]
7773        )
7774   {
7775     ON_ERROR("ON_Mesh::MeshPart mesh_part.vi[] is not valid");
7776     return 0;
7777   }
7778 
7779   const int submesh_V_count = mesh_part.vi[1] - mesh_part.vi[0];
7780   const int submesh_F_count = mesh_part.fi[1] - mesh_part.fi[0];
7781 
7782   const bool bHasVertexNormals = HasVertexNormals();
7783   const bool bHasTextureCoordinates = HasTextureCoordinates();
7784   const bool bHasVertexColors = HasVertexColors();
7785   const bool bHasFaceNormals = HasFaceNormals();
7786   const bool bHasSurfaceParameters = HasSurfaceParameters();
7787   const bool bHasPrincipalCurvatures = HasPrincipalCurvatures();
7788   const bool bHasHiddenVertices = HiddenVertexCount() > 0;
7789 
7790   ON_Mesh* submesh = (0 != mesh)
7791                    ? mesh
7792                    : new ON_Mesh(mesh_part.triangle_count,
7793                                  mesh_part.vertex_count,
7794                                  bHasVertexNormals,
7795                                  bHasTextureCoordinates
7796                                  );
7797 
7798   if ( bHasVertexColors )
7799     submesh->m_C.Reserve(submesh_V_count);
7800   if ( bHasSurfaceParameters )
7801     submesh->m_S.Reserve(submesh_V_count);
7802   if ( bHasPrincipalCurvatures )
7803     submesh->m_K.Reserve(submesh_V_count);
7804   if ( bHasHiddenVertices )
7805     submesh->m_H.Reserve(submesh_V_count);
7806   if ( bHasFaceNormals )
7807     submesh->m_FN.Reserve(submesh_F_count);
7808 
7809   // put vertex information into submesh
7810   const int vi0 = mesh_part.vi[0];
7811   const int vi1 = mesh_part.vi[1];
7812   for ( int vi = vi0; vi < vi1; vi++ )
7813   {
7814     submesh->m_V.Append(m_V[vi]);
7815     if ( bHasVertexNormals )
7816       submesh->m_N.Append(m_N[vi]);
7817     if ( bHasTextureCoordinates )
7818       submesh->m_T.Append(m_T[vi]);
7819     if ( bHasVertexColors )
7820       submesh->m_C.Append(m_C[vi]);
7821     if ( bHasSurfaceParameters )
7822       submesh->m_S.Append(m_S[vi]);
7823     if ( bHasPrincipalCurvatures )
7824       submesh->m_K.Append(m_K[vi]);
7825     if ( bHasHiddenVertices )
7826     {
7827       bool bHidden = m_H[vi];
7828       submesh->m_H.Append(bHidden);
7829       if ( bHidden )
7830         submesh->m_hidden_count++;
7831     }
7832   }
7833   if ( submesh->m_hidden_count <= 0 )
7834   {
7835     submesh->m_H.Destroy();
7836     submesh->m_hidden_count = 0;
7837   }
7838 
7839   // put face information into submesh
7840   int bad_face_count = 0;
7841   const int fi0 = mesh_part.fi[0];
7842   const int fi1 = mesh_part.fi[1];
7843   for ( int fi = fi0; fi < fi1; fi++ )
7844   {
7845     ON_MeshFace f = m_F[fi];
7846     f.vi[0] -= vi0;
7847     f.vi[1] -= vi0;
7848     f.vi[2] -= vi0;
7849     f.vi[3] -= vi0;
7850     if (    f.vi[0] >= submesh_V_count || f.vi[0] < 0
7851          || f.vi[1] >= submesh_V_count || f.vi[1] < 0
7852          || f.vi[2] >= submesh_V_count || f.vi[2] < 0
7853          || f.vi[3] >= submesh_V_count || f.vi[3] < 0
7854          )
7855     {
7856       bad_face_count++;
7857       ON_ERROR("ON_Mesh::MeshPart Invalid face in partition");
7858       continue;
7859     }
7860     submesh->m_F.Append(f);
7861     if ( bHasFaceNormals )
7862       submesh->m_FN.Append(m_FN[fi]);
7863   }
7864 
7865   if ( submesh->m_F.Count() < 1 && bad_face_count > 0 )
7866   {
7867     if ( submesh != mesh )
7868       delete submesh;
7869     else
7870       mesh->Destroy();
7871 
7872     submesh = 0;
7873   }
7874 
7875   return submesh;
7876 }
7877 
DuplicateFace(int face_index,ON_Mesh * mesh) const7878 ON_Mesh* ON_Mesh::DuplicateFace( int face_index, ON_Mesh* mesh ) const
7879 {
7880   if ( mesh == this )
7881     return 0;
7882   if ( 0 != mesh )
7883     mesh->Destroy();
7884   if ( face_index < 0 || face_index >= m_F.Count() )
7885     return 0;
7886   const int vcnt = m_V.Count();
7887   if ( vcnt < 3 )
7888     return 0;
7889 
7890   const ON_3dPoint* dV = ( HasDoublePrecisionVertices() && DoublePrecisionVerticesAreValid() )
7891                         ? DoublePrecisionVertices().Array()
7892                         : 0;
7893   const ON_3fPoint* fV = (0 == dV) ? m_V.Array() : 0;
7894   bool bHasFaceNormals = HasFaceNormals();
7895   bool bHasVertexNormals = HasVertexNormals();
7896   bool bHasVertexColors = HasVertexColors();
7897   bool bHasTextureCoordinates = HasTextureCoordinates();
7898   bool bHasSurfaceParameters = HasSurfaceParameters();
7899   bool bHasPrincipalCurvatures = HasPrincipalCurvatures();
7900 
7901   ON_MeshFace f = m_F[face_index];
7902   if ( dV )
7903   {
7904     if ( !f.IsValid(vcnt,dV) )
7905     {
7906       // invalid vertex indices - see if it can be fixed
7907       if ( !f.Repair(vcnt,dV) )
7908         return 0;
7909     }
7910   }
7911   else
7912   {
7913     if ( !f.IsValid(vcnt,fV) )
7914     {
7915       // invalid vertex indices - see if it can be fixed
7916       if ( !f.Repair(vcnt,fV) )
7917         return 0;
7918     }
7919   }
7920   const int newvcnt = f.IsTriangle() ? 3 : 4;
7921   if ( 0 == mesh )
7922     mesh = new ON_Mesh();
7923   ON_3dPointArray* newdV = 0;
7924   if ( dV )
7925   {
7926     newdV = &mesh->DoublePrecisionVertices();
7927     newdV->Reserve(newvcnt);
7928   }
7929   mesh->m_V.Reserve(newvcnt);
7930   mesh->m_F.Reserve(1);
7931   ON_MeshFace& newface = mesh->m_F.AppendNew();
7932   newface.vi[0] = 0;
7933   newface.vi[1] = 1;
7934   newface.vi[2] = 2;
7935   newface.vi[3] = (4 == newvcnt) ? 3 : newface.vi[2];
7936 
7937   if ( bHasFaceNormals )
7938   {
7939     mesh->m_FN.Reserve(1);
7940     mesh->m_FN.Append(m_FN[face_index]);
7941   }
7942 
7943   if ( bHasVertexNormals )
7944     mesh->m_N.Reserve(newvcnt);
7945   if ( bHasTextureCoordinates )
7946     mesh->m_T.Reserve(newvcnt);
7947   if ( bHasVertexColors )
7948     mesh->m_C.Reserve(newvcnt);
7949   if ( bHasSurfaceParameters )
7950     mesh->m_S.Reserve(newvcnt);
7951   if ( bHasPrincipalCurvatures )
7952     mesh->m_K.Reserve(newvcnt);
7953   for ( int vi = 0; vi < newvcnt; vi++ )
7954   {
7955     if ( dV )
7956       newdV->Append(dV[f.vi[vi]]);
7957     else
7958       mesh->m_V.Append(fV[f.vi[vi]]);
7959     if ( bHasVertexNormals )
7960       mesh->m_N.Append(m_N[f.vi[vi]]);
7961     if ( bHasTextureCoordinates )
7962       mesh->m_T.Append(m_T[f.vi[vi]]);
7963     if ( bHasVertexColors )
7964       mesh->m_C.Append(m_C[f.vi[vi]]);
7965     if ( bHasSurfaceParameters )
7966       mesh->m_S.Append(m_S[f.vi[vi]]);
7967     if ( bHasPrincipalCurvatures )
7968       mesh->m_K.Append(m_K[f.vi[vi]]);
7969   }
7970   if ( dV )
7971   {
7972     mesh->SetDoublePrecisionVerticesAsValid();
7973     mesh->UpdateSinglePrecisionVertices();
7974   }
7975 
7976   return mesh;
7977 }
7978 
7979 
7980 ON_OBJECT_IMPLEMENT(ON_MeshVertexRef,ON_Geometry,"C547B4BD-BDCD-49b6-A983-0C4A7F02E31A");
7981 
7982 ON_OBJECT_IMPLEMENT(ON_MeshEdgeRef,ON_Geometry,"ED727872-463A-4424-851F-9EC02CB0F155");
7983 
7984 ON_OBJECT_IMPLEMENT(ON_MeshFaceRef,ON_Geometry,"4F529AA5-EF8D-4c25-BCBB-162D510AA280");
7985 
ON_MeshVertexRef()7986 ON_MeshVertexRef::ON_MeshVertexRef()
7987 {
7988   m_mesh = 0;
7989   m_mesh_vi = -1;
7990   m_top_vi = -1;
7991 }
7992 
~ON_MeshVertexRef()7993 ON_MeshVertexRef::~ON_MeshVertexRef()
7994 {
7995   m_mesh = 0;
7996   m_mesh_vi = -1;
7997   m_top_vi = -1;
7998 }
7999 
operator =(const ON_MeshVertexRef & src)8000 ON_MeshVertexRef& ON_MeshVertexRef::operator=(const ON_MeshVertexRef& src)
8001 {
8002   if ( this != &src )
8003   {
8004     ON_Geometry::operator=(src);
8005     m_mesh = src.m_mesh;
8006     m_mesh_vi = src.m_mesh_vi;
8007     m_top_vi = src.m_top_vi;
8008   }
8009   return *this;
8010 }
8011 
8012 
IsValid(ON_TextLog * text_log) const8013 ON_BOOL32 ON_MeshVertexRef::IsValid( ON_TextLog* text_log ) const
8014 {
8015   if ( 0 == m_mesh )
8016   {
8017     if ( 0 != text_log )
8018     {
8019       text_log->Print("m_mesh = NULL\n");
8020     }
8021     return false;
8022   }
8023 
8024   if ( -1 != m_mesh_vi )
8025   {
8026     if ( m_mesh_vi < 0 || m_mesh_vi >= m_mesh->m_V.Count() )
8027     {
8028       if ( 0 != text_log )
8029       {
8030         text_log->Print("m_mesh_vi = %d (should have 0 <= m_mesh_vi < %d)\n",m_mesh_vi,m_mesh->m_V.Count());
8031       }
8032       return false;
8033     }
8034   }
8035   else if ( -1 == m_top_vi )
8036   {
8037     if ( 0 != text_log )
8038     {
8039       text_log->Print("m_mesh_vi = -1 and m_top_vi = -1\n");
8040     }
8041     return false;
8042   }
8043 
8044   if ( -1 != m_top_vi )
8045   {
8046     const ON_MeshTopology* top = MeshTopology();
8047     if ( 0 == top )
8048     {
8049       if ( 0 != text_log )
8050       {
8051         text_log->Print("m_top_vi = %d and MeshTopology()=NULL\n",m_top_vi);
8052       }
8053       return false;
8054     }
8055     if ( m_top_vi < 0 || m_top_vi >= top->m_tope.Count() )
8056     {
8057       if ( 0 != text_log )
8058       {
8059         text_log->Print("m_top_vi = %d (should have 0 <= m_top_vi < %d)\n",m_top_vi,top->m_topv.Count());
8060       }
8061       return false;
8062     }
8063 
8064     if ( -1 != m_mesh_vi )
8065     {
8066       const ON_MeshTopologyVertex& topv = top->m_topv[m_top_vi];
8067       int i;
8068       for ( i = 0; i < topv.m_v_count; i++ )
8069       {
8070         if ( topv.m_vi[i] == m_mesh_vi )
8071           break;
8072       }
8073       if ( i >= topv.m_v_count )
8074       {
8075         if ( 0 != text_log )
8076         {
8077           text_log->Print("m_mesh_vi=%d is not in m_top->m_topv[m_top_vi=%d].m_vi[] array.\n",
8078                           m_mesh_vi,m_top_vi);
8079 
8080         }
8081         return false;
8082       }
8083     }
8084   }
8085 
8086   return true;
8087 }
8088 
Dump(ON_TextLog & text_log) const8089 void ON_MeshVertexRef::Dump( ON_TextLog& text_log ) const
8090 {
8091   text_log.Print("m_mesh=%08x m_mesh_vi=%d m_top_vi=%d\n",
8092                   m_mesh,m_mesh_vi,m_top_vi);
8093   ON_3dPoint v = Point();
8094   if ( v.IsValid() )
8095   {
8096     text_log.PushIndent();
8097     text_log.Print("Location: ");
8098     text_log.Print(v);
8099     text_log.Print("\n");
8100     text_log.PopIndent();
8101   }
8102 
8103 }
SizeOf() const8104 unsigned int ON_MeshVertexRef::SizeOf() const
8105 {
8106   unsigned int sz = sizeof(*this) - sizeof(ON_Geometry);
8107   sz += ON_Geometry::SizeOf();
8108   return sz;
8109 }
8110 
ObjectType() const8111 ON::object_type ON_MeshVertexRef::ObjectType() const
8112 {
8113   return ON::meshvertex_object;
8114 }
8115 
8116 // overrides of virtual ON_Geometry functions
Dimension() const8117 int ON_MeshVertexRef::Dimension() const
8118 {
8119   return 3;
8120 }
8121 
GetBBox(double * boxmin,double * boxmax,ON_BOOL32 bGrowBox) const8122 ON_BOOL32 ON_MeshVertexRef::GetBBox(
8123        double* boxmin,
8124        double* boxmax,
8125        ON_BOOL32 bGrowBox
8126        ) const
8127 {
8128   bool rc = false;
8129   ON_3dPoint v = Point();
8130   if ( v.IsValid() )
8131   {
8132     rc = ON_GetPointListBoundingBox( 3, 0, 1, 3, &v.x, boxmin, boxmax, bGrowBox?true:false );
8133   }
8134   return rc;
8135 }
8136 
Transform(const ON_Xform & xform)8137 ON_BOOL32 ON_MeshVertexRef::Transform(
8138        const ON_Xform& xform
8139        )
8140 {
8141   return false;
8142 }
8143 
MeshTopology() const8144 const ON_MeshTopology* ON_MeshVertexRef::MeshTopology() const
8145 {
8146   return (0 != m_mesh) ? &m_mesh->m_top : 0;
8147 }
8148 
Point() const8149 ON_3dPoint ON_MeshVertexRef::Point() const
8150 {
8151   ON_3dPoint v = ON_UNSET_POINT;
8152   if ( 0 != m_mesh )
8153   {
8154     int vi = m_mesh_vi;
8155     if ( -1 == vi && m_top_vi >= 0 && m_top_vi < m_mesh->m_top.m_topv.Count() )
8156     {
8157       const ON_MeshTopologyVertex& topv = m_mesh->m_top.m_topv[m_top_vi];
8158       if ( topv.m_v_count > 0 )
8159         vi = topv.m_vi[0];
8160     }
8161     if ( vi >= 0 && vi < m_mesh->m_V.Count() )
8162       v = m_mesh->m_V[vi];
8163   }
8164   return v;
8165 }
8166 
MeshTopologyVertex() const8167 const ON_MeshTopologyVertex* ON_MeshVertexRef::MeshTopologyVertex() const
8168 {
8169   const ON_MeshTopologyVertex* topv = 0;
8170   if ( 0 != m_mesh && m_top_vi >= 0 && m_top_vi < m_mesh->m_top.m_topv.Count() )
8171   {
8172     topv = &m_mesh->m_top.m_topv[m_top_vi];
8173   }
8174   return topv;
8175 }
8176 
ON_MeshEdgeRef()8177 ON_MeshEdgeRef::ON_MeshEdgeRef()
8178 {
8179   m_mesh = 0;
8180   m_top_ei = -1;
8181 }
8182 
~ON_MeshEdgeRef()8183 ON_MeshEdgeRef::~ON_MeshEdgeRef()
8184 {
8185   m_mesh = 0;
8186   m_top_ei = -1;
8187 }
8188 
operator =(const ON_MeshEdgeRef & src)8189 ON_MeshEdgeRef& ON_MeshEdgeRef::operator=(const ON_MeshEdgeRef& src)
8190 {
8191   if ( this != &src )
8192   {
8193     ON_Geometry::operator=(src);
8194     m_mesh = src.m_mesh;
8195     m_top_ei = src.m_top_ei;
8196   }
8197   return *this;
8198 }
8199 
8200 
IsValid(ON_TextLog * text_log) const8201 ON_BOOL32 ON_MeshEdgeRef::IsValid( ON_TextLog* text_log ) const
8202 {
8203   if ( 0 == m_mesh)
8204   {
8205     if ( 0 != text_log )
8206     {
8207       text_log->Print("m_mesh = NULL\n");
8208     }
8209     return false;
8210   }
8211 
8212   if ( m_top_ei < 0 || m_top_ei >= m_mesh->m_top.m_tope.Count() )
8213   {
8214     if ( 0 != text_log )
8215     {
8216       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());
8217     }
8218     return false;
8219   }
8220 
8221   return true;
8222 }
8223 
Dump(ON_TextLog & text_log) const8224 void ON_MeshEdgeRef::Dump( ON_TextLog& text_log ) const
8225 {
8226   text_log.Print("m_mesh=%08x, m_top_ei=%d\n",m_mesh,m_top_ei);
8227   ON_Line line = Line();
8228   if ( line.from.IsValid() )
8229   {
8230     text_log.PushIndent();
8231     text_log.Print("Location: ");
8232     text_log.Print(line.from);
8233     text_log.Print(" to ");
8234     text_log.Print(line.to);
8235     text_log.Print("\n");
8236     text_log.PopIndent();
8237   }
8238 
8239 }
SizeOf() const8240 unsigned int ON_MeshEdgeRef::SizeOf() const
8241 {
8242   unsigned int sz = sizeof(*this) - sizeof(ON_Geometry);
8243   sz += ON_Geometry::SizeOf();
8244   return sz;
8245 }
8246 
ObjectType() const8247 ON::object_type ON_MeshEdgeRef::ObjectType() const
8248 {
8249   return ON::meshedge_object;
8250 }
8251 
8252 // overrides of virtual ON_Geometry functions
Dimension() const8253 int ON_MeshEdgeRef::Dimension() const
8254 {
8255   return 3;
8256 }
8257 
GetBBox(double * boxmin,double * boxmax,ON_BOOL32 bGrowBox) const8258 ON_BOOL32 ON_MeshEdgeRef::GetBBox(
8259        double* boxmin,
8260        double* boxmax,
8261        ON_BOOL32 bGrowBox
8262        ) const
8263 {
8264   bool rc = false;
8265   ON_Line line = Line();
8266   if ( line.from.IsValid() && line.to.IsValid() )
8267   {
8268     rc = ON_GetPointListBoundingBox( 3, 0, 2, 3, &line.from.x, boxmin, boxmax, bGrowBox?true:false );
8269   }
8270   return rc;
8271 }
8272 
Transform(const ON_Xform & xform)8273 ON_BOOL32 ON_MeshEdgeRef::Transform(
8274        const ON_Xform& xform
8275        )
8276 {
8277   return false;
8278 }
8279 
MeshTopology() const8280 const ON_MeshTopology* ON_MeshEdgeRef::MeshTopology() const
8281 {
8282   return (0 != m_mesh) ? &m_mesh->m_top : 0;
8283 }
8284 
Line() const8285 ON_Line ON_MeshEdgeRef::Line() const
8286 {
8287   ON_Line line(ON_UNSET_POINT,ON_UNSET_POINT);
8288   const ON_MeshTopologyEdge* tope = MeshTopologyEdge();
8289   if ( 0 != tope )
8290   {
8291     ON_MeshVertexRef vr;
8292     vr.m_mesh = m_mesh;
8293     vr.m_top_vi = tope->m_topvi[0];
8294     line.from = vr.Point();
8295     if ( line.from.IsValid() )
8296     {
8297       vr.m_top_vi = tope->m_topvi[1];
8298       line.to = vr.Point();
8299       if ( !line.to.IsValid() )
8300         line.from = ON_UNSET_POINT;
8301     }
8302   }
8303   return line;
8304 }
8305 
MeshTopologyEdge() const8306 const ON_MeshTopologyEdge* ON_MeshEdgeRef::MeshTopologyEdge() const
8307 {
8308   const ON_MeshTopologyEdge* tope = 0;
8309   if ( m_top_ei >= 0 )
8310   {
8311     const ON_MeshTopology* top = MeshTopology();
8312     if ( 0 != top && m_top_ei < top->m_tope.Count() )
8313       tope = &top->m_tope[m_top_ei];
8314   }
8315   return tope;
8316 }
8317 
8318 /////////////////////////////////////////////////
8319 
8320 
ON_MeshFaceRef()8321 ON_MeshFaceRef::ON_MeshFaceRef()
8322 {
8323   m_mesh = 0;
8324   m_mesh_fi = -1;
8325 }
8326 
~ON_MeshFaceRef()8327 ON_MeshFaceRef::~ON_MeshFaceRef()
8328 {
8329   m_mesh = 0;
8330   m_mesh_fi = -1;
8331 }
8332 
operator =(const ON_MeshFaceRef & src)8333 ON_MeshFaceRef& ON_MeshFaceRef::operator=(const ON_MeshFaceRef& src)
8334 {
8335   if ( this != &src )
8336   {
8337     ON_Geometry::operator=(src);
8338     m_mesh = src.m_mesh;
8339     m_mesh_fi = src.m_mesh_fi;
8340   }
8341   return *this;
8342 }
8343 
8344 
IsValid(ON_TextLog * text_log) const8345 ON_BOOL32 ON_MeshFaceRef::IsValid( ON_TextLog* text_log ) const
8346 {
8347   if ( 0 == m_mesh)
8348   {
8349     if ( 0 != text_log )
8350     {
8351       text_log->Print("m_mesh = NULL\n");
8352     }
8353     return false;
8354   }
8355 
8356   if ( m_mesh_fi < 0 || m_mesh_fi >= m_mesh->m_F.Count() )
8357   {
8358     if ( 0 != text_log )
8359     {
8360       text_log->Print("m_mesh_fi = %d (should have 0 <= m_mesh_fi < %d)\n",m_mesh_fi,m_mesh->m_F.Count());
8361     }
8362     return false;
8363   }
8364 
8365   return true;
8366 }
8367 
Dump(ON_TextLog & text_log) const8368 void ON_MeshFaceRef::Dump( ON_TextLog& text_log ) const
8369 {
8370   text_log.Print("m_mesh=%08x, m_mesh_fi=%d\n",m_mesh,m_mesh_fi);
8371 }
8372 
SizeOf() const8373 unsigned int ON_MeshFaceRef::SizeOf() const
8374 {
8375   unsigned int sz = sizeof(*this) - sizeof(ON_Geometry);
8376   sz += ON_Geometry::SizeOf();
8377   return sz;
8378 }
8379 
ObjectType() const8380 ON::object_type ON_MeshFaceRef::ObjectType() const
8381 {
8382   return ON::meshface_object;
8383 }
8384 
8385 // overrides of virtual ON_Geometry functions
Dimension() const8386 int ON_MeshFaceRef::Dimension() const
8387 {
8388   return 3;
8389 }
8390 
GetBBox(double * boxmin,double * boxmax,ON_BOOL32 bGrowBox) const8391 ON_BOOL32 ON_MeshFaceRef::GetBBox(
8392        double* boxmin,
8393        double* boxmax,
8394        ON_BOOL32 bGrowBox
8395        ) const
8396 {
8397   bool rc = false;
8398   if ( 0 != m_mesh && m_mesh_fi >= 0 && m_mesh_fi < m_mesh->m_F.Count() )
8399   {
8400     const int vcnt = m_mesh->m_V.Count();
8401     const int* Fvi = m_mesh->m_F[m_mesh_fi].vi;
8402     ON_3dPoint v[4];
8403     //int count = 0;
8404     int i;
8405     for ( i = 0; i < 4; i++ )
8406     {
8407       if ( Fvi[i] >= 0 && Fvi[i] < vcnt )
8408       {
8409         v[i] = m_mesh->m_V[Fvi[i]];
8410       }
8411       else
8412         return false;
8413     }
8414     rc = ON_GetPointListBoundingBox( 3, 0, 4, 3, &v[0].x, boxmin, boxmax, bGrowBox?true:false );
8415   }
8416   return rc;
8417 }
8418 
Transform(const ON_Xform & xform)8419 ON_BOOL32 ON_MeshFaceRef::Transform(
8420        const ON_Xform& xform
8421        )
8422 {
8423   return false;
8424 }
8425 
MeshTopology() const8426 const ON_MeshTopology* ON_MeshFaceRef::MeshTopology() const
8427 {
8428   return (0 != m_mesh) ? &m_mesh->m_top : 0;
8429 }
8430 
8431 
8432 
MeshFace() const8433 const ON_MeshFace* ON_MeshFaceRef::MeshFace() const
8434 {
8435   const ON_MeshFace* f = 0;
8436   if ( 0 != m_mesh && m_mesh_fi >= 0 && m_mesh_fi < m_mesh->m_F.Count() )
8437   {
8438     f = &m_mesh->m_F[m_mesh_fi];
8439   }
8440   return f;
8441 }
8442 
8443 
MeshTopologyFace() const8444 const ON_MeshTopologyFace* ON_MeshFaceRef::MeshTopologyFace() const
8445 {
8446   const ON_MeshTopologyFace* topf = 0;
8447   if ( m_mesh_fi >= 0 )
8448   {
8449     const ON_MeshTopology* top = MeshTopology();
8450     if ( 0 != top && m_mesh_fi < top->m_topf.Count() )
8451       topf = &top->m_topf[m_mesh_fi];
8452   }
8453   return topf;
8454 }
8455 
VertexRef(int mesh_V_index) const8456 ON_MeshVertexRef ON_Mesh::VertexRef(int mesh_V_index) const
8457 {
8458   ON_MeshVertexRef vr;
8459   if ( mesh_V_index >= 0 && mesh_V_index < m_V.Count() )
8460   {
8461     vr.m_mesh = this;
8462     vr.m_mesh_vi = mesh_V_index;
8463     if ( m_top.m_topv_map.Count() == m_V.Count() )
8464     {
8465       vr.m_top_vi = m_top.m_topv_map[mesh_V_index];
8466     }
8467   }
8468 
8469   return vr;
8470 }
8471 
VertexRef(ON_COMPONENT_INDEX ci) const8472 ON_MeshVertexRef ON_Mesh::VertexRef(ON_COMPONENT_INDEX ci) const
8473 {
8474   ON_MeshVertexRef vr;
8475 
8476   //int vertex_index = -1;
8477   switch (ci.m_type)
8478   {
8479   case ON_COMPONENT_INDEX::meshtop_vertex:
8480     vr = m_top.VertexRef(ci);
8481     break;
8482   case ON_COMPONENT_INDEX::mesh_vertex:
8483     if ( ci.m_index >= 0 && ci.m_index < m_V.Count() )
8484     {
8485       vr.m_mesh = this;
8486       vr.m_mesh_vi = ci.m_index;
8487       if ( m_top.m_topv_map.Count() == m_V.Count() )
8488       {
8489         vr.m_top_vi = m_top.m_topv_map[vr.m_mesh_vi];
8490       }
8491     }
8492     break;
8493   default:
8494     // intentionally skipping other enum values (also quiets gcc warnings)
8495     break;
8496   }
8497 
8498   return vr;
8499 }
8500 
8501 
EdgeRef(int tope_index) const8502 ON_MeshEdgeRef ON_Mesh::EdgeRef(int tope_index) const
8503 {
8504   return m_top.EdgeRef(tope_index);
8505 }
8506 
EdgeRef(ON_COMPONENT_INDEX ci) const8507 ON_MeshEdgeRef ON_Mesh::EdgeRef(ON_COMPONENT_INDEX ci) const
8508 {
8509   return m_top.EdgeRef(ci);
8510 }
8511 
FaceRef(int mesh_F_index) const8512 ON_MeshFaceRef ON_Mesh::FaceRef(int mesh_F_index) const
8513 {
8514   ON_MeshFaceRef fr;
8515 
8516   if ( mesh_F_index >= 0 && mesh_F_index < m_F.Count() )
8517   {
8518     fr.m_mesh = this;
8519     fr.m_mesh_fi = mesh_F_index;
8520   }
8521 
8522   return fr;
8523 }
8524 
FaceRef(ON_COMPONENT_INDEX ci) const8525 ON_MeshFaceRef ON_Mesh::FaceRef(ON_COMPONENT_INDEX ci) const
8526 {
8527   ON_MeshFaceRef fr;
8528 
8529   if ( ci.m_type == ON_COMPONENT_INDEX::mesh_face )
8530   {
8531     if ( ci.m_index >= 0 && ci.m_index < m_F.Count() )
8532     {
8533       fr.m_mesh = this;
8534       fr.m_mesh_fi = ci.m_index;
8535     }
8536   }
8537 
8538   return fr;
8539 }
8540 
VertexRef(ON_COMPONENT_INDEX ci) const8541 ON_MeshVertexRef ON_MeshTopology::VertexRef(ON_COMPONENT_INDEX ci) const
8542 {
8543   ON_MeshVertexRef vr;
8544   if ( ci.m_index >= 0 )
8545   {
8546     switch(ci.m_type)
8547     {
8548     case ON_COMPONENT_INDEX::mesh_vertex:
8549       if (m_mesh)
8550       {
8551         vr = m_mesh->VertexRef(ci);
8552       }
8553       break;
8554     case ON_COMPONENT_INDEX::meshtop_vertex:
8555       if ( ci.m_index < m_topv.Count() )
8556       {
8557         vr.m_mesh = m_mesh;
8558         vr.m_top_vi = ci.m_index;
8559         if ( m_topv[ci.m_index].m_vi && 1 == m_topv[ci.m_index].m_v_count )
8560         {
8561           vr.m_mesh_vi = m_topv[ci.m_index].m_vi[0];
8562         }
8563       }
8564       break;
8565     default:
8566       // intentionally skip other ON_COMPONENT_INDEX::TYPE
8567       // enum values.
8568       break;
8569     }
8570   }
8571   return vr;
8572 }
8573 
VertexRef(int topv_index) const8574 ON_MeshVertexRef ON_MeshTopology::VertexRef(int topv_index) const
8575 {
8576   ON_MeshVertexRef vr;
8577 
8578   if ( topv_index >= 0 && topv_index < m_topv.Count() )
8579   {
8580     vr.m_mesh = m_mesh;
8581     vr.m_top_vi = topv_index;
8582     if ( 1 == m_topv[topv_index].m_v_count )
8583     {
8584       vr.m_mesh_vi = m_topv[topv_index].m_vi[0];
8585     }
8586   }
8587 
8588   return vr;
8589 }
8590 
EdgeRef(ON_COMPONENT_INDEX ci) const8591 ON_MeshEdgeRef ON_MeshTopology::EdgeRef(ON_COMPONENT_INDEX ci) const
8592 {
8593   ON_MeshEdgeRef er;
8594   if ( ON_COMPONENT_INDEX::meshtop_edge == ci.m_type && ci.m_index >= 0 && ci.m_index < m_tope.Count() )
8595   {
8596     er.m_mesh = m_mesh;
8597     er.m_top_ei = ci.m_index;
8598   }
8599   return er;
8600 }
8601 
EdgeRef(int tope_index) const8602 ON_MeshEdgeRef ON_MeshTopology::EdgeRef(int tope_index) const
8603 {
8604   ON_MeshEdgeRef er;
8605 
8606   if ( tope_index >= 0 && tope_index < m_tope.Count() )
8607   {
8608     er.m_mesh = m_mesh;
8609     er.m_top_ei = tope_index;
8610   }
8611 
8612   return er;
8613 }
8614 
FaceRef(ON_COMPONENT_INDEX ci) const8615 ON_MeshFaceRef ON_MeshTopology::FaceRef(ON_COMPONENT_INDEX ci) const
8616 {
8617   ON_MeshFaceRef fr;
8618   if ( m_mesh )
8619   {
8620     fr = m_mesh->FaceRef(ci.m_index);
8621   }
8622   return fr;
8623 }
8624 
FaceRef(int topf_index) const8625 ON_MeshFaceRef ON_MeshTopology::FaceRef(int topf_index) const
8626 {
8627   return ( 0 != m_mesh ) ? m_mesh->FaceRef(topf_index) : ON_MeshFaceRef();
8628 }
8629 
8630 
8631 /*
8632 ON_Mesh::COMPONENT_TYPE ON_Mesh::ComponentIndexType( int component_index )
8633 {
8634   switch( (mesh_component_mask & component_index) )
8635   {
8636   case mesh_vertex:    return mesh_vertex;
8637   case meshtop_vertex: return meshtop_vertex;
8638   case meshtop_edge:   return meshtop_edge;
8639   case mesh_face:      return mesh_face;
8640   }
8641   return mesh_component_unset;
8642 }
8643 */
8644 
ComponentIndex() const8645 ON_COMPONENT_INDEX ON_MeshVertexRef::ComponentIndex() const
8646 {
8647   ON_COMPONENT_INDEX ci(ON_COMPONENT_INDEX::invalid_type,-1);
8648   if ( m_mesh_vi >= 0 )
8649   {
8650     if ( 0 == m_mesh || m_mesh_vi < m_mesh->m_V.Count() )
8651     {
8652       ci.Set(ON_COMPONENT_INDEX::mesh_vertex,m_mesh_vi);
8653     }
8654   }
8655   else if ( m_top_vi >= 0 )
8656   {
8657     if ( 0 == m_mesh )
8658     {
8659       // not enough information to perform validation check
8660       // assume this is a persistent index to be resolved
8661       // later and press on
8662       ci.Set(ON_COMPONENT_INDEX::meshtop_vertex,m_top_vi);
8663     }
8664     else if (m_top_vi < m_mesh->m_V.Count())
8665     {
8666       if ( 0 == m_mesh->m_top.m_topv.Count() )
8667       {
8668         // not enough information to perform validation check
8669         // assume this is a persistent index to be resolved
8670         // later and press on
8671         ci.Set(ON_COMPONENT_INDEX::meshtop_vertex,m_top_vi);
8672       }
8673       else if ( m_top_vi < m_mesh->m_top.m_topv.Count() )
8674       {
8675         // m_top is constructed and m_top_vi is valid
8676         ci.Set(ON_COMPONENT_INDEX::meshtop_vertex,m_top_vi);
8677       }
8678     }
8679   }
8680   return ci;
8681 }
8682 
ComponentIndex() const8683 ON_COMPONENT_INDEX ON_MeshEdgeRef::ComponentIndex() const
8684 {
8685   ON_COMPONENT_INDEX ci(ON_COMPONENT_INDEX::invalid_type,-1);
8686   if ( m_top_ei >= 0 )
8687   {
8688     if ( 0 == m_mesh || 0 == m_mesh->m_top.m_tope.Count() )
8689     {
8690       // not enough information to perform validation check
8691       // assume this is a persistent index to be resolved
8692       // later and press on
8693       ci.Set(ON_COMPONENT_INDEX::meshtop_edge,m_top_ei);
8694     }
8695     else if ( m_top_ei < m_mesh->m_top.m_tope.Count() )
8696     {
8697       // valid index
8698       ci.Set(ON_COMPONENT_INDEX::meshtop_edge,m_top_ei);
8699     }
8700   }
8701   return ci;
8702 }
8703 
ComponentIndex() const8704 ON_COMPONENT_INDEX ON_MeshFaceRef::ComponentIndex() const
8705 {
8706   ON_COMPONENT_INDEX ci(ON_COMPONENT_INDEX::invalid_type,-1);
8707   if ( m_mesh_fi >= 0 )
8708   {
8709     if ( 0 == m_mesh || m_mesh_fi < m_mesh->m_F.Count() )
8710     {
8711       ci.Set(ON_COMPONENT_INDEX::mesh_face,m_mesh_fi);
8712     }
8713   }
8714   return ci;
8715 }
8716 
MeshComponent(ON_COMPONENT_INDEX ci) const8717 ON_Geometry* ON_Mesh::MeshComponent(
8718   ON_COMPONENT_INDEX ci
8719   ) const
8720 {
8721   ON_Geometry* component = 0;
8722   if ( ci.m_index >= 0 )
8723   {
8724     switch( ci.m_type )
8725     {
8726     case ON_COMPONENT_INDEX::mesh_vertex:
8727       {
8728         ON_MeshVertexRef r = VertexRef(ci);
8729         component = new ON_MeshVertexRef(r);
8730       }
8731       break;
8732 
8733     case ON_COMPONENT_INDEX::meshtop_vertex:
8734       {
8735         ON_MeshVertexRef r = Topology().VertexRef(ci);
8736         component = new ON_MeshVertexRef(r);
8737       }
8738       break;
8739 
8740     case ON_COMPONENT_INDEX::meshtop_edge:
8741       {
8742         ON_MeshEdgeRef r = EdgeRef(ci);
8743         component = new ON_MeshEdgeRef(r);
8744       }
8745       break;
8746 
8747     case ON_COMPONENT_INDEX::mesh_face:
8748       {
8749         ON_MeshFaceRef r = FaceRef(ci);
8750         component = new ON_MeshFaceRef(r);
8751       }
8752       break;
8753 
8754     default:
8755       // intentionally skip other ON_COMPONENT_INDEX::TYPE
8756       // enum values.
8757       break;
8758     }
8759   }
8760   return component;
8761 }
8762 
ON_TriangleNormal(const ON_3dPoint & A,const ON_3dPoint & B,const ON_3dPoint & C)8763 ON_3dVector ON_TriangleNormal(
8764         const ON_3dPoint& A,
8765         const ON_3dPoint& B,
8766         const ON_3dPoint& C
8767         )
8768 {
8769   // N = normal to triangle's plane
8770   ON_3dVector N;
8771   double a, b, c, d;
8772   N.x = A.y*(B.z-C.z) + B.y*(C.z-A.z) + C.y*(A.z-B.z);
8773   N.y = A.z*(B.x-C.x) + B.z*(C.x-A.x) + C.z*(A.x-B.x);
8774   N.z = A.x*(B.y-C.y) + B.x*(C.y-A.y) + C.x*(A.y-B.y);
8775 
8776   a = fabs(N.x);
8777   b = fabs(N.y);
8778   c = fabs(N.z);
8779   if ( b > a )
8780   {
8781     if ( c > b )
8782     {
8783       // c is largest
8784       if ( c > ON_DBL_MIN )
8785       {
8786         a /= c; b /= c; d = c*sqrt(1.0 + a*a + b*b);
8787       }
8788       else
8789       {
8790         d = c;
8791       }
8792     }
8793     else
8794     {
8795       if ( b > ON_DBL_MIN )
8796       {
8797         // b is largest
8798         a /= b; c /= b; d = b*sqrt(1.0 + c*c + a*a);
8799       }
8800       else
8801       {
8802         d = b;
8803       }
8804     }
8805   }
8806   else if ( c > a )
8807   {
8808     // c is largest
8809     if ( c > ON_DBL_MIN )
8810     {
8811       a /= c; b /= c; d = c*sqrt(1.0 + a*a + b*b);
8812     }
8813     else
8814     {
8815       d = c;
8816     }
8817   }
8818   else if ( a > ON_DBL_MIN )
8819   {
8820     // a is largest
8821     b /= a; c /= a; d = a*sqrt(1.0 + b*b + c*c);
8822   }
8823   else
8824   {
8825     d = a;
8826   }
8827 
8828   if ( d > 0.0 )
8829   {
8830     N.x /= d; N.y /= d; N.z /= d;
8831   }
8832 
8833   return N;
8834 }
8835 
ON_GetTrianglePlaneEquation(const ON_3dPoint & A,const ON_3dPoint & B,const ON_3dPoint & C,double * a,double * b,double * c,double * d,double * evaluation_tol)8836 bool ON_GetTrianglePlaneEquation(
8837         const ON_3dPoint& A,
8838         const ON_3dPoint& B,
8839         const ON_3dPoint& C,
8840         double* a,
8841         double* b,
8842         double* c,
8843         double* d,
8844         double* evaluation_tol
8845         )
8846 {
8847   const ON_3dVector N(ON_TriangleNormal(A,B,C));
8848   const double dd( -(N.x*A.x + N.y*A.y + N.z*A.z) );
8849 
8850   *a = N.x;
8851   *b = N.y;
8852   *c = N.z;
8853   *d = dd;
8854 
8855   if ( 0 != evaluation_tol )
8856   {
8857     *evaluation_tol = fabs(N.x*A.x + N.y*A.y + N.z*A.z + dd);
8858     double e = fabs(N.x*B.x + N.y*B.y + N.z*B.z + dd);
8859     if ( e > *evaluation_tol )
8860       *evaluation_tol = e;
8861     e = fabs(N.x*C.x + N.y*C.y + N.z*C.z + dd);
8862     if ( e > *evaluation_tol )
8863       *evaluation_tol = e;
8864     *evaluation_tol *= (1.0 + ON_EPSILON);
8865   }
8866 
8867   return (0.0 != N.x || 0.0 != N.y || 0.0 != N.z);
8868 }
8869 
HiddenVertexArray() const8870 const bool* ON_Mesh::HiddenVertexArray() const
8871 {
8872   return ( ( m_hidden_count > 0 && m_H.Count() == m_V.Count() ) ? m_H.Array() : 0);
8873 }
8874 
DestroyHiddenVertexArray()8875 void ON_Mesh::DestroyHiddenVertexArray()
8876 {
8877   m_H.Destroy();
8878   m_hidden_count = 0;
8879 }
8880 
HiddenVertexCount() const8881 int ON_Mesh::HiddenVertexCount() const
8882 {
8883   return ((m_H.Count() == m_V.Count()) ? m_hidden_count : 0);
8884 }
8885 
SetVertexHiddenFlag(int meshvi,bool bHidden)8886 void ON_Mesh::SetVertexHiddenFlag( int meshvi, bool bHidden )
8887 {
8888   const int vcount = m_V.Count();
8889   if ( meshvi >= 0 && meshvi < vcount )
8890   {
8891     if ( bHidden )
8892     {
8893       if ( vcount != m_H.Count() )
8894       {
8895         m_H.SetCapacity(vcount);
8896         m_H.SetCount(vcount);
8897         m_H.Zero();
8898         m_H[meshvi] = true;
8899         m_hidden_count = 1;
8900       }
8901       else if ( false == m_H[meshvi] )
8902       {
8903         m_H[meshvi] = true;
8904         m_hidden_count++;
8905       }
8906     }
8907     else
8908     {
8909       // show this vertex
8910       if ( m_hidden_count > 0 && vcount == m_H.Count() )
8911       {
8912         if  ( m_H[meshvi] )
8913         {
8914           m_H[meshvi] = false;
8915           m_hidden_count--;
8916           if ( 0 == m_hidden_count )
8917           {
8918             DestroyHiddenVertexArray();
8919           }
8920         }
8921       }
8922       else if ( m_hidden_count > 0 || m_H.Capacity() > 0 )
8923       {
8924         // if m_H exists, it is bogus.
8925         DestroyHiddenVertexArray();
8926       }
8927     }
8928   }
8929 }
8930 
8931 
VertexIsHidden(int meshvi) const8932 bool ON_Mesh::VertexIsHidden(int meshvi) const
8933 {
8934   const int vcount = m_V.Count();
8935   return ((m_hidden_count > 0 && meshvi >= 0 && meshvi < vcount && vcount == m_H.Count())
8936           ? m_H[meshvi]
8937           : false);
8938 }
8939 
FaceIsHidden(int meshfi) const8940 bool ON_Mesh::FaceIsHidden(int meshfi) const
8941 {
8942   const bool* bHiddenVertex = HiddenVertexArray();
8943   if ( bHiddenVertex && meshfi >= 0 && meshfi < m_F.Count() )
8944   {
8945     ON_MeshFace f = m_F[meshfi];
8946     if ( bHiddenVertex[f.vi[0]] || bHiddenVertex[f.vi[1]] || bHiddenVertex[f.vi[2]] || bHiddenVertex[f.vi[3]] )
8947       return true;
8948   }
8949   return false;
8950 }
8951 
TopVertexIsHidden(int topvi) const8952 bool ON_MeshTopology::TopVertexIsHidden( int topvi ) const
8953 {
8954   const bool* bHiddenVertex = m_mesh ? m_mesh->HiddenVertexArray() : 0;
8955   if ( bHiddenVertex && topvi >= 0 && topvi < m_topv.Count() )
8956   {
8957     const ON_MeshTopologyVertex& topv = m_topv[topvi];
8958     int i;
8959     for ( i = 0; i < topv.m_v_count; i++ )
8960     {
8961       if ( !bHiddenVertex[topv.m_vi[i]] )
8962         return false;
8963     }
8964     return true;
8965   }
8966   return false;
8967 }
8968 
TopEdgeIsHidden(int topei) const8969 bool ON_MeshTopology::TopEdgeIsHidden( int topei ) const
8970 {
8971   // ugly - but faster than calling TopVertexIsHidden()
8972   const bool* bHiddenVertex = m_mesh ? m_mesh->HiddenVertexArray() : 0;
8973   if ( bHiddenVertex && topei >= 0 && topei < m_tope.Count()  )
8974   {
8975     const ON_MeshTopologyEdge& tope = m_tope[topei];
8976     const ON_MeshTopologyVertex& topv0 = m_topv[tope.m_topvi[0]];
8977     const ON_MeshTopologyVertex& topv1 = m_topv[tope.m_topvi[1]];
8978     int i;
8979 
8980     for ( i = 0; i < topv0.m_v_count; i++ )
8981     {
8982       if ( !bHiddenVertex[topv0.m_vi[i]] )
8983         break;
8984     }
8985     if ( i >= topv0.m_v_count )
8986       return true;
8987 
8988     for ( i = 0; i < topv1.m_v_count; i++ )
8989     {
8990       if ( !bHiddenVertex[topv1.m_vi[i]] )
8991         return false;
8992     }
8993 
8994     return true;
8995   }
8996   return false;
8997 }
8998 
TopFaceIsHidden(int topfi) const8999 bool ON_MeshTopology::TopFaceIsHidden( int topfi ) const
9000 {
9001   // topology and mesh face indices are the same
9002   return m_mesh ? m_mesh->FaceIsHidden(topfi) : false;
9003 }
9004 
9005 
ON_MappingTag()9006 ON_MappingTag::ON_MappingTag()
9007 {
9008   Default();
9009 }
9010 
Dump(ON_TextLog & text_log) const9011 void ON_MappingTag::Dump( ON_TextLog& text_log ) const
9012 {
9013   text_log.Print("Texture/color coordinates tag:\n");
9014   text_log.PushIndent();
9015   text_log.Print("mapping id: "); text_log.Print(m_mapping_id); text_log.Print("\n");
9016   text_log.Print("mapping crc: %08x\n",m_mapping_crc);
9017   text_log.Print("mesh xform:\n");
9018   text_log.PushIndent(); text_log.Print(m_mesh_xform); text_log.PopIndent();
9019   text_log.PushIndent();
9020   text_log.Print(m_mesh_xform);
9021   text_log.PopIndent();
9022   text_log.PopIndent();
9023 }
9024 
Transform(const ON_Xform & xform)9025 void ON_MappingTag::Transform( const ON_Xform& xform )
9026 {
9027   if ( !ON_UuidIsNil(m_mapping_id) )
9028   {
9029     // Update mapping transformation.
9030     // Do NOT change the value of m_mapping_crc.
9031     m_mesh_xform = xform*m_mesh_xform;
9032   }
9033 }
9034 
SetDefaultSurfaceParameterMappingTag()9035 void ON_MappingTag::SetDefaultSurfaceParameterMappingTag()
9036 {
9037   ON_TextureMapping srfp_mapping;
9038   srfp_mapping.m_type = ON_TextureMapping::srfp_mapping;
9039   srfp_mapping.m_mapping_id = ON_nil_uuid;
9040   Set(srfp_mapping);
9041 }
9042 
Set(const ON_TextureMapping & mapping)9043 void ON_MappingTag::Set(const ON_TextureMapping& mapping)
9044 {
9045   Default();
9046   m_mapping_id   = mapping.m_mapping_id;
9047   m_mapping_type = mapping.m_type;
9048   m_mapping_crc  = mapping.MappingCRC();
9049 }
9050 
IsSet() const9051 bool ON_MappingTag::IsSet() const
9052 {
9053   return (0 != m_mapping_crc || !ON_UuidIsNil(m_mapping_id));
9054 }
9055 
IsDefaultSurfaceParameterMapping() const9056 bool ON_MappingTag::IsDefaultSurfaceParameterMapping() const
9057 {
9058   bool rc = (ON_TextureMapping::srfp_mapping == m_mapping_type);
9059   if ( rc )
9060   {
9061     // The crc needs to be checked to insure that m_uvw
9062     // was the identity.
9063     ON_TextureMapping tmp;
9064     tmp.m_type = ON_TextureMapping::srfp_mapping;
9065     rc = (m_mapping_crc == tmp.MappingCRC());
9066   }
9067   return rc;
9068 }
9069 
9070 
Default()9071 void ON_MappingTag::Default()
9072 {
9073   memset(this,0,sizeof(*this));
9074   m_mesh_xform.m_xform[0][0] = 1.0;
9075   m_mesh_xform.m_xform[1][1] = 1.0;
9076   m_mesh_xform.m_xform[2][2] = 1.0;
9077   m_mesh_xform.m_xform[3][3] = 1.0;
9078 }
9079 
Compare(const ON_MappingTag & other,bool bCompareId,bool bCompareCRC,bool bCompareXform) const9080 int ON_MappingTag::Compare(
9081              const ON_MappingTag& other,
9082              bool bCompareId,
9083              bool bCompareCRC,
9084              bool bCompareXform
9085              ) const
9086 {
9087   int rc = 0;
9088   if ( !rc && bCompareId )
9089   {
9090     rc = ON_UuidCompare(m_mapping_id,other.m_mapping_id);
9091   }
9092   if ( !rc && bCompareCRC )
9093   {
9094     rc = ((int)m_mapping_crc) - ((int)other.m_mapping_crc);
9095   }
9096   if ( !rc && bCompareXform )
9097   {
9098     rc = m_mesh_xform.Compare(other.m_mesh_xform);
9099   }
9100   return rc;
9101 }
9102 
9103 
9104 
Write(ON_BinaryArchive & archive) const9105 bool ON_MappingTag::Write(ON_BinaryArchive& archive) const
9106 {
9107   bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1);
9108   if (!rc)
9109     return false;
9110 
9111   for(;;)
9112   {
9113     rc = archive.WriteUuid(m_mapping_id);
9114     if(!rc) break;
9115     rc = archive.WriteInt(m_mapping_crc);
9116     if(!rc) break;
9117     rc = archive.WriteXform(m_mesh_xform);
9118     if(!rc) break;
9119 
9120     // 1.1 fields
9121     rc = archive.WriteInt(m_mapping_type);
9122     if (!rc) break;
9123 
9124     break;
9125   }
9126 
9127   if ( !archive.EndWrite3dmChunk() )
9128     rc = false;
9129 
9130   return rc;
9131 }
9132 
Read(ON_BinaryArchive & archive)9133 bool ON_MappingTag::Read(ON_BinaryArchive& archive)
9134 {
9135   Default();
9136   int mjv = 0, mnv = 0;
9137   bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&mjv,&mnv);
9138   if (!rc)
9139     return false;
9140 
9141   for(;;)
9142   {
9143     rc = (1 == mjv);
9144     if (!rc) break;
9145     rc = archive.ReadUuid(m_mapping_id);
9146     if(!rc) break;
9147     if ( 0 == ON_UuidCompare(&obsolete_default_srfp_mapping_id,&m_mapping_id) )
9148       m_mapping_id = ON_nil_uuid;
9149     rc = archive.ReadInt(&m_mapping_crc);
9150     if(!rc) break;
9151     rc = archive.ReadXform(m_mesh_xform);
9152     if(!rc) break;
9153 
9154     if ( mnv >= 1 )
9155     {
9156       // 1.1 fields
9157       int i = m_mapping_type;
9158       rc = archive.ReadInt(&i);
9159       if ( rc )
9160         m_mapping_type = ON_TextureMapping::TypeFromInt(i);
9161       if (!rc) break;
9162     }
9163 
9164 
9165     break;
9166   }
9167 
9168   if ( !archive.EndRead3dmChunk() )
9169     rc = false;
9170 
9171   return rc;
9172 }
9173 
ON_TextureCoordinates()9174 ON_TextureCoordinates::ON_TextureCoordinates()
9175 {
9176   m_dim = 0;
9177 }
9178 
9179 
9180 ///////////////////////////////////////////////////////
9181 //
9182 // Double precision vertices
9183 //
9184 
9185 ON_OBJECT_IMPLEMENT(ON_MeshDoubleVertices,ON_UserData,"17F24E75-21BE-4a7b-9F3D-7F85225247E3");
9186 
Get(const ON_Mesh * mesh)9187 ON_MeshDoubleVertices* ON_MeshDoubleVertices::Get(const ON_Mesh* mesh)
9188 {
9189   return ON_MeshDoubleVertices::Cast(mesh->GetUserData(ON_MeshDoubleVertices::m_ON_MeshDoubleVertices_class_id.Uuid()));
9190 }
9191 
Attach(const ON_Mesh * mesh)9192 ON_MeshDoubleVertices* ON_MeshDoubleVertices::Attach(const ON_Mesh* mesh)
9193 {
9194   ON_MeshDoubleVertices* dv = ON_MeshDoubleVertices::Get(mesh);
9195   if ( 0 != dv )
9196     return 0;
9197   dv = new ON_MeshDoubleVertices();
9198   const_cast<ON_Mesh*>(mesh)->AttachUserData(dv);
9199   return dv;
9200 }
9201 
ON_MeshDoubleVertices()9202 ON_MeshDoubleVertices::ON_MeshDoubleVertices()
9203 : ON_UserData()
9204 , m_fcount(0)
9205 , m_dcount(0)
9206 , m_fCRC(0)
9207 , m_dCRC(0)
9208 {
9209   m_userdata_uuid = ON_MeshDoubleVertices::m_ON_MeshDoubleVertices_class_id.Uuid();
9210   m_application_uuid = ON_opennurbs5_id;
9211   m_userdata_copycount = 1;
9212 }
9213 
~ON_MeshDoubleVertices()9214 ON_MeshDoubleVertices::~ON_MeshDoubleVertices()
9215 {}
9216 
IsValid(ON_TextLog *) const9217 ON_BOOL32 ON_MeshDoubleVertices::IsValid( ON_TextLog* ) const
9218 {
9219   return true;
9220 }
9221 
Dump(ON_TextLog & text_log) const9222 void ON_MeshDoubleVertices::Dump( ON_TextLog& text_log ) const
9223 {
9224   // TODO - print some double values
9225   ON_UserData::Dump(text_log);
9226 }
9227 
SizeOf() const9228 unsigned int ON_MeshDoubleVertices::SizeOf() const
9229 {
9230   return m_dV.SizeOfArray() + ON_UserData::SizeOf();
9231 }
9232 
DataCRC(ON__UINT32 current_remainder) const9233 ON__UINT32 ON_MeshDoubleVertices::DataCRC(ON__UINT32 current_remainder) const
9234 {
9235   current_remainder = ON_CRC32(current_remainder,sizeof(m_fcount),&m_fcount);
9236   current_remainder = ON_CRC32(current_remainder,sizeof(m_dcount),&m_dcount);
9237   current_remainder = ON_CRC32(current_remainder,sizeof(m_fCRC),&m_fCRC);
9238   current_remainder = ON_CRC32(current_remainder,sizeof(m_dCRC),&m_dCRC);
9239   current_remainder = m_dV.DataCRC(current_remainder);
9240   return current_remainder;
9241 }
9242 
Write(ON_BinaryArchive & file) const9243 ON_BOOL32 ON_MeshDoubleVertices::Write(ON_BinaryArchive& file) const
9244 {
9245   bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
9246   if (!rc)
9247     return false;
9248   for(;;)
9249   {
9250     rc = file.WriteInt(m_fcount);
9251     if (!rc)
9252       break;
9253     rc = file.WriteInt(m_dcount);
9254     if (!rc)
9255       break;
9256     rc = file.WriteInt(m_fCRC);
9257     if (!rc)
9258       break;
9259     rc = file.WriteInt(m_dCRC);
9260     if (!rc)
9261       break;
9262     rc = file.WriteArray(m_dV);
9263     if (!rc)
9264       break;
9265     break;
9266   }
9267   if ( !file.EndWrite3dmChunk() )
9268     rc = false;
9269   return rc;
9270 }
9271 
Read(ON_BinaryArchive & file)9272 ON_BOOL32 ON_MeshDoubleVertices::Read(ON_BinaryArchive& file)
9273 {
9274   m_fcount = 0;
9275   m_dcount = 0;
9276   m_fCRC = 0;
9277   m_dCRC = 0;
9278   m_dV.Destroy();
9279 
9280   int major_version = 0;
9281   int minor_version = 0;
9282   bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
9283   if (!rc)
9284     return false;
9285   for(;;)
9286   {
9287     rc = file.ReadInt(&m_fcount);
9288     if (!rc)
9289       break;
9290     rc = file.ReadInt(&m_dcount);
9291     if (!rc)
9292       break;
9293     rc = file.ReadInt(&m_fCRC);
9294     if (!rc)
9295       break;
9296     rc = file.ReadInt(&m_dCRC);
9297     if (!rc)
9298       break;
9299     rc = file.ReadArray(m_dV);
9300     if (!rc)
9301       break;
9302     break;
9303   }
9304   if ( !file.EndRead3dmChunk() )
9305     rc = false;
9306   return rc;
9307 }
9308 
9309 // virtual ON_UserData overrides
GetDescription(ON_wString & description)9310 ON_BOOL32 ON_MeshDoubleVertices::GetDescription( ON_wString& description )
9311 {
9312   description = L"ON_Mesh double precision vertices";
9313   return true;
9314 }
9315 
Archive() const9316 ON_BOOL32 ON_MeshDoubleVertices::Archive() const
9317 {
9318   // refuse to save garbage in files.
9319 
9320   if ( m_fcount != m_dcount )
9321   {
9322     ON_ERROR("m_fcount != m_dcount");
9323     return false;
9324   }
9325 
9326   if ( m_dcount != m_dV.Count() )
9327   {
9328     ON_ERROR("m_dcount != m_dV.Count()");
9329     return false;
9330   }
9331 
9332   if ( m_dCRC != DoubleCRC() )
9333   {
9334     ON_ERROR("m_dCRC != DoubleCRC()");
9335     return false;
9336   }
9337 
9338   const ON_Mesh* mesh = ON_Mesh::Cast( Owner() );
9339   if ( 0 == mesh )
9340   {
9341     ON_ERROR("0 = ON_Mesh::Cast( Owner() )");
9342     return false;
9343   }
9344 
9345   if ( m_fcount != mesh->m_V.Count() )
9346   {
9347     ON_ERROR("m_fcount != mesh->m_V.Count()");
9348     return false;
9349   }
9350 
9351   if ( m_fCRC != ON_MeshDoubleVertices::FloatCRC(mesh->m_V) )
9352   {
9353     ON_ERROR("m_fCRC != ON_MeshDoubleVertices::FloatCRC(mesh->m_V)");
9354     return false;
9355   }
9356 
9357   return true;
9358 }
9359 
Transform(const ON_Xform & xform)9360 ON_BOOL32 ON_MeshDoubleVertices::Transform( const ON_Xform& xform )
9361 {
9362   if ( !xform.IsIdentity() )
9363   {
9364     const ON__UINT32 crc0 = DoubleCRC();
9365     m_dV.Transform(xform);
9366     const ON__UINT32 crc1 = DoubleCRC();
9367     if ( crc0 == m_dCRC && m_dV.Count() == m_dcount )
9368       m_dCRC = crc1; // update m_dCRC so it stays valid
9369     else
9370       m_dCRC = (0==crc1)?1:0; // make sure m_dCRC is not valid
9371   }
9372   return true;
9373 }
9374 
HasSynchronizedDoubleAndSinglePrecisionVertices() const9375 bool ON_Mesh::HasSynchronizedDoubleAndSinglePrecisionVertices() const
9376 {
9377   ON_3fPoint P;
9378   const ON_MeshDoubleVertices* dv = ON_MeshDoubleVertices::Get(this);
9379   if ( 0 == dv )
9380     return false;
9381   int count = m_V.Count();
9382   if ( count != dv->m_dV.Count() )
9383     return false;
9384   const ON_3fPoint* FV = m_V.Array();
9385   const ON_3dPoint* DV = dv->m_dV.Array();
9386   while (count--)
9387   {
9388     // Compare float values.
9389     P.x = (float)DV->x;
9390     P.y = (float)DV->y;
9391     P.z = (float)DV->z;
9392     if ( !(P.x == FV->x && P.y == FV->y && P.z == FV->z) )
9393       return false;
9394   }
9395   return true;
9396 }
9397 
HasDoublePrecisionVertices() const9398 bool ON_Mesh::HasDoublePrecisionVertices() const
9399 {
9400   return ( 0 != ON_MeshDoubleVertices::Get(this));
9401 }
9402 
DestroyDoublePrecisionVertices()9403 void ON_Mesh::DestroyDoublePrecisionVertices()
9404 {
9405   ON_MeshDoubleVertices* dv = ON_MeshDoubleVertices::Get(this);
9406   if ( dv )
9407     delete dv;
9408 }
9409 
EnableDoublePrecisionVertices(bool bEnableDoublePrecisionVertices)9410 void ON_Mesh::EnableDoublePrecisionVertices(bool bEnableDoublePrecisionVertices)
9411 {
9412   ON_MeshDoubleVertices* dv = ON_MeshDoubleVertices::Get(this);
9413   if ( bEnableDoublePrecisionVertices )
9414   {
9415     if ( 0 == dv )
9416     {
9417       dv = ON_MeshDoubleVertices::Attach(this);
9418       UpdateDoublePrecisionVertices();
9419     }
9420   }
9421   else
9422   {
9423     // destroy double precision vertices
9424     if ( 0 != dv )
9425       delete dv;
9426   }
9427 }
9428 
FloatCRC(const ON_3fPointArray & V)9429 ON__UINT32 ON_MeshDoubleVertices::FloatCRC( const ON_3fPointArray& V )
9430 {
9431   return V.DataCRC(0);
9432 }
9433 
DoubleCRC() const9434 ON__UINT32 ON_MeshDoubleVertices::DoubleCRC() const
9435 {
9436   return m_dV.DataCRC(0);
9437 }
9438 
UpdateSinglePrecisionVertices()9439 void ON_Mesh::UpdateSinglePrecisionVertices()
9440 {
9441   ON_MeshDoubleVertices* dv = ON_MeshDoubleVertices::Get(this);
9442   if ( 0 == dv )
9443     return;
9444   int count = dv->m_dV.Count();
9445   m_V.Reserve(count);
9446   m_V.SetCount(count);
9447   ON_3fPoint* fV = m_V.Array();
9448   const ON_3dPoint* dV = dv->m_dV.Array();
9449   while(count--)
9450   {
9451     fV->x = (float)dV->x;
9452     fV->y = (float)dV->y;
9453     fV->z = (float)dV->z;
9454     fV++;
9455     dV++;
9456   }
9457   dv->m_fcount = dv->m_dV.Count();
9458   dv->m_dcount = dv->m_dV.Count();
9459   dv->m_fCRC = ON_MeshDoubleVertices::FloatCRC(m_V);
9460   dv->m_dCRC = dv->DoubleCRC();
9461 }
9462 
UpdateDoublePrecisionVertices()9463 void ON_Mesh::UpdateDoublePrecisionVertices()
9464 {
9465   ON_MeshDoubleVertices* dv = ON_MeshDoubleVertices::Get(this);
9466   if ( 0 == dv )
9467     return;
9468   int count = m_V.Count();
9469   bool bSelectiveUpdate = (count == dv->m_dV.Count());
9470   dv->m_dV.Reserve(count);
9471   dv->m_dV.SetCount(count);
9472   ON_3dPoint* dV = dv->m_dV.Array();
9473   const ON_3fPoint* fV = m_V.Array();
9474   if ( bSelectiveUpdate )
9475   {
9476     // double precision vertices already existed
9477     // and there is a reasonable chance that
9478     // a subset of the float precision vertices
9479     // have been modified.  So, attempt to
9480     // keep the precision on double vertices
9481     // that alread agree with the float vertices
9482     // in float precision.
9483     ON_3fPoint P;
9484     while(count--)
9485     {
9486       P.x = (float)dV->x;
9487       P.y = (float)dV->y;
9488       P.z = (float)dV->z;
9489       if ( !(P.x == fV->x && P.y == fV->y && P.z == fV->z) )
9490       {
9491         // (float)dV != fV, so update dV
9492         dV->x = (double)fV->x;
9493         dV->y = (double)fV->y;
9494         dV->z = (double)fV->z;
9495       }
9496       dV++;
9497       fV++;
9498     }
9499   }
9500   else
9501   {
9502     while(count--)
9503     {
9504       dV->x = (double)fV->x;
9505       dV->y = (double)fV->y;
9506       dV->z = (double)fV->z;
9507       dV++;
9508       fV++;
9509     }
9510   }
9511   dv->m_fcount = m_V.Count();
9512   dv->m_dcount = m_V.Count();
9513   dv->m_fCRC = ON_MeshDoubleVertices::FloatCRC(m_V);
9514   dv->m_dCRC = dv->DoubleCRC();
9515 }
9516 
SetSinglePrecisionVerticesAsValid()9517 void ON_Mesh::SetSinglePrecisionVerticesAsValid()
9518 {
9519   ON_MeshDoubleVertices* dv = ON_MeshDoubleVertices::Get(this);
9520   if ( 0 == dv )
9521     return;
9522   dv->m_fcount = m_V.Count();
9523   dv->m_fCRC   = ON_MeshDoubleVertices::FloatCRC(m_V);
9524 }
9525 
SetDoublePrecisionVerticesAsValid()9526 void ON_Mesh::SetDoublePrecisionVerticesAsValid()
9527 {
9528   ON_MeshDoubleVertices* dv = ON_MeshDoubleVertices::Get(this);
9529   if ( 0 == dv )
9530     return;
9531   dv->m_dcount = dv->m_dV.Count();
9532   dv->m_dCRC   = dv->DoubleCRC();
9533 }
9534 
SinglePrecisionVerticesAreValid() const9535 bool ON_Mesh::SinglePrecisionVerticesAreValid() const
9536 {
9537   const ON_MeshDoubleVertices* dv = ON_MeshDoubleVertices::Get(this);
9538   if ( 0 == dv )
9539     return true;
9540   return ( dv->m_fcount == m_V.Count() && dv->m_fCRC == ON_MeshDoubleVertices::FloatCRC(m_V) );
9541 }
9542 
DoublePrecisionVerticesAreValid() const9543 bool ON_Mesh::DoublePrecisionVerticesAreValid() const
9544 {
9545   const ON_MeshDoubleVertices* dv = ON_MeshDoubleVertices::Get(this);
9546   if ( 0 == dv )
9547     return true;
9548   // DO NOT COMPARE dv->m_dV.Count() and m_V.Count() because m_V might be
9549   // the bogus set of values.
9550   return ( dv->m_dcount == dv->m_dV.Count() && dv->m_dCRC == dv->DoubleCRC() );
9551 }
9552 
9553 
9554 
DoublePrecisionVertices()9555 ON_3dPointArray& ON_Mesh::DoublePrecisionVertices()
9556 {
9557   ON_MeshDoubleVertices* dv = ON_MeshDoubleVertices::Get(this);
9558 
9559   bool bUpdate = false;
9560   if ( 0 == dv )
9561   {
9562     dv = ON_MeshDoubleVertices::Attach(this);
9563     bUpdate = true;
9564   }
9565   else if ( dv->m_dcount != dv->m_dV.Count()|| dv->m_dCRC != dv->DoubleCRC() )
9566   {
9567     if ( dv->m_fcount == m_V.Count() && dv->m_fCRC == ON_MeshDoubleVertices::FloatCRC(m_V) )
9568     bUpdate = true;
9569   }
9570 
9571   if ( bUpdate )
9572     UpdateDoublePrecisionVertices();
9573 
9574   return dv->m_dV;
9575 }
9576 
Vertex(int vertex_index) const9577 ON_3dPoint ON_Mesh::Vertex(int vertex_index) const
9578 {
9579   if ( vertex_index < 0 || vertex_index >= m_V.Count() )
9580     return ON_3dPoint::UnsetPoint;
9581 
9582   const ON_3fPoint F = m_V[vertex_index];
9583   ON_MeshDoubleVertices* dv = ON_MeshDoubleVertices::Get(this);
9584   if ( 0 != dv && dv->m_dV.Count() == m_V.Count() )
9585   {
9586     ON_3dPoint D = dv->m_dV[vertex_index];
9587     if ( F.x == (float)D.x && F.y == (float)D.y && F.z == (float)D.z )
9588     {
9589       // double precision vertex is valid
9590       return D;
9591     }
9592   }
9593 
9594   return F;
9595 }
9596 
DoublePrecisionVertices() const9597 const ON_3dPointArray& ON_Mesh::DoublePrecisionVertices() const
9598 {
9599   ON_Mesh& mesh = const_cast<ON_Mesh&>(*this);
9600   ON_3dPointArray& a = mesh.DoublePrecisionVertices();
9601   return a;
9602 }
9603 
SinglePrecisionVertices()9604 ON_3fPointArray& ON_Mesh::SinglePrecisionVertices()
9605 {
9606   ON_MeshDoubleVertices* dv = ON_MeshDoubleVertices::Get(this);
9607 
9608   if ( 0 != dv )
9609   {
9610     if ( dv->m_fcount != m_V.Count()|| dv->m_fCRC != ON_MeshDoubleVertices::FloatCRC(m_V) )
9611     {
9612       if ( dv->m_dcount == dv->m_dV.Count() && dv->m_dCRC == dv->DoubleCRC() )
9613         UpdateSinglePrecisionVertices();
9614     }
9615   }
9616 
9617   return m_V;
9618 }
9619 
SinglePrecisionVertices() const9620 const ON_3fPointArray& ON_Mesh::SinglePrecisionVertices() const
9621 {
9622   ON_Mesh& mesh = const_cast<ON_Mesh&>(*this);
9623   ON_3fPointArray& a = mesh.SinglePrecisionVertices();
9624   return a;
9625 }
9626 
9627 
9628 ////////////////////////////////////////////////////////////////
9629 //
9630 // BEGIN ON_PerObjectMeshParameters user data class
9631 //
9632 
9633 
9634 class /*NEVER EXPORT THIS CLASS DEFINITION*/ ON_PerObjectMeshParameters : public ON_UserData
9635 {
9636 #if !defined(ON_BOZO_VACCINE_B5628CA982C44CAE9883487B3E4AB28B)
9637 #error Never copy this class definition or put this definition in a header file!
9638 #endif
9639   ON_OBJECT_DECLARE(ON_PerObjectMeshParameters);
9640 
9641 public:
9642   ON_PerObjectMeshParameters();
9643   ~ON_PerObjectMeshParameters();
9644   // default copy constructor and operator= work fine.
9645 
9646 public:
9647   // virtual ON_Object override
9648   ON_BOOL32 IsValid( ON_TextLog* text_log = NULL ) const;
9649   // virtual ON_Object override
9650   unsigned int SizeOf() const;
9651   // virtual ON_Object override
9652   ON__UINT32 DataCRC(ON__UINT32 current_remainder) const;
9653   // virtual ON_Object override
9654   ON_BOOL32 Write(ON_BinaryArchive& binary_archive) const;
9655   // virtual ON_Object override
9656   ON_BOOL32 Read(ON_BinaryArchive& binary_archive);
9657   // virtual ON_UserData override
9658   ON_BOOL32 Archive() const;
9659   // virtual ON_UserData override
9660   ON_BOOL32 GetDescription( ON_wString& description );
9661 
9662 public:
9663   static
9664   ON_PerObjectMeshParameters* FindOrCreate( const ON_Object*, bool bCreate );
9665 
9666   ON_MeshParameters m_mp;
9667 };
9668 
9669 #undef ON_BOZO_VACCINE_B5628CA982C44CAE9883487B3E4AB28B
9670 
9671 ON_OBJECT_IMPLEMENT(ON_PerObjectMeshParameters,ON_UserData,"B5628CA9-82C4-4CAE-9883-487B3E4AB28B");
9672 
FindOrCreate(const ON_Object * object,bool bCreate)9673 ON_PerObjectMeshParameters* ON_PerObjectMeshParameters::FindOrCreate(const ON_Object* object,bool bCreate)
9674 {
9675   if ( 0 == object )
9676     return 0;
9677   ON_PerObjectMeshParameters* ud = ON_PerObjectMeshParameters::Cast(object->GetUserData(ON_PerObjectMeshParameters::m_ON_PerObjectMeshParameters_class_id.Uuid()));
9678   if ( !ud && bCreate )
9679   {
9680     ud = new ON_PerObjectMeshParameters();
9681     const_cast< ON_Object* >(object)->AttachUserData(ud);
9682   }
9683   return ud;
9684 }
9685 
ON_PerObjectMeshParameters()9686 ON_PerObjectMeshParameters::ON_PerObjectMeshParameters()
9687 : m_mp(ON_MeshParameters::FastRenderMesh)
9688 {
9689   m_userdata_uuid = ON_PerObjectMeshParameters::m_ON_PerObjectMeshParameters_class_id.Uuid();
9690   m_application_uuid = ON_opennurbs5_id;
9691   m_userdata_copycount = 1;
9692   m_mp.m_bCustomSettings = true;
9693   m_mp.m_bComputeCurvature = false;
9694 }
9695 
~ON_PerObjectMeshParameters()9696 ON_PerObjectMeshParameters::~ON_PerObjectMeshParameters()
9697 {
9698 }
9699 
9700 // virtual ON_Object override
IsValid(ON_TextLog * text_log) const9701 ON_BOOL32 ON_PerObjectMeshParameters::IsValid( ON_TextLog* text_log ) const
9702 {
9703   return true;
9704 }
9705 
9706 // virtual ON_Object override
SizeOf() const9707 unsigned int ON_PerObjectMeshParameters::SizeOf() const
9708 {
9709   size_t sz = sizeof(*this) - sizeof(ON_UserData);
9710   return (unsigned int)sz;
9711 }
9712 
9713 // virtual ON_Object override
DataCRC(ON__UINT32 current_remainder) const9714 ON__UINT32 ON_PerObjectMeshParameters::DataCRC(ON__UINT32 current_remainder) const
9715 {
9716   return m_mp.DataCRC(current_remainder);
9717 }
9718 
9719 // virtual ON_Object override
Write(ON_BinaryArchive & binary_archive) const9720 ON_BOOL32 ON_PerObjectMeshParameters::Write(ON_BinaryArchive& binary_archive) const
9721 {
9722   if ( !binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0) )
9723     return false;
9724 
9725   bool rc = false;
9726   for(;;)
9727   {
9728     if ( !binary_archive.BeginWrite3dmBigChunk(TCODE_ANONYMOUS_CHUNK,0) )
9729       break;
9730     bool mprc = m_mp.Write(binary_archive);
9731     if ( !binary_archive.EndWrite3dmChunk() )
9732       break;
9733     if ( !mprc )
9734       break;
9735     rc = true;
9736     break;
9737   }
9738 
9739   if ( !binary_archive.EndWrite3dmChunk() )
9740     rc = false;
9741 
9742   return rc;
9743 }
9744 
9745 // virtual ON_Object override
Read(ON_BinaryArchive & binary_archive)9746 ON_BOOL32 ON_PerObjectMeshParameters::Read(ON_BinaryArchive& binary_archive)
9747 {
9748   m_mp = ON_MeshParameters::FastRenderMesh;
9749   m_mp.m_bCustomSettings = true;
9750   m_mp.m_bComputeCurvature = false;
9751 
9752   int major_version = 0;
9753   int minor_version = 0;
9754   if ( !binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version) )
9755     return false;
9756 
9757   bool rc = false;
9758   for(;;)
9759   {
9760     if ( 1 != major_version )
9761       break;
9762 
9763     unsigned int tcode(0);
9764     ON__INT64 value(0);
9765     if (!binary_archive.BeginRead3dmBigChunk(&tcode,&value))
9766       break;
9767     bool mprc = false;
9768     for(;;)
9769     {
9770       if (TCODE_ANONYMOUS_CHUNK != tcode )
9771         break;
9772       if (value <= 0)
9773         break;
9774       if (!m_mp.Read(binary_archive))
9775         break;
9776       mprc = true;
9777       break;
9778     }
9779     if (!binary_archive.EndRead3dmChunk())
9780       break;
9781     if (!mprc)
9782       break;
9783 
9784     rc = true;
9785     break;
9786   }
9787 
9788   if ( !binary_archive.EndRead3dmChunk() )
9789     rc = false;
9790 
9791   m_mp.m_bCustomSettings = true;
9792   m_mp.m_bComputeCurvature = false;
9793 
9794   return rc;
9795 }
9796 
9797 // virtual ON_UserData override
Archive() const9798 ON_BOOL32 ON_PerObjectMeshParameters::Archive() const
9799 {
9800   return true;
9801 }
9802 
9803 // virtual ON_UserData override
GetDescription(ON_wString & description)9804 ON_BOOL32 ON_PerObjectMeshParameters::GetDescription( ON_wString& description )
9805 {
9806   description = L"Custom Render Mesh Parameters";
9807   return true;
9808 }
9809 
9810 //
9811 // END ON_PerObjectMeshParameters user data class
9812 //
9813 ////////////////////////////////////////////////////////////////
9814 
CustomRenderMeshParameters() const9815 const ON_MeshParameters* ON_3dmObjectAttributes::CustomRenderMeshParameters() const
9816 {
9817   const ON_PerObjectMeshParameters* ud = ON_PerObjectMeshParameters::FindOrCreate(this,false);
9818   return (0 != ud) ? &ud->m_mp : 0;
9819 }
9820 
SetCustomRenderMeshParameters(const ON_MeshParameters & mp)9821 bool ON_3dmObjectAttributes::SetCustomRenderMeshParameters( const ON_MeshParameters& mp )
9822 {
9823   ON_PerObjectMeshParameters* ud = ON_PerObjectMeshParameters::FindOrCreate(this,true);
9824   if ( 0 == ud )
9825     return false;
9826   ud->m_mp = mp;
9827   ud->m_mp.m_bCustomSettings = true;
9828   ud->m_mp.m_bComputeCurvature = false;
9829   if ( 1 != ud->m_mp.m_texture_range && 2 != ud->m_mp.m_texture_range )
9830     ud->m_mp.m_texture_range = 2;
9831   return true;
9832 }
9833 
DeleteCustomRenderMeshParameters()9834 void ON_3dmObjectAttributes::DeleteCustomRenderMeshParameters()
9835 {
9836   ON_PerObjectMeshParameters* ud = ON_PerObjectMeshParameters::FindOrCreate(this,false);
9837   if ( 0 != ud )
9838     delete ud;
9839 }
9840 
EnableCustomRenderMeshParameters(bool bEnable)9841 bool ON_3dmObjectAttributes::EnableCustomRenderMeshParameters(bool bEnable)
9842 {
9843   ON_PerObjectMeshParameters* ud = ON_PerObjectMeshParameters::FindOrCreate(this,false);
9844   if ( 0 != ud )
9845     ud->m_mp.m_bCustomSettingsEnabled = bEnable ? true : false;
9846   return (!bEnable || 0 != ud);
9847 }
9848 
DestroyTree(bool bDeleteTree)9849 void ON_Mesh::DestroyTree( bool bDeleteTree )
9850 {
9851 }
9852 
9853 
9854 int* ON_Mesh_GetVidHelper( const int Vcount, const ON_3fPoint* fV, const ON_3dPoint* dV, int first_vid, int* Vid, int* Vindex );
comparefV(const void * a,const void * b)9855 static int comparefV( const void* a, const void* b )
9856 {
9857   const float* af = (const float*)a;
9858   const float* bf = (const float*)b;
9859   if ( af[0] < bf[0] )
9860     return -1;
9861   if ( af[0] > bf[0] )
9862     return 1;
9863   if ( af[1] < bf[1] )
9864     return -1;
9865   if ( af[1] > bf[1] )
9866     return 1;
9867   if ( af[2] < bf[2] )
9868     return -1;
9869   if ( af[2] > bf[2] )
9870     return 1;
9871   return 0;
9872 }
9873 
comparedV(const void * a,const void * b)9874 static int comparedV( const void* a, const void* b )
9875 {
9876   const double* af = (const double*)a;
9877   const double* bf = (const double*)b;
9878   if ( af[0] < bf[0] )
9879     return -1;
9880   if ( af[0] > bf[0] )
9881     return 1;
9882   if ( af[1] < bf[1] )
9883     return -1;
9884   if ( af[1] > bf[1] )
9885     return 1;
9886   if ( af[2] < bf[2] )
9887     return -1;
9888   if ( af[2] > bf[2] )
9889     return 1;
9890   return 0;
9891 }
9892 
ON_Mesh_GetVidHelper(const int Vcount,const ON_3fPoint * fV,const ON_3dPoint * dV,int first_vid,int * Vid,int * Vindex)9893 int* ON_Mesh_GetVidHelper( const int Vcount, const ON_3fPoint* fV, const ON_3dPoint* dV, int first_vid, int* Vid, int* Vindex )
9894 {
9895   // Used here and in opennurbs_plus_xmeshfast.cpp
9896 
9897   if ( Vcount <= 0 || (0 == dV && 0 == fV) )
9898     return 0;
9899 
9900   int Vmapbuffer[1024];
9901   int* Vmap;
9902   int id, i;
9903   const ON_3fPoint* fP0;
9904   const ON_3fPoint* fP1;
9905   const ON_3dPoint* dP0;
9906   const ON_3dPoint* dP1;
9907   if ( Vindex )
9908   {
9909     Vmap = Vindex;
9910   }
9911   else
9912   {
9913     Vmap = ( Vcount*sizeof(*Vmap) <= sizeof(Vmapbuffer) )
9914          ? &Vmapbuffer[0]
9915          : (int*)onmalloc( Vcount*sizeof(*Vmap) );
9916   }
9917   if ( 0 == Vmap )
9918     return 0;
9919 
9920   // The call to ON_Sort fills in Vmap[] with a permutation
9921   // of (0,1,...,Vcount-1) so that all coincident points
9922   // are adjacent in the Vmap[] list.  The vertex locations
9923   // are often partially sorted.  In the past, heap sort was
9924   // a better choice but the qsort in VC 2010
9925   // is now faster than heap sort.
9926   if ( 0 != dV )
9927     ON_Sort( ON::quick_sort, Vmap, dV, Vcount, sizeof(dV[0]), comparedV );
9928   else
9929     ON_Sort( ON::quick_sort, Vmap, fV, Vcount, sizeof(fV[0]), comparefV );
9930 
9931   // Assign a sequential one based index to each unique point location.
9932   if ( 0 == Vid )
9933     Vid = (int*)onmalloc(Vcount*sizeof(*Vid));
9934 
9935   if ( 0 != dV )
9936   {
9937     dP0 = dV + Vmap[0];
9938     id = first_vid;
9939     Vid[Vmap[0]] = id;
9940     for ( i = 1; i < Vcount; i++ )
9941     {
9942       dP1 = dV + Vmap[i];
9943       if ( dP0->x != dP1->x || dP0->y != dP1->y || dP0->z != dP1->z )
9944       {
9945         dP0 = dP1;
9946         id++;
9947       }
9948       Vid[Vmap[i]] = id;
9949     }
9950   }
9951   else
9952   {
9953     fP0 = fV + Vmap[0];
9954     id = first_vid;
9955     Vid[Vmap[0]] = id;
9956     for ( i = 1; i < Vcount; i++ )
9957     {
9958       fP1 = fV + Vmap[i];
9959       if ( fP0->x != fP1->x || fP0->y != fP1->y || fP0->z != fP1->z )
9960       {
9961         fP0 = fP1;
9962         id++;
9963       }
9964       Vid[Vmap[i]] = id;
9965     }
9966   }
9967 
9968   if ( 0 != Vmap && Vmap != &Vmapbuffer[0] && Vmap != Vindex )
9969     onfree(Vmap);
9970 
9971   return Vid;
9972 }
9973 
GetVertexLocationIds(int first_vid,int * Vid,int * Vindex) const9974 int* ON_Mesh::GetVertexLocationIds( int first_vid, int* Vid, int* Vindex ) const
9975 {
9976   const ON_3dPoint* dV = Mesh_dV(*this);
9977   const ON_3fPoint* fV = (0 == dV) ? m_V.Array() : 0;
9978   return ON_Mesh_GetVidHelper( m_V.Count(), fV, dV, first_vid, Vid, Vindex );
9979 }
9980 
9981 /*
9982 Description:
9983   Helper to get edge info array needed by ON_Mesh::GetEdgeList()
9984   and ON_Mesh::IsClosed().
9985 Parameters:
9986   Fcount - [in]
9987   F - [in]
9988   Vid - [in] array from ON_Mesh_GetVidHelper()
9989   Eid_list - [out]
9990     input array needs to be large enough to hold one element for
9991     each face side.  4*Fcount is always large enough.
9992 */
9993 
ON_Mesh_GetEidHelper(const int Vcount,const int Fcount,const ON_MeshFace * F,const int * Vid,ON_MeshFaceSide * Eid_list)9994 int ON_Mesh_GetEidHelper(
9995       const int Vcount,
9996       const int Fcount,
9997       const ON_MeshFace* F,
9998       const int* Vid,
9999       ON_MeshFaceSide* Eid_list
10000       )
10001 {
10002   // Used here and in opennurbs_plus_xmeshfast.cpp
10003   const int* fvi;
10004   int i, Vid0;
10005   int Eid_count = 0;
10006   struct ON_MeshFaceSide Eid;
10007   memset(&Eid,0,sizeof(Eid));
10008 
10009   if ( 0 == Vid )
10010   {
10011     // use mesh m_V[] index
10012     for ( Eid.fi = 0; Eid.fi < Fcount; Eid.fi++ )
10013     {
10014       fvi = F[Eid.fi].vi;
10015 
10016       // These checks are necessary to prevent crashes
10017       if ( fvi[0] < 0 || fvi[0] >= Vcount )
10018         continue;
10019       if ( fvi[1] < 0 || fvi[1] >= Vcount )
10020         continue;
10021       if ( fvi[2] < 0 || fvi[2] >= Vcount )
10022         continue;
10023       if ( fvi[3] < 0 || fvi[3] >= Vcount )
10024         continue;
10025 
10026       Eid.vi[0] = fvi[0];
10027       Vid0 = Eid.vi[1] = fvi[1];
10028       Eid.side = 0;
10029       if ( Eid.vi[0] < Eid.vi[1] )
10030       {
10031         Eid.dir = 0;
10032         Eid_list[Eid_count++] = Eid;
10033       }
10034       else if ( Eid.vi[0] > Eid.vi[1] )
10035       {
10036         i = Eid.vi[0]; Eid.vi[0] = Eid.vi[1]; Eid.vi[1] = i;
10037         Eid.dir = 1;
10038         Eid_list[Eid_count++] = Eid;
10039       }
10040 
10041       Eid.vi[0] = Vid0;
10042       Vid0 = Eid.vi[1] = fvi[2];
10043       Eid.side = 1;
10044       if ( Eid.vi[0] < Eid.vi[1] )
10045       {
10046         Eid.dir = 0;
10047         Eid_list[Eid_count++] = Eid;
10048       }
10049       else if ( Eid.vi[0] > Eid.vi[1] )
10050       {
10051         i = Eid.vi[0]; Eid.vi[0] = Eid.vi[1]; Eid.vi[1] = i;
10052         Eid.dir = 1;
10053         Eid_list[Eid_count++] = Eid;
10054       }
10055 
10056       if ( fvi[2] != fvi[3] )
10057       {
10058         // quad
10059         Eid.vi[0] = Vid0;
10060         Vid0 = Eid.vi[1] = fvi[3];
10061         Eid.side = 2;
10062         if ( Eid.vi[0] < Eid.vi[1] )
10063         {
10064           Eid.dir = 0;
10065           Eid_list[Eid_count++] = Eid;
10066         }
10067         else if ( Eid.vi[0] > Eid.vi[1] )
10068         {
10069           i = Eid.vi[0]; Eid.vi[0] = Eid.vi[1]; Eid.vi[1] = i;
10070           Eid.dir = 1;
10071           Eid_list[Eid_count++] = Eid;
10072         }
10073       }
10074 
10075       Eid.vi[0] = Vid0;
10076       Eid.vi[1] = fvi[0];
10077       Eid.side = 3;
10078       if ( Eid.vi[0] < Eid.vi[1] )
10079       {
10080         Eid.dir = 0;
10081         Eid_list[Eid_count++] = Eid;
10082       }
10083       else if ( Eid.vi[0] > Eid.vi[1] )
10084       {
10085         i = Eid.vi[0]; Eid.vi[0] = Eid.vi[1]; Eid.vi[1] = i;
10086         Eid.dir = 1;
10087         Eid_list[Eid_count++] = Eid;
10088       }
10089     }
10090   }
10091   else
10092   {
10093     // use Vid[mesh m_V[] index]
10094 
10095     for ( Eid.fi = 0; Eid.fi < Fcount; Eid.fi++ )
10096     {
10097       fvi = F[Eid.fi].vi;
10098 
10099       // These checks are necessary to prevent crashes
10100       if ( fvi[0] < 0 || fvi[0] >= Vcount )
10101         continue;
10102       if ( fvi[1] < 0 || fvi[1] >= Vcount )
10103         continue;
10104       if ( fvi[2] < 0 || fvi[2] >= Vcount )
10105         continue;
10106       if ( fvi[3] < 0 || fvi[3] >= Vcount )
10107         continue;
10108 
10109       Eid.vi[0] = Vid[fvi[0]];
10110       Vid0 = Eid.vi[1] = Vid[fvi[1]];
10111       Eid.side = 0;
10112       if ( Eid.vi[0] < Eid.vi[1] )
10113       {
10114         Eid.dir = 0;
10115         Eid_list[Eid_count++] = Eid;
10116       }
10117       else if ( Eid.vi[0] > Eid.vi[1] )
10118       {
10119         i = Eid.vi[0]; Eid.vi[0] = Eid.vi[1]; Eid.vi[1] = i;
10120         Eid.dir = 1;
10121         Eid_list[Eid_count++] = Eid;
10122       }
10123 
10124       Eid.vi[0] = Vid0;
10125       Vid0 = Eid.vi[1] = Vid[fvi[2]];
10126       Eid.side = 1;
10127       if ( Eid.vi[0] < Eid.vi[1] )
10128       {
10129         Eid.dir = 0;
10130         Eid_list[Eid_count++] = Eid;
10131       }
10132       else if ( Eid.vi[0] > Eid.vi[1] )
10133       {
10134         i = Eid.vi[0]; Eid.vi[0] = Eid.vi[1]; Eid.vi[1] = i;
10135         Eid.dir = 1;
10136         Eid_list[Eid_count++] = Eid;
10137       }
10138 
10139       if ( fvi[2] != fvi[3] )
10140       {
10141         // quad
10142         Eid.vi[0] = Vid0;
10143         Vid0 = Eid.vi[1] = Vid[fvi[3]];
10144         Eid.side = 2;
10145         if ( Eid.vi[0] < Eid.vi[1] )
10146         {
10147           Eid.dir = 0;
10148           Eid_list[Eid_count++] = Eid;
10149         }
10150         else if ( Eid.vi[0] > Eid.vi[1] )
10151         {
10152           i = Eid.vi[0]; Eid.vi[0] = Eid.vi[1]; Eid.vi[1] = i;
10153           Eid.dir = 1;
10154           Eid_list[Eid_count++] = Eid;
10155         }
10156       }
10157 
10158       Eid.vi[0] = Vid0;
10159       Eid.vi[1] = Vid[fvi[0]];
10160       Eid.side = 3;
10161       if ( Eid.vi[0] < Eid.vi[1] )
10162       {
10163         Eid.dir = 0;
10164         Eid_list[Eid_count++] = Eid;
10165       }
10166       else if ( Eid.vi[0] > Eid.vi[1] )
10167       {
10168         i = Eid.vi[0]; Eid.vi[0] = Eid.vi[1]; Eid.vi[1] = i;
10169         Eid.dir = 1;
10170         Eid_list[Eid_count++] = Eid;
10171       }
10172     }
10173   }
10174 
10175   return Eid_count;
10176 }
10177 
GetMeshFaceSideList(const int * Vid,struct ON_MeshFaceSide * & sides) const10178 int ON_Mesh::GetMeshFaceSideList(
10179     const int* Vid,
10180     struct ON_MeshFaceSide*& sides
10181     ) const
10182 {
10183   const int Vcount = m_V.Count();
10184   const int Fcount = m_F.Count();
10185   const ON_MeshFace* F = m_F.Array();
10186 
10187   if ( Fcount < 1 || 0 == F || Vcount < 2 )
10188     return 0;
10189 
10190   struct ON_MeshFaceSide* Elist = sides;
10191   if ( 0 == Elist )
10192   {
10193     Elist = (struct ON_MeshFaceSide*)onmalloc(4*Fcount*sizeof(Elist[0]));
10194     if ( 0 == Elist )
10195       return 0;
10196   }
10197 
10198   int sides_count = ON_Mesh_GetEidHelper( Vcount, Fcount, F, Vid, Elist );
10199   if ( sides_count <= 0 )
10200   {
10201     if ( 0 == sides )
10202       delete(Elist);
10203   }
10204   else if ( 0 == sides )
10205   {
10206     sides = Elist;
10207   }
10208 
10209   return sides_count;
10210 }
10211 
10212 #define ON_COMPILING_OPENNURBS_QSORT_FUNCTIONS
10213 #define ON_SORT_TEMPLATE_STATIC_FUNCTION
10214 #define ON_SORT_TEMPLATE_TYPE struct ON_MeshFaceSide
10215 
10216 #define ON_SORT_TEMPLATE_COMPARE ON_qsort_MeshFaceSide_compare
ON_SORT_TEMPLATE_COMPARE(ON_SORT_TEMPLATE_TYPE const * side1,ON_SORT_TEMPLATE_TYPE const * side2)10217 static int ON_SORT_TEMPLATE_COMPARE(
10218         ON_SORT_TEMPLATE_TYPE const * side1,
10219         ON_SORT_TEMPLATE_TYPE const * side2
10220         )
10221 {
10222   if ( side1->vi[0] < side2->vi[0] )
10223     return -1;
10224   if ( side1->vi[0] > side2->vi[0] )
10225     return 1;
10226   if ( side1->vi[1] < side2->vi[1] )
10227     return -1;
10228   if ( side1->vi[1] > side2->vi[1] )
10229     return 1;
10230   if ( side1->fi < side2->fi )
10231     return -1;
10232   if ( side1->fi > side2->fi )
10233     return 1;
10234   if ( side1->side < side2->side )
10235     return -1;
10236   if ( side1->side > side2->side )
10237     return 1;
10238   return 0;
10239 }
10240 
10241 #define ON_QSORT_FNAME ON_qsort_MeshFaceSide
10242 #include "opennurbs_qsort_template.h"
10243 
ON_SortMeshFaceSidesByVertexIndex(int sides_count,struct ON_MeshFaceSide * sides)10244 void ON_SortMeshFaceSidesByVertexIndex( int sides_count, struct ON_MeshFaceSide* sides )
10245 {
10246   if ( sides_count >= 2 && 0 != sides )
10247     ON_QSORT_FNAME( sides, sides_count );
10248 }
10249 
10250 #undef ON_COMPILING_OPENNURBS_QSORT_FUNCTIONS
10251 #undef ON_SORT_TEMPLATE_STATIC_FUNCTION
10252 #undef ON_SORT_TEMPLATE_TYPE
10253 #undef ON_QSORT_FNAME
10254