Bugfix: tangent horizontal mesh surfaces were not included in slices under rare circumstances, generating almost invalid polygons that confused Clipper and caused skipped layers. Includes regression test

This commit is contained in:
Alessandro Ranellucci 2015-01-28 13:00:38 +01:00
parent 8a5a0b6726
commit b1f1893481
4 changed files with 83 additions and 21 deletions

View File

@ -32,9 +32,17 @@ SVG::draw(const Line &line, std::string stroke)
} }
void void
SVG::AddLine(const IntersectionLine &line) SVG::draw(const Lines &lines, std::string stroke)
{ {
this->draw(Line(line.a, line.b)); for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it)
this->draw(*it, stroke);
}
void
SVG::draw(const IntersectionLines &lines, std::string stroke)
{
for (IntersectionLines::const_iterator it = lines.begin(); it != lines.end(); ++it)
this->draw((Line)*it, stroke);
} }
void void
@ -50,11 +58,25 @@ SVG::draw(const ExPolygon &expolygon, std::string fill)
this->path(d, true); this->path(d, true);
} }
void
SVG::draw(const ExPolygons &expolygons, std::string fill)
{
for (ExPolygons::const_iterator it = expolygons.begin(); it != expolygons.end(); ++it)
this->draw(*it, fill);
}
void void
SVG::draw(const Polygon &polygon, std::string fill) SVG::draw(const Polygon &polygon, std::string fill)
{ {
this->fill = fill; this->fill = fill;
this->path(this->get_path_d(polygon, true), true); this->path(this->get_path_d(polygon, true), !fill.empty());
}
void
SVG::draw(const Polygons &polygons, std::string fill)
{
for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it)
this->draw(*it, fill);
} }
void void
@ -64,6 +86,13 @@ SVG::draw(const Polyline &polyline, std::string stroke)
this->path(this->get_path_d(polyline, false), false); this->path(this->get_path_d(polyline, false), false);
} }
void
SVG::draw(const Polylines &polylines, std::string stroke)
{
for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it)
this->draw(*it, fill);
}
void void
SVG::draw(const Point &point, std::string fill, unsigned int radius) SVG::draw(const Point &point, std::string fill, unsigned int radius)
{ {
@ -75,6 +104,13 @@ SVG::draw(const Point &point, std::string fill, unsigned int radius)
fprintf(this->f, "%s\n", svg.str().c_str()); fprintf(this->f, "%s\n", svg.str().c_str());
} }
void
SVG::draw(const Points &points, std::string fill, unsigned int radius)
{
for (Points::const_iterator it = points.begin(); it != points.end(); ++it)
this->draw(*it, fill, radius);
}
void void
SVG::path(const std::string &d, bool fill) SVG::path(const std::string &d, bool fill)
{ {

View File

@ -15,12 +15,17 @@ class SVG
std::string fill, stroke; std::string fill, stroke;
SVG(const char* filename); SVG(const char* filename);
void AddLine(const IntersectionLine &line);
void draw(const Line &line, std::string stroke = "black"); void draw(const Line &line, std::string stroke = "black");
void draw(const Lines &lines, std::string stroke = "black");
void draw(const IntersectionLines &lines, std::string stroke = "black");
void draw(const ExPolygon &expolygon, std::string fill = "grey"); void draw(const ExPolygon &expolygon, std::string fill = "grey");
void draw(const ExPolygons &expolygons, std::string fill = "grey");
void draw(const Polygon &polygon, std::string fill = "grey"); void draw(const Polygon &polygon, std::string fill = "grey");
void draw(const Polygons &polygons, std::string fill = "grey");
void draw(const Polyline &polyline, std::string stroke = "black"); void draw(const Polyline &polyline, std::string stroke = "black");
void draw(const Polylines &polylines, std::string stroke = "black");
void draw(const Point &point, std::string fill = "black", unsigned int radius = 3); void draw(const Point &point, std::string fill = "black", unsigned int radius = 3);
void draw(const Points &points, std::string fill = "black", unsigned int radius = 3);
void Close(); void Close();
private: private:

View File

@ -181,6 +181,7 @@ TriangleMesh::WriteOBJFile(char* output_file) {
void TriangleMesh::scale(float factor) void TriangleMesh::scale(float factor)
{ {
stl_scale(&(this->stl), factor); stl_scale(&(this->stl), factor);
stl_invalidate_shared_vertices(&this->stl);
} }
void TriangleMesh::scale(const Pointf3 &versor) void TriangleMesh::scale(const Pointf3 &versor)
@ -190,41 +191,49 @@ void TriangleMesh::scale(const Pointf3 &versor)
fversor[1] = versor.y; fversor[1] = versor.y;
fversor[2] = versor.z; fversor[2] = versor.z;
stl_scale_versor(&this->stl, fversor); stl_scale_versor(&this->stl, fversor);
stl_invalidate_shared_vertices(&this->stl);
} }
void TriangleMesh::translate(float x, float y, float z) void TriangleMesh::translate(float x, float y, float z)
{ {
stl_translate_relative(&(this->stl), x, y, z); stl_translate_relative(&(this->stl), x, y, z);
stl_invalidate_shared_vertices(&this->stl);
} }
void TriangleMesh::rotate_x(float angle) void TriangleMesh::rotate_x(float angle)
{ {
stl_rotate_x(&(this->stl), angle); stl_rotate_x(&(this->stl), angle);
stl_invalidate_shared_vertices(&this->stl);
} }
void TriangleMesh::rotate_y(float angle) void TriangleMesh::rotate_y(float angle)
{ {
stl_rotate_y(&(this->stl), angle); stl_rotate_y(&(this->stl), angle);
stl_invalidate_shared_vertices(&this->stl);
} }
void TriangleMesh::rotate_z(float angle) void TriangleMesh::rotate_z(float angle)
{ {
stl_rotate_z(&(this->stl), angle); stl_rotate_z(&(this->stl), angle);
stl_invalidate_shared_vertices(&this->stl);
} }
void TriangleMesh::flip_x() void TriangleMesh::flip_x()
{ {
stl_mirror_yz(&this->stl); stl_mirror_yz(&this->stl);
stl_invalidate_shared_vertices(&this->stl);
} }
void TriangleMesh::flip_y() void TriangleMesh::flip_y()
{ {
stl_mirror_xz(&this->stl); stl_mirror_xz(&this->stl);
stl_invalidate_shared_vertices(&this->stl);
} }
void TriangleMesh::flip_z() void TriangleMesh::flip_z()
{ {
stl_mirror_xy(&this->stl); stl_mirror_xy(&this->stl);
stl_invalidate_shared_vertices(&this->stl);
} }
void TriangleMesh::align_to_origin() void TriangleMesh::align_to_origin()
@ -507,7 +516,7 @@ TriangleMeshSlicer::slice(const std::vector<float> &z, std::vector<ExPolygons>*
for (std::vector<Polygons>::const_iterator loops = layers_p.begin(); loops != layers_p.end(); ++loops) { for (std::vector<Polygons>::const_iterator loops = layers_p.begin(); loops != layers_p.end(); ++loops) {
#ifdef SLIC3R_DEBUG #ifdef SLIC3R_DEBUG
size_t layer_id = loops - layers_p.begin(); size_t layer_id = loops - layers_p.begin();
printf("Layer %zu (slice_z = %.2f): ", layer_id, z[layer_id]); printf("Layer %zu (slice_z = %.2f):\n", layer_id, z[layer_id]);
#endif #endif
this->make_expolygons(*loops, &(*layers)[ loops - layers_p.begin() ]); this->make_expolygons(*loops, &(*layers)[ loops - layers_p.begin() ]);
@ -542,15 +551,19 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int
if (a->z == b->z && a->z == slice_z) { if (a->z == b->z && a->z == slice_z) {
// edge is horizontal and belongs to the current layer // edge is horizontal and belongs to the current layer
/* We assume that this method is never being called for horizontal stl_vertex &v0 = this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[0] ];
facets, so no other edge is going to be on this layer. */ stl_vertex &v1 = this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[1] ];
stl_vertex* v0 = &this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[0] ]; stl_vertex &v2 = this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[2] ];
stl_vertex* v1 = &this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[1] ];
stl_vertex* v2 = &this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[2] ];
IntersectionLine line; IntersectionLine line;
if (min_z == max_z) { if (min_z == max_z) {
line.edge_type = feHorizontal; line.edge_type = feHorizontal;
} else if (v0->z < slice_z || v1->z < slice_z || v2->z < slice_z) { if (this->mesh->stl.facet_start[facet_idx].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 if (v0.z < slice_z || v1.z < slice_z || v2.z < slice_z) {
line.edge_type = feTop; line.edge_type = feTop;
std::swap(a, b); std::swap(a, b);
std::swap(a_id, b_id); std::swap(a_id, b_id);
@ -624,12 +637,9 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int
void void
TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops) TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops)
{ {
/* /*
SVG svg("lines.svg"); SVG svg("lines.svg");
for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++line) { svg.draw(lines);
svg.AddLine(*line);
}
svg.Close(); svg.Close();
*/ */
@ -837,9 +847,9 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices)
of the loops, since the Orientation() function provided by Clipper of the loops, since the Orientation() function provided by Clipper
would do the same, thus repeating the calculation */ would do the same, thus repeating the calculation */
Polygons::const_iterator loop = loops.begin() + *loop_idx; Polygons::const_iterator loop = loops.begin() + *loop_idx;
if (area[*loop_idx] >= 0) { if (area[*loop_idx] > +EPSILON) {
p_slices.push_back(*loop); p_slices.push_back(*loop);
} else { } else if (area[*loop_idx] < -EPSILON) {
diff(p_slices, *loop, &p_slices); diff(p_slices, *loop, &p_slices);
} }
} }

View File

@ -4,7 +4,7 @@ use strict;
use warnings; use warnings;
use Slic3r::XS; use Slic3r::XS;
use Test::More tests => 46; use Test::More tests => 49;
is Slic3r::TriangleMesh::hello_world(), 'Hello world!', is Slic3r::TriangleMesh::hello_world(), 'Hello world!',
'hello world'; 'hello world';
@ -79,7 +79,7 @@ my $cube = {
my $m = Slic3r::TriangleMesh->new; my $m = Slic3r::TriangleMesh->new;
$m->ReadFromPerl($cube->{vertices}, $cube->{facets}); $m->ReadFromPerl($cube->{vertices}, $cube->{facets});
$m->repair; $m->repair;
my @z = (2,4,8,6,8,10,12,14,16,18,20); my @z = (0,2,4,8,6,8,10,12,14,16,18,20);
my $result = $m->slice(\@z); my $result = $m->slice(\@z);
my $SCALING_FACTOR = 0.000001; my $SCALING_FACTOR = 0.000001;
for my $i (0..$#z) { for my $i (0..$#z) {
@ -95,8 +95,19 @@ my $cube = {
[ [0,1,2],[2,1,3],[1,0,4],[5,1,4],[0,2,4],[4,2,6],[7,6,8],[4,6,7],[9,4,7],[7,8,10],[2,3,6],[11,3,12],[7,12,9],[13,12,7],[6,3,11],[11,12,13],[3,1,5],[12,3,5],[5,4,9],[12,5,9],[13,7,10],[14,13,10],[8,15,10],[10,15,14],[6,11,8],[8,11,15],[15,11,13],[14,15,13] ], [ [0,1,2],[2,1,3],[1,0,4],[5,1,4],[0,2,4],[4,2,6],[7,6,8],[4,6,7],[9,4,7],[7,8,10],[2,3,6],[11,3,12],[7,12,9],[13,12,7],[6,3,11],[11,12,13],[3,1,5],[12,3,5],[5,4,9],[12,5,9],[13,7,10],[14,13,10],[8,15,10],[10,15,14],[6,11,8],[8,11,15],[15,11,13],[14,15,13] ],
); );
$m->repair; $m->repair;
my $slices = $m->slice([ 5, 10 ]); {
is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a tangent plane includes its area'; # at Z = 10 we have a top horizontal surface
my $slices = $m->slice([ 5, 10 ]);
is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a top tangent plane includes its area';
}
$m->flip_z;
{
# this second test also checks that performing a second slice on a mesh after
# a transformation works properly (shared_vertices is correctly invalidated);
# at Z = -10 we have a bottom horizontal surface
my $slices = $m->slice([ -5, -10 ]);
is $slices->[0][0]->area, $slices->[1][0]->area, 'slicing a bottom tangent plane includes its area';
}
} }
{ {