/**
* 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:
* (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
#include
#include
#include // std::iota
#include
#include
#include
#include
#include
#include "mcut/internal/bvh.h"
#include "mcut/internal/hmesh.h"
#include "mcut/internal/kernel.h"
#include "mcut/internal/math.h"
#include "mcut/internal/timer.h"
#include "mcut/internal/utils.h"
#ifndef LICENSE_PURCHASED
#define lmsg() printf("NOTE: MCUT is copyrighted and may not be sold or included in commercial products without a license.\n")
#else
#define lmsg()
#endif // #ifndef LICENSE_PURCHASED
namespace std {
// need to declare partial and explicit specializations in every translation unit
// that uses them (before any use that would implicitly instantiate that
// specialization)
template <>
typename edge_array_iterator_t::difference_type distance(
edge_array_iterator_t first,
edge_array_iterator_t last);
}
logger_t* logger_ptr = nullptr;
std::string to_string(const sm_frag_location_t& v)
{
std::string s;
switch (v) {
case sm_frag_location_t::ABOVE:
s = "a";
break;
case sm_frag_location_t::BELOW:
s = "b";
break;
case sm_frag_location_t::UNDEFINED:
s = "u";
break;
}
return s;
}
std::string to_string(const cm_patch_location_t& v)
{
std::string s;
switch (v) {
case cm_patch_location_t::INSIDE:
s = "i";
break;
case cm_patch_location_t::OUTSIDE:
s = "o";
break;
case cm_patch_location_t::UNDEFINED:
s = "u";
break;
}
return s;
}
std::string to_string(const status_t& v)
{
std::string s;
switch (v) {
case status_t::SUCCESS:
s = "SUCCESS";
break;
case status_t::INVALID_SRC_MESH:
s = "INVALID_SRC_MESH";
break;
case status_t::INVALID_CUT_MESH:
s = "INVALID_CUT_MESH";
break;
case status_t::INVALID_MESH_INTERSECTION:
s = "INVALID_MESH_INTERSECTION";
break;
case status_t::GENERAL_POSITION_VIOLATION:
s = "GENERAL_POSITION_VIOLATION";
break;
case status_t::DETECTED_FLOATING_POLYGON:
s = "DETECTED_FLOATING_POLYGON";
break;
// case status_t::FACE_VERTEX_INTERSECTION:
// s = "FACE_VERTEX_INTERSECTION";
// break;
}
return s;
}
std::string to_string(const cm_patch_winding_order_t& v)
{
std::string s;
switch (v) {
case cm_patch_winding_order_t::DEFAULT:
s = "def";
break;
case cm_patch_winding_order_t::REVERSE:
s = "rev";
break;
}
return s;
}
// returns whether a polygon-soup vertex is an intersection vertex/point
inline bool m0_is_intersection_point(const vd_t& ps_vd, const int ps_vtx_cnt)
{
return ((int)ps_vd) >= ps_vtx_cnt;
}
// returns whether a polygon-soup vertex belongs to the cut mesh
bool inline ps_is_cutmesh_vertex(const vd_t& ps_vd, const int sm_vtx_cnt)
{
return ((int)ps_vd) >= sm_vtx_cnt;
}
bool inline ps_is_cutmesh_face(const fd_t& ps_fd, const int sm_face_count)
{
return ((int)ps_fd) >= sm_face_count;
}
void dump_mesh(const hmesh_t& mesh, const char* fbasename)
{
const std::string name = std::string(fbasename) + ".off";
for (vertex_array_iterator_t v = mesh.vertices_begin(); v != mesh.vertices_end(); ++v) {
}
for (edge_array_iterator_t e = mesh.edges_begin(); e != mesh.edges_end(); ++e) {
}
for (halfedge_array_iterator_t h = mesh.halfedges_begin(); h != mesh.halfedges_end(); ++h) {
}
for (face_array_iterator_t face_iter = mesh.faces_begin(); face_iter != mesh.faces_end(); ++face_iter) {
const std::vector& halfedges_around_face = mesh.get_halfedges_around_face(*face_iter);
// int num_halfedges = ;
MCUT_ASSERT((int)halfedges_around_face.size());
//
for (std::vector::const_iterator h = halfedges_around_face.cbegin();
h != halfedges_around_face.cend();
++h) {
}
}
write_off(name.c_str(), mesh);
}
#if 0
bool point_on_face_plane(const hmesh_t& m, const fd_t& f, const vec3& p, int& fv_count)
{
const std::vector vertices = m.get_vertices_around_face(f);
fv_count = (int)vertices.size();
{
for (int i = 0; i < fv_count; ++i) {
const int j = (i + 1) % fv_count;
const int k = (i + 2) % fv_count;
const vd_t& vi = vertices[i];
const vd_t& vj = vertices[j];
const vd_t& vk = vertices[k];
const vec3& vi_coords = m.vertex(vi);
const vec3& vj_coords = m.vertex(vj);
const vec3& vk_coords = m.vertex(vk);
const bool are_coplaner = coplaner(vi_coords, vj_coords, vk_coords, p);
if (!are_coplaner) {
return false;
}
}
}
return true;
}
#endif
/*
dfs(node u)
for each node v connected to u :
if v is not visited :
visited[v] = true
dfs(v)
*/
void dfs_cc(vd_t u, const hmesh_t& mesh, std::vector& visited, int connected_component_id)
{
std::vector verts = mesh.get_vertices_around_vertex(u);
for (std::vector::const_iterator v = verts.cbegin(); v != verts.cend(); ++v) {
if (SAFE_ACCESS(visited, *v) == -1) {
visited[*v] = connected_component_id;
dfs_cc(*v, mesh, visited, connected_component_id);
}
}
}
int find_connected_components(
#if defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
thread_pool& scheduler,
#endif
std::vector& fccmap,
const hmesh_t& mesh,
std::vector& cc_to_vertex_count,
std::vector& cc_to_face_count)
{
MCUT_ASSERT(mesh.number_of_vertices() >= 3);
MCUT_ASSERT(mesh.number_of_edges() >= 3);
MCUT_ASSERT(mesh.number_of_faces() >= 1);
/*
for each node u:
if u is not visited :
visited[u] = true
connected_component += 1
dfs(u)
*/
std::vector visited(mesh.number_of_vertices(), -1); // if vertex does not exist, then its not visited
int connected_component_id = -1;
std::vector queued(mesh.number_of_vertices(), false);
std::queue queue; // .. to discover all vertices of current connected component
std::vector vertices_of_v;
std::vector vertices_of_u;
for (vertex_array_iterator_t u = mesh.vertices_begin(); u != mesh.vertices_end(); ++u) {
if (visited[*u] == -1) {
connected_component_id += 1;
visited[*u] = connected_component_id;
queued[*u] = true;
cc_to_vertex_count.push_back(1); // each discovered cc has at least one vertex
vertices_of_u.clear();
mesh.get_vertices_around_vertex(vertices_of_u, *u);
for (int i = 0; i < (int)vertices_of_u.size(); ++i) {
vd_t vou = vertices_of_u[i];
queue.push(vou);
queued[vou] = true;
}
while (!queue.empty()) {
vd_t v = queue.front(); // current
queue.pop();
if (visited[v] == -1) // v not yet associated with a cc
{
visited[v] = connected_component_id;
cc_to_vertex_count[connected_component_id] += 1;
vertices_of_v.clear();
mesh.get_vertices_around_vertex(vertices_of_v, v);
for (int i = 0; i < (int)vertices_of_v.size(); ++i) {
vd_t vov = vertices_of_v[i];
if (visited[vov] == -1 || queued[vov] == false) {
queue.push(vertices_of_v[i]);
queued[vov] = true;
}
}
}
}
// dfs_cc(*u, mesh, visited, connected_component_id);
}
}
fccmap.clear();
fccmap.resize(mesh.number_of_faces());
int num_connected_components = (connected_component_id + 1); // number of CCs
cc_to_face_count.resize(num_connected_components);
#if defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
auto fn_set_cc_to_face_count = [](std::vector::iterator block_start_, std::vector::iterator block_end_) {
for (std::vector::iterator it = block_start_; it != block_end_; ++it) {
*it = 0;
}
};
parallel_for(
scheduler,
cc_to_face_count.begin(),
cc_to_face_count.end(),
fn_set_cc_to_face_count);
#else
for (int i = 0; i < (int)cc_to_face_count.size(); ++i) {
cc_to_face_count[i] = 0;
}
#endif
fccmap.reserve(mesh.number_of_faces());
#if defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
auto fn_map_faces = [&](face_array_iterator_t block_start_, face_array_iterator_t block_end_) {
for (face_array_iterator_t f = block_start_; f != block_end_; ++f) {
const std::vector vertices = mesh.get_vertices_around_face(*f);
int face_cc_id = SAFE_ACCESS(visited, vertices.front());
// all vertices belong to the same conn comp
fccmap[*f] = face_cc_id;
cc_to_face_count[face_cc_id] += 1;
}
};
parallel_for(
scheduler,
mesh.faces_begin(),
mesh.faces_end(),
fn_map_faces);
#else
// map each face to a connected component
for (face_array_iterator_t f = mesh.faces_begin(); f != mesh.faces_end(); ++f) {
const std::vector vertices = mesh.get_vertices_around_face(*f);
int face_cc_id = SAFE_ACCESS(visited, vertices.front());
// all vertices belong to the same conn comp
fccmap[*f] = face_cc_id;
cc_to_face_count[face_cc_id] += 1;
}
#endif
return num_connected_components;
}
struct connected_component_info_t {
sm_frag_location_t location = sm_frag_location_t::UNDEFINED; // above/ below
// vertices along the cut path seam
std::vector seam_vertices;
// mapping from mesh descriptors to input mesh descriptors (vertex and face)
output_mesh_data_maps_t data_maps;
};
// a seam vertex is simply an intersection point, including a duplicated instance if it exists as determined by the
// parameters "ps_num_vertices" and "m1_num_vertices_after_srcmesh_partitioning"
void mark_seam_vertices(
std::vector& mesh_seam_vertices,
hmesh_t& mesh,
const int ps_num_vertices,
const int m1_num_vertices_after_srcmesh_partitioning = std::numeric_limits::max())
{
mesh_seam_vertices.resize(mesh.number_of_vertices());
for (vertex_array_iterator_t i = mesh.vertices_begin();
i != mesh.vertices_end();
++i) {
const int idx = static_cast(*i);
mesh_seam_vertices[*i] = (idx >= ps_num_vertices && idx < m1_num_vertices_after_srcmesh_partitioning);
}
}
// returns the unseparated/merged connected components
hmesh_t extract_connected_components(
#if defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
thread_pool& scheduler,
#endif
// key = cc-id; value = list of cc copies each differing by one newly stitched polygon
std::map, connected_component_info_t>>>& connected_components,
const hmesh_t& in,
const int traced_polygons_base_offset,
const std::vector>& mX_traced_polygons, // "m0" or "m1" (dependent on function-call location)
const std::vector& sm_polygons_below_cs,
const std::vector& sm_polygons_above_cs,
const std::vector& mesh_vertex_to_seam_flag,
// vertex & face data mapping parameters
// const std::map &m1_to_m0_sm_ovtx_colored,
const std::vector& m1_to_m0_sm_ovtx_colored,
const std::unordered_map& m1_to_m0_cm_ovtx_colored,
/*const*/ std::unordered_map& m1_to_m0_face_colored,
// const std::map &m0_to_ps_vtx,
const std::vector& m0_to_ps_vtx,
/*const*/ std::unordered_map& m0_to_ps_face,
// const std::map &ps_to_sm_vtx,
const std::vector& ps_to_sm_vtx,
// const std::map &ps_to_sm_face,
const std::vector& ps_to_sm_face,
// const std::map &ps_to_cm_vtx,
const std::vector& ps_to_cm_vtx,
// const std::map &ps_to_cm_face,
const std::vector& ps_to_cm_face,
const int sm_vtx_cnt,
const int sm_face_count,
bool popuplate_vertex_maps,
bool popuplate_face_maps,
bool keep_fragments_below_cutmesh,
bool keep_fragments_above_cutmesh,
bool keep_fragments_partially_cut)
{
// the auxilliary halfedge mesh containing vertices and edges referenced by the traced polygons
hmesh_t mesh = in; // copy
mesh.reserve_for_additional_elements((std::uint32_t)mX_traced_polygons.size() / 2);
///////////////////////////////////////////////////////////////////////////
// Insert traced polygons into the auxilliary mesh
///////////////////////////////////////////////////////////////////////////
TIMESTACK_PUSH("Extract CC: Insert polygons");
#if defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
{
typedef std::tuple<
std::vector> // "mesh" faces
>
OutputStorageTypesTuple;
typedef std::vector>::const_iterator InputStorageIteratorType;
auto fn_compute_inserted_faces = [&mesh](InputStorageIteratorType block_start_, InputStorageIteratorType block_end_) -> OutputStorageTypesTuple {
OutputStorageTypesTuple local_output;
std::vector>& faces_LOCAL = std::get<0>(local_output);
faces_LOCAL.reserve(std::distance(block_start_, block_end_));
for (InputStorageIteratorType mX_traced_polygons_iter = block_start_; mX_traced_polygons_iter != block_end_; ++mX_traced_polygons_iter) {
const std::vector& mX_traced_polygon = *mX_traced_polygons_iter;
faces_LOCAL.push_back(std::vector());
std::vector& polygon_vertices = faces_LOCAL.back();
polygon_vertices.reserve(mX_traced_polygon.size());
// for each halfedge in polygon
for (std::vector::const_iterator mX_traced_polygon_halfedge_iter = mX_traced_polygon.cbegin();
mX_traced_polygon_halfedge_iter != mX_traced_polygon.cend();
++mX_traced_polygon_halfedge_iter) {
polygon_vertices.push_back(mesh.target(*mX_traced_polygon_halfedge_iter));
}
}
return local_output;
};
std::vector> futures;
OutputStorageTypesTuple partial_res;
parallel_for(
scheduler,
mX_traced_polygons.cbegin(),
mX_traced_polygons.cend(),
fn_compute_inserted_faces,
partial_res, // output computed by master thread
futures);
const std::vector>& faces_MASTER_THREAD_LOCAL = std::get<0>(partial_res);
auto merge_local_faces = [&mesh](const std::vector>& faces_) {
for (std::vector>::const_iterator face_iter = faces_.cbegin();
face_iter != faces_.cend();
++face_iter) {
const fd_t f = mesh.add_face(*face_iter);
MCUT_ASSERT(f != hmesh_t::null_face());
}
};
for (int i = 0; i < (int)futures.size(); ++i) {
std::future& f = futures[i];
MCUT_ASSERT(f.valid());
OutputStorageTypesTuple future_result = f.get(); // "get()" is a blocking function
const std::vector>& faces_FUTURE = std::get<0>(future_result);
merge_local_faces(faces_FUTURE);
}
// merge faces computed by master thread at the end to maintain the same order
// of the traced polygons
merge_local_faces(faces_MASTER_THREAD_LOCAL);
} // endif of parallel scope
#else
// for each traced polygon
for (std::vector>::const_iterator mX_traced_polygons_iter = mX_traced_polygons.cbegin();
mX_traced_polygons_iter != mX_traced_polygons.cend();
++mX_traced_polygons_iter) {
// const int polygon_idx = (int)std::distance(traced_polygons.cbegin(), traced_sm_polygon_iter);
const std::vector& mX_traced_polygon = *mX_traced_polygons_iter;
//
// gather polygon's vertices
//
std::vector polygon_vertices;
polygon_vertices.reserve(mX_traced_polygon.size());
// for each halfedge in polygon
for (std::vector::const_iterator mX_traced_polygon_halfedge_iter = mX_traced_polygon.cbegin();
mX_traced_polygon_halfedge_iter != mX_traced_polygon.cend();
++mX_traced_polygon_halfedge_iter) {
polygon_vertices.push_back(mesh.target(*mX_traced_polygon_halfedge_iter));
}
// insert face into halfedge data structure
const fd_t f = mesh.add_face(polygon_vertices);
// we have violated halfedge data structure construction
// rules probably because we are refering to a halfedge
// and its opposite in one polygon
MCUT_ASSERT(f != hmesh_t::null_face());
}
#endif
TIMESTACK_POP();
///////////////////////////////////////////////////////////////////////////
// find connected components in "mesh"
///////////////////////////////////////////////////////////////////////////
// connected components
std::map> ccID_to_mesh;
// location of each connected component w.r.t cut-mesh (above | below | undefined)
std::map ccID_to_cs_descriptor;
// for each component, we have a map which relates the vertex descriptors (indices) in the
// auxilliary halfedge data structure "mesh" to the (local) vertex descriptors in
// the connected-component.
//
// the "X" in "...mX_..." stands for "0" or "1" depending on where the current function is called from!
// Before "m1" is created in "dispatch", X = "0". Afterwards, X == "1" to signify the fact that the
// input paramater called "in" (in this function) represents "m0" or "m1"
std::map> ccID_to_mX_to_cc_vertex;
// std::map> ccID_to_cc_to_mX_vertex;
std::map> ccID_to_cc_to_mX_vertex;
// the vertex descriptors [in the cc] which are seam vertices!
std::map> cc_to_seam_vertices;
// here we create a map to tag each polygon in "mesh" with the connected component it belongs to.
std::vector fccmap;
TIMESTACK_PUSH("Extract CC: find connected components");
std::vector cc_to_vertex_count;
std::vector cc_to_face_count;
find_connected_components(
#if defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
scheduler,
#endif
fccmap, mesh, cc_to_vertex_count, cc_to_face_count);
TIMESTACK_POP();
//
///////////////////////////////////////////////////////////////////////////
// Map vertex descriptors to each connected component
///////////////////////////////////////////////////////////////////////////
// NOTE: even if the number of connected components is one, we proceed anyway
// because each connected connected excludes unused vertices in "mesh"
TIMESTACK_PUSH("Extract CC: Map vertices");
// for each face in the auxilliary mesh (i.e. traced polygon)
for (face_array_iterator_t face_iter = mesh.faces_begin(); face_iter != mesh.faces_end(); ++face_iter) {
face_descriptor_t fd = *face_iter;
const int face_cc_id = SAFE_ACCESS(fccmap, fd); // get connected component of face
std::map>::iterator ccID_to_mesh_fiter = ccID_to_mesh.find(face_cc_id);
if (ccID_to_mesh_fiter == ccID_to_mesh.end()) {
// create new mesh to store connected component
std::pair>::iterator, bool> p = ccID_to_mesh.insert(std::make_pair(face_cc_id, std::shared_ptr(new hmesh_t)));
ccID_to_mesh_fiter = p.first;
}
std::shared_ptr cc_mesh = ccID_to_mesh_fiter->second;
std::map>::iterator ccID_to_mX_to_cc_vertex_fiter = ccID_to_mX_to_cc_vertex.find(face_cc_id);
if (ccID_to_mX_to_cc_vertex_fiter == ccID_to_mX_to_cc_vertex.end()) {
// create new component descriptor map
std::pair>::iterator, bool> p = ccID_to_mX_to_cc_vertex.insert(std::make_pair(face_cc_id, std::unordered_map()));
ccID_to_mX_to_cc_vertex_fiter = p.first;
}
std::unordered_map& mX_to_cc_vertex = ccID_to_mX_to_cc_vertex_fiter->second;
std::map>::iterator ccID_to_cc_to_mX_vertex_fiter = ccID_to_cc_to_mX_vertex.find(face_cc_id);
if (ccID_to_cc_to_mX_vertex_fiter == ccID_to_cc_to_mX_vertex.end()) {
std::pair>::iterator, bool> p = ccID_to_cc_to_mX_vertex.insert(std::make_pair(face_cc_id, std::vector()));
ccID_to_cc_to_mX_vertex_fiter = p.first;
}
std::vector& cc_to_mX_vertex = ccID_to_cc_to_mX_vertex_fiter->second;
std::map>::iterator cc_to_seam_vertices_fiter = cc_to_seam_vertices.find(face_cc_id);
if (cc_to_seam_vertices_fiter == cc_to_seam_vertices.end()) {
std::pair>::iterator, bool> p = cc_to_seam_vertices.insert(std::make_pair(face_cc_id, std::vector()));
cc_to_seam_vertices_fiter = p.first;
}
std::vector& cc_seam_vertices = cc_to_seam_vertices_fiter->second;
//
// Determine the location of the connected component w.r.t the cut-mesh (above/below/undefined)
//
// check if the current face is already marked as "below" (w.r.t the cut-mesh).
const bool cc_is_below_cs = std::binary_search(sm_polygons_below_cs.cbegin(), sm_polygons_below_cs.cend(), static_cast(fd));
if (cc_is_below_cs) {
// try to save the fact that the current connected component is "below"
std::pair::iterator, bool> p = ccID_to_cs_descriptor.insert(std::make_pair(face_cc_id, sm_frag_location_t::BELOW));
// if 1) insertion did not take place (connected component already registered), and
// 2) the existing connected component at that entry is marked as "above":
// --> partial cut: thus, the notion "above"/"below" is undefined
if (p.second == false && p.first->second == sm_frag_location_t::ABOVE) {
// polygon classed as both above and below cs
// this is because the connected component contains polygons which are both "above"
// and "below" the cutting surface (we have a partial cut)
p.first->second = sm_frag_location_t::UNDEFINED;
}
}
// check if connected component is marked as "above"
const bool cc_is_above_cs = std::binary_search(sm_polygons_above_cs.cbegin(), sm_polygons_above_cs.cend(), static_cast(fd));
if (cc_is_above_cs) {
// try to save the fact that the current connected component is tagged "above"
std::pair::iterator, bool> p = ccID_to_cs_descriptor.insert(std::make_pair(face_cc_id, sm_frag_location_t::ABOVE));
// if 1) insertion did not take place (connected component is already registered), and
// 2) the existing connected component at that entry is marked as "below":
//--> partial cut: connected component has polygon whch are both "above" and "below"
if (p.second == false && p.first->second == sm_frag_location_t::BELOW) {
p.first->second = sm_frag_location_t::UNDEFINED; // polygon classed as both above and below cs
}
}
//
// We now map the vertices of the current face from the auxilliary data
// structure "mesh" to the (local) connected-component
//
//#define EXTRACT_SEAM_HALFEDGES
#ifdef EXTRACT_SEAM_HALFEDGES
std::vector cc_seam_halfedges;
const std::vector& halfedges_on_face = mesh.get_halfedges_around_face(fd);
bool prev_vertex_belonged_to_seam = false; // previous in face
#endif
// for each vertex around the current face
const std::vector vertices_around_face = mesh.get_vertices_around_face(fd); // order according to "halfedges_on_face" (targets)
for (std::vector::const_iterator face_vertex_iter = vertices_around_face.cbegin();
face_vertex_iter != vertices_around_face.cend();
++face_vertex_iter) {
MCUT_ASSERT(ccID_to_mX_to_cc_vertex.find(face_cc_id) != ccID_to_mX_to_cc_vertex.cend());
// if vertex is not already mapped from "mesh" to connected component
if (mX_to_cc_vertex.find(*face_vertex_iter) == mX_to_cc_vertex.end()) {
// MCUT_ASSERT(ccID_to_mesh.find(face_cc_id) != ccID_to_mesh.cend());
// copy vertex from auxilliary data structure "mesh", add it into connected component mesh,
// and save the vertex's descriptor in the conected component mesh.
const vd_t cc_descriptor = cc_mesh->add_vertex(mesh.vertex(*face_vertex_iter));
// map vertex
mX_to_cc_vertex.insert(std::make_pair(*face_vertex_iter, cc_descriptor));
if (popuplate_vertex_maps) {
// SAFE_ACCESS(ccID_to_cc_to_mX_vertex, face_cc_id).insert(std::make_pair(cc_descriptor, *face_vertex_iter));
cc_to_mX_vertex.push_back(*face_vertex_iter);
}
// check if we need to save vertex as being a seam vertex
// std::vector::const_iterator fiter = mesh_vertex_to_seam_flag.find(*face_vertex_iter);
bool is_seam_vertex = (size_t)(*face_vertex_iter) < mesh_vertex_to_seam_flag.size() && SAFE_ACCESS(mesh_vertex_to_seam_flag, *face_vertex_iter); //(size_t)(*face_vertex_iter) < mesh_vertex_to_seam_flag.size(); //fiter != mesh_vertex_to_seam_flag.cend() && fiter->second == true;
if (is_seam_vertex) {
cc_seam_vertices.push_back(cc_descriptor);
#ifdef EXTRACT_SEAM_HALFEDGES
const uint32_t face_vertex_idx = std::distance(vertices_around_face.cbegin(), face_vertex_iter);
const bool is_first_face_vertex = (face_vertex_idx == 0);
bool have_seam_halfedge = prev_vertex_belonged_to_seam;
if (is_first_face_vertex) {
vd_t last_vtx_descr = (*(vertices_around_face.end() - 1));
bool last_vertex_is_seam_vertex = (size_t)(last_vtx_descr) < mesh_vertex_to_seam_flag.size() && SAFE_ACCESS(mesh_vertex_to_seam_flag, last_vtx_descr); //(size_t)(*face_vertex_iter) < mesh_vertex_to_seam_flag.size(); //fiter != mesh_vertex_to_seam_flag.cend() && fiter->second == true;
have_seam_halfedge = (last_vertex_is_seam_vertex);
}
if (have_seam_halfedge) {
const halfedge_descriptor_t seam_he = SAFE_ACCESS(halfedges_on_face, face_vertex_idx); // number of halfedge == number of vertices in face
cc_seam_halfedges.push_back(seam_he);
}
#endif
}
#ifdef EXTRACT_SEAM_HALFEDGES
prev_vertex_belonged_to_seam = is_seam_vertex;
#endif
}
}
} // for (face_array_iterator_t face_iter = mesh.faces_begin(); face_iter != mesh.faces_end(); ++face_iter)
TIMESTACK_POP();
// bool extractingSeams = (m1_num_vertices_after_srcmesh_partitioning == -1);
// stores a flag per connected component indicating whether we should
// keep this CC or throw it away, as per user flags.
std::map ccID_to_keepFlag;
for (std::map>::const_iterator it = ccID_to_mesh.cbegin(); it != ccID_to_mesh.cend(); ++it) {
int ccID = (int)it->first;
std::map::iterator fiter = ccID_to_cs_descriptor.find(ccID);
const bool isSeam = (fiter == ccID_to_cs_descriptor.cend()); // Seams have no notion of "location"
ccID_to_keepFlag[ccID] = isSeam || ((keep_fragments_above_cutmesh && fiter->second == sm_frag_location_t::ABOVE) || //
(keep_fragments_below_cutmesh && fiter->second == sm_frag_location_t::BELOW) || //
(keep_fragments_partially_cut && fiter->second == sm_frag_location_t::UNDEFINED));
}
///////////////////////////////////////////////////////////////////////////
// TODO: shift the logic to add vertices into CC to here (like for face below)
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
// Insert faces into connected components using mapped vertex descriptors
///////////////////////////////////////////////////////////////////////////
std::map> ccID_to_cc_to_mX_face;
for (std::map>::const_iterator it = ccID_to_mesh.cbegin(); it != ccID_to_mesh.cend(); ++it) {
bool userWantsCC = SAFE_ACCESS(ccID_to_keepFlag, it->first);
if (!userWantsCC) {
continue;
}
ccID_to_cc_to_mX_face[it->first] = std::vector();
}
TIMESTACK_PUSH("Extract CC: Map faces");
#if defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
{
typedef std::tuple<
std::vector>, // remapped_faces, // using remapped cc descriptors
std::vector, // the cc id of each remapped
std::vector // the mX mesh i.e. the halfedge data structure we call "mesh" that is remapped
>
OutputStorageTypesTuple;
typedef face_array_iterator_t InputStorageIteratorType;
auto fn_compute_remapped_cc_faces = [&](InputStorageIteratorType block_start_, InputStorageIteratorType block_end_) -> OutputStorageTypesTuple {
OutputStorageTypesTuple local_output;
const uint32_t num_elems = (uint32_t)std::distance(block_start_, block_end_);
std::vector>& remapped_faces_LOCAL = std::get<0>(local_output);
remapped_faces_LOCAL.reserve(num_elems);
std::vector& local_remapped_face_to_ccID = std::get<1>(local_output);
local_remapped_face_to_ccID.reserve(num_elems);
std::vector& local_remapped_face_to_mX_face = std::get<2>(local_output);
local_remapped_face_to_mX_face.reserve(num_elems);
for (face_array_iterator_t face_iter = block_start_; face_iter != block_end_; ++face_iter) {
face_descriptor_t fd = *face_iter;
const size_t cc_id = fccmap[fd]; // the connected component which contains the current face
bool userWantsCC = SAFE_ACCESS(ccID_to_keepFlag, cc_id);
if (!userWantsCC) {
continue;
}
remapped_faces_LOCAL.push_back(std::vector());
std::vector& remapped_face = remapped_faces_LOCAL.back(); // using remapped cc descriptors
local_remapped_face_to_ccID.push_back((int)cc_id);
local_remapped_face_to_mX_face.push_back(fd);
// std::map>::iterator ccID_to_cc_to_mX_face_fiter = ccID_to_cc_to_mX_face.find(cc_id);
MCUT_ASSERT(ccID_to_cc_to_mX_face.find(cc_id) != ccID_to_cc_to_mX_face.cend());
// std::vector &cc_to_mX_face = ccID_to_cc_to_mX_face_fiter->second;
// std::map>::iterator ccID_to_mX_to_cc_vertex_fiter = ;
MCUT_ASSERT(ccID_to_mX_to_cc_vertex.find(cc_id) != ccID_to_mX_to_cc_vertex.end());
std::unordered_map& mX_to_cc_vertex = SAFE_ACCESS(ccID_to_mX_to_cc_vertex, cc_id);
// for each vertex around face
const std::vector vertices_around_face = mesh.get_vertices_around_face(fd);
for (std::vector::const_iterator face_vertex_iter = vertices_around_face.cbegin();
face_vertex_iter != vertices_around_face.cend();
++face_vertex_iter) {
MCUT_ASSERT(ccID_to_mX_to_cc_vertex.find(cc_id) != ccID_to_mX_to_cc_vertex.cend());
/*const*/ std::unordered_map& vertex_map = mX_to_cc_vertex;
const vd_t m1_sm_descr = *face_vertex_iter;
MCUT_ASSERT(vertex_map.find(m1_sm_descr) != vertex_map.cend());
const vd_t cc_descr = SAFE_ACCESS(vertex_map, m1_sm_descr);
remapped_face.push_back(cc_descr);
}
}
return local_output;
};
std::vector> futures;
OutputStorageTypesTuple partial_res;
parallel_for(
scheduler,
mesh.faces_begin(),
mesh.faces_end(),
fn_compute_remapped_cc_faces,
partial_res, // output computed by master thread
futures);
// remapped_faces
const std::vector>& remapped_faces_MASTER_THREAD_LOCAL = std::get<0>(partial_res);
const std::vector& local_remapped_face_to_ccID_MASTER_THREAD_LOCAL = std::get<1>(partial_res);
const std::vector& local_remapped_face_to_mX_face_MASTER_THREAD_LOCAL = std::get<2>(partial_res);
auto merge_local_remapped_cc_faces = [](
const std::vector>& remapped_faces_,
const std::vector& local_remapped_face_to_ccID_,
const std::vector& local_remapped_face_to_mX_face_,
const bool popuplate_face_maps,
std::map>& ccID_to_mesh,
std::map>& ccID_to_cc_to_mX_face) {
for (std::vector>::const_iterator remapped_face_iter = remapped_faces_.cbegin();
remapped_face_iter != remapped_faces_.cend();
++remapped_face_iter) {
uint32_t idx = (uint32_t)std::distance(remapped_faces_.cbegin(), remapped_face_iter);
uint32_t remapped_face_cc_id = SAFE_ACCESS(local_remapped_face_to_ccID_, idx);
MCUT_ASSERT(ccID_to_mesh.find(remapped_face_cc_id) != ccID_to_mesh.end());
std::shared_ptr cc_mesh = SAFE_ACCESS(ccID_to_mesh, remapped_face_cc_id);
fd_t f = cc_mesh->add_face(*remapped_face_iter); // insert the face
MCUT_ASSERT(f != hmesh_t::null_face());
if (popuplate_face_maps) {
// NOTE: "mX" refers to our halfedge data structure called "mesh" (see single threaded code)
std::vector& cc_to_mX_face = SAFE_ACCESS(ccID_to_cc_to_mX_face, remapped_face_cc_id);
MCUT_ASSERT((size_t)f == cc_to_mX_face.size() /*cc_to_mX_face.count(f) == 0*/);
const fd_t fd = local_remapped_face_to_mX_face_[idx];
// cc_to_mX_face[f] = fd;
cc_to_mX_face.push_back(fd);
}
}
};
merge_local_remapped_cc_faces(
remapped_faces_MASTER_THREAD_LOCAL,
local_remapped_face_to_ccID_MASTER_THREAD_LOCAL,
local_remapped_face_to_mX_face_MASTER_THREAD_LOCAL,
popuplate_face_maps,
ccID_to_mesh,
ccID_to_cc_to_mX_face);
// merge thread-local output into global data structures
for (int i = 0; i < (int)futures.size(); ++i) {
std::future& f = futures[i];
MCUT_ASSERT(f.valid());
OutputStorageTypesTuple future_result = f.get(); // "get()" is a blocking function
const std::vector>& remapped_faces_FUTURE = std::get<0>(future_result);
const std::vector& local_remapped_face_to_ccID_FUTURE = std::get<1>(future_result);
const std::vector& local_remapped_face_to_mX_face_FUTURE = std::get<2>(future_result);
merge_local_remapped_cc_faces(
remapped_faces_FUTURE,
local_remapped_face_to_ccID_FUTURE,
local_remapped_face_to_mX_face_FUTURE,
popuplate_face_maps,
ccID_to_mesh,
ccID_to_cc_to_mX_face);
}
} // end of parallel scope
#else
// for each face in the auxilliary data structure "mesh" (traced polygon)
for (face_array_iterator_t face_iter = mesh.faces_begin(); face_iter != mesh.faces_end(); ++face_iter) {
face_descriptor_t fd = *face_iter;
const size_t cc_id = SAFE_ACCESS(fccmap, fd); // the connected component which contains the current face
bool userWantsCC = SAFE_ACCESS(ccID_to_keepFlag, cc_id);
if (!userWantsCC) {
continue;
}
std::vector remapped_face; // using remapped cc descriptors
std::map>::iterator ccID_to_cc_to_mX_face_fiter = ccID_to_cc_to_mX_face.find(cc_id);
MCUT_ASSERT(ccID_to_cc_to_mX_face_fiter != ccID_to_cc_to_mX_face.cend());
std::vector& cc_to_mX_face = ccID_to_cc_to_mX_face_fiter->second;
// std::map>::iterator ccID_to_mX_to_cc_vertex_fiter = ;
MCUT_ASSERT(ccID_to_mX_to_cc_vertex.find(cc_id) != ccID_to_mX_to_cc_vertex.end());
std::unordered_map& mX_to_cc_vertex = SAFE_ACCESS(ccID_to_mX_to_cc_vertex, cc_id);
// for each vertex around face
/*const*/ std::vector vertices_around_face = mesh.get_vertices_around_face(fd);
for (std::vector::/*const_*/ iterator face_vertex_iter = vertices_around_face.begin();
face_vertex_iter != vertices_around_face.end();
++face_vertex_iter) {
MCUT_ASSERT(ccID_to_mX_to_cc_vertex.find(cc_id) != ccID_to_mX_to_cc_vertex.cend());
/*const*/ std::unordered_map& vertex_map = mX_to_cc_vertex;
const vd_t m1_sm_descr = *face_vertex_iter;
MCUT_ASSERT(vertex_map.find(m1_sm_descr) != vertex_map.cend());
const vd_t cc_descr = SAFE_ACCESS(vertex_map, m1_sm_descr);
remapped_face.push_back(cc_descr);
}
MCUT_ASSERT(ccID_to_mesh.find(cc_id) != ccID_to_mesh.end());
std::shared_ptr cc_mesh = SAFE_ACCESS(ccID_to_mesh, cc_id);
fd_t f = cc_mesh->add_face(remapped_face); // insert the face
MCUT_ASSERT(f != hmesh_t::null_face());
if (popuplate_face_maps) {
MCUT_ASSERT((size_t)f == cc_to_mX_face.size() /*cc_to_mX_face.count(f) == 0*/);
// cc_to_mX_face[f] = fd;
cc_to_mX_face.push_back(fd);
}
}
#endif
TIMESTACK_POP();
// Note: at this stage we have our connected components (meshes) with their
// vertices and faces defined
///////////////////////////////////////////////////////////////////////////
// Save the output connected components marked with location
///////////////////////////////////////////////////////////////////////////
TIMESTACK_PUSH("Extract CC: save CCs with location properties");
// for each connected component
for (std::map>::const_iterator cc_iter = ccID_to_mesh.cbegin();
cc_iter != ccID_to_mesh.cend();
++cc_iter) {
const std::size_t& cc_id = cc_iter->first;
bool userWantsCC = SAFE_ACCESS(ccID_to_keepFlag, cc_id);
if (!userWantsCC) {
continue;
}
const std::shared_ptr cc = cc_iter->second;
// The boolean is needed to prevent saving duplicate connected components into the vector "connected_components[cc_id]".
// This can happen because the current function is called for each new cut-mesh polygon that is stitched, during the
// polygon stitching phases. In the other times when the current function is called, we are guarranteed that
// "connected_components[cc_id]" is empty.
//
// The above has the implication that the newly stitched polygon (during the stitching phase) is added to just [one] of the
// discovered connected components (which are of a particular color tag), thus leaving the other connected components to be
// discovered as having exactly the same number of polygons as before since no new polygon has been added to them.
// So to prevent this connected component dupliction issue, a connected component is only added into "connected_components[cc_id]"
// if the following hold:
// 1) "connected_components[cc_id]" is empty (making the added connected component new and unique)
// 2) the most-recent connected component instance at "connected_components[cc_id].back()" has less faces (in which case, always differing by one)
// than the new connected component we wish to add i.e. "cc"
auto cc_fiter = connected_components.find(cc_id);
bool proceed_to_save_mesh = cc_fiter == connected_components.cend() || cc_fiter->second.back().first->number_of_faces() != cc->number_of_faces();
if (proceed_to_save_mesh) {
sm_frag_location_t location = sm_frag_location_t::UNDEFINED;
if (!sm_polygons_below_cs.empty() && !sm_polygons_above_cs.empty()) {
MCUT_ASSERT(ccID_to_cs_descriptor.find(cc_id) != ccID_to_cs_descriptor.cend());
location = SAFE_ACCESS(ccID_to_cs_descriptor, cc_id);
}
connected_component_info_t ccinfo;
ccinfo.location = location;
ccinfo.seam_vertices = std::move(SAFE_ACCESS(cc_to_seam_vertices, cc_id));
//
// Map vertex and face descriptors to original values in the input source- and cut-mesh
// For vertices it is only non-intersection points that have defined mapping otherwise
// the mapped-to value is undefined (hmesh_t::null_vertex())
//
const std::vector& cc_to_mX_vertex = SAFE_ACCESS(ccID_to_cc_to_mX_vertex, cc_id);
if (popuplate_vertex_maps) {
// map cc vertices to original input mesh
// -----------------------------------
ccinfo.data_maps.vertex_map.resize(cc->number_of_vertices());
for (vertex_array_iterator_t i = cc->vertices_begin(); i != cc->vertices_end(); ++i) {
const vd_t cc_descr = *i;
MCUT_ASSERT((size_t)cc_descr < cc_to_mX_vertex.size() /*cc_to_mX_vertex.count(cc_descr) == 1*/);
const vd_t mX_descr = SAFE_ACCESS(cc_to_mX_vertex, cc_descr);
// NOTE: "m1_to_m0_sm_ovtx_colored" contains only non-intersection points from the source mesh
// std::vector::const_iterator m1_to_m0_sm_ovtx_colored_fiter = m1_to_m0_sm_ovtx_colored.find(mX_descr);
bool is_m1_sm_overtex = (size_t)mX_descr < m1_to_m0_sm_ovtx_colored.size(); // m1_to_m0_sm_ovtx_colored_fiter != m1_to_m0_sm_ovtx_colored.cend();
vd_t m0_descr = hmesh_t::null_vertex(); // NOTE: two cut-mesh "m1" original vertices may map to one "m0" vertex (due to winding order duplication)
if (is_m1_sm_overtex) {
m0_descr = SAFE_ACCESS(m1_to_m0_sm_ovtx_colored, mX_descr); // m1_to_m0_sm_ovtx_colored_fiter->second;
} else if (!m1_to_m0_cm_ovtx_colored.empty()) { // are we in the stitching stage..? (calling with "m1")
// Lets search through the map "m1_to_m0_cm_ovtx_colored"
// NOTE: "m1_to_m0_cm_ovtx_colored" contains only non-intersection points from the cut mesh
std::unordered_map::const_iterator m1_to_m0_cm_ovtx_colored_fiter = m1_to_m0_cm_ovtx_colored.find(mX_descr);
bool is_m1_cm_overtex = m1_to_m0_cm_ovtx_colored_fiter != m1_to_m0_cm_ovtx_colored.cend();
if (is_m1_cm_overtex) {
m0_descr = m1_to_m0_cm_ovtx_colored_fiter->second;
}
}
if (m0_descr == hmesh_t::null_vertex()) { // if still not found, then we are strictly "mX" polygons is "m0" polygons
m0_descr = mX_descr;
}
const bool vertex_is_in_input_mesh_or_is_intersection_point = (m0_descr != hmesh_t::null_vertex()); // i.e. is it an original vertex (its not an intersection point/along cut-path)
if (vertex_is_in_input_mesh_or_is_intersection_point) {
// std::map::const_iterator m0_to_ps_vtx_fiter = m0_to_ps_vtx.find(m0_descr);
bool vertex_is_in_input_mesh = (int)m0_descr < (int)m0_to_ps_vtx.size(); // m0_to_ps_vtx_fiter != m0_to_ps_vtx.cend();
vd_t input_mesh_descr = hmesh_t::null_vertex(); // i.e. source-mesh or cut-mesh
if (vertex_is_in_input_mesh) {
// MCUT_ASSERT(m0_to_ps_vtx.count(m0_descr) == 1);
const vd_t ps_descr = SAFE_ACCESS(m0_to_ps_vtx, m0_descr); // m0_to_ps_vtx_fiter->second; // SAFE_ACCESS(m0_to_ps_vtx, m0_descr);
// we don't know whether it belongs to cut-mesh patch or source-mesh, so check
const bool is_cutmesh_vtx = ps_is_cutmesh_vertex(ps_descr, sm_vtx_cnt);
if (is_cutmesh_vtx) {
input_mesh_descr = SAFE_ACCESS(ps_to_cm_vtx, ps_descr);
// add an offset which allows users to deduce which birth/origin mesh (source or cut mesh) a vertex (map value) belongs to.
input_mesh_descr = static_cast(input_mesh_descr + sm_vtx_cnt);
} else { // source-mesh vertex
input_mesh_descr = SAFE_ACCESS(ps_to_sm_vtx, ps_descr);
}
}
MCUT_ASSERT(SAFE_ACCESS(ccinfo.data_maps.vertex_map, cc_descr) == hmesh_t::null_vertex() /*ccinfo.data_maps.vertex_map.count(cc_descr) == 0*/);
ccinfo.data_maps.vertex_map[cc_descr] = input_mesh_descr;
}
}
} // if (popuplate_vertex_maps) {
if (popuplate_face_maps) {
// map face to original input mesh
// -----------------------------------
MCUT_ASSERT(ccID_to_cc_to_mX_face.count(cc_id) == 1);
std::vector& cc_to_mX_face = SAFE_ACCESS(ccID_to_cc_to_mX_face, cc_id);
ccinfo.data_maps.face_map.resize(cc->number_of_faces());
for (face_array_iterator_t f = cc->faces_begin(); f != cc->faces_end(); ++f) {
const fd_t cc_descr = *f;
// account for the fact that the parameter "mX_traced_polygons" may contain only a subset of traced polygons
// need this to compute correct polygon index to access std::maps
// const fd_t cc_descr_offsetted(traced_polygons_base_offset + static_cast(cc_descr));
MCUT_ASSERT((size_t)cc_descr < cc_to_mX_face.size() /*cc_to_mX_face.count(cc_descr) == 1*/);
const fd_t mX_descr = SAFE_ACCESS(cc_to_mX_face, cc_descr);
const fd_t offsetted_mX_descr(traced_polygons_base_offset + static_cast(mX_descr)); // global traced polygon index
int m0_descr = -1;
if (m1_to_m0_face_colored.size() > 0) { // are we calling from during the patch stitching phase..?
const fd_t m1_descr = offsetted_mX_descr;
MCUT_ASSERT(m1_to_m0_face_colored.count(mX_descr) == 1);
m0_descr = SAFE_ACCESS(m1_to_m0_face_colored, m1_descr);
} else {
m0_descr = static_cast(offsetted_mX_descr);
}
MCUT_ASSERT(m0_to_ps_face.count(m0_descr) == 1);
const fd_t ps_descr = SAFE_ACCESS(m0_to_ps_face, m0_descr); // every traced polygon can be mapped back to an input mesh polygon
fd_t input_mesh_descr = hmesh_t::null_face();
const bool from_cutmesh_face = ps_is_cutmesh_face(ps_descr, sm_face_count);
if (from_cutmesh_face) {
MCUT_ASSERT((int)ps_descr < (int)ps_to_cm_face.size());
input_mesh_descr = SAFE_ACCESS(ps_to_cm_face, ps_descr);
// add an offset which allows users to deduce which birth/origin mesh (source or cut mesh) a face (map value) belongs to.
input_mesh_descr = static_cast(input_mesh_descr + sm_face_count);
} else {
MCUT_ASSERT((int)ps_descr < (int)ps_to_sm_face.size());
input_mesh_descr = SAFE_ACCESS(ps_to_sm_face, ps_descr);
}
// map to input mesh face
MCUT_ASSERT(SAFE_ACCESS(ccinfo.data_maps.face_map, cc_descr) == hmesh_t::null_face() /* (ccinfo.data_maps.face_map.count(cc_descr) == 0*/);
ccinfo.data_maps.face_map[cc_descr] = input_mesh_descr;
}
} // if (popuplate_face_maps) {
connected_components[cc_id].emplace_back(cc, std::move(ccinfo));
}
}
TIMESTACK_POP();
return (mesh);
}
bool is_virtual_face(const fd_t& face)
{
return (face == hmesh_t::null_face());
}
/*
@brief: Given a list of sorted vertices which belong to a histogram bin, check that a
particular component (x, y, or z) of their coordinates is not the same amongst two or more vertices
*/
bool have_same_coordinate(
const std::vector>& bin_vertices_sorted,
const int coordinate_index = 0 // 0 = x, 1 = y, 2 = z component
)
{
// for each vertex, compare to all others in vector (compare by given component)
bool is_duplicate = false;
for (std::vector>::const_iterator i = bin_vertices_sorted.begin(); i != bin_vertices_sorted.end(); ++i) {
const vec3& vertex_i_coordinates = i->second;
const double vertex_i_coordinate = vertex_i_coordinates[coordinate_index];
bool vertex_i_coordinate_is_duplicate = false;
for (std::vector>::const_iterator j = bin_vertices_sorted.begin(); j != bin_vertices_sorted.end(); ++j) {
if (j == i) {
continue; // same vertex, skip
}
const vec3& vertex_j_coordinates = j->second;
const double vertex_j_coordinate = vertex_j_coordinates[coordinate_index];
vertex_i_coordinate_is_duplicate = (vertex_i_coordinate == vertex_j_coordinate);
if (vertex_i_coordinate_is_duplicate) {
is_duplicate = true;
break;
}
}
if (is_duplicate) {
break;
}
}
return is_duplicate;
}
// TODO: replace code parts that use "m0_ivtx_to_intersection_registry_entry" with calls to this
// function which is much cheaper
inline bool m0_is_polygon_boundary_halfedge(const hd_t& h, uint32_t m0_num_cutpath_halfedges)
{
return (uint32_t)h >= m0_num_cutpath_halfedges;
}
inline bool m0_is_polygon_boundary_edge(const ed_t& e, uint32_t m0_num_cutpath_edges)
{
return (uint32_t)e >= m0_num_cutpath_edges;
}
// point an intersection halfedge to the correct instance of an intersection point
vd_t resolve_intersection_point_descriptor(
const hmesh_t& ps,
const hmesh_t& m0,
hmesh_t& m1,
const hd_t& m0_h,
const vd_t& m0_h_tgt,
const vd_t& m1_h_tgt,
const bool m0_h_is_ox,
/*const*/ std::vector>& m0_h_to_ply,
/*const*/ std::unordered_map>& ivtx_to_incoming_hlist,
/*const*/ std::unordered_map& m0_sm_ihe_to_flag,
const std::vector>& m0_ivtx_to_intersection_registry_entry,
/*const*/ std::unordered_map& m0_to_m1_ihe,
// const std::map &m0_to_ps_vtx,
const std::vector& m0_to_ps_vtx,
const int ps_vtx_cnt,
const int sm_vtx_cnt,
const int sm_face_count,
const int m0_num_cutpath_halfedges)
{
// the descriptor instance we want to return
vd_t resolved_inst = m1_h_tgt;
// First, we get list of all other halfedges in (in "m0") whose target-vertex
// is the same as the target of the current halfedge
const std::vector& incoming = SAFE_ACCESS(ivtx_to_incoming_hlist, m0_h_tgt);
// the minimum number of halfedges whose target is "m0_h_tgt"
// this "minimum" case come from interior edges
MCUT_ASSERT(incoming.size() >= 2);
// Second, we will now filter "incoming" (those pointing to "m0_h_tgt") by
// keeping only the halfedges which are:
// 1) Processed/transformed (so that we can use it to infer what to do with "resolved_inst")
// 2) are incident to a traced polygon, and
// 3) used by a traced polygon of the src-mesh
//
// The remaining halfedges will be the ones we can use to infer the correct value of "resolved_inst"
std::vector halfedges_across_cut_path = incoming;
// for each halfedge across the cut-path
for (std::vector::iterator halfedge_across_cut_path_iter = halfedges_across_cut_path.begin();
halfedge_across_cut_path_iter != halfedges_across_cut_path.end();) {
const vd_t s = m0.source(*halfedge_across_cut_path_iter);
const vd_t t = m0.target(*halfedge_across_cut_path_iter);
const bool s_is_ivtx = m0_is_intersection_point(s, ps_vtx_cnt);
const bool t_is_ivtx = m0_is_intersection_point(t, ps_vtx_cnt);
// check if the halfedge is only next to the cut-mesh
const bool is_ox_cs_h = (!s_is_ivtx && ps_is_cutmesh_vertex(SAFE_ACCESS(m0_to_ps_vtx, s), sm_vtx_cnt));
const bool is_xo_cs_h = (!t_is_ivtx && ps_is_cutmesh_vertex(SAFE_ACCESS(m0_to_ps_vtx, t), sm_vtx_cnt));
bool is_strictly_cs_h = is_ox_cs_h || is_xo_cs_h; // check if halfedge is used only by a cut-surface polygon
const bool is_xx = s_is_ivtx && t_is_ivtx;
if (!is_strictly_cs_h && is_xx) {
#if 0
//const hd_t s_ps_h = SAFE_ACCESS(m0_ivtx_to_ps_edge, s);
//const hd_t t_ps_h = SAFE_ACCESS(m0_ivtx_to_ps_edge, t);
MCUT_ASSERT((size_t)s - ps_vtx_cnt < m0_ivtx_to_intersection_registry_entry.size() /*m0_ivtx_to_intersection_registry_entry.find(s) != m0_ivtx_to_intersection_registry_entry.cend()*/);
const ed_t s_ps_e = SAFE_ACCESS(m0_ivtx_to_intersection_registry_entry, s - ps_vtx_cnt).first; // SAFE_ACCESS(m0_ivtx_to_ps_edge, s); // ps.edge(s_ps_h);
MCUT_ASSERT((size_t)t - ps_vtx_cnt < m0_ivtx_to_intersection_registry_entry.size() /*m0_ivtx_to_intersection_registry_entry.find(t) != m0_ivtx_to_intersection_registry_entry.cend()*/);
const ed_t t_ps_e = SAFE_ACCESS(m0_ivtx_to_intersection_registry_entry, t - ps_vtx_cnt).first; //SAFE_ACCESS(m0_ivtx_to_ps_edge, t); // ps.edge(t_ps_h);
#else
const bool is_boundary_halfedge = m0_is_polygon_boundary_halfedge(
*halfedge_across_cut_path_iter,
m0_num_cutpath_halfedges);
#endif
const bool oh_is_exterior = is_boundary_halfedge; //(s_ps_e == t_ps_e); // lays on exterior of ps polygon
if (oh_is_exterior) {
MCUT_ASSERT((size_t)s - ps_vtx_cnt < m0_ivtx_to_intersection_registry_entry.size() /*m0_ivtx_to_intersection_registry_entry.find(s) != m0_ivtx_to_intersection_registry_entry.cend()*/);
const ed_t s_ps_e = SAFE_ACCESS(m0_ivtx_to_intersection_registry_entry, (std::size_t)s - ps_vtx_cnt).first; // SAFE_ACCESS(m0_ivtx_to_ps_edge, s); // ps.edge(s_ps_h);
const hd_t s_ps_h0 = ps.halfedge(s_ps_e, 0); // could alternatively use t_ps_e since both he's are part of same edge
fd_t incident_face = ps.face(s_ps_h0);
if (incident_face == hmesh_t::null_face()) {
const hd_t s_ps_h1 = ps.halfedge(s_ps_e, 1);
incident_face = ps.face(s_ps_h1);
MCUT_ASSERT(incident_face != hmesh_t::null_face());
}
// TODO: use "ps_is_cutmesh_vertex" since it will require using much less queries on ps
is_strictly_cs_h = ps_is_cutmesh_face(incident_face, sm_face_count);
}
}
// if
// 1) halfedge strictly belongs to the cut-mesh, OR
// 2) halfedge is not used for tracing, OR
// 3) halfedge has not been processed
if (is_strictly_cs_h || //
SAFE_ACCESS(m0_h_to_ply, *halfedge_across_cut_path_iter).size() == 0 /*m0_h_to_ply.find(*halfedge_across_cut_path_iter) == m0_h_to_ply.end()*/ || //
SAFE_ACCESS(m0_sm_ihe_to_flag, *halfedge_across_cut_path_iter) == false) { // is halfedge incident to a traced polygon and is it processed..?
halfedge_across_cut_path_iter = halfedges_across_cut_path.erase(halfedge_across_cut_path_iter);
} else {
++halfedge_across_cut_path_iter; // next
}
}
// there exists not transformed halfedges connected to the current halfedge
if (halfedges_across_cut_path.empty()) {
return resolved_inst; // return the original descriptor
}
// At this, point we have found a number of halfedges which share "m0_h_tgt"
// with the current halfedge. So we need to decide what value (instance) of
// "m0_h_tgt" we should assign "resolved_inst"
// We classify "halfedges_across_cut_path" into two sets:
// 1) "halfedges_on_same_side" (... as m0_h )
// 2) "halfedges_across_cut_path" (other-side)
//
std::vector halfedges_on_same_side;
if (m0_h_is_ox) {
//
// check if the opposite halfedge has been transformed
//
// get opposite halfedge of the current halfedge (m0_h)
const hd_t opp = m0.opposite(m0_h);
if (SAFE_ACCESS(m0_h_to_ply, opp).size() > 0 /*m0_h_to_ply.find(opp) != m0_h_to_ply.end()*/) { // was the opposite halfedge used to trace a polygon
// get the previous of the opposite halfedge (because it is one of the "incoming" halfedges)
const hd_t prv_opp = m0.prev(opp);
if (SAFE_ACCESS(m0_sm_ihe_to_flag, prv_opp)) { // is halfedge processed
halfedges_on_same_side.push_back(prv_opp);
// prv_opp is guarranteed to be in halfedges_on_same_side becz halfedges_across_cut_path is simply a vec of all incoming hes
halfedges_across_cut_path.erase(std::find(halfedges_across_cut_path.begin(), halfedges_across_cut_path.end(), prv_opp));
}
}
} else if (SAFE_ACCESS(m0_h_to_ply, m0_h).size() == 0 /*m0_h_to_ply.find(m0_h) == m0_h_to_ply.end()*/) // edge-case when src-mesh is not watertight (e.g. test 21)
{
MCUT_ASSERT(halfedges_across_cut_path.size() == 1);
const hd_t& h = halfedges_across_cut_path.front();
const hd_t& h_proc = SAFE_ACCESS(m0_to_m1_ihe, h);
vd_t h_tgt = m1.target(h_proc);
const vd_t tgt_copy = m1.add_vertex(m1.vertex(h_tgt)); // make a copy
resolved_inst = tgt_copy;
} else { // then halfedge is either xx or xo (see the conditions with which function is called)
const hd_t nxt = m0.next(m0_h);
const hd_t opp_nxt = m0.opposite(nxt);
// MCUT_ASSERT(opp_nxt != hmesh_t::null_halfedge());
// if halfedge incident to traced polygon and is it processed
if (SAFE_ACCESS(m0_h_to_ply, opp_nxt).size() > 0 /*m0_h_to_ply.find(opp_nxt) != m0_h_to_ply.end()*/ && SAFE_ACCESS(m0_sm_ihe_to_flag, opp_nxt)) {
const vd_t nxt_src = m0_h_tgt; // i.e. m0.source(nxt);
const vd_t nxt_tgt = m0.target(nxt);
const bool nxt_src_is_itvx = m0_is_intersection_point(nxt_src, ps_vtx_cnt);
const bool nxt_tgt_is_itvx = m0_is_intersection_point(nxt_tgt, ps_vtx_cnt);
const bool nxt_is_xx = nxt_src_is_itvx && nxt_tgt_is_itvx;
bool on_same_side = true;
if (nxt_is_xx) {
#if 0
//const hd_t nxt_src_ps_h = SAFE_ACCESS(m0_ivtx_to_ps_edge, nxt_src);
//const hd_t nxt_tgt_ps_h = SAFE_ACCESS(m0_ivtx_to_ps_edge, nxt_tgt);
MCUT_ASSERT((size_t)nxt_src - ps_vtx_cnt < m0_ivtx_to_intersection_registry_entry.size() /*m0_ivtx_to_intersection_registry_entry.find(nxt_src) != m0_ivtx_to_intersection_registry_entry.cend()*/);
const ed_t nxt_src_ps_e = SAFE_ACCESS(m0_ivtx_to_intersection_registry_entry, nxt_src - ps_vtx_cnt).first; // SAFE_ACCESS(m0_ivtx_to_ps_edge, nxt_src); // ps.edge(nxt_src_ps_h);
MCUT_ASSERT((size_t)nxt_tgt - ps_vtx_cnt < m0_ivtx_to_intersection_registry_entry.size() /*m0_ivtx_to_intersection_registry_entry.find(nxt_tgt) != m0_ivtx_to_intersection_registry_entry.cend()*/);
const ed_t nxt_tgt_ps_e = SAFE_ACCESS(m0_ivtx_to_intersection_registry_entry, nxt_tgt - ps_vtx_cnt).first; // SAFE_ACCESS(m0_ivtx_to_ps_edge, nxt_tgt); // ps.edge(nxt_tgt_ps_h);
#else
const bool is_boundary_halfedge = m0_is_polygon_boundary_halfedge(
nxt,
m0_num_cutpath_halfedges);
#endif
const bool nxt_is_exterior = is_boundary_halfedge; //(nxt_src_ps_e == nxt_tgt_ps_e); // lays on exterior of ps polygon
on_same_side = nxt_is_exterior;
}
if (on_same_side) {
halfedges_on_same_side.push_back(opp_nxt);
halfedges_across_cut_path.erase(std::find(halfedges_across_cut_path.begin(), halfedges_across_cut_path.end(), opp_nxt));
}
}
}
//
// Decide what to do with target(h) i.e. determine the correct value for "resolved_inst"
//
if (!halfedges_on_same_side.empty()) { // do we already have a halfedge on the [same side] which is tranformed...?
const hd_t& ss_h = halfedges_on_same_side.front(); // we can retrieve any one
const hd_t& ss_h_proc = SAFE_ACCESS(m0_to_m1_ihe, ss_h); // m1 version
resolved_inst = m1.target(ss_h_proc); // update reference
} else { // do we already have a halfedge on the [other side] which is tranformed...?
MCUT_ASSERT(!halfedges_across_cut_path.empty());
const hd_t& h = halfedges_across_cut_path.front();
MCUT_ASSERT(m0_to_m1_ihe.find(h) != m0_to_m1_ihe.cend());
const hd_t& h_proc = SAFE_ACCESS(m0_to_m1_ihe, h);
MCUT_ASSERT((uint32_t)h_proc < (uint32_t)m1.number_of_halfedges());
vd_t h_tgt = m1.target(h_proc);
MCUT_ASSERT((uint32_t)h_tgt < (uint32_t)m1.number_of_vertices());
const vec3 vertex = m1.vertex(h_tgt);
const vd_t tgt_copy = m1.add_vertex(vertex); // make a copy
resolved_inst = tgt_copy;
}
return resolved_inst;
};
inline std::vector ps_get_ivtx_registry_entry_faces(const hmesh_t& ps, const std::pair& ivtx_registry_entry)
{
const hd_t h0 = ps.halfedge(ivtx_registry_entry.first, 0);
const hd_t h1 = ps.halfedge(ivtx_registry_entry.first, 1);
const fd_t h0_face = ps.face(h0);
const fd_t h1_face = ps.face(h1);
return { ivtx_registry_entry.second, h0_face, h1_face };
}
//
// update the m0 edges incident on two given intersecting faces of the polygon soup mesh
// "incident" just means that the edge will be used to clip the face in question.
void update_neighouring_ps_iface_m0_edge_list(
const vd_t& src_vertex,
const vd_t& tgt_vertex,
const hmesh_t& ps,
const fd_t sm_face,
const fd_t cs_face,
const std::vector>& m0_ivtx_to_intersection_registry_entry,
std::unordered_map>& ps_iface_to_m0_edge_list,
const std::vector& m0_cutpath_edges)
{
const int ps_vtx_cnt = ps.number_of_vertices();
// for all neighbours of "sm_face" and "cs_face"
// if the face is in the registry of src and tgt vertex
// get edge list of face
// if list does not already contain new edge
// add new edge to list
std::vector neighbouring_ifaces;
for (auto neigh_face : { sm_face, cs_face }) {
const std::vector faces_around_face = ps.get_faces_around_face(neigh_face);
neighbouring_ifaces.insert(neighbouring_ifaces.end(), faces_around_face.cbegin(), faces_around_face.cend());
}
MCUT_ASSERT((size_t)src_vertex - ps_vtx_cnt < m0_ivtx_to_intersection_registry_entry.size() /*m0_ivtx_to_intersection_registry_entry.find(src_vertex) != m0_ivtx_to_intersection_registry_entry.cend()*/);
const std::pair& src_vertex_ipair = SAFE_ACCESS(m0_ivtx_to_intersection_registry_entry, (std::size_t)src_vertex - ps_vtx_cnt);
const std::vector src_registry = ps_get_ivtx_registry_entry_faces(ps, src_vertex_ipair); // SAFE_ACCESS(m0_ivtx_to_ps_faces, src_vertex);
MCUT_ASSERT((size_t)tgt_vertex - ps_vtx_cnt < m0_ivtx_to_intersection_registry_entry.size() /*m0_ivtx_to_intersection_registry_entry.find(tgt_vertex) != m0_ivtx_to_intersection_registry_entry.cend()*/);
const std::pair& tgt_vertex_ipair = SAFE_ACCESS(m0_ivtx_to_intersection_registry_entry, (std::size_t)tgt_vertex - ps_vtx_cnt);
const std::vector tgt_registry = ps_get_ivtx_registry_entry_faces(ps, tgt_vertex_ipair); // SAFE_ACCESS(m0_ivtx_to_ps_faces, tgt_vertex);
// for each face that is a neighbour to either sm-face or cm-face
for (std::vector::const_iterator neigh_face_it = neighbouring_ifaces.cbegin();
neigh_face_it != neighbouring_ifaces.cend();
++neigh_face_it) {
const fd_t iface = *neigh_face_it;
MCUT_ASSERT(iface != sm_face && iface != cs_face);
const bool in_src_reg = std::find(src_registry.cbegin(), src_registry.cend(), iface) != src_registry.cend();
const bool in_tgt_reg = std::find(tgt_registry.cbegin(), tgt_registry.cend(), iface) != tgt_registry.cend();
if (in_src_reg && in_tgt_reg) {
std::unordered_map>::iterator fiter = ps_iface_to_m0_edge_list.find(iface);
bool iface_associated_with_some_edges = true;
if (fiter == ps_iface_to_m0_edge_list.cend()) {
// insert
std::pair>::iterator, bool> p = ps_iface_to_m0_edge_list.insert(std::make_pair(iface, std::vector()));
MCUT_ASSERT(p.second == true);
fiter = p.first;
iface_associated_with_some_edges = false;
}
MCUT_ASSERT(fiter != ps_iface_to_m0_edge_list.cend());
std::vector& iface_m0_edge_list = fiter->second;
bool associate_iface_with_edge = true;
if (iface_associated_with_some_edges) {
bool edge_already_associated_with_iface = std::find(iface_m0_edge_list.cbegin(), iface_m0_edge_list.cend(), m0_cutpath_edges.back()) != iface_m0_edge_list.cend();
associate_iface_with_edge = !(edge_already_associated_with_iface);
}
if (associate_iface_with_edge) {
iface_m0_edge_list.push_back(m0_cutpath_edges.back());
}
}
}
}
typedef std::vector traced_polygon_t;
bool mesh_is_closed(
#if 0 //defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
thread_pool& scheduler,
#endif
const hmesh_t& mesh)
{
bool all_halfedges_incident_to_face = true;
#if 0 // defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
{
printf("mesh=%d\n", (int)mesh.number_of_halfedges());
all_halfedges_incident_to_face = parallel_find_if(
scheduler,
mesh.halfedges_begin(),
mesh.halfedges_end(),
[&](hd_t h) {
const fd_t f = mesh.face(h);
return (f == hmesh_t::null_face());
})
== mesh.halfedges_end();
}
#else
for (halfedge_array_iterator_t iter = mesh.halfedges_begin(); iter != mesh.halfedges_end(); ++iter) {
const fd_t f = mesh.face(*iter);
if (f == hmesh_t::null_face()) {
all_halfedges_incident_to_face = false;
break;
}
}
#endif
return all_halfedges_incident_to_face;
}
// TODO: thsi can be improved by comparing based on the largest component of the difference vector
// sort points along a straight line
std::vector linear_projection_sort(const std::vector>& points)
{
/*
1. pick one point as the origin
2. pick any other point as the vector destination point
3. compute normalize vector from <1> to <2>
4. for each point in list of all points
a) compute unnormalized vector from <1> to <4>
b) project a) onto 3) using scalar product, and save result in list
5. sort points according to scalar products values from <4b>
*/
MCUT_ASSERT(points.size() >= 2);
const std::vector>::const_iterator origin = points.cbegin();
const std::vector>::const_iterator dst = points.cbegin() + 1;
vec3 orig_to_dst_vec = normalize(origin->second - dst->second);
std::vector> point_projections;
for (std::vector>::const_iterator i = points.cbegin(); i != points.cend(); ++i) {
vec3 orig_to_point_vec = (origin->second - i->second);
point_projections.emplace_back(i->first, dot_product(orig_to_point_vec, orig_to_dst_vec));
}
std::sort(point_projections.begin(), point_projections.end(),
[&](const std::pair& a, const std::pair& b) {
return a.second < b.second;
});
std::vector sorted_descriptors;
for (std::vector>::const_iterator i = point_projections.cbegin(); i != point_projections.cend(); ++i) {
sorted_descriptors.push_back(i->first);
}
return sorted_descriptors;
}
//
// entry point
//
void dispatch(output_t& output, const input_t& input)
{
lmsg();
TIMESTACK_PUSH(__FUNCTION__);
logger_t& lg = output.logger;
logger_ptr = &output.logger;
lg.reset();
lg.set_verbose(input.verbose);
#if defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
output.status.store(status_t::SUCCESS);
#endif
const hmesh_t& sm = (*input.src_mesh);
const hmesh_t& cs = (*input.cut_mesh);
if (input.verbose) {
dump_mesh(sm, "src-mesh");
dump_mesh(cs, "cut-mesh");
}
const int sm_vtx_cnt = sm.number_of_vertices();
const int sm_face_count = sm.number_of_faces();
const int cs_face_count = cs.number_of_faces();
const int cs_vtx_count = cs.number_of_vertices();
TIMESTACK_PUSH("Check source mesh is closed");
const bool sm_is_watertight = mesh_is_closed(
#if 0 //defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
*input.scheduler,
#endif
sm);
TIMESTACK_POP();
TIMESTACK_PUSH("Check cut mesh is closed");
const bool cm_is_watertight = mesh_is_closed(
#if 0// defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
*input.scheduler,
#endif
cs);
TIMESTACK_POP();
///////////////////////////////////////////////////////////////////////////
// create polygon soup
///////////////////////////////////////////////////////////////////////////
TIMESTACK_PUSH("Create ps");
hmesh_t ps = sm; // copy
ps.reserve_for_additional_elements(cs.number_of_vertices()); // hint
// std::map ps_to_sm_vtx;
std::vector ps_to_sm_vtx((std::size_t)sm_vtx_cnt + cs.number_of_vertices());
#if 0
std::iota(std::begin(ps_to_sm_vtx), std::end(ps_to_sm_vtx), vd_t(0));
#else
#if defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
{
auto fn_iota = [&](vertex_array_iterator_t block_start_, vertex_array_iterator_t block_end_) {
for (vertex_array_iterator_t v = block_start_; v != block_end_; ++v) {
ps_to_sm_vtx[*v] = *v;
}
};
parallel_for(
*input.scheduler,
sm.vertices_begin(),
sm.vertices_end(),
fn_iota);
}
#else
for (vertex_array_iterator_t v = sm.vertices_begin(); v != sm.vertices_end(); ++v) {
ps_to_sm_vtx[*v] = *v; // one to one mapping since ps is initially a copy of sm!
}
#endif
#endif
// std::map ps_to_sm_face;
std::vector ps_to_sm_face((std::size_t)sm.number_of_faces() + cs.number_of_faces());
#if 0
std::iota(std::begin(ps_to_sm_face), std::end(ps_to_sm_face), fd_t(0));
#else
#if defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
{
auto fn_iota = [&](face_array_iterator_t block_start_, face_array_iterator_t block_end_) {
for (face_array_iterator_t f = block_start_; f != block_end_; ++f) {
ps_to_sm_face[*f] = *f;
}
};
parallel_for(
*input.scheduler,
sm.faces_begin(),
sm.faces_end(),
fn_iota);
}
#else
for (face_array_iterator_t f = sm.faces_begin(); f != sm.faces_end(); ++f) {
ps_to_sm_face[*f] = *f; // one to one mapping since ps is initially a copy of sm!
}
#endif
#endif
// std::map cs_to_ps_vtx;
// std::map ps_to_cm_vtx;
// std::vector cs_to_ps_vtx(cs.number_of_vertices());
std::vector ps_to_cm_vtx((std::size_t)sm_vtx_cnt + cs.number_of_vertices());
// merge cm vertices
for (auto i = cs.vertices_begin(); i != cs.vertices_end(); ++i) {
const vd_t v = ps.add_vertex(cs.vertex(*i));
MCUT_ASSERT(v != hmesh_t::null_vertex());
// cs_to_ps_vtx.insert(std::make_pair(*i, v));
// cs_to_ps_vtx[*i] = v;
ps_to_cm_vtx[v] = *i;
}
// std::map ps_to_cm_face;
std::vector ps_to_cm_face((std::size_t)sm_face_count + cs_face_count);
// merge cm faces
{
#if defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
{
auto fn_remap_ps_faces = [&](face_array_iterator_t block_start_, face_array_iterator_t block_end_) {
std::vector>> result(std::distance(block_start_, block_end_));
uint32_t counter = 0;
for (face_array_iterator_t i = block_start_; i != block_end_; ++i) {
std::pair>& p = result[counter++];
std::vector& remapped_face_vertices = p.second;
cs.get_vertices_around_face(remapped_face_vertices, *i, sm_vtx_cnt);
p.first = *i;
}
return result;
};
std::vector>>>> futures;
std::vector>> master_thread_res;
parallel_for(
*input.scheduler,
cs.faces_begin(),
cs.faces_end(),
fn_remap_ps_faces,
master_thread_res,
futures);
auto add_faces = [&](const std::vector>>& remapped_faces) {
for (std::vector>>::const_iterator it = remapped_faces.cbegin(); it != remapped_faces.cend(); ++it) {
const std::pair>& p = *it;
const fd_t f = ps.add_face(p.second);
MCUT_ASSERT(f != hmesh_t::null_face());
ps_to_cm_face[f] = p.first;
}
};
for (uint32_t i = 0; i < (uint32_t)futures.size(); ++i) {
const std::vector>> f_res = futures[i].get();
add_faces(f_res);
}
add_faces(master_thread_res);
}
#else // #if defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
std::vector remapped_face_vertices_tmp;
for (face_array_iterator_t i = cs.faces_begin(); i != cs.faces_end(); ++i) {
// std::vector fv = get_vertices_on_face(cs, *i);
cs.get_vertices_around_face(remapped_face_vertices_tmp, *i, sm_vtx_cnt);
const std::vector& remapped_face_vertices = remapped_face_vertices_tmp;
const fd_t f = ps.add_face(remapped_face_vertices);
MCUT_ASSERT(f != hmesh_t::null_face());
ps_to_cm_face[f] = *i;
}
#endif // #if defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
}
TIMESTACK_POP();
// cs_to_ps_vtx.clear();
if (input.verbose) {
dump_mesh(ps, "polygon-soup");
}
const int ps_vtx_cnt = ps.number_of_vertices();
// const int ps_face_cnt = ps.number_of_faces();
//
///////////////////////////////////////////////////////////////////////////
// create the first auxilliary halfedge data structure ("m0")
///////////////////////////////////////////////////////////////////////////
TIMESTACK_PUSH("Create m0");
// The auxilliary data structure stores:
// 1) vertices of the polygon-soup, including new intersection points
// 2) Non-intersecting edges of the polygon-soup
// 3) New edges created from intersection points
hmesh_t m0;
// copy ps vertices into the auxilliary mesh (map is used to maintain original vertex order)
// std::map m0_to_ps_vtx;
std::vector m0_to_ps_vtx; // NOTE: only ps vertices are stored here
// std::map ps_to_m0_vtx;
std::vector ps_to_m0_vtx((std::size_t)sm_vtx_cnt + cs.number_of_vertices());
for (auto i = ps.vertices_begin(); i != ps.vertices_end(); ++i) {
const vd_t v = m0.add_vertex(ps.vertex(*i));
MCUT_ASSERT(v != hmesh_t::null_vertex());
// m0_to_ps_vtx.emplace(v, *i);
m0_to_ps_vtx.emplace_back(*i);
// ps_to_m0_vtx.emplace(*i, v);
ps_to_m0_vtx[*i] = v;
}
TIMESTACK_POP();
MCUT_ASSERT(m0.number_of_vertices() == ps_vtx_cnt); // ... because we have only copied vertices
///////////////////////////////////////////////////////////////////////////
// Calculate polygon intersection points
///////////////////////////////////////////////////////////////////////////
std::unordered_map> ps_edge_face_intersection_pairs;
TIMESTACK_PUSH("Prepare edge-to-face pairs");
#if defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
{ // NOTE: parallel implementation is different from sequential one
typedef std::unordered_map> OutputStorageType;
typedef std::map>::const_iterator InputStorageIteratorType;
std::vector> futures;
auto fn_compute_ps_edge_to_faces_map = [&](InputStorageIteratorType block_start_, InputStorageIteratorType block_end_) {
std::unordered_map> ps_edge_face_intersection_pairs_local;
for (InputStorageIteratorType iter = block_start_; iter != block_end_; ++iter) {
// the face with the intersecting edges (i.e. the edges to be tested against the other face)
const fd_t& intersecting_edge_face = iter->first; // sm_face != hmesh_t::null_face() ? sm_face : cm_face;
const std::vector& halfedges = ps.get_halfedges_around_face(intersecting_edge_face);
for (std::vector::const_iterator hIter = halfedges.cbegin(); hIter != halfedges.cend(); ++hIter) {
const ed_t edge = ps.edge(*hIter);
std::vector& edge_ifaces = ps_edge_face_intersection_pairs_local[edge];
if (edge_ifaces.empty()) {
edge_ifaces = iter->second;
if (edge_ifaces.size() > 1) {
std::sort(edge_ifaces.begin(), edge_ifaces.end()); // alows us to do binary search (std::lower_bound)
}
} else {
for (std::vector::const_iterator iface_iter = iter->second.cbegin();
iface_iter != iter->second.cend();
++iface_iter) {
std::vector::iterator fiter = std::lower_bound(edge_ifaces.begin(), edge_ifaces.end(), *iface_iter);
bool exists = fiter != edge_ifaces.end() && (*fiter == *iface_iter);
if (!exists) {
edge_ifaces.insert(fiter, *iface_iter); // insert and maintain sorted order
}
}
}
}
}
return ps_edge_face_intersection_pairs_local;
};
parallel_for(
*input.scheduler,
input.ps_face_to_potentially_intersecting_others->cbegin(),
input.ps_face_to_potentially_intersecting_others->cend(),
fn_compute_ps_edge_to_faces_map,
ps_edge_face_intersection_pairs, // out
futures);
// merge results from other threads
for (int fi = 0; fi < (int)futures.size(); ++fi) {
std::future& f = futures[fi];
MCUT_ASSERT(f.valid()); // The behavior is undefined if valid()== false before the call to wait_for
OutputStorageType future_res = f.get();
// merge results for current block
for (OutputStorageType::const_iterator i = future_res.cbegin(); i != future_res.cend(); ++i) {
OutputStorageType::iterator fiter = ps_edge_face_intersection_pairs.find(i->first);
if (fiter == ps_edge_face_intersection_pairs.cend()) {
ps_edge_face_intersection_pairs[i->first] = i->second;
} else {
for (std::vector::const_iterator j = i->second.cbegin(); j != i->second.cend(); ++j) {
std::vector::iterator lb_iter = std::lower_bound(fiter->second.begin(), fiter->second.end(), *j);
bool exists = lb_iter != fiter->second.end() && (*lb_iter == *j);
if (!exists) {
fiter->second.insert(lb_iter, *j); // insert and maintain sorted order
}
}
}
}
}
} // end of parallel code
#else
{
std::vector unvisited_ps_ifaces; //= *input.ps_face_to_potentially_intersecting_others;
unvisited_ps_ifaces.reserve(input.ps_face_to_potentially_intersecting_others->size());
// NOTE: the elements of "unvisited_ps_ifaces" are already sorted because they come directly from
// "input.ps_face_to_potentially_intersecting_others", which is an std::map (keys are always sorted)
std::transform(
input.ps_face_to_potentially_intersecting_others->cbegin(),
input.ps_face_to_potentially_intersecting_others->cend(),
std::back_inserter(unvisited_ps_ifaces),
[](const std::pair>& kv) { return kv.first; });
std::vector ps_iface_enqueued(ps.number_of_faces(), false);
std::vector ps_edge_visited(ps.number_of_edges(), false);
// initially null
std::map>::const_iterator cur_ps_cc_face = input.ps_face_to_potentially_intersecting_others->cend();
// start with any face, but we choose the first
std::map>::const_iterator next_ps_cc_face = input.ps_face_to_potentially_intersecting_others->cbegin();
ps_iface_enqueued[next_ps_cc_face->first] = true;
// an element of this queue is an iterator/ptr to an element of "input.ps_face_to_potentially_intersecting_others"
std::queue>::const_iterator> adj_ps_face_queue;
do { // each iteration will find a set of edges that belong to a connected-component patch of intersectng faces (of sm or cm) in ps
cur_ps_cc_face = next_ps_cc_face;
next_ps_cc_face = input.ps_face_to_potentially_intersecting_others->cend(); // set null
// register unique edges of current face, and the add the neighbouring faces to queue
adj_ps_face_queue.push(cur_ps_cc_face);
do { // each interation will add unregistered edges of current face, and add unvisited faces to queue
const std::map>::const_iterator cc_iface = adj_ps_face_queue.front(); // current face of connected-component patch
adj_ps_face_queue.pop();
{ // face is now visisted so we remove it
std::vector::iterator fiter = std::lower_bound(
unvisited_ps_ifaces.begin(),
unvisited_ps_ifaces.end(),
cc_iface->first);
MCUT_ASSERT(fiter != unvisited_ps_ifaces.cend());
unvisited_ps_ifaces.erase(fiter); // NOTE: list remains sorted
}
std::vector cur_ps_face_ifaces_sorted = cc_iface->second; // copy
if (cur_ps_face_ifaces_sorted.size() > 1) {
std::sort(cur_ps_face_ifaces_sorted.begin(), cur_ps_face_ifaces_sorted.end()); // allows quick binary search
}
// bool is_sm_face = cc_iface->first < sm_face_count;
// const fd_t cc_iface_descr = is_sm_face ? cc_iface->first - sm_face_count : sm_face_count;
const std::vector& cur_ps_face_halfedges = ps.get_halfedges_around_face(cc_iface->first);
// all neighbours
const std::vector cur_ps_face_neigh_faces = ps.get_faces_around_face(cc_iface->first, &cur_ps_face_halfedges);
// neighbours [which are intersecting faces]
std::vector cur_ps_face_neigh_ifaces;
cur_ps_face_neigh_ifaces.reserve(cur_ps_face_neigh_faces.size());
for (std::vector::const_iterator face_iter = cur_ps_face_neigh_faces.cbegin();
face_iter != cur_ps_face_neigh_faces.cend();
++face_iter) {
bool is_iface = input.ps_face_to_potentially_intersecting_others->find(*face_iter) != input.ps_face_to_potentially_intersecting_others->cend();
if (is_iface) {
cur_ps_face_neigh_ifaces.push_back(*face_iter);
}
}
if (cur_ps_face_neigh_ifaces.size() > 1) {
std::sort(cur_ps_face_neigh_ifaces.begin(), cur_ps_face_neigh_ifaces.end());
}
// for each halfedge of current iface
for (std::vector::const_iterator hiter = cur_ps_face_halfedges.cbegin(); hiter != cur_ps_face_halfedges.cend(); ++hiter) {
const ed_t halfedge_edge = ps.edge(*hiter);
const hd_t opp_he = ps.opposite(*hiter);
// Here we simply access corresponding element in "cur_ps_face_neigh_faces" based on
// how "ps.get_faces_around_face" populates "cur_ps_face_neigh_faces", given "cur_ps_face_halfedges"
// as done above
if (ps_edge_visited[halfedge_edge] == false) {
ps_edge_face_intersection_pairs.insert(std::make_pair(halfedge_edge, cur_ps_face_ifaces_sorted)); // add edge
ps_edge_visited[halfedge_edge] = true;
bool is_border_ps_face = cur_ps_face_neigh_faces.size() != cur_ps_face_halfedges.size();
const size_t idx = std::distance(cur_ps_face_halfedges.cbegin(), hiter);
fd_t opp_he_face = is_border_ps_face ? ps.face(opp_he) : SAFE_ACCESS(cur_ps_face_neigh_faces, idx);
if (!is_virtual_face(opp_he_face) && ps_iface_enqueued[opp_he_face] == false) { // two neighbouring faces might share more that 1 edge (case of non-triangulated mesh)
bool is_iface = std::binary_search(cur_ps_face_neigh_ifaces.cbegin(), cur_ps_face_neigh_ifaces.cend(), opp_he_face);
if (is_iface) {
std::map>::const_iterator fiter = input.ps_face_to_potentially_intersecting_others->find(opp_he_face);
adj_ps_face_queue.push(fiter);
ps_iface_enqueued[opp_he_face] = true;
}
}
} else {
MCUT_ASSERT(ps_edge_face_intersection_pairs.find(halfedge_edge) != ps_edge_face_intersection_pairs.cend());
// merge shared "intersected" faces.
// Two intersecting faces that share an edge will share intersected faces. The shared faces
// are those intersected by the shared edge.
std::vector& existing_edge_ifaces = ps_edge_face_intersection_pairs[halfedge_edge]; // sorted list
for (std::vector::const_iterator i = cur_ps_face_ifaces_sorted.cbegin(); i != cur_ps_face_ifaces_sorted.cend(); ++i) {
std::vector::iterator iter = std::lower_bound(existing_edge_ifaces.begin(), existing_edge_ifaces.end(), *i);
bool found = iter != existing_edge_ifaces.end() && iter != existing_edge_ifaces.end() && (*iter == *i);
if (!found) {
existing_edge_ifaces.insert(iter, *i); // insert into sorted list (i.e. possibly shifts some elements forward)
}
}
}
} // for each halfedge of current iface
} while (adj_ps_face_queue.empty() == false);
// find "next_ps_cc_face" as any face in "input.ps_face_to_potentially_intersecting_others" that is not visited
if (unvisited_ps_ifaces.size() > 0) {
fd_t next_face = unvisited_ps_ifaces.back(); // pick any unvisited iface (we choose the last for faster elemt removal from std::vector)
next_ps_cc_face = input.ps_face_to_potentially_intersecting_others->find(next_face);
MCUT_ASSERT(next_ps_cc_face != input.ps_face_to_potentially_intersecting_others->cend());
}
} while (next_ps_cc_face != input.ps_face_to_potentially_intersecting_others->cend());
}
// std::unordered_map> ps_edge_face_intersection_pairs;
#endif // #if defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
TIMESTACK_POP();
//
// build bounding boxes for each intersecting edge
//
#if 1
TIMESTACK_PUSH("Build edge bounding boxes");
// http://gamma.cs.unc.edu/RTRI/i3d08_RTRI.pdf
std::unordered_map> ps_edge_to_bbox;
#if defined(MCUT_WITH_COMPUTE_HELPER_THREADPOOL)
{
typedef std::unordered_map> OutputStorageType;
typedef std::unordered_map>::const_iterator InputStorageIteratorType;
auto fn_compute_ps_edge_bbox = [&](InputStorageIteratorType block_start_, InputStorageIteratorType block_end_) {
OutputStorageType ps_edge_to_bbox_local;
for (std::unordered_map