diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 7f7b589d0..a4146f169 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -15,20 +15,4 @@ sub config { return $self->object->config; } -sub region { - my $self = shift; - my ($region_id) = @_; - - while ($self->region_count <= $region_id) { - $self->add_region($self->object->print->get_region($self->region_count)); - } - - return $self->get_region($region_id); -} - -sub regions { - my ($self) = @_; - return [ map $self->get_region($_), 0..($self->region_count-1) ]; -} - 1; diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index ab12bc8ce..0c1060b7d 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -220,6 +220,16 @@ public: config.set_deserialize_strict(items); return config; } + static DynamicPrintConfig new_with(const t_config_option_key &opt_key, const std::string &str, bool append = false) { + DynamicPrintConfig config; + config.set_deserialize_strict(opt_key, str, append); + return config; + } + static DynamicPrintConfig new_with(std::initializer_list items) { + DynamicPrintConfig config; + config.set_deserialize_strict(items); + return config; + } static DynamicPrintConfig* new_from_defaults_keys(const std::vector &keys); // Overrides ConfigBase::def(). Static configuration definition. Any value stored into this ConfigBase shall have its definition here. @@ -1131,6 +1141,8 @@ public: void set(const std::string &opt_key, T value) { m_data.set(opt_key, value, true); this->touch(); } void set_deserialize(const t_config_option_key &opt_key, const std::string &str, ConfigSubstitutionContext &substitution_context, bool append = false) { m_data.set_deserialize(opt_key, str, substitution_context, append); this->touch(); } + void set_deserialize_strict(const t_config_option_key &opt_key, const std::string &str, bool append = false) + { m_data.set_deserialize_strict(opt_key, str, append); this->touch(); } bool erase(const t_config_option_key &opt_key) { bool out = m_data.erase(opt_key); if (out) this->touch(); return out; } // Getters are thread safe. diff --git a/t/custom_gcode.t b/t/custom_gcode.t deleted file mode 100644 index 1bb52b618..000000000 --- a/t/custom_gcode.t +++ /dev/null @@ -1,210 +0,0 @@ -use Test::More tests => 38; -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::Test; - -{ - my $config = Slic3r::Config::new_from_defaults; - - my $test = sub { - my ($conf) = @_; - $conf ||= $config; - - my $print = Slic3r::Test::init_print('2x20x10', config => $conf); - - my $last_move_was_z_change = 0; - Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { - my ($self, $cmd, $args, $info) = @_; - - if ($last_move_was_z_change && $cmd ne $config->layer_gcode) { - fail 'custom layer G-code was not applied after Z change'; - } - if (!$last_move_was_z_change && $cmd eq $config->layer_gcode) { - fail 'custom layer G-code was not applied after Z change'; - } - - $last_move_was_z_change = (defined $info->{dist_Z} && $info->{dist_Z} > 0); - }); - - 1; - }; - - $config->set('start_gcode', '_MY_CUSTOM_START_GCODE_'); # to avoid dealing with the nozzle lift in start G-code - $config->set('layer_gcode', '_MY_CUSTOM_LAYER_GCODE_'); - ok $test->(), "custom layer G-code is applied after Z move and before other moves"; -} - -#========================================================== - -{ - my $config = Slic3r::Config::new_from_defaults; - $config->set('output_filename_format', 'ts_[travel_speed]_lh_[layer_height].gcode'); - $config->set('start_gcode', "TRAVEL:[travel_speed] HEIGHT:[layer_height]\n"); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - - my $output_file = $print->print->output_filepath; - my ($t, $h) = map $config->$_, qw(travel_speed layer_height); - ok $output_file =~ /ts_${t}_/, 'print config options are replaced in output filename'; - ok $output_file =~ /lh_$h\./, 'region config options are replaced in output filename'; - - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /TRAVEL:$t/, 'print config options are replaced in custom G-code'; - ok $gcode =~ /HEIGHT:$h/, 'region config options are replaced in custom G-code'; -} - -{ - my $config = Slic3r::Config->new; - $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]); - $config->set('extruder', 2); - $config->set('first_layer_temperature', [200,205]); - - { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /M104 S205 T1/, 'temperature set correctly for non-zero yet single extruder'; - ok $gcode !~ /M104 S\d+ T0/, 'unused extruder correctly ignored'; - } - - $config->set('infill_extruder', 1); - { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /M104 S200 T0/, 'temperature set correctly for first extruder'; - ok $gcode =~ /M104 S205 T1/, 'temperature set correctly for second extruder'; - } - - my @start_gcode = (qq! -;__temp0:[first_layer_temperature_0]__ -;__temp1:[first_layer_temperature_1]__ -;__temp2:[first_layer_temperature_2]__ - !, qq! -;__temp0:{first_layer_temperature[0]}__ -;__temp1:{first_layer_temperature[1]}__ -;__temp2:{first_layer_temperature[2]}__ - !); - my @syntax_description = (' (legacy syntax)', ' (new syntax)'); - for my $i (0, 1) { - $config->set('start_gcode', $start_gcode[$i]); - { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - # we use the [infill_extruder] placeholder to make sure this test doesn't - # catch a false positive caused by the unparsed start G-code option itself - # being embedded in the G-code - ok $gcode =~ /temp0:200/, 'temperature placeholder for first extruder correctly populated' . $syntax_description[$i]; - ok $gcode =~ /temp1:205/, 'temperature placeholder for second extruder correctly populated' . $syntax_description[$i]; - ok $gcode =~ /temp2:200/, 'temperature placeholder for unused extruder populated with first value' . $syntax_description[$i]; - } - } - - $config->set('start_gcode', qq! -;substitution:{if infill_extruder==1}extruder1 - {elsif infill_extruder==2}extruder2 - {else}extruder3{endif} - !); - { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /substitution:extruder1/, 'if / else / endif - first block returned'; - } -} - -{ - my $config = Slic3r::Config::new_from_defaults; - $config->set('before_layer_gcode', ';BEFORE [layer_num]'); - $config->set('layer_gcode', ';CHANGE [layer_num]'); - $config->set('support_material', 1); - $config->set('layer_height', 0.2); - my $print = Slic3r::Test::init_print('overhang', config => $config); - my $gcode = Slic3r::Test::gcode($print); - - my @before = (); - my @change = (); - foreach my $line (split /\R+/, $gcode) { - if ($line =~ /;BEFORE (\d+)/) { - push @before, $1; - } elsif ($line =~ /;CHANGE (\d+)/) { - push @change, $1; - fail 'inconsistent layer_num before and after layer change' - if $1 != $before[-1]; - } - } - is_deeply \@before, \@change, 'layer_num is consistent before and after layer changes'; - ok !defined(first { $change[$_] != $change[$_-1]+1 } 1..$#change), - 'layer_num grows continously'; # i.e. no duplicates or regressions -} - -{ - my $config = Slic3r::Config->new; - $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6,0.6]); - $config->set('start_gcode', qq! -;substitution:{if infill_extruder==1}if block - {elsif infill_extruder==2}elsif block 1 - {elsif infill_extruder==3}elsif block 2 - {elsif infill_extruder==4}elsif block 3 - {else}endif block{endif} - !); - my @returned = ('', 'if block', 'elsif block 1', 'elsif block 2', 'elsif block 3', 'endif block'); - for my $i (1,2,3,4,5) { - $config->set('infill_extruder', $i); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - my $found_other = 0; - for my $j (1,2,3,4,5) { - next if $i == $j; - $found_other = 1 if $gcode =~ /substitution:$returned[$j]/; - } - ok $gcode =~ /substitution:$returned[$i]/, 'if / else / endif - ' . $returned[$i] . ' returned'; - ok !$found_other, 'if / else / endif - only ' . $returned[$i] . ' returned'; - } -} - -{ - my $config = Slic3r::Config->new; - $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]); - $config->set('start_gcode', - ';substitution:{if infill_extruder==1}{if perimeter_extruder==1}block11{else}block12{endif}' . - '{elsif infill_extruder==2}{if perimeter_extruder==1}block21{else}block22{endif}' . - '{else}{if perimeter_extruder==1}block31{else}block32{endif}{endif}:end'); - for my $i (1,2,3) { - $config->set('infill_extruder', $i); - for my $j (1,2) { - $config->set('perimeter_extruder', $j); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /substitution:block$i$j:end/, "two level if / else / endif - block$i$j returned"; - } - } -} - -{ - my $config = Slic3r::Config->new; - $config->set('start_gcode', - ';substitution:{if notes=="MK2"}MK2{elsif notes=="MK3"}MK3{else}MK1{endif}:end'); - for my $printer_name ("MK2", "MK3", "MK1") { - $config->set('notes', $printer_name); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /substitution:$printer_name:end/, "printer name $printer_name matched"; - } -} - -{ - my $config = Slic3r::Config::new_from_defaults; - $config->set('complete_objects', 1); - $config->set('between_objects_gcode', '_MY_CUSTOM_GCODE_'); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 3); - my $gcode = Slic3r::Test::gcode($print); - is scalar(() = $gcode =~ /^_MY_CUSTOM_GCODE_/gm), 2, 'between_objects_gcode is applied correctly'; -} - -__END__ diff --git a/t/print.t b/t/print.t deleted file mode 100644 index 2144e80c1..000000000 --- a/t/print.t +++ /dev/null @@ -1,65 +0,0 @@ -use Test::More tests => 6; -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::Geometry qw(unscale X Y); -use Slic3r::Test; - -{ - my $config = Slic3r::Config::new_from_defaults; - my $print_center = [100,100]; - my $print = Slic3r::Test::init_print('20mm_cube', config => $config, print_center => $print_center); - my @extrusion_points = (); - Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { - my ($self, $cmd, $args, $info) = @_; - - if ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) { - push @extrusion_points, my $point = Slic3r::Point->new_scale($args->{X}, $args->{Y}); - } - }); - my $bb = Slic3r::Geometry::BoundingBox->new_from_points(\@extrusion_points); - my $center = $bb->center; - ok abs(unscale($center->[X]) - $print_center->[X]) < 0.005, 'print is centered around print_center (X)'; - ok abs(unscale($center->[Y]) - $print_center->[Y]) < 0.005, 'print is centered around print_center (Y)'; -} - -{ - # this represents the aggregate config from presets - my $config = Slic3r::Config::new_from_defaults; - # Define 4 extruders. - $config->set('nozzle_diameter', [0.4, 0.4, 0.4, 0.4]); - - # user adds one object to the plater - my $print = Slic3r::Test::init_print(my $model = Slic3r::Test::model('20mm_cube'), config => $config); - - # user sets a per-region option - my $model2 = $model->clone; - $model2->get_object(0)->config->set('fill_density', 100); - $print->apply($model2, $config); - - is $print->print->regions->[0]->config->fill_density, 100, 'region config inherits model object config'; - - # user exports G-code, thus the default config is reapplied - $model2->get_object(0)->config->erase('fill_density'); - $print->apply($model2, $config); - - is $print->print->regions->[0]->config->fill_density, 20, 'region config is resetted'; - - # user assigns object extruders - $model2->get_object(0)->config->set('extruder', 3); - $model2->get_object(0)->config->set('perimeter_extruder', 2); - $print->apply($model2, $config); - - is $print->print->regions->[0]->config->infill_extruder, 3, 'extruder setting is correctly expanded'; - is $print->print->regions->[0]->config->perimeter_extruder, 2, 'extruder setting does not override explicitely specified extruders'; -} - -__END__ diff --git a/tests/fff_print/test_custom_gcode.cpp b/tests/fff_print/test_custom_gcode.cpp index 0368b9604..37103b316 100644 --- a/tests/fff_print/test_custom_gcode.cpp +++ b/tests/fff_print/test_custom_gcode.cpp @@ -1,8 +1,11 @@ #include +#include #include #include +#include + #include "libslic3r/Config.hpp" #include "libslic3r/Print.hpp" #include "libslic3r/PrintConfig.hpp" @@ -12,11 +15,12 @@ using namespace Slic3r; -#if 0 SCENARIO("Output file format", "[CustomGCode]") { WHEN("output_file_format set") { auto config = Slic3r::DynamicPrintConfig::full_print_config_with({ + { "travel_speed", "130"}, + { "layer_height", "0.4"}, { "output_filename_format", "ts_[travel_speed]_lh_[layer_height].gcode" }, { "start_gcode", "TRAVEL:[travel_speed] HEIGHT:[layer_height]\n" } }); @@ -25,20 +29,9 @@ SCENARIO("Output file format", "[CustomGCode]") Model model; Test::init_print({ Test::TestMesh::cube_2x20x10 }, print, model, config); - std::string output_file = print.output_filepath(); + std::string output_file = print.output_filepath({}, {}); THEN("print config options are replaced in output filename") { - output_file.find(std::string("ts_") + ) - } - my ($t, $h) = map $config->$_, qw(travel_speed layer_height); - ok $output_file =~ /ts_${t}_/, ''; - ok $output_file =~ /lh_$h\./, 'region config options are replaced in output filename'; - - std::string gcode = print.gcode(print); - THEN("print config options are replaced in custom G-code") { - ok $gcode =~ /TRAVEL:$t/, ''; - } - THEN("region config options are replaced in custom G-code") { - ok $gcode =~ /HEIGHT:$h/, ''; + REQUIRE(output_file == "ts_130_lh_0.4.gcode"); } } } @@ -56,165 +49,213 @@ SCENARIO("Custom G-code", "[CustomGCode]") parser.parse_buffer(Slic3r::Test::slice({ Test::TestMesh::cube_2x20x10 }, config), [&last_move_was_z_change, &num_layer_changes_not_applied](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) { - if (line.extruding(self)) { - if (! was_extruding) - seam_points.emplace_back(self.xy_scaled()); - was_extruding = true; - } else if (! line.cmd_is("M73")) { - // skips remaining time lines (M73) - was_extruding = false; - } if (last_move_was_z_change != line.cmd_is("_MY_CUSTOM_LAYER_GCODE_")) ++ num_layer_changes_not_applied; last_move_was_z_change = line.dist_Z(self) > 0; }); - THEN("custom layer G-code is applied after Z move and before other moves"); + THEN("custom layer G-code is applied after Z move and before other moves") { + REQUIRE(num_layer_changes_not_applied == 0); + } }; -{ - my $config = Slic3r::Config->new; - $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]); - $config->set('extruder', 2); - $config->set('first_layer_temperature', [200,205]); + auto config = Slic3r::DynamicPrintConfig::new_with({ + { "nozzle_diameter", { 0.6,0.6,0.6,0.6 } }, + { "extruder", 2 }, + { "first_layer_temperature", { 200, 205 } } + }); + config.normalize_fdm(); + WHEN("Printing with single but non-zero extruder") { + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config); + THEN("temperature set correctly for non-zero yet single extruder") { + REQUIRE(Slic3r::Test::contains(gcode, "\nM104 S205 T1 ;")); + } + THEN("unused extruder correctly ignored") { + REQUIRE(! Slic3r::Test::contains_regex(gcode, "M104 S\\d+ T0")); + } + } + WHEN("Printing with two extruders") { + config.opt_int("infill_extruder") = 1; + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config); + THEN("temperature set correctly for first extruder") { + REQUIRE(Slic3r::Test::contains(gcode, "\nM104 S200 T0 ;")); + }; + THEN("temperature set correctly for second extruder") { + REQUIRE(Slic3r::Test::contains(gcode, "\nM104 S205 T1 ;")); + }; + } + auto test = [](DynamicPrintConfig &config) { + // we use the [infill_extruder] placeholder to make sure this test doesn't + // catch a false positive caused by the unparsed start G-code option itself + // being embedded in the G-code + config.opt_int("infill_extruder") = 1; + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config); + THEN("temperature placeholder for first extruder correctly populated") { + REQUIRE(Slic3r::Test::contains(gcode, "temp0:200")); + } + THEN("temperature placeholder for second extruder correctly populated") { + REQUIRE(Slic3r::Test::contains(gcode, "temp1:205")); + } + THEN("temperature placeholder for unused extruder populated with first value") { + REQUIRE(Slic3r::Test::contains(gcode, "temp2:200")); + } + }; + WHEN("legacy syntax") { + config.set_deserialize_strict("start_gcode", + ";__temp0:[first_layer_temperature_0]__\n" + ";__temp1:[first_layer_temperature_1]__\n" + ";__temp2:[first_layer_temperature_2]__\n"); + test(config); + } + WHEN("new syntax") { + config.set_deserialize_strict("start_gcode", + ";__temp0:{first_layer_temperature[0]}__\n" + ";__temp1:{first_layer_temperature[1]}__\n" + ";__temp2:{first_layer_temperature[2]}__\n"); + test(config); + } + WHEN("Vojtech's syntax") { + config.set_deserialize_strict({ + { "infill_extruder", 1 }, + { "start_gcode", + ";substitution:{if infill_extruder==1}extruder1" + "{elsif infill_extruder==2}extruder2" + "{else}extruder3{endif}" + } + }); + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config); + THEN("if / else / endif - first block returned") { + REQUIRE(Test::contains(gcode, "\n;substitution:extruder1\n")); + } + } + GIVEN("Layer change G-codes") { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /M104 S205 T1/, 'temperature set correctly for non-zero yet single extruder'; - ok $gcode !~ /M104 S\d+ T0/, 'unused extruder correctly ignored'; + auto config = Slic3r::DynamicPrintConfig::full_print_config_with({ + { "before_layer_gcode", ";BEFORE [layer_num]" }, + { "layer_gcode", ";CHANGE [layer_num]" }, + { "support_material", 1 }, + { "layer_height", 0.2 } + }); + WHEN("before and after layer change G-codes set") { + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::overhang }, config); + GCodeReader parser; + std::vector before; + std::vector change; + parser.parse_buffer(gcode, [&before, &change](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line){ + int d; + if (sscanf(line.raw().c_str(), ";BEFORE %d", &d) == 1) + before.emplace_back(d); + else if (sscanf(line.raw().c_str(), ";CHANGE %d", &d) == 1) { + change.emplace_back(d); + if (d != before.back()) + throw std::runtime_error("inconsistent layer_num before and after layer change"); + } + }); + THEN("layer_num is consistent before and after layer changes") { + REQUIRE(before == change); + } + THEN("layer_num grows continously") { + // i.e. no duplicates or regressions + bool successive = true; + for (size_t i = 1; i < change.size(); ++ i) + if (change[i - 1] + 1 != change[i]) + successive = false; + REQUIRE(successive); + } + } } - - $config->set('infill_extruder', 1); + GIVEN("if / elsif / elsif / elsif / else / endif") { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /M104 S200 T0/, 'temperature set correctly for first extruder'; - ok $gcode =~ /M104 S205 T1/, 'temperature set correctly for second extruder'; + auto config = Slic3r::DynamicPrintConfig::new_with({ + { "nozzle_diameter", { 0.6,0.6,0.6,0.6,0.6 } }, + { "start_gcode", + ";substitution:{if infill_extruder==1}if block" + "{elsif infill_extruder==2}elsif block 1" + "{elsif infill_extruder==3}elsif block 2" + "{elsif infill_extruder==4}elsif block 3" + "{else}endif block{endif}" + ":end" + } + }); + std::string returned[] = { "" /* indexed by one based extruder ID */, "if block", "elsif block 1", "elsif block 2", "elsif block 3", "endif block" }; + auto test = [&config, &returned](int i) { + config.set_deserialize_strict({ { "infill_extruder", i } }); + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config); + int found_error = 0; + for (int j = 1; j <= 5; ++ j) + if (i != j && Slic3r::Test::contains(gcode, std::string("substitution:") + returned[j] + ":end")) + // failure + ++ found_error; + THEN(std::string("if / else / endif returned ") + returned[i]) { + REQUIRE(Slic3r::Test::contains(gcode, std::string("substitution:") + returned[i] + ":end")); + } + THEN(std::string("if / else / endif - only ") + returned[i] + "returned") { + REQUIRE(found_error == 0); + } + }; + WHEN("infill_extruder == 1") { test(1); } + WHEN("infill_extruder == 2") { test(2); } + WHEN("infill_extruder == 3") { test(3); } + WHEN("infill_extruder == 4") { test(4); } + WHEN("infill_extruder == 5") { test(5); } } - - my @start_gcode = (qq! -;__temp0:[first_layer_temperature_0]__ -;__temp1:[first_layer_temperature_1]__ -;__temp2:[first_layer_temperature_2]__ - !, qq! -;__temp0:{first_layer_temperature[0]}__ -;__temp1:{first_layer_temperature[1]}__ -;__temp2:{first_layer_temperature[2]}__ - !); - my @syntax_description = (' (legacy syntax)', ' (new syntax)'); - for my $i (0, 1) { - $config->set('start_gcode', $start_gcode[$i]); - { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - # we use the [infill_extruder] placeholder to make sure this test doesn't - # catch a false positive caused by the unparsed start G-code option itself - # being embedded in the G-code - ok $gcode =~ /temp0:200/, 'temperature placeholder for first extruder correctly populated' . $syntax_description[$i]; - ok $gcode =~ /temp1:205/, 'temperature placeholder for second extruder correctly populated' . $syntax_description[$i]; - ok $gcode =~ /temp2:200/, 'temperature placeholder for unused extruder populated with first value' . $syntax_description[$i]; - } + GIVEN("nested if / if / else / endif") { + auto config = Slic3r::DynamicPrintConfig::full_print_config_with({ + { "nozzle_diameter", { 0.6,0.6,0.6,0.6,0.6 } }, + { "start_gcode", + ";substitution:{if infill_extruder==1}{if perimeter_extruder==1}block11{else}block12{endif}" + "{elsif infill_extruder==2}{if perimeter_extruder==1}block21{else}block22{endif}" + "{else}{if perimeter_extruder==1}block31{else}block32{endif}{endif}:end" + } + }); + auto test = [&config](int i) { + config.opt_int("infill_extruder") = i; + int failed = 0; + for (int j = 1; j <= 2; ++ j) { + config.opt_int("perimeter_extruder") = j; + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config); + if (! Slic3r::Test::contains(gcode, std::string("substitution:block") + std::to_string(i) + std::to_string(j) + ":end")) + ++ failed; + } + THEN(std::string("two level if / else / endif - block for infill_extruder ") + std::to_string(i) + "succeeded") { + REQUIRE(failed == 0); + } + }; + WHEN("infill_extruder == 1") { test(1); } + WHEN("infill_extruder == 2") { test(2); } + WHEN("infill_extruder == 3") { test(3); } } - - $config->set('start_gcode', qq! -;substitution:{if infill_extruder==1}extruder1 - {elsif infill_extruder==2}extruder2 - {else}extruder3{endif} - !); - { - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /substitution:extruder1/, 'if / else / endif - first block returned'; + GIVEN("printer type in notes") { + auto config = Slic3r::DynamicPrintConfig::new_with({ + { "start_gcode", + ";substitution:{if notes==\"MK2\"}MK2{elsif notes==\"MK3\"}MK3{else}MK1{endif}:end" + } + }); + auto test = [&config](const std::string &printer_name) { + config.set_deserialize_strict("notes", printer_name); + std::string gcode = Slic3r::Test::slice({ Slic3r::Test::TestMesh::cube_20x20x20 }, config); + THEN(std::string("printer name ") + printer_name + " matched") { + REQUIRE(Slic3r::Test::contains(gcode, std::string("substitution:") + printer_name + ":end")); + } + }; + WHEN("printer MK2") { test("MK2"); } + WHEN("printer MK3") { test("MK3"); } + WHEN("printer MK1") { test("MK1"); } } -} - -{ - my $config = Slic3r::Config::new_from_defaults; - $config->set('before_layer_gcode', ';BEFORE [layer_num]'); - $config->set('layer_gcode', ';CHANGE [layer_num]'); - $config->set('support_material', 1); - $config->set('layer_height', 0.2); - my $print = Slic3r::Test::init_print('overhang', config => $config); - my $gcode = Slic3r::Test::gcode($print); - - my @before = (); - my @change = (); - foreach my $line (split /\R+/, $gcode) { - if ($line =~ /;BEFORE (\d+)/) { - push @before, $1; - } elsif ($line =~ /;CHANGE (\d+)/) { - push @change, $1; - fail 'inconsistent layer_num before and after layer change' - if $1 != $before[-1]; - } - } - is_deeply \@before, \@change, 'layer_num is consistent before and after layer changes'; - ok !defined(first { $change[$_] != $change[$_-1]+1 } 1..$#change), - 'layer_num grows continously'; # i.e. no duplicates or regressions -} - -{ - my $config = Slic3r::Config->new; - $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6,0.6]); - $config->set('start_gcode', qq! -;substitution:{if infill_extruder==1}if block - {elsif infill_extruder==2}elsif block 1 - {elsif infill_extruder==3}elsif block 2 - {elsif infill_extruder==4}elsif block 3 - {else}endif block{endif} - !); - my @returned = ('', 'if block', 'elsif block 1', 'elsif block 2', 'elsif block 3', 'endif block'); - for my $i (1,2,3,4,5) { - $config->set('infill_extruder', $i); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - my $found_other = 0; - for my $j (1,2,3,4,5) { - next if $i == $j; - $found_other = 1 if $gcode =~ /substitution:$returned[$j]/; - } - ok $gcode =~ /substitution:$returned[$i]/, 'if / else / endif - ' . $returned[$i] . ' returned'; - ok !$found_other, 'if / else / endif - only ' . $returned[$i] . ' returned'; - } -} - -{ - my $config = Slic3r::Config->new; - $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]); - $config->set('start_gcode', - ';substitution:{if infill_extruder==1}{if perimeter_extruder==1}block11{else}block12{endif}' . - '{elsif infill_extruder==2}{if perimeter_extruder==1}block21{else}block22{endif}' . - '{else}{if perimeter_extruder==1}block31{else}block32{endif}{endif}:end'); - for my $i (1,2,3) { - $config->set('infill_extruder', $i); - for my $j (1,2) { - $config->set('perimeter_extruder', $j); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /substitution:block$i$j:end/, "two level if / else / endif - block$i$j returned"; + GIVEN("sequential print with between_objects_gcode") { + auto config = Slic3r::DynamicPrintConfig::full_print_config_with({ + { "complete_objects", 1 }, + { "between_objects_gcode", "_MY_CUSTOM_GCODE_" } + }); + std::string gcode = Slic3r::Test::slice( + // 3x 20mm box + { Slic3r::Test::TestMesh::cube_20x20x20, Slic3r::Test::TestMesh::cube_20x20x20, Slic3r::Test::TestMesh::cube_20x20x20 }, + config); + THEN("between_objects_gcode is applied correctly") { + const boost::regex expression("^_MY_CUSTOM_GCODE_"); + const std::ptrdiff_t match_count = + std::distance(boost::sregex_iterator(gcode.begin(), gcode.end(), expression), boost::sregex_iterator()); + REQUIRE(match_count == 2); } } } - -{ - my $config = Slic3r::Config->new; - $config->set('start_gcode', - ';substitution:{if notes=="MK2"}MK2{elsif notes=="MK3"}MK3{else}MK1{endif}:end'); - for my $printer_name ("MK2", "MK3", "MK1") { - $config->set('notes', $printer_name); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config); - my $gcode = Slic3r::Test::gcode($print); - ok $gcode =~ /substitution:$printer_name:end/, "printer name $printer_name matched"; - } -} - -{ - my $config = Slic3r::Config::new_from_defaults; - $config->set('complete_objects', 1); - $config->set('between_objects_gcode', '_MY_CUSTOM_GCODE_'); - my $print = Slic3r::Test::init_print('20mm_cube', config => $config, duplicate => 3); - my $gcode = Slic3r::Test::gcode($print); - is scalar(() = $gcode =~ /^_MY_CUSTOM_GCODE_/gm), 2, 'between_objects_gcode is applied correctly'; -} - -#endif \ No newline at end of file diff --git a/tests/fff_print/test_data.cpp b/tests/fff_print/test_data.cpp index a52583cfc..6d8756057 100644 --- a/tests/fff_print/test_data.cpp +++ b/tests/fff_print/test_data.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include using namespace std; @@ -228,6 +229,7 @@ void init_print(std::vector &&meshes, Slic3r::Print &print, Slic3r object->add_instance(); } arrange_objects(model, InfiniteBed{}, ArrangeParams{ scaled(min_object_distance(config))}); + model.center_instances_around_point({100, 100}); for (ModelObject *mo : model.objects) { mo->ensure_on_bed(); print.auto_assign_extruders(mo); @@ -352,6 +354,17 @@ std::string slice(std::initializer_list meshes, std::initializer_l return gcode(print); } +bool contains(const std::string &data, const std::string &pattern) +{ + return data.find(pattern) != data.npos; +} + +bool contains_regex(const std::string &data, const std::string &pattern) +{ + boost::regex re(pattern); + return boost::regex_match(data, re); +} + } } // namespace Slic3r::Test #include diff --git a/tests/fff_print/test_data.hpp b/tests/fff_print/test_data.hpp index b699e5e4e..fd110caf1 100644 --- a/tests/fff_print/test_data.hpp +++ b/tests/fff_print/test_data.hpp @@ -81,6 +81,9 @@ std::string slice(std::initializer_list meshes, const DynamicPrint std::string slice(std::initializer_list meshes, std::initializer_list config_items, bool comments = false); std::string slice(std::initializer_list meshes, std::initializer_list config_items, bool comments = false); +bool contains(const std::string &data, const std::string &pattern); +bool contains_regex(const std::string &data, const std::string &pattern); + } } // namespace Slic3r::Test diff --git a/tests/fff_print/test_print.cpp b/tests/fff_print/test_print.cpp index a139e4c2b..395f75841 100644 --- a/tests/fff_print/test_print.cpp +++ b/tests/fff_print/test_print.cpp @@ -126,3 +126,62 @@ SCENARIO("Print: Brim generation", "[Print]") { } } } + +SCENARIO("Ported from Perl", "[Print]") { + GIVEN("20mm cube") { + WHEN("Print center is set to 100x100 (test framework default)") { + auto config = Slic3r::DynamicPrintConfig::full_print_config(); + std::string gcode = Slic3r::Test::slice({ TestMesh::cube_20x20x20 }, config); + GCodeReader parser; + Points extrusion_points; + parser.parse_buffer(gcode, [&extrusion_points](Slic3r::GCodeReader &self, const Slic3r::GCodeReader::GCodeLine &line) + { + if (line.cmd_is("G1") && line.extruding(self) && line.dist_XY(self) > 0) + extrusion_points.emplace_back(line.new_XY_scaled(self)); + }); + Vec2d center = unscaled(BoundingBox(extrusion_points).center()); + THEN("print is centered around print_center") { + REQUIRE(is_approx(center.x(), 100.)); + REQUIRE(is_approx(center.y(), 100.)); + } + } + } + GIVEN("Model with multiple objects") { + auto config = Slic3r::DynamicPrintConfig::full_print_config_with({ + { "nozzle_diameter", { 0.4, 0.4, 0.4, 0.4 } } + }); + Print print; + Model model; + Slic3r::Test::init_print({ TestMesh::cube_20x20x20 }, print, model, config); + + // User sets a per-region option, also testing a deep copy of Model. + Model model2(model); + model2.objects.front()->config.set_deserialize_strict("fill_density", "100%"); + WHEN("fill_density overridden") { + print.apply(model2, config); + THEN("region config inherits model object config") { + REQUIRE(print.get_print_region(0).config().fill_density == 100); + } + } + + model2.objects.front()->config.erase("fill_density"); + WHEN("fill_density resetted") { + print.apply(model2, config); + THEN("region config is resetted") { + REQUIRE(print.get_print_region(0).config().fill_density == 20); + } + } + + WHEN("extruder is assigned") { + model2.objects.front()->config.set("extruder", 3); + model2.objects.front()->config.set("perimeter_extruder", 2); + print.apply(model2, config); + THEN("extruder setting is correctly expanded") { + REQUIRE(print.get_print_region(0).config().infill_extruder == 3); + } + THEN("extruder setting does not override explicitely specified extruders") { + REQUIRE(print.get_print_region(0).config().perimeter_extruder == 2); + } + } + } +} diff --git a/tests/fff_print/test_skirt_brim.cpp b/tests/fff_print/test_skirt_brim.cpp index 8f508f323..655517220 100644 --- a/tests/fff_print/test_skirt_brim.cpp +++ b/tests/fff_print/test_skirt_brim.cpp @@ -38,6 +38,7 @@ TEST_CASE("Skirt height is honored", "[Skirt]") { { "support_material_speed", 99 }, // avoid altering speeds unexpectedly { "cooling", false }, + // avoid altering speeds unexpectedly { "first_layer_speed", "100%" } }); diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 97cbdafe3..6b6f6e9be 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -60,8 +60,6 @@ size_t object_count() %code%{ RETVAL = THIS->objects().size(); %}; - PrintRegionPtrs* regions() - %code%{ RETVAL = const_cast(&THIS->print_regions_mutable()); %}; void auto_assign_extruders(ModelObject* model_object); std::string output_filepath(std::string path = "")