1412 lines
47 KiB
C++
1412 lines
47 KiB
C++
|
/**
|
||
|
* Copyright (c) 2021-2022 Floyd M. Chitalu.
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* NOTE: This file is licensed under GPL-3.0-or-later (default).
|
||
|
* A commercial license can be purchased from Floyd M. Chitalu.
|
||
|
*
|
||
|
* License details:
|
||
|
*
|
||
|
* (A) GNU General Public License ("GPL"); a copy of which you should have
|
||
|
* recieved with this file.
|
||
|
* - see also: <http://www.gnu.org/licenses/>
|
||
|
* (B) Commercial license.
|
||
|
* - email: floyd.m.chitalu@gmail.com
|
||
|
*
|
||
|
* The commercial license options is for users that wish to use MCUT in
|
||
|
* their products for comercial purposes but do not wish to release their
|
||
|
* software products under the GPL license.
|
||
|
*
|
||
|
* Author(s) : Floyd M. Chitalu
|
||
|
*/
|
||
|
|
||
|
#include "mcut/internal/hmesh.h"
|
||
|
|
||
|
#include <algorithm>
|
||
|
#include <cstdio>
|
||
|
|
||
|
#define ENABLE_EDGE_DESCRIPTOR_TRICK 1
|
||
|
|
||
|
//
|
||
|
// array_iterator_t
|
||
|
//
|
||
|
template <>
|
||
|
vertex_array_iterator_t vertex_array_iterator_t::cbegin(bool account_for_removed_elems, id_<vertex_array_iterator_t>)
|
||
|
{
|
||
|
return mesh_ptr->vertices_begin(account_for_removed_elems);
|
||
|
}
|
||
|
|
||
|
template <>
|
||
|
vertex_array_iterator_t vertex_array_iterator_t::cend(id_<vertex_array_iterator_t>)
|
||
|
{
|
||
|
return mesh_ptr->vertices_end();
|
||
|
}
|
||
|
|
||
|
template <>
|
||
|
edge_array_iterator_t edge_array_iterator_t::cbegin(bool account_for_removed_elems, id_<edge_array_iterator_t>)
|
||
|
{
|
||
|
return mesh_ptr->edges_begin(account_for_removed_elems);
|
||
|
}
|
||
|
|
||
|
template <>
|
||
|
edge_array_iterator_t edge_array_iterator_t::cend(id_<edge_array_iterator_t>)
|
||
|
{
|
||
|
return mesh_ptr->edges_end();
|
||
|
}
|
||
|
|
||
|
template <>
|
||
|
halfedge_array_iterator_t halfedge_array_iterator_t::cbegin(bool account_for_removed_elems, id_<halfedge_array_iterator_t>)
|
||
|
{
|
||
|
return mesh_ptr->halfedges_begin(account_for_removed_elems);
|
||
|
}
|
||
|
|
||
|
template <>
|
||
|
halfedge_array_iterator_t halfedge_array_iterator_t::cend(id_<halfedge_array_iterator_t>)
|
||
|
{
|
||
|
return mesh_ptr->halfedges_end();
|
||
|
}
|
||
|
|
||
|
template <>
|
||
|
face_array_iterator_t face_array_iterator_t::cbegin(bool account_for_removed_elems, id_<face_array_iterator_t>)
|
||
|
{
|
||
|
return mesh_ptr->faces_begin(account_for_removed_elems);
|
||
|
}
|
||
|
template <>
|
||
|
face_array_iterator_t face_array_iterator_t::cend(id_<face_array_iterator_t>)
|
||
|
{
|
||
|
return mesh_ptr->faces_end();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// hmesh_t
|
||
|
//
|
||
|
|
||
|
hmesh_t::hmesh_t()
|
||
|
{
|
||
|
}
|
||
|
hmesh_t::~hmesh_t() { }
|
||
|
|
||
|
// static member functions
|
||
|
// -----------------------
|
||
|
|
||
|
vertex_descriptor_t hmesh_t::null_vertex()
|
||
|
{
|
||
|
return vertex_descriptor_t();
|
||
|
}
|
||
|
|
||
|
halfedge_descriptor_t hmesh_t::null_halfedge()
|
||
|
{
|
||
|
return halfedge_descriptor_t();
|
||
|
}
|
||
|
|
||
|
edge_descriptor_t hmesh_t::null_edge()
|
||
|
{
|
||
|
return edge_descriptor_t();
|
||
|
}
|
||
|
|
||
|
face_descriptor_t hmesh_t::null_face()
|
||
|
{
|
||
|
return face_descriptor_t();
|
||
|
}
|
||
|
|
||
|
// regular member functions
|
||
|
// ------------------------
|
||
|
|
||
|
int hmesh_t::number_of_vertices() const
|
||
|
{
|
||
|
return number_of_internal_vertices() - number_of_vertices_removed();
|
||
|
}
|
||
|
|
||
|
int hmesh_t::number_of_edges() const
|
||
|
{
|
||
|
return number_of_internal_edges() - number_of_edges_removed();
|
||
|
}
|
||
|
|
||
|
int hmesh_t::number_of_halfedges() const
|
||
|
{
|
||
|
return number_of_internal_halfedges() - number_of_halfedges_removed();
|
||
|
}
|
||
|
|
||
|
int hmesh_t::number_of_faces() const
|
||
|
{
|
||
|
return number_of_internal_faces() - number_of_faces_removed();
|
||
|
}
|
||
|
|
||
|
vertex_descriptor_t hmesh_t::source(const halfedge_descriptor_t& h) const
|
||
|
{
|
||
|
MCUT_ASSERT((size_t)h < m_halfedges.size() /*h != null_halfedge()*/);
|
||
|
const halfedge_data_t& hd = m_halfedges[h];
|
||
|
MCUT_ASSERT((size_t)hd.o < m_halfedges.size() /*hd.o != null_halfedge()*/);
|
||
|
const halfedge_data_t& ohd = m_halfedges[hd.o]; // opposite
|
||
|
return ohd.t;
|
||
|
}
|
||
|
|
||
|
vertex_descriptor_t hmesh_t::target(const halfedge_descriptor_t& h) const
|
||
|
{
|
||
|
MCUT_ASSERT(h != null_halfedge());
|
||
|
MCUT_ASSERT((size_t)h < m_halfedges.size() /*m_halfedges.count(h) == 1*/);
|
||
|
const halfedge_data_t& hd = m_halfedges[h];
|
||
|
return hd.t;
|
||
|
}
|
||
|
|
||
|
halfedge_descriptor_t hmesh_t::opposite(const halfedge_descriptor_t& h) const
|
||
|
{
|
||
|
MCUT_ASSERT(h != null_halfedge());
|
||
|
MCUT_ASSERT((size_t)h < m_halfedges.size() /*m_halfedges.count(h) == 1*/);
|
||
|
#if ENABLE_EDGE_DESCRIPTOR_TRICK
|
||
|
return halfedge_descriptor_t((h % 2 == 0) ? h + 1 : h - 1);
|
||
|
#else
|
||
|
const halfedge_data_t& hd = m_halfedges[h];
|
||
|
return hd.o;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
halfedge_descriptor_t hmesh_t::prev(const halfedge_descriptor_t& h) const
|
||
|
{
|
||
|
MCUT_ASSERT(h != null_halfedge());
|
||
|
MCUT_ASSERT((size_t)h < m_halfedges.size() /*m_halfedges.count(h) == 1*/);
|
||
|
const halfedge_data_t& hd = m_halfedges[h];
|
||
|
return hd.p;
|
||
|
}
|
||
|
|
||
|
halfedge_descriptor_t hmesh_t::next(const halfedge_descriptor_t& h) const
|
||
|
{
|
||
|
MCUT_ASSERT(h != null_halfedge());
|
||
|
MCUT_ASSERT((size_t)h < m_halfedges.size() /*m_halfedges.count(h) == 1*/);
|
||
|
const halfedge_data_t& hd = m_halfedges[h];
|
||
|
return hd.n;
|
||
|
}
|
||
|
|
||
|
void hmesh_t::set_next(const halfedge_descriptor_t& h, const halfedge_descriptor_t& nxt)
|
||
|
{
|
||
|
MCUT_ASSERT(h != null_halfedge());
|
||
|
MCUT_ASSERT(nxt != null_halfedge());
|
||
|
MCUT_ASSERT((size_t)h < m_halfedges.size() /*m_halfedges.count(h) == 1*/);
|
||
|
halfedge_data_t& hd = m_halfedges[h];
|
||
|
hd.n = nxt;
|
||
|
set_previous(nxt, h);
|
||
|
}
|
||
|
|
||
|
void hmesh_t::set_previous(const halfedge_descriptor_t& h, const halfedge_descriptor_t& prev)
|
||
|
{
|
||
|
MCUT_ASSERT(h != null_halfedge());
|
||
|
MCUT_ASSERT(prev != null_halfedge());
|
||
|
MCUT_ASSERT((size_t)h < m_halfedges.size() /*m_halfedges.count(h) == 1*/);
|
||
|
halfedge_data_t& hd = m_halfedges[h];
|
||
|
hd.p = prev;
|
||
|
}
|
||
|
|
||
|
edge_descriptor_t hmesh_t::edge(const halfedge_descriptor_t& h) const
|
||
|
{
|
||
|
MCUT_ASSERT(h != null_halfedge());
|
||
|
MCUT_ASSERT((size_t)h < m_halfedges.size() /*m_halfedges.count(h) == 1*/);
|
||
|
#if ENABLE_EDGE_DESCRIPTOR_TRICK
|
||
|
return edge_descriptor_t(h / 2);
|
||
|
#else
|
||
|
const halfedge_data_t& hd = m_halfedges[h];
|
||
|
return hd.e;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
face_descriptor_t hmesh_t::face(const halfedge_descriptor_t& h) const
|
||
|
{
|
||
|
MCUT_ASSERT(h != null_halfedge());
|
||
|
MCUT_ASSERT((size_t)h < m_halfedges.size() /*m_halfedges.count(h) == 1*/);
|
||
|
const halfedge_data_t& hd = m_halfedges[h];
|
||
|
return hd.f;
|
||
|
}
|
||
|
|
||
|
vertex_descriptor_t hmesh_t::vertex(const edge_descriptor_t e, const int v) const
|
||
|
{
|
||
|
MCUT_ASSERT(e != null_edge());
|
||
|
MCUT_ASSERT(v == 0 || v == 1);
|
||
|
MCUT_ASSERT((size_t)e < m_edges.size() /*m_edges.count(e) == 1*/);
|
||
|
#if ENABLE_EDGE_DESCRIPTOR_TRICK
|
||
|
return target(halfedge_descriptor_t((e * 2) + v));
|
||
|
#else
|
||
|
const edge_data_t& ed = m_edges[e];
|
||
|
const halfedge_descriptor_t h = ed.h;
|
||
|
MCUT_ASSERT((size_t)h < m_halfedges.size() /*m_halfedges.count(h) == 1*/);
|
||
|
const halfedge_data_t& hd = m_halfedges[h];
|
||
|
vertex_descriptor_t v_out = hd.t; // assuming v ==0
|
||
|
|
||
|
if (v == 1) {
|
||
|
const halfedge_descriptor_t opp = hd.o;
|
||
|
MCUT_ASSERT((size_t)opp < m_halfedges.size() /*m_halfedges.count(opp) == 1*/);
|
||
|
const halfedge_data_t& ohd = m_halfedges[opp];
|
||
|
v_out = ohd.t;
|
||
|
}
|
||
|
|
||
|
return v_out;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
bool hmesh_t::is_border(const halfedge_descriptor_t h)
|
||
|
{
|
||
|
MCUT_ASSERT(h != null_halfedge());
|
||
|
return face(h) == null_face();
|
||
|
}
|
||
|
|
||
|
bool hmesh_t::is_border(const edge_descriptor_t e)
|
||
|
{
|
||
|
MCUT_ASSERT(e != null_edge());
|
||
|
halfedge_descriptor_t h0 = halfedge(e, 0);
|
||
|
MCUT_ASSERT(h0 != null_halfedge());
|
||
|
halfedge_descriptor_t h1 = halfedge(e, 1);
|
||
|
MCUT_ASSERT(h1 != null_halfedge());
|
||
|
|
||
|
return is_border(h0) || is_border(h1);
|
||
|
}
|
||
|
|
||
|
halfedge_descriptor_t hmesh_t::halfedge(const edge_descriptor_t e, const int i) const
|
||
|
{
|
||
|
MCUT_ASSERT(i == 0 || i == 1);
|
||
|
MCUT_ASSERT(e != null_edge());
|
||
|
MCUT_ASSERT((size_t)e < m_edges.size() /*m_edges.count(e) == 1*/);
|
||
|
#if ENABLE_EDGE_DESCRIPTOR_TRICK
|
||
|
return halfedge_descriptor_t(e * 2 + i);
|
||
|
#else
|
||
|
const edge_data_t& ed = m_edges[e];
|
||
|
halfedge_descriptor_t h = ed.h; // assuming i ==0
|
||
|
|
||
|
MCUT_ASSERT(h != null_halfedge());
|
||
|
|
||
|
if (i == 1) {
|
||
|
MCUT_ASSERT((size_t)h < m_halfedges.size() /*m_halfedges.count(h) == 1*/);
|
||
|
|
||
|
const halfedge_data_t& hd = m_halfedges[h];
|
||
|
h = hd.o;
|
||
|
|
||
|
MCUT_ASSERT(h != null_halfedge());
|
||
|
}
|
||
|
|
||
|
return h;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// TODO: maybe a caching mechanism might help with this lookup e,g. "std::unordered_map<std::pair<vertex_descriptor_t, vertex_descriptor_t>, halfedge_descriptor_t>"
|
||
|
// But this cache need to be invalidated if we delete and edge
|
||
|
halfedge_descriptor_t hmesh_t::halfedge(const vertex_descriptor_t s, const vertex_descriptor_t t, bool strict_check) const
|
||
|
{
|
||
|
MCUT_ASSERT((size_t)s < m_vertices.size()); // MCUT_ASSERT(m_vertices.count(s) == 1);
|
||
|
const vertex_data_t& svd = m_vertices[s];
|
||
|
const std::vector<halfedge_descriptor_t>& s_halfedges = svd.m_halfedges;
|
||
|
MCUT_ASSERT((size_t)t < m_vertices.size()); // MCUT_ASSERT(m_vertices.count(t) == 1);
|
||
|
const vertex_data_t& tvd = m_vertices[t];
|
||
|
const std::vector<halfedge_descriptor_t>& t_halfedges = tvd.m_halfedges;
|
||
|
|
||
|
std::vector<edge_descriptor_t> t_edges(t_halfedges.size());
|
||
|
//t_edges.resize(0);
|
||
|
//t_edges.resize(t_halfedges.size());
|
||
|
uint32_t counter = 0;
|
||
|
for (std::vector<halfedge_descriptor_t>::const_iterator i = t_halfedges.cbegin(); i != t_halfedges.cend(); i++) {
|
||
|
edge_descriptor_t e = edge(*i);
|
||
|
MCUT_ASSERT(e != null_edge());
|
||
|
t_edges[counter++] = (e);
|
||
|
}
|
||
|
|
||
|
halfedge_descriptor_t result = null_halfedge();
|
||
|
for (std::vector<halfedge_descriptor_t>::const_iterator i = s_halfedges.cbegin(); i != s_halfedges.cend(); ++i) {
|
||
|
edge_descriptor_t s_edge = edge(*i);
|
||
|
if (std::find(t_edges.cbegin(), t_edges.cend(), s_edge) != t_edges.cend()) // belong to same edge?
|
||
|
{
|
||
|
|
||
|
|
||
|
// check if we need to return the opposite halfedge
|
||
|
if ((source(*i) == s && target(*i) == t) == false) {
|
||
|
MCUT_ASSERT(source(*i) == t);
|
||
|
MCUT_ASSERT(target(*i) == s);
|
||
|
|
||
|
halfedge_descriptor_t h = opposite(*i);
|
||
|
|
||
|
if (strict_check || face(h) != null_face()) { // "strict_check" ensures that we return the halfedge matching the input vertices
|
||
|
result = h;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
MCUT_ASSERT(source(*i) == s); // confirm our assumption
|
||
|
MCUT_ASSERT(target(*i) == t);
|
||
|
|
||
|
if (strict_check || face(*i) != null_face()) { // "strict_check" ensures that we return the halfedge matching the input vertices
|
||
|
result = *i; // assume source(*i) and target(*i) match "s" and "t"
|
||
|
}
|
||
|
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
edge_descriptor_t hmesh_t::edge(const vertex_descriptor_t s, const vertex_descriptor_t t, bool strict_check) const
|
||
|
{
|
||
|
halfedge_descriptor_t h = halfedge(s, t, strict_check);
|
||
|
return (h == hmesh_t::null_halfedge() ? hmesh_t::null_edge() : edge(h));
|
||
|
}
|
||
|
|
||
|
vertex_descriptor_t hmesh_t::add_vertex(const vec3& point)
|
||
|
{
|
||
|
const double& x = point.x();
|
||
|
const double& y = point.y();
|
||
|
const double& z = point.z();
|
||
|
return add_vertex(x, y, z);
|
||
|
}
|
||
|
|
||
|
vertex_descriptor_t hmesh_t::add_vertex(const double& x, const double& y, const double& z)
|
||
|
{
|
||
|
vertex_descriptor_t vd = hmesh_t::null_vertex();
|
||
|
vertex_data_t* data_ptr = nullptr;
|
||
|
bool reusing_removed_descr = (!m_vertices_removed.empty());
|
||
|
|
||
|
if (reusing_removed_descr) // can we re-use a slot?
|
||
|
{
|
||
|
std::vector<vertex_descriptor_t>::iterator it = m_vertices_removed.begin(); // take the oldest unused slot (NOTE: important for user data mapping)
|
||
|
vd = *it;
|
||
|
m_vertices_removed.erase(it);
|
||
|
MCUT_ASSERT((size_t)vd < m_vertices.size()); // MCUT_ASSERT(m_vertices.find(vd) != m_vertices.cend());
|
||
|
data_ptr = &m_vertices[vd];
|
||
|
} else {
|
||
|
vd = static_cast<vertex_descriptor_t>(number_of_vertices());
|
||
|
|
||
|
// std::pair<typename std::map<vertex_descriptor_t, vertex_data_t>::iterator, bool> ret = m_vertices.insert(std::make_pair(vd, vertex_data_t()));
|
||
|
// MCUT_ASSERT(ret.second == true);
|
||
|
m_vertices.emplace_back(vertex_data_t());
|
||
|
|
||
|
MCUT_ASSERT((size_t)vd <= (m_vertices.size() - 1));
|
||
|
|
||
|
data_ptr = &m_vertices[vd]; // &ret.first->second;
|
||
|
}
|
||
|
|
||
|
MCUT_ASSERT(vd != hmesh_t::null_vertex());
|
||
|
|
||
|
data_ptr->p[0] = x;
|
||
|
data_ptr->p[1] = y;
|
||
|
data_ptr->p[2] = z;
|
||
|
|
||
|
return vd;
|
||
|
}
|
||
|
|
||
|
halfedge_descriptor_t hmesh_t::add_edge(const vertex_descriptor_t v0, const vertex_descriptor_t v1)
|
||
|
{
|
||
|
MCUT_ASSERT(v0 != null_vertex());
|
||
|
MCUT_ASSERT(v1 != null_vertex());
|
||
|
|
||
|
// primary halfedge(0) of edge
|
||
|
halfedge_descriptor_t h0_idx(static_cast<face_descriptor_t::index_type>(number_of_halfedges())); // primary halfedge of new edge to be created
|
||
|
bool reusing_removed_h0_descr = (!m_halfedges_removed.empty());
|
||
|
|
||
|
if (reusing_removed_h0_descr) // can we re-use a slot?
|
||
|
{
|
||
|
std::vector<halfedge_descriptor_t>::iterator hIter = m_halfedges_removed.begin(); // take the oldest unused slot (NOTE: important for user data mapping)
|
||
|
h0_idx = *hIter;
|
||
|
m_halfedges_removed.erase(hIter);
|
||
|
MCUT_ASSERT((size_t)h0_idx < m_halfedges.size() /*m_halfedges.find(h0_idx) != m_halfedges.cend()*/);
|
||
|
}
|
||
|
|
||
|
halfedge_data_t* halfedge0_data_ptr = nullptr;
|
||
|
if (reusing_removed_h0_descr) {
|
||
|
// halfedge0_data_ptr = &m_halfedges[h0_idx);
|
||
|
} else {
|
||
|
// create new halfedge --> h0
|
||
|
// std::pair<typename std::map<halfedge_descriptor_t, halfedge_data_t>::iterator, bool> h0_ret = m_halfedges.insert(std::make_pair(h0_idx, halfedge_data_t()));
|
||
|
// MCUT_ASSERT(h0_ret.second == true);
|
||
|
// halfedge0_data_ptr = &h0_ret.first->second;
|
||
|
m_halfedges.emplace_back(halfedge_data_t());
|
||
|
}
|
||
|
|
||
|
// second halfedge(1) of edge
|
||
|
halfedge_descriptor_t h1_idx(static_cast<face_descriptor_t::index_type>(number_of_halfedges())); // second halfedge of new edge to be created (opposite of h0_idx)
|
||
|
bool reusing_removed_h1_descr = (!m_halfedges_removed.empty());
|
||
|
|
||
|
if (reusing_removed_h1_descr) // can we re-use a slot?
|
||
|
{
|
||
|
std::vector<halfedge_descriptor_t>::iterator hIter = m_halfedges_removed.begin() /*+ (m_halfedges_removed.size() - 1)*/; // take the most recently removed
|
||
|
h1_idx = *hIter;
|
||
|
m_halfedges_removed.erase(hIter);
|
||
|
MCUT_ASSERT((size_t)h1_idx < m_halfedges.size() /*m_halfedges.find(h1_idx) != m_halfedges.cend()*/);
|
||
|
}
|
||
|
|
||
|
halfedge_data_t* halfedge1_data_ptr = nullptr;
|
||
|
if (reusing_removed_h1_descr) {
|
||
|
// halfedge1_data_ptr = &m_halfedges[h1_idx);
|
||
|
} else {
|
||
|
// create new halfedge --> h1
|
||
|
// std::pair<typename std::map<halfedge_descriptor_t, halfedge_data_t>::iterator, bool> h1_ret = m_halfedges.insert(std::make_pair(h1_idx, halfedge_data_t()));
|
||
|
// MCUT_ASSERT(h1_ret.second == true);
|
||
|
// halfedge1_data_ptr = &h1_ret.first->second;
|
||
|
m_halfedges.emplace_back(halfedge_data_t());
|
||
|
}
|
||
|
|
||
|
// https://stackoverflow.com/questions/34708189/c-vector-of-pointer-loses-the-reference-after-push-back
|
||
|
halfedge0_data_ptr = &m_halfedges[h0_idx];
|
||
|
halfedge1_data_ptr = &m_halfedges[h1_idx];
|
||
|
|
||
|
// edge
|
||
|
|
||
|
edge_descriptor_t e_idx(static_cast<face_descriptor_t::index_type>(number_of_edges())); // index of new edge
|
||
|
bool reusing_removed_edge_descr = (!m_edges_removed.empty());
|
||
|
|
||
|
if (reusing_removed_edge_descr) // can we re-use a slot?
|
||
|
{
|
||
|
std::vector<edge_descriptor_t>::iterator eIter = m_edges_removed.begin(); // take the oldest unused slot (NOTE: important for user data mapping)
|
||
|
e_idx = *eIter;
|
||
|
m_edges_removed.erase(eIter);
|
||
|
MCUT_ASSERT((size_t)e_idx < m_edges.size() /*m_edges.find(e_idx) != m_edges.cend()*/);
|
||
|
}
|
||
|
|
||
|
edge_data_t* edge_data_ptr = nullptr;
|
||
|
if (reusing_removed_edge_descr) {
|
||
|
edge_data_ptr = &m_edges[e_idx];
|
||
|
} else {
|
||
|
// std::pair<typename std::map<edge_descriptor_t, edge_data_t>::iterator, bool> eret = m_edges.insert(std::make_pair(e_idx, edge_data_t())); // create a new edge
|
||
|
// MCUT_ASSERT(eret.second == true);
|
||
|
// edge_data_ptr = &eret.first->second;
|
||
|
m_edges.emplace_back(edge_data_t());
|
||
|
edge_data_ptr = &m_edges.back();
|
||
|
}
|
||
|
|
||
|
// update incidence information
|
||
|
|
||
|
// edge_data_t& edge_data = eret.first->second;
|
||
|
edge_data_ptr->h = h0_idx; // even/primary halfedge
|
||
|
// eret.first->h = h0_idx; // even/primary halfedge
|
||
|
|
||
|
// halfedge_data_t& halfedge0_data = h0_ret.first->second;
|
||
|
halfedge0_data_ptr->t = v1; // target vertex of h0
|
||
|
halfedge0_data_ptr->o = h1_idx; // ... because opp has idx differing by 1
|
||
|
halfedge0_data_ptr->e = e_idx;
|
||
|
|
||
|
// halfedge_data_t& halfedge1_data = h1_ret.first->second;
|
||
|
halfedge1_data_ptr->t = v0; // target vertex of h1
|
||
|
halfedge1_data_ptr->o = h0_idx; // ... because opp has idx differing by 1
|
||
|
halfedge1_data_ptr->e = e_idx;
|
||
|
|
||
|
// MCUT_ASSERT(h1_ret.first->first == halfedge0_data_ptr->o); // h1 comes just afterward (its index)
|
||
|
|
||
|
// update vertex incidence
|
||
|
|
||
|
// v0
|
||
|
MCUT_ASSERT((size_t)v0 < m_vertices.size()); // MCUT_ASSERT(m_vertices.count(v0) == 1);
|
||
|
vertex_data_t& v0_data = m_vertices[v0];
|
||
|
// MCUT_ASSERT();
|
||
|
if (std::find(v0_data.m_halfedges.cbegin(), v0_data.m_halfedges.cend(), h1_idx) == v0_data.m_halfedges.cend()) {
|
||
|
v0_data.m_halfedges.emplace_back(h1_idx); // halfedge whose target is v0
|
||
|
}
|
||
|
// v1
|
||
|
MCUT_ASSERT((size_t)v1 < m_vertices.size()); // MCUT_ASSERT(m_vertices.count(v1) == 1);
|
||
|
vertex_data_t& v1_data = m_vertices[v1];
|
||
|
// MCUT_ASSERT();
|
||
|
if (std::find(v1_data.m_halfedges.cbegin(), v1_data.m_halfedges.cend(), h0_idx) == v1_data.m_halfedges.cend()) {
|
||
|
v1_data.m_halfedges.emplace_back(h0_idx); // halfedge whose target is v1
|
||
|
}
|
||
|
|
||
|
return static_cast<halfedge_descriptor_t>(h0_idx); // return halfedge whose target is v1
|
||
|
}
|
||
|
|
||
|
face_descriptor_t hmesh_t::add_face(const std::vector<vertex_descriptor_t>& vi)
|
||
|
{
|
||
|
const int face_vertex_count = static_cast<int>(vi.size());
|
||
|
MCUT_ASSERT(face_vertex_count >= 3);
|
||
|
|
||
|
const int face_count = number_of_faces();
|
||
|
face_data_t new_face_data;
|
||
|
face_descriptor_t new_face_idx(static_cast<face_descriptor_t::index_type>(face_count));
|
||
|
bool reusing_removed_face_descr = (!m_faces_removed.empty());
|
||
|
|
||
|
if (reusing_removed_face_descr) // can we re-use a slot?
|
||
|
{
|
||
|
std::vector<face_descriptor_t>::iterator fIter = m_faces_removed.begin(); // take the oldest unused slot (NOTE: important for user data mapping)
|
||
|
new_face_idx = *fIter;
|
||
|
m_faces_removed.erase(fIter); // slot is going to be used again
|
||
|
|
||
|
MCUT_ASSERT((size_t)new_face_idx < m_faces.size() /*m_faces.find(new_face_idx) != m_faces.cend()*/);
|
||
|
}
|
||
|
|
||
|
face_data_t* face_data_ptr = reusing_removed_face_descr ? &m_faces[new_face_idx] : &new_face_data;
|
||
|
face_data_ptr->m_halfedges.clear();
|
||
|
face_data_ptr->m_halfedges.reserve(face_vertex_count);
|
||
|
|
||
|
for (int i = 0; i < face_vertex_count; ++i) {
|
||
|
const vertex_descriptor_t v0 = vi[i]; // i.e. src
|
||
|
|
||
|
MCUT_ASSERT(v0 != null_vertex());
|
||
|
|
||
|
const vertex_descriptor_t v1 = vi[(i + 1) % face_vertex_count]; // i.e. tgt
|
||
|
|
||
|
MCUT_ASSERT(v1 != null_vertex());
|
||
|
|
||
|
// check if edge exists between v0 and v1 (using halfedges incident to either v0 or v1)
|
||
|
// TODO: use the halfedge(..., true) function
|
||
|
// vertex_data_t& v0_data = m_vertices[v0];
|
||
|
// vertex_data_t& v1_data = m_vertices[v1];
|
||
|
|
||
|
#if 0
|
||
|
vertex_data_t& v0_data = m_vertices[v0];
|
||
|
vertex_data_t& v1_data = m_vertices[v1];
|
||
|
|
||
|
bool connecting_edge_exists = false;
|
||
|
halfedge_descriptor_t v0_h = null_halfedge();
|
||
|
halfedge_descriptor_t v1_h = null_halfedge();
|
||
|
|
||
|
for (int v0_h_iter = 0; v0_h_iter < static_cast<int>(v0_data.m_halfedges.size()); ++v0_h_iter) {
|
||
|
v0_h = v0_data.m_halfedges[v0_h_iter];
|
||
|
const edge_descriptor_t v0_e = edge(v0_h);
|
||
|
|
||
|
for (int v1_h_iter = 0; v1_h_iter < static_cast<int>(v1_data.m_halfedges.size()); ++v1_h_iter) {
|
||
|
|
||
|
v1_h = v1_data.m_halfedges[v1_h_iter];
|
||
|
const edge_descriptor_t v1_e = edge(v1_h);
|
||
|
const bool same_edge = (v0_e == v1_e);
|
||
|
|
||
|
if (same_edge) {
|
||
|
connecting_edge_exists = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (connecting_edge_exists) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
halfedge_descriptor_t v1_h = halfedge(v0, v1, true);
|
||
|
bool connecting_edge_exists = v1_h != null_halfedge();
|
||
|
|
||
|
// halfedge_descriptor_t v1_h = null_halfedge();
|
||
|
|
||
|
// we use v1 in the following since v1 is the target (vertices are associated with halfedges which point to them)
|
||
|
|
||
|
halfedge_data_t* v1_hd_ptr = nullptr; // refer to halfedge whose tgt is v1
|
||
|
|
||
|
if (connecting_edge_exists) // edge connecting v0 and v1
|
||
|
{
|
||
|
MCUT_ASSERT((size_t)v1_h < m_halfedges.size() /*m_halfedges.count(v1_h) == 1*/);
|
||
|
|
||
|
v1_hd_ptr = &m_halfedges[v1_h];
|
||
|
} else { // there exists no edge between v0 and v1, so we create it
|
||
|
v1_h = add_edge(v0, v1);
|
||
|
MCUT_ASSERT((size_t)v1_h < m_halfedges.size() /*m_halfedges.count(h) == 1*/);
|
||
|
v1_hd_ptr = &m_halfedges[v1_h]; // add to vertex list since v1 is the target of h
|
||
|
}
|
||
|
|
||
|
MCUT_ASSERT(source(v1_h) == v0);
|
||
|
MCUT_ASSERT(target(v1_h) == v1);
|
||
|
|
||
|
if (v1_hd_ptr->f != null_face()) {
|
||
|
#if 0 // used for debugging triangulation
|
||
|
printf("face f%d uses halfedge: v%d v%d\n", (int)v1_hd_ptr->f, (int)v0, (int)v1);
|
||
|
const auto verts = get_vertices_around_face(v1_hd_ptr->f);
|
||
|
for (auto v : verts)
|
||
|
printf("p%d ", (int)v);
|
||
|
printf("\n");
|
||
|
printf("h%d.opp = h%d; =%d \n", (int)v1_h, (int)opposite(v1_h), (int)face(opposite(v1_h)));
|
||
|
#endif
|
||
|
return null_face(); // face is incident to a non-manifold edge
|
||
|
}
|
||
|
|
||
|
v1_hd_ptr->f = new_face_idx; // associate halfedge with face
|
||
|
face_data_ptr->m_halfedges.emplace_back(v1_h);
|
||
|
}
|
||
|
|
||
|
if (!reusing_removed_face_descr) {
|
||
|
MCUT_ASSERT((size_t)new_face_idx == m_faces.size() /*m_faces.count(new_face_idx) == 0*/);
|
||
|
// m_faces.insert(std::make_pair(new_face_idx, *face_data_ptr));
|
||
|
m_faces.emplace_back(*face_data_ptr);
|
||
|
}
|
||
|
|
||
|
// update halfedges (next halfedge)
|
||
|
// const std::vector<halfedge_descriptor_t>& halfedges_around_new_face = get_halfedges_around_face(new_face_idx);
|
||
|
const int num_halfedges = static_cast<int>(face_data_ptr->m_halfedges.size());
|
||
|
|
||
|
for (int i = 0; i < num_halfedges; ++i) {
|
||
|
const halfedge_descriptor_t h = face_data_ptr->m_halfedges[i];
|
||
|
const halfedge_descriptor_t nh = face_data_ptr->m_halfedges[(i + 1) % num_halfedges];
|
||
|
set_next(h, nh);
|
||
|
}
|
||
|
|
||
|
return new_face_idx;
|
||
|
}
|
||
|
|
||
|
bool hmesh_t::is_insertable(const std::vector<vertex_descriptor_t>& vi) const
|
||
|
{
|
||
|
const int face_vertex_count = static_cast<int>(vi.size());
|
||
|
MCUT_ASSERT(face_vertex_count >= 3);
|
||
|
|
||
|
for (int i = 0; i < face_vertex_count; ++i) {
|
||
|
const vertex_descriptor_t v0 = vi[i]; // i.e. src
|
||
|
|
||
|
MCUT_ASSERT(v0 != null_vertex());
|
||
|
|
||
|
const vertex_descriptor_t v1 = vi[(i + 1) % face_vertex_count]; // i.e. tgt
|
||
|
|
||
|
MCUT_ASSERT(v1 != null_vertex());
|
||
|
|
||
|
// halfedge from v0 to v1
|
||
|
const halfedge_descriptor_t v1_h = halfedge(v0, v1, false);
|
||
|
const bool connecting_edge_exists = v1_h != null_halfedge();
|
||
|
|
||
|
// we use v1 in the following since v1 is the target (vertices are associated with halfedges which point to them)
|
||
|
|
||
|
if (connecting_edge_exists) // edge connecting v0 and v1
|
||
|
{
|
||
|
MCUT_ASSERT((size_t)v1_h < m_halfedges.size() /*m_halfedges.count(v1_h) == 1*/);
|
||
|
|
||
|
const halfedge_data_t* const v1_hd_ptr = &m_halfedges[v1_h];
|
||
|
|
||
|
if (v1_hd_ptr->f != null_face()) {
|
||
|
//printf("h%d(v%d -> v%d) = f%d\n", (int)v1_h, (int)v0, (int)v1, (int)v1_hd_ptr->f);
|
||
|
return false; // face is incident to a non-manifold edge
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
const vec3& hmesh_t::vertex(const vertex_descriptor_t& vd) const
|
||
|
{
|
||
|
MCUT_ASSERT(vd != null_vertex());
|
||
|
MCUT_ASSERT((size_t)vd < m_vertices.size());
|
||
|
const vertex_data_t& vdata = m_vertices[vd];
|
||
|
return vdata.p;
|
||
|
}
|
||
|
|
||
|
uint32_t hmesh_t::get_num_vertices_around_face(const face_descriptor_t f) const
|
||
|
{
|
||
|
MCUT_ASSERT(f != null_face());
|
||
|
|
||
|
const std::vector<halfedge_descriptor_t>& halfedges_on_face = get_halfedges_around_face(f);
|
||
|
|
||
|
return (uint32_t)halfedges_on_face.size();
|
||
|
}
|
||
|
|
||
|
std::vector<vertex_descriptor_t> hmesh_t::get_vertices_around_face(const face_descriptor_t f, uint32_t prepend_offset) const
|
||
|
{
|
||
|
MCUT_ASSERT(f != null_face());
|
||
|
|
||
|
const std::vector<halfedge_descriptor_t>& halfedges_on_face = get_halfedges_around_face(f);
|
||
|
|
||
|
std::vector<vertex_descriptor_t> vertex_descriptors(halfedges_on_face.size());
|
||
|
|
||
|
for (int i = 0; i < (int)halfedges_on_face.size(); ++i) {
|
||
|
const halfedge_descriptor_t h = halfedges_on_face[i];
|
||
|
// MCUT_ASSERT((size_t)h < m_halfedges.size() /*m_halfedges.count(h) == 1*/);
|
||
|
// const halfedge_data_t& hd = m_halfedges[h];
|
||
|
vertex_descriptors[i] = vertex_descriptor_t(prepend_offset + target(h) /*hd.t*/);
|
||
|
}
|
||
|
return vertex_descriptors;
|
||
|
}
|
||
|
|
||
|
void hmesh_t::get_vertices_around_face(std::vector<vertex_descriptor_t>& vertex_descriptors, const face_descriptor_t f, uint32_t prepend_offset) const
|
||
|
{
|
||
|
MCUT_ASSERT(f != null_face());
|
||
|
|
||
|
const std::vector<halfedge_descriptor_t>& halfedges_on_face = get_halfedges_around_face(f);
|
||
|
vertex_descriptors.resize(halfedges_on_face.size());
|
||
|
|
||
|
for (int i = 0; i < (int)halfedges_on_face.size(); ++i) {
|
||
|
const halfedge_descriptor_t h = halfedges_on_face[i];
|
||
|
vertex_descriptors[i] = vertex_descriptor_t(prepend_offset + target(h) /*hd.t*/);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
std::vector<vertex_descriptor_t> hmesh_t::get_vertices_around_vertex(const vertex_descriptor_t v) const
|
||
|
{
|
||
|
MCUT_ASSERT(v != null_vertex());
|
||
|
// halfedges whoe target is 'v'
|
||
|
const std::vector<halfedge_descriptor_t>& halfedges = get_halfedges_around_vertex(v);
|
||
|
std::vector<vertex_descriptor_t> out(halfedges.size());
|
||
|
//out.resize(halfedges.size());
|
||
|
uint32_t counter = 0;
|
||
|
for (std::vector<halfedge_descriptor_t>::const_iterator h = halfedges.cbegin(); h != halfedges.cend(); ++h) {
|
||
|
vertex_descriptor_t src = source(*h);
|
||
|
out[counter++] = src;
|
||
|
}
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
void hmesh_t::get_vertices_around_vertex(std::vector<vertex_descriptor_t>& vertices_around_vertex, const vertex_descriptor_t v) const
|
||
|
{
|
||
|
MCUT_ASSERT(v != null_vertex());
|
||
|
// halfedges whoe target is 'v'
|
||
|
const std::vector<halfedge_descriptor_t>& halfedges = get_halfedges_around_vertex(v);
|
||
|
|
||
|
vertices_around_vertex.reserve(halfedges.size());
|
||
|
for (std::vector<halfedge_descriptor_t>::const_iterator h = halfedges.cbegin(); h != halfedges.cend(); ++h) {
|
||
|
vertex_descriptor_t src = source(*h);
|
||
|
vertices_around_vertex.emplace_back(src);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const std::vector<halfedge_descriptor_t>& hmesh_t::get_halfedges_around_face(const face_descriptor_t f) const
|
||
|
{
|
||
|
MCUT_ASSERT(f != null_face());
|
||
|
MCUT_ASSERT((size_t)f < m_faces.size() /*m_faces.count(f) == 1*/);
|
||
|
return m_faces[f].m_halfedges;
|
||
|
}
|
||
|
|
||
|
const std::vector<face_descriptor_t> hmesh_t::get_faces_around_face(const face_descriptor_t f, const std::vector<halfedge_descriptor_t>* halfedges_around_face_) const
|
||
|
{
|
||
|
MCUT_ASSERT(f != null_face());
|
||
|
|
||
|
std::vector<face_descriptor_t> faces_around_face;
|
||
|
|
||
|
const std::vector<halfedge_descriptor_t>& halfedges_on_face = (halfedges_around_face_ != nullptr) ? *halfedges_around_face_ : get_halfedges_around_face(f);
|
||
|
|
||
|
for (int i = 0; i < (int)halfedges_on_face.size(); ++i) {
|
||
|
|
||
|
const halfedge_descriptor_t h = halfedges_on_face[i];
|
||
|
MCUT_ASSERT((size_t)h < m_halfedges.size() /*m_halfedges.count(h) == 1*/);
|
||
|
const halfedge_data_t& hd = m_halfedges[h];
|
||
|
|
||
|
if (hd.o != null_halfedge()) {
|
||
|
MCUT_ASSERT((size_t)hd.o < m_halfedges.size() /*m_halfedges.count(hd.o) == 1*/);
|
||
|
const halfedge_data_t& ohd = m_halfedges[hd.o];
|
||
|
|
||
|
if (ohd.f != null_face()) {
|
||
|
faces_around_face.emplace_back(ohd.f);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return faces_around_face;
|
||
|
}
|
||
|
// NOTE: we have a lot of dupication here
|
||
|
void hmesh_t::get_faces_around_face(std::vector<face_descriptor_t>& faces_around_face, const face_descriptor_t f, const std::vector<halfedge_descriptor_t>* halfedges_around_face_) const
|
||
|
{
|
||
|
MCUT_ASSERT(f != null_face());
|
||
|
|
||
|
faces_around_face.clear();
|
||
|
|
||
|
const std::vector<halfedge_descriptor_t>& halfedges_on_face = (halfedges_around_face_ != nullptr) ? *halfedges_around_face_ : get_halfedges_around_face(f);
|
||
|
|
||
|
for (int i = 0; i < (int)halfedges_on_face.size(); ++i) {
|
||
|
|
||
|
const halfedge_descriptor_t h = halfedges_on_face[i];
|
||
|
MCUT_ASSERT((size_t)h < m_halfedges.size() /*m_halfedges.count(h) == 1*/);
|
||
|
const halfedge_data_t& hd = m_halfedges[h];
|
||
|
|
||
|
if (hd.o != null_halfedge()) {
|
||
|
MCUT_ASSERT((size_t)hd.o < m_halfedges.size() /*m_halfedges.count(hd.o) == 1*/);
|
||
|
const halfedge_data_t& ohd = m_halfedges[hd.o];
|
||
|
|
||
|
if (ohd.f != null_face()) {
|
||
|
faces_around_face.emplace_back(ohd.f);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uint32_t hmesh_t::get_num_faces_around_face(const face_descriptor_t f, const std::vector<halfedge_descriptor_t>* halfedges_around_face_) const
|
||
|
{
|
||
|
MCUT_ASSERT(f != null_face());
|
||
|
|
||
|
uint32_t num_faces_around_face = 0;
|
||
|
|
||
|
const std::vector<halfedge_descriptor_t>& halfedges_on_face = (halfedges_around_face_ != nullptr) ? *halfedges_around_face_ : get_halfedges_around_face(f);
|
||
|
|
||
|
for (uint32_t i = 0; i < (uint32_t)halfedges_on_face.size(); ++i) {
|
||
|
|
||
|
const halfedge_descriptor_t h = halfedges_on_face[i];
|
||
|
|
||
|
MCUT_ASSERT((size_t)h < m_halfedges.size() /*m_halfedges.count(h) == 1*/);
|
||
|
|
||
|
const halfedge_data_t& hd = m_halfedges[h];
|
||
|
|
||
|
if (hd.o != null_halfedge()) {
|
||
|
|
||
|
MCUT_ASSERT((size_t)hd.o < m_halfedges.size() /*m_halfedges.count(hd.o) == 1*/);
|
||
|
|
||
|
const halfedge_data_t& ohd = m_halfedges[hd.o];
|
||
|
|
||
|
if (ohd.f != null_face()) {
|
||
|
++num_faces_around_face;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return num_faces_around_face;
|
||
|
}
|
||
|
|
||
|
const std::vector<halfedge_descriptor_t>& hmesh_t::get_halfedges_around_vertex(const vertex_descriptor_t v) const
|
||
|
{
|
||
|
MCUT_ASSERT(v != hmesh_t::null_vertex());
|
||
|
MCUT_ASSERT((size_t)v < m_vertices.size());
|
||
|
const vertex_data_t& vd = m_vertices[v];
|
||
|
const std::vector<halfedge_descriptor_t>& incoming_halfedges = vd.m_halfedges;
|
||
|
return incoming_halfedges;
|
||
|
}
|
||
|
|
||
|
vertex_array_iterator_t hmesh_t::vertices_begin(bool account_for_removed_elems) const
|
||
|
{
|
||
|
vertex_array_t::const_iterator it = m_vertices.cbegin();
|
||
|
if (account_for_removed_elems) {
|
||
|
uint32_t index = 0;
|
||
|
while (it != m_vertices.cend() && is_removed(vertex_descriptor_t(index++)) /*is_removed(it)*/) {
|
||
|
++it; // shift the pointer to the first valid mesh element
|
||
|
}
|
||
|
}
|
||
|
return vertex_array_iterator_t(it, this);
|
||
|
}
|
||
|
|
||
|
vertex_array_iterator_t hmesh_t::vertices_end() const
|
||
|
{
|
||
|
return vertex_array_iterator_t(m_vertices.cend(), this);
|
||
|
}
|
||
|
|
||
|
edge_array_iterator_t hmesh_t::edges_begin(bool account_for_removed_elems) const
|
||
|
{
|
||
|
edge_array_t::const_iterator it = m_edges.cbegin();
|
||
|
if (account_for_removed_elems) {
|
||
|
uint32_t index = 0;
|
||
|
while (it != m_edges.cend() && is_removed(edge_descriptor_t(index++) /*it->first*/)) {
|
||
|
++it; // shift the pointer to the first valid mesh element
|
||
|
}
|
||
|
}
|
||
|
return edge_array_iterator_t(it, this);
|
||
|
}
|
||
|
|
||
|
edge_array_iterator_t hmesh_t::edges_end() const
|
||
|
{
|
||
|
return edge_array_iterator_t(m_edges.cend(), this);
|
||
|
}
|
||
|
|
||
|
halfedge_array_iterator_t hmesh_t::halfedges_begin(bool account_for_removed_elems) const
|
||
|
{
|
||
|
halfedge_array_t::const_iterator it = m_halfedges.cbegin();
|
||
|
if (account_for_removed_elems) {
|
||
|
uint32_t index = 0;
|
||
|
while (it != m_halfedges.cend() && is_removed(halfedge_descriptor_t(index++) /*it->first*/)) {
|
||
|
++it; // shift the pointer to the first valid mesh element
|
||
|
}
|
||
|
}
|
||
|
return halfedge_array_iterator_t(it, this);
|
||
|
}
|
||
|
|
||
|
halfedge_array_iterator_t hmesh_t::halfedges_end() const
|
||
|
{
|
||
|
return halfedge_array_iterator_t(m_halfedges.cend(), this);
|
||
|
}
|
||
|
|
||
|
face_array_iterator_t hmesh_t::faces_begin(bool account_for_removed_elems) const
|
||
|
{
|
||
|
face_array_t::const_iterator it = m_faces.cbegin();
|
||
|
if (account_for_removed_elems) {
|
||
|
uint32_t index = 0;
|
||
|
while (it != m_faces.cend() && is_removed(face_descriptor_t(index++) /*it->first*/)) {
|
||
|
++it; // shift the pointer to the first valid mesh element
|
||
|
}
|
||
|
}
|
||
|
return face_array_iterator_t(it, this);
|
||
|
}
|
||
|
|
||
|
face_array_iterator_t hmesh_t::faces_end() const
|
||
|
{
|
||
|
return face_array_iterator_t(m_faces.cend(), this);
|
||
|
}
|
||
|
|
||
|
// also disassociates (not remove) any halfedges(s) and vertices incident to face
|
||
|
void hmesh_t::remove_face(const face_descriptor_t f)
|
||
|
{
|
||
|
MCUT_ASSERT(f != null_face());
|
||
|
MCUT_ASSERT(std::find(m_faces_removed.cbegin(), m_faces_removed.cend(), f) == m_faces_removed.cend());
|
||
|
|
||
|
face_data_t& fd = m_faces[f];
|
||
|
|
||
|
static thread_local std::vector<vertex_descriptor_t> face_vertices; // ... that are used by face
|
||
|
face_vertices.resize(0);
|
||
|
|
||
|
// disassociate halfedges
|
||
|
|
||
|
for (std::vector<halfedge_descriptor_t>::const_iterator it = fd.m_halfedges.cbegin(); it != fd.m_halfedges.cend(); ++it) {
|
||
|
halfedge_data_t& hd = m_halfedges[*it];
|
||
|
MCUT_ASSERT(hd.f != null_face());
|
||
|
hd.f = null_face();
|
||
|
|
||
|
// NOTE: "next" and "previous" are only meaningful when the halfedge is used by a
|
||
|
// face. So we reset that information here since the halfedge is not longer used
|
||
|
// by a face
|
||
|
if (hd.n != null_halfedge()) { // disassociate "next"
|
||
|
const halfedge_descriptor_t hn = hd.n;
|
||
|
halfedge_data_t& hnd = m_halfedges[hn];
|
||
|
MCUT_ASSERT(hnd.p == *it);
|
||
|
hnd.p = null_halfedge();
|
||
|
//
|
||
|
hd.n = null_halfedge();
|
||
|
}
|
||
|
|
||
|
if (hd.p != null_halfedge()) { // disassociate "previous"
|
||
|
const halfedge_descriptor_t hp = hd.p;
|
||
|
halfedge_data_t& hpd = m_halfedges[hp];
|
||
|
MCUT_ASSERT(hpd.n == *it);
|
||
|
hpd.n = null_halfedge();
|
||
|
//
|
||
|
hd.p = null_halfedge();
|
||
|
}
|
||
|
|
||
|
face_vertices.emplace_back(hd.t);
|
||
|
}
|
||
|
|
||
|
// disassociate vertices
|
||
|
#if 0
|
||
|
// for each vertex used by face
|
||
|
for (std::vector<vertex_descriptor_t>::const_iterator it = face_vertices.cbegin(); it != face_vertices.cend(); ++it) {
|
||
|
vertex_descriptor_t face_vertex = *it;
|
||
|
vertex_data_t& vd = m_vertices[face_vertex];
|
||
|
|
||
|
std::vector<face_descriptor_t>::iterator fIter = std::find(vd.m_faces.begin(), vd.m_faces.end(), f);
|
||
|
|
||
|
if (fIter != vd.m_faces.cend()) {
|
||
|
vd.m_faces.erase(fIter); // remove association
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
m_faces_removed.emplace_back(f);
|
||
|
}
|
||
|
|
||
|
// also disassociates (not remove) the halfedges(s) and vertex incident to this halfedge
|
||
|
void hmesh_t::remove_halfedge(halfedge_descriptor_t h)
|
||
|
{
|
||
|
MCUT_ASSERT(h != null_halfedge());
|
||
|
MCUT_ASSERT(std::find(m_halfedges_removed.cbegin(), m_halfedges_removed.cend(), h) == m_halfedges_removed.cend());
|
||
|
|
||
|
halfedge_data_t& hd = m_halfedges[h];
|
||
|
|
||
|
MCUT_ASSERT(hd.e == null_edge()); // there must not be an edge dependent on h if we are to remove h
|
||
|
MCUT_ASSERT(hd.f == null_face()); // there must not be a face dependent on h if we are to remove h
|
||
|
|
||
|
if (hd.n != null_halfedge()) { // disassociate next
|
||
|
const halfedge_descriptor_t hn = hd.n;
|
||
|
halfedge_data_t& hnd = m_halfedges[hn];
|
||
|
MCUT_ASSERT(hnd.p == h);
|
||
|
hnd.p = null_halfedge();
|
||
|
//
|
||
|
hd.n = null_halfedge();
|
||
|
}
|
||
|
|
||
|
if (hd.o != null_halfedge()) { // disassociate opposite
|
||
|
const halfedge_descriptor_t ho = hd.o;
|
||
|
halfedge_data_t& hod = m_halfedges[ho];
|
||
|
MCUT_ASSERT(hod.o == h);
|
||
|
hod.o = null_halfedge();
|
||
|
//
|
||
|
hd.o = null_halfedge();
|
||
|
}
|
||
|
|
||
|
if (hd.p != null_halfedge()) { // disassociate previous
|
||
|
const halfedge_descriptor_t hp = hd.p;
|
||
|
halfedge_data_t& hpd = m_halfedges[hp];
|
||
|
MCUT_ASSERT(hpd.n == h);
|
||
|
hpd.n = null_halfedge();
|
||
|
//
|
||
|
hd.p = null_halfedge();
|
||
|
}
|
||
|
|
||
|
MCUT_ASSERT(hd.t != null_vertex()); // every h has a target vertex which is effectively dependent on h
|
||
|
|
||
|
// disassociate target vertex
|
||
|
vertex_data_t& htd = m_vertices[hd.t];
|
||
|
std::vector<halfedge_descriptor_t>::iterator hIter = std::find(htd.m_halfedges.begin(), htd.m_halfedges.end(), h);
|
||
|
|
||
|
MCUT_ASSERT(hIter != htd.m_halfedges.end()); // because not yet removed h
|
||
|
|
||
|
htd.m_halfedges.erase(hIter); // remove association
|
||
|
|
||
|
m_halfedges_removed.emplace_back(h);
|
||
|
}
|
||
|
|
||
|
// also disassociates (not remove) any face(s) incident to edge via its halfedges, and also disassociates the halfedges
|
||
|
void hmesh_t::remove_edge(const edge_descriptor_t e, bool remove_halfedges)
|
||
|
{
|
||
|
MCUT_ASSERT(e != null_edge());
|
||
|
MCUT_ASSERT(std::find(m_edges_removed.cbegin(), m_edges_removed.cend(), e) == m_edges_removed.cend());
|
||
|
|
||
|
edge_data_t& ed = m_edges[e];
|
||
|
std::vector<halfedge_descriptor_t> halfedges = { ed.h, opposite(ed.h) }; // both halfedges incident to edge must be disassociated
|
||
|
|
||
|
for (std::vector<halfedge_descriptor_t>::const_iterator it = halfedges.cbegin(); it != halfedges.cend(); ++it) {
|
||
|
const halfedge_descriptor_t h = *it;
|
||
|
MCUT_ASSERT(h != null_halfedge());
|
||
|
|
||
|
// disassociate halfedge
|
||
|
halfedge_data_t& hd = m_halfedges[h];
|
||
|
MCUT_ASSERT(hd.e == e);
|
||
|
hd.e = null_edge();
|
||
|
if (remove_halfedges) {
|
||
|
remove_halfedge(h);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ed.h = null_halfedge(); // we are removing the edge so every associated data element must be nullified
|
||
|
|
||
|
m_edges_removed.emplace_back(e);
|
||
|
}
|
||
|
|
||
|
void hmesh_t::remove_vertex(const vertex_descriptor_t v)
|
||
|
{
|
||
|
MCUT_ASSERT(v != null_vertex());
|
||
|
MCUT_ASSERT((size_t)v < m_vertices.size());
|
||
|
MCUT_ASSERT(std::find(m_vertices_removed.cbegin(), m_vertices_removed.cend(), v) == m_vertices_removed.cend());
|
||
|
//MCUT_ASSERT(m_vertices[v].m_faces.empty());
|
||
|
MCUT_ASSERT(m_vertices[v].m_halfedges.empty());
|
||
|
|
||
|
m_vertices_removed.emplace_back(v);
|
||
|
}
|
||
|
|
||
|
void hmesh_t::remove_elements()
|
||
|
{
|
||
|
for (face_array_iterator_t i = faces_begin(); i != faces_end(); ++i) {
|
||
|
remove_face(*i);
|
||
|
}
|
||
|
|
||
|
for (edge_array_iterator_t i = edges_begin(); i != edges_end(); ++i) {
|
||
|
remove_edge(*i);
|
||
|
}
|
||
|
|
||
|
for (halfedge_array_iterator_t i = halfedges_begin(); i != halfedges_end(); ++i) {
|
||
|
remove_halfedge(*i);
|
||
|
}
|
||
|
|
||
|
for (vertex_array_iterator_t i = vertices_begin(); i != vertices_end(); ++i) {
|
||
|
remove_vertex(*i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void hmesh_t::reset()
|
||
|
{
|
||
|
m_vertices.clear();
|
||
|
m_vertices.shrink_to_fit();
|
||
|
m_vertices_removed.clear();
|
||
|
m_vertices_removed.shrink_to_fit();
|
||
|
m_halfedges.clear();
|
||
|
m_halfedges.shrink_to_fit();
|
||
|
m_halfedges_removed.clear();
|
||
|
m_halfedges_removed.shrink_to_fit();
|
||
|
m_edges.clear();
|
||
|
m_edges.shrink_to_fit();
|
||
|
m_edges_removed.clear();
|
||
|
m_edges_removed.shrink_to_fit();
|
||
|
m_faces.clear();
|
||
|
m_faces.shrink_to_fit();
|
||
|
m_faces_removed.clear();
|
||
|
m_faces_removed.shrink_to_fit();
|
||
|
}
|
||
|
|
||
|
int hmesh_t::number_of_internal_faces() const
|
||
|
{
|
||
|
return static_cast<int>(m_faces.size());
|
||
|
}
|
||
|
|
||
|
int hmesh_t::number_of_internal_edges() const
|
||
|
{
|
||
|
return static_cast<int>(m_edges.size());
|
||
|
}
|
||
|
|
||
|
int hmesh_t::number_of_internal_halfedges() const
|
||
|
{
|
||
|
return static_cast<int>(m_halfedges.size());
|
||
|
}
|
||
|
|
||
|
int hmesh_t::number_of_internal_vertices() const
|
||
|
{
|
||
|
return static_cast<int>(m_vertices.size());
|
||
|
}
|
||
|
|
||
|
//
|
||
|
int hmesh_t::number_of_vertices_removed() const
|
||
|
{
|
||
|
return (int)this->m_vertices_removed.size();
|
||
|
}
|
||
|
|
||
|
int hmesh_t::number_of_edges_removed() const
|
||
|
{
|
||
|
return (int)this->m_edges_removed.size();
|
||
|
}
|
||
|
|
||
|
int hmesh_t::number_of_halfedges_removed() const
|
||
|
{
|
||
|
return (int)this->m_halfedges_removed.size();
|
||
|
}
|
||
|
|
||
|
int hmesh_t::number_of_faces_removed() const
|
||
|
{
|
||
|
return (int)this->m_faces_removed.size();
|
||
|
}
|
||
|
|
||
|
bool hmesh_t::is_removed(face_descriptor_t f) const
|
||
|
{
|
||
|
return std::find(m_faces_removed.cbegin(), m_faces_removed.cend(), f) != m_faces_removed.cend();
|
||
|
}
|
||
|
|
||
|
bool hmesh_t::is_removed(edge_descriptor_t e) const
|
||
|
{
|
||
|
return std::find(m_edges_removed.cbegin(), m_edges_removed.cend(), e) != m_edges_removed.cend();
|
||
|
}
|
||
|
|
||
|
bool hmesh_t::is_removed(halfedge_descriptor_t h) const
|
||
|
{
|
||
|
return std::find(m_halfedges_removed.cbegin(), m_halfedges_removed.cend(), h) != m_halfedges_removed.cend();
|
||
|
}
|
||
|
|
||
|
bool hmesh_t::is_removed(vertex_descriptor_t v) const
|
||
|
{
|
||
|
return std::find(m_vertices_removed.cbegin(), m_vertices_removed.cend(), v) != m_vertices_removed.cend();
|
||
|
}
|
||
|
|
||
|
void hmesh_t::reserve_for_additional_vertices(std::uint32_t n)
|
||
|
{
|
||
|
m_vertices.reserve((std::uint64_t)number_of_internal_vertices() + n);
|
||
|
}
|
||
|
|
||
|
void hmesh_t::reserve_for_additional_edges(std::uint32_t n)
|
||
|
{
|
||
|
m_edges.reserve((std::uint64_t)number_of_internal_edges() + n);
|
||
|
}
|
||
|
|
||
|
void hmesh_t::reserve_for_additional_halfedges(std::uint32_t n)
|
||
|
{
|
||
|
m_halfedges.reserve((std::uint64_t)number_of_internal_halfedges() + n);
|
||
|
}
|
||
|
|
||
|
void hmesh_t::reserve_for_additional_faces(std::uint32_t n)
|
||
|
{
|
||
|
m_faces.reserve((std::uint64_t)number_of_internal_faces() + n);
|
||
|
}
|
||
|
|
||
|
void hmesh_t::reserve_for_additional_elements(std::uint32_t n)
|
||
|
{
|
||
|
const std::uint32_t nv = n;
|
||
|
reserve_for_additional_vertices(nv);
|
||
|
const std::uint32_t nf = nv * 2;
|
||
|
reserve_for_additional_faces(nf);
|
||
|
const std::uint32_t ne = std::uint32_t((3.0 / 2.0) * (double)nf);
|
||
|
reserve_for_additional_edges(ne);
|
||
|
const std::uint32_t nh = ne * 2;
|
||
|
reserve_for_additional_halfedges(nh);
|
||
|
}
|
||
|
|
||
|
const std::vector<vertex_descriptor_t>& hmesh_t::get_removed_elements(id_<array_iterator_t<vertex_array_t>>) const
|
||
|
{
|
||
|
return get_removed_vertices();
|
||
|
}
|
||
|
|
||
|
const std::vector<edge_descriptor_t>& hmesh_t::get_removed_elements(id_<array_iterator_t<edge_array_t>>) const
|
||
|
{
|
||
|
return get_removed_edges();
|
||
|
}
|
||
|
|
||
|
const std::vector<halfedge_descriptor_t>& hmesh_t::get_removed_elements(id_<array_iterator_t<halfedge_array_t>>) const
|
||
|
{
|
||
|
return get_removed_halfedges();
|
||
|
}
|
||
|
|
||
|
const std::vector<face_descriptor_t>& hmesh_t::get_removed_elements(id_<array_iterator_t<face_array_t>>) const
|
||
|
{
|
||
|
return get_removed_faces();
|
||
|
}
|
||
|
|
||
|
const std::vector<vertex_descriptor_t>& hmesh_t::get_removed_vertices() const
|
||
|
{
|
||
|
return m_vertices_removed;
|
||
|
}
|
||
|
|
||
|
const std::vector<edge_descriptor_t>& hmesh_t::get_removed_edges() const
|
||
|
{
|
||
|
return m_edges_removed;
|
||
|
}
|
||
|
|
||
|
const std::vector<halfedge_descriptor_t>& hmesh_t::get_removed_halfedges() const
|
||
|
{
|
||
|
return m_halfedges_removed;
|
||
|
}
|
||
|
|
||
|
const std::vector<face_descriptor_t>& hmesh_t::get_removed_faces() const
|
||
|
{
|
||
|
return m_faces_removed;
|
||
|
}
|
||
|
|
||
|
const vertex_array_iterator_t hmesh_t::elements_begin_(id_<array_iterator_t<vertex_array_t>>, bool account_for_removed_elems) const
|
||
|
{
|
||
|
return vertices_begin(account_for_removed_elems);
|
||
|
}
|
||
|
|
||
|
const edge_array_iterator_t hmesh_t::elements_begin_(id_<array_iterator_t<edge_array_t>>, bool account_for_removed_elems) const
|
||
|
{
|
||
|
return edges_begin(account_for_removed_elems);
|
||
|
}
|
||
|
|
||
|
const halfedge_array_iterator_t hmesh_t::elements_begin_(id_<array_iterator_t<halfedge_array_t>>, bool account_for_removed_elems) const
|
||
|
{
|
||
|
return halfedges_begin(account_for_removed_elems);
|
||
|
}
|
||
|
|
||
|
const face_array_iterator_t hmesh_t::elements_begin_(id_<array_iterator_t<face_array_t>>, bool account_for_removed_elems) const
|
||
|
{
|
||
|
return faces_begin(account_for_removed_elems);
|
||
|
}
|
||
|
|
||
|
void write_off(const char* fpath, const hmesh_t& mesh)
|
||
|
{
|
||
|
|
||
|
std::ofstream outfile(fpath);
|
||
|
|
||
|
if (!outfile.is_open()) {
|
||
|
printf("error: could not open file: %s\n", fpath);
|
||
|
std::exit(1);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// file header
|
||
|
//
|
||
|
outfile << "OFF\n";
|
||
|
|
||
|
//
|
||
|
// #vertices, #faces, #edges
|
||
|
//
|
||
|
outfile << mesh.number_of_vertices() << " " << mesh.number_of_faces() << " " << 0 /*mesh.number_of_edges()*/ << "\n";
|
||
|
|
||
|
//
|
||
|
// vertices
|
||
|
//
|
||
|
for (vertex_array_iterator_t iter = mesh.vertices_begin(); iter != mesh.vertices_end(); ++iter) {
|
||
|
// const vertex_data_t& vdata = iter.second;
|
||
|
const vec3& point = mesh.vertex(*iter);
|
||
|
outfile << (double)point.x() << " " << (double)point.y() << " " << (double)point.z() << "\n";
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// edges
|
||
|
//
|
||
|
|
||
|
#if 0
|
||
|
for (typename hmesh_t::edge_iterator_t iter = mesh.edges_begin(); iter != mesh.edges_end(); ++iter) {
|
||
|
const hmesh_t::edge_descriptor_t ed = iter.first;
|
||
|
const hmesh_t::vertex_descriptor_t& v0 = vertex(ed, 0);
|
||
|
const hmesh_t::vertex_descriptor_t& v1 = vertex(ed, 1);
|
||
|
// TODO
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// faces
|
||
|
//
|
||
|
for (face_array_iterator_t iter = mesh.faces_begin(); iter != mesh.faces_end(); ++iter) {
|
||
|
// const typename hmesh_t::face_descriptor_t& fd = iter.first;
|
||
|
const std::vector<vertex_descriptor_t> vertices_around_face = mesh.get_vertices_around_face(*iter);
|
||
|
|
||
|
MCUT_ASSERT(!vertices_around_face.empty());
|
||
|
|
||
|
outfile << vertices_around_face.size() << " ";
|
||
|
|
||
|
for (std::vector<vertex_descriptor_t>::const_iterator i = vertices_around_face.cbegin(); i != vertices_around_face.cend(); ++i) {
|
||
|
outfile << (*i) << " ";
|
||
|
}
|
||
|
outfile << " \n";
|
||
|
}
|
||
|
|
||
|
outfile.close();
|
||
|
}
|
||
|
|
||
|
void read_off(hmesh_t& mesh, const char* fpath)
|
||
|
{
|
||
|
auto next_line = [&](std::ifstream& f, std::string& s) -> bool {
|
||
|
while (getline(f, s)) {
|
||
|
if (s.length() > 1 && s[0] != '#') {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
std::ifstream infile(fpath);
|
||
|
|
||
|
if (!infile.is_open()) {
|
||
|
printf("error: could not open file: %s\n", fpath);
|
||
|
std::exit(1);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// file header
|
||
|
//
|
||
|
std::string header;
|
||
|
if (!next_line(infile, header)) {
|
||
|
printf("error: .off file header not found\n");
|
||
|
std::exit(1);
|
||
|
}
|
||
|
|
||
|
if (header != "OFF") {
|
||
|
printf("error: unrecognised .off file header\n");
|
||
|
std::exit(1);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// #vertices, #faces, #edges
|
||
|
//
|
||
|
std::string info;
|
||
|
if (!next_line(infile, info)) {
|
||
|
printf("error: .off element count not found\n");
|
||
|
std::exit(1);
|
||
|
}
|
||
|
|
||
|
std::istringstream info_stream;
|
||
|
info_stream.str(info);
|
||
|
|
||
|
int nvertices;
|
||
|
int nfaces;
|
||
|
int nedges;
|
||
|
info_stream >> nvertices >> nfaces >> nedges;
|
||
|
|
||
|
//
|
||
|
// vertices
|
||
|
//
|
||
|
std::map<int, vd_t> vmap;
|
||
|
for (int i = 0; i < nvertices; ++i) {
|
||
|
if (!next_line(infile, info)) {
|
||
|
printf("error: .off vertex not found\n");
|
||
|
std::exit(1);
|
||
|
}
|
||
|
std::istringstream vtx_line_stream(info);
|
||
|
|
||
|
double x;
|
||
|
double y;
|
||
|
double z;
|
||
|
vtx_line_stream >> x >> y >> z;
|
||
|
vmap[i] = mesh.add_vertex(x, y, z);
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// edges
|
||
|
//
|
||
|
for (int i = 0; i < nedges; ++i) {
|
||
|
// TODO
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// faces
|
||
|
//
|
||
|
for (auto i = 0; i < nfaces; ++i) {
|
||
|
if (!next_line(infile, info)) {
|
||
|
printf("error: .off file face not found\n");
|
||
|
std::exit(1);
|
||
|
}
|
||
|
std::istringstream face_line_stream(info);
|
||
|
int n; // number of vertices in face
|
||
|
int index;
|
||
|
face_line_stream >> n;
|
||
|
|
||
|
if (n < 3) {
|
||
|
printf("error: invalid polygon vertex count in file (%d)\n", n);
|
||
|
std::exit(1);
|
||
|
}
|
||
|
|
||
|
typename std::vector<vd_t> face;
|
||
|
face.resize(n);
|
||
|
for (int j = 0; j < n; ++j) {
|
||
|
info_stream >> index;
|
||
|
face[j] = vmap[index];
|
||
|
}
|
||
|
|
||
|
mesh.add_face(face);
|
||
|
}
|
||
|
|
||
|
infile.close();
|
||
|
}
|