FIX Creation of non volume place in mode during simplification
This commit is contained in:
parent
e6eae62584
commit
25feacfd95
@ -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)
|
||||||
|
@ -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());
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user