Merge remote-tracking branch 'origin/vb_slicing_fix'

This commit is contained in:
bubnikv 2018-09-14 15:08:07 +02:00
commit a744ed7897
8 changed files with 379 additions and 142 deletions

View File

@ -86,8 +86,8 @@ void FillHoneycomb::_fill_surface_single(
Polylines paths; Polylines paths;
{ {
Polylines p; Polylines p;
for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it) for (Polygon &poly : polygons)
p.push_back((Polyline)(*it)); p.emplace_back(poly.points);
paths = intersection_pl(p, to_polygons(expolygon)); paths = intersection_pl(p, to_polygons(expolygon));
} }

View File

@ -610,6 +610,7 @@ const BoundingBoxf3& ModelObject::bounding_box() const
BoundingBoxf3 raw_bbox; BoundingBoxf3 raw_bbox;
for (const ModelVolume *v : this->volumes) for (const ModelVolume *v : this->volumes)
if (! v->modifier) if (! v->modifier)
// mesh.bounding_box() returns a cached value.
raw_bbox.merge(v->mesh.bounding_box()); raw_bbox.merge(v->mesh.bounding_box());
BoundingBoxf3 bb; BoundingBoxf3 bb;
for (const ModelInstance *i : this->instances) for (const ModelInstance *i : this->instances)

View File

@ -19,6 +19,8 @@ public:
Polyline() {}; Polyline() {};
Polyline(const Polyline &other) : MultiPoint(other.points) {} Polyline(const Polyline &other) : MultiPoint(other.points) {}
Polyline(Polyline &&other) : MultiPoint(std::move(other.points)) {} Polyline(Polyline &&other) : MultiPoint(std::move(other.points)) {}
explicit Polyline(const Points &points) : MultiPoint(points) {}
explicit Polyline(Points &&points) : MultiPoint(std::move(points)) {}
Polyline& operator=(const Polyline &other) { points = other.points; return *this; } Polyline& operator=(const Polyline &other) { points = other.points; return *this; }
Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; } Polyline& operator=(Polyline &&other) { points = std::move(other.points); return *this; }
static Polyline new_scale(std::vector<Pointf> points) { static Polyline new_scale(std::vector<Pointf> points) {

View File

@ -21,16 +21,20 @@
#include <Eigen/Dense> #include <Eigen/Dense>
// for SLIC3R_DEBUG_SLICE_PROCESSING
#include "libslic3r.h"
#if 0 #if 0
#define DEBUG #define DEBUG
#define _DEBUG #define _DEBUG
#undef NDEBUG #undef NDEBUG
#define SLIC3R_DEBUG
// #define SLIC3R_TRIANGLEMESH_DEBUG
#endif #endif
#include <assert.h> #include <assert.h>
#ifdef SLIC3R_DEBUG #if defined(SLIC3R_DEBUG) || defined(SLIC3R_DEBUG_SLICE_PROCESSING)
// #define SLIC3R_TRIANGLEMESH_DEBUG
#include "SVG.hpp" #include "SVG.hpp"
#endif #endif
@ -225,7 +229,6 @@ TriangleMesh::repair() {
BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished"; BOOST_LOG_TRIVIAL(debug) << "TriangleMesh::repair() finished";
} }
float TriangleMesh::volume() float TriangleMesh::volume()
{ {
if (this->stl.stats.volume == -1) if (this->stl.stats.volume == -1)
@ -440,7 +443,7 @@ bool TriangleMesh::has_multiple_patches() const
facet_visited[facet_idx] = true; facet_visited[facet_idx] = true;
for (int j = 0; j < 3; ++ j) { for (int j = 0; j < 3; ++ j) {
int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j]; int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
if (! facet_visited[neighbor_idx]) if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
facet_queue[facet_queue_cnt ++] = neighbor_idx; facet_queue[facet_queue_cnt ++] = neighbor_idx;
} }
} }
@ -483,7 +486,7 @@ size_t TriangleMesh::number_of_patches() const
facet_visited[facet_idx] = true; facet_visited[facet_idx] = true;
for (int j = 0; j < 3; ++ j) { for (int j = 0; j < 3; ++ j) {
int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j]; int neighbor_idx = this->stl.neighbors_start[facet_idx].neighbor[j];
if (! facet_visited[neighbor_idx]) if (neighbor_idx != -1 && ! facet_visited[neighbor_idx])
facet_queue[facet_queue_cnt ++] = neighbor_idx; facet_queue[facet_queue_cnt ++] = neighbor_idx;
} }
} }
@ -492,8 +495,7 @@ size_t TriangleMesh::number_of_patches() const
return num_bodies; return num_bodies;
} }
TriangleMeshPtrs TriangleMeshPtrs TriangleMesh::split() const
TriangleMesh::split() const
{ {
TriangleMeshPtrs meshes; TriangleMeshPtrs meshes;
std::set<int> seen_facets; std::set<int> seen_facets;
@ -545,8 +547,7 @@ TriangleMesh::split() const
return meshes; return meshes;
} }
void void TriangleMesh::merge(const TriangleMesh &mesh)
TriangleMesh::merge(const TriangleMesh &mesh)
{ {
// reset stats and metadata // reset stats and metadata
int number_of_facets = this->stl.stats.number_of_facets; int number_of_facets = this->stl.stats.number_of_facets;
@ -600,8 +601,7 @@ Polygon TriangleMesh::convex_hull()
return Slic3r::Geometry::convex_hull(pp); return Slic3r::Geometry::convex_hull(pp);
} }
BoundingBoxf3 BoundingBoxf3 TriangleMesh::bounding_box() const
TriangleMesh::bounding_box() const
{ {
BoundingBoxf3 bb; BoundingBoxf3 bb;
bb.defined = true; bb.defined = true;
@ -748,8 +748,7 @@ const float* TriangleMesh::first_vertex() const
return stl.facet_start ? &stl.facet_start->vertex[0].x : nullptr; return stl.facet_start ? &stl.facet_start->vertex[0].x : nullptr;
} }
void void TriangleMesh::require_shared_vertices()
TriangleMesh::require_shared_vertices()
{ {
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - start"; BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - start";
if (!this->repaired) if (!this->repaired)
@ -758,10 +757,23 @@ TriangleMesh::require_shared_vertices()
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices"; BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - stl_generate_shared_vertices";
stl_generate_shared_vertices(&(this->stl)); stl_generate_shared_vertices(&(this->stl));
} }
#ifdef _DEBUG
// Verify validity of neighborship data.
for (int facet_idx = 0; facet_idx < stl.stats.number_of_facets; ++facet_idx) {
const stl_neighbors &nbr = stl.neighbors_start[facet_idx];
const int *vertices = stl.v_indices[facet_idx].vertex;
for (int nbr_idx = 0; nbr_idx < 3; ++nbr_idx) {
int nbr_face = this->stl.neighbors_start[facet_idx].neighbor[nbr_idx];
if (nbr_face != -1) {
assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == vertices[(nbr_idx + 1) % 3]);
assert(stl.v_indices[nbr_face].vertex[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == vertices[nbr_idx]);
}
}
}
#endif /* _DEBUG */
BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end"; BOOST_LOG_TRIVIAL(trace) << "TriangleMeshSlicer::require_shared_vertices - end";
} }
TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) :
mesh(_mesh) mesh(_mesh)
{ {
@ -847,8 +859,7 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) :
} }
} }
void void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* layers) const
{ {
BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice"; BOOST_LOG_TRIVIAL(debug) << "TriangleMeshSlicer::slice";
@ -910,13 +921,30 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons>* la
{ {
static int iRun = 0; static int iRun = 0;
for (size_t i = 0; i < z.size(); ++ i) { for (size_t i = 0; i < z.size(); ++ i) {
Polygons &polygons = (*layers)[i]; Polygons &polygons = (*layers)[i];
SVG::export_expolygons(debug_out_path("slice_%d_%d.svg", iRun, i).c_str(), union_ex(polygons, true)); 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();
}
#if 0
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
for (Polygon &poly : polygons) { for (Polygon &poly : polygons) {
for (size_t i = 1; i < poly.points.size(); ++ i) for (size_t i = 1; i < poly.points.size(); ++ i)
assert(poly.points[i-1] != poly.points[i]); assert(poly.points[i-1] != poly.points[i]);
assert(poly.points.front() != poly.points.back()); assert(poly.points.front() != poly.points.back());
} }
#endif
} }
++ iRun; ++ iRun;
} }
@ -932,54 +960,58 @@ void TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector<IntersectionLin
const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z)); const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z));
const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z)); const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z));
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx,
facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z, facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z,
facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z, facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z,
facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z); facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z);
printf("z: min = %.2f, max = %.2f\n", min_z, max_z); printf("z: min = %.2f, max = %.2f\n", min_z, max_z);
#endif #endif /* SLIC3R_TRIANGLEMESH_DEBUG */
// find layer extents // find layer extents
std::vector<float>::const_iterator min_layer, max_layer; std::vector<float>::const_iterator min_layer, max_layer;
min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z
max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_TRIANGLEMESH_DEBUG
printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin())); printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin()));
#endif #endif /* SLIC3R_TRIANGLEMESH_DEBUG */
for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) { for (std::vector<float>::const_iterator it = min_layer; it != max_layer + 1; ++it) {
std::vector<float>::size_type layer_idx = it - z.begin(); std::vector<float>::size_type layer_idx = it - z.begin();
IntersectionLine il; IntersectionLine il;
if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il)) { if (this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &il) == TriangleMeshSlicer::Slicing) {
boost::lock_guard<boost::mutex> l(*lines_mutex); boost::lock_guard<boost::mutex> l(*lines_mutex);
if (il.edge_type == feHorizontal) { if (il.edge_type == feHorizontal) {
// Insert all three edges of the face. // Insert all marked edges of the face. The marked edges do not share an edge with another horizontal face
const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex; // (they may not have a nighbor, or their neighbor is vertical)
const bool reverse = this->mesh->stl.facet_start[facet_idx].normal.z < 0; const int *vertices = this->mesh->stl.v_indices[facet_idx].vertex;
for (int j = 0; j < 3; ++ j) { const bool reverse = this->mesh->stl.facet_start[facet_idx].normal.z < 0;
int a_id = vertices[j % 3]; for (int j = 0; j < 3; ++ j)
int b_id = vertices[(j+1) % 3]; if (il.flags & ((IntersectionLine::EDGE0_NO_NEIGHBOR | IntersectionLine::EDGE0_FOLD) << j)) {
if (reverse) int a_id = vertices[j % 3];
std::swap(a_id, b_id); int b_id = vertices[(j+1) % 3];
const stl_vertex *a = &this->v_scaled_shared[a_id]; if (reverse)
const stl_vertex *b = &this->v_scaled_shared[b_id]; std::swap(a_id, b_id);
il.a.x = a->x; const stl_vertex *a = &this->v_scaled_shared[a_id];
il.a.y = a->y; const stl_vertex *b = &this->v_scaled_shared[b_id];
il.b.x = b->x; il.a.x = a->x;
il.b.y = b->y; il.a.y = a->y;
il.a_id = a_id; il.b.x = b->x;
il.b_id = b_id; il.b.y = b->y;
(*lines)[layer_idx].push_back(il); il.a_id = a_id;
} il.b_id = b_id;
assert(il.a != il.b);
// This edge will not be used as a seed for loop extraction if it was added due to a fold of two overlapping horizontal faces.
il.set_no_seed((IntersectionLine::EDGE0_FOLD << j) != 0);
(*lines)[layer_idx].emplace_back(il);
}
} else } else
(*lines)[layer_idx].push_back(il); (*lines)[layer_idx].emplace_back(il);
} }
} }
} }
void void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const
TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const
{ {
std::vector<Polygons> layers_p; std::vector<Polygons> layers_p;
this->slice(z, &layers_p); this->slice(z, &layers_p);
@ -1000,23 +1032,22 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>*
} }
// Return true, if the facet has been sliced and line_out has been filled. // Return true, if the facet has been sliced and line_out has been filled.
bool TriangleMeshSlicer::slice_facet( TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
float slice_z, const stl_facet &facet, const int facet_idx, float slice_z, const stl_facet &facet, const int facet_idx,
const float min_z, const float max_z, const float min_z, const float max_z,
IntersectionLine *line_out) const IntersectionLine *line_out) const
{ {
IntersectionPoint points[3]; IntersectionPoint points[3];
size_t num_points = 0; size_t num_points = 0;
size_t points_on_layer[3]; size_t point_on_layer = size_t(-1);
size_t num_points_on_layer = 0;
// Reorder vertices so that the first one is the one with lowest Z. // 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 // This is needed to get all intersection lines in a consistent order
// (external on the right of the line) // (external on the right of the line)
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); 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 for (int j = i; j - i < 3; ++j) { // loop through facet edges
int edge_id = this->facets_edges[facet_idx * 3 + (j % 3)]; 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 a_id = vertices[j % 3];
int b_id = vertices[(j+1) % 3]; int b_id = vertices[(j+1) % 3];
const stl_vertex *a = &this->v_scaled_shared[a_id]; const stl_vertex *a = &this->v_scaled_shared[a_id];
@ -1028,22 +1059,110 @@ bool TriangleMeshSlicer::slice_facet(
const stl_vertex &v0 = this->v_scaled_shared[vertices[0]]; const stl_vertex &v0 = this->v_scaled_shared[vertices[0]];
const stl_vertex &v1 = this->v_scaled_shared[vertices[1]]; const stl_vertex &v1 = this->v_scaled_shared[vertices[1]];
const stl_vertex &v2 = this->v_scaled_shared[vertices[2]]; 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];
if (min_z == max_z) { if (min_z == max_z) {
// All three vertices are aligned with slice_z. // All three vertices are aligned with slice_z.
line_out->edge_type = feHorizontal; line_out->edge_type = feHorizontal;
if (this->mesh->stl.facet_start[facet_idx].normal.z < 0) { // Mark neighbor edges, which do not have a neighbor.
uint32_t edges = 0;
for (int nbr_idx = 0; nbr_idx != 3; ++ nbr_idx) {
// 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] == -1) {
// Mark this edge to be added to the slice.
edges |= (IntersectionLine::EDGE0_NO_NEIGHBOR << nbr_idx);
}
#if 1
else if (normal.z > 0) {
// Produce edges for opposite faced overlapping horizontal faces aka folds.
// This method often produces connecting lines (noise) at the cutting plane.
// Produce the edges for the top facing face of the pair of top / bottom facing faces.
// Index of a neighbor face.
const int nbr_face = nbr.neighbor[nbr_idx];
const int *nbr_vertices = this->mesh->stl.v_indices[nbr_face].vertex;
int idx_vertex_opposite = nbr_vertices[nbr.which_vertex_not[nbr_idx]];
const stl_vertex *c2 = &this->v_scaled_shared[idx_vertex_opposite];
if (c2->z == slice_z) {
// Edge shared by facet_idx and nbr_face.
int a_id = vertices[nbr_idx];
int b_id = vertices[(nbr_idx + 1) % 3];
int c1_id = vertices[(nbr_idx + 2) % 3];
const stl_vertex *a = &this->v_scaled_shared[a_id];
const stl_vertex *b = &this->v_scaled_shared[b_id];
const stl_vertex *c1 = &this->v_scaled_shared[c1_id];
// Verify that the two neighbor faces share a common edge.
assert(nbr_vertices[(nbr.which_vertex_not[nbr_idx] + 1) % 3] == b_id);
assert(nbr_vertices[(nbr.which_vertex_not[nbr_idx] + 2) % 3] == a_id);
double n1 = (double(c1->x) - double(a->x)) * (double(b->y) - double(a->y)) - (double(c1->y) - double(a->y)) * (double(b->x) - double(a->x));
double n2 = (double(c2->x) - double(a->x)) * (double(b->y) - double(a->y)) - (double(c2->y) - double(a->y)) * (double(b->x) - double(a->x));
if (n1 * n2 > 0)
// The two faces overlap. This indicates an invalid mesh geometry (non-manifold),
// but these are the real world objects, and leaving out these edges leads to missing contours.
edges |= (IntersectionLine::EDGE0_FOLD << nbr_idx);
}
}
#endif
}
// 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 (normal.z < 0) {
// If normal points downwards this is a bottom horizontal facet so we reverse its point order. // If normal points downwards this is a bottom horizontal facet so we reverse its point order.
std::swap(a, b); std::swap(a, b);
std::swap(a_id, b_id); std::swap(a_id, b_id);
} }
} else if (v0.z < slice_z || v1.z < slice_z || v2.z < slice_z) {
// Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
line_out->edge_type = feTop;
std::swap(a, b);
std::swap(a_id, b_id);
} else { } else {
// Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane. // Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane.
line_out->edge_type = feBottom; 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;
// Is this a concave corner?
if (nbr_face == -1) {
#ifdef _DEBUG
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) {
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));
#if 0
if ((normal_nbr < 0) == third_below) {
printf("Flipped normal?\n");
}
#endif
result =
// 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.
#if 1
// Ignore concave corners for slicing.
// This method has the unfortunate property, that folds in a horizontal plane create concave corners,
// leading to broken contours, if these concave corners are not replaced by edges of the folds, see above.
((normal_nbr < 0) == third_below) ? Cutting : Slicing;
#else
// Use concave corners for slicing. This leads to the test 01_trianglemesh.t "slicing a top tangent plane includes its area" failing,
// and rightly so.
Slicing;
#endif
} else {
// For a pair of faces touching exactly at the cutting plane, ignore one of them. An arbitrary rule is to ignore the face with a higher index.
result = (facet_idx < nbr_face) ? Slicing : Cutting;
}
}
if (third_below) {
line_out->edge_type = feTop;
std::swap(a, b);
std::swap(a_id, b_id);
} else
line_out->edge_type = feBottom;
} }
line_out->a.x = a->x; line_out->a.x = a->x;
line_out->a.y = a->y; line_out->a.y = a->y;
@ -1051,97 +1170,170 @@ bool TriangleMeshSlicer::slice_facet(
line_out->b.y = b->y; line_out->b.y = b->y;
line_out->a_id = a_id; line_out->a_id = a_id;
line_out->b_id = b_id; line_out->b_id = b_id;
return true; assert(line_out->a != line_out->b);
return result;
} }
if (a->z == slice_z) { if (a->z == slice_z) {
// Only point a alings with the cutting plane. // Only point a alings with the cutting plane.
points_on_layer[num_points_on_layer ++] = num_points; if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
IntersectionPoint &point = points[num_points ++]; point_on_layer = num_points;
point.x = a->x; IntersectionPoint &point = points[num_points ++];
point.y = a->y; point.x = a->x;
point.point_id = a_id; point.y = a->y;
point.point_id = a_id;
}
} else if (b->z == slice_z) { } else if (b->z == slice_z) {
// Only point b alings with the cutting plane. // Only point b alings with the cutting plane.
points_on_layer[num_points_on_layer ++] = num_points; if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
IntersectionPoint &point = points[num_points ++]; point_on_layer = num_points;
point.x = b->x; IntersectionPoint &point = points[num_points ++];
point.y = b->y; point.x = b->x;
point.point_id = b_id; point.y = b->y;
point.point_id = b_id;
}
} else if ((a->z < slice_z && b->z > slice_z) || (b->z < slice_z && a->z > slice_z)) { } 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. // A general case. The face edge intersects the cutting plane. Calculate the intersection point.
IntersectionPoint &point = points[num_points ++]; assert(a_id != b_id);
point.x = b->x + (a->x - b->x) * (slice_z - b->z) / (a->z - b->z); // Sort the edge to give a consistent answer.
point.y = b->y + (a->y - b->y) * (slice_z - b->z) / (a->z - b->z); if (a_id > b_id) {
point.edge_id = edge_id; std::swap(a_id, b_id);
std::swap(a, b);
}
IntersectionPoint &point = points[num_points];
double t = (double(slice_z) - double(b->z)) / (double(a->z) - double(b->z));
if (t <= 0.) {
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
point.x = a->x;
point.y = a->y;
point_on_layer = num_points ++;
point.point_id = a_id;
}
} else if (t >= 1.) {
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
point.x = b->x;
point.y = b->y;
point_on_layer = num_points ++;
point.point_id = b_id;
}
} else {
point.x = coord_t(floor(double(b->x) + (double(a->x) - double(b->x)) * t + 0.5));
point.y = coord_t(floor(double(b->y) + (double(a->y) - double(b->y)) * t + 0.5));
point.edge_id = edge_id;
++ num_points;
}
} }
} }
// We can't have only one point on layer because each vertex gets detected // Facets must intersect each plane 0 or 2 times, or it may touch the plane at a single vertex only.
// twice (once for each edge), and we can't have three points on layer, assert(num_points < 3);
// because we assume this code is not getting called for horizontal facets.
assert(num_points_on_layer == 0 || num_points_on_layer == 2);
if (num_points_on_layer > 0) {
assert(points[points_on_layer[0]].point_id == points[points_on_layer[1]].point_id);
assert(num_points == 2 || num_points == 3);
if (num_points < 3)
// This triangle touches the cutting plane with a single vertex. Ignore it.
return false;
// Erase one of the duplicate points.
-- num_points;
for (int i = points_on_layer[1]; i < num_points; ++ i)
points[i] = points[i + 1];
}
// Facets must intersect each plane 0 or 2 times.
assert(num_points == 0 || num_points == 2);
if (num_points == 2) { if (num_points == 2) {
line_out->edge_type = feNone; line_out->edge_type = feGeneral;
line_out->a = (Point)points[1]; line_out->a = (Point)points[1];
line_out->b = (Point)points[0]; line_out->b = (Point)points[0];
line_out->a_id = points[1].point_id; line_out->a_id = points[1].point_id;
line_out->b_id = points[0].point_id; line_out->b_id = points[0].point_id;
line_out->edge_a_id = points[1].edge_id; line_out->edge_a_id = points[1].edge_id;
line_out->edge_b_id = points[0].edge_id; line_out->edge_b_id = points[0].edge_id;
return true; // Not a zero lenght edge.
//FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
//assert(line_out->a != line_out->b);
// The plane cuts at least one edge in a general position.
assert(line_out->a_id == -1 || line_out->b_id == -1);
assert(line_out->edge_a_id != -1 || line_out->edge_b_id != -1);
// General slicing position, use the segment for both slicing and object cutting.
#if 0
if (line_out->a_id != -1 && line_out->b_id != -1) {
// Solving a degenerate case, where both the intersections snapped to an edge.
// Correctly classify the face as below or above based on the position of the 3rd point.
int i = vertices[0];
if (i == line_out->a_id || i == line_out->b_id)
i = vertices[1];
if (i == line_out->a_id || i == line_out->b_id)
i = vertices[2];
assert(i != line_out->a_id && i != line_out->b_id);
line_out->edge_type = (this->v_scaled_shared[i].z < slice_z) ? feTop : feBottom;
}
#endif
return Slicing;
}
return NoSlice;
}
//FIXME Should this go away? For valid meshes the function slice_facet() returns Slicing
// and sets edges of vertical triangles to produce only a single edge per pair of neighbor faces.
// So the following code makes only sense now to handle degenerate meshes with more than two faces
// sharing a single edge.
static inline void remove_tangent_edges(std::vector<IntersectionLine> &lines)
{
std::vector<IntersectionLine*> by_vertex_pair;
by_vertex_pair.reserve(lines.size());
for (IntersectionLine& line : lines)
if (line.edge_type != feGeneral && line.a_id != -1)
// This is a face edge. Check whether there is its neighbor stored in lines.
by_vertex_pair.emplace_back(&line);
auto edges_lower_sorted = [](const IntersectionLine *l1, const IntersectionLine *l2) {
// Sort vertices of l1, l2 lexicographically
int l1a = l1->a_id;
int l1b = l1->b_id;
int l2a = l2->a_id;
int l2b = l2->b_id;
if (l1a > l1b)
std::swap(l1a, l1b);
if (l2a > l2b)
std::swap(l2a, l2b);
// Lexicographical "lower" operator on lexicographically sorted vertices should bring equal edges together when sored.
return l1a < l2a || (l1a == l2a && l1b < l2b);
};
std::sort(by_vertex_pair.begin(), by_vertex_pair.end(), edges_lower_sorted);
for (auto line = by_vertex_pair.begin(); line != by_vertex_pair.end(); ++ line) {
IntersectionLine &l1 = **line;
if (! l1.skip()) {
// Iterate as long as line and line2 edges share the same end points.
for (auto line2 = line + 1; line2 != by_vertex_pair.end() && ! edges_lower_sorted(*line, *line2); ++ line2) {
// Lines must share the end points.
assert(! edges_lower_sorted(*line, *line2));
assert(! edges_lower_sorted(*line2, *line));
IntersectionLine &l2 = **line2;
if (l2.skip())
continue;
if (l1.a_id == l2.a_id) {
assert(l1.b_id == l2.b_id);
l2.set_skip();
// If they are both oriented upwards or downwards (like a 'V'),
// then we can remove both edges from this layer since it won't
// affect the sliced shape.
// If one of them is oriented upwards and the other is oriented
// downwards, let's only keep one of them (it doesn't matter which
// one since all 'top' lines were reversed at slicing).
if (l1.edge_type == l2.edge_type) {
l1.set_skip();
break;
}
} else {
assert(l1.a_id == l2.b_id && l1.b_id == l2.a_id);
// If this edge joins two horizontal facets, remove both of them.
if (l1.edge_type == feHorizontal && l2.edge_type == feHorizontal) {
l1.set_skip();
l2.set_skip();
break;
}
}
}
}
} }
return false;
} }
void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
{ {
// Remove tangent edges. #if 0
//FIXME This is O(n^2) in rare cases when many faces intersect the cutting plane. //FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++ line) //#ifdef _DEBUG
if (! line->skip && line->edge_type != feNone) { for (const Line &l : lines)
// This line is af facet edge. There may be a duplicate line with the same end vertices. assert(l.a != l.b);
// If the line is is an edge connecting two facets, find another facet edge #endif /* _DEBUG */
// having the same endpoints but in reverse order.
for (IntersectionLines::iterator line2 = line + 1; line2 != lines.end(); ++ line2) remove_tangent_edges(lines);
if (! line2->skip && line2->edge_type != feNone) {
// Are these facets adjacent? (sharing a common edge on this layer)
if (line->a_id == line2->a_id && line->b_id == line2->b_id) {
line2->skip = true;
/* if they are both oriented upwards or downwards (like a 'V')
then we can remove both edges from this layer since it won't
affect the sliced shape */
/* if one of them is oriented upwards and the other is oriented
downwards, let's only keep one of them (it doesn't matter which
one since all 'top' lines were reversed at slicing) */
if (line->edge_type == line2->edge_type) {
line->skip = true;
break;
}
} else if (line->a_id == line2->b_id && line->b_id == line2->a_id) {
/* if this edge joins two horizontal facets, remove both of them */
if (line->edge_type == feHorizontal && line2->edge_type == feHorizontal) {
line->skip = true;
line2->skip = true;
break;
}
}
}
}
struct OpenPolyline { struct OpenPolyline {
OpenPolyline() {}; OpenPolyline() {};
@ -1164,7 +1356,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
by_edge_a_id.reserve(lines.size()); by_edge_a_id.reserve(lines.size());
by_a_id.reserve(lines.size()); by_a_id.reserve(lines.size());
for (IntersectionLine &line : lines) { for (IntersectionLine &line : lines) {
if (! line.skip) { if (! line.skip()) {
if (line.edge_a_id != -1) if (line.edge_a_id != -1)
by_edge_a_id.emplace_back(&line); by_edge_a_id.emplace_back(&line);
if (line.a_id != -1) if (line.a_id != -1)
@ -1181,13 +1373,14 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
// take first spare line and start a new loop // take first spare line and start a new loop
IntersectionLine *first_line = nullptr; IntersectionLine *first_line = nullptr;
for (; it_line_seed != lines.end(); ++ it_line_seed) for (; it_line_seed != lines.end(); ++ it_line_seed)
if (! it_line_seed->skip) { if (it_line_seed->is_seed_candidate()) {
//if (! it_line_seed->skip()) {
first_line = &(*it_line_seed ++); first_line = &(*it_line_seed ++);
break; break;
} }
if (first_line == nullptr) if (first_line == nullptr)
break; break;
first_line->skip = true; first_line->set_skip();
Points loop_pts; Points loop_pts;
loop_pts.emplace_back(first_line->a); loop_pts.emplace_back(first_line->a);
IntersectionLine *last_line = first_line; IntersectionLine *last_line = first_line;
@ -1208,7 +1401,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
if (it_begin != by_edge_a_id.end()) { if (it_begin != by_edge_a_id.end()) {
auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower); auto it_end = std::upper_bound(it_begin, by_edge_a_id.end(), &key, by_edge_lower);
for (auto it_line = it_begin; it_line != it_end; ++ it_line) for (auto it_line = it_begin; it_line != it_end; ++ it_line)
if (! (*it_line)->skip) { if (! (*it_line)->skip()) {
next_line = *it_line; next_line = *it_line;
break; break;
} }
@ -1220,7 +1413,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
if (it_begin != by_a_id.end()) { if (it_begin != by_a_id.end()) {
auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower); auto it_end = std::upper_bound(it_begin, by_a_id.end(), &key, by_vertex_lower);
for (auto it_line = it_begin; it_line != it_end; ++ it_line) for (auto it_line = it_begin; it_line != it_end; ++ it_line)
if (! (*it_line)->skip) { if (! (*it_line)->skip()) {
next_line = *it_line; next_line = *it_line;
break; break;
} }
@ -1251,7 +1444,7 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
*/ */
loop_pts.emplace_back(next_line->a); loop_pts.emplace_back(next_line->a);
last_line = next_line; last_line = next_line;
next_line->skip = true; next_line->set_skip();
} }
} }
} }
@ -1343,8 +1536,8 @@ void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygo
if ((ip1.edge_id != -1 && ip1.edge_id == ip2.edge_id) || if ((ip1.edge_id != -1 && ip1.edge_id == ip2.edge_id) ||
(ip1.point_id != -1 && ip1.point_id == ip2.point_id)) { (ip1.point_id != -1 && ip1.point_id == ip2.point_id)) {
// The current loop is complete. Add it to the output. // The current loop is complete. Add it to the output.
assert(opl.points.front().point_id == opl.points.back().point_id); //assert(opl.points.front().point_id == opl.points.back().point_id);
assert(opl.points.front().edge_id == opl.points.back().edge_id); //assert(opl.points.front().edge_id == opl.points.back().edge_id);
// Remove the duplicate last point. // Remove the duplicate last point.
opl.points.pop_back(); opl.points.pop_back();
if (opl.points.size() >= 3) { if (opl.points.size() >= 3) {
@ -1544,7 +1737,7 @@ void TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower)
// intersect facet with cutting plane // intersect facet with cutting plane
IntersectionLine line; IntersectionLine line;
if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line)) { if (this->slice_facet(scaled_z, *facet, facet_idx, min_z, max_z, &line) != TriangleMeshSlicer::NoSlice) {
// Save intersection lines for generating correct triangulations. // Save intersection lines for generating correct triangulations.
if (line.edge_type == feTop) { if (line.edge_type == feTop) {
lower_lines.push_back(line); lower_lines.push_back(line);

View File

@ -82,7 +82,7 @@ private:
enum FacetEdgeType { enum FacetEdgeType {
// A general case, the cutting plane intersect a face at two different edges. // A general case, the cutting plane intersect a face at two different edges.
feNone, feGeneral,
// Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane. // Two vertices are aligned with the cutting plane, the third vertex is below the cutting plane.
feTop, feTop,
// Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane. // Two vertices are aligned with the cutting plane, the third vertex is above the cutting plane.
@ -116,6 +116,14 @@ public:
class IntersectionLine : public Line class IntersectionLine : public Line
{ {
public: public:
IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feGeneral), flags(0) {}
bool skip() const { return (this->flags & SKIP) != 0; }
void set_skip() { this->flags |= SKIP; }
bool is_seed_candidate() const { return (this->flags & NO_SEED) == 0 && ! this->skip(); }
void set_no_seed(bool set) { if (set) this->flags |= NO_SEED; else this->flags &= ~NO_SEED; }
// Inherits Point a, b // Inherits Point a, b
// For each line end point, either {a,b}_id or {a,b}edge_a_id is set, the other is left to -1. // For each line end point, either {a,b}_id or {a,b}edge_a_id is set, the other is left to -1.
// Vertex indices of the line end points. // Vertex indices of the line end points.
@ -124,11 +132,23 @@ public:
// Source mesh edges of the line end points. // Source mesh edges of the line end points.
int edge_a_id; int edge_a_id;
int edge_b_id; int edge_b_id;
// feNone, feTop, feBottom, feHorizontal // feGeneral, feTop, feBottom, feHorizontal
FacetEdgeType edge_type; FacetEdgeType edge_type;
// Used by TriangleMeshSlicer::make_loops() to skip duplicate edges. // Used by TriangleMeshSlicer::slice() to skip duplicate edges.
bool skip; enum {
IntersectionLine() : a_id(-1), b_id(-1), edge_a_id(-1), edge_b_id(-1), edge_type(feNone), skip(false) {}; // Triangle edge added, because it has no neighbor.
EDGE0_NO_NEIGHBOR = 0x001,
EDGE1_NO_NEIGHBOR = 0x002,
EDGE2_NO_NEIGHBOR = 0x004,
// Triangle edge added, because it makes a fold with another horizontal edge.
EDGE0_FOLD = 0x010,
EDGE1_FOLD = 0x020,
EDGE2_FOLD = 0x040,
// The edge cannot be a seed of a greedy loop extraction (folds are not safe to become seeds).
NO_SEED = 0x100,
SKIP = 0x200,
};
uint32_t flags;
}; };
typedef std::vector<IntersectionLine> IntersectionLines; typedef std::vector<IntersectionLine> IntersectionLines;
typedef std::vector<IntersectionLine*> IntersectionLinePtrs; typedef std::vector<IntersectionLine*> IntersectionLinePtrs;
@ -139,7 +159,12 @@ public:
TriangleMeshSlicer(TriangleMesh* _mesh); TriangleMeshSlicer(TriangleMesh* _mesh);
void slice(const std::vector<float> &z, std::vector<Polygons>* layers) const; void slice(const std::vector<float> &z, std::vector<Polygons>* layers) const;
void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const; void slice(const std::vector<float> &z, std::vector<ExPolygons>* layers) const;
bool slice_facet(float slice_z, const stl_facet &facet, const int facet_idx, enum FacetSliceType {
NoSlice = 0,
Slicing = 1,
Cutting = 2
};
FacetSliceType slice_facet(float slice_z, const stl_facet &facet, const int facet_idx,
const float min_z, const float max_z, IntersectionLine *line_out) const; const float min_z, const float max_z, IntersectionLine *line_out) const;
void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const; void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const;

View File

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

View File

@ -24,6 +24,8 @@
#include <boost/nowide/integration/filesystem.hpp> #include <boost/nowide/integration/filesystem.hpp>
#include <boost/nowide/convert.hpp> #include <boost/nowide/convert.hpp>
#include <tbb/task_scheduler_init.h>
namespace Slic3r { namespace Slic3r {
static boost::log::trivial::severity_level logSeverity = boost::log::trivial::error; 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; (::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; static std::string g_var_dir;
void set_var_dir(const std::string &dir) void set_var_dir(const std::string &dir)

View File

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