From d041fa6c0ccbebfc3ee967597b3ccd1213f32d2f Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 27 Oct 2022 19:08:43 +0200 Subject: [PATCH] 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. --- lib/Slic3r/Print/Object.pm | 5 - src/libslic3r/Layer.cpp | 12 +- src/libslic3r/Layer.hpp | 2 +- src/libslic3r/LayerRegion.cpp | 6 +- src/libslic3r/PerimeterGenerator.cpp | 14 +- src/libslic3r/PerimeterGenerator.hpp | 4 +- src/libslic3r/libslic3r.h | 9 +- t/combineinfill.t | 110 --------- tests/fff_print/test_extrusion_entity.cpp | 279 +++++++++++++++++++++- tests/fff_print/test_fill.cpp | 129 +++++++++- tests/fff_print/test_perimeters.cpp | 4 +- xs/CMakeLists.txt | 4 - xs/lib/Slic3r/XS.pm | 2 - xs/src/perlglue.cpp | 2 - xs/t/07_extrusionpath.t | 36 --- xs/t/08_extrusionloop.t | 157 ------------ xs/t/12_extrusionpathcollection.t | 91 ------- xs/xsp/ExtrusionEntityCollection.xsp | 102 -------- xs/xsp/ExtrusionLoop.xsp | 65 ----- xs/xsp/ExtrusionPath.xsp | 126 ---------- xs/xsp/Layer.xsp | 72 ------ xs/xsp/Print.xsp | 7 - xs/xsp/my.map | 20 -- xs/xsp/typemap.xspt | 16 -- 24 files changed, 422 insertions(+), 852 deletions(-) delete mode 100644 t/combineinfill.t delete mode 100644 xs/t/07_extrusionpath.t delete mode 100644 xs/t/08_extrusionloop.t delete mode 100644 xs/t/12_extrusionpathcollection.t delete mode 100644 xs/xsp/ExtrusionEntityCollection.xsp delete mode 100644 xs/xsp/ExtrusionLoop.xsp delete mode 100644 xs/xsp/ExtrusionPath.xsp delete mode 100644 xs/xsp/Layer.xsp diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index b4e53245a..d5c19e4a4 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -6,9 +6,4 @@ use warnings; use List::Util qw(min max sum first); use Slic3r::Surface ':types'; -sub layers { - my $self = shift; - return [ map $self->get_layer($_), 0..($self->layer_count - 1) ]; -} - 1; diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index 3bd2566a5..82c82372b 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -426,12 +426,12 @@ void Layer::make_perimeters() } } - SurfaceCollection fill_surfaces; + ExPolygons fill_expolygons; if (layerms.size() == 1) { // optimization (*layerm)->m_fill_expolygons.clear(); (*layerm)->m_fill_surfaces.clear(); - (*layerm)->make_perimeters((*layerm)->slices(), &fill_surfaces); - (*layerm)->m_fill_expolygons = to_expolygons(fill_surfaces.surfaces); + (*layerm)->make_perimeters((*layerm)->slices(), fill_expolygons); + (*layerm)->m_fill_expolygons = std::move(fill_expolygons); } else { 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. @@ -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()); } // make perimeters - layerm_config->make_perimeters(new_slices, &fill_surfaces); + layerm_config->make_perimeters(new_slices, fill_expolygons); // assign fill_surfaces to each layer - if (! fill_surfaces.empty()) { + if (! fill_expolygons.empty()) { // Separate the fill surfaces. 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); } } } diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 9a1a77aea..0fda3a6b2 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -67,7 +67,7 @@ public: void slices_to_fill_surfaces_clipped(); 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); double infill_area_threshold() const; // Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer. diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 555da2146..4b346647b 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -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_thin_fills.clear(); @@ -100,7 +100,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec // output: m_perimeters, m_thin_fills, - *fill_surfaces); + fill_expolygons); else PerimeterGenerator::process_classic( // input: @@ -111,7 +111,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec // output: m_perimeters, m_thin_fills, - *fill_surfaces); + fill_expolygons); } //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3. diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 2856323a4..27bc0e9d7 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -611,7 +611,7 @@ void PerimeterGenerator::process_arachne( // Gaps without the thin walls ExtrusionEntityCollection &out_gap_fill, // Infills without the gap fills - SurfaceCollection &out_fill_surfaces) + ExPolygons &out_fill_expolygons) { // other perimeters coord_t perimeter_spacing = params.perimeter_flow.scaled_spacing(); @@ -817,12 +817,11 @@ void PerimeterGenerator::process_arachne( // collapse too narrow infill areas const auto min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE)); // append infill areas to fill_surfaces - out_fill_surfaces.append( + append(out_fill_expolygons, offset2_ex( union_ex(pp), float(- min_perimeter_infill_spacing / 2.), - float(inset + min_perimeter_infill_spacing / 2.)), - stInternal); + float(inset + min_perimeter_infill_spacing / 2.))); } } @@ -839,7 +838,7 @@ void PerimeterGenerator::process_classic( // Gaps without the thin walls ExtrusionEntityCollection &out_gap_fill, // Infills without the gap fills - SurfaceCollection &out_fill_surfaces) + ExPolygons &out_fill_expolygons) { // other perimeters coord_t perimeter_width = params.perimeter_flow.scaled_width(); @@ -1094,12 +1093,11 @@ void PerimeterGenerator::process_classic( // collapse too narrow infill areas coord_t min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE)); // append infill areas to fill_surfaces - out_fill_surfaces.append( + append(out_fill_expolygons, offset2_ex( union_ex(pp), float(- inset - min_perimeter_infill_spacing / 2.), - float(min_perimeter_infill_spacing / 2.)), - stInternal); + float(min_perimeter_infill_spacing / 2.))); } // for each island } diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index 92710c4ee..de52dd89a 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -77,7 +77,7 @@ void process_classic( // Gaps without the thin walls ExtrusionEntityCollection &out_gap_fill, // Infills without the gap fills - SurfaceCollection &out_fill_surfaces); + ExPolygons &out_fill_expolygons); void process_arachne( // Inputs: @@ -92,7 +92,7 @@ void process_arachne( // Gaps without the thin walls ExtrusionEntityCollection &out_gap_fill, // 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); diff --git a/src/libslic3r/libslic3r.h b/src/libslic3r/libslic3r.h index 2285c29a6..cd1b41c38 100644 --- a/src/libslic3r/libslic3r.h +++ b/src/libslic3r/libslic3r.h @@ -32,12 +32,13 @@ #include "Technologies.hpp" #include "Semver.hpp" +using coord_t = #if 1 // 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 -//FIXME At least FillRectilinear2 and std::boost Voronoi require coord_t to be 32bit. -typedef int64_t coord_t; + //FIXME At least FillRectilinear2 and std::boost Voronoi require coord_t to be 32bit. + int64_t; #endif using coordf_t = double; @@ -366,4 +367,4 @@ inline IntegerOnly fast_round_up(double a) } // namespace Slic3r -#endif +#endif // _libslic3r_h_ diff --git a/t/combineinfill.t b/t/combineinfill.t deleted file mode 100644 index ebb430419..000000000 --- a/t/combineinfill.t +++ /dev/null @@ -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__ diff --git a/tests/fff_print/test_extrusion_entity.cpp b/tests/fff_print/test_extrusion_entity.cpp index c98e0ec1a..56588d98a 100644 --- a/tests/fff_print/test_extrusion_entity.cpp +++ b/tests/fff_print/test_extrusion_entity.cpp @@ -35,7 +35,254 @@ static Slic3r::ExtrusionPaths random_paths(size_t count = 10, size_t length = 20 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(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(dynamic_cast(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(dynamic_cast(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(dynamic_cast(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(dynamic_cast(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(dynamic_cast(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(collection.entities[1])->entities.size() == 1); + } + WHEN("cloned") { + auto coll2 = std::unique_ptr(dynamic_cast(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(dynamic_cast(coll2->clone())); + assert(coll3->no_sort); + } + } +} + +SCENARIO("ExtrusionEntityCollection: Polygon flattening", "[ExtrusionEntity]") +{ srand(0xDEADBEEF); // consistent seed for test reproducibility. // 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,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} }, { {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} }, { {15,0}, {10,0}, {4,0} } }, - { 30,0 } + { 30, 0 } }, }; for (const Test &test : tests) { @@ -132,12 +379,24 @@ TEST_CASE("ExtrusionEntityCollection: Chained path", "[ExtrusionEntity]") { 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(chained_extrusions.entities[i])->polyline.points; - REQUIRE(p1 == p2); + THEN("Chaining works") { + 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(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(chained_extrusions.entities[i])->polyline.points; + REQUIRE(p1 == p2); + } } } } diff --git a/tests/fff_print/test_fill.cpp b/tests/fff_print/test_fill.cpp index 75eb16a97..af59fca2a 100644 --- a/tests/fff_print/test_fill.cpp +++ b/tests/fff_print/test_fill.cpp @@ -3,14 +3,17 @@ #include #include +#include "libslic3r/libslic3r.h" + #include "libslic3r/ClipperUtils.hpp" #include "libslic3r/Fill/Fill.hpp" #include "libslic3r/Flow.hpp" +#include "libslic3r/Layer.hpp" #include "libslic3r/Geometry.hpp" #include "libslic3r/Geometry/ConvexHull.hpp" +#include "libslic3r/Point.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/SVG.hpp" -#include "libslic3r/libslic3r.h" #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 layers; // layer_z => 1 + std::map 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]") { WHEN("20mm cube is sliced") { diff --git a/tests/fff_print/test_perimeters.cpp b/tests/fff_print/test_perimeters.cpp index 8fc75e744..decdb4802 100644 --- a/tests/fff_print/test_perimeters.cpp +++ b/tests/fff_print/test_perimeters.cpp @@ -44,7 +44,7 @@ SCENARIO("Perimeter nesting", "[Perimeters]") ExtrusionEntityCollection loops; ExtrusionEntityCollection gap_fill; - SurfaceCollection fill_surfaces; + ExPolygons fill_expolygons; Flow flow(1., 1., 1.); PerimeterGenerator::Parameters perimeter_generator_params( 1., // layer height @@ -67,7 +67,7 @@ SCENARIO("Perimeter nesting", "[Perimeters]") // cache: lower_layer_polygons_cache, // output: - loops, gap_fill, fill_surfaces); + loops, gap_fill, fill_expolygons); THEN("expected number of collections") { REQUIRE(loops.entities.size() == data.expolygons.size()); diff --git a/xs/CMakeLists.txt b/xs/CMakeLists.txt index efc07af31..237fb0a9a 100644 --- a/xs/CMakeLists.txt +++ b/xs/CMakeLists.txt @@ -46,11 +46,7 @@ set(XS_XSP_FILES ${XSP_DIR}/BoundingBox.xsp ${XSP_DIR}/Config.xsp ${XSP_DIR}/ExPolygon.xsp - ${XSP_DIR}/ExtrusionEntityCollection.xsp - ${XSP_DIR}/ExtrusionLoop.xsp - ${XSP_DIR}/ExtrusionPath.xsp ${XSP_DIR}/Geometry.xsp - ${XSP_DIR}/Layer.xsp ${XSP_DIR}/Line.xsp ${XSP_DIR}/Model.xsp ${XSP_DIR}/Point.xsp diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index 87fb267c5..de3ff0102 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -159,8 +159,6 @@ for my $class (qw( Slic3r::ExtrusionPath Slic3r::ExtrusionPath::Collection Slic3r::Geometry::BoundingBox - Slic3r::Layer - Slic3r::Layer::Region Slic3r::Line Slic3r::Model Slic3r::Model::Instance diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index 8484b1d64..62a80d018 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -8,8 +8,6 @@ REGISTER_CLASS(ExtrusionPath, "ExtrusionPath"); REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop"); REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection"); REGISTER_CLASS(GCode, "GCode"); -REGISTER_CLASS(Layer, "Layer"); -REGISTER_CLASS(LayerRegion, "Layer::Region"); REGISTER_CLASS(Line, "Line"); REGISTER_CLASS(Polygon, "Polygon"); REGISTER_CLASS(Polyline, "Polyline"); diff --git a/xs/t/07_extrusionpath.t b/xs/t/07_extrusionpath.t deleted file mode 100644 index 084b4f03e..000000000 --- a/xs/t/07_extrusionpath.t +++ /dev/null @@ -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__ diff --git a/xs/t/08_extrusionloop.t b/xs/t/08_extrusionloop.t deleted file mode 100644 index 3abfbd728..000000000 --- a/xs/t/08_extrusionloop.t +++ /dev/null @@ -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__ diff --git a/xs/t/12_extrusionpathcollection.t b/xs/t/12_extrusionpathcollection.t deleted file mode 100644 index e02854245..000000000 --- a/xs/t/12_extrusionpathcollection.t +++ /dev/null @@ -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__ diff --git a/xs/xsp/ExtrusionEntityCollection.xsp b/xs/xsp/ExtrusionEntityCollection.xsp deleted file mode 100644 index 1c3337303..000000000 --- a/xs/xsp/ExtrusionEntityCollection.xsp +++ /dev/null @@ -1,102 +0,0 @@ -%module{Slic3r::XS}; - -%{ -#include -#include "libslic3r/ExtrusionEntityCollection.hpp" -%} - -%name{Slic3r::ExtrusionPath::Collection} class ExtrusionEntityCollection { - %name{_new} ExtrusionEntityCollection(); - ~ExtrusionEntityCollection(); - Clone 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 first_point(); - Clone 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(*it)) { - sv_setref_pv( sv, perl_class_name_ref(path), path ); - } else if (ExtrusionLoop* loop = dynamic_cast(*it)) { - sv_setref_pv( sv, perl_class_name_ref(loop), loop ); - } else if (ExtrusionEntityCollection* collection = dynamic_cast(*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(entity)) { - THIS->entities.push_back( new ExtrusionPath(*path) ); - } else if (ExtrusionLoop* loop = dynamic_cast(entity)) { - THIS->entities.push_back( new ExtrusionLoop(*loop) ); - } else if(ExtrusionEntityCollection* collection = dynamic_cast(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 - -%} -}; diff --git a/xs/xsp/ExtrusionLoop.xsp b/xs/xsp/ExtrusionLoop.xsp deleted file mode 100644 index ded17cb19..000000000 --- a/xs/xsp/ExtrusionLoop.xsp +++ /dev/null @@ -1,65 +0,0 @@ -%module{Slic3r::XS}; - -%{ -#include -#include "libslic3r/ExtrusionEntity.hpp" -%} - -%name{Slic3r::ExtrusionLoop} class ExtrusionLoop { - ExtrusionLoop(); - ~ExtrusionLoop(); - Clone clone() - %code{% RETVAL = THIS; %}; - void reverse(); - bool make_clockwise(); - bool make_counter_clockwise(); - Clone first_point(); - Clone last_point(); - Clone 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 - -%} diff --git a/xs/xsp/ExtrusionPath.xsp b/xs/xsp/ExtrusionPath.xsp deleted file mode 100644 index 1dbc00917..000000000 --- a/xs/xsp/ExtrusionPath.xsp +++ /dev/null @@ -1,126 +0,0 @@ -%module{Slic3r::XS}; - -%{ -#include -#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 first_point(); - Clone 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 -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 - -%} - diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp deleted file mode 100644 index da2962c1e..000000000 --- a/xs/xsp/Layer.xsp +++ /dev/null @@ -1,72 +0,0 @@ -%module{Slic3r::XS}; - -%{ -#include -#include "libslic3r/Layer.hpp" -%} - -%name{Slic3r::Layer::Region} class LayerRegion { - // owned by Layer, no constructor/destructor - - Ref layer(); - Ref region() - %code%{ RETVAL = &THIS->region(); %}; - - Ref slices() - %code%{ RETVAL = const_cast(&THIS->slices()); %}; - Ref thin_fills() - %code%{ RETVAL = const_cast(&THIS->thin_fills()); %}; - Ref fill_surfaces() - %code%{ RETVAL = const_cast(&THIS->fill_surfaces()); %}; - Ref perimeters() - %code%{ RETVAL = const_cast(&THIS->perimeters()); %}; - Ref fills() - %code%{ RETVAL = const_cast(&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 as_layer() - %code%{ RETVAL = THIS; %}; - - int id(); - void set_id(int id); - Ref 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 get_region(int idx); - Ref 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); -}; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 6b6f6e9be..9379aea20 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -21,9 +21,6 @@ %code%{ RETVAL = &THIS->config(); %}; Clone bounding_box(); - size_t layer_count(); - Ref get_layer(int idx); - void slice(); }; @@ -35,10 +32,6 @@ %code%{ RETVAL = const_cast(&THIS->model()); %}; Ref config() %code%{ RETVAL = const_cast(static_cast(&THIS->config())); %}; - Ref skirt() - %code%{ RETVAL = const_cast(&THIS->skirt()); %}; - Ref brim() - %code%{ RETVAL = const_cast(&THIS->brim()); %}; double total_used_filament() %code%{ RETVAL = THIS->print_statistics().total_used_filament; %}; double total_extruded_volume() diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 42ca74292..dcf20bdab 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -70,18 +70,6 @@ ExPolygon* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T -ExtrusionEntityCollection* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T -Clone O_OBJECT_SLIC3R_T - -ExtrusionPath* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T -Clone O_OBJECT_SLIC3R_T - -ExtrusionLoop* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T -Clone O_OBJECT_SLIC3R_T - Surface* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T @@ -119,12 +107,6 @@ Print* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T -LayerRegion* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T - -Layer* O_OBJECT_SLIC3R -Ref O_OBJECT_SLIC3R_T - Axis T_UV ExtrusionLoopRole T_UV ExtrusionRole T_UV @@ -137,7 +119,6 @@ Lines T_ARRAYREF Polygons T_ARRAYREF Polylines T_ARRAYREF ExPolygons T_ARRAYREF -ExtrusionPaths T_ARRAYREF Surfaces T_ARRAYREF # 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 PrintRegionPtrs* 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 # by reference and not marked ::Ref because they're newly allocated diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index bf1e8e2ac..e5bba9812 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -53,15 +53,6 @@ %typemap{Polygon*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; -%typemap{ExtrusionEntityCollection*}; -%typemap{Ref}{simple}; -%typemap{Clone}{simple}; -%typemap{ExtrusionPath*}; -%typemap{Ref}{simple}; -%typemap{Clone}{simple}; -%typemap{ExtrusionLoop*}; -%typemap{Ref}{simple}; -%typemap{Clone}{simple}; %typemap{TriangleMesh*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; @@ -86,19 +77,12 @@ %typemap{Ref}{simple}; %typemap{Clone}{simple}; -%typemap{LayerRegion*}; -%typemap{Ref}{simple}; - -%typemap{Layer*}; -%typemap{Ref}{simple}; - %typemap{Points}; %typemap{Pointfs}; %typemap{Lines}; %typemap{Polygons}; %typemap{Polylines}; %typemap{ExPolygons}; -%typemap{ExtrusionPaths}; %typemap{Surfaces}; %typemap{Polygons*}; %typemap{TriangleMesh*};