Improved accuracy of slicing (triangle cutting) code,

improved debugging outputs and asserts of the slicing code.
Disabled detection of concave corners with horizontal faces,
as too often there were found models with badly triangulated faces,
see for example GH issue #895.
This commit is contained in:
bubnikv 2018-08-09 21:15:49 +02:00
parent 00e9f07a03
commit 0ea4557632
4 changed files with 78 additions and 35 deletions

View file

@ -15,16 +15,20 @@
#include <tbb/parallel_for.h>
// for SLIC3R_DEBUG_SLICE_PROCESSING
#include "libslic3r.h"
#if 0
#define DEBUG
#define _DEBUG
#undef NDEBUG
#define SLIC3R_DEBUG
// #define SLIC3R_TRIANGLEMESH_DEBUG
#endif
#include <assert.h>
#ifdef SLIC3R_DEBUG
// #define SLIC3R_TRIANGLEMESH_DEBUG
#if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING)
#include "SVG.hpp"
#endif
@ -758,8 +762,22 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons
{
static int iRun = 0;
for (size_t i = 0; i < z.size(); ++ i) {
Polygons &polygons = (*layers)[i];
SVG::export_expolygons(debug_out_path("slice_%d_%d.svg", iRun, i).c_str(), union_ex(polygons, true));
Polygons &polygons = (*layers)[i];
ExPolygons expolygons = union_ex(polygons, true);
SVG::export_expolygons(debug_out_path("slice_%d_%d.svg", iRun, i).c_str(), expolygons);
{
BoundingBox bbox;
for (const IntersectionLine &l : lines[i]) {
bbox.merge(l.a);
bbox.merge(l.b);
}
SVG svg(debug_out_path("slice_loops_%d_%d.svg", iRun, i).c_str(), bbox);
svg.draw(expolygons);
for (const IntersectionLine &l : lines[i])
svg.draw(l, "red", 0);
svg.draw_outline(expolygons, "black", "blue", 0);
svg.Close();
}
for (Polygon &poly : polygons) {
for (size_t i = 1; i < poly.points.size(); ++ i)
assert(poly.points[i-1] != poly.points[i]);
@ -821,6 +839,7 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
il.b.y = b->y;
il.a_id = a_id;
il.b_id = b_id;
assert(il.a != il.b);
(*lines)[layer_idx].emplace_back(il);
}
} else
@ -849,16 +868,6 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygo
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::make_expolygons in parallel - end";
}
static inline float cross_product(const stl_vertex *a, const stl_vertex *b, const stl_vertex *c)
{
float v1_x = b->x - a->x;
float v1_y = b->y - a->y;
float v2_x = c->x - a->x;
float v2_y = c->y - a->y;
float dir = (b->x - a->x) * (c->y - a->y) - (b->y - a->y) * (c->x - a->x);
return dir;
}
// Return true, if the facet has been sliced and line_out has been filled.
TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
float slice_z, const stl_facet &facet, const int facet_idx,
@ -873,10 +882,10 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
// Reorder vertices so that the first one is the one with lowest Z.
// This is needed to get all intersection lines in a consistent order
// (external on the right of the line)
int i = (facet.vertex[1].z == min_z) ? 1 : ((facet.vertex[2].z == min_z) ? 2 : 0);
for (int j = i; j - i < 3; ++ j) { // loop through facet edges
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
int i = (facet.vertex[1].z == min_z) ? 1 : ((facet.vertex[2].z == min_z) ? 2 : 0);
for (int j = i; j - i < 3; ++j) { // loop through facet edges
int edge_id = this->facets_edges[facet_idx * 3 + (j % 3)];
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
int a_id = vertices[j % 3];
int b_id = vertices[(j+1) % 3];
const stl_vertex *a = &this->v_scaled_shared[a_id];
@ -888,6 +897,7 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
const stl_vertex &v0 = this->v_scaled_shared[vertices[0]];
const stl_vertex &v1 = this->v_scaled_shared[vertices[1]];
const stl_vertex &v2 = this->v_scaled_shared[vertices[2]];
const stl_normal &normal = this->mesh->stl.facet_start[facet_idx].normal;
// We may ignore this edge for slicing purposes, but we may still use it for object cutting.
FacetSliceType result = Slicing;
const stl_neighbors &nbr = this->mesh->stl.neighbors_start[facet_idx];
@ -897,23 +907,23 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
// Mark neighbor edges, which do not have a neighbor.
uint32_t edges = 0;
uint32_t mask = IntersectionLine::EDGE0;
for (int nbr_idx = 2; nbr_idx != 5; ++ nbr_idx, mask <<= 1)
for (int nbr_idx = 0; nbr_idx != 3; ++ nbr_idx, mask <<= 1)
// If the neighbor with an edge starting with a vertex idx (nbr_idx - 2) shares no
// opposite face, add it to the edges to process when slicing.
if (nbr.neighbor[nbr_idx % 3] == -1)
if (nbr.neighbor[nbr_idx] == -1)
// Mark this edge.
edges |= mask;
// Use some edges of this triangle for slicing only if at least one of its edge does not have an opposite face.
result = (edges == 0) ? Cutting : Slicing;
line_out->flags |= edges;
if (this->mesh->stl.facet_start[facet_idx].normal.z < 0) {
if (normal.z < 0) {
// If normal points downwards this is a bottom horizontal facet so we reverse its point order.
std::swap(a, b);
std::swap(a_id, b_id);
}
} else {
// Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane.
int nbr_idx = (j + 2) % 3;
int nbr_idx = j % 3;
int nbr_face = nbr.neighbor[nbr_idx];
// Is the third vertex below the cutting plane?
bool third_below = v0.z < slice_z || v1.z < slice_z || v2.z < slice_z;
@ -923,19 +933,25 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
printf("Face has no neighbor!\n");
#endif
} else {
assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == b_id);
assert(this->mesh->stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id);
int idx_vertex_opposite = this->mesh->stl.v_indices[nbr_face].vertex[nbr.which_vertex_not[nbr_idx]];
const stl_vertex *c = &this->v_scaled_shared[idx_vertex_opposite];
if (c->z > slice_z) {
// If an edge resides on a cutting plane, and none of the two triangles are coplanar with the cutting plane,
// igore the lower triangle.
if (third_below)
result = Cutting;
} else if (c->z == slice_z) {
// A vertical face shares edge with a horizontal face. Verify, whether the shared corner is convex or concave.
float dir = cross_product(a, b, c);
if (third_below ? (dir < 0.) : (dir > 0.))
result = Cutting;
}
// double side = double(normal.x) * (double(c->x) - double(a->x)) + double(normal.y) * (double(c->y) - double(a->y));
// assert(c->z != slice_z || side != 0.);
// double normal_nbr = (double(c->x) - double(a->x)) * (double(b->y) - double(a->y)) - (double(c->y) - double(a->y)) * (double(b->x) - double(a->x));
result =
(c->z == slice_z) ?
// A vertical face shares edge with a horizontal face. Verify, whether the shared edge makes a convex or concave corner.
// Unfortunately too often there are flipped normals, which brake our assumption. Let's rather return every edge,
// and leth the code downstream hopefully handle it.
Slicing :
// Failing tests: Ignore concave corners for slicing.
// (((normal_nbr < 0) == third_below) ? Cutting : Slicing) :
// or
// (((this->mesh->stl.facet_start[nbr_face].normal.z < 0) == third_below) ? Cutting : Slicing) :
// For a pair of faces touching exactly at the cutting plane, ignore the face with a higher index.
(facet_idx < nbr_face) ? Slicing : Cutting;
}
if (third_below) {
line_out->edge_type = feTop;
@ -950,6 +966,7 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
line_out->b.y = b->y;
line_out->a_id = a_id;
line_out->b_id = b_id;
assert(line_out->a != line_out->b);
return result;
}
@ -970,8 +987,9 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
} else if ((a->z < slice_z && b->z > slice_z) || (b->z < slice_z && a->z > slice_z)) {
// A general case. The face edge intersects the cutting plane. Calculate the intersection point.
IntersectionPoint &point = points[num_points ++];
point.x = b->x + (a->x - b->x) * (slice_z - b->z) / (a->z - b->z);
point.y = b->y + (a->y - b->y) * (slice_z - b->z) / (a->z - b->z);
double t = (double(slice_z) - double(b->z)) / (double(a->z) - double(b->z));
point.x = float(double(b->x) + (double(a->x) - double(b->x)) * t);
point.y = float(double(b->y) + (double(a->y) - double(b->y)) * t);
point.edge_id = edge_id;
}
}
@ -1003,7 +1021,11 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
line_out->edge_a_id = points[1].edge_id;
line_out->edge_b_id = points[0].edge_id;
// General slicing position, use the segment for both slicing and object cutting.
return Slicing;
// In a degenerate case where a plane cuts the triangle very close to its vertex, it is possible, that
// a zero length edge is created. In that case the zero length edge could be safely ignored
// as the polyline will still be connected, because both the sliced edges of the triangle will be
// sliced the same way at the neighbor triangles.
return (line_out->a == line_out->b) ? NoSlice : Slicing;
}
return NoSlice;
}
@ -1074,6 +1096,11 @@ static inline void remove_tangent_edges(std::vector<IntersectionLine> &lines)
void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
{
#ifdef _DEBUG
for (const Line &l : lines_src)
assert(l.a != l.b);
#endif /* _DEBUG */
remove_tangent_edges(lines);
struct OpenPolyline {

View file

@ -9,6 +9,7 @@ namespace Slic3r {
extern void set_logging_level(unsigned int level);
extern void trace(unsigned int level, const char *message);
extern void disable_multi_threading();
// Set a path with GUI resource files.
void set_var_dir(const std::string &path);

View file

@ -24,6 +24,8 @@
#include <boost/nowide/integration/filesystem.hpp>
#include <boost/nowide/convert.hpp>
#include <tbb/task_scheduler_init.h>
namespace Slic3r {
static boost::log::trivial::severity_level logSeverity = boost::log::trivial::error;
@ -82,6 +84,14 @@ void trace(unsigned int level, const char *message)
(::boost::log::keywords::severity = severity)) << message;
}
void disable_multi_threading()
{
// Disable parallelization so the Shiny profiler works
static tbb::task_scheduler_init *tbb_init = nullptr;
if (tbb_init == nullptr)
tbb_init = new tbb::task_scheduler_init(1);
}
static std::string g_var_dir;
void set_var_dir(const std::string &dir)

View file

@ -48,6 +48,11 @@ trace(level, message)
CODE:
Slic3r::trace(level, message);
void
disable_multi_threading()
CODE:
Slic3r::disable_multi_threading();
void
set_var_dir(dir)
char *dir;