mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-01 03:26:09 +08:00
1564 lines
41 KiB
C++
1564 lines
41 KiB
C++
//
|
|
// Copyright (c) 1993-2022 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
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// SwapMeshEdge()
|
|
//
|
|
|
|
bool ON_Mesh::SwapEdge_Helper( int topei, bool bTestOnly )
|
|
{
|
|
ON_Mesh& mesh = *this;
|
|
const ON_MeshTopology& top = mesh.Topology();
|
|
const int F_count = mesh.FaceCount();
|
|
const int V_count = mesh.VertexCount();
|
|
const int topv_count = top.m_topv.Count();
|
|
//const int tope_count = top.m_tope.Count();
|
|
|
|
if ( topei < 0 || topei >= top.m_tope.Count() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( top.m_topf.Count() != F_count )
|
|
return false;
|
|
|
|
const ON_MeshTopologyEdge& tope = top.m_tope[topei];
|
|
|
|
if ( tope.m_topf_count != 2
|
|
|| tope.m_topvi[0] == tope.m_topvi[1]
|
|
|| tope.m_topvi[0] < 0
|
|
|| tope.m_topvi[1] < 0
|
|
|| tope.m_topvi[0] >= topv_count
|
|
|| tope.m_topvi[1] >= topv_count )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int fi0 = tope.m_topfi[0];
|
|
int fi1 = tope.m_topfi[1];
|
|
if ( fi0 < 0 || fi0 >= F_count || fi1 < 0 || fi1 >= F_count || fi0 == fi1 )
|
|
return false;
|
|
|
|
const ON_MeshFace& f0 = mesh.m_F[fi0];
|
|
const ON_MeshFace& f1 = mesh.m_F[fi1];
|
|
if ( !f0.IsValid(V_count) )
|
|
return false;
|
|
if ( !f1.IsValid(V_count) )
|
|
return false;
|
|
if ( !f0.IsTriangle() )
|
|
return false;
|
|
if ( !f1.IsTriangle() )
|
|
return false;
|
|
|
|
const ON_MeshTopologyFace& topf0 = top.m_topf[fi0];
|
|
const ON_MeshTopologyFace& topf1 = top.m_topf[fi1];
|
|
|
|
int fei0;
|
|
for ( fei0 = 0; fei0 < 3; fei0++ )
|
|
{
|
|
if ( topf0.m_topei[fei0] == topei )
|
|
break;
|
|
}
|
|
if ( fei0 >= 3 )
|
|
return false;
|
|
|
|
int f0_vi0 = f0.vi[(fei0+3)%4];
|
|
int f0_vi1 = f0.vi[fei0];
|
|
int f0_vi2 = f0.vi[(fei0+1)%3];
|
|
|
|
int fei1;
|
|
for ( fei1 = 0; fei1 < 3; fei1++ )
|
|
{
|
|
if ( topf1.m_topei[fei1] == topei )
|
|
break;
|
|
}
|
|
if ( fei1 >= 3 )
|
|
return false;
|
|
|
|
int f1_vi0 = f1.vi[(fei1+3)%4];
|
|
int f1_vi1 = f1.vi[fei1];
|
|
int f1_vi2 = f1.vi[(fei1+1)%3];
|
|
|
|
if ( topf0.m_reve[fei0] == topf1.m_reve[fei1] )
|
|
return false;
|
|
if ( f0_vi0 != f1_vi1 )
|
|
return false;
|
|
if ( f0_vi1 != f1_vi0 )
|
|
return false;
|
|
|
|
const ON_MeshTopologyVertex& topv0 = top.m_topv[tope.m_topvi[0]];
|
|
const ON_MeshTopologyVertex& topv1 = top.m_topv[tope.m_topvi[1]];
|
|
if ( topv0.m_v_count < 1 || topv1.m_v_count < 1 )
|
|
{
|
|
return false;
|
|
}
|
|
if ( topv0.m_vi[0] < 0 || topv0.m_vi[0] >= V_count )
|
|
{
|
|
return false;
|
|
}
|
|
if ( topv1.m_vi[0] < 0 || topv1.m_vi[0] >= V_count )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( bTestOnly )
|
|
return true;
|
|
|
|
|
|
ON_MeshFace newf0;
|
|
ON_MeshFace newf1;
|
|
newf0.vi[0] = f0_vi1;
|
|
newf0.vi[1] = f0_vi2;
|
|
newf0.vi[2] = f1_vi2;
|
|
newf0.vi[3] = newf0.vi[2];
|
|
|
|
newf1.vi[0] = f1_vi1;
|
|
newf1.vi[1] = f1_vi2;
|
|
newf1.vi[2] = f0_vi2;
|
|
newf1.vi[3] = newf1.vi[2];
|
|
|
|
mesh.m_F[fi0] = newf0;
|
|
mesh.m_F[fi1] = newf1;
|
|
|
|
mesh.DestroyTopology();
|
|
mesh.DestroyPartition();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ON_Mesh::IsSwappableEdge(int topei )
|
|
{
|
|
return const_cast<ON_Mesh*>(this)->SwapEdge_Helper( topei, true );
|
|
}
|
|
|
|
bool ON_Mesh::SwapEdge( int topei )
|
|
{
|
|
return SwapEdge_Helper( topei, false );
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// CollapseMeshEdge()
|
|
//
|
|
|
|
// DO NOT PUT THIS CLASS IN A HEADER FILE
|
|
class ON__MESHEDGE
|
|
{
|
|
public:
|
|
// ON_Mesh m_V[] indices
|
|
int vi0;
|
|
int vi1; // always > vi0
|
|
|
|
// ON_MeshTopology m_topvi[] indices
|
|
int topvi0;
|
|
int topvi1;
|
|
|
|
// ON_Mesh m_V[] index of new vertex
|
|
int newvi;
|
|
|
|
// location of vertex that will replace the edge
|
|
ON_3dPoint newV;
|
|
ON_3fVector newN;
|
|
ON_2fPoint newT;
|
|
};
|
|
|
|
// DO NOT PUT THIS CLASS IN A HEADER FILE
|
|
class ON__NEWVI
|
|
{
|
|
public:
|
|
// ON_Mesh m_V[] indices
|
|
int oldvi;
|
|
int newvi;
|
|
};
|
|
|
|
typedef int (*QSORTCMPFUNC)(const void*,const void*);
|
|
|
|
static int CompareMESHEDGE( const ON__MESHEDGE* a, const ON__MESHEDGE* b )
|
|
{
|
|
// sort based on "vi0" and vi1" values (vi0 is always < vi1)
|
|
int i = (a->vi0 - b->vi0);
|
|
if ( 0 == i )
|
|
{
|
|
i = a->vi1 - b->vi1;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
static int CompareNEWVI( const ON__NEWVI* a, const ON__NEWVI* b )
|
|
{
|
|
// sort based on "oldvi" value
|
|
return a->oldvi - b->oldvi;
|
|
}
|
|
|
|
static int CompareInt( const void* a, const void* b )
|
|
{
|
|
return ( *((int*)a) - *((int*)b) );
|
|
}
|
|
|
|
static void RemoveFaceOrVertexFromNgon(ON_Mesh& mesh, const unsigned int faceidx, const int* vertidx = nullptr)
|
|
{
|
|
unsigned int NgonIdx = mesh.NgonIndexFromFaceIndex(faceidx);
|
|
const ON_MeshNgon* pOldNgon = nullptr;
|
|
pOldNgon = mesh.Ngon(NgonIdx);
|
|
|
|
if (nullptr != pOldNgon)
|
|
{
|
|
// Make a copy of the ngon we need to update.
|
|
ON_MeshNgon NewNgon;
|
|
NewNgon.m_Vcount = pOldNgon->m_Vcount;
|
|
NewNgon.m_Fcount = pOldNgon->m_Fcount;
|
|
|
|
NewNgon.m_vi = (unsigned int*)onmalloc((NewNgon.m_Vcount+ NewNgon.m_Fcount)*sizeof(unsigned int));
|
|
memcpy(NewNgon.m_vi, pOldNgon->m_vi, NewNgon.m_Vcount*sizeof(unsigned int));
|
|
|
|
NewNgon.m_fi = &NewNgon.m_vi[NewNgon.m_Vcount];
|
|
memcpy(NewNgon.m_fi, pOldNgon->m_fi, NewNgon.m_Fcount*sizeof(unsigned int));
|
|
|
|
if (nullptr != NewNgon.m_vi && nullptr != NewNgon.m_fi)
|
|
{
|
|
unsigned int ct = 0;
|
|
if (nullptr == vertidx)
|
|
{
|
|
while (NewNgon.m_Fcount > ct)
|
|
{
|
|
// find the face index in the ngon's list of faces
|
|
if (faceidx == NewNgon.m_fi[ct])
|
|
break;
|
|
ct++;
|
|
}
|
|
|
|
if (NewNgon.m_Fcount > ct)
|
|
{
|
|
// if ct isn't bogus and is not at the end already, shift the indexes
|
|
if (NewNgon.m_Fcount - 1 > ct)
|
|
memcpy(&NewNgon.m_fi[ct], &NewNgon.m_fi[ct + 1], (NewNgon.m_Fcount - (ct + 1))*sizeof(NewNgon.m_fi[0]));
|
|
|
|
NewNgon.m_Fcount--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unsigned int idx = ON_UNSET_UINT_INDEX;
|
|
while (NewNgon.m_Vcount > ct)
|
|
{
|
|
idx = *vertidx;
|
|
// find the vertex index in the ngon's list of vertexes
|
|
if (idx == NewNgon.m_vi[ct])
|
|
break;
|
|
ct++;
|
|
}
|
|
|
|
if (NewNgon.m_Vcount > ct)
|
|
{
|
|
// if vi_idx isn't bogus and is not at the end already, shift the indexes
|
|
if (NewNgon.m_Vcount - 1 > ct)
|
|
memcpy(&NewNgon.m_vi[ct], &NewNgon.m_vi[ct + 1], (NewNgon.m_Vcount - (ct + 1))*sizeof(NewNgon.m_vi[0]));
|
|
|
|
NewNgon.m_Vcount--;
|
|
}
|
|
}
|
|
|
|
mesh.ModifyNgon(NgonIdx, &NewNgon);
|
|
}
|
|
|
|
if (nullptr != NewNgon.m_vi)
|
|
onfree(NewNgon.m_vi);
|
|
}
|
|
}
|
|
|
|
bool ON_Mesh::CollapseEdge( int topei )
|
|
{
|
|
ON_Mesh& mesh = *this;
|
|
|
|
ON__MESHEDGE me;
|
|
memset(&me,0,sizeof(me));
|
|
const ON_MeshTopology& top = mesh.Topology();
|
|
const int F_count = mesh.FaceCount();
|
|
const int V_count = mesh.VertexCount();
|
|
const int topv_count = top.m_topv.Count();
|
|
//const int tope_count = top.m_tope.Count();
|
|
|
|
if ( topei < 0 || topei >= top.m_tope.Count() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const ON_MeshTopologyEdge& tope = top.m_tope[topei];
|
|
|
|
if ( tope.m_topf_count < 1
|
|
|| tope.m_topvi[0] == tope.m_topvi[1]
|
|
|| tope.m_topvi[0] < 0
|
|
|| tope.m_topvi[1] < 0
|
|
|| tope.m_topvi[0] >= topv_count
|
|
|| tope.m_topvi[1] >= topv_count )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const ON_MeshTopologyVertex& topv0 = top.m_topv[tope.m_topvi[0]];
|
|
const ON_MeshTopologyVertex& topv1 = top.m_topv[tope.m_topvi[1]];
|
|
if ( topv0.m_v_count < 1 || topv1.m_v_count < 1 )
|
|
{
|
|
return false;
|
|
}
|
|
if ( topv0.m_vi[0] < 0 || topv0.m_vi[0] >= V_count )
|
|
{
|
|
return false;
|
|
}
|
|
if ( topv1.m_vi[0] < 0 || topv1.m_vi[0] >= V_count )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// create a ON__MESHEDGE for each face (usually one or two) that uses the edge
|
|
ON__MESHEDGE* me_list = (ON__MESHEDGE*)alloca(tope.m_topf_count*sizeof(me_list[0]));
|
|
int me_list_count = 0;
|
|
int efi;
|
|
for ( efi = 0; efi < tope.m_topf_count; efi++ )
|
|
{
|
|
int fi = tope.m_topfi[efi];
|
|
if ( fi < 0 || fi >= F_count )
|
|
continue;
|
|
const ON_MeshFace& f = mesh.m_F[fi];
|
|
if ( !f.IsValid(V_count) )
|
|
continue;
|
|
me.vi1 = f.vi[3];
|
|
me.topvi1 = top.m_topv_map[me.vi1];
|
|
int fvi;
|
|
for ( fvi = 0; fvi < 4; fvi++ )
|
|
{
|
|
me.vi0 = me.vi1;
|
|
me.topvi0 = me.topvi1;
|
|
me.vi1 = f.vi[fvi];
|
|
me.topvi1 = top.m_topv_map[me.vi1];
|
|
if ( me.vi0 != me.vi1 )
|
|
{
|
|
if ( (me.topvi0 == tope.m_topvi[0] && me.topvi1 == tope.m_topvi[1])
|
|
|| (me.topvi0 == tope.m_topvi[1] && me.topvi1 == tope.m_topvi[0]) )
|
|
{
|
|
if ( me.vi0 > me.vi1 )
|
|
{
|
|
int i = me.vi0; me.vi0 = me.vi1; me.vi1 = i;
|
|
i = me.topvi0; me.topvi0 = me.topvi1; me.topvi1 = i;
|
|
}
|
|
me_list[me_list_count++] = me;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (me_list_count<1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Sort me_list[] so edges using same vertices are adjacent
|
|
// to each other in the list. This is needed so that non-manifold
|
|
// crease edges will be properly collapsed.
|
|
ON_qsort(me_list,me_list_count,sizeof(me_list[0]),(QSORTCMPFUNC)CompareMESHEDGE);
|
|
|
|
// create new vertex or vertices that edge will be
|
|
// collapsed to.
|
|
mesh.m_C.Destroy();
|
|
mesh.m_K.Destroy();
|
|
|
|
int mei;
|
|
bool bHasVertexNormals = mesh.HasVertexNormals();
|
|
bool bHasTextureCoordinates = mesh.HasTextureCoordinates();
|
|
bool bHasFaceNormals = mesh.HasFaceNormals();
|
|
if ( topv0.m_v_count == 1 || topv1.m_v_count == 1 )
|
|
{
|
|
// a single new vertex
|
|
ON_Line Vline(ON_3dPoint::Origin,ON_3dPoint::Origin);
|
|
ON_Line Tline(ON_3dPoint::Origin,ON_3dPoint::Origin);
|
|
ON_3dVector N0(0,0,0);
|
|
ON_3dVector N1(0,0,0);
|
|
ON_3dPoint P;
|
|
int vi, tvi, cnt;
|
|
|
|
int newvi = topv0.m_vi[0];
|
|
|
|
cnt = 0;
|
|
for ( tvi = 0; tvi < topv0.m_v_count; tvi++ )
|
|
{
|
|
vi = topv0.m_vi[tvi];
|
|
if ( vi < 0 || vi > V_count )
|
|
continue;
|
|
if ( vi < newvi )
|
|
newvi = vi;
|
|
cnt++;
|
|
P = mesh.Vertex(vi);
|
|
Vline.from += P;
|
|
if ( bHasVertexNormals )
|
|
{
|
|
N0 += ON_3dVector(mesh.m_N[vi]);
|
|
}
|
|
if ( bHasTextureCoordinates )
|
|
{
|
|
P = mesh.m_T[vi];
|
|
Tline.from += P;
|
|
}
|
|
}
|
|
|
|
if (cnt > 1)
|
|
{
|
|
double s = 1.0/((double)cnt);
|
|
Vline.from.x *= s;
|
|
Vline.from.y *= s;
|
|
Vline.from.z *= s;
|
|
Tline.from.x *= s;
|
|
Tline.from.y *= s;
|
|
Tline.from.z *= s;
|
|
N0 = s*N0;
|
|
}
|
|
|
|
cnt = 0;
|
|
for (tvi = 0; tvi < topv1.m_v_count; tvi++)
|
|
{
|
|
vi = topv1.m_vi[tvi];
|
|
if (vi < 0 || vi > V_count)
|
|
continue;
|
|
if (vi < newvi)
|
|
newvi = vi;
|
|
cnt++;
|
|
P = mesh.Vertex(vi);
|
|
Vline.to += P;
|
|
if ( bHasVertexNormals )
|
|
{
|
|
N1 += ON_3dVector(mesh.m_N[vi]);
|
|
}
|
|
if ( bHasTextureCoordinates )
|
|
{
|
|
P = mesh.m_T[vi];
|
|
Tline.to += P;
|
|
}
|
|
}
|
|
|
|
if (cnt > 1)
|
|
{
|
|
double s = 1.0/((double)cnt);
|
|
Vline.to.x *= s;
|
|
Vline.to.y *= s;
|
|
Vline.to.z *= s;
|
|
Tline.to.x *= s;
|
|
Tline.to.y *= s;
|
|
Tline.to.z *= s;
|
|
N1 = s*N1;
|
|
}
|
|
|
|
ON_3fPoint newV(Vline.PointAt(0.5));
|
|
ON_3fVector newN(ON_3fVector::ZeroVector);
|
|
ON_2fPoint newT(ON_2fPoint::Origin);
|
|
if ( bHasVertexNormals )
|
|
{
|
|
N0.Unitize();
|
|
N1.Unitize();
|
|
ON_3dVector N = N0+N1;
|
|
if ( !N.Unitize() )
|
|
{
|
|
N = (topv0.m_v_count == 1) ? mesh.m_N[topv0.m_vi[0]] :mesh.m_N[topv1.m_vi[0]];
|
|
}
|
|
newN = N;
|
|
}
|
|
if ( bHasTextureCoordinates )
|
|
{
|
|
newT = Tline.PointAt(0.5);
|
|
}
|
|
for ( mei = 0; mei < me_list_count; mei++ )
|
|
{
|
|
me_list[mei].newvi = newvi;
|
|
me_list[mei].newV = newV;
|
|
me_list[mei].newN = newN;
|
|
me_list[mei].newT = newT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// collapsing a "crease" edge - attempt to preserve
|
|
// the crease.
|
|
memset(&me,0,sizeof(me));
|
|
me.vi0 = -1;
|
|
me.vi1 = -1;
|
|
for ( mei = 0; mei < me_list_count; mei++ )
|
|
{
|
|
// cook up new vertex
|
|
me_list[mei].newvi = mesh.VertexCount();
|
|
me = me_list[mei];
|
|
ON_Line line;
|
|
line.from = mesh.Vertex(me.vi0);
|
|
line.to = mesh.Vertex(me.vi1);
|
|
me.newV = line.PointAt(0.5);
|
|
if ( bHasVertexNormals )
|
|
{
|
|
ON_3dVector N0(mesh.m_N[me.vi0]);
|
|
ON_3dVector N1(mesh.m_N[me.vi1]);
|
|
ON_3dVector N = N0 + N1;
|
|
if ( !N.Unitize() )
|
|
N = N0;
|
|
me.newN = N;
|
|
}
|
|
if ( bHasTextureCoordinates )
|
|
{
|
|
line.from = mesh.m_T[me.vi0];
|
|
line.to = mesh.m_T[me.vi1];
|
|
me.newT = line.PointAt(0.5);
|
|
}
|
|
me.newvi = (me.vi0 < me.vi1) ? me.vi0 : me.vi1;
|
|
|
|
me_list[mei].newvi = me.newvi;
|
|
me_list[mei].newV = me.newV;
|
|
me_list[mei].newN = me.newN;
|
|
me_list[mei].newT = me.newT;
|
|
}
|
|
}
|
|
|
|
// We are done averaging old mesh values.
|
|
// Change values in mesh m_V[], m_N[] and m_T[] arrays.
|
|
for ( mei = 0; mei < me_list_count; mei++ )
|
|
{
|
|
// We need to set the vertex location of all mesh vertices associated with the topological
|
|
// edge or we risk "tearing apart" vertexes at corners where there are more than 2 mesh vertexes
|
|
// associated with the topological vertex. Don't to this for anything but the location.
|
|
int i;
|
|
for (i = 0; top.m_topv[me_list[mei].topvi0].m_v_count > i; i++)
|
|
mesh.SetVertex(top.m_topv[me_list[mei].topvi0].m_vi[i], me_list[mei].newV);
|
|
for (i = 0; top.m_topv[me_list[mei].topvi1].m_v_count > i; i++)
|
|
mesh.SetVertex(top.m_topv[me_list[mei].topvi1].m_vi[i], me_list[mei].newV);
|
|
|
|
if ( bHasVertexNormals )
|
|
{
|
|
mesh.m_N[me_list[mei].vi0] = me_list[mei].newN;
|
|
mesh.m_N[me_list[mei].vi1] = me_list[mei].newN;
|
|
}
|
|
if ( bHasTextureCoordinates )
|
|
{
|
|
mesh.m_T[me_list[mei].vi0] = me_list[mei].newT;
|
|
mesh.m_T[me_list[mei].vi1] = me_list[mei].newT;
|
|
}
|
|
}
|
|
|
|
// make a map of old to new
|
|
int old2new_map_count = 0;
|
|
ON__NEWVI* old2new_map = (ON__NEWVI*)alloca(2*me_list_count*sizeof(old2new_map[0]));
|
|
|
|
for ( mei = 0; mei < me_list_count; mei++ )
|
|
{
|
|
old2new_map[old2new_map_count].oldvi = me_list[mei].vi0;
|
|
old2new_map[old2new_map_count].newvi = me_list[mei].newvi;
|
|
old2new_map_count++;
|
|
old2new_map[old2new_map_count].oldvi = me_list[mei].vi1;
|
|
old2new_map[old2new_map_count].newvi = me_list[mei].newvi;
|
|
old2new_map_count++;
|
|
}
|
|
|
|
// sort old2new_map[] so we can use a fast bsearch() call
|
|
// to update faces.
|
|
ON_qsort(old2new_map,old2new_map_count,sizeof(old2new_map[0]),(QSORTCMPFUNC)CompareNEWVI);
|
|
|
|
// count faces that use the vertices that are being changed
|
|
int bad_fi_count = 0;
|
|
int topv_end, vei, fi, fvi23, fvi;
|
|
ON__NEWVI nvi;
|
|
|
|
for ( topv_end = 0; topv_end < 2; topv_end++ )
|
|
{
|
|
const ON_MeshTopologyVertex& topv = (topv_end) ? topv1 : topv0;
|
|
for ( vei = 0; vei < topv.m_tope_count; vei++ )
|
|
{
|
|
topei = topv.m_topei[vei];
|
|
if ( topei < 0 && topei >= top.m_tope.Count() )
|
|
continue;
|
|
bad_fi_count += top.m_tope[topei].m_topf_count;
|
|
}
|
|
}
|
|
int* bad_fi = (int*)alloca(bad_fi_count*sizeof(*bad_fi));
|
|
bad_fi_count = 0;
|
|
|
|
// Go through all the faces that use the vertices at the
|
|
// ends of the edge and update the vi[] values to use the
|
|
// new vertices.
|
|
for ( topv_end = 0; topv_end < 2; topv_end++ )
|
|
{
|
|
const ON_MeshTopologyVertex& topv = (topv_end) ? topv1 : topv0;
|
|
for ( vei = 0; vei < topv.m_tope_count; vei++ )
|
|
{
|
|
topei = topv.m_topei[vei];
|
|
if ( topei < 0 && topei >= top.m_tope.Count() )
|
|
continue;
|
|
const ON_MeshTopologyEdge& e = top.m_tope[topei];
|
|
for ( efi = 0; efi < e.m_topf_count; efi++ )
|
|
{
|
|
fi = e.m_topfi[efi];
|
|
if ( fi < 0 || fi >= F_count )
|
|
continue;
|
|
bool bChangedFace = false;
|
|
ON_MeshFace& f = mesh.m_F[fi];
|
|
for ( fvi = 0; fvi < 4; fvi++ )
|
|
{
|
|
nvi.oldvi = f.vi[fvi];
|
|
ON__NEWVI* p = (ON__NEWVI*)bsearch(&nvi,old2new_map,old2new_map_count,sizeof(old2new_map[0]),(QSORTCMPFUNC)CompareNEWVI);
|
|
if ( 0 != p && p->oldvi != p->newvi)
|
|
{
|
|
RemoveFaceOrVertexFromNgon(mesh, fi, &p->oldvi);
|
|
f.vi[fvi] = p->newvi;
|
|
bChangedFace = true;
|
|
}
|
|
}
|
|
if ( bChangedFace )
|
|
{
|
|
if ( !f.IsValid(V_count) )
|
|
{
|
|
if ( f.vi[3] == f.vi[0] )
|
|
{
|
|
f.vi[0] = f.vi[1];
|
|
f.vi[1] = f.vi[2];
|
|
f.vi[2] = f.vi[3];
|
|
}
|
|
else if ( f.vi[0] == f.vi[1] )
|
|
{
|
|
fvi23 = f.vi[0];
|
|
f.vi[0] = f.vi[2];
|
|
f.vi[1] = f.vi[3];
|
|
f.vi[2] = fvi23;
|
|
f.vi[3] = fvi23;
|
|
}
|
|
else if ( f.vi[1] == f.vi[2] )
|
|
{
|
|
fvi23 = f.vi[1];
|
|
f.vi[1] = f.vi[0];
|
|
f.vi[0] = f.vi[3];
|
|
f.vi[2] = fvi23;
|
|
f.vi[3] = fvi23;
|
|
}
|
|
if ( f.vi[0] == f.vi[1] || f.vi[1] == f.vi[2] || f.vi[2] == f.vi[0] || f.vi[2] != f.vi[3] )
|
|
{
|
|
bad_fi[bad_fi_count++] = fi;
|
|
}
|
|
}
|
|
if ( bHasFaceNormals )
|
|
{
|
|
// invalid faces are removed below
|
|
ON_3fVector a, b, n;
|
|
a = mesh.Vertex(f.vi[2]) - mesh.Vertex(f.vi[0]);
|
|
b = mesh.Vertex(f.vi[3]) - mesh.Vertex(f.vi[1]);
|
|
n = ON_CrossProduct( a, b );
|
|
n.Unitize();
|
|
mesh.m_FN[fi] = n;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bad_fi_count > 0 )
|
|
{
|
|
// remove collapsed faces from mesh
|
|
ON_qsort(bad_fi,bad_fi_count,sizeof(bad_fi[0]),CompareInt);
|
|
int bfi = 1;
|
|
int dest_fi = bad_fi[0];
|
|
unsigned int* face_index_map = nullptr;
|
|
ON_SimpleArray<unsigned int> face_index_map_array;
|
|
if (mesh.HasNgons())
|
|
{
|
|
face_index_map_array.Reserve(mesh.m_F.UnsignedCount());
|
|
face_index_map = face_index_map_array.Array();
|
|
for (fi = 0; fi < dest_fi; fi++)
|
|
face_index_map[fi] = fi;
|
|
for (fi = dest_fi; fi < mesh.m_F.Count(); fi++)
|
|
face_index_map[fi] = ON_UNSET_UINT_INDEX;
|
|
}
|
|
|
|
for ( fi = dest_fi+1; fi < F_count && bfi < bad_fi_count; fi++ )
|
|
{
|
|
if ( fi == bad_fi[bfi] )
|
|
{
|
|
bfi++;
|
|
if (nullptr != face_index_map)
|
|
face_index_map[fi] = ON_UNSET_UINT_INDEX;
|
|
}
|
|
else
|
|
{
|
|
// remove collapsed faces from ngons
|
|
if (nullptr != face_index_map)
|
|
face_index_map[fi] = dest_fi;
|
|
mesh.m_F[dest_fi++] = mesh.m_F[fi];
|
|
}
|
|
}
|
|
while (fi<F_count)
|
|
{
|
|
if (nullptr != face_index_map)
|
|
face_index_map[fi] = dest_fi;
|
|
mesh.m_F[dest_fi++] = mesh.m_F[fi++];
|
|
}
|
|
mesh.m_F.SetCount(dest_fi);
|
|
const unsigned int new_mesh_F_count = mesh.m_F.UnsignedCount();
|
|
|
|
|
|
bool bUpdateNgonMap = false;
|
|
for ( unsigned int ni = 0; ni < mesh.m_Ngon.UnsignedCount(); ni++ )
|
|
{
|
|
ON_MeshNgon* ngon = m_Ngon[ni];
|
|
if (nullptr == ngon)
|
|
continue;
|
|
unsigned int new_ngon_F_count = 0;
|
|
for (unsigned int nfi = 0; nfi < ngon->m_Fcount; nfi++)
|
|
{
|
|
unsigned int new_fi = face_index_map[ngon->m_fi[nfi]];
|
|
if (new_fi >= new_mesh_F_count)
|
|
continue;
|
|
|
|
ngon->m_fi[new_ngon_F_count++] = new_fi;
|
|
}
|
|
|
|
ON_MeshNgon* new_ngon;
|
|
if (0 == new_ngon_F_count)
|
|
{
|
|
new_ngon = nullptr;
|
|
}
|
|
else if (new_ngon_F_count != ngon->m_Fcount)
|
|
{
|
|
new_ngon = m_NgonAllocator.AllocateNgon(ngon->m_Vcount, new_ngon_F_count);
|
|
// copy old ngon vertex list to new ngon
|
|
for (unsigned int nvi_for_loop = 0; nvi_for_loop < new_ngon->m_Vcount; nvi_for_loop++)
|
|
new_ngon->m_vi[nvi_for_loop] = ngon->m_vi[nvi_for_loop];
|
|
// update new ngon face list
|
|
for (unsigned int nfi = 0; nfi < new_ngon->m_Fcount; nfi++)
|
|
new_ngon->m_fi[nfi] = ngon->m_fi[nfi];
|
|
}
|
|
else
|
|
{
|
|
continue;
|
|
}
|
|
bUpdateNgonMap = true;
|
|
m_Ngon[ni] = new_ngon;
|
|
mesh.m_NgonAllocator.DeallocateNgon(ngon);
|
|
}
|
|
if (bUpdateNgonMap)
|
|
{
|
|
mesh.m_NgonMap.Destroy();
|
|
mesh.CreateNgonMap();
|
|
}
|
|
|
|
if ( bHasFaceNormals )
|
|
{
|
|
bfi = 1;
|
|
dest_fi = bad_fi[0];
|
|
for ( fi = dest_fi+1; fi < F_count && bfi < bad_fi_count; fi++ )
|
|
{
|
|
if ( fi == bad_fi[bfi] )
|
|
{
|
|
bfi++;
|
|
}
|
|
else
|
|
{
|
|
mesh.m_FN[dest_fi++] = mesh.m_FN[fi];
|
|
}
|
|
}
|
|
while (fi<F_count)
|
|
{
|
|
mesh.m_FN[dest_fi++] = mesh.m_FN[fi++];
|
|
}
|
|
mesh.m_FN.SetCount(dest_fi);
|
|
}
|
|
}
|
|
|
|
mesh.Compact();
|
|
mesh.DestroyTopology();
|
|
mesh.DestroyPartition();
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//static int NewIndex( int old_index, int missing_index_count, const int* missing_index_list )
|
|
//{
|
|
// // tool to calculate new indices when some elements are deleted from a list
|
|
// int delta;
|
|
// for ( delta = 0; delta < missing_index_count; delta++ )
|
|
// {
|
|
// if ( old_index < missing_index_list[delta] )
|
|
// break;
|
|
// }
|
|
// return old_index - delta;
|
|
//}
|
|
|
|
bool ON_Mesh::DeleteFace( int meshfi )
|
|
{
|
|
// Do NOT add a call Compact() in this function.
|
|
// Compact() is slow and this function may be called
|
|
// many times in sequence.
|
|
// It is the callers responsibility to call Compact()
|
|
// when it is needed.
|
|
bool rc = false;
|
|
|
|
if ( meshfi >= 0 && meshfi < m_F.Count() )
|
|
{
|
|
if ( m_top.m_topf.Count() > 0 )
|
|
{
|
|
DestroyTopology();
|
|
}
|
|
DestroyPartition();
|
|
DestroyTree();
|
|
if ( m_FN.Count() == m_F.Count() )
|
|
{
|
|
m_FN.Remove(meshfi);
|
|
}
|
|
m_F.Remove(meshfi);
|
|
|
|
// 6 Mar 2010 S. Baer
|
|
// Invalidate the cached IsClosed flag. This forces the mesh to
|
|
// recompute IsClosed the next time it is called
|
|
SetClosed(-1);
|
|
|
|
rc = true;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
ON_Mesh* ON_ControlPolygonMesh(
|
|
const ON_NurbsSurface& nurbs_surface,
|
|
bool bCleanMesh,
|
|
ON_Mesh* input_mesh
|
|
)
|
|
{
|
|
int u0 = 0;
|
|
int u1 = nurbs_surface.CVCount(0);
|
|
|
|
int v0 = 0;
|
|
int v1 = nurbs_surface.CVCount(1);
|
|
|
|
if ( 0 == nurbs_surface.m_cv || !nurbs_surface.IsValid() )
|
|
{
|
|
ON_ERROR("ON_ControlPolygonMesh - surface is not valid");
|
|
return nullptr;
|
|
}
|
|
|
|
ON_SimpleArray<double> gu(u1);
|
|
ON_SimpleArray<double> gv(v1);
|
|
gu.SetCount(u1);
|
|
gv.SetCount(v1);
|
|
nurbs_surface.GetGrevilleAbcissae(0,gu.Array());
|
|
nurbs_surface.GetGrevilleAbcissae(1,gv.Array());
|
|
|
|
ON_Interval d0 = nurbs_surface.Domain(0);
|
|
ON_Interval d1 = nurbs_surface.Domain(1);
|
|
|
|
bool bPeriodic0 = nurbs_surface.IsPeriodic(0)?true:false;
|
|
bool bIsClosed0 = bPeriodic0 ? true : (nurbs_surface.IsClosed(0)?true:false);
|
|
bool bPeriodic1 = nurbs_surface.IsPeriodic(1)?true:false;
|
|
bool bIsClosed1 = bPeriodic1 ? true : (nurbs_surface.IsClosed(1)?true:false);
|
|
|
|
if ( bPeriodic0 )
|
|
{
|
|
u1 -= (nurbs_surface.Degree(0) - 1);
|
|
while ( u1 < nurbs_surface.CVCount(0) && gu[u0] < d0[0] && gu[u1-1] <= d0[1] )
|
|
{
|
|
u0++;
|
|
u1++;
|
|
}
|
|
d0.Set(gu[u0],gu[u1-1]);
|
|
}
|
|
|
|
if ( bPeriodic1 )
|
|
{
|
|
v1 -= (nurbs_surface.Degree(1) - 1);
|
|
while ( v1 < nurbs_surface.CVCount(1) && gv[v0] < d1[0] && gv[v1-1] <= d1[1] )
|
|
{
|
|
v0++;
|
|
v1++;
|
|
}
|
|
d1.Set(gv[v0],gv[v1-1]);
|
|
}
|
|
|
|
|
|
ON_Mesh* mesh = (0 == input_mesh) ? new ON_Mesh() : input_mesh;
|
|
|
|
int vertex_count = (u1-u0)*(v1-v0);
|
|
int face_count = (u1-u0-1)*(v1-v0-1);
|
|
|
|
ON_3dPointArray& dpv = mesh->DoublePrecisionVertices();
|
|
dpv.Reserve(vertex_count);
|
|
mesh->m_N.Reserve(vertex_count);
|
|
mesh->m_T.Reserve(vertex_count);
|
|
mesh->m_S.Reserve(vertex_count);
|
|
mesh->m_F.Reserve(face_count);
|
|
mesh->m_srf_domain[0] = d0;
|
|
mesh->m_srf_domain[1] = d1;
|
|
|
|
ON_3dPoint V;
|
|
ON_3dVector N;
|
|
ON_2dPoint S;
|
|
ON_2dPoint T;
|
|
|
|
int hint[2] = {0,0};
|
|
int i, j;
|
|
int k = -1;
|
|
for ( j = v0; j < v1; j++)
|
|
{
|
|
S.y = gv[j];
|
|
T.y = d1.NormalizedParameterAt(S.y);
|
|
for ( i = u0; i < u1; i++)
|
|
{
|
|
nurbs_surface.GetCV( i, j, V);
|
|
S.x = gu[i];
|
|
T.x = d0.NormalizedParameterAt(S.x);
|
|
nurbs_surface.EvNormal(gu[i],gv[j],N,0,hint);
|
|
dpv.AppendNew() = V;
|
|
mesh->m_N.AppendNew() = N;
|
|
mesh->m_S.AppendNew() = S;
|
|
mesh->m_T.AppendNew() = T;
|
|
if ( i > u0 && j > v0 )
|
|
{
|
|
ON_MeshFace& f = mesh->m_F.AppendNew();
|
|
f.vi[0] = k++;
|
|
f.vi[1] = k;
|
|
f.vi[2] = dpv.Count()-1;
|
|
f.vi[3] = f.vi[2]-1;
|
|
}
|
|
}
|
|
k++;
|
|
}
|
|
|
|
mesh->UpdateSinglePrecisionVertices();
|
|
|
|
u1 -= u0;
|
|
v1 -= v0;
|
|
|
|
// make sure closed seams are spot on
|
|
if ( bIsClosed0 )
|
|
{
|
|
i = 0;
|
|
for ( j = 0; j < v1; j++ )
|
|
{
|
|
k = i + (u1-1);
|
|
mesh->SetVertex(k, mesh->Vertex(i));
|
|
if ( bPeriodic0 )
|
|
{
|
|
mesh->m_N[k] = mesh->m_N[i];
|
|
}
|
|
i = k+1;
|
|
// do NOT synch texture coordinates
|
|
}
|
|
}
|
|
|
|
if ( bIsClosed1 )
|
|
{
|
|
for ( i = 0, k = u1*(v1-1); i < u1; i++, k++ )
|
|
{
|
|
mesh->SetVertex(k, mesh->Vertex(i));
|
|
if ( bPeriodic1 )
|
|
{
|
|
mesh->m_N[k] = mesh->m_N[i];
|
|
}
|
|
// do NOT synch texture coordinates
|
|
}
|
|
}
|
|
|
|
// make sure singular ends are spot on
|
|
i=0;
|
|
for ( k = 0; k < 4; k++ )
|
|
{
|
|
if ( nurbs_surface.IsSingular(k) )
|
|
{
|
|
switch(k)
|
|
{
|
|
case 0: // 0 = south
|
|
i = 0;
|
|
j = 1;
|
|
k = u1;
|
|
break;
|
|
|
|
case 1: // 1 = east
|
|
i = u1-1;
|
|
j = u1;
|
|
k = u1*v1;
|
|
break;
|
|
|
|
case 2: // 2 = north
|
|
i = u1*(v1-1);
|
|
j = 1;
|
|
k = u1*v1;
|
|
break;
|
|
|
|
case 3: // 3 = west
|
|
i = 0;
|
|
j = u1;
|
|
k = u1*(v1-1)+1;
|
|
break;
|
|
}
|
|
V = mesh->Vertex(i);
|
|
for ( i = i+j; i < k; i += j )
|
|
{
|
|
mesh->SetVertex(i, V);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( bCleanMesh )
|
|
{
|
|
// Clean up triangles etc.
|
|
ON_3dPoint P[4];
|
|
ON_SimpleArray<int> badfi(32);
|
|
for ( i = 0; i < mesh->m_F.Count(); i++ )
|
|
{
|
|
ON_MeshFace& f = mesh->m_F[i];
|
|
P[0] = mesh->Vertex(f.vi[0]);
|
|
P[1] = mesh->Vertex(f.vi[1]);
|
|
P[2] = mesh->Vertex(f.vi[2]);
|
|
P[3] = mesh->Vertex(f.vi[3]);
|
|
if ( P[0] == P[1] )
|
|
{
|
|
f.vi[1] = f.vi[2];
|
|
f.vi[2] = f.vi[3];
|
|
P[1] = P[2];
|
|
P[2] = P[3];
|
|
}
|
|
if ( P[1] == P[2] )
|
|
{
|
|
f.vi[2] = f.vi[3];
|
|
P[2] = P[3];
|
|
}
|
|
if ( P[2] == P[3] )
|
|
{
|
|
f.vi[2] = f.vi[3];
|
|
P[2] = P[3];
|
|
}
|
|
if ( P[3] == P[0] )
|
|
{
|
|
f.vi[0] = f.vi[1];
|
|
f.vi[1] = f.vi[2];
|
|
f.vi[2] = f.vi[3];
|
|
P[0] = P[1];
|
|
P[1] = P[2];
|
|
P[2] = P[3];
|
|
}
|
|
if ( f.vi[0] == f.vi[1]
|
|
|| f.vi[1] == f.vi[2]
|
|
|| f.vi[3] == f.vi[0]
|
|
|| P[0] == P[2] || P[1] == P[3] )
|
|
{
|
|
badfi.Append(i);
|
|
}
|
|
}
|
|
|
|
if ( badfi.Count() > 0 )
|
|
{
|
|
if ( badfi.Count() == mesh->m_F.Count() )
|
|
{
|
|
if ( input_mesh )
|
|
{
|
|
mesh->Destroy();
|
|
}
|
|
else
|
|
{
|
|
delete mesh;
|
|
}
|
|
mesh = 0;
|
|
}
|
|
else
|
|
{
|
|
// remove bad faces
|
|
i = badfi[0];
|
|
j = i+1;
|
|
k = 1;
|
|
for ( j = i+1; j < mesh->m_F.Count(); j++ )
|
|
{
|
|
if ( k < badfi.Count() && j == badfi[k] )
|
|
{
|
|
k++;
|
|
}
|
|
else
|
|
{
|
|
mesh->m_F[i++] = mesh->m_F[j];
|
|
}
|
|
}
|
|
mesh->m_F.SetCount(i);
|
|
}
|
|
|
|
// 29 May 2008: Mikko, TRR 34687:
|
|
// Added crash protection. At this point mesh is nullptr if it contained all bad faces.
|
|
if ( mesh)
|
|
mesh->CullUnusedVertices();
|
|
}
|
|
}
|
|
|
|
return mesh;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
//
|
|
// mesh components
|
|
static bool IsUnweldedEdge(int edgeidx, const ON_MeshTopology& Top)
|
|
{
|
|
|
|
const ON_MeshTopologyEdge& edge = Top.m_tope[edgeidx];
|
|
if (1 == edge.m_topf_count)
|
|
return true;
|
|
|
|
if (1 == Top.m_topv[edge.m_topvi[0]].m_v_count || 1 == Top.m_topv[edge.m_topvi[1]].m_v_count)
|
|
{
|
|
//Both ends of the edge have to have more than one mesh vertex or they are for sure welded.
|
|
//However having more than 1 vertex at both ends does not necessarily mean it is unwelded.
|
|
return false;
|
|
}
|
|
|
|
ON_3dPoint ptA = Top.m_mesh->Vertex(Top.m_topv[edge.m_topvi[0]].m_vi[0]);
|
|
ON_3dPoint ptB = Top.m_mesh->Vertex(Top.m_topv[edge.m_topvi[1]].m_vi[0]);
|
|
ON_SimpleArray<int> ptAindexes(Top.m_topv[edge.m_topvi[0]].m_v_count);
|
|
ON_SimpleArray<int> ptBindexes(Top.m_topv[edge.m_topvi[1]].m_v_count);
|
|
|
|
int i, ict = edge.m_topf_count;
|
|
int j, jct;
|
|
int k, kct;
|
|
for (i=0; ict>i; i++)
|
|
{
|
|
const ON_MeshFace& face = Top.m_mesh->m_F[edge.m_topfi[i]];
|
|
jct = face.IsQuad()?4:3;
|
|
for (j=0; jct>j; j++)
|
|
{
|
|
if (ptA == Top.m_mesh->Vertex(face.vi[j]))
|
|
{
|
|
if (0 == ptAindexes.Count())
|
|
{
|
|
ptAindexes.Append(face.vi[j]);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
kct = ptAindexes.Count();
|
|
for (k=0; kct>k; k++)
|
|
{
|
|
if (ptAindexes[k] == face.vi[j])
|
|
return false;
|
|
}
|
|
ptAindexes.Append(face.vi[j]);
|
|
}
|
|
}
|
|
else if (ptB == Top.m_mesh->Vertex(face.vi[j]))
|
|
{
|
|
if (0 == ptBindexes.Count())
|
|
{
|
|
ptBindexes.Append(face.vi[j]);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
kct = ptBindexes.Count();
|
|
for (k=0; kct>k; k++)
|
|
{
|
|
if (ptBindexes[k] == face.vi[j])
|
|
return false;
|
|
}
|
|
ptBindexes.Append(face.vi[j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void FindAdjacentFaces(const ON_MeshTopology& Top,
|
|
ON_SimpleArray<int>& FacesToCheck,
|
|
const ON_SimpleArray<int>& SortedFaceArray,
|
|
ON_SimpleArray<int>& DupFaceArray,
|
|
bool bUseVertexConnections,
|
|
bool bTopologicalConnections)
|
|
{
|
|
int fi, vi, ei, facecount = FacesToCheck.Count(), totalcount = SortedFaceArray.Count();
|
|
DupFaceArray.Zero();
|
|
ON_SimpleArray<int> OldFacesToCheck = FacesToCheck;
|
|
|
|
FacesToCheck.Empty();
|
|
|
|
for (fi=0;fi<facecount;fi++)
|
|
{
|
|
if (totalcount > OldFacesToCheck[fi])
|
|
{
|
|
if (0 == SortedFaceArray[OldFacesToCheck[fi]])
|
|
{
|
|
FacesToCheck.Append(OldFacesToCheck[fi]);
|
|
DupFaceArray[OldFacesToCheck[fi]] = 1;
|
|
}
|
|
|
|
if (false == bUseVertexConnections)
|
|
{
|
|
int j;
|
|
const ON_MeshTopologyFace& face = Top.m_topf[OldFacesToCheck[fi]];
|
|
for(ei=0;ei<(face.IsQuad()?4:3);ei++)
|
|
{
|
|
const ON_MeshTopologyEdge& edge = Top.m_tope[face.m_topei[ei]];
|
|
|
|
if (1 == edge.m_topf_count || (false == bTopologicalConnections && true == IsUnweldedEdge(face.m_topei[ei], Top)))
|
|
continue;
|
|
|
|
for(j=0;j<edge.m_topf_count;j++)
|
|
{
|
|
if (0 == SortedFaceArray[edge.m_topfi[j]] && 1 != DupFaceArray[edge.m_topfi[j]])
|
|
{
|
|
FacesToCheck.Append(edge.m_topfi[j]);
|
|
DupFaceArray[edge.m_topfi[j]] = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int j, k, m;
|
|
ON_3dPoint Pt;
|
|
const ON_MeshFace& face = Top.m_mesh->m_F[OldFacesToCheck[fi]];
|
|
for(vi=0;vi<(face.IsQuad()?4:3);vi++)
|
|
{
|
|
const ON_MeshTopologyVertex& vertex = Top.m_topv[Top.m_topv_map[face.vi[vi]]];
|
|
for (j=0; vertex.m_tope_count>j; j++)
|
|
{
|
|
const ON_MeshTopologyEdge& edge = Top.m_tope[vertex.m_topei[j]];
|
|
for (k=0; edge.m_topf_count>k; k++)
|
|
{
|
|
if (true == bTopologicalConnections)
|
|
{
|
|
if (0 == SortedFaceArray[edge.m_topfi[k]] && 1 != DupFaceArray[edge.m_topfi[k]])
|
|
{
|
|
FacesToCheck.Append(edge.m_topfi[k]);
|
|
DupFaceArray[edge.m_topfi[k]] = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Pt = Top.m_mesh->Vertex(vertex.m_vi[0]);
|
|
const ON_MeshFace& thisface = Top.m_mesh->m_F[edge.m_topfi[k]];
|
|
for (m=0; m<(thisface.IsQuad()?4:3);m++)
|
|
{
|
|
if (Pt != Top.m_mesh->Vertex(thisface.vi[m]))
|
|
continue;
|
|
|
|
if (face.vi[vi] == thisface.vi[m] && 0 == SortedFaceArray[edge.m_topfi[k]] && 1 != DupFaceArray[edge.m_topfi[k]])
|
|
{
|
|
//Faces share vertex
|
|
FacesToCheck.Append(edge.m_topfi[k]);
|
|
DupFaceArray[edge.m_topfi[k]] = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
int ON_Mesh::GetConnectedComponents( bool bUseVertexConnections,
|
|
bool bTopologicalConnections,
|
|
ON_SimpleArray<int>& facet_component_labels
|
|
) const
|
|
{
|
|
int i, facecount = m_F.Count(), meshidx = 0;
|
|
|
|
//This array will act as an associative array to m_F since ON_MeshFace do not have something
|
|
//like m_trim_user.i on a ON_BrepTrim. It will have the indice of the final mesh the face
|
|
//belongs to.
|
|
if (facecount != facet_component_labels.Count())
|
|
{
|
|
facet_component_labels.Reserve(facecount);
|
|
facet_component_labels.SetCount(facecount);
|
|
}
|
|
|
|
//initialize to 0
|
|
facet_component_labels.MemSet(0);
|
|
|
|
ON_SimpleArray<int> DupFaceArray(facecount);
|
|
DupFaceArray.SetCount(facecount);
|
|
|
|
const ON_MeshTopology& Top = Topology();
|
|
if (!Top.IsValid())
|
|
return 0;
|
|
|
|
ON_SimpleArray<int> FacesToCheck;
|
|
FacesToCheck.Reserve(64);
|
|
i = 0;
|
|
while (i < facecount)
|
|
{
|
|
meshidx++;
|
|
|
|
FacesToCheck.Append(i);
|
|
|
|
while(0 != FacesToCheck.Count())
|
|
{
|
|
//Figure out which faces are connected to each other
|
|
FindAdjacentFaces(Top, FacesToCheck, facet_component_labels, DupFaceArray, bUseVertexConnections, bTopologicalConnections);
|
|
int j;
|
|
for (j=0;j<FacesToCheck.Count();j++)
|
|
facet_component_labels[FacesToCheck[j]] = meshidx;
|
|
}
|
|
|
|
for(;i<facecount;i++)
|
|
{
|
|
if (0 == facet_component_labels[i])
|
|
break;
|
|
}
|
|
}
|
|
|
|
return meshidx;
|
|
}
|
|
|
|
int ON_Mesh::GetConnectedComponents( bool bUseVertexConnections,
|
|
bool bTopologicalConnections,
|
|
ON_SimpleArray<ON_Mesh*>* components
|
|
) const
|
|
{
|
|
int i, j, k, kct, facecount = m_F.Count();
|
|
ON_SimpleArray<int> SortedFaceArray(facecount);
|
|
SortedFaceArray.SetCount(facecount);
|
|
|
|
int compct = GetConnectedComponents(bUseVertexConnections, bTopologicalConnections, SortedFaceArray);
|
|
if (0 == compct || 0 == components)
|
|
return compct;
|
|
|
|
bool bHasFaceNormals = HasFaceNormals();
|
|
bool bHasPrincipalCurvatures = HasPrincipalCurvatures();
|
|
bool bHasSurfaceParameters = HasSurfaceParameters();
|
|
bool bHasTextureCoordinates = HasTextureCoordinates();
|
|
bool bHasVertexColors = HasVertexColors();
|
|
bool bHasVertexNormals = HasVertexNormals();
|
|
|
|
ON_MeshFace newface;
|
|
newface.vi[0] = -1; newface.vi[1] = -1; newface.vi[2] = -1; newface.vi[3] = -1;
|
|
|
|
ON_SimpleArray<int> vertidxarray(VertexCount());
|
|
vertidxarray.SetCount(VertexCount());
|
|
|
|
ON_SimpleArray<ON_MeshNgon> ngons;
|
|
ON_SimpleArray<unsigned int> ngonfacecount;
|
|
unsigned int ngonct = this->NgonUnsignedCount();
|
|
ON_SimpleArray<unsigned int> ngonfacevertexarray;
|
|
if (0 < ngonct)
|
|
{
|
|
// if the original mesh had ngons setup an array of new ngons
|
|
// to be filled in as the face/vertex remap occurs
|
|
ngons.Reserve(ngonct);
|
|
|
|
// this array will hold the current index for the face of the
|
|
// new ngon
|
|
ngonfacecount.Reserve(ngonct);
|
|
ngonfacecount.SetCount(ngonct);
|
|
ngonfacecount.MemSet(0);
|
|
|
|
unsigned int j_local, ct;
|
|
// first figure out how much space we'll need for
|
|
// face and vertex arrays
|
|
ct = 0;
|
|
for (j_local = 0; ngonct > j_local; j_local++)
|
|
{
|
|
const ON_MeshNgon* ngon = this->Ngon(j_local);
|
|
if (nullptr == ngon)
|
|
continue;
|
|
ct += ngon->m_Vcount + ngon->m_Fcount;
|
|
}
|
|
|
|
ngonfacevertexarray.Reserve(ct);
|
|
ngonfacevertexarray.SetCount(ct);
|
|
unsigned int* tmp = ngonfacevertexarray.Array();
|
|
//initialize to unset, not zero, so we can check for ngons
|
|
//that were not part of this mark and prevent adding them to
|
|
//output
|
|
memset(tmp, ON_UNSET_UINT_INDEX, ct*sizeof(tmp[0]));
|
|
ct = 0;
|
|
for (j_local = 0; ngonct > j_local; j_local++)
|
|
{
|
|
const ON_MeshNgon* ngon = this->Ngon(j_local);
|
|
if (nullptr == ngon)
|
|
continue;
|
|
|
|
ON_MeshNgon& new_ngon = ngons.AppendNew();
|
|
new_ngon.m_Vcount = ngon->m_Vcount;
|
|
new_ngon.m_Fcount = ngon->m_Fcount;
|
|
new_ngon.m_vi = &ngonfacevertexarray[ct];
|
|
ct += new_ngon.m_Vcount;
|
|
new_ngon.m_fi = &ngonfacevertexarray[ct];
|
|
ct += new_ngon.m_Fcount;
|
|
}
|
|
}
|
|
|
|
const ON_3dPoint* pMesh_D = 0;
|
|
if ( HasDoublePrecisionVertices() )
|
|
{
|
|
if ( VertexCount() > 0 && HasSynchronizedDoubleAndSinglePrecisionVertices() )
|
|
{
|
|
pMesh_D = DoublePrecisionVertices().Array();
|
|
}
|
|
}
|
|
|
|
ON_3dPointArray bogus_D;
|
|
|
|
for (i=1;compct>=i;i++)
|
|
{
|
|
kct = vertidxarray.Count();
|
|
for (k=0; kct>k; k++)
|
|
vertidxarray[k] = -1;
|
|
|
|
ON_Mesh* pNewMesh = new ON_Mesh();
|
|
if (0 == pNewMesh)
|
|
continue;
|
|
|
|
// Jussi, July 16 2024, RH-82825:
|
|
// Copy packed texture information so that surface paramater mapping
|
|
// can be reapplied without changing texture appearance.
|
|
pNewMesh->m_packed_tex_domain[0] = m_packed_tex_domain[0];
|
|
pNewMesh->m_packed_tex_domain[1] = m_packed_tex_domain[1];
|
|
pNewMesh->m_packed_tex_rotate = m_packed_tex_rotate;
|
|
pNewMesh->m_srf_domain[0] = m_srf_domain[0];
|
|
pNewMesh->m_srf_domain[1] = m_srf_domain[1];
|
|
pNewMesh->m_srf_scale[0] = m_srf_scale[0];
|
|
pNewMesh->m_srf_scale[1] = m_srf_scale[1];
|
|
|
|
ON_3dPointArray& pNewMesh_D = ( 0 != pMesh_D )
|
|
? pNewMesh->DoublePrecisionVertices()
|
|
: bogus_D;
|
|
|
|
for (j=0;j<facecount;j++)
|
|
{
|
|
if ( i == SortedFaceArray[j] )
|
|
{
|
|
const ON_MeshFace& face = m_F[j];
|
|
kct = face.IsTriangle()?3:4;
|
|
for (k=0; k<kct; k++)
|
|
{
|
|
if (-1 != vertidxarray[face.vi[k]])
|
|
{
|
|
newface.vi[k] = vertidxarray[face.vi[k]];
|
|
continue;
|
|
}
|
|
|
|
int newvi;
|
|
if ( 0 != pMesh_D )
|
|
{
|
|
newvi = pNewMesh_D.Count();
|
|
pNewMesh_D.Append(pMesh_D[face.vi[k]]);
|
|
}
|
|
else
|
|
{
|
|
newvi = pNewMesh->VertexCount();
|
|
pNewMesh->m_V.AppendNew() = ON_3fPoint(Vertex(face.vi[k]));
|
|
}
|
|
|
|
newface.vi[k] = vertidxarray[face.vi[k]] = newvi;
|
|
|
|
if (true == bHasPrincipalCurvatures)
|
|
pNewMesh->m_K.Append(m_K[face.vi[k]]);
|
|
|
|
if (true == bHasSurfaceParameters)
|
|
pNewMesh->m_S.Append(m_S[face.vi[k]]);
|
|
|
|
if (true == bHasTextureCoordinates)
|
|
pNewMesh->m_T.Append(m_T[face.vi[k]]);
|
|
|
|
if (true == bHasVertexColors)
|
|
pNewMesh->m_C.Append(m_C[face.vi[k]]);
|
|
|
|
if (true == bHasVertexNormals)
|
|
pNewMesh->m_N.Append(m_N[face.vi[k]]);
|
|
}
|
|
|
|
if (3 == kct)
|
|
newface.vi[3] = newface.vi[2];
|
|
|
|
if (0 < ngonct)
|
|
{
|
|
// Add new face indexes to the new ngons
|
|
unsigned int idx = this->NgonIndexFromFaceIndex(j);
|
|
if (ON_UNSET_UINT_INDEX != idx)
|
|
ngons[idx].m_fi[ngonfacecount[idx]++] = pNewMesh->FaceCount();
|
|
}
|
|
|
|
pNewMesh->m_F.Append(newface);
|
|
if (true == bHasFaceNormals)
|
|
pNewMesh->m_FN.Append(m_FN[j]);
|
|
}
|
|
}
|
|
|
|
if (0 < ngonct)
|
|
{
|
|
unsigned int j_local, k_local;
|
|
for (j_local = 0; ngonct > j_local; j_local++)
|
|
{
|
|
const ON_MeshNgon* oldngon = this->Ngon(j_local);
|
|
if (nullptr == oldngon)
|
|
continue;
|
|
|
|
ON_MeshNgon& newngon = ngons[j_local];
|
|
if (oldngon->m_Vcount != newngon.m_Vcount)
|
|
continue; //should never happen
|
|
|
|
for (k_local = 0; newngon.m_Vcount > k_local; k_local++)
|
|
newngon.m_vi[k_local] = vertidxarray[oldngon->m_vi[k_local]];
|
|
}
|
|
|
|
bool bIsValid;
|
|
for (j_local = 0; ngonct > j_local; j_local++)
|
|
{
|
|
bIsValid = true;
|
|
ON_MeshNgon& ngon = ngons[j_local];
|
|
|
|
// if the faces/ngons were not marked then the
|
|
// output ngon will have not been set, do not
|
|
// add it to the output mesh
|
|
for (k_local = 0; ngon.m_Vcount > k_local && bIsValid; k_local++)
|
|
{
|
|
if (ON_UNSET_UINT_INDEX == ngon.m_vi[k_local])
|
|
bIsValid = false;
|
|
}
|
|
|
|
for (k_local = 0; ngon.m_Fcount > k_local && bIsValid; k_local++)
|
|
{
|
|
if (ON_UNSET_UINT_INDEX == ngon.m_fi[k_local])
|
|
bIsValid = false;
|
|
}
|
|
|
|
if (bIsValid)
|
|
pNewMesh->AddNgon(ngons[j_local].m_Vcount, ngons[j_local].m_vi, ngons[j_local].m_Fcount, ngons[j_local].m_fi);
|
|
}
|
|
|
|
pNewMesh->CreateNgonMap();
|
|
}
|
|
|
|
if ( 0 != pMesh_D )
|
|
pNewMesh->UpdateSinglePrecisionVertices();
|
|
|
|
pNewMesh->Compact();
|
|
|
|
if (0 < pNewMesh->m_F.Count())
|
|
components->Append(pNewMesh);
|
|
else
|
|
{
|
|
delete pNewMesh;
|
|
pNewMesh = 0;
|
|
}
|
|
}
|
|
|
|
return compct;
|
|
}
|