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:
parent
8a5a0b6726
commit
b1f1893481
@ -32,9 +32,17 @@ SVG::draw(const Line &line, std::string stroke)
|
||||
}
|
||||
|
||||
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
|
||||
@ -50,11 +58,25 @@ SVG::draw(const ExPolygon &expolygon, std::string fill)
|
||||
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
|
||||
SVG::draw(const Polygon &polygon, std::string 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
|
||||
@ -64,6 +86,13 @@ SVG::draw(const Polyline &polyline, std::string stroke)
|
||||
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
|
||||
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());
|
||||
}
|
||||
|
||||
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
|
||||
SVG::path(const std::string &d, bool fill)
|
||||
{
|
||||
|
@ -15,12 +15,17 @@ class SVG
|
||||
std::string fill, stroke;
|
||||
|
||||
SVG(const char* filename);
|
||||
void AddLine(const IntersectionLine &line);
|
||||
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 ExPolygons &expolygons, 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 Polylines &polylines, std::string stroke = "black");
|
||||
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();
|
||||
|
||||
private:
|
||||
|
@ -181,6 +181,7 @@ TriangleMesh::WriteOBJFile(char* output_file) {
|
||||
void TriangleMesh::scale(float factor)
|
||||
{
|
||||
stl_scale(&(this->stl), factor);
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
}
|
||||
|
||||
void TriangleMesh::scale(const Pointf3 &versor)
|
||||
@ -190,41 +191,49 @@ void TriangleMesh::scale(const Pointf3 &versor)
|
||||
fversor[1] = versor.y;
|
||||
fversor[2] = versor.z;
|
||||
stl_scale_versor(&this->stl, fversor);
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
}
|
||||
|
||||
void TriangleMesh::translate(float x, float y, float z)
|
||||
{
|
||||
stl_translate_relative(&(this->stl), x, y, z);
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
}
|
||||
|
||||
void TriangleMesh::rotate_x(float angle)
|
||||
{
|
||||
stl_rotate_x(&(this->stl), angle);
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
}
|
||||
|
||||
void TriangleMesh::rotate_y(float angle)
|
||||
{
|
||||
stl_rotate_y(&(this->stl), angle);
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
}
|
||||
|
||||
void TriangleMesh::rotate_z(float angle)
|
||||
{
|
||||
stl_rotate_z(&(this->stl), angle);
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
}
|
||||
|
||||
void TriangleMesh::flip_x()
|
||||
{
|
||||
stl_mirror_yz(&this->stl);
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
}
|
||||
|
||||
void TriangleMesh::flip_y()
|
||||
{
|
||||
stl_mirror_xz(&this->stl);
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
}
|
||||
|
||||
void TriangleMesh::flip_z()
|
||||
{
|
||||
stl_mirror_xy(&this->stl);
|
||||
stl_invalidate_shared_vertices(&this->stl);
|
||||
}
|
||||
|
||||
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) {
|
||||
#ifdef SLIC3R_DEBUG
|
||||
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
|
||||
|
||||
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) {
|
||||
// edge is horizontal and belongs to the current layer
|
||||
|
||||
/* We assume that this method is never being called for horizontal
|
||||
facets, so no other edge is going to be on this layer. */
|
||||
stl_vertex* v0 = &this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[0] ];
|
||||
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] ];
|
||||
stl_vertex &v0 = this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[0] ];
|
||||
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;
|
||||
if (min_z == max_z) {
|
||||
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;
|
||||
std::swap(a, b);
|
||||
std::swap(a_id, b_id);
|
||||
@ -624,12 +637,9 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int
|
||||
void
|
||||
TriangleMeshSlicer::make_loops(std::vector<IntersectionLine> &lines, Polygons* loops)
|
||||
{
|
||||
|
||||
/*
|
||||
SVG svg("lines.svg");
|
||||
for (IntersectionLines::iterator line = lines.begin(); line != lines.end(); ++line) {
|
||||
svg.AddLine(*line);
|
||||
}
|
||||
svg.draw(lines);
|
||||
svg.Close();
|
||||
*/
|
||||
|
||||
@ -837,9 +847,9 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices)
|
||||
of the loops, since the Orientation() function provided by Clipper
|
||||
would do the same, thus repeating the calculation */
|
||||
Polygons::const_iterator loop = loops.begin() + *loop_idx;
|
||||
if (area[*loop_idx] >= 0) {
|
||||
if (area[*loop_idx] > +EPSILON) {
|
||||
p_slices.push_back(*loop);
|
||||
} else {
|
||||
} else if (area[*loop_idx] < -EPSILON) {
|
||||
diff(p_slices, *loop, &p_slices);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use strict;
|
||||
use warnings;
|
||||
|
||||
use Slic3r::XS;
|
||||
use Test::More tests => 46;
|
||||
use Test::More tests => 49;
|
||||
|
||||
is Slic3r::TriangleMesh::hello_world(), 'Hello world!',
|
||||
'hello world';
|
||||
@ -79,7 +79,7 @@ my $cube = {
|
||||
my $m = Slic3r::TriangleMesh->new;
|
||||
$m->ReadFromPerl($cube->{vertices}, $cube->{facets});
|
||||
$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 $SCALING_FACTOR = 0.000001;
|
||||
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] ],
|
||||
);
|
||||
$m->repair;
|
||||
{
|
||||
# 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 tangent plane includes its area';
|
||||
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';
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user