Ported Infill unit tests from Perl to C++.
This commit is contained in:
parent
d88e9634f5
commit
33b2478b69
@ -10,6 +10,7 @@
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include "libslic3r.h"
|
||||
#include "clonable_ptr.hpp"
|
||||
@ -1989,6 +1990,7 @@ public:
|
||||
struct SetDeserializeItem {
|
||||
SetDeserializeItem(const char *opt_key, const char *opt_value, bool append = false) : opt_key(opt_key), opt_value(opt_value), append(append) {}
|
||||
SetDeserializeItem(const std::string &opt_key, const std::string &opt_value, bool append = false) : opt_key(opt_key), opt_value(opt_value), append(append) {}
|
||||
SetDeserializeItem(const std::string &opt_key, const std::string_view opt_value, bool append = false) : opt_key(opt_key), opt_value(opt_value), append(append) {}
|
||||
SetDeserializeItem(const char *opt_key, const bool value, bool append = false) : opt_key(opt_key), opt_value(value ? "1" : "0"), append(append) {}
|
||||
SetDeserializeItem(const std::string &opt_key, const bool value, bool append = false) : opt_key(opt_key), opt_value(value ? "1" : "0"), append(append) {}
|
||||
SetDeserializeItem(const char *opt_key, const int value, bool append = false) : opt_key(opt_key), opt_value(std::to_string(value)), append(append) {}
|
||||
|
@ -324,10 +324,10 @@ inline void extrusion_paths_append(ExtrusionPaths &dst, Polylines &&polylines, E
|
||||
polylines.clear();
|
||||
}
|
||||
|
||||
inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
|
||||
inline void extrusion_entities_append_paths(ExtrusionEntitiesPtr &dst, const Polylines &polylines, ExtrusionRole role, double mm3_per_mm, float width, float height)
|
||||
{
|
||||
dst.reserve(dst.size() + polylines.size());
|
||||
for (Polyline &polyline : polylines)
|
||||
for (const Polyline &polyline : polylines)
|
||||
if (polyline.is_valid()) {
|
||||
ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height);
|
||||
dst.push_back(extrusion_path);
|
||||
|
@ -35,6 +35,8 @@ public:
|
||||
float new_Z(const GCodeReader &reader) const { return this->has(Z) ? this->z() : reader.z(); }
|
||||
float new_E(const GCodeReader &reader) const { return this->has(E) ? this->e() : reader.e(); }
|
||||
float new_F(const GCodeReader &reader) const { return this->has(F) ? this->f() : reader.f(); }
|
||||
Point new_XY_scaled(const GCodeReader &reader) const
|
||||
{ return Point::new_scale(this->new_X(reader), this->new_Y(reader)); }
|
||||
float dist_X(const GCodeReader &reader) const { return this->has(X) ? (this->x() - reader.x()) : 0; }
|
||||
float dist_Y(const GCodeReader &reader) const { return this->has(Y) ? (this->y() - reader.y()) : 0; }
|
||||
float dist_Z(const GCodeReader &reader) const { return this->has(Z) ? (this->z() - reader.z()) : 0; }
|
||||
@ -134,6 +136,8 @@ public:
|
||||
float e() const { return m_position[E]; }
|
||||
float& f() { return m_position[F]; }
|
||||
float f() const { return m_position[F]; }
|
||||
Point xy_scaled() const { return Point::new_scale(this->x(), this->y()); }
|
||||
|
||||
|
||||
// Returns 0 for gcfNoExtrusion.
|
||||
char extrusion_axis() const { return m_extrusion_axis; }
|
||||
|
318
t/fill.t
318
t/fill.t
@ -1,318 +0,0 @@
|
||||
use Test::More;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
#plan tests => 43;
|
||||
# Test of a 100% coverage is off.
|
||||
plan tests => 19;
|
||||
|
||||
BEGIN {
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
use local::lib "$FindBin::Bin/../local-lib";
|
||||
}
|
||||
|
||||
use List::Util qw(first sum);
|
||||
use Slic3r;
|
||||
use Slic3r::Geometry qw(X Y scale unscale convex_hull);
|
||||
use Slic3r::Geometry::Clipper qw(union diff diff_ex offset offset2_ex);
|
||||
use Slic3r::Surface qw(:types);
|
||||
use Slic3r::Test;
|
||||
|
||||
sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
|
||||
|
||||
{
|
||||
my $expolygon = Slic3r::ExPolygon->new([ scale_points [0,0], [50,0], [50,50], [0,50] ]);
|
||||
my $filler = Slic3r::Filler->new_from_type('rectilinear');
|
||||
$filler->set_bounding_box($expolygon->bounding_box);
|
||||
$filler->set_angle(0);
|
||||
my $surface = Slic3r::Surface->new(
|
||||
surface_type => S_TYPE_TOP,
|
||||
expolygon => $expolygon,
|
||||
);
|
||||
my $flow = Slic3r::Flow->new(
|
||||
width => 0.69,
|
||||
height => 0.4,
|
||||
nozzle_diameter => 0.50,
|
||||
);
|
||||
$filler->set_spacing($flow->spacing);
|
||||
foreach my $angle (0, 45) {
|
||||
$surface->expolygon->rotate(Slic3r::Geometry::deg2rad($angle), [0,0]);
|
||||
my $paths = $filler->fill_surface($surface, layer_height => 0.4, density => 0.4);
|
||||
is scalar @$paths, 1, 'one continuous path';
|
||||
}
|
||||
}
|
||||
|
||||
SKIP:
|
||||
{
|
||||
skip "The FillRectilinear2 does not fill the surface completely", 1;
|
||||
|
||||
my $test = sub {
|
||||
my ($expolygon, $flow_spacing, $angle, $density) = @_;
|
||||
|
||||
my $filler = Slic3r::Filler->new_from_type('rectilinear');
|
||||
$filler->set_bounding_box($expolygon->bounding_box);
|
||||
$filler->set_angle($angle // 0);
|
||||
# Adjust line spacing to fill the region.
|
||||
$filler->set_dont_adjust(0);
|
||||
$filler->set_link_max_length(scale(1.2*$flow_spacing));
|
||||
my $surface = Slic3r::Surface->new(
|
||||
surface_type => S_TYPE_BOTTOM,
|
||||
expolygon => $expolygon,
|
||||
);
|
||||
my $flow = Slic3r::Flow->new(
|
||||
width => $flow_spacing,
|
||||
height => 0.4,
|
||||
nozzle_diameter => $flow_spacing,
|
||||
);
|
||||
$filler->set_spacing($flow->spacing);
|
||||
my $paths = $filler->fill_surface(
|
||||
$surface,
|
||||
layer_height => $flow->height,
|
||||
density => $density // 1,
|
||||
);
|
||||
|
||||
# check whether any part was left uncovered
|
||||
my @grown_paths = map @{Slic3r::Polyline->new(@$_)->grow(scale $filler->spacing/2)}, @$paths;
|
||||
my $uncovered = diff_ex([ @$expolygon ], [ @grown_paths ], 1);
|
||||
|
||||
# ignore very small dots
|
||||
my $uncovered_filtered = [ grep $_->area > (scale $flow_spacing)**2, @$uncovered ];
|
||||
|
||||
is scalar(@$uncovered_filtered), 0, 'solid surface is fully filled';
|
||||
|
||||
if (0 && @$uncovered_filtered) {
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output("uncovered.svg",
|
||||
no_arrows => 1,
|
||||
expolygons => [ $expolygon ],
|
||||
blue_expolygons => [ @$uncovered ],
|
||||
red_expolygons => [ @$uncovered_filtered ],
|
||||
polylines => [ @$paths ],
|
||||
);
|
||||
exit;
|
||||
}
|
||||
};
|
||||
|
||||
my $expolygon = Slic3r::ExPolygon->new([
|
||||
[6883102, 9598327.01296997],
|
||||
[6883102, 20327272.01297],
|
||||
[3116896, 20327272.01297],
|
||||
[3116896, 9598327.01296997],
|
||||
]);
|
||||
$test->($expolygon, 0.55);
|
||||
|
||||
for (1..20) {
|
||||
$expolygon->scale(1.05);
|
||||
$test->($expolygon, 0.55);
|
||||
}
|
||||
|
||||
$expolygon = Slic3r::ExPolygon->new(
|
||||
[[59515297,5422499],[59531249,5578697],[59695801,6123186],[59965713,6630228],[60328214,7070685],[60773285,7434379],[61274561,7702115],[61819378,7866770],[62390306,7924789],[62958700,7866744],[63503012,7702244],[64007365,7434357],[64449960,7070398],[64809327,6634999],[65082143,6123325],[65245005,5584454],[65266967,5422499],[66267307,5422499],[66269190,8310081],[66275379,17810072],[66277259,20697500],[65267237,20697500],[65245004,20533538],[65082082,19994444],[64811462,19488579],[64450624,19048208],[64012101,18686514],[63503122,18415781],[62959151,18251378],[62453416,18198442],[62390147,18197355],[62200087,18200576],[61813519,18252990],[61274433,18415918],[60768598,18686517],[60327567,19047892],[59963609,19493297],[59695865,19994587],[59531222,20539379],[59515153,20697500],[58502480,20697500],[58502480,5422499]]
|
||||
);
|
||||
$test->($expolygon, 0.524341649025257);
|
||||
|
||||
$expolygon = Slic3r::ExPolygon->new([ scale_points [0,0], [98,0], [98,10], [0,10] ]);
|
||||
$test->($expolygon, 0.5, 45, 0.99); # non-solid infill
|
||||
}
|
||||
|
||||
{
|
||||
my $collection = Slic3r::Polyline::Collection->new(
|
||||
Slic3r::Polyline->new([0,15], [0,18], [0,20]),
|
||||
Slic3r::Polyline->new([0,10], [0,8], [0,5]),
|
||||
);
|
||||
is_deeply
|
||||
[ map $_->[Y], map @$_, @{$collection->chained_path_from(Slic3r::Point->new(0,30), 0)} ],
|
||||
[20, 18, 15, 10, 8, 5],
|
||||
'chained path';
|
||||
}
|
||||
|
||||
{
|
||||
my $collection = Slic3r::Polyline::Collection->new(
|
||||
Slic3r::Polyline->new([4,0], [10,0], [15,0]),
|
||||
Slic3r::Polyline->new([10,5], [15,5], [20,5]),
|
||||
);
|
||||
is_deeply
|
||||
[ map $_->[X], map @$_, @{$collection->chained_path_from(Slic3r::Point->new(30,0), 0)} ],
|
||||
[reverse 4, 10, 15, 10, 15, 20],
|
||||
'chained path';
|
||||
}
|
||||
|
||||
{
|
||||
my $collection = Slic3r::ExtrusionPath::Collection->new(
|
||||
map Slic3r::ExtrusionPath->new(polyline => $_, role => 0, mm3_per_mm => 1),
|
||||
Slic3r::Polyline->new([0,15], [0,18], [0,20]),
|
||||
Slic3r::Polyline->new([0,10], [0,8], [0,5]),
|
||||
);
|
||||
is_deeply
|
||||
[ map $_->[Y], map @{$_->polyline}, @{$collection->chained_path_from(Slic3r::Point->new(0,30), 0)} ],
|
||||
[20, 18, 15, 10, 8, 5],
|
||||
'chained path';
|
||||
}
|
||||
|
||||
{
|
||||
my $collection = Slic3r::ExtrusionPath::Collection->new(
|
||||
map Slic3r::ExtrusionPath->new(polyline => $_, role => 0, mm3_per_mm => 1),
|
||||
Slic3r::Polyline->new([15,0], [10,0], [4,0]),
|
||||
Slic3r::Polyline->new([10,5], [15,5], [20,5]),
|
||||
);
|
||||
is_deeply
|
||||
[ map $_->[X], map @{$_->polyline}, @{$collection->chained_path_from(Slic3r::Point->new(30,0), 0)} ],
|
||||
[reverse 4, 10, 15, 10, 15, 20],
|
||||
'chained path';
|
||||
}
|
||||
|
||||
for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
|
||||
my $config = Slic3r::Config::new_from_defaults;
|
||||
$config->set('nozzle_diameter', [0.4,0.4,0.4,0.4]);
|
||||
$config->set('fill_pattern', $pattern);
|
||||
$config->set('top_fill_pattern', $pattern);
|
||||
$config->set('bottom_fill_pattern', $pattern);
|
||||
$config->set('perimeters', 1);
|
||||
$config->set('skirts', 0);
|
||||
$config->set('fill_density', 20);
|
||||
$config->set('layer_height', 0.05);
|
||||
$config->set('perimeter_extruder', 1);
|
||||
$config->set('infill_extruder', 2);
|
||||
my $print = Slic3r::Test::init_print('20mm_cube', config => $config, scale => 2);
|
||||
ok my $gcode = Slic3r::Test::gcode($print), "successful $pattern infill generation";
|
||||
my $tool = undef;
|
||||
my @perimeter_points = my @infill_points = ();
|
||||
Slic3r::GCode::Reader->new->parse($gcode, sub {
|
||||
my ($self, $cmd, $args, $info) = @_;
|
||||
|
||||
if ($cmd =~ /^T(\d+)/) {
|
||||
$tool = $1;
|
||||
} elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
|
||||
if ($tool == $config->perimeter_extruder-1) {
|
||||
push @perimeter_points, Slic3r::Point->new_scale($args->{X}, $args->{Y});
|
||||
} elsif ($tool == $config->infill_extruder-1) {
|
||||
push @infill_points, Slic3r::Point->new_scale($args->{X}, $args->{Y});
|
||||
}
|
||||
}
|
||||
});
|
||||
my $convex_hull = convex_hull(\@perimeter_points);
|
||||
ok !(defined first { !$convex_hull->contains_point($_) } @infill_points), "infill does not exceed perimeters ($pattern)";
|
||||
}
|
||||
|
||||
{
|
||||
my $config = Slic3r::Config::new_from_defaults;
|
||||
$config->set('nozzle_diameter', [0.4,0.4,0.4,0.4]);
|
||||
$config->set('infill_only_where_needed', 1);
|
||||
$config->set('bottom_solid_layers', 0);
|
||||
$config->set('infill_extruder', 2);
|
||||
$config->set('infill_extrusion_width', 0.5);
|
||||
$config->set('wipe_into_infill', 0);
|
||||
$config->set('fill_density', 40);
|
||||
$config->set('cooling', [ 0 ]); # for preventing speeds from being altered
|
||||
$config->set('first_layer_speed', '100%'); # for preventing speeds from being altered
|
||||
|
||||
my $test = sub {
|
||||
my $print = Slic3r::Test::init_print('pyramid', config => $config);
|
||||
|
||||
my $tool = undef;
|
||||
my @infill_extrusions = (); # array of polylines
|
||||
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
|
||||
my ($self, $cmd, $args, $info) = @_;
|
||||
|
||||
if ($cmd =~ /^T(\d+)/) {
|
||||
$tool = $1;
|
||||
} elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
|
||||
if ($tool == $config->infill_extruder-1) {
|
||||
push @infill_extrusions, Slic3r::Line->new_scale(
|
||||
[ $self->X, $self->Y ],
|
||||
[ $info->{new_X}, $info->{new_Y} ],
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
return 0 if !@infill_extrusions; # prevent calling convex_hull() with no points
|
||||
|
||||
my $convex_hull = convex_hull([ map $_->pp, map @$_, @infill_extrusions ]);
|
||||
return unscale unscale sum(map $_->area, @{offset([$convex_hull], scale(+$config->infill_extrusion_width/2))});
|
||||
};
|
||||
|
||||
my $tolerance = 5; # mm^2
|
||||
|
||||
$config->set('solid_infill_below_area', 0);
|
||||
ok $test->() < $tolerance,
|
||||
'no infill is generated when using infill_only_where_needed on a pyramid';
|
||||
|
||||
$config->set('solid_infill_below_area', 70);
|
||||
ok abs($test->() - $config->solid_infill_below_area) < $tolerance,
|
||||
'infill is only generated under the forced solid shells';
|
||||
}
|
||||
|
||||
{
|
||||
my $config = Slic3r::Config::new_from_defaults;
|
||||
$config->set('skirts', 0);
|
||||
$config->set('perimeters', 1);
|
||||
$config->set('fill_density', 0);
|
||||
$config->set('top_solid_layers', 0);
|
||||
$config->set('bottom_solid_layers', 0);
|
||||
$config->set('solid_infill_below_area', 20000000);
|
||||
$config->set('solid_infill_every_layers', 2);
|
||||
$config->set('perimeter_speed', 99);
|
||||
$config->set('external_perimeter_speed', 99);
|
||||
$config->set('cooling', [ 0 ]);
|
||||
$config->set('first_layer_speed', '100%');
|
||||
|
||||
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
|
||||
my %layers_with_extrusion = ();
|
||||
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
|
||||
my ($self, $cmd, $args, $info) = @_;
|
||||
|
||||
if ($cmd eq 'G1' && $info->{dist_XY} > 0 && $info->{extruding}) {
|
||||
if (($args->{F} // $self->F) != $config->perimeter_speed*60) {
|
||||
$layers_with_extrusion{$self->Z} = ($args->{F} // $self->F);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ok !%layers_with_extrusion,
|
||||
"solid_infill_below_area and solid_infill_every_layers are ignored when fill_density is 0";
|
||||
}
|
||||
|
||||
{
|
||||
my $config = Slic3r::Config::new_from_defaults;
|
||||
$config->set('skirts', 0);
|
||||
$config->set('perimeters', 3);
|
||||
$config->set('fill_density', 0);
|
||||
$config->set('layer_height', 0.2);
|
||||
$config->set('first_layer_height', 0.2);
|
||||
$config->set('nozzle_diameter', [0.35,0.35,0.35,0.35]);
|
||||
$config->set('infill_extruder', 2);
|
||||
$config->set('solid_infill_extruder', 2);
|
||||
$config->set('infill_extrusion_width', 0.52);
|
||||
$config->set('solid_infill_extrusion_width', 0.52);
|
||||
$config->set('first_layer_extrusion_width', 0);
|
||||
|
||||
my $print = Slic3r::Test::init_print('A', config => $config);
|
||||
my %infill = (); # Z => [ Line, Line ... ]
|
||||
my $tool = undef;
|
||||
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
|
||||
my ($self, $cmd, $args, $info) = @_;
|
||||
|
||||
if ($cmd =~ /^T(\d+)/) {
|
||||
$tool = $1;
|
||||
} elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
|
||||
if ($tool == $config->infill_extruder-1) {
|
||||
my $z = 1 * $self->Z;
|
||||
$infill{$z} ||= [];
|
||||
push @{$infill{$z}}, Slic3r::Line->new_scale(
|
||||
[ $self->X, $self->Y ],
|
||||
[ $info->{new_X}, $info->{new_Y} ],
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
my $grow_d = scale($config->infill_extrusion_width)/2;
|
||||
my $layer0_infill = union([ map @{$_->grow($grow_d)}, @{ $infill{0.2} } ]);
|
||||
my $layer1_infill = union([ map @{$_->grow($grow_d)}, @{ $infill{0.4} } ]);
|
||||
my $diff = diff($layer0_infill, $layer1_infill);
|
||||
$diff = offset2_ex($diff, -$grow_d, +$grow_d);
|
||||
$diff = [ grep { $_->area > 2*(($grow_d*2)**2) } @$diff ];
|
||||
is scalar(@$diff), 0, 'no missing parts in solid shell when fill_density is 0';
|
||||
}
|
||||
|
||||
__END__
|
@ -187,6 +187,19 @@ TriangleMesh mesh(TestMesh m)
|
||||
return mesh;
|
||||
}
|
||||
|
||||
TriangleMesh mesh(TestMesh min, Vec3d translate, Vec3d scale)
|
||||
{
|
||||
TriangleMesh m = mesh(min);
|
||||
m.translate(translate.cast<float>());
|
||||
m.scale(scale.cast<float>());
|
||||
return m;
|
||||
}
|
||||
|
||||
TriangleMesh mesh(TestMesh m, Vec3d translate, double scale)
|
||||
{
|
||||
return mesh(m, translate, Vec3d(scale, scale, scale));
|
||||
}
|
||||
|
||||
static bool verbose_gcode()
|
||||
{
|
||||
const char *v = std::getenv("SLIC3R_TESTS_GCODE");
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "libslic3r/ExtrusionEntityCollection.hpp"
|
||||
#include "libslic3r/ExtrusionEntity.hpp"
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/ShortestPath.hpp"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
|
||||
#include "test_data.hpp"
|
||||
@ -83,3 +84,60 @@ SCENARIO("ExtrusionEntityCollection: Polygon flattening", "[ExtrusionEntity]") {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("ExtrusionEntityCollection: Chained path", "[ExtrusionEntity]") {
|
||||
struct Test {
|
||||
Polylines unchained;
|
||||
Polylines chained;
|
||||
Point initial_point;
|
||||
};
|
||||
std::vector<Test> tests {
|
||||
{
|
||||
{
|
||||
{ {0,15}, {0,18}, {0,20} },
|
||||
{ {0,10}, {0,8}, {0,5} }
|
||||
},
|
||||
{
|
||||
{ {0,20}, {0,18}, {0,15} },
|
||||
{ {0,10}, {0,8}, {0,5} }
|
||||
},
|
||||
{ 0,30 }
|
||||
},
|
||||
{
|
||||
{
|
||||
{ {4,0}, {10,0}, {15,0} },
|
||||
{ {10,5}, {15,5}, {20,5} }
|
||||
},
|
||||
{
|
||||
{ {20,5}, {15,5}, {10,5} },
|
||||
{ {15,0}, {10,0}, {4,0} }
|
||||
},
|
||||
{ 30,0 }
|
||||
},
|
||||
{
|
||||
{
|
||||
{ {15,0}, {10,0}, {4,0} },
|
||||
{ {10,5}, {15,5}, {20,5} }
|
||||
},
|
||||
{
|
||||
{ {20,5}, {15,5}, {10,5} },
|
||||
{ {15,0}, {10,0}, {4,0} }
|
||||
},
|
||||
{ 30,0 }
|
||||
},
|
||||
};
|
||||
for (const Test &test : tests) {
|
||||
Polylines chained = chain_polylines(test.unchained, &test.initial_point);
|
||||
REQUIRE(chained == test.chained);
|
||||
ExtrusionEntityCollection unchained_extrusions;
|
||||
extrusion_entities_append_paths(unchained_extrusions.entities, test.unchained,
|
||||
erInternalInfill, 0., 0.4f, 0.3f);
|
||||
ExtrusionEntityCollection chained_extrusions = unchained_extrusions.chained_path_from(test.initial_point);
|
||||
REQUIRE(chained_extrusions.entities.size() == test.chained.size());
|
||||
for (size_t i = 0; i < chained_extrusions.entities.size(); ++ i) {
|
||||
const Points &p1 = test.chained[i].points;
|
||||
const Points &p2 = dynamic_cast<const ExtrusionPath*>(chained_extrusions.entities[i])->polyline.points;
|
||||
REQUIRE(p1 == p2);
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
#include "libslic3r/Fill/Fill.hpp"
|
||||
#include "libslic3r/Flow.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/Geometry/ConvexHull.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/SVG.hpp"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
@ -14,6 +15,7 @@
|
||||
#include "test_data.hpp"
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace std::literals;
|
||||
|
||||
bool test_if_solid_surface_filled(const ExPolygon& expolygon, double flow_spacing, double angle = 0, double density = 1.0);
|
||||
|
||||
@ -43,7 +45,7 @@ TEST_CASE("Fill: Pattern Path Length", "[Fill]") {
|
||||
SECTION("Square") {
|
||||
Slic3r::Points test_set;
|
||||
test_set.reserve(4);
|
||||
std::vector<Vec2d> points {Vec2d(0,0), Vec2d(100,0), Vec2d(100,100), Vec2d(0,100)};
|
||||
std::vector<Vec2d> points { {0,0}, {100,0}, {100,100}, {0,100} };
|
||||
for (size_t i = 0; i < 4; ++i) {
|
||||
std::transform(points.cbegin()+i, points.cend(), std::back_inserter(test_set), [] (const Vec2d& a) -> Point { return Point::new_scale(a.x(), a.y()); } );
|
||||
std::transform(points.cbegin(), points.cbegin()+i, std::back_inserter(test_set), [] (const Vec2d& a) -> Point { return Point::new_scale(a.x(), a.y()); } );
|
||||
@ -118,26 +120,28 @@ TEST_CASE("Fill: Pattern Path Length", "[Fill]") {
|
||||
REQUIRE(std::abs(paths[0].length() - static_cast<double>(scale_(3*100 + 2*50))) - SCALED_EPSILON > 0); // path has expected length
|
||||
}
|
||||
|
||||
SECTION("Rotated Square") {
|
||||
Slic3r::Points square { Point::new_scale(0,0), Point::new_scale(50,0), Point::new_scale(50,50), Point::new_scale(0,50)};
|
||||
Slic3r::ExPolygon expolygon(square);
|
||||
SECTION("Rotated Square produces one continuous path") {
|
||||
Slic3r::ExPolygon expolygon(Polygon::new_scale({ {0, 0}, {50, 0}, {50, 50}, {0, 50} }));
|
||||
std::unique_ptr<Slic3r::Fill> filler(Slic3r::Fill::new_from_type("rectilinear"));
|
||||
filler->bounding_box = get_extents(expolygon.contour);
|
||||
filler->bounding_box = get_extents(expolygon);
|
||||
filler->angle = 0;
|
||||
|
||||
Surface surface(stTop, expolygon);
|
||||
auto flow = Slic3r::Flow(0.69f, 0.4f, 0.50f);
|
||||
// width, height, nozzle_dmr
|
||||
auto flow = Slic3r::Flow(0.69f, 0.4f, 0.5f);
|
||||
|
||||
FillParams fill_params;
|
||||
fill_params.density = 1.0;
|
||||
for (auto density : { 0.4, 1.0 }) {
|
||||
fill_params.density = density;
|
||||
filler->spacing = flow.spacing();
|
||||
|
||||
for (auto angle : { 0.0, 45.0}) {
|
||||
surface.expolygon.rotate(angle, Point(0,0));
|
||||
Polylines paths = filler->fill_surface(&surface, fill_params);
|
||||
// one continuous path
|
||||
REQUIRE(paths.size() == 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0 // Disabled temporarily due to precission issues on the Mac VM
|
||||
SECTION("Solid surface fill") {
|
||||
@ -190,202 +194,226 @@ TEST_CASE("Fill: Pattern Path Length", "[Fill]") {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
SCENARIO("Infill does not exceed perimeters", "[Fill]")
|
||||
{
|
||||
my $collection = Slic3r::Polyline::Collection->new(
|
||||
Slic3r::Polyline->new([0,15], [0,18], [0,20]),
|
||||
Slic3r::Polyline->new([0,10], [0,8], [0,5]),
|
||||
);
|
||||
is_deeply
|
||||
[ map $_->[Y], map @$_, @{$collection->chained_path_from(Slic3r::Point->new(0,30), 0)} ],
|
||||
[20, 18, 15, 10, 8, 5],
|
||||
'chained path';
|
||||
}
|
||||
auto test = [](const std::string_view pattern) {
|
||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({
|
||||
{ "nozzle_diameter", "0.4, 0.4, 0.4, 0.4" },
|
||||
{ "fill_pattern", pattern },
|
||||
{ "top_fill_pattern", pattern },
|
||||
{ "bottom_fill_pattern", pattern },
|
||||
{ "perimeters", 1 },
|
||||
{ "skirts", 0 },
|
||||
{ "fill_density", 0.2 },
|
||||
{ "layer_height", 0.05 },
|
||||
{ "perimeter_extruder", 1 },
|
||||
{ "infill_extruder", 2 }
|
||||
});
|
||||
|
||||
WHEN("40mm cube sliced") {
|
||||
std::string gcode = Slic3r::Test::slice({ mesh(Slic3r::Test::TestMesh::cube_20x20x20, Vec3d::Zero(), 2.0) }, config);
|
||||
THEN("gcode not empty") {
|
||||
REQUIRE(! gcode.empty());
|
||||
}
|
||||
THEN("infill does not exceed perimeters") {
|
||||
GCodeReader parser;
|
||||
const int perimeter_extruder = config.opt_int("perimeter_extruder");
|
||||
const int infill_extruder = config.opt_int("infill_extruder");
|
||||
int tool = -1;
|
||||
Points perimeter_points;
|
||||
Points infill_points;
|
||||
parser.parse_buffer(gcode, [&tool, &perimeter_points, &infill_points, perimeter_extruder, infill_extruder]
|
||||
(Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line)
|
||||
{
|
||||
my $collection = Slic3r::Polyline::Collection->new(
|
||||
Slic3r::Polyline->new([4,0], [10,0], [15,0]),
|
||||
Slic3r::Polyline->new([10,5], [15,5], [20,5]),
|
||||
);
|
||||
is_deeply
|
||||
[ map $_->[X], map @$_, @{$collection->chained_path_from(Slic3r::Point->new(30,0), 0)} ],
|
||||
[reverse 4, 10, 15, 10, 15, 20],
|
||||
'chained path';
|
||||
}
|
||||
|
||||
{
|
||||
my $collection = Slic3r::ExtrusionPath::Collection->new(
|
||||
map Slic3r::ExtrusionPath->new(polyline => $_, role => 0, mm3_per_mm => 1),
|
||||
Slic3r::Polyline->new([0,15], [0,18], [0,20]),
|
||||
Slic3r::Polyline->new([0,10], [0,8], [0,5]),
|
||||
);
|
||||
is_deeply
|
||||
[ map $_->[Y], map @{$_->polyline}, @{$collection->chained_path_from(Slic3r::Point->new(0,30), 0)} ],
|
||||
[20, 18, 15, 10, 8, 5],
|
||||
'chained path';
|
||||
}
|
||||
|
||||
{
|
||||
my $collection = Slic3r::ExtrusionPath::Collection->new(
|
||||
map Slic3r::ExtrusionPath->new(polyline => $_, role => 0, mm3_per_mm => 1),
|
||||
Slic3r::Polyline->new([15,0], [10,0], [4,0]),
|
||||
Slic3r::Polyline->new([10,5], [15,5], [20,5]),
|
||||
);
|
||||
is_deeply
|
||||
[ map $_->[X], map @{$_->polyline}, @{$collection->chained_path_from(Slic3r::Point->new(30,0), 0)} ],
|
||||
[reverse 4, 10, 15, 10, 15, 20],
|
||||
'chained path';
|
||||
}
|
||||
|
||||
for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
|
||||
my $config = Slic3r::Config->new_from_defaults;
|
||||
$config->set('fill_pattern', $pattern);
|
||||
$config->set('external_fill_pattern', $pattern);
|
||||
$config->set('perimeters', 1);
|
||||
$config->set('skirts', 0);
|
||||
$config->set('fill_density', 20);
|
||||
$config->set('layer_height', 0.05);
|
||||
$config->set('perimeter_extruder', 1);
|
||||
$config->set('infill_extruder', 2);
|
||||
my $print = Slic3r::Test::init_print('20mm_cube', config => $config, scale => 2);
|
||||
ok my $gcode = Slic3r::Test::gcode($print), "successful $pattern infill generation";
|
||||
my $tool = undef;
|
||||
my @perimeter_points = my @infill_points = ();
|
||||
Slic3r::GCode::Reader->new->parse($gcode, sub {
|
||||
my ($self, $cmd, $args, $info) = @_;
|
||||
|
||||
if ($cmd =~ /^T(\d+)/) {
|
||||
$tool = $1;
|
||||
} elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
|
||||
if ($tool == $config->perimeter_extruder-1) {
|
||||
push @perimeter_points, Slic3r::Point->new_scale($args->{X}, $args->{Y});
|
||||
} elsif ($tool == $config->infill_extruder-1) {
|
||||
push @infill_points, Slic3r::Point->new_scale($args->{X}, $args->{Y});
|
||||
}
|
||||
// if the command is a T command, set the the current tool
|
||||
if (boost::starts_with(line.cmd(), "T")) {
|
||||
tool = atoi(line.cmd().data() + 1) + 1;
|
||||
} else if (line.cmd() == "G1" && line.extruding(self) && line.dist_XY(self) > 0) {
|
||||
if (tool == perimeter_extruder)
|
||||
perimeter_points.emplace_back(line.new_XY_scaled(self));
|
||||
else if (tool == infill_extruder)
|
||||
infill_points.emplace_back(line.new_XY_scaled(self));
|
||||
}
|
||||
});
|
||||
my $convex_hull = convex_hull(\@perimeter_points);
|
||||
ok !(defined first { !$convex_hull->contains_point($_) } @infill_points), "infill does not exceed perimeters ($pattern)";
|
||||
}
|
||||
|
||||
{
|
||||
my $config = Slic3r::Config->new_from_defaults;
|
||||
$config->set('infill_only_where_needed', 1);
|
||||
$config->set('bottom_solid_layers', 0);
|
||||
$config->set('infill_extruder', 2);
|
||||
$config->set('infill_extrusion_width', 0.5);
|
||||
$config->set('fill_density', 40);
|
||||
$config->set('cooling', 0); # for preventing speeds from being altered
|
||||
$config->set('first_layer_speed', '100%'); # for preventing speeds from being altered
|
||||
|
||||
my $test = sub {
|
||||
my $print = Slic3r::Test::init_print('pyramid', config => $config);
|
||||
|
||||
my $tool = undef;
|
||||
my @infill_extrusions = (); # array of polylines
|
||||
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
|
||||
my ($self, $cmd, $args, $info) = @_;
|
||||
|
||||
if ($cmd =~ /^T(\d+)/) {
|
||||
$tool = $1;
|
||||
} elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
|
||||
if ($tool == $config->infill_extruder-1) {
|
||||
push @infill_extrusions, Slic3r::Line->new_scale(
|
||||
[ $self->X, $self->Y ],
|
||||
[ $info->{new_X}, $info->{new_Y} ],
|
||||
);
|
||||
auto convex_hull = Geometry::convex_hull(perimeter_points);
|
||||
int num_inside = std::count_if(infill_points.begin(), infill_points.end(), [&convex_hull](const Point &pt){ return convex_hull.contains(pt); });
|
||||
REQUIRE(num_inside == infill_points.size());
|
||||
}
|
||||
}
|
||||
});
|
||||
return 0 if !@infill_extrusions; # prevent calling convex_hull() with no points
|
||||
|
||||
my $convex_hull = convex_hull([ map $_->pp, map @$_, @infill_extrusions ]);
|
||||
return unscale unscale sum(map $_->area, @{offset([$convex_hull], scale(+$config->infill_extrusion_width/2))});
|
||||
};
|
||||
|
||||
my $tolerance = 5; # mm^2
|
||||
|
||||
$config->set('solid_infill_below_area', 0);
|
||||
ok $test->() < $tolerance,
|
||||
'no infill is generated when using infill_only_where_needed on a pyramid';
|
||||
|
||||
$config->set('solid_infill_below_area', 70);
|
||||
ok abs($test->() - $config->solid_infill_below_area) < $tolerance,
|
||||
'infill is only generated under the forced solid shells';
|
||||
GIVEN("Rectilinear") { test("rectilinear"sv); }
|
||||
GIVEN("Honeycomb") { test("honeycomb"sv); }
|
||||
GIVEN("HilbertCurve") { test("hilbertcurve"sv); }
|
||||
GIVEN("Concentric") { test("concentric"sv); }
|
||||
}
|
||||
|
||||
SCENARIO("Infill only where needed", "[Fill]")
|
||||
{
|
||||
my $config = Slic3r::Config->new_from_defaults;
|
||||
$config->set('skirts', 0);
|
||||
$config->set('perimeters', 1);
|
||||
$config->set('fill_density', 0);
|
||||
$config->set('top_solid_layers', 0);
|
||||
$config->set('bottom_solid_layers', 0);
|
||||
$config->set('solid_infill_below_area', 20000000);
|
||||
$config->set('solid_infill_every_layers', 2);
|
||||
$config->set('perimeter_speed', 99);
|
||||
$config->set('external_perimeter_speed', 99);
|
||||
$config->set('cooling', 0);
|
||||
$config->set('first_layer_speed', '100%');
|
||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({
|
||||
{ "nozzle_diameter", "0.4, 0.4, 0.4, 0.4" },
|
||||
{ "infill_only_where_needed", true },
|
||||
{ "bottom_solid_layers", 0 },
|
||||
{ "infill_extruder", 2 },
|
||||
{ "infill_extrusion_width", 0.5 },
|
||||
{ "wipe_into_infill", false },
|
||||
{ "fill_density", 0.4 },
|
||||
// for preventing speeds from being altered
|
||||
{ "cooling", "0, 0, 0, 0" },
|
||||
// for preventing speeds from being altered
|
||||
{ "first_layer_speed", "100%" }
|
||||
});
|
||||
|
||||
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
|
||||
my %layers_with_extrusion = ();
|
||||
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
|
||||
my ($self, $cmd, $args, $info) = @_;
|
||||
auto test = [&config]() -> double {
|
||||
std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::pyramid }, config);
|
||||
THEN("gcode not empty") {
|
||||
REQUIRE(! gcode.empty());
|
||||
}
|
||||
|
||||
if ($cmd eq 'G1' && $info->{dist_XY} > 0 && $info->{extruding}) {
|
||||
if (($args->{F} // $self->F) != $config->perimeter_speed*60) {
|
||||
$layers_with_extrusion{$self->Z} = ($args->{F} // $self->F);
|
||||
GCodeReader parser;
|
||||
int tool = -1;
|
||||
const int infill_extruder = config.opt_int("infill_extruder");
|
||||
Points infill_points;
|
||||
parser.parse_buffer(gcode, [&tool, &infill_points, infill_extruder](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line)
|
||||
{
|
||||
// if the command is a T command, set the the current tool
|
||||
if (boost::starts_with(line.cmd(), "T")) {
|
||||
tool = atoi(line.cmd().data() + 1) + 1;
|
||||
} else if (line.cmd() == "G1" && line.extruding(self) && line.dist_XY(self) > 0) {
|
||||
if (tool == infill_extruder) {
|
||||
infill_points.emplace_back(self.xy_scaled());
|
||||
infill_points.emplace_back(line.new_XY_scaled(self));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ok !%layers_with_extrusion,
|
||||
"solid_infill_below_area and solid_infill_every_layers are ignored when fill_density is 0";
|
||||
// prevent calling convex_hull() with no points
|
||||
THEN("infill not empty") {
|
||||
REQUIRE(! infill_points.empty());
|
||||
}
|
||||
|
||||
auto opt_width = config.opt<ConfigOptionFloatOrPercent>("infill_extrusion_width");
|
||||
REQUIRE(! opt_width->percent);
|
||||
Polygons convex_hull = expand(Geometry::convex_hull(infill_points), scaled<float>(opt_width->value / 2));
|
||||
return SCALING_FACTOR * SCALING_FACTOR * std::accumulate(convex_hull.begin(), convex_hull.end(), 0., [](double acc, const Polygon &poly){ return acc + poly.area(); });
|
||||
};
|
||||
|
||||
double tolerance = 5; // mm^2
|
||||
|
||||
GIVEN("solid_infill_below_area == 0") {
|
||||
config.opt_float("solid_infill_below_area") = 0;
|
||||
WHEN("pyramid is sliced ") {
|
||||
auto area = test();
|
||||
THEN("no infill is generated when using infill_only_where_needed on a pyramid") {
|
||||
REQUIRE(area < tolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
GIVEN("solid_infill_below_area == 70") {
|
||||
config.opt_float("solid_infill_below_area") = 70;
|
||||
WHEN("pyramid is sliced ") {
|
||||
auto area = test();
|
||||
THEN("infill is only generated under the forced solid shells") {
|
||||
REQUIRE(std::abs(area - 70) < tolerance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SCENARIO("Infill density zero", "[Fill]")
|
||||
{
|
||||
my $config = Slic3r::Config->new_from_defaults;
|
||||
$config->set('skirts', 0);
|
||||
$config->set('perimeters', 3);
|
||||
$config->set('fill_density', 0);
|
||||
$config->set('layer_height', 0.2);
|
||||
$config->set('first_layer_height', 0.2);
|
||||
$config->set('nozzle_diameter', [0.35]);
|
||||
$config->set('infill_extruder', 2);
|
||||
$config->set('solid_infill_extruder', 2);
|
||||
$config->set('infill_extrusion_width', 0.52);
|
||||
$config->set('solid_infill_extrusion_width', 0.52);
|
||||
$config->set('first_layer_extrusion_width', 0);
|
||||
WHEN("20mm cube is sliced") {
|
||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({
|
||||
{ "skirts", 0 },
|
||||
{ "perimeters", 1 },
|
||||
{ "fill_density", 0 },
|
||||
{ "top_solid_layers", 0 },
|
||||
{ "bottom_solid_layers", 0 },
|
||||
{ "solid_infill_below_area", 20000000 },
|
||||
{ "solid_infill_every_layers", 2 },
|
||||
{ "perimeter_speed", 99 },
|
||||
{ "external_perimeter_speed", 99 },
|
||||
{ "cooling", "0" },
|
||||
{ "first_layer_speed", "100%" }
|
||||
});
|
||||
|
||||
my $print = Slic3r::Test::init_print('A', config => $config);
|
||||
my %infill = (); # Z => [ Line, Line ... ]
|
||||
my $tool = undef;
|
||||
Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub {
|
||||
my ($self, $cmd, $args, $info) = @_;
|
||||
|
||||
if ($cmd =~ /^T(\d+)/) {
|
||||
$tool = $1;
|
||||
} elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) {
|
||||
if ($tool == $config->infill_extruder-1) {
|
||||
my $z = 1 * $self->Z;
|
||||
$infill{$z} ||= [];
|
||||
push @{$infill{$z}}, Slic3r::Line->new_scale(
|
||||
[ $self->X, $self->Y ],
|
||||
[ $info->{new_X}, $info->{new_Y} ],
|
||||
);
|
||||
std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config);
|
||||
THEN("gcode not empty") {
|
||||
REQUIRE(! gcode.empty());
|
||||
}
|
||||
|
||||
THEN("solid_infill_below_area and solid_infill_every_layers are ignored when fill_density is 0") {
|
||||
GCodeReader parser;
|
||||
const double perimeter_speed = config.opt_float("perimeter_speed");
|
||||
std::map<double, double> layers_with_extrusion;
|
||||
parser.parse_buffer(gcode, [&layers_with_extrusion, perimeter_speed](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) {
|
||||
if (line.cmd() == "G1" && line.extruding(self) && line.dist_XY(self) > 0) {
|
||||
double f = line.new_F(self);
|
||||
if (std::abs(f - perimeter_speed * 60.) > 0.01)
|
||||
// It is a perimeter.
|
||||
layers_with_extrusion[self.z()] = f;
|
||||
}
|
||||
});
|
||||
my $grow_d = scale($config->infill_extrusion_width)/2;
|
||||
my $layer0_infill = union([ map @{$_->grow($grow_d)}, @{ $infill{0.2} } ]);
|
||||
my $layer1_infill = union([ map @{$_->grow($grow_d)}, @{ $infill{0.4} } ]);
|
||||
my $diff = diff($layer0_infill, $layer1_infill);
|
||||
$diff = offset2_ex($diff, -$grow_d, +$grow_d);
|
||||
$diff = [ grep { $_->area > 2*(($grow_d*2)**2) } @$diff ];
|
||||
is scalar(@$diff), 0, 'no missing parts in solid shell when fill_density is 0';
|
||||
REQUIRE(layers_with_extrusion.empty());
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("A is sliced") {
|
||||
DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({
|
||||
{ "skirts", 0 },
|
||||
{ "perimeters", 3 },
|
||||
{ "fill_density", 0 },
|
||||
{ "layer_height", 0.2 },
|
||||
{ "first_layer_height", 0.2 },
|
||||
{ "nozzle_diameter", "0.35,0.35,0.35,0.35" },
|
||||
{ "infill_extruder", 2 },
|
||||
{ "solid_infill_extruder", 2 },
|
||||
{ "infill_extrusion_width", 0.52 },
|
||||
{ "solid_infill_extrusion_width", 0.52 },
|
||||
{ "first_layer_extrusion_width", 0 }
|
||||
});
|
||||
|
||||
std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::A }, config);
|
||||
THEN("gcode not empty") {
|
||||
REQUIRE(! gcode.empty());
|
||||
}
|
||||
|
||||
THEN("no missing parts in solid shell when fill_density is 0") {
|
||||
GCodeReader parser;
|
||||
int tool = -1;
|
||||
const int infill_extruder = config.opt_int("infill_extruder");
|
||||
std::map<coord_t, Lines> infill;
|
||||
parser.parse_buffer(gcode, [&tool, &infill, infill_extruder](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) {
|
||||
if (boost::starts_with(line.cmd(), "T")) {
|
||||
tool = atoi(line.cmd().data() + 1) + 1;
|
||||
} else if (line.cmd() == "G1" && line.extruding(self) && line.dist_XY(self) > 0) {
|
||||
if (tool == infill_extruder)
|
||||
infill[scaled<coord_t>(self.z())].emplace_back(self.xy_scaled(), line.new_XY_scaled(self));
|
||||
}
|
||||
});
|
||||
auto opt_width = config.opt<ConfigOptionFloatOrPercent>("infill_extrusion_width");
|
||||
REQUIRE(! opt_width->percent);
|
||||
auto grow_d = scaled<float>(opt_width->value / 2);
|
||||
auto inflate_lines = [grow_d](const Lines &lines) {
|
||||
Polygons out;
|
||||
for (const Line &line : lines)
|
||||
append(out, offset(Polyline{ line.a, line.b }, grow_d, Slic3r::ClipperLib::jtSquare, 3.));
|
||||
return union_(out);
|
||||
};
|
||||
Polygons layer0_infill = inflate_lines(infill[scaled<coord_t>(0.2)]);
|
||||
Polygons layer1_infill = inflate_lines(infill[scaled<coord_t>(0.4)]);
|
||||
ExPolygons poly = opening_ex(diff_ex(layer0_infill, layer1_infill), grow_d);
|
||||
const double threshold = 2. * sqr(grow_d * 2.);
|
||||
int missing_parts = std::count_if(poly.begin(), poly.end(), [threshold](const ExPolygon &poly){ return poly.area() > threshold; });
|
||||
REQUIRE(missing_parts == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
# GH: #2697
|
||||
my $config = Slic3r::Config->new_from_defaults;
|
||||
@ -427,6 +455,78 @@ for my $pattern (qw(rectilinear honeycomb hilbertcurve concentric)) {
|
||||
my @holes = map @{$_->holes}, @$covered;
|
||||
ok sum(map unscale unscale $_->area*-1, @holes) < 1, 'no gaps between top solid infill and perimeters';
|
||||
}
|
||||
|
||||
{
|
||||
skip "The FillRectilinear2 does not fill the surface completely", 1;
|
||||
|
||||
my $test = sub {
|
||||
my ($expolygon, $flow_spacing, $angle, $density) = @_;
|
||||
|
||||
my $filler = Slic3r::Filler->new_from_type('rectilinear');
|
||||
$filler->set_bounding_box($expolygon->bounding_box);
|
||||
$filler->set_angle($angle // 0);
|
||||
# Adjust line spacing to fill the region.
|
||||
$filler->set_dont_adjust(0);
|
||||
$filler->set_link_max_length(scale(1.2*$flow_spacing));
|
||||
my $surface = Slic3r::Surface->new(
|
||||
surface_type => S_TYPE_BOTTOM,
|
||||
expolygon => $expolygon,
|
||||
);
|
||||
my $flow = Slic3r::Flow->new(
|
||||
width => $flow_spacing,
|
||||
height => 0.4,
|
||||
nozzle_diameter => $flow_spacing,
|
||||
);
|
||||
$filler->set_spacing($flow->spacing);
|
||||
my $paths = $filler->fill_surface(
|
||||
$surface,
|
||||
layer_height => $flow->height,
|
||||
density => $density // 1,
|
||||
);
|
||||
|
||||
# check whether any part was left uncovered
|
||||
my @grown_paths = map @{Slic3r::Polyline->new(@$_)->grow(scale $filler->spacing/2)}, @$paths;
|
||||
my $uncovered = diff_ex([ @$expolygon ], [ @grown_paths ], 1);
|
||||
|
||||
# ignore very small dots
|
||||
my $uncovered_filtered = [ grep $_->area > (scale $flow_spacing)**2, @$uncovered ];
|
||||
|
||||
is scalar(@$uncovered_filtered), 0, 'solid surface is fully filled';
|
||||
|
||||
if (0 && @$uncovered_filtered) {
|
||||
require "Slic3r/SVG.pm";
|
||||
Slic3r::SVG::output("uncovered.svg",
|
||||
no_arrows => 1,
|
||||
expolygons => [ $expolygon ],
|
||||
blue_expolygons => [ @$uncovered ],
|
||||
red_expolygons => [ @$uncovered_filtered ],
|
||||
polylines => [ @$paths ],
|
||||
);
|
||||
exit;
|
||||
}
|
||||
};
|
||||
|
||||
my $expolygon = Slic3r::ExPolygon->new([
|
||||
[6883102, 9598327.01296997],
|
||||
[6883102, 20327272.01297],
|
||||
[3116896, 20327272.01297],
|
||||
[3116896, 9598327.01296997],
|
||||
]);
|
||||
$test->($expolygon, 0.55);
|
||||
|
||||
for (1..20) {
|
||||
$expolygon->scale(1.05);
|
||||
$test->($expolygon, 0.55);
|
||||
}
|
||||
|
||||
$expolygon = Slic3r::ExPolygon->new(
|
||||
[[59515297,5422499],[59531249,5578697],[59695801,6123186],[59965713,6630228],[60328214,7070685],[60773285,7434379],[61274561,7702115],[61819378,7866770],[62390306,7924789],[62958700,7866744],[63503012,7702244],[64007365,7434357],[64449960,7070398],[64809327,6634999],[65082143,6123325],[65245005,5584454],[65266967,5422499],[66267307,5422499],[66269190,8310081],[66275379,17810072],[66277259,20697500],[65267237,20697500],[65245004,20533538],[65082082,19994444],[64811462,19488579],[64450624,19048208],[64012101,18686514],[63503122,18415781],[62959151,18251378],[62453416,18198442],[62390147,18197355],[62200087,18200576],[61813519,18252990],[61274433,18415918],[60768598,18686517],[60327567,19047892],[59963609,19493297],[59695865,19994587],[59531222,20539379],[59515153,20697500],[58502480,20697500],[58502480,5422499]]
|
||||
);
|
||||
$test->($expolygon, 0.524341649025257);
|
||||
|
||||
$expolygon = Slic3r::ExPolygon->new([ scale_points [0,0], [98,0], [98,10], [0,10] ]);
|
||||
$test->($expolygon, 0.5, 45, 0.99); # non-solid infill
|
||||
}
|
||||
*/
|
||||
|
||||
bool test_if_solid_surface_filled(const ExPolygon& expolygon, double flow_spacing, double angle, double density)
|
||||
|
@ -53,11 +53,8 @@ set(XS_XSP_FILES
|
||||
${XSP_DIR}/ExtrusionLoop.xsp
|
||||
${XSP_DIR}/ExtrusionMultiPath.xsp
|
||||
${XSP_DIR}/ExtrusionPath.xsp
|
||||
${XSP_DIR}/ExtrusionSimulator.xsp
|
||||
${XSP_DIR}/Filler.xsp
|
||||
${XSP_DIR}/Flow.xsp
|
||||
${XSP_DIR}/GCode.xsp
|
||||
# ${XSP_DIR}/GCodeSender.xsp
|
||||
${XSP_DIR}/Geometry.xsp
|
||||
${XSP_DIR}/Layer.xsp
|
||||
${XSP_DIR}/Line.xsp
|
||||
|
@ -133,23 +133,6 @@ sub clone {
|
||||
);
|
||||
}
|
||||
|
||||
package Slic3r::ExtrusionSimulator;
|
||||
|
||||
sub new {
|
||||
my ($class, %args) = @_;
|
||||
return $class->_new();
|
||||
}
|
||||
|
||||
package Slic3r::Filler;
|
||||
|
||||
sub fill_surface {
|
||||
my ($self, $surface, %args) = @_;
|
||||
$self->set_density($args{density}) if defined($args{density});
|
||||
$self->set_dont_adjust($args{dont_adjust}) if defined($args{dont_adjust});
|
||||
$self->set_complete($args{complete}) if defined($args{complete});
|
||||
return $self->_fill_surface($surface);
|
||||
}
|
||||
|
||||
package Slic3r::Flow;
|
||||
|
||||
sub new {
|
||||
@ -255,19 +238,12 @@ for my $class (qw(
|
||||
Slic3r::ExtrusionMultiPath
|
||||
Slic3r::ExtrusionPath
|
||||
Slic3r::ExtrusionPath::Collection
|
||||
Slic3r::ExtrusionSimulator
|
||||
Slic3r::Filler
|
||||
Slic3r::Flow
|
||||
Slic3r::GCode
|
||||
Slic3r::GCode::PlaceholderParser
|
||||
Slic3r::Geometry::BoundingBox
|
||||
Slic3r::Geometry::BoundingBoxf
|
||||
Slic3r::Geometry::BoundingBoxf3
|
||||
Slic3r::GUI::_3DScene::GLShader
|
||||
Slic3r::GUI::_3DScene::GLVolume
|
||||
Slic3r::GUI::Preset
|
||||
Slic3r::GUI::PresetCollection
|
||||
Slic3r::GUI::Tab
|
||||
Slic3r::Layer
|
||||
Slic3r::Layer::Region
|
||||
Slic3r::Layer::Support
|
||||
|
@ -2,7 +2,6 @@
|
||||
#include <cstdlib>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
// #include <libslic3r/GCodeSender.hpp>
|
||||
|
||||
#ifdef __cplusplus
|
||||
/* extern "C" { */
|
||||
|
@ -9,8 +9,6 @@ REGISTER_CLASS(ExtrusionMultiPath, "ExtrusionMultiPath");
|
||||
REGISTER_CLASS(ExtrusionPath, "ExtrusionPath");
|
||||
REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop");
|
||||
REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection");
|
||||
REGISTER_CLASS(ExtrusionSimulator, "ExtrusionSimulator");
|
||||
REGISTER_CLASS(Filler, "Filler");
|
||||
REGISTER_CLASS(Flow, "Flow");
|
||||
REGISTER_CLASS(CoolingBuffer, "GCode::CoolingBuffer");
|
||||
REGISTER_CLASS(GCode, "GCode");
|
||||
|
@ -1,50 +0,0 @@
|
||||
%module{Slic3r::XS};
|
||||
|
||||
%{
|
||||
#include <xsinit.h>
|
||||
#include "libslic3r/ExtrusionSimulator.hpp"
|
||||
%}
|
||||
|
||||
%name{Slic3r::ExtrusionSimulator} class ExtrusionSimulator {
|
||||
~ExtrusionSimulator();
|
||||
%name{_new} ExtrusionSimulator();
|
||||
|
||||
Clone<ExtrusionSimulator> clone()
|
||||
%code{% RETVAL = THIS; %};
|
||||
|
||||
void set_image_size(Point *image_size)
|
||||
%code{% THIS->set_image_size(*image_size); %};
|
||||
void set_viewport(BoundingBox *viewport)
|
||||
%code{% THIS->set_viewport(*viewport); %};
|
||||
void set_bounding_box(BoundingBox *bbox)
|
||||
%code{% THIS->set_bounding_box(*bbox); %};
|
||||
|
||||
void reset_accumulator();
|
||||
void extrude_to_accumulator(ExtrusionPath *path, Point *shift, ExtrusionSimulationType simulationType)
|
||||
%code{% THIS->extrude_to_accumulator(*path, *shift, simulationType); %};
|
||||
void evaluate_accumulator(ExtrusionSimulationType simulationType);
|
||||
void* image_ptr()
|
||||
%code{% RETVAL = const_cast<void*>(const_cast<Slic3r::ExtrusionSimulator*>(THIS)->image_ptr()); %};
|
||||
|
||||
%{
|
||||
|
||||
%}
|
||||
};
|
||||
|
||||
%package{Slic3r::ExtrusionSimulator};
|
||||
%{
|
||||
|
||||
IV
|
||||
_constant()
|
||||
ALIAS:
|
||||
EXTRSIM_SIMPLE = ExtrusionSimulationSimple
|
||||
EXTRSIM_DONT_SPREAD = ExtrusionSimulationDontSpread
|
||||
EXTRSIM_SPREAD_NFULL = ExtrisopmSimulationSpreadNotOverfilled
|
||||
EXTRSIM_SPREAD_FULL = ExtrusionSimulationSpreadFull
|
||||
EXTRSIM_SPREAD_EXCESS = ExtrusionSimulationSpreadExcess
|
||||
PROTOTYPE:
|
||||
CODE:
|
||||
RETVAL = ix;
|
||||
OUTPUT: RETVAL
|
||||
|
||||
%}
|
@ -1,65 +0,0 @@
|
||||
%module{Slic3r::XS};
|
||||
|
||||
%{
|
||||
#include <xsinit.h>
|
||||
#include "libslic3r/Fill/Fill.hpp"
|
||||
#include "libslic3r/ExtrusionEntity.hpp"
|
||||
#include "libslic3r/ExtrusionEntityCollection.hpp"
|
||||
%}
|
||||
|
||||
%name{Slic3r::Filler} class Filler {
|
||||
~Filler();
|
||||
|
||||
void set_bounding_box(BoundingBox *bbox)
|
||||
%code{% THIS->fill->set_bounding_box(*bbox); %};
|
||||
void set_spacing(coordf_t spacing)
|
||||
%code{% THIS->fill->spacing = spacing; %};
|
||||
coordf_t spacing()
|
||||
%code{% RETVAL = THIS->fill->spacing; %};
|
||||
void set_layer_id(size_t layer_id)
|
||||
%code{% THIS->fill->layer_id = layer_id; %};
|
||||
void set_z(coordf_t z)
|
||||
%code{% THIS->fill->z = z; %};
|
||||
void set_angle(float angle)
|
||||
%code{% THIS->fill->angle = angle; %};
|
||||
void set_link_max_length(coordf_t len)
|
||||
%code{% THIS->fill->link_max_length = len; %};
|
||||
void set_loop_clipping(coordf_t clipping)
|
||||
%code{% THIS->fill->loop_clipping = clipping; %};
|
||||
|
||||
bool use_bridge_flow()
|
||||
%code{% RETVAL = THIS->fill->use_bridge_flow(); %};
|
||||
bool no_sort()
|
||||
%code{% RETVAL = THIS->fill->no_sort(); %};
|
||||
|
||||
void set_density(float density)
|
||||
%code{% THIS->params.density = density; %};
|
||||
void set_dont_adjust(bool dont_adjust)
|
||||
%code{% THIS->params.dont_adjust = dont_adjust; %};
|
||||
|
||||
PolylineCollection* _fill_surface(Surface *surface)
|
||||
%code{%
|
||||
PolylineCollection *pc = NULL;
|
||||
if (THIS->fill != NULL) {
|
||||
pc = new PolylineCollection();
|
||||
pc->polylines = THIS->fill->fill_surface(surface, THIS->params);
|
||||
}
|
||||
RETVAL = pc;
|
||||
%};
|
||||
|
||||
%{
|
||||
|
||||
Filler*
|
||||
new_from_type(CLASS, type)
|
||||
char* CLASS;
|
||||
std::string type;
|
||||
CODE:
|
||||
Filler *filler = new Filler();
|
||||
filler->fill = Fill::new_from_type(type);
|
||||
RETVAL = filler;
|
||||
OUTPUT:
|
||||
RETVAL
|
||||
|
||||
%}
|
||||
|
||||
};
|
@ -1,24 +0,0 @@
|
||||
%module{Slic3r::XS};
|
||||
|
||||
%{
|
||||
#include <xsinit.h>
|
||||
#include "libslic3r/GCodeSender.hpp"
|
||||
%}
|
||||
|
||||
%name{Slic3r::GCode::Sender} class GCodeSender {
|
||||
GCodeSender();
|
||||
~GCodeSender();
|
||||
|
||||
bool connect(std::string port, unsigned int baud_rate);
|
||||
void disconnect();
|
||||
bool is_connected();
|
||||
bool wait_connected(unsigned int timeout = 3);
|
||||
int queue_size();
|
||||
void send(std::string s, bool priority = false);
|
||||
void pause_queue();
|
||||
void resume_queue();
|
||||
void purge_queue(bool priority = false);
|
||||
std::vector<std::string> purge_log();
|
||||
std::string getT();
|
||||
std::string getB();
|
||||
};
|
@ -118,14 +118,6 @@ ExtrusionLoop* O_OBJECT_SLIC3R
|
||||
Ref<ExtrusionLoop> O_OBJECT_SLIC3R_T
|
||||
Clone<ExtrusionLoop> O_OBJECT_SLIC3R_T
|
||||
|
||||
ExtrusionSimulator* O_OBJECT_SLIC3R
|
||||
Ref<ExtrusionSimulator> O_OBJECT_SLIC3R_T
|
||||
Clone<ExtrusionSimulator> O_OBJECT_SLIC3R_T
|
||||
|
||||
Filler* O_OBJECT_SLIC3R
|
||||
Ref<Filler> O_OBJECT_SLIC3R_T
|
||||
Clone<Filler> O_OBJECT_SLIC3R_T
|
||||
|
||||
Flow* O_OBJECT_SLIC3R
|
||||
Ref<Flow> O_OBJECT_SLIC3R_T
|
||||
Clone<Flow> O_OBJECT_SLIC3R_T
|
||||
|
@ -58,9 +58,6 @@
|
||||
%typemap{ExPolygonCollection*};
|
||||
%typemap{Ref<ExPolygonCollection>}{simple};
|
||||
%typemap{Clone<ExPolygonCollection>}{simple};
|
||||
%typemap{Filler*};
|
||||
%typemap{Ref<Filler>}{simple};
|
||||
%typemap{Clone<Filler>}{simple};
|
||||
%typemap{Flow*};
|
||||
%typemap{Ref<Flow>}{simple};
|
||||
%typemap{Clone<Flow>}{simple};
|
||||
@ -88,9 +85,6 @@
|
||||
%typemap{ExtrusionLoop*};
|
||||
%typemap{Ref<ExtrusionLoop>}{simple};
|
||||
%typemap{Clone<ExtrusionLoop>}{simple};
|
||||
%typemap{ExtrusionSimulator*};
|
||||
%typemap{Ref<ExtrusionSimulator>}{simple};
|
||||
%typemap{Clone<ExtrusionSimulator>}{simple};
|
||||
%typemap{TriangleMesh*};
|
||||
%typemap{Ref<TriangleMesh>}{simple};
|
||||
%typemap{Clone<TriangleMesh>}{simple};
|
||||
|
Loading…
Reference in New Issue
Block a user