FIX:fix bugs of non manifold edge

Jira: STUDIO-5267

Change-Id: I8ac9a2cf96da0bc07ee00b309e65611b92fd174d
This commit is contained in:
zhou.xu 2023-11-20 10:34:27 +08:00 committed by Lane.Wei
parent 7f43532615
commit 099756fc1a
2 changed files with 172 additions and 70 deletions

View File

@ -447,6 +447,11 @@ const ModelObjectPtrs &Cut::perform_by_contour(std::vector<Part> parts, int dowe
if (m_attributes.has(ModelObjectCutAttribute::KeepLower))
cut_mo->clone_for_cut(&lower);
if (upper && lower) {
upper->name = upper->name + "_A";
lower->name = lower->name + "_B";
}
const size_t cut_parts_cnt = parts.size();
bool has_modifiers = false;

View File

@ -47,9 +47,12 @@ bool is_equal(float lh, float rh)
return abs(lh - rh) <= epson;
}
bool is_less(float lh, float rh)
{
return lh + epson < rh;
bool is_equal_for_sort(float lh, float rh) {
return abs(lh - rh) <= 1e-8;
}
bool is_equal(const Vec3f &lh, const Vec3f &rh) {
return is_equal(lh[0], rh[0]) && is_equal(lh[1], rh[1]) && is_equal(lh[2], rh[2]);
}
class IntersectionReference
@ -170,7 +173,7 @@ static FacetSliceType slice_facet(
// (external on the right of the line)
for (int j = 0; j < 3; ++ j) { // loop through facet edges
int edge_id;
const stl_vertex *a, *b;
const stl_vertex *a, *b, *c;
int a_id, b_id;
{
int k = (idx_vertex_lowest + j) % 3;
@ -180,10 +183,11 @@ static FacetSliceType slice_facet(
a = vertices + k;
b_id = indices[l];
b = vertices + l;
c = vertices + (k + 2) % 3;
}
// Is edge or face aligned with the cutting plane?
if (a->z() == slice_z && b->z() == slice_z) {
if (is_equal(a->z(), slice_z) && is_equal(b->z() ,slice_z)) {
// Edge is horizontal and belongs to the current layer.
// The following rotation of the three vertices may not be efficient, but this branch happens rarely.
const stl_vertex &v0 = vertices[0];
@ -204,7 +208,7 @@ static FacetSliceType slice_facet(
} else {
// Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane.
// Is the third vertex below the cutting plane?
bool third_below = v0.z() < slice_z || v1.z() < slice_z || v2.z() < slice_z;
bool third_below = c->z() < slice_z;
// Two vertices on the cutting plane, the third vertex is below the plane. Consider the edge to be part of the slice
// only if it is the upper edge.
// (the bottom most edge resp. vertex of a triangle is not owned by the triangle, but the top most edge resp. vertex is part of the triangle
@ -227,7 +231,7 @@ static FacetSliceType slice_facet(
return result;
}
if (a->z() == slice_z) {
if (is_equal(a->z() ,slice_z)) {
// Only point a alings with the cutting plane.
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
point_on_layer = num_points;
@ -236,7 +240,7 @@ static FacetSliceType slice_facet(
point.y() = a->y();
point.point_id = a_id;
}
} else if (b->z() == slice_z) {
} else if (is_equal(b->z() , slice_z)) {
// Only point b alings with the cutting plane.
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
point_on_layer = num_points;
@ -336,7 +340,7 @@ void slice_facet_at_zs(
// find layer extents
auto min_layer = std::lower_bound(zs.begin(), zs.end(), min_z); // first layer whose slice_z is >= min_z
auto max_layer = std::upper_bound(min_layer, zs.end(), max_z); // first layer whose slice_z is > max_z
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
int idx_vertex_lowest = is_equal(vertices[1].z(), min_z) ? 1 : (is_equal(vertices[2].z() , min_z) ? 2 : 0);
for (auto it = min_layer; it != max_layer; ++ it) {
IntersectionLine il;
@ -392,7 +396,7 @@ static inline IntersectionLines slice_make_lines(
const float min_z = fminf(vertices[0].z(), fminf(vertices[1].z(), vertices[2].z()));
const float max_z = fmaxf(vertices[0].z(), fmaxf(vertices[1].z(), vertices[2].z()));
assert(min_z <= plane_z && max_z >= plane_z);
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
int idx_vertex_lowest = is_equal(vertices[1].z(), min_z) ? 1 : (is_equal(vertices[2].z(), min_z) ? 2 : 0);
IntersectionLine il;
// Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume.
if (!is_equal(min_z, max_z) && slice_facet(plane_z, vertices, indices, face_edge_ids[face_idx], idx_vertex_lowest, false, il) == FacetSliceType::Slicing) {
@ -447,8 +451,8 @@ void slice_facet_with_slabs(
// find facet extents
const float min_z = fminf(vertices[0].z(), fminf(vertices[1].z(), vertices[2].z()));
const float max_z = fmaxf(vertices[0].z(), fmaxf(vertices[1].z(), vertices[2].z()));
const bool horizontal = min_z == max_z;
const bool horizontal = is_equal(min_z , max_z);
// find layer extents
auto min_layer = std::lower_bound(zs.begin(), zs.end(), min_z); // first layer whose slice_z is >= min_z
auto max_layer = std::upper_bound(min_layer, zs.end(), max_z); // first layer whose slice_z is > max_z
@ -537,7 +541,7 @@ void slice_facet_with_slabs(
}
} else {
// The triangle is not horizontal and at least a single slicing plane intersects the triangle.
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
int idx_vertex_lowest = is_equal(vertices[1].z(), min_z) ? 1 : (is_equal(vertices[2].z() , min_z) ? 2 : 0);
IntersectionLine il_prev;
for (auto it = min_layer; it != max_layer; ++ it) {
IntersectionLine il;
@ -1997,15 +2001,16 @@ void slice_mesh_slabs(
// Remove duplicates of slice_vertices, optionally triangulate the cut.
static void triangulate_slice(
indexed_triangle_set &its,
IntersectionLines &lines,
indexed_triangle_set &its,
IntersectionLines &lines,
std::vector<int> &slice_vertices,
// Vertices of the original (unsliced) mesh. Newly added vertices are those on the slice.
int num_original_vertices,
// Z height of the slice.
float z,
bool triangulate,
bool normals_down)
float z,
bool triangulate,
bool normals_down,
const std::map<int, Vec3f*> &section_vertices_map)
{
sort_remove_duplicates(slice_vertices);
@ -2015,11 +2020,12 @@ static void triangulate_slice(
map_vertex_to_index.reserve(slice_vertices.size());
for (int i : slice_vertices)
map_vertex_to_index.emplace_back(to_2d(its.vertices[i]), i);
std::sort(map_vertex_to_index.begin(), map_vertex_to_index.end(),
[](const std::pair<Vec2f, int> &l, const std::pair<Vec2f, int> &r) {
return is_less(l.first.x(), r.first.x()) ||
(is_equal(l.first.x(), r.first.x()) && (is_less(l.first.y(), r.first.y()) ||
(is_equal(l.first.y(), r.first.y()) && l.second < r.second))); });
std::sort(map_vertex_to_index.begin(), map_vertex_to_index.end(),
[](const std::pair<Vec2f, int> &l, const std::pair<Vec2f, int> &r) {
return l.first.x() < r.first.x() ||
(is_equal_for_sort(l.first.x(), r.first.x()) && (l.first.y()< r.first.y() ||
(is_equal_for_sort(l.first.y(), r.first.y()) && l.second < r.second)));
});
// 2) Discover duplicate points on the slice. Remap duplicate vertices to a vertex with a lowest index.
// Remove denegerate triangles, if they happen to be created by merging duplicate vertices.
@ -2068,14 +2074,40 @@ static void triangulate_slice(
stl_triangle_vertex_indices facet;
for (size_t j = 0; j < 3; ++ j) {
Vec3f v = triangles[i ++].cast<float>();
auto it = lower_bound_by_predicate(map_vertex_to_index.begin(), map_vertex_to_index.end(),
auto it = lower_bound_by_predicate(map_vertex_to_index.begin(), map_vertex_to_index.end(),
[&v](const std::pair<Vec2f, int> &l) {
return is_less(l.first.x(), v.x()) || (is_equal(l.first.x(), v.x()) && is_less(l.first.y(), v.y()));
return l.first.x() < v.x() || (is_equal_for_sort(l.first.x(), v.x()) && l.first.y() < v.y());
});
int idx = -1;
if (it != map_vertex_to_index.end() && is_equal(it->first.x(), v.x()) && is_equal(it->first.y(), v.y()))
idx = it->second;
else {
bool exist = false;
for (auto i = section_vertices_map.begin(); i != section_vertices_map.end(); i++) {
if (is_equal(v, *i->second)) {
idx = i->first;
exist = true;
break;
}
}
// go on finding
if (!exist) {
for (; it != map_vertex_to_index.end(); it++) {
if (is_equal(it->first.x(), v.x()) && is_equal(it->first.y(), v.y())) {
idx = it->second;
exist = true;
break;
}
}
}
// go on finding
if (!exist) {
for (; it != map_vertex_to_index.begin(); it--) {
if (is_equal(it->first.x(), v.x()) && is_equal(it->first.y(), v.y())) {
idx = it->second;
exist = true;
break;
}
}
}
if (!exist){
// Try to find the vertex in the list of newly added vertices. Those vertices are not matched on the cut and they shall be rare.
for (size_t k = idx_vertex_new_first; k < its.vertices.size(); ++ k)
if (its.vertices[k] == v) {
@ -2156,16 +2188,22 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u
IntersectionLines upper_lines, lower_lines;
std::vector<int> upper_slice_vertices, lower_slice_vertices;
std::vector<Vec3i> facets_edge_ids = its_face_edge_ids(mesh);
std::map<int, Vec3f *> section_vertices_map;
for (int facet_idx = 0; facet_idx < int(mesh.indices.size()); ++ facet_idx) {
const stl_triangle_vertex_indices &facet = mesh.indices[facet_idx];
Vec3f vertices[3] { mesh.vertices[facet(0)], mesh.vertices[facet(1)], mesh.vertices[facet(2)] };
float min_z = std::min(vertices[0].z(), std::min(vertices[1].z(), vertices[2].z()));
float max_z = std::max(vertices[0].z(), std::max(vertices[1].z(), vertices[2].z()));
for (size_t i = 0; i < 3; i++) {
if (is_equal(z, vertices[i].z()) && section_vertices_map[facet(i)] == nullptr) {
section_vertices_map[facet(i)] = new Vec3f(vertices[i].x(), vertices[i].y(), vertices[i].z());
}
}
// intersect facet with cutting plane
IntersectionLine line;
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
int idx_vertex_lowest = is_equal(vertices[1].z(), min_z) ? 1 : (is_equal(vertices[2].z() , min_z) ? 2 : 0);
FacetSliceType slice_type = FacetSliceType::NoSlice;
if (z > min_z - EPSILON && z < max_z + EPSILON) {
Vec3f vertices_scaled[3];
@ -2176,7 +2214,7 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u
dst.y() = scale_(src.y());
dst.z() = src.z();
}
slice_type = slice_facet(z, vertices_scaled, mesh.indices[facet_idx], facets_edge_ids[facet_idx], idx_vertex_lowest, min_z == max_z, line);
slice_type = slice_facet(z, vertices_scaled, mesh.indices[facet_idx], facets_edge_ids[facet_idx], idx_vertex_lowest, is_equal(min_z , max_z), line);
}
if (slice_type != FacetSliceType::NoSlice) {
@ -2194,12 +2232,12 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u
upper_lines.emplace_back(line);
}
}
if (min_z > z || (min_z == z && max_z > z)) {
if (min_z > z || (is_equal(min_z , z) && max_z > z)) {
// facet is above the cut plane and does not belong to it
if (upper != nullptr)
upper->indices.emplace_back(facet);
} else if (max_z < z || (max_z == z && min_z < z)) {
} else if (max_z < z || (is_equal(max_z, z) && min_z < z)) {
// facet is below the cut plane and does not belong to it
if (lower != nullptr)
lower->indices.emplace_back(facet);
@ -2211,21 +2249,56 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u
assert(line.edge_b_id != -1);
// look for the vertex on whose side of the slicing plane there are no other vertices
int isolated_vertex =
(vertices[0].z() > z) == (vertices[1].z() > z) ? 2 :
(vertices[1].z() > z) == (vertices[2].z() > z) ? 0 : 1;
// get vertices starting from the isolated one
int iv = isolated_vertex;
stl_vertex v0v1, v2v0;
assert(facets_edge_ids[facet_idx](iv) == line.edge_a_id || facets_edge_ids[facet_idx](iv) == line.edge_b_id);
if (facets_edge_ids[facet_idx](iv) == line.edge_a_id) {
// Unscale to doubles first, then to floats to reach the same accuracy as triangulate_expolygons_2d().
v0v1 = to_3d(unscaled<double>(line.a).cast<float>().eval(), z);
v2v0 = to_3d(unscaled<double>(line.b).cast<float>().eval(), z);
int isolated_vertex, isolated_vertex_option = -1;
std::vector<int> list{0, 1, 2};
auto get_third = [&list](int isolated_vertex, int temp) {// not use vertex data
for (size_t i = 0; i < list.size(); i++) {
if (list[i] != isolated_vertex && list[i] != temp) {
return list[i];
}
}
return -1;
};
if (is_equal(vertices[0].z(), z)) {
isolated_vertex = vertices[1].z() > z ? 1 : 2;
isolated_vertex_option = get_third(isolated_vertex, 0);
} else if (is_equal(vertices[1].z(), z)) {
isolated_vertex = vertices[2].z() > z ? 2 : 0;
isolated_vertex_option = get_third(isolated_vertex, 1);
} else if (is_equal(vertices[2].z(), z)) {
isolated_vertex = vertices[0].z() > z ? 0 : 1;
isolated_vertex_option = get_third(isolated_vertex, 2);
} else {
v0v1 = to_3d(unscaled<double>(line.b).cast<float>().eval(), z);
v2v0 = to_3d(unscaled<double>(line.a).cast<float>().eval(), z);
isolated_vertex = (vertices[0].z() > z) == (vertices[1].z() > z) ? 2 : (vertices[1].z() > z) == (vertices[2].z() > z) ? 0 : 1;
}
// get vertices starting from the isolated one
stl_vertex v0v1, v2v0;
auto calc_isolated_vertex = [&v0v1, &v2v0, &line, &facet_idx, &facets_edge_ids, &z](int iv, bool &is_find) {
assert(facets_edge_ids[facet_idx](iv) == line.edge_a_id || facets_edge_ids[facet_idx](iv) == line.edge_b_id);
is_find = true;
if (facets_edge_ids[facet_idx](iv) == line.edge_a_id) {
// Unscale to doubles first, then to floats to reach the same accuracy as triangulate_expolygons_2d().
v0v1 = to_3d(unscaled<double>(line.a).cast<float>().eval(), z);
v2v0 = to_3d(unscaled<double>(line.b).cast<float>().eval(), z);
} else if (facets_edge_ids[facet_idx](iv) == line.edge_b_id) {
v0v1 = to_3d(unscaled<double>(line.b).cast<float>().eval(), z);
v2v0 = to_3d(unscaled<double>(line.a).cast<float>().eval(), z);
} else {
is_find = false;
}
};
bool find_isolated_vertex;
int iv;
calc_isolated_vertex(isolated_vertex, find_isolated_vertex);
if (!find_isolated_vertex && isolated_vertex_option != -1) {
calc_isolated_vertex(isolated_vertex_option, find_isolated_vertex);
if (!find_isolated_vertex) {
BOOST_LOG_TRIVIAL(trace) << "cut_mesh:error:could not find isolated_vertex";
continue;
}
iv = isolated_vertex_option;
} else {
iv = isolated_vertex;
}
const stl_vertex &v0 = vertices[iv];
const int iv0 = facet[iv];
@ -2239,48 +2312,71 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u
const int iv2 = facet[iv];
// intersect v0-v1 and v2-v0 with cutting plane and make new vertices
auto new_vertex = [upper, lower, &upper_slice_vertices, &lower_slice_vertices](const Vec3f &a, const int ia, const Vec3f &b, const int ib, const Vec3f &c) {
auto new_vertex = [upper, lower, &upper_slice_vertices, &lower_slice_vertices](const Vec3f &a, const int ia, const Vec3f &b, const int ib, const Vec3f &c,
const int ic, const Vec3f &new_pt, bool &is_new_vertex) {
int iupper, ilower;
if (c == a)
is_new_vertex = false;
if (is_equal(new_pt, a))
iupper = ilower = ia;
else if (c == b)
else if (is_equal(new_pt, b))
iupper = ilower = ib;
else if (is_equal(new_pt, c))
iupper = ilower = ic;
else {
// Insert a new vertex into upper / lower.
is_new_vertex = true;
if (upper) {
iupper = int(upper->vertices.size());
upper->vertices.emplace_back(c);
upper->vertices.emplace_back(new_pt);
upper_slice_vertices.emplace_back(iupper);
}
if (lower) {
ilower = int(lower->vertices.size());
lower->vertices.emplace_back(c);
lower->vertices.emplace_back(new_pt);
lower_slice_vertices.emplace_back(ilower);
}
}
return std::make_pair(iupper, ilower);
};
auto [iv0v1_upper, iv0v1_lower] = new_vertex(v1, iv1, v0, iv0, v0v1);
auto [iv2v0_upper, iv2v0_lower] = new_vertex(v2, iv2, v0, iv0, v2v0);
auto new_face = [](indexed_triangle_set *its, int i, int j, int k) {
if (its != nullptr && i != j && i != k && j != k)
its->indices.emplace_back(i, j, k);
bool is_new_vertex_v0v1;
bool is_new_vertex_v2v0;
auto [iv0v1_upper, iv0v1_lower] = new_vertex(v1, iv1, v0, iv0, v2, iv2, v0v1, is_new_vertex_v0v1);
auto [iv2v0_upper, iv2v0_lower] = new_vertex(v2, iv2, v0, iv0, v1, iv1, v2v0, is_new_vertex_v2v0);
auto new_face = [](indexed_triangle_set *its, int i, int j, int k) {
if (its != nullptr && i != j && i != k && j != k) its->indices.emplace_back(i, j, k);
};
if (v0.z() > z) {
new_face(upper, iv0, iv0v1_upper, iv2v0_upper);
new_face(lower, iv1, iv2, iv0v1_lower);
new_face(lower, iv2, iv2v0_lower, iv0v1_lower);
} else {
new_face(upper, iv1, iv2, iv0v1_upper);
new_face(upper, iv2, iv2v0_upper, iv0v1_upper);
new_face(lower, iv0, iv0v1_lower, iv2v0_lower);
if (is_new_vertex_v0v1 && is_new_vertex_v2v0) {
if (v0.z() > z) {
new_face(upper, iv0, iv0v1_upper, iv2v0_upper);
new_face(lower, iv1, iv2, iv0v1_lower);
new_face(lower, iv2, iv2v0_lower, iv0v1_lower);
} else {
new_face(upper, iv1, iv2, iv0v1_upper);
new_face(upper, iv2, iv2v0_upper, iv0v1_upper);
new_face(lower, iv0, iv0v1_lower, iv2v0_lower);
}
} else if (is_new_vertex_v0v1) {
if (v0.z() > z) {
new_face(upper, iv0, iv0v1_upper, iv2);
new_face(lower, iv1, iv2, iv0v1_lower);
} else {
new_face(lower, iv0, iv0v1_lower, iv2);
new_face(upper, iv1, iv2, iv0v1_upper);
}
} else if (is_new_vertex_v2v0) {
if (v0.z() > z) {
new_face(upper, iv0, iv1, iv2v0_upper);
new_face(lower, iv1, iv2, iv2v0_lower);
} else {
new_face(lower, iv0, iv1, iv2v0_lower);
new_face(upper, iv1, iv2, iv2v0_upper);
}
}
}
}
if (upper != nullptr) {
triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_DOWN);
triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_DOWN, section_vertices_map);
#ifndef NDEBUG
if (triangulate_caps) {
size_t num_open_edges_new = its_num_open_edges(*upper);
@ -2290,7 +2386,7 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u
}
if (lower != nullptr) {
triangulate_slice(*lower, lower_lines, lower_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_UP);
triangulate_slice(*lower, lower_lines, lower_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_UP, section_vertices_map);
#ifndef NDEBUG
if (triangulate_caps) {
size_t num_open_edges_new = its_num_open_edges(*lower);
@ -2298,6 +2394,7 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u
}
#endif // NDEBUG
}
std::map<int, Vec3f*>().swap(section_vertices_map);
}
// BBS: implement plane cut with cgal