Files
opennurbs/opennurbs_mesh.cpp
2020-01-16 10:59:30 -08:00

15211 lines
396 KiB
C++

/* $NoKeywords: $ */
/*
//
// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
// McNeel & Associates.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
//
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
*/
#include "opennurbs.h"
#if !defined(ON_COMPILING_OPENNURBS)
// This check is included in all opennurbs source .c and .cpp files to insure
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
// and the opennurbs .h files alter what is declared and how it is declared.
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
#endif
// NEVER COPY OR MOVE THE NEXT 2 LINES
#define ON_BOZO_VACCINE_17F24E7521BE4a7b9F3D7F85225247E3
#define ON_BOZO_VACCINE_B5628CA982C44CAE9883487B3E4AB28B
// NEVER COPY OR MOVE THE PREVIOUS 2 LINES
///////////////////////////////////////////////////////
//
// Rhino V5 Double precision vertices user data
// Used to read and write version 5 .3dm files.
//
class /* DO NOT copy, move, or export this class */ ON_V5_MeshDoubleVertices : public ON_UserData
{
ON_OBJECT_DECLARE(ON_V5_MeshDoubleVertices);
public:
ON_V5_MeshDoubleVertices();
~ON_V5_MeshDoubleVertices();
// default copy constructor and operator= work fine.
/*
If the mesh has ON_V5_MeshDoubleVertices user data, then return
a pointer to it.
*/
static ON_V5_MeshDoubleVertices* GetV5(const ON_Mesh* mesh);
/*
Attach new ON_V5_MeshDoubleVertices user data to the mesh.
This will fail and return nullptr if the mesh already has
ON_V5_MeshDoubleVertices user data.
*/
static ON_V5_MeshDoubleVertices* AttachV5(const ON_Mesh* mesh);
// virtual ON_Object overrides
bool IsValid( class ON_TextLog* text_log = nullptr ) const override;
void Dump( ON_TextLog& ) const override;
unsigned int SizeOf() const override;
ON__UINT32 DataCRC(ON__UINT32) const override;
bool Write(ON_BinaryArchive&) const override;
bool Read(ON_BinaryArchive&) override;
// virtual ON_UserData overrides
bool GetDescription( ON_wString& ) override;
bool Archive() const override;
bool Transform( const ON_Xform& ) override;
bool DeleteAfterWrite(
const class ON_BinaryArchive& archive,
const class ON_Object* parent_object
) const override;
bool DeleteAfterRead(
const class ON_BinaryArchive& archive,
class ON_Object* parent_object
) const override;
#if !defined(ON_BOZO_VACCINE_17F24E7521BE4a7b9F3D7F85225247E3)
#error DO NOT copy, move or export the definition of ON_V5_MeshDoubleVertices
#endif
#undef ON_BOZO_VACCINE_17F24E7521BE4a7b9F3D7F85225247E3
ON__UINT32 DoubleCRC() const;
static ON__UINT32 FloatCRC( const ON_3fPointArray& );
// If m_dV.Count() != m_dcount or
// m_dCRC != ON_CRC32(0,m_dV.Count()*sizeof(ON_3dPoint),m_dV.Array()),
// then somebody has changed m_dV and not called
// SetDoublePrecisionVerticesAsValid() to mark the
// values as valid.
//
// If ON_Mesh.M_V.Count() != m_fcount or
// m_fCRC != ON_CRC32(0,m_V.Count()*sizeof(ON_3fPoint),m_V.Array()),
// then somebody has changed ON_Mesh.m_V and not called
// SetSinglePrecisionVerticesAsValid() to mark the
// values as valid.
//
// Whenever there is a question about which values are valid,
// it is assumed the m_V array is valid and the double precision
// informtion should be destroyed.
int m_fcount = 0; // single precision vertex count
int m_dcount = 0; // double precision vertex count
ON__UINT32 m_fCRC = 0; // crc of float vertex array
ON__UINT32 m_dCRC = 0; // crc of double vertex array
ON_3dPointArray m_V5_dV; // double precision mesh vertices
};
ON_MeshCurveParameters::ON_MeshCurveParameters()
{
memset(this,0,sizeof(*this));
}
ON_OBJECT_IMPLEMENT(ON_Mesh,ON_Geometry,"4ED7D4E4-E947-11d3-BFE5-0010830122F0");
/*
ON_MeshEdge& ON_MeshEdge::operator=( const ON_MeshEdge& src )
{
int fc = src.m_fcount+(src.m_fcount%1);
if ( fc <= 2 ) {
if ( m_fi && m_fi != m_private_fi ) onfree((void*)m_fi);
m_fi = m_private_fi;
fc = 2;
}
else {
if ( (m_fcount+(m_fcount%1)) != fc ) {
if ( m_fi == m_private_fi )
m_fi = 0;
m_fi = (int*)onrealloc(m_fi,fc*sizeof(*m_fi));
}
}
memcpy(m_fi,src.m_fi,fc*sizeof(*m_fi));
m_fcount=src.m_fcount;
return *this;
}
void ON_MeshEdge::AppendFaceIndex(int face_index)
{
if ( m_fcount>0 && !(m_fcount%1) ) {
if ( m_fi == m_private_fi ) {
m_fi = (int*)onmalloc((m_fcount+2)*sizeof(*m_fi));
m_fi[0] = m_private_fi[0];
m_fi[1] = m_private_fi[1];
}
else {
m_fi = (int*)onrealloc(m_fi,(m_fcount+2)*sizeof(*m_fi));
}
}
m_fi[m_fcount++] = face_index;
}
*/
bool
ON_MeshFace::IsValid(int mesh_vertex_count) const
{
return (vi[0] >= 0 && vi[0] < mesh_vertex_count
&& vi[1] >= 0 && vi[1] < mesh_vertex_count
&& vi[2] >= 0 && vi[2] < mesh_vertex_count
&& vi[3] >= 0 && vi[3] < mesh_vertex_count
&& vi[0] != vi[1] && vi[1] != vi[2] && vi[2] != vi[0]
&& (vi[2] == vi[3] || (vi[0] != vi[3] && vi[1] != vi[3])));
}
bool
ON_MeshFace::IsValid(unsigned int mesh_vertex_count) const
{
const unsigned int* uvi = (const unsigned int*)vi;
return (uvi[0] < mesh_vertex_count
&& uvi[1] < mesh_vertex_count
&& uvi[2] < mesh_vertex_count
&& uvi[3] < mesh_vertex_count
&& uvi[0] != uvi[1] && uvi[1] != uvi[2] && uvi[2] != uvi[0]
&& (uvi[2] == uvi[3] || (uvi[0] != uvi[3] && uvi[1] != uvi[3])));
}
bool
ON_MeshFace::IsValid(int mesh_vertex_count, const ON_3fPoint* V ) const
{
if ( !IsValid(mesh_vertex_count) )
return false;
if ( !(V[vi[0]] != V[vi[1]]) )
return false;
if ( !(V[vi[0]] != V[vi[2]]) )
return false;
if ( !(V[vi[1]] != V[vi[2]]) )
return false;
if ( vi[2] != vi[3] )
{
if ( !(V[vi[0]] != V[vi[3]]) )
return false;
if ( !(V[vi[1]] != V[vi[3]]) )
return false;
if ( !(V[vi[2]] != V[vi[3]]) )
return false;
}
return true;
}
bool
ON_MeshFace::IsValid(int mesh_vertex_count, const ON_3dPoint* V ) const
{
if ( !IsValid(mesh_vertex_count) )
return false;
if ( !(V[vi[0]] != V[vi[1]]) )
return false;
if ( !(V[vi[0]] != V[vi[2]]) )
return false;
if ( !(V[vi[1]] != V[vi[2]]) )
return false;
if ( vi[2] != vi[3] )
{
if ( !(V[vi[0]] != V[vi[3]]) )
return false;
if ( !(V[vi[1]] != V[vi[3]]) )
return false;
if ( !(V[vi[2]] != V[vi[3]]) )
return false;
}
return true;
}
bool ON_MeshFace::Repair(
int mesh_vertex_count
)
{
ON_MeshFace f;
int fvi_count = 0;
f.vi[0] = f.vi[1] = f.vi[2] = f.vi[3] = -1;
if ( vi[0] >= 0 && vi[0] < mesh_vertex_count )
f.vi[fvi_count++] = vi[0];
if ( vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] )
f.vi[fvi_count++] = vi[1];
if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] )
f.vi[fvi_count++] = vi[2];
if ( vi[3] >= 0 && vi[3] < mesh_vertex_count && f.vi[0] != vi[3] && f.vi[1] != vi[3] && f.vi[2] != vi[3] )
f.vi[fvi_count++] = vi[3];
if ( fvi_count < 3 )
return false;
if ( 3 == fvi_count )
f.vi[3] = f.vi[2];
if ( !f.IsValid(mesh_vertex_count) )
return false;
vi[0] = f.vi[0];
vi[1] = f.vi[1];
vi[2] = f.vi[2];
vi[3] = f.vi[3];
return true;
}
bool ON_MeshFace::Repair(
int mesh_vertex_count,
const ON_3fPoint* V
)
{
ON_MeshFace f;
int fvi_count = 0;
f.vi[0] = f.vi[1] = f.vi[2] = f.vi[3] = -1;
if ( vi[0] >= 0 && vi[0] < mesh_vertex_count && V[vi[0]].IsValid() )
f.vi[fvi_count++] = vi[0];
if (vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] && V[vi[1]].IsValid())
{
if ( 0 == fvi_count || V[f.vi[0]] != V[vi[1]] )
f.vi[fvi_count++] = vi[1];
}
if ( fvi_count < 1 )
return false;
if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] && V[vi[2]].IsValid() && V[f.vi[0]] != V[vi[2]] )
{
if ( 1 == fvi_count || V[f.vi[1]] != V[vi[2]] )
f.vi[fvi_count++] = vi[2];
}
if ( fvi_count < 2 )
return false;
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[vi[3]].IsValid() && V[f.vi[0]] != V[vi[3]] && V[f.vi[1]] != V[vi[3]] )
{
if ( 2 == fvi_count || V[f.vi[2]] != V[vi[3]] )
f.vi[fvi_count++] = vi[3];
}
if ( fvi_count < 3 )
return false;
if ( 3 == fvi_count )
f.vi[3] = f.vi[2];
if ( !f.IsValid(mesh_vertex_count) )
return false;
vi[0] = f.vi[0];
vi[1] = f.vi[1];
vi[2] = f.vi[2];
vi[3] = f.vi[3];
return true;
}
bool ON_MeshFace::Repair(
int mesh_vertex_count,
const ON_3dPoint* V
)
{
ON_MeshFace f;
int fvi_count = 0;
f.vi[0] = f.vi[1] = f.vi[2] = f.vi[3] = -1;
if ( vi[0] >= 0 && vi[0] < mesh_vertex_count && V[vi[0]].IsValid() )
f.vi[fvi_count++] = vi[0];
if (vi[1] >= 0 && vi[1] < mesh_vertex_count && f.vi[0] != vi[1] && V[vi[1]].IsValid())
{
if ( 0 == fvi_count || V[f.vi[0]] != V[vi[1]] )
f.vi[fvi_count++] = vi[1];
}
if ( fvi_count < 1 )
return false;
if ( vi[2] >= 0 && vi[2] < mesh_vertex_count && f.vi[0] != vi[2] && f.vi[1] != vi[2] && V[vi[2]].IsValid() && V[f.vi[0]] != V[vi[2]] )
{
if ( 1 == fvi_count || V[f.vi[1]] != V[vi[2]] )
f.vi[fvi_count++] = vi[2];
}
if ( fvi_count < 2 )
return false;
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[vi[3]].IsValid() && V[f.vi[0]] != V[vi[3]] && V[f.vi[1]] != V[vi[3]] )
{
if ( 2 == fvi_count || V[f.vi[2]] != V[vi[3]] )
f.vi[fvi_count++] = vi[3];
}
if ( fvi_count < 3 )
return false;
if ( 3 == fvi_count )
f.vi[3] = f.vi[2];
if ( !f.IsValid(mesh_vertex_count) )
return false;
vi[0] = f.vi[0];
vi[1] = f.vi[1];
vi[2] = f.vi[2];
vi[3] = f.vi[3];
return true;
}
ON_Mesh::ON_Mesh()
: m_packed_tex_rotate(0)
, m_parent(0)
, m_mesh_parameters(0)
, m_invalid_count(0)
, m_quad_count(0)
, m_triangle_count(0)
, m_mesh_is_closed(0)
, m_mesh_is_manifold(0)
, m_mesh_is_oriented(0)
, m_mesh_is_solid(0)
{
m_top.m_mesh = this;
m_srf_scale[0] = 0.0;
m_srf_scale[1] = 0.0;
m_kstat[0] = 0;
m_kstat[1] = 0;
m_kstat[2] = 0;
m_kstat[3] = 0;
InvalidateBoundingBoxes();
m_partition = 0;
m_hidden_count = 0;
}
ON_Mesh::ON_Mesh(
int initial_facet_capacity, // initial facet array capacity
int initial_vertex_capacity, // initial vertex array capacity
bool bHasVertexNormals, // true if mesh has unit vertex normals
bool bHasTextureCoordinates // true if mesh has texture coordinates
)
: m_V(initial_vertex_capacity)
, m_F(initial_facet_capacity)
, m_N(bHasVertexNormals?initial_vertex_capacity:0)
, m_T(bHasTextureCoordinates?initial_vertex_capacity:0)
, m_packed_tex_rotate(0)
, m_parent(0)
, m_mesh_parameters(0)
, m_invalid_count(0)
, m_quad_count(0)
, m_triangle_count(0)
, m_mesh_is_closed(0)
, m_mesh_is_manifold(0)
, m_mesh_is_oriented(0)
, m_mesh_is_solid(0)
{
m_top.m_mesh = this;
m_srf_scale[0] = 0.0;
m_srf_scale[1] = 0.0;
m_kstat[0] = 0;
m_kstat[1] = 0;
m_kstat[2] = 0;
m_kstat[3] = 0;
InvalidateBoundingBoxes();
m_partition = 0;
m_hidden_count = 0;
}
ON_Mesh::ON_Mesh( const ON_Mesh& src )
: m_packed_tex_rotate(0)
, m_parent(0)
, m_mesh_parameters(0)
, m_invalid_count(0)
, m_quad_count(0)
, m_triangle_count(0)
, m_mesh_is_closed(0)
, m_mesh_is_manifold(0)
, m_mesh_is_oriented(0)
, m_mesh_is_solid(0)
{
// Do not copy m_mesh_cache. Cached information will
// be recalculated if it is needed.
m_top.m_mesh = this;
m_srf_scale[0] = 0.0;
m_srf_scale[1] = 0.0;
m_kstat[0] = 0;
m_kstat[1] = 0;
m_kstat[2] = 0;
m_kstat[3] = 0;
InvalidateBoundingBoxes();
m_partition = 0;
m_hidden_count = 0;
ON_Mesh::operator=(src);
}
unsigned int ON_Mesh::SizeOf() const
{
unsigned int sz = ON_Geometry::SizeOf();
sz += m_V.SizeOfArray();
sz += m_F.SizeOfArray();
sz += m_N.SizeOfArray();
sz += m_FN.SizeOfArray();
sz += m_T.SizeOfArray();
sz += m_S.SizeOfArray();
sz += m_K.SizeOfArray();
sz += m_C.SizeOfArray();
sz += m_top.m_topv_map.SizeOfArray();
sz += m_top.m_topv.SizeOfArray();
sz += m_top.m_tope.SizeOfArray();
sz += m_top.m_topf.SizeOfArray();
return sz;
}
ON__UINT32 ON_Mesh::DataCRC(ON__UINT32 current_remainder) const
{
const ON_3fPoint* p = m_V.Array();
current_remainder = ON_CRC32(current_remainder,m_V.Count()*sizeof(p[0]),p);
current_remainder = ON_CRC32(current_remainder,m_F.Count()*sizeof(ON_MeshFace),m_F.Array());
const ON_3fVector* v = m_N.Array();
current_remainder = ON_CRC32(current_remainder,m_N.Count()*sizeof(v[0]),v);
return current_remainder;
}
ON_Mesh& ON_Mesh::operator=( const ON_Mesh& src )
{
if ( this != &src )
{
Destroy();
ON_Geometry::operator=(src);
m_V = src.m_V;
m_dV = src.m_dV;
m_F = src.m_F;
m_N = src.m_N;
m_FN = src.m_FN;
m_T = src.m_T;
m_TC = src.m_TC;
m_S = src.m_S;
m_H = src.m_H;
m_hidden_count = src.m_hidden_count;
unsigned int ngon_count = src.HasNgons() ? src.m_Ngon.UnsignedCount() : 0;
if ( ngon_count > 0 )
{
const unsigned int src_V_count = src.m_V.UnsignedCount();
const unsigned int src_F_count = src.m_F.UnsignedCount();
m_Ngon.Reserve(ngon_count);
for ( unsigned int i = 0; i < ngon_count; i++ )
{
const ON_MeshNgon* src_ngon = src.m_Ngon[i];
if ( 0 == src_ngon )
continue;
if ( src_ngon->m_Vcount <= 0 )
continue;
if ( 0 == src_ngon->m_vi )
continue;
if ( src_ngon->m_Vcount > src_V_count )
{
ON_ERROR("Corrupt ngon");
continue;
}
if ( src_ngon->m_Fcount > src_F_count )
{
ON_ERROR("Corrupt ngon");
continue;
}
if ( 0 == src_ngon->m_fi && src_ngon->m_Fcount > 0 )
{
ON_ERROR("Corrupt ngon");
continue;
}
ON_MeshNgon* ngon = m_NgonAllocator.CopyNgon(src_ngon);
if (ngon)
m_Ngon.Append(ngon);
}
if ( 0 != src.NgonMap())
{
this->CreateNgonMap();
}
}
m_Ctag = src.m_Ctag;
m_Ttag = src.m_Ttag;
m_packed_tex_domain[0] = src.m_packed_tex_domain[0];
m_packed_tex_domain[1] = src.m_packed_tex_domain[1];
m_srf_domain[0] = src.m_srf_domain[0];
m_srf_domain[1] = src.m_srf_domain[1];
m_srf_scale[0] = src.m_srf_scale[0];
m_srf_scale[1] = src.m_srf_scale[1];
m_packed_tex_rotate = src.m_packed_tex_rotate;
m_K = src.m_K;
m_C = src.m_C;
m_parent = src.m_parent;
//m_material_index = src.m_material_index;
if ( m_mesh_parameters ) {
delete m_mesh_parameters;
m_mesh_parameters = 0;
}
if ( src.m_mesh_parameters )
m_mesh_parameters = new ON_MeshParameters(*src.m_mesh_parameters);
m_invalid_count = src.m_invalid_count;
m_quad_count = src.m_quad_count;
m_triangle_count = src.m_triangle_count;
m_mesh_is_closed = src.m_mesh_is_closed;
m_mesh_is_manifold = src.m_mesh_is_manifold;
m_mesh_is_oriented = src.m_mesh_is_oriented;
m_mesh_is_solid = src.m_mesh_is_solid;
m_vertex_bbox = src.m_vertex_bbox;
m_tight_bbox_cache = src.m_tight_bbox_cache;
memcpy(m_nbox,src.m_nbox,sizeof(m_nbox));
memcpy(m_tbox,src.m_tbox,sizeof(m_tbox));
int i;
for ( i = 0; i < 4; i++ ) {
if ( m_kstat[i] )
{
delete m_kstat[i];
m_kstat[i] = 0;
}
if ( src.m_kstat[i] )
{
m_kstat[i] = new ON_MeshCurvatureStats(*src.m_kstat[i]);
}
}
// do not copy m_top
// Do not copy m_mesh_cache. Cached information will
// be recalculated if it is needed.
}
return *this;
}
ON_Mesh::~ON_Mesh()
{
Destroy();
m_top.m_mesh = 0;
}
void ON_Mesh::MemoryRelocate()
{
// the back pointer on m_top needs to be updated.
m_top.m_mesh = this;
}
void ON_Mesh::Destroy()
{
PurgeUserData();
DestroyRuntimeCache( true );
m_Ttag.Default();
m_Ctag.Default();
m_dV.Destroy();
m_V.Destroy();
m_F.Destroy();
m_N.Destroy();
m_FN.Destroy();
m_T.Destroy();
m_TC.Destroy();
m_S.Destroy();
m_K.Destroy();
m_C.Destroy();
m_NgonMap.Destroy();
m_Ngon.Destroy();
m_NgonAllocator.DeallocateAllNgons();
m_vertex_bbox = ON_BoundingBox::UnsetBoundingBox;
m_tight_bbox_cache.RemoveAllBoundingBoxes();
}
void ON_Mesh::EmergencyDestroy()
{
DestroyRuntimeCache( false );
m_dV.EmergencyDestroy();
m_V.EmergencyDestroy();
m_F.EmergencyDestroy();
m_N.EmergencyDestroy();
m_FN.EmergencyDestroy();
m_T.EmergencyDestroy();
m_TC.EmergencyDestroy();
m_S.EmergencyDestroy();
m_K.EmergencyDestroy();
m_C.EmergencyDestroy();
m_NgonMap.EmergencyDestroy();
m_Ngon.EmergencyDestroy();
}
static bool ON_MeshIsNotValid(bool bSilentError)
{
return bSilentError ? false : ON_IsNotValid(); // good place for a breakpoint;
}
static unsigned int ON_MeshNgonIsNotValid(bool bSilentError)
{
if (!bSilentError)
ON_IsNotValid(); // good place for a breakpoint;
return 0;
}
static int compare2u(const void* a, const void*b)
{
unsigned int x = ((const unsigned int*)a)[0];
unsigned int y = ((const unsigned int*)b)[0];
if ( x < y)
return -1;
if ( x > y)
return 1;
x = ((const unsigned int*)a)[1];
y = ((const unsigned int*)b)[1];
if ( x < y)
return -1;
if ( x > y)
return 1;
return 0;
}
unsigned int ON_MeshNgon::IsValid(
const ON_MeshNgon* ngon,
unsigned int ngon_index,
ON_TextLog* text_logx,
unsigned int mesh_vertex_count,
unsigned int mesh_face_count,
const ON_MeshFace* mesh_F,
ON_SimpleArray< unsigned int >& workspace_buffer
)
{
workspace_buffer.SetCount(0);
if ( nullptr == ngon )
return true; // null ngons are ok
const ON__INT_PTR lowbit = 1;
const ON__INT_PTR hightbits = ~lowbit;
bool bSilentError = ( 0 != (lowbit & ((ON__INT_PTR)text_logx)) );
ON_TextLog* text_log = (ON_TextLog*)(((ON__INT_PTR)text_logx) & hightbits);
if (ngon->m_Vcount < 3)
{
if ( text_log )
{
text_log->Print("ON_Mesh.Ngon(%u)->m_Vcount < 3.\n",ngon_index);
}
return ON_MeshNgonIsNotValid(bSilentError);
}
if (ngon->m_Fcount < 1)
{
if ( text_log )
{
text_log->Print("ON_Mesh.Ngon(%u)->m_Fcount < 1.\n",ngon_index);
}
return ON_MeshNgonIsNotValid(bSilentError);
}
if ( nullptr == ngon->m_vi )
{
if ( text_log )
{
text_log->Print("ON_Mesh.Ngon(%u)->m_vi is nullptr.\n",ngon_index);
}
return ON_MeshNgonIsNotValid(bSilentError);
}
if ( nullptr == ngon->m_fi )
{
if ( text_log )
{
text_log->Print("ON_Mesh.Ngon(%u)->m_fi is nullptr.\n",ngon_index);
}
return ON_MeshNgonIsNotValid(bSilentError);
}
for (unsigned int nvi = 0; nvi < ngon->m_Vcount; nvi++)
{
if (ngon->m_vi[nvi] >= mesh_vertex_count)
{
if ( text_log )
{
text_log->Print("ON_Mesh.Ngon(%u)->m_vi[%u] is invalid.\n",ngon_index,nvi);
}
return ON_MeshNgonIsNotValid(bSilentError);
}
}
for (unsigned int nfi = 0; nfi < ngon->m_Fcount; nfi++)
{
unsigned int fi = ngon->m_fi[nfi];
if (fi >= mesh_face_count)
{
if (text_log)
{
text_log->Print("ON_Mesh.Ngon(%u)->m_fi[%u] is invalid.\n", ngon_index, nfi);
}
return ON_MeshNgonIsNotValid(bSilentError);
}
}
if (nullptr == mesh_F)
{
// mesh faces are required to check the boundary
return true;
}
if (1 == ngon->m_Fcount && ngon->m_Vcount >= 3 && ngon->m_Vcount <= 4)
{
// fast test for a valid single face ngon
unsigned int fi = ngon->m_fi[0];
if (false == mesh_F[fi].IsValid(mesh_vertex_count))
{
if (text_log)
{
text_log->Print("ON_Mesh.Ngon(%u)->m_fi[0] is invalid.\n", ngon_index);
}
return ON_MeshNgonIsNotValid(bSilentError);
}
const unsigned int* fvi = (const unsigned int*)mesh_F[fi].vi;
for (unsigned int nvi = 0; nvi < ngon->m_Vcount; nvi++)
{
if (fvi[0] == ngon->m_vi[nvi] && fvi[1] == ngon->m_vi[(nvi+1)%ngon->m_Vcount] && fvi[2] == ngon->m_vi[(nvi+2)%ngon->m_Vcount])
{
if (3 == ngon->m_Vcount && fvi[3] == fvi[2])
return 3;
if (4 == ngon->m_Vcount && fvi[3] == ngon->m_vi[(nvi+3)%ngon->m_Vcount])
return 4;
}
}
// boundary may be reversed
}
unsigned int* workspace = workspace_buffer.Reserve(8*ngon->m_Fcount);
ON_2udex* edges = (ON_2udex*)workspace;
unsigned int edge_count = 0;
for (unsigned int nfi = 0; nfi < ngon->m_Fcount; nfi++)
{
unsigned int fi = ngon->m_fi[nfi];
const unsigned int* fvi = (const unsigned int*)(mesh_F[fi].vi);
unsigned int vi0;
unsigned int vi1 = fvi[3];
for (const unsigned int* fvi1 = fvi + 4; fvi < fvi1; fvi++)
{
vi0 = vi1;
vi1 = *fvi;
if (vi0 < vi1)
{
edges[edge_count].i = vi0;
edges[edge_count++].j = vi1;
}
else if (vi0 > vi1)
{
edges[edge_count].i = vi1;
edges[edge_count++].j = vi0;
}
}
}
if (edge_count < ngon->m_Vcount)
{
if (text_log)
{
text_log->Print("ON_Mesh.Ngon(%u) has invalid face or vertex list.\n",ngon_index);
}
return ON_MeshNgonIsNotValid(bSilentError);
}
ON_qsort(edges, edge_count, sizeof(edges[0]),compare2u);
unsigned int bdry_edge_count = 0;
for (unsigned int i = 0; i < edge_count; /*empty iterator*/)
{
const ON_2udex e0 = edges[i++];
if (i < edge_count && e0.i == edges[i].i && e0.j == edges[i].j)
{
// not a boundary edge
for (i++; i < edge_count; i++)
{
if (e0.i != edges[i].i || e0.j != edges[i].j)
break;
}
}
else
{
edges[bdry_edge_count++] = e0;
}
}
if (bdry_edge_count < ngon->m_Vcount)
{
if (text_log)
{
text_log->Print("ON_Mesh.Ngon(%u) has invalid face or vertex list.\n",ngon_index);
}
return ON_MeshNgonIsNotValid(bSilentError);
}
ON_SortUnsignedIntArray( ON::sort_algorithm::quick_sort, workspace, 2*bdry_edge_count );
for (unsigned int nvi = 0; nvi < ngon->m_Vcount; nvi++)
{
if ( nullptr == ON_BinarySearchUnsignedIntArray( ngon->m_vi[nvi], workspace, 2*bdry_edge_count) )
{
if ( text_log )
{
text_log->Print("ON_Mesh.Ngon(%u)->m_vi[%u] is not a boundary vertex.\n",ngon_index,nvi);
}
return ON_MeshNgonIsNotValid(bSilentError);
}
}
// It is possible that the ngon is still not valid.
//
// If bdry_edge_count > ngon->m_Vcount, then there "could" be one or
// more inner "holes" and the ngon->m_vi[] could reference a vertex
// on an inner boundary.
//
// The vertices in ngon->m_v[] may not be a proper outer boundary.
//
// ...
return bdry_edge_count;
}
static void Internal_ON_Mesh_IsCorruptMessage(
bool bSilentError,
bool& bIsCorrupt,
ON_TextLog* text_log,
const wchar_t* corruption_description
)
{
if (false == bIsCorrupt)
{
if (false == bSilentError)
{
ON_ERROR("ON_Mesh data is corrupt.");
}
bIsCorrupt = true;
if (nullptr != text_log)
text_log->PrintString(corruption_description);
}
}
bool ON_Mesh::IsCorrupt(
bool bRepair,
bool bSilentError,
class ON_TextLog* text_log
) const
{
const unsigned int V_count = m_V.UnsignedCount();
unsigned int F_count = m_F.UnsignedCount();
bool bIsCorrupt = false;
// test faces first
for (unsigned int fi = 0; fi < F_count; fi++)
{
unsigned int* fvi = (unsigned int*)m_F[fi].vi;
for (int j = 0; j < 4; j++)
{
if (fvi[j] >= V_count)
{
Internal_ON_Mesh_IsCorruptMessage(
bSilentError,
bIsCorrupt,
text_log,
L"ON_Mesh.m_F[] has out of range vertex indices.\n"
);
if (bRepair)
{
fvi[0] = V_count; // mark this corrupt face for deletion below
// deleting a face makes cached information invalid
// and the fact the face was corrupt makes any
// set values suspect.
const_cast<ON_Mesh*>(this)->m_invalid_count = 0;
const_cast<ON_Mesh*>(this)->m_quad_count = 0;
const_cast<ON_Mesh*>(this)->m_triangle_count = 0;
const_cast<ON_Mesh*>(this)->m_mesh_is_closed = 0;
const_cast<ON_Mesh*>(this)->m_mesh_is_manifold = 0;
const_cast<ON_Mesh*>(this)->m_mesh_is_oriented = 0;
const_cast<ON_Mesh*>(this)->m_mesh_is_solid = 0;
}
}
}
}
if (bIsCorrupt && bRepair)
{
// remove corrupt faces
// N-gon indices reference faces that will get new indices
// or get deleted.
// If the creator couldn't get the vertex indices in a
// face set correctly, then they loose any N-gon work
// too that may or may not have been done correctly.
// (Harsh, I know).
const_cast<ON_Mesh*>(this)->RemoveAllNgons();
unsigned int new_F_count = 0;
ON_MeshFace* F = const_cast<ON_MeshFace*>(m_F.Array());
ON_3fVector* FN =
(F_count == m_FN.UnsignedCount())
? const_cast<ON_3fVector*>(m_FN.Array())
: nullptr;
if ( nullptr == FN)
const_cast<ON_Mesh*>(this)->m_F.SetCount(0);
for (unsigned int fi = 0; fi < F_count; fi++)
{
if (V_count ==(unsigned int)F[fi].vi[0])
continue; // corrupt face
F[new_F_count] = F[fi];
if (nullptr != FN)
FN[new_F_count] = FN[fi];
new_F_count++;
}
const_cast<ON_Mesh*>(this)->m_F.SetCount(new_F_count);
if (nullptr != FN)
const_cast<ON_Mesh*>(this)->m_FN.SetCount(new_F_count);
F_count = new_F_count;
}
if (0 != m_dV.UnsignedCount() && V_count != m_dV.UnsignedCount())
{
Internal_ON_Mesh_IsCorruptMessage(
bSilentError,
bIsCorrupt,
text_log,
L"ON_Mesh.m_dV[] has wrong size.\n"
);
if( bRepair )
const_cast<ON_Mesh*>(this)->m_dV.SetCount(0);
}
return bIsCorrupt;
}
unsigned int ON_MeshNgon::IsValid(
const ON_MeshNgon* ngon,
unsigned int ngon_index,
ON_TextLog* text_logx,
unsigned int mesh_vertex_count,
unsigned int mesh_face_count,
const ON_MeshFace* mesh_F
)
{
// If low bit of text_log pointer is 1, then ON_Error is not called when the ngon is invalid.
ON_SimpleArray< unsigned int > workspace_buffer;
return ON_MeshNgon::IsValid(ngon,ngon_index,text_logx,mesh_vertex_count,mesh_face_count,mesh_F,workspace_buffer);
}
bool ON_Mesh::IsValid( ON_TextLog* text_logx ) const
{
// If low bit of text_log pointer is 1, then ON_Error is not called when the mesh is invalid.
const ON__INT_PTR lowbit = 1;
const ON__INT_PTR hightbits = ~lowbit;
bool bSilentError = ( 0 != (lowbit & ((ON__INT_PTR)text_logx)) );
ON_TextLog* text_log = (ON_TextLog*)(((ON__INT_PTR)text_logx) & hightbits);
if (IsCorrupt(false, bSilentError, text_log))
return false;
const unsigned int facet_count = FaceUnsignedCount();
const unsigned int vertex_count = VertexUnsignedCount();
unsigned int fi, vi;
if (facet_count < 1)
{
if ( text_log )
{
text_log->Print("ON_Mesh.m_F.Count() < 1 (should be at least 1).\n");
}
return ON_MeshIsNotValid(bSilentError);
}
if ( vertex_count < 3 )
{
if ( text_log )
{
text_log->Print("ON_Mesh.m_V.Count() < 3 (should be at least 3).\n");
}
return ON_MeshIsNotValid(bSilentError);
}
if ( m_V.UnsignedCount() != vertex_count )
{
if ( text_log )
{
text_log->Print("ON_Mesh.m_V.Count() = %u (should be %u=vertex_count).\n",
m_V.UnsignedCount(),vertex_count);
}
return ON_MeshIsNotValid(bSilentError);
}
if ( m_dV.UnsignedCount() > 0 && m_dV.UnsignedCount() != vertex_count )
{
if ( text_log )
{
text_log->Print("ON_Mesh.m_dV.Count() = %u (should be 0 or %u=vertex_count).\n",
m_dV.UnsignedCount(),vertex_count);
}
return ON_MeshIsNotValid(bSilentError);
}
if ( 0 != m_dV.UnsignedCount() )
{
if (false == HasSynchronizedDoubleAndSinglePrecisionVertices())
{
if ( text_log )
{
text_log->Print("ON_Mesh.m_dV[] and m_V[] are not synchronized.\n",
m_dV.UnsignedCount(),vertex_count);
}
return ON_MeshIsNotValid(bSilentError);
}
}
if ( m_N.UnsignedCount() > 0 && m_N.UnsignedCount() != vertex_count )
{
if ( text_log )
{
text_log->Print("ON_Mesh.m_N.Count() = %u (should be 0 or %u=vertex_count).\n",
m_N.UnsignedCount(),vertex_count);
}
return ON_MeshIsNotValid(bSilentError);
}
if ( m_T.UnsignedCount() > 0 && m_T.UnsignedCount() != vertex_count )
{
if ( text_log )
{
text_log->Print("ON_Mesh.m_T.Count() = %u (should be 0 or %u=vertex_count).\n",
m_T.UnsignedCount(),vertex_count);
}
return ON_MeshIsNotValid(bSilentError);
}
if ( m_S.UnsignedCount() > 0 && m_S.UnsignedCount() != vertex_count )
{
if ( text_log )
{
text_log->Print("ON_Mesh.m_S.Count() = %u (should be 0 or %u=vertex_count).\n",
m_S.UnsignedCount(),vertex_count);
}
return ON_MeshIsNotValid(bSilentError);
}
if ( HasVertexNormals() )
{
float x;
for ( vi = 0; vi < vertex_count; vi++ ) {
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];
if ( x < 0.985 || x > 1.015 )
{
if ( text_log )
{
text_log->Print("ON_Mesh.m_N[%u] is not a unit vector (length = %g).\n",vi,sqrt(x));
}
return ON_MeshIsNotValid(bSilentError);
}
}
}
int i;
for ( i = 0; i < 3; i++ )
{
const double float_max = 3.402823466e+38;
if ( m_vertex_bbox.IsNotEmpty() )
{
if (
fabs(m_vertex_bbox.m_min.MaximumCoordinate()) > float_max
|| fabs(m_vertex_bbox.m_max.MaximumCoordinate()) > float_max
)
{
// Greg Arden 9 May 2003. Fixes TRR#10604.
// Attempt to detect meshes with non-float-finite vertices by testing the bounding box.
if (text_log)
{
text_log->Print("ON_Mesh.m_fvertex_bbox is not finite. Check for invalid vertices\n");
}
return ON_MeshIsNotValid(bSilentError);
}
}
}
const ON_3dPoint* dV = 0;
while ( HasDoublePrecisionVertices() )
{
const unsigned int vertex_count_local = VertexUnsignedCount();
bool bValidDoubles = (vertex_count_local == m_dV.UnsignedCount());
if ( bValidDoubles )
dV = DoublePrecisionVertices().Array();
bool bValidFloats = (vertex_count_local == m_V.UnsignedCount());
bool bSynchronized = HasSynchronizedDoubleAndSinglePrecisionVertices();
if ( bSynchronized && bValidDoubles && bValidFloats )
break;
if ( !bSynchronized )
{
if ( text_log )
{
text_log->Print("Single and double precision vertices are not synchronized.\n");
}
return ON_MeshIsNotValid(bSilentError);
}
break;
}
if ( 0 != dV )
{
for ( fi = 0; fi < facet_count; fi++ )
{
if ( !m_F[fi].IsValid( vertex_count, dV ) )
{
if ( text_log )
{
if ( !m_F[fi].IsValid( vertex_count) )
text_log->Print("ON_Mesh.m_F[%u].vi[] has invalid vertex indices.\n",fi);
else
text_log->Print("ON_Mesh.m_F[%u] has degenerate double precision vertex locations.\n",fi);
}
return ON_MeshIsNotValid(bSilentError);
}
}
}
else
{
//const ON_3fPoint* fV = m_V.Array();
for ( fi = 0; fi < facet_count; fi++ )
{
// This test was too harsh for float precision meshes
// with nearly degnerate faces after they are transformed
// by a transform with a reasonable sized translation
// component.
// See bug http://dev.mcneel.com/bugtrack/?q=87465
//if ( !m_F[fi].IsValid( vertex_count, fV ) )
//{
// if ( text_log )
// {
// if ( !m_F[fi].IsValid( vertex_count) )
// text_log->Print("ON_Mesh.m_F[%d].vi[] has invalid vertex indices.\n",fi);
// else
// text_log->Print("ON_Mesh.m_F[%d] has degenerate float precision vertex locations.\n",fi);
// }
// return ON_MeshIsNotValid(bSilentError);
//}
if ( !m_F[fi].IsValid( vertex_count ) )
{
if ( text_log )
text_log->Print("ON_Mesh.m_F[%u].vi[] has invalid vertex indices.\n",fi);
return ON_MeshIsNotValid(bSilentError);
}
}
}
const unsigned int ngon_count = m_Ngon.UnsignedCount();
if (ngon_count > 0)
{
ON_SimpleArray< unsigned int > workspace_buffer;
for (unsigned int ngon_index = 0; ngon_index < ngon_count; ngon_index++)
{
const ON_MeshNgon* ngon = m_Ngon[ngon_index];
if (nullptr == ngon)
continue; // valid - means the ngon was removed
if (0 == ON_MeshNgon::IsValid(ngon, ngon_index, text_logx, (unsigned int)vertex_count, (unsigned int)facet_count, m_F.Array(), workspace_buffer))
return false;
}
}
return true;
}
void ON_Mesh::Dump( ON_TextLog& dump ) const
{
const int half_max = 8;
const int fcount = m_F.Count();
int i;
const int vcount = m_V.Count();
ON_3dPoint p, q;
bool bDoubles = vcount > 0
&& HasDoublePrecisionVertices()
&& HasSynchronizedDoubleAndSinglePrecisionVertices();
dump.Print("ON_Mesh: vertex count = %d facet count = %d\n", m_V.Count(), m_F.Count() );
dump.Print("double precision: %s\n",bDoubles?"true":"false");
dump.Print("vertex normals: %s\n",HasVertexNormals()?"true":"false");
dump.Print("face normals: %s\n",HasFaceNormals()?"true":"false");
dump.Print("n-gons: %s\n",HasNgons()?"true":"false");
dump.Print("srf parameters: %s\n",HasSurfaceParameters()?"true":"false");
dump.Print("tex coords: %s\n",HasTextureCoordinates()?"true":"false");
dump.Print("vertex kappa: %s\n",HasPrincipalCurvatures()?"true":"false");
dump.Print("vertex colors: %s\n",HasVertexColors()?"true":"false");
dump.Print("m_Ctag:\n"); dump.PushIndent(); m_Ctag.Dump(dump); dump.PopIndent();
dump.Print("m_packed_tex_rotate: %s\n",m_packed_tex_rotate?"true":"false");
dump.Print("m_packed_tex_domain: (%g,%g)x(%g,%g)\n",
m_packed_tex_domain[0][0],m_packed_tex_domain[0][1],
m_packed_tex_domain[1][0],m_packed_tex_domain[1][1]);
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]);
dump.Print("m_srf_scale: %g,%g\n",m_srf_scale[0],m_srf_scale[0]);
dump.Print("m_Ttag:\n"); dump.PushIndent(); m_Ttag.Dump(dump); dump.PopIndent();
dump.PushIndent();
dump.Print("%d mesh vertices:\n",m_V.Count());
{
dump.PushIndent();
const ON_3dPoint* D = 0;
if ( bDoubles )
{
D = DoublePrecisionVertices().Array();
}
for (i = 0; i < vcount; i++)
{
if ( i == half_max && 2*half_max < vcount )
{
dump.Print("...\n");
i = vcount-half_max;
}
else
{
p = m_V[i];
if ( 0 != D )
{
q = D[i];
dump.Print("m_V[%d] = (%.17g,%.17g,%.17g) D = (%.17g,%.17g,%.17g)\n",
i,
p.x,p.y,p.z,
q.x,q.y,q.z
);
}
else
{
dump.Print("m_V[%d] = (%g,%g,%g)\n",i,p.x,p.y,p.z);
}
}
}
dump.PopIndent();
}
if ( HasVertexNormals() )
{
dump.Print("%d mesh vertex normals:\n",m_N.Count());
{
dump.PushIndent();
for (i = 0; i < vcount; i++)
{
if ( i == half_max && 2*half_max < vcount )
{
dump.Print("...\n");
i = vcount-half_max;
}
else
{
p = m_N[i];
dump.Print("m_N[%d] = (%g,%g,%g)\n",i,p.x,p.y,p.z);
}
}
dump.PopIndent();
}
}
if ( HasTextureCoordinates() )
{
dump.Print("%d mesh vertex texture coordinates:\n",m_T.Count());
{
dump.PushIndent();
for (i = 0; i < vcount; i++)
{
if ( i == half_max && 2*half_max < vcount )
{
dump.Print("...\n");
i = vcount-half_max;
}
else
{
ON_2fPoint tp = m_T[i];
p.x = tp.x;
p.y = tp.y;
dump.Print("m_T[%d] = (%g,%g)\n",i,p.x,p.y);
}
}
dump.PopIndent();
}
}
if ( HasSurfaceParameters() )
{
dump.Print("%d mesh vertex surface parameters:\n",m_S.Count());
{
dump.PushIndent();
for (i = 0; i < vcount; i++)
{
if ( i == half_max && 2*half_max < vcount )
{
dump.Print("...\n");
i = vcount-half_max;
}
else
{
ON_2dPoint srfuv = m_S[i];
dump.Print("m_S[%d] = (%g,%g)\n",i,srfuv.x,srfuv.y);
}
}
dump.PopIndent();
}
}
dump.Print("%d mesh faces:\n",m_F.Count());
{
dump.PushIndent();
for (i = 0; i < fcount; i++)
{
if ( i == half_max && 2*half_max < fcount )
{
dump.Print("...\n");
i = fcount-half_max;
}
else if ( m_F[i].vi[2] == m_F[i].vi[3] )
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]);
else
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]);
}
dump.PopIndent();
}
if ( HasFaceNormals() )
{
dump.Print("%d mesh face normals:\n",m_FN.Count());
{
dump.PushIndent();
for (i = 0; i < fcount; i++)
{
if ( i == half_max && 2*half_max < fcount )
{
dump.Print("...\n");
i = fcount-half_max;
}
else
{
p = m_FN[i];
dump.Print("m_FN[%d] = (%g,%g,%g)\n",i,p.x,p.y,p.z);
}
}
dump.PopIndent();
}
}
if ( HasNgons() )
{
const ON_MeshFaceList mesh_face_list(this);
const int ngon_count = NgonCount();
dump.Print("%d mesh n-gons:\n",ngon_count);
{
dump.PushIndent();
for (i = 0; i < ngon_count; i++)
{
if ( i == half_max && 2*half_max < ngon_count )
{
dump.Print("...\n");
i = ngon_count-half_max;
}
else
{
const ON_MeshNgon* ngon = Ngon(i);
if ( 0 == ngon )
dump.Print("Ngon(%d) = null\n",i);
else
{
dump.Print("Ngon(%d): ",i);
ngon->Dump(dump);
const int bcount = (int)ngon->BoundaryEdgeCount(mesh_face_list);
const int outer_boundary_count = (int)ngon->OuterBoundaryEdgeCount();
const int inner_boundary_count = bcount - outer_boundary_count;
dump.Print(" boundary counts outer = %d, inner = %d", outer_boundary_count, inner_boundary_count);
dump.Print("\n");
}
}
}
dump.PopIndent();
}
}
dump.PopIndent();
}
bool ON_Mesh::WriteFaceArray( int vcount, int fcount, ON_BinaryArchive& file ) const
{
unsigned char cvi[4];
unsigned short svi[4];
const int* vi;
int i_size = 0;
if ( vcount < 256 ) {
i_size = 1; // unsigned chars
}
else if (vcount < 65536 ) {
i_size = 2; // unsigned shorts
}
else {
i_size = 4; // 4 byte ints
}
bool rc = file.WriteInt( i_size );
int i;
switch(i_size) {
case 1:
for ( i = 0; i < fcount && rc ; i++ ) {
vi = m_F[i].vi;
cvi[0] = (unsigned char)vi[0];
cvi[1] = (unsigned char)vi[1];
cvi[2] = (unsigned char)vi[2];
cvi[3] = (unsigned char)vi[3];
rc = file.WriteChar( 4, cvi );
}
break;
case 2:
for ( i = 0; i < fcount && rc ; i++ ) {
vi = m_F[i].vi;
svi[0] = (unsigned short)vi[0];
svi[1] = (unsigned short)vi[1];
svi[2] = (unsigned short)vi[2];
svi[3] = (unsigned short)vi[3];
rc = file.WriteShort( 4, svi );
}
break;
case 4:
for ( i = 0; i < fcount && rc ; i++ ) {
rc = file.WriteInt( 4, m_F[i].vi );
}
break;
}
return rc;
}
bool ON_Mesh::ReadFaceArray( int vcount, int fcount, ON_BinaryArchive& file )
{
unsigned char cvi[4];
unsigned short svi[4];
unsigned int* vi;
int i_size = 0;
if ( m_F.Capacity() < fcount )
m_F.SetCapacity(fcount);
bool rc = file.ReadInt( &i_size );
int i = 0;
switch(i_size) {
case 1:
for ( i = 0; i < fcount && rc ; i++ ) {
rc = file.ReadChar( 4, cvi );
vi = (unsigned int*)m_F[i].vi;
vi[0] = cvi[0];
vi[1] = cvi[1];
vi[2] = cvi[2];
vi[3] = cvi[3];
}
break;
case 2:
for ( i = 0; i < fcount && rc ; i++ ) {
rc = file.ReadShort( 4, svi );
vi = (unsigned int*)m_F[i].vi;
vi[0] = svi[0];
vi[1] = svi[1];
vi[2] = svi[2];
vi[3] = svi[3];
}
break;
case 4:
for ( i = 0; i < fcount && rc ; i++ ) {
rc = file.ReadInt( 4, m_F[i].vi );
}
break;
}
m_F.SetCount(i);
return rc;
}
bool ON_Mesh::Write_1( ON_BinaryArchive& file ) const
{
// ver 1.0 uncompressed format
bool rc = file.WriteArray( m_V );
if (rc) rc = file.WriteArray( m_N );
if (rc) rc = file.WriteArray( m_T );
if (rc) rc = file.WriteArray( m_K );
if (rc) rc = file.WriteArray( m_C );
return rc;
}
bool ON_Mesh::Read_1( ON_BinaryArchive& file )
{
// common to all 1.x formats (uncompressed)
bool rc = file.ReadArray( m_V );
if (rc) rc = file.ReadArray( m_N );
if (rc) rc = file.ReadArray( m_T );
if (rc) rc = file.ReadArray( m_K );
if (rc) rc = file.ReadArray( m_C );
return rc;
}
bool ON_Mesh::Write_2( int Vcount, ON_BinaryArchive& file ) const
{
// ver 2.0 compressed format
const ON::endian e = file.Endian();
bool rc = true;
if ( Vcount > m_V.Count() )
return false;
if ( Vcount > 0 )
{
const int Ncount = (m_V.Count() == m_N.Count()) ? Vcount : 0;
const int Tcount = (m_V.Count() == m_T.Count()) ? Vcount : 0;
const int Kcount = (m_V.Count() == m_K.Count()) ? Vcount : 0;
const int Ccount = (m_V.Count() == m_C.Count()) ? Vcount : 0;
if (e == ON::endian::big_endian)
{
// These calls temporarily put the m_V[], m_N[], m_T[], m_K[]
// and m_C[] arrays in little endian byte order because 3dm archives
// are always in little endian byte order.
//
// This code assumes sizeof(ON_Color)=4, sizeof(float)=4
// and sizeof(double)=8.
// If this is not the case, then changing the 4's and 8's below
// will not work. You will have to copy the mesh definition
// into temporary arrays of 4 byte floats/8 byte doubles
// and them compress the temporary arrays. If you do this,
// then remove the "restore" byte order calls below.
file.ToggleByteOrder( Vcount*3, 4, m_V.Array(), (void*)m_V.Array() );
file.ToggleByteOrder( Ncount*3, 4, m_N.Array(), (void*)m_N.Array() );
file.ToggleByteOrder( Tcount*2, 4, m_T.Array(), (void*)m_T.Array() );
file.ToggleByteOrder( Kcount*2, 8, m_K.Array(), (void*)m_K.Array() );
file.ToggleByteOrder( Ccount, 4, m_C.Array(), (void*)m_C.Array() );
}
if (rc) rc = file.WriteCompressedBuffer( Vcount*sizeof(ON_3fPoint), m_V.Array() );
if (rc) rc = file.WriteCompressedBuffer( Ncount*sizeof(ON_3fVector), m_N.Array() );
if (rc) rc = file.WriteCompressedBuffer( Tcount*sizeof(ON_2fPoint), m_T.Array() );
if (rc) rc = file.WriteCompressedBuffer( Kcount*sizeof(ON_SurfaceCurvature),m_K.Array() );
if (rc) rc = file.WriteCompressedBuffer( Ccount*sizeof(ON_Color), m_C.Array() );
if (e == ON::endian::big_endian)
{
// These calls restore the m_V[], m_N[], m_T[], m_K[] and m_C[] arrays
// to the correct big endian runtime byte order. This must be done even
// if rc is false.
file.ToggleByteOrder( Vcount*3, 4, m_V.Array(), (void*)m_V.Array() );
file.ToggleByteOrder( Ncount*3, 4, m_N.Array(), (void*)m_N.Array() );
file.ToggleByteOrder( Tcount*2, 4, m_T.Array(), (void*)m_T.Array() );
file.ToggleByteOrder( Kcount*2, 8, m_K.Array(), (void*)m_K.Array() );
file.ToggleByteOrder( Ccount, 4, m_C.Array(), (void*)m_C.Array() );
}
}
return rc;
}
bool ON_Mesh::Read_2( int vcount, ON_BinaryArchive& file )
{
// common to all 2.x formats (compressed)
const ON::endian e = file.Endian();
bool rc = true;
if ( vcount > 0 )
{
size_t sz = 0;
bool bFailedCRC;
sz = 0;
if (rc) rc = file.ReadCompressedBufferSize( &sz );
if (rc && sz)
{
if ( sz == vcount*sizeof(m_V[0]) )
{
m_V.SetCapacity(vcount);
if (rc) rc = file.ReadCompressedBuffer( sz,m_V.Array(),&bFailedCRC);
if (rc) m_V.SetCount(vcount);
}
else
{
ON_ERROR("ON_Mesh::Read - compressed vertex point buffer size is wrong.");
rc = false; // buffer is wrong size
}
}
sz = 0;
if (rc) rc = file.ReadCompressedBufferSize( &sz );
if (rc && sz)
{
if ( sz == vcount*sizeof(m_N[0]) )
{
m_N.SetCapacity(vcount);
if (rc) rc = file.ReadCompressedBuffer( sz,m_N.Array(),&bFailedCRC );
if (rc) m_N.SetCount(vcount);
}
else
{
ON_ERROR("ON_Mesh::Read - compressed vertex normal buffer size is wrong.");
rc = false; // buffer is wrong size
}
}
sz = 0;
if (rc) rc = file.ReadCompressedBufferSize( &sz );
if (rc && sz)
{
if ( sz == vcount*sizeof(m_T[0]) )
{
m_T.SetCapacity(vcount);
if (rc) rc = file.ReadCompressedBuffer( sz,m_T.Array(),&bFailedCRC );
if (rc) m_T.SetCount(vcount);
}
else
{
ON_ERROR("ON_Mesh::Read - compressed texture coordinate buffer size is wrong.");
rc = false; // buffer is wrong size
}
}
sz = 0;
if (rc) rc = file.ReadCompressedBufferSize( &sz );
if (rc && sz)
{
if ( sz == vcount*sizeof(m_K[0]) )
{
m_K.SetCapacity(vcount);
if (rc) rc = file.ReadCompressedBuffer( sz,m_K.Array(),&bFailedCRC );
if (rc) m_K.SetCount(vcount);
}
else
{
ON_ERROR("ON_Mesh::Read - compressed vertex curvature buffer size is wrong.");
rc = false; // buffer is wrong size
}
}
sz = 0;
if (rc) rc = file.ReadCompressedBufferSize( &sz );
if (rc && sz)
{
if ( sz == vcount*sizeof(m_C[0]) )
{
m_C.SetCapacity(vcount);
if (rc) rc = file.ReadCompressedBuffer( sz,m_C.Array(),&bFailedCRC );
if (rc) m_C.SetCount(vcount);
}
else
{
ON_ERROR("ON_Mesh::Read - compressed vertex color buffer size is wrong.");
rc = false; // buffer is wrong size
}
}
if (e == ON::endian::big_endian)
{
// This code assumes sizeof(ON_Color)=4, sizeof(float)=4
// and sizeof(double)=8.
// If this is not the case, then changing the 4's and 8's below
// will not work. You will have to read the compressed
// information into temporary arrays of 4 byte floats/8 byte doubles
// and then convert those numbers to whatever is stored in the
// m_V[], m_N[], m_T[], m_K[] and m_C[] arrays/
file.ToggleByteOrder( m_V.Count()*3, 4, m_V.Array(), (void*)m_V.Array() );
file.ToggleByteOrder( m_N.Count()*3, 4, m_N.Array(), (void*)m_N.Array() );
file.ToggleByteOrder( m_T.Count()*2, 4, m_T.Array(), (void*)m_T.Array() );
file.ToggleByteOrder( m_K.Count()*2, 8, m_K.Array(), (void*)m_K.Array() );
file.ToggleByteOrder( m_C.Count()*3, 4, m_C.Array(), (void*)m_C.Array() );
}
}
return rc;
}
static
bool WriteMeshNgons( ON_BinaryArchive& file, const ON_SimpleArray<ON_MeshNgon*>& ngons )
{
unsigned int i, Vcount;
if ( !file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0) )
return false;
bool rc = false;
for(;;)
{
unsigned int ngon_count = ngons.UnsignedCount();
if (!file.WriteInt(ngon_count))
break;
for ( i = 0; i < ngon_count; i++)
{
const ON_MeshNgon* ngon = ngons[i];
Vcount = ( 0 != ngon && ngon->m_Vcount > 0 && ngon->m_Fcount > 0 && 0 != ngon->m_vi && 0 != ngon->m_fi )
? ngon->m_Vcount
: 0;
if ( !file.WriteInt(Vcount) )
break;
if (Vcount <= 0 )
continue;
if ( !file.WriteInt(ngon->m_Fcount) )
break;
if ( !file.WriteInt(Vcount,ngon->m_vi) )
break;
if ( !file.WriteInt(ngon->m_Fcount,ngon->m_fi) )
break;
}
if (i==ngon_count)
rc = true;
break;
}
if ( !file.EndWrite3dmChunk() )
rc = false;
return rc;
}
static
bool ReadMeshNgons( ON_BinaryArchive& file, ON_MeshNgonAllocator& a, ON_SimpleArray<ON_MeshNgon*>& ngons )
{
ngons.SetCount(0);
int major_version = 0;
int minor_version = 0;
unsigned int i, Vcount, Fcount;
if ( !file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version) )
return false;
bool rc = false;
for(;;)
{
unsigned int ngon_count = 0;
if (!file.ReadInt(&ngon_count))
break;
ngons.Reserve(ngon_count);
for ( i = 0; i < ngon_count; i++)
{
Vcount = 0;
if ( !file.ReadInt(&Vcount) )
break;
if ( Vcount <= 0 )
continue;
if ( !file.ReadInt(&Fcount) )
break;
ON_MeshNgon* ngon = a.AllocateNgon(Vcount,Fcount);
if ( !file.ReadInt(Vcount,ngon->m_vi) )
break;
if ( !file.ReadInt(ngon->m_Fcount,ngon->m_fi) )
break;
ngons.Append(ngon);
}
if (i == ngon_count)
rc = true;
break;
}
if ( !file.EndRead3dmChunk() )
rc = false;
return rc;
}
static
bool WriteMeshDoublePrecisionVertices(
ON_BinaryArchive& file,
const ON_SimpleArray<ON_3dPoint>& dV
)
{
const int major_version = 1;
const int minor_version = 0;
if ( !file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version) )
return false;
bool rc = false;
for (;;)
{
const unsigned int dVcount = dV.UnsignedCount();
if (!file.WriteInt(dVcount))
break;
if (0 == dVcount)
{
rc = true;
break;
}
const ON::endian e = file.Endian();
if (e == ON::endian::big_endian)
{
file.ToggleByteOrder(dVcount * 3, 8, dV.Array(), (void*)dV.Array());
}
if (!file.WriteCompressedBuffer(dVcount*sizeof(ON_3dPoint), dV.Array()))
break;
if (e == ON::endian::big_endian)
{
file.ToggleByteOrder(dVcount * 3, 8, dV.Array(), (void*)dV.Array());
}
rc = true;
break;
}
if ( !file.EndWrite3dmChunk() )
rc = false;
return rc;
}
static
bool ReadMeshDoublePrecisionVertices(
ON_BinaryArchive& file,
ON_SimpleArray<ON_3dPoint>& dV
)
{
dV.SetCount(0);
int major_version = 0;
int minor_version = 0;
if ( !file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version) )
return false;
bool rc = false;
for(;;)
{
if (1 != major_version)
break;
unsigned int dVcount = 0;
if (!file.ReadInt(&dVcount))
break;
if (0 == dVcount)
{
rc = true;
break;
}
size_t sz = 0;
if (!file.ReadCompressedBufferSize( &sz ))
break;
if (sz != ((size_t)dVcount)*sizeof(ON_3dPoint))
{
ON_ERROR("Compressed double precision vertex point buffer size is wrong.");
break;
}
dV.SetCapacity(dVcount);
dV.SetCount(dVcount);
ON_3dPoint* a = dV.Array();
if (nullptr == a || dVcount != dV.UnsignedCount())
{
ON_ERROR("Unable to allocate double precision vertex point array.");
break;
}
bool bFailedCRC = 0;
if (!file.ReadCompressedBuffer( sz, a, &bFailedCRC))
break;
rc = true;
break;
}
if (!rc)
dV.SetCount(0);
if ( !file.EndRead3dmChunk() )
rc = false;
return rc;
}
bool ON_Mesh::Write( ON_BinaryArchive& file ) const
{
int i;
//const int major_version = 1; // uncompressed
//const int major_version = 2; // beta format (never used)
const int major_version = 3; // compressed
const int minor_version
= (file.Archive3dmVersion() >= 60)
//? 6 // ngons saved in V6 files as of mid November, 2013
//? 7 // double precision vertices no longer on user data.
? 8 // double precision vertex box
: 5;
bool rc = file.Write3dmChunkVersion(major_version,minor_version);
const unsigned int vcount = VertexUnsignedCount();
const unsigned int fcount = FaceUnsignedCount();
if (rc) rc = file.WriteInt( vcount );
if (rc) rc = file.WriteInt( fcount );
if (rc) rc = file.WriteInterval( m_packed_tex_domain[0] );
if (rc) rc = file.WriteInterval( m_packed_tex_domain[1] );
if (rc) rc = file.WriteInterval( m_srf_domain[0] );
if (rc) rc = file.WriteInterval( m_srf_domain[1] );
if (rc) rc = file.WriteDouble( 2, m_srf_scale );
// legacy float precision vertex bounding box
float fbbox[2][3] = { { 1.0f, 1.0f, 1.0f }, { -1.0f, -1.0f, -1.0f } };
if (m_vertex_bbox.IsNotEmpty())
{
fbbox[0][0] = ON_FloatFloor(m_vertex_bbox.m_min.x);
fbbox[0][1] = ON_FloatFloor(m_vertex_bbox.m_min.y);
fbbox[0][2] = ON_FloatFloor(m_vertex_bbox.m_min.z);
fbbox[1][0] = ON_FloatCeil(m_vertex_bbox.m_max.x);
fbbox[1][1] = ON_FloatCeil(m_vertex_bbox.m_max.y);
fbbox[1][2] = ON_FloatCeil(m_vertex_bbox.m_max.z);
}
if (rc) rc = file.WriteFloat( 6, &fbbox[0][0] );
if (rc) rc = file.WriteFloat( 6, &m_nbox[0][0] );
if (rc) rc = file.WriteFloat( 4, &m_tbox[0][0] );
// archive int value meaning: -1 = unknown 0 = mesh is not closed, 1 = mesh is closed
i = -1;
switch( m_mesh_is_closed )
{
case 0: // unset
i = -1;
break;
case 1: // closed
i = 1;
break;
case 2: // not closed
i = 0;
break;
}
if (rc) rc = file.WriteInt( i );
unsigned char b = m_mesh_parameters ? 1 : 0;
if (rc) rc = file.WriteChar(b);
if (rc && b) {
if (rc) rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 );
if (rc) {
rc = m_mesh_parameters->Write(file);
if ( !file.EndWrite3dmChunk() )
rc = false;
}
}
for ( i = 0; rc && i < 4; i++ ) {
b = m_kstat[i] ? 1 : 0;
rc = file.WriteChar(b);
if (b) {
rc = file.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 0 );
if (rc) {
rc = m_kstat[i]->Write(file);
if ( !file.EndWrite3dmChunk() )
rc = false;
}
}
}
if (rc) rc = WriteFaceArray( vcount, fcount, file );
if (rc) {
// major version is a hard coded 3
//if ( major_version == 1 )
// rc = Write_1(file);
//else if ( major_version == 3 )
rc = Write_2(vcount,file);
//else
// rc = false;
}
// added for minor version 1.2 and 3.2
i = m_packed_tex_rotate ? 1 : 0;
if (rc) rc = file.WriteInt( i );
// added for minor version 3.3
if (rc) rc = file.WriteUuid( m_Ttag.m_mapping_id );
// compressed m_S[]
if ( rc && vcount > 0U )
{
// Before 201011049 there was a bug that let m_S[] arrays
// with the wrong size get saved in files.
const unsigned int Scount = (vcount == m_S.UnsignedCount()) ? m_S.UnsignedCount() : 0;
const ON::endian e = file.Endian();
if (e == ON::endian::big_endian)
{
file.ToggleByteOrder( Scount*2, 8, m_S.Array(), (void*)m_S.Array() );
}
if (rc) rc = file.WriteCompressedBuffer( Scount*sizeof(ON_2dPoint),m_S.Array() );
if (e == ON::endian::big_endian)
{
file.ToggleByteOrder( Scount*2, 8, m_S.Array(), (void*)m_S.Array() );
}
}
// added for minor version 3.4
if (rc) rc = m_Ttag.Write(file);
// added for minor version 3.5
if (rc) rc = file.WriteChar( m_mesh_is_manifold );
if (rc) rc = file.WriteChar( m_mesh_is_oriented );
if (rc) rc = file.WriteChar( m_mesh_is_solid );
if ( rc && minor_version >= 6 )
{
// added n-gons version 3.6
bool bHasNgons = HasNgons();
rc = file.WriteBool(bHasNgons);
if (rc && bHasNgons)
rc = WriteMeshNgons(file,m_Ngon);
if (rc && minor_version >= 7)
{
// added explicit double precision vertices chunk version 3.7
// (used to be on user data)
const bool bHasDoublePrecisionVertices = HasDoublePrecisionVertices();
if (rc) rc = file.WriteBool(bHasDoublePrecisionVertices);
if (rc && bHasDoublePrecisionVertices)
rc = WriteMeshDoublePrecisionVertices(file, m_dV);
if (rc && minor_version >= 8)
{
rc = file.WriteBoundingBox(m_vertex_bbox);
}
}
}
if (rc
&& 50 == file.Archive3dmVersion()
&& vcount > 0
&& HasSynchronizedDoubleAndSinglePrecisionVertices()
)
{
// save double precision vertices using V5 user data.
ON_V5_MeshDoubleVertices* ud = ON_V5_MeshDoubleVertices::AttachV5(this);
if (nullptr != ud)
{
ud->m_V5_dV = m_dV;
ud->m_dcount = ud->m_V5_dV.UnsignedCount();
ud->m_dCRC = ud->DoubleCRC();
ud->m_fcount = m_V.UnsignedCount();
ud->m_fCRC = ON_V5_MeshDoubleVertices::FloatCRC(m_V);
// This obsolete user data will be deleted after it is written to the V5 .3dm file.
}
}
return rc;
}
//// This id was used in the ON_Mesh::m_mapping_id
//// field to indicate the texture coordinates are the
//// canonical ON_Mesh uv texture coordinates by the
//// OpenNURBS parameteric surface meshers like
//// ON_Surface::CreateMesh() and ON_Brep::CreateMesh().
//
//// {B988A6C2-61A6-45a7-AAEE-9AED7EF4E316}
static const ON_UUID obsolete_default_srfp_mapping_id = { 0xb988a6c2, 0x61a6, 0x45a7, { 0xaa, 0xee, 0x9a, 0xed, 0x7e, 0xf4, 0xe3, 0x16 } };
bool ON_TextureMapping::Internal_WriteV5(
ON_BinaryArchive& file
) const
{
bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1);
if (rc)
{
for(;;)
{
rc = file.WriteUuid( Id() );
if (!rc) break;
rc = file.WriteInt( static_cast<unsigned int>(m_type) );
if (!rc) break;
rc = file.WriteInt( static_cast<unsigned int>(m_projection) );
if (!rc) break;
rc = file.WriteXform( m_Pxyz );
if (!rc) break;
// Do not write m_Nxyz - it is calculated from m_Pxyz.
rc = file.WriteXform( m_uvw );
if (!rc) break;
rc = file.WriteString(Name());
if (!rc) break;
rc = file.WriteObject(m_mapping_primitive.get());
if (!rc) break;
// 13 October 2006 ver 1.1 fields
rc = file.WriteInt( static_cast<unsigned int>(m_texture_space) );
if (!rc) break;
rc = file.WriteBool(m_bCapped);
if (!rc) break;
break;
}
if ( !file.EndWrite3dmChunk() )
rc = false;
}
return rc;
}
bool ON_TextureMapping::Internal_ReadV5(
ON_BinaryArchive& file
)
{
*this = ON_TextureMapping::Unset;
int major_version = 0;
int minor_version = 0;
bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
if (rc)
{
if ( 1 == major_version )
{
// DO NOT SAVE m_mapping_index in archive.
// 1.0 fields
for(;;)
{
ON_UUID id = ON_nil_uuid;
rc = file.ReadUuid( id );
if (!rc) break;
if (id == ON_nil_uuid || id == ON_TextureMapping::SurfaceParameterTextureMapping.Id())
{
// nil ids are no longer permitted and
// system component ids can not be used by
// ordinary components.
id = ON_CreateId();
}
SetId(id);
unsigned int type_as_unsigned = 0;
rc = file.ReadInt( &type_as_unsigned );
if (!rc) break;
m_type = TypeFromUnsigned(type_as_unsigned);
unsigned int projection_as_unsigned = 0;
rc = file.ReadInt( &projection_as_unsigned );
if (!rc) break;
m_projection = ProjectionFromUnsigned(projection_as_unsigned);
rc = file.ReadXform( m_Pxyz );
if (!rc) break;
m_Pxyz.GetSurfaceNormalXform(m_Nxyz);
rc = file.ReadXform( m_uvw );
if (!rc) break;
ON_wString name;
rc = file.ReadString(name);
if (!rc) break;
SetName(name);
ON_Object* pMappingPrimitive = nullptr;
rc = (file.ReadObject(&pMappingPrimitive) >= 0);
if (!rc) break;
m_mapping_primitive.reset(pMappingPrimitive);
if ( minor_version >= 1 )
{
unsigned int texture_space_as_unsigned = 0;
rc = file.ReadInt(&texture_space_as_unsigned);
if (!rc) break;
m_texture_space = TextureSpaceFromUnsigned(texture_space_as_unsigned);
rc = file.ReadBool(&m_bCapped);
if (!rc) break;
}
break;
}
}
if ( !file.EndRead3dmChunk() )
rc = false;
}
return rc;
}
bool ON_TextureMapping::Write(
ON_BinaryArchive& archive
) const
{
return Internal_WriteV5(archive);
}
bool ON_TextureMapping::Read(
ON_BinaryArchive& archive
)
{
return Internal_ReadV5(archive);
}
static
void GetSurfaceParametersHelper( const ON_Mesh& mesh,
double tex_x, double tex_y,
double* srf_s, double* srf_t )
{
// convert texture coordinates to surface parameters
// Used to reconstruct m_S[] when old files are read.
double tex_s, tex_t;
if ( mesh.m_packed_tex_rotate )
{
// undo rotation and normalize
tex_s = mesh.m_packed_tex_domain[1].NormalizedParameterAt( tex_y );
tex_t = 1.0 - mesh.m_packed_tex_domain[0].NormalizedParameterAt( tex_x );
}
else
{
// normalize
tex_s = mesh.m_packed_tex_domain[0].NormalizedParameterAt( tex_x );
tex_t = mesh.m_packed_tex_domain[1].NormalizedParameterAt( tex_y );
}
*srf_s = mesh.m_srf_domain[0].ParameterAt(tex_s);
*srf_t = mesh.m_srf_domain[1].ParameterAt(tex_t);
}
bool ON_Mesh::Read( ON_BinaryArchive& file )
{
Destroy();
int major_version = 0;
int minor_version = 0;
int i;
bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
if (rc && (1 == major_version || 3 == major_version) )
{
int vcount = 0;
int fcount = 0;
if (rc) rc = file.ReadInt( &vcount );
if (rc) rc = file.ReadInt( &fcount );
if (rc) rc = file.ReadInterval( m_packed_tex_domain[0] );
if (rc) rc = file.ReadInterval( m_packed_tex_domain[1] );
if (rc) rc = file.ReadInterval( m_srf_domain[0] );
if (rc) rc = file.ReadInterval( m_srf_domain[1] );
if (rc) rc = file.ReadDouble( 2, m_srf_scale );
float fbbox[2][3] = { { 1.0f, 1.0f, 1.0f }, { -1.0f, -1.0f, -1.0f } };
if (rc) rc = file.ReadFloat( 6, &fbbox[0][0] );
if (rc && fbbox[0][0] <= fbbox[1][0] && fbbox[0][1] <= fbbox[1][1] && fbbox[0][2] <= fbbox[1][2] )
{
// When minor_version >= 8, m_vertex_bbox will be read from the archive later.
m_vertex_bbox.m_min = ON_3fPoint(fbbox[0]);
m_vertex_bbox.m_max = ON_3fPoint(fbbox[1]);
}
if (rc) rc = file.ReadFloat( 6, &m_nbox[0][0] );
if (rc) rc = file.ReadFloat( 4, &m_tbox[0][0] );
// int value meaning: -1 = unknown 0 = mesh is not closed, 1 = mesh is closed
i = -1;
if (rc) rc = file.ReadInt( &i );
if (rc)
{
switch(i)
{
case 0: // not closed;
SetClosed(0);
break;
case 1: // closed;
SetClosed(1);
break;
case 2: // 13 April 2010 Dale Lear - "2" value is obsolete but appears in old files
SetClosed(1);
break;
}
}
unsigned char b = 0;
ON__UINT32 tcode=0;
ON__INT64 big_value=0;
if (rc) rc = file.ReadChar(&b);
if (rc && b)
{
// mesh parameters are in an anonymous chunk
rc = file.BeginRead3dmBigChunk(&tcode,&big_value);
if (rc)
{
if ( TCODE_ANONYMOUS_CHUNK == tcode )
{
m_mesh_parameters = new ON_MeshParameters();
rc = m_mesh_parameters->Read( file );
}
else
rc = false;
if (!file.EndRead3dmChunk())
rc = false;
}
}
for ( i = 0; rc && i < 4; i++ )
{
rc = file.ReadChar(&b);
if (rc && b)
{
// m_kstat[i] curvature stats are in an anonymous chunk
tcode = 0;
big_value = 0;
rc = file.BeginRead3dmBigChunk( &tcode, &big_value );
if (rc)
{
if ( TCODE_ANONYMOUS_CHUNK == tcode )
{
m_kstat[i] = new ON_MeshCurvatureStats();
rc = m_kstat[i]->Read(file);
}
else
rc = false;
if ( !file.EndRead3dmChunk() )
rc = false;
}
}
}
if (rc) rc = ReadFaceArray( vcount, fcount, file );
if (rc) {
if ( major_version==1) {
rc = Read_1(file);
}
else if ( major_version == 3 ) {
rc = Read_2(vcount,file);
}
else
rc = false;
}
if ( minor_version >= 2 )
{
int b_packed_tex_rotate = m_packed_tex_rotate;
if (rc) rc = file.ReadInt( &b_packed_tex_rotate );
m_packed_tex_rotate = b_packed_tex_rotate?true:false;
}
if ( 3 == major_version )
{
if ( minor_version >= 3 )
{
// added for minor version 3.3
if (rc) rc = file.ReadUuid( m_Ttag.m_mapping_id );
// compressed m_S[]
if ( rc && vcount > 0 )
{
size_t sz = 0;
bool bFailedCRC=false;
if (rc) rc = file.ReadCompressedBufferSize( &sz );
if (rc && sz)
{
if ( sz == vcount*sizeof(ON_2dPoint) )
{
m_S.SetCapacity(vcount);
if (rc) rc = file.ReadCompressedBuffer( sz, m_S.Array(), &bFailedCRC );
if (rc) m_S.SetCount(vcount);
if (ON::endian::big_endian == file.Endian())
{
file.ToggleByteOrder( m_S.Count()*2, 8, m_S.Array(), (void*)m_S.Array() );
}
}
else
{
ON_ERROR("ON_Mesh::Read - surface parameter buffer size is wrong.");
if ( rc
&& file.ArchiveOpenNURBSVersion() <= 201011049
&& 0 == (sz % sizeof(ON_2dPoint))
&& sz >= sizeof(ON_2dPoint)
)
{
// Before 201011049 there was a bug that let m_S[] arrays with
// the wrong size get saved in files. There was also a bug in
// the Rhino .OBJ file reader that created meshes with m_S[]
// arrays that had the wrong size. The next 4 lines of code
// let us read the junk, discard it and then successfully read
// the rest of the file.
int Scount = (int)(sz / sizeof(ON_2dPoint));
m_S.SetCapacity(Scount);
rc = file.ReadCompressedBuffer( sz, m_S.Array(), &bFailedCRC );
m_S.Destroy();
}
else
{
rc = false; // buffer is wrong size
}
}
}
}
if ( minor_version >= 4 && file.ArchiveOpenNURBSVersion() >= 200606010 )
{
if (rc) rc = m_Ttag.Read(file);
if ( minor_version >= 5 )
{
if (rc) rc = file.ReadChar( &m_mesh_is_manifold );
if (rc) rc = file.ReadChar( &m_mesh_is_oriented );
if (rc) rc = file.ReadChar( &m_mesh_is_solid );
if ( rc && minor_version >= 6 )
{
// added n-gons version 3.6
bool bHasNgons = false;
rc = file.ReadBool(&bHasNgons);
if (rc && bHasNgons)
rc = ReadMeshNgons(file, m_NgonAllocator, m_Ngon);
if (rc && minor_version >= 7)
{
bool bHasDoublePrecisionVertices = false;
rc = file.ReadBool(&bHasDoublePrecisionVertices);
bool bHasInvalidDoublePrecisionVertices = false;
if (bHasDoublePrecisionVertices)
{
// Added explicit double precision vertices chunk version 3.7
// (used to be on user data)
rc = ReadMeshDoublePrecisionVertices(file, m_dV);
if (rc && m_dV.UnsignedCount() == m_V.UnsignedCount())
{
// Validate m_dV[] - some files contain different values for m_V[] and m_dV[].
// When this happens, delete m_dV[].
const unsigned int dvcount = m_dV.UnsignedCount();
for (unsigned int vdex = 0; vdex < dvcount; vdex++)
{
const ON_3fPoint P(m_dV[vdex]);
const double m = fabs(m_V[vdex].MaximumCoordinate());
const double d = fabs((P - m_V[vdex]).MaximumCoordinate());
if (d <= m*1.0e-6)
continue;
bHasInvalidDoublePrecisionVertices = true;
m_dV.Destroy();
break;
}
}
if (rc && m_dV.UnsignedCount() == m_V.UnsignedCount() && minor_version <= 7)
{
m_vertex_bbox.Set(m_dV, false);
}
}
if (rc && minor_version >= 8)
{
rc = file.ReadBoundingBox(m_vertex_bbox);
}
if (bHasInvalidDoublePrecisionVertices)
{
m_vertex_bbox.Set(m_V, false);
}
}
}
}
}
}
}
if ( 0 == m_S.Count()
&& m_V.Count() > 0
&& HasTextureCoordinates()
&& m_srf_domain[0].IsIncreasing()
&& m_srf_domain[1].IsIncreasing()
&& m_packed_tex_domain[0].IsInterval()
&& m_packed_tex_domain[1].IsInterval()
&& 0 == m_Ttag.m_mapping_crc
&& ON_UuidIsNil(m_Ttag.m_mapping_id)
)
{
// This is a mesh from an old file - but there is enough
// information to calculate the m_S[] values from the
// m_T[] values.
m_S.SetCapacity(vcount);
m_S.SetCount(0);
ON_2dPoint sp;
ON_2fPoint tc;
for ( i = 0; i < vcount; i++)
{
tc = m_T[i];
sp.x = tc.x;
sp.y = tc.y;
GetSurfaceParametersHelper(*this,sp.x,sp.y,&sp.x,&sp.y);
m_S.Append(sp);
}
m_Ttag.SetDefaultSurfaceParameterMappingTag();
}
}
return rc;
}
ON::object_type ON_Mesh::ObjectType() const
{
return ON::mesh_object;
}
int ON_Mesh::Dimension() const
{
return 3;
}
#if defined(ON_COMPILER_MSC)
// Disable the MSC /W4 warning
// C4189: 'breakpoint_here_for_bad_vbox' : local variable is initialized but not referenced
// on the line
// int breakpoint_here_for_bad_vbox = 0.
// The warning disable is here because MS is ignoring it
// if I put it inside of the function.
#pragma ON_PRAGMA_WARNING_PUSH
#pragma ON_PRAGMA_WARNING_DISABLE_MSC( 4189 )
#endif
float ON_FloatFloor(double x)
{
// If x is a NaN, you get what you deserve.
//
// If x is a finite valid double in the range -3.402823466e+38
// to +3.402823466e+38, then returned value of f is the largest
// float value that is mathematically less than or equal to the
// value of x.
//
// If x is not in the float range or x is a NaN, then you get
// what you deserve.
//
// ON_FLOAT_EPSILON = 1.192092896e-07 is the smallest number such that
// 1.0f + 1.192092896e-07f > 1.0f.
//
// If x < 0, then (1.0 + 0.5*1.192092896e-07)*x rounds x down so
// that converting the double precision mantissa cannot create a
// float value that is mathematically larger than the value of x.
//
// If x > 0, then (1.0 - 0.5*1.192092896e-07)*x rounds x down so
// that converting the double precision mantissa cannot create a
// float value that is mathematically larger than the value of x.
//
const double e = (x < 0.0) ? (1.0 + 0.5*ON_FLOAT_EPSILON) : (1.0 - 0.5*ON_FLOAT_EPSILON);
float f;
f = (float)(e*x);
return f;
}
float ON_FloatCeil(double x)
{
float f = (x != 0.0) ? (-ON_FloatFloor(-x)) : ((float)x);
return f;
}
bool ON_Mesh::GetBBox( // returns true if successful
double* boxmin, // minimum
double* boxmax, // maximum
bool bGrowBox
) const
{
bool rc = false;
const unsigned int facet_count = FaceCount();
const unsigned int vertex_count = VertexCount();
if ( facet_count >= 1 && vertex_count >= 3 )
{
rc = m_vertex_bbox.IsNotEmpty();
if ( false == rc )
{
if ( HasDoublePrecisionVertices() )
{
const ON_3dPointArray& dV = DoublePrecisionVertices();
if ( dV.UnsignedCount() == m_V.UnsignedCount() )
rc = m_vertex_bbox.Set(dV, false);
}
if (false == rc)
{
rc = m_vertex_bbox.Set(m_V, false);
}
}
if ( rc )
{
ON_BoundingBox vbox(m_vertex_bbox);
if ( bGrowBox )
{
vbox.Union( ON_BoundingBox(ON_3dPoint(boxmin),ON_3dPoint(boxmax)) );
}
boxmin[0] = vbox.m_min.x;
boxmin[1] = vbox.m_min.y;
boxmin[2] = vbox.m_min.z;
boxmax[0] = vbox.m_max.x;
boxmax[1] = vbox.m_max.y;
boxmax[2] = vbox.m_max.z;
}
}
return rc;
}
#if defined(ON_COMPILER_MSC)
#pragma ON_PRAGMA_WARNING_POP
#endif
bool ON_Mesh::IsDeformable() const
{
return true;
}
bool ON_Mesh::MakeDeformable()
{
return true;
}
bool ON_Mesh::Transform(
const ON_Xform& xform
)
{
// Tansform user data before doing sanity checks in case rogue code
// damages m_V[], m_dV[] or other data members.
TransformUserData(xform);
DestroyTree();
const unsigned int vertex_count = VertexUnsignedCount();
const bool bIsValid_fV = (vertex_count == m_V.UnsignedCount());
if (false == bIsValid_fV)
m_V.SetCount(0);
bool bIsValid_dV = (vertex_count == m_dV.UnsignedCount());
// 2 Jan 2020 S. Baer (RH-54464)
// If the transform is moving the mesh into or out of "beyond single precision",
// set up double precision vertices in order to get our best possible precision
// after the transform.
if (false == bIsValid_dV)
{
ON_BoundingBox bbox = BoundingBox();
ON_BoundingBox transformed_bbox = bbox;
transformed_bbox.Transform(xform);
if (ON_BeyondSinglePrecision(bbox, nullptr) || ON_BeyondSinglePrecision(transformed_bbox, nullptr))
{
UpdateDoublePrecisionVertices();
bIsValid_dV = (vertex_count == m_dV.UnsignedCount());
}
}
if (false == bIsValid_dV)
m_dV.SetCount(0);
const bool bSyncheddV = bIsValid_fV && bIsValid_dV && HasSynchronizedDoubleAndSinglePrecisionVertices();
if (bIsValid_dV)
ON_TransformPointList(3, false, vertex_count, 3, &m_dV[0][0], xform);
double d = xform.Determinant();
bool rc = false;
if ( bSyncheddV )
{
// transforming the double precision vertices is the
// best way to set the floats.
UpdateSinglePrecisionVertices();
rc = true;
}
else if ( bIsValid_fV )
{
rc = ON_TransformPointList( 3, false, vertex_count, 3, &m_V[0][0], xform );
}
if ( rc )
{
m_Ctag.Transform(xform);
m_Ttag.Transform(xform);
int tci, tccnt = m_TC.Count();
for ( tci = 0; tci < tccnt; tci++ )
{
m_TC[tci].m_tag.Transform(xform);
}
}
if ( rc && 0.0 == d )
{
// mesh has been squashed to a plane (or worse)
if ( HasVertexNormals() )
{
ComputeFaceNormals();
ComputeVertexNormals();
}
else if ( HasFaceNormals() )
{
ComputeFaceNormals();
}
}
else if ( rc )
{
if ( HasVertexNormals() )
{
// See http://www.gignews.com/realtime020100.htm or these
// references.
//
// 1. Hanrahan, Pat,
// "A Survey of Ray-Surface Intersection Algorithms",
// chapter 3 in Andrew Glassner (editor),
// An Introduction to Ray Tracing,
// Academic Press Inc., London, 1989.
//
// 2. Turkowski, Ken,
// "Properties of Surface-Normal Transformations",
// in Andrew Glassner (editor),
// Graphics Gems, Academic Press, Inc.,
// pp. 539-547, 1990.
ON_Xform N_xform;
const double det = xform.GetSurfaceNormalXform(N_xform);
rc = ON_TransformVectorList( 3, vertex_count, 3, &m_N[0][0], N_xform )?true:false;
if ( det < 0.0 )
{
FlipVertexNormals();
}
UnitizeVertexNormals();
}
if ( rc && HasFaceNormals() )
{
ComputeFaceNormals();
}
}
if ( rc && HasPrincipalCurvatures() )
{
if ( fabs(fabs(d) - 1.0) > ON_SQRT_EPSILON )
{
// If it's a uniform scale, handle it, otherwise we can't do it.
double scale = xform.m_xform[0][0];
if ( 0.0 != scale && 0.0 != d
&& scale == xform.m_xform[1][1]
&& scale == xform.m_xform[2][2]
&& fabs(d - scale*scale*scale) <= d*ON_SQRT_EPSILON )
{
// uniform scale
const double ks = 1.0/scale;
ON_SurfaceCurvature* sc = m_K.Array();
int ki = m_K.Count();
while ( ki-- )
{
sc->k1 *= ks;
sc->k2 *= ks;
sc++;
}
// update curvature stats.
for ( int j = 0; j < 4; j++ )
{
if ( m_kstat[j] )
m_kstat[j]->Set( m_kstat[j]->m_style,m_K.Count(),m_K.Array(),m_N.Array() );
}
}
else
{
ON_ERROR("ON_Mesh::Transform() cannot apply this transform to curvatures.\n");
rc = false;
}
}
}
InvalidateVertexBoundingBox();
InvalidateVertexNormalBoundingBox();
if ( fabs(d) <= ON_ZERO_TOLERANCE )
DestroyTopology(); // transform may not be one-to-one on vertices
return rc;
}
void ON_Mesh::DestroyRuntimeCache( bool bDelete )
{
int i;
DestroyTree(bDelete);
if (bDelete )
{
DestroyPartition();
m_top.Destroy();
DeleteMeshParameters();
InvalidateCurvatureStats();
}
else
{
// do not free any memory
m_top.EmergencyDestroy();
}
InvalidateBoundingBoxes();
m_partition = 0;
m_mesh_parameters = 0;
m_top.m_mesh = this;
m_parent = 0;
//m_material_index = -1;
m_mesh_is_closed = 0;
m_mesh_is_manifold = 0;
m_mesh_is_oriented = 0;
m_mesh_is_solid = 0;
for ( i = 0; i < 4; i++ )
{
m_kstat[i] = 0;
}
}
bool ON_Mesh::SwapCoordinates(
int i, int j // indices of coords to swap
)
{
if ( i == j )
return true;
const unsigned int vertex_count = VertexUnsignedCount();
const bool bIsValid_fV = (vertex_count == m_V.UnsignedCount());
const bool bIsValid_dV = (vertex_count == m_dV.UnsignedCount());
bool rc = false;
if (bIsValid_dV && ON_SwapPointListCoordinates(vertex_count, 3, &m_dV[0][0], i, j))
rc = true;
if (bIsValid_fV && ON_SwapPointListCoordinates(vertex_count, 3, &m_V[0][0], i, j))
rc = true;
if ( rc && HasVertexNormals() )
{
rc = ON_SwapPointListCoordinates( vertex_count, 3, &m_N[0][0], i, j );
}
if ( rc )
{
float x;
if( m_vertex_bbox.IsNotEmpty())
m_vertex_bbox.SwapCoordinates(i, j);
if ( m_nbox[0][0] <= m_nbox[1][0] )
{
x = m_nbox[0][i]; m_nbox[0][i] = m_nbox[0][j]; m_nbox[0][j] = x;
x = m_nbox[1][i]; m_nbox[1][i] = m_nbox[1][j]; m_nbox[1][j] = x;
}
}
return rc;
}
void ON_Mesh::SetClosed(int b)
{
// 6 Novermber 2003 Dale Lear - let expert user set m_mesh_is_closed
char mesh_is_closed = 0;
switch(b)
{
case 0: // not closed - at least one boundary edge
mesh_is_closed = 2;
SetSolidOrientation(0);
break;
case 1: // all edges are shared
// DO NOT SET m_mesh_is_solid here.
// Meshes can be closed but not solid
mesh_is_closed = 1;
break;
case 2: // 31 April 2010 Dale Lear - 2 is obsolete - it's either 0 or 1 now.
mesh_is_closed = 1;
// DO NOT SET m_mesh_is_solid here.
// Meshes can be closed but not solid
break;
default:
mesh_is_closed = 0; // unset
break;
}
if ( 0 == mesh_is_closed || m_mesh_is_closed != mesh_is_closed )
{
m_mesh_is_closed = mesh_is_closed;
m_mesh_is_manifold = 0; // unset - will be reevaluated when needed
m_mesh_is_oriented = 0; // unset - will be reevaluated when needed
}
}
void ON_Mesh::SetSolidOrientation(int solid_orientation)
{
switch(solid_orientation)
{
case -1: // closed oriented manifold solid with inward face normals
SetClosed(1);
m_mesh_is_manifold = 1;
m_mesh_is_oriented = 1;
m_mesh_is_solid = 2;
break;
case 0: // not solid
m_mesh_is_solid = 3;
// DO NOT SET m_mesh_is_closed here.
// Meshes can be closed but not solid
break;
case 1: // closed oriented manifold solid with outward face normals
SetClosed(1);
m_mesh_is_manifold = 1;
m_mesh_is_oriented = 1;
m_mesh_is_solid = 1;
break;
default:
m_mesh_is_solid = 0;
break;
}
}
static
int ON_MeshIsManifold_CompareV( const void* a, const void* b )
{
return memcmp(a,b,sizeof(ON_3fPoint));
/*
float d;
const float* fa = (const float*)a;
const float* fb = (const float*)b;
if ( 0.0f == (d = (*fa++ - *fb++)) )
{
if ( 0.0f == (d = (*fa++ - *fb++)) )
{
if ( 0.0f == (d = (*fa++ - *fb++)) )
return 0;
}
}
return ( d < 0.0f ) ? -1 : 1;
*/
}
static
int ON_MeshGetVertexEdges_Compare2dex( const void* a, const void* b )
{
return ON_Compare2dex((const ON_2dex*)a,(const ON_2dex*)b);
}
static
int ON_MeshIsManifold_Compare3dex( const void* a, const void* b )
{
return ON_Compare3dex((const ON_3dex*)a,(const ON_3dex*)b);
}
//static
//int ON_MeshGetVertexEdges_CompareInt( const int* a, const int* b )
//{
// return (*a-*b);
//}
int ON_Mesh::GetVertexEdges(
int vertex_index_count,
const int* vertex_index,
bool bNoDuplicates,
ON_SimpleArray<ON_2dex>& edges
) const
{
// Get edges connected to vertices in vertex_index[] array.
const int edges_count0 = edges.Count();
const int mesh_vcount = m_V.Count();
//03/12/2007 TimH. The line below appears to be a typo. Using the following line works better.
//const int mesh_fcount = m_V.Count();
const int mesh_fcount = m_F.Count();
if ( vertex_index_count <= 0 || 0 == vertex_index
|| mesh_fcount <= 0 || mesh_vcount < 3 )
{
return 0;
}
int vei, efi, fvi, ei, fi, j, n, vi;
const int* f_vi;
ON_2dex edge_ends;
const ON_MeshFace* f = m_F.Array();
if ( TopologyExists()
&& mesh_vcount == m_top.m_topv_map.Count()
&& m_top.m_tope.Count() > 0 )
{
// Topology looks good; use it to speed up the search.
const int* topv_map = m_top.m_topv_map;
const int top_vcount = m_top.m_topv.Count();
const int top_ecount = m_top.m_tope.Count();
int top_vi;
for ( n = 0; n < vertex_index_count; n++ )
{
vi = vertex_index[n];
if ( vi < 0 || vi >= mesh_vcount )
continue;
top_vi = topv_map[vi];
if ( top_vi < 0 || top_vi > top_vcount )
continue;
edge_ends.i = vi;
const ON_MeshTopologyVertex& v = m_top.m_topv[top_vi];
for ( vei = 0; vei < v.m_tope_count; vei++ )
{
ei = v.m_topei[vei];
if ( ei < 0 || ei >= top_ecount )
continue;
const ON_MeshTopologyEdge& e = m_top.m_tope[ei];
for ( efi = 0; efi < e.m_topf_count; efi++ )
{
fi = e.m_topfi[efi];
if ( fi < 0 || fi >= mesh_fcount )
continue;
f_vi = f[fi].vi;
for ( fvi = 0; fvi < 4; fvi++ )
{
if ( f_vi[fvi] == vi )
{
j = f_vi[(fvi+3)%4];
if ( j >= 0 && j < mesh_vcount && vi != j )
{
edge_ends.i = j;
edge_ends.j = vi;
edges.Append(edge_ends);
}
j = f_vi[ (2==fvi && f_vi[2]==f_vi[3]) ? 0 : ((fvi+1)%4) ];
if ( j >= 0 && j < mesh_vcount && vi != j )
{
edge_ends.i = vi;
edge_ends.j = j;
edges.Append(edge_ends);
}
break; // done with this face
}
}
}
}
}
}
else
{
// slow-n-stupid search through all the faces
// Sort vertex_index[] array so we can use a quick
// binary search to see if a face is using one of
// the vertices in the list.
ON_Workspace ws;
for ( vi = 1; vi < vertex_index_count; vi++ )
{
if ( vertex_index[vi] < vertex_index[vi-1] )
{
// need to sort vertex_index[] array
int* tmp = ws.GetIntMemory(vertex_index_count);
memcpy(tmp,vertex_index,vertex_index_count*sizeof(tmp[0]));
ON_SortIntArray(ON::sort_algorithm::quick_sort,tmp,vertex_index_count);
vertex_index = tmp;
break;
}
}
// Find all the faces that use a vertex in the vertex_index[] array.
for ( fi = 0; fi < mesh_fcount; fi++ )
{
f_vi = f[fi].vi;
for ( fvi = 0; fvi < 4; fvi++ )
{
vi = f_vi[fvi];
if ( ON_BinarySearchIntArray(vi,vertex_index,vertex_index_count) )
{
// vi is in the vertex_index[] array. Add the edges
// of this face that begin and end at this vertex.
j = f_vi[(fvi+3)%4];
if ( j >= 0 && j < mesh_vcount && vi != j )
{
edge_ends.i = j;
edge_ends.j = vi;
edges.Append(edge_ends);
}
j = f_vi[ (2==fvi && f_vi[2]==f_vi[3]) ? 0 : ((fvi+1)%4) ];
if ( j >= 0 && j < mesh_vcount && vi != j )
{
edge_ends.i = vi;
edge_ends.j = j;
edges.Append(edge_ends);
}
}
}
}
}
if ( bNoDuplicates && edges.Count() > edges_count0 )
{
for ( ei = edges_count0; ei < edges.Count(); ei++ )
{
edge_ends = edges[ei];
if ( edge_ends.i > edge_ends.j )
{
j = edge_ends.i; edge_ends.i = edge_ends.j; edge_ends.j = j;
edges[ei] = edge_ends;
}
}
ON_qsort( edges.Array() + edges_count0,
edges.Count() - edges_count0,
sizeof(edge_ends),
ON_MeshGetVertexEdges_Compare2dex);
edge_ends = edges[edges_count0];
for ( ei = j = edges_count0+1; ei < edges.Count(); ei++ )
{
if ( ON_Compare2dex(&edge_ends,&edges[ei]) )
{
edge_ends = edges[ei];
if ( j != ei )
edges[j] = edge_ends;
j++;
}
}
edges.SetCount(j);
}
return (edges.Count() - edges_count0);
}
int ON_Mesh::GetMeshEdges(
ON_SimpleArray<ON_2dex>& edges
) const
{
const int edges_count0 = edges.Count();
int fi, ei, j, fvi;
const int* f_vi;
const ON_MeshFace* f = m_F.Array();
const int mesh_vcount = m_V.Count();
const int mesh_fcount = m_F.Count();
edges.Reserve( edges_count0 + 4*mesh_fcount );
ON_2dex e;
// Find all the faces that use a vertex in the vertex_index[] array.
for ( fi = 0; fi < mesh_fcount; fi++ )
{
f_vi = f[fi].vi;
ei = f_vi[3];
for ( fvi = 0; fvi < 4; fvi++ )
{
e.i = ei;
ei = *f_vi++;
e.j = ei;
if ( e.i > e.j )
{
j = e.i; e.i = e.j; e.j = j;
}
if ( e.i != e.j && e.i >= 0 && e.j < mesh_vcount )
{
edges.Append(e);
}
}
}
if ( edges.Count() > edges_count0 )
{
ON_qsort( edges.Array() + edges_count0,
edges.Count() - edges_count0,
sizeof(e),
ON_MeshGetVertexEdges_Compare2dex);
e = edges[edges_count0];
for ( ei = j = edges_count0+1; ei < edges.Count(); ei++ )
{
if ( ON_Compare2dex(&e,&edges[ei]) )
{
e = edges[ei];
if ( j != ei )
edges[j] = e;
j++;
}
}
edges.SetCount(j);
}
return edges.Count() - edges_count0;
}
int ON_Mesh::SolidOrientation() const
{
if ( m_mesh_is_solid <= 0 || m_mesh_is_solid > 3 )
{
// NOTE: calling IsClosed() will set m_mesh_is_solid
// to 3 if mes is non-manifold
if ( IsSolid() )
{
}
}
switch(m_mesh_is_solid)
{
case 1:
return 1;
break;
case 2:
return -1;
break;
case 3:
return 0;
break;
}
return 0; // answer "no" if we don't know.
}
bool ON_Mesh::IsPointInside(
ON_3dPoint test_point,
double tolerance,
bool bStrictlyInside
) const
{
if ( IsSolid() )
{
}
return false;
}
bool ON_Mesh::IsSolid() const
{
return ( IsClosed() && IsManifold() && IsOriented() );
}
bool ON_Mesh::IsManifold(
bool bTopologicalTest,
bool* pbIsOriented,
bool* pbHasBoundary
) const
{
bool bIsManifold = false;
if ( pbIsOriented )
*pbIsOriented = false;
if ( pbHasBoundary )
*pbHasBoundary = false;
const int vcount = m_V.Count();
const int fcount = m_F.Count();
if ( vcount > 0 && fcount > 0 )
{
ON_Workspace ws;
ON_3dex e;
int i, j, ecount;
const int* fvi;
ON_3fPoint v0;
const ON_3fPoint* v;
const ON_MeshFace* f;
int* vid = ws.GetIntMemory(vcount);
ON_3dex* edge = (ON_3dex*)ws.GetMemory(4*fcount*sizeof(*edge));
if ( bTopologicalTest )
{
// coincident vertices are assigned the same vertex id
ON_Sort(ON::sort_algorithm::quick_sort,vid,m_V.Array(),vcount,sizeof(m_V[0]),ON_MeshIsManifold_CompareV);
ecount = 0;
v = m_V.Array();
ecount = 0;
j = vcount;
for ( i = 0; i < vcount; i = j)
{
v0 = v[vid[i]];
vid[i] = ecount;
for ( j = i+1; j < vcount; j++ )
{
if ( ON_MeshIsManifold_CompareV(&v,v+vid[j]) )
{
ecount++;
break;
}
vid[j] = ecount;
}
}
}
else
{
// each vertex gets a unique id.
for ( i = 0; i < vcount; i++ )
vid[i] = i;
}
// build a list of edges
f = m_F.Array();
ecount = 0;
for ( i = 0; i < fcount; i++ )
{
fvi = (f++)->vi;
if ( fvi[0] >= 0 && fvi[0] < vcount
&& fvi[1] >= 0 && fvi[1] < vcount
&& fvi[2] >= 0 && fvi[2] < vcount
&& fvi[3] >= 0 && fvi[3] < vcount )
{
// loop unrolled for speed
j = ecount;
e.i = vid[fvi[0]]; e.j = vid[fvi[1]];
if ( 0 != (e.k = e.j - e.i) )
{
if ( e.k < 0 ) {e.k = e.i; e.i = e.j; e.j = e.k; e.k = 1;} else e.k = 0;
edge[ecount++] = e;
}
e.i = vid[fvi[1]]; e.j = vid[fvi[2]];
if ( 0 != (e.k = e.j - e.i) )
{
if ( e.k < 0 ) {e.k = e.i; e.i = e.j; e.j = e.k; e.k = 1;} else e.k = 0;
edge[ecount++] = e;
}
e.i = vid[fvi[2]]; e.j = vid[fvi[3]];
if ( 0 != (e.k = e.j - e.i) )
{
if ( e.k < 0 ) {e.k = e.i; e.i = e.j; e.j = e.k; e.k = 1;} else e.k = 0;
edge[ecount++] = e;
}
e.i = vid[fvi[3]]; e.j = vid[fvi[0]];
if ( 0 != (e.k = e.j - e.i) )
{
if ( e.k < 0 ) {e.k = e.i; e.i = e.j; e.j = e.k; e.k = 1;} else e.k = 0;
edge[ecount++] = e;
}
if ( ecount < j+3 )
ecount = j;
}
}
if ( ecount >= 4 )
{
bIsManifold = true;
bool bIsOriented = (pbIsOriented) ? true : false;
bool bHasBoundary = (pbHasBoundary) ? false : true;
ON_qsort(edge,ecount,sizeof(edge[0]),ON_MeshIsManifold_Compare3dex);
i = 0;
e = *edge;
while ( --ecount )
{
edge++;
if ( memcmp(&e,edge,2*sizeof(int)) )
{
if (!i)
bHasBoundary = true;
e = *edge;
i = 0;
}
else
{
if ( i++ )
{
bIsManifold = false;
break;
}
if ( e.k == edge->k )
bIsOriented = false;
}
}
if ( bIsManifold )
{
if ( pbIsOriented )
*pbIsOriented = bIsOriented;
if ( pbHasBoundary )
*pbHasBoundary = bHasBoundary;
}
}
}
return bIsManifold;
}
static void ON_hsort_3udex(ON_3udex *e, size_t nel)
{
// dictionary sort e[]
size_t i_end, k, i, j;
ON_3udex e_tmp;
if (nel < 2) return;
k = nel >> 1;
i_end = nel - 1;
for (;;)
{
if (k)
{
--k;
e_tmp = e[k];
}
else
{
e_tmp = e[i_end];
e[i_end] = e[0];
if (!(--i_end))
{
e[0] = e_tmp;
break;
}
}
i = k;
j = (k << 1) + 1;
while (j <= i_end)
{
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)))))
j++;
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))))
{
e[i] = e[j];
i = j;
j = (j << 1) + 1;
}
else
j = i_end + 1;
}
e[i] = e_tmp;
}
}
static void ON_Mesh_SetClosedHelper(
bool bClosedOnly,
const ON_Mesh& mesh,
const char& m_mesh_is_manifold,
const char& m_mesh_is_oriented
)
{
// thread safe lazy evaluation for mesh's m_mesh_is_... flags
// Sets: m_mesh_is_closed.
// If bClosedOnly is false, also sets m_mesh_is_manifold and m_mesh_is_oriented
int is_closed = 0;
char is_manifold = 2;
char is_oriented = 2;
for (;;)
{
const unsigned int Vcount = mesh.m_V.UnsignedCount();
const unsigned int Fcount = mesh.m_F.UnsignedCount();
if ( Vcount < 3 || Fcount < 1 )
{
ON_ERROR("Mesh is not valid.");
break;
}
if ( bClosedOnly && (Vcount < 4 || Fcount < 4) )
{
// not closed - don't waste any more time.
break;
}
unsigned int i, j;
unsigned int Vidbuffer[256];
unsigned int* Vid = mesh.GetVertexLocationIds(
1,
(Vcount*sizeof(*Vid) <= sizeof(Vidbuffer) ? &Vidbuffer[0] : nullptr),
nullptr // unwanted Vindex[] values
);
if ( 0 == Vid )
{
ON_ERROR("Mesh has corrupt vertex information.");
bClosedOnly = false;
break;
}
// build an edge list where the "vertex" indices identify unique 3d locations
ON_3udex* E_list = (ON_3udex*)onmalloc(4 * Fcount*sizeof(E_list[0]));
ON_3udex E;
unsigned int Vid0;
const int* fvi;
unsigned int E_count = 0;
const ON_MeshFace* F = mesh.m_F.Array();
for ( j = 0; j < Fcount; j++ )
{
fvi = F[j].vi;
E.i = Vid[fvi[0]];
Vid0 = E.j = Vid[fvi[1]];
if ( E.i == E.j )
break;
if ( E.i > E.j )
{
i = E.i; E.i = E.j; E.j = i;
E.k = 1;
}
else
{
E.k = 0;
}
E_list[E_count++] = E;
E.i = Vid0;
Vid0 = E.j = Vid[fvi[2]];
if ( E.i == E.j )
break;
if ( E.i > E.j )
{
i = E.i; E.i = E.j; E.j = i;
E.k = 1;
}
else
{
E.k = 0;
}
E_list[E_count++] = E;
if ( fvi[2] != fvi[3] )
{
// quad
E.i = Vid0;
Vid0 = E.j = Vid[fvi[3]];
if ( E.i == E.j )
break;
if ( E.i > E.j )
{
i = E.i; E.i = E.j; E.j = i;
E.k = 1;
}
else
{
E.k = 0;
}
E_list[E_count++] = E;
}
E.i = Vid0;
E.j = Vid[fvi[0]];
if ( E.i == E.j )
break;
if ( E.i > E.j )
{
i = E.i; E.i = E.j; E.j = i;
E.k = 1;
}
else
{
E.k = 0;
}
E_list[E_count++] = E;
}
if ( Vid != &Vidbuffer[0] )
onfree(Vid);
if ( E_count < 3 || j != Fcount )
{
ON_ERROR("Mesh is corrupt or collapsed");
bClosedOnly = false;
break;
}
// sort the the edges
ON_hsort_3udex(E_list,E_count);
// Look for duplicate edges. If we find an edge with no duplicate,
// then the mesh is open. It is possible that degenerate meshes,
// like a flattened box, will be flagged as closed.
is_closed = (Fcount >= 4 && E_count >= 6) ? 1 : 0;
is_oriented = 1;
is_manifold = 1;
i = 0;
if ( !bClosedOnly || 1 == is_closed ) for ( i = 0; i < E_count; /*empty iterator*/ )
{
E = E_list[i];
if ( ++i >= E_count )
{
// boundary edge (and the last edge in our list)
is_closed = 0;
break;
}
if ( E.i != E_list[i].i || E.j != E_list[i].j )
{
// boundary edge
is_closed = 0;
if ( 2 == is_oriented && 2 == is_manifold )
{
bClosedOnly = false;
break;
}
if ( bClosedOnly )
break; // don't spend time with further testing
continue;
}
if ( E.k == E_list[i].k )
{
// opposite face normals along this edge - mesh is not oriented
is_oriented = 2;
}
if ( ++i >= E_count || E.i != E_list[i].i || E.j != E_list[i].j )
{
// two faces share this edge
continue;
}
// three or more faces share this edge - mesh is not oriented manifold
is_oriented = 2;
is_manifold = 2;
if ( 0 == is_closed )
{
bClosedOnly = false;
break;
}
// Check for more faces sharing this edge.
for ( i++; i < E_count; i++ )
{
if ( E.i != E_list[i].i || E.j != E_list[i].j )
{
// the edges E and Eid_list[i] are in different locations
break;
}
}
}
if (E_count > 0 && i >= E_count)
{
// is_manifold and is_oriented are set correctly
bClosedOnly = false;
}
onfree(E_list);
break;
}
const_cast<ON_Mesh&>(mesh).SetClosed(is_closed);
if ( !bClosedOnly )
{
// is_manifold and is_oriented are set correctly
if ( 2 == is_manifold )
is_oriented = 2;
const_cast<char&>(m_mesh_is_manifold) = is_manifold;
const_cast<char&>(m_mesh_is_oriented) = is_oriented;
}
}
bool ON_Mesh::IsClosed() const
{
if ( m_mesh_is_closed <= 0 || m_mesh_is_closed > 2)
{
// thread safe lazy evaluation
ON_Mesh_SetClosedHelper( true, *this, m_mesh_is_manifold, m_mesh_is_oriented );
}
return (1 == m_mesh_is_closed);
}
bool ON_Mesh::IsManifold() const
{
if ( m_mesh_is_manifold <= 0 || m_mesh_is_manifold > 2 )
{
// thread safe lazy evaluation
ON_Mesh_SetClosedHelper( false, *this, m_mesh_is_manifold, m_mesh_is_oriented );
}
return (1 == m_mesh_is_manifold);
}
bool ON_Mesh::IsOriented() const
{
if ( m_mesh_is_oriented <= 0 || m_mesh_is_oriented > 2 )
{
// thread safe lazy evaluation
ON_Mesh_SetClosedHelper( false, *this, m_mesh_is_manifold, m_mesh_is_oriented );
}
return (1 == m_mesh_is_oriented);
}
/*
Returns
true if there are zero vertices and zero faces.
*/
bool ON_Mesh::IsEmpty() const
{
// Deep sigh.
// The "this == &ON_Mesh::Empty" check is to handle the case when
// confused developers const cast ON_Mesh::Empty and then modify it.
return (0 == VertexUnsignedCount() && 0 == FaceUnsignedCount()) || (this == &ON_Mesh::Empty);
}
bool ON_Mesh::IsNotEmpty() const
{
// Deep sigh.
// The "this != &ON_Mesh::Empty" check is to handle the case when
// confused developers const cast ON_Mesh::Empty and then modify it.
return ( VertexUnsignedCount() > 0 && FaceUnsignedCount() > 0 && this != &ON_Mesh::Empty);
}
bool ON_Mesh::SetVertex(
int vertex_index,
const ON_3dPoint& vertex_location
)
{
const unsigned int vertex_count = VertexUnsignedCount();
const bool rc = vertex_index >= 0 && ((unsigned int)vertex_index) <= vertex_count;
if ( rc )
{
const unsigned int i = (unsigned int)vertex_index;
if ( vertex_count == m_dV.UnsignedCount() )
{
if ( i < vertex_count )
m_dV[i] = vertex_location;
else
m_dV.Append(vertex_location);
}
if ( vertex_count == m_V.UnsignedCount() )
{
if ( i < vertex_count )
m_V[i] = vertex_location;
else
m_V.AppendNew() = ON_3fPoint(vertex_location);
}
}
return rc;
}
bool ON_Mesh::SetVertex(
int vertex_index,
const ON_3fPoint& vertex_location
)
{
const unsigned int vertex_count = VertexUnsignedCount();
const bool rc = vertex_index >= 0 && ((unsigned int)vertex_index) <= vertex_count;
if ( rc )
{
const unsigned int i = (unsigned int)vertex_index;
if ( vertex_count == m_dV.UnsignedCount() )
{
if ( i < vertex_count )
m_dV[i] = vertex_location;
else
m_dV.Append(vertex_location);
}
if ( vertex_count == m_V.UnsignedCount() )
{
if ( i < vertex_count )
m_V[i] = vertex_location;
else
m_V.Append(vertex_location);
}
}
return rc;
}
bool ON_Mesh::SetVertexNormal(
int vertex_index,
const ON_3dVector& normal
)
{
bool rc = false;
// use double precision for unitizing normal
ON_3dVector unit_vector = normal;
const bool bUnitVector = unit_vector.Unitize();
ON_3fVector v((float)unit_vector.x, (float)unit_vector.y, (float)unit_vector.z);
int normal_count = m_N.Count();
if ( vertex_index >= 0 ) {
if ( vertex_index < normal_count ) {
m_N[vertex_index] = v;
rc = bUnitVector;
}
else if ( vertex_index == normal_count ) {
m_N.Append(v);
rc = bUnitVector;
}
}
return rc;
}
bool ON_Mesh::SetVertexNormal(
int vertex_index,
const ON_3fVector& normal
)
{
ON_3dVector v(normal.x,normal.y,normal.z);
return SetVertexNormal(vertex_index,v);
}
bool ON_Mesh::SetTextureCoord(
int vertex_index,
double s, double t // texture coordinates
)
{
ON_2fPoint tc((float)s,(float)t);
bool rc = false;
int vertex_count = m_T.Count();
if ( vertex_index >= 0 ) {
if ( vertex_index < vertex_count ) {
m_T[vertex_index] = tc;
rc = true;
}
else if ( vertex_index == vertex_count ) {
m_T.Append(tc);
rc = true;
}
}
return rc;
}
unsigned int ON_Mesh::AppendDuplicateVertex(
unsigned int vertex_index
)
{
const unsigned int vertex_count = VertexUnsignedCount();
if ( vertex_index >= vertex_count )
return ON_UNSET_UINT_INDEX;
if (vertex_count == m_V.UnsignedCount())
{
ON_3fPoint V = m_V[vertex_index];
m_V.Append(V);
}
if ( vertex_count == m_dV.UnsignedCount())
{
ON_3dPoint D = m_dV[vertex_index];
m_dV.Append(D);
}
if ( vertex_count == m_N.UnsignedCount() )
{
ON_3fVector N = m_N[vertex_index];
m_N.Append(N);
}
if ( vertex_count == m_T.UnsignedCount() )
{
ON_2fPoint T = m_T[vertex_index];
m_T.Append(T);
}
if ( vertex_count == m_S.UnsignedCount() )
{
ON_2dPoint S = m_S[vertex_index];
m_S.Append(S);
}
if ( vertex_count == m_K.UnsignedCount() )
{
ON_SurfaceCurvature K = m_K[vertex_index];
m_K.Append(K);
}
if ( vertex_count == m_C.UnsignedCount() )
{
ON_Color C = m_C[vertex_index];
m_C.Append(C);
}
if ( vertex_count == m_H.UnsignedCount() )
{
bool H = m_H[vertex_index];
m_H.Append(H);
}
return vertex_count;
}
/*
Description:
Increases the capactiy of arrays to be at least the requested capacity.
Parameters:
new_vertex_capacity - [in]
desired capacity
Returns:
true if successful.
Remarks:
This function is useful if you are getting ready to add a known number
of vertices and want to increase the dynamic array capacities before
you begin adding vertices.
*/
bool ON_Mesh::ReserveVertexCapacity(
size_t new_vertex_capacity
)
{
const unsigned int vertex_count = VertexUnsignedCount();
if ( new_vertex_capacity <= (size_t)vertex_count )
return true;
if ( vertex_count == m_V.UnsignedCount() )
m_V.Reserve(new_vertex_capacity);
if ( vertex_count == m_dV.UnsignedCount() )
m_dV.Reserve(new_vertex_capacity);
if ( vertex_count == m_N.UnsignedCount() )
m_N.Reserve(new_vertex_capacity);
if ( vertex_count == m_T.UnsignedCount() )
m_T.Reserve(new_vertex_capacity);
if ( vertex_count == m_S.UnsignedCount() )
m_S.Reserve(new_vertex_capacity);
if ( vertex_count == m_K.UnsignedCount() )
m_K.Reserve(new_vertex_capacity);
if ( vertex_count == m_C.UnsignedCount() )
m_C.Reserve(new_vertex_capacity);
if ( vertex_count == m_H.UnsignedCount() )
m_H.Reserve(new_vertex_capacity);
return true;
}
bool ON_Mesh::SetTriangle(
int face_index,
int a,int b ,int c // vertex indices
)
{
return SetQuad( face_index, a,b,c,c );
}
bool ON_Mesh::SetQuad(
int face_index,
int a, int b, int c, int d // vertex indices
)
{
bool rc = false;
int face_count = m_F.Count();
if ( face_index >= 0 ) {
ON_MeshFace f;
f.vi[0] = a;
f.vi[1] = b;
f.vi[2] = c;
f.vi[3] = d;
if ( face_index < face_count ) {
m_F[face_index] = f;
rc = true;
}
else if ( face_index == face_count ) {
m_F.Append(f);
rc = true;
}
if ( rc )
rc = f.IsValid(m_V.Count());
}
return rc;
}
int ON_Mesh::FaceCount() const
{
return m_F.Count();
}
unsigned int ON_Mesh::FaceUnsignedCount() const
{
return m_F.UnsignedCount();
}
int ON_Mesh::QuadCount() const
{
// number of faces that are quads
if ( m_quad_count < 0
|| m_triangle_count < 0
|| m_invalid_count < 0
|| m_quad_count + m_triangle_count + m_invalid_count != FaceCount() )
{
const_cast<ON_Mesh*>(this)->CountQuads();
}
return m_quad_count;
}
int ON_Mesh::TriangleCount() const
{
// number of faces that are triangles
QuadCount(); // makes sure counts are valid
return m_triangle_count;
}
int ON_Mesh::InvalidFaceCount() const
{
// number of faces that are invalid
QuadCount(); // makes sure counts are valid
return m_invalid_count;
}
int ON_Mesh::VertexCount() const
{
// When m_V becomes obsolete, this will return m_dV.Count().
return m_V.Count();
}
unsigned int ON_Mesh::VertexUnsignedCount() const
{
// When m_V becomes obsolete, this will return m_dV.UnsignedCount().
return m_V.UnsignedCount();
}
bool ON_Mesh::HasNgons() const
{
return ( m_F.Count() > 0 && m_Ngon.Count() );
}
bool ON_Mesh::HasVertexNormals() const
{
const int vertex_count = VertexCount();
return ( vertex_count > 0 && m_N.Count() == vertex_count ) ? true : false;
}
bool ON_Mesh::HasFaceNormals() const
{
const int face_count = FaceCount();
return ( face_count > 0 && m_FN.Count() == face_count ) ? true : false;
}
bool ON_Mesh::HasTextureCoordinates() const
{
const int vertex_count = VertexCount();
return ( vertex_count > 0 && m_T.Count() == vertex_count ) ? true : false;
}
bool ON_Mesh::HasCachedTextureCoordinates() const
{
const int vertex_count = VertexCount();
if (vertex_count > 0 )
{
int tci, tccount = m_TC.Count();
for ( tci = 0; tci < tccount; tci++ )
{
if ( vertex_count == m_TC[tci].m_T.Count() )
return true;
}
}
return false;
}
const ON_TextureCoordinates*
ON_Mesh::CachedTextureCoordinates( const ON_UUID& mapping_id ) const
{
const int vertex_count = VertexCount();
if (vertex_count > 0 )
{
const ON_TextureCoordinates* TC = m_TC.Array();
int tci, tccount = m_TC.Count();
for ( tci = 0; tci < tccount; tci++ )
{
if ( vertex_count == TC->m_T.Count()
&& mapping_id == TC->m_tag.m_mapping_id )
{
return TC;
}
TC++;
}
}
return 0;
}
bool ON_Mesh::HasSurfaceParameters() const
{
const int vertex_count = VertexCount();
return ( vertex_count > 0 && m_S.Count() == vertex_count ) ? true : false;
}
bool ON_Mesh::HasPrincipalCurvatures() const
{
const int vertex_count = VertexCount();
return ( vertex_count > 0 && m_K.Count() == vertex_count ) ? true : false;
}
bool ON_Mesh::HasVertexColors() const
{
const int vertex_count = VertexCount();
return ( vertex_count > 0 && m_C.Count() == vertex_count ) ? true : false;
}
void ON_Mesh::InvalidateBoundingBoxes()
{
InvalidateVertexBoundingBox();
InvalidateVertexNormalBoundingBox();
InvalidateTextureCoordinateBoundingBox();
InvalidateCurvatureStats();
}
void ON_Mesh::InvalidateVertexBoundingBox()
{
m_vertex_bbox = ON_BoundingBox::UnsetBoundingBox;
m_tight_bbox_cache.RemoveAllBoundingBoxes();
}
void ON_Mesh::InvalidateVertexNormalBoundingBox()
{
m_nbox[0][0] = m_nbox[0][1] = m_nbox[0][2] = 1.0;
m_nbox[1][0] = m_nbox[1][1] = m_nbox[1][2] = -1.0;
}
void ON_Mesh::InvalidateTextureCoordinateBoundingBox()
{
m_tbox[0][0] = m_tbox[0][1] = 1.0;
m_tbox[1][0] = m_tbox[1][1] = -1.0;
}
void ON_Mesh::InvalidateCurvatureStats()
{
int i;
for ( i = 0; i < 4; i++ ) {
if ( m_kstat[i] ) {
delete m_kstat[i];
m_kstat[i] = 0;
}
}
}
bool ON_Mesh::UnitizeVertexNormals()
{
bool rc = HasVertexNormals();
if ( rc ) {
const int vertex_count = VertexCount();
float* n = &m_N[0][0];
int i;
ON_3dVector N;
for ( i = 0; i < vertex_count; i++ ) {
N.x = n[0];
N.y = n[1];
N.z = n[2];
if ( !N.Unitize() )
rc = false;
*n++ = (float)N.x;
*n++ = (float)N.y;
*n++ = (float)N.z;
}
}
return rc;
}
bool ON_Mesh::UnitizeFaceNormals()
{
bool rc = HasFaceNormals();
if ( rc ) {
const int face_count = FaceCount();
float* n = &m_FN[0][0];
int i;
ON_3dVector N;
for ( i = 0; i < face_count; i++ ) {
N.x = n[0];
N.y = n[1];
N.z = n[2];
if ( !N.Unitize() )
rc = false;
*n++ = (float)N.x;
*n++ = (float)N.y;
*n++ = (float)N.z;
}
}
return rc;
}
bool ON_Mesh::GetCurvatureStats( // returns true if successful
ON::curvature_style kappa_style,
ON_MeshCurvatureStats& stats
) const
{
bool rc = false;
stats.Destroy();
int ksi;
switch ( kappa_style ) {
case ON::gaussian_curvature:
ksi = 0;
break;
case ON::mean_curvature:
ksi = 1;
break;
case ON::min_curvature: // minimum unsigned radius of curvature
ksi = 2;
break;
case ON::max_curvature: // maximum unsigned radius of curvature
ksi = 3;
break;
//case ON::section_curvature_x:
// ksi = 4;
// break;
//case ON::section_curvature_y:
// ksi = 5;
// break;
//case ON::section_curvature_z:
// ksi = 6;
// break;
default:
ksi = -1;
break;
}
if ( ksi >= 0 && ksi <= 3 && HasPrincipalCurvatures() ) {
ON_Mesh* p = (ON_Mesh*)this; // const lie
if ( !m_kstat[ksi] ) {
p->m_kstat[ksi] = new ON_MeshCurvatureStats();
p->m_kstat[ksi]->Set( kappa_style, m_K.Count(), m_K.Array(), m_N.Array() );
}
if ( p->m_kstat[ksi] ) {
stats = *p->m_kstat[ksi];
rc = true;
}
}
return rc;
}
int ON_MeshTopology::WaitUntilReady(int sleep_value) const
{
return m_b32IsValid;
}
bool ON_Mesh::TopologyExists() const
{
return HasMeshTopology();
}
bool ON_Mesh::HasMeshTopology() const
{
return (1 == m_top.WaitUntilReady(0));
}
const ON_MeshTopology& ON_Mesh::Topology() const
{
int top_b32IsValid = m_top.WaitUntilReady(-1);
if ( 0 == top_b32IsValid )
{
ON_MeshTopology& top = const_cast<ON_MeshTopology&>(m_top);
top.m_mesh = this;
top_b32IsValid = top.Create() ? 1 : 0;
top.m_b32IsValid = top_b32IsValid;
}
return m_top;
}
static ON_MeshTriangle ON_UnsetMeshTriangleInitalizer()
{
ON_MeshTriangle unset_mesh_triangle;
unset_mesh_triangle.m_vi[0] = ON_UNSET_UINT_INDEX;
unset_mesh_triangle.m_vi[1] = ON_UNSET_UINT_INDEX;
unset_mesh_triangle.m_vi[2] = ON_UNSET_UINT_INDEX;
return unset_mesh_triangle;
}
const ON_MeshTriangle ON_MeshTriangle::UnsetMeshTriangle(ON_UnsetMeshTriangleInitalizer());
bool ON_MeshTriangle::IsValid(
size_t mesh_vertex_count
) const
{
if ( mesh_vertex_count < 2 || mesh_vertex_count >= (size_t)ON_UNSET_UINT_INDEX )
return false;
if ( m_vi[0] == m_vi[1] || m_vi[1] == m_vi[2] || m_vi[2] == m_vi[0] )
return false;
const unsigned int c = (unsigned int)mesh_vertex_count;
if ( m_vi[0] >= c || m_vi[1] >= c || m_vi[2] >= c )
return false;
return true;
}
bool ON_MeshTriangle::IsValid(
size_t mesh_vertex_count,
const class ON_3fPoint* V
) const
{
if ( false == IsValid(mesh_vertex_count) )
return false;
if ( 0 == V )
return false;
return ( V[m_vi[0]] != V[m_vi[1]] && V[m_vi[1]] != V[m_vi[2]] && V[m_vi[2]] != V[m_vi[0]] );
}
bool ON_MeshTriangle::IsValid(
size_t mesh_vertex_count,
const class ON_3dPoint* V
) const
{
if ( false == IsValid(mesh_vertex_count) )
return false;
if ( 0 == V )
return false;
return ( V[m_vi[0]] != V[m_vi[1]] && V[m_vi[1]] != V[m_vi[2]] && V[m_vi[2]] != V[m_vi[0]] );
}
bool ON_MeshTriangle::IsValid(
const class ON_3dPointListRef& vertex_list
) const
{
if ( false == IsValid(vertex_list.PointCount()) )
return false;
ON_3dPoint V[3] = {vertex_list[m_vi[0]],vertex_list[m_vi[1]],vertex_list[m_vi[2]]};
return ( V[0] != V[1] && V[1] != V[2] && V[2] != V[0] );
}
void ON_MeshTriangle::Flip()
{
unsigned int i = m_vi[1];
m_vi[1] = m_vi[2];
m_vi[2] = i;
}
static bool ON_MeshTriangle_GetTriangleNormal(
const double* p,
const double* q,
const double* r,
class ON_3dVector& triangle_normal
)
{
const double a[3] = {r[0]-p[0],r[1]-p[1],r[2]-p[2]};
const double b[3] = {p[0]-q[0],p[1]-q[1],p[2]-q[2]};
triangle_normal.x = a[1]*b[2] - b[1]*a[2];
triangle_normal.y = a[2]*b[0] - b[2]*a[0];
triangle_normal.z = a[0]*b[1] - b[0]*a[1];
return triangle_normal.Unitize();
}
bool ON_MeshTriangle::GetTriangleNormal(
const class ON_3dPoint* dV,
class ON_3dVector& triangle_normal
) const
{
if ( 0 == dV )
return false;
return ON_MeshTriangle_GetTriangleNormal(&dV[m_vi[0]].x,&dV[m_vi[1]].x,&dV[m_vi[2]].x,triangle_normal);
}
bool ON_MeshTriangle::GetTriangleNormal(
const class ON_3fPoint* fV,
class ON_3dVector& triangle_normal
) const
{
const ON_3dPoint p(fV[m_vi[0]]);
const ON_3dPoint q(fV[m_vi[1]]);
const ON_3dPoint r(fV[m_vi[2]]);
return ON_MeshTriangle_GetTriangleNormal(&p.x,&q.x,&r.x,triangle_normal);
}
bool ON_MeshTriangle::GetTriangleNormal(
const class ON_3dPointListRef& vertex_list,
class ON_3dVector& triangle_normal
) const
{
const ON_3dPoint p(vertex_list[m_vi[0]]);
const ON_3dPoint q(vertex_list[m_vi[1]]);
const ON_3dPoint r(vertex_list[m_vi[2]]);
return ON_MeshTriangle_GetTriangleNormal(&p.x,&q.x,&r.x,triangle_normal);
}
bool ON_MeshTriangle::GetTriangleNormal(
ON_3dPoint point0,
ON_3dPoint point1,
ON_3dPoint point2,
class ON_3dVector& triangle_normal
)
{
return ON_MeshTriangle_GetTriangleNormal(&point0.x,&point1.x,&point2.x,triangle_normal);
}
static ON_MeshFace ON_UnsetMeshFaceInitalizer()
{
ON_MeshFace unset_mesh_face;
unset_mesh_face.vi[0] = -1;
unset_mesh_face.vi[1] = -1;
unset_mesh_face.vi[2] = -1;
unset_mesh_face.vi[3] = -1;
return unset_mesh_face;
}
const ON_MeshFace ON_MeshFace::UnsetMeshFace(ON_UnsetMeshFaceInitalizer());
void ON_Mesh::DestroyTopology()
{
m_top.Destroy();
}
bool
ON_MeshFace::IsTriangle() const
{
return vi[2]==vi[3];
}
bool
ON_MeshFace::IsQuad() const
{
return vi[2]!=vi[3];
}
void
ON_MeshFace::Flip()
{
int i;
if ( vi[2] == vi[3] ) {
i = vi[1];
vi[1] = vi[2];
vi[2] = i;
vi[3] = i;
}
else {
i = vi[1];
vi[1] = vi[3];
vi[3] = i;
}
}
void
ON_Mesh::Flip()
{
FlipFaceOrientation();
FlipFaceNormals();
FlipVertexNormals();
FlipNgonOrientation();
// Do not modify m_S[] or m_T[]
// values here.
}
void
ON_Mesh::FlipVertexNormals()
{
int i;
const int vcount = VertexCount();
if ( HasVertexNormals() ) {
for ( i = 0; i < vcount; i++ ) {
m_N[i] = -m_N[i];
}
}
}
void
ON_Mesh::FlipFaceNormals()
{
int i;
const int fcount = FaceCount();
if ( HasFaceNormals() ) {
for( i = 0; i < fcount; i++ ) {
m_FN[i] = -m_FN[i];
}
}
}
void
ON_Mesh::FlipFaceOrientation()
{
int i;
const int fcount = FaceCount();
for( i = 0; i < fcount; i++ ) {
m_F[i].Flip();
}
if ( fcount > 0 )
DestroyTopology(); // flipping changes order of face corners
}
bool ON_MeshFace::ComputeFaceNormal( const ON_3dPoint* dV, ON_3dVector& FN ) const
{
if ( 0 != dV )
{
ON_3dVector a = dV[vi[2]] - dV[vi[0]];
ON_3dVector b = dV[vi[3]] - dV[vi[1]];
FN = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads
if ( FN.Unitize() )
return true;
}
FN = ON_3dVector::ZeroVector;
return false;
}
bool ON_MeshFace::ComputeFaceNormal( const ON_3fPoint* fV, ON_3dVector& FN ) const
{
if ( 0 != fV )
{
ON_3dVector a = fV[vi[2]] - fV[vi[0]];
ON_3dVector b = fV[vi[3]] - fV[vi[1]];
FN = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads
if ( FN.Unitize() )
return true;
}
FN = ON_3dVector::ZeroVector;
return false;
}
bool ON_MeshFace::ComputeFaceNormal( const class ON_3dPointListRef& vertex_list, ON_3dVector& FN ) const
{
ON_3dVector a = vertex_list[vi[2]] - vertex_list[vi[0]];
ON_3dVector b = vertex_list[vi[3]] - vertex_list[vi[1]];
FN = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads
if ( FN.Unitize() )
return true;
FN = ON_3dVector::ZeroVector;
return false;
}
unsigned int ON_MeshFace::GetCornerNormals(
const class ON_3dPointListRef& vertex_list,
ON_3dVector corner_normals[4]
) const
{
unsigned int rc = 0;
if (vi[2] == vi[3])
{
if (ComputeFaceNormal(vertex_list, corner_normals[0]))
{
rc = 4;
}
else
{
corner_normals[0] = ON_3dVector::UnsetVector;
corner_normals[1] = corner_normals[0];
corner_normals[2] = corner_normals[0];
corner_normals[3] = corner_normals[0];
}
return rc;
}
else
{
const double unset_x = ON_3dVector::UnsetVector.x;
ON_3dVector C = vertex_list[vi[0]] - vertex_list[vi[3]];
if (!C.Unitize())
C = ON_3dVector::UnsetVector;
ON_3dVector B = C;
for (unsigned int i = 0; i < 4; i++)
{
ON_3dVector A = B;
if (3 == i)
B = C;
else
{
B = vertex_list[vi[i + 1]] - vertex_list[vi[i]];
if (!B.Unitize())
B = ON_3dVector::UnsetVector;
}
if (unset_x != A.x && unset_x != B.x)
{
corner_normals[i] = ON_CrossProduct(A, B);
if (corner_normals[i].Unitize())
{
rc++;
continue;
}
}
corner_normals[i] = ON_3dVector::UnsetVector;
}
}
return rc;
}
bool ON_MeshFace::GetPlaneEquation(
const class ON_3dPointListRef& vertex_list,
ON_PlaneEquation& face_plane_equation
) const
{
ON_3dVector N;
if (ComputeFaceNormal(vertex_list, N) && face_plane_equation.Create(vertex_list[vi[0]], N))
return true;
face_plane_equation = ON_PlaneEquation::UnsetPlaneEquation;
return false;
}
bool ON_MeshFace::IsPlanar(
double planar_tolerance,
double angle_tolerance_radians,
const class ON_3dPointListRef& vertex_list,
ON_PlaneEquation* face_plane_equation
) const
{
ON_PlaneEquation e;
for (;;)
{
if (!GetPlaneEquation(vertex_list, e))
break;
if ( face_plane_equation )
*face_plane_equation = e;
if (vi[2] == vi[3])
return true; // triangle
if (planar_tolerance >= 0.0)
{
double h0, h1, h;
h0 = h1 = 0.0;
for (int i = 1; i < 3; i++)
{
if (vi[i - 1] == vi[i])
continue;
h = e.ValueAt(vertex_list[vi[i]]);
if (h < h0)
h0 = h;
else if (h > h1)
h1 = h;
else
continue;
if (h1 - h0 > planar_tolerance)
{
return false;
}
}
}
if (angle_tolerance_radians >= 0.0)
{
ON_3dVector corner_normals[4];
GetCornerNormals(vertex_list, corner_normals);
const double unset_x = ON_3dVector::UnsetVector.x;
const double cos_atol = (angle_tolerance_radians < ON_PI) ? cos(angle_tolerance_radians) : -1.0;
for (unsigned int i = 0; i < 2; i++)
{
if (unset_x != corner_normals[i].x && unset_x != corner_normals[i+2].x && corner_normals[i] * corner_normals[i+2] < cos_atol)
return false;
}
}
return true;
}
if (face_plane_equation)
*face_plane_equation = ON_PlaneEquation::UnsetPlaneEquation;
return false;
}
bool ON_Mesh::ComputeFaceNormal( int fi )
{
if ( fi < 0 )
return false;
if ( fi >= m_F.Count() )
return false;
if ( m_FN.Count() != m_F.Count() )
return false;
ON_3dVector FN;
bool rc = ( HasDoublePrecisionVertices() )
? m_F[fi].ComputeFaceNormal(DoublePrecisionVertices().Array(),FN)
: m_F[fi].ComputeFaceNormal(m_V.Array(),FN);
m_FN[fi] = FN;
return rc;
}
bool ON_Mesh::ComputeFaceNormals()
{
bool rc = false;
//const unsigned int vcount = VertexUnsignedCount();
const int fcount = FaceCount();
if ( fcount > 0 )
{
ON_3dVector a, b, n;
int fi;
const int* vi;
if ( m_FN.Capacity() < fcount )
m_FN.SetCapacity(fcount);
m_FN.SetCount(0);
rc = true;
if ( HasSynchronizedDoubleAndSinglePrecisionVertices() )
{
const ON_3dPointArray& dV = DoublePrecisionVertices();
for ( fi = 0; fi < fcount; fi++ ) {
vi = m_F[fi].vi;
a = dV[vi[2]] - dV[vi[0]];
b = dV[vi[3]] - dV[vi[1]];
n = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads
n.Unitize();
m_FN.AppendNew() = ON_3fVector(n);
}
}
else
{
for ( fi = 0; fi < fcount; fi++ ) {
vi = m_F[fi].vi;
a = m_V[vi[2]] - m_V[vi[0]];
b = m_V[vi[3]] - m_V[vi[1]];
n = ON_CrossProduct( a, b ); // works for triangles, quads, and nonplanar quads
n.Unitize();
m_FN.AppendNew() = ON_3fVector(n);
}
}
}
else
{
m_FN.Destroy();
}
return rc;
}
//static int compareRadial3fPoint( const ON_3fPoint* a, const ON_3fPoint* b )
//{
// double ar = a->x+a->y+a->z;
// double br = b->x+b->y+b->z;
// if ( ar < br )
// return -1;
// if ( ar > br )
// return 1;
// return 0;
//}
bool ON_Mesh::CombineCoincidentVertices(
const ON_3fVector tolerance,
double cos_normal_angle // = -1.0 // cosine(break angle) -1.0 will merge all coincident vertices
)
{
// TODO - If you need this function, please ask Dale Lear to finish it.
//bool rc = false;
//const int vcount = VertexCount();
//if ( vcount > 0 && rc ) {
// ON_Workspace ws;
// int* index = ws.GetIntMemory(vcount);
// rc = m_V.Sort( ON::sort_algorithm::quick_sort, index, compareRadial3fPoint );
// int i, j;
// ON_3fPoint p0, p1, pmin, pmax;
// for ( i = 0; i < vcount; i++ ) {
// p0 = m_V[i];
// pmin = p0 - tolerance;
// pmax = p0 + tolerance;
// for ( j = i+1; j < vcount; j++ ) {
// p1 = m_V[j];
// // TODO
// }
// }
//}
return false;
}
struct tagMESHPOINTS
{
// p0 = bogus pointer - never dereferenced - that is used
// to calculate vertex index in CompareMeshPoint().
const char* p0;
ON_3fPoint* V;
ON_2fPoint* T;
ON_3fVector* N;
ON_SurfaceCurvature* K;
ON_Color* C;
};
static int CompareMeshPoint(const void* a,const void* b,void* ptr)
{
float d;
const struct tagMESHPOINTS * mp = (const struct tagMESHPOINTS *)ptr;
// use bogus pointer to convert a,b into vertex indices
int i = (int)(((const char*)a) - mp->p0); // the (int) is for 64 bit size_t conversion
int j = (int)(((const char*)b) - mp->p0);
d = mp->V[j].x - mp->V[i].x;
if ( d == 0.0f )
{
d = mp->V[j].y - mp->V[i].y;
if ( d == 0.0f )
{
d = mp->V[j].z - mp->V[i].z;
//if ( d == 0.0f )
// return 0;
if ( d == 0.0f && 0 != mp->N)
{
d = mp->N[j].x - mp->N[i].x;
if ( d == 0.0f )
{
d = mp->N[j].y - mp->N[i].y;
if ( d == 0.0f )
{
d = mp->N[j].z - mp->N[i].z;
}
}
}
if ( d == 0.0f && 0 != mp->T)
{
d = mp->T[j].x - mp->T[i].x;
if ( d == 0.0f )
{
d = mp->T[j].y - mp->T[i].y;
}
}
if ( d == 0.0f && 0 != mp->C )
{
int u = ((int)mp->C[j])-((int)mp->C[i]);
if ( u < 0 )
d = -1.0f;
else if ( u > 0 )
d = 1.0f;
}
if ( d == 0.0f && 0 != mp->K )
{
double dk = mp->K[j].k1 - mp->K[i].k1;
if ( dk < 0.0 )
d = -1.0;
else if ( dk > 0.0 )
d = 1.0;
else
{
dk = mp->K[j].k2 - mp->K[i].k2;
if ( dk < 0.0 )
d = -1.0;
else if ( dk > 0.0 )
d = 1.0;
}
}
}
}
if ( d < 0.0f )
return -1;
if ( d > 0.0f )
return 1;
return 0;
}
unsigned int ON_Mesh::RemoveAllCreases()
{
unsigned int vertex_count0 = this->VertexUnsignedCount();
bool bChanged = this->CombineIdenticalVertices(true, true);
const unsigned int vertex_count1 = this->VertexUnsignedCount();
if (vertex_count0 == vertex_count1 && bChanged)
vertex_count0 = vertex_count1 + 1;
return (vertex_count0 - vertex_count1);
}
bool ON_Mesh::CombineIdenticalVertices(
bool bIgnoreVertexNormals,
bool bIgnoreTextureCoordinates
)
{
// 11 June 2003 - added and tested.
bool rc = false;
ON_Mesh& mesh = *this;
int vertex_count = mesh.m_V.Count();
if ( vertex_count > 0 )
{
ON_SimpleArray<int> index_array(vertex_count);
ON_SimpleArray<int> remap_array(vertex_count);
int remap_vertex_count = 0;
int merge_count = 0;
int i0, i1, k;
struct tagMESHPOINTS mp;
memset(&mp,0,sizeof(mp));
mp.p0 = (const char*)&mp; // bogus pointer - never dereferenced
mp.V = mesh.m_V.Array();
mp.N = mesh.HasVertexNormals() ? mesh.m_N.Array() : 0;
mp.T = mesh.HasTextureCoordinates() ? mesh.m_T.Array() : 0;
mp.C = mesh.HasVertexColors() ? mesh.m_C.Array() : 0;
mp.K = mesh.HasPrincipalCurvatures() ? mesh.m_K.Array() : 0;
if ( bIgnoreVertexNormals )
{
mp.N = 0;
}
if ( bIgnoreTextureCoordinates )
{
mp.T = 0;
mp.C = 0;
mp.K = 0;
}
index_array.SetCount(vertex_count);
index_array.Zero();
int* index = index_array.Array();
remap_array.SetCount(vertex_count);
int* remap = remap_array.Array();
for ( k = 0; k < vertex_count; k++ )
remap[k] = -1;
ON_Sort(
ON::sort_algorithm::quick_sort,
index,
mp.p0, // data buffer
vertex_count,
sizeof(*mp.p0),
CompareMeshPoint,
&mp
);
for ( i0 = 0; i0 < vertex_count; i0 = i1 )
{
for ( i1 = i0+1; i1 < vertex_count; i1++ )
{
if ( CompareMeshPoint( mp.p0+index[i0], mp.p0+index[i1], &mp ) )
break;
else
merge_count++;
}
for ( /*empty*/; i0 < i1; i0++ )
{
remap[index[i0]] = remap_vertex_count;
}
remap_vertex_count++;
}
if ( bIgnoreVertexNormals )
{
mp.N = mesh.HasVertexNormals() ? mesh.m_N.Array() : 0;
}
if ( bIgnoreTextureCoordinates )
{
mp.T = mesh.HasTextureCoordinates() ? mesh.m_T.Array() : 0;
mp.C = mesh.HasVertexColors() ? mesh.m_C.Array() : 0;
mp.K = mesh.HasPrincipalCurvatures() ? mesh.m_K.Array() : 0;
}
if ( remap_vertex_count > 0 && remap_vertex_count < vertex_count )
{
ON_SimpleArray<ON_3fPoint> p_array(remap_vertex_count);
p_array.SetCount(remap_vertex_count);
ON_3fPoint* p = p_array.Array();
ON_3fVector* v = (ON_3fVector*)p;
for ( k = 0; k < vertex_count; k++ )
{
p[remap[k]] = mp.V[k];
}
for ( k = 0; k < remap_vertex_count; k++ )
mp.V[k] = p[k];
mesh.m_V.SetCount(remap_vertex_count);
if (vertex_count == m_dV.Count())
{
ON_SimpleArray<ON_3dPoint> dp_array;
ON_3dPoint* dp = dp_array.Reserve(remap_vertex_count);
ON_3dPoint* D = m_dV.Array();
for (k = 0; k < vertex_count; k++)
{
dp[remap[k]] = D[k];
}
for (k = 0; k < remap_vertex_count; k++)
D[k] = dp[k];
m_dV.SetCount(remap_vertex_count);
}
else
m_dV.Destroy();
if ( 0 != mp.N )
{
if ( bIgnoreVertexNormals )
{
// average vertex normals of combined vertices
p_array.Zero();
for ( k = 0; k < vertex_count; k++ )
{
v[remap[k]] += mp.N[k];
}
for ( k = 0; k < remap_vertex_count; k++ )
{
v[k].Unitize();
}
}
else
{
for ( k = 0; k < vertex_count; k++ )
{
v[remap[k]] = mp.N[k];
}
}
for ( k = 0; k < remap_vertex_count; k++ )
mp.N[k] = v[k];
mesh.m_N.SetCount(remap_vertex_count);
}
else
mesh.m_N.SetCount(0);
if ( 0 != mp.T && !bIgnoreTextureCoordinates )
{
for ( k = 0; k < vertex_count; k++ )
{
p[remap[k]] = mp.T[k];
}
for ( k = 0; k < remap_vertex_count; k++ )
mp.T[k] = p[k];
mesh.m_T.SetCount(remap_vertex_count);
}
else
mesh.m_T.SetCount(0);
if ( 0 != mp.C && !bIgnoreTextureCoordinates )
{
ON_SimpleArray<ON_Color> c_array(remap_vertex_count);
c_array.SetCount(remap_vertex_count);
ON_Color* c = c_array.Array();
for ( k = 0; k < vertex_count; k++ )
{
c[remap[k]] = mp.C[k];
}
for ( k = 0; k < remap_vertex_count; k++ )
mp.C[k] = c[k];
mesh.m_C.SetCount(remap_vertex_count);
}
else
mesh.m_C.SetCount(0);
if ( 0 != mp.K && !bIgnoreTextureCoordinates )
{
ON_SimpleArray<ON_SurfaceCurvature> s_array(remap_vertex_count);
s_array.SetCount(remap_vertex_count);
ON_SurfaceCurvature* s = s_array.Array();
for ( k = 0; k < vertex_count; k++ )
{
s[remap[k]] = mp.K[k];
}
for ( k = 0; k < remap_vertex_count; k++ )
mp.K[k] = s[k];
mesh.m_K.SetCount(remap_vertex_count);
}
else
mesh.m_K.SetCount(0);
const int face_count = mesh.m_F.Count();
ON_MeshFace* f = mesh.m_F.Array();
int* fvi;
for ( k = 0; k < face_count; k++ )
{
fvi = f[k].vi;
fvi[0] = remap[fvi[0]];
fvi[1] = remap[fvi[1]];
fvi[2] = remap[fvi[2]];
fvi[3] = remap[fvi[3]];
}
if ( HasNgons() )
{
for ( int ngon_index = 0; ngon_index < m_Ngon.Count(); ngon_index++ )
{
ON_MeshNgon* ngon = m_Ngon[ngon_index];
if ( 0 == ngon )
continue;
for ( unsigned int ngon_vertex_index = 0; ngon_vertex_index < ngon->m_Vcount; ngon_vertex_index++ )
ngon->m_vi[ngon_vertex_index] = remap[ngon->m_vi[ngon_vertex_index]];
}
}
//if (0 != V4V5_NgonList())
//{
// //This mesh has an ngon list.
// //Modify the vertex indexes in the ngons as
// //we did the faces above
// ON_V4V5_MeshNgonList* ngonlist = V4V5_ModifyNgonList();
// int kk, kkct = ngonlist->V4V5_NgonCount();
// for (kk = 0; kk < kkct; kk++)
// {
// ON_MeshNgon* ngon = ngonlist->V4V5_Ngon(kk);
// if (0 == ngon)
// continue;
// for ( k = 0; k < ngon->m_N; k++ )
// ngon->m_vi[k] = remap[ngon->m_vi[k]];
// }
//}
mesh.DestroyPartition();
mesh.DestroyTopology();
mesh.m_S.Destroy();
if ( mesh.m_V.Capacity() > 4*mesh.m_V.Count() && mesh.m_V.Capacity() > 50 )
{
// There is lots of unused memory in the dynamic arrays.
// Release what we can.
mesh.Compact();
}
rc = true;
}
}
return rc;
}
void ON_Mesh::Append( int mesh_count, const ON_Mesh* const* meshes )
{
if ( mesh_count <= 0 || 0 == meshes )
return;
int mi;
int vcount0 = VertexCount();
if ( vcount0 <= 0 )
m_F.SetCount(0);
int fcount0 = FaceCount();
int* vi;
int j;
const ON_Mesh* m;
// The calls to Has*() must happen before the m_V[] and m_F[] arrays get enlarged
// Allow the appendage of VertexNormals, TextureCoordinates, PrincipalCurvatures to empty meshes
// by checking for 0 == vcount0 && 0 == fcount0
bool bHasVertexNormals = (0 == vcount0 || HasVertexNormals());
bool bHasFaceNormals = (0 == vcount0 || 0 == fcount0 || HasFaceNormals());
bool bHasTextureCoordinates = (0 == vcount0 || HasTextureCoordinates());
bool bHasPrincipalCurvatures = (0 == vcount0 || HasPrincipalCurvatures());
bool bHasVertexColors = (0 == vcount0 || HasVertexColors());
bool bHasSurfaceParameters = (0 == vcount0 || HasSurfaceParameters());
bool bHasDoubles = (0 == vcount0 || HasSynchronizedDoubleAndSinglePrecisionVertices());
bool bHasNgonMap = (NgonCount() > 0 && 0 != NgonMap());
bool bHasSurfaceDomain
= bHasSurfaceParameters
&& (0 == vcount0 || (m_srf_domain[0].IsIncreasing() && m_srf_domain[1].IsIncreasing()));
ON_Interval srf_domain[2];
srf_domain[0] = (bHasSurfaceDomain && vcount0 > 0) ? m_srf_domain[0] : ON_Interval::EmptyInterval;
srf_domain[1] = (bHasSurfaceDomain && vcount0 > 0) ? m_srf_domain[1] : ON_Interval::EmptyInterval;
bool bHasTexturePackingDomain
= bHasTextureCoordinates
&& (0 == vcount0 || (m_packed_tex_domain[0].IsIncreasing() && m_packed_tex_domain[1].IsIncreasing()));
ON_Interval packed_tex_domain[2];
packed_tex_domain[0] = (bHasTexturePackingDomain && vcount0 > 0) ? m_packed_tex_domain[0] : ON_Interval::EmptyInterval;
packed_tex_domain[1] = (bHasTexturePackingDomain && vcount0 > 0) ? m_packed_tex_domain[1] : ON_Interval::EmptyInterval;
bool packed_tex_rotate = (bHasTexturePackingDomain && m_packed_tex_rotate) ? true : false;
double srf_scale[2] = { m_srf_scale[0], m_srf_scale[1]};
int fcount = fcount0;
int vcount = vcount0;
unsigned int merged_count = vcount0 > 0 ? 1 : 0;
for ( mi = 0; mi < mesh_count; mi++ )
{
m = meshes[mi];
if ( 0 == m )
continue;
int vcount1 = m->m_V.Count();
if ( vcount1 <= 0 )
continue;
merged_count++;
int fcount1 = m->m_F.Count();
if ( fcount1 > 0 )
fcount += fcount1;
vcount += vcount1;
if ( bHasVertexNormals && !m->HasVertexNormals() )
bHasVertexNormals = false;
if ( bHasTextureCoordinates && !m->HasTextureCoordinates())
bHasTextureCoordinates = false;
if ( bHasPrincipalCurvatures && !m->HasPrincipalCurvatures())
bHasPrincipalCurvatures = false;
if ( bHasVertexColors && !m->HasVertexColors())
bHasVertexColors = false;
if ( bHasSurfaceParameters && !m->HasSurfaceParameters())
bHasSurfaceParameters = false;
if ( bHasDoubles && !m->HasSynchronizedDoubleAndSinglePrecisionVertices())
bHasDoubles = false;
if ( bHasFaceNormals && fcount1 > 0 && !m->HasFaceNormals() )
bHasFaceNormals = false;
if (bHasSurfaceDomain)
{
bHasSurfaceDomain
= bHasSurfaceParameters
&& m->m_srf_domain[0].IsIncreasing()
&& m->m_srf_domain[1].IsIncreasing();
if (bHasSurfaceDomain)
{
srf_domain[0].Union(m->m_srf_domain[0]);
srf_domain[1].Union(m->m_srf_domain[1]);
}
}
if (1 == merged_count && bHasSurfaceParameters && bHasSurfaceDomain)
{
srf_scale[0] = m->m_srf_scale[0];
srf_scale[1] = m->m_srf_scale[1];
}
else
{
srf_scale[0] = 0.0;
srf_scale[1] = 0.0;
}
if (bHasTexturePackingDomain)
{
// 2014-04-01 Dale Lear
// Trying to merger packed_tex_domain[] intervals
// is questionable at best.
// A strong argument can be made for simply deleting
// packed texture domain information when there are
// two non-empty meshes that are merged.
bHasTexturePackingDomain
= bHasTextureCoordinates
&& m->m_packed_tex_domain[0].IsIncreasing()
&& m->m_packed_tex_domain[1].IsIncreasing();
if (packed_tex_rotate != (m->m_packed_tex_rotate?true:false))
{
if (0 == vcount0 && 0 == mi)
{
packed_tex_rotate = (m->m_packed_tex_rotate ? true : false);
}
else
{
bHasTexturePackingDomain = false;
}
}
if (bHasTexturePackingDomain)
{
if (1 == merged_count)
{
packed_tex_rotate = (m->m_packed_tex_rotate ? true : false);
}
else if (packed_tex_rotate != (m->m_packed_tex_rotate ? true : false))
{
bHasTexturePackingDomain = false;
}
packed_tex_domain[0].Union(m->m_packed_tex_domain[0]);
packed_tex_domain[1].Union(m->m_packed_tex_domain[1]);
}
}
}
if ( vcount <= vcount0 && fcount <= fcount0 )
return;
if (!bHasSurfaceParameters || !bHasSurfaceDomain)
{
srf_domain[0] = ON_Interval::EmptyInterval;
srf_domain[1] = ON_Interval::EmptyInterval;
srf_scale[0] = 0.0;
srf_scale[1] = 0.0;
}
if (!bHasTextureCoordinates || !bHasTexturePackingDomain)
{
packed_tex_domain[0] = ON_Interval::EmptyInterval;
packed_tex_domain[1] = ON_Interval::EmptyInterval;
packed_tex_rotate = false;
}
m_srf_domain[0] = srf_domain[0];
m_srf_domain[1] = srf_domain[1];
m_srf_scale[0] = srf_scale[0];
m_srf_scale[1] = srf_scale[1];
m_packed_tex_domain[0] = packed_tex_domain[0];
m_packed_tex_domain[1] = packed_tex_domain[1];
m_packed_tex_rotate = packed_tex_rotate;
m_top.Destroy();
// It is critical to call DoublePrecisionVertices() before
// we modify m_V[] because DoublePrecisionVertices() will
// attempt to update the double precision information
// when it notices that m_V has new vertices added.
m_V.Reserve(vcount);
m_F.Reserve(fcount);
for (mi = 0; mi < mesh_count; mi++ )
{
const unsigned int vcount0_local = m_V.UnsignedCount();
const unsigned int fcount0_local = m_F.UnsignedCount();
m = meshes[mi];
if ( 0 == m )
continue;
int vcount1 = m->m_V.Count();
if ( vcount1 <= 0 )
continue;
int fcount1 = m->m_F.Count();
if ( fcount1 > 0 )
{
j = m_F.Count();
m_F.Append(fcount1,m->m_F.Array());
fcount1 += j;
while (j < fcount1)
{
vi = m_F[j].vi;
vi[0] += (int)vcount0_local;
vi[1] += (int)vcount0_local;
vi[2] += (int)vcount0_local;
vi[3] += (int)vcount0_local;
j++;
}
}
m_V.Append(vcount1,m->m_V.Array());
if ( m->HasNgons() )
{
if ( 0 != m->NgonMap() )
bHasNgonMap = true;
m_NgonMap.Destroy();
unsigned int ngon_count = m->NgonUnsignedCount();
for ( unsigned int ni = 0; ni < ngon_count; ni++ )
{
const ON_MeshNgon* ngon0 = m->Ngon(ni);
if ( 0 == ngon0 )
continue;
if ( 0 == ngon0->m_Vcount && 0 == ngon0->m_Fcount )
continue;
ON_MeshNgon* ngon1 = this->m_NgonAllocator.CopyNgon(ngon0);
if ( 0 == ngon1 )
continue;
for ( unsigned int nvi = 0; nvi < ngon1->m_Vcount; nvi++ )
{
ngon1->m_vi[nvi] += vcount0_local;
}
for ( unsigned int nfi = 0; nfi < ngon1->m_Fcount; nfi++ )
{
ngon1->m_fi[nfi] += fcount0_local;
}
this->AddNgon(ngon1);
}
}
}
if ( bHasDoubles)
{
// Now update the double precision vertex locations.
m_dV.Reserve(vcount);
for (mi = 0; mi < mesh_count; mi++ )
{
m = meshes[mi];
if ( 0 == m || m->m_dV.Count() <= 0 )
continue;
m_dV.Append(m->m_dV.Count(),m->m_dV.Array());
}
if (m_dV.Count() != vcount)
bHasDoubles = false;
}
if ( false == bHasDoubles )
{
bHasDoubles = false;
DestroyDoublePrecisionVertices();
}
if ( bHasVertexNormals )
{
m_N.Reserve(vcount);
for (mi = 0; mi < mesh_count; mi++ )
{
m = meshes[mi];
if ( 0 == m )
continue;
m_N.Append(m->m_N.Count(), m->m_N.Array());
}
}
else
{
m_N.Destroy();
}
if ( bHasFaceNormals )
{
m_FN.Reserve(fcount);
for (mi = 0; mi < mesh_count; mi++ )
{
m = meshes[mi];
if ( 0 == m || m->m_V.Count() <= 0 )
continue;
m_FN.Append(m->m_FN.Count(), m->m_FN.Array());
}
}
else
{
m_FN.Destroy();
}
if ( bHasTextureCoordinates )
{
m_T.Reserve(vcount);
for (mi = 0; mi < mesh_count; mi++ )
{
m = meshes[mi];
if ( 0 == m )
continue;
m_T.Append(m->m_T.Count(), m->m_T.Array());
}
}
else
{
m_T.Destroy();
}
if ( bHasSurfaceParameters )
{
m_S.Reserve(vcount);
for (mi = 0; mi < mesh_count; mi++ )
{
m = meshes[mi];
if ( 0 == m )
continue;
m_S.Append(m->m_S.Count(), m->m_S.Array());
}
}
else
{
m_S.Destroy();
}
if ( bHasPrincipalCurvatures )
{
m_K.Reserve(vcount);
for (mi = 0; mi < mesh_count; mi++ )
{
m = meshes[mi];
if ( 0 == m )
continue;
m_K.Append(m->m_K.Count(), m->m_K.Array());
}
}
else
{
m_K.Destroy();
}
if ( bHasVertexColors )
{
m_C.Reserve(vcount);
for (mi = 0; mi < mesh_count; mi++ )
{
m = meshes[mi];
if ( 0 == m )
continue;
m_C.Append(m->m_C.Count(), m->m_C.Array());
}
}
else
{
m_C.Destroy();
}
if ( 0 != m_mesh_parameters )
{
for (mi = 0; mi < mesh_count; mi++ )
{
m = meshes[mi];
if ( 0 == m )
continue;
if ( 0 == m->m_mesh_parameters || *m_mesh_parameters != *m->m_mesh_parameters )
{
delete m_mesh_parameters;
m_mesh_parameters = 0;
break;
}
}
}
for ( j = 0; j < 4; j++ )
{
if ( m_kstat[j] )
{
// will be recomputed if required
delete m_kstat[j];
m_kstat[j] = 0;
}
}
SetClosed(-99);
SetSolidOrientation(-99);
InvalidateBoundingBoxes();
if ( NgonCount() > 0 && bHasNgonMap )
CreateNgonMap();
}
void ON_Mesh::Append( const ON_Mesh& m )
{
const ON_Mesh* meshes[1];
meshes[0] = &m;
Append(1,meshes);
}
ON_MeshParameters::MESH_STYLE ON_MeshParameters::MeshStyleFromUnsigned(
unsigned int mesh_style_as_unsigned
)
{
switch (mesh_style_as_unsigned)
{
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_STYLE::unset_mesh_style);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_STYLE::render_mesh_fast);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_STYLE::render_mesh_quality);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_STYLE::render_mesh_custom);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_STYLE::render_mesh_per_object);
}
ON_ERROR("Invalid mesh_style_as_unsigned parameter");
return ON_MeshParameters::MESH_STYLE::unset_mesh_style;
}
ON_MeshParameters::MESH_PARAMETER_ID ON_MeshParameters::MeshParameterIdFromUnsigned(
unsigned int mesh_parameter_id_as_unsigned
)
{
switch (mesh_parameter_id_as_unsigned)
{
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::unspecified_mesh_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::bComputeCurvature_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::bSimplePlanes_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::bRefine_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::bJaggedSeams_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::bDoublePrecision_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::mesher_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::texture_range_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::tolerance_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::relative_tolerance_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::min_tolerance_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::min_edge_length_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::max_edge_length_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::grid_aspect_ratio_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::grid_min_count_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::grid_max_count_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::grid_angle_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::grid_amplification_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::refine_angle_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::face_type_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::srf_domain_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::bClosedObjectPostProcess_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::mesher_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::crv_tess_min_num_segments_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::crv_tess_angle_tol_in_degrees_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::crv_tess_max_dist_between_points_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::crv_tess_min_parametric_ratio_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::bEvaluatorBasedTessellation_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::srf_tess_chord_height_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::srf_tess_angle_tol_in_degrees_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::srf_tess_max_edge_length_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::srf_tess_min_edge_length_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::srf_tess_min_edge_length_ratio_uv_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::srf_tess_max_aspect_ratio_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::smoothing_passes_parameter_id);
ON_ENUM_FROM_UNSIGNED_CASE(ON_MeshParameters::MESH_PARAMETER_ID::max_mesh_parameter_id);
}
ON_ERROR("Invalid mesh_parameter_id_as_unsigned parameter");
return ON_MeshParameters::MESH_PARAMETER_ID::unspecified_mesh_parameter_id;
}
void ON_MeshParameters::Internal_SetCharHelper(unsigned int u, unsigned char minc, unsigned char maxc, unsigned char* dest)
{
if (u > 255)
return;
const unsigned char c = (unsigned char)u;
if (c >= minc && c <= maxc && c != *dest)
{
m_geometry_settings_hash = ON_SHA1_Hash::ZeroDigest;
*dest = c;
}
}
void ON_MeshParameters::Internal_SetBoolHelper(bool b, bool* dest)
{
b = b ? true : false;
if (b != *dest)
{
m_geometry_settings_hash = ON_SHA1_Hash::ZeroDigest;
*dest = b;
}
}
void ON_MeshParameters::Internal_SetDoubleHelper(double x, double minx, double maxx, double* dest)
{
if (!ON_IsValid(x))
return;
if (ON_UNSET_VALUE != minx && !(x >= minx))
return;
if (ON_UNSET_VALUE != maxx && !(x <= maxx))
return;
if (x == *dest)
return;
m_geometry_settings_hash = ON_SHA1_Hash::ZeroDigest;
*dest = x;
}
void ON_MeshParameters::Internal_SetIntHelper(int i, int mini, int maxi, int* dest)
{
if (!ON_IsValid(i))
return;
if (ON_UNSET_INT_INDEX != mini && !(i >= mini))
return;
if (ON_UNSET_INT_INDEX != maxi && !(i <= maxi))
return;
if (i == *dest)
return;
m_geometry_settings_hash = ON_SHA1_Hash::ZeroDigest;
*dest = i;
}
const bool ON_MeshParameters::CustomSettings() const
{
return m_bCustomSettings;
}
void ON_MeshParameters::SetCustomSettings(
bool bCustomSettings
)
{
Internal_SetBoolHelper(bCustomSettings, &m_bCustomSettings);
}
const bool ON_MeshParameters::CustomSettingsEnabled() const
{
return m_bCustomSettingsEnabled;
}
void ON_MeshParameters::SetCustomSettingsEnabled(
bool bCustomSettingsEnabled
)
{
Internal_SetBoolHelper(bCustomSettingsEnabled, &m_bCustomSettingsEnabled);
}
const bool ON_MeshParameters::ComputeCurvature() const
{
return m_bComputeCurvature;
}
void ON_MeshParameters::SetComputeCurvature(
bool bComputeCurvature
)
{
Internal_SetBoolHelper(bComputeCurvature, &m_bComputeCurvature);
}
const bool ON_MeshParameters::SimplePlanes() const
{
return m_bSimplePlanes;
}
void ON_MeshParameters::SetSimplePlanes(
bool bSimplePlanes
)
{
Internal_SetBoolHelper(bSimplePlanes, &m_bSimplePlanes);
}
void ON_MeshParameters::SetSubDDisplayParameters(
const ON_SubDDisplayParameters& subd_parameters
)
{
m_subd_mesh_parameters = subd_parameters.EncodeAsUnsignedChar();
}
const ON_SubDDisplayParameters ON_MeshParameters::SubDDisplayParameters() const
{
return ON_SubDDisplayParameters::DecodeFromUnsignedChar(m_subd_mesh_parameters);
}
const bool ON_MeshParameters::Refine() const
{
return m_bRefine;
}
void ON_MeshParameters::SetRefine(
bool bRefine
)
{
Internal_SetBoolHelper(bRefine, &m_bRefine);
}
const bool ON_MeshParameters::JaggedSeams() const
{
return m_bJaggedSeams;
}
void ON_MeshParameters::SetJaggedSeams(
bool bJaggedSeams
)
{
Internal_SetBoolHelper(bJaggedSeams, &m_bJaggedSeams);
}
const bool ON_MeshParameters::DoublePrecision() const
{
return m_bDoublePrecision;
}
void ON_MeshParameters::SetDoublePrecision(
bool bDoublePrecision
)
{
Internal_SetBoolHelper(bDoublePrecision, &m_bDoublePrecision);
}
const unsigned int ON_MeshParameters::Mesher() const
{
return static_cast<unsigned int>(m_mesher);
}
void ON_MeshParameters::SetMesher(
unsigned int mesher
)
{
Internal_SetCharHelper(mesher, 0, 1, &m_mesher);
}
const unsigned int ON_MeshParameters::TextureRange() const
{
return static_cast<unsigned int>(m_texture_range);
}
void ON_MeshParameters::SetTextureRange(
unsigned int texture_range
)
{
Internal_SetCharHelper(texture_range, 1, 2, &m_texture_range);
}
const bool ON_MeshParameters::TextureRangeIsValid() const
{
return (1 == m_texture_range || 2 == m_texture_range);
}
void ON_MeshParameters::SetTextureRangePictureFrameHack()
{
// A 2012 hack to fix cited issues. Serch for calls in core Rhino
// code for comments with more details.
// Fix http://mcneel.myjetbrains.com/youtrack/issue/RH-17042
// Setting mp.TextureRange() = 0 here fixes RH-17042.
// What's one more hack where picture frames are concerned?
// As of March 26, tech support felt this picture frame tc bug was
// so critical that we held up the release of Rhino 5 SR2
// for this fix.
//
// In November of 2012, tech support felt the following
// five bugs were so critical, Rhino SR1 could not be released.
// http://dev.mcneel.com/bugtrack/?q=120560 = http://mcneel.myjetbrains.com/youtrack/issue/RH-15423
// http://dev.mcneel.com/bugtrack/?q=101150 = http://mcneel.myjetbrains.com/youtrack/issue/RH-12450
// http://dev.mcneel.com/bugtrack/?q=105787 = http://mcneel.myjetbrains.com/youtrack/issue/RH-13203
// http://dev.mcneel.com/bugtrack/?q=119292 = http://mcneel.myjetbrains.com/youtrack/issue/RH-15277
// http://dev.mcneel.com/bugtrack/?q=119294 = http://mcneel.myjetbrains.com/youtrack/issue/RH-15279
//
// Do not modify this code without testing all the bugs listed above.
// If you modify this code, put in an extensive comment that includes
// bug citations.
unsigned int invalid_texture_range = 0;
Internal_SetCharHelper(invalid_texture_range, 0, 0, &m_texture_range);
}
const bool ON_MeshParameters::ClosedObjectPostProcess() const
{
return m_bClosedObjectPostProcess;
}
void ON_MeshParameters::SetClosedObjectPostProcess(
bool bClosedObjectPostProcess
)
{
Internal_SetBoolHelper(bClosedObjectPostProcess, &m_bClosedObjectPostProcess);
}
const double ON_MeshParameters::Tolerance() const
{
return m_tolerance;
}
void ON_MeshParameters::SetTolerance(
double tolerance
)
{
Internal_SetDoubleHelper(tolerance, 0.0, ON_UNSET_VALUE, &m_tolerance);
}
const double ON_MeshParameters::RelativeTolerance() const
{
return m_relative_tolerance;
}
void ON_MeshParameters::SetRelativeTolerance(
double relative_tolerance
)
{
Internal_SetDoubleHelper(relative_tolerance, 0.0, 1.0, &m_relative_tolerance);
}
const double ON_MeshParameters::MinimumTolerance() const
{
return m_min_tolerance;
}
void ON_MeshParameters::SetMinimumTolerance(
double minimum_tolerance
)
{
Internal_SetDoubleHelper(minimum_tolerance, 0.0, ON_UNSET_VALUE, &m_min_tolerance);
}
const double ON_MeshParameters::MinimumEdgeLength() const
{
return m_min_edge_length;
}
void ON_MeshParameters::SetMinimumEdgeLength(
double minimum_edge_length
)
{
// Allow 0.0 or values >= ON_ZERO_TOLERANCE
const double minx
= (0.0 == minimum_edge_length)
? 0.0
: ON_ZERO_TOLERANCE;
Internal_SetDoubleHelper(minimum_edge_length, minx, ON_UNSET_VALUE, &m_min_edge_length);
}
const double ON_MeshParameters::MaximumEdgeLength() const { return m_max_edge_length; }
void ON_MeshParameters::SetMaximumEdgeLength(
double maximum_edge_length
)
{
Internal_SetDoubleHelper(maximum_edge_length, 0.0, ON_UNSET_VALUE, &m_max_edge_length);
}
const double ON_MeshParameters::GridAspectRatio() const
{
return m_grid_aspect_ratio;
}
void ON_MeshParameters::SetGridAspectRatio(
double grid_aspect_ratio
)
{
Internal_SetDoubleHelper(grid_aspect_ratio, 0.0, ON_UNSET_VALUE, &m_grid_aspect_ratio);
}
const int ON_MeshParameters::GridMinCount() const
{
return m_grid_min_count;
}
void ON_MeshParameters::SetGridMinCount(
int grid_min_count
)
{
Internal_SetIntHelper(grid_min_count, 0, ON_UNSET_INT_INDEX, &m_grid_min_count);
}
const int ON_MeshParameters::GridMaxCount() const
{
return m_grid_max_count;
}
void ON_MeshParameters::SetGridMaxCount(
int grid_max_count
)
{
Internal_SetIntHelper(grid_max_count, 0, ON_UNSET_INT_INDEX, &m_grid_max_count);
}
const double ON_MeshParameters::GridAngleRadians() const
{
return m_grid_angle_radians;
}
void ON_MeshParameters::SetGridAngleRadians(
double grid_angle_radians
)
{
if (grid_angle_radians > ON_PI && grid_angle_radians < (1.0 + ON_EPSILON)*2.0*ON_PI)
grid_angle_radians = ON_PI;
Internal_SetDoubleHelper(grid_angle_radians, 0.0, ON_PI, &m_grid_angle_radians);
}
const double ON_MeshParameters::GridAngleDegrees() const
{
return ON_DegreesFromRadians(GridAngleRadians());
}
void ON_MeshParameters::SetGridAngleDegrees(
double grid_angle_degrees
)
{
SetGridAngleRadians(ON_RadiansFromDegrees(grid_angle_degrees));
}
const double ON_MeshParameters::GridAmplification() const
{
return m_grid_amplification;
}
void ON_MeshParameters::SetGridAmplification(
double grid_amplification
)
{
Internal_SetDoubleHelper(grid_amplification, 0.0, ON_UNSET_VALUE, &m_grid_amplification);
}
const double ON_MeshParameters::RefineAngleRadians() const
{
return m_refine_angle_radians;
}
void ON_MeshParameters::SetRefineAngleRadians(
double refine_angle_radians
)
{
if (refine_angle_radians > ON_PI && refine_angle_radians < (1.0 + ON_EPSILON)*2.0*ON_PI)
refine_angle_radians = ON_PI;
Internal_SetDoubleHelper(refine_angle_radians, 0.0, ON_PI, &m_refine_angle_radians);
}
const double ON_MeshParameters::RefineAngleDegrees() const
{
return ON_DegreesFromRadians(RefineAngleRadians());
}
void ON_MeshParameters::SetRefineAngleDegrees(
double refine_angle_degrees
)
{
SetRefineAngleRadians(ON_RadiansFromDegrees(refine_angle_degrees));
}
const unsigned int ON_MeshParameters::FaceType() const
{
return static_cast<unsigned int>(m_face_type);
}
void ON_MeshParameters::SetFaceType(
unsigned int face_type
)
{
Internal_SetCharHelper(face_type, 0, 2, &m_face_type);
}
ON_MeshParameters::ON_MeshParameters(
double density,
double min_edge_length
)
{
// non-default values
SetTextureRange(0);
SetGridAngleRadians(0.0);
SetGridAmplification(0.0);
SetRefineAngleRadians(0.0);
SetMinimumEdgeLength(min_edge_length);
if ( ON_IsValid(density) )
{
if ( density < 0.0 )
density = 0.0;
else if ( density > 1.0 )
density = 1.0;
SetRelativeTolerance(density);
SetRefine((density < 0.65));
SetSimplePlanes((0.0 == density));
unsigned int subd_display_density = ON_SubDDisplayParameters::Default.DisplayDensity();
if (density <= ON_ZERO_TOLERANCE)
subd_display_density = 1;
else if (density < 1.0/6.0)
subd_display_density = ON_SubDDisplayParameters::CourseDensity;
else if (density < 1.0/3.0)
subd_display_density = (ON_SubDDisplayParameters::DefaultDensity+ON_SubDDisplayParameters::CourseDensity)/2;
else if (density <= 0.75)
subd_display_density = ON_SubDDisplayParameters::DefaultDensity;
else if (density <= 1.0-ON_ZERO_TOLERANCE)
subd_display_density = (ON_SubDDisplayParameters::DefaultDensity+ON_SubDDisplayParameters::MaximumDensity)/2;
else if (density >= 1.0 - ON_ZERO_TOLERANCE)
subd_display_density = ON_SubDDisplayParameters::MaximumDensity;
ON_SubDDisplayParameters subd_parameters(ON_SubDDisplayParameters::Default);
subd_parameters.SetDisplayDensity(subd_display_density);
SetSubDDisplayParameters(subd_parameters);
}
}
double ON_MeshParameters::MinimumEdgeLengthFromTolerance(
double max_edge_length,
double tolerance
)
{
// The 1.0e-4 is a guess. 1.0e-12 was too small to help with objects
// like the one in 67885.
double min_edge_length = 1.0e-4;
if ( max_edge_length > 0.0 && min_edge_length > 1.0e-3*max_edge_length )
min_edge_length = 1.0e-3*max_edge_length;
if ( tolerance > 0.0 && min_edge_length > 1.0e-2*tolerance )
min_edge_length = 1.0e-2*tolerance;
return min_edge_length;
}
double ON_MeshParameters::ToleranceFromObjectSize(
double relative_tolerance,
double actual_size
)
{
// min_rel_tol creates the most facets
//const double min_rel_tol = 5.0e-5;
//max rel tol creates the fewest facets
//const double max_rel_tol = 0.1;
//m_relative_tolerance = 0.0;
//if ( relative_tolerance <= 0.0 )
// m_relative_tolerance = max_rel_tol;
//else if ( relative_tolerance >= 1.0 )
// m_relative_tolerance = min_rel_tol;
//else
//{
// ON_Interval e(log10(max_rel_tol),log10(min_rel_tol));
// m_relative_tolerance = pow(10.0,e.ParameterAt(relative_tolerance));
//}
double tol = 0.0;
double x, e;
if ( ON_IsValid(relative_tolerance) && ON_IsValid(actual_size)
&& relative_tolerance > 0.0 && actual_size > 0.0 )
{
if ( relative_tolerance > 1.0 )
relative_tolerance = 1.0;
//e = (relative_tolerance*(12.0 + relative_tolerance*(2.0*relative_tolerance - 9.0)));
//e = (1.0 + relative_tolerance*(8.0 - 4.0*relative_tolerance));
//e = 1.0 + relative_tolerance*4.0;
//x = 5.0*pow(10.0,-e);
e = (relative_tolerance < 0.5)
? 1.0 + relative_tolerance*(6.0 - 4.0*relative_tolerance) // 1.0 + 4.0*relative_tolerance
: 2.0 + 2.0*relative_tolerance;
x = pow(10.0,-e);
tol = actual_size*x;
}
return tol;
}
bool operator==(const ON_MeshParameters& a, const ON_MeshParameters& b)
{
return 0 == ON_MeshParameters::Compare(a,b);
}
bool operator!=(const ON_MeshParameters& a, const ON_MeshParameters& b)
{
return 0 != ON_MeshParameters::Compare(a,b);
}
void ON_MeshParameters::Dump( ON_TextLog& text_log ) const
{
text_log.Print(L"Gridding:\n");
text_log.PushIndent();
text_log.Print(L"Min grid count = %d\n",m_grid_min_count);
text_log.Print(L"Max grid count = %d\n",m_grid_max_count);
text_log.Print(L"Gridding angle = %g radians (%g degrees)\n",GridAngleRadians(),GridAngleDegrees());
text_log.Print(L"Aspect ratio = %g\n",m_grid_aspect_ratio);
text_log.Print(L"Amplification = %g\n",m_grid_amplification);
text_log.PopIndent();
text_log.Print(L"Refining:\n");
text_log.PushIndent();
text_log.Print(L"Refine = %ls\n", m_bRefine? L"true" : L"false");
text_log.Print(L"Refine angle = %g radians (%g degrees)\n",RefineAngleRadians(),RefineAngleDegrees());
text_log.PopIndent();
text_log.Print(L"Metrics:\n");
text_log.PushIndent();
text_log.Print(L"Tolerance from size 1 object = %g (relative tolerance = %g)\n",ON_MeshParameters::ToleranceFromObjectSize(RelativeTolerance(),1.0),RelativeTolerance());
text_log.Print(L"Minimum tolerance = %g\n",MinimumTolerance());
text_log.Print(L"Tolerance = %g\n",m_tolerance);
text_log.Print(L"Min edge length = %g\n",m_min_edge_length);
text_log.Print(L"Max edge length = %g\n",m_max_edge_length);
text_log.PopIndent();
text_log.Print(L"Misceleanous:\n");
text_log.PushIndent();
text_log.Print(L"Face type = %d\n",m_face_type );
text_log.Print(L"Compute curvature = %ls\n",m_bComputeCurvature?L"true":L"false");
text_log.Print(L"Texture range = %d\n",m_texture_range);
text_log.Print(L"Simple planes = %ls\n",m_bSimplePlanes?L"true":L"false");
text_log.Print(L"Jagged Seams = %ls\n",m_bJaggedSeams?L"true":L"false");
text_log.Print(L"Double Precision = %ls\n",m_bDoublePrecision?L"true":L"false");
text_log.Print(L"Closed object mesh healing = %ls\n",ClosedObjectPostProcess()?L"true":L"false");
text_log.Print(L"Custom settings = %ls\n",m_bCustomSettings?L"true":L"false");
text_log.PopIndent();
}
static double ON_MeshParameters_SHA1Double(double t, double default_value)
{
return (ON_IsValid(t) && t > 0.0) ? t : default_value;
}
int ON_MeshParameters::Compare(
const ON_MeshParameters& a,
const ON_MeshParameters& b
)
{
return ON_SHA1_Hash::Compare(a.ContentHash(), b.ContentHash());
}
int ON_MeshParameters::CompareGeometrySettings(
const ON_MeshParameters& a,
const ON_MeshParameters& b
)
{
return ON_SHA1_Hash::Compare(a.GeometrySettingsHash(), b.GeometrySettingsHash());
}
ON_SHA1_Hash ON_MeshParameters::ContentHash() const
{
// Discuss any changes with Dale Lear
// Use ON_MeshParameters::GeometrySettingsHash() if you want to ignore any of these values, like m_bComputeCurvature.
ON_SHA1 sha1;
sha1.AccumulateBool(m_bCustomSettings);
sha1.AccumulateBool(m_bComputeCurvature);
sha1.AccumulateUnsigned32(m_texture_range);
const ON_SHA1_Hash geometry_settings_hash = GeometrySettingsHash();
sha1.AccumulateSubHash(geometry_settings_hash);
return sha1.Hash();
}
void ON_MeshParameters::Internal_AccumulatePangolinParameters(
const ON_MeshParameters & pangolin_defaults,
ON_SHA1& sha1
) const
{
// Pangolin parameters
if (pangolin_defaults.m_bEvaluatorBasedTessellation != m_bEvaluatorBasedTessellation)
sha1.AccumulateBool(m_bEvaluatorBasedTessellation);
if (pangolin_defaults.m_curve_tess_min_num_segments != m_curve_tess_min_num_segments)
sha1.AccumulateInteger32(m_curve_tess_min_num_segments);
if (pangolin_defaults.m_curve_tess_angle_tol_in_degrees != m_curve_tess_angle_tol_in_degrees)
sha1.AccumulateDouble(m_curve_tess_angle_tol_in_degrees);
if (pangolin_defaults.m_curve_tess_max_dist_between_points != m_curve_tess_max_dist_between_points)
sha1.AccumulateDouble(m_curve_tess_max_dist_between_points);
if (pangolin_defaults.m_curve_tess_min_parametric_ratio != m_curve_tess_min_parametric_ratio)
sha1.AccumulateDouble(m_curve_tess_min_parametric_ratio);
if (pangolin_defaults.m_surface_tess_angle_tol_in_degrees != m_surface_tess_angle_tol_in_degrees)
sha1.AccumulateDouble(m_surface_tess_angle_tol_in_degrees);
if (pangolin_defaults.m_surface_tess_max_edge_length != m_surface_tess_max_edge_length)
sha1.AccumulateDouble(m_surface_tess_max_edge_length);
if (pangolin_defaults.m_surface_tess_min_edge_length != m_surface_tess_min_edge_length)
sha1.AccumulateDouble(m_surface_tess_min_edge_length);
if (pangolin_defaults.m_surface_tess_min_edge_length_ratio_uv != m_surface_tess_min_edge_length_ratio_uv)
sha1.AccumulateDouble(m_surface_tess_min_edge_length_ratio_uv);
if (pangolin_defaults.m_surface_tess_max_aspect_ratio != m_surface_tess_max_aspect_ratio)
sha1.AccumulateDouble(m_surface_tess_max_aspect_ratio);
if (pangolin_defaults.m_smoothing_passes != m_smoothing_passes)
sha1.AccumulateInteger32(m_smoothing_passes);
}
ON_SHA1_Hash ON_MeshParameters::GeometrySettingsHash() const
{
// Discuss any changes with Dale Lear
if (m_geometry_settings_hash.IsZeroDigest())
{
ON_SHA1 sha1;
sha1.AccumulateBool(m_bSimplePlanes);
sha1.AccumulateBool(m_bRefine);
sha1.AccumulateBool(m_bJaggedSeams);
sha1.AccumulateUnsigned32(m_mesher);
sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_tolerance,0.0));
sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_relative_tolerance,0.0));
// Do not include m_min_tolerance as a geometry setting.
// It is a runtime lower bound clamp.
// If it is included here, Rhino will remesh everytime model tolerance changes.
sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_min_edge_length,0.0));
sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_max_edge_length,0.0));
sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_grid_aspect_ratio,0.0));
sha1.AccumulateInteger32(m_grid_min_count);
sha1.AccumulateInteger32(m_grid_max_count);
sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_grid_angle_radians,ON_PI));
sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_refine_angle_radians,0.0));
sha1.AccumulateDouble(ON_MeshParameters_SHA1Double(m_grid_amplification,1.0));
sha1.AccumulateUnsigned32(m_face_type);
sha1.AccumulateBool(m_bClosedObjectPostProcess);
// The Pangolin parameters and any other, parameters we add in the future,
// contribute to the SHA1 only when they differ from default values.
// This keeps old SHA-1 values correct and prevents remeshing when openning
// old files.
sha1.AccumulateId(m_mesher_id);
// Pangolin parameters
Internal_AccumulatePangolinParameters(ON_MeshParameters::DefaultMesh,sha1);
m_geometry_settings_hash = sha1.Hash();
}
return m_geometry_settings_hash;
}
ON_UUID ON_MeshParameters::MesherId() const
{
return m_mesher_id;
}
void ON_MeshParameters::SetMesherId(
ON_UUID id
)
{
if (id != m_mesher_id)
{
m_geometry_settings_hash = ON_SHA1_Hash::ZeroDigest;
m_mesher_id = id;
}
}
const ON_MeshParameters::MESH_STYLE ON_MeshParameters::GeometrySettingsRenderMeshStyle(
const ON_MeshParameters* custom_mp,
MESH_STYLE no_match_found_result
) const
{
if ( 0 == ON_MeshParameters::CompareGeometrySettings(ON_MeshParameters::FastRenderMesh,*this))
return ON_MeshParameters::MESH_STYLE::render_mesh_fast;
if ( 0 == ON_MeshParameters::CompareGeometrySettings(ON_MeshParameters::QualityRenderMesh,*this))
return ON_MeshParameters::MESH_STYLE::render_mesh_quality;
if ( 0 != custom_mp && 0 == ON_MeshParameters::CompareGeometrySettings(*custom_mp,*this))
return ON_MeshParameters::MESH_STYLE::render_mesh_custom;
return no_match_found_result;
}
const int ON_MeshParameters::GeometrySettingsDensityPercentage(
int no_match_found_result
) const
{
for ( int n = 0; n <= 100; n++ )
{
double density = (50 == n) ? 0.5 : (n/100.0);
ON_MeshParameters mp_at_n(density);
mp_at_n.m_bDoublePrecision = m_bDoublePrecision;
mp_at_n.m_texture_range = m_texture_range;
if ( 0 == ON_MeshParameters::CompareGeometrySettings(mp_at_n,*this) )
return n;
}
return no_match_found_result;
}
ON__UINT32 ON_MeshParameters::DataCRC(ON__UINT32 current_remainder) const
{
const ON_SHA1_Hash content_hash = ContentHash();
const ON__UINT32 crc = ON_CRC32(current_remainder,sizeof(content_hash),&content_hash);
return crc;
}
bool ON_MeshParameters::Write( ON_BinaryArchive& file ) const
{
int minor_version = 4;
bool rc = file.Write3dmChunkVersion(1,minor_version);
if (rc)
{
if (rc) rc = file.WriteInt(m_bComputeCurvature);
if (rc) rc = file.WriteInt(m_bSimplePlanes);
if (rc) rc = file.WriteInt(m_bRefine);
if (rc) rc = file.WriteInt(m_bJaggedSeams);
if (rc) rc = file.WriteInt(0); // obsolete m_bWeld field
if (rc) rc = file.WriteDouble(m_tolerance);
if (rc) rc = file.WriteDouble(m_min_edge_length);
if (rc) rc = file.WriteDouble(m_max_edge_length);
if (rc) rc = file.WriteDouble(m_grid_aspect_ratio);
if (rc) rc = file.WriteInt(m_grid_min_count);
if (rc) rc = file.WriteInt(m_grid_max_count);
if (rc) rc = file.WriteDouble(m_grid_angle_radians);
if (rc) rc = file.WriteDouble(m_grid_amplification);
if (rc) rc = file.WriteDouble(m_refine_angle_radians);
if (rc) rc = file.WriteDouble(5.0*ON_PI/180.0); // obsolete m_combine_angle field
int mft = m_face_type;
if ( mft < 0 || mft > 2 )
{
ON_ERROR("ON_MeshParameters::Read() - m_face_type out of bounds.");
mft = 0;
}
if (rc) rc = file.WriteInt(mft);
// added for chunk version 1.1
if (rc) rc = file.WriteInt( m_texture_range );
// added for chunk version 1.2 - 14 October 2005
if (rc) rc = file.WriteBool( m_bCustomSettings );
if (rc) rc = file.WriteDouble( m_relative_tolerance );
// DO NOT SAVE m_min_tolerance - yet ??
// added for chunk version 1.3 - 20 February 2006
if (rc) rc = file.WriteChar(m_mesher);
// added for chunk version 1.4 - 3 March 2011
if (rc) rc = file.WriteBool( m_bCustomSettingsEnabled );
}
return rc;
}
bool ON_MeshParameters::Read( ON_BinaryArchive& file )
{
*this = ON_MeshParameters::DefaultMesh;
m_geometry_settings_hash = ON_SHA1_Hash::ZeroDigest;
int major_version = 0;
int minor_version = 0;
bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
if ( rc && major_version == 1 )
{
int i;
i = m_bComputeCurvature;
if (rc) rc = file.ReadInt(&i);
m_bComputeCurvature = i?true:false;
i = m_bSimplePlanes;
if (rc) rc = file.ReadInt(&i);
m_bSimplePlanes = i?true:false;
i = m_bRefine;
if (rc) rc = file.ReadInt(&i);
m_bRefine = i?true:false;
i = m_bJaggedSeams;
if (rc) rc = file.ReadInt(&i);
m_bJaggedSeams = i?true:false;
int obsolete_m_bWeld;
if (rc) rc = file.ReadInt(&obsolete_m_bWeld);
if (rc) rc = file.ReadDouble(&m_tolerance);
if (rc) rc = file.ReadDouble(&m_min_edge_length);
if (rc) rc = file.ReadDouble(&m_max_edge_length);
if (rc) rc = file.ReadDouble(&m_grid_aspect_ratio);
if (rc) rc = file.ReadInt(&m_grid_min_count);
if (rc) rc = file.ReadInt(&m_grid_max_count);
if (rc) rc = file.ReadDouble(&m_grid_angle_radians);
if (rc) rc = file.ReadDouble(&m_grid_amplification);
if (rc) rc = file.ReadDouble(&m_refine_angle_radians);
double obsolete_m_combine_angle;
if (rc) rc = file.ReadDouble(&obsolete_m_combine_angle);
unsigned int face_type = FaceType();
if (rc) rc = file.ReadInt(&face_type);
if (rc)
SetFaceType(face_type);
if ( rc && minor_version >= 1 )
{
unsigned int texture_range = TextureRange();
rc = file.ReadInt( &texture_range );
if (rc)
SetTextureRange(texture_range);
if ( rc && minor_version >= 2 )
{
rc = file.ReadBool(&m_bCustomSettings);
if (rc) rc = file.ReadDouble(&m_relative_tolerance);
if ( rc && minor_version >= 3 )
{
unsigned char mesher = (unsigned char)Mesher();
rc = file.ReadChar(&mesher);
if (rc)
SetMesher(mesher);
if ( rc && minor_version >= 4 )
{
rc = file.ReadBool(&m_bCustomSettingsEnabled);
}
}
}
}
}
return rc;
}
ON_MeshCurvatureStats::ON_MeshCurvatureStats()
{
Destroy(); // initializes defaults
}
ON_MeshCurvatureStats::~ON_MeshCurvatureStats()
{}
void ON_MeshCurvatureStats::Destroy()
{
m_style = ON::unknown_curvature_style;
m_infinity = 0.0;
m_count_infinite = 0;
m_count = 0;
m_mode = 0.0;
m_average = 0.0;
m_adev = 0.0;
m_range.Set(0.0,0.0);
}
void ON_MeshCurvatureStats::EmergencyDestroy()
{
Destroy(); // - no memory is freed so Destroy() will work
}
ON_MeshCurvatureStats::ON_MeshCurvatureStats(const ON_MeshCurvatureStats& src)
{
*this = src;
}
ON_MeshCurvatureStats& ON_MeshCurvatureStats::operator=(const ON_MeshCurvatureStats& src)
{
if ( this != &src ) {
m_style = src.m_style;
m_infinity = src.m_infinity;
m_count_infinite = src.m_count_infinite;
m_count = src.m_count;
m_mode = src.m_mode;
m_average = src.m_average;
m_adev = src.m_adev;
m_range = src.m_range;
}
return *this;
}
bool ON_MeshCurvatureStats::Write( ON_BinaryArchive& file ) const
{
int i;
bool rc = file.Write3dmChunkVersion(1,1);
if (rc) {
i = m_style;
if (rc) rc = file.WriteInt(i);
if (rc) rc = file.WriteDouble(m_infinity);
if (rc) rc = file.WriteInt(m_count_infinite);
if (rc) rc = file.WriteInt(m_count);
if (rc) rc = file.WriteDouble(m_mode);
if (rc) rc = file.WriteDouble(m_average);
if (rc) rc = file.WriteDouble(m_adev);
if (rc) rc = file.WriteInterval(m_range);
}
return rc;
}
bool ON_MeshCurvatureStats::Read( ON_BinaryArchive& file )
{
int major_version = 0;
int minor_version = 0;
Destroy();
bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
if (rc && major_version == 1) {
int i=0;
if (rc) rc = file.ReadInt(&i);
if (rc) m_style = ON::CurvatureStyle(i);
if (rc) rc = file.ReadDouble(&m_infinity);
if (rc) rc = file.ReadInt(&m_count_infinite);
if (rc) rc = file.ReadInt(&m_count);
if (rc) rc = file.ReadDouble(&m_mode);
if (rc) rc = file.ReadDouble(&m_average);
if (rc) rc = file.ReadDouble(&m_adev);
if (rc) rc = file.ReadInterval(m_range);
}
return rc;
}
bool ON_MeshCurvatureStats::Set( ON::curvature_style kappa_style,
int Kcount,
const ON_SurfaceCurvature* K,
const ON_3fVector* N, // needed for normal sectional curvatures
double infinity
)
{
bool rc = (Kcount > 0 && K != nullptr);
Destroy();
if (rc) {
ON_Workspace ws;
//ON_3dVector tangent;
double k;
double* kappa = ws.GetDoubleMemory(Kcount);
int i;
switch( kappa_style ) {
case ON::gaussian_curvature:
m_style = kappa_style;
m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e20;
break;
case ON::mean_curvature: // unsigned mean
m_style = kappa_style;
m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
break;
case ON::min_curvature: // minimum unsigned radius of curvature
m_style = kappa_style;
m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
break;
case ON::max_curvature: // maximum unsigned radius of curvature
m_style = kappa_style;
m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
break;
//case ON::section_curvature_x:
// if ( !N )
// kappa_style = ON::unknown_curvature_style;
// m_style = kappa_style;
// m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
// break;
//case ON::section_curvature_y:
// if ( !N )
// kappa_style = ON::unknown_curvature_style;
// m_style = kappa_style;
// m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
// break;
//case ON::section_curvature_z:
// if ( !N )
// kappa_style = ON::unknown_curvature_style;
// m_style = kappa_style;
// m_infinity = ( infinity > 0.0 ) ? infinity : 1.0e10;
// break;
default:
rc = false;
break;
}
for ( i = 0; i < Kcount; i++ ) {
switch( kappa_style ) {
case ON::gaussian_curvature:
k = K[i].GaussianCurvature();
break;
case ON::mean_curvature: // unsigned mean
k = fabs(K[i].MeanCurvature());
break;
case ON::min_curvature: // minimum unsigned radius of curvature
k = fabs(K[i].MinimumRadius());
break;
case ON::max_curvature: // maximum unsigned radius of curvature
k = fabs(K[i].MaximumRadius());
break;
//case ON::section_curvature_x:
// tangent.x = 0.0; tangent.y = -N[i].z; tangent.z = N[i].y;
// if ( fabs(tangent.y) <= ON_SQRT_EPSILON && fabs(tangent.z) <= ON_SQRT_EPSILON )
// tangent.Zero();
// else
// tangent.Unitize();
// k = fabs(K[i].NormalCurvature(tangent));
// break;
//case ON::section_curvature_y:
// tangent.x = N[i].z; tangent.y = 0.0; tangent.z = -N[i].x;
// if ( fabs(tangent.x) <= ON_SQRT_EPSILON && fabs(tangent.z) <= ON_SQRT_EPSILON )
// tangent.Zero();
// else
// tangent.Unitize();
// k = fabs(K[i].NormalCurvature(tangent));
// break;
//case ON::section_curvature_z:
// tangent.x = -N[i].y; tangent.y = N[i].x; tangent.z = 0.0;
// if ( fabs(tangent.x) <= ON_SQRT_EPSILON && fabs(tangent.y) <= ON_SQRT_EPSILON )
// tangent.Zero();
// else
// tangent.Unitize();
// k = fabs(K[i].NormalCurvature(tangent));
// break;
default:
k=0.0;
break;
}
if ( fabs(k) >= m_infinity ) {
m_count_infinite++;
continue;
}
if ( m_count ) {
if ( k < m_range.m_t[0] )
m_range.m_t[0] = k;
else if ( k > m_range.m_t[1] )
m_range.m_t[1] = k;
}
else {
m_range.m_t[0] = m_range.m_t[1] = k;
}
kappa[m_count++] = k;
}
if ( m_count == 0 )
rc = false;
else {
// sum curvatures
ON_SortDoubleArray( ON::sort_algorithm::quick_sort, kappa, m_count );
// mode
m_mode = kappa[m_count/2];
if ( 0 == (m_count % 2) ) {
m_mode += kappa[(m_count/2)-1];
m_mode *= 0.5;
}
// average
for ( i = 0; i < m_count; i++ ) {
m_average += kappa[i];
}
m_average = m_average/m_count;
// average deviation
for ( i = 0; i < m_count; i++ ) {
m_adev += fabs(kappa[i] - m_average);
}
m_adev = m_adev/m_count;
}
}
return rc;
}
struct EDGEINFO
{
int fi[2]; // m_F[] triangles on either side of the edge
int vi[4]; // potential m_V[] quad indices
int flag; // 0 = available,
// 1 = side of quad,
// 2 = bndry or nonmanifold edge,
// 3 = edge crease angle > angle_tol_radians
// 4 = edge is not longest side of triangle
// 5 = edge length is zero
// 6 = edge would be a boundary if mesh were exploded
// 7 = quad would not be convex
// 8 = if the edge were removed, the quad would not pass the min_diagonal_length_ratio test (not "square" enough)
// 16 = tha diagnoal is too short to remove in the first pass that makes the "obvious" quads.
double length;
};
bool ON_Mesh::ConvertTrianglesToQuads(
double angle_tol_radians,
double min_diagonal_length_ratio
)
{
ON_Workspace ws;
ON_3dPoint QCornerPoints[4];
ON_3dVector QuadSides[4];
ON_3dVector QuadCornerNormals[4];
double QuadCornerDots[4];
double d;
int i, ii, jj;
int diagonal_count;
const int* f0vi;
const int* f1vi;
const int* fei;
diagonal_count = 0;
if ( angle_tol_radians < 0.0
|| !ON_IsValid(angle_tol_radians)
|| angle_tol_radians > 0.5*ON_PI )
{
// 2.5 degrees
angle_tol_radians = 0.043633231299858239423092269212215;
}
else if ( angle_tol_radians < ON_ZERO_TOLERANCE )
{
angle_tol_radians = ON_ZERO_TOLERANCE;
}
angle_tol_radians = cos(angle_tol_radians);
if ( angle_tol_radians < 0.5 )
angle_tol_radians = 0.5;
else if ( angle_tol_radians > 1.0-ON_SQRT_EPSILON )
angle_tol_radians = 1.0-ON_SQRT_EPSILON;
const ON_MeshTopology& top = Topology();
if ( !HasFaceNormals() )
ComputeFaceNormals();
if ( min_diagonal_length_ratio < ON_ZERO_TOLERANCE )
min_diagonal_length_ratio = ON_ZERO_TOLERANCE;
double max_diagonal_length_ratio = 1.0/min_diagonal_length_ratio;
if( min_diagonal_length_ratio > max_diagonal_length_ratio )
{
d = min_diagonal_length_ratio;
min_diagonal_length_ratio = max_diagonal_length_ratio;
max_diagonal_length_ratio = d;
}
//double rel_tol = ON_SQRT_EPSILON;
double rel_tol = 8.0*ON_SQRT_EPSILON; // to fix RR 51543
if ( min_diagonal_length_ratio > 1.0-rel_tol )
min_diagonal_length_ratio = 1.0-rel_tol;
if ( max_diagonal_length_ratio < 1.0+rel_tol )
max_diagonal_length_ratio = 1.0+rel_tol;
const int face_count = m_F.Count();
int* face_flag = ws.GetIntMemory(face_count);
for ( i = 0; i < face_count; i++ )
{
f0vi = m_F[i].vi;
face_flag[i] = ( f0vi[2] == f0vi[3] ) ? 0 : 1;
}
const int edge_count = top.m_tope.Count();
struct EDGEINFO* EI = (struct EDGEINFO*)ws.GetMemory(edge_count*sizeof(*EI));
for ( i = 0; i < edge_count; i++ )
{
struct EDGEINFO& ei = EI[i];
const ON_MeshTopologyEdge& tope = top.m_tope[i];
ei.flag = 0;
ei.vi[0] = top.m_topv[tope.m_topvi[0]].m_vi[0];
ei.vi[2] = top.m_topv[tope.m_topvi[1]].m_vi[0];
ei.length = m_V[ei.vi[0]].DistanceTo(m_V[ei.vi[2]]);
if ( !(ei.length > 0.0) || !ON_IsValid(ei.length) )
{
// ei.length is a nan or worse
ei.flag = 5;
}
else if ( tope.m_topf_count != 2 )
{
ei.flag = 2;
}
else
{
ei.fi[0] = tope.m_topfi[0];
ei.fi[1] = tope.m_topfi[1];
if (face_flag[ei.fi[0]] || face_flag[ei.fi[1]] )
{
ei.flag = 1;
}
else if ( m_FN[ei.fi[0]] * m_FN[ei.fi[1]] < angle_tol_radians )
{
ei.flag = 3;
}
else
{
f0vi = m_F[ei.fi[0]].vi;
f1vi = m_F[ei.fi[1]].vi;
ei.flag = 6;
for ( ii = 0; ii < 3 && 0 != ei.flag; ii++ )
{
for (jj = 0; jj < 3; jj++)
{
if ( f0vi[ii] == f1vi[jj]
&& f0vi[(ii+1)%3] == f1vi[(jj+2)%3]
&& f0vi[(ii+2)%3] != f1vi[(jj+1)%3] )
{
if ( ei.fi[0] > ei.fi[1] )
{
jj = ei.fi[0]; ei.fi[0] = ei.fi[1]; ei.fi[1] = jj;
}
ei.vi[0] = f0vi[ii];
ei.vi[1] = f1vi[(jj+1)%3];
ei.vi[2] = f0vi[(ii+1)%3];
ei.vi[3] = f0vi[(ii+2)%3];
// convexity test
QCornerPoints[0] = m_V[ei.vi[0]];
QCornerPoints[1] = m_V[ei.vi[1]];
QCornerPoints[2] = m_V[ei.vi[2]];
QCornerPoints[3] = m_V[ei.vi[3]];
QuadSides[0] = QCornerPoints[1] - QCornerPoints[0];
QuadSides[1] = QCornerPoints[2] - QCornerPoints[1];
QuadSides[2] = QCornerPoints[3] - QCornerPoints[2];
QuadSides[3] = QCornerPoints[0] - QCornerPoints[3];
// Actually, opposite normal but
QuadCornerNormals[0] = ON_CrossProduct(QuadSides[0], QuadSides[3]);
QuadCornerNormals[1] = ON_CrossProduct(QuadSides[1], QuadSides[0]);
QuadCornerNormals[2] = ON_CrossProduct(QuadSides[2], QuadSides[1]);
QuadCornerNormals[3] = ON_CrossProduct(QuadSides[3], QuadSides[2]);
QuadCornerDots[0] = QuadCornerNormals[0] * QuadCornerNormals[1];
QuadCornerDots[1] = QuadCornerNormals[1] * QuadCornerNormals[2];
QuadCornerDots[2] = QuadCornerNormals[2] * QuadCornerNormals[3];
QuadCornerDots[3] = QuadCornerNormals[3] * QuadCornerNormals[0];
if (QuadCornerDots[0] > 0.0)
{
if (QuadCornerDots[1] > 0.0 && QuadCornerDots[2] > 0.0 && QuadCornerDots[3] > 0.0)
{
// If the diagonal were removed, the resulting quad would be convex
// so this edge is a candidate for being removed.
ei.flag = 0;
break;
}
}
else if (QuadCornerDots[0] < 0.0)
{
if (QuadCornerDots[1] < 0.0 && QuadCornerDots[2] < 0.0 && QuadCornerDots[3] < 0.0)
{
// If the diagonal were removed, the resulting quad would be convex
// so this edge is a candidate for being removed.
ei.flag = 0;
break;
}
}
// If the diagonal were removed, the resulting quad would not be convex
// or one of the QuadCornerDots[] values is a nan.
// In either case, this edge will not be removed.
ei.flag = 7;
break;
}
}
}
}
}
}
for ( i = 0; i < edge_count; i++ )
{
struct EDGEINFO& ei = EI[i];
if ( 0 != ei.flag )
continue;
d = m_V[ei.vi[1]].DistanceTo(m_V[ei.vi[3]]);
d /= ei.length;
if (d < min_diagonal_length_ratio || d > max_diagonal_length_ratio)
{
// quad wouldn't be square enough
ei.flag = 8;
continue;
}
if (face_count > 2)
{
// It is CRITICAL that the length compare use >=.
// Otherwise tesselations of equailateral triangles
// will not work right in this fuction.
fei = top.m_topf[ei.fi[0]].m_topei;
if ((i != fei[0] && EI[fei[0]].length >= ei.length)
|| (i != fei[1] && EI[fei[1]].length >= ei.length)
|| (i != fei[2] && EI[fei[2]].length >= ei.length)
)
{
// diagonal is not strictly longest in this triangle.
// A 2nd pass would be required to determine which
// diagonals could be removed.
// See the file "RH-5481 IssueClarified.3dm" in
// http://mcneel.myjetbrains.com/youtrack/issue/RH-5481
// for an example.
ei.flag = 16;
continue;
}
// It is CRITICAL that the length compare use >=.
// Otherwise tesselations of equailateral triangles
// will not work right in this fuction.
fei = top.m_topf[ei.fi[1]].m_topei;
if ((i != fei[0] && EI[fei[0]].length >= ei.length)
|| (i != fei[1] && EI[fei[1]].length >= ei.length)
|| (i != fei[2] && EI[fei[2]].length >= ei.length)
)
{
// diagonal is not strictly longest in this triangle
// A 2nd pass would be required to determine which
// diagonals could be removed.
// See the file "RH-5481 IssueClarified.3dm" in
// http://mcneel.myjetbrains.com/youtrack/issue/RH-5481
// for an example.
ei.flag = 16;
continue;
}
}
ei.flag = 0;
diagonal_count++;
}
// Dale Lear 2016-March-28
// If we are going to fix bugs like the issue raised in
// http://mcneel.myjetbrains.com/youtrack/issue/RH-5481
// Additional passes need to be made here that decide which diagonals to
// remove hen the diagonal is shorter than a resulting quad side.
// See the file "RH-5481 IssueClarified.3dm" in
// http://mcneel.myjetbrains.com/youtrack/issue/RH-5481
// for an example.
if ( diagonal_count > 0 )
{
DestroyTree();
DestroyPartition();
m_top.Destroy();
m_invalid_count = 0;
m_triangle_count = 0;
m_quad_count = 0;
for (i = 0; i < edge_count; i++)
{
struct EDGEINFO& ei = EI[i];
if ( ei.flag )
continue;
memcpy( m_F[ei.fi[0]].vi, ei.vi, 4*sizeof(ei.vi[0]) );
memset( m_F[ei.fi[1]].vi, 0xFF, 4*sizeof(ei.vi[0]) );
}
CullDegenerateFaces();
}
return (diagonal_count > 0 );
}
bool ON_Mesh::ConvertQuadsToTriangles()
{
double planar_tolerance = ON_UNSET_VALUE;
double angle_tolerance_radians = ON_UNSET_VALUE;
unsigned int split_method = 1;
ConvertNonPlanarQuadsToTriangles(
planar_tolerance,
angle_tolerance_radians,
split_method
);
return ( QuadCount() == 0 && TriangleCount() == FaceCount() ) ? true : false;
}
double ON_TriangleArea3d(ON_3dPoint A, ON_3dPoint B, ON_3dPoint C)
{
// speed this up if needed
return 0.5*ON_CrossProduct(B-A,C-A).Length();
}
double ON_TriangleArea2d(ON_2dPoint A, ON_2dPoint B, ON_2dPoint C)
{
const double ABx = B.x-A.x;
const double ABy = B.y-A.y;
const double CAx = C.x-A.x;
const double CAy = C.y-A.y;
return 0.5*fabs((ABx*CAy) - (ABy*CAx));
}
unsigned int ON_Mesh::ConvertNonPlanarQuadsToTriangles(
double planar_tolerance,
double angle_tolerance_radians,
unsigned int split_method
)
{
bool bDeleteNgonsContainingSplitQuads = false;
return ConvertNonPlanarQuadsToTriangles(
planar_tolerance,
angle_tolerance_radians,
split_method,
bDeleteNgonsContainingSplitQuads
);
}
static int ON_CompareUnsignedInt(const unsigned int* i0, const unsigned int* i1)
{
if ( i0 < i1 )
return -1;
if ( i0 > i1 )
return 1;
return 0;
}
unsigned int ON_Mesh::ConvertNonPlanarQuadsToTriangles(
double planar_tolerance,
double angle_tolerance_radians,
unsigned int split_method,
bool bDeleteNgonsContainingSplitQuads
)
{
const unsigned int face_count0 = FaceUnsignedCount();
if ( face_count0 <= 0 )
return 0;
const ON_3dPointListRef vertex_list(this);
const unsigned int vertex_count = vertex_list.PointCount();
if ( vertex_count <= 0 )
return 0;
const unsigned int quad_count0 = (unsigned int)QuadCount();
if ( quad_count0 <= 0 )
return 0;
const bool bSplitAllQuads = (ON_UNSET_VALUE == planar_tolerance && ON_UNSET_VALUE == angle_tolerance_radians);
if (false == bSplitAllQuads && !(planar_tolerance >= 0.0) && !(angle_tolerance_radians >= 0.0))
return 0;
if ( 0 == split_method )
split_method = 1;
if ( split_method < 1 || split_method > 6 )
return 0;
const bool bHasFaceNormals = HasFaceNormals();
unsigned int ngon_count = HasNgons() ? NgonUnsignedCount() : 0;
unsigned int ngon_index = ON_UNSET_UINT_INDEX;
unsigned int fi;
int idmin;
double d0, d1, dmin, d;
ON_3dPoint P[4];
// use SetCapacity() instead of Reserve() because it's unlikely
// this mesh will have anything else added and we don't want to
// waste memory.
if ( ((unsigned int)m_F.Capacity()) < face_count0 + quad_count0 )
m_F.SetCapacity( face_count0 + quad_count0 );
if ( bHasFaceNormals && ((unsigned int)m_FN.Capacity()) < face_count0 + quad_count0 )
m_FN.SetCapacity( face_count0 + quad_count0 );
const double rel_tol = 8.0*( vertex_list.DoublePrecision() ? ON_EPSILON : ON_FLOAT_EPSILON );
ON_3dVector FN;
// If bDeleteNgonsContainingSplitQuads = true, then uint_buffer[] is a list
// of ngons to delete.
//
// if bDeleteNgonsContainingSplitQuads = false, then ... //
// If the mesh has n-gons, then ngon_new_face_count[ngon_index]
// is the number of new faces to add to an existing ngon.
// This happens when an n-gon contains a quad that is divided
// into two triangles.
ON_SimpleArray<unsigned int> uint_buffer;
unsigned int* ngon_new_face_count = nullptr;
bool bUpdateNgons = false;
bool bDeleteNgonMap = false;
if ( ngon_count > 0 )
{
if (bDeleteNgonsContainingSplitQuads)
{
uint_buffer.Reserve(64);
}
else
{
bDeleteNgonMap = (0 == NgonMap());
if (0 == CreateNgonMap())
{
ngon_count = 0;
bDeleteNgonMap = true;
bDeleteNgonsContainingSplitQuads = true;
}
else
{
uint_buffer.Reserve(ngon_count);
uint_buffer.SetCount(ngon_count);
uint_buffer.Zero();
ngon_new_face_count = uint_buffer.Array();
}
}
}
for ( fi = 0; fi < face_count0; fi++ )
{
ON_MeshFace& f0 = m_F[fi];
if ( ngon_count > 0 )
{
ngon_index = m_NgonMap[fi];
if ( ngon_index >= ngon_count )
{
ngon_index = ON_UNSET_UINT_INDEX;
m_NgonMap[fi] = ngon_index;
}
}
if ( false == f0.IsValid(vertex_count) )
continue;
if ( false == f0.IsQuad() )
continue;
if (planar_tolerance >= 0.0 || angle_tolerance_radians >= 0.0)
{
if (true == f0.IsPlanar(planar_tolerance, angle_tolerance_radians, vertex_list, 0))
continue;
}
P[0] = vertex_list[f0.vi[0]];
P[1] = vertex_list[f0.vi[1]];
P[2] = vertex_list[f0.vi[2]];
P[3] = vertex_list[f0.vi[3]];
d0 = P[0].DistanceTo(P[2]);
d1 = P[1].DistanceTo(P[3]);
// if quad is degenerate, just turn it into a triangle
idmin = -1;
dmin = ((d0<=d1)?d0:d1)*rel_tol;
if ( dmin > ON_ZERO_TOLERANCE )
dmin = ON_ZERO_TOLERANCE;
d = P[0].DistanceTo(P[1]);
if ( d < dmin )
{
idmin = 0;
dmin = d;
}
d = P[1].DistanceTo(P[2]);
if ( d < dmin )
{
idmin = 1;
dmin = d;
}
d = P[2].DistanceTo(P[3]);
if ( d < dmin )
{
idmin = 2;
dmin = d;
}
d = P[3].DistanceTo(P[0]);
if ( d < dmin )
{
idmin = 3;
dmin = d;
}
if ( !(d0 > 0.0) )
{
if ( !(d1 > 0.0) )
continue;
// d0 = 0 or is invalid and d1 > 0
// force split along v[1],v[3]
idmin = -1;
d0 = 1.0;
d1 = 0.0;
}
else if ( !(d1 > 0.0) )
{
// d1 = 0 or is invalid and d0 > 0
// force split along v[0],v[1]
idmin = -1;
d1 = 1.0;
d0 = 0.0;
}
m_quad_count--;
m_triangle_count++;
if ( 0 == idmin ) // m_V[f0.vi[0]] == m_V[f0.vi[1]] (nearly)
{
// degenerate quad - remove duplicate vertex
f0.vi[0] = f0.vi[1];
f0.vi[1] = f0.vi[2];
f0.vi[2] = f0.vi[3];
}
else if ( 1 == idmin ) // m_V[f0.vi[1]] == m_V[f0.vi[2]] (nearly)
{
// degenerate quad - remove duplicate vertex
int vi0 = f0.vi[0];
f0.vi[0] = f0.vi[2];
f0.vi[1] = f0.vi[3];
f0.vi[2] = vi0;
f0.vi[3] = vi0;
}
else if ( 2 == idmin ) // m_V[f0.vi[2]] == m_V[f0.vi[3]] (nearly)
{
// degenerate quad - remove duplicate vertex
f0.vi[2] = f0.vi[1];
f0.vi[1] = f0.vi[0];
f0.vi[0] = f0.vi[3];
f0.vi[3] = f0.vi[2];
}
else if ( 3 == idmin ) // m_V[f0.vi[3]] == m_V[f0.vi[0]] (nearly)
{
// degenerate quad - remove duplicate vertex
f0.vi[3] = f0.vi[2];
}
else
{
// split non-degenerate quad into two triangles
if ( ngon_count > 0 )
{
if (bDeleteNgonsContainingSplitQuads)
{
if (ON_UNSET_INT_INDEX != ngon_index)
{
uint_buffer.Append(ngon_index);
bUpdateNgons = true;
}
}
else
{
// update n-gon map
m_NgonMap.Append(ngon_index);
if (ngon_index < ngon_count)
{
// The quad was in an n-gon and the new face index
// will be appended to the n-gons m_fi[] list.
ngon_new_face_count[ngon_index]++;
bUpdateNgons = true;
}
}
}
ON_MeshFace& f1 = m_F.AppendNew();
bool bSplitAlongDiagonal02 = true;
switch(split_method)
{
case 1:
if ( d1 < d0 )
bSplitAlongDiagonal02 = false;
break;
case 2:
if ( d1 > d0 )
bSplitAlongDiagonal02 = false;
break;
case 3:
case 4:
{
double a0 = ON_TriangleArea3d(P[0],P[1],P[2]) + ON_TriangleArea3d(P[0],P[2],P[3]);
double a1 = ON_TriangleArea3d(P[0],P[1],P[3]) + ON_TriangleArea3d(P[1],P[2],P[3]);
if (a0 == a1)
{
if (d1 < d0)
bSplitAlongDiagonal02 = false;
}
else if ( 3 == split_method )
{
// minimize area
if (a1 < a0)
bSplitAlongDiagonal02 = false;
}
else
{
// maximize area
if (a1 > a0)
bSplitAlongDiagonal02 = false;
}
}
break;
case 5:
case 6:
{
ON_3dVector corner_normals[4];
if (f0.GetCornerNormals(vertex_list, corner_normals))
{
double a[2];
for (unsigned int i = 0; i < 2; i++)
{
a[i]
= (ON_UNSET_VALUE != corner_normals[i].x && ON_UNSET_VALUE != corner_normals[i + 2].x)
? corner_normals[i] * corner_normals[i + 2]
: 2.0; // some valid number bigger than 1.0
}
if (a[0] == a[1])
{
if (d1 < d)
bSplitAlongDiagonal02 = false;
}
else if (5 == split_method)
{
// minimize angle
if (a[0] > a[1])
bSplitAlongDiagonal02 = false;
}
else
{
// maximize angle
if (a[0] < a[1])
bSplitAlongDiagonal02 = false;
}
}
}
break;
}
if ( bSplitAlongDiagonal02 )
{
f1.vi[0] = f0.vi[0];
f1.vi[1] = f0.vi[2];
f1.vi[2] = f0.vi[3];
f1.vi[3] = f1.vi[2];
f0.vi[3] = f0.vi[2];
}
else
{
f1.vi[0] = f0.vi[1];
f1.vi[1] = f0.vi[2];
f1.vi[2] = f0.vi[3];
f1.vi[3] = f1.vi[2];
f0.vi[2] = f0.vi[3];
}
if ( bHasFaceNormals )
{
m_F[fi].ComputeFaceNormal(vertex_list,FN);
m_FN[fi] = FN;
m_F[m_F.Count()-1].ComputeFaceNormal(vertex_list,FN);
m_FN.AppendNew() = FN;
}
}
}
if ( face_count0 != m_F.UnsignedCount() )
{
// we added some triangles which means cached topology
// and partition information is no longer valid.
DestroyTopology();
DestroyPartition();
}
if ( bUpdateNgons )
{
if (bDeleteNgonsContainingSplitQuads)
{
const unsigned int uint_buffer_count = uint_buffer.UnsignedCount();
if (uint_buffer_count > 0)
{
uint_buffer.QuickSort(ON_CompareUnsignedInt);
unsigned int ngon_index_count = 0;
unsigned int* ngon_index_list = uint_buffer.Array();
ngon_index = ON_UNSET_UINT_INDEX;
for (unsigned int i = 0; i < uint_buffer_count; i++ )
{
if (ngon_index == uint_buffer[i])
continue;
ngon_index = uint_buffer[i];
ngon_index_list[ngon_index_count++] = ngon_index;
}
RemoveNgons(ngon_index_count, ngon_index_list);
}
}
else
{
if (m_NgonMap.UnsignedCount() == m_F.UnsignedCount())
{
// allocate room for new face indices
for (ngon_index = 0; ngon_index < ngon_count; ngon_index++)
{
if (ngon_new_face_count[ngon_index] <= 0)
continue;
ON_MeshNgon* ngon = this->m_Ngon[ngon_index];
if (0 == ngon)
continue;
unsigned int Fcount0 = ngon->m_Fcount;
ngon = m_NgonAllocator.ReallocateNgon(ngon, ngon->m_Vcount, ngon->m_Fcount + ngon_new_face_count[ngon_index]);
ngon->m_Fcount = Fcount0;
m_Ngon[ngon_index] = ngon;
}
// Add new faces to appropriate ngon face index lists
for (fi = face_count0; fi < m_F.UnsignedCount(); fi++)
{
ngon_index = m_NgonMap[fi];
if (ngon_index < ngon_count)
{
ON_MeshNgon* ngon = m_Ngon[ngon_index];
if (ngon)
ngon->m_fi[ngon->m_Fcount++] = fi;
}
}
}
else
{
bDeleteNgonMap = true;
}
}
}
if ( bDeleteNgonMap )
m_NgonMap.Destroy();
return quad_count0 - QuadCount();
}
bool ON_Mesh::CountQuads()
{
const int fcount = FaceCount();
const int vcount = VertexCount();
int fi;
m_quad_count = 0;
m_triangle_count = 0;
m_invalid_count = 0;
for ( fi = 0; fi < fcount; fi++ ) {
const ON_MeshFace& f = m_F[fi];
if ( f.IsValid(vcount) ) {
if ( f.IsTriangle() )
m_triangle_count++;
else
m_quad_count++;
}
else
m_invalid_count++;
}
return true;
}
bool ON_Mesh::ComputeVertexNormals()
{
bool rc = false;
const int fcount = FaceCount();
const int vcount = VertexCount();
int vi, fi, j;
ON_3fVector n;
if ( fcount > 0 && vcount > 0 ) {
rc = HasFaceNormals();
if ( !rc )
rc = ComputeFaceNormals();
if ( rc ) {
ON_Workspace ws;
//double* face_area = ws.GetDoubleMemory(fcount);
int* vfcount = ws.GetIntMemory( vcount );
memset ( vfcount, 0, vcount*sizeof(*vfcount) );
// count number of faces that use each vertex
for ( fi = 0; fi < fcount; fi++ ) {
ON_MeshFace& f = m_F[fi];
if ( f.IsValid(vcount) ) {
vfcount[f.vi[0]]++;
vfcount[f.vi[1]]++;
vfcount[f.vi[2]]++;
if ( f.IsQuad() )
vfcount[f.vi[3]]++;
}
}
// set vfi[vi][] = array of face indices that use vertex vi
int** vfi = (int**)ws.GetMemory( vcount*sizeof(vfi[0] ) );
{
int scratch_sz = 0;
for ( vi = 0; vi < vcount; vi++ ) {
scratch_sz += vfcount[vi];
}
int* scratch = ws.GetIntMemory(scratch_sz);
for ( vi = 0; vi < vcount; vi++ ) {
if ( vfcount[vi] ) {
vfi[vi] = scratch;
scratch += vfcount[vi];
}
vfcount[vi] = 0;
}
}
for ( fi = 0; fi < fcount; fi++ ) {
ON_MeshFace& f = m_F[fi];
if ( f.IsValid(vcount) ) {
vi = f.vi[0]; vfi[vi][vfcount[vi]++] = fi;
vi = f.vi[1]; vfi[vi][vfcount[vi]++] = fi;
vi = f.vi[2]; vfi[vi][vfcount[vi]++] = fi;
if ( f.IsQuad() ) {
vi = f.vi[3]; vfi[vi][vfcount[vi]++] = fi;
}
}
}
// average face normals to get an estimate for a vertex normal
m_N.SetCapacity(vcount);
m_N.SetCount(0);
for ( vi = 0; vi < vcount; vi++ )
{
n = ON_3fVector::ZeroVector;
for ( j = vfcount[vi]-1; j >= 0; j-- )
{
n += m_FN[vfi[vi][j]];
}
if ( !n.Unitize() )
{
// this vertex is not used by a face or the face normals cancel out.
// set a unit z normal and press on.
n.Set(0,0,1);
}
m_N.Append(n);
}
}
}
return rc;
}
bool ON_Mesh::NormalizeTextureCoordinates()
{
ON_2fPoint t0;//, t1;
int ti;
const int vertex_count = m_V.Count();
bool rc = HasSurfaceParameters();
if ( rc )
{
const ON_2dPoint* S = m_S.Array();
ON_Interval udom = m_srf_domain[0];
ON_Interval vdom = m_srf_domain[1];
rc = udom.IsIncreasing() && vdom.IsIncreasing();
if ( !rc )
{
udom.Set(S[0].x,S[0].x);
vdom.Set(S[0].y,S[0].y);
for ( ti = 1; ti < vertex_count; ti++ )
{
if ( S[ti].x < udom.m_t[0] ) udom.m_t[0] = S[ti].x;
else if ( S[ti].x > udom.m_t[1] ) udom.m_t[1] = S[ti].x;
if ( S[ti].y < vdom.m_t[0] ) vdom.m_t[0] = S[ti].y;
else if ( S[ti].y > vdom.m_t[1] ) vdom.m_t[1] = S[ti].y;
}
rc = udom.IsIncreasing() && vdom.IsIncreasing();
}
if (rc)
{
m_T.Reserve(vertex_count);
m_T.SetCount(0);
for (ti = 0; ti < vertex_count; ti++ )
{
t0.x = (float)udom.NormalizedParameterAt(S[ti].x);
t0.y = (float)vdom.NormalizedParameterAt(S[ti].y);
m_T.Append(t0);
}
m_packed_tex_domain[0].Set(0.0,1.0);
m_packed_tex_domain[1].Set(0.0,1.0);
m_packed_tex_rotate = false;
m_Ttag.SetDefaultSurfaceParameterMappingTag();
if ( m_mesh_parameters )
m_mesh_parameters->SetTextureRange(1);
}
}
return rc;
}
bool ON_Mesh::TransposeSurfaceParameters()
{
// swap m_srf_domain
ON_Interval temp = m_srf_domain[0];
m_srf_domain[0] = m_srf_domain[1];
m_srf_domain[1] = temp;
double t = m_srf_scale[0];
m_srf_scale[0] = m_srf_scale[1];
m_srf_scale[1] = t;
int S_count = m_S.Count();
ON_2dPoint* S_array = m_S.Array();
for (int i = 0; i < S_count; i++ )
{
ON_2dPoint& S = S_array[i];
t = S.x; S.x = S.y; S.y = t;
}
return true;
}
bool ON_Mesh::HasPackedTextureRegion() const
{
return ( ON_IsValid(m_srf_scale[0])
&& m_srf_scale[0] > 0.0
&& ON_IsValid(m_srf_scale[1])
&& m_srf_scale[1] > 0.0
&& m_packed_tex_domain[0].IsInterval()
&& m_packed_tex_domain[1].IsInterval()
&& 0.0 <= m_packed_tex_domain[0].Min()
&& m_packed_tex_domain[0].Max() <= 1.0
&& 0.0 <= m_packed_tex_domain[1].Min()
&& m_packed_tex_domain[1].Max() <= 1.0
&& ( fabs(m_packed_tex_domain[0].Length()) < 1.0
|| fabs(m_packed_tex_domain[1].Length()) < 1.0)
);
}
bool ON_Mesh::TransposeTextureCoordinates()
{
if ( !HasTextureCoordinates() )
return false;
const int vcnt = m_T.Count();
int i;
bool bPackedRegion = HasPackedTextureRegion();
bool bSrfParamTag = (!m_Ttag.IsSet() || m_Ttag.IsDefaultSurfaceParameterMapping());
if ( bPackedRegion && bSrfParamTag )
{
// The region of the bitmap the texture uses
// cannot change. The texture coordinates
// themselves get reflected in the subrectangle
// about either the lowerleft to upperright
// diagonal (llur = true) or the lowerright
// to upperleft diagonal (llur = false).
bool bRevU = m_packed_tex_domain[0].IsDecreasing();
bool bRevV = m_packed_tex_domain[1].IsDecreasing();
bool llur = (bRevU == bRevV) ? true : false;
if ( m_packed_tex_rotate )
llur = !llur;
ON_Interval TD[2];
TD[0] = m_packed_tex_domain[0];
TD[1] = m_packed_tex_domain[1];
TD[0].MakeIncreasing();
TD[1].MakeIncreasing();
for( i=0; i<vcnt; i++)
{
ON_2fPoint tc = m_T[i];
double x = TD[0].NormalizedParameterAt(tc[0]);
double y = TD[1].NormalizedParameterAt(tc[1]);
if(!llur)
{
x = 1.0-x;
y = 1.0-y;
}
double s = TD[0].ParameterAt(y);
double t = TD[1].ParameterAt(x);
m_T[i].Set((float)s,(float)t);
}
}
else
{
float f;
for(i=0; i<vcnt; i++)
{
ON_2fPoint& tc = m_T[i];
f = tc.x; tc.x = tc.y; tc.y = f;
}
}
return true;
}
bool ON_Mesh::ReverseTextureCoordinates( int dir )
{
if ( dir < 0 || dir > 1 || !HasTextureCoordinates() )
return false;
bool bPackedRegion = HasPackedTextureRegion();
bool bSrfParamTag = (!m_Ttag.IsSet() || m_Ttag.IsDefaultSurfaceParameterMapping());
const int vcount = m_T.Count();
int i;
if ( bPackedRegion && bSrfParamTag )
{
// The region of the bitmap the texture uses
// cannot change. The texture coordinates
// themselves get reflected in the subrectangle
// about either the lowerleft to upperright
// diagonal (llur = true) or the lowerright
// to upperleft diagonal (llur = false).
if ( m_packed_tex_rotate )
dir = 1-dir;
const ON_Interval tex_dom = m_packed_tex_domain[dir];
double s;
m_packed_tex_domain[dir].Swap(); // Swap() is correct - Reverse() is wrong.
for (i = 0; i < vcount; i++ )
{
ON_2fPoint& tc = m_T[i];
s = 1.0 - tex_dom.NormalizedParameterAt(tc[dir]);
if ( dir )
tc.y = (float)tex_dom.ParameterAt(s);
else
tc.x = (float)tex_dom.ParameterAt(s);
}
}
else
{
for (i = 0; i < vcount; i++ )
{
ON_2fPoint& tc = m_T[i];
if ( dir )
tc.y = 1.0f-tc.y;
else
tc.x = 1.0f-tc.x;
}
}
return true;
}
bool ON_Mesh::ReverseSurfaceParameters( int dir )
{
if ( dir < 0 || dir > 1 || !HasSurfaceParameters() )
return false;
if ( m_srf_domain[dir].IsIncreasing() )
m_srf_domain[dir].Reverse();
int i, vcount = m_S.Count();
for (i = 0; i < vcount; i++)
{
ON_2dPoint& S = m_S[i];
if ( dir )
S.y = -S.y;
else
S.x = -S.x;
}
return true;
}
bool ON_Mesh::EvaluateMeshGeometry( const ON_Surface& srf )
{
bool rc = false;
const int vcount = VertexCount();
const bool bHasSurfaceParameters = HasSurfaceParameters();
if ( bHasSurfaceParameters )
{
const bool bHasDoublePrecisionVertices = this->HasDoublePrecisionVertices();
if (false == bHasDoublePrecisionVertices)
this->m_dV.Destroy();
const bool bHasVertexNormals = HasVertexNormals();
m_N.SetCapacity(vcount);
int vi, side, hint[2];
ON_3dPoint point;
ON_3dVector normal, Ds, Dt, Dss, Dst, Dtt, K1, K2;
const ON_2dPoint* srf_st;
double s, t, kgauss, kmean;
side = 0;
hint[0] = 0;
hint[1] = 0;
const double smax = srf.Domain(0)[1];
const double tmax = srf.Domain(1)[1];
if ( HasPrincipalCurvatures() )
{
for ( vi = 0; vi < vcount; vi++ )
{
//ON_2fPoint& tex = m_T[vi];
// convert texture coordinates to normalizied texture coordinates
srf_st = &m_S[vi];
s = srf_st->x;
t = srf_st->y;
// 19 April 2006 Dale Lear - added side setter so singular normals
// are correctly evaluated RR 12482
side = ( smax == s ) ? ((tmax == t) ? 3 : 2) : ((tmax == t) ? 4 : 1);
srf.Ev2Der( s, t, point, Ds, Dt, Dss, Dst, Dtt, side, hint );
ON_EvNormal( side, Ds, Dt, Dss, Dst, Dtt, normal );
ON_EvPrincipalCurvatures( Ds, Dt, Dss, Dst, Dtt, normal,
&kgauss, &kmean,
&m_K[vi].k1, &m_K[vi].k2,
K1, K2 ); //m_K[vi].e1, m_K[vi].e2 );
m_V[vi] = ON_3fPoint(&point.x); // use ON_3fPoint double* conversion (quiets gcc)
if (bHasDoublePrecisionVertices)
m_dV[vi] = point;
if ( bHasVertexNormals )
m_N[vi] = ON_3fVector(&normal.x); // use ON_3fVector double* conversion (quiets gcc)
}
InvalidateCurvatureStats();
}
else if ( bHasVertexNormals )
{
for ( vi = 0; vi < vcount; vi++ )
{
//ON_2fPoint& tex = m_T[vi];
srf_st = &m_S[vi];
s = srf_st->x;
t = srf_st->y;
// 19 April 2006 Dale Lear - added side setter so singular normals
// are correctly evaluated RR 12482
side = ( smax == s ) ? ((tmax == t) ? 3 : 2) : ((tmax == t) ? 4 : 1);
srf.EvNormal( s, t, point, normal, side, hint );
m_V[vi] = ON_3fPoint(&point.x); // use ON_3fPoint double* conversion (quiets gcc)
if (bHasDoublePrecisionVertices)
m_dV[vi] = point;
m_N[vi] = ON_3fVector(&normal.x); // use ON_3fVector double* conversion (quiets gcc)
}
}
else
{
for ( vi = 0; vi < vcount; vi++ )
{
//ON_2fPoint& tex = m_T[vi];
srf_st = &m_S[vi];
s = srf_st->x;
t = srf_st->y;
srf.EvPoint( s, t, point, side, hint );
m_V[vi] = ON_3fPoint(&point.x);
if (bHasDoublePrecisionVertices)
m_dV[vi] = point;
}
}
if ( HasFaceNormals() )
{
ComputeFaceNormals();
}
rc = true;
m_Ctag.Default();
InvalidateVertexBoundingBox();
InvalidateVertexNormalBoundingBox();
DeleteMeshParameters();
DestroyTree();
}
return rc;
}
void ON_Mesh::SetMeshParameters( const ON_MeshParameters& mp )
{
DeleteMeshParameters();
m_mesh_parameters = new ON_MeshParameters(mp);
}
const ON_MeshParameters* ON_Mesh::MeshParameters() const
{
return m_mesh_parameters;
}
void ON_Mesh::DeleteMeshParameters()
{
if ( m_mesh_parameters ) {
delete m_mesh_parameters;
m_mesh_parameters = 0;
}
}
static bool isValid3fPoint(const ON_3fPoint* a)
{
return (ON_IS_VALID_FLOAT(a->x) && ON_IS_VALID_FLOAT(a->y) && ON_IS_VALID_FLOAT(a->z)) ? true : false;
}
static int compare3fPoint( const ON_3fPoint* a, const ON_3fPoint* b )
{
const bool aValid = isValid3fPoint(a);
const bool bValid = isValid3fPoint(b);
if (aValid != bValid)
return (aValid ? -1 : 1); // invalid points sort to end.
if (false == aValid)
return 0; // all invalid points are "equal"
if ( a->x < b->x ) return -1;
if ( a->x > b->x ) return 1;
if ( a->y < b->y ) return -1;
if ( a->y > b->y ) return 1;
if ( a->z < b->z ) return -1;
if ( a->z > b->z ) return 1;
return 0;
}
static bool isValid3dPoint(const ON_3dPoint* a)
{
return (ON_IS_VALID(a->x) && ON_IS_VALID(a->y) && ON_IS_VALID(a->z)) ? true : false;
}
static int compare3dPoint( const ON_3dPoint* a, const ON_3dPoint* b )
{
const bool aValid = isValid3dPoint(a);
const bool bValid = isValid3dPoint(b);
if (aValid != bValid)
return (aValid ? -1 : 1); // invalid points sort to end.
if (false == aValid)
return 0; // all invalid points are "equal"
if ( a->x < b->x ) return -1;
if ( a->x > b->x ) return 1;
if ( a->y < b->y ) return -1;
if ( a->y > b->y ) return 1;
if ( a->z < b->z ) return -1;
if ( a->z > b->z ) return 1;
return 0;
}
typedef int (*ON_COMPAR_LPVOID_LPVOID)(const void*,const void*);
static
unsigned int GetRemoveDegenerateFacesPointMap(
unsigned int pt_count,
const ON_3fPoint* fV,
const ON_3dPoint* dV,
ON_SimpleArray<unsigned int>& pt_map
)
{
// Faster than ON_Mesh::GetVertexLocationIds()
// This static is used only in CullDegenerateFaces().
//
// Builds a mapping array, pt_map[], such that the length of pt_map[]
// is pt_count and pt_map[i] == pt_map[j] if and only if pt[i] == pt[j]
// as 3d points. The values in map[] run from 0 to max_pt_index.
unsigned int vt0, vt1;
ON_3fPoint fp0;
ON_3dPoint dp0;
unsigned int* map;
unsigned int* index;
unsigned int max_pt_index = 0;
if (pt_count > 0 && (nullptr != dV || nullptr != fV))
{
index = (unsigned int*)onmalloc(pt_count*sizeof(*index));
if ( dV )
ON_Sort( ON::sort_algorithm::quick_sort, index, dV, pt_count, sizeof(*dV), (ON_COMPAR_LPVOID_LPVOID)compare3dPoint );
else
ON_Sort( ON::sort_algorithm::quick_sort, index, fV, pt_count, sizeof(*fV), (ON_COMPAR_LPVOID_LPVOID)compare3fPoint );
pt_map.SetCapacity( pt_count );
pt_map.SetCount( pt_count );
map = pt_map.Array();
for (vt0 = 0; vt0 < pt_count; vt0++)
map[vt0] = ON_UNSET_UINT_INDEX;
if ( dV )
{
for (vt0 = 0; vt0 < pt_count; vt0 = vt1, max_pt_index++)
{
dp0 = dV[index[vt0]];
for ( vt1=vt0+1; vt1<pt_count && 0==compare3dPoint(&dp0,dV+index[vt1]); vt1++ ) {
// empty
}
while ( vt0 < vt1 ) {
map[index[vt0++]] = max_pt_index;
}
}
// invalid points are sorted to the end
vt1 = pt_count;
while (vt1-- > 0)
{
if (isValid3dPoint(dV + index[vt1]))
break;
map[index[vt1]] = ON_UNSET_UINT_INDEX; // invalid point get invalid index
}
}
else
{
for (vt0 = 0; vt0 < pt_count; vt0 = vt1, max_pt_index++)
{
fp0 = fV[index[vt0]];
for ( vt1=vt0+1; vt1<pt_count && 0==compare3fPoint(&fp0,fV+index[vt1]); vt1++ ) {
// empty
}
while ( vt0 < vt1 ) {
map[index[vt0++]] = max_pt_index;
}
}
// invalid points are sorted to the end
vt1 = pt_count;
while (vt1-- > 0)
{
if (isValid3fPoint(fV + index[vt1]))
break;
map[index[vt1]] = ON_UNSET_UINT_INDEX; // invalid point get invalid index
}
}
onfree(index);
}
if ( max_pt_index == 0 )
pt_map.Destroy();
return max_pt_index;
}
unsigned int ON_Mesh::CullDegenerateFaces()
{
const unsigned int face_count0 = m_F.UnsignedCount();
DeleteComponents(
nullptr,
0,
true, // bIgnoreInvalidComponents
true, // bRemoveDegenerateFaces
false, // bRemoveUnusedVertices
true // bRemoveEmptyNgons
);
const unsigned int face_count1 = m_F.UnsignedCount();
return (face_count0 > face_count1) ? face_count0 - face_count1 : 0;
}
unsigned int ON_Mesh::CullDegenerates()
{
const int mesh_vertex_count0 = VertexCount();
const int mesh_face_count0 = FaceCount();
const int mesh_quad_count0 = QuadCount();
const int mesh_tri_count0 = TriangleCount();
// now cull bad faces, invalid vertices, and unreferenced vertices
DeleteComponents(
nullptr,
0,
true,
true,
true,
true
);
const int mesh_vertex_count1 = VertexUnsignedCount();
const int mesh_face_count1 = FaceUnsignedCount();
const int mesh_quad_count1 = QuadCount();
const int mesh_tri_count1 = TriangleCount();
if (
mesh_vertex_count0 == mesh_vertex_count1
&& mesh_face_count0 == mesh_face_count1
&& mesh_quad_count0 == mesh_quad_count1
&& mesh_tri_count0 == mesh_tri_count1
)
return 0;
int rc = abs(mesh_vertex_count1 - mesh_vertex_count0) + abs(mesh_face_count1 - mesh_face_count0);
if (0 == rc)
{
// need to return nonzero if a degenerate quad got changed into a triangle
rc = abs(mesh_quad_count1 - mesh_quad_count0);
if (0 == rc)
{
rc = abs(mesh_tri_count1 - mesh_tri_count0);
}
}
return (unsigned int)rc;
}
int ON_Mesh::CullUnusedVertices()
{
const unsigned int vcount0 = m_V.UnsignedCount();
DeleteComponents(
nullptr, // ci_list
0, // ci_count
true, // bIgnoreInvalidComponents
false, // bRemoveDegenerateFaces
true, // bRemoveUnusedVertices
false // bRemoveEmptyNgons
);
if ( 0 == m_V.UnsignedCount() )
Destroy();
return ((int)(vcount0 - m_V.UnsignedCount()));
}
bool ON_Mesh::Compact()
{
unsigned int meshVcount = m_V.UnsignedCount();
unsigned int meshFcount = m_F.UnsignedCount();
if (m_FN.UnsignedCount() != meshFcount)
m_FN.Destroy();
if (m_N.UnsignedCount() != meshVcount)
m_N.Destroy();
if (m_T.UnsignedCount() != meshVcount)
m_T.Destroy();
if (m_S.UnsignedCount() != meshVcount)
m_S.Destroy();
if (m_K.UnsignedCount() != meshVcount)
m_K.Destroy();
if (m_C.UnsignedCount() != meshVcount)
m_C.Destroy();
if (m_H.UnsignedCount() != meshVcount)
m_H.Destroy();
CullUnusedVertices();
m_V.Shrink();
m_F.Shrink();
m_N.Shrink();
m_FN.Shrink();
m_K.Shrink();
m_C.Shrink();
m_S.Shrink();
m_T.Shrink();
return true;
}
void ON_Mesh::Cleanup(
bool bRemoveNgons,
bool bRemoveDegenerateFaces,
bool bCompact
)
{
V4V5_DestroyNgonList(); // old junk
if ( bRemoveNgons )
SetNgonCount(0);
if (bRemoveDegenerateFaces)
CullDegenerateFaces();
DestroyRuntimeCache(true);
if ( bCompact )
Compact();
}
void ON_Mesh::Cleanup(bool bRemoveNgons)
{
const bool bRemoveDegenerateFaces = true;
const bool bCompact = true;
Cleanup(
bRemoveNgons,
bRemoveDegenerateFaces,
bCompact
);
}
////////////////////////////////////////////////////////////////
//
// ON_SurfaceCurvature
//
double ON_SurfaceCurvature::GaussianCurvature() const
{
return k1*k2;
}
double ON_SurfaceCurvature::MeanCurvature() const
{
return 0.5*(k1+k2);
}
double ON_SurfaceCurvature::MinimumRadius() const
{
double k;
k = (fabs(k1)>=fabs(k2)) ? fabs(k1) : fabs(k2); // k = maximum directional curvature
k = ( k > 1.0e-300 ) ? 1.0/k : 1.0e300; // 1/k = minimum radius of curvature
return k;
}
double ON_SurfaceCurvature::MaximumRadius() const
{
double k;
if ( k1*k2 <= 0.0 ) {
// if principal curvatures have opposite signs, there
// is a direction with zero directional curvature
k = 0.0;
}
else {
k = (fabs(k1)<=fabs(k2)) ? fabs(k1) : fabs(k2);
}
// k = minimum directional curvature
k = ( k > 1.0e-300 ) ? 1.0/k : 1.0e300; // 1/k = maximum radius of curvature
return k;
}
//double ON_SurfaceCurvature::NormalCurvature(const ON_3dVector& tangent) const
//{
// double c = tangent*e1;
// double s = tangent*e2;
// return k1*c*c + k2*s*s;
//}
//double ON_SurfaceCurvature::NormalSectionCurvature( const ON_3dVector& section_normal, const ON_3dVector& surface_normal ) const
//{
// ON_3dVector tangent = ON_CrossProduct( section_normal, surface_normal );
// if ( fabs(tangent.x) <= ON_SQRT_EPSILON && fabs(tangent.y) <= ON_SQRT_EPSILON && fabs(tangent.z) <= ON_SQRT_EPSILON )
// tangent.Zero();
// else
// tangent.Unitize();
// return NormalCurvature(tangent);
//}
ON_MeshTopology::ON_MeshTopology()
: m_mesh(0)
, m_memchunk(0)
, m_b32IsValid(0)
{
}
ON_MeshTopology::~ON_MeshTopology()
{
Destroy();
}
void ON_MeshTopology::Destroy()
{
m_topv_map.Destroy();
m_topv.Destroy();
m_tope.Destroy();
m_topf.Destroy();
struct memchunk *p, *n;
n = m_memchunk;
while(n)
{
p = n;
n = n->next;
onfree(p);
}
m_memchunk = 0;
if ( -1 != m_b32IsValid)
m_b32IsValid = 0;
}
void ON_MeshTopology::EmergencyDestroy()
{
m_mesh = 0;
m_topv_map.EmergencyDestroy();
m_topv.EmergencyDestroy();
m_tope.EmergencyDestroy();
m_topf.EmergencyDestroy();
m_memchunk = 0;
m_b32IsValid = 0;
}
int ON_MeshTopology::TopVertexCount() const
{
return m_topv.Count();
}
//////////
// number of topoligical edges
int ON_MeshTopology::TopEdgeCount() const
{
return m_tope.Count();
}
//////////
// number of topoligical faces (same as m_mesh.FaceCount())
int ON_MeshTopology::TopFaceCount() const
{
return m_topf.Count();
}
ON_3dPoint ON_MeshTopology::TopVertexPoint(int vtopi) const
{
const int mesh_vertex_index = m_topv[vtopi].m_vi[0];
if (mesh_vertex_index >= 0)
{
const unsigned int vertex_count = m_mesh->VertexUnsignedCount();
if ((unsigned int)mesh_vertex_index < vertex_count)
{
if (vertex_count == m_mesh->m_dV.UnsignedCount())
return m_mesh->m_dV[mesh_vertex_index];
if (vertex_count == m_mesh->m_V.UnsignedCount())
return m_mesh->m_V[mesh_vertex_index];
}
}
ON_ERROR("Invalid topology");
return ON_3dPoint::NanPoint;
}
ON_Line ON_MeshTopology::TopEdgeLine( int tope_index ) const
{
ON_Line L(ON_3dPoint::UnsetPoint,ON_3dPoint::UnsetPoint);
if ( m_mesh && tope_index >= 0 && tope_index < m_tope.Count() )
{
const int* topvi = m_tope[tope_index].m_topvi;
if ( topvi[0] >= 0 && topvi[0] < m_topv.Count()
&& topvi[1] >= 0 && topvi[1] < m_topv.Count() )
{
const ON_MeshTopologyVertex& v0 = m_topv[topvi[0]];
const ON_MeshTopologyVertex& v1 = m_topv[topvi[1]];
if ( v0.m_v_count > 0 && v0.m_vi && v1.m_v_count > 0 && v1.m_vi )
{
int vi0 = v0.m_vi[0];
int vi1 = v1.m_vi[0];
int vcount = m_mesh->m_V.Count();
if ( vi0 >= 0 && vi0 < vcount && vi1 >= 0 && vi1 < vcount )
{
const ON_3dPointListRef vertex_list(m_mesh);
L.from = vertex_list[vi0];
L.to = vertex_list[vi1];
}
}
}
}
return L;
}
////////
// returns index of edge that connects topological vertices
// returns -1 if no edge is found.
int ON_MeshTopology::TopEdge( int vtopi0, int vtopi1 ) const
{
int i0, i1, ei, vi0;
if ( vtopi0 > vtopi1 ) {vi0 = vtopi0; vtopi0 = vtopi1; vtopi1 = vi0;}
if ( vtopi0 < vtopi1 ) {
const int tope_count = TopEdgeCount();
const ON_MeshTopologyEdge* tope = m_tope.Array(); // to speed array access
i0 = 0;
i1 = tope_count;
while ( i0 < i1 )
{
ei = (i0+i1)/2;
vi0 = tope[ei].m_topvi[0];
if ( vi0 < vtopi0 ) {
if ( i0 == ei )
break;
i0 = ei;
}
else if ( vi0 > vtopi0 ) {
if ( i1 == ei )
break;
i1 = ei;
}
else {
while (ei > 0 && tope[ei-1].m_topvi[0] == vtopi0 )
ei--;
while ( ei < tope_count && tope[ei].m_topvi[0] == vtopi0 ) {
if ( tope[ei].m_topvi[1] == vtopi1 )
return ei;
ei++;
}
break;
}
}
}
return -1;
}
bool ON_MeshTopology::GetTopFaceVertices( int fi, int topvi[4] ) const
{
if ( fi >= 0 && fi < m_mesh->m_F.Count() ) {
const int* fvi = m_mesh->m_F[fi].vi;
topvi[0] = m_topv_map[fvi[0]];
topvi[1] = m_topv_map[fvi[1]];
topvi[2] = m_topv_map[fvi[2]];
topvi[3] = m_topv_map[fvi[3]];
}
return true;
}
bool ON_MeshTopology::SortVertexEdges() const
{
bool rc = true;
int topvi, topv_count = m_topv.Count();
for ( topvi = 0; topvi < topv_count; topvi++ )
{
if ( !SortVertexEdges(topvi) )
rc = false;
}
return rc;
}
// TODO make public and add to header file new ON_SortIntArray
static
void ON_ReverseIntArray(
int* e, // array of ints
size_t nel // length of array
)
{
int ei;
size_t i;
nel--;
for ( i = 0; i<nel; i++, nel--)
{
ei = e[i];
e[i] = e[nel];
e[nel] = ei;
}
}
bool ON_MeshTopology::SortVertexEdges(int topvi) const
{
if ( topvi < 0 || topvi >= m_topv.Count() )
return false;
const ON_MeshTopologyVertex& topv = m_topv[topvi];
if ( topv.m_tope_count < 2 )
return true;
// Divide the edges that use this vertex into two sets:
// e1f[] = indices of edges that bound 1 face, 3 or
// more faces, or no faces (in that order).
// e2f[] = indices of edges that bound exactly 2 faces.
int i, j;
int topei;
int vei;
int efcnt;
ON_SimpleArray<int> new_tope_buffer(5*topv.m_tope_count);
new_tope_buffer.SetCount(5*topv.m_tope_count);
int* new_tope = new_tope_buffer.Array();
int* e2f = new_tope + topv.m_tope_count;
int* e1f = e2f + topv.m_tope_count;
int e1fcnt = 0;
int e2fcnt = 0;
{
int* e3f = e1f + topv.m_tope_count; // e3f[] = edges with 3 or more faces
int* e0f = e3f + topv.m_tope_count; // e0f[] = edges with no faces
int e3fcnt = 0;
int e0fcnt = 0;
for ( vei = 0; vei < topv.m_tope_count; vei++ )
{
topei = topv.m_topei[vei];
if ( topei >= 0 && topei < m_tope.Count() )
{
const ON_MeshTopologyEdge& tope = m_tope[topei];
if ( tope.m_topvi[0] == topvi || tope.m_topvi[1] == topvi )
{
efcnt = m_tope[topei].m_topf_count;
if ( efcnt < 0 )
{
ON_ERROR("ON_MeshTopology::SortVertexEdges(int topvi) - m_tope[topei].m_topf_count < 0");
return false;
}
switch(efcnt)
{
case 0: // edge has no faces
// (never happens if topology is from a valid ON_Mesh.)
e0f[e0fcnt++] = topei;
break;
case 1: // edge has exactly one face
e1f[e1fcnt++] = topei;
break;
case 2: // edge has exactly two faces
e2f[e2fcnt++] = topei;
break;
default: // edge has 3 or more faces
// (happens when mesh is non-manifold)
e3f[e3fcnt++] = topei;
break;
}
}
}
}
// append list of non-manifold edges (3 or more faces) to e1f[]
for ( i = 0; i < e3fcnt; i++ )
{
e1f[e1fcnt++] = e3f[i];
}
// append list of wire edges (0 faces) to e1f[]
for ( i = 0; i < e0fcnt; i++ )
{
e1f[e1fcnt++] = e0f[i];
}
e0fcnt = 0;
e3fcnt = 0;
e0f = 0;
e3f = 0;
}
// Put the edge indices in new_tope[] in radial order.
// NOTE: The code below works for non-oriented meshes and
// non-manifold meshes. If you change the code, you
// must make sure that it still works in these cases.
if ( e1fcnt + e2fcnt != topv.m_tope_count )
{
ON_ERROR("ON_MeshTopology::SortVertexEdges() input vertex had bogus m_topei[]");
return false;
}
int fi = -1;
int next_topei = -1;
int efi, fecnt, fei, next_fei;
vei = 0;
int vei0 = 0;
int vei1 = 0;
int elist_dir = 0;
while(vei < topv.m_tope_count)
{
if ( next_topei >= 0 )
{
// continue with this group of edges
topei = next_topei;
next_topei = -1;
}
else if ( e1fcnt > 0 )
{
// start a new group of edges
topei = *e1f++;
e1fcnt--;
vei1 = vei;
}
else if ( e2fcnt > 0 )
{
// start a new group of edges
// (this only happens in non-manifold cases)
topei = *e2f++;
e2fcnt--;
vei1 = vei;
}
else
{
ON_ERROR("ON_MeshTopology::SortVertexEdges() input vertex had bogus topology information.");
return false;
}
if ( vei0 < vei1 )
{
if ( 1 == elist_dir )
{
// 30 December 2003 Dale Lear added this feature
// edges new_tope[vei0],...,new_tope[vei1-1] are radially sorted
// but the order is not counterclockwise with respect to
// the normal of the face attached to the first edge.
// So, this group of edges in new_tope[] needs to
// be reversed.
ON_ReverseIntArray( new_tope+vei0, vei1-vei0 );
}
elist_dir = 0;
vei0 = vei1;
}
new_tope[vei++] = topei;
// search faces connected to tope for the next edge
const ON_MeshTopologyEdge& tope = m_tope[topei];
for ( efi = 0; next_topei < 0 && efi < tope.m_topf_count; efi++ )
{
fi = tope.m_topfi[efi];
if ( fi < 0 || fi >= m_topf.Count() )
{
// bogus face index into m_topf[] array
continue;
}
// find fei so that topf.m_topei[fei] = topei
const ON_MeshTopologyFace& topf = m_topf[fi];
fecnt = topf.IsQuad() ? 4 : 3;
for ( fei = 0; fei < fecnt; fei++ )
{
if ( topf.m_topei[fei] != topei )
continue;
if ( tope.m_topvi[0] == topvi )
{
next_fei = ( topf.m_reve[fei] ) ? 1 : -1;
}
else
{
next_fei = ( topf.m_reve[fei] ) ? -1 : 1;
}
if ( 0 == elist_dir )
elist_dir = next_fei;
next_fei = (fei+next_fei+fecnt)%fecnt;
next_topei = topf.m_topei[next_fei];
// next_topei = candidate for the next edge
// Check to see that it is available by
// finding it in the e1f[] or e2f[] arrays.
j = 0;
for ( i = 0; i < e1fcnt; i++ )
{
if ( next_topei == e1f[i] )
{
// found it in the e1f list.
for ( j = i+1; j < e1fcnt; j++ )
e1f[j-1] = e1f[j];
e1fcnt--;
break;
}
}
if ( 0 == j )
{
// search the e2f[] list
for ( i = 0; i < e2fcnt; i++ )
{
if ( next_topei == e2f[i] )
{
for ( j = i+1; j < e2fcnt; j++ )
e2f[j-1] = e2f[j];
e2fcnt--;
break;
}
}
}
if ( 0 == j )
{
// the candidate was already used, check the next
// face attached to this edge.
next_topei = -1;
}
else
{
break;
}
}
}
}
if ( topv.m_tope_count != vei )
{
ON_ERROR("ON_MeshTopology::SortVertexEdges() edge sorting error.");
return false;
}
vei1 = vei;
if ( vei0 < vei1 )
{
if ( 1 == elist_dir )
{
// 30 December 2003 Dale Lear added this feature
// edges new_tope[vei0],...,new_tope[vei1-1] are radially sorted
// but the order is not counterclockwise with respect to
// the normal of the face attached to the first edge.
// So, this group of edges in new_tope[] needs to
// be reversed.
ON_ReverseIntArray( new_tope+vei0, vei1-vei0 );
}
elist_dir = 0;
vei0 = vei1;
}
int* topv_m_topei = const_cast<int*>(topv.m_topei);
for ( vei = 0; vei < topv.m_tope_count; vei++ )
{
topv_m_topei[vei] = new_tope[vei];
}
return true;
}
int* ON_MeshTopology::GetIntArray(int length)
{
int* a = 0;
if ( length > 0 ) {
struct memchunk* pm;
pm = (struct memchunk*)onmalloc(length*sizeof(*a) + sizeof(*pm));
if ( pm ) {
pm->next = m_memchunk;
m_memchunk = pm++;
a = (int*)pm;
}
}
return a;
}
bool ON_MeshTopology::IsWeldedEdge(int top_ei) const
{
if (top_ei < 0 || top_ei >= m_tope.Count() || nullptr == m_mesh)
return false;
const ON_MeshTopologyEdge& e = m_tope[top_ei];
if (e.m_topf_count <= 1 || e.m_topvi[0] < 0 || e.m_topvi[1] < 0)
return false;
const int face_count = m_topf.Count();
if (face_count < 2 || face_count != m_mesh->FaceCount())
return false;
if (e.m_topvi[0] < 0 || e.m_topvi[1] < 0 || e.m_topvi[0] == e.m_topvi[1] )
return false;
const int topv_count = m_topv.Count();
if (e.m_topvi[0] >= topv_count || e.m_topvi[1] >= topv_count || topv_count < 3)
return false;
const int meshv_count = m_mesh->VertexCount();
if (meshv_count < topv_count || meshv_count != m_topv_map.Count())
return false;
if (1 == m_topv[e.m_topvi[0]].m_v_count && 1 == m_topv[e.m_topvi[0]].m_v_count)
return true;
// need to examine faces
int mesh_vi[2] = { -1,-1 };
for (int efi = 0; efi < e.m_topf_count; ++efi)
{
int top_fi = e.m_topfi[efi];
if (top_fi < 0 || top_fi >= face_count)
return false;
const ON_MeshTopologyFace& f = m_topf[top_fi];
const int fe_count = f.IsTriangle() ? 3 : 4;
int fvi[2] = { -1, -1 };
for (int fei = 0; fei < fe_count; ++fei)
{
if (top_ei != f.m_topei[fei])
continue;
const ON_MeshFace& mf = m_mesh->m_F[top_fi];
const bool bRev = f.m_reve[fei];
fvi[bRev?1:0] = mf.vi[(fei + fe_count - 1) % fe_count];
fvi[bRev?0:1] = mf.vi[fei];
if (fvi[0] < 0 || fvi[0] >= meshv_count)
return false;
if (fvi[1] < 0 || fvi[1] >= meshv_count)
return false;
if (m_topv_map[fvi[0]] != e.m_topvi[0] || m_topv_map[fvi[1]] != e.m_topvi[1])
{
ON_ERROR("Bug in this loop or bad mesh topology.");
fvi[0] = -1;
fvi[1] = -1;
continue;
}
break;
}
if (0 == efi)
{
if (fvi[0] < 0 || fvi[1] < 0 || fvi[0] == fvi[1])
return false;
mesh_vi[0] = fvi[0];
mesh_vi[1] = fvi[1];
}
else
{
if (mesh_vi[0] != fvi[0])
return false;
if (mesh_vi[1] != fvi[1])
return false;
}
}
return (mesh_vi[0] >= 0 && mesh_vi[1]);
}
bool ON_MeshTopologyFace::IsTriangle() const
{
return ( m_topei[2] == m_topei[3] && m_topei[0] != m_topei[1] )
? true : false;
}
bool ON_MeshTopologyFace::IsQuad() const
{
return ( m_topei[2] != m_topei[3] ) ? true : false;
}
bool ON_MeshTopologyFace::IsValid() const
{
return ( m_topei[0] != m_topei[1] ) ? true : false;
}
bool ON_MeshTopology::IsValid() const
{
ON_Workspace ws;
int topvi, topei, topfi, vi, fi, j, jmax, k, tfvi[4];
ON_3fPoint p;
WaitUntilReady(0);
// simple checks
if ( 1 != m_b32IsValid )
return false;
if ( !m_mesh )
return false;
if ( this != &(m_mesh->Topology()) )
return false;
const int v_count = m_mesh->VertexCount();
const int f_count = m_mesh->FaceCount();
const int topv_count = TopVertexCount();
const int tope_count = TopEdgeCount();
const int topf_count = TopFaceCount();
if ( topv_count > v_count || topv_count < 0 )
return false;
if ( topv_count == 0 && v_count > 0 )
return false;
if ( topf_count != f_count )
return false;
if ( f_count > 0 && tope_count < 3 )
return false;
if ( m_topv_map.Count() != v_count )
return false;
// check vertex information
for ( vi = 0; vi < v_count; vi++ ) {
topvi = m_topv_map[vi];
if ( topvi < 0 || topvi >= topv_count )
return false;
}
char* vCheck = (char*)ws.GetMemory( v_count*sizeof(*vCheck) );
memset( vCheck, 0, v_count*sizeof(*vCheck) );
for ( topvi = 0; topvi < topv_count; topvi++ )
{
const ON_MeshTopologyVertex& topv = m_topv[topvi];
if ( topv.m_v_count <= 0 )
return false;
if ( !topv.m_vi )
return false;
p = TopVertexPoint(topvi);
for ( j = 0; j < topv.m_v_count; j++ ) {
vi = topv.m_vi[j];
if ( vi < 0 )
return false;
if ( vi >= v_count )
return false;
if ( vCheck[vi] != 0 )
return false; // mesh.m_V[vi] is referenced multiple times
if ( compare3fPoint( &p, &m_mesh->m_V[vi] ) )
return false; // mesh.m_V[vi] not at same location as topv
if ( m_topv_map[vi] != topvi )
return false;
vCheck[vi] = 1;
}
// check that edges in m_topei[] list have topvi has a start/end
if ( topv.m_tope_count < 0 )
return false;
if ( topv.m_tope_count == 0 && topv.m_topei )
return false; // array should be nullptr
if ( topv.m_tope_count > 0 && !topv.m_topei )
return false; // array should not be nullptr
for ( j = 0; j < topv.m_tope_count; j++ ) {
topei = topv.m_topei[j];
if ( topei < 0 )
return false;
if ( topei >= tope_count )
return false;
const ON_MeshTopologyEdge& tope = m_tope[topei];
if ( tope.m_topvi[0] != topvi && tope.m_topvi[1] != topvi )
return false; // edge doesn't reference this top vtx
for ( k = 0; k < j; k++ ) {
if ( topv.m_topei[k] == topei )
return false; // edge listed multiple times
}
}
}
// make sure every mesh.m_V[] maps to a topoligical vertex
for ( vi = 0; vi < v_count; vi++ ) {
if ( vCheck[vi] != 1 )
return false; // mesh.m_V[vi] is not referenced
topvi = m_topv_map[vi];
if ( topvi < 0 )
return false;
if ( topvi >= topv_count )
return false;
const ON_MeshTopologyVertex& topv = m_topv[topvi];
for ( j = 0; j < topv.m_v_count; j++ ) {
if ( topv.m_vi[j] == vi )
break;
}
if ( j >= topv.m_v_count )
return false; // bogus m_topv_map[] array
}
// check edges
for ( topei = 0; topei < tope_count; topei++ ) {
const ON_MeshTopologyEdge& tope = m_tope[topei];
if ( tope.m_topvi[0] < 0 || tope.m_topvi[0] >= topv_count )
return false;
if ( tope.m_topvi[1] < 0 || tope.m_topvi[1] >= topv_count )
return false;
if ( tope.m_topvi[0] == tope.m_topvi[1] )
return false;
const ON_MeshTopologyVertex& topv0 = m_topv[tope.m_topvi[0]];
for ( j = 0; j < topv0.m_tope_count; j++ ) {
if ( topv0.m_topei[j] == topei )
break;
}
if ( j >= topv0.m_tope_count )
return false; // start vtx not linked to edge
const ON_MeshTopologyVertex& topv1 = m_topv[tope.m_topvi[1]];
for ( j = 0; j < topv1.m_tope_count; j++ ) {
if ( topv1.m_topei[j] == topei )
break;
}
if ( j >= topv1.m_tope_count )
return false; // end vtx not linked to edge
if ( tope.m_topf_count < 0 )
return false;
if ( tope.m_topf_count == 0 && tope.m_topfi )
return false; // array should be nullptr
if ( tope.m_topf_count > 0 && !tope.m_topfi )
return false; // array should not be nullptr
for ( j = 0; j < tope.m_topf_count; j++ ) {
fi = tope.m_topfi[j];
if ( fi < 0 || fi >= f_count )
return false;
const ON_MeshTopologyFace& topf = m_topf[fi];
for ( k = 0; k < 4; k++ ) {
if ( topf.m_topei[k] == topei )
break;
}
if ( k >= 4 )
return false; // edge not in face's list
}
}
// check faces
for ( fi = 0; fi < f_count; fi++ ) {
topfi = fi;
const ON_MeshTopologyFace& topf = m_topf[topfi];
const ON_MeshFace& f = m_mesh->m_F[fi];
if ( topf.m_topei[0] < 0 || topf.m_topei[0] >= tope_count )
return false;
if ( topf.m_topei[1] < 0 || topf.m_topei[1] >= tope_count )
return false;
if ( topf.m_topei[2] < 0 || topf.m_topei[2] >= tope_count )
return false;
if ( topf.m_topei[3] < 0 || topf.m_topei[3] >= tope_count )
return false;
tfvi[0] = m_topv_map[f.vi[0]];
tfvi[1] = m_topv_map[f.vi[1]];
tfvi[2] = m_topv_map[f.vi[2]];
tfvi[3] = m_topv_map[f.vi[3]];
if ( topf.m_topei[0] != 0 || topf.m_topei[1] != 0
|| topf.m_topei[2] != 0 || topf.m_topei[3] != 0 ) {
if ( !f.IsValid(v_count) )
return false;
if ( f.IsTriangle() ) {
if (topf.m_topei[2] != topf.m_topei[3] )
return false;
jmax = 3;
}
else {
if (topf.m_topei[2] == topf.m_topei[3] )
return false;
jmax = 4;
}
for ( j = 0; j < jmax; j++ ) {
const ON_MeshTopologyEdge& tope = m_tope[topf.m_topei[j]];
for ( k = 0; k < tope.m_topf_count; k++ ) {
if ( tope.m_topfi[k] == topfi )
break;
}
if ( k >= tope.m_topf_count )
return false;
// topedge m_tope[topf.m_topei[j]] ENDS at topvtx m_topv[tfvi[j]]
if ( topf.m_reve[j] ) {
if ( tope.m_topvi[1] != tfvi[(j+3)%4] )
return false;
if ( tope.m_topvi[0] != tfvi[j] )
return false;
}
else {
if ( tope.m_topvi[0] != tfvi[(j+3)%4] )
return false;
if ( tope.m_topvi[1] != tfvi[j] )
return false;
}
}
}
else {
// all topf.m_topei[] must be zeros
if ( topf.m_topei[0] != 0 || topf.m_topei[1] != 0
|| topf.m_topei[2] != 0 || topf.m_topei[3] != 0 )
return false;
}
}
return true;
}
void ON_MeshTopology::Dump( ON_TextLog& dump ) const
{
ON_3fPoint p;
int vi, ei, fi, j;
const int topv_count = m_topv.Count();
const int tope_count = m_tope.Count();
const int topf_count = m_topf.Count();
// topological vertex information
for ( vi = 0; vi < topv_count; vi++ ) {
const ON_MeshTopologyVertex& v = m_topv[vi];
dump.Print("topv %d: ",vi);
if (m_mesh) {
// dump geometric location of this vertex
p = m_mesh->m_V[v.m_vi[0]];
dump.Print("{%g,%g,%g} ", p.x, p.y, p.z);
}
// list all mesh geometry viertices that are coincident with this
// topological vertex
dump.Print("(");
for ( j = 0; j < v.m_v_count; j++ ) {
if ( j )
dump.Print(",");
dump.Print("m_V[%d]",v.m_vi[j]);
}
// list all toplogical edges that begin/end at this topological vertex
dump.Print(") (");
for ( j = 0; j < v.m_tope_count; j++ ) {
if ( j )
dump.Print(",");
dump.Print("%d",v.m_topei[j]);
}
dump.Print(")\n");
}
// topological edge information
for ( ei = 0; ei < tope_count; ei++ ) {
const ON_MeshTopologyEdge& e = m_tope[ei];
dump.Print("tope %d: topv%d to topvv%d (", ei, e.m_topvi[0], e.m_topvi[1] );
// list all mesh topolical faces attached to this topolical edge
for ( j = 0; j < e.m_topf_count; j++ ) {
if (j)
dump.Print(",");
dump.Print("f%d",e.m_topfi[j]);
}
dump.Print(")\n");
}
// topological face information
// mesh m_F[] index = mesh topology m_topf[] index
for ( fi = 0; fi < topf_count; fi++ ) {
const ON_MeshTopologyFace& f = m_topf[fi];
dump.Print("topf %d: (",fi);
for ( j = 0; j < 4; j++ ) {
if ( j == 3 && f.m_topei[3] == f.m_topei[2] )
break; // triangular face
if (j)
dump.Print(",");
dump.Print("%ce%d",f.m_reve[j]?'-':'+',f.m_topei[j]);
}
dump.Print(")\n");
}
}
bool ON_MeshTopology::Create()
{
// When -1 == m_b32IsValid, this ON_MeshTopology
// is the m_top field on an ON_Mesh and is being
// created in an ON_Mesh::Topology() call and a
// sleep lock is used to keep ON_Mesh::Topology()
// thread safe.
//
// When 0 == m_b32IsValid, this ON_MeshTopology
// is being created stand alone.
//
// When 1 == m_b32IsValid, this ON_MeshTopology
// is already created and valid.
if ( 1 == m_b32IsValid )
return true;
const int b32IsValid0 = m_b32IsValid;
if ( 0 == b32IsValid0 )
{
// no sleep lock is being used
m_b32IsValid = -1;
}
int b32IsValid = b32IsValid0;
while ( 0 == b32IsValid || -1 == b32IsValid )
{
// while() is for flow control - this is a while() {... break;} statment.
Destroy();
b32IsValid = 0;
// build vertex topology information
const int fcount = m_mesh->FaceCount();
const int vcount = m_mesh->VertexCount();
if ( 0 == vcount )
break;
unsigned int* vindex = (unsigned int*)GetIntArray(vcount);
m_topv_map.SetCapacity( vcount );
m_topv_map.SetCount( vcount );
if ( 0 == m_mesh->GetVertexLocationIds(
0, // first id
(unsigned int*)m_topv_map.Array(), // vertex ids returned here
vindex
) )
{
Destroy();
break;
}
{
int topv_count = m_topv_map[vindex[vcount-1]]+1;
m_topv_map.SetCapacity( vcount );
m_topv_map.SetCount( vcount );
m_topv.SetCapacity( topv_count );
int vt0, vt1, topvi;
for (vt0 = 0; vt0 < vcount; vt0 = vt1)
{
topvi = m_topv.Count();
#if defined(ON_DEBUG)
if ( topvi != m_topv_map[vindex[vt0]] )
{
// 20 April 2010 Dale Lear:
// If you get this error, save the mesh and tell Dale Lear.
ON_ERROR("ON_MeshTopology::Create() - topvi != vertex id from GetVertexLocationIds()");
}
#endif
ON_MeshTopologyVertex& topv = m_topv.AppendNew();
topv.m_vi = (const int*)(vindex+vt0);
for ( vt1=vt0+1; vt1<vcount && topvi == m_topv_map[vindex[vt1]]; vt1++ ) {
// empty
}
topv.m_v_count = vt1-vt0;
}
}
// build edge topology information
const int topv_count = m_topv.Count();
if ( topv_count >= 2 )
{
bool rc = false;
int ei, ecnt, vi0, vi1, fi, efi, topfvi[4];
ON_MeshFace f;
if ( fcount > 0 && vcount > 0 )
{
// When working on this code be sure to test bug# 9271 and 9254 and file fsv_r4.3dm
ON_Workspace ws;
ON_MeshFaceSide* e = (ON_MeshFaceSide*)ws.GetMemory( 4*fcount*sizeof(*e) );
ecnt = m_mesh->GetMeshFaceSideList( (const unsigned int*)m_topv_map.Array(), e );
if ( ecnt > 0 )
{
rc = true;
ON_MeshFaceSide::SortByVertexIndex( e, ecnt );
// count number of topological edges and allocate storage
int etop_count = 0;
ei = 0;
while( ei < ecnt )
{
vi0 = e[ei].m_vi[0];
vi1 = e[ei].m_vi[1];
ei++;
while (ei < ecnt && e[ei].m_vi[0] == (unsigned int)vi0 && e[ei].m_vi[1] == (unsigned int)vi1 )
ei++;
etop_count++;
}
m_tope.SetCapacity(etop_count);
// fill in the m_tope[] array information
int* efindex = GetIntArray(ecnt);
for ( ei = 0; ei < ecnt; /*empty*/ )
{
ON_MeshTopologyEdge& tope = m_tope.AppendNew();
tope.m_topvi[0] = (int)(vi0 = e[ei].m_vi[0]);
tope.m_topvi[1] = (int)(vi1 = e[ei].m_vi[1]);
tope.m_topfi = efindex;
tope.m_topf_count = 0;
*efindex++ = (int)e[ei].m_fi;
tope.m_topf_count++;
ei++;
while( ei < ecnt && e[ei].m_vi[0] == (unsigned int)vi0 && e[ei].m_vi[1] == (unsigned int)vi1 )
{
*efindex++ = (int)e[ei].m_fi;
tope.m_topf_count++;
ei++;
}
}
efindex = 0; // memory deallocated by ~ON_MeshTopology()
// connect vertices to edges;
ecnt = m_tope.Count();
int* ve_count = (int*)onmalloc(topv_count*sizeof(*ve_count));
// set ve_count[topvi] = number of edges that begin or end at m_topv[topvi]
memset( ve_count, 0, topv_count*sizeof(*ve_count));
for ( ei = 0; ei < ecnt; ei++ ) {
const ON_MeshTopologyEdge& tope = m_tope[ei];
ve_count[tope.m_topvi[0]]++;
ve_count[tope.m_topvi[1]]++;
}
// allocate and distribute storage for the mopv.m_topei[] array
int* vei = GetIntArray(2*ecnt);
for ( vi0 = 0; vi0 < topv_count; vi0++ ) {
ON_MeshTopologyVertex& topv = m_topv[vi0];
if ( ve_count[vi0] > 0 ) {
topv.m_topei = vei;
vei += ve_count[vi0];
}
}
onfree(ve_count); ve_count = 0;
vei = 0; // memory deallocated by ~ON_MeshTopology()
// fill in the m_topv[].m_topei[] values
for ( ei = 0; ei < ecnt; ei++ ) {
ON_MeshTopologyEdge& tope = m_tope[ei];
ON_MeshTopologyVertex& topv0 = m_topv[tope.m_topvi[0]];
ON_MeshTopologyVertex& topv1 = m_topv[tope.m_topvi[1]];
vei = const_cast<int*>(topv0.m_topei);
vei[topv0.m_tope_count++] = ei;
vei = const_cast<int*>(topv1.m_topei);
vei[topv1.m_tope_count++] = ei;
}
// build face topology information
m_topf.SetCapacity(fcount);
m_topf.SetCount(fcount);
memset( m_topf.Array(), 0, fcount*sizeof(ON_MeshTopologyFace) );
for ( fi = 0; fi < fcount; fi++ ) {
ON_MeshTopologyFace& f_local = m_topf[fi];
f_local.m_topei[0] = -1;
f_local.m_topei[1] = -1;
f_local.m_topei[2] = -1;
f_local.m_topei[3] = -1;
}
for ( ei = 0; ei < ecnt; ei++ ) {
ON_MeshTopologyEdge& tope = m_tope[ei];
for (efi = 0; efi < tope.m_topf_count; efi++ ) {
// Because ON_MeshFace.vi[2] == ON_MeshFace.vi[3] for triangles,
// we have topf.m_topei[j] BEGIN at ON_MeshFace.vi[(j+3)%4] and END at ON_MeshFace.vi[j]
fi = tope.m_topfi[efi];
f = m_mesh->m_F[fi];
topfvi[0] = m_topv_map[f.vi[0]];
topfvi[1] = m_topv_map[f.vi[1]];
topfvi[2] = m_topv_map[f.vi[2]];
topfvi[3] = m_topv_map[f.vi[3]];
ON_MeshTopologyFace& topf = m_topf[fi];
vi0 = tope.m_topvi[0];
vi1 = tope.m_topvi[1];
// unroll loop for speed
if ( vi0 == topfvi[3] && vi1 == topfvi[0] ) {
topf.m_topei[0] = ei;
topf.m_reve[0] = 0;
}
else if ( vi0 == topfvi[0] && vi1 == topfvi[1] ) {
topf.m_topei[1] = ei;
topf.m_reve[1] = 0;
}
else if ( vi0 == topfvi[1] && vi1 == topfvi[2] ) {
topf.m_topei[2] = ei;
topf.m_reve[2] = 0;
}
else if ( vi0 == topfvi[2] && vi1 == topfvi[3] ) {
topf.m_topei[3] = ei;
topf.m_reve[3] = 0;
}
else if ( vi1 == topfvi[3] && vi0 == topfvi[0] ) {
topf.m_topei[0] = ei;
topf.m_reve[0] = 1;
}
else if ( vi1 == topfvi[0] && vi0 == topfvi[1] ) {
topf.m_topei[1] = ei;
topf.m_reve[1] = 1;
}
else if ( vi1 == topfvi[1] && vi0 == topfvi[2] ) {
topf.m_topei[2] = ei;
topf.m_reve[2] = 1;
}
else if ( vi1 == topfvi[2] && vi0 == topfvi[3] ) {
topf.m_topei[3] = ei;
topf.m_reve[3] = 1;
}
}
}
for ( fi = 0; fi < fcount; fi++ )
{
ON_MeshTopologyFace& f_local = m_topf[fi];
bool bIsGood = false;
if ( f_local.m_topei[0] >= 0 && f_local.m_topei[1] >= 0 && f_local.m_topei[2] >=0
&& f_local.m_topei[0] != f_local.m_topei[1]
&& f_local.m_topei[1] != f_local.m_topei[2]
&& f_local.m_topei[2] != f_local.m_topei[0]
) {
if ( m_mesh->m_F[fi].IsTriangle() ) {
bIsGood = true;
f_local.m_topei[3] = f_local.m_topei[2];
}
else if ( f_local.m_topei[3] >= 0
&& f_local.m_topei[0] != f_local.m_topei[3]
&& f_local.m_topei[1] != f_local.m_topei[3]
&& f_local.m_topei[2] != f_local.m_topei[3] ) {
bIsGood = true;
}
}
if ( !bIsGood ) {
f_local.m_topei[0] = 0;
f_local.m_topei[1] = 0;
f_local.m_topei[2] = 0;
f_local.m_topei[3] = 0;
f_local.m_reve[0] = 0;
f_local.m_reve[1] = 0;
f_local.m_reve[2] = 0;
f_local.m_reve[3] = 0;
}
}
}
}
}
b32IsValid = 1;
break;
}
if ( -1 != b32IsValid0 )
{
// no sleep lock is in use
m_b32IsValid = b32IsValid;
}
if ( 1 != b32IsValid )
{
Destroy();
}
return (1 == b32IsValid);
}
struct tagFPT
{
int x, y, z;
};
//static int compare_fpt( const struct tagFPT* a, const struct tagFPT* b )
//{
// if ( a->x < b->x )
// return -1;
// if ( a->x > b->x )
// return 1;
// if ( a->y < b->y )
// return -1;
// if ( a->y > b->y )
// return 1;
// if ( a->z < b->z )
// return -1;
// if ( a->z > b->z )
// return 1;
// return 0;
//}
static int compare_pmark( const int* a, const int* b )
{
if ( *a < *b )
return -1;
if ( *a > *b )
return 1;
if ( a < b )
return -1;
if ( a > b )
return 1;
return 0;
}
static int compare_vmap( const void* a, const void* b )
{
int i = *((int*)a);
int j = *((int*)b);
if ( i < j )
return -1;
if ( i > j )
return 1;
return 0;
}
static int DupVertex( ON_Mesh* mesh, int vi )
{
int vertex_count = mesh->m_V.Count();
ON_3fVector n;
ON_3fPoint v;
ON_Color c;
ON_2fPoint t;
ON_SurfaceCurvature k;
v = mesh->m_V[vi];
mesh->m_V.Append(v);
if (mesh->m_N.Count() == vertex_count) {
n = mesh->m_N[vi];
mesh->m_N.Append(n);
}
if (mesh->m_T.Count() == vertex_count) {
t = mesh->m_T[vi];
mesh->m_T.Append(t);
}
if (mesh->m_K.Count() == vertex_count) {
k = mesh->m_K[vi];
mesh->m_K.Append(k);
}
if (mesh->m_C.Count() == vertex_count) {
c = mesh->m_C[vi];
mesh->m_C.Append(c);
}
return vertex_count;
}
static int AddToPartition( ON_Mesh* mesh, ON_SimpleArray<int>& pmark, int vi, int partition_mark, int fi0 )
{
bool b = true;
int i, fi, new_vi, face_count, *fvi;
i = pmark[vi];
if ( !i ) {
pmark[vi] = partition_mark;
}
else if ( i != partition_mark && i != partition_mark-1 ) {
if ( i == partition_mark-2 )
pmark[vi] = partition_mark-1; // vertex vi shared between two partitions
else {
new_vi = DupVertex(mesh,vi);
face_count = mesh->m_F.Count();
for ( fi = fi0; fi < face_count; fi++ ) {
fvi = mesh->m_F[fi].vi;
if ( fvi[0] == vi )
fvi[0] = new_vi;
if ( fvi[1] == vi )
fvi[1] = new_vi;
if ( fvi[2] == vi )
fvi[2] = new_vi;
if ( fvi[3] == vi )
fvi[3] = new_vi;
}
pmark.Append(partition_mark);
}
}
else
b = false; // vertex already in this partition
return b;
}
bool ON_MeshPartition_IsValid( const ON_MeshPartition& p, const ON_Mesh& mesh )
{
bool rc = false;
const int* fvi;
int j, tcnt, fi, parti, partcount;
partcount = p.m_part.Count();
rc = ( partcount > 0 );
if ( p.m_partition_max_triangle_count < 1 )
rc = false;
if ( p.m_partition_max_vertex_count < 3 )
rc = false;
for ( parti = 0; parti < partcount && rc; parti++ ) {
const ON_MeshPart& part = p.m_part[parti];
if ( part.triangle_count < 1 )
rc = false;
if ( part.vertex_count < 1 )
rc = false;
if ( part.vertex_count != part.vi[1] - part.vi[0] )
rc = false;
tcnt = 0;
for ( fi = part.fi[0]; fi < part.fi[1]; fi++ ) {
fvi = mesh.m_F[fi].vi;
tcnt++;
if ( fvi[2] != fvi[3] )
tcnt++;
for ( j = 0; j < 4; j++ ) {
if ( fvi[j] < part.vi[0] || fvi[j] >= part.vi[1] )
rc = false;
}
}
if ( tcnt != part.triangle_count )
rc = false;
if ( parti ) {
if ( part.fi[0] != p.m_part[parti-1].fi[1] )
rc = false;
if ( part.vi[0] > p.m_part[parti-1].vi[1] )
rc = false;
}
}
if ( partcount ) {
if ( p.m_part[0].fi[0] != 0 || p.m_part[partcount-1].fi[1] != mesh.m_F.Count() )
rc = false;
}
return rc;
}
static
bool ON_Mesh_CreatePartition_SortFaces(const ON_Mesh& mesh, int* fmap )
{
ON_RTree rt;
if ( !rt.CreateMeshFaceTree(&mesh) )
return false;
const int mesh_F_count = mesh.m_F.Count();
int fmap_count = 0;
const ON_RTreeBranch* branch;
ON_RTreeIterator rit(rt);
for ( rit.First(); 0 != (branch = rit.Value()); rit.Next() )
{
if ( fmap_count > mesh_F_count )
break;
fmap[fmap_count++] = (int)(branch->m_id);
}
if ( fmap_count != mesh_F_count )
{
ON_ERROR("ON_Mesh::CreatePartition unable to get fmap[]");
return false;
}
return true;
}
const ON_MeshPartition* ON_Mesh::CreatePartition(
int partition_max_vertex_count, // maximum number of vertices in a partition
int partition_max_triangle_count // maximum number of triangles in a partition
)
{
ON_Workspace ws;
bool bNeedFaceSort = true;
if ( m_partition )
{
bNeedFaceSort = false;
if ( m_partition->m_partition_max_triangle_count > partition_max_triangle_count
|| m_partition->m_partition_max_vertex_count > partition_max_vertex_count )
DestroyPartition();
}
if ( !m_partition )
{
// TODO: create one
struct ON_MeshPart p;
int vertex_count = VertexCount();
const int face_count = FaceCount();
const int triangle_count = TriangleCount() + 2*QuadCount();
m_partition = new ON_MeshPartition();
int k = triangle_count/partition_max_triangle_count;
if ( k < vertex_count/partition_max_vertex_count )
k = vertex_count/partition_max_vertex_count;
k++;
m_partition->m_part.Reserve(k);
if ( vertex_count <= partition_max_vertex_count &&
triangle_count <= partition_max_triangle_count )
{
m_partition->m_partition_max_vertex_count = vertex_count;
m_partition->m_partition_max_triangle_count = triangle_count;
memset(&p,0,sizeof(p));
p.vi[0] = 0;
p.vi[1] = vertex_count;
p.fi[0] = 0;
p.fi[1] = face_count;
p.vertex_count = vertex_count;
p.triangle_count = triangle_count;
m_partition->m_part.Append(p);
}
else
{
int fi;
int* fvi;
DestroyTopology();
// sort faces
int* fmap = (int*)ws.GetMemory( face_count*sizeof(fmap[0]) );
if ( !ON_Mesh_CreatePartition_SortFaces(*this,fmap) )
{
for ( fi = 0; fi < face_count; fi++ )
fmap[fi] = fi;
}
//ON_SimpleArray<struct tagFPT> fpt(face_count);
//fpt.SetCount(face_count);
//if ( HasTextureCoordinates() )
//{
// ON_2fPoint fcenter;
// ON_BoundingBox bbox = ON_PointListBoundingBox(2,0,m_T.Count(),2,&m_T[0].x);
// const ON_Interval txdom(bbox.m_min.x,bbox.m_max.x);
// const ON_Interval tydom(bbox.m_min.y,bbox.m_max.y);
// for ( fi = 0; fi < face_count; fi++ ) {
// fvi = m_F[fi].vi;
// if ( fvi[2] == fvi[3] ) {
// fcenter = 0.333333333333333333f*(m_T[fvi[0]] + m_T[fvi[1]] + m_T[fvi[2]]);
// }
// else {
// fcenter = 0.25f*(m_T[fvi[0]] + m_T[fvi[1]] + m_T[fvi[2]] + m_T[fvi[3]]);
// }
// fpt[fi].x = (int)floor(txdom.NormalizedParameterAt(fcenter.x)*100);
// fpt[fi].y = (int)floor(tydom.NormalizedParameterAt(fcenter.y)*100);
// fpt[fi].z = 0;
// }
//}
//else
//{
// ON_3dPoint fcenter;
// ON_BoundingBox bbox = BoundingBox();
// const ON_Interval vxdom(bbox.m_min.x,bbox.m_max.x);
// const ON_Interval vydom(bbox.m_min.y,bbox.m_max.y);
// const ON_Interval vzdom(bbox.m_min.z,bbox.m_max.z);
// for ( fi = 0; fi < face_count; fi++ ) {
// fvi = m_F[fi].vi;
// if ( fvi[2] == fvi[3] ) {
// fcenter = 0.333333333333333333f*(m_V[fvi[0]] + m_V[fvi[1]] + m_V[fvi[2]]);
// }
// else {
// fcenter = 0.25f*(m_V[fvi[0]] + m_V[fvi[1]] + m_V[fvi[2]] + m_V[fvi[3]]);
// }
// fpt[fi].x = (int)floor(vxdom.NormalizedParameterAt(fcenter.x)*100);
// fpt[fi].y = (int)floor(vydom.NormalizedParameterAt(fcenter.y)*100);
// fpt[fi].z = (int)floor(vzdom.NormalizedParameterAt(fcenter.z)*100);
// }
//}
//fpt.Sort( ON::sort_algorithm::quick_sort, fmap, compare_fpt );
m_F.Permute( fmap );
if ( m_FN.Count() == face_count )
m_FN.Permute( fmap );
// sort vertices
ON_SimpleArray<int>pmark(2*vertex_count);
pmark.SetCount(vertex_count);
pmark.Zero();
int fi0, fi1, partition_mark, partition_vertex_count, partition_triangle_count;
fi1 = 0;
fi0 = 0;
for ( partition_mark = 3, fi0 = 0; fi0 < face_count; fi0 = fi1, partition_mark += 2 )
{
partition_vertex_count = 0;
partition_triangle_count = 0;
for ( fi1 = fi0;
fi1 < face_count
&& partition_triangle_count+2 <= partition_max_triangle_count
&& partition_vertex_count+4 <= partition_max_vertex_count;
fi1++ )
{
fvi = m_F[fi1].vi;
partition_triangle_count++;
if ( AddToPartition( this, pmark, fvi[0], partition_mark, fi0 ) )
partition_vertex_count++;
if ( AddToPartition( this, pmark, fvi[1], partition_mark, fi0 ) )
partition_vertex_count++;
if ( AddToPartition( this, pmark, fvi[2], partition_mark, fi0 ) )
partition_vertex_count++;
if ( fvi[2] != fvi[3] ) {
partition_triangle_count++; // quads = 2 triangles
if ( AddToPartition( this, pmark, fvi[3], partition_mark, fi0 ) )
partition_vertex_count++;
}
}
if ( fi0 < fi1 ) {
struct ON_MeshPart p_local;
memset(&p_local,0,sizeof(p_local));
p_local.fi[0] = fi0;
p_local.fi[1] = fi1;
p_local.vertex_count = partition_vertex_count;
p_local.triangle_count = partition_triangle_count;
m_partition->m_part.Append(p_local);
}
if ( partition_triangle_count > m_partition->m_partition_max_triangle_count )
m_partition->m_partition_max_triangle_count = partition_triangle_count;
if ( partition_vertex_count > m_partition->m_partition_max_vertex_count )
m_partition->m_partition_max_vertex_count = partition_vertex_count;
}
// the calls to AddToPartition() may have increased vertex count
vertex_count = m_V.Count();
// sort vertices
int* vmap = (int*)ws.GetMemory( vertex_count*sizeof(vmap[0]) );
pmark.Sort( ON::sort_algorithm::quick_sort, vmap, compare_pmark );
m_V.Permute( vmap );
if ( m_N.Count() == vertex_count )
m_N.Permute( vmap );
if ( m_T.Count() == vertex_count )
m_T.Permute( vmap );
if ( m_K.Count() == vertex_count )
m_K.Permute( vmap );
if ( m_C.Count() == vertex_count )
m_C.Permute( vmap );
pmark.Permute( vmap );
// pamv[] = inverse of mapv permutation
int* pamv = (int*)ws.GetMemory( vertex_count*sizeof(pamv[0]) );
ON_Sort( ON::sort_algorithm::quick_sort, pamv, vmap, vertex_count, sizeof(vmap[0]), compare_vmap );
for ( fi = 0; fi < face_count; fi++ ) {
fvi = m_F[fi].vi;
fvi[0] = pamv[fvi[0]];
fvi[1] = pamv[fvi[1]];
fvi[2] = pamv[fvi[2]];
fvi[3] = pamv[fvi[3]];
}
// fill in m_part.vi[]
int m, pi, partition_count = m_partition->m_part.Count();
int vi0, vi1, vi2, vi3;
for (vi2 = 0; vi2 < vertex_count && pmark[vi2]<2; vi2++)
{/*empty for body*/}
vi3=vi2;
for ( pi = 0; pi < partition_count; pi++ ) {
vi0 = vi2;
vi1 = vi3;
m = 2*pi + 4;
for ( vi2 = vi3; vi2 < vertex_count && pmark[vi2] < m; vi2++)
{/*empty for body*/}
for ( vi3 = vi2; vi3 < vertex_count && pmark[vi3] <= m; vi3++)
{/*empty for body*/}
m_partition->m_part[pi].vi[0] = vi0;
m_partition->m_part[pi].vi[1] = vi3;
}
}
// debugging - test partition
if ( m_partition && !ON_MeshPartition_IsValid( *m_partition, *this ) ) {
delete m_partition;
m_partition = 0;
}
}
return m_partition;
}
const ON_MeshPartition* ON_Mesh::Partition() const
{
return m_partition;
}
void ON_Mesh::DestroyPartition()
{
if ( m_partition ) {
delete m_partition;
m_partition = 0;
}
}
ON_MeshPartition::ON_MeshPartition()
{
m_partition_max_vertex_count = 0;
m_partition_max_triangle_count = 0;
m_part = 0;
}
ON_MeshPartition::~ON_MeshPartition()
{
m_part.Destroy();
}
ON_Mesh* ON_Mesh::MeshPart(
const ON_MeshPart& mesh_part,
ON_Mesh* mesh
) const
{
if ( this == mesh )
{
ON_ERROR("ON_Mesh::MeshPart this == mesh");
return 0;
}
if ( mesh )
mesh->Destroy();
if ( mesh_part.fi[0] < 0
|| mesh_part.fi[1] > m_F.Count()
|| mesh_part.fi[0] > mesh_part.fi[1]
)
{
ON_ERROR("ON_Mesh::MeshPart mesh_part.fi[] is not valid");
return 0;
}
if ( mesh_part.vi[0] < 0
|| mesh_part.vi[1] > m_V.Count()
|| mesh_part.vi[0] >= mesh_part.vi[1]
)
{
ON_ERROR("ON_Mesh::MeshPart mesh_part.vi[] is not valid");
return 0;
}
const int submesh_V_count = mesh_part.vi[1] - mesh_part.vi[0];
const int submesh_F_count = mesh_part.fi[1] - mesh_part.fi[0];
const bool bHasVertexNormals = HasVertexNormals();
const bool bHasTextureCoordinates = HasTextureCoordinates();
const bool bHasVertexColors = HasVertexColors();
const bool bHasFaceNormals = HasFaceNormals();
const bool bHasSurfaceParameters = HasSurfaceParameters();
const bool bHasPrincipalCurvatures = HasPrincipalCurvatures();
const bool bHasHiddenVertices = HiddenVertexCount() > 0;
ON_Mesh* submesh = (0 != mesh)
? mesh
: new ON_Mesh(mesh_part.triangle_count,
mesh_part.vertex_count,
bHasVertexNormals,
bHasTextureCoordinates
);
if ( bHasVertexColors )
submesh->m_C.Reserve(submesh_V_count);
if ( bHasSurfaceParameters )
submesh->m_S.Reserve(submesh_V_count);
if ( bHasPrincipalCurvatures )
submesh->m_K.Reserve(submesh_V_count);
if ( bHasHiddenVertices )
submesh->m_H.Reserve(submesh_V_count);
if ( bHasFaceNormals )
submesh->m_FN.Reserve(submesh_F_count);
// put vertex information into submesh
const int vi0 = mesh_part.vi[0];
const int vi1 = mesh_part.vi[1];
for ( int vi = vi0; vi < vi1; vi++ )
{
submesh->m_V.Append(m_V[vi]);
if ( bHasVertexNormals )
submesh->m_N.Append(m_N[vi]);
if ( bHasTextureCoordinates )
submesh->m_T.Append(m_T[vi]);
if ( bHasVertexColors )
submesh->m_C.Append(m_C[vi]);
if ( bHasSurfaceParameters )
submesh->m_S.Append(m_S[vi]);
if ( bHasPrincipalCurvatures )
submesh->m_K.Append(m_K[vi]);
if ( bHasHiddenVertices )
{
bool bHidden = m_H[vi];
submesh->m_H.Append(bHidden);
if ( bHidden )
submesh->m_hidden_count++;
}
}
if ( submesh->m_hidden_count <= 0 )
{
submesh->m_H.Destroy();
submesh->m_hidden_count = 0;
}
// put face information into submesh
int bad_face_count = 0;
const int fi0 = mesh_part.fi[0];
const int fi1 = mesh_part.fi[1];
for ( int fi = fi0; fi < fi1; fi++ )
{
ON_MeshFace f = m_F[fi];
f.vi[0] -= vi0;
f.vi[1] -= vi0;
f.vi[2] -= vi0;
f.vi[3] -= vi0;
if ( f.vi[0] >= submesh_V_count || f.vi[0] < 0
|| f.vi[1] >= submesh_V_count || f.vi[1] < 0
|| f.vi[2] >= submesh_V_count || f.vi[2] < 0
|| f.vi[3] >= submesh_V_count || f.vi[3] < 0
)
{
bad_face_count++;
ON_ERROR("ON_Mesh::MeshPart Invalid face in partition");
continue;
}
submesh->m_F.Append(f);
if ( bHasFaceNormals )
submesh->m_FN.Append(m_FN[fi]);
}
if ( submesh->m_F.Count() < 1 && bad_face_count > 0 )
{
if ( submesh != mesh )
delete submesh;
else
mesh->Destroy();
submesh = 0;
}
return submesh;
}
ON_Mesh* ON_Mesh::DuplicateFace( int face_index, ON_Mesh* mesh ) const
{
if ( mesh == this )
return nullptr;
if ( 0 != mesh )
mesh->Destroy();
if ( face_index < 0 || face_index >= m_F.Count() )
return nullptr;
const unsigned int vcnt = VertexUnsignedCount();
if ( vcnt < 3 )
return nullptr;
const ON_3dPoint* dV = ( vcnt == m_dV.UnsignedCount() ) ? m_dV.Array() : nullptr;
const ON_3fPoint* fV = (nullptr == dV && vcnt == m_V.UnsignedCount() ) ? m_V.Array() : nullptr;
bool bHasFaceNormals = HasFaceNormals();
bool bHasVertexNormals = HasVertexNormals();
bool bHasVertexColors = HasVertexColors();
bool bHasTextureCoordinates = HasTextureCoordinates();
bool bHasSurfaceParameters = HasSurfaceParameters();
bool bHasPrincipalCurvatures = HasPrincipalCurvatures();
ON_MeshFace f = m_F[face_index];
if ( nullptr != dV )
{
if ( !f.IsValid(vcnt,dV) )
{
// invalid vertex indices - see if it can be fixed
if ( !f.Repair(vcnt,dV) )
return nullptr;
}
}
else if (nullptr != fV)
{
if (!f.IsValid(vcnt, fV))
{
// invalid vertex indices - see if it can be fixed
if (!f.Repair(vcnt, fV))
return nullptr;
}
}
else
return nullptr;
const int newvcnt = f.IsTriangle() ? 3 : 4;
if ( 0 == mesh )
mesh = new ON_Mesh();
ON_3dPointArray* newdV = 0;
if ( dV )
{
newdV = &mesh->m_dV;
newdV->Reserve(newvcnt);
}
mesh->m_V.Reserve(newvcnt);
mesh->m_F.Reserve(1);
ON_MeshFace& newface = mesh->m_F.AppendNew();
newface.vi[0] = 0;
newface.vi[1] = 1;
newface.vi[2] = 2;
newface.vi[3] = (4 == newvcnt) ? 3 : newface.vi[2];
if ( bHasFaceNormals )
{
mesh->m_FN.Reserve(1);
mesh->m_FN.Append(m_FN[face_index]);
}
if ( bHasVertexNormals )
mesh->m_N.Reserve(newvcnt);
if ( bHasTextureCoordinates )
mesh->m_T.Reserve(newvcnt);
if ( bHasVertexColors )
mesh->m_C.Reserve(newvcnt);
if ( bHasSurfaceParameters )
mesh->m_S.Reserve(newvcnt);
if ( bHasPrincipalCurvatures )
mesh->m_K.Reserve(newvcnt);
for ( int vi = 0; vi < newvcnt; vi++ )
{
if ( dV )
newdV->Append(dV[f.vi[vi]]);
else
mesh->m_V.Append(fV[f.vi[vi]]);
if ( bHasVertexNormals )
mesh->m_N.Append(m_N[f.vi[vi]]);
if ( bHasTextureCoordinates )
mesh->m_T.Append(m_T[f.vi[vi]]);
if ( bHasVertexColors )
mesh->m_C.Append(m_C[f.vi[vi]]);
if ( bHasSurfaceParameters )
mesh->m_S.Append(m_S[f.vi[vi]]);
if ( bHasPrincipalCurvatures )
mesh->m_K.Append(m_K[f.vi[vi]]);
}
if ( nullptr != dV )
mesh->UpdateSinglePrecisionVertices();
return mesh;
}
//ON_OBJECT_IMPLEMENT(ON_MeshVertexRef,ON_Geometry,"C547B4BD-BDCD-49b6-A983-0C4A7F02E31A");
//
//ON_OBJECT_IMPLEMENT(ON_MeshEdgeRef,ON_Geometry,"ED727872-463A-4424-851F-9EC02CB0F155");
//
//ON_OBJECT_IMPLEMENT(ON_MeshFaceRef,ON_Geometry,"4F529AA5-EF8D-4c25-BCBB-162D510AA280");
ON_OBJECT_IMPLEMENT(ON_MeshComponentRef,ON_Geometry,"1FD2F2BE-3346-4D7A-AE94-73C0B326D8F4");
const ON_MeshComponentRef ON_MeshComponentRef::Unset;
ON_MeshComponentRef::ON_MeshComponentRef()
: m_mesh(0)
, m_mesh_ci(ON_COMPONENT_INDEX::invalid_type,-1)
{
}
ON_MeshComponentRef::ON_MeshComponentRef(
const class ON_Mesh* mesh,
ON_COMPONENT_INDEX ci
)
: m_mesh(mesh)
, m_mesh_ci(ci)
{
}
ON_MeshComponentRef::~ON_MeshComponentRef()
{
m_mesh = 0;
m_mesh_ci.Set(ON_COMPONENT_INDEX::invalid_type,-1);
}
ON_MeshComponentRef& ON_MeshComponentRef::operator=(const ON_MeshComponentRef& src)
{
if ( this != &src )
{
ON_Geometry::operator=(src);
m_mesh = src.m_mesh;
m_mesh_ci = src.m_mesh_ci;
}
return *this;
}
void ON_MeshComponentRef::Set(
const class ON_Mesh* mesh,
ON_COMPONENT_INDEX ci
)
{
m_mesh = mesh;
m_mesh_ci = ci;
}
int ON_MeshComponentRef::CompareMeshPointer(const ON_MeshComponentRef* lhs, const ON_MeshComponentRef* rhs)
{
if (lhs == rhs)
return 0;
// nullptrs are last
if (nullptr == lhs)
return 1;
if (nullptr == rhs)
return -1;
const ON__UINT_PTR lhs_ptr = (ON__UINT_PTR)lhs->m_mesh;
const ON__UINT_PTR rhs_ptr = (ON__UINT_PTR)rhs->m_mesh;
if (lhs_ptr < rhs_ptr)
return -1;
if (lhs_ptr > rhs_ptr)
return 1;
return 0;
}
int ON_MeshComponentRef::Compare(const ON_MeshComponentRef* lhs, const ON_MeshComponentRef* rhs)
{
if (lhs == rhs)
return 0;
// nullptrs are last
if (nullptr == lhs)
return 1;
if (nullptr == rhs)
return -1;
const ON__UINT_PTR lhs_ptr = (ON__UINT_PTR)lhs->m_mesh;
const ON__UINT_PTR rhs_ptr = (ON__UINT_PTR)rhs->m_mesh;
if (lhs_ptr < rhs_ptr)
return -1;
if (lhs_ptr > rhs_ptr)
return 1;
return ON_COMPONENT_INDEX::Compare(&lhs->m_mesh_ci, &rhs->m_mesh_ci);
}
int ON_MeshComponentRef::Compare2(const ON_MeshComponentRef*const* lhs, const ON_MeshComponentRef*const* rhs)
{
if (lhs == rhs)
return 0;
// nullptrs are last
if (nullptr == lhs)
return 1;
if (nullptr == rhs)
return -1;
return ON_MeshComponentRef::Compare(*lhs, *rhs);
}
bool ON_MeshComponentRef::IsValid( ON_TextLog* text_log ) const
{
if ( 0 == m_mesh)
{
if ( 0 != text_log )
{
text_log->Print("m_mesh = nullptr.\n");
}
return false;
}
if ( false == m_mesh_ci.IsMeshComponentIndex() )
{
if ( 0 != text_log )
{
text_log->Print("m_mesh_ci = ");
m_mesh_ci.Dump(*text_log);
text_log->Print(" has an invalid m_type.\n");
}
return false;
}
if ( false == m_mesh->IsValidMeshComponentIndex(m_mesh_ci) )
{
if ( 0 != text_log )
{
text_log->Print("m_mesh_ci = ");
m_mesh_ci.Dump(*text_log);
text_log->Print(" has an invalid m_index.\n");
}
return false;
}
return true;
}
void ON_MeshComponentRef::Dump( ON_TextLog& text_log ) const
{
text_log.Print("m_mesh=%p, m_mesh_ci: ",m_mesh);
m_mesh_ci.Dump(text_log);
text_log.Print("\n");
}
unsigned int ON_MeshComponentRef::SizeOf() const
{
unsigned int sz = sizeof(*this) - sizeof(ON_Geometry);
sz += ON_Geometry::SizeOf();
return sz;
}
ON::object_type ON_MeshComponentRef::ObjectType() const
{
return ON::meshcomponent_reference;
}
// overrides of virtual ON_Geometry functions
int ON_MeshComponentRef::Dimension() const
{
return 3;
}
bool ON_MeshComponentRef::GetBBox(
double* boxmin,
double* boxmax,
bool bGrowBox
) const
{
const unsigned int* vdex_list = 0;
unsigned int vdex_count = 0;
unsigned int vdex_buffer[2];
if ( 0 == m_mesh )
return false;
switch(m_mesh_ci.m_type)
{
case ON_COMPONENT_INDEX::mesh_vertex:
if (m_mesh_ci.m_index >= 0)
vdex_buffer[vdex_count++] = (unsigned int)m_mesh_ci.m_index;
break;
case ON_COMPONENT_INDEX::meshtop_vertex:
if ( m_mesh_ci.m_index >= 0 )
{
const ON_MeshTopology* top = MeshTopology();
if ( 0 != top && m_mesh_ci.m_index < top->m_topv.Count())
{
const ON_MeshTopologyVertex& v = top->m_topv[m_mesh_ci.m_index];
if ( v.m_v_count > 0 && 0 != v.m_vi && v.m_vi[0] >= 0 )
vdex_buffer[vdex_count++] = (unsigned int)v.m_vi[0];
}
}
break;
case ON_COMPONENT_INDEX::meshtop_edge:
if ( m_mesh_ci.m_index >= 0 )
{
const ON_MeshTopology* top = MeshTopology();
if ( 0 != top && m_mesh_ci.m_index < top->m_tope.Count())
{
const ON_MeshTopologyEdge& e = top->m_tope[m_mesh_ci.m_index];
for ( unsigned int j = 0; j < 2; j++ )
{
if ( e.m_topvi[j] >= 0 && e.m_topvi[j] < top->m_topv.Count() )
{
const ON_MeshTopologyVertex& v = top->m_topv[e.m_topvi[j]];
if ( v.m_v_count > 0 && 0 != v.m_vi && v.m_vi[0] >= 0 )
{
vdex_buffer[vdex_count++] = (unsigned int)v.m_vi[0];
}
}
}
}
}
break;
case ON_COMPONENT_INDEX::mesh_face:
if (m_mesh_ci.m_index >= 0 && m_mesh_ci.m_index < m_mesh->m_F.Count())
{
vdex_list = (const unsigned int*)(m_mesh->m_F[m_mesh_ci.m_index].vi);
vdex_count = (vdex_list[2] == vdex_list[3]) ? 3 : 4;
}
break;
case ON_COMPONENT_INDEX::mesh_ngon:
{
const ON_MeshNgon* ngon = m_mesh->Ngon(m_mesh_ci.m_index);
if ( ngon && ngon->m_Vcount > 0 && 0 != ngon->m_vi )
{
vdex_list = ngon->m_vi;
vdex_count = ngon->m_Vcount;
}
}
break;
default:
break;
}
if ( 0 == vdex_list )
{
if ( 0 == vdex_count )
return 0;
vdex_list = vdex_buffer;
}
bool rc = false;
const ON_3dPointListRef vertex_list(m_mesh);
const unsigned int vertex_count = vertex_list.PointCount();
ON_3dPoint v[16];
const unsigned int v_capacity = (unsigned int)(sizeof(v)/sizeof(v[0]));
unsigned int v_count = 0;
for ( unsigned int i = 0; i < vdex_count; i++ )
{
if ( vdex_list[i] < vertex_count )
{
vertex_list.GetPoint(vdex_list[i],&v[v_count].x);
if ( v[v_count].IsValid() )
{
v_count++;
if ( v_capacity == v_count )
{
if ( ON_GetPointListBoundingBox( 3, 0, v_count, 3, &v[0].x, boxmin, boxmax, bGrowBox?true:false ) )
{
bGrowBox = true;
rc = true;
}
v_count = 0;
}
}
}
}
if ( v_count > 0 )
{
if ( ON_GetPointListBoundingBox( 3, 0, v_count, 3, &v[0].x, boxmin, boxmax, bGrowBox?true:false ) )
rc = true;
v_count = 0;
}
return rc;
}
bool ON_MeshComponentRef::Transform(
const ON_Xform& xform
)
{
return false;
}
const ON_MeshTopology* ON_MeshComponentRef::MeshTopology() const
{
const ON_MeshTopology* top = 0;
if ( 0 != m_mesh )
{
if (m_mesh && m_mesh->HasMeshTopology())
{
top = &m_mesh->Topology();
}
else if ( m_mesh->m_V.UnsignedCount() >= 3 && m_mesh->m_F.UnsignedCount() > 0 )
{
if ( ON_COMPONENT_INDEX::meshtop_vertex == m_mesh_ci.m_type
|| ON_COMPONENT_INDEX::meshtop_edge == m_mesh_ci.m_type
)
{
// create missing topology
top = &m_mesh->Topology();
}
}
}
return top;
}
unsigned int ON_MeshComponentRef::VertexIndex() const
{
unsigned int vi = ON_UNSET_UINT_INDEX;
if ( 0 != m_mesh && m_mesh_ci.m_index >= 0 )
{
switch(m_mesh_ci.m_type)
{
case ON_COMPONENT_INDEX::meshtop_vertex:
{
const struct ON_MeshTopologyVertex* v = MeshTopologyVertex();
if ( 0 != v && 1 == v->m_v_count && 0 != v->m_vi
&& ((unsigned int)v->m_vi[0]) < m_mesh->m_V.UnsignedCount()
)
{
vi = (unsigned int)v->m_vi[0];
}
}
break;
case ON_COMPONENT_INDEX::mesh_vertex:
if ( ((unsigned int)m_mesh_ci.m_index) < m_mesh->m_V.UnsignedCount() )
vi = (unsigned int)m_mesh_ci.m_index;
break;
}
}
return vi;
}
ON_3dPoint ON_MeshComponentRef::VertexPoint() const
{
double buffer[3];
ON_3dPointListRef(m_mesh).GetPoint(VertexIndex(),buffer);
return *((const ON_3dPoint*)buffer);
}
unsigned int ON_MeshComponentRef::GetVertexPoint(class ON_3dPoint& point) const
{
const unsigned int vi = VertexIndex();
if ( ON_UNSET_UINT_INDEX != vi )
{
const ON_3dPointListRef vertex_list(m_mesh);
vertex_list.GetPoint(vi,&point.x);
}
else
{
point = ON_3dPoint::UnsetPoint;
}
return vi;
}
const struct ON_MeshTopologyVertex* ON_MeshComponentRef::MeshTopologyVertex() const
{
const struct ON_MeshTopologyVertex* topv;
ON_3dPoint point;
GetMeshTopologyVertexAndPoint(topv,point);
return topv;
}
unsigned int ON_MeshComponentRef::MeshTopologyVertexIndex() const
{
const struct ON_MeshTopologyVertex* topv;
ON_3dPoint point;
return GetMeshTopologyVertexAndPoint(topv,point);
}
unsigned int ON_MeshComponentRef::GetMeshTopologyVertexPoint(
class ON_3dPoint& point
) const
{
const struct ON_MeshTopologyVertex* topv;
return GetMeshTopologyVertexAndPoint(topv,point);
}
unsigned int ON_MeshComponentRef::GetMeshTopologyVertex(
const struct ON_MeshTopologyVertex*& topv
) const
{
ON_3dPoint point;
return GetMeshTopologyVertexAndPoint(topv,point);
}
unsigned int ON_MeshComponentRef::GetMeshTopologyVertexAndPoint(
const struct ON_MeshTopologyVertex*& topv,
class ON_3dPoint& point
) const
{
if ( m_mesh && m_mesh_ci.m_index >= 0 )
{
const ON_MeshTopology* top = MeshTopology();
if ( 0 != top )
{
const ON_3dPointListRef vertex_list(m_mesh);
if ( top->m_topv_map.UnsignedCount() == vertex_list.PointCount() )
{
switch(m_mesh_ci.m_type)
{
case ON_COMPONENT_INDEX::mesh_vertex:
if ( (unsigned int)m_mesh_ci.m_index < vertex_list.PointCount() )
{
int tvi = top->m_topv_map[m_mesh_ci.m_index];
if ( tvi >= 0 && tvi < top->m_topv.Count() )
{
topv = &top->m_topv[tvi];
vertex_list.GetPoint(m_mesh_ci.m_index,&point.x);
return (unsigned int)tvi;
}
}
break;
case ON_COMPONENT_INDEX::meshtop_vertex:
if ( (unsigned int)m_mesh_ci.m_index < top->m_topv.UnsignedCount() )
{
const ON_MeshTopologyVertex& v = top->m_topv[m_mesh_ci.m_index];
topv = &v;
if ( v.m_v_count > 0 && 0 != v.m_vi && v.m_vi[0] >= 0 && (unsigned int)v.m_vi[0] < vertex_list.PointCount() )
vertex_list.GetPoint(v.m_vi[0],&point.x);
else
point = ON_3dPoint::UnsetPoint;
return (unsigned int)m_mesh_ci.m_index;
}
break;
}
}
}
}
topv = 0;
point = ON_3dPoint::UnsetPoint;
return ON_UNSET_UINT_INDEX;
}
unsigned int ON_MeshComponentRef::MeshTopologyEdgeIndex() const
{
const struct ON_MeshTopologyEdge* tope;
return GetMeshTopologyEdge(tope);
}
const struct ON_MeshTopologyEdge* ON_MeshComponentRef::MeshTopologyEdge() const
{
const struct ON_MeshTopologyEdge* tope;
GetMeshTopologyEdge(tope);
return tope;
}
unsigned int ON_MeshComponentRef::GetMeshTopologyEdge(
const struct ON_MeshTopologyEdge*& tope
) const
{
if ( ON_COMPONENT_INDEX::meshtop_edge == m_mesh_ci.m_type && m_mesh_ci.m_index >= 0 )
{
const ON_MeshTopology* top = MeshTopology();
if ( top && m_mesh_ci.m_index < top->m_tope.Count() )
{
tope = &top->m_tope[m_mesh_ci.m_index];
return (unsigned int)m_mesh_ci.m_index;
}
}
tope = 0;
return ON_UNSET_UINT_INDEX;
}
unsigned int ON_MeshComponentRef::GetMeshTopologyEdgeLine(
class ON_Line& line
) const
{
const struct ON_MeshTopologyEdge* tope;
return GetMeshTopologyEdgeAndLine(tope,line);
}
unsigned int ON_MeshComponentRef::GetMeshTopologyEdgeAndLine(
const struct ON_MeshTopologyEdge*& tope,
ON_Line& line
) const
{
tope = 0;
unsigned int topei = GetMeshTopologyEdge(tope);
if ( ON_UNSET_UINT_INDEX != topei && 0 != tope && tope->m_topvi[0] >= 0 && tope->m_topvi[1] >= 0 )
{
const ON_MeshTopology* top = MeshTopology();
if ( 0 != top )
{
int topv_count = top->m_topv.Count();
if ( tope->m_topvi[0] < topv_count && tope->m_topvi[1] < topv_count )
{
const ON_MeshTopologyVertex& v0 = top->m_topv[tope->m_topvi[0]];
const ON_MeshTopologyVertex& v1 = top->m_topv[tope->m_topvi[1]];
if ( v0.m_v_count > 0 && v1.m_v_count > 0 && 0 != v0.m_vi && 0 != v1.m_vi )
{
if ( v0.m_vi[0] >= 0 && v1.m_vi[0] >= 0 && v0.m_vi[0] < m_mesh->m_V.Count() && v1.m_vi[0] < m_mesh->m_V.Count() )
{
ON_3dPointListRef vlist(m_mesh);
vlist.GetPoint(v0.m_vi[0],&line.from.x);
vlist.GetPoint(v1.m_vi[0],&line.to.x);
return topei;
}
}
}
}
}
line = ON_Line::UnsetLine;
return topei;
}
unsigned int ON_MeshComponentRef::MeshFaceIndex() const
{
const ON_MeshFace* mesh_face;
return GetMeshFace(mesh_face);
}
const class ON_MeshFace* ON_MeshComponentRef::MeshFace() const
{
const ON_MeshFace* mesh_face = 0;
GetMeshFace(mesh_face);
return mesh_face;
}
unsigned int ON_MeshComponentRef::GetMeshFace(
const ON_MeshFace*& mesh_face
) const
{
if ( 0 != m_mesh && m_mesh_ci.m_index >= 0 )
{
switch(m_mesh_ci.m_type)
{
case ON_COMPONENT_INDEX::mesh_face:
if ( m_mesh_ci.m_index < m_mesh->m_F.Count() )
{
mesh_face = &m_mesh->m_F[m_mesh_ci.m_index];
return (unsigned int)m_mesh_ci.m_index;
}
break;
case ON_COMPONENT_INDEX::mesh_ngon:
{
const ON_MeshNgon* ngon = m_mesh->Ngon(m_mesh_ci.m_index);
if ( 0 != ngon && 1 == ngon->m_Fcount && 0 != ngon->m_fi )
{
if ( ngon->m_fi[0] < m_mesh->m_F.UnsignedCount() )
{
mesh_face = &m_mesh->m_F[ngon->m_fi[0]];
return ngon->m_fi[0];
}
}
}
break;
}
}
mesh_face = 0;
return ON_UNSET_UINT_INDEX;
}
unsigned int ON_MeshComponentRef::MeshNgonIndex() const
{
unsigned int ngon_index = ON_UNSET_UINT_INDEX;
if ( 0 != m_mesh && m_mesh_ci.m_index >= 0 )
{
switch( m_mesh_ci.m_type)
{
case ON_COMPONENT_INDEX::mesh_face:
if ( m_mesh_ci.m_index < m_mesh->m_F.Count() )
{
ngon_index = m_mesh->NgonIndexFromFaceIndex(m_mesh_ci.m_index);
if ( ngon_index < ON_UNSET_UINT_INDEX && 0 == m_mesh->Ngon(ngon_index) )
ngon_index = ON_UNSET_UINT_INDEX;
}
break;
case ON_COMPONENT_INDEX::mesh_ngon:
if ( 0 != m_mesh->Ngon(m_mesh_ci.m_index) )
ngon_index = (unsigned int)m_mesh_ci.m_index;
break;
}
}
return ngon_index;
}
const class ON_MeshNgon* ON_MeshComponentRef::MeshNgon(
class ON_MeshNgonBuffer& ngon_buffer
) const
{
const class ON_MeshNgon* ngon = 0;
if ( 0 != m_mesh && m_mesh_ci.m_index >= 0 )
{
if ( ON_COMPONENT_INDEX::mesh_ngon == m_mesh_ci.m_type )
{
ngon = m_mesh->Ngon(m_mesh_ci.m_index);
}
else if ( ON_COMPONENT_INDEX::mesh_face == m_mesh_ci.m_type && m_mesh_ci.m_index < m_mesh->m_F.Count() )
{
ngon = ON_MeshNgon::NgonFromMeshFace(ngon_buffer,(unsigned int)m_mesh_ci.m_index,(const unsigned int*)m_mesh->m_F[m_mesh_ci.m_index].vi);
}
}
return ngon;
}
const class ON_MeshNgon* ON_MeshComponentRef::MeshNgon() const
{
unsigned int ngon_index = MeshNgonIndex();
return ( ON_UNSET_UINT_INDEX != ngon_index ) ? m_mesh->Ngon(ngon_index) : 0;
}
ON_MeshComponentRef ON_Mesh::MeshComponentRef(
ON_COMPONENT_INDEX ci
) const
{
ON_MeshComponentRef cr;
if ( ci.IsMeshComponentIndex() && ci.m_index >= 0 )
{
cr.Set( this, ci );
}
return cr;
}
const ON_COMPONENT_INDEX ON_MeshTopology::TopVertexComponentIndex(
ON_COMPONENT_INDEX ci
) const
{
switch (ci.m_type)
{
case ON_COMPONENT_INDEX::TYPE::mesh_vertex:
if (ci.m_index >= 0
&& nullptr != m_mesh
&& ci.m_index < m_mesh->VertexCount()
&& m_mesh->VertexCount() == m_topv_map.Count()
)
{
ci.m_type = ON_COMPONENT_INDEX::TYPE::meshtop_vertex;
ci.m_index = m_topv_map[ci.m_index];
}
// no break here
case ON_COMPONENT_INDEX::TYPE::meshtop_vertex:
if (ci.m_index >= 0 && ci.m_index < m_topv.Count())
{
return ci;
}
break;
}
return ON_COMPONENT_INDEX::UnsetComponentIndex;
}
ON_MeshComponentRef ON_MeshTopology::MeshComponentRef(ON_COMPONENT_INDEX ci) const
{
ON_MeshComponentRef cr;
if ( 0 != m_mesh )
cr = m_mesh->MeshComponentRef(ci);
return cr;
}
const ON_Mesh* ON_MeshComponentRef::Mesh() const
{
return m_mesh;
}
ON_COMPONENT_INDEX ON_MeshComponentRef::ComponentIndex() const
{
ON_COMPONENT_INDEX ci;
if ( m_mesh && m_mesh->IsValidMeshComponentIndex(m_mesh_ci) )
ci = m_mesh_ci;
return ci;
}
bool ON_Mesh::IsValidMeshComponentIndex(
ON_COMPONENT_INDEX ci
) const
{
bool rc = ci.m_index >= 0;
if (rc)
{
switch(ci.m_type)
{
case ON_COMPONENT_INDEX::mesh_vertex:
if (ci.m_index >= m_V.Count())
rc = false;
break;
case ON_COMPONENT_INDEX::meshtop_vertex:
if ( false == TopologyExists() || ci.m_index >= m_top.m_topv.Count() )
rc = false;
break;
case ON_COMPONENT_INDEX::meshtop_edge:
if ( false == TopologyExists() || ci.m_index >= m_top.m_tope.Count() )
rc = false;
break;
case ON_COMPONENT_INDEX::mesh_face:
if (ci.m_index >= m_F.Count())
rc = false;
break;
case ON_COMPONENT_INDEX::mesh_ngon:
if ( 0 == Ngon(ci.m_index) )
rc = false;
break;
default:
rc = false;
break;
}
}
return rc;
}
ON_MeshComponentRef* ON_Mesh::MeshComponent(
ON_COMPONENT_INDEX ci
) const
{
ON_MeshComponentRef* cr_ptr = 0;
ON_MeshComponentRef cr = MeshComponentRef(ci);
if ( this == cr.Mesh() && cr.ComponentIndex() == ci )
cr_ptr = new ON_MeshComponentRef(cr);
return cr_ptr;
}
ON_3dVector ON_TriangleNormal(
const ON_3dPoint& A,
const ON_3dPoint& B,
const ON_3dPoint& C
)
{
// N = normal to triangle's plane
ON_3dVector N;
double a, b, c, d;
N.x = A.y*(B.z-C.z) + B.y*(C.z-A.z) + C.y*(A.z-B.z);
N.y = A.z*(B.x-C.x) + B.z*(C.x-A.x) + C.z*(A.x-B.x);
N.z = A.x*(B.y-C.y) + B.x*(C.y-A.y) + C.x*(A.y-B.y);
a = fabs(N.x);
b = fabs(N.y);
c = fabs(N.z);
if ( b > a )
{
if ( c > b )
{
// c is largest
if ( c > ON_DBL_MIN )
{
a /= c; b /= c; d = c*sqrt(1.0 + a*a + b*b);
}
else
{
d = c;
}
}
else
{
if ( b > ON_DBL_MIN )
{
// b is largest
a /= b; c /= b; d = b*sqrt(1.0 + c*c + a*a);
}
else
{
d = b;
}
}
}
else if ( c > a )
{
// c is largest
if ( c > ON_DBL_MIN )
{
a /= c; b /= c; d = c*sqrt(1.0 + a*a + b*b);
}
else
{
d = c;
}
}
else if ( a > ON_DBL_MIN )
{
// a is largest
b /= a; c /= a; d = a*sqrt(1.0 + b*b + c*c);
}
else
{
d = a;
}
if ( d > 0.0 )
{
N.x /= d; N.y /= d; N.z /= d;
}
return N;
}
bool 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
)
{
const ON_3dVector N(ON_TriangleNormal(A,B,C));
const double dd( -(N.x*A.x + N.y*A.y + N.z*A.z) );
*a = N.x;
*b = N.y;
*c = N.z;
*d = dd;
if ( 0 != evaluation_tol )
{
*evaluation_tol = fabs(N.x*A.x + N.y*A.y + N.z*A.z + dd);
double e = fabs(N.x*B.x + N.y*B.y + N.z*B.z + dd);
if ( e > *evaluation_tol )
*evaluation_tol = e;
e = fabs(N.x*C.x + N.y*C.y + N.z*C.z + dd);
if ( e > *evaluation_tol )
*evaluation_tol = e;
*evaluation_tol *= (1.0 + ON_EPSILON);
}
return (0.0 != N.x || 0.0 != N.y || 0.0 != N.z);
}
const bool* ON_Mesh::HiddenVertexArray() const
{
return ( ( m_hidden_count > 0 && m_H.Count() == m_V.Count() ) ? m_H.Array() : 0);
}
void ON_Mesh::DestroyHiddenVertexArray()
{
m_H.Destroy();
m_hidden_count = 0;
}
int ON_Mesh::HiddenVertexCount() const
{
return ((m_H.Count() == m_V.Count()) ? m_hidden_count : 0);
}
void ON_Mesh::SetVertexHiddenFlag( int meshvi, bool bHidden )
{
const int vcount = m_V.Count();
if ( meshvi >= 0 && meshvi < vcount )
{
if ( bHidden )
{
if ( vcount != m_H.Count() )
{
m_H.SetCapacity(vcount);
m_H.SetCount(vcount);
m_H.Zero();
m_H[meshvi] = true;
m_hidden_count = 1;
}
else if ( false == m_H[meshvi] )
{
m_H[meshvi] = true;
m_hidden_count++;
}
}
else
{
// show this vertex
if ( m_hidden_count > 0 && vcount == m_H.Count() )
{
if ( m_H[meshvi] )
{
m_H[meshvi] = false;
m_hidden_count--;
if ( 0 == m_hidden_count )
{
DestroyHiddenVertexArray();
}
}
}
else if ( m_hidden_count > 0 || m_H.Capacity() > 0 )
{
// if m_H exists, it is bogus.
DestroyHiddenVertexArray();
}
}
}
}
bool ON_Mesh::VertexIsHidden(int meshvi) const
{
const int vcount = m_V.Count();
return ((m_hidden_count > 0 && meshvi >= 0 && meshvi < vcount && vcount == m_H.Count())
? m_H[meshvi]
: false);
}
bool ON_Mesh::FaceIsHidden(int meshfi) const
{
const bool* bHiddenVertex = HiddenVertexArray();
if ( bHiddenVertex && meshfi >= 0 && meshfi < m_F.Count() )
{
ON_MeshFace f = m_F[meshfi];
if ( bHiddenVertex[f.vi[0]] || bHiddenVertex[f.vi[1]] || bHiddenVertex[f.vi[2]] || bHiddenVertex[f.vi[3]] )
return true;
}
return false;
}
bool ON_MeshTopology::TopVertexIsHidden( int topvi ) const
{
const bool* bHiddenVertex = m_mesh ? m_mesh->HiddenVertexArray() : 0;
if ( bHiddenVertex && topvi >= 0 && topvi < m_topv.Count() )
{
const ON_MeshTopologyVertex& topv = m_topv[topvi];
int i;
for ( i = 0; i < topv.m_v_count; i++ )
{
if ( !bHiddenVertex[topv.m_vi[i]] )
return false;
}
return true;
}
return false;
}
bool ON_MeshTopology::TopEdgeIsHidden( int topei ) const
{
// ugly - but faster than calling TopVertexIsHidden()
const bool* bHiddenVertex = m_mesh ? m_mesh->HiddenVertexArray() : 0;
if ( bHiddenVertex && topei >= 0 && topei < m_tope.Count() )
{
const ON_MeshTopologyEdge& tope = m_tope[topei];
const ON_MeshTopologyVertex& topv0 = m_topv[tope.m_topvi[0]];
const ON_MeshTopologyVertex& topv1 = m_topv[tope.m_topvi[1]];
int i;
for ( i = 0; i < topv0.m_v_count; i++ )
{
if ( !bHiddenVertex[topv0.m_vi[i]] )
break;
}
if ( i >= topv0.m_v_count )
return true;
for ( i = 0; i < topv1.m_v_count; i++ )
{
if ( !bHiddenVertex[topv1.m_vi[i]] )
return false;
}
return true;
}
return false;
}
bool ON_MeshTopology::TopFaceIsHidden( int topfi ) const
{
// topology and mesh face indices are the same
return m_mesh ? m_mesh->FaceIsHidden(topfi) : false;
}
ON_MappingTag::ON_MappingTag()
{
Default();
}
ON_MappingTag::ON_MappingTag(const ON_TextureMapping & mapping, const ON_Xform * xform)
{
Default();
Set(mapping);
if (
ON_TextureMapping::TYPE::no_mapping != mapping.m_type && ON_TextureMapping::TYPE::srfp_mapping != mapping.m_type
&& nullptr != xform && xform->IsValid() && false == xform->IsIdentity(ON_ZERO_TOLERANCE) && false == xform->IsZero())
m_mesh_xform = *xform;
}
void ON_MappingTag::Dump( ON_TextLog& text_log ) const
{
text_log.Print("Texture/color coordinates tag:\n");
text_log.PushIndent();
text_log.Print("mapping id: "); text_log.Print(m_mapping_id); text_log.Print("\n");
text_log.Print("mapping crc: %08x\n",m_mapping_crc);
text_log.Print("mesh xform:\n");
text_log.PushIndent(); text_log.Print(m_mesh_xform); text_log.PopIndent();
text_log.PushIndent();
text_log.Print(m_mesh_xform);
text_log.PopIndent();
text_log.PopIndent();
}
void ON_MappingTag::Transform( const ON_Xform& xform )
{
if ( !ON_UuidIsNil(m_mapping_id) )
{
// Update mapping transformation.
// Do NOT change the value of m_mapping_crc.
m_mesh_xform = xform*m_mesh_xform;
}
}
void ON_MappingTag::SetDefaultSurfaceParameterMappingTag()
{
Set(ON_TextureMapping::SurfaceParameterTextureMapping);
}
void ON_MappingTag::Set(const ON_TextureMapping& mapping)
{
Default();
m_mapping_id = mapping.Id();
m_mapping_type = mapping.m_type;
m_mapping_crc = mapping.MappingCRC();
}
bool ON_MappingTag::IsSet() const
{
return (0 != m_mapping_crc || !ON_UuidIsNil(m_mapping_id));
}
bool ON_MappingTag::IsDefaultSurfaceParameterMapping() const
{
bool rc = (ON_TextureMapping::TYPE::srfp_mapping == m_mapping_type);
if ( rc )
{
// The crc needs to be checked to insure that m_uvw
// was the identity.
rc = (m_mapping_crc == ON_TextureMapping::SurfaceParameterTextureMapping.MappingCRC());
}
return rc;
}
void ON_MappingTag::Default()
{
memset(this,0,sizeof(*this));
m_mesh_xform.m_xform[0][0] = 1.0;
m_mesh_xform.m_xform[1][1] = 1.0;
m_mesh_xform.m_xform[2][2] = 1.0;
m_mesh_xform.m_xform[3][3] = 1.0;
}
int ON_MappingTag::Compare(
const ON_MappingTag& other,
bool bCompareId,
bool bCompareCRC,
bool bCompareXform
) const
{
int rc = 0;
if ( !rc && bCompareId )
{
rc = ON_UuidCompare(m_mapping_id,other.m_mapping_id);
}
if ( !rc && bCompareCRC )
{
rc = ((int)m_mapping_crc) - ((int)other.m_mapping_crc);
}
if ( !rc && bCompareXform )
{
rc = m_mesh_xform.Compare(other.m_mesh_xform);
}
return rc;
}
bool ON_MappingTag::Write(ON_BinaryArchive& archive) const
{
bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1);
if (!rc)
return false;
for(;;)
{
rc = archive.WriteUuid(m_mapping_id);
if(!rc) break;
rc = archive.WriteInt(m_mapping_crc);
if(!rc) break;
rc = archive.WriteXform(m_mesh_xform);
if(!rc) break;
// 1.1 fields
rc = archive.WriteInt(static_cast<unsigned int>(m_mapping_type));
if (!rc) break;
break;
}
if ( !archive.EndWrite3dmChunk() )
rc = false;
return rc;
}
bool ON_MappingTag::Read(ON_BinaryArchive& archive)
{
Default();
int mjv = 0, mnv = 0;
bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&mjv,&mnv);
if (!rc)
return false;
for(;;)
{
rc = (1 == mjv);
if (!rc) break;
rc = archive.ReadUuid(m_mapping_id);
if(!rc) break;
if ( 0 == ON_UuidCompare(&obsolete_default_srfp_mapping_id,&m_mapping_id) )
m_mapping_id = ON_nil_uuid;
rc = archive.ReadInt(&m_mapping_crc);
if(!rc) break;
rc = archive.ReadXform(m_mesh_xform);
if(!rc) break;
if ( mnv >= 1 )
{
// 1.1 fields
unsigned int i = static_cast<unsigned int>(m_mapping_type);
rc = archive.ReadInt(&i);
if ( rc )
m_mapping_type = ON_TextureMapping::TypeFromUnsigned(i);
if (!rc) break;
}
break;
}
if ( !archive.EndRead3dmChunk() )
rc = false;
return rc;
}
ON_TextureCoordinates::ON_TextureCoordinates()
{
m_dim = 0;
}
///////////////////////////////////////////////////////
//
// Double precision vertices
//
ON_OBJECT_IMPLEMENT(ON_V5_MeshDoubleVertices,ON_UserData,"17F24E75-21BE-4a7b-9F3D-7F85225247E3");
ON_V5_MeshDoubleVertices* ON_V5_MeshDoubleVertices::GetV5(const ON_Mesh* mesh)
{
return ON_V5_MeshDoubleVertices::Cast(mesh->GetUserData(ON_CLASS_ID(ON_V5_MeshDoubleVertices)));
}
ON_V5_MeshDoubleVertices* ON_V5_MeshDoubleVertices::AttachV5(const ON_Mesh* mesh)
{
ON_V5_MeshDoubleVertices* dv = ON_V5_MeshDoubleVertices::GetV5(mesh);
if ( 0 != dv )
return 0;
dv = new ON_V5_MeshDoubleVertices();
const_cast<ON_Mesh*>(mesh)->AttachUserData(dv);
return dv;
}
ON_V5_MeshDoubleVertices::ON_V5_MeshDoubleVertices()
{
m_userdata_uuid = ON_CLASS_ID(ON_V5_MeshDoubleVertices);
m_application_uuid = ON_opennurbs5_id;
m_userdata_copycount = 1;
}
ON_V5_MeshDoubleVertices::~ON_V5_MeshDoubleVertices()
{}
bool ON_V5_MeshDoubleVertices::IsValid( ON_TextLog* ) const
{
return true;
}
void ON_V5_MeshDoubleVertices::Dump( ON_TextLog& text_log ) const
{
// TODO - print some double values
ON_UserData::Dump(text_log);
}
unsigned int ON_V5_MeshDoubleVertices::SizeOf() const
{
return m_V5_dV.SizeOfArray() + ON_UserData::SizeOf();
}
ON__UINT32 ON_V5_MeshDoubleVertices::DataCRC(ON__UINT32 current_remainder) const
{
current_remainder = ON_CRC32(current_remainder,sizeof(m_fcount),&m_fcount);
current_remainder = ON_CRC32(current_remainder,sizeof(m_dcount),&m_dcount);
current_remainder = ON_CRC32(current_remainder,sizeof(m_fCRC),&m_fCRC);
current_remainder = ON_CRC32(current_remainder,sizeof(m_dCRC),&m_dCRC);
current_remainder = m_V5_dV.DataCRC(current_remainder);
return current_remainder;
}
bool ON_V5_MeshDoubleVertices::DeleteAfterWrite(
const class ON_BinaryArchive& archive,
const class ON_Object* parent_object
) const
{
// ON_V5_MeshDoubleVertices is used when saving earlier versions of files and then deleted.
return true;
}
bool ON_V5_MeshDoubleVertices::DeleteAfterRead(
const class ON_BinaryArchive& archive,
class ON_Object* parent_object
) const
{
ON_Mesh* mesh = ON_Mesh::Cast(parent_object);
if (nullptr != mesh && 0 == mesh->m_dV.UnsignedCount() )
{
const unsigned int vertex_count = mesh->VertexUnsignedCount();
if (vertex_count == m_V5_dV.UnsignedCount())
{
mesh->m_dV = m_V5_dV;
if (false == mesh->HasSynchronizedDoubleAndSinglePrecisionVertices())
mesh->DestroyDoublePrecisionVertices();
else
{
// update vertex bounding boxes
mesh->InvalidateVertexBoundingBox();
mesh->BoundingBox();
}
}
}
return true;
}
bool ON_V5_MeshDoubleVertices::Write(ON_BinaryArchive& file) const
{
bool rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
if (!rc)
return false;
for(;;)
{
rc = file.WriteInt(m_fcount);
if (!rc)
break;
rc = file.WriteInt(m_dcount);
if (!rc)
break;
rc = file.WriteInt(m_fCRC);
if (!rc)
break;
rc = file.WriteInt(m_dCRC);
if (!rc)
break;
rc = file.WriteArray(m_V5_dV);
if (!rc)
break;
break;
}
if ( !file.EndWrite3dmChunk() )
rc = false;
return rc;
}
bool ON_V5_MeshDoubleVertices::Read(ON_BinaryArchive& file)
{
m_fcount = 0;
m_dcount = 0;
m_fCRC = 0;
m_dCRC = 0;
m_V5_dV.Destroy();
int major_version = 0;
int minor_version = 0;
bool rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
if (!rc)
return false;
for(;;)
{
rc = file.ReadInt(&m_fcount);
if (!rc)
break;
rc = file.ReadInt(&m_dcount);
if (!rc)
break;
rc = file.ReadInt(&m_fCRC);
if (!rc)
break;
rc = file.ReadInt(&m_dCRC);
if (!rc)
break;
rc = file.ReadArray(m_V5_dV);
if (!rc)
break;
break;
}
if ( !file.EndRead3dmChunk() )
rc = false;
return rc;
}
// virtual ON_UserData overrides
bool ON_V5_MeshDoubleVertices::GetDescription( ON_wString& description )
{
description = L"ON_Mesh double precision vertices";
return true;
}
bool ON_V5_MeshDoubleVertices::Archive() const
{
// refuse to save garbage in files.
if ( m_fcount != m_dcount )
{
ON_ERROR("m_fcount != m_dcount");
return false;
}
if ( m_dcount != m_V5_dV.Count() )
{
ON_ERROR("m_dcount != m_dV.Count()");
return false;
}
if ( m_dCRC != DoubleCRC() )
{
ON_ERROR("m_dCRC != DoubleCRC()");
return false;
}
const ON_Mesh* mesh = ON_Mesh::Cast( Owner() );
if ( 0 == mesh )
{
ON_ERROR("0 = ON_Mesh::Cast( Owner() )");
return false;
}
if ( m_fcount != mesh->m_V.Count() )
{
ON_ERROR("m_fcount != mesh->m_V.Count()");
return false;
}
if ( m_fCRC != ON_V5_MeshDoubleVertices::FloatCRC(mesh->m_V) )
{
ON_ERROR("m_fCRC != ON_V5_MeshDoubleVertices::FloatCRC(mesh->m_V)");
return false;
}
return true;
}
bool ON_V5_MeshDoubleVertices::Transform( const ON_Xform& xform )
{
if ( !xform.IsIdentity() )
{
const ON__UINT32 crc0 = DoubleCRC();
m_V5_dV.Transform(xform);
const ON__UINT32 crc1 = DoubleCRC();
if ( crc0 == m_dCRC && m_V5_dV.Count() == m_dcount )
m_dCRC = crc1; // update m_dCRC so it stays valid
else
m_dCRC = (0==crc1)?1:0; // make sure m_dCRC is not valid
}
return true;
}
bool ON_Mesh::HasSynchronizedDoubleAndSinglePrecisionVertices() const
{
const unsigned int vertex_count = VertexUnsignedCount();
if (0 == vertex_count)
return false;
if (vertex_count != m_V.UnsignedCount())
return false;
if (vertex_count != m_dV.UnsignedCount())
return false;
ON_3dPoint P;
const ON_3fPoint* FV = m_V.Array();
const ON_3dPoint* DV = m_dV.Array();
for (unsigned int vi = 0; vi < vertex_count; vi++ )
{
// Compare float values.
P.x = (float)DV->x;
P.y = (float)DV->y;
P.z = (float)DV->z;
if ( !(P.x == FV->x && P.y == FV->y && P.z == FV->z) )
return false;
DV++;
FV++;
}
return true;
}
bool ON_Mesh::HasDoublePrecisionVertices() const
{
const unsigned int vertex_count = VertexUnsignedCount();
return ( vertex_count > 0 && vertex_count == m_dV.UnsignedCount() );
}
bool ON_Mesh::HasSinglePrecisionVertices() const
{
const unsigned int vertex_count = VertexUnsignedCount();
return ( vertex_count > 0 && vertex_count == m_V.UnsignedCount() );
}
void ON_Mesh::DestroyDoublePrecisionVertices()
{
m_dV.Destroy();
}
ON__UINT32 ON_V5_MeshDoubleVertices::FloatCRC( const ON_3fPointArray& V )
{
return V.DataCRC(0);
}
ON__UINT32 ON_V5_MeshDoubleVertices::DoubleCRC() const
{
return m_V5_dV.DataCRC(0);
}
void ON_Mesh::UpdateSinglePrecisionVertices()
{
unsigned int vertex_count = m_dV.UnsignedCount();
m_V.Reserve(vertex_count);
m_V.SetCount(vertex_count);
if (0 == vertex_count)
return;
ON_3fPoint* fV = m_V.Array();
ON_3fPoint* fVend = fV + vertex_count;
const ON_3dPoint* dV = m_dV.Array();
while (fV < fVend)
{
fV->x = (float)dV->x;
fV->y = (float)dV->y;
fV->z = (float)dV->z;
fV++;
dV++;
}
}
void ON_Mesh::UpdateDoublePrecisionVertices()
{
const unsigned int vertex_count = m_V.UnsignedCount();
const bool bSelectiveUpdate = (vertex_count == m_dV.UnsignedCount());
m_dV.Reserve(vertex_count);
m_dV.SetCount(vertex_count);
if (0 == vertex_count)
return;
ON_3dPoint* dV = m_dV.Array();
ON_3dPoint* dVend = dV + vertex_count;
const ON_3fPoint* fV = m_V.Array();
if ( bSelectiveUpdate )
{
// double precision vertices already existed
// and there is a reasonable chance that
// a subset of the float precision vertices
// have been modified. So, attempt to
// keep the precision on double vertices
// that alread agree with the float vertices
// in float precision.
ON_3fPoint P;
while (dV < dVend)
{
P.x = (float)dV->x;
P.y = (float)dV->y;
P.z = (float)dV->z;
if ( !(P.x == fV->x && P.y == fV->y && P.z == fV->z) )
{
// (float)dV != fV, so update dV
dV->x = (double)fV->x;
dV->y = (double)fV->y;
dV->z = (double)fV->z;
}
dV++;
fV++;
}
}
else
{
while (dV < dVend)
{
dV->x = (double)fV->x;
dV->y = (double)fV->y;
dV->z = (double)fV->z;
dV++;
fV++;
}
}
}
ON_3dPointArray& ON_Mesh::DoublePrecisionVertices()
{
const unsigned int vertex_count = VertexUnsignedCount();
if ( vertex_count != m_dV.UnsignedCount() )
UpdateDoublePrecisionVertices();
return m_dV;
}
ON_3dPoint ON_Mesh::Vertex(int vertex_index) const
{
const unsigned int vertex_count = m_V.UnsignedCount();
if ( vertex_index < 0 || ((unsigned int)vertex_index) >= vertex_count )
return ON_3dPoint::UnsetPoint;
const ON_3fPoint F = m_V[vertex_index];
if (vertex_count == m_dV.UnsignedCount())
{
ON_3dPoint D = m_dV[vertex_index];
if ( F.x == (float)D.x && F.y == (float)D.y && F.z == (float)D.z )
{
// double precision vertex is valid
return D;
}
}
return F;
}
const ON_3dPointArray& ON_Mesh::DoublePrecisionVertices() const
{
ON_Mesh& mesh = const_cast<ON_Mesh&>(*this);
ON_3dPointArray& a = mesh.DoublePrecisionVertices();
return a;
}
////////////////////////////////////////////////////////////////
//
// BEGIN ON_PerObjectMeshParameters user data class
//
class /*NEVER EXPORT THIS CLASS DEFINITION*/ ON_PerObjectMeshParameters : public ON_UserData
{
#if !defined(ON_BOZO_VACCINE_B5628CA982C44CAE9883487B3E4AB28B)
#error Never copy this class definition or put this definition in a header file!
#endif
ON_OBJECT_DECLARE(ON_PerObjectMeshParameters);
public:
ON_PerObjectMeshParameters();
~ON_PerObjectMeshParameters();
// default copy constructor and operator= work fine.
public:
// virtual ON_Object override
bool IsValid( class ON_TextLog* text_log = nullptr ) const override;
// virtual ON_Object override
unsigned int SizeOf() const override;
// virtual ON_Object override
ON__UINT32 DataCRC(ON__UINT32 current_remainder) const override;
// virtual ON_Object override
bool Write(ON_BinaryArchive& binary_archive) const override;
// virtual ON_Object override
bool Read(ON_BinaryArchive& binary_archive) override;
// virtual ON_UserData override
bool Archive() const override;
// virtual ON_UserData override
bool GetDescription( ON_wString& description ) override;
public:
static
ON_PerObjectMeshParameters* FindOrCreate( const ON_Object*, bool bCreate );
ON_MeshParameters m_mp;
};
#undef ON_BOZO_VACCINE_B5628CA982C44CAE9883487B3E4AB28B
ON_OBJECT_IMPLEMENT(ON_PerObjectMeshParameters,ON_UserData,"B5628CA9-82C4-4CAE-9883-487B3E4AB28B");
ON_PerObjectMeshParameters* ON_PerObjectMeshParameters::FindOrCreate(const ON_Object* object,bool bCreate)
{
if ( 0 == object )
return 0;
ON_PerObjectMeshParameters* ud = ON_PerObjectMeshParameters::Cast(object->GetUserData(ON_CLASS_ID(ON_PerObjectMeshParameters)));
if ( !ud && bCreate )
{
ud = new ON_PerObjectMeshParameters();
const_cast< ON_Object* >(object)->AttachUserData(ud);
}
return ud;
}
ON_PerObjectMeshParameters::ON_PerObjectMeshParameters()
: m_mp(ON_MeshParameters::FastRenderMesh)
{
m_userdata_uuid = ON_CLASS_ID(ON_PerObjectMeshParameters);
m_application_uuid = ON_opennurbs5_id;
m_userdata_copycount = 1;
m_mp.SetCustomSettings(true);
m_mp.SetComputeCurvature(false);
}
ON_PerObjectMeshParameters::~ON_PerObjectMeshParameters()
{
}
// virtual ON_Object override
bool ON_PerObjectMeshParameters::IsValid( ON_TextLog* text_log ) const
{
return true;
}
// virtual ON_Object override
unsigned int ON_PerObjectMeshParameters::SizeOf() const
{
size_t sz = sizeof(*this) - sizeof(ON_UserData);
return (unsigned int)sz;
}
// virtual ON_Object override
ON__UINT32 ON_PerObjectMeshParameters::DataCRC(ON__UINT32 current_remainder) const
{
return m_mp.DataCRC(current_remainder);
}
// virtual ON_Object override
bool ON_PerObjectMeshParameters::Write(ON_BinaryArchive& binary_archive) const
{
if ( !binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0) )
return false;
bool rc = false;
for(;;)
{
if ( !binary_archive.BeginWrite3dmBigChunk(TCODE_ANONYMOUS_CHUNK,0) )
break;
bool mprc = m_mp.Write(binary_archive);
if ( !binary_archive.EndWrite3dmChunk() )
break;
if ( !mprc )
break;
rc = true;
break;
}
if ( !binary_archive.EndWrite3dmChunk() )
rc = false;
return rc;
}
// virtual ON_Object override
bool ON_PerObjectMeshParameters::Read(ON_BinaryArchive& binary_archive)
{
m_mp = ON_MeshParameters::FastRenderMesh;
m_mp.SetCustomSettings(true);
m_mp.SetComputeCurvature(false);
int major_version = 0;
int minor_version = 0;
if ( !binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version) )
return false;
bool rc = false;
for(;;)
{
if ( 1 != major_version )
break;
unsigned int tcode(0);
ON__INT64 value(0);
if (!binary_archive.BeginRead3dmBigChunk(&tcode,&value))
break;
bool mprc = false;
for(;;)
{
if (TCODE_ANONYMOUS_CHUNK != tcode )
break;
if (value <= 0)
break;
if (!m_mp.Read(binary_archive))
break;
mprc = true;
break;
}
if (!binary_archive.EndRead3dmChunk())
break;
if (!mprc)
break;
rc = true;
break;
}
if ( !binary_archive.EndRead3dmChunk() )
rc = false;
m_mp.SetCustomSettings(true);
m_mp.SetComputeCurvature(false);
return rc;
}
// virtual ON_UserData override
bool ON_PerObjectMeshParameters::Archive() const
{
return true;
}
// virtual ON_UserData override
bool ON_PerObjectMeshParameters::GetDescription( ON_wString& description )
{
description = L"Custom Render Mesh Parameters";
return true;
}
//
// END ON_PerObjectMeshParameters user data class
//
////////////////////////////////////////////////////////////////
const ON_MeshParameters* ON_3dmObjectAttributes::CustomRenderMeshParameters() const
{
const ON_PerObjectMeshParameters* ud = ON_PerObjectMeshParameters::FindOrCreate(this,false);
return (0 != ud) ? &ud->m_mp : 0;
}
bool ON_3dmObjectAttributes::SetCustomRenderMeshParameters( const ON_MeshParameters& mp )
{
ON_PerObjectMeshParameters* ud = ON_PerObjectMeshParameters::FindOrCreate(this,true);
if ( 0 == ud )
return false;
ud->m_mp = mp;
ud->m_mp.SetCustomSettings(true);
ud->m_mp.SetComputeCurvature(false);
return true;
}
void ON_3dmObjectAttributes::DeleteCustomRenderMeshParameters()
{
ON_PerObjectMeshParameters* ud = ON_PerObjectMeshParameters::FindOrCreate(this,false);
if ( 0 != ud )
delete ud;
}
bool ON_3dmObjectAttributes::EnableCustomRenderMeshParameters(bool bEnable)
{
ON_PerObjectMeshParameters* ud = ON_PerObjectMeshParameters::FindOrCreate(this,false);
if ( 0 != ud )
ud->m_mp.SetCustomSettingsEnabled(bEnable);
return (!bEnable || nullptr != ud);
}
void ON_Mesh::DestroyTree( bool bDeleteTree )
{
}
static int compare2fPoint(const void* a, const void* b)
{
const float* af = (const float*)a;
const float* bf = (const float*)b;
if (af[0] < bf[0])
return -1;
if (af[0] > bf[0])
return 1;
if (af[1] < bf[1])
return -1;
if (af[1] > bf[1])
return 1;
return 0;
}
static int compare2dPoint(const void* a, const void* b)
{
const double* af = (const double*)a;
const double* bf = (const double*)b;
if (af[0] < bf[0])
return -1;
if (af[0] > bf[0])
return 1;
if (af[1] < bf[1])
return -1;
if (af[1] > bf[1])
return 1;
return 0;
}
//static int compare3fPoint(const void* a, const void* b)
//{
// const float* af = (const float*)a;
// const float* bf = (const float*)b;
// if (af[0] < bf[0])
// return -1;
// if (af[0] > bf[0])
// return 1;
// if (af[1] < bf[1])
// return -1;
// if (af[1] > bf[1])
// return 1;
// if (af[2] < bf[2])
// return -1;
// if (af[2] > bf[2])
// return 1;
// return 0;
//}
//
//static int compare3dPoint(const void* a, const void* b)
//{
// const double* af = (const double*)a;
// const double* bf = (const double*)b;
// if (af[0] < bf[0])
// return -1;
// if (af[0] > bf[0])
// return 1;
// if (af[1] < bf[1])
// return -1;
// if (af[1] > bf[1])
// return 1;
// if (af[2] < bf[2])
// return -1;
// if (af[2] > bf[2])
// return 1;
// return 0;
//}
static int comparedUnsignedPair(const void* a, const void* b)
{
const unsigned int* au = ((const unsigned int*)a);
const unsigned int* bu = ((const unsigned int*)b);
if (au[0] < bu[0])
return -1;
if (au[0] > bu[0])
return 1;
if (au[1] < bu[1])
return -1;
if (au[1] > bu[1])
return 1;
return 0;
}
static unsigned int* ON_GetPointLocationIdsHelper(
size_t point_dim,
size_t point_count,
size_t point_stride,
const float* fPoints,
const double* dPoints,
unsigned int first_vid,
unsigned int* Vid,
unsigned int* Vindex
)
{
// Used here and in opennurbs_plus_xmeshfast.cpp
if (point_count <= 0 || point_dim < 2 || point_dim > 3 || point_stride < point_dim)
return nullptr;
if (nullptr == fPoints && nullptr == dPoints)
return nullptr;
const unsigned int Vcount = (unsigned int)point_count;
if (0 == Vid)
Vid = (unsigned int*)onmalloc(Vcount*sizeof(*Vid));
if (1 == point_count)
{
Vid[0] = first_vid;
if (nullptr != Vindex)
Vindex[0] = 0;
return Vid;
}
if (2 == point_dim)
{
// Dictionary sort the 2d points (sort on x, then y).
if (nullptr != dPoints)
ON_Sort(ON::sort_algorithm::quick_sort, Vid, dPoints, Vcount, point_stride*sizeof(dPoints[0]), compare2dPoint);
else
ON_Sort(ON::sort_algorithm::quick_sort, Vid, fPoints, Vcount, point_stride*sizeof(fPoints[0]), compare2fPoint);
}
else
{
// Dictionary sort the 3d points (sort on x, then y, then z).
if (nullptr != dPoints)
ON_Sort(ON::sort_algorithm::quick_sort, Vid, dPoints, Vcount, point_stride*sizeof(dPoints[0]), (ON_COMPAR_LPVOID_LPVOID)compare3dPoint);
else
ON_Sort(ON::sort_algorithm::quick_sort, Vid, fPoints, Vcount, point_stride*sizeof(fPoints[0]), (ON_COMPAR_LPVOID_LPVOID)compare3fPoint);
}
// Assign a unique temporary id to each group of coincident points.
// The id is temporary because the order of the above sort will change
// if the point set is rotated.
struct tagUnsignedPair
{
unsigned int m_temporary_id;
unsigned int m_vertex_index;
} *up = new tagUnsignedPair[Vcount];
unsigned int temporary_id = Vid[0];
unsigned int i0 = 0;
if (nullptr != dPoints)
{
const double* dP0 = dPoints + point_stride*Vid[0];
if (3 == point_dim)
{
for (unsigned int i = 1; i < Vcount; i++)
{
const double* dP1 = dPoints + point_stride*Vid[i];
if (dP0[0] == dP1[0] && dP0[1] == dP1[1] && dP0[2] == dP1[2])
{
if (Vid[i] < temporary_id)
temporary_id = Vid[i];
}
else
{
while (i0 < i)
{
up[i0].m_temporary_id = temporary_id;
up[i0].m_vertex_index = Vid[i0];
i0++;
}
temporary_id = Vid[i0];
dP0 = dP1;
}
}
}
else
{
for (unsigned int i = 1; i < Vcount; i++)
{
const double* dP1 = dPoints + point_stride*Vid[i];
if (dP0[0] == dP1[0] && dP0[1] == dP1[1])
{
if (Vid[i] < temporary_id)
temporary_id = Vid[i];
}
else
{
while (i0 < i)
{
up[i0].m_temporary_id = temporary_id;
up[i0].m_vertex_index = Vid[i0];
i0++;
}
temporary_id = Vid[i0];
dP0 = dP1;
}
}
}
}
else
{
const float* fP0 = fPoints + point_stride*Vid[0];
if (3 == point_dim)
{
for (unsigned int i = 1; i < Vcount; i++)
{
const float* fP1 = fPoints + point_stride*Vid[i];
if (fP0[0] == fP1[0] && fP0[1] == fP1[1] && fP0[2] == fP1[2])
{
if (Vid[i] < temporary_id)
temporary_id = Vid[i];
}
else
{
while (i0 < i)
{
up[i0].m_temporary_id = temporary_id;
up[i0].m_vertex_index = Vid[i0];
i0++;
}
temporary_id = Vid[i0];
fP0 = fP1;
}
}
}
else
{
for (unsigned int i = 1; i < Vcount; i++)
{
const float* fP1 = fPoints + point_stride*Vid[i];
if (fP0[0] == fP1[0] && fP0[1] == fP1[1])
{
if (Vid[i] < temporary_id)
temporary_id = Vid[i];
}
else
{
while (i0 < i)
{
up[i0].m_temporary_id = temporary_id;
up[i0].m_vertex_index = Vid[i0];
i0++;
}
temporary_id = Vid[i0];
fP0 = fP1;
}
}
}
}
while (i0 < Vcount)
{
up[i0].m_temporary_id = temporary_id;
up[i0].m_vertex_index = Vid[i0];
i0++;
}
// This sort is necessary to insure the vertex id does not change when
// the point set is transformed. It is important that the id be the
// same in these situations so that vertex id groups retain the same
// id when meshes are rotated.
ON_qsort(up, Vcount, sizeof(up[0]), comparedUnsignedPair);
temporary_id = up[0].m_temporary_id;
unsigned int id = first_vid;
Vid[up[0].m_vertex_index] = id;
for (unsigned int i = 1; i < Vcount; i++)
{
if (temporary_id != up[i].m_temporary_id)
{
temporary_id = up[i].m_temporary_id;
id++;
}
Vid[up[i].m_vertex_index] = id;
}
if (0 != Vindex)
{
for (unsigned int i = 0; i < Vcount; i++)
{
Vindex[i] = up[i].m_vertex_index;
}
}
delete[] up;
return Vid;
}
unsigned int* ON_GetPointLocationIds(
size_t point_dim,
size_t point_count,
size_t point_stride,
const float* points,
unsigned int first_point_id,
unsigned int* point_ids,
unsigned int* point_id_index
)
{
return ON_GetPointLocationIdsHelper(
point_dim,
point_count,
point_stride,
points,
nullptr,
first_point_id,
point_ids,
point_id_index
);
}
unsigned int* ON_GetPointLocationIds(
size_t point_dim,
size_t point_count,
size_t point_stride,
const double* points,
unsigned int first_point_id,
unsigned int* point_ids,
unsigned int* point_id_index
)
{
return ON_GetPointLocationIdsHelper(
point_dim,
point_count,
point_stride,
nullptr,
points,
first_point_id,
point_ids,
point_id_index
);
}
unsigned int* ON_GetPointLocationIds(
size_t point_count,
const class ON_2fPoint* points,
unsigned int first_point_id,
unsigned int* point_ids,
unsigned int* point_id_map
)
{
const unsigned int point_dim = 2;
return ON_GetPointLocationIds(point_dim, point_count, point_dim, (const float*)points, first_point_id, point_ids, point_id_map);
}
unsigned int* ON_GetPointLocationIds(
size_t point_count,
const class ON_3fPoint* points,
unsigned int first_point_id,
unsigned int* point_ids,
unsigned int* point_id_map
)
{
const unsigned int point_dim = 3;
return ON_GetPointLocationIds(point_dim, point_count, point_dim, (const float*)points, first_point_id, point_ids, point_id_map);
}
unsigned int* ON_GetPointLocationIds(
size_t point_count,
const class ON_2dPoint* points,
unsigned int first_point_id,
unsigned int* point_ids,
unsigned int* point_id_map
)
{
const unsigned int point_dim = 2;
return ON_GetPointLocationIds(point_dim, point_count, point_dim, (const double*)points, first_point_id, point_ids, point_id_map);
}
unsigned int* ON_GetPointLocationIds(
size_t point_count,
const class ON_3dPoint* points,
unsigned int first_point_id,
unsigned int* point_ids,
unsigned int* point_id_map
)
{
const unsigned int point_dim = 3;
return ON_GetPointLocationIds(point_dim, point_count, point_dim, (const double*)points, first_point_id, point_ids, point_id_map);
}
unsigned int* ON_Mesh::GetVertexLocationIds(
unsigned int first_vid,
unsigned int* Vid,
unsigned int* Vindex
) const
{
const unsigned int vertex_count = VertexUnsignedCount();
return (HasSynchronizedDoubleAndSinglePrecisionVertices())
? ON_GetPointLocationIds(vertex_count, m_dV.Array(), first_vid, Vid, Vindex)
: ON_GetPointLocationIds(vertex_count, m_V.Array(), first_vid, Vid, Vindex);
}
unsigned int ON_MeshFaceSide::GetFaceSideList(
size_t mesh_vertex_count,
const class ON_MeshFaceList& mesh_face_list,
const unsigned int* fi_list,
size_t fi_list_count,
const unsigned int* vertex_id_map,
ON_MeshFaceSide*& face_side_list
)
{
const unsigned int Vcount = (unsigned int)mesh_vertex_count;
if ( Vcount <= 0 )
return 0;
const unsigned int mesh_face_count = mesh_face_list.FaceCount();
const unsigned int Fcount = (0 == fi_list) ? mesh_face_count : ((unsigned int)fi_list_count);
if ( Fcount <= 0 )
return 0;
unsigned int fvi[4];
int face_side_count = 0;
ON_MeshFaceSide face_side = ON_MeshFaceSide::Unset;
ON_MeshFaceSide* fs_list = face_side_list;
if ( 0 == fs_list )
{
fs_list = (ON_MeshFaceSide*)onmalloc(4*Fcount*sizeof(fs_list[0]));
if ( 0 == fs_list )
return 0;
}
for ( face_side.m_fi = 0; face_side.m_fi < Fcount; face_side.m_fi++ )
{
if ( 0 != fi_list )
{
if ( fi_list[face_side.m_fi] >= mesh_face_count )
continue;
mesh_face_list.QuadFvi(fi_list[face_side.m_fi],fvi);
}
else
{
mesh_face_list.QuadFvi(face_side.m_fi,fvi);
}
// These checks are necessary to prevent crashes
if ( fvi[0] >= Vcount )
continue;
if ( fvi[1] >= Vcount )
continue;
if ( fvi[2] >= Vcount )
continue;
if ( fvi[3] >= Vcount )
continue;
if ( 0 != vertex_id_map )
{
fvi[0] = vertex_id_map[fvi[0]];
fvi[1] = vertex_id_map[fvi[1]];
fvi[2] = vertex_id_map[fvi[2]];
fvi[3] = vertex_id_map[fvi[3]];
}
if ( fvi[0] < fvi[1] )
{
face_side.m_vi[0] = fvi[0];
face_side.m_vi[1] = fvi[1];
face_side.m_side = 0;
face_side.m_dir = 0;
fs_list[face_side_count++] = face_side;
}
else if ( fvi[0] > fvi[1] )
{
face_side.m_vi[0] = fvi[1];
face_side.m_vi[1] = fvi[0];
face_side.m_side = 0;
face_side.m_dir = 1;
fs_list[face_side_count++] = face_side;
}
if ( fvi[1] < fvi[2] )
{
face_side.m_vi[0] = fvi[1];
face_side.m_vi[1] = fvi[2];
face_side.m_side = 1;
face_side.m_dir = 0;
fs_list[face_side_count++] = face_side;
}
else if ( fvi[1] > fvi[2] )
{
face_side.m_vi[0] = fvi[2];
face_side.m_vi[1] = fvi[1];
face_side.m_side = 1;
face_side.m_dir = 1;
fs_list[face_side_count++] = face_side;
}
if ( fvi[2] < fvi[3] )
{
face_side.m_vi[0] = fvi[2];
face_side.m_vi[1] = fvi[3];
face_side.m_side = 2;
face_side.m_dir = 0;
fs_list[face_side_count++] = face_side;
}
else if ( fvi[2] > fvi[3] )
{
face_side.m_vi[0] = fvi[3];
face_side.m_vi[1] = fvi[2];
face_side.m_side = 2;
face_side.m_dir = 1;
fs_list[face_side_count++] = face_side;
}
if ( fvi[3] < fvi[0] )
{
face_side.m_vi[0] = fvi[3];
face_side.m_vi[1] = fvi[0];
face_side.m_side = 3;
face_side.m_dir = 0;
fs_list[face_side_count++] = face_side;
}
else if ( fvi[3] > fvi[0] )
{
face_side.m_vi[0] = fvi[0];
face_side.m_vi[1] = fvi[3];
face_side.m_side = 3;
face_side.m_dir = 1;
fs_list[face_side_count++] = face_side;
}
}
if ( 0 == face_side_list )
{
if ( face_side_count <= 0 )
onfree(fs_list);
else
face_side_list = fs_list;
}
return face_side_count;
}
unsigned int ON_Mesh::GetMeshFaceSideList(
const unsigned int* Vid,
ON_MeshFaceSide*& face_side_list
) const
{
const unsigned int mesh_vertex_count = m_V.UnsignedCount();
if ( mesh_vertex_count < 2 )
return 0;
const ON_MeshFaceList mesh_face_list(this);
if ( mesh_face_list.FaceCount() < 1 )
return 0;
return (int)ON_MeshFaceSide::GetFaceSideList(
mesh_vertex_count,
mesh_face_list,
0,0,
Vid,
face_side_list
);
}
bool ON_Mesh::DeleteComponents(
const ON_SimpleArray<ON_COMPONENT_INDEX>& ci_list
)
{
return DeleteComponents(ci_list.Array(), ci_list.UnsignedCount());
}
bool ON_Mesh::DeleteComponent(
ON_COMPONENT_INDEX ci
)
{
return DeleteComponents(&ci, 1);
}
bool ON_Mesh::DeleteComponents(
const ON_COMPONENT_INDEX* ci_list,
size_t ci_count
)
{
if (ci_count <= 0)
return true;
if (nullptr == ci_list)
return false;
bool bIgnoreInvalidComponents = true;
bool bRemoveDegenerateFaces = false;
bool bRemoveUnusedVertices = true;
bool bRemoveEmptyNgons = true;
return DeleteComponents(
ci_list,ci_count,
bIgnoreInvalidComponents,
bRemoveDegenerateFaces,
bRemoveUnusedVertices,
bRemoveEmptyNgons
);
}
bool ON_Mesh::DeleteComponents(
const ON_COMPONENT_INDEX* ci_list,
size_t ci_count,
bool bIgnoreInvalidComponents,
bool bRemoveDegenerateFaces,
bool bRemoveUnusedVertices,
bool bRemoveEmptyNgons
)
{
return DeleteComponents(ci_list, ci_count,
bIgnoreInvalidComponents, bRemoveDegenerateFaces, bRemoveUnusedVertices, bRemoveEmptyNgons,
nullptr);
}
static int Internal_FaceDegenerateAreaCheck(
ON_MeshFace& f,
int vertex_count,
const ON_3fPoint* fV,
const ON_3dPoint* dV
)
{
// returns:
// 0: f is degenerate
// 1: f is not degenerate
// 2: f was a degenerate quad and fixed to be a good triangle
ON_3dPoint V[4];
double a[2];
const double atol = 1.0e-36; // a hair bigger than the smallest positive normalized float.
if (nullptr != dV)
{
V[0] = dV[f.vi[0]];
V[1] = dV[f.vi[1]];
V[2] = dV[f.vi[2]];
V[3] = dV[f.vi[3]];
}
else
{
V[0] = fV[f.vi[0]];
V[1] = fV[f.vi[1]];
V[2] = fV[f.vi[2]];
V[3] = fV[f.vi[3]];
}
if (f.IsTriangle())
{
a[0] = ON_CrossProduct(V[1]-V[0],V[2]-V[0]).Length();
return (a[0] > atol) ? 1 : 0;
}
a[0] = V[0].DistanceTo(V[2]);
a[1] = V[1].DistanceTo(V[3]);
// discard L shaped quads
if (false == (a[0] > atol && a[1] > atol))
return 0;
if (a[0] <= a[1] * 1e-8)
return 0; // quad is L shaped at floating point precision
if (a[1] <= a[0] * 1e-8)
return 0; // quad is L shaped at floating point precision
if (a[0] <= a[1])
{
a[0] = ON_CrossProduct(V[1] - V[0], V[2] - V[0]).Length();
a[1] = ON_CrossProduct(V[2] - V[0], V[3] - V[0]).Length();
if (a[0] > atol)
{
if (a[1] > atol)
return 1;
f.vi[3] = -1;
}
else if (a[1] > atol)
f.vi[1] = -1;
}
else if (a[1] < a[0] )
{
a[0] = ON_CrossProduct(V[2] - V[1], V[3] - V[1]).Length();
a[1] = ON_CrossProduct(V[3] - V[1], V[2] - V[1]).Length();
if (a[0] > atol)
{
if (a[1] > atol)
return 1;
f.vi[0] = -1;
}
else if (a[1] > atol)
f.vi[2] = -1;
}
else
return 0; // nan snuck through
const bool bRepaired = (nullptr != dV) ? f.Repair(vertex_count, dV) : f.Repair(vertex_count, fV);
return bRepaired ? 2 : 0;
}
bool ON_Mesh::DeleteComponents(
const ON_COMPONENT_INDEX* ci_list,
size_t ci_count,
bool bIgnoreInvalidComponents,
bool bRemoveDegenerateFaces,
bool bRemoveUnusedVertices,
bool bRemoveEmptyNgons,
unsigned int* faceMap
)
{
if (ci_count <= 0 && false == bRemoveUnusedVertices && false == bRemoveEmptyNgons && false == bRemoveDegenerateFaces)
return true;
if (0 == ci_list && ci_count > 0)
return false;
const ON_MeshTopology* top = m_top.IsValid() ? &m_top : 0;
const unsigned int vertex_count0 = m_V.UnsignedCount();
const unsigned int face_count0 = m_F.UnsignedCount();
const unsigned int ngon_count0 = NgonUnsignedCount();
const unsigned int topvertex_count0 = top ? top->m_topv.UnsignedCount() : 0;
const unsigned int topedge_count0 = top ? top->m_tope.UnsignedCount() : 0;
unsigned int cdex, fi;
ON_SimpleArray< unsigned int > point_id_map_buffer;
ON_MeshFace point_id_map_f;
unsigned int* point_id_map_fvi = (unsigned int*)point_id_map_f.vi;
if (false == bIgnoreInvalidComponents)
{
for (size_t i = 0; i < ci_count; i++)
{
cdex = (unsigned int)(ci_list[i].m_index);
switch (ci_list[i].m_type)
{
case ON_COMPONENT_INDEX::mesh_vertex:
if (cdex >= vertex_count0)
return false;
break;
case ON_COMPONENT_INDEX::meshtop_vertex:
if (cdex >= topvertex_count0)
return false;
break;
case ON_COMPONENT_INDEX::meshtop_edge:
if (cdex >= topedge_count0)
return false;
break;
case ON_COMPONENT_INDEX::mesh_face:
if (cdex >= face_count0)
return false;
break;
case ON_COMPONENT_INDEX::mesh_ngon:
if (cdex >= ngon_count0)
return false;
break;
}
}
}
if (vertex_count0 <= 0)
return (face_count0 <= 0);
ON_SimpleArray<unsigned int> vertex_status_buffer(vertex_count0);
vertex_status_buffer.SetCount(vertex_count0);
vertex_status_buffer.Zero();
unsigned int* vertex_status = vertex_status_buffer.Array();
ON_SimpleArray<unsigned int> face_status_buffer;
unsigned int* face_status;
if (faceMap == nullptr)
{
face_status_buffer.SetCapacity(face_count0);
face_status_buffer.SetCount(face_count0);
face_status = face_status_buffer.Array();
}
else
{
face_status = faceMap;
}
memset(face_status, 0, m_F.UnsignedCount() * sizeof(unsigned int));
bool bDoomedFaces = false;
bool bDoomedVertices = false;
bool bDoomedNgons = false;
bool bModifiedFaces = false;
const unsigned int* fvi;
for (size_t i = 0; i < ci_count; i++)
{
cdex = (unsigned int)(ci_list[i].m_index);
switch (ci_list[i].m_type)
{
case ON_COMPONENT_INDEX::mesh_vertex:
if (cdex < vertex_count0)
{
bDoomedVertices = true;
vertex_status[cdex] = ON_UNSET_UINT_INDEX;
}
break;
case ON_COMPONENT_INDEX::meshtop_vertex:
if (cdex < topvertex_count0)
{
const ON_MeshTopologyVertex& tv = top->m_topv[cdex];
for (int tvi = 0; tvi < tv.m_v_count; tvi++)
{
const unsigned int vi = (unsigned int)(tv.m_vi[tvi]);
if (vi < vertex_count0)
{
bDoomedVertices = true;
vertex_status[vi] = ON_UNSET_UINT_INDEX;
}
}
}
break;
case ON_COMPONENT_INDEX::meshtop_edge:
if (cdex < topedge_count0)
{
const ON_MeshTopologyEdge& te = top->m_tope[cdex];
if (0 != te.m_topfi)
{
for (int j = 0; j < te.m_topf_count; j++)
{
fi = (unsigned int)(te.m_topfi[j]);
if (fi < face_count0)
{
bDoomedFaces = true;
face_status[fi] = ON_UNSET_UINT_INDEX;
}
}
}
}
break;
case ON_COMPONENT_INDEX::mesh_face:
if (cdex < face_count0)
{
bDoomedFaces = true;
face_status[cdex] = ON_UNSET_UINT_INDEX;
}
break;
case ON_COMPONENT_INDEX::mesh_ngon:
if (cdex < ngon_count0)
{
const ON_MeshNgon* ngon = Ngon(cdex);
if (0 != ngon)
{
const int ngon_index = cdex;
bDoomedNgons = true;
m_NgonMap.SetCount(0);
if (0 != ngon->m_fi)
{
for (unsigned int j = 0; j < ngon->m_Fcount; j++)
{
fi = ngon->m_fi[j];
if (fi < face_count0)
{
bDoomedFaces = true;
face_status[fi] = ON_UNSET_UINT_INDEX;
}
}
}
RemoveNgon(ngon_index);
}
}
break;
}
}
unsigned int point_id_count = 0;
const unsigned int* point_id_map = nullptr;
if (bRemoveDegenerateFaces)
{
// point_id_map[vi0] == point_id_map[vi1] if and only if m_V[vi0] == m_V[vi1].
const unsigned int point_count = m_V.UnsignedCount();
ON_3fPoint* fV = m_V.Array();
ON_3dPoint* dV = HasDoublePrecisionVertices() ? DoublePrecisionVertices().Array() : 0;
if (nullptr != dV)
{
ON_3dPoint* p1 = dV + point_count;
const ON_3dPoint badp(ON_DBL_MAX, ON_DBL_MAX, ON_DBL_MAX);
for (ON_3dPoint* p = dV; p < p1; ++p)
{
if (p->IsValid())
continue;
*p = badp; // so bad points will sort las
}
}
point_id_count = GetRemoveDegenerateFacesPointMap(point_count, fV, dV, point_id_map_buffer);
if (point_id_count > 0
&& point_id_count <= point_id_map_buffer.UnsignedCount()
&& point_count == point_id_map_buffer.UnsignedCount()
)
{
point_id_map = point_id_map_buffer.Array();
}
else
{
point_id_count = 0;
}
}
unsigned int face_count1 = 0;
{
const ON_3fPoint* fV = m_V.Array();
const ON_3dPoint* dV = HasDoublePrecisionVertices() ? DoublePrecisionVertices().Array() : 0;
for (fi = 0; fi < face_count0; fi++)
{
if (0 != face_status[fi])
continue; // this face status is already known
ON_MeshFace& f0 = m_F[fi];
fvi = (const unsigned int*)f0.vi;
for (unsigned int j = 0; j < 4; j++)
{
if (fvi[j] >= vertex_count0 || ON_UNSET_UINT_INDEX == vertex_status[fvi[j]])
{
bDoomedFaces = true;
face_status[fi] = ON_UNSET_UINT_INDEX;
break;
}
}
if (ON_UNSET_UINT_INDEX == face_status[fi])
continue; // f0 has invalid vertex indices
if (point_id_count > 0)
{
// if point_id_count > 0, then bRemoveDegenerateFaces = true.
// set point_id_map_f.vi[] to values of "topological indices"
point_id_map_fvi[0] = point_id_map[f0.vi[0]];
point_id_map_fvi[1] = point_id_map[f0.vi[1]];
point_id_map_fvi[2] = point_id_map[f0.vi[2]];
point_id_map_fvi[3] = point_id_map[f0.vi[3]];
if (false == point_id_map_f.IsValid(point_id_count))
{
// point_id_map_f invalid means we have a degenerate quad or worse.
if (f0.IsQuad() && (point_id_map_f.vi[0] == point_id_map_f.vi[2] || point_id_map_f.vi[1] == point_id_map_f.vi[3]))
{
// "L" quads just get deleted.
bDoomedFaces = true;
face_status[fi] = ON_UNSET_UINT_INDEX;
continue;
}
// the corners of face f0 are not perfect. We either have to convert a quad to a triangle
// or we have a degenerate face.
if ((nullptr != dV) ? f0.Repair(vertex_count0, dV) : f0.Repair(vertex_count0, fV))
{
// f0 what an invalid quad fixed to be a valid triangle
bModifiedFaces = true;
}
else
{
// face cannot be repaired by juggling vertex indices
bDoomedFaces = true;
face_status[fi] = ON_UNSET_UINT_INDEX;
continue;
}
}
switch (Internal_FaceDegenerateAreaCheck(f0, (int)vertex_count0, fV, dV))
{
case 0:
bDoomedFaces = true;
face_status[fi] = ON_UNSET_UINT_INDEX;
break;
case 1:
// f0 is totally valid
break;
case 2:
// f0 what an invalid quad fixed to be a valid triangle
bModifiedFaces = true;
break;
}
if (ON_UNSET_UINT_INDEX == face_status[fi])
continue;
}
face_status[fi] = face_count1++;
for (unsigned int j = 0; j < 4; j++)
{
vertex_status[fvi[j]] = 1; // vertex is referenced by a face
}
}
}
unsigned int vertex_count1 = 0;
if ( bRemoveUnusedVertices )
{
for ( unsigned int vi = 0; vi < vertex_count0; vi++ )
{
if ( 1 == vertex_status[vi] )
vertex_status[vi] = vertex_count1++; // vertex referenced by a face
else
vertex_status[vi] = ON_UNSET_UINT_INDEX; // vertex deleted or not referenced by a face
}
}
else
{
for ( unsigned int vi = 0; vi < vertex_count0; vi++ )
{
if ( vertex_status[vi] <= 1 )
vertex_status[vi] = vertex_count1++;
else
vertex_status[vi] = ON_UNSET_UINT_INDEX; // vertex explicitly deleted
}
}
if ( 0 == vertex_count1 || 0 == face_count1 )
{
Destroy();
return true;
}
if ( vertex_count1 < vertex_count0 || face_count1 < face_count0 )
{
DestroyTopology();
DestroyPartition();
DestroyTree();
for ( unsigned int ngon_index = 0; ngon_index < ngon_count0; ngon_index ++ )
{
const ON_MeshNgon* ngon = Ngon(ngon_index);
if ( 0 == ngon || 0 == ngon->m_vi )
continue;
if ( face_count1 < face_count0 )
{
for ( unsigned int j = 0; j < ngon->m_Fcount; j++ )
{
fi = ngon->m_fi[j];
if ( fi >= face_count0 || ON_UNSET_UINT_INDEX == face_status[fi] )
{
bDoomedNgons = true;
RemoveNgon(ngon_index);
ngon = 0;
break;
}
ngon->m_fi[j] = face_status[fi];
}
if ( 0 == ngon )
continue;
}
if ( vertex_count1 < vertex_count0 )
{
for ( unsigned int j = 0; j < ngon->m_Vcount; j++ )
{
const unsigned int vi = ngon->m_vi[j];
if ( vi >= vertex_count0 || ON_UNSET_UINT_INDEX == vertex_status[vi] )
{
bDoomedNgons = true;
RemoveNgon(ngon_index);
ngon = 0;
break;
}
ngon->m_vi[j] = vertex_status[vi];
}
}
}
unsigned int* fvi1;
ON_MeshFace* F = m_F.Array();
ON_3fVector* FN = HasFaceNormals() ? m_FN.Array() : 0;
if ( vertex_count1 == vertex_count0 )
{
for ( face_count1 = 0; face_count1 < face_count0; face_count1++ )
{
if ( ON_UNSET_UINT_INDEX == face_status[face_count1] )
break;
}
}
else
{
face_count1 = 0;
}
for ( fi = face_count1; fi < face_count0; fi++ )
{
if ( ON_UNSET_UINT_INDEX == face_status[fi] )
continue;
fvi = (const unsigned int*)F[fi].vi;
fvi1 = (unsigned int*)(F[face_count1].vi);
fvi1[0] = vertex_status[fvi[0]];
fvi1[1] = vertex_status[fvi[1]];
fvi1[2] = vertex_status[fvi[2]];
fvi1[3] = vertex_status[fvi[3]];
if ( FN )
FN[face_count1] = FN[fi];
face_count1++;
}
if ( face_count1 < face_count0 )
{
m_F.SetCount(face_count1);
m_F.Shrink();
if ( FN )
{
m_FN.SetCount(face_count1);
m_FN.Shrink();
}
}
if ( vertex_count1 < vertex_count0 )
{
vertex_count1 = 0;
bool bValidSingles = (VertexUnsignedCount() == m_V.UnsignedCount());
bool bValidDoubles = (VertexUnsignedCount() == m_dV.UnsignedCount());
ON_3fPoint* fV = bValidSingles ? m_V.Array() : nullptr;
ON_3dPoint* dV = bValidDoubles ? m_dV.Array() : nullptr;
ON_2dPoint* S = HasSurfaceParameters() ? m_S.Array() : 0;
ON_3fVector* N = HasVertexNormals() ? m_N.Array() : 0;
ON_2fPoint* T = HasTextureCoordinates() ? m_T.Array() : 0;
ON_SurfaceCurvature* K = HasPrincipalCurvatures() ? m_K.Array() : 0;
ON_Color* C = HasVertexColors() ? m_C.Array() : 0;
for ( vertex_count1 = 0; vertex_count1 < vertex_count0; vertex_count1++ )
{
if ( ON_UNSET_UINT_INDEX == vertex_status[vertex_count1] )
break;
}
for ( unsigned int vi = vertex_count1+1; vi < vertex_count0; vi++ )
{
if ( ON_UNSET_UINT_INDEX == vertex_status[vi] )
continue;
if ( fV )
fV[vertex_count1] = fV[vi];
if ( dV )
dV[vertex_count1] = dV[vi];
if ( S )
S[vertex_count1] = S[vi];
if ( N )
N[vertex_count1] = N[vi];
if ( T )
T[vertex_count1] = T[vi];
if ( K )
K[vertex_count1] = K[vi];
if ( C )
C[vertex_count1] = C[vi];
vertex_count1++;
}
if ( fV )
{
m_V.SetCount(vertex_count1);
m_V.Shrink();
}
if ( dV )
{
m_dV.SetCount(vertex_count1);
m_dV.Shrink();
}
if ( S )
{
m_S.SetCount(vertex_count1);
m_S.Shrink();
}
if ( N )
{
m_N.SetCount(vertex_count1);
m_N.Shrink();
}
if ( T )
{
m_T.SetCount(vertex_count1);
m_T.Shrink();
}
if ( K )
{
m_K.SetCount(vertex_count1);
m_K.Shrink();
}
if ( C )
{
m_C.SetCount(vertex_count1);
m_C.Shrink();
}
InvalidateBoundingBoxes();
}
m_invalid_count = 0;
m_quad_count = 0;
m_triangle_count = 0;
SetClosed(-1);
}
else if (bModifiedFaces)
{
// converted degenerate quads into triangles
DestroyTopology();
m_invalid_count = 0;
m_quad_count = 0;
m_triangle_count = 0;
SetClosed(-1);
}
if ( bRemoveEmptyNgons )
RemoveEmptyNgons();
return true;
}
unsigned int* InitializeMap(unsigned int count,ON_SimpleArray< unsigned int >& map_buffer)
{
map_buffer.Reserve(count);
map_buffer.SetCount(count);
unsigned int* map = map_buffer.Array();
for ( unsigned int i = 0; i < count; i++ )
map[i] = ON_UNSET_UINT_INDEX;
return map;
}
ON_Mesh* ON_Mesh::CopyComponents(
const ON_COMPONENT_INDEX* ci_list,
size_t ci_count,
class ON_Mesh* destination_mesh
) const
{
if ( ci_count <= 0 || 0 == ci_list )
return 0;
const unsigned int vertex_count = m_V.UnsignedCount();
const unsigned int face_count = m_F.UnsignedCount();
if ( vertex_count <= 0 && face_count <= 0 )
return 0;
const ON_MeshTopology* top = TopologyExists() ? &Topology() : 0;
const unsigned int topv_count = top ? top->m_topv.UnsignedCount() : 0;
const unsigned int tope_count = top ? top->m_tope.UnsignedCount() : 0;
ON_SimpleArray< unsigned int > vertex_map_buffer;
ON_SimpleArray< unsigned int > face_map_buffer;
unsigned int* vertex_map = 0;
unsigned int* face_map = 0;
unsigned int i, fi, vi;
ON_MeshNgonBuffer ngon_buffer;
for ( size_t ci_index = 0; ci_index < ci_count; ci_index++ )
{
ON_COMPONENT_INDEX ci = ci_list[ci_index];
if ( !ci.IsMeshComponentIndex() )
continue;
if ( ci.m_index < 0 )
continue;
i = (unsigned int)ci.m_index;
switch(ci.m_type)
{
case ON_COMPONENT_INDEX::mesh_vertex:
if ( i >= vertex_count )
break;
if ( 0 == vertex_map )
vertex_map = InitializeMap(vertex_count,vertex_map_buffer);
vertex_map[i] = 0;
break;
case ON_COMPONENT_INDEX::mesh_face:
case ON_COMPONENT_INDEX::mesh_ngon:
{
const ON_MeshNgon* ngon = NgonFromComponentIndex(ngon_buffer,ci);
if ( 0 == ngon )
break;
if ( 0 != ngon->m_vi )
{
for ( i = 0; i < ngon->m_Vcount; i++ )
{
vi = ngon->m_vi[i];
if ( vi >= vertex_count )
continue;
if ( 0 == vertex_map )
vertex_map = InitializeMap(vertex_count,vertex_map_buffer);
vertex_map[vi] = 0;
}
}
if ( 0 != ngon->m_fi )
{
for ( i = 0; i < ngon->m_Fcount; i++ )
{
fi = ngon->m_fi[i];
if ( fi >= face_count )
continue;
if ( 0 == face_map )
face_map = InitializeMap(face_count,face_map_buffer);
face_map[fi] = 0;
}
}
}
break;
case ON_COMPONENT_INDEX::meshtop_vertex:
if ( i < topv_count )
{
const ON_MeshTopologyVertex& v = top->m_topv[i];
if ( v.m_vi )
{
for ( int j = 0; j < v.m_v_count; j++ )
{
if ( v.m_vi[j] < 0 )
continue;
vi = (unsigned int)v.m_vi[j];
if ( vi < vertex_count )
{
if ( 0 == vertex_map )
vertex_map = InitializeMap(vertex_count,vertex_map_buffer);
vertex_map[vi] = 0;
}
}
}
}
break;
case ON_COMPONENT_INDEX::meshtop_edge:
if ( i < tope_count )
{
const ON_MeshTopologyEdge& e = top->m_tope[i];
if ( e.m_topfi )
{
for ( int j = 0; j < e.m_topf_count; j++ )
{
if ( e.m_topfi[j] < 0 )
continue;
fi = (unsigned int)e.m_topfi[j];
if ( fi >= face_count )
continue;
if ( 0 == face_map )
face_map = InitializeMap(face_count,face_map_buffer);
face_map[fi] = 0;
}
}
}
break;
default:
break;
}
}
unsigned int face_count1 = 0;
if ( 0 != face_map )
{
for ( i = 0; i < face_count; i++ )
{
if ( 0 == face_map[i] )
{
ON_MeshFace f = m_F[i];
if ( f.IsValid(vertex_count) )
{
face_map[i] = face_count1++;
if ( 0 == vertex_map )
vertex_map = InitializeMap(vertex_count,vertex_map_buffer);
vertex_map[f.vi[0]] = 0;
vertex_map[f.vi[1]] = 0;
vertex_map[f.vi[2]] = 0;
vertex_map[f.vi[3]] = 0;
}
else
face_map[i] = ON_UNSET_UINT_INDEX; // bogus input face
}
}
}
if ( 0 == vertex_map )
return 0;
unsigned int vertex_count1 = 0;
for ( i = 0; i < vertex_count; i++ )
{
if ( 0 == vertex_map[i] )
vertex_map[i] = vertex_count1++;
}
if ( vertex_count1 <= 0 )
return 0;
ON_Mesh* mesh_copy = 0;
if ( destination_mesh )
{
destination_mesh->Destroy();
mesh_copy = destination_mesh;
}
else
{
mesh_copy = new ON_Mesh();
}
if ( face_count1 > 0 )
{
// copy face information
const ON_MeshFace* F = m_F.Array();
mesh_copy->m_F.Reserve(face_count1);
const ON_3fVector* FN = HasFaceNormals() ? m_FN.Array() : 0;
if ( 0 != FN )
mesh_copy->m_FN.Reserve(face_count1);
const unsigned int* fvi;
unsigned int* fvi1;
for ( fi = 0; fi < face_count; fi++ )
{
if ( ON_UNSET_UINT_INDEX == face_map[fi] )
continue;
fvi = (const unsigned int*)F[fi].vi;
fvi1 = (unsigned int*)(mesh_copy->m_F.AppendNew().vi);
fvi1[0] = vertex_map[fvi[0]];
fvi1[1] = vertex_map[fvi[1]];
fvi1[2] = vertex_map[fvi[2]];
fvi1[3] = vertex_map[fvi[3]];
if ( FN )
mesh_copy->m_FN.AppendNew() = FN[fi];
}
}
const bool bValidSingles = (VertexUnsignedCount() == m_V.UnsignedCount());
const ON_3fPoint* fV = bValidSingles ? m_V.Array() : nullptr;
const bool bValidDoubles = (VertexUnsignedCount() == m_dV.UnsignedCount());
const ON_3dPoint* dV = bValidDoubles ? m_dV.Array() : nullptr;
const ON_2dPoint* S = HasSurfaceParameters() ? m_S.Array() : 0;
const ON_3fVector* N = HasVertexNormals() ? m_N.Array() : 0;
const ON_2fPoint* T = HasTextureCoordinates() ? m_T.Array() : 0;
const ON_SurfaceCurvature* K = HasPrincipalCurvatures() ? m_K.Array() : 0;
const ON_Color* C = HasVertexColors() ? m_C.Array() : 0;
if (fV)
mesh_copy->m_V.Reserve(vertex_count1);
if (dV)
mesh_copy->m_dV.Reserve(vertex_count1);
if (S)
mesh_copy->m_S.Reserve(vertex_count1);
if (N)
mesh_copy->m_N.Reserve(vertex_count1);
if (T)
mesh_copy->m_T.Reserve(vertex_count1);
if (K)
mesh_copy->m_K.Reserve(vertex_count1);
if (C)
mesh_copy->m_C.Reserve(vertex_count1);
for ( vi = 0; vi < vertex_count; vi++ )
{
if ( ON_UNSET_UINT_INDEX == vertex_map[vi] )
continue;
if (fV)
mesh_copy->m_V.AppendNew() = fV[vi];
if (dV)
mesh_copy->m_dV.AppendNew() = dV[vi];
if ( S )
mesh_copy->m_S.AppendNew() = S[vi];
if ( N )
mesh_copy->m_N.AppendNew() = N[vi];
if ( T )
mesh_copy->m_T.AppendNew() = T[vi];
if ( K )
mesh_copy->m_K.AppendNew() = K[vi];
if ( C )
mesh_copy->m_C.AppendNew() = C[vi];
}
const unsigned int ngon_count = NgonCount();
for ( unsigned int ngon_index = 0; ngon_index < ngon_count; ngon_index++ )
{
const ON_MeshNgon* ngon = Ngon(ngon_index);
if ( 0 == ngon )
continue;
if ( 0 == ngon->m_Vcount || 0 == ngon->m_vi )
continue;
if ( 0 == ngon->m_Fcount || 0 == ngon->m_fi )
continue;
unsigned int j;
for ( j = 0; j < ngon->m_Vcount; j++ )
{
vi = ngon->m_vi[j];
if ( vi >= vertex_count )
break;
if ( ON_UNSET_UINT_INDEX == vertex_map[vi] )
break;
}
if ( j < ngon->m_Vcount )
continue;
for ( j = 0; j < ngon->m_Fcount; j++ )
{
fi = ngon->m_fi[j];
if ( fi >= face_count )
break;
if ( ON_UNSET_UINT_INDEX == face_map[fi] )
break;
}
if ( j < ngon->m_Fcount )
continue;
ON_MeshNgon* ngon1 = mesh_copy->AllocateNgon(ngon->m_Vcount,ngon->m_Fcount);
ngon1->m_Vcount = 0;
ngon1->m_Fcount = 0;
for ( j = 0; j < ngon->m_Vcount; j++ )
{
ngon1->m_vi[ngon1->m_Vcount++] = vertex_map[ngon->m_vi[j]];
}
for ( j = 0; j < ngon->m_Fcount; j++ )
{
ngon1->m_fi[ngon1->m_Fcount++] = face_map[ngon->m_fi[j]];
}
mesh_copy->AddNgon(ngon1);
if ( 0 != NgonMap() )
mesh_copy->CreateNgonMap();
}
return mesh_copy;
}
ON_Mesh* ON_Mesh::CopyComponents(
const ON_SimpleArray<ON_COMPONENT_INDEX>& ci_list,
class ON_Mesh* destination_mesh
) const
{
return CopyComponents(ci_list.Array(), ci_list.UnsignedCount(), destination_mesh);
}
bool ON_Mesh::SetSurfaceParamtersFromTextureCoodinates()
{
unsigned int i;
const unsigned int vcount = m_V.UnsignedCount();
bool rc;
ON_Interval dom;
if (vcount == m_T.UnsignedCount())
{
dom.Set(0.0, 1.0);
m_S.SetCount(0);
m_S.Reserve(vcount);
for (i = 0; i < vcount; i++)
{
ON_2dPoint S = m_T[i];
m_S.Append(S);
}
rc = true;
}
else
{
dom = ON_Interval::EmptyInterval;
m_S.Destroy();
rc = false;
}
m_srf_domain[0] = dom;
m_srf_domain[1] = dom;
m_srf_scale[0] = 0.0;
m_srf_scale[1] = 0.0;
m_packed_tex_domain[0] = dom;
m_packed_tex_domain[1] = dom;
m_packed_tex_rotate = false;
return rc;
//bool rc = (vcount > 0 && vcount == m_T.UnsignedCount() && vcount != m_S.UnsignedCount());
//if (!rc)
// return false;
//
//ON_2dPoint Tmin = m_T[0];
//ON_2dPoint Tmax = m_T[0];
//ON_2dPoint T;
//for (i = 1; i < vcount; i++)
//{
// T = m_T[i];
// if (T.x < Tmin.x)
// Tmin.x = T.x;
// else if (T.x > Tmax.x)
// Tmax.x = T.x;
// if (T.y < Tmin.y)
// Tmin.y = T.y;
// else if (T.y > Tmax.y)
// Tmax.y = T.y;
//}
//
//rc = (0.0f <= Tmin.x && Tmax.x <= 1.0f && 0.0f <= Tmin.y && Tmax.y <= 1.0f);
//if (!rc)
// return false;
//ON_Interval packed_tex_domain[2];
//packed_tex_domain[0] = m_packed_tex_domain[0];
//packed_tex_domain[1] = m_packed_tex_domain[1];
//const float Ttol = 1.0e-5f;
//if (packed_tex_domain[0].IsIncreasing() && packed_tex_domain[1].IsIncreasing())
//{
// rc = (packed_tex_domain[0].IsIncreasing() && packed_tex_domain[1].IsIncreasing());
// if (!rc)
// return false;
// rc = (packed_tex_domain[0][0] <= Tmin.x && Tmax.x <= packed_tex_domain[0][1]);
// if (!rc)
// return false;
// rc = (packed_tex_domain[1][0] <= Tmin.y && Tmax.y <= packed_tex_domain[1][1]);
// if (!rc)
// return false;
//}
//else if (Tmin.x > Ttol || Tmax.x < 1.0 - Ttol || Tmin.y > Ttol || Tmax.y < 1.0 - Ttol)
//{
// packed_tex_domain[0].Set(Tmin.x, Tmax.x);
// packed_tex_domain[1].Set(Tmin.y, Tmax.y);
//}
//else
//{
// packed_tex_domain[0].Set(0.0, 1.0);
// packed_tex_domain[1].Set(0.0, 1.0);
//}
//ON_Interval Sdomain[2];
//if (m_packed_tex_rotate)
//{
// Sdomain[0] = packed_tex_domain[1];
// Sdomain[1].Set(1.0 - packed_tex_domain[0][1], 1.0 - packed_tex_domain[0][0]);
//}
//else
//{
// Sdomain[0] = packed_tex_domain[0];
// Sdomain[1] = packed_tex_domain[1];
//}
//rc = Sdomain[0].IsIncreasing() && Sdomain[1].IsIncreasing();
//if (!rc)
// return false;
//m_packed_tex_domain[0] = packed_tex_domain[0];
//m_packed_tex_domain[1] = packed_tex_domain[1];
//m_srf_scale[0] = 0.0;
//m_srf_scale[1] = 0.0;
//m_srf_domain[0] = Sdomain[0];
//m_srf_domain[1] = Sdomain[1];
//m_S.Reserve(vcount);
//m_S.SetCount(0);
//ON_2dPoint S;
//if (m_packed_tex_rotate)
//{
// for (i = 0; i < vcount; i++)
// {
// T = m_T[i];
// double x = m_packed_tex_domain[0].NormalizedParameterAt(T.x);
// double y = m_packed_tex_domain[1].NormalizedParameterAt(T.y);
// S.x = m_srf_domain[0].ParameterAt(y);
// S.y = m_srf_domain[1].ParameterAt(1.0 - x);
// S = T;
// m_S.Append(S);
// }
//}
//else
//{
// for (i = 0; i < vcount; i++)
// {
// T = m_T[i];
// S = T;
// m_S.Append(S);
// }
//}
//return true;
}
//////////////////////////////////////////////////////////////////////////
//
// ON_MeshCache
//
class ON_MeshCacheItem
{
public:
ON_MeshCacheItem() = default;
~ON_MeshCacheItem() = default;
ON_MeshCacheItem(const ON_MeshCacheItem&) = default;
ON_MeshCacheItem& operator=(const ON_MeshCacheItem&) = default;
#if defined(ON_HAS_RVALUEREF)
// rvalue copy constructor
ON_MeshCacheItem( ON_MeshCacheItem&& ) ON_NOEXCEPT;
// rvalue assignment operator
ON_MeshCacheItem& operator=( ON_MeshCacheItem&& );
#endif
bool Write(
ON_BinaryArchive& archive
) const;
bool Read(
ON_BinaryArchive& archive
);
void Dump(
ON_TextLog& text_log
) const;
public:
ON_UUID m_mesh_id = ON_nil_uuid;
std::shared_ptr<ON_Mesh> m_mesh_sp;
ON_MeshCacheItem* m_next = nullptr;
};
#if defined(ON_HAS_RVALUEREF)
ON_MeshCacheItem::ON_MeshCacheItem( ON_MeshCacheItem&& src ) ON_NOEXCEPT
: m_mesh_id(src.m_mesh_id)
, m_mesh_sp(std::move(src.m_mesh_sp))
{
}
ON_MeshCacheItem& ON_MeshCacheItem::operator=( ON_MeshCacheItem&& src )
{
std::move(src.m_mesh_sp).swap(m_mesh_sp);
m_mesh_id = src.m_mesh_id;
return *this;
}
#endif
bool ON_MeshCacheItem::Write(
ON_BinaryArchive& archive
) const
{
const ON_Mesh* mesh = m_mesh_sp.get();
if (nullptr == mesh)
return true; // not an error
if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0))
return false;
bool rc = false;
for (;;)
{
if (!archive.WriteUuid(m_mesh_id))
break;
if ( !archive.WriteObject(mesh) )
break;
rc = true;
break;
}
if (!archive.EndWrite3dmChunk())
rc = false;
return rc;
}
bool ON_MeshCacheItem::Read(
ON_BinaryArchive& archive
)
{
m_mesh_id = ON_nil_uuid;
m_mesh_sp.reset();
int major_version = 0;
int minor_version = 0;
if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version))
return false;
bool rc = false;
for (;;)
{
if ( 1 != major_version )
break;
if (!archive.ReadUuid(m_mesh_id))
break;
ON_Object* mesh_object = nullptr;
if ( !archive.ReadObject(&mesh_object) )
break;
if ( nullptr == mesh_object )
break;
ON_Mesh* mesh = ON_Mesh::Cast(mesh_object);
if (nullptr == mesh)
{
delete mesh_object;
break;
}
std::shared_ptr<ON_Mesh>(mesh).swap(m_mesh_sp);
rc = true;
break;
}
if (!archive.EndRead3dmChunk())
rc = false;
return rc;
}
void ON_MeshCacheItem::Dump(
ON_TextLog& text_log
) const
{
const char* description = nullptr;
if ( ON_MeshCache::RenderMeshId == m_mesh_id )
description = "Render Mesh";
else if ( ON_MeshCache::AnalysisMeshId == m_mesh_id )
description = "Analysis Mesh";
else if ( ON_MeshCache::PreviewMeshId == m_mesh_id )
description = "Preview Mesh";
if (nullptr != description)
{
text_log.Print("%s\n", description);
}
else
{
char buffer[48] = { 0 };
ON_UuidToString(m_mesh_id, buffer);
text_log.Print("mesh id: %s\n",buffer);
}
text_log.PushIndent();
const ON_Mesh* mesh = m_mesh_sp.get();
if (nullptr == mesh)
text_log.Print("Null mesh\n");
else if (mesh->IsEmpty())
text_log.Print("Empty mesh\n");
else
{
const char* mp_style = "Custom";
const ON_MeshParameters* mp = mesh->MeshParameters();
if (mp)
{
if (0 == ON_MeshParameters::CompareGeometrySettings(*mp, ON_MeshParameters::FastRenderMesh))
mp_style = "Fast";
else if (0 == ON_MeshParameters::CompareGeometrySettings(*mp, ON_MeshParameters::QualityRenderMesh))
mp_style = "Quality";
}
text_log.Print("%s mesh with %d faces and %d vertices.\n", mp_style, mesh->FaceCount(), mesh->VertexCount());
}
text_log.PopIndent();
}
ON_UUID ON_MeshCache::MeshIdFromMeshType(
ON::mesh_type mesh_type
)
{
switch (mesh_type)
{
case ON::default_mesh:
return ON_MeshCache::CoarseMeshId;
break;
case ON::render_mesh:
return ON_MeshCache::RenderMeshId;
break;
case ON::analysis_mesh:
return ON_MeshCache::AnalysisMeshId;
break;
case ON::preview_mesh:
return ON_MeshCache::PreviewMeshId;
break;
case ON::any_mesh:
return ON_MeshCache::AnyMeshId;
break;
default:
break;
}
return ON_nil_uuid;
}
class ON_MeshCacheItem* ON_MeshCache::Internal_CreateItem()
{
// Items must come from the main memory pool to prevent
// crashing when meshing operations are canceled.
// Use placement operator new with main heap memory.
ON_MeshCacheItem* item;
void* p = onmalloc(sizeof(*item));
item = new (p) ON_MeshCacheItem();
return item;
}
class ON_MeshCacheItem* ON_MeshCache::Internal_CopyItem(const class ON_MeshCacheItem& src_item)
{
// Items must come from the main memory pool to prevent
// crashing when meshing operations are canceled.
// Use placement operator new with main heap memory.
ON_MeshCacheItem* item;
void* p = onmalloc(sizeof(*item));
item = new (p) ON_MeshCacheItem(src_item);
item->m_next = nullptr;
return item;
}
void ON_MeshCache::Internal_DeleteItem(class ON_MeshCacheItem* item, bool bDeleteMesh)
{
if (nullptr == item)
return;
void* p = (void*)item;
// When bDeleteMesh is false, the memory used by item->m_mesh_sp and the ON_Mesh
// it points to belongs to a memory pool that has been deleted because a worker
// thread was canceled. In this case, calling ~ON_MeshCacheItem() will crash the
// application.
if ( bDeleteMesh )
{
item->~ON_MeshCacheItem();
}
onfree(p);
}
void ON_MeshCache::Internal_CopyHelper(
const class ON_MeshCacheItem* src_impl
)
{
m_impl = 0;
ON_MeshCacheItem* prev = nullptr;
for (const ON_MeshCacheItem* src_item = src_impl; nullptr != src_item; src_item = src_item->m_next)
{
ON_MeshCacheItem* item_copy = Internal_CopyItem(*src_item);
if (nullptr == prev)
m_impl = item_copy;
else
prev->m_next = item_copy;
prev = item_copy;
}
}
class ON_MeshCacheItem* ON_MeshCache::Internal_FindHelper(
ON_UUID mesh_id
) const
{
unsigned int coarse_mesh_face_count = 0xFFFFFFFFU;
unsigned int fine_mesh_face_count = 0;
ON_MeshCacheItem* item_coarse_mesh = nullptr;
ON_MeshCacheItem* item_fine_mesh = nullptr;
const bool bCountFaces = (ON_MeshCache::FineMeshId == mesh_id || ON_MeshCache::CoarseMeshId == mesh_id);
for (ON_MeshCacheItem* item = m_impl; nullptr != item; item = item->m_next)
{
if (mesh_id == item->m_mesh_id)
return item;
if (false == bCountFaces)
continue;
const ON_Mesh* mesh = item->m_mesh_sp.get();
if (nullptr == mesh)
continue;
unsigned int mesh_face_count = mesh->FaceUnsignedCount();
if (mesh_face_count <= 0 || mesh->VertexUnsignedCount() < 3)
continue;
if (mesh_face_count > fine_mesh_face_count)
{
item_fine_mesh = item;
fine_mesh_face_count = mesh_face_count;
}
if (mesh_face_count < coarse_mesh_face_count)
{
item_coarse_mesh = item;
coarse_mesh_face_count = mesh_face_count;
}
}
if (ON_MeshCache::CoarseMeshId == mesh_id)
return item_coarse_mesh;
if (ON_MeshCache::FineMeshId == mesh_id)
return item_fine_mesh;
return nullptr;
}
ON_MeshCache::~ON_MeshCache()
{
ClearAllMeshes();
}
ON_MeshCache::ON_MeshCache(const ON_MeshCache& src)
{
Internal_CopyHelper(src.m_impl);
}
ON_MeshCache& ON_MeshCache::operator=(const ON_MeshCache& src)
{
if (this != &src)
{
ClearAllMeshes();
Internal_CopyHelper(src.m_impl);
}
return *this;
}
#if defined(ON_HAS_RVALUEREF)
// rvalue copy constructor
ON_MeshCache::ON_MeshCache( ON_MeshCache&& src) ON_NOEXCEPT
{
m_impl = src.m_impl;
src.m_impl = nullptr;
}
ON_MeshCache& ON_MeshCache::operator=( ON_MeshCache&& src )
{
if (this != &src)
{
m_impl = src.m_impl;
src.m_impl = nullptr;
}
return *this;
}
#endif
void ON_MeshCache::SetMesh(
ON::mesh_type mesh_type,
const std::shared_ptr<ON_Mesh>& mesh_sp
)
{
SetMesh(ON_MeshCache::MeshIdFromMeshType(mesh_type),mesh_sp);
}
void ON_MeshCache::SetMesh(
ON_UUID mesh_id,
const std::shared_ptr<ON_Mesh>& mesh_sp
)
{
if ( ON_nil_uuid == mesh_id )
return;
if ( ON_max_uuid == mesh_id )
return;
const ON_Mesh* mesh = mesh_sp.get();
if (nullptr == mesh || mesh->IsEmpty())
{
ClearMesh(mesh_id);
return;
}
if ( ON_MeshCache::AnyMeshId == mesh_id )
return;
ON_MeshCacheItem* item = Internal_FindHelper(mesh_id);
if (nullptr == item)
{
item = Internal_CreateItem();
item->m_mesh_id = mesh_id;
item->m_next = m_impl;
m_impl = item;
}
if ( nullptr == item )
return;
item->m_mesh_sp = mesh_sp;
}
void ON_MeshCache::ClearMesh(
ON::mesh_type mesh_type
)
{
ClearMesh(mesh_type, true);
}
void ON_MeshCache::ClearMesh(
ON::mesh_type mesh_type,
bool bDeleteMesh
)
{
ClearMesh(ON_MeshCache::MeshIdFromMeshType(mesh_type),bDeleteMesh);
}
void ON_MeshCache::ClearMesh(
ON_UUID mesh_id
)
{
ClearMesh(mesh_id, true);
}
void ON_MeshCache::ClearMesh(
ON_UUID mesh_id,
bool bDeleteMesh
)
{
if (ON_MeshCache::AnyMeshId == mesh_id)
ClearAllMeshes(bDeleteMesh);
else
{
ON_MeshCacheItem* prev = nullptr;
for (ON_MeshCacheItem* item = m_impl; nullptr != item; item = item->m_next)
{
if (mesh_id == item->m_mesh_id)
{
if ( nullptr == prev )
m_impl = item->m_next;
else
prev->m_next = item->m_next;
Internal_DeleteItem(item,bDeleteMesh);
return;
}
prev = item;
}
}
}
void ON_MeshCache::ClearAllMeshes()
{
ClearAllMeshes(true);
}
void ON_MeshCache::ClearAllMeshes(bool bDeleteMesh)
{
if ( 0 != m_impl )
{
ON_MeshCacheItem* next = m_impl;
m_impl = nullptr;
for (ON_MeshCacheItem* item = next; nullptr != item; item = next)
{
next = item->m_next;
Internal_DeleteItem(item,bDeleteMesh);
}
}
}
const ON_Mesh* ON_MeshCache::Mesh(
ON::mesh_type mesh_type
) const
{
return Mesh(ON_MeshCache::MeshIdFromMeshType(mesh_type));
}
const ON_Mesh* ON_MeshCache::Mesh(
ON_UUID mesh_id
) const
{
return MeshSharedPtr(mesh_id).get();
}
std::shared_ptr<ON_Mesh> ON_MeshCache::MeshSharedPtr(
ON::mesh_type mesh_type
) const
{
return MeshSharedPtr(ON_MeshCache::MeshIdFromMeshType(mesh_type));
}
std::shared_ptr<ON_Mesh> ON_MeshCache::MeshSharedPtr(
ON_UUID mesh_id
) const
{
const ON_MeshCacheItem* item
= (ON_MeshCache::AnyMeshId == mesh_id)
? m_impl
: Internal_FindHelper(mesh_id);
if ( item )
return item->m_mesh_sp;
return std::shared_ptr<ON_Mesh>();
}
bool ON_MeshCache::Write(
ON_BinaryArchive& archive
) const
{
if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0))
return false;
bool rc = true;
char c;
for (const ON_MeshCacheItem* item = m_impl; nullptr != item; item = item->m_next)
{
c = 1;
rc = archive.WriteChar(c);
if ( false == rc )
break;
rc = item->Write(archive);
if ( false == rc )
break;
}
if (rc)
{
c = 0;
rc = archive.WriteChar(c);
}
if (!archive.EndWrite3dmChunk())
rc = false;
return rc;
}
bool ON_MeshCache::Read(
ON_BinaryArchive& archive
)
{
int major_version = 0;
int minor_version = 0;
if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version))
return false;
bool rc = false;
for (;;)
{
if (1 != major_version)
break;
ON_MeshCacheItem* prev = nullptr;
for (;;)
{
char c = 0;
if (!archive.ReadChar(&c))
break;
if (0 == c)
{
rc = true;
break;
}
if (1 != c)
break;
ON_MeshCacheItem* item = Internal_CreateItem();
if (!item->Read(archive))
{
Internal_DeleteItem(item,true);
break;
}
if (nullptr == prev)
m_impl = item;
else
prev->m_next = item;
prev = item;
}
break;
}
if (!archive.EndRead3dmChunk())
rc = false;
return rc;
}
unsigned int ON_MeshCache::MeshCount() const
{
unsigned int count = 0;
for (const ON_MeshCacheItem* item = m_impl; nullptr != item; item = item->m_next)
{
count++;
}
return count;
}
void ON_MeshCache::Dump(
ON_TextLog& text_log
) const
{
unsigned int count = MeshCount();
text_log.Print("%u cached meshes.\n",count);
for (const ON_MeshCacheItem* item = m_impl; nullptr != item; item = item->m_next)
{
item->Dump(text_log);
}
}
bool ON_MeshCache::Transform(
const ON_Xform& xform
)
{
if (!xform.IsValid())
return false;
if (xform.IsZero())
return false;
if (xform.IsIdentity())
return true;
bool rc = true;
for (ON_MeshCacheItem* item = m_impl; nullptr != item; item = item->m_next)
{
ON_Mesh* mesh = item->m_mesh_sp.get();
if (nullptr == mesh || mesh->IsEmpty())
continue;
if (false == item->m_mesh_sp.unique())
{
// make a copy and transform the copy
std::shared_ptr<ON_Mesh>(new ON_Mesh(*mesh)).swap(item->m_mesh_sp);
mesh = item->m_mesh_sp.get();
}
if ( !mesh->Transform(xform) )
rc = false;
}
return rc;
}
//////////////////////////////////////////////////////////////////////////
//
// ON_MeshRef
//
ON_MeshRef::ON_MeshRef() ON_NOEXCEPT
{}
ON_MeshRef::~ON_MeshRef()
{
m_mesh_sp.reset();
}
ON_MeshRef::ON_MeshRef(const ON_MeshRef& src) ON_NOEXCEPT
: m_mesh_sp(src.m_mesh_sp)
{}
ON_MeshRef& ON_MeshRef::operator=(const ON_MeshRef& src)
{
if ( this != &src )
m_mesh_sp = src.m_mesh_sp;
return *this;
}
#if defined(ON_HAS_RVALUEREF)
// rvalue copy constructor
ON_MeshRef::ON_MeshRef( ON_MeshRef&& src) ON_NOEXCEPT
: m_mesh_sp(std::move(src.m_mesh_sp))
{}
// rvalue assignment operator
ON_MeshRef& ON_MeshRef::operator=(ON_MeshRef&& src)
{
m_mesh_sp.reset();
m_mesh_sp = std::move(src.m_mesh_sp);
return *this;
}
#endif
const class ON_Mesh& ON_MeshRef::Mesh() const
{
const ON_Mesh* mesh = m_mesh_sp.get();
if ( nullptr == mesh )
mesh = &ON_Mesh::Empty;
return *mesh;
}
unsigned int ON_MeshRef::ReferenceCount() const
{
return (unsigned int)m_mesh_sp.use_count();
}
void ON_MeshRef::Clear()
{
m_mesh_sp.reset();
}
class ON_Mesh& ON_MeshRef::NewMesh()
{
ON_Mesh* mesh = new ON_Mesh();
ON_Mesh* managed_mesh = SetMeshForExperts(mesh);
return *managed_mesh;
}
class ON_Mesh& ON_MeshRef::CopyMesh(
const ON_MeshRef& src
)
{
return CopyMesh(src.Mesh());
}
class ON_Mesh& ON_MeshRef::CopyMesh(
const ON_Mesh& src
)
{
ON_Mesh* mesh_copy = new ON_Mesh(src);
ON_Mesh* managed_mesh = SetMeshForExperts(mesh_copy);
return *managed_mesh;
}
class ON_Mesh& ON_MeshRef::UniqueMesh()
{
const ON_Mesh& mesh = Mesh();
if (m_mesh_sp.use_count() > 1 )
return CopyMesh(mesh);
return const_cast< ON_Mesh& >(mesh);
}
class ON_Mesh* ON_MeshRef::SetMeshForExperts(
class ON_Mesh*& mesh
)
{
Clear();
ON_Mesh* managed_mesh = ( mesh == &ON_Mesh::Empty ) ? nullptr : mesh;
mesh = nullptr;
if (nullptr != managed_mesh )
m_mesh_sp = std::shared_ptr<class ON_Mesh>(managed_mesh);
return managed_mesh;
}
unsigned int ON_Mesh::DissolveOrDelete(
const ON_SimpleArray<ON_COMPONENT_INDEX>& ci_list
)
{
// Dissolve edges and vertices, delete faces
const unsigned int bailout_rc = ON_UNSET_UINT_INDEX;
const int ci_list_count = ci_list.UnsignedCount();
if (ci_list_count <= 0)
return bailout_rc;
const int mesh0_vertex_count = this->VertexCount();
if (mesh0_vertex_count < 3)
return bailout_rc;
const int mesh0_face_count = this->FaceCount();
if (mesh0_face_count < 1)
return bailout_rc;
const ON_MeshTopology& top0 = Topology();
const int top0_vertex_count = top0.m_topv.Count();
ON_SimpleArray<ON_COMPONENT_INDEX> faces(ci_list_count);
ON_SimpleArray<ON_COMPONENT_INDEX> edges_and_vertices(ci_list_count);
ON_SimpleArray<ON_2dex> edges(ci_list_count);
for (int i = 0; i < ci_list_count; ++i)
{
ON_COMPONENT_INDEX ci = ci_list[i];
if (ci.m_index < 0)
continue;
switch (ci.m_type)
{
case ON_COMPONENT_INDEX::TYPE::mesh_vertex:
if ( ci.m_index < mesh0_vertex_count)
edges_and_vertices.Append(ci);
break;
case ON_COMPONENT_INDEX::TYPE::meshtop_vertex:
// must save input top vertex as an ordinary vertex index
// because input topology indices will be changed if faces are deleted.
if (ci.m_index < top0_vertex_count)
{
const ON_MeshTopologyVertex& v = top0.m_topv[ci.m_index];
if (v.m_v_count > 0 && nullptr != v.m_vi)
{
ON_COMPONENT_INDEX vci(ON_COMPONENT_INDEX::TYPE::mesh_vertex, v.m_vi[0]);
if ( vci.m_index >= 0 && vci.m_index < mesh0_vertex_count)
edges_and_vertices.Append(vci);
}
}
break;
case ON_COMPONENT_INDEX::TYPE::meshtop_edge:
// must save input edge as a pair of ordinary vertex indices
// because input topology indices will be changed if faces are deleted.
if ( ci.m_index < top0.m_tope.Count())
{
const ON_MeshTopologyEdge& e = top0.m_tope[ci.m_index];
ON_2dex evdex(-1, -1);
int* evi = &evdex.i;
for (int j = 0; j < 2; j++)
{
if (e.m_topvi[j] < 0 || e.m_topvi[j] >= top0_vertex_count)
break;
const ON_MeshTopologyVertex& v = top0.m_topv[e.m_topvi[j]];
if (v.m_v_count > 0 && nullptr != v.m_vi)
evi[j] = v.m_vi[0];
}
if (evdex.i > evdex.j)
{
const int x = evdex.i;
evdex.i = evdex.j;
evdex.j = x;
}
if (0 <= evdex.i && evdex.i < evdex.j && evdex.j < mesh0_vertex_count)
edges.Append(evdex);
}
break;
case ON_COMPONENT_INDEX::TYPE::mesh_face:
faces.Append(ci);
break;
case ON_COMPONENT_INDEX::TYPE::mesh_ngon:
{
// must convert ngon to a bucnh of face indices
const ON_MeshNgon* ngon = this->Ngon(ci.m_index);
if (nullptr == ngon || nullptr == ngon->m_fi)
break;
ON_COMPONENT_INDEX fci(ON_COMPONENT_INDEX::TYPE::mesh_face, -1);
for (unsigned nfi = 0; nfi < ngon->m_Fcount; ++nfi)
{
fci.m_index = ngon->m_fi[nfi];
if ( fci.m_index < mesh0_face_count)
faces.Append(fci);
}
}
break;
};
}
// delete the faces in a way that does not change ordinary vertex indices
DeleteComponents(
faces.Array(),
faces.UnsignedCount(),
true, // bIgnoreInvalidComponents,
true, // bRemoveDegenerateFaces,
false, // bRemoveUnusedVertices CRITICAL to preserve vertex index values
true, // bRemoveEmptyNgons,
nullptr // unsigned int* faceMap
);
if (FaceCount() <= 0)
{
Destroy();
return bailout_rc;
}
if (mesh0_vertex_count != VertexCount())
return bailout_rc; // vertex array was modified
if (edges.Count() > 0 || edges_and_vertices.Count() > 0)
{
// convert ordinary vertex indices into top1 indices
const ON_MeshTopology& top1 = Topology();
const int top1_vertex_count = top1.m_topv.Count();
const int top1_edge_count = top1.m_tope.Count();
if (mesh0_vertex_count == top1.m_topv_map.Count())
{
// get a clean list of top1.m_topv[] indices
int vertex_count = 0;
for (int i = 0; i < edges_and_vertices.Count(); ++i)
{
ON_COMPONENT_INDEX ci = edges_and_vertices[i];
ci.m_type = ON_COMPONENT_INDEX::TYPE::meshtop_vertex;
ci.m_index = top1.m_topv_map[ci.m_index];
if (ci.m_index >= 0 && ci.m_index < top1_vertex_count)
edges_and_vertices[vertex_count++] = ci;
}
edges_and_vertices.SetCount(vertex_count);
edges_and_vertices.QuickSortAndRemoveDuplicates(ON_COMPONENT_INDEX::Compare);
vertex_count = edges_and_vertices.Count();
// now add edges to edges_and_vertices[]
ON_COMPONENT_INDEX eci(ON_COMPONENT_INDEX::TYPE::meshtop_edge, -1);
for (int i = 0; i < edges.Count(); i++)
{
ON_2dex evi = edges[i];
const int topv[2] = { top1.m_topv_map[evi.i],top1.m_topv_map[evi.j] };
if (
topv[0] != topv[1]
&& topv[0] >= 0 && topv[0] < top1_vertex_count
&& topv[1] >= 0 && topv[1] < top1_vertex_count
)
{
const ON_MeshTopologyVertex& v0 = top1.m_topv[topv[0]];
if ( v0.m_tope_count > 0 && nullptr != v0.m_topei )
{
// find the edge connecting the two vertices
for (int k = 0; k < v0.m_tope_count; ++k)
{
eci.m_index = v0.m_topei[k];
if (eci.m_index >= 0 && eci.m_index < top1_edge_count)
{
const ON_MeshTopologyEdge& e = top1.m_tope[eci.m_index];
if (
(e.m_topvi[0] == topv[0] && e.m_topvi[1] == topv[1])
|| (e.m_topvi[0] == topv[1] && e.m_topvi[1] == topv[0]))
{
edges_and_vertices.Append(eci);
}
}
}
}
}
}
}
}
// dissolve the edges and vertices by merging faces
unsigned int rc
= (edges_and_vertices.Count() > 0)
? MergeFaceSets(edges_and_vertices)
: bailout_rc;
// Finally, we can remove unused vertices.
DeleteComponents(
nullptr,
0,
true, // bIgnoreInvalidComponents,
true, // bRemoveDegenerateFaces,
true, // bRemoveUnusedVertices,
true, // bRemoveEmptyNgons,
nullptr // unsigned int* faceMap
);
return rc;
}
static void Internal_WeldNgonCandiate(
const ON_Mesh& mesh,
const ON_MeshTopology& top,
unsigned char* fmarks,
const unsigned char xmark,
const ON_SimpleArray<unsigned>& ngon_fi
)
{
const unsigned ngon_face_count = ngon_fi.UnsignedCount();
if (ngon_face_count < 2)
return;
// Assumption fmarks[] has no xmark.
// Set xmark for all faces referenced in ngon_fi[] and
// use that mark to find interior vertices that need
// to be welded before the ngon can be created.
for (unsigned nfi = 0; nfi < ngon_face_count; ++nfi)
fmarks[ngon_fi[nfi]] |= xmark;
for (unsigned nfi = 0; nfi < ngon_face_count; ++nfi)
{
const int fi = ngon_fi[nfi];
const ON_MeshTopologyFace& f = top.m_topf[fi];
const int fv_count = f.IsTriangle() ? 3 : 4;
int* fvi = const_cast<int*>( mesh.m_F[fi].vi );
for (int fei = 0; fei < fv_count; ++fei)
{
const int evi = (0 == f.m_reve[fei]) ? 1 : 0;
const ON_MeshTopologyEdge& fe = top.m_tope[f.m_topei[fei]];
const int topvi = fe.m_topvi[evi];
const ON_MeshTopologyVertex& v = top.m_topv[topvi];
if (v.m_v_count <= 1 || nullptr == v.m_vi)
continue;
int mesh_vi[2] = { fvi[fei],fvi[fei] };
for (int pass = 0; pass < 2; ++pass)
{
for (int vei = 0; vei < v.m_v_count; ++vei)
{
const ON_MeshTopologyEdge& ve = top.m_tope[f.m_topei[fei]];
for (int efi = 0; efi < ve.m_topf_count; ++efi)
{
const int f1i = ve.m_topfi[efi];
if (fi == f1i)
continue;
if (0 == (fmarks[f1i] |= xmark))
continue; // this face is not in the ngon
const ON_MeshTopologyFace& f1 = top.m_topf[f1i];
int* f1vi = const_cast<int*>( mesh.m_F[f1i].vi );
const int f1v_count = f1.IsTriangle() ? 3 : 4;
for (int f1ei = 0; f1ei < f1v_count; ++f1ei)
{
const int k = (0 == f1.m_reve[f1ei]) ? 1 : 0;
const ON_MeshTopologyEdge& f1e = top.m_tope[f1.m_topei[f1ei]];
if (topvi == f1e.m_topvi[k])
{
if (0 == pass)
{
if (f1vi[f1ei] < mesh_vi[0])
mesh_vi[0] = f1vi[f1ei];
else if (f1vi[f1ei] > mesh_vi[1])
mesh_vi[1] = f1vi[f1ei];
}
else
{
// weld all faces in ngon to use vertex mesh_vi[0]
if (2 == f1ei && f1vi[2] == f1vi[3])
{
f1vi[2] = mesh_vi[0];
f1vi[3] = mesh_vi[0];
}
else
{
f1vi[f1ei] = mesh_vi[0];
}
}
}
}
if (3 == f1v_count)
f1vi[3] = f1vi[2];
}
}
if (0 == pass)
{
if (mesh_vi[0] == mesh_vi[1])
break;
}
else
{
// weld all faces in ngon to use vertex mesh_vi[0]
if (2 == fei && fvi[2] == fvi[3])
{
fvi[2] = mesh_vi[0];
fvi[3] = mesh_vi[0];
}
else
{
fvi[fei] = mesh_vi[0];
}
}
}
}
}
// clear the xmarks set above
const unsigned char mask = ~xmark;
for (unsigned nfi = 0; nfi < ngon_face_count; ++nfi)
fmarks[ngon_fi[nfi]] &= mask;
}
static void Internal_GrowNgon(
const ON_MeshTopology& top,
unsigned char* emarks,
unsigned char* fmarks,
unsigned char etest_mask,
unsigned char etest_result,
unsigned char ftest_mask,
unsigned char ftest_result,
const unsigned char merged_mark,
ON_SimpleArray<unsigned>& ngon_fi
)
{
if (ngon_fi.Count() <= 0)
return;
const int face_count = top.m_topf.Count();
for (int nfi = 0; nfi < ngon_fi.Count(); ++nfi)
fmarks[ngon_fi[nfi]] |= merged_mark;
etest_mask |= merged_mark;
etest_result &= ~merged_mark;
ftest_mask |= merged_mark;
ftest_result &= ~merged_mark;
for (int nfi = 0; nfi < ngon_fi.Count(); ++nfi)
{
const int f0i = ngon_fi[nfi];
const ON_MeshTopologyFace& f0 = top.m_topf[f0i];
const int f0_ecount = f0.IsTriangle() ? 3 : 4;
for (int fei = 0; fei < f0_ecount; ++fei)
{
const int ei = f0.m_topei[fei];
if (ei < 0 || ei > top.m_tope.Count())
continue;
if (etest_result != (emarks[ei] & etest_mask))
continue;
emarks[ei] |= merged_mark;
const ON_MeshTopologyEdge& e = top.m_tope[ei];
if (2 != e.m_topf_count || nullptr == e.m_topfi)
continue;
const int efi = (f0i != e.m_topfi[0]) ? 0 : 1;
const int f1i = e.m_topfi[efi];
if (f1i < 0 || f1i >= face_count)
continue;
if (ftest_result != (fmarks[f1i] & ftest_mask))
continue;
fmarks[f1i] |= merged_mark;
ngon_fi.Append(f1i);
}
}
}
static void Internal_AddMarkToFaceAndEdgesAndVertices(
const ON_Mesh& mesh,
const ON_MeshTopology& top,
const unsigned int* ngon0_map,
int face_index,
const unsigned char mark,
unsigned char* fmarks,
unsigned char* emarks,
unsigned char* vmarks
)
{
if (0 == mark || face_index < 0 || face_index > top.m_topf.Count())
return;
if (nullptr == fmarks && nullptr == emarks && nullptr == vmarks)
return;
unsigned fi_count = 1;
const int* fi_list = &face_index;
if (nullptr != ngon0_map)
{
const unsigned ni = ngon0_map[face_index];
if (ni < mesh.NgonUnsignedCount())
{
const ON_MeshNgon* ngon = mesh.Ngon(ni);
if (ngon->m_Fcount > 1 && nullptr != ngon->m_fi)
{
for (unsigned nfi = 0; nfi < ngon->m_Fcount; ++nfi)
{
if (face_index == (int)(ngon->m_fi[nfi]))
{
fi_count = ngon->m_Fcount;
fi_list = (const int*)ngon->m_fi;
break;
}
}
}
}
}
for (unsigned nfi = 0; nfi < fi_count; ++nfi)
{
int fi = fi_list[nfi];
if (fi < 0 || fi >= top.m_topf.Count())
continue;
if (nullptr != fmarks)
fmarks[fi] |= mark;
if (nullptr != emarks || nullptr != vmarks)
{
const ON_MeshTopologyFace& f = top.m_topf[fi];
//const int fecount = f.IsTriangle() ? 3 : 4;
const int tope_count = top.m_tope.Count();
const int topv_count = top.m_topv.Count();
for (int fei = 0; fei < 4; fei++)
{
const int ei = f.m_topei[fei];
if (ei < 0 || ei >= tope_count)
continue;
if (nullptr != emarks)
emarks[ei] |= mark;
if (nullptr != vmarks)
{
const ON_MeshTopologyEdge& e = top.m_tope[ei];
for (int evi = 0; evi < 2; ++evi)
{
const int topvi = e.m_topvi[evi];
if (topvi < 0 || topvi >= topv_count)
continue;
vmarks[topvi] |= mark;
}
}
}
}
}
}
static void Internal_AddMarkToNgonInteriorEdges(
const ON_Mesh& mesh,
const ON_MeshTopology& top,
const unsigned int* ngon0_map,
int face_index,
const unsigned char interior_edge_mark,
unsigned char* emarks
)
{
if (nullptr == ngon0_map)
return;
const int face_count = top.m_topf.Count();
const int edge_count = top.m_tope.Count();
if (face_index < 0 || face_index >= face_count)
return;
const unsigned ni = ngon0_map[face_index];
if (ni >= mesh.NgonUnsignedCount())
return;
const ON_MeshNgon* ngon = mesh.Ngon(ni);
if (nullptr == ngon || ngon->m_Fcount < 2 || nullptr == ngon->m_fi)
return;
for (unsigned nfi = 0; nfi < ngon->m_Fcount; ++nfi)
{
const int fi = (int)ngon->m_fi[nfi];
if (fi < 0 || fi >= face_count)
continue;
const ON_MeshTopologyFace& f = top.m_topf[fi];
const int f_ecount = f.IsTriangle() ? 3 : 4;
for (int fei = 0; fei < f_ecount; ++fei)
{
const int ei = f.m_topei[fei];
if (ei < 0 || ei >= edge_count)
continue;
const ON_MeshTopologyEdge& e = top.m_tope[ei];
if (2 != e.m_topf_count || nullptr == e.m_topfi)
continue;
const int f1i = e.m_topfi[(fi != e.m_topfi[0]) ? 0 : 1];
if (ni == ngon0_map[f1i])
emarks[ei] |= interior_edge_mark;
}
}
}
static void Internal_WeldAndAddNgon(
ON_Mesh& mesh,
const ON_MeshTopology& top,
const unsigned char xmark,
unsigned char* fmarks,
ON_SimpleArray<unsigned>& ngon_fi
)
{
if (ngon_fi.Count() < 2)
return;
// ngons must be welded and MergeFaces supports making an ngon across an unwelded edge.
Internal_WeldNgonCandiate(mesh,top,fmarks,xmark,ngon_fi);
if (
2 == ngon_fi.Count()
&&ngon_fi[0] != ngon_fi[1]
&& mesh.m_F[ngon_fi[0]].IsTriangle()
&& mesh.m_F[ngon_fi[1]].IsTriangle()
)
{
// make a single quad face
ON_MeshFace f[2] = {mesh.m_F[ngon_fi[0]],mesh.m_F[ngon_fi[1]]};
for (int f0ei = 0; f0ei < 3; ++f0ei)
{
const int f0vi[2] = { f[0].vi[f0ei],f[0].vi[(f0ei + 1) % 3] };
for (int f1ei = 0; f1ei < 3; ++f1ei)
{
const int f1vi[2] = { f[1].vi[f1ei],f[1].vi[(f1ei + 1) % 3] };
if (f0vi[0] != f1vi[1] || f0vi[1] != f1vi[0])
continue;
// merge triangles into a quad
ON_MeshFace q;
q.vi[f0ei] = f[0].vi[f0ei];
q.vi[(f0ei+1)%4] = f[1].vi[(f1ei+2)%3];
q.vi[(f0ei+2)%4] = f[0].vi[(f0ei+1)%3];
q.vi[(f0ei+3)%4] = f[0].vi[(f0ei+2)%3];
if (q.IsQuad() && q.IsValid(mesh.m_V.Count()))
{
mesh.m_F[ngon_fi[0]] = q;
q.vi[0] = -1;
q.vi[1] = -1;
q.vi[2] = -1;
q.vi[3] = -1;
mesh.m_F[ngon_fi[1]] = q;
ngon_fi.SetCount(1);
mesh.AddNgon(1, ngon_fi.Array());
return;
}
}
}
}
mesh.AddNgon(ngon_fi.UnsignedCount(), ngon_fi.Array());
}
unsigned int ON_Mesh::MergeFaceSets(
const ON_SimpleArray<ON_COMPONENT_INDEX>& ci_list
)
{
const unsigned int bailout_rc = ON_UNSET_UINT_INDEX;
const int ci_list_count = ci_list.UnsignedCount();
if (ci_list_count <= 0)
return bailout_rc;
const int vertex_count = VertexCount();
const int face_count = FaceCount();
if (face_count < 1 || vertex_count < 3)
return bailout_rc;
const ON_MeshTopology& top = Topology();
if (top.m_topf.Count() != face_count)
return bailout_rc;
const int topv_count = top.m_topv.Count();
const int tope_count = top.m_tope.Count();
if (topv_count < 3 || topv_count > vertex_count || tope_count < 3)
return bailout_rc;
ON_SimpleArray<unsigned char> vmarks_buffer(topv_count);
vmarks_buffer.SetCount(topv_count);
vmarks_buffer.Zero();
unsigned char* vmarks = vmarks_buffer.Array();
ON_SimpleArray<unsigned char> emarks_buffer(tope_count);
emarks_buffer.SetCount(tope_count);
emarks_buffer.Zero();
unsigned char* emarks = emarks_buffer.Array();
ON_SimpleArray<unsigned char> fmarks_buffer(face_count);
fmarks_buffer.SetCount(face_count);
fmarks_buffer.Zero();
unsigned char* fmarks = fmarks_buffer.Array();
const unsigned char in_ci_list_mark = 1;
const unsigned char vmark = 2;
const unsigned char emark = 4;
const unsigned char fmark = 8;
const unsigned char xmark = 0x10;
const unsigned char v_list_mark = vmark | in_ci_list_mark;
const unsigned char e_list_mark = emark | in_ci_list_mark;
const unsigned char f_list_mark = fmark | in_ci_list_mark;
const unsigned char v_x_mark = vmark | xmark;
const unsigned char e_x_mark = emark | xmark;
//const unsigned char f_x_mark = fmark | xmark;
const unsigned char merged_mark = 0x80; // face has been merged
ON_MeshNgonBuffer ngon_buffer;
unsigned ngon0_count = this->NgonCount();
const unsigned int* ngon0_map = ( ngon0_count > 0) ? this->NgonMap(true) : nullptr;
// Mark faces, edges, and vertices referenced in ci_list[]
// by setting bits in fmarks[], emarks[], and vmarks[]
for (int i = 0; i < ci_list_count; ++i)
{
ON_COMPONENT_INDEX ci = ci_list[i];
if (ci.m_index < 0)
continue;
switch (ci.m_type)
{
case ON_COMPONENT_INDEX::mesh_vertex:
// convert ci.m_index from a ON_COMPONENT_INDEX::mesh_vertex
// to a ON_COMPONENT_INDEX::meshtop_vertex index.
if (ci.m_index >= vertex_count)
break;
ci.m_index = top.m_topv_map[ci.m_index];
if (ci.m_index < 0)
break;
// no break here
case ON_COMPONENT_INDEX::meshtop_vertex:
if (ci.m_index < topv_count)
{
const ON_MeshTopologyVertex& v = top.m_topv[ci.m_index];
if (nullptr == v.m_topei || v.m_tope_count < 2)
break;
// validate the vertex
unsigned ni[2] = { ON_UNSET_UINT_INDEX,ON_UNSET_UINT_INDEX };
bool bValid = (v.m_tope_count >= 2 && nullptr != v.m_topei);
for (int vei = 0; vei < v.m_tope_count && bValid; ++vei)
{
int topei = v.m_topei[vei];
bValid = (topei >= 0 && topei < tope_count);
if (bValid)
{
const ON_MeshTopologyEdge& e = top.m_tope[topei];
bValid = (nullptr != e.m_topfi && e.m_topf_count >= 1 && e.m_topf_count <= 2);
for (int efi = 0; efi < e.m_topf_count && bValid; ++efi)
{
int fi = e.m_topfi[efi];
bValid = (fi >= 0 && fi < face_count);
if (nullptr != ngon0_map && ni[0] == ni[1])
{
const unsigned k = ngon0_map[fi];
const ON_MeshNgon* ngon = (k < ngon0_count) ? Ngon(k) : nullptr;
if (nullptr != ngon && ngon->m_Fcount >= 2 && nullptr != ngon->m_fi)
{
if (ON_UNSET_UINT_INDEX == ni[0])
ni[0] = k;
ni[1] = k;
}
else
{
// this face is not part of an ngon - stop checking
ni[0] = ON_UNSET_UINT_INDEX;
ni[1] = 0;
}
}
}
}
}
if (false == bValid)
break;
if (ON_UNSET_UINT_INDEX != ni[0] && ni[0] == ni[1])
break; // v is inside an ngon
vmarks[ci.m_index] |= v_list_mark;
// add a vmark to every face touching this vertex
for (int vei = 0; vei < v.m_tope_count; ++vei)
{
const ON_MeshTopologyEdge& e = top.m_tope[v.m_topei[vei]];
for (int efi = 0; efi < e.m_topf_count; ++efi)
Internal_AddMarkToFaceAndEdgesAndVertices(*this, top, ngon0_map, e.m_topfi[efi], vmark, fmarks, nullptr, nullptr);
}
}
break;
case ON_COMPONENT_INDEX::meshtop_edge:
if (ci.m_index < tope_count)
{
const ON_MeshTopologyEdge& e = top.m_tope[ci.m_index];
if (nullptr == e.m_topfi || 2 != e.m_topf_count)
continue;
if (e.m_topvi[0] < 0 || e.m_topvi[0] >= topv_count)
continue;
if (e.m_topvi[1] < 0 || e.m_topvi[1] >= topv_count)
continue;
if (e.m_topfi[0] < 0 || e.m_topfi[0] >= face_count)
continue;
if (e.m_topfi[1] < 0 || e.m_topfi[1] >= face_count)
continue;
if (nullptr != ngon0_map)
{
const unsigned ni[2] = { ngon0_map[e.m_topfi[0]],ngon0_map[e.m_topfi[1]] };
if (ni[0] < ngon0_count && ni[0] == ni[1])
continue; // edge is inside an ngon
}
emarks[ci.m_index] |= e_list_mark;
// add an emark to face tounching this edge
vmarks[e.m_topvi[0]] |= emark;
vmarks[e.m_topvi[1]] |= emark;
for (int efi = 0; efi < e.m_topf_count; ++efi)
{
const int fi = e.m_topfi[efi];
Internal_AddMarkToFaceAndEdgesAndVertices(*this, top, ngon0_map, fi, emark, fmarks, nullptr, nullptr);
Internal_AddMarkToNgonInteriorEdges(*this, top, ngon0_map, fi, e_list_mark, emarks);
}
}
break;
case ON_COMPONENT_INDEX::mesh_face:
if (ci.m_index >= face_count)
break;
if (ngon0_count > 0)
{
const unsigned ni = ngon0_map[ci.m_index];
if (ni < ngon0_count )
{
const ON_MeshNgon* ngon = Ngon(ni);
if (nullptr != ngon && ngon->m_Fcount > 1 && nullptr != ngon->m_fi)
{
ci.m_type = ON_COMPONENT_INDEX::mesh_ngon;
ci.m_index = (int)ni;
}
}
}
// no break here;
case ON_COMPONENT_INDEX::mesh_ngon:
if (ON_COMPONENT_INDEX::mesh_face == ci.m_type || ci.m_index < NgonCount())
{
const ON_MeshNgon* ngon =
(ON_COMPONENT_INDEX::mesh_face == ci.m_type)
? ngon_buffer.CreateFromMeshFaceIndex(this,ci.m_index)
: Ngon(ci.m_index);
if (nullptr == ngon || nullptr == ngon->m_fi || ngon->m_Fcount < 1)
break;
for (unsigned nfi = 0; nfi < ngon->m_Fcount; ++nfi)
{
int fi = ngon->m_fi[nfi];
if (fi >= 0 && fi < face_count)
{
const ON_MeshTopologyFace& f = top.m_topf[fi];
bool bValidFace = true;
for (int fei = 0; fei < 4 && bValidFace; fei++)
{
const int ei = f.m_topei[fei];
bValidFace = (ei >= 0 && ei < tope_count);
if (bValidFace)
{
const ON_MeshTopologyEdge& e = top.m_tope[ei];
for (int evi = 0; evi < 2 && bValidFace; ++evi)
{
int topvi = e.m_topvi[evi];
bValidFace = (topvi >= 0 && topvi < topv_count);
}
}
}
if (bValidFace)
{
fmarks[fi] |= f_list_mark;
Internal_AddMarkToFaceAndEdgesAndVertices(*this, top, ngon0_map, fi, fmark, fmarks, emarks, vmarks);
}
}
}
}
break;
default:
break;
}
}
// Resolve multi-marked faces and remove all marked faces from ngons.
unsigned int fmark_face_count = 0;
unsigned int vmark_face_count = 0;
unsigned int emark_face_count = 0;
for (int fi = 0; fi < face_count; ++fi)
{
if (f_list_mark == ( fmarks[fi] & f_list_mark ))
{
fmarks[fi] = (f_list_mark | xmark);
++fmark_face_count;
}
else if (emark == (fmarks[fi] & e_list_mark))
{
fmarks[fi] = emark;
++emark_face_count;
}
else if (vmark == (fmarks[fi] & v_list_mark))
{
fmarks[fi] = vmark;
++vmark_face_count;
}
else
{
fmarks[fi] = 0;
}
}
unsigned int emark_count = 0;
if (emark_face_count >= 2)
{
// ignore edges that are part of a boundary of a face in ci_list[]
emark_face_count = 0;
for (int ei = 0; ei < tope_count; ++ei)
{
unsigned char m = emarks[ei];
emarks[ei] = 0;
if (e_list_mark != m)
continue;
const ON_MeshTopologyEdge& e = top.m_tope[ei];
for (int efi = 0; efi < e.m_topf_count && 0 != m; ++efi)
{
const int fi = e.m_topfi[efi];
if (emark != fmarks[fi] && e_x_mark != fmarks[fi])
m = 0;
}
if (0 != m)
{
emarks[ei] = e_list_mark;
++emark_count;
for (int efi = 0; efi < e.m_topf_count && 0 != m; ++efi)
{
const int fi = e.m_topfi[efi];
Internal_AddMarkToFaceAndEdgesAndVertices(*this, top, ngon0_map, fi, emark, fmarks, emarks, vmarks);
if (emark == fmarks[fi])
{
fmarks[fi] |= xmark;
++emark_face_count;
}
}
}
}
}
if (0 == emark_count)
emark_face_count = 0;
unsigned int vmark_count = 0;
if (vmark_face_count >= 2)
{
// ignore vertices that belong to faces or edges in ci_list[]
vmark_face_count = 0;
for (int topvi = 0; topvi < topv_count; ++topvi)
{
unsigned char m = vmarks[topvi];
vmarks[topvi] = 0;
if (v_list_mark != m)
continue;
const ON_MeshTopologyVertex& v = top.m_topv[topvi];
for (int vei = 0; vei < v.m_tope_count && 0 != m; ++vei)
{
const int ei = v.m_topei[vei];
if (0 != (emarks[ei] & (fmark|emark)) )
m = 0;
else
{
const ON_MeshTopologyEdge& e = top.m_tope[ei];
for (int efi = 0; efi < e.m_topf_count && 0 != m; ++efi)
{
const int fi = e.m_topfi[efi];
if (vmark != fmarks[fi] && v_x_mark != fmarks[fi])
m = 0;
}
}
}
if (0 != m)
{
vmarks[topvi] = v_list_mark;
++vmark_count;
for (int vei = 0; vei < v.m_tope_count && 0 != m; ++vei)
{
const int ei = v.m_topei[vei];
emarks[ei] |= vmark;
const ON_MeshTopologyEdge& e = top.m_tope[ei];
for (int efi = 0; efi < e.m_topf_count && 0 != m; ++efi)
{
const int fi = e.m_topfi[efi];
Internal_AddMarkToFaceAndEdgesAndVertices(*this, top, ngon0_map, fi, vmark, fmarks, nullptr, vmarks);
Internal_AddMarkToNgonInteriorEdges(*this, top, ngon0_map, fi, vmark, emarks);
if (vmark == fmarks[fi])
{
fmarks[fi] |= xmark;
++vmark_face_count;
}
}
}
}
}
}
if (0 == vmark_count)
vmark_face_count = 0;
unsigned max_ngon_face_count = fmark_face_count;
if (max_ngon_face_count < emark_face_count)
max_ngon_face_count = emark_face_count;
if (max_ngon_face_count < vmark_face_count)
max_ngon_face_count = vmark_face_count;
if (max_ngon_face_count < 2 )
return bailout_rc;
ON_SimpleArray<unsigned> ngon_fi(max_ngon_face_count);
if (ngon0_count > 0)
{
// remove ngons that include marked faces
const unsigned int* ngon_map = ( ngon0_count > 0) ? this->NgonMap(true) : nullptr;
for (int fi = 0; fi < face_count; ++fi)
{
if (0 != (fmarks[fi] & xmark))
{
unsigned int ngon_index = (nullptr != ngon_map) ? ngon_map[fi] : ON_UNSET_UINT_INDEX;
if (ngon_index < ngon0_count)
ngon_fi.Append(ngon_index);
}
}
ngon_fi.QuickSortAndRemoveDuplicates(ON_CompareDecreasing< unsigned>);
for (int i = 0; i < ngon_fi.Count(); ++i)
this->RemoveNgon((unsigned)ngon_fi[i]);
this->RemoveEmptyNgons();
// Any ngons with indices >= ngon0_count are ngons added by the code below.
ngon0_count = this->NgonCount();
ngon_fi.SetCount(0);
}
// zero x mark in fmarks[] because it is used in Internal_WeldAndAddNgon()
for (int fi = 0; fi < face_count; ++fi)
fmarks[fi] &= ~xmark;
for (int fi = 0; fi < face_count; ++fi)
{
if (f_list_mark != (fmarks[fi] & (f_list_mark | merged_mark)))
continue; // face not in ci_list[] or already merged
// seed ngon with this face
ngon_fi.SetCount(0);
fmarks[fi] |= merged_mark;
ngon_fi.Append((unsigned)fi);
// grow ngon by adding other faces in ci_list[] that share and edge with a face in the ngon.
Internal_GrowNgon(
top, emarks, fmarks,
0, // etest_mask,
0, // etest_result,
f_list_mark, // ftest_mask,
f_list_mark, // ftest_result,
merged_mark,
ngon_fi
);
// weld ngon vertices and then add ngon
Internal_WeldAndAddNgon(*this, top, xmark, fmarks, ngon_fi);
}
for (int ei = 0; ei < tope_count; ++ei)
{
if ( e_list_mark != (emarks[ei] & (e_list_mark | merged_mark)) )
continue; // edge not in ci_list[] or already merged
emarks[ei] |= merged_mark;
const ON_MeshTopologyEdge& e = top.m_tope[ei];
// seed ngon with faces attached to this edge
ngon_fi.SetCount(0);
for (int efi = 0; efi < e.m_topf_count; ++efi)
{
const int fi = e.m_topfi[efi];
if (fi < 0 || fi >= face_count)
break;
if (emark != (fmarks[fi] & (emark|fmark|in_ci_list_mark|merged_mark)))
break;
fmarks[fi] |= merged_mark;
ngon_fi.Append(fi);
}
// grow ngon by jumping accross edges in ci_list[]
Internal_GrowNgon(
top, emarks, fmarks,
e_list_mark | fmark, // etest_mask,
e_list_mark, // etest_result,
emark | fmark, // ftest_mask,
emark, // ftest_result,
merged_mark,
ngon_fi
);
// weld ngon vertices and then add ngon
Internal_WeldAndAddNgon(*this, top, xmark, fmarks, ngon_fi);
}
unsigned int mark_mask = v_list_mark | merged_mark;
for (int vi = 0; vi < topv_count; ++vi)
{
if (v_list_mark != (vmarks[vi] & mark_mask))
continue; // edge not in ci_list[] or already merged
vmarks[vi] |= merged_mark;
const ON_MeshTopologyVertex& v = top.m_topv[vi];
// seed ngon with faces attached to this vertex
ngon_fi.SetCount(0);
for (int vei = 0; vei < v.m_tope_count; ++vei)
{
const ON_MeshTopologyEdge& e = top.m_tope[v.m_topei[vei]];
for (int efi = 0; efi < e.m_topf_count; ++efi)
{
int fi = e.m_topfi[efi];
if (fi < 0 || fi >= face_count)
continue;
if (vmark != (fmarks[fi] & (vmark|emark|fmark|in_ci_list_mark|merged_mark)))
continue;
fmarks[fi] |= merged_mark;
ngon_fi.Append(fi);
}
}
// grow ngon by jumping across edges with a vmark
Internal_GrowNgon(
top, emarks, fmarks,
vmark, // etest_mask,
vmark, // etest_result,
vmark | emark | fmark, // ftest_mask,
vmark, // ftest_result,
merged_mark,
ngon_fi
);
// weld ngon vertices and then add ngon
Internal_WeldAndAddNgon(*this, top, xmark, fmarks, ngon_fi);
}
// clean up
DeleteComponents(
nullptr,
0,
true, // bIgnoreInvalidComponents
true, // bRemoveDegenerateFaces
true, // bRemoveUnusedVertices
true // bRemoveEmptyNgons
);
const unsigned ngon1_count = this->NgonCount();
// return index where new ngons begin
return (ngon1_count > ngon0_count) ? ngon0_count : bailout_rc;
}