Another improvement in robustness of mesh slicing.

This commit is contained in:
bubnikv 2018-08-10 17:37:09 +02:00
parent b67f32a94d
commit 13ce087606

View file

@ -469,7 +469,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;
} }
} }
@ -787,7 +787,7 @@ void TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<Polygons
svg.Close(); svg.Close();
} }
#if 0 #if 0
//FIXME the slice_facet() creates zero length edges. //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]);
@ -888,15 +888,14 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
{ {
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; 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)];
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];
@ -1026,46 +1025,57 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
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);
double t = (double(slice_z) - double(b->z)) / (double(a->z) - double(b->z)); // Sort the edge to give a consistent answer.
point.x = float(double(b->x) + (double(a->x) - double(b->x)) * t); if (a_id > b_id) {
point.y = float(double(b->y) + (double(a->y) - double(b->y)) * t); std::swap(a_id, b_id);
point.edge_id = edge_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 NoSlice;
// 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 = feGeneral; line_out->edge_type = feGeneral;
line_out->a = (Point)points[1]; line_out->a = (Point)points[1];
@ -1074,18 +1084,27 @@ TriangleMeshSlicer::FacetSliceType TriangleMeshSlicer::slice_facet(
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;
// 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. // General slicing position, use the segment for both slicing and object cutting.
#if 0 #if 0
// In a degenerate case where a plane cuts the triangle very close to its vertex, it is possible, that if (line_out->a_id != -1 && line_out->b_id != -1) {
// a zero length edge is created. In that case the zero length edge could be safely ignored // Solving a degenerate case, where both the intersections snapped to an edge.
// as the polyline will still be connected, because both the sliced edges of the triangle will be // Correctly classify the face as below or above based on the position of the 3rd point.
// sliced the same way at the neighbor triangles. int i = vertices[0];
return (line_out->a == line_out->b) ? NoSlice : Slicing; if (i == line_out->a_id || i == line_out->b_id)
#else i = vertices[1];
// The chaining code primarily relies on the IDs of the edges. if (i == line_out->a_id || i == line_out->b_id)
// Even though there may be a zero length edge generated, it is still important, i = vertices[2];
return Slicing; 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 #endif
return Slicing;
} }
return NoSlice; return NoSlice;
} }
@ -1157,7 +1176,7 @@ static inline void remove_tangent_edges(std::vector<IntersectionLine> &lines)
void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const void TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) const
{ {
#if 0 #if 0
//FIXME the slice_facet() creates zero length edges. //FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
//#ifdef _DEBUG //#ifdef _DEBUG
for (const Line &l : lines) for (const Line &l : lines)
assert(l.a != l.b); assert(l.a != l.b);