Sync changes from upstream repository

Co-authored-by: Andrew Le Bihan <andy@mcneel.com>
Co-authored-by: chuck <chuck@mcneel.com>
Co-authored-by: Dale Fugier <dale@mcneel.com>
Co-authored-by: Dale Lear <dalelear@mcneel.com>
Co-authored-by: David Eränen <david.eranen@mcneel.com>
Co-authored-by: Greg Arden <greg@mcneel.com>
Co-authored-by: John Croudy <john.croudy@mcneel.com>
Co-authored-by: Lowell Walmsley <lowell@mcneel.com>
Co-authored-by: Nathan Letwory <nathan@mcneel.com>
Co-authored-by: piac <giulio@mcneel.com>
Co-authored-by: Steve Baer <steve@mcneel.com>
Co-authored-by: Tim Hemmelman <tim@mcneel.com>
This commit is contained in:
Bozo The Builder
2020-03-12 09:00:26 -07:00
parent acbc998a03
commit 01cdb463e6
50 changed files with 5774 additions and 1857 deletions

View File

@@ -256,10 +256,101 @@ static void Internal_CreateFromMesh_ValidateNonmanifoldVertex(
return;
}
class ON_NgonBoundaryChecker
{
public:
/*
Parametes:
ngon - [in]
ngon to test
mesh [in]
mesh that ngon is a part of
bMustBeOriented - [in]
If true, the faces in the ngon must be compatibly oriented
*/
bool IsSimpleNgon(
const class ON_MeshNgon* ngon,
const class ON_Mesh* mesh,
bool bMustBeOriented
);
enum : unsigned int
{
HashTableSize = 256
};
private:
void Internal_Reset();
class ON_NgonBoundaryComponent* Internal_AddVertex(unsigned int vertex_index);
class ON_NgonBoundaryComponent* Internal_AddEdge(unsigned int vertex_index0, unsigned int vertex_index1, bool bMustBeOriented);
static unsigned int Internal_VertexHashIndex(unsigned int vertex_index);
static unsigned int Internal_EdgeHashIndex(unsigned int vertex_index0, unsigned int vertex_index1);
void Internal_InitialzeFixedSizePool();
void Internal_ReturnIsNotSimple();
// m_fsp manages the memory used for boundary components.
ON_FixedSizePool m_fsp;
class ON_NgonBoundaryComponent* m_hash_table[ON_NgonBoundaryChecker::HashTableSize] = {};
unsigned m_vertex_count = 0;
unsigned m_edge_count = 0;
bool m_bIsSimple = false;
bool m_bIsNotSimple = false;
};
ON_SubD* ON_SubD::CreateFromMesh(
const class ON_Mesh* level_zero_mesh,
const class ON_ToSubDParameters* from_mesh_options,
ON_SubD* subd
)
{
ON_Mesh* local_copy = nullptr;
if (nullptr != level_zero_mesh)
{
// remove ngons with holes and other damaged ngons so the underlying faces get used.
ON_NgonBoundaryChecker bc;
const bool bMustBeOrientedNgon = false;
const unsigned ngon_count = level_zero_mesh->NgonUnsignedCount();
ON_SimpleArray<unsigned> ngons_with_holes(ngon_count);
for (unsigned ni = 0; ni < ngon_count; ++ni)
{
const class ON_MeshNgon* ngon = level_zero_mesh->Ngon(ni);
if ( nullptr == ngon)
continue;
if (ngon->m_Vcount < 3 || ngon->m_Fcount <= 1)
continue;
if ( false == bc.IsSimpleNgon(ngon, level_zero_mesh,bMustBeOrientedNgon) )
ngons_with_holes.Append(ni);
}
for (;;)
{
if (0 == ngons_with_holes.UnsignedCount())
break;
local_copy = new ON_Mesh(*level_zero_mesh);
if (nullptr == local_copy)
break;
if (ngon_count != local_copy->NgonUnsignedCount())
break;
const unsigned removed_count = local_copy->RemoveNgons(ngons_with_holes.UnsignedCount(), ngons_with_holes.Array());
if (removed_count > 0)
level_zero_mesh = local_copy;
break;
}
}
ON_SubD* subd_from_mesh = Internal_CreateFromMeshWithValidNgons(level_zero_mesh, from_mesh_options, subd);
if (nullptr != local_copy)
delete local_copy;
return subd_from_mesh;
}
ON_SubD* ON_SubD::Internal_CreateFromMeshWithValidNgons(
const class ON_Mesh* level_zero_mesh,
const class ON_ToSubDParameters* from_mesh_options,
ON_SubD* subd
)
{
if (nullptr != subd)
@@ -1463,3 +1554,378 @@ ON_SubD* ON_SubD::CreateSubDBox(
return subd;
}
/*
The ONLY use for this class is in a calculation to determine if an ngon has a single simple outer boundary.
*/
class ON_NgonBoundaryComponent
{
public:
enum class Type : unsigned char
{
Unset = 0,
Vertex = 1,
Edge = 2
};
static const ON_NgonBoundaryComponent Unset;
unsigned int IsBoundaryVertex() const;
unsigned int IsBoundaryEdge() const;
ON_NgonBoundaryComponent::Type m_type = ON_NgonBoundaryComponent::Type::Unset;
mutable unsigned char m_mark = 0;
unsigned char m_face_count = 0; // 0, 1, 2, or 3 = number of faces attached to this edge. 3 means 3 or more
unsigned char m_attached_count = 0; // 0, 1, or 2 = number of values set in m_attached_to[]
unsigned int m_index = 0;
// If this component is a vertex, these will be the first two edges attached to the vertex.
// If this component is an edge, these will be the vertices at the start and end.
ON_NgonBoundaryComponent* m_attached_to[2] = {};
private:
bool Internal_IsAttachedToTwo(ON_NgonBoundaryComponent::Type attached_type) const;
friend class ON_NgonBoundaryChecker;
// hash table pointer
// m_next is a list in a ON_NgonBoundaryChecker.m_vertex_hash_table[] element.
ON_NgonBoundaryComponent* m_next = nullptr;
};
bool ON_NgonBoundaryComponent::Internal_IsAttachedToTwo(ON_NgonBoundaryComponent::Type attached_type) const
{
return
2 == m_attached_count
&& nullptr != m_attached_to[0]
&& nullptr != m_attached_to[1]
&& m_attached_to[0] != m_attached_to[1]
&& attached_type == m_attached_to[0]->m_type
&& attached_type == m_attached_to[1]->m_type
;
}
unsigned int ON_NgonBoundaryComponent::IsBoundaryVertex() const
{
return
ON_NgonBoundaryComponent::Type::Vertex == m_type
&& 0 == m_face_count
&& Internal_IsAttachedToTwo(ON_NgonBoundaryComponent::Type::Edge)
&& 1 == m_attached_to[0]->m_face_count
&& 1 == m_attached_to[1]->m_face_count
;
}
unsigned int ON_NgonBoundaryComponent::IsBoundaryEdge() const
{
return
ON_NgonBoundaryComponent::Type::Edge == m_type
&& 1 == m_face_count
&& Internal_IsAttachedToTwo(ON_NgonBoundaryComponent::Type::Vertex)
&& 0 == m_attached_to[0]->m_face_count
&& 0 == m_attached_to[1]->m_face_count
;
}
const ON_NgonBoundaryComponent ON_NgonBoundaryComponent::Unset ON_CLANG_CONSTRUCTOR_BUG_INIT(ON_NgonBoundaryComponent);
void ON_NgonBoundaryChecker::Internal_ReturnIsNotSimple()
{
m_bIsSimple = false;
m_bIsNotSimple = true;
}
void ON_NgonBoundaryChecker::Internal_Reset()
{
m_fsp.ReturnAll();
for (unsigned i = 0; i < ON_NgonBoundaryChecker::HashTableSize; ++i)
m_hash_table[i] = nullptr;
m_vertex_count = 0;
m_edge_count = 0;
m_bIsSimple = false;
m_bIsNotSimple = false;
}
unsigned int ON_NgonBoundaryChecker::Internal_VertexHashIndex(unsigned int vertex_index)
{
return ON_CRC32(0, sizeof(vertex_index), &vertex_index) % ON_NgonBoundaryChecker::HashTableSize;
}
unsigned int ON_NgonBoundaryChecker::Internal_EdgeHashIndex(unsigned int vertex_index0, unsigned int vertex_index1)
{
return (vertex_index0 < vertex_index1)
? (ON_CRC32(vertex_index0, sizeof(vertex_index1), &vertex_index1) % ON_NgonBoundaryChecker::HashTableSize)
: (ON_CRC32(vertex_index1, sizeof(vertex_index0), &vertex_index0) % ON_NgonBoundaryChecker::HashTableSize)
;
}
void ON_NgonBoundaryChecker::Internal_InitialzeFixedSizePool()
{
if (0 == m_fsp.SizeofElement())
m_fsp.Create(sizeof(ON_NgonBoundaryComponent), 0, 0);
}
ON_NgonBoundaryComponent* ON_NgonBoundaryChecker::Internal_AddVertex(unsigned int vertex_index)
{
if (m_bIsNotSimple)
return nullptr;
const unsigned hash_index = ON_NgonBoundaryChecker::Internal_VertexHashIndex(vertex_index);
ON_NgonBoundaryComponent* v;
for (
v = m_hash_table[hash_index];
nullptr != v;
v = v->m_next
)
{
if (ON_NgonBoundaryComponent::Type::Vertex == v->m_type && vertex_index == v->m_index)
return v;
}
if (nullptr == v)
{
Internal_InitialzeFixedSizePool();
v = (ON_NgonBoundaryComponent*)m_fsp.AllocateElement();
v->m_type = ON_NgonBoundaryComponent::Type::Vertex;
v->m_index = vertex_index;
v->m_next = m_hash_table[hash_index];
m_hash_table[hash_index] = v;
++m_vertex_count;
}
return v;
}
ON_NgonBoundaryComponent* ON_NgonBoundaryChecker::Internal_AddEdge(unsigned int vertex_index0, unsigned int vertex_index1, bool bMustBeOriented)
{
if (m_bIsNotSimple)
return nullptr;
if (vertex_index0 == vertex_index1)
return (Internal_ReturnIsNotSimple(),nullptr);
ON_NgonBoundaryComponent* v[2] = { Internal_AddVertex(vertex_index0), Internal_AddVertex(vertex_index1) };
if (nullptr == v[0] || nullptr == v[1])
return (Internal_ReturnIsNotSimple(), nullptr);
const unsigned hash_index = ON_NgonBoundaryChecker::Internal_EdgeHashIndex(vertex_index0, vertex_index1);
ON_NgonBoundaryComponent* e;
for (
e = m_hash_table[hash_index];
nullptr != e;
e = e->m_next
)
{
if (
ON_NgonBoundaryComponent::Type::Edge == e->m_type
&&
((e->m_attached_to[0] == v[0] && e->m_attached_to[1] == v[1]) || (e->m_attached_to[0] == v[1] && e->m_attached_to[1] == v[0]))
)
{
if (1 == e->m_face_count)
{
if (bMustBeOriented)
{
if (e->m_attached_to[0] != v[1] || e->m_attached_to[1] != v[0])
{
// The 2 faces attached to this edge are not compatibly oriented.
return (Internal_ReturnIsNotSimple(), nullptr);
}
}
// this is an interior edge
e->m_face_count = 2;
return e;
}
// nonmanifold edge
return (Internal_ReturnIsNotSimple(), nullptr);
}
}
e = (ON_NgonBoundaryComponent*)m_fsp.AllocateElement();
e->m_type = ON_NgonBoundaryComponent::Type::Edge;
e->m_face_count = 1;
e->m_attached_count = 2;
e->m_attached_to[0] = v[0];
e->m_attached_to[1] = v[1];
e->m_next = m_hash_table[hash_index];
m_hash_table[hash_index] = e;
++m_edge_count;
return e;
}
bool ON_NgonBoundaryChecker::IsSimpleNgon(
const class ON_MeshNgon* ngon,
const class ON_Mesh* mesh,
bool bMustBeOriented
)
{
Internal_Reset();
if (nullptr == ngon || nullptr == mesh)
return (Internal_ReturnIsNotSimple(),false);
const unsigned ngon_face_count = ngon->m_Fcount;
if (ngon_face_count < 1 || nullptr == ngon->m_fi)
return (Internal_ReturnIsNotSimple(), false);
const int mesh_vertex_count = mesh->VertexCount();
const unsigned mesh_face_count = mesh->m_F.UnsignedCount();
if (mesh_vertex_count < 3 || mesh_face_count < 1)
return (Internal_ReturnIsNotSimple(), false);
const ON_MeshFace* a = mesh->m_F.Array();
for (unsigned i = 0; i < ngon_face_count; ++i)
{
const unsigned fi = ngon->m_fi[i];
if (fi >= mesh_face_count)
return (Internal_ReturnIsNotSimple(), false); // invalid face index in this ngon
const int* fvi = a[fi].vi;
if (fvi[0] < 0 || fvi[0] >= mesh_vertex_count)
return (Internal_ReturnIsNotSimple(), false); // invalid face in this ngon
if (fvi[1] < 0 || fvi[1] >= mesh_vertex_count)
return (Internal_ReturnIsNotSimple(), false); // invalid face in this ngon
if (fvi[2] < 0 || fvi[2] >= mesh_vertex_count)
return (Internal_ReturnIsNotSimple(), false); // invalid face in this ngon
if (fvi[3] < 0 || fvi[3] >= mesh_vertex_count)
return (Internal_ReturnIsNotSimple(), false); // invalid face in this ngon
if (nullptr == this->Internal_AddEdge(fvi[0], fvi[1], bMustBeOriented))
return (Internal_ReturnIsNotSimple(), false);
if (nullptr == this->Internal_AddEdge(fvi[1], fvi[2], bMustBeOriented))
return (Internal_ReturnIsNotSimple(), false);
if (fvi[2] != fvi[3])
{
if (nullptr == this->Internal_AddEdge(fvi[2], fvi[3], bMustBeOriented))
return (Internal_ReturnIsNotSimple(), false);
}
if (nullptr == this->Internal_AddEdge(fvi[3], fvi[0], bMustBeOriented))
return (Internal_ReturnIsNotSimple(), false);
}
if (m_edge_count < 3 || m_vertex_count < 3)
return (Internal_ReturnIsNotSimple(), false);
// A simple ngon has Euler number = ( V - E + F) = 1.
if (m_vertex_count + ngon_face_count != m_edge_count + 1)
return (Internal_ReturnIsNotSimple(), false); // wrong Euler number
// set vertex attachments
for (unsigned hash_index = 0; hash_index < ON_NgonBoundaryChecker::HashTableSize; ++hash_index)
{
for (ON_NgonBoundaryComponent* e = m_hash_table[hash_index]; nullptr != e; e = e->m_next)
{
if (1 != e->m_face_count)
continue;
for (unsigned evi = 0; evi < 2; ++evi)
{
ON_NgonBoundaryComponent* v = e->m_attached_to[evi];
if (v->m_attached_count >= 2)
return (Internal_ReturnIsNotSimple(), false); // vertex is attached to 3 or more boundary edges
v->m_attached_to[v->m_attached_count++] = e;
}
}
}
bool bBoundaryIsMarked = false;
ON_FixedSizePoolIterator fspit(m_fsp);
for (ON_NgonBoundaryComponent* e = (ON_NgonBoundaryComponent * )fspit.FirstElement(); nullptr != e; e = (ON_NgonBoundaryComponent * )fspit.NextElement())
{
if (1 != e->m_face_count)
continue; // vertex components alwasy have m_face_count = 0;
// e is a boundary edge
if (bBoundaryIsMarked)
{
if (0 == e->m_mark)
{
// this is a boundary edge that part of the boundary we marked. The ngon is not simple.
return (Internal_ReturnIsNotSimple(), false);
}
}
else
{
if ( false == e->IsBoundaryEdge())
return (Internal_ReturnIsNotSimple(), false);
// e is the first boundary edge in the pool
if (0 != e->m_mark)
{
ON_ERROR("Bug in this code - all edges should have m_mark = 0 at this point.");
return (Internal_ReturnIsNotSimple(), false);
}
// Walk along the boundary beginning at e0 and mark every edge in the boundary.
ON_NgonBoundaryComponent* e0 = e;
ON_NgonBoundaryComponent* v0 = e0->m_attached_to[0];
if ( nullptr == v0 || 0 != v0->m_mark)
{
ON_ERROR("Bug in this code - vertices should have m_mark = 0 at this point.");
return (Internal_ReturnIsNotSimple(), false);
}
if (false == v0->IsBoundaryVertex())
return (Internal_ReturnIsNotSimple(), false);
ON_NgonBoundaryComponent* e1 = e0;
ON_NgonBoundaryComponent* v1 = v0;
for (unsigned i = 0; i < m_edge_count; ++i) // counter limits infinite loop if there is a bug
{
if (0 != v1->m_mark)
return (Internal_ReturnIsNotSimple(), false);
if (0 != e1->m_mark)
return (Internal_ReturnIsNotSimple(), false);
// mark v1 and e1 as part of the boundary.
v1->m_mark = 1;
e1->m_mark = 1;
// set v1 = "next" vertex in the boundary
if ( v1 == e1->m_attached_to[0])
v1 = e1->m_attached_to[1];
else if (v1 == e1->m_attached_to[1])
{
if (bMustBeOriented)
return (Internal_ReturnIsNotSimple(), false);
v1 = e1->m_attached_to[0];
}
else
return (Internal_ReturnIsNotSimple(), false);
if (nullptr == v1)
return (Internal_ReturnIsNotSimple(), false);
// set e1 = "next" edge in the boundary
if (e1 == v1->m_attached_to[0])
e1 = v1->m_attached_to[1];
else if (e1 == v1->m_attached_to[1])
e1 = v1->m_attached_to[0];
else
return (Internal_ReturnIsNotSimple(), false);
if ( nullptr == e1)
return (Internal_ReturnIsNotSimple(), false);
if (e0 == e1 || v0 == v1)
{
if (e0 == e1 && v0 == v1)
{
// all edges and vertices in this boundary are marked. There should be no unmarked boundary edges.
bBoundaryIsMarked = true;
break;
}
return (Internal_ReturnIsNotSimple(), false);
}
if (false == v1->IsBoundaryVertex())
return (Internal_ReturnIsNotSimple(), false);
if (false == e1->IsBoundaryEdge())
return (Internal_ReturnIsNotSimple(), false);
}
if ( false == bBoundaryIsMarked)
return (Internal_ReturnIsNotSimple(), false); // for loop finished without marking a boundary
}
}
m_bIsSimple = (bBoundaryIsMarked && false == m_bIsNotSimple);
return m_bIsSimple;
}