Refactored PerimeterGenerator to output out_fill_surfaces as ExPolygons,

not SurfaceCollection.

Reworked combineinfill.t, 07_extrusionpath.t, 08_extrusionloop.t to c++.
Removed Layer / ExtrusionPath / ExtrusionLoop / ExtrusionEntityCollection
from Perl bindings.
This commit is contained in:
Vojtech Bubnik 2022-10-27 19:08:43 +02:00
parent 237e56c7ce
commit d041fa6c0c
24 changed files with 422 additions and 852 deletions

View File

@ -6,9 +6,4 @@ use warnings;
use List::Util qw(min max sum first); use List::Util qw(min max sum first);
use Slic3r::Surface ':types'; use Slic3r::Surface ':types';
sub layers {
my $self = shift;
return [ map $self->get_layer($_), 0..($self->layer_count - 1) ];
}
1; 1;

View File

@ -426,12 +426,12 @@ void Layer::make_perimeters()
} }
} }
SurfaceCollection fill_surfaces; ExPolygons fill_expolygons;
if (layerms.size() == 1) { // optimization if (layerms.size() == 1) { // optimization
(*layerm)->m_fill_expolygons.clear(); (*layerm)->m_fill_expolygons.clear();
(*layerm)->m_fill_surfaces.clear(); (*layerm)->m_fill_surfaces.clear();
(*layerm)->make_perimeters((*layerm)->slices(), &fill_surfaces); (*layerm)->make_perimeters((*layerm)->slices(), fill_expolygons);
(*layerm)->m_fill_expolygons = to_expolygons(fill_surfaces.surfaces); (*layerm)->m_fill_expolygons = std::move(fill_expolygons);
} else { } else {
SurfaceCollection new_slices; SurfaceCollection new_slices;
// Use the region with highest infill rate, as the make_perimeters() function below decides on the gap fill based on the infill existence. // Use the region with highest infill rate, as the make_perimeters() function below decides on the gap fill based on the infill existence.
@ -452,12 +452,12 @@ void Layer::make_perimeters()
new_slices.append(offset_ex(surfaces_with_extra_perimeters.second, ClipperSafetyOffset), surfaces_with_extra_perimeters.second.front()); new_slices.append(offset_ex(surfaces_with_extra_perimeters.second, ClipperSafetyOffset), surfaces_with_extra_perimeters.second.front());
} }
// make perimeters // make perimeters
layerm_config->make_perimeters(new_slices, &fill_surfaces); layerm_config->make_perimeters(new_slices, fill_expolygons);
// assign fill_surfaces to each layer // assign fill_surfaces to each layer
if (! fill_surfaces.empty()) { if (! fill_expolygons.empty()) {
// Separate the fill surfaces. // Separate the fill surfaces.
for (LayerRegion *l : layerms) for (LayerRegion *l : layerms)
l->m_fill_expolygons = intersection_ex(fill_surfaces.surfaces, l->slices().surfaces); l->m_fill_expolygons = intersection_ex(l->slices().surfaces, fill_expolygons);
} }
} }
} }

View File

@ -67,7 +67,7 @@ public:
void slices_to_fill_surfaces_clipped(); void slices_to_fill_surfaces_clipped();
void prepare_fill_surfaces(); void prepare_fill_surfaces();
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); void make_perimeters(const SurfaceCollection &slices, ExPolygons &fill_expolygons);
void process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered); void process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered);
double infill_area_threshold() const; double infill_area_threshold() const;
// Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer. // Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer.

View File

@ -59,7 +59,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
} }
} }
void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces) void LayerRegion::make_perimeters(const SurfaceCollection &slices, ExPolygons &fill_expolygons)
{ {
m_perimeters.clear(); m_perimeters.clear();
m_thin_fills.clear(); m_thin_fills.clear();
@ -100,7 +100,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
// output: // output:
m_perimeters, m_perimeters,
m_thin_fills, m_thin_fills,
*fill_surfaces); fill_expolygons);
else else
PerimeterGenerator::process_classic( PerimeterGenerator::process_classic(
// input: // input:
@ -111,7 +111,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
// output: // output:
m_perimeters, m_perimeters,
m_thin_fills, m_thin_fills,
*fill_surfaces); fill_expolygons);
} }
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3. //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.

View File

@ -611,7 +611,7 @@ void PerimeterGenerator::process_arachne(
// Gaps without the thin walls // Gaps without the thin walls
ExtrusionEntityCollection &out_gap_fill, ExtrusionEntityCollection &out_gap_fill,
// Infills without the gap fills // Infills without the gap fills
SurfaceCollection &out_fill_surfaces) ExPolygons &out_fill_expolygons)
{ {
// other perimeters // other perimeters
coord_t perimeter_spacing = params.perimeter_flow.scaled_spacing(); coord_t perimeter_spacing = params.perimeter_flow.scaled_spacing();
@ -817,12 +817,11 @@ void PerimeterGenerator::process_arachne(
// collapse too narrow infill areas // collapse too narrow infill areas
const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE)); const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
// append infill areas to fill_surfaces // append infill areas to fill_surfaces
out_fill_surfaces.append( append(out_fill_expolygons,
offset2_ex( offset2_ex(
union_ex(pp), union_ex(pp),
float(- min_perimeter_infill_spacing / 2.), float(- min_perimeter_infill_spacing / 2.),
float(inset + min_perimeter_infill_spacing / 2.)), float(inset + min_perimeter_infill_spacing / 2.)));
stInternal);
} }
} }
@ -839,7 +838,7 @@ void PerimeterGenerator::process_classic(
// Gaps without the thin walls // Gaps without the thin walls
ExtrusionEntityCollection &out_gap_fill, ExtrusionEntityCollection &out_gap_fill,
// Infills without the gap fills // Infills without the gap fills
SurfaceCollection &out_fill_surfaces) ExPolygons &out_fill_expolygons)
{ {
// other perimeters // other perimeters
coord_t perimeter_width = params.perimeter_flow.scaled_width(); coord_t perimeter_width = params.perimeter_flow.scaled_width();
@ -1094,12 +1093,11 @@ void PerimeterGenerator::process_classic(
// collapse too narrow infill areas // collapse too narrow infill areas
coord_t min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE)); coord_t min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE));
// append infill areas to fill_surfaces // append infill areas to fill_surfaces
out_fill_surfaces.append( append(out_fill_expolygons,
offset2_ex( offset2_ex(
union_ex(pp), union_ex(pp),
float(- inset - min_perimeter_infill_spacing / 2.), float(- inset - min_perimeter_infill_spacing / 2.),
float(min_perimeter_infill_spacing / 2.)), float(min_perimeter_infill_spacing / 2.)));
stInternal);
} // for each island } // for each island
} }

View File

@ -77,7 +77,7 @@ void process_classic(
// Gaps without the thin walls // Gaps without the thin walls
ExtrusionEntityCollection &out_gap_fill, ExtrusionEntityCollection &out_gap_fill,
// Infills without the gap fills // Infills without the gap fills
SurfaceCollection &out_fill_surfaces); ExPolygons &out_fill_expolygons);
void process_arachne( void process_arachne(
// Inputs: // Inputs:
@ -92,7 +92,7 @@ void process_arachne(
// Gaps without the thin walls // Gaps without the thin walls
ExtrusionEntityCollection &out_gap_fill, ExtrusionEntityCollection &out_gap_fill,
// Infills without the gap fills // Infills without the gap fills
SurfaceCollection &out_fill_surfaces); ExPolygons &out_fill_expolygons);
ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, float tolerance, float merge_tolerance); ExtrusionMultiPath thick_polyline_to_multi_path(const ThickPolyline &thick_polyline, ExtrusionRole role, const Flow &flow, float tolerance, float merge_tolerance);

View File

@ -32,12 +32,13 @@
#include "Technologies.hpp" #include "Technologies.hpp"
#include "Semver.hpp" #include "Semver.hpp"
using coord_t =
#if 1 #if 1
// Saves around 32% RAM after slicing step, 6.7% after G-code export (tested on PrusaSlicer 2.2.0 final). // Saves around 32% RAM after slicing step, 6.7% after G-code export (tested on PrusaSlicer 2.2.0 final).
using coord_t = int32_t; int32_t;
#else #else
//FIXME At least FillRectilinear2 and std::boost Voronoi require coord_t to be 32bit. //FIXME At least FillRectilinear2 and std::boost Voronoi require coord_t to be 32bit.
typedef int64_t coord_t; int64_t;
#endif #endif
using coordf_t = double; using coordf_t = double;
@ -366,4 +367,4 @@ inline IntegerOnly<I, I> fast_round_up(double a)
} // namespace Slic3r } // namespace Slic3r
#endif #endif // _libslic3r_h_

View File

@ -1,110 +0,0 @@
use Test::More;
use strict;
use warnings;
BEGIN {
use FindBin;
use lib "$FindBin::Bin/../lib";
use local::lib "$FindBin::Bin/../local-lib";
}
use List::Util qw(first);
use Slic3r;
use Slic3r::Surface ':types';
use Slic3r::Test;
plan tests => 8;
{
my $test = sub {
my ($config) = @_;
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
ok my $gcode = Slic3r::Test::gcode($print), "infill_every_layers does not crash";
my $tool = undef;
my %layers = (); # layer_z => 1
my %layer_infill = (); # layer_z => has_infill
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 && $tool != $config->support_material_extruder-1) {
$layer_infill{$self->Z} //= 0;
if ($tool == $config->infill_extruder-1) {
$layer_infill{$self->Z} = 1;
}
}
# Previously, all G-code commands had a fixed number of decimal points with means with redundant zeros after decimal points.
# We changed this behavior and got rid of these redundant padding zeros, which caused this test to fail
# because the position in Z-axis is compared as a string, and previously, G-code contained the following two commands:
# "G1 Z5 F5000 ; lift nozzle"
# "G1 Z5.000 F7800.000"
# That has a different Z-axis position from the view of string comparisons of floating-point numbers.
# To correct the computation of the number of printed layers, even in the case of string comparisons of floating-point numbers,
# we filtered out the G-code command with the commend 'lift nozzle'.
$layers{$args->{Z}} = 1 if $cmd eq 'G1' && $info->{dist_Z} && index($info->{comment}, 'lift nozzle') == -1;
});
my $layers_with_perimeters = scalar(keys %layer_infill);
my $layers_with_infill = grep $_ > 0, values %layer_infill;
is scalar(keys %layers), $layers_with_perimeters+$config->raft_layers, 'expected number of layers';
if ($config->raft_layers == 0) {
# first infill layer printed directly on print bed is not combined, so we don't consider it.
$layers_with_infill--;
$layers_with_perimeters--;
}
# we expect that infill is generated for half the number of combined layers
# plus for each single layer that was not combined (remainder)
is $layers_with_infill,
int($layers_with_perimeters/$config->infill_every_layers) + ($layers_with_perimeters % $config->infill_every_layers),
'infill is only present in correct number of layers';
};
my $config = Slic3r::Config::new_from_defaults;
$config->set('layer_height', 0.2);
$config->set('first_layer_height', 0.2);
$config->set('nozzle_diameter', [0.5,0.5,0.5,0.5]);
$config->set('infill_every_layers', 2);
$config->set('perimeter_extruder', 1);
$config->set('infill_extruder', 2);
$config->set('wipe_into_infill', 0);
$config->set('support_material_extruder', 3);
$config->set('support_material_interface_extruder', 3);
$config->set('top_solid_layers', 0);
$config->set('bottom_solid_layers', 0);
$test->($config);
$config->set('skirts', 0); # prevent usage of perimeter_extruder in raft layers
$config->set('raft_layers', 5);
$test->($config);
}
{
my $config = Slic3r::Config::new_from_defaults;
$config->set('layer_height', 0.2);
$config->set('first_layer_height', 0.2);
$config->set('nozzle_diameter', [0.5]);
$config->set('infill_every_layers', 2);
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
$print->process;
ok defined(first { @{$_->get_region(0)->fill_surfaces->filter_by_type(S_TYPE_INTERNALVOID)} > 0 }
@{$print->print->get_object(0)->layers}),
'infill combination produces internal void surfaces';
# we disable combination after infill has been generated
$config->set('infill_every_layers', 1);
$print->apply($print->print->model->clone, $config);
$print->process;
ok !(defined first { @{$_->get_region(0)->fill_surfaces} == 0 }
@{$print->print->get_object(0)->layers}),
'infill combination is idempotent';
}
__END__

View File

@ -35,7 +35,254 @@ static Slic3r::ExtrusionPaths random_paths(size_t count = 10, size_t length = 20
return p; return p;
} }
SCENARIO("ExtrusionEntityCollection: Polygon flattening", "[ExtrusionEntity]") { SCENARIO("ExtrusionPath", "[ExtrusionEntity]") {
GIVEN("Simple path") {
Slic3r::ExtrusionPath path{ erExternalPerimeter };
path.polyline = { { 100, 100 }, { 200, 100 }, { 200, 200 } };
path.mm3_per_mm = 1.;
THEN("first point") {
REQUIRE(path.first_point() == path.polyline.front());
}
THEN("cloned") {
auto cloned = std::unique_ptr<ExtrusionEntity>(path.clone());
REQUIRE(cloned->role() == path.role());
}
}
}
static ExtrusionPath new_extrusion_path(const Polyline &polyline, ExtrusionRole role, double mm3_per_mm)
{
ExtrusionPath path(role);
path.polyline = polyline;
path.mm3_per_mm = 1.;
return path;
}
SCENARIO("ExtrusionLoop", "[ExtrusionEntity]")
{
GIVEN("Square") {
Polygon square { { 100, 100 }, { 200, 100 }, { 200, 200 }, { 100, 200 } };
ExtrusionLoop loop;
loop.paths.emplace_back(new_extrusion_path(square.split_at_first_point(), erExternalPerimeter, 1.));
THEN("polygon area") {
REQUIRE(loop.polygon().area() == Approx(square.area()));
}
THEN("loop length") {
REQUIRE(loop.length() == Approx(square.length()));
}
WHEN("cloned") {
auto loop2 = std::unique_ptr<ExtrusionLoop>(dynamic_cast<ExtrusionLoop*>(loop.clone()));
THEN("cloning worked") {
REQUIRE(loop2 != nullptr);
}
THEN("loop contains one path") {
REQUIRE(loop2->paths.size() == 1);
}
THEN("cloned role") {
REQUIRE(loop2->paths.front().role() == erExternalPerimeter);
}
}
WHEN("cloned and split") {
auto loop2 = std::unique_ptr<ExtrusionLoop>(dynamic_cast<ExtrusionLoop*>(loop.clone()));
loop2->split_at_vertex(square.points[2]);
THEN("splitting a single-path loop results in a single path") {
REQUIRE(loop2->paths.size() == 1);
}
THEN("path has correct number of points") {
REQUIRE(loop2->paths.front().size() == 5);
}
THEN("expected point order") {
REQUIRE(loop2->paths.front().polyline[0] == square.points[2]);
REQUIRE(loop2->paths.front().polyline[1] == square.points[3]);
REQUIRE(loop2->paths.front().polyline[2] == square.points[0]);
REQUIRE(loop2->paths.front().polyline[3] == square.points[1]);
REQUIRE(loop2->paths.front().polyline[4] == square.points[2]);
}
}
}
GIVEN("Loop with two pieces") {
Polyline polyline1 { { 100, 100 }, { 200, 100 }, { 200, 200 } };
Polyline polyline2 { { 200, 200 }, { 100, 200 }, { 100, 100 } };
ExtrusionLoop loop;
loop.paths.emplace_back(new_extrusion_path(polyline1, erExternalPerimeter, 1.));
loop.paths.emplace_back(new_extrusion_path(polyline2, erOverhangPerimeter, 1.));
double tot_len = polyline1.length() + polyline2.length();
THEN("length") {
REQUIRE(loop.length() == Approx(tot_len));
}
WHEN("splitting at intermediate point") {
auto loop2 = std::unique_ptr<ExtrusionLoop>(dynamic_cast<ExtrusionLoop*>(loop.clone()));
loop2->split_at_vertex(polyline1.points[1]);
THEN("length after splitting is unchanged") {
REQUIRE(loop2->length() == Approx(tot_len));
}
THEN("loop contains three paths after splitting") {
REQUIRE(loop2->paths.size() == 3);
}
THEN("expected starting point") {
REQUIRE(loop2->paths.front().polyline.front() == polyline1.points[1]);
}
THEN("expected ending point") {
REQUIRE(loop2->paths.back().polyline.back() == polyline1.points[1]);
}
THEN("paths have common point") {
REQUIRE(loop2->paths.front().polyline.back() == loop2->paths[1].polyline.front());
REQUIRE(loop2->paths[1].polyline.back() == loop2->paths[2].polyline.front());
}
THEN("expected order after splitting") {
REQUIRE(loop2->paths.front().role() == erExternalPerimeter);
REQUIRE(loop2->paths[1].role() == erOverhangPerimeter);
REQUIRE(loop2->paths[2].role() == erExternalPerimeter);
}
THEN("path has correct number of points") {
REQUIRE(loop2->paths.front().polyline.size() == 2);
REQUIRE(loop2->paths[1].polyline.size() == 3);
REQUIRE(loop2->paths[2].polyline.size() == 2);
}
THEN("clipped path has expected length") {
double l = loop2->length();
ExtrusionPaths paths;
loop2->clip_end(3, &paths);
double l2 = 0;
for (const ExtrusionPath &p : paths)
l2 += p.length();
REQUIRE(l2 == Approx(l - 3.));
}
}
WHEN("splitting at endpoint") {
auto loop2 = std::unique_ptr<ExtrusionLoop>(dynamic_cast<ExtrusionLoop*>(loop.clone()));
loop2->split_at_vertex(polyline2.points.front());
THEN("length after splitting is unchanged") {
REQUIRE(loop2->length() == Approx(tot_len));
}
THEN("loop contains two paths after splitting") {
REQUIRE(loop2->paths.size() == 2);
}
THEN("expected starting point") {
REQUIRE(loop2->paths.front().polyline.front() == polyline2.points.front());
}
THEN("expected ending point") {
REQUIRE(loop2->paths.back().polyline.back() == polyline2.points.front());
}
THEN("paths have common point") {
REQUIRE(loop2->paths.front().polyline.back() == loop2->paths[1].polyline.front());
REQUIRE(loop2->paths[1].polyline.back() == loop2->paths.front().polyline.front());
}
THEN("expected order after splitting") {
REQUIRE(loop2->paths.front().role() == erOverhangPerimeter);
REQUIRE(loop2->paths[1].role() == erExternalPerimeter);
}
THEN("path has correct number of points") {
REQUIRE(loop2->paths.front().polyline.size() == 3);
REQUIRE(loop2->paths[1].polyline.size() == 3);
}
}
WHEN("splitting at an edge") {
Point point(250, 150);
auto loop2 = std::unique_ptr<ExtrusionLoop>(dynamic_cast<ExtrusionLoop*>(loop.clone()));
loop2->split_at(point, false, 0);
THEN("length after splitting is unchanged") {
REQUIRE(loop2->length() == Approx(tot_len));
}
Point expected_start_point(200, 150);
THEN("expected starting point") {
REQUIRE(loop2->paths.front().polyline.front() == expected_start_point);
}
THEN("expected ending point") {
REQUIRE(loop2->paths.back().polyline.back() == expected_start_point);
}
}
}
GIVEN("Loop with four pieces") {
Polyline polyline1 { { 59312736, 4821067 }, { 64321068, 4821067 }, { 64321068, 4821067 }, { 64321068, 9321068 }, { 59312736, 9321068 } };
Polyline polyline2 { { 59312736, 9321068 }, { 9829401, 9321068 } };
Polyline polyline3 { { 9829401, 9321068 }, { 4821067, 9321068 }, { 4821067, 4821067 }, { 9829401, 4821067 } };
Polyline polyline4 { { 9829401, 4821067 }, { 59312736,4821067 } };
ExtrusionLoop loop;
loop.paths.emplace_back(new_extrusion_path(polyline1, erExternalPerimeter, 1.));
loop.paths.emplace_back(new_extrusion_path(polyline2, erOverhangPerimeter, 1.));
loop.paths.emplace_back(new_extrusion_path(polyline3, erExternalPerimeter, 1.));
loop.paths.emplace_back(new_extrusion_path(polyline4, erOverhangPerimeter, 1.));
double len = loop.length();
WHEN("splitting at vertex") {
Point point(4821067, 9321068);
if (! loop.split_at_vertex(point))
loop.split_at(point, false, 0);
THEN("total length is preserved after splitting") {
REQUIRE(loop.length() == Approx(len));
}
THEN("order is correctly preserved after splitting") {
REQUIRE(loop.paths.front().role() == erExternalPerimeter);
REQUIRE(loop.paths[1].role() == erOverhangPerimeter);
REQUIRE(loop.paths[2].role() == erExternalPerimeter);
REQUIRE(loop.paths[3].role() == erOverhangPerimeter);
}
}
}
GIVEN("Some complex loop") {
ExtrusionLoop loop;
loop.paths.emplace_back(new_extrusion_path(
Polyline { { 15896783, 15868739 }, { 24842049, 12117558 }, { 33853238, 15801279 }, { 37591780, 24780128 }, { 37591780, 24844970 },
{ 33853231, 33825297 }, { 24842049, 37509013 }, { 15896798, 33757841 }, { 12211841, 24812544 }, { 15896783, 15868739 } },
erExternalPerimeter, 1.));
double len = loop.length();
THEN("split_at() preserves total length") {
loop.split_at({ 15896783, 15868739 }, false, 0);
REQUIRE(loop.length() == Approx(len));
}
}
}
SCENARIO("ExtrusionEntityCollection: Basics", "[ExtrusionEntity]")
{
Polyline polyline { { 100, 100 }, { 200, 100 }, { 200, 200 } };
ExtrusionPath path = new_extrusion_path(polyline, erExternalPerimeter, 1.);
ExtrusionLoop loop;
loop.paths.emplace_back(new_extrusion_path(Polygon(polyline.points).split_at_first_point(), erInternalInfill, 1.));
ExtrusionEntityCollection collection;
collection.append(path);
THEN("no_sort is false by default") {
REQUIRE(! collection.no_sort);
}
collection.append(collection);
THEN("append ExtrusionEntityCollection") {
REQUIRE(collection.entities.size() == 2);
}
collection.append(path);
THEN("append ExtrusionPath") {
REQUIRE(collection.entities.size() == 3);
}
collection.append(loop);
THEN("append ExtrusionLoop") {
REQUIRE(collection.entities.size() == 4);
}
THEN("appended collection was duplicated") {
REQUIRE(dynamic_cast<ExtrusionEntityCollection*>(collection.entities[1])->entities.size() == 1);
}
WHEN("cloned") {
auto coll2 = std::unique_ptr<ExtrusionEntityCollection>(dynamic_cast<ExtrusionEntityCollection*>(collection.clone()));
THEN("expected no_sort value") {
assert(! coll2->no_sort);
}
coll2->no_sort = true;
THEN("no_sort is kept after clone") {
auto coll3 = std::unique_ptr<ExtrusionEntityCollection>(dynamic_cast<ExtrusionEntityCollection*>(coll2->clone()));
assert(coll3->no_sort);
}
}
}
SCENARIO("ExtrusionEntityCollection: Polygon flattening", "[ExtrusionEntity]")
{
srand(0xDEADBEEF); // consistent seed for test reproducibility. srand(0xDEADBEEF); // consistent seed for test reproducibility.
// Generate one specific random path set and save it for later comparison // Generate one specific random path set and save it for later comparison
@ -101,7 +348,7 @@ TEST_CASE("ExtrusionEntityCollection: Chained path", "[ExtrusionEntity]") {
{ {0,20}, {0,18}, {0,15} }, { {0,20}, {0,18}, {0,15} },
{ {0,10}, {0,8}, {0,5} } { {0,10}, {0,8}, {0,5} }
}, },
{ 0,30 } { 0, 30 }
}, },
{ {
{ {
@ -112,7 +359,7 @@ TEST_CASE("ExtrusionEntityCollection: Chained path", "[ExtrusionEntity]") {
{ {20,5}, {15,5}, {10,5} }, { {20,5}, {15,5}, {10,5} },
{ {15,0}, {10,0}, {4,0} } { {15,0}, {10,0}, {4,0} }
}, },
{ 30,0 } { 30, 0 }
}, },
{ {
{ {
@ -123,7 +370,7 @@ TEST_CASE("ExtrusionEntityCollection: Chained path", "[ExtrusionEntity]") {
{ {20,5}, {15,5}, {10,5} }, { {20,5}, {15,5}, {10,5} },
{ {15,0}, {10,0}, {4,0} } { {15,0}, {10,0}, {4,0} }
}, },
{ 30,0 } { 30, 0 }
}, },
}; };
for (const Test &test : tests) { for (const Test &test : tests) {
@ -132,12 +379,24 @@ TEST_CASE("ExtrusionEntityCollection: Chained path", "[ExtrusionEntity]") {
ExtrusionEntityCollection unchained_extrusions; ExtrusionEntityCollection unchained_extrusions;
extrusion_entities_append_paths(unchained_extrusions.entities, test.unchained, extrusion_entities_append_paths(unchained_extrusions.entities, test.unchained,
erInternalInfill, 0., 0.4f, 0.3f); erInternalInfill, 0., 0.4f, 0.3f);
ExtrusionEntityCollection chained_extrusions = unchained_extrusions.chained_path_from(test.initial_point); THEN("Chaining works") {
REQUIRE(chained_extrusions.entities.size() == test.chained.size()); ExtrusionEntityCollection chained_extrusions = unchained_extrusions.chained_path_from(test.initial_point);
for (size_t i = 0; i < chained_extrusions.entities.size(); ++ i) { REQUIRE(chained_extrusions.entities.size() == test.chained.size());
const Points &p1 = test.chained[i].points; for (size_t i = 0; i < chained_extrusions.entities.size(); ++ i) {
const Points &p2 = dynamic_cast<const ExtrusionPath*>(chained_extrusions.entities[i])->polyline.points; const Points &p1 = test.chained[i].points;
REQUIRE(p1 == p2); const Points &p2 = dynamic_cast<const ExtrusionPath*>(chained_extrusions.entities[i])->polyline.points;
REQUIRE(p1 == p2);
}
}
THEN("Chaining produces no change with no_sort") {
unchained_extrusions.no_sort = true;
ExtrusionEntityCollection chained_extrusions = unchained_extrusions.chained_path_from(test.initial_point);
REQUIRE(chained_extrusions.entities.size() == test.unchained.size());
for (size_t i = 0; i < chained_extrusions.entities.size(); ++ i) {
const Points &p1 = test.unchained[i].points;
const Points &p2 = dynamic_cast<const ExtrusionPath*>(chained_extrusions.entities[i])->polyline.points;
REQUIRE(p1 == p2);
}
} }
} }
} }

View File

@ -3,14 +3,17 @@
#include <numeric> #include <numeric>
#include <sstream> #include <sstream>
#include "libslic3r/libslic3r.h"
#include "libslic3r/ClipperUtils.hpp" #include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/Fill/Fill.hpp" #include "libslic3r/Fill/Fill.hpp"
#include "libslic3r/Flow.hpp" #include "libslic3r/Flow.hpp"
#include "libslic3r/Layer.hpp"
#include "libslic3r/Geometry.hpp" #include "libslic3r/Geometry.hpp"
#include "libslic3r/Geometry/ConvexHull.hpp" #include "libslic3r/Geometry/ConvexHull.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/Print.hpp" #include "libslic3r/Print.hpp"
#include "libslic3r/SVG.hpp" #include "libslic3r/SVG.hpp"
#include "libslic3r/libslic3r.h"
#include "test_data.hpp" #include "test_data.hpp"
@ -329,6 +332,130 @@ SCENARIO("Infill only where needed", "[Fill]")
} }
} }
SCENARIO("Combine infill", "[Fill]")
{
{
auto test = [](const DynamicPrintConfig &config) {
std::string gcode = Test::slice({ Test::TestMesh::cube_20x20x20 }, config);
THEN("infill_every_layers does not crash") {
REQUIRE(! gcode.empty());
}
Slic3r::GCodeReader parser;
int tool = -1;
std::set<coord_t> layers; // layer_z => 1
std::map<coord_t, bool> layer_infill; // layer_z => has_infill
const int infill_extruder = config.opt_int("infill_extruder");
const int support_material_extruder = config.opt_int("support_material_extruder");
parser.parse_buffer(gcode,
[&tool, &layers, &layer_infill, infill_extruder, support_material_extruder](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line)
{
coord_t z = line.new_Z(self) / SCALING_FACTOR;
if (boost::starts_with(line.cmd(), "T")) {
tool = atoi(line.cmd().data() + 1);
} else if (line.cmd_is("G1") && line.extruding(self) && line.dist_XY(self) > 0 && tool + 1 != support_material_extruder) {
if (tool + 1 == infill_extruder)
layer_infill[z] = true;
else if (auto it = layer_infill.find(z); it == layer_infill.end())
layer_infill.insert(it, std::make_pair(z, false));
}
// Previously, all G-code commands had a fixed number of decimal points with means with redundant zeros after decimal points.
// We changed this behavior and got rid of these redundant padding zeros, which caused this test to fail
// because the position in Z-axis is compared as a string, and previously, G-code contained the following two commands:
// "G1 Z5 F5000 ; lift nozzle"
// "G1 Z5.000 F7800.000"
// That has a different Z-axis position from the view of string comparisons of floating-point numbers.
// To correct the computation of the number of printed layers, even in the case of string comparisons of floating-point numbers,
// we filtered out the G-code command with the commend 'lift nozzle'.
if (line.cmd_is("G1") && line.dist_Z(self) != 0 && line.comment().find("lift nozzle") == std::string::npos)
layers.insert(z);
});
auto layers_with_perimeters = int(layer_infill.size());
auto layers_with_infill = int(std::count_if(layer_infill.begin(), layer_infill.end(), [](auto &v){ return v.second; }));
THEN("expected number of layers") {
REQUIRE(layers.size() == layers_with_perimeters + config.opt_int("raft_layers"));
}
if (config.opt_int("raft_layers") == 0) {
// first infill layer printed directly on print bed is not combined, so we don't consider it.
-- layers_with_infill;
-- layers_with_perimeters;
}
// we expect that infill is generated for half the number of combined layers
// plus for each single layer that was not combined (remainder)
THEN("infill is only present in correct number of layers") {
int infill_every = config.opt_int("infill_every_layers");
REQUIRE(layers_with_infill == int(layers_with_perimeters / infill_every) + (layers_with_perimeters % infill_every));
}
};
auto config = Slic3r::DynamicPrintConfig::full_print_config_with({
{ "nozzle_diameter", "0.5, 0.5, 0.5, 0.5" },
{ "layer_height", 0.2 },
{ "first_layer_height", 0.2 },
{ "infill_every_layers", 2 },
{ "perimeter_extruder", 1 },
{ "infill_extruder", 2 },
{ "wipe_into_infill", false },
{ "support_material_extruder", 3 },
{ "support_material_interface_extruder", 3 },
{ "top_solid_layers", 0 },
{ "bottom_solid_layers", 0 }
});
test(config);
// Reuse the config above
config.set_deserialize_strict({
{ "skirts", 0 }, // prevent usage of perimeter_extruder in raft layers
{ "raft_layers", 5 }
});
test(config);
}
WHEN("infill_every_layers == 2") {
Slic3r::Print print;
Slic3r::Test::init_and_process_print({ Test::TestMesh::cube_20x20x20 }, print, {
{ "nozzle_diameter", "0.5" },
{ "layer_height", 0.2 },
{ "first_layer_height", 0.2 },
{ "infill_every_layers", 2 }
});
THEN("infill combination produces internal void surfaces") {
bool has_void = false;
for (const Layer *layer : print.get_object(0)->layers())
if (layer->get_region(0)->fill_surfaces().filter_by_type(stInternalVoid).size() > 0) {
has_void = true;
break;
}
REQUIRE(has_void);
}
}
WHEN("infill_every_layers disabled") {
// we disable combination after infill has been generated
Slic3r::Print print;
Slic3r::Test::init_and_process_print({ Test::TestMesh::cube_20x20x20 }, print, {
{ "nozzle_diameter", "0.5" },
{ "layer_height", 0.2 },
{ "first_layer_height", 0.2 },
{ "infill_every_layers", 1 }
});
THEN("infill combination is idempotent") {
bool has_infill_on_each_layer = true;
for (const Layer *layer : print.get_object(0)->layers())
if (layer->get_region(0)->fill_surfaces().empty()) {
has_infill_on_each_layer = false;
break;
}
REQUIRE(has_infill_on_each_layer);
}
}
}
SCENARIO("Infill density zero", "[Fill]") SCENARIO("Infill density zero", "[Fill]")
{ {
WHEN("20mm cube is sliced") { WHEN("20mm cube is sliced") {

View File

@ -44,7 +44,7 @@ SCENARIO("Perimeter nesting", "[Perimeters]")
ExtrusionEntityCollection loops; ExtrusionEntityCollection loops;
ExtrusionEntityCollection gap_fill; ExtrusionEntityCollection gap_fill;
SurfaceCollection fill_surfaces; ExPolygons fill_expolygons;
Flow flow(1., 1., 1.); Flow flow(1., 1., 1.);
PerimeterGenerator::Parameters perimeter_generator_params( PerimeterGenerator::Parameters perimeter_generator_params(
1., // layer height 1., // layer height
@ -67,7 +67,7 @@ SCENARIO("Perimeter nesting", "[Perimeters]")
// cache: // cache:
lower_layer_polygons_cache, lower_layer_polygons_cache,
// output: // output:
loops, gap_fill, fill_surfaces); loops, gap_fill, fill_expolygons);
THEN("expected number of collections") { THEN("expected number of collections") {
REQUIRE(loops.entities.size() == data.expolygons.size()); REQUIRE(loops.entities.size() == data.expolygons.size());

View File

@ -46,11 +46,7 @@ set(XS_XSP_FILES
${XSP_DIR}/BoundingBox.xsp ${XSP_DIR}/BoundingBox.xsp
${XSP_DIR}/Config.xsp ${XSP_DIR}/Config.xsp
${XSP_DIR}/ExPolygon.xsp ${XSP_DIR}/ExPolygon.xsp
${XSP_DIR}/ExtrusionEntityCollection.xsp
${XSP_DIR}/ExtrusionLoop.xsp
${XSP_DIR}/ExtrusionPath.xsp
${XSP_DIR}/Geometry.xsp ${XSP_DIR}/Geometry.xsp
${XSP_DIR}/Layer.xsp
${XSP_DIR}/Line.xsp ${XSP_DIR}/Line.xsp
${XSP_DIR}/Model.xsp ${XSP_DIR}/Model.xsp
${XSP_DIR}/Point.xsp ${XSP_DIR}/Point.xsp

View File

@ -159,8 +159,6 @@ for my $class (qw(
Slic3r::ExtrusionPath Slic3r::ExtrusionPath
Slic3r::ExtrusionPath::Collection Slic3r::ExtrusionPath::Collection
Slic3r::Geometry::BoundingBox Slic3r::Geometry::BoundingBox
Slic3r::Layer
Slic3r::Layer::Region
Slic3r::Line Slic3r::Line
Slic3r::Model Slic3r::Model
Slic3r::Model::Instance Slic3r::Model::Instance

View File

@ -8,8 +8,6 @@ REGISTER_CLASS(ExtrusionPath, "ExtrusionPath");
REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop"); REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop");
REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection"); REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection");
REGISTER_CLASS(GCode, "GCode"); REGISTER_CLASS(GCode, "GCode");
REGISTER_CLASS(Layer, "Layer");
REGISTER_CLASS(LayerRegion, "Layer::Region");
REGISTER_CLASS(Line, "Line"); REGISTER_CLASS(Line, "Line");
REGISTER_CLASS(Polygon, "Polygon"); REGISTER_CLASS(Polygon, "Polygon");
REGISTER_CLASS(Polyline, "Polyline"); REGISTER_CLASS(Polyline, "Polyline");

View File

@ -1,36 +0,0 @@
#!/usr/bin/perl
use strict;
use warnings;
use Slic3r::XS;
use Test::More tests => 5;
my $points = [
[100, 100],
[200, 100],
[200, 200],
];
my $path = Slic3r::ExtrusionPath->new(
polyline => Slic3r::Polyline->new(@$points),
role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
mm3_per_mm => 1,
);
$path->reverse;
is_deeply $path->polyline->pp, [ reverse @$points ], 'reverse path';
$path->append([ 150, 150 ]);
is scalar(@$path), 4, 'append to path';
$path->pop_back;
is scalar(@$path), 3, 'pop_back from path';
ok $path->first_point->coincides_with($path->polyline->[0]), 'first_point';
$path = $path->clone;
is $path->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'role';
__END__

View File

@ -1,157 +0,0 @@
#!/usr/bin/perl
use strict;
use warnings;
use List::Util qw(sum);
use Slic3r::XS;
use Test::More tests => 46;
{
my $square = [
[100, 100],
[200, 100],
[200, 200],
[100, 200],
];
my $square_p = Slic3r::Polygon->new(@$square);
my $loop = Slic3r::ExtrusionLoop->new;
$loop->append(Slic3r::ExtrusionPath->new(
polyline => $square_p->split_at_first_point,
role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
mm3_per_mm => 1,
));
isa_ok $loop, 'Slic3r::ExtrusionLoop';
isa_ok $loop->polygon, 'Slic3r::Polygon', 'loop polygon';
is $loop->polygon->area, $square_p->area, 'polygon area';
is $loop->length, $square_p->length(), 'loop length';
$loop = $loop->clone;
is scalar(@$loop), 1, 'loop contains one path';
{
my $path = $loop->[0];
is $path->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'role';
}
$loop->split_at_vertex($square_p->[2]);
is scalar(@$loop), 1, 'splitting a single-path loop results in a single path';
is scalar(@{$loop->[0]->polyline}), 5, 'path has correct number of points';
ok $loop->[0]->polyline->[0]->coincides_with($square_p->[2]), 'expected point order';
ok $loop->[0]->polyline->[1]->coincides_with($square_p->[3]), 'expected point order';
ok $loop->[0]->polyline->[2]->coincides_with($square_p->[0]), 'expected point order';
ok $loop->[0]->polyline->[3]->coincides_with($square_p->[1]), 'expected point order';
ok $loop->[0]->polyline->[4]->coincides_with($square_p->[2]), 'expected point order';
}
{
my $polyline1 = Slic3r::Polyline->new([100,100], [200,100], [200,200]);
my $polyline2 = Slic3r::Polyline->new([200,200], [100,200], [100,100]);
my $loop = Slic3r::ExtrusionLoop->new_from_paths(
Slic3r::ExtrusionPath->new(
polyline => $polyline1,
role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
mm3_per_mm => 1,
),
Slic3r::ExtrusionPath->new(
polyline => $polyline2,
role => Slic3r::ExtrusionPath::EXTR_ROLE_OVERHANG_PERIMETER,
mm3_per_mm => 1,
),
);
my $tot_len = sum($polyline1->length, $polyline2->length);
is $loop->length, $tot_len, 'length';
is scalar(@$loop), 2, 'loop contains two paths';
{
# check splitting at intermediate point
my $loop2 = $loop->clone;
isa_ok $loop2, 'Slic3r::ExtrusionLoop';
$loop2->split_at_vertex($polyline1->[1]);
is $loop2->length, $tot_len, 'length after splitting is unchanged';
is scalar(@$loop2), 3, 'loop contains three paths after splitting';
ok $loop2->[0]->polyline->[0]->coincides_with($polyline1->[1]), 'expected starting point';
ok $loop2->[-1]->polyline->[-1]->coincides_with($polyline1->[1]), 'expected ending point';
ok $loop2->[0]->polyline->[-1]->coincides_with($loop2->[1]->polyline->[0]), 'paths have common point';
ok $loop2->[1]->polyline->[-1]->coincides_with($loop2->[2]->polyline->[0]), 'paths have common point';
is $loop2->[0]->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'expected order after splitting';
is $loop2->[1]->role, Slic3r::ExtrusionPath::EXTR_ROLE_OVERHANG_PERIMETER, 'expected order after splitting';
is $loop2->[2]->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'expected order after splitting';
is scalar(@{$loop2->[0]->polyline}), 2, 'path has correct number of points';
is scalar(@{$loop2->[1]->polyline}), 3, 'path has correct number of points';
is scalar(@{$loop2->[2]->polyline}), 2, 'path has correct number of points';
my @paths = @{$loop2->clip_end(3)};
is sum(map $_->length, @paths), $loop2->length - 3, 'returned paths have expected length';
}
{
# check splitting at endpoint
my $loop2 = $loop->clone;
$loop2->split_at_vertex($polyline2->[0]);
is $loop2->length, $tot_len, 'length after splitting is unchanged';
is scalar(@$loop2), 2, 'loop contains two paths after splitting';
ok $loop2->[0]->polyline->[0]->coincides_with($polyline2->[0]), 'expected starting point';
ok $loop2->[-1]->polyline->[-1]->coincides_with($polyline2->[0]), 'expected ending point';
ok $loop2->[0]->polyline->[-1]->coincides_with($loop2->[1]->polyline->[0]), 'paths have common point';
ok $loop2->[1]->polyline->[-1]->coincides_with($loop2->[0]->polyline->[0]), 'paths have common point';
is $loop2->[0]->role, Slic3r::ExtrusionPath::EXTR_ROLE_OVERHANG_PERIMETER, 'expected order after splitting';
is $loop2->[1]->role, Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, 'expected order after splitting';
is scalar(@{$loop2->[0]->polyline}), 3, 'path has correct number of points';
is scalar(@{$loop2->[1]->polyline}), 3, 'path has correct number of points';
}
{
my $loop2 = $loop->clone;
my $point = Slic3r::Point->new(250,150);
$loop2->split_at($point);
is $loop2->length, $tot_len, 'length after splitting is unchanged';
is scalar(@$loop2), 3, 'loop contains three paths after splitting';
my $expected_start_point = Slic3r::Point->new(200,150);
ok $loop2->[0]->polyline->[0]->coincides_with($expected_start_point), 'expected starting point';
ok $loop2->[-1]->polyline->[-1]->coincides_with($expected_start_point), 'expected ending point';
}
}
{
my @polylines = (
Slic3r::Polyline->new([59312736,4821067],[64321068,4821067],[64321068,4821067],[64321068,9321068],[59312736,9321068]),
Slic3r::Polyline->new([59312736,9321068],[9829401,9321068]),
Slic3r::Polyline->new([9829401,9321068],[4821067,9321068],[4821067,4821067],[9829401,4821067]),
Slic3r::Polyline->new([9829401,4821067],[59312736,4821067]),
);
my $loop = Slic3r::ExtrusionLoop->new;
$loop->append($_) for (
Slic3r::ExtrusionPath->new(polyline => $polylines[0], role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, mm3_per_mm => 1),
Slic3r::ExtrusionPath->new(polyline => $polylines[1], role => Slic3r::ExtrusionPath::EXTR_ROLE_OVERHANG_PERIMETER, mm3_per_mm => 1),
Slic3r::ExtrusionPath->new(polyline => $polylines[2], role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, mm3_per_mm => 1),
Slic3r::ExtrusionPath->new(polyline => $polylines[3], role => Slic3r::ExtrusionPath::EXTR_ROLE_OVERHANG_PERIMETER, mm3_per_mm => 1),
);
my $len = $loop->length;
my $point = Slic3r::Point->new(4821067,9321068);
$loop->split_at_vertex($point) or $loop->split_at($point);
is $loop->length, $len, 'total length is preserved after splitting';
is_deeply [ map $_->role, @$loop ], [
Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
Slic3r::ExtrusionPath::EXTR_ROLE_OVERHANG_PERIMETER,
Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
Slic3r::ExtrusionPath::EXTR_ROLE_OVERHANG_PERIMETER,
Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
], 'order is correctly preserved after splitting';
}
{
my $loop = Slic3r::ExtrusionLoop->new;
$loop->append(Slic3r::ExtrusionPath->new(
polyline => Slic3r::Polyline->new([15896783,15868739],[24842049,12117558],[33853238,15801279],[37591780,24780128],[37591780,24844970],[33853231,33825297],[24842049,37509013],[15896798,33757841],[12211841,24812544],[15896783,15868739]),
role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, mm3_per_mm => 1
));
my $len = $loop->length;
$loop->split_at(Slic3r::Point->new(15896783,15868739));
is $loop->length, $len, 'split_at() preserves total length';
}
__END__

View File

@ -1,91 +0,0 @@
#!/usr/bin/perl
use strict;
use warnings;
use Slic3r::XS;
use Test::More tests => 13;
my $points = [
[100, 100],
[200, 100],
[200, 200],
];
my $path = Slic3r::ExtrusionPath->new(
polyline => Slic3r::Polyline->new(@$points),
role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER,
mm3_per_mm => 1,
);
my $loop = Slic3r::ExtrusionLoop->new_from_paths(
Slic3r::ExtrusionPath->new(
polyline => Slic3r::Polygon->new(@$points)->split_at_first_point,
role => Slic3r::ExtrusionPath::EXTR_ROLE_FILL,
mm3_per_mm => 1,
),
);
my $collection = Slic3r::ExtrusionPath::Collection->new(
$path,
);
isa_ok $collection, 'Slic3r::ExtrusionPath::Collection', 'collection object with items in constructor';
ok !$collection->no_sort, 'no_sort is false by default';
$collection->append($collection);
is scalar(@$collection), 2, 'append ExtrusionPath::Collection';
$collection->append($path);
is scalar(@$collection), 3, 'append ExtrusionPath';
$collection->append($loop);
is scalar(@$collection), 4, 'append ExtrusionLoop';
is scalar(@{$collection->[1]}), 1, 'appended collection was duplicated';
{
my $collection_loop = $collection->[3];
$collection_loop->polygon->scale(2);
is_deeply $collection->[3]->polygon->pp, $collection_loop->polygon->pp, 'items are returned by reference';
}
{
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_from';
is_deeply
[ map $_->y, map @{$_->polyline}, @{$collection->chained_path(0)} ],
[15, 18, 20, 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_from';
$collection->no_sort(1);
my @foo = @{$collection->chained_path(0)};
pass 'chained_path with no_sort';
}
{
my $coll2 = $collection->clone;
ok !$coll2->no_sort, 'expected no_sort value';
$coll2->no_sort(1);
ok $coll2->clone->no_sort, 'no_sort is kept after clone';
}
__END__

View File

@ -1,102 +0,0 @@
%module{Slic3r::XS};
%{
#include <xsinit.h>
#include "libslic3r/ExtrusionEntityCollection.hpp"
%}
%name{Slic3r::ExtrusionPath::Collection} class ExtrusionEntityCollection {
%name{_new} ExtrusionEntityCollection();
~ExtrusionEntityCollection();
Clone<ExtrusionEntityCollection> clone()
%code{% RETVAL = (ExtrusionEntityCollection*)THIS->clone(); %};
void reverse();
void clear();
ExtrusionEntityCollection* chained_path(bool no_reverse, ExtrusionRole role = erMixed)
%code{%
if (no_reverse)
croak("no_reverse must be false");
RETVAL = new ExtrusionEntityCollection();
*RETVAL = THIS->chained_path_from(THIS->entities.front()->first_point());
%};
ExtrusionEntityCollection* chained_path_from(Point* start_near, bool no_reverse, ExtrusionRole role = erMixed)
%code{%
if (no_reverse)
croak("no_reverse must be false");
RETVAL = new ExtrusionEntityCollection();
*RETVAL = THIS->chained_path_from(*start_near, role);
%};
Clone<Point> first_point();
Clone<Point> last_point();
int count()
%code{% RETVAL = THIS->entities.size(); %};
int items_count()
%code{% RETVAL = THIS->items_count(); %};
ExtrusionEntityCollection* flatten()
%code{%
RETVAL = new ExtrusionEntityCollection();
*RETVAL = THIS->flatten();
%};
double min_mm3_per_mm();
bool empty()
%code{% RETVAL = THIS->entities.empty(); %};
Polygons polygons_covered_by_width();
Polygons polygons_covered_by_spacing();
%{
SV*
ExtrusionEntityCollection::arrayref()
CODE:
AV* av = newAV();
av_fill(av, THIS->entities.size()-1);
int i = 0;
for (ExtrusionEntitiesPtr::iterator it = THIS->entities.begin(); it != THIS->entities.end(); ++it) {
SV* sv = newSV(0);
// return our item by reference
if (ExtrusionPath* path = dynamic_cast<ExtrusionPath*>(*it)) {
sv_setref_pv( sv, perl_class_name_ref(path), path );
} else if (ExtrusionLoop* loop = dynamic_cast<ExtrusionLoop*>(*it)) {
sv_setref_pv( sv, perl_class_name_ref(loop), loop );
} else if (ExtrusionEntityCollection* collection = dynamic_cast<ExtrusionEntityCollection*>(*it)) {
sv_setref_pv( sv, perl_class_name_ref(collection), collection );
} else {
croak("Unexpected type in ExtrusionEntityCollection");
}
av_store(av, i++, sv);
}
RETVAL = newRV_noinc((SV*)av);
OUTPUT:
RETVAL
void
ExtrusionEntityCollection::append(...)
CODE:
for (unsigned int i = 1; i < items; i++) {
if(!sv_isobject( ST(i) ) || (SvTYPE(SvRV( ST(i) )) != SVt_PVMG)) {
croak("Argument %d is not object", i);
}
ExtrusionEntity* entity = (ExtrusionEntity *)SvIV((SV*)SvRV( ST(i) ));
// append COPIES
if (ExtrusionPath* path = dynamic_cast<ExtrusionPath*>(entity)) {
THIS->entities.push_back( new ExtrusionPath(*path) );
} else if (ExtrusionLoop* loop = dynamic_cast<ExtrusionLoop*>(entity)) {
THIS->entities.push_back( new ExtrusionLoop(*loop) );
} else if(ExtrusionEntityCollection* collection = dynamic_cast<ExtrusionEntityCollection*>(entity)) {
THIS->entities.push_back( collection->clone() );
} else {
croak("Argument %d is of unknown type", i);
}
}
bool
ExtrusionEntityCollection::no_sort(...)
CODE:
if (items > 1) {
THIS->no_sort = SvTRUE(ST(1));
}
RETVAL = THIS->no_sort;
OUTPUT:
RETVAL
%}
};

View File

@ -1,65 +0,0 @@
%module{Slic3r::XS};
%{
#include <xsinit.h>
#include "libslic3r/ExtrusionEntity.hpp"
%}
%name{Slic3r::ExtrusionLoop} class ExtrusionLoop {
ExtrusionLoop();
~ExtrusionLoop();
Clone<ExtrusionLoop> clone()
%code{% RETVAL = THIS; %};
void reverse();
bool make_clockwise();
bool make_counter_clockwise();
Clone<Point> first_point();
Clone<Point> last_point();
Clone<Polygon> polygon();
void append(ExtrusionPath* path)
%code{% THIS->paths.push_back(*path); %};
double length();
bool split_at_vertex(Point* point)
%code{% RETVAL = THIS->split_at_vertex(*point); %};
void split_at(Point* point, int prefer_non_overhang = 0, double scaled_epsilon = 0.)
%code{% THIS->split_at(*point, prefer_non_overhang != 0, scaled_epsilon); %};
ExtrusionPaths clip_end(double distance)
%code{% THIS->clip_end(distance, &RETVAL); %};
bool has_overhang_point(Point* point)
%code{% RETVAL = THIS->has_overhang_point(*point); %};
ExtrusionRole role() const;
ExtrusionLoopRole loop_role() const;
Polygons polygons_covered_by_width();
Polygons polygons_covered_by_spacing();
%{
SV*
ExtrusionLoop::arrayref()
CODE:
AV* av = newAV();
av_fill(av, THIS->paths.size()-1);
for (ExtrusionPaths::iterator it = THIS->paths.begin(); it != THIS->paths.end(); ++it) {
av_store(av, it - THIS->paths.begin(), perl_to_SV_ref(*it));
}
RETVAL = newRV_noinc((SV*)av);
OUTPUT:
RETVAL
%}
};
%package{Slic3r::ExtrusionLoop};
%{
IV
_constant()
ALIAS:
EXTRL_ROLE_DEFAULT = elrDefault
EXTRL_ROLE_CONTOUR_INTERNAL_PERIMETER = elrContourInternalPerimeter
EXTRL_ROLE_SKIRT = elrSkirt
PROTOTYPE:
CODE:
RETVAL = ix;
OUTPUT: RETVAL
%}

View File

@ -1,126 +0,0 @@
%module{Slic3r::XS};
%{
#include <xsinit.h>
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/ExtrusionEntityCollection.hpp"
%}
%name{Slic3r::ExtrusionPath} class ExtrusionPath {
~ExtrusionPath();
SV* arrayref()
%code{% RETVAL = to_AV(&THIS->polyline); %};
SV* pp()
%code{% RETVAL = to_SV_pureperl(&THIS->polyline); %};
void pop_back()
%code{% THIS->polyline.points.pop_back(); %};
void reverse();
Lines lines()
%code{% RETVAL = THIS->polyline.lines(); %};
Clone<Point> first_point();
Clone<Point> last_point();
void clip_end(double distance);
void simplify(double tolerance);
double length();
ExtrusionRole role() const;
bool is_bridge()
%code{% RETVAL = is_bridge(THIS->role()); %};
Polygons polygons_covered_by_width();
Polygons polygons_covered_by_spacing();
%{
ExtrusionPath*
_new(CLASS, polyline_sv, role, mm3_per_mm, width, height)
char* CLASS;
SV* polyline_sv;
ExtrusionRole role;
double mm3_per_mm;
float width;
float height;
CODE:
RETVAL = new ExtrusionPath (role);
from_SV_check(polyline_sv, &RETVAL->polyline);
RETVAL->mm3_per_mm = mm3_per_mm;
RETVAL->width = width;
RETVAL->height = height;
OUTPUT:
RETVAL
Ref<Polyline>
ExtrusionPath::polyline(...)
CODE:
if (items > 1) {
from_SV_check(ST(1), &THIS->polyline);
}
RETVAL = &(THIS->polyline);
OUTPUT:
RETVAL
double
ExtrusionPath::mm3_per_mm(...)
CODE:
if (items > 1) {
THIS->mm3_per_mm = (double)SvNV(ST(1));
}
RETVAL = THIS->mm3_per_mm;
OUTPUT:
RETVAL
float
ExtrusionPath::width(...)
CODE:
if (items > 1) {
THIS->width = (float)SvNV(ST(1));
}
RETVAL = THIS->width;
OUTPUT:
RETVAL
float
ExtrusionPath::height(...)
CODE:
if (items > 1) {
THIS->height = (float)SvNV(ST(1));
}
RETVAL = THIS->height;
OUTPUT:
RETVAL
void
ExtrusionPath::append(...)
CODE:
for (unsigned int i = 1; i < items; i++) {
Point p;
from_SV_check(ST(i), &p);
THIS->polyline.points.push_back(p);
}
%}
};
%package{Slic3r::ExtrusionPath};
%{
IV
_constant()
ALIAS:
EXTR_ROLE_NONE = erNone
EXTR_ROLE_PERIMETER = erPerimeter
EXTR_ROLE_EXTERNAL_PERIMETER = erExternalPerimeter
EXTR_ROLE_OVERHANG_PERIMETER = erOverhangPerimeter
EXTR_ROLE_FILL = erInternalInfill
EXTR_ROLE_SOLIDFILL = erSolidInfill
EXTR_ROLE_TOPSOLIDFILL = erTopSolidInfill
EXTR_ROLE_BRIDGE = erBridgeInfill
EXTR_ROLE_GAPFILL = erGapFill
EXTR_ROLE_SKIRT = erSkirt
EXTR_ROLE_SUPPORTMATERIAL = erSupportMaterial
EXTR_ROLE_SUPPORTMATERIAL_INTERFACE = erSupportMaterialInterface
EXTR_ROLE_MIXED = erMixed
PROTOTYPE:
CODE:
RETVAL = ix;
OUTPUT: RETVAL
%}

View File

@ -1,72 +0,0 @@
%module{Slic3r::XS};
%{
#include <xsinit.h>
#include "libslic3r/Layer.hpp"
%}
%name{Slic3r::Layer::Region} class LayerRegion {
// owned by Layer, no constructor/destructor
Ref<Layer> layer();
Ref<PrintRegion> region()
%code%{ RETVAL = &THIS->region(); %};
Ref<SurfaceCollection> slices()
%code%{ RETVAL = const_cast<SurfaceCollection*>(&THIS->slices()); %};
Ref<ExtrusionEntityCollection> thin_fills()
%code%{ RETVAL = const_cast<ExtrusionEntityCollection*>(&THIS->thin_fills()); %};
Ref<SurfaceCollection> fill_surfaces()
%code%{ RETVAL = const_cast<SurfaceCollection*>(&THIS->fill_surfaces()); %};
Ref<ExtrusionEntityCollection> perimeters()
%code%{ RETVAL = const_cast<ExtrusionEntityCollection*>(&THIS->perimeters()); %};
Ref<ExtrusionEntityCollection> fills()
%code%{ RETVAL = const_cast<ExtrusionEntityCollection*>(&THIS->fills()); %};
void prepare_fill_surfaces();
void make_perimeters(SurfaceCollection* slices, SurfaceCollection* fill_surfaces)
%code%{ THIS->make_perimeters(*slices, fill_surfaces); %};
double infill_area_threshold();
void export_region_slices_to_svg(const char *path) const;
void export_region_fill_surfaces_to_svg(const char *path) const;
void export_region_slices_to_svg_debug(const char *name) const;
void export_region_fill_surfaces_to_svg_debug(const char *name) const;
};
%name{Slic3r::Layer} class Layer {
// owned by PrintObject, no constructor/destructor
Ref<Layer> as_layer()
%code%{ RETVAL = THIS; %};
int id();
void set_id(int id);
Ref<PrintObject> object();
bool slicing_errors()
%code%{ RETVAL = THIS->slicing_errors; %};
coordf_t slice_z()
%code%{ RETVAL = THIS->slice_z; %};
coordf_t print_z()
%code%{ RETVAL = THIS->print_z; %};
coordf_t height()
%code%{ RETVAL = THIS->height; %};
size_t region_count();
Ref<LayerRegion> get_region(int idx);
Ref<LayerRegion> add_region(PrintRegion* print_region);
int ptr()
%code%{ RETVAL = (int)(intptr_t)THIS; %};
void make_slices();
void backup_untyped_slices();
void restore_untyped_slices();
void make_perimeters();
void make_fills();
void export_region_slices_to_svg(const char *path);
void export_region_fill_surfaces_to_svg(const char *path);
void export_region_slices_to_svg_debug(const char *name);
void export_region_fill_surfaces_to_svg_debug(const char *name);
};

View File

@ -21,9 +21,6 @@
%code%{ RETVAL = &THIS->config(); %}; %code%{ RETVAL = &THIS->config(); %};
Clone<BoundingBox> bounding_box(); Clone<BoundingBox> bounding_box();
size_t layer_count();
Ref<Layer> get_layer(int idx);
void slice(); void slice();
}; };
@ -35,10 +32,6 @@
%code%{ RETVAL = const_cast<Model*>(&THIS->model()); %}; %code%{ RETVAL = const_cast<Model*>(&THIS->model()); %};
Ref<StaticPrintConfig> config() Ref<StaticPrintConfig> config()
%code%{ RETVAL = const_cast<GCodeConfig*>(static_cast<const GCodeConfig*>(&THIS->config())); %}; %code%{ RETVAL = const_cast<GCodeConfig*>(static_cast<const GCodeConfig*>(&THIS->config())); %};
Ref<ExtrusionEntityCollection> skirt()
%code%{ RETVAL = const_cast<ExtrusionEntityCollection*>(&THIS->skirt()); %};
Ref<ExtrusionEntityCollection> brim()
%code%{ RETVAL = const_cast<ExtrusionEntityCollection*>(&THIS->brim()); %};
double total_used_filament() double total_used_filament()
%code%{ RETVAL = THIS->print_statistics().total_used_filament; %}; %code%{ RETVAL = THIS->print_statistics().total_used_filament; %};
double total_extruded_volume() double total_extruded_volume()

View File

@ -70,18 +70,6 @@ ExPolygon* O_OBJECT_SLIC3R
Ref<ExPolygon> O_OBJECT_SLIC3R_T Ref<ExPolygon> O_OBJECT_SLIC3R_T
Clone<ExPolygon> O_OBJECT_SLIC3R_T Clone<ExPolygon> O_OBJECT_SLIC3R_T
ExtrusionEntityCollection* O_OBJECT_SLIC3R
Ref<ExtrusionEntityCollection> O_OBJECT_SLIC3R_T
Clone<ExtrusionEntityCollection> O_OBJECT_SLIC3R_T
ExtrusionPath* O_OBJECT_SLIC3R
Ref<ExtrusionPath> O_OBJECT_SLIC3R_T
Clone<ExtrusionPath> O_OBJECT_SLIC3R_T
ExtrusionLoop* O_OBJECT_SLIC3R
Ref<ExtrusionLoop> O_OBJECT_SLIC3R_T
Clone<ExtrusionLoop> O_OBJECT_SLIC3R_T
Surface* O_OBJECT_SLIC3R Surface* O_OBJECT_SLIC3R
Ref<Surface> O_OBJECT_SLIC3R_T Ref<Surface> O_OBJECT_SLIC3R_T
Clone<Surface> O_OBJECT_SLIC3R_T Clone<Surface> O_OBJECT_SLIC3R_T
@ -119,12 +107,6 @@ Print* O_OBJECT_SLIC3R
Ref<Print> O_OBJECT_SLIC3R_T Ref<Print> O_OBJECT_SLIC3R_T
Clone<Print> O_OBJECT_SLIC3R_T Clone<Print> O_OBJECT_SLIC3R_T
LayerRegion* O_OBJECT_SLIC3R
Ref<LayerRegion> O_OBJECT_SLIC3R_T
Layer* O_OBJECT_SLIC3R
Ref<Layer> O_OBJECT_SLIC3R_T
Axis T_UV Axis T_UV
ExtrusionLoopRole T_UV ExtrusionLoopRole T_UV
ExtrusionRole T_UV ExtrusionRole T_UV
@ -137,7 +119,6 @@ Lines T_ARRAYREF
Polygons T_ARRAYREF Polygons T_ARRAYREF
Polylines T_ARRAYREF Polylines T_ARRAYREF
ExPolygons T_ARRAYREF ExPolygons T_ARRAYREF
ExtrusionPaths T_ARRAYREF
Surfaces T_ARRAYREF Surfaces T_ARRAYREF
# we return these types whenever we want the items to be returned # we return these types whenever we want the items to be returned
@ -149,7 +130,6 @@ ModelVolumePtrs* T_PTR_ARRAYREF_PTR
ModelInstancePtrs* T_PTR_ARRAYREF_PTR ModelInstancePtrs* T_PTR_ARRAYREF_PTR
PrintRegionPtrs* T_PTR_ARRAYREF_PTR PrintRegionPtrs* T_PTR_ARRAYREF_PTR
PrintObjectPtrs* T_PTR_ARRAYREF_PTR PrintObjectPtrs* T_PTR_ARRAYREF_PTR
LayerPtrs* T_PTR_ARRAYREF_PTR
# we return these types whenever we want the items to be returned # we return these types whenever we want the items to be returned
# by reference and not marked ::Ref because they're newly allocated # by reference and not marked ::Ref because they're newly allocated

View File

@ -53,15 +53,6 @@
%typemap{Polygon*}; %typemap{Polygon*};
%typemap{Ref<Polygon>}{simple}; %typemap{Ref<Polygon>}{simple};
%typemap{Clone<Polygon>}{simple}; %typemap{Clone<Polygon>}{simple};
%typemap{ExtrusionEntityCollection*};
%typemap{Ref<ExtrusionEntityCollection>}{simple};
%typemap{Clone<ExtrusionEntityCollection>}{simple};
%typemap{ExtrusionPath*};
%typemap{Ref<ExtrusionPath>}{simple};
%typemap{Clone<ExtrusionPath>}{simple};
%typemap{ExtrusionLoop*};
%typemap{Ref<ExtrusionLoop>}{simple};
%typemap{Clone<ExtrusionLoop>}{simple};
%typemap{TriangleMesh*}; %typemap{TriangleMesh*};
%typemap{Ref<TriangleMesh>}{simple}; %typemap{Ref<TriangleMesh>}{simple};
%typemap{Clone<TriangleMesh>}{simple}; %typemap{Clone<TriangleMesh>}{simple};
@ -86,19 +77,12 @@
%typemap{Ref<Print>}{simple}; %typemap{Ref<Print>}{simple};
%typemap{Clone<Print>}{simple}; %typemap{Clone<Print>}{simple};
%typemap{LayerRegion*};
%typemap{Ref<LayerRegion>}{simple};
%typemap{Layer*};
%typemap{Ref<Layer>}{simple};
%typemap{Points}; %typemap{Points};
%typemap{Pointfs}; %typemap{Pointfs};
%typemap{Lines}; %typemap{Lines};
%typemap{Polygons}; %typemap{Polygons};
%typemap{Polylines}; %typemap{Polylines};
%typemap{ExPolygons}; %typemap{ExPolygons};
%typemap{ExtrusionPaths};
%typemap{Surfaces}; %typemap{Surfaces};
%typemap{Polygons*}; %typemap{Polygons*};
%typemap{TriangleMesh*}; %typemap{TriangleMesh*};