FIX Creation of non volume place in mode during simplification

This commit is contained in:
Filip Sykala 2021-09-01 19:15:16 +02:00
parent e6eae62584
commit 25feacfd95
2 changed files with 114 additions and 25 deletions

View File

@ -7,6 +7,8 @@
using namespace Slic3r; using namespace Slic3r;
#define NDEBUG
// only private namespace not neccessary be in .hpp // only private namespace not neccessary be in .hpp
namespace QuadricEdgeCollapse { namespace QuadricEdgeCollapse {
using Vertices = std::vector<stl_vertex>; using Vertices = std::vector<stl_vertex>;
@ -79,10 +81,13 @@ namespace QuadricEdgeCollapse {
init(const indexed_triangle_set &its, ThrowOnCancel& throw_on_cancel, StatusFn& status_fn); init(const indexed_triangle_set &its, ThrowOnCancel& throw_on_cancel, StatusFn& status_fn);
std::optional<uint32_t> find_triangle_index1(uint32_t vi, const VertexInfo& v_info, std::optional<uint32_t> find_triangle_index1(uint32_t vi, const VertexInfo& v_info,
uint32_t ti, const EdgeInfos& e_infos, const Indices& indices); uint32_t ti, const EdgeInfos& e_infos, const Indices& indices);
void reorder_edges(EdgeInfos &e_infos, const VertexInfo &v_info, uint32_t ti0, uint32_t ti1);
bool is_flipped(const Vec3f &new_vertex, uint32_t ti0, uint32_t ti1, const VertexInfo& v_info, bool is_flipped(const Vec3f &new_vertex, uint32_t ti0, uint32_t ti1, const VertexInfo& v_info,
const TriangleInfos &t_infos, const EdgeInfos &e_infos, const indexed_triangle_set &its); const TriangleInfos &t_infos, const EdgeInfos &e_infos, const indexed_triangle_set &its);
bool degenerate(uint32_t vi, uint32_t ti0, uint32_t ti1, const VertexInfo &v_info, bool degenerate(uint32_t vi, uint32_t ti0, uint32_t ti1, const VertexInfo &v_info,
const EdgeInfos &e_infos, const Indices &indices); const EdgeInfos &e_infos, const Indices &indices);
bool create_no_volume(uint32_t vi0, uint32_t vi1, uint32_t ti0, uint32_t ti1,
const VertexInfo &v_info0, const VertexInfo &v_info1, const EdgeInfos &e_infos, const Indices &indices);
// find edge with smallest error in triangle // find edge with smallest error in triangle
Vec3d calculate_3errors(const Triangle &t, const Vertices &vertices, const VertexInfos &v_infos); Vec3d calculate_3errors(const Triangle &t, const Vertices &vertices, const VertexInfos &v_infos);
Error calculate_error(uint32_t ti, const Triangle& t,const Vertices &vertices, const VertexInfos& v_infos, unsigned char& min_index); Error calculate_error(uint32_t ti, const Triangle& t,const Vertices &vertices, const VertexInfos& v_infos, unsigned char& min_index);
@ -99,6 +104,10 @@ namespace QuadricEdgeCollapse {
const VertexInfos &v_infos, const EdgeInfos &e_infos); const VertexInfos &v_infos, const EdgeInfos &e_infos);
#endif /* NDEBUG */ #endif /* NDEBUG */
// constants --> may be move to config
const int status_init_size = 10; // in percents
const uint32_t check_cancel_period = 16; // how many edge to reduce before call throw_on_cancel
const size_t max_triangle_count_for_one_vertex = 50;
} // namespace QuadricEdgeCollapse } // namespace QuadricEdgeCollapse
using namespace QuadricEdgeCollapse; using namespace QuadricEdgeCollapse;
@ -110,10 +119,6 @@ void Slic3r::its_quadric_edge_collapse(
std::function<void(void)> throw_on_cancel, std::function<void(void)> throw_on_cancel,
std::function<void(int)> status_fn) std::function<void(int)> status_fn)
{ {
// constants --> may be move to config
const int status_init_size = 10; // in percents
const int check_cancel_period = 16; // how many edge to reduce before call throw_on_cancel
// check input // check input
if (triangle_count >= its.indices.size()) return; if (triangle_count >= its.indices.size()) return;
float maximal_error = (max_error == nullptr)? std::numeric_limits<float>::max() : *max_error; float maximal_error = (max_error == nullptr)? std::numeric_limits<float>::max() : *max_error;
@ -122,7 +127,8 @@ void Slic3r::its_quadric_edge_collapse(
if (status_fn == nullptr) status_fn = [](int) {}; if (status_fn == nullptr) status_fn = [](int) {};
StatusFn init_status_fn = [&](int percent) { StatusFn init_status_fn = [&](int percent) {
status_fn(std::round((percent * status_init_size) / 100.)); float n_percent = percent * status_init_size / 100.f;
status_fn(static_cast<int>(std::round(n_percent)));
}; };
TriangleInfos t_infos; // only normals with information about deleted triangle TriangleInfos t_infos; // only normals with information about deleted triangle
@ -145,7 +151,6 @@ void Slic3r::its_quadric_edge_collapse(
mpq.reserve(its.indices.size()); mpq.reserve(its.indices.size());
for (Error &error :errors) mpq.push(error); for (Error &error :errors) mpq.push(error);
const size_t max_triangle_count_for_one_vertex = 50;
CopyEdgeInfos ceis; CopyEdgeInfos ceis;
ceis.reserve(max_triangle_count_for_one_vertex); ceis.reserve(max_triangle_count_for_one_vertex);
EdgeInfos e_infos_swap; EdgeInfos e_infos_swap;
@ -162,8 +167,9 @@ void Slic3r::its_quadric_edge_collapse(
(1. - reduced); (1. - reduced);
status_fn(static_cast<int>(std::round(status))); status_fn(static_cast<int>(std::round(status)));
}; };
// modulo for update status // modulo for update status, call each percent only once
uint32_t status_mod = std::max(uint32_t(16), count_triangle_to_reduce / 100); uint32_t status_mod = std::max(uint32_t(16),
count_triangle_to_reduce / (100 - status_init_size));
uint32_t iteration_number = 0; uint32_t iteration_number = 0;
float last_collapsed_error = 0.f; float last_collapsed_error = 0.f;
@ -195,14 +201,21 @@ void Slic3r::its_quadric_edge_collapse(
q += v_info1.q; q += v_info1.q;
Vec3f new_vertex0 = calculate_vertex(vi0, vi1, q, its.vertices); Vec3f new_vertex0 = calculate_vertex(vi0, vi1, q, its.vertices);
// set of triangle indices that change quadric // set of triangle indices that change quadric
uint32_t ti1 = -1; // triangle 1 index
auto ti1_opt = (v_info0.count < v_info1.count)? auto ti1_opt = (v_info0.count < v_info1.count)?
find_triangle_index1(vi1, v_info0, ti0, e_infos, its.indices) : find_triangle_index1(vi1, v_info0, ti0, e_infos, its.indices) :
find_triangle_index1(vi0, v_info1, ti0, e_infos, its.indices) ; find_triangle_index1(vi0, v_info1, ti0, e_infos, its.indices) ;
if (ti1_opt.has_value()) {
ti1 = *ti1_opt;
reorder_edges(e_infos, v_info0, ti0, ti1);
reorder_edges(e_infos, v_info1, ti0, ti1);
}
if (!ti1_opt.has_value() || // edge has only one triangle if (!ti1_opt.has_value() || // edge has only one triangle
degenerate(vi0, ti0, *ti1_opt, v_info1, e_infos, its.indices) || degenerate(vi0, ti0, ti1, v_info1, e_infos, its.indices) ||
degenerate(vi1, ti0, *ti1_opt, v_info0, e_infos, its.indices) || degenerate(vi1, ti0, ti1, v_info0, e_infos, its.indices) ||
is_flipped(new_vertex0, ti0, *ti1_opt, v_info0, t_infos, e_infos, its) || create_no_volume(vi0, vi1, ti0, ti1, v_info0, v_info1, e_infos, its.indices) ||
is_flipped(new_vertex0, ti0, *ti1_opt, v_info1, t_infos, e_infos, its)) { is_flipped(new_vertex0, ti0, ti1, v_info0, t_infos, e_infos, its) ||
is_flipped(new_vertex0, ti0, ti1, v_info1, t_infos, e_infos, its)) {
// try other triangle's edge // try other triangle's edge
Vec3d errors = calculate_3errors(t0, its.vertices, v_infos); Vec3d errors = calculate_3errors(t0, its.vertices, v_infos);
Vec3i ord = (errors[0] < errors[1]) ? Vec3i ord = (errors[0] < errors[1]) ?
@ -227,29 +240,25 @@ void Slic3r::its_quadric_edge_collapse(
mpq.push(e); mpq.push(e);
continue; continue;
} }
uint32_t ti1 = *ti1_opt;
last_collapsed_error = e.value; last_collapsed_error = e.value;
changed_triangle_indices.clear(); changed_triangle_indices.clear();
changed_triangle_indices.reserve(v_info0.count + v_info1.count - 4); changed_triangle_indices.reserve(v_info0.count + v_info1.count - 4);
// for each vertex0 triangles // for each vertex0 triangles
uint32_t v_info0_end = v_info0.start + v_info0.count; uint32_t v_info0_end = v_info0.start + v_info0.count - 2;
for (uint32_t di = v_info0.start; di < v_info0_end; ++di) { for (uint32_t di = v_info0.start; di < v_info0_end; ++di) {
assert(di < e_infos.size()); assert(di < e_infos.size());
uint32_t ti = e_infos[di].t_index; uint32_t ti = e_infos[di].t_index;
if (ti == ti0) continue; // ti0 will be deleted
if (ti == ti1) continue; // ti1 will be deleted
changed_triangle_indices.emplace_back(ti); changed_triangle_indices.emplace_back(ti);
} }
// for each vertex1 triangles // for each vertex1 triangles
uint32_t v_info1_end = v_info1.start + v_info1.count; uint32_t v_info1_end = v_info1.start + v_info1.count - 2;
for (uint32_t di = v_info1.start; di < v_info1_end; ++di) { for (uint32_t di = v_info1.start; di < v_info1_end; ++di) {
assert(di < e_infos.size()); assert(di < e_infos.size());
EdgeInfo &e_info = e_infos[di]; EdgeInfo &e_info = e_infos[di];
uint32_t ti = e_info.t_index; uint32_t ti = e_info.t_index;
if (ti == ti0) continue; // ti0 will be deleted
if (ti == ti1) continue; // ti1 will be deleted
Triangle &t = its.indices[ti]; Triangle &t = its.indices[ti];
t[e_info.edge] = vi0; // change index t[e_info.edge] = vi0; // change index
changed_triangle_indices.emplace_back(ti); changed_triangle_indices.emplace_back(ti);
@ -282,7 +291,9 @@ void Slic3r::its_quadric_edge_collapse(
t_info1.set_deleted(); t_info1.set_deleted();
// triangle counter decrementation // triangle counter decrementation
actual_triangle_count-=2; actual_triangle_count-=2;
#ifndef NDEBUG
assert(check_neighbors(its, t_infos, v_infos, e_infos)); assert(check_neighbors(its, t_infos, v_infos, e_infos));
#endif // NDEBUG
} }
// compact triangle // compact triangle
@ -506,6 +517,38 @@ std::optional<uint32_t> QuadricEdgeCollapse::find_triangle_index1(uint32_t
return {}; return {};
} }
void QuadricEdgeCollapse::reorder_edges(EdgeInfos & e_infos,
const VertexInfo &v_info,
uint32_t ti0,
uint32_t ti1)
{
// swap edge info of ti0 and ti1 to end(last one and one before)
size_t v_info_end = v_info.start + v_info.count - 2;
EdgeInfo &e_info_ti0 = e_infos[v_info_end];
EdgeInfo &e_info_ti1 = e_infos[v_info_end+1];
bool is_swaped = false;
for (size_t ei = v_info.start; ei < v_info_end; ++ei) {
EdgeInfo &e_info = e_infos[ei];
if (e_info.t_index == ti0) {
std::swap(e_info, e_info_ti0);
if (is_swaped) return;
if (e_info.t_index == ti1) {
std::swap(e_info, e_info_ti1);
return;
}
is_swaped = true;
} else if (e_info.t_index == ti1) {
std::swap(e_info, e_info_ti1);
if (is_swaped) return;
if (e_info.t_index == ti0) {
std::swap(e_info, e_info_ti0);
return;
}
is_swaped = true;
}
}
}
bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex, bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex,
uint32_t ti0, uint32_t ti0,
uint32_t ti1, uint32_t ti1,
@ -519,12 +562,10 @@ bool QuadricEdgeCollapse::is_flipped(const Vec3f & new_vertex,
static const float dot_thr = 0.2f; // Value from simplify mesh cca 80 DEG static const float dot_thr = 0.2f; // Value from simplify mesh cca 80 DEG
// for each vertex triangles // for each vertex triangles
size_t v_info_end = v_info.start + v_info.count; size_t v_info_end = v_info.start + v_info.count-2;
for (size_t ei = v_info.start; ei < v_info_end; ++ei) { for (size_t ei = v_info.start; ei < v_info_end; ++ei) {
assert(ei < e_infos.size()); assert(ei < e_infos.size());
const EdgeInfo &e_info = e_infos[ei]; const EdgeInfo &e_info = e_infos[ei];
if (e_info.t_index == ti0) continue; // ti0 will be deleted
if (e_info.t_index == ti1) continue; // ti1 will be deleted
const Triangle &t = its.indices[e_info.t_index]; const Triangle &t = its.indices[e_info.t_index];
const Vec3f &normal = t_infos[e_info.t_index].n; const Vec3f &normal = t_infos[e_info.t_index].n;
const Vec3f &vf = its.vertices[t[(e_info.edge + 1) % 3]]; const Vec3f &vf = its.vertices[t[(e_info.edge + 1) % 3]];
@ -554,12 +595,10 @@ bool QuadricEdgeCollapse::degenerate(uint32_t vi,
{ {
// check surround triangle do not contain vertex index // check surround triangle do not contain vertex index
// protect from creation of triangle with two same vertices inside // protect from creation of triangle with two same vertices inside
size_t v_info_end = v_info.start + v_info.count; size_t v_info_end = v_info.start + v_info.count - 2;
for (size_t ei = v_info.start; ei < v_info_end; ++ei) { for (size_t ei = v_info.start; ei < v_info_end; ++ei) {
assert(ei < e_infos.size()); assert(ei < e_infos.size());
const EdgeInfo &e_info = e_infos[ei]; const EdgeInfo &e_info = e_infos[ei];
if (e_info.t_index == ti0) continue; // ti0 will be deleted
if (e_info.t_index == ti1) continue; // ti1 will be deleted
const Triangle &t = indices[e_info.t_index]; const Triangle &t = indices[e_info.t_index];
for (size_t i = 0; i < 3; ++i) for (size_t i = 0; i < 3; ++i)
if (static_cast<uint32_t>(t[i]) == vi) return true; if (static_cast<uint32_t>(t[i]) == vi) return true;
@ -567,6 +606,47 @@ bool QuadricEdgeCollapse::degenerate(uint32_t vi,
return false; return false;
} }
bool QuadricEdgeCollapse::create_no_volume(
uint32_t vi0 , uint32_t vi1,
uint32_t ti0 , uint32_t ti1,
const VertexInfo &v_info0, const VertexInfo &v_info1,
const EdgeInfos & e_infos, const Indices &indices)
{
// check that triangles around vertex0 doesn't have half edge
// with opposit order in set of triangles around vertex1
// protect from creation of two triangles with oposit order - no volume space
size_t v_info0_end = v_info0.start + v_info0.count - 2;
size_t v_info1_end = v_info1.start + v_info1.count - 2;
for (size_t ei0 = v_info0.start; ei0 < v_info0_end; ++ei0) {
const EdgeInfo &e_info0 = e_infos[ei0];
const Triangle &t0 = indices[e_info0.t_index];
// edge CCW vertex indices are t0vi0, t0vi1
size_t t0i = 0;
if (t0[t0i] == vi0) ++t0i;
uint32_t t0vi0 = t0[t0i];
++t0i;
if (t0[t0i] == vi0) ++t0i;
uint32_t t0vi1 = t0[t0i];
for (size_t ei1 = v_info1.start; ei1 < v_info1_end; ++ei1) {
const EdgeInfo &e_info1 = e_infos[ei1];
const Triangle &t1 = indices[e_info1.t_index];
size_t t1i = 0;
for (; t1i < 3; ++t1i) if (t1[t1i] == t0vi1) break;
if (t1i >= 3) continue; // without vertex index from triangle 0
// check if second index is same too
++t1i;
if (t1i == 3) t1i = 0; // triangle loop(modulo 3)
if (t1[t1i] == vi1) {
++t1i;
if (t1i == 3) t1i = 0; // triangle loop(modulo 3)
}
if (t1[t1i] == t0vi0) return true;
}
}
return false;
}
Vec3d QuadricEdgeCollapse::calculate_3errors(const Triangle & t, Vec3d QuadricEdgeCollapse::calculate_3errors(const Triangle & t,
const Vertices & vertices, const Vertices & vertices,
const VertexInfos &v_infos) const VertexInfos &v_infos)

View File

@ -288,3 +288,12 @@ TEST_CASE("Simplify trouble case", "[its]")
its_quadric_edge_collapse(tm.its, wanted_count, &max_error); its_quadric_edge_collapse(tm.its, wanted_count, &max_error);
CHECK(tm.its.indices.size() <= 8); CHECK(tm.its.indices.size() <= 8);
} }
TEST_CASE("Simplified cube should not be empty.", "[its]")
{
auto its = its_make_cube(1, 2, 3);
float max_error = std::numeric_limits<float>::max();
uint32_t wanted_count = 0;
its_quadric_edge_collapse(its, wanted_count, &max_error);
CHECK(!its.indices.empty());
}