Return edges as planar-component boundaries in CGAL #5485

This commit is contained in:
Thomas Krijnen
2024-10-04 19:11:02 +02:00
parent 0935159c42
commit 07fda60794
3 changed files with 207 additions and 115 deletions
@@ -31,44 +31,6 @@ using IfcGeom::OpaqueCoordinate;
using IfcGeom::NumberNativeDouble;
using IfcGeom::ConversionResultShape;
struct EdgeKey {
int v1, v2;
// These are not part of the hash or equality,
// but retained to easily created a directed
// graph of the original boundary edges. Since
// the boundary edges are exactly those with
// count=1 we don't need to worry about
// conflicting original vertex indices.
int ov1, ov2;
EdgeKey(int a, int b)
: ov1(a)
, ov2(b)
{
if (a < b) {
v1 = a;
v2 = b;
} else {
v1 = b;
v2 = a;
}
}
bool operator==(const EdgeKey& other) const {
return v1 == other.v1 && v2 == other.v2;
}
};
namespace std {
template <>
struct hash<EdgeKey> {
std::size_t operator()(const EdgeKey& ek) const {
return std::hash<int>()(ek.v1) ^ std::hash<int>()(ek.v2);
}
};
}
namespace {
// We bypass the conversion to gp_GTrsf, because it does not work
void taxonomy_transform(const Eigen::Matrix4d* m, gp_XYZ& xyz) {
@@ -80,78 +42,6 @@ namespace {
xyz.ChangeData()[2] = v2(2);
}
}
// Function to find boundary loops from triangles
std::vector<std::vector<int>> find_boundary_loops(const std::vector<double>& positions, const std::vector<std::tuple<int, int, int>>& triangles) {
std::unordered_map<EdgeKey, int> edge_count;
// Count how many triangles each edge belongs to
for (const auto& triangle : triangles) {
int v1, v2, v3;
std::tie(v1, v2, v3) = triangle;
edge_count[{v1, v2}]++;
edge_count[{v2, v3}]++;
edge_count[{v3, v1}]++;
}
// Boundary edges have count 1
std::vector<EdgeKey> boundary_edges;
for (auto& p : edge_count) {
if (p.second == 1) {
boundary_edges.push_back(p.first);
}
}
// We retained original directed edges so we build
// a mapping out of these directed edges.
std::unordered_map<int, int> vertex_successors;
for (const auto& e : boundary_edges) {
vertex_successors[e.ov1] = e.ov2;
}
std::vector<std::vector<int>> loops;
while (!vertex_successors.empty()) {
loops.emplace_back();
auto it = vertex_successors.begin();
loops.back() = { it->first, it->second };
vertex_successors.erase(it);
int current = loops.back().back();
while (!vertex_successors.empty() && current != loops.back().front()) {
auto next = vertex_successors[current];
if (loops.back().front() != next) {
loops.back().push_back(next);
}
vertex_successors.erase(current);
current = next;
}
}
// Sort the loops by smallest x-coord of their constituent positions
// In order to put the outermost loop in front
if (loops.size() > 1) {
std::vector<std::pair<double, size_t>> min_xs;
for (auto& l : loops) {
double min_x = std::numeric_limits<double>::infinity();
for (auto& i : l) {
const auto& x = positions[i * 3];
if (x < min_x) {
min_x = x;
}
}
min_xs.push_back({ min_x, min_xs.size() });
}
std::sort(min_xs.begin(), min_xs.end());
decltype(loops) loops_copy;
for (auto& p : min_xs) {
loops_copy.emplace_back(std::move(loops[p.second]));
}
std::swap(loops, loops_copy);
}
return loops;
}
}
void ifcopenshell::geometry::OpenCascadeShape::Triangulate(ifcopenshell::geometry::Settings settings, const ifcopenshell::geometry::taxonomy::matrix4& place, IfcGeom::Representation::Triangulation* t, int item_id, int surface_style_id) const {
@@ -313,7 +203,7 @@ void ifcopenshell::geometry::OpenCascadeShape::Triangulate(ifcopenshell::geometr
}
if (polyhedral_output_without_holes || polyhedral_output_with_holes) {
auto loops = find_boundary_loops(t->verts(), triangle_indices);
auto loops = IfcGeom::util::find_boundary_loops(t->verts(), triangle_indices);
if (polyhedral_output_without_holes) {
if (!loops.empty() && !loops[0].empty()) {
t->addFace(item_id, surface_style_id, loops[0]);