From 468d0eac94eacee5bc382a77229a33f376e39ed1 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Fri, 1 Jul 2016 11:45:28 +0200 Subject: [PATCH 001/225] Bugfix: use Lift-z option for 2. extruder #3385 --- xs/src/libslic3r/GCodeWriter.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xs/src/libslic3r/GCodeWriter.cpp b/xs/src/libslic3r/GCodeWriter.cpp index d256ab2af..3990e83ee 100644 --- a/xs/src/libslic3r/GCodeWriter.cpp +++ b/xs/src/libslic3r/GCodeWriter.cpp @@ -500,10 +500,10 @@ GCodeWriter::lift() // check whether the above/below conditions are met double target_lift = 0; { - double above = this->config.retract_lift_above.get_at(0); - double below = this->config.retract_lift_below.get_at(0); + double above = this->config.retract_lift_above.get_at(this->_extruder->id); + double below = this->config.retract_lift_below.get_at(this->_extruder->id); if (this->_pos.z >= above && (below == 0 || this->_pos.z <= below)) - target_lift = this->config.retract_lift.get_at(0); + target_lift = this->config.retract_lift.get_at(this->_extruder->id); } if (this->_lifted == 0 && target_lift > 0) { this->_lifted = target_lift; From c5c1cab4f90e3326aac68c3acd888578c49970a8 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Tue, 12 Jul 2016 19:26:17 -0500 Subject: [PATCH 002/225] Adds new has_heatbed option to capabilities to disable autogeneration of bed temp settings. --- lib/Slic3r/GUI/Tab.pm | 3 ++- lib/Slic3r/Print/GCode.pm | 6 +++--- xs/src/libslic3r/PrintConfig.cpp | 5 +++++ xs/src/libslic3r/PrintConfig.hpp | 2 ++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index fe4c7e452..bbdf5c2f9 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -990,7 +990,7 @@ sub build { my (%params) = @_; $self->init_config_options(qw( - bed_shape z_offset + bed_shape z_offset has_heatbed gcode_flavor use_relative_e_distances serial_port serial_speed octoprint_host octoprint_apikey @@ -1057,6 +1057,7 @@ sub build { ); $optgroup->append_single_option_line($option); } + $optgroup->append_single_option_line('has_heatbed'); $optgroup->on_change(sub { my ($opt_id) = @_; if ($opt_id eq 'extruders_count') { diff --git a/lib/Slic3r/Print/GCode.pm b/lib/Slic3r/Print/GCode.pm index 3c32875ed..798395ba3 100644 --- a/lib/Slic3r/Print/GCode.pm +++ b/lib/Slic3r/Print/GCode.pm @@ -162,7 +162,7 @@ sub export { if $self->config->cooling && $self->config->disable_fan_first_layers; # set bed temperature - if ((my $temp = $self->config->first_layer_bed_temperature) && $self->config->start_gcode !~ /M(?:190|140)/i) { + if ($self->config->has_heatbed && (my $temp = $self->config->first_layer_bed_temperature) && $self->config->start_gcode !~ /M(?:190|140)/i) { printf $fh $gcodegen->writer->set_bed_temperature($temp, 1); } @@ -264,7 +264,7 @@ sub export { # is triggered, so machine has more time to reach such temperatures if ($layer->id == 0 && $finished_objects > 0) { printf $fh $gcodegen->writer->set_bed_temperature($self->config->first_layer_bed_temperature), - if $self->config->first_layer_bed_temperature; + if $self->config->first_layer_bed_temperature && $self->config->has_heatbed; $self->_print_first_layer_temperature(0); } $self->process_layer($layer, [$copy]); @@ -375,7 +375,7 @@ sub process_layer { if $temperature && $temperature != $self->config->get_at('first_layer_temperature', $extruder->id); } $gcode .= $self->_gcodegen->writer->set_bed_temperature($self->print->config->bed_temperature) - if $self->print->config->bed_temperature && $self->print->config->bed_temperature != $self->print->config->first_layer_bed_temperature; + if $self->config->has_heatbed && $self->print->config->bed_temperature && $self->print->config->bed_temperature != $self->print->config->first_layer_bed_temperature; $self->_second_layer_things_done(1); } diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 90af82fbd..3fed57549 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -24,6 +24,11 @@ PrintConfigDef::PrintConfigDef() opt->values.push_back(Pointf(0,200)); def->default_value = opt; } + def = this->add("has_heatbed", coBool); + def->label = "Has heated bed"; + def->tooltip = "Unselecting this will suppress automatic generation of bed heating gcode."; + def->cli = "has_heatbed!"; + def->default_value = new ConfigOptionBool(true); def = this->add("bed_temperature", coInt); def->label = "Other layers"; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 442af51c2..5e62bf278 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -318,6 +318,7 @@ class PrintConfig : public GCodeConfig public: ConfigOptionBool avoid_crossing_perimeters; ConfigOptionPoints bed_shape; + ConfigOptionBool has_heatbed; ConfigOptionInt bed_temperature; ConfigOptionFloat bridge_acceleration; ConfigOptionInt bridge_fan_speed; @@ -374,6 +375,7 @@ class PrintConfig : public GCodeConfig virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(avoid_crossing_perimeters); OPT_PTR(bed_shape); + OPT_PTR(has_heatbed); OPT_PTR(bed_temperature); OPT_PTR(bridge_acceleration); OPT_PTR(bridge_fan_speed); From e0d8101627659ef2dcf1c49d5c9b512eb4e31a46 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sat, 16 Jul 2016 09:52:11 -0500 Subject: [PATCH 003/225] Added Repetier FW flavor, fixed accel gcode generation for it. Addresses #3426 --- README.md | 2 +- slic3r.pl | 2 +- utils/zsh/functions/_slic3r | 2 +- xs/src/libslic3r/GCodeWriter.cpp | 9 +++++++-- xs/src/libslic3r/PrintConfig.cpp | 4 +++- xs/src/libslic3r/PrintConfig.hpp | 3 ++- 6 files changed, 15 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9c34e7be7..f84b6edac 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ The author of the Silk icon set is Mark James. (default: 100,100) --z-offset Additional height in mm to add to vertical coordinates (+/-, default: 0) - --gcode-flavor The type of G-code to generate (reprap/teacup/makerware/sailfish/mach3/machinekit/smoothie/no-extrusion, + --gcode-flavor The type of G-code to generate (reprap/teacup/repetier/makerware/sailfish/mach3/machinekit/smoothie/no-extrusion, default: reprap) --use-relative-e-distances Enable this to get relative E values (default: no) --use-firmware-retraction Enable firmware-controlled retraction using G10/G11 (default: no) diff --git a/slic3r.pl b/slic3r.pl index c634e7272..1893de033 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -296,7 +296,7 @@ $j (default: 100,100) --z-offset Additional height in mm to add to vertical coordinates (+/-, default: $config->{z_offset}) - --gcode-flavor The type of G-code to generate (reprap/teacup/makerware/sailfish/mach3/machinekit/smoothie/no-extrusion, + --gcode-flavor The type of G-code to generate (reprap/teacup/repetier/makerware/sailfish/mach3/machinekit/smoothie/no-extrusion, default: $config->{gcode_flavor}) --use-relative-e-distances Enable this to get relative E values (default: no) --use-firmware-retraction Enable firmware-controlled retraction using G10/G11 (default: no) diff --git a/utils/zsh/functions/_slic3r b/utils/zsh/functions/_slic3r index df1cecb03..a78da948a 100644 --- a/utils/zsh/functions/_slic3r +++ b/utils/zsh/functions/_slic3r @@ -22,7 +22,7 @@ _arguments -S \ '*--nozzle-diameter[specify nozzle diameter]:nozzle diameter in mm' \ '--print-center[specify print center coordinates]:print center coordinates in mm,mm' \ '--z-offset[specify Z-axis offset]:Z-axis offset in mm' \ - '--gcode-flavor[specify the type of G-code to generate]:G-code flavor:(reprap teacup makerware sailfish mach3 machinekit no-extrusion)' \ + '--gcode-flavor[specify the type of G-code to generate]:G-code flavor:(reprap teacup repetier makerware sailfish mach3 machinekit no-extrusion)' \ '(--use-relative-e-distances --no-use-relative-e-distances)'--{no-,}use-relative-e-distances'[disable/enable relative E values]' \ '--extrusion-axis[specify letter associated with the extrusion axis]:extrusion axis letter' \ '(--gcode-arcs --no-gcode-arcs)'--{no-,}gcode-arcs'[disable/enable G2/G3 commands for native arcs]' \ diff --git a/xs/src/libslic3r/GCodeWriter.cpp b/xs/src/libslic3r/GCodeWriter.cpp index d256ab2af..7c2b17a14 100644 --- a/xs/src/libslic3r/GCodeWriter.cpp +++ b/xs/src/libslic3r/GCodeWriter.cpp @@ -54,7 +54,7 @@ GCodeWriter::preamble() gcode << "G21 ; set units to millimeters\n"; gcode << "G90 ; use absolute coordinates\n"; } - if (FLAVOR_IS(gcfRepRap) || FLAVOR_IS(gcfTeacup) || FLAVOR_IS(gcfSmoothie)) { + if (FLAVOR_IS(gcfRepRap) || FLAVOR_IS(gcfTeacup) || FLAVOR_IS(gcfRepetier) || FLAVOR_IS(gcfSmoothie)) { if (this->config.use_relative_e_distances) { gcode << "M83 ; use relative distances for extrusion\n"; } else { @@ -185,7 +185,12 @@ GCodeWriter::set_acceleration(unsigned int acceleration) this->_last_acceleration = acceleration; std::ostringstream gcode; - gcode << "M204 S" << acceleration; + if (FLAVOR_IS(gcfRepetier)) { + gcode << "M201 X" << acceleration << " Y" << acceleration; + gcode << "M202 X" << acceleration << " Y" << acceleration; + } else { + gcode << "M204 S" << acceleration; + } if (this->config.gcode_comments) gcode << " ; adjust acceleration"; gcode << "\n"; diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 90af82fbd..03efad703 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -463,6 +463,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "gcode-flavor=s"; def->enum_keys_map = ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("reprap"); + def->enum_values.push_back("repetier"); def->enum_values.push_back("teacup"); def->enum_values.push_back("makerware"); def->enum_values.push_back("sailfish"); @@ -470,7 +471,8 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("machinekit"); def->enum_values.push_back("smoothie"); def->enum_values.push_back("no-extrusion"); - def->enum_labels.push_back("RepRap (Marlin/Sprinter/Repetier)"); + def->enum_labels.push_back("RepRap (Marlin/Sprinter)"); + def->enum_labels.push_back("Repetier"); def->enum_labels.push_back("Teacup"); def->enum_labels.push_back("MakerWare (MakerBot)"); def->enum_labels.push_back("Sailfish (MakerBot)"); diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 442af51c2..9b43070c5 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -9,7 +9,7 @@ namespace Slic3r { enum GCodeFlavor { - gcfRepRap, gcfTeacup, gcfMakerWare, gcfSailfish, gcfMach3, gcfMachinekit, gcfNoExtrusion, gcfSmoothie, + gcfRepRap, gcfTeacup, gcfMakerWare, gcfSailfish, gcfMach3, gcfMachinekit, gcfNoExtrusion, gcfSmoothie, gcfRepetier, }; enum InfillPattern { @@ -28,6 +28,7 @@ enum SeamPosition { template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { t_config_enum_values keys_map; keys_map["reprap"] = gcfRepRap; + keys_map["repetier"] = gcfRepetier; keys_map["teacup"] = gcfTeacup; keys_map["makerware"] = gcfMakerWare; keys_map["sailfish"] = gcfSailfish; From 885f27b8aea0df8be351825b9dd6061696f5edc9 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sat, 16 Jul 2016 09:56:41 -0500 Subject: [PATCH 004/225] Oops, forgot to add newline. --- xs/src/libslic3r/GCodeWriter.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xs/src/libslic3r/GCodeWriter.cpp b/xs/src/libslic3r/GCodeWriter.cpp index 7c2b17a14..e894f382a 100644 --- a/xs/src/libslic3r/GCodeWriter.cpp +++ b/xs/src/libslic3r/GCodeWriter.cpp @@ -187,6 +187,8 @@ GCodeWriter::set_acceleration(unsigned int acceleration) std::ostringstream gcode; if (FLAVOR_IS(gcfRepetier)) { gcode << "M201 X" << acceleration << " Y" << acceleration; + if (this->config.gcode_comments) gcode << " ; adjust acceleration"; + gcode << "\n"; gcode << "M202 X" << acceleration << " Y" << acceleration; } else { gcode << "M204 S" << acceleration; From 748768de4cfab815c82c4279d58c52c90f88c99c Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 19 Jul 2016 09:24:30 +0200 Subject: [PATCH 005/225] testcase for multiple extruders with different lift_z values #3385 --- t/retraction.t | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/t/retraction.t b/t/retraction.t index a7076c025..35de94e58 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -1,4 +1,4 @@ -use Test::More tests => 22; +use Test::More tests => 26; use strict; use warnings; @@ -26,6 +26,7 @@ use Slic3r::Test qw(_eq); my @retracted = (1); # ignore the first travel move from home to first point my @retracted_length = (0); my $lifted = 0; + my $lift_dist = 0; # track lifted distance for toolchanges and extruders with different retract_lift values my $changed_tool = 0; my $wait_for_toolchange = 0; Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { @@ -48,12 +49,14 @@ use Slic3r::Test qw(_eq); fail 'only lifting while retracted' if !$retracted[$tool]; fail 'double lift' if $lifted; $lifted = 1; + $lift_dist = $info->{dist_Z}; } if ($info->{dist_Z} < 0) { fail 'going down only after lifting' if !$lifted; fail 'going down by the same amount of the lift or by the amount needed to get to next layer' - if !_eq($info->{dist_Z}, -$print->print->config->get_at('retract_lift', $tool)) - && !_eq($info->{dist_Z}, -$print->print->config->get_at('retract_lift', $tool) + $conf->layer_height); + if !_eq($info->{dist_Z}, -$lift_dist) + && !_eq($info->{dist_Z}, -lift_dist + $conf->layer_height); + $lift_dist = 0; $lifted = 0; } fail 'move Z at travel speed' if ($args->{F} // $self->F) != $conf->travel_speed * 60; @@ -110,7 +113,7 @@ use Slic3r::Test qw(_eq); $conf->set('retract_restart_extra', [-1]); ok $test->($conf), "negative restart extra length$descr"; - $conf->set('retract_lift', [1]); + $conf->set('retract_lift', [1, 2]); ok $test->($conf), "lift$descr"; }; @@ -204,7 +207,7 @@ use Slic3r::Test qw(_eq); { my $config = Slic3r::Config->new_from_defaults; $config->set('start_gcode', ''); - $config->set('retract_lift', [3]); + $config->set('retract_lift', [3, 4]); my @lifted_at = (); my $test = sub { @@ -219,19 +222,36 @@ use Slic3r::Test qw(_eq); }); }; - $config->set('retract_lift_above', [0]); - $config->set('retract_lift_below', [0]); + $config->set('retract_lift_above', [0, 0]); + $config->set('retract_lift_below', [0, 0]); $test->(); ok !!@lifted_at, 'lift takes place when above/below == 0'; - $config->set('retract_lift_above', [5]); - $config->set('retract_lift_below', [15]); + $config->set('retract_lift_above', [5, 6]); + $config->set('retract_lift_below', [15, 16]); $test->(); ok !!@lifted_at, 'lift takes place when above/below != 0'; ok !(any { $_ < $config->get_at('retract_lift_above', 0) } @lifted_at), 'Z is not lifted below the configured value'; ok !(any { $_ > $config->get_at('retract_lift_below', 0) } @lifted_at), 'Z is not lifted above the configured value'; + + # check lifting with different values for 2. extruder + $config->set('perimeter_extruder', 2); + $config->set('infill_extruder', 2); + $config->set('retract_lift_above', [0, 0]); + $config->set('retract_lift_below', [0, 0]); + $test->(); + ok !!@lifted_at, 'lift takes place when above/below == 0 for 2. extruder'; + + $config->set('retract_lift_above', [5, 6]); + $config->set('retract_lift_below', [15, 13]); + $test->(); + ok !!@lifted_at, 'lift takes place when above/below != 0 for 2. extruder'; + ok !(any { $_ < $config->get_at('retract_lift_above', 1) } @lifted_at), + 'Z is not lifted below the configured value for 2. extruder'; + ok !(any { $_ > $config->get_at('retract_lift_below', 1) } @lifted_at), + 'Z is not lifted above the configured value for 2. extruder'; } __END__ From d3a72c39748f89176be6918a9aa48b1879dc7f90 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 20 Jul 2016 09:19:06 +0200 Subject: [PATCH 006/225] better value for retract_lift_below test --- t/retraction.t | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/t/retraction.t b/t/retraction.t index 35de94e58..a35562c58 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -228,7 +228,7 @@ use Slic3r::Test qw(_eq); ok !!@lifted_at, 'lift takes place when above/below == 0'; $config->set('retract_lift_above', [5, 6]); - $config->set('retract_lift_below', [15, 16]); + $config->set('retract_lift_below', [15, 13]); $test->(); ok !!@lifted_at, 'lift takes place when above/below != 0'; ok !(any { $_ < $config->get_at('retract_lift_above', 0) } @lifted_at), From e56a29aaf3d9f67bfe9a0bae5c85356b3db4c40c Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Wed, 20 Jul 2016 18:15:24 -0500 Subject: [PATCH 007/225] Test to ensure that the repetier firmware returns the correct acceleration M code and that the values are set properly. --- t/gcode.t | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/t/gcode.t b/t/gcode.t index ef2209a80..9e7bd354a 100644 --- a/t/gcode.t +++ b/t/gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 23; +use Test::More tests => 25; use strict; use warnings; @@ -215,4 +215,31 @@ use Slic3r::Test; ok !$spiral, 'spiral vase is correctly disabled on layers with multiple loops'; } + +{ + # Tests that the Repetier flavor produces M201 Xnnn Ynnn for resetting + # acceleration, also that M204 Snnn syntax is not generated. + my $config = Slic3r::Config->new_from_defaults; + $config->set('gcode_flavor', 'repetier'); + $config->set('default_acceleration', 1337); + my $print = Slic3r::Test::init_print('cube_with_hole', config => $config); + + my $has_accel = 0; + my $has_m204 = 0; + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { + my ($self, $cmd, $args, $info) = @_; + + if ($cmd eq 'M201' && exists $args->{X} && exists $args->{Y}) { + if ($args->{X} == 1337 && $args->{Y} == 1337) { + $has_accel = 1; + } + } + if ($cmd eq 'M204' && exists $args->{S}) { + $has_m204 = 1; + } + }); + ok $has_accel, 'M201 is generated for repetier firmware.'; + ok !$has_m204, 'M204 is not generated for repetier firmware'; +} + __END__ From 7f2c1478eb0ddbdf16f6909c8bbc83ef3a4ffc56 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Wed, 20 Jul 2016 18:33:16 -0500 Subject: [PATCH 008/225] Added tests to check the functionality of has_heatbed option. --- t/gcode.t | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/t/gcode.t b/t/gcode.t index ef2209a80..243acb24b 100644 --- a/t/gcode.t +++ b/t/gcode.t @@ -1,4 +1,4 @@ -use Test::More tests => 23; +use Test::More tests => 25; use strict; use warnings; @@ -215,4 +215,47 @@ use Slic3r::Test; ok !$spiral, 'spiral vase is correctly disabled on layers with multiple loops'; } +{ + # Test verifies that if has_heatbed is false, M190/M140 gcodes are not + # generated by default even if a bed temperature is set. + my $config = Slic3r::Config->new_from_defaults; + $config->set('has_heatbed', 0); + $config->set('first_layer_bed_temperature', 100); + $config->set('bed_temperature', 60); + + my $had_gcode = 0; + my $print = Slic3r::Test::init_print('cube_with_hole', config => $config); + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { + my ($self, $cmd, $args, $info) = @_; + + if (($cmd eq 'M140' && exists $args->{S}) || + ($cmd eq 'M190' && exists $args->{S})) { + $had_gcode = 1; + } + }); + + ok !$had_gcode, 'M190/M140 codes are not generated if has_heatbed = 0'; +} + +{ + # Test verifies that if has_heatbed is true, M190/M140 gcodes are + # generated by default if a bed temperature is set. + my $config = Slic3r::Config->new_from_defaults; + $config->set('has_heatbed', 1); + $config->set('first_layer_bed_temperature', 100); + $config->set('bed_temperature', 60); + + my $had_gcode = 0; + my $print = Slic3r::Test::init_print('cube_with_hole', config => $config); + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { + my ($self, $cmd, $args, $info) = @_; + + if (($cmd eq 'M140' && exists $args->{S}) || + ($cmd eq 'M190' && exists $args->{S})) { + $had_gcode = 1; + } + }); + + ok $had_gcode, 'M190/M140 codes are generated if has_heatbed = 1'; +} __END__ From 7faa0fbfb51caace434afddb6c619b83657fbdf3 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 10 Jul 2016 19:27:57 -0500 Subject: [PATCH 009/225] Calculate actual flow for later inclusion as a ExtrusionPath, using the correct layer height for the surface. --- lib/Slic3r/Fill.pm | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 8c63fde59..10a912cbe 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -229,6 +229,13 @@ sub make_fill { ); $f->spacing($internal_flow->spacing); $using_internal_flow = 1; + # create the actual flow for internal flow that is used later. + $flow = Slic3r::Flow->new_from_spacing( + spacing => $internal_flow->spacing, + nozzle_diameter => $flow->nozzle_diameter, + layer_height => $h, + bridge => 0, + ); } else { $f->spacing($flow->spacing); } From ea77f66629b4d00ba700e418676ea200b24ca399 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 21 Aug 2016 21:46:17 +0200 Subject: [PATCH 010/225] Updated xs/Build.PL to support Visual Studio compiler suite. Updated xs/Build.PL to understand BOOST_LIBRARY_PATH and BOOST_INCLUDE_PATH environment variables. This way one may easily switch between various boost builds. Some minor tweeks were done to make Slic3r compile with Visual Studio 2013. --- xs/Build.PL | 123 ++++++++++++++++++------- xs/src/libslic3r/PlaceholderParser.cpp | 12 ++- xs/src/libslic3r/libslic3r.h | 9 +- xs/src/xsinit.h | 6 ++ 4 files changed, 112 insertions(+), 38 deletions(-) diff --git a/xs/Build.PL b/xs/Build.PL index 8a111c1a4..abcf24868 100644 --- a/xs/Build.PL +++ b/xs/Build.PL @@ -7,6 +7,9 @@ use Devel::CheckLib; use ExtUtils::CppGuess; use Module::Build::WithXSpp; +my $cpp_guess = ExtUtils::CppGuess->new; +my $mswin = $^O eq 'MSWin32'; + # _GLIBCXX_USE_C99 : to get the long long type for g++ # HAS_BOOL : stops Perl/lib/CORE/handy.h from doing "# define bool char" for MSVC # NOGDI : prevents inclusion of wingdi.h which defines functions Polygon() and Polyline() in global namespace @@ -16,32 +19,67 @@ my @ldflags = (); if ($^O eq 'darwin') { push @ldflags, qw(-framework IOKit -framework CoreFoundation); } +if ($mswin) { + # In case windows.h is included, we don't want the min / max macros to be active. + # If is included, we want the #defines to be active (M_PI etc.) + push @cflags, qw(-DNOMINMAX -D_USE_MATH_DEFINES); +} my @INC = qw(-Isrc/libslic3r); -my @LIBS = qw(-Lsrc/libslic3r); +my @LIBS = $cpp_guess->is_msvc ? qw(-LIBPATH:src/libslic3r) : qw(-Lsrc/libslic3r); # search for Boost in a number of places -my @boost_include = my @boost_libs = (); -if (defined $ENV{BOOST_DIR}) { - if (-d "$ENV{BOOST_DIR}/include") { - push @boost_include, $ENV{BOOST_DIR} . '/include'; +my @boost_include = (); +if (defined $ENV{BOOST_INCLUDEDIR}) { + push @boost_include, $ENV{BOOST_INCLUDEDIR} +} elsif (defined $ENV{BOOST_DIR}) { + my $subdir = $ENV{BOOST_DIR} . (($mswin == 1) ? '\include' : '/include'); + if (-d $subdir) { + push @boost_include, $subdir; } else { push @boost_include, $ENV{BOOST_DIR}; } - push @boost_libs, $ENV{BOOST_DIR}; } else { - push @boost_include, grep { -d $_ } - qw(/opt/local/include /usr/local/include /opt/include), - qw(/usr/include C:\Boost\include); - push @boost_libs, grep { -d $_ } - qw(/opt/local/lib /usr/local/lib /opt/lib /usr/lib), - qw(C:\Boost\lib /lib); - - if ($^O eq 'MSWin32') { - for my $path (glob('C:\dev\boost*'), glob ('C:\boost*')) { + # Boost library was not defined by the environment. + # Try to guess at some default paths. + if ($mswin) { + for my $path (glob('C:\dev\boost*\include'), glob ('C:\boost*\include')) { push @boost_include, $path; - push @boost_libs, $path . "/stage/lib"; } + if (! @boost_include) { + # No boost\include. Try to include the boost root. + for my $path (glob('C:\dev\boost*'), glob ('C:\boost*')) { + push @boost_include, $path; + } + } + } else { + push @boost_include, grep { -d $_ } + qw(/opt/local/include /usr/local/include /opt/include /usr/include); + } +} + +my @boost_libs = (); +if (defined $ENV{BOOST_LIBRARYDIR}) { + push @boost_libs, $ENV{BOOST_LIBRARYDIR} +} elsif (defined $ENV{BOOST_DIR}) { + my $subdir = $ENV{BOOST_DIR} . ($mswin ? '\stage\lib' : '/stage/lib'); + if (-d $subdir) { + push @boost_libs, $subdir; + } else { + push @boost_libs, $ENV{BOOST_DIR}; + } +} else { + # Boost library was not defined by the environment. + # Try to guess at some default paths. + if ($mswin) { + for my $path ( + glob('C:\dev\boost*\lib'), glob ('C:\boost*\lib'), + glob('C:\dev\boost*\stage\lib'), glob ('C:\boost*\stage\lib')) { + push @boost_libs, $path; + } + } else { + push @boost_libs, grep { -d $_ } + qw(/opt/local/lib /usr/local/lib /opt/lib /usr/lib /lib); } } @@ -50,29 +88,44 @@ my $have_boost = 0; my @boost_libraries = qw(system thread filesystem); # we need these # check without explicit lib path (works on Linux) -$have_boost = 1 - if check_lib( - lib => [ map "boost_${_}", @boost_libraries ], - ); +if (! $mswin) { + $have_boost = 1 + if check_lib( + lib => [ map "boost_${_}", @boost_libraries ], + ); +} if (!$ENV{SLIC3R_STATIC} && $have_boost) { + # The boost library was detected by check_lib on Linux. push @LIBS, map "-lboost_${_}", @boost_libraries; } else { - foreach my $path (@boost_libs) { - my @files = glob "$path/libboost_system*"; + # Either static linking, or check_lib could not be used to find the boost libraries. + my $lib_prefix = 'libboost_'; + my $lib_ext = ${$cpp_guess}{config}{lib_ext}; + PATH: foreach my $path (@boost_libs) { + # Try to find the boost system library. + my @files = glob "$path/${lib_prefix}system*$lib_ext"; next if !@files; - if ($files[0] =~ /libboost_system([^.]+)/) { + if ($files[0] =~ /${lib_prefix}system([^.]+)$lib_ext$/) { + # Suffix contains the version number, the build type etc. my $suffix = $1; - check_lib( - lib => [ map "boost_${_}${suffix}", @boost_libraries ], - INC => join(' ', map "-I$_", @INC, @boost_include), - LIBS => "-L$path", - ) or next; - + # Verify existence of all required boost libraries at $path. + for my $lib (map "${lib_prefix}${_}${suffix}${lib_ext}", @boost_libraries) { + # If the library file does not exist, try next library path. + -f "$path/$lib" or next PATH; + } + if (! $cpp_guess->is_msvc) { + # Test the correctness of boost libraries by linking them to a minimal C program. + check_lib( + lib => [ map "boost_${_}${suffix}", @boost_libraries ], + INC => join(' ', map "-I$_", @INC, @boost_include), + LIBS => "-L$path", + ) or next; + } push @INC, (map " -I$_", @boost_include); # TODO: only use the one related to the chosen lib path - if ($ENV{SLIC3R_STATIC}) { - push @LIBS, map "$path/libboost_$_$suffix.a", @boost_libraries; + if ($ENV{SLIC3R_STATIC} || $cpp_guess->is_msvc) { + push @LIBS, map "${path}/${lib_prefix}$_${suffix}${lib_ext}", @boost_libraries; } else { push @LIBS, " -L$path", (map " -lboost_$_$suffix", @boost_libraries); } @@ -91,13 +144,17 @@ path through the BOOST_DIR environment variable: BOOST_DIR=/path/to/boost perl Build.PL +Or you may specify BOOST_INCLUDEPATH and BOOST_LIBRARYPATH separatly, which +is handy, if you have built Boost libraries with mutliple settings. + EOF if ($ENV{SLIC3R_DEBUG}) { # only on newer GCCs: -ftemplate-backtrace-limit=0 - push @cflags, qw(-DSLIC3R_DEBUG -g); + push @cflags, '-DSLIC3R_DEBUG'; + push @cflags, $cpp_guess->is_msvc ? '-Gd' : '-g'; } -if (ExtUtils::CppGuess->new->is_gcc) { +if ($cpp_guess->is_gcc) { # check whether we're dealing with a buggy GCC version # see https://github.com/alexrj/Slic3r/issues/1965 if (`cc --version` =~ / 4\.7\.[012]/) { diff --git a/xs/src/libslic3r/PlaceholderParser.cpp b/xs/src/libslic3r/PlaceholderParser.cpp index cddfa21fa..501349d5c 100644 --- a/xs/src/libslic3r/PlaceholderParser.cpp +++ b/xs/src/libslic3r/PlaceholderParser.cpp @@ -3,14 +3,22 @@ #include #include #include -#include // provides **environ +#ifdef _MSC_VER + #include // provides **_environ +#else + #include // provides **environ +#endif #ifdef __APPLE__ #include #undef environ #define environ (*_NSGetEnviron()) #else - extern char **environ; + #ifdef _MSC_VER + #define environ _environ + #else + extern char **environ; + #endif #endif namespace Slic3r { diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index eb3fb8f03..168e3f49c 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -25,14 +25,17 @@ typedef double coordf_t; namespace Slic3r { -// TODO: make sure X = 0 -enum Axis { X, Y, Z }; +enum Axis { X=0, Y, Z }; } using namespace Slic3r; /* Implementation of CONFESS("foo"): */ -#define CONFESS(...) confess_at(__FILE__, __LINE__, __func__, __VA_ARGS__) +#ifdef _MSC_VER + #define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) +#else + #define CONFESS(...) confess_at(__FILE__, __LINE__, __func__, __VA_ARGS__) +#endif void confess_at(const char *file, int line, const char *func, const char *pat, ...); /* End implementation of CONFESS("foo"): */ diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index 69ac337f7..99b0e8f6a 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -40,6 +40,12 @@ extern "C" { #include "ppport.h" #undef do_open #undef do_close +#ifdef _MSC_VER + // Undef some of the macros set by Perl , which cause compilation errors on Win32 + #undef send + #undef connect + #undef bind +#endif /* _MSC_VER */ } #endif From aaae7bf1c0dae25cb9c4acc0692b59e9af3b79e8 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 5 Sep 2016 11:32:58 +0200 Subject: [PATCH 011/225] New command line option: --dont-arrange Don't arrange the objects on the build plate. The model coordinates define the absolute positions on the build plate. The option --print-center will be ignored. Also there is a fix in the command line invocation of --duplicate-grid so it will run, but still there is something broken there and the results are not correct. --- README.md | 3 +++ lib/Slic3r/Print/Simple.pm | 11 ++++++++--- slic3r.pl | 5 +++++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 44a6f53bc..777b23008 100644 --- a/README.md +++ b/README.md @@ -324,6 +324,9 @@ The author of the Silk icon set is Mark James. --duplicate Number of items with auto-arrange (1+, default: 1) --duplicate-grid Number of items with grid arrangement (default: 1,1) --duplicate-distance Distance in mm between copies (default: 6) + --dont-arrange Don't arrange the objects on the build plate. The model coordinates + define the absolute positions on the build plate. + The option --print-center will be ignored. --xy-size-compensation Grow/shrink objects by the configured absolute distance (mm, default: 0) diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm index 5618484fa..d84d1cb53 100644 --- a/lib/Slic3r/Print/Simple.pm +++ b/lib/Slic3r/Print/Simple.pm @@ -41,6 +41,11 @@ has 'print_center' => ( default => sub { Slic3r::Pointf->new(100,100) }, ); +has 'dont_arrange' => ( + is => 'rw', + default => sub { 0 }, +); + has 'output_file' => ( is => 'rw', ); @@ -52,7 +57,7 @@ sub set_model { $self->_print->clear_objects; # make sure all objects have at least one defined instance - my $need_arrange = $model->add_default_instances; + my $need_arrange = $model->add_default_instances && ! $self->dont_arrange; # apply scaling and rotation supplied from command line if any foreach my $instance (map @{$_->instances}, @{$model->objects}) { @@ -61,7 +66,7 @@ sub set_model { } if ($self->duplicate_grid->[X] > 1 || $self->duplicate_grid->[Y] > 1) { - $model->duplicate_objects_grid($self->duplicate_grid, $self->_print->config->duplicate_distance); + $model->duplicate_objects_grid($self->duplicate_grid->[X], $self->duplicate_grid->[Y], $self->_print->config->duplicate_distance); } elsif ($need_arrange) { $model->duplicate_objects($self->duplicate, $self->_print->config->min_object_distance); } elsif ($self->duplicate > 1) { @@ -69,7 +74,7 @@ sub set_model { $model->duplicate($self->duplicate, $self->_print->config->min_object_distance); } $_->translate(0,0,-$_->bounding_box->z_min) for @{$model->objects}; - $model->center_instances_around_point($self->print_center); + $model->center_instances_around_point($self->print_center) if (! $self->dont_arrange); foreach my $model_object (@{$model->objects}) { $self->_print->auto_assign_extruders($model_object); diff --git a/slic3r.pl b/slic3r.pl index c634e7272..b6ab68a87 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -48,6 +48,7 @@ my %cli_options = (); 'duplicate=i' => \$opt{duplicate}, 'duplicate-grid=s' => \$opt{duplicate_grid}, 'print-center=s' => \$opt{print_center}, + 'dont-arrange' => \$opt{dont_arrange}, ); foreach my $opt_key (keys %{$Slic3r::Config::Options}) { my $cli = $Slic3r::Config::Options->{$opt_key}->{cli} or next; @@ -204,6 +205,7 @@ if (@ARGV) { # slicing from command line duplicate => $opt{duplicate} // 1, duplicate_grid => $opt{duplicate_grid} // [1,1], print_center => $opt{print_center} // Slic3r::Pointf->new(100,100), + dont_arrange => $opt{dont_arrange} // 0, status_cb => sub { my ($percent, $message) = @_; printf "=> %s\n", $message; @@ -485,6 +487,9 @@ $j --duplicate Number of items with auto-arrange (1+, default: 1) --duplicate-grid Number of items with grid arrangement (default: 1,1) --duplicate-distance Distance in mm between copies (default: $config->{duplicate_distance}) + --dont-arrange Don't arrange the objects on the build plate. The model coordinates + define the absolute positions on the build plate. + The option --print-center will be ignored. --xy-size-compensation Grow/shrink objects by the configured absolute distance (mm, default: $config->{xy_size_compensation}) From 53f536a73f4a600a9c70ccd6ef1e8dc6de74b76a Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 11 Sep 2016 10:16:44 -0500 Subject: [PATCH 012/225] Implemented a Slice Now button in the UI, starts a background process. --- lib/Slic3r/GUI/Plater.pm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index fb1a36004..bbc817f88 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -191,6 +191,8 @@ sub new { # right pane buttons $self->{btn_export_gcode} = Wx::Button->new($self, -1, "Export G-code…", wxDefaultPosition, [-1, 30], wxBU_LEFT); + $self->{btn_reslice} = Wx::Button->new($self, -1, "Slice now…", wxDefaultPosition, [-1, 30], wxBU_LEFT); + $self->{btn_print} = Wx::Button->new($self, -1, "Print…", wxDefaultPosition, [-1, 30], wxBU_LEFT); $self->{btn_print} = Wx::Button->new($self, -1, "Print…", wxDefaultPosition, [-1, 30], wxBU_LEFT); $self->{btn_send_gcode} = Wx::Button->new($self, -1, "Send to printer", wxDefaultPosition, [-1, 30], wxBU_LEFT); $self->{btn_export_stl} = Wx::Button->new($self, -1, "Export STL…", wxDefaultPosition, [-1, 30], wxBU_LEFT); @@ -228,6 +230,9 @@ sub new { EVT_BUTTON($self, $self->{btn_export_gcode}, sub { $self->export_gcode; }); + EVT_BUTTON($self, $self->{btn_reslice}, sub { + $self->start_background_process; + }); EVT_BUTTON($self, $self->{btn_print}, sub { $self->{print_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir()); }); @@ -391,6 +396,7 @@ sub new { $buttons_sizer->Add($self->{btn_print}, 0, wxALIGN_RIGHT, 0); $buttons_sizer->Add($self->{btn_send_gcode}, 0, wxALIGN_RIGHT, 0); $buttons_sizer->Add($self->{btn_export_gcode}, 0, wxALIGN_RIGHT, 0); + $buttons_sizer->Add($self->{btn_reslice}, 0, wxALIGN_RIGHT, 0); my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); $right_sizer->Add($presets, 0, wxEXPAND | wxTOP, 10) if defined $presets; From a020cd1316871f21b29cbfdd32216e9b723c1588 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 11 Sep 2016 11:02:19 -0500 Subject: [PATCH 013/225] Added a reslice now option to main menu, bound it to ctrl+s. Reslicing stops the background process before starting a new one. --- lib/Slic3r/GUI/MainFrame.pm | 10 ++++++++++ lib/Slic3r/GUI/Plater.pm | 12 ++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index d97ad22ef..9aefb7b6c 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -87,6 +87,13 @@ sub new { return $self; } +sub reslice_now { + my ($self) = @_; + + if ($self->{plater}) { + $self->{plater}->reslice; + } +} sub _init_tabpanel { my ($self) = @_; @@ -205,6 +212,9 @@ sub _init_menubar { $self->_append_menu_item($fileMenu, "Slice to SV&G…\tCtrl+G", 'Slice file to SVG', sub { $self->quick_slice(save_as => 1, export_svg => 1); }, undef, 'shape_handles.png'); + $self->_append_menu_item($fileMenu, "(&Re)Slice Now\tCtrl+S", 'Start new slicing process', sub { + $self->reslice_now; + }, undef, 'shape_handles.png'); $fileMenu->AppendSeparator(); $self->_append_menu_item($fileMenu, "Repair STL file…", 'Automatically repair an STL file', sub { $self->repair_stl; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index bbc817f88..648fd3b1e 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -231,7 +231,7 @@ sub new { $self->export_gcode; }); EVT_BUTTON($self, $self->{btn_reslice}, sub { - $self->start_background_process; + $self->reslice; }); EVT_BUTTON($self, $self->{btn_print}, sub { $self->{print_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir()); @@ -634,6 +634,13 @@ sub bed_centerf { return Slic3r::Pointf->new(unscale($bed_center->x), unscale($bed_center->y)); #) } +sub reslice { + # explicitly cancel a previous thread and start a new one. + my ($self) = @_; + + $self->stop_background_process; + $self->start_background_process; +} sub remove { my $self = shift; my ($obj_idx) = @_; @@ -1610,12 +1617,13 @@ sub object_list_changed { my $have_objects = @{$self->{objects}} ? 1 : 0; my $method = $have_objects ? 'Enable' : 'Disable'; $self->{"btn_$_"}->$method - for grep $self->{"btn_$_"}, qw(reset arrange export_gcode export_stl print send_gcode); + for grep $self->{"btn_$_"}, qw(reslice reset arrange export_gcode export_stl print send_gcode); if ($self->{export_gcode_output_file} || $self->{send_gcode_file}) { $self->{btn_export_gcode}->Disable; $self->{btn_print}->Disable; $self->{btn_send_gcode}->Disable; + $self->{btn_reslice}->Disable; } if ($self->{htoolbar}) { From 24afffc421ebd8a9297528bdfa038ba33490e3c2 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 13 Sep 2016 09:44:30 +0200 Subject: [PATCH 014/225] Fix in splitting the loop at a new point. --- xs/src/libslic3r/ExtrusionEntity.cpp | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/xs/src/libslic3r/ExtrusionEntity.cpp b/xs/src/libslic3r/ExtrusionEntity.cpp index 5d0e5502a..ab462eb03 100644 --- a/xs/src/libslic3r/ExtrusionEntity.cpp +++ b/xs/src/libslic3r/ExtrusionEntity.cpp @@ -193,6 +193,7 @@ ExtrusionLoop::split_at_vertex(const Point &point) } else { // new paths list starts with the second half of current path ExtrusionPaths new_paths; + new_paths.reserve(this->paths.size() + 1); { ExtrusionPath p = *path; p.polyline.points.erase(p.polyline.points.begin(), p.polyline.points.begin() + idx); @@ -212,7 +213,7 @@ ExtrusionLoop::split_at_vertex(const Point &point) if (p.polyline.is_valid()) new_paths.push_back(p); } // we can now override the old path list with the new one and stop looping - this->paths = new_paths; + std::swap(this->paths, new_paths); } return true; } @@ -240,14 +241,24 @@ ExtrusionLoop::split_at(const Point &point) } // now split path_idx in two parts - ExtrusionPath p1 = this->paths[path_idx]; - ExtrusionPath p2 = p1; + ExtrusionPath p1(this->paths[path_idx].role), p2(this->paths[path_idx].role); this->paths[path_idx].polyline.split_at(p, &p1.polyline, &p2.polyline); - // install the two paths - this->paths.erase(this->paths.begin() + path_idx); - if (p2.polyline.is_valid()) this->paths.insert(this->paths.begin() + path_idx, p2); - if (p1.polyline.is_valid()) this->paths.insert(this->paths.begin() + path_idx, p1); + if (this->paths.size() == 1) { + if (! p1.polyline.is_valid()) + std::swap(this->paths.front().polyline.points, p2.polyline.points); + else if (! p2.polyline.is_valid()) + std::swap(this->paths.front().polyline.points, p1.polyline.points); + else { + p2.polyline.points.insert(p2.polyline.points.end(), p1.polyline.points.begin() + 1, p1.polyline.points.end()); + std::swap(this->paths.front().polyline.points, p2.polyline.points); + } + } else { + // install the two paths + this->paths.erase(this->paths.begin() + path_idx); + if (p2.polyline.is_valid()) this->paths.insert(this->paths.begin() + path_idx, p2); + if (p1.polyline.is_valid()) this->paths.insert(this->paths.begin() + path_idx, p1); + } // split at the new vertex this->split_at_vertex(p); From 8f66b33c3ebf31eced5bb5e5bb7c287b0ffce7ef Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 19 Sep 2016 19:44:54 -0500 Subject: [PATCH 015/225] EXPERIMENTAL: Added all support infill types to the support dropdown. --- xs/src/libslic3r/PrintConfig.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 461981cda..520e82e6a 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1144,11 +1144,23 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("rectilinear"); def->enum_values.push_back("rectilinear-grid"); def->enum_values.push_back("honeycomb"); + def->enum_values.push_back("3dhoneycomb"); def->enum_values.push_back("pillars"); + def->enum_values.push_back("line"); + def->enum_values.push_back("concentric"); + def->enum_values.push_back("hilbertcurve"); + def->enum_values.push_back("archimedeanchords"); + def->enum_values.push_back("octagramspiral"); def->enum_labels.push_back("rectilinear"); def->enum_labels.push_back("rectilinear grid"); def->enum_labels.push_back("honeycomb"); + def->enum_labels.push_back("3D Honeycomb"); def->enum_labels.push_back("pillars"); + def->enum_labels.push_back("line"); + def->enum_labels.push_back("Concentric"); + def->enum_labels.push_back("Hilbert Curve"); + def->enum_labels.push_back("Archimedean Chords"); + def->enum_labels.push_back("Octagram Spiral"); def->default_value = new ConfigOptionEnum(smpPillars); def = this->add("support_material_spacing", coFloat); From 4ee1793bfd05342e343e0b41662a5c63cb49892d Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 11 Sep 2016 10:16:44 -0500 Subject: [PATCH 016/225] Implemented a Slice Now button in the UI, starts a background process. --- lib/Slic3r/GUI/Plater.pm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index fb1a36004..bbc817f88 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -191,6 +191,8 @@ sub new { # right pane buttons $self->{btn_export_gcode} = Wx::Button->new($self, -1, "Export G-code…", wxDefaultPosition, [-1, 30], wxBU_LEFT); + $self->{btn_reslice} = Wx::Button->new($self, -1, "Slice now…", wxDefaultPosition, [-1, 30], wxBU_LEFT); + $self->{btn_print} = Wx::Button->new($self, -1, "Print…", wxDefaultPosition, [-1, 30], wxBU_LEFT); $self->{btn_print} = Wx::Button->new($self, -1, "Print…", wxDefaultPosition, [-1, 30], wxBU_LEFT); $self->{btn_send_gcode} = Wx::Button->new($self, -1, "Send to printer", wxDefaultPosition, [-1, 30], wxBU_LEFT); $self->{btn_export_stl} = Wx::Button->new($self, -1, "Export STL…", wxDefaultPosition, [-1, 30], wxBU_LEFT); @@ -228,6 +230,9 @@ sub new { EVT_BUTTON($self, $self->{btn_export_gcode}, sub { $self->export_gcode; }); + EVT_BUTTON($self, $self->{btn_reslice}, sub { + $self->start_background_process; + }); EVT_BUTTON($self, $self->{btn_print}, sub { $self->{print_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir()); }); @@ -391,6 +396,7 @@ sub new { $buttons_sizer->Add($self->{btn_print}, 0, wxALIGN_RIGHT, 0); $buttons_sizer->Add($self->{btn_send_gcode}, 0, wxALIGN_RIGHT, 0); $buttons_sizer->Add($self->{btn_export_gcode}, 0, wxALIGN_RIGHT, 0); + $buttons_sizer->Add($self->{btn_reslice}, 0, wxALIGN_RIGHT, 0); my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); $right_sizer->Add($presets, 0, wxEXPAND | wxTOP, 10) if defined $presets; From 6e1d10bd0602f89172e82f1af7b3e7be19b30fbb Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 11 Sep 2016 11:02:19 -0500 Subject: [PATCH 017/225] Added a reslice now option to main menu, bound it to ctrl+s. Reslicing stops the background process before starting a new one. --- lib/Slic3r/GUI/MainFrame.pm | 10 ++++++++++ lib/Slic3r/GUI/Plater.pm | 12 ++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index d97ad22ef..9aefb7b6c 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -87,6 +87,13 @@ sub new { return $self; } +sub reslice_now { + my ($self) = @_; + + if ($self->{plater}) { + $self->{plater}->reslice; + } +} sub _init_tabpanel { my ($self) = @_; @@ -205,6 +212,9 @@ sub _init_menubar { $self->_append_menu_item($fileMenu, "Slice to SV&G…\tCtrl+G", 'Slice file to SVG', sub { $self->quick_slice(save_as => 1, export_svg => 1); }, undef, 'shape_handles.png'); + $self->_append_menu_item($fileMenu, "(&Re)Slice Now\tCtrl+S", 'Start new slicing process', sub { + $self->reslice_now; + }, undef, 'shape_handles.png'); $fileMenu->AppendSeparator(); $self->_append_menu_item($fileMenu, "Repair STL file…", 'Automatically repair an STL file', sub { $self->repair_stl; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index bbc817f88..648fd3b1e 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -231,7 +231,7 @@ sub new { $self->export_gcode; }); EVT_BUTTON($self, $self->{btn_reslice}, sub { - $self->start_background_process; + $self->reslice; }); EVT_BUTTON($self, $self->{btn_print}, sub { $self->{print_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir()); @@ -634,6 +634,13 @@ sub bed_centerf { return Slic3r::Pointf->new(unscale($bed_center->x), unscale($bed_center->y)); #) } +sub reslice { + # explicitly cancel a previous thread and start a new one. + my ($self) = @_; + + $self->stop_background_process; + $self->start_background_process; +} sub remove { my $self = shift; my ($obj_idx) = @_; @@ -1610,12 +1617,13 @@ sub object_list_changed { my $have_objects = @{$self->{objects}} ? 1 : 0; my $method = $have_objects ? 'Enable' : 'Disable'; $self->{"btn_$_"}->$method - for grep $self->{"btn_$_"}, qw(reset arrange export_gcode export_stl print send_gcode); + for grep $self->{"btn_$_"}, qw(reslice reset arrange export_gcode export_stl print send_gcode); if ($self->{export_gcode_output_file} || $self->{send_gcode_file}) { $self->{btn_export_gcode}->Disable; $self->{btn_print}->Disable; $self->{btn_send_gcode}->Disable; + $self->{btn_reslice}->Disable; } if ($self->{htoolbar}) { From a3ddfde51814129258d90294f8c086caa0fed77a Mon Sep 17 00:00:00 2001 From: Forrest Pieper Date: Fri, 23 Sep 2016 00:00:48 -0400 Subject: [PATCH 018/225] Add fill pattern for aligned rectilinear fixes #3225 --- lib/Slic3r/Fill.pm | 2 ++ lib/Slic3r/Fill/Rectilinear.pm | 7 +++++++ xs/src/libslic3r/PrintConfig.cpp | 4 ++++ xs/src/libslic3r/PrintConfig.hpp | 3 ++- 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 8c63fde59..14410eab8 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -9,6 +9,7 @@ use Slic3r::Fill::Concentric; use Slic3r::Fill::Honeycomb; use Slic3r::Fill::PlanePath; use Slic3r::Fill::Rectilinear; +use Slic3r::Fill::AlignedRectilinear; use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(X Y PI scale chained_path deg2rad); use Slic3r::Geometry::Clipper qw(union union_ex diff diff_ex intersection_ex offset offset2); @@ -20,6 +21,7 @@ has 'fillers' => (is => 'rw', default => sub { {} }); our %FillTypes = ( archimedeanchords => 'Slic3r::Fill::ArchimedeanChords', + alignedrectilinear => 'Slic3r::Fill::AlignedRectilinear', rectilinear => 'Slic3r::Fill::Rectilinear', grid => 'Slic3r::Fill::Grid', flowsnake => 'Slic3r::Fill::Flowsnake', diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index 0922ff771..66fbde330 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -165,4 +165,11 @@ extends 'Slic3r::Fill::Rectilinear'; sub angles () { [0] } sub horizontal_lines { 1 } + +package Slic3r::Fill::AlignedRectilinear; +use Moo; +extends 'Slic3r::Fill::Rectilinear'; + +sub angles () { [0, 0] } + 1; diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 461981cda..50aee4cdd 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -156,11 +156,13 @@ PrintConfigDef::PrintConfigDef() def->cli = "external-fill-pattern|solid-fill-pattern=s"; def->enum_keys_map = ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("rectilinear"); + def->enum_values.push_back("alignedrectilinear"); def->enum_values.push_back("concentric"); def->enum_values.push_back("hilbertcurve"); def->enum_values.push_back("archimedeanchords"); def->enum_values.push_back("octagramspiral"); def->enum_labels.push_back("Rectilinear"); + def->enum_labels.push_back("AlignedRectilinear"); def->enum_labels.push_back("Concentric"); def->enum_labels.push_back("Hilbert Curve"); def->enum_labels.push_back("Archimedean Chords"); @@ -363,6 +365,7 @@ PrintConfigDef::PrintConfigDef() def->cli = "fill-pattern=s"; def->enum_keys_map = ConfigOptionEnum::get_enum_values(); def->enum_values.push_back("rectilinear"); + def->enum_values.push_back("alignedrectilinear"); def->enum_values.push_back("grid"); def->enum_values.push_back("line"); def->enum_values.push_back("concentric"); @@ -372,6 +375,7 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("archimedeanchords"); def->enum_values.push_back("octagramspiral"); def->enum_labels.push_back("Rectilinear"); + def->enum_labels.push_back("AlignedRectilinear"); def->enum_labels.push_back("Grid"); def->enum_labels.push_back("Line"); def->enum_labels.push_back("Concentric"); diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 762aa4c97..1b9283f26 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -13,7 +13,7 @@ enum GCodeFlavor { }; enum InfillPattern { - ipRectilinear, ipGrid, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, + ipRectilinear, ipAlignedRectilinear, ipGrid, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, }; @@ -41,6 +41,7 @@ template<> inline t_config_enum_values ConfigOptionEnum::get_enum_v template<> inline t_config_enum_values ConfigOptionEnum::get_enum_values() { t_config_enum_values keys_map; keys_map["rectilinear"] = ipRectilinear; + keys_map["alignedrectilinear"] = ipAlignedRectilinear; keys_map["grid"] = ipGrid; keys_map["line"] = ipLine; keys_map["concentric"] = ipConcentric; From 5e52407ad8bde259062d00673631228f9c1d370a Mon Sep 17 00:00:00 2001 From: Forrest Pieper Date: Wed, 28 Sep 2016 13:58:02 -0400 Subject: [PATCH 019/225] improve aligned rectilinear menu text --- xs/src/libslic3r/PrintConfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 50aee4cdd..a12537b1d 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -162,7 +162,7 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("archimedeanchords"); def->enum_values.push_back("octagramspiral"); def->enum_labels.push_back("Rectilinear"); - def->enum_labels.push_back("AlignedRectilinear"); + def->enum_labels.push_back("Aligned Rectilinear"); def->enum_labels.push_back("Concentric"); def->enum_labels.push_back("Hilbert Curve"); def->enum_labels.push_back("Archimedean Chords"); From d7edc08287fb00cde3ca0d29d5101c950e048b20 Mon Sep 17 00:00:00 2001 From: uclaros Date: Wed, 2 Nov 2016 14:05:36 +0200 Subject: [PATCH 020/225] Added small axis marker on the center of rotation when rotating or translating. --- lib/Slic3r/GUI/3DScene.pm | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index a36d2263c..95269cbd2 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -46,6 +46,7 @@ use constant GROUND_Z => -0.02; use constant DEFAULT_COLOR => [1,1,0]; use constant SELECTED_COLOR => [0,1,0,1]; use constant HOVER_COLOR => [0.4,0.9,0,1]; +use constant PI => 3.1415927; # make OpenGL::Array thread-safe { @@ -833,11 +834,46 @@ sub Render { glDisable(GL_BLEND); } + if (defined $self->_drag_start_pos || defined $self->_drag_start_xy) { + $self->draw_center_of_rotation($self->_camera_target->x, $self->_camera_target->y, $self->_camera_target->z); + } + glFlush(); $self->SwapBuffers(); } +sub draw_axes { + my ($self, $x, $y, $z, $length, $width, $allways_visible) = @_; + if ($allways_visible) { + glDisable(GL_DEPTH_TEST); + } else { + glEnable(GL_DEPTH_TEST); + } + glLineWidth($width); + glBegin(GL_LINES); + # draw line for x axis + glColor3f(1, 0, 0); + glVertex3f($x, $y, $z); + glVertex3f($x + $length, $y, $z); + # draw line for y axis + glColor3f(0, 1, 0); + glVertex3f($x, $y, $z); + glVertex3f($x, $y + $length, $z); + # draw line for Z axis + glColor3f(0, 0, 1); + glVertex3f($x, $y, $z); + glVertex3f($x, $y, $z + $length); + glEnd(); +} + +sub draw_center_of_rotation { + my ($self, $x, $y, $z) = @_; + + $self->draw_axes($x, $y, $z, 10, 1, 1); + $self->draw_axes($x, $y, $z, 10, 4, 0); +} + sub draw_volumes { my ($self, $fakecolor) = @_; From 8738d7f5e7b7964c38479754e9bae7b23bebe6c9 Mon Sep 17 00:00:00 2001 From: uclaros Date: Wed, 2 Nov 2016 14:11:22 +0200 Subject: [PATCH 021/225] Don't select objects when the shift button pressed. This allows for easier rotating when zoomed in. --- lib/Slic3r/GUI/3DScene.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 95269cbd2..ce158bd74 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -148,7 +148,7 @@ sub mouse_event { } elsif ($e->LeftDClick) { $self->on_double_click->() if $self->on_double_click; - } elsif ($e->LeftDown || $e->RightDown) { + } elsif (($e->LeftDown || $e->RightDown) && not $e->ShiftDown) { # If user pressed left or right button we first check whether this happened # on a volume or not. my $volume_idx = $self->_hover_volume_idx // -1; From 45922e6f5df5ee74e63c4945cd9c4224692258f8 Mon Sep 17 00:00:00 2001 From: uclaros Date: Wed, 2 Nov 2016 14:38:25 +0200 Subject: [PATCH 022/225] DoubleClick middle mouse button to zoom to extents (AutoCad style). --- lib/Slic3r/GUI/3DScene.pm | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index ce158bd74..079008314 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -148,6 +148,14 @@ sub mouse_event { } elsif ($e->LeftDClick) { $self->on_double_click->() if $self->on_double_click; + } elsif ($e->MiddleDClick) { + if (@{$self->volumes}) { + $self->zoom_to_volumes; + } else { + $self->zoom_to_bed; + } + $self->_dirty(1); + $self->Refresh; } elsif (($e->LeftDown || $e->RightDown) && not $e->ShiftDown) { # If user pressed left or right button we first check whether this happened # on a volume or not. From 6563a5fe9ac6a34212cdcdc9767fd209fce91a6a Mon Sep 17 00:00:00 2001 From: uclaros Date: Wed, 2 Nov 2016 15:53:09 +0200 Subject: [PATCH 023/225] Use Alt modifier to move camera center (center of rotation) up or down --- lib/Slic3r/GUI/3DScene.pm | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 079008314..9adf7db03 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -218,7 +218,16 @@ sub mouse_event { $self->_dragged(1); $self->Refresh; } elsif ($e->Dragging) { - if ($e->LeftIsDown) { + if ($e->AltDown) { + # Move the camera center on the Z axis based on mouse Y axis movement + if (defined $self->_drag_start_pos) { + my $orig = $self->_drag_start_pos; + $self->_camera_target->translate(0, 0, $pos->y - $orig->y); + $self->on_viewport_changed->() if $self->on_viewport_changed; + $self->Refresh; + } + $self->_drag_start_pos($pos); + } elsif ($e->LeftIsDown) { # if dragging over blank area with left button, rotate if (defined $self->_drag_start_pos) { my $orig = $self->_drag_start_pos; From 11fa9e47ab8fd09a1b02c46b40e2f073e6b7a756 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Tue, 8 Nov 2016 10:13:28 -0600 Subject: [PATCH 024/225] Fix to output correct set & wait gcode for Sailfish and MakerWare. Fixes #3546 and reimplements #3547 --- xs/src/libslic3r/GCodeWriter.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/xs/src/libslic3r/GCodeWriter.cpp b/xs/src/libslic3r/GCodeWriter.cpp index d256ab2af..a7a52cc1a 100644 --- a/xs/src/libslic3r/GCodeWriter.cpp +++ b/xs/src/libslic3r/GCodeWriter.cpp @@ -78,11 +78,9 @@ GCodeWriter::postamble() const std::string GCodeWriter::set_temperature(unsigned int temperature, bool wait, int tool) const { - if (wait && (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish))) - return ""; std::string code, comment; - if (wait && FLAVOR_IS_NOT(gcfTeacup)) { + if (wait && FLAVOR_IS_NOT(gcfTeacup) && FLAVOR_IS_NOT(gcfMakerWare) && FLAVOR_IS_NOT(gcfSailfish)) { code = "M109"; comment = "set temperature and wait for it to be reached"; } else { @@ -106,6 +104,9 @@ GCodeWriter::set_temperature(unsigned int temperature, bool wait, int tool) cons if (FLAVOR_IS(gcfTeacup) && wait) gcode << "M116 ; wait for temperature to be reached\n"; + if (wait && tool !=-1 && (FLAVOR_IS(gcfMakerWare) || FLAVOR_IS(gcfSailfish))) + gcode << "M6 T" << tool << " ; wait for temperature to be reached\n"; + return gcode.str(); } From d066f60964259b5967ea5265645285760081892d Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Tue, 8 Nov 2016 10:15:05 -0600 Subject: [PATCH 025/225] Pass in tool ID for toolchange temperature delta. Fixes #3546 and reimplements #3547 for master --- xs/src/libslic3r/GCode.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 41a8671a6..8780ef61e 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -98,7 +98,7 @@ OozePrevention::pre_toolchange(GCode &gcodegen) if (gcodegen.config.standby_temperature_delta.value != 0) { // we assume that heating is always slower than cooling, so no need to block gcode += gcodegen.writer.set_temperature - (this->_get_temp(gcodegen) + gcodegen.config.standby_temperature_delta.value, false); + (this->_get_temp(gcodegen) + gcodegen.config.standby_temperature_delta.value, false, gcodegen.writer.extruder()->id); } return gcode; @@ -110,7 +110,7 @@ OozePrevention::post_toolchange(GCode &gcodegen) std::string gcode; if (gcodegen.config.standby_temperature_delta.value != 0) { - gcode += gcodegen.writer.set_temperature(this->_get_temp(gcodegen), true); + gcode += gcodegen.writer.set_temperature(this->_get_temp(gcodegen), true, gcodegen.writer.extruder()->id); } return gcode; From 1e37195af1d93b0b4e18871e58a6229f61bd2a0d Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Tue, 8 Nov 2016 10:23:30 -0600 Subject: [PATCH 026/225] Unit test to verify whether Slic3r would hang when croaking from a C++ exception handler. This is an unfortunate error in some Strawberry Perl distributions. Conflicts: xs/xsp/XS.xsp --- xs/MANIFEST | 1 + xs/t/22_exception.t | 16 ++++++++++++++++ xs/xsp/XS.xsp | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 xs/t/22_exception.t diff --git a/xs/MANIFEST b/xs/MANIFEST index 4e544d472..5453a289c 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -121,6 +121,7 @@ t/18_motionplanner.t t/19_model.t t/20_print.t t/21_gcode.t +t/22_exception.t xsp/BoundingBox.xsp xsp/BridgeDetector.xsp xsp/Clipper.xsp diff --git a/xs/t/22_exception.t b/xs/t/22_exception.t new file mode 100644 index 000000000..ca2ffea89 --- /dev/null +++ b/xs/t/22_exception.t @@ -0,0 +1,16 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Slic3r::XS; +use Test::More tests => 1; + +{ + eval { + Slic3r::xspp_test_croak_hangs_on_strawberry(); + }; + is $@, "xspp_test_croak_hangs_on_strawberry: exception catched\n", 'croak from inside a C++ exception delivered'; +} + +__END__ diff --git a/xs/xsp/XS.xsp b/xs/xsp/XS.xsp index c54e06be5..c324396b2 100644 --- a/xs/xsp/XS.xsp +++ b/xs/xsp/XS.xsp @@ -16,4 +16,24 @@ VERSION() RETVAL = newSVpv(SLIC3R_VERSION, 0); OUTPUT: RETVAL +SV* +DEBUG_OUT_PATH_PREFIX() + CODE: + RETVAL = newSVpv(SLIC3R_DEBUG_OUT_PATH_PREFIX, 0); + OUTPUT: RETVAL + +SV* +FORK_NAME() + CODE: + RETVAL = newSVpv(SLIC3R_FORK_NAME, 0); + OUTPUT: RETVAL + +void +xspp_test_croak_hangs_on_strawberry() + CODE: + try { + throw 1; + } catch (...) { + croak("xspp_test_croak_hangs_on_strawberry: exception catched\n"); + } %} \ No newline at end of file From 07009b601f75e6166d75ae6dd292c9863356e99e Mon Sep 17 00:00:00 2001 From: uclaros Date: Wed, 9 Nov 2016 21:41:47 +0200 Subject: [PATCH 027/225] Set the tooltip timer to the max accepted value --- lib/Slic3r/GUI/MainFrame.pm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index d97ad22ef..ad1c0fcb2 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -33,6 +33,10 @@ sub new { $self->_init_tabpanel; $self->_init_menubar; + # set default tooltip timer in msec + # SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values + Wx::ToolTip::SetAutoPop(32767); + # initialize status bar $self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, -1); $self->{statusbar}->SetStatusText("Version $Slic3r::VERSION - Remember to check for updates at http://slic3r.org/"); From 94f60db114f45433c47e1cf71f84ab552902d99e Mon Sep 17 00:00:00 2001 From: uclaros Date: Thu, 10 Nov 2016 00:29:02 +0200 Subject: [PATCH 028/225] Fixed a bug with the "Window" menu items. If the "Controller" tab was not visible, then the menu items and their shortcuts oppened the wrong tab. Also, the controller tab was not visible if the plater tab was not visible too. --- lib/Slic3r/GUI/MainFrame.pm | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index d97ad22ef..cbf303d1a 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -98,9 +98,9 @@ sub _init_tabpanel { if (!$self->{no_plater}) { $panel->AddPage($self->{plater} = Slic3r::GUI::Plater->new($panel), "Plater"); - if (!$self->{no_controller}) { - $panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller"); - } + } + if (!$self->{no_controller}) { + $panel->AddPage($self->{controller} = Slic3r::GUI::Controller->new($panel), "Controller"); } $self->{options_tabs} = {}; @@ -254,14 +254,18 @@ sub _init_menubar { $self->_append_menu_item($windowMenu, "Select &Plater Tab\tCtrl+1", 'Show the plater', sub { $self->select_tab(0); }, undef, 'application_view_tile.png'); - if (!$self->{no_controller}) { - $self->_append_menu_item($windowMenu, "Select &Controller Tab\tCtrl+T", 'Show the printer controller', sub { - $self->select_tab(1); - }, undef, 'printer_empty.png'); - } - $windowMenu->AppendSeparator(); - $tab_offset += 2; + $tab_offset += 1; } + if (!$self->{no_controller}) { + $self->_append_menu_item($windowMenu, "Select &Controller Tab\tCtrl+T", 'Show the printer controller', sub { + $self->select_tab(1); + }, undef, 'printer_empty.png'); + $tab_offset += 1; + } + if ($tab_offset > 0) { + $windowMenu->AppendSeparator(); + } + $self->_append_menu_item($windowMenu, "Select P&rint Settings Tab\tCtrl+2", 'Show the print settings', sub { $self->select_tab($tab_offset+0); }, undef, 'cog.png'); From b28069fe5fc246e778e6c7f58563b122aac1b874 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 13 Nov 2016 16:46:17 -0600 Subject: [PATCH 029/225] Update XS.xsp Removed references to prusa-specific SLIC3R_FORK_NAME --- xs/xsp/XS.xsp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/xs/xsp/XS.xsp b/xs/xsp/XS.xsp index c324396b2..1f6c8b610 100644 --- a/xs/xsp/XS.xsp +++ b/xs/xsp/XS.xsp @@ -16,18 +16,6 @@ VERSION() RETVAL = newSVpv(SLIC3R_VERSION, 0); OUTPUT: RETVAL -SV* -DEBUG_OUT_PATH_PREFIX() - CODE: - RETVAL = newSVpv(SLIC3R_DEBUG_OUT_PATH_PREFIX, 0); - OUTPUT: RETVAL - -SV* -FORK_NAME() - CODE: - RETVAL = newSVpv(SLIC3R_FORK_NAME, 0); - OUTPUT: RETVAL - void xspp_test_croak_hangs_on_strawberry() CODE: @@ -36,4 +24,4 @@ xspp_test_croak_hangs_on_strawberry() } catch (...) { croak("xspp_test_croak_hangs_on_strawberry: exception catched\n"); } -%} \ No newline at end of file +%} From 4cd8ea9e5fad7a8dc2300518dc926b81d4efbe59 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Thu, 17 Nov 2016 12:05:42 -0600 Subject: [PATCH 030/225] hamfisted fix for opengl 0.70 problems, making and using buffers explicitly for bed grid base --- lib/Slic3r/GUI/3DScene.pm | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index a36d2263c..715be6402 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -750,15 +750,26 @@ sub Render { # draw ground my $ground_z = GROUND_Z; if ($self->bed_triangles) { + my($VertexObjID,$NormalObjID,$ColorObjID); + my $colors = OpenGL::Array->new_list(GL_FLOAT,(0.8,0.6,0.5,0.4)); + my $norms = OpenGL::Array->new_list(GL_FLOAT,(0,0,1)); + ($VertexObjID,$NormalObjID,$ColorObjID) = + glGenBuffersARB_p(3); + $self->bed_triangles->bind($VertexObjID); + glBufferDataARB_p(GL_ARRAY_BUFFER_ARB, $self->bed_triangles, GL_STATIC_DRAW_ARB); + glVertexPointer_c(3, GL_FLOAT, 0, 0); + $norms->bind($NormalObjID); + glBufferDataARB_p(GL_ARRAY_BUFFER_ARB, $norms, GL_STATIC_DRAW_ARB); + glNormalPointer_c(GL_FLOAT, 0, 0); + glDisable(GL_DEPTH_TEST); - + glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnableClientState(GL_VERTEX_ARRAY); - glColor4f(0.8, 0.6, 0.5, 0.4); - glNormal3d(0,0,1); - glVertexPointer_p(3, $self->bed_triangles); + #glNormal3d(0,0,1); + #glVertexPointer_p(3, $self->bed_triangles); glDrawArrays(GL_TRIANGLES, 0, $self->bed_triangles->elements / 3); glDisableClientState(GL_VERTEX_ARRAY); @@ -766,11 +777,17 @@ sub Render { # the object from below glEnable(GL_DEPTH_TEST); + my($GridVertexObjID,$GridNormalObjID,$GridColorObjID); + ($GridVertexObjID,$GridNormalObjID,$GridColorObjID) = + glGenBuffersARB_p(3); + glBufferDataARB_p(GL_ARRAY_BUFFER_ARB, $self->bed_grid_lines, GL_STATIC_DRAW_ARB); + glVertexPointer_c(3, GL_FLOAT, 0, 0); # draw grid glLineWidth(3); glColor4f(0.2, 0.2, 0.2, 0.4); glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer_p(3, $self->bed_grid_lines); + + #glVertexPointer_p(3, $self->bed_grid_lines); glDrawArrays(GL_LINES, 0, $self->bed_grid_lines->elements / 3); glDisableClientState(GL_VERTEX_ARRAY); From 2df6b00f1d6f3e8bdfab0e047bb8a83eebc5245c Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Thu, 17 Nov 2016 13:10:22 -0600 Subject: [PATCH 031/225] Significant code cleanup; remember to turn off VBOs after done with use to not interfere with other code. --- lib/Slic3r/GUI/3DScene.pm | 62 ++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 715be6402..7f6f7ae97 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1,9 +1,9 @@ package Slic3r::GUI::3DScene::Base; use strict; use warnings; - use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS); # must load OpenGL *before* Wx::GLCanvas + use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants); use base qw(Wx::GLCanvas Class::Accessor); use Math::Trig qw(asin); @@ -46,6 +46,8 @@ use constant GROUND_Z => -0.02; use constant DEFAULT_COLOR => [1,1,0]; use constant SELECTED_COLOR => [0,1,0,1]; use constant HOVER_COLOR => [0.4,0.9,0,1]; +use constant HAS_VBO => 1; + # make OpenGL::Array thread-safe { @@ -56,6 +58,7 @@ use constant HOVER_COLOR => [0.4,0.9,0,1]; sub new { my ($class, $parent) = @_; + # We can only enable multi sample anti aliasing wih wxWidgets 3.0.3 and with a hacked Wx::GLCanvas, # which exports some new WX_GL_XXX constants, namely WX_GL_SAMPLE_BUFFERS and WX_GL_SAMPLES. my $can_multisample = @@ -74,6 +77,7 @@ sub new { # we request a depth buffer explicitely because it looks like it's not created by # default on Linux, causing transparency issues my $self = $class->SUPER::new($parent, -1, Wx::wxDefaultPosition, Wx::wxDefaultSize, 0, "", $attrib); + if (Wx::wxVERSION >= 3.000003) { # Wx 3.0.3 contains an ugly hack to support some advanced OpenGL attributes through the attribute list. # The attribute list is transferred between the wxGLCanvas and wxGLContext constructors using a single static array s_wglContextAttribs. @@ -133,6 +137,7 @@ sub new { $self->Refresh; }); EVT_MOUSE_EVENTS($self, \&mouse_event); + return $self; } @@ -750,26 +755,27 @@ sub Render { # draw ground my $ground_z = GROUND_Z; if ($self->bed_triangles) { - my($VertexObjID,$NormalObjID,$ColorObjID); - my $colors = OpenGL::Array->new_list(GL_FLOAT,(0.8,0.6,0.5,0.4)); - my $norms = OpenGL::Array->new_list(GL_FLOAT,(0,0,1)); - ($VertexObjID,$NormalObjID,$ColorObjID) = - glGenBuffersARB_p(3); - $self->bed_triangles->bind($VertexObjID); - glBufferDataARB_p(GL_ARRAY_BUFFER_ARB, $self->bed_triangles, GL_STATIC_DRAW_ARB); - glVertexPointer_c(3, GL_FLOAT, 0, 0); - $norms->bind($NormalObjID); - glBufferDataARB_p(GL_ARRAY_BUFFER_ARB, $norms, GL_STATIC_DRAW_ARB); - glNormalPointer_c(GL_FLOAT, 0, 0); + my $norms = OpenGL::Array->new_list(GL_FLOAT,(0.0, 0.0, 1.0)); # common normal for both glDisable(GL_DEPTH_TEST); - + glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnableClientState(GL_VERTEX_ARRAY); - #glNormal3d(0,0,1); - #glVertexPointer_p(3, $self->bed_triangles); + if (HAS_VBO) { + my ($triangle_vertex); + ($triangle_vertex) = + glGenBuffersARB_p(1); + $self->bed_triangles->bind($triangle_vertex); + glBufferDataARB_p(GL_ARRAY_BUFFER_ARB, $self->bed_triangles, GL_STATIC_DRAW_ARB); + glVertexPointer_c(3, GL_FLOAT, 0, 0); + } else { + # fall back on old behavior + glVertexPointer_p(3, $self->bed_triangles); + } + glColor4f(0.8, 0.6, 0.5, 0.4); + glNormal3d(0,0,1); glDrawArrays(GL_TRIANGLES, 0, $self->bed_triangles->elements / 3); glDisableClientState(GL_VERTEX_ARRAY); @@ -777,21 +783,31 @@ sub Render { # the object from below glEnable(GL_DEPTH_TEST); - my($GridVertexObjID,$GridNormalObjID,$GridColorObjID); - ($GridVertexObjID,$GridNormalObjID,$GridColorObjID) = - glGenBuffersARB_p(3); - glBufferDataARB_p(GL_ARRAY_BUFFER_ARB, $self->bed_grid_lines, GL_STATIC_DRAW_ARB); - glVertexPointer_c(3, GL_FLOAT, 0, 0); # draw grid glLineWidth(3); - glColor4f(0.2, 0.2, 0.2, 0.4); glEnableClientState(GL_VERTEX_ARRAY); - - #glVertexPointer_p(3, $self->bed_grid_lines); + if (HAS_VBO) { + my ($grid_vertex); + ($grid_vertex) = + glGenBuffersARB_p(1); + $self->bed_grid_lines->bind($grid_vertex); + glBufferDataARB_p(GL_ARRAY_BUFFER_ARB, $self->bed_grid_lines, GL_STATIC_DRAW_ARB); + glVertexPointer_c(3, GL_FLOAT, 0, 0); + } else { + # fall back on old behavior + glVertexPointer_p(3, $self->bed_grid_lines); + } + glColor4f(0.2, 0.2, 0.2, 0.4); + glNormal3d(0,0,1); glDrawArrays(GL_LINES, 0, $self->bed_grid_lines->elements / 3); glDisableClientState(GL_VERTEX_ARRAY); glDisable(GL_BLEND); + if (HAS_VBO) { + # Turn off buffer objects to let the rest of the draw code work. + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + } } my $volumes_bb = $self->volumes_bounding_box; From 0563a95b3bc43e523f26ac024eaac9c18fa41c77 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Thu, 17 Nov 2016 23:01:15 -0600 Subject: [PATCH 032/225] Added comments and VBO implementation for drawing of cut plane, which also crashes. --- lib/Slic3r/GUI/3DScene.pm | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 7f6f7ae97..ee9e310c1 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -46,6 +46,10 @@ use constant GROUND_Z => -0.02; use constant DEFAULT_COLOR => [1,1,0]; use constant SELECTED_COLOR => [0,1,0,1]; use constant HOVER_COLOR => [0.4,0.9,0,1]; + +# Constant to determine if Vertex Buffer objects are used to draw +# bed grid and the cut plane for object separation. +# Old Perl (5.10.x) should set to 0. use constant HAS_VBO => 1; @@ -755,8 +759,6 @@ sub Render { # draw ground my $ground_z = GROUND_Z; if ($self->bed_triangles) { - my $norms = OpenGL::Array->new_list(GL_FLOAT,(0.0, 0.0, 1.0)); # common normal for both - glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); @@ -947,10 +949,26 @@ sub draw_volumes { glDisable(GL_BLEND); if (defined $self->cutting_plane_z) { + if (HAS_VBO) { + # Use Vertex Buffer Object for cutting plane (previous method crashes on modern POGL). + my ($cut_vertex) = glGenBuffersARB_p(1); + $self->cut_lines_vertices->bind($cut_vertex); + glBufferDataARB_p(GL_ARRAY_BUFFER_ARB, $self->cut_lines_vertices, GL_STATIC_DRAW_ARB); + glVertexPointer_c(3, GL_FLOAT, 0, 0); + } else { + # Use legacy method. + glVertexPointer_p(3, $self->cut_lines_vertices); + } glLineWidth(2); glColor3f(0, 0, 0); - glVertexPointer_p(3, $self->cut_lines_vertices); glDrawArrays(GL_LINES, 0, $self->cut_lines_vertices->elements / 3); + + if (HAS_VBO) { + # Turn off buffer objects to let the rest of the draw code work. + glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + } + } glDisableClientState(GL_VERTEX_ARRAY); } From fded56c1264c4e126968a28b9577cce3abca99a9 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 11 Nov 2016 15:05:39 +0100 Subject: [PATCH 033/225] New feature: Propose to enable "detect bridging perimeters" when the supports are first enabled. Don't check keep asking, if the user does not want the bridging perimeters to be enabled. --- lib/Slic3r/GUI/Tab.pm | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index fe4c7e452..9eaeedef5 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -761,6 +761,35 @@ sub _update { $self->load_config($new_conf); } } + + if ($config->support_material) { + # Ask only once. + if (! $self->{support_material_overhangs_queried}) { + $self->{support_material_overhangs_queried} = 1; + if ($config->overhangs != 1) { + my $dialog = Wx::MessageDialog->new($self, + "Supports work better, if the following feature is enabled:\n" + . "- Detect bridging perimeters\n" + . "\nShall I adjust those settings for supports?", + 'Support Generator', wxICON_WARNING | wxYES | wxNO | wxCANCEL); + my $answer = $dialog->ShowModal(); + my $new_conf = Slic3r::Config->new; + if ($answer == wxID_YES) { + # Enable "detect bridging perimeters". + $new_conf->set("overhangs", 1); + } elsif ($answer == wxID_NO) { + # Do nothing, leave supports on and "detect bridging perimeters" off. + } elsif ($answer == wxID_CANCEL) { + # Disable supports. + $new_conf->set("support_material", 0); + $self->{support_material_overhangs_queried} = 0; + } + $self->load_config($new_conf); + } + } + } else { + $self->{support_material_overhangs_queried} = 0; + } if ($config->fill_density == 100 && !first { $_ eq $config->fill_pattern } @{$Slic3r::Config::Options->{external_fill_pattern}{values}}) { From be00f642438c9825ef67b5d29f3b4aa70624e50c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 19 Nov 2016 20:46:19 +0100 Subject: [PATCH 034/225] Include support material in raft shape for --export-svg as well --- xs/src/libslic3r/SVGExport.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/xs/src/libslic3r/SVGExport.cpp b/xs/src/libslic3r/SVGExport.cpp index a209977cf..3eede97a0 100644 --- a/xs/src/libslic3r/SVGExport.cpp +++ b/xs/src/libslic3r/SVGExport.cpp @@ -41,19 +41,6 @@ SVGExport::writeSVG(const std::string &outputfile) std::vector layers; TriangleMeshSlicer(&this->mesh).slice(slice_z, &layers); - // generate a solid raft if requested - if (this->config.raft_layers > 0) { - ExPolygons raft = offset_ex(layers.front(), scale_(this->config.raft_offset)); - for (int i = this->config.raft_layers; i >= 1; --i) { - layer_z.insert(layer_z.begin(), first_lh + lh * (i-1)); - layers.insert(layers.begin(), raft); - } - - // prepend total raft height to all sliced layers - for (int i = this->config.raft_layers; i < layer_z.size(); ++i) - layer_z[i] += first_lh + lh * (this->config.raft_layers-1); - } - // generate support material std::vector support_material(layers.size()); if (this->config.support_material) { @@ -82,6 +69,21 @@ SVGExport::writeSVG(const std::string &outputfile) } } + // generate a solid raft if requested + // (do this after support material because we take support material shape into account) + if (this->config.raft_layers > 0) { + ExPolygons raft = offset_ex(layers.front(), scale_(this->config.raft_offset)); + for (int i = this->config.raft_layers; i >= 1; --i) { + layer_z.insert(layer_z.begin(), first_lh + lh * (i-1)); + layers.insert(layers.begin(), raft); + support_material.insert(support_material.begin(), Points()); // no support on this layer, we have raft + } + + // prepend total raft height to all sliced layers + for (int i = this->config.raft_layers; i < layer_z.size(); ++i) + layer_z[i] += first_lh + lh * (this->config.raft_layers-1); + } + double support_material_radius = this->config.support_material_extrusion_width.get_abs_value(this->config.layer_height)/2; FILE* f = fopen(outputfile.c_str(), "w"); From 9fcd7f38de44ecaa09c40db0b3846e7adaf6e0ef Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 21 Nov 2016 18:30:35 +0100 Subject: [PATCH 035/225] Refactored Clipper wrapper functions --- lib/Slic3r/Geometry/Clipper.pm | 4 +- xs/src/libslic3r/BridgeDetector.cpp | 65 +-- xs/src/libslic3r/BridgeDetector.hpp | 2 - xs/src/libslic3r/ClipperUtils.cpp | 586 ++++++++---------------- xs/src/libslic3r/ClipperUtils.hpp | 199 +++++--- xs/src/libslic3r/ExPolygon.cpp | 29 +- xs/src/libslic3r/ExPolygon.hpp | 2 + xs/src/libslic3r/ExtrusionEntity.cpp | 10 +- xs/src/libslic3r/GCode.cpp | 7 +- xs/src/libslic3r/Geometry.cpp | 14 - xs/src/libslic3r/Geometry.hpp | 1 - xs/src/libslic3r/Layer.cpp | 6 +- xs/src/libslic3r/LayerRegion.cpp | 3 +- xs/src/libslic3r/MotionPlanner.cpp | 6 +- xs/src/libslic3r/PerimeterGenerator.cpp | 6 +- xs/src/libslic3r/Polygon.cpp | 13 +- xs/src/libslic3r/Print.cpp | 10 +- xs/src/libslic3r/PrintObject.cpp | 2 +- xs/src/libslic3r/Surface.cpp | 11 + xs/src/libslic3r/Surface.hpp | 2 + xs/src/libslic3r/TriangleMesh.cpp | 10 +- xs/src/perlglue.cpp | 3 +- xs/xsp/Clipper.xsp | 209 ++------- xs/xsp/Geometry.xsp | 9 - xs/xsp/Polyline.xsp | 2 +- xs/xsp/Surface.xsp | 2 +- xs/xsp/typemap.xspt | 3 +- 27 files changed, 449 insertions(+), 767 deletions(-) diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index 652abb8db..a3239c2fa 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -5,9 +5,9 @@ use warnings; require Exporter; our @ISA = qw(Exporter); our @EXPORT_OK = qw(offset offset_ex - diff_ex diff union_ex intersection_ex xor_ex JT_ROUND JT_MITER + diff_ex diff union_ex intersection_ex JT_ROUND JT_MITER JT_SQUARE is_counter_clockwise union_pt offset2 offset2_ex intersection intersection_pl diff_pl union CLIPPER_OFFSET_SCALE - union_pt_chained diff_ppl intersection_ppl); + union_pt_chained intersection_ppl); 1; diff --git a/xs/src/libslic3r/BridgeDetector.cpp b/xs/src/libslic3r/BridgeDetector.cpp index 46d6ff1f3..f9061e405 100644 --- a/xs/src/libslic3r/BridgeDetector.cpp +++ b/xs/src/libslic3r/BridgeDetector.cpp @@ -30,12 +30,11 @@ BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonColle { /* outset our bridge by an arbitrary amout; we'll use this outer margin for detecting anchors */ - Polygons grown; - offset((Polygons)this->expolygon, &grown, this->extrusion_width); + Polygons grown = offset(this->expolygon, this->extrusion_width); // detect what edges lie on lower slices by turning bridge contour and holes // into polylines and then clipping them with each lower slice's contour - intersection(grown, this->lower_slices.contours(), &this->_edges); + this->_edges = intersection_pl(grown, this->lower_slices.contours()); #ifdef SLIC3R_DEBUG printf(" bridge has %zu support(s)\n", this->_edges.size()); @@ -43,7 +42,7 @@ BridgeDetector::BridgeDetector(const ExPolygon &_expolygon, const ExPolygonColle // detect anchors as intersection between our bridge expolygon and the lower slices // safety offset required to avoid Clipper from detecting empty intersection while Boost actually found some edges - intersection(grown, this->lower_slices, &this->_anchors, true); + this->_anchors = intersection_ex(grown, this->lower_slices, true); /* if (0) { @@ -65,8 +64,7 @@ BridgeDetector::detect_angle() /* Outset the bridge expolygon by half the amount we used for detecting anchors; we'll use this one to clip our test lines and be sure that their endpoints are inside the anchors and not on their contours leading to false negatives. */ - Polygons clip_area; - offset(this->expolygon, &clip_area, +this->extrusion_width/2); + Polygons clip_area = offset(this->expolygon, +this->extrusion_width/2); /* we'll now try several directions using a rudimentary visibility check: bridge in several directions and then sum the length of lines having both @@ -131,8 +129,7 @@ BridgeDetector::detect_angle() for (coord_t y = bb.min.y; y <= bb.max.y; y += line_increment) lines.push_back(Line(Point(bb.min.x, y), Point(bb.max.x, y))); - Lines clipped_lines; - intersection(lines, my_clip_area, &clipped_lines); + Lines clipped_lines = intersection_ln(lines, my_clip_area); // remove any line not having both endpoints within anchors for (size_t i = 0; i < clipped_lines.size(); ++i) { @@ -194,11 +191,11 @@ BridgeDetector::detect_angle() return true; } -void -BridgeDetector::coverage(double angle, Polygons* coverage) const +Polygons +BridgeDetector::coverage(double angle) const { if (angle == -1) angle = this->angle; - if (angle == -1) return; + if (angle == -1) return Polygons(); // Clone our expolygon and rotate it so that we work with vertical lines. ExPolygon expolygon = this->expolygon; @@ -207,8 +204,7 @@ BridgeDetector::coverage(double angle, Polygons* coverage) const /* Outset the bridge expolygon by half the amount we used for detecting anchors; we'll use this one to generate our trapezoids and be sure that their vertices are inside the anchors and not on their contours leading to false negatives. */ - ExPolygons grown; - offset(expolygon, &grown, this->extrusion_width/2.0); + ExPolygons grown = offset_ex(expolygon, this->extrusion_width/2.0); // Compute trapezoids according to a vertical orientation Polygons trapezoids; @@ -226,9 +222,7 @@ BridgeDetector::coverage(double angle, Polygons* coverage) const Polygons covered; for (Polygons::const_iterator trapezoid = trapezoids.begin(); trapezoid != trapezoids.end(); ++trapezoid) { - Lines lines = trapezoid->lines(); - Lines supported; - intersection(lines, anchors, &supported); + Lines supported = intersection_ln(trapezoid->lines(), anchors); // not nice, we need a more robust non-numeric check for (size_t i = 0; i < supported.size(); ++i) { @@ -242,14 +236,13 @@ BridgeDetector::coverage(double angle, Polygons* coverage) const } // merge trapezoids and rotate them back - Polygons _coverage; - union_(covered, &_coverage); + Polygons _coverage = union_(covered); for (Polygons::iterator p = _coverage.begin(); p != _coverage.end(); ++p) p->rotate(-(PI/2.0 - angle), Point(0,0)); // intersect trapezoids with actual bridge area to remove extra margins // and append it to result - intersection(_coverage, this->expolygon, coverage); + return intersection(_coverage, this->expolygon); /* if (0) { @@ -268,22 +261,14 @@ BridgeDetector::coverage(double angle, Polygons* coverage) const */ } -Polygons -BridgeDetector::coverage(double angle) const -{ - Polygons pp; - this->coverage(angle, &pp); - return pp; -} - /* This method returns the bridge edges (as polylines) that are not supported but would allow the entire bridge area to be bridged with detected angle if supported too */ -void -BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const +Polylines +BridgeDetector::unsupported_edges(double angle) const { if (angle == -1) angle = this->angle; - if (angle == -1) return; + if (angle == -1) return Polylines(); // get bridge edges (both contour and holes) Polylines bridge_edges; @@ -293,10 +278,10 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const } // get unsupported edges - Polygons grown_lower; - offset(this->lower_slices, &grown_lower, +this->extrusion_width); - Polylines _unsupported; - diff(bridge_edges, grown_lower, &_unsupported); + Polylines _unsupported = diff_pl( + bridge_edges, + offset(this->lower_slices, +this->extrusion_width) + ); /* Split into individual segments and filter out edges parallel to the bridging angle TODO: angle tolerance should probably be based on segment length and flow width, @@ -304,13 +289,15 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const extrusions would be anchored within such length (i.e. a slightly non-parallel bridging direction might still benefit from anchors if long enough) double angle_tolerance = PI / 180.0 * 5.0; */ + Polylines unsupported; for (Polylines::const_iterator polyline = _unsupported.begin(); polyline != _unsupported.end(); ++polyline) { Lines lines = polyline->lines(); for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { if (!Slic3r::Geometry::directions_parallel(line->direction(), angle)) - unsupported->push_back(*line); + unsupported.push_back(*line); } } + return unsupported; /* if (0) { @@ -328,12 +315,4 @@ BridgeDetector::unsupported_edges(double angle, Polylines* unsupported) const */ } -Polylines -BridgeDetector::unsupported_edges(double angle) const -{ - Polylines pp; - this->unsupported_edges(angle, &pp); - return pp; -} - } diff --git a/xs/src/libslic3r/BridgeDetector.hpp b/xs/src/libslic3r/BridgeDetector.hpp index 5b1566b27..14bfd80af 100644 --- a/xs/src/libslic3r/BridgeDetector.hpp +++ b/xs/src/libslic3r/BridgeDetector.hpp @@ -18,9 +18,7 @@ class BridgeDetector { BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width); bool detect_angle(); - void coverage(double angle, Polygons* coverage) const; Polygons coverage(double angle = -1) const; - void unsupported_edges(double angle, Polylines* unsupported) const; Polylines unsupported_edges(double angle = -1) const; private: diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index 69c8643f3..37cc4b698 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -5,54 +5,53 @@ namespace Slic3r { //----------------------------------------------------------- // legacy code from Clipper documentation -void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPolygons* expolygons) +void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, ExPolygons* expolygons) { size_t cnt = expolygons->size(); expolygons->resize(cnt + 1); - ClipperPath_to_Slic3rMultiPoint(polynode.Contour, &(*expolygons)[cnt].contour); + (*expolygons)[cnt].contour = ClipperPath_to_Slic3rMultiPoint(polynode.Contour); (*expolygons)[cnt].holes.resize(polynode.ChildCount()); for (int i = 0; i < polynode.ChildCount(); ++i) { - ClipperPath_to_Slic3rMultiPoint(polynode.Childs[i]->Contour, &(*expolygons)[cnt].holes[i]); + (*expolygons)[cnt].holes[i] = ClipperPath_to_Slic3rMultiPoint(polynode.Childs[i]->Contour); //Add outer polygons contained by (nested within) holes ... for (int j = 0; j < polynode.Childs[i]->ChildCount(); ++j) AddOuterPolyNodeToExPolygons(*polynode.Childs[i]->Childs[j], expolygons); } } -void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons* expolygons) +ExPolygons +PolyTreeToExPolygons(ClipperLib::PolyTree& polytree) { - expolygons->clear(); - for (int i = 0; i < polytree.ChildCount(); ++i) - AddOuterPolyNodeToExPolygons(*polytree.Childs[i], expolygons); + ExPolygons retval; + for (int i = 0; i < polytree.ChildCount(); ++i) + AddOuterPolyNodeToExPolygons(*polytree.Childs[i], &retval); + return retval; } //----------------------------------------------------------- template -void -ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T* output) +T +ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input) { - output->points.clear(); - for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) { - output->points.push_back(Slic3r::Point( (*pit).X, (*pit).Y )); - } + T retval; + for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) + retval.points.push_back(Point( (*pit).X, (*pit).Y )); + return retval; } -template void ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, Slic3r::Polygon* output); template -void -ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T* output) +T +ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input) { - output->clear(); - for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) { - typename T::value_type p; - ClipperPath_to_Slic3rMultiPoint(*it, &p); - output->push_back(p); - } + T retval; + for (ClipperLib::Paths::const_iterator it = input.begin(); it != input.end(); ++it) + retval.push_back(ClipperPath_to_Slic3rMultiPoint(*it)); + return retval; } -void -ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons* output) +ExPolygons +ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input) { // init Clipper ClipperLib::Clipper clipper; @@ -64,29 +63,26 @@ ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolyg clipper.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftEvenOdd, ClipperLib::pftEvenOdd); // offset results work with both EvenOdd and NonZero // write to ExPolygons object - output->clear(); - PolyTreeToExPolygons(polytree, output); + return PolyTreeToExPolygons(polytree); } -void -Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path* output) +ClipperLib::Path +Slic3rMultiPoint_to_ClipperPath(const MultiPoint &input) { - output->clear(); - for (Slic3r::Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit) { - output->push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y )); - } + ClipperLib::Path retval; + for (Points::const_iterator pit = input.points.begin(); pit != input.points.end(); ++pit) + retval.push_back(ClipperLib::IntPoint( (*pit).x, (*pit).y )); + return retval; } template -void -Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths* output) +ClipperLib::Paths +Slic3rMultiPoints_to_ClipperPaths(const T &input) { - output->clear(); - for (typename T::const_iterator it = input.begin(); it != input.end(); ++it) { - ClipperLib::Path p; - Slic3rMultiPoint_to_ClipperPath(*it, &p); - output->push_back(p); - } + ClipperLib::Paths retval; + for (typename T::const_iterator it = input.begin(); it != input.end(); ++it) + retval.push_back(Slic3rMultiPoint_to_ClipperPath(*it)); + return retval; } void @@ -100,13 +96,12 @@ scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale) } } -void -offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta, +ClipperLib::Paths +_offset(const Polygons &polygons, const float delta, double scale, ClipperLib::JoinType joinType, double miterLimit) { // read input - ClipperLib::Paths input; - Slic3rMultiPoints_to_ClipperPaths(polygons, &input); + ClipperLib::Paths input = Slic3rMultiPoints_to_ClipperPaths(polygons); // scale input scaleClipperPolygons(input, scale); @@ -119,40 +114,20 @@ offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float co.MiterLimit = miterLimit; } co.AddPaths(input, joinType, ClipperLib::etClosedPolygon); - co.Execute(*retval, (delta*scale)); + ClipperLib::Paths retval; + co.Execute(retval, (delta*scale)); // unscale output - scaleClipperPolygons(*retval, 1/scale); + scaleClipperPolygons(retval, 1/scale); + return retval; } -void -offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) -{ - // perform offset - ClipperLib::Paths output; - offset(polygons, &output, delta, scale, joinType, miterLimit); - - // convert into ExPolygons - ClipperPaths_to_Slic3rMultiPoints(output, retval); -} - -Slic3r::Polygons -offset(const Slic3r::Polygons &polygons, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) -{ - Slic3r::Polygons pp; - offset(polygons, &pp, delta, scale, joinType, miterLimit); - return pp; -} - -void -offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta, +ClipperLib::Paths +_offset(const Polylines &polylines, const float delta, double scale, ClipperLib::JoinType joinType, double miterLimit) { // read input - ClipperLib::Paths input; - Slic3rMultiPoints_to_ClipperPaths(polylines, &input); + ClipperLib::Paths input = Slic3rMultiPoints_to_ClipperPaths(polylines); // scale input scaleClipperPolygons(input, scale); @@ -165,82 +140,78 @@ offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const floa co.MiterLimit = miterLimit; } co.AddPaths(input, joinType, ClipperLib::etOpenButt); - co.Execute(*retval, (delta*scale)); + ClipperLib::Paths retval; + co.Execute(retval, (delta*scale)); // unscale output - scaleClipperPolygons(*retval, 1/scale); + scaleClipperPolygons(retval, 1/scale); + return retval; } -void -offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float delta, +Polygons +offset(const Polygons &polygons, const float delta, double scale, ClipperLib::JoinType joinType, double miterLimit) { // perform offset - ClipperLib::Paths output; - offset(polylines, &output, delta, scale, joinType, miterLimit); + ClipperLib::Paths output = _offset(polygons, delta, scale, joinType, miterLimit); // convert into ExPolygons - ClipperPaths_to_Slic3rMultiPoints(output, retval); + return ClipperPaths_to_Slic3rMultiPoints(output); } -void -offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float delta, +Polygons +offset(const Polylines &polylines, const float delta, double scale, ClipperLib::JoinType joinType, double miterLimit) { // perform offset - Slic3r::ExPolygons expp; - offset(surface.expolygon, &expp, delta, scale, joinType, miterLimit); + ClipperLib::Paths output = _offset(polylines, delta, scale, joinType, miterLimit); + + // convert into ExPolygons + return ClipperPaths_to_Slic3rMultiPoints(output); +} + +Surfaces +offset(const Surface &surface, const float delta, + double scale, ClipperLib::JoinType joinType, double miterLimit) +{ + // perform offset + ExPolygons expp = offset_ex(surface.expolygon, delta, scale, joinType, miterLimit); // clone the input surface for each expolygon we got - retval->clear(); - retval->reserve(expp.size()); + Surfaces retval; + retval.reserve(expp.size()); for (ExPolygons::iterator it = expp.begin(); it != expp.end(); ++it) { Surface s = surface; // clone s.expolygon = *it; - retval->push_back(s); + retval.push_back(s); } + return retval; } -void -offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta, +ExPolygons +offset_ex(const Polygons &polygons, const float delta, double scale, ClipperLib::JoinType joinType, double miterLimit) { // perform offset - ClipperLib::Paths output; - offset(polygons, &output, delta, scale, joinType, miterLimit); + ClipperLib::Paths output = _offset(polygons, delta, scale, joinType, miterLimit); // convert into ExPolygons - ClipperPaths_to_Slic3rExPolygons(output, retval); + return ClipperPaths_to_Slic3rExPolygons(output); } -Slic3r::ExPolygons -offset_ex(const Slic3r::Polygons &polygons, const float delta, +ExPolygons +offset_ex(const ExPolygons &expolygons, const float delta, double scale, ClipperLib::JoinType joinType, double miterLimit) { - Slic3r::ExPolygons expp; - offset(polygons, &expp, delta, scale, joinType, miterLimit); - return expp; + return offset_ex(to_polygons(expolygons), delta, scale, joinType, miterLimit); } -Slic3r::ExPolygons -offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, - double scale, ClipperLib::JoinType joinType, double miterLimit) -{ - Slic3r::Polygons pp; - for (Slic3r::ExPolygons::const_iterator ex = expolygons.begin(); ex != expolygons.end(); ++ex) { - Slic3r::Polygons pp2 = *ex; - pp.insert(pp.end(), pp2.begin(), pp2.end()); - } - return offset_ex(pp, delta, scale, joinType, miterLimit); -} - -void -offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1, - const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) +ClipperLib::Paths +_offset2(const Polygons &polygons, const float delta1, const float delta2, + const double scale, const ClipperLib::JoinType joinType, const double miterLimit) { // read input - ClipperLib::Paths input; - Slic3rMultiPoints_to_ClipperPaths(polygons, &input); + ClipperLib::Paths input = Slic3rMultiPoints_to_ClipperPaths(polygons); // scale input scaleClipperPolygons(input, scale); @@ -261,62 +232,44 @@ offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float // perform second offset co.Clear(); co.AddPaths(output1, joinType, ClipperLib::etClosedPolygon); - co.Execute(*retval, (delta2*scale)); + ClipperLib::Paths retval; + co.Execute(retval, (delta2*scale)); // unscale output - scaleClipperPolygons(*retval, 1/scale); + scaleClipperPolygons(retval, 1/scale); + return retval; } -void -offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1, - const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) +Polygons +offset2(const Polygons &polygons, const float delta1, const float delta2, + const double scale, const ClipperLib::JoinType joinType, const double miterLimit) { // perform offset - ClipperLib::Paths output; - offset2(polygons, &output, delta1, delta2, scale, joinType, miterLimit); + ClipperLib::Paths output = _offset2(polygons, delta1, delta2, scale, joinType, miterLimit); // convert into ExPolygons - ClipperPaths_to_Slic3rMultiPoints(output, retval); + return ClipperPaths_to_Slic3rMultiPoints(output); } -Slic3r::Polygons -offset2(const Slic3r::Polygons &polygons, const float delta1, - const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) -{ - Slic3r::Polygons pp; - offset2(polygons, &pp, delta1, delta2, scale, joinType, miterLimit); - return pp; -} - -void -offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1, - const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) +ExPolygons +offset2_ex(const Polygons &polygons, const float delta1, const float delta2, + const double scale, const ClipperLib::JoinType joinType, const double miterLimit) { // perform offset - ClipperLib::Paths output; - offset2(polygons, &output, delta1, delta2, scale, joinType, miterLimit); + ClipperLib::Paths output = _offset2(polygons, delta1, delta2, scale, joinType, miterLimit); // convert into ExPolygons - ClipperPaths_to_Slic3rExPolygons(output, retval); -} - -Slic3r::ExPolygons -offset2_ex(const Slic3r::Polygons &polygons, const float delta1, - const float delta2, const double scale, const ClipperLib::JoinType joinType, const double miterLimit) -{ - Slic3r::ExPolygons expp; - offset2(polygons, &expp, delta1, delta2, scale, joinType, miterLimit); - return expp; + return ClipperPaths_to_Slic3rExPolygons(output); } template -void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, T* retval, const ClipperLib::PolyFillType fillType, const bool safety_offset_) +T +_clipper_do(const ClipperLib::ClipType clipType, const Polygons &subject, + const Polygons &clip, const ClipperLib::PolyFillType fillType, const bool safety_offset_) { // read input - ClipperLib::Paths input_subject, input_clip; - Slic3rMultiPoints_to_ClipperPaths(subject, &input_subject); - Slic3rMultiPoints_to_ClipperPaths(clip, &input_clip); + ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject); + ClipperLib::Paths input_clip = Slic3rMultiPoints_to_ClipperPaths(clip); // perform safety offset if (safety_offset_) { @@ -333,20 +286,22 @@ void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polygons &su // add polygons clipper.AddPaths(input_subject, ClipperLib::ptSubject, true); - clipper.AddPaths(input_clip, ClipperLib::ptClip, true); + clipper.AddPaths(input_clip, ClipperLib::ptClip, true); // perform operation - clipper.Execute(clipType, *retval, fillType, fillType); + T retval; + clipper.Execute(clipType, retval, fillType, fillType); + return retval; } -void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, - const Slic3r::Polygons &clip, ClipperLib::PolyTree* retval, const ClipperLib::PolyFillType fillType, +ClipperLib::PolyTree +_clipper_do(const ClipperLib::ClipType clipType, const Polylines &subject, + const Polygons &clip, const ClipperLib::PolyFillType fillType, const bool safety_offset_) { // read input - ClipperLib::Paths input_subject, input_clip; - Slic3rMultiPoints_to_ClipperPaths(subject, &input_subject); - Slic3rMultiPoints_to_ClipperPaths(clip, &input_clip); + ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject); + ClipperLib::Paths input_clip = Slic3rMultiPoints_to_ClipperPaths(clip); // perform safety offset if (safety_offset_) safety_offset(&input_clip); @@ -360,285 +315,133 @@ void _clipper_do(const ClipperLib::ClipType clipType, const Slic3r::Polylines &s clipper.AddPaths(input_clip, ClipperLib::ptClip, true); // perform operation - clipper.Execute(clipType, *retval, fillType, fillType); + ClipperLib::PolyTree retval; + clipper.Execute(clipType, retval, fillType, fillType); + return retval; } -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_) +Polygons +_clipper(ClipperLib::ClipType clipType, const Polygons &subject, + const Polygons &clip, bool safety_offset_) { // perform operation - ClipperLib::Paths output; - _clipper_do(clipType, subject, clip, &output, ClipperLib::pftNonZero, safety_offset_); + ClipperLib::Paths output = _clipper_do(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_); // convert into Polygons - ClipperPaths_to_Slic3rMultiPoints(output, retval); + return ClipperPaths_to_Slic3rMultiPoints(output); } -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_) +ExPolygons +_clipper_ex(ClipperLib::ClipType clipType, const Polygons &subject, + const Polygons &clip, bool safety_offset_) { // perform operation - ClipperLib::PolyTree polytree; - _clipper_do(clipType, subject, clip, &polytree, ClipperLib::pftNonZero, safety_offset_); + ClipperLib::PolyTree polytree = _clipper_do(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_); // convert into ExPolygons - PolyTreeToExPolygons(polytree, retval); + return PolyTreeToExPolygons(polytree); } -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, - const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_) +Polylines +_clipper_pl(ClipperLib::ClipType clipType, const Polylines &subject, + const Polygons &clip, bool safety_offset_) { // perform operation - ClipperLib::PolyTree polytree; - _clipper_do(clipType, subject, clip, &polytree, ClipperLib::pftNonZero, safety_offset_); + ClipperLib::PolyTree polytree = _clipper_do(clipType, subject, clip, ClipperLib::pftNonZero, safety_offset_); // convert into Polylines ClipperLib::Paths output; ClipperLib::PolyTreeToPaths(polytree, output); - ClipperPaths_to_Slic3rMultiPoints(output, retval); + return ClipperPaths_to_Slic3rMultiPoints(output); } -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, - const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_) -{ - // convert Lines to Polylines - Slic3r::Polylines polylines; - polylines.reserve(subject.size()); - for (Slic3r::Lines::const_iterator line = subject.begin(); line != subject.end(); ++line) - polylines.push_back(*line); - - // perform operation - _clipper(clipType, polylines, clip, &polylines, safety_offset_); - - // convert Polylines to Lines - for (Slic3r::Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) - retval->push_back(*polyline); -} - -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_) +Polylines +_clipper_pl(ClipperLib::ClipType clipType, const Polygons &subject, + const Polygons &clip, bool safety_offset_) { // transform input polygons into polylines - Slic3r::Polylines polylines; + Polylines polylines; polylines.reserve(subject.size()); - for (Slic3r::Polygons::const_iterator polygon = subject.begin(); polygon != subject.end(); ++polygon) + for (Polygons::const_iterator polygon = subject.begin(); polygon != subject.end(); ++polygon) polylines.push_back(*polygon); // implicit call to split_at_first_point() // perform clipping - _clipper(clipType, polylines, clip, retval, safety_offset_); + Polylines retval = _clipper_pl(clipType, polylines, clip, safety_offset_); /* If the split_at_first_point() call above happens to split the polygon inside the clipping area we would get two consecutive polylines instead of a single one, so we go through them in order to recombine continuous polylines. */ - for (size_t i = 0; i < retval->size(); ++i) { - for (size_t j = i+1; j < retval->size(); ++j) { - if ((*retval)[i].points.back().coincides_with((*retval)[j].points.front())) { + for (size_t i = 0; i < retval.size(); ++i) { + for (size_t j = i+1; j < retval.size(); ++j) { + if (retval[i].points.back().coincides_with(retval[j].points.front())) { /* If last point of i coincides with first point of j, append points of j to i and delete j */ - (*retval)[i].points.insert((*retval)[i].points.end(), (*retval)[j].points.begin()+1, (*retval)[j].points.end()); - retval->erase(retval->begin() + j); + retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end()); + retval.erase(retval.begin() + j); --j; - } else if ((*retval)[i].points.front().coincides_with((*retval)[j].points.back())) { + } else if (retval[i].points.front().coincides_with(retval[j].points.back())) { /* If first point of i coincides with last point of j, prepend points of j to i and delete j */ - (*retval)[i].points.insert((*retval)[i].points.begin(), (*retval)[j].points.begin(), (*retval)[j].points.end()-1); - retval->erase(retval->begin() + j); + retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1); + retval.erase(retval.begin() + j); --j; - } else if ((*retval)[i].points.front().coincides_with((*retval)[j].points.front())) { + } else if (retval[i].points.front().coincides_with(retval[j].points.front())) { /* Since Clipper does not preserve orientation of polylines, also check the case when first point of i coincides with first point of j. */ - (*retval)[j].reverse(); - (*retval)[i].points.insert((*retval)[i].points.begin(), (*retval)[j].points.begin(), (*retval)[j].points.end()-1); - retval->erase(retval->begin() + j); + retval[j].reverse(); + retval[i].points.insert(retval[i].points.begin(), retval[j].points.begin(), retval[j].points.end()-1); + retval.erase(retval.begin() + j); --j; - } else if ((*retval)[i].points.back().coincides_with((*retval)[j].points.back())) { + } else if (retval[i].points.back().coincides_with(retval[j].points.back())) { /* Since Clipper does not preserve orientation of polylines, also check the case when last point of i coincides with last point of j. */ - (*retval)[j].reverse(); - (*retval)[i].points.insert((*retval)[i].points.end(), (*retval)[j].points.begin()+1, (*retval)[j].points.end()); - retval->erase(retval->begin() + j); + retval[j].reverse(); + retval[i].points.insert(retval[i].points.end(), retval[j].points.begin()+1, retval[j].points.end()); + retval.erase(retval.begin() + j); --j; } } - } -} - -template -void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_) -{ - _clipper(ClipperLib::ctDifference, subject, clip, retval, safety_offset_); -} -template void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_); -template void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_); -template void diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_); -template void diff(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_); -template void diff(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_); - -template -void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_) -{ - Slic3r::Polygons pp; - for (Slic3r::ExPolygons::const_iterator ex = clip.begin(); ex != clip.end(); ++ex) { - Slic3r::Polygons ppp = *ex; - pp.insert(pp.end(), ppp.begin(), ppp.end()); } - diff(subject, pp, retval, safety_offset_); -} -template void diff(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_); - -template -void diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_) -{ - Slic3r::Polygons pp; - for (Slic3r::ExPolygons::const_iterator ex = subject.begin(); ex != subject.end(); ++ex) { - Slic3r::Polygons ppp = *ex; - pp.insert(pp.end(), ppp.begin(), ppp.end()); - } - diff(pp, clip, retval, safety_offset_); -} - -Slic3r::Polygons -diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_) -{ - Slic3r::Polygons pp; - diff(subject, clip, &pp, safety_offset_); - return pp; -} - -template -Slic3r::ExPolygons -diff_ex(const SubjectType &subject, const ClipType &clip, bool safety_offset_) -{ - Slic3r::ExPolygons expp; - diff(subject, clip, &expp, safety_offset_); - return expp; -} -template Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_); -template Slic3r::ExPolygons diff_ex(const Slic3r::Polygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_); -template Slic3r::ExPolygons diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_); - -template -void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_) -{ - _clipper(ClipperLib::ctIntersection, subject, clip, retval, safety_offset_); -} -template void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_); -template void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_); -template void intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_); -template void intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, Slic3r::Polylines* retval, bool safety_offset_); -template void intersection(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, Slic3r::Lines* retval, bool safety_offset_); - -template -SubjectType intersection(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_) -{ - SubjectType pp; - intersection(subject, clip, &pp, safety_offset_); - return pp; -} - -template Slic3r::Polygons intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_); -template Slic3r::Polylines intersection(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_); -template Slic3r::Lines intersection(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_); - -Slic3r::ExPolygons -intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_) -{ - Slic3r::ExPolygons expp; - intersection(subject, clip, &expp, safety_offset_); - return expp; -} - -template -bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_) -{ - SubjectType retval; - intersection(subject, clip, &retval, safety_offset_); - return !retval.empty(); -} -template bool intersects(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_); -template bool intersects(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_); -template bool intersects(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_); - -void xor_(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, - bool safety_offset_) -{ - _clipper(ClipperLib::ctXor, subject, clip, retval, safety_offset_); -} - -template -void union_(const Slic3r::Polygons &subject, T* retval, bool safety_offset_) -{ - Slic3r::Polygons p; - _clipper(ClipperLib::ctUnion, subject, p, retval, safety_offset_); -} -template void union_(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool safety_offset_); -template void union_(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool safety_offset_); - -Slic3r::Polygons -union_(const Slic3r::Polygons &subject, bool safety_offset) -{ - Polygons pp; - union_(subject, &pp, safety_offset); - return pp; -} - -Slic3r::ExPolygons -union_ex(const Slic3r::Polygons &subject, bool safety_offset) -{ - ExPolygons expp; - union_(subject, &expp, safety_offset); - return expp; -} - -Slic3r::ExPolygons -union_ex(const Slic3r::Surfaces &subject, bool safety_offset) -{ - Polygons pp; - for (Slic3r::Surfaces::const_iterator s = subject.begin(); s != subject.end(); ++s) { - Polygons spp = *s; - pp.insert(pp.end(), spp.begin(), spp.end()); - } - return union_ex(pp, safety_offset); -} - -void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons* retval, bool safety_offset) -{ - Polygons pp = subject1; - pp.insert(pp.end(), subject2.begin(), subject2.end()); - union_(pp, retval, safety_offset); -} - -Slic3r::Polygons -union_(const Slic3r::ExPolygons &subject1, const Slic3r::ExPolygons &subject2, bool safety_offset) -{ - Polygons pp; - for (Slic3r::ExPolygons::const_iterator it = subject1.begin(); it != subject1.end(); ++it) { - Polygons spp = *it; - pp.insert(pp.end(), spp.begin(), spp.end()); - } - for (Slic3r::ExPolygons::const_iterator it = subject2.begin(); it != subject2.end(); ++it) { - Polygons spp = *it; - pp.insert(pp.end(), spp.begin(), spp.end()); - } - Polygons retval; - union_(pp, &retval, safety_offset); return retval; } -void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree* retval, bool safety_offset_) +Lines +_clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Polygons &clip, + bool safety_offset_) { - Slic3r::Polygons clip; - _clipper_do(ClipperLib::ctUnion, subject, clip, retval, ClipperLib::pftEvenOdd, safety_offset_); + // convert Lines to Polylines + Polylines polylines; + polylines.reserve(subject.size()); + for (Lines::const_iterator line = subject.begin(); line != subject.end(); ++line) + polylines.push_back(*line); + + // perform operation + polylines = _clipper_pl(clipType, polylines, clip, safety_offset_); + + // convert Polylines to Lines + Lines retval; + for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) + retval.push_back(*polyline); + return retval; } -void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool safety_offset_) +ClipperLib::PolyTree +union_pt(const Polygons &subject, bool safety_offset_) { - ClipperLib::PolyTree pt; - union_pt(subject, &pt, safety_offset_); - traverse_pt(pt.Childs, retval); + return _clipper_do(ClipperLib::ctUnion, subject, Polygons(), ClipperLib::pftEvenOdd, safety_offset_); } -static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval) +Polygons +union_pt_chained(const Polygons &subject, bool safety_offset_) +{ + ClipperLib::PolyTree polytree = union_pt(subject, safety_offset_); + + Polygons retval; + traverse_pt(polytree.Childs, &retval); + return retval; +} + +static void traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval) { /* use a nearest neighbor search to order these children TODO: supply start_near to chained_path() too? */ @@ -660,19 +463,19 @@ static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval) // traverse the next depth traverse_pt((*it)->Childs, retval); - Polygon p; - ClipperPath_to_Slic3rMultiPoint((*it)->Contour, &p); + Polygon p = ClipperPath_to_Slic3rMultiPoint((*it)->Contour); retval->push_back(p); if ((*it)->IsHole()) retval->back().reverse(); // ccw } } -void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool preserve_collinear) +Polygons +simplify_polygons(const Polygons &subject, bool preserve_collinear) { // convert into Clipper polygons - ClipperLib::Paths input_subject, output; - Slic3rMultiPoints_to_ClipperPaths(subject, &input_subject); + ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject); + ClipperLib::Paths output; if (preserve_collinear) { ClipperLib::Clipper c; c.PreserveCollinear(true); @@ -684,21 +487,18 @@ void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons* retval } // convert into Slic3r polygons - ClipperPaths_to_Slic3rMultiPoints(output, retval); + return ClipperPaths_to_Slic3rMultiPoints(output); } -void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool preserve_collinear) +ExPolygons +simplify_polygons_ex(const Polygons &subject, bool preserve_collinear) { if (!preserve_collinear) { - Polygons polygons; - simplify_polygons(subject, &polygons, preserve_collinear); - union_(polygons, retval); - return; + return union_ex(simplify_polygons(subject, preserve_collinear)); } // convert into Clipper polygons - ClipperLib::Paths input_subject; - Slic3rMultiPoints_to_ClipperPaths(subject, &input_subject); + ClipperLib::Paths input_subject = Slic3rMultiPoints_to_ClipperPaths(subject); ClipperLib::PolyTree polytree; @@ -709,7 +509,7 @@ void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retv c.Execute(ClipperLib::ctUnion, polytree, ClipperLib::pftNonZero, ClipperLib::pftNonZero); // convert into ExPolygons - PolyTreeToExPolygons(polytree, retval); + return PolyTreeToExPolygons(polytree); } void safety_offset(ClipperLib::Paths* paths) diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index 08697652c..ae1c969fe 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -22,22 +22,19 @@ void AddOuterPolyNodeToExPolygons(ClipperLib::PolyNode& polynode, Slic3r::ExPoly void PolyTreeToExPolygons(ClipperLib::PolyTree& polytree, Slic3r::ExPolygons& expolygons); //----------------------------------------------------------- -void Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input, ClipperLib::Path* output); +ClipperLib::Path Slic3rMultiPoint_to_ClipperPath(const Slic3r::MultiPoint &input); template -void Slic3rMultiPoints_to_ClipperPaths(const T &input, ClipperLib::Paths* output); +ClipperLib::Paths Slic3rMultiPoints_to_ClipperPaths(const T &input); template -void ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input, T* output); +T ClipperPath_to_Slic3rMultiPoint(const ClipperLib::Path &input); template -void ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input, T* output); -void ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, Slic3r::ExPolygons* output); +T ClipperPaths_to_Slic3rMultiPoints(const ClipperLib::Paths &input); +Slic3r::ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input); void scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale); // offset Polygons -void offset(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta, - double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); -void offset(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta, +ClipperLib::Paths _offset(const Slic3r::Polygons &polygons, const float delta, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, @@ -45,19 +42,16 @@ Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, double miterLimit = 3); // offset Polylines -void offset(const Slic3r::Polylines &polylines, ClipperLib::Paths* retval, const float delta, +ClipperLib::Paths _offset(const Slic3r::Polylines &polylines, const float delta, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3); -void offset(const Slic3r::Polylines &polylines, Slic3r::Polygons* retval, const float delta, +Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3); -void offset(const Slic3r::Surface &surface, Slic3r::Surfaces* retval, const float delta, +Slic3r::Surfaces offset(const Slic3r::Surface &surface, const float delta, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3); -void offset(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta, - double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); @@ -65,78 +59,159 @@ Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float d double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); -void offset2(const Slic3r::Polygons &polygons, ClipperLib::Paths* retval, const float delta1, - const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); -void offset2(const Slic3r::Polygons &polygons, Slic3r::Polygons* retval, const float delta1, +ClipperLib::Paths _offset2(const Slic3r::Polygons &polygons, const float delta1, const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1, const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); -void offset2(const Slic3r::Polygons &polygons, Slic3r::ExPolygons* retval, const float delta1, - const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, - double miterLimit = 3); Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1, const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); template -void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, T* retval, bool safety_offset_); -void _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, - const Slic3r::Polygons &clip, ClipperLib::Paths* retval, bool safety_offset_); -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, Slic3r::Polygons* retval, bool safety_offset_); -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, - const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, bool safety_offset_); -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, - const Slic3r::Polygons &clip, Slic3r::Polylines* retval); -void _clipper(ClipperLib::ClipType clipType, const Slic3r::Lines &subject, - const Slic3r::Polygons &clip, Slic3r::Lines* retval); +T _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polygons &subject, + const Slic3r::Polygons &clip, const ClipperLib::PolyFillType fillType, bool safety_offset_ = false); -template -void diff(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false); +ClipperLib::PolyTree _clipper_do(ClipperLib::ClipType clipType, const Slic3r::Polylines &subject, + const Slic3r::Polygons &clip, const ClipperLib::PolyFillType fillType, bool safety_offset_ = false); -template -void diff(const SubjectType &subject, const Slic3r::ExPolygons &clip, ResultType* retval, bool safety_offset_ = false); +Slic3r::Polygons _clipper(ClipperLib::ClipType clipType, + const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +Slic3r::ExPolygons _clipper_ex(ClipperLib::ClipType clipType, + const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType, + const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +Slic3r::Polylines _clipper_pl(ClipperLib::ClipType clipType, + const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +Slic3r::Lines _clipper_ln(ClipperLib::ClipType clipType, + const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); -Slic3r::Polygons diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +// diff +inline Slic3r::Polygons +diff(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper(ClipperLib::ctDifference, subject, clip, safety_offset_); +} -template -Slic3r::ExPolygons diff_ex(const SubjectType &subject, const ClipType &clip, bool safety_offset_ = false); +inline Slic3r::ExPolygons +diff_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper_ex(ClipperLib::ctDifference, subject, clip, safety_offset_); +} -template -void intersection(const SubjectType &subject, const Slic3r::Polygons &clip, ResultType* retval, bool safety_offset_ = false); +inline Slic3r::ExPolygons +diff_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) +{ + return _clipper_ex(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_); +} -template -SubjectType intersection(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +inline Slic3r::Polygons +diff(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) +{ + return _clipper(ClipperLib::ctDifference, to_polygons(subject), to_polygons(clip), safety_offset_); +} -Slic3r::ExPolygons -intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +inline Slic3r::Polylines +diff_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_); +} -template -bool intersects(const SubjectType &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false); +inline Slic3r::Polylines +diff_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper_pl(ClipperLib::ctDifference, subject, clip, safety_offset_); +} -void xor_(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, Slic3r::ExPolygons* retval, - bool safety_offset_ = false); +inline Slic3r::Lines +diff_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper_ln(ClipperLib::ctDifference, subject, clip, safety_offset_); +} -template -void union_(const Slic3r::Polygons &subject, T* retval, bool safety_offset_ = false); +// intersection +inline Slic3r::Polygons +intersection(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper(ClipperLib::ctIntersection, subject, clip, safety_offset_); +} -Slic3r::Polygons union_(const Slic3r::Polygons &subject, bool safety_offset = false); -Slic3r::ExPolygons union_ex(const Slic3r::Polygons &subject, bool safety_offset = false); -Slic3r::ExPolygons union_ex(const Slic3r::Surfaces &subject, bool safety_offset = false); +inline Slic3r::ExPolygons +intersection_ex(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper_ex(ClipperLib::ctIntersection, subject, clip, safety_offset_); +} -void union_(const Slic3r::Polygons &subject1, const Slic3r::Polygons &subject2, Slic3r::Polygons* retval, bool safety_offset = false); -Slic3r::Polygons union_(const Slic3r::ExPolygons &subject1, const Slic3r::ExPolygons &subject2, bool safety_offset = false); +inline Slic3r::ExPolygons +intersection_ex(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) +{ + return _clipper_ex(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_); +} -void union_pt(const Slic3r::Polygons &subject, ClipperLib::PolyTree* retval, bool safety_offset_ = false); -void union_pt_chained(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool safety_offset_ = false); +inline Slic3r::Polygons +intersection(const Slic3r::ExPolygons &subject, const Slic3r::ExPolygons &clip, bool safety_offset_ = false) +{ + return _clipper(ClipperLib::ctIntersection, to_polygons(subject), to_polygons(clip), safety_offset_); +} + +inline Slic3r::Polylines +intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_); +} + +inline Slic3r::Polylines +intersection_pl(const Slic3r::Polylines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper_pl(ClipperLib::ctIntersection, subject, clip, safety_offset_); +} + +inline Slic3r::Lines +intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip, bool safety_offset_ = false) +{ + return _clipper_ln(ClipperLib::ctIntersection, subject, clip, safety_offset_); +} + +// union +inline Slic3r::Polygons +union_(const Slic3r::Polygons &subject, bool safety_offset_ = false) +{ + return _clipper(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_); +} + +inline Slic3r::Polygons +union_(const Slic3r::Polygons &subject, const Slic3r::Polygons &subject2, bool safety_offset_ = false) +{ + return _clipper(ClipperLib::ctUnion, subject, subject2, safety_offset_); +} + +inline Slic3r::ExPolygons +union_ex(const Slic3r::Polygons &subject, bool safety_offset_ = false) +{ + return _clipper_ex(ClipperLib::ctUnion, subject, Slic3r::Polygons(), safety_offset_); +} + +inline Slic3r::ExPolygons +union_ex(const Slic3r::ExPolygons &subject, bool safety_offset_ = false) +{ + return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_); +} + +inline Slic3r::ExPolygons +union_ex(const Slic3r::Surfaces &subject, bool safety_offset_ = false) +{ + return _clipper_ex(ClipperLib::ctUnion, to_polygons(subject), Slic3r::Polygons(), safety_offset_); +} + + +ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject, bool safety_offset_ = false); +Slic3r::Polygons union_pt_chained(const Slic3r::Polygons &subject, bool safety_offset_ = false); static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval); -void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::Polygons* retval, bool preserve_collinear = false); -void simplify_polygons(const Slic3r::Polygons &subject, Slic3r::ExPolygons* retval, bool preserve_collinear = false); +/* OTHER */ +Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false); +Slic3r::ExPolygons simplify_polygons_ex(const Slic3r::Polygons &subject, bool preserve_collinear = false); void safety_offset(ClipperLib::Paths* paths); diff --git a/xs/src/libslic3r/ExPolygon.cpp b/xs/src/libslic3r/ExPolygon.cpp index 3640d3918..c9bb08048 100644 --- a/xs/src/libslic3r/ExPolygon.cpp +++ b/xs/src/libslic3r/ExPolygon.cpp @@ -90,9 +90,7 @@ ExPolygon::contains(const Line &line) const bool ExPolygon::contains(const Polyline &polyline) const { - Polylines pl_out; - diff((Polylines)polyline, *this, &pl_out); - return pl_out.empty(); + return diff_pl((Polylines)polyline, *this).empty(); } bool @@ -152,8 +150,7 @@ ExPolygon::simplify_p(double tolerance) const p.points.pop_back(); pp.push_back(p); } - simplify_polygons(pp, &pp); - return pp; + return simplify_polygons(pp); } ExPolygons @@ -346,8 +343,7 @@ ExPolygon::get_trapezoids2(Polygons* polygons) const poly[3].y = bb.max.y; // intersect with this expolygon - Polygons trapezoids; - intersection(poly, *this, &trapezoids); + Polygons trapezoids = intersection(poly, *this); // append results to return value polygons->insert(polygons->end(), trapezoids.begin(), trapezoids.end()); @@ -384,10 +380,7 @@ ExPolygon::triangulate_pp(Polygons* polygons) const // convert polygons std::list input; - Polygons pp = *this; - simplify_polygons(pp, &pp, true); - ExPolygons expp; - union_(pp, &expp); + ExPolygons expp = simplify_polygons_ex(*this, true); for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { // contour @@ -440,8 +433,7 @@ ExPolygon::triangulate_pp(Polygons* polygons) const void ExPolygon::triangulate_p2t(Polygons* polygons) const { - ExPolygons expp; - simplify_polygons(*this, &expp, true); + ExPolygons expp = simplify_polygons_ex(*this, true); for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { // TODO: prevent duplicate points @@ -505,4 +497,15 @@ ExPolygon::dump_perl() const return ret.str(); } +Polygons +to_polygons(const ExPolygons &expolygons) +{ + Slic3r::Polygons pp; + for (ExPolygons::const_iterator ex = expolygons.begin(); ex != expolygons.end(); ++ex) { + Slic3r::Polygons ppp = *ex; + pp.insert(pp.end(), ppp.begin(), ppp.end()); + } + return pp; +} + } diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp index 69fe9fb0e..a9c86bf66 100644 --- a/xs/src/libslic3r/ExPolygon.hpp +++ b/xs/src/libslic3r/ExPolygon.hpp @@ -45,6 +45,8 @@ class ExPolygon std::string dump_perl() const; }; +Polygons to_polygons(const ExPolygons &expolygons); + } // start Boost diff --git a/xs/src/libslic3r/ExtrusionEntity.cpp b/xs/src/libslic3r/ExtrusionEntity.cpp index 5d0e5502a..a844cdd77 100644 --- a/xs/src/libslic3r/ExtrusionEntity.cpp +++ b/xs/src/libslic3r/ExtrusionEntity.cpp @@ -36,8 +36,7 @@ void ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const { // perform clipping - Polylines clipped; - intersection(this->polyline, collection, &clipped); + Polylines clipped = intersection_pl(this->polyline, collection); return this->_inflate_collection(clipped, retval); } @@ -45,8 +44,7 @@ void ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const { // perform clipping - Polylines clipped; - diff(this->polyline, collection, &clipped); + Polylines clipped = diff_pl(this->polyline, collection); return this->_inflate_collection(clipped, retval); } @@ -113,9 +111,7 @@ ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCo Polygons ExtrusionPath::grow() const { - Polygons pp; - offset(this->polyline, &pp, +scale_(this->width/2)); - return pp; + return offset(this->polyline, +scale_(this->width/2)); } ExtrusionLoop* diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 41a8671a6..7a935b364 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -283,11 +283,8 @@ GCode::change_layer(const Layer &layer) this->first_layer = (layer.id() == 0); // avoid computing islands and overhangs if they're not needed - if (this->config.avoid_crossing_perimeters) { - ExPolygons islands; - union_(layer.slices, &islands, true); - this->avoid_crossing_perimeters.init_layer_mp(islands); - } + if (this->config.avoid_crossing_perimeters) + this->avoid_crossing_perimeters.init_layer_mp(union_ex(layer.slices, true)); std::string gcode; if (this->layer_count > 0) { diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp index 6d864f631..fcf39f705 100644 --- a/xs/src/libslic3r/Geometry.cpp +++ b/xs/src/libslic3r/Geometry.cpp @@ -149,20 +149,6 @@ deg2rad(double angle) return PI * angle / 180.0; } -void -simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval) -{ - Polygons pp; - for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) { - Polygon p = *it; - p.points.push_back(p.points.front()); - p.points = MultiPoint::_douglas_peucker(p.points, tolerance); - p.points.pop_back(); - pp.push_back(p); - } - Slic3r::simplify_polygons(pp, retval); -} - double linint(double value, double oldmin, double oldmax, double newmin, double newmax) { diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp index 14bc3f0ca..724279f4b 100644 --- a/xs/src/libslic3r/Geometry.hpp +++ b/xs/src/libslic3r/Geometry.hpp @@ -23,7 +23,6 @@ template bool contains(const std::vector &vector, const Point &point double rad2deg(double angle); double rad2deg_dir(double angle); double deg2rad(double angle); -void simplify_polygons(const Polygons &polygons, double tolerance, Polygons* retval); class ArrangeItem { public: diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp index dc931923c..4d12a76f7 100644 --- a/xs/src/libslic3r/Layer.cpp +++ b/xs/src/libslic3r/Layer.cpp @@ -110,7 +110,7 @@ Layer::make_slices() Polygons region_slices_p = (*layerm)->slices; slices_p.insert(slices_p.end(), region_slices_p.begin(), region_slices_p.end()); } - union_(slices_p, &slices); + slices = union_ex(slices_p); } this->slices.expolygons.clear(); @@ -229,8 +229,8 @@ Layer::make_perimeters() if (!fill_surfaces.surfaces.empty()) { for (LayerRegionPtrs::iterator l = layerms.begin(); l != layerms.end(); ++l) { ExPolygons expp = intersection_ex( - fill_surfaces, - (*l)->slices + (Polygons) fill_surfaces, + (Polygons) (*l)->slices ); (*l)->fill_surfaces.surfaces.clear(); diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index f459c5878..64e5a3dec 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -45,9 +45,8 @@ LayerRegion::flow(FlowRole role, bool bridge, double width) const void LayerRegion::merge_slices() { - ExPolygons expp; // without safety offset, artifacts are generated (GH #2494) - union_(this->slices, &expp, true); + ExPolygons expp = union_ex((Polygons)this->slices, true); this->slices.surfaces.clear(); this->slices.surfaces.reserve(expp.size()); diff --git a/xs/src/libslic3r/MotionPlanner.cpp b/xs/src/libslic3r/MotionPlanner.cpp index a1df679cf..6c6d7336d 100644 --- a/xs/src/libslic3r/MotionPlanner.cpp +++ b/xs/src/libslic3r/MotionPlanner.cpp @@ -155,12 +155,12 @@ MotionPlanner::shortest_path(const Point &from, const Point &to) if (!grown_env.contains(from)) { // delete second point while the line connecting first to third crosses the // boundaries as many times as the current first to second - while (polyline.points.size() > 2 && intersection((Lines)Line(from, polyline.points[2]), grown_env).size() == 1) { + while (polyline.points.size() > 2 && intersection_ln(Line(from, polyline.points[2]), grown_env).size() == 1) { polyline.points.erase(polyline.points.begin() + 1); } } if (!grown_env.contains(to)) { - while (polyline.points.size() > 2 && intersection((Lines)Line(*(polyline.points.end() - 3), to), grown_env).size() == 1) { + while (polyline.points.size() > 2 && intersection_ln(Line(*(polyline.points.end() - 3), to), grown_env).size() == 1) { polyline.points.erase(polyline.points.end() - 2); } } @@ -294,7 +294,7 @@ MotionPlannerEnv::nearest_env_point(const Point &from, const Point &to) const size_t result = from.nearest_waypoint_index(pp, to); // as we assume 'from' is outside env, any node will require at least one crossing - if (intersection((Lines)Line(from, pp[result]), this->island).size() > 1) { + if (intersection_ln(Line(from, pp[result]), this->island).size() > 1) { // discard result pp.erase(pp.begin() + result); } else { diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index f9e6a8c6e..842801d4e 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -343,8 +343,7 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops, && !(this->object_config->support_material && this->object_config->support_material_contact_distance.value == 0)) { // get non-overhang paths by intersecting this loop with the grown lower slices { - Polylines polylines; - intersection((Polygons)loop->polygon, this->_lower_slices_p, &polylines); + Polylines polylines = intersection_pl(loop->polygon, this->_lower_slices_p); for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) { ExtrusionPath path(role); @@ -360,8 +359,7 @@ PerimeterGenerator::_traverse_loops(const PerimeterGeneratorLoops &loops, // outside the grown lower slices (thus where the distance between // the loop centerline and original lower slices is >= half nozzle diameter { - Polylines polylines; - diff((Polygons)loop->polygon, this->_lower_slices_p, &polylines); + Polylines polylines = diff_pl(loop->polygon, this->_lower_slices_p); for (Polylines::const_iterator polyline = polylines.begin(); polyline != polylines.end(); ++polyline) { ExtrusionPath path(erOverhangPerimeter); diff --git a/xs/src/libslic3r/Polygon.cpp b/xs/src/libslic3r/Polygon.cpp index 102838809..0fbfda370 100644 --- a/xs/src/libslic3r/Polygon.cpp +++ b/xs/src/libslic3r/Polygon.cpp @@ -86,17 +86,13 @@ Polygon::equally_spaced_points(double distance) const double Polygon::area() const { - ClipperLib::Path p; - Slic3rMultiPoint_to_ClipperPath(*this, &p); - return ClipperLib::Area(p); + return ClipperLib::Area(Slic3rMultiPoint_to_ClipperPath(*this)); } bool Polygon::is_counter_clockwise() const { - ClipperLib::Path p; - Slic3rMultiPoint_to_ClipperPath(*this, &p); - return ClipperLib::Orientation(p); + return ClipperLib::Orientation(Slic3rMultiPoint_to_ClipperPath(*this)); } bool @@ -157,10 +153,7 @@ Polygon::simplify(double tolerance) const Polygon p(MultiPoint::_douglas_peucker(points, tolerance)); p.points.pop_back(); - Polygons pp; - pp.push_back(p); - simplify_polygons(pp, &pp); - return pp; + return simplify_polygons(p); } void diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 8d114f6e2..393677343 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -607,20 +607,16 @@ Print::validate() const object->model_object()->instances.front()->transform_polygon(&convex_hull); // grow convex hull with the clearance margin - { - Polygons grown_hull; - offset(convex_hull, &grown_hull, scale_(this->config.extruder_clearance_radius.value)/2, 1, jtRound, scale_(0.1)); - convex_hull = grown_hull.front(); - } + convex_hull = offset(convex_hull, scale_(this->config.extruder_clearance_radius.value)/2, 1, jtRound, scale_(0.1)).front(); // now we check that no instance of convex_hull intersects any of the previously checked object instances for (Points::const_iterator copy = object->_shifted_copies.begin(); copy != object->_shifted_copies.end(); ++copy) { Polygon p = convex_hull; p.translate(*copy); - if (intersects(a, p)) + if (!intersection(a, p).empty()) throw PrintValidationException("Some objects are too close; your extruder will collide with them."); - union_(a, p, &a); + a = union_(a, p); } } } diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 23d9e74e1..13ba53da1 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -429,7 +429,7 @@ PrintObject::bridge_over_infill() #endif // compute the remaning internal solid surfaces as difference - ExPolygons not_to_bridge = diff_ex(internal_solid, to_bridge, true); + ExPolygons not_to_bridge = diff_ex(internal_solid, to_polygons(to_bridge), true); // build the new collection of fill_surfaces { diff --git a/xs/src/libslic3r/Surface.cpp b/xs/src/libslic3r/Surface.cpp index 4d2234e4d..ba32ecd10 100644 --- a/xs/src/libslic3r/Surface.cpp +++ b/xs/src/libslic3r/Surface.cpp @@ -54,4 +54,15 @@ Surface::is_bridge() const || this->surface_type == stInternalBridge; } +Polygons +to_polygons(const Surfaces &surfaces) +{ + Slic3r::Polygons pp; + for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) { + Slic3r::Polygons ppp = *s; + pp.insert(pp.end(), ppp.begin(), ppp.end()); + } + return pp; +} + } diff --git a/xs/src/libslic3r/Surface.hpp b/xs/src/libslic3r/Surface.hpp index 21395bdc6..d445db208 100644 --- a/xs/src/libslic3r/Surface.hpp +++ b/xs/src/libslic3r/Surface.hpp @@ -34,6 +34,8 @@ class Surface typedef std::vector Surfaces; typedef std::vector SurfacesPtr; +Polygons to_polygons(const Surfaces &surfaces); + } #endif diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 4286d5aa3..437bea44c 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -390,10 +390,7 @@ TriangleMesh::horizontal_projection() const } // the offset factor was tuned using groovemount.stl - offset(pp, &pp, 0.01 / SCALING_FACTOR); - ExPolygons retval; - union_(pp, &retval, true); - return retval; + return union_ex(offset(pp, 0.01 / SCALING_FACTOR), true); } Polygon @@ -848,14 +845,13 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) if (area[*loop_idx] > +EPSILON) { p_slices.push_back(*loop); } else if (area[*loop_idx] < -EPSILON) { - diff(p_slices, *loop, &p_slices); + p_slices = diff(p_slices, *loop); } } // perform a safety offset to merge very close facets (TODO: find test case for this) double safety_offset = scale_(0.0499); - ExPolygons ex_slices; - offset2(p_slices, &ex_slices, +safety_offset, -safety_offset); + ExPolygons ex_slices = offset2_ex(p_slices, +safety_offset, -safety_offset); #ifdef SLIC3R_DEBUG size_t holes_count = 0; diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index d8621ac91..5bba8fcb6 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -531,8 +531,7 @@ SV* polynode2perl(const ClipperLib::PolyNode& node) { HV* hv = newHV(); - Slic3r::Polygon p; - ClipperPath_to_Slic3rMultiPoint(node.Contour, &p); + Polygon p = ClipperPath_to_Slic3rMultiPoint(node.Contour); if (node.IsHole()) { (void)hv_stores( hv, "hole", Slic3r::perl_to_SV_clone_ref(p) ); } else { diff --git a/xs/xsp/Clipper.xsp b/xs/xsp/Clipper.xsp index 7a33ea0c4..7bbbdb8d6 100644 --- a/xs/xsp/Clipper.xsp +++ b/xs/xsp/Clipper.xsp @@ -8,6 +8,40 @@ %package{Slic3r::Geometry::Clipper}; +Polygons offset(Polygons polygons, float delta, double scale = CLIPPER_OFFSET_SCALE, + ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); + +ExPolygons offset_ex(Polygons polygons, float delta, double scale = CLIPPER_OFFSET_SCALE, + ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); + +Polygons offset2(Polygons polygons, float delta1, float delta2, double scale = CLIPPER_OFFSET_SCALE, + ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); + +ExPolygons offset2_ex(Polygons polygons, float delta1, float delta2, double scale = CLIPPER_OFFSET_SCALE, + ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); + +Polygons diff(Polygons subject, Polygons clip, bool safety_offset = false); + +ExPolygons diff_ex(Polygons subject, Polygons clip, bool safety_offset = false); + +Polylines diff_pl(Polylines subject, Polygons clip); + +Polygons intersection(Polygons subject, Polygons clip, bool safety_offset = false); + +ExPolygons intersection_ex(Polygons subject, Polygons clip, bool safety_offset = false); + +Polylines intersection_pl(Polylines subject, Polygons clip); + +%name{intersection_ppl} Polylines intersection_pl(Polygons subject, Polygons clip); + +%name{union} Polygons union_(Polygons subject, bool safety_offset = false); + +ExPolygons union_ex(Polygons subject, bool safety_offset = false); + +Polygons union_pt_chained(Polygons subject, bool safety_offset = false); + +Polygons simplify_polygons(Polygons subject); + %{ IV @@ -21,188 +55,15 @@ _constant() RETVAL = ix; OUTPUT: RETVAL -Polygons -offset(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3) - Polygons polygons - const float delta - double scale - ClipperLib::JoinType joinType - double miterLimit - CODE: - offset(polygons, &RETVAL, delta, scale, joinType, miterLimit); - OUTPUT: - RETVAL - -ExPolygons -offset_ex(polygons, delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3) - Polygons polygons - const float delta - double scale - ClipperLib::JoinType joinType - double miterLimit - CODE: - offset(polygons, &RETVAL, delta, scale, joinType, miterLimit); - OUTPUT: - RETVAL - -Polygons -offset2(polygons, delta1, delta2, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3) - Polygons polygons - const float delta1 - const float delta2 - double scale - ClipperLib::JoinType joinType - double miterLimit - CODE: - offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit); - OUTPUT: - RETVAL - -ExPolygons -offset2_ex(polygons, delta1, delta2, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMiter, miterLimit = 3) - Polygons polygons - const float delta1 - const float delta2 - double scale - ClipperLib::JoinType joinType - double miterLimit - CODE: - offset2(polygons, &RETVAL, delta1, delta2, scale, joinType, miterLimit); - OUTPUT: - RETVAL - -Polygons -diff(subject, clip, safety_offset = false) - Polygons subject - Polygons clip - bool safety_offset - CODE: - diff(subject, clip, &RETVAL, safety_offset); - OUTPUT: - RETVAL - -ExPolygons -diff_ex(subject, clip, safety_offset = false) - Polygons subject - Polygons clip - bool safety_offset - CODE: - diff(subject, clip, &RETVAL, safety_offset); - OUTPUT: - RETVAL - -Polylines -diff_pl(subject, clip) - Polylines subject - Polygons clip - CODE: - diff(subject, clip, &RETVAL); - OUTPUT: - RETVAL - -Polylines -diff_ppl(subject, clip) - Polygons subject - Polygons clip - CODE: - diff(subject, clip, &RETVAL); - OUTPUT: - RETVAL - -Polygons -intersection(subject, clip, safety_offset = false) - Polygons subject - Polygons clip - bool safety_offset - CODE: - intersection(subject, clip, &RETVAL, safety_offset); - OUTPUT: - RETVAL - -ExPolygons -intersection_ex(subject, clip, safety_offset = false) - Polygons subject - Polygons clip - bool safety_offset - CODE: - intersection(subject, clip, &RETVAL, safety_offset); - OUTPUT: - RETVAL - -Polylines -intersection_pl(subject, clip) - Polylines subject - Polygons clip - CODE: - intersection(subject, clip, &RETVAL); - OUTPUT: - RETVAL - -Polylines -intersection_ppl(subject, clip) - Polygons subject - Polygons clip - CODE: - intersection(subject, clip, &RETVAL); - OUTPUT: - RETVAL - -ExPolygons -xor_ex(subject, clip, safety_offset = false) - Polygons subject - Polygons clip - bool safety_offset - CODE: - xor_(subject, clip, &RETVAL, safety_offset); - OUTPUT: - RETVAL - -Polygons -union(subject, safety_offset = false) - Polygons subject - bool safety_offset - CODE: - union_(subject, &RETVAL, safety_offset); - OUTPUT: - RETVAL - -ExPolygons -union_ex(subject, safety_offset = false) - Polygons subject - bool safety_offset - CODE: - union_(subject, &RETVAL, safety_offset); - OUTPUT: - RETVAL - SV* union_pt(subject, safety_offset = false) Polygons subject bool safety_offset CODE: // perform operation - ClipperLib::PolyTree polytree; - union_pt(subject, &polytree, safety_offset); - + ClipperLib::PolyTree polytree = union_pt(subject, safety_offset); RETVAL = polynode_children_2_perl(polytree); OUTPUT: RETVAL -Polygons -union_pt_chained(subject, safety_offset = false) - Polygons subject - bool safety_offset - CODE: - union_pt_chained(subject, &RETVAL, safety_offset); - OUTPUT: - RETVAL - -Polygons -simplify_polygons(subject) - Polygons subject - CODE: - simplify_polygons(subject, &RETVAL); - OUTPUT: - RETVAL - %} diff --git a/xs/xsp/Geometry.xsp b/xs/xsp/Geometry.xsp index 718ce5bdf..de911ccb0 100644 --- a/xs/xsp/Geometry.xsp +++ b/xs/xsp/Geometry.xsp @@ -81,15 +81,6 @@ deg2rad(angle) OUTPUT: RETVAL -Polygons -simplify_polygons(polygons, tolerance) - Polygons polygons - double tolerance - CODE: - Slic3r::Geometry::simplify_polygons(polygons, tolerance, &RETVAL); - OUTPUT: - RETVAL - IV _constant() diff --git a/xs/xsp/Polyline.xsp b/xs/xsp/Polyline.xsp index 31bd4045e..41cf4f359 100644 --- a/xs/xsp/Polyline.xsp +++ b/xs/xsp/Polyline.xsp @@ -86,7 +86,7 @@ Polyline::grow(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtSqu ClipperLib::JoinType joinType double miterLimit CODE: - offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit); + RETVAL = offset(*THIS, delta, scale, joinType, miterLimit); OUTPUT: RETVAL diff --git a/xs/xsp/Surface.xsp b/xs/xsp/Surface.xsp index ebacd1795..021a8b7e4 100644 --- a/xs/xsp/Surface.xsp +++ b/xs/xsp/Surface.xsp @@ -89,7 +89,7 @@ Surface::offset(delta, scale = CLIPPER_OFFSET_SCALE, joinType = ClipperLib::jtMi ClipperLib::JoinType joinType double miterLimit CODE: - offset(*THIS, &RETVAL, delta, scale, joinType, miterLimit); + RETVAL = offset(*THIS, delta, scale, joinType, miterLimit); OUTPUT: RETVAL diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 37d7a9620..89975c949 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -198,7 +198,8 @@ %typemap{LayerPtrs*}; %typemap{SupportLayerPtrs*}; - +%typemap{ClipperLib::JoinType}{simple}; +%typemap{ClipperLib::PolyFillType}{simple}; %typemap{Axis}{parsed}{ %cpp_type{Axis}; %precall_code{% From 38291d3c80aa5911140f3f251c2150d8d96bb3d3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 21 Nov 2016 18:37:51 +0100 Subject: [PATCH 036/225] Use the CLIPPER_OFFSET_SCALE constant. #3580 --- xs/src/libslic3r/ClipperUtils.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index ae1c969fe..5410672e4 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -35,38 +35,38 @@ void scaleClipperPolygons(ClipperLib::Paths &polygons, const double scale); // offset Polygons ClipperLib::Paths _offset(const Slic3r::Polygons &polygons, const float delta, - double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); Slic3r::Polygons offset(const Slic3r::Polygons &polygons, const float delta, - double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); // offset Polylines ClipperLib::Paths _offset(const Slic3r::Polylines &polylines, const float delta, - double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtSquare, + double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3); Slic3r::Polygons offset(const Slic3r::Polylines &polylines, const float delta, - double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtSquare, + double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3); Slic3r::Surfaces offset(const Slic3r::Surface &surface, const float delta, - double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtSquare, + double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtSquare, double miterLimit = 3); Slic3r::ExPolygons offset_ex(const Slic3r::Polygons &polygons, const float delta, - double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); Slic3r::ExPolygons offset_ex(const Slic3r::ExPolygons &expolygons, const float delta, - double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); ClipperLib::Paths _offset2(const Slic3r::Polygons &polygons, const float delta1, - const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); Slic3r::Polygons offset2(const Slic3r::Polygons &polygons, const float delta1, - const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); Slic3r::ExPolygons offset2_ex(const Slic3r::Polygons &polygons, const float delta1, - const float delta2, double scale = 100000, ClipperLib::JoinType joinType = ClipperLib::jtMiter, + const float delta2, double scale = CLIPPER_OFFSET_SCALE, ClipperLib::JoinType joinType = ClipperLib::jtMiter, double miterLimit = 3); template From d9a92bc4dcd320833f7c7b49f04b745f3e184bb5 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 20 Nov 2016 22:20:19 -0600 Subject: [PATCH 037/225] Code documentation of options and functions for libslic3r internals, ported from Prusa3d fork (written by @bubnikv) --- xs/src/libslic3r/ClipperUtils.hpp | 5 ++ xs/src/libslic3r/Config.cpp | 5 +- xs/src/libslic3r/Config.hpp | 93 +++++++++++++++++++++++-- xs/src/libslic3r/GCode.hpp | 7 ++ xs/src/libslic3r/Layer.cpp | 4 ++ xs/src/libslic3r/LayerRegion.cpp | 1 + xs/src/libslic3r/Model.hpp | 41 ++++++++++- xs/src/libslic3r/MultiPoint.hpp | 2 +- xs/src/libslic3r/PerimeterGenerator.cpp | 6 -- xs/src/libslic3r/PerimeterGenerator.hpp | 40 ++++++++--- xs/src/libslic3r/Polygon.cpp | 6 ++ xs/src/libslic3r/Polygon.hpp | 4 ++ xs/src/libslic3r/Print.cpp | 3 +- xs/src/libslic3r/Print.hpp | 5 +- xs/src/libslic3r/PrintConfig.hpp | 42 +++++++++-- xs/src/libslic3r/libslic3r.h | 10 +++ 16 files changed, 243 insertions(+), 31 deletions(-) diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index 5410672e4..8ed333753 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -14,6 +14,11 @@ using ClipperLib::jtSquare; namespace Slic3r { +// Factor to convert from coord_t (which is int32) to an int64 type used by the Clipper library. +//FIXME Vojtech: Better to use a power of 2 coefficient and to use bit shifts for scaling. +// How about 2^17=131072? +// By the way, is the scalling needed at all? Cura runs all the computation with a fixed point precision of 1um, while Slic3r scales to 1nm, +// further scaling by 10e5 brings us to #define CLIPPER_OFFSET_SCALE 100000.0 //----------------------------------------------------------- diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index 59b0401c6..3cae922e9 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -140,6 +140,8 @@ ConfigBase::set_deserialize(const t_config_option_key &opt_key, std::string str, return opt->deserialize(str, append); } +// Return an absolute value of a possibly relative config variable. +// For example, return absolute infill extrusion width, either from an absolute value, or relative to the layer height. double ConfigBase::get_abs_value(const t_config_option_key &opt_key) { ConfigOption* opt = this->option(opt_key, false); @@ -157,6 +159,8 @@ ConfigBase::get_abs_value(const t_config_option_key &opt_key) { } } +// Return an absolute value of a possibly relative config variable. +// For example, return absolute infill extrusion width, either from an absolute value, or relative to a provided value. double ConfigBase::get_abs_value(const t_config_option_key &opt_key, double ratio_over) { // get stored option value @@ -392,7 +396,6 @@ StaticConfig::set_defaults() t_config_option_keys keys = this->keys(); for (t_config_option_keys::const_iterator it = keys.begin(); it != keys.end(); ++it) { const ConfigOptionDef* def = this->def->get(*it); - if (def->default_value != NULL) this->option(*it)->set(*def->default_value); } diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index c293b4ef8..08a5a6a4b 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -15,9 +15,11 @@ namespace Slic3r { +// Name of the configuration option. typedef std::string t_config_option_key; typedef std::vector t_config_option_keys; +// A generic value of a configuration option. class ConfigOption { public: virtual ~ConfigOption() {}; @@ -34,6 +36,7 @@ class ConfigOption { friend bool operator!= (const ConfigOption &a, const ConfigOption &b); }; +// Value of a single valued option (bool, int, float, string, point, enum) template class ConfigOptionSingle : public ConfigOption { public: @@ -47,12 +50,14 @@ class ConfigOptionSingle : public ConfigOption { }; }; +// Value of a vector valued option (bools, ints, floats, strings, points) class ConfigOptionVectorBase : public ConfigOption { public: virtual ~ConfigOptionVectorBase() {}; virtual std::vector vserialize() const = 0; }; +// Value of a vector valued option (bools, ints, floats, strings, points), template template class ConfigOptionVector : public ConfigOptionVectorBase { @@ -482,6 +487,7 @@ class ConfigOptionBools : public ConfigOptionVector }; }; +// Map from an enum name to an enum integer value. typedef std::map t_config_enum_values; template @@ -508,11 +514,14 @@ class ConfigOptionEnum : public ConfigOptionSingle return true; }; + // Map from an enum name to an enum integer value. + //FIXME The map is called often, it shall be initialized statically. static t_config_enum_values get_enum_values(); }; -/* We use this one in DynamicConfig objects, otherwise it's better to use - the specialized ConfigOptionEnum containers. */ +// Generic enum configuration value. +// We use this one in DynamicConfig objects when creating a config value object for ConfigOptionType == coEnum. +// In the StaticConfig, it is better to use the specialized ConfigOptionEnum containers. class ConfigOptionEnumGeneric : public ConfigOptionInt { public: @@ -532,51 +541,105 @@ class ConfigOptionEnumGeneric : public ConfigOptionInt }; }; +// Type of a configuration value. enum ConfigOptionType { coNone, + // single float coFloat, + // vector of floats coFloats, + // single int coInt, + // vector of ints coInts, + // single string coString, + // vector of strings coStrings, + // percent value. Currently only used for infill. coPercent, + // a fraction or an absolute value coFloatOrPercent, + // single 2d point. Currently not used. coPoint, - coPoint3, + // vector of 2d points. Currently used for the definition of the print bed and for the extruder offsets. coPoints, + coPoint3, + // single boolean value coBool, + // vector of boolean values coBools, + // a generic enum coEnum, }; +// Definition of a configuration value for the purpose of GUI presentation, editing, value mapping and config file handling. class ConfigOptionDef { public: + // What type? bool, int, string etc. ConfigOptionType type; + // Default value of this option. The default value object is owned by ConfigDef, it is released in its destructor. ConfigOption* default_value; + + // Usually empty. + // Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection, + // "select_open" - to open a selection dialog (currently only a serial port selection). std::string gui_type; + // Usually empty. Otherwise "serialized" or "show_value" + // The flags may be combined. + // "serialized" - vector valued option is entered in a single edit field. Values are separated by a semicolon. + // "show_value" - even if enum_values / enum_labels are set, still display the value, not the enum label. std::string gui_flags; + // Label of the GUI input field. + // In case the GUI input fields are grouped in some views, the label defines a short label of a grouped value, + // while full_label contains a label of a stand-alone field. + // The full label is shown, when adding an override parameter for an object or a modified object. std::string label; std::string full_label; + // Category of a configuration field, from the GUI perspective. + // One of: "Layers and Perimeters", "Infill", "Support material", "Speed", "Extruders", "Advanced", "Extrusion Width" std::string category; + // A tooltip text shown in the GUI. std::string tooltip; + // Text right from the input field, usually a unit of measurement. std::string sidetext; + // Format of this parameter on a command line. std::string cli; + // Set for type == coFloatOrPercent. + // It provides a link to a configuration value, of which this option provides a ratio. + // For example, + // For example external_perimeter_speed may be defined as a fraction of perimeter_speed. t_config_option_key ratio_over; + // True for multiline strings. bool multiline; + // For text input: If true, the GUI text box spans the complete page width. bool full_width; + // Not editable. Currently only used for the display of the number of threads. bool readonly; + // Height of a multiline GUI text box. int height; + // Optional width of an input field. int width; + // limit of a numeric input. + // If not set, the is set to + // By setting min=0, only nonnegative input is allowed. int min; int max; + // Legacy names for this configuration option. + // Used when parsing legacy configuration file. std::vector aliases; + // Sometimes a single value may well define multiple values in a "beginner" mode. + // Currently used for aliasing "solid_layers" to "top_solid_layers", "bottom_solid_layers". std::vector shortcut; + // Definition of values / labels for a combo box. + // Mostly used for enums (when type == coEnum), but may be used for ints resp. floats, if gui_type is set to "i_enum_open" resp. "f_enum_open". std::vector enum_values; std::vector enum_labels; + // For enums (when type == coEnum). Maps enum_values to enums. + // Initialized by ConfigOptionEnum::get_enum_values() t_config_enum_values enum_keys_map; - + ConfigOptionDef() : type(coNone), default_value(NULL), multiline(false), full_width(false), readonly(false), height(-1), width(-1), min(INT_MIN), max(INT_MAX) {}; @@ -587,8 +650,14 @@ class ConfigOptionDef ConfigOptionDef& operator= (ConfigOptionDef other); }; +// Map from a config option name to its definition. +// The definition does not carry an actual value of the config option, only its constant default value. +// t_config_option_key is std::string typedef std::map t_optiondef_map; +// Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling. +// The configuration definition is static: It does not carry the actual configuration values, +// but it carries the defaults of the configuration values. class ConfigDef { public: @@ -598,9 +667,14 @@ class ConfigDef void merge(const ConfigDef &other); }; +// An abstract configuration store. class ConfigBase { public: + // Definition of configuration values for the purpose of GUI presentation, editing, value mapping and config file handling. + // The configuration definition is static: It does not carry the actual configuration values, + // but it carries the defaults of the configuration values. + // ConfigBase does not own ConfigDef, it only references it. const ConfigDef* def; ConfigBase() : def(NULL) {}; @@ -623,6 +697,8 @@ class ConfigBase void save(const std::string &file) const; }; +// Configuration store with dynamic number of configuration values. +// In Slic3r, the dynamic config is mostly used at the user interface layer. class DynamicConfig : public virtual ConfigBase { public: @@ -643,13 +719,20 @@ class DynamicConfig : public virtual ConfigBase t_options_map options; }; +// Configuration store with a static definition of configuration values. +// In Slic3r, the static configuration stores are during the slicing / g-code generation for efficiency reasons, +// because the configuration values could be accessed directly. class StaticConfig : public virtual ConfigBase { public: StaticConfig() : ConfigBase() {}; + // Gets list of config option names for each config option of this->def, which has a static counter-part defined by the derived object + // and which could be resolved by this->optptr(key) call. t_config_option_keys keys() const; - //virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0; + // Set all statically defined config options to their defaults defined by this->def. void set_defaults(); + // The derived class has to implement optptr to resolve a static configuration value. + // virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) = 0; }; class UnknownOptionException : public std::exception {}; diff --git a/xs/src/libslic3r/GCode.hpp b/xs/src/libslic3r/GCode.hpp index ebeb50e78..8698eb1a6 100644 --- a/xs/src/libslic3r/GCode.hpp +++ b/xs/src/libslic3r/GCode.hpp @@ -76,12 +76,19 @@ class GCode { Wipe wipe; AvoidCrossingPerimeters avoid_crossing_perimeters; bool enable_loop_clipping; + // If enabled, the G-code generator will put following comments at the ends + // of the G-code lines: _EXTRUDE_SET_SPEED, _WIPE, _BRIDGE_FAN_START, _BRIDGE_FAN_END + // Those comments are received and consumed (removed from the G-code) by the CoolingBuffer.pm Perl module. bool enable_cooling_markers; size_t layer_count; int layer_index; // just a counter const Layer* layer; std::map _seam_position; bool first_layer; // this flag triggers first layer speeds + // Used by the CoolingBuffer.pm Perl module to calculate time spent per layer change. + // This value is not quite precise. First it only accouts for extrusion moves and travel moves, + // it does not account for wipe, retract / unretract moves. + // second it does not account for the velocity profiles of the printer. float elapsed_time; // seconds double volumetric_speed; diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp index 4d12a76f7..55d3c8811 100644 --- a/xs/src/libslic3r/Layer.cpp +++ b/xs/src/libslic3r/Layer.cpp @@ -162,6 +162,10 @@ Layer::any_bottom_region_slice_contains(const T &item) const } template bool Layer::any_bottom_region_slice_contains(const Polyline &item) const; + +// Here the perimeters are created cummulatively for all layer regions sharing the same parameters influencing the perimeters. +// The perimeter paths and the thin fills (ExtrusionEntityCollection) are assigned to the first compatible layer region. +// The resulting fill surface is split back among the originating regions. void Layer::make_perimeters() { diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index 64e5a3dec..f5d10cd52 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -76,6 +76,7 @@ LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* ); if (this->layer()->lower_layer != NULL) + // Cummulative sum of polygons over all the regions. g.lower_slices = &this->layer()->lower_layer->slices; g.layer_id = this->layer()->id(); diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index aeb784b0b..c0a7d78ea 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -28,10 +28,18 @@ typedef std::vector ModelObjectPtrs; typedef std::vector ModelVolumePtrs; typedef std::vector ModelInstancePtrs; +// The print bed content. +// Description of a triangular model with multiple materials, multiple instances with various affine transformations +// and with multiple modifier meshes. +// A model groups multiple objects, each object having possibly multiple instances, +// all objects may share mutliple materials. class Model { public: + // Materials are owned by a model and referenced by objects through t_model_material_id. + // Single material may be shared by multiple models. ModelMaterialMap materials; + // Objects are owned by a model. Each model may have multiple instances, each instance having its own transformation (shift, scale, rotation). ModelObjectPtrs objects; Model(); @@ -66,34 +74,48 @@ class Model void print_info() const; }; +// Material, which may be shared across multiple ModelObjects of a single Model. class ModelMaterial { friend class Model; public: + // Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose. t_model_material_attributes attributes; + // Dynamic configuration storage for the object specific configuration values, overriding the global configuration. DynamicPrintConfig config; Model* get_model() const { return this->model; }; void apply(const t_model_material_attributes &attributes); private: + // Parent, owning this material. Model* model; ModelMaterial(Model *model); ModelMaterial(Model *model, const ModelMaterial &other); }; +// A printable object, possibly having multiple print volumes (each with its own set of parameters and materials), +// and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials. +// Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed, +// different rotation and different uniform scaling. class ModelObject { friend class Model; public: std::string name; std::string input_file; + // Instances of this ModelObject. Each instance defines a shift on the print bed, rotation around the Z axis and a uniform scaling. + // Instances are owned by this ModelObject. ModelInstancePtrs instances; + // Printable and modifier volumes, each with its material ID and a set of override parameters. + // ModelVolumes are owned by this ModelObject. ModelVolumePtrs volumes; + // Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings. DynamicPrintConfig config; + // Variation of a layer thickness for spans of Z coordinates. t_layer_height_ranges layer_height_ranges; - + /* This vector accumulates the total translation applied to the object by the center_around_origin() method. Callers might want to apply the same translation to new volumes before adding them to this object in order to preserve alignment @@ -142,6 +164,7 @@ class ModelObject void print_info() const; private: + // Parent object, owning this ModelObject. Model* model; ModelObject(Model *model); @@ -151,15 +174,22 @@ class ModelObject ~ModelObject(); }; +// An object STL, or a modifier volume, over which a different set of parameters shall be applied. +// ModelVolume instances are owned by a ModelObject. class ModelVolume { friend class ModelObject; public: std::string name; + // The triangular model. TriangleMesh mesh; + // Configuration parameters specific to an object model geometry or a modifier volume, + // overriding the global Slic3r settings and the ModelObject settings. DynamicPrintConfig config; + // Is it an object to be printed, or a modifier volume? bool modifier; + // A parent object owning this modifier volume. ModelObject* get_object() const { return this->object; }; t_model_material_id material_id() const; void material_id(t_model_material_id material_id); @@ -169,6 +199,7 @@ class ModelVolume ModelMaterial* assign_unique_material(); private: + // Parent object owning this ModelVolume. ModelObject* object; t_model_material_id _material_id; @@ -178,19 +209,25 @@ class ModelVolume void swap(ModelVolume &other); }; +// A single instance of a ModelObject. +// Knows the affine transformation of an object. class ModelInstance { friend class ModelObject; public: - double rotation; // in radians around mesh center point + double rotation; // Rotation around the Z axis, in radians around mesh center point double scaling_factor; Pointf offset; // in unscaled coordinates ModelObject* get_object() const { return this->object; }; + + // To be called on an external mesh void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; + // To be called on an external polygon. It does not translate the polygon, only rotates and scales. void transform_polygon(Polygon* polygon) const; private: + // Parent object, owning this instance. ModelObject* object; ModelInstance(ModelObject *object); diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp index d057b14f1..b83f23368 100644 --- a/xs/src/libslic3r/MultiPoint.hpp +++ b/xs/src/libslic3r/MultiPoint.hpp @@ -42,6 +42,6 @@ class MultiPoint static Points _douglas_peucker(const Points &points, const double tolerance); }; -} +} // namespace Slic3r #endif diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index 842801d4e..db1eb3255 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -535,12 +535,6 @@ PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRo return coll; } -bool -PerimeterGeneratorLoop::is_external() const -{ - return this->depth == 0; -} - bool PerimeterGeneratorLoop::is_internal_contour() const { diff --git a/xs/src/libslic3r/PerimeterGenerator.hpp b/xs/src/libslic3r/PerimeterGenerator.hpp index eb3fa0fcb..0e7fbd3e4 100644 --- a/xs/src/libslic3r/PerimeterGenerator.hpp +++ b/xs/src/libslic3r/PerimeterGenerator.hpp @@ -11,25 +11,33 @@ namespace Slic3r { -class PerimeterGeneratorLoop; -typedef std::vector PerimeterGeneratorLoops; - +// Hierarchy of perimeters. class PerimeterGeneratorLoop { - public: +public: + // Polygon of this contour. Polygon polygon; + // Is it a contour or a hole? + // Contours are CCW oriented, holes are CW oriented. bool is_contour; + // Depth in the hierarchy. External perimeter has depth = 0. An external perimeter could be both a contour and a hole. unsigned short depth; + // Children contour, may be both CCW and CW oriented (outer contours or holes). std::vector children; PerimeterGeneratorLoop(Polygon polygon, unsigned short depth) : polygon(polygon), is_contour(false), depth(depth) {}; - bool is_external() const; + // External perimeter. It may be CCW or CW oriented (outer contour or hole contour). + bool is_external() const { return this->depth == 0; } + // An island, which may have holes, but it does not have another internal island. bool is_internal_contour() const; }; +typedef std::vector PerimeterGeneratorLoops; + class PerimeterGenerator { - public: +public: + // Inputs: const SurfaceCollection* slices; const ExPolygonCollection* lower_slices; double layer_height; @@ -41,14 +49,26 @@ class PerimeterGenerator { PrintRegionConfig* config; PrintObjectConfig* object_config; PrintConfig* print_config; + // Outputs: ExtrusionEntityCollection* loops; ExtrusionEntityCollection* gap_fill; SurfaceCollection* fill_surfaces; - PerimeterGenerator(const SurfaceCollection* slices, double layer_height, Flow flow, - PrintRegionConfig* config, PrintObjectConfig* object_config, - PrintConfig* print_config, ExtrusionEntityCollection* loops, - ExtrusionEntityCollection* gap_fill, SurfaceCollection* fill_surfaces) + PerimeterGenerator( + // Input: + const SurfaceCollection* slices, + double layer_height, + Flow flow, + PrintRegionConfig* config, + PrintObjectConfig* object_config, + PrintConfig* print_config, + // Output: + // Loops with the external thin walls + ExtrusionEntityCollection* loops, + // Gaps without the thin walls + ExtrusionEntityCollection* gap_fill, + // Infills without the gap fills + SurfaceCollection* fill_surfaces) : slices(slices), lower_slices(NULL), layer_height(layer_height), layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow), overhang_flow(flow), solid_infill_flow(flow), diff --git a/xs/src/libslic3r/Polygon.cpp b/xs/src/libslic3r/Polygon.cpp index 0fbfda370..e36512970 100644 --- a/xs/src/libslic3r/Polygon.cpp +++ b/xs/src/libslic3r/Polygon.cpp @@ -59,6 +59,7 @@ Polygon::split_at_vertex(const Point &point) const return Polyline(); } +// Split a closed polygon into an open polyline, with the split point duplicated at both ends. Polyline Polygon::split_at_index(int index) const { @@ -71,6 +72,7 @@ Polygon::split_at_index(int index) const return polyline; } +// Split a closed polygon into an open polyline, with the split point duplicated at both ends. Polyline Polygon::split_at_first_point() const { @@ -127,6 +129,8 @@ Polygon::is_valid() const return this->points.size() >= 3; } +// Does an unoriented polygon contain a point? +// Tested by counting intersections along a horizontal line. bool Polygon::contains(const Point &point) const { @@ -135,6 +139,8 @@ Polygon::contains(const Point &point) const Points::const_iterator i = this->points.begin(); Points::const_iterator j = this->points.end() - 1; for (; i != this->points.end(); j = i++) { + //FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point.y well. + // Does the ray with y == point.y intersect this line segment? if ( ((i->y > point.y) != (j->y > point.y)) && ((double)point.x < (double)(j->x - i->x) * (double)(point.y - i->y) / (double)(j->y - i->y) + (double)i->x) ) result = !result; diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp index ccde4a740..e3ea13f24 100644 --- a/xs/src/libslic3r/Polygon.hpp +++ b/xs/src/libslic3r/Polygon.hpp @@ -25,7 +25,9 @@ class Polygon : public MultiPoint { Point last_point() const; virtual Lines lines() const; Polyline split_at_vertex(const Point &point) const; + // Split a closed polygon into an open polyline, with the split point duplicated at both ends. Polyline split_at_index(int index) const; + // Split a closed polygon into an open polyline, with the split point duplicated at both ends. Polyline split_at_first_point() const; Points equally_spaced_points(double distance) const; double area() const; @@ -34,6 +36,8 @@ class Polygon : public MultiPoint { bool make_counter_clockwise(); bool make_clockwise(); bool is_valid() const; + // Does an unoriented polygon contain a point? + // Tested by counting intersections along a horizontal line. bool contains(const Point &point) const; Polygons simplify(double tolerance) const; void simplify(double tolerance, Polygons &polygons) const; diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 393677343..42827e6e9 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -635,7 +635,7 @@ Print::validate() const if (!object_height.empty() && object_height.back() > scale_(this->config.extruder_clearance_height.value)) throw PrintValidationException("Some objects are too tall and cannot be printed without extruder collisions."); } - } + } // end if (this->config.complete_objects) if (this->config.spiral_vase) { size_t total_copies_count = 0; @@ -833,6 +833,7 @@ Print::auto_assign_extruders(ModelObject* model_object) const size_t extruders = this->config.nozzle_diameter.values.size(); for (ModelVolumePtrs::const_iterator v = model_object->volumes.begin(); v != model_object->volumes.end(); ++v) { if (!(*v)->material_id().empty()) { + //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned. size_t extruder_id = (v - model_object->volumes.begin()) + 1; if (!(*v)->config.has("extruder")) (*v)->config.opt("extruder", true)->value = extruder_id; diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index fcf6d0a79..a2a5ba7b4 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -20,7 +20,7 @@ class Print; class PrintObject; class ModelObject; - +// Print step IDs for keeping track of the print state. enum PrintStep { psSkirt, psBrim, }; @@ -34,6 +34,7 @@ class PrintValidationException : public std::runtime_error { PrintValidationException(const std::string &error) : std::runtime_error(error) {}; }; +// To be instantiated over PrintStep or PrintObjectStep enums. template class PrintState { @@ -120,6 +121,7 @@ class PrintObject size_t layer_count() const; void clear_layers(); Layer* get_layer(int idx); + // print_z: top of the layer; slice_z: center of the layer. Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z); void delete_layer(int idx); @@ -152,6 +154,7 @@ class PrintObject typedef std::vector PrintObjectPtrs; typedef std::vector PrintRegionPtrs; +// The complete print tray with possibly multiple objects. class Print { public: diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 8aaebab35..fc517c37f 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -1,3 +1,20 @@ +// Configuration store of Slic3r. +// +// The configuration store is either static or dynamic. +// DynamicPrintConfig is used mainly at the user interface. while the StaticPrintConfig is used +// during the slicing and the g-code generation. +// +// The classes derived from StaticPrintConfig form a following hierarchy. +// Virtual inheritance is used for some of the parent objects. +// +// FullPrintConfig +// PrintObjectConfig +// PrintRegionConfig +// PrintConfig +// GCodeConfig +// HostConfig +// + #ifndef slic3r_PrintConfig_hpp_ #define slic3r_PrintConfig_hpp_ @@ -69,14 +86,19 @@ template<> inline t_config_enum_values ConfigOptionEnum::get_enum_ return keys_map; } +// Defines each and every confiuration option of Slic3r, including the properties of the GUI dialogs. +// Does not store the actual values, but defines default values. class PrintConfigDef : public ConfigDef { public: PrintConfigDef(); }; +// The one and only global definition of SLic3r configuration options. +// This definition is constant. extern PrintConfigDef print_config_def; +// Slic3r configuration storage with print_config_def assigned. class PrintConfigBase : public virtual ConfigBase { public: @@ -87,6 +109,12 @@ class PrintConfigBase : public virtual ConfigBase double min_object_distance() const; }; +// Slic3r dynamic configuration, used to override the configuration +// per object, per modification volume or per printing material. +// The dynamic configuration is also used to store user modifications of the print global parameters, +// so the modified configuration values may be diffed against the active configuration +// to invalidate the proper slicing resp. g-code generation processing steps. +// This object is mapped to Perl as Slic3r::Config. class DynamicPrintConfig : public PrintConfigBase, public DynamicConfig { public: @@ -94,12 +122,14 @@ class DynamicPrintConfig : public PrintConfigBase, public DynamicConfig void normalize(); }; + class StaticPrintConfig : public PrintConfigBase, public StaticConfig { public: StaticPrintConfig() : PrintConfigBase(), StaticConfig() {}; }; +// This object is mapped to Perl as Slic3r::Config::PrintObject. class PrintObjectConfig : public virtual StaticPrintConfig { public: @@ -131,7 +161,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig if (initialize) this->set_defaults(); } - + virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(dont_support_bridges); OPT_PTR(extrusion_width); @@ -161,6 +191,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig }; }; +// This object is mapped to Perl as Slic3r::Config::PrintRegion. class PrintRegionConfig : public virtual StaticPrintConfig { public: @@ -201,7 +232,7 @@ class PrintRegionConfig : public virtual StaticPrintConfig if (initialize) this->set_defaults(); } - + virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(bottom_solid_layers); OPT_PTR(bridge_flow_ratio); @@ -240,6 +271,7 @@ class PrintRegionConfig : public virtual StaticPrintConfig }; }; +// This object is mapped to Perl as Slic3r::Config::GCode. class GCodeConfig : public virtual StaticPrintConfig { public: @@ -316,6 +348,7 @@ class GCodeConfig : public virtual StaticPrintConfig }; }; +// This object is mapped to Perl as Slic3r::Config::Print. class PrintConfig : public GCodeConfig { public: @@ -374,7 +407,7 @@ class PrintConfig : public GCodeConfig if (initialize) this->set_defaults(); } - + virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(avoid_crossing_perimeters); OPT_PTR(bed_shape); @@ -447,7 +480,7 @@ class HostConfig : public virtual StaticPrintConfig if (initialize) this->set_defaults(); } - + virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(octoprint_host); OPT_PTR(octoprint_apikey); @@ -458,6 +491,7 @@ class HostConfig : public virtual StaticPrintConfig }; }; +// This object is mapped to Perl as Slic3r::Config::Full. class FullPrintConfig : public PrintObjectConfig, public PrintRegionConfig, public PrintConfig, public HostConfig { diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index eb3fb8f03..db38bc215 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -8,12 +8,22 @@ #define SLIC3R_VERSION "1.3.0-dev" +//FIXME This epsilon value is used for many non-related purposes: +// For a threshold of a squared Euclidean distance, +// for a trheshold in a difference of radians, +// for a threshold of a cross product of two non-normalized vectors etc. #define EPSILON 1e-4 +// Scaling factor for a conversion from coord_t to coordf_t: 10e-6 +// This scaling generates a following fixed point representation with for a 32bit integer: +// 0..4294mm with 1nm resolution #define SCALING_FACTOR 0.000001 +// RESOLUTION, SCALED_RESOLUTION: Used as an error threshold for a Douglas-Peucker polyline simplification algorithm. #define RESOLUTION 0.0125 #define SCALED_RESOLUTION (RESOLUTION / SCALING_FACTOR) #define PI 3.141592653589793238 +// When extruding a closed loop, the loop is interrupted and shortened a bit to reduce the seam. #define LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER 0.15 +// Maximum perimeter length for the loop to apply the small perimeter speed. #define SMALL_PERIMETER_LENGTH (6.5 / SCALING_FACTOR) * 2 * PI #define INSET_OVERLAP_TOLERANCE 0.4 #define EXTERNAL_INFILL_MARGIN 3 From 5d769bd9da6112efc208d283ea0320aef591c634 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 13 Sep 2016 09:46:41 +0200 Subject: [PATCH 038/225] Some inlining optimizations. --- xs/src/libslic3r/MultiPoint.cpp | 6 ------ xs/src/libslic3r/MultiPoint.hpp | 3 ++- xs/src/libslic3r/Point.cpp | 6 ------ xs/src/libslic3r/Point.hpp | 2 +- 4 files changed, 3 insertions(+), 14 deletions(-) diff --git a/xs/src/libslic3r/MultiPoint.cpp b/xs/src/libslic3r/MultiPoint.cpp index 6857d6393..62b05a9b9 100644 --- a/xs/src/libslic3r/MultiPoint.cpp +++ b/xs/src/libslic3r/MultiPoint.cpp @@ -61,12 +61,6 @@ MultiPoint::length() const return len; } -bool -MultiPoint::is_valid() const -{ - return this->points.size() >= 2; -} - int MultiPoint::find_point(const Point &point) const { diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp index b83f23368..1f0eb489f 100644 --- a/xs/src/libslic3r/MultiPoint.hpp +++ b/xs/src/libslic3r/MultiPoint.hpp @@ -28,7 +28,8 @@ class MultiPoint virtual Point last_point() const = 0; virtual Lines lines() const = 0; double length() const; - bool is_valid() const; + bool is_valid() const { return this->points.size() >= 2; } + int find_point(const Point &point) const; bool has_boundary_point(const Point &point) const; BoundingBox bounding_box() const; diff --git a/xs/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp index 29ce0b153..07462a351 100644 --- a/xs/src/libslic3r/Point.cpp +++ b/xs/src/libslic3r/Point.cpp @@ -63,12 +63,6 @@ Point::rotate(double angle, const Point ¢er) this->y = (coord_t)round( (double)center.y + cos(angle) * (cur_y - (double)center.y) + sin(angle) * (cur_x - (double)center.x) ); } -bool -Point::coincides_with(const Point &point) const -{ - return this->x == point.x && this->y == point.y; -} - bool Point::coincides_with_epsilon(const Point &point) const { diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp index 6638e0eb3..8fe2ded7e 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/xs/src/libslic3r/Point.hpp @@ -43,7 +43,7 @@ class Point void translate(double x, double y); void translate(const Vector &vector); void rotate(double angle, const Point ¢er); - bool coincides_with(const Point &point) const; + bool coincides_with(const Point &point) const { return this->x == point.x && this->y == point.y; } bool coincides_with_epsilon(const Point &point) const; int nearest_point_index(const Points &points) const; int nearest_point_index(const PointConstPtrs &points) const; From 36d1c575b915e18bdaa80cc3e76f2ac96fa05a21 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 10 Apr 2016 19:06:46 +0200 Subject: [PATCH 039/225] Added optimized methods for point and polyline rotation. Existing methods for rotation were optimized by calculating the sin/cos values once only. Added an operator- for points. --- xs/src/libslic3r/MultiPoint.cpp | 14 ++++++++++++ xs/src/libslic3r/MultiPoint.hpp | 1 + xs/src/libslic3r/Point.cpp | 40 +++++++++++++++++++++++++++++---- xs/src/libslic3r/Point.hpp | 3 +++ 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/xs/src/libslic3r/MultiPoint.cpp b/xs/src/libslic3r/MultiPoint.cpp index 62b05a9b9..701fa4c3a 100644 --- a/xs/src/libslic3r/MultiPoint.cpp +++ b/xs/src/libslic3r/MultiPoint.cpp @@ -30,6 +30,20 @@ MultiPoint::translate(const Point &vector) this->translate(vector.x, vector.y); } +void +MultiPoint::rotate(double angle) +{ + double s = sin(angle); + double c = cos(angle); + for (Points::iterator it = points.begin(); it != points.end(); ++it) { + (*it).rotate(angle); + double cur_x = (double)it->x; + double cur_y = (double)it->y; + it->x = (coord_t)round(c * cur_x - s * cur_y); + it->y = (coord_t)round(c * cur_y + s * cur_x); + } +} + void MultiPoint::rotate(double angle, const Point ¢er) { diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp index 1f0eb489f..9c71f42f4 100644 --- a/xs/src/libslic3r/MultiPoint.hpp +++ b/xs/src/libslic3r/MultiPoint.hpp @@ -22,6 +22,7 @@ class MultiPoint void scale(double factor); void translate(double x, double y); void translate(const Point &vector); + void rotate(double angle); void rotate(double angle, const Point ¢er); void reverse(); Point first_point() const; diff --git a/xs/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp index 07462a351..c70164e19 100644 --- a/xs/src/libslic3r/Point.cpp +++ b/xs/src/libslic3r/Point.cpp @@ -54,13 +54,26 @@ Point::translate(const Vector &vector) this->translate(vector.x, vector.y); } +void +Point::rotate(double angle) +{ + double cur_x = (double)this->x; + double cur_y = (double)this->y; + double s = sin(angle); + double c = cos(angle); + this->x = (coord_t)round(c * cur_x - s * cur_y); + this->y = (coord_t)round(c * cur_y + s * cur_x); +} + void Point::rotate(double angle, const Point ¢er) { double cur_x = (double)this->x; double cur_y = (double)this->y; - this->x = (coord_t)round( (double)center.x + cos(angle) * (cur_x - (double)center.x) - sin(angle) * (cur_y - (double)center.y) ); - this->y = (coord_t)round( (double)center.y + cos(angle) * (cur_y - (double)center.y) + sin(angle) * (cur_x - (double)center.x) ); + double s = sin(angle); + double c = cos(angle); + this->x = (coord_t)round( (double)center.x + c * (cur_x - (double)center.x) - s * (cur_y - (double)center.y) ); + this->y = (coord_t)round( (double)center.y + c * (cur_y - (double)center.y) + s * (cur_x - (double)center.x) ); } bool @@ -295,6 +308,12 @@ operator+(const Point& point1, const Point& point2) return Point(point1.x + point2.x, point1.y + point2.y); } +Point +operator-(const Point& point1, const Point& point2) +{ + return Point(point1.x - point2.x, point1.y - point2.y); +} + Point operator*(double scalar, const Point& point2) { @@ -343,13 +362,26 @@ Pointf::translate(const Vectorf &vector) this->translate(vector.x, vector.y); } +void +Pointf::rotate(double angle) +{ + double cur_x = this->x; + double cur_y = this->y; + double s = sin(angle); + double c = cos(angle); + this->x = c * cur_x - s * cur_y; + this->y = c * cur_y + s * cur_x; +} + void Pointf::rotate(double angle, const Pointf ¢er) { double cur_x = this->x; double cur_y = this->y; - this->x = center.x + cos(angle) * (cur_x - center.x) - sin(angle) * (cur_y - center.y); - this->y = center.y + cos(angle) * (cur_y - center.y) + sin(angle) * (cur_x - center.x); + double s = sin(angle); + double c = cos(angle); + this->x = center.x + c * (cur_x - center.x) - s * (cur_y - center.y); + this->y = center.y + c * (cur_y - center.y) + s * (cur_x - center.x); } Pointf diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp index 8fe2ded7e..9c23252cb 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/xs/src/libslic3r/Point.hpp @@ -42,6 +42,7 @@ class Point void scale(double factor); void translate(double x, double y); void translate(const Vector &vector); + void rotate(double angle); void rotate(double angle, const Point ¢er); bool coincides_with(const Point &point) const { return this->x == point.x && this->y == point.y; } bool coincides_with_epsilon(const Point &point) const; @@ -64,6 +65,7 @@ class Point }; Point operator+(const Point& point1, const Point& point2); +Point operator-(const Point& point1, const Point& point2); Point operator*(double scalar, const Point& point2); class Point3 : public Point @@ -92,6 +94,7 @@ class Pointf void scale(double factor); void translate(double x, double y); void translate(const Vectorf &vector); + void rotate(double angle); void rotate(double angle, const Pointf ¢er); Pointf negative() const; Vectorf vector_to(const Pointf &point) const; From 0a15af54884c37ee480401721d1aec7e804792a1 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 22 Nov 2016 18:07:07 +0100 Subject: [PATCH 040/225] Better support material in SVG export with conic tips --- xs/src/libslic3r/SVGExport.cpp | 100 +++++++++++++++++++++++++-------- xs/src/libslic3r/SVGExport.hpp | 7 +++ 2 files changed, 85 insertions(+), 22 deletions(-) diff --git a/xs/src/libslic3r/SVGExport.cpp b/xs/src/libslic3r/SVGExport.cpp index 3eede97a0..66613ecfe 100644 --- a/xs/src/libslic3r/SVGExport.cpp +++ b/xs/src/libslic3r/SVGExport.cpp @@ -42,41 +42,83 @@ SVGExport::writeSVG(const std::string &outputfile) TriangleMeshSlicer(&this->mesh).slice(slice_z, &layers); // generate support material - std::vector support_material(layers.size()); + std::vector sm_pillars; + ExPolygons overhangs; if (this->config.support_material) { - // generate a grid of points according to the configured spacing, - // covering the entire object bounding box - Points support_material_points; - for (coordf_t x = bb.min.x; x <= bb.max.x; x += this->config.support_material_spacing) { - for (coordf_t y = bb.min.y; y <= bb.max.y; y += this->config.support_material_spacing) { - support_material_points.push_back(Point(scale_(x), scale_(y))); + // flatten and merge all the overhangs + { + Polygons pp; + for (std::vector::const_iterator it = layers.begin()+1; it != layers.end(); ++it) { + Polygons oh = diff(*it, *(it - 1)); + pp.insert(pp.end(), oh.begin(), oh.end()); + } + overhangs = union_ex(pp); + } + + // generate points following the shape of each island + Points pillars_pos; + const float spacing = scale_(this->config.support_material_spacing); + for (ExPolygons::const_iterator it = overhangs.begin(); it != overhangs.end(); ++it) { + for (float inset = -spacing/2; inset += spacing; ) { + // inset according to the configured spacing + Polygons curr = offset(*it, -inset); + if (curr.empty()) break; + + // generate points along the contours + for (Polygons::const_iterator pg = curr.begin(); pg != curr.end(); ++pg) { + Points pp = pg->equally_spaced_points(spacing); + for (Points::const_iterator p = pp.begin(); p != pp.end(); ++p) + pillars_pos.push_back(*p); + } } } - // check overhangs, starting from the upper layer, and detect which points apply - // to each layer - ExPolygons overhangs; - for (int i = layer_z.size()-1; i >= 0; --i) { - overhangs = diff_ex(union_(overhangs, layers[i+1]), layers[i]); - for (Points::const_iterator it = support_material_points.begin(); it != support_material_points.end(); ++it) { - for (ExPolygons::const_iterator e = overhangs.begin(); e != overhangs.end(); ++e) { - if (e->contains(*it)) { - support_material[i].push_back(*it); + // for each pillar, check which layers it applies to + for (Points::const_iterator p = pillars_pos.begin(); p != pillars_pos.end(); ++p) { + SupportPillar pillar(*p); + bool object_hit = false; + + // check layers top-down + for (int i = layer_z.size()-1; i >= 0; --i) { + // check whether point is void in this layer + bool is_void = true; + for (ExPolygons::const_iterator it = layers[i].begin(); it != layers[i].end(); ++it) { + if (it->contains(*p)) { + is_void = false; break; } } + + if (is_void) { + if (pillar.top_layer > 0) { + // we have a pillar, so extend it + pillar.bottom_layer = i + this->config.raft_layers; + } else if (object_hit) { + // we don't have a pillar and we're below the object, so create one + pillar.top_layer = i + this->config.raft_layers; + } + } else { + if (pillar.top_layer > 0) { + // we have a pillar which is not needed anymore, so store it and initialize a new potential pillar + sm_pillars.push_back(pillar); + pillar = SupportPillar(*p); + } + object_hit = true; + } } + if (pillar.top_layer > 0) sm_pillars.push_back(pillar); } } // generate a solid raft if requested // (do this after support material because we take support material shape into account) if (this->config.raft_layers > 0) { - ExPolygons raft = offset_ex(layers.front(), scale_(this->config.raft_offset)); + ExPolygons raft = layers.front(); + raft.insert(raft.end(), overhangs.begin(), overhangs.end()); // take support material into account + raft = offset_ex(raft, scale_(this->config.raft_offset)); for (int i = this->config.raft_layers; i >= 1; --i) { layer_z.insert(layer_z.begin(), first_lh + lh * (i-1)); layers.insert(layers.begin(), raft); - support_material.insert(support_material.begin(), Points()); // no support on this layer, we have raft } // prepend total raft height to all sliced layers @@ -113,11 +155,25 @@ SVGExport::writeSVG(const std::string &outputfile) pd.c_str(), "white", "black", "0", unscale(unscale(it->area())) ); } - for (Points::const_iterator it = support_material[i].begin(); it != support_material[i].end(); ++it) { - fprintf(f,"\t\t\n", - unscale(it->x), unscale(it->y), support_material_radius - ); + + // don't print support material in raft layers + if (i >= this->config.raft_layers) { + // look for support material pillars belonging to this layer + for (std::vector::const_iterator it = sm_pillars.begin(); it != sm_pillars.end(); ++it) { + if (!(it->top_layer >= i && it->bottom_layer <= i)) continue; + + // generate a conic tip + float radius = fminf( + support_material_radius, + (it->top_layer - i + 1) * lh + ); + + fprintf(f,"\t\t\n", + unscale(it->x), unscale(it->y), radius + ); + } } + fprintf(f,"\t\n"); } fprintf(f,"\n"); diff --git a/xs/src/libslic3r/SVGExport.hpp b/xs/src/libslic3r/SVGExport.hpp index 2075f1c6d..ff1f1a143 100644 --- a/xs/src/libslic3r/SVGExport.hpp +++ b/xs/src/libslic3r/SVGExport.hpp @@ -19,6 +19,13 @@ class SVGExport this->mesh.mirror_x(); }; void writeSVG(const std::string &outputfile); + + private: + class SupportPillar : public Point { + public: + size_t top_layer, bottom_layer; + SupportPillar(const Point &p) : Point(p), top_layer(0), bottom_layer(0) {}; + }; }; } From 9ef37b93c5fbbe479d873f89d8a28eb5c415a7d2 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 22 Nov 2016 18:22:29 +0100 Subject: [PATCH 041/225] List @lordofhyphens and @bubnikv among the contributors --- lib/Slic3r/GUI/AboutDialog.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/AboutDialog.pm b/lib/Slic3r/GUI/AboutDialog.pm index 7b9625976..831377a1f 100644 --- a/lib/Slic3r/GUI/AboutDialog.pm +++ b/lib/Slic3r/GUI/AboutDialog.pm @@ -51,7 +51,7 @@ sub new { 'Slic3r is licensed under the ' . 'GNU Affero General Public License, version 3.' . '


' . - 'Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Y. Sapir, Mike Sheldrake and numerous others. ' . + 'Contributions by Henrik Brix Andersen, Nicolas Dandrimont, Mark Hindess, Petr Ledvina, Joseph Lenox, Y. Sapir, Mike Sheldrake, Vojtech Bubnik and numerous others. ' . 'Manual by Gary Hodgson. Inspired by the RepRap community.
' . 'Slic3r logo designed by Corey Daniels, Silk Icon Set designed by Mark James. ' . '' . From 32ba66c48f8552a1f2116d35834bf6d85b91a39c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 22 Nov 2016 18:46:32 +0100 Subject: [PATCH 042/225] Don't grey out the retract_speed option when using firmware retraction because it's still used by auto-speed pressure regulator. #3283 --- lib/Slic3r/GUI/Tab.pm | 7 ++++++- xs/src/libslic3r/PrintConfig.cpp | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index fe4c7e452..91f6e9273 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -1385,7 +1385,12 @@ sub _update { # some options only apply when not using firmware retraction $self->get_field($_, $i)->toggle($retraction && !$config->use_firmware_retraction) - for qw(retract_speed retract_restart_extra wipe); + for qw(retract_restart_extra wipe); + + # retraction speed is also used by auto-speed pressure regulator, even when + # user enabled firmware retraction + $self->get_field('retract_speed', $i)->toggle($retraction); + if ($config->use_firmware_retraction && $config->get_at('wipe', $i)) { my $dialog = Wx::MessageDialog->new($self, "The Wipe option is not available when using the Firmware Retraction mode.\n" diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index b9e68cf88..97c6f1f92 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -882,7 +882,7 @@ PrintConfigDef::PrintConfigDef() def = this->add("retract_speed", coFloats); def->label = "Speed"; def->full_label = "Retraction Speed"; - def->tooltip = "The speed for retractions (it only applies to the extruder motor)."; + def->tooltip = "The speed for retractions (it only applies to the extruder motor). If you use the Firmware Retraction option, please note this value still affects the auto-speed pressure regulator."; def->sidetext = "mm/s"; def->cli = "retract-speed=f@"; { From dcdb5056fe33d408471a209aca77196ce114b13a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 22 Nov 2016 22:26:08 +0100 Subject: [PATCH 043/225] Fixed regression causing slowdown_below_layer_time to be ignored. #3515 #3443 --- lib/Slic3r/GCode/CoolingBuffer.pm | 2 +- t/cooling.t | 27 ++++++++++++++++++++++++++- xs/src/libslic3r/GCode.cpp | 7 ++++++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GCode/CoolingBuffer.pm b/lib/Slic3r/GCode/CoolingBuffer.pm index 19fb8d7a8..bb1292b39 100644 --- a/lib/Slic3r/GCode/CoolingBuffer.pm +++ b/lib/Slic3r/GCode/CoolingBuffer.pm @@ -53,7 +53,7 @@ sub flush { * ($elapsed - $self->config->slowdown_below_layer_time) / ($self->config->fan_below_layer_time - $self->config->slowdown_below_layer_time); #/ } - Slic3r::debugf " fan = %d%%, speed = %d%%\n", $fan_speed, $speed_factor * 100; + Slic3r::debugf " fan = %d%%, speed = %f%%\n", $fan_speed, $speed_factor * 100; if ($speed_factor < 1) { $gcode =~ s/^(?=.*?;_EXTRUDE_SET_SPEED)(?!.*?;_WIPE)(? 11; +plan tests => 12; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; } +use List::Util qw(first); use Slic3r; use Slic3r::Test; @@ -135,4 +136,28 @@ $config->set('disable_fan_first_layers', 0); ok !$bridge_with_no_fan, 'bridge fan is turned on for all bridges'; } +{ + my $config = Slic3r::Config->new_from_defaults; + $config->set('cooling', 1); + $config->set('fan_below_layer_time', 0); + $config->set('slowdown_below_layer_time', 10); + $config->set('min_print_speed', 0); + $config->set('start_gcode', ''); + + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + my @layer_times = (0); # in seconds + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { + my ($self, $cmd, $args, $info) = @_; + + if ($cmd eq 'G1') { + if ($info->{dist_Z}) { + push @layer_times, 0; + } + $layer_times[-1] += abs($info->{dist_XY} || $info->{dist_E} || $info->{dist_Z} || 0) / ($args->{F} // $self->F) * 60; + } + }); + my $all_below = !defined first { $_ > 0 && $_ < $config->slowdown_below_layer_time } @layer_times; + ok $all_below, 'slowdown_below_layer_time is honored'; +} + __END__ diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 220384632..9a71b2a64 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -632,8 +632,13 @@ GCode::travel_to(const Point &point, ExtrusionRole role, std::string comment) for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) gcode += this->writer.travel_to_xy(this->point_to_gcode(line->b), comment); + /* While this makes the estimate more accurate, CoolingBuffer calculates the slowdown + factor on the whole elapsed time but only alters non-travel moves, thus the resulting + time is still shorter than the configured threshold. We could create a new + elapsed_travel_time but we would still need to account for bridges, retractions, wipe etc. if (this->config.cooling) - this->elapsed_time += travel.length() / this->config.get_abs_value("travel_speed"); + this->elapsed_time += unscale(travel.length()) / this->config.get_abs_value("travel_speed"); + */ return gcode; } From 893fc2343f0aa3855078f20561d2f7a977610791 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 22 Nov 2016 22:29:20 +0100 Subject: [PATCH 044/225] Fixed regression in Build.PL after recent merged changes --- xs/Build.PL | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xs/Build.PL b/xs/Build.PL index abcf24868..fc82615cd 100644 --- a/xs/Build.PL +++ b/xs/Build.PL @@ -3,6 +3,7 @@ use strict; use warnings; +use Config; use Devel::CheckLib; use ExtUtils::CppGuess; use Module::Build::WithXSpp; @@ -101,7 +102,7 @@ if (!$ENV{SLIC3R_STATIC} && $have_boost) { } else { # Either static linking, or check_lib could not be used to find the boost libraries. my $lib_prefix = 'libboost_'; - my $lib_ext = ${$cpp_guess}{config}{lib_ext}; + my $lib_ext = $Config{lib_ext}; PATH: foreach my $path (@boost_libs) { # Try to find the boost system library. my @files = glob "$path/${lib_prefix}system*$lib_ext"; From 4ac7f6e8f14689cd5db2a9f2a426c9ab8ee5769d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 23 Nov 2016 15:51:12 +0100 Subject: [PATCH 045/225] Refactoring: rename SVGExport to SLAPrint --- src/CMakeLists.txt | 2 +- src/slic3r.cpp | 12 +-- xs/src/libslic3r/PrintConfig.hpp | 4 +- .../libslic3r/{SVGExport.cpp => SLAPrint.cpp} | 98 +++++++++++-------- xs/src/libslic3r/SLAPrint.hpp | 46 +++++++++ xs/src/libslic3r/SVGExport.hpp | 33 ------- 6 files changed, 110 insertions(+), 85 deletions(-) rename xs/src/libslic3r/{SVGExport.cpp => SLAPrint.cpp} (68%) create mode 100644 xs/src/libslic3r/SLAPrint.hpp delete mode 100644 xs/src/libslic3r/SVGExport.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index efeaa41de..9b704d5b0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -67,10 +67,10 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/PrintConfig.cpp ${LIBDIR}/libslic3r/PrintObject.cpp ${LIBDIR}/libslic3r/PrintRegion.cpp + ${LIBDIR}/libslic3r/SLAPrint.cpp ${LIBDIR}/libslic3r/Surface.cpp ${LIBDIR}/libslic3r/SurfaceCollection.cpp ${LIBDIR}/libslic3r/SVG.cpp - ${LIBDIR}/libslic3r/SVGExport.cpp ${LIBDIR}/libslic3r/TriangleMesh.cpp ) add_library(admesh STATIC diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 2ea600f4b..f96b05862 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -2,7 +2,7 @@ #include "Model.hpp" #include "IO.hpp" #include "TriangleMesh.hpp" -#include "SVGExport.hpp" +#include "SLAPrint.hpp" #include "libslic3r.h" #include #include @@ -97,11 +97,11 @@ main(const int argc, const char **argv) } else if (cli_config.export_svg) { std::string outfile = cli_config.output.value; if (outfile.empty()) outfile = model->objects.front()->input_file + ".svg"; - - SVGExport svg_export(model->mesh()); - svg_export.mesh.repair(); - svg_export.config.apply(print_config, true); - svg_export.writeSVG(outfile); + + SLAPrint print(&*model); + print.config.apply(print_config, true); + print.slice(); + print.write_svg(outfile); printf("SVG file exported to %s\n", outfile.c_str()); } else { std::cerr << "error: only --export-svg and --export-obj are currently supported" << std::endl; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index c672b4669..ef5af261e 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -518,7 +518,7 @@ class FullPrintConfig }; }; -class SVGExportConfig +class SLAPrintConfig : public virtual StaticPrintConfig { public: @@ -530,7 +530,7 @@ class SVGExportConfig ConfigOptionFloatOrPercent support_material_extrusion_width; ConfigOptionFloat support_material_spacing; - SVGExportConfig() : StaticPrintConfig() { + SLAPrintConfig() : StaticPrintConfig() { this->set_defaults(); }; diff --git a/xs/src/libslic3r/SVGExport.cpp b/xs/src/libslic3r/SLAPrint.cpp similarity index 68% rename from xs/src/libslic3r/SVGExport.cpp rename to xs/src/libslic3r/SLAPrint.cpp index 66613ecfe..a0a8c38a1 100644 --- a/xs/src/libslic3r/SVGExport.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -1,4 +1,4 @@ -#include "SVGExport.hpp" +#include "SLAPrint.hpp" #include "ClipperUtils.hpp" #include #include @@ -6,50 +6,61 @@ namespace Slic3r { void -SVGExport::writeSVG(const std::string &outputfile) +SLAPrint::slice() { + TriangleMesh mesh = this->model->mesh(); + mesh.repair(); + mesh.mirror_x(); + // align to origin taking raft into account - BoundingBoxf3 bb = this->mesh.bounding_box(); + BoundingBoxf3 bb = mesh.bounding_box(); if (this->config.raft_layers > 0) { bb.min.x -= this->config.raft_offset.value; bb.min.y -= this->config.raft_offset.value; bb.max.x += this->config.raft_offset.value; bb.max.y += this->config.raft_offset.value; } - this->mesh.translate(-bb.min.x, -bb.min.y, -bb.min.z); // align to origin - bb.translate(-bb.min.x, -bb.min.y, -bb.min.z); // align to origin - const Sizef3 size = bb.size(); + mesh.translate(-bb.min.x, -bb.min.y, -bb.min.z); // align to origin + bb.translate(-bb.min.x, -bb.min.y, -bb.min.z); // align to origin + this->size = bb.size(); // if we are generating a raft, first_layer_height will not affect mesh slicing - const float lh = this->config.layer_height.value; + const float lh = this->config.layer_height.value; const float first_lh = this->config.first_layer_height.value; // generate the list of Z coordinates for mesh slicing // (we slice each layer at half of its thickness) - std::vector slice_z, layer_z; + this->layers.clear(); { const float first_slice_lh = (this->config.raft_layers > 0) ? lh : first_lh; - slice_z.push_back(first_slice_lh/2); - layer_z.push_back(first_slice_lh); + this->layers.push_back(Layer(first_slice_lh/2, first_slice_lh)); } - while (layer_z.back() + lh/2 <= this->mesh.stl.stats.max.z) { - slice_z.push_back(layer_z.back() + lh/2); - layer_z.push_back(layer_z.back() + lh); + while (this->layers.back().print_z + lh/2 <= mesh.stl.stats.max.z) { + this->layers.push_back(Layer(this->layers.back().print_z + lh/2, this->layers.back().print_z + lh)); } - // perform the slicing - std::vector layers; - TriangleMeshSlicer(&this->mesh).slice(slice_z, &layers); + // perform slicing and generate layers + { + std::vector slice_z; + for (size_t i = 0; i < this->layers.size(); ++i) + slice_z.push_back(this->layers[i].slice_z); + + std::vector slices; + TriangleMeshSlicer(&mesh).slice(slice_z, &slices); + + for (size_t i = 0; i < slices.size(); ++i) + this->layers[i].slices = ExPolygonCollection(slices[i]); + } // generate support material - std::vector sm_pillars; + this->sm_pillars.clear(); ExPolygons overhangs; if (this->config.support_material) { // flatten and merge all the overhangs { Polygons pp; - for (std::vector::const_iterator it = layers.begin()+1; it != layers.end(); ++it) { - Polygons oh = diff(*it, *(it - 1)); + for (std::vector::const_iterator it = this->layers.begin()+1; it != this->layers.end(); ++it) { + Polygons oh = diff(it->slices, (it - 1)->slices); pp.insert(pp.end(), oh.begin(), oh.end()); } overhangs = union_ex(pp); @@ -79,17 +90,10 @@ SVGExport::writeSVG(const std::string &outputfile) bool object_hit = false; // check layers top-down - for (int i = layer_z.size()-1; i >= 0; --i) { + for (int i = this->layers.size()-1; i >= 0; --i) { // check whether point is void in this layer - bool is_void = true; - for (ExPolygons::const_iterator it = layers[i].begin(); it != layers[i].end(); ++it) { - if (it->contains(*p)) { - is_void = false; - break; - } - } - - if (is_void) { + if (!this->layers[i].slices.contains(*p)) { + // no slice contains the point, so it's in the void if (pillar.top_layer > 0) { // we have a pillar, so extend it pillar.bottom_layer = i + this->config.raft_layers; @@ -100,32 +104,36 @@ SVGExport::writeSVG(const std::string &outputfile) } else { if (pillar.top_layer > 0) { // we have a pillar which is not needed anymore, so store it and initialize a new potential pillar - sm_pillars.push_back(pillar); + this->sm_pillars.push_back(pillar); pillar = SupportPillar(*p); } object_hit = true; } } - if (pillar.top_layer > 0) sm_pillars.push_back(pillar); + if (pillar.top_layer > 0) this->sm_pillars.push_back(pillar); } } // generate a solid raft if requested // (do this after support material because we take support material shape into account) if (this->config.raft_layers > 0) { - ExPolygons raft = layers.front(); + ExPolygons raft = this->layers.front().slices; raft.insert(raft.end(), overhangs.begin(), overhangs.end()); // take support material into account raft = offset_ex(raft, scale_(this->config.raft_offset)); for (int i = this->config.raft_layers; i >= 1; --i) { - layer_z.insert(layer_z.begin(), first_lh + lh * (i-1)); - layers.insert(layers.begin(), raft); + this->layers.insert(this->layers.begin(), Layer(0, first_lh + lh * (i-1))); + this->layers.front().slices = raft; } // prepend total raft height to all sliced layers - for (int i = this->config.raft_layers; i < layer_z.size(); ++i) - layer_z[i] += first_lh + lh * (this->config.raft_layers-1); + for (int i = this->config.raft_layers; i < this->layers.size(); ++i) + this->layers[i].print_z += first_lh + lh * (this->config.raft_layers-1); } - +} + +void +SLAPrint::write_svg(const std::string &outputfile) const +{ double support_material_radius = this->config.support_material_extrusion_width.get_abs_value(this->config.layer_height)/2; FILE* f = fopen(outputfile.c_str(), "w"); @@ -134,11 +142,15 @@ SVGExport::writeSVG(const std::string &outputfile) "\n" "\n" "\n" - , size.x, size.y, SLIC3R_VERSION); + , this->size.x, this->size.y, SLIC3R_VERSION); - for (size_t i = 0; i < layer_z.size(); ++i) { - fprintf(f, "\t\n", i, layer_z[i]); - for (ExPolygons::const_iterator it = layers[i].begin(); it != layers[i].end(); ++it) { + for (size_t i = 0; i < this->layers.size(); ++i) { + const Layer &layer = this->layers[i]; + fprintf(f, "\t\n", i, + layer.print_z, layer.slice_z); + + const ExPolygons &slices = layer.slices.expolygons; + for (ExPolygons::const_iterator it = slices.begin(); it != slices.end(); ++it) { std::string pd; Polygons pp = *it; for (Polygons::const_iterator mp = pp.begin(); mp != pp.end(); ++mp) { @@ -159,13 +171,13 @@ SVGExport::writeSVG(const std::string &outputfile) // don't print support material in raft layers if (i >= this->config.raft_layers) { // look for support material pillars belonging to this layer - for (std::vector::const_iterator it = sm_pillars.begin(); it != sm_pillars.end(); ++it) { + for (std::vector::const_iterator it = this->sm_pillars.begin(); it != this->sm_pillars.end(); ++it) { if (!(it->top_layer >= i && it->bottom_layer <= i)) continue; // generate a conic tip float radius = fminf( support_material_radius, - (it->top_layer - i + 1) * lh + (it->top_layer - i + 1) * this->config.layer_height.value ); fprintf(f,"\t\t\n", diff --git a/xs/src/libslic3r/SLAPrint.hpp b/xs/src/libslic3r/SLAPrint.hpp new file mode 100644 index 000000000..202eed8c2 --- /dev/null +++ b/xs/src/libslic3r/SLAPrint.hpp @@ -0,0 +1,46 @@ +#ifndef slic3r_SLAPrint_hpp_ +#define slic3r_SLAPrint_hpp_ + +#include "libslic3r.h" +#include "ExPolygon.hpp" +#include "ExPolygonCollection.hpp" +#include "Model.hpp" +#include "Point.hpp" +#include "PrintConfig.hpp" +#include "SVG.hpp" + +namespace Slic3r { + +class SLAPrint +{ + public: + SLAPrintConfig config; + + class Layer { + public: + ExPolygonCollection slices; + float slice_z, print_z; + + Layer(float _slice_z, float _print_z) : slice_z(_slice_z), print_z(_print_z) {}; + }; + std::vector layers; + + class SupportPillar : public Point { + public: + size_t top_layer, bottom_layer; + SupportPillar(const Point &p) : Point(p), top_layer(0), bottom_layer(0) {}; + }; + std::vector sm_pillars; + + SLAPrint(Model* _model) : model(_model) {}; + void slice(); + void write_svg(const std::string &outputfile) const; + + private: + Model* model; + Sizef3 size; +}; + +} + +#endif diff --git a/xs/src/libslic3r/SVGExport.hpp b/xs/src/libslic3r/SVGExport.hpp deleted file mode 100644 index ff1f1a143..000000000 --- a/xs/src/libslic3r/SVGExport.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef slic3r_SVGExport_hpp_ -#define slic3r_SVGExport_hpp_ - -#include "libslic3r.h" -#include "ExPolygon.hpp" -#include "PrintConfig.hpp" -#include "SVG.hpp" -#include "TriangleMesh.hpp" - -namespace Slic3r { - -class SVGExport -{ - public: - SVGExportConfig config; - TriangleMesh mesh; - - SVGExport(const TriangleMesh &mesh) : mesh(mesh) { - this->mesh.mirror_x(); - }; - void writeSVG(const std::string &outputfile); - - private: - class SupportPillar : public Point { - public: - size_t top_layer, bottom_layer; - SupportPillar(const Point &p) : Point(p), top_layer(0), bottom_layer(0) {}; - }; -}; - -} - -#endif From bda4ae0b57e2c3109a794fe9584585dc1f48eba6 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 23 Nov 2016 18:38:33 +0100 Subject: [PATCH 046/225] Use SLAPrint in DLP Projector --- lib/Slic3r/GUI/Projector.pm | 132 +++++++++++++++++----------------- xs/src/libslic3r/SLAPrint.cpp | 43 ++++++----- xs/src/libslic3r/SLAPrint.hpp | 4 +- xs/src/perlglue.cpp | 1 + xs/xsp/SLAPrint.xsp | 61 ++++++++++++++++ xs/xsp/my.map | 4 ++ xs/xsp/typemap.xspt | 3 + 7 files changed, 164 insertions(+), 84 deletions(-) create mode 100644 xs/xsp/SLAPrint.xsp diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index a783ecf55..d7d9e7eb8 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -415,7 +415,7 @@ sub new { my $duration = $self->controller->remaining_print_time; $self->_set_status(sprintf "Printing layer %d/%d (z = %.2f); %d minutes and %d seconds left", - $layer_num, $self->controller->layer_count, + $layer_num, $self->controller->_print->layer_count, $self->controller->current_layer_height, int($duration/60), ($duration - int($duration/60)*60)); # % truncates to integer }, @@ -426,7 +426,7 @@ sub new { }, )); { - my $max = $self->controller->layer_count-1; + my $max = $self->controller->_print->layer_count-1; $self->{layers_spinctrl}->SetRange(0, $max); $self->{layers_slider}->SetRange(0, $max); } @@ -527,7 +527,24 @@ sub BUILD { Slic3r::GUI::disable_screensaver(); - $self->set_print(wxTheApp->{mainframe}->{plater}->{print}); + # init print + { + my $print = Slic3r::SLAPrint->new(wxTheApp->{mainframe}->{plater}->{model}); + $print->apply_config(wxTheApp->{mainframe}->config); + $self->_print($print); + $self->screen->print($print); + + # make sure layers were sliced + { + my $progress_dialog = Wx::ProgressDialog->new('Slicing…', "Processing layers…", 100, undef, 0); + $progress_dialog->Pulse; + $print->slice; + $progress_dialog->Destroy; + } + + $self->_layers([ map $print->slices($_), 0..($print->layer_count-1) ]); + $self->_heights($print->heights); + } # projection timer my $timer_id = &Wx::NewId(); @@ -546,40 +563,6 @@ sub delay { $self->timer->Start($wait * 1000, wxTIMER_ONE_SHOT); } -sub set_print { - my ($self, $print) = @_; - - # make sure layers were sliced - { - my $progress_dialog; - foreach my $object (@{$print->objects}) { - next if $object->step_done(STEP_SLICE); - $progress_dialog //= Wx::ProgressDialog->new('Slicing…', "Processing layers…", 100, undef, 0); - $progress_dialog->Pulse; - $object->slice; - } - $progress_dialog->Destroy if $progress_dialog; - } - - $self->_print($print); - - # sort layers by Z - my %layers = (); - foreach my $layer (map { @{$_->layers}, @{$_->support_layers} } @{$print->objects}) { - my $height = $layer->print_z; - $layers{$height} //= []; - push @{$layers{$height}}, $layer; - } - $self->_layers({ %layers }); - $self->_heights([ sort { $a <=> $b } keys %layers ]); -} - -sub layer_count { - my ($self) = @_; - - return scalar @{$self->_heights}; -} - sub current_layer_height { my ($self) = @_; @@ -613,7 +596,7 @@ sub start_print { # start with black Slic3r::debugf "starting black projection\n"; $self->_layer_num(-1); - $self->screen->project_layers(undef); + $self->screen->project_layer(undef); $self->delay($self->config2->{settle_time}, sub { $self->project_next_layer; }); @@ -630,7 +613,7 @@ sub stop_print { $self->is_printing(0); $self->timer->Stop; $self->_timer_cb(undef); - $self->screen->project_layers(undef); + $self->screen->project_layer(undef); } sub print_completed { @@ -652,19 +635,18 @@ sub print_completed { sub is_projecting { my ($self) = @_; - return defined $self->screen->layers; + return defined $self->screen->layer; } sub project_layer { my ($self, $layer_num) = @_; - if (!defined $layer_num || $layer_num >= $self->layer_count) { - $self->screen->project_layers(undef); + if (!defined $layer_num || $layer_num >= $self->_print->layer_count) { + $self->screen->project_layer(undef); return; } - my @layers = @{ $self->_layers->{ $self->_heights->[$layer_num] } }; - $self->screen->project_layers([ @layers ]); + $self->screen->project_layer($self->_layers->[$layer_num], $layer_num); } sub project_next_layer { @@ -672,7 +654,7 @@ sub project_next_layer { $self->_layer_num($self->_layer_num + 1); Slic3r::debugf "projecting layer %d\n", $self->_layer_num; - if ($self->_layer_num >= $self->layer_count) { + if ($self->_layer_num >= $self->_print->layer_count) { $self->print_completed; return; } @@ -699,7 +681,7 @@ sub project_next_layer { } $self->delay($time, sub { - $self->screen->project_layers(undef); + $self->screen->project_layer(undef); $self->project_next_layer; }); }); @@ -743,7 +725,7 @@ use List::Util qw(min); use Slic3r::Geometry qw(X Y unscale scale); use Slic3r::Geometry::Clipper qw(intersection_pl); -__PACKAGE__->mk_accessors(qw(config config2 scaling_factor bed_origin layers)); +__PACKAGE__->mk_accessors(qw(config config2 scaling_factor bed_origin layer print layer_num)); sub new { my ($class, $parent, $config, $config2) = @_; @@ -803,10 +785,11 @@ sub _resize { $self->Refresh; } -sub project_layers { - my ($self, $layers) = @_; +sub project_layer { + my ($self, $layer, $layer_num) = @_; - $self->layers($layers); + $self->layer($layer); + $self->layer_num($layer_num); $self->Refresh; } @@ -865,31 +848,46 @@ sub _repaint { $dc->SetTextForeground(wxWHITE); $dc->SetFont(Wx::Font->new(20, wxDEFAULT, wxNORMAL, wxNORMAL)); - $dc->DrawText("X", @{$self->unscaled_point_to_pixel([10, -2])}); - $dc->DrawText("Y", @{$self->unscaled_point_to_pixel([-2, 10])}); + + my $p = $self->unscaled_point_to_pixel([10, 0]); + $dc->DrawText("X", $p->[X], $p->[Y]); + $p = $self->unscaled_point_to_pixel([0, 10]); + $dc->DrawText("Y", $p->[X]-20, $p->[Y]-10); } } - return if !defined $self->layers; + return if !defined $self->layer; # get layers at this height # draw layers $dc->SetPen(Wx::Pen->new(wxWHITE, 1, wxSOLID)); - foreach my $layer (@{$self->layers}) { - my @polygons = sort { $a->contains_point($b->first_point) ? -1 : 1 } map @$_, @{ $layer->slices }; - foreach my $copy (@{$layer->object->_shifted_copies}) { - foreach my $polygon (@polygons) { - $polygon = $polygon->clone; - $polygon->translate(@$copy); - - if ($polygon->is_counter_clockwise) { - $dc->SetBrush(Wx::Brush->new(wxWHITE, wxSOLID)); - } else { - $dc->SetBrush(Wx::Brush->new(wxBLACK, wxSOLID)); - } - $dc->DrawPolygon($self->scaled_points_to_pixel($polygon->pp), 0, 0); - } + + my @polygons = sort { $a->contains_point($b->first_point) ? -1 : 1 } map @$_, @{$self->layer}; + foreach my $polygon (@polygons) { + if ($polygon->is_counter_clockwise) { + $dc->SetBrush(Wx::Brush->new(wxWHITE, wxSOLID)); + } else { + $dc->SetBrush(Wx::Brush->new(wxBLACK, wxSOLID)); } + $dc->DrawPolygon($self->scaled_points_to_pixel($polygon->pp), 0, 0); + } + + # draw support material + my $sm_radius = $self->print->config->get_abs_value_over('support_material_extrusion_width', $self->print->config->layer_height)/2; + $dc->SetBrush(Wx::Brush->new(wxWHITE, wxSOLID)); + foreach my $pillar (@{$self->print->sm_pillars}) { + next unless $pillar->{top_layer} >= $self->layer_num + && $pillar->{bottom_layer} <= $self->layer_num; + + my $radius = min( + $sm_radius, + ($pillar->{top_layer} - $self->layer_num + 1) * $self->print->config->layer_height, + ); + + $dc->DrawCircle( + @{$self->scaled_points_to_pixel([$pillar->{point}])->[0]}, + $radius * $self->scaling_factor, + ); } } diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index a0a8c38a1..a50859fbd 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -10,19 +10,17 @@ SLAPrint::slice() { TriangleMesh mesh = this->model->mesh(); mesh.repair(); - mesh.mirror_x(); // align to origin taking raft into account - BoundingBoxf3 bb = mesh.bounding_box(); + this->bb = mesh.bounding_box(); if (this->config.raft_layers > 0) { - bb.min.x -= this->config.raft_offset.value; - bb.min.y -= this->config.raft_offset.value; - bb.max.x += this->config.raft_offset.value; - bb.max.y += this->config.raft_offset.value; + this->bb.min.x -= this->config.raft_offset.value; + this->bb.min.y -= this->config.raft_offset.value; + this->bb.max.x += this->config.raft_offset.value; + this->bb.max.y += this->config.raft_offset.value; } - mesh.translate(-bb.min.x, -bb.min.y, -bb.min.z); // align to origin - bb.translate(-bb.min.x, -bb.min.y, -bb.min.z); // align to origin - this->size = bb.size(); + mesh.translate(0, 0, -bb.min.z); + this->bb.translate(0, 0, -bb.min.z); // if we are generating a raft, first_layer_height will not affect mesh slicing const float lh = this->config.layer_height.value; @@ -68,9 +66,11 @@ SLAPrint::slice() // generate points following the shape of each island Points pillars_pos; - const float spacing = scale_(this->config.support_material_spacing); + const coordf_t spacing = scale_(this->config.support_material_spacing); + const coordf_t radius = scale_(this->sm_pillars_radius()); for (ExPolygons::const_iterator it = overhangs.begin(); it != overhangs.end(); ++it) { - for (float inset = -spacing/2; inset += spacing; ) { + // leave a radius/2 gap between pillars and contour to prevent lateral adhesion + for (float inset = radius * 1.5;; inset += spacing) { // inset according to the configured spacing Polygons curr = offset(*it, -inset); if (curr.empty()) break; @@ -134,7 +134,8 @@ SLAPrint::slice() void SLAPrint::write_svg(const std::string &outputfile) const { - double support_material_radius = this->config.support_material_extrusion_width.get_abs_value(this->config.layer_height)/2; + const Sizef3 size = this->bb.size(); + const double support_material_radius = sm_pillars_radius(); FILE* f = fopen(outputfile.c_str(), "w"); fprintf(f, @@ -142,7 +143,7 @@ SLAPrint::write_svg(const std::string &outputfile) const "\n" "\n" "\n" - , this->size.x, this->size.y, SLIC3R_VERSION); + , size.x, size.y, SLIC3R_VERSION); for (size_t i = 0; i < this->layers.size(); ++i) { const Layer &layer = this->layers[i]; @@ -157,8 +158,8 @@ SLAPrint::write_svg(const std::string &outputfile) const std::ostringstream d; d << "M "; for (Points::const_iterator p = mp->points.begin(); p != mp->points.end(); ++p) { - d << unscale(p->x) << " "; - d << unscale(p->y) << " "; + d << unscale(p->x) - this->bb.min.x << " "; + d << size.y - (unscale(p->y) - this->bb.min.y) << " "; // mirror Y coordinates as SVG uses downwards Y } d << "z"; pd += d.str() + " "; @@ -181,7 +182,9 @@ SLAPrint::write_svg(const std::string &outputfile) const ); fprintf(f,"\t\t\n", - unscale(it->x), unscale(it->y), radius + unscale(it->x) - this->bb.min.x, + size.y - (unscale(it->y) - this->bb.min.y), + radius ); } } @@ -191,4 +194,12 @@ SLAPrint::write_svg(const std::string &outputfile) const fprintf(f,"\n"); } +coordf_t +SLAPrint::sm_pillars_radius() const +{ + coordf_t radius = this->config.support_material_extrusion_width.get_abs_value(this->config.support_material_spacing)/2; + if (radius == 0) radius = this->config.support_material_spacing / 3; // auto + return radius; +} + } diff --git a/xs/src/libslic3r/SLAPrint.hpp b/xs/src/libslic3r/SLAPrint.hpp index 202eed8c2..09664b386 100644 --- a/xs/src/libslic3r/SLAPrint.hpp +++ b/xs/src/libslic3r/SLAPrint.hpp @@ -38,7 +38,9 @@ class SLAPrint private: Model* model; - Sizef3 size; + BoundingBoxf3 bb; + + coordf_t sm_pillars_radius() const; }; } diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index 5bba8fcb6..6852cb8dd 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -51,6 +51,7 @@ REGISTER_CLASS(PrintRegionConfig, "Config::PrintRegion"); REGISTER_CLASS(GCodeConfig, "Config::GCode"); REGISTER_CLASS(PrintConfig, "Config::Print"); REGISTER_CLASS(FullPrintConfig, "Config::Full"); +REGISTER_CLASS(SLAPrint, "SLAPrint"); REGISTER_CLASS(Surface, "Surface"); REGISTER_CLASS(SurfaceCollection, "Surface::Collection"); REGISTER_CLASS(TriangleMesh, "TriangleMesh"); diff --git a/xs/xsp/SLAPrint.xsp b/xs/xsp/SLAPrint.xsp new file mode 100644 index 000000000..daecce38f --- /dev/null +++ b/xs/xsp/SLAPrint.xsp @@ -0,0 +1,61 @@ +%module{Slic3r::XS}; + +%{ +#include +#include "libslic3r/SLAPrint.hpp" +%} + +%name{Slic3r::SLAPrint} class SLAPrint { + SLAPrint(Model* model); + ~SLAPrint(); + + void apply_config(DynamicPrintConfig* config) + %code%{ THIS->config.apply(*config, true); %}; + void slice(); + size_t layer_count() + %code{% RETVAL = THIS->layers.size(); %}; + + DynamicPrintConfig* config() + %code%{ RETVAL = new DynamicPrintConfig; RETVAL->apply(THIS->config); %}; + +%{ + +ExPolygons +SLAPrint::slices(i) + size_t i; + CODE: + RETVAL = THIS->layers[i].slices; + OUTPUT: + RETVAL + +std::vector +SLAPrint::heights() + CODE: + for (std::vector::const_iterator it = THIS->layers.begin(); + it != THIS->layers.end(); + ++it) + RETVAL.push_back(it->print_z); + OUTPUT: + RETVAL + +SV* +SLAPrint::sm_pillars() + CODE: + AV* av = newAV(); + for (std::vector::const_iterator it = THIS->sm_pillars.begin(); + it != THIS->sm_pillars.end(); + ++it) + { + HV* hv = newHV(); + (void)hv_stores( hv, "top_layer", newSViv(it->top_layer) ); + (void)hv_stores( hv, "bottom_layer", newSViv(it->bottom_layer) ); + (void)hv_stores( hv, "point", perl_to_SV_clone_ref((Point)*it) ); + av_push(av, newRV_noinc((SV*)hv)); + } + RETVAL = newRV_noinc((SV*)av); + OUTPUT: + RETVAL + +%} + +}; diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 5f87f51c2..b91ba4794 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -56,6 +56,10 @@ TriangleMesh* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T +SLAPrint* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +Clone O_OBJECT_SLIC3R_T + Point* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 89975c949..87039c564 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -192,6 +192,9 @@ %typemap{Ref}{simple}; %typemap{Clone}{simple}; %typemap{GLVertexArray*}; +%typemap{SLAPrint*}; +%typemap{Ref}{simple}; +%typemap{Clone}{simple}; %typemap{PrintRegionPtrs*}; %typemap{PrintObjectPtrs*}; From a4a68cb9fb16adc283331e87d653d3886ab3a8bc Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 23 Nov 2016 18:56:02 +0100 Subject: [PATCH 047/225] Material estimate in DLP projector --- lib/Slic3r/GUI/Projector.pm | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index d7d9e7eb8..f91ddf96a 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -468,8 +468,9 @@ sub show_print_time { my $duration = $self->controller->print_time; - $self->_set_status(sprintf "Estimated print time: %d minutes and %d seconds", - int($duration/60), ($duration - int($duration/60)*60)); # % truncates to integer + $self->_set_status(sprintf "Estimated print time: %d minutes and %d seconds - %.2f liters", + int($duration/60), ($duration - int($duration/60)*60), # % truncates to integer + $self->controller->total_resin); } sub _close { @@ -505,6 +506,7 @@ package Slic3r::GUI::Projector::Controller; use Moo; use Wx qw(wxTheApp :id :timer); use Wx::Event qw(EVT_TIMER); +use Slic3r::Geometry qw(unscale); use Slic3r::Print::State ':steps'; use Time::HiRes qw(gettimeofday tv_interval); @@ -704,8 +706,21 @@ sub print_time { my ($self) = @_; return $self->config2->{bottom_layers} * $self->config2->{bottom_exposure_time} - + (@{$self->_heights} - $self->config2->{bottom_layers}) * $self->config2->{exposure_time} - + @{$self->_heights} * $self->config2->{settle_time}; + + ($self->_print->layer_count - $self->config2->{bottom_layers}) * $self->config2->{exposure_time} + + $self->_print->layer_count * $self->config2->{settle_time}; +} + +sub total_resin { + my ($self) = @_; + + my $vol = 0; # mm^3 + + for my $i (0..($self->_print->layer_count-1)) { + my $lh = $self->_heights->[$i] - ($i == 0 ? 0 : $self->_heights->[$i-1]); + $vol += unscale(unscale($_->area)) * $lh for @{ $self->_print->slices($i) }; + } + + return $vol/1000/1000; # liters } sub DESTROY { From e8331f066dee9fc83fe3faa774419f4e56e71ef3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 23 Nov 2016 18:58:40 +0100 Subject: [PATCH 048/225] Include layer-height as sttribute in SLA SVG output --- xs/src/libslic3r/SLAPrint.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index a50859fbd..985e0e2cb 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -147,8 +147,12 @@ SLAPrint::write_svg(const std::string &outputfile) const for (size_t i = 0; i < this->layers.size(); ++i) { const Layer &layer = this->layers[i]; - fprintf(f, "\t\n", i, - layer.print_z, layer.slice_z); + fprintf(f, + "\t\n", i, + layer.print_z, + layer.slice_z, + layer.print_z - (i == 0 ? 0 : this->layers[i-1].print_z) + ); const ExPolygons &slices = layer.slices.expolygons; for (ExPolygons::const_iterator it = slices.begin(); it != slices.end(); ++it) { From c87659fdc2be71fa1f332b6af1ef5c96dff8b791 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 23 Nov 2016 21:12:01 +0100 Subject: [PATCH 049/225] Update MANIFEST and rename t/22_config to t/23_config because we already have a 22 --- xs/MANIFEST | 5 +++++ xs/t/{22_config.t => 23_config.t} | 0 2 files changed, 5 insertions(+) rename xs/t/{22_config.t => 23_config.t} (100%) diff --git a/xs/MANIFEST b/xs/MANIFEST index 5453a289c..9c3154826 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -70,6 +70,8 @@ src/libslic3r/PrintConfig.cpp src/libslic3r/PrintConfig.hpp src/libslic3r/PrintObject.cpp src/libslic3r/PrintRegion.cpp +src/libslic3r/SLAPrint.cpp +src/libslic3r/SLAPrint.hpp src/libslic3r/SupportMaterial.hpp src/libslic3r/Surface.cpp src/libslic3r/Surface.hpp @@ -122,6 +124,8 @@ t/19_model.t t/20_print.t t/21_gcode.t t/22_exception.t +t/23_config.t +t/inc/22_config_bad_config_options.ini xsp/BoundingBox.xsp xsp/BridgeDetector.xsp xsp/Clipper.xsp @@ -152,6 +156,7 @@ xsp/Polygon.xsp xsp/Polyline.xsp xsp/PolylineCollection.xsp xsp/Print.xsp +xsp/SLAPrint.xsp xsp/SupportMaterial.xsp xsp/Surface.xsp xsp/SurfaceCollection.xsp diff --git a/xs/t/22_config.t b/xs/t/23_config.t similarity index 100% rename from xs/t/22_config.t rename to xs/t/23_config.t From 9bcb467eb154804c88122bd40f4669a8a960e8c2 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 3 Jun 2016 09:36:23 +0200 Subject: [PATCH 050/225] Moved the Bonjour discovery to Tab.pm, when no Bonjour devices found, a simple message box is shown. Otherwise the Bonjour selection dialog with an empty list crashes on windows. --- lib/Slic3r/GUI/BonjourBrowser.pm | 8 ++------ lib/Slic3r/GUI/Tab.pm | 25 ++++++++++++++++++------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/lib/Slic3r/GUI/BonjourBrowser.pm b/lib/Slic3r/GUI/BonjourBrowser.pm index 21966d6e8..469d0302e 100644 --- a/lib/Slic3r/GUI/BonjourBrowser.pm +++ b/lib/Slic3r/GUI/BonjourBrowser.pm @@ -9,14 +9,10 @@ use base 'Wx::Dialog'; sub new { my $class = shift; - my ($parent) = @_; + my ($parent, $devices) = @_; my $self = $class->SUPER::new($parent, -1, "Device Browser", wxDefaultPosition, [350,700], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); - # look for devices - eval "use Net::Bonjour; 1"; - my $res = Net::Bonjour->new('http'); - $res->discover; - $self->{devices} = [ $res->entries ]; + $self->{devices} = $devices; # label my $text = Wx::StaticText->new($self, -1, "Choose an OctoPrint device in your network:", wxDefaultPosition, wxDefaultSize); diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 75d51e98f..a49d6fd2f 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -1161,13 +1161,24 @@ sub build { } EVT_BUTTON($self, $btn, sub { - my $dlg = Slic3r::GUI::BonjourBrowser->new($self); - if ($dlg->ShowModal == wxID_OK) { - my $value = $dlg->GetValue . ":" . $dlg->GetPort; - $self->{config}->set('octoprint_host', $value); - $self->update_dirty; - $self->_on_value_change('octoprint_host', $value); - $self->reload_config; + # look for devices + my $entries; + { + my $res = Net::Bonjour->new('http'); + $res->discover; + $entries = [ $res->entries ]; + } + if (@{$entries}) { + my $dlg = Slic3r::GUI::BonjourBrowser->new($self, $entries); + if ($dlg->ShowModal == wxID_OK) { + my $value = $dlg->GetValue . ":" . $dlg->GetPort; + $self->{config}->set('octoprint_host', $value); + $self->update_dirty; + $self->_on_value_change('octoprint_host', $value); + $self->reload_config; + } + } else { + Wx::MessageDialog->new($self, 'No Bonjour device found', 'Device Browser', wxOK | wxICON_INFORMATION)->ShowModal; } }); From febf2b06c0263dfde21cd98559832d8915c96e6d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 27 Jun 2016 15:15:06 +0200 Subject: [PATCH 051/225] Documented the build process --- doc/How_to_build_Slic3r.txt | 279 ++++++++++++++++++++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 doc/How_to_build_Slic3r.txt diff --git a/doc/How_to_build_Slic3r.txt b/doc/How_to_build_Slic3r.txt new file mode 100644 index 000000000..326b80a50 --- /dev/null +++ b/doc/How_to_build_Slic3r.txt @@ -0,0 +1,279 @@ +How to build Slic3r on Mac OS X 10.7 Lion 64bit +--------------------------------------------- +Vojtech Bubnik, 2016-04-26 + + +1) Install Mac OS X 10.7 Lion 64 bit with X Code +------------------------------------------------ + +Installation is possible using the http://iatkos.me/ distribution on Virtual Box: +\\rs.prusa\MacOSX_10.7.2_on_VirtualBox_with_XCode4.pdf + +Here is the modified Mac OS X 10.7 Lion 64bit installation image: +\\rs.prusa\Development\Slic3r-Prusa\How_to_build_on_MacOSX_Lion\iATKOS_L2 + +Naturally, it is possible to develop on a real Mac. On Mac, two commercial solutions are available to legally virtualize MacOS on MacOS: +http://www.parallels.com/eu/products/desktop/ +http://www.vmware.com/products/workstation/ + +Installation of a X Code on an OS X 10.7 Lion needs a bit of work. The latest X Code supported by the Lion on a Virtual Box is 4.21. The trouble is, the certificates of the X Code 4.21 installation package expired. One way to work around the certificate is to flatten the installation package by unpacking and repacking it: +pkgutil --expand Foobar.pkg foobar +pkgutil --flatten foobar barfoo.pkg + +The flattened package is available here: +\\rs.prusa\Development\Slic3r-Prusa\installxcode_421_lion_fixed.pkg + +This installer does not install the X Code directly. Instead, it installs another installer with a set of 47 pkg files. These files have their certificates expired as well. You will find the packages on your MacOS here: +/Applications/Install Xcode.app/Contents/Resources/Packages/ + +It is best to flatten them in a loop: +cd /Applications/Install\ Xcode.app/Contents/Resources/Packages/ +for f in *.pkg; do + pkgutil --expand $f /tmp/$f + rm -f $f + pkgutil --flatten /tmp/$f $f +done + +After that, you may finish the installation of Xcode by running +/Applications/Install\ Xcode.app + + +1b) Installing the Xcode on a newer system +------------------------------------------- +You will need to register as an Apple developer on +https://developer.apple.com/ +log in and download and install Xcode +https://developer.apple.com/downloads/ +You will likely need to download and install Xcode Command Line Tools, though the Xcode 4.1 came with the command line tools installed. + + +2) Prepare the development environment +-------------------------------------- + +Install the brew package manager: +http://brew.sh/ +The brew package manager requires the git command line tool. Normally the git tool is installed as part of the Xcode command line tools. +Copy and execute a command line from the top of http://brew.sh/ . It is possible, that the invocation of git fails because of some parameters the old git does not recognize. If so, invoke the git call manually. + +Compile the boost library using brew. Following line compiles a 64bit boost with both static and shared libraries. +brew install boost --universal + +Install dylibbundler tool. The dylibbundler tool serves to collect dependent dynamic libraries and fix their linkage. Execute +brew install dylibbundler + +3) Install perl +--------------- + +We don't want to distribute perl pre-installed on the Mac OS box. First, the system perl installation is not correct on some Mac OS versions, second it is not rellocatable. To compile a 64bit rellocatable perl, we use the perlbrew distribution. The perlbrew distribution installs into a user home directory and it allows switching between multiple versions of perl. +http://perlbrew.pl/ + +First install perlbrew +\curl -L http://install.perlbrew.pl | bash +Then compile the newest perl with the rellocatable @INC path and with multithreading enabled, execute following line: +perlbrew install --threads -Duserelocatableinc --switch perl-5.22.0 +The --switch parameter switches the active perl to the currently compiled one. +Available perl versions could be listed by calling +perlbrew available + +Initialize CPAN, install PAR and PAR::Packer modules +execute cpan command, from the cpan prompt, run +install App::cpanminus +install PAR +install PAR::Packer +quit + +4) Download and install Slic3r +------------------------------ + +git clone git://github.com/alexrj/Slic3r +cd Slic3r +perl Build.PL + +Now Slic3r shall be compiled. You may try to execute +perl slic3r.pl +to get a help screen, or +perl slic3r.pl some_model.stl +to have the model sliced. + +5) Download and compile the GUI libraries needed to execute Slic3r in GUI mode +------------------------------------------------------------------------------ + +For the current Slic3r 1.2.30 code base, set the environment variable SLIC3R_STATIC to link a static version of the boost library: +export SLIC3R_STATIC=1 + +then execute +perl Build.PL --gui +and keep your fingers crossed. The Build.PL script downloads and compiles the WxWidgets 3.0 through a Alien::Wx PERL package. The WxWidget shared libraries will land into +~/perl5/perlbrew/perls/perl-5.22.1/lib/site_perl/5.22.1/darwin-thread-multi-2level/Alien/wxWidgets/ + +On Maverics, we experienced following issue compiling WxPerl: +http://wiki.bolay.net/doku.php?id=acdsn:acdsn-a:mac + +Now you could run the GUI version of slic3r by calling +perl slic3r.pl --gui +If some dependency is missing, the MacOS system will let you know. + +6) Packing the Slic3r +--------------------- + +Perl is an operating system on its own. Many modules are shared among multiple applications and it is difficult to extract a stand-alone application from a perl installation manually. Fortunately, tools are available, which automate the process to some extent. One of the tools is the PAR::Packer. The PAR::Packer tool (pp executable) is able to create a standalone executable for a perl script. The standalone executable contains a PAR archive (a zip file) bundled with a perl interpreter. When executed, the bundled executable will decompress most of the PAR archive into a temp folder. Because of that, we will use the PAR::Packer to resolve and collect the dependencies, but we will create an installer manually. + +The PAR::Packer could analyze the dependencies by a statical analysis, or at a runtime. The statical analysis does not resolve the dynamically loaded modules. On the other side, the statical analysis is pessimistic, therefore it often collects unneeded packages. The dynamic analysis may miss some package, if not all branches of a code are executed. We will try to solely depend on the dynamic analysis to keep the installation size minimal. We may need to develop a protocol or an automatic UI tool to exercise as much as possible from the Slic3r GUI to pack the GUI version reliably. Once a reliable list of dependencies is collected, we may not need the PAR::Packer anymore. + +To create a PAR archive of a command line slic3r, execute +pp -e -p -x slic3r.pl --xargs cube.stl -o slic3r.par +and let the slic3r slice a cube.stl to load the dynamic modules. + +To create a PAR archive of a GUI slic3r, execute +pp -e -p -x slic3r.pl --xargs --gui -o slic3r.par +and exercise the slic3r gui to load all modules. + +Rename the slic3r.par file to slic3r.zip and decompress. Most of the code needed to execute Slic3r is there, only the perl executable is missing for the command line slic3r, and the WxWidgets shared libraries and the liblzma shared library are missing for the GUI version. + +7) Collecting the dependent shared libraries, making the link paths relative +---------------------------------------------------------------------------- + +We have linked Slic3r against a static boost library, therefore the command line slic3r is not dependent on any non-system shared library. The situation is different for the GUI slic3r, which links dynamically against WxWidgets and liblzma. + +The trick developed by Apple to allow rellocable shared libraries is to addres a shared library relatively to the path of the executable by encoding a special token @executable_path at the start of the path. Unfortunately the libraries requried by Slic3r are compiled with absolute paths. + +Once the slic3r.par archive is unpacked, one may list the native shared libraries by +find ./ -name '*.bundle' +and one may list the dependencies by running +otool -L somefile.bundle +Most of the dependencies point to system directores and these dependences are always fulfilled. Dependencies pointing to the WxWidget libraries need to be fixed. These have a form +~/perlbrew/perls/perl-5.22.1/lib/site_perl/5.22.1/darwin-thread-multi-2level/Alien/wxWidgets/osx_cocoa_3_0_2_uni/lib/libwx_*.dylib +and we need to replace them with +@executable_path/../Frameworks/libwx_*.dylib +Another dependency, which needs our attention is +/usr/local/Cellar/xz/5.2.2/lib/liblzma.5.dylib + +Fortunately, a tool dylibbundler was developed to address this problem. +First install dylibbundler by calling +brew dylibbundler + +For some installations, the dylibbundler tool sufficiently fixes all dependencies. Unfortunately, the WxWidgets installation is inconsistent in the versioning, therefore a certain clean-up is required. Namely, the WxWidgets libraries are compiled with the full build number in their file name. For example, the base library is built as libwx_baseu-3.0.0.2.0.dylib and a symlink is created libwx_baseu-3.0.dylib pointing to the full name. Then some of the Wx libraries link against the full name and some against the symlink, leading the dylibbundler to pack both. We solved the problem by whipping up a following script: +\\rs.prusa\Development\Slic3r-Prusa\How_to_build_on_MacOSX_Lion\fix_dependencies.sh + +call +slic3r_dependencies.sh --fix +to collect the shared libraries into Content/Frameworks and to fix their linkage. + +call +slic3r_dependencies.sh --show +to list dependent libraries in a sorted order. All the non-system dependencies shall start with @executable_path after the fix. + + + +8) Packing Slic3r into a dmg image using a bunch of scripts +----------------------------------------------------------- + +Instead of relying on the PAR::Packer to collect the dependencies, we have used PAR::Packer to extract the dependencies, we manually cleaned them up and created an installer script. +\\rs.prusa\Development\Slic3r-Prusa\How_to_build_on_MacOSX_Lion\Slic3r-Build-MacOS +First compile Slic3r, then call build_dmg.sh with a path to the Slic3r source tree as a parameter. +The script will collect all dependencies into Slic3r.app and it will create Slic3r.dmg. +If SLIC3R_GUI variable is defined, a GUI variant of Slic3r will be packed. + + + + +How to build on Windows +----------------------- + +The prefered perl distribution on MS Windows is the Strawberry Perl 5.22.1.3 (32bit) +http://strawberryperl.com/ + +Let it install into c:\strawberry +You may make a copy of the distribution. We recommend to make following copies: +For a release command line only build: c:\strawberry-minimal +For a release GUI build: c:\strawberry-minimal-gui +For a development build with debugging information: c:\strawberry-debug +and to make one of them active by making a directory junction: +mklink /d c:\Strawberry c:\Strawberry-debug + +Building boost: +Slic3r seems to have a trouble with the latest boost 1.60.0 on Windows. Please use 1.59. +Decompress it to +c:\dev\ +otherwise it will not be found by the Build.PL on Windows. You may consider to hack xs\Build.PL with your prefered boost path. +run +bootstrap.bat mingw +b2 toolset=gcc + +Install git command line +https://git-scm.com/ + +Download Slic3r source code +git clone git://github.com/alexrj/Slic3r.git + +Run compilation +cd Slic3r +perl Build.PL +perl Build.PL --gui + +With a bit of luck, you will end up with a working Slic3r including GUI. + + + + +Packing on Windows +------------------ + +Life is easy on Windows. PAR::Packer will help again to collect the dependencies. The basic procedure is the same as for MacOS: + +To create a PAR archive of a command line slic3r, execute +pp -e -p -x slic3r.pl --xargs cube.stl -o slic3r.par +and let the slic3r slice a cube.stl to load the dynamic modules. + +To create a PAR archive of a GUI slic3r, execute +pp -e -p -x slic3r.pl --xargs --gui -o slic3r.par +and exercise the slic3r gui to load all modules. + +The standalone installation is then created from the PAR archive by renaming it into a zip and adding following binaries from c:\strawberry to the root of the extracted zip: +perl5.22.1.exe +perl522.dll +libgcc_s_sjlj-1.dll +libstdc++-6.dll +libwinpthread-1.dll + +The GUI build requires following DLLs in addition: +libglut-0_.dll +wxbase30u_gcc_custom.dll +wxmsw30u_adv_gcc_custom.dll +wxmsw30u_core_gcc_custom.dll +wxmsw30u_gl_gcc_custom.dll +wxmsw30u_html_gcc_custom.dll + +and the var directory with the icons needs to be copied to the destination directory. + +To run the slic3r, move the script\slic3r.pl one level up and create a following tiny windows batch in the root of the unpacked zip: +@perl5.22.1.exe slic3r.pl %* +A windows shortcut may be created for the GUI version. Instead of the perl.exe, it is better to use the wperl.exe to start the GUI Slic3r, because it does not open a text console. + +The strawberry perl is already rellocatable, which means that the perl interpreter will find the perl modules in the lib directory, +and Windows look up the missing DLLs in the directory of the executable, therefore no further rellocation effort is necessary. + + +Packing on Windows, a single EXE solution +----------------------------------------- + +One may try to create a PAR executable for command line slic3r: +pp -M Encode::Locale -M Moo -M Thread::Semaphore -M Slic3r::XS -M Unicode::Normalize -o slic3r.exe slic3r.pl + +One may as well create a PAR executable for Windows GUI: +pp -M Encode::Locale -M Moo -M Thread::Semaphore -M OpenGL -M Slic3r::XS -M Unicode::Normalize -M Wx -M Class::Accessor -M Wx::DND -M Wx::Grid -M Wx::Print -M Wx::Html -M Wx::GLCanvas -M Math::Trig -M threads -M threads::shared -M Thread::Queue -l C:\strawberry\perl\site\lib\auto\Wx\Wx.xs.dll -o -l C:\strawberry\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxbase30u_gcc_custom.dll -l C:\strawberry\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_core_gcc_custom.dll -l C:\strawberry\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_gl_gcc_custom.dll -l C:\strawberry\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_adv_gcc_custom.dll -l C:\strawberry\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_html_gcc_custom.dll -o slic3r.exe slic3r.pl + +Remember, that these executables will unpack into a temporary directory. The directory may be declared by setting an environment variable PAR_GLOBAL_TEMP. Otherwise the temporaries are unpacked into +C:\Users\xxx\AppData\Local\Temp\par-xxxxxx + + +Debugging the perl, debugging on Windows +---------------------------------------- + +It is possible to debug perl using the integrated debugger. The EPIC plugin for Eclipse works very well with an older eclipse-SDK-3.6.2. There is a catch though: The Perl debugger does not work correctly with multiple threads running under the Perl interpreter. If that happens, the EPIC plugin gets confused and the debugger stops working. The same happens with the Komodo IDE. + +Debugging the C++ code works fine using the latest Eclipse for C++ and the gdb of MinGW. The gdb packed with the Strawberry distribution does not contain the Python support, so pretty printing of the stl containers only works if another gdb build is used. The one of the QT installation works well. + +It is yet a bit more complicated. The Strawberry MINGW is compiled for a different C++ exception passing model (SJLJ) than the other MINGWs, so one cannot simply combine MINGWs on Windows. For an unknown reason the nice debugger of the QT Creator hangs when debugging the C++ compiled by the Strawberry MINGW. Mabe it is because of the different exception passing models. + +And to disable optimization of the C/C++ code, one has to manually modify Config_heavy.pl in the Perl central installation. The SLIC3R_DEBUG environment variable did not override all the -O2 and -O3 flags that the perl build adds the gcc execution line. From c8f2e55d43e076663b525df0fbb2f5bff3dc35d5 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 27 Jun 2016 15:22:58 +0200 Subject: [PATCH 052/225] Revert "Documented the build process" This reverts commit 5e7aee217e6accdca305ed7108dd44142aed64db. --- doc/How_to_build_Slic3r.txt | 279 ------------------------------------ 1 file changed, 279 deletions(-) delete mode 100644 doc/How_to_build_Slic3r.txt diff --git a/doc/How_to_build_Slic3r.txt b/doc/How_to_build_Slic3r.txt deleted file mode 100644 index 326b80a50..000000000 --- a/doc/How_to_build_Slic3r.txt +++ /dev/null @@ -1,279 +0,0 @@ -How to build Slic3r on Mac OS X 10.7 Lion 64bit ---------------------------------------------- -Vojtech Bubnik, 2016-04-26 - - -1) Install Mac OS X 10.7 Lion 64 bit with X Code ------------------------------------------------- - -Installation is possible using the http://iatkos.me/ distribution on Virtual Box: -\\rs.prusa\MacOSX_10.7.2_on_VirtualBox_with_XCode4.pdf - -Here is the modified Mac OS X 10.7 Lion 64bit installation image: -\\rs.prusa\Development\Slic3r-Prusa\How_to_build_on_MacOSX_Lion\iATKOS_L2 - -Naturally, it is possible to develop on a real Mac. On Mac, two commercial solutions are available to legally virtualize MacOS on MacOS: -http://www.parallels.com/eu/products/desktop/ -http://www.vmware.com/products/workstation/ - -Installation of a X Code on an OS X 10.7 Lion needs a bit of work. The latest X Code supported by the Lion on a Virtual Box is 4.21. The trouble is, the certificates of the X Code 4.21 installation package expired. One way to work around the certificate is to flatten the installation package by unpacking and repacking it: -pkgutil --expand Foobar.pkg foobar -pkgutil --flatten foobar barfoo.pkg - -The flattened package is available here: -\\rs.prusa\Development\Slic3r-Prusa\installxcode_421_lion_fixed.pkg - -This installer does not install the X Code directly. Instead, it installs another installer with a set of 47 pkg files. These files have their certificates expired as well. You will find the packages on your MacOS here: -/Applications/Install Xcode.app/Contents/Resources/Packages/ - -It is best to flatten them in a loop: -cd /Applications/Install\ Xcode.app/Contents/Resources/Packages/ -for f in *.pkg; do - pkgutil --expand $f /tmp/$f - rm -f $f - pkgutil --flatten /tmp/$f $f -done - -After that, you may finish the installation of Xcode by running -/Applications/Install\ Xcode.app - - -1b) Installing the Xcode on a newer system -------------------------------------------- -You will need to register as an Apple developer on -https://developer.apple.com/ -log in and download and install Xcode -https://developer.apple.com/downloads/ -You will likely need to download and install Xcode Command Line Tools, though the Xcode 4.1 came with the command line tools installed. - - -2) Prepare the development environment --------------------------------------- - -Install the brew package manager: -http://brew.sh/ -The brew package manager requires the git command line tool. Normally the git tool is installed as part of the Xcode command line tools. -Copy and execute a command line from the top of http://brew.sh/ . It is possible, that the invocation of git fails because of some parameters the old git does not recognize. If so, invoke the git call manually. - -Compile the boost library using brew. Following line compiles a 64bit boost with both static and shared libraries. -brew install boost --universal - -Install dylibbundler tool. The dylibbundler tool serves to collect dependent dynamic libraries and fix their linkage. Execute -brew install dylibbundler - -3) Install perl ---------------- - -We don't want to distribute perl pre-installed on the Mac OS box. First, the system perl installation is not correct on some Mac OS versions, second it is not rellocatable. To compile a 64bit rellocatable perl, we use the perlbrew distribution. The perlbrew distribution installs into a user home directory and it allows switching between multiple versions of perl. -http://perlbrew.pl/ - -First install perlbrew -\curl -L http://install.perlbrew.pl | bash -Then compile the newest perl with the rellocatable @INC path and with multithreading enabled, execute following line: -perlbrew install --threads -Duserelocatableinc --switch perl-5.22.0 -The --switch parameter switches the active perl to the currently compiled one. -Available perl versions could be listed by calling -perlbrew available - -Initialize CPAN, install PAR and PAR::Packer modules -execute cpan command, from the cpan prompt, run -install App::cpanminus -install PAR -install PAR::Packer -quit - -4) Download and install Slic3r ------------------------------- - -git clone git://github.com/alexrj/Slic3r -cd Slic3r -perl Build.PL - -Now Slic3r shall be compiled. You may try to execute -perl slic3r.pl -to get a help screen, or -perl slic3r.pl some_model.stl -to have the model sliced. - -5) Download and compile the GUI libraries needed to execute Slic3r in GUI mode ------------------------------------------------------------------------------- - -For the current Slic3r 1.2.30 code base, set the environment variable SLIC3R_STATIC to link a static version of the boost library: -export SLIC3R_STATIC=1 - -then execute -perl Build.PL --gui -and keep your fingers crossed. The Build.PL script downloads and compiles the WxWidgets 3.0 through a Alien::Wx PERL package. The WxWidget shared libraries will land into -~/perl5/perlbrew/perls/perl-5.22.1/lib/site_perl/5.22.1/darwin-thread-multi-2level/Alien/wxWidgets/ - -On Maverics, we experienced following issue compiling WxPerl: -http://wiki.bolay.net/doku.php?id=acdsn:acdsn-a:mac - -Now you could run the GUI version of slic3r by calling -perl slic3r.pl --gui -If some dependency is missing, the MacOS system will let you know. - -6) Packing the Slic3r ---------------------- - -Perl is an operating system on its own. Many modules are shared among multiple applications and it is difficult to extract a stand-alone application from a perl installation manually. Fortunately, tools are available, which automate the process to some extent. One of the tools is the PAR::Packer. The PAR::Packer tool (pp executable) is able to create a standalone executable for a perl script. The standalone executable contains a PAR archive (a zip file) bundled with a perl interpreter. When executed, the bundled executable will decompress most of the PAR archive into a temp folder. Because of that, we will use the PAR::Packer to resolve and collect the dependencies, but we will create an installer manually. - -The PAR::Packer could analyze the dependencies by a statical analysis, or at a runtime. The statical analysis does not resolve the dynamically loaded modules. On the other side, the statical analysis is pessimistic, therefore it often collects unneeded packages. The dynamic analysis may miss some package, if not all branches of a code are executed. We will try to solely depend on the dynamic analysis to keep the installation size minimal. We may need to develop a protocol or an automatic UI tool to exercise as much as possible from the Slic3r GUI to pack the GUI version reliably. Once a reliable list of dependencies is collected, we may not need the PAR::Packer anymore. - -To create a PAR archive of a command line slic3r, execute -pp -e -p -x slic3r.pl --xargs cube.stl -o slic3r.par -and let the slic3r slice a cube.stl to load the dynamic modules. - -To create a PAR archive of a GUI slic3r, execute -pp -e -p -x slic3r.pl --xargs --gui -o slic3r.par -and exercise the slic3r gui to load all modules. - -Rename the slic3r.par file to slic3r.zip and decompress. Most of the code needed to execute Slic3r is there, only the perl executable is missing for the command line slic3r, and the WxWidgets shared libraries and the liblzma shared library are missing for the GUI version. - -7) Collecting the dependent shared libraries, making the link paths relative ----------------------------------------------------------------------------- - -We have linked Slic3r against a static boost library, therefore the command line slic3r is not dependent on any non-system shared library. The situation is different for the GUI slic3r, which links dynamically against WxWidgets and liblzma. - -The trick developed by Apple to allow rellocable shared libraries is to addres a shared library relatively to the path of the executable by encoding a special token @executable_path at the start of the path. Unfortunately the libraries requried by Slic3r are compiled with absolute paths. - -Once the slic3r.par archive is unpacked, one may list the native shared libraries by -find ./ -name '*.bundle' -and one may list the dependencies by running -otool -L somefile.bundle -Most of the dependencies point to system directores and these dependences are always fulfilled. Dependencies pointing to the WxWidget libraries need to be fixed. These have a form -~/perlbrew/perls/perl-5.22.1/lib/site_perl/5.22.1/darwin-thread-multi-2level/Alien/wxWidgets/osx_cocoa_3_0_2_uni/lib/libwx_*.dylib -and we need to replace them with -@executable_path/../Frameworks/libwx_*.dylib -Another dependency, which needs our attention is -/usr/local/Cellar/xz/5.2.2/lib/liblzma.5.dylib - -Fortunately, a tool dylibbundler was developed to address this problem. -First install dylibbundler by calling -brew dylibbundler - -For some installations, the dylibbundler tool sufficiently fixes all dependencies. Unfortunately, the WxWidgets installation is inconsistent in the versioning, therefore a certain clean-up is required. Namely, the WxWidgets libraries are compiled with the full build number in their file name. For example, the base library is built as libwx_baseu-3.0.0.2.0.dylib and a symlink is created libwx_baseu-3.0.dylib pointing to the full name. Then some of the Wx libraries link against the full name and some against the symlink, leading the dylibbundler to pack both. We solved the problem by whipping up a following script: -\\rs.prusa\Development\Slic3r-Prusa\How_to_build_on_MacOSX_Lion\fix_dependencies.sh - -call -slic3r_dependencies.sh --fix -to collect the shared libraries into Content/Frameworks and to fix their linkage. - -call -slic3r_dependencies.sh --show -to list dependent libraries in a sorted order. All the non-system dependencies shall start with @executable_path after the fix. - - - -8) Packing Slic3r into a dmg image using a bunch of scripts ------------------------------------------------------------ - -Instead of relying on the PAR::Packer to collect the dependencies, we have used PAR::Packer to extract the dependencies, we manually cleaned them up and created an installer script. -\\rs.prusa\Development\Slic3r-Prusa\How_to_build_on_MacOSX_Lion\Slic3r-Build-MacOS -First compile Slic3r, then call build_dmg.sh with a path to the Slic3r source tree as a parameter. -The script will collect all dependencies into Slic3r.app and it will create Slic3r.dmg. -If SLIC3R_GUI variable is defined, a GUI variant of Slic3r will be packed. - - - - -How to build on Windows ------------------------ - -The prefered perl distribution on MS Windows is the Strawberry Perl 5.22.1.3 (32bit) -http://strawberryperl.com/ - -Let it install into c:\strawberry -You may make a copy of the distribution. We recommend to make following copies: -For a release command line only build: c:\strawberry-minimal -For a release GUI build: c:\strawberry-minimal-gui -For a development build with debugging information: c:\strawberry-debug -and to make one of them active by making a directory junction: -mklink /d c:\Strawberry c:\Strawberry-debug - -Building boost: -Slic3r seems to have a trouble with the latest boost 1.60.0 on Windows. Please use 1.59. -Decompress it to -c:\dev\ -otherwise it will not be found by the Build.PL on Windows. You may consider to hack xs\Build.PL with your prefered boost path. -run -bootstrap.bat mingw -b2 toolset=gcc - -Install git command line -https://git-scm.com/ - -Download Slic3r source code -git clone git://github.com/alexrj/Slic3r.git - -Run compilation -cd Slic3r -perl Build.PL -perl Build.PL --gui - -With a bit of luck, you will end up with a working Slic3r including GUI. - - - - -Packing on Windows ------------------- - -Life is easy on Windows. PAR::Packer will help again to collect the dependencies. The basic procedure is the same as for MacOS: - -To create a PAR archive of a command line slic3r, execute -pp -e -p -x slic3r.pl --xargs cube.stl -o slic3r.par -and let the slic3r slice a cube.stl to load the dynamic modules. - -To create a PAR archive of a GUI slic3r, execute -pp -e -p -x slic3r.pl --xargs --gui -o slic3r.par -and exercise the slic3r gui to load all modules. - -The standalone installation is then created from the PAR archive by renaming it into a zip and adding following binaries from c:\strawberry to the root of the extracted zip: -perl5.22.1.exe -perl522.dll -libgcc_s_sjlj-1.dll -libstdc++-6.dll -libwinpthread-1.dll - -The GUI build requires following DLLs in addition: -libglut-0_.dll -wxbase30u_gcc_custom.dll -wxmsw30u_adv_gcc_custom.dll -wxmsw30u_core_gcc_custom.dll -wxmsw30u_gl_gcc_custom.dll -wxmsw30u_html_gcc_custom.dll - -and the var directory with the icons needs to be copied to the destination directory. - -To run the slic3r, move the script\slic3r.pl one level up and create a following tiny windows batch in the root of the unpacked zip: -@perl5.22.1.exe slic3r.pl %* -A windows shortcut may be created for the GUI version. Instead of the perl.exe, it is better to use the wperl.exe to start the GUI Slic3r, because it does not open a text console. - -The strawberry perl is already rellocatable, which means that the perl interpreter will find the perl modules in the lib directory, -and Windows look up the missing DLLs in the directory of the executable, therefore no further rellocation effort is necessary. - - -Packing on Windows, a single EXE solution ------------------------------------------ - -One may try to create a PAR executable for command line slic3r: -pp -M Encode::Locale -M Moo -M Thread::Semaphore -M Slic3r::XS -M Unicode::Normalize -o slic3r.exe slic3r.pl - -One may as well create a PAR executable for Windows GUI: -pp -M Encode::Locale -M Moo -M Thread::Semaphore -M OpenGL -M Slic3r::XS -M Unicode::Normalize -M Wx -M Class::Accessor -M Wx::DND -M Wx::Grid -M Wx::Print -M Wx::Html -M Wx::GLCanvas -M Math::Trig -M threads -M threads::shared -M Thread::Queue -l C:\strawberry\perl\site\lib\auto\Wx\Wx.xs.dll -o -l C:\strawberry\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxbase30u_gcc_custom.dll -l C:\strawberry\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_core_gcc_custom.dll -l C:\strawberry\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_gl_gcc_custom.dll -l C:\strawberry\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_adv_gcc_custom.dll -l C:\strawberry\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_html_gcc_custom.dll -o slic3r.exe slic3r.pl - -Remember, that these executables will unpack into a temporary directory. The directory may be declared by setting an environment variable PAR_GLOBAL_TEMP. Otherwise the temporaries are unpacked into -C:\Users\xxx\AppData\Local\Temp\par-xxxxxx - - -Debugging the perl, debugging on Windows ----------------------------------------- - -It is possible to debug perl using the integrated debugger. The EPIC plugin for Eclipse works very well with an older eclipse-SDK-3.6.2. There is a catch though: The Perl debugger does not work correctly with multiple threads running under the Perl interpreter. If that happens, the EPIC plugin gets confused and the debugger stops working. The same happens with the Komodo IDE. - -Debugging the C++ code works fine using the latest Eclipse for C++ and the gdb of MinGW. The gdb packed with the Strawberry distribution does not contain the Python support, so pretty printing of the stl containers only works if another gdb build is used. The one of the QT installation works well. - -It is yet a bit more complicated. The Strawberry MINGW is compiled for a different C++ exception passing model (SJLJ) than the other MINGWs, so one cannot simply combine MINGWs on Windows. For an unknown reason the nice debugger of the QT Creator hangs when debugging the C++ compiled by the Strawberry MINGW. Mabe it is because of the different exception passing models. - -And to disable optimization of the C/C++ code, one has to manually modify Config_heavy.pl in the Perl central installation. The SLIC3R_DEBUG environment variable did not override all the -O2 and -O3 flags that the perl build adds the gcc execution line. From 96dd99c1f8f1e02791c821c4a05a25a44471e41e Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 27 Jun 2016 15:28:13 +0200 Subject: [PATCH 053/225] Documented the build process --- doc/How_to_build_Slic3r.txt | 273 ++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 doc/How_to_build_Slic3r.txt diff --git a/doc/How_to_build_Slic3r.txt b/doc/How_to_build_Slic3r.txt new file mode 100644 index 000000000..0c5c709b0 --- /dev/null +++ b/doc/How_to_build_Slic3r.txt @@ -0,0 +1,273 @@ +How to build Slic3r on Mac OS X 10.7 Lion 64bit +--------------------------------------------- +Vojtech Bubnik, 2016-04-26 + + +1) Install Mac OS X 10.7 Lion 64 bit with X Code +------------------------------------------------ + +One has to build the OSX Slic3r on a real Mac, either directly on the system, or on a virtualized OSX. On Mac, two commercial solutions are available to legally virtualize MacOS on MacOS: +http://www.parallels.com/eu/products/desktop/ +http://www.vmware.com/products/workstation/ + +Installation of a X Code on an OS X 10.7 Lion needs a bit of work. The latest X Code supported by the Lion on a Virtual Box is 4.21. The trouble is, the certificates of the X Code 4.21 installation package expired. One way to work around the certificate is to flatten the installation package by unpacking and repacking it: +pkgutil --expand Foobar.pkg foobar +pkgutil --flatten foobar barfoo.pkg + +The flattened package is available here: +\\rs.prusa\Development\Slic3r-Prusa\installxcode_421_lion_fixed.pkg + +This installer does not install the X Code directly. Instead, it installs another installer with a set of 47 pkg files. These files have their certificates expired as well. You will find the packages on your MacOS here: +/Applications/Install Xcode.app/Contents/Resources/Packages/ + +It is best to flatten them in a loop: +cd /Applications/Install\ Xcode.app/Contents/Resources/Packages/ +for f in *.pkg; do + pkgutil --expand $f /tmp/$f + rm -f $f + pkgutil --flatten /tmp/$f $f +done + +After that, you may finish the installation of Xcode by running +/Applications/Install\ Xcode.app + + +1b) Installing the Xcode on a newer system +------------------------------------------- +You will need to register as an Apple developer on +https://developer.apple.com/ +log in and download and install Xcode +https://developer.apple.com/downloads/ +You will likely need to download and install Xcode Command Line Tools, though the Xcode 4.1 came with the command line tools installed. + + +2) Prepare the development environment +-------------------------------------- + +Install the brew package manager: +http://brew.sh/ +The brew package manager requires the git command line tool. Normally the git tool is installed as part of the Xcode command line tools. +Copy and execute a command line from the top of http://brew.sh/ . It is possible, that the invocation of git fails because of some parameters the old git does not recognize. If so, invoke the git call manually. + +Compile the boost library using brew. Following line compiles a 64bit boost with both static and shared libraries. +brew install boost --universal + +Install dylibbundler tool. The dylibbundler tool serves to collect dependent dynamic libraries and fix their linkage. Execute +brew install dylibbundler + +3) Install perl +--------------- + +We don't want to distribute perl pre-installed on the Mac OS box. First, the system perl installation is not correct on some Mac OS versions, second it is not rellocatable. To compile a 64bit rellocatable perl, we use the perlbrew distribution. The perlbrew distribution installs into a user home directory and it allows switching between multiple versions of perl. +http://perlbrew.pl/ + +First install perlbrew +\curl -L http://install.perlbrew.pl | bash +Then compile the newest perl with the rellocatable @INC path and with multithreading enabled, execute following line: +perlbrew install --threads -Duserelocatableinc --switch perl-5.22.0 +The --switch parameter switches the active perl to the currently compiled one. +Available perl versions could be listed by calling +perlbrew available + +Initialize CPAN, install PAR and PAR::Packer modules +execute cpan command, from the cpan prompt, run +install App::cpanminus +install PAR +install PAR::Packer +quit + +4) Download and install Slic3r +------------------------------ + +git clone git://github.com/alexrj/Slic3r +cd Slic3r +perl Build.PL + +Now Slic3r shall be compiled. You may try to execute +perl slic3r.pl +to get a help screen, or +perl slic3r.pl some_model.stl +to have the model sliced. + +5) Download and compile the GUI libraries needed to execute Slic3r in GUI mode +------------------------------------------------------------------------------ + +For the current Slic3r 1.2.30 code base, set the environment variable SLIC3R_STATIC to link a static version of the boost library: +export SLIC3R_STATIC=1 + +then execute +perl Build.PL --gui +and keep your fingers crossed. The Build.PL script downloads and compiles the WxWidgets 3.0 through a Alien::Wx PERL package. The WxWidget shared libraries will land into +~/perl5/perlbrew/perls/perl-5.22.1/lib/site_perl/5.22.1/darwin-thread-multi-2level/Alien/wxWidgets/ + +On Maverics, we experienced following issue compiling WxPerl: +http://wiki.bolay.net/doku.php?id=acdsn:acdsn-a:mac + +Now you could run the GUI version of slic3r by calling +perl slic3r.pl --gui +If some dependency is missing, the MacOS system will let you know. + +6) Packing the Slic3r +--------------------- + +Perl is an operating system on its own. Many modules are shared among multiple applications and it is difficult to extract a stand-alone application from a perl installation manually. Fortunately, tools are available, which automate the process to some extent. One of the tools is the PAR::Packer. The PAR::Packer tool (pp executable) is able to create a standalone executable for a perl script. The standalone executable contains a PAR archive (a zip file) bundled with a perl interpreter. When executed, the bundled executable will decompress most of the PAR archive into a temp folder. Because of that, we will use the PAR::Packer to resolve and collect the dependencies, but we will create an installer manually. + +The PAR::Packer could analyze the dependencies by a statical analysis, or at a runtime. The statical analysis does not resolve the dynamically loaded modules. On the other side, the statical analysis is pessimistic, therefore it often collects unneeded packages. The dynamic analysis may miss some package, if not all branches of a code are executed. We will try to solely depend on the dynamic analysis to keep the installation size minimal. We may need to develop a protocol or an automatic UI tool to exercise as much as possible from the Slic3r GUI to pack the GUI version reliably. Once a reliable list of dependencies is collected, we may not need the PAR::Packer anymore. + +To create a PAR archive of a command line slic3r, execute +pp -e -p -x slic3r.pl --xargs cube.stl -o slic3r.par +and let the slic3r slice a cube.stl to load the dynamic modules. + +To create a PAR archive of a GUI slic3r, execute +pp -e -p -x slic3r.pl --xargs --gui -o slic3r.par +and exercise the slic3r gui to load all modules. + +Rename the slic3r.par file to slic3r.zip and decompress. Most of the code needed to execute Slic3r is there, only the perl executable is missing for the command line slic3r, and the WxWidgets shared libraries and the liblzma shared library are missing for the GUI version. + +7) Collecting the dependent shared libraries, making the link paths relative +---------------------------------------------------------------------------- + +We have linked Slic3r against a static boost library, therefore the command line slic3r is not dependent on any non-system shared library. The situation is different for the GUI slic3r, which links dynamically against WxWidgets and liblzma. + +The trick developed by Apple to allow rellocable shared libraries is to addres a shared library relatively to the path of the executable by encoding a special token @executable_path at the start of the path. Unfortunately the libraries requried by Slic3r are compiled with absolute paths. + +Once the slic3r.par archive is unpacked, one may list the native shared libraries by +find ./ -name '*.bundle' +and one may list the dependencies by running +otool -L somefile.bundle +Most of the dependencies point to system directores and these dependences are always fulfilled. Dependencies pointing to the WxWidget libraries need to be fixed. These have a form +~/perlbrew/perls/perl-5.22.1/lib/site_perl/5.22.1/darwin-thread-multi-2level/Alien/wxWidgets/osx_cocoa_3_0_2_uni/lib/libwx_*.dylib +and we need to replace them with +@executable_path/../Frameworks/libwx_*.dylib +Another dependency, which needs our attention is +/usr/local/Cellar/xz/5.2.2/lib/liblzma.5.dylib + +Fortunately, a tool dylibbundler was developed to address this problem. +First install dylibbundler by calling +brew dylibbundler + +For some installations, the dylibbundler tool sufficiently fixes all dependencies. Unfortunately, the WxWidgets installation is inconsistent in the versioning, therefore a certain clean-up is required. Namely, the WxWidgets libraries are compiled with the full build number in their file name. For example, the base library is built as libwx_baseu-3.0.0.2.0.dylib and a symlink is created libwx_baseu-3.0.dylib pointing to the full name. Then some of the Wx libraries link against the full name and some against the symlink, leading the dylibbundler to pack both. We solved the problem by whipping up a following script: +\\rs.prusa\Development\Slic3r-Prusa\How_to_build_on_MacOSX_Lion\fix_dependencies.sh + +call +slic3r_dependencies.sh --fix +to collect the shared libraries into Content/Frameworks and to fix their linkage. + +call +slic3r_dependencies.sh --show +to list dependent libraries in a sorted order. All the non-system dependencies shall start with @executable_path after the fix. + + + +8) Packing Slic3r into a dmg image using a bunch of scripts +----------------------------------------------------------- + +Instead of relying on the PAR::Packer to collect the dependencies, we have used PAR::Packer to extract the dependencies, we manually cleaned them up and created an installer script. +\\rs.prusa\Development\Slic3r-Prusa\How_to_build_on_MacOSX_Lion\Slic3r-Build-MacOS +First compile Slic3r, then call build_dmg.sh with a path to the Slic3r source tree as a parameter. +The script will collect all dependencies into Slic3r.app and it will create Slic3r.dmg. +If SLIC3R_GUI variable is defined, a GUI variant of Slic3r will be packed. + + + + +How to build on Windows +----------------------- + +The prefered perl distribution on MS Windows is the Strawberry Perl 5.22.1.3 (32bit) +http://strawberryperl.com/ + +Let it install into c:\strawberry +You may make a copy of the distribution. We recommend to make following copies: +For a release command line only build: c:\strawberry-minimal +For a release GUI build: c:\strawberry-minimal-gui +For a development build with debugging information: c:\strawberry-debug +and to make one of them active by making a directory junction: +mklink /d c:\Strawberry c:\Strawberry-debug + +Building boost: +Slic3r seems to have a trouble with the latest boost 1.60.0 on Windows. Please use 1.59. +Decompress it to +c:\dev\ +otherwise it will not be found by the Build.PL on Windows. You may consider to hack xs\Build.PL with your prefered boost path. +run +bootstrap.bat mingw +b2 toolset=gcc + +Install git command line +https://git-scm.com/ + +Download Slic3r source code +git clone git://github.com/alexrj/Slic3r.git + +Run compilation +cd Slic3r +perl Build.PL +perl Build.PL --gui + +With a bit of luck, you will end up with a working Slic3r including GUI. + + + + +Packing on Windows +------------------ + +Life is easy on Windows. PAR::Packer will help again to collect the dependencies. The basic procedure is the same as for MacOS: + +To create a PAR archive of a command line slic3r, execute +pp -e -p -x slic3r.pl --xargs cube.stl -o slic3r.par +and let the slic3r slice a cube.stl to load the dynamic modules. + +To create a PAR archive of a GUI slic3r, execute +pp -e -p -x slic3r.pl --xargs --gui -o slic3r.par +and exercise the slic3r gui to load all modules. + +The standalone installation is then created from the PAR archive by renaming it into a zip and adding following binaries from c:\strawberry to the root of the extracted zip: +perl5.22.1.exe +perl522.dll +libgcc_s_sjlj-1.dll +libstdc++-6.dll +libwinpthread-1.dll + +The GUI build requires following DLLs in addition: +libglut-0_.dll +wxbase30u_gcc_custom.dll +wxmsw30u_adv_gcc_custom.dll +wxmsw30u_core_gcc_custom.dll +wxmsw30u_gl_gcc_custom.dll +wxmsw30u_html_gcc_custom.dll + +and the var directory with the icons needs to be copied to the destination directory. + +To run the slic3r, move the script\slic3r.pl one level up and create a following tiny windows batch in the root of the unpacked zip: +@perl5.22.1.exe slic3r.pl %* +A windows shortcut may be created for the GUI version. Instead of the perl.exe, it is better to use the wperl.exe to start the GUI Slic3r, because it does not open a text console. + +The strawberry perl is already rellocatable, which means that the perl interpreter will find the perl modules in the lib directory, +and Windows look up the missing DLLs in the directory of the executable, therefore no further rellocation effort is necessary. + + +Packing on Windows, a single EXE solution +----------------------------------------- + +One may try to create a PAR executable for command line slic3r: +pp -M Encode::Locale -M Moo -M Thread::Semaphore -M Slic3r::XS -M Unicode::Normalize -o slic3r.exe slic3r.pl + +One may as well create a PAR executable for Windows GUI: +pp -M Encode::Locale -M Moo -M Thread::Semaphore -M OpenGL -M Slic3r::XS -M Unicode::Normalize -M Wx -M Class::Accessor -M Wx::DND -M Wx::Grid -M Wx::Print -M Wx::Html -M Wx::GLCanvas -M Math::Trig -M threads -M threads::shared -M Thread::Queue -l C:\strawberry\perl\site\lib\auto\Wx\Wx.xs.dll -o -l C:\strawberry\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxbase30u_gcc_custom.dll -l C:\strawberry\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_core_gcc_custom.dll -l C:\strawberry\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_gl_gcc_custom.dll -l C:\strawberry\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_adv_gcc_custom.dll -l C:\strawberry\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_html_gcc_custom.dll -o slic3r.exe slic3r.pl + +Remember, that these executables will unpack into a temporary directory. The directory may be declared by setting an environment variable PAR_GLOBAL_TEMP. Otherwise the temporaries are unpacked into +C:\Users\xxx\AppData\Local\Temp\par-xxxxxx + + +Debugging the perl, debugging on Windows +---------------------------------------- + +It is possible to debug perl using the integrated debugger. The EPIC plugin for Eclipse works very well with an older eclipse-SDK-3.6.2. There is a catch though: The Perl debugger does not work correctly with multiple threads running under the Perl interpreter. If that happens, the EPIC plugin gets confused and the debugger stops working. The same happens with the Komodo IDE. + +Debugging the C++ code works fine using the latest Eclipse for C++ and the gdb of MinGW. The gdb packed with the Strawberry distribution does not contain the Python support, so pretty printing of the stl containers only works if another gdb build is used. The one of the QT installation works well. + +It is yet a bit more complicated. The Strawberry MINGW is compiled for a different C++ exception passing model (SJLJ) than the other MINGWs, so one cannot simply combine MINGWs on Windows. For an unknown reason the nice debugger of the QT Creator hangs when debugging the C++ compiled by the Strawberry MINGW. Mabe it is because of the different exception passing models. + +And to disable optimization of the C/C++ code, one has to manually modify Config_heavy.pl in the Perl central installation. The SLIC3R_DEBUG environment variable did not override all the -O2 and -O3 flags that the perl build adds the gcc execution line. From 08287e76e57579ac11bebdf57d0984b7fd056297 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 21 Aug 2016 19:09:31 +0200 Subject: [PATCH 054/225] Fixed a crash in a constructor of FullPrintConfig due to an incorrect use of virtual inheritance. Note that an invocation of ConfigBase::optptr() is routed to FullPrintConfig::optptr() for all classes of the FullPrintConfig hierarchy. FullPrintConfig::optptr() in turn invokes optptr() of PrintObjectConfig, PrintRegionConfig, PrintConfig and HostConfig. Due to the use of virtual inheritance, this all happens, when PrintObjectConfig gets constructed as part of FullPrintConfig, but at that time PrintRegionConfig, PrintConfig and HostConfig are not constructed yet. Accessing them at that time leads to crashes, when compiled with Visual Studio 2013 compiler. For some reason the code generated by gcc does not crash, but I believe the behavior is undefined and it is better to be fixed anyway. The patch solves the problem by calling set_defaults() by the topmost object, which not only fixes the crashes, but also avoids repeated initialization. Conflicts: xs/src/libslic3r/PrintConfig.hpp --- xs/src/libslic3r/PrintConfig.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index ef5af261e..8d86bddfe 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -163,7 +163,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig if (initialize) this->set_defaults(); } - + virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(dont_support_bridges); OPT_PTR(extrusion_width); @@ -234,7 +234,7 @@ class PrintRegionConfig : public virtual StaticPrintConfig if (initialize) this->set_defaults(); } - + virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(bottom_solid_layers); OPT_PTR(bridge_flow_ratio); @@ -409,7 +409,7 @@ class PrintConfig : public GCodeConfig if (initialize) this->set_defaults(); } - + virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(avoid_crossing_perimeters); OPT_PTR(bed_shape); @@ -482,7 +482,7 @@ class HostConfig : public virtual StaticPrintConfig if (initialize) this->set_defaults(); } - + virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(octoprint_host); OPT_PTR(octoprint_apikey); From f3e0efa4fe047dd305a75325b9d24f2dc6a1210d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 27 Mar 2016 10:53:59 +0200 Subject: [PATCH 055/225] Debugging visualization of the gap fills into a SVG format, if SLIC3R_DEBUG is set. --- xs/src/libslic3r/Geometry.cpp | 481 ++++++++++++++++++++++++++++++++++ xs/src/libslic3r/Geometry.hpp | 8 +- xs/src/libslic3r/SVG.cpp | 90 +++++-- xs/src/libslic3r/SVG.hpp | 21 +- 4 files changed, 573 insertions(+), 27 deletions(-) diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp index fcf39f705..d988a9726 100644 --- a/xs/src/libslic3r/Geometry.cpp +++ b/xs/src/libslic3r/Geometry.cpp @@ -11,12 +11,186 @@ #include #include #include +#include #include #ifdef SLIC3R_DEBUG #include "SVG.hpp" #endif +#ifdef SLIC3R_DEBUG +namespace boost { namespace polygon { + +// The following code for the visualization of the boost Voronoi diagram is based on: +// +// Boost.Polygon library voronoi_graphic_utils.hpp header file +// Copyright Andrii Sydorchuk 2010-2012. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +template +class voronoi_visual_utils { + public: + // Discretize parabolic Voronoi edge. + // Parabolic Voronoi edges are always formed by one point and one segment + // from the initial input set. + // + // Args: + // point: input point. + // segment: input segment. + // max_dist: maximum discretization distance. + // discretization: point discretization of the given Voronoi edge. + // + // Template arguments: + // InCT: coordinate type of the input geometries (usually integer). + // Point: point type, should model point concept. + // Segment: segment type, should model segment concept. + // + // Important: + // discretization should contain both edge endpoints initially. + template class Point, + template class Segment> + static + typename enable_if< + typename gtl_and< + typename gtl_if< + typename is_point_concept< + typename geometry_concept< Point >::type + >::type + >::type, + typename gtl_if< + typename is_segment_concept< + typename geometry_concept< Segment >::type + >::type + >::type + >::type, + void + >::type discretize( + const Point& point, + const Segment& segment, + const CT max_dist, + std::vector< Point >* discretization) { + // Apply the linear transformation to move start point of the segment to + // the point with coordinates (0, 0) and the direction of the segment to + // coincide the positive direction of the x-axis. + CT segm_vec_x = cast(x(high(segment))) - cast(x(low(segment))); + CT segm_vec_y = cast(y(high(segment))) - cast(y(low(segment))); + CT sqr_segment_length = segm_vec_x * segm_vec_x + segm_vec_y * segm_vec_y; + + // Compute x-coordinates of the endpoints of the edge + // in the transformed space. + CT projection_start = sqr_segment_length * + get_point_projection((*discretization)[0], segment); + CT projection_end = sqr_segment_length * + get_point_projection((*discretization)[1], segment); + + // Compute parabola parameters in the transformed space. + // Parabola has next representation: + // f(x) = ((x-rot_x)^2 + rot_y^2) / (2.0*rot_y). + CT point_vec_x = cast(x(point)) - cast(x(low(segment))); + CT point_vec_y = cast(y(point)) - cast(y(low(segment))); + CT rot_x = segm_vec_x * point_vec_x + segm_vec_y * point_vec_y; + CT rot_y = segm_vec_x * point_vec_y - segm_vec_y * point_vec_x; + + // Save the last point. + Point last_point = (*discretization)[1]; + discretization->pop_back(); + + // Use stack to avoid recursion. + std::stack point_stack; + point_stack.push(projection_end); + CT cur_x = projection_start; + CT cur_y = parabola_y(cur_x, rot_x, rot_y); + + // Adjust max_dist parameter in the transformed space. + const CT max_dist_transformed = max_dist * max_dist * sqr_segment_length; + while (!point_stack.empty()) { + CT new_x = point_stack.top(); + CT new_y = parabola_y(new_x, rot_x, rot_y); + + // Compute coordinates of the point of the parabola that is + // furthest from the current line segment. + CT mid_x = (new_y - cur_y) / (new_x - cur_x) * rot_y + rot_x; + CT mid_y = parabola_y(mid_x, rot_x, rot_y); + + // Compute maximum distance between the given parabolic arc + // and line segment that discretize it. + CT dist = (new_y - cur_y) * (mid_x - cur_x) - + (new_x - cur_x) * (mid_y - cur_y); + dist = dist * dist / ((new_y - cur_y) * (new_y - cur_y) + + (new_x - cur_x) * (new_x - cur_x)); + if (dist <= max_dist_transformed) { + // Distance between parabola and line segment is less than max_dist. + point_stack.pop(); + CT inter_x = (segm_vec_x * new_x - segm_vec_y * new_y) / + sqr_segment_length + cast(x(low(segment))); + CT inter_y = (segm_vec_x * new_y + segm_vec_y * new_x) / + sqr_segment_length + cast(y(low(segment))); + discretization->push_back(Point(inter_x, inter_y)); + cur_x = new_x; + cur_y = new_y; + } else { + point_stack.push(mid_x); + } + } + + // Update last point. + discretization->back() = last_point; + } + + private: + // Compute y(x) = ((x - a) * (x - a) + b * b) / (2 * b). + static CT parabola_y(CT x, CT a, CT b) { + return ((x - a) * (x - a) + b * b) / (b + b); + } + + // Get normalized length of the distance between: + // 1) point projection onto the segment + // 2) start point of the segment + // Return this length divided by the segment length. This is made to avoid + // sqrt computation during transformation from the initial space to the + // transformed one and vice versa. The assumption is made that projection of + // the point lies between the start-point and endpoint of the segment. + template class Point, + template class Segment> + static + typename enable_if< + typename gtl_and< + typename gtl_if< + typename is_point_concept< + typename geometry_concept< Point >::type + >::type + >::type, + typename gtl_if< + typename is_segment_concept< + typename geometry_concept< Segment >::type + >::type + >::type + >::type, + CT + >::type get_point_projection( + const Point& point, const Segment& segment) { + CT segment_vec_x = cast(x(high(segment))) - cast(x(low(segment))); + CT segment_vec_y = cast(y(high(segment))) - cast(y(low(segment))); + CT point_vec_x = x(point) - cast(x(low(segment))); + CT point_vec_y = y(point) - cast(y(low(segment))); + CT sqr_segment_length = + segment_vec_x * segment_vec_x + segment_vec_y * segment_vec_y; + CT vec_dot = segment_vec_x * point_vec_x + segment_vec_y * point_vec_y; + return vec_dot / sqr_segment_length; + } + + template + static CT cast(const InCT& value) { + return static_cast(value); + } +}; + +} } // namespace boost::polygon +#endif + using namespace boost::polygon; // provides also high() and low() namespace Slic3r { namespace Geometry { @@ -276,6 +450,294 @@ arrange(size_t total_parts, Pointf part, coordf_t dist, const BoundingBoxf* bb) return positions; } +#ifdef SLIC3R_DEBUG +// The following code for the visualization of the boost Voronoi diagram is based on: +// +// Boost.Polygon library voronoi_visualizer.cpp file +// Copyright Andrii Sydorchuk 2010-2012. +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +namespace Voronoi { namespace Internal { + + typedef double coordinate_type; + typedef boost::polygon::point_data point_type; + typedef boost::polygon::segment_data segment_type; + typedef boost::polygon::rectangle_data rect_type; +// typedef voronoi_builder VB; + typedef boost::polygon::voronoi_diagram VD; + typedef VD::cell_type cell_type; + typedef VD::cell_type::source_index_type source_index_type; + typedef VD::cell_type::source_category_type source_category_type; + typedef VD::edge_type edge_type; + typedef VD::cell_container_type cell_container_type; + typedef VD::cell_container_type vertex_container_type; + typedef VD::edge_container_type edge_container_type; + typedef VD::const_cell_iterator const_cell_iterator; + typedef VD::const_vertex_iterator const_vertex_iterator; + typedef VD::const_edge_iterator const_edge_iterator; + + static const std::size_t EXTERNAL_COLOR = 1; + + inline void color_exterior(const VD::edge_type* edge) + { + if (edge->color() == EXTERNAL_COLOR) + return; + edge->color(EXTERNAL_COLOR); + edge->twin()->color(EXTERNAL_COLOR); + const VD::vertex_type* v = edge->vertex1(); + if (v == NULL || !edge->is_primary()) + return; + v->color(EXTERNAL_COLOR); + const VD::edge_type* e = v->incident_edge(); + do { + color_exterior(e); + e = e->rot_next(); + } while (e != v->incident_edge()); + } + + inline point_type retrieve_point(const std::vector &segments, const cell_type& cell) + { + assert(cell.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT || cell.source_category() == SOURCE_CATEGORY_SEGMENT_END_POINT); + return (cell.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT) ? low(segments[cell.source_index()]) : high(segments[cell.source_index()]); + } + + inline void clip_infinite_edge(const std::vector &segments, const edge_type& edge, coordinate_type bbox_max_size, std::vector* clipped_edge) + { + const cell_type& cell1 = *edge.cell(); + const cell_type& cell2 = *edge.twin()->cell(); + point_type origin, direction; + // Infinite edges could not be created by two segment sites. + if (cell1.contains_point() && cell2.contains_point()) { + point_type p1 = retrieve_point(segments, cell1); + point_type p2 = retrieve_point(segments, cell2); + origin.x((p1.x() + p2.x()) * 0.5); + origin.y((p1.y() + p2.y()) * 0.5); + direction.x(p1.y() - p2.y()); + direction.y(p2.x() - p1.x()); + } else { + origin = cell1.contains_segment() ? retrieve_point(segments, cell2) : retrieve_point(segments, cell1); + segment_type segment = cell1.contains_segment() ? segments[cell1.source_index()] : segments[cell2.source_index()]; + coordinate_type dx = high(segment).x() - low(segment).x(); + coordinate_type dy = high(segment).y() - low(segment).y(); + if ((low(segment) == origin) ^ cell1.contains_point()) { + direction.x(dy); + direction.y(-dx); + } else { + direction.x(-dy); + direction.y(dx); + } + } + coordinate_type koef = bbox_max_size / (std::max)(fabs(direction.x()), fabs(direction.y())); + if (edge.vertex0() == NULL) { + clipped_edge->push_back(point_type( + origin.x() - direction.x() * koef, + origin.y() - direction.y() * koef)); + } else { + clipped_edge->push_back( + point_type(edge.vertex0()->x(), edge.vertex0()->y())); + } + if (edge.vertex1() == NULL) { + clipped_edge->push_back(point_type( + origin.x() + direction.x() * koef, + origin.y() + direction.y() * koef)); + } else { + clipped_edge->push_back( + point_type(edge.vertex1()->x(), edge.vertex1()->y())); + } + } + + inline void sample_curved_edge(const std::vector &segments, const edge_type& edge, std::vector &sampled_edge, coordinate_type max_dist) + { + point_type point = edge.cell()->contains_point() ? + retrieve_point(segments, *edge.cell()) : + retrieve_point(segments, *edge.twin()->cell()); + segment_type segment = edge.cell()->contains_point() ? + segments[edge.twin()->cell()->source_index()] : + segments[edge.cell()->source_index()]; + ::boost::polygon::voronoi_visual_utils::discretize(point, segment, max_dist, &sampled_edge); + } + +} /* namespace Internal */ } // namespace Voronoi + +static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_diagram &vd, const ThickPolylines *polylines, const char *path) +{ + const double scale = 0.2; + const std::string inputSegmentPointColor = "lightseagreen"; + const coord_t inputSegmentPointRadius = coord_t(0.09 * scale / SCALING_FACTOR); + const std::string inputSegmentColor = "lightseagreen"; + const coord_t inputSegmentLineWidth = coord_t(0.03 * scale / SCALING_FACTOR); + + const std::string voronoiPointColor = "black"; + const coord_t voronoiPointRadius = coord_t(0.06 * scale / SCALING_FACTOR); + const std::string voronoiLineColorPrimary = "black"; + const std::string voronoiLineColorSecondary = "green"; + const std::string voronoiArcColor = "red"; + const coord_t voronoiLineWidth = coord_t(0.02 * scale / SCALING_FACTOR); + + const bool internalEdgesOnly = false; + const bool primaryEdgesOnly = false; + + BoundingBox bbox = BoundingBox(lines); + bbox.min.x -= coord_t(1. / SCALING_FACTOR); + bbox.min.y -= coord_t(1. / SCALING_FACTOR); + bbox.max.x += coord_t(1. / SCALING_FACTOR); + bbox.max.y += coord_t(1. / SCALING_FACTOR); + + ::Slic3r::SVG svg(path, bbox); + + if (polylines != NULL) + svg.draw(*polylines, "lime", "lime", voronoiLineWidth); + +// bbox.scale(1.2); + // For clipping of half-lines to some reasonable value. + // The line will then be clipped by the SVG viewer anyway. + const double bbox_dim_max = double(bbox.max.x - bbox.min.x) + double(bbox.max.y - bbox.min.y); + // For the discretization of the Voronoi parabolic segments. + const double discretization_step = 0.0005 * bbox_dim_max; + + // Make a copy of the input segments with the double type. + std::vector segments; + for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++ it) + segments.push_back(Voronoi::Internal::segment_type( + Voronoi::Internal::point_type(double(it->a.x), double(it->a.y)), + Voronoi::Internal::point_type(double(it->b.x), double(it->b.y)))); + + // Color exterior edges. + for (voronoi_diagram::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it) + if (!it->is_finite()) + Voronoi::Internal::color_exterior(&(*it)); + + // Draw the end points of the input polygon. + for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) { + svg.draw(it->a, inputSegmentPointColor, inputSegmentPointRadius); + svg.draw(it->b, inputSegmentPointColor, inputSegmentPointRadius); + } + // Draw the input polygon. + for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) + svg.draw(Line(Point(coord_t(it->a.x), coord_t(it->a.y)), Point(coord_t(it->b.x), coord_t(it->b.y))), inputSegmentColor, inputSegmentLineWidth); + +#if 1 + // Draw voronoi vertices. + for (voronoi_diagram::const_vertex_iterator it = vd.vertices().begin(); it != vd.vertices().end(); ++it) + if (! internalEdgesOnly || it->color() != Voronoi::Internal::EXTERNAL_COLOR) + svg.draw(Point(coord_t(it->x()), coord_t(it->y())), voronoiPointColor, voronoiPointRadius); + + for (voronoi_diagram::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it) { + if (primaryEdgesOnly && !it->is_primary()) + continue; + if (internalEdgesOnly && (it->color() == Voronoi::Internal::EXTERNAL_COLOR)) + continue; + std::vector samples; + std::string color = voronoiLineColorPrimary; + if (!it->is_finite()) { + Voronoi::Internal::clip_infinite_edge(segments, *it, bbox_dim_max, &samples); + if (! it->is_primary()) + color = voronoiLineColorSecondary; + } else { + // Store both points of the segment into samples. sample_curved_edge will split the initial line + // until the discretization_step is reached. + samples.push_back(Voronoi::Internal::point_type(it->vertex0()->x(), it->vertex0()->y())); + samples.push_back(Voronoi::Internal::point_type(it->vertex1()->x(), it->vertex1()->y())); + if (it->is_curved()) { + Voronoi::Internal::sample_curved_edge(segments, *it, samples, discretization_step); + color = voronoiArcColor; + } else if (! it->is_primary()) + color = voronoiLineColorSecondary; + } + for (std::size_t i = 0; i + 1 < samples.size(); ++i) + svg.draw(Line(Point(coord_t(samples[i].x()), coord_t(samples[i].y())), Point(coord_t(samples[i+1].x()), coord_t(samples[i+1].y()))), color, voronoiLineWidth); + } +#endif + + if (polylines != NULL) + svg.draw(*polylines, "blue", voronoiLineWidth); + + svg.Close(); +} +#endif /* SLIC3R_DEBUG */ + +// Euclidian distance of two boost::polygon points. +template +T dist(const boost::polygon::point_data &p1,const boost::polygon::point_data &p2) +{ + T dx = p2.x() - p1.x(); + T dy = p2.y() - p1.y(); + return sqrt(dx*dx+dy*dy); +} + +// Find a foot point of "px" on a segment "seg". +template +inline point_type project_point_to_segment(segment_type &seg, point_type &px) +{ + typedef typename point_type::coordinate_type T; + const point_type &p0 = low(seg); + const point_type &p1 = high(seg); + const point_type dir(p1.x()-p0.x(), p1.y()-p0.y()); + const point_type dproj(px.x()-p0.x(), px.y()-p0.y()); + const T t = (dir.x()*dproj.x() + dir.y()*dproj.y()) / (dir.x()*dir.x() + dir.y()*dir.y()); + assert(t >= T(-1e-6) && t <= T(1. + 1e-6)); + return point_type(p0.x() + t*dir.x(), p0.y() + t*dir.y()); +} + +template +inline const typename VD::point_type retrieve_cell_point(const typename VD::cell_type& cell, const SEGMENTS &segments) +{ + assert(cell.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT || cell.source_category() == SOURCE_CATEGORY_SEGMENT_END_POINT); + return (cell.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT) ? low(segments[cell.source_index()]) : high(segments[cell.source_index()]); +} + +template +inline std::pair +measure_edge_thickness(const VD &vd, const typename VD::edge_type& edge, const SEGMENTS &segments) +{ + typedef typename VD::coord_type T; + const typename VD::point_type pa(edge.vertex0()->x(), edge.vertex0()->y()); + const typename VD::point_type pb(edge.vertex1()->x(), edge.vertex1()->y()); + const typename VD::cell_type &cell1 = *edge.cell(); + const typename VD::cell_type &cell2 = *edge.twin()->cell(); + if (cell1.contains_segment()) { + if (cell2.contains_segment()) { + // Both cells contain a linear segment, the left / right cells are symmetric. + // Project pa, pb to the left segment. + const typename VD::segment_type segment1 = segments[cell1.source_index()]; + const typename VD::point_type p1a = project_point_to_segment(segment1, pa); + const typename VD::point_type p1b = project_point_to_segment(segment1, pb); + return std::pair(T(2.)*dist(pa, p1a), T(2.)*dist(pb, p1b)); + } else { + // 1st cell contains a linear segment, 2nd cell contains a point. + // The medial axis between the cells is a parabolic arc. + // Project pa, pb to the left segment. + const typename VD::point_type p2 = retrieve_cell_point(cell2, segments); + return std::pair(T(2.)*dist(pa, p2), T(2.)*dist(pb, p2)); + } + } else if (cell2.contains_segment()) { + // 1st cell contains a point, 2nd cell contains a linear segment. + // The medial axis between the cells is a parabolic arc. + const typename VD::point_type p1 = retrieve_cell_point(cell1, segments); + return std::pair(T(2.)*dist(pa, p1), T(2.)*dist(pb, p1)); + } else { + // Both cells contain a point. The left / right regions are triangular and symmetric. + const typename VD::point_type p1 = retrieve_cell_point(cell1, segments); + return std::pair(T(2.)*dist(pa, p1), T(2.)*dist(pb, p1)); + } +} + +// Converts the Line instances of Lines vector to VD::segment_type. +template +class Lines2VDSegments +{ +public: + Lines2VDSegments(const Lines &alines) : lines(alines) {} + typename VD::segment_type operator[](size_t idx) const { + return typename VD::segment_type( + typename VD::point_type(typename VD::coord_type(lines[idx].a.x), typename VD::coord_type(lines[idx].a.y)), + typename VD::point_type(typename VD::coord_type(lines[idx].b.x), typename VD::coord_type(lines[idx].b.y))); + } +private: + const Lines &lines; +}; + void MedialAxis::build(ThickPolylines* polylines) { @@ -359,6 +821,25 @@ MedialAxis::build(ThickPolylines* polylines) // append polyline to result polylines->push_back(polyline); } + + #ifdef SLIC3R_DEBUG + { + char path[2048]; + static int iRun = 0; + sprintf(path, "out/MedialAxis-%d.svg", iRun ++); + dump_voronoi_to_svg(this->lines, this->vd, polylines, path); + + + printf("Thick lines: "); + for (ThickPolylines::const_iterator it = polylines->begin(); it != polylines->end(); ++ it) { + ThickLines lines = it->thicklines(); + for (ThickLines::const_iterator it2 = lines.begin(); it2 != lines.end(); ++ it2) { + printf("%f,%f ", it2->a_width, it2->b_width); + } + } + printf("\n"); + } + #endif /* SLIC3R_DEBUG */ } void diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp index 724279f4b..ccdfb5b2e 100644 --- a/xs/src/libslic3r/Geometry.hpp +++ b/xs/src/libslic3r/Geometry.hpp @@ -51,7 +51,13 @@ class MedialAxis { void build(Polylines* polylines); private: - typedef voronoi_diagram VD; + class VD : public voronoi_diagram { + public: + typedef double coord_type; + typedef boost::polygon::point_data point_type; + typedef boost::polygon::segment_data segment_type; + typedef boost::polygon::rectangle_data rect_type; + }; VD vd; std::set edges, valid_edges; std::map > thickness; diff --git a/xs/src/libslic3r/SVG.cpp b/xs/src/libslic3r/SVG.cpp index af367ae76..444810044 100644 --- a/xs/src/libslic3r/SVG.cpp +++ b/xs/src/libslic3r/SVG.cpp @@ -19,18 +19,54 @@ SVG::SVG(const char* filename) ); } +SVG::SVG(const char* filename, const BoundingBox &bbox) + : arrows(false), fill("grey"), stroke("black"), filename(filename), origin(bbox.min) +{ + this->f = fopen(filename, "w"); + float w = COORD(bbox.max.x - bbox.min.x); + float h = COORD(bbox.max.y - bbox.min.y); + fprintf(this->f, + "\n" + "\n" + "\n" + " \n" + " \n" + " \n", + h, w); +} + void -SVG::draw(const Line &line, std::string stroke) +SVG::draw(const Line &line, std::string stroke, coord_t stroke_width) { fprintf(this->f, - " arrows) fprintf(this->f, " marker-end=\"url(#endArrow)\""); fprintf(this->f, "/>\n"); } +void SVG::draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coord_t stroke_width) +{ + Pointf dir(line.b.x-line.a.x, line.b.y-line.a.y); + Pointf perp(-dir.y, dir.x); + coordf_t len = sqrt(perp.x*perp.x + perp.y*perp.y); + coordf_t da = coordf_t(0.5)*line.a_width/len; + coordf_t db = coordf_t(0.5)*line.b_width/len; + fprintf(this->f, + " \n", + COORD(line.a.x-da*perp.x-origin.x), + COORD(line.a.y-da*perp.y-origin.y), + COORD(line.b.x-db*perp.x-origin.x), + COORD(line.b.y-db*perp.y-origin.y), + COORD(line.b.x+db*perp.x-origin.x), + COORD(line.b.y+db*perp.y-origin.y), + COORD(line.a.x+da*perp.x-origin.x), + COORD(line.a.y+da*perp.y-origin.y), + fill.c_str(), stroke.c_str(), + (stroke_width == 0) ? 1.f : COORD(stroke_width)); +} + void SVG::draw(const Lines &lines, std::string stroke) { @@ -80,31 +116,45 @@ SVG::draw(const Polygons &polygons, std::string fill) } void -SVG::draw(const Polyline &polyline, std::string stroke) +SVG::draw(const Polyline &polyline, std::string stroke, coord_t stroke_width) { this->stroke = stroke; - this->path(this->get_path_d(polyline, false), false); + this->path(this->get_path_d(polyline, false), false, stroke_width); } void -SVG::draw(const Polylines &polylines, std::string stroke) +SVG::draw(const Polylines &polylines, std::string stroke, coord_t stroke_width) { for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) - this->draw(*it, stroke); + this->draw(*it, fill, stroke_width); +} + +void SVG::draw(const ThickLines &thicklines, const std::string &fill, const std::string &stroke, coord_t stroke_width) +{ + for (ThickLines::const_iterator it = thicklines.begin(); it != thicklines.end(); ++it) + this->draw(*it, fill, stroke, stroke_width); } void -SVG::draw(const ThickPolylines &polylines, std::string stroke) +SVG::draw(const ThickPolylines &polylines, const std::string &stroke, coord_t stroke_width) { for (ThickPolylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) - this->draw((Polyline)*it, stroke); + this->draw((Polyline)*it, stroke, stroke_width); +} + +void +SVG::draw(const ThickPolylines &thickpolylines, const std::string &fill, const std::string &stroke, coord_t stroke_width) +{ + for (ThickPolylines::const_iterator it = thickpolylines.begin(); it != thickpolylines.end(); ++ it) + draw(it->thicklines(), fill, stroke, stroke_width); } void -SVG::draw(const Point &point, std::string fill, unsigned int radius) +SVG::draw(const Point &point, std::string fill, coord_t iradius) { + float radius = (iradius == 0) ? 3.f : COORD(iradius); std::ostringstream svg; - svg << " "; @@ -112,22 +162,26 @@ SVG::draw(const Point &point, std::string fill, unsigned int radius) } void -SVG::draw(const Points &points, std::string fill, unsigned int radius) +SVG::draw(const Points &points, std::string fill, coord_t radius) { for (Points::const_iterator it = points.begin(); it != points.end(); ++it) this->draw(*it, fill, radius); } void -SVG::path(const std::string &d, bool fill) +SVG::path(const std::string &d, bool fill, coord_t stroke_width) { + float lineWidth = 0.f; + if (! fill) + lineWidth = (stroke_width == 0) ? 2.f : COORD(stroke_width); + fprintf( this->f, - " \n", + " \n", d.c_str(), fill ? this->fill.c_str() : "none", this->stroke.c_str(), - fill ? "0" : "2", + lineWidth, (this->arrows && !fill) ? " marker-end=\"url(#endArrow)\"" : "" ); } @@ -138,8 +192,8 @@ SVG::get_path_d(const MultiPoint &mp, bool closed) const std::ostringstream d; d << "M "; for (Points::const_iterator p = mp.points.begin(); p != mp.points.end(); ++p) { - d << COORD(p->x) << " "; - d << COORD(p->y) << " "; + d << COORD(p->x - origin.x) << " "; + d << COORD(p->y - origin.y) << " "; } if (closed) d << "z"; return d.str(); diff --git a/xs/src/libslic3r/SVG.hpp b/xs/src/libslic3r/SVG.hpp index f0ae06d04..94cfc27dd 100644 --- a/xs/src/libslic3r/SVG.hpp +++ b/xs/src/libslic3r/SVG.hpp @@ -13,27 +13,32 @@ class SVG public: bool arrows; std::string fill, stroke; - + Point origin; + SVG(const char* filename); - void draw(const Line &line, std::string stroke = "black"); + SVG(const char* filename, const BoundingBox &bbox); + void draw(const Line &line, std::string stroke = "black", coord_t stroke_width = 0); + void draw(const ThickLine &line, const std::string &fill, const std::string &stroke, coord_t stroke_width = 0); void draw(const Lines &lines, std::string stroke = "black"); void draw(const IntersectionLines &lines, std::string stroke = "black"); void draw(const ExPolygon &expolygon, std::string fill = "grey"); void draw(const ExPolygons &expolygons, std::string fill = "grey"); void draw(const Polygon &polygon, std::string fill = "grey"); void draw(const Polygons &polygons, std::string fill = "grey"); - void draw(const Polyline &polyline, std::string stroke = "black"); - void draw(const Polylines &polylines, std::string stroke = "black"); - void draw(const ThickPolylines &polylines, std::string stroke = "black"); - void draw(const Point &point, std::string fill = "black", unsigned int radius = 3); - void draw(const Points &points, std::string fill = "black", unsigned int radius = 3); + void draw(const Polyline &polyline, std::string stroke = "black", coord_t stroke_width = 0); + void draw(const Polylines &polylines, std::string stroke = "black", coord_t stroke_width = 0); + void draw(const ThickLines &thicklines, const std::string &fill = "lime", const std::string &stroke = "black", coord_t stroke_width = 0); + void draw(const ThickPolylines &polylines, const std::string &stroke = "black", coord_t stroke_width = 0); + void draw(const ThickPolylines &thickpolylines, const std::string &fill, const std::string &stroke, coord_t stroke_width); + void draw(const Point &point, std::string fill = "black", coord_t radius = 0); + void draw(const Points &points, std::string fill = "black", coord_t radius = 0); void Close(); private: std::string filename; FILE* f; - void path(const std::string &d, bool fill); + void path(const std::string &d, bool fill, coord_t stroke_width = 0); std::string get_path_d(const MultiPoint &mp, bool closed = false) const; }; From 9a91704b8d14770935aa7fc3e5d23fe3ce0e0c2b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 10 Apr 2016 19:02:00 +0200 Subject: [PATCH 056/225] New constructors for the bounding box with min/max points were added. empty(bbox) function template was added. --- xs/src/libslic3r/BoundingBox.hpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp index 3fd6008a5..f5260ee5f 100644 --- a/xs/src/libslic3r/BoundingBox.hpp +++ b/xs/src/libslic3r/BoundingBox.hpp @@ -21,6 +21,7 @@ class BoundingBoxBase bool defined; BoundingBoxBase() : defined(false) {}; + BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) : min(pmin), max(pmax) {} BoundingBoxBase(const std::vector &points); void merge(const PointClass &point); void merge(const std::vector &points); @@ -38,6 +39,7 @@ class BoundingBox3Base : public BoundingBoxBase { public: BoundingBox3Base() : BoundingBoxBase() {}; + BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) : BoundingBoxBase(pmin, pmax) {} BoundingBox3Base(const std::vector &points); void merge(const PointClass &point); void merge(const std::vector &points); @@ -55,6 +57,7 @@ class BoundingBox : public BoundingBoxBase Polygon polygon() const; BoundingBox() : BoundingBoxBase() {}; + BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase(pmin, pmax) {}; BoundingBox(const Points &points) : BoundingBoxBase(points) {}; BoundingBox(const Lines &lines); }; @@ -66,15 +69,39 @@ class BoundingBox3 : public BoundingBox3Base {}; class BoundingBoxf : public BoundingBoxBase { public: BoundingBoxf() : BoundingBoxBase() {}; + BoundingBoxf(const Pointf &pmin, const Pointf &pmax) : BoundingBoxBase(pmin, pmax) {}; BoundingBoxf(const std::vector &points) : BoundingBoxBase(points) {}; }; class BoundingBoxf3 : public BoundingBox3Base { public: BoundingBoxf3() : BoundingBox3Base() {}; + BoundingBoxf3(const Pointf3 &pmin, const Pointf3 &pmax) : BoundingBox3Base(pmin, pmax) {}; BoundingBoxf3(const std::vector &points) : BoundingBox3Base(points) {}; }; +template +inline bool operator==(const BoundingBoxBase &bb1, const BoundingBoxBase &bb2) +{ + return bb1.min == bb2.min && bb1.max == bb2.max; +} + +template +inline bool operator!=(const BoundingBoxBase &bb1, const BoundingBoxBase &bb2) +{ + return !(bb1 == bb2); +} + +template +inline bool empty(const BoundingBoxBase &bb) +{ + return bb.min.x > bb.max.y || bb.min.y > bb.max.y; +} + +template +inline bool empty(const BoundingBox3Base &bb) +{ + return bb.min.x > bb.max.x || bb.min.y > bb.max.y || bb.min.z > bb.max.z;} } #endif From 80a962b838b6e3dfd6da0ee496eb172505514cee Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 10 Apr 2016 19:07:34 +0200 Subject: [PATCH 057/225] Added a macro SLIC3R_CPPVER to indicate the C++ language version supported by the compiler. --- xs/src/libslic3r/libslic3r.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index c60ed6d03..680716bb8 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -49,4 +49,14 @@ using namespace Slic3r; void confess_at(const char *file, int line, const char *func, const char *pat, ...); /* End implementation of CONFESS("foo"): */ +// Which C++ version is supported? +// For example, could optimized functions with move semantics be used? +#if __cplusplus==201402L + #define SLIC3R_CPPVER 14 +#elif __cplusplus==201103L + #define SLIC3R_CPPVER 11 +#else + #define SLIC3R_CPPVER 0 +#endif + #endif From 47337a3a5e2177a9523676526ba11d5ec705c138 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 10 Apr 2016 19:13:08 +0200 Subject: [PATCH 058/225] Fixed a typo when SLIC3R_DEBUG is active. --- xs/src/libslic3r/LayerRegion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index f5d10cd52..9f803094e 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -112,7 +112,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer) ); #ifdef SLIC3R_DEBUG - printf("Processing bridge at layer %zu:\n", this->layer()->id(); + printf("Processing bridge at layer %zu:\n", this->layer()->id()); #endif if (bd.detect_angle()) { From d98b7cf863c3e9a4994d920f1daf82e6e6c793a7 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sun, 10 Apr 2016 19:32:39 +0200 Subject: [PATCH 059/225] The chaining and leftmost_point methods were rewritten as static methods, so they may be called on Polylines without having to convert to PolylineCollection first. --- xs/src/libslic3r/PolylineCollection.cpp | 139 ++++++++++++++++++------ xs/src/libslic3r/PolylineCollection.hpp | 22 +++- 2 files changed, 123 insertions(+), 38 deletions(-) diff --git a/xs/src/libslic3r/PolylineCollection.cpp b/xs/src/libslic3r/PolylineCollection.cpp index f3077b0e1..136065127 100644 --- a/xs/src/libslic3r/PolylineCollection.cpp +++ b/xs/src/libslic3r/PolylineCollection.cpp @@ -2,50 +2,121 @@ namespace Slic3r { -void -PolylineCollection::chained_path(PolylineCollection* retval, bool no_reverse) const +struct Chaining { - if (this->polylines.empty()) return; - this->chained_path_from(this->polylines.front().first_point(), retval, no_reverse); -} + Point first; + Point last; + size_t idx; +}; -void -PolylineCollection::chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse) const +#ifndef sqr +template +inline sqr(T x) { return x * x; } +#endif /* sqr */ + +template +inline int nearest_point_index(const std::vector &pairs, const Point &start_near, bool no_reverse) { - Polylines my_paths = this->polylines; - - Points endpoints; - for (Polylines::const_iterator it = my_paths.begin(); it != my_paths.end(); ++it) { - endpoints.push_back(it->first_point()); - if (no_reverse) { - endpoints.push_back(it->first_point()); - } else { - endpoints.push_back(it->last_point()); + T dmin = std::numeric_limits::max(); + int idx = 0; + for (std::vector::const_iterator it = pairs.begin(); it != pairs.end(); ++it) { + T d = sqr(T(start_near.x - it->first.x)); + if (d <= dmin) { + d += sqr(T(start_near.y - it->first.y)); + if (d < dmin) { + idx = (it - pairs.begin()) * 2; + dmin = d; + if (dmin < EPSILON) + break; + } + } + if (! no_reverse) { + d = sqr(T(start_near.x - it->last.x)); + if (d <= dmin) { + d += sqr(T(start_near.y - it->last.y)); + if (d < dmin) { + idx = (it - pairs.begin()) * 2 + 1; + dmin = d; + if (dmin < EPSILON) + break; + } + } } } - - while (!my_paths.empty()) { + + return idx; +} + +Polylines PolylineCollection::chained_path_from( +#if SLIC3R_CPPVER > 11 + Polylines &&src, +#else + const Polylines &src, +#endif + Point start_near, + bool no_reverse) +{ + std::vector endpoints; + endpoints.reserve(src.size()); + for (size_t i = 0; i < src.size(); ++ i) { + Chaining c; + c.first = src[i].first_point(); + if (! no_reverse) + c.last = src[i].last_point(); + c.idx = i; + } + + Polylines retval; + while (! endpoints.empty()) { // find nearest point - int start_index = start_near.nearest_point_index(endpoints); - int path_index = start_index/2; - if (start_index % 2 && !no_reverse) { - my_paths.at(path_index).reverse(); - } - retval->polylines.push_back(my_paths.at(path_index)); - my_paths.erase(my_paths.begin() + path_index); - endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2); - start_near = retval->polylines.back().last_point(); + int endpoint_index = nearest_point_index(endpoints, start_near, no_reverse); +#if SLIC3R_CPPVER > 11 + retval.push_back(std::move(src[endpoints[endpoint_index/2].idx])); +#else + retval.push_back(src[endpoints[endpoint_index/2].idx]); +#endif + if (endpoint_index & 1) + retval.back().reverse(); + endpoints.erase(endpoints.begin() + endpoint_index/2); + start_near = retval.back().last_point(); } } -Point -PolylineCollection::leftmost_point() const +#if SLIC3R_CPPVER > 11 +Polylines PolylineCollection::chained_path(Polylines &&src, bool no_reverse) { - if (this->polylines.empty()) CONFESS("leftmost_point() called on empty PolylineCollection"); - Point p = this->polylines.front().leftmost_point(); - for (Polylines::const_iterator it = this->polylines.begin() + 1; it != this->polylines.end(); ++it) { + return (src.empty() || src.front().empty()) ? + Polylines() : + chained_path_from(std::move(src), src.front().first_point(), no_reverse); +} +Polylines PolylineCollection::chained_path_from(Polylines src, Point start_near, bool no_reverse) +{ + return chained_path_from(std::move(src), start_near, no_reverse); +} +Polylines PolylineCollection::chained_path(Polylines src, bool no_reverse) +{ + return (src.empty() || src.front().empty()) ? + Polylines() : + chained_path_from(std::move(src), src.front().first_point(), no_reverse); +} +#else +Polylines PolylineCollection::chained_path(const Polylines &src, bool no_reverse) +{ + return (src.empty() || src.front().points.empty()) ? + Polylines() : + chained_path_from(src, src.front().first_point(), no_reverse); +} +#endif + +Point PolylineCollection::leftmost_point(const Polylines &polylines) +{ + if (polylines.empty()) CONFESS("leftmost_point() called on empty PolylineCollection"); + Polylines::const_iterator it = polylines.begin(); + Point p = it->leftmost_point(); + for (++ it; it != polylines.end(); ++it) { Point p2 = it->leftmost_point(); - if (p2.x < p.x) p = p2; + if (p2.x < p.x) + p = p2; } return p; } @@ -56,4 +127,4 @@ PolylineCollection::append(const Polylines &pp) this->polylines.insert(this->polylines.end(), pp.begin(), pp.end()); } -} +} // namespace Slic3r diff --git a/xs/src/libslic3r/PolylineCollection.hpp b/xs/src/libslic3r/PolylineCollection.hpp index d41e8c465..492f0318f 100644 --- a/xs/src/libslic3r/PolylineCollection.hpp +++ b/xs/src/libslic3r/PolylineCollection.hpp @@ -8,12 +8,26 @@ namespace Slic3r { class PolylineCollection { - public: +public: Polylines polylines; - void chained_path(PolylineCollection* retval, bool no_reverse = false) const; - void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const; - Point leftmost_point() const; + void chained_path(PolylineCollection* retval, bool no_reverse = false) const + { retval->polylines = chained_path(this->polylines, no_reverse); } + void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const + { retval->polylines = chained_path_from(this->polylines, start_near, no_reverse); } + Point leftmost_point() const + { return leftmost_point(polylines); } void append(const Polylines &polylines); + + static Point leftmost_point(const Polylines &polylines); +#if SLIC3R_CPPVER > 11 + static Polylines chained_path(Polylines &&src, bool no_reverse = false); + static Polylines chained_path_from(Polylines &&src, Point start_near, bool no_reverse = false); + static Polylines chained_path(Polylines src, bool no_reverse = false); + static Polylines chained_path_from(Polylines src, Point start_near, bool no_reverse = false); +#else + static Polylines chained_path(const Polylines &src, bool no_reverse = false); + static Polylines chained_path_from(const Polylines &src, Point start_near, bool no_reverse = false); +#endif }; } From 103ec057744ba22957a0a461aed631ef4e4ae274 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 24 Nov 2016 15:32:32 +0100 Subject: [PATCH 060/225] Ported infill to XS/C++. Credits and many thanks go to @bubnikv for this work --- Build.PL | 6 - lib/Slic3r.pm | 8 +- lib/Slic3r/Fill.pm | 303 --- lib/Slic3r/Fill/3DHoneycomb.pm | 230 --- lib/Slic3r/Fill/Base.pm | 91 - lib/Slic3r/Fill/Concentric.pm | 57 - lib/Slic3r/Fill/Honeycomb.pm | 129 -- lib/Slic3r/Fill/PlanePath.pm | 118 -- lib/Slic3r/Fill/Rectilinear.pm | 175 -- lib/Slic3r/Layer.pm | 9 - lib/Slic3r/Print/Object.pm | 4 +- lib/Slic3r/Print/SupportMaterial.pm | 35 +- t/fill.t | 46 +- utils/package_win32.ps1 | 11 - xs/Build.PL | 3 + xs/MANIFEST | 17 + xs/lib/Slic3r/XS.pm | 12 + xs/src/libslic3r/BoundingBox.cpp | 20 + xs/src/libslic3r/BoundingBox.hpp | 18 +- xs/src/libslic3r/ExPolygon.cpp | 20 +- xs/src/libslic3r/ExPolygon.hpp | 10 +- .../libslic3r/ExtrusionEntityCollection.cpp | 11 + .../libslic3r/ExtrusionEntityCollection.hpp | 2 + xs/src/libslic3r/Fill/Fill.cpp | 290 +++ xs/src/libslic3r/Fill/Fill.hpp | 23 + xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp | 227 +++ xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp | 31 + xs/src/libslic3r/Fill/FillBase.cpp | 138 ++ xs/src/libslic3r/Fill/FillBase.hpp | 111 ++ xs/src/libslic3r/Fill/FillConcentric.cpp | 63 + xs/src/libslic3r/Fill/FillConcentric.hpp | 26 + xs/src/libslic3r/Fill/FillHoneycomb.cpp | 126 ++ xs/src/libslic3r/Fill/FillHoneycomb.hpp | 47 + xs/src/libslic3r/Fill/FillPlanePath.cpp | 206 ++ xs/src/libslic3r/Fill/FillPlanePath.hpp | 66 + xs/src/libslic3r/Fill/FillRectilinear.cpp | 138 ++ xs/src/libslic3r/Fill/FillRectilinear.hpp | 86 + xs/src/libslic3r/Fill/FillRectilinear2.cpp | 1712 +++++++++++++++++ xs/src/libslic3r/Fill/FillRectilinear2.hpp | 76 + xs/src/libslic3r/Layer.cpp | 39 +- xs/src/libslic3r/Layer.hpp | 25 +- xs/src/libslic3r/LayerRegion.cpp | 44 +- xs/src/libslic3r/MultiPoint.cpp | 48 +- xs/src/libslic3r/MultiPoint.hpp | 8 +- xs/src/libslic3r/Point.cpp | 27 +- xs/src/libslic3r/Point.hpp | 25 +- xs/src/libslic3r/Polygon.hpp | 2 +- xs/src/libslic3r/Polyline.hpp | 19 + xs/src/libslic3r/PolylineCollection.cpp | 64 +- xs/src/libslic3r/PolylineCollection.hpp | 20 +- xs/src/libslic3r/PrintConfig.cpp | 12 +- xs/src/libslic3r/PrintConfig.hpp | 9 +- xs/src/libslic3r/Surface.cpp | 11 - xs/src/libslic3r/Surface.hpp | 28 +- xs/src/libslic3r/SurfaceCollection.cpp | 51 +- xs/src/libslic3r/SurfaceCollection.hpp | 6 +- xs/src/libslic3r/libslic3r.h | 10 + xs/src/perlglue.cpp | 1 + xs/src/xsinit.h | 15 + xs/xsp/Filler.xsp | 78 + xs/xsp/Layer.xsp | 1 + xs/xsp/SurfaceCollection.xsp | 6 +- xs/xsp/my.map | 5 + xs/xsp/typemap.xspt | 4 + 64 files changed, 3916 insertions(+), 1343 deletions(-) delete mode 100644 lib/Slic3r/Fill.pm delete mode 100644 lib/Slic3r/Fill/3DHoneycomb.pm delete mode 100644 lib/Slic3r/Fill/Base.pm delete mode 100644 lib/Slic3r/Fill/Concentric.pm delete mode 100644 lib/Slic3r/Fill/Honeycomb.pm delete mode 100644 lib/Slic3r/Fill/PlanePath.pm delete mode 100644 lib/Slic3r/Fill/Rectilinear.pm create mode 100644 xs/src/libslic3r/Fill/Fill.cpp create mode 100644 xs/src/libslic3r/Fill/Fill.hpp create mode 100644 xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp create mode 100644 xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp create mode 100644 xs/src/libslic3r/Fill/FillBase.cpp create mode 100644 xs/src/libslic3r/Fill/FillBase.hpp create mode 100644 xs/src/libslic3r/Fill/FillConcentric.cpp create mode 100644 xs/src/libslic3r/Fill/FillConcentric.hpp create mode 100644 xs/src/libslic3r/Fill/FillHoneycomb.cpp create mode 100644 xs/src/libslic3r/Fill/FillHoneycomb.hpp create mode 100644 xs/src/libslic3r/Fill/FillPlanePath.cpp create mode 100644 xs/src/libslic3r/Fill/FillPlanePath.hpp create mode 100644 xs/src/libslic3r/Fill/FillRectilinear.cpp create mode 100644 xs/src/libslic3r/Fill/FillRectilinear.hpp create mode 100644 xs/src/libslic3r/Fill/FillRectilinear2.cpp create mode 100644 xs/src/libslic3r/Fill/FillRectilinear2.hpp create mode 100644 xs/xsp/Filler.xsp diff --git a/Build.PL b/Build.PL index 06261f77a..68a9af16e 100644 --- a/Build.PL +++ b/Build.PL @@ -15,7 +15,6 @@ my %prereqs = qw( File::Basename 0 File::Spec 0 Getopt::Long 0 - Math::PlanePath 53 Module::Build::WithXSpp 0.14 Moo 1.003001 POSIX 0 @@ -112,11 +111,6 @@ EOF # make sure our cpanm is updated (old ones don't support the ~ syntax) system $cpanm, @cpanm_args, 'App::cpanminus'; - # install the Windows-compatible Math::Libm - if ($^O eq 'MSWin32' && !eval "use Math::Libm; 1") { - system $cpanm, @cpanm_args, 'https://github.com/alexrj/Math-Libm/tarball/master'; - } - my %modules = (%prereqs, %recommends); foreach my $module (sort keys %modules) { my $version = $modules{$module}; diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index aa9aea28c..4a1bd6026 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -39,7 +39,6 @@ use Slic3r::Config; use Slic3r::ExPolygon; use Slic3r::ExtrusionLoop; use Slic3r::ExtrusionPath; -use Slic3r::Fill; use Slic3r::Flow; use Slic3r::Format::AMF; use Slic3r::Format::OBJ; @@ -113,6 +112,12 @@ sub spawn_thread { return $thread; } +# If the threading is enabled, spawn a set of threads. +# Otherwise run the task on the current thread. +# Used for +# Slic3r::Print::Object->layers->make_perimeters +# Slic3r::Print::Object->layers->make_fill +# Slic3r::Print::SupportMaterial::generate_toolpaths sub parallelize { my %params = @_; @@ -193,6 +198,7 @@ sub thread_cleanup { *Slic3r::ExtrusionLoop::DESTROY = sub {}; *Slic3r::ExtrusionPath::DESTROY = sub {}; *Slic3r::ExtrusionPath::Collection::DESTROY = sub {}; + *Slic3r::Filler::DESTROY = sub {}; *Slic3r::Flow::DESTROY = sub {}; *Slic3r::GCode::DESTROY = sub {}; *Slic3r::GCode::AvoidCrossingPerimeters::DESTROY = sub {}; diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm deleted file mode 100644 index 908d07528..000000000 --- a/lib/Slic3r/Fill.pm +++ /dev/null @@ -1,303 +0,0 @@ -package Slic3r::Fill; -use Moo; - -use List::Util qw(max); -use Slic3r::ExtrusionPath ':roles'; -use Slic3r::Fill::3DHoneycomb; -use Slic3r::Fill::Base; -use Slic3r::Fill::Concentric; -use Slic3r::Fill::Honeycomb; -use Slic3r::Fill::PlanePath; -use Slic3r::Fill::Rectilinear; -use Slic3r::Fill::AlignedRectilinear; -use Slic3r::Flow ':roles'; -use Slic3r::Geometry qw(X Y PI scale chained_path deg2rad); -use Slic3r::Geometry::Clipper qw(union union_ex diff diff_ex intersection_ex offset offset2); -use Slic3r::Surface ':types'; - - -has 'bounding_box' => (is => 'ro', required => 0); -has 'fillers' => (is => 'rw', default => sub { {} }); - -our %FillTypes = ( - archimedeanchords => 'Slic3r::Fill::ArchimedeanChords', - alignedrectilinear => 'Slic3r::Fill::AlignedRectilinear', - rectilinear => 'Slic3r::Fill::Rectilinear', - grid => 'Slic3r::Fill::Grid', - flowsnake => 'Slic3r::Fill::Flowsnake', - octagramspiral => 'Slic3r::Fill::OctagramSpiral', - hilbertcurve => 'Slic3r::Fill::HilbertCurve', - line => 'Slic3r::Fill::Line', - concentric => 'Slic3r::Fill::Concentric', - honeycomb => 'Slic3r::Fill::Honeycomb', - '3dhoneycomb' => 'Slic3r::Fill::3DHoneycomb', -); - -sub filler { - my $self = shift; - my ($filler) = @_; - - if (!ref $self) { - return $FillTypes{$filler}->new; - } - - $self->fillers->{$filler} ||= $FillTypes{$filler}->new( - bounding_box => $self->bounding_box, - ); - return $self->fillers->{$filler}; -} - -sub make_fill { - my $self = shift; - my ($layerm) = @_; - - Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id; - - my $fill_density = $layerm->region->config->fill_density; - my $infill_flow = $layerm->flow(FLOW_ROLE_INFILL); - my $solid_infill_flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL); - my $top_solid_infill_flow = $layerm->flow(FLOW_ROLE_TOP_SOLID_INFILL); - - my @surfaces = (); - - # merge adjacent surfaces - # in case of bridge surfaces, the ones with defined angle will be attached to the ones - # without any angle (shouldn't this logic be moved to process_external_surfaces()?) - { - my @surfaces_with_bridge_angle = grep { $_->bridge_angle >= 0 } @{$layerm->fill_surfaces}; - - # group surfaces by distinct properties - my @groups = @{$layerm->fill_surfaces->group}; - - # merge compatible groups (we can generate continuous infill for them) - { - # cache flow widths and patterns used for all solid groups - # (we'll use them for comparing compatible groups) - my @is_solid = my @fw = my @pattern = (); - for (my $i = 0; $i <= $#groups; $i++) { - # we can only merge solid non-bridge surfaces, so discard - # non-solid surfaces - if ($groups[$i][0]->is_solid && (!$groups[$i][0]->is_bridge || $layerm->layer->id == 0)) { - $is_solid[$i] = 1; - $fw[$i] = ($groups[$i][0]->surface_type == S_TYPE_TOP) - ? $top_solid_infill_flow->width - : $solid_infill_flow->width; - $pattern[$i] = $groups[$i][0]->is_external - ? $layerm->region->config->external_fill_pattern - : 'rectilinear'; - } else { - $is_solid[$i] = 0; - $fw[$i] = 0; - $pattern[$i] = 'none'; - } - } - - # loop through solid groups - for (my $i = 0; $i <= $#groups; $i++) { - next if !$is_solid[$i]; - - # find compatible groups and append them to this one - for (my $j = $i+1; $j <= $#groups; $j++) { - next if !$is_solid[$j]; - - if ($fw[$i] == $fw[$j] && $pattern[$i] eq $pattern[$j]) { - # groups are compatible, merge them - push @{$groups[$i]}, @{$groups[$j]}; - splice @groups, $j, 1; - splice @is_solid, $j, 1; - splice @fw, $j, 1; - splice @pattern, $j, 1; - } - } - } - } - - # give priority to bridges - @groups = sort { ($a->[0]->bridge_angle >= 0) ? -1 : 0 } @groups; - - foreach my $group (@groups) { - my $union_p = union([ map $_->p, @$group ], 1); - - # subtract surfaces having a defined bridge_angle from any other - if (@surfaces_with_bridge_angle && $group->[0]->bridge_angle < 0) { - $union_p = diff( - $union_p, - [ map $_->p, @surfaces_with_bridge_angle ], - 1, - ); - } - - # subtract any other surface already processed - my $union = diff_ex( - $union_p, - [ map $_->p, @surfaces ], - 1, - ); - - push @surfaces, map $group->[0]->clone(expolygon => $_), @$union; - } - } - - # we need to detect any narrow surfaces that might collapse - # when adding spacing below - # such narrow surfaces are often generated in sloping walls - # by bridge_over_infill() and combine_infill() as a result of the - # subtraction of the combinable area from the layer infill area, - # which leaves small areas near the perimeters - # we are going to grow such regions by overlapping them with the void (if any) - # TODO: detect and investigate whether there could be narrow regions without - # any void neighbors - { - my $distance_between_surfaces = max( - $infill_flow->scaled_spacing, - $solid_infill_flow->scaled_spacing, - $top_solid_infill_flow->scaled_spacing, - ); - my $collapsed = diff( - [ map @{$_->expolygon}, @surfaces ], - offset2([ map @{$_->expolygon}, @surfaces ], -$distance_between_surfaces/2, +$distance_between_surfaces/2), - 1, - ); - push @surfaces, map Slic3r::Surface->new( - expolygon => $_, - surface_type => S_TYPE_INTERNALSOLID, - ), @{intersection_ex( - offset($collapsed, $distance_between_surfaces), - [ - (map @{$_->expolygon}, grep $_->surface_type == S_TYPE_INTERNALVOID, @surfaces), - (@$collapsed), - ], - 1, - )}; - } - - if (0) { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output("fill_" . $layerm->print_z . ".svg", - expolygons => [ map $_->expolygon, grep !$_->is_solid, @surfaces ], - red_expolygons => [ map $_->expolygon, grep $_->is_solid, @surfaces ], - ); - } - - my @fills = (); - SURFACE: foreach my $surface (@surfaces) { - next if $surface->surface_type == S_TYPE_INTERNALVOID; - my $filler = $layerm->region->config->fill_pattern; - my $density = $fill_density; - my $role = ($surface->surface_type == S_TYPE_TOP) ? FLOW_ROLE_TOP_SOLID_INFILL - : $surface->is_solid ? FLOW_ROLE_SOLID_INFILL - : FLOW_ROLE_INFILL; - my $is_bridge = $layerm->layer->id > 0 && $surface->is_bridge; - my $is_solid = $surface->is_solid; - - if ($surface->is_solid) { - $density = 100; - $filler = 'rectilinear'; - if ($surface->is_external && !$is_bridge) { - $filler = $layerm->region->config->external_fill_pattern; - } - } else { - next SURFACE unless $density > 0; - } - - # get filler object - my $f = $self->filler($filler); - - # calculate the actual flow we'll be using for this infill - my $h = $surface->thickness == -1 ? $layerm->layer->height : $surface->thickness; - my $flow = $layerm->region->flow( - $role, - $h, - $is_bridge || $f->use_bridge_flow, - $layerm->layer->id == 0, - -1, - $layerm->layer->object, - ); - - # calculate flow spacing for infill pattern generation - my $using_internal_flow = 0; - if (!$is_solid && !$is_bridge) { - # it's internal infill, so we can calculate a generic flow spacing - # for all layers, for avoiding the ugly effect of - # misaligned infill on first layer because of different extrusion width and - # layer height - my $internal_flow = $layerm->region->flow( - FLOW_ROLE_INFILL, - $layerm->layer->object->config->layer_height, # TODO: handle infill_every_layers? - 0, # no bridge - 0, # no first layer - -1, # auto width - $layerm->layer->object, - ); - $f->spacing($internal_flow->spacing); - $using_internal_flow = 1; - # create the actual flow for internal flow that is used later. - $flow = Slic3r::Flow->new_from_spacing( - spacing => $internal_flow->spacing, - nozzle_diameter => $flow->nozzle_diameter, - layer_height => $h, - bridge => 0, - ); - } else { - $f->spacing($flow->spacing); - } - - $f->layer_id($layerm->layer->id); - $f->z($layerm->layer->print_z); - $f->angle(deg2rad($layerm->region->config->fill_angle)); - $f->loop_clipping(scale($flow->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER); - - # apply half spacing using this flow's own spacing and generate infill - my @polylines = map $f->fill_surface( - $_, - density => $density/100, - layer_height => $h, - ), @{ $surface->offset(-scale($f->spacing)/2) }; - - next unless @polylines; - - # calculate actual flow from spacing (which might have been adjusted by the infill - # pattern generator) - if ($using_internal_flow) { - # if we used the internal flow we're not doing a solid infill - # so we can safely ignore the slight variation that might have - # been applied to $f->flow_spacing - } else { - $flow = Slic3r::Flow->new_from_spacing( - spacing => $f->spacing, - nozzle_diameter => $flow->nozzle_diameter, - layer_height => $h, - bridge => $is_bridge || $f->use_bridge_flow, - ); - } - my $mm3_per_mm = $flow->mm3_per_mm; - - # save into layer - { - my $role = $is_bridge ? EXTR_ROLE_BRIDGE - : $is_solid ? (($surface->surface_type == S_TYPE_TOP) ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL) - : EXTR_ROLE_FILL; - - push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new; - $collection->no_sort($f->no_sort); - $collection->append( - map Slic3r::ExtrusionPath->new( - polyline => $_, - role => $role, - mm3_per_mm => $mm3_per_mm, - width => $flow->width, - height => $flow->height, - ), @polylines, - ); - } - } - - # add thin fill regions - foreach my $thin_fill (@{$layerm->thin_fills}) { - push @fills, Slic3r::ExtrusionPath::Collection->new($thin_fill); - } - - return @fills; -} - -1; diff --git a/lib/Slic3r/Fill/3DHoneycomb.pm b/lib/Slic3r/Fill/3DHoneycomb.pm deleted file mode 100644 index 3bf7e547f..000000000 --- a/lib/Slic3r/Fill/3DHoneycomb.pm +++ /dev/null @@ -1,230 +0,0 @@ -package Slic3r::Fill::3DHoneycomb; -use Moo; - -extends 'Slic3r::Fill::Base'; - -use POSIX qw(ceil fmod); -use Slic3r::Geometry qw(scale scaled_epsilon); -use Slic3r::Geometry::Clipper qw(intersection_pl); - -# require bridge flow since most of this pattern hangs in air -sub use_bridge_flow { 1 } - -sub fill_surface { - my ($self, $surface, %params) = @_; - - my $expolygon = $surface->expolygon; - my $bb = $expolygon->bounding_box; - my $size = $bb->size; - - my $distance = scale($self->spacing) / $params{density}; - - # align bounding box to a multiple of our honeycomb grid module - # (a module is 2*$distance since one $distance half-module is - # growing while the other $distance half-module is shrinking) - { - my $min = $bb->min_point; - $min->translate( - -($bb->x_min % (2*$distance)), - -($bb->y_min % (2*$distance)), - ); - $bb->merge_point($min); - } - - # generate pattern - my @polylines = map Slic3r::Polyline->new(@$_), - makeGrid( - scale($self->z), - $distance, - ceil($size->x / $distance) + 1, - ceil($size->y / $distance) + 1, #// - (($self->layer_id / $surface->thickness_layers) % 2) + 1, - ); - - # move pattern in place - $_->translate($bb->x_min, $bb->y_min) for @polylines; - - # clip pattern to boundaries - @polylines = @{intersection_pl(\@polylines, \@$expolygon)}; - - # connect lines - unless ($params{dont_connect} || !@polylines) { # prevent calling leftmost_point() on empty collections - my ($expolygon_off) = @{$expolygon->offset_ex(scaled_epsilon)}; - my $collection = Slic3r::Polyline::Collection->new(@polylines); - @polylines = (); - foreach my $polyline (@{$collection->chained_path_from($collection->leftmost_point, 0)}) { - # try to append this polyline to previous one if any - if (@polylines) { - my $line = Slic3r::Line->new($polylines[-1]->last_point, $polyline->first_point); - if ($line->length <= 1.5*$distance && $expolygon_off->contains_line($line)) { - $polylines[-1]->append_polyline($polyline); - next; - } - } - - # make a clone before $collection goes out of scope - push @polylines, $polyline->clone; - } - } - - # TODO: return ExtrusionLoop objects to get better chained paths - return @polylines; -} - - -=head1 DESCRIPTION - -Creates a contiguous sequence of points at a specified height that make -up a horizontal slice of the edges of a space filling truncated -octahedron tesselation. The octahedrons are oriented so that the -square faces are in the horizontal plane with edges parallel to the X -and Y axes. - -Credits: David Eccles (gringer). - -=head2 makeGrid(z, gridSize, gridWidth, gridHeight, curveType) - -Generate a set of curves (array of array of 2d points) that describe a -horizontal slice of a truncated regular octahedron with a specified -grid square size. - -=cut - -sub makeGrid { - my ($z, $gridSize, $gridWidth, $gridHeight, $curveType) = @_; - my $scaleFactor = $gridSize; - my $normalisedZ = $z / $scaleFactor; - my @points = makeNormalisedGrid($normalisedZ, $gridWidth, $gridHeight, $curveType); - foreach my $lineRef (@points) { - foreach my $pointRef (@$lineRef) { - $pointRef->[0] *= $scaleFactor; - $pointRef->[1] *= $scaleFactor; - } - } - return @points; -} - -=head1 FUNCTIONS -=cut - -=head2 colinearPoints(offset, gridLength) - -Generate an array of points that are in the same direction as the -basic printing line (i.e. Y points for columns, X points for rows) - -Note: a negative offset only causes a change in the perpendicular -direction - -=cut - -sub colinearPoints { - my ($offset, $baseLocation, $gridLength) = @_; - - my @points = (); - push @points, $baseLocation - abs($offset/2); - for (my $i = 0; $i < $gridLength; $i++) { - push @points, $baseLocation + $i + abs($offset/2); - push @points, $baseLocation + ($i+1) - abs($offset/2); - } - push @points, $baseLocation + $gridLength + abs($offset/2); - return @points; -} - -=head2 colinearPoints(offset, baseLocation, gridLength) - -Generate an array of points for the dimension that is perpendicular to -the basic printing line (i.e. X points for columns, Y points for rows) - -=cut - -sub perpendPoints { - my ($offset, $baseLocation, $gridLength) = @_; - - my @points = (); - my $side = 2*(($baseLocation) % 2) - 1; - push @points, $baseLocation - $offset/2 * $side; - for (my $i = 0; $i < $gridLength; $i++) { - $side = 2*(($i+$baseLocation) % 2) - 1; - push @points, $baseLocation + $offset/2 * $side; - push @points, $baseLocation + $offset/2 * $side; - } - push @points, $baseLocation - $offset/2 * $side; - - return @points; -} - -=head2 trim(pointArrayRef, minX, minY, maxX, maxY) - -Trims an array of points to specified rectangular limits. Point -components that are outside these limits are set to the limits. - -=cut - -sub trim { - my ($pointArrayRef, $minX, $minY, $maxX, $maxY) = @_; - - foreach (@$pointArrayRef) { - $_->[0] = ($_->[0] < $minX) ? $minX : (($_->[0] > $maxX) ? $maxX : $_->[0]); - $_->[1] = ($_->[1] < $minY) ? $minY : (($_->[1] > $maxY) ? $maxY : $_->[1]); - } -} - -=head2 makeNormalisedGrid(z, gridWidth, gridHeight, curveType) - -Generate a set of curves (array of array of 2d points) that describe a -horizontal slice of a truncated regular octahedron with edge length 1. - -curveType specifies which lines to print, 1 for vertical lines -(columns), 2 for horizontal lines (rows), and 3 for both. - -=cut - -sub makeNormalisedGrid { - my ($z, $gridWidth, $gridHeight, $curveType) = @_; - - ## offset required to create a regular octagram - my $octagramGap = 0.5; - - # sawtooth wave function for range f($z) = [-$octagramGap .. $octagramGap] - my $a = sqrt(2); # period - my $wave = abs(fmod($z, $a) - $a/2)/$a*4 - 1; - my $offset = $wave * $octagramGap; - - my @points = (); - if (($curveType & 1) != 0) { - for (my $x = 0; $x <= $gridWidth; $x++) { - my @xPoints = perpendPoints($offset, $x, $gridHeight); - my @yPoints = colinearPoints($offset, 0, $gridHeight); - # This is essentially @newPoints = zip(@xPoints, @yPoints) - my @newPoints = map [ $xPoints[$_], $yPoints[$_] ], 0..$#xPoints; - - # trim points to grid edges - #trim(\@newPoints, 0, 0, $gridWidth, $gridHeight); - - if ($x % 2 == 0){ - push @points, [ @newPoints ]; - } else { - push @points, [ reverse @newPoints ]; - } - } - } - if (($curveType & 2) != 0) { - for (my $y = 0; $y <= $gridHeight; $y++) { - my @xPoints = colinearPoints($offset, 0, $gridWidth); - my @yPoints = perpendPoints($offset, $y, $gridWidth); - my @newPoints = map [ $xPoints[$_], $yPoints[$_] ], 0..$#xPoints; - - # trim points to grid edges - #trim(\@newPoints, 0, 0, $gridWidth, $gridHeight); - - if ($y % 2 == 0) { - push @points, [ @newPoints ]; - } else { - push @points, [ reverse @newPoints ]; - } - } - } - return @points; -} - -1; diff --git a/lib/Slic3r/Fill/Base.pm b/lib/Slic3r/Fill/Base.pm deleted file mode 100644 index 75c8e03e6..000000000 --- a/lib/Slic3r/Fill/Base.pm +++ /dev/null @@ -1,91 +0,0 @@ -package Slic3r::Fill::Base; -use Moo; - -has 'layer_id' => (is => 'rw'); -has 'z' => (is => 'rw'); # in unscaled coordinates -has 'angle' => (is => 'rw'); # in radians, ccw, 0 = East -has 'spacing' => (is => 'rw'); # in unscaled coordinates -has 'loop_clipping' => (is => 'rw', default => sub { 0 }); # in scaled coordinates -has 'bounding_box' => (is => 'ro', required => 0); # Slic3r::Geometry::BoundingBox object - -sub adjust_solid_spacing { - my $self = shift; - my %params = @_; - - my $number_of_lines = int($params{width} / $params{distance}) + 1; - return $params{distance} if $number_of_lines <= 1; - - my $extra_space = $params{width} % $params{distance}; - return $params{distance} + $extra_space / ($number_of_lines - 1); -} - -sub no_sort { 0 } -sub use_bridge_flow { 0 } - - -package Slic3r::Fill::WithDirection; -use Moo::Role; - -use Slic3r::Geometry qw(PI rad2deg); - -sub angles () { [0, PI/2] } - -sub infill_direction { - my $self = shift; - my ($surface) = @_; - - if (!defined $self->angle) { - warn "Using undefined infill angle"; - $self->angle(0); - } - - # set infill angle - my (@rotate); - $rotate[0] = $self->angle; - $rotate[1] = $self->bounding_box - ? $self->bounding_box->center - : $surface->expolygon->bounding_box->center; - my $shift = $rotate[1]->clone; - - if (defined $self->layer_id) { - # alternate fill direction - my $layer_num = $self->layer_id / $surface->thickness_layers; - my $angle = $self->angles->[$layer_num % @{$self->angles}]; - $rotate[0] = $self->angle + $angle if $angle; - } - - # use bridge angle - if ($surface->bridge_angle >= 0) { - Slic3r::debugf "Filling bridge with angle %d\n", rad2deg($surface->bridge_angle); - $rotate[0] = $surface->bridge_angle; - } - - $rotate[0] += PI/2; - $shift->rotate(@rotate); - return [\@rotate, $shift]; -} - -# this method accepts any object that implements rotate() and translate() -sub rotate_points { - my $self = shift; - my ($expolygon, $rotate_vector) = @_; - - # rotate points - my ($rotate, $shift) = @$rotate_vector; - $rotate = [ -$rotate->[0], $rotate->[1] ]; - $expolygon->rotate(@$rotate); - $expolygon->translate(@$shift); -} - -sub rotate_points_back { - my $self = shift; - my ($paths, $rotate_vector) = @_; - - my ($rotate, $shift) = @$rotate_vector; - $shift = [ map -$_, @$shift ]; - - $_->translate(@$shift) for @$paths; - $_->rotate(@$rotate) for @$paths; -} - -1; diff --git a/lib/Slic3r/Fill/Concentric.pm b/lib/Slic3r/Fill/Concentric.pm deleted file mode 100644 index ca1837c4e..000000000 --- a/lib/Slic3r/Fill/Concentric.pm +++ /dev/null @@ -1,57 +0,0 @@ -package Slic3r::Fill::Concentric; -use Moo; - -extends 'Slic3r::Fill::Base'; - -use Slic3r::Geometry qw(scale unscale X); -use Slic3r::Geometry::Clipper qw(offset offset2 union_pt_chained); - -sub no_sort { 1 } - -sub fill_surface { - my $self = shift; - my ($surface, %params) = @_; - - # no rotation is supported for this infill pattern - - my $expolygon = $surface->expolygon; - my $bounding_box = $expolygon->bounding_box; - - my $min_spacing = scale($self->spacing); - my $distance = $min_spacing / $params{density}; - - if ($params{density} == 1 && !$params{dont_adjust}) { - $distance = $self->adjust_solid_spacing( - width => $bounding_box->size->[X], - distance => $distance, - ); - $self->spacing(unscale $distance); - } - - my @loops = my @last = map $_->clone, @$expolygon; - while (@last) { - push @loops, @last = @{offset2(\@last, -($distance + 0.5*$min_spacing), +0.5*$min_spacing)}; - } - - # generate paths from the outermost to the innermost, to avoid - # adhesion problems of the first central tiny loops - @loops = map Slic3r::Polygon->new(@$_), - reverse @{union_pt_chained(\@loops)}; - - # split paths using a nearest neighbor search - my @paths = (); - my $last_pos = Slic3r::Point->new(0,0); - foreach my $loop (@loops) { - push @paths, $loop->split_at_index($last_pos->nearest_point_index(\@$loop)); - $last_pos = $paths[-1]->last_point; - } - - # clip the paths to prevent the extruder from getting exactly on the first point of the loop - $_->clip_end($self->loop_clipping) for @paths; - @paths = grep $_->is_valid, @paths; # remove empty paths (too short, thus eaten by clipping) - - # TODO: return ExtrusionLoop objects to get better chained paths - return @paths; -} - -1; diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm deleted file mode 100644 index b0fbd65ff..000000000 --- a/lib/Slic3r/Fill/Honeycomb.pm +++ /dev/null @@ -1,129 +0,0 @@ -package Slic3r::Fill::Honeycomb; -use Moo; - -extends 'Slic3r::Fill::Base'; -with qw(Slic3r::Fill::WithDirection); - -has 'cache' => (is => 'rw', default => sub {{}}); - -use Slic3r::Geometry qw(PI X Y MIN MAX scale scaled_epsilon); -use Slic3r::Geometry::Clipper qw(intersection intersection_pl); - -sub angles () { [0, PI/3, PI/3*2] } - -sub fill_surface { - my $self = shift; - my ($surface, %params) = @_; - - my $rotate_vector = $self->infill_direction($surface); - - # cache hexagons math - my $cache_id = sprintf "d%s_s%s", $params{density}, $self->spacing; - my $m; - if (!($m = $self->cache->{$cache_id})) { - $m = $self->cache->{$cache_id} = {}; - my $min_spacing = scale($self->spacing); - $m->{distance} = $min_spacing / $params{density}; - $m->{hex_side} = $m->{distance} / (sqrt(3)/2); - $m->{hex_width} = $m->{distance} * 2; # $m->{hex_width} == $m->{hex_side} * sqrt(3); - my $hex_height = $m->{hex_side} * 2; - $m->{pattern_height} = $hex_height + $m->{hex_side}; - $m->{y_short} = $m->{distance} * sqrt(3)/3; - $m->{x_offset} = $min_spacing / 2; - $m->{y_offset} = $m->{x_offset} * sqrt(3)/3; - $m->{hex_center} = Slic3r::Point->new($m->{hex_width}/2, $m->{hex_side}); - } - - my @polygons = (); - { - # adjust actual bounding box to the nearest multiple of our hex pattern - # and align it so that it matches across layers - - my $bounding_box = $surface->expolygon->bounding_box; - { - # rotate bounding box according to infill direction - my $bb_polygon = $bounding_box->polygon; - $bb_polygon->rotate($rotate_vector->[0][0], $m->{hex_center}); - $bounding_box = $bb_polygon->bounding_box; - - # extend bounding box so that our pattern will be aligned with other layers - # $bounding_box->[X1] and [Y1] represent the displacement between new bounding box offset and old one - $bounding_box->merge_point(Slic3r::Point->new( - $bounding_box->x_min - ($bounding_box->x_min % $m->{hex_width}), - $bounding_box->y_min - ($bounding_box->y_min % $m->{pattern_height}), - )); - } - - my $x = $bounding_box->x_min; - while ($x <= $bounding_box->x_max) { - my $p = []; - - my @x = ($x + $m->{x_offset}, $x + $m->{distance} - $m->{x_offset}); - for (1..2) { - @$p = reverse @$p; # turn first half upside down - my @p = (); - for (my $y = $bounding_box->y_min; $y <= $bounding_box->y_max; $y += $m->{y_short} + $m->{hex_side} + $m->{y_short} + $m->{hex_side}) { - push @$p, - [ $x[1], $y + $m->{y_offset} ], - [ $x[0], $y + $m->{y_short} - $m->{y_offset} ], - [ $x[0], $y + $m->{y_short} + $m->{hex_side} + $m->{y_offset} ], - [ $x[1], $y + $m->{y_short} + $m->{hex_side} + $m->{y_short} - $m->{y_offset} ], - [ $x[1], $y + $m->{y_short} + $m->{hex_side} + $m->{y_short} + $m->{hex_side} + $m->{y_offset} ]; - } - @x = map $_ + $m->{distance}, reverse @x; # draw symmetrical pattern - $x += $m->{distance}; - } - - push @polygons, Slic3r::Polygon->new(@$p); - } - - $_->rotate(-$rotate_vector->[0][0], $m->{hex_center}) for @polygons; - } - - my @paths; - if ($params{complete} || 1) { - # we were requested to complete each loop; - # in this case we don't try to make more continuous paths - @paths = map $_->split_at_first_point, - @{intersection([ $surface->p ], \@polygons)}; - - } else { - # consider polygons as polylines without re-appending the initial point: - # this cuts the last segment on purpose, so that the jump to the next - # path is more straight - @paths = @{intersection_pl( - [ map Slic3r::Polyline->new(@$_), @polygons ], - [ @{$surface->expolygon} ], - )}; - - # connect paths - if (@paths) { # prevent calling leftmost_point() on empty collections - my $collection = Slic3r::Polyline::Collection->new(@paths); - @paths = (); - foreach my $path (@{$collection->chained_path_from($collection->leftmost_point, 0)}) { - if (@paths) { - # distance between first point of this path and last point of last path - my $distance = $paths[-1]->last_point->distance_to($path->first_point); - - if ($distance <= $m->{hex_width}) { - $paths[-1]->append_polyline($path); - next; - } - } - - # make a clone before $collection goes out of scope - push @paths, $path->clone; - } - } - - # clip paths again to prevent connection segments from crossing the expolygon boundaries - @paths = @{intersection_pl( - \@paths, - [ map @$_, @{$surface->expolygon->offset_ex(scaled_epsilon)} ], - )}; - } - - return @paths; -} - -1; diff --git a/lib/Slic3r/Fill/PlanePath.pm b/lib/Slic3r/Fill/PlanePath.pm deleted file mode 100644 index 556835ec4..000000000 --- a/lib/Slic3r/Fill/PlanePath.pm +++ /dev/null @@ -1,118 +0,0 @@ -package Slic3r::Fill::PlanePath; -use Moo; - -extends 'Slic3r::Fill::Base'; -with qw(Slic3r::Fill::WithDirection); - -use Slic3r::Geometry qw(scale X1 Y1 X2 Y2); -use Slic3r::Geometry::Clipper qw(intersection_pl); - -sub angles () { [0] } -sub multiplier () { 1 } - -sub process_polyline {} - -sub fill_surface { - my $self = shift; - my ($surface, %params) = @_; - - # rotate polygons - my $expolygon = $surface->expolygon->clone; - my $rotate_vector = $self->infill_direction($surface); - $self->rotate_points($expolygon, $rotate_vector); - - my $distance_between_lines = scale($self->spacing) / $params{density} * $self->multiplier; - - # align infill across layers using the object's bounding box - my $bb_polygon = $self->bounding_box->polygon; - $self->rotate_points($bb_polygon, $rotate_vector); - my $bounding_box = $bb_polygon->bounding_box; - - (ref $self) =~ /::([^:]+)$/; - my $path = "Math::PlanePath::$1"->new; - - my $translate = Slic3r::Point->new(0,0); # vector - if ($path->x_negative || $path->y_negative) { - # if the curve extends on both positive and negative coordinate space, - # center our expolygon around origin - $translate = $bounding_box->center->negative; - } else { - # if the curve does not extend in negative coordinate space, - # move expolygon entirely in positive coordinate space - $translate = $bounding_box->min_point->negative; - } - $expolygon->translate(@$translate); - $bounding_box->translate(@$translate); - - my ($n_lo, $n_hi) = $path->rect_to_n_range( - map { $_ / $distance_between_lines } - @{$bounding_box->min_point}, - @{$bounding_box->max_point}, - ); - - my $polyline = Slic3r::Polyline->new( - map [ map { $_ * $distance_between_lines } $path->n_to_xy($_) ], ($n_lo..$n_hi) - ); - return {} if @$polyline <= 1; - - $self->process_polyline($polyline, $bounding_box); - - my @paths = @{intersection_pl([$polyline], \@$expolygon)}; - - if (0) { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output("fill.svg", - no_arrows => 1, - polygons => \@$expolygon, - green_polygons => [ $bounding_box->polygon ], - polylines => [ $polyline ], - red_polylines => \@paths, - ); - } - - # paths must be repositioned and rotated back - $_->translate(@{$translate->negative}) for @paths; - $self->rotate_points_back(\@paths, $rotate_vector); - - return @paths; -} - - -package Slic3r::Fill::ArchimedeanChords; -use Moo; -extends 'Slic3r::Fill::PlanePath'; -use Math::PlanePath::ArchimedeanChords; - - -package Slic3r::Fill::Flowsnake; -use Moo; -extends 'Slic3r::Fill::PlanePath'; -use Math::PlanePath::Flowsnake; -use Slic3r::Geometry qw(X); - -# Sorry, this fill is currently broken. - -sub process_polyline { - my $self = shift; - my ($polyline, $bounding_box) = @_; - - $_->[X] += $bounding_box->center->[X] for @$polyline; -} - - -package Slic3r::Fill::HilbertCurve; -use Moo; -extends 'Slic3r::Fill::PlanePath'; -use Math::PlanePath::HilbertCurve; - - -package Slic3r::Fill::OctagramSpiral; -use Moo; -extends 'Slic3r::Fill::PlanePath'; -use Math::PlanePath::OctagramSpiral; - -sub multiplier () { sqrt(2) } - - - -1; diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm deleted file mode 100644 index 66fbde330..000000000 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ /dev/null @@ -1,175 +0,0 @@ -package Slic3r::Fill::Rectilinear; -use Moo; - -extends 'Slic3r::Fill::Base'; -with qw(Slic3r::Fill::WithDirection); - -has '_min_spacing' => (is => 'rw'); -has '_line_spacing' => (is => 'rw'); -has '_diagonal_distance' => (is => 'rw'); -has '_line_oscillation' => (is => 'rw'); - -use Slic3r::Geometry qw(scale unscale scaled_epsilon); -use Slic3r::Geometry::Clipper qw(intersection_pl); - -sub horizontal_lines { 0 } - -sub fill_surface { - my $self = shift; - my ($surface, %params) = @_; - - # rotate polygons so that we can work with vertical lines here - my $expolygon = $surface->expolygon->clone; - my $rotate_vector = $self->infill_direction($surface); - $self->rotate_points($expolygon, $rotate_vector); - - $self->_min_spacing(scale $self->spacing); - $self->_line_spacing($self->_min_spacing / $params{density}); - $self->_diagonal_distance($self->_line_spacing * 2); - $self->_line_oscillation($self->_line_spacing - $self->_min_spacing); # only for Line infill - my $bounding_box = $expolygon->bounding_box; - - # define flow spacing according to requested density - if ($params{density} == 1 && !$params{dont_adjust}) { - $self->_line_spacing($self->adjust_solid_spacing( - width => $bounding_box->size->x, - distance => $self->_line_spacing, - )); - $self->spacing(unscale $self->_line_spacing); - } else { - # extend bounding box so that our pattern will be aligned with other layers - $bounding_box->merge_point(Slic3r::Point->new( - $bounding_box->x_min - ($bounding_box->x_min % $self->_line_spacing), - $bounding_box->y_min - ($bounding_box->y_min % $self->_line_spacing), - )); - } - - # generate the basic pattern - my $x_max = $bounding_box->x_max + scaled_epsilon; - my @lines = (); - for (my $x = $bounding_box->x_min; $x <= $x_max; $x += $self->_line_spacing) { - push @lines, $self->_line($#lines, $x, $bounding_box->y_min, $bounding_box->y_max); - } - if ($self->horizontal_lines) { - my $y_max = $bounding_box->y_max + scaled_epsilon; - for (my $y = $bounding_box->y_min; $y <= $y_max; $y += $self->_line_spacing) { - push @lines, Slic3r::Polyline->new( - [$bounding_box->x_min, $y], - [$bounding_box->x_max, $y], - ); - } - } - - # clip paths against a slightly larger expolygon, so that the first and last paths - # are kept even if the expolygon has vertical sides - # the minimum offset for preventing edge lines from being clipped is scaled_epsilon; - # however we use a larger offset to support expolygons with slightly skewed sides and - # not perfectly straight - my @polylines = @{intersection_pl(\@lines, $expolygon->offset(+scale 0.02))}; - - my $extra = $self->_min_spacing * &Slic3r::INFILL_OVERLAP_OVER_SPACING; - foreach my $polyline (@polylines) { - my ($first_point, $last_point) = @$polyline[0,-1]; - if ($first_point->y > $last_point->y) { #> - ($first_point, $last_point) = ($last_point, $first_point); - } - $first_point->set_y($first_point->y - $extra); #-- - $last_point->set_y($last_point->y + $extra); #++ - } - - # connect lines - unless ($params{dont_connect} || !@polylines) { # prevent calling leftmost_point() on empty collections - # offset the expolygon by max(min_spacing/2, extra) - my ($expolygon_off) = @{$expolygon->offset_ex($self->_min_spacing/2)}; - my $collection = Slic3r::Polyline::Collection->new(@polylines); - @polylines = (); - - foreach my $polyline (@{$collection->chained_path_from($collection->leftmost_point, 0)}) { - if (@polylines) { - my $first_point = $polyline->first_point; - my $last_point = $polylines[-1]->last_point; - my @distance = map abs($first_point->$_ - $last_point->$_), qw(x y); - - # TODO: we should also check that both points are on a fill_boundary to avoid - # connecting paths on the boundaries of internal regions - if ($self->_can_connect(@distance) && $expolygon_off->contains_line(Slic3r::Line->new($last_point, $first_point))) { - $polylines[-1]->append_polyline($polyline); - next; - } - } - - # make a clone before $collection goes out of scope - push @polylines, $polyline->clone; - } - } - - # paths must be rotated back - $self->rotate_points_back(\@polylines, $rotate_vector); - - return @polylines; -} - -sub _line { - my ($self, $i, $x, $y_min, $y_max) = @_; - - return Slic3r::Polyline->new( - [$x, $y_min], - [$x, $y_max], - ); -} - -sub _can_connect { - my ($self, $dist_X, $dist_Y) = @_; - - return $dist_X <= $self->_diagonal_distance - && $dist_Y <= $self->_diagonal_distance; -} - - -package Slic3r::Fill::Line; -use Moo; -extends 'Slic3r::Fill::Rectilinear'; - -use Slic3r::Geometry qw(scaled_epsilon); - -sub _line { - my ($self, $i, $x, $y_min, $y_max) = @_; - - if ($i % 2) { - return Slic3r::Polyline->new( - [$x - $self->_line_oscillation, $y_min], - [$x + $self->_line_oscillation, $y_max], - ); - } else { - return Slic3r::Polyline->new( - [$x, $y_min], - [$x, $y_max], - ); - } -} - -sub _can_connect { - my ($self, $dist_X, $dist_Y) = @_; - - my $TOLERANCE = 10 * scaled_epsilon; - return ($dist_X >= ($self->_line_spacing - $self->_line_oscillation) - $TOLERANCE) - && ($dist_X <= ($self->_line_spacing + $self->_line_oscillation) + $TOLERANCE) - && $dist_Y <= $self->_diagonal_distance; -} - - -package Slic3r::Fill::Grid; -use Moo; -extends 'Slic3r::Fill::Rectilinear'; - -sub angles () { [0] } -sub horizontal_lines { 1 } - - -package Slic3r::Fill::AlignedRectilinear; -use Moo; -extends 'Slic3r::Fill::Rectilinear'; - -sub angles () { [0, 0] } - -1; diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 32a6e62fc..b5e8e42f7 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -29,15 +29,6 @@ sub regions { return [ map $self->get_region($_), 0..($self->region_count-1) ]; } -sub make_fill { - my ($self) = @_; - - foreach my $layerm (@{$self->regions}) { - $layerm->fills->clear; - $layerm->fills->append($_) for $self->object->fill_maker->make_fill($layerm); - } -} - package Slic3r::Layer::Support; our @ISA = qw(Slic3r::Layer); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 3f0b7f50d..da8487d9d 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -522,12 +522,12 @@ sub infill { thread_cb => sub { my $q = shift; while (defined (my $i = $q->dequeue)) { - $self->get_layer($i)->make_fill; + $self->get_layer($i)->make_fills; } }, no_threads_cb => sub { foreach my $layer (@{$self->layers}) { - $layer->make_fill; + $layer->make_fills; } }, ); diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index eea6397af..16c3b944c 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -558,11 +558,6 @@ sub generate_toolpaths { $pattern = 'honeycomb'; } - my %fillers = ( - interface => $object->fill_maker->filler('rectilinear'), - support => $object->fill_maker->filler($pattern), - ); - my $interface_angle = $self->object_config->support_material_angle + 90; my $interface_spacing = $self->object_config->support_material_interface_spacing + $interface_flow->spacing; my $interface_density = $interface_spacing == 0 ? 1 : $interface_flow->spacing / $interface_spacing; @@ -673,10 +668,20 @@ sub generate_toolpaths { $layer->support_interface_fills->append(@loops); } + # Allocate the fillers exclusively in the worker threads! Don't allocate them at the main thread, + # as Perl copies the C++ pointers by default, so then the C++ objects are shared between threads! + my %fillers = ( + interface => Slic3r::Filler->new_from_type('rectilinear'), + support => Slic3r::Filler->new_from_type($pattern), + ); + my $bounding_box = $object->bounding_box; + $fillers{interface}->set_bounding_box($object->bounding_box); + $fillers{support}->set_bounding_box($object->bounding_box); + # interface and contact infill if (@$interface || @$contact_infill) { - $fillers{interface}->angle($interface_angle); - $fillers{interface}->spacing($_interface_flow->spacing); + $fillers{interface}->set_angle($interface_angle); + $fillers{interface}->set_spacing($_interface_flow->spacing); # find centerline of the external loop $interface = offset2($interface, +scaled_epsilon, -(scaled_epsilon + $_interface_flow->scaled_width/2)); @@ -702,7 +707,7 @@ sub generate_toolpaths { my @paths = (); foreach my $expolygon (@{union_ex($interface)}) { - my @p = $fillers{interface}->fill_surface( + my $p = $fillers{interface}->fill_surface( Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL), density => $interface_density, layer_height => $layer->height, @@ -716,7 +721,7 @@ sub generate_toolpaths { mm3_per_mm => $mm3_per_mm, width => $_interface_flow->width, height => $layer->height, - ), @p; + ), @$p; } $layer->support_interface_fills->append(@paths); @@ -725,11 +730,11 @@ sub generate_toolpaths { # support or flange if (@$base) { my $filler = $fillers{support}; - $filler->angle($angles[ ($layer_id) % @angles ]); + $filler->set_angle($angles[ ($layer_id) % @angles ]); # We don't use $base_flow->spacing because we need a constant spacing # value that guarantees that all layers are correctly aligned. - $filler->spacing($flow->spacing); + $filler->set_spacing($flow->spacing); my $density = $support_density; my $base_flow = $_flow; @@ -742,13 +747,13 @@ sub generate_toolpaths { # base flange if ($layer_id == 0) { $filler = $fillers{interface}; - $filler->angle($self->object_config->support_material_angle + 90); + $filler->set_angle($self->object_config->support_material_angle + 90); $density = 0.5; $base_flow = $self->first_layer_flow; # use the proper spacing for first layer as we don't need to align # its pattern to the other layers - $filler->spacing($base_flow->spacing); + $filler->set_spacing($base_flow->spacing); } else { # draw a perimeter all around support infill # TODO: use brim ordering algorithm @@ -767,7 +772,7 @@ sub generate_toolpaths { my $mm3_per_mm = $base_flow->mm3_per_mm; foreach my $expolygon (@$to_infill) { - my @p = $filler->fill_surface( + my $p = $filler->fill_surface( Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL), density => $density, layer_height => $layer->height, @@ -780,7 +785,7 @@ sub generate_toolpaths { mm3_per_mm => $mm3_per_mm, width => $base_flow->width, height => $layer->height, - ), @p; + ), @$p; } $layer->support_fills->append(@paths); diff --git a/t/fill.t b/t/fill.t index 6fb05196e..af17c86ff 100644 --- a/t/fill.t +++ b/t/fill.t @@ -11,7 +11,7 @@ BEGIN { use List::Util qw(first sum); use Slic3r; -use Slic3r::Geometry qw(X Y scale unscale convex_hull); +use Slic3r::Geometry qw(PI X Y scale unscale convex_hull); use Slic3r::Geometry::Clipper qw(union diff diff_ex offset offset2_ex); use Slic3r::Surface qw(:types); use Slic3r::Test; @@ -20,25 +20,17 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } { my $print = Slic3r::Print->new; - my $filler = Slic3r::Fill::Rectilinear->new( - print => $print, - bounding_box => Slic3r::Geometry::BoundingBox->new_from_points([ Slic3r::Point->new(0, 0), Slic3r::Point->new(10, 10) ]), - ); my $surface_width = 250; - my $distance = $filler->adjust_solid_spacing( - width => $surface_width, - distance => 100, - ); - is $distance, 125, 'adjusted solid distance'; + my $distance = Slic3r::Filler::adjust_solid_spacing($surface_width, 47); + is $distance, 50, 'adjusted solid distance'; is $surface_width % $distance, 0, 'adjusted solid distance'; } { my $expolygon = Slic3r::ExPolygon->new([ scale_points [0,0], [50,0], [50,50], [0,50] ]); - my $filler = Slic3r::Fill::Rectilinear->new( - bounding_box => $expolygon->bounding_box, - angle => 0, - ); + my $filler = Slic3r::Filler->new_from_type('rectilinear'); + $filler->set_bounding_box($expolygon->bounding_box); + $filler->set_angle(0); my $surface = Slic3r::Surface->new( surface_type => S_TYPE_TOP, expolygon => $expolygon, @@ -48,11 +40,11 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } height => 0.4, nozzle_diameter => 0.50, ); - $filler->spacing($flow->spacing); + $filler->set_spacing($flow->spacing); foreach my $angle (0, 45) { $surface->expolygon->rotate(Slic3r::Geometry::deg2rad($angle), [0,0]); - my @paths = $filler->fill_surface($surface, layer_height => 0.4, density => 0.4); - is scalar @paths, 1, 'one continuous path'; + my $paths = $filler->fill_surface($surface, layer_height => 0.4, density => 0.4); + is scalar @$paths, 1, 'one continuous path'; } } @@ -60,10 +52,9 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } my $test = sub { my ($expolygon, $flow_spacing, $angle, $density) = @_; - my $filler = Slic3r::Fill::Rectilinear->new( - bounding_box => $expolygon->bounding_box, - angle => $angle // 0, - ); + my $filler = Slic3r::Filler->new_from_type('rectilinear'); + $filler->set_bounding_box($expolygon->bounding_box); + $filler->set_angle($angle // 0); my $surface = Slic3r::Surface->new( surface_type => S_TYPE_BOTTOM, expolygon => $expolygon, @@ -73,15 +64,15 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } height => 0.4, nozzle_diameter => $flow_spacing, ); - $filler->spacing($flow->spacing); - my @paths = $filler->fill_surface( + $filler->set_spacing($flow->spacing); + my $paths = $filler->fill_surface( $surface, layer_height => $flow->height, density => $density // 1, ); # check whether any part was left uncovered - my @grown_paths = map @{Slic3r::Polyline->new(@$_)->grow(scale $filler->spacing/2)}, @paths; + my @grown_paths = map @{Slic3r::Polyline->new(@$_)->grow(scale $filler->spacing/2)}, @$paths; my $uncovered = diff_ex([ @$expolygon ], [ @grown_paths ], 1); # ignore very small dots @@ -93,8 +84,9 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } require "Slic3r/SVG.pm"; Slic3r::SVG::output( "uncovered.svg", - expolygons => [$expolygon], - red_expolygons => $uncovered, + expolygons => [$expolygon], + red_expolygons => $uncovered, + polylines => $paths, ); exit; } @@ -116,7 +108,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } $expolygon = Slic3r::ExPolygon->new( [[59515297,5422499],[59531249,5578697],[59695801,6123186],[59965713,6630228],[60328214,7070685],[60773285,7434379],[61274561,7702115],[61819378,7866770],[62390306,7924789],[62958700,7866744],[63503012,7702244],[64007365,7434357],[64449960,7070398],[64809327,6634999],[65082143,6123325],[65245005,5584454],[65266967,5422499],[66267307,5422499],[66269190,8310081],[66275379,17810072],[66277259,20697500],[65267237,20697500],[65245004,20533538],[65082082,19994444],[64811462,19488579],[64450624,19048208],[64012101,18686514],[63503122,18415781],[62959151,18251378],[62453416,18198442],[62390147,18197355],[62200087,18200576],[61813519,18252990],[61274433,18415918],[60768598,18686517],[60327567,19047892],[59963609,19493297],[59695865,19994587],[59531222,20539379],[59515153,20697500],[58502480,20697500],[58502480,5422499]] ); - $test->($expolygon, 0.524341649025257); + $test->($expolygon, 0.524341649025257, PI/2); $expolygon = Slic3r::ExPolygon->new([ scale_points [0,0], [98,0], [98,10], [0,10] ]); $test->($expolygon, 0.5, 45, 0.99); # non-solid infill diff --git a/utils/package_win32.ps1 b/utils/package_win32.ps1 index 0acbed6a2..f4e0bf250 100644 --- a/utils/package_win32.ps1 +++ b/utils/package_win32.ps1 @@ -97,17 +97,6 @@ pp ` -M LWP::Protocol::http ` -M LWP::UserAgent ` -M List::Util ` --M Math::Libm ` --M Math::PlanePath ` --M Math::PlanePath::ArchimedeanChords ` --M Math::PlanePath::Base::Digits ` --M Math::PlanePath::Base::Generic ` --M Math::PlanePath::Base::NSEW ` --M Math::PlanePath::Flowsnake ` --M Math::PlanePath::FlowsnakeCentres ` --M Math::PlanePath::HilbertCurve ` --M Math::PlanePath::OctagramSpiral ` --M Math::PlanePath::SacksSpiral ` -M Math::Trig ` -M Method::Generate::Accessor ` -M Method::Generate::BuildAll ` diff --git a/xs/Build.PL b/xs/Build.PL index fc82615cd..7d2556da8 100644 --- a/xs/Build.PL +++ b/xs/Build.PL @@ -154,6 +154,9 @@ if ($ENV{SLIC3R_DEBUG}) { # only on newer GCCs: -ftemplate-backtrace-limit=0 push @cflags, '-DSLIC3R_DEBUG'; push @cflags, $cpp_guess->is_msvc ? '-Gd' : '-g'; +} else { + # Disable asserts in the release builds. + push @cflags, '-DNDEBUG'; } if ($cpp_guess->is_gcc) { # check whether we're dealing with a buggy GCC version diff --git a/xs/MANIFEST b/xs/MANIFEST index 9c3154826..e3c4b9665 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -28,6 +28,22 @@ src/libslic3r/ExtrusionEntity.cpp src/libslic3r/ExtrusionEntity.hpp src/libslic3r/ExtrusionEntityCollection.cpp src/libslic3r/ExtrusionEntityCollection.hpp +src/libslic3r/Fill/Fill.cpp +src/libslic3r/Fill/Fill.hpp +src/libslic3r/Fill/FillBase.cpp +src/libslic3r/Fill/FillBase.hpp +src/libslic3r/Fill/FillConcentric.cpp +src/libslic3r/Fill/FillConcentric.hpp +src/libslic3r/Fill/FillHoneycomb.cpp +src/libslic3r/Fill/FillHoneycomb.hpp +src/libslic3r/Fill/Fill3DHoneycomb.cpp +src/libslic3r/Fill/Fill3DHoneycomb.hpp +src/libslic3r/Fill/FillPlanePath.cpp +src/libslic3r/Fill/FillPlanePath.hpp +src/libslic3r/Fill/FillRectilinear.cpp +src/libslic3r/Fill/FillRectilinear.hpp +src/libslic3r/Fill/FillRectilinear2.cpp +src/libslic3r/Fill/FillRectilinear2.hpp src/libslic3r/Flow.cpp src/libslic3r/Flow.hpp src/libslic3r/GCode.cpp @@ -136,6 +152,7 @@ xsp/Extruder.xsp xsp/ExtrusionEntityCollection.xsp xsp/ExtrusionLoop.xsp xsp/ExtrusionPath.xsp +xsp/Filler.xsp xsp/Flow.xsp xsp/GCode.xsp xsp/GCodeSender.xsp diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index 4f53966de..364264d67 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -123,6 +123,17 @@ sub clone { ); } +package Slic3r::Filler; + +sub fill_surface { + my ($self, $surface, %args) = @_; + $self->set_density($args{density}) if defined($args{density}); + $self->set_dont_connect($args{dont_connect}) if defined($args{dont_connect}); + $self->set_dont_adjust($args{dont_adjust}) if defined($args{dont_adjust}); + $self->set_complete($args{complete}) if defined($args{complete}); + return $self->_fill_surface($surface); +} + package Slic3r::Flow; sub new { @@ -215,6 +226,7 @@ for my $class (qw( Slic3r::ExtrusionLoop Slic3r::ExtrusionPath Slic3r::ExtrusionPath::Collection + Slic3r::Filler Slic3r::Flow Slic3r::GCode Slic3r::GCode::AvoidCrossingPerimeters diff --git a/xs/src/libslic3r/BoundingBox.cpp b/xs/src/libslic3r/BoundingBox.cpp index 3ec2258a9..809f8925c 100644 --- a/xs/src/libslic3r/BoundingBox.cpp +++ b/xs/src/libslic3r/BoundingBox.cpp @@ -68,6 +68,26 @@ BoundingBox::polygon() const return p; } +BoundingBox BoundingBox::rotated(double angle) const +{ + BoundingBox out; + out.merge(this->min.rotated(angle)); + out.merge(this->max.rotated(angle)); + out.merge(Point(this->min.x, this->max.y).rotated(angle)); + out.merge(Point(this->max.x, this->min.y).rotated(angle)); + return out; +} + +BoundingBox BoundingBox::rotated(double angle, const Point ¢er) const +{ + BoundingBox out; + out.merge(this->min.rotated(angle, center)); + out.merge(this->max.rotated(angle, center)); + out.merge(Point(this->min.x, this->max.y).rotated(angle, center)); + out.merge(Point(this->max.x, this->min.y).rotated(angle, center)); + return out; +} + template void BoundingBoxBase::scale(double factor) { diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp index f5260ee5f..71d0fa0b6 100644 --- a/xs/src/libslic3r/BoundingBox.hpp +++ b/xs/src/libslic3r/BoundingBox.hpp @@ -55,6 +55,14 @@ class BoundingBox : public BoundingBoxBase public: void polygon(Polygon* polygon) const; Polygon polygon() const; + BoundingBox rotated(double angle) const; + BoundingBox rotated(double angle, const Point ¢er) const; + void rotate(double angle) { + *this = this->rotated(angle); + } + void rotate(double angle, const Point ¢er) { + *this = this->rotated(angle, center); + } BoundingBox() : BoundingBoxBase() {}; BoundingBox(const Point &pmin, const Point &pmax) : BoundingBoxBase(pmin, pmax) {}; @@ -92,16 +100,6 @@ inline bool operator!=(const BoundingBoxBase &bb1, const BoundingBoxBase return !(bb1 == bb2); } -template -inline bool empty(const BoundingBoxBase &bb) -{ - return bb.min.x > bb.max.y || bb.min.y > bb.max.y; -} - -template -inline bool empty(const BoundingBox3Base &bb) -{ - return bb.min.x > bb.max.x || bb.min.y > bb.max.y || bb.min.z > bb.max.z;} } #endif diff --git a/xs/src/libslic3r/ExPolygon.cpp b/xs/src/libslic3r/ExPolygon.cpp index c9bb08048..19c3184f3 100644 --- a/xs/src/libslic3r/ExPolygon.cpp +++ b/xs/src/libslic3r/ExPolygon.cpp @@ -52,6 +52,15 @@ ExPolygon::translate(double x, double y) } } +void +ExPolygon::rotate(double angle) +{ + contour.rotate(angle); + for (Polygons::iterator it = holes.begin(); it != holes.end(); ++it) { + (*it).rotate(angle); + } +} + void ExPolygon::rotate(double angle, const Point ¢er) { @@ -497,15 +506,4 @@ ExPolygon::dump_perl() const return ret.str(); } -Polygons -to_polygons(const ExPolygons &expolygons) -{ - Slic3r::Polygons pp; - for (ExPolygons::const_iterator ex = expolygons.begin(); ex != expolygons.end(); ++ex) { - Slic3r::Polygons ppp = *ex; - pp.insert(pp.end(), ppp.begin(), ppp.end()); - } - return pp; -} - } diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp index a9c86bf66..0b4140e90 100644 --- a/xs/src/libslic3r/ExPolygon.hpp +++ b/xs/src/libslic3r/ExPolygon.hpp @@ -20,6 +20,7 @@ class ExPolygon operator Polygons() const; void scale(double factor); void translate(double x, double y); + void rotate(double angle); void rotate(double angle, const Point ¢er); double area() const; bool is_valid() const; @@ -45,7 +46,14 @@ class ExPolygon std::string dump_perl() const; }; -Polygons to_polygons(const ExPolygons &expolygons); +inline Polygons +to_polygons(const ExPolygons &expolygons) +{ + Polygons pp; + for (ExPolygons::const_iterator ex = expolygons.begin(); ex != expolygons.end(); ++ex) + append_to(pp, (Polygons)*ex); + return pp; +} } diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.cpp b/xs/src/libslic3r/ExtrusionEntityCollection.cpp index 31aa3d4c8..1d16a5f50 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.cpp @@ -101,6 +101,17 @@ ExtrusionEntityCollection::append(const ExtrusionPaths &paths) this->append(*path); } +void +ExtrusionEntityCollection::append(const Polylines &polylines, const ExtrusionPath &templ) +{ + this->entities.reserve(this->entities.size() + polylines.size()); + for (Polylines::const_iterator it_polyline = polylines.begin(); it_polyline != polylines.end(); ++ it_polyline) { + ExtrusionPath *path = templ.clone(); + path->polyline = *it_polyline; + this->entities.push_back(path); + } +} + void ExtrusionEntityCollection::replace(size_t i, const ExtrusionEntity &entity) { diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp index 7e44ccd18..491411aa7 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp @@ -3,6 +3,7 @@ #include "libslic3r.h" #include "ExtrusionEntity.hpp" +#include "Polyline.hpp" namespace Slic3r { @@ -36,6 +37,7 @@ class ExtrusionEntityCollection : public ExtrusionEntity void append(const ExtrusionEntity &entity); void append(const ExtrusionEntitiesPtr &entities); void append(const ExtrusionPaths &paths); + void append(const Polylines &polylines, const ExtrusionPath &templ); void replace(size_t i, const ExtrusionEntity &entity); void remove(size_t i); ExtrusionEntityCollection chained_path(bool no_reverse = false, std::vector* orig_indices = NULL) const; diff --git a/xs/src/libslic3r/Fill/Fill.cpp b/xs/src/libslic3r/Fill/Fill.cpp new file mode 100644 index 000000000..5a0897413 --- /dev/null +++ b/xs/src/libslic3r/Fill/Fill.cpp @@ -0,0 +1,290 @@ +#include +#include +#include + +#include "../ClipperUtils.hpp" +#include "../Geometry.hpp" +#include "../Layer.hpp" +#include "../Print.hpp" +#include "../PrintConfig.hpp" +#include "../Surface.hpp" +#include "../SurfaceCollection.hpp" + +#include "FillBase.hpp" + +namespace Slic3r { + +struct SurfaceGroupAttrib +{ + SurfaceGroupAttrib() : is_solid(false), fw(0.f), pattern(-1) {} + bool operator==(const SurfaceGroupAttrib &other) const + { return is_solid == other.is_solid && fw == other.fw && pattern == other.pattern; } + bool is_solid; + float fw; + // pattern is of type InfillPattern, -1 for an unset pattern. + int pattern; +}; + +// Generate infills for a LayerRegion. +// The LayerRegion at this point of time may contain +// surfaces of various types (internal/bridge/top/bottom/solid). +// The infills are generated on the groups of surfaces with a compatible type. +// Returns an array of ExtrusionPathCollection objects containing the infills generated now +// and the thin fills generated by generate_perimeters(). +void make_fill(const LayerRegion &layerm, ExtrusionEntityCollection* out) +{ +// Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id; + + double fill_density = layerm.region()->config.fill_density; + const Flow infill_flow = layerm.flow(frInfill); + const Flow solid_infill_flow = layerm.flow(frSolidInfill); + const Flow top_solid_infill_flow = layerm.flow(frTopSolidInfill); + + SurfaceCollection surfaces; + + // merge adjacent surfaces + // in case of bridge surfaces, the ones with defined angle will be attached to the ones + // without any angle (shouldn't this logic be moved to process_external_surfaces()?) + { + Polygons polygons_bridged; + polygons_bridged.reserve(layerm.fill_surfaces.surfaces.size()); + for (Surfaces::const_iterator it = layerm.fill_surfaces.surfaces.begin(); it != layerm.fill_surfaces.surfaces.end(); ++it) + if (it->bridge_angle >= 0) + append_to(polygons_bridged, (Polygons)*it); + + // group surfaces by distinct properties (equal surface_type, thickness, thickness_layers, bridge_angle) + // group is of type SurfaceCollection + // FIXME: Use some smart heuristics to merge similar surfaces to eliminate tiny regions. + std::vector groups; + layerm.fill_surfaces.group(&groups); + + // merge compatible groups (we can generate continuous infill for them) + { + // cache flow widths and patterns used for all solid groups + // (we'll use them for comparing compatible groups) + std::vector group_attrib(groups.size()); + for (size_t i = 0; i < groups.size(); ++i) { + // we can only merge solid non-bridge surfaces, so discard + // non-solid surfaces + const Surface &surface = *groups[i].front(); + if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) { + group_attrib[i].is_solid = true; + group_attrib[i].fw = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width; + group_attrib[i].pattern = surface.is_external() ? layerm.region()->config.external_fill_pattern.value : ipRectilinear; + } + } + // Loop through solid groups, find compatible groups and append them to this one. + for (size_t i = 0; i < groups.size(); ++i) { + if (!group_attrib[i].is_solid) + continue; + for (size_t j = i + 1; j < groups.size();) { + if (group_attrib[i] == group_attrib[j]) { + // groups are compatible, merge them + append_to(groups[i], groups[j]); + groups.erase(groups.begin() + j); + group_attrib.erase(group_attrib.begin() + j); + } else { + ++j; + } + } + } + } + + // Give priority to bridges. Process the bridges in the first round, the rest of the surfaces in the 2nd round. + for (size_t round = 0; round < 2; ++ round) { + for (std::vector::const_iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) { + const SurfacesConstPtr &group = *it_group; + bool is_bridge = group.front()->bridge_angle >= 0; + if (is_bridge != (round == 0)) + continue; + + // Make a union of polygons defining the infiill regions of a group, use a safety offset. + Polygons union_p = union_(to_polygons(group), true); + + // Subtract surfaces having a defined bridge_angle from any other, use a safety offset. + if (!polygons_bridged.empty() && !is_bridge) + union_p = diff(union_p, polygons_bridged, true); + + // subtract any other surface already processed + //FIXME Vojtech: Because the bridge surfaces came first, they are subtracted twice! + surfaces.append( + diff_ex(union_p, to_polygons(surfaces), true), + *group.front() // template + ); + } + } + } + + // we need to detect any narrow surfaces that might collapse + // when adding spacing below + // such narrow surfaces are often generated in sloping walls + // by bridge_over_infill() and combine_infill() as a result of the + // subtraction of the combinable area from the layer infill area, + // which leaves small areas near the perimeters + // we are going to grow such regions by overlapping them with the void (if any) + // TODO: detect and investigate whether there could be narrow regions without + // any void neighbors + { + coord_t distance_between_surfaces = std::max( + std::max(infill_flow.scaled_spacing(), solid_infill_flow.scaled_spacing()), + top_solid_infill_flow.scaled_spacing() + ); + + Polygons surfaces_polygons = (Polygons)surfaces; + Polygons collapsed = diff( + surfaces_polygons, + offset2(surfaces_polygons, -distance_between_surfaces/2, +distance_between_surfaces/2), + true + ); + + Polygons to_subtract; + surfaces.filter_by_type(stInternalVoid, &to_subtract); + + append_to(to_subtract, collapsed); + surfaces.append( + intersection_ex( + offset(collapsed, distance_between_surfaces), + to_subtract, + true + ), + stInternalSolid + ); + } + + if (false) { +// require "Slic3r/SVG.pm"; +// Slic3r::SVG::output("fill_" . $layerm->print_z . ".svg", +// expolygons => [ map $_->expolygon, grep !$_->is_solid, @surfaces ], +// red_expolygons => [ map $_->expolygon, grep $_->is_solid, @surfaces ], +// ); + } + + for (Surfaces::const_iterator surface_it = surfaces.surfaces.begin(); + surface_it != surfaces.surfaces.end(); ++surface_it) { + + const Surface &surface = *surface_it; + if (surface.surface_type == stInternalVoid) + continue; + + InfillPattern fill_pattern = layerm.region()->config.fill_pattern.value; + double density = fill_density; + FlowRole role = (surface.surface_type == stTop) ? frTopSolidInfill + : surface.is_solid() ? frSolidInfill + : frInfill; + const bool is_bridge = layerm.layer()->id() > 0 && surface.is_bridge(); + + if (surface.is_solid()) { + density = 100.; + fill_pattern = (surface.is_external() && !is_bridge) + ? layerm.region()->config.external_fill_pattern.value + : ipRectilinear; + } else if (density <= 0) + continue; + + // get filler object + #if SLIC3R_CPPVER >= 11 + std::unique_ptr f = std::unique_ptr(Fill::new_from_type(fill_pattern)); + #else + std::auto_ptr f = std::auto_ptr(Fill::new_from_type(fill_pattern)); + #endif + f->set_bounding_box(layerm.layer()->object()->bounding_box()); + + // calculate the actual flow we'll be using for this infill + coordf_t h = (surface.thickness == -1) ? layerm.layer()->height : surface.thickness; + Flow flow = layerm.region()->flow( + role, + h, + is_bridge || f->use_bridge_flow(), // bridge flow? + layerm.layer()->id() == 0, // first layer? + -1, // auto width + *layerm.layer()->object() + ); + + // calculate flow spacing for infill pattern generation + bool using_internal_flow = false; + if (!surface.is_solid() && !is_bridge) { + // it's internal infill, so we can calculate a generic flow spacing + // for all layers, for avoiding the ugly effect of + // misaligned infill on first layer because of different extrusion width and + // layer height + Flow internal_flow = layerm.region()->flow( + frInfill, + layerm.layer()->object()->config.layer_height.value, // TODO: handle infill_every_layers? + false, // no bridge + false, // no first layer + -1, // auto width + *layerm.layer()->object() + ); + f->spacing = internal_flow.spacing(); + using_internal_flow = true; + } else { + f->spacing = flow.spacing(); + } + + f->layer_id = layerm.layer()->id(); + f->z = layerm.layer()->print_z; + f->angle = Geometry::deg2rad(layerm.region()->config.fill_angle.value); + + // Maximum length of the perimeter segment linking two infill lines. + f->link_max_length = (!is_bridge && density > 80) + ? scale_(3 * f->spacing) + : 0; + + // Used by the concentric infill pattern to clip the loops to create extrusion paths. + f->loop_clipping = scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER; + + // apply half spacing using this flow's own spacing and generate infill + FillParams params; + params.density = density/100; + params.dont_adjust = false; + Polylines polylines = f->fill_surface(surface, params); + if (polylines.empty()) + continue; + + // calculate actual flow from spacing (which might have been adjusted by the infill + // pattern generator) + if (using_internal_flow) { + // if we used the internal flow we're not doing a solid infill + // so we can safely ignore the slight variation that might have + // been applied to f->spacing + } else { + flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow()); + } + + // Save into layer. + ExtrusionEntityCollection* coll = new ExtrusionEntityCollection(); + coll->no_sort = f->no_sort(); + out->entities.push_back(coll); + + { + ExtrusionRole role; + if (is_bridge) { + role = erBridgeInfill; + } else if (surface.is_solid()) { + role = (surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill; + } else { + role = erInternalInfill; + } + + ExtrusionPath templ(role); + templ.mm3_per_mm = flow.mm3_per_mm(); + templ.width = flow.width; + templ.height = flow.height; + + coll->append(STDMOVE(polylines), templ); + } + } + + // add thin fill regions + // thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection + // Unpacks the collection, creates multiple collections per path so that they will + // be individually included in the nearest neighbor search. + // The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection. + for (ExtrusionEntitiesPtr::const_iterator thin_fill = layerm.thin_fills.entities.begin(); thin_fill != layerm.thin_fills.entities.end(); ++ thin_fill) { + ExtrusionEntityCollection* coll = new ExtrusionEntityCollection(); + out->entities.push_back(coll); + coll->entities.push_back((*thin_fill)->clone()); + } +} + +} // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/Fill.hpp b/xs/src/libslic3r/Fill/Fill.hpp new file mode 100644 index 000000000..58e446933 --- /dev/null +++ b/xs/src/libslic3r/Fill/Fill.hpp @@ -0,0 +1,23 @@ +#ifndef slic3r_Fill_hpp_ +#define slic3r_Fill_hpp_ + +#include +#include +#include + +#include "../libslic3r.h" +#include "../BoundingBox.hpp" +#include "../PrintConfig.hpp" + +#include "FillBase.hpp" + +namespace Slic3r { + +class ExtrusionEntityCollection; +class LayerRegion; + +void make_fill(const LayerRegion &layerm, ExtrusionEntityCollection* out); + +} // namespace Slic3r + +#endif // slic3r_Fill_hpp_ diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp new file mode 100644 index 000000000..e23f77b2c --- /dev/null +++ b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -0,0 +1,227 @@ +#include "../ClipperUtils.hpp" +#include "../PolylineCollection.hpp" +#include "../Surface.hpp" + +#include "Fill3DHoneycomb.hpp" + +namespace Slic3r { + +/* +Creates a contiguous sequence of points at a specified height that make +up a horizontal slice of the edges of a space filling truncated +octahedron tesselation. The octahedrons are oriented so that the +square faces are in the horizontal plane with edges parallel to the X +and Y axes. + +Credits: David Eccles (gringer). +*/ + +// Generate an array of points that are in the same direction as the +// basic printing line (i.e. Y points for columns, X points for rows) +// Note: a negative offset only causes a change in the perpendicular +// direction +static std::vector +colinearPoints(const coordf_t offset, const size_t baseLocation, size_t gridLength) +{ + const coordf_t offset2 = std::abs(offset / coordf_t(2.)); + std::vector points; + points.push_back(baseLocation - offset2); + for (size_t i = 0; i < gridLength; ++i) { + points.push_back(baseLocation + i + offset2); + points.push_back(baseLocation + i + 1 - offset2); + } + points.push_back(baseLocation + gridLength + offset2); + return points; +} + +// Generate an array of points for the dimension that is perpendicular to +// the basic printing line (i.e. X points for columns, Y points for rows) +static std::vector +perpendPoints(const coordf_t offset, const size_t baseLocation, size_t gridLength) +{ + const coordf_t offset2 = offset / coordf_t(2.); + coord_t side = 2 * (baseLocation & 1) - 1; + std::vector points; + points.push_back(baseLocation - offset2 * side); + for (size_t i = 0; i < gridLength; ++i) { + side = 2*((i+baseLocation) & 1) - 1; + points.push_back(baseLocation + offset2 * side); + points.push_back(baseLocation + offset2 * side); + } + points.push_back(baseLocation - offset2 * side); + return points; +} + +template +static inline T +clamp(T low, T high, T x) +{ + return std::max(low, std::min(high, x)); +} + +// Trims an array of points to specified rectangular limits. Point +// components that are outside these limits are set to the limits. +static inline void +trim(Pointfs &pts, coordf_t minX, coordf_t minY, coordf_t maxX, coordf_t maxY) +{ + for (Pointfs::iterator it = pts.begin(); it != pts.end(); ++ it) { + it->x = clamp(minX, maxX, it->x); + it->y = clamp(minY, maxY, it->y); + } +} + +static inline Pointfs +zip(const std::vector &x, const std::vector &y) +{ + assert(x.size() == y.size()); + Pointfs out; + out.reserve(x.size()); + for (size_t i = 0; i < x.size(); ++ i) + out.push_back(Pointf(x[i], y[i])); + return out; +} + +// Generate a set of curves (array of array of 2d points) that describe a +// horizontal slice of a truncated regular octahedron with edge length 1. +// curveType specifies which lines to print, 1 for vertical lines +// (columns), 2 for horizontal lines (rows), and 3 for both. +static std::vector +makeNormalisedGrid(coordf_t z, size_t gridWidth, size_t gridHeight, size_t curveType) +{ + // offset required to create a regular octagram + coordf_t octagramGap = coordf_t(0.5); + + // sawtooth wave function for range f($z) = [-$octagramGap .. $octagramGap] + coordf_t a = std::sqrt(coordf_t(2.)); // period + coordf_t wave = fabs(fmod(z, a) - a/2.)/a*4. - 1.; + coordf_t offset = wave * octagramGap; + + std::vector points; + if ((curveType & 1) != 0) { + for (size_t x = 0; x <= gridWidth; ++x) { + points.push_back(Pointfs()); + Pointfs &newPoints = points.back(); + newPoints = zip( + perpendPoints(offset, x, gridHeight), + colinearPoints(offset, 0, gridHeight)); + // trim points to grid edges + trim(newPoints, coordf_t(0.), coordf_t(0.), coordf_t(gridWidth), coordf_t(gridHeight)); + if (x & 1) + std::reverse(newPoints.begin(), newPoints.end()); + } + } + if ((curveType & 2) != 0) { + for (size_t y = 0; y <= gridHeight; ++y) { + points.push_back(Pointfs()); + Pointfs &newPoints = points.back(); + newPoints = zip( + colinearPoints(offset, 0, gridWidth), + perpendPoints(offset, y, gridWidth) + ); + // trim points to grid edges + trim(newPoints, coordf_t(0.), coordf_t(0.), coordf_t(gridWidth), coordf_t(gridHeight)); + if (y & 1) + std::reverse(newPoints.begin(), newPoints.end()); + } + } + return points; +} + +// Generate a set of curves (array of array of 2d points) that describe a +// horizontal slice of a truncated regular octahedron with a specified +// grid square size. +static Polylines +makeGrid(coord_t z, coord_t gridSize, size_t gridWidth, size_t gridHeight, size_t curveType) +{ + coord_t scaleFactor = gridSize; + coordf_t normalisedZ = coordf_t(z) / coordf_t(scaleFactor); + std::vector polylines = makeNormalisedGrid(normalisedZ, gridWidth, gridHeight, curveType); + Polylines result; + result.reserve(polylines.size()); + for (std::vector::const_iterator it_polylines = polylines.begin(); it_polylines != polylines.end(); ++ it_polylines) { + result.push_back(Polyline()); + Polyline &polyline = result.back(); + for (Pointfs::const_iterator it = it_polylines->begin(); it != it_polylines->end(); ++ it) + polyline.points.push_back(Point(coord_t(it->x * scaleFactor), coord_t(it->y * scaleFactor))); + } + return result; +} + +void +Fill3DHoneycomb::_fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out) +{ + // no rotation is supported for this infill pattern + BoundingBox bb = expolygon.contour.bounding_box(); + const coord_t distance = coord_t(scale_(this->spacing) / params.density); + + // align bounding box to a multiple of our honeycomb grid module + // (a module is 2*$distance since one $distance half-module is + // growing while the other $distance half-module is shrinking) + bb.min.align_to_grid(Point(2*distance, 2*distance)); + + // generate pattern + Polylines polylines = makeGrid( + scale_(this->z), + distance, + ceil(bb.size().x / distance) + 1, + ceil(bb.size().y / distance) + 1, + ((this->layer_id/thickness_layers) % 2) + 1 + ); + + // move pattern in place + for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) + it->translate(bb.min.x, bb.min.y); + + // clip pattern to boundaries + polylines = intersection_pl(polylines, (Polygons)expolygon); + + // connect lines + if (!params.dont_connect && !polylines.empty()) { // prevent calling leftmost_point() on empty collections + ExPolygon expolygon_off; + { + ExPolygons expolygons_off = offset_ex(expolygon, SCALED_EPSILON); + if (!expolygons_off.empty()) { + // When expanding a polygon, the number of islands could only shrink. Therefore the offset_ex shall generate exactly one expanded island for one input island. + assert(expolygons_off.size() == 1); + std::swap(expolygon_off, expolygons_off.front()); + } + } + Polylines chained = PolylineCollection::chained_path_from( + STDMOVE(polylines), + PolylineCollection::leftmost_point(polylines), + false // reverse allowed + ); + bool first = true; + for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) { + if (!first) { + // Try to connect the lines. + Points &pts_end = polylines_out->back().points; + const Point &first_point = it_polyline->points.front(); + const Point &last_point = pts_end.back(); + // TODO: we should also check that both points are on a fill_boundary to avoid + // connecting paths on the boundaries of internal regions + if (first_point.distance_to(last_point) <= 1.5 * distance && + expolygon_off.contains(Line(last_point, first_point))) { + // Append the polyline. + pts_end.insert(pts_end.end(), it_polyline->points.begin(), it_polyline->points.end()); + continue; + } + } + // The lines cannot be connected. + #if SLIC3R_CPPVER >= 11 + polylines_out->push_back(std::move(*it_polyline)); + #else + polylines_out->push_back(Polyline()); + std::swap(polylines_out->back(), *it_polyline); + #endif + first = false; + } + } +} + +} // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp b/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp new file mode 100644 index 000000000..89b781e1e --- /dev/null +++ b/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp @@ -0,0 +1,31 @@ +#ifndef slic3r_Fill3DHoneycomb_hpp_ +#define slic3r_Fill3DHoneycomb_hpp_ + +#include + +#include "../libslic3r.h" + +#include "FillBase.hpp" + +namespace Slic3r { + +class Fill3DHoneycomb : public Fill +{ +public: + virtual ~Fill3DHoneycomb() {} + + // require bridge flow since most of this pattern hangs in air + virtual bool use_bridge_flow() const { return true; } + +protected: + virtual void _fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out); +}; + +} // namespace Slic3r + +#endif // slic3r_Fill3DHoneycomb_hpp_ diff --git a/xs/src/libslic3r/Fill/FillBase.cpp b/xs/src/libslic3r/Fill/FillBase.cpp new file mode 100644 index 000000000..987d51f3d --- /dev/null +++ b/xs/src/libslic3r/Fill/FillBase.cpp @@ -0,0 +1,138 @@ +#include +#include + +#include "../ClipperUtils.hpp" +#include "../Surface.hpp" +#include "../PrintConfig.hpp" + +#include "FillBase.hpp" +#include "FillConcentric.hpp" +#include "FillHoneycomb.hpp" +#include "Fill3DHoneycomb.hpp" +#include "FillPlanePath.hpp" +#include "FillRectilinear.hpp" +#include "FillRectilinear2.hpp" + +namespace Slic3r { + +Fill* +Fill::new_from_type(const InfillPattern type) +{ + switch (type) { + case ipConcentric: return new FillConcentric(); + case ipHoneycomb: return new FillHoneycomb(); + case ip3DHoneycomb: return new Fill3DHoneycomb(); + + case ipRectilinear: return new FillRectilinear(); + case ipLine: return new FillLine(); + case ipGrid: return new FillGrid(); + case ipAlignedRectilinear: return new FillAlignedRectilinear(); + + case ipRectilinear2: return new FillRectilinear2(); + case ipGrid2: return new FillGrid2(); + case ipTriangles: return new FillTriangles(); + case ipStars: return new FillStars(); + case ipCubic: return new FillCubic(); + + case ipArchimedeanChords: return new FillArchimedeanChords(); + case ipHilbertCurve: return new FillHilbertCurve(); + case ipOctagramSpiral: return new FillOctagramSpiral(); + + default: CONFESS("unknown type"); return NULL; + } +} + +Fill* +Fill::new_from_type(const std::string &type) +{ + static t_config_enum_values enum_keys_map = ConfigOptionEnum::get_enum_values(); + t_config_enum_values::const_iterator it = enum_keys_map.find(type); + return (it == enum_keys_map.end()) ? NULL : new_from_type(InfillPattern(it->second)); +} + +Polylines +Fill::fill_surface(const Surface &surface, const FillParams ¶ms) +{ + // Perform offset. + ExPolygons expp = offset_ex(surface.expolygon, -scale_(this->spacing)/2); + + // Create the infills for each of the regions. + Polylines polylines_out; + for (size_t i = 0; i < expp.size(); ++i) + this->_fill_surface_single( + params, + surface.thickness_layers, + this->_infill_direction(surface), + expp[i], + &polylines_out + ); + return polylines_out; +} + +// Calculate a new spacing to fill width with possibly integer number of lines, +// the first and last line being centered at the interval ends. +// This function possibly increases the spacing, never decreases, +// and for a narrow width the increase in spacing may become severe, +// therefore the adjustment is limited to 20% increase. +coord_t +Fill::adjust_solid_spacing(const coord_t width, const coord_t distance) +{ + assert(width >= 0); + assert(distance > 0); + // floor(width / distance) + coord_t number_of_intervals = floor(width / distance); + coord_t distance_new = (number_of_intervals == 0) + ? distance + : (width / number_of_intervals); + + const coordf_t factor = coordf_t(distance_new) / coordf_t(distance); + assert(factor > 1. - 1e-5); + + // How much could the extrusion width be increased? By 20%. + const coordf_t factor_max = 1.2; + if (factor > factor_max) + distance_new = floor((double)distance * factor_max + 0.5); + + return distance_new; +} + +// Returns orientation of the infill and the reference point of the infill pattern. +// For a normal print, the reference point is the center of a bounding box of the STL. +std::pair +Fill::_infill_direction(const Surface &surface) const +{ + // set infill angle + float out_angle = this->angle; + + // Bounding box is the bounding box of a Slic3r::PrintObject + // The bounding box is only undefined in unit tests. + Point out_shift = this->bounding_box.defined + ? this->bounding_box.center() + : surface.expolygon.contour.bounding_box().center(); + + #if 0 + if (!this->bounding_box.defined) { + printf("Fill::_infill_direction: empty bounding box!"); + } else { + printf("Fill::_infill_direction: reference point %d, %d\n", out_shift.x, out_shift.y); + } + #endif + + if (surface.bridge_angle >= 0) { + // use bridge angle + //FIXME Vojtech: Add a debugf? + // Slic3r::debugf "Filling bridge with angle %d\n", rad2deg($surface->bridge_angle); + #ifdef SLIC3R_DEBUG + printf("Filling bridge with angle %f\n", surface.bridge_angle); + #endif + out_angle = surface.bridge_angle; + } else if (this->layer_id != size_t(-1)) { + // alternate fill direction + out_angle += this->_layer_angle(this->layer_id / surface.thickness_layers); + } + + out_angle += float(M_PI/2.); + return std::pair(out_angle, out_shift); +} + +} // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/FillBase.hpp b/xs/src/libslic3r/Fill/FillBase.hpp new file mode 100644 index 000000000..75627d29e --- /dev/null +++ b/xs/src/libslic3r/Fill/FillBase.hpp @@ -0,0 +1,111 @@ +#ifndef slic3r_FillBase_hpp_ +#define slic3r_FillBase_hpp_ + +#include +#include +#include +#include + +#include "../libslic3r.h" +#include "../BoundingBox.hpp" +#include "../ExPolygon.hpp" +#include "../Polyline.hpp" +#include "../PrintConfig.hpp" + +namespace Slic3r { + +class Surface; + +struct FillParams +{ + public: + FillParams() : density(0), dont_connect(false), dont_adjust(false), complete(false) {}; + + // Fill density, fraction in <0, 1> + float density; + + // Don't connect the fill lines around the inner perimeter. + bool dont_connect; + + // Don't adjust spacing to fill the space evenly. + bool dont_adjust; + + // For Honeycomb. + // we were requested to complete each loop; + // in this case we don't try to make more continuous paths + bool complete; +}; + +class Fill +{ +public: + // Index of the layer. + size_t layer_id; + + // Z coordinate of the top print surface, in unscaled coordinates + coordf_t z; + + // in unscaled coordinates + coordf_t spacing; + + // in radians, ccw, 0 = East + float angle; + + // In scaled coordinates. Maximum lenght of a perimeter segment connecting two infill lines. + // Used by the FillRectilinear2, FillGrid2, FillTriangles, FillStars and FillCubic. + // If left to zero, the links will not be limited. + coord_t link_max_length; + + // In scaled coordinates. Used by the concentric infill pattern to clip the loops to create extrusion paths. + coord_t loop_clipping; + + // In scaled coordinates. Bounding box of the 2D projection of the object. + BoundingBox bounding_box; + +public: + virtual ~Fill() {} + + static Fill* new_from_type(const InfillPattern type); + static Fill* new_from_type(const std::string &type); + + void set_bounding_box(const BoundingBox &bb) { this->bounding_box = bb; } + + // Use bridge flow for the fill? + virtual bool use_bridge_flow() const { return false; } + + // Do not sort the fill lines to optimize the print head path? + virtual bool no_sort() const { return false; } + + // Perform the fill. + virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms); + + static coord_t adjust_solid_spacing(const coord_t width, const coord_t distance); + +protected: + Fill() : + layer_id(size_t(-1)), + z(0.f), + spacing(0.f), + angle(0), + link_max_length(0), + loop_clipping(0) + {}; + + // The expolygon may be modified by the method to avoid a copy. + virtual void _fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out) {}; + + virtual float _layer_angle(size_t idx) const { + return (idx % 2) == 0 ? (M_PI/2.) : 0; + } + + std::pair _infill_direction(const Surface &surface) const; +}; + +} // namespace Slic3r + +#endif // slic3r_FillBase_hpp_ diff --git a/xs/src/libslic3r/Fill/FillConcentric.cpp b/xs/src/libslic3r/Fill/FillConcentric.cpp new file mode 100644 index 000000000..d31efcafc --- /dev/null +++ b/xs/src/libslic3r/Fill/FillConcentric.cpp @@ -0,0 +1,63 @@ +#include "../ClipperUtils.hpp" +#include "../ExPolygon.hpp" +#include "../Surface.hpp" + +#include "FillConcentric.hpp" + +namespace Slic3r { + +void +FillConcentric::_fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out) +{ + // no rotation is supported for this infill pattern + + const coord_t min_spacing = scale_(this->spacing); + coord_t distance = coord_t(min_spacing / params.density); + + if (params.density > 0.9999f && !params.dont_adjust) { + BoundingBox bounding_box = expolygon.contour.bounding_box(); + distance = this->adjust_solid_spacing(bounding_box.size().x, distance); + this->spacing = unscale(distance); + } + + Polygons loops = (Polygons)expolygon; + Polygons last = loops; + while (!last.empty()) { + last = offset2(last, -(distance + min_spacing/2), +min_spacing/2); + loops.insert(loops.end(), last.begin(), last.end()); + } + + // generate paths from the outermost to the innermost, to avoid + // adhesion problems of the first central tiny loops + loops = union_pt_chained(loops, false); + + // split paths using a nearest neighbor search + size_t iPathFirst = polylines_out->size(); + Point last_pos(0, 0); + for (Polygons::const_iterator it_loop = loops.begin(); it_loop != loops.end(); ++ it_loop) { + polylines_out->push_back(it_loop->split_at_index(last_pos.nearest_point_index(*it_loop))); + last_pos = polylines_out->back().last_point(); + } + + // clip the paths to prevent the extruder from getting exactly on the first point of the loop + // Keep valid paths only. + size_t j = iPathFirst; + for (size_t i = iPathFirst; i < polylines_out->size(); ++ i) { + (*polylines_out)[i].clip_end(this->loop_clipping); + if ((*polylines_out)[i].is_valid()) { + if (j < i) + std::swap((*polylines_out)[j], (*polylines_out)[i]); + ++j; + } + } + if (j < polylines_out->size()) + polylines_out->erase(polylines_out->begin() + j, polylines_out->end()); + // TODO: return ExtrusionLoop objects to get better chained paths +} + +} // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/FillConcentric.hpp b/xs/src/libslic3r/Fill/FillConcentric.hpp new file mode 100644 index 000000000..a4e1ddb11 --- /dev/null +++ b/xs/src/libslic3r/Fill/FillConcentric.hpp @@ -0,0 +1,26 @@ +#ifndef slic3r_FillConcentric_hpp_ +#define slic3r_FillConcentric_hpp_ + +#include "FillBase.hpp" + +namespace Slic3r { + +class FillConcentric : public Fill +{ +public: + virtual ~FillConcentric() {} + +protected: + virtual void _fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out); + + virtual bool no_sort() const { return true; } +}; + +} // namespace Slic3r + +#endif // slic3r_FillConcentric_hpp_ diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.cpp b/xs/src/libslic3r/Fill/FillHoneycomb.cpp new file mode 100644 index 000000000..e3a67f3f7 --- /dev/null +++ b/xs/src/libslic3r/Fill/FillHoneycomb.cpp @@ -0,0 +1,126 @@ +#include "FillHoneycomb.hpp" +#include "../ClipperUtils.hpp" +#include "../PolylineCollection.hpp" +#include "../Surface.hpp" + + +namespace Slic3r { + +void +FillHoneycomb::_fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out) +{ + // cache hexagons math + CacheID cache_id = std::make_pair(params.density, this->spacing); + Cache::iterator it_m = this->cache.find(cache_id); + if (it_m == this->cache.end()) { + it_m = this->cache.insert(it_m, std::pair(cache_id, CacheData())); + CacheData &m = it_m->second; + coord_t min_spacing = scale_(this->spacing); + m.distance = min_spacing / params.density; + m.hex_side = m.distance / (sqrt(3)/2); + m.hex_width = m.distance * 2; // $m->{hex_width} == $m->{hex_side} * sqrt(3); + coord_t hex_height = m.hex_side * 2; + m.pattern_height = hex_height + m.hex_side; + m.y_short = m.distance * sqrt(3)/3; + m.x_offset = min_spacing / 2; + m.y_offset = m.x_offset * sqrt(3)/3; + m.hex_center = Point(m.hex_width/2, m.hex_side); + } + CacheData &m = it_m->second; + + Polygons polygons; + { + // adjust actual bounding box to the nearest multiple of our hex pattern + // and align it so that it matches across layers + + BoundingBox bounding_box = expolygon.contour.bounding_box(); + { + // rotate bounding box according to infill direction + Polygon bb_polygon = bounding_box.polygon(); + bb_polygon.rotate(direction.first, m.hex_center); + bounding_box = bb_polygon.bounding_box(); + + // extend bounding box so that our pattern will be aligned with other layers + // $bounding_box->[X1] and [Y1] represent the displacement between new bounding box offset and old one + // The infill is not aligned to the object bounding box, but to a world coordinate system. Supposedly good enough. + bounding_box.min.align_to_grid(Point(m.hex_width, m.pattern_height)); + } + + for (coord_t x = bounding_box.min.x; x <= bounding_box.max.x; ) { + Polygon p; + coord_t ax[2] = { x + m.x_offset, x + m.distance - m.x_offset }; + for (size_t i = 0; i < 2; ++ i) { + std::reverse(p.points.begin(), p.points.end()); // turn first half upside down + for (coord_t y = bounding_box.min.y; y <= bounding_box.max.y; y += m.y_short + m.hex_side + m.y_short + m.hex_side) { + p.points.push_back(Point(ax[1], y + m.y_offset)); + p.points.push_back(Point(ax[0], y + m.y_short - m.y_offset)); + p.points.push_back(Point(ax[0], y + m.y_short + m.hex_side + m.y_offset)); + p.points.push_back(Point(ax[1], y + m.y_short + m.hex_side + m.y_short - m.y_offset)); + p.points.push_back(Point(ax[1], y + m.y_short + m.hex_side + m.y_short + m.hex_side + m.y_offset)); + } + ax[0] = ax[0] + m.distance; + ax[1] = ax[1] + m.distance; + std::swap(ax[0], ax[1]); // draw symmetrical pattern + x += m.distance; + } + p.rotate(-direction.first, m.hex_center); + polygons.push_back(p); + } + } + + if (true || params.complete) { + // we were requested to complete each loop; + // in this case we don't try to make more continuous paths + Polygons polygons_trimmed = intersection((Polygons)expolygon, polygons); + for (Polygons::iterator it = polygons_trimmed.begin(); it != polygons_trimmed.end(); ++ it) + polylines_out->push_back(it->split_at_first_point()); + } else { + // consider polygons as polylines without re-appending the initial point: + // this cuts the last segment on purpose, so that the jump to the next + // path is more straight + Polylines paths = intersection_pl( + to_polylines(polygons), + (Polygons)expolygon + ); + + // connect paths + if (!paths.empty()) { // prevent calling leftmost_point() on empty collections + Polylines chained = PolylineCollection::chained_path_from( + STDMOVE(paths), + PolylineCollection::leftmost_point(paths), + false + ); + assert(paths.empty()); + paths.clear(); + + for (Polylines::iterator it_path = chained.begin(); it_path != chained.end(); ++ it_path) { + if (!paths.empty()) { + // distance between first point of this path and last point of last path + double distance = paths.back().last_point().distance_to(it_path->first_point()); + if (distance <= m.hex_width) { + paths.back().points.insert(paths.back().points.end(), it_path->points.begin(), it_path->points.end()); + continue; + } + } + // Don't connect the paths. + paths.push_back(*it_path); + } + } + + // clip paths again to prevent connection segments from crossing the expolygon boundaries + paths = intersection_pl(paths, to_polygons(offset_ex(expolygon, SCALED_EPSILON))); + + // Move the polylines to the output, avoid a deep copy. + size_t j = polylines_out->size(); + polylines_out->resize(j + paths.size(), Polyline()); + for (size_t i = 0; i < paths.size(); ++ i) + std::swap((*polylines_out)[j++], paths[i]); + } +} + +} // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.hpp b/xs/src/libslic3r/Fill/FillHoneycomb.hpp new file mode 100644 index 000000000..70caaf7a0 --- /dev/null +++ b/xs/src/libslic3r/Fill/FillHoneycomb.hpp @@ -0,0 +1,47 @@ +#ifndef slic3r_FillHoneycomb_hpp_ +#define slic3r_FillHoneycomb_hpp_ + +#include + +#include "../libslic3r.h" + +#include "FillBase.hpp" + +namespace Slic3r { + +class FillHoneycomb : public Fill +{ +public: + virtual ~FillHoneycomb() {} + +protected: + virtual void _fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out + ); + + // Cache the hexagon math. + struct CacheData + { + coord_t distance; + coord_t hex_side; + coord_t hex_width; + coord_t pattern_height; + coord_t y_short; + coord_t x_offset; + coord_t y_offset; + Point hex_center; + }; + typedef std::pair CacheID; // density, spacing + typedef std::map Cache; + Cache cache; + + virtual float _layer_angle(size_t idx) const { return float(M_PI/3.) * (idx % 3); } +}; + +} // namespace Slic3r + +#endif // slic3r_FillHoneycomb_hpp_ diff --git a/xs/src/libslic3r/Fill/FillPlanePath.cpp b/xs/src/libslic3r/Fill/FillPlanePath.cpp new file mode 100644 index 000000000..7952daf5b --- /dev/null +++ b/xs/src/libslic3r/Fill/FillPlanePath.cpp @@ -0,0 +1,206 @@ +#include "../ClipperUtils.hpp" +#include "../PolylineCollection.hpp" +#include "../Surface.hpp" + +#include "FillPlanePath.hpp" + +namespace Slic3r { + +void FillPlanePath::_fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out) +{ + expolygon.rotate(-direction.first); + + const coord_t distance_between_lines = scale_(this->spacing) / params.density; + + // align infill across layers using the object's bounding box + // Rotated bounding box of the whole object. + BoundingBox bounding_box = this->bounding_box.rotated(-direction.first); + + const Point shift = this->_centered() + ? bounding_box.center() + : bounding_box.min; + expolygon.translate(-shift.x, -shift.y); + bounding_box.translate(-shift.x, -shift.y); + + const Pointfs pts = this->_generate( + coord_t(ceil(coordf_t(bounding_box.min.x) / distance_between_lines)), + coord_t(ceil(coordf_t(bounding_box.min.y) / distance_between_lines)), + coord_t(ceil(coordf_t(bounding_box.max.x) / distance_between_lines)), + coord_t(ceil(coordf_t(bounding_box.max.y) / distance_between_lines)) + ); + + Polylines polylines; + if (pts.size() >= 2) { + // Convert points to a polyline, upscale. + polylines.push_back(Polyline()); + Polyline &polyline = polylines.back(); + polyline.points.reserve(pts.size()); + for (Pointfs::const_iterator it = pts.begin(); it != pts.end(); ++ it) { + polyline.points.push_back(Point( + coord_t(floor(it->x * distance_between_lines + 0.5)), + coord_t(floor(it->y * distance_between_lines + 0.5)) + )); + } +// polylines = intersection_pl(polylines_src, offset((Polygons)expolygon, scale_(0.02))); + polylines = intersection_pl(polylines, (Polygons)expolygon); + +/* + if (1) { + require "Slic3r/SVG.pm"; + print "Writing fill.svg\n"; + Slic3r::SVG::output("fill.svg", + no_arrows => 1, + polygons => \@$expolygon, + green_polygons => [ $bounding_box->polygon ], + polylines => [ $polyline ], + red_polylines => \@paths, + ); + } +*/ + + // paths must be repositioned and rotated back + for (Polylines::iterator it = polylines.begin(); it != polylines.end(); ++ it) { + it->translate(shift.x, shift.y); + it->rotate(direction.first); + } + } + + // Move the polylines to the output, avoid a deep copy. + size_t j = polylines_out->size(); + polylines_out->resize(j + polylines.size(), Polyline()); + for (size_t i = 0; i < polylines.size(); ++ i) + std::swap((*polylines_out)[j++], polylines[i]); +} + +// Follow an Archimedean spiral, in polar coordinates: r=a+b\theta +Pointfs FillArchimedeanChords::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) +{ + // Radius to achieve. + const coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5; + // Now unwind the spiral. + const coordf_t a = 1.; + const coordf_t b = 1./(2.*M_PI); + coordf_t theta = 0.; + coordf_t r = 1; + Pointfs out; + //FIXME Vojtech: If used as a solid infill, there is a gap left at the center. + out.push_back(Pointf(0, 0)); + out.push_back(Pointf(1, 0)); + while (r < rmax) { + theta += 1. / r; + r = a + b * theta; + out.push_back(Pointf(r * cos(theta), r * sin(theta))); + } + return out; +} + +// Adapted from +// http://cpansearch.perl.org/src/KRYDE/Math-PlanePath-122/lib/Math/PlanePath/HilbertCurve.pm +// +// state=0 3--2 plain +// | +// 0--1 +// +// state=4 1--2 transpose +// | | +// 0 3 +// +// state=8 +// +// state=12 3 0 rot180 + transpose +// | | +// 2--1 +// +static inline Point hilbert_n_to_xy(const size_t n) +{ + static const int next_state[16] = { 4,0,0,12, 0,4,4,8, 12,8,8,4, 8,12,12,0 }; + static const int digit_to_x[16] = { 0,1,1,0, 0,0,1,1, 1,0,0,1, 1,1,0,0 }; + static const int digit_to_y[16] = { 0,0,1,1, 0,1,1,0, 1,1,0,0, 1,0,0,1 }; + + // Number of 2 bit digits. + size_t ndigits = 0; + { + size_t nc = n; + while(nc > 0) { + nc >>= 2; + ++ ndigits; + } + } + int state = (ndigits & 1) ? 4 : 0; + int dirstate = (ndigits & 1) ? 0 : 4; + coord_t x = 0; + coord_t y = 0; + for (int i = (int)ndigits - 1; i >= 0; -- i) { + int digit = (n >> (i * 2)) & 3; + state += digit; + if (digit != 3) + dirstate = state; // lowest non-3 digit + x |= digit_to_x[state] << i; + y |= digit_to_y[state] << i; + state = next_state[state]; + } + return Point(x, y); +} + +Pointfs FillHilbertCurve::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) +{ + // Minimum power of two square to fit the domain. + size_t sz = 2; + size_t pw = 1; + { + size_t sz0 = std::max(max_x + 1 - min_x, max_y + 1 - min_y); + while (sz < sz0) { + sz = sz << 1; + ++pw; + } + } + + const size_t sz2 = sz * sz; + Pointfs line; + line.reserve(sz2); + for (size_t i = 0; i < sz2; ++ i) { + Point p = hilbert_n_to_xy(i); + line.push_back(Pointf(p.x + min_x, p.y + min_y)); + } + return line; +} + +Pointfs FillOctagramSpiral::_generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) +{ + // Radius to achieve. + const coordf_t rmax = std::sqrt(coordf_t(max_x)*coordf_t(max_x)+coordf_t(max_y)*coordf_t(max_y)) * std::sqrt(2.) + 1.5; + // Now unwind the spiral. + coordf_t r = 0; + const coordf_t r_inc = sqrt(2.); + Pointfs out; + out.push_back(Pointf(0, 0)); + while (r < rmax) { + r += r_inc; + coordf_t rx = r / sqrt(2.); + coordf_t r2 = r + rx; + out.push_back(Pointf( r, 0.)); + out.push_back(Pointf( r2, rx)); + out.push_back(Pointf( rx, rx)); + out.push_back(Pointf( rx, r2)); + out.push_back(Pointf(0., r)); + out.push_back(Pointf(-rx, r2)); + out.push_back(Pointf(-rx, rx)); + out.push_back(Pointf(-r2, rx)); + out.push_back(Pointf(-r, 0.)); + out.push_back(Pointf(-r2, -rx)); + out.push_back(Pointf(-rx, -rx)); + out.push_back(Pointf(-rx, -r2)); + out.push_back(Pointf(0., -r)); + out.push_back(Pointf( rx, -r2)); + out.push_back(Pointf( rx, -rx)); + out.push_back(Pointf( r2+r_inc, -rx)); + } + return out; +} + +} // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/FillPlanePath.hpp b/xs/src/libslic3r/Fill/FillPlanePath.hpp new file mode 100644 index 000000000..33b3ec1db --- /dev/null +++ b/xs/src/libslic3r/Fill/FillPlanePath.hpp @@ -0,0 +1,66 @@ +#ifndef slic3r_FillPlanePath_hpp_ +#define slic3r_FillPlanePath_hpp_ + +#include + +#include "../libslic3r.h" + +#include "FillBase.hpp" + +namespace Slic3r { + +// The original Perl code used path generators from Math::PlanePath library: +// http://user42.tuxfamily.org/math-planepath/ +// http://user42.tuxfamily.org/math-planepath/gallery.html + +class FillPlanePath : public Fill +{ +public: + virtual ~FillPlanePath() {} + +protected: + virtual void _fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out); + + virtual float _layer_angle(size_t idx) const { return 0.f; } + virtual bool _centered() const = 0; + virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y) = 0; +}; + +class FillArchimedeanChords : public FillPlanePath +{ +public: + virtual ~FillArchimedeanChords() {} + +protected: + virtual bool _centered() const { return true; } + virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y); +}; + +class FillHilbertCurve : public FillPlanePath +{ +public: + virtual ~FillHilbertCurve() {} + +protected: + virtual bool _centered() const { return false; } + virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y); +}; + +class FillOctagramSpiral : public FillPlanePath +{ +public: + virtual ~FillOctagramSpiral() {} + +protected: + virtual bool _centered() const { return true; } + virtual Pointfs _generate(coord_t min_x, coord_t min_y, coord_t max_x, coord_t max_y); +}; + +} // namespace Slic3r + +#endif // slic3r_FillPlanePath_hpp_ diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/xs/src/libslic3r/Fill/FillRectilinear.cpp new file mode 100644 index 000000000..dde04d27b --- /dev/null +++ b/xs/src/libslic3r/Fill/FillRectilinear.cpp @@ -0,0 +1,138 @@ +#include "../ClipperUtils.hpp" +#include "../ExPolygon.hpp" +#include "../PolylineCollection.hpp" +#include "../Surface.hpp" + +#include "FillRectilinear.hpp" + +namespace Slic3r { + +void FillRectilinear::_fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out) +{ + assert(params.density > 0.0001f && params.density <= 1.f); + + // rotate polygons so that we can work with vertical lines here + expolygon.rotate(-direction.first); + + this->_min_spacing = scale_(this->spacing); + this->_line_spacing = coord_t(coordf_t(this->_min_spacing) / params.density); + this->_diagonal_distance = this->_line_spacing * 2; + this->_line_oscillation = this->_line_spacing - this->_min_spacing; // only for Line infill + BoundingBox bounding_box = expolygon.contour.bounding_box(); + + // define flow spacing according to requested density + if (params.density > 0.9999f && !params.dont_adjust) { + this->_line_spacing = this->adjust_solid_spacing(bounding_box.size().x, this->_line_spacing); + this->spacing = unscale(this->_line_spacing); + } else { + // extend bounding box so that our pattern will be aligned with other layers + // Transform the reference point to the rotated coordinate system. + bounding_box.min.align_to_grid( + Point(this->_line_spacing, this->_line_spacing), + direction.second.rotated(-direction.first) + ); + } + + // generate the basic pattern + const coord_t x_max = bounding_box.max.x + SCALED_EPSILON; + Lines lines; + for (coord_t x = bounding_box.min.x; x <= x_max; x += this->_line_spacing) + lines.push_back(this->_line(lines.size(), x, bounding_box.min.y, bounding_box.max.y)); + if (this->_horizontal_lines()) { + const coord_t y_max = bounding_box.max.y + SCALED_EPSILON; + for (coord_t y = bounding_box.min.y; y <= y_max; y += this->_line_spacing) + lines.push_back(Line(Point(bounding_box.min.x, y), Point(bounding_box.max.x, y))); + } + + // clip paths against a slightly larger expolygon, so that the first and last paths + // are kept even if the expolygon has vertical sides + // the minimum offset for preventing edge lines from being clipped is SCALED_EPSILON; + // however we use a larger offset to support expolygons with slightly skewed sides and + // not perfectly straight + + Polylines polylines = intersection_pl( + to_polylines(lines), + offset(expolygon, scale_(0.02)), + false + ); + + // FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines! + const float INFILL_OVERLAP_OVER_SPACING = 0.3f; + + // How much to extend an infill path from expolygon outside? + const coord_t extra = coord_t(floor(this->_min_spacing * INFILL_OVERLAP_OVER_SPACING + 0.5f)); + for (Polylines::iterator it_polyline = polylines.begin(); + it_polyline != polylines.end(); ++ it_polyline) { + Point *first_point = &it_polyline->points.front(); + Point *last_point = &it_polyline->points.back(); + if (first_point->y > last_point->y) + std::swap(first_point, last_point); + first_point->y -= extra; + last_point->y += extra; + } + + size_t n_polylines_out_old = polylines_out->size(); + + // connect lines + if (!params.dont_connect && !polylines.empty()) { // prevent calling leftmost_point() on empty collections + // offset the expolygon by max(min_spacing/2, extra) + ExPolygon expolygon_off; + { + ExPolygons expolygons_off = offset_ex(expolygon, this->_min_spacing/2); + if (!expolygons_off.empty()) { + // When expanding a polygon, the number of islands could only shrink. + // Therefore the offset_ex shall generate exactly one expanded island + // for one input island. + assert(expolygons_off.size() == 1); + std::swap(expolygon_off, expolygons_off.front()); + } + } + Polylines chained = PolylineCollection::chained_path_from( + STDMOVE(polylines), + PolylineCollection::leftmost_point(polylines), + false // reverse allowed + ); + bool first = true; + for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) { + if (!first) { + // Try to connect the lines. + Points &pts_end = polylines_out->back().points; + const Point &first_point = it_polyline->points.front(); + const Point &last_point = pts_end.back(); + // Distance in X, Y. + const Vector distance = first_point.vector_to(last_point); + // TODO: we should also check that both points are on a fill_boundary to avoid + // connecting paths on the boundaries of internal regions + if (this->_can_connect(std::abs(distance.x), std::abs(distance.y)) + && expolygon_off.contains(Line(last_point, first_point))) { + // Append the polyline. + append_to(pts_end, it_polyline->points); + continue; + } + } + // The lines cannot be connected. + #if SLIC3R_CPPVER >= 11 + polylines_out->push_back(std::move(*it_polyline)); + #else + polylines_out->push_back(Polyline()); + std::swap(polylines_out->back(), *it_polyline); + #endif + first = false; + } + } + + // paths must be rotated back + for (Polylines::iterator it = polylines_out->begin() + n_polylines_out_old; + it != polylines_out->end(); ++ it) { + // No need to translate, the absolute position is irrelevant. + // it->translate(- direction.second.x, - direction.second.y); + it->rotate(direction.first); + } +} + +} // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/FillRectilinear.hpp b/xs/src/libslic3r/Fill/FillRectilinear.hpp new file mode 100644 index 000000000..03c0826bc --- /dev/null +++ b/xs/src/libslic3r/Fill/FillRectilinear.hpp @@ -0,0 +1,86 @@ +#ifndef slic3r_FillRectilinear_hpp_ +#define slic3r_FillRectilinear_hpp_ + +#include "../libslic3r.h" + +#include "FillBase.hpp" + +namespace Slic3r { + +class FillRectilinear : public Fill +{ +public: + virtual ~FillRectilinear() {} + +protected: + virtual void _fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out); + + coord_t _min_spacing; + coord_t _line_spacing; + // distance threshold for allowing the horizontal infill lines to be connected into a continuous path + coord_t _diagonal_distance; + // only for line infill + coord_t _line_oscillation; + + // Enabled for the grid infill, disabled for the rectilinear and line infill. + virtual bool _horizontal_lines() const { return false; }; + + virtual Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const + { return Line(Point(x, y_min), Point(x, y_max)); }; + + virtual bool _can_connect(coord_t dist_X, coord_t dist_Y) { + return dist_X <= this->_diagonal_distance + && dist_Y <= this->_diagonal_distance; + }; +}; + +class FillLine : public FillRectilinear +{ +public: + virtual ~FillLine() {} + +protected: + virtual Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const { + coord_t osc = (i & 1) ? this->_line_oscillation : 0; + return Line(Point(x - osc, y_min), Point(x + osc, y_max)); + }; + + virtual bool _can_connect(coord_t dist_X, coord_t dist_Y) + { + coord_t TOLERANCE = 10 * SCALED_EPSILON; + return (dist_X >= (this->_line_spacing - this->_line_oscillation) - TOLERANCE) + && (dist_X <= (this->_line_spacing + this->_line_oscillation) + TOLERANCE) + && (dist_Y <= this->_diagonal_distance); + }; +}; + +class FillGrid : public FillRectilinear +{ +public: + virtual ~FillGrid() {} + +protected: + // The grid fill will keep the angle constant between the layers,; see the implementation of Slic3r::Fill. + virtual float _layer_angle(size_t idx) const { return 0.f; } + // Flag for Slic3r::Fill::Rectilinear to fill both directions. + virtual bool _horizontal_lines() const { return true; }; +}; + +class FillAlignedRectilinear : public FillRectilinear +{ +public: + virtual ~FillAlignedRectilinear() {}; + +protected: + // Keep the angle constant in all layers. + virtual float _layer_angle(size_t idx) const { printf("ALIGNED\n"); return 0.f; }; +}; + +}; // namespace Slic3r + +#endif // slic3r_FillRectilinear_hpp_ diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.cpp b/xs/src/libslic3r/Fill/FillRectilinear2.cpp new file mode 100644 index 000000000..1458c90ac --- /dev/null +++ b/xs/src/libslic3r/Fill/FillRectilinear2.cpp @@ -0,0 +1,1712 @@ +#include +#include + +#include +#include +#include + +#include + +#include "../ClipperUtils.hpp" +#include "../ExPolygon.hpp" +#include "../Surface.hpp" + +#include "FillRectilinear2.hpp" + +// #define SLIC3R_DEBUG + +// Make assert active if SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG + #undef NDEBUG + #include "SVG.hpp" +#endif + +#include + +// We want our version of assert. +#include "../libslic3r.h" + +#ifndef myassert +#define myassert assert +#endif + +namespace Slic3r { + +#ifndef clamp +template +static inline T clamp(T low, T high, T x) +{ + return std::max(low, std::min(high, x)); +} +#endif /* clamp */ + +#ifndef sqr +template +static inline T sqr(T x) +{ + return x * x; +} +#endif /* sqr */ + +#ifndef mag2 +static inline coordf_t mag2(const Point &p) +{ + return sqr(coordf_t(p.x)) + sqr(coordf_t(p.y)); +} +#endif /* mag2 */ + +#ifndef mag +static inline coordf_t mag(const Point &p) +{ + return std::sqrt(mag2(p)); +} +#endif /* mag */ + +enum Orientation +{ + ORIENTATION_CCW = 1, + ORIENTATION_CW = -1, + ORIENTATION_COLINEAR = 0 +}; + +// Return orientation of the three points (clockwise, counter-clockwise, colinear) +// The predicate is exact for the coord_t type, using 64bit signed integers for the temporaries. +//FIXME Make sure the temporaries do not overflow, +// which means, the coord_t types must not have some of the topmost bits utilized. +static inline Orientation orient(const Point &a, const Point &b, const Point &c) +{ + // BOOST_STATIC_ASSERT(sizeof(coord_t) * 2 == sizeof(int64_t)); + int64_t u = int64_t(b.x) * int64_t(c.y) - int64_t(b.y) * int64_t(c.x); + int64_t v = int64_t(a.x) * int64_t(c.y) - int64_t(a.y) * int64_t(c.x); + int64_t w = int64_t(a.x) * int64_t(b.y) - int64_t(a.y) * int64_t(b.x); + int64_t d = u - v + w; + return (d > 0) ? ORIENTATION_CCW : ((d == 0) ? ORIENTATION_COLINEAR : ORIENTATION_CW); +} + +// Return orientation of the polygon. +// The input polygon must not contain duplicate points. +static inline bool is_ccw(const Polygon &poly) +{ + // The polygon shall be at least a triangle. + myassert(poly.points.size() >= 3); + if (poly.points.size() < 3) + return true; + + // 1) Find the lowest lexicographical point. + int imin = 0; + for (size_t i = 1; i < poly.points.size(); ++ i) { + const Point &pmin = poly.points[imin]; + const Point &p = poly.points[i]; + if (p.x < pmin.x || (p.x == pmin.x && p.y < pmin.y)) + imin = i; + } + + // 2) Detect the orientation of the corner imin. + size_t iPrev = ((imin == 0) ? poly.points.size() : imin) - 1; + size_t iNext = ((imin + 1 == poly.points.size()) ? 0 : imin + 1); + Orientation o = orient(poly.points[iPrev], poly.points[imin], poly.points[iNext]); + // The lowest bottom point must not be collinear if the polygon does not contain duplicate points + // or overlapping segments. + myassert(o != ORIENTATION_COLINEAR); + return o == ORIENTATION_CCW; +} + +// Having a segment of a closed polygon, calculate its Euclidian length. +// The segment indices seg1 and seg2 signify an end point of an edge in the forward direction of the loop, +// therefore the point p1 lies on poly.points[seg1-1], poly.points[seg1] etc. +static inline coordf_t segment_length(const Polygon &poly, size_t seg1, const Point &p1, size_t seg2, const Point &p2) +{ +#ifdef SLIC3R_DEBUG + // Verify that p1 lies on seg1. This is difficult to verify precisely, + // but at least verify, that p1 lies in the bounding box of seg1. + for (size_t i = 0; i < 2; ++ i) { + size_t seg = (i == 0) ? seg1 : seg2; + Point px = (i == 0) ? p1 : p2; + Point pa = poly.points[((seg == 0) ? poly.points.size() : seg) - 1]; + Point pb = poly.points[seg]; + if (pa.x > pb.x) + std::swap(pa.x, pb.x); + if (pa.y > pb.y) + std::swap(pa.y, pb.y); + myassert(px.x >= pa.x && px.x <= pb.x); + myassert(px.y >= pa.y && px.y <= pb.y); + } +#endif /* SLIC3R_DEBUG */ + const Point *pPrev = &p1; + const Point *pThis = NULL; + coordf_t len = 0; + if (seg1 <= seg2) { + for (size_t i = seg1; i < seg2; ++ i, pPrev = pThis) + len += pPrev->distance_to(*(pThis = &poly.points[i])); + } else { + for (size_t i = seg1; i < poly.points.size(); ++ i, pPrev = pThis) + len += pPrev->distance_to(*(pThis = &poly.points[i])); + for (size_t i = 0; i < seg2; ++ i, pPrev = pThis) + len += pPrev->distance_to(*(pThis = &poly.points[i])); + } + len += pPrev->distance_to(p2); + return len; +} + +// Append a segment of a closed polygon to a polyline. +// The segment indices seg1 and seg2 signify an end point of an edge in the forward direction of the loop. +// Only insert intermediate points between seg1 and seg2. +static inline void polygon_segment_append(Points &out, const Polygon &polygon, size_t seg1, size_t seg2) +{ + if (seg1 == seg2) { + // Nothing to append from this segment. + } else if (seg1 < seg2) { + // Do not append a point pointed to by seg2. + out.insert(out.end(), polygon.points.begin() + seg1, polygon.points.begin() + seg2); + } else { + out.reserve(out.size() + seg2 + polygon.points.size() - seg1); + out.insert(out.end(), polygon.points.begin() + seg1, polygon.points.end()); + // Do not append a point pointed to by seg2. + out.insert(out.end(), polygon.points.begin(), polygon.points.begin() + seg2); + } +} + +// Append a segment of a closed polygon to a polyline. +// The segment indices seg1 and seg2 signify an end point of an edge in the forward direction of the loop, +// but this time the segment is traversed backward. +// Only insert intermediate points between seg1 and seg2. +static inline void polygon_segment_append_reversed(Points &out, const Polygon &polygon, size_t seg1, size_t seg2) +{ + if (seg1 >= seg2) { + out.reserve(seg1 - seg2); + for (size_t i = seg1; i > seg2; -- i) + out.push_back(polygon.points[i - 1]); + } else { + // it could be, that seg1 == seg2. In that case, append the complete loop. + out.reserve(out.size() + seg2 + polygon.points.size() - seg1); + for (size_t i = seg1; i > 0; -- i) + out.push_back(polygon.points[i - 1]); + for (size_t i = polygon.points.size(); i > seg2; -- i) + out.push_back(polygon.points[i - 1]); + } +} + +// Intersection point of a vertical line with a polygon segment. +class SegmentIntersection +{ +public: + SegmentIntersection() : + iContour(0), + iSegment(0), + pos_p(0), + pos_q(1), + type(UNKNOWN), + consumed_vertical_up(false), + consumed_perimeter_right(false) + {} + + // Index of a contour in ExPolygonWithOffset, with which this vertical line intersects. + size_t iContour; + // Index of a segment in iContour, with which this vertical line intersects. + size_t iSegment; + // y position of the intersection, ratinal number. + int64_t pos_p; + uint32_t pos_q; + + coord_t pos() const { + // Division rounds both positive and negative down to zero. + // Add half of q for an arithmetic rounding effect. + int64_t p = pos_p; + if (p < 0) + p -= int64_t(pos_q>>1); + else + p += int64_t(pos_q>>1); + return coord_t(p / int64_t(pos_q)); + } + + // Kind of intersection. With the original contour, or with the inner offestted contour? + // A vertical segment will be at least intersected by OUTER_LOW, OUTER_HIGH, + // but it could be intersected with OUTER_LOW, INNER_LOW, INNER_HIGH, OUTER_HIGH, + // and there may be more than one pair of INNER_LOW, INNER_HIGH between OUTER_LOW, OUTER_HIGH. + enum SegmentIntersectionType { + OUTER_LOW = 0, + OUTER_HIGH = 1, + INNER_LOW = 2, + INNER_HIGH = 3, + UNKNOWN = -1 + }; + SegmentIntersectionType type; + + // Was this segment along the y axis consumed? + // Up means up along the vertical segment. + bool consumed_vertical_up; + // Was a segment of the inner perimeter contour consumed? + // Right means right from the vertical segment. + bool consumed_perimeter_right; + + // For the INNER_LOW type, this point may be connected to another INNER_LOW point following a perimeter contour. + // For the INNER_HIGH type, this point may be connected to another INNER_HIGH point following a perimeter contour. + // If INNER_LOW is connected to INNER_HIGH or vice versa, + // one has to make sure the vertical infill line does not overlap with the connecting perimeter line. + bool is_inner() const { return type == INNER_LOW || type == INNER_HIGH; } + bool is_outer() const { return type == OUTER_LOW || type == OUTER_HIGH; } + bool is_low () const { return type == INNER_LOW || type == OUTER_LOW; } + bool is_high () const { return type == INNER_HIGH || type == OUTER_HIGH; } + + // Compare two y intersection points given by rational numbers. + // Note that the rational number is given as pos_p/pos_q, where pos_p is int64 and pos_q is uint32. + // This function calculates pos_p * other.pos_q < other.pos_p * pos_q as a 48bit number. + // We don't use 128bit intrinsic data types as these are usually not supported by 32bit compilers and + // we don't need the full 128bit precision anyway. + bool operator<(const SegmentIntersection &other) const + { + assert(pos_q > 0); + assert(other.pos_q > 0); + if (pos_p == 0 || other.pos_p == 0) { + // Because the denominators are positive and one of the nominators is zero, + // following simple statement holds. + return pos_p < other.pos_p; + } else { + // None of the nominators is zero. + char sign1 = (pos_p > 0) ? 1 : -1; + char sign2 = (other.pos_p > 0) ? 1 : -1; + char signs = sign1 * sign2; + assert(signs == 1 || signs == -1); + if (signs < 0) { + // The nominators have different signs. + return sign1 < 0; + } else { + // The nominators have the same sign. + // Absolute values + uint64_t p1, p2; + if (sign1 > 0) { + p1 = uint64_t(pos_p); + p2 = uint64_t(other.pos_p); + } else { + p1 = uint64_t(- pos_p); + p2 = uint64_t(- other.pos_p); + }; + // Multiply low and high 32bit words of p1 by other_pos.q + // 32bit x 32bit => 64bit + // l_hi and l_lo overlap by 32 bits. + uint64_t l_hi = (p1 >> 32) * uint64_t(other.pos_q); + uint64_t l_lo = (p1 & 0xffffffffll) * uint64_t(other.pos_q); + l_hi += (l_lo >> 32); + uint64_t r_hi = (p2 >> 32) * uint64_t(pos_q); + uint64_t r_lo = (p2 & 0xffffffffll) * uint64_t(pos_q); + r_hi += (r_lo >> 32); + // Compare the high 64 bits. + if (l_hi == r_hi) { + // Compare the low 32 bits. + l_lo &= 0xffffffffll; + r_lo &= 0xffffffffll; + return (sign1 < 0) ? (l_lo > r_lo) : (l_lo < r_lo); + } + return (sign1 < 0) ? (l_hi > r_hi) : (l_hi < r_hi); + } + } + } + + bool operator==(const SegmentIntersection &other) const + { + assert(pos_q > 0); + assert(other.pos_q > 0); + if (pos_p == 0 || other.pos_p == 0) { + // Because the denominators are positive and one of the nominators is zero, + // following simple statement holds. + return pos_p == other.pos_p; + } + + // None of the nominators is zero, none of the denominators is zero. + bool positive = pos_p > 0; + if (positive != (other.pos_p > 0)) + return false; + // The nominators have the same sign. + // Absolute values + uint64_t p1 = positive ? uint64_t(pos_p) : uint64_t(- pos_p); + uint64_t p2 = positive ? uint64_t(other.pos_p) : uint64_t(- other.pos_p); + // Multiply low and high 32bit words of p1 by other_pos.q + // 32bit x 32bit => 64bit + // l_hi and l_lo overlap by 32 bits. + uint64_t l_lo = (p1 & 0xffffffffll) * uint64_t(other.pos_q); + uint64_t r_lo = (p2 & 0xffffffffll) * uint64_t(pos_q); + if (l_lo != r_lo) + return false; + uint64_t l_hi = (p1 >> 32) * uint64_t(other.pos_q); + uint64_t r_hi = (p2 >> 32) * uint64_t(pos_q); + return l_hi + (l_lo >> 32) == r_hi + (r_lo >> 32); + } +}; + +// A vertical line with intersection points with polygons. +class SegmentedIntersectionLine +{ +public: + // Index of this vertical intersection line. + size_t idx; + // x position of this vertical intersection line. + coord_t pos; + // List of intersection points with polygons, sorted increasingly by the y axis. + std::vector intersections; +}; + +// A container maintaining an expolygon with its inner offsetted polygon. +// The purpose of the inner offsetted polygon is to provide segments to connect the infill lines. +struct ExPolygonWithOffset +{ +public: + ExPolygonWithOffset( + const ExPolygon &expolygon, + float angle, + coord_t aoffset1, + coord_t aoffset2) + { + // Copy and rotate the source polygons. + polygons_src = expolygon; + polygons_src.contour.rotate(angle); + for (Polygons::iterator it = polygons_src.holes.begin(); it != polygons_src.holes.end(); ++ it) + it->rotate(angle); + + double mitterLimit = 3.; + // for the infill pattern, don't cut the corners. + // default miterLimt = 3 + //double mitterLimit = 10.; + myassert(aoffset1 < 0); + myassert(aoffset2 < 0); + myassert(aoffset2 < aoffset1); + bool sticks_removed = remove_sticks(polygons_src); +// if (sticks_removed) printf("Sticks removed!\n"); + polygons_outer = offset(polygons_src, aoffset1, + CLIPPER_OFFSET_SCALE, + ClipperLib::jtMiter, + mitterLimit); + polygons_inner = offset(polygons_outer, aoffset2 - aoffset1, + CLIPPER_OFFSET_SCALE, + ClipperLib::jtMiter, + mitterLimit); + // Filter out contours with zero area or small area, contours with 2 points only. + const double min_area_threshold = 0.01 * aoffset2 * aoffset2; + remove_small(polygons_outer, min_area_threshold); + remove_small(polygons_inner, min_area_threshold); + remove_sticks(polygons_outer); + remove_sticks(polygons_inner); + n_contours_outer = polygons_outer.size(); + n_contours_inner = polygons_inner.size(); + n_contours = n_contours_outer + n_contours_inner; + polygons_ccw.assign(n_contours, false); + for (size_t i = 0; i < n_contours; ++ i) { + contour(i).remove_duplicate_points(); + myassert(! contour(i).has_duplicate_points()); + polygons_ccw[i] = is_ccw(contour(i)); + } + } + + // Any contour with offset1 + bool is_contour_outer(size_t idx) const { return idx < n_contours_outer; } + // Any contour with offset2 + bool is_contour_inner(size_t idx) const { return idx >= n_contours_outer; } + + const Polygon& contour(size_t idx) const + { return is_contour_outer(idx) ? polygons_outer[idx] : polygons_inner[idx - n_contours_outer]; } + + Polygon& contour(size_t idx) + { return is_contour_outer(idx) ? polygons_outer[idx] : polygons_inner[idx - n_contours_outer]; } + + bool is_contour_ccw(size_t idx) const { return polygons_ccw[idx]; } + +#ifdef SLIC3R_DEBUG + void export_to_svg(Slic3r::SVG &svg) { + svg.draw_outline(polygons_src, "black"); + svg.draw_outline(polygons_outer, "green"); + svg.draw_outline(polygons_inner, "brown"); + } +#endif /* SLIC3R_DEBUG */ + + ExPolygon polygons_src; + Polygons polygons_outer; + Polygons polygons_inner; + + size_t n_contours_outer; + size_t n_contours_inner; + size_t n_contours; + +protected: + // For each polygon of polygons_inner, remember its orientation. + std::vector polygons_ccw; +}; + +static inline int distance_of_segmens(const Polygon &poly, size_t seg1, size_t seg2, bool forward) +{ + int d = int(seg2) - int(seg1); + if (! forward) + d = - d; + if (d < 0) + d += int(poly.points.size()); + return d; +} + +// For a vertical line, an inner contour and an intersection point, +// find an intersection point on the previous resp. next vertical line. +// The intersection point is connected with the prev resp. next intersection point with iInnerContour. +// Return -1 if there is no such point on the previous resp. next vertical line. +static inline int intersection_on_prev_next_vertical_line( + const ExPolygonWithOffset &poly_with_offset, + const std::vector &segs, + size_t iVerticalLine, + size_t iInnerContour, + size_t iIntersection, + bool dir_is_next) +{ + size_t iVerticalLineOther = iVerticalLine; + if (dir_is_next) { + if (++ iVerticalLineOther == segs.size()) + // No successive vertical line. + return -1; + } else if (iVerticalLineOther -- == 0) { + // No preceding vertical line. + return -1; + } + + const SegmentedIntersectionLine &il = segs[iVerticalLine]; + const SegmentIntersection &itsct = il.intersections[iIntersection]; + const SegmentedIntersectionLine &il2 = segs[iVerticalLineOther]; + const Polygon &poly = poly_with_offset.contour(iInnerContour); +// const bool ccw = poly_with_offset.is_contour_ccw(iInnerContour); + const bool forward = itsct.is_low() == dir_is_next; + // Resulting index of an intersection point on il2. + int out = -1; + // Find an intersection point on iVerticalLineOther, intersecting iInnerContour + // at the same orientation as iIntersection, and being closest to iIntersection + // in the number of contour segments, when following the direction of the contour. + int dmin = std::numeric_limits::max(); + for (size_t i = 0; i < il2.intersections.size(); ++ i) { + const SegmentIntersection &itsct2 = il2.intersections[i]; + if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) { + /* + if (itsct.is_low()) { + myassert(itsct.type == SegmentIntersection::INNER_LOW); + myassert(iIntersection > 0); + myassert(il.intersections[iIntersection-1].type == SegmentIntersection::OUTER_LOW); + myassert(i > 0); + if (il2.intersections[i-1].is_inner()) + // Take only the lowest inner intersection point. + continue; + myassert(il2.intersections[i-1].type == SegmentIntersection::OUTER_LOW); + } else { + myassert(itsct.type == SegmentIntersection::INNER_HIGH); + myassert(iIntersection+1 < il.intersections.size()); + myassert(il.intersections[iIntersection+1].type == SegmentIntersection::OUTER_HIGH); + myassert(i+1 < il2.intersections.size()); + if (il2.intersections[i+1].is_inner()) + // Take only the highest inner intersection point. + continue; + myassert(il2.intersections[i+1].type == SegmentIntersection::OUTER_HIGH); + } + */ + // The intersection points lie on the same contour and have the same orientation. + // Find the intersection point with a shortest path in the direction of the contour. + int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, forward); + if (d < dmin) { + out = i; + dmin = d; + } + } + } + //FIXME this routine is not asymptotic optimal, it will be slow if there are many intersection points along the line. + return out; +} + +static inline int intersection_on_prev_vertical_line( + const ExPolygonWithOffset &poly_with_offset, + const std::vector &segs, + size_t iVerticalLine, + size_t iInnerContour, + size_t iIntersection) +{ + return intersection_on_prev_next_vertical_line(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, false); +} + +static inline int intersection_on_next_vertical_line( + const ExPolygonWithOffset &poly_with_offset, + const std::vector &segs, + size_t iVerticalLine, + size_t iInnerContour, + size_t iIntersection) +{ + return intersection_on_prev_next_vertical_line(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, true); +} + +enum IntersectionTypeOtherVLine { + // There is no connection point on the other vertical line. + INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED = -1, + // Connection point on the other vertical segment was found + // and it could be followed. + INTERSECTION_TYPE_OTHER_VLINE_OK = 0, + // The connection segment connects to a middle of a vertical segment. + // Cannot follow. + INTERSECTION_TYPE_OTHER_VLINE_INNER, + // Cannot extend the contor to this intersection point as either the connection segment + // or the succeeding vertical segment were already consumed. + INTERSECTION_TYPE_OTHER_VLINE_CONSUMED, + // Not the first intersection along the contor. This intersection point + // has been preceded by an intersection point along the vertical line. + INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST, +}; + +// Find an intersection on a previous line, but return -1, if the connecting segment of a perimeter was already extruded. +static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical_line( + const std::vector &segs, + size_t iVerticalLine, + size_t iIntersection, + size_t iIntersectionOther, + bool dir_is_next) +{ + // This routine will propose a connecting line even if the connecting perimeter segment intersects + // iVertical line multiple times before reaching iIntersectionOther. + if (iIntersectionOther == -1) + return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED; + myassert(dir_is_next ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0)); + const SegmentedIntersectionLine &il_this = segs[iVerticalLine]; + const SegmentIntersection &itsct_this = il_this.intersections[iIntersection]; + const SegmentedIntersectionLine &il_other = segs[dir_is_next ? (iVerticalLine+1) : (iVerticalLine-1)]; + const SegmentIntersection &itsct_other = il_other.intersections[iIntersectionOther]; + myassert(itsct_other.is_inner()); + myassert(iIntersectionOther > 0); + myassert(iIntersectionOther + 1 < il_other.intersections.size()); + // Is iIntersectionOther at the boundary of a vertical segment? + const SegmentIntersection &itsct_other2 = il_other.intersections[itsct_other.is_low() ? iIntersectionOther - 1 : iIntersectionOther + 1]; + if (itsct_other2.is_inner()) + // Cannot follow a perimeter segment into the middle of another vertical segment. + // Only perimeter segments connecting to the end of a vertical segment are followed. + return INTERSECTION_TYPE_OTHER_VLINE_INNER; + myassert(itsct_other.is_low() == itsct_other2.is_low()); + if (dir_is_next ? itsct_this.consumed_perimeter_right : itsct_other.consumed_perimeter_right) + // This perimeter segment was already consumed. + return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED; + if (itsct_other.is_low() ? itsct_other.consumed_vertical_up : il_other.intersections[iIntersectionOther-1].consumed_vertical_up) + // This vertical segment was already consumed. + return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED; + return INTERSECTION_TYPE_OTHER_VLINE_OK; +} + +static inline IntersectionTypeOtherVLine intersection_type_on_prev_vertical_line( + const std::vector &segs, + size_t iVerticalLine, + size_t iIntersection, + size_t iIntersectionPrev) +{ + return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, iIntersectionPrev, false); +} + +static inline IntersectionTypeOtherVLine intersection_type_on_next_vertical_line( + const std::vector &segs, + size_t iVerticalLine, + size_t iIntersection, + size_t iIntersectionNext) +{ + return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, iIntersectionNext, true); +} + +// Measure an Euclidian length of a perimeter segment when going from iIntersection to iIntersection2. +static inline coordf_t measure_perimeter_prev_next_segment_length( + const ExPolygonWithOffset &poly_with_offset, + const std::vector &segs, + size_t iVerticalLine, + size_t iInnerContour, + size_t iIntersection, + size_t iIntersection2, + bool dir_is_next) +{ + size_t iVerticalLineOther = iVerticalLine; + if (dir_is_next) { + if (++ iVerticalLineOther == segs.size()) + // No successive vertical line. + return coordf_t(-1); + } else if (iVerticalLineOther -- == 0) { + // No preceding vertical line. + return coordf_t(-1); + } + + const SegmentedIntersectionLine &il = segs[iVerticalLine]; + const SegmentIntersection &itsct = il.intersections[iIntersection]; + const SegmentedIntersectionLine &il2 = segs[iVerticalLineOther]; + const SegmentIntersection &itsct2 = il2.intersections[iIntersection2]; + const Polygon &poly = poly_with_offset.contour(iInnerContour); +// const bool ccw = poly_with_offset.is_contour_ccw(iInnerContour); + myassert(itsct.type == itsct2.type); + myassert(itsct.iContour == itsct2.iContour); + myassert(itsct.is_inner()); + const bool forward = itsct.is_low() == dir_is_next; + + Point p1(il.pos, itsct.pos()); + Point p2(il2.pos, itsct2.pos()); + return forward ? + segment_length(poly, itsct .iSegment, p1, itsct2.iSegment, p2) : + segment_length(poly, itsct2.iSegment, p2, itsct .iSegment, p1); +} + +static inline coordf_t measure_perimeter_prev_segment_length( + const ExPolygonWithOffset &poly_with_offset, + const std::vector &segs, + size_t iVerticalLine, + size_t iInnerContour, + size_t iIntersection, + size_t iIntersection2) +{ + return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, iIntersection2, false); +} + +static inline coordf_t measure_perimeter_next_segment_length( + const ExPolygonWithOffset &poly_with_offset, + const std::vector &segs, + size_t iVerticalLine, + size_t iInnerContour, + size_t iIntersection, + size_t iIntersection2) +{ + return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, iIntersection2, true); +} + +// Append the points of a perimeter segment when going from iIntersection to iIntersection2. +// The first point (the point of iIntersection) will not be inserted, +// the last point will be inserted. +static inline void emit_perimeter_prev_next_segment( + const ExPolygonWithOffset &poly_with_offset, + const std::vector &segs, + size_t iVerticalLine, + size_t iInnerContour, + size_t iIntersection, + size_t iIntersection2, + Polyline &out, + bool dir_is_next) +{ + size_t iVerticalLineOther = iVerticalLine; + if (dir_is_next) { + ++ iVerticalLineOther; + myassert(iVerticalLineOther < segs.size()); + } else { + myassert(iVerticalLineOther > 0); + -- iVerticalLineOther; + } + + const SegmentedIntersectionLine &il = segs[iVerticalLine]; + const SegmentIntersection &itsct = il.intersections[iIntersection]; + const SegmentedIntersectionLine &il2 = segs[iVerticalLineOther]; + const SegmentIntersection &itsct2 = il2.intersections[iIntersection2]; + const Polygon &poly = poly_with_offset.contour(iInnerContour); +// const bool ccw = poly_with_offset.is_contour_ccw(iInnerContour); + myassert(itsct.type == itsct2.type); + myassert(itsct.iContour == itsct2.iContour); + myassert(itsct.is_inner()); + const bool forward = itsct.is_low() == dir_is_next; + // Do not append the first point. + // out.points.push_back(Point(il.pos, itsct.pos)); + if (forward) + polygon_segment_append(out.points, poly, itsct.iSegment, itsct2.iSegment); + else + polygon_segment_append_reversed(out.points, poly, itsct.iSegment, itsct2.iSegment); + // Append the last point. + out.points.push_back(Point(il2.pos, itsct2.pos())); +} + +static inline coordf_t measure_perimeter_segment_on_vertical_line_length( + const ExPolygonWithOffset &poly_with_offset, + const std::vector &segs, + size_t iVerticalLine, + size_t iInnerContour, + size_t iIntersection, + size_t iIntersection2, + bool forward) +{ + const SegmentedIntersectionLine &il = segs[iVerticalLine]; + const SegmentIntersection &itsct = il.intersections[iIntersection]; + const SegmentIntersection &itsct2 = il.intersections[iIntersection2]; + const Polygon &poly = poly_with_offset.contour(iInnerContour); + myassert(itsct.is_inner()); + myassert(itsct2.is_inner()); + myassert(itsct.type != itsct2.type); + myassert(itsct.iContour == iInnerContour); + myassert(itsct.iContour == itsct2.iContour); + Point p1(il.pos, itsct.pos()); + Point p2(il.pos, itsct2.pos()); + return forward ? + segment_length(poly, itsct .iSegment, p1, itsct2.iSegment, p2) : + segment_length(poly, itsct2.iSegment, p2, itsct .iSegment, p1); +} + +// Append the points of a perimeter segment when going from iIntersection to iIntersection2. +// The first point (the point of iIntersection) will not be inserted, +// the last point will be inserted. +static inline void emit_perimeter_segment_on_vertical_line( + const ExPolygonWithOffset &poly_with_offset, + const std::vector &segs, + size_t iVerticalLine, + size_t iInnerContour, + size_t iIntersection, + size_t iIntersection2, + Polyline &out, + bool forward) +{ + const SegmentedIntersectionLine &il = segs[iVerticalLine]; + const SegmentIntersection &itsct = il.intersections[iIntersection]; + const SegmentIntersection &itsct2 = il.intersections[iIntersection2]; + const Polygon &poly = poly_with_offset.contour(iInnerContour); + myassert(itsct.is_inner()); + myassert(itsct2.is_inner()); + myassert(itsct.type != itsct2.type); + myassert(itsct.iContour == iInnerContour); + myassert(itsct.iContour == itsct2.iContour); + // Do not append the first point. + // out.points.push_back(Point(il.pos, itsct.pos)); + if (forward) + polygon_segment_append(out.points, poly, itsct.iSegment, itsct2.iSegment); + else + polygon_segment_append_reversed(out.points, poly, itsct.iSegment, itsct2.iSegment); + // Append the last point. + out.points.push_back(Point(il.pos, itsct2.pos())); +} + +//TBD: For precise infill, measure the area of a slab spanned by an infill line. +/* +static inline float measure_outer_contour_slab( + const ExPolygonWithOffset &poly_with_offset, + const std::vector &segs, + size_t i_vline, + size_t iIntersection) +{ + const SegmentedIntersectionLine &il = segs[i_vline]; + const SegmentIntersection &itsct = il.intersections[i_vline]; + const SegmentIntersection &itsct2 = il.intersections[iIntersection2]; + const Polygon &poly = poly_with_offset.contour((itsct.iContour); + myassert(itsct.is_outer()); + myassert(itsct2.is_outer()); + myassert(itsct.type != itsct2.type); + myassert(itsct.iContour == itsct2.iContour); + if (! itsct.is_outer() || ! itsct2.is_outer() || itsct.type == itsct2.type || itsct.iContour != itsct2.iContour) + // Error, return zero area. + return 0.f; + + // Find possible connection points on the previous / next vertical line. + int iPrev = intersection_on_prev_vertical_line(poly_with_offset, segs, i_vline, itsct.iContour, i_intersection); + int iNext = intersection_on_next_vertical_line(poly_with_offset, segs, i_vline, itsct.iContour, i_intersection); + // Find possible connection points on the same vertical line. + int iAbove = iBelow = -1; + // Does the perimeter intersect the current vertical line above intrsctn? + for (size_t i = i_intersection + 1; i + 1 < seg.intersections.size(); ++ i) + if (seg.intersections[i].iContour == itsct.iContour) + { iAbove = i; break; } + // Does the perimeter intersect the current vertical line below intrsctn? + for (int i = int(i_intersection) - 1; i > 0; -- i) + if (seg.intersections[i].iContour == itsct.iContour) + { iBelow = i; break; } + + if (iSegAbove != -1 && seg.intersections[iAbove].type == SegmentIntersection::OUTER_HIGH) { + // Invalidate iPrev resp. iNext, if the perimeter crosses the current vertical line earlier than iPrev resp. iNext. + // The perimeter contour orientation. + const Polygon &poly = poly_with_offset.contour(itsct.iContour); + { + int d_horiz = (iPrev == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, segs[i_vline-1].intersections[iPrev].iSegment, itsct.iSegment, true); + int d_down = (iBelow == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, iSegBelow, itsct.iSegment, true); + int d_up = (iAbove == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, iSegAbove, itsct.iSegment, true); + if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && d_horiz > std::min(d_down, d_up)) + // The vertical crossing comes eralier than the prev crossing. + // Disable the perimeter going back. + intrsctn_type_prev = INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST; + if (d_up > std::min(d_horiz, d_down)) + // The horizontal crossing comes earlier than the vertical crossing. + vert_seg_dir_valid_mask &= ~DIR_BACKWARD; + } + { + int d_horiz = (iNext == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, itsct.iSegment, segs[i_vline+1].intersections[iNext].iSegment, true); + int d_down = (iSegBelow == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, itsct.iSegment, iSegBelow, true); + int d_up = (iSegAbove == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, itsct.iSegment, iSegAbove, true); + if (d_up > std::min(d_horiz, d_down)) + // The horizontal crossing comes earlier than the vertical crossing. + vert_seg_dir_valid_mask &= ~DIR_FORWARD; + } + } +} +*/ + +enum DirectionMask +{ + DIR_FORWARD = 1, + DIR_BACKWARD = 2 +}; + +bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out) +{ + // At the end, only the new polylines will be rotated back. + size_t n_polylines_out_initial = polylines_out.size(); + + // Shrink the input polygon a bit first to not push the infill lines out of the perimeters. +// const float INFILL_OVERLAP_OVER_SPACING = 0.3f; + const float INFILL_OVERLAP_OVER_SPACING = 0.45f; + myassert(INFILL_OVERLAP_OVER_SPACING > 0 && INFILL_OVERLAP_OVER_SPACING < 0.5f); + + // Rotate polygons so that we can work with vertical lines here + std::pair rotate_vector = this->_infill_direction(*surface); + rotate_vector.first += angleBase; + + myassert(params.density > 0.0001f && params.density <= 1.f); + coord_t line_spacing = coord_t(scale_(this->spacing) / params.density); + + // On the polygons of poly_with_offset, the infill lines will be connected. + ExPolygonWithOffset poly_with_offset( + surface->expolygon, + - rotate_vector.first, + scale_(- (0.5 - INFILL_OVERLAP_OVER_SPACING) * this->spacing), + scale_(- 0.5 * this->spacing)); + if (poly_with_offset.n_contours_inner == 0) { + // Not a single infill line fits. + //FIXME maybe one shall trigger the gap fill here? + return true; + } + + BoundingBox bounding_box(poly_with_offset.polygons_src); + + // define flow spacing according to requested density + bool full_infill = params.density > 0.9999f; + if (full_infill && !params.dont_adjust) { + line_spacing = this->adjust_solid_spacing(bounding_box.size().x, line_spacing); + this->spacing = unscale(line_spacing); + } else { + // extend bounding box so that our pattern will be aligned with other layers + // Transform the reference point to the rotated coordinate system. + Point refpt = rotate_vector.second.rotated(- rotate_vector.first); + // _align_to_grid will not work correctly with positive pattern_shift. + coord_t pattern_shift_scaled = coord_t(scale_(pattern_shift)) % line_spacing; + refpt.x -= (pattern_shift_scaled > 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled); + bounding_box.min.align_to_grid( + Point(line_spacing, line_spacing), + refpt + ); + } + + // Intersect a set of euqally spaced vertical lines wiht expolygon. + // n_vlines = ceil(bbox_width / line_spacing) + size_t n_vlines = (bounding_box.max.x - bounding_box.min.x + line_spacing - 1) / line_spacing; + coord_t x0 = bounding_box.min.x + (line_spacing + SCALED_EPSILON) / 2; + +#ifdef SLIC3R_DEBUG + static int iRun = 0; + BoundingBox bbox_svg(to_points(poly_with_offset.polygons_outer)); + ::Slic3r::SVG svg(debug_out_path("FillRectilinear2-%d.svg", iRun), bbox_svg); // , scale_(1.)); + poly_with_offset.export_to_svg(svg); + { + ::Slic3r::SVG svg(debug_out_path("FillRectilinear2-initial-%d.svg", iRun), bbox_svg); // , scale_(1.)); + poly_with_offset.export_to_svg(svg); + } + iRun ++; +#endif /* SLIC3R_DEBUG */ + + // For each contour + // Allocate storage for the segments. + std::vector segs(n_vlines, SegmentedIntersectionLine()); + for (size_t i = 0; i < n_vlines; ++ i) { + segs[i].idx = i; + segs[i].pos = x0 + i * line_spacing; + } + for (size_t iContour = 0; iContour < poly_with_offset.n_contours; ++ iContour) { + const Points &contour = poly_with_offset.contour(iContour).points; + if (contour.size() < 2) + continue; + // For each segment + for (size_t iSegment = 0; iSegment < contour.size(); ++ iSegment) { + size_t iPrev = ((iSegment == 0) ? contour.size() : iSegment) - 1; + const Point &p1 = contour[iPrev]; + const Point &p2 = contour[iSegment]; + // Which of the equally spaced vertical lines is intersected by this segment? + coord_t l = p1.x; + coord_t r = p2.x; + if (l > r) + std::swap(l, r); + // il, ir are the left / right indices of vertical lines intersecting a segment + int il = (l - x0) / line_spacing; + while (il * line_spacing + x0 < l) + ++ il; + il = std::max(int(0), il); + int ir = (r - x0 + line_spacing) / line_spacing; + while (ir * line_spacing + x0 > r) + -- ir; + ir = std::min(int(segs.size()) - 1, ir); + if (il > ir) + // No vertical line intersects this segment. + continue; + myassert(il >= 0 && il < segs.size()); + myassert(ir >= 0 && ir < segs.size()); + for (int i = il; i <= ir; ++ i) { + coord_t this_x = segs[i].pos; + assert(this_x == i * line_spacing + x0); + SegmentIntersection is; + is.iContour = iContour; + is.iSegment = iSegment; + myassert(l <= this_x); + myassert(r >= this_x); + // Calculate the intersection position in y axis. x is known. + if (p1.x == this_x) { + if (p2.x == this_x) { + // Ignore strictly vertical segments. + continue; + } + is.pos_p = p1.y; + is.pos_q = 1; + } else if (p2.x == this_x) { + is.pos_p = p2.y; + is.pos_q = 1; + } else { + // First calculate the intersection parameter 't' as a rational number with non negative denominator. + if (p2.x > p1.x) { + is.pos_p = this_x - p1.x; + is.pos_q = p2.x - p1.x; + } else { + is.pos_p = p1.x - this_x; + is.pos_q = p1.x - p2.x; + } + myassert(is.pos_p >= 0 && is.pos_p <= is.pos_q); + // Make an intersection point from the 't'. + is.pos_p *= int64_t(p2.y - p1.y); + is.pos_p += p1.y * int64_t(is.pos_q); + } + // +-1 to take rounding into account. + myassert(is.pos() + 1 >= std::min(p1.y, p2.y)); + myassert(is.pos() <= std::max(p1.y, p2.y) + 1); + segs[i].intersections.push_back(is); + } + } + } + + // Sort the intersections along their segments, specify the intersection types. + for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) { + SegmentedIntersectionLine &sil = segs[i_seg]; + // Sort the intersection points using exact rational arithmetic. + std::sort(sil.intersections.begin(), sil.intersections.end()); + +#if 0 + // Verify the order, bubble sort the intersections until sorted. + bool modified = false; + do { + modified = false; + for (size_t i = 1; i < sil.intersections.size(); ++ i) { + size_t iContour1 = sil.intersections[i-1].iContour; + size_t iContour2 = sil.intersections[i].iContour; + const Points &contour1 = poly_with_offset.contour(iContour1).points; + const Points &contour2 = poly_with_offset.contour(iContour2).points; + size_t iSegment1 = sil.intersections[i-1].iSegment; + size_t iPrev1 = ((iSegment1 == 0) ? contour1.size() : iSegment1) - 1; + size_t iSegment2 = sil.intersections[i].iSegment; + size_t iPrev2 = ((iSegment2 == 0) ? contour2.size() : iSegment2) - 1; + bool swap = false; + if (iContour1 == iContour2 && iSegment1 == iSegment2) { + // The same segment, it has to be vertical. + myassert(iPrev1 == iPrev2); + swap = contour1[iPrev1].y > contour1[iContour1].y; + #ifdef SLIC3R_DEBUG + if (swap) + printf("Swapping when single vertical segment\n"); + #endif + } else { + // Segments are in a general position. Here an exact airthmetics may come into play. + coord_t y1max = std::max(contour1[iPrev1].y, contour1[iSegment1].y); + coord_t y2min = std::min(contour2[iPrev2].y, contour2[iSegment2].y); + if (y1max < y2min) { + // The segments are separated, nothing to do. + } else { + // Use an exact predicate to verify, that segment1 is below segment2. + const Point *a = &contour1[iPrev1]; + const Point *b = &contour1[iSegment1]; + const Point *c = &contour2[iPrev2]; + const Point *d = &contour2[iSegment2]; +#ifdef SLIC3R_DEBUG + const Point x1(sil.pos, sil.intersections[i-1].pos); + const Point x2(sil.pos, sil.intersections[i ].pos); + bool successive = false; +#endif /* SLIC3R_DEBUG */ + // Sort the points in the two segments by x. + if (a->x > b->x) + std::swap(a, b); + if (c->x > d->x) + std::swap(c, d); + myassert(a->x <= sil.pos); + myassert(c->x <= sil.pos); + myassert(b->x >= sil.pos); + myassert(d->x >= sil.pos); + // Sort the two segments, so the segment will be on the left of . + bool upper_more_left = false; + if (a->x > c->x) { + upper_more_left = true; + std::swap(a, c); + std::swap(b, d); + } + if (a == c) { + // The segments iSegment1 and iSegment2 are directly connected. + myassert(iContour1 == iContour2); + myassert(iSegment1 == iPrev2 || iPrev1 == iSegment2); + std::swap(c, d); + myassert(a != c && b != c); +#ifdef SLIC3R_DEBUG + successive = true; +#endif /* SLIC3R_DEBUG */ + } +#ifdef SLIC3R_DEBUG + else if (b == d) { + // The segments iSegment1 and iSegment2 are directly connected. + myassert(iContour1 == iContour2); + myassert(iSegment1 == iPrev2 || iPrev1 == iSegment2); + myassert(a != c && b != c); + successive = true; + } +#endif /* SLIC3R_DEBUG */ + Orientation o = orient(*a, *b, *c); + myassert(o != ORIENTATION_COLINEAR); + swap = upper_more_left != (o == ORIENTATION_CW); +#ifdef SLIC3R_DEBUG + if (swap) + printf(successive ? + "Swapping when iContour1 == iContour2 and successive segments\n" : + "Swapping when exact predicate\n"); +#endif + } + } + if (swap) { + // Swap the intersection points, but keep the original positions, so they stay sorted by the y axis. + std::swap(sil.intersections[i-1], sil.intersections[i]); + std::swap(sil.intersections[i-1].pos_p, sil.intersections[i].pos_p); + std::swap(sil.intersections[i-1].pos_q, sil.intersections[i].pos_q); + modified = true; + } + } + } while (modified); +#endif + + // Assign the intersection types, remove duplicate or overlapping intersection points. + // When a loop vertex touches a vertical line, intersection point is generated for both segments. + // If such two segments are oriented equally, then one of them is removed. + // Otherwise the vertex is tangential to the vertical line and both segments are removed. + // The same rule applies, if the loop is pinched into a single point and this point touches the vertical line: + // The loop has a zero vertical size at the vertical line, therefore the intersection point is removed. + size_t j = 0; + for (size_t i = 0; i < sil.intersections.size(); ++ i) { + // What is the orientation of the segment at the intersection point? + size_t iContour = sil.intersections[i].iContour; + const Points &contour = poly_with_offset.contour(iContour).points; + size_t iSegment = sil.intersections[i].iSegment; + size_t iPrev = ((iSegment == 0) ? contour.size() : iSegment) - 1; + coord_t dir = contour[iSegment].x - contour[iPrev].x; + // bool ccw = poly_with_offset.is_contour_ccw(iContour); + // bool low = (dir > 0) == ccw; + bool low = dir > 0; + sil.intersections[i].type = poly_with_offset.is_contour_outer(iContour) ? + (low ? SegmentIntersection::OUTER_LOW : SegmentIntersection::OUTER_HIGH) : + (low ? SegmentIntersection::INNER_LOW : SegmentIntersection::INNER_HIGH); + if (j > 0 && + sil.intersections[i].pos() == sil.intersections[j-1].pos() && + sil.intersections[i].iContour == sil.intersections[j-1].iContour) { + if (sil.intersections[i].type == sil.intersections[j-1].type) { + // This has to be a corner point crossing the vertical line. + // Remove the second intersection point. + #ifdef SLIC3R_DEBUG + size_t iSegment2 = sil.intersections[j-1].iSegment; + size_t iPrev2 = ((iSegment2 == 0) ? contour.size() : iSegment2) - 1; + myassert(iSegment == iPrev2 || iSegment2 == iPrev); + #endif /* SLIC3R_DEBUG */ + } else { + // This is a loop returning to the same point. + // It may as well be a vertex of a loop touching this vertical line. + // Remove both the lines. + -- j; + } + } else { + if (j < i) + sil.intersections[j] = sil.intersections[i]; + ++ j; + } + //FIXME solve a degenerate case, where there is a vertical segment on this vertical line and the contour + // follows from left to right or vice versa, leading to low,low or high,high intersections. + } + // Shrink the list of intersections, if any of the intersection was removed during the classification. + if (j < sil.intersections.size()) + sil.intersections.erase(sil.intersections.begin() + j, sil.intersections.end()); + } + + // Verify the segments. If something is wrong, give up. +#define ASSERT_OR_RETURN(CONDITION) do { assert(CONDITION); if (! (CONDITION)) return false; } while (0) + for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) { + SegmentedIntersectionLine &sil = segs[i_seg]; + // The intersection points have to be even. + ASSERT_OR_RETURN((sil.intersections.size() & 1) == 0); + for (size_t i = 0; i < sil.intersections.size();) { + // An intersection segment crossing the bigger contour may cross the inner offsetted contour even number of times. + ASSERT_OR_RETURN(sil.intersections[i].type == SegmentIntersection::OUTER_LOW); + size_t j = i + 1; + ASSERT_OR_RETURN(j < sil.intersections.size()); + ASSERT_OR_RETURN(sil.intersections[j].type == SegmentIntersection::INNER_LOW || sil.intersections[j].type == SegmentIntersection::OUTER_HIGH); + for (; j < sil.intersections.size() && sil.intersections[j].is_inner(); ++ j) ; + ASSERT_OR_RETURN(j < sil.intersections.size()); + ASSERT_OR_RETURN((j & 1) == 1); + ASSERT_OR_RETURN(sil.intersections[j].type == SegmentIntersection::OUTER_HIGH); + ASSERT_OR_RETURN(i + 1 == j || sil.intersections[j - 1].type == SegmentIntersection::INNER_HIGH); + i = j + 1; + } + } +#undef ASSERT_OR_RETURN + +#ifdef SLIC3R_DEBUG + // Paint the segments and finalize the SVG file. + for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) { + SegmentedIntersectionLine &sil = segs[i_seg]; + for (size_t i = 0; i < sil.intersections.size();) { + size_t j = i + 1; + for (; j < sil.intersections.size() && sil.intersections[j].is_inner(); ++ j) ; + if (i + 1 == j) { + svg.draw(Line(Point(sil.pos, sil.intersections[i].pos()), Point(sil.pos, sil.intersections[j].pos())), "blue"); + } else { + svg.draw(Line(Point(sil.pos, sil.intersections[i].pos()), Point(sil.pos, sil.intersections[i+1].pos())), "green"); + svg.draw(Line(Point(sil.pos, sil.intersections[i+1].pos()), Point(sil.pos, sil.intersections[j-1].pos())), (j - i + 1 > 4) ? "yellow" : "magenta"); + svg.draw(Line(Point(sil.pos, sil.intersections[j-1].pos()), Point(sil.pos, sil.intersections[j].pos())), "green"); + } + i = j + 1; + } + } + svg.Close(); +#endif /* SLIC3R_DEBUG */ + + // For each outer only chords, measure their maximum distance to the bow of the outer contour. + // Mark an outer only chord as consumed, if the distance is low. + for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) { + SegmentedIntersectionLine &seg = segs[i_vline]; + for (size_t i_intersection = 0; i_intersection + 1 < seg.intersections.size(); ++ i_intersection) { + if (seg.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW && + seg.intersections[i_intersection+1].type == SegmentIntersection::OUTER_HIGH) { + bool consumed = false; +// if (full_infill) { +// measure_outer_contour_slab(poly_with_offset, segs, i_vline, i_ntersection); +// } else + consumed = true; + seg.intersections[i_intersection].consumed_vertical_up = consumed; + } + } + } + + // Now construct a graph. + // Find the first point. + // Naively one would expect to achieve best results by chaining the paths by the shortest distance, + // but that procedure does not create the longest continuous paths. + // A simple "sweep left to right" procedure achieves better results. + size_t i_vline = 0; + size_t i_intersection = size_t(-1); + // Follow the line, connect the lines into a graph. + // Until no new line could be added to the output path: + Point pointLast; + Polyline *polyline_current = NULL; + if (! polylines_out.empty()) + pointLast = polylines_out.back().points.back(); + for (;;) { + if (i_intersection == size_t(-1)) { + // The path has been interrupted. Find a next starting point, closest to the previous extruder position. + coordf_t dist2min = std::numeric_limits().max(); + for (size_t i_vline2 = 0; i_vline2 < segs.size(); ++ i_vline2) { + const SegmentedIntersectionLine &seg = segs[i_vline2]; + if (! seg.intersections.empty()) { + myassert(seg.intersections.size() > 1); + // Even number of intersections with the loops. + myassert((seg.intersections.size() & 1) == 0); + myassert(seg.intersections.front().type == SegmentIntersection::OUTER_LOW); + for (size_t i = 0; i < seg.intersections.size(); ++ i) { + const SegmentIntersection &intrsctn = seg.intersections[i]; + if (intrsctn.is_outer()) { + myassert(intrsctn.is_low() || i > 0); + bool consumed = intrsctn.is_low() ? + intrsctn.consumed_vertical_up : + seg.intersections[i-1].consumed_vertical_up; + if (! consumed) { + coordf_t dist2 = sqr(coordf_t(pointLast.x - seg.pos)) + sqr(coordf_t(pointLast.y - intrsctn.pos())); + if (dist2 < dist2min) { + dist2min = dist2; + i_vline = i_vline2; + i_intersection = i; + //FIXME We are taking the first left point always. Verify, that the caller chains the paths + // by a shortest distance, while reversing the paths if needed. + //if (polylines_out.empty()) + // Initial state, take the first line, which is the first from the left. + goto found; + } + } + } + } + } + } + if (i_intersection == size_t(-1)) + // We are finished. + break; + found: + // Start a new path. + polylines_out.push_back(Polyline()); + polyline_current = &polylines_out.back(); + // Emit the first point of a path. + pointLast = Point(segs[i_vline].pos, segs[i_vline].intersections[i_intersection].pos()); + polyline_current->points.push_back(pointLast); + } + + // From the initial point (i_vline, i_intersection), follow a path. + SegmentedIntersectionLine &seg = segs[i_vline]; + SegmentIntersection *intrsctn = &seg.intersections[i_intersection]; + bool going_up = intrsctn->is_low(); + bool try_connect = false; + if (going_up) { + myassert(! intrsctn->consumed_vertical_up); + myassert(i_intersection + 1 < seg.intersections.size()); + // Step back to the beginning of the vertical segment to mark it as consumed. + if (intrsctn->is_inner()) { + myassert(i_intersection > 0); + -- intrsctn; + -- i_intersection; + } + // Consume the complete vertical segment up to the outer contour. + do { + intrsctn->consumed_vertical_up = true; + ++ intrsctn; + ++ i_intersection; + myassert(i_intersection < seg.intersections.size()); + } while (intrsctn->type != SegmentIntersection::OUTER_HIGH); + if ((intrsctn - 1)->is_inner()) { + // Step back. + -- intrsctn; + -- i_intersection; + myassert(intrsctn->type == SegmentIntersection::INNER_HIGH); + try_connect = true; + } + } else { + // Going down. + myassert(intrsctn->is_high()); + myassert(i_intersection > 0); + myassert(! (intrsctn - 1)->consumed_vertical_up); + // Consume the complete vertical segment up to the outer contour. + if (intrsctn->is_inner()) + intrsctn->consumed_vertical_up = true; + do { + myassert(i_intersection > 0); + -- intrsctn; + -- i_intersection; + intrsctn->consumed_vertical_up = true; + } while (intrsctn->type != SegmentIntersection::OUTER_LOW); + if ((intrsctn + 1)->is_inner()) { + // Step back. + ++ intrsctn; + ++ i_intersection; + myassert(intrsctn->type == SegmentIntersection::INNER_LOW); + try_connect = true; + } + } + if (try_connect) { + // Decide, whether to finish the segment, or whether to follow the perimeter. + + // 1) Find possible connection points on the previous / next vertical line. + int iPrev = intersection_on_prev_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection); + int iNext = intersection_on_next_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection); + IntersectionTypeOtherVLine intrsctn_type_prev = intersection_type_on_prev_vertical_line(segs, i_vline, i_intersection, iPrev); + IntersectionTypeOtherVLine intrsctn_type_next = intersection_type_on_next_vertical_line(segs, i_vline, i_intersection, iNext); + + // 2) Find possible connection points on the same vertical line. + int iAbove = -1; + int iBelow = -1; + int iSegAbove = -1; + int iSegBelow = -1; + { + SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ? + SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW; + // Does the perimeter intersect the current vertical line above intrsctn? + for (size_t i = i_intersection + 1; i + 1 < seg.intersections.size(); ++ i) +// if (seg.intersections[i].iContour == intrsctn->iContour && seg.intersections[i].type == type_crossing) { + if (seg.intersections[i].iContour == intrsctn->iContour) { + iAbove = i; + iSegAbove = seg.intersections[i].iSegment; + break; + } + // Does the perimeter intersect the current vertical line below intrsctn? + for (size_t i = i_intersection - 1; i > 0; -- i) +// if (seg.intersections[i].iContour == intrsctn->iContour && seg.intersections[i].type == type_crossing) { + if (seg.intersections[i].iContour == intrsctn->iContour) { + iBelow = i; + iSegBelow = seg.intersections[i].iSegment; + break; + } + } + + // 3) Sort the intersection points, clear iPrev / iNext / iSegBelow / iSegAbove, + // if it is preceded by any other intersection point along the contour. + unsigned int vert_seg_dir_valid_mask = + (going_up ? + (iSegAbove != -1 && seg.intersections[iAbove].type == SegmentIntersection::INNER_LOW) : + (iSegBelow != -1 && seg.intersections[iBelow].type == SegmentIntersection::INNER_HIGH)) ? + (DIR_FORWARD | DIR_BACKWARD) : + 0; + { + // Invalidate iPrev resp. iNext, if the perimeter crosses the current vertical line earlier than iPrev resp. iNext. + // The perimeter contour orientation. + const bool forward = intrsctn->is_low(); // == poly_with_offset.is_contour_ccw(intrsctn->iContour); + const Polygon &poly = poly_with_offset.contour(intrsctn->iContour); + { + int d_horiz = (iPrev == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, segs[i_vline-1].intersections[iPrev].iSegment, intrsctn->iSegment, forward); + int d_down = (iSegBelow == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, iSegBelow, intrsctn->iSegment, forward); + int d_up = (iSegAbove == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, iSegAbove, intrsctn->iSegment, forward); + if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && d_horiz > std::min(d_down, d_up)) + // The vertical crossing comes eralier than the prev crossing. + // Disable the perimeter going back. + intrsctn_type_prev = INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST; + if (going_up ? (d_up > std::min(d_horiz, d_down)) : (d_down > std::min(d_horiz, d_up))) + // The horizontal crossing comes earlier than the vertical crossing. + vert_seg_dir_valid_mask &= ~(forward ? DIR_BACKWARD : DIR_FORWARD); + } + { + int d_horiz = (iNext == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, intrsctn->iSegment, segs[i_vline+1].intersections[iNext].iSegment, forward); + int d_down = (iSegBelow == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, intrsctn->iSegment, iSegBelow, forward); + int d_up = (iSegAbove == -1) ? std::numeric_limits::max() : + distance_of_segmens(poly, intrsctn->iSegment, iSegAbove, forward); + if (intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK && d_horiz > std::min(d_down, d_up)) + // The vertical crossing comes eralier than the prev crossing. + // Disable the perimeter going forward. + intrsctn_type_next = INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST; + if (going_up ? (d_up > std::min(d_horiz, d_down)) : (d_down > std::min(d_horiz, d_up))) + // The horizontal crossing comes earlier than the vertical crossing. + vert_seg_dir_valid_mask &= ~(forward ? DIR_FORWARD : DIR_BACKWARD); + } + } + + // 4) Try to connect to a previous or next vertical line, making a zig-zag pattern. + if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK || intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) { + coordf_t distPrev = (intrsctn_type_prev != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : + measure_perimeter_prev_segment_length(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iPrev); + coordf_t distNext = (intrsctn_type_next != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : + measure_perimeter_next_segment_length(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iNext); + // Take the shorter path. + //FIXME this may not be always the best strategy to take the shortest connection line now. + bool take_next = (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) ? + (distNext < distPrev) : + intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK; + myassert(intrsctn->is_inner()); + bool skip = params.dont_connect || (link_max_length > 0 && (take_next ? distNext : distPrev) > link_max_length); + if (skip) { + // Just skip the connecting contour and start a new path. + goto dont_connect; + polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); + polylines_out.push_back(Polyline()); + polyline_current = &polylines_out.back(); + const SegmentedIntersectionLine &il2 = segs[take_next ? (i_vline + 1) : (i_vline - 1)]; + polyline_current->points.push_back(Point(il2.pos, il2.intersections[take_next ? iNext : iPrev].pos())); + } else { + polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); + emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, take_next ? iNext : iPrev, *polyline_current, take_next); + } + // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed. + if (iPrev != -1) + segs[i_vline-1].intersections[iPrev].consumed_perimeter_right = true; + if (iNext != -1) + intrsctn->consumed_perimeter_right = true; + //FIXME consume the left / right connecting segments at the other end of this line? Currently it is not critical because a perimeter segment is not followed if the vertical segment at the other side has already been consumed. + // Advance to the neighbor line. + if (take_next) { + ++ i_vline; + i_intersection = iNext; + } else { + -- i_vline; + i_intersection = iPrev; + } + continue; + } + + // 5) Try to connect to a previous or next point on the same vertical line. + if (vert_seg_dir_valid_mask) { + bool valid = true; + // Verify, that there is no intersection with the inner contour up to the end of the contour segment. + // Verify, that the successive segment has not been consumed yet. + if (going_up) { + if (seg.intersections[iAbove].consumed_vertical_up) { + valid = false; + } else { + for (int i = (int)i_intersection + 1; i < iAbove && valid; ++i) + if (seg.intersections[i].is_inner()) + valid = false; + } + } else { + if (seg.intersections[iBelow-1].consumed_vertical_up) { + valid = false; + } else { + for (int i = iBelow + 1; i < (int)i_intersection && valid; ++i) + if (seg.intersections[i].is_inner()) + valid = false; + } + } + if (valid) { + const Polygon &poly = poly_with_offset.contour(intrsctn->iContour); + int iNext = going_up ? iAbove : iBelow; + int iSegNext = going_up ? iSegAbove : iSegBelow; + bool dir_forward = (vert_seg_dir_valid_mask == (DIR_FORWARD | DIR_BACKWARD)) ? + // Take the shorter length between the current and the next intersection point. + (distance_of_segmens(poly, intrsctn->iSegment, iSegNext, true) < + distance_of_segmens(poly, intrsctn->iSegment, iSegNext, false)) : + (vert_seg_dir_valid_mask == DIR_FORWARD); + // Skip this perimeter line? + bool skip = params.dont_connect; + if (! skip && link_max_length > 0) { + coordf_t link_length = measure_perimeter_segment_on_vertical_line_length( + poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iNext, dir_forward); + skip = link_length > link_max_length; + } + polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); + if (skip) { + // Just skip the connecting contour and start a new path. + polylines_out.push_back(Polyline()); + polyline_current = &polylines_out.back(); + polyline_current->points.push_back(Point(seg.pos, seg.intersections[iNext].pos())); + } else { + // Consume the connecting contour and the next segment. + emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iNext, *polyline_current, dir_forward); + } + // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed. + // If there are any outer intersection points skipped (bypassed) by the contour, + // mark them as processed. + if (going_up) { + for (int i = (int)i_intersection; i < iAbove; ++ i) + seg.intersections[i].consumed_vertical_up = true; + } else { + for (int i = iBelow; i < (int)i_intersection; ++ i) + seg.intersections[i].consumed_vertical_up = true; + } +// seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true; + intrsctn->consumed_perimeter_right = true; + i_intersection = iNext; + if (going_up) + ++ intrsctn; + else + -- intrsctn; + intrsctn->consumed_perimeter_right = true; + continue; + } + } + dont_connect: + // No way to continue the current polyline. Take the rest of the line up to the outer contour. + // This will finish the polyline, starting another polyline at a new point. + if (going_up) + ++ intrsctn; + else + -- intrsctn; + } + + // Finish the current vertical line, + // reset the current vertical line to pick a new starting point in the next round. + myassert(intrsctn->is_outer()); + myassert(intrsctn->is_high() == going_up); + pointLast = Point(seg.pos, intrsctn->pos()); + polyline_current->points.push_back(pointLast); + // Handle duplicate points and zero length segments. + polyline_current->remove_duplicate_points(); + myassert(! polyline_current->has_duplicate_points()); + // Handle nearly zero length edges. + if (polyline_current->points.size() <= 1 || + (polyline_current->points.size() == 2 && + std::abs(polyline_current->points.front().x - polyline_current->points.back().x) < SCALED_EPSILON && + std::abs(polyline_current->points.front().y - polyline_current->points.back().y) < SCALED_EPSILON)) + polylines_out.pop_back(); + intrsctn = NULL; + i_intersection = -1; + polyline_current = NULL; + } + +#ifdef SLIC3R_DEBUG + { + { + ::Slic3r::SVG svg(debug_out_path("FillRectilinear2-final-%03d.svg", iRun), bbox_svg); // , scale_(1.)); + poly_with_offset.export_to_svg(svg); + for (size_t i = n_polylines_out_initial; i < polylines_out.size(); ++ i) + svg.draw(polylines_out[i].lines(), "black"); + } + // Paint a picture per polyline. This makes it easier to discover the order of the polylines and their overlap. + for (size_t i_polyline = n_polylines_out_initial; i_polyline < polylines_out.size(); ++ i_polyline) { + ::Slic3r::SVG svg(debug_out_path("FillRectilinear2-final-%03d-%03d.svg", iRun, i_polyline), bbox_svg); // , scale_(1.)); + svg.draw(polylines_out[i_polyline].lines(), "black"); + } + } +#endif /* SLIC3R_DEBUG */ + + // paths must be rotated back + for (Polylines::iterator it = polylines_out.begin() + n_polylines_out_initial; it != polylines_out.end(); ++ it) { + // No need to translate, the absolute position is irrelevant. + // it->translate(- rotate_vector.second.x, - rotate_vector.second.y); + myassert(! it->has_duplicate_points()); + it->rotate(rotate_vector.first); + //FIXME rather simplify the paths to avoid very short edges? + //myassert(! it->has_duplicate_points()); + it->remove_duplicate_points(); + } + +#ifdef SLIC3R_DEBUG + // Verify, that there are no duplicate points in the sequence. + for (Polylines::iterator it = polylines_out.begin(); it != polylines_out.end(); ++ it) + myassert(! it->has_duplicate_points()); +#endif /* SLIC3R_DEBUG */ + + return true; +} + +Polylines FillRectilinear2::fill_surface(const Surface &surface, const FillParams ¶ms) +{ + Polylines polylines_out; + if (! fill_surface_by_lines(&surface, params, 0.f, 0.f, polylines_out)) { + printf("FillRectilinear2::fill_surface() failed to fill a region.\n"); + } + return polylines_out; +} + +Polylines FillGrid2::fill_surface(const Surface &surface, const FillParams ¶ms) +{ + // Each linear fill covers half of the target coverage. + FillParams params2 = params; + params2.density *= 0.5f; + Polylines polylines_out; + if (! fill_surface_by_lines(&surface, params2, 0.f, 0.f, polylines_out) || + ! fill_surface_by_lines(&surface, params2, float(M_PI / 2.), 0.f, polylines_out)) { + printf("FillGrid2::fill_surface() failed to fill a region.\n"); + } + return polylines_out; +} + +Polylines FillTriangles::fill_surface(const Surface &surface, const FillParams ¶ms) +{ + // Each linear fill covers 1/3 of the target coverage. + FillParams params2 = params; + params2.density *= 0.333333333f; + Polylines polylines_out; + if (! fill_surface_by_lines(&surface, params2, 0.f, 0., polylines_out) || + ! fill_surface_by_lines(&surface, params2, float(M_PI / 3.), 0., polylines_out) || + ! fill_surface_by_lines(&surface, params2, float(2. * M_PI / 3.), 0.5 * this->spacing / params2.density, polylines_out)) { + printf("FillTriangles::fill_surface() failed to fill a region.\n"); + } + return polylines_out; +} + +Polylines FillStars::fill_surface(const Surface &surface, const FillParams ¶ms) +{ + // Each linear fill covers 1/3 of the target coverage. + FillParams params2 = params; + params2.density *= 0.333333333f; + Polylines polylines_out; + if (! fill_surface_by_lines(&surface, params2, 0.f, 0., polylines_out) || + ! fill_surface_by_lines(&surface, params2, float(M_PI / 3.), 0., polylines_out) || + ! fill_surface_by_lines(&surface, params2, float(2. * M_PI / 3.), 0., polylines_out)) { + printf("FillStars::fill_surface() failed to fill a region.\n"); + } + return polylines_out; +} + +Polylines FillCubic::fill_surface(const Surface &surface, const FillParams ¶ms) +{ + // Each linear fill covers 1/3 of the target coverage. + FillParams params2 = params; + params2.density *= 0.333333333f; + Polylines polylines_out; + if (! fill_surface_by_lines(&surface, params2, 0.f, z, polylines_out) || + ! fill_surface_by_lines(&surface, params2, float(M_PI / 3.), -z, polylines_out) || + // Rotated by PI*2/3 + PI to achieve reverse sloping wall. + ! fill_surface_by_lines(&surface, params2, float(M_PI * 2. / 3.), z, polylines_out)) { + printf("FillCubic::fill_surface() failed to fill a region.\n"); + } + return polylines_out; +} + +static inline bool is_stick(const Point &p1, const Point &p2, const Point &p3) +{ + Point v1 = p2 - p1; + Point v2 = p3 - p2; + int64_t dir = int64_t(v1.x) * int64_t(v2.x) + int64_t(v1.y) * int64_t(v2.y); + if (dir > 0) + // p3 does not turn back to p1. Do not remove p2. + return false; + double l2_1 = double(v1.x) * double(v1.x) + double(v1.y) * double(v1.y); + double l2_2 = double(v2.x) * double(v2.x) + double(v2.y) * double(v2.y); + if (dir == 0) + // p1, p2, p3 may make a perpendicular corner, or there is a zero edge length. + // Remove p2 if it is coincident with p1 or p2. + return l2_1 == 0 || l2_2 == 0; + // p3 turns back to p1 after p2. Are p1, p2, p3 collinear? + // Calculate distance from p3 to a segment (p1, p2) or from p1 to a segment(p2, p3), + // whichever segment is longer + double cross = double(v1.x) * double(v2.y) - double(v2.x) * double(v1.y); + double dist2 = cross * cross / std::max(l2_1, l2_2); + return dist2 < EPSILON * EPSILON; +} + +bool remove_sticks(Polygon &poly) +{ + bool modified = false; + size_t j = 1; + for (size_t i = 1; i + 1 < poly.points.size(); ++ i) { + if (! is_stick(poly[j-1], poly[i], poly[i+1])) { + // Keep the point. + if (j < i) + poly.points[j] = poly.points[i]; + ++ j; + } + } + if (++ j < poly.points.size()) { + poly.points[j-1] = poly.points.back(); + poly.points.erase(poly.points.begin() + j, poly.points.end()); + modified = true; + } + while (poly.points.size() >= 3 && is_stick(poly.points[poly.points.size()-2], poly.points.back(), poly.points.front())) { + poly.points.pop_back(); + modified = true; + } + while (poly.points.size() >= 3 && is_stick(poly.points.back(), poly.points.front(), poly.points[1])) + poly.points.erase(poly.points.begin()); + return modified; +} + +bool remove_sticks(Polygons &polys) +{ + bool modified = false; + size_t j = 0; + for (size_t i = 0; i < polys.size(); ++ i) { + modified |= remove_sticks(polys[i]); + if (polys[i].points.size() >= 3) { + if (j < i) + std::swap(polys[i].points, polys[j].points); + ++ j; + } + } + if (j < polys.size()) + polys.erase(polys.begin() + j, polys.end()); + return modified; +} + +bool remove_sticks(ExPolygon &poly) +{ + return remove_sticks(poly.contour) || remove_sticks(poly.holes); +} + +bool remove_small(Polygons &polys, double min_area) +{ + bool modified = false; + size_t j = 0; + for (size_t i = 0; i < polys.size(); ++ i) { + if (std::abs(polys[i].area()) >= min_area) { + if (j < i) + std::swap(polys[i].points, polys[j].points); + ++ j; + } else + modified = true; + } + if (j < polys.size()) + polys.erase(polys.begin() + j, polys.end()); + return modified; +} + + + +} // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.hpp b/xs/src/libslic3r/Fill/FillRectilinear2.hpp new file mode 100644 index 000000000..f1f8973de --- /dev/null +++ b/xs/src/libslic3r/Fill/FillRectilinear2.hpp @@ -0,0 +1,76 @@ +#ifndef slic3r_FillRectilinear2_hpp_ +#define slic3r_FillRectilinear2_hpp_ + +#include "../libslic3r.h" + +#include "FillBase.hpp" + +namespace Slic3r { + +class Surface; + +class FillRectilinear2 : public Fill +{ +public: + virtual ~FillRectilinear2() {} + virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms); + +protected: + bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out); +}; + +class FillGrid2 : public FillRectilinear2 +{ +public: + virtual ~FillGrid2() {} + virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms); + +protected: + // The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill. + virtual float _layer_angle(size_t idx) const { return 0.f; } +}; + +class FillTriangles : public FillRectilinear2 +{ +public: + virtual ~FillTriangles() {} + virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms); + +protected: + // The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill. + virtual float _layer_angle(size_t idx) const { return 0.f; } +}; + +class FillStars : public FillRectilinear2 +{ +public: + virtual ~FillStars() {} + virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms); + +protected: + // The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill. + virtual float _layer_angle(size_t idx) const { return 0.f; } +}; + +class FillCubic : public FillRectilinear2 +{ +public: + virtual ~FillCubic() {} + virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms); + +protected: + // The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill. + virtual float _layer_angle(size_t idx) const { return 0.f; } +}; + + +// Remove sticks (tentacles with zero area) from the polygon. +extern bool remove_sticks(Polygon &poly); +extern bool remove_sticks(Polygons &polys); +extern bool remove_sticks(ExPolygon &poly); +extern bool remove_small(Polygons &polys, double min_area); + + +}; // namespace Slic3r + +#endif // slic3r_FillRectilinear2_hpp_ diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp index 55d3c8811..da71552c9 100644 --- a/xs/src/libslic3r/Layer.cpp +++ b/xs/src/libslic3r/Layer.cpp @@ -2,7 +2,7 @@ #include "ClipperUtils.hpp" #include "Geometry.hpp" #include "Print.hpp" - +#include "Fill/Fill.hpp" namespace Slic3r { @@ -47,18 +47,6 @@ Layer::set_id(size_t id) this->_id = id; } -PrintObject* -Layer::object() -{ - return this->_object; -} - -const PrintObject* -Layer::object() const -{ - return this->_object; -} - size_t Layer::region_count() const @@ -249,16 +237,23 @@ Layer::make_perimeters() } } - -SupportLayer::SupportLayer(size_t id, PrintObject *object, coordf_t height, - coordf_t print_z, coordf_t slice_z) -: Layer(id, object, height, print_z, slice_z) +void +Layer::make_fills() { + #ifdef SLIC3R_DEBUG + printf("Making fills for layer %zu\n", this->id()); + #endif + + FOREACH_LAYERREGION(this, it_layerm) { + LayerRegion &layerm = **it_layerm; + layerm.fills.clear(); + make_fill(layerm, &layerm.fills); + + #ifndef NDEBUG + for (size_t i = 0; i < layerm.fills.entities.size(); ++i) + assert(dynamic_cast(layerm.fills.entities[i]) != NULL); + #endif + } } -SupportLayer::~SupportLayer() -{ -} - - } diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp index 4badb8374..59badcb1f 100644 --- a/xs/src/libslic3r/Layer.hpp +++ b/xs/src/libslic3r/Layer.hpp @@ -25,8 +25,10 @@ class LayerRegion friend class Layer; public: - Layer* layer(); - PrintRegion* region(); + Layer* layer() { return this->_layer; }; + const Layer* layer() const { return this->_layer; }; + PrintRegion* region() { return this->_region; }; + const PrintRegion* region() const { return this->_region; }; // collection of surfaces generated by slicing the original geometry // divided by type top/bottom/internal @@ -64,8 +66,9 @@ class LayerRegion Layer *_layer; PrintRegion *_region; - LayerRegion(Layer *layer, PrintRegion *region); - ~LayerRegion(); + LayerRegion(Layer *layer, PrintRegion *region) + : _layer(layer), _region(region) {}; + ~LayerRegion() {}; }; @@ -77,8 +80,8 @@ class Layer { public: size_t id() const; void set_id(size_t id); - PrintObject* object(); - const PrintObject* object() const; + PrintObject* object() { return this->_object; }; + const PrintObject* object() const { return this->_object; }; Layer *upper_layer; Layer *lower_layer; @@ -102,10 +105,11 @@ class Layer { template bool any_internal_region_slice_contains(const T &item) const; template bool any_bottom_region_slice_contains(const T &item) const; void make_perimeters(); + void make_fills(); protected: size_t _id; // sequential number of layer, 0-based - PrintObject *_object; + PrintObject* _object; Layer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, @@ -126,9 +130,10 @@ class SupportLayer : public Layer { ExtrusionEntityCollection support_interface_fills; protected: - SupportLayer(size_t id, PrintObject *object, coordf_t height, coordf_t print_z, - coordf_t slice_z); - virtual ~SupportLayer(); + SupportLayer(size_t id, PrintObject *object, coordf_t height, + coordf_t print_z, coordf_t slice_z) + : Layer(id, object, height, print_z, slice_z) {}; + virtual ~SupportLayer() {}; }; diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index 9f803094e..b97fa0727 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -7,28 +7,6 @@ namespace Slic3r { -LayerRegion::LayerRegion(Layer *layer, PrintRegion *region) -: _layer(layer), - _region(region) -{ -} - -LayerRegion::~LayerRegion() -{ -} - -Layer* -LayerRegion::layer() -{ - return this->_layer; -} - -PrintRegion* -LayerRegion::region() -{ - return this->_region; -} - Flow LayerRegion::flow(FlowRole role, bool bridge, double width) const { @@ -167,18 +145,16 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer) { // merge top and bottom in a single collection SurfaceCollection tb = top; - tb.surfaces.insert(tb.surfaces.end(), bottom.surfaces.begin(), bottom.surfaces.end()); + tb.append(bottom); // group surfaces - std::vector groups; + std::vector groups; tb.group(&groups); - for (std::vector::const_iterator g = groups.begin(); g != groups.end(); ++g) { + for (std::vector::const_iterator g = groups.begin(); g != groups.end(); ++g) { Polygons subject; - for (SurfacesPtr::const_iterator s = g->begin(); s != g->end(); ++s) { - Polygons pp = **s; - subject.insert(subject.end(), pp.begin(), pp.end()); - } + for (SurfacesConstPtr::const_iterator s = g->begin(); s != g->end(); ++s) + append_to(subject, (Polygons)**s); ExPolygons expp = intersection_ex( subject, @@ -203,15 +179,13 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer) } // group surfaces - std::vector groups; + std::vector groups; other.group(&groups); - for (std::vector::const_iterator g = groups.begin(); g != groups.end(); ++g) { + for (std::vector::const_iterator g = groups.begin(); g != groups.end(); ++g) { Polygons subject; - for (SurfacesPtr::const_iterator s = g->begin(); s != g->end(); ++s) { - Polygons pp = **s; - subject.insert(subject.end(), pp.begin(), pp.end()); - } + for (SurfacesConstPtr::const_iterator s = g->begin(); s != g->end(); ++s) + append_to(subject, (Polygons)**s); ExPolygons expp = diff_ex( subject, diff --git a/xs/src/libslic3r/MultiPoint.cpp b/xs/src/libslic3r/MultiPoint.cpp index 701fa4c3a..297e7ec3c 100644 --- a/xs/src/libslic3r/MultiPoint.cpp +++ b/xs/src/libslic3r/MultiPoint.cpp @@ -33,22 +33,26 @@ MultiPoint::translate(const Point &vector) void MultiPoint::rotate(double angle) { - double s = sin(angle); - double c = cos(angle); + double s = sin(angle); + double c = cos(angle); for (Points::iterator it = points.begin(); it != points.end(); ++it) { - (*it).rotate(angle); - double cur_x = (double)it->x; - double cur_y = (double)it->y; - it->x = (coord_t)round(c * cur_x - s * cur_y); - it->y = (coord_t)round(c * cur_y + s * cur_x); + double cur_x = (double)it->x; + double cur_y = (double)it->y; + it->x = (coord_t)round(c * cur_x - s * cur_y); + it->y = (coord_t)round(c * cur_y + s * cur_x); } } void MultiPoint::rotate(double angle, const Point ¢er) { + double s = sin(angle); + double c = cos(angle); for (Points::iterator it = points.begin(); it != points.end(); ++it) { - (*it).rotate(angle, center); + double dx = double(it->x - center.x); + double dy = double(it->y - center.y); + it->x = (coord_t)round(double(center.x) + c * dx - s * dy); + it->y = (coord_t)round(double(center.y) + c * dy + s * dx); } } @@ -97,15 +101,33 @@ MultiPoint::bounding_box() const return BoundingBox(this->points); } -void +bool +MultiPoint::has_duplicate_points() const +{ + for (size_t i = 1; i < points.size(); ++i) + if (points[i-1].coincides_with(points[i])) + return true; + return false; +} + +bool MultiPoint::remove_duplicate_points() { - for (size_t i = 1; i < this->points.size(); ++i) { - if (this->points.at(i).coincides_with(this->points.at(i-1))) { - this->points.erase(this->points.begin() + i); - --i; + size_t j = 0; + for (size_t i = 1; i < points.size(); ++i) { + if (points[j].coincides_with(points[i])) { + // Just increase index i. + } else { + ++ j; + if (j < i) + points[j] = points[i]; } } + if (++ j < points.size()) { + points.erase(points.begin() + j, points.end()); + return true; + } + return false; } void diff --git a/xs/src/libslic3r/MultiPoint.hpp b/xs/src/libslic3r/MultiPoint.hpp index 9c71f42f4..99bc79281 100644 --- a/xs/src/libslic3r/MultiPoint.hpp +++ b/xs/src/libslic3r/MultiPoint.hpp @@ -34,7 +34,13 @@ class MultiPoint int find_point(const Point &point) const; bool has_boundary_point(const Point &point) const; BoundingBox bounding_box() const; - void remove_duplicate_points(); + + // Return true if there are exact duplicates. + bool has_duplicate_points() const; + + // Remove exact duplicates, return true if any duplicate has been removed. + bool remove_duplicate_points(); + void append(const Point &point); void append(const Points &points); void append(const Points::const_iterator &begin, const Points::const_iterator &end); diff --git a/xs/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp index c70164e19..ba4576a21 100644 --- a/xs/src/libslic3r/Point.cpp +++ b/xs/src/libslic3r/Point.cpp @@ -72,8 +72,10 @@ Point::rotate(double angle, const Point ¢er) double cur_y = (double)this->y; double s = sin(angle); double c = cos(angle); - this->x = (coord_t)round( (double)center.x + c * (cur_x - (double)center.x) - s * (cur_y - (double)center.y) ); - this->y = (coord_t)round( (double)center.y + c * (cur_y - (double)center.y) + s * (cur_x - (double)center.x) ); + double dx = cur_x - (double)center.x; + double dy = cur_y - (double)center.y; + this->x = (coord_t)round( (double)center.x + c * dx - s * dy ); + this->y = (coord_t)round( (double)center.y + c * dy + s * dx ); } bool @@ -302,6 +304,27 @@ Point::vector_to(const Point &point) const return Vector(point.x - this->x, point.y - this->y); } +// Align a coordinate to a grid. The coordinate may be negative, +// the aligned value will never be bigger than the original one. +static coord_t +_align_to_grid(const coord_t coord, const coord_t spacing) { + // Current C++ standard defines the result of integer division to be rounded to zero, + // for both positive and negative numbers. Here we want to round down for negative + // numbers as well. + coord_t aligned = (coord < 0) ? + ((coord - spacing + 1) / spacing) * spacing : + (coord / spacing) * spacing; + assert(aligned <= coord); + return aligned; +} + +void +Point::align_to_grid(const Point &spacing, const Point &base) +{ + this->x = base.x + _align_to_grid(this->x - base.x, spacing.x); + this->y = base.y + _align_to_grid(this->y - base.y, spacing.y); +} + Point operator+(const Point& point1, const Point& point2) { diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp index 9c23252cb..c6c340446 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/xs/src/libslic3r/Point.hpp @@ -2,10 +2,10 @@ #define slic3r_Point_hpp_ #include "libslic3r.h" -#include #include -#include #include +#include +#include namespace Slic3r { @@ -44,6 +44,16 @@ class Point void translate(const Vector &vector); void rotate(double angle); void rotate(double angle, const Point ¢er); + Point rotated(double angle) const { + Point p(*this); + p.rotate(angle); + return p; + } + Point rotated(double angle, const Point ¢er) const { + Point p(*this); + p.rotate(angle, center); + return p; + } bool coincides_with(const Point &point) const { return this->x == point.x && this->y == point.y; } bool coincides_with_epsilon(const Point &point) const; int nearest_point_index(const Points &points) const; @@ -62,6 +72,7 @@ class Point Point projection_onto(const Line &line) const; Point negative() const; Vector vector_to(const Point &point) const; + void align_to_grid(const Point &spacing, const Point &base = Point(0,0)); }; Point operator+(const Point& point1, const Point& point2); @@ -116,6 +127,16 @@ class Pointf3 : public Pointf Vectorf3 vector_to(const Pointf3 &point) const; }; +template +inline Points +to_points(const std::vector &items) +{ + Points pp; + for (typename std::vector::const_iterator it = items.begin(); it != items.end(); ++it) + append_to(pp, (Points)*it); + return pp; +} + } // start Boost diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp index e3ea13f24..415aa404e 100644 --- a/xs/src/libslic3r/Polygon.hpp +++ b/xs/src/libslic3r/Polygon.hpp @@ -6,11 +6,11 @@ #include #include "Line.hpp" #include "MultiPoint.hpp" -#include "Polyline.hpp" namespace Slic3r { class Polygon; +class Polyline; typedef std::vector Polygons; class Polygon : public MultiPoint { diff --git a/xs/src/libslic3r/Polyline.hpp b/xs/src/libslic3r/Polyline.hpp index 4d6673f2c..dfd87fef7 100644 --- a/xs/src/libslic3r/Polyline.hpp +++ b/xs/src/libslic3r/Polyline.hpp @@ -4,6 +4,7 @@ #include "libslic3r.h" #include "Line.hpp" #include "MultiPoint.hpp" +#include "Polygon.hpp" #include #include @@ -42,6 +43,24 @@ class ThickPolyline : public Polyline { void reverse(); }; +inline Polylines +to_polylines(const Polygons &polygons) +{ + Polylines pp; + for (Polygons::const_iterator it = polygons.begin(); it != polygons.end(); ++it) + pp.push_back((Polyline)*it); + return pp; +} + +inline Polylines +to_polylines(const Lines &lines) +{ + Polylines pp; + for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) + pp.push_back((Polyline)*it); + return pp; +} + } #endif diff --git a/xs/src/libslic3r/PolylineCollection.cpp b/xs/src/libslic3r/PolylineCollection.cpp index 136065127..f92db0b03 100644 --- a/xs/src/libslic3r/PolylineCollection.cpp +++ b/xs/src/libslic3r/PolylineCollection.cpp @@ -10,9 +10,8 @@ struct Chaining }; #ifndef sqr -template -inline sqr(T x) { return x * x; } -#endif /* sqr */ + #define sqr(x) (x * x); +#endif template inline int nearest_point_index(const std::vector &pairs, const Point &start_near, bool no_reverse) @@ -43,18 +42,17 @@ inline int nearest_point_index(const std::vector &pairs, const Point & } } } - return idx; } -Polylines PolylineCollection::chained_path_from( -#if SLIC3R_CPPVER > 11 - Polylines &&src, -#else +Polylines PolylineCollection::_chained_path_from( const Polylines &src, + Point start_near, + bool no_reverse +#if SLIC3R_CPPVER >= 11 + , bool move_from_src #endif - Point start_near, - bool no_reverse) + ) { std::vector endpoints; endpoints.reserve(src.size()); @@ -64,14 +62,19 @@ Polylines PolylineCollection::chained_path_from( if (! no_reverse) c.last = src[i].last_point(); c.idx = i; + endpoints.push_back(c); } - Polylines retval; while (! endpoints.empty()) { // find nearest point int endpoint_index = nearest_point_index(endpoints, start_near, no_reverse); + assert(endpoint_index >= 0 && endpoint_index < endpoints.size() * 2); #if SLIC3R_CPPVER > 11 - retval.push_back(std::move(src[endpoints[endpoint_index/2].idx])); + if (move_from_src) { + retval.push_back(std::move(src[endpoints[endpoint_index/2].idx])); + } else { + retval.push_back(src[endpoints[endpoint_index/2].idx]); + } #else retval.push_back(src[endpoints[endpoint_index/2].idx]); #endif @@ -80,33 +83,42 @@ Polylines PolylineCollection::chained_path_from( endpoints.erase(endpoints.begin() + endpoint_index/2); start_near = retval.back().last_point(); } + return retval; } -#if SLIC3R_CPPVER > 11 +#if SLIC3R_CPPVER >= 11 Polylines PolylineCollection::chained_path(Polylines &&src, bool no_reverse) { - return (src.empty() || src.front().empty()) ? + return (src.empty() || src.front().points.empty()) ? Polylines() : - chained_path_from(std::move(src), src.front().first_point(), no_reverse); + _chained_path_from(src, src.front().first_point(), no_reverse, true); } -Polylines PolylineCollection::chained_path_from(Polylines src, Point start_near, bool no_reverse) + +Polylines PolylineCollection::chained_path_from(Polylines &&src, Point start_near, bool no_reverse) { - return chained_path_from(std::move(src), start_near, no_reverse); + return _chained_path_from(src, start_near, no_reverse, true); } -Polylines PolylineCollection::chained_path(Polylines src, bool no_reverse) -{ - return (src.empty() || src.front().empty()) ? - Polylines() : - chained_path_from(std::move(src), src.front().first_point(), no_reverse); -} -#else +#endif + Polylines PolylineCollection::chained_path(const Polylines &src, bool no_reverse) { return (src.empty() || src.front().points.empty()) ? Polylines() : - chained_path_from(src, src.front().first_point(), no_reverse); -} + _chained_path_from(src, src.front().first_point(), no_reverse +#if SLIC3R_CPPVER >= 11 + , false #endif + ); +} + +Polylines PolylineCollection::chained_path_from(const Polylines &src, Point start_near, bool no_reverse) +{ + return _chained_path_from(src, start_near, no_reverse +#if SLIC3R_CPPVER >= 11 + , false +#endif + ); +} Point PolylineCollection::leftmost_point(const Polylines &polylines) { diff --git a/xs/src/libslic3r/PolylineCollection.hpp b/xs/src/libslic3r/PolylineCollection.hpp index 492f0318f..80d609410 100644 --- a/xs/src/libslic3r/PolylineCollection.hpp +++ b/xs/src/libslic3r/PolylineCollection.hpp @@ -8,26 +8,32 @@ namespace Slic3r { class PolylineCollection { + static Polylines _chained_path_from( + const Polylines &src, + Point start_near, + bool no_reverse +#if SLIC3R_CPPVER >= 11 + , bool move_from_src +#endif + ); + public: Polylines polylines; void chained_path(PolylineCollection* retval, bool no_reverse = false) const { retval->polylines = chained_path(this->polylines, no_reverse); } - void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const + void chained_path_from(Point start_near, PolylineCollection* retval, bool no_reverse = false) const { retval->polylines = chained_path_from(this->polylines, start_near, no_reverse); } Point leftmost_point() const { return leftmost_point(polylines); } void append(const Polylines &polylines); static Point leftmost_point(const Polylines &polylines); -#if SLIC3R_CPPVER > 11 +#if SLIC3R_CPPVER >= 11 static Polylines chained_path(Polylines &&src, bool no_reverse = false); static Polylines chained_path_from(Polylines &&src, Point start_near, bool no_reverse = false); - static Polylines chained_path(Polylines src, bool no_reverse = false); - static Polylines chained_path_from(Polylines src, Point start_near, bool no_reverse = false); -#else - static Polylines chained_path(const Polylines &src, bool no_reverse = false); - static Polylines chained_path_from(const Polylines &src, Point start_near, bool no_reverse = false); #endif + static Polylines chained_path(const Polylines &src, bool no_reverse = false); + static Polylines chained_path_from(const Polylines &src, Point start_near, bool no_reverse = false); }; } diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 59dc5bfb1..fca964a74 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -368,6 +368,11 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("alignedrectilinear"); def->enum_values.push_back("grid"); def->enum_values.push_back("line"); + def->enum_values.push_back("rectilinear2"); + def->enum_values.push_back("grid2"); + def->enum_values.push_back("triangles"); + def->enum_values.push_back("stars"); + def->enum_values.push_back("cubic"); def->enum_values.push_back("concentric"); def->enum_values.push_back("honeycomb"); def->enum_values.push_back("3dhoneycomb"); @@ -375,9 +380,14 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("archimedeanchords"); def->enum_values.push_back("octagramspiral"); def->enum_labels.push_back("Rectilinear"); - def->enum_labels.push_back("AlignedRectilinear"); + def->enum_labels.push_back("Aligned Rectilinear"); def->enum_labels.push_back("Grid"); def->enum_labels.push_back("Line"); + def->enum_labels.push_back("Rectilinear 2"); + def->enum_labels.push_back("Grid 2"); + def->enum_labels.push_back("Triangles"); + def->enum_labels.push_back("Stars"); + def->enum_labels.push_back("Cubic"); def->enum_labels.push_back("Concentric"); def->enum_labels.push_back("Honeycomb"); def->enum_labels.push_back("3D Honeycomb"); diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 8d86bddfe..953487acd 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -30,7 +30,9 @@ enum GCodeFlavor { }; enum InfillPattern { - ipRectilinear, ipAlignedRectilinear, ipGrid, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, + ipRectilinear, ipGrid, ipLine, ipAlignedRectilinear, + ipRectilinear2, ipGrid2, ipTriangles, ipStars, ipCubic, + ipConcentric, ipHoneycomb, ip3DHoneycomb, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, }; @@ -62,6 +64,11 @@ template<> inline t_config_enum_values ConfigOptionEnum::get_enum keys_map["alignedrectilinear"] = ipAlignedRectilinear; keys_map["grid"] = ipGrid; keys_map["line"] = ipLine; + keys_map["rectilinear2"] = ipRectilinear2; + keys_map["grid2"] = ipGrid2; + keys_map["triangles"] = ipTriangles; + keys_map["stars"] = ipStars; + keys_map["cubic"] = ipCubic; keys_map["concentric"] = ipConcentric; keys_map["honeycomb"] = ipHoneycomb; keys_map["3dhoneycomb"] = ip3DHoneycomb; diff --git a/xs/src/libslic3r/Surface.cpp b/xs/src/libslic3r/Surface.cpp index ba32ecd10..4d2234e4d 100644 --- a/xs/src/libslic3r/Surface.cpp +++ b/xs/src/libslic3r/Surface.cpp @@ -54,15 +54,4 @@ Surface::is_bridge() const || this->surface_type == stInternalBridge; } -Polygons -to_polygons(const Surfaces &surfaces) -{ - Slic3r::Polygons pp; - for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) { - Slic3r::Polygons ppp = *s; - pp.insert(pp.end(), ppp.begin(), ppp.end()); - } - return pp; -} - } diff --git a/xs/src/libslic3r/Surface.hpp b/xs/src/libslic3r/Surface.hpp index d445db208..895d7e904 100644 --- a/xs/src/libslic3r/Surface.hpp +++ b/xs/src/libslic3r/Surface.hpp @@ -33,8 +33,34 @@ class Surface typedef std::vector Surfaces; typedef std::vector SurfacesPtr; +typedef std::vector SurfacesConstPtr; -Polygons to_polygons(const Surfaces &surfaces); +inline Polygons +to_polygons(const Surfaces &surfaces) +{ + Slic3r::Polygons pp; + for (Surfaces::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) + append_to(pp, (Polygons)*s); + return pp; +} + +inline Polygons +to_polygons(const SurfacesPtr &surfaces) +{ + Slic3r::Polygons pp; + for (SurfacesPtr::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) + append_to(pp, (Polygons)**s); + return pp; +} + +inline Polygons +to_polygons(const SurfacesConstPtr &surfaces) +{ + Slic3r::Polygons pp; + for (SurfacesConstPtr::const_iterator s = surfaces.begin(); s != surfaces.end(); ++s) + append_to(pp, (Polygons)**s); + return pp; +} } diff --git a/xs/src/libslic3r/SurfaceCollection.cpp b/xs/src/libslic3r/SurfaceCollection.cpp index 21e6eb0cc..fb92796ca 100644 --- a/xs/src/libslic3r/SurfaceCollection.cpp +++ b/xs/src/libslic3r/SurfaceCollection.cpp @@ -41,13 +41,13 @@ SurfaceCollection::simplify(double tolerance) /* group surfaces by common properties */ void -SurfaceCollection::group(std::vector *retval) +SurfaceCollection::group(std::vector *retval) const { - for (Surfaces::iterator it = this->surfaces.begin(); it != this->surfaces.end(); ++it) { + for (Surfaces::const_iterator it = this->surfaces.begin(); it != this->surfaces.end(); ++it) { // find a group with the same properties - SurfacesPtr* group = NULL; - for (std::vector::iterator git = retval->begin(); git != retval->end(); ++git) { - Surface* gkey = git->front(); + SurfacesConstPtr* group = NULL; + for (std::vector::iterator git = retval->begin(); git != retval->end(); ++git) { + const Surface* gkey = git->front(); if ( gkey->surface_type == it->surface_type && gkey->thickness == it->thickness && gkey->thickness_layers == it->thickness_layers @@ -104,17 +104,48 @@ void SurfaceCollection::filter_by_type(SurfaceType type, Polygons* polygons) { for (Surfaces::iterator surface = this->surfaces.begin(); surface != this->surfaces.end(); ++surface) { - if (surface->surface_type == type) { - Polygons pp = surface->expolygon; - polygons->insert(polygons->end(), pp.begin(), pp.end()); - } + if (surface->surface_type == type) + append_to(*polygons, (Polygons)surface->expolygon); } } void SurfaceCollection::append(const SurfaceCollection &coll) { - this->surfaces.insert(this->surfaces.end(), coll.surfaces.begin(), coll.surfaces.end()); + this->append(coll.surfaces); +} + +void +SurfaceCollection::append(const Surfaces &surfaces) +{ + append_to(this->surfaces, surfaces); +} + +void +SurfaceCollection::append(const ExPolygons &src, const Surface &templ) +{ + this->surfaces.reserve(this->surfaces.size() + src.size()); + for (ExPolygons::const_iterator it = src.begin(); it != src.end(); ++ it) { + this->surfaces.push_back(templ); + this->surfaces.back().expolygon = *it; + } +} + +void +SurfaceCollection::append(const ExPolygons &src, SurfaceType surfaceType) +{ + this->surfaces.reserve(this->surfaces.size() + src.size()); + for (ExPolygons::const_iterator it = src.begin(); it != src.end(); ++ it) + this->surfaces.push_back(Surface(surfaceType, *it)); +} + +size_t +SurfaceCollection::polygons_count() const +{ + size_t count = 0; + for (Surfaces::const_iterator it = this->surfaces.begin(); it != this->surfaces.end(); ++ it) + count += 1 + it->expolygon.holes.size(); + return count; } } diff --git a/xs/src/libslic3r/SurfaceCollection.hpp b/xs/src/libslic3r/SurfaceCollection.hpp index a4a3a7e5d..42d26b892 100644 --- a/xs/src/libslic3r/SurfaceCollection.hpp +++ b/xs/src/libslic3r/SurfaceCollection.hpp @@ -18,12 +18,16 @@ class SurfaceCollection operator Polygons() const; operator ExPolygons() const; void simplify(double tolerance); - void group(std::vector *retval); + void group(std::vector *retval) const; template bool any_internal_contains(const T &item) const; template bool any_bottom_contains(const T &item) const; SurfacesPtr filter_by_type(SurfaceType type); void filter_by_type(SurfaceType type, Polygons* polygons); void append(const SurfaceCollection &coll); + void append(const Surfaces &surfaces); + void append(const ExPolygons &src, const Surface &templ); + void append(const ExPolygons &src, SurfaceType surfaceType); + size_t polygons_count() const; }; } diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index 680716bb8..d35018c12 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -5,6 +5,7 @@ #include #include #include +#include #define SLIC3R_VERSION "1.3.0-dev" @@ -37,6 +38,12 @@ namespace Slic3r { enum Axis { X=0, Y, Z }; +template +inline void append_to(std::vector &dst, const std::vector &src) +{ + dst.insert(dst.end(), src.begin(), src.end()); +} + } using namespace Slic3r; @@ -53,10 +60,13 @@ void confess_at(const char *file, int line, const char *func, const char *pat, . // For example, could optimized functions with move semantics be used? #if __cplusplus==201402L #define SLIC3R_CPPVER 14 + #define STDMOVE(WHAT) std::move(WHAT) #elif __cplusplus==201103L #define SLIC3R_CPPVER 11 + #define STDMOVE(WHAT) std::move(WHAT) #else #define SLIC3R_CPPVER 0 + #define STDMOVE(WHAT) (WHAT) #endif #endif diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index 6852cb8dd..ad87768b5 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -11,6 +11,7 @@ REGISTER_CLASS(ExtrusionLoop, "ExtrusionLoop"); // there is no ExtrusionLoop::Collection or ExtrusionEntity::Collection REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection"); REGISTER_CLASS(Flow, "Flow"); +REGISTER_CLASS(Filler, "Filler"); REGISTER_CLASS(AvoidCrossingPerimeters, "GCode::AvoidCrossingPerimeters"); REGISTER_CLASS(OozePrevention, "GCode::OozePrevention"); REGISTER_CLASS(Wipe, "GCode::Wipe"); diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index 99b0e8f6a..700319fb6 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -53,6 +53,7 @@ extern "C" { #include #include #include +#include #include #include #include @@ -151,6 +152,20 @@ SV* to_SV(TriangleMesh* THIS); SV* polynode_children_2_perl(const ClipperLib::PolyNode& node); SV* polynode2perl(const ClipperLib::PolyNode& node); +class Filler +{ + public: + Filler() : fill(NULL) {}; + ~Filler() { + if (fill != NULL) { + delete fill; + fill = NULL; + } + }; + Fill *fill; + FillParams params; +}; + } #endif diff --git a/xs/xsp/Filler.xsp b/xs/xsp/Filler.xsp new file mode 100644 index 000000000..85d83d549 --- /dev/null +++ b/xs/xsp/Filler.xsp @@ -0,0 +1,78 @@ +%module{Slic3r::XS}; + +%{ +#include +#include "libslic3r/Fill/Fill.hpp" +#include "libslic3r/PolylineCollection.hpp" +#include "libslic3r/ExtrusionEntity.hpp" +#include "libslic3r/ExtrusionEntityCollection.hpp" +%} + +%name{Slic3r::Filler} class Filler { + ~Filler(); + + void set_bounding_box(BoundingBox *bbox) + %code{% THIS->fill->set_bounding_box(*bbox); %}; + void set_spacing(coordf_t spacing) + %code{% THIS->fill->spacing = spacing; %}; + coordf_t spacing() + %code{% RETVAL = THIS->fill->spacing; %}; + void set_layer_id(size_t layer_id) + %code{% THIS->fill->layer_id = layer_id; %}; + void set_z(coordf_t z) + %code{% THIS->fill->z = z; %}; + void set_angle(float angle) + %code{% THIS->fill->angle = angle; %}; + void set_link_max_length(coordf_t len) + %code{% THIS->fill->link_max_length = len; %}; + void set_loop_clipping(coordf_t clipping) + %code{% THIS->fill->loop_clipping = clipping; %}; + + bool use_bridge_flow() + %code{% RETVAL = THIS->fill->use_bridge_flow(); %}; + bool no_sort() + %code{% RETVAL = THIS->fill->no_sort(); %}; + + void set_density(float density) + %code{% THIS->params.density = density; %}; + void set_dont_connect(bool dont_connect) + %code{% THIS->params.dont_connect = dont_connect; %}; + void set_dont_adjust(bool dont_adjust) + %code{% THIS->params.dont_adjust = dont_adjust; %}; + void set_complete(bool complete) + %code{% THIS->params.complete = complete; %}; + + PolylineCollection* _fill_surface(Surface *surface) + %code{% + PolylineCollection *pc = NULL; + if (THIS->fill != NULL) { + pc = new PolylineCollection(); + pc->polylines = THIS->fill->fill_surface(*surface, THIS->params); + } + RETVAL = pc; + %}; + +%{ + +Filler* +new_from_type(CLASS, type) + char* CLASS; + std::string type; + CODE: + Filler *filler = new Filler(); + filler->fill = Fill::new_from_type(type); + RETVAL = filler; + OUTPUT: + RETVAL + +%} + +}; + +%package{Slic3r::Filler}; + +void make_fill(LayerRegion* layerm, ExtrusionEntityCollection* out) + %code{% make_fill(*layerm, out); %}; + +coord_t adjust_solid_spacing(coord_t width, coord_t distance) + %code{% RETVAL = Fill::adjust_solid_spacing(width, distance); %}; diff --git a/xs/xsp/Layer.xsp b/xs/xsp/Layer.xsp index e5697ce89..4be9feccf 100644 --- a/xs/xsp/Layer.xsp +++ b/xs/xsp/Layer.xsp @@ -86,6 +86,7 @@ bool any_bottom_region_slice_contains_polyline(Polyline* polyline) %code%{ RETVAL = THIS->any_bottom_region_slice_contains(*polyline); %}; void make_perimeters(); + void make_fills(); }; %name{Slic3r::Layer::Support} class SupportLayer { diff --git a/xs/xsp/SurfaceCollection.xsp b/xs/xsp/SurfaceCollection.xsp index 19cf3f828..a4fade4e5 100644 --- a/xs/xsp/SurfaceCollection.xsp +++ b/xs/xsp/SurfaceCollection.xsp @@ -60,18 +60,18 @@ SV* SurfaceCollection::group() CODE: // perform grouping - std::vector groups; + std::vector groups; THIS->group(&groups); // build return arrayref AV* av = newAV(); av_fill(av, groups.size()-1); size_t i = 0; - for (std::vector::iterator it = groups.begin(); it != groups.end(); ++it) { + for (std::vector::const_iterator it = groups.begin(); it != groups.end(); ++it) { AV* innerav = newAV(); av_fill(innerav, it->size()-1); size_t j = 0; - for (SurfacesPtr::iterator it_s = it->begin(); it_s != it->end(); ++it_s) { + for (SurfacesConstPtr::const_iterator it_s = it->begin(); it_s != it->end(); ++it_s) { av_store(innerav, j++, perl_to_SV_clone_ref(**it_s)); } av_store(av, i++, newRV_noinc((SV*)innerav)); diff --git a/xs/xsp/my.map b/xs/xsp/my.map index b91ba4794..18110d5dd 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -1,3 +1,4 @@ +coord_t T_IV coordf_t T_NV std::string T_STD_STRING @@ -116,6 +117,10 @@ ExtrusionLoop* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T +Filler* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +Clone O_OBJECT_SLIC3R_T + Flow* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 87039c564..94740e0e7 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -1,5 +1,6 @@ %typemap{bool}{simple}; %typemap{size_t}{simple}; +%typemap{coord_t}{simple}; %typemap{coordf_t}{simple}; %typemap{std::string}; %typemap{t_config_option_key}; @@ -60,6 +61,9 @@ %typemap{Flow*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; +%typemap{Filler*}; +%typemap{Ref}{simple}; +%typemap{Clone}{simple}; %typemap{Line*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; From 335638a6146c93b0e238490f4e0478a4c8d7795a Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 13 Sep 2016 11:24:55 +0200 Subject: [PATCH 061/225] Documented perl modules. Conflicts: lib/Slic3r.pm lib/Slic3r/Config.pm --- lib/Slic3r.pm | 27 +++++++++++++++++++++------ lib/Slic3r/Config.pm | 24 +++++++++++++++++++++++- lib/Slic3r/GCode/CoolingBuffer.pm | 17 +++++++++++++++-- lib/Slic3r/GCode/PressureRegulator.pm | 2 ++ lib/Slic3r/GUI.pm | 3 +++ lib/Slic3r/GUI/Plater.pm | 4 ++++ lib/Slic3r/Layer.pm | 2 ++ lib/Slic3r/Model.pm | 3 +++ lib/Slic3r/Print.pm | 3 +++ lib/Slic3r/Print/Object.pm | 13 +++++++++++++ lib/Slic3r/Print/Simple.pm | 8 ++++++++ lib/Slic3r/Print/State.pm | 1 + lib/Slic3r/Test/SectionCut.pm | 1 + 13 files changed, 99 insertions(+), 9 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 4a1bd6026..c45f3e065 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -1,3 +1,6 @@ +# This package loads all the non-GUI Slic3r perl packages. +# In addition, it implements utility functions for file handling and threading. + package Slic3r; # Copyright holder: Alessandro Ranellucci @@ -26,10 +29,11 @@ BEGIN { $have_threads = 0 if $Moo::VERSION == 1.003000; } -warn "Running Slic3r under Perl 5.16 is not supported nor recommended\n" +warn "Running Slic3r under Perl 5.16 is neither supported nor recommended\n" if $^V == v5.16; use FindBin; +# Path to the images. our $var = sub { decode_path($FindBin::Bin) . "/var/" . $_[0] }; use Moo 1.003001; @@ -70,13 +74,16 @@ use Encode::Locale 1.05; use Encode; use Unicode::Normalize; +# Scaling between the float and integer coordinates. +# Floats are in mm. use constant SCALING_FACTOR => 0.000001; -use constant RESOLUTION => 0.0125; -use constant SCALED_RESOLUTION => RESOLUTION / SCALING_FACTOR; +# Resolution to simplify perimeters to. These constants are now used in C++ code only. Better to publish them to Perl from the C++ code. +# use constant RESOLUTION => 0.0125; +# use constant SCALED_RESOLUTION => RESOLUTION / SCALING_FACTOR; use constant LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER => 0.15; -use constant INFILL_OVERLAP_OVER_SPACING => 0.3; +# use constant INFILL_OVERLAP_OVER_SPACING => 0.3; -# keep track of threads we created +# Keep track of threads we created. Each thread keeps its own list of threads it spwaned. my @my_threads = (); my @threads : shared = (); my $pause_sema = Thread::Semaphore->new; @@ -181,7 +188,7 @@ sub thread_cleanup { warn "Calling thread_cleanup() from main thread\n"; return; } - + # prevent destruction of shared objects no warnings 'redefine'; *Slic3r::BridgeDetector::DESTROY = sub {}; @@ -272,6 +279,12 @@ sub resume_all_threads { $pause_sema->up; } +# Convert a Unicode path to a file system locale. +# The encoding is (from Encode::Locale POD): +# Alias | Windows | Mac OS X | POSIX +# locale_fs | ANSI | UTF-8 | nl_langinfo +# where nl_langinfo is en-US.UTF-8 on a modern Linux as well. +# So this conversion seems to make the most sense on Windows. sub encode_path { my ($path) = @_; @@ -281,6 +294,7 @@ sub encode_path { return $path; } +# Convert a path coded by a file system locale to Unicode. sub decode_path { my ($path) = @_; @@ -296,6 +310,7 @@ sub decode_path { return $path; } +# Open a file by converting $filename to local file system locales. sub open { my ($fh, $mode, $filename) = @_; return CORE::open $$fh, $mode, encode_path($filename); diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 366c96f2c..5cd94cf32 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -1,3 +1,6 @@ +# Extends C++ class Slic3r::DynamicPrintConfig +# This perl class does not keep any perl class variables, +# all the storage is handled by the underlying C++ code. package Slic3r::Config; use strict; use warnings; @@ -11,6 +14,8 @@ our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_ rotate scale duplicate_grid start_perimeters_at_concave_points start_perimeters_at_non_overhang randomize_start seal_position bed_size print_center g0); +# C++ Slic3r::PrintConfigDef exported as a Perl hash of hashes. +# The C++ counterpart is a constant singleton. our $Options = print_config_def(); # overwrite the hard-coded readonly value (this information is not available in XS) @@ -24,6 +29,8 @@ $Options->{threads}{readonly} = !$Slic3r::have_threads; } } +# Fill in the underlying C++ Slic3r::DynamicPrintConfig with the content of the defaults +# provided by the C++ class Slic3r::FullPrintConfig. sub new_from_defaults { my $class = shift; my (@opt_keys) = @_; @@ -39,12 +46,16 @@ sub new_from_defaults { return $self; } +# From command line parameters sub new_from_cli { my $class = shift; my %args = @_; + # Delete hash keys with undefined value. delete $args{$_} for grep !defined $args{$_}, keys %args; + # Replace the start_gcode, end_gcode ... hash values + # with the content of the files they reference. for (qw(start end layer toolchange)) { my $opt_key = "${_}_gcode"; if ($args{$opt_key}) { @@ -57,7 +68,7 @@ sub new_from_cli { } } } - + my $self = $class->new; foreach my $opt_key (keys %args) { my $opt_def = $Options->{$opt_key}; @@ -83,6 +94,8 @@ sub merge { return $config; } +# Load a flat ini file without a category into the underlying C++ Slic3r::DynamicConfig class, +# convert legacy configuration names. sub load { my $class = shift; my ($file) = @_; @@ -100,6 +113,8 @@ sub save { return $self->_save(Slic3r::encode_path($file)); } +# Deserialize a perl hash into the underlying C++ Slic3r::DynamicConfig class, +# convert legacy configuration names. sub load_ini_hash { my $class = shift; my ($ini_hash) = @_; @@ -184,6 +199,8 @@ sub _handle_legacy { return ($opt_key, $value); } +# Create a hash of hashes from the underlying C++ Slic3r::DynamicPrintConfig. +# The first hash key is '_' meaning no category. sub as_ini { my ($self) = @_; @@ -345,6 +362,7 @@ sub validate { # CLASS METHODS: +# Write a "Windows" style ini file with categories enclosed in squre brackets. sub write_ini { my $class = shift; my ($file, $ini) = @_; @@ -363,6 +381,10 @@ sub write_ini { close $fh; } +# Parse a "Windows" style ini file with categories enclosed in squre brackets. +# Returns a hash of hashes over strings. +# {category}{name}=value +# Non-categorized entries are stored under a category '_'. sub read_ini { my $class = shift; my ($file) = @_; diff --git a/lib/Slic3r/GCode/CoolingBuffer.pm b/lib/Slic3r/GCode/CoolingBuffer.pm index bb1292b39..bbb9af472 100644 --- a/lib/Slic3r/GCode/CoolingBuffer.pm +++ b/lib/Slic3r/GCode/CoolingBuffer.pm @@ -1,9 +1,13 @@ +# A standalone (no C++ implementation) G-code filter, to control cooling of the print. +# The G-code is processed per layer. Once a layer is collected, fan start / stop commands are edited +# and the print is modified to stretch over a minimum layer time. + package Slic3r::GCode::CoolingBuffer; use Moo; has 'config' => (is => 'ro', required => 1); # Slic3r::Config::Print -has 'gcodegen' => (is => 'ro', required => 1); -has 'gcode' => (is => 'rw', default => sub {""}); +has 'gcodegen' => (is => 'ro', required => 1); # Slic3r::GCode C++ instance +has 'gcode' => (is => 'rw', default => sub {""}); # Cache of a G-code snippet emitted for a single layer. has 'elapsed_time' => (is => 'rw', default => sub {0}); has 'layer_id' => (is => 'rw'); has 'last_z' => (is => 'rw', default => sub { {} }); # obj_id => z (basically a 'last seen' table) @@ -20,12 +24,15 @@ sub append { my $return = ""; if (exists $self->last_z->{$obj_id} && $self->last_z->{$obj_id} != $print_z) { + # A layer was finished, Z of the object's layer changed. Process the layer. $return = $self->flush; } $self->layer_id($layer_id); $self->last_z->{$obj_id} = $print_z; $self->gcode($self->gcode . $gcode); + #FIXME Vojtech: This is a very rough estimate of the print time, + # not taking into account the acceleration profiles generated by the printer firmware. $self->elapsed_time($self->elapsed_time + $self->gcodegen->elapsed_time); $self->gcodegen->set_elapsed_time(0); @@ -46,9 +53,12 @@ sub flush { if ($self->config->cooling) { Slic3r::debugf "Layer %d estimated printing time: %d seconds\n", $self->layer_id, $elapsed; if ($elapsed < $self->config->slowdown_below_layer_time) { + # Layer time very short. Enable the fan to a full throttle and slow down the print + # (stretch the layer print time to slowdown_below_layer_time). $fan_speed = $self->config->max_fan_speed; $speed_factor = $elapsed / $self->config->slowdown_below_layer_time; } elsif ($elapsed < $self->config->fan_below_layer_time) { + # Layer time quite short. Enable the fan proportionally according to the current layer time. $fan_speed = $self->config->max_fan_speed - ($self->config->max_fan_speed - $self->config->min_fan_speed) * ($elapsed - $self->config->slowdown_below_layer_time) / ($self->config->fan_below_layer_time - $self->config->slowdown_below_layer_time); #/ @@ -56,6 +66,9 @@ sub flush { Slic3r::debugf " fan = %d%%, speed = %f%%\n", $fan_speed, $speed_factor * 100; if ($speed_factor < 1) { + # Adjust feed rate of G1 commands marked with an _EXTRUDE_SET_SPEED + # as long as they are not _WIPE moves (they cannot if they are _EXTRUDE_SET_SPEED) + # and they are not preceded directly by _BRIDGE_FAN_START (do not adjust bridging speed). $gcode =~ s/^(?=.*?;_EXTRUDE_SET_SPEED)(?!.*?;_WIPE)(?min_print_speed ? $self->min_print_speed : $new_speed) diff --git a/lib/Slic3r/GCode/PressureRegulator.pm b/lib/Slic3r/GCode/PressureRegulator.pm index 6074b9486..d03094ab2 100644 --- a/lib/Slic3r/GCode/PressureRegulator.pm +++ b/lib/Slic3r/GCode/PressureRegulator.pm @@ -1,3 +1,5 @@ +# A pure perl (no C++ implementation) G-code filter, to control the pressure inside the nozzle. + package Slic3r::GCode::PressureRegulator; use Moo; diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 7f4a028a5..c95e9afbb 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -95,6 +95,9 @@ sub OnInit { $self->{notifier} = Slic3r::GUI::Notifier->new; # locate or create data directory + # Unix: ~/.Slic3r + # Windows: "C:\Users\username\AppData\Roaming\Slic3r" or "C:\Documents and Settings\username\Application Data\Slic3r" + # Mac: "~/Library/Application Support/Slic3r" $datadir ||= Slic3r::decode_path(Wx::StandardPaths::Get->GetUserDataDir); my $enc_datadir = Slic3r::encode_path($datadir); Slic3r::debugf "Data directory: %s\n", $datadir; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index fb1a36004..daa4d6c95 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -51,6 +51,7 @@ sub new { )); $self->{model} = Slic3r::Model->new; $self->{print} = Slic3r::Print->new; + # List of Perl objects Slic3r::GUI::Plater::Object, representing a 2D preview of the platter. $self->{objects} = []; $self->{print}->set_status_cb(sub { @@ -976,6 +977,8 @@ sub schedule_background_process { } } +# Executed asynchronously by a timer every PROCESS_DELAY (0.5 second). +# The timer is started by schedule_background_process(), sub async_apply_config { my ($self) = @_; @@ -1850,6 +1853,7 @@ sub OnDropFiles { $self->{window}->load_file($_) for @$filenames; } +# 2D preview of an object. Each object is previewed by its convex hull. package Slic3r::GUI::Plater::Object; use Moo; diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index b5e8e42f7..4bff9b61f 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -1,3 +1,5 @@ +# Extends the C++ class Slic3r::Layer. + package Slic3r::Layer; use strict; use warnings; diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index 01797316b..e852a4ab3 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -1,3 +1,4 @@ +# extends C++ class Slic3r::Model package Slic3r::Model; use List::Util qw(first max any); @@ -94,6 +95,7 @@ sub convert_multipart_object { $self->delete_object($_) for reverse 0..($self->objects_count-2); } +# Extends C++ class Slic3r::ModelMaterial package Slic3r::Model::Material; sub apply { @@ -101,6 +103,7 @@ sub apply { $self->set_attribute($_, $attributes{$_}) for keys %$attributes; } +# Extends C++ class Slic3r::ModelObject package Slic3r::Model::Object; use File::Basename qw(basename); diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 053fcdef9..d349eb1a0 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -1,3 +1,5 @@ +# The slicing work horse. +# Extends C++ class Slic3r::Print package Slic3r::Print; use strict; use warnings; @@ -89,6 +91,7 @@ sub export_gcode { } } +# Export SVG slices for the offline SLA printing. sub export_svg { my $self = shift; my %params = @_; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index da8487d9d..662067fb0 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -1,4 +1,5 @@ package Slic3r::Print::Object; +# extends c++ class Slic3r::PrintObject (Print.xsp) use strict; use warnings; @@ -32,6 +33,14 @@ sub support_layers { return [ map $self->get_support_layer($_), 0..($self->support_layer_count - 1) ]; } +# 1) Decides Z positions of the layers, +# 2) Initializes layers and their regions +# 3) Slices the object meshes +# 4) Slices the modifier meshes and reclassifies the slices of the object meshes by the slices of the modifier meshes +# 5) Applies size compensation (offsets the slices in XY plane) +# 6) Replaces bad slices by the slices reconstructed from the upper/lower layer +# Resulting expolygons of layer regions are marked as Internal. +# # this should be idempotent sub slice { my $self = shift; @@ -326,6 +335,7 @@ sub slice { $self->set_step_done(STEP_SLICE); } +# called from slice() sub _slice_region { my ($self, $region_id, $z, $modifier) = @_; @@ -1112,6 +1122,9 @@ sub combine_infill { } } +# Simplify the sliced model, if "resolution" configuration parameter > 0. +# The simplification is problematic, because it simplifies the slices independent from each other, +# which makes the simplified discretization visible on the object surface. sub _simplify_slices { my ($self, $distance) = @_; diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm index d84d1cb53..3771bfe4a 100644 --- a/lib/Slic3r/Print/Simple.pm +++ b/lib/Slic3r/Print/Simple.pm @@ -1,3 +1,10 @@ +# A simple wrapper to quickly print a single model without a GUI. +# Used by the command line slic3r.pl, by command line utilities pdf-slic3s.pl and view-toolpaths.pl, +# and by the quick slice menu of the Slic3r GUI. +# +# It creates and owns an instance of Slic3r::Print to perform the slicing +# and it accepts an instance of Slic3r::Model from the outside. + package Slic3r::Print::Simple; use Moo; @@ -51,6 +58,7 @@ has 'output_file' => ( ); sub set_model { + # $model is of type Slic3r::Model my ($self, $model) = @_; # make method idempotent so that the object is reusable diff --git a/lib/Slic3r/Print/State.pm b/lib/Slic3r/Print/State.pm index 7220aa818..d242e3760 100644 --- a/lib/Slic3r/Print/State.pm +++ b/lib/Slic3r/Print/State.pm @@ -1,3 +1,4 @@ +# Wraps C++ enums Slic3r::PrintStep and Slic3r::PrintObjectStep package Slic3r::Print::State; use strict; use warnings; diff --git a/lib/Slic3r/Test/SectionCut.pm b/lib/Slic3r/Test/SectionCut.pm index 467f4e9d4..04106d46f 100644 --- a/lib/Slic3r/Test/SectionCut.pm +++ b/lib/Slic3r/Test/SectionCut.pm @@ -1,3 +1,4 @@ +# Slices at the XZ plane, for debugging purposes. package Slic3r::Test::SectionCut; use Moo; From 9f0523cb5327e3ba5f333a95d1a60e14a706d4b4 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 14 Sep 2016 11:22:41 +0200 Subject: [PATCH 062/225] Documented the purpose of various perl modules. Conflicts: lib/Slic3r/GUI/3DScene.pm lib/Slic3r/GUI/Plater/2DToolpaths.pm --- lib/Slic3r.pm | 8 ++++++++ lib/Slic3r/GUI/2DBed.pm | 2 ++ lib/Slic3r/GUI/3DScene.pm | 14 +++++++++++++- lib/Slic3r/GUI/BedShapeDialog.pm | 3 +++ lib/Slic3r/GUI/BonjourBrowser.pm | 2 ++ lib/Slic3r/GUI/ConfigWizard.pm | 3 +++ lib/Slic3r/GUI/Controller.pm | 4 ++++ lib/Slic3r/GUI/Controller/ManualControlDialog.pm | 2 ++ lib/Slic3r/GUI/MainFrame.pm | 2 ++ lib/Slic3r/GUI/Notifier.pm | 3 +++ lib/Slic3r/GUI/OptionsGroup.pm | 2 ++ lib/Slic3r/GUI/Plater.pm | 2 ++ lib/Slic3r/GUI/Plater/2D.pm | 3 +++ lib/Slic3r/GUI/Plater/2DToolpaths.pm | 4 ++++ lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 3 +++ lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 3 +++ lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm | 4 ++++ lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm | 3 +++ lib/Slic3r/GUI/Preferences.pm | 2 ++ lib/Slic3r/GUI/ProgressStatusBar.pm | 2 ++ lib/Slic3r/GUI/Projector.pm | 2 ++ lib/Slic3r/GUI/SimpleTab.pm | 3 +++ lib/Slic3r/GUI/Tab.pm | 3 +++ lib/Slic3r/Test/SectionCut.pm | 4 +++- xs/src/libslic3r/PerimeterGenerator.cpp | 2 ++ 25 files changed, 83 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index c45f3e065..5161a86b2 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -20,6 +20,7 @@ sub debugf { # load threads before Moo as required by it our $have_threads; BEGIN { + # Test, whether the perl was compiled with ithreads support and ithreads actually work. use Config; $have_threads = $Config{useithreads} && eval "use threads; use threads::shared; use Thread::Queue; 1"; warn "threads.pm >= 1.96 is required, please update\n" if $have_threads && $threads::VERSION < 1.96; @@ -27,6 +28,13 @@ BEGIN { ### temporarily disable threads if using the broken Moo version use Moo; $have_threads = 0 if $Moo::VERSION == 1.003000; + + # Disable multi threading completely by an environment value. + # This is useful for debugging as the Perl debugger does not work + # in multi-threaded context at all. + # A good interactive perl debugger is the ActiveState Komodo IDE + # or the EPIC http://www.epic-ide.org/ + $have_threads = 0 if (defined($ENV{'SLIC3R_SINGLETHREADED'}) && $ENV{'SLIC3R_SINGLETHREADED'} == 1) } warn "Running Slic3r under Perl 5.16 is neither supported nor recommended\n" diff --git a/lib/Slic3r/GUI/2DBed.pm b/lib/Slic3r/GUI/2DBed.pm index c8896a795..afa31f951 100644 --- a/lib/Slic3r/GUI/2DBed.pm +++ b/lib/Slic3r/GUI/2DBed.pm @@ -1,3 +1,5 @@ +# Bed shape dialog + package Slic3r::GUI::2DBed; use strict; use warnings; diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index abfe41cf0..6595852af 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1376,6 +1376,8 @@ sub _expolygons_to_verts { gluDeleteTess($tess); } +# Fill in the $qverts and $tverts with quads and triangles +# for the extrusion $entity. sub _extrusionentity_to_verts { my ($self, $entity, $top_z, $copy, $qverts, $tverts) = @_; @@ -1407,8 +1409,18 @@ sub _extrusionentity_to_verts { push @$heights, map $path->height, 0..$#$path_lines; } } + + # Calling the C++ implementation Slic3r::_3DScene::_extrusionentity_to_verts_do() + # This adds new vertices to the $qverts and $tverts. Slic3r::GUI::_3DScene::_extrusionentity_to_verts_do($lines, $widths, $heights, - $closed, $top_z, $copy, $qverts, $tverts); + $closed, + # Top height of the extrusion. + $top_z, + # $copy is not used here. + $copy, + # GLVertexArray object: C++ class maintaining an std::vector for coords and normals. + $qverts, + $tverts); } sub object_idx { diff --git a/lib/Slic3r/GUI/BedShapeDialog.pm b/lib/Slic3r/GUI/BedShapeDialog.pm index 49c8b9e07..ed6fb5cde 100644 --- a/lib/Slic3r/GUI/BedShapeDialog.pm +++ b/lib/Slic3r/GUI/BedShapeDialog.pm @@ -1,3 +1,6 @@ +# The bed shape dialog. +# The dialog opens from Print Settins tab -> Bed Shape: Set... + package Slic3r::GUI::BedShapeDialog; use strict; use warnings; diff --git a/lib/Slic3r/GUI/BonjourBrowser.pm b/lib/Slic3r/GUI/BonjourBrowser.pm index 469d0302e..c7513165f 100644 --- a/lib/Slic3r/GUI/BonjourBrowser.pm +++ b/lib/Slic3r/GUI/BonjourBrowser.pm @@ -1,3 +1,5 @@ +# A tiny dialog to select an OctoPrint device to print to. + package Slic3r::GUI::BonjourBrowser; use strict; use warnings; diff --git a/lib/Slic3r/GUI/ConfigWizard.pm b/lib/Slic3r/GUI/ConfigWizard.pm index 008b76f25..b90837937 100644 --- a/lib/Slic3r/GUI/ConfigWizard.pm +++ b/lib/Slic3r/GUI/ConfigWizard.pm @@ -1,3 +1,6 @@ +# The config wizard is executed when the Slic3r is first started. +# The wizard helps the user to specify the 3D printer properties. + package Slic3r::GUI::ConfigWizard; use strict; use warnings; diff --git a/lib/Slic3r/GUI/Controller.pm b/lib/Slic3r/GUI/Controller.pm index 6d2d16b9e..c6a568f82 100644 --- a/lib/Slic3r/GUI/Controller.pm +++ b/lib/Slic3r/GUI/Controller.pm @@ -1,3 +1,7 @@ +# The "Controller" tab to control the printer using serial / USB. +# This feature is rarely used. Much more often, the firmware reads the G-codes from a SD card. +# May there be multiple subtabs per each printer connected? + package Slic3r::GUI::Controller; use strict; use warnings; diff --git a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm index ebd0031a9..ac997730d 100644 --- a/lib/Slic3r/GUI/Controller/ManualControlDialog.pm +++ b/lib/Slic3r/GUI/Controller/ManualControlDialog.pm @@ -1,3 +1,5 @@ +# A printer "Controller" -> "ManualControlDialog" subtab, opened per 3D printer connected? + package Slic3r::GUI::Controller::ManualControlDialog; use strict; use warnings; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 43ede10b6..d386d7f86 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -1,3 +1,5 @@ +# The main frame, the parent of all. + package Slic3r::GUI::MainFrame; use strict; use warnings; diff --git a/lib/Slic3r/GUI/Notifier.pm b/lib/Slic3r/GUI/Notifier.pm index a2242bcdf..eb548d014 100644 --- a/lib/Slic3r/GUI/Notifier.pm +++ b/lib/Slic3r/GUI/Notifier.pm @@ -1,3 +1,6 @@ +# Notify about the end of slicing. +# The notifications are sent out using the Growl protocol if installed, and using DBus XWindow protocol. + package Slic3r::GUI::Notifier; use Moo; diff --git a/lib/Slic3r/GUI/OptionsGroup.pm b/lib/Slic3r/GUI/OptionsGroup.pm index b64fc8cb7..382dbc744 100644 --- a/lib/Slic3r/GUI/OptionsGroup.pm +++ b/lib/Slic3r/GUI/OptionsGroup.pm @@ -1,3 +1,5 @@ +# A dialog group object. Used by the Tab, SimpleTab, Preferences dialog, ManualControlDialog etc. + package Slic3r::GUI::OptionsGroup; use Moo; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index daa4d6c95..0d44d8875 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1,3 +1,5 @@ +# The "Plater" tab. It contains the "3D", "2D", "Preview" and "Layers" subtabs. + package Slic3r::GUI::Plater; use strict; use warnings; diff --git a/lib/Slic3r/GUI/Plater/2D.pm b/lib/Slic3r/GUI/Plater/2D.pm index 24bf80135..8fb8908e1 100644 --- a/lib/Slic3r/GUI/Plater/2D.pm +++ b/lib/Slic3r/GUI/Plater/2D.pm @@ -1,3 +1,6 @@ +# 2D preview on the platter. +# 3D objects are visualized by their convex hulls. + package Slic3r::GUI::Plater::2D; use strict; use warnings; diff --git a/lib/Slic3r/GUI/Plater/2DToolpaths.pm b/lib/Slic3r/GUI/Plater/2DToolpaths.pm index ee59c2980..bc3d4e605 100644 --- a/lib/Slic3r/GUI/Plater/2DToolpaths.pm +++ b/lib/Slic3r/GUI/Plater/2DToolpaths.pm @@ -1,3 +1,7 @@ +# 2D preview of the tool paths of a single layer, using a thin line. +# OpenGL is used to render the paths. +# Vojtech also added a 2D simulation of under/over extrusion in a single layer. + package Slic3r::GUI::Plater::2DToolpaths; use strict; use warnings; diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index a523fb085..cfdeba649 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -1,3 +1,6 @@ +# Cut an object at a Z position, keep either the top or the bottom of the object. +# This dialog gets opened with the "Cut..." button above the platter. + package Slic3r::GUI::Plater::ObjectCutDialog; use strict; use warnings; diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index a75be5902..4882233a6 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -1,3 +1,6 @@ +# Configuration of mesh modifiers and their parameters. +# This panel is inserted into ObjectSettingsDialog. + package Slic3r::GUI::Plater::ObjectPartsPanel; use strict; use warnings; diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm index c920b796a..d7909816b 100644 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm @@ -1,3 +1,7 @@ +# This dialog opens up when double clicked on an object line in the list at the right side of the platter. +# One may load additional STLs and additional modifier STLs, +# one may change the properties of the print per each modifier mesh or a Z-span. + package Slic3r::GUI::Plater::ObjectSettingsDialog; use strict; use warnings; diff --git a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm index 1b976c10e..e848b7638 100644 --- a/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm +++ b/lib/Slic3r/GUI/Plater/OverrideSettingsPanel.pm @@ -1,3 +1,6 @@ +# Included in ObjectSettingsDialog -> ObjectPartsPanel. +# Maintains, displays, adds and removes overrides of slicing parameters for an object and its modifier mesh. + package Slic3r::GUI::Plater::OverrideSettingsPanel; use strict; use warnings; diff --git a/lib/Slic3r/GUI/Preferences.pm b/lib/Slic3r/GUI/Preferences.pm index ed210d229..431f642d6 100644 --- a/lib/Slic3r/GUI/Preferences.pm +++ b/lib/Slic3r/GUI/Preferences.pm @@ -1,3 +1,5 @@ +# Preferences dialog, opens from Menu: File->Preferences + package Slic3r::GUI::Preferences; use Wx qw(:dialog :id :misc :sizer :systemsettings wxTheApp); use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER); diff --git a/lib/Slic3r/GUI/ProgressStatusBar.pm b/lib/Slic3r/GUI/ProgressStatusBar.pm index b901740a9..32fd52680 100644 --- a/lib/Slic3r/GUI/ProgressStatusBar.pm +++ b/lib/Slic3r/GUI/ProgressStatusBar.pm @@ -1,3 +1,5 @@ +# Status bar at the bottom of the main screen. + package Slic3r::GUI::ProgressStatusBar; use strict; use warnings; diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index f91ddf96a..5ccd53b59 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -1,3 +1,5 @@ +# DLP Projector screen for the SLA (stereolitography) print process + package Slic3r::GUI::Projector; use strict; use warnings; diff --git a/lib/Slic3r/GUI/SimpleTab.pm b/lib/Slic3r/GUI/SimpleTab.pm index 888ff0e9b..13c6efc88 100644 --- a/lib/Slic3r/GUI/SimpleTab.pm +++ b/lib/Slic3r/GUI/SimpleTab.pm @@ -1,3 +1,6 @@ +# The "Simple" Print Settings tab. +# The "Simple" mode is enabled by File->Preferences dialog. + package Slic3r::GUI::SimpleTab; use strict; use warnings; diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index a49d6fd2f..501b69c0e 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -1,3 +1,6 @@ +# The "Expert" tab at the right of the main tabbed window. +# The "Expert" is enabled by File->Preferences dialog. + package Slic3r::GUI::Tab; use strict; use warnings; diff --git a/lib/Slic3r/Test/SectionCut.pm b/lib/Slic3r/Test/SectionCut.pm index 04106d46f..8cfda13d1 100644 --- a/lib/Slic3r/Test/SectionCut.pm +++ b/lib/Slic3r/Test/SectionCut.pm @@ -1,4 +1,6 @@ -# Slices at the XZ plane, for debugging purposes. +# 2D cut in the XZ plane through the toolpaths. +# For debugging purposes. + package Slic3r::Test::SectionCut; use Moo; diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index db1eb3255..b0de42b1b 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -116,6 +116,8 @@ PerimeterGenerator::process() */ } } else { + //FIXME Is this offset correct if the line width of the inner perimeters differs + // from the line width of the infill? coord_t distance = (i == 1) ? ext_pspacing2 : pspacing; if (this->config->thin_walls) { From c353eef62644273bea5716338d5fa918e7f31837 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 15 Sep 2016 00:06:42 +0200 Subject: [PATCH 063/225] Generate multiple vertex lists rather than requesting a single huge memory block. --- lib/Slic3r/GUI/3DScene.pm | 118 ++++++++++++++++++++++++++++---------- 1 file changed, 88 insertions(+), 30 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 6595852af..67edea7b8 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1268,6 +1268,21 @@ sub load_print_object_toolpaths { my @layers = sort { $a->print_z <=> $b->print_z } @{$object->layers}, @{$object->support_layers}; + # Bounding box of the object and its copies. + my $bb = Slic3r::Geometry::BoundingBoxf3->new; + { + my $obb = $object->bounding_box; + foreach my $copy (@{ $object->_shifted_copies }) { + my $cbb = $obb->clone; + $cbb->translate(@$copy); + $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$cbb->min_point}, 0)); + $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$cbb->max_point}, $object->size->z)); + } + } + + # Maximum size of an allocation block: 32MB / sizeof(float) + my $alloc_size_max = 32 * 1048576 / 4; + foreach my $layer (@layers) { my $top_z = $layer->print_z; @@ -1275,9 +1290,13 @@ sub load_print_object_toolpaths { $perim_offsets{$top_z} = [ $perim_qverts->size, $perim_tverts->size, ]; + } + if (!exists $infill_offsets{$top_z}) { $infill_offsets{$top_z} = [ $infill_qverts->size, $infill_tverts->size, ]; + } + if (!exists $support_offsets{$top_z}) { $support_offsets{$top_z} = [ $support_qverts->size, $support_tverts->size, ]; @@ -1304,40 +1323,79 @@ sub load_print_object_toolpaths { $support_qverts, $support_tverts); } } + + if ($perim_qverts->size() > $alloc_size_max || $perim_tverts->size() > $alloc_size_max) { + # Store the vertex arrays and restart their containers. + push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( + bounding_box => $bb, + color => COLORS->[0], + qverts => $perim_qverts, + tverts => $perim_tverts, + offsets => { %perim_offsets }, + ); + $perim_qverts = Slic3r::GUI::_3DScene::GLVertexArray->new; + $perim_tverts = Slic3r::GUI::_3DScene::GLVertexArray->new; + %perim_offsets = (); + } + + if ($infill_qverts->size() > $alloc_size_max || $infill_tverts->size() > $alloc_size_max) { + # Store the vertex arrays and restart their containers. + push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( + bounding_box => $bb, + color => COLORS->[1], + qverts => $infill_qverts, + tverts => $infill_tverts, + offsets => { %infill_offsets }, + ); + $infill_qverts = Slic3r::GUI::_3DScene::GLVertexArray->new; + $infill_tverts = Slic3r::GUI::_3DScene::GLVertexArray->new; + %infill_offsets = (); + } + + if ($support_qverts->size() > $alloc_size_max || $support_tverts->size() > $alloc_size_max) { + # Store the vertex arrays and restart their containers. + push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( + bounding_box => $bb, + color => COLORS->[2], + qverts => $support_qverts, + tverts => $support_tverts, + offsets => { %support_offsets }, + ); + $support_qverts = Slic3r::GUI::_3DScene::GLVertexArray->new; + $support_tverts = Slic3r::GUI::_3DScene::GLVertexArray->new; + %support_offsets = (); + } + } + + if ($perim_qverts->size() > 0 || $perim_tverts->size() > 0) { + push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( + bounding_box => $bb, + color => COLORS->[0], + qverts => $perim_qverts, + tverts => $perim_tverts, + offsets => { %perim_offsets }, + ); } - my $obb = $object->bounding_box; - my $bb = Slic3r::Geometry::BoundingBoxf3->new; - foreach my $copy (@{ $object->_shifted_copies }) { - my $cbb = $obb->clone; - $cbb->translate(@$copy); - $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$cbb->min_point}, 0)); - $bb->merge_point(Slic3r::Pointf3->new_unscale(@{$cbb->max_point}, $object->size->z)); + if ($infill_qverts->size() > 0 || $infill_tverts->size() > 0) { + push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( + bounding_box => $bb, + color => COLORS->[1], + qverts => $infill_qverts, + tverts => $infill_tverts, + offsets => { %infill_offsets }, + ); } - push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( - bounding_box => $bb, - color => COLORS->[0], - qverts => $perim_qverts, - tverts => $perim_tverts, - offsets => { %perim_offsets }, - ); - - push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( - bounding_box => $bb, - color => COLORS->[1], - qverts => $infill_qverts, - tverts => $infill_tverts, - offsets => { %infill_offsets }, - ); - - push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( - bounding_box => $bb, - color => COLORS->[2], - qverts => $support_qverts, - tverts => $support_tverts, - offsets => { %support_offsets }, - ); + if ($support_qverts->size() > 0 || $support_tverts->size() > 0) { + push @{$self->volumes}, Slic3r::GUI::3DScene::Volume->new( + bounding_box => $bb, + color => COLORS->[2], + qverts => $support_qverts, + tverts => $support_tverts, + offsets => { %support_offsets }, + ); + } } sub set_toolpaths_range { From a0da2c49908a4b3e12c39844efdff4b133f2e035 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 23 Sep 2016 12:59:20 +0200 Subject: [PATCH 064/225] Fix of a spiral vase with realtive extruder distances enabled. The clone function is used by the SpiralVase.pm only, so the change is safe. --- lib/Slic3r/GCode/Reader.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GCode/Reader.pm b/lib/Slic3r/GCode/Reader.pm index 6743e6f83..5763b3848 100644 --- a/lib/Slic3r/GCode/Reader.pm +++ b/lib/Slic3r/GCode/Reader.pm @@ -22,7 +22,7 @@ sub apply_print_config { sub clone { my $self = shift; return (ref $self)->new( - map { $_ => $self->$_ } (@AXES, 'F', '_extrusion_axis'), + map { $_ => $self->$_ } (@AXES, 'F', '_extrusion_axis', 'config'), ); } From 893d92273d51e73ca447de2491f804f75057141b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 26 Sep 2016 12:44:45 +0200 Subject: [PATCH 065/225] Documented MultiPoint. --- xs/src/libslic3r/MultiPoint.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/MultiPoint.cpp b/xs/src/libslic3r/MultiPoint.cpp index 297e7ec3c..d8ee1d09d 100644 --- a/xs/src/libslic3r/MultiPoint.cpp +++ b/xs/src/libslic3r/MultiPoint.cpp @@ -171,9 +171,12 @@ MultiPoint::dump_perl() const return ret.str(); } +//FIXME This is very inefficient in term of memory use. +// The recursive algorithm shall run in place, not allocating temporary data in each recursion. Points MultiPoint::_douglas_peucker(const Points &points, const double tolerance) { + assert(points.size() >= 2); Points results; double dmax = 0; size_t index = 0; @@ -190,13 +193,15 @@ MultiPoint::_douglas_peucker(const Points &points, const double tolerance) Points dp0; dp0.reserve(index + 1); dp0.insert(dp0.end(), points.begin(), points.begin() + index + 1); + // Recursive call. Points dp1 = MultiPoint::_douglas_peucker(dp0, tolerance); results.reserve(results.size() + dp1.size() - 1); results.insert(results.end(), dp1.begin(), dp1.end() - 1); dp0.clear(); - dp0.reserve(points.size() - index + 1); + dp0.reserve(points.size() - index); dp0.insert(dp0.end(), points.begin() + index, points.end()); + // Recursive call. dp1 = MultiPoint::_douglas_peucker(dp0, tolerance); results.reserve(results.size() + dp1.size()); results.insert(results.end(), dp1.begin(), dp1.end()); From 56d281c72057285008445d9b6a25763194a4e7d8 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 26 Sep 2016 12:49:29 +0200 Subject: [PATCH 066/225] Bugfix: use Lift-z option for 2. extruder #3385 Thanks to https://github.com/platsch https://github.com/alexrj/Slic3r/pull/3392 --- xs/src/libslic3r/GCodeWriter.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xs/src/libslic3r/GCodeWriter.hpp b/xs/src/libslic3r/GCodeWriter.hpp index 72d54215b..51dc0d6d6 100644 --- a/xs/src/libslic3r/GCodeWriter.hpp +++ b/xs/src/libslic3r/GCodeWriter.hpp @@ -10,7 +10,7 @@ namespace Slic3r { class GCodeWriter { - public: +public: GCodeConfig config; std::map extruders; bool multiple_extruders; @@ -48,7 +48,7 @@ class GCodeWriter { std::string unlift(); Pointf3 get_position() const; - private: +private: std::string _extrusion_axis; Extruder* _extruder; unsigned int _last_acceleration; @@ -60,6 +60,6 @@ class GCodeWriter { std::string _retract(double length, double restart_extra, const std::string &comment); }; -} +} /* namespace Slic3r */ -#endif +#endif /* slic3r_GCodeWriter_hpp_ */ From 476839e077ffed8def76906eeb602e13cbfeff4c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 26 Sep 2016 12:53:41 +0200 Subject: [PATCH 067/225] Documented the bridge detector. --- xs/src/libslic3r/BridgeDetector.hpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/xs/src/libslic3r/BridgeDetector.hpp b/xs/src/libslic3r/BridgeDetector.hpp index 14bfd80af..bbbd26df9 100644 --- a/xs/src/libslic3r/BridgeDetector.hpp +++ b/xs/src/libslic3r/BridgeDetector.hpp @@ -9,11 +9,16 @@ namespace Slic3r { class BridgeDetector { - public: +public: + // The non-grown hole. ExPolygon expolygon; + // Lower slices, all regions. ExPolygonCollection lower_slices; - double extrusion_width; // scaled + // Scaled extrusion width of the infill. + double extrusion_width; + // Angle resolution for the brute force search of the best bridging angle. double resolution; + // The final optimal angle. double angle; BridgeDetector(const ExPolygon &_expolygon, const ExPolygonCollection &_lower_slices, coord_t _extrusion_width); @@ -21,8 +26,10 @@ class BridgeDetector { Polygons coverage(double angle = -1) const; Polylines unsupported_edges(double angle = -1) const; - private: - Polylines _edges; // representing the supporting edges +private: + // Open lines representing the supporting edges. + Polylines _edges; + // Closed polygons representing the supporting areas. ExPolygons _anchors; }; From 8c315ff467ad1cbf138df2e1fd9cd5776fad7052 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 5 Oct 2016 14:13:07 +0200 Subject: [PATCH 068/225] Select standard camera views (left / right / top / bottom / front / rear / default) by menu. --- lib/Slic3r/GUI/3DScene.pm | 44 +++++++++++++++++++++++++++++++++++++ lib/Slic3r/GUI/MainFrame.pm | 21 ++++++++++++++++++ lib/Slic3r/GUI/Plater.pm | 14 ++++++++++++ 3 files changed, 79 insertions(+) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 67edea7b8..ca662dc88 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -54,6 +54,15 @@ use constant PI => 3.1415927; use constant HAS_VBO => 1; +# phi / theta angles to orient the camera. +use constant VIEW_DEFAULT => [45.0,45.0]; +use constant VIEW_LEFT => [90.0,90.0]; +use constant VIEW_RIGHT => [-90.0,90.0]; +use constant VIEW_TOP => [0.0,0.0]; +use constant VIEW_BOTTOM => [0.0,180.0]; +use constant VIEW_FRONT => [0.0,90.0]; +use constant VIEW_REAR => [180.0,90.0]; + # make OpenGL::Array thread-safe { no warnings 'redefine'; @@ -317,6 +326,41 @@ sub set_viewport_from_scene { $self->_dirty(1); } +# Set the camera to a default orientation, +# zoom to volumes. +sub select_view { + my ($self, $direction) = @_; + my $dirvec; + if (ref($direction)) { + $dirvec = $direction; + } else { + if ($direction eq 'default') { + $dirvec = VIEW_DEFAULT; + } elsif ($direction eq 'left') { + $dirvec = VIEW_LEFT; + } elsif ($direction eq 'right') { + $dirvec = VIEW_RIGHT; + } elsif ($direction eq 'top') { + $dirvec = VIEW_TOP; + } elsif ($direction eq 'bottom') { + $dirvec = VIEW_BOTTOM; + } elsif ($direction eq 'front') { + $dirvec = VIEW_FRONT; + } elsif ($direction eq 'rear') { + $dirvec = VIEW_REAR; + } + } + $self->_sphi($dirvec->[0]); + $self->_stheta($dirvec->[1]); + # Avoid gimball lock. + $self->_stheta(150) if $self->_stheta > 150; + $self->_stheta(0) if $self->_stheta < 0; + # View everything. + $self->zoom_to_volumes; + $self->on_viewport_changed->() if $self->on_viewport_changed; + $self->Refresh; +} + sub zoom_to_bounding_box { my ($self, $bb) = @_; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index d386d7f86..53ac00e14 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -282,6 +282,18 @@ sub _init_menubar { $self->select_tab($tab_offset+2); }, undef, 'printer_empty.png'); } + + # View menu + if (!$self->{no_plater}) { + $self->{viewMenu} = Wx::Menu->new; + $self->_append_menu_item($self->{viewMenu}, "Default", 'Default View', sub { $self->select_view('default'); }); + $self->_append_menu_item($self->{viewMenu}, "Top" , 'Top View' , sub { $self->select_view('top' ); }); + $self->_append_menu_item($self->{viewMenu}, "Bottom" , 'Bottom View' , sub { $self->select_view('bottom' ); }); + $self->_append_menu_item($self->{viewMenu}, "Front" , 'Front View' , sub { $self->select_view('front' ); }); + $self->_append_menu_item($self->{viewMenu}, "Rear" , 'Rear View' , sub { $self->select_view('rear' ); }); + $self->_append_menu_item($self->{viewMenu}, "Left" , 'Left View' , sub { $self->select_view('left' ); }); + $self->_append_menu_item($self->{viewMenu}, "Right" , 'Right View' , sub { $self->select_view('right' ); }); + } # Help menu my $helpMenu = Wx::Menu->new; @@ -315,6 +327,7 @@ sub _init_menubar { $menubar->Append($self->{plater_menu}, "&Plater") if $self->{plater_menu}; $menubar->Append($self->{object_menu}, "&Object") if $self->{object_menu}; $menubar->Append($windowMenu, "&Window"); + $menubar->Append($self->{viewMenu}, "&View") if $self->{viewMenu}; $menubar->Append($helpMenu, "&Help"); $self->SetMenuBar($menubar); } @@ -783,6 +796,14 @@ sub select_tab { $self->{tabpanel}->SetSelection($tab); } +# Set a camera direction, zoom to all objects. +sub select_view { + my ($self, $direction) = @_; + if (! $self->{no_plater}) { + $self->{plater}->select_view($direction); + } +} + sub _append_menu_item { my ($self, $menu, $string, $description, $cb, $id, $icon) = @_; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 0d44d8875..f7c1168e6 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1829,6 +1829,20 @@ sub object_menu { return $menu; } +# Set a camera direction, zoom to all objects. +sub select_view { + my ($self, $direction) = @_; + my $idx_page = $self->{preview_notebook}->GetSelection; + my $page = ($idx_page == &Wx::wxNOT_FOUND) ? '3D' : $self->{preview_notebook}->GetPageText($idx_page); + if ($page eq 'Preview') { + $self->{preview3D}->canvas->select_view($direction); + $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); + } else { + $self->{canvas3D}->select_view($direction); + $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D}); + } +} + package Slic3r::GUI::Plater::DropTarget; use Wx::DND; use base 'Wx::FileDropTarget'; From 736e2cc4e60be0240131df59d01d4a05bf62053b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 24 Oct 2016 18:05:26 +0200 Subject: [PATCH 069/225] On Windows use the Slic3r.ico instead of the PNG file for the application icon. Conflicts: lib/Slic3r/GUI/MainFrame.pm --- lib/Slic3r/GUI/MainFrame.pm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 53ac00e14..a518a3ab2 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -21,7 +21,11 @@ sub new { my ($class, %params) = @_; my $self = $class->SUPER::new(undef, -1, 'Slic3r', wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE); - $self->SetIcon(Wx::Icon->new($Slic3r::var->("Slic3r_128px.png"), wxBITMAP_TYPE_PNG) ); + if ($^O eq 'MSWin32') { + $self->SetIcon(Wx::Icon->new($Slic3r::var->("Slic3r.ico"), wxBITMAP_TYPE_ICO)); + } else { + $self->SetIcon(Wx::Icon->new($Slic3r::var->("Slic3r_128px.png"), wxBITMAP_TYPE_PNG)); + } # store input params $self->{mode} = $params{mode}; From 4773987b0f9e0983c9ef7d7e2fc6c4f8c8be5c46 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 1 Nov 2016 13:41:24 +0100 Subject: [PATCH 070/225] Added "Notes" page to the filament configuration. Added "filament_max_volumetric_speed", a cap on the maximum volumetric extrusion role, filament specific. This is very useful when mixing rigid filament with a soft filament. Extended the import / export of multi-string values into configuration values, including the test cases. Multi-line strings will be enclosed into quotes, quotes escaped using a C-style escape sequences. Single word strings could still be stored without quotes. Conflicts: xs/src/libslic3r/Config.hpp --- lib/Slic3r/GUI/Tab.pm | 26 ++++- xs/src/libslic3r/Config.cpp | 165 +++++++++++++++++++++++++++++-- xs/src/libslic3r/Config.hpp | 59 ++++------- xs/src/libslic3r/GCode.cpp | 7 ++ xs/src/libslic3r/PrintConfig.cpp | 25 +++++ xs/src/libslic3r/PrintConfig.hpp | 4 + xs/t/15_config.t | 46 ++++++++- 7 files changed, 283 insertions(+), 49 deletions(-) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 501b69c0e..259ec7752 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -109,7 +109,10 @@ sub new { $self->_on_presets_changed; }); + # C++ instance DynamicPrintConfig $self->{config} = Slic3r::Config->new; + # Initialize the DynamicPrintConfig by default keys/values. + # Possible %params keys: no_controller $self->build(%params); $self->update_tree; $self->_update; @@ -885,7 +888,7 @@ sub build { my $self = shift; $self->init_config_options(qw( - filament_colour filament_diameter extrusion_multiplier + filament_colour filament_diameter filament_notes filament_max_volumetric_speed extrusion_multiplier temperature first_layer_temperature bed_temperature first_layer_bed_temperature fan_always_on cooling min_fan_speed max_fan_speed bridge_fan_speed disable_fan_first_layers @@ -966,6 +969,27 @@ sub build { $optgroup->append_single_option_line('min_print_speed'); } } + + { + my $page = $self->add_options_page('Advanced', 'wrench.png'); + { + my $optgroup = $page->new_optgroup('Print speed override'); + $optgroup->append_single_option_line('filament_max_volumetric_speed', 0); + } + } + + { + my $page = $self->add_options_page('Notes', 'note.png'); + { + my $optgroup = $page->new_optgroup('Notes', + label_width => 0, + ); + my $option = $optgroup->get_option('filament_notes', 0); + $option->full_width(1); + $option->height(250); + $optgroup->append_single_option_line($option); + } + } } sub _update { diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index 3cae922e9..36332c789 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -19,6 +19,159 @@ namespace Slic3r { +std::string escape_string_cstyle(const std::string &str) +{ + // Allocate a buffer twice the input string length, + // so the output will fit even if all input characters get escaped. + std::vector out(str.size() * 2, 0); + char *outptr = out.data(); + for (size_t i = 0; i < str.size(); ++ i) { + char c = str[i]; + if (c == '\n' || c == '\r') { + (*outptr ++) = '\\'; + (*outptr ++) = 'n'; + } else + (*outptr ++) = c; + } + return std::string(out.data(), outptr - out.data()); +} + +std::string escape_strings_cstyle(const std::vector &strs) +{ + // 1) Estimate the output buffer size to avoid buffer reallocation. + size_t outbuflen = 0; + for (size_t i = 0; i < strs.size(); ++ i) + // Reserve space for every character escaped + quotes + semicolon. + outbuflen += strs[i].size() * 2 + 3; + // 2) Fill in the buffer. + std::vector out(outbuflen, 0); + char *outptr = out.data(); + for (size_t j = 0; j < strs.size(); ++ j) { + if (j > 0) + // Separate the strings. + (*outptr ++) = ';'; + const std::string &str = strs[j]; + // Is the string simple or complex? Complex string contains spaces, tabs, new lines and other + // escapable characters. Empty string shall be quoted as well, if it is the only string in strs. + bool should_quote = strs.size() == 1 && str.empty(); + for (size_t i = 0; i < str.size(); ++ i) { + char c = str[i]; + if (c == ' ' || c == '\t' || c == '\\' || c == '"' || c == '\r' || c == '\n') { + should_quote = true; + break; + } + } + if (should_quote) { + (*outptr ++) = '"'; + for (size_t i = 0; i < str.size(); ++ i) { + char c = str[i]; + if (c == '\\' || c == '"') { + (*outptr ++) = '\\'; + (*outptr ++) = c; + } else if (c == '\n' || c == '\r') { + (*outptr ++) = '\\'; + (*outptr ++) = 'n'; + } else + (*outptr ++) = c; + } + (*outptr ++) = '"'; + } else { + memcpy(outptr, str.data(), str.size()); + outptr += str.size(); + } + } + return std::string(out.data(), outptr - out.data()); +} + +bool unescape_string_cstyle(const std::string &str, std::string &str_out) +{ + std::vector out(str.size(), 0); + char *outptr = out.data(); + for (size_t i = 0; i < str.size(); ++ i) { + char c = str[i]; + if (c == '\\') { + if (++ i == str.size()) + return false; + c = str[i]; + if (c == 'n') + (*outptr ++) = '\n'; + } else + (*outptr ++) = c; + } + str_out.assign(out.data(), outptr - out.data()); + return true; +} + +bool unescape_strings_cstyle(const std::string &str, std::vector &out) +{ + out.clear(); + if (str.empty()) + return true; + + size_t i = 0; + for (;;) { + // Skip white spaces. + char c = str[i]; + while (c == ' ' || c == '\t') { + if (++ i == str.size()) + return true; + c = str[i]; + } + // Start of a word. + std::vector buf; + buf.reserve(16); + // Is it enclosed in quotes? + c = str[i]; + if (c == '"') { + // Complex case, string is enclosed in quotes. + for (++ i; i < str.size(); ++ i) { + c = str[i]; + if (c == '"') { + // End of string. + break; + } + if (c == '\\') { + if (++ i == str.size()) + return false; + c = str[i]; + if (c == 'n') + c = '\n'; + } + buf.push_back(c); + } + if (i == str.size()) + return false; + ++ i; + } else { + for (; i < str.size(); ++ i) { + c = str[i]; + if (c == ';') + break; + buf.push_back(c); + } + } + // Store the string into the output vector. + out.push_back(std::string(buf.data(), buf.size())); + if (i == str.size()) + break; + // Skip white spaces. + c = str[i]; + while (c == ' ' || c == '\t') { + if (++ i == str.size()) + // End of string. This is correct. + return true; + c = str[i]; + } + if (c != ';') + return false; + if (++ i == str.size()) { + // Emit one additional empty string. + out.push_back(std::string()); + return true; + } + } +} + bool operator== (const ConfigOption &a, const ConfigOption &b) { @@ -143,16 +296,16 @@ ConfigBase::set_deserialize(const t_config_option_key &opt_key, std::string str, // Return an absolute value of a possibly relative config variable. // For example, return absolute infill extrusion width, either from an absolute value, or relative to the layer height. double -ConfigBase::get_abs_value(const t_config_option_key &opt_key) { - ConfigOption* opt = this->option(opt_key, false); - if (ConfigOptionFloatOrPercent* optv = dynamic_cast(opt)) { +ConfigBase::get_abs_value(const t_config_option_key &opt_key) const { + const ConfigOption* opt = this->option(opt_key); + if (const ConfigOptionFloatOrPercent* optv = dynamic_cast(opt)) { // get option definition const ConfigOptionDef* def = this->def->get(opt_key); assert(def != NULL); // compute absolute value over the absolute value of the base option return optv->get_abs_value(this->get_abs_value(def->ratio_over)); - } else if (ConfigOptionFloat* optv = dynamic_cast(opt)) { + } else if (const ConfigOptionFloat* optv = dynamic_cast(opt)) { return optv->value; } else { throw "Not a valid option type for get_abs_value()"; @@ -162,9 +315,9 @@ ConfigBase::get_abs_value(const t_config_option_key &opt_key) { // Return an absolute value of a possibly relative config variable. // For example, return absolute infill extrusion width, either from an absolute value, or relative to a provided value. double -ConfigBase::get_abs_value(const t_config_option_key &opt_key, double ratio_over) { +ConfigBase::get_abs_value(const t_config_option_key &opt_key, double ratio_over) const { // get stored option value - ConfigOptionFloatOrPercent* opt = dynamic_cast(this->option(opt_key)); + const ConfigOptionFloatOrPercent* opt = dynamic_cast(this->option(opt_key)); assert(opt != NULL); // compute absolute value diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 08a5a6a4b..00142be87 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -19,6 +19,11 @@ namespace Slic3r { typedef std::string t_config_option_key; typedef std::vector t_config_option_keys; +extern std::string escape_string_cstyle(const std::string &str); +extern std::string escape_strings_cstyle(const std::vector &strs); +extern bool unescape_string_cstyle(const std::string &str, std::string &out); +extern bool unescape_strings_cstyle(const std::string &str, std::vector &out); + // A generic value of a configuration option. class ConfigOption { public: @@ -121,6 +126,7 @@ class ConfigOptionFloats : public ConfigOptionVector std::vector vserialize() const { std::vector vv; + vv.reserve(this->values.size()); for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { std::ostringstream ss; ss << *it; @@ -184,6 +190,7 @@ class ConfigOptionInts : public ConfigOptionVector std::vector vserialize() const { std::vector vv; + vv.reserve(this->values.size()); for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { std::ostringstream ss; ss << *it; @@ -215,29 +222,12 @@ class ConfigOptionString : public ConfigOptionSingle std::string getString() const { return this->value; }; - std::string serialize() const { - std::string str = this->value; - - // s/\R/\\n/g - size_t pos = 0; - while ((pos = str.find("\n", pos)) != std::string::npos || (pos = str.find("\r", pos)) != std::string::npos) { - str.replace(pos, 1, "\\n"); - pos += 2; // length of "\\n" - } - - return str; - }; - - bool deserialize(std::string str, bool append = false) { - // s/\\n/\n/g - size_t pos = 0; - while ((pos = str.find("\\n", pos)) != std::string::npos) { - str.replace(pos, 2, "\n"); - pos += 1; // length of "\n" - } - - this->value = str; - return true; + std::string serialize() const { + return escape_string_cstyle(this->value); + } + + bool deserialize(std::string str) { + return unescape_string_cstyle(str, this->value); }; }; @@ -250,26 +240,15 @@ class ConfigOptionStrings : public ConfigOptionVector ConfigOptionStrings* clone() const { return new ConfigOptionStrings(this->values); }; std::string serialize() const { - std::ostringstream ss; - for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { - if (it - this->values.begin() != 0) ss << ";"; - ss << *it; - } - return ss.str(); + return escape_strings_cstyle(this->values); }; std::vector vserialize() const { return this->values; }; - bool deserialize(std::string str, bool append = false) { - if (!append) this->values.clear(); - std::istringstream is(str); - std::string item_str; - while (std::getline(is, item_str, ';')) { - this->values.push_back(item_str); - } - return true; + bool deserialize(std::string str) { + return unescape_strings_cstyle(str, this->values); }; }; @@ -689,9 +668,9 @@ class ConfigBase bool equals(ConfigBase &other); t_config_option_keys diff(ConfigBase &other); std::string serialize(const t_config_option_key &opt_key) const; - bool set_deserialize(const t_config_option_key &opt_key, std::string str, bool append = false); - double get_abs_value(const t_config_option_key &opt_key); - double get_abs_value(const t_config_option_key &opt_key, double ratio_over); + bool set_deserialize(const t_config_option_key &opt_key, std::string str); + double get_abs_value(const t_config_option_key &opt_key) const; + double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const; void setenv_(); void load(const std::string &file); void save(const std::string &file) const; diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 9a71b2a64..0ac7cd66c 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -557,6 +557,13 @@ GCode::_extrude(ExtrusionPath path, std::string description, double speed) this->config.max_volumetric_speed.value / path.mm3_per_mm ); } + if (EXTRUDER_CONFIG(filament_max_volumetric_speed) > 0) { + // cap speed with max_volumetric_speed anyway (even if user is not using autospeed) + speed = std::min( + speed, + EXTRUDER_CONFIG(filament_max_volumetric_speed) / path.mm3_per_mm + ); + } double F = speed * 60; // convert mm/sec to mm/min // extrude arc or line diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index fca964a74..a93fcd00f 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -293,6 +293,31 @@ PrintConfigDef::PrintConfigDef() def->default_value = opt; } + def = this->add("filament_notes", coStrings); + def->label = "Filament notes"; + def->tooltip = "You can put your notes regarding the filament here."; + def->cli = "filament-notes=s@"; + def->multiline = true; + def->full_width = true; + def->height = 130; + { + ConfigOptionStrings* opt = new ConfigOptionStrings(); + opt->values.push_back(""); + def->default_value = opt; + } + + def = this->add("filament_max_volumetric_speed", coFloats); + def->label = "Max volumetric speed"; + def->tooltip = "Maximum volumetric speed allowed for this filament. Limits the maximum volumetric speed of a print to the minimum of print and filament volumetric speed. Set to zero for no limit."; + def->sidetext = "mm³/s"; + def->cli = "filament-max-volumetric-speed=f@"; + def->min = 0; + { + ConfigOptionFloats* opt = new ConfigOptionFloats(); + opt->values.push_back(0.f); + def->default_value = opt; + } + def = this->add("filament_diameter", coFloats); def->label = "Diameter"; def->tooltip = "Enter your filament diameter here. Good precision is required, so use a caliper and do multiple measurements along the filament, then compute the average."; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 953487acd..0c402e50a 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -289,6 +289,7 @@ class GCodeConfig : public virtual StaticPrintConfig ConfigOptionString extrusion_axis; ConfigOptionFloats extrusion_multiplier; ConfigOptionFloats filament_diameter; + ConfigOptionFloats filament_max_volumetric_speed; ConfigOptionBool gcode_comments; ConfigOptionEnum gcode_flavor; ConfigOptionString layer_gcode; @@ -321,6 +322,7 @@ class GCodeConfig : public virtual StaticPrintConfig OPT_PTR(extrusion_axis); OPT_PTR(extrusion_multiplier); OPT_PTR(filament_diameter); + OPT_PTR(filament_max_volumetric_speed); OPT_PTR(gcode_comments); OPT_PTR(gcode_flavor); OPT_PTR(layer_gcode); @@ -378,6 +380,7 @@ class PrintConfig : public GCodeConfig ConfigOptionBool fan_always_on; ConfigOptionInt fan_below_layer_time; ConfigOptionStrings filament_colour; + ConfigOptionStrings filament_notes; ConfigOptionFloat first_layer_acceleration; ConfigOptionInt first_layer_bed_temperature; ConfigOptionFloatOrPercent first_layer_extrusion_width; @@ -435,6 +438,7 @@ class PrintConfig : public GCodeConfig OPT_PTR(fan_always_on); OPT_PTR(fan_below_layer_time); OPT_PTR(filament_colour); + OPT_PTR(filament_notes); OPT_PTR(first_layer_acceleration); OPT_PTR(first_layer_bed_temperature); OPT_PTR(first_layer_extrusion_width); diff --git a/xs/t/15_config.t b/xs/t/15_config.t index 838ce18b0..a4c5d5925 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -4,7 +4,8 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 110; +use Test::More tests => 146; +use Data::Dumper; foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintConfig) { $config->set('layer_height', 0.3); @@ -24,7 +25,48 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo is $config->serialize('notes'), 'foo\nbar', 'serialize string with newline'; $config->set_deserialize('notes', 'bar\nbaz'); is $config->get('notes'), "bar\nbaz", 'deserialize string with newline'; - + + foreach my $test_data ( + { + name => 'empty', + values => [], + serialized => '' + }, + { + name => 'single empty', + values => [''], + serialized => '""' + }, + { + name => 'single noempty, simple', + values => ['RGB'], + serialized => 'RGB' + }, + { + name => 'multiple noempty, simple', + values => ['ABC', 'DEF', '09182745@!#$*(&'], + serialized => 'ABC;DEF;09182745@!#$*(&' + }, + { + name => 'multiple, simple, some empty', + values => ['ABC', 'DEF', '', '09182745@!#$*(&', ''], + serialized => 'ABC;DEF;;09182745@!#$*(&;' + }, + { + name => 'complex', + values => ['some "quoted" notes', "yet\n some notes", "whatever \n notes", ''], + serialized => '"some \"quoted\" notes";"yet\n some notes";"whatever \n notes";' + } + ) + { + $config->set('filament_notes', $test_data->{values}); + is $config->serialize('filament_notes'), $test_data->{serialized}, 'serialize multi-string value ' . $test_data->{name}; + $config->set_deserialize('filament_notes', ''); + is_deeply $config->get('filament_notes'), [], 'deserialize multi-string value - empty ' . $test_data->{name}; + $config->set_deserialize('filament_notes', $test_data->{serialized}); + is_deeply $config->get('filament_notes'), $test_data->{values}, 'deserialize complex multi-string value ' . $test_data->{name}; + } + $config->set('first_layer_height', 0.3); ok abs($config->get('first_layer_height') - 0.3) < 1e-4, 'set/get absolute floatOrPercent'; is $config->serialize('first_layer_height'), '0.3', 'serialize absolute floatOrPercent'; From c93ac84f5e454ea5dd0cde047a5667619e92c449 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 1 Nov 2016 13:53:28 +0100 Subject: [PATCH 071/225] Missing include for memcpy for Linux. Conflicts: xs/src/libslic3r/Config.cpp --- xs/src/libslic3r/Config.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index 36332c789..899936334 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #if defined(_WIN32) && !defined(setenv) && defined(_putenv_s) #define setenv(k, v, o) _putenv_s(k, v) From cc01e6565de5f306a5aa6f02bfe37c67ec75022a Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 1 Nov 2016 14:52:44 +0100 Subject: [PATCH 072/225] Fixed a return value in the Config multi-string parser. --- xs/src/libslic3r/Config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index 899936334..104fa28d0 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -154,7 +154,7 @@ bool unescape_strings_cstyle(const std::string &str, std::vector &o // Store the string into the output vector. out.push_back(std::string(buf.data(), buf.size())); if (i == str.size()) - break; + return true; // Skip white spaces. c = str[i]; while (c == ' ' || c == '\t') { From 9c7e4871db0ff722fa091c28f4b8a5b23f8864e9 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 24 Nov 2016 16:20:04 +0100 Subject: [PATCH 073/225] Fix conflict in recent merges --- xs/src/libslic3r/Config.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 00142be87..41923a6a5 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -226,7 +226,7 @@ class ConfigOptionString : public ConfigOptionSingle return escape_string_cstyle(this->value); } - bool deserialize(std::string str) { + bool deserialize(std::string str, bool append = false) { return unescape_string_cstyle(str, this->value); }; }; @@ -247,7 +247,7 @@ class ConfigOptionStrings : public ConfigOptionVector return this->values; }; - bool deserialize(std::string str) { + bool deserialize(std::string str, bool append = false) { return unescape_strings_cstyle(str, this->values); }; }; @@ -668,7 +668,7 @@ class ConfigBase bool equals(ConfigBase &other); t_config_option_keys diff(ConfigBase &other); std::string serialize(const t_config_option_key &opt_key) const; - bool set_deserialize(const t_config_option_key &opt_key, std::string str); + bool set_deserialize(const t_config_option_key &opt_key, std::string str, bool append = false); double get_abs_value(const t_config_option_key &opt_key) const; double get_abs_value(const t_config_option_key &opt_key, double ratio_over) const; void setenv_(); From 954e2d3e5c2788159c0aac2de4f4b0f68026e8c9 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 24 Nov 2016 16:22:14 +0100 Subject: [PATCH 074/225] ExtrusionEntity and derived classes: Documented, short methods made inline for efficiency and readability --- xs/src/libslic3r/ExtrusionEntity.cpp | 117 +----------------- xs/src/libslic3r/ExtrusionEntity.hpp | 112 +++++++++++------ .../libslic3r/ExtrusionEntityCollection.cpp | 6 +- xs/src/libslic3r/PerimeterGenerator.cpp | 2 + 4 files changed, 81 insertions(+), 156 deletions(-) diff --git a/xs/src/libslic3r/ExtrusionEntity.cpp b/xs/src/libslic3r/ExtrusionEntity.cpp index 4fd878f83..2b635fc93 100644 --- a/xs/src/libslic3r/ExtrusionEntity.cpp +++ b/xs/src/libslic3r/ExtrusionEntity.cpp @@ -4,34 +4,11 @@ #include "ClipperUtils.hpp" #include "Extruder.hpp" #include +#include #include namespace Slic3r { - -ExtrusionPath* -ExtrusionPath::clone() const -{ - return new ExtrusionPath (*this); -} -void -ExtrusionPath::reverse() -{ - this->polyline.reverse(); -} - -Point -ExtrusionPath::first_point() const -{ - return this->polyline.points.front(); -} - -Point -ExtrusionPath::last_point() const -{ - return this->polyline.points.back(); -} - void ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const { @@ -66,38 +43,6 @@ ExtrusionPath::length() const return this->polyline.length(); } -bool -ExtrusionPath::is_perimeter() const -{ - return this->role == erPerimeter - || this->role == erExternalPerimeter - || this->role == erOverhangPerimeter; -} - -bool -ExtrusionPath::is_infill() const -{ - return this->role == erBridgeInfill - || this->role == erInternalInfill - || this->role == erSolidInfill - || this->role == erTopSolidInfill; -} - -bool -ExtrusionPath::is_solid_infill() const -{ - return this->role == erBridgeInfill - || this->role == erSolidInfill - || this->role == erTopSolidInfill; -} - -bool -ExtrusionPath::is_bridge() const -{ - return this->role == erBridgeInfill - || this->role == erOverhangPerimeter; -} - void ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const { @@ -114,12 +59,6 @@ ExtrusionPath::grow() const return offset(this->polyline, +scale_(this->width/2)); } -ExtrusionLoop* -ExtrusionLoop::clone() const -{ - return new ExtrusionLoop (*this); -} - bool ExtrusionLoop::make_clockwise() { @@ -144,18 +83,6 @@ ExtrusionLoop::reverse() std::reverse(this->paths.begin(), this->paths.end()); } -Point -ExtrusionLoop::first_point() const -{ - return this->paths.front().polyline.points.front(); -} - -Point -ExtrusionLoop::last_point() const -{ - return this->paths.back().polyline.points.back(); // which coincides with first_point(), by the way -} - Polygon ExtrusionLoop::polygon() const { @@ -292,53 +219,21 @@ ExtrusionLoop::has_overhang_point(const Point &point) const return false; } -bool -ExtrusionLoop::is_perimeter() const -{ - return this->paths.front().role == erPerimeter - || this->paths.front().role == erExternalPerimeter - || this->paths.front().role == erOverhangPerimeter; -} - -bool -ExtrusionLoop::is_infill() const -{ - return this->paths.front().role == erBridgeInfill - || this->paths.front().role == erInternalInfill - || this->paths.front().role == erSolidInfill - || this->paths.front().role == erTopSolidInfill; -} - -bool -ExtrusionLoop::is_solid_infill() const -{ - return this->paths.front().role == erBridgeInfill - || this->paths.front().role == erSolidInfill - || this->paths.front().role == erTopSolidInfill; -} - Polygons ExtrusionLoop::grow() const { Polygons pp; - for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) { - Polygons path_pp = path->grow(); - pp.insert(pp.end(), path_pp.begin(), path_pp.end()); - } + for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) + append_to(pp, path->grow()); return pp; } double ExtrusionLoop::min_mm3_per_mm() const { - double min_mm3_per_mm = 0; - for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) { - if (min_mm3_per_mm == 0) { - min_mm3_per_mm = path->mm3_per_mm; - } else { - min_mm3_per_mm = fmin(min_mm3_per_mm, path->mm3_per_mm); - } - } + double min_mm3_per_mm = std::numeric_limits::max(); + for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) + min_mm3_per_mm = std::min(min_mm3_per_mm, path->mm3_per_mm); return min_mm3_per_mm; } diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index 7b4e796e4..1e1ae3d5b 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -36,22 +36,18 @@ enum ExtrusionLoopRole { class ExtrusionEntity { - public: - virtual bool is_collection() const { - return false; - }; - virtual bool is_loop() const { - return false; - }; - virtual bool can_reverse() const { - return true; - }; +public: + virtual bool is_collection() const { return false; } + virtual bool is_loop() const { return false; } + virtual bool can_reverse() const { return true; } virtual ExtrusionEntity* clone() const = 0; virtual ~ExtrusionEntity() {}; virtual void reverse() = 0; virtual Point first_point() const = 0; virtual Point last_point() const = 0; + // Produce a list of 2D polygons covered by the extruded path. virtual Polygons grow() const = 0; + // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. virtual double min_mm3_per_mm() const = 0; virtual Polyline as_polyline() const = 0; virtual double length() const { return 0; }; @@ -61,34 +57,57 @@ typedef std::vector ExtrusionEntitiesPtr; class ExtrusionPath : public ExtrusionEntity { - public: +public: Polyline polyline; ExtrusionRole role; - double mm3_per_mm; // mm^3 of plastic per mm of linear head motion + // Volumetric velocity. mm^3 of plastic per mm of linear head motion + double mm3_per_mm; + // Width of the extrusion. float width; + // Height of the extrusion. float height; ExtrusionPath(ExtrusionRole role) : role(role), mm3_per_mm(-1), width(-1), height(-1) {}; - ExtrusionPath* clone() const; - void reverse(); - Point first_point() const; - Point last_point() const; + ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : role(role), mm3_per_mm(mm3_per_mm), width(width), height(height) {}; +// ExtrusionPath(ExtrusionRole role, const Flow &flow) : role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height) {}; + ExtrusionPath* clone() const { return new ExtrusionPath (*this); } + void reverse() { this->polyline.reverse(); } + Point first_point() const { return this->polyline.points.front(); } + Point last_point() const { return this->polyline.points.back(); } + // Produce a list of extrusion paths into retval by clipping this path by ExPolygonCollection. + // Currently not used. void intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const; + // Produce a list of extrusion paths into retval by removing parts of this path by ExPolygonCollection. + // Currently not used. void subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const; void clip_end(double distance); void simplify(double tolerance); virtual double length() const; - bool is_perimeter() const; - bool is_infill() const; - bool is_solid_infill() const; - bool is_bridge() const; + bool is_perimeter() const { + return this->role == erPerimeter + || this->role == erExternalPerimeter + || this->role == erOverhangPerimeter; + } + bool is_infill() const { + return this->role == erBridgeInfill + || this->role == erInternalInfill + || this->role == erSolidInfill + || this->role == erTopSolidInfill; + } + bool is_solid_infill() const { + return this->role == erBridgeInfill + || this->role == erSolidInfill + || this->role == erTopSolidInfill; + } + bool is_bridge() const { + return this->role == erBridgeInfill + || this->role == erOverhangPerimeter; + } + // Produce a list of 2D polygons covered by the extruded path. Polygons grow() const; - double min_mm3_per_mm() const { - return this->mm3_per_mm; - }; - Polyline as_polyline() const { - return this->polyline; - }; + // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. + double min_mm3_per_mm() const { return this->mm3_per_mm; } + Polyline as_polyline() const { return this->polyline; } private: void _inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const; @@ -109,32 +128,43 @@ class ExtrusionLoop : public ExtrusionEntity : role(role) { this->paths.push_back(path); }; - bool is_loop() const { - return true; - }; - bool can_reverse() const { - return false; - }; - ExtrusionLoop* clone() const; + bool is_loop() const { return true; } + bool can_reverse() const { return false; } + ExtrusionLoop* clone() const { return new ExtrusionLoop (*this); } bool make_clockwise(); bool make_counter_clockwise(); void reverse(); - Point first_point() const; - Point last_point() const; + Point first_point() const { return this->paths.front().polyline.points.front(); } + Point last_point() const { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); } Polygon polygon() const; virtual double length() const; bool split_at_vertex(const Point &point); void split_at(const Point &point); void clip_end(double distance, ExtrusionPaths* paths) const; + // Test, whether the point is extruded by a bridging flow. + // This used to be used to avoid placing seams on overhangs, but now the EdgeGrid is used instead. bool has_overhang_point(const Point &point) const; - bool is_perimeter() const; - bool is_infill() const; - bool is_solid_infill() const; + bool is_perimeter() const { + return this->paths.front().role == erPerimeter + || this->paths.front().role == erExternalPerimeter + || this->paths.front().role == erOverhangPerimeter; + } + bool is_infill() const { + return this->paths.front().role == erBridgeInfill + || this->paths.front().role == erInternalInfill + || this->paths.front().role == erSolidInfill + || this->paths.front().role == erTopSolidInfill; + } + bool is_solid_infill() const { + return this->paths.front().role == erBridgeInfill + || this->paths.front().role == erSolidInfill + || this->paths.front().role == erTopSolidInfill; + } + // Produce a list of 2D polygons covered by the extruded path. Polygons grow() const; + // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. double min_mm3_per_mm() const; - Polyline as_polyline() const { - return this->polygon().split_at_first_point(); - }; + Polyline as_polyline() const { return this->polygon().split_at_first_point(); } }; } diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.cpp b/xs/src/libslic3r/ExtrusionEntityCollection.cpp index 1d16a5f50..ec3e28e6c 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.cpp @@ -192,10 +192,8 @@ Polygons ExtrusionEntityCollection::grow() const { Polygons pp; - for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) { - Polygons entity_pp = (*it)->grow(); - pp.insert(pp.end(), entity_pp.begin(), entity_pp.end()); - } + for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) + append_to(pp, (*it)->grow()); return pp; } diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index b0de42b1b..574dcebe2 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -272,6 +272,8 @@ PerimeterGenerator::process() are not subtracted from fill surfaces (they might be too short gaps that medial axis skips but infill might join with other infill regions and use zigzag). */ + //FIXME Vojtech: This grows by a rounded extrusion width, not by line spacing, + // therefore it may cover the area, but no the volume. last = diff(last, gap_fill.grow()); } } From a5135f4369a2173a233b98c41028ccf3d0e40a70 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 4 Nov 2016 15:03:51 +0100 Subject: [PATCH 075/225] Hopefully a fix of https://github.com/prusa3d/Slic3r/issues/11 Replaced eval { die } construct with a bool return value indicating success or failure of an automatic arrangement of parts on the print bed. Don't know exactly what is happening here, but throwing a "die" inside a XS function and then catching it inside an eval {} block is suspcious. Conflicts: xs/src/libslic3r/Geometry.cpp xs/src/libslic3r/Geometry.hpp --- lib/Slic3r/GUI/Plater.pm | 4 +- xs/src/libslic3r/ExtrusionEntity.hpp | 12 +- xs/src/libslic3r/Geometry.cpp | 316 ++------------------------- xs/src/libslic3r/Geometry.hpp | 18 +- xs/src/libslic3r/Model.cpp | 18 +- xs/src/libslic3r/Model.hpp | 5 +- xs/xsp/Geometry.xsp | 7 +- xs/xsp/Model.xsp | 3 +- 8 files changed, 56 insertions(+), 327 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index f7c1168e6..4a75390a9 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -918,9 +918,7 @@ sub arrange { $self->pause_background_process; my $bb = Slic3r::Geometry::BoundingBoxf->new_from_points($self->{config}->bed_shape); - eval { - $self->{model}->arrange_objects($self->GetFrame->config->min_object_distance, $bb); - }; + my $success = $self->{model}->arrange_objects($self->GetFrame->config->min_object_distance, $bb); # ignore arrange failures on purpose: user has visual feedback and we don't need to warn him # when parts don't fit in print bed diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index 1e1ae3d5b..7f411f1f4 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -87,22 +87,22 @@ public: return this->role == erPerimeter || this->role == erExternalPerimeter || this->role == erOverhangPerimeter; - } + }; bool is_infill() const { return this->role == erBridgeInfill || this->role == erInternalInfill || this->role == erSolidInfill || this->role == erTopSolidInfill; - } + }; bool is_solid_infill() const { return this->role == erBridgeInfill || this->role == erSolidInfill || this->role == erTopSolidInfill; - } + }; bool is_bridge() const { return this->role == erBridgeInfill || this->role == erOverhangPerimeter; - } + }; // Produce a list of 2D polygons covered by the extruded path. Polygons grow() const; // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. @@ -148,13 +148,13 @@ class ExtrusionLoop : public ExtrusionEntity return this->paths.front().role == erPerimeter || this->paths.front().role == erExternalPerimeter || this->paths.front().role == erOverhangPerimeter; - } + }; bool is_infill() const { return this->paths.front().role == erBridgeInfill || this->paths.front().role == erInternalInfill || this->paths.front().role == erSolidInfill || this->paths.front().role == erTopSolidInfill; - } + }; bool is_solid_infill() const { return this->paths.front().role == erBridgeInfill || this->paths.front().role == erSolidInfill diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp index d988a9726..fec7dea89 100644 --- a/xs/src/libslic3r/Geometry.cpp +++ b/xs/src/libslic3r/Geometry.cpp @@ -329,9 +329,26 @@ linint(double value, double oldmin, double oldmax, double newmin, double newmax) return (value - oldmin) * (newmax - newmin) / (oldmax - oldmin) + newmin; } -Pointfs -arrange(size_t total_parts, Pointf part, coordf_t dist, const BoundingBoxf* bb) +class ArrangeItem { + public: + Pointf pos; + size_t index_x, index_y; + coordf_t dist; +}; +class ArrangeItemIndex { + public: + coordf_t index; + ArrangeItem item; + ArrangeItemIndex(coordf_t _index, ArrangeItem _item) : index(_index), item(_item) {}; +}; + +bool +arrange(size_t total_parts, const Pointf &part_size, coordf_t dist, const BoundingBoxf* bb, Pointfs &positions) { + positions.clear(); + + Pointf part = part_size; + // use actual part size (the largest) plus separation distance (half on each side) in spacing algorithm part.x += dist; part.y += dist; @@ -349,7 +366,7 @@ arrange(size_t total_parts, Pointf part, coordf_t dist, const BoundingBoxf* bb) size_t cellw = floor((area.x + dist) / part.x); size_t cellh = floor((area.y + dist) / part.y); if (total_parts > (cellw * cellh)) - CONFESS("%zu parts won't fit in your print area!\n", total_parts); + return false; // total space used by cells Pointf cells(cellw * part.x, cellh * part.y); @@ -404,7 +421,7 @@ arrange(size_t total_parts, Pointf part, coordf_t dist, const BoundingBoxf* bb) } cellsorder.insert(cellsorder.begin() + low, ArrangeItemIndex(index, c)); } - ENDSORT: true; + ENDSORT: ; } } @@ -430,7 +447,6 @@ arrange(size_t total_parts, Pointf part, coordf_t dist, const BoundingBoxf* bb) } } // now we actually place objects into cells, positioned such that the left and bottom borders are at 0 - Pointfs positions; for (size_t i = 1; i <= total_parts; ++i) { ArrangeItemIndex c = cellsorder.front(); cellsorder.erase(cellsorder.begin()); @@ -447,297 +463,9 @@ arrange(size_t total_parts, Pointf part, coordf_t dist, const BoundingBoxf* bb) } } - return positions; + return true; } -#ifdef SLIC3R_DEBUG -// The following code for the visualization of the boost Voronoi diagram is based on: -// -// Boost.Polygon library voronoi_visualizer.cpp file -// Copyright Andrii Sydorchuk 2010-2012. -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE_1_0.txt or copy at -// http://www.boost.org/LICENSE_1_0.txt) -namespace Voronoi { namespace Internal { - - typedef double coordinate_type; - typedef boost::polygon::point_data point_type; - typedef boost::polygon::segment_data segment_type; - typedef boost::polygon::rectangle_data rect_type; -// typedef voronoi_builder VB; - typedef boost::polygon::voronoi_diagram VD; - typedef VD::cell_type cell_type; - typedef VD::cell_type::source_index_type source_index_type; - typedef VD::cell_type::source_category_type source_category_type; - typedef VD::edge_type edge_type; - typedef VD::cell_container_type cell_container_type; - typedef VD::cell_container_type vertex_container_type; - typedef VD::edge_container_type edge_container_type; - typedef VD::const_cell_iterator const_cell_iterator; - typedef VD::const_vertex_iterator const_vertex_iterator; - typedef VD::const_edge_iterator const_edge_iterator; - - static const std::size_t EXTERNAL_COLOR = 1; - - inline void color_exterior(const VD::edge_type* edge) - { - if (edge->color() == EXTERNAL_COLOR) - return; - edge->color(EXTERNAL_COLOR); - edge->twin()->color(EXTERNAL_COLOR); - const VD::vertex_type* v = edge->vertex1(); - if (v == NULL || !edge->is_primary()) - return; - v->color(EXTERNAL_COLOR); - const VD::edge_type* e = v->incident_edge(); - do { - color_exterior(e); - e = e->rot_next(); - } while (e != v->incident_edge()); - } - - inline point_type retrieve_point(const std::vector &segments, const cell_type& cell) - { - assert(cell.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT || cell.source_category() == SOURCE_CATEGORY_SEGMENT_END_POINT); - return (cell.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT) ? low(segments[cell.source_index()]) : high(segments[cell.source_index()]); - } - - inline void clip_infinite_edge(const std::vector &segments, const edge_type& edge, coordinate_type bbox_max_size, std::vector* clipped_edge) - { - const cell_type& cell1 = *edge.cell(); - const cell_type& cell2 = *edge.twin()->cell(); - point_type origin, direction; - // Infinite edges could not be created by two segment sites. - if (cell1.contains_point() && cell2.contains_point()) { - point_type p1 = retrieve_point(segments, cell1); - point_type p2 = retrieve_point(segments, cell2); - origin.x((p1.x() + p2.x()) * 0.5); - origin.y((p1.y() + p2.y()) * 0.5); - direction.x(p1.y() - p2.y()); - direction.y(p2.x() - p1.x()); - } else { - origin = cell1.contains_segment() ? retrieve_point(segments, cell2) : retrieve_point(segments, cell1); - segment_type segment = cell1.contains_segment() ? segments[cell1.source_index()] : segments[cell2.source_index()]; - coordinate_type dx = high(segment).x() - low(segment).x(); - coordinate_type dy = high(segment).y() - low(segment).y(); - if ((low(segment) == origin) ^ cell1.contains_point()) { - direction.x(dy); - direction.y(-dx); - } else { - direction.x(-dy); - direction.y(dx); - } - } - coordinate_type koef = bbox_max_size / (std::max)(fabs(direction.x()), fabs(direction.y())); - if (edge.vertex0() == NULL) { - clipped_edge->push_back(point_type( - origin.x() - direction.x() * koef, - origin.y() - direction.y() * koef)); - } else { - clipped_edge->push_back( - point_type(edge.vertex0()->x(), edge.vertex0()->y())); - } - if (edge.vertex1() == NULL) { - clipped_edge->push_back(point_type( - origin.x() + direction.x() * koef, - origin.y() + direction.y() * koef)); - } else { - clipped_edge->push_back( - point_type(edge.vertex1()->x(), edge.vertex1()->y())); - } - } - - inline void sample_curved_edge(const std::vector &segments, const edge_type& edge, std::vector &sampled_edge, coordinate_type max_dist) - { - point_type point = edge.cell()->contains_point() ? - retrieve_point(segments, *edge.cell()) : - retrieve_point(segments, *edge.twin()->cell()); - segment_type segment = edge.cell()->contains_point() ? - segments[edge.twin()->cell()->source_index()] : - segments[edge.cell()->source_index()]; - ::boost::polygon::voronoi_visual_utils::discretize(point, segment, max_dist, &sampled_edge); - } - -} /* namespace Internal */ } // namespace Voronoi - -static inline void dump_voronoi_to_svg(const Lines &lines, /* const */ voronoi_diagram &vd, const ThickPolylines *polylines, const char *path) -{ - const double scale = 0.2; - const std::string inputSegmentPointColor = "lightseagreen"; - const coord_t inputSegmentPointRadius = coord_t(0.09 * scale / SCALING_FACTOR); - const std::string inputSegmentColor = "lightseagreen"; - const coord_t inputSegmentLineWidth = coord_t(0.03 * scale / SCALING_FACTOR); - - const std::string voronoiPointColor = "black"; - const coord_t voronoiPointRadius = coord_t(0.06 * scale / SCALING_FACTOR); - const std::string voronoiLineColorPrimary = "black"; - const std::string voronoiLineColorSecondary = "green"; - const std::string voronoiArcColor = "red"; - const coord_t voronoiLineWidth = coord_t(0.02 * scale / SCALING_FACTOR); - - const bool internalEdgesOnly = false; - const bool primaryEdgesOnly = false; - - BoundingBox bbox = BoundingBox(lines); - bbox.min.x -= coord_t(1. / SCALING_FACTOR); - bbox.min.y -= coord_t(1. / SCALING_FACTOR); - bbox.max.x += coord_t(1. / SCALING_FACTOR); - bbox.max.y += coord_t(1. / SCALING_FACTOR); - - ::Slic3r::SVG svg(path, bbox); - - if (polylines != NULL) - svg.draw(*polylines, "lime", "lime", voronoiLineWidth); - -// bbox.scale(1.2); - // For clipping of half-lines to some reasonable value. - // The line will then be clipped by the SVG viewer anyway. - const double bbox_dim_max = double(bbox.max.x - bbox.min.x) + double(bbox.max.y - bbox.min.y); - // For the discretization of the Voronoi parabolic segments. - const double discretization_step = 0.0005 * bbox_dim_max; - - // Make a copy of the input segments with the double type. - std::vector segments; - for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++ it) - segments.push_back(Voronoi::Internal::segment_type( - Voronoi::Internal::point_type(double(it->a.x), double(it->a.y)), - Voronoi::Internal::point_type(double(it->b.x), double(it->b.y)))); - - // Color exterior edges. - for (voronoi_diagram::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it) - if (!it->is_finite()) - Voronoi::Internal::color_exterior(&(*it)); - - // Draw the end points of the input polygon. - for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) { - svg.draw(it->a, inputSegmentPointColor, inputSegmentPointRadius); - svg.draw(it->b, inputSegmentPointColor, inputSegmentPointRadius); - } - // Draw the input polygon. - for (Lines::const_iterator it = lines.begin(); it != lines.end(); ++it) - svg.draw(Line(Point(coord_t(it->a.x), coord_t(it->a.y)), Point(coord_t(it->b.x), coord_t(it->b.y))), inputSegmentColor, inputSegmentLineWidth); - -#if 1 - // Draw voronoi vertices. - for (voronoi_diagram::const_vertex_iterator it = vd.vertices().begin(); it != vd.vertices().end(); ++it) - if (! internalEdgesOnly || it->color() != Voronoi::Internal::EXTERNAL_COLOR) - svg.draw(Point(coord_t(it->x()), coord_t(it->y())), voronoiPointColor, voronoiPointRadius); - - for (voronoi_diagram::const_edge_iterator it = vd.edges().begin(); it != vd.edges().end(); ++it) { - if (primaryEdgesOnly && !it->is_primary()) - continue; - if (internalEdgesOnly && (it->color() == Voronoi::Internal::EXTERNAL_COLOR)) - continue; - std::vector samples; - std::string color = voronoiLineColorPrimary; - if (!it->is_finite()) { - Voronoi::Internal::clip_infinite_edge(segments, *it, bbox_dim_max, &samples); - if (! it->is_primary()) - color = voronoiLineColorSecondary; - } else { - // Store both points of the segment into samples. sample_curved_edge will split the initial line - // until the discretization_step is reached. - samples.push_back(Voronoi::Internal::point_type(it->vertex0()->x(), it->vertex0()->y())); - samples.push_back(Voronoi::Internal::point_type(it->vertex1()->x(), it->vertex1()->y())); - if (it->is_curved()) { - Voronoi::Internal::sample_curved_edge(segments, *it, samples, discretization_step); - color = voronoiArcColor; - } else if (! it->is_primary()) - color = voronoiLineColorSecondary; - } - for (std::size_t i = 0; i + 1 < samples.size(); ++i) - svg.draw(Line(Point(coord_t(samples[i].x()), coord_t(samples[i].y())), Point(coord_t(samples[i+1].x()), coord_t(samples[i+1].y()))), color, voronoiLineWidth); - } -#endif - - if (polylines != NULL) - svg.draw(*polylines, "blue", voronoiLineWidth); - - svg.Close(); -} -#endif /* SLIC3R_DEBUG */ - -// Euclidian distance of two boost::polygon points. -template -T dist(const boost::polygon::point_data &p1,const boost::polygon::point_data &p2) -{ - T dx = p2.x() - p1.x(); - T dy = p2.y() - p1.y(); - return sqrt(dx*dx+dy*dy); -} - -// Find a foot point of "px" on a segment "seg". -template -inline point_type project_point_to_segment(segment_type &seg, point_type &px) -{ - typedef typename point_type::coordinate_type T; - const point_type &p0 = low(seg); - const point_type &p1 = high(seg); - const point_type dir(p1.x()-p0.x(), p1.y()-p0.y()); - const point_type dproj(px.x()-p0.x(), px.y()-p0.y()); - const T t = (dir.x()*dproj.x() + dir.y()*dproj.y()) / (dir.x()*dir.x() + dir.y()*dir.y()); - assert(t >= T(-1e-6) && t <= T(1. + 1e-6)); - return point_type(p0.x() + t*dir.x(), p0.y() + t*dir.y()); -} - -template -inline const typename VD::point_type retrieve_cell_point(const typename VD::cell_type& cell, const SEGMENTS &segments) -{ - assert(cell.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT || cell.source_category() == SOURCE_CATEGORY_SEGMENT_END_POINT); - return (cell.source_category() == SOURCE_CATEGORY_SEGMENT_START_POINT) ? low(segments[cell.source_index()]) : high(segments[cell.source_index()]); -} - -template -inline std::pair -measure_edge_thickness(const VD &vd, const typename VD::edge_type& edge, const SEGMENTS &segments) -{ - typedef typename VD::coord_type T; - const typename VD::point_type pa(edge.vertex0()->x(), edge.vertex0()->y()); - const typename VD::point_type pb(edge.vertex1()->x(), edge.vertex1()->y()); - const typename VD::cell_type &cell1 = *edge.cell(); - const typename VD::cell_type &cell2 = *edge.twin()->cell(); - if (cell1.contains_segment()) { - if (cell2.contains_segment()) { - // Both cells contain a linear segment, the left / right cells are symmetric. - // Project pa, pb to the left segment. - const typename VD::segment_type segment1 = segments[cell1.source_index()]; - const typename VD::point_type p1a = project_point_to_segment(segment1, pa); - const typename VD::point_type p1b = project_point_to_segment(segment1, pb); - return std::pair(T(2.)*dist(pa, p1a), T(2.)*dist(pb, p1b)); - } else { - // 1st cell contains a linear segment, 2nd cell contains a point. - // The medial axis between the cells is a parabolic arc. - // Project pa, pb to the left segment. - const typename VD::point_type p2 = retrieve_cell_point(cell2, segments); - return std::pair(T(2.)*dist(pa, p2), T(2.)*dist(pb, p2)); - } - } else if (cell2.contains_segment()) { - // 1st cell contains a point, 2nd cell contains a linear segment. - // The medial axis between the cells is a parabolic arc. - const typename VD::point_type p1 = retrieve_cell_point(cell1, segments); - return std::pair(T(2.)*dist(pa, p1), T(2.)*dist(pb, p1)); - } else { - // Both cells contain a point. The left / right regions are triangular and symmetric. - const typename VD::point_type p1 = retrieve_cell_point(cell1, segments); - return std::pair(T(2.)*dist(pa, p1), T(2.)*dist(pb, p1)); - } -} - -// Converts the Line instances of Lines vector to VD::segment_type. -template -class Lines2VDSegments -{ -public: - Lines2VDSegments(const Lines &alines) : lines(alines) {} - typename VD::segment_type operator[](size_t idx) const { - return typename VD::segment_type( - typename VD::point_type(typename VD::coord_type(lines[idx].a.x), typename VD::coord_type(lines[idx].a.y)), - typename VD::point_type(typename VD::coord_type(lines[idx].b.x), typename VD::coord_type(lines[idx].b.y))); - } -private: - const Lines &lines; -}; - void MedialAxis::build(ThickPolylines* polylines) { diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp index ccdfb5b2e..4cb2be1a3 100644 --- a/xs/src/libslic3r/Geometry.hpp +++ b/xs/src/libslic3r/Geometry.hpp @@ -24,20 +24,12 @@ double rad2deg(double angle); double rad2deg_dir(double angle); double deg2rad(double angle); -class ArrangeItem { - public: - Pointf pos; - size_t index_x, index_y; - coordf_t dist; -}; -class ArrangeItemIndex { - public: - coordf_t index; - ArrangeItem item; - ArrangeItemIndex(coordf_t _index, ArrangeItem _item) : index(_index), item(_item) {}; -}; double linint(double value, double oldmin, double oldmax, double newmin, double newmax); -Pointfs arrange(size_t total_parts, Pointf part, coordf_t dist, const BoundingBoxf* bb); +bool arrange( + // input + size_t num_parts, const Pointf &part_size, coordf_t gap, const BoundingBoxf* bed_bounding_box, + // output + Pointfs &positions); class MedialAxis { public: diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 9175995bf..585e657dd 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -224,21 +224,22 @@ Model::raw_mesh() const return mesh; } -Pointfs -Model::_arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb) const +bool +Model::_arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb, Pointfs &out) const { // we supply unscaled data to arrange() return Slic3r::Geometry::arrange( sizes.size(), // number of parts BoundingBoxf(sizes).max, // width and height of a single cell dist, // distance between cells - bb // bounding box of the area to fill + bb, // bounding box of the area to fill + out // output positions ); } /* arrange objects preserving their instance count but altering their instance positions */ -void +bool Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) { // get the (transformed) size of each instance so that we take @@ -250,7 +251,9 @@ Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) } } - Pointfs positions = this->_arrange(instance_sizes, dist, bb); + Pointfs positions; + if (! this->_arrange(instance_sizes, dist, bb, positions)) + return false; for (ModelObjectPtrs::const_iterator o = this->objects.begin(); o != this->objects.end(); ++o) { for (ModelInstancePtrs::const_iterator i = (*o)->instances.begin(); i != (*o)->instances.end(); ++i) { @@ -259,6 +262,7 @@ Model::arrange_objects(coordf_t dist, const BoundingBoxf* bb) } (*o)->invalidate_bounding_box(); } + return true; } /* duplicate the entire model preserving instance relative positions */ @@ -266,7 +270,9 @@ void Model::duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb) { Pointfs model_sizes(copies_num-1, this->bounding_box().size()); - Pointfs positions = this->_arrange(model_sizes, dist, bb); + Pointfs positions; + if (! this->_arrange(model_sizes, dist, bb, positions)) + CONFESS("Cannot duplicate part as the resulting objects would not fit on the print bed.\n"); // note that this will leave the object count unaltered diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index c0a7d78ea..e714907bd 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -66,8 +66,9 @@ class Model void translate(coordf_t x, coordf_t y, coordf_t z); TriangleMesh mesh() const; TriangleMesh raw_mesh() const; - Pointfs _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb = NULL) const; - void arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL); + bool _arrange(const Pointfs &sizes, coordf_t dist, const BoundingBoxf* bb, Pointfs &out) const; + bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL); + // Croaks if the duplicated objects do not fit the print bed. void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL); void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL); void duplicate_objects_grid(size_t x, size_t y, coordf_t dist); diff --git a/xs/xsp/Geometry.xsp b/xs/xsp/Geometry.xsp index de911ccb0..37dad4aee 100644 --- a/xs/xsp/Geometry.xsp +++ b/xs/xsp/Geometry.xsp @@ -9,7 +9,12 @@ %package{Slic3r::Geometry}; Pointfs arrange(size_t total_parts, Pointf* part, coordf_t dist, BoundingBoxf* bb = NULL) - %code{% RETVAL = Slic3r::Geometry::arrange(total_parts, *part, dist, bb); %}; + %code{% + Pointfs points; + if (! Slic3r::Geometry::arrange(total_parts, *part, dist, bb, points)) + CONFESS("%zu parts won't fit in your print area!\n", total_parts); + RETVAL = points; + %}; %{ diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 8bfd10123..187b57e68 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -66,8 +66,7 @@ ModelObjectPtrs* objects() %code%{ RETVAL = &THIS->objects; %}; - Pointfs _arrange(Pointfs sizes, double dist, BoundingBoxf* bb = NULL); - void arrange_objects(double dist, BoundingBoxf* bb = NULL); + bool arrange_objects(double dist, BoundingBoxf* bb = NULL); void duplicate(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL); void duplicate_objects(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL); void duplicate_objects_grid(unsigned int x, unsigned int y, double dist); From 12961d52126675c0bcb07f67ba706eb0052584da Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 4 Nov 2016 16:49:08 +0100 Subject: [PATCH 076/225] Fix of https://github.com/prusa3d/Slic3r/issues/13 The BoundingBox::defined flag was not set in constructor, if initialized from min/max corners. Conflicts: xs/src/libslic3r/BoundingBox.hpp --- xs/src/libslic3r/BoundingBox.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp index 71d0fa0b6..e77e7bdc9 100644 --- a/xs/src/libslic3r/BoundingBox.hpp +++ b/xs/src/libslic3r/BoundingBox.hpp @@ -21,7 +21,8 @@ class BoundingBoxBase bool defined; BoundingBoxBase() : defined(false) {}; - BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) : min(pmin), max(pmax) {} + BoundingBoxBase(const PointClass &pmin, const PointClass &pmax) : + min(pmin), max(pmax), defined(pmin.x < pmax.x && pmin.y < pmax.y) {} BoundingBoxBase(const std::vector &points); void merge(const PointClass &point); void merge(const std::vector &points); @@ -39,7 +40,9 @@ class BoundingBox3Base : public BoundingBoxBase { public: BoundingBox3Base() : BoundingBoxBase() {}; - BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) : BoundingBoxBase(pmin, pmax) {} + BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) : + BoundingBoxBase(pmin, pmax) + { if (pmin.z >= pmax.z) defined = false; } BoundingBox3Base(const std::vector &points); void merge(const PointClass &point); void merge(const std::vector &points); @@ -100,6 +103,4 @@ inline bool operator!=(const BoundingBoxBase &bb1, const BoundingBoxBase return !(bb1 == bb2); } -} - #endif From cc7d2c1aa2bdc8a2b6975099ddc025bb09f2a465 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 4 Nov 2016 17:09:01 +0100 Subject: [PATCH 077/225] Let's hope to fix compilation on gcc. --- xs/src/libslic3r/BoundingBox.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp index e77e7bdc9..cb1dbb88e 100644 --- a/xs/src/libslic3r/BoundingBox.hpp +++ b/xs/src/libslic3r/BoundingBox.hpp @@ -42,7 +42,7 @@ class BoundingBox3Base : public BoundingBoxBase BoundingBox3Base() : BoundingBoxBase() {}; BoundingBox3Base(const PointClass &pmin, const PointClass &pmax) : BoundingBoxBase(pmin, pmax) - { if (pmin.z >= pmax.z) defined = false; } + { if (pmin.z >= pmax.z) BoundingBoxBase::defined = false; } BoundingBox3Base(const std::vector &points); void merge(const PointClass &point); void merge(const std::vector &points); From 832773e3109b0ec61a4e0999783a893ba9dca822 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 4 Nov 2016 23:23:01 +0100 Subject: [PATCH 078/225] Perl OpenGL 0.7 package causes crashes on many platforms. Constrain compilation to Perl OpenGL version lower than 0.7. https://github.com/alexrj/Slic3r/pull/3552/commits/897ca52654dd380d07de9f87cb36043cdc59db87 --- Build.PL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Build.PL b/Build.PL index 68a9af16e..d402e8a3a 100644 --- a/Build.PL +++ b/Build.PL @@ -44,7 +44,7 @@ if ($gui) { %recommends = qw( Growl::GNTP 0.15 Wx::GLCanvas 0 - OpenGL 0 + OpenGL <0.70 LWP::UserAgent 0 Net::Bonjour 0 ); From a1fd0fe60a75357a2aa0440b69be1fdbfc56bf92 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sat, 5 Nov 2016 02:23:46 +0100 Subject: [PATCH 079/225] Refactored Print::validate() method to not throw an exception, but to return a string with an error message instead. This was necessary to avoid a hang-up on some Strawberry Perl distributions, when a perl "croak" function is called after a C++ exception is caught. Conflicts: xs/src/libslic3r/Print.cpp --- lib/Slic3r/Print.pm | 9 +++++++++ xs/src/libslic3r/Print.cpp | 18 ++++++++++-------- xs/src/libslic3r/Print.hpp | 10 +++------- xs/xsp/Print.xsp | 10 ++-------- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index d349eb1a0..187ee84e5 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -450,4 +450,13 @@ sub expanded_output_filepath { return $self->placeholder_parser->process($path); } +# Wrapper around the C++ Slic3r::Print::validate() +# to produce a Perl exception without a hang-up on some Strawberry perls. +sub validate +{ + my $self = shift; + my $err = $self->_validate; + die $err . "\n" if (defined($err) && $err ne ''); +} + 1; diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 42827e6e9..c97ca30f0 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -576,7 +576,7 @@ bool Print::has_skirt() const || this->has_infinite_skirt(); } -void +std::string Print::validate() const { if (this->config.complete_objects) { @@ -614,7 +614,7 @@ Print::validate() const Polygon p = convex_hull; p.translate(*copy); if (!intersection(a, p).empty()) - throw PrintValidationException("Some objects are too close; your extruder will collide with them."); + return "Some objects are too close; your extruder will collide with them."; a = union_(a, p); } @@ -633,7 +633,7 @@ Print::validate() const // it will be printed as last one so its height doesn't matter object_height.pop_back(); if (!object_height.empty() && object_height.back() > scale_(this->config.extruder_clearance_height.value)) - throw PrintValidationException("Some objects are too tall and cannot be printed without extruder collisions."); + return "Some objects are too tall and cannot be printed without extruder collisions."; } } // end if (this->config.complete_objects) @@ -641,16 +641,16 @@ Print::validate() const size_t total_copies_count = 0; FOREACH_OBJECT(this, i_object) total_copies_count += (*i_object)->copies().size(); if (total_copies_count > 1) - throw PrintValidationException("The Spiral Vase option can only be used when printing a single object."); + return "The Spiral Vase option can only be used when printing a single object."; if (this->regions.size() > 1) - throw PrintValidationException("The Spiral Vase option can only be used when printing single material objects."); + return "The Spiral Vase option can only be used when printing single material objects."; } { // find the smallest nozzle diameter std::set extruders = this->extruders(); if (extruders.empty()) - throw PrintValidationException("The supplied settings will cause an empty print."); + return "The supplied settings will cause an empty print."; std::set nozzle_diameters; for (std::set::iterator it = extruders.begin(); it != extruders.end(); ++it) @@ -674,13 +674,15 @@ Print::validate() const first_layer_min_nozzle_diameter = min_nozzle_diameter; } if (first_layer_height > first_layer_min_nozzle_diameter) - throw PrintValidationException("First layer height can't be greater than nozzle diameter"); + return "First layer height can't be greater than nozzle diameter"; // validate layer_height if (object->config.layer_height.value > min_nozzle_diameter) - throw PrintValidationException("Layer height can't be greater than nozzle diameter"); + return "Layer height can't be greater than nozzle diameter"; } } + + return std::string(); } // the bounding box of objects placed in copies position diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index a2a5ba7b4..56ff11fe5 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -4,7 +4,7 @@ #include "libslic3r.h" #include #include -#include +#include #include "BoundingBox.hpp" #include "Flow.hpp" #include "PrintConfig.hpp" @@ -29,11 +29,6 @@ enum PrintObjectStep { posInfill, posSupportMaterial, }; -class PrintValidationException : public std::runtime_error { - public: - PrintValidationException(const std::string &error) : std::runtime_error(error) {}; -}; - // To be instantiated over PrintStep or PrintObjectStep enums. template class PrintState @@ -196,7 +191,8 @@ class Print bool apply_config(DynamicPrintConfig config); bool has_infinite_skirt() const; bool has_skirt() const; - void validate() const; + // Returns an empty string if valid, otherwise returns an error message. + std::string validate() const; BoundingBox bounding_box() const; BoundingBox total_bounding_box() const; double skirt_first_layer_height() const; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 27d970a5b..9168e9d85 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -218,14 +218,8 @@ _constant() %code%{ RETVAL = THIS->apply_config(*config); %}; bool has_infinite_skirt(); bool has_skirt(); - void validate() - %code%{ - try { - THIS->validate(); - } catch (PrintValidationException &e) { - croak("%s\n", e.what()); - } - %}; + std::string _validate() + %code%{ RETVAL = THIS->validate(); %}; Clone bounding_box(); Clone total_bounding_box(); double skirt_first_layer_height(); From 0c63bc0f83cb36a9b2bd17bf8cfde23dbf6b8b59 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 24 Nov 2016 17:00:09 +0100 Subject: [PATCH 080/225] Typo --- xs/src/libslic3r/BoundingBox.hpp | 2 ++ xs/src/libslic3r/Config.hpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/BoundingBox.hpp b/xs/src/libslic3r/BoundingBox.hpp index cb1dbb88e..2b5120110 100644 --- a/xs/src/libslic3r/BoundingBox.hpp +++ b/xs/src/libslic3r/BoundingBox.hpp @@ -103,4 +103,6 @@ inline bool operator!=(const BoundingBoxBase &bb1, const BoundingBoxBase return !(bb1 == bb2); } +} + #endif diff --git a/xs/src/libslic3r/Config.hpp b/xs/src/libslic3r/Config.hpp index 41923a6a5..ad01f1644 100644 --- a/xs/src/libslic3r/Config.hpp +++ b/xs/src/libslic3r/Config.hpp @@ -224,7 +224,7 @@ class ConfigOptionString : public ConfigOptionSingle std::string serialize() const { return escape_string_cstyle(this->value); - } + }; bool deserialize(std::string str, bool append = false) { return unescape_string_cstyle(str, this->value); From 9ed127274e1fc70c126ff1f800fef1ae641fa123 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Sat, 12 Nov 2016 19:04:40 +0100 Subject: [PATCH 081/225] Fixes https://github.com/prusa3d/Slic3r/issues/32#issuecomment-260135542 Reverts an inadverent bug introduced in https://github.com/prusa3d/Slic3r/commit/4460b5ce50c7160cd922972a190c27bf4381ffbd Conflicts: xs/src/libslic3r/PerimeterGenerator.cpp --- xs/src/libslic3r/PerimeterGenerator.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index 574dcebe2..89d0f06c5 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -121,12 +121,20 @@ PerimeterGenerator::process() coord_t distance = (i == 1) ? ext_pspacing2 : pspacing; if (this->config->thin_walls) { + // This path will ensure, that the perimeters do not overfill, as in + // prusa3d/Slic3r GH #32, but with the cost of rounding the perimeters + // excessively, creating gaps, which then need to be filled in by the not very + // reliable gap fill algorithm. + // Also the offset2(perimeter, -x, x) may sometimes lead to a perimeter, which is larger than + // the original. offsets = offset2( last, -(distance + min_spacing/2 - 1), +(min_spacing/2 - 1) ); } else { + // If "detect thin walls" is not enabled, this paths will be entered, which + // leads to overflows, as in prusa3d/Slic3r GH #32 offsets = offset( last, -distance From 497b7fb6c4abf0eccb5e233d6e09e17b61ece88d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 15 Nov 2016 17:22:00 +0100 Subject: [PATCH 082/225] Fixed a 64bit compatiblity in admesh, fixed a typo in TriangleMesh::swap() Conflicts: xs/src/libslic3r/TriangleMesh.cpp --- xs/src/admesh/stl.h | 10 +++++++++- xs/src/libslic3r/TriangleMesh.cpp | 6 +++--- xs/src/libslic3r/TriangleMesh.hpp | 2 +- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/xs/src/admesh/stl.h b/xs/src/admesh/stl.h index b2be5830e..449e2b5aa 100644 --- a/xs/src/admesh/stl.h +++ b/xs/src/admesh/stl.h @@ -24,6 +24,7 @@ #define __admesh_stl__ #include +#include #ifdef __cplusplus extern "C" { @@ -33,11 +34,15 @@ extern "C" { #define STL_MIN(A,B) ((A)<(B)? (A):(B)) #define ABS(X) ((X) < 0 ? -(X) : (X)) +// Size of the binary STL header, free form. #define LABEL_SIZE 80 +// Binary STL, length of the "number of faces" counter. #define NUM_FACET_SIZE 4 +// Binary STL, sizeof header + number of faces. #define HEADER_SIZE 84 #define STL_MIN_FILE_SIZE 284 #define ASCII_LINES_PER_FACET 7 +// Comparing an edge by memcmp, 2x3x4 bytes = 24 #define SIZEOF_EDGE_SORT 24 typedef struct { @@ -70,14 +75,17 @@ typedef struct { } stl_edge; typedef struct stl_hash_edge { - unsigned key[6]; + // Key of a hash edge: 2x binary copy of a floating point vertex. + uint32_t key[6]; int facet_number; int which_edge; struct stl_hash_edge *next; } stl_hash_edge; typedef struct { + // Index of a neighbor facet. int neighbor[3]; + // Index of an opposite vertex at the neighbor face. char which_vertex_not[3]; } stl_neighbors; diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 437bea44c..4a569ed40 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -55,10 +55,10 @@ TriangleMesh& TriangleMesh::operator= (TriangleMesh other) } void -TriangleMesh::swap(TriangleMesh &first, TriangleMesh &second) +TriangleMesh::swap(TriangleMesh &other) { - std::swap(first.repaired, second.repaired); - std::swap(first.stl, second.stl); + std::swap(this->stl, other.stl); + std::swap(this->repaired, other.repaired); } TriangleMesh::~TriangleMesh() { diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 196ef17c0..b10e3fae6 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -22,7 +22,7 @@ class TriangleMesh TriangleMesh(); TriangleMesh(const TriangleMesh &other); TriangleMesh& operator= (TriangleMesh other); - void swap(TriangleMesh &first, TriangleMesh &second); + void swap(TriangleMesh &other); ~TriangleMesh(); void ReadSTLFile(const std::string &input_file); void write_ascii(const std::string &output_file); From 9367eb6db7f9b8b0e32ed18fac636bbd217d8438 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 16 Nov 2016 09:24:27 +0100 Subject: [PATCH 083/225] Increased the gimbal lock threshold to 170 degrees from 150 degrees. This makes it possible to look at the bottom of an object. Conflicts: lib/Slic3r/GUI/3DScene.pm --- lib/Slic3r/GUI/3DScene.pm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index ca662dc88..61d45f1f4 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -63,6 +63,8 @@ use constant VIEW_BOTTOM => [0.0,180.0]; use constant VIEW_FRONT => [0.0,90.0]; use constant VIEW_REAR => [180.0,90.0]; +use constant GIMBALL_LOCK_THETA_MAX => 170; + # make OpenGL::Array thread-safe { no warnings 'redefine'; @@ -252,7 +254,7 @@ sub mouse_event { if (TURNTABLE_MODE) { $self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE); $self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #- - $self->_stheta(150) if $self->_stheta > 150; + $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX; $self->_stheta(0) if $self->_stheta < 0; } else { my $size = $self->GetClientSize; @@ -353,7 +355,7 @@ sub select_view { $self->_sphi($dirvec->[0]); $self->_stheta($dirvec->[1]); # Avoid gimball lock. - $self->_stheta(150) if $self->_stheta > 150; + $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX; $self->_stheta(0) if $self->_stheta < 0; # View everything. $self->zoom_to_volumes; From 10c0be2e9989c6d319649181f08f76dc11f3ddb0 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 16 Nov 2016 09:31:18 +0100 Subject: [PATCH 084/225] Positive and negative zeros are possible in the floats, which are considered equal by the FP unit. When using a memcmp on raw floats, those numbers report to be different. Unify all +0 and -0 to +0 to make the floats equal under memcmp. --- xs/src/admesh/stlinit.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/xs/src/admesh/stlinit.c b/xs/src/admesh/stlinit.c index 58f6947c7..90e9a6126 100644 --- a/xs/src/admesh/stlinit.c +++ b/xs/src/admesh/stlinit.c @@ -284,6 +284,20 @@ stl_read(stl_file *stl, int first_facet, int first) { stl->error = 1; return; } +#if 0 + // Report close to zero vertex coordinates. Due to the nature of the floating point numbers, + // close to zero values may be represented with singificantly higher precision than the rest of the vertices. + // It may be worth to round these numbers to zero during loading to reduce the number of errors reported + // during the STL import. + for (size_t j = 0; j < 3; ++ j) { + if (facet.vertex[j].x > -1e-12f && facet.vertex[j].x < 1e-12f) + printf("stl_read: facet %d.x = %e\r\n", j, facet.vertex[j].x); + if (facet.vertex[j].y > -1e-12f && facet.vertex[j].y < 1e-12f) + printf("stl_read: facet %d.y = %e\r\n", j, facet.vertex[j].y); + if (facet.vertex[j].z > -1e-12f && facet.vertex[j].z < 1e-12f) + printf("stl_read: facet %d.z = %e\r\n", j, facet.vertex[j].z); + } +#endif } else /* Read a single facet from an ASCII .STL file */ { @@ -304,6 +318,29 @@ stl_read(stl_file *stl, int first_facet, int first) { return; } } + +#if 1 + { + // Positive and negative zeros are possible in the floats, which are considered equal by the FP unit. + // When using a memcmp on raw floats, those numbers report to be different. + // Unify all +0 and -0 to +0 to make the floats equal under memcmp. + uint32_t *f = (uint32_t*)&facet; + for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats + if (*f == 0x80000000) + // Negative zero, switch to positive zero. + *f = 0; + } +#else + { + // Due to the nature of the floating point numbers, close to zero values may be represented with singificantly higher precision + // than the rest of the vertices. Round them to zero. + float *f = (float*)&facet; + for (int j = 0; j < 12; ++ j, ++ f) // 3x vertex + normal: 4x3 = 12 floats + if (*f > -1e-12f && *f < 1e-12f) + // Negative zero, switch to positive zero. + *f = 0; + } +#endif /* Write the facet into memory. */ stl->facet_start[i] = facet; From 96ef2692d767e40497de25a8a988bcac3d220525 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 16 Nov 2016 10:33:23 +0100 Subject: [PATCH 085/225] Improvements of admesh robustness when loading and fixing STLs. https://github.com/prusa3d/Slic3r/issues/33 --- xs/src/admesh/connect.c | 159 +++++++++++++++++++--------------------- xs/src/admesh/stl.h | 3 + xs/src/admesh/stlinit.c | 29 ++++---- 3 files changed, 94 insertions(+), 97 deletions(-) diff --git a/xs/src/admesh/connect.c b/xs/src/admesh/connect.c index 6ce775f9f..56ebfa144 100644 --- a/xs/src/admesh/connect.c +++ b/xs/src/admesh/connect.c @@ -106,53 +106,42 @@ stl_check_facets_exact(stl_file *stl) { } } stl_free_edges(stl); + +#if 0 + printf("Number of faces: %d, number of manifold edges: %d, number of connected edges: %d, number of unconnected edges: %d\r\n", + stl->stats.number_of_facets, stl->stats.number_of_facets * 3, + stl->stats.connected_edges, stl->stats.number_of_facets * 3 - stl->stats.connected_edges); +#endif } static void stl_load_edge_exact(stl_file *stl, stl_hash_edge *edge, stl_vertex *a, stl_vertex *b) { - float diff_x; - float diff_y; - float diff_z; - float max_diff; - if (stl->error) return; - diff_x = ABS(a->x - b->x); - diff_y = ABS(a->y - b->y); - diff_z = ABS(a->z - b->z); - max_diff = STL_MAX(diff_x, diff_y); - max_diff = STL_MAX(diff_z, max_diff); - stl->stats.shortest_edge = STL_MIN(max_diff, stl->stats.shortest_edge); + { + float diff_x = ABS(a->x - b->x); + float diff_y = ABS(a->y - b->y); + float diff_z = ABS(a->z - b->z); + float max_diff = STL_MAX(diff_x, diff_y); + max_diff = STL_MAX(diff_z, max_diff); + stl->stats.shortest_edge = STL_MIN(max_diff, stl->stats.shortest_edge); + } - if(diff_x == max_diff) { - if(a->x > b->x) { - memcpy(&edge->key[0], a, sizeof(stl_vertex)); - memcpy(&edge->key[3], b, sizeof(stl_vertex)); - } else { - memcpy(&edge->key[0], b, sizeof(stl_vertex)); - memcpy(&edge->key[3], a, sizeof(stl_vertex)); - edge->which_edge += 3; /* this edge is loaded backwards */ - } - } else if(diff_y == max_diff) { - if(a->y > b->y) { - memcpy(&edge->key[0], a, sizeof(stl_vertex)); - memcpy(&edge->key[3], b, sizeof(stl_vertex)); - } else { - memcpy(&edge->key[0], b, sizeof(stl_vertex)); - memcpy(&edge->key[3], a, sizeof(stl_vertex)); - edge->which_edge += 3; /* this edge is loaded backwards */ - } + // Ensure identical vertex ordering of equal edges. + // This method is numerically robust. + if ((a->x != b->x) ? + (a->x < b->x) : + ((a->y != b->y) ? + (a->y < b->y) : + (a->z < b->z))) { + memcpy(&edge->key[0], a, sizeof(stl_vertex)); + memcpy(&edge->key[3], b, sizeof(stl_vertex)); } else { - if(a->z > b->z) { - memcpy(&edge->key[0], a, sizeof(stl_vertex)); - memcpy(&edge->key[3], b, sizeof(stl_vertex)); - } else { - memcpy(&edge->key[0], b, sizeof(stl_vertex)); - memcpy(&edge->key[3], a, sizeof(stl_vertex)); - edge->which_edge += 3; /* this edge is loaded backwards */ - } + memcpy(&edge->key[0], b, sizeof(stl_vertex)); + memcpy(&edge->key[3], a, sizeof(stl_vertex)); + edge->which_edge += 3; /* this edge is loaded backwards */ } } @@ -309,26 +298,17 @@ stl_check_facets_nearby(stl_file *stl, float tolerance) { static int stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, stl_vertex *a, stl_vertex *b, float tolerance) { - float diff_x; - float diff_y; - float diff_z; - float max_diff; - unsigned vertex1[3]; - unsigned vertex2[3]; - - - diff_x = ABS(a->x - b->x); - diff_y = ABS(a->y - b->y); - diff_z = ABS(a->z - b->z); - max_diff = STL_MAX(diff_x, diff_y); - max_diff = STL_MAX(diff_z, max_diff); - - vertex1[0] = (unsigned)((a->x - stl->stats.min.x) / tolerance); - vertex1[1] = (unsigned)((a->y - stl->stats.min.y) / tolerance); - vertex1[2] = (unsigned)((a->z - stl->stats.min.z) / tolerance); - vertex2[0] = (unsigned)((b->x - stl->stats.min.x) / tolerance); - vertex2[1] = (unsigned)((b->y - stl->stats.min.y) / tolerance); - vertex2[2] = (unsigned)((b->z - stl->stats.min.z) / tolerance); + // Index of a grid cell spaced by tolerance. + uint32_t vertex1[3] = { + (uint32_t)((a->x - stl->stats.min.x) / tolerance), + (uint32_t)((a->y - stl->stats.min.y) / tolerance), + (uint32_t)((a->z - stl->stats.min.z) / tolerance) + }; + uint32_t vertex2[3] = { + (uint32_t)((b->x - stl->stats.min.x) / tolerance), + (uint32_t)((b->y - stl->stats.min.y) / tolerance), + (uint32_t)((b->z - stl->stats.min.z) / tolerance) + }; if( (vertex1[0] == vertex2[0]) && (vertex1[1] == vertex2[1]) @@ -337,33 +317,19 @@ stl_load_edge_nearby(stl_file *stl, stl_hash_edge *edge, return 0; } - if(diff_x == max_diff) { - if(a->x > b->x) { - memcpy(&edge->key[0], vertex1, sizeof(stl_vertex)); - memcpy(&edge->key[3], vertex2, sizeof(stl_vertex)); - } else { - memcpy(&edge->key[0], vertex2, sizeof(stl_vertex)); - memcpy(&edge->key[3], vertex1, sizeof(stl_vertex)); - edge->which_edge += 3; /* this edge is loaded backwards */ - } - } else if(diff_y == max_diff) { - if(a->y > b->y) { - memcpy(&edge->key[0], vertex1, sizeof(stl_vertex)); - memcpy(&edge->key[3], vertex2, sizeof(stl_vertex)); - } else { - memcpy(&edge->key[0], vertex2, sizeof(stl_vertex)); - memcpy(&edge->key[3], vertex1, sizeof(stl_vertex)); - edge->which_edge += 3; /* this edge is loaded backwards */ - } + // Ensure identical vertex ordering of edges, which vertices land into equal grid cells. + // This method is numerically robust. + if ((vertex1[0] != vertex2[0]) ? + (vertex1[0] < vertex2[0]) : + ((vertex1[1] != vertex2[1]) ? + (vertex1[1] < vertex2[1]) : + (vertex1[2] < vertex2[2]))) { + memcpy(&edge->key[0], vertex1, sizeof(stl_vertex)); + memcpy(&edge->key[3], vertex2, sizeof(stl_vertex)); } else { - if(a->z > b->z) { - memcpy(&edge->key[0], vertex1, sizeof(stl_vertex)); - memcpy(&edge->key[3], vertex2, sizeof(stl_vertex)); - } else { - memcpy(&edge->key[0], vertex2, sizeof(stl_vertex)); - memcpy(&edge->key[3], vertex1, sizeof(stl_vertex)); - edge->which_edge += 3; /* this edge is loaded backwards */ - } + memcpy(&edge->key[0], vertex2, sizeof(stl_vertex)); + memcpy(&edge->key[3], vertex1, sizeof(stl_vertex)); + edge->which_edge += 3; /* this edge is loaded backwards */ } return 1; } @@ -564,6 +530,33 @@ stl_change_vertices(stl_file *stl, int facet_num, int vnot, next_edge = pivot_vertex; } } +#if 0 + if (stl->facet_start[facet_num].vertex[pivot_vertex].x == new_vertex.x && + stl->facet_start[facet_num].vertex[pivot_vertex].y == new_vertex.y && + stl->facet_start[facet_num].vertex[pivot_vertex].z == new_vertex.z) + printf("Changing vertex %f,%f,%f: Same !!!\r\n", + new_vertex.x, new_vertex.y, new_vertex.z); + else { + if (stl->facet_start[facet_num].vertex[pivot_vertex].x != new_vertex.x) + printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", + stl->facet_start[facet_num].vertex[pivot_vertex].x, + *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex].x), + new_vertex.x, + *reinterpret_cast(&new_vertex.x)); + if (stl->facet_start[facet_num].vertex[pivot_vertex].y != new_vertex.y) + printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", + stl->facet_start[facet_num].vertex[pivot_vertex].y, + *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex].y), + new_vertex.y, + *reinterpret_cast(&new_vertex.y)); + if (stl->facet_start[facet_num].vertex[pivot_vertex].z != new_vertex.z) + printf("Changing coordinate x, vertex %e (0x%08x) to %e(0x%08x)\r\n", + stl->facet_start[facet_num].vertex[pivot_vertex].z, + *reinterpret_cast(&stl->facet_start[facet_num].vertex[pivot_vertex].z), + new_vertex.z, + *reinterpret_cast(&new_vertex.z)); + } +#endif stl->facet_start[facet_num].vertex[pivot_vertex] = new_vertex; vnot = stl->neighbors_start[facet_num].which_vertex_not[next_edge]; facet_num = stl->neighbors_start[facet_num].neighbor[next_edge]; diff --git a/xs/src/admesh/stl.h b/xs/src/admesh/stl.h index 449e2b5aa..257fb275c 100644 --- a/xs/src/admesh/stl.h +++ b/xs/src/admesh/stl.h @@ -77,7 +77,10 @@ typedef struct { typedef struct stl_hash_edge { // Key of a hash edge: 2x binary copy of a floating point vertex. uint32_t key[6]; + // Index of a facet owning this edge. int facet_number; + // Index of this edge inside the facet with an index of facet_number. + // If this edge is stored backwards, which_edge is increased by 3. int which_edge; struct stl_hash_edge *next; } stl_hash_edge; diff --git a/xs/src/admesh/stlinit.c b/xs/src/admesh/stlinit.c index 90e9a6126..05c5beadb 100644 --- a/xs/src/admesh/stlinit.c +++ b/xs/src/admesh/stlinit.c @@ -284,20 +284,6 @@ stl_read(stl_file *stl, int first_facet, int first) { stl->error = 1; return; } -#if 0 - // Report close to zero vertex coordinates. Due to the nature of the floating point numbers, - // close to zero values may be represented with singificantly higher precision than the rest of the vertices. - // It may be worth to round these numbers to zero during loading to reduce the number of errors reported - // during the STL import. - for (size_t j = 0; j < 3; ++ j) { - if (facet.vertex[j].x > -1e-12f && facet.vertex[j].x < 1e-12f) - printf("stl_read: facet %d.x = %e\r\n", j, facet.vertex[j].x); - if (facet.vertex[j].y > -1e-12f && facet.vertex[j].y < 1e-12f) - printf("stl_read: facet %d.y = %e\r\n", j, facet.vertex[j].y); - if (facet.vertex[j].z > -1e-12f && facet.vertex[j].z < 1e-12f) - printf("stl_read: facet %d.z = %e\r\n", j, facet.vertex[j].z); - } -#endif } else /* Read a single facet from an ASCII .STL file */ { @@ -319,6 +305,21 @@ stl_read(stl_file *stl, int first_facet, int first) { } } +#if 0 + // Report close to zero vertex coordinates. Due to the nature of the floating point numbers, + // close to zero values may be represented with singificantly higher precision than the rest of the vertices. + // It may be worth to round these numbers to zero during loading to reduce the number of errors reported + // during the STL import. + for (size_t j = 0; j < 3; ++ j) { + if (facet.vertex[j].x > -1e-12f && facet.vertex[j].x < 1e-12f) + printf("stl_read: facet %d.x = %e\r\n", j, facet.vertex[j].x); + if (facet.vertex[j].y > -1e-12f && facet.vertex[j].y < 1e-12f) + printf("stl_read: facet %d.y = %e\r\n", j, facet.vertex[j].y); + if (facet.vertex[j].z > -1e-12f && facet.vertex[j].z < 1e-12f) + printf("stl_read: facet %d.z = %e\r\n", j, facet.vertex[j].z); + } +#endif + #if 1 { // Positive and negative zeros are possible in the floats, which are considered equal by the FP unit. From 4a91a7221f73b167f1ce54118155a189be02a824 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 16 Nov 2016 11:53:29 +0100 Subject: [PATCH 086/225] Optimization of Model bounding box routines (avoids copying the mesh), optimization of the admesh rotate function (also made numerically more robust). --- xs/src/admesh/util.c | 33 +++++++------- xs/src/libslic3r/Model.cpp | 88 +++++++++++++++++++++++++++++++++----- xs/src/libslic3r/Model.hpp | 4 ++ 3 files changed, 100 insertions(+), 25 deletions(-) diff --git a/xs/src/admesh/util.c b/xs/src/admesh/util.c index 15f405468..b2282d3bc 100644 --- a/xs/src/admesh/util.c +++ b/xs/src/admesh/util.c @@ -27,7 +27,7 @@ #include "stl.h" -static void stl_rotate(float *x, float *y, float angle); +static void stl_rotate(float *x, float *y, const double c, const double s); static float get_area(stl_facet *facet); static float get_volume(stl_file *stl); @@ -189,13 +189,16 @@ void stl_rotate_x(stl_file *stl, float angle) { int i; int j; + double radian_angle = (angle / 180.0) * M_PI; + double c = cos(radian_angle); + double s = sin(radian_angle); if (stl->error) return; for(i = 0; i < stl->stats.number_of_facets; i++) { for(j = 0; j < 3; j++) { stl_rotate(&stl->facet_start[i].vertex[j].y, - &stl->facet_start[i].vertex[j].z, angle); + &stl->facet_start[i].vertex[j].z, c, s); } } stl_get_size(stl); @@ -206,13 +209,16 @@ void stl_rotate_y(stl_file *stl, float angle) { int i; int j; + double radian_angle = (angle / 180.0) * M_PI; + double c = cos(radian_angle); + double s = sin(radian_angle); if (stl->error) return; for(i = 0; i < stl->stats.number_of_facets; i++) { for(j = 0; j < 3; j++) { stl_rotate(&stl->facet_start[i].vertex[j].z, - &stl->facet_start[i].vertex[j].x, angle); + &stl->facet_start[i].vertex[j].x, c, s); } } stl_get_size(stl); @@ -223,13 +229,16 @@ void stl_rotate_z(stl_file *stl, float angle) { int i; int j; + double radian_angle = (angle / 180.0) * M_PI; + double c = cos(radian_angle); + double s = sin(radian_angle); if (stl->error) return; for(i = 0; i < stl->stats.number_of_facets; i++) { for(j = 0; j < 3; j++) { stl_rotate(&stl->facet_start[i].vertex[j].x, - &stl->facet_start[i].vertex[j].y, angle); + &stl->facet_start[i].vertex[j].y, c, s); } } stl_get_size(stl); @@ -239,17 +248,11 @@ stl_rotate_z(stl_file *stl, float angle) { static void -stl_rotate(float *x, float *y, float angle) { - double r; - double theta; - double radian_angle; - - radian_angle = (angle / 180.0) * M_PI; - - r = sqrt((*x **x) + (*y **y)); - theta = atan2(*y, *x); - *x = r * cos(theta + radian_angle); - *y = r * sin(theta + radian_angle); +stl_rotate(float *x, float *y, const double c, const double s) { + double xold = *x; + double yold = *y; + *x = float(c * xold - s * yold); + *y = float(s * xold + c * yold); } extern void diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 585e657dd..b6328f9c9 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -489,7 +489,16 @@ ModelObject::invalidate_bounding_box() void ModelObject::update_bounding_box() { - this->_bounding_box = this->mesh().bounding_box(); +// this->_bounding_box = this->mesh().bounding_box(); + BoundingBoxf3 raw_bbox; + for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { + if ((*v)->modifier) continue; + raw_bbox.merge((*v)->mesh.bounding_box()); + } + BoundingBoxf3 bb; + for (ModelInstancePtrs::const_iterator i = this->instances.begin(); i != this->instances.end(); ++i) + bb.merge((*i)->transform_bounding_box(raw_bbox)); + this->_bounding_box = bb; this->_bounding_box_valid = true; } @@ -532,12 +541,8 @@ ModelObject::raw_bounding_box() const BoundingBoxf3 bb; for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { if ((*v)->modifier) continue; - TriangleMesh mesh = (*v)->mesh; - if (this->instances.empty()) CONFESS("Can't call raw_bounding_box() with no instances"); - this->instances.front()->transform_mesh(&mesh, true); - - bb.merge(mesh.bounding_box()); + bb.merge(this->instances.front()->transform_mesh_bounding_box(&(*v)->mesh, true)); } return bb; } @@ -546,9 +551,12 @@ ModelObject::raw_bounding_box() const BoundingBoxf3 ModelObject::instance_bounding_box(size_t instance_idx) const { - TriangleMesh mesh = this->raw_mesh(); - this->instances[instance_idx]->transform_mesh(&mesh); - return mesh.bounding_box(); + BoundingBoxf3 bb; + for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { + if ((*v)->modifier) continue; + bb.merge(this->instances[instance_idx]->transform_mesh_bounding_box(&(*v)->mesh, true)); + } + return bb; } void @@ -556,7 +564,10 @@ ModelObject::center_around_origin() { // calculate the displacements needed to // center this object around the origin - BoundingBoxf3 bb = this->raw_mesh().bounding_box(); + BoundingBoxf3 bb; + for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) + if (! (*v)->modifier) + bb.merge((*v)->mesh.bounding_box()); // first align to origin on XYZ Vectorf3 vector(-bb.min.x, -bb.min.y, -bb.min.z); @@ -890,6 +901,63 @@ ModelInstance::transform_mesh(TriangleMesh* mesh, bool dont_translate) const mesh->translate(this->offset.x, this->offset.y, 0); } +BoundingBoxf3 ModelInstance::transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate) const +{ + // rotate around mesh origin + double c = cos(this->rotation); + double s = sin(this->rotation); + BoundingBoxf3 bbox; + for (int i = 0; i < mesh->stl.stats.number_of_facets; ++ i) { + const stl_facet &facet = mesh->stl.facet_start[i]; + for (int j = 0; j < 3; ++ j) { + stl_vertex v = facet.vertex[j]; + double xold = v.x; + double yold = v.y; + v.x = float(c * xold - s * yold); + v.y = float(s * xold + c * yold); + v.x *= float(this->scaling_factor); + v.y *= float(this->scaling_factor); + v.z *= float(this->scaling_factor); + if (!dont_translate) { + v.x += this->offset.x; + v.y += this->offset.y; + } + bbox.merge(Pointf3(v.x, v.y, v.z)); + } + } + return bbox; +} + +BoundingBoxf3 ModelInstance::transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate) const +{ + // rotate around mesh origin + double c = cos(this->rotation); + double s = sin(this->rotation); + Pointf3 pts[4] = { + bbox.min, + bbox.max, + Pointf3(bbox.min.x, bbox.max.y, bbox.min.z), + Pointf3(bbox.max.x, bbox.min.y, bbox.max.z) + }; + BoundingBoxf3 out; + for (int i = 0; i < 4; ++ i) { + Pointf3 &v = pts[i]; + double xold = v.x; + double yold = v.y; + v.x = float(c * xold - s * yold); + v.y = float(s * xold + c * yold); + v.x *= this->scaling_factor; + v.y *= this->scaling_factor; + v.z *= this->scaling_factor; + if (!dont_translate) { + v.x += this->offset.x; + v.y += this->offset.y; + } + out.merge(v); + } + return out; +} + void ModelInstance::transform_polygon(Polygon* polygon) const { diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index e714907bd..8d93325f3 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -224,6 +224,10 @@ class ModelInstance // To be called on an external mesh void transform_mesh(TriangleMesh* mesh, bool dont_translate = false) const; + // Calculate a bounding box of a transformed mesh. To be called on an external mesh. + BoundingBoxf3 transform_mesh_bounding_box(const TriangleMesh* mesh, bool dont_translate = false) const; + // Transform an external bounding box. + BoundingBoxf3 transform_bounding_box(const BoundingBoxf3 &bbox, bool dont_translate = false) const; // To be called on an external polygon. It does not translate the polygon, only rotates and scales. void transform_polygon(Polygon* polygon) const; From 3a87ed9de92d623baaaa59747ce0805291ebe407 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 16 Nov 2016 13:14:42 +0100 Subject: [PATCH 087/225] Fixed compilation of XS modules by undefinig the "seed" macro redefined crazily by Perl. --- xs/src/xsinit.h | 1 + 1 file changed, 1 insertion(+) diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index 700319fb6..9475a9e27 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -26,6 +26,7 @@ #undef socketpair #undef recvfrom #undef sendto +#undef seed // these need to be included early for Win32 (listing it in Build.PL is not enough) #include From 731fa437baf72c14549436ed73f2dc91a191a92f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 16 Nov 2016 13:06:51 +0100 Subject: [PATCH 088/225] Fix for compilation on Strawberry Perl with C++11 enabled. --- xs/src/xsinit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index 9475a9e27..37424384b 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -41,11 +41,11 @@ extern "C" { #include "ppport.h" #undef do_open #undef do_close +#undef bind #ifdef _MSC_VER // Undef some of the macros set by Perl , which cause compilation errors on Win32 #undef send #undef connect - #undef bind #endif /* _MSC_VER */ } #endif From bd3df215607f87bb99c90d93f4a9afe5fb105683 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 16 Nov 2016 13:19:48 +0100 Subject: [PATCH 089/225] Undef seed macro because of crazy perl macro substitutions. --- xs/src/xsinit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index 37424384b..d414afc40 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -26,7 +26,6 @@ #undef socketpair #undef recvfrom #undef sendto -#undef seed // these need to be included early for Win32 (listing it in Build.PL is not enough) #include @@ -42,6 +41,7 @@ extern "C" { #undef do_open #undef do_close #undef bind +#undef seed #ifdef _MSC_VER // Undef some of the macros set by Perl , which cause compilation errors on Win32 #undef send From a20cc0a3a6e2e0810322116e008fb8d82c20d5c7 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 16 Nov 2016 22:09:00 +0100 Subject: [PATCH 090/225] Reduced some compiler warnings. Conflicts: xs/xsp/BoundingBox.xsp xs/xsp/Point.xsp --- xs/xsp/BoundingBox.xsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/xsp/BoundingBox.xsp b/xs/xsp/BoundingBox.xsp index 9da9173aa..ed648a795 100644 --- a/xs/xsp/BoundingBox.xsp +++ b/xs/xsp/BoundingBox.xsp @@ -26,7 +26,7 @@ long x_max() %code{% RETVAL = THIS->max.x; %}; long y_min() %code{% RETVAL = THIS->min.y; %}; long y_max() %code{% RETVAL = THIS->max.y; %}; - + %{ BoundingBox* From d88f05945d40e82fc887f9130f716355d534c690 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 17 Nov 2016 16:57:58 +0100 Subject: [PATCH 091/225] admesh: Fixed a problem in loading an STL when compiled with Visual Studio 2013. Added multiple compile time checks for data sizes and alignment. The library STL import is not big endian safe, so added a test for endianity, modified STL export to a faster little endian only. --- xs/src/admesh/stl.h | 17 +++++++++++ xs/src/admesh/stl_io.c | 65 ++--------------------------------------- xs/src/admesh/stlinit.c | 14 +++------ 3 files changed, 24 insertions(+), 72 deletions(-) diff --git a/xs/src/admesh/stl.h b/xs/src/admesh/stl.h index 257fb275c..d5ee6d130 100644 --- a/xs/src/admesh/stl.h +++ b/xs/src/admesh/stl.h @@ -25,6 +25,12 @@ #include #include +#include +#include + +#ifndef BOOST_LITTLE_ENDIAN +#error "admesh works correctly on little endian machines only!" +#endif #ifdef __cplusplus extern "C" { @@ -51,12 +57,16 @@ typedef struct { float z; } stl_vertex; +static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect"); + typedef struct { float x; float y; float z; } stl_normal; +static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect"); + typedef char stl_extra[2]; typedef struct { @@ -66,6 +76,11 @@ typedef struct { } stl_facet; #define SIZEOF_STL_FACET 50 +static_assert(offsetof(stl_facet, normal) == 0, "stl_facet.normal has correct offset"); +static_assert(offsetof(stl_facet, vertex) == 12, "stl_facet.vertex has correct offset"); +static_assert(offsetof(stl_facet, extra ) == 48, "stl_facet.extra has correct offset"); +static_assert(sizeof(stl_facet) >= SIZEOF_STL_FACET, "size of stl_facet incorrect"); + typedef enum {binary, ascii, inmemory} stl_type; typedef struct { @@ -85,6 +100,8 @@ typedef struct stl_hash_edge { struct stl_hash_edge *next; } stl_hash_edge; +static_assert(offsetof(stl_hash_edge, facet_number) == SIZEOF_EDGE_SORT, "size of stl_hash_edge.key incorrect"); + typedef struct { // Index of a neighbor facet. int neighbor[3]; diff --git a/xs/src/admesh/stl_io.c b/xs/src/admesh/stl_io.c index e015cee99..7d8e4eab8 100644 --- a/xs/src/admesh/stl_io.c +++ b/xs/src/admesh/stl_io.c @@ -203,63 +203,6 @@ stl_print_neighbors(stl_file *stl, char *file) { fclose(fp); } -void -stl_put_little_int(FILE *fp, int value_in) { - int new_value; - union { - int int_value; - char char_value[4]; - } value; - - value.int_value = value_in; - - new_value = value.char_value[0] & 0xFF; - new_value |= (value.char_value[1] & 0xFF) << 0x08; - new_value |= (value.char_value[2] & 0xFF) << 0x10; - new_value |= (value.char_value[3] & 0xFF) << 0x18; - fwrite(&new_value, sizeof(int), 1, fp); -} - -void -stl_put_little_float(FILE *fp, float value_in) { - int new_value; - union { - float float_value; - char char_value[4]; - } value; - - value.float_value = value_in; - - new_value = value.char_value[0] & 0xFF; - new_value |= (value.char_value[1] & 0xFF) << 0x08; - new_value |= (value.char_value[2] & 0xFF) << 0x10; - new_value |= (value.char_value[3] & 0xFF) << 0x18; - fwrite(&new_value, sizeof(int), 1, fp); -} - -void -stl_write_binary_block(stl_file *stl, FILE *fp) -{ - int i; - for(i = 0; i < stl->stats.number_of_facets; i++) - { - stl_put_little_float(fp, stl->facet_start[i].normal.x); - stl_put_little_float(fp, stl->facet_start[i].normal.y); - stl_put_little_float(fp, stl->facet_start[i].normal.z); - stl_put_little_float(fp, stl->facet_start[i].vertex[0].x); - stl_put_little_float(fp, stl->facet_start[i].vertex[0].y); - stl_put_little_float(fp, stl->facet_start[i].vertex[0].z); - stl_put_little_float(fp, stl->facet_start[i].vertex[1].x); - stl_put_little_float(fp, stl->facet_start[i].vertex[1].y); - stl_put_little_float(fp, stl->facet_start[i].vertex[1].z); - stl_put_little_float(fp, stl->facet_start[i].vertex[2].x); - stl_put_little_float(fp, stl->facet_start[i].vertex[2].y); - stl_put_little_float(fp, stl->facet_start[i].vertex[2].z); - fputc(stl->facet_start[i].extra[0], fp); - fputc(stl->facet_start[i].extra[1], fp); - } -} - void stl_write_binary(stl_file *stl, const char *file, const char *label) { FILE *fp; @@ -285,11 +228,9 @@ stl_write_binary(stl_file *stl, const char *file, const char *label) { for(i = strlen(label); i < LABEL_SIZE; i++) putc(0, fp); fseek(fp, LABEL_SIZE, SEEK_SET); - - stl_put_little_int(fp, stl->stats.number_of_facets); - - stl_write_binary_block(stl, fp); - + fwrite(&stl->stats.number_of_facets, 4, 1, fp); + for(i = 0; i < stl->stats.number_of_facets; i++) + fwrite(stl->facet_start + i, SIZEOF_STL_FACET, 1, fp); fclose(fp); } diff --git a/xs/src/admesh/stlinit.c b/xs/src/admesh/stlinit.c index 05c5beadb..d800e8f9c 100644 --- a/xs/src/admesh/stlinit.c +++ b/xs/src/admesh/stlinit.c @@ -27,10 +27,8 @@ #include "stl.h" -#if !defined(SEEK_SET) -#define SEEK_SET 0 -#define SEEK_CUR 1 -#define SEEK_END 2 +#ifndef SEEK_SET +#error "SEEK_SET not defined" #endif void @@ -277,10 +275,7 @@ stl_read(stl_file *stl, int first_facet, int first) { /* Read a single facet from a binary .STL file */ { /* we assume little-endian architecture! */ - if (fread(&facet.normal, sizeof(stl_normal), 1, stl->fp) \ - + fread(&facet.vertex, sizeof(stl_vertex), 3, stl->fp) \ - + fread(&facet.extra, sizeof(char), 2, stl->fp) != 6) { - perror("Cannot read facet"); + if (fread(&facet, 1, SIZEOF_STL_FACET, stl->fp) != SIZEOF_STL_FACET) { stl->error = 1; return; } @@ -343,8 +338,7 @@ stl_read(stl_file *stl, int first_facet, int first) { } #endif /* Write the facet into memory. */ - stl->facet_start[i] = facet; - + memcpy(stl->facet_start+i, &facet, SIZEOF_STL_FACET); stl_facet_stats(stl, facet, first); first = 0; } From 048fafb9f9490374c32478e597a802b7e21e845e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 24 Nov 2016 17:54:41 +0100 Subject: [PATCH 092/225] Fix compilation --- xs/src/admesh/stl.h | 8 ++++++++ xs/src/libslic3r/TriangleMesh.cpp | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/xs/src/admesh/stl.h b/xs/src/admesh/stl.h index d5ee6d130..f9fb2535a 100644 --- a/xs/src/admesh/stl.h +++ b/xs/src/admesh/stl.h @@ -57,7 +57,9 @@ typedef struct { float z; } stl_vertex; +#ifdef static_assert static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect"); +#endif typedef struct { float x; @@ -65,7 +67,9 @@ typedef struct { float z; } stl_normal; +#ifdef static_assert static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect"); +#endif typedef char stl_extra[2]; @@ -76,10 +80,12 @@ typedef struct { } stl_facet; #define SIZEOF_STL_FACET 50 +#ifdef static_assert static_assert(offsetof(stl_facet, normal) == 0, "stl_facet.normal has correct offset"); static_assert(offsetof(stl_facet, vertex) == 12, "stl_facet.vertex has correct offset"); static_assert(offsetof(stl_facet, extra ) == 48, "stl_facet.extra has correct offset"); static_assert(sizeof(stl_facet) >= SIZEOF_STL_FACET, "size of stl_facet incorrect"); +#endif typedef enum {binary, ascii, inmemory} stl_type; @@ -100,7 +106,9 @@ typedef struct stl_hash_edge { struct stl_hash_edge *next; } stl_hash_edge; +#ifdef static_assert static_assert(offsetof(stl_hash_edge, facet_number) == SIZEOF_EDGE_SORT, "size of stl_hash_edge.key incorrect"); +#endif typedef struct { // Index of a neighbor facet. diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 4a569ed40..4fb2ca9a0 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -50,7 +50,7 @@ TriangleMesh::TriangleMesh(const TriangleMesh &other) TriangleMesh& TriangleMesh::operator= (TriangleMesh other) { - swap(*this, other); + this->swap(other); return *this; } From 9c4e82cecfade53a5d593561f59e5191c1d56e94 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Thu, 24 Nov 2016 12:58:03 -0600 Subject: [PATCH 093/225] "Background slice now" function, initial implementation by @lordofhyphens https://github.com/alexrj/Slic3r/pull/3501 Conflicts: lib/Slic3r/GUI/MainFrame.pm lib/Slic3r/GUI/Plater.pm --- lib/Slic3r/GUI.pm | 7 +++++ lib/Slic3r/GUI/MainFrame.pm | 23 ++++++++++++-- lib/Slic3r/GUI/Plater.pm | 56 ++++++++++++++++++++++++++++++++-- lib/Slic3r/GUI/Preferences.pm | 3 ++ var/reslice.png | Bin 0 -> 665 bytes 5 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 var/reslice.png diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 7f4a028a5..58c4964d9 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -269,6 +269,13 @@ sub save_settings { Slic3r::Config->write_ini("$datadir/slic3r.ini", $Settings); } +# Called after the Preferences dialog is closed and the program settings are saved. +# Update the UI based on the current preferences. +sub update_ui_from_settings { + my ($self) = @_; + $self->{mainframe}->update_ui_from_settings; +} + sub presets { my ($self, $section) = @_; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 9aefb7b6c..ac81c48e0 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -83,6 +83,8 @@ sub new { # propagate event $event->Skip; }); + + $self->update_ui_from_settings; return $self; } @@ -212,9 +214,9 @@ sub _init_menubar { $self->_append_menu_item($fileMenu, "Slice to SV&G…\tCtrl+G", 'Slice file to SVG', sub { $self->quick_slice(save_as => 1, export_svg => 1); }, undef, 'shape_handles.png'); - $self->_append_menu_item($fileMenu, "(&Re)Slice Now\tCtrl+S", 'Start new slicing process', sub { - $self->reslice_now; - }, undef, 'shape_handles.png'); + $self->{menu_item_reslice_now} = $self->_append_menu_item( + $fileMenu, "(&Re)Slice Now\tCtrl+S", 'Start new slicing process', + sub { $self->reslice_now; }, undef, 'shape_handles.png'); $fileMenu->AppendSeparator(); $self->_append_menu_item($fileMenu, "Repair STL file…", 'Automatically repair an STL file', sub { $self->repair_stl; @@ -448,6 +450,13 @@ sub quick_slice { Slic3r::GUI::catch_error($self, sub { $progress_dialog->Destroy if $progress_dialog }); } +sub reslice_now { + my ($self) = @_; + if ($self->{plater}) { + $self->{plater}->reslice; + } +} + sub repair_stl { my $self = shift; @@ -803,4 +812,12 @@ sub _set_menu_item_icon { } } +# Called after the Preferences dialog is closed and the program settings are saved. +# Update the UI based on the current preferences. +sub update_ui_from_settings { + my ($self) = @_; + $self->{menu_item_reslice_now}->Enable(! $Slic3r::GUI::Settings->{_}{background_processing}); + $self->{plater}->update_ui_from_settings if ($self->{plater}); +} + 1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 648fd3b1e..a8c117ccd 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -33,6 +33,7 @@ use constant TB_SETTINGS => &Wx::NewId; our $THUMBNAIL_DONE_EVENT : shared = Wx::NewEventType; our $PROGRESS_BAR_EVENT : shared = Wx::NewEventType; our $ERROR_EVENT : shared = Wx::NewEventType; +# Emitted from the worker thread when the G-code export is finished. our $EXPORT_COMPLETED_EVENT : shared = Wx::NewEventType; our $PROCESS_COMPLETED_EVENT : shared = Wx::NewEventType; @@ -191,8 +192,7 @@ sub new { # right pane buttons $self->{btn_export_gcode} = Wx::Button->new($self, -1, "Export G-code…", wxDefaultPosition, [-1, 30], wxBU_LEFT); - $self->{btn_reslice} = Wx::Button->new($self, -1, "Slice now…", wxDefaultPosition, [-1, 30], wxBU_LEFT); - $self->{btn_print} = Wx::Button->new($self, -1, "Print…", wxDefaultPosition, [-1, 30], wxBU_LEFT); + $self->{btn_reslice} = Wx::Button->new($self, -1, "Slice now", wxDefaultPosition, [-1, 30], wxBU_LEFT); $self->{btn_print} = Wx::Button->new($self, -1, "Print…", wxDefaultPosition, [-1, 30], wxBU_LEFT); $self->{btn_send_gcode} = Wx::Button->new($self, -1, "Send to printer", wxDefaultPosition, [-1, 30], wxBU_LEFT); $self->{btn_export_stl} = Wx::Button->new($self, -1, "Export STL…", wxDefaultPosition, [-1, 30], wxBU_LEFT); @@ -210,6 +210,7 @@ sub new { export_gcode cog_go.png print arrow_up.png send_gcode arrow_up.png + reslice reslice.png export_stl brick_go.png increase add.png @@ -239,6 +240,7 @@ sub new { EVT_BUTTON($self, $self->{btn_send_gcode}, sub { $self->{send_gcode_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir()); }); + EVT_BUTTON($self, $self->{btn_reslice}, \&reslice); EVT_BUTTON($self, $self->{btn_export_stl}, \&export_stl); if ($self->{htoolbar}) { @@ -391,8 +393,10 @@ sub new { } my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + $self->{buttons_sizer} = $buttons_sizer; $buttons_sizer->AddStretchSpacer(1); $buttons_sizer->Add($self->{btn_export_stl}, 0, wxALIGN_RIGHT, 0); + $buttons_sizer->Add($self->{btn_reslice}, 0, wxALIGN_RIGHT, 0); $buttons_sizer->Add($self->{btn_print}, 0, wxALIGN_RIGHT, 0); $buttons_sizer->Add($self->{btn_send_gcode}, 0, wxALIGN_RIGHT, 0); $buttons_sizer->Add($self->{btn_export_gcode}, 0, wxALIGN_RIGHT, 0); @@ -416,6 +420,8 @@ sub new { $sizer->SetSizeHints($self); $self->SetSizer($sizer); } + + $self->update_ui_from_settings(); return $self; } @@ -453,6 +459,25 @@ sub GetFrame { return &Wx::GetTopLevelParent($self); } +# Called after the Preferences dialog is closed and the program settings are saved. +# Update the UI based on the current preferences. +sub update_ui_from_settings +{ + my ($self) = @_; + if (defined($self->{btn_reslice}) && $self->{buttons_sizer}->IsShown($self->{btn_reslice}) != (! $Slic3r::GUI::Settings->{_}{background_processing})) { + $self->{buttons_sizer}->Show($self->{btn_reslice}, ! $Slic3r::GUI::Settings->{_}{background_processing}); + $self->{buttons_sizer}->Layout; + } +} + +# Update presets (Print settings, Filament, Printer) from their respective tabs. +# Called by +# Slic3r::GUI::Tab::Print::_on_presets_changed +# Slic3r::GUI::Tab::Filament::_on_presets_changed +# Slic3r::GUI::Tab::Printer::_on_presets_changed +# when the presets are loaded or the user selects another preset. +# For Print settings and Printer, synchronize the selection index with their tabs. +# For Filament, synchronize the selection index for a single extruder printer only, otherwise keep the selection. sub update_presets { my $self = shift; my ($group, $presets, $selected, $is_dirty) = @_; @@ -1109,6 +1134,22 @@ sub resume_background_process { } } +sub reslice { + # explicitly cancel a previous thread and start a new one. + my ($self) = @_; + # Don't reslice if export of G-code or sending to OctoPrint is running. + if ($Slic3r::have_threads && ! defined($self->{export_gcode_output_file}) && ! defined($self->{send_gcode_file})) { + $self->stop_background_process; + $self->statusbar->SetCancelCallback(sub { + $self->stop_background_process; + $self->statusbar->SetStatusText("Slicing cancelled"); + # this updates buttons status + $self->object_list_changed; + }); + $self->start_background_process; + } +} + sub export_gcode { my ($self, $output_file) = @_; @@ -1243,6 +1284,7 @@ sub on_progress_event { $self->statusbar->SetStatusText("$message…"); } +# Called when the G-code export finishes, either successfully or with an error. # This gets called also if we don't have threads. sub on_export_completed { my ($self, $result) = @_; @@ -1259,6 +1301,7 @@ sub on_export_completed { my $send_gcode = 0; my $do_print = 0; if ($result) { + # G-code file exported successfully. if ($self->{print_file}) { $message = "File added to print queue"; $do_print = 1; @@ -1276,6 +1319,7 @@ sub on_export_completed { wxTheApp->notify($message); $self->do_print if $do_print; + # Send $self->{send_gcode_file} to OctoPrint. $self->send_gcode if $send_gcode; $self->{print_file} = undef; $self->{send_gcode_file} = undef; @@ -1301,6 +1345,8 @@ sub do_print { $self->GetFrame->select_tab(1); } +# Send $self->{send_gcode_file} to OctoPrint. +#FIXME Currently this call blocks the UI. Make it asynchronous. sub send_gcode { my ($self) = @_; @@ -1611,15 +1657,19 @@ sub object_settings_dialog { } } +# Called to update various buttons depending on whether there are any objects or +# whether background processing (export of a G-code, sending to Octoprint, forced background re-slicing) is active. sub object_list_changed { my $self = shift; + # Enable/disable buttons depending on whether there are any objects on the platter. my $have_objects = @{$self->{objects}} ? 1 : 0; my $method = $have_objects ? 'Enable' : 'Disable'; $self->{"btn_$_"}->$method - for grep $self->{"btn_$_"}, qw(reslice reset arrange export_gcode export_stl print send_gcode); + for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode); if ($self->{export_gcode_output_file} || $self->{send_gcode_file}) { + $self->{btn_reslice}->Disable; $self->{btn_export_gcode}->Disable; $self->{btn_print}->Disable; $self->{btn_send_gcode}->Disable; diff --git a/lib/Slic3r/GUI/Preferences.pm b/lib/Slic3r/GUI/Preferences.pm index ed210d229..8455304e9 100644 --- a/lib/Slic3r/GUI/Preferences.pm +++ b/lib/Slic3r/GUI/Preferences.pm @@ -91,6 +91,9 @@ sub _accept { $self->EndModal(wxID_OK); $self->Close; # needed on Linux + + # Nothify the UI to update itself from the ini file. + wxTheApp->update_ui_from_settings; } 1; diff --git a/var/reslice.png b/var/reslice.png new file mode 100644 index 0000000000000000000000000000000000000000..167c0eccd73146fb8f0a365cd4734ff614f94cd6 GIT binary patch literal 665 zcmV;K0%rY*P)WkaV2hCDeioVd5P(4X8Hn2yAdD2L73^H!-P?aQNpL-9}HG;44Rnl z<xJ%vm1pJ@=f$We^cuAcTxYqxpOe0P%QS5QM9%EB64P3;X?EmSs01%d%k@ZiYcb zLWr*Gv)K$0L!po;iZ?emi1_;Ys_Xh@vtbw}kw{!$U%$M(fFpAfAcQE2qN=K@sx-+wB$+FD@<+aWEJhk4Hkta=8o!gSKtQ<8ex9zu#Z4*URP7GaipKnapmt zQ&pAYxao9SE|*UUA0Hq6e!pc|yWK7v4(qzkavXPgd6`TmL!nS05Qs*j!{LxpN+}%< zhp|}f?Ck9A?aegJ^Ye3#<2+8SUayzu`R#Vg^ZaJB>GgU5U|H5`wc79ZJkOu{d|yCB zN~zE1OQlkVVNgmNjfT5@Ns^k)CL(%F(_~o|5iQHIZQHUehG80wh9pT34-b>c#963T ztM~W!cXxN5NF?(7{49!MFc>rp<8V0CYBgv2!}s6v&lvzzvO>4pEffj>0Jd#wn)dYc z)ai5 z*%*ca^A7+Ni$wtNI2vBBH^ZtDB9}0znBuQ>Y)3k243jlus%BPUxt^MY700000NkvXXu0mjf Date: Fri, 25 Nov 2016 10:19:57 +0100 Subject: [PATCH 094/225] Revert "Perl OpenGL 0.7 package causes crashes on many platforms." This reverts commit 832773e3109b0ec61a4e0999783a893ba9dca822. --- Build.PL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Build.PL b/Build.PL index d402e8a3a..68a9af16e 100644 --- a/Build.PL +++ b/Build.PL @@ -44,7 +44,7 @@ if ($gui) { %recommends = qw( Growl::GNTP 0.15 Wx::GLCanvas 0 - OpenGL <0.70 + OpenGL 0 LWP::UserAgent 0 Net::Bonjour 0 ); From 66591bcc556c01572ec7519b1f8cb4ee2d430685 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 26 Nov 2016 12:28:39 +0100 Subject: [PATCH 095/225] Ported make_perimeters() and infill() to C++/XS, use pure C++ threads --- lib/Slic3r/Print.pm | 4 + lib/Slic3r/Print/Object.pm | 130 +--------------------- xs/src/libslic3r/Print.hpp | 8 +- xs/src/libslic3r/PrintObject.cpp | 185 +++++++++++++++++++++++++++++++ xs/xsp/Print.xsp | 2 + 5 files changed, 201 insertions(+), 128 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 187ee84e5..b274836e3 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -41,8 +41,12 @@ sub size { sub process { my ($self) = @_; + $self->status_cb->(20, "Generating perimeters"); $_->make_perimeters for @{$self->objects}; + + $self->status_cb->(70, "Infilling layers"); $_->infill for @{$self->objects}; + $_->generate_support_material for @{$self->objects}; $self->make_skirt; $self->make_brim; # must come after make_skirt diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 662067fb0..30f684034 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -369,113 +369,12 @@ sub _slice_region { } sub make_perimeters { - my $self = shift; + my ($self) = @_; # prerequisites $self->slice; - return if $self->step_done(STEP_PERIMETERS); - $self->set_step_started(STEP_PERIMETERS); - $self->print->status_cb->(20, "Generating perimeters"); - - # merge slices if they were split into types - if ($self->typed_slices) { - $_->merge_slices for @{$self->layers}; - $self->set_typed_slices(0); - $self->invalidate_step(STEP_PREPARE_INFILL); - } - - # compare each layer to the one below, and mark those slices needing - # one additional inner perimeter, like the top of domed objects- - - # this algorithm makes sure that at least one perimeter is overlapping - # but we don't generate any extra perimeter if fill density is zero, as they would be floating - # inside the object - infill_only_where_needed should be the method of choice for printing - # hollow objects - for my $region_id (0 .. ($self->print->region_count-1)) { - my $region = $self->print->regions->[$region_id]; - my $region_perimeters = $region->config->perimeters; - - next if !$region->config->extra_perimeters; - next if $region_perimeters == 0; - next if $region->config->fill_density == 0; - - for my $i (0 .. ($self->layer_count - 2)) { - my $layerm = $self->get_layer($i)->get_region($region_id); - my $upper_layerm = $self->get_layer($i+1)->get_region($region_id); - my $upper_layerm_polygons = [ map $_->p, @{$upper_layerm->slices} ]; - # Filter upper layer polygons in intersection_ppl by their bounding boxes? - # my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ]; - my $total_loop_length = sum(map $_->length, @$upper_layerm_polygons) // 0; - - my $perimeter_spacing = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing; - my $ext_perimeter_flow = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER); - my $ext_perimeter_width = $ext_perimeter_flow->scaled_width; - my $ext_perimeter_spacing = $ext_perimeter_flow->scaled_spacing; - - foreach my $slice (@{$layerm->slices}) { - while (1) { - # compute the total thickness of perimeters - my $perimeters_thickness = $ext_perimeter_width/2 + $ext_perimeter_spacing/2 - + ($region_perimeters-1 + $slice->extra_perimeters) * $perimeter_spacing; - - # define a critical area where we don't want the upper slice to fall into - # (it should either lay over our perimeters or outside this area) - my $critical_area_depth = $perimeter_spacing*1.5; - my $critical_area = diff( - offset($slice->expolygon->arrayref, -$perimeters_thickness), - offset($slice->expolygon->arrayref, -($perimeters_thickness + $critical_area_depth)), - ); - - # check whether a portion of the upper slices falls inside the critical area - my $intersection = intersection_ppl( - $upper_layerm_polygons, - $critical_area, - ); - - # only add an additional loop if at least 30% of the slice loop would benefit from it - my $total_intersection_length = sum(map $_->length, @$intersection) // 0; - last unless $total_intersection_length > $total_loop_length*0.3; - - if (0) { - require "Slic3r/SVG.pm"; - Slic3r::SVG::output( - "extra.svg", - no_arrows => 1, - expolygons => union_ex($critical_area), - polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ], - ); - } - - $slice->extra_perimeters($slice->extra_perimeters + 1); - } - Slic3r::debugf " adding %d more perimeter(s) at layer %d\n", - $slice->extra_perimeters, $layerm->layer->id - if $slice->extra_perimeters > 0; - } - } - } - - Slic3r::parallelize( - threads => $self->print->config->threads, - items => sub { 0 .. ($self->layer_count - 1) }, - thread_cb => sub { - my $q = shift; - while (defined (my $i = $q->dequeue)) { - $self->get_layer($i)->make_perimeters; - } - }, - no_threads_cb => sub { - $_->make_perimeters for @{$self->layers}; - }, - ); - - # simplify slices (both layer and region slices), - # we only need the max resolution for perimeters - ### This makes this method not-idempotent, so we keep it disabled for now. - ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION); - - $self->set_step_done(STEP_PERIMETERS); + $self->_make_perimeters; } sub prepare_infill { @@ -522,30 +421,7 @@ sub infill { # prerequisites $self->prepare_infill; - return if $self->step_done(STEP_INFILL); - $self->set_step_started(STEP_INFILL); - $self->print->status_cb->(70, "Infilling layers"); - - Slic3r::parallelize( - threads => $self->print->config->threads, - items => sub { 0..$#{$self->layers} }, - thread_cb => sub { - my $q = shift; - while (defined (my $i = $q->dequeue)) { - $self->get_layer($i)->make_fills; - } - }, - no_threads_cb => sub { - foreach my $layer (@{$self->layers}) { - $layer->make_fills; - } - }, - ); - - ### we could free memory now, but this would make this step not idempotent - ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; - - $self->set_step_done(STEP_INFILL); + $self->_infill; } sub generate_support_material { diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 56ff11fe5..14eb5472d 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -2,9 +2,11 @@ #define slic3r_Print_hpp_ #include "libslic3r.h" +#include #include -#include #include +#include +#include #include "BoundingBox.hpp" #include "Flow.hpp" #include "PrintConfig.hpp" @@ -134,6 +136,8 @@ class PrintObject bool has_support_material() const; void process_external_surfaces(); void bridge_over_infill(); + void _make_perimeters(); + void _infill(); private: Print* _print; @@ -144,6 +148,8 @@ class PrintObject // parameter PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); ~PrintObject(); + void _make_perimeters_do(std::queue* queue, boost::mutex* queue_mutex); + void _infill_do(std::queue* queue, boost::mutex* queue_mutex); }; typedef std::vector PrintObjectPtrs; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 13ba53da1..a2b0260b7 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -485,4 +485,189 @@ PrintObject::bridge_over_infill() } } +void +PrintObject::_make_perimeters() +{ + if (this->state.is_done(posPerimeters)) return; + this->state.set_started(posPerimeters); + + // merge slices if they were split into types + if (this->typed_slices) { + FOREACH_LAYER(this, layer_it) + (*layer_it)->merge_slices(); + this->typed_slices = false; + this->state.invalidate(posPrepareInfill); + } + + // compare each layer to the one below, and mark those slices needing + // one additional inner perimeter, like the top of domed objects- + + // this algorithm makes sure that at least one perimeter is overlapping + // but we don't generate any extra perimeter if fill density is zero, as they would be floating + // inside the object - infill_only_where_needed should be the method of choice for printing + // hollow objects + FOREACH_REGION(this->_print, region_it) { + size_t region_id = region_it - this->_print->regions.begin(); + const PrintRegion ®ion = **region_it; + + + if (!region.config.extra_perimeters + || region.config.perimeters == 0 + || region.config.fill_density == 0) continue; + + for (size_t i = 0; i <= (this->layer_count()-2); ++i) { + LayerRegion &layerm = *this->get_layer(i)->get_region(region_id); + const LayerRegion &upper_layerm = *this->get_layer(i+1)->get_region(region_id); + const Polygons upper_layerm_polygons = upper_layerm.slices; + + // Filter upper layer polygons in intersection_ppl by their bounding boxes? + // my $upper_layerm_poly_bboxes= [ map $_->bounding_box, @{$upper_layerm_polygons} ]; + double total_loop_length = 0; + for (Polygons::const_iterator it = upper_layerm_polygons.begin(); it != upper_layerm_polygons.end(); ++it) + total_loop_length += it->length(); + + const coord_t perimeter_spacing = layerm.flow(frPerimeter).scaled_spacing(); + const Flow ext_perimeter_flow = layerm.flow(frExternalPerimeter); + const coord_t ext_perimeter_width = ext_perimeter_flow.scaled_width(); + const coord_t ext_perimeter_spacing = ext_perimeter_flow.scaled_spacing(); + + for (Surfaces::iterator slice = layerm.slices.surfaces.begin(); + slice != layerm.slices.surfaces.end(); ++slice) { + while (true) { + // compute the total thickness of perimeters + const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2 + + (region.config.perimeters-1 + region.config.extra_perimeters) * perimeter_spacing; + + // define a critical area where we don't want the upper slice to fall into + // (it should either lay over our perimeters or outside this area) + const coord_t critical_area_depth = perimeter_spacing * 1.5; + const Polygons critical_area = diff( + offset(slice->expolygon, -perimeters_thickness), + offset(slice->expolygon, -(perimeters_thickness + critical_area_depth)) + ); + + // check whether a portion of the upper slices falls inside the critical area + const Polylines intersection = intersection_pl( + upper_layerm_polygons, + critical_area + ); + + // only add an additional loop if at least 30% of the slice loop would benefit from it + { + double total_intersection_length = 0; + for (Polylines::const_iterator it = intersection.begin(); it != intersection.end(); ++it) + total_intersection_length += it->length(); + if (total_intersection_length <= total_loop_length*0.3) break; + } + + /* + if (0) { + require "Slic3r/SVG.pm"; + Slic3r::SVG::output( + "extra.svg", + no_arrows => 1, + expolygons => union_ex($critical_area), + polylines => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ], + ); + } + */ + + slice->extra_perimeters++; + } + + #ifdef DEBUG + if (slice->extra_perimeters > 0) + printf(" adding %d more perimeter(s) at layer %zu\n", slice->extra_perimeters, layer->id(); + #endif + } + } + } + + { + // queue all the layer numbers + std::queue queue; + boost::mutex queue_mutex; + for (size_t i = 0; i < this->layer_count(); ++i) + queue.push(i); + + boost::thread_group workers; + for (int i = 0; i < this->_print->config.threads; i++) + workers.add_thread(new boost::thread(&Slic3r::PrintObject::_make_perimeters_do, this, &queue, &queue_mutex)); + workers.join_all(); + } + + /* + simplify slices (both layer and region slices), + we only need the max resolution for perimeters + ### This makes this method not-idempotent, so we keep it disabled for now. + ###$self->_simplify_slices(&Slic3r::SCALED_RESOLUTION); + */ + + this->state.set_done(posPerimeters); +} + +void +PrintObject::_make_perimeters_do(std::queue* queue, boost::mutex* queue_mutex) +{ + //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; + + while (true) { + size_t layer_id; + { + boost::lock_guard l(*queue_mutex); + if (queue->empty()) return; + layer_id = queue->front(); + queue->pop(); + } + //std::cout << " Layer " << layer_id << " (" << boost::this_thread::get_id() << ")" << std::endl; + this->get_layer(layer_id)->make_perimeters(); + boost::this_thread::interruption_point(); + } +} + +void +PrintObject::_infill() +{ + if (this->state.is_done(posInfill)) return; + this->state.set_started(posInfill); + + { + // queue all the layer numbers + std::queue queue; + boost::mutex queue_mutex; + for (size_t i = 0; i < this->layer_count(); ++i) + queue.push(i); + + boost::thread_group workers; + for (int i = 0; i < this->_print->config.threads; i++) + workers.add_thread(new boost::thread(&Slic3r::PrintObject::_infill_do, this, &queue, &queue_mutex)); + workers.join_all(); + } + + /* we could free memory now, but this would make this step not idempotent + ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; + */ + + this->state.set_done(posInfill); +} + +void +PrintObject::_infill_do(std::queue* queue, boost::mutex* queue_mutex) +{ + //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; + + while (true) { + size_t layer_id; + { + boost::lock_guard l(*queue_mutex); + if (queue->empty()) return; + layer_id = queue->front(); + queue->pop(); + } + //std::cout << " Layer " << layer_id << " (" << boost::this_thread::get_id() << ")" << std::endl; + this->get_layer(layer_id)->make_fills(); + boost::this_thread::interruption_point(); + } +} + } diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 9168e9d85..dec2e115e 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -109,6 +109,8 @@ _constant() void process_external_surfaces(); void bridge_over_infill(); + void _make_perimeters(); + void _infill(); int ptr() %code%{ RETVAL = (int)(intptr_t)THIS; %}; From fee5de076f2184415e0114766332d709dd172108 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 26 Nov 2016 12:47:11 +0100 Subject: [PATCH 096/225] Fixed regression in the _make_perimeters port --- xs/src/libslic3r/PrintObject.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index a2b0260b7..7625edd71 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -513,7 +513,8 @@ PrintObject::_make_perimeters() if (!region.config.extra_perimeters || region.config.perimeters == 0 - || region.config.fill_density == 0) continue; + || region.config.fill_density == 0 + || this->layer_count() < 2) continue; for (size_t i = 0; i <= (this->layer_count()-2); ++i) { LayerRegion &layerm = *this->get_layer(i)->get_region(region_id); @@ -577,7 +578,7 @@ PrintObject::_make_perimeters() #ifdef DEBUG if (slice->extra_perimeters > 0) - printf(" adding %d more perimeter(s) at layer %zu\n", slice->extra_perimeters, layer->id(); + printf(" adding %d more perimeter(s) at layer %zu\n", slice->extra_perimeters, i); #endif } } From 83ad123d951c6ee663d2f3b02e095c203ca794e7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 26 Nov 2016 13:45:58 +0100 Subject: [PATCH 097/225] Parallelize TriangleMeshSlicer::slice() --- xs/src/libslic3r/TriangleMesh.cpp | 134 ++++++++++++++++++++++-------- xs/src/libslic3r/TriangleMesh.hpp | 24 ++++-- 2 files changed, 117 insertions(+), 41 deletions(-) diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 4fb2ca9a0..56accd35c 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -2,7 +2,6 @@ #include "ClipperUtils.hpp" #include "Geometry.hpp" #include -#include #include #include #include @@ -427,7 +426,7 @@ TriangleMesh::require_shared_vertices() } void -TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) +TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const { /* This method gets called with a list of unscaled Z coordinates and outputs @@ -451,7 +450,6 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* la At the end, we free the tables generated by analyze() as we don't need them anymore. - FUTURE: parallelize slice_facet() and make_loops() NOTE: this method accepts a vector of floats because the mesh coordinate type is float. @@ -459,18 +457,66 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* la std::vector lines(z.size()); - for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) { - stl_facet* facet = &this->mesh->stl.facet_start[facet_idx]; + { + // queue all the facet indices + std::queue queue; + boost::mutex queue_mutex, lines_mutex; + for (size_t i = 0; i < this->mesh->stl.stats.number_of_facets; ++i) queue.push(i); + + boost::thread_group workers; + for (int i = 0; i < boost::thread::hardware_concurrency(); i++) + workers.add_thread(new boost::thread(&TriangleMeshSlicer::_slice_do, this, + &queue, &queue_mutex, &lines, &lines_mutex, z)); + workers.join_all(); + } + + // v_scaled_shared could be freed here + + // build loops + layers->resize(z.size()); + { + // queue all the layer numbers + std::queue queue; + boost::mutex queue_mutex; + for (size_t i = 0; i < lines.size(); ++i) queue.push(i); + + // We don't use a mutex for lines because workers are only writing the skip property + // and no workers work on the same layer (i.e. item of 'lines'). + boost::thread_group workers; + for (int i = 0; i < boost::thread::hardware_concurrency(); i++) + workers.add_thread(new boost::thread(&TriangleMeshSlicer::_make_loops_do, this, + &queue, &queue_mutex, &lines, layers)); + workers.join_all(); + } +} + +void +TriangleMeshSlicer::_slice_do(std::queue* queue, boost::mutex* queue_mutex, + std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const +{ + //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; + + while (true) { + int facet_idx; + { + boost::lock_guard l(*queue_mutex); + if (queue->empty()) return; + facet_idx = queue->front(); + queue->pop(); + } + //std::cout << " Facet " << facet_idx << " (" << boost::this_thread::get_id() << ")" << std::endl; + + const stl_facet &facet = this->mesh->stl.facet_start[facet_idx]; // find facet extents - float min_z = fminf(facet->vertex[0].z, fminf(facet->vertex[1].z, facet->vertex[2].z)); - float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z)); + const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z)); + const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z)); #ifdef SLIC3R_DEBUG printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, - facet->vertex[0].x, facet->vertex[0].y, facet->vertex[0].z, - facet->vertex[1].x, facet->vertex[1].y, facet->vertex[1].z, - facet->vertex[2].x, facet->vertex[2].y, facet->vertex[2].z); + facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z, + facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z, + facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z); printf("z: min = %.2f, max = %.2f\n", min_z, max_z); #endif @@ -484,25 +530,15 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* la for (std::vector::const_iterator it = min_layer; it != max_layer + 1; ++it) { std::vector::size_type layer_idx = it - z.begin(); - this->slice_facet(*it / SCALING_FACTOR, *facet, facet_idx, min_z, max_z, &lines[layer_idx]); + this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &(*lines)[layer_idx], lines_mutex); } - } - - // v_scaled_shared could be freed here - - // build loops - layers->resize(z.size()); - for (std::vector::iterator it = lines.begin(); it != lines.end(); ++it) { - size_t layer_idx = it - lines.begin(); - #ifdef SLIC3R_DEBUG - printf("Layer %zu:\n", layer_idx); - #endif - this->make_loops(*it, &(*layers)[layer_idx]); + + boost::this_thread::interruption_point(); } } void -TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) +TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const { std::vector layers_p; this->slice(z, &layers_p); @@ -519,7 +555,9 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* } void -TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector* lines) const +TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, + const float &min_z, const float &max_z, std::vector* lines, + boost::mutex* lines_mutex) const { std::vector points; std::vector< std::vector::size_type > points_on_layer; @@ -571,7 +609,12 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int line.b.y = b->y; line.a_id = a_id; line.b_id = b_id; - lines->push_back(line); + if (lines_mutex != NULL) { + boost::lock_guard l(*lines_mutex); + lines->push_back(line); + } else { + lines->push_back(line); + } found_horizontal_edge = true; @@ -624,13 +667,38 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int line.b_id = points[0].point_id; line.edge_a_id = points[1].edge_id; line.edge_b_id = points[0].edge_id; - lines->push_back(line); + if (lines_mutex != NULL) { + boost::lock_guard l(*lines_mutex); + lines->push_back(line); + } else { + lines->push_back(line); + } return; } } void -TriangleMeshSlicer::make_loops(std::vector &lines, Polygons* loops) +TriangleMeshSlicer::_make_loops_do(std::queue* queue, boost::mutex* queue_mutex, + std::vector* lines, std::vector* layers) const +{ + //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; + + while (true) { + size_t layer_id; + { + boost::lock_guard l(*queue_mutex); + if (queue->empty()) return; + layer_id = queue->front(); + queue->pop(); + } + //std::cout << " Layer " << layer_id << " (" << boost::this_thread::get_id() << ")" << std::endl; + this->make_loops((*lines)[layer_id], &(*layers)[layer_id]); + boost::this_thread::interruption_point(); + } +} + +void +TriangleMeshSlicer::make_loops(std::vector &lines, Polygons* loops) const { /* SVG svg("lines.svg"); @@ -731,6 +799,7 @@ TriangleMeshSlicer::make_loops(std::vector &lines, Polygons* l for (IntersectionLinePtrs::const_iterator lineptr = loop.begin(); lineptr != loop.end(); ++lineptr) { p.points.push_back((*lineptr)->a); } + loops->push_back(p); #ifdef SLIC3R_DEBUG @@ -770,7 +839,7 @@ class _area_comp { }; void -TriangleMeshSlicer::make_expolygons_simple(std::vector &lines, ExPolygons* slices) +TriangleMeshSlicer::make_expolygons_simple(std::vector &lines, ExPolygons* slices) const { Polygons loops; this->make_loops(lines, &loops); @@ -804,7 +873,7 @@ TriangleMeshSlicer::make_expolygons_simple(std::vector &lines, } void -TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) +TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) const { /* Input loops are not suitable for evenodd nor nonzero fill types, as we might get @@ -867,7 +936,7 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) } void -TriangleMeshSlicer::make_expolygons(std::vector &lines, ExPolygons* slices) +TriangleMeshSlicer::make_expolygons(std::vector &lines, ExPolygons* slices) const { Polygons pp; this->make_loops(lines, &pp); @@ -875,7 +944,7 @@ TriangleMeshSlicer::make_expolygons(std::vector &lines, ExPoly } void -TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) +TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const { IntersectionLines upper_lines, lower_lines; @@ -1027,7 +1096,6 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) stl_get_size(&(upper->stl)); stl_get_size(&(lower->stl)); - } TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_scaled_shared(NULL) diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index b10e3fae6..0225d7f9b 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -3,7 +3,9 @@ #include "libslic3r.h" #include +#include #include +#include #include "BoundingBox.hpp" #include "Line.hpp" #include "Point.hpp" @@ -92,19 +94,25 @@ class TriangleMeshSlicer TriangleMesh* mesh; TriangleMeshSlicer(TriangleMesh* _mesh); ~TriangleMeshSlicer(); - void slice(const std::vector &z, std::vector* layers); - void slice(const std::vector &z, std::vector* layers); - void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector* lines) const; - void cut(float z, TriangleMesh* upper, TriangleMesh* lower); + void slice(const std::vector &z, std::vector* layers) const; + void slice(const std::vector &z, std::vector* layers) const; + void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, + const float &min_z, const float &max_z, std::vector* lines, + boost::mutex* lines_mutex = NULL) const; + void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const; private: typedef std::vector< std::vector > t_facets_edges; t_facets_edges facets_edges; stl_vertex* v_scaled_shared; - void make_loops(std::vector &lines, Polygons* loops); - void make_expolygons(const Polygons &loops, ExPolygons* slices); - void make_expolygons_simple(std::vector &lines, ExPolygons* slices); - void make_expolygons(std::vector &lines, ExPolygons* slices); + void _slice_do(std::queue* queue, boost::mutex* queue_mutex, + std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const; + void _make_loops_do(std::queue* queue, boost::mutex* queue_mutex, + std::vector* lines, std::vector* layers) const; + void make_loops(std::vector &lines, Polygons* loops) const; + void make_expolygons(const Polygons &loops, ExPolygons* slices) const; + void make_expolygons_simple(std::vector &lines, ExPolygons* slices) const; + void make_expolygons(std::vector &lines, ExPolygons* slices) const; }; } From dabb7cb2ee095601d443a61eea365d76c4bab8d1 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 26 Nov 2016 13:46:19 +0100 Subject: [PATCH 098/225] Automatically detect the number of cores and use that as a default for threads number --- xs/src/libslic3r/PrintConfig.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index a93fcd00f..1fc1bd5ce 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1,4 +1,5 @@ #include "PrintConfig.hpp" +#include namespace Slic3r { @@ -1247,7 +1248,7 @@ PrintConfigDef::PrintConfigDef() def->readonly = true; def->min = 1; def->max = 16; - def->default_value = new ConfigOptionInt(2); + def->default_value = new ConfigOptionInt(boost::thread::hardware_concurrency()); def = this->add("toolchange_gcode", coString); def->label = "Tool change G-code"; From 36789774471a0bf9a66bb33cc3ab6984f8ede8c6 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 26 Nov 2016 16:07:36 +0100 Subject: [PATCH 099/225] Refactored parallelization code --- xs/src/libslic3r/Print.hpp | 3 - xs/src/libslic3r/PrintConfig.cpp | 2 +- xs/src/libslic3r/PrintObject.cpp | 72 +++--------------- xs/src/libslic3r/TriangleMesh.cpp | 122 ++++++++++-------------------- xs/src/libslic3r/TriangleMesh.hpp | 7 +- xs/src/libslic3r/libslic3r.h | 67 ++++++++++++---- 6 files changed, 107 insertions(+), 166 deletions(-) diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 14eb5472d..0e3de68ef 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -2,7 +2,6 @@ #define slic3r_Print_hpp_ #include "libslic3r.h" -#include #include #include #include @@ -148,8 +147,6 @@ class PrintObject // parameter PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox); ~PrintObject(); - void _make_perimeters_do(std::queue* queue, boost::mutex* queue_mutex); - void _infill_do(std::queue* queue, boost::mutex* queue_mutex); }; typedef std::vector PrintObjectPtrs; diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 1fc1bd5ce..abdb26bd8 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1248,7 +1248,7 @@ PrintConfigDef::PrintConfigDef() def->readonly = true; def->min = 1; def->max = 16; - def->default_value = new ConfigOptionInt(boost::thread::hardware_concurrency()); + def->default_value = new ConfigOptionInt(boost::thread::hardware_concurrency() || 2); def = this->add("toolchange_gcode", coString); def->label = "Tool change G-code"; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 7625edd71..e3a21bb80 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -584,18 +584,11 @@ PrintObject::_make_perimeters() } } - { - // queue all the layer numbers - std::queue queue; - boost::mutex queue_mutex; - for (size_t i = 0; i < this->layer_count(); ++i) - queue.push(i); - - boost::thread_group workers; - for (int i = 0; i < this->_print->config.threads; i++) - workers.add_thread(new boost::thread(&Slic3r::PrintObject::_make_perimeters_do, this, &queue, &queue_mutex)); - workers.join_all(); - } + parallelize( + std::queue(std::deque(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue + boost::bind(&Slic3r::Layer::make_perimeters, _1), + this->_print->config.threads.value + ); /* simplify slices (both layer and region slices), @@ -607,43 +600,17 @@ PrintObject::_make_perimeters() this->state.set_done(posPerimeters); } -void -PrintObject::_make_perimeters_do(std::queue* queue, boost::mutex* queue_mutex) -{ - //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; - - while (true) { - size_t layer_id; - { - boost::lock_guard l(*queue_mutex); - if (queue->empty()) return; - layer_id = queue->front(); - queue->pop(); - } - //std::cout << " Layer " << layer_id << " (" << boost::this_thread::get_id() << ")" << std::endl; - this->get_layer(layer_id)->make_perimeters(); - boost::this_thread::interruption_point(); - } -} - void PrintObject::_infill() { if (this->state.is_done(posInfill)) return; this->state.set_started(posInfill); - { - // queue all the layer numbers - std::queue queue; - boost::mutex queue_mutex; - for (size_t i = 0; i < this->layer_count(); ++i) - queue.push(i); - - boost::thread_group workers; - for (int i = 0; i < this->_print->config.threads; i++) - workers.add_thread(new boost::thread(&Slic3r::PrintObject::_infill_do, this, &queue, &queue_mutex)); - workers.join_all(); - } + parallelize( + std::queue(std::deque(this->layers.begin(), this->layers.end())), // cast LayerPtrs to std::queue + boost::bind(&Slic3r::Layer::make_fills, _1), + this->_print->config.threads.value + ); /* we could free memory now, but this would make this step not idempotent ### $_->fill_surfaces->clear for map @{$_->regions}, @{$object->layers}; @@ -652,23 +619,4 @@ PrintObject::_infill() this->state.set_done(posInfill); } -void -PrintObject::_infill_do(std::queue* queue, boost::mutex* queue_mutex) -{ - //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; - - while (true) { - size_t layer_id; - { - boost::lock_guard l(*queue_mutex); - if (queue->empty()) return; - layer_id = queue->front(); - queue->pop(); - } - //std::cout << " Layer " << layer_id << " (" << boost::this_thread::get_id() << ")" << std::endl; - this->get_layer(layer_id)->make_fills(); - boost::this_thread::interruption_point(); - } -} - } diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 56accd35c..c1d7a9edf 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -3,6 +3,7 @@ #include "Geometry.hpp" #include #include +#include #include #include #include @@ -456,84 +457,55 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* la */ std::vector lines(z.size()); - { - // queue all the facet indices - std::queue queue; - boost::mutex queue_mutex, lines_mutex; - for (size_t i = 0; i < this->mesh->stl.stats.number_of_facets; ++i) queue.push(i); - - boost::thread_group workers; - for (int i = 0; i < boost::thread::hardware_concurrency(); i++) - workers.add_thread(new boost::thread(&TriangleMeshSlicer::_slice_do, this, - &queue, &queue_mutex, &lines, &lines_mutex, z)); - workers.join_all(); + boost::mutex lines_mutex; + parallelize( + 0, + this->mesh->stl.stats.number_of_facets-1, + boost::bind(&TriangleMeshSlicer::_slice_do, this, _1, &lines, &lines_mutex, z) + ); } // v_scaled_shared could be freed here // build loops layers->resize(z.size()); - { - // queue all the layer numbers - std::queue queue; - boost::mutex queue_mutex; - for (size_t i = 0; i < lines.size(); ++i) queue.push(i); - - // We don't use a mutex for lines because workers are only writing the skip property - // and no workers work on the same layer (i.e. item of 'lines'). - boost::thread_group workers; - for (int i = 0; i < boost::thread::hardware_concurrency(); i++) - workers.add_thread(new boost::thread(&TriangleMeshSlicer::_make_loops_do, this, - &queue, &queue_mutex, &lines, layers)); - workers.join_all(); - } + parallelize( + 0, + lines.size()-1, + boost::bind(&TriangleMeshSlicer::_make_loops_do, this, _1, &lines, layers) + ); } void -TriangleMeshSlicer::_slice_do(std::queue* queue, boost::mutex* queue_mutex, - std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const +TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector* lines, boost::mutex* lines_mutex, + const std::vector &z) const { - //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; + const stl_facet &facet = this->mesh->stl.facet_start[facet_idx]; - while (true) { - int facet_idx; - { - boost::lock_guard l(*queue_mutex); - if (queue->empty()) return; - facet_idx = queue->front(); - queue->pop(); - } - //std::cout << " Facet " << facet_idx << " (" << boost::this_thread::get_id() << ")" << std::endl; - - const stl_facet &facet = this->mesh->stl.facet_start[facet_idx]; - - // find facet extents - const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z)); - const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z)); - - #ifdef SLIC3R_DEBUG - printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, - facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z, - facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z, - facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z); - printf("z: min = %.2f, max = %.2f\n", min_z, max_z); - #endif - - // find layer extents - std::vector::const_iterator min_layer, max_layer; - min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z - max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z - #ifdef SLIC3R_DEBUG - printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin())); - #endif - - for (std::vector::const_iterator it = min_layer; it != max_layer + 1; ++it) { - std::vector::size_type layer_idx = it - z.begin(); - this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &(*lines)[layer_idx], lines_mutex); - } - - boost::this_thread::interruption_point(); + // find facet extents + const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z)); + const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z)); + + #ifdef SLIC3R_DEBUG + printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, + facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z, + facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z, + facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z); + printf("z: min = %.2f, max = %.2f\n", min_z, max_z); + #endif + + // find layer extents + std::vector::const_iterator min_layer, max_layer; + min_layer = std::lower_bound(z.begin(), z.end(), min_z); // first layer whose slice_z is >= min_z + max_layer = std::upper_bound(z.begin() + (min_layer - z.begin()), z.end(), max_z) - 1; // last layer whose slice_z is <= max_z + #ifdef SLIC3R_DEBUG + printf("layers: min = %d, max = %d\n", (int)(min_layer - z.begin()), (int)(max_layer - z.begin())); + #endif + + for (std::vector::const_iterator it = min_layer; it != max_layer + 1; ++it) { + std::vector::size_type layer_idx = it - z.begin(); + this->slice_facet(*it / SCALING_FACTOR, facet, facet_idx, min_z, max_z, &(*lines)[layer_idx], lines_mutex); } } @@ -678,23 +650,9 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int } void -TriangleMeshSlicer::_make_loops_do(std::queue* queue, boost::mutex* queue_mutex, - std::vector* lines, std::vector* layers) const +TriangleMeshSlicer::_make_loops_do(size_t i, std::vector* lines, std::vector* layers) const { - //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; - - while (true) { - size_t layer_id; - { - boost::lock_guard l(*queue_mutex); - if (queue->empty()) return; - layer_id = queue->front(); - queue->pop(); - } - //std::cout << " Layer " << layer_id << " (" << boost::this_thread::get_id() << ")" << std::endl; - this->make_loops((*lines)[layer_id], &(*layers)[layer_id]); - boost::this_thread::interruption_point(); - } + this->make_loops((*lines)[i], &(*layers)[i]); } void diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 0225d7f9b..2c538c3f5 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -3,7 +3,6 @@ #include "libslic3r.h" #include -#include #include #include #include "BoundingBox.hpp" @@ -105,10 +104,8 @@ class TriangleMeshSlicer typedef std::vector< std::vector > t_facets_edges; t_facets_edges facets_edges; stl_vertex* v_scaled_shared; - void _slice_do(std::queue* queue, boost::mutex* queue_mutex, - std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const; - void _make_loops_do(std::queue* queue, boost::mutex* queue_mutex, - std::vector* lines, std::vector* layers) const; + void _slice_do(size_t facet_idx, std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const; + void _make_loops_do(size_t i, std::vector* lines, std::vector* layers) const; void make_loops(std::vector &lines, Polygons* loops) const; void make_expolygons(const Polygons &loops, ExPolygons* slices) const; void make_expolygons_simple(std::vector &lines, ExPolygons* slices) const; diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index d35018c12..ad8047ac8 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -4,8 +4,10 @@ // this needs to be included early for MSVC (listing it in Build.PL is not enough) #include #include +#include #include #include +#include #define SLIC3R_VERSION "1.3.0-dev" @@ -34,19 +36,6 @@ typedef long coord_t; typedef double coordf_t; -namespace Slic3r { - -enum Axis { X=0, Y, Z }; - -template -inline void append_to(std::vector &dst, const std::vector &src) -{ - dst.insert(dst.end(), src.begin(), src.end()); -} - -} -using namespace Slic3r; - /* Implementation of CONFESS("foo"): */ #ifdef _MSC_VER #define CONFESS(...) confess_at(__FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) @@ -69,4 +58,56 @@ void confess_at(const char *file, int line, const char *func, const char *pat, . #define STDMOVE(WHAT) (WHAT) #endif +namespace Slic3r { + +enum Axis { X=0, Y, Z }; + +template +inline void append_to(std::vector &dst, const std::vector &src) +{ + dst.insert(dst.end(), src.begin(), src.end()); +} + +template void +_parallelize_do(std::queue* queue, boost::mutex* queue_mutex, boost::function func) +{ + //std::cout << "THREAD STARTED: " << boost::this_thread::get_id() << std::endl; + while (true) { + T i; + { + boost::lock_guard l(*queue_mutex); + if (queue->empty()) return; + i = queue->front(); + queue->pop(); + } + //std::cout << " Thread " << boost::this_thread::get_id() << " processing item " << i << std::endl; + func(i); + boost::this_thread::interruption_point(); + } +} + +template void +parallelize(std::queue queue, boost::function func, + int threads_count = boost::thread::hardware_concurrency()) +{ + boost::mutex queue_mutex; + boost::thread_group workers; + for (int i = 0; i < threads_count; i++) + workers.add_thread(new boost::thread(&_parallelize_do, &queue, &queue_mutex, func)); + workers.join_all(); +} + +template void +parallelize(T start, T end, boost::function func, + int threads_count = boost::thread::hardware_concurrency()) +{ + std::queue queue; + for (T i = start; i <= end; ++i) queue.push(i); + parallelize(queue, func, threads_count); +} + +} // namespace Slic3r + +using namespace Slic3r; + #endif From d47f6d30af1e9c6d03583bd9ce969c40ae3c5794 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 26 Nov 2016 16:58:23 +0100 Subject: [PATCH 100/225] Ported detect_surfaces_type() to XS/C++ --- lib/Slic3r/Print/Object.pm | 141 --------------------- xs/src/libslic3r/Layer.cpp | 6 - xs/src/libslic3r/Layer.hpp | 3 +- xs/src/libslic3r/Print.cpp | 6 - xs/src/libslic3r/Print.hpp | 4 +- xs/src/libslic3r/PrintObject.cpp | 164 +++++++++++++++++++++++++ xs/src/libslic3r/SurfaceCollection.hpp | 2 + xs/xsp/Print.xsp | 1 + 8 files changed, 172 insertions(+), 155 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 30f684034..75c9877ef 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -467,147 +467,6 @@ sub _support_material { ); } -sub detect_surfaces_type { - my $self = shift; - Slic3r::debugf "Detecting solid surfaces...\n"; - - for my $region_id (0 .. ($self->print->region_count-1)) { - for my $i (0 .. ($self->layer_count - 1)) { - my $layerm = $self->get_layer($i)->regions->[$region_id]; - - # prepare a reusable subroutine to make surface differences - my $difference = sub { - my ($subject, $clip, $result_type) = @_; - my $diff = diff( - [ map @$_, @$subject ], - [ map @$_, @$clip ], - 1, - ); - - # collapse very narrow parts (using the safety offset in the diff is not enough) - my $offset = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER)->scaled_width / 10; - return map Slic3r::Surface->new(expolygon => $_, surface_type => $result_type), - @{ offset2_ex($diff, -$offset, +$offset) }; - }; - - # comparison happens against the *full* slices (considering all regions) - # unless internal shells are requested - my $upper_layer = $i < $self->layer_count - 1 ? $self->get_layer($i+1) : undef; - my $lower_layer = $i > 0 ? $self->get_layer($i-1) : undef; - - # find top surfaces (difference between current surfaces - # of current layer and upper one) - my @top = (); - if ($upper_layer) { - my $upper_slices = $self->config->interface_shells - ? [ map $_->expolygon, @{$upper_layer->regions->[$region_id]->slices} ] - : $upper_layer->slices; - - @top = $difference->( - [ map $_->expolygon, @{$layerm->slices} ], - $upper_slices, - S_TYPE_TOP, - ); - } else { - # if no upper layer, all surfaces of this one are solid - # we clone surfaces because we're going to clear the slices collection - @top = map $_->clone, @{$layerm->slices}; - $_->surface_type(S_TYPE_TOP) for @top; - } - - # find bottom surfaces (difference between current surfaces - # of current layer and lower one) - my @bottom = (); - if ($lower_layer) { - # any surface lying on the void is a true bottom bridge - push @bottom, $difference->( - [ map $_->expolygon, @{$layerm->slices} ], - $lower_layer->slices, - S_TYPE_BOTTOMBRIDGE, - ); - - # if we have soluble support material, don't bridge - if ($self->config->support_material && $self->config->support_material_contact_distance == 0) { - $_->surface_type(S_TYPE_BOTTOM) for @bottom; - } - - # if user requested internal shells, we need to identify surfaces - # lying on other slices not belonging to this region - if ($self->config->interface_shells) { - # non-bridging bottom surfaces: any part of this layer lying - # on something else, excluding those lying on our own region - my $supported = intersection_ex( - [ map @{$_->expolygon}, @{$layerm->slices} ], - [ map @$_, @{$lower_layer->slices} ], - ); - push @bottom, $difference->( - $supported, - [ map $_->expolygon, @{$lower_layer->regions->[$region_id]->slices} ], - S_TYPE_BOTTOM, - ); - } - } else { - # if no lower layer, all surfaces of this one are solid - # we clone surfaces because we're going to clear the slices collection - @bottom = map $_->clone, @{$layerm->slices}; - - # if we have raft layers, consider bottom layer as a bridge - # just like any other bottom surface lying on the void - if ($self->config->raft_layers > 0 && $self->config->support_material_contact_distance > 0) { - $_->surface_type(S_TYPE_BOTTOMBRIDGE) for @bottom; - } else { - $_->surface_type(S_TYPE_BOTTOM) for @bottom; - } - } - - # now, if the object contained a thin membrane, we could have overlapping bottom - # and top surfaces; let's do an intersection to discover them and consider them - # as bottom surfaces (to allow for bridge detection) - if (@top && @bottom) { - my $overlapping = intersection_ex([ map $_->p, @top ], [ map $_->p, @bottom ]); - Slic3r::debugf " layer %d contains %d membrane(s)\n", $layerm->layer->id, scalar(@$overlapping) - if $Slic3r::debug; - @top = $difference->([map $_->expolygon, @top], $overlapping, S_TYPE_TOP); - } - - # find internal surfaces (difference between top/bottom surfaces and others) - my @internal = $difference->( - [ map $_->expolygon, @{$layerm->slices} ], - [ map $_->expolygon, @top, @bottom ], - S_TYPE_INTERNAL, - ); - - # save surfaces to layer - $layerm->slices->clear; - $layerm->slices->append($_) for (@bottom, @top, @internal); - - Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n", - $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug; - } - - # clip surfaces to the fill boundaries - foreach my $layer (@{$self->layers}) { - my $layerm = $layer->regions->[$region_id]; - - # Note: this method should be idempotent, but fill_surfaces gets modified - # in place. However we're now only using its boundaries (which are invariant) - # so we're safe. This guarantees idempotence of prepare_infill() also in case - # that combine_infill() turns some fill_surface into VOID surfaces. - my $fill_boundaries = [ map $_->clone->p, @{$layerm->fill_surfaces} ]; - $layerm->fill_surfaces->clear; - foreach my $surface (@{$layerm->slices}) { - my $intersection = intersection_ex( - [ $surface->p ], - $fill_boundaries, - ); - $layerm->fill_surfaces->append($_) - for map Slic3r::Surface->new(expolygon => $_, surface_type => $surface->surface_type), - @$intersection; - } - } - } -} - # Idempotence of this method is guaranteed by the fact that we don't remove things from # fill_surfaces but we only turn them into VOID surfaces, thus preserving the boundaries. sub clip_fill_surfaces { diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp index da71552c9..3b944c97e 100644 --- a/xs/src/libslic3r/Layer.cpp +++ b/xs/src/libslic3r/Layer.cpp @@ -61,12 +61,6 @@ Layer::clear_regions() this->delete_region(i); } -LayerRegion* -Layer::get_region(int idx) -{ - return this->regions.at(idx); -} - LayerRegion* Layer::add_region(PrintRegion* print_region) { diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp index 59badcb1f..7355ed919 100644 --- a/xs/src/libslic3r/Layer.hpp +++ b/xs/src/libslic3r/Layer.hpp @@ -97,7 +97,8 @@ class Layer { size_t region_count() const; - LayerRegion* get_region(int idx); + LayerRegion* get_region(size_t idx) { return this->regions.at(idx); }; + const LayerRegion* get_region(size_t idx) const { return this->regions.at(idx); }; LayerRegion* add_region(PrintRegion* print_region); void make_slices(); diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index c97ca30f0..a4b9818ba 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -133,12 +133,6 @@ Print::clear_regions() this->delete_region(i); } -PrintRegion* -Print::get_region(size_t idx) -{ - return regions.at(idx); -} - PrintRegion* Print::add_region() { diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 0e3de68ef..50b68d2a5 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -133,6 +133,7 @@ class PrintObject bool invalidate_all_steps(); bool has_support_material() const; + void detect_surfaces_type(); void process_external_surfaces(); void bridge_over_infill(); void _make_perimeters(); @@ -181,7 +182,8 @@ class Print bool reload_model_instances(); // methods for handling regions - PrintRegion* get_region(size_t idx); + PrintRegion* get_region(size_t idx) { return this->regions.at(idx); }; + const PrintRegion* get_region(size_t idx) const { return this->regions.at(idx); }; PrintRegion* add_region(); // methods for handling state diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index e3a21bb80..649c468fb 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -340,6 +340,170 @@ PrintObject::has_support_material() const || this->config.support_material_enforce_layers > 0; } +// This function analyzes slices of a region (SurfaceCollection slices). +// Each region slice (instance of Surface) is analyzed, whether it is supported or whether it is the top surface. +// Initially all slices are of type S_TYPE_INTERNAL. +// Slices are compared against the top / bottom slices and regions and classified to the following groups: +// S_TYPE_TOP - Part of a region, which is not covered by any upper layer. This surface will be filled with a top solid infill. +// S_TYPE_BOTTOMBRIDGE - Part of a region, which is not fully supported, but it hangs in the air, or it hangs losely on a support or a raft. +// S_TYPE_BOTTOM - Part of a region, which is not supported by the same region, but it is supported either by another region, or by a soluble interface layer. +// S_TYPE_INTERNAL - Part of a region, which is supported by the same region type. +// If a part of a region is of S_TYPE_BOTTOM and S_TYPE_TOP, the S_TYPE_BOTTOM wins. +void +PrintObject::detect_surfaces_type() +{ + //Slic3r::debugf "Detecting solid surfaces...\n"; + FOREACH_REGION(this->_print, region) { + size_t region_id = region - this->_print->regions.begin(); + + FOREACH_LAYER(this, layer_it) { + size_t layer_idx = layer_it - this->layers.begin(); + Layer &layer = **layer_it; + LayerRegion &layerm = *layer.get_region(region_id); + // comparison happens against the *full* slices (considering all regions) + // unless internal shells are requested + + const Layer* upper_layer = layer_idx < (this->layer_count()-1) ? this->get_layer(layer_idx+1) : NULL; + const Layer* lower_layer = layer_idx > 0 ? this->get_layer(layer_idx-1) : NULL; + + // collapse very narrow parts (using the safety offset in the diff is not enough) + const float offset = layerm.flow(frExternalPerimeter).scaled_width() / 10.f; + + const Polygons layerm_slices_surfaces = layerm.slices; + + // find top surfaces (difference between current surfaces + // of current layer and upper one) + SurfaceCollection top; + if (upper_layer != NULL) { + const Polygons upper_slices = this->config.interface_shells.value + ? (Polygons)upper_layer->get_region(region_id)->slices + : (Polygons)upper_layer->slices; + + top.append( + offset2_ex( + diff(layerm_slices_surfaces, upper_slices, true), + -offset, offset + ), + stTop + ); + } else { + // if no upper layer, all surfaces of this one are solid + // we clone surfaces because we're going to clear the slices collection + top = layerm.slices; + for (Surfaces::iterator it = top.surfaces.begin(); it != top.surfaces.end(); ++ it) + it->surface_type = stTop; + } + + // find bottom surfaces (difference between current surfaces + // of current layer and lower one) + SurfaceCollection bottom; + if (lower_layer != NULL) { + // If we have soluble support material, don't bridge. The overhang will be squished against a soluble layer separating + // the support from the print. + const SurfaceType surface_type_bottom = + (this->config.support_material.value && this->config.support_material_contact_distance.value == 0) + ? stBottom + : stBottomBridge; + + // Any surface lying on the void is a true bottom bridge (an overhang) + bottom.append( + offset2_ex( + diff(layerm_slices_surfaces, lower_layer->slices, true), + -offset, offset + ), + surface_type_bottom + ); + + // if user requested internal shells, we need to identify surfaces + // lying on other slices not belonging to this region + if (this->config.interface_shells) { + // non-bridging bottom surfaces: any part of this layer lying + // on something else, excluding those lying on our own region + bottom.append( + offset2_ex( + diff( + intersection(layerm_slices_surfaces, lower_layer->slices), // supported + lower_layer->get_region(region_id)->slices, + true + ), + -offset, offset + ), + stBottom + ); + } + } else { + // if no lower layer, all surfaces of this one are solid + // we clone surfaces because we're going to clear the slices collection + bottom = layerm.slices; + + // if we have raft layers, consider bottom layer as a bridge + // just like any other bottom surface lying on the void + const SurfaceType surface_type_bottom = + (this->config.raft_layers.value > 0 && this->config.support_material_contact_distance.value > 0) + ? stBottomBridge + : stBottom; + for (Surfaces::iterator it = bottom.surfaces.begin(); it != bottom.surfaces.end(); ++ it) + it->surface_type = surface_type_bottom; + } + + // now, if the object contained a thin membrane, we could have overlapping bottom + // and top surfaces; let's do an intersection to discover them and consider them + // as bottom surfaces (to allow for bridge detection) + if (!top.empty() && !bottom.empty()) { + const Polygons top_polygons = to_polygons(STDMOVE(top)); + top.clear(); + top.append( + offset2_ex(diff(top_polygons, bottom, true), -offset, offset), + stTop + ); + } + + // save surfaces to layer + layerm.slices.clear(); + layerm.slices.append(STDMOVE(top)); + layerm.slices.append(STDMOVE(bottom)); + + // find internal surfaces (difference between top/bottom surfaces and others) + { + Polygons topbottom = top; append_to(topbottom, (Polygons)bottom); + + layerm.slices.append( + offset2_ex( + diff(layerm_slices_surfaces, topbottom, true), + -offset, offset + ), + stInternal + ); + } + + /* + Slic3r::debugf " layer %d has %d bottom, %d top and %d internal surfaces\n", + $layerm->layer->id, scalar(@bottom), scalar(@top), scalar(@internal) if $Slic3r::debug; + */ + + } // for each layer of a region + + /* Fill in layerm->fill_surfaces by trimming the layerm->slices by the cummulative layerm->fill_surfaces. + Note: this method should be idempotent, but fill_surfaces gets modified + in place. However we're now only using its boundaries (which are invariant) + so we're safe. This guarantees idempotence of prepare_infill() also in case + that combine_infill() turns some fill_surface into VOID surfaces. */ + FOREACH_LAYER(this, layer_it) { + LayerRegion &layerm = *(*layer_it)->get_region(region_id); + + const Polygons fill_boundaries = layerm.fill_surfaces; + layerm.fill_surfaces.clear(); + for (Surfaces::const_iterator surface = layerm.slices.surfaces.begin(); + surface != layerm.slices.surfaces.end(); ++ surface) { + layerm.fill_surfaces.append( + intersection_ex(*surface, fill_boundaries), + surface->surface_type + ); + } + } + } +} + void PrintObject::process_external_surfaces() { diff --git a/xs/src/libslic3r/SurfaceCollection.hpp b/xs/src/libslic3r/SurfaceCollection.hpp index 42d26b892..e9d42fc49 100644 --- a/xs/src/libslic3r/SurfaceCollection.hpp +++ b/xs/src/libslic3r/SurfaceCollection.hpp @@ -28,6 +28,8 @@ class SurfaceCollection void append(const ExPolygons &src, const Surface &templ); void append(const ExPolygons &src, SurfaceType surfaceType); size_t polygons_count() const; + bool empty() const { return this->surfaces.empty(); }; + void clear() { this->surfaces.clear(); }; }; } diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index dec2e115e..b37890380 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -107,6 +107,7 @@ _constant() void set_step_started(PrintObjectStep step) %code%{ THIS->state.set_started(step); %}; + void detect_surfaces_type(); void process_external_surfaces(); void bridge_over_infill(); void _make_perimeters(); From b2562a9f3122279a419628227ac85c373766fd87 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sat, 26 Nov 2016 19:55:43 -0600 Subject: [PATCH 101/225] Permit sliding modifer meshes around in the Settings dialog. --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 104 +++++++++++++++++++++- 1 file changed, 101 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 4882233a6..05ed657a0 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -22,6 +22,18 @@ sub new { my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); my $object = $self->{model_object} = $params{model_object}; + + # Save state for sliders. + $self->{move_options} = { + x => 0, + y => 0, + z => 0, + }; + $self->{last_coords} = { + x => 0, + y => 0, + z => 0, + }; # create TreeCtrl my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100], @@ -61,12 +73,61 @@ sub new { $self->{settings_panel} = Slic3r::GUI::Plater::OverrideSettingsPanel->new($self, on_change => sub { $self->{part_settings_changed} = 1; }); my $settings_sizer = Wx::StaticBoxSizer->new($self->{staticbox} = Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL); $settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0); + + my $optgroup; + $optgroup = $self->{optgroup} = Slic3r::GUI::OptionsGroup->new( + parent => $self, + title => 'Move', + on_change => sub { + my ($opt_id) = @_; + # There seems to be an issue with wxWidgets 3.0.2/3.0.3, where the slider + # genates tens of events for a single value change. + # Only trigger the recalculation if the value changes + # or a live preview was activated and the mesh cut is not valid yet. + if ($self->{move_options}{$opt_id} != $optgroup->get_value($opt_id)) { + $self->{move_options}{$opt_id} = $optgroup->get_value($opt_id); + wxTheApp->CallAfter(sub { + $self->_update; + }); + } + }, + label_width => 20, + ); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'x', + type => 'slider', + label => 'X', + default => 0, + min => -($self->{model_object}->bounding_box->size->x)*4, + max => $self->{model_object}->bounding_box->size->x*4, + full_width => 1, + )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'y', + type => 'slider', + label => 'Y', + default => 0, + min => -($self->{model_object}->bounding_box->size->y)*4, + max => $self->{model_object}->bounding_box->size->y*4, + full_width => 1, + )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'z', + type => 'slider', + label => 'Z', + default => 0, + min => -($self->{model_object}->bounding_box->size->z)*4, + max => $self->{model_object}->bounding_box->size->z*4, + full_width => 1, + )); + # left pane with tree my $left_sizer = Wx::BoxSizer->new(wxVERTICAL); $left_sizer->Add($tree, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10); $left_sizer->Add($buttons_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10); $left_sizer->Add($settings_sizer, 1, wxEXPAND | wxALL, 0); + $left_sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); # right pane with preview canvas my $canvas; @@ -179,6 +240,21 @@ sub selection_changed { $self->{btn_delete}->Disable; $self->{settings_panel}->disable; $self->{settings_panel}->set_config(undef); + + # reset move sliders + $self->{optgroup}->set_value("x", 0); + $self->{optgroup}->set_value("y", 0); + $self->{optgroup}->set_value("z", 0); + $self->{move_options} = { + x => 0, + y => 0, + z => 0, + }; + $self->{last_coords} = { + x => 0, + y => 0, + z => 0, + }; if (my $itemData = $self->get_selection) { my ($config, @opt_keys); @@ -254,17 +330,17 @@ sub on_btn_load { sub on_btn_delete { my ($self) = @_; - + my $itemData = $self->get_selection; if ($itemData && $itemData->{type} eq 'volume') { my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}]; - + # if user is deleting the last solid part, throw error if (!$volume->modifier && scalar(grep !$_->modifier, @{$self->{model_object}->volumes}) == 1) { Slic3r::GUI::show_error($self, "You can't delete the last solid part from this object."); return; } - + $self->{model_object}->delete_volume($itemData->{volume_id}); $self->{parts_changed} = 1; } @@ -310,4 +386,26 @@ sub PartSettingsChanged { return $self->{part_settings_changed}; } +sub _update { + my ($self) = @_; + my ($m_x, $m_y, $m_z) = ($self->{move_options}{x}, $self->{move_options}{y}, $self->{move_options}{z}); + my ($l_x, $l_y, $l_z) = ($self->{last_coords}{x}, $self->{last_coords}{y}, $self->{last_coords}{z}); + + my $itemData = $self->get_selection; + if ($itemData && $itemData->{type} eq 'volume') { + my $d = Slic3r::Pointf3->new($m_x - $l_x, $m_y - $l_y, $m_z - $l_z); + my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}]; + $volume->mesh->translate(@{$d}); + $self->{last_coords}{x} = $m_x; + $self->{last_coords}{y} = $m_y; + $self->{last_coords}{z} = $m_z; + } + + my @objects = (); + push @objects, $self->{model_object}; + $self->{canvas}->reset_objects; + $self->{canvas}->load_object($_, undef, [0]) for @objects; + $self->{canvas}->Render; +} + 1; From 89dab6a19dbebb9885d8f456d98789db481d2ed4 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sat, 26 Nov 2016 19:57:35 -0600 Subject: [PATCH 102/225] Anonymous object supports. Initial implementation provides for a box of arbitrary size. --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 110 ++++++++++++++++++++ lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 29 +++++- lib/Slic3r/Test.pm | 8 ++ 3 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm new file mode 100644 index 000000000..e0de07309 --- /dev/null +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -0,0 +1,110 @@ +# Generate an anonymous or "lambda" 3D object. This gets used with the Add Generic option in Settings. +# + +package Slic3r::GUI::Plater::LambdaObjectDialog; +use strict; +use warnings; +use utf8; + +use Slic3r::Geometry qw(PI X); +use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL wxCB_READONLY wxTE_PROCESS_TAB); +use Wx::Event qw(EVT_CLOSE EVT_BUTTON EVT_COMBOBOX EVT_TEXT); +use Scalar::Util qw(looks_like_number); +use base 'Wx::Dialog'; + +sub new { + my $class = shift; + my ($parent, %params) = @_; + my $self = $class->SUPER::new($parent, -1, "Lambda Object", wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); + # Note whether the window was already closed, so a pending update is not executed. + $self->{already_closed} = 0; + $self->{object_parameters} = { + type => "box", + dim => [1, 1, 1], + }; + + $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); + my $button_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + my $buttons = $self->CreateStdDialogButtonSizer(wxOK); + EVT_BUTTON($self, wxID_OK, sub { + # validate user input + return if !$self->CanClose; + + $self->EndModal(wxID_OK); + $self->Destroy; + }); + $button_sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + + my @options = ("box"); + $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); + + my $sbox = Wx::StaticBox->new($self, -1, '', wxDefaultPosition, wxDefaultSize, 0, 'sbox'); + my $cube_dim_sizer = Wx::StaticBoxSizer->new($sbox, wxVERTICAL); + { + my $opt_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + { + my $lbl = Wx::StaticText->new($self, 2, "X", wxDefaultPosition, Wx::Size->new(10,-1)); + $self->{dim_x} = Wx::TextCtrl->new($self, 2, "1", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_TAB); + $opt_sizer->Add($lbl, 1, wxRIGHT , 8); + $opt_sizer->Add($self->{dim_x}); + + } + $cube_dim_sizer->Add($opt_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10); + $opt_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + { + my $lbl = Wx::StaticText->new($self, -1, "Y", wxDefaultPosition, Wx::Size->new(10,-1)); + $self->{dim_y} = Wx::TextCtrl->new($self, 2, "1", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_TAB); + $opt_sizer->Add($lbl, 1, wxRIGHT , 8); + $opt_sizer->Add($self->{dim_y}); + } + $cube_dim_sizer->Add($opt_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10); + $opt_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + { + my $lbl = Wx::StaticText->new($self, -1, "Z", wxDefaultPosition, Wx::Size->new(10,-1)); + $self->{dim_z} = Wx::TextCtrl->new($self, 2, "1", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_TAB); + $opt_sizer->Add($lbl, 1, wxRIGHT , 8); + $opt_sizer->Add($self->{dim_z}); + } + $cube_dim_sizer->Add($opt_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10); + EVT_TEXT($self, 2, sub { + if (!looks_like_number($self->{dim_x}->GetValue)) { + return 0; + } + if (!looks_like_number($self->{dim_y}->GetValue)) { + return 0; + } + if (!looks_like_number($self->{dim_z}->GetValue)) { + return 0; + } + if ($self->{dim_x}->GetValue() > 0) { + $self->{object_parameters}->{dim}[0] = $self->{dim_x}->GetValue; + } + if ($self->{dim_y}->GetValue() > 0) { + $self->{object_parameters}->{dim}[1] = $self->{dim_y}->GetValue; + } + if ($self->{dim_z}->GetValue() > 0) { + $self->{object_parameters}->{dim}[2] = $self->{dim_z}->GetValue; + } + }); + } + EVT_COMBOBOX($self, 1, sub{ + $self->{object_parameters}->{type} = $self->{type}->GetValue(); + }); + $self->{sizer}->Add($self->{type}, 0, wxEXPAND, 3); + $self->{sizer}->Add($cube_dim_sizer, 0, wxEXPAND, 10); + $self->{sizer}->Add($button_sizer); + $self->SetSizer($self->{sizer}); + $self->{sizer}->Fit($self); + $self->{sizer}->SetSizeHints($self); + + + return $self; +} +sub CanClose { + return 1; +} +sub ObjectParameter { + my ($self) = @_; + return $self->{object_parameters}; +} +1; diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 05ed657a0..c4ee24f0b 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -53,10 +53,12 @@ sub new { # buttons $self->{btn_load_part} = Wx::Button->new($self, -1, "Load part…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); $self->{btn_load_modifier} = Wx::Button->new($self, -1, "Load modifier…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); + $self->{btn_load_lambda_modifier} = Wx::Button->new($self, -1, "Load generic…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); $self->{btn_delete} = Wx::Button->new($self, -1, "Delete part", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); if ($Slic3r::GUI::have_button_icons) { $self->{btn_load_part}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG)); $self->{btn_load_modifier}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG)); + $self->{btn_load_lambda_modifier}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG)); $self->{btn_delete}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_delete.png"), wxBITMAP_TYPE_PNG)); } @@ -64,9 +66,11 @@ sub new { my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL); $buttons_sizer->Add($self->{btn_load_part}, 0); $buttons_sizer->Add($self->{btn_load_modifier}, 0); + $buttons_sizer->Add($self->{btn_load_lambda_modifier}, 0); $buttons_sizer->Add($self->{btn_delete}, 0); $self->{btn_load_part}->SetFont($Slic3r::GUI::small_font); $self->{btn_load_modifier}->SetFont($Slic3r::GUI::small_font); + $self->{btn_load_lambda_modifier}->SetFont($Slic3r::GUI::small_font); $self->{btn_delete}->SetFont($Slic3r::GUI::small_font); # part settings panel @@ -145,7 +149,7 @@ sub new { $canvas->load_object($self->{model_object}, undef, [0]); $canvas->set_auto_bed_shape; - $canvas->SetSize([500,500]); + $canvas->SetSize([500,700]); $canvas->zoom_to_volumes; } @@ -168,6 +172,7 @@ sub new { }); EVT_BUTTON($self, $self->{btn_load_part}, sub { $self->on_btn_load(0) }); EVT_BUTTON($self, $self->{btn_load_modifier}, sub { $self->on_btn_load(1) }); + EVT_BUTTON($self, $self->{btn_load_lambda_modifier}, sub { $self->on_btn_lambda(1) }); EVT_BUTTON($self, $self->{btn_delete}, \&on_btn_delete); $self->reload_tree; @@ -328,6 +333,28 @@ sub on_btn_load { $self->_parts_changed; } +sub on_btn_lambda { + my ($self, $is_modifier) = @_; + + my $dlg = Slic3r::GUI::Plater::LambdaObjectDialog->new($self); + $dlg->ShowModal(); + my $params = $dlg->ObjectParameter; + my $name = "lambda-".$params->{"type"}; + + my $new_volume = $self->{model_object}->add_volume(mesh => Slic3r::Test::mesh($params->{"type"}, dim=>$params->{"dim"}), material_id=>"generic"); + $new_volume->set_modifier($is_modifier); + $new_volume->set_name($name); + + # apply the same translation we applied to the object + $new_volume->mesh->translate(@{$self->{model_object}->origin_translation}); + + # set a default extruder value, since user can't add it manually + $new_volume->config->set_ifndef('extruder', 0); + + $self->{parts_changed} = 1; + $self->_parts_changed; +} + sub on_btn_delete { my ($self) = @_; diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index 1d8431b14..4b4d35520 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -27,6 +27,14 @@ sub mesh { $facets = [ [0,1,2], [0,2,3], [4,5,6], [4,6,7], [0,4,7], [0,7,1], [1,7,6], [1,6,2], [2,6,5], [2,5,3], [4,0,3], [4,3,5], ], + } elsif ($name eq 'box') { + my ($x, $y, $z) = @{ $params{"dim"} }; + $vertices = [ + [$x,$y,0], [$x,0,0], [0,0,0], [0,$y,0], [$x,$y,$z], [0,$y,$z], [0,0,$z], [$x,0,$z], + ]; + $facets = [ + [0,1,2], [0,2,3], [4,5,6], [4,6,7], [0,4,7], [0,7,1], [1,7,6], [1,6,2], [2,6,5], [2,5,3], [4,0,3], [4,3,5], + ], } elsif ($name eq 'cube_with_hole') { $vertices = [ [0,0,0],[0,0,10],[0,20,0],[0,20,10],[20,0,0],[20,0,10],[5,5,0],[15,5,0],[5,15,0],[20,20,0],[15,15,0],[20,20,10],[5,5,10],[5,15,10],[15,5,10],[15,15,10] From a41fcb90666d715a05c368df1349255a7457e4b4 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sat, 26 Nov 2016 22:13:55 -0600 Subject: [PATCH 103/225] Update main plater view after moving mesh --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index c4ee24f0b..ee812a5a6 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -428,6 +428,7 @@ sub _update { $self->{last_coords}{z} = $m_z; } + $self->{parts_changed} = 1; my @objects = (); push @objects, $self->{model_object}; $self->{canvas}->reset_objects; From 9abd5eecee8ac835d154e1d46826fac41365a733 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sat, 26 Nov 2016 23:03:51 -0600 Subject: [PATCH 104/225] Added a cancel button, and rewrote initial option code to reuse OptionGroup. --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 116 ++++++++++---------- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 6 +- 2 files changed, 64 insertions(+), 58 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm index e0de07309..49d1a1888 100644 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -25,7 +25,10 @@ sub new { $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); my $button_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - my $buttons = $self->CreateStdDialogButtonSizer(wxOK); + my $button_ok = $self->CreateStdDialogButtonSizer(wxOK); + my $button_cancel = $self->CreateStdDialogButtonSizer(wxCANCEL); + $button_sizer->Add($button_ok); + $button_sizer->Add($button_cancel); EVT_BUTTON($self, wxID_OK, sub { # validate user input return if !$self->CanClose; @@ -33,66 +36,67 @@ sub new { $self->EndModal(wxID_OK); $self->Destroy; }); - $button_sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - + EVT_BUTTON($self, wxID_CANCEL, sub { + # validate user input + return if !$self->CanClose; + + $self->EndModal(wxID_CANCEL); + $self->Destroy; + }); + + my $optgroup; + $optgroup = $self->{optgroup} = Slic3r::GUI::OptionsGroup->new( + parent => $self, + title => 'Add Generic...', + on_change => sub { + # Do validation + my ($opt_id) = @_; + if ($opt_id == 0 || $opt_id == 1 || $opt_id == 2) { + if (!looks_like_number($optgroup->get_value($opt_id))) { + return 0; + } + } + $self->{object_parameters}->{dim}[$opt_id] = $optgroup->get_value($opt_id); + }, + label_width => 100, + ); my @options = ("box"); $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); - - my $sbox = Wx::StaticBox->new($self, -1, '', wxDefaultPosition, wxDefaultSize, 0, 'sbox'); - my $cube_dim_sizer = Wx::StaticBoxSizer->new($sbox, wxVERTICAL); - { - my $opt_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - { - my $lbl = Wx::StaticText->new($self, 2, "X", wxDefaultPosition, Wx::Size->new(10,-1)); - $self->{dim_x} = Wx::TextCtrl->new($self, 2, "1", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_TAB); - $opt_sizer->Add($lbl, 1, wxRIGHT , 8); - $opt_sizer->Add($self->{dim_x}); - - } - $cube_dim_sizer->Add($opt_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10); - $opt_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - { - my $lbl = Wx::StaticText->new($self, -1, "Y", wxDefaultPosition, Wx::Size->new(10,-1)); - $self->{dim_y} = Wx::TextCtrl->new($self, 2, "1", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_TAB); - $opt_sizer->Add($lbl, 1, wxRIGHT , 8); - $opt_sizer->Add($self->{dim_y}); - } - $cube_dim_sizer->Add($opt_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10); - $opt_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - { - my $lbl = Wx::StaticText->new($self, -1, "Z", wxDefaultPosition, Wx::Size->new(10,-1)); - $self->{dim_z} = Wx::TextCtrl->new($self, 2, "1", wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_TAB); - $opt_sizer->Add($lbl, 1, wxRIGHT , 8); - $opt_sizer->Add($self->{dim_z}); - } - $cube_dim_sizer->Add($opt_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10); - EVT_TEXT($self, 2, sub { - if (!looks_like_number($self->{dim_x}->GetValue)) { - return 0; - } - if (!looks_like_number($self->{dim_y}->GetValue)) { - return 0; - } - if (!looks_like_number($self->{dim_z}->GetValue)) { - return 0; - } - if ($self->{dim_x}->GetValue() > 0) { - $self->{object_parameters}->{dim}[0] = $self->{dim_x}->GetValue; - } - if ($self->{dim_y}->GetValue() > 0) { - $self->{object_parameters}->{dim}[1] = $self->{dim_y}->GetValue; - } - if ($self->{dim_z}->GetValue() > 0) { - $self->{object_parameters}->{dim}[2] = $self->{dim_z}->GetValue; - } - }); - } + #my @types = ("box"); + #$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + # opt_id => 'type', + # label => 'Type', + # type => 'select', + # values => \@types, + # default => 'box', + #)); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 0, + label => 'L', + type => 'f', + default => '1', + )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 1, + label => 'W', + type => 'f', + default => '1', + )); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 2, + label => 'H', + type => 'f', + default => '1', + )); EVT_COMBOBOX($self, 1, sub{ $self->{object_parameters}->{type} = $self->{type}->GetValue(); }); - $self->{sizer}->Add($self->{type}, 0, wxEXPAND, 3); - $self->{sizer}->Add($cube_dim_sizer, 0, wxEXPAND, 10); - $self->{sizer}->Add($button_sizer); + + + $optgroup->sizer->Add($self->{type}, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->{sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->{sizer}->Add($button_sizer,0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->SetSizer($self->{sizer}); $self->{sizer}->Fit($self); $self->{sizer}->SetSizeHints($self); diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index ee812a5a6..1fb0e3bd9 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -7,7 +7,7 @@ use warnings; use utf8; use File::Basename qw(basename); -use Wx qw(:misc :sizer :treectrl :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG +use Wx qw(:misc :sizer :treectrl :button wxTAB_TRAVERSAL wxSUNKEN_BORDER wxBITMAP_TYPE_PNG wxID_CANCEL wxTheApp); use Wx::Event qw(EVT_BUTTON EVT_TREE_ITEM_COLLAPSING EVT_TREE_SEL_CHANGED); use base 'Wx::Panel'; @@ -337,7 +337,9 @@ sub on_btn_lambda { my ($self, $is_modifier) = @_; my $dlg = Slic3r::GUI::Plater::LambdaObjectDialog->new($self); - $dlg->ShowModal(); + if ($dlg->ShowModal() == wxID_CANCEL) { + return; + } my $params = $dlg->ObjectParameter; my $name = "lambda-".$params->{"type"}; From cc9bae90a48b52a62b88c74ff09c41a812a86db4 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sat, 26 Nov 2016 23:16:50 -0600 Subject: [PATCH 105/225] Removed commented code. --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm index 49d1a1888..d6d4c3b09 100644 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -62,14 +62,7 @@ sub new { ); my @options = ("box"); $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); - #my @types = ("box"); - #$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - # opt_id => 'type', - # label => 'Type', - # type => 'select', - # values => \@types, - # default => 'box', - #)); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 0, label => 'L', From 11585b88d5a1063f28a17ae5a90698e2c23ddcda Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 27 Nov 2016 09:56:30 +0100 Subject: [PATCH 106/225] Updated Clipper to 6.4.0 --- xs/src/clipper.cpp | 599 ++++++++++++++++++++++++--------------------- xs/src/clipper.hpp | 64 ++--- 2 files changed, 357 insertions(+), 306 deletions(-) mode change 100644 => 100755 xs/src/clipper.cpp mode change 100644 => 100755 xs/src/clipper.hpp diff --git a/xs/src/clipper.cpp b/xs/src/clipper.cpp old mode 100644 new mode 100755 index bfdcd5d98..09657560f --- a/xs/src/clipper.cpp +++ b/xs/src/clipper.cpp @@ -1,8 +1,8 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.2.9 * -* Date : 16 February 2015 * +* Version : 6.4.0 * +* Date : 2 July 2015 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2015 * * * @@ -65,12 +65,11 @@ static int const Skip = -2; //edge that would otherwise close a path struct TEdge { IntPoint Bot; - IntPoint Curr; + IntPoint Curr; //current (updated for every new scanbeam) IntPoint Top; - IntPoint Delta; double Dx; PolyType PolyTyp; - EdgeSide Side; + EdgeSide Side; //side only refers to current side of solution poly int WindDelta; //1 or -1 depending on winding direction int WindCnt; int WindCnt2; //winding count of the opposite polytype @@ -98,6 +97,8 @@ struct LocalMinimum { struct OutPt; +//OutRec: contains a path in the clipping solution. Edges in the AEL will +//carry a pointer to an OutRec when they are part of the clipping solution. struct OutRec { int Idx; bool IsHole; @@ -402,19 +403,25 @@ double Area(const Path &poly) } //------------------------------------------------------------------------------ -double Area(const OutRec &outRec) +double Area(const OutPt *op) { - OutPt *op = outRec.Pts; + const OutPt *startOp = op; if (!op) return 0; double a = 0; do { a += (double)(op->Prev->Pt.X + op->Pt.X) * (double)(op->Prev->Pt.Y - op->Pt.Y); op = op->Next; - } while (op != outRec.Pts); + } while (op != startOp); return a * 0.5; } //------------------------------------------------------------------------------ +double Area(const OutRec &outRec) +{ + return Area(outRec.Pts); +} +//------------------------------------------------------------------------------ + bool PointIsVertex(const IntPoint &Pt, OutPt *pp) { OutPt *pp2 = pp; @@ -535,15 +542,17 @@ bool SlopesEqual(const TEdge &e1, const TEdge &e2, bool UseFullInt64Range) { #ifndef use_int32 if (UseFullInt64Range) - return Int128Mul(e1.Delta.Y, e2.Delta.X) == Int128Mul(e1.Delta.X, e2.Delta.Y); + return Int128Mul(e1.Top.Y - e1.Bot.Y, e2.Top.X - e2.Bot.X) == + Int128Mul(e1.Top.X - e1.Bot.X, e2.Top.Y - e2.Bot.Y); else #endif - return e1.Delta.Y * e2.Delta.X == e1.Delta.X * e2.Delta.Y; + return (e1.Top.Y - e1.Bot.Y) * (e2.Top.X - e2.Bot.X) == + (e1.Top.X - e1.Bot.X) * (e2.Top.Y - e2.Bot.Y); } //------------------------------------------------------------------------------ -bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, - const IntPoint &pt3, bool UseFullInt64Range) +bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, + const IntPoint pt3, bool UseFullInt64Range) { #ifndef use_int32 if (UseFullInt64Range) @@ -554,8 +563,8 @@ bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, } //------------------------------------------------------------------------------ -bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, - const IntPoint &pt3, const IntPoint &pt4, bool UseFullInt64Range) +bool SlopesEqual(const IntPoint pt1, const IntPoint pt2, + const IntPoint pt3, const IntPoint pt4, bool UseFullInt64Range) { #ifndef use_int32 if (UseFullInt64Range) @@ -568,11 +577,11 @@ bool SlopesEqual(const IntPoint &pt1, const IntPoint &pt2, inline bool IsHorizontal(TEdge &e) { - return e.Delta.Y == 0; + return e.Dx == HORIZONTAL; } //------------------------------------------------------------------------------ -inline double GetDx(const IntPoint &pt1, const IntPoint &pt2) +inline double GetDx(const IntPoint pt1, const IntPoint pt2) { return (pt1.Y == pt2.Y) ? HORIZONTAL : (double)(pt2.X - pt1.X) / (pt2.Y - pt1.Y); @@ -581,11 +590,9 @@ inline double GetDx(const IntPoint &pt1, const IntPoint &pt2) inline void SetDx(TEdge &e) { - e.Delta.X = (e.Top.X - e.Bot.X); - e.Delta.Y = (e.Top.Y - e.Bot.Y); - - if (e.Delta.Y == 0) e.Dx = HORIZONTAL; - else e.Dx = (double)(e.Delta.X) / e.Delta.Y; + cInt dy = (e.Top.Y - e.Bot.Y); + if (dy == 0) e.Dx = HORIZONTAL; + else e.Dx = (double)(e.Top.X - e.Bot.X) / dy; } //--------------------------------------------------------------------------- @@ -625,7 +632,7 @@ void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) ip.X = TopX(Edge1, ip.Y); return; } - else if (Edge1.Delta.X == 0) + else if (Edge1.Dx == 0) { ip.X = Edge1.Bot.X; if (IsHorizontal(Edge2)) @@ -636,7 +643,7 @@ void IntersectPoint(TEdge &Edge1, TEdge &Edge2, IntPoint &ip) ip.Y = Round(ip.X / Edge2.Dx + b2); } } - else if (Edge2.Delta.X == 0) + else if (Edge2.Dx == 0) { ip.X = Edge2.Bot.X; if (IsHorizontal(Edge1)) @@ -803,7 +810,12 @@ bool FirstIsBottomPt(const OutPt* btmPt1, const OutPt* btmPt2) p = btmPt2->Next; while ((p->Pt == btmPt2->Pt) && (p != btmPt2)) p = p->Next; double dx2n = std::fabs(GetDx(btmPt2->Pt, p->Pt)); - return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); + + if (std::max(dx1p, dx1n) == std::max(dx2p, dx2n) && + std::min(dx1p, dx1n) == std::min(dx2p, dx2n)) + return Area(btmPt1) > 0; //if otherwise identical use orientation + else + return (dx1p >= dx2p && dx1p >= dx2n) || (dx1n >= dx2p && dx1n >= dx2n); } //------------------------------------------------------------------------------ @@ -845,8 +857,8 @@ OutPt* GetBottomPt(OutPt *pp) } //------------------------------------------------------------------------------ -bool Pt2IsBetweenPt1AndPt3(const IntPoint &pt1, - const IntPoint &pt2, const IntPoint &pt3) +bool Pt2IsBetweenPt1AndPt3(const IntPoint pt1, + const IntPoint pt2, const IntPoint pt3) { if ((pt1 == pt3) || (pt1 == pt2) || (pt3 == pt2)) return false; @@ -886,7 +898,7 @@ void RangeTest(const IntPoint& Pt, bool& useFullRange) if (useFullRange) { if (Pt.X > hiRange || Pt.Y > hiRange || -Pt.X > hiRange || -Pt.Y > hiRange) - throw "Coordinate outside allowed range"; + throw clipperException("Coordinate outside allowed range"); } else if (Pt.X > loRange|| Pt.Y > loRange || -Pt.X > loRange || -Pt.Y > loRange) { @@ -1185,8 +1197,6 @@ bool ClipperBase::AddPath(const Path &pg, PolyType PolyTyp, bool Closed) locMin.RightBound = E->Prev; leftBoundIsForward = true; //Q.nextInLML = Q.next } - locMin.LeftBound->Side = esLeft; - locMin.RightBound->Side = esRight; if (!Closed) locMin.LeftBound->WindDelta = 0; else if (locMin.LeftBound->Next == locMin.RightBound) @@ -1225,8 +1235,6 @@ void ClipperBase::Clear() DisposeLocalMinimaList(); for (EdgeList::size_type i = 0; i < m_edges.size(); ++i) { - //for each edge array in turn, find the first used edge and - //check for and remove any hiddenPts in each edge in the array. TEdge* edges = m_edges[i]; delete [] edges; } @@ -1242,9 +1250,11 @@ void ClipperBase::Reset() if (m_CurrentLM == m_MinimaList.end()) return; //ie nothing to process std::sort(m_MinimaList.begin(), m_MinimaList.end(), LocMinSorter()); + m_Scanbeam = ScanbeamList(); //clears/resets priority_queue //reset all edges ... for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) { + InsertScanbeam(lm->Y); TEdge* e = lm->LeftBound; if (e) { @@ -1261,6 +1271,8 @@ void ClipperBase::Reset() e->OutIdx = Unassigned; } } + m_ActiveEdges = 0; + m_CurrentLM = m_MinimaList.begin(); } //------------------------------------------------------------------------------ @@ -1271,10 +1283,12 @@ void ClipperBase::DisposeLocalMinimaList() } //------------------------------------------------------------------------------ -void ClipperBase::PopLocalMinima() +bool ClipperBase::PopLocalMinima(cInt Y, const LocalMinimum *&locMin) { - if (m_CurrentLM == m_MinimaList.end()) return; + if (m_CurrentLM == m_MinimaList.end() || (*m_CurrentLM).Y != Y) return false; + locMin = &(*m_CurrentLM); ++m_CurrentLM; + return true; } //------------------------------------------------------------------------------ @@ -1293,6 +1307,7 @@ IntRect ClipperBase::GetBounds() result.bottom = lm->LeftBound->Bot.Y; while (lm != m_MinimaList.end()) { + //todo - needs fixing for open paths result.bottom = std::max(result.bottom, lm->LeftBound->Bot.Y); TEdge* e = lm->LeftBound; for (;;) { @@ -1315,6 +1330,142 @@ IntRect ClipperBase::GetBounds() } return result; } +//------------------------------------------------------------------------------ + +void ClipperBase::InsertScanbeam(const cInt Y) +{ + m_Scanbeam.push(Y); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::PopScanbeam(cInt &Y) +{ + if (m_Scanbeam.empty()) return false; + Y = m_Scanbeam.top(); + m_Scanbeam.pop(); + while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. + return true; +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeAllOutRecs(){ + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + DisposeOutRec(i); + m_PolyOuts.clear(); +} +//------------------------------------------------------------------------------ + +void ClipperBase::DisposeOutRec(PolyOutList::size_type index) +{ + OutRec *outRec = m_PolyOuts[index]; + if (outRec->Pts) DisposeOutPts(outRec->Pts); + delete outRec; + m_PolyOuts[index] = 0; +} +//------------------------------------------------------------------------------ + +void ClipperBase::DeleteFromAEL(TEdge *e) +{ + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (!AelPrev && !AelNext && (e != m_ActiveEdges)) return; //already deleted + if (AelPrev) AelPrev->NextInAEL = AelNext; + else m_ActiveEdges = AelNext; + if (AelNext) AelNext->PrevInAEL = AelPrev; + e->NextInAEL = 0; + e->PrevInAEL = 0; +} +//------------------------------------------------------------------------------ + +OutRec* ClipperBase::CreateOutRec() +{ + OutRec* result = new OutRec; + result->IsHole = false; + result->IsOpen = false; + result->FirstLeft = 0; + result->Pts = 0; + result->BottomPt = 0; + result->PolyNd = 0; + m_PolyOuts.push_back(result); + result->Idx = (int)m_PolyOuts.size() - 1; + return result; +} +//------------------------------------------------------------------------------ + +void ClipperBase::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) +{ + //check that one or other edge hasn't already been removed from AEL ... + if (Edge1->NextInAEL == Edge1->PrevInAEL || + Edge2->NextInAEL == Edge2->PrevInAEL) return; + + if (Edge1->NextInAEL == Edge2) + { + TEdge* Next = Edge2->NextInAEL; + if (Next) Next->PrevInAEL = Edge1; + TEdge* Prev = Edge1->PrevInAEL; + if (Prev) Prev->NextInAEL = Edge2; + Edge2->PrevInAEL = Prev; + Edge2->NextInAEL = Edge1; + Edge1->PrevInAEL = Edge2; + Edge1->NextInAEL = Next; + } + else if (Edge2->NextInAEL == Edge1) + { + TEdge* Next = Edge1->NextInAEL; + if (Next) Next->PrevInAEL = Edge2; + TEdge* Prev = Edge2->PrevInAEL; + if (Prev) Prev->NextInAEL = Edge1; + Edge1->PrevInAEL = Prev; + Edge1->NextInAEL = Edge2; + Edge2->PrevInAEL = Edge1; + Edge2->NextInAEL = Next; + } + else + { + TEdge* Next = Edge1->NextInAEL; + TEdge* Prev = Edge1->PrevInAEL; + Edge1->NextInAEL = Edge2->NextInAEL; + if (Edge1->NextInAEL) Edge1->NextInAEL->PrevInAEL = Edge1; + Edge1->PrevInAEL = Edge2->PrevInAEL; + if (Edge1->PrevInAEL) Edge1->PrevInAEL->NextInAEL = Edge1; + Edge2->NextInAEL = Next; + if (Edge2->NextInAEL) Edge2->NextInAEL->PrevInAEL = Edge2; + Edge2->PrevInAEL = Prev; + if (Edge2->PrevInAEL) Edge2->PrevInAEL->NextInAEL = Edge2; + } + + if (!Edge1->PrevInAEL) m_ActiveEdges = Edge1; + else if (!Edge2->PrevInAEL) m_ActiveEdges = Edge2; +} +//------------------------------------------------------------------------------ + +void ClipperBase::UpdateEdgeIntoAEL(TEdge *&e) +{ + if (!e->NextInLML) + throw clipperException("UpdateEdgeIntoAEL: invalid call"); + + e->NextInLML->OutIdx = e->OutIdx; + TEdge* AelPrev = e->PrevInAEL; + TEdge* AelNext = e->NextInAEL; + if (AelPrev) AelPrev->NextInAEL = e->NextInLML; + else m_ActiveEdges = e->NextInLML; + if (AelNext) AelNext->PrevInAEL = e->NextInLML; + e->NextInLML->Side = e->Side; + e->NextInLML->WindDelta = e->WindDelta; + e->NextInLML->WindCnt = e->WindCnt; + e->NextInLML->WindCnt2 = e->WindCnt2; + e = e->NextInLML; + e->Curr = e->Bot; + e->PrevInAEL = AelPrev; + e->NextInAEL = AelNext; + if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y); +} +//------------------------------------------------------------------------------ + +bool ClipperBase::LocalMinimaPending() +{ + return (m_CurrentLM != m_MinimaList.end()); +} //------------------------------------------------------------------------------ // TClipper methods ... @@ -1322,8 +1473,6 @@ IntRect ClipperBase::GetBounds() Clipper::Clipper(int initOptions) : ClipperBase() //constructor { - m_ActiveEdges = 0; - m_SortedEdges = 0; m_ExecuteLocked = false; m_UseFullRange = false; m_ReverseOutput = ((initOptions & ioReverseSolution) != 0); @@ -1336,12 +1485,6 @@ Clipper::Clipper(int initOptions) : ClipperBase() //constructor } //------------------------------------------------------------------------------ -Clipper::~Clipper() //destructor -{ - Clear(); -} -//------------------------------------------------------------------------------ - #ifdef use_xyz void Clipper::ZFillFunction(ZFillCallback zFillFunc) { @@ -1350,18 +1493,6 @@ void Clipper::ZFillFunction(ZFillCallback zFillFunc) //------------------------------------------------------------------------------ #endif -void Clipper::Reset() -{ - ClipperBase::Reset(); - m_Scanbeam = ScanbeamList(); - m_Maxima = MaximaList(); - m_ActiveEdges = 0; - m_SortedEdges = 0; - for (MinimaList::iterator lm = m_MinimaList.begin(); lm != m_MinimaList.end(); ++lm) - InsertScanbeam(lm->Y); -} -//------------------------------------------------------------------------------ - bool Clipper::Execute(ClipType clipType, Paths &solution, PolyFillType fillType) { return Execute(clipType, solution, fillType, fillType); @@ -1431,19 +1562,26 @@ bool Clipper::ExecuteInternal() bool succeeded = true; try { Reset(); - if (m_CurrentLM == m_MinimaList.end()) return true; - cInt botY = PopScanbeam(); - do { - InsertLocalMinimaIntoAEL(botY); + m_Maxima = MaximaList(); + m_SortedEdges = 0; + + succeeded = true; + cInt botY, topY; + if (!PopScanbeam(botY)) return false; + InsertLocalMinimaIntoAEL(botY); + while (PopScanbeam(topY) || LocalMinimaPending()) + { ProcessHorizontals(); - ClearGhostJoins(); - if (m_Scanbeam.empty()) break; - cInt topY = PopScanbeam(); - succeeded = ProcessIntersections(topY); - if (!succeeded) break; + ClearGhostJoins(); + if (!ProcessIntersections(topY)) + { + succeeded = false; + break; + } ProcessEdgesAtTopOfScanbeam(topY); botY = topY; - } while (!m_Scanbeam.empty() || m_CurrentLM != m_MinimaList.end()); + InsertLocalMinimaIntoAEL(botY); + } } catch(...) { @@ -1483,45 +1621,20 @@ bool Clipper::ExecuteInternal() } //------------------------------------------------------------------------------ -void Clipper::InsertScanbeam(const cInt Y) -{ - m_Scanbeam.push(Y); -} -//------------------------------------------------------------------------------ - -cInt Clipper::PopScanbeam() -{ - const cInt Y = m_Scanbeam.top(); - m_Scanbeam.pop(); - while (!m_Scanbeam.empty() && Y == m_Scanbeam.top()) { m_Scanbeam.pop(); } // Pop duplicates. - return Y; -} -//------------------------------------------------------------------------------ - -void Clipper::DisposeAllOutRecs(){ - for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) - DisposeOutRec(i); - m_PolyOuts.clear(); -} -//------------------------------------------------------------------------------ - -void Clipper::DisposeOutRec(PolyOutList::size_type index) -{ - OutRec *outRec = m_PolyOuts[index]; - if (outRec->Pts) DisposeOutPts(outRec->Pts); - delete outRec; - m_PolyOuts[index] = 0; -} -//------------------------------------------------------------------------------ - -void Clipper::SetWindingCount(TEdge &edge) const +void Clipper::SetWindingCount(TEdge &edge) { TEdge *e = edge.PrevInAEL; //find the edge of the same polytype that immediately preceeds 'edge' in AEL while (e && ((e->PolyTyp != edge.PolyTyp) || (e->WindDelta == 0))) e = e->PrevInAEL; if (!e) { - edge.WindCnt = (edge.WindDelta == 0 ? 1 : edge.WindDelta); + if (edge.WindDelta == 0) + { + PolyFillType pft = (edge.PolyTyp == ptSubject ? m_SubjFillType : m_ClipFillType); + edge.WindCnt = (pft == pftNegative ? -1 : 1); + } + else + edge.WindCnt = edge.WindDelta; edge.WindCnt2 = 0; e = m_ActiveEdges; //ie get ready to calc WindCnt2 } @@ -1753,13 +1866,16 @@ OutPt* Clipper::AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &Pt) prevE = e->PrevInAEL; } - if (prevE && prevE->OutIdx >= 0 && - (TopX(*prevE, Pt.Y) == TopX(*e, Pt.Y)) && - SlopesEqual(*e, *prevE, m_UseFullRange) && - (e->WindDelta != 0) && (prevE->WindDelta != 0)) + if (prevE && prevE->OutIdx >= 0) { - OutPt* outPt = AddOutPt(prevE, Pt); - AddJoin(result, outPt, e->Top); + cInt xPrev = TopX(*prevE, Pt.Y); + cInt xE = TopX(*e, Pt.Y); + if (xPrev == xE && (e->WindDelta != 0) && (prevE->WindDelta != 0) && + SlopesEqual(IntPoint(xPrev, Pt.Y), prevE->Top, IntPoint(xE, Pt.Y), e->Top, m_UseFullRange)) + { + OutPt* outPt = AddOutPt(prevE, Pt); + AddJoin(result, outPt, e->Top); + } } return result; } @@ -1801,6 +1917,15 @@ void Clipper::AddEdgeToSEL(TEdge *edge) } //------------------------------------------------------------------------------ +bool Clipper::PopEdgeFromSEL(TEdge *&edge) +{ + if (!m_SortedEdges) return false; + edge = m_SortedEdges; + DeleteFromSEL(m_SortedEdges); + return true; +} +//------------------------------------------------------------------------------ + void Clipper::CopyAELToSEL() { TEdge* e = m_ActiveEdges; @@ -1814,7 +1939,7 @@ void Clipper::CopyAELToSEL() } //------------------------------------------------------------------------------ -void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint &OffPt) +void Clipper::AddJoin(OutPt *op1, OutPt *op2, const IntPoint OffPt) { Join* j = new Join; j->OutPt1 = op1; @@ -1840,7 +1965,7 @@ void Clipper::ClearGhostJoins() } //------------------------------------------------------------------------------ -void Clipper::AddGhostJoin(OutPt *op, const IntPoint &OffPt) +void Clipper::AddGhostJoin(OutPt *op, const IntPoint OffPt) { Join* j = new Join; j->OutPt1 = op; @@ -1852,11 +1977,12 @@ void Clipper::AddGhostJoin(OutPt *op, const IntPoint &OffPt) void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) { - while (m_CurrentLM != m_MinimaList.end() && (m_CurrentLM->Y == botY)) + const LocalMinimum *lm; + while (PopLocalMinima(botY, lm)) { - TEdge* lb = m_CurrentLM->LeftBound; - TEdge* rb = m_CurrentLM->RightBound; - PopLocalMinima(); + TEdge* lb = lm->LeftBound; + TEdge* rb = lm->RightBound; + OutPt *Op1 = 0; if (!lb) { @@ -1888,8 +2014,13 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) if (rb) { - if(IsHorizontal(*rb)) AddEdgeToSEL(rb); - else InsertScanbeam( rb->Top.Y ); + if (IsHorizontal(*rb)) + { + AddEdgeToSEL(rb); + if (rb->NextInLML) + InsertScanbeam(rb->NextInLML->Top.Y); + } + else InsertScanbeam( rb->Top.Y ); } if (!lb || !rb) continue; @@ -1911,7 +2042,7 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) if (lb->OutIdx >= 0 && lb->PrevInAEL && lb->PrevInAEL->Curr.X == lb->Bot.X && lb->PrevInAEL->OutIdx >= 0 && - SlopesEqual(*lb->PrevInAEL, *lb, m_UseFullRange) && + SlopesEqual(lb->PrevInAEL->Bot, lb->PrevInAEL->Top, lb->Curr, lb->Top, m_UseFullRange) && (lb->WindDelta != 0) && (lb->PrevInAEL->WindDelta != 0)) { OutPt *Op2 = AddOutPt(lb->PrevInAEL, lb->Bot); @@ -1922,7 +2053,7 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) { if (rb->OutIdx >= 0 && rb->PrevInAEL->OutIdx >= 0 && - SlopesEqual(*rb->PrevInAEL, *rb, m_UseFullRange) && + SlopesEqual(rb->PrevInAEL->Curr, rb->PrevInAEL->Top, rb->Curr, rb->Top, m_UseFullRange) && (rb->WindDelta != 0) && (rb->PrevInAEL->WindDelta != 0)) { OutPt *Op2 = AddOutPt(rb->PrevInAEL, rb->Bot); @@ -1946,19 +2077,6 @@ void Clipper::InsertLocalMinimaIntoAEL(const cInt botY) } //------------------------------------------------------------------------------ -void Clipper::DeleteFromAEL(TEdge *e) -{ - TEdge* AelPrev = e->PrevInAEL; - TEdge* AelNext = e->NextInAEL; - if( !AelPrev && !AelNext && (e != m_ActiveEdges) ) return; //already deleted - if( AelPrev ) AelPrev->NextInAEL = AelNext; - else m_ActiveEdges = AelNext; - if( AelNext ) AelNext->PrevInAEL = AelPrev; - e->NextInAEL = 0; - e->PrevInAEL = 0; -} -//------------------------------------------------------------------------------ - void Clipper::DeleteFromSEL(TEdge *e) { TEdge* SelPrev = e->PrevInSEL; @@ -2180,21 +2298,29 @@ void Clipper::IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &Pt) } //------------------------------------------------------------------------------ -void Clipper::SetHoleState(TEdge *e, OutRec *outrec) const +void Clipper::SetHoleState(TEdge *e, OutRec *outrec) { - bool IsHole = false; TEdge *e2 = e->PrevInAEL; + TEdge *eTmp = 0; while (e2) { if (e2->OutIdx >= 0 && e2->WindDelta != 0) { - IsHole = !IsHole; - if (! outrec->FirstLeft) - outrec->FirstLeft = m_PolyOuts[e2->OutIdx]; + if (!eTmp) eTmp = e2; + else if (eTmp->OutIdx == e2->OutIdx) eTmp = 0; } e2 = e2->PrevInAEL; } - if (IsHole) outrec->IsHole = true; + if (!eTmp) + { + outrec->FirstLeft = 0; + outrec->IsHole = false; + } + else + { + outrec->FirstLeft = m_PolyOuts[eTmp->OutIdx]; + outrec->IsHole = !outrec->FirstLeft->IsHole; + } } //------------------------------------------------------------------------------ @@ -2218,7 +2344,7 @@ OutRec* GetLowermostRec(OutRec *outRec1, OutRec *outRec2) } //------------------------------------------------------------------------------ -bool Param1RightOfParam2(OutRec* outRec1, OutRec* outRec2) +bool OutRec1RightOfOutRec2(OutRec* outRec1, OutRec* outRec2) { do { @@ -2238,16 +2364,16 @@ OutRec* Clipper::GetOutRec(int Idx) } //------------------------------------------------------------------------------ -void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const +void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) { //get the start and ends of both output polygons ... OutRec *outRec1 = m_PolyOuts[e1->OutIdx]; OutRec *outRec2 = m_PolyOuts[e2->OutIdx]; OutRec *holeStateRec; - if (Param1RightOfParam2(outRec1, outRec2)) + if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2; - else if (Param1RightOfParam2(outRec2, outRec1)) + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1; else holeStateRec = GetLowermostRec(outRec1, outRec2); @@ -2260,7 +2386,6 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const OutPt* p2_lft = outRec2->Pts; OutPt* p2_rt = p2_lft->Prev; - EdgeSide Side; //join e2 poly onto e1 poly and delete pointers to e2 ... if( e1->Side == esLeft ) { @@ -2282,7 +2407,6 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const p1_rt->Next = p2_lft; outRec1->Pts = p2_lft; } - Side = esLeft; } else { if( e2->Side == esRight ) @@ -2301,7 +2425,6 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const p1_lft->Prev = p2_rt; p2_rt->Next = p1_lft; } - Side = esRight; } outRec1->BottomPt = 0; @@ -2327,7 +2450,7 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const if( e->OutIdx == ObsoleteIdx ) { e->OutIdx = OKIdx; - e->Side = Side; + e->Side = e1->Side; break; } e = e->NextInAEL; @@ -2337,21 +2460,6 @@ void Clipper::AppendPolygon(TEdge *e1, TEdge *e2) const } //------------------------------------------------------------------------------ -OutRec* Clipper::CreateOutRec() -{ - OutRec* result = new OutRec; - result->IsHole = false; - result->IsOpen = false; - result->FirstLeft = 0; - result->Pts = 0; - result->BottomPt = 0; - result->PolyNd = 0; - m_PolyOuts.push_back(result); - result->Idx = (int)m_PolyOuts.size()-1; - return result; -} -//------------------------------------------------------------------------------ - OutPt* Clipper::AddOutPt(TEdge *e, const IntPoint &pt) { if( e->OutIdx < 0 ) @@ -2403,13 +2511,9 @@ OutPt* Clipper::GetLastOutPt(TEdge *e) void Clipper::ProcessHorizontals() { - TEdge* horzEdge = m_SortedEdges; - while(horzEdge) - { - DeleteFromSEL(horzEdge); + TEdge* horzEdge; + while (PopEdgeFromSEL(horzEdge)) ProcessHorizontal(horzEdge); - horzEdge = m_SortedEdges; - } } //------------------------------------------------------------------------------ @@ -2433,64 +2537,21 @@ inline bool IsIntermediate(TEdge *e, const cInt Y) TEdge *GetMaximaPair(TEdge *e) { - TEdge* result = 0; if ((e->Next->Top == e->Top) && !e->Next->NextInLML) - result = e->Next; + return e->Next; else if ((e->Prev->Top == e->Top) && !e->Prev->NextInLML) - result = e->Prev; - - if (result && (result->OutIdx == Skip || - //result is false if both NextInAEL & PrevInAEL are nil & not horizontal ... - (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) - return 0; - return result; + return e->Prev; + else return 0; } //------------------------------------------------------------------------------ -void Clipper::SwapPositionsInAEL(TEdge *Edge1, TEdge *Edge2) +TEdge *GetMaximaPairEx(TEdge *e) { - //check that one or other edge hasn't already been removed from AEL ... - if (Edge1->NextInAEL == Edge1->PrevInAEL || - Edge2->NextInAEL == Edge2->PrevInAEL) return; - - if( Edge1->NextInAEL == Edge2 ) - { - TEdge* Next = Edge2->NextInAEL; - if( Next ) Next->PrevInAEL = Edge1; - TEdge* Prev = Edge1->PrevInAEL; - if( Prev ) Prev->NextInAEL = Edge2; - Edge2->PrevInAEL = Prev; - Edge2->NextInAEL = Edge1; - Edge1->PrevInAEL = Edge2; - Edge1->NextInAEL = Next; - } - else if( Edge2->NextInAEL == Edge1 ) - { - TEdge* Next = Edge1->NextInAEL; - if( Next ) Next->PrevInAEL = Edge2; - TEdge* Prev = Edge2->PrevInAEL; - if( Prev ) Prev->NextInAEL = Edge1; - Edge1->PrevInAEL = Prev; - Edge1->NextInAEL = Edge2; - Edge2->PrevInAEL = Edge1; - Edge2->NextInAEL = Next; - } - else - { - TEdge* Next = Edge1->NextInAEL; - TEdge* Prev = Edge1->PrevInAEL; - Edge1->NextInAEL = Edge2->NextInAEL; - if( Edge1->NextInAEL ) Edge1->NextInAEL->PrevInAEL = Edge1; - Edge1->PrevInAEL = Edge2->PrevInAEL; - if( Edge1->PrevInAEL ) Edge1->PrevInAEL->NextInAEL = Edge1; - Edge2->NextInAEL = Next; - if( Edge2->NextInAEL ) Edge2->NextInAEL->PrevInAEL = Edge2; - Edge2->PrevInAEL = Prev; - if( Edge2->PrevInAEL ) Edge2->PrevInAEL->NextInAEL = Edge2; - } - - if( !Edge1->PrevInAEL ) m_ActiveEdges = Edge1; - else if( !Edge2->PrevInAEL ) m_ActiveEdges = Edge2; + //as GetMaximaPair() but returns 0 if MaxPair isn't in AEL (unless it's horizontal) + TEdge* result = GetMaximaPair(e); + if (result && (result->OutIdx == Skip || + (result->NextInAEL == result->PrevInAEL && !IsHorizontal(*result)))) return 0; + return result; } //------------------------------------------------------------------------------ @@ -2576,7 +2637,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) { Direction dir; cInt horzLeft, horzRight; - bool IsOpen = (horzEdge->OutIdx >= 0 && m_PolyOuts[horzEdge->OutIdx]->IsOpen); + bool IsOpen = (horzEdge->WindDelta == 0); GetHorzDirection(*horzEdge, dir, horzLeft, horzRight); @@ -2588,20 +2649,20 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) MaximaList::const_iterator maxIt; MaximaList::const_reverse_iterator maxRit; - if (!m_Maxima.empty()) + if (m_Maxima.size() > 0) { //get the first maxima in range (X) ... if (dir == dLeftToRight) { maxIt = m_Maxima.begin(); - while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) ++maxIt; + while (maxIt != m_Maxima.end() && *maxIt <= horzEdge->Bot.X) maxIt++; if (maxIt != m_Maxima.end() && *maxIt >= eLastHorz->Top.X) maxIt = m_Maxima.end(); } else { maxRit = m_Maxima.rbegin(); - while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) ++maxRit; + while (maxRit != m_Maxima.rend() && *maxRit > horzEdge->Bot.X) maxRit++; if (maxRit != m_Maxima.rend() && *maxRit <= eLastHorz->Top.X) maxRit = m_Maxima.rend(); } @@ -2620,7 +2681,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) //this code block inserts extra coords into horizontal edges (in output //polygons) whereever maxima touch these horizontal edges. This helps //'simplifying' polygons (ie if the Simplify property is set). - if (!m_Maxima.empty()) + if (m_Maxima.size() > 0) { if (dir == dLeftToRight) { @@ -2628,7 +2689,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) { if (horzEdge->OutIdx >= 0 && !IsOpen) AddOutPt(horzEdge, IntPoint(*maxIt, horzEdge->Bot.Y)); - ++maxIt; + maxIt++; } } else @@ -2637,7 +2698,7 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) { if (horzEdge->OutIdx >= 0 && !IsOpen) AddOutPt(horzEdge, IntPoint(*maxRit, horzEdge->Bot.Y)); - ++maxRit; + maxRit++; } } }; @@ -2759,29 +2820,6 @@ void Clipper::ProcessHorizontal(TEdge *horzEdge) } //------------------------------------------------------------------------------ -void Clipper::UpdateEdgeIntoAEL(TEdge *&e) -{ - if( !e->NextInLML ) throw - clipperException("UpdateEdgeIntoAEL: invalid call"); - - e->NextInLML->OutIdx = e->OutIdx; - TEdge* AelPrev = e->PrevInAEL; - TEdge* AelNext = e->NextInAEL; - if (AelPrev) AelPrev->NextInAEL = e->NextInLML; - else m_ActiveEdges = e->NextInLML; - if (AelNext) AelNext->PrevInAEL = e->NextInLML; - e->NextInLML->Side = e->Side; - e->NextInLML->WindDelta = e->WindDelta; - e->NextInLML->WindCnt = e->WindCnt; - e->NextInLML->WindCnt2 = e->WindCnt2; - e = e->NextInLML; - e->Curr = e->Bot; - e->PrevInAEL = AelPrev; - e->NextInAEL = AelNext; - if (!IsHorizontal(*e)) InsertScanbeam(e->Top.Y); -} -//------------------------------------------------------------------------------ - bool Clipper::ProcessIntersections(const cInt topY) { if( !m_ActiveEdges ) return true; @@ -2839,6 +2877,7 @@ void Clipper::BuildIntersectList(const cInt topY) if(e->Curr.X > eNext->Curr.X) { IntersectPoint(*e, *eNext, Pt); + if (Pt.Y < topY) Pt = IntPoint(TopX(*e, topY), topY); IntersectNode * newNode = new IntersectNode; newNode->Edge1 = e; newNode->Edge2 = eNext; @@ -2913,7 +2952,7 @@ bool Clipper::FixupIntersectionOrder() void Clipper::DoMaxima(TEdge *e) { - TEdge* eMaxPair = GetMaximaPair(e); + TEdge* eMaxPair = GetMaximaPairEx(e); if (!eMaxPair) { if (e->OutIdx >= 0) @@ -2974,7 +3013,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) if(IsMaximaEdge) { - TEdge* eMaxPair = GetMaximaPair(e); + TEdge* eMaxPair = GetMaximaPairEx(e); IsMaximaEdge = (!eMaxPair || !IsHorizontal(*eMaxPair)); } @@ -3046,7 +3085,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) if (ePrev && ePrev->Curr.X == e->Bot.X && ePrev->Curr.Y == e->Bot.Y && op && ePrev->OutIdx >= 0 && ePrev->Curr.Y > ePrev->Top.Y && - SlopesEqual(*e, *ePrev, m_UseFullRange) && + SlopesEqual(e->Curr, e->Top, ePrev->Curr, ePrev->Top, m_UseFullRange) && (e->WindDelta != 0) && (ePrev->WindDelta != 0)) { OutPt* op2 = AddOutPt(ePrev, e->Bot); @@ -3055,7 +3094,7 @@ void Clipper::ProcessEdgesAtTopOfScanbeam(const cInt topY) else if (eNext && eNext->Curr.X == e->Bot.X && eNext->Curr.Y == e->Bot.Y && op && eNext->OutIdx >= 0 && eNext->Curr.Y > eNext->Top.Y && - SlopesEqual(*e, *eNext, m_UseFullRange) && + SlopesEqual(e->Curr, e->Top, eNext->Curr, eNext->Top, m_UseFullRange) && (e->WindDelta != 0) && (eNext->WindDelta != 0)) { OutPt* op2 = AddOutPt(eNext, e->Bot); @@ -3323,7 +3362,7 @@ OutPt* DupOutPt(OutPt* outPt, bool InsertAfter) //------------------------------------------------------------------------------ bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, - const IntPoint &Pt, bool DiscardLeft) + const IntPoint Pt, bool DiscardLeft) { Direction Dir1 = (op1->Pt.X > op1b->Pt.X ? dRightToLeft : dLeftToRight); Direction Dir2 = (op2->Pt.X > op2b->Pt.X ? dRightToLeft : dLeftToRight); @@ -3576,15 +3615,14 @@ static OutRec* ParseFirstLeft(OutRec* FirstLeft) } //------------------------------------------------------------------------------ -void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const +void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) { //tests if NewOutRec contains the polygon before reassigning FirstLeft for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { OutRec* outRec = m_PolyOuts[i]; - if (!outRec->Pts || !outRec->FirstLeft) continue; OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); - if (firstLeft == OldOutRec) + if (outRec->Pts && firstLeft == OldOutRec) { if (Poly2ContainsPoly1(outRec->Pts, NewOutRec->Pts)) outRec->FirstLeft = NewOutRec; @@ -3593,13 +3631,40 @@ void Clipper::FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const } //---------------------------------------------------------------------- -void Clipper::FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const -{ +void Clipper::FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec) +{ + //A polygon has split into two such that one is now the inner of the other. + //It's possible that these polygons now wrap around other polygons, so check + //every polygon that's also contained by OuterOutRec's FirstLeft container + //(including 0) to see if they've become inner to the new inner polygon ... + OutRec* orfl = OuterOutRec->FirstLeft; + for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) + { + OutRec* outRec = m_PolyOuts[i]; + + if (!outRec->Pts || outRec == OuterOutRec || outRec == InnerOutRec) + continue; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (firstLeft != orfl && firstLeft != InnerOutRec && firstLeft != OuterOutRec) + continue; + if (Poly2ContainsPoly1(outRec->Pts, InnerOutRec->Pts)) + outRec->FirstLeft = InnerOutRec; + else if (Poly2ContainsPoly1(outRec->Pts, OuterOutRec->Pts)) + outRec->FirstLeft = OuterOutRec; + else if (outRec->FirstLeft == InnerOutRec || outRec->FirstLeft == OuterOutRec) + outRec->FirstLeft = orfl; + } +} +//---------------------------------------------------------------------- +void Clipper::FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec) +{ //reassigns FirstLeft WITHOUT testing if NewOutRec contains the polygon for (PolyOutList::size_type i = 0; i < m_PolyOuts.size(); ++i) { OutRec* outRec = m_PolyOuts[i]; - if (outRec->FirstLeft == OldOutRec) outRec->FirstLeft = NewOutRec; + OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); + if (outRec->Pts && outRec->FirstLeft == OldOutRec) + outRec->FirstLeft = NewOutRec; } } //---------------------------------------------------------------------- @@ -3620,8 +3685,8 @@ void Clipper::JoinCommonEdges() //before calling JoinPoints() ... OutRec *holeStateRec; if (outRec1 == outRec2) holeStateRec = outRec1; - else if (Param1RightOfParam2(outRec1, outRec2)) holeStateRec = outRec2; - else if (Param1RightOfParam2(outRec2, outRec1)) holeStateRec = outRec1; + else if (OutRec1RightOfOutRec2(outRec1, outRec2)) holeStateRec = outRec2; + else if (OutRec1RightOfOutRec2(outRec2, outRec1)) holeStateRec = outRec1; else holeStateRec = GetLowermostRec(outRec1, outRec2); if (!JoinPoints(join, outRec1, outRec2)) continue; @@ -3638,25 +3703,12 @@ void Clipper::JoinCommonEdges() //update all OutRec2.Pts Idx's ... UpdateOutPtIdxs(*outRec2); - //We now need to check every OutRec.FirstLeft pointer. If it points - //to OutRec1 it may need to point to OutRec2 instead ... - if (m_UsingPolyTree) - for (PolyOutList::size_type j = 0; j < m_PolyOuts.size() - 1; j++) - { - OutRec* oRec = m_PolyOuts[j]; - if (!oRec->Pts || ParseFirstLeft(oRec->FirstLeft) != outRec1 || - oRec->IsHole == outRec1->IsHole) continue; - if (Poly2ContainsPoly1(oRec->Pts, join->OutPt2)) - oRec->FirstLeft = outRec2; - } - if (Poly2ContainsPoly1(outRec2->Pts, outRec1->Pts)) { - //outRec2 is contained by outRec1 ... + //outRec1 contains outRec2 ... outRec2->IsHole = !outRec1->IsHole; outRec2->FirstLeft = outRec1; - //fixup FirstLeft pointers that may need reassigning to OutRec1 if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); if ((outRec2->IsHole ^ m_ReverseOutput) == (Area(*outRec2) > 0)) @@ -3664,13 +3716,12 @@ void Clipper::JoinCommonEdges() } else if (Poly2ContainsPoly1(outRec1->Pts, outRec2->Pts)) { - //outRec1 is contained by outRec2 ... + //outRec2 contains outRec1 ... outRec2->IsHole = outRec1->IsHole; outRec1->IsHole = !outRec2->IsHole; outRec2->FirstLeft = outRec1->FirstLeft; outRec1->FirstLeft = outRec2; - //fixup FirstLeft pointers that may need reassigning to OutRec1 if (m_UsingPolyTree) FixupFirstLefts2(outRec1, outRec2); if ((outRec1->IsHole ^ m_ReverseOutput) == (Area(*outRec1) > 0)) @@ -3699,8 +3750,7 @@ void Clipper::JoinCommonEdges() outRec1->FirstLeft = outRec2->FirstLeft; outRec2->FirstLeft = outRec1; - //fixup FirstLeft pointers that may need reassigning to OutRec1 - if (m_UsingPolyTree) FixupFirstLefts2(outRec2, outRec1); + if (m_UsingPolyTree) FixupFirstLefts3(outRec2, outRec1); } } } @@ -4398,6 +4448,7 @@ void CleanPolygon(Path& poly, double distance) void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance) { + out_polys.resize(in_polys.size()); for (Paths::size_type i = 0; i < in_polys.size(); ++i) CleanPolygon(in_polys[i], out_polys[i], distance); } @@ -4462,7 +4513,7 @@ void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool p } //------------------------------------------------------------------------------ -void TranslatePath(const Path& input, Path& output, const IntPoint& delta) +void TranslatePath(const Path& input, Path& output, const IntPoint delta) { //precondition: input != output output.resize(input.size()); diff --git a/xs/src/clipper.hpp b/xs/src/clipper.hpp old mode 100644 new mode 100755 index c8009e64e..c5bcacc0a --- a/xs/src/clipper.hpp +++ b/xs/src/clipper.hpp @@ -1,8 +1,8 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.2.9 * -* Date : 16 February 2015 * +* Version : 6.4.0 * +* Date : 2 July 2015 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2015 * * * @@ -220,20 +220,27 @@ class ClipperBase public: ClipperBase(); virtual ~ClipperBase(); - bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); + virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); virtual void Clear(); IntRect GetBounds(); - bool PreserveCollinear() const {return m_PreserveCollinear;}; + bool PreserveCollinear() {return m_PreserveCollinear;}; void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; protected: void DisposeLocalMinimaList(); TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); - void PopLocalMinima(); virtual void Reset(); TEdge* ProcessBound(TEdge* E, bool IsClockwise); - TEdge* DescendToMin(TEdge *&E); - void AscendToMax(TEdge *&E, bool Appending, bool IsClosed); + void InsertScanbeam(const cInt Y); + bool PopScanbeam(cInt &Y); + bool LocalMinimaPending(); + bool PopLocalMinima(cInt Y, const LocalMinimum *&locMin); + OutRec* CreateOutRec(); + void DisposeAllOutRecs(); + void DisposeOutRec(PolyOutList::size_type index); + void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); + void DeleteFromAEL(TEdge *e); + void UpdateEdgeIntoAEL(TEdge *&e); typedef std::vector MinimaList; MinimaList::iterator m_CurrentLM; @@ -241,8 +248,13 @@ protected: bool m_UseFullRange; EdgeList m_edges; - bool m_PreserveCollinear; - bool m_HasOpenPaths; + bool m_PreserveCollinear; + bool m_HasOpenPaths; + PolyOutList m_PolyOuts; + TEdge *m_ActiveEdges; + + typedef std::priority_queue ScanbeamList; + ScanbeamList m_Scanbeam; }; //------------------------------------------------------------------------------ @@ -250,7 +262,6 @@ class Clipper : public virtual ClipperBase { public: Clipper(int initOptions = 0); - ~Clipper(); bool Execute(ClipType clipType, Paths &solution, PolyFillType fillType = pftEvenOdd); @@ -265,28 +276,23 @@ public: PolyTree &polytree, PolyFillType subjFillType, PolyFillType clipFillType); - bool ReverseSolution() const { return m_ReverseOutput; }; + bool ReverseSolution() { return m_ReverseOutput; }; void ReverseSolution(bool value) {m_ReverseOutput = value;}; - bool StrictlySimple() const {return m_StrictSimple;}; + bool StrictlySimple() {return m_StrictSimple;}; void StrictlySimple(bool value) {m_StrictSimple = value;}; //set the callback function for z value filling on intersections (otherwise Z is 0) #ifdef use_xyz void ZFillFunction(ZFillCallback zFillFunc); #endif protected: - void Reset(); virtual bool ExecuteInternal(); private: - PolyOutList m_PolyOuts; JoinList m_Joins; JoinList m_GhostJoins; IntersectList m_IntersectList; ClipType m_ClipType; - typedef std::priority_queue ScanbeamList; - ScanbeamList m_Scanbeam; typedef std::list MaximaList; MaximaList m_Maxima; - TEdge *m_ActiveEdges; TEdge *m_SortedEdges; bool m_ExecuteLocked; PolyFillType m_ClipFillType; @@ -297,42 +303,35 @@ private: #ifdef use_xyz ZFillCallback m_ZFill; //custom callback #endif - void SetWindingCount(TEdge& edge) const; + void SetWindingCount(TEdge& edge); bool IsEvenOddFillType(const TEdge& edge) const; bool IsEvenOddAltFillType(const TEdge& edge) const; - void InsertScanbeam(const cInt Y); - cInt PopScanbeam(); void InsertLocalMinimaIntoAEL(const cInt botY); void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge); void AddEdgeToSEL(TEdge *edge); + bool PopEdgeFromSEL(TEdge *&edge); void CopyAELToSEL(); void DeleteFromSEL(TEdge *e); - void DeleteFromAEL(TEdge *e); - void UpdateEdgeIntoAEL(TEdge *&e); void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2); bool IsContributing(const TEdge& edge) const; bool IsTopHorz(const cInt XPos); - void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2); void DoMaxima(TEdge *e); void ProcessHorizontals(); void ProcessHorizontal(TEdge *horzEdge); void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt); OutRec* GetOutRec(int idx); - void AppendPolygon(TEdge *e1, TEdge *e2) const; + void AppendPolygon(TEdge *e1, TEdge *e2); void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt); - OutRec* CreateOutRec(); OutPt* AddOutPt(TEdge *e, const IntPoint &pt); OutPt* GetLastOutPt(TEdge *e); - void DisposeAllOutRecs(); - void DisposeOutRec(PolyOutList::size_type index); bool ProcessIntersections(const cInt topY); void BuildIntersectList(const cInt topY); void ProcessIntersectList(); void ProcessEdgesAtTopOfScanbeam(const cInt topY); void BuildResult(Paths& polys); void BuildResult2(PolyTree& polytree); - void SetHoleState(TEdge *e, OutRec *outrec) const; + void SetHoleState(TEdge *e, OutRec *outrec); void DisposeIntersectNodes(); bool FixupIntersectionOrder(); void FixupOutPolygon(OutRec &outrec); @@ -340,15 +339,16 @@ private: bool IsHole(TEdge *e); bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl); void FixHoleLinkage(OutRec &outrec); - void AddJoin(OutPt *op1, OutPt *op2, const IntPoint &offPt); + void AddJoin(OutPt *op1, OutPt *op2, const IntPoint offPt); void ClearJoins(); void ClearGhostJoins(); - void AddGhostJoin(OutPt *op, const IntPoint &offPt); + void AddGhostJoin(OutPt *op, const IntPoint offPt); bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2); void JoinCommonEdges(); void DoSimplePolygons(); - void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const; - void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const; + void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); + void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec); + void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec); #ifdef use_xyz void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); #endif From 23b4f6b19352277aea911fe5ce85118a6490ec2b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 27 Nov 2016 15:25:05 +0100 Subject: [PATCH 107/225] Removed useless virtual keywords from clipper --- xs/src/clipper.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/xs/src/clipper.hpp b/xs/src/clipper.hpp index c5bcacc0a..775807602 100755 --- a/xs/src/clipper.hpp +++ b/xs/src/clipper.hpp @@ -137,7 +137,7 @@ class PolyNode { public: PolyNode(); - virtual ~PolyNode(){}; + ~PolyNode(){}; Path Contour; PolyNodes Childs; PolyNode* Parent; @@ -219,17 +219,17 @@ class ClipperBase { public: ClipperBase(); - virtual ~ClipperBase(); - virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); + ~ClipperBase(); + bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); - virtual void Clear(); + void Clear(); IntRect GetBounds(); bool PreserveCollinear() {return m_PreserveCollinear;}; void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; protected: void DisposeLocalMinimaList(); TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); - virtual void Reset(); + void Reset(); TEdge* ProcessBound(TEdge* E, bool IsClockwise); void InsertScanbeam(const cInt Y); bool PopScanbeam(cInt &Y); @@ -258,7 +258,7 @@ protected: }; //------------------------------------------------------------------------------ -class Clipper : public virtual ClipperBase +class Clipper : public ClipperBase { public: Clipper(int initOptions = 0); @@ -285,7 +285,7 @@ public: void ZFillFunction(ZFillCallback zFillFunc); #endif protected: - virtual bool ExecuteInternal(); + bool ExecuteInternal(); private: JoinList m_Joins; JoinList m_GhostJoins; @@ -390,8 +390,8 @@ class clipperException : public std::exception { public: clipperException(const char* description): m_descr(description) {} - virtual ~clipperException() throw() {} - virtual const char* what() const throw() {return m_descr.c_str();} + ~clipperException() throw() {} + const char* what() const throw() {return m_descr.c_str();} private: std::string m_descr; }; From 5242b3e03ab2b195ba9c7c53fba705a8ed1c7abd Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 27 Nov 2016 15:25:22 +0100 Subject: [PATCH 108/225] Minor fixes to parallelize code --- xs/src/libslic3r/PrintConfig.cpp | 8 ++++++-- xs/src/libslic3r/libslic3r.h | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index abdb26bd8..a82ed11ce 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1248,8 +1248,12 @@ PrintConfigDef::PrintConfigDef() def->readonly = true; def->min = 1; def->max = 16; - def->default_value = new ConfigOptionInt(boost::thread::hardware_concurrency() || 2); - + { + int threads = boost::thread::hardware_concurrency(); + if (threads == 0) threads = 2; + def->default_value = new ConfigOptionInt(threads); + } + def = this->add("toolchange_gcode", coString); def->label = "Tool change G-code"; def->tooltip = "This custom code is inserted right before every extruder change. Note that you can use placeholder variables for all Slic3r settings as well as [previous_extruder] and [next_extruder]."; diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index ad8047ac8..561d0b03f 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -4,6 +4,7 @@ // this needs to be included early for MSVC (listing it in Build.PL is not enough) #include #include +#include #include #include #include @@ -90,9 +91,10 @@ template void parallelize(std::queue queue, boost::function func, int threads_count = boost::thread::hardware_concurrency()) { + if (threads_count == 0) threads_count = 2; boost::mutex queue_mutex; boost::thread_group workers; - for (int i = 0; i < threads_count; i++) + for (int i = 0; i < fminf(threads_count, queue.size()); i++) workers.add_thread(new boost::thread(&_parallelize_do, &queue, &queue_mutex, func)); workers.join_all(); } From a7693c4719c0e0e0129aea7461706d2ed53a52cc Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 27 Nov 2016 16:48:48 +0100 Subject: [PATCH 109/225] Fixes and improvements to the view selection menu --- lib/Slic3r/GUI/3DScene.pm | 24 +++++++++++++++--------- lib/Slic3r/GUI/MainFrame.pm | 2 +- xs/xsp/BoundingBox.xsp | 3 +++ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 61d45f1f4..c9bbd7962 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -55,7 +55,7 @@ use constant HAS_VBO => 1; # phi / theta angles to orient the camera. -use constant VIEW_DEFAULT => [45.0,45.0]; +use constant VIEW_ISO => [45.0,45.0]; use constant VIEW_LEFT => [90.0,90.0]; use constant VIEW_RIGHT => [-90.0,90.0]; use constant VIEW_TOP => [0.0,0.0]; @@ -63,7 +63,7 @@ use constant VIEW_BOTTOM => [0.0,180.0]; use constant VIEW_FRONT => [0.0,90.0]; use constant VIEW_REAR => [180.0,90.0]; -use constant GIMBALL_LOCK_THETA_MAX => 170; +use constant GIMBAL_LOCK_THETA_MAX => 170; # make OpenGL::Array thread-safe { @@ -254,7 +254,7 @@ sub mouse_event { if (TURNTABLE_MODE) { $self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE); $self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #- - $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX; + $self->_stheta(GIMBAL_LOCK_THETA_MAX) if $self->_stheta > GIMBAL_LOCK_THETA_MAX; $self->_stheta(0) if $self->_stheta < 0; } else { my $size = $self->GetClientSize; @@ -336,8 +336,8 @@ sub select_view { if (ref($direction)) { $dirvec = $direction; } else { - if ($direction eq 'default') { - $dirvec = VIEW_DEFAULT; + if ($direction eq 'iso') { + $dirvec = VIEW_ISO; } elsif ($direction eq 'left') { $dirvec = VIEW_LEFT; } elsif ($direction eq 'right') { @@ -354,12 +354,18 @@ sub select_view { } $self->_sphi($dirvec->[0]); $self->_stheta($dirvec->[1]); - # Avoid gimball lock. - $self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX; + + # Avoid gimbal lock. + $self->_stheta(GIMBAL_LOCK_THETA_MAX) if $self->_stheta > GIMBAL_LOCK_THETA_MAX; $self->_stheta(0) if $self->_stheta < 0; + # View everything. - $self->zoom_to_volumes; + $self->volumes_bounding_box->defined + ? $self->zoom_to_volumes + : $self->zoom_to_bed; + $self->on_viewport_changed->() if $self->on_viewport_changed; + $self->_dirty(1); $self->Refresh; } @@ -368,7 +374,7 @@ sub zoom_to_bounding_box { # calculate the zoom factor needed to adjust viewport to # bounding box - my $max_size = max(@{$bb->size}) * 2; + my $max_size = max(@{$bb->size}) * 1.05; my $min_viewport_size = min($self->GetSizeWH); $self->_zoom($min_viewport_size / $max_size); diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index a518a3ab2..9acf8f1d4 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -290,7 +290,7 @@ sub _init_menubar { # View menu if (!$self->{no_plater}) { $self->{viewMenu} = Wx::Menu->new; - $self->_append_menu_item($self->{viewMenu}, "Default", 'Default View', sub { $self->select_view('default'); }); + $self->_append_menu_item($self->{viewMenu}, "Iso" , 'Iso View' , sub { $self->select_view('iso' ); }); $self->_append_menu_item($self->{viewMenu}, "Top" , 'Top View' , sub { $self->select_view('top' ); }); $self->_append_menu_item($self->{viewMenu}, "Bottom" , 'Bottom View' , sub { $self->select_view('bottom' ); }); $self->_append_menu_item($self->{viewMenu}, "Front" , 'Front View' , sub { $self->select_view('front' ); }); diff --git a/xs/xsp/BoundingBox.xsp b/xs/xsp/BoundingBox.xsp index ed648a795..53af895bf 100644 --- a/xs/xsp/BoundingBox.xsp +++ b/xs/xsp/BoundingBox.xsp @@ -26,6 +26,7 @@ long x_max() %code{% RETVAL = THIS->max.x; %}; long y_min() %code{% RETVAL = THIS->min.y; %}; long y_max() %code{% RETVAL = THIS->max.y; %}; + bool defined() %code{% RETVAL = THIS->defined; %}; %{ @@ -63,6 +64,7 @@ new_from_points(CLASS, points) void set_x_max(double val) %code{% THIS->max.x = val; %}; void set_y_min(double val) %code{% THIS->min.y = val; %}; void set_y_max(double val) %code{% THIS->max.y = val; %}; + bool defined() %code{% RETVAL = THIS->defined; %}; %{ @@ -97,4 +99,5 @@ new_from_points(CLASS, points) double y_max() %code{% RETVAL = THIS->max.y; %}; double z_min() %code{% RETVAL = THIS->min.z; %}; double z_max() %code{% RETVAL = THIS->max.z; %}; + bool defined() %code{% RETVAL = THIS->defined; %}; }; From 01cd85f7cfe4b12bf6785a7173ebf330f7396633 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 27 Nov 2016 18:04:39 +0100 Subject: [PATCH 110/225] New command for importing 2.5D/TIN meshes in plater --- lib/Slic3r/Format/OBJ.pm | 2 +- lib/Slic3r/Format/STL.pm | 2 +- lib/Slic3r/GUI/MainFrame.pm | 7 ++++ lib/Slic3r/GUI/Plater.pm | 31 ++++++++++++++++++ slic3r.pl | 1 + src/utils/extrude-tin.cpp | 51 ++---------------------------- var/map_add.png | Bin 0 -> 836 bytes xs/src/admesh/stlinit.c | 3 +- xs/src/admesh/util.c | 4 +-- xs/src/libslic3r/TriangleMesh.cpp | 50 +++++++++++++++++++++++++++++ xs/src/libslic3r/TriangleMesh.hpp | 1 + xs/xsp/Model.xsp | 13 ++++++++ 12 files changed, 111 insertions(+), 54 deletions(-) create mode 100755 var/map_add.png diff --git a/lib/Slic3r/Format/OBJ.pm b/lib/Slic3r/Format/OBJ.pm index 616b38cdc..7a92fec82 100644 --- a/lib/Slic3r/Format/OBJ.pm +++ b/lib/Slic3r/Format/OBJ.pm @@ -21,7 +21,7 @@ sub read_file { my $mesh = Slic3r::TriangleMesh->new; $mesh->ReadFromPerl($vertices, $facets); - $mesh->repair; + $mesh->check_topology; my $model = Slic3r::Model->new; diff --git a/lib/Slic3r/Format/STL.pm b/lib/Slic3r/Format/STL.pm index 11928d8b2..a3b859104 100644 --- a/lib/Slic3r/Format/STL.pm +++ b/lib/Slic3r/Format/STL.pm @@ -12,7 +12,7 @@ sub read_file { my $mesh = Slic3r::TriangleMesh->new; $mesh->ReadSTLFile($path); - $mesh->repair; + $mesh->check_topology; die "This STL file couldn't be read because it's empty.\n" if $mesh->facets_count == 0; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 9acf8f1d4..a8190b934 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -179,6 +179,13 @@ sub _init_menubar { # File menu my $fileMenu = Wx::Menu->new; { + $self->_append_menu_item($fileMenu, "Open STL/OBJ/AMF…\tCtrl+O", 'Open a model', sub { + $self->{plater}->add if $self->{plater}; + }, undef, 'brick_add.png'); + $self->_append_menu_item($fileMenu, "Open 2.5D TIN mesh…", 'Import a 2.5D TIN mesh', sub { + $self->{plater}->add_tin if $self->{plater}; + }, undef, 'map_add.png'); + $fileMenu->AppendSeparator(); $self->_append_menu_item($fileMenu, "&Load Config…\tCtrl+L", 'Load exported configuration file', sub { $self->load_config_file; }, undef, 'plugin_add.png'); diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 4a75390a9..b27a3e0cf 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -510,6 +510,36 @@ sub add { $self->load_file($_) for @input_files; } +sub add_tin { + my $self = shift; + + my @input_files = wxTheApp->open_model($self); + return if !@input_files; + + my $offset = Wx::GetNumberFromUser("", "Enter the minimum thickness in mm (i.e. the offset from the lowest point):", "2.5D TIN", + 5, 0, 1000000, $self); + return if $offset < 0; + + foreach my $input_file (@input_files) { + my $model = eval { Slic3r::Model->read_from_file($input_file) }; + Slic3r::GUI::show_error($self, $@) if $@; + next if !$model; + + if ($model->looks_like_multipart_object) { + Slic3r::GUI::show_error($self, "Multi-part models cannot be opened as 2.5D TIN files. Please load a single continuous mesh."); + next; + } + + my $model_object = $model->get_object(0); + eval { + $model_object->get_volume(0)->extrude_tin($offset); + }; + Slic3r::GUI::show_error($self, $@) if $@; + + $self->load_model_objects($model_object); + } +} + sub load_file { my $self = shift; my ($input_file) = @_; @@ -555,6 +585,7 @@ sub load_model_objects { my @obj_idx = (); foreach my $model_object (@model_objects) { my $o = $self->{model}->add_object($model_object); + $o->repair; push @{ $self->{objects} }, Slic3r::GUI::Plater::Object->new( name => basename($model_object->input_file), diff --git a/slic3r.pl b/slic3r.pl index 6f40e57a3..962562530 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -186,6 +186,7 @@ if (@ARGV) { # slicing from command line } else { $model = Slic3r::Model->read_from_file($input_file); } + $model->repair; if ($opt{info}) { $model->print_info; diff --git a/src/utils/extrude-tin.cpp b/src/utils/extrude-tin.cpp index d2b9b49e6..0043b42e8 100644 --- a/src/utils/extrude-tin.cpp +++ b/src/utils/extrude-tin.cpp @@ -34,59 +34,12 @@ main(const int argc, const char **argv) for (t_config_option_keys::const_iterator it = input_files.begin(); it != input_files.end(); ++it) { TriangleMesh mesh; Slic3r::IO::STL::read(*it, &mesh); - calculate_normals(&mesh.stl); - - if (mesh.facets_count() == 0) { - printf("Error: file is empty: %s\n", it->c_str()); - continue; - } - - float z = mesh.stl.stats.min.z - config.option("offset", true)->getFloat(); - printf("min.z = %f, z = %f\n", mesh.stl.stats.min.z, z); - TriangleMesh mesh2 = mesh; - - for (int i = 0; i < mesh.stl.stats.number_of_facets; ++i) { - const stl_facet &facet = mesh.stl.facet_start[i]; - - if (facet.normal.z < 0) { - printf("Invalid 2.5D mesh / TIN (one facet points downwards = %f)\n", facet.normal.z); - exit(1); - } - - for (int j = 0; j < 3; ++j) { - if (mesh.stl.neighbors_start[i].neighbor[j] == -1) { - stl_facet new_facet; - float normal[3]; - - // first triangle - new_facet.vertex[0] = new_facet.vertex[2] = facet.vertex[(j+1)%3]; - new_facet.vertex[1] = facet.vertex[j]; - new_facet.vertex[2].z = z; - stl_calculate_normal(normal, &new_facet); - stl_normalize_vector(normal); - new_facet.normal.x = normal[0]; - new_facet.normal.y = normal[1]; - new_facet.normal.z = normal[2]; - stl_add_facet(&mesh2.stl, &new_facet); - - // second triangle - new_facet.vertex[0] = new_facet.vertex[1] = facet.vertex[j]; - new_facet.vertex[2] = facet.vertex[(j+1)%3]; - new_facet.vertex[1].z = new_facet.vertex[2].z = z; - new_facet.normal.x = normal[0]; - new_facet.normal.y = normal[1]; - new_facet.normal.z = normal[2]; - stl_add_facet(&mesh2.stl, &new_facet); - } - } - } - - mesh2.repair(); + mesh.extrude_tin(config.option("offset", true)->getFloat()); std::string outfile = config.option("output", true)->getString(); if (outfile.empty()) outfile = *it + "_extruded.stl"; - Slic3r::IO::STL::write(mesh2, outfile); + Slic3r::IO::STL::write(mesh, outfile); printf("Extruded mesh written to %s\n", outfile.c_str()); } diff --git a/var/map_add.png b/var/map_add.png new file mode 100755 index 0000000000000000000000000000000000000000..2b72da06ae25cb1cd191d100b7ad2080fb2e2e0c GIT binary patch literal 836 zcmV-K1H1f*P)%4Eq2Gf?M=uT9%H^ z8mm>^ym`Tm?6?O@iCV4h@k*i;m#t2+H|C_oG_K0ZUyH!)t9 zyt~8Xrz-|8Z%~=;n(eJ9SD&=DQc@l%Yqok=B@hunr_+_CEy-%4IhOnPSvp_*rN}#? z-f5k7s@KhQvedAb2_T3AV03gswVoNT*FpKMEk-{$FV3H`v2jgjv~5%Mipt26Ui+?6 z^tOOF0GdfAO}ohay70)oeq{YKx#v#Yv~V!Xh2@6AiwZM}BW3p*+TVj&2qPds6D z&~>@ha<^ADH96(czF^dCwVE49N@+h}!!>jVp_u}(=ED!IB0$PL2Qr=e?sTvy{x27_t zx~6K&d}8zc1xwkAa;SP^`+MNXzT=*r*{LuT5rhz_sjG&XNnj=^34AL7flM9_nKD# O0000 #include #include +#include #ifdef SLIC3R_DEBUG #include "SVG.hpp" @@ -426,6 +427,55 @@ TriangleMesh::require_shared_vertices() if (this->stl.v_shared == NULL) stl_generate_shared_vertices(&(this->stl)); } +void +TriangleMesh::extrude_tin(float offset) +{ + calculate_normals(&this->stl); + + const int number_of_facets = this->stl.stats.number_of_facets; + if (number_of_facets == 0) + throw std::runtime_error("Error: file is empty"); + + const float z = this->stl.stats.min.z - offset; + + for (int i = 0; i < number_of_facets; ++i) { + const stl_facet &facet = this->stl.facet_start[i]; + + if (facet.normal.z < 0) + throw std::runtime_error("Invalid 2.5D mesh: at least one facet points downwards."); + + for (int j = 0; j < 3; ++j) { + if (this->stl.neighbors_start[i].neighbor[j] == -1) { + stl_facet new_facet; + float normal[3]; + + // first triangle + new_facet.vertex[0] = new_facet.vertex[2] = facet.vertex[(j+1)%3]; + new_facet.vertex[1] = facet.vertex[j]; + new_facet.vertex[2].z = z; + stl_calculate_normal(normal, &new_facet); + stl_normalize_vector(normal); + new_facet.normal.x = normal[0]; + new_facet.normal.y = normal[1]; + new_facet.normal.z = normal[2]; + stl_add_facet(&this->stl, &new_facet); + + // second triangle + new_facet.vertex[0] = new_facet.vertex[1] = facet.vertex[j]; + new_facet.vertex[2] = facet.vertex[(j+1)%3]; + new_facet.vertex[1].z = new_facet.vertex[2].z = z; + new_facet.normal.x = normal[0]; + new_facet.normal.y = normal[1]; + new_facet.normal.z = normal[2]; + stl_add_facet(&this->stl, &new_facet); + } + } + } + stl_get_size(&this->stl); + + this->repair(); +} + void TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const { diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 2c538c3f5..dc8450a51 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -55,6 +55,7 @@ class TriangleMesh void reset_repair_stats(); bool needed_repair() const; size_t facets_count() const; + void extrude_tin(float offset); stl_file stl; bool repaired; diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 187b57e68..83576c814 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -71,6 +71,7 @@ void duplicate_objects(unsigned int copies_num, double dist, BoundingBoxf* bb = NULL); void duplicate_objects_grid(unsigned int x, unsigned int y, double dist); void print_info(); + void repair(); }; @@ -142,6 +143,8 @@ ModelMaterial::attributes() void clear_volumes(); int volumes_count() %code%{ RETVAL = THIS->volumes.size(); %}; + Ref get_volume(int idx) + %code%{ RETVAL = THIS->volumes.at(idx); %}; %name{_add_instance} Ref add_instance(); Ref _add_instance_clone(ModelInstance* other) @@ -198,6 +201,7 @@ ModelMaterial::attributes() %}; void print_info(); + void repair(); }; @@ -225,6 +229,15 @@ ModelMaterial::attributes() %code%{ THIS->modifier = modifier; %}; ModelMaterial* assign_unique_material(); + + void extrude_tin(float offset) + %code%{ + try { + THIS->mesh.extrude_tin(offset); + } catch (std::exception& e) { + croak("%s", e.what()); + } + %}; }; From b8c4464a207aa412f3e994764390534fb06528f5 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 27 Nov 2016 18:18:01 +0100 Subject: [PATCH 111/225] Try to fix compilation error about the new threads default --- lib/Slic3r/Config.pm | 2 +- xs/src/libslic3r/PrintConfig.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 5cd94cf32..6c51477a3 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -218,7 +218,7 @@ sub validate { my $self = shift; # -j, --threads - die "Invalid value for --threads\n" + die "Invalid value for --threads: " . $self->threads . "\n" if $self->threads < 1; # --layer-height diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index a82ed11ce..490550abd 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1249,7 +1249,7 @@ PrintConfigDef::PrintConfigDef() def->min = 1; def->max = 16; { - int threads = boost::thread::hardware_concurrency(); + unsigned int threads = boost::thread::hardware_concurrency(); if (threads == 0) threads = 2; def->default_value = new ConfigOptionInt(threads); } From 9eb28371def188032242fbfbaa56475e0440efb5 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 27 Nov 2016 18:31:21 +0100 Subject: [PATCH 112/225] Maybe the Travis CI server has more than 16 cores? --- lib/Slic3r/Config.pm | 2 +- xs/src/libslic3r/PrintConfig.cpp | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 6c51477a3..5cd94cf32 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -218,7 +218,7 @@ sub validate { my $self = shift; # -j, --threads - die "Invalid value for --threads: " . $self->threads . "\n" + die "Invalid value for --threads\n" if $self->threads < 1; # --layer-height diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 490550abd..6ab205375 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1247,11 +1247,9 @@ PrintConfigDef::PrintConfigDef() def->cli = "threads|j=i"; def->readonly = true; def->min = 1; - def->max = 16; { unsigned int threads = boost::thread::hardware_concurrency(); - if (threads == 0) threads = 2; - def->default_value = new ConfigOptionInt(threads); + def->default_value = new ConfigOptionInt(threads > 0 ? threads : 2); } def = this->add("toolchange_gcode", coString); From d398c27e1fa437eb137461428d96798439ea2eba Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 27 Nov 2016 11:34:38 -0600 Subject: [PATCH 113/225] Avoid recentering viewport if the bounding box is max size 0, as noticed by @jreinam --- lib/Slic3r/GUI/3DScene.pm | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 61d45f1f4..c16312b21 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -370,12 +370,15 @@ sub zoom_to_bounding_box { # bounding box my $max_size = max(@{$bb->size}) * 2; my $min_viewport_size = min($self->GetSizeWH); - $self->_zoom($min_viewport_size / $max_size); + if ($max_size != 0) { + # only re-zoom if we have a valid bounding box, avoid a divide by 0 error. + $self->_zoom($min_viewport_size / $max_size); - # center view around bounding box center - $self->_camera_target($bb->center); + # center view around bounding box center + $self->_camera_target($bb->center); - $self->on_viewport_changed->() if $self->on_viewport_changed; + $self->on_viewport_changed->() if $self->on_viewport_changed; + } } sub zoom_to_bed { From 3a70a448abe56aa1b473b928887af61ee483c9e6 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 27 Nov 2016 16:05:28 -0600 Subject: [PATCH 114/225] Use LambdaObjectDialog in GUI.pm to avoid crash on Windows. --- lib/Slic3r/GUI.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index c95e9afbb..a29845189 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -24,6 +24,7 @@ use Slic3r::GUI::Plater::3DPreview; use Slic3r::GUI::Plater::ObjectPartsPanel; use Slic3r::GUI::Plater::ObjectCutDialog; use Slic3r::GUI::Plater::ObjectSettingsDialog; +use Slic3r::GUI::Plater::LambdaObjectDialog; use Slic3r::GUI::Plater::OverrideSettingsPanel; use Slic3r::GUI::Preferences; use Slic3r::GUI::ProgressStatusBar; From efeb49e3a7831930a83d406850f4988690f3eb00 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 27 Nov 2016 16:06:45 -0600 Subject: [PATCH 115/225] Added new constructor to TriangleMesh that accepts vectors of Points and make_cube function to make a new simple cube mesh. --- xs/src/libslic3r/TriangleMesh.cpp | 61 +++++++++++++++++++++++++++++++ xs/src/libslic3r/TriangleMesh.hpp | 3 ++ xs/xsp/TriangleMesh.xsp | 2 + 3 files changed, 66 insertions(+) diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index c1d7a9edf..b43b1f0d6 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -24,6 +24,48 @@ TriangleMesh::TriangleMesh() stl_initialize(&this->stl); } +TriangleMesh::TriangleMesh(const Pointf3s &points, const std::vector& facets ) + : repaired(false) +{ + stl_initialize(&this->stl); + stl_file &stl = this->stl; + stl.error = 0; + stl.stats.type = inmemory; + + // count facets and allocate memory + stl.stats.number_of_facets = facets.size(); + stl.stats.original_num_facets = stl.stats.number_of_facets; + stl_allocate(&stl); + + for (int i = 0; i < stl.stats.number_of_facets; i++) { + stl_facet facet; + facet.normal.x = 0; + facet.normal.y = 0; + facet.normal.z = 0; + + const Pointf3& ref_f1 = points[facets[i].x]; + facet.vertex[0].x = ref_f1.x; + facet.vertex[0].y = ref_f1.y; + facet.vertex[0].z = ref_f1.z; + + const Pointf3& ref_f2 = points[facets[i].y]; + facet.vertex[1].x = ref_f2.x; + facet.vertex[1].y = ref_f2.y; + facet.vertex[1].z = ref_f2.z; + + const Pointf3& ref_f3 = points[facets[i].z]; + facet.vertex[2].x = ref_f3.x; + facet.vertex[2].y = ref_f3.y; + facet.vertex[2].z = ref_f3.z; + + facet.extra[0] = 0; + facet.extra[1] = 0; + + stl.facet_start[i] = facet; + } + stl_get_size(&stl); +} + TriangleMesh::TriangleMesh(const TriangleMesh &other) : stl(other.stl), repaired(other.repaired) { @@ -1120,4 +1162,23 @@ TriangleMeshSlicer::~TriangleMeshSlicer() if (this->v_scaled_shared != NULL) free(this->v_scaled_shared); } +TriangleMesh make_cube(double x, double y, double z) { + Pointf3 pv[8] = { + Pointf3(x, y, 0), Pointf3(x, 0, 0), Pointf3(0, 0, 0), + Pointf3(0, y, 0), Pointf3(x, y, z), Pointf3(0, y, z), + Pointf3(0, 0, z), Pointf3(x, 0, z) + }; + Point3 fv[12] = { + Point3(0, 1, 2), Point3(0, 2, 3), Point3(4, 5, 6), + Point3(4, 6, 7), Point3(0, 4, 7), Point3(0, 7, 1), + Point3(1, 7, 6), Point3(1, 6, 2), Point3(2, 6, 5), + Point3(2, 5, 3), Point3(4, 0, 3), Point3(4, 3, 5) + }; + + std::vector facets(&fv[0], &fv[0]+12); + Pointf3s vertices(&pv[0], &pv[0]+8); + + TriangleMesh mesh(vertices ,facets); + return mesh; +} } diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 2c538c3f5..5a393738a 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -21,6 +21,7 @@ class TriangleMesh { public: TriangleMesh(); + TriangleMesh(const Pointf3s &points, const std::vector &facets); TriangleMesh(const TriangleMesh &other); TriangleMesh& operator= (TriangleMesh other); void swap(TriangleMesh &other); @@ -112,6 +113,8 @@ class TriangleMeshSlicer void make_expolygons(std::vector &lines, ExPolygons* slices) const; }; +TriangleMesh make_cube(double x, double y, double z); + } #endif diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 3b8f1022c..5f67b9561 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -39,6 +39,8 @@ %code{% RETVAL = THIS->bounding_box().center(); %}; int facets_count(); void reset_repair_stats(); + Clone cube(double x, double y, double z) + %code{% RETVAL = make_cube(x, y, z); %}; %{ void From 2171d6ab969d3ba807de99b16a8660fc1c595b5e Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 27 Nov 2016 19:15:27 -0600 Subject: [PATCH 116/225] Added prototype make_cylinder() --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 2 +- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 13 ++++--- xs/src/libslic3r/TriangleMesh.cpp | 40 ++++++++++++++++++++- xs/src/libslic3r/TriangleMesh.hpp | 3 ++ xs/xsp/TriangleMesh.xsp | 2 ++ 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm index d6d4c3b09..2f815c27e 100644 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -60,7 +60,7 @@ sub new { }, label_width => 100, ); - my @options = ("box"); + my @options = ("box", "cylinder"); $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 1fb0e3bd9..5049d7dda 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -342,14 +342,19 @@ sub on_btn_lambda { } my $params = $dlg->ObjectParameter; my $name = "lambda-".$params->{"type"}; + my $mesh = Slic3r::TriangleMesh->new(); - my $new_volume = $self->{model_object}->add_volume(mesh => Slic3r::Test::mesh($params->{"type"}, dim=>$params->{"dim"}), material_id=>"generic"); + #TODO support non-boxes + if ($name eq "box") { + $mesh = $mesh->cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]); + } elsif ($name eq "cylinder") { + $mesh = $mesh->cylinder($params->{"dim"}[0], $params->{"dim"}[1]); + } + + my $new_volume = $self->{model_object}->add_volume(mesh => $mesh); $new_volume->set_modifier($is_modifier); $new_volume->set_name($name); - # apply the same translation we applied to the object - $new_volume->mesh->translate(@{$self->{model_object}->origin_translation}); - # set a default extruder value, since user can't add it manually $new_volume->config->set_ifndef('extruder', 0); diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index b43b1f0d6..b71f29215 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -1161,7 +1161,7 @@ TriangleMeshSlicer::~TriangleMeshSlicer() { if (this->v_scaled_shared != NULL) free(this->v_scaled_shared); } - +// Generate the vertex list for a cube solid of arbitrary size in X/Y/Z. TriangleMesh make_cube(double x, double y, double z) { Pointf3 pv[8] = { Pointf3(x, y, 0), Pointf3(x, 0, 0), Pointf3(0, 0, 0), @@ -1181,4 +1181,42 @@ TriangleMesh make_cube(double x, double y, double z) { TriangleMesh mesh(vertices ,facets); return mesh; } + +// Generate the mesh for a cylinder and return it, using +// the generated angle to calculate the top mesh triangles. +TriangleMesh make_cylinder(double r, double h, double fa) { + Pointf3s vertices; + std::vector facets; + + // 2 special vertices, top and bottom center, rest are relative to this + vertices.push_back(Pointf3(0.0, 0.0, 0.0)); + vertices.push_back(Pointf3(0.0, 0.0, h)); + + // adjust via rounding + double angle = (2*PI / floor(2*PI / fa)); + + // for each line along the polygon approximating the top/bottom of the + // circle, generate four points and four facets (2 for the wall, 2 for the + // top and bottom. + // Special case: Last line shares 2 vertices with the first line. + unsigned id = 3; + vertices.push_back(Pointf3(sin(0) * r , cos(0) * r, 0)); + vertices.push_back(Pointf3(sin(0) * r , cos(0) * r, h)); + for (double i = angle; i < 2*PI; i+=angle) { + vertices.push_back(Pointf3(sin(i) * r , cos(i) * r, 0)); + vertices.push_back(Pointf3(sin(i) * r , cos(i) * r, h)); + id += 2; + facets.push_back(Point3(0, id - 1, id - 3)); + facets.push_back(Point3(1, id, id - 2)); + facets.push_back(Point3(id, id - 1, id - 3)); + facets.push_back(Point3(id - 3, id - 2, id)); + } + facets.push_back(Point3(0, 2, id -1)); + facets.push_back(Point3(1, 3, id)); + facets.push_back(Point3(id - 1, 2, 3)); + facets.push_back(Point3(id - 1, 3, id)); + + TriangleMesh mesh(vertices ,facets); + return mesh; +} } diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 5a393738a..586deb414 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -115,6 +115,9 @@ class TriangleMeshSlicer TriangleMesh make_cube(double x, double y, double z); +// Generate a TriangleMesh of a cylinder +TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360)); + } #endif diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 5f67b9561..69035ba37 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -41,6 +41,8 @@ void reset_repair_stats(); Clone cube(double x, double y, double z) %code{% RETVAL = make_cube(x, y, z); %}; + Clone cylinder(double r, double h) + %code{% RETVAL = make_cylinder(r, h); %}; %{ void From d961a88969740aaf33304af36a33975e7a5c28a2 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 27 Nov 2016 22:34:10 -0600 Subject: [PATCH 117/225] Fixed mesh generation to generate cylinders. --- xs/src/libslic3r/TriangleMesh.cpp | 36 ++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index b71f29215..f7b26aa83 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -1184,6 +1184,7 @@ TriangleMesh make_cube(double x, double y, double z) { // Generate the mesh for a cylinder and return it, using // the generated angle to calculate the top mesh triangles. +// Default is 360 sides, angle fa is in radians. TriangleMesh make_cylinder(double r, double h, double fa) { Pointf3s vertices; std::vector facets; @@ -1192,31 +1193,36 @@ TriangleMesh make_cylinder(double r, double h, double fa) { vertices.push_back(Pointf3(0.0, 0.0, 0.0)); vertices.push_back(Pointf3(0.0, 0.0, h)); - // adjust via rounding + // adjust via rounding to get an even multiple for any provided angle. double angle = (2*PI / floor(2*PI / fa)); // for each line along the polygon approximating the top/bottom of the // circle, generate four points and four facets (2 for the wall, 2 for the // top and bottom. // Special case: Last line shares 2 vertices with the first line. - unsigned id = 3; + unsigned id = vertices.size() - 1; vertices.push_back(Pointf3(sin(0) * r , cos(0) * r, 0)); vertices.push_back(Pointf3(sin(0) * r , cos(0) * r, h)); - for (double i = angle; i < 2*PI; i+=angle) { - vertices.push_back(Pointf3(sin(i) * r , cos(i) * r, 0)); - vertices.push_back(Pointf3(sin(i) * r , cos(i) * r, h)); - id += 2; - facets.push_back(Point3(0, id - 1, id - 3)); - facets.push_back(Point3(1, id, id - 2)); - facets.push_back(Point3(id, id - 1, id - 3)); - facets.push_back(Point3(id - 3, id - 2, id)); + for (double i = 0; i < 2*PI; i+=angle) { + Pointf3 b(0, r, 0); + Pointf3 t(0, r, h); + b.rotate(i, Pointf3(0,0,0)); + t.rotate(i, Pointf3(0,0,h)); + vertices.push_back(b); + vertices.push_back(t); + id = vertices.size() - 1; + facets.push_back(Point3( 0, id - 1, id - 3)); // top + facets.push_back(Point3(id, 1, id - 2)); // bottom + facets.push_back(Point3(id, id - 2, id - 3)); // upper-right of side + facets.push_back(Point3(id, id - 3, id - 1)); // bottom-left of side } - facets.push_back(Point3(0, 2, id -1)); - facets.push_back(Point3(1, 3, id)); - facets.push_back(Point3(id - 1, 2, 3)); - facets.push_back(Point3(id - 1, 3, id)); + // Connect the last set of vertices with the first. + facets.push_back(Point3( 2, 0, id - 1)); + facets.push_back(Point3( 1, 3, id)); + facets.push_back(Point3(id, 3, 2)); + facets.push_back(Point3(id, 2, id - 1)); - TriangleMesh mesh(vertices ,facets); + TriangleMesh mesh(vertices, facets); return mesh; } } From 7aede7aa2891f1c4e587286b98cd8656773efee1 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 27 Nov 2016 22:35:19 -0600 Subject: [PATCH 118/225] Menu now works to select cylinders (reusing the gui options) --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 5049d7dda..a34a4d7f5 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -341,16 +341,18 @@ sub on_btn_lambda { return; } my $params = $dlg->ObjectParameter; + my $type = "".$params->{"type"}; my $name = "lambda-".$params->{"type"}; my $mesh = Slic3r::TriangleMesh->new(); #TODO support non-boxes - if ($name eq "box") { + if ($type eq "box") { $mesh = $mesh->cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]); - } elsif ($name eq "cylinder") { + } elsif ($type eq "cylinder") { $mesh = $mesh->cylinder($params->{"dim"}[0], $params->{"dim"}[1]); + } else { + return; } - my $new_volume = $self->{model_object}->add_volume(mesh => $mesh); $new_volume->set_modifier($is_modifier); $new_volume->set_name($name); From 9e2e5079f0029f2e4ea04fde537825092fc93033 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 27 Nov 2016 23:45:40 -0600 Subject: [PATCH 119/225] UI now changes its options based on the object type selected. --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 71 +++++++++++++++++---- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm index 2f815c27e..17a3ad613 100644 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -19,8 +19,10 @@ sub new { # Note whether the window was already closed, so a pending update is not executed. $self->{already_closed} = 0; $self->{object_parameters} = { - type => "box", + type => "box", dim => [1, 1, 1], + cyl_r => 1, + cyl_h => 1, }; $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); @@ -44,51 +46,84 @@ sub new { $self->Destroy; }); - my $optgroup; - $optgroup = $self->{optgroup} = Slic3r::GUI::OptionsGroup->new( + my $optgroup_box; + $optgroup_box = $self->{optgroup_box} = Slic3r::GUI::OptionsGroup->new( parent => $self, - title => 'Add Generic...', + title => 'Add Cube...', on_change => sub { # Do validation my ($opt_id) = @_; if ($opt_id == 0 || $opt_id == 1 || $opt_id == 2) { - if (!looks_like_number($optgroup->get_value($opt_id))) { + if (!looks_like_number($optgroup_box->get_value($opt_id))) { return 0; } } - $self->{object_parameters}->{dim}[$opt_id] = $optgroup->get_value($opt_id); + $self->{object_parameters}->{dim}[$opt_id] = $optgroup_box->get_value($opt_id); }, label_width => 100, ); my @options = ("box", "cylinder"); $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 0, label => 'L', type => 'f', default => '1', )); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 1, label => 'W', type => 'f', default => '1', )); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 2, label => 'H', type => 'f', default => '1', )); + + my $optgroup_cylinder; + $optgroup_cylinder = $self->{optgroup_cylinder} = Slic3r::GUI::OptionsGroup->new( + parent => $self, + title => 'Add Cylinder...', + on_change => sub { + # Do validation + my ($opt_id) = @_; + if ($opt_id eq 'cyl_r' || $opt_id eq 'cyl_h') { + if (!looks_like_number($optgroup_cylinder->get_value($opt_id))) { + return 0; + } + } + $self->{object_parameters}->{$opt_id} = $optgroup_cylinder->get_value($opt_id); + }, + label_width => 100, + ); + + $optgroup_cylinder->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => "cyl_r", + label => 'Radius', + type => 'f', + default => '1', + )); + $optgroup_cylinder->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => "cyl_h", + label => 'Height', + type => 'f', + default => '1', + )); EVT_COMBOBOX($self, 1, sub{ $self->{object_parameters}->{type} = $self->{type}->GetValue(); + $self->_update_ui; }); - $optgroup->sizer->Add($self->{type}, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - $self->{sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->{sizer}->Add($self->{type}, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->{sizer}->Add($optgroup_box->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->{sizer}->Add($optgroup_cylinder->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->{sizer}->Add($button_sizer,0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->_update_ui; $self->SetSizer($self->{sizer}); $self->{sizer}->Fit($self); @@ -104,4 +139,18 @@ sub ObjectParameter { my ($self) = @_; return $self->{object_parameters}; } + +sub _update_ui { + my ($self) = @_; + $self->{sizer}->Hide($self->{optgroup_cylinder}->sizer); + $self->{sizer}->Hide($self->{optgroup_box}->sizer); + if ($self->{type}->GetValue eq "box") { + $self->{sizer}->Show($self->{optgroup_box}->sizer); + } elsif ($self->{type}->GetValue eq "cylinder") { + $self->{sizer}->Show($self->{optgroup_cylinder}->sizer); + } + $self->{sizer}->Fit($self); + $self->{sizer}->SetSizeHints($self); + +} 1; From 9ffba8c4346b9f71dd86131f1c3936d72c591931 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 27 Nov 2016 23:46:05 -0600 Subject: [PATCH 120/225] Disable sliders if a volume is not selected. --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 30 +++++++++++------------ 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index a34a4d7f5..2e8a57cf8 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -78,8 +78,8 @@ sub new { my $settings_sizer = Wx::StaticBoxSizer->new($self->{staticbox} = Wx::StaticBox->new($self, -1, "Part Settings"), wxVERTICAL); $settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0); - my $optgroup; - $optgroup = $self->{optgroup} = Slic3r::GUI::OptionsGroup->new( + my $optgroup_movers; + $optgroup_movers = $self->{optgroup_movers} = Slic3r::GUI::OptionsGroup->new( parent => $self, title => 'Move', on_change => sub { @@ -88,8 +88,8 @@ sub new { # genates tens of events for a single value change. # Only trigger the recalculation if the value changes # or a live preview was activated and the mesh cut is not valid yet. - if ($self->{move_options}{$opt_id} != $optgroup->get_value($opt_id)) { - $self->{move_options}{$opt_id} = $optgroup->get_value($opt_id); + if ($self->{move_options}{$opt_id} != $optgroup_movers->get_value($opt_id)) { + $self->{move_options}{$opt_id} = $optgroup_movers->get_value($opt_id); wxTheApp->CallAfter(sub { $self->_update; }); @@ -97,7 +97,7 @@ sub new { }, label_width => 20, ); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + $optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 'x', type => 'slider', label => 'X', @@ -106,7 +106,7 @@ sub new { max => $self->{model_object}->bounding_box->size->x*4, full_width => 1, )); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + $optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 'y', type => 'slider', label => 'Y', @@ -115,7 +115,7 @@ sub new { max => $self->{model_object}->bounding_box->size->y*4, full_width => 1, )); - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + $optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 'z', type => 'slider', label => 'Z', @@ -124,14 +124,13 @@ sub new { max => $self->{model_object}->bounding_box->size->z*4, full_width => 1, )); - - + # left pane with tree my $left_sizer = Wx::BoxSizer->new(wxVERTICAL); $left_sizer->Add($tree, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10); $left_sizer->Add($buttons_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10); $left_sizer->Add($settings_sizer, 1, wxEXPAND | wxALL, 0); - $left_sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $left_sizer->Add($optgroup_movers->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); # right pane with preview canvas my $canvas; @@ -247,9 +246,9 @@ sub selection_changed { $self->{settings_panel}->set_config(undef); # reset move sliders - $self->{optgroup}->set_value("x", 0); - $self->{optgroup}->set_value("y", 0); - $self->{optgroup}->set_value("z", 0); + $self->{optgroup_movers}->set_value("x", 0); + $self->{optgroup_movers}->set_value("y", 0); + $self->{optgroup_movers}->set_value("z", 0); $self->{move_options} = { x => 0, y => 0, @@ -269,6 +268,7 @@ sub selection_changed { $self->{canvas}->volumes->[ $itemData->{volume_id} ]{selected} = 1; } $self->{btn_delete}->Enable; + $self->{optgroup_movers}->enable; # attach volume config to settings panel my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ]; @@ -281,6 +281,7 @@ sub selection_changed { # select nothing in 3D preview # attach object config to settings panel + $self->{optgroup_movers}->disable; $self->{staticbox}->SetLabel('Object Settings'); @opt_keys = (map @{$_->get_keys}, Slic3r::Config::PrintObject->new, Slic3r::Config::PrintRegion->new); $config = $self->{model_object}->config; @@ -345,11 +346,10 @@ sub on_btn_lambda { my $name = "lambda-".$params->{"type"}; my $mesh = Slic3r::TriangleMesh->new(); - #TODO support non-boxes if ($type eq "box") { $mesh = $mesh->cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]); } elsif ($type eq "cylinder") { - $mesh = $mesh->cylinder($params->{"dim"}[0], $params->{"dim"}[1]); + $mesh = $mesh->cylinder($params->{"cyl_r"}, $params->{"cyl_h"}); } else { return; } From f0a45356c83a6e32fe254d789494e6ef828ea102 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 28 Nov 2016 02:46:43 -0600 Subject: [PATCH 121/225] Made cube and cylinder static functions of the package, not some specific TriangleMesh object. --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 6 +++--- xs/xsp/TriangleMesh.xsp | 20 ++++++++++++++++---- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 2e8a57cf8..579f3d5ff 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -344,12 +344,12 @@ sub on_btn_lambda { my $params = $dlg->ObjectParameter; my $type = "".$params->{"type"}; my $name = "lambda-".$params->{"type"}; - my $mesh = Slic3r::TriangleMesh->new(); + my $mesh; if ($type eq "box") { - $mesh = $mesh->cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]); + $mesh = Slic3r::TriangleMesh::cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]); } elsif ($type eq "cylinder") { - $mesh = $mesh->cylinder($params->{"cyl_r"}, $params->{"cyl_h"}); + $mesh = Slic3r::TriangleMesh::cylinder($params->{"cyl_r"}, $params->{"cyl_h"}); } else { return; } diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 69035ba37..8fdd3a5da 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -39,10 +39,7 @@ %code{% RETVAL = THIS->bounding_box().center(); %}; int facets_count(); void reset_repair_stats(); - Clone cube(double x, double y, double z) - %code{% RETVAL = make_cube(x, y, z); %}; - Clone cylinder(double r, double h) - %code{% RETVAL = make_cylinder(r, h); %}; + %{ void @@ -230,6 +227,21 @@ TriangleMesh::bb3() OUTPUT: RETVAL + +Clone +cube(double x, double y, double z) + CODE: + RETVAL = make_cube(x,y,z); + OUTPUT: + RETVAL + +Clone +cylinder(double r, double h) + CODE: + RETVAL = make_cylinder(r, h); + OUTPUT: + RETVAL + %} }; From cba3b19be4d269a35c28abf7ad20f7df0922adf0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 29 Nov 2016 19:25:08 +0100 Subject: [PATCH 122/225] Handle input file errors gracefully --- src/slic3r.cpp | 25 +++++++++++++++++++++++-- xs/src/libslic3r/IO.cpp | 8 ++++++-- xs/src/libslic3r/TriangleMesh.cpp | 1 + 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/slic3r.cpp b/src/slic3r.cpp index f96b05862..bbec6a5cf 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -8,6 +8,7 @@ #include #include #include +#include "boost/filesystem.hpp" using namespace Slic3r; @@ -33,8 +34,18 @@ main(const int argc, const char **argv) // load config files supplied via --load for (std::vector::const_iterator file = cli_config.load.values.begin(); file != cli_config.load.values.end(); ++file) { + if (!boost::filesystem::exists(*file)) { + std::cout << "No such file: " << *file << std::endl; + exit(1); + } + DynamicPrintConfig c; - c.load(*file); + try { + c.load(*file); + } catch (std::exception &e) { + std::cout << "Error while reading config file: " << e.what() << std::endl; + exit(1); + } c.normalize(); print_config.apply(c); } @@ -50,9 +61,19 @@ main(const int argc, const char **argv) // read input file(s) if any std::vector models; for (t_config_option_keys::const_iterator it = input_files.begin(); it != input_files.end(); ++it) { + if (!boost::filesystem::exists(*it)) { + std::cout << "No such file: " << *it << std::endl; + exit(1); + } + Model model; // TODO: read other file formats with Model::read_from_file() - Slic3r::IO::STL::read(*it, &model); + try { + Slic3r::IO::STL::read(*it, &model); + } catch (std::exception &e) { + std::cout << *it << ": " << e.what() << std::endl; + exit(1); + } if (model.objects.empty()) { printf("Error: file is empty: %s\n", it->c_str()); diff --git a/xs/src/libslic3r/IO.cpp b/xs/src/libslic3r/IO.cpp index 4a44cf23c..9aabe0c45 100644 --- a/xs/src/libslic3r/IO.cpp +++ b/xs/src/libslic3r/IO.cpp @@ -8,8 +8,12 @@ namespace Slic3r { namespace IO { bool STL::read(std::string input_file, TriangleMesh* mesh) { - mesh->ReadSTLFile(input_file); - mesh->check_topology(); + try { + mesh->ReadSTLFile(input_file); + mesh->check_topology(); + } catch (...) { + throw std::runtime_error("Error while reading STL file"); + } return true; } diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 26432c569..c0a2063cd 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -69,6 +69,7 @@ TriangleMesh::~TriangleMesh() { void TriangleMesh::ReadSTLFile(const std::string &input_file) { stl_open(&stl, input_file.c_str()); + if (this->stl.error != 0) throw std::runtime_error("Failed to read STL file"); } void From 2dbc5d6e07eff10d030d55cc479fa0b64195284a Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Tue, 29 Nov 2016 20:46:44 -0600 Subject: [PATCH 123/225] Fix perl redefining multiple functions also defined/used by boostlib through the include chain. (#3593) Resolved ordering dependency (thanks win32 perl!) --- xs/src/xsinit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index d414afc40..86dbcad83 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -31,6 +31,7 @@ #include #include #include +#include #ifdef SLIC3RXS extern "C" { @@ -50,7 +51,6 @@ extern "C" { } #endif -#include #include #include #include From 3f3f69650289287613a9efe142074e6bd6f5ba68 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 30 Nov 2016 09:38:50 +0100 Subject: [PATCH 124/225] Infill for SLAPrint --- lib/Slic3r/GUI/Projector.pm | 52 +++++++----- xs/src/libslic3r/ClipperUtils.cpp | 4 +- xs/src/libslic3r/PrintConfig.hpp | 10 +++ xs/src/libslic3r/SLAPrint.cpp | 136 ++++++++++++++++++++++++++---- xs/src/libslic3r/SLAPrint.hpp | 9 +- xs/xsp/SLAPrint.xsp | 19 +++-- 6 files changed, 183 insertions(+), 47 deletions(-) diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index 5ccd53b59..e75ac7e09 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -521,7 +521,6 @@ has 'sender' => (is => 'rw'); has 'timer' => (is => 'rw'); has 'is_printing' => (is => 'rw', default => sub { 0 }); has '_print' => (is => 'rw'); -has '_layers' => (is => 'rw'); has '_heights' => (is => 'rw'); has '_layer_num' => (is => 'rw'); has '_timer_cb' => (is => 'rw'); @@ -545,8 +544,7 @@ sub BUILD { $print->slice; $progress_dialog->Destroy; } - - $self->_layers([ map $print->slices($_), 0..($print->layer_count-1) ]); + $self->_heights($print->heights); } @@ -639,7 +637,7 @@ sub print_completed { sub is_projecting { my ($self) = @_; - return defined $self->screen->layer; + return defined $self->screen->layer_num; } sub project_layer { @@ -650,7 +648,7 @@ sub project_layer { return; } - $self->screen->project_layer($self->_layers->[$layer_num], $layer_num); + $self->screen->project_layer($layer_num); } sub project_next_layer { @@ -719,7 +717,7 @@ sub total_resin { for my $i (0..($self->_print->layer_count-1)) { my $lh = $self->_heights->[$i] - ($i == 0 ? 0 : $self->_heights->[$i-1]); - $vol += unscale(unscale($_->area)) * $lh for @{ $self->_print->slices($i) }; + $vol += unscale(unscale($_->area)) * $lh for @{ $self->_print->layer_slices($i) }; } return $vol/1000/1000; # liters @@ -742,7 +740,7 @@ use List::Util qw(min); use Slic3r::Geometry qw(X Y unscale scale); use Slic3r::Geometry::Clipper qw(intersection_pl); -__PACKAGE__->mk_accessors(qw(config config2 scaling_factor bed_origin layer print layer_num)); +__PACKAGE__->mk_accessors(qw(config config2 scaling_factor bed_origin print layer_num)); sub new { my ($class, $parent, $config, $config2) = @_; @@ -803,9 +801,8 @@ sub _resize { } sub project_layer { - my ($self, $layer, $layer_num) = @_; + my ($self, $layer_num) = @_; - $self->layer($layer); $self->layer_num($layer_num); $self->Refresh; } @@ -873,20 +870,19 @@ sub _repaint { } } - return if !defined $self->layer; - # get layers at this height # draw layers $dc->SetPen(Wx::Pen->new(wxWHITE, 1, wxSOLID)); - my @polygons = sort { $a->contains_point($b->first_point) ? -1 : 1 } map @$_, @{$self->layer}; - foreach my $polygon (@polygons) { - if ($polygon->is_counter_clockwise) { - $dc->SetBrush(Wx::Brush->new(wxWHITE, wxSOLID)); - } else { - $dc->SetBrush(Wx::Brush->new(wxBLACK, wxSOLID)); - } - $dc->DrawPolygon($self->scaled_points_to_pixel($polygon->pp), 0, 0); + return if !$self->print || !defined $self->layer_num; + + if ($self->print->layer_solid($self->layer_num)) { + $self->_paint_expolygon($_, $dc) for @{$self->print->layer_slices($self->layer_num)}; + } else { + $self->_paint_expolygon($_, $dc) for @{$self->print->layer_solid_infill($self->layer_num)}; + $self->_paint_polygon($_, $dc) for map @{$_->grow}, + @{$self->print->layer_perimeters($self->layer_num)}, + @{$self->print->layer_infill($self->layer_num)}; } # draw support material @@ -908,6 +904,24 @@ sub _repaint { } } +sub _paint_expolygon { + my ($self, $expolygon, $dc) = @_; + + my @polygons = sort { $a->contains_point($b->first_point) ? -1 : 1 } @$expolygon; + $self->_paint_polygon($_, $dc) for @polygons; +} + +sub _paint_polygon { + my ($self, $polygon, $dc) = @_; + + if ($polygon->is_counter_clockwise) { + $dc->SetBrush(Wx::Brush->new(wxWHITE, wxSOLID)); + } else { + $dc->SetBrush(Wx::Brush->new(wxBLACK, wxSOLID)); + } + $dc->DrawPolygon($self->scaled_points_to_pixel($polygon->pp), 0, 0); +} + # convert a model coordinate into a pixel coordinate sub unscaled_point_to_pixel { my ($self, $point) = @_; diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index 37cc4b698..6ae6b8426 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -155,7 +155,7 @@ offset(const Polygons &polygons, const float delta, // perform offset ClipperLib::Paths output = _offset(polygons, delta, scale, joinType, miterLimit); - // convert into ExPolygons + // convert into Polygons return ClipperPaths_to_Slic3rMultiPoints(output); } @@ -166,7 +166,7 @@ offset(const Polylines &polylines, const float delta, // perform offset ClipperLib::Paths output = _offset(polylines, delta, scale, joinType, miterLimit); - // convert into ExPolygons + // convert into Polygons return ClipperPaths_to_Slic3rMultiPoints(output); } diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 0c402e50a..864f2da7a 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -533,8 +533,13 @@ class SLAPrintConfig : public virtual StaticPrintConfig { public: + ConfigOptionFloat fill_angle; + ConfigOptionPercent fill_density; + ConfigOptionEnum fill_pattern; ConfigOptionFloatOrPercent first_layer_height; + ConfigOptionFloatOrPercent infill_extrusion_width; ConfigOptionFloat layer_height; + ConfigOptionFloatOrPercent perimeter_extrusion_width; ConfigOptionInt raft_layers; ConfigOptionFloat raft_offset; ConfigOptionBool support_material; @@ -546,8 +551,13 @@ class SLAPrintConfig }; virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { + OPT_PTR(fill_angle); + OPT_PTR(fill_density); + OPT_PTR(fill_pattern); OPT_PTR(first_layer_height); + OPT_PTR(infill_extrusion_width); OPT_PTR(layer_height); + OPT_PTR(perimeter_extrusion_width); OPT_PTR(raft_layers); OPT_PTR(raft_offset); OPT_PTR(support_material); diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index 985e0e2cb..053cfe05d 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -1,5 +1,9 @@ #include "SLAPrint.hpp" #include "ClipperUtils.hpp" +#include "ExtrusionEntity.hpp" +#include "Fill/FillBase.hpp" +#include "Geometry.hpp" +#include "Surface.hpp" #include #include @@ -47,7 +51,73 @@ SLAPrint::slice() TriangleMeshSlicer(&mesh).slice(slice_z, &slices); for (size_t i = 0; i < slices.size(); ++i) - this->layers[i].slices = ExPolygonCollection(slices[i]); + this->layers[i].slices.expolygons = slices[i]; + } + + // generate infill + if (this->config.fill_density < 100) { + const float shell_thickness = this->config.get_abs_value("perimeter_extrusion_width", this->config.layer_height.value); + std::auto_ptr fill = std::auto_ptr(Fill::new_from_type(this->config.fill_pattern.value)); + //fill->bounding_box = this->bb; + fill->spacing = this->config.get_abs_value("infill_extrusion_width", this->config.layer_height.value); + fill->angle = Geometry::deg2rad(this->config.fill_angle.value); + FillParams fill_params; + fill_params.density = this->config.fill_density.value/100; + + ExtrusionPath templ(erInternalInfill); + templ.width = fill->spacing; + + for (size_t i = 0; i < this->layers.size(); ++i) { + Layer &layer = this->layers[i]; + + // In order to detect what regions of this layer need to be solid, + // perform an intersection with layers within the requested shell thickness. + Polygons internal = layer.slices; + for (size_t j = 0; j < this->layers.size(); ++j) { + const Layer &other = this->layers[j]; + if (abs(other.print_z - layer.print_z) > shell_thickness) continue; + + if (j == 0 || j == this->layers.size()-1) { + internal.clear(); + break; + } else if (i != j) { + internal = intersection(internal, other.slices); + if (internal.empty()) break; + } + } + + // If we have no internal infill, just print the whole layer as a solid slice. + if (internal.empty()) continue; + layer.solid = false; + + const Polygons infill = offset(layer.slices, -scale_(shell_thickness)); + + // Generate solid infill. + layer.solid_infill.append(diff_ex(infill, internal, true)); + + // Generate internal infill. + { + fill->layer_id = i; + fill->z = layer.print_z; + + const ExPolygons internal_ex = intersection_ex(infill, internal); + for (ExPolygons::const_iterator it = internal_ex.begin(); it != internal_ex.end(); ++it) { + Polylines polylines = fill->fill_surface(Surface(stInternal, *it), fill_params); + layer.infill.append(polylines, templ); + } + } + + // Generate perimeter(s). + { + const Polygons perimeters = offset(layer.slices, -scale_(shell_thickness)/2); + for (Polygons::const_iterator it = perimeters.begin(); it != perimeters.end(); ++it) { + ExtrusionPath ep(erPerimeter); + ep.polyline = *it; + ep.width = shell_thickness; + layer.perimeters.append(ExtrusionLoop(ep)); + } + } + } } // generate support material @@ -154,23 +224,31 @@ SLAPrint::write_svg(const std::string &outputfile) const layer.print_z - (i == 0 ? 0 : this->layers[i-1].print_z) ); - const ExPolygons &slices = layer.slices.expolygons; - for (ExPolygons::const_iterator it = slices.begin(); it != slices.end(); ++it) { - std::string pd; - Polygons pp = *it; - for (Polygons::const_iterator mp = pp.begin(); mp != pp.end(); ++mp) { - std::ostringstream d; - d << "M "; - for (Points::const_iterator p = mp->points.begin(); p != mp->points.end(); ++p) { - d << unscale(p->x) - this->bb.min.x << " "; - d << size.y - (unscale(p->y) - this->bb.min.y) << " "; // mirror Y coordinates as SVG uses downwards Y - } - d << "z"; - pd += d.str() + " "; + if (layer.solid) { + const ExPolygons &slices = layer.slices.expolygons; + for (ExPolygons::const_iterator it = slices.begin(); it != slices.end(); ++it) { + std::string pd = this->_SVG_path_d(*it); + + fprintf(f,"\t\t\n", + pd.c_str(), "white", "black", "0", unscale(unscale(it->area())) + ); + } + } else { + // Solid infill. + const ExPolygons &solid_infill = layer.solid_infill.expolygons; + for (ExPolygons::const_iterator it = solid_infill.begin(); it != solid_infill.end(); ++it) { + std::string pd = this->_SVG_path_d(*it); + + fprintf(f,"\t\t\n", + pd.c_str(), "white", "black", "0" + ); + } + + // Generate perimeters. + for (ExtrusionEntitiesPtr::const_iterator it = layer.perimeters.entities.begin(); + it != layer.perimeters.entities.end(); ++it) { + //std::string pd = this->_SVG_path_d(it->polygon()); } - fprintf(f,"\t\t\n", - pd.c_str(), "white", "black", "0", unscale(unscale(it->area())) - ); } // don't print support material in raft layers @@ -206,4 +284,28 @@ SLAPrint::sm_pillars_radius() const return radius; } +std::string +SLAPrint::_SVG_path_d(const Polygon &polygon) const +{ + const Sizef3 size = this->bb.size(); + std::ostringstream d; + d << "M "; + for (Points::const_iterator p = polygon.points.begin(); p != polygon.points.end(); ++p) { + d << unscale(p->x) - this->bb.min.x << " "; + d << size.y - (unscale(p->y) - this->bb.min.y) << " "; // mirror Y coordinates as SVG uses downwards Y + } + d << "z"; + return d.str(); +} + +std::string +SLAPrint::_SVG_path_d(const ExPolygon &expolygon) const +{ + std::string pd; + const Polygons pp = expolygon; + for (Polygons::const_iterator mp = pp.begin(); mp != pp.end(); ++mp) + pd += this->_SVG_path_d(*mp) + " "; + return pd; +} + } diff --git a/xs/src/libslic3r/SLAPrint.hpp b/xs/src/libslic3r/SLAPrint.hpp index 09664b386..99d3c2f8e 100644 --- a/xs/src/libslic3r/SLAPrint.hpp +++ b/xs/src/libslic3r/SLAPrint.hpp @@ -19,9 +19,14 @@ class SLAPrint class Layer { public: ExPolygonCollection slices; + ExtrusionEntityCollection perimeters; + ExtrusionEntityCollection infill; + ExPolygonCollection solid_infill; float slice_z, print_z; + bool solid; - Layer(float _slice_z, float _print_z) : slice_z(_slice_z), print_z(_print_z) {}; + Layer(float _slice_z, float _print_z) + : slice_z(_slice_z), print_z(_print_z), solid(true) {}; }; std::vector layers; @@ -41,6 +46,8 @@ class SLAPrint BoundingBoxf3 bb; coordf_t sm_pillars_radius() const; + std::string _SVG_path_d(const Polygon &polygon) const; + std::string _SVG_path_d(const ExPolygon &expolygon) const; }; } diff --git a/xs/xsp/SLAPrint.xsp b/xs/xsp/SLAPrint.xsp index daecce38f..3788d3d14 100644 --- a/xs/xsp/SLAPrint.xsp +++ b/xs/xsp/SLAPrint.xsp @@ -18,16 +18,19 @@ DynamicPrintConfig* config() %code%{ RETVAL = new DynamicPrintConfig; RETVAL->apply(THIS->config); %}; + ExPolygons layer_slices(size_t i) + %code%{ RETVAL = THIS->layers[i].slices; %}; + ExPolygons layer_solid_infill(size_t i) + %code%{ RETVAL = THIS->layers[i].solid_infill.expolygons; %}; + Ref layer_perimeters(size_t i) + %code%{ RETVAL = &THIS->layers[i].perimeters; %}; + Ref layer_infill(size_t i) + %code%{ RETVAL = &THIS->layers[i].infill; %}; + bool layer_solid(size_t i) + %code%{ RETVAL = THIS->layers[i].solid; %}; + %{ -ExPolygons -SLAPrint::slices(i) - size_t i; - CODE: - RETVAL = THIS->layers[i].slices; - OUTPUT: - RETVAL - std::vector SLAPrint::heights() CODE: From 6cdff99df35a058c0f9ae31b3260fed7ee574e48 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 30 Nov 2016 11:26:26 +0100 Subject: [PATCH 125/225] Apply print bounding box for SLAPrint infill --- xs/src/libslic3r/SLAPrint.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index 053cfe05d..35244f560 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -58,7 +58,8 @@ SLAPrint::slice() if (this->config.fill_density < 100) { const float shell_thickness = this->config.get_abs_value("perimeter_extrusion_width", this->config.layer_height.value); std::auto_ptr fill = std::auto_ptr(Fill::new_from_type(this->config.fill_pattern.value)); - //fill->bounding_box = this->bb; + fill->bounding_box.merge(Point::new_scale(bb.min.x, bb.min.y)); + fill->bounding_box.merge(Point::new_scale(bb.max.x, bb.max.y)); fill->spacing = this->config.get_abs_value("infill_extrusion_width", this->config.layer_height.value); fill->angle = Geometry::deg2rad(this->config.fill_angle.value); FillParams fill_params; From 2e82fb01a65deb4e0872de9219f47e58b7000ebf Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 30 Nov 2016 11:45:31 +0100 Subject: [PATCH 126/225] Ignore failure to call SetAutoPop when not available. #3596 --- lib/Slic3r/GUI/MainFrame.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index a8190b934..62ea30121 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -41,7 +41,8 @@ sub new { # set default tooltip timer in msec # SetAutoPop supposedly accepts long integers but some bug doesn't allow for larger values - Wx::ToolTip::SetAutoPop(32767); + # (SetAutoPop is not available on GTK.) + eval { Wx::ToolTip::SetAutoPop(32767) }; # initialize status bar $self->{statusbar} = Slic3r::GUI::ProgressStatusBar->new($self, -1); From a02d5caa4235a6bdab0e8bdba4db78efc5e28c25 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 30 Nov 2016 11:50:02 +0100 Subject: [PATCH 127/225] Try to fix compilation problem with Boost 1.54. #3595 --- xs/src/admesh/stl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/admesh/stl.h b/xs/src/admesh/stl.h index f9fb2535a..c27571993 100644 --- a/xs/src/admesh/stl.h +++ b/xs/src/admesh/stl.h @@ -26,7 +26,7 @@ #include #include #include -#include +#include #ifndef BOOST_LITTLE_ENDIAN #error "admesh works correctly on little endian machines only!" From a9609a6dd5a0b08a53131f3702ce2cec89e3ee46 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 30 Nov 2016 17:43:58 +0100 Subject: [PATCH 128/225] Finished infill implementation for SLAPrint --- lib/Slic3r/GUI/Projector.pm | 12 +++--- src/CMakeLists.txt | 8 ++++ xs/src/libslic3r/ExPolygon.hpp | 6 +++ xs/src/libslic3r/ExPolygonCollection.hpp | 6 +++ xs/src/libslic3r/Fill/FillConcentric.cpp | 6 +-- xs/src/libslic3r/Point.hpp | 12 ++++++ xs/src/libslic3r/Polygon.hpp | 12 ++++++ xs/src/libslic3r/SLAPrint.cpp | 54 ++++++++++++++---------- xs/src/libslic3r/SLAPrint.hpp | 2 +- xs/xsp/SLAPrint.xsp | 4 +- 10 files changed, 89 insertions(+), 33 deletions(-) diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index e75ac7e09..760484231 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -738,7 +738,7 @@ use base qw(Wx::Dialog Class::Accessor); use List::Util qw(min); use Slic3r::Geometry qw(X Y unscale scale); -use Slic3r::Geometry::Clipper qw(intersection_pl); +use Slic3r::Geometry::Clipper qw(intersection_pl union_ex); __PACKAGE__->mk_accessors(qw(config config2 scaling_factor bed_origin print layer_num)); @@ -879,10 +879,12 @@ sub _repaint { if ($self->print->layer_solid($self->layer_num)) { $self->_paint_expolygon($_, $dc) for @{$self->print->layer_slices($self->layer_num)}; } else { - $self->_paint_expolygon($_, $dc) for @{$self->print->layer_solid_infill($self->layer_num)}; - $self->_paint_polygon($_, $dc) for map @{$_->grow}, - @{$self->print->layer_perimeters($self->layer_num)}, - @{$self->print->layer_infill($self->layer_num)}; + $self->_paint_expolygon($_, $dc) for + @{$self->print->layer_solid_infill($self->layer_num)}, + @{$self->print->layer_perimeters($self->layer_num)}; + + $self->_paint_expolygon($_, $dc) + for @{union_ex($self->print->layer_infill($self->layer_num)->grow)}; } # draw support material diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9b704d5b0..dfd5ce3f3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -45,6 +45,14 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/Extruder.cpp ${LIBDIR}/libslic3r/ExtrusionEntity.cpp ${LIBDIR}/libslic3r/ExtrusionEntityCollection.cpp + ${LIBDIR}/libslic3r/Fill/Fill.cpp + ${LIBDIR}/libslic3r/Fill/Fill3DHoneycomb.cpp + ${LIBDIR}/libslic3r/Fill/FillBase.cpp + ${LIBDIR}/libslic3r/Fill/FillConcentric.cpp + ${LIBDIR}/libslic3r/Fill/FillHoneycomb.cpp + ${LIBDIR}/libslic3r/Fill/FillPlanePath.cpp + ${LIBDIR}/libslic3r/Fill/FillRectilinear.cpp + ${LIBDIR}/libslic3r/Fill/FillRectilinear2.cpp ${LIBDIR}/libslic3r/Flow.cpp ${LIBDIR}/libslic3r/GCode.cpp ${LIBDIR}/libslic3r/GCodeSender.cpp diff --git a/xs/src/libslic3r/ExPolygon.hpp b/xs/src/libslic3r/ExPolygon.hpp index 0b4140e90..4289eb390 100644 --- a/xs/src/libslic3r/ExPolygon.hpp +++ b/xs/src/libslic3r/ExPolygon.hpp @@ -55,6 +55,12 @@ to_polygons(const ExPolygons &expolygons) return pp; } +inline ExPolygons +operator+(ExPolygons src1, const ExPolygons &src2) { + append_to(src1, src2); + return src1; +}; + } // start Boost diff --git a/xs/src/libslic3r/ExPolygonCollection.hpp b/xs/src/libslic3r/ExPolygonCollection.hpp index ec3cb9522..e63dd9142 100644 --- a/xs/src/libslic3r/ExPolygonCollection.hpp +++ b/xs/src/libslic3r/ExPolygonCollection.hpp @@ -34,6 +34,12 @@ class ExPolygonCollection void append(const ExPolygons &expolygons); }; +inline ExPolygonCollection& +operator <<(ExPolygonCollection &coll, const ExPolygons &expolygons) { + coll.append(expolygons); + return coll; +}; + } #endif diff --git a/xs/src/libslic3r/Fill/FillConcentric.cpp b/xs/src/libslic3r/Fill/FillConcentric.cpp index d31efcafc..4bec2bb7d 100644 --- a/xs/src/libslic3r/Fill/FillConcentric.cpp +++ b/xs/src/libslic3r/Fill/FillConcentric.cpp @@ -29,7 +29,7 @@ FillConcentric::_fill_surface_single( Polygons last = loops; while (!last.empty()) { last = offset2(last, -(distance + min_spacing/2), +min_spacing/2); - loops.insert(loops.end(), last.begin(), last.end()); + append_to(loops, last); } // generate paths from the outermost to the innermost, to avoid @@ -37,7 +37,7 @@ FillConcentric::_fill_surface_single( loops = union_pt_chained(loops, false); // split paths using a nearest neighbor search - size_t iPathFirst = polylines_out->size(); + const size_t iPathFirst = polylines_out->size(); Point last_pos(0, 0); for (Polygons::const_iterator it_loop = loops.begin(); it_loop != loops.end(); ++ it_loop) { polylines_out->push_back(it_loop->split_at_index(last_pos.nearest_point_index(*it_loop))); @@ -47,7 +47,7 @@ FillConcentric::_fill_surface_single( // clip the paths to prevent the extruder from getting exactly on the first point of the loop // Keep valid paths only. size_t j = iPathFirst; - for (size_t i = iPathFirst; i < polylines_out->size(); ++ i) { + for (size_t i = iPathFirst; i < polylines_out->size(); ++i) { (*polylines_out)[i].clip_end(this->loop_clipping); if ((*polylines_out)[i].is_valid()) { if (j < i) diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp index c6c340446..7fd05ff48 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/xs/src/libslic3r/Point.hpp @@ -79,6 +79,18 @@ Point operator+(const Point& point1, const Point& point2); Point operator-(const Point& point1, const Point& point2); Point operator*(double scalar, const Point& point2); +inline Points& +operator+=(Points &dst, const Points &src) { + append_to(dst, src); + return dst; +}; + +inline Points& +operator+=(Points &dst, const Point &p) { + dst.push_back(p); + return dst; +}; + class Point3 : public Point { public: diff --git a/xs/src/libslic3r/Polygon.hpp b/xs/src/libslic3r/Polygon.hpp index 415aa404e..1b9887218 100644 --- a/xs/src/libslic3r/Polygon.hpp +++ b/xs/src/libslic3r/Polygon.hpp @@ -48,6 +48,18 @@ class Polygon : public MultiPoint { Points convex_points(double angle = PI) const; }; +inline Polygons +operator+(Polygons src1, const Polygons &src2) { + append_to(src1, src2); + return src1; +}; + +inline Polygons& +operator+=(Polygons &dst, const Polygons &src2) { + append_to(dst, src2); + return dst; +}; + } // start Boost diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index 35244f560..77122057b 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -94,7 +94,7 @@ SLAPrint::slice() const Polygons infill = offset(layer.slices, -scale_(shell_thickness)); // Generate solid infill. - layer.solid_infill.append(diff_ex(infill, internal, true)); + layer.solid_infill << diff_ex(infill, internal, true); // Generate internal infill. { @@ -109,15 +109,10 @@ SLAPrint::slice() } // Generate perimeter(s). - { - const Polygons perimeters = offset(layer.slices, -scale_(shell_thickness)/2); - for (Polygons::const_iterator it = perimeters.begin(); it != perimeters.end(); ++it) { - ExtrusionPath ep(erPerimeter); - ep.polyline = *it; - ep.width = shell_thickness; - layer.perimeters.append(ExtrusionLoop(ep)); - } - } + layer.perimeters << diff_ex( + layer.slices, + offset(layer.slices, -scale_(shell_thickness)) + ); } } @@ -128,10 +123,8 @@ SLAPrint::slice() // flatten and merge all the overhangs { Polygons pp; - for (std::vector::const_iterator it = this->layers.begin()+1; it != this->layers.end(); ++it) { - Polygons oh = diff(it->slices, (it - 1)->slices); - pp.insert(pp.end(), oh.begin(), oh.end()); - } + for (std::vector::const_iterator it = this->layers.begin()+1; it != this->layers.end(); ++it) + pp += diff(it->slices, (it - 1)->slices); overhangs = union_ex(pp); } @@ -188,8 +181,7 @@ SLAPrint::slice() // generate a solid raft if requested // (do this after support material because we take support material shape into account) if (this->config.raft_layers > 0) { - ExPolygons raft = this->layers.front().slices; - raft.insert(raft.end(), overhangs.begin(), overhangs.end()); // take support material into account + ExPolygons raft = this->layers.front().slices + overhangs; // take support material into account raft = offset_ex(raft, scale_(this->config.raft_offset)); for (int i = this->config.raft_layers; i >= 1; --i) { this->layers.insert(this->layers.begin(), Layer(0, first_lh + lh * (i-1))); @@ -235,9 +227,19 @@ SLAPrint::write_svg(const std::string &outputfile) const ); } } else { + // Perimeters. + for (ExPolygons::const_iterator it = layer.perimeters.expolygons.begin(); + it != layer.perimeters.expolygons.end(); ++it) { + std::string pd = this->_SVG_path_d(*it); + + fprintf(f,"\t\t\n", + pd.c_str(), "white", "black", "0" + ); + } + // Solid infill. - const ExPolygons &solid_infill = layer.solid_infill.expolygons; - for (ExPolygons::const_iterator it = solid_infill.begin(); it != solid_infill.end(); ++it) { + for (ExPolygons::const_iterator it = layer.solid_infill.expolygons.begin(); + it != layer.solid_infill.expolygons.end(); ++it) { std::string pd = this->_SVG_path_d(*it); fprintf(f,"\t\t\n", @@ -245,10 +247,18 @@ SLAPrint::write_svg(const std::string &outputfile) const ); } - // Generate perimeters. - for (ExtrusionEntitiesPtr::const_iterator it = layer.perimeters.entities.begin(); - it != layer.perimeters.entities.end(); ++it) { - //std::string pd = this->_SVG_path_d(it->polygon()); + // Internal infill. + for (ExtrusionEntitiesPtr::const_iterator it = layer.infill.entities.begin(); + it != layer.infill.entities.end(); ++it) { + const ExPolygons infill = union_ex((*it)->grow()); + + for (ExPolygons::const_iterator e = infill.begin(); e != infill.end(); ++e) { + std::string pd = this->_SVG_path_d(*e); + + fprintf(f,"\t\t\n", + pd.c_str(), "white", "black", "0" + ); + } } } diff --git a/xs/src/libslic3r/SLAPrint.hpp b/xs/src/libslic3r/SLAPrint.hpp index 99d3c2f8e..e3373b508 100644 --- a/xs/src/libslic3r/SLAPrint.hpp +++ b/xs/src/libslic3r/SLAPrint.hpp @@ -19,7 +19,7 @@ class SLAPrint class Layer { public: ExPolygonCollection slices; - ExtrusionEntityCollection perimeters; + ExPolygonCollection perimeters; ExtrusionEntityCollection infill; ExPolygonCollection solid_infill; float slice_z, print_z; diff --git a/xs/xsp/SLAPrint.xsp b/xs/xsp/SLAPrint.xsp index 3788d3d14..559d2973d 100644 --- a/xs/xsp/SLAPrint.xsp +++ b/xs/xsp/SLAPrint.xsp @@ -22,8 +22,8 @@ %code%{ RETVAL = THIS->layers[i].slices; %}; ExPolygons layer_solid_infill(size_t i) %code%{ RETVAL = THIS->layers[i].solid_infill.expolygons; %}; - Ref layer_perimeters(size_t i) - %code%{ RETVAL = &THIS->layers[i].perimeters; %}; + ExPolygons layer_perimeters(size_t i) + %code%{ RETVAL = THIS->layers[i].perimeters; %}; Ref layer_infill(size_t i) %code%{ RETVAL = &THIS->layers[i].infill; %}; bool layer_solid(size_t i) From 3e04877571eb72e87ee2062b9d86cc5fa9eec5fd Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 30 Nov 2016 18:07:44 +0100 Subject: [PATCH 129/225] Refactoring: make_fills() is now a LayerRegion method, and we move the base Fill class to Fill.hpp, no need for FillBase.hpp anymore --- src/CMakeLists.txt | 2 +- xs/MANIFEST | 2 - xs/src/libslic3r/Fill/Fill.cpp | 394 +++++++-------------- xs/src/libslic3r/Fill/Fill.hpp | 98 ++++- xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp | 2 +- xs/src/libslic3r/Fill/FillBase.cpp | 138 -------- xs/src/libslic3r/Fill/FillBase.hpp | 111 ------ xs/src/libslic3r/Fill/FillConcentric.hpp | 2 +- xs/src/libslic3r/Fill/FillHoneycomb.hpp | 2 +- xs/src/libslic3r/Fill/FillPlanePath.hpp | 2 +- xs/src/libslic3r/Fill/FillRectilinear.hpp | 2 +- xs/src/libslic3r/Fill/FillRectilinear2.hpp | 2 +- xs/src/libslic3r/Layer.cpp | 9 +- xs/src/libslic3r/Layer.hpp | 1 + xs/src/libslic3r/LayerRegionFill.cpp | 285 +++++++++++++++ xs/src/libslic3r/SLAPrint.cpp | 2 +- xs/src/xsinit.h | 2 +- xs/xsp/Filler.xsp | 3 - 18 files changed, 512 insertions(+), 547 deletions(-) delete mode 100644 xs/src/libslic3r/Fill/FillBase.cpp delete mode 100644 xs/src/libslic3r/Fill/FillBase.hpp create mode 100644 xs/src/libslic3r/LayerRegionFill.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dfd5ce3f3..e656d7aab 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,7 +47,6 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/ExtrusionEntityCollection.cpp ${LIBDIR}/libslic3r/Fill/Fill.cpp ${LIBDIR}/libslic3r/Fill/Fill3DHoneycomb.cpp - ${LIBDIR}/libslic3r/Fill/FillBase.cpp ${LIBDIR}/libslic3r/Fill/FillConcentric.cpp ${LIBDIR}/libslic3r/Fill/FillHoneycomb.cpp ${LIBDIR}/libslic3r/Fill/FillPlanePath.cpp @@ -61,6 +60,7 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/IO.cpp ${LIBDIR}/libslic3r/Layer.cpp ${LIBDIR}/libslic3r/LayerRegion.cpp + ${LIBDIR}/libslic3r/LayerRegionFill.cpp ${LIBDIR}/libslic3r/Line.cpp ${LIBDIR}/libslic3r/Model.cpp ${LIBDIR}/libslic3r/MotionPlanner.cpp diff --git a/xs/MANIFEST b/xs/MANIFEST index e3c4b9665..9281abd93 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -30,8 +30,6 @@ src/libslic3r/ExtrusionEntityCollection.cpp src/libslic3r/ExtrusionEntityCollection.hpp src/libslic3r/Fill/Fill.cpp src/libslic3r/Fill/Fill.hpp -src/libslic3r/Fill/FillBase.cpp -src/libslic3r/Fill/FillBase.hpp src/libslic3r/Fill/FillConcentric.cpp src/libslic3r/Fill/FillConcentric.hpp src/libslic3r/Fill/FillHoneycomb.cpp diff --git a/xs/src/libslic3r/Fill/Fill.cpp b/xs/src/libslic3r/Fill/Fill.cpp index 5a0897413..7de4e1fdd 100644 --- a/xs/src/libslic3r/Fill/Fill.cpp +++ b/xs/src/libslic3r/Fill/Fill.cpp @@ -1,290 +1,138 @@ -#include +#include #include -#include #include "../ClipperUtils.hpp" -#include "../Geometry.hpp" -#include "../Layer.hpp" -#include "../Print.hpp" -#include "../PrintConfig.hpp" #include "../Surface.hpp" -#include "../SurfaceCollection.hpp" +#include "../PrintConfig.hpp" -#include "FillBase.hpp" +#include "Fill.hpp" +#include "FillConcentric.hpp" +#include "FillHoneycomb.hpp" +#include "Fill3DHoneycomb.hpp" +#include "FillPlanePath.hpp" +#include "FillRectilinear.hpp" +#include "FillRectilinear2.hpp" namespace Slic3r { -struct SurfaceGroupAttrib +Fill* +Fill::new_from_type(const InfillPattern type) { - SurfaceGroupAttrib() : is_solid(false), fw(0.f), pattern(-1) {} - bool operator==(const SurfaceGroupAttrib &other) const - { return is_solid == other.is_solid && fw == other.fw && pattern == other.pattern; } - bool is_solid; - float fw; - // pattern is of type InfillPattern, -1 for an unset pattern. - int pattern; -}; - -// Generate infills for a LayerRegion. -// The LayerRegion at this point of time may contain -// surfaces of various types (internal/bridge/top/bottom/solid). -// The infills are generated on the groups of surfaces with a compatible type. -// Returns an array of ExtrusionPathCollection objects containing the infills generated now -// and the thin fills generated by generate_perimeters(). -void make_fill(const LayerRegion &layerm, ExtrusionEntityCollection* out) -{ -// Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id; - - double fill_density = layerm.region()->config.fill_density; - const Flow infill_flow = layerm.flow(frInfill); - const Flow solid_infill_flow = layerm.flow(frSolidInfill); - const Flow top_solid_infill_flow = layerm.flow(frTopSolidInfill); - - SurfaceCollection surfaces; - - // merge adjacent surfaces - // in case of bridge surfaces, the ones with defined angle will be attached to the ones - // without any angle (shouldn't this logic be moved to process_external_surfaces()?) - { - Polygons polygons_bridged; - polygons_bridged.reserve(layerm.fill_surfaces.surfaces.size()); - for (Surfaces::const_iterator it = layerm.fill_surfaces.surfaces.begin(); it != layerm.fill_surfaces.surfaces.end(); ++it) - if (it->bridge_angle >= 0) - append_to(polygons_bridged, (Polygons)*it); + switch (type) { + case ipConcentric: return new FillConcentric(); + case ipHoneycomb: return new FillHoneycomb(); + case ip3DHoneycomb: return new Fill3DHoneycomb(); - // group surfaces by distinct properties (equal surface_type, thickness, thickness_layers, bridge_angle) - // group is of type SurfaceCollection - // FIXME: Use some smart heuristics to merge similar surfaces to eliminate tiny regions. - std::vector groups; - layerm.fill_surfaces.group(&groups); + case ipRectilinear: return new FillRectilinear(); + case ipLine: return new FillLine(); + case ipGrid: return new FillGrid(); + case ipAlignedRectilinear: return new FillAlignedRectilinear(); - // merge compatible groups (we can generate continuous infill for them) - { - // cache flow widths and patterns used for all solid groups - // (we'll use them for comparing compatible groups) - std::vector group_attrib(groups.size()); - for (size_t i = 0; i < groups.size(); ++i) { - // we can only merge solid non-bridge surfaces, so discard - // non-solid surfaces - const Surface &surface = *groups[i].front(); - if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) { - group_attrib[i].is_solid = true; - group_attrib[i].fw = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width; - group_attrib[i].pattern = surface.is_external() ? layerm.region()->config.external_fill_pattern.value : ipRectilinear; - } - } - // Loop through solid groups, find compatible groups and append them to this one. - for (size_t i = 0; i < groups.size(); ++i) { - if (!group_attrib[i].is_solid) - continue; - for (size_t j = i + 1; j < groups.size();) { - if (group_attrib[i] == group_attrib[j]) { - // groups are compatible, merge them - append_to(groups[i], groups[j]); - groups.erase(groups.begin() + j); - group_attrib.erase(group_attrib.begin() + j); - } else { - ++j; - } - } - } - } + case ipRectilinear2: return new FillRectilinear2(); + case ipGrid2: return new FillGrid2(); + case ipTriangles: return new FillTriangles(); + case ipStars: return new FillStars(); + case ipCubic: return new FillCubic(); - // Give priority to bridges. Process the bridges in the first round, the rest of the surfaces in the 2nd round. - for (size_t round = 0; round < 2; ++ round) { - for (std::vector::const_iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) { - const SurfacesConstPtr &group = *it_group; - bool is_bridge = group.front()->bridge_angle >= 0; - if (is_bridge != (round == 0)) - continue; - - // Make a union of polygons defining the infiill regions of a group, use a safety offset. - Polygons union_p = union_(to_polygons(group), true); - - // Subtract surfaces having a defined bridge_angle from any other, use a safety offset. - if (!polygons_bridged.empty() && !is_bridge) - union_p = diff(union_p, polygons_bridged, true); - - // subtract any other surface already processed - //FIXME Vojtech: Because the bridge surfaces came first, they are subtracted twice! - surfaces.append( - diff_ex(union_p, to_polygons(surfaces), true), - *group.front() // template - ); - } - } - } - - // we need to detect any narrow surfaces that might collapse - // when adding spacing below - // such narrow surfaces are often generated in sloping walls - // by bridge_over_infill() and combine_infill() as a result of the - // subtraction of the combinable area from the layer infill area, - // which leaves small areas near the perimeters - // we are going to grow such regions by overlapping them with the void (if any) - // TODO: detect and investigate whether there could be narrow regions without - // any void neighbors - { - coord_t distance_between_surfaces = std::max( - std::max(infill_flow.scaled_spacing(), solid_infill_flow.scaled_spacing()), - top_solid_infill_flow.scaled_spacing() - ); + case ipArchimedeanChords: return new FillArchimedeanChords(); + case ipHilbertCurve: return new FillHilbertCurve(); + case ipOctagramSpiral: return new FillOctagramSpiral(); - Polygons surfaces_polygons = (Polygons)surfaces; - Polygons collapsed = diff( - surfaces_polygons, - offset2(surfaces_polygons, -distance_between_surfaces/2, +distance_between_surfaces/2), - true - ); - - Polygons to_subtract; - surfaces.filter_by_type(stInternalVoid, &to_subtract); - - append_to(to_subtract, collapsed); - surfaces.append( - intersection_ex( - offset(collapsed, distance_between_surfaces), - to_subtract, - true - ), - stInternalSolid - ); - } - - if (false) { -// require "Slic3r/SVG.pm"; -// Slic3r::SVG::output("fill_" . $layerm->print_z . ".svg", -// expolygons => [ map $_->expolygon, grep !$_->is_solid, @surfaces ], -// red_expolygons => [ map $_->expolygon, grep $_->is_solid, @surfaces ], -// ); - } - - for (Surfaces::const_iterator surface_it = surfaces.surfaces.begin(); - surface_it != surfaces.surfaces.end(); ++surface_it) { - - const Surface &surface = *surface_it; - if (surface.surface_type == stInternalVoid) - continue; - - InfillPattern fill_pattern = layerm.region()->config.fill_pattern.value; - double density = fill_density; - FlowRole role = (surface.surface_type == stTop) ? frTopSolidInfill - : surface.is_solid() ? frSolidInfill - : frInfill; - const bool is_bridge = layerm.layer()->id() > 0 && surface.is_bridge(); - - if (surface.is_solid()) { - density = 100.; - fill_pattern = (surface.is_external() && !is_bridge) - ? layerm.region()->config.external_fill_pattern.value - : ipRectilinear; - } else if (density <= 0) - continue; - - // get filler object - #if SLIC3R_CPPVER >= 11 - std::unique_ptr f = std::unique_ptr(Fill::new_from_type(fill_pattern)); - #else - std::auto_ptr f = std::auto_ptr(Fill::new_from_type(fill_pattern)); - #endif - f->set_bounding_box(layerm.layer()->object()->bounding_box()); - - // calculate the actual flow we'll be using for this infill - coordf_t h = (surface.thickness == -1) ? layerm.layer()->height : surface.thickness; - Flow flow = layerm.region()->flow( - role, - h, - is_bridge || f->use_bridge_flow(), // bridge flow? - layerm.layer()->id() == 0, // first layer? - -1, // auto width - *layerm.layer()->object() - ); - - // calculate flow spacing for infill pattern generation - bool using_internal_flow = false; - if (!surface.is_solid() && !is_bridge) { - // it's internal infill, so we can calculate a generic flow spacing - // for all layers, for avoiding the ugly effect of - // misaligned infill on first layer because of different extrusion width and - // layer height - Flow internal_flow = layerm.region()->flow( - frInfill, - layerm.layer()->object()->config.layer_height.value, // TODO: handle infill_every_layers? - false, // no bridge - false, // no first layer - -1, // auto width - *layerm.layer()->object() - ); - f->spacing = internal_flow.spacing(); - using_internal_flow = true; - } else { - f->spacing = flow.spacing(); - } - - f->layer_id = layerm.layer()->id(); - f->z = layerm.layer()->print_z; - f->angle = Geometry::deg2rad(layerm.region()->config.fill_angle.value); - - // Maximum length of the perimeter segment linking two infill lines. - f->link_max_length = (!is_bridge && density > 80) - ? scale_(3 * f->spacing) - : 0; - - // Used by the concentric infill pattern to clip the loops to create extrusion paths. - f->loop_clipping = scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER; - - // apply half spacing using this flow's own spacing and generate infill - FillParams params; - params.density = density/100; - params.dont_adjust = false; - Polylines polylines = f->fill_surface(surface, params); - if (polylines.empty()) - continue; - - // calculate actual flow from spacing (which might have been adjusted by the infill - // pattern generator) - if (using_internal_flow) { - // if we used the internal flow we're not doing a solid infill - // so we can safely ignore the slight variation that might have - // been applied to f->spacing - } else { - flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow()); - } - - // Save into layer. - ExtrusionEntityCollection* coll = new ExtrusionEntityCollection(); - coll->no_sort = f->no_sort(); - out->entities.push_back(coll); - - { - ExtrusionRole role; - if (is_bridge) { - role = erBridgeInfill; - } else if (surface.is_solid()) { - role = (surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill; - } else { - role = erInternalInfill; - } - - ExtrusionPath templ(role); - templ.mm3_per_mm = flow.mm3_per_mm(); - templ.width = flow.width; - templ.height = flow.height; - - coll->append(STDMOVE(polylines), templ); - } - } - - // add thin fill regions - // thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection - // Unpacks the collection, creates multiple collections per path so that they will - // be individually included in the nearest neighbor search. - // The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection. - for (ExtrusionEntitiesPtr::const_iterator thin_fill = layerm.thin_fills.entities.begin(); thin_fill != layerm.thin_fills.entities.end(); ++ thin_fill) { - ExtrusionEntityCollection* coll = new ExtrusionEntityCollection(); - out->entities.push_back(coll); - coll->entities.push_back((*thin_fill)->clone()); + default: CONFESS("unknown type"); return NULL; } } +Fill* +Fill::new_from_type(const std::string &type) +{ + static t_config_enum_values enum_keys_map = ConfigOptionEnum::get_enum_values(); + t_config_enum_values::const_iterator it = enum_keys_map.find(type); + return (it == enum_keys_map.end()) ? NULL : new_from_type(InfillPattern(it->second)); +} + +Polylines +Fill::fill_surface(const Surface &surface, const FillParams ¶ms) +{ + // Perform offset. + ExPolygons expp = offset_ex(surface.expolygon, -scale_(this->spacing)/2); + + // Create the infills for each of the regions. + Polylines polylines_out; + for (size_t i = 0; i < expp.size(); ++i) + this->_fill_surface_single( + params, + surface.thickness_layers, + this->_infill_direction(surface), + expp[i], + &polylines_out + ); + return polylines_out; +} + +// Calculate a new spacing to fill width with possibly integer number of lines, +// the first and last line being centered at the interval ends. +// This function possibly increases the spacing, never decreases, +// and for a narrow width the increase in spacing may become severe, +// therefore the adjustment is limited to 20% increase. +coord_t +Fill::adjust_solid_spacing(const coord_t width, const coord_t distance) +{ + assert(width >= 0); + assert(distance > 0); + // floor(width / distance) + coord_t number_of_intervals = floor(width / distance); + coord_t distance_new = (number_of_intervals == 0) + ? distance + : (width / number_of_intervals); + + const coordf_t factor = coordf_t(distance_new) / coordf_t(distance); + assert(factor > 1. - 1e-5); + + // How much could the extrusion width be increased? By 20%. + const coordf_t factor_max = 1.2; + if (factor > factor_max) + distance_new = floor((double)distance * factor_max + 0.5); + + return distance_new; +} + +// Returns orientation of the infill and the reference point of the infill pattern. +// For a normal print, the reference point is the center of a bounding box of the STL. +std::pair +Fill::_infill_direction(const Surface &surface) const +{ + // set infill angle + float out_angle = this->angle; + + // Bounding box is the bounding box of a Slic3r::PrintObject + // The bounding box is only undefined in unit tests. + Point out_shift = this->bounding_box.defined + ? this->bounding_box.center() + : surface.expolygon.contour.bounding_box().center(); + + #if 0 + if (!this->bounding_box.defined) { + printf("Fill::_infill_direction: empty bounding box!"); + } else { + printf("Fill::_infill_direction: reference point %d, %d\n", out_shift.x, out_shift.y); + } + #endif + + if (surface.bridge_angle >= 0) { + // use bridge angle + //FIXME Vojtech: Add a debugf? + // Slic3r::debugf "Filling bridge with angle %d\n", rad2deg($surface->bridge_angle); + #ifdef SLIC3R_DEBUG + printf("Filling bridge with angle %f\n", surface.bridge_angle); + #endif + out_angle = surface.bridge_angle; + } else if (this->layer_id != size_t(-1)) { + // alternate fill direction + out_angle += this->_layer_angle(this->layer_id / surface.thickness_layers); + } + + out_angle += float(M_PI/2.); + return std::pair(out_angle, out_shift); +} + } // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/Fill.hpp b/xs/src/libslic3r/Fill/Fill.hpp index 58e446933..f14b93e7b 100644 --- a/xs/src/libslic3r/Fill/Fill.hpp +++ b/xs/src/libslic3r/Fill/Fill.hpp @@ -1,22 +1,110 @@ #ifndef slic3r_Fill_hpp_ #define slic3r_Fill_hpp_ +#include #include #include #include #include "../libslic3r.h" #include "../BoundingBox.hpp" +#include "../ExPolygon.hpp" +#include "../Polyline.hpp" #include "../PrintConfig.hpp" -#include "FillBase.hpp" - namespace Slic3r { -class ExtrusionEntityCollection; -class LayerRegion; +class Surface; -void make_fill(const LayerRegion &layerm, ExtrusionEntityCollection* out); +struct FillParams +{ + public: + FillParams() : density(0), dont_connect(false), dont_adjust(false), complete(false) {}; + + // Fill density, fraction in <0, 1> + float density; + + // Don't connect the fill lines around the inner perimeter. + bool dont_connect; + + // Don't adjust spacing to fill the space evenly. + bool dont_adjust; + + // For Honeycomb. + // we were requested to complete each loop; + // in this case we don't try to make more continuous paths + bool complete; +}; + +class Fill +{ +public: + // Index of the layer. + size_t layer_id; + + // Z coordinate of the top print surface, in unscaled coordinates + coordf_t z; + + // in unscaled coordinates + coordf_t spacing; + + // in radians, ccw, 0 = East + float angle; + + // In scaled coordinates. Maximum lenght of a perimeter segment connecting two infill lines. + // Used by the FillRectilinear2, FillGrid2, FillTriangles, FillStars and FillCubic. + // If left to zero, the links will not be limited. + coord_t link_max_length; + + // In scaled coordinates. Used by the concentric infill pattern to clip the loops to create extrusion paths. + coord_t loop_clipping; + + // In scaled coordinates. Bounding box of the 2D projection of the object. + BoundingBox bounding_box; + +public: + virtual ~Fill() {} + + static Fill* new_from_type(const InfillPattern type); + static Fill* new_from_type(const std::string &type); + + void set_bounding_box(const BoundingBox &bb) { this->bounding_box = bb; } + + // Use bridge flow for the fill? + virtual bool use_bridge_flow() const { return false; } + + // Do not sort the fill lines to optimize the print head path? + virtual bool no_sort() const { return false; } + + // Perform the fill. + virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms); + + static coord_t adjust_solid_spacing(const coord_t width, const coord_t distance); + +protected: + Fill() : + layer_id(size_t(-1)), + z(0.f), + spacing(0.f), + angle(0), + link_max_length(0), + loop_clipping(0) + {}; + + // The expolygon may be modified by the method to avoid a copy. + virtual void _fill_surface_single( + const FillParams ¶ms, + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out) {}; + + virtual float _layer_angle(size_t idx) const { + return (idx % 2) == 0 ? (M_PI/2.) : 0; + } + + std::pair _infill_direction(const Surface &surface) const; +}; } // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp b/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp index 89b781e1e..19a45818f 100644 --- a/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp +++ b/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp @@ -5,7 +5,7 @@ #include "../libslic3r.h" -#include "FillBase.hpp" +#include "Fill.hpp" namespace Slic3r { diff --git a/xs/src/libslic3r/Fill/FillBase.cpp b/xs/src/libslic3r/Fill/FillBase.cpp deleted file mode 100644 index 987d51f3d..000000000 --- a/xs/src/libslic3r/Fill/FillBase.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include -#include - -#include "../ClipperUtils.hpp" -#include "../Surface.hpp" -#include "../PrintConfig.hpp" - -#include "FillBase.hpp" -#include "FillConcentric.hpp" -#include "FillHoneycomb.hpp" -#include "Fill3DHoneycomb.hpp" -#include "FillPlanePath.hpp" -#include "FillRectilinear.hpp" -#include "FillRectilinear2.hpp" - -namespace Slic3r { - -Fill* -Fill::new_from_type(const InfillPattern type) -{ - switch (type) { - case ipConcentric: return new FillConcentric(); - case ipHoneycomb: return new FillHoneycomb(); - case ip3DHoneycomb: return new Fill3DHoneycomb(); - - case ipRectilinear: return new FillRectilinear(); - case ipLine: return new FillLine(); - case ipGrid: return new FillGrid(); - case ipAlignedRectilinear: return new FillAlignedRectilinear(); - - case ipRectilinear2: return new FillRectilinear2(); - case ipGrid2: return new FillGrid2(); - case ipTriangles: return new FillTriangles(); - case ipStars: return new FillStars(); - case ipCubic: return new FillCubic(); - - case ipArchimedeanChords: return new FillArchimedeanChords(); - case ipHilbertCurve: return new FillHilbertCurve(); - case ipOctagramSpiral: return new FillOctagramSpiral(); - - default: CONFESS("unknown type"); return NULL; - } -} - -Fill* -Fill::new_from_type(const std::string &type) -{ - static t_config_enum_values enum_keys_map = ConfigOptionEnum::get_enum_values(); - t_config_enum_values::const_iterator it = enum_keys_map.find(type); - return (it == enum_keys_map.end()) ? NULL : new_from_type(InfillPattern(it->second)); -} - -Polylines -Fill::fill_surface(const Surface &surface, const FillParams ¶ms) -{ - // Perform offset. - ExPolygons expp = offset_ex(surface.expolygon, -scale_(this->spacing)/2); - - // Create the infills for each of the regions. - Polylines polylines_out; - for (size_t i = 0; i < expp.size(); ++i) - this->_fill_surface_single( - params, - surface.thickness_layers, - this->_infill_direction(surface), - expp[i], - &polylines_out - ); - return polylines_out; -} - -// Calculate a new spacing to fill width with possibly integer number of lines, -// the first and last line being centered at the interval ends. -// This function possibly increases the spacing, never decreases, -// and for a narrow width the increase in spacing may become severe, -// therefore the adjustment is limited to 20% increase. -coord_t -Fill::adjust_solid_spacing(const coord_t width, const coord_t distance) -{ - assert(width >= 0); - assert(distance > 0); - // floor(width / distance) - coord_t number_of_intervals = floor(width / distance); - coord_t distance_new = (number_of_intervals == 0) - ? distance - : (width / number_of_intervals); - - const coordf_t factor = coordf_t(distance_new) / coordf_t(distance); - assert(factor > 1. - 1e-5); - - // How much could the extrusion width be increased? By 20%. - const coordf_t factor_max = 1.2; - if (factor > factor_max) - distance_new = floor((double)distance * factor_max + 0.5); - - return distance_new; -} - -// Returns orientation of the infill and the reference point of the infill pattern. -// For a normal print, the reference point is the center of a bounding box of the STL. -std::pair -Fill::_infill_direction(const Surface &surface) const -{ - // set infill angle - float out_angle = this->angle; - - // Bounding box is the bounding box of a Slic3r::PrintObject - // The bounding box is only undefined in unit tests. - Point out_shift = this->bounding_box.defined - ? this->bounding_box.center() - : surface.expolygon.contour.bounding_box().center(); - - #if 0 - if (!this->bounding_box.defined) { - printf("Fill::_infill_direction: empty bounding box!"); - } else { - printf("Fill::_infill_direction: reference point %d, %d\n", out_shift.x, out_shift.y); - } - #endif - - if (surface.bridge_angle >= 0) { - // use bridge angle - //FIXME Vojtech: Add a debugf? - // Slic3r::debugf "Filling bridge with angle %d\n", rad2deg($surface->bridge_angle); - #ifdef SLIC3R_DEBUG - printf("Filling bridge with angle %f\n", surface.bridge_angle); - #endif - out_angle = surface.bridge_angle; - } else if (this->layer_id != size_t(-1)) { - // alternate fill direction - out_angle += this->_layer_angle(this->layer_id / surface.thickness_layers); - } - - out_angle += float(M_PI/2.); - return std::pair(out_angle, out_shift); -} - -} // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/FillBase.hpp b/xs/src/libslic3r/Fill/FillBase.hpp deleted file mode 100644 index 75627d29e..000000000 --- a/xs/src/libslic3r/Fill/FillBase.hpp +++ /dev/null @@ -1,111 +0,0 @@ -#ifndef slic3r_FillBase_hpp_ -#define slic3r_FillBase_hpp_ - -#include -#include -#include -#include - -#include "../libslic3r.h" -#include "../BoundingBox.hpp" -#include "../ExPolygon.hpp" -#include "../Polyline.hpp" -#include "../PrintConfig.hpp" - -namespace Slic3r { - -class Surface; - -struct FillParams -{ - public: - FillParams() : density(0), dont_connect(false), dont_adjust(false), complete(false) {}; - - // Fill density, fraction in <0, 1> - float density; - - // Don't connect the fill lines around the inner perimeter. - bool dont_connect; - - // Don't adjust spacing to fill the space evenly. - bool dont_adjust; - - // For Honeycomb. - // we were requested to complete each loop; - // in this case we don't try to make more continuous paths - bool complete; -}; - -class Fill -{ -public: - // Index of the layer. - size_t layer_id; - - // Z coordinate of the top print surface, in unscaled coordinates - coordf_t z; - - // in unscaled coordinates - coordf_t spacing; - - // in radians, ccw, 0 = East - float angle; - - // In scaled coordinates. Maximum lenght of a perimeter segment connecting two infill lines. - // Used by the FillRectilinear2, FillGrid2, FillTriangles, FillStars and FillCubic. - // If left to zero, the links will not be limited. - coord_t link_max_length; - - // In scaled coordinates. Used by the concentric infill pattern to clip the loops to create extrusion paths. - coord_t loop_clipping; - - // In scaled coordinates. Bounding box of the 2D projection of the object. - BoundingBox bounding_box; - -public: - virtual ~Fill() {} - - static Fill* new_from_type(const InfillPattern type); - static Fill* new_from_type(const std::string &type); - - void set_bounding_box(const BoundingBox &bb) { this->bounding_box = bb; } - - // Use bridge flow for the fill? - virtual bool use_bridge_flow() const { return false; } - - // Do not sort the fill lines to optimize the print head path? - virtual bool no_sort() const { return false; } - - // Perform the fill. - virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms); - - static coord_t adjust_solid_spacing(const coord_t width, const coord_t distance); - -protected: - Fill() : - layer_id(size_t(-1)), - z(0.f), - spacing(0.f), - angle(0), - link_max_length(0), - loop_clipping(0) - {}; - - // The expolygon may be modified by the method to avoid a copy. - virtual void _fill_surface_single( - const FillParams ¶ms, - unsigned int thickness_layers, - const std::pair &direction, - ExPolygon &expolygon, - Polylines* polylines_out) {}; - - virtual float _layer_angle(size_t idx) const { - return (idx % 2) == 0 ? (M_PI/2.) : 0; - } - - std::pair _infill_direction(const Surface &surface) const; -}; - -} // namespace Slic3r - -#endif // slic3r_FillBase_hpp_ diff --git a/xs/src/libslic3r/Fill/FillConcentric.hpp b/xs/src/libslic3r/Fill/FillConcentric.hpp index a4e1ddb11..472898f6e 100644 --- a/xs/src/libslic3r/Fill/FillConcentric.hpp +++ b/xs/src/libslic3r/Fill/FillConcentric.hpp @@ -1,7 +1,7 @@ #ifndef slic3r_FillConcentric_hpp_ #define slic3r_FillConcentric_hpp_ -#include "FillBase.hpp" +#include "Fill.hpp" namespace Slic3r { diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.hpp b/xs/src/libslic3r/Fill/FillHoneycomb.hpp index 70caaf7a0..b9b0851bb 100644 --- a/xs/src/libslic3r/Fill/FillHoneycomb.hpp +++ b/xs/src/libslic3r/Fill/FillHoneycomb.hpp @@ -5,7 +5,7 @@ #include "../libslic3r.h" -#include "FillBase.hpp" +#include "Fill.hpp" namespace Slic3r { diff --git a/xs/src/libslic3r/Fill/FillPlanePath.hpp b/xs/src/libslic3r/Fill/FillPlanePath.hpp index 33b3ec1db..3eae2fa86 100644 --- a/xs/src/libslic3r/Fill/FillPlanePath.hpp +++ b/xs/src/libslic3r/Fill/FillPlanePath.hpp @@ -5,7 +5,7 @@ #include "../libslic3r.h" -#include "FillBase.hpp" +#include "Fill.hpp" namespace Slic3r { diff --git a/xs/src/libslic3r/Fill/FillRectilinear.hpp b/xs/src/libslic3r/Fill/FillRectilinear.hpp index 03c0826bc..7e7ce4df8 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.hpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.hpp @@ -3,7 +3,7 @@ #include "../libslic3r.h" -#include "FillBase.hpp" +#include "Fill.hpp" namespace Slic3r { diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.hpp b/xs/src/libslic3r/Fill/FillRectilinear2.hpp index f1f8973de..44d8297bb 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear2.hpp +++ b/xs/src/libslic3r/Fill/FillRectilinear2.hpp @@ -3,7 +3,7 @@ #include "../libslic3r.h" -#include "FillBase.hpp" +#include "Fill.hpp" namespace Slic3r { diff --git a/xs/src/libslic3r/Layer.cpp b/xs/src/libslic3r/Layer.cpp index 3b944c97e..102654be0 100644 --- a/xs/src/libslic3r/Layer.cpp +++ b/xs/src/libslic3r/Layer.cpp @@ -2,7 +2,6 @@ #include "ClipperUtils.hpp" #include "Geometry.hpp" #include "Print.hpp" -#include "Fill/Fill.hpp" namespace Slic3r { @@ -239,13 +238,11 @@ Layer::make_fills() #endif FOREACH_LAYERREGION(this, it_layerm) { - LayerRegion &layerm = **it_layerm; - layerm.fills.clear(); - make_fill(layerm, &layerm.fills); + (*it_layerm)->make_fill(); #ifndef NDEBUG - for (size_t i = 0; i < layerm.fills.entities.size(); ++i) - assert(dynamic_cast(layerm.fills.entities[i]) != NULL); + for (size_t i = 0; i < (*it_layerm)->fills.entities.size(); ++i) + assert(dynamic_cast((*it_layerm)->fills.entities[i]) != NULL); #endif } } diff --git a/xs/src/libslic3r/Layer.hpp b/xs/src/libslic3r/Layer.hpp index 7355ed919..c0db224ec 100644 --- a/xs/src/libslic3r/Layer.hpp +++ b/xs/src/libslic3r/Layer.hpp @@ -59,6 +59,7 @@ class LayerRegion void merge_slices(); void prepare_fill_surfaces(); void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); + void make_fill(); void process_external_surfaces(const Layer* lower_layer); double infill_area_threshold() const; diff --git a/xs/src/libslic3r/LayerRegionFill.cpp b/xs/src/libslic3r/LayerRegionFill.cpp new file mode 100644 index 000000000..8319ab195 --- /dev/null +++ b/xs/src/libslic3r/LayerRegionFill.cpp @@ -0,0 +1,285 @@ +#include "Layer.hpp" +#include "ClipperUtils.hpp" +#include "Fill/Fill.hpp" +#include "Geometry.hpp" +#include "Print.hpp" +#include "PrintConfig.hpp" +#include "Surface.hpp" + +namespace Slic3r { + +struct SurfaceGroupAttrib +{ + SurfaceGroupAttrib() : is_solid(false), fw(0.f), pattern(-1) {} + bool operator==(const SurfaceGroupAttrib &other) const + { return is_solid == other.is_solid && fw == other.fw && pattern == other.pattern; } + bool is_solid; + float fw; + // pattern is of type InfillPattern, -1 for an unset pattern. + int pattern; +}; + +// Generate infills for a LayerRegion. +// The LayerRegion at this point of time may contain +// surfaces of various types (internal/bridge/top/bottom/solid). +// The infills are generated on the groups of surfaces with a compatible type. +// Fills an array of ExtrusionPathCollection objects containing the infills generated now +// and the thin fills generated by generate_perimeters(). +void +LayerRegion::make_fill() +{ + this->fills.clear(); + + const double fill_density = this->region()->config.fill_density; + const Flow infill_flow = this->flow(frInfill); + const Flow solid_infill_flow = this->flow(frSolidInfill); + const Flow top_solid_infill_flow = this->flow(frTopSolidInfill); + + SurfaceCollection surfaces; + + // merge adjacent surfaces + // in case of bridge surfaces, the ones with defined angle will be attached to the ones + // without any angle (shouldn't this logic be moved to process_external_surfaces()?) + { + Polygons polygons_bridged; + polygons_bridged.reserve(this->fill_surfaces.surfaces.size()); + for (Surfaces::const_iterator it = this->fill_surfaces.surfaces.begin(); it != this->fill_surfaces.surfaces.end(); ++it) + if (it->bridge_angle >= 0) + append_to(polygons_bridged, (Polygons)*it); + + // group surfaces by distinct properties (equal surface_type, thickness, thickness_layers, bridge_angle) + // group is of type SurfaceCollection + // FIXME: Use some smart heuristics to merge similar surfaces to eliminate tiny regions. + std::vector groups; + this->fill_surfaces.group(&groups); + + // merge compatible groups (we can generate continuous infill for them) + { + // cache flow widths and patterns used for all solid groups + // (we'll use them for comparing compatible groups) + std::vector group_attrib(groups.size()); + for (size_t i = 0; i < groups.size(); ++i) { + // we can only merge solid non-bridge surfaces, so discard + // non-solid surfaces + const Surface &surface = *groups[i].front(); + if (surface.is_solid() && (!surface.is_bridge() || this->layer()->id() == 0)) { + group_attrib[i].is_solid = true; + group_attrib[i].fw = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width; + group_attrib[i].pattern = surface.is_external() ? this->region()->config.external_fill_pattern.value : ipRectilinear; + } + } + // Loop through solid groups, find compatible groups and append them to this one. + for (size_t i = 0; i < groups.size(); ++i) { + if (!group_attrib[i].is_solid) + continue; + for (size_t j = i + 1; j < groups.size();) { + if (group_attrib[i] == group_attrib[j]) { + // groups are compatible, merge them + append_to(groups[i], groups[j]); + groups.erase(groups.begin() + j); + group_attrib.erase(group_attrib.begin() + j); + } else { + ++j; + } + } + } + } + + // Give priority to bridges. Process the bridges in the first round, the rest of the surfaces in the 2nd round. + for (size_t round = 0; round < 2; ++ round) { + for (std::vector::const_iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) { + const SurfacesConstPtr &group = *it_group; + bool is_bridge = group.front()->bridge_angle >= 0; + if (is_bridge != (round == 0)) + continue; + + // Make a union of polygons defining the infiill regions of a group, use a safety offset. + Polygons union_p = union_(to_polygons(group), true); + + // Subtract surfaces having a defined bridge_angle from any other, use a safety offset. + if (!polygons_bridged.empty() && !is_bridge) + union_p = diff(union_p, polygons_bridged, true); + + // subtract any other surface already processed + //FIXME Vojtech: Because the bridge surfaces came first, they are subtracted twice! + surfaces.append( + diff_ex(union_p, to_polygons(surfaces), true), + *group.front() // template + ); + } + } + } + + // we need to detect any narrow surfaces that might collapse + // when adding spacing below + // such narrow surfaces are often generated in sloping walls + // by bridge_over_infill() and combine_infill() as a result of the + // subtraction of the combinable area from the layer infill area, + // which leaves small areas near the perimeters + // we are going to grow such regions by overlapping them with the void (if any) + // TODO: detect and investigate whether there could be narrow regions without + // any void neighbors + { + coord_t distance_between_surfaces = std::max( + std::max(infill_flow.scaled_spacing(), solid_infill_flow.scaled_spacing()), + top_solid_infill_flow.scaled_spacing() + ); + + Polygons surfaces_polygons = (Polygons)surfaces; + Polygons collapsed = diff( + surfaces_polygons, + offset2(surfaces_polygons, -distance_between_surfaces/2, +distance_between_surfaces/2), + true + ); + + Polygons to_subtract; + surfaces.filter_by_type(stInternalVoid, &to_subtract); + + append_to(to_subtract, collapsed); + surfaces.append( + intersection_ex( + offset(collapsed, distance_between_surfaces), + to_subtract, + true + ), + stInternalSolid + ); + } + + if (false) { +// require "Slic3r/SVG.pm"; +// Slic3r::SVG::output("fill_" . $layerm->print_z . ".svg", +// expolygons => [ map $_->expolygon, grep !$_->is_solid, @surfaces ], +// red_expolygons => [ map $_->expolygon, grep $_->is_solid, @surfaces ], +// ); + } + + for (Surfaces::const_iterator surface_it = surfaces.surfaces.begin(); + surface_it != surfaces.surfaces.end(); ++surface_it) { + + const Surface &surface = *surface_it; + if (surface.surface_type == stInternalVoid) + continue; + + InfillPattern fill_pattern = this->region()->config.fill_pattern.value; + double density = fill_density; + FlowRole role = (surface.surface_type == stTop) ? frTopSolidInfill + : surface.is_solid() ? frSolidInfill + : frInfill; + const bool is_bridge = this->layer()->id() > 0 && surface.is_bridge(); + + if (surface.is_solid()) { + density = 100.; + fill_pattern = (surface.is_external() && !is_bridge) + ? this->region()->config.external_fill_pattern.value + : ipRectilinear; + } else if (density <= 0) + continue; + + // get filler object + #if SLIC3R_CPPVER >= 11 + std::unique_ptr f = std::unique_ptr(Fill::new_from_type(fill_pattern)); + #else + std::auto_ptr f = std::auto_ptr(Fill::new_from_type(fill_pattern)); + #endif + f->set_bounding_box(this->layer()->object()->bounding_box()); + + // calculate the actual flow we'll be using for this infill + coordf_t h = (surface.thickness == -1) ? this->layer()->height : surface.thickness; + Flow flow = this->region()->flow( + role, + h, + is_bridge || f->use_bridge_flow(), // bridge flow? + this->layer()->id() == 0, // first layer? + -1, // auto width + *this->layer()->object() + ); + + // calculate flow spacing for infill pattern generation + bool using_internal_flow = false; + if (!surface.is_solid() && !is_bridge) { + // it's internal infill, so we can calculate a generic flow spacing + // for all layers, for avoiding the ugly effect of + // misaligned infill on first layer because of different extrusion width and + // layer height + Flow internal_flow = this->region()->flow( + frInfill, + this->layer()->object()->config.layer_height.value, // TODO: handle infill_every_layers? + false, // no bridge + false, // no first layer + -1, // auto width + *this->layer()->object() + ); + f->spacing = internal_flow.spacing(); + using_internal_flow = true; + } else { + f->spacing = flow.spacing(); + } + + f->layer_id = this->layer()->id(); + f->z = this->layer()->print_z; + f->angle = Geometry::deg2rad(this->region()->config.fill_angle.value); + + // Maximum length of the perimeter segment linking two infill lines. + f->link_max_length = (!is_bridge && density > 80) + ? scale_(3 * f->spacing) + : 0; + + // Used by the concentric infill pattern to clip the loops to create extrusion paths. + f->loop_clipping = scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER; + + // apply half spacing using this flow's own spacing and generate infill + FillParams params; + params.density = density/100; + params.dont_adjust = false; + Polylines polylines = f->fill_surface(surface, params); + if (polylines.empty()) + continue; + + // calculate actual flow from spacing (which might have been adjusted by the infill + // pattern generator) + if (using_internal_flow) { + // if we used the internal flow we're not doing a solid infill + // so we can safely ignore the slight variation that might have + // been applied to f->spacing + } else { + flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow()); + } + + // Save into layer. + ExtrusionEntityCollection* coll = new ExtrusionEntityCollection(); + coll->no_sort = f->no_sort(); + this->fills.entities.push_back(coll); + + { + ExtrusionRole role; + if (is_bridge) { + role = erBridgeInfill; + } else if (surface.is_solid()) { + role = (surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill; + } else { + role = erInternalInfill; + } + + ExtrusionPath templ(role); + templ.mm3_per_mm = flow.mm3_per_mm(); + templ.width = flow.width; + templ.height = flow.height; + + coll->append(STDMOVE(polylines), templ); + } + } + + // add thin fill regions + // thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection + // Unpacks the collection, creates multiple collections per path so that they will + // be individually included in the nearest neighbor search. + // The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection. + for (ExtrusionEntitiesPtr::const_iterator thin_fill = this->thin_fills.entities.begin(); thin_fill != this->thin_fills.entities.end(); ++ thin_fill) { + ExtrusionEntityCollection* coll = new ExtrusionEntityCollection(); + this->fills.entities.push_back(coll); + coll->append(**thin_fill); + } +} + +} // namespace Slic3r diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index 77122057b..8762d180a 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -1,7 +1,7 @@ #include "SLAPrint.hpp" #include "ClipperUtils.hpp" #include "ExtrusionEntity.hpp" -#include "Fill/FillBase.hpp" +#include "Fill/Fill.hpp" #include "Geometry.hpp" #include "Surface.hpp" #include diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index 86dbcad83..06724f875 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -54,7 +54,7 @@ extern "C" { #include #include #include -#include +#include #include #include #include diff --git a/xs/xsp/Filler.xsp b/xs/xsp/Filler.xsp index 85d83d549..1e9652a6c 100644 --- a/xs/xsp/Filler.xsp +++ b/xs/xsp/Filler.xsp @@ -71,8 +71,5 @@ new_from_type(CLASS, type) %package{Slic3r::Filler}; -void make_fill(LayerRegion* layerm, ExtrusionEntityCollection* out) - %code{% make_fill(*layerm, out); %}; - coord_t adjust_solid_spacing(coord_t width, coord_t distance) %code{% RETVAL = Fill::adjust_solid_spacing(width, distance); %}; From ea98d97743fee443847a51eb761cb69cb857ae38 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 30 Nov 2016 18:43:56 +0100 Subject: [PATCH 130/225] No (more) need for a separate FillParams class --- xs/src/libslic3r/Fill/Fill.cpp | 5 +- xs/src/libslic3r/Fill/Fill.hpp | 62 ++++++++++----------- xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp | 5 +- xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp | 1 - xs/src/libslic3r/Fill/FillConcentric.cpp | 5 +- xs/src/libslic3r/Fill/FillConcentric.hpp | 1 - xs/src/libslic3r/Fill/FillHoneycomb.cpp | 7 +-- xs/src/libslic3r/Fill/FillHoneycomb.hpp | 1 - xs/src/libslic3r/Fill/FillPlanePath.cpp | 11 ++-- xs/src/libslic3r/Fill/FillPlanePath.hpp | 1 - xs/src/libslic3r/Fill/FillRectilinear.cpp | 11 ++-- xs/src/libslic3r/Fill/FillRectilinear.hpp | 1 - xs/src/libslic3r/Fill/FillRectilinear2.cpp | 64 +++++++++++----------- xs/src/libslic3r/Fill/FillRectilinear2.hpp | 12 ++-- xs/src/libslic3r/LayerRegionFill.cpp | 9 ++- xs/src/libslic3r/SLAPrint.cpp | 7 +-- xs/src/xsinit.h | 3 +- xs/xsp/Filler.xsp | 12 ++-- 18 files changed, 103 insertions(+), 115 deletions(-) diff --git a/xs/src/libslic3r/Fill/Fill.cpp b/xs/src/libslic3r/Fill/Fill.cpp index 7de4e1fdd..6f7b5486d 100644 --- a/xs/src/libslic3r/Fill/Fill.cpp +++ b/xs/src/libslic3r/Fill/Fill.cpp @@ -51,8 +51,10 @@ Fill::new_from_type(const std::string &type) } Polylines -Fill::fill_surface(const Surface &surface, const FillParams ¶ms) +Fill::fill_surface(const Surface &surface) { + if (this->density == 0) return Polylines(); + // Perform offset. ExPolygons expp = offset_ex(surface.expolygon, -scale_(this->spacing)/2); @@ -60,7 +62,6 @@ Fill::fill_surface(const Surface &surface, const FillParams ¶ms) Polylines polylines_out; for (size_t i = 0; i < expp.size(); ++i) this->_fill_surface_single( - params, surface.thickness_layers, this->_infill_direction(surface), expp[i], diff --git a/xs/src/libslic3r/Fill/Fill.hpp b/xs/src/libslic3r/Fill/Fill.hpp index f14b93e7b..93b4b7e5f 100644 --- a/xs/src/libslic3r/Fill/Fill.hpp +++ b/xs/src/libslic3r/Fill/Fill.hpp @@ -16,26 +16,7 @@ namespace Slic3r { class Surface; -struct FillParams -{ - public: - FillParams() : density(0), dont_connect(false), dont_adjust(false), complete(false) {}; - - // Fill density, fraction in <0, 1> - float density; - - // Don't connect the fill lines around the inner perimeter. - bool dont_connect; - - // Don't adjust spacing to fill the space evenly. - bool dont_adjust; - - // For Honeycomb. - // we were requested to complete each loop; - // in this case we don't try to make more continuous paths - bool complete; -}; - +// Abstract base class for the infill generators. class Fill { public: @@ -60,16 +41,29 @@ public: coord_t loop_clipping; // In scaled coordinates. Bounding box of the 2D projection of the object. + // If not defined, the bounding box of each single expolygon is used. BoundingBox bounding_box; + + // Fill density, fraction in <0, 1> + float density; + + // Don't connect the fill lines around the inner perimeter. + bool dont_connect; + + // Don't adjust spacing to fill the space evenly. + bool dont_adjust; + + // For Honeycomb. + // we were requested to complete each loop; + // in this case we don't try to make more continuous paths + bool complete; public: - virtual ~Fill() {} - static Fill* new_from_type(const InfillPattern type); static Fill* new_from_type(const std::string &type); + static coord_t adjust_solid_spacing(const coord_t width, const coord_t distance); - void set_bounding_box(const BoundingBox &bb) { this->bounding_box = bb; } - + // Implementations can override the following virtual methods: // Use bridge flow for the fill? virtual bool use_bridge_flow() const { return false; } @@ -77,10 +71,8 @@ public: virtual bool no_sort() const { return false; } // Perform the fill. - virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms); - - static coord_t adjust_solid_spacing(const coord_t width, const coord_t distance); - + virtual Polylines fill_surface(const Surface &surface); + protected: Fill() : layer_id(size_t(-1)), @@ -88,17 +80,21 @@ protected: spacing(0.f), angle(0), link_max_length(0), - loop_clipping(0) + loop_clipping(0), + density(0), + dont_connect(false), + dont_adjust(false), + complete(false) {}; - + // The expolygon may be modified by the method to avoid a copy. virtual void _fill_surface_single( - const FillParams ¶ms, - unsigned int thickness_layers, + unsigned int thickness_layers, const std::pair &direction, ExPolygon &expolygon, Polylines* polylines_out) {}; - + + // Implementations can override the following virtual method: virtual float _layer_angle(size_t idx) const { return (idx % 2) == 0 ? (M_PI/2.) : 0; } diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp index e23f77b2c..1c316e920 100644 --- a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp +++ b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -149,7 +149,6 @@ makeGrid(coord_t z, coord_t gridSize, size_t gridWidth, size_t gridHeight, size_ void Fill3DHoneycomb::_fill_surface_single( - const FillParams ¶ms, unsigned int thickness_layers, const std::pair &direction, ExPolygon &expolygon, @@ -157,7 +156,7 @@ Fill3DHoneycomb::_fill_surface_single( { // no rotation is supported for this infill pattern BoundingBox bb = expolygon.contour.bounding_box(); - const coord_t distance = coord_t(scale_(this->spacing) / params.density); + const coord_t distance = coord_t(scale_(this->spacing) / this->density); // align bounding box to a multiple of our honeycomb grid module // (a module is 2*$distance since one $distance half-module is @@ -181,7 +180,7 @@ Fill3DHoneycomb::_fill_surface_single( polylines = intersection_pl(polylines, (Polygons)expolygon); // connect lines - if (!params.dont_connect && !polylines.empty()) { // prevent calling leftmost_point() on empty collections + if (!this->dont_connect && !polylines.empty()) { // prevent calling leftmost_point() on empty collections ExPolygon expolygon_off; { ExPolygons expolygons_off = offset_ex(expolygon, SCALED_EPSILON); diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp b/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp index 19a45818f..550630596 100644 --- a/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp +++ b/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp @@ -19,7 +19,6 @@ public: protected: virtual void _fill_surface_single( - const FillParams ¶ms, unsigned int thickness_layers, const std::pair &direction, ExPolygon &expolygon, diff --git a/xs/src/libslic3r/Fill/FillConcentric.cpp b/xs/src/libslic3r/Fill/FillConcentric.cpp index 4bec2bb7d..bf537e532 100644 --- a/xs/src/libslic3r/Fill/FillConcentric.cpp +++ b/xs/src/libslic3r/Fill/FillConcentric.cpp @@ -8,7 +8,6 @@ namespace Slic3r { void FillConcentric::_fill_surface_single( - const FillParams ¶ms, unsigned int thickness_layers, const std::pair &direction, ExPolygon &expolygon, @@ -17,9 +16,9 @@ FillConcentric::_fill_surface_single( // no rotation is supported for this infill pattern const coord_t min_spacing = scale_(this->spacing); - coord_t distance = coord_t(min_spacing / params.density); + coord_t distance = coord_t(min_spacing / this->density); - if (params.density > 0.9999f && !params.dont_adjust) { + if (this->density > 0.9999f && !this->dont_adjust) { BoundingBox bounding_box = expolygon.contour.bounding_box(); distance = this->adjust_solid_spacing(bounding_box.size().x, distance); this->spacing = unscale(distance); diff --git a/xs/src/libslic3r/Fill/FillConcentric.hpp b/xs/src/libslic3r/Fill/FillConcentric.hpp index 472898f6e..836107f4a 100644 --- a/xs/src/libslic3r/Fill/FillConcentric.hpp +++ b/xs/src/libslic3r/Fill/FillConcentric.hpp @@ -12,7 +12,6 @@ public: protected: virtual void _fill_surface_single( - const FillParams ¶ms, unsigned int thickness_layers, const std::pair &direction, ExPolygon &expolygon, diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.cpp b/xs/src/libslic3r/Fill/FillHoneycomb.cpp index e3a67f3f7..127d7a810 100644 --- a/xs/src/libslic3r/Fill/FillHoneycomb.cpp +++ b/xs/src/libslic3r/Fill/FillHoneycomb.cpp @@ -8,20 +8,19 @@ namespace Slic3r { void FillHoneycomb::_fill_surface_single( - const FillParams ¶ms, unsigned int thickness_layers, const std::pair &direction, ExPolygon &expolygon, Polylines* polylines_out) { // cache hexagons math - CacheID cache_id = std::make_pair(params.density, this->spacing); + CacheID cache_id = std::make_pair(this->density, this->spacing); Cache::iterator it_m = this->cache.find(cache_id); if (it_m == this->cache.end()) { it_m = this->cache.insert(it_m, std::pair(cache_id, CacheData())); CacheData &m = it_m->second; coord_t min_spacing = scale_(this->spacing); - m.distance = min_spacing / params.density; + m.distance = min_spacing / this->density; m.hex_side = m.distance / (sqrt(3)/2); m.hex_width = m.distance * 2; // $m->{hex_width} == $m->{hex_side} * sqrt(3); coord_t hex_height = m.hex_side * 2; @@ -73,7 +72,7 @@ FillHoneycomb::_fill_surface_single( } } - if (true || params.complete) { + if (true || this->complete) { // we were requested to complete each loop; // in this case we don't try to make more continuous paths Polygons polygons_trimmed = intersection((Polygons)expolygon, polygons); diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.hpp b/xs/src/libslic3r/Fill/FillHoneycomb.hpp index b9b0851bb..5d87e6138 100644 --- a/xs/src/libslic3r/Fill/FillHoneycomb.hpp +++ b/xs/src/libslic3r/Fill/FillHoneycomb.hpp @@ -16,7 +16,6 @@ public: protected: virtual void _fill_surface_single( - const FillParams ¶ms, unsigned int thickness_layers, const std::pair &direction, ExPolygon &expolygon, diff --git a/xs/src/libslic3r/Fill/FillPlanePath.cpp b/xs/src/libslic3r/Fill/FillPlanePath.cpp index 7952daf5b..3dad299f4 100644 --- a/xs/src/libslic3r/Fill/FillPlanePath.cpp +++ b/xs/src/libslic3r/Fill/FillPlanePath.cpp @@ -7,7 +7,6 @@ namespace Slic3r { void FillPlanePath::_fill_surface_single( - const FillParams ¶ms, unsigned int thickness_layers, const std::pair &direction, ExPolygon &expolygon, @@ -15,11 +14,13 @@ void FillPlanePath::_fill_surface_single( { expolygon.rotate(-direction.first); - const coord_t distance_between_lines = scale_(this->spacing) / params.density; + const coord_t distance_between_lines = scale_(this->spacing) / this->density; - // align infill across layers using the object's bounding box - // Rotated bounding box of the whole object. - BoundingBox bounding_box = this->bounding_box.rotated(-direction.first); + // align infill across layers using the object's bounding box (if available) + BoundingBox bounding_box = this->bounding_box.defined + ? this->bounding_box + : expolygon.contour.bounding_box(); + bounding_box = bounding_box.rotated(-direction.first); const Point shift = this->_centered() ? bounding_box.center() diff --git a/xs/src/libslic3r/Fill/FillPlanePath.hpp b/xs/src/libslic3r/Fill/FillPlanePath.hpp index 3eae2fa86..00c05fa9d 100644 --- a/xs/src/libslic3r/Fill/FillPlanePath.hpp +++ b/xs/src/libslic3r/Fill/FillPlanePath.hpp @@ -20,7 +20,6 @@ public: protected: virtual void _fill_surface_single( - const FillParams ¶ms, unsigned int thickness_layers, const std::pair &direction, ExPolygon &expolygon, diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/xs/src/libslic3r/Fill/FillRectilinear.cpp index dde04d27b..0a08e78a7 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.cpp @@ -8,25 +8,26 @@ namespace Slic3r { void FillRectilinear::_fill_surface_single( - const FillParams ¶ms, unsigned int thickness_layers, const std::pair &direction, ExPolygon &expolygon, Polylines* polylines_out) { - assert(params.density > 0.0001f && params.density <= 1.f); + assert(this->density > 0.0001f && this->density <= 1.f); // rotate polygons so that we can work with vertical lines here expolygon.rotate(-direction.first); this->_min_spacing = scale_(this->spacing); - this->_line_spacing = coord_t(coordf_t(this->_min_spacing) / params.density); + this->_line_spacing = coord_t(coordf_t(this->_min_spacing) / this->density); this->_diagonal_distance = this->_line_spacing * 2; this->_line_oscillation = this->_line_spacing - this->_min_spacing; // only for Line infill + + // We ignore this->bounding_box because it doesn't matter; we're doing align_to_grid below. BoundingBox bounding_box = expolygon.contour.bounding_box(); // define flow spacing according to requested density - if (params.density > 0.9999f && !params.dont_adjust) { + if (this->density > 0.9999f && !this->dont_adjust) { this->_line_spacing = this->adjust_solid_spacing(bounding_box.size().x, this->_line_spacing); this->spacing = unscale(this->_line_spacing); } else { @@ -79,7 +80,7 @@ void FillRectilinear::_fill_surface_single( size_t n_polylines_out_old = polylines_out->size(); // connect lines - if (!params.dont_connect && !polylines.empty()) { // prevent calling leftmost_point() on empty collections + if (!this->dont_connect && !polylines.empty()) { // prevent calling leftmost_point() on empty collections // offset the expolygon by max(min_spacing/2, extra) ExPolygon expolygon_off; { diff --git a/xs/src/libslic3r/Fill/FillRectilinear.hpp b/xs/src/libslic3r/Fill/FillRectilinear.hpp index 7e7ce4df8..1606aeb3c 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.hpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.hpp @@ -14,7 +14,6 @@ public: protected: virtual void _fill_surface_single( - const FillParams ¶ms, unsigned int thickness_layers, const std::pair &direction, ExPolygon &expolygon, diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.cpp b/xs/src/libslic3r/Fill/FillRectilinear2.cpp index 1458c90ac..3ec5e0bac 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear2.cpp @@ -835,7 +835,7 @@ enum DirectionMask DIR_BACKWARD = 2 }; -bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out) +bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angleBase, float pattern_shift, Polylines &polylines_out) { // At the end, only the new polylines will be rotated back. size_t n_polylines_out_initial = polylines_out.size(); @@ -849,8 +849,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP std::pair rotate_vector = this->_infill_direction(*surface); rotate_vector.first += angleBase; - myassert(params.density > 0.0001f && params.density <= 1.f); - coord_t line_spacing = coord_t(scale_(this->spacing) / params.density); + myassert(this->density > 0.0001f && this->density <= 1.f); + coord_t line_spacing = coord_t(scale_(this->spacing) / this->density); // On the polygons of poly_with_offset, the infill lines will be connected. ExPolygonWithOffset poly_with_offset( @@ -867,8 +867,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP BoundingBox bounding_box(poly_with_offset.polygons_src); // define flow spacing according to requested density - bool full_infill = params.density > 0.9999f; - if (full_infill && !params.dont_adjust) { + bool full_infill = this->density > 0.9999f; + if (full_infill && !this->dont_adjust) { line_spacing = this->adjust_solid_spacing(bounding_box.size().x, line_spacing); this->spacing = unscale(line_spacing); } else { @@ -1391,7 +1391,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP (distNext < distPrev) : intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK; myassert(intrsctn->is_inner()); - bool skip = params.dont_connect || (link_max_length > 0 && (take_next ? distNext : distPrev) > link_max_length); + bool skip = this->dont_connect || (link_max_length > 0 && (take_next ? distNext : distPrev) > link_max_length); if (skip) { // Just skip the connecting contour and start a new path. goto dont_connect; @@ -1453,7 +1453,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP distance_of_segmens(poly, intrsctn->iSegment, iSegNext, false)) : (vert_seg_dir_valid_mask == DIR_FORWARD); // Skip this perimeter line? - bool skip = params.dont_connect; + bool skip = this->dont_connect; if (! skip && link_max_length > 0) { coordf_t link_length = measure_perimeter_segment_on_vertical_line_length( poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iNext, dir_forward); @@ -1555,66 +1555,66 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP return true; } -Polylines FillRectilinear2::fill_surface(const Surface &surface, const FillParams ¶ms) +Polylines FillRectilinear2::fill_surface(const Surface &surface) { Polylines polylines_out; - if (! fill_surface_by_lines(&surface, params, 0.f, 0.f, polylines_out)) { + if (! fill_surface_by_lines(&surface, 0.f, 0.f, polylines_out)) { printf("FillRectilinear2::fill_surface() failed to fill a region.\n"); } return polylines_out; } -Polylines FillGrid2::fill_surface(const Surface &surface, const FillParams ¶ms) +Polylines FillGrid2::fill_surface(const Surface &surface) { // Each linear fill covers half of the target coverage. - FillParams params2 = params; - params2.density *= 0.5f; + FillGrid2 fill2 = *this; + fill2.density *= 0.5f; Polylines polylines_out; - if (! fill_surface_by_lines(&surface, params2, 0.f, 0.f, polylines_out) || - ! fill_surface_by_lines(&surface, params2, float(M_PI / 2.), 0.f, polylines_out)) { + if (! fill2.fill_surface_by_lines(&surface, 0.f, 0.f, polylines_out) || + ! fill2.fill_surface_by_lines(&surface, float(M_PI / 2.), 0.f, polylines_out)) { printf("FillGrid2::fill_surface() failed to fill a region.\n"); } return polylines_out; } -Polylines FillTriangles::fill_surface(const Surface &surface, const FillParams ¶ms) +Polylines FillTriangles::fill_surface(const Surface &surface) { // Each linear fill covers 1/3 of the target coverage. - FillParams params2 = params; - params2.density *= 0.333333333f; + FillTriangles fill2 = *this; + fill2.density *= 0.333333333f; Polylines polylines_out; - if (! fill_surface_by_lines(&surface, params2, 0.f, 0., polylines_out) || - ! fill_surface_by_lines(&surface, params2, float(M_PI / 3.), 0., polylines_out) || - ! fill_surface_by_lines(&surface, params2, float(2. * M_PI / 3.), 0.5 * this->spacing / params2.density, polylines_out)) { + if (! fill2.fill_surface_by_lines(&surface, 0.f, 0., polylines_out) || + ! fill2.fill_surface_by_lines(&surface, float(M_PI / 3.), 0., polylines_out) || + ! fill2.fill_surface_by_lines(&surface, float(2. * M_PI / 3.), 0.5 * this->spacing / fill2.density, polylines_out)) { printf("FillTriangles::fill_surface() failed to fill a region.\n"); } return polylines_out; } -Polylines FillStars::fill_surface(const Surface &surface, const FillParams ¶ms) +Polylines FillStars::fill_surface(const Surface &surface) { // Each linear fill covers 1/3 of the target coverage. - FillParams params2 = params; - params2.density *= 0.333333333f; + FillStars fill2 = *this; + fill2.density *= 0.333333333f; Polylines polylines_out; - if (! fill_surface_by_lines(&surface, params2, 0.f, 0., polylines_out) || - ! fill_surface_by_lines(&surface, params2, float(M_PI / 3.), 0., polylines_out) || - ! fill_surface_by_lines(&surface, params2, float(2. * M_PI / 3.), 0., polylines_out)) { + if (! fill2.fill_surface_by_lines(&surface, 0.f, 0., polylines_out) || + ! fill2.fill_surface_by_lines(&surface, float(M_PI / 3.), 0., polylines_out) || + ! fill2.fill_surface_by_lines(&surface, float(2. * M_PI / 3.), 0., polylines_out)) { printf("FillStars::fill_surface() failed to fill a region.\n"); } return polylines_out; } -Polylines FillCubic::fill_surface(const Surface &surface, const FillParams ¶ms) +Polylines FillCubic::fill_surface(const Surface &surface) { // Each linear fill covers 1/3 of the target coverage. - FillParams params2 = params; - params2.density *= 0.333333333f; + FillCubic fill2 = *this; + fill2.density *= 0.333333333f; Polylines polylines_out; - if (! fill_surface_by_lines(&surface, params2, 0.f, z, polylines_out) || - ! fill_surface_by_lines(&surface, params2, float(M_PI / 3.), -z, polylines_out) || + if (! fill2.fill_surface_by_lines(&surface, 0.f, z, polylines_out) || + ! fill2.fill_surface_by_lines(&surface, float(M_PI / 3.), -z, polylines_out) || // Rotated by PI*2/3 + PI to achieve reverse sloping wall. - ! fill_surface_by_lines(&surface, params2, float(M_PI * 2. / 3.), z, polylines_out)) { + ! fill2.fill_surface_by_lines(&surface, float(M_PI * 2. / 3.), z, polylines_out)) { printf("FillCubic::fill_surface() failed to fill a region.\n"); } return polylines_out; diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.hpp b/xs/src/libslic3r/Fill/FillRectilinear2.hpp index 44d8297bb..45368089f 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear2.hpp +++ b/xs/src/libslic3r/Fill/FillRectilinear2.hpp @@ -13,17 +13,17 @@ class FillRectilinear2 : public Fill { public: virtual ~FillRectilinear2() {} - virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms); + virtual Polylines fill_surface(const Surface &surface); protected: - bool fill_surface_by_lines(const Surface *surface, const FillParams ¶ms, float angleBase, float pattern_shift, Polylines &polylines_out); + bool fill_surface_by_lines(const Surface *surface, float angleBase, float pattern_shift, Polylines &polylines_out); }; class FillGrid2 : public FillRectilinear2 { public: virtual ~FillGrid2() {} - virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms); + virtual Polylines fill_surface(const Surface &surface); protected: // The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill. @@ -34,7 +34,7 @@ class FillTriangles : public FillRectilinear2 { public: virtual ~FillTriangles() {} - virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms); + virtual Polylines fill_surface(const Surface &surface); protected: // The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill. @@ -45,7 +45,7 @@ class FillStars : public FillRectilinear2 { public: virtual ~FillStars() {} - virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms); + virtual Polylines fill_surface(const Surface &surface); protected: // The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill. @@ -56,7 +56,7 @@ class FillCubic : public FillRectilinear2 { public: virtual ~FillCubic() {} - virtual Polylines fill_surface(const Surface &surface, const FillParams ¶ms); + virtual Polylines fill_surface(const Surface &surface); protected: // The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill. diff --git a/xs/src/libslic3r/LayerRegionFill.cpp b/xs/src/libslic3r/LayerRegionFill.cpp index 8319ab195..048514773 100644 --- a/xs/src/libslic3r/LayerRegionFill.cpp +++ b/xs/src/libslic3r/LayerRegionFill.cpp @@ -182,7 +182,7 @@ LayerRegion::make_fill() #else std::auto_ptr f = std::auto_ptr(Fill::new_from_type(fill_pattern)); #endif - f->set_bounding_box(this->layer()->object()->bounding_box()); + f->bounding_box = this->layer()->object()->bounding_box(); // calculate the actual flow we'll be using for this infill coordf_t h = (surface.thickness == -1) ? this->layer()->height : surface.thickness; @@ -229,10 +229,9 @@ LayerRegion::make_fill() f->loop_clipping = scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER; // apply half spacing using this flow's own spacing and generate infill - FillParams params; - params.density = density/100; - params.dont_adjust = false; - Polylines polylines = f->fill_surface(surface, params); + f->density = density/100; + f->dont_adjust = false; + Polylines polylines = f->fill_surface(surface); if (polylines.empty()) continue; diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index 8762d180a..a3bfc4341 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -62,9 +62,8 @@ SLAPrint::slice() fill->bounding_box.merge(Point::new_scale(bb.max.x, bb.max.y)); fill->spacing = this->config.get_abs_value("infill_extrusion_width", this->config.layer_height.value); fill->angle = Geometry::deg2rad(this->config.fill_angle.value); - FillParams fill_params; - fill_params.density = this->config.fill_density.value/100; - + fill->density = this->config.fill_density.value/100; + ExtrusionPath templ(erInternalInfill); templ.width = fill->spacing; @@ -103,7 +102,7 @@ SLAPrint::slice() const ExPolygons internal_ex = intersection_ex(infill, internal); for (ExPolygons::const_iterator it = internal_ex.begin(); it != internal_ex.end(); ++it) { - Polylines polylines = fill->fill_surface(Surface(stInternal, *it), fill_params); + Polylines polylines = fill->fill_surface(Surface(stInternal, *it)); layer.infill.append(polylines, templ); } } diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index 06724f875..a5a3949d6 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -163,8 +163,7 @@ class Filler fill = NULL; } }; - Fill *fill; - FillParams params; + Fill* fill; }; } diff --git a/xs/xsp/Filler.xsp b/xs/xsp/Filler.xsp index 1e9652a6c..bd223a680 100644 --- a/xs/xsp/Filler.xsp +++ b/xs/xsp/Filler.xsp @@ -12,7 +12,7 @@ ~Filler(); void set_bounding_box(BoundingBox *bbox) - %code{% THIS->fill->set_bounding_box(*bbox); %}; + %code{% THIS->fill->bounding_box = *bbox; %}; void set_spacing(coordf_t spacing) %code{% THIS->fill->spacing = spacing; %}; coordf_t spacing() @@ -34,20 +34,20 @@ %code{% RETVAL = THIS->fill->no_sort(); %}; void set_density(float density) - %code{% THIS->params.density = density; %}; + %code{% THIS->fill->density = density; %}; void set_dont_connect(bool dont_connect) - %code{% THIS->params.dont_connect = dont_connect; %}; + %code{% THIS->fill->dont_connect = dont_connect; %}; void set_dont_adjust(bool dont_adjust) - %code{% THIS->params.dont_adjust = dont_adjust; %}; + %code{% THIS->fill->dont_adjust = dont_adjust; %}; void set_complete(bool complete) - %code{% THIS->params.complete = complete; %}; + %code{% THIS->fill->complete = complete; %}; PolylineCollection* _fill_surface(Surface *surface) %code{% PolylineCollection *pc = NULL; if (THIS->fill != NULL) { pc = new PolylineCollection(); - pc->polylines = THIS->fill->fill_surface(*surface, THIS->params); + pc->polylines = THIS->fill->fill_surface(*surface); } RETVAL = pc; %}; From 52fbe10f3dc4622276dde399088838b95748f82e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 30 Nov 2016 19:37:14 +0100 Subject: [PATCH 131/225] Improvements to C++ CLI options parsing (support single-letter form) --- xs/src/libslic3r/Config.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/xs/src/libslic3r/Config.cpp b/xs/src/libslic3r/Config.cpp index 104fa28d0..ed8ffc468 100644 --- a/xs/src/libslic3r/Config.cpp +++ b/xs/src/libslic3r/Config.cpp @@ -5,13 +5,14 @@ #include #include #include // std::runtime_error +#include +#include #include #include #include +#include #include #include -#include -#include #include #if defined(_WIN32) && !defined(setenv) && defined(_putenv_s) @@ -500,8 +501,8 @@ DynamicConfig::read_cli(const int argc, const char** argv, t_config_option_keys* if (token == "--") { // stop parsing tokens as options parse_options = false; - } else if (parse_options && boost::starts_with(token, "--")) { - boost::algorithm::erase_head(token, 2); + } else if (parse_options && boost::starts_with(token, "-")) { + boost::algorithm::trim_left_if(token, boost::algorithm::is_any_of("-")); // TODO: handle --key=value // look for the option def @@ -513,7 +514,9 @@ DynamicConfig::read_cli(const int argc, const char** argv, t_config_option_keys* if (optdef->cli == token || optdef->cli == token + '!' - || boost::starts_with(optdef->cli, token + "=")) { + || boost::starts_with(optdef->cli, token + "=") + || boost::starts_with(optdef->cli, token + "|") + || (token.length() == 1 && boost::contains(optdef->cli, "|" + token))) { opt_key = oit->first; break; } From 27d41f89b8b72cc803389053c1a9eeaaa15f2103 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 30 Nov 2016 19:47:45 +0100 Subject: [PATCH 132/225] Implement clone() for Fill classes --- xs/MANIFEST | 1 + xs/src/libslic3r/Fill/Fill.hpp | 4 +++- xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp | 1 + xs/src/libslic3r/Fill/FillConcentric.hpp | 1 + xs/src/libslic3r/Fill/FillHoneycomb.hpp | 1 + xs/src/libslic3r/Fill/FillPlanePath.hpp | 3 +++ xs/src/libslic3r/Fill/FillRectilinear.hpp | 1 + xs/src/libslic3r/Fill/FillRectilinear2.hpp | 5 +++++ 8 files changed, 16 insertions(+), 1 deletion(-) diff --git a/xs/MANIFEST b/xs/MANIFEST index 9281abd93..963dde283 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -57,6 +57,7 @@ src/libslic3r/IO.hpp src/libslic3r/Layer.cpp src/libslic3r/Layer.hpp src/libslic3r/LayerRegion.cpp +src/libslic3r/LayerRegionFill.cpp src/libslic3r/libslic3r.h src/libslic3r/Line.cpp src/libslic3r/Line.hpp diff --git a/xs/src/libslic3r/Fill/Fill.hpp b/xs/src/libslic3r/Fill/Fill.hpp index 93b4b7e5f..df752b47d 100644 --- a/xs/src/libslic3r/Fill/Fill.hpp +++ b/xs/src/libslic3r/Fill/Fill.hpp @@ -62,7 +62,8 @@ public: static Fill* new_from_type(const InfillPattern type); static Fill* new_from_type(const std::string &type); static coord_t adjust_solid_spacing(const coord_t width, const coord_t distance); - + virtual Fill* clone() const = 0; + // Implementations can override the following virtual methods: // Use bridge flow for the fill? virtual bool use_bridge_flow() const { return false; } @@ -86,6 +87,7 @@ protected: dont_adjust(false), complete(false) {}; + virtual ~Fill(); // The expolygon may be modified by the method to avoid a copy. virtual void _fill_surface_single( diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp b/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp index 550630596..82699397f 100644 --- a/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp +++ b/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp @@ -12,6 +12,7 @@ namespace Slic3r { class Fill3DHoneycomb : public Fill { public: + virtual Fill* clone() const { return new Fill3DHoneycomb(*this); }; virtual ~Fill3DHoneycomb() {} // require bridge flow since most of this pattern hangs in air diff --git a/xs/src/libslic3r/Fill/FillConcentric.hpp b/xs/src/libslic3r/Fill/FillConcentric.hpp index 836107f4a..7bb7687f5 100644 --- a/xs/src/libslic3r/Fill/FillConcentric.hpp +++ b/xs/src/libslic3r/Fill/FillConcentric.hpp @@ -11,6 +11,7 @@ public: virtual ~FillConcentric() {} protected: + virtual Fill* clone() const { return new FillConcentric(*this); }; virtual void _fill_surface_single( unsigned int thickness_layers, const std::pair &direction, diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.hpp b/xs/src/libslic3r/Fill/FillHoneycomb.hpp index 5d87e6138..8fdb00265 100644 --- a/xs/src/libslic3r/Fill/FillHoneycomb.hpp +++ b/xs/src/libslic3r/Fill/FillHoneycomb.hpp @@ -15,6 +15,7 @@ public: virtual ~FillHoneycomb() {} protected: + virtual Fill* clone() const { return new FillHoneycomb(*this); }; virtual void _fill_surface_single( unsigned int thickness_layers, const std::pair &direction, diff --git a/xs/src/libslic3r/Fill/FillPlanePath.hpp b/xs/src/libslic3r/Fill/FillPlanePath.hpp index 00c05fa9d..04fb225a8 100644 --- a/xs/src/libslic3r/Fill/FillPlanePath.hpp +++ b/xs/src/libslic3r/Fill/FillPlanePath.hpp @@ -33,6 +33,7 @@ protected: class FillArchimedeanChords : public FillPlanePath { public: + virtual Fill* clone() const { return new FillArchimedeanChords(*this); }; virtual ~FillArchimedeanChords() {} protected: @@ -43,6 +44,7 @@ protected: class FillHilbertCurve : public FillPlanePath { public: + virtual Fill* clone() const { return new FillHilbertCurve(*this); }; virtual ~FillHilbertCurve() {} protected: @@ -53,6 +55,7 @@ protected: class FillOctagramSpiral : public FillPlanePath { public: + virtual Fill* clone() const { return new FillOctagramSpiral(*this); }; virtual ~FillOctagramSpiral() {} protected: diff --git a/xs/src/libslic3r/Fill/FillRectilinear.hpp b/xs/src/libslic3r/Fill/FillRectilinear.hpp index 1606aeb3c..f6c633e3a 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.hpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.hpp @@ -10,6 +10,7 @@ namespace Slic3r { class FillRectilinear : public Fill { public: + virtual Fill* clone() const { return new FillRectilinear(*this); }; virtual ~FillRectilinear() {} protected: diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.hpp b/xs/src/libslic3r/Fill/FillRectilinear2.hpp index 45368089f..50ecf6c38 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear2.hpp +++ b/xs/src/libslic3r/Fill/FillRectilinear2.hpp @@ -12,6 +12,7 @@ class Surface; class FillRectilinear2 : public Fill { public: + virtual Fill* clone() const { return new FillRectilinear2(*this); }; virtual ~FillRectilinear2() {} virtual Polylines fill_surface(const Surface &surface); @@ -22,6 +23,7 @@ protected: class FillGrid2 : public FillRectilinear2 { public: + virtual Fill* clone() const { return new FillGrid2(*this); }; virtual ~FillGrid2() {} virtual Polylines fill_surface(const Surface &surface); @@ -33,6 +35,7 @@ protected: class FillTriangles : public FillRectilinear2 { public: + virtual Fill* clone() const { return new FillTriangles(*this); }; virtual ~FillTriangles() {} virtual Polylines fill_surface(const Surface &surface); @@ -44,6 +47,7 @@ protected: class FillStars : public FillRectilinear2 { public: + virtual Fill* clone() const { return new FillStars(*this); }; virtual ~FillStars() {} virtual Polylines fill_surface(const Surface &surface); @@ -55,6 +59,7 @@ protected: class FillCubic : public FillRectilinear2 { public: + virtual Fill* clone() const { return new FillCubic(*this); }; virtual ~FillCubic() {} virtual Polylines fill_surface(const Surface &surface); From 578658a2fb5cc6ef2cf88d29a3bb7c83e633ab26 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 30 Nov 2016 19:48:08 +0100 Subject: [PATCH 133/225] Multithreaded infill generation for SLAPrint --- xs/src/libslic3r/PrintConfig.hpp | 2 + xs/src/libslic3r/SLAPrint.cpp | 113 +++++++++++++++++-------------- xs/src/libslic3r/SLAPrint.hpp | 2 + 3 files changed, 66 insertions(+), 51 deletions(-) diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 864f2da7a..37e376114 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -545,6 +545,7 @@ class SLAPrintConfig ConfigOptionBool support_material; ConfigOptionFloatOrPercent support_material_extrusion_width; ConfigOptionFloat support_material_spacing; + ConfigOptionInt threads; SLAPrintConfig() : StaticPrintConfig() { this->set_defaults(); @@ -563,6 +564,7 @@ class SLAPrintConfig OPT_PTR(support_material); OPT_PTR(support_material_extrusion_width); OPT_PTR(support_material_spacing); + OPT_PTR(threads); return NULL; }; diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index a3bfc4341..d57cf71bb 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -56,63 +56,19 @@ SLAPrint::slice() // generate infill if (this->config.fill_density < 100) { - const float shell_thickness = this->config.get_abs_value("perimeter_extrusion_width", this->config.layer_height.value); - std::auto_ptr fill = std::auto_ptr(Fill::new_from_type(this->config.fill_pattern.value)); + std::auto_ptr fill(Fill::new_from_type(this->config.fill_pattern.value)); fill->bounding_box.merge(Point::new_scale(bb.min.x, bb.min.y)); fill->bounding_box.merge(Point::new_scale(bb.max.x, bb.max.y)); fill->spacing = this->config.get_abs_value("infill_extrusion_width", this->config.layer_height.value); fill->angle = Geometry::deg2rad(this->config.fill_angle.value); fill->density = this->config.fill_density.value/100; - ExtrusionPath templ(erInternalInfill); - templ.width = fill->spacing; - - for (size_t i = 0; i < this->layers.size(); ++i) { - Layer &layer = this->layers[i]; - - // In order to detect what regions of this layer need to be solid, - // perform an intersection with layers within the requested shell thickness. - Polygons internal = layer.slices; - for (size_t j = 0; j < this->layers.size(); ++j) { - const Layer &other = this->layers[j]; - if (abs(other.print_z - layer.print_z) > shell_thickness) continue; - - if (j == 0 || j == this->layers.size()-1) { - internal.clear(); - break; - } else if (i != j) { - internal = intersection(internal, other.slices); - if (internal.empty()) break; - } - } - - // If we have no internal infill, just print the whole layer as a solid slice. - if (internal.empty()) continue; - layer.solid = false; - - const Polygons infill = offset(layer.slices, -scale_(shell_thickness)); - - // Generate solid infill. - layer.solid_infill << diff_ex(infill, internal, true); - - // Generate internal infill. - { - fill->layer_id = i; - fill->z = layer.print_z; - - const ExPolygons internal_ex = intersection_ex(infill, internal); - for (ExPolygons::const_iterator it = internal_ex.begin(); it != internal_ex.end(); ++it) { - Polylines polylines = fill->fill_surface(Surface(stInternal, *it)); - layer.infill.append(polylines, templ); - } - } - - // Generate perimeter(s). - layer.perimeters << diff_ex( - layer.slices, - offset(layer.slices, -scale_(shell_thickness)) - ); - } + parallelize( + 0, + this->layers.size()-1, + boost::bind(&SLAPrint::_infill_layer, this, _1, fill.get()), + this->config.threads.value + ); } // generate support material @@ -193,6 +149,61 @@ SLAPrint::slice() } } +void +SLAPrint::_infill_layer(size_t i, const Fill* _fill) +{ + Layer &layer = this->layers[i]; + + const float shell_thickness = this->config.get_abs_value("perimeter_extrusion_width", this->config.layer_height.value); + + // In order to detect what regions of this layer need to be solid, + // perform an intersection with layers within the requested shell thickness. + Polygons internal = layer.slices; + for (size_t j = 0; j < this->layers.size(); ++j) { + const Layer &other = this->layers[j]; + if (abs(other.print_z - layer.print_z) > shell_thickness) continue; + + if (j == 0 || j == this->layers.size()-1) { + internal.clear(); + break; + } else if (i != j) { + internal = intersection(internal, other.slices); + if (internal.empty()) break; + } + } + + // If we have no internal infill, just print the whole layer as a solid slice. + if (internal.empty()) return; + layer.solid = false; + + const Polygons infill = offset(layer.slices, -scale_(shell_thickness)); + + // Generate solid infill + layer.solid_infill << diff_ex(infill, internal, true); + + // Generate internal infill + { + std::auto_ptr fill(_fill->clone()); + fill->layer_id = i; + fill->z = layer.print_z; + + ExtrusionPath templ(erInternalInfill); + templ.width = fill->spacing; + + const ExPolygons internal_ex = intersection_ex(infill, internal); + for (ExPolygons::const_iterator it = internal_ex.begin(); it != internal_ex.end(); ++it) { + Polylines polylines = fill->fill_surface(Surface(stInternal, *it)); + layer.infill.append(polylines, templ); + } + } + + // Generate perimeter(s). + layer.perimeters << diff_ex( + layer.slices, + offset(layer.slices, -scale_(shell_thickness)) + ); +} + void SLAPrint::write_svg(const std::string &outputfile) const { diff --git a/xs/src/libslic3r/SLAPrint.hpp b/xs/src/libslic3r/SLAPrint.hpp index e3373b508..6c6028abb 100644 --- a/xs/src/libslic3r/SLAPrint.hpp +++ b/xs/src/libslic3r/SLAPrint.hpp @@ -4,6 +4,7 @@ #include "libslic3r.h" #include "ExPolygon.hpp" #include "ExPolygonCollection.hpp" +#include "Fill/Fill.hpp" #include "Model.hpp" #include "Point.hpp" #include "PrintConfig.hpp" @@ -45,6 +46,7 @@ class SLAPrint Model* model; BoundingBoxf3 bb; + void _infill_layer(size_t i, const Fill* fill); coordf_t sm_pillars_radius() const; std::string _SVG_path_d(const Polygon &polygon) const; std::string _SVG_path_d(const ExPolygon &expolygon) const; From c6ea0118a7f84bafd6a71a48e48f481eb635888b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 30 Nov 2016 20:55:53 +0100 Subject: [PATCH 134/225] Fix compilation --- xs/src/libslic3r/Fill/Fill.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/xs/src/libslic3r/Fill/Fill.hpp b/xs/src/libslic3r/Fill/Fill.hpp index df752b47d..266dad4c6 100644 --- a/xs/src/libslic3r/Fill/Fill.hpp +++ b/xs/src/libslic3r/Fill/Fill.hpp @@ -63,13 +63,14 @@ public: static Fill* new_from_type(const std::string &type); static coord_t adjust_solid_spacing(const coord_t width, const coord_t distance); virtual Fill* clone() const = 0; + virtual ~Fill() {}; // Implementations can override the following virtual methods: // Use bridge flow for the fill? - virtual bool use_bridge_flow() const { return false; } + virtual bool use_bridge_flow() const { return false; }; // Do not sort the fill lines to optimize the print head path? - virtual bool no_sort() const { return false; } + virtual bool no_sort() const { return false; }; // Perform the fill. virtual Polylines fill_surface(const Surface &surface); @@ -87,7 +88,6 @@ protected: dont_adjust(false), complete(false) {}; - virtual ~Fill(); // The expolygon may be modified by the method to avoid a copy. virtual void _fill_surface_single( @@ -99,7 +99,7 @@ protected: // Implementations can override the following virtual method: virtual float _layer_angle(size_t idx) const { return (idx % 2) == 0 ? (M_PI/2.) : 0; - } + }; std::pair _infill_direction(const Surface &surface) const; }; From 92845300beea50deac3ed540c1fbd9007cd65a43 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 1 Dec 2016 15:53:29 +0100 Subject: [PATCH 135/225] New options dialog for SLAPrint --- lib/Slic3r/Config.pm | 1 + lib/Slic3r/GUI.pm | 1 + lib/Slic3r/GUI/MainFrame.pm | 6 +- lib/Slic3r/GUI/Projector.pm | 45 ++++++++- lib/Slic3r/GUI/SLAPrintOptions.pm | 118 ++++++++++++++++++++++ xs/src/libslic3r/Fill/FillRectilinear.cpp | 2 +- xs/src/libslic3r/Point.cpp | 1 + xs/src/libslic3r/SLAPrint.cpp | 6 +- xs/xsp/Config.xsp | 4 + xs/xsp/SLAPrint.xsp | 1 + 10 files changed, 171 insertions(+), 14 deletions(-) create mode 100644 lib/Slic3r/GUI/SLAPrintOptions.pm diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 5cd94cf32..031edaf37 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -421,5 +421,6 @@ sub Slic3r::Config::Print::new { Slic3r::Config::Static::new_PrintConfig } sub Slic3r::Config::PrintObject::new { Slic3r::Config::Static::new_PrintObjectConfig } sub Slic3r::Config::PrintRegion::new { Slic3r::Config::Static::new_PrintRegionConfig } sub Slic3r::Config::Full::new { Slic3r::Config::Static::new_FullPrintConfig } +sub Slic3r::Config::SLAPrint::new { Slic3r::Config::Static::new_SLAPrintConfig } 1; diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index c95e9afbb..1bc2eeae0 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -31,6 +31,7 @@ use Slic3r::GUI::Projector; use Slic3r::GUI::OptionsGroup; use Slic3r::GUI::OptionsGroup::Field; use Slic3r::GUI::SimpleTab; +use Slic3r::GUI::SLAPrintOptions; use Slic3r::GUI::Tab; our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1"; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 62ea30121..c2455874c 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -253,11 +253,7 @@ sub _init_menubar { $plater->export_amf; }, undef, 'brick_go.png'); $self->_append_menu_item($self->{plater_menu}, "Open DLP Projector…\tCtrl+L", 'Open projector window for DLP printing', sub { - my $projector = Slic3r::GUI::Projector->new($self); - - # this double invocation is needed for properly hiding the MainFrame - $projector->Show; - $projector->ShowModal; + Slic3r::GUI::SLAPrintOptions->new($self)->ShowModal; }, undef, 'film.png'); $self->{object_menu} = $self->{plater}->object_menu; diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index 760484231..2feba3413 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -3,7 +3,8 @@ package Slic3r::GUI::Projector; use strict; use warnings; -use Wx qw(:dialog :id :misc :sizer :systemsettings :bitmap :button :icon wxTheApp); +use File::Basename qw(basename dirname); +use Wx qw(:dialog :id :misc :sizer :systemsettings :bitmap :button :icon :filedialog wxTheApp); use Wx::Event qw(EVT_BUTTON EVT_CLOSE EVT_TEXT_ENTER EVT_SPINCTRL EVT_SLIDER); use base qw(Wx::Dialog Class::Accessor); use utf8; @@ -378,10 +379,23 @@ sub new { { # should be wxCLOSE but it crashes on Linux, maybe it's a Wx bug - my $buttons = $self->CreateStdDialogButtonSizer(wxOK); - EVT_BUTTON($self, wxID_OK, sub { - $self->_close; - }); + my $buttons = Wx::BoxSizer->new(wxHORIZONTAL); + { + my $btn = Wx::Button->new($self, -1, "Export SVG…"); + EVT_BUTTON($self, $btn, sub { + $self->_export_svg; + }); + $buttons->Add($btn, 0); + } + $buttons->AddStretchSpacer(1); + { + my $btn = Wx::Button->new($self, -1, "Close"); + $btn->SetDefault; + EVT_BUTTON($self, $btn, sub { + $self->_close; + }); + $buttons->Add($btn, 0); + } $sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); } EVT_CLOSE($self, sub { @@ -457,6 +471,27 @@ sub _update_buttons { $self->Layout; } +sub _export_svg { + my ($self) = @_; + + my $output_file = 'print.svg'; + my $dlg = Wx::FileDialog->new( + $self, + 'Save SVG file as:', + wxTheApp->output_path(dirname($output_file)), + basename($output_file), + &Slic3r::GUI::FILE_WILDCARDS->{svg}, + wxFD_SAVE | wxFD_OVERWRITE_PROMPT, + ); + if ($dlg->ShowModal != wxID_OK) { + $dlg->Destroy; + return; + } + $output_file = Slic3r::decode_path($dlg->GetPath); + + $self->controller->_print->write_svg($output_file); +} + sub _set_status { my ($self, $status) = @_; $self->{status_text}->SetLabel($status // ''); diff --git a/lib/Slic3r/GUI/SLAPrintOptions.pm b/lib/Slic3r/GUI/SLAPrintOptions.pm new file mode 100644 index 000000000..7b3348823 --- /dev/null +++ b/lib/Slic3r/GUI/SLAPrintOptions.pm @@ -0,0 +1,118 @@ +package Slic3r::GUI::SLAPrintOptions; +use Wx qw(:dialog :id :misc :sizer :systemsettings wxTheApp); +use Wx::Event qw(EVT_BUTTON EVT_TEXT_ENTER); +use base qw(Wx::Dialog Class::Accessor); + +__PACKAGE__->mk_accessors(qw(config)); + +sub new { + my ($class, $parent) = @_; + my $self = $class->SUPER::new($parent, -1, "SLA/DLP Print", wxDefaultPosition, wxDefaultSize); + + $self->config(Slic3r::Config::SLAPrint->new); + $self->config->apply_dynamic(wxTheApp->{mainframe}->config); + + my $sizer = Wx::BoxSizer->new(wxVERTICAL); + my $new_optgroup = sub { + my ($title) = @_; + + my $optgroup = Slic3r::GUI::ConfigOptionsGroup->new( + parent => $self, + title => $title, + config => $self->config, + label_width => 200, + ); + $sizer->Add($optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + return $optgroup; + }; + { + my $optgroup = $new_optgroup->('Layers'); + $optgroup->append_single_option_line('layer_height'); + $optgroup->append_single_option_line('first_layer_height'); + } + { + my $optgroup = $new_optgroup->('Infill'); + $optgroup->append_single_option_line('fill_density'); + $optgroup->append_single_option_line('fill_pattern'); + { + my $line = $optgroup->create_single_option_line('perimeter_extrusion_width'); + $line->label('Shell thickness'); + my $opt = $line->get_options->[0]; + $opt->sidetext('mm'); + $opt->tooltip('Thickness of the external shell (both horizontal and vertical).'); + $optgroup->append_line($line); + } + { + my $line = $optgroup->create_single_option_line('infill_extrusion_width'); + $line->label('Infill thickness'); + my $opt = $line->get_options->[0]; + $opt->sidetext('mm'); + $opt->tooltip('Thickness of the infill lines.'); + $optgroup->append_line($line); + } + $optgroup->append_single_option_line('fill_angle'); + } + { + my $optgroup = $new_optgroup->('Raft'); + $optgroup->append_single_option_line('raft_layers'); + $optgroup->append_single_option_line('raft_offset'); + } + { + my $optgroup = $new_optgroup->('Support Material'); + $optgroup->append_single_option_line('support_material'); + { + my $line = $optgroup->create_single_option_line('support_material_spacing'); + $line->label('Pillars spacing'); + my $opt = $line->get_options->[0]; + $opt->tooltip('Max spacing between support material pillars.'); + $optgroup->append_line($line); + } + { + my $line = $optgroup->create_single_option_line('support_material_extrusion_width'); + $line->label('Pillars diameter'); + my $opt = $line->get_options->[0]; + $opt->sidetext('mm'); + $opt->tooltip('Diameter of the cylindrical support pillars.'); + $optgroup->append_line($line); + } + } + + + my $buttons = $self->CreateStdDialogButtonSizer(wxOK | wxCANCEL); + EVT_BUTTON($self, wxID_OK, sub { $self->_accept }); + $sizer->Add($buttons, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + + $self->SetSizer($sizer); + $sizer->SetSizeHints($self); + + return $self; +} + +sub _accept { + my $self = shift; + + # validate config + eval { + die "Invalid shell thickness (must be greater than 0).\n" + if $self->config->fill_density < 100 && $self->config->perimeter_extrusion_width == 0; + die "Invalid infill thickness (must be greater than 0).\n" + if $self->config->fill_density < 100 && $self->config->infill_extrusion_width == 0; + }; + if ($@) { + Slic3r::GUI::show_error($self, $@); + return; + } + + wxTheApp->{mainframe}->load_config($self->config->dynamic); + + $self->EndModal(wxID_OK); + $self->Close; # needed on Linux + + my $projector = Slic3r::GUI::Projector->new($self->GetParent); + + # this double invocation is needed for properly hiding the MainFrame + $projector->Show; + $projector->ShowModal; +} + +1; diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/xs/src/libslic3r/Fill/FillRectilinear.cpp index 0a08e78a7..8fa311471 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.cpp @@ -78,7 +78,7 @@ void FillRectilinear::_fill_surface_single( } size_t n_polylines_out_old = polylines_out->size(); - + // connect lines if (!this->dont_connect && !polylines.empty()) { // prevent calling leftmost_point() on empty collections // offset the expolygon by max(min_spacing/2, extra) diff --git a/xs/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp index ba4576a21..5269a4c11 100644 --- a/xs/src/libslic3r/Point.cpp +++ b/xs/src/libslic3r/Point.cpp @@ -311,6 +311,7 @@ _align_to_grid(const coord_t coord, const coord_t spacing) { // Current C++ standard defines the result of integer division to be rounded to zero, // for both positive and negative numbers. Here we want to round down for negative // numbers as well. + assert(spacing > 0); coord_t aligned = (coord < 0) ? ((coord - spacing + 1) / spacing) * spacing : (coord / spacing) * spacing; diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index d57cf71bb..9704cff52 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -55,11 +55,12 @@ SLAPrint::slice() } // generate infill - if (this->config.fill_density < 100) { + const float infill_spacing = this->config.get_abs_value("infill_extrusion_width", this->config.layer_height.value); + if (this->config.fill_density < 100 && infill_spacing > 0) { std::auto_ptr fill(Fill::new_from_type(this->config.fill_pattern.value)); fill->bounding_box.merge(Point::new_scale(bb.min.x, bb.min.y)); fill->bounding_box.merge(Point::new_scale(bb.max.x, bb.max.y)); - fill->spacing = this->config.get_abs_value("infill_extrusion_width", this->config.layer_height.value); + fill->spacing = infill_spacing; fill->angle = Geometry::deg2rad(this->config.fill_angle.value); fill->density = this->config.fill_density.value/100; @@ -189,7 +190,6 @@ SLAPrint::_infill_layer(size_t i, const Fill* _fill) ExtrusionPath templ(erInternalInfill); templ.width = fill->spacing; - const ExPolygons internal_ex = intersection_ex(infill, internal); for (ExPolygons::const_iterator it = internal_ex.begin(); it != internal_ex.end(); ++it) { Polylines polylines = fill->fill_surface(Surface(stInternal, *it)); diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 056f1e1fd..ab4bdf025 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -53,6 +53,8 @@ %code{% RETVAL = new PrintRegionConfig (); %}; static StaticPrintConfig* new_FullPrintConfig() %code{% RETVAL = new FullPrintConfig (); %}; + static StaticPrintConfig* new_SLAPrintConfig() + %code{% RETVAL = new SLAPrintConfig (); %}; ~StaticPrintConfig(); bool has(t_config_option_key opt_key); SV* as_hash() @@ -88,6 +90,8 @@ double min_object_distance(); %name{_load} void load(std::string file); %name{_save} void save(std::string file); + DynamicPrintConfig* dynamic() + %code{% RETVAL = new DynamicPrintConfig (); RETVAL->apply(*THIS, true); %}; }; %package{Slic3r::Config}; diff --git a/xs/xsp/SLAPrint.xsp b/xs/xsp/SLAPrint.xsp index 559d2973d..98823ff06 100644 --- a/xs/xsp/SLAPrint.xsp +++ b/xs/xsp/SLAPrint.xsp @@ -28,6 +28,7 @@ %code%{ RETVAL = &THIS->layers[i].infill; %}; bool layer_solid(size_t i) %code%{ RETVAL = THIS->layers[i].solid; %}; + void write_svg(std::string file); %{ From 3232350ffd558153a9579ea6ba81fd99cbda0854 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 1 Dec 2016 16:57:47 +0100 Subject: [PATCH 136/225] Fix infill projection --- lib/Slic3r/GUI/Projector.pm | 5 +++-- xs/src/libslic3r/Fill/FillRectilinear.hpp | 5 ++++- xs/xsp/SLAPrint.xsp | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/GUI/Projector.pm b/lib/Slic3r/GUI/Projector.pm index 2feba3413..b6d1cb5a6 100644 --- a/lib/Slic3r/GUI/Projector.pm +++ b/lib/Slic3r/GUI/Projector.pm @@ -914,9 +914,10 @@ sub _repaint { if ($self->print->layer_solid($self->layer_num)) { $self->_paint_expolygon($_, $dc) for @{$self->print->layer_slices($self->layer_num)}; } else { + # perimeters first, because their "hole" is painted black $self->_paint_expolygon($_, $dc) for - @{$self->print->layer_solid_infill($self->layer_num)}, - @{$self->print->layer_perimeters($self->layer_num)}; + @{$self->print->layer_perimeters($self->layer_num)}, + @{$self->print->layer_solid_infill($self->layer_num)}; $self->_paint_expolygon($_, $dc) for @{union_ex($self->print->layer_infill($self->layer_num)->grow)}; diff --git a/xs/src/libslic3r/Fill/FillRectilinear.hpp b/xs/src/libslic3r/Fill/FillRectilinear.hpp index f6c633e3a..d2bd8d777 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.hpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.hpp @@ -42,6 +42,7 @@ protected: class FillLine : public FillRectilinear { public: + virtual Fill* clone() const { return new FillLine(*this); }; virtual ~FillLine() {} protected: @@ -62,6 +63,7 @@ protected: class FillGrid : public FillRectilinear { public: + virtual Fill* clone() const { return new FillGrid(*this); }; virtual ~FillGrid() {} protected: @@ -74,11 +76,12 @@ protected: class FillAlignedRectilinear : public FillRectilinear { public: + virtual Fill* clone() const { return new FillAlignedRectilinear(*this); }; virtual ~FillAlignedRectilinear() {}; protected: // Keep the angle constant in all layers. - virtual float _layer_angle(size_t idx) const { printf("ALIGNED\n"); return 0.f; }; + virtual float _layer_angle(size_t idx) const { return 0.f; }; }; }; // namespace Slic3r diff --git a/xs/xsp/SLAPrint.xsp b/xs/xsp/SLAPrint.xsp index 98823ff06..6526f713c 100644 --- a/xs/xsp/SLAPrint.xsp +++ b/xs/xsp/SLAPrint.xsp @@ -21,7 +21,7 @@ ExPolygons layer_slices(size_t i) %code%{ RETVAL = THIS->layers[i].slices; %}; ExPolygons layer_solid_infill(size_t i) - %code%{ RETVAL = THIS->layers[i].solid_infill.expolygons; %}; + %code%{ RETVAL = THIS->layers[i].solid_infill; %}; ExPolygons layer_perimeters(size_t i) %code%{ RETVAL = THIS->layers[i].perimeters; %}; Ref layer_infill(size_t i) From eb86fb6c652ffdc366c6ff66563e12d62bb40dd6 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 1 Dec 2016 17:00:04 +0100 Subject: [PATCH 137/225] Fix concurrency when opening DLP projector while background slicing was performed --- lib/Slic3r/GUI/MainFrame.pm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index c2455874c..970c754f7 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -253,7 +253,9 @@ sub _init_menubar { $plater->export_amf; }, undef, 'brick_go.png'); $self->_append_menu_item($self->{plater_menu}, "Open DLP Projector…\tCtrl+L", 'Open projector window for DLP printing', sub { + $plater->pause_background_process; Slic3r::GUI::SLAPrintOptions->new($self)->ShowModal; + $plater->resume_background_process; }, undef, 'film.png'); $self->{object_menu} = $self->{plater}->object_menu; From 6a1a3fc94c4a07517f82a2a9cacbad5dec03adf5 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 1 Dec 2016 17:30:06 +0100 Subject: [PATCH 138/225] SLAPrintConfig needs other defaults --- xs/src/libslic3r/PrintConfig.hpp | 8 ++++++++ xs/src/libslic3r/SLAPrint.cpp | 7 +++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 37e376114..4e7f14e16 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -549,6 +549,14 @@ class SLAPrintConfig SLAPrintConfig() : StaticPrintConfig() { this->set_defaults(); + + // override some defaults + this->fill_density.value = 100; + this->fill_pattern.value = ipGrid; + this->infill_extrusion_width.value = 0.5; + this->infill_extrusion_width.percent = false; + this->perimeter_extrusion_width.value = 1; + this->perimeter_extrusion_width.percent = false; }; virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index 9704cff52..3fb05e442 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -55,12 +55,11 @@ SLAPrint::slice() } // generate infill - const float infill_spacing = this->config.get_abs_value("infill_extrusion_width", this->config.layer_height.value); - if (this->config.fill_density < 100 && infill_spacing > 0) { + if (this->config.fill_density < 100) { std::auto_ptr fill(Fill::new_from_type(this->config.fill_pattern.value)); fill->bounding_box.merge(Point::new_scale(bb.min.x, bb.min.y)); fill->bounding_box.merge(Point::new_scale(bb.max.x, bb.max.y)); - fill->spacing = infill_spacing; + fill->spacing = this->config.get_abs_value("infill_extrusion_width", this->config.layer_height.value); fill->angle = Geometry::deg2rad(this->config.fill_angle.value); fill->density = this->config.fill_density.value/100; @@ -156,7 +155,7 @@ SLAPrint::_infill_layer(size_t i, const Fill* _fill) Layer &layer = this->layers[i]; const float shell_thickness = this->config.get_abs_value("perimeter_extrusion_width", this->config.layer_height.value); - + // In order to detect what regions of this layer need to be solid, // perform an intersection with layers within the requested shell thickness. Polygons internal = layer.slices; From c931addda3bd0806e36b3f6f27c955e03955ddd1 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 1 Dec 2016 18:40:11 +0100 Subject: [PATCH 139/225] SLAPrint::DESTROY --- lib/Slic3r.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 5161a86b2..531ab7304 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -239,6 +239,7 @@ sub thread_cleanup { *Slic3r::Print::DESTROY = sub {}; *Slic3r::Print::Object::DESTROY = sub {}; *Slic3r::Print::Region::DESTROY = sub {}; + *Slic3r::SLAPrint::DESTROY = sub {}; *Slic3r::Surface::DESTROY = sub {}; *Slic3r::Surface::Collection::DESTROY = sub {}; *Slic3r::TriangleMesh::DESTROY = sub {}; From f16aaeafbad4d6024a6d4f441a3a7340929f344d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 1 Dec 2016 19:21:36 +0100 Subject: [PATCH 140/225] Fixed minor compilation warnings --- src/CMakeLists.txt | 7 ++- xs/src/admesh/stlinit.c | 3 ++ xs/src/libslic3r/ClipperUtils.cpp | 3 +- xs/src/libslic3r/ClipperUtils.hpp | 2 +- xs/src/libslic3r/Fill/FillRectilinear2.cpp | 52 +++++++++++----------- xs/src/libslic3r/Geometry.cpp | 2 +- xs/src/libslic3r/Geometry.hpp | 2 +- xs/src/libslic3r/Line.hpp | 2 +- xs/src/libslic3r/Model.cpp | 2 +- xs/src/libslic3r/MotionPlanner.cpp | 8 ++-- xs/src/libslic3r/MotionPlanner.hpp | 4 +- xs/src/libslic3r/PerimeterGenerator.cpp | 27 ++++++----- xs/src/libslic3r/PolylineCollection.cpp | 2 +- xs/src/libslic3r/Print.cpp | 1 - xs/src/libslic3r/PrintConfig.cpp | 4 -- xs/src/libslic3r/SLAPrint.cpp | 4 +- 16 files changed, 61 insertions(+), 64 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e656d7aab..d326be030 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,16 +2,15 @@ cmake_minimum_required (VERSION 2.8) project (slic3r) # only on newer GCCs: -ftemplate-backtrace-limit=0 -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSLIC3R_DEBUG") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -DM_PI=3.14159265358979323846 -D_GLIBCXX_USE_C99 -DHAS_BOOL -DNOGDI -DBOOST_ASIO_DISABLE_KQUEUE") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -DSLIC3R_DEBUG") -set(workaround "") if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.7.0) if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7.3) - set(workaround "-fno-inline-small-functions") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-inline-small-functions") endif(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.7.3) endif(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.7.0) -set(CMAKE_CXX_FLAGS "-DM_PI=3.14159265358979323846 -D_GLIBCXX_USE_C99 -DHAS_BOOL -DNOGDI -DBOOST_ASIO_DISABLE_KQUEUE ${workaround}") set(CMAKE_INCLUDE_CURRENT_DIR ON) IF(CMAKE_HOST_APPLE) diff --git a/xs/src/admesh/stlinit.c b/xs/src/admesh/stlinit.c index 12b3fa772..d7a436665 100644 --- a/xs/src/admesh/stlinit.c +++ b/xs/src/admesh/stlinit.c @@ -20,6 +20,7 @@ * https://github.com/admesh/admesh/issues */ +#include #include #include #include @@ -53,6 +54,8 @@ stl_initialize(stl_file *stl) { stl->stats.number_of_parts = 0; stl->stats.original_num_facets = 0; stl->stats.number_of_facets = 0; + stl->stats.bounding_diameter = 0; + stl->stats.shortest_edge = FLT_MAX; stl->stats.facets_malloced = 0; stl->stats.volume = -1.0; diff --git a/xs/src/libslic3r/ClipperUtils.cpp b/xs/src/libslic3r/ClipperUtils.cpp index 6ae6b8426..98ca0e272 100644 --- a/xs/src/libslic3r/ClipperUtils.cpp +++ b/xs/src/libslic3r/ClipperUtils.cpp @@ -441,7 +441,8 @@ union_pt_chained(const Polygons &subject, bool safety_offset_) return retval; } -static void traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval) +void +traverse_pt(ClipperLib::PolyNodes &nodes, Polygons* retval) { /* use a nearest neighbor search to order these children TODO: supply start_near to chained_path() too? */ diff --git a/xs/src/libslic3r/ClipperUtils.hpp b/xs/src/libslic3r/ClipperUtils.hpp index 8ed333753..9e4e5e89a 100644 --- a/xs/src/libslic3r/ClipperUtils.hpp +++ b/xs/src/libslic3r/ClipperUtils.hpp @@ -212,7 +212,7 @@ union_ex(const Slic3r::Surfaces &subject, bool safety_offset_ = false) ClipperLib::PolyTree union_pt(const Slic3r::Polygons &subject, bool safety_offset_ = false); Slic3r::Polygons union_pt_chained(const Slic3r::Polygons &subject, bool safety_offset_ = false); -static void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval); +void traverse_pt(ClipperLib::PolyNodes &nodes, Slic3r::Polygons* retval); /* OTHER */ Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false); diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.cpp b/xs/src/libslic3r/Fill/FillRectilinear2.cpp index 3ec5e0bac..653bb789e 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear2.cpp @@ -93,7 +93,7 @@ static inline bool is_ccw(const Polygon &poly) return true; // 1) Find the lowest lexicographical point. - int imin = 0; + size_t imin = 0; for (size_t i = 1; i < poly.points.size(); ++ i) { const Point &pmin = poly.points[imin]; const Point &p = poly.points[i]; @@ -369,7 +369,7 @@ public: myassert(aoffset1 < 0); myassert(aoffset2 < 0); myassert(aoffset2 < aoffset1); - bool sticks_removed = remove_sticks(polygons_src); +// bool sticks_removed = remove_sticks(polygons_src); // if (sticks_removed) printf("Sticks removed!\n"); polygons_outer = offset(polygons_src, aoffset1, CLIPPER_OFFSET_SCALE, @@ -411,9 +411,9 @@ public: #ifdef SLIC3R_DEBUG void export_to_svg(Slic3r::SVG &svg) { - svg.draw_outline(polygons_src, "black"); - svg.draw_outline(polygons_outer, "green"); - svg.draw_outline(polygons_inner, "brown"); + //svg.draw_outline(polygons_src, "black"); + //svg.draw_outline(polygons_outer, "green"); + //svg.draw_outline(polygons_inner, "brown"); } #endif /* SLIC3R_DEBUG */ @@ -553,7 +553,7 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical const std::vector &segs, size_t iVerticalLine, size_t iIntersection, - size_t iIntersectionOther, + int iIntersectionOther, bool dir_is_next) { // This routine will propose a connecting line even if the connecting perimeter segment intersects @@ -567,7 +567,7 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical const SegmentIntersection &itsct_other = il_other.intersections[iIntersectionOther]; myassert(itsct_other.is_inner()); myassert(iIntersectionOther > 0); - myassert(iIntersectionOther + 1 < il_other.intersections.size()); + myassert(iIntersectionOther + 1 < (int)il_other.intersections.size()); // Is iIntersectionOther at the boundary of a vertical segment? const SegmentIntersection &itsct_other2 = il_other.intersections[itsct_other.is_low() ? iIntersectionOther - 1 : iIntersectionOther + 1]; if (itsct_other2.is_inner()) @@ -892,11 +892,11 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angle #ifdef SLIC3R_DEBUG static int iRun = 0; BoundingBox bbox_svg(to_points(poly_with_offset.polygons_outer)); - ::Slic3r::SVG svg(debug_out_path("FillRectilinear2-%d.svg", iRun), bbox_svg); // , scale_(1.)); - poly_with_offset.export_to_svg(svg); + //::Slic3r::SVG svg(debug_out_path("FillRectilinear2-%d.svg", iRun), bbox_svg); // , scale_(1.)); + //poly_with_offset.export_to_svg(svg); { - ::Slic3r::SVG svg(debug_out_path("FillRectilinear2-initial-%d.svg", iRun), bbox_svg); // , scale_(1.)); - poly_with_offset.export_to_svg(svg); + //::Slic3r::SVG svg(debug_out_path("FillRectilinear2-initial-%d.svg", iRun), bbox_svg); // , scale_(1.)); + //poly_with_offset.export_to_svg(svg); } iRun ++; #endif /* SLIC3R_DEBUG */ @@ -934,8 +934,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angle if (il > ir) // No vertical line intersects this segment. continue; - myassert(il >= 0 && il < segs.size()); - myassert(ir >= 0 && ir < segs.size()); + myassert(il >= 0 && il < (int)segs.size()); + myassert(ir >= 0 && ir < (int)segs.size()); for (int i = il; i <= ir; ++ i) { coord_t this_x = segs[i].pos; assert(this_x == i * line_spacing + x0); @@ -1160,16 +1160,16 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angle size_t j = i + 1; for (; j < sil.intersections.size() && sil.intersections[j].is_inner(); ++ j) ; if (i + 1 == j) { - svg.draw(Line(Point(sil.pos, sil.intersections[i].pos()), Point(sil.pos, sil.intersections[j].pos())), "blue"); + //svg.draw(Line(Point(sil.pos, sil.intersections[i].pos()), Point(sil.pos, sil.intersections[j].pos())), "blue"); } else { - svg.draw(Line(Point(sil.pos, sil.intersections[i].pos()), Point(sil.pos, sil.intersections[i+1].pos())), "green"); - svg.draw(Line(Point(sil.pos, sil.intersections[i+1].pos()), Point(sil.pos, sil.intersections[j-1].pos())), (j - i + 1 > 4) ? "yellow" : "magenta"); - svg.draw(Line(Point(sil.pos, sil.intersections[j-1].pos()), Point(sil.pos, sil.intersections[j].pos())), "green"); + //svg.draw(Line(Point(sil.pos, sil.intersections[i].pos()), Point(sil.pos, sil.intersections[i+1].pos())), "green"); + //svg.draw(Line(Point(sil.pos, sil.intersections[i+1].pos()), Point(sil.pos, sil.intersections[j-1].pos())), (j - i + 1 > 4) ? "yellow" : "magenta"); + //svg.draw(Line(Point(sil.pos, sil.intersections[j-1].pos()), Point(sil.pos, sil.intersections[j].pos())), "green"); } i = j + 1; } } - svg.Close(); + //svg.Close(); #endif /* SLIC3R_DEBUG */ // For each outer only chords, measure their maximum distance to the bow of the outer contour. @@ -1314,8 +1314,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angle int iSegAbove = -1; int iSegBelow = -1; { - SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ? - SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW; +// SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ? +// SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW; // Does the perimeter intersect the current vertical line above intrsctn? for (size_t i = i_intersection + 1; i + 1 < seg.intersections.size(); ++ i) // if (seg.intersections[i].iContour == intrsctn->iContour && seg.intersections[i].type == type_crossing) { @@ -1522,15 +1522,15 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angle #ifdef SLIC3R_DEBUG { { - ::Slic3r::SVG svg(debug_out_path("FillRectilinear2-final-%03d.svg", iRun), bbox_svg); // , scale_(1.)); - poly_with_offset.export_to_svg(svg); - for (size_t i = n_polylines_out_initial; i < polylines_out.size(); ++ i) - svg.draw(polylines_out[i].lines(), "black"); + //::Slic3r::SVG svg(debug_out_path("FillRectilinear2-final-%03d.svg", iRun), bbox_svg); // , scale_(1.)); + //poly_with_offset.export_to_svg(svg); + //for (size_t i = n_polylines_out_initial; i < polylines_out.size(); ++ i) + //svg.draw(polylines_out[i].lines(), "black"); } // Paint a picture per polyline. This makes it easier to discover the order of the polylines and their overlap. for (size_t i_polyline = n_polylines_out_initial; i_polyline < polylines_out.size(); ++ i_polyline) { - ::Slic3r::SVG svg(debug_out_path("FillRectilinear2-final-%03d-%03d.svg", iRun, i_polyline), bbox_svg); // , scale_(1.)); - svg.draw(polylines_out[i_polyline].lines(), "black"); + //::Slic3r::SVG svg(debug_out_path("FillRectilinear2-final-%03d-%03d.svg", iRun, i_polyline), bbox_svg); // , scale_(1.)); + //svg.draw(polylines_out[i_polyline].lines(), "black"); } } #endif /* SLIC3R_DEBUG */ diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp index fec7dea89..629129b1e 100644 --- a/xs/src/libslic3r/Geometry.cpp +++ b/xs/src/libslic3r/Geometry.cpp @@ -555,7 +555,7 @@ MedialAxis::build(ThickPolylines* polylines) char path[2048]; static int iRun = 0; sprintf(path, "out/MedialAxis-%d.svg", iRun ++); - dump_voronoi_to_svg(this->lines, this->vd, polylines, path); + //dump_voronoi_to_svg(this->lines, this->vd, polylines, path); printf("Thick lines: "); diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp index 4cb2be1a3..d25901529 100644 --- a/xs/src/libslic3r/Geometry.hpp +++ b/xs/src/libslic3r/Geometry.hpp @@ -38,7 +38,7 @@ class MedialAxis { double max_width; double min_width; MedialAxis(double _max_width, double _min_width, const ExPolygon* _expolygon = NULL) - : max_width(_max_width), min_width(_min_width), expolygon(_expolygon) {}; + : expolygon(_expolygon), max_width(_max_width), min_width(_min_width) {}; void build(ThickPolylines* polylines); void build(Polylines* polylines); diff --git a/xs/src/libslic3r/Line.hpp b/xs/src/libslic3r/Line.hpp index 42e811449..ff8b086e1 100644 --- a/xs/src/libslic3r/Line.hpp +++ b/xs/src/libslic3r/Line.hpp @@ -53,7 +53,7 @@ class ThickLine : public Line coordf_t a_width, b_width; ThickLine() : a_width(0), b_width(0) {}; - ThickLine(Point _a, Point _b) : a_width(0), b_width(0), Line(_a, _b) {}; + ThickLine(Point _a, Point _b) : Line(_a, _b), a_width(0), b_width(0) {}; }; class Linef diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index b6328f9c9..73adc56c9 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -346,7 +346,7 @@ ModelMaterial::apply(const t_model_material_attributes &attributes) ModelObject::ModelObject(Model *model) - : model(model), _bounding_box_valid(false) + : _bounding_box_valid(false), model(model) {} ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volumes) diff --git a/xs/src/libslic3r/MotionPlanner.cpp b/xs/src/libslic3r/MotionPlanner.cpp index 6c6d7336d..1f604d5fa 100644 --- a/xs/src/libslic3r/MotionPlanner.cpp +++ b/xs/src/libslic3r/MotionPlanner.cpp @@ -313,10 +313,10 @@ MotionPlannerEnv::nearest_env_point(const Point &from, const Point &to) const } void -MotionPlannerGraph::add_edge(size_t from, size_t to, double weight) +MotionPlannerGraph::add_edge(node_t from, node_t to, double weight) { // extend adjacency list until this start node - if (this->adjacency_list.size() < from+1) + if (this->adjacency_list.size() < (size_t)from+1) this->adjacency_list.resize(from+1); this->adjacency_list[from].push_back(neighbor(to, weight)); @@ -334,7 +334,7 @@ MotionPlannerGraph::find_node(const Point &point) const } Polyline -MotionPlannerGraph::shortest_path(size_t from, size_t to) +MotionPlannerGraph::shortest_path(node_t from, node_t to) { // this prevents a crash in case for some reason we got here with an empty adjacency list if (this->adjacency_list.empty()) return Polyline(); @@ -345,7 +345,7 @@ MotionPlannerGraph::shortest_path(size_t from, size_t to) std::vector previous; { // number of nodes - size_t n = this->adjacency_list.size(); + const int n = this->adjacency_list.size(); // initialize dist and previous dist.clear(); diff --git a/xs/src/libslic3r/MotionPlanner.hpp b/xs/src/libslic3r/MotionPlanner.hpp index b6251ff21..1fbdb4123 100644 --- a/xs/src/libslic3r/MotionPlanner.hpp +++ b/xs/src/libslic3r/MotionPlanner.hpp @@ -47,9 +47,9 @@ class MotionPlannerGraph public: Points nodes; //std::map, double> edges; - void add_edge(size_t from, size_t to, double weight); + void add_edge(node_t from, node_t to, double weight); size_t find_node(const Point &point) const; - Polyline shortest_path(size_t from, size_t to); + Polyline shortest_path(node_t from, node_t to); }; class MotionPlanner diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index 89d0f06c5..f65b0c555 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -54,8 +54,7 @@ PerimeterGenerator::process() for (Surfaces::const_iterator surface = this->slices->surfaces.begin(); surface != this->slices->surfaces.end(); ++surface) { // detect how many perimeters must be generated for this island - signed short loop_number = this->config->perimeters + surface->extra_perimeters; - loop_number--; // 0-indexed loops + const size_t loop_number = this->config->perimeters + surface->extra_perimeters -1; // 0-indexed loops Polygons gaps; @@ -67,7 +66,7 @@ PerimeterGenerator::process() ThickPolylines thin_walls; // we loop one time more than needed in order to find gaps after the last perimeter was applied - for (signed short i = 0; i <= loop_number+1; ++i) { // outer loop is 0 + for (size_t i = 0; i <= loop_number+1; ++i) { // outer loop is 0 Polygons offsets; if (i == 0) { // the minimum thickness of a single loop is: @@ -170,16 +169,16 @@ PerimeterGenerator::process() } // nest loops: holes first - for (signed short d = 0; d <= loop_number; ++d) { + for (size_t d = 0; d <= loop_number; ++d) { PerimeterGeneratorLoops &holes_d = holes[d]; // loop through all holes having depth == d - for (signed short i = 0; i < holes_d.size(); ++i) { + for (size_t i = 0; i < holes_d.size(); ++i) { const PerimeterGeneratorLoop &loop = holes_d[i]; // find the hole loop that contains this one, if any - for (signed short t = d+1; t <= loop_number; ++t) { - for (signed short j = 0; j < holes[t].size(); ++j) { + for (size_t t = d+1; t <= loop_number; ++t) { + for (size_t j = 0; j < holes[t].size(); ++j) { PerimeterGeneratorLoop &candidate_parent = holes[t][j]; if (candidate_parent.polygon.contains(loop.polygon.first_point())) { candidate_parent.children.push_back(loop); @@ -191,8 +190,8 @@ PerimeterGenerator::process() } // if no hole contains this hole, find the contour loop that contains it - for (signed short t = loop_number; t >= 0; --t) { - for (signed short j = 0; j < contours[t].size(); ++j) { + for (size_t t = loop_number; t --> 0;) { + for (size_t j = 0; j < contours[t].size(); ++j) { PerimeterGeneratorLoop &candidate_parent = contours[t][j]; if (candidate_parent.polygon.contains(loop.polygon.first_point())) { candidate_parent.children.push_back(loop); @@ -207,16 +206,16 @@ PerimeterGenerator::process() } // nest contour loops - for (signed short d = loop_number; d >= 1; --d) { + for (size_t d = loop_number; d --> 0;) { PerimeterGeneratorLoops &contours_d = contours[d]; // loop through all contours having depth == d - for (signed short i = 0; i < contours_d.size(); ++i) { + for (size_t i = 0; i < contours_d.size(); ++i) { const PerimeterGeneratorLoop &loop = contours_d[i]; // find the contour loop that contains it - for (signed short t = d-1; t >= 0; --t) { - for (signed short j = 0; j < contours[t].size(); ++j) { + for (size_t t = d-1; t --> 0;) { + for (size_t j = 0; j < contours[t].size(); ++j) { PerimeterGeneratorLoop &candidate_parent = contours[t][j]; if (candidate_parent.polygon.contains(loop.polygon.first_point())) { candidate_parent.children.push_back(loop); @@ -465,7 +464,7 @@ PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRo double thickness_delta = fabs(line.a_width - line.b_width); if (thickness_delta > tolerance) { - const unsigned short segments = ceil(thickness_delta / tolerance); + const size_t segments = ceil(thickness_delta / tolerance); const coordf_t seg_len = line_len / segments; Points pp; std::vector width; diff --git a/xs/src/libslic3r/PolylineCollection.cpp b/xs/src/libslic3r/PolylineCollection.cpp index f92db0b03..756fa7a82 100644 --- a/xs/src/libslic3r/PolylineCollection.cpp +++ b/xs/src/libslic3r/PolylineCollection.cpp @@ -68,7 +68,7 @@ Polylines PolylineCollection::_chained_path_from( while (! endpoints.empty()) { // find nearest point int endpoint_index = nearest_point_index(endpoints, start_near, no_reverse); - assert(endpoint_index >= 0 && endpoint_index < endpoints.size() * 2); + assert(endpoint_index >= 0 && endpoint_index < (int)endpoints.size() * 2); #if SLIC3R_CPPVER > 11 if (move_from_src) { retval.push_back(std::move(src[endpoints[endpoint_index/2].idx])); diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index a4b9818ba..179d501b5 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -826,7 +826,6 @@ Print::auto_assign_extruders(ModelObject* model_object) const // only assign extruders if object has more than one volume if (model_object->volumes.size() < 2) return; - size_t extruders = this->config.nozzle_diameter.values.size(); for (ModelVolumePtrs::const_iterator v = model_object->volumes.begin(); v != model_object->volumes.end(); ++v) { if (!(*v)->material_id().empty()) { //FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned. diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 6ab205375..6771733ba 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -5,8 +5,6 @@ namespace Slic3r { PrintConfigDef::PrintConfigDef() { - t_optiondef_map &Options = this->options; - ConfigOptionDef* def; def = this->add("avoid_crossing_perimeters", coBool); @@ -1399,8 +1397,6 @@ PrintConfigBase::min_object_distance() const CLIConfigDef::CLIConfigDef() { - t_optiondef_map &Options = this->options; - ConfigOptionDef* def; def = this->add("export_obj", coBool); diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index 3fb05e442..e32d45d26 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -144,7 +144,7 @@ SLAPrint::slice() } // prepend total raft height to all sliced layers - for (int i = this->config.raft_layers; i < this->layers.size(); ++i) + for (size_t i = this->config.raft_layers; i < this->layers.size(); ++i) this->layers[i].print_z += first_lh + lh * (this->config.raft_layers-1); } } @@ -272,7 +272,7 @@ SLAPrint::write_svg(const std::string &outputfile) const } // don't print support material in raft layers - if (i >= this->config.raft_layers) { + if (i >= (size_t)this->config.raft_layers) { // look for support material pillars belonging to this layer for (std::vector::const_iterator it = this->sm_pillars.begin(); it != this->sm_pillars.end(); ++it) { if (!(it->top_layer >= i && it->bottom_layer <= i)) continue; From f43bb9eb75ffaedd7269ddaa5843271a31c61456 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 1 Dec 2016 19:47:44 +0100 Subject: [PATCH 141/225] More minor fixes --- xs/src/libslic3r/SLAPrint.cpp | 5 +++-- xs/src/libslic3r/SVG.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index e32d45d26..0f98b79ee 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -220,10 +220,11 @@ SLAPrint::write_svg(const std::string &outputfile) const for (size_t i = 0; i < this->layers.size(); ++i) { const Layer &layer = this->layers[i]; fprintf(f, - "\t\n", i, + "\t\n", + i, layer.print_z, layer.slice_z, - layer.print_z - (i == 0 ? 0 : this->layers[i-1].print_z) + layer.print_z - ((i == 0) ? 0. : this->layers[i-1].print_z) ); if (layer.solid) { diff --git a/xs/src/libslic3r/SVG.cpp b/xs/src/libslic3r/SVG.cpp index 444810044..5fb0e7d5f 100644 --- a/xs/src/libslic3r/SVG.cpp +++ b/xs/src/libslic3r/SVG.cpp @@ -20,7 +20,7 @@ SVG::SVG(const char* filename) } SVG::SVG(const char* filename, const BoundingBox &bbox) - : arrows(false), fill("grey"), stroke("black"), filename(filename), origin(bbox.min) + : arrows(false), fill("grey"), stroke("black"), origin(bbox.min), filename(filename) { this->f = fopen(filename, "w"); float w = COORD(bbox.max.x - bbox.min.x); From fde6e2e61deabd097a3bf565d4948077147b25ab Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 2 Dec 2016 15:07:49 +0100 Subject: [PATCH 142/225] Fix compilation --- xs/src/libslic3r/PerimeterGenerator.cpp | 26 ++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index f65b0c555..d2ed60aaf 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -54,7 +54,7 @@ PerimeterGenerator::process() for (Surfaces::const_iterator surface = this->slices->surfaces.begin(); surface != this->slices->surfaces.end(); ++surface) { // detect how many perimeters must be generated for this island - const size_t loop_number = this->config->perimeters + surface->extra_perimeters -1; // 0-indexed loops + const int loop_number = this->config->perimeters + surface->extra_perimeters -1; // 0-indexed loops Polygons gaps; @@ -66,7 +66,7 @@ PerimeterGenerator::process() ThickPolylines thin_walls; // we loop one time more than needed in order to find gaps after the last perimeter was applied - for (size_t i = 0; i <= loop_number+1; ++i) { // outer loop is 0 + for (int i = 0; i <= loop_number+1; ++i) { // outer loop is 0 Polygons offsets; if (i == 0) { // the minimum thickness of a single loop is: @@ -169,16 +169,16 @@ PerimeterGenerator::process() } // nest loops: holes first - for (size_t d = 0; d <= loop_number; ++d) { + for (int d = 0; d <= loop_number; ++d) { PerimeterGeneratorLoops &holes_d = holes[d]; // loop through all holes having depth == d - for (size_t i = 0; i < holes_d.size(); ++i) { + for (int i = 0; i < (int)holes_d.size(); ++i) { const PerimeterGeneratorLoop &loop = holes_d[i]; // find the hole loop that contains this one, if any - for (size_t t = d+1; t <= loop_number; ++t) { - for (size_t j = 0; j < holes[t].size(); ++j) { + for (int t = d+1; t <= loop_number; ++t) { + for (int j = 0; j < (int)holes[t].size(); ++j) { PerimeterGeneratorLoop &candidate_parent = holes[t][j]; if (candidate_parent.polygon.contains(loop.polygon.first_point())) { candidate_parent.children.push_back(loop); @@ -190,8 +190,8 @@ PerimeterGenerator::process() } // if no hole contains this hole, find the contour loop that contains it - for (size_t t = loop_number; t --> 0;) { - for (size_t j = 0; j < contours[t].size(); ++j) { + for (int t = loop_number; t >= 0; --t) { + for (int j = 0; j < (int)contours[t].size(); ++j) { PerimeterGeneratorLoop &candidate_parent = contours[t][j]; if (candidate_parent.polygon.contains(loop.polygon.first_point())) { candidate_parent.children.push_back(loop); @@ -206,16 +206,16 @@ PerimeterGenerator::process() } // nest contour loops - for (size_t d = loop_number; d --> 0;) { + for (int d = loop_number; d >= 1; --d) { PerimeterGeneratorLoops &contours_d = contours[d]; // loop through all contours having depth == d - for (size_t i = 0; i < contours_d.size(); ++i) { + for (int i = 0; i < (int)contours_d.size(); ++i) { const PerimeterGeneratorLoop &loop = contours_d[i]; // find the contour loop that contains it - for (size_t t = d-1; t --> 0;) { - for (size_t j = 0; j < contours[t].size(); ++j) { + for (int t = d-1; t >= 0; --t) { + for (int j = 0; j < contours[t].size(); ++j) { PerimeterGeneratorLoop &candidate_parent = contours[t][j]; if (candidate_parent.polygon.contains(loop.polygon.first_point())) { candidate_parent.children.push_back(loop); @@ -456,7 +456,7 @@ PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRo ExtrusionPath path(role); ThickLines lines = p->thicklines(); - for (size_t i = 0; i < lines.size(); ++i) { + for (int i = 0; i < (int)lines.size(); ++i) { const ThickLine& line = lines[i]; const coordf_t line_len = line.length(); From bf0dd34a78dbf77755a85cfc37e15605b2ddccd9 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 5 Dec 2016 22:43:55 +0000 Subject: [PATCH 143/225] Added make_sphere, generates a mesh with specified step angle and raidus rho. --- xs/src/libslic3r/TriangleMesh.cpp | 80 +++++++++++++++++++++++++++++++ xs/src/libslic3r/TriangleMesh.hpp | 2 + xs/xsp/TriangleMesh.xsp | 7 +++ 3 files changed, 89 insertions(+) diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index f7b26aa83..cefe9b042 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -1225,4 +1225,84 @@ TriangleMesh make_cylinder(double r, double h, double fa) { TriangleMesh mesh(vertices, facets); return mesh; } + +// Generates mesh for a sphere centered about the origin, using the generated angle +// to determine the granularity. +// Default angle is 1 degree. +TriangleMesh make_sphere(double rho, double fa) { + Pointf3s vertices; + std::vector facets; + + // Algorithm: + // Add points one-by-one to the sphere grid and form facets using relative coordinates. + // Sphere is composed effectively of a mesh of stacked circles. + + // adjust via rounding to get an even multiple for any provided angle. + double angle = (2*PI / floor(2*PI / fa)); + + // Ring to be scaled to generate the steps of the sphere + std::vector ring; + for (double i = 0; i < 2*PI; i+=angle) { + ring.push_back(i); + } + const size_t steps = ring.size(); + const double increment = (double)(1.0 / (double)steps); + + // special case: first ring connects to 0,0,0 + // insert and form facets. + vertices.push_back(Pointf3(0.0, 0.0, -rho)); + size_t id = vertices.size(); + for (size_t i = 0; i < ring.size(); i++) { + // Fixed scaling + const double z = -rho + increment*rho*2.0; + // radius of the circle for this step. + const double r = sqrt(abs(rho*rho - z*z)); + Pointf3 b(0, r, z); + b.rotate(ring[i], Pointf3(0,0,z)); + vertices.push_back(b); + if (i == 0) { + facets.push_back(Point3(1, 0, ring.size())); + } else { + facets.push_back(Point3(id, 0, id - 1)); + } + id++; + } + + // General case: insert and form facets for each step, joining it to the ring below it. + for (size_t s = 2; s < steps - 1; s++) { + const double z = -rho + increment*(double)s*2.0*rho; + const double r = sqrt(abs(rho*rho - z*z)); + + for (size_t i = 0; i < ring.size(); i++) { + Pointf3 b(0, r, z); + b.rotate(ring[i], Pointf3(0,0,z)); + vertices.push_back(b); + if (i == 0) { + // wrap around + facets.push_back(Point3(id + ring.size() - 1 , id, id - 1)); + facets.push_back(Point3(id, id - ring.size(), id - 1)); + } else { + facets.push_back(Point3(id , id - ring.size(), (id - 1) - ring.size())); + facets.push_back(Point3(id, id - 1 - ring.size() , id - 1)); + } + id++; + } + } + + + // special case: last ring connects to 0,0,rho*2.0 + // only form facets. + vertices.push_back(Pointf3(0.0, 0.0, rho)); + for (size_t i = 0; i < ring.size(); i++) { + if (i == 0) { + // third vertex is on the other side of the ring. + facets.push_back(Point3(id, id - ring.size(), id - 1)); + } else { + facets.push_back(Point3(id, id - ring.size() + i, id - ring.size() + (i - 1))); + } + } + id++; + TriangleMesh mesh(vertices, facets); + return mesh; +} } diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 586deb414..f9337f6aa 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -118,6 +118,8 @@ TriangleMesh make_cube(double x, double y, double z); // Generate a TriangleMesh of a cylinder TriangleMesh make_cylinder(double r, double h, double fa=(2*PI/360)); +TriangleMesh make_sphere(double rho, double fa=(2*PI/360)); + } #endif diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 8fdd3a5da..028bb586b 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -242,6 +242,13 @@ cylinder(double r, double h) OUTPUT: RETVAL +Clone +sphere(double rho) + CODE: + RETVAL = make_sphere(rho); + OUTPUT: + RETVAL + %} }; From fbc954ac1e4a5dde974eb672083677b77b574eda Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 5 Dec 2016 22:44:17 +0000 Subject: [PATCH 144/225] UI code to generate a generic sphere. --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 32 ++++++++++++++++++++- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 2 ++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm index 17a3ad613..3b96c0db9 100644 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -23,6 +23,7 @@ sub new { dim => [1, 1, 1], cyl_r => 1, cyl_h => 1, + cyl_rho => 1.0, }; $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); @@ -62,7 +63,7 @@ sub new { }, label_width => 100, ); - my @options = ("box", "cylinder"); + my @options = ("box", "cylinder", "sphere"); $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( @@ -113,6 +114,31 @@ sub new { type => 'f', default => '1', )); + + my $optgroup_sphere; + $optgroup_sphere = $self->{optgroup_sphere} = Slic3r::GUI::OptionsGroup->new( + parent => $self, + title => 'Add Cylinder...', + on_change => sub { + # Do validation + my ($opt_id) = @_; + if ($opt_id eq 'cyl_rho') { + if (!looks_like_number($optgroup_sphere->get_value($opt_id))) { + return 0; + } + } + $self->{object_parameters}->{$opt_id} = $optgroup_sphere->get_value($opt_id); + }, + label_width => 100, + ); + + $optgroup_sphere->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => "cyl_rho", + label => 'Rho', + type => 'f', + default => '1', + )); + EVT_COMBOBOX($self, 1, sub{ $self->{object_parameters}->{type} = $self->{type}->GetValue(); $self->_update_ui; @@ -122,6 +148,7 @@ sub new { $self->{sizer}->Add($self->{type}, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->{sizer}->Add($optgroup_box->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->{sizer}->Add($optgroup_cylinder->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->{sizer}->Add($optgroup_sphere->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->{sizer}->Add($button_sizer,0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->_update_ui; @@ -144,10 +171,13 @@ sub _update_ui { my ($self) = @_; $self->{sizer}->Hide($self->{optgroup_cylinder}->sizer); $self->{sizer}->Hide($self->{optgroup_box}->sizer); + $self->{sizer}->Hide($self->{optgroup_sphere}->sizer); if ($self->{type}->GetValue eq "box") { $self->{sizer}->Show($self->{optgroup_box}->sizer); } elsif ($self->{type}->GetValue eq "cylinder") { $self->{sizer}->Show($self->{optgroup_cylinder}->sizer); + } elsif ($self->{type}->GetValue eq "sphere") { + $self->{sizer}->Show($self->{optgroup_sphere}->sizer); } $self->{sizer}->Fit($self); $self->{sizer}->SetSizeHints($self); diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 579f3d5ff..f123e042b 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -350,6 +350,8 @@ sub on_btn_lambda { $mesh = Slic3r::TriangleMesh::cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]); } elsif ($type eq "cylinder") { $mesh = Slic3r::TriangleMesh::cylinder($params->{"cyl_r"}, $params->{"cyl_h"}); + } elsif ($type eq "sphere") { + $mesh = Slic3r::TriangleMesh::sphere($params->{"cyl_rho"}); } else { return; } From 6414c10e7e233750f21fefee4ad9502328f08768 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 5 Dec 2016 23:08:12 +0000 Subject: [PATCH 145/225] UI: fixed naming sphere is not a cylinder. --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 8 ++++---- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm index 3b96c0db9..cabaebb5b 100644 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -23,7 +23,7 @@ sub new { dim => [1, 1, 1], cyl_r => 1, cyl_h => 1, - cyl_rho => 1.0, + sph_rho => 1.0, }; $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); @@ -118,11 +118,11 @@ sub new { my $optgroup_sphere; $optgroup_sphere = $self->{optgroup_sphere} = Slic3r::GUI::OptionsGroup->new( parent => $self, - title => 'Add Cylinder...', + title => 'Add Sphere...', on_change => sub { # Do validation my ($opt_id) = @_; - if ($opt_id eq 'cyl_rho') { + if ($opt_id eq 'sph_rho') { if (!looks_like_number($optgroup_sphere->get_value($opt_id))) { return 0; } @@ -133,7 +133,7 @@ sub new { ); $optgroup_sphere->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => "cyl_rho", + opt_id => "sph_rho", label => 'Rho', type => 'f', default => '1', diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index f123e042b..8e2dcdeb7 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -351,7 +351,7 @@ sub on_btn_lambda { } elsif ($type eq "cylinder") { $mesh = Slic3r::TriangleMesh::cylinder($params->{"cyl_r"}, $params->{"cyl_h"}); } elsif ($type eq "sphere") { - $mesh = Slic3r::TriangleMesh::sphere($params->{"cyl_rho"}); + $mesh = Slic3r::TriangleMesh::sphere($params->{"sph_rho"}); } else { return; } From bbb84278d4baf11ca4efdae9cfafe728296717f6 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 5 Dec 2016 23:08:36 +0000 Subject: [PATCH 146/225] Added UI options to make a slab, defaulting to the model object's bounding box * 1.5 --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 38 ++++++++++++++++++++- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 4 +++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm index cabaebb5b..683b990bd 100644 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -24,6 +24,8 @@ sub new { cyl_r => 1, cyl_h => 1, sph_rho => 1.0, + slab_h => 1.0, + slab_z => 0.0, }; $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); @@ -63,7 +65,7 @@ sub new { }, label_width => 100, ); - my @options = ("box", "cylinder", "sphere"); + my @options = ("box", "slab", "cylinder", "sphere"); $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( @@ -139,6 +141,36 @@ sub new { default => '1', )); + my $optgroup_slab; + $optgroup_slab = $self->{optgroup_slab} = Slic3r::GUI::OptionsGroup->new( + parent => $self, + title => 'Add Slab...', + on_change => sub { + # Do validation + my ($opt_id) = @_; + if ($opt_id eq 'slab_z' || $opt_id eq 'slab_h') { + if (!looks_like_number($optgroup_slab->get_value($opt_id))) { + return 0; + } + } + $self->{object_parameters}->{$opt_id} = $optgroup_slab->get_value($opt_id); + }, + label_width => 100, + ); + $optgroup_slab->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => "slab_h", + label => 'H', + type => 'f', + default => '1', + )); + $optgroup_slab->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => "slab_z", + label => 'Initial Z', + type => 'f', + default => '0', + )); + + EVT_COMBOBOX($self, 1, sub{ $self->{object_parameters}->{type} = $self->{type}->GetValue(); $self->_update_ui; @@ -149,6 +181,7 @@ sub new { $self->{sizer}->Add($optgroup_box->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->{sizer}->Add($optgroup_cylinder->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->{sizer}->Add($optgroup_sphere->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->{sizer}->Add($optgroup_slab->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->{sizer}->Add($button_sizer,0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->_update_ui; @@ -170,12 +203,15 @@ sub ObjectParameter { sub _update_ui { my ($self) = @_; $self->{sizer}->Hide($self->{optgroup_cylinder}->sizer); + $self->{sizer}->Hide($self->{optgroup_slab}->sizer); $self->{sizer}->Hide($self->{optgroup_box}->sizer); $self->{sizer}->Hide($self->{optgroup_sphere}->sizer); if ($self->{type}->GetValue eq "box") { $self->{sizer}->Show($self->{optgroup_box}->sizer); } elsif ($self->{type}->GetValue eq "cylinder") { $self->{sizer}->Show($self->{optgroup_cylinder}->sizer); + } elsif ($self->{type}->GetValue eq "slab") { + $self->{sizer}->Show($self->{optgroup_slab}->sizer); } elsif ($self->{type}->GetValue eq "sphere") { $self->{sizer}->Show($self->{optgroup_sphere}->sizer); } diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 8e2dcdeb7..fd93675c2 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -352,6 +352,10 @@ sub on_btn_lambda { $mesh = Slic3r::TriangleMesh::cylinder($params->{"cyl_r"}, $params->{"cyl_h"}); } elsif ($type eq "sphere") { $mesh = Slic3r::TriangleMesh::sphere($params->{"sph_rho"}); + } elsif ($type eq "slab") { + $mesh = Slic3r::TriangleMesh::cube($self->{model_object}->bounding_box->size->x*1.5, $self->{model_object}->bounding_box->size->y*1.5, $params->{"slab_h"}); + # box sets the base coordinate at 0,0, move to center of plate and move it up to initial_z + $mesh->translate(-$self->{model_object}->bounding_box->size->x*1.5/2.0, -$self->{model_object}->bounding_box->size->y*1.5/2.0, $params->{"slab_z"}); } else { return; } From 2875624eac2ac6dcd103b77f0c04756fc2db64d0 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 5 Dec 2016 23:40:28 +0000 Subject: [PATCH 147/225] Repair the generic mesh so it can be exported. --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index fd93675c2..4632635c5 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -359,6 +359,7 @@ sub on_btn_lambda { } else { return; } + $mesh->repair; my $new_volume = $self->{model_object}->add_volume(mesh => $mesh); $new_volume->set_modifier($is_modifier); $new_volume->set_name($name); From a26a60f8dd7b4d373081dccf11d7df3b534f6ec4 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Tue, 6 Dec 2016 00:57:16 -0600 Subject: [PATCH 148/225] Only enable movers if modifier mesh. --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 4632635c5..013cec3d9 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -268,10 +268,15 @@ sub selection_changed { $self->{canvas}->volumes->[ $itemData->{volume_id} ]{selected} = 1; } $self->{btn_delete}->Enable; - $self->{optgroup_movers}->enable; # attach volume config to settings panel my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ]; + + if ($volume->modifier) { + $self->{optgroup_movers}->enable; + } else { + $self->{optgroup_movers}->disable; + } $config = $volume->config; $self->{staticbox}->SetLabel('Part Settings'); From 0033cf9974d08372e4c30d56bcf94e087f0c1e00 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Tue, 6 Dec 2016 22:10:18 -0600 Subject: [PATCH 149/225] Add LWP::UserAgent to use of plater.pm (#3608) --- lib/Slic3r/GUI/Plater.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index b27a3e0cf..508e0450c 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -8,6 +8,7 @@ use utf8; use File::Basename qw(basename dirname); use List::Util qw(sum first max); use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad); +use LWP::UserAgent; use threads::shared qw(shared_clone); use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc :panel :sizer :toolbar :window wxTheApp :notebook :combobox); From d1bd9ab5b71cb4a8e9ac5f080676893a8017227f Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Thu, 8 Dec 2016 19:02:34 -0600 Subject: [PATCH 150/225] Fixed small issue with perimeter path splitting could cause negative flow/feedrates. --- xs/src/libslic3r/ExtrusionEntity.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/ExtrusionEntity.cpp b/xs/src/libslic3r/ExtrusionEntity.cpp index 2b635fc93..376a417c8 100644 --- a/xs/src/libslic3r/ExtrusionEntity.cpp +++ b/xs/src/libslic3r/ExtrusionEntity.cpp @@ -164,8 +164,10 @@ ExtrusionLoop::split_at(const Point &point) } // now split path_idx in two parts - ExtrusionPath p1(this->paths[path_idx].role), p2(this->paths[path_idx].role); - this->paths[path_idx].polyline.split_at(p, &p1.polyline, &p2.polyline); + const ExtrusionPath &path = this->paths[path_idx]; + ExtrusionPath p1(path.role, path.mm3_per_mm, path.width, path.height); + ExtrusionPath p2(path.role, path.mm3_per_mm, path.width, path.height); + path.polyline.split_at(p, &p1.polyline, &p2.polyline); if (this->paths.size() == 1) { if (! p1.polyline.is_valid()) From a881f755a1dc3a8b45a3a3a1f354297890fc3a08 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 12 Dec 2016 01:38:48 +0100 Subject: [PATCH 151/225] New Rectilinear implementation based on @bubnikv's work. Faster, more robust, follows perimeters better, makes more continuous lines. Also includes the new Triangles, Stars, Cubic patterns. Line pattern was removed --- t/fill.t | 69 ++- xs/src/libslic3r/Fill/Fill.cpp | 22 +- xs/src/libslic3r/Fill/Fill.hpp | 10 +- xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp | 2 +- xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp | 2 +- xs/src/libslic3r/Fill/FillConcentric.cpp | 2 +- xs/src/libslic3r/Fill/FillConcentric.hpp | 2 +- xs/src/libslic3r/Fill/FillHoneycomb.cpp | 2 +- xs/src/libslic3r/Fill/FillHoneycomb.hpp | 2 +- xs/src/libslic3r/Fill/FillPlanePath.cpp | 2 +- xs/src/libslic3r/Fill/FillPlanePath.hpp | 2 +- xs/src/libslic3r/Fill/FillRectilinear.cpp | 539 +++++++++++++++++---- xs/src/libslic3r/Fill/FillRectilinear.hpp | 144 ++++-- xs/src/libslic3r/Fill/FillRectilinear2.cpp | 40 +- xs/src/libslic3r/Fill/FillRectilinear2.hpp | 18 +- xs/src/libslic3r/Geometry.cpp | 19 - xs/src/libslic3r/Point.cpp | 6 + xs/src/libslic3r/Point.hpp | 1 + xs/src/libslic3r/PrintConfig.cpp | 8 +- xs/src/libslic3r/PrintConfig.hpp | 7 +- xs/xsp/Filler.xsp | 7 + 21 files changed, 666 insertions(+), 240 deletions(-) diff --git a/t/fill.t b/t/fill.t index af17c86ff..aec533f23 100644 --- a/t/fill.t +++ b/t/fill.t @@ -2,7 +2,7 @@ use Test::More; use strict; use warnings; -plan tests => 43; +plan tests => 92; BEGIN { use FindBin; @@ -11,8 +11,8 @@ BEGIN { use List::Util qw(first sum); use Slic3r; -use Slic3r::Geometry qw(PI X Y scale unscale convex_hull); -use Slic3r::Geometry::Clipper qw(union diff diff_ex offset offset2_ex); +use Slic3r::Geometry qw(PI X Y scaled_epsilon scale unscale convex_hull); +use Slic3r::Geometry::Clipper qw(union diff diff_ex offset offset2_ex diff_pl); use Slic3r::Surface qw(:types); use Slic3r::Test; @@ -26,6 +26,67 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } is $surface_width % $distance, 0, 'adjusted solid distance'; } +{ + my $filler = Slic3r::Filler->new_from_type('rectilinear'); + $filler->set_angle(-(PI)/2); + $filler->set_spacing(5); + $filler->set_dont_adjust(1); + $filler->set_endpoints_overlap(0); + + my $test = sub { + my ($expolygon) = @_; + my $surface = Slic3r::Surface->new( + surface_type => S_TYPE_TOP, + expolygon => $expolygon, + ); + return $filler->fill_surface($surface); + }; + + # square + $filler->set_density($filler->spacing / 50); + for my $i (0..3) { + # check that it works regardless of the points order + my @points = ([0,0], [100,0], [100,100], [0,100]); + @points = (@points[$i..$#points], @points[0..($i-1)]); + my $paths = $test->(my $e = Slic3r::ExPolygon->new([ scale_points @points ])); + + is(scalar @$paths, 1, 'one continuous path') or done_testing, exit; + ok abs($paths->[0]->length - scale(3*100 + 2*50)) - scaled_epsilon, 'path has expected length'; + } + + # diamond with endpoints on grid + { + my $paths = $test->(my $e = Slic3r::ExPolygon->new([ scale_points [0,0], [100,0], [150,50], [100,100], [0,100], [-50,50] ])); + is(scalar @$paths, 1, 'one continuous path') or done_testing, exit; + } + + # square with hole + for my $angle (-(PI/2), -(PI/4), -(PI), PI/2, PI) { + for my $spacing (25, 5, 7.5, 8.5) { + $filler->set_density($filler->spacing / $spacing); + $filler->set_angle($angle); + my $paths = $test->(my $e = Slic3r::ExPolygon->new( + [ scale_points [0,0], [100,0], [100,100], [0,100] ], + [ scale_points reverse [25,25], [75,25], [75,75], [25,75] ], + )); + + if (0) { + require "Slic3r/SVG.pm"; + Slic3r::SVG::output( + "fill.svg", + no_arrows => 1, + expolygons => [$e], + polylines => $paths, + ); + } + + ok(@$paths >= 2 && @$paths <= 3, '2 or 3 continuous paths') or done_testing, exit; + ok(!@{diff_pl($paths->arrayref, offset(\@$e, +scaled_epsilon*10))}, + 'paths don\'t cross hole') or done_testing, exit; + } + } +} + { my $expolygon = Slic3r::ExPolygon->new([ scale_points [0,0], [50,0], [50,50], [0,50] ]); my $filler = Slic3r::Filler->new_from_type('rectilinear'); @@ -41,6 +102,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } nozzle_diameter => 0.50, ); $filler->set_spacing($flow->spacing); + $filler->set_density(1); foreach my $angle (0, 45) { $surface->expolygon->rotate(Slic3r::Geometry::deg2rad($angle), [0,0]); my $paths = $filler->fill_surface($surface, layer_height => 0.4, density => 0.4); @@ -55,6 +117,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } my $filler = Slic3r::Filler->new_from_type('rectilinear'); $filler->set_bounding_box($expolygon->bounding_box); $filler->set_angle($angle // 0); + $filler->set_dont_adjust(0); my $surface = Slic3r::Surface->new( surface_type => S_TYPE_BOTTOM, expolygon => $expolygon, diff --git a/xs/src/libslic3r/Fill/Fill.cpp b/xs/src/libslic3r/Fill/Fill.cpp index 6f7b5486d..f57a746d2 100644 --- a/xs/src/libslic3r/Fill/Fill.cpp +++ b/xs/src/libslic3r/Fill/Fill.cpp @@ -24,12 +24,9 @@ Fill::new_from_type(const InfillPattern type) case ip3DHoneycomb: return new Fill3DHoneycomb(); case ipRectilinear: return new FillRectilinear(); - case ipLine: return new FillLine(); - case ipGrid: return new FillGrid(); case ipAlignedRectilinear: return new FillAlignedRectilinear(); + case ipGrid: return new FillGrid(); - case ipRectilinear2: return new FillRectilinear2(); - case ipGrid2: return new FillGrid2(); case ipTriangles: return new FillTriangles(); case ipStars: return new FillStars(); case ipCubic: return new FillCubic(); @@ -80,26 +77,27 @@ Fill::adjust_solid_spacing(const coord_t width, const coord_t distance) { assert(width >= 0); assert(distance > 0); - // floor(width / distance) - coord_t number_of_intervals = floor(width / distance); - coord_t distance_new = (number_of_intervals == 0) - ? distance - : (width / number_of_intervals); + const int number_of_intervals = floor(width / distance); + if (number_of_intervals == 0) return distance; + + coord_t distance_new = (width / number_of_intervals); const coordf_t factor = coordf_t(distance_new) / coordf_t(distance); assert(factor > 1. - 1e-5); // How much could the extrusion width be increased? By 20%. const coordf_t factor_max = 1.2; - if (factor > factor_max) + if (false && factor > factor_max) distance_new = floor((double)distance * factor_max + 0.5); + assert((distance_new * number_of_intervals) <= width); + return distance_new; } // Returns orientation of the infill and the reference point of the infill pattern. // For a normal print, the reference point is the center of a bounding box of the STL. -std::pair +Fill::direction_t Fill::_infill_direction(const Surface &surface) const { // set infill angle @@ -133,7 +131,7 @@ Fill::_infill_direction(const Surface &surface) const } out_angle += float(M_PI/2.); - return std::pair(out_angle, out_shift); + return direction_t(out_angle, out_shift); } } // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/Fill.hpp b/xs/src/libslic3r/Fill/Fill.hpp index 266dad4c6..a733a870d 100644 --- a/xs/src/libslic3r/Fill/Fill.hpp +++ b/xs/src/libslic3r/Fill/Fill.hpp @@ -29,6 +29,9 @@ public: // in unscaled coordinates coordf_t spacing; + // overlap over spacing for extrusion endpoints + float endpoints_overlap; + // in radians, ccw, 0 = East float angle; @@ -80,6 +83,7 @@ protected: layer_id(size_t(-1)), z(0.f), spacing(0.f), + endpoints_overlap(0.3f), angle(0), link_max_length(0), loop_clipping(0), @@ -89,10 +93,12 @@ protected: complete(false) {}; + typedef std::pair direction_t; + // The expolygon may be modified by the method to avoid a copy. virtual void _fill_surface_single( unsigned int thickness_layers, - const std::pair &direction, + const direction_t &direction, ExPolygon &expolygon, Polylines* polylines_out) {}; @@ -101,7 +107,7 @@ protected: return (idx % 2) == 0 ? (M_PI/2.) : 0; }; - std::pair _infill_direction(const Surface &surface) const; + direction_t _infill_direction(const Surface &surface) const; }; } // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp index 1c316e920..3f523b27c 100644 --- a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp +++ b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -150,7 +150,7 @@ makeGrid(coord_t z, coord_t gridSize, size_t gridWidth, size_t gridHeight, size_ void Fill3DHoneycomb::_fill_surface_single( unsigned int thickness_layers, - const std::pair &direction, + const direction_t &direction, ExPolygon &expolygon, Polylines* polylines_out) { diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp b/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp index 82699397f..43d43ab83 100644 --- a/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp +++ b/xs/src/libslic3r/Fill/Fill3DHoneycomb.hpp @@ -21,7 +21,7 @@ public: protected: virtual void _fill_surface_single( unsigned int thickness_layers, - const std::pair &direction, + const direction_t &direction, ExPolygon &expolygon, Polylines* polylines_out); }; diff --git a/xs/src/libslic3r/Fill/FillConcentric.cpp b/xs/src/libslic3r/Fill/FillConcentric.cpp index bf537e532..cb09993b9 100644 --- a/xs/src/libslic3r/Fill/FillConcentric.cpp +++ b/xs/src/libslic3r/Fill/FillConcentric.cpp @@ -9,7 +9,7 @@ namespace Slic3r { void FillConcentric::_fill_surface_single( unsigned int thickness_layers, - const std::pair &direction, + const direction_t &direction, ExPolygon &expolygon, Polylines* polylines_out) { diff --git a/xs/src/libslic3r/Fill/FillConcentric.hpp b/xs/src/libslic3r/Fill/FillConcentric.hpp index 7bb7687f5..f19a5d2b1 100644 --- a/xs/src/libslic3r/Fill/FillConcentric.hpp +++ b/xs/src/libslic3r/Fill/FillConcentric.hpp @@ -14,7 +14,7 @@ protected: virtual Fill* clone() const { return new FillConcentric(*this); }; virtual void _fill_surface_single( unsigned int thickness_layers, - const std::pair &direction, + const direction_t &direction, ExPolygon &expolygon, Polylines* polylines_out); diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.cpp b/xs/src/libslic3r/Fill/FillHoneycomb.cpp index 127d7a810..df4e62a3d 100644 --- a/xs/src/libslic3r/Fill/FillHoneycomb.cpp +++ b/xs/src/libslic3r/Fill/FillHoneycomb.cpp @@ -9,7 +9,7 @@ namespace Slic3r { void FillHoneycomb::_fill_surface_single( unsigned int thickness_layers, - const std::pair &direction, + const direction_t &direction, ExPolygon &expolygon, Polylines* polylines_out) { diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.hpp b/xs/src/libslic3r/Fill/FillHoneycomb.hpp index 8fdb00265..b048a3b2f 100644 --- a/xs/src/libslic3r/Fill/FillHoneycomb.hpp +++ b/xs/src/libslic3r/Fill/FillHoneycomb.hpp @@ -18,7 +18,7 @@ protected: virtual Fill* clone() const { return new FillHoneycomb(*this); }; virtual void _fill_surface_single( unsigned int thickness_layers, - const std::pair &direction, + const direction_t &direction, ExPolygon &expolygon, Polylines* polylines_out ); diff --git a/xs/src/libslic3r/Fill/FillPlanePath.cpp b/xs/src/libslic3r/Fill/FillPlanePath.cpp index 3dad299f4..09b0ea700 100644 --- a/xs/src/libslic3r/Fill/FillPlanePath.cpp +++ b/xs/src/libslic3r/Fill/FillPlanePath.cpp @@ -8,7 +8,7 @@ namespace Slic3r { void FillPlanePath::_fill_surface_single( unsigned int thickness_layers, - const std::pair &direction, + const direction_t &direction, ExPolygon &expolygon, Polylines* polylines_out) { diff --git a/xs/src/libslic3r/Fill/FillPlanePath.hpp b/xs/src/libslic3r/Fill/FillPlanePath.hpp index 04fb225a8..7e308aac5 100644 --- a/xs/src/libslic3r/Fill/FillPlanePath.hpp +++ b/xs/src/libslic3r/Fill/FillPlanePath.hpp @@ -21,7 +21,7 @@ public: protected: virtual void _fill_surface_single( unsigned int thickness_layers, - const std::pair &direction, + const direction_t &direction, ExPolygon &expolygon, Polylines* polylines_out); diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/xs/src/libslic3r/Fill/FillRectilinear.cpp index 8fa311471..c56c233b4 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.cpp @@ -2,138 +2,471 @@ #include "../ExPolygon.hpp" #include "../PolylineCollection.hpp" #include "../Surface.hpp" +#include +#include #include "FillRectilinear.hpp" +//#define DEBUG_RECTILINEAR +#ifdef DEBUG_RECTILINEAR + #include "../SVG.hpp" +#endif + namespace Slic3r { -void FillRectilinear::_fill_surface_single( - unsigned int thickness_layers, - const std::pair &direction, - ExPolygon &expolygon, - Polylines* polylines_out) +void +FillRectilinear::_fill_single_direction(ExPolygon expolygon, + const direction_t &direction, coord_t x_shift, Polylines* out) { - assert(this->density > 0.0001f && this->density <= 1.f); - // rotate polygons so that we can work with vertical lines here expolygon.rotate(-direction.first); - this->_min_spacing = scale_(this->spacing); - this->_line_spacing = coord_t(coordf_t(this->_min_spacing) / this->density); - this->_diagonal_distance = this->_line_spacing * 2; - this->_line_oscillation = this->_line_spacing - this->_min_spacing; // only for Line infill + assert(this->density > 0.0001f && this->density <= 1.f); + const coord_t min_spacing = scale_(this->spacing); + coord_t line_spacing = (double) min_spacing / this->density; // We ignore this->bounding_box because it doesn't matter; we're doing align_to_grid below. BoundingBox bounding_box = expolygon.contour.bounding_box(); + // Due to integer rounding, rotated polygons might not preserve verticality + // (i.e. when rotating by PI/2 two points having the same x coordinate + // they might get different y coordinates), thus the first line will be skipped. + bounding_box.offset(-1); + // define flow spacing according to requested density if (this->density > 0.9999f && !this->dont_adjust) { - this->_line_spacing = this->adjust_solid_spacing(bounding_box.size().x, this->_line_spacing); - this->spacing = unscale(this->_line_spacing); + line_spacing = this->adjust_solid_spacing(bounding_box.size().x, line_spacing); + this->spacing = unscale(line_spacing); } else { // extend bounding box so that our pattern will be aligned with other layers // Transform the reference point to the rotated coordinate system. + Point p = direction.second.rotated(-direction.first); + p.x -= x_shift > 0 ? x_shift : (x_shift + line_spacing); bounding_box.min.align_to_grid( - Point(this->_line_spacing, this->_line_spacing), - direction.second.rotated(-direction.first) + Point(line_spacing, line_spacing), + p ); } - - // generate the basic pattern - const coord_t x_max = bounding_box.max.x + SCALED_EPSILON; - Lines lines; - for (coord_t x = bounding_box.min.x; x <= x_max; x += this->_line_spacing) - lines.push_back(this->_line(lines.size(), x, bounding_box.min.y, bounding_box.max.y)); - if (this->_horizontal_lines()) { - const coord_t y_max = bounding_box.max.y + SCALED_EPSILON; - for (coord_t y = bounding_box.min.y; y <= y_max; y += this->_line_spacing) - lines.push_back(Line(Point(bounding_box.min.x, y), Point(bounding_box.max.x, y))); - } - - // clip paths against a slightly larger expolygon, so that the first and last paths - // are kept even if the expolygon has vertical sides - // the minimum offset for preventing edge lines from being clipped is SCALED_EPSILON; - // however we use a larger offset to support expolygons with slightly skewed sides and - // not perfectly straight - Polylines polylines = intersection_pl( - to_polylines(lines), - offset(expolygon, scale_(0.02)), - false - ); - - // FIXME Vojtech: This is only performed for horizontal lines, not for the vertical lines! - const float INFILL_OVERLAP_OVER_SPACING = 0.3f; + // Find all the polygons points intersecting the rectilinear vertical lines and store + // them in an std::map<> (grid) which orders them automatically by x and y. + // For each intersection point we store its position (upper/lower): upper means it's + // the upper endpoint of an intersection line, and vice versa. + // Whenever between two intersection points we find vertices of the original polygon, + // store them in the 'skipped' member of the latter point. - // How much to extend an infill path from expolygon outside? - const coord_t extra = coord_t(floor(this->_min_spacing * INFILL_OVERLAP_OVER_SPACING + 0.5f)); - for (Polylines::iterator it_polyline = polylines.begin(); - it_polyline != polylines.end(); ++ it_polyline) { - Point *first_point = &it_polyline->points.front(); - Point *last_point = &it_polyline->points.back(); - if (first_point->y > last_point->y) - std::swap(first_point, last_point); - first_point->y -= extra; - last_point->y += extra; - } - - size_t n_polylines_out_old = polylines_out->size(); - - // connect lines - if (!this->dont_connect && !polylines.empty()) { // prevent calling leftmost_point() on empty collections - // offset the expolygon by max(min_spacing/2, extra) - ExPolygon expolygon_off; - { - ExPolygons expolygons_off = offset_ex(expolygon, this->_min_spacing/2); - if (!expolygons_off.empty()) { - // When expanding a polygon, the number of islands could only shrink. - // Therefore the offset_ex shall generate exactly one expanded island - // for one input island. - assert(expolygons_off.size() == 1); - std::swap(expolygon_off, expolygons_off.front()); - } - } - Polylines chained = PolylineCollection::chained_path_from( - STDMOVE(polylines), - PolylineCollection::leftmost_point(polylines), - false // reverse allowed - ); - bool first = true; - for (Polylines::iterator it_polyline = chained.begin(); it_polyline != chained.end(); ++ it_polyline) { - if (!first) { - // Try to connect the lines. - Points &pts_end = polylines_out->back().points; - const Point &first_point = it_polyline->points.front(); - const Point &last_point = pts_end.back(); - // Distance in X, Y. - const Vector distance = first_point.vector_to(last_point); - // TODO: we should also check that both points are on a fill_boundary to avoid - // connecting paths on the boundaries of internal regions - if (this->_can_connect(std::abs(distance.x), std::abs(distance.y)) - && expolygon_off.contains(Line(last_point, first_point))) { - // Append the polyline. - append_to(pts_end, it_polyline->points); + grid_t grid; + { + const Polygons polygons = expolygon; + for (Polygons::const_iterator polygon = polygons.begin(); polygon != polygons.end(); ++polygon) { + const Points &points = polygon->points; + + // This vector holds the original polygon vertices found after the last intersection + // point. We'll flush it as soon as we find the next intersection point. + Points skipped_points; + + // This vector holds the coordinates of the intersection points found while + // looping through the polygon. + Points ips; + + for (Points::const_iterator p = points.begin(); p != points.end(); ++p) { + const Point &prev = p == points.begin() ? *(points.end()-1) : *(p-1); + const Point &next = p == points.end()-1 ? *points.begin() : *(p+1); + const Point &next2 = p == (points.end()-2) ? *points.begin() + : p == (points.end()-1) ? *(points.begin()+1) : *(p+2); + + // Does the p-next line belong to an intersection line? + if (p->x == next.x && ((p->x - bounding_box.min.x) % line_spacing) == 0) { + if (p->y == next.y) continue; // skip coinciding points + vertical_t &v = grid[p->x]; + + // Detect line direction. + IntersectionPoint::ipType p_type = IntersectionPoint::ipTypeLower; + IntersectionPoint::ipType n_type = IntersectionPoint::ipTypeUpper; + if (p->y > next.y) std::swap(p_type, n_type); // line goes downwards + + // Do we already have 'p' in our grid? + vertical_t::iterator pit = v.find(p->y); + if (pit != v.end()) { + // Yes, we have it. If its not of the same type, it means it's + // an intermediate point of a longer line. We store this information + // for now and we'll remove it later. + if (pit->second.type != p_type) + pit->second.type = IntersectionPoint::ipTypeMiddle; + } else { + // Store the point. + IntersectionPoint ip(p->x, p->y, p_type); + v[p->y] = ip; + ips.push_back(ip); + } + + // Do we already have 'next' in our grid? + pit = v.find(next.y); + if (pit != v.end()) { + // Yes, we have it. If its not of the same type, it means it's + // an intermediate point of a longer line. We store this information + // for now and we'll remove it later. + if (pit->second.type != n_type) + pit->second.type = IntersectionPoint::ipTypeMiddle; + } else { + // Store the point. + IntersectionPoint ip(next.x, next.y, n_type); + v[next.y] = ip; + ips.push_back(ip); + } continue; } + + // We're going to look for intersection points within this line. + // First, let's sort its x coordinates regardless of the original line direction. + const coord_t min_x = std::min(p->x, next.x); + const coord_t max_x = std::max(p->x, next.x); + + // Now find the leftmost intersection point belonging to the line. + const coord_t min_x2 = bounding_box.min.x + ceil((double) (min_x - bounding_box.min.x) / (double)line_spacing) * (double)line_spacing; + assert(min_x2 >= min_x); + + // In case this coordinate does not belong to this line, we have no intersection points. + if (min_x2 > max_x) { + // Store the two skipped points and move on. + skipped_points.push_back(*p); + skipped_points.push_back(next); + continue; + } + + // Find the rightmost intersection point belonging to the line. + const coord_t max_x2 = bounding_box.min.x + floor((double) (max_x - bounding_box.min.x) / (double) line_spacing) * (double)line_spacing; + assert(max_x2 <= max_x); + + // We're now going past the first point, so save it. + if (p->x < min_x2) + skipped_points.push_back(*p); + + // Now loop through those intersection points according the original direction + // of the line (because we need to store them in this order). + const bool line_goes_right = next.x > p->x; + for (coord_t x = line_goes_right ? min_x2 : max_x2; + x >= min_x && x <= max_x; + x += line_goes_right ? +line_spacing : -line_spacing) { + + // Is this intersection an endpoint of the original line *and* is the + // intersection just a tangent point? If so, just skip it. + if (x == p->x && ((prev.x > x && next.x > x) || (prev.x < x && next.x < x))) { + skipped_points.push_back(*p); + continue; + } + if (x == next.x && ((p->x > x && next2.x > x) || (p->x < x && next2.x < x))) { + skipped_points.push_back(next); + continue; + } + + // Calculate the y coordinate of this intersection. + IntersectionPoint ip( + x, + p->y + double(next.y - p->y) * double(x - p->x) / double(next.x - p->x), + line_goes_right ? IntersectionPoint::ipTypeLower : IntersectionPoint::ipTypeUpper + ); + vertical_t &v = grid[ip.x]; + + // Did we already find this point? + // (We might have found it as the endpoint of a vertical line.) + { + vertical_t::iterator pit = v.find(ip.y); + if (pit != v.end()) { + // Yes, we have it. If its not of the same type, it means it's + // an intermediate point of a longer line. We store this information + // for now and we'll remove it later. + if (pit->second.type != ip.type) + pit->second.type = IntersectionPoint::ipTypeMiddle; + continue; + } + } + + // Store the skipped polygon vertices along with this point. + ip.skipped = skipped_points; + skipped_points.clear(); + + #ifdef DEBUG_RECTILINEAR + printf("NEW POINT at %f,%f\n", unscale(ip.x), unscale(ip.y)); + for (Points::const_iterator it = ip.skipped.begin(); it != ip.skipped.end(); ++it) + printf(" skipped: %f,%f\n", unscale(it->x), unscale(it->y)); + #endif + + // Store the point. + v[ip.y] = ip; + ips.push_back(ip); + } + + // We're now going past the final point, so save it. + if (next.x > max_x2) + skipped_points.push_back(next); + } + + if (!this->dont_connect) { + // We'll now build connections between the vertical intersection lines. + // Each intersection point will be connected to the first intersection point + // found along the original polygon having a greater x coordinate (or the same + // x coordinate: think about two vertical intersection lines having the same x + // separated by a hole polygon: we'll connect them with the hole portion). + // We will sweep only from left to right, so we only need to build connections + // in this direction. + for (Points::const_iterator it = ips.begin(); it != ips.end(); ++it) { + IntersectionPoint &ip = grid[it->x][it->y]; + IntersectionPoint &next = it == ips.end()-1 ? grid[ips.begin()->x][ips.begin()->y] : grid[(it+1)->x][(it+1)->y]; + + #ifdef DEBUG_RECTILINEAR + printf("CONNECTING %f,%f to %f,%f\n", + unscale(ip.x), unscale(ip.y), + unscale(next.x), unscale(next.y) + ); + #endif + + // We didn't flush the skipped_points vector after completing the loop above: + // it now contains the polygon vertices between the last and the first + // intersection points. + if (it == ips.begin()) + ip.skipped.insert(ip.skipped.begin(), skipped_points.begin(), skipped_points.end()); + + if (ip.x <= next.x) { + // Link 'ip' to 'next' ---> + if (ip.next.empty()) { + ip.next = next.skipped; + ip.next.push_back(next); + } + } else if (next.x < ip.x) { + // Link 'next' to 'ip' ---> + if (next.next.empty()) { + next.next = next.skipped; + std::reverse(next.next.begin(), next.next.end()); + next.next.push_back(ip); + } + } + } + } + + // Do some cleanup: remove the 'skipped' points we used for building + // connections and also remove the middle intersection points. + for (Points::const_iterator it = ips.begin(); it != ips.end(); ++it) { + vertical_t &v = grid[it->x]; + IntersectionPoint &ip = v[it->y]; + ip.skipped.clear(); + if (ip.type == IntersectionPoint::ipTypeMiddle) + v.erase(it->y); } - // The lines cannot be connected. - #if SLIC3R_CPPVER >= 11 - polylines_out->push_back(std::move(*it_polyline)); - #else - polylines_out->push_back(Polyline()); - std::swap(polylines_out->back(), *it_polyline); - #endif - first = false; } } - - // paths must be rotated back - for (Polylines::iterator it = polylines_out->begin() + n_polylines_out_old; - it != polylines_out->end(); ++ it) { - // No need to translate, the absolute position is irrelevant. - // it->translate(- direction.second.x, - direction.second.y); - it->rotate(direction.first); + + #ifdef DEBUG_RECTILINEAR + SVG svg("grid.svg"); + svg.draw(expolygon); + + printf("GRID:\n"); + for (grid_t::const_iterator it = grid.begin(); it != grid.end(); ++it) { + printf("x = %f:\n", unscale(it->first)); + for (vertical_t::const_iterator v = it->second.begin(); v != it->second.end(); ++v) { + const IntersectionPoint &ip = v->second; + printf(" y = %f (%s, next = %f,%f, extra = %zu)\n", unscale(v->first), + ip.type == IntersectionPoint::ipTypeLower ? "lower" + : ip.type == IntersectionPoint::ipTypeMiddle ? "middle" : "upper", + (ip.next.empty() ? -1 : unscale(ip.next.back().x)), + (ip.next.empty() ? -1 : unscale(ip.next.back().y)), + (ip.next.empty() ? 0 : ip.next.size()-1) + ); + svg.draw(ip, ip.type == IntersectionPoint::ipTypeLower ? "blue" + : ip.type == IntersectionPoint::ipTypeMiddle ? "yellow" : "red"); + } } + printf("\n"); + + svg.Close(); + #endif + + // Calculate the extension of the vertical endpoints according to the configured value. + const coord_t extra_y = floor((double)min_spacing * this->endpoints_overlap + 0.5f); + + // Store the number of polygons already existing in the output container. + const size_t n_polylines_out_old = out->size(); + + // Loop until we have no more vertical lines available. + while (!grid.empty()) { + // Get the first x coordinate. + vertical_t &v = grid.begin()->second; + + // If this x coordinate does not have any y coordinate, remove it. + if (v.empty()) { + grid.erase(grid.begin()); + continue; + } + + // We expect every x coordinate to contain an even number of y coordinates + // because they are the endpoints of vertical intersection lines: + // lower/upper, lower/upper etc. + assert(v.size() % 2 == 0); + + // Get the first lower point. + vertical_t::iterator it = v.begin(); // minimum x,y + IntersectionPoint p = it->second; + assert(p.type == IntersectionPoint::ipTypeLower); + + // Start our polyline. + Polyline polyline; + polyline.append(p); + polyline.points.back().y -= extra_y; + + while (true) { + // Complete the vertical line by finding the corresponding upper or lower point. + if (p.type == IntersectionPoint::ipTypeUpper) { + // find first point along c.x with y < c.y + assert(it != grid[p.x].begin()); + --it; + } else { + // find first point along c.x with y > c.y + ++it; + assert(it != grid[p.x].end()); + } + + // Append the point to our polyline. + IntersectionPoint b = it->second; + assert(b.type != p.type); + polyline.append(b); + polyline.points.back().y += extra_y * (b.type == IntersectionPoint::ipTypeUpper ? 1 : -1); + + // Remove the two endpoints of this vertical line from the grid. + { + vertical_t &v = grid[p.x]; + v.erase(p.y); + v.erase(it); + if (v.empty()) grid.erase(p.x); + } + // Do we have a connection starting from here? + // If not, stop the polyline. + if (b.next.empty()) + break; + + // If we have a connection, append it to the polyline. + // We apply the y extension to the whole connection line. This works well when + // the connection is straight and horizontal, but doesn't work well when the + // connection is articulated and also has vertical parts. + { + const size_t n = polyline.points.size(); + polyline.append(b.next); + for (Points::iterator pit = polyline.points.begin()+n; pit != polyline.points.end(); ++pit) + pit->y += extra_y * (b.type == IntersectionPoint::ipTypeUpper ? 1 : -1); + } + + // Is the final point still available? + if (grid.count(b.next.back().x) == 0 + || grid[b.next.back().x].count(b.next.back().y) == 0) + // We already used this point or we might have removed this + // point while building the grid because it's collinear (middle); in either + // cases the connection line from the previous one is legit and worth having. + break; + + // Retrieve the intersection point. The next loop will find the correspondent + // endpoint of the vertical line. + it = grid[ b.next.back().x ].find(b.next.back().y); + p = it->second; + + // If the connection brought us to another x coordinate, we expect the point + // type to be the same. + assert((p.type == b.type && p.x > b.x) + || (p.type != b.type && p.x == b.x)); + } + + // Yay, we have a polyline! + if (polyline.is_valid()) + out->push_back(polyline); + } + + // paths must be rotated back + for (Polylines::iterator it = out->begin() + n_polylines_out_old; + it != out->end(); ++it) + it->rotate(direction.first); +} + +void FillRectilinear::_fill_surface_single( + unsigned int thickness_layers, + const direction_t &direction, + ExPolygon &expolygon, + Polylines* out) +{ + this->_fill_single_direction(expolygon, direction, 0, out); +} + +void FillGrid::_fill_surface_single( + unsigned int thickness_layers, + const direction_t &direction, + ExPolygon &expolygon, + Polylines* out) +{ + FillGrid fill2 = *this; + fill2.density /= 2.; + + direction_t direction2 = direction; + direction2.first += PI/2; + fill2._fill_single_direction(expolygon, direction, 0, out); + fill2._fill_single_direction(expolygon, direction2, 0, out); +} + +void FillTriangles::_fill_surface_single( + unsigned int thickness_layers, + const direction_t &direction, + ExPolygon &expolygon, + Polylines* out) +{ + FillTriangles fill2 = *this; + fill2.density /= 3.; + direction_t direction2 = direction; + + fill2._fill_single_direction(expolygon, direction2, 0, out); + + direction2.first += PI/3; + fill2._fill_single_direction(expolygon, direction2, 0, out); + + direction2.first += PI/3; + fill2._fill_single_direction(expolygon, direction2, 0, out); +} + +void FillStars::_fill_surface_single( + unsigned int thickness_layers, + const direction_t &direction, + ExPolygon &expolygon, + Polylines* out) +{ + FillStars fill2 = *this; + fill2.density /= 3.; + direction_t direction2 = direction; + + fill2._fill_single_direction(expolygon, direction2, 0, out); + + direction2.first += PI/3; + fill2._fill_single_direction(expolygon, direction2, 0, out); + + direction2.first += PI/3; + const coord_t x_shift = 0.5 * scale_(fill2.spacing) / fill2.density; + fill2._fill_single_direction(expolygon, direction2, x_shift, out); +} + +void FillCubic::_fill_surface_single( + unsigned int thickness_layers, + const direction_t &direction, + ExPolygon &expolygon, + Polylines* out) +{ + FillCubic fill2 = *this; + fill2.density /= 3.; + direction_t direction2 = direction; + + const coord_t range = scale_(this->spacing / this->density); + const coord_t x_shift = abs(( (coord_t)(scale_(this->z) + range) % (coord_t)(range * 2)) - range); + + fill2._fill_single_direction(expolygon, direction2, -x_shift, out); + + direction2.first += PI/3; + fill2._fill_single_direction(expolygon, direction2, +x_shift, out); + + direction2.first += PI/3; + fill2._fill_single_direction(expolygon, direction2, -x_shift, out); } } // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/FillRectilinear.hpp b/xs/src/libslic3r/Fill/FillRectilinear.hpp index d2bd8d777..6d493fbc7 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.hpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.hpp @@ -16,61 +16,33 @@ public: protected: virtual void _fill_surface_single( unsigned int thickness_layers, - const std::pair &direction, + const direction_t &direction, ExPolygon &expolygon, Polylines* polylines_out); - - coord_t _min_spacing; - coord_t _line_spacing; - // distance threshold for allowing the horizontal infill lines to be connected into a continuous path - coord_t _diagonal_distance; - // only for line infill - coord_t _line_oscillation; - - // Enabled for the grid infill, disabled for the rectilinear and line infill. - virtual bool _horizontal_lines() const { return false; }; - - virtual Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const - { return Line(Point(x, y_min), Point(x, y_max)); }; - - virtual bool _can_connect(coord_t dist_X, coord_t dist_Y) { - return dist_X <= this->_diagonal_distance - && dist_Y <= this->_diagonal_distance; + + void _fill_single_direction(ExPolygon expolygon, const direction_t &direction, + coord_t x_shift, Polylines* out); + + struct IntersectionPoint : Point { + enum ipType { ipTypeLower, ipTypeUpper, ipTypeMiddle }; + ipType type; + + // skipped contains the polygon points accumulated between the previous intersection + // point and the current one, in the original polygon winding order (does not contain + // either points) + Points skipped; + + // next contains a polygon portion connecting this point to the first intersection + // point found following the polygon in any direction but having: + // x > this->x || (x == this->x && y > this->y) + // (it doesn't contain *this but it contains the target intersection point) + Points next; + + IntersectionPoint() : Point() {}; + IntersectionPoint(coord_t x, coord_t y, ipType _type) : Point(x,y), type(_type) {}; }; -}; - -class FillLine : public FillRectilinear -{ -public: - virtual Fill* clone() const { return new FillLine(*this); }; - virtual ~FillLine() {} - -protected: - virtual Line _line(int i, coord_t x, coord_t y_min, coord_t y_max) const { - coord_t osc = (i & 1) ? this->_line_oscillation : 0; - return Line(Point(x - osc, y_min), Point(x + osc, y_max)); - }; - - virtual bool _can_connect(coord_t dist_X, coord_t dist_Y) - { - coord_t TOLERANCE = 10 * SCALED_EPSILON; - return (dist_X >= (this->_line_spacing - this->_line_oscillation) - TOLERANCE) - && (dist_X <= (this->_line_spacing + this->_line_oscillation) + TOLERANCE) - && (dist_Y <= this->_diagonal_distance); - }; -}; - -class FillGrid : public FillRectilinear -{ -public: - virtual Fill* clone() const { return new FillGrid(*this); }; - virtual ~FillGrid() {} - -protected: - // The grid fill will keep the angle constant between the layers,; see the implementation of Slic3r::Fill. - virtual float _layer_angle(size_t idx) const { return 0.f; } - // Flag for Slic3r::Fill::Rectilinear to fill both directions. - virtual bool _horizontal_lines() const { return true; }; + typedef std::map vertical_t; // + typedef std::map grid_t; // > }; class FillAlignedRectilinear : public FillRectilinear @@ -84,6 +56,74 @@ protected: virtual float _layer_angle(size_t idx) const { return 0.f; }; }; +class FillGrid : public FillRectilinear +{ +public: + virtual Fill* clone() const { return new FillGrid(*this); }; + virtual ~FillGrid() {} + +protected: + // The grid fill will keep the angle constant between the layers,; see the implementation of Slic3r::Fill. + virtual float _layer_angle(size_t idx) const { return 0.f; } + + virtual void _fill_surface_single( + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out); +}; + +class FillTriangles : public FillRectilinear +{ +public: + virtual Fill* clone() const { return new FillTriangles(*this); }; + virtual ~FillTriangles() {} + +protected: + // The grid fill will keep the angle constant between the layers,; see the implementation of Slic3r::Fill. + virtual float _layer_angle(size_t idx) const { return 0.f; } + + virtual void _fill_surface_single( + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out); +}; + +class FillStars : public FillRectilinear +{ +public: + virtual Fill* clone() const { return new FillStars(*this); }; + virtual ~FillStars() {} + +protected: + // The grid fill will keep the angle constant between the layers,; see the implementation of Slic3r::Fill. + virtual float _layer_angle(size_t idx) const { return 0.f; } + + virtual void _fill_surface_single( + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out); +}; + +class FillCubic : public FillRectilinear +{ +public: + virtual Fill* clone() const { return new FillCubic(*this); }; + virtual ~FillCubic() {} + +protected: + // The grid fill will keep the angle constant between the layers,; see the implementation of Slic3r::Fill. + virtual float _layer_angle(size_t idx) const { return 0.f; } + + virtual void _fill_surface_single( + unsigned int thickness_layers, + const std::pair &direction, + ExPolygon &expolygon, + Polylines* polylines_out); +}; + }; // namespace Slic3r #endif // slic3r_FillRectilinear_hpp_ diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.cpp b/xs/src/libslic3r/Fill/FillRectilinear2.cpp index 653bb789e..b205bcc69 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear2.cpp @@ -16,7 +16,7 @@ // #define SLIC3R_DEBUG // Make assert active if SLIC3R_DEBUG -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG2 #undef NDEBUG #include "SVG.hpp" #endif @@ -116,7 +116,7 @@ static inline bool is_ccw(const Polygon &poly) // therefore the point p1 lies on poly.points[seg1-1], poly.points[seg1] etc. static inline coordf_t segment_length(const Polygon &poly, size_t seg1, const Point &p1, size_t seg2, const Point &p2) { -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG2 // Verify that p1 lies on seg1. This is difficult to verify precisely, // but at least verify, that p1 lies in the bounding box of seg1. for (size_t i = 0; i < 2; ++ i) { @@ -409,7 +409,7 @@ public: bool is_contour_ccw(size_t idx) const { return polygons_ccw[idx]; } -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG2 void export_to_svg(Slic3r::SVG &svg) { //svg.draw_outline(polygons_src, "black"); //svg.draw_outline(polygons_outer, "green"); @@ -846,7 +846,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angle myassert(INFILL_OVERLAP_OVER_SPACING > 0 && INFILL_OVERLAP_OVER_SPACING < 0.5f); // Rotate polygons so that we can work with vertical lines here - std::pair rotate_vector = this->_infill_direction(*surface); + direction_t rotate_vector = this->_infill_direction(*surface); rotate_vector.first += angleBase; myassert(this->density > 0.0001f && this->density <= 1.f); @@ -889,7 +889,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angle size_t n_vlines = (bounding_box.max.x - bounding_box.min.x + line_spacing - 1) / line_spacing; coord_t x0 = bounding_box.min.x + (line_spacing + SCALED_EPSILON) / 2; -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG2 static int iRun = 0; BoundingBox bbox_svg(to_points(poly_with_offset.polygons_outer)); //::Slic3r::SVG svg(debug_out_path("FillRectilinear2-%d.svg", iRun), bbox_svg); // , scale_(1.)); @@ -1002,7 +1002,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angle // The same segment, it has to be vertical. myassert(iPrev1 == iPrev2); swap = contour1[iPrev1].y > contour1[iContour1].y; - #ifdef SLIC3R_DEBUG + #ifdef SLIC3R_DEBUG2 if (swap) printf("Swapping when single vertical segment\n"); #endif @@ -1018,7 +1018,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angle const Point *b = &contour1[iSegment1]; const Point *c = &contour2[iPrev2]; const Point *d = &contour2[iSegment2]; -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG2 const Point x1(sil.pos, sil.intersections[i-1].pos); const Point x2(sil.pos, sil.intersections[i ].pos); bool successive = false; @@ -1045,11 +1045,11 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angle myassert(iSegment1 == iPrev2 || iPrev1 == iSegment2); std::swap(c, d); myassert(a != c && b != c); -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG2 successive = true; #endif /* SLIC3R_DEBUG */ } -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG2 else if (b == d) { // The segments iSegment1 and iSegment2 are directly connected. myassert(iContour1 == iContour2); @@ -1061,7 +1061,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angle Orientation o = orient(*a, *b, *c); myassert(o != ORIENTATION_COLINEAR); swap = upper_more_left != (o == ORIENTATION_CW); -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG2 if (swap) printf(successive ? "Swapping when iContour1 == iContour2 and successive segments\n" : @@ -1106,7 +1106,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angle if (sil.intersections[i].type == sil.intersections[j-1].type) { // This has to be a corner point crossing the vertical line. // Remove the second intersection point. - #ifdef SLIC3R_DEBUG + #ifdef SLIC3R_DEBUG2 size_t iSegment2 = sil.intersections[j-1].iSegment; size_t iPrev2 = ((iSegment2 == 0) ? contour.size() : iSegment2) - 1; myassert(iSegment == iPrev2 || iSegment2 == iPrev); @@ -1152,7 +1152,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angle } #undef ASSERT_OR_RETURN -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG2 // Paint the segments and finalize the SVG file. for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) { SegmentedIntersectionLine &sil = segs[i_seg]; @@ -1519,7 +1519,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angle polyline_current = NULL; } -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG2 { { //::Slic3r::SVG svg(debug_out_path("FillRectilinear2-final-%03d.svg", iRun), bbox_svg); // , scale_(1.)); @@ -1546,7 +1546,7 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angle it->remove_duplicate_points(); } -#ifdef SLIC3R_DEBUG +#ifdef SLIC3R_DEBUG2 // Verify, that there are no duplicate points in the sequence. for (Polylines::iterator it = polylines_out.begin(); it != polylines_out.end(); ++ it) myassert(! it->has_duplicate_points()); @@ -1577,10 +1577,10 @@ Polylines FillGrid2::fill_surface(const Surface &surface) return polylines_out; } -Polylines FillTriangles::fill_surface(const Surface &surface) +Polylines FillTriangles2::fill_surface(const Surface &surface) { // Each linear fill covers 1/3 of the target coverage. - FillTriangles fill2 = *this; + FillTriangles2 fill2 = *this; fill2.density *= 0.333333333f; Polylines polylines_out; if (! fill2.fill_surface_by_lines(&surface, 0.f, 0., polylines_out) || @@ -1591,10 +1591,10 @@ Polylines FillTriangles::fill_surface(const Surface &surface) return polylines_out; } -Polylines FillStars::fill_surface(const Surface &surface) +Polylines FillStars2::fill_surface(const Surface &surface) { // Each linear fill covers 1/3 of the target coverage. - FillStars fill2 = *this; + FillStars2 fill2 = *this; fill2.density *= 0.333333333f; Polylines polylines_out; if (! fill2.fill_surface_by_lines(&surface, 0.f, 0., polylines_out) || @@ -1605,10 +1605,10 @@ Polylines FillStars::fill_surface(const Surface &surface) return polylines_out; } -Polylines FillCubic::fill_surface(const Surface &surface) +Polylines FillCubic2::fill_surface(const Surface &surface) { // Each linear fill covers 1/3 of the target coverage. - FillCubic fill2 = *this; + FillCubic2 fill2 = *this; fill2.density *= 0.333333333f; Polylines polylines_out; if (! fill2.fill_surface_by_lines(&surface, 0.f, z, polylines_out) || diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.hpp b/xs/src/libslic3r/Fill/FillRectilinear2.hpp index 50ecf6c38..a28ffc4c7 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear2.hpp +++ b/xs/src/libslic3r/Fill/FillRectilinear2.hpp @@ -32,11 +32,11 @@ protected: virtual float _layer_angle(size_t idx) const { return 0.f; } }; -class FillTriangles : public FillRectilinear2 +class FillTriangles2 : public FillRectilinear2 { public: - virtual Fill* clone() const { return new FillTriangles(*this); }; - virtual ~FillTriangles() {} + virtual Fill* clone() const { return new FillTriangles2(*this); }; + virtual ~FillTriangles2() {} virtual Polylines fill_surface(const Surface &surface); protected: @@ -44,11 +44,11 @@ protected: virtual float _layer_angle(size_t idx) const { return 0.f; } }; -class FillStars : public FillRectilinear2 +class FillStars2 : public FillRectilinear2 { public: - virtual Fill* clone() const { return new FillStars(*this); }; - virtual ~FillStars() {} + virtual Fill* clone() const { return new FillStars2(*this); }; + virtual ~FillStars2() {} virtual Polylines fill_surface(const Surface &surface); protected: @@ -56,11 +56,11 @@ protected: virtual float _layer_angle(size_t idx) const { return 0.f; } }; -class FillCubic : public FillRectilinear2 +class FillCubic2 : public FillRectilinear2 { public: - virtual Fill* clone() const { return new FillCubic(*this); }; - virtual ~FillCubic() {} + virtual Fill* clone() const { return new FillCubic2(*this); }; + virtual ~FillCubic2() {} virtual Polylines fill_surface(const Surface &surface); protected: diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp index 629129b1e..2ca3088b2 100644 --- a/xs/src/libslic3r/Geometry.cpp +++ b/xs/src/libslic3r/Geometry.cpp @@ -549,25 +549,6 @@ MedialAxis::build(ThickPolylines* polylines) // append polyline to result polylines->push_back(polyline); } - - #ifdef SLIC3R_DEBUG - { - char path[2048]; - static int iRun = 0; - sprintf(path, "out/MedialAxis-%d.svg", iRun ++); - //dump_voronoi_to_svg(this->lines, this->vd, polylines, path); - - - printf("Thick lines: "); - for (ThickPolylines::const_iterator it = polylines->begin(); it != polylines->end(); ++ it) { - ThickLines lines = it->thicklines(); - for (ThickLines::const_iterator it2 = lines.begin(); it2 != lines.end(); ++ it2) { - printf("%f,%f ", it2->a_width, it2->b_width); - } - } - printf("\n"); - } - #endif /* SLIC3R_DEBUG */ } void diff --git a/xs/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp index 5269a4c11..803549949 100644 --- a/xs/src/libslic3r/Point.cpp +++ b/xs/src/libslic3r/Point.cpp @@ -18,6 +18,12 @@ Point::operator==(const Point& rhs) const return this->coincides_with(rhs); } +bool +Point::operator<(const Point& rhs) const +{ + return this->x < rhs.x || this->y < rhs.y; +} + std::string Point::wkt() const { diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp index 7fd05ff48..daafe728e 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/xs/src/libslic3r/Point.hpp @@ -37,6 +37,7 @@ class Point return Point(scale_(x), scale_(y)); }; bool operator==(const Point& rhs) const; + bool operator<(const Point& rhs) const; std::string wkt() const; std::string dump_perl() const; void scale(double factor); diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 6771733ba..036e80e19 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -391,9 +391,6 @@ PrintConfigDef::PrintConfigDef() def->enum_values.push_back("rectilinear"); def->enum_values.push_back("alignedrectilinear"); def->enum_values.push_back("grid"); - def->enum_values.push_back("line"); - def->enum_values.push_back("rectilinear2"); - def->enum_values.push_back("grid2"); def->enum_values.push_back("triangles"); def->enum_values.push_back("stars"); def->enum_values.push_back("cubic"); @@ -406,9 +403,6 @@ PrintConfigDef::PrintConfigDef() def->enum_labels.push_back("Rectilinear"); def->enum_labels.push_back("Aligned Rectilinear"); def->enum_labels.push_back("Grid"); - def->enum_labels.push_back("Line"); - def->enum_labels.push_back("Rectilinear 2"); - def->enum_labels.push_back("Grid 2"); def->enum_labels.push_back("Triangles"); def->enum_labels.push_back("Stars"); def->enum_labels.push_back("Cubic"); @@ -418,7 +412,7 @@ PrintConfigDef::PrintConfigDef() def->enum_labels.push_back("Hilbert Curve"); def->enum_labels.push_back("Archimedean Chords"); def->enum_labels.push_back("Octagram Spiral"); - def->default_value = new ConfigOptionEnum(ipHoneycomb); + def->default_value = new ConfigOptionEnum(ipStars); def = this->add("first_layer_acceleration", coFloat); def->label = "First layer"; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 4e7f14e16..6bc2b6099 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -30,8 +30,8 @@ enum GCodeFlavor { }; enum InfillPattern { - ipRectilinear, ipGrid, ipLine, ipAlignedRectilinear, - ipRectilinear2, ipGrid2, ipTriangles, ipStars, ipCubic, + ipRectilinear, ipGrid, ipAlignedRectilinear, + ipTriangles, ipStars, ipCubic, ipConcentric, ipHoneycomb, ip3DHoneycomb, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, }; @@ -63,9 +63,6 @@ template<> inline t_config_enum_values ConfigOptionEnum::get_enum keys_map["rectilinear"] = ipRectilinear; keys_map["alignedrectilinear"] = ipAlignedRectilinear; keys_map["grid"] = ipGrid; - keys_map["line"] = ipLine; - keys_map["rectilinear2"] = ipRectilinear2; - keys_map["grid2"] = ipGrid2; keys_map["triangles"] = ipTriangles; keys_map["stars"] = ipStars; keys_map["cubic"] = ipCubic; diff --git a/xs/xsp/Filler.xsp b/xs/xsp/Filler.xsp index bd223a680..7a3e995d3 100644 --- a/xs/xsp/Filler.xsp +++ b/xs/xsp/Filler.xsp @@ -13,10 +13,17 @@ void set_bounding_box(BoundingBox *bbox) %code{% THIS->fill->bounding_box = *bbox; %}; + void set_spacing(coordf_t spacing) %code{% THIS->fill->spacing = spacing; %}; coordf_t spacing() %code{% RETVAL = THIS->fill->spacing; %}; + + void set_endpoints_overlap(float overlap) + %code{% THIS->fill->endpoints_overlap = overlap; %}; + float endpoints_overlap() + %code{% RETVAL = THIS->fill->endpoints_overlap; %}; + void set_layer_id(size_t layer_id) %code{% THIS->fill->layer_id = layer_id; %}; void set_z(coordf_t z) From 4b0766a82e1d678bf7770ddad9b24d9a91037fc1 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 12 Dec 2016 01:42:30 +0100 Subject: [PATCH 152/225] Removed Rectilinear2.* --- src/CMakeLists.txt | 1 - xs/MANIFEST | 2 - xs/src/libslic3r/Fill/Fill.cpp | 1 - xs/src/libslic3r/Fill/FillRectilinear2.cpp | 1712 -------------------- xs/src/libslic3r/Fill/FillRectilinear2.hpp | 81 - 5 files changed, 1797 deletions(-) delete mode 100644 xs/src/libslic3r/Fill/FillRectilinear2.cpp delete mode 100644 xs/src/libslic3r/Fill/FillRectilinear2.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d326be030..e85b52dc5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -50,7 +50,6 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/Fill/FillHoneycomb.cpp ${LIBDIR}/libslic3r/Fill/FillPlanePath.cpp ${LIBDIR}/libslic3r/Fill/FillRectilinear.cpp - ${LIBDIR}/libslic3r/Fill/FillRectilinear2.cpp ${LIBDIR}/libslic3r/Flow.cpp ${LIBDIR}/libslic3r/GCode.cpp ${LIBDIR}/libslic3r/GCodeSender.cpp diff --git a/xs/MANIFEST b/xs/MANIFEST index 963dde283..733bd09e0 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -40,8 +40,6 @@ src/libslic3r/Fill/FillPlanePath.cpp src/libslic3r/Fill/FillPlanePath.hpp src/libslic3r/Fill/FillRectilinear.cpp src/libslic3r/Fill/FillRectilinear.hpp -src/libslic3r/Fill/FillRectilinear2.cpp -src/libslic3r/Fill/FillRectilinear2.hpp src/libslic3r/Flow.cpp src/libslic3r/Flow.hpp src/libslic3r/GCode.cpp diff --git a/xs/src/libslic3r/Fill/Fill.cpp b/xs/src/libslic3r/Fill/Fill.cpp index f57a746d2..d0fee59f7 100644 --- a/xs/src/libslic3r/Fill/Fill.cpp +++ b/xs/src/libslic3r/Fill/Fill.cpp @@ -11,7 +11,6 @@ #include "Fill3DHoneycomb.hpp" #include "FillPlanePath.hpp" #include "FillRectilinear.hpp" -#include "FillRectilinear2.hpp" namespace Slic3r { diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.cpp b/xs/src/libslic3r/Fill/FillRectilinear2.cpp deleted file mode 100644 index b205bcc69..000000000 --- a/xs/src/libslic3r/Fill/FillRectilinear2.cpp +++ /dev/null @@ -1,1712 +0,0 @@ -#include -#include - -#include -#include -#include - -#include - -#include "../ClipperUtils.hpp" -#include "../ExPolygon.hpp" -#include "../Surface.hpp" - -#include "FillRectilinear2.hpp" - -// #define SLIC3R_DEBUG - -// Make assert active if SLIC3R_DEBUG -#ifdef SLIC3R_DEBUG2 - #undef NDEBUG - #include "SVG.hpp" -#endif - -#include - -// We want our version of assert. -#include "../libslic3r.h" - -#ifndef myassert -#define myassert assert -#endif - -namespace Slic3r { - -#ifndef clamp -template -static inline T clamp(T low, T high, T x) -{ - return std::max(low, std::min(high, x)); -} -#endif /* clamp */ - -#ifndef sqr -template -static inline T sqr(T x) -{ - return x * x; -} -#endif /* sqr */ - -#ifndef mag2 -static inline coordf_t mag2(const Point &p) -{ - return sqr(coordf_t(p.x)) + sqr(coordf_t(p.y)); -} -#endif /* mag2 */ - -#ifndef mag -static inline coordf_t mag(const Point &p) -{ - return std::sqrt(mag2(p)); -} -#endif /* mag */ - -enum Orientation -{ - ORIENTATION_CCW = 1, - ORIENTATION_CW = -1, - ORIENTATION_COLINEAR = 0 -}; - -// Return orientation of the three points (clockwise, counter-clockwise, colinear) -// The predicate is exact for the coord_t type, using 64bit signed integers for the temporaries. -//FIXME Make sure the temporaries do not overflow, -// which means, the coord_t types must not have some of the topmost bits utilized. -static inline Orientation orient(const Point &a, const Point &b, const Point &c) -{ - // BOOST_STATIC_ASSERT(sizeof(coord_t) * 2 == sizeof(int64_t)); - int64_t u = int64_t(b.x) * int64_t(c.y) - int64_t(b.y) * int64_t(c.x); - int64_t v = int64_t(a.x) * int64_t(c.y) - int64_t(a.y) * int64_t(c.x); - int64_t w = int64_t(a.x) * int64_t(b.y) - int64_t(a.y) * int64_t(b.x); - int64_t d = u - v + w; - return (d > 0) ? ORIENTATION_CCW : ((d == 0) ? ORIENTATION_COLINEAR : ORIENTATION_CW); -} - -// Return orientation of the polygon. -// The input polygon must not contain duplicate points. -static inline bool is_ccw(const Polygon &poly) -{ - // The polygon shall be at least a triangle. - myassert(poly.points.size() >= 3); - if (poly.points.size() < 3) - return true; - - // 1) Find the lowest lexicographical point. - size_t imin = 0; - for (size_t i = 1; i < poly.points.size(); ++ i) { - const Point &pmin = poly.points[imin]; - const Point &p = poly.points[i]; - if (p.x < pmin.x || (p.x == pmin.x && p.y < pmin.y)) - imin = i; - } - - // 2) Detect the orientation of the corner imin. - size_t iPrev = ((imin == 0) ? poly.points.size() : imin) - 1; - size_t iNext = ((imin + 1 == poly.points.size()) ? 0 : imin + 1); - Orientation o = orient(poly.points[iPrev], poly.points[imin], poly.points[iNext]); - // The lowest bottom point must not be collinear if the polygon does not contain duplicate points - // or overlapping segments. - myassert(o != ORIENTATION_COLINEAR); - return o == ORIENTATION_CCW; -} - -// Having a segment of a closed polygon, calculate its Euclidian length. -// The segment indices seg1 and seg2 signify an end point of an edge in the forward direction of the loop, -// therefore the point p1 lies on poly.points[seg1-1], poly.points[seg1] etc. -static inline coordf_t segment_length(const Polygon &poly, size_t seg1, const Point &p1, size_t seg2, const Point &p2) -{ -#ifdef SLIC3R_DEBUG2 - // Verify that p1 lies on seg1. This is difficult to verify precisely, - // but at least verify, that p1 lies in the bounding box of seg1. - for (size_t i = 0; i < 2; ++ i) { - size_t seg = (i == 0) ? seg1 : seg2; - Point px = (i == 0) ? p1 : p2; - Point pa = poly.points[((seg == 0) ? poly.points.size() : seg) - 1]; - Point pb = poly.points[seg]; - if (pa.x > pb.x) - std::swap(pa.x, pb.x); - if (pa.y > pb.y) - std::swap(pa.y, pb.y); - myassert(px.x >= pa.x && px.x <= pb.x); - myassert(px.y >= pa.y && px.y <= pb.y); - } -#endif /* SLIC3R_DEBUG */ - const Point *pPrev = &p1; - const Point *pThis = NULL; - coordf_t len = 0; - if (seg1 <= seg2) { - for (size_t i = seg1; i < seg2; ++ i, pPrev = pThis) - len += pPrev->distance_to(*(pThis = &poly.points[i])); - } else { - for (size_t i = seg1; i < poly.points.size(); ++ i, pPrev = pThis) - len += pPrev->distance_to(*(pThis = &poly.points[i])); - for (size_t i = 0; i < seg2; ++ i, pPrev = pThis) - len += pPrev->distance_to(*(pThis = &poly.points[i])); - } - len += pPrev->distance_to(p2); - return len; -} - -// Append a segment of a closed polygon to a polyline. -// The segment indices seg1 and seg2 signify an end point of an edge in the forward direction of the loop. -// Only insert intermediate points between seg1 and seg2. -static inline void polygon_segment_append(Points &out, const Polygon &polygon, size_t seg1, size_t seg2) -{ - if (seg1 == seg2) { - // Nothing to append from this segment. - } else if (seg1 < seg2) { - // Do not append a point pointed to by seg2. - out.insert(out.end(), polygon.points.begin() + seg1, polygon.points.begin() + seg2); - } else { - out.reserve(out.size() + seg2 + polygon.points.size() - seg1); - out.insert(out.end(), polygon.points.begin() + seg1, polygon.points.end()); - // Do not append a point pointed to by seg2. - out.insert(out.end(), polygon.points.begin(), polygon.points.begin() + seg2); - } -} - -// Append a segment of a closed polygon to a polyline. -// The segment indices seg1 and seg2 signify an end point of an edge in the forward direction of the loop, -// but this time the segment is traversed backward. -// Only insert intermediate points between seg1 and seg2. -static inline void polygon_segment_append_reversed(Points &out, const Polygon &polygon, size_t seg1, size_t seg2) -{ - if (seg1 >= seg2) { - out.reserve(seg1 - seg2); - for (size_t i = seg1; i > seg2; -- i) - out.push_back(polygon.points[i - 1]); - } else { - // it could be, that seg1 == seg2. In that case, append the complete loop. - out.reserve(out.size() + seg2 + polygon.points.size() - seg1); - for (size_t i = seg1; i > 0; -- i) - out.push_back(polygon.points[i - 1]); - for (size_t i = polygon.points.size(); i > seg2; -- i) - out.push_back(polygon.points[i - 1]); - } -} - -// Intersection point of a vertical line with a polygon segment. -class SegmentIntersection -{ -public: - SegmentIntersection() : - iContour(0), - iSegment(0), - pos_p(0), - pos_q(1), - type(UNKNOWN), - consumed_vertical_up(false), - consumed_perimeter_right(false) - {} - - // Index of a contour in ExPolygonWithOffset, with which this vertical line intersects. - size_t iContour; - // Index of a segment in iContour, with which this vertical line intersects. - size_t iSegment; - // y position of the intersection, ratinal number. - int64_t pos_p; - uint32_t pos_q; - - coord_t pos() const { - // Division rounds both positive and negative down to zero. - // Add half of q for an arithmetic rounding effect. - int64_t p = pos_p; - if (p < 0) - p -= int64_t(pos_q>>1); - else - p += int64_t(pos_q>>1); - return coord_t(p / int64_t(pos_q)); - } - - // Kind of intersection. With the original contour, or with the inner offestted contour? - // A vertical segment will be at least intersected by OUTER_LOW, OUTER_HIGH, - // but it could be intersected with OUTER_LOW, INNER_LOW, INNER_HIGH, OUTER_HIGH, - // and there may be more than one pair of INNER_LOW, INNER_HIGH between OUTER_LOW, OUTER_HIGH. - enum SegmentIntersectionType { - OUTER_LOW = 0, - OUTER_HIGH = 1, - INNER_LOW = 2, - INNER_HIGH = 3, - UNKNOWN = -1 - }; - SegmentIntersectionType type; - - // Was this segment along the y axis consumed? - // Up means up along the vertical segment. - bool consumed_vertical_up; - // Was a segment of the inner perimeter contour consumed? - // Right means right from the vertical segment. - bool consumed_perimeter_right; - - // For the INNER_LOW type, this point may be connected to another INNER_LOW point following a perimeter contour. - // For the INNER_HIGH type, this point may be connected to another INNER_HIGH point following a perimeter contour. - // If INNER_LOW is connected to INNER_HIGH or vice versa, - // one has to make sure the vertical infill line does not overlap with the connecting perimeter line. - bool is_inner() const { return type == INNER_LOW || type == INNER_HIGH; } - bool is_outer() const { return type == OUTER_LOW || type == OUTER_HIGH; } - bool is_low () const { return type == INNER_LOW || type == OUTER_LOW; } - bool is_high () const { return type == INNER_HIGH || type == OUTER_HIGH; } - - // Compare two y intersection points given by rational numbers. - // Note that the rational number is given as pos_p/pos_q, where pos_p is int64 and pos_q is uint32. - // This function calculates pos_p * other.pos_q < other.pos_p * pos_q as a 48bit number. - // We don't use 128bit intrinsic data types as these are usually not supported by 32bit compilers and - // we don't need the full 128bit precision anyway. - bool operator<(const SegmentIntersection &other) const - { - assert(pos_q > 0); - assert(other.pos_q > 0); - if (pos_p == 0 || other.pos_p == 0) { - // Because the denominators are positive and one of the nominators is zero, - // following simple statement holds. - return pos_p < other.pos_p; - } else { - // None of the nominators is zero. - char sign1 = (pos_p > 0) ? 1 : -1; - char sign2 = (other.pos_p > 0) ? 1 : -1; - char signs = sign1 * sign2; - assert(signs == 1 || signs == -1); - if (signs < 0) { - // The nominators have different signs. - return sign1 < 0; - } else { - // The nominators have the same sign. - // Absolute values - uint64_t p1, p2; - if (sign1 > 0) { - p1 = uint64_t(pos_p); - p2 = uint64_t(other.pos_p); - } else { - p1 = uint64_t(- pos_p); - p2 = uint64_t(- other.pos_p); - }; - // Multiply low and high 32bit words of p1 by other_pos.q - // 32bit x 32bit => 64bit - // l_hi and l_lo overlap by 32 bits. - uint64_t l_hi = (p1 >> 32) * uint64_t(other.pos_q); - uint64_t l_lo = (p1 & 0xffffffffll) * uint64_t(other.pos_q); - l_hi += (l_lo >> 32); - uint64_t r_hi = (p2 >> 32) * uint64_t(pos_q); - uint64_t r_lo = (p2 & 0xffffffffll) * uint64_t(pos_q); - r_hi += (r_lo >> 32); - // Compare the high 64 bits. - if (l_hi == r_hi) { - // Compare the low 32 bits. - l_lo &= 0xffffffffll; - r_lo &= 0xffffffffll; - return (sign1 < 0) ? (l_lo > r_lo) : (l_lo < r_lo); - } - return (sign1 < 0) ? (l_hi > r_hi) : (l_hi < r_hi); - } - } - } - - bool operator==(const SegmentIntersection &other) const - { - assert(pos_q > 0); - assert(other.pos_q > 0); - if (pos_p == 0 || other.pos_p == 0) { - // Because the denominators are positive and one of the nominators is zero, - // following simple statement holds. - return pos_p == other.pos_p; - } - - // None of the nominators is zero, none of the denominators is zero. - bool positive = pos_p > 0; - if (positive != (other.pos_p > 0)) - return false; - // The nominators have the same sign. - // Absolute values - uint64_t p1 = positive ? uint64_t(pos_p) : uint64_t(- pos_p); - uint64_t p2 = positive ? uint64_t(other.pos_p) : uint64_t(- other.pos_p); - // Multiply low and high 32bit words of p1 by other_pos.q - // 32bit x 32bit => 64bit - // l_hi and l_lo overlap by 32 bits. - uint64_t l_lo = (p1 & 0xffffffffll) * uint64_t(other.pos_q); - uint64_t r_lo = (p2 & 0xffffffffll) * uint64_t(pos_q); - if (l_lo != r_lo) - return false; - uint64_t l_hi = (p1 >> 32) * uint64_t(other.pos_q); - uint64_t r_hi = (p2 >> 32) * uint64_t(pos_q); - return l_hi + (l_lo >> 32) == r_hi + (r_lo >> 32); - } -}; - -// A vertical line with intersection points with polygons. -class SegmentedIntersectionLine -{ -public: - // Index of this vertical intersection line. - size_t idx; - // x position of this vertical intersection line. - coord_t pos; - // List of intersection points with polygons, sorted increasingly by the y axis. - std::vector intersections; -}; - -// A container maintaining an expolygon with its inner offsetted polygon. -// The purpose of the inner offsetted polygon is to provide segments to connect the infill lines. -struct ExPolygonWithOffset -{ -public: - ExPolygonWithOffset( - const ExPolygon &expolygon, - float angle, - coord_t aoffset1, - coord_t aoffset2) - { - // Copy and rotate the source polygons. - polygons_src = expolygon; - polygons_src.contour.rotate(angle); - for (Polygons::iterator it = polygons_src.holes.begin(); it != polygons_src.holes.end(); ++ it) - it->rotate(angle); - - double mitterLimit = 3.; - // for the infill pattern, don't cut the corners. - // default miterLimt = 3 - //double mitterLimit = 10.; - myassert(aoffset1 < 0); - myassert(aoffset2 < 0); - myassert(aoffset2 < aoffset1); -// bool sticks_removed = remove_sticks(polygons_src); -// if (sticks_removed) printf("Sticks removed!\n"); - polygons_outer = offset(polygons_src, aoffset1, - CLIPPER_OFFSET_SCALE, - ClipperLib::jtMiter, - mitterLimit); - polygons_inner = offset(polygons_outer, aoffset2 - aoffset1, - CLIPPER_OFFSET_SCALE, - ClipperLib::jtMiter, - mitterLimit); - // Filter out contours with zero area or small area, contours with 2 points only. - const double min_area_threshold = 0.01 * aoffset2 * aoffset2; - remove_small(polygons_outer, min_area_threshold); - remove_small(polygons_inner, min_area_threshold); - remove_sticks(polygons_outer); - remove_sticks(polygons_inner); - n_contours_outer = polygons_outer.size(); - n_contours_inner = polygons_inner.size(); - n_contours = n_contours_outer + n_contours_inner; - polygons_ccw.assign(n_contours, false); - for (size_t i = 0; i < n_contours; ++ i) { - contour(i).remove_duplicate_points(); - myassert(! contour(i).has_duplicate_points()); - polygons_ccw[i] = is_ccw(contour(i)); - } - } - - // Any contour with offset1 - bool is_contour_outer(size_t idx) const { return idx < n_contours_outer; } - // Any contour with offset2 - bool is_contour_inner(size_t idx) const { return idx >= n_contours_outer; } - - const Polygon& contour(size_t idx) const - { return is_contour_outer(idx) ? polygons_outer[idx] : polygons_inner[idx - n_contours_outer]; } - - Polygon& contour(size_t idx) - { return is_contour_outer(idx) ? polygons_outer[idx] : polygons_inner[idx - n_contours_outer]; } - - bool is_contour_ccw(size_t idx) const { return polygons_ccw[idx]; } - -#ifdef SLIC3R_DEBUG2 - void export_to_svg(Slic3r::SVG &svg) { - //svg.draw_outline(polygons_src, "black"); - //svg.draw_outline(polygons_outer, "green"); - //svg.draw_outline(polygons_inner, "brown"); - } -#endif /* SLIC3R_DEBUG */ - - ExPolygon polygons_src; - Polygons polygons_outer; - Polygons polygons_inner; - - size_t n_contours_outer; - size_t n_contours_inner; - size_t n_contours; - -protected: - // For each polygon of polygons_inner, remember its orientation. - std::vector polygons_ccw; -}; - -static inline int distance_of_segmens(const Polygon &poly, size_t seg1, size_t seg2, bool forward) -{ - int d = int(seg2) - int(seg1); - if (! forward) - d = - d; - if (d < 0) - d += int(poly.points.size()); - return d; -} - -// For a vertical line, an inner contour and an intersection point, -// find an intersection point on the previous resp. next vertical line. -// The intersection point is connected with the prev resp. next intersection point with iInnerContour. -// Return -1 if there is no such point on the previous resp. next vertical line. -static inline int intersection_on_prev_next_vertical_line( - const ExPolygonWithOffset &poly_with_offset, - const std::vector &segs, - size_t iVerticalLine, - size_t iInnerContour, - size_t iIntersection, - bool dir_is_next) -{ - size_t iVerticalLineOther = iVerticalLine; - if (dir_is_next) { - if (++ iVerticalLineOther == segs.size()) - // No successive vertical line. - return -1; - } else if (iVerticalLineOther -- == 0) { - // No preceding vertical line. - return -1; - } - - const SegmentedIntersectionLine &il = segs[iVerticalLine]; - const SegmentIntersection &itsct = il.intersections[iIntersection]; - const SegmentedIntersectionLine &il2 = segs[iVerticalLineOther]; - const Polygon &poly = poly_with_offset.contour(iInnerContour); -// const bool ccw = poly_with_offset.is_contour_ccw(iInnerContour); - const bool forward = itsct.is_low() == dir_is_next; - // Resulting index of an intersection point on il2. - int out = -1; - // Find an intersection point on iVerticalLineOther, intersecting iInnerContour - // at the same orientation as iIntersection, and being closest to iIntersection - // in the number of contour segments, when following the direction of the contour. - int dmin = std::numeric_limits::max(); - for (size_t i = 0; i < il2.intersections.size(); ++ i) { - const SegmentIntersection &itsct2 = il2.intersections[i]; - if (itsct.iContour == itsct2.iContour && itsct.type == itsct2.type) { - /* - if (itsct.is_low()) { - myassert(itsct.type == SegmentIntersection::INNER_LOW); - myassert(iIntersection > 0); - myassert(il.intersections[iIntersection-1].type == SegmentIntersection::OUTER_LOW); - myassert(i > 0); - if (il2.intersections[i-1].is_inner()) - // Take only the lowest inner intersection point. - continue; - myassert(il2.intersections[i-1].type == SegmentIntersection::OUTER_LOW); - } else { - myassert(itsct.type == SegmentIntersection::INNER_HIGH); - myassert(iIntersection+1 < il.intersections.size()); - myassert(il.intersections[iIntersection+1].type == SegmentIntersection::OUTER_HIGH); - myassert(i+1 < il2.intersections.size()); - if (il2.intersections[i+1].is_inner()) - // Take only the highest inner intersection point. - continue; - myassert(il2.intersections[i+1].type == SegmentIntersection::OUTER_HIGH); - } - */ - // The intersection points lie on the same contour and have the same orientation. - // Find the intersection point with a shortest path in the direction of the contour. - int d = distance_of_segmens(poly, itsct.iSegment, itsct2.iSegment, forward); - if (d < dmin) { - out = i; - dmin = d; - } - } - } - //FIXME this routine is not asymptotic optimal, it will be slow if there are many intersection points along the line. - return out; -} - -static inline int intersection_on_prev_vertical_line( - const ExPolygonWithOffset &poly_with_offset, - const std::vector &segs, - size_t iVerticalLine, - size_t iInnerContour, - size_t iIntersection) -{ - return intersection_on_prev_next_vertical_line(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, false); -} - -static inline int intersection_on_next_vertical_line( - const ExPolygonWithOffset &poly_with_offset, - const std::vector &segs, - size_t iVerticalLine, - size_t iInnerContour, - size_t iIntersection) -{ - return intersection_on_prev_next_vertical_line(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, true); -} - -enum IntersectionTypeOtherVLine { - // There is no connection point on the other vertical line. - INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED = -1, - // Connection point on the other vertical segment was found - // and it could be followed. - INTERSECTION_TYPE_OTHER_VLINE_OK = 0, - // The connection segment connects to a middle of a vertical segment. - // Cannot follow. - INTERSECTION_TYPE_OTHER_VLINE_INNER, - // Cannot extend the contor to this intersection point as either the connection segment - // or the succeeding vertical segment were already consumed. - INTERSECTION_TYPE_OTHER_VLINE_CONSUMED, - // Not the first intersection along the contor. This intersection point - // has been preceded by an intersection point along the vertical line. - INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST, -}; - -// Find an intersection on a previous line, but return -1, if the connecting segment of a perimeter was already extruded. -static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical_line( - const std::vector &segs, - size_t iVerticalLine, - size_t iIntersection, - int iIntersectionOther, - bool dir_is_next) -{ - // This routine will propose a connecting line even if the connecting perimeter segment intersects - // iVertical line multiple times before reaching iIntersectionOther. - if (iIntersectionOther == -1) - return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED; - myassert(dir_is_next ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0)); - const SegmentedIntersectionLine &il_this = segs[iVerticalLine]; - const SegmentIntersection &itsct_this = il_this.intersections[iIntersection]; - const SegmentedIntersectionLine &il_other = segs[dir_is_next ? (iVerticalLine+1) : (iVerticalLine-1)]; - const SegmentIntersection &itsct_other = il_other.intersections[iIntersectionOther]; - myassert(itsct_other.is_inner()); - myassert(iIntersectionOther > 0); - myassert(iIntersectionOther + 1 < (int)il_other.intersections.size()); - // Is iIntersectionOther at the boundary of a vertical segment? - const SegmentIntersection &itsct_other2 = il_other.intersections[itsct_other.is_low() ? iIntersectionOther - 1 : iIntersectionOther + 1]; - if (itsct_other2.is_inner()) - // Cannot follow a perimeter segment into the middle of another vertical segment. - // Only perimeter segments connecting to the end of a vertical segment are followed. - return INTERSECTION_TYPE_OTHER_VLINE_INNER; - myassert(itsct_other.is_low() == itsct_other2.is_low()); - if (dir_is_next ? itsct_this.consumed_perimeter_right : itsct_other.consumed_perimeter_right) - // This perimeter segment was already consumed. - return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED; - if (itsct_other.is_low() ? itsct_other.consumed_vertical_up : il_other.intersections[iIntersectionOther-1].consumed_vertical_up) - // This vertical segment was already consumed. - return INTERSECTION_TYPE_OTHER_VLINE_CONSUMED; - return INTERSECTION_TYPE_OTHER_VLINE_OK; -} - -static inline IntersectionTypeOtherVLine intersection_type_on_prev_vertical_line( - const std::vector &segs, - size_t iVerticalLine, - size_t iIntersection, - size_t iIntersectionPrev) -{ - return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, iIntersectionPrev, false); -} - -static inline IntersectionTypeOtherVLine intersection_type_on_next_vertical_line( - const std::vector &segs, - size_t iVerticalLine, - size_t iIntersection, - size_t iIntersectionNext) -{ - return intersection_type_on_prev_next_vertical_line(segs, iVerticalLine, iIntersection, iIntersectionNext, true); -} - -// Measure an Euclidian length of a perimeter segment when going from iIntersection to iIntersection2. -static inline coordf_t measure_perimeter_prev_next_segment_length( - const ExPolygonWithOffset &poly_with_offset, - const std::vector &segs, - size_t iVerticalLine, - size_t iInnerContour, - size_t iIntersection, - size_t iIntersection2, - bool dir_is_next) -{ - size_t iVerticalLineOther = iVerticalLine; - if (dir_is_next) { - if (++ iVerticalLineOther == segs.size()) - // No successive vertical line. - return coordf_t(-1); - } else if (iVerticalLineOther -- == 0) { - // No preceding vertical line. - return coordf_t(-1); - } - - const SegmentedIntersectionLine &il = segs[iVerticalLine]; - const SegmentIntersection &itsct = il.intersections[iIntersection]; - const SegmentedIntersectionLine &il2 = segs[iVerticalLineOther]; - const SegmentIntersection &itsct2 = il2.intersections[iIntersection2]; - const Polygon &poly = poly_with_offset.contour(iInnerContour); -// const bool ccw = poly_with_offset.is_contour_ccw(iInnerContour); - myassert(itsct.type == itsct2.type); - myassert(itsct.iContour == itsct2.iContour); - myassert(itsct.is_inner()); - const bool forward = itsct.is_low() == dir_is_next; - - Point p1(il.pos, itsct.pos()); - Point p2(il2.pos, itsct2.pos()); - return forward ? - segment_length(poly, itsct .iSegment, p1, itsct2.iSegment, p2) : - segment_length(poly, itsct2.iSegment, p2, itsct .iSegment, p1); -} - -static inline coordf_t measure_perimeter_prev_segment_length( - const ExPolygonWithOffset &poly_with_offset, - const std::vector &segs, - size_t iVerticalLine, - size_t iInnerContour, - size_t iIntersection, - size_t iIntersection2) -{ - return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, iIntersection2, false); -} - -static inline coordf_t measure_perimeter_next_segment_length( - const ExPolygonWithOffset &poly_with_offset, - const std::vector &segs, - size_t iVerticalLine, - size_t iInnerContour, - size_t iIntersection, - size_t iIntersection2) -{ - return measure_perimeter_prev_next_segment_length(poly_with_offset, segs, iVerticalLine, iInnerContour, iIntersection, iIntersection2, true); -} - -// Append the points of a perimeter segment when going from iIntersection to iIntersection2. -// The first point (the point of iIntersection) will not be inserted, -// the last point will be inserted. -static inline void emit_perimeter_prev_next_segment( - const ExPolygonWithOffset &poly_with_offset, - const std::vector &segs, - size_t iVerticalLine, - size_t iInnerContour, - size_t iIntersection, - size_t iIntersection2, - Polyline &out, - bool dir_is_next) -{ - size_t iVerticalLineOther = iVerticalLine; - if (dir_is_next) { - ++ iVerticalLineOther; - myassert(iVerticalLineOther < segs.size()); - } else { - myassert(iVerticalLineOther > 0); - -- iVerticalLineOther; - } - - const SegmentedIntersectionLine &il = segs[iVerticalLine]; - const SegmentIntersection &itsct = il.intersections[iIntersection]; - const SegmentedIntersectionLine &il2 = segs[iVerticalLineOther]; - const SegmentIntersection &itsct2 = il2.intersections[iIntersection2]; - const Polygon &poly = poly_with_offset.contour(iInnerContour); -// const bool ccw = poly_with_offset.is_contour_ccw(iInnerContour); - myassert(itsct.type == itsct2.type); - myassert(itsct.iContour == itsct2.iContour); - myassert(itsct.is_inner()); - const bool forward = itsct.is_low() == dir_is_next; - // Do not append the first point. - // out.points.push_back(Point(il.pos, itsct.pos)); - if (forward) - polygon_segment_append(out.points, poly, itsct.iSegment, itsct2.iSegment); - else - polygon_segment_append_reversed(out.points, poly, itsct.iSegment, itsct2.iSegment); - // Append the last point. - out.points.push_back(Point(il2.pos, itsct2.pos())); -} - -static inline coordf_t measure_perimeter_segment_on_vertical_line_length( - const ExPolygonWithOffset &poly_with_offset, - const std::vector &segs, - size_t iVerticalLine, - size_t iInnerContour, - size_t iIntersection, - size_t iIntersection2, - bool forward) -{ - const SegmentedIntersectionLine &il = segs[iVerticalLine]; - const SegmentIntersection &itsct = il.intersections[iIntersection]; - const SegmentIntersection &itsct2 = il.intersections[iIntersection2]; - const Polygon &poly = poly_with_offset.contour(iInnerContour); - myassert(itsct.is_inner()); - myassert(itsct2.is_inner()); - myassert(itsct.type != itsct2.type); - myassert(itsct.iContour == iInnerContour); - myassert(itsct.iContour == itsct2.iContour); - Point p1(il.pos, itsct.pos()); - Point p2(il.pos, itsct2.pos()); - return forward ? - segment_length(poly, itsct .iSegment, p1, itsct2.iSegment, p2) : - segment_length(poly, itsct2.iSegment, p2, itsct .iSegment, p1); -} - -// Append the points of a perimeter segment when going from iIntersection to iIntersection2. -// The first point (the point of iIntersection) will not be inserted, -// the last point will be inserted. -static inline void emit_perimeter_segment_on_vertical_line( - const ExPolygonWithOffset &poly_with_offset, - const std::vector &segs, - size_t iVerticalLine, - size_t iInnerContour, - size_t iIntersection, - size_t iIntersection2, - Polyline &out, - bool forward) -{ - const SegmentedIntersectionLine &il = segs[iVerticalLine]; - const SegmentIntersection &itsct = il.intersections[iIntersection]; - const SegmentIntersection &itsct2 = il.intersections[iIntersection2]; - const Polygon &poly = poly_with_offset.contour(iInnerContour); - myassert(itsct.is_inner()); - myassert(itsct2.is_inner()); - myassert(itsct.type != itsct2.type); - myassert(itsct.iContour == iInnerContour); - myassert(itsct.iContour == itsct2.iContour); - // Do not append the first point. - // out.points.push_back(Point(il.pos, itsct.pos)); - if (forward) - polygon_segment_append(out.points, poly, itsct.iSegment, itsct2.iSegment); - else - polygon_segment_append_reversed(out.points, poly, itsct.iSegment, itsct2.iSegment); - // Append the last point. - out.points.push_back(Point(il.pos, itsct2.pos())); -} - -//TBD: For precise infill, measure the area of a slab spanned by an infill line. -/* -static inline float measure_outer_contour_slab( - const ExPolygonWithOffset &poly_with_offset, - const std::vector &segs, - size_t i_vline, - size_t iIntersection) -{ - const SegmentedIntersectionLine &il = segs[i_vline]; - const SegmentIntersection &itsct = il.intersections[i_vline]; - const SegmentIntersection &itsct2 = il.intersections[iIntersection2]; - const Polygon &poly = poly_with_offset.contour((itsct.iContour); - myassert(itsct.is_outer()); - myassert(itsct2.is_outer()); - myassert(itsct.type != itsct2.type); - myassert(itsct.iContour == itsct2.iContour); - if (! itsct.is_outer() || ! itsct2.is_outer() || itsct.type == itsct2.type || itsct.iContour != itsct2.iContour) - // Error, return zero area. - return 0.f; - - // Find possible connection points on the previous / next vertical line. - int iPrev = intersection_on_prev_vertical_line(poly_with_offset, segs, i_vline, itsct.iContour, i_intersection); - int iNext = intersection_on_next_vertical_line(poly_with_offset, segs, i_vline, itsct.iContour, i_intersection); - // Find possible connection points on the same vertical line. - int iAbove = iBelow = -1; - // Does the perimeter intersect the current vertical line above intrsctn? - for (size_t i = i_intersection + 1; i + 1 < seg.intersections.size(); ++ i) - if (seg.intersections[i].iContour == itsct.iContour) - { iAbove = i; break; } - // Does the perimeter intersect the current vertical line below intrsctn? - for (int i = int(i_intersection) - 1; i > 0; -- i) - if (seg.intersections[i].iContour == itsct.iContour) - { iBelow = i; break; } - - if (iSegAbove != -1 && seg.intersections[iAbove].type == SegmentIntersection::OUTER_HIGH) { - // Invalidate iPrev resp. iNext, if the perimeter crosses the current vertical line earlier than iPrev resp. iNext. - // The perimeter contour orientation. - const Polygon &poly = poly_with_offset.contour(itsct.iContour); - { - int d_horiz = (iPrev == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, segs[i_vline-1].intersections[iPrev].iSegment, itsct.iSegment, true); - int d_down = (iBelow == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, iSegBelow, itsct.iSegment, true); - int d_up = (iAbove == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, iSegAbove, itsct.iSegment, true); - if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && d_horiz > std::min(d_down, d_up)) - // The vertical crossing comes eralier than the prev crossing. - // Disable the perimeter going back. - intrsctn_type_prev = INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST; - if (d_up > std::min(d_horiz, d_down)) - // The horizontal crossing comes earlier than the vertical crossing. - vert_seg_dir_valid_mask &= ~DIR_BACKWARD; - } - { - int d_horiz = (iNext == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, itsct.iSegment, segs[i_vline+1].intersections[iNext].iSegment, true); - int d_down = (iSegBelow == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, itsct.iSegment, iSegBelow, true); - int d_up = (iSegAbove == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, itsct.iSegment, iSegAbove, true); - if (d_up > std::min(d_horiz, d_down)) - // The horizontal crossing comes earlier than the vertical crossing. - vert_seg_dir_valid_mask &= ~DIR_FORWARD; - } - } -} -*/ - -enum DirectionMask -{ - DIR_FORWARD = 1, - DIR_BACKWARD = 2 -}; - -bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, float angleBase, float pattern_shift, Polylines &polylines_out) -{ - // At the end, only the new polylines will be rotated back. - size_t n_polylines_out_initial = polylines_out.size(); - - // Shrink the input polygon a bit first to not push the infill lines out of the perimeters. -// const float INFILL_OVERLAP_OVER_SPACING = 0.3f; - const float INFILL_OVERLAP_OVER_SPACING = 0.45f; - myassert(INFILL_OVERLAP_OVER_SPACING > 0 && INFILL_OVERLAP_OVER_SPACING < 0.5f); - - // Rotate polygons so that we can work with vertical lines here - direction_t rotate_vector = this->_infill_direction(*surface); - rotate_vector.first += angleBase; - - myassert(this->density > 0.0001f && this->density <= 1.f); - coord_t line_spacing = coord_t(scale_(this->spacing) / this->density); - - // On the polygons of poly_with_offset, the infill lines will be connected. - ExPolygonWithOffset poly_with_offset( - surface->expolygon, - - rotate_vector.first, - scale_(- (0.5 - INFILL_OVERLAP_OVER_SPACING) * this->spacing), - scale_(- 0.5 * this->spacing)); - if (poly_with_offset.n_contours_inner == 0) { - // Not a single infill line fits. - //FIXME maybe one shall trigger the gap fill here? - return true; - } - - BoundingBox bounding_box(poly_with_offset.polygons_src); - - // define flow spacing according to requested density - bool full_infill = this->density > 0.9999f; - if (full_infill && !this->dont_adjust) { - line_spacing = this->adjust_solid_spacing(bounding_box.size().x, line_spacing); - this->spacing = unscale(line_spacing); - } else { - // extend bounding box so that our pattern will be aligned with other layers - // Transform the reference point to the rotated coordinate system. - Point refpt = rotate_vector.second.rotated(- rotate_vector.first); - // _align_to_grid will not work correctly with positive pattern_shift. - coord_t pattern_shift_scaled = coord_t(scale_(pattern_shift)) % line_spacing; - refpt.x -= (pattern_shift_scaled > 0) ? pattern_shift_scaled : (line_spacing + pattern_shift_scaled); - bounding_box.min.align_to_grid( - Point(line_spacing, line_spacing), - refpt - ); - } - - // Intersect a set of euqally spaced vertical lines wiht expolygon. - // n_vlines = ceil(bbox_width / line_spacing) - size_t n_vlines = (bounding_box.max.x - bounding_box.min.x + line_spacing - 1) / line_spacing; - coord_t x0 = bounding_box.min.x + (line_spacing + SCALED_EPSILON) / 2; - -#ifdef SLIC3R_DEBUG2 - static int iRun = 0; - BoundingBox bbox_svg(to_points(poly_with_offset.polygons_outer)); - //::Slic3r::SVG svg(debug_out_path("FillRectilinear2-%d.svg", iRun), bbox_svg); // , scale_(1.)); - //poly_with_offset.export_to_svg(svg); - { - //::Slic3r::SVG svg(debug_out_path("FillRectilinear2-initial-%d.svg", iRun), bbox_svg); // , scale_(1.)); - //poly_with_offset.export_to_svg(svg); - } - iRun ++; -#endif /* SLIC3R_DEBUG */ - - // For each contour - // Allocate storage for the segments. - std::vector segs(n_vlines, SegmentedIntersectionLine()); - for (size_t i = 0; i < n_vlines; ++ i) { - segs[i].idx = i; - segs[i].pos = x0 + i * line_spacing; - } - for (size_t iContour = 0; iContour < poly_with_offset.n_contours; ++ iContour) { - const Points &contour = poly_with_offset.contour(iContour).points; - if (contour.size() < 2) - continue; - // For each segment - for (size_t iSegment = 0; iSegment < contour.size(); ++ iSegment) { - size_t iPrev = ((iSegment == 0) ? contour.size() : iSegment) - 1; - const Point &p1 = contour[iPrev]; - const Point &p2 = contour[iSegment]; - // Which of the equally spaced vertical lines is intersected by this segment? - coord_t l = p1.x; - coord_t r = p2.x; - if (l > r) - std::swap(l, r); - // il, ir are the left / right indices of vertical lines intersecting a segment - int il = (l - x0) / line_spacing; - while (il * line_spacing + x0 < l) - ++ il; - il = std::max(int(0), il); - int ir = (r - x0 + line_spacing) / line_spacing; - while (ir * line_spacing + x0 > r) - -- ir; - ir = std::min(int(segs.size()) - 1, ir); - if (il > ir) - // No vertical line intersects this segment. - continue; - myassert(il >= 0 && il < (int)segs.size()); - myassert(ir >= 0 && ir < (int)segs.size()); - for (int i = il; i <= ir; ++ i) { - coord_t this_x = segs[i].pos; - assert(this_x == i * line_spacing + x0); - SegmentIntersection is; - is.iContour = iContour; - is.iSegment = iSegment; - myassert(l <= this_x); - myassert(r >= this_x); - // Calculate the intersection position in y axis. x is known. - if (p1.x == this_x) { - if (p2.x == this_x) { - // Ignore strictly vertical segments. - continue; - } - is.pos_p = p1.y; - is.pos_q = 1; - } else if (p2.x == this_x) { - is.pos_p = p2.y; - is.pos_q = 1; - } else { - // First calculate the intersection parameter 't' as a rational number with non negative denominator. - if (p2.x > p1.x) { - is.pos_p = this_x - p1.x; - is.pos_q = p2.x - p1.x; - } else { - is.pos_p = p1.x - this_x; - is.pos_q = p1.x - p2.x; - } - myassert(is.pos_p >= 0 && is.pos_p <= is.pos_q); - // Make an intersection point from the 't'. - is.pos_p *= int64_t(p2.y - p1.y); - is.pos_p += p1.y * int64_t(is.pos_q); - } - // +-1 to take rounding into account. - myassert(is.pos() + 1 >= std::min(p1.y, p2.y)); - myassert(is.pos() <= std::max(p1.y, p2.y) + 1); - segs[i].intersections.push_back(is); - } - } - } - - // Sort the intersections along their segments, specify the intersection types. - for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) { - SegmentedIntersectionLine &sil = segs[i_seg]; - // Sort the intersection points using exact rational arithmetic. - std::sort(sil.intersections.begin(), sil.intersections.end()); - -#if 0 - // Verify the order, bubble sort the intersections until sorted. - bool modified = false; - do { - modified = false; - for (size_t i = 1; i < sil.intersections.size(); ++ i) { - size_t iContour1 = sil.intersections[i-1].iContour; - size_t iContour2 = sil.intersections[i].iContour; - const Points &contour1 = poly_with_offset.contour(iContour1).points; - const Points &contour2 = poly_with_offset.contour(iContour2).points; - size_t iSegment1 = sil.intersections[i-1].iSegment; - size_t iPrev1 = ((iSegment1 == 0) ? contour1.size() : iSegment1) - 1; - size_t iSegment2 = sil.intersections[i].iSegment; - size_t iPrev2 = ((iSegment2 == 0) ? contour2.size() : iSegment2) - 1; - bool swap = false; - if (iContour1 == iContour2 && iSegment1 == iSegment2) { - // The same segment, it has to be vertical. - myassert(iPrev1 == iPrev2); - swap = contour1[iPrev1].y > contour1[iContour1].y; - #ifdef SLIC3R_DEBUG2 - if (swap) - printf("Swapping when single vertical segment\n"); - #endif - } else { - // Segments are in a general position. Here an exact airthmetics may come into play. - coord_t y1max = std::max(contour1[iPrev1].y, contour1[iSegment1].y); - coord_t y2min = std::min(contour2[iPrev2].y, contour2[iSegment2].y); - if (y1max < y2min) { - // The segments are separated, nothing to do. - } else { - // Use an exact predicate to verify, that segment1 is below segment2. - const Point *a = &contour1[iPrev1]; - const Point *b = &contour1[iSegment1]; - const Point *c = &contour2[iPrev2]; - const Point *d = &contour2[iSegment2]; -#ifdef SLIC3R_DEBUG2 - const Point x1(sil.pos, sil.intersections[i-1].pos); - const Point x2(sil.pos, sil.intersections[i ].pos); - bool successive = false; -#endif /* SLIC3R_DEBUG */ - // Sort the points in the two segments by x. - if (a->x > b->x) - std::swap(a, b); - if (c->x > d->x) - std::swap(c, d); - myassert(a->x <= sil.pos); - myassert(c->x <= sil.pos); - myassert(b->x >= sil.pos); - myassert(d->x >= sil.pos); - // Sort the two segments, so the segment will be on the left of . - bool upper_more_left = false; - if (a->x > c->x) { - upper_more_left = true; - std::swap(a, c); - std::swap(b, d); - } - if (a == c) { - // The segments iSegment1 and iSegment2 are directly connected. - myassert(iContour1 == iContour2); - myassert(iSegment1 == iPrev2 || iPrev1 == iSegment2); - std::swap(c, d); - myassert(a != c && b != c); -#ifdef SLIC3R_DEBUG2 - successive = true; -#endif /* SLIC3R_DEBUG */ - } -#ifdef SLIC3R_DEBUG2 - else if (b == d) { - // The segments iSegment1 and iSegment2 are directly connected. - myassert(iContour1 == iContour2); - myassert(iSegment1 == iPrev2 || iPrev1 == iSegment2); - myassert(a != c && b != c); - successive = true; - } -#endif /* SLIC3R_DEBUG */ - Orientation o = orient(*a, *b, *c); - myassert(o != ORIENTATION_COLINEAR); - swap = upper_more_left != (o == ORIENTATION_CW); -#ifdef SLIC3R_DEBUG2 - if (swap) - printf(successive ? - "Swapping when iContour1 == iContour2 and successive segments\n" : - "Swapping when exact predicate\n"); -#endif - } - } - if (swap) { - // Swap the intersection points, but keep the original positions, so they stay sorted by the y axis. - std::swap(sil.intersections[i-1], sil.intersections[i]); - std::swap(sil.intersections[i-1].pos_p, sil.intersections[i].pos_p); - std::swap(sil.intersections[i-1].pos_q, sil.intersections[i].pos_q); - modified = true; - } - } - } while (modified); -#endif - - // Assign the intersection types, remove duplicate or overlapping intersection points. - // When a loop vertex touches a vertical line, intersection point is generated for both segments. - // If such two segments are oriented equally, then one of them is removed. - // Otherwise the vertex is tangential to the vertical line and both segments are removed. - // The same rule applies, if the loop is pinched into a single point and this point touches the vertical line: - // The loop has a zero vertical size at the vertical line, therefore the intersection point is removed. - size_t j = 0; - for (size_t i = 0; i < sil.intersections.size(); ++ i) { - // What is the orientation of the segment at the intersection point? - size_t iContour = sil.intersections[i].iContour; - const Points &contour = poly_with_offset.contour(iContour).points; - size_t iSegment = sil.intersections[i].iSegment; - size_t iPrev = ((iSegment == 0) ? contour.size() : iSegment) - 1; - coord_t dir = contour[iSegment].x - contour[iPrev].x; - // bool ccw = poly_with_offset.is_contour_ccw(iContour); - // bool low = (dir > 0) == ccw; - bool low = dir > 0; - sil.intersections[i].type = poly_with_offset.is_contour_outer(iContour) ? - (low ? SegmentIntersection::OUTER_LOW : SegmentIntersection::OUTER_HIGH) : - (low ? SegmentIntersection::INNER_LOW : SegmentIntersection::INNER_HIGH); - if (j > 0 && - sil.intersections[i].pos() == sil.intersections[j-1].pos() && - sil.intersections[i].iContour == sil.intersections[j-1].iContour) { - if (sil.intersections[i].type == sil.intersections[j-1].type) { - // This has to be a corner point crossing the vertical line. - // Remove the second intersection point. - #ifdef SLIC3R_DEBUG2 - size_t iSegment2 = sil.intersections[j-1].iSegment; - size_t iPrev2 = ((iSegment2 == 0) ? contour.size() : iSegment2) - 1; - myassert(iSegment == iPrev2 || iSegment2 == iPrev); - #endif /* SLIC3R_DEBUG */ - } else { - // This is a loop returning to the same point. - // It may as well be a vertex of a loop touching this vertical line. - // Remove both the lines. - -- j; - } - } else { - if (j < i) - sil.intersections[j] = sil.intersections[i]; - ++ j; - } - //FIXME solve a degenerate case, where there is a vertical segment on this vertical line and the contour - // follows from left to right or vice versa, leading to low,low or high,high intersections. - } - // Shrink the list of intersections, if any of the intersection was removed during the classification. - if (j < sil.intersections.size()) - sil.intersections.erase(sil.intersections.begin() + j, sil.intersections.end()); - } - - // Verify the segments. If something is wrong, give up. -#define ASSERT_OR_RETURN(CONDITION) do { assert(CONDITION); if (! (CONDITION)) return false; } while (0) - for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) { - SegmentedIntersectionLine &sil = segs[i_seg]; - // The intersection points have to be even. - ASSERT_OR_RETURN((sil.intersections.size() & 1) == 0); - for (size_t i = 0; i < sil.intersections.size();) { - // An intersection segment crossing the bigger contour may cross the inner offsetted contour even number of times. - ASSERT_OR_RETURN(sil.intersections[i].type == SegmentIntersection::OUTER_LOW); - size_t j = i + 1; - ASSERT_OR_RETURN(j < sil.intersections.size()); - ASSERT_OR_RETURN(sil.intersections[j].type == SegmentIntersection::INNER_LOW || sil.intersections[j].type == SegmentIntersection::OUTER_HIGH); - for (; j < sil.intersections.size() && sil.intersections[j].is_inner(); ++ j) ; - ASSERT_OR_RETURN(j < sil.intersections.size()); - ASSERT_OR_RETURN((j & 1) == 1); - ASSERT_OR_RETURN(sil.intersections[j].type == SegmentIntersection::OUTER_HIGH); - ASSERT_OR_RETURN(i + 1 == j || sil.intersections[j - 1].type == SegmentIntersection::INNER_HIGH); - i = j + 1; - } - } -#undef ASSERT_OR_RETURN - -#ifdef SLIC3R_DEBUG2 - // Paint the segments and finalize the SVG file. - for (size_t i_seg = 0; i_seg < segs.size(); ++ i_seg) { - SegmentedIntersectionLine &sil = segs[i_seg]; - for (size_t i = 0; i < sil.intersections.size();) { - size_t j = i + 1; - for (; j < sil.intersections.size() && sil.intersections[j].is_inner(); ++ j) ; - if (i + 1 == j) { - //svg.draw(Line(Point(sil.pos, sil.intersections[i].pos()), Point(sil.pos, sil.intersections[j].pos())), "blue"); - } else { - //svg.draw(Line(Point(sil.pos, sil.intersections[i].pos()), Point(sil.pos, sil.intersections[i+1].pos())), "green"); - //svg.draw(Line(Point(sil.pos, sil.intersections[i+1].pos()), Point(sil.pos, sil.intersections[j-1].pos())), (j - i + 1 > 4) ? "yellow" : "magenta"); - //svg.draw(Line(Point(sil.pos, sil.intersections[j-1].pos()), Point(sil.pos, sil.intersections[j].pos())), "green"); - } - i = j + 1; - } - } - //svg.Close(); -#endif /* SLIC3R_DEBUG */ - - // For each outer only chords, measure their maximum distance to the bow of the outer contour. - // Mark an outer only chord as consumed, if the distance is low. - for (size_t i_vline = 0; i_vline < segs.size(); ++ i_vline) { - SegmentedIntersectionLine &seg = segs[i_vline]; - for (size_t i_intersection = 0; i_intersection + 1 < seg.intersections.size(); ++ i_intersection) { - if (seg.intersections[i_intersection].type == SegmentIntersection::OUTER_LOW && - seg.intersections[i_intersection+1].type == SegmentIntersection::OUTER_HIGH) { - bool consumed = false; -// if (full_infill) { -// measure_outer_contour_slab(poly_with_offset, segs, i_vline, i_ntersection); -// } else - consumed = true; - seg.intersections[i_intersection].consumed_vertical_up = consumed; - } - } - } - - // Now construct a graph. - // Find the first point. - // Naively one would expect to achieve best results by chaining the paths by the shortest distance, - // but that procedure does not create the longest continuous paths. - // A simple "sweep left to right" procedure achieves better results. - size_t i_vline = 0; - size_t i_intersection = size_t(-1); - // Follow the line, connect the lines into a graph. - // Until no new line could be added to the output path: - Point pointLast; - Polyline *polyline_current = NULL; - if (! polylines_out.empty()) - pointLast = polylines_out.back().points.back(); - for (;;) { - if (i_intersection == size_t(-1)) { - // The path has been interrupted. Find a next starting point, closest to the previous extruder position. - coordf_t dist2min = std::numeric_limits().max(); - for (size_t i_vline2 = 0; i_vline2 < segs.size(); ++ i_vline2) { - const SegmentedIntersectionLine &seg = segs[i_vline2]; - if (! seg.intersections.empty()) { - myassert(seg.intersections.size() > 1); - // Even number of intersections with the loops. - myassert((seg.intersections.size() & 1) == 0); - myassert(seg.intersections.front().type == SegmentIntersection::OUTER_LOW); - for (size_t i = 0; i < seg.intersections.size(); ++ i) { - const SegmentIntersection &intrsctn = seg.intersections[i]; - if (intrsctn.is_outer()) { - myassert(intrsctn.is_low() || i > 0); - bool consumed = intrsctn.is_low() ? - intrsctn.consumed_vertical_up : - seg.intersections[i-1].consumed_vertical_up; - if (! consumed) { - coordf_t dist2 = sqr(coordf_t(pointLast.x - seg.pos)) + sqr(coordf_t(pointLast.y - intrsctn.pos())); - if (dist2 < dist2min) { - dist2min = dist2; - i_vline = i_vline2; - i_intersection = i; - //FIXME We are taking the first left point always. Verify, that the caller chains the paths - // by a shortest distance, while reversing the paths if needed. - //if (polylines_out.empty()) - // Initial state, take the first line, which is the first from the left. - goto found; - } - } - } - } - } - } - if (i_intersection == size_t(-1)) - // We are finished. - break; - found: - // Start a new path. - polylines_out.push_back(Polyline()); - polyline_current = &polylines_out.back(); - // Emit the first point of a path. - pointLast = Point(segs[i_vline].pos, segs[i_vline].intersections[i_intersection].pos()); - polyline_current->points.push_back(pointLast); - } - - // From the initial point (i_vline, i_intersection), follow a path. - SegmentedIntersectionLine &seg = segs[i_vline]; - SegmentIntersection *intrsctn = &seg.intersections[i_intersection]; - bool going_up = intrsctn->is_low(); - bool try_connect = false; - if (going_up) { - myassert(! intrsctn->consumed_vertical_up); - myassert(i_intersection + 1 < seg.intersections.size()); - // Step back to the beginning of the vertical segment to mark it as consumed. - if (intrsctn->is_inner()) { - myassert(i_intersection > 0); - -- intrsctn; - -- i_intersection; - } - // Consume the complete vertical segment up to the outer contour. - do { - intrsctn->consumed_vertical_up = true; - ++ intrsctn; - ++ i_intersection; - myassert(i_intersection < seg.intersections.size()); - } while (intrsctn->type != SegmentIntersection::OUTER_HIGH); - if ((intrsctn - 1)->is_inner()) { - // Step back. - -- intrsctn; - -- i_intersection; - myassert(intrsctn->type == SegmentIntersection::INNER_HIGH); - try_connect = true; - } - } else { - // Going down. - myassert(intrsctn->is_high()); - myassert(i_intersection > 0); - myassert(! (intrsctn - 1)->consumed_vertical_up); - // Consume the complete vertical segment up to the outer contour. - if (intrsctn->is_inner()) - intrsctn->consumed_vertical_up = true; - do { - myassert(i_intersection > 0); - -- intrsctn; - -- i_intersection; - intrsctn->consumed_vertical_up = true; - } while (intrsctn->type != SegmentIntersection::OUTER_LOW); - if ((intrsctn + 1)->is_inner()) { - // Step back. - ++ intrsctn; - ++ i_intersection; - myassert(intrsctn->type == SegmentIntersection::INNER_LOW); - try_connect = true; - } - } - if (try_connect) { - // Decide, whether to finish the segment, or whether to follow the perimeter. - - // 1) Find possible connection points on the previous / next vertical line. - int iPrev = intersection_on_prev_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection); - int iNext = intersection_on_next_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection); - IntersectionTypeOtherVLine intrsctn_type_prev = intersection_type_on_prev_vertical_line(segs, i_vline, i_intersection, iPrev); - IntersectionTypeOtherVLine intrsctn_type_next = intersection_type_on_next_vertical_line(segs, i_vline, i_intersection, iNext); - - // 2) Find possible connection points on the same vertical line. - int iAbove = -1; - int iBelow = -1; - int iSegAbove = -1; - int iSegBelow = -1; - { -// SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ? -// SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW; - // Does the perimeter intersect the current vertical line above intrsctn? - for (size_t i = i_intersection + 1; i + 1 < seg.intersections.size(); ++ i) -// if (seg.intersections[i].iContour == intrsctn->iContour && seg.intersections[i].type == type_crossing) { - if (seg.intersections[i].iContour == intrsctn->iContour) { - iAbove = i; - iSegAbove = seg.intersections[i].iSegment; - break; - } - // Does the perimeter intersect the current vertical line below intrsctn? - for (size_t i = i_intersection - 1; i > 0; -- i) -// if (seg.intersections[i].iContour == intrsctn->iContour && seg.intersections[i].type == type_crossing) { - if (seg.intersections[i].iContour == intrsctn->iContour) { - iBelow = i; - iSegBelow = seg.intersections[i].iSegment; - break; - } - } - - // 3) Sort the intersection points, clear iPrev / iNext / iSegBelow / iSegAbove, - // if it is preceded by any other intersection point along the contour. - unsigned int vert_seg_dir_valid_mask = - (going_up ? - (iSegAbove != -1 && seg.intersections[iAbove].type == SegmentIntersection::INNER_LOW) : - (iSegBelow != -1 && seg.intersections[iBelow].type == SegmentIntersection::INNER_HIGH)) ? - (DIR_FORWARD | DIR_BACKWARD) : - 0; - { - // Invalidate iPrev resp. iNext, if the perimeter crosses the current vertical line earlier than iPrev resp. iNext. - // The perimeter contour orientation. - const bool forward = intrsctn->is_low(); // == poly_with_offset.is_contour_ccw(intrsctn->iContour); - const Polygon &poly = poly_with_offset.contour(intrsctn->iContour); - { - int d_horiz = (iPrev == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, segs[i_vline-1].intersections[iPrev].iSegment, intrsctn->iSegment, forward); - int d_down = (iSegBelow == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, iSegBelow, intrsctn->iSegment, forward); - int d_up = (iSegAbove == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, iSegAbove, intrsctn->iSegment, forward); - if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && d_horiz > std::min(d_down, d_up)) - // The vertical crossing comes eralier than the prev crossing. - // Disable the perimeter going back. - intrsctn_type_prev = INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST; - if (going_up ? (d_up > std::min(d_horiz, d_down)) : (d_down > std::min(d_horiz, d_up))) - // The horizontal crossing comes earlier than the vertical crossing. - vert_seg_dir_valid_mask &= ~(forward ? DIR_BACKWARD : DIR_FORWARD); - } - { - int d_horiz = (iNext == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, intrsctn->iSegment, segs[i_vline+1].intersections[iNext].iSegment, forward); - int d_down = (iSegBelow == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, intrsctn->iSegment, iSegBelow, forward); - int d_up = (iSegAbove == -1) ? std::numeric_limits::max() : - distance_of_segmens(poly, intrsctn->iSegment, iSegAbove, forward); - if (intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK && d_horiz > std::min(d_down, d_up)) - // The vertical crossing comes eralier than the prev crossing. - // Disable the perimeter going forward. - intrsctn_type_next = INTERSECTION_TYPE_OTHER_VLINE_NOT_FIRST; - if (going_up ? (d_up > std::min(d_horiz, d_down)) : (d_down > std::min(d_horiz, d_up))) - // The horizontal crossing comes earlier than the vertical crossing. - vert_seg_dir_valid_mask &= ~(forward ? DIR_FORWARD : DIR_BACKWARD); - } - } - - // 4) Try to connect to a previous or next vertical line, making a zig-zag pattern. - if (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK || intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) { - coordf_t distPrev = (intrsctn_type_prev != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : - measure_perimeter_prev_segment_length(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iPrev); - coordf_t distNext = (intrsctn_type_next != INTERSECTION_TYPE_OTHER_VLINE_OK) ? std::numeric_limits::max() : - measure_perimeter_next_segment_length(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iNext); - // Take the shorter path. - //FIXME this may not be always the best strategy to take the shortest connection line now. - bool take_next = (intrsctn_type_prev == INTERSECTION_TYPE_OTHER_VLINE_OK && intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK) ? - (distNext < distPrev) : - intrsctn_type_next == INTERSECTION_TYPE_OTHER_VLINE_OK; - myassert(intrsctn->is_inner()); - bool skip = this->dont_connect || (link_max_length > 0 && (take_next ? distNext : distPrev) > link_max_length); - if (skip) { - // Just skip the connecting contour and start a new path. - goto dont_connect; - polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); - polylines_out.push_back(Polyline()); - polyline_current = &polylines_out.back(); - const SegmentedIntersectionLine &il2 = segs[take_next ? (i_vline + 1) : (i_vline - 1)]; - polyline_current->points.push_back(Point(il2.pos, il2.intersections[take_next ? iNext : iPrev].pos())); - } else { - polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); - emit_perimeter_prev_next_segment(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, take_next ? iNext : iPrev, *polyline_current, take_next); - } - // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed. - if (iPrev != -1) - segs[i_vline-1].intersections[iPrev].consumed_perimeter_right = true; - if (iNext != -1) - intrsctn->consumed_perimeter_right = true; - //FIXME consume the left / right connecting segments at the other end of this line? Currently it is not critical because a perimeter segment is not followed if the vertical segment at the other side has already been consumed. - // Advance to the neighbor line. - if (take_next) { - ++ i_vline; - i_intersection = iNext; - } else { - -- i_vline; - i_intersection = iPrev; - } - continue; - } - - // 5) Try to connect to a previous or next point on the same vertical line. - if (vert_seg_dir_valid_mask) { - bool valid = true; - // Verify, that there is no intersection with the inner contour up to the end of the contour segment. - // Verify, that the successive segment has not been consumed yet. - if (going_up) { - if (seg.intersections[iAbove].consumed_vertical_up) { - valid = false; - } else { - for (int i = (int)i_intersection + 1; i < iAbove && valid; ++i) - if (seg.intersections[i].is_inner()) - valid = false; - } - } else { - if (seg.intersections[iBelow-1].consumed_vertical_up) { - valid = false; - } else { - for (int i = iBelow + 1; i < (int)i_intersection && valid; ++i) - if (seg.intersections[i].is_inner()) - valid = false; - } - } - if (valid) { - const Polygon &poly = poly_with_offset.contour(intrsctn->iContour); - int iNext = going_up ? iAbove : iBelow; - int iSegNext = going_up ? iSegAbove : iSegBelow; - bool dir_forward = (vert_seg_dir_valid_mask == (DIR_FORWARD | DIR_BACKWARD)) ? - // Take the shorter length between the current and the next intersection point. - (distance_of_segmens(poly, intrsctn->iSegment, iSegNext, true) < - distance_of_segmens(poly, intrsctn->iSegment, iSegNext, false)) : - (vert_seg_dir_valid_mask == DIR_FORWARD); - // Skip this perimeter line? - bool skip = this->dont_connect; - if (! skip && link_max_length > 0) { - coordf_t link_length = measure_perimeter_segment_on_vertical_line_length( - poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iNext, dir_forward); - skip = link_length > link_max_length; - } - polyline_current->points.push_back(Point(seg.pos, intrsctn->pos())); - if (skip) { - // Just skip the connecting contour and start a new path. - polylines_out.push_back(Polyline()); - polyline_current = &polylines_out.back(); - polyline_current->points.push_back(Point(seg.pos, seg.intersections[iNext].pos())); - } else { - // Consume the connecting contour and the next segment. - emit_perimeter_segment_on_vertical_line(poly_with_offset, segs, i_vline, intrsctn->iContour, i_intersection, iNext, *polyline_current, dir_forward); - } - // Mark both the left and right connecting segment as consumed, because one cannot go to this intersection point as it has been consumed. - // If there are any outer intersection points skipped (bypassed) by the contour, - // mark them as processed. - if (going_up) { - for (int i = (int)i_intersection; i < iAbove; ++ i) - seg.intersections[i].consumed_vertical_up = true; - } else { - for (int i = iBelow; i < (int)i_intersection; ++ i) - seg.intersections[i].consumed_vertical_up = true; - } -// seg.intersections[going_up ? i_intersection : i_intersection - 1].consumed_vertical_up = true; - intrsctn->consumed_perimeter_right = true; - i_intersection = iNext; - if (going_up) - ++ intrsctn; - else - -- intrsctn; - intrsctn->consumed_perimeter_right = true; - continue; - } - } - dont_connect: - // No way to continue the current polyline. Take the rest of the line up to the outer contour. - // This will finish the polyline, starting another polyline at a new point. - if (going_up) - ++ intrsctn; - else - -- intrsctn; - } - - // Finish the current vertical line, - // reset the current vertical line to pick a new starting point in the next round. - myassert(intrsctn->is_outer()); - myassert(intrsctn->is_high() == going_up); - pointLast = Point(seg.pos, intrsctn->pos()); - polyline_current->points.push_back(pointLast); - // Handle duplicate points and zero length segments. - polyline_current->remove_duplicate_points(); - myassert(! polyline_current->has_duplicate_points()); - // Handle nearly zero length edges. - if (polyline_current->points.size() <= 1 || - (polyline_current->points.size() == 2 && - std::abs(polyline_current->points.front().x - polyline_current->points.back().x) < SCALED_EPSILON && - std::abs(polyline_current->points.front().y - polyline_current->points.back().y) < SCALED_EPSILON)) - polylines_out.pop_back(); - intrsctn = NULL; - i_intersection = -1; - polyline_current = NULL; - } - -#ifdef SLIC3R_DEBUG2 - { - { - //::Slic3r::SVG svg(debug_out_path("FillRectilinear2-final-%03d.svg", iRun), bbox_svg); // , scale_(1.)); - //poly_with_offset.export_to_svg(svg); - //for (size_t i = n_polylines_out_initial; i < polylines_out.size(); ++ i) - //svg.draw(polylines_out[i].lines(), "black"); - } - // Paint a picture per polyline. This makes it easier to discover the order of the polylines and their overlap. - for (size_t i_polyline = n_polylines_out_initial; i_polyline < polylines_out.size(); ++ i_polyline) { - //::Slic3r::SVG svg(debug_out_path("FillRectilinear2-final-%03d-%03d.svg", iRun, i_polyline), bbox_svg); // , scale_(1.)); - //svg.draw(polylines_out[i_polyline].lines(), "black"); - } - } -#endif /* SLIC3R_DEBUG */ - - // paths must be rotated back - for (Polylines::iterator it = polylines_out.begin() + n_polylines_out_initial; it != polylines_out.end(); ++ it) { - // No need to translate, the absolute position is irrelevant. - // it->translate(- rotate_vector.second.x, - rotate_vector.second.y); - myassert(! it->has_duplicate_points()); - it->rotate(rotate_vector.first); - //FIXME rather simplify the paths to avoid very short edges? - //myassert(! it->has_duplicate_points()); - it->remove_duplicate_points(); - } - -#ifdef SLIC3R_DEBUG2 - // Verify, that there are no duplicate points in the sequence. - for (Polylines::iterator it = polylines_out.begin(); it != polylines_out.end(); ++ it) - myassert(! it->has_duplicate_points()); -#endif /* SLIC3R_DEBUG */ - - return true; -} - -Polylines FillRectilinear2::fill_surface(const Surface &surface) -{ - Polylines polylines_out; - if (! fill_surface_by_lines(&surface, 0.f, 0.f, polylines_out)) { - printf("FillRectilinear2::fill_surface() failed to fill a region.\n"); - } - return polylines_out; -} - -Polylines FillGrid2::fill_surface(const Surface &surface) -{ - // Each linear fill covers half of the target coverage. - FillGrid2 fill2 = *this; - fill2.density *= 0.5f; - Polylines polylines_out; - if (! fill2.fill_surface_by_lines(&surface, 0.f, 0.f, polylines_out) || - ! fill2.fill_surface_by_lines(&surface, float(M_PI / 2.), 0.f, polylines_out)) { - printf("FillGrid2::fill_surface() failed to fill a region.\n"); - } - return polylines_out; -} - -Polylines FillTriangles2::fill_surface(const Surface &surface) -{ - // Each linear fill covers 1/3 of the target coverage. - FillTriangles2 fill2 = *this; - fill2.density *= 0.333333333f; - Polylines polylines_out; - if (! fill2.fill_surface_by_lines(&surface, 0.f, 0., polylines_out) || - ! fill2.fill_surface_by_lines(&surface, float(M_PI / 3.), 0., polylines_out) || - ! fill2.fill_surface_by_lines(&surface, float(2. * M_PI / 3.), 0.5 * this->spacing / fill2.density, polylines_out)) { - printf("FillTriangles::fill_surface() failed to fill a region.\n"); - } - return polylines_out; -} - -Polylines FillStars2::fill_surface(const Surface &surface) -{ - // Each linear fill covers 1/3 of the target coverage. - FillStars2 fill2 = *this; - fill2.density *= 0.333333333f; - Polylines polylines_out; - if (! fill2.fill_surface_by_lines(&surface, 0.f, 0., polylines_out) || - ! fill2.fill_surface_by_lines(&surface, float(M_PI / 3.), 0., polylines_out) || - ! fill2.fill_surface_by_lines(&surface, float(2. * M_PI / 3.), 0., polylines_out)) { - printf("FillStars::fill_surface() failed to fill a region.\n"); - } - return polylines_out; -} - -Polylines FillCubic2::fill_surface(const Surface &surface) -{ - // Each linear fill covers 1/3 of the target coverage. - FillCubic2 fill2 = *this; - fill2.density *= 0.333333333f; - Polylines polylines_out; - if (! fill2.fill_surface_by_lines(&surface, 0.f, z, polylines_out) || - ! fill2.fill_surface_by_lines(&surface, float(M_PI / 3.), -z, polylines_out) || - // Rotated by PI*2/3 + PI to achieve reverse sloping wall. - ! fill2.fill_surface_by_lines(&surface, float(M_PI * 2. / 3.), z, polylines_out)) { - printf("FillCubic::fill_surface() failed to fill a region.\n"); - } - return polylines_out; -} - -static inline bool is_stick(const Point &p1, const Point &p2, const Point &p3) -{ - Point v1 = p2 - p1; - Point v2 = p3 - p2; - int64_t dir = int64_t(v1.x) * int64_t(v2.x) + int64_t(v1.y) * int64_t(v2.y); - if (dir > 0) - // p3 does not turn back to p1. Do not remove p2. - return false; - double l2_1 = double(v1.x) * double(v1.x) + double(v1.y) * double(v1.y); - double l2_2 = double(v2.x) * double(v2.x) + double(v2.y) * double(v2.y); - if (dir == 0) - // p1, p2, p3 may make a perpendicular corner, or there is a zero edge length. - // Remove p2 if it is coincident with p1 or p2. - return l2_1 == 0 || l2_2 == 0; - // p3 turns back to p1 after p2. Are p1, p2, p3 collinear? - // Calculate distance from p3 to a segment (p1, p2) or from p1 to a segment(p2, p3), - // whichever segment is longer - double cross = double(v1.x) * double(v2.y) - double(v2.x) * double(v1.y); - double dist2 = cross * cross / std::max(l2_1, l2_2); - return dist2 < EPSILON * EPSILON; -} - -bool remove_sticks(Polygon &poly) -{ - bool modified = false; - size_t j = 1; - for (size_t i = 1; i + 1 < poly.points.size(); ++ i) { - if (! is_stick(poly[j-1], poly[i], poly[i+1])) { - // Keep the point. - if (j < i) - poly.points[j] = poly.points[i]; - ++ j; - } - } - if (++ j < poly.points.size()) { - poly.points[j-1] = poly.points.back(); - poly.points.erase(poly.points.begin() + j, poly.points.end()); - modified = true; - } - while (poly.points.size() >= 3 && is_stick(poly.points[poly.points.size()-2], poly.points.back(), poly.points.front())) { - poly.points.pop_back(); - modified = true; - } - while (poly.points.size() >= 3 && is_stick(poly.points.back(), poly.points.front(), poly.points[1])) - poly.points.erase(poly.points.begin()); - return modified; -} - -bool remove_sticks(Polygons &polys) -{ - bool modified = false; - size_t j = 0; - for (size_t i = 0; i < polys.size(); ++ i) { - modified |= remove_sticks(polys[i]); - if (polys[i].points.size() >= 3) { - if (j < i) - std::swap(polys[i].points, polys[j].points); - ++ j; - } - } - if (j < polys.size()) - polys.erase(polys.begin() + j, polys.end()); - return modified; -} - -bool remove_sticks(ExPolygon &poly) -{ - return remove_sticks(poly.contour) || remove_sticks(poly.holes); -} - -bool remove_small(Polygons &polys, double min_area) -{ - bool modified = false; - size_t j = 0; - for (size_t i = 0; i < polys.size(); ++ i) { - if (std::abs(polys[i].area()) >= min_area) { - if (j < i) - std::swap(polys[i].points, polys[j].points); - ++ j; - } else - modified = true; - } - if (j < polys.size()) - polys.erase(polys.begin() + j, polys.end()); - return modified; -} - - - -} // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/FillRectilinear2.hpp b/xs/src/libslic3r/Fill/FillRectilinear2.hpp deleted file mode 100644 index a28ffc4c7..000000000 --- a/xs/src/libslic3r/Fill/FillRectilinear2.hpp +++ /dev/null @@ -1,81 +0,0 @@ -#ifndef slic3r_FillRectilinear2_hpp_ -#define slic3r_FillRectilinear2_hpp_ - -#include "../libslic3r.h" - -#include "Fill.hpp" - -namespace Slic3r { - -class Surface; - -class FillRectilinear2 : public Fill -{ -public: - virtual Fill* clone() const { return new FillRectilinear2(*this); }; - virtual ~FillRectilinear2() {} - virtual Polylines fill_surface(const Surface &surface); - -protected: - bool fill_surface_by_lines(const Surface *surface, float angleBase, float pattern_shift, Polylines &polylines_out); -}; - -class FillGrid2 : public FillRectilinear2 -{ -public: - virtual Fill* clone() const { return new FillGrid2(*this); }; - virtual ~FillGrid2() {} - virtual Polylines fill_surface(const Surface &surface); - -protected: - // The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill. - virtual float _layer_angle(size_t idx) const { return 0.f; } -}; - -class FillTriangles2 : public FillRectilinear2 -{ -public: - virtual Fill* clone() const { return new FillTriangles2(*this); }; - virtual ~FillTriangles2() {} - virtual Polylines fill_surface(const Surface &surface); - -protected: - // The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill. - virtual float _layer_angle(size_t idx) const { return 0.f; } -}; - -class FillStars2 : public FillRectilinear2 -{ -public: - virtual Fill* clone() const { return new FillStars2(*this); }; - virtual ~FillStars2() {} - virtual Polylines fill_surface(const Surface &surface); - -protected: - // The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill. - virtual float _layer_angle(size_t idx) const { return 0.f; } -}; - -class FillCubic2 : public FillRectilinear2 -{ -public: - virtual Fill* clone() const { return new FillCubic2(*this); }; - virtual ~FillCubic2() {} - virtual Polylines fill_surface(const Surface &surface); - -protected: - // The grid fill will keep the angle constant between the layers, see the implementation of Slic3r::Fill. - virtual float _layer_angle(size_t idx) const { return 0.f; } -}; - - -// Remove sticks (tentacles with zero area) from the polygon. -extern bool remove_sticks(Polygon &poly); -extern bool remove_sticks(Polygons &polys); -extern bool remove_sticks(ExPolygon &poly); -extern bool remove_small(Polygons &polys, double min_area); - - -}; // namespace Slic3r - -#endif // slic3r_FillRectilinear2_hpp_ From f58d1d1fc5f1d84f2a5d785c9f18691b5872b5f1 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 12 Dec 2016 11:25:04 +0100 Subject: [PATCH 153/225] Minor fixes to the new rectilinear implementation --- xs/src/libslic3r/Fill/Fill.cpp | 2 +- xs/src/libslic3r/Point.cpp | 6 ------ xs/src/libslic3r/Point.hpp | 1 - xs/t/15_config.t | 8 ++++---- 4 files changed, 5 insertions(+), 12 deletions(-) diff --git a/xs/src/libslic3r/Fill/Fill.cpp b/xs/src/libslic3r/Fill/Fill.cpp index d0fee59f7..82a392943 100644 --- a/xs/src/libslic3r/Fill/Fill.cpp +++ b/xs/src/libslic3r/Fill/Fill.cpp @@ -86,7 +86,7 @@ Fill::adjust_solid_spacing(const coord_t width, const coord_t distance) // How much could the extrusion width be increased? By 20%. const coordf_t factor_max = 1.2; - if (false && factor > factor_max) + if (factor > factor_max) distance_new = floor((double)distance * factor_max + 0.5); assert((distance_new * number_of_intervals) <= width); diff --git a/xs/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp index 803549949..5269a4c11 100644 --- a/xs/src/libslic3r/Point.cpp +++ b/xs/src/libslic3r/Point.cpp @@ -18,12 +18,6 @@ Point::operator==(const Point& rhs) const return this->coincides_with(rhs); } -bool -Point::operator<(const Point& rhs) const -{ - return this->x < rhs.x || this->y < rhs.y; -} - std::string Point::wkt() const { diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp index daafe728e..7fd05ff48 100644 --- a/xs/src/libslic3r/Point.hpp +++ b/xs/src/libslic3r/Point.hpp @@ -37,7 +37,6 @@ class Point return Point(scale_(x), scale_(y)); }; bool operator==(const Point& rhs) const; - bool operator<(const Point& rhs) const; std::string wkt() const; std::string dump_perl() const; void scale(double factor); diff --git a/xs/t/15_config.t b/xs/t/15_config.t index a4c5d5925..3a7ad0de6 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -97,8 +97,8 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo $config->set_deserialize('gcode_flavor', 'machinekit'); is $config->get('gcode_flavor'), 'machinekit', 'deserialize enum (gcode_flavor)'; - $config->set_deserialize('fill_pattern', 'line'); - is $config->get('fill_pattern'), 'line', 'deserialize enum (fill_pattern)'; + $config->set_deserialize('fill_pattern', 'stars'); + is $config->get('fill_pattern'), 'stars', 'deserialize enum (fill_pattern)'; $config->set_deserialize('support_material_pattern', 'pillars'); is $config->get('support_material_pattern'), 'pillars', 'deserialize enum (support_material_pattern)'; @@ -199,12 +199,12 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo { my $config = Slic3r::Config->new; - $config->set('fill_pattern', 'line'); + $config->set('fill_pattern', 'stars'); my $config2 = Slic3r::Config->new; $config2->set('fill_pattern', 'hilbertcurve'); - is $config->get('fill_pattern'), 'line', 'no interferences between DynamicConfig objects'; + is $config->get('fill_pattern'), 'stars', 'no interferences between DynamicConfig objects'; } { From 3a3b24ec7c803c58919a5401e5804261999875b4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 12 Dec 2016 11:38:39 +0100 Subject: [PATCH 154/225] Two minor fixes --- xs/src/libslic3r/LayerRegion.cpp | 2 +- xs/src/libslic3r/SLAPrint.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index b97fa0727..173cb919f 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -86,7 +86,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer) BridgeDetector bd( surface->expolygon, lower_layer->slices, - this->flow(frInfill, this->layer()->height, true).scaled_width() + this->flow(frInfill, true).scaled_width() ); #ifdef SLIC3R_DEBUG diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index 0f98b79ee..2fdf16fb4 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -5,6 +5,7 @@ #include "Geometry.hpp" #include "Surface.hpp" #include +#include #include namespace Slic3r { @@ -161,7 +162,7 @@ SLAPrint::_infill_layer(size_t i, const Fill* _fill) Polygons internal = layer.slices; for (size_t j = 0; j < this->layers.size(); ++j) { const Layer &other = this->layers[j]; - if (abs(other.print_z - layer.print_z) > shell_thickness) continue; + if (std::abs(other.print_z - layer.print_z) > shell_thickness) continue; if (j == 0 || j == this->layers.size()-1) { internal.clear(); From 3d2742e4206cee4e5365105d40d47f6e263451c9 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 12 Dec 2016 14:34:13 +0100 Subject: [PATCH 155/225] One more minor fix, a minor optimization and a TODO --- xs/src/libslic3r/Fill/FillRectilinear.cpp | 24 +++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/xs/src/libslic3r/Fill/FillRectilinear.cpp index c56c233b4..632314d22 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.cpp @@ -72,8 +72,6 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, for (Points::const_iterator p = points.begin(); p != points.end(); ++p) { const Point &prev = p == points.begin() ? *(points.end()-1) : *(p-1); const Point &next = p == points.end()-1 ? *points.begin() : *(p+1); - const Point &next2 = p == (points.end()-2) ? *points.begin() - : p == (points.end()-1) ? *(points.begin()+1) : *(p+2); // Does the p-next line belong to an intersection line? if (p->x == next.x && ((p->x - bounding_box.min.x) % line_spacing) == 0) { @@ -139,12 +137,12 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, assert(max_x2 <= max_x); // We're now going past the first point, so save it. - if (p->x < min_x2) + const bool line_goes_right = next.x > p->x; + if (line_goes_right ? (p->x < min_x2) : (p->x > max_x2)) skipped_points.push_back(*p); // Now loop through those intersection points according the original direction // of the line (because we need to store them in this order). - const bool line_goes_right = next.x > p->x; for (coord_t x = line_goes_right ? min_x2 : max_x2; x >= min_x && x <= max_x; x += line_goes_right ? +line_spacing : -line_spacing) { @@ -155,9 +153,13 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, skipped_points.push_back(*p); continue; } - if (x == next.x && ((p->x > x && next2.x > x) || (p->x < x && next2.x < x))) { - skipped_points.push_back(next); - continue; + if (x == next.x) { + const Point &next2 = p == (points.end()-2) ? *points.begin() + : p == (points.end()-1) ? *(points.begin()+1) : *(p+2); + if ((p->x > x && next2.x > x) || (p->x < x && next2.x < x)) { + skipped_points.push_back(next); + continue; + } } // Calculate the y coordinate of this intersection. @@ -198,7 +200,7 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, } // We're now going past the final point, so save it. - if (next.x > max_x2) + if (line_goes_right ? (next.x > max_x2) : (next.x < min_x2)) skipped_points.push_back(next); } @@ -348,6 +350,12 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, // the connection is straight and horizontal, but doesn't work well when the // connection is articulated and also has vertical parts. { + // TODO: here's where we should check for overextrusion. We should only add + // connection points while they are not generating vertical lines within the + // extrusion thickness of the main vertical lines. We should also check whether + // a previous run of this method occupied this polygon portion (derived infill + // patterns doing multiple runs at different angles generate overlapping connections). + // In both cases, we should just stop the connection and break the polyline here. const size_t n = polyline.points.size(); polyline.append(b.next); for (Points::iterator pit = polyline.points.begin()+n; pit != polyline.points.end(); ++pit) From 376df2e3f2335c221156515de2bd48cedc7bb430 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 15 Dec 2016 11:50:58 +0100 Subject: [PATCH 156/225] Minor GUI fix in the prompt for solid rectilinear infill. #3610 --- lib/Slic3r/GUI/Tab.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 259ec7752..ffef07066 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -806,7 +806,7 @@ sub _update { my $new_conf = Slic3r::Config->new; if ($dialog->ShowModal() == wxID_YES) { - $new_conf->set("fill_pattern", 1); + $new_conf->set("fill_pattern", 'rectilinear'); } else { $new_conf->set("fill_density", 40); } From e5412b5d69dc4d5415c92a7daaa47fb4860ca4ea Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 15 Dec 2016 18:23:01 +0100 Subject: [PATCH 157/225] Use --local-lib when calling cpanm --- .gitignore | 1 + .travis.yml | 1 + Build.PL | 11 ++++++++++- slic3r.pl | 1 + t/angles.t | 3 +++ t/arcs.t | 1 + t/avoid_crossing_perimeters.t | 1 + t/bridges.t | 1 + t/clean_polylines.t | 1 + t/clipper.t | 1 + t/collinear.t | 1 + t/combineinfill.t | 1 + t/config.t | 1 + t/cooling.t | 1 + t/custom_gcode.t | 1 + t/dynamic.t | 1 + t/fill.t | 1 + t/flow.t | 1 + t/gaps.t | 1 + t/gcode.t | 1 + t/geometry.t | 1 + t/layers.t | 1 + t/loops.t | 1 + t/multi.t | 1 + t/perimeters.t | 1 + t/polyclip.t | 1 + t/pressure.t | 1 + t/print.t | 1 + t/retraction.t | 1 + t/shells.t | 1 + t/skirt_brim.t | 1 + t/slice.t | 1 + t/support.t | 1 + t/svg.t | 1 + t/thin.t | 1 + t/threads.t | 1 + t/vibrationlimit.t | 1 + utils/amf-to-stl.pl | 1 + utils/config-bundle-to-config.pl | 1 + utils/dump-stl.pl | 1 + utils/gcode_sectioncut.pl | 1 + utils/pdf-slices.pl | 1 + utils/send-gcode.pl | 1 + utils/split_stl.pl | 1 + utils/stl-to-amf.pl | 1 + utils/view-mesh.pl | 1 + utils/view-toolpaths.pl | 1 + utils/wireframe.pl | 1 + 48 files changed, 59 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 95428a002..089e0a2c0 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ MANIFEST.bak xs/MANIFEST.bak xs/assertlib* .init_bundle.ini +local-lib diff --git a/.travis.yml b/.travis.yml index f25d6bcbd..8e9616ab5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,3 +20,4 @@ addons: - libboost-thread1.55-dev - libboost-system1.55-dev - libboost-filesystem1.55-dev + - liblocal-lib-perl diff --git a/Build.PL b/Build.PL index 68a9af16e..bace8ae4b 100644 --- a/Build.PL +++ b/Build.PL @@ -107,7 +107,16 @@ EOF if !$cpanm; my @cpanm_args = (); push @cpanm_args, "--sudo" if $sudo; - + + # install local::lib without --local-lib otherwise it's not usable afterwards + if (!eval "use local::lib; 1") { + my $res = system $cpanm, @cpanm_args, 'local::lib'; + warn "Warning: local::lib is required. You might need to run the `cpanm --sudo local::lib` command in order to install it.\n" + if $res != 0; + } + + push @cpanm_args, ('--local-lib', 'local-lib'); + # make sure our cpanm is updated (old ones don't support the ~ syntax) system $cpanm, @cpanm_args, 'App::cpanminus'; diff --git a/slic3r.pl b/slic3r.pl index 962562530..bf9618c6c 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -6,6 +6,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/lib"; + use local::lib "$FindBin::Bin/local-lib"; } use File::Basename qw(basename); diff --git a/t/angles.t b/t/angles.t index 1e1a6c9c5..2ae00b4e8 100644 --- a/t/angles.t +++ b/t/angles.t @@ -7,6 +7,9 @@ plan tests => 34; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use lib "$FindBin::Bin/../lib"; + use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Slic3r; diff --git a/t/arcs.t b/t/arcs.t index 65b83de43..ec85dfbc2 100644 --- a/t/arcs.t +++ b/t/arcs.t @@ -7,6 +7,7 @@ plan tests => 24; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Slic3r; diff --git a/t/avoid_crossing_perimeters.t b/t/avoid_crossing_perimeters.t index dd6c3e7b6..7cd04e50e 100644 --- a/t/avoid_crossing_perimeters.t +++ b/t/avoid_crossing_perimeters.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first sum); diff --git a/t/bridges.t b/t/bridges.t index 432d5e7a9..51dda5c64 100644 --- a/t/bridges.t +++ b/t/bridges.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first sum); diff --git a/t/clean_polylines.t b/t/clean_polylines.t index 4526bd60c..50c6f5bbd 100644 --- a/t/clean_polylines.t +++ b/t/clean_polylines.t @@ -7,6 +7,7 @@ plan tests => 6; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Slic3r; diff --git a/t/clipper.t b/t/clipper.t index a898a53f6..3c9838143 100644 --- a/t/clipper.t +++ b/t/clipper.t @@ -7,6 +7,7 @@ plan tests => 6; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(sum); diff --git a/t/collinear.t b/t/collinear.t index 9dee77700..b28a3602c 100644 --- a/t/collinear.t +++ b/t/collinear.t @@ -7,6 +7,7 @@ plan tests => 11; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Slic3r; diff --git a/t/combineinfill.t b/t/combineinfill.t index 6661cdd6c..66e461d45 100644 --- a/t/combineinfill.t +++ b/t/combineinfill.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first); diff --git a/t/config.t b/t/config.t index 7f85dae84..829ef5f39 100644 --- a/t/config.t +++ b/t/config.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Slic3r; diff --git a/t/cooling.t b/t/cooling.t index b12b46b95..d06204ba1 100644 --- a/t/cooling.t +++ b/t/cooling.t @@ -7,6 +7,7 @@ plan tests => 12; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first); diff --git a/t/custom_gcode.t b/t/custom_gcode.t index 9d68d1eed..d99b6785f 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first); diff --git a/t/dynamic.t b/t/dynamic.t index beae06ad0..5d4d3ceb4 100644 --- a/t/dynamic.t +++ b/t/dynamic.t @@ -8,6 +8,7 @@ plan tests => 20; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first); diff --git a/t/fill.t b/t/fill.t index aec533f23..7cea2726e 100644 --- a/t/fill.t +++ b/t/fill.t @@ -7,6 +7,7 @@ plan tests => 92; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first sum); diff --git a/t/flow.t b/t/flow.t index 071d290c8..37e9deafa 100644 --- a/t/flow.t +++ b/t/flow.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first sum); diff --git a/t/gaps.t b/t/gaps.t index 16baa0363..01ec1b4e3 100644 --- a/t/gaps.t +++ b/t/gaps.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first); diff --git a/t/gcode.t b/t/gcode.t index 9e7bd354a..956c579d4 100644 --- a/t/gcode.t +++ b/t/gcode.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first); diff --git a/t/geometry.t b/t/geometry.t index 4529e6b1c..0f37c0aa3 100644 --- a/t/geometry.t +++ b/t/geometry.t @@ -7,6 +7,7 @@ plan tests => 42; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Slic3r; diff --git a/t/layers.t b/t/layers.t index 57fd25760..a85998077 100644 --- a/t/layers.t +++ b/t/layers.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first); diff --git a/t/loops.t b/t/loops.t index 6293a4be7..e662469ca 100644 --- a/t/loops.t +++ b/t/loops.t @@ -8,6 +8,7 @@ plan tests => 4; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Slic3r; diff --git a/t/multi.t b/t/multi.t index 362f1f7b4..6ce862818 100644 --- a/t/multi.t +++ b/t/multi.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first); diff --git a/t/perimeters.t b/t/perimeters.t index 47b844887..828504311 100644 --- a/t/perimeters.t +++ b/t/perimeters.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Slic3r::ExtrusionLoop ':roles'; diff --git a/t/polyclip.t b/t/polyclip.t index 1292d3216..0808c7be9 100644 --- a/t/polyclip.t +++ b/t/polyclip.t @@ -7,6 +7,7 @@ plan tests => 18; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Slic3r; diff --git a/t/pressure.t b/t/pressure.t index 6bbb81d84..dde236954 100644 --- a/t/pressure.t +++ b/t/pressure.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(); diff --git a/t/print.t b/t/print.t index 28cf1ee42..b78739e49 100644 --- a/t/print.t +++ b/t/print.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first); diff --git a/t/retraction.t b/t/retraction.t index a35562c58..571e9755d 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(any); diff --git a/t/shells.t b/t/shells.t index e7eb5da13..e6eb65a9d 100644 --- a/t/shells.t +++ b/t/shells.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first sum); diff --git a/t/skirt_brim.t b/t/skirt_brim.t index 3353149cc..9b2a0a126 100644 --- a/t/skirt_brim.t +++ b/t/skirt_brim.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first); diff --git a/t/slice.t b/t/slice.t index 301ae164e..51b52163d 100644 --- a/t/slice.t +++ b/t/slice.t @@ -8,6 +8,7 @@ plan tests => 16; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } # temporarily disable compilation errors due to constant not being exported anymore diff --git a/t/support.t b/t/support.t index 3eba6e64b..33e5d9cb3 100644 --- a/t/support.t +++ b/t/support.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first); diff --git a/t/svg.t b/t/svg.t index 9299eeefa..6092efe24 100644 --- a/t/svg.t +++ b/t/svg.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Slic3r; diff --git a/t/thin.t b/t/thin.t index 514614802..2d256d286 100644 --- a/t/thin.t +++ b/t/thin.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Slic3r; diff --git a/t/threads.t b/t/threads.t index 106a68af5..7fcd86f0e 100644 --- a/t/threads.t +++ b/t/threads.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use List::Util qw(first); diff --git a/t/vibrationlimit.t b/t/vibrationlimit.t index 7bfa27acb..1a5488a6e 100644 --- a/t/vibrationlimit.t +++ b/t/vibrationlimit.t @@ -5,6 +5,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Slic3r; diff --git a/utils/amf-to-stl.pl b/utils/amf-to-stl.pl index b421dd33a..f9599704f 100755 --- a/utils/amf-to-stl.pl +++ b/utils/amf-to-stl.pl @@ -7,6 +7,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use File::Basename qw(basename); diff --git a/utils/config-bundle-to-config.pl b/utils/config-bundle-to-config.pl index beecd666f..e1d7f6143 100755 --- a/utils/config-bundle-to-config.pl +++ b/utils/config-bundle-to-config.pl @@ -9,6 +9,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Getopt::Long qw(:config no_auto_abbrev); diff --git a/utils/dump-stl.pl b/utils/dump-stl.pl index 08b4d750a..0c7f609de 100644 --- a/utils/dump-stl.pl +++ b/utils/dump-stl.pl @@ -8,6 +8,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Slic3r; diff --git a/utils/gcode_sectioncut.pl b/utils/gcode_sectioncut.pl index 4d9619e4c..b94a2fc4b 100644 --- a/utils/gcode_sectioncut.pl +++ b/utils/gcode_sectioncut.pl @@ -7,6 +7,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Getopt::Long qw(:config no_auto_abbrev); diff --git a/utils/pdf-slices.pl b/utils/pdf-slices.pl index fa7612bb1..30c41ae45 100755 --- a/utils/pdf-slices.pl +++ b/utils/pdf-slices.pl @@ -7,6 +7,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Getopt::Long qw(:config no_auto_abbrev); diff --git a/utils/send-gcode.pl b/utils/send-gcode.pl index 989b17872..0b803baa6 100644 --- a/utils/send-gcode.pl +++ b/utils/send-gcode.pl @@ -6,6 +6,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Slic3r; diff --git a/utils/split_stl.pl b/utils/split_stl.pl index 8e7d957a4..e9990b8fc 100755 --- a/utils/split_stl.pl +++ b/utils/split_stl.pl @@ -7,6 +7,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use File::Basename qw(basename); diff --git a/utils/stl-to-amf.pl b/utils/stl-to-amf.pl index e1adec7ce..f4e133ce1 100755 --- a/utils/stl-to-amf.pl +++ b/utils/stl-to-amf.pl @@ -7,6 +7,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use File::Basename qw(basename); diff --git a/utils/view-mesh.pl b/utils/view-mesh.pl index 687a56224..36461534e 100644 --- a/utils/view-mesh.pl +++ b/utils/view-mesh.pl @@ -7,6 +7,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Getopt::Long qw(:config no_auto_abbrev); diff --git a/utils/view-toolpaths.pl b/utils/view-toolpaths.pl index 4bbbaab4c..d4c47f072 100755 --- a/utils/view-toolpaths.pl +++ b/utils/view-toolpaths.pl @@ -7,6 +7,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Getopt::Long qw(:config no_auto_abbrev); diff --git a/utils/wireframe.pl b/utils/wireframe.pl index 053581dec..f49b66e56 100644 --- a/utils/wireframe.pl +++ b/utils/wireframe.pl @@ -8,6 +8,7 @@ use warnings; BEGIN { use FindBin; use lib "$FindBin::Bin/../lib"; + use local::lib "$FindBin::Bin/../local-lib"; } use Getopt::Long qw(:config no_auto_abbrev); From d4d26a5dbbb43f0a8a0cccd176eb8c9bf431b58a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Dec 2016 22:00:08 +0100 Subject: [PATCH 158/225] Always retract in bottom layers. #3053 #3618 --- xs/src/libslic3r/GCode.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/xs/src/libslic3r/GCode.cpp b/xs/src/libslic3r/GCode.cpp index 0ac7cd66c..b5d44f333 100644 --- a/xs/src/libslic3r/GCode.cpp +++ b/xs/src/libslic3r/GCode.cpp @@ -672,15 +672,6 @@ GCode::needs_retraction(const Polyline &travel, ExtrusionRole role) /* skip retraction if travel is contained in an internal slice *and* internal infill is enabled (so that stringing is entirely not visible) */ return false; - } else if (this->layer->any_bottom_region_slice_contains(travel) - && this->layer->upper_layer != NULL - && this->layer->upper_layer->slices.contains(travel) - && (this->config.bottom_solid_layers.value >= 2 || this->config.fill_density.value > 0)) { - /* skip retraction if travel is contained in an *infilled* bottom slice - but only if it's also covered by an *infilled* upper layer's slice - so that it's not visible from above (a bottom surface might not have an - upper slice in case of a thin membrane) */ - return false; } } From 8f0d8f209628e0dec825a15988de434dfb052940 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 16 Dec 2016 23:10:48 +0100 Subject: [PATCH 159/225] Fixed regression causing hangs and bad extra perimeter calculation. Spotted by @lordofhyphens #3613 #3614 --- xs/src/libslic3r/PrintObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 649c468fb..b1d25220a 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -701,7 +701,7 @@ PrintObject::_make_perimeters() while (true) { // compute the total thickness of perimeters const coord_t perimeters_thickness = ext_perimeter_width/2 + ext_perimeter_spacing/2 - + (region.config.perimeters-1 + region.config.extra_perimeters) * perimeter_spacing; + + (region.config.perimeters-1 + slice->extra_perimeters) * perimeter_spacing; // define a critical area where we don't want the upper slice to fall into // (it should either lay over our perimeters or outside this area) From 6575a15cf7a1995b5298641db2e266db7ba1858b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Dec 2016 00:02:21 +0100 Subject: [PATCH 160/225] Bugfix: --rotate did not support decimal values and interpreted them as radians. #3460 --- slic3r.pl | 5 +++-- src/slic3r.cpp | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/slic3r.pl b/slic3r.pl index bf9618c6c..35773ac4b 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -14,6 +14,7 @@ use Getopt::Long qw(:config no_auto_abbrev); use List::Util qw(first); use POSIX qw(setlocale LC_NUMERIC); use Slic3r; +use Slic3r::Geometry qw(deg2rad); use Time::HiRes qw(gettimeofday tv_interval); $|++; binmode STDOUT, ':utf8'; @@ -45,7 +46,7 @@ my %cli_options = (); 'info' => \$opt{info}, 'scale=f' => \$opt{scale}, - 'rotate=i' => \$opt{rotate}, + 'rotate=f' => \$opt{rotate}, 'duplicate=i' => \$opt{duplicate}, 'duplicate-grid=s' => \$opt{duplicate_grid}, 'print-center=s' => \$opt{print_center}, @@ -203,7 +204,7 @@ if (@ARGV) { # slicing from command line my $sprint = Slic3r::Print::Simple->new( scale => $opt{scale} // 1, - rotate => $opt{rotate} // 0, + rotate => deg2rad($opt{rotate} // 0), duplicate => $opt{duplicate} // 1, duplicate_grid => $opt{duplicate_grid} // [1,1], print_center => $opt{print_center} // Slic3r::Pointf->new(100,100), diff --git a/src/slic3r.cpp b/src/slic3r.cpp index bbec6a5cf..2c86ed853 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -88,7 +88,7 @@ main(const int argc, const char **argv) (*o)->scale_to_fit(cli_config.scale_to_fit.value); (*o)->scale(cli_config.scale.value); - (*o)->rotate(cli_config.rotate.value, Z); + (*o)->rotate(deg2rad(cli_config.rotate.value), Z); } // TODO: handle --merge From 4be6c973e3a719bbdd61c8c06b4a58ecd5019c36 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Dec 2016 00:26:57 +0100 Subject: [PATCH 161/225] Support decimal scaling factors and rotation angles. #1617 --- lib/Slic3r/GUI/Plater.pm | 43 ++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 508e0450c..b4412a256 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -7,7 +7,7 @@ use utf8; use File::Basename qw(basename dirname); use List::Util qw(sum first max); -use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad); +use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad rad2deg); use LWP::UserAgent; use threads::shared qw(shared_clone); use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc @@ -801,15 +801,17 @@ sub rotate { if (!defined $angle) { my $axis_name = $axis == X ? 'X' : $axis == Y ? 'Y' : 'Z'; - $angle = Wx::GetNumberFromUser("", "Enter the rotation angle:", "Rotate around $axis_name axis", $model_instance->rotation, -364, 364, $self); - return if !$angle || $angle == -1; - $angle = 0 - $angle; # rotate clockwise (be consistent with button icon) + my $default = $axis == Z ? rad2deg($model_instance->rotation) : 0; + # Wx::GetNumberFromUser() does not support decimal numbers + $angle = Wx::GetTextFromUser("Enter the rotation angle:", "Rotate around $axis_name axis", + $default, $self); + return if !$angle || $angle !~ /^-?\d*(?:\.\d*)?$/ || $angle == -1; } $self->stop_background_process; if ($axis == Z) { - my $new_angle = $model_instance->rotation + deg2rad($angle); + my $new_angle = deg2rad($angle); $_->set_rotation($new_angle) for @{ $model_object->instances }; $object->transform_thumbnail($self->{model}, $obj_idx); } else { @@ -886,15 +888,20 @@ sub changescale { my $scale; if ($tosize) { my $cursize = $object_size->[$axis]; - my $newsize = Wx::GetNumberFromUser("", "Enter the new size for the selected object:", "Scale along $axis_name", - $cursize, 0, $bed_size->[$axis], $self); - return if !$newsize || $newsize < 0; + # Wx::GetNumberFromUser() does not support decimal numbers + my $newsize = Wx::GetTextFromUser( + sprintf("Enter the new size for the selected object (print bed: %smm):", $bed_size->[$axis]), + "Scale along $axis_name", + $cursize, $self); + return if !$newsize || $newsize !~ /^\d*(?:\.\d*)?$/ || $newsize < 0; $scale = $newsize / $cursize * 100; } else { - $scale = Wx::GetNumberFromUser("", "Enter the scale % for the selected object:", "Scale along $axis_name", - 100, 0, 100000, $self); + # Wx::GetNumberFromUser() does not support decimal numbers + $scale = Wx::GetTextFromUser("Enter the scale % for the selected object:", + "Scale along $axis_name", 100, $self); + $scale =~ s/%$//; + return if !$scale || $scale !~ /^\d*(?:\.\d*)?$/ || $scale < 0; } - return if !$scale || $scale < 0; # apply Z rotation before scaling if ($model_instance->rotation != 0) { @@ -911,16 +918,18 @@ sub changescale { my $scale; if ($tosize) { my $cursize = max(@$object_size); - my $newsize = Wx::GetNumberFromUser("", "Enter the new max size for the selected object:", "Scale", - $cursize, 0, max(@$bed_size), $self); - return if !$newsize || $newsize < 0; + # Wx::GetNumberFromUser() does not support decimal numbers + my $newsize = Wx::GetTextFromUser("Enter the new max size for the selected object:", + "Scale", $cursize, $self); + return if !$newsize || $newsize !~ /^\d*(?:\.\d*)?$/ || $newsize < 0; $scale = $newsize / $cursize * 100; } else { # max scale factor should be above 2540 to allow importing files exported in inches - $scale = Wx::GetNumberFromUser("", "Enter the scale % for the selected object:", 'Scale', - $model_instance->scaling_factor*100, 0, 100000, $self); + # Wx::GetNumberFromUser() does not support decimal numbers + $scale = Wx::GetTextFromUser("Enter the scale % for the selected object:", 'Scale', + $model_instance->scaling_factor*100, $self); + return if !$scale || $scale !~ /^\d*(?:\.\d*)?$/ || $scale < 0; } - return if !$scale || $scale < 0; $self->{list}->SetItem($obj_idx, 2, "$scale%"); $scale /= 100; # turn percent into factor From 1d7483937364ce2260d5476994b74c75403b2da6 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Dec 2016 00:47:07 +0100 Subject: [PATCH 162/225] Allow bed_temperature = 0. #3505 --- lib/Slic3r/Print/GCode.pm | 2 +- xs/src/libslic3r/PrintConfig.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Print/GCode.pm b/lib/Slic3r/Print/GCode.pm index 798395ba3..6764f5be8 100644 --- a/lib/Slic3r/Print/GCode.pm +++ b/lib/Slic3r/Print/GCode.pm @@ -375,7 +375,7 @@ sub process_layer { if $temperature && $temperature != $self->config->get_at('first_layer_temperature', $extruder->id); } $gcode .= $self->_gcodegen->writer->set_bed_temperature($self->print->config->bed_temperature) - if $self->config->has_heatbed && $self->print->config->bed_temperature && $self->print->config->bed_temperature != $self->print->config->first_layer_bed_temperature; + if $self->config->has_heatbed && $self->print->config->first_layer_bed_temperature && $self->print->config->bed_temperature != $self->print->config->first_layer_bed_temperature; $self->_second_layer_things_done(1); } diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 4197b1465..6eeb6dc69 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -31,7 +31,7 @@ PrintConfigDef::PrintConfigDef() def = this->add("bed_temperature", coInt); def->label = "Other layers"; - def->tooltip = "Bed temperature for layers after the first one. Set this to zero to disable bed temperature control commands in the output."; + def->tooltip = "Bed temperature for layers after the first one."; def->cli = "bed-temperature=i"; def->full_label = "Bed temperature"; def->min = 0; From b23e50603e1916fd591855928d6e170bd19c579f Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Dec 2016 11:23:40 +0100 Subject: [PATCH 163/225] Bugfix: missing bottom solid infill when support_material_contact_distance == 0, z > 0, and the layer contained non-oriented bridges as well. #3503 --- xs/src/libslic3r/LayerRegion.cpp | 7 +++---- xs/src/libslic3r/LayerRegionFill.cpp | 26 +++++++++++++------------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/xs/src/libslic3r/LayerRegion.cpp b/xs/src/libslic3r/LayerRegion.cpp index 173cb919f..42809d0ae 100644 --- a/xs/src/libslic3r/LayerRegion.cpp +++ b/xs/src/libslic3r/LayerRegion.cpp @@ -75,14 +75,14 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer) for (Surfaces::const_iterator surface = surfaces.begin(); surface != surfaces.end(); ++surface) { if (!surface->is_bottom()) continue; - ExPolygons grown = offset_ex(surface->expolygon, +margin); + const ExPolygons grown = offset_ex(surface->expolygon, +margin); /* detect bridge direction before merging grown surfaces otherwise adjacent bridges would get merged into a single one while they need different directions also, supply the original expolygon instead of the grown one, because in case of very thin (but still working) anchors, the grown expolygon would go beyond them */ double angle = -1; - if (lower_layer != NULL) { + if (lower_layer != NULL && surface->is_bridge()) { BridgeDetector bd( surface->expolygon, lower_layer->slices, @@ -97,8 +97,7 @@ LayerRegion::process_external_surfaces(const Layer* lower_layer) angle = bd.angle; if (this->layer()->object()->config.support_material) { - Polygons coverage = bd.coverage(); - this->bridged.insert(this->bridged.end(), coverage.begin(), coverage.end()); + append_to(this->bridged, bd.coverage()); this->unsupported_bridge_edges.append(bd.unsupported_edges()); } } diff --git a/xs/src/libslic3r/LayerRegionFill.cpp b/xs/src/libslic3r/LayerRegionFill.cpp index 048514773..c5b806542 100644 --- a/xs/src/libslic3r/LayerRegionFill.cpp +++ b/xs/src/libslic3r/LayerRegionFill.cpp @@ -44,7 +44,7 @@ LayerRegion::make_fill() Polygons polygons_bridged; polygons_bridged.reserve(this->fill_surfaces.surfaces.size()); for (Surfaces::const_iterator it = this->fill_surfaces.surfaces.begin(); it != this->fill_surfaces.surfaces.end(); ++it) - if (it->bridge_angle >= 0) + if (it->is_bridge() && it->bridge_angle >= 0) append_to(polygons_bridged, (Polygons)*it); // group surfaces by distinct properties (equal surface_type, thickness, thickness_layers, bridge_angle) @@ -53,20 +53,20 @@ LayerRegion::make_fill() std::vector groups; this->fill_surfaces.group(&groups); - // merge compatible groups (we can generate continuous infill for them) + // merge compatible solid groups (we can generate continuous infill for them) { // cache flow widths and patterns used for all solid groups // (we'll use them for comparing compatible groups) std::vector group_attrib(groups.size()); for (size_t i = 0; i < groups.size(); ++i) { - // we can only merge solid non-bridge surfaces, so discard - // non-solid surfaces const Surface &surface = *groups[i].front(); - if (surface.is_solid() && (!surface.is_bridge() || this->layer()->id() == 0)) { - group_attrib[i].is_solid = true; - group_attrib[i].fw = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width; - group_attrib[i].pattern = surface.is_external() ? this->region()->config.external_fill_pattern.value : ipRectilinear; - } + // we can only merge solid non-bridge surfaces, so discard + // non-solid or bridge surfaces + if (!surface.is_solid() || surface.is_bridge()) continue; + + group_attrib[i].is_solid = true; + group_attrib[i].fw = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width; + group_attrib[i].pattern = surface.is_external() ? this->region()->config.external_fill_pattern.value : ipRectilinear; } // Loop through solid groups, find compatible groups and append them to this one. for (size_t i = 0; i < groups.size(); ++i) { @@ -85,19 +85,19 @@ LayerRegion::make_fill() } } - // Give priority to bridges. Process the bridges in the first round, the rest of the surfaces in the 2nd round. + // Give priority to oriented bridges. Process the bridges in the first round, the rest of the surfaces in the 2nd round. for (size_t round = 0; round < 2; ++ round) { for (std::vector::const_iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) { const SurfacesConstPtr &group = *it_group; - bool is_bridge = group.front()->bridge_angle >= 0; - if (is_bridge != (round == 0)) + const bool is_oriented_bridge = group.front()->is_bridge() && group.front()->bridge_angle >= 0; + if (is_oriented_bridge != (round == 0)) continue; // Make a union of polygons defining the infiill regions of a group, use a safety offset. Polygons union_p = union_(to_polygons(group), true); // Subtract surfaces having a defined bridge_angle from any other, use a safety offset. - if (!polygons_bridged.empty() && !is_bridge) + if (!is_oriented_bridge && !polygons_bridged.empty()) union_p = diff(union_p, polygons_bridged, true); // subtract any other surface already processed From a778cd9820d7d53f6b0463f512c2dbcac0827eda Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Dec 2016 19:51:29 +0100 Subject: [PATCH 164/225] Cut along X and Y axes too --- lib/Slic3r/GUI/3DScene.pm | 47 +++++-- lib/Slic3r/GUI/OptionsGroup/Field.pm | 6 + lib/Slic3r/GUI/Plater.pm | 17 +-- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 73 ++++++++--- src/slic3r.cpp | 42 +++++-- utils/view-mesh.pl | 3 +- xs/src/admesh/stl.h | 10 +- xs/src/libslic3r/Model.cpp | 32 ++++- xs/src/libslic3r/Model.hpp | 3 +- xs/src/libslic3r/PrintConfig.cpp | 18 +++ xs/src/libslic3r/PrintConfig.hpp | 6 + xs/src/libslic3r/SLAPrint.cpp | 2 +- xs/src/libslic3r/TriangleMesh.cpp | 151 +++++++++++++---------- xs/src/libslic3r/TriangleMesh.hpp | 39 +++++- xs/xsp/Model.xsp | 8 +- xs/xsp/TriangleMesh.xsp | 19 ++- 16 files changed, 346 insertions(+), 130 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 7a71375c1..268b5f615 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -23,6 +23,7 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init on_move volumes _sphi _stheta + cutting_plane_axis cutting_plane_z cut_lines_vertices bed_shape @@ -505,19 +506,35 @@ sub select_volume { } sub SetCuttingPlane { - my ($self, $z, $expolygons) = @_; + my ($self, $axis, $z, $expolygons) = @_; + $self->cutting_plane_axis($axis); $self->cutting_plane_z($z); # grow slices in order to display them better $expolygons = offset_ex([ map @$_, @$expolygons ], scale 0.1); + my $bb = $self->volumes_bounding_box; + my @verts = (); foreach my $line (map @{$_->lines}, map @$_, @$expolygons) { - push @verts, ( - unscale($line->a->x), unscale($line->a->y), $z, #)) - unscale($line->b->x), unscale($line->b->y), $z, #)) - ); + if ($axis == X) { + push @verts, ( + $bb->x_min + $z, unscale($line->a->x), unscale($line->a->y), #)) + $bb->x_min + $z, unscale($line->b->x), unscale($line->b->y), #)) + ); + } elsif ($axis == Y) { + push @verts, ( + unscale($line->a->y), $bb->y_min + $z, unscale($line->a->x), #)) + unscale($line->b->y), $bb->y_min + $z, unscale($line->b->x), #)) + ); + } else { + push @verts, ( + unscale($line->a->x), unscale($line->a->y), $z, #)) + unscale($line->b->x), unscale($line->b->y), $z, #)) + ); + } + } $self->cut_lines_vertices(OpenGL::Array->new_list(GL_FLOAT, @verts)); } @@ -932,10 +949,22 @@ sub Render { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBegin(GL_QUADS); glColor4f(0.8, 0.8, 0.8, 0.5); - glVertex3f($bb->x_min-20, $bb->y_min-20, $plane_z); - glVertex3f($bb->x_max+20, $bb->y_min-20, $plane_z); - glVertex3f($bb->x_max+20, $bb->y_max+20, $plane_z); - glVertex3f($bb->x_min-20, $bb->y_max+20, $plane_z); + if ($self->cutting_plane_axis == X) { + glVertex3f($bb->x_min+$plane_z, $bb->y_min-20, $bb->z_min-20); + glVertex3f($bb->x_min+$plane_z, $bb->y_max+20, $bb->z_min-20); + glVertex3f($bb->x_min+$plane_z, $bb->y_max+20, $bb->z_max+20); + glVertex3f($bb->x_min+$plane_z, $bb->y_min-20, $bb->z_max+20); + } elsif ($self->cutting_plane_axis == Y) { + glVertex3f($bb->x_min-20, $bb->y_min+$plane_z, $bb->z_min-20); + glVertex3f($bb->x_max+20, $bb->y_min+$plane_z, $bb->z_min-20); + glVertex3f($bb->x_max+20, $bb->y_min+$plane_z, $bb->z_max+20); + glVertex3f($bb->x_min-20, $bb->y_min+$plane_z, $bb->z_max+20); + } elsif ($self->cutting_plane_axis == Z) { + glVertex3f($bb->x_min-20, $bb->y_min-20, $bb->z_min+$plane_z); + glVertex3f($bb->x_max+20, $bb->y_min-20, $bb->z_min+$plane_z); + glVertex3f($bb->x_max+20, $bb->y_max+20, $bb->z_min+$plane_z); + glVertex3f($bb->x_min-20, $bb->y_max+20, $bb->z_min+$plane_z); + } glEnd(); glEnable(GL_CULL_FACE); glDisable(GL_BLEND); diff --git a/lib/Slic3r/GUI/OptionsGroup/Field.pm b/lib/Slic3r/GUI/OptionsGroup/Field.pm index fc17a65e7..d2cf76ce6 100644 --- a/lib/Slic3r/GUI/OptionsGroup/Field.pm +++ b/lib/Slic3r/GUI/OptionsGroup/Field.pm @@ -592,4 +592,10 @@ sub disable { $self->textctrl->SetEditable(0); } +sub set_range { + my ($self, $min, $max) = @_; + + $self->slider->SetRange($min * $self->scale, $max * $self->scale); +} + 1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index b4412a256..2fb7c2979 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -812,15 +812,12 @@ sub rotate { if ($axis == Z) { my $new_angle = deg2rad($angle); - $_->set_rotation($new_angle) for @{ $model_object->instances }; + $_->set_rotation($_->rotation + $new_angle) for @{ $model_object->instances }; $object->transform_thumbnail($self->{model}, $obj_idx); } else { # rotation around X and Y needs to be performed on mesh # so we first apply any Z rotation - if ($model_instance->rotation != 0) { - $model_object->rotate($model_instance->rotation, Z); - $_->set_rotation(0) for @{ $model_object->instances }; - } + $model_object->transform_by_instance($model_instance, 1); $model_object->rotate(deg2rad($angle), $axis); # realign object to Z = 0 @@ -847,10 +844,7 @@ sub mirror { my $model_instance = $model_object->instances->[0]; # apply Z rotation before mirroring - if ($model_instance->rotation != 0) { - $model_object->rotate($model_instance->rotation, Z); - $_->set_rotation(0) for @{ $model_object->instances }; - } + $model_object->transform_by_instance($model_instance, 1); $model_object->mirror($axis); $model_object->update_bounding_box; @@ -904,10 +898,7 @@ sub changescale { } # apply Z rotation before scaling - if ($model_instance->rotation != 0) { - $model_object->rotate($model_instance->rotation, Z); - $_->set_rotation(0) for @{ $model_object->instances }; - } + $model_object->transform_by_instance($model_instance, 1); my $versor = [1,1,1]; $versor->[$axis] = $scale/100; diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index cfdeba649..74dcae834 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -6,7 +6,7 @@ use strict; use warnings; use utf8; -use Slic3r::Geometry qw(PI X); +use Slic3r::Geometry qw(PI X Y Z); use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL); use Wx::Event qw(EVT_CLOSE EVT_BUTTON); use base 'Wx::Dialog'; @@ -24,12 +24,16 @@ sub new { # Note whether the window was already closed, so a pending update is not executed. $self->{already_closed} = 0; + $self->{model_object}->transform_by_instance($self->{model_object}->get_instance(0), 1); + # cut options + my $size_z = $self->{model_object}->instance_bounding_box(0)->size->z; $self->{cut_options} = { - z => 0, - keep_upper => 1, + axis => Z, + z => $size_z/2, + keep_upper => 0, keep_lower => 1, - rotate_lower => 1, + rotate_lower => 0, preview => 1, }; @@ -54,13 +58,21 @@ sub new { }, label_width => 120, ); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'axis', + type => 'select', + label => 'Axis', + labels => ['X','Y','Z'], + values => [X,Y,Z], + default => $self->{cut_options}{axis}, + )); $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 'z', type => 'slider', label => 'Z', default => $self->{cut_options}{z}, min => 0, - max => $self->{model_object}->bounding_box->size->z, + max => $size_z, full_width => 1, )); { @@ -133,10 +145,10 @@ sub new { # Adjust position / orientation of the split object halves. if ($self->{new_model_objects}{lower}) { - if ($self->{cut_options}{rotate_lower}) { + if ($self->{cut_options}{rotate_lower} && $self->{cut_options}{axis} == Z) { $self->{new_model_objects}{lower}->rotate(PI, X); - $self->{new_model_objects}{lower}->center_around_origin; # align to Z = 0 } + $self->{new_model_objects}{lower}->center_around_origin; # align to Z = 0 } if ($self->{new_model_objects}{upper}) { $self->{new_model_objects}{upper}->center_around_origin; # align to Z = 0 @@ -165,7 +177,15 @@ sub new { sub _mesh_slice_z_pos { my ($self) = @_; - return $self->{cut_options}{z} / $self->{model_object}->instances->[0]->scaling_factor; + + my $bb = $self->{model_object}->instance_bounding_box(0); + my $z = $self->{cut_options}{axis} == X ? $bb->x_min + : $self->{cut_options}{axis} == Y ? $bb->y_min + : $bb->z_min; + + $z += $self->{cut_options}{z} / $self->{model_object}->instances->[0]->scaling_factor; + + return $z; } # Only perform live preview if just a single part of the object shall survive. @@ -184,8 +204,8 @@ sub _perform_cut return if $self->{mesh_cut_valid}; my $z = $self->_mesh_slice_z_pos(); - - my ($new_model) = $self->{model_object}->cut($z); + + my ($new_model) = $self->{model_object}->cut($self->{cut_options}{axis}, $z); my ($upper_object, $lower_object) = @{$new_model->objects}; $self->{new_model} = $new_model; $self->{new_model_objects} = {}; @@ -210,12 +230,11 @@ sub _update { # Only recalculate the cut, if the live cut preview is active. my $life_preview_active = $self->_life_preview_active(); $self->_perform_cut() if $life_preview_active; - + { # scale Z down to original size since we're using the transformed mesh for 3D preview # and cut dialog but ModelObject::cut() needs Z without any instance transformation my $z = $self->_mesh_slice_z_pos(); - # update canvas if ($self->{canvas}) { @@ -226,25 +245,38 @@ sub _update { } else { push @objects, $self->{model_object}; } - + # get section contour my @expolygons = (); foreach my $volume (@{$self->{model_object}->volumes}) { next if !$volume->mesh; next if $volume->modifier; - my $expp = $volume->mesh->slice([ $z + $volume->mesh->bounding_box->z_min ])->[0]; + my $expp = $volume->mesh->slice_at($self->{cut_options}{axis}, $z); push @expolygons, @$expp; } + + my $offset = $self->{model_object}->instances->[0]->offset; foreach my $expolygon (@expolygons) { $self->{model_object}->instances->[0]->transform_polygon($_) for @$expolygon; - $expolygon->translate(map Slic3r::Geometry::scale($_), @{ $self->{model_object}->instances->[0]->offset }); + + if ($self->{cut_options}{axis} != X) { + $expolygon->translate(0, Slic3r::Geometry::scale($offset->y)); #) + } + if ($self->{cut_options}{axis} != Y) { + $expolygon->translate(Slic3r::Geometry::scale($offset->x), 0); + } } $self->{canvas}->reset_objects; $self->{canvas}->load_object($_, undef, [0]) for @objects; + + my $plane_z = $self->{cut_options}{z}; + $plane_z += 0.02 if !$self->{cut_options}{keep_upper}; + $plane_z -= 0.02 if !$self->{cut_options}{keep_lower}; $self->{canvas}->SetCuttingPlane( - $self->{cut_options}{z}, + $self->{cut_options}{axis}, + $plane_z, [@expolygons], ); $self->{canvas}->Render; @@ -255,9 +287,16 @@ sub _update { { my $z = $self->{cut_options}{z}; my $optgroup = $self->{optgroup}; + { + my $bb = $self->{model_object}->instance_bounding_box(0); + my $max = $self->{cut_options}{axis} == X ? $bb->size->x + : $self->{cut_options}{axis} == Y ? $bb->size->y ### + : $bb->size->z; + $optgroup->get_field('z')->set_range(0, $max); + } $optgroup->get_field('keep_upper')->toggle(my $have_upper = abs($z - $optgroup->get_option('z')->max) > 0.1); $optgroup->get_field('keep_lower')->toggle(my $have_lower = $z > 0.1); - $optgroup->get_field('rotate_lower')->toggle($z > 0 && $self->{cut_options}{keep_lower}); + $optgroup->get_field('rotate_lower')->toggle($z > 0 && $self->{cut_options}{keep_lower} && $self->{cut_options}{axis} == Z); $optgroup->get_field('preview')->toggle($self->{cut_options}{keep_upper} != $self->{cut_options}{keep_lower}); # update cut button diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 2c86ed853..f97177187 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -1,8 +1,9 @@ #include "Config.hpp" -#include "Model.hpp" +#include "Geometry.hpp" #include "IO.hpp" -#include "TriangleMesh.hpp" +#include "Model.hpp" #include "SLAPrint.hpp" +#include "TriangleMesh.hpp" #include "libslic3r.h" #include #include @@ -69,7 +70,7 @@ main(const int argc, const char **argv) Model model; // TODO: read other file formats with Model::read_from_file() try { - Slic3r::IO::STL::read(*it, &model); + IO::STL::read(*it, &model); } catch (std::exception &e) { std::cout << *it << ": " << e.what() << std::endl; exit(1); @@ -88,7 +89,7 @@ main(const int argc, const char **argv) (*o)->scale_to_fit(cli_config.scale_to_fit.value); (*o)->scale(cli_config.scale.value); - (*o)->rotate(deg2rad(cli_config.rotate.value), Z); + (*o)->rotate(Geometry::deg2rad(cli_config.rotate.value), Z); } // TODO: handle --merge @@ -105,7 +106,7 @@ main(const int argc, const char **argv) TriangleMesh mesh = model->mesh(); mesh.repair(); - Slic3r::IO::OBJ::write(mesh, outfile); + IO::OBJ::write(mesh, outfile); printf("File exported to %s\n", outfile.c_str()); } else if (cli_config.export_pov) { std::string outfile = cli_config.output.value; @@ -113,7 +114,7 @@ main(const int argc, const char **argv) TriangleMesh mesh = model->mesh(); mesh.repair(); - Slic3r::IO::POV::write(mesh, outfile); + IO::POV::write(mesh, outfile); printf("File exported to %s\n", outfile.c_str()); } else if (cli_config.export_svg) { std::string outfile = cli_config.output.value; @@ -124,8 +125,35 @@ main(const int argc, const char **argv) print.slice(); print.write_svg(outfile); printf("SVG file exported to %s\n", outfile.c_str()); + } else if (cli_config.cut_x > 0 || cli_config.cut_y > 0 || cli_config.cut > 0) { + model->repair(); + model->translate(0, 0, -model->bounding_box().min.z); + + if (!model->objects.empty()) { + // FIXME: cut all objects + Model out; + if (cli_config.cut_x > 0) { + model->objects.front()->cut(X, cli_config.cut_x, &out); + } else if (cli_config.cut_y > 0) { + model->objects.front()->cut(Y, cli_config.cut_y, &out); + } else { + model->objects.front()->cut(Z, cli_config.cut, &out); + } + + ModelObject &upper = *out.objects[0]; + ModelObject &lower = *out.objects[1]; + + if (upper.facets_count() > 0) { + TriangleMesh m = upper.mesh(); + IO::STL::write(m, upper.input_file + "_upper.stl"); + } + if (lower.facets_count() > 0) { + TriangleMesh m = lower.mesh(); + IO::STL::write(m, lower.input_file + "_lower.stl"); + } + } } else { - std::cerr << "error: only --export-svg and --export-obj are currently supported" << std::endl; + std::cerr << "error: command not supported" << std::endl; return 1; } } diff --git a/utils/view-mesh.pl b/utils/view-mesh.pl index 36461534e..91cd20a29 100644 --- a/utils/view-mesh.pl +++ b/utils/view-mesh.pl @@ -12,6 +12,7 @@ BEGIN { use Getopt::Long qw(:config no_auto_abbrev); use Slic3r; +use Slic3r::Geometry qw(Z); use Slic3r::GUI; use Slic3r::GUI::3DScene; $|++; @@ -40,7 +41,7 @@ my %opt = (); $app->{canvas}->load_object($model, 0); $app->{canvas}->set_auto_bed_shape; $app->{canvas}->zoom_to_volumes; - $app->{canvas}->SetCuttingPlane($opt{cut}) if defined $opt{cut}; + $app->{canvas}->SetCuttingPlane(Z, $opt{cut}) if defined $opt{cut}; $app->MainLoop; } diff --git a/xs/src/admesh/stl.h b/xs/src/admesh/stl.h index c27571993..7871c0351 100644 --- a/xs/src/admesh/stl.h +++ b/xs/src/admesh/stl.h @@ -61,15 +61,7 @@ typedef struct { static_assert(sizeof(stl_vertex) == 12, "size of stl_vertex incorrect"); #endif -typedef struct { - float x; - float y; - float z; -} stl_normal; - -#ifdef static_assert -static_assert(sizeof(stl_normal) == 12, "size of stl_normal incorrect"); -#endif +typedef stl_vertex stl_normal; typedef char stl_extra[2]; diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index 73adc56c9..bb5b8c89f 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -660,6 +660,24 @@ ModelObject::mirror(const Axis &axis) this->invalidate_bounding_box(); } +void +ModelObject::transform_by_instance(const ModelInstance &instance, bool dont_translate) +{ + this->rotate(instance.rotation, Z); + this->scale(instance.scaling_factor); + if (!dont_translate) + this->translate(instance.offset.x, instance.offset.y, 0); + + for (ModelInstancePtrs::iterator i = this->instances.begin(); i != this->instances.end(); ++i) { + (*i)->rotation -= instance.rotation; + (*i)->scaling_factor /= instance.scaling_factor; + if (!dont_translate) + (*i)->offset.translate(-instance.offset.x, -instance.offset.y); + } + this->origin_translation = Pointf3(0,0,0); + this->invalidate_bounding_box(); +} + size_t ModelObject::materials_count() const { @@ -692,7 +710,7 @@ ModelObject::needed_repair() const } void -ModelObject::cut(coordf_t z, Model* model) const +ModelObject::cut(Axis axis, coordf_t z, Model* model) const { // clone this one to duplicate instances, materials etc. ModelObject* upper = model->add_object(*this); @@ -707,10 +725,16 @@ ModelObject::cut(coordf_t z, Model* model) const upper->add_volume(*volume); lower->add_volume(*volume); } else { - TriangleMeshSlicer tms(&volume->mesh); TriangleMesh upper_mesh, lower_mesh; - // TODO: shouldn't we use object bounding box instead of per-volume bb? - tms.cut(z + volume->mesh.bounding_box().min.z, &upper_mesh, &lower_mesh); + + if (axis == X) { + TriangleMeshSlicer(&volume->mesh).cut(z, &upper_mesh, &lower_mesh); + } else if (axis == Y) { + TriangleMeshSlicer(&volume->mesh).cut(z, &upper_mesh, &lower_mesh); + } else if (axis == Z) { + TriangleMeshSlicer(&volume->mesh).cut(z, &upper_mesh, &lower_mesh); + } + upper_mesh.repair(); lower_mesh.repair(); upper_mesh.reset_repair_stats(); diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index 8d93325f3..16ea81155 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -156,10 +156,11 @@ class ModelObject void scale_to_fit(const Sizef3 &size); void rotate(float angle, const Axis &axis); void mirror(const Axis &axis); + void transform_by_instance(const ModelInstance &instance, bool dont_translate = false); size_t materials_count() const; size_t facets_count() const; bool needed_repair() const; - void cut(coordf_t z, Model* model) const; + void cut(Axis axis, coordf_t z, Model* model) const; void split(ModelObjectPtrs* new_objects); void update_bounding_box(); // this is a private method but we expose it until we need to expose it via XS void print_info() const; diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 6eeb6dc69..6ffdfefe6 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1398,6 +1398,24 @@ CLIConfigDef::CLIConfigDef() { ConfigOptionDef* def; + def = this->add("cut", coFloat); + def->label = "Cut"; + def->tooltip = "Cut model at the given Z."; + def->cli = "cut"; + def->default_value = new ConfigOptionFloat(0); + + def = this->add("cut_x", coFloat); + def->label = "Cut"; + def->tooltip = "Cut model at the given X."; + def->cli = "cut-x"; + def->default_value = new ConfigOptionFloat(0); + + def = this->add("cut_y", coFloat); + def->label = "Cut"; + def->tooltip = "Cut model at the given Y."; + def->cli = "cut-y"; + def->default_value = new ConfigOptionFloat(0); + def = this->add("export_obj", coBool); def->label = "Export SVG"; def->tooltip = "Export the model as OBJ."; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 9a446cf77..df2c33af3 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -589,6 +589,9 @@ class CLIConfig : public virtual ConfigBase, public StaticConfig { public: + ConfigOptionFloat cut; + ConfigOptionFloat cut_x; + ConfigOptionFloat cut_y; ConfigOptionBool export_obj; ConfigOptionBool export_pov; ConfigOptionBool export_svg; @@ -606,6 +609,9 @@ class CLIConfig }; virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { + OPT_PTR(cut); + OPT_PTR(cut_x); + OPT_PTR(cut_y); OPT_PTR(export_obj); OPT_PTR(export_pov); OPT_PTR(export_svg); diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index 2fdf16fb4..57fe64832 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -49,7 +49,7 @@ SLAPrint::slice() slice_z.push_back(this->layers[i].slice_z); std::vector slices; - TriangleMeshSlicer(&mesh).slice(slice_z, &slices); + TriangleMeshSlicer(&mesh).slice(slice_z, &slices); for (size_t i = 0; i < slices.size(); ++i) this->layers[i].slices.expolygons = slices[i]; diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index c0a2063cd..992cd59a7 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -477,8 +477,9 @@ TriangleMesh::extrude_tin(float offset) this->repair(); } +template void -TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const +TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const { /* This method gets called with a list of unscaled Z coordinates and outputs @@ -513,7 +514,7 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* la parallelize( 0, this->mesh->stl.stats.number_of_facets-1, - boost::bind(&TriangleMeshSlicer::_slice_do, this, _1, &lines, &lines_mutex, z) + boost::bind(&TriangleMeshSlicer::_slice_do, this, _1, &lines, &lines_mutex, z) ); } @@ -524,25 +525,26 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* la parallelize( 0, lines.size()-1, - boost::bind(&TriangleMeshSlicer::_make_loops_do, this, _1, &lines, layers) + boost::bind(&TriangleMeshSlicer::_make_loops_do, this, _1, &lines, layers) ); } +template void -TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector* lines, boost::mutex* lines_mutex, +TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector* lines, boost::mutex* lines_mutex, const std::vector &z) const { const stl_facet &facet = this->mesh->stl.facet_start[facet_idx]; // find facet extents - const float min_z = fminf(facet.vertex[0].z, fminf(facet.vertex[1].z, facet.vertex[2].z)); - const float max_z = fmaxf(facet.vertex[0].z, fmaxf(facet.vertex[1].z, facet.vertex[2].z)); + const float min_z = fminf(_z(facet.vertex[0]), fminf(_z(facet.vertex[1]), _z(facet.vertex[2]))); + const float max_z = fmaxf(_z(facet.vertex[0]), fmaxf(_z(facet.vertex[1]), _z(facet.vertex[2]))); #ifdef SLIC3R_DEBUG - printf("\n==> FACET %d (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, - facet.vertex[0].x, facet.vertex[0].y, facet.vertex[0].z, - facet.vertex[1].x, facet.vertex[1].y, facet.vertex[1].z, - facet.vertex[2].x, facet.vertex[2].y, facet.vertex[2].z); + printf("\n==> FACET %zu (%f,%f,%f - %f,%f,%f - %f,%f,%f):\n", facet_idx, + _x(facet.vertex[0]), _y(facet.vertex[0]), _z(facet.vertex[0]), + _x(facet.vertex[1]), _y(facet.vertex[1]), _z(facet.vertex[1]), + _x(facet.vertex[2]), _y(facet.vertex[2]), _z(facet.vertex[2])); printf("z: min = %.2f, max = %.2f\n", min_z, max_z); #endif @@ -560,8 +562,9 @@ TriangleMeshSlicer::_slice_do(size_t facet_idx, std::vector* } } +template void -TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const +TriangleMeshSlicer::slice(const std::vector &z, std::vector* layers) const { std::vector layers_p; this->slice(z, &layers_p); @@ -577,8 +580,20 @@ TriangleMeshSlicer::slice(const std::vector &z, std::vector* } } +template void -TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, +TriangleMeshSlicer::slice(float z, ExPolygons* slices) const +{ + std::vector zz; + zz.push_back(z); + std::vector layers; + this->slice(zz, &layers); + append_to(*slices, layers.front()); +} + +template +void +TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector* lines, boost::mutex* lines_mutex) const { @@ -590,10 +605,10 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int this is needed to get all intersection lines in a consistent order (external on the right of the line) */ int i = 0; - if (facet.vertex[1].z == min_z) { + if (_z(facet.vertex[1]) == min_z) { // vertex 1 has lowest Z i = 1; - } else if (facet.vertex[2].z == min_z) { + } else if (_z(facet.vertex[2]) == min_z) { // vertex 2 has lowest Z i = 2; } @@ -604,7 +619,7 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int stl_vertex* a = &this->v_scaled_shared[a_id]; stl_vertex* b = &this->v_scaled_shared[b_id]; - if (a->z == b->z && a->z == slice_z) { + if (_z(*a) == _z(*b) && _z(*a) == slice_z) { // edge is horizontal and belongs to the current layer stl_vertex &v0 = this->v_scaled_shared[ this->mesh->stl.v_indices[facet_idx].vertex[0] ]; @@ -613,23 +628,23 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int IntersectionLine line; if (min_z == max_z) { line.edge_type = feHorizontal; - if (this->mesh->stl.facet_start[facet_idx].normal.z < 0) { + if (_z(this->mesh->stl.facet_start[facet_idx].normal) < 0) { /* if normal points downwards this is a bottom horizontal facet so we reverse its point order */ std::swap(a, b); std::swap(a_id, b_id); } - } else if (v0.z < slice_z || v1.z < slice_z || v2.z < slice_z) { + } else if (_z(v0) < slice_z || _z(v1) < slice_z || _z(v2) < slice_z) { line.edge_type = feTop; std::swap(a, b); std::swap(a_id, b_id); } else { line.edge_type = feBottom; } - line.a.x = a->x; - line.a.y = a->y; - line.b.x = b->x; - line.b.y = b->y; + line.a.x = _x(*a); + line.a.y = _y(*a); + line.b.x = _x(*b); + line.b.y = _y(*b); line.a_id = a_id; line.b_id = b_id; if (lines_mutex != NULL) { @@ -645,26 +660,26 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int // because we won't find anything interesting if (line.edge_type != feHorizontal) return; - } else if (a->z == slice_z) { + } else if (_z(*a) == slice_z) { IntersectionPoint point; - point.x = a->x; - point.y = a->y; + point.x = _x(*a); + point.y = _y(*a); point.point_id = a_id; points.push_back(point); points_on_layer.push_back(points.size()-1); - } else if (b->z == slice_z) { + } else if (_z(*b) == slice_z) { IntersectionPoint point; - point.x = b->x; - point.y = b->y; + point.x = _x(*b); + point.y = _y(*b); point.point_id = b_id; points.push_back(point); points_on_layer.push_back(points.size()-1); - } else if ((a->z < slice_z && b->z > slice_z) || (b->z < slice_z && a->z > slice_z)) { + } else if ((_z(*a) < slice_z && _z(*b) > slice_z) || (_z(*b) < slice_z && _z(*a) > slice_z)) { // edge intersects the current layer; calculate intersection IntersectionPoint point; - point.x = b->x + (a->x - b->x) * (slice_z - b->z) / (a->z - b->z); - point.y = b->y + (a->y - b->y) * (slice_z - b->z) / (a->z - b->z); + point.x = _x(*b) + (_x(*a) - _x(*b)) * (slice_z - _z(*b)) / (_z(*a) - _z(*b)); + point.y = _y(*b) + (_y(*a) - _y(*b)) * (slice_z - _z(*b)) / (_z(*a) - _z(*b)); point.edge_id = edge_id; points.push_back(point); } @@ -700,14 +715,16 @@ TriangleMeshSlicer::slice_facet(float slice_z, const stl_facet &facet, const int } } +template void -TriangleMeshSlicer::_make_loops_do(size_t i, std::vector* lines, std::vector* layers) const +TriangleMeshSlicer::_make_loops_do(size_t i, std::vector* lines, std::vector* layers) const { this->make_loops((*lines)[i], &(*layers)[i]); } +template void -TriangleMeshSlicer::make_loops(std::vector &lines, Polygons* loops) const +TriangleMeshSlicer::make_loops(std::vector &lines, Polygons* loops) const { /* SVG svg("lines.svg"); @@ -847,8 +864,9 @@ class _area_comp { std::vector* abs_area; }; +template void -TriangleMeshSlicer::make_expolygons_simple(std::vector &lines, ExPolygons* slices) const +TriangleMeshSlicer::make_expolygons_simple(std::vector &lines, ExPolygons* slices) const { Polygons loops; this->make_loops(lines, &loops); @@ -881,8 +899,9 @@ TriangleMeshSlicer::make_expolygons_simple(std::vector &lines, } } +template void -TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) const +TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) const { /* Input loops are not suitable for evenodd nor nonzero fill types, as we might get @@ -944,26 +963,28 @@ TriangleMeshSlicer::make_expolygons(const Polygons &loops, ExPolygons* slices) c slices->insert(slices->end(), ex_slices.begin(), ex_slices.end()); } +template void -TriangleMeshSlicer::make_expolygons(std::vector &lines, ExPolygons* slices) const +TriangleMeshSlicer::make_expolygons(std::vector &lines, ExPolygons* slices) const { Polygons pp; this->make_loops(lines, &pp); this->make_expolygons(pp, slices); } +template void -TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const +TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const { IntersectionLines upper_lines, lower_lines; - float scaled_z = scale_(z); + const float scaled_z = scale_(z); for (int facet_idx = 0; facet_idx < this->mesh->stl.stats.number_of_facets; facet_idx++) { stl_facet* facet = &this->mesh->stl.facet_start[facet_idx]; // find facet extents - float min_z = fminf(facet->vertex[0].z, fminf(facet->vertex[1].z, facet->vertex[2].z)); - float max_z = fmaxf(facet->vertex[0].z, fmaxf(facet->vertex[1].z, facet->vertex[2].z)); + float min_z = fminf(_z(facet->vertex[0]), fminf(_z(facet->vertex[1]), _z(facet->vertex[2]))); + float max_z = fmaxf(_z(facet->vertex[0]), fmaxf(_z(facet->vertex[1]), _z(facet->vertex[2]))); // intersect facet with cutting plane IntersectionLines lines; @@ -992,9 +1013,9 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const // look for the vertex on whose side of the slicing plane there are no other vertices int isolated_vertex; - if ( (facet->vertex[0].z > z) == (facet->vertex[1].z > z) ) { + if ( (_z(facet->vertex[0]) > z) == (_z(facet->vertex[1]) > z) ) { isolated_vertex = 2; - } else if ( (facet->vertex[1].z > z) == (facet->vertex[2].z > z) ) { + } else if ( (_z(facet->vertex[1]) > z) == (_z(facet->vertex[2]) > z) ) { isolated_vertex = 0; } else { isolated_vertex = 1; @@ -1007,12 +1028,12 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const // intersect v0-v1 and v2-v0 with cutting plane and make new vertices stl_vertex v0v1, v2v0; - v0v1.x = v1->x + (v0->x - v1->x) * (z - v1->z) / (v0->z - v1->z); - v0v1.y = v1->y + (v0->y - v1->y) * (z - v1->z) / (v0->z - v1->z); - v0v1.z = z; - v2v0.x = v2->x + (v0->x - v2->x) * (z - v2->z) / (v0->z - v2->z); - v2v0.y = v2->y + (v0->y - v2->y) * (z - v2->z) / (v0->z - v2->z); - v2v0.z = z; + _x(v0v1) = _x(*v1) + (_x(*v0) - _x(*v1)) * (z - _z(*v1)) / (_z(*v0) - _z(*v1)); + _y(v0v1) = _y(*v1) + (_y(*v0) - _y(*v1)) * (z - _z(*v1)) / (_z(*v0) - _z(*v1)); + _z(v0v1) = z; + _x(v2v0) = _x(*v2) + (_x(*v0) - _x(*v2)) * (z - _z(*v2)) / (_z(*v0) - _z(*v2)); + _y(v2v0) = _y(*v2) + (_y(*v0) - _y(*v2)) * (z - _z(*v2)) / (_z(*v0) - _z(*v2)); + _z(v2v0) = z; // build the triangular facet stl_facet triangle; @@ -1032,7 +1053,7 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const quadrilateral[1].vertex[1] = v2v0; quadrilateral[1].vertex[2] = v0v1; - if (v0->z > z) { + if (_z(*v0) > z) { if (upper != NULL) stl_add_facet(&upper->stl, &triangle); if (lower != NULL) { stl_add_facet(&lower->stl, &quadrilateral[0]); @@ -1064,13 +1085,13 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const Polygon p = *polygon; p.reverse(); stl_facet facet; - facet.normal.x = 0; - facet.normal.y = 0; - facet.normal.z = -1; + _x(facet.normal) = 0; + _y(facet.normal) = 0; + _z(facet.normal) = -1; for (size_t i = 0; i <= 2; ++i) { - facet.vertex[i].x = unscale(p.points[i].x); - facet.vertex[i].y = unscale(p.points[i].y); - facet.vertex[i].z = z; + _x(facet.vertex[i]) = unscale(p.points[i].x); + _y(facet.vertex[i]) = unscale(p.points[i].y); + _z(facet.vertex[i]) = z; } stl_add_facet(&upper->stl, &facet); } @@ -1090,13 +1111,13 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const // convert triangles to facets and append them to mesh for (Polygons::const_iterator polygon = triangles.begin(); polygon != triangles.end(); ++polygon) { stl_facet facet; - facet.normal.x = 0; - facet.normal.y = 0; - facet.normal.z = 1; + _x(facet.normal) = 0; + _y(facet.normal) = 0; + _z(facet.normal) = 1; for (size_t i = 0; i <= 2; ++i) { - facet.vertex[i].x = unscale(polygon->points[i].x); - facet.vertex[i].y = unscale(polygon->points[i].y); - facet.vertex[i].z = z; + _x(facet.vertex[i]) = unscale(polygon->points[i].x); + _y(facet.vertex[i]) = unscale(polygon->points[i].y); + _z(facet.vertex[i]) = z; } stl_add_facet(&lower->stl, &facet); } @@ -1107,7 +1128,8 @@ TriangleMeshSlicer::cut(float z, TriangleMesh* upper, TriangleMesh* lower) const stl_get_size(&(lower->stl)); } -TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_scaled_shared(NULL) +template +TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_scaled_shared(NULL) { // build a table to map a facet_idx to its three edge indices this->mesh->require_shared_vertices(); @@ -1166,9 +1188,14 @@ TriangleMeshSlicer::TriangleMeshSlicer(TriangleMesh* _mesh) : mesh(_mesh), v_sca } } -TriangleMeshSlicer::~TriangleMeshSlicer() +template +TriangleMeshSlicer::~TriangleMeshSlicer() { if (this->v_scaled_shared != NULL) free(this->v_scaled_shared); } +template class TriangleMeshSlicer; +template class TriangleMeshSlicer; +template class TriangleMeshSlicer; + } diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index dc8450a51..4a9bb9e5d 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -14,7 +14,7 @@ namespace Slic3r { class TriangleMesh; -class TriangleMeshSlicer; +template class TriangleMeshSlicer; typedef std::vector TriangleMeshPtrs; class TriangleMesh @@ -61,7 +61,9 @@ class TriangleMesh private: void require_shared_vertices(); - friend class TriangleMeshSlicer; + friend class TriangleMeshSlicer; + friend class TriangleMeshSlicer; + friend class TriangleMeshSlicer; }; enum FacetEdgeType { feNone, feTop, feBottom, feHorizontal }; @@ -88,6 +90,7 @@ class IntersectionLine : public Line typedef std::vector IntersectionLines; typedef std::vector IntersectionLinePtrs; +template class TriangleMeshSlicer { public: @@ -96,11 +99,15 @@ class TriangleMeshSlicer ~TriangleMeshSlicer(); void slice(const std::vector &z, std::vector* layers) const; void slice(const std::vector &z, std::vector* layers) const; + void slice(float z, ExPolygons* slices) const; void slice_facet(float slice_z, const stl_facet &facet, const int &facet_idx, const float &min_z, const float &max_z, std::vector* lines, boost::mutex* lines_mutex = NULL) const; + void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const; + static void cut(TriangleMesh* mesh, Axis axis, float z, TriangleMesh* upper, TriangleMesh* lower); + private: typedef std::vector< std::vector > t_facets_edges; t_facets_edges facets_edges; @@ -111,8 +118,36 @@ class TriangleMeshSlicer void make_expolygons(const Polygons &loops, ExPolygons* slices) const; void make_expolygons_simple(std::vector &lines, ExPolygons* slices) const; void make_expolygons(std::vector &lines, ExPolygons* slices) const; + + float& _x(stl_vertex &vertex) const; + float& _y(stl_vertex &vertex) const; + float& _z(stl_vertex &vertex) const; + const float& _x(stl_vertex const &vertex) const; + const float& _y(stl_vertex const &vertex) const; + const float& _z(stl_vertex const &vertex) const; }; +template<> inline float& TriangleMeshSlicer::_x(stl_vertex &vertex) const { return vertex.y; } +template<> inline float& TriangleMeshSlicer::_y(stl_vertex &vertex) const { return vertex.z; } +template<> inline float& TriangleMeshSlicer::_z(stl_vertex &vertex) const { return vertex.x; } +template<> inline float const& TriangleMeshSlicer::_x(stl_vertex const &vertex) const { return vertex.y; } +template<> inline float const& TriangleMeshSlicer::_y(stl_vertex const &vertex) const { return vertex.z; } +template<> inline float const& TriangleMeshSlicer::_z(stl_vertex const &vertex) const { return vertex.x; } + +template<> inline float& TriangleMeshSlicer::_x(stl_vertex &vertex) const { return vertex.z; } +template<> inline float& TriangleMeshSlicer::_y(stl_vertex &vertex) const { return vertex.x; } +template<> inline float& TriangleMeshSlicer::_z(stl_vertex &vertex) const { return vertex.y; } +template<> inline float const& TriangleMeshSlicer::_x(stl_vertex const &vertex) const { return vertex.z; } +template<> inline float const& TriangleMeshSlicer::_y(stl_vertex const &vertex) const { return vertex.x; } +template<> inline float const& TriangleMeshSlicer::_z(stl_vertex const &vertex) const { return vertex.y; } + +template<> inline float& TriangleMeshSlicer::_x(stl_vertex &vertex) const { return vertex.x; } +template<> inline float& TriangleMeshSlicer::_y(stl_vertex &vertex) const { return vertex.y; } +template<> inline float& TriangleMeshSlicer::_z(stl_vertex &vertex) const { return vertex.z; } +template<> inline float const& TriangleMeshSlicer::_x(stl_vertex const &vertex) const { return vertex.x; } +template<> inline float const& TriangleMeshSlicer::_y(stl_vertex const &vertex) const { return vertex.y; } +template<> inline float const& TriangleMeshSlicer::_z(stl_vertex const &vertex) const { return vertex.z; } + } #endif diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 83576c814..0825867be 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -153,6 +153,8 @@ ModelMaterial::attributes() void clear_instances(); int instances_count() %code%{ RETVAL = THIS->instances.size(); %}; + Ref get_instance(int idx) + %code%{ RETVAL = THIS->instances.at(idx); %}; std::string name() %code%{ RETVAL = THIS->name; %}; @@ -187,11 +189,13 @@ ModelMaterial::attributes() %code{% THIS->scale(*versor); %}; void rotate(float angle, Axis axis); void mirror(Axis axis); + void transform_by_instance(ModelInstance* instance, bool dont_translate = false) + %code{% THIS->transform_by_instance(*instance, dont_translate); %}; - Model* cut(double z) + Model* cut(Axis axis, double z) %code%{ RETVAL = new Model(); - THIS->cut(z, RETVAL); + THIS->cut(axis, z, RETVAL); %}; ModelObjectPtrs* split_object() diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index 3b8f1022c..ec76d1aff 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -185,7 +185,7 @@ TriangleMesh::slice(z) std::vector z_f(z.begin(), z.end()); std::vector layers; - TriangleMeshSlicer mslicer(THIS); + TriangleMeshSlicer mslicer(THIS); mslicer.slice(z_f, &layers); AV* layers_av = newAV(); @@ -205,13 +205,28 @@ TriangleMesh::slice(z) OUTPUT: RETVAL +ExPolygons +TriangleMesh::slice_at(axis, z) + Axis axis + double z + CODE: + if (axis == X) { + TriangleMeshSlicer(THIS).slice(z, &RETVAL); + } else if (axis == Y) { + TriangleMeshSlicer(THIS).slice(z, &RETVAL); + } else if (axis == Z) { + TriangleMeshSlicer(THIS).slice(z, &RETVAL); + } + OUTPUT: + RETVAL + void TriangleMesh::cut(z, upper, lower) float z; TriangleMesh* upper; TriangleMesh* lower; CODE: - TriangleMeshSlicer mslicer(THIS); + TriangleMeshSlicer mslicer(THIS); mslicer.cut(z, upper, lower); std::vector From 27e456039c7a4a00606436c18a1160330694e472 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Dec 2016 20:30:40 +0100 Subject: [PATCH 165/225] New --cut-grid command for cutting a mesh in multiple tiles according to a XY grid --- slic3r.pl | 50 ++++++++++++++++++++++++++++++-- src/slic3r.cpp | 41 ++++++++++++++++++++++++++ xs/src/libslic3r/PrintConfig.cpp | 6 ++++ xs/src/libslic3r/PrintConfig.hpp | 2 ++ xs/t/01_trianglemesh.t | 6 ++-- xs/xsp/TriangleMesh.xsp | 12 ++++++-- 6 files changed, 109 insertions(+), 8 deletions(-) diff --git a/slic3r.pl b/slic3r.pl index 35773ac4b..9f3ca42fe 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -12,9 +12,9 @@ BEGIN { use File::Basename qw(basename); use Getopt::Long qw(:config no_auto_abbrev); use List::Util qw(first); -use POSIX qw(setlocale LC_NUMERIC); +use POSIX qw(setlocale LC_NUMERIC ceil); use Slic3r; -use Slic3r::Geometry qw(deg2rad); +use Slic3r::Geometry qw(epsilon X Y Z deg2rad); use Time::HiRes qw(gettimeofday tv_interval); $|++; binmode STDOUT, ':utf8'; @@ -42,6 +42,7 @@ my %cli_options = (); 'merge|m' => \$opt{merge}, 'repair' => \$opt{repair}, 'cut=f' => \$opt{cut}, + 'cut-grid=s' => \$opt{cut_grid}, 'split' => \$opt{split}, 'info' => \$opt{info}, @@ -150,7 +151,7 @@ if (@ARGV) { # slicing from command line $mesh->translate(0, 0, -$mesh->bounding_box->z_min); my $upper = Slic3r::TriangleMesh->new; my $lower = Slic3r::TriangleMesh->new; - $mesh->cut($opt{cut}, $upper, $lower); + $mesh->cut(Z, $opt{cut}, $upper, $lower); $upper->repair; $lower->repair; $upper->write_ascii("${file}_upper.stl") @@ -161,6 +162,49 @@ if (@ARGV) { # slicing from command line exit; } + if ($opt{cut_grid}) { + my ($grid_x, $grid_y) = split /[,x]/, $opt{cut_grid}, 2; + foreach my $file (@ARGV) { + $file = Slic3r::decode_path($file); + my $model = Slic3r::Model->read_from_file($file); + $model->add_default_instances; + my $mesh = $model->mesh; + my $bb = $mesh->bounding_box; + $mesh->translate(0, 0, -$bb->z_min); + + my $x_parts = ceil(($bb->size->x - epsilon)/$grid_x); + my $y_parts = ceil(($bb->size->y - epsilon)/$grid_y); #-- + + for my $i (1..$x_parts) { + my $this = Slic3r::TriangleMesh->new; + if ($i == $x_parts) { + $this = $mesh; + } else { + my $next = Slic3r::TriangleMesh->new; + $mesh->cut(X, $bb->x_min + ($grid_x * $i), $next, $this); + $this->repair; + $next->repair; + $mesh = $next; + } + + for my $j (1..$y_parts) { + my $tile = Slic3r::TriangleMesh->new; + if ($j == $y_parts) { + $tile = $this; + } else { + my $next = Slic3r::TriangleMesh->new; + $this->cut(Y, $bb->y_min + ($grid_y * $j), $next, $tile); + $tile->repair; + $next->repair; + $this = $next; + } + $tile->write_ascii("${file}_${i}_${j}.stl"); + } + } + } + exit; + } + if ($opt{split}) { foreach my $file (@ARGV) { $file = Slic3r::decode_path($file); diff --git a/src/slic3r.cpp b/src/slic3r.cpp index f97177187..725c20de6 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "boost/filesystem.hpp" using namespace Slic3r; @@ -152,6 +153,46 @@ main(const int argc, const char **argv) IO::STL::write(m, lower.input_file + "_lower.stl"); } } + } else if (cli_config.cut_grid.value.x > 0 && cli_config.cut_grid.value.y > 0) { + TriangleMesh mesh = model->mesh(); + mesh.repair(); + + const BoundingBoxf3 bb = mesh.bounding_box(); + mesh.translate(0, 0, -bb.min.z); + + const Sizef3 size = bb.size(); + const size_t x_parts = ceil((size.x - EPSILON)/cli_config.cut_grid.value.x); + const size_t y_parts = ceil((size.y - EPSILON)/cli_config.cut_grid.value.y); + + for (size_t i = 1; i <= x_parts; ++i) { + TriangleMesh curr; + if (i == x_parts) { + curr = mesh; + } else { + TriangleMesh next; + TriangleMeshSlicer(&mesh).cut(bb.min.x + (cli_config.cut_grid.value.x * i), &next, &curr); + curr.repair(); + next.repair(); + mesh = next; + } + + for (size_t j = 1; j <= y_parts; ++j) { + TriangleMesh tile; + if (j == y_parts) { + tile = curr; + } else { + TriangleMesh next; + TriangleMeshSlicer(&curr).cut(bb.min.y + (cli_config.cut_grid.value.y * j), &next, &tile); + tile.repair(); + next.repair(); + curr = next; + } + + std::ostringstream ss; + ss << model->objects.front()->input_file << "_" << i << "_" << j << ".stl"; + IO::STL::write(tile, ss.str()); + } + } } else { std::cerr << "error: command not supported" << std::endl; return 1; diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 6ffdfefe6..c6e56ee91 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1404,6 +1404,12 @@ CLIConfigDef::CLIConfigDef() def->cli = "cut"; def->default_value = new ConfigOptionFloat(0); + def = this->add("cut_grid", coFloat); + def->label = "Cut"; + def->tooltip = "Cut model in the XY plane into tiles of the specified max size."; + def->cli = "cut-grid"; + def->default_value = new ConfigOptionPoint(); + def = this->add("cut_x", coFloat); def->label = "Cut"; def->tooltip = "Cut model at the given X."; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index df2c33af3..2c7fa2d58 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -590,6 +590,7 @@ class CLIConfig { public: ConfigOptionFloat cut; + ConfigOptionPoint cut_grid; ConfigOptionFloat cut_x; ConfigOptionFloat cut_y; ConfigOptionBool export_obj; @@ -610,6 +611,7 @@ class CLIConfig virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(cut); + OPT_PTR(cut_grid); OPT_PTR(cut_x); OPT_PTR(cut_y); OPT_PTR(export_obj); diff --git a/xs/t/01_trianglemesh.t b/xs/t/01_trianglemesh.t index fd57cf805..377d4a022 100644 --- a/xs/t/01_trianglemesh.t +++ b/xs/t/01_trianglemesh.t @@ -6,6 +6,8 @@ use warnings; use Slic3r::XS; use Test::More tests => 49; +use constant Z => 2; + is Slic3r::TriangleMesh::hello_world(), 'Hello world!', 'hello world'; @@ -117,7 +119,7 @@ my $cube = { { my $upper = Slic3r::TriangleMesh->new; my $lower = Slic3r::TriangleMesh->new; - $m->cut(0, $upper, $lower); + $m->cut(Z, 0, $upper, $lower); $upper->repair; $lower->repair; is $upper->facets_count, 12, 'upper mesh has all facets except those belonging to the slicing plane'; is $lower->facets_count, 0, 'lower mesh has no facets'; @@ -125,7 +127,7 @@ my $cube = { { my $upper = Slic3r::TriangleMesh->new; my $lower = Slic3r::TriangleMesh->new; - $m->cut(10, $upper, $lower); + $m->cut(Z, 10, $upper, $lower); #$upper->repair; $lower->repair; # we expect: # 2 facets on external horizontal surfaces diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index ec76d1aff..1640322df 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -221,13 +221,19 @@ TriangleMesh::slice_at(axis, z) RETVAL void -TriangleMesh::cut(z, upper, lower) +TriangleMesh::cut(axis, z, upper, lower) + Axis axis float z; TriangleMesh* upper; TriangleMesh* lower; CODE: - TriangleMeshSlicer mslicer(THIS); - mslicer.cut(z, upper, lower); + if (axis == X) { + TriangleMeshSlicer(THIS).cut(z, upper, lower); + } else if (axis == Y) { + TriangleMeshSlicer(THIS).cut(z, upper, lower); + } else { + TriangleMeshSlicer(THIS).cut(z, upper, lower); + } std::vector TriangleMesh::bb3() From 6f6ddf50865b69c228a0b8b3ebac73311c454140 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Dec 2016 21:19:58 +0100 Subject: [PATCH 166/225] Prompt for filename and ask whether to start printing when uploading to OctoPrint. #3217 #3027 #2780 #2666 --- lib/Slic3r/GUI/Plater.pm | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 2fb7c2979..0e7ee16ef 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -236,7 +236,16 @@ sub new { $self->{print_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir()); }); EVT_BUTTON($self, $self->{btn_send_gcode}, sub { - $self->{send_gcode_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir()); + my $filename = basename($self->{print}->expanded_output_filepath($main::opt{output})); + $filename = Wx::GetTextFromUser("Save to printer with the following name:", + "OctoPrint", $filename, $self); + + my $dialog = Wx::MessageDialog->new($self, + "Shall I start the print after uploading the file?", + 'OctoPrint', wxICON_QUESTION | wxYES | wxNO); + $self->{send_gcode_file_print} = ($dialog->ShowModal() == wxID_YES); + + $self->{send_gcode_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir() . "/$filename"); }); EVT_BUTTON($self, $self->{btn_export_stl}, \&export_stl); @@ -1340,6 +1349,7 @@ sub send_gcode { # OctoPrint doesn't like Windows paths so we use basename() # Also, since we need to read from filesystem we process it through encode_path() file => [ $path, basename($path) ], + print => $self->{send_gcode_file} ? 1 : 0, ], ); From 1ae8d13ffda1710e82c3099384970ea16bf744bc Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Dec 2016 21:31:41 +0100 Subject: [PATCH 167/225] Query OctoPrint and prompt user if a file with same name exists. #2666 --- lib/Slic3r/GUI/Plater.pm | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 0e7ee16ef..2be9074b6 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -240,6 +240,23 @@ sub new { $filename = Wx::GetTextFromUser("Save to printer with the following name:", "OctoPrint", $filename, $self); + my $process_dialog = Wx::ProgressDialog->new('Querying OctoPrint…', "Checking whether file already exists…", 100, $self, 0); + $process_dialog->Pulse; + + my $ua = LWP::UserAgent->new; + $ua->timeout(5); + my $res = $ua->get("http://" . $self->{config}->octoprint_host . "/api/files/local"); + $process_dialog->Destroy; + if ($res->is_success) { + if ($res->decoded_content =~ /"name":\s*"\Q$filename\E"/) { + my $dialog = Wx::MessageDialog->new($self, + "It looks like a file with the same name already exists in the server. " + . "Shall I overwrite it?", + 'OctoPrint', wxICON_WARNING | wxYES | wxNO); + return if $dialog->ShowModal() == wxID_NO; + } + } + my $dialog = Wx::MessageDialog->new($self, "Shall I start the print after uploading the file?", 'OctoPrint', wxICON_QUESTION | wxYES | wxNO); From fbfd47b23550c46ec4f7997d4675fbb08f5ca84a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Dec 2016 21:47:45 +0100 Subject: [PATCH 168/225] Command for reloading an object from file. #1466 --- lib/Slic3r/GUI/Plater.pm | 38 ++++++++++++++++++++++++++++++++++++- var/arrow_refresh.png | Bin 0 -> 685 bytes xs/src/libslic3r/Model.cpp | 3 +++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100755 var/arrow_refresh.png diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 2be9074b6..99713e059 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -582,6 +582,7 @@ sub load_file { my $model = eval { Slic3r::Model->read_from_file($input_file) }; Slic3r::GUI::show_error($self, $@) if $@; + my @obj_idx = (); if (defined $model) { if ($model->looks_like_multipart_object) { my $dialog = Wx::MessageDialog->new($self, @@ -593,11 +594,13 @@ sub load_file { $model->convert_multipart_object; } } - $self->load_model_objects(@{$model->objects}); + @obj_idx = $self->load_model_objects(@{$model->objects}); $self->statusbar->SetStatusText("Loaded " . basename($input_file)); } $process_dialog->Destroy; + + return @obj_idx; } sub load_model_objects { @@ -679,6 +682,8 @@ sub load_model_objects { $self->object_list_changed; $self->schedule_background_process; + + return @obj_idx; } sub bed_centerf { @@ -1391,6 +1396,34 @@ sub export_stl { $self->statusbar->SetStatusText("STL file exported to $output_file"); } +sub reload_from_disk { + my ($self) = @_; + + my ($obj_idx, $object) = $self->selected_object; + return if !defined $obj_idx; + + my $model_object = $self->{model}->objects->[$obj_idx]; + return if !$model_object->input_file + || !-e $model_object->input_file; + + my @obj_idx = $self->load_file($model_object->input_file); + return if !@obj_idx; + + foreach my $new_obj_idx (@obj_idx) { + my $o = $self->{model}->objects->[$new_obj_idx]; + $o->clear_instances; + $o->add_instance($_) for @{$model_object->instances}; + + if ($o->volumes_count == $model_object->volumes_count) { + for my $i (0..($o->volumes_count-1)) { + $o->get_volume($i)->config->apply($model_object->get_volume($i)->config); + } + } + } + + $self->remove($obj_idx); +} + sub export_object_stl { my $self = shift; @@ -1879,6 +1912,9 @@ sub object_menu { $self->object_settings_dialog; }, undef, 'cog.png'); $menu->AppendSeparator(); + $frame->_append_menu_item($menu, "Reload from Disk", 'Reload the selected file from Disk', sub { + $self->reload_from_disk; + }, undef, 'arrow_refresh.png'); $frame->_append_menu_item($menu, "Export object as STL…", 'Export this single object as STL file', sub { $self->export_object_stl; }, undef, 'brick_go.png'); diff --git a/var/arrow_refresh.png b/var/arrow_refresh.png new file mode 100755 index 0000000000000000000000000000000000000000..0de26566d4102eec080253c2d08985ec58b14838 GIT binary patch literal 685 zcmV;e0#f~nP)`!iy8(2_#ButL^3%VaH2WCpD^U)OZxp@C)2#hU)y+@T%ZNzJigNk%37 zz-WYJwT%teVfiEI+B*@v4ey@58(ld4VY_&5-ox`e@AKg+0U-I`y79bmuw_~y6+4rZ zBG5EdFDS+@M0OSE`>d7SUDOzKZ&h*4eB1iX7tOd9RiYtW2mQ--bUahxr1`i{RG@dM zL#}_X=DDO1{;UI$pFu=dLYT_=5d8WC-sLfjr7UO-HKMAwa=!>)kEhvuwre zuW3yF@ZxFCkI*+ad|5kOX%5zu8IQjhan)UqgSrFGA_0nQFn@Z08DSEUToCSz4Z1ls z&fDbq$T&7|6iq$_uDI$@q1_kQ@dfqk*0>{SDL6V)94@)ete)j++*>bIc9sj}Y;R1o z#OpH+Yt-^4wfv{nern^iVag8add_object(*this); upper->clear_volumes(); lower->clear_volumes(); + upper->input_file = ""; + lower->input_file = ""; for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { ModelVolume* volume = *v; @@ -772,6 +774,7 @@ ModelObject::split(ModelObjectPtrs* new_objects) (*mesh)->repair(); ModelObject* new_object = this->model->add_object(*this, false); + new_object->input_file = ""; ModelVolume* new_volume = new_object->add_volume(**mesh); new_volume->name = volume->name; new_volume->config = volume->config; From 2c11dd768a70c264a6e498f639793b5ca2044e0d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Dec 2016 21:56:57 +0100 Subject: [PATCH 169/225] Write to a temporary file before renaming with the final name. #1844 --- lib/Slic3r/Print.pm | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index b274836e3..0c94fdf5a 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -393,13 +393,16 @@ sub write_gcode { my $self = shift; my ($file) = @_; + my $tempfile; + # open output gcode file if we weren't supplied a file-handle my $fh; if (ref $file eq 'IO::Scalar') { $fh = $file; } else { - Slic3r::open(\$fh, ">", $file) - or die "Failed to open $file for writing\n"; + $tempfile = "$file.tmp"; + Slic3r::open(\$fh, ">", $tempfile) + or die "Failed to open $tempfile for writing\n"; # enable UTF-8 output since user might have entered Unicode characters in fields like notes binmode $fh, ':utf8'; @@ -413,6 +416,10 @@ sub write_gcode { # close our gcode file close $fh; + + if ($tempfile) { + rename $tempfile, $file; + } } # this method will return the supplied input file path after expanding its From c79444a3d78a9946f91c3eb0c2bc3bd1877a44ea Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 17 Dec 2016 21:59:33 +0100 Subject: [PATCH 170/225] Remove unnecessary method call --- lib/Slic3r/Print.pm | 58 +++++++++++++++++++-------------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 0c94fdf5a..fbf15b47f 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -78,7 +78,30 @@ sub export_gcode { # output everything to a G-code file my $output_file = $self->expanded_output_filepath($params{output_file}); $self->status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : "")); - $self->write_gcode($params{output_fh} || $output_file); + + { + # open output gcode file if we weren't supplied a file-handle + my ($fh, $tempfile); + if ($params{output_fh}) { + $fh = $params{output_fh}; + } else { + $tempfile = "$output_file.tmp"; + Slic3r::open(\$fh, ">", $tempfile) + or die "Failed to open $tempfile for writing\n"; + + # enable UTF-8 output since user might have entered Unicode characters in fields like notes + binmode $fh, ':utf8'; + } + + Slic3r::Print::GCode->new( + print => $self, + fh => $fh, + )->export; + + # close our gcode file + close $fh; + rename $tempfile, $output_file if $tempfile; + } # run post-processing scripts if (@{$self->config->post_process}) { @@ -389,39 +412,6 @@ sub make_brim { $self->set_step_done(STEP_BRIM); } -sub write_gcode { - my $self = shift; - my ($file) = @_; - - my $tempfile; - - # open output gcode file if we weren't supplied a file-handle - my $fh; - if (ref $file eq 'IO::Scalar') { - $fh = $file; - } else { - $tempfile = "$file.tmp"; - Slic3r::open(\$fh, ">", $tempfile) - or die "Failed to open $tempfile for writing\n"; - - # enable UTF-8 output since user might have entered Unicode characters in fields like notes - binmode $fh, ':utf8'; - } - - my $exporter = Slic3r::Print::GCode->new( - print => $self, - fh => $fh, - ); - $exporter->export; - - # close our gcode file - close $fh; - - if ($tempfile) { - rename $tempfile, $file; - } -} - # this method will return the supplied input file path after expanding its # format variables with their values sub expanded_output_filepath { From d0f22196adcf24b58cd40dbf43c0b510d74a55c0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Dec 2016 00:42:43 +0100 Subject: [PATCH 171/225] New --rotate-x and --rotate-y CLI options --- src/slic3r.cpp | 3 +++ xs/src/libslic3r/Model.cpp | 2 ++ xs/src/libslic3r/PrintConfig.cpp | 12 ++++++++++++ xs/src/libslic3r/PrintConfig.hpp | 4 ++++ 4 files changed, 21 insertions(+) diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 725c20de6..82f536691 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -89,7 +89,10 @@ main(const int argc, const char **argv) if (cli_config.scale_to_fit.is_positive_volume()) (*o)->scale_to_fit(cli_config.scale_to_fit.value); + // TODO: honor option order? (*o)->scale(cli_config.scale.value); + (*o)->rotate(Geometry::deg2rad(cli_config.rotate_x.value), X); + (*o)->rotate(Geometry::deg2rad(cli_config.rotate_y.value), Y); (*o)->rotate(Geometry::deg2rad(cli_config.rotate.value), Z); } diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index a494228a9..0fe072218 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -617,6 +617,7 @@ ModelObject::scale(float factor) void ModelObject::scale(const Pointf3 &versor) { + if (versor.x == 1 && versor.y == 1 && versor.z == 1) return; for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { (*v)->mesh.scale(versor); } @@ -643,6 +644,7 @@ ModelObject::scale_to_fit(const Sizef3 &size) void ModelObject::rotate(float angle, const Axis &axis) { + if (angle == 0) return; for (ModelVolumePtrs::const_iterator v = this->volumes.begin(); v != this->volumes.end(); ++v) { (*v)->mesh.rotate(angle, axis); } diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index c6e56ee91..7a4067d3e 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -1464,6 +1464,18 @@ CLIConfigDef::CLIConfigDef() def->cli = "rotate"; def->default_value = new ConfigOptionFloat(0); + def = this->add("rotate_x", coFloat); + def->label = "Rotate around X"; + def->tooltip = "Rotation angle around the X axis in degrees (0-360, default: 0)."; + def->cli = "rotate-x"; + def->default_value = new ConfigOptionFloat(0); + + def = this->add("rotate_y", coFloat); + def->label = "Rotate around Y"; + def->tooltip = "Rotation angle around the Y axis in degrees (0-360, default: 0)."; + def->cli = "rotate-y"; + def->default_value = new ConfigOptionFloat(0); + def = this->add("save", coString); def->label = "Save config file"; def->tooltip = "Save configuration to the specified file."; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 2c7fa2d58..dff798a75 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -600,6 +600,8 @@ class CLIConfig ConfigOptionStrings load; ConfigOptionString output; ConfigOptionFloat rotate; + ConfigOptionFloat rotate_x; + ConfigOptionFloat rotate_y; ConfigOptionString save; ConfigOptionFloat scale; ConfigOptionPoint3 scale_to_fit; @@ -621,6 +623,8 @@ class CLIConfig OPT_PTR(load); OPT_PTR(output); OPT_PTR(rotate); + OPT_PTR(rotate_x); + OPT_PTR(rotate_y); OPT_PTR(save); OPT_PTR(scale); OPT_PTR(scale_to_fit); From accd174566ae9fcc77b662d22ab3a93c43b73add Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Dec 2016 12:00:18 +0100 Subject: [PATCH 172/225] Use absolute coordinates for the modifier sliders --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 82 +++++++++---------- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 89 +++++++++------------ 2 files changed, 76 insertions(+), 95 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm index 683b990bd..18cd7008a 100644 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -1,4 +1,4 @@ -# Generate an anonymous or "lambda" 3D object. This gets used with the Add Generic option in Settings. +# Generate an anonymous or "lambda" 3D object. This gets used with the Create Modifier option in Settings. # package Slic3r::GUI::Plater::LambdaObjectDialog; @@ -15,40 +15,35 @@ use base 'Wx::Dialog'; sub new { my $class = shift; my ($parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, "Lambda Object", wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); + my $self = $class->SUPER::new($parent, -1, "Create Modifier", wxDefaultPosition, [500,500], + wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); + # Note whether the window was already closed, so a pending update is not executed. $self->{already_closed} = 0; $self->{object_parameters} = { - type => "box", - dim => [1, 1, 1], - cyl_r => 1, - cyl_h => 1, + type => 'slab', + dim => [1, 1, 1], + cyl_r => 1, + cyl_h => 1, sph_rho => 1.0, - slab_h => 1.0, - slab_z => 0.0, + slab_h => 1.0, }; - $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); - my $button_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - my $button_ok = $self->CreateStdDialogButtonSizer(wxOK); - my $button_cancel = $self->CreateStdDialogButtonSizer(wxCANCEL); - $button_sizer->Add($button_ok); - $button_sizer->Add($button_cancel); + $self->{sizer} = Wx::BoxSizer->new(wxVERTICAL); + my $buttons = $self->CreateStdDialogButtonSizer(wxOK | wxCANCEL); EVT_BUTTON($self, wxID_OK, sub { - # validate user input - return if !$self->CanClose; - $self->EndModal(wxID_OK); $self->Destroy; }); EVT_BUTTON($self, wxID_CANCEL, sub { - # validate user input - return if !$self->CanClose; - $self->EndModal(wxID_CANCEL); $self->Destroy; }); + $self->{type} = Wx::ComboBox->new($self, 1, $self->{object_parameters}{type}, + wxDefaultPosition, wxDefaultSize, + [qw(slab box cylinder sphere)], wxCB_READONLY); + my $optgroup_box; $optgroup_box = $self->{optgroup_box} = Slic3r::GUI::OptionsGroup->new( parent => $self, @@ -65,26 +60,27 @@ sub new { }, label_width => 100, ); - my @options = ("box", "slab", "cylinder", "sphere"); - $self->{type} = Wx::ComboBox->new($self, 1, "box", wxDefaultPosition, wxDefaultSize, \@options, wxCB_READONLY); $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 0, - label => 'L', + label => 'L (x)', type => 'f', - default => '1', + default => $self->{object_parameters}{dim}[0], + sidetext => 'mm', )); $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 1, - label => 'W', + label => 'W (x)', type => 'f', - default => '1', + default => $self->{object_parameters}{dim}[1], + sidetext => 'mm', )); $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 2, - label => 'H', + label => 'H (z)', type => 'f', - default => '1', + default => $self->{object_parameters}{dim}[2], + sidetext => 'mm', )); my $optgroup_cylinder; @@ -108,13 +104,15 @@ sub new { opt_id => "cyl_r", label => 'Radius', type => 'f', - default => '1', + default => $self->{object_parameters}{cyl_r}, + sidetext => 'mm', )); $optgroup_cylinder->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => "cyl_h", label => 'Height', type => 'f', - default => '1', + default => $self->{object_parameters}{cyl_h}, + sidetext => 'mm', )); my $optgroup_sphere; @@ -136,9 +134,10 @@ sub new { $optgroup_sphere->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => "sph_rho", - label => 'Rho', + label => 'Radius', type => 'f', - default => '1', + default => $self->{object_parameters}{sph_rho}, + sidetext => 'mm', )); my $optgroup_slab; @@ -148,7 +147,7 @@ sub new { on_change => sub { # Do validation my ($opt_id) = @_; - if ($opt_id eq 'slab_z' || $opt_id eq 'slab_h') { + if ($opt_id eq 'slab_h') { if (!looks_like_number($optgroup_slab->get_value($opt_id))) { return 0; } @@ -159,15 +158,10 @@ sub new { ); $optgroup_slab->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => "slab_h", - label => 'H', + label => 'Thickness', type => 'f', - default => '1', - )); - $optgroup_slab->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => "slab_z", - label => 'Initial Z', - type => 'f', - default => '0', + default => $self->{object_parameters}{slab_h}, + sidetext => 'mm', )); @@ -182,7 +176,7 @@ sub new { $self->{sizer}->Add($optgroup_cylinder->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->{sizer}->Add($optgroup_sphere->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->{sizer}->Add($optgroup_slab->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); - $self->{sizer}->Add($button_sizer,0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); + $self->{sizer}->Add($buttons,0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10); $self->_update_ui; $self->SetSizer($self->{sizer}); @@ -192,9 +186,7 @@ sub new { return $self; } -sub CanClose { - return 1; -} + sub ObjectParameter { my ($self) = @_; return $self->{object_parameters}; diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 6955d2775..94cbe931a 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -22,18 +22,6 @@ sub new { my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); my $object = $self->{model_object} = $params{model_object}; - - # Save state for sliders. - $self->{move_options} = { - x => 0, - y => 0, - z => 0, - }; - $self->{last_coords} = { - x => 0, - y => 0, - z => 0, - }; # create TreeCtrl my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100], @@ -53,7 +41,7 @@ sub new { # buttons $self->{btn_load_part} = Wx::Button->new($self, -1, "Load part…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); $self->{btn_load_modifier} = Wx::Button->new($self, -1, "Load modifier…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); - $self->{btn_load_lambda_modifier} = Wx::Button->new($self, -1, "Load generic…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); + $self->{btn_load_lambda_modifier} = Wx::Button->new($self, -1, "Create modifier…", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); $self->{btn_delete} = Wx::Button->new($self, -1, "Delete part", wxDefaultPosition, wxDefaultSize, wxBU_LEFT); if ($Slic3r::GUI::have_button_icons) { $self->{btn_load_part}->SetBitmap(Wx::Bitmap->new($Slic3r::var->("brick_add.png"), wxBITMAP_TYPE_PNG)); @@ -88,8 +76,9 @@ sub new { # genates tens of events for a single value change. # Only trigger the recalculation if the value changes # or a live preview was activated and the mesh cut is not valid yet. - if ($self->{move_options}{$opt_id} != $optgroup_movers->get_value($opt_id)) { - $self->{move_options}{$opt_id} = $optgroup_movers->get_value($opt_id); + my $new = Slic3r::Pointf3->new(map $optgroup_movers->get_value($_), qw(x y z)); + if ($self->{move_target}->distance_to($new) > 0) { + $self->{move_target} = $new; wxTheApp->CallAfter(sub { $self->_update; }); @@ -102,8 +91,6 @@ sub new { type => 'slider', label => 'X', default => 0, - min => -($self->{model_object}->bounding_box->size->x)*4, - max => $self->{model_object}->bounding_box->size->x*4, full_width => 1, )); $optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( @@ -111,8 +98,6 @@ sub new { type => 'slider', label => 'Y', default => 0, - min => -($self->{model_object}->bounding_box->size->y)*4, - max => $self->{model_object}->bounding_box->size->y*4, full_width => 1, )); $optgroup_movers->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( @@ -120,13 +105,11 @@ sub new { type => 'slider', label => 'Z', default => 0, - min => -($self->{model_object}->bounding_box->size->z)*4, - max => $self->{model_object}->bounding_box->size->z*4, full_width => 1, )); # left pane with tree - my $left_sizer = Wx::BoxSizer->new(wxVERTICAL); + my $left_sizer = $self->{left_sizer} = Wx::BoxSizer->new(wxVERTICAL); $left_sizer->Add($tree, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10); $left_sizer->Add($buttons_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10); $left_sizer->Add($settings_sizer, 1, wxEXPAND | wxALL, 0); @@ -249,16 +232,7 @@ sub selection_changed { $self->{optgroup_movers}->set_value("x", 0); $self->{optgroup_movers}->set_value("y", 0); $self->{optgroup_movers}->set_value("z", 0); - $self->{move_options} = { - x => 0, - y => 0, - z => 0, - }; - $self->{last_coords} = { - x => 0, - y => 0, - z => 0, - }; + $self->{move_target} = Slic3r::Pointf3->new; if (my $itemData = $self->get_selection) { my ($config, @opt_keys); @@ -273,9 +247,21 @@ sub selection_changed { my $volume = $self->{model_object}->volumes->[ $itemData->{volume_id} ]; if ($volume->modifier) { - $self->{optgroup_movers}->enable; + my $movers = $self->{optgroup_movers}; + + my $obj_bb = $self->{model_object}->raw_bounding_box; + my $vol_bb = $volume->mesh->bounding_box; + my $vol_size = $vol_bb->size; + $movers->get_field('x')->set_range($obj_bb->x_min - $vol_size->x, $obj_bb->x_max); + $movers->get_field('y')->set_range($obj_bb->y_min - $vol_size->y, $obj_bb->y_max); #,, + $movers->get_field('z')->set_range($obj_bb->z_min - $vol_size->z, $obj_bb->z_max); + $movers->get_field('x')->set_value($vol_bb->x_min); + $movers->get_field('y')->set_value($vol_bb->y_min); + $movers->get_field('z')->set_value($vol_bb->z_min); + + $self->{left_sizer}->Show($movers->sizer); } else { - $self->{optgroup_movers}->disable; + $self->{left_sizer}->Hide($self->{optgroup_movers}->sizer); } $config = $volume->config; $self->{staticbox}->SetLabel('Part Settings'); @@ -286,7 +272,7 @@ sub selection_changed { # select nothing in 3D preview # attach object config to settings panel - $self->{optgroup_movers}->disable; + $self->{left_sizer}->Hide($self->{optgroup_movers}->sizer); $self->{staticbox}->SetLabel('Object Settings'); @opt_keys = (map @{$_->get_keys}, Slic3r::Config::PrintObject->new, Slic3r::Config::PrintRegion->new); $config = $self->{model_object}->config; @@ -352,15 +338,24 @@ sub on_btn_lambda { my $mesh; if ($type eq "box") { - $mesh = Slic3r::TriangleMesh::make_cube($params->{"dim"}[0], $params->{"dim"}[1], $params->{"dim"}[2]); + $mesh = Slic3r::TriangleMesh::make_cube(@{$params->{"dim"}}); } elsif ($type eq "cylinder") { $mesh = Slic3r::TriangleMesh::make_cylinder($params->{"cyl_r"}, $params->{"cyl_h"}); } elsif ($type eq "sphere") { $mesh = Slic3r::TriangleMesh::make_sphere($params->{"sph_rho"}); } elsif ($type eq "slab") { - $mesh = Slic3r::TriangleMesh::make_cube($self->{model_object}->bounding_box->size->x*1.5, $self->{model_object}->bounding_box->size->y*1.5, $params->{"slab_h"}); #** - # box sets the base coordinate at 0,0, move to center of plate and move it up to initial_z - $mesh->translate(-$self->{model_object}->bounding_box->size->x*1.5/2.0, -$self->{model_object}->bounding_box->size->y*1.5/2.0, $params->{"slab_z"}); #** + my $size = $self->{model_object}->bounding_box->size; + $mesh = Slic3r::TriangleMesh::make_cube( + $size->x*1.5, + $size->y*1.5, #** + $params->{"slab_h"}, + ); + # box sets the base coordinate at 0,0, move to center of plate + $mesh->translate( + -$size->x*1.5/2.0, + -$size->y*1.5/2.0, #** + 0, + ); } else { return; } @@ -373,7 +368,7 @@ sub on_btn_lambda { $new_volume->config->set_ifndef('extruder', 0); $self->{parts_changed} = 1; - $self->_parts_changed; + $self->_parts_changed($self->{model_object}->volumes_count-1); } sub on_btn_delete { @@ -397,9 +392,9 @@ sub on_btn_delete { } sub _parts_changed { - my ($self) = @_; + my ($self, $selected_volume_idx) = @_; - $self->reload_tree; + $self->reload_tree($selected_volume_idx); if ($self->{canvas}) { $self->{canvas}->reset_objects; $self->{canvas}->load_object($self->{model_object}); @@ -436,17 +431,11 @@ sub PartSettingsChanged { sub _update { my ($self) = @_; - my ($m_x, $m_y, $m_z) = ($self->{move_options}{x}, $self->{move_options}{y}, $self->{move_options}{z}); - my ($l_x, $l_y, $l_z) = ($self->{last_coords}{x}, $self->{last_coords}{y}, $self->{last_coords}{z}); - + my $itemData = $self->get_selection; if ($itemData && $itemData->{type} eq 'volume') { - my $d = Slic3r::Pointf3->new($m_x - $l_x, $m_y - $l_y, $m_z - $l_z); my $volume = $self->{model_object}->volumes->[$itemData->{volume_id}]; - $volume->mesh->translate(@{$d}); - $self->{last_coords}{x} = $m_x; - $self->{last_coords}{y} = $m_y; - $self->{last_coords}{z} = $m_z; + $volume->mesh->translate(@{ $volume->mesh->bounding_box->min_point->vector_to($self->{move_target}) }); } $self->{parts_changed} = 1; From 54f782c121dea874c91cccb5bfe63c3d4095eac8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Dec 2016 12:09:01 +0100 Subject: [PATCH 173/225] Preserve transparency when modifier volumes are selected --- lib/Slic3r/GUI/3DScene.pm | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 268b5f615..511305efc 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -44,9 +44,8 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init use constant TRACKBALLSIZE => 0.8; use constant TURNTABLE_MODE => 1; use constant GROUND_Z => -0.02; -use constant DEFAULT_COLOR => [1,1,0]; -use constant SELECTED_COLOR => [0,1,0,1]; -use constant HOVER_COLOR => [0.4,0.9,0,1]; +use constant SELECTED_COLOR => [0,1,0]; +use constant HOVER_COLOR => [0.4,0.9,0]; use constant PI => 3.1415927; # Constant to determine if Vertex Buffer objects are used to draw @@ -1030,9 +1029,9 @@ sub draw_volumes { my $b = ($volume_idx & 0x00FF0000) >> 16; glColor4f($r/255.0, $g/255.0, $b/255.0, 1); } elsif ($volume->selected) { - glColor4f(@{ &SELECTED_COLOR }); + glColor4f(@{ &SELECTED_COLOR }, $volume->color->[3]); } elsif ($volume->hover) { - glColor4f(@{ &HOVER_COLOR }); + glColor4f(@{ &HOVER_COLOR }, $volume->color->[3]); } else { glColor4f(@{ $volume->color }); } From 5cbdb7865d6a5b0441c8b34da7c235446e9df488 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Dec 2016 13:10:25 +0100 Subject: [PATCH 174/225] Cut by Grid also in GUI --- lib/Slic3r/GUI/Plater.pm | 9 ++- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 70 ++++++++++++++++++++---- src/slic3r.cpp | 41 ++------------ xs/src/libslic3r/TriangleMesh.cpp | 41 ++++++++++++++ xs/src/libslic3r/TriangleMesh.hpp | 3 +- xs/xsp/TriangleMesh.xsp | 2 + 6 files changed, 117 insertions(+), 49 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 99713e059..e496e1233 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -618,7 +618,7 @@ sub load_model_objects { $o->repair; push @{ $self->{objects} }, Slic3r::GUI::Plater::Object->new( - name => basename($model_object->input_file), + name => $model_object->name || basename($model_object->input_file), ); push @obj_idx, $#{ $self->{objects} }; @@ -1649,9 +1649,14 @@ sub object_cut_dialog { return unless $dlg->ShowModal == wxID_OK; if (my @new_objects = $dlg->NewModelObjects) { + my $process_dialog = Wx::ProgressDialog->new('Loading…', "Loading new objects…", 100, $self, 0); + $process_dialog->Pulse; + $self->remove($obj_idx); $self->load_model_objects(grep defined($_), @new_objects); - $self->arrange; + $self->arrange if @new_objects <= 2; # don't arrange for grid cuts + + $process_dialog->Destroy; } } diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index 74dcae834..14a3500e0 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -6,6 +6,8 @@ use strict; use warnings; use utf8; +use POSIX qw(ceil); +use Scalar::Util qw(looks_like_number); use Slic3r::Geometry qw(PI X Y Z); use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL); use Wx::Event qw(EVT_CLOSE EVT_BUTTON); @@ -109,8 +111,14 @@ sub new { )); { my $cut_button_sizer = Wx::BoxSizer->new(wxVERTICAL); + $self->{btn_cut} = Wx::Button->new($self, -1, "Perform cut", wxDefaultPosition, wxDefaultSize); + $self->{btn_cut}->SetDefault; $cut_button_sizer->Add($self->{btn_cut}, 0, wxALIGN_RIGHT | wxALL, 10); + + $self->{btn_cut_grid} = Wx::Button->new($self, -1, "Cut by grid…", wxDefaultPosition, wxDefaultSize); + $cut_button_sizer->Add($self->{btn_cut_grid}, 0, wxALIGN_RIGHT | wxALL, 10); + $optgroup->append_line(Slic3r::GUI::OptionsGroup::Line->new( sizer => $cut_button_sizer, )); @@ -144,14 +152,14 @@ sub new { $self->_perform_cut() unless $self->{mesh_cut_valid}; # Adjust position / orientation of the split object halves. - if ($self->{new_model_objects}{lower}) { + if (my $lower = $self->{new_model_objects}[0]) { if ($self->{cut_options}{rotate_lower} && $self->{cut_options}{axis} == Z) { - $self->{new_model_objects}{lower}->rotate(PI, X); + $lower->rotate(PI, X); } - $self->{new_model_objects}{lower}->center_around_origin; # align to Z = 0 + $lower->center_around_origin; # align to Z = 0 } - if ($self->{new_model_objects}{upper}) { - $self->{new_model_objects}{upper}->center_around_origin; # align to Z = 0 + if (my $upper = $self->{new_model_objects}[1]) { + $upper->center_around_origin; # align to Z = 0 } # Note that the window was already closed, so a pending update will not be executed. @@ -159,6 +167,48 @@ sub new { $self->EndModal(wxID_OK); $self->Destroy(); }); + + EVT_BUTTON($self, $self->{btn_cut_grid}, sub { + my $grid_x = Wx::GetTextFromUser("Enter the width of the desired tiles along the X axis:", + "Cut by Grid", 100, $self); + return if !looks_like_number($grid_x) || $grid_x <= 0; + + my $grid_y = Wx::GetTextFromUser("Enter the width of the desired tiles along the Y axis:", + "Cut by Grid", 100, $self); + return if !looks_like_number($grid_y) || $grid_y <= 0; + + my $process_dialog = Wx::ProgressDialog->new('Cutting…', "Cutting model by grid…", 100, $self, 0); + $process_dialog->Pulse; + + my $meshes = $self->{model_object}->mesh->cut_by_grid(Slic3r::Pointf->new($grid_x, $grid_y)); + $self->{new_model_objects} = []; + + my $bb = $self->{model_object}->bounding_box; + $self->{new_model} = my $model = Slic3r::Model->new; + for my $i (0..$#$meshes) { + push @{$self->{new_model_objects}}, my $o = $model->add_object( + name => sprintf('%s (%d)', $self->{model_object}->name, $i+1), + ); + my $v = $o->add_volume( + mesh => $meshes->[$i], + name => $o->name, + ); + my $min = $v->mesh->bounding_box->min_point->clone; + $o->center_around_origin; + my $i = $o->add_instance(offset => Slic3r::Pointf->new(@{$min}[X,Y])); + $i->offset->translate( + 5 * ceil(($i->offset->x - $bb->center->x) / $grid_x), + 5 * ceil(($i->offset->y - $bb->center->y) / $grid_y), + ); + } + + $process_dialog->Destroy; + + # Note that the window was already closed, so a pending update will not be executed. + $self->{already_closed} = 1; + $self->EndModal(wxID_OK); + $self->Destroy(); + }); EVT_CLOSE($self, sub { # Note that the window was already closed, so a pending update will not be executed. @@ -208,12 +258,12 @@ sub _perform_cut my ($new_model) = $self->{model_object}->cut($self->{cut_options}{axis}, $z); my ($upper_object, $lower_object) = @{$new_model->objects}; $self->{new_model} = $new_model; - $self->{new_model_objects} = {}; + $self->{new_model_objects} = []; if ($self->{cut_options}{keep_upper} && $upper_object->volumes_count > 0) { - $self->{new_model_objects}{upper} = $upper_object; + $self->{new_model_objects}[1] = $upper_object; } if ($self->{cut_options}{keep_lower} && $lower_object->volumes_count > 0) { - $self->{new_model_objects}{lower} = $lower_object; + $self->{new_model_objects}[0] = $lower_object; } $self->{mesh_cut_valid} = 1; @@ -241,7 +291,7 @@ sub _update { # get volumes to render my @objects = (); if ($life_preview_active) { - push @objects, values %{$self->{new_model_objects}}; + push @objects, grep defined, @{$self->{new_model_objects}}; } else { push @objects, $self->{model_object}; } @@ -311,7 +361,7 @@ sub _update { sub NewModelObjects { my ($self) = @_; - return values %{ $self->{new_model_objects} }; + return grep defined, @{ $self->{new_model_objects} }; } 1; diff --git a/src/slic3r.cpp b/src/slic3r.cpp index 82f536691..3192fb618 100644 --- a/src/slic3r.cpp +++ b/src/slic3r.cpp @@ -160,41 +160,12 @@ main(const int argc, const char **argv) TriangleMesh mesh = model->mesh(); mesh.repair(); - const BoundingBoxf3 bb = mesh.bounding_box(); - mesh.translate(0, 0, -bb.min.z); - - const Sizef3 size = bb.size(); - const size_t x_parts = ceil((size.x - EPSILON)/cli_config.cut_grid.value.x); - const size_t y_parts = ceil((size.y - EPSILON)/cli_config.cut_grid.value.y); - - for (size_t i = 1; i <= x_parts; ++i) { - TriangleMesh curr; - if (i == x_parts) { - curr = mesh; - } else { - TriangleMesh next; - TriangleMeshSlicer(&mesh).cut(bb.min.x + (cli_config.cut_grid.value.x * i), &next, &curr); - curr.repair(); - next.repair(); - mesh = next; - } - - for (size_t j = 1; j <= y_parts; ++j) { - TriangleMesh tile; - if (j == y_parts) { - tile = curr; - } else { - TriangleMesh next; - TriangleMeshSlicer(&curr).cut(bb.min.y + (cli_config.cut_grid.value.y * j), &next, &tile); - tile.repair(); - next.repair(); - curr = next; - } - - std::ostringstream ss; - ss << model->objects.front()->input_file << "_" << i << "_" << j << ".stl"; - IO::STL::write(tile, ss.str()); - } + TriangleMeshPtrs meshes = mesh.cut_by_grid(cli_config.cut_grid.value); + for (TriangleMeshPtrs::iterator m = meshes.begin(); m != meshes.end(); ++m) { + std::ostringstream ss; + ss << model->objects.front()->input_file << "_" << (m - meshes.begin()) << ".stl"; + IO::STL::write(**m, ss.str()); + delete *m; } } else { std::cerr << "error: command not supported" << std::endl; diff --git a/xs/src/libslic3r/TriangleMesh.cpp b/xs/src/libslic3r/TriangleMesh.cpp index 5ce86817d..2a52c865f 100644 --- a/xs/src/libslic3r/TriangleMesh.cpp +++ b/xs/src/libslic3r/TriangleMesh.cpp @@ -395,6 +395,47 @@ TriangleMesh::split() const return meshes; } +TriangleMeshPtrs +TriangleMesh::cut_by_grid(const Pointf &grid) const +{ + TriangleMesh mesh = *this; + const BoundingBoxf3 bb = mesh.bounding_box(); + const Sizef3 size = bb.size(); + const size_t x_parts = ceil((size.x - EPSILON)/grid.x); + const size_t y_parts = ceil((size.y - EPSILON)/grid.y); + + TriangleMeshPtrs meshes; + for (size_t i = 1; i <= x_parts; ++i) { + TriangleMesh curr; + if (i == x_parts) { + curr = mesh; + } else { + TriangleMesh next; + TriangleMeshSlicer(&mesh).cut(bb.min.x + (grid.x * i), &next, &curr); + curr.repair(); + next.repair(); + mesh = next; + } + + for (size_t j = 1; j <= y_parts; ++j) { + TriangleMesh* tile; + if (j == y_parts) { + tile = new TriangleMesh(curr); + } else { + TriangleMesh next; + tile = new TriangleMesh; + TriangleMeshSlicer(&curr).cut(bb.min.y + (grid.y * j), &next, tile); + tile->repair(); + next.repair(); + curr = next; + } + + meshes.push_back(tile); + } + } + return meshes; +} + void TriangleMesh::merge(const TriangleMesh &mesh) { diff --git a/xs/src/libslic3r/TriangleMesh.hpp b/xs/src/libslic3r/TriangleMesh.hpp index 225d744d4..cba8ba43e 100644 --- a/xs/src/libslic3r/TriangleMesh.hpp +++ b/xs/src/libslic3r/TriangleMesh.hpp @@ -49,6 +49,7 @@ class TriangleMesh void center_around_origin(); void rotate(double angle, Point* center); TriangleMeshPtrs split() const; + TriangleMeshPtrs cut_by_grid(const Pointf &grid) const; void merge(const TriangleMesh &mesh); ExPolygons horizontal_projection() const; Polygon convex_hull(); @@ -112,8 +113,6 @@ class TriangleMeshSlicer void cut(float z, TriangleMesh* upper, TriangleMesh* lower) const; - static void cut(TriangleMesh* mesh, Axis axis, float z, TriangleMesh* upper, TriangleMesh* lower); - private: typedef std::vector< std::vector > t_facets_edges; t_facets_edges facets_edges; diff --git a/xs/xsp/TriangleMesh.xsp b/xs/xsp/TriangleMesh.xsp index a3b1bf4a0..373cba12e 100644 --- a/xs/xsp/TriangleMesh.xsp +++ b/xs/xsp/TriangleMesh.xsp @@ -30,6 +30,8 @@ void align_to_origin(); void rotate(double angle, Point* center); TriangleMeshPtrs split(); + TriangleMeshPtrs cut_by_grid(Pointf* grid) + %code{% RETVAL = THIS->cut_by_grid(*grid); %}; void merge(TriangleMesh* mesh) %code{% THIS->merge(*mesh); %}; ExPolygons horizontal_projection(); From 42a6fb130fca14f6b871e295c4d94dccbbf45d4d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Dec 2016 13:31:23 +0100 Subject: [PATCH 175/225] Revert "Merge branch 'lordofhyphens-background-slice-now'" This reverts commit 807d441c6ea5b0b149649bae4dc4fe8a03239c16, reversing changes made to 5cbdb7865d6a5b0441c8b34da7c235446e9df488. --- lib/Slic3r/GUI.pm | 7 ---- lib/Slic3r/GUI/MainFrame.pm | 27 -------------- lib/Slic3r/GUI/Plater.pm | 66 +--------------------------------- lib/Slic3r/GUI/Preferences.pm | 3 -- var/reslice.png | Bin 665 -> 0 bytes 5 files changed, 1 insertion(+), 102 deletions(-) delete mode 100644 var/reslice.png diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index dce9f4be0..760a0f330 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -274,13 +274,6 @@ sub save_settings { Slic3r::Config->write_ini("$datadir/slic3r.ini", $Settings); } -# Called after the Preferences dialog is closed and the program settings are saved. -# Update the UI based on the current preferences. -sub update_ui_from_settings { - my ($self) = @_; - $self->{mainframe}->update_ui_from_settings; -} - sub presets { my ($self, $section) = @_; diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 675449228..970c754f7 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -94,19 +94,10 @@ sub new { # propagate event $event->Skip; }); - - $self->update_ui_from_settings; return $self; } -sub reslice_now { - my ($self) = @_; - - if ($self->{plater}) { - $self->{plater}->reslice; - } -} sub _init_tabpanel { my ($self) = @_; @@ -232,9 +223,6 @@ sub _init_menubar { $self->_append_menu_item($fileMenu, "Slice to SV&G…\tCtrl+G", 'Slice file to SVG', sub { $self->quick_slice(save_as => 1, export_svg => 1); }, undef, 'shape_handles.png'); - $self->{menu_item_reslice_now} = $self->_append_menu_item( - $fileMenu, "(&Re)Slice Now\tCtrl+S", 'Start new slicing process', - sub { $self->reslice_now; }, undef, 'shape_handles.png'); $fileMenu->AppendSeparator(); $self->_append_menu_item($fileMenu, "Repair STL file…", 'Automatically repair an STL file', sub { $self->repair_stl; @@ -483,13 +471,6 @@ sub quick_slice { Slic3r::GUI::catch_error($self, sub { $progress_dialog->Destroy if $progress_dialog }); } -sub reslice_now { - my ($self) = @_; - if ($self->{plater}) { - $self->{plater}->reslice; - } -} - sub repair_stl { my $self = shift; @@ -853,12 +834,4 @@ sub _set_menu_item_icon { } } -# Called after the Preferences dialog is closed and the program settings are saved. -# Update the UI based on the current preferences. -sub update_ui_from_settings { - my ($self) = @_; - $self->{menu_item_reslice_now}->Enable(! $Slic3r::GUI::Settings->{_}{background_processing}); - $self->{plater}->update_ui_from_settings if ($self->{plater}); -} - 1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index d565bd2ad..e496e1233 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -36,7 +36,6 @@ use constant TB_SETTINGS => &Wx::NewId; our $THUMBNAIL_DONE_EVENT : shared = Wx::NewEventType; our $PROGRESS_BAR_EVENT : shared = Wx::NewEventType; our $ERROR_EVENT : shared = Wx::NewEventType; -# Emitted from the worker thread when the G-code export is finished. our $EXPORT_COMPLETED_EVENT : shared = Wx::NewEventType; our $PROCESS_COMPLETED_EVENT : shared = Wx::NewEventType; @@ -196,7 +195,6 @@ sub new { # right pane buttons $self->{btn_export_gcode} = Wx::Button->new($self, -1, "Export G-code…", wxDefaultPosition, [-1, 30], wxBU_LEFT); - $self->{btn_reslice} = Wx::Button->new($self, -1, "Slice now", wxDefaultPosition, [-1, 30], wxBU_LEFT); $self->{btn_print} = Wx::Button->new($self, -1, "Print…", wxDefaultPosition, [-1, 30], wxBU_LEFT); $self->{btn_send_gcode} = Wx::Button->new($self, -1, "Send to printer", wxDefaultPosition, [-1, 30], wxBU_LEFT); $self->{btn_export_stl} = Wx::Button->new($self, -1, "Export STL…", wxDefaultPosition, [-1, 30], wxBU_LEFT); @@ -214,7 +212,6 @@ sub new { export_gcode cog_go.png print arrow_up.png send_gcode arrow_up.png - reslice reslice.png export_stl brick_go.png increase add.png @@ -235,9 +232,6 @@ sub new { EVT_BUTTON($self, $self->{btn_export_gcode}, sub { $self->export_gcode; }); - EVT_BUTTON($self, $self->{btn_reslice}, sub { - $self->reslice; - }); EVT_BUTTON($self, $self->{btn_print}, sub { $self->{print_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir()); }); @@ -270,7 +264,6 @@ sub new { $self->{send_gcode_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir() . "/$filename"); }); - EVT_BUTTON($self, $self->{btn_reslice}, \&reslice); EVT_BUTTON($self, $self->{btn_export_stl}, \&export_stl); if ($self->{htoolbar}) { @@ -423,14 +416,11 @@ sub new { } my $buttons_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $self->{buttons_sizer} = $buttons_sizer; $buttons_sizer->AddStretchSpacer(1); $buttons_sizer->Add($self->{btn_export_stl}, 0, wxALIGN_RIGHT, 0); - $buttons_sizer->Add($self->{btn_reslice}, 0, wxALIGN_RIGHT, 0); $buttons_sizer->Add($self->{btn_print}, 0, wxALIGN_RIGHT, 0); $buttons_sizer->Add($self->{btn_send_gcode}, 0, wxALIGN_RIGHT, 0); $buttons_sizer->Add($self->{btn_export_gcode}, 0, wxALIGN_RIGHT, 0); - $buttons_sizer->Add($self->{btn_reslice}, 0, wxALIGN_RIGHT, 0); my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); $right_sizer->Add($presets, 0, wxEXPAND | wxTOP, 10) if defined $presets; @@ -450,8 +440,6 @@ sub new { $sizer->SetSizeHints($self); $self->SetSizer($sizer); } - - $self->update_ui_from_settings(); return $self; } @@ -489,25 +477,6 @@ sub GetFrame { return &Wx::GetTopLevelParent($self); } -# Called after the Preferences dialog is closed and the program settings are saved. -# Update the UI based on the current preferences. -sub update_ui_from_settings -{ - my ($self) = @_; - if (defined($self->{btn_reslice}) && $self->{buttons_sizer}->IsShown($self->{btn_reslice}) != (! $Slic3r::GUI::Settings->{_}{background_processing})) { - $self->{buttons_sizer}->Show($self->{btn_reslice}, ! $Slic3r::GUI::Settings->{_}{background_processing}); - $self->{buttons_sizer}->Layout; - } -} - -# Update presets (Print settings, Filament, Printer) from their respective tabs. -# Called by -# Slic3r::GUI::Tab::Print::_on_presets_changed -# Slic3r::GUI::Tab::Filament::_on_presets_changed -# Slic3r::GUI::Tab::Printer::_on_presets_changed -# when the presets are loaded or the user selects another preset. -# For Print settings and Printer, synchronize the selection index with their tabs. -# For Filament, synchronize the selection index for a single extruder printer only, otherwise keep the selection. sub update_presets { my $self = shift; my ($group, $presets, $selected, $is_dirty) = @_; @@ -725,13 +694,6 @@ sub bed_centerf { return Slic3r::Pointf->new(unscale($bed_center->x), unscale($bed_center->y)); #) } -sub reslice { - # explicitly cancel a previous thread and start a new one. - my ($self) = @_; - - $self->stop_background_process; - $self->start_background_process; -} sub remove { my $self = shift; my ($obj_idx) = @_; @@ -1200,22 +1162,6 @@ sub resume_background_process { } } -sub reslice { - # explicitly cancel a previous thread and start a new one. - my ($self) = @_; - # Don't reslice if export of G-code or sending to OctoPrint is running. - if ($Slic3r::have_threads && ! defined($self->{export_gcode_output_file}) && ! defined($self->{send_gcode_file})) { - $self->stop_background_process; - $self->statusbar->SetCancelCallback(sub { - $self->stop_background_process; - $self->statusbar->SetStatusText("Slicing cancelled"); - # this updates buttons status - $self->object_list_changed; - }); - $self->start_background_process; - } -} - sub export_gcode { my ($self, $output_file) = @_; @@ -1350,7 +1296,6 @@ sub on_progress_event { $self->statusbar->SetStatusText("$message…"); } -# Called when the G-code export finishes, either successfully or with an error. # This gets called also if we don't have threads. sub on_export_completed { my ($self, $result) = @_; @@ -1367,7 +1312,6 @@ sub on_export_completed { my $send_gcode = 0; my $do_print = 0; if ($result) { - # G-code file exported successfully. if ($self->{print_file}) { $message = "File added to print queue"; $do_print = 1; @@ -1385,7 +1329,6 @@ sub on_export_completed { wxTheApp->notify($message); $self->do_print if $do_print; - # Send $self->{send_gcode_file} to OctoPrint. $self->send_gcode if $send_gcode; $self->{print_file} = undef; $self->{send_gcode_file} = undef; @@ -1411,8 +1354,6 @@ sub do_print { $self->GetFrame->select_tab(1); } -# Send $self->{send_gcode_file} to OctoPrint. -#FIXME Currently this call blocks the UI. Make it asynchronous. sub send_gcode { my ($self) = @_; @@ -1757,23 +1698,18 @@ sub object_settings_dialog { } } -# Called to update various buttons depending on whether there are any objects or -# whether background processing (export of a G-code, sending to Octoprint, forced background re-slicing) is active. sub object_list_changed { my $self = shift; - # Enable/disable buttons depending on whether there are any objects on the platter. my $have_objects = @{$self->{objects}} ? 1 : 0; my $method = $have_objects ? 'Enable' : 'Disable'; $self->{"btn_$_"}->$method - for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode); + for grep $self->{"btn_$_"}, qw(reset arrange export_gcode export_stl print send_gcode); if ($self->{export_gcode_output_file} || $self->{send_gcode_file}) { - $self->{btn_reslice}->Disable; $self->{btn_export_gcode}->Disable; $self->{btn_print}->Disable; $self->{btn_send_gcode}->Disable; - $self->{btn_reslice}->Disable; } if ($self->{htoolbar}) { diff --git a/lib/Slic3r/GUI/Preferences.pm b/lib/Slic3r/GUI/Preferences.pm index 26a78a570..431f642d6 100644 --- a/lib/Slic3r/GUI/Preferences.pm +++ b/lib/Slic3r/GUI/Preferences.pm @@ -93,9 +93,6 @@ sub _accept { $self->EndModal(wxID_OK); $self->Close; # needed on Linux - - # Nothify the UI to update itself from the ini file. - wxTheApp->update_ui_from_settings; } 1; diff --git a/var/reslice.png b/var/reslice.png deleted file mode 100644 index 167c0eccd73146fb8f0a365cd4734ff614f94cd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 665 zcmV;K0%rY*P)WkaV2hCDeioVd5P(4X8Hn2yAdD2L73^H!-P?aQNpL-9}HG;44Rnl z<xJ%vm1pJ@=f$We^cuAcTxYqxpOe0P%QS5QM9%EB64P3;X?EmSs01%d%k@ZiYcb zLWr*Gv)K$0L!po;iZ?emi1_;Ys_Xh@vtbw}kw{!$U%$M(fFpAfAcQE2qN=K@sx-+wB$+FD@<+aWEJhk4Hkta=8o!gSKtQ<8ex9zu#Z4*URP7GaipKnapmt zQ&pAYxao9SE|*UUA0Hq6e!pc|yWK7v4(qzkavXPgd6`TmL!nS05Qs*j!{LxpN+}%< zhp|}f?Ck9A?aegJ^Ye3#<2+8SUayzu`R#Vg^ZaJB>GgU5U|H5`wc79ZJkOu{d|yCB zN~zE1OQlkVVNgmNjfT5@Ns^k)CL(%F(_~o|5iQHIZQHUehG80wh9pT34-b>c#963T ztM~W!cXxN5NF?(7{49!MFc>rp<8V0CYBgv2!}s6v&lvzzvO>4pEffj>0Jd#wn)dYc z)ai5 z*%*ca^A7+Ni$wtNI2vBBH^ZtDB9}0znBuQ>Y)3k243jlus%BPUxt^MY700000NkvXXu0mjf Date: Sun, 18 Dec 2016 14:01:57 +0100 Subject: [PATCH 176/225] If background slicing is disabled, reslice automatically when selecting the preview tabs. When making changes, go back to the plater view. #2979 --- lib/Slic3r/GUI/Plater.pm | 25 ++++++++++++++++++++++--- lib/Slic3r/GUI/Plater/3DPreview.pm | 1 - 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index e496e1233..626564436 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -114,6 +114,7 @@ sub new { $self->{canvas}->on_instances_moved($on_instances_moved); # Initialize 3D toolpaths preview + $self->{preview3D_page_idx} = -1; if ($Slic3r::GUI::have_OpenGL) { $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}); $self->{preview3D}->canvas->on_viewport_changed(sub { @@ -124,15 +125,29 @@ sub new { } # Initialize toolpaths preview + $self->{toolpaths2D_page_idx} = -1; if ($Slic3r::GUI::have_OpenGL) { $self->{toolpaths2D} = Slic3r::GUI::Plater::2DToolpaths->new($self->{preview_notebook}, $self->{print}); $self->{preview_notebook}->AddPage($self->{toolpaths2D}, 'Layers'); + $self->{toolpaths2D_page_idx} = $self->{preview_notebook}->GetPageCount-1; } EVT_NOTEBOOK_PAGE_CHANGED($self, $self->{preview_notebook}, sub { - if ($self->{preview_notebook}->GetSelection == $self->{preview3D_page_idx}) { - $self->{preview3D}->load_print; - } + wxTheApp->CallAfter(sub { + my $sel = $self->{preview_notebook}->GetSelection; + if ($sel == $self->{preview3D_page_idx} || $sel == $self->{toolpaths2D_page_idx}) { + $self->{preview3D}->load_print; + + if (!$Slic3r::GUI::Settings->{_}{background_processing}) { + $self->statusbar->SetCancelCallback(sub { + $self->stop_background_process; + $self->statusbar->SetStatusText("Slicing cancelled"); + $self->{preview_notebook}->SetSelection(0); + }); + $self->start_background_process; + } + } + }); }); # toolbar for object manipulation @@ -1035,6 +1050,10 @@ sub split_object { sub schedule_background_process { my ($self) = @_; + if (!$Slic3r::GUI::Settings->{_}{background_processing}) { + $self->{preview_notebook}->SetSelection(0); + } + if (defined $self->{apply_config_timer}) { $self->{apply_config_timer}->Start(PROCESS_DELAY, 1); # 1 = one shot } diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index fa71fcb1d..c1d1cf57e 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -126,7 +126,6 @@ sub load_print { #my @volume_ids = $self->canvas->load_object($object->model_object); #$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids; } - $self->canvas->zoom_to_volumes; $self->_loaded(1); } From 7dfea3ecf071663be5cc316018f1436b4b212a41 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Dec 2016 14:28:13 +0100 Subject: [PATCH 177/225] Typo --- lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm index 18cd7008a..e812ddaa0 100644 --- a/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm +++ b/lib/Slic3r/GUI/Plater/LambdaObjectDialog.pm @@ -70,7 +70,7 @@ sub new { )); $optgroup_box->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( opt_id => 1, - label => 'W (x)', + label => 'W (y)', type => 'f', default => $self->{object_parameters}{dim}[1], sidetext => 'mm', From e901514212f10cb5d18a296170af3a4c3cde5c03 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Dec 2016 15:21:27 +0100 Subject: [PATCH 178/225] New feature: brim connections --- lib/Slic3r/GUI/3DScene.pm | 4 +- lib/Slic3r/GUI/Tab.pm | 5 ++- lib/Slic3r/Print.pm | 66 ++++++++++++++++++++++++++++++-- lib/Slic3r/Print/GCode.pm | 4 +- xs/src/libslic3r/Print.cpp | 5 ++- xs/src/libslic3r/PrintConfig.cpp | 8 ++++ xs/src/libslic3r/PrintConfig.hpp | 2 + 7 files changed, 84 insertions(+), 10 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 511305efc..c7b796146 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -1285,7 +1285,9 @@ sub load_print_toolpaths { return if !$print->step_done(STEP_SKIRT); return if !$print->step_done(STEP_BRIM); - return if !$print->has_skirt && $print->config->brim_width == 0; + return if !$print->has_skirt + && $print->config->brim_width == 0 + && $print->config->brim_connections_width == 0; my $qverts = Slic3r::GUI::_3DScene::GLVertexArray->new; my $tverts = Slic3r::GUI::_3DScene::GLVertexArray->new; diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 86c7cde9c..517d469f4 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -479,7 +479,7 @@ sub build { perimeter_acceleration infill_acceleration bridge_acceleration first_layer_acceleration default_acceleration skirts skirt_distance skirt_height min_skirt_length - brim_width + brim_connections_width brim_width support_material support_material_threshold support_material_enforce_layers raft_layers support_material_pattern support_material_spacing support_material_angle @@ -571,6 +571,7 @@ sub build { { my $optgroup = $page->new_optgroup('Brim'); $optgroup->append_single_option_line('brim_width'); + $optgroup->append_single_option_line('brim_connections_width'); } } @@ -848,7 +849,7 @@ sub _update { $self->get_field($_)->toggle($have_skirt) for qw(skirt_distance skirt_height); - my $have_brim = $config->brim_width > 0; + my $have_brim = $config->brim_width > 0 || $config->brim_connections_width; # perimeter_extruder uses the same logic as in Print::extruders() $self->get_field('perimeter_extruder')->toggle($have_perimeters || $have_brim); diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index fbf15b47f..f3ad134e9 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -12,8 +12,9 @@ use Slic3r::ExtrusionPath ':roles'; use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 MIN MAX PI scale unscale convex_hull); use Slic3r::Geometry::Clipper qw(diff_ex union_ex intersection_ex intersection offset - offset2 union union_pt_chained JT_ROUND JT_SQUARE); + offset2 union union_pt_chained JT_ROUND JT_SQUARE diff_pl); use Slic3r::Print::State ':steps'; +use Slic3r::Surface qw(S_TYPE_BOTTOM); our $status_cb; @@ -356,7 +357,7 @@ sub make_brim { # checking whether we need to generate them $self->brim->clear; - if ($self->config->brim_width == 0) { + if ($self->config->brim_width == 0 && $self->config->brim_connections_width == 0) { $self->set_step_done(STEP_BRIM); return; } @@ -390,7 +391,7 @@ sub make_brim { } my @loops = (); - my $num_loops = sprintf "%.0f", $self->config->brim_width / $flow->width; + my $num_loops = int($self->config->brim_width / $flow->width + 0.5); for my $i (reverse 1 .. $num_loops) { # JT_SQUARE ensures no vertex is outside the given offset distance # -0.5 because islands are not represented by their centerlines @@ -409,6 +410,65 @@ sub make_brim { ), ), reverse @{union_pt_chained(\@loops)}); + if ($self->config->brim_connections_width > 0) { + # get islands to connects + @islands = map convex_hull(\@$_), @islands; + @islands = @{offset(\@islands, ($num_loops-0.2) * $flow->scaled_spacing, 10000, JT_SQUARE)}; + + # compute centroid for each island + my @centroids = map $_->centroid, @islands; + + # in order to check visibility we need to account for the connections width, + # so let's use grown islands + my $scaled_width = scale($self->config->brim_connections_width); + my $grown = offset(\@islands, +$scaled_width/2); + + # find pairs of islands having direct visibility + my @lines = (); + for my $i (0..$#islands) { + for my $j (($i+1)..$#islands) { + # check visibility + my $line = Slic3r::Line->new(@centroids[$i,$j]); + next if @{diff_pl([$line->as_polyline], $grown)} != 1; + + push @lines, $line; + } + } + + my $filler = Slic3r::Filler->new_from_type('rectilinear'); + $filler->set_spacing($flow->spacing); + $filler->set_dont_adjust(1); + + # subtract already generated connections in order to prevent crossings + # and overextrusion + my @other = (); + + foreach my $line (@lines) { + my $expolygons = diff_ex( + $line->grow($scaled_width/2), + [ @islands, @other ], + ); + push @other, map $_->clone, map @$_, @$expolygons; + + $filler->set_angle($line->direction); + foreach my $expolygon (@$expolygons) { + my $paths = $filler->fill_surface( + Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_BOTTOM), + layer_height => $first_layer_height, + density => 1, + ); + + $self->brim->append(map Slic3r::ExtrusionPath->new( + polyline => $_, + role => EXTR_ROLE_SKIRT, + mm3_per_mm => $mm3_per_mm, + width => $flow->width, + height => $first_layer_height, + ), @$paths); + } + } + } + $self->set_step_done(STEP_BRIM); } diff --git a/lib/Slic3r/Print/GCode.pm b/lib/Slic3r/Print/GCode.pm index 6764f5be8..25e8fb3c6 100644 --- a/lib/Slic3r/Print/GCode.pm +++ b/lib/Slic3r/Print/GCode.pm @@ -357,7 +357,7 @@ sub process_layer { # check whether we're going to apply spiralvase logic if (defined $self->_spiral_vase) { $self->_spiral_vase->enable( - ($layer->id > 0 || $self->print->config->brim_width == 0) + ($layer->id > 0 || $self->print->config->brim_width == 0 || $self->print->config->brim_connections_width == 0) && ($layer->id >= $self->print->config->skirt_height && !$self->print->has_infinite_skirt) && !defined(first { $_->region->config->bottom_solid_layers > $layer->id } @{$layer->regions}) && !defined(first { $_->perimeters->items_count > 1 } @{$layer->regions}) @@ -447,7 +447,7 @@ sub process_layer { $gcode .= $self->_gcodegen->set_extruder($self->print->regions->[0]->config->perimeter_extruder-1); $self->_gcodegen->set_origin(Slic3r::Pointf->new(0,0)); $self->_gcodegen->avoid_crossing_perimeters->set_use_external_mp(1); - $gcode .= $self->_gcodegen->extrude_loop($_, 'brim', $object->config->support_material_speed) + $gcode .= $self->_gcodegen->extrude($_, 'brim', $object->config->support_material_speed) for @{$self->print->brim}; $self->_brim_done(1); $self->_gcodegen->avoid_crossing_perimeters->set_use_external_mp(0); diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 179d501b5..8b918eebb 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -163,7 +163,8 @@ Print::invalidate_state_by_config_options(const std::vector || *opt_key == "min_skirt_length" || *opt_key == "ooze_prevention") { steps.insert(psSkirt); - } else if (*opt_key == "brim_width") { + } else if (*opt_key == "brim_width" + || *opt_key == "brim_connections_width") { steps.insert(psBrim); steps.insert(psSkirt); } else if (*opt_key == "nozzle_diameter" @@ -304,7 +305,7 @@ Print::object_extruders() const FOREACH_REGION(this, region) { // these checks reflect the same logic used in the GUI for enabling/disabling // extruder selection fields - if ((*region)->config.perimeters.value > 0 || this->config.brim_width.value > 0) + if ((*region)->config.perimeters.value > 0 || this->config.brim_width.value > 0 || this->config.brim_connections_width.value > 0) extruders.insert((*region)->config.perimeter_extruder - 1); if ((*region)->config.fill_density.value > 0) diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 7a4067d3e..9fa971121 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -91,6 +91,14 @@ PrintConfigDef::PrintConfigDef() def->min = 0; def->default_value = new ConfigOptionFloat(60); + def = this->add("brim_connections_width", coFloat); + def->label = "Brim connections width"; + def->tooltip = "If set to a positive value, straight connections will be built on the first layer between adjacent objects."; + def->sidetext = "mm"; + def->cli = "brim-connections-width=f"; + def->min = 0; + def->default_value = new ConfigOptionFloat(0); + def = this->add("brim_width", coFloat); def->label = "Brim width"; def->tooltip = "Horizontal width of the brim that will be printed around each object on the first layer."; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index dff798a75..8388a0615 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -366,6 +366,7 @@ class PrintConfig : public GCodeConfig ConfigOptionInt bed_temperature; ConfigOptionFloat bridge_acceleration; ConfigOptionInt bridge_fan_speed; + ConfigOptionFloat brim_connections_width; ConfigOptionFloat brim_width; ConfigOptionBool complete_objects; ConfigOptionBool cooling; @@ -425,6 +426,7 @@ class PrintConfig : public GCodeConfig OPT_PTR(bed_temperature); OPT_PTR(bridge_acceleration); OPT_PTR(bridge_fan_speed); + OPT_PTR(brim_connections_width); OPT_PTR(brim_width); OPT_PTR(complete_objects); OPT_PTR(cooling); From c661a2fcd46f9e1b7971c1ff62441b202094de98 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Dec 2016 15:24:22 +0100 Subject: [PATCH 179/225] Prevent overextrusion, don't connect multi-rectilinear infill after the first pass --- xs/src/libslic3r/Fill/FillRectilinear.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/xs/src/libslic3r/Fill/FillRectilinear.cpp index 632314d22..5fcb779cf 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.cpp @@ -413,6 +413,7 @@ void FillGrid::_fill_surface_single( direction_t direction2 = direction; direction2.first += PI/2; fill2._fill_single_direction(expolygon, direction, 0, out); + fill2.dont_connect = true; fill2._fill_single_direction(expolygon, direction2, 0, out); } @@ -428,6 +429,7 @@ void FillTriangles::_fill_surface_single( fill2._fill_single_direction(expolygon, direction2, 0, out); + fill2.dont_connect = true; direction2.first += PI/3; fill2._fill_single_direction(expolygon, direction2, 0, out); @@ -447,6 +449,7 @@ void FillStars::_fill_surface_single( fill2._fill_single_direction(expolygon, direction2, 0, out); + fill2.dont_connect = true; direction2.first += PI/3; fill2._fill_single_direction(expolygon, direction2, 0, out); @@ -470,6 +473,7 @@ void FillCubic::_fill_surface_single( fill2._fill_single_direction(expolygon, direction2, -x_shift, out); + fill2.dont_connect = true; direction2.first += PI/3; fill2._fill_single_direction(expolygon, direction2, +x_shift, out); From f2d8ab5b87f6864320e342ca4386cf17e910fee8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 18 Dec 2016 19:40:28 +0100 Subject: [PATCH 180/225] Minor fix: don't reselect the 3D plater when the 2D plater is already selected --- lib/Slic3r/GUI/Plater.pm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 626564436..3243f9e70 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1051,7 +1051,10 @@ sub schedule_background_process { my ($self) = @_; if (!$Slic3r::GUI::Settings->{_}{background_processing}) { - $self->{preview_notebook}->SetSelection(0); + my $sel = $self->{preview_notebook}->GetSelection; + if ($sel == $self->{preview3D_page_idx} || $sel == $self->{toolpaths2D_page_idx}) { + $self->{preview_notebook}->SetSelection(0); + } } if (defined $self->{apply_config_timer}) { From 22708fe126c8f7131c0253fe53f11f37dde29602 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Dec 2016 14:58:05 +0100 Subject: [PATCH 181/225] Improvements to SectionCut --- lib/Slic3r/Test/SectionCut.pm | 192 +++++++++++++++------------ xs/src/libslic3r/ExtrusionEntity.cpp | 27 +++- 2 files changed, 133 insertions(+), 86 deletions(-) diff --git a/lib/Slic3r/Test/SectionCut.pm b/lib/Slic3r/Test/SectionCut.pm index 8cfda13d1..8b3de5a76 100644 --- a/lib/Slic3r/Test/SectionCut.pm +++ b/lib/Slic3r/Test/SectionCut.pm @@ -4,7 +4,7 @@ package Slic3r::Test::SectionCut; use Moo; -use List::Util qw(first min max); +use List::Util qw(any min max); use Slic3r::Geometry qw(unscale); use Slic3r::Geometry::Clipper qw(intersection_pl); use SVG; @@ -13,20 +13,27 @@ use Slic3r::SVG; has 'print' => (is => 'ro', required => 1); has 'scale' => (is => 'ro', default => sub { 30 }); has 'y_percent' => (is => 'ro', default => sub { 0.5 }); # Y coord of section line expressed as factor -has 'line' => (is => 'rw'); +has '_line' => (is => 'lazy'); has '_height' => (is => 'rw'); has '_svg' => (is => 'rw'); has '_svg_style' => (is => 'rw', default => sub { {} }); +has '_bb' => (is => 'lazy'); -sub BUILD { +sub _build__line { my $self = shift; # calculate the Y coordinate of the section line - my $bb = $self->print->bounding_box; + my $bb = $self->_bb; my $y = ($bb->y_min + $bb->y_max) * $self->y_percent; # store our section line - $self->line(Slic3r::Line->new([ $bb->x_min, $y ], [ $bb->x_max, $y ])); + return Slic3r::Line->new([ $bb->x_min, $y ], [ $bb->x_max, $y ]); +} + +sub _build__bb { + my ($self) = @_; + + return $self->print->bounding_box; } sub export_svg { @@ -35,8 +42,7 @@ sub export_svg { # get bounding box of print and its height # (Print should return a BoundingBox3 object instead) - my $bb = $self->print->bounding_box; - my $print_size = $bb->size; + my $print_size = $self->_bb->size; $self->_height(max(map $_->print_z, map @{$_->layers}, @{$self->print->objects})); # initialize the SVG canvas @@ -75,98 +81,118 @@ sub _plot_group { my $self = shift; my ($filter) = @_; - my $bb = $self->print->bounding_box; - my $g = $self->_svg->group(style => { %{$self->_svg_style} }); - foreach my $object (@{$self->print->objects}) { - foreach my $copy (@{$object->_shifted_copies}) { - foreach my $layer (@{$object->layers}, @{$object->support_layers}) { - # get all ExtrusionPath objects - my @paths = map $_->clone, - map { ($_->isa('Slic3r::ExtrusionLoop') || $_->isa('Slic3r::ExtrusionPath::Collection')) ? @$_ : $_ } - grep defined $_, - $filter->($layer); - - # move paths to location of copy - $_->polyline->translate(@$copy) for @paths; - + foreach my $layer (@{$object->layers}, @{$object->support_layers}) { + my @paths = map $_->clone, map @{$_->flatten}, grep defined $_, $filter->($layer); + + my $name = sprintf "%s %d (z = %f)", + ($layer->isa('Slic3r::Layer::Support') ? 'Support Layer' : 'Layer'), + $layer->id, + $layer->print_z; + + my $g = $self->_svg->getElementByID($name) + || $self->_svg->group(id => $name, style => { %{$self->_svg_style} }); + + foreach my $copy (@{$object->_shifted_copies}) { if (0) { # export plan with section line and exit + my @grown = map @{$_->grow}, @paths; + $_->translate(@$copy) for @paths; + require "Slic3r/SVG.pm"; Slic3r::SVG::output( "line.svg", no_arrows => 1, - lines => [ $self->line ], - red_polylines => [ map $_->polyline, @paths ], + polygons => \@grown, + red_lines => [ $self->_line ], ); exit; } - foreach my $path (@paths) { - foreach my $line (@{$path->lines}) { - my @intersections = @{intersection_pl( - [ $self->line->as_polyline ], - $line->grow(Slic3r::Geometry::scale $path->width/2), - )}; - - die "Intersection has more than two points!\n" - if defined first { @$_ > 2 } @intersections; - - # turn intersections to lines - my @lines = map Slic3r::Line->new(@$_), @intersections; - - # align intersections to canvas - $_->translate(-$bb->x_min, 0) for @lines; - - # we want lines oriented from left to right in order to draw - # rectangles correctly - foreach my $line (@lines) { - $line->reverse if $line->a->x > $line->b->x; - } - - if ($path->is_bridge) { - foreach my $line (@lines) { - my $radius = $path->width / 2; - my $width = unscale abs($line->b->x - $line->a->x); - if ((10 * $radius) < $width) { - # we're cutting the path in the longitudinal direction, so we've got a rectangle - $g->rectangle( - 'x' => $self->scale * unscale($line->a->x), - 'y' => $self->scale * $self->_y($layer->print_z), - 'width' => $self->scale * $width, - 'height' => $self->scale * $radius * 2, - 'rx' => $self->scale * $radius * 0.35, - 'ry' => $self->scale * $radius * 0.35, - ); - } else { - $g->circle( - 'cx' => $self->scale * (unscale($line->a->x) + $radius), - 'cy' => $self->scale * $self->_y($layer->print_z - $radius), - 'r' => $self->scale * $radius, - ); - } - } - } else { - foreach my $line (@lines) { - my $height = $path->height; - $height = $layer->height if $height == -1; - $g->rectangle( - 'x' => $self->scale * unscale($line->a->x), - 'y' => $self->scale * $self->_y($layer->print_z), - 'width' => $self->scale * unscale($line->b->x - $line->a->x), - 'height' => $self->scale * $height, - 'rx' => $self->scale * $height * 0.5, - 'ry' => $self->scale * $height * 0.5, - ); - } - } - } - } + $self->_plot_path($_, $g, $copy, $layer) for @paths; } } } } +sub _plot_path { + my ($self, $path, $g, $copy, $layer) = @_; + + my $grown = $path->grow; + $_->translate(@$copy) for @$grown; + my $intersections = intersection_pl( + [ $self->_line->as_polyline ], + $grown, + ); + + if (0 && @$intersections) { + # export plan with section line and exit + require "Slic3r/SVG.pm"; + Slic3r::SVG::output( + "intersections.svg", + no_arrows => 1, + polygons => $grown, + red_lines => [ $self->_line ], + ); + exit; + } + + # turn intersections to lines + die "Intersection has more than two points!\n" + if any { @$_ > 2 } @$intersections; + my @lines = map Slic3r::Line->new(@$_), @$intersections; + + my $is_bridge = $path->isa('Slic3r::ExtrusionPath') + ? $path->is_bridge + : any { $_->is_bridge } @$path; + + foreach my $line (@lines) { + my $this_path = $path; + if ($path->isa('Slic3r::ExtrusionLoop')) { + # FIXME: find the actual ExtrusionPath of this intersection + $this_path = $path->[0]; + } + + # align to canvas + $line->translate(-$self->_bb->x_min, 0); + + # we want lines oriented from left to right in order to draw rectangles correctly + $line->reverse if $line->a->x > $line->b->x; + + if ($is_bridge) { + my $radius = $this_path->width / 2; + my $width = unscale abs($line->b->x - $line->a->x); + if ((10 * $radius) < $width) { + # we're cutting the path in the longitudinal direction, so we've got a rectangle + $g->rectangle( + 'x' => $self->scale * unscale($line->a->x), + 'y' => $self->scale * $self->_y($layer->print_z), + 'width' => $self->scale * $width, + 'height' => $self->scale * $radius * 2, + 'rx' => $self->scale * $radius * 0.35, + 'ry' => $self->scale * $radius * 0.35, + ); + } else { + $g->circle( + 'cx' => $self->scale * (unscale($line->a->x) + $radius), + 'cy' => $self->scale * $self->_y($layer->print_z - $radius), + 'r' => $self->scale * $radius, + ); + } + } else { + my $height = $this_path->height != -1 ? $this_path->height : $layer->height; + $g->rectangle( + 'x' => $self->scale * unscale($line->a->x), + 'y' => $self->scale * $self->_y($layer->print_z), + 'width' => $self->scale * unscale($line->b->x - $line->a->x), + 'height' => $self->scale * $height, + 'rx' => $self->scale * $height * 0.5, + 'ry' => $self->scale * $height * 0.5, + ); + } + } +} + sub _y { my $self = shift; my ($y) = @_; diff --git a/xs/src/libslic3r/ExtrusionEntity.cpp b/xs/src/libslic3r/ExtrusionEntity.cpp index 376a417c8..bd4323650 100644 --- a/xs/src/libslic3r/ExtrusionEntity.cpp +++ b/xs/src/libslic3r/ExtrusionEntity.cpp @@ -3,6 +3,7 @@ #include "ExPolygonCollection.hpp" #include "ClipperUtils.hpp" #include "Extruder.hpp" +#include #include #include #include @@ -224,10 +225,30 @@ ExtrusionLoop::has_overhang_point(const Point &point) const Polygons ExtrusionLoop::grow() const { - Polygons pp; + if (this->paths.empty()) return Polygons(); + + // collect all the path widths + std::vector widths; for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) - append_to(pp, path->grow()); - return pp; + widths.push_back(path->width); + + // grow this polygon with the minimum common width + // (this ensures vertices are grown correctly, which doesn't happen if we just + // union the paths grown individually) + const float min_width = *std::min_element(widths.begin(), widths.end()); + const Polygon p = this->polygon(); + Polygons pp = diff( + offset(p, +scale_(min_width/2)), + offset(p, -scale_(min_width/2)) + ); + + // if we have thicker segments, grow them + if (min_width != *std::max_element(widths.begin(), widths.end())) { + for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) + append_to(pp, path->grow()); + } + + return union_(pp); } double From 11ed3d8cf3fd1713b465604535a3a0403cd6d0ad Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Dec 2016 15:24:06 +0100 Subject: [PATCH 182/225] Fix regression causing top/bottom solid layer number not to be enforced for some slopes. #2750 --- lib/Slic3r/Print/Object.pm | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 75c9877ef..78ff619cb 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -618,19 +618,27 @@ sub discover_horizontal_shells { # find intersection between neighbor and current layer's surfaces # intersections have contours and holes - # we update $solid so that we limit the next neighbor layer to the areas that were - # found on this one - in other words, solid shells on one layer (for a given external surface) - # are always a subset of the shells found on the previous shell layer - # this approach allows for DWIM in hollow sloping vases, where we want bottom - # shells to be generated in the base but not in the walls (where there are many - # narrow bottom surfaces): reassigning $solid will consider the 'shadow' of the - # upper perimeter as an obstacle and shell will not be propagated to more upper layers - my $new_internal_solid = $solid = intersection( + my $new_internal_solid = intersection( $solid, [ map $_->p, grep { ($_->surface_type == S_TYPE_INTERNAL) || ($_->surface_type == S_TYPE_INTERNALSOLID) } @neighbor_fill_surfaces ], 1, ); - next EXTERNAL if !@$new_internal_solid; + if (!@$new_internal_solid) { + # No internal solid needed on this layer. In order to decide whether to continue + # searching on the next neighbor (thus enforcing the configured number of solid + # layers, use different strategies according to configured infill density: + if ($layerm->region->config->fill_density == 0) { + # If user expects the object to be void (for example a hollow sloping vase), + # don't continue the search. In this case, we only generate the external solid + # shell if the object would otherwise show a hole (gap between perimeters of + # the two layers), and internal solid shells are a subset of the shells found + # on each previous layer. + next EXTERNAL; + } else { + # If we have internal infill, we can generate internal solid shells freely. + next NEIGHBOR; + } + } if ($layerm->region->config->fill_density == 0) { # if we're printing a hollow object we discard any solid shell thinner From 807b0111ed640eefabe02a74e3729962ab3ba55d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Dec 2016 15:42:44 +0100 Subject: [PATCH 183/225] More reasonable axes length in 3D preview --- lib/Slic3r/GUI/3DScene.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index c7b796146..fecfc014f 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -909,8 +909,8 @@ sub Render { glDisable(GL_DEPTH_TEST); my $origin = $self->origin; my $axis_len = max( - 0.3 * max(@{ $self->bed_bounding_box->size }), - 2 * max(@{ $volumes_bb->size }), + max(@{ $self->bed_bounding_box->size }), + 1.2 * max(@{ $volumes_bb->size }), ); glLineWidth(2); glBegin(GL_LINES); From 4724f0fb99abee2fa1b3252c4137e738f760e7d6 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Dec 2016 17:04:34 +0100 Subject: [PATCH 184/225] Also check void volume before bridging over infill. #3468 #1986 --- xs/src/libslic3r/PrintObject.cpp | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index b1d25220a..90752461e 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -526,13 +526,13 @@ void PrintObject::bridge_over_infill() { FOREACH_REGION(this->_print, region) { - size_t region_id = region - this->_print->regions.begin(); + const size_t region_id = region - this->_print->regions.begin(); // skip bridging in case there are no voids if ((*region)->config.fill_density.value == 100) continue; // get bridge flow - Flow bridge_flow = (*region)->flow( + const Flow bridge_flow = (*region)->flow( frSolidInfill, -1, // layer height, not relevant for bridge flow true, // bridge @@ -541,6 +541,9 @@ PrintObject::bridge_over_infill() *this ); + // get the average extrusion volume per surface unit + const double mm3_per_mm2 = bridge_flow.mm3_per_mm() / bridge_flow.width; + FOREACH_LAYER(this, layer_it) { // skip first layer if (layer_it == this->layers.begin()) continue; @@ -559,13 +562,23 @@ PrintObject::bridge_over_infill() { Polygons to_bridge_pp = internal_solid; + // Only bridge where internal infill exists below the solid shell matching + // these two conditions: + // 1) its depth is at least equal to our bridge extrusion diameter; + // 2) its free volume (thus considering infill density) is at least equal + // to the volume needed by our bridge flow. + double excess_mm3_per_mm2 = mm3_per_mm2; + // iterate through lower layers spanned by bridge_flow - double bottom_z = layer->print_z - bridge_flow.height; + const double bottom_z = layer->print_z - bridge_flow.height; for (int i = (layer_it - this->layers.begin()) - 1; i >= 0; --i) { const Layer* lower_layer = this->layers[i]; - // stop iterating if layer is lower than bottom_z - if (lower_layer->print_z < bottom_z) break; + // subtract the void volume of this layer + excess_mm3_per_mm2 -= lower_layer->height * (100 - (*region)->config.fill_density.value)/100; + + // stop iterating if both conditions are matched + if (lower_layer->print_z < bottom_z && excess_mm3_per_mm2 <= 0) break; // iterate through regions and collect internal surfaces Polygons lower_internal; @@ -576,9 +589,12 @@ PrintObject::bridge_over_infill() to_bridge_pp = intersection(to_bridge_pp, lower_internal); } + // don't bridge if the volume condition isn't matched + if (excess_mm3_per_mm2 > 0) continue; + // there's no point in bridging too thin/short regions { - double min_width = bridge_flow.scaled_width() * 3; + const double min_width = bridge_flow.scaled_width() * 3; to_bridge_pp = offset2(to_bridge_pp, -min_width, +min_width); } @@ -593,7 +609,7 @@ PrintObject::bridge_over_infill() #endif // compute the remaning internal solid surfaces as difference - ExPolygons not_to_bridge = diff_ex(internal_solid, to_polygons(to_bridge), true); + const ExPolygons not_to_bridge = diff_ex(internal_solid, to_polygons(to_bridge), true); // build the new collection of fill_surfaces { From 7bace8060787781557ab90150a174e0db01012e9 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 19 Dec 2016 19:07:34 +0100 Subject: [PATCH 185/225] Check infill density and normal solid infill flow before bridging over sparse infill. #240 #3468 #1986 --- xs/src/libslic3r/PrintObject.cpp | 39 +++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 90752461e..d3ff4ba50 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -2,6 +2,7 @@ #include "BoundingBox.hpp" #include "ClipperUtils.hpp" #include "Geometry.hpp" +#include namespace Slic3r { @@ -542,7 +543,8 @@ PrintObject::bridge_over_infill() ); // get the average extrusion volume per surface unit - const double mm3_per_mm2 = bridge_flow.mm3_per_mm() / bridge_flow.width; + const double mm3_per_mm = bridge_flow.mm3_per_mm(); + const double mm3_per_mm2 = mm3_per_mm / bridge_flow.width; FOREACH_LAYER(this, layer_it) { // skip first layer @@ -554,6 +556,41 @@ PrintObject::bridge_over_infill() // extract the stInternalSolid surfaces that might be transformed into bridges Polygons internal_solid; layerm->fill_surfaces.filter_by_type(stInternalSolid, &internal_solid); + if (internal_solid.empty()) continue; + + // check whether we should bridge or not according to density + { + // get the normal solid infill flow we would use if not bridging + const Flow normal_flow = layerm->flow(frSolidInfill, false); + + // Bridging over sparse infill has two purposes: + // 1) cover better the gaps of internal sparse infill, especially when + // printing at very low densities; + // 2) provide a greater flow when printing very thin layers where normal + // solid flow would be very poor. + // So we calculate density threshold as interpolation according to normal flow. + // If normal flow would be equal or greater than the bridge flow, we can keep + // a low threshold like 25% in order to bridge only when printing at very low + // densities, when sparse infill has significant gaps. + // If normal flow would be equal or smaller than half the bridge flow, we + // use a higher threshold like 50% in order to bridge in more cases. + // We still never bridge whenever fill density is greater than 50% because + // we would overstuff. + const float min_threshold = 25.0; + const float max_threshold = 50.0; + const float density_threshold = std::max( + std::min( + min_threshold + + (max_threshold - min_threshold) + * (normal_flow.mm3_per_mm() - mm3_per_mm) + / (mm3_per_mm/2 - mm3_per_mm), + max_threshold + ), + min_threshold + ); + + if ((*region)->config.fill_density.value > density_threshold) continue; + } // check whether the lower area is deep enough for absorbing the extra flow // (for obvious physical reasons but also for preventing the bridge extrudates From 12165c727e57c124e5ac86c57561ff6541772d09 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 20 Dec 2016 00:44:03 +0100 Subject: [PATCH 186/225] Ported brim generation to C++/XS --- lib/Slic3r/Print.pm | 120 +--------------- xs/src/libslic3r/ExtrusionEntity.hpp | 3 + .../libslic3r/ExtrusionEntityCollection.cpp | 7 + .../libslic3r/ExtrusionEntityCollection.hpp | 4 +- xs/src/libslic3r/Print.cpp | 136 ++++++++++++++++++ xs/src/libslic3r/Print.hpp | 1 + xs/xsp/Print.xsp | 1 + 7 files changed, 150 insertions(+), 122 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index f3ad134e9..2bb298c19 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -350,126 +350,8 @@ sub make_brim { $_->generate_support_material for @{$self->objects}; $self->make_skirt; - return if $self->step_done(STEP_BRIM); - $self->set_step_started(STEP_BRIM); - - # since this method must be idempotent, we clear brim paths *before* - # checking whether we need to generate them - $self->brim->clear; - - if ($self->config->brim_width == 0 && $self->config->brim_connections_width == 0) { - $self->set_step_done(STEP_BRIM); - return; - } $self->status_cb->(88, "Generating brim"); - - # brim is only printed on first layer and uses perimeter extruder - my $first_layer_height = $self->skirt_first_layer_height; - my $flow = $self->brim_flow; - my $mm3_per_mm = $flow->mm3_per_mm; - - my $grow_distance = $flow->scaled_width / 2; - my @islands = (); # array of polygons - foreach my $obj_idx (0 .. ($self->object_count - 1)) { - my $object = $self->objects->[$obj_idx]; - my $layer0 = $object->get_layer(0); - my @object_islands = ( - (map $_->contour, @{$layer0->slices}), - ); - if (@{ $object->support_layers }) { - my $support_layer0 = $object->support_layers->[0]; - push @object_islands, - (map @{$_->polyline->grow($grow_distance)}, @{$support_layer0->support_fills}) - if $support_layer0->support_fills; - push @object_islands, - (map @{$_->polyline->grow($grow_distance)}, @{$support_layer0->support_interface_fills}) - if $support_layer0->support_interface_fills; - } - foreach my $copy (@{$object->_shifted_copies}) { - push @islands, map { $_->translate(@$copy); $_ } map $_->clone, @object_islands; - } - } - - my @loops = (); - my $num_loops = int($self->config->brim_width / $flow->width + 0.5); - for my $i (reverse 1 .. $num_loops) { - # JT_SQUARE ensures no vertex is outside the given offset distance - # -0.5 because islands are not represented by their centerlines - # (first offset more, then step back - reverse order than the one used for - # perimeters because here we're offsetting outwards) - push @loops, @{offset2(\@islands, ($i + 0.5) * $flow->scaled_spacing, -1.0 * $flow->scaled_spacing, 100000, JT_SQUARE)}; - } - - $self->brim->append(map Slic3r::ExtrusionLoop->new_from_paths( - Slic3r::ExtrusionPath->new( - polyline => Slic3r::Polygon->new(@$_)->split_at_first_point, - role => EXTR_ROLE_SKIRT, - mm3_per_mm => $mm3_per_mm, - width => $flow->width, - height => $first_layer_height, - ), - ), reverse @{union_pt_chained(\@loops)}); - - if ($self->config->brim_connections_width > 0) { - # get islands to connects - @islands = map convex_hull(\@$_), @islands; - @islands = @{offset(\@islands, ($num_loops-0.2) * $flow->scaled_spacing, 10000, JT_SQUARE)}; - - # compute centroid for each island - my @centroids = map $_->centroid, @islands; - - # in order to check visibility we need to account for the connections width, - # so let's use grown islands - my $scaled_width = scale($self->config->brim_connections_width); - my $grown = offset(\@islands, +$scaled_width/2); - - # find pairs of islands having direct visibility - my @lines = (); - for my $i (0..$#islands) { - for my $j (($i+1)..$#islands) { - # check visibility - my $line = Slic3r::Line->new(@centroids[$i,$j]); - next if @{diff_pl([$line->as_polyline], $grown)} != 1; - - push @lines, $line; - } - } - - my $filler = Slic3r::Filler->new_from_type('rectilinear'); - $filler->set_spacing($flow->spacing); - $filler->set_dont_adjust(1); - - # subtract already generated connections in order to prevent crossings - # and overextrusion - my @other = (); - - foreach my $line (@lines) { - my $expolygons = diff_ex( - $line->grow($scaled_width/2), - [ @islands, @other ], - ); - push @other, map $_->clone, map @$_, @$expolygons; - - $filler->set_angle($line->direction); - foreach my $expolygon (@$expolygons) { - my $paths = $filler->fill_surface( - Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_BOTTOM), - layer_height => $first_layer_height, - density => 1, - ); - - $self->brim->append(map Slic3r::ExtrusionPath->new( - polyline => $_, - role => EXTR_ROLE_SKIRT, - mm3_per_mm => $mm3_per_mm, - width => $flow->width, - height => $first_layer_height, - ), @$paths); - } - } - } - - $self->set_step_done(STEP_BRIM); + $self->_make_brim; } # this method will return the supplied input file path after expanding its diff --git a/xs/src/libslic3r/ExtrusionEntity.hpp b/xs/src/libslic3r/ExtrusionEntity.hpp index 7f411f1f4..1d7ef9a99 100644 --- a/xs/src/libslic3r/ExtrusionEntity.hpp +++ b/xs/src/libslic3r/ExtrusionEntity.hpp @@ -165,6 +165,9 @@ class ExtrusionLoop : public ExtrusionEntity // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. double min_mm3_per_mm() const; Polyline as_polyline() const { return this->polygon().split_at_first_point(); } + void append(const ExtrusionPath &path) { + this->paths.push_back(path); + }; }; } diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.cpp b/xs/src/libslic3r/ExtrusionEntityCollection.cpp index ec3e28e6c..091cd3ff0 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.cpp @@ -33,9 +33,16 @@ ExtrusionEntityCollection::swap (ExtrusionEntityCollection &c) } ExtrusionEntityCollection::~ExtrusionEntityCollection() +{ + this->clear(); +} + +void +ExtrusionEntityCollection::clear() { for (ExtrusionEntitiesPtr::iterator it = this->entities.begin(); it != this->entities.end(); ++it) delete *it; + this->entities.clear(); } ExtrusionEntityCollection::operator ExtrusionPaths() const diff --git a/xs/src/libslic3r/ExtrusionEntityCollection.hpp b/xs/src/libslic3r/ExtrusionEntityCollection.hpp index 491411aa7..c3a26df82 100644 --- a/xs/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/xs/src/libslic3r/ExtrusionEntityCollection.hpp @@ -30,9 +30,7 @@ class ExtrusionEntityCollection : public ExtrusionEntity bool empty() const { return this->entities.empty(); }; - void clear() { - this->entities.clear(); - }; + void clear(); void swap (ExtrusionEntityCollection &c); void append(const ExtrusionEntity &entity); void append(const ExtrusionEntitiesPtr &entities); diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 8b918eebb..6fc7e4569 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -1,6 +1,7 @@ #include "Print.hpp" #include "BoundingBox.hpp" #include "ClipperUtils.hpp" +#include "Fill/Fill.hpp" #include "Flow.hpp" #include "Geometry.hpp" #include "SupportMaterial.hpp" @@ -787,6 +788,141 @@ Print::skirt_flow() const ); } +void +Print::_make_brim() +{ + if (this->state.is_done(psBrim)) return; + this->state.set_started(psBrim); + + // since this method must be idempotent, we clear brim paths *before* + // checking whether we need to generate them + this->brim.clear(); + + if (this->config.brim_width == 0 && this->config.brim_connections_width == 0) { + this->state.set_done(psBrim); + return; + } + + // brim is only printed on first layer and uses perimeter extruder + const double first_layer_height = this->skirt_first_layer_height(); + const Flow flow = this->brim_flow(); + const double mm3_per_mm = flow.mm3_per_mm(); + + const coord_t grow_distance = flow.scaled_width()/2; + Polygons islands; + + FOREACH_OBJECT(this, object) { + const Layer &layer0 = *(*object)->get_layer(0); + + Polygons object_islands = layer0.slices.contours(); + + if (!(*object)->support_layers.empty()) { + const SupportLayer &support_layer0 = *(*object)->get_support_layer(0); + + for (ExtrusionEntitiesPtr::const_iterator it = support_layer0.support_fills.entities.begin(); + it != support_layer0.support_fills.entities.end(); ++it) + append_to(object_islands, offset((*it)->as_polyline(), grow_distance)); + + for (ExtrusionEntitiesPtr::const_iterator it = support_layer0.support_interface_fills.entities.begin(); + it != support_layer0.support_interface_fills.entities.end(); ++it) + append_to(object_islands, offset((*it)->as_polyline(), grow_distance)); + } + for (Points::const_iterator copy = (*object)->_shifted_copies.begin(); copy != (*object)->_shifted_copies.end(); + ++copy) { + for (Polygons::const_iterator p = object_islands.begin(); p != object_islands.end(); ++p) { + Polygon p2 = *p; + p2.translate(*copy); + islands.push_back(p2); + } + } + } + + Polygons loops; + const int num_loops = floor(this->config.brim_width / flow.width + 0.5); + for (int i = num_loops; i >= 1; --i) { + // JT_SQUARE ensures no vertex is outside the given offset distance + // -0.5 because islands are not represented by their centerlines + // (first offset more, then step back - reverse order than the one used for + // perimeters because here we're offsetting outwards) + append_to(loops, offset2( + islands, + flow.scaled_spacing() * (i + 0.5), + flow.scaled_spacing() * -1.0, + 100000, + ClipperLib::jtSquare + )); + } + + { + Polygons chained = union_pt_chained(loops); + for (Polygons::const_reverse_iterator p = chained.rbegin(); p != chained.rend(); ++p) { + ExtrusionPath path(erSkirt, mm3_per_mm, flow.width, first_layer_height); + path.polyline = p->split_at_first_point(); + this->brim.append(ExtrusionLoop(path)); + } + } + + if (this->config.brim_connections_width > 0) { + // get islands to connects + for (Polygons::iterator p = islands.begin(); p != islands.end(); ++p) + *p = Geometry::convex_hull(p->points); + + islands = offset(islands, flow.scaled_spacing() * (num_loops-0.2), 10000, jtSquare); + + // compute centroid for each island + Points centroids; + centroids.reserve(islands.size()); + for (Polygons::const_iterator p = islands.begin(); p != islands.end(); ++p) + centroids.push_back(p->centroid()); + + // in order to check visibility we need to account for the connections width, + // so let's use grown islands + const double scaled_width = scale_(this->config.brim_connections_width); + const Polygons grown = offset(islands, +scaled_width/2); + + // find pairs of islands having direct visibility + Lines lines; + for (size_t i = 0; i < islands.size(); ++i) { + for (size_t j = (i+1); j < islands.size(); ++j) { + // check visibility + Line line(centroids[i], centroids[j]); + if (diff_pl((Polyline)line, grown).size() != 1) continue; + lines.push_back(line); + } + } + + std::auto_ptr filler(Fill::new_from_type(ipRectilinear)); + filler->spacing = flow.spacing(); + filler->dont_adjust = true; + filler->density = 1; + + // subtract already generated connections in order to prevent crossings + // and overextrusion + Polygons other; + + for (Lines::const_iterator line = lines.begin(); line != lines.end(); ++line) { + ExPolygons expp = diff_ex( + offset((Polyline)*line, scaled_width/2), + islands + other + ); + + filler->angle = line->direction(); + for (ExPolygons::const_iterator ex = expp.begin(); ex != expp.end(); ++ex) { + append_to(other, (Polygons)*ex); + + const Polylines paths = filler->fill_surface(Surface(stBottom, *ex)); + for (Polylines::const_iterator pl = paths.begin(); pl != paths.end(); ++pl) { + ExtrusionPath path(erSkirt, mm3_per_mm, flow.width, first_layer_height); + path.polyline = *pl; + this->brim.append(path); + } + } + } + } + + this->state.set_done(psBrim); +} + PrintRegionConfig Print::_region_config_from_model_volume(const ModelVolume &volume) diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 50b68d2a5..6cd24ef79 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -203,6 +203,7 @@ class Print double skirt_first_layer_height() const; Flow brim_flow() const; Flow skirt_flow() const; + void _make_brim(); std::set object_extruders() const; std::set support_material_extruders() const; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index b37890380..3992a0aff 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -228,6 +228,7 @@ _constant() double skirt_first_layer_height(); Clone brim_flow(); Clone skirt_flow(); + void _make_brim(); %{ double From 346671e16139d85eb4801eaff3b761b4704ace8e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 20 Dec 2016 13:21:39 +0100 Subject: [PATCH 187/225] Fix crash in bed shape dialog --- lib/Slic3r/GUI/BedShapeDialog.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/GUI/BedShapeDialog.pm b/lib/Slic3r/GUI/BedShapeDialog.pm index ed6fb5cde..9da717ab8 100644 --- a/lib/Slic3r/GUI/BedShapeDialog.pm +++ b/lib/Slic3r/GUI/BedShapeDialog.pm @@ -212,6 +212,7 @@ sub _update_shape { my $rect_origin = $self->{optgroups}[SHAPE_RECTANGULAR]->get_value('rect_origin'); my ($x, $y) = @$rect_size; return if !looks_like_number($x) || !looks_like_number($y); # empty strings or '-' or other things + return if !$x || !$y; my ($x0, $y0) = (0,0); my ($x1, $y1) = ($x,$y); { From b973d2a6826a3c904455c724b3e306873a8c3bf3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 20 Dec 2016 13:25:26 +0100 Subject: [PATCH 188/225] Fix Unicode characters in Config.xsp --- xs/xsp/Config.xsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index ab4bdf025..ba236f93e 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -137,7 +137,7 @@ print_config_def() (void)hv_stores( hv, "label", newSVpvn_utf8(optdef->label.c_str(), optdef->label.length(), true) ); if (!optdef->full_label.empty()) (void)hv_stores( hv, "full_label", newSVpvn_utf8(optdef->full_label.c_str(), optdef->full_label.length(), true) ); - (void)hv_stores( hv, "category", newSVpvn(optdef->category.c_str(), optdef->category.length()) ); + (void)hv_stores( hv, "category", newSVpvn_utf8(optdef->category.c_str(), optdef->category.length()) ); (void)hv_stores( hv, "tooltip", newSVpvn_utf8(optdef->tooltip.c_str(), optdef->tooltip.length(), true) ); (void)hv_stores( hv, "sidetext", newSVpvn_utf8(optdef->sidetext.c_str(), optdef->sidetext.length(), true) ); (void)hv_stores( hv, "cli", newSVpvn(optdef->cli.c_str(), optdef->cli.length()) ); From e585b183732cca6afa2bb6d82e4b6b9cf731dff2 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 20 Dec 2016 13:54:32 +0100 Subject: [PATCH 189/225] One more fix --- xs/xsp/Config.xsp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index ba236f93e..96c4830e0 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -137,7 +137,7 @@ print_config_def() (void)hv_stores( hv, "label", newSVpvn_utf8(optdef->label.c_str(), optdef->label.length(), true) ); if (!optdef->full_label.empty()) (void)hv_stores( hv, "full_label", newSVpvn_utf8(optdef->full_label.c_str(), optdef->full_label.length(), true) ); - (void)hv_stores( hv, "category", newSVpvn_utf8(optdef->category.c_str(), optdef->category.length()) ); + (void)hv_stores( hv, "category", newSVpvn_utf8(optdef->category.c_str(), optdef->category.length(), true) ); (void)hv_stores( hv, "tooltip", newSVpvn_utf8(optdef->tooltip.c_str(), optdef->tooltip.length(), true) ); (void)hv_stores( hv, "sidetext", newSVpvn_utf8(optdef->sidetext.c_str(), optdef->sidetext.length(), true) ); (void)hv_stores( hv, "cli", newSVpvn(optdef->cli.c_str(), optdef->cli.length()) ); From c5a285f43576d253c0d68a2441ff3429567393cb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 20 Dec 2016 19:01:51 +0100 Subject: [PATCH 190/225] Ported expanded_output_filepath() to C++/XS --- lib/Slic3r/GUI/MainFrame.pm | 2 +- lib/Slic3r/GUI/Plater.pm | 8 +++--- lib/Slic3r/Print.pm | 43 ++--------------------------- lib/Slic3r/Print/Simple.pm | 2 +- t/custom_gcode.t | 2 +- xs/src/libslic3r/Print.cpp | 55 +++++++++++++++++++++++++++++++++++++ xs/src/libslic3r/Print.hpp | 2 ++ xs/xsp/Print.xsp | 2 ++ 8 files changed, 68 insertions(+), 48 deletions(-) diff --git a/lib/Slic3r/GUI/MainFrame.pm b/lib/Slic3r/GUI/MainFrame.pm index 970c754f7..c1df8b30e 100644 --- a/lib/Slic3r/GUI/MainFrame.pm +++ b/lib/Slic3r/GUI/MainFrame.pm @@ -426,7 +426,7 @@ sub quick_slice { if ($params{reslice}) { $output_file = $qs_last_output_file if defined $qs_last_output_file; } elsif ($params{save_as}) { - $output_file = $sprint->expanded_output_filepath; + $output_file = $sprint->output_filepath; $output_file =~ s/\.gcode$/.svg/i if $params{export_svg}; my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:', wxTheApp->output_path(dirname($output_file)), diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 3243f9e70..427b89f61 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -251,7 +251,7 @@ sub new { $self->{print_file} = $self->export_gcode(Wx::StandardPaths::Get->GetTempDir()); }); EVT_BUTTON($self, $self->{btn_send_gcode}, sub { - my $filename = basename($self->{print}->expanded_output_filepath($main::opt{output})); + my $filename = basename($self->{print}->output_filepath($main::opt{output})); $filename = Wx::GetTextFromUser("Save to printer with the following name:", "OctoPrint", $filename, $self); @@ -1218,9 +1218,9 @@ sub export_gcode { # select output file if ($output_file) { - $self->{export_gcode_output_file} = $self->{print}->expanded_output_filepath($output_file); + $self->{export_gcode_output_file} = $self->{print}->output_filepath($output_file); } else { - my $default_output_file = $self->{print}->expanded_output_filepath($main::opt{output}); + my $default_output_file = $self->{print}->output_filepath($main::opt{output}); my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', wxTheApp->output_path(dirname($default_output_file)), basename($default_output_file), &Slic3r::GUI::FILE_WILDCARDS->{gcode}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); if ($dlg->ShowModal != wxID_OK) { @@ -1477,7 +1477,7 @@ sub _get_export_file { my $output_file = $main::opt{output}; { - $output_file = $self->{print}->expanded_output_filepath($output_file); + $output_file = $self->{print}->output_filepath($output_file); $output_file =~ s/\.gcode$/$suffix/i; my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file), basename($output_file), &Slic3r::GUI::MODEL_WILDCARD, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 2bb298c19..6be0b058b 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -77,7 +77,7 @@ sub export_gcode { $self->process; # output everything to a G-code file - my $output_file = $self->expanded_output_filepath($params{output_file}); + my $output_file = $self->output_filepath($params{output_file} // ''); $self->status_cb->(90, "Exporting G-code" . ($output_file ? " to $output_file" : "")); { @@ -128,7 +128,7 @@ sub export_svg { my $fh = $params{output_fh}; if (!$fh) { - my $output_file = $self->expanded_output_filepath($params{output_file}); + my $output_file = $self->output_filepath($params{output_file}); $output_file =~ s/\.gcode$/.svg/i; Slic3r::open(\$fh, ">", $output_file) or die "Failed to open $output_file for writing\n"; print "Exporting to $output_file..." unless $params{quiet}; @@ -354,45 +354,6 @@ sub make_brim { $self->_make_brim; } -# this method will return the supplied input file path after expanding its -# format variables with their values -sub expanded_output_filepath { - my $self = shift; - my ($path) = @_; - - return undef if !@{$self->objects}; - my $input_file = first { defined $_ } map $_->model_object->input_file, @{$self->objects}; - return undef if !defined $input_file; - - my $filename = my $filename_base = basename($input_file); - $filename_base =~ s/\.[^.]+$//; # without suffix - - # set filename in placeholder parser so that it's available also in custom G-code - $self->placeholder_parser->set(input_filename => $filename); - $self->placeholder_parser->set(input_filename_base => $filename_base); - - # set other variables from model object - $self->placeholder_parser->set_multiple( - scale => [ map $_->model_object->instances->[0]->scaling_factor * 100 . "%", @{$self->objects} ], - ); - - if ($path && -d $path) { - # if output path is an existing directory, we take that and append - # the specified filename format - $path = File::Spec->join($path, $self->config->output_filename_format); - } elsif (!$path) { - # if no explicit output file was defined, we take the input - # file directory and append the specified filename format - $path = (fileparse($input_file))[1] . $self->config->output_filename_format; - } else { - # path is a full path to a file so we use it as it is - } - - # make sure we use an up-to-date timestamp - $self->placeholder_parser->update_timestamp; - return $self->placeholder_parser->process($path); -} - # Wrapper around the C++ Slic3r::Print::validate() # to produce a Perl exception without a hang-up on some Strawberry perls. sub validate diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm index 3771bfe4a..4fe3eb820 100644 --- a/lib/Slic3r/Print/Simple.pm +++ b/lib/Slic3r/Print/Simple.pm @@ -13,7 +13,7 @@ use Slic3r::Geometry qw(X Y); has '_print' => ( is => 'ro', default => sub { Slic3r::Print->new }, - handles => [qw(apply_config extruders expanded_output_filepath + handles => [qw(apply_config extruders output_filepath total_used_filament total_extruded_volume placeholder_parser process)], ); diff --git a/t/custom_gcode.t b/t/custom_gcode.t index d99b6785f..653bb26ae 100644 --- a/t/custom_gcode.t +++ b/t/custom_gcode.t @@ -60,7 +60,7 @@ use Slic3r::Test; $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->expanded_output_filepath; + 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'; diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 6fc7e4569..8cff8dc41 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -6,6 +6,8 @@ #include "Geometry.hpp" #include "SupportMaterial.hpp" #include +#include +#include namespace Slic3r { @@ -973,4 +975,57 @@ Print::auto_assign_extruders(ModelObject* model_object) const } } +std::string +Print::output_filename() const +{ + PlaceholderParser pp = this->placeholder_parser; + + // get input file name + std::string input_file; + FOREACH_OBJECT(this, object) { + input_file = (*object)->model_object()->input_file; + if (!input_file.empty()) break; + } + + // get basename with and without suffix and set placeholders + const std::string input_basename = boost::filesystem::path(input_file).filename().string(); + pp.set("input_filename", input_basename); + const std::string input_basename_base = input_basename.substr(0, input_basename.find_last_of(".")); + pp.set("input_filename_base", input_basename_base); + + // set other variables from model object + { + std::vector v_scale; + FOREACH_OBJECT(this, object) + v_scale.push_back( boost::lexical_cast((*object)->model_object()->instances[0]->scaling_factor*100) + "%" ); + pp.set("scale", v_scale); + } + + pp.update_timestamp(); + return pp.process(this->config.output_filename_format.value); +} + +std::string +Print::output_filepath(const std::string &path) const +{ + // if we were supplied no path, generate an automatic one based on our first object's input file + if (path.empty()) { + // get the first input file name + std::string input_file; + FOREACH_OBJECT(this, object) { + input_file = (*object)->model_object()->input_file; + if (!input_file.empty()) break; + } + return (boost::filesystem::path(input_file).parent_path() / this->output_filename()).string(); + } + + // if we were supplied a directory, use it and append our automatically generated filename + boost::filesystem::path p(path); + if (boost::filesystem::is_directory(p)) + return (p / this->output_filename()).string(); + + // if we were supplied a file which is not a directory, use it + return path; +} + } diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 6cd24ef79..a14772379 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -212,6 +212,8 @@ class Print double max_allowed_layer_height() const; bool has_support_material() const; void auto_assign_extruders(ModelObject* model_object) const; + std::string output_filename() const; + std::string output_filepath(const std::string &path) const; private: void clear_regions(); diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 3992a0aff..9da32d674 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -215,6 +215,8 @@ _constant() double max_allowed_layer_height() const; bool has_support_material() const; void auto_assign_extruders(ModelObject* model_object); + std::string output_filename(); + std::string output_filepath(std::string path = ""); void add_model_object(ModelObject* model_object, int idx = -1); bool apply_config(DynamicPrintConfig* config) From 6f9e1cde6ea347eddac30f0023852f4b2699129c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 20 Dec 2016 19:09:09 +0100 Subject: [PATCH 191/225] Update Clipper to 6.4.1 --- xs/src/clipper.cpp | 10 +++++----- xs/src/clipper.hpp | 30 ++++++++++++++++-------------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/xs/src/clipper.cpp b/xs/src/clipper.cpp index 09657560f..a5b8829e1 100755 --- a/xs/src/clipper.cpp +++ b/xs/src/clipper.cpp @@ -1,10 +1,10 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.4.0 * -* Date : 2 July 2015 * +* Version : 6.4.1 * +* Date : 5 December 2016 * * Website : http://www.angusj.com * -* Copyright : Angus Johnson 2010-2015 * +* Copyright : Angus Johnson 2010-2016 * * * * License: * * Use, modification & distribution is subject to Boost Software License Ver 1. * @@ -179,7 +179,7 @@ int PolyTree::Total() const // PolyNode methods ... //------------------------------------------------------------------------------ -PolyNode::PolyNode(): Childs(), Parent(0), Index(0), m_IsOpen(false) +PolyNode::PolyNode(): Parent(0), Index(0), m_IsOpen(false) { } //------------------------------------------------------------------------------ @@ -3663,7 +3663,7 @@ void Clipper::FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec) { OutRec* outRec = m_PolyOuts[i]; OutRec* firstLeft = ParseFirstLeft(outRec->FirstLeft); - if (outRec->Pts && outRec->FirstLeft == OldOutRec) + if (outRec->Pts && firstLeft == OldOutRec) outRec->FirstLeft = NewOutRec; } } diff --git a/xs/src/clipper.hpp b/xs/src/clipper.hpp index 775807602..e60873bcc 100755 --- a/xs/src/clipper.hpp +++ b/xs/src/clipper.hpp @@ -1,8 +1,8 @@ /******************************************************************************* * * * Author : Angus Johnson * -* Version : 6.4.0 * -* Date : 2 July 2015 * +* Version : 6.4.1 * +* Date : 5 December 2016 * * Website : http://www.angusj.com * * Copyright : Angus Johnson 2010-2015 * * * @@ -34,7 +34,7 @@ #ifndef clipper_hpp #define clipper_hpp -#define CLIPPER_VERSION "6.2.6" +#define CLIPPER_VERSION "6.4.1" //use_int32: When enabled 32bit ints are used instead of 64bit ints. This //improve performance but coordinate values are limited to the range +/- 46340 @@ -137,7 +137,7 @@ class PolyNode { public: PolyNode(); - ~PolyNode(){}; + virtual ~PolyNode(){}; Path Contour; PolyNodes Childs; PolyNode* Parent; @@ -146,6 +146,7 @@ public: bool IsOpen() const; int ChildCount() const; private: + //PolyNode& operator =(PolyNode& other); unsigned Index; //node index in Parent.Childs bool m_IsOpen; JoinType m_jointype; @@ -159,12 +160,13 @@ private: class PolyTree: public PolyNode { public: - ~PolyTree(){Clear();}; + ~PolyTree(){ Clear(); }; PolyNode* GetFirst() const; void Clear(); int Total() const; private: - PolyNodes AllNodes; + //PolyTree& operator =(PolyTree& other); + PolyNodes AllNodes; friend class Clipper; //to access AllNodes }; @@ -219,17 +221,17 @@ class ClipperBase { public: ClipperBase(); - ~ClipperBase(); - bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); + virtual ~ClipperBase(); + virtual bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed); bool AddPaths(const Paths &ppg, PolyType PolyTyp, bool Closed); - void Clear(); + virtual void Clear(); IntRect GetBounds(); bool PreserveCollinear() {return m_PreserveCollinear;}; void PreserveCollinear(bool value) {m_PreserveCollinear = value;}; protected: void DisposeLocalMinimaList(); TEdge* AddBoundsToLML(TEdge *e, bool IsClosed); - void Reset(); + virtual void Reset(); TEdge* ProcessBound(TEdge* E, bool IsClockwise); void InsertScanbeam(const cInt Y); bool PopScanbeam(cInt &Y); @@ -258,7 +260,7 @@ protected: }; //------------------------------------------------------------------------------ -class Clipper : public ClipperBase +class Clipper : public virtual ClipperBase { public: Clipper(int initOptions = 0); @@ -285,7 +287,7 @@ public: void ZFillFunction(ZFillCallback zFillFunc); #endif protected: - bool ExecuteInternal(); + virtual bool ExecuteInternal(); private: JoinList m_Joins; JoinList m_GhostJoins; @@ -390,8 +392,8 @@ class clipperException : public std::exception { public: clipperException(const char* description): m_descr(description) {} - ~clipperException() throw() {} - const char* what() const throw() {return m_descr.c_str();} + virtual ~clipperException() throw() {} + virtual const char* what() const throw() {return m_descr.c_str();} private: std::string m_descr; }; From ec9c1fe6542753949859f9bc45706d20f425d573 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 20 Dec 2016 19:26:23 +0100 Subject: [PATCH 192/225] Restore [input_filename] in custom G-code as well --- xs/src/libslic3r/Print.cpp | 54 +++++++++++++++++++------------------- xs/src/libslic3r/Print.hpp | 4 +-- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 8cff8dc41..5dee986bc 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -436,6 +436,29 @@ Print::add_model_object(ModelObject* model_object, int idx) // apply config to print object o->config.apply(this->default_object_config); o->config.apply(object_config, true); + + // update placeholders + { + // get the first input file name + std::string input_file; + std::vector v_scale; + FOREACH_OBJECT(this, object) { + const ModelObject &mobj = *(*object)->model_object(); + v_scale.push_back( boost::lexical_cast(mobj.instances[0]->scaling_factor*100) + "%" ); + if (input_file.empty()) + input_file = mobj.input_file; + } + + PlaceholderParser &pp = this->placeholder_parser; + pp.set("scale", v_scale); + if (!input_file.empty()) { + // get basename with and without suffix + const std::string input_basename = boost::filesystem::path(input_file).filename().string(); + pp.set("input_filename", input_basename); + const std::string input_basename_base = input_basename.substr(0, input_basename.find_last_of(".")); + pp.set("input_filename_base", input_basename_base); + } + } } bool @@ -976,37 +999,14 @@ Print::auto_assign_extruders(ModelObject* model_object) const } std::string -Print::output_filename() const +Print::output_filename() { - PlaceholderParser pp = this->placeholder_parser; - - // get input file name - std::string input_file; - FOREACH_OBJECT(this, object) { - input_file = (*object)->model_object()->input_file; - if (!input_file.empty()) break; - } - - // get basename with and without suffix and set placeholders - const std::string input_basename = boost::filesystem::path(input_file).filename().string(); - pp.set("input_filename", input_basename); - const std::string input_basename_base = input_basename.substr(0, input_basename.find_last_of(".")); - pp.set("input_filename_base", input_basename_base); - - // set other variables from model object - { - std::vector v_scale; - FOREACH_OBJECT(this, object) - v_scale.push_back( boost::lexical_cast((*object)->model_object()->instances[0]->scaling_factor*100) + "%" ); - pp.set("scale", v_scale); - } - - pp.update_timestamp(); - return pp.process(this->config.output_filename_format.value); + this->placeholder_parser.update_timestamp(); + return this->placeholder_parser.process(this->config.output_filename_format.value); } std::string -Print::output_filepath(const std::string &path) const +Print::output_filepath(const std::string &path) { // if we were supplied no path, generate an automatic one based on our first object's input file if (path.empty()) { diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index a14772379..4b586a2f4 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -212,8 +212,8 @@ class Print double max_allowed_layer_height() const; bool has_support_material() const; void auto_assign_extruders(ModelObject* model_object) const; - std::string output_filename() const; - std::string output_filepath(const std::string &path) const; + std::string output_filename(); + std::string output_filepath(const std::string &path); private: void clear_regions(); From 32fdd4d7eef4470303b3075fc6ef256a43fefeb3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 20 Dec 2016 20:51:07 +0100 Subject: [PATCH 193/225] Ported _slice_region() to C++/XS --- lib/Slic3r/Print/Object.pm | 33 ----------------------- xs/src/libslic3r/Print.hpp | 3 ++- xs/src/libslic3r/PrintObject.cpp | 45 +++++++++++++++++++++++++++----- xs/xsp/Print.xsp | 19 ++++++++++++++ 4 files changed, 60 insertions(+), 40 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 78ff619cb..168f78bb3 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -335,39 +335,6 @@ sub slice { $self->set_step_done(STEP_SLICE); } -# called from slice() -sub _slice_region { - my ($self, $region_id, $z, $modifier) = @_; - - return [] if !@{$self->get_region_volumes($region_id)}; - - # compose mesh - my $mesh; - foreach my $volume_id (@{ $self->get_region_volumes($region_id) }) { - my $volume = $self->model_object->volumes->[$volume_id]; - next if $volume->modifier && !$modifier; - next if !$volume->modifier && $modifier; - - if (defined $mesh) { - $mesh->merge($volume->mesh); - } else { - $mesh = $volume->mesh->clone; - } - } - return if !defined $mesh; - - # transform mesh - # we ignore the per-instance transformations currently and only - # consider the first one - $self->model_object->instances->[0]->transform_mesh($mesh, 1); - - # align mesh to Z = 0 (it should be already aligned actually) and apply XY shift - $mesh->translate((map unscale(-$_), @{$self->_copies_shift}), -$self->model_object->bounding_box->z_min); - - # perform actual slicing - return $mesh->slice($z); -} - sub make_perimeters { my ($self) = @_; diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 4b586a2f4..65f61f3fb 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -100,7 +100,7 @@ class PrintObject PrintState state; Print* print(); - ModelObject* model_object(); + ModelObject* model_object() { return this->_model_object; }; Points copies() const; bool add_copy(const Pointf &point); @@ -136,6 +136,7 @@ class PrintObject void detect_surfaces_type(); void process_external_surfaces(); void bridge_over_infill(); + std::vector _slice_region(size_t region_id, std::vector z, bool modifier); void _make_perimeters(); void _infill(); diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 90752461e..97263036d 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -40,12 +40,6 @@ PrintObject::print() return this->_print; } -ModelObject* -PrintObject::model_object() -{ - return this->_model_object; -} - Points PrintObject::copies() const { @@ -665,6 +659,45 @@ PrintObject::bridge_over_infill() } } +// called from slice() +std::vector +PrintObject::_slice_region(size_t region_id, std::vector z, bool modifier) +{ + std::vector layers; + std::vector ®ion_volumes = this->region_volumes[region_id]; + if (region_volumes.empty()) return layers; + + ModelObject &object = *this->model_object(); + + // compose mesh + TriangleMesh mesh; + for (std::vector::const_iterator it = region_volumes.begin(); + it != region_volumes.end(); ++it) { + + const ModelVolume &volume = *object.volumes[*it]; + if (volume.modifier != modifier) continue; + + mesh.merge(volume.mesh); + } + if (mesh.facets_count() == 0) return layers; + + // transform mesh + // we ignore the per-instance transformations currently and only + // consider the first one + object.instances[0]->transform_mesh(&mesh, true); + + // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift + mesh.translate( + unscale(this->_copies_shift.x), + unscale(this->_copies_shift.y), + -object.bounding_box().min.z + ); + + // perform actual slicing + TriangleMeshSlicer(&mesh).slice(z, &layers); + return layers; +} + void PrintObject::_make_perimeters() { diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 9da32d674..0cd8e01f4 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -110,6 +110,25 @@ _constant() void detect_surfaces_type(); void process_external_surfaces(); void bridge_over_infill(); + SV* _slice_region(size_t region_id, std::vector z, bool modifier) + %code%{ + std::vector z_f(z.begin(), z.end()); + std::vector layers = THIS->_slice_region(region_id, z_f, modifier); + AV* layers_av = newAV(); + size_t len = layers.size(); + if (len > 0) av_extend(layers_av, len-1); + for (unsigned int i = 0; i < layers.size(); i++) { + AV* expolygons_av = newAV(); + len = layers[i].size(); + if (len > 0) av_extend(expolygons_av, len-1); + unsigned int j = 0; + for (ExPolygons::iterator it = layers[i].begin(); it != layers[i].end(); ++it) { + av_store(expolygons_av, j++, perl_to_SV_clone_ref(*it)); + } + av_store(layers_av, i, newRV_noinc((SV*)expolygons_av)); + } + RETVAL = (SV*)newRV_noinc((SV*)layers_av); + %}; void _make_perimeters(); void _infill(); From b540f9788bae77498577914acb2e10f519ff52c4 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 20 Dec 2016 21:34:13 +0100 Subject: [PATCH 194/225] codecov.io integration --- .travis.yml | 8 +++++++- README.md | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8e9616ab5..6cdc15ce5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: perl install: export LDLOADLIBS=-lstdc++ -script: perl ./Build.PL perl: - "5.14" - "5.18" @@ -21,3 +20,10 @@ addons: - libboost-system1.55-dev - libboost-filesystem1.55-dev - liblocal-lib-perl +before_script: + - cpanm --quiet --notest --skip-satisfied git://github.com/codecov/codecov-perl@master +script: + - perl ./Build.PL + - cover -test +after_success: + - cover -report codecov diff --git a/README.md b/README.md index 24d5829c2..dd6be9a9c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,10 @@ _Q: Oh cool, a new RepRap slicer?_ A: Yes. -Slic3r [![Build Status](https://travis-ci.org/alexrj/Slic3r.png?branch=master)](https://travis-ci.org/alexrj/Slic3r) [![Build status](https://ci.appveyor.com/api/projects/status/8iqmeat6cj158vo6?svg=true)](https://ci.appveyor.com/project/lordofhyphens/slic3r) +Slic3r +[![Build Status](https://travis-ci.org/alexrj/Slic3r.png?branch=master)](https://travis-ci.org/alexrj/Slic3r) +[![Build status](https://ci.appveyor.com/api/projects/status/8iqmeat6cj158vo6?svg=true)](https://ci.appveyor.com/project/lordofhyphens/slic3r) +[![codecov.io](https://codecov.io/github/alexrj/Slic3r/coverage.svg?branch=master)] ====== Prebuilt Win32 builds https://bintray.com/lordofhyphens/Slic3r/slic3r_dev/view From ab05177a089d796f7a45f5c29423468192813058 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 20 Dec 2016 21:36:43 +0100 Subject: [PATCH 195/225] Try to fix codecov.io --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6cdc15ce5..7c33130d3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ addons: - libboost-filesystem1.55-dev - liblocal-lib-perl before_script: - - cpanm --quiet --notest --skip-satisfied git://github.com/codecov/codecov-perl@master + - cpanm --quiet --notest --skip-satisfied Devel::Cover Devel::Cover::Report::Codecov script: - perl ./Build.PL - cover -test From 6d3c0e3e98edabd58a2cc4c1d2fc9ff676ea468d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 20 Dec 2016 21:46:47 +0100 Subject: [PATCH 196/225] Revert "codecov.io integration" (It looks like codecov.io doesn't work with our mixed Perl/C++ environment) This reverts commit b540f9788bae77498577914acb2e10f519ff52c4. Conflicts: .travis.yml --- .travis.yml | 8 +------- README.md | 5 +---- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7c33130d3..8e9616ab5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: perl install: export LDLOADLIBS=-lstdc++ +script: perl ./Build.PL perl: - "5.14" - "5.18" @@ -20,10 +21,3 @@ addons: - libboost-system1.55-dev - libboost-filesystem1.55-dev - liblocal-lib-perl -before_script: - - cpanm --quiet --notest --skip-satisfied Devel::Cover Devel::Cover::Report::Codecov -script: - - perl ./Build.PL - - cover -test -after_success: - - cover -report codecov diff --git a/README.md b/README.md index dd6be9a9c..24d5829c2 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,7 @@ _Q: Oh cool, a new RepRap slicer?_ A: Yes. -Slic3r -[![Build Status](https://travis-ci.org/alexrj/Slic3r.png?branch=master)](https://travis-ci.org/alexrj/Slic3r) -[![Build status](https://ci.appveyor.com/api/projects/status/8iqmeat6cj158vo6?svg=true)](https://ci.appveyor.com/project/lordofhyphens/slic3r) -[![codecov.io](https://codecov.io/github/alexrj/Slic3r/coverage.svg?branch=master)] +Slic3r [![Build Status](https://travis-ci.org/alexrj/Slic3r.png?branch=master)](https://travis-ci.org/alexrj/Slic3r) [![Build status](https://ci.appveyor.com/api/projects/status/8iqmeat6cj158vo6?svg=true)](https://ci.appveyor.com/project/lordofhyphens/slic3r) ====== Prebuilt Win32 builds https://bintray.com/lordofhyphens/Slic3r/slic3r_dev/view From bd1f03a1216e94830f79bca3cfeed7690e5f2283 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 21 Dec 2016 11:56:39 +0100 Subject: [PATCH 197/225] Error in 32fdd4d7eef4470303b3075fc6ef256a43fefeb3. #3632 --- xs/src/libslic3r/PrintObject.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 97263036d..c50987002 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -688,8 +688,8 @@ PrintObject::_slice_region(size_t region_id, std::vector z, bool modifier // align mesh to Z = 0 (it should be already aligned actually) and apply XY shift mesh.translate( - unscale(this->_copies_shift.x), - unscale(this->_copies_shift.y), + -unscale(this->_copies_shift.x), + -unscale(this->_copies_shift.y), -object.bounding_box().min.z ); From 4a25b3e8befc119e7dad880ec4473a6ea460d28b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 21 Dec 2016 15:39:07 +0100 Subject: [PATCH 198/225] Fix infill_overlap and endpoints_overlap not being linked and not considering both perimeter and infill extrusion width (thus causing potential overflows). #3289 --- xs/src/libslic3r/Fill/FillRectilinear.cpp | 9 +++------ xs/src/libslic3r/LayerRegionFill.cpp | 3 +++ xs/src/libslic3r/PerimeterGenerator.cpp | 4 ---- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/xs/src/libslic3r/Fill/FillRectilinear.cpp index 5fcb779cf..286b1a550 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.cpp @@ -283,9 +283,6 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, svg.Close(); #endif - // Calculate the extension of the vertical endpoints according to the configured value. - const coord_t extra_y = floor((double)min_spacing * this->endpoints_overlap + 0.5f); - // Store the number of polygons already existing in the output container. const size_t n_polylines_out_old = out->size(); @@ -313,7 +310,7 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, // Start our polyline. Polyline polyline; polyline.append(p); - polyline.points.back().y -= extra_y; + polyline.points.back().y -= this->endpoints_overlap; while (true) { // Complete the vertical line by finding the corresponding upper or lower point. @@ -331,7 +328,7 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, IntersectionPoint b = it->second; assert(b.type != p.type); polyline.append(b); - polyline.points.back().y += extra_y * (b.type == IntersectionPoint::ipTypeUpper ? 1 : -1); + polyline.points.back().y += this->endpoints_overlap * (b.type == IntersectionPoint::ipTypeUpper ? 1 : -1); // Remove the two endpoints of this vertical line from the grid. { @@ -359,7 +356,7 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, const size_t n = polyline.points.size(); polyline.append(b.next); for (Points::iterator pit = polyline.points.begin()+n; pit != polyline.points.end(); ++pit) - pit->y += extra_y * (b.type == IntersectionPoint::ipTypeUpper ? 1 : -1); + pit->y += this->endpoints_overlap * (b.type == IntersectionPoint::ipTypeUpper ? 1 : -1); } // Is the final point still available? diff --git a/xs/src/libslic3r/LayerRegionFill.cpp b/xs/src/libslic3r/LayerRegionFill.cpp index c5b806542..142e4758f 100644 --- a/xs/src/libslic3r/LayerRegionFill.cpp +++ b/xs/src/libslic3r/LayerRegionFill.cpp @@ -34,6 +34,7 @@ LayerRegion::make_fill() const Flow infill_flow = this->flow(frInfill); const Flow solid_infill_flow = this->flow(frSolidInfill); const Flow top_solid_infill_flow = this->flow(frTopSolidInfill); + const coord_t perimeter_spacing = this->flow(frPerimeter).scaled_spacing(); SurfaceCollection surfaces; @@ -215,6 +216,8 @@ LayerRegion::make_fill() } else { f->spacing = flow.spacing(); } + f->endpoints_overlap = this->region()->config.get_abs_value("infill_overlap", + (perimeter_spacing + scale_(f->spacing))/2); f->layer_id = this->layer()->id(); f->z = this->layer()->print_z; diff --git a/xs/src/libslic3r/PerimeterGenerator.cpp b/xs/src/libslic3r/PerimeterGenerator.cpp index d2ed60aaf..83056e868 100644 --- a/xs/src/libslic3r/PerimeterGenerator.cpp +++ b/xs/src/libslic3r/PerimeterGenerator.cpp @@ -298,10 +298,6 @@ PerimeterGenerator::process() inset += pspacing/2; } - // only apply infill overlap if we actually have one perimeter - if (inset > 0) - inset -= this->config->get_abs_value("infill_overlap", inset + ispacing/2); - { ExPolygons expp = union_ex(last); From b33d04fd2dbbebf0ba4bbc5d5c28e8796f2890cb Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 21 Dec 2016 17:12:37 +0100 Subject: [PATCH 199/225] Bugfix: adjusted solid infill could become very large in thin areas. #3215 --- lib/Slic3r/Print/SupportMaterial.pm | 6 +++--- t/fill.t | 10 +++++----- xs/src/libslic3r/Fill/Fill.cpp | 7 ++++++- xs/src/libslic3r/Fill/Fill.hpp | 12 +++++++++--- xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp | 2 +- xs/src/libslic3r/Fill/FillConcentric.cpp | 4 ++-- xs/src/libslic3r/Fill/FillHoneycomb.cpp | 4 ++-- xs/src/libslic3r/Fill/FillPlanePath.cpp | 2 +- xs/src/libslic3r/Fill/FillRectilinear.cpp | 8 ++++---- xs/src/libslic3r/LayerRegionFill.cpp | 13 +++++++------ xs/xsp/Filler.xsp | 9 ++++++--- 11 files changed, 46 insertions(+), 31 deletions(-) diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 16c3b944c..34829a2a2 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -681,7 +681,7 @@ sub generate_toolpaths { # interface and contact infill if (@$interface || @$contact_infill) { $fillers{interface}->set_angle($interface_angle); - $fillers{interface}->set_spacing($_interface_flow->spacing); + $fillers{interface}->set_min_spacing($_interface_flow->spacing); # find centerline of the external loop $interface = offset2($interface, +scaled_epsilon, -(scaled_epsilon + $_interface_flow->scaled_width/2)); @@ -734,7 +734,7 @@ sub generate_toolpaths { # We don't use $base_flow->spacing because we need a constant spacing # value that guarantees that all layers are correctly aligned. - $filler->set_spacing($flow->spacing); + $filler->set_min_spacing($flow->spacing); my $density = $support_density; my $base_flow = $_flow; @@ -753,7 +753,7 @@ sub generate_toolpaths { # use the proper spacing for first layer as we don't need to align # its pattern to the other layers - $filler->set_spacing($base_flow->spacing); + $filler->set_min_spacing($base_flow->spacing); } else { # draw a perimeter all around support infill # TODO: use brim ordering algorithm diff --git a/t/fill.t b/t/fill.t index 7cea2726e..a9e505c16 100644 --- a/t/fill.t +++ b/t/fill.t @@ -30,7 +30,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } { my $filler = Slic3r::Filler->new_from_type('rectilinear'); $filler->set_angle(-(PI)/2); - $filler->set_spacing(5); + $filler->set_min_spacing(5); $filler->set_dont_adjust(1); $filler->set_endpoints_overlap(0); @@ -44,7 +44,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } }; # square - $filler->set_density($filler->spacing / 50); + $filler->set_density($filler->min_spacing / 50); for my $i (0..3) { # check that it works regardless of the points order my @points = ([0,0], [100,0], [100,100], [0,100]); @@ -64,7 +64,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } # square with hole for my $angle (-(PI/2), -(PI/4), -(PI), PI/2, PI) { for my $spacing (25, 5, 7.5, 8.5) { - $filler->set_density($filler->spacing / $spacing); + $filler->set_density($filler->min_spacing / $spacing); $filler->set_angle($angle); my $paths = $test->(my $e = Slic3r::ExPolygon->new( [ scale_points [0,0], [100,0], [100,100], [0,100] ], @@ -102,7 +102,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } height => 0.4, nozzle_diameter => 0.50, ); - $filler->set_spacing($flow->spacing); + $filler->set_min_spacing($flow->spacing); $filler->set_density(1); foreach my $angle (0, 45) { $surface->expolygon->rotate(Slic3r::Geometry::deg2rad($angle), [0,0]); @@ -128,7 +128,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } height => 0.4, nozzle_diameter => $flow_spacing, ); - $filler->set_spacing($flow->spacing); + $filler->set_min_spacing($flow->spacing); my $paths = $filler->fill_surface( $surface, layer_height => $flow->height, diff --git a/xs/src/libslic3r/Fill/Fill.cpp b/xs/src/libslic3r/Fill/Fill.cpp index 82a392943..c88a32514 100644 --- a/xs/src/libslic3r/Fill/Fill.cpp +++ b/xs/src/libslic3r/Fill/Fill.cpp @@ -52,7 +52,10 @@ Fill::fill_surface(const Surface &surface) if (this->density == 0) return Polylines(); // Perform offset. - ExPolygons expp = offset_ex(surface.expolygon, -scale_(this->spacing)/2); + ExPolygons expp = offset_ex(surface.expolygon, -scale_(this->min_spacing)/2); + + // Implementations can change this if they adjust the flow. + this->_spacing = this->min_spacing; // Create the infills for each of the regions. Polylines polylines_out; @@ -85,6 +88,8 @@ Fill::adjust_solid_spacing(const coord_t width, const coord_t distance) assert(factor > 1. - 1e-5); // How much could the extrusion width be increased? By 20%. + // Because of this limit, this method is not idempotent: each run + // will increment distance by 20%. const coordf_t factor_max = 1.2; if (factor > factor_max) distance_new = floor((double)distance * factor_max + 0.5); diff --git a/xs/src/libslic3r/Fill/Fill.hpp b/xs/src/libslic3r/Fill/Fill.hpp index a733a870d..151d61436 100644 --- a/xs/src/libslic3r/Fill/Fill.hpp +++ b/xs/src/libslic3r/Fill/Fill.hpp @@ -27,7 +27,7 @@ public: coordf_t z; // in unscaled coordinates - coordf_t spacing; + coordf_t min_spacing; // overlap over spacing for extrusion endpoints float endpoints_overlap; @@ -78,11 +78,16 @@ public: // Perform the fill. virtual Polylines fill_surface(const Surface &surface); + coordf_t spacing() const { return this->_spacing; }; + protected: + // the actual one in unscaled coordinates, we fill this while generating paths + coordf_t _spacing; + Fill() : layer_id(size_t(-1)), z(0.f), - spacing(0.f), + min_spacing(0.f), endpoints_overlap(0.3f), angle(0), link_max_length(0), @@ -90,7 +95,8 @@ protected: density(0), dont_connect(false), dont_adjust(false), - complete(false) + complete(false), + _spacing(0.f) {}; typedef std::pair direction_t; diff --git a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp index 3f523b27c..4464cc877 100644 --- a/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp +++ b/xs/src/libslic3r/Fill/Fill3DHoneycomb.cpp @@ -156,7 +156,7 @@ Fill3DHoneycomb::_fill_surface_single( { // no rotation is supported for this infill pattern BoundingBox bb = expolygon.contour.bounding_box(); - const coord_t distance = coord_t(scale_(this->spacing) / this->density); + const coord_t distance = coord_t(scale_(this->min_spacing) / this->density); // align bounding box to a multiple of our honeycomb grid module // (a module is 2*$distance since one $distance half-module is diff --git a/xs/src/libslic3r/Fill/FillConcentric.cpp b/xs/src/libslic3r/Fill/FillConcentric.cpp index cb09993b9..24d8f318a 100644 --- a/xs/src/libslic3r/Fill/FillConcentric.cpp +++ b/xs/src/libslic3r/Fill/FillConcentric.cpp @@ -15,13 +15,13 @@ FillConcentric::_fill_surface_single( { // no rotation is supported for this infill pattern - const coord_t min_spacing = scale_(this->spacing); + const coord_t min_spacing = scale_(this->min_spacing); coord_t distance = coord_t(min_spacing / this->density); if (this->density > 0.9999f && !this->dont_adjust) { BoundingBox bounding_box = expolygon.contour.bounding_box(); distance = this->adjust_solid_spacing(bounding_box.size().x, distance); - this->spacing = unscale(distance); + this->_spacing = unscale(distance); } Polygons loops = (Polygons)expolygon; diff --git a/xs/src/libslic3r/Fill/FillHoneycomb.cpp b/xs/src/libslic3r/Fill/FillHoneycomb.cpp index df4e62a3d..02198bcd7 100644 --- a/xs/src/libslic3r/Fill/FillHoneycomb.cpp +++ b/xs/src/libslic3r/Fill/FillHoneycomb.cpp @@ -14,12 +14,12 @@ FillHoneycomb::_fill_surface_single( Polylines* polylines_out) { // cache hexagons math - CacheID cache_id = std::make_pair(this->density, this->spacing); + CacheID cache_id = std::make_pair(this->density, this->min_spacing); Cache::iterator it_m = this->cache.find(cache_id); if (it_m == this->cache.end()) { it_m = this->cache.insert(it_m, std::pair(cache_id, CacheData())); CacheData &m = it_m->second; - coord_t min_spacing = scale_(this->spacing); + coord_t min_spacing = scale_(this->min_spacing); m.distance = min_spacing / this->density; m.hex_side = m.distance / (sqrt(3)/2); m.hex_width = m.distance * 2; // $m->{hex_width} == $m->{hex_side} * sqrt(3); diff --git a/xs/src/libslic3r/Fill/FillPlanePath.cpp b/xs/src/libslic3r/Fill/FillPlanePath.cpp index 09b0ea700..2b78f24e1 100644 --- a/xs/src/libslic3r/Fill/FillPlanePath.cpp +++ b/xs/src/libslic3r/Fill/FillPlanePath.cpp @@ -14,7 +14,7 @@ void FillPlanePath::_fill_surface_single( { expolygon.rotate(-direction.first); - const coord_t distance_between_lines = scale_(this->spacing) / this->density; + const coord_t distance_between_lines = scale_(this->min_spacing) / this->density; // align infill across layers using the object's bounding box (if available) BoundingBox bounding_box = this->bounding_box.defined diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/xs/src/libslic3r/Fill/FillRectilinear.cpp index 286b1a550..9af184295 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.cpp @@ -22,7 +22,7 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, expolygon.rotate(-direction.first); assert(this->density > 0.0001f && this->density <= 1.f); - const coord_t min_spacing = scale_(this->spacing); + const coord_t min_spacing = scale_(this->min_spacing); coord_t line_spacing = (double) min_spacing / this->density; // We ignore this->bounding_box because it doesn't matter; we're doing align_to_grid below. @@ -36,7 +36,7 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, // define flow spacing according to requested density if (this->density > 0.9999f && !this->dont_adjust) { line_spacing = this->adjust_solid_spacing(bounding_box.size().x, line_spacing); - this->spacing = unscale(line_spacing); + this->_spacing = unscale(line_spacing); } else { // extend bounding box so that our pattern will be aligned with other layers // Transform the reference point to the rotated coordinate system. @@ -451,7 +451,7 @@ void FillStars::_fill_surface_single( fill2._fill_single_direction(expolygon, direction2, 0, out); direction2.first += PI/3; - const coord_t x_shift = 0.5 * scale_(fill2.spacing) / fill2.density; + const coord_t x_shift = 0.5 * scale_(fill2.min_spacing) / fill2.density; fill2._fill_single_direction(expolygon, direction2, x_shift, out); } @@ -465,7 +465,7 @@ void FillCubic::_fill_surface_single( fill2.density /= 3.; direction_t direction2 = direction; - const coord_t range = scale_(this->spacing / this->density); + const coord_t range = scale_(this->min_spacing / this->density); const coord_t x_shift = abs(( (coord_t)(scale_(this->z) + range) % (coord_t)(range * 2)) - range); fill2._fill_single_direction(expolygon, direction2, -x_shift, out); diff --git a/xs/src/libslic3r/LayerRegionFill.cpp b/xs/src/libslic3r/LayerRegionFill.cpp index 142e4758f..6beb82e36 100644 --- a/xs/src/libslic3r/LayerRegionFill.cpp +++ b/xs/src/libslic3r/LayerRegionFill.cpp @@ -211,13 +211,14 @@ LayerRegion::make_fill() -1, // auto width *this->layer()->object() ); - f->spacing = internal_flow.spacing(); + f->min_spacing = internal_flow.spacing(); using_internal_flow = true; } else { - f->spacing = flow.spacing(); + f->min_spacing = flow.spacing(); } + f->endpoints_overlap = this->region()->config.get_abs_value("infill_overlap", - (perimeter_spacing + scale_(f->spacing))/2); + (perimeter_spacing + scale_(f->min_spacing))/2); f->layer_id = this->layer()->id(); f->z = this->layer()->print_z; @@ -225,7 +226,7 @@ LayerRegion::make_fill() // Maximum length of the perimeter segment linking two infill lines. f->link_max_length = (!is_bridge && density > 80) - ? scale_(3 * f->spacing) + ? scale_(3 * f->min_spacing) : 0; // Used by the concentric infill pattern to clip the loops to create extrusion paths. @@ -243,9 +244,9 @@ LayerRegion::make_fill() if (using_internal_flow) { // if we used the internal flow we're not doing a solid infill // so we can safely ignore the slight variation that might have - // been applied to f->spacing + // been applied to f->spacing() } else { - flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow()); + flow = Flow::new_from_spacing(f->spacing(), flow.nozzle_diameter, h, is_bridge || f->use_bridge_flow()); } // Save into layer. diff --git a/xs/xsp/Filler.xsp b/xs/xsp/Filler.xsp index 7a3e995d3..0c5c08b6c 100644 --- a/xs/xsp/Filler.xsp +++ b/xs/xsp/Filler.xsp @@ -14,10 +14,13 @@ void set_bounding_box(BoundingBox *bbox) %code{% THIS->fill->bounding_box = *bbox; %}; - void set_spacing(coordf_t spacing) - %code{% THIS->fill->spacing = spacing; %}; + void set_min_spacing(coordf_t spacing) + %code{% THIS->fill->min_spacing = spacing; %}; + coordf_t min_spacing() + %code{% RETVAL = THIS->fill->min_spacing; %}; + coordf_t spacing() - %code{% RETVAL = THIS->fill->spacing; %}; + %code{% RETVAL = THIS->fill->spacing(); %}; void set_endpoints_overlap(float overlap) %code{% THIS->fill->endpoints_overlap = overlap; %}; From 27b2f0d6c3c3e8b33ceaaaf30088e97c4908f5ec Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 21 Dec 2016 18:10:48 +0100 Subject: [PATCH 200/225] Prevent useless reloading of 3D preview --- lib/Slic3r/GUI/Plater.pm | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 427b89f61..4474550f5 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -54,6 +54,7 @@ sub new { )); $self->{model} = Slic3r::Model->new; $self->{print} = Slic3r::Print->new; + $self->{processed} = 0; # List of Perl objects Slic3r::GUI::Plater::Object, representing a 2D preview of the platter. $self->{objects} = []; @@ -136,15 +137,16 @@ sub new { wxTheApp->CallAfter(sub { my $sel = $self->{preview_notebook}->GetSelection; if ($sel == $self->{preview3D_page_idx} || $sel == $self->{toolpaths2D_page_idx}) { - $self->{preview3D}->load_print; - - if (!$Slic3r::GUI::Settings->{_}{background_processing}) { + if (!$Slic3r::GUI::Settings->{_}{background_processing} && !$self->{processed}) { $self->statusbar->SetCancelCallback(sub { $self->stop_background_process; $self->statusbar->SetStatusText("Slicing cancelled"); $self->{preview_notebook}->SetSelection(0); }); $self->start_background_process; + } else { + $self->{preview3D}->load_print + if $sel == $self->{preview3D_page_idx}; } } }); @@ -1050,6 +1052,8 @@ sub split_object { sub schedule_background_process { my ($self) = @_; + $self->{processed} = 0; + if (!$Slic3r::GUI::Settings->{_}{background_processing}) { my $sel = $self->{preview_notebook}->GetSelection; if ($sel == $self->{preview3D_page_idx} || $sel == $self->{toolpaths2D_page_idx}) { @@ -1143,6 +1147,7 @@ sub stop_background_process { $self->statusbar->SetCancelCallback(undef); $self->statusbar->StopBusy; $self->statusbar->SetStatusText(""); + $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; $self->{preview3D}->reload_print if $self->{preview3D}; @@ -1276,6 +1281,7 @@ sub on_process_completed { Slic3r::debugf "Background processing completed.\n"; $self->{process_thread}->detach if $self->{process_thread}; $self->{process_thread} = undef; + $self->{processed} = 1; # if we're supposed to perform an explicit export let's display the error in a dialog if ($error && $self->{export_gcode_output_file}) { From 357b6bbd55808d9245337000548cf084dfe4ae0b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 21 Dec 2016 19:48:39 +0100 Subject: [PATCH 201/225] Minor changes to MedialAxis code --- xs/src/libslic3r/Geometry.cpp | 7 +++++++ xs/src/libslic3r/Geometry.hpp | 8 +------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/xs/src/libslic3r/Geometry.cpp b/xs/src/libslic3r/Geometry.cpp index 2ca3088b2..0cc9a575c 100644 --- a/xs/src/libslic3r/Geometry.cpp +++ b/xs/src/libslic3r/Geometry.cpp @@ -474,14 +474,21 @@ MedialAxis::build(ThickPolylines* polylines) /* // DEBUG: dump all Voronoi edges { + SVG svg("voronoi.svg"); + svg.draw(*this->expolygon); for (VD::const_edge_iterator edge = this->vd.edges().begin(); edge != this->vd.edges().end(); ++edge) { if (edge->is_infinite()) continue; ThickPolyline polyline; polyline.points.push_back(Point( edge->vertex0()->x(), edge->vertex0()->y() )); polyline.points.push_back(Point( edge->vertex1()->x(), edge->vertex1()->y() )); + polyline.width.push_back(this->max_width); + polyline.width.push_back(this->max_width); polylines->push_back(polyline); + + svg.draw(polyline); } + svg.Close(); return; } */ diff --git a/xs/src/libslic3r/Geometry.hpp b/xs/src/libslic3r/Geometry.hpp index d25901529..55c760f7e 100644 --- a/xs/src/libslic3r/Geometry.hpp +++ b/xs/src/libslic3r/Geometry.hpp @@ -43,13 +43,7 @@ class MedialAxis { void build(Polylines* polylines); private: - class VD : public voronoi_diagram { - public: - typedef double coord_type; - typedef boost::polygon::point_data point_type; - typedef boost::polygon::segment_data segment_type; - typedef boost::polygon::rectangle_data rect_type; - }; + typedef voronoi_diagram VD; VD vd; std::set edges, valid_edges; std::map > thickness; From 0578a40a6d3f5e7ef1b56c5cf9304b5ca91093ab Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 21 Dec 2016 19:49:40 +0100 Subject: [PATCH 202/225] Update SLAPrint with the new Fill API --- xs/src/libslic3r/SLAPrint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index 57fe64832..7acddb9ff 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -60,7 +60,7 @@ SLAPrint::slice() std::auto_ptr fill(Fill::new_from_type(this->config.fill_pattern.value)); fill->bounding_box.merge(Point::new_scale(bb.min.x, bb.min.y)); fill->bounding_box.merge(Point::new_scale(bb.max.x, bb.max.y)); - fill->spacing = this->config.get_abs_value("infill_extrusion_width", this->config.layer_height.value); + fill->min_spacing = this->config.get_abs_value("infill_extrusion_width", this->config.layer_height.value); fill->angle = Geometry::deg2rad(this->config.fill_angle.value); fill->density = this->config.fill_density.value/100; From 949fda3abbf42140072234762e86616ffafc916a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 21 Dec 2016 19:55:46 +0100 Subject: [PATCH 203/225] One more fix to SLAPrint --- xs/src/libslic3r/SLAPrint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/SLAPrint.cpp b/xs/src/libslic3r/SLAPrint.cpp index 7acddb9ff..826e6184e 100644 --- a/xs/src/libslic3r/SLAPrint.cpp +++ b/xs/src/libslic3r/SLAPrint.cpp @@ -189,7 +189,7 @@ SLAPrint::_infill_layer(size_t i, const Fill* _fill) fill->z = layer.print_z; ExtrusionPath templ(erInternalInfill); - templ.width = fill->spacing; + templ.width = fill->spacing(); const ExPolygons internal_ex = intersection_ex(infill, internal); for (ExPolygons::const_iterator it = internal_ex.begin(); it != internal_ex.end(); ++it) { Polylines polylines = fill->fill_surface(Surface(stInternal, *it)); From 49a9492ad21021ee5defed48563e80a9b703c5a7 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 21 Dec 2016 20:32:57 +0100 Subject: [PATCH 204/225] Allow any layer height and limit it to the smallest nozzle diameter. #2706 --- lib/Slic3r/Config.pm | 2 +- lib/Slic3r/Print/Object.pm | 10 ++++++-- xs/src/libslic3r/Print.cpp | 41 ++++---------------------------- xs/src/libslic3r/Print.hpp | 2 ++ xs/src/libslic3r/PrintObject.cpp | 22 +++++++++++++++++ xs/xsp/Print.xsp | 16 +++++++++++++ 6 files changed, 53 insertions(+), 40 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 031edaf37..a399a8333 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -325,7 +325,7 @@ sub validate { my $max_nozzle_diameter = max(@{ $self->nozzle_diameter }); die "Invalid extrusion width (too large)\n" if defined first { $_ > 10 * $max_nozzle_diameter } - map $self->get_abs_value_over("${_}_extrusion_width", $self->layer_height), + map $self->get_abs_value_over("${_}_extrusion_width", $max_nozzle_diameter), qw(perimeter infill solid_infill top_infill support_material first_layer); } diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 168f78bb3..9b9e99e9f 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -49,6 +49,13 @@ sub slice { $self->set_step_started(STEP_SLICE); $self->print->status_cb->(10, "Processing triangulated mesh"); + { + my @nozzle_diameters = map $self->print->config->get_at('nozzle_diameter', $_), + @{$self->print->object_extruders}; + + $self->config->set('layer_height', min(@nozzle_diameters, $self->config->layer_height)); + } + # init layers { $self->clear_layers; @@ -73,8 +80,7 @@ sub slice { { my @nozzle_diameters = ( map $self->print->config->get_at('nozzle_diameter', $_), - $self->config->support_material_extruder-1, - $self->config->support_material_interface_extruder-1, + @{$self->support_material_extruders}, ); $support_material_layer_height = 0.75 * min(@nozzle_diameters); } diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 5dee986bc..9dec28e9f 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -667,42 +667,9 @@ Print::validate() const return "The Spiral Vase option can only be used when printing single material objects."; } - { - // find the smallest nozzle diameter - std::set extruders = this->extruders(); - if (extruders.empty()) - return "The supplied settings will cause an empty print."; - - std::set nozzle_diameters; - for (std::set::iterator it = extruders.begin(); it != extruders.end(); ++it) - nozzle_diameters.insert(this->config.nozzle_diameter.get_at(*it)); - double min_nozzle_diameter = *std::min_element(nozzle_diameters.begin(), nozzle_diameters.end()); - - FOREACH_OBJECT(this, i_object) { - PrintObject* object = *i_object; - - // validate first_layer_height - double first_layer_height = object->config.get_abs_value("first_layer_height"); - double first_layer_min_nozzle_diameter; - if (object->config.raft_layers > 0) { - // if we have raft layers, only support material extruder is used on first layer - size_t first_layer_extruder = object->config.raft_layers == 1 - ? object->config.support_material_interface_extruder-1 - : object->config.support_material_extruder-1; - first_layer_min_nozzle_diameter = this->config.nozzle_diameter.get_at(first_layer_extruder); - } else { - // if we don't have raft layers, any nozzle diameter is potentially used in first layer - first_layer_min_nozzle_diameter = min_nozzle_diameter; - } - if (first_layer_height > first_layer_min_nozzle_diameter) - return "First layer height can't be greater than nozzle diameter"; - - // validate layer_height - if (object->config.layer_height.value > min_nozzle_diameter) - return "Layer height can't be greater than nozzle diameter"; - } - } - + if (this->extruders().empty()) + return "The supplied settings will cause an empty print."; + return std::string(); } @@ -917,7 +884,7 @@ Print::_make_brim() } std::auto_ptr filler(Fill::new_from_type(ipRectilinear)); - filler->spacing = flow.spacing(); + filler->min_spacing = flow.spacing(); filler->dont_adjust = true; filler->density = 1; diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 65f61f3fb..5ec3653ed 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -109,6 +109,8 @@ class PrintObject bool set_copies(const Points &points); bool reload_model_instances(); BoundingBox bounding_box() const; + std::set extruders() const; + std::set support_material_extruders() const; // adds region_id, too, if necessary void add_region_volume(int region_id, int volume_id); diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index ff3f80969..8002ab0be 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -115,6 +115,28 @@ PrintObject::bounding_box() const return BoundingBox(pp); } +// returns 0-based indices of used extruders +std::set +PrintObject::extruders() const +{ + std::set extruders = this->_print->extruders(); + std::set sm_extruders = this->support_material_extruders(); + extruders.insert(sm_extruders.begin(), sm_extruders.end()); + return extruders; +} + +// returns 0-based indices of used extruders +std::set +PrintObject::support_material_extruders() const +{ + std::set extruders; + if (this->has_support_material()) { + extruders.insert(this->config.support_material_extruder - 1); + extruders.insert(this->config.support_material_interface_extruder - 1); + } + return extruders; +} + void PrintObject::add_region_volume(int region_id, int volume_id) { diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 0cd8e01f4..fbd4c3765 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -63,6 +63,22 @@ _constant() Clone bounding_box(); Ref _copies_shift() %code%{ RETVAL = &THIS->_copies_shift; %}; + std::vector support_material_extruders() + %code%{ + std::set extruders = THIS->support_material_extruders(); + RETVAL.reserve(extruders.size()); + for (std::set::const_iterator e = extruders.begin(); e != extruders.end(); ++e) { + RETVAL.push_back(*e); + } + %}; + std::vector extruders() + %code%{ + std::set extruders = THIS->extruders(); + RETVAL.reserve(extruders.size()); + for (std::set::const_iterator e = extruders.begin(); e != extruders.end(); ++e) { + RETVAL.push_back(*e); + } + %}; bool typed_slices() %code%{ RETVAL = THIS->typed_slices; %}; From 475f9d45a555741e5556acc2d558b4f5e932e689 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 21 Dec 2016 20:42:17 +0100 Subject: [PATCH 205/225] Some minor fixes. #3603 --- lib/Slic3r/GUI/3DScene.pm | 5 ++++- xs/src/libslic3r/Fill/FillRectilinear.cpp | 2 +- xs/src/libslic3r/libslic3r.h | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index fecfc014f..f63a82146 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -304,7 +304,7 @@ sub mouse_event { $self->_dragged(undef); } elsif ($e->Moving) { $self->_mouse_pos($pos); - $self->Refresh; + $self->Refresh if $self->enable_picking; } else { $e->Skip(); } @@ -976,6 +976,9 @@ sub Render { glFlush(); $self->SwapBuffers(); + + # Calling glFinish has a performance penalty, but it seems to fix some OpenGL driver hang-up with extremely large scenes. + glFinish(); } sub draw_axes { diff --git a/xs/src/libslic3r/Fill/FillRectilinear.cpp b/xs/src/libslic3r/Fill/FillRectilinear.cpp index 9af184295..69991db74 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.cpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.cpp @@ -41,7 +41,7 @@ FillRectilinear::_fill_single_direction(ExPolygon expolygon, // extend bounding box so that our pattern will be aligned with other layers // Transform the reference point to the rotated coordinate system. Point p = direction.second.rotated(-direction.first); - p.x -= x_shift > 0 ? x_shift : (x_shift + line_spacing); + p.x -= x_shift >= 0 ? x_shift : (x_shift + line_spacing); bounding_box.min.align_to_grid( Point(line_spacing, line_spacing), p diff --git a/xs/src/libslic3r/libslic3r.h b/xs/src/libslic3r/libslic3r.h index 561d0b03f..6789d58ff 100644 --- a/xs/src/libslic3r/libslic3r.h +++ b/xs/src/libslic3r/libslic3r.h @@ -94,7 +94,7 @@ parallelize(std::queue queue, boost::function func, if (threads_count == 0) threads_count = 2; boost::mutex queue_mutex; boost::thread_group workers; - for (int i = 0; i < fminf(threads_count, queue.size()); i++) + for (int i = 0; i < std::min(threads_count, (int)queue.size()); i++) workers.add_thread(new boost::thread(&_parallelize_do, &queue, &queue_mutex, func)); workers.join_all(); } From 117fb306363d40ac67f6bc51f8481010dc9b9419 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 21 Dec 2016 23:09:58 +0100 Subject: [PATCH 206/225] Ported CoolingBuffer to C++/XS --- lib/Slic3r.pm | 1 - lib/Slic3r/GCode/CoolingBuffer.pm | 94 ---------------- lib/Slic3r/Print/GCode.pm | 5 +- src/CMakeLists.txt | 1 + t/cooling.t | 23 ++-- xs/MANIFEST | 2 + xs/src/libslic3r/GCode/CoolingBuffer.cpp | 132 +++++++++++++++++++++++ xs/src/libslic3r/GCode/CoolingBuffer.hpp | 39 +++++++ xs/src/perlglue.cpp | 1 + xs/xsp/GCode.xsp | 11 ++ xs/xsp/my.map | 4 + xs/xsp/typemap.xspt | 4 + 12 files changed, 205 insertions(+), 112 deletions(-) delete mode 100644 lib/Slic3r/GCode/CoolingBuffer.pm create mode 100644 xs/src/libslic3r/GCode/CoolingBuffer.cpp create mode 100644 xs/src/libslic3r/GCode/CoolingBuffer.hpp diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 531ab7304..81cbc7eb5 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -56,7 +56,6 @@ use Slic3r::Format::AMF; use Slic3r::Format::OBJ; use Slic3r::Format::STL; use Slic3r::GCode::ArcFitting; -use Slic3r::GCode::CoolingBuffer; use Slic3r::GCode::MotionPlanner; use Slic3r::GCode::PressureRegulator; use Slic3r::GCode::Reader; diff --git a/lib/Slic3r/GCode/CoolingBuffer.pm b/lib/Slic3r/GCode/CoolingBuffer.pm deleted file mode 100644 index bbb9af472..000000000 --- a/lib/Slic3r/GCode/CoolingBuffer.pm +++ /dev/null @@ -1,94 +0,0 @@ -# A standalone (no C++ implementation) G-code filter, to control cooling of the print. -# The G-code is processed per layer. Once a layer is collected, fan start / stop commands are edited -# and the print is modified to stretch over a minimum layer time. - -package Slic3r::GCode::CoolingBuffer; -use Moo; - -has 'config' => (is => 'ro', required => 1); # Slic3r::Config::Print -has 'gcodegen' => (is => 'ro', required => 1); # Slic3r::GCode C++ instance -has 'gcode' => (is => 'rw', default => sub {""}); # Cache of a G-code snippet emitted for a single layer. -has 'elapsed_time' => (is => 'rw', default => sub {0}); -has 'layer_id' => (is => 'rw'); -has 'last_z' => (is => 'rw', default => sub { {} }); # obj_id => z (basically a 'last seen' table) -has 'min_print_speed' => (is => 'lazy'); - -sub _build_min_print_speed { - my $self = shift; - return 60 * $self->config->min_print_speed; -} - -sub append { - my $self = shift; - my ($gcode, $obj_id, $layer_id, $print_z) = @_; - - my $return = ""; - if (exists $self->last_z->{$obj_id} && $self->last_z->{$obj_id} != $print_z) { - # A layer was finished, Z of the object's layer changed. Process the layer. - $return = $self->flush; - } - - $self->layer_id($layer_id); - $self->last_z->{$obj_id} = $print_z; - $self->gcode($self->gcode . $gcode); - #FIXME Vojtech: This is a very rough estimate of the print time, - # not taking into account the acceleration profiles generated by the printer firmware. - $self->elapsed_time($self->elapsed_time + $self->gcodegen->elapsed_time); - $self->gcodegen->set_elapsed_time(0); - - return $return; -} - -sub flush { - my $self = shift; - - my $gcode = $self->gcode; - my $elapsed = $self->elapsed_time; - $self->gcode(""); - $self->elapsed_time(0); - $self->last_z({}); # reset the whole table otherwise we would compute overlapping times - - my $fan_speed = $self->config->fan_always_on ? $self->config->min_fan_speed : 0; - my $speed_factor = 1; - if ($self->config->cooling) { - Slic3r::debugf "Layer %d estimated printing time: %d seconds\n", $self->layer_id, $elapsed; - if ($elapsed < $self->config->slowdown_below_layer_time) { - # Layer time very short. Enable the fan to a full throttle and slow down the print - # (stretch the layer print time to slowdown_below_layer_time). - $fan_speed = $self->config->max_fan_speed; - $speed_factor = $elapsed / $self->config->slowdown_below_layer_time; - } elsif ($elapsed < $self->config->fan_below_layer_time) { - # Layer time quite short. Enable the fan proportionally according to the current layer time. - $fan_speed = $self->config->max_fan_speed - ($self->config->max_fan_speed - $self->config->min_fan_speed) - * ($elapsed - $self->config->slowdown_below_layer_time) - / ($self->config->fan_below_layer_time - $self->config->slowdown_below_layer_time); #/ - } - Slic3r::debugf " fan = %d%%, speed = %f%%\n", $fan_speed, $speed_factor * 100; - - if ($speed_factor < 1) { - # Adjust feed rate of G1 commands marked with an _EXTRUDE_SET_SPEED - # as long as they are not _WIPE moves (they cannot if they are _EXTRUDE_SET_SPEED) - # and they are not preceded directly by _BRIDGE_FAN_START (do not adjust bridging speed). - $gcode =~ s/^(?=.*?;_EXTRUDE_SET_SPEED)(?!.*?;_WIPE)(?min_print_speed ? $self->min_print_speed : $new_speed) - /gexm; - } - } - $fan_speed = 0 if $self->layer_id < $self->config->disable_fan_first_layers; - $gcode = $self->gcodegen->writer->set_fan($fan_speed) . $gcode; - - # bridge fan speed - if (!$self->config->cooling || $self->config->bridge_fan_speed == 0 || $self->layer_id < $self->config->disable_fan_first_layers) { - $gcode =~ s/^;_BRIDGE_FAN_(?:START|END)\n//gm; - } else { - $gcode =~ s/^;_BRIDGE_FAN_START\n/ $self->gcodegen->writer->set_fan($self->config->bridge_fan_speed, 1) /gmex; - $gcode =~ s/^;_BRIDGE_FAN_END\n/ $self->gcodegen->writer->set_fan($fan_speed, 1) /gmex; - } - $gcode =~ s/;_WIPE//g; - $gcode =~ s/;_EXTRUDE_SET_SPEED//g; - - return $gcode; -} - -1; diff --git a/lib/Slic3r/Print/GCode.pm b/lib/Slic3r/Print/GCode.pm index 25e8fb3c6..9e36322de 100644 --- a/lib/Slic3r/Print/GCode.pm +++ b/lib/Slic3r/Print/GCode.pm @@ -98,10 +98,7 @@ sub BUILD { } } - $self->_cooling_buffer(Slic3r::GCode::CoolingBuffer->new( - config => $self->config, - gcodegen => $self->_gcodegen, - )); + $self->_cooling_buffer(Slic3r::GCode::CoolingBuffer->new($self->_gcodegen)); $self->_spiral_vase(Slic3r::GCode::SpiralVase->new(config => $self->config)) if $self->config->spiral_vase; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e85b52dc5..faf23a75c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -52,6 +52,7 @@ add_library(libslic3r STATIC ${LIBDIR}/libslic3r/Fill/FillRectilinear.cpp ${LIBDIR}/libslic3r/Flow.cpp ${LIBDIR}/libslic3r/GCode.cpp + ${LIBDIR}/libslic3r/GCode/CoolingBuffer.cpp ${LIBDIR}/libslic3r/GCodeSender.cpp ${LIBDIR}/libslic3r/GCodeWriter.cpp ${LIBDIR}/libslic3r/Geometry.cpp diff --git a/t/cooling.t b/t/cooling.t index d06204ba1..a413a2b33 100644 --- a/t/cooling.t +++ b/t/cooling.t @@ -14,20 +14,17 @@ use List::Util qw(first); use Slic3r; use Slic3r::Test; +my $gcodegen; sub buffer { my $config = shift || Slic3r::Config->new; my $print_config = Slic3r::Config::Print->new; $print_config->apply_dynamic($config); - my $gcodegen = Slic3r::GCode->new; + $gcodegen = Slic3r::GCode->new; $gcodegen->apply_print_config($print_config); $gcodegen->set_layer_count(10); - my $buffer = Slic3r::GCode::CoolingBuffer->new( - config => $print_config, - gcodegen => $gcodegen, - ); - return $buffer; + return Slic3r::GCode::CoolingBuffer->new($gcodegen); } my $config = Slic3r::Config->new_from_defaults; @@ -35,14 +32,14 @@ $config->set('disable_fan_first_layers', 0); { my $buffer = buffer($config); - $buffer->gcodegen->set_elapsed_time($buffer->config->slowdown_below_layer_time + 1); + $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->slowdown_below_layer_time + 1); my $gcode = $buffer->append('G1 F3000;_EXTRUDE_SET_SPEED\nG1 X100 E1', 0, 0, 0.4) . $buffer->flush; like $gcode, qr/F3000/, 'speed is not altered when elapsed time is greater than slowdown threshold'; } { my $buffer = buffer($config); - $buffer->gcodegen->set_elapsed_time($buffer->config->slowdown_below_layer_time - 1); + $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->slowdown_below_layer_time - 1); my $gcode = $buffer->append( "G1 X50 F2500\n" . "G1 F3000;_EXTRUDE_SET_SPEED\n" . @@ -57,7 +54,7 @@ $config->set('disable_fan_first_layers', 0); { my $buffer = buffer($config); - $buffer->gcodegen->set_elapsed_time($buffer->config->fan_below_layer_time + 1); + $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time + 1); my $gcode = $buffer->append('G1 X100 E1 F3000', 0, 0, 0.4) . $buffer->flush; unlike $gcode, qr/M106/, 'fan is not activated when elapsed time is greater than fan threshold'; } @@ -67,7 +64,7 @@ $config->set('disable_fan_first_layers', 0); my $gcode = ""; for my $obj_id (0 .. 1) { # use an elapsed time which is < the slowdown threshold but greater than it when summed twice - $buffer->gcodegen->set_elapsed_time($buffer->config->slowdown_below_layer_time - 1); + $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->slowdown_below_layer_time - 1); $gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, 0, 0.4); } $gcode .= $buffer->flush; @@ -80,7 +77,7 @@ $config->set('disable_fan_first_layers', 0); for my $layer_id (0 .. 1) { for my $obj_id (0 .. 1) { # use an elapsed time which is < the threshold but greater than it when summed twice - $buffer->gcodegen->set_elapsed_time($buffer->config->fan_below_layer_time - 1); + $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time - 1); $gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, $layer_id, 0.4 + 0.4*$layer_id + 0.1*$obj_id); # print same layer at distinct heights } } @@ -94,7 +91,7 @@ $config->set('disable_fan_first_layers', 0); for my $layer_id (0 .. 1) { for my $obj_id (0 .. 1) { # use an elapsed time which is < the threshold even when summed twice - $buffer->gcodegen->set_elapsed_time($buffer->config->fan_below_layer_time/2 - 1); + $buffer->gcodegen->set_elapsed_time($buffer->gcodegen->config->fan_below_layer_time/2 - 1); $gcode .= $buffer->append("G1 X100 E1 F3000\n", $obj_id, $layer_id, 0.4 + 0.4*$layer_id + 0.1*$obj_id); # print same layer at distinct heights } } @@ -147,7 +144,7 @@ $config->set('disable_fan_first_layers', 0); my $print = Slic3r::Test::init_print('20mm_cube', config => $config); my @layer_times = (0); # in seconds - Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { + Slic3r::GCode::Reader->new->parse(my $gcode = Slic3r::Test::gcode($print), sub { my ($self, $cmd, $args, $info) = @_; if ($cmd eq 'G1') { diff --git a/xs/MANIFEST b/xs/MANIFEST index 733bd09e0..bbf7ab85a 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -44,6 +44,8 @@ src/libslic3r/Flow.cpp src/libslic3r/Flow.hpp src/libslic3r/GCode.cpp src/libslic3r/GCode.hpp +src/libslic3r/GCode/CoolingBuffer.cpp +src/libslic3r/GCode/CoolingBuffer.hpp src/libslic3r/GCodeSender.cpp src/libslic3r/GCodeSender.hpp src/libslic3r/GCodeWriter.cpp diff --git a/xs/src/libslic3r/GCode/CoolingBuffer.cpp b/xs/src/libslic3r/GCode/CoolingBuffer.cpp new file mode 100644 index 000000000..84df9cfd3 --- /dev/null +++ b/xs/src/libslic3r/GCode/CoolingBuffer.cpp @@ -0,0 +1,132 @@ +#include "CoolingBuffer.hpp" +#include +#include +#include + +namespace Slic3r { + +std::string +CoolingBuffer::append(const std::string &gcode, std::string obj_id, size_t layer_id, float print_z) +{ + std::string out; + if (this->_last_z.find(obj_id) != this->_last_z.end()) { + // A layer was finished, Z of the object's layer changed. Process the layer. + out = this->flush(); + } + + this->_layer_id = layer_id; + this->_last_z[obj_id] = print_z; + this->_gcode += gcode; + // This is a very rough estimate of the print time, + // not taking into account the acceleration curves generated by the printer firmware. + this->_elapsed_time += this->_gcodegen->elapsed_time; + this->_gcodegen->elapsed_time = 0; + + return out; +} + +void +apply_speed_factor(std::string &line, float speed_factor, float min_print_speed) +{ + // find pos of F + size_t pos = line.find_first_of('F'); + size_t last_pos = line.find_first_of(' ', pos+1); + + // extract current speed + float speed; + { + std::istringstream iss(line.substr(pos+1, last_pos)); + iss >> speed; + } + + // change speed + speed *= speed_factor; + speed = std::max(speed, min_print_speed); + + // replace speed in string + { + std::ostringstream oss; + oss << speed; + line.replace(pos+1, (last_pos-pos), oss.str()); + } +} + +std::string +CoolingBuffer::flush() +{ + GCode &gg = *this->_gcodegen; + + std::string gcode = this->_gcode; + float elapsed = this->_elapsed_time; + this->_gcode = ""; + this->_elapsed_time = 0; + this->_last_z.clear(); // reset the whole table otherwise we would compute overlapping times + + int fan_speed = gg.config.fan_always_on ? gg.config.min_fan_speed.value : 0; + + float speed_factor = 1.0; + + if (gg.config.cooling) { + #ifdef SLIC3R_DEBUG + printf("Layer %zu estimated printing time: %f seconds\n", layer_id, elapsed); + #endif + + if (elapsed < (float)gg.config.slowdown_below_layer_time) { + // Layer time very short. Enable the fan to a full throttle and slow down the print + // (stretch the layer print time to slowdown_below_layer_time). + fan_speed = gg.config.max_fan_speed; + speed_factor = elapsed / (float)gg.config.slowdown_below_layer_time; + } else if (elapsed < (float)gg.config.fan_below_layer_time) { + // Layer time quite short. Enable the fan proportionally according to the current layer time. + fan_speed = gg.config.max_fan_speed + - (gg.config.max_fan_speed - gg.config.min_fan_speed) + * (elapsed - (float)gg.config.slowdown_below_layer_time) + / (gg.config.fan_below_layer_time - gg.config.slowdown_below_layer_time); + } + + #ifdef SLIC3R_DEBUG + printf(" fan = %d%%, speed = %f%%\n", fan_speed, speed_factor * 100); + #endif + + if (speed_factor < 1.0) { + // Adjust feed rate of G1 commands marked with an _EXTRUDE_SET_SPEED + // as long as they are not _WIPE moves (they cannot if they are _EXTRUDE_SET_SPEED) + // and they are not preceded directly by _BRIDGE_FAN_START (do not adjust bridging speed). + std::string new_gcode; + std::istringstream ss(gcode); + std::string line; + bool bridge_fan_start = false; + while (std::getline(ss, line)) { + if (boost::starts_with(line, "G1") + && boost::contains(line, ";_EXTRUDE_SET_SPEED") + && !boost::contains(line, ";_WIPE") + && !bridge_fan_start) { + apply_speed_factor(line, speed_factor, this->_min_print_speed); + boost::replace_first(line, ";_EXTRUDE_SET_SPEED", ""); + } + bridge_fan_start = boost::contains(line, ";_BRIDGE_FAN_START"); + new_gcode += line + '\n'; + } + gcode = new_gcode; + } + } + if (this->_layer_id < gg.config.disable_fan_first_layers) + fan_speed = 0; + + gcode = gg.writer.set_fan(fan_speed) + gcode; + + // bridge fan speed + if (!gg.config.cooling || gg.config.bridge_fan_speed == 0 || this->_layer_id < gg.config.disable_fan_first_layers) { + boost::replace_all(gcode, ";_BRIDGE_FAN_START", ""); + boost::replace_all(gcode, ";_BRIDGE_FAN_END", ""); + } else { + boost::replace_all(gcode, ";_BRIDGE_FAN_START", gg.writer.set_fan(gg.config.bridge_fan_speed, true)); + boost::replace_all(gcode, ";_BRIDGE_FAN_END", gg.writer.set_fan(fan_speed, true)); + } + boost::replace_all(gcode, ";_WIPE", ""); + boost::replace_all(gcode, ";_EXTRUDE_SET_SPEED", ""); + + return gcode; +} + +} diff --git a/xs/src/libslic3r/GCode/CoolingBuffer.hpp b/xs/src/libslic3r/GCode/CoolingBuffer.hpp new file mode 100644 index 000000000..01770a58d --- /dev/null +++ b/xs/src/libslic3r/GCode/CoolingBuffer.hpp @@ -0,0 +1,39 @@ +#ifndef slic3r_CoolingBuffer_hpp_ +#define slic3r_CoolingBuffer_hpp_ + +#include "libslic3r.h" +#include "GCode.hpp" +#include +#include + +namespace Slic3r { + +/* +A standalone G-code filter, to control cooling of the print. +The G-code is processed per layer. Once a layer is collected, fan start / stop commands are edited +and the print is modified to stretch over a minimum layer time. +*/ + +class CoolingBuffer { + public: + CoolingBuffer(GCode &gcodegen) + : _gcodegen(&gcodegen), _elapsed_time(0.), _layer_id(0) + { + this->_min_print_speed = this->_gcodegen->config.min_print_speed * 60; + }; + std::string append(const std::string &gcode, std::string obj_id, size_t layer_id, float print_z); + std::string flush(); + GCode* gcodegen() { return this->_gcodegen; }; + + private: + GCode* _gcodegen; + std::string _gcode; + float _elapsed_time; + size_t _layer_id; + std::map _last_z; + float _min_print_speed; +}; + +} + +#endif diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index ad87768b5..3c08d6144 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -13,6 +13,7 @@ REGISTER_CLASS(ExtrusionEntityCollection, "ExtrusionPath::Collection"); REGISTER_CLASS(Flow, "Flow"); REGISTER_CLASS(Filler, "Filler"); REGISTER_CLASS(AvoidCrossingPerimeters, "GCode::AvoidCrossingPerimeters"); +REGISTER_CLASS(CoolingBuffer, "GCode::CoolingBuffer"); REGISTER_CLASS(OozePrevention, "GCode::OozePrevention"); REGISTER_CLASS(Wipe, "GCode::Wipe"); REGISTER_CLASS(GCode, "GCode"); diff --git a/xs/xsp/GCode.xsp b/xs/xsp/GCode.xsp index 7f17a3a12..579951c73 100644 --- a/xs/xsp/GCode.xsp +++ b/xs/xsp/GCode.xsp @@ -3,6 +3,7 @@ %{ #include #include "libslic3r/GCode.hpp" +#include "libslic3r/GCode/CoolingBuffer.hpp" %} %name{Slic3r::GCode::AvoidCrossingPerimeters} class AvoidCrossingPerimeters { @@ -70,6 +71,16 @@ %code{% THIS->path = *value; %}; }; +%name{Slic3r::GCode::CoolingBuffer} class CoolingBuffer { + CoolingBuffer(GCode* gcode) + %code{% RETVAL = new CoolingBuffer(*gcode); %}; + ~CoolingBuffer(); + Ref gcodegen(); + + std::string append(std::string gcode, std::string obj_id, size_t layer_id, float print_z); + std::string flush(); +}; + %name{Slic3r::GCode} class GCode { GCode(); ~GCode(); diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 18110d5dd..1ce5d719f 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -194,6 +194,10 @@ OozePrevention* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T +CoolingBuffer* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T +Clone O_OBJECT_SLIC3R_T + GCode* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T Clone O_OBJECT_SLIC3R_T diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 94740e0e7..1433b828f 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -152,6 +152,10 @@ %typemap{Ref}{simple}; %typemap{Clone}{simple}; +%typemap{CoolingBuffer*}; +%typemap{Ref}{simple}; +%typemap{Clone}{simple}; + %typemap{GCode*}; %typemap{Ref}{simple}; %typemap{Clone}{simple}; From 9581ddf641e7194733c334f5e7bd309872801142 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 22 Dec 2016 10:52:46 +0100 Subject: [PATCH 207/225] Bugfix: local::lib check was creating ~/perl5. #3635 --- Build.PL | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Build.PL b/Build.PL index bace8ae4b..93cc7b1e6 100644 --- a/Build.PL +++ b/Build.PL @@ -109,7 +109,7 @@ EOF push @cpanm_args, "--sudo" if $sudo; # install local::lib without --local-lib otherwise it's not usable afterwards - if (!eval "use local::lib; 1") { + if (!eval "use local::lib qw(local-lib); 1") { my $res = system $cpanm, @cpanm_args, 'local::lib'; warn "Warning: local::lib is required. You might need to run the `cpanm --sudo local::lib` command in order to install it.\n" if $res != 0; From da0f4c4e01a2f893092f8475e20027deec42c9ab Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 22 Dec 2016 11:16:28 +0100 Subject: [PATCH 208/225] Since we have removed the INFILL_OVERLAP_OVER_SPACING constant, increase the infill_overlap value for compensation --- xs/src/libslic3r/PrintConfig.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 9fa971121..100532199 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -581,7 +581,7 @@ PrintConfigDef::PrintConfigDef() def->sidetext = "mm or %"; def->cli = "infill-overlap=s"; def->ratio_over = "perimeter_extrusion_width"; - def->default_value = new ConfigOptionFloatOrPercent(15, true); + def->default_value = new ConfigOptionFloatOrPercent(55, true); def = this->add("infill_speed", coFloat); def->label = "Infill"; From 85bf2ed7aed3f70f7db755dd075940d33ba50cf9 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 22 Dec 2016 12:13:28 +0100 Subject: [PATCH 209/225] Bugfix: object disappeared from 2D plater when reloaded from disk. #3634 --- lib/Slic3r/GUI/Plater.pm | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 4474550f5..9e61630fb 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1434,10 +1434,10 @@ sub reload_from_disk { return if !$model_object->input_file || !-e $model_object->input_file; - my @obj_idx = $self->load_file($model_object->input_file); - return if !@obj_idx; + my @new_obj_idx = $self->load_file($model_object->input_file); + return if !@new_obj_idx; - foreach my $new_obj_idx (@obj_idx) { + foreach my $new_obj_idx (@new_obj_idx) { my $o = $self->{model}->objects->[$new_obj_idx]; $o->clear_instances; $o->add_instance($_) for @{$model_object->instances}; @@ -1450,6 +1450,12 @@ sub reload_from_disk { } $self->remove($obj_idx); + + # Trigger thumbnail generation again, because the remove() method altered + # object indexes before background thumbnail generation called its completion + # event, so the on_thumbnail_made callback is called with the wrong $obj_idx. + # When porting to C++ we'll probably have cleaner ways to do this. + $self->make_thumbnail($_-1) for @new_obj_idx; } sub export_object_stl { From cb72afc10e5a8556b35b7a20939ca4d40a8f3dc3 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 22 Dec 2016 12:22:00 +0100 Subject: [PATCH 210/225] Update invalidate_state_by_config_options() for the new semantics of infill_overlap --- xs/src/libslic3r/PrintObject.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 8002ab0be..4f38cfa1d 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -235,7 +235,6 @@ PrintObject::invalidate_state_by_config_options(const std::vector Date: Thu, 22 Dec 2016 13:18:22 +0100 Subject: [PATCH 211/225] Bugfix: rare crash when loading 3D preview. #3636 --- xs/src/slic3r/GUI/3DScene.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xs/src/slic3r/GUI/3DScene.cpp b/xs/src/slic3r/GUI/3DScene.cpp index e398a1c37..519f96aa9 100644 --- a/xs/src/slic3r/GUI/3DScene.cpp +++ b/xs/src/slic3r/GUI/3DScene.cpp @@ -8,6 +8,8 @@ _3DScene::_extrusionentity_to_verts_do(const Lines &lines, const std::vector &heights, bool closed, double top_z, const Point ©, GLVertexArray* qverts, GLVertexArray* tverts) { + if (lines.empty()) return; + /* It looks like it's faster without reserving capacity... // each segment has 4 quads, thus 16 vertices; + 2 caps qverts->reserve_more(3 * 4 * (4 * lines.size() + 2)); From df4f6434dbdc1440f1dc14991606e4af21cff1b4 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sat, 31 Dec 2016 00:17:42 -0600 Subject: [PATCH 212/225] Added second link to bintray for manually packaged --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 24d5829c2..b537fb9b5 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ A: Yes. Slic3r [![Build Status](https://travis-ci.org/alexrj/Slic3r.png?branch=master)](https://travis-ci.org/alexrj/Slic3r) [![Build status](https://ci.appveyor.com/api/projects/status/8iqmeat6cj158vo6?svg=true)](https://ci.appveyor.com/project/lordofhyphens/slic3r) ====== -Prebuilt Win32 builds https://bintray.com/lordofhyphens/Slic3r/slic3r_dev/view +Prebuilt Win32 builds: +* https://bintray.com/lordofhyphens/Slic3r/slic3r_dev/view (from build server) +* https://bintray.com/lordofhyphens/Slic3r/slic3r_dev/1.3.0-dev (manually packaged) Slic3r takes 3D models (STL, OBJ, AMF) and converts them into G-code instructions for 3D printers. It's compatible with any modern printer based on the RepRap toolchain, From a2c7e5686249a97ac9adfac4132b3900dfe43f18 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 1 Jan 2017 17:51:51 -0600 Subject: [PATCH 213/225] Fix typo in debug code, use this->_layer_id not plain layer_id. --- xs/src/libslic3r/GCode/CoolingBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/GCode/CoolingBuffer.cpp b/xs/src/libslic3r/GCode/CoolingBuffer.cpp index 84df9cfd3..e976c9f46 100644 --- a/xs/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/xs/src/libslic3r/GCode/CoolingBuffer.cpp @@ -68,7 +68,7 @@ CoolingBuffer::flush() if (gg.config.cooling) { #ifdef SLIC3R_DEBUG - printf("Layer %zu estimated printing time: %f seconds\n", layer_id, elapsed); + printf("Layer %zu estimated printing time: %f seconds\n", this->_layer_id, elapsed); #endif if (elapsed < (float)gg.config.slowdown_below_layer_time) { From 43c203a8e02138bc235bc4c9bdc0800a40f33ea5 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 1 Jan 2017 17:58:52 -0600 Subject: [PATCH 214/225] Cleanup of Win32 packaging build script (#3646) * cleaned up package to use local::lib Doesn't touch anything but the package script in utils :) --- utils/package_win32.ps1 | 88 ++++------------------------------------- 1 file changed, 7 insertions(+), 81 deletions(-) diff --git a/utils/package_win32.ps1 b/utils/package_win32.ps1 index f4e0bf250..d56a9429f 100644 --- a/utils/package_win32.ps1 +++ b/utils/package_win32.ps1 @@ -29,38 +29,24 @@ pp ` -a "${STRAWBERRY_PATH}\perl\bin\libwinpthread-1.dll;libwinpthread-1.dll" ` -a "${STRAWBERRY_PATH}\perl\bin\freeglut.dll;freeglut.dll" ` -a "${STRAWBERRY_PATH}\c\bin\libglut-0_.dll;libglut-0_.dll" ` --a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxbase30u_gcc_custom.dll;wxbase30u_gcc_custom.dll" ` --a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_adv_gcc_custom.dll;wxmsw30u_adv_gcc_custom.dll" ` --a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_gl_gcc_custom.dll;wxmsw30u_gl_gcc_custom.dll" ` --a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_core_gcc_custom.dll;wxmsw30u_core_gcc_custom.dll" ` --a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxmsw30u_html_gcc_custom.dll;wxmsw30u_html_gcc_custom.dll" ` --a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxbase30u_xml_gcc_custom.dll;wxbase30u_xml_gcc_custom.dll" ` --a "${STRAWBERRY_PATH}\perl\site\lib\Alien\wxWidgets\msw_3_0_2_uni_gcc_3_4\lib\wxbase30u_net_gcc_custom.dll;wxbase30u_net_gcc_custom.dll" ` -a "../lib;lib" ` +-a "../local-lib;local-lib" ` -a "../slic3r.pl;slic3r.pl" ` -M AutoLoader ` -M B ` -M Carp ` +-M Config ` +-M Crypt::CBC ` -M Class::Accessor ` -M Class::XSAccessor ` -M Class::XSAccessor::Heavy ` --M Config ` --M Crypt::CBC ` -M Cwd ` --M Data::UUID ` -M Devel::GlobalDestruction ` -M Digest ` -M Digest::MD5 ` -M Digest::SHA ` -M Digest::base ` -M DynaLoader ` --M Encode ` --M Encode::Alias ` --M Encode::Byte ` --M Encode::Config ` --M Encode::Encoding ` --M Encode::Locale ` --M Encode::MIME::Name ` -M Errno ` -M Exporter ` -M Exporter::Heavy ` @@ -71,8 +57,6 @@ pp ` -M File::Spec::Unix ` -M File::Spec::Win32 ` -M FindBin ` --M Getopt::Long ` --M Growl::GNTP ` -M HTTP::Config ` -M HTTP::Date ` -M HTTP::Headers ` @@ -85,11 +69,6 @@ pp ` -M IO ` -M IO::Handle ` -M IO::Select ` --M IO::Socket ` --M IO::Socket::INET ` --M IO::Socket::INET6 ` --M IO::Socket::IP ` --M IO::Socket::UNIX ` -M LWP ` -M LWP::MediaTypes ` -M LWP::MemberMixin ` @@ -102,40 +81,11 @@ pp ` -M Method::Generate::BuildAll ` -M Method::Generate::Constructor ` -M Module::Runtime ` --M Moo ` --M Moo::HandleMoose ` --M Moo::Object ` --M Moo::Role ` --M Moo::sification ` --M Net::Bonjour ` --M Net::Bonjour::Entry ` --M Net::DNS ` --M Net::DNS::Domain ` --M Net::DNS::DomainName ` --M Net::DNS::Header ` --M Net::DNS::Packet ` --M Net::DNS::Parameters ` --M Net::DNS::Question ` --M Net::DNS::RR ` --M Net::DNS::RR::OPT ` --M Net::DNS::RR::PTR ` --M Net::DNS::Resolver ` --M Net::DNS::Resolver ` --M Net::DNS::Resolver::Base ` --M Net::DNS::Resolver::MSWin32 ` --M Net::DNS::Update ` --M Net::HTTP ` --M Net::HTTP::Methods ` --M OpenGL ` -M POSIX ` -M Pod::Escapes ` -M Pod::Text ` -M Pod::Usage ` --M Role::Tiny ` --M Scalar::Util ` -M SelectSaver ` --M Slic3r::* ` --M Slic3r::XS ` -M Socket ` -M Socket6 ` -M Storable ` @@ -143,7 +93,6 @@ pp ` -M Sub::Exporter ` -M Sub::Exporter::Progressive ` -M Sub::Name ` --M Sub::Quote ` -M Sub::Util ` -M Symbol ` -M Term::Cap ` @@ -154,41 +103,16 @@ pp ` -M Tie::Handle ` -M Tie::Hash ` -M Tie::StdHandle ` --M Time::HiRes ` -M Time::Local ` -M URI ` -M URI::Escape ` -M URI::http ` -M Unicode::Normalize ` --M Win32 ` --M Win32::API ` --M Win32::API::Struct ` --M Win32::API::Type ` --M Win32::IPHelper ` --M Win32::TieRegistry ` --M Win32::WinError ` --M Win32API::Registry ` --M Wx ` --M Wx::App ` --M Wx::DND ` --M Wx::DropSource ` --M Wx::Event ` --M Wx::GLCanvas ` --M Wx::Grid ` --M Wx::Html ` --M Wx::Locale ` --M Wx::Menu ` --M Wx::Mini ` --M Wx::Print ` --M Wx::RadioBox ` --M Wx::Timer ` -M XSLoader ` -M attributes ` -M base ` -M bytes ` -M constant ` --M constant ` --M constant::defer ` -M enum ` -M feature ` -M integer ` @@ -201,12 +125,14 @@ pp ` -M parent ` -M re ` -M strict ` --M threads ` --M threads::shared ` -M utf8 ` -M vars ` -M warnings ` -M warnings::register ` +-M Win32::API ` +-M Win32::TieRegistry ` +-M Win32::WinError ` +-M Win32API::Registry ` -e -p ..\slic3r.pl -o ..\slic3r.par copy ..\slic3r.par "..\slic3r-${current_branch}-${APPVEYOR_BUILD_NUMBER}-$(git rev-parse --short HEAD).zip" From ed0173364f56ce0b508e3f19e6e6bdebf71cdf77 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 1 Jan 2017 23:43:13 -0600 Subject: [PATCH 215/225] initialize $self->{move_target} before calling functions on it. --- lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm index 94cbe931a..1898ac6a0 100644 --- a/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm +++ b/lib/Slic3r/GUI/Plater/ObjectPartsPanel.pm @@ -67,6 +67,10 @@ sub new { $settings_sizer->Add($self->{settings_panel}, 1, wxEXPAND | wxALL, 0); my $optgroup_movers; + # initialize the movement target before it's used. + # on Windows this causes a segfault due to calling distance_to() + # on the object. + $self->{move_target} = Slic3r::Pointf3->new; $optgroup_movers = $self->{optgroup_movers} = Slic3r::GUI::OptionsGroup->new( parent => $self, title => 'Move', From 16df4df8f87cb035528f4447a1a21ac79eb2e1af Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 2 Jan 2017 01:01:04 -0600 Subject: [PATCH 216/225] Using Perl 5.24.0 instead of 5.22.2, made packaging script smarter --- utils/autorun.bat | 2 +- utils/package_win32.ps1 | 67 +++++++++++++++++++++++------------------ 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/utils/autorun.bat b/utils/autorun.bat index b952b87e5..be2c7b3f0 100644 --- a/utils/autorun.bat +++ b/utils/autorun.bat @@ -1 +1 @@ -@perl5.22.2.exe slic3r.pl %* +@perl5.24.0.exe slic3r.pl %* diff --git a/utils/package_win32.ps1 b/utils/package_win32.ps1 index d56a9429f..6349590c3 100644 --- a/utils/package_win32.ps1 +++ b/utils/package_win32.ps1 @@ -1,17 +1,27 @@ # Written by Joseph Lenox # Licensed under the same license as the rest of Slic3r. # ------------------------ -# You need to have Strawberry Perl 5.22 installed for this to work, +# You need to have Strawberry Perl 5.24.0.1 installed for this to work, +param ( + [switch]$exe = $false +) echo "Make this is run from the perl command window." echo "Requires PAR." New-Variable -Name "current_branch" -Value "" +New-Variable -Name "current_date" -Value "$(Get-Date -UFormat '%Y.%m.%d')" +New-Variable -Name "output_file" -Value "" git branch | foreach { - if ($_ -match "` (.*)"){ + if ($_ -match "\*` (.*)"){ $current_branch += $matches[1] } } +if ($exe) { + $output_file = "slic3r.exe" +} else { + $output_file = "slic3r.par" +} # Change this to where you have Strawberry Perl installed. New-Variable -Name "STRAWBERRY_PATH" -Value "C:\Strawberry" @@ -22,8 +32,8 @@ pp ` -a "../utils;utils" ` -a "autorun.bat;slic3r.bat" ` -a "../var;var" ` --a "${STRAWBERRY_PATH}\perl\bin\perl5.22.2.exe;perl5.22.2.exe" ` --a "${STRAWBERRY_PATH}\perl\bin\perl522.dll;perl522.dll" ` +-a "${STRAWBERRY_PATH}\perl\bin\perl5.24.0.exe;perl5.24.0.exe" ` +-a "${STRAWBERRY_PATH}\perl\bin\perl524.dll;perl524.dll" ` -a "${STRAWBERRY_PATH}\perl\bin\libgcc_s_sjlj-1.dll;libgcc_s_sjlj-1.dll" ` -a "${STRAWBERRY_PATH}\perl\bin\libstdc++-6.dll;libstdc++-6.dll" ` -a "${STRAWBERRY_PATH}\perl\bin\libwinpthread-1.dll;libwinpthread-1.dll" ` @@ -35,11 +45,11 @@ pp ` -M AutoLoader ` -M B ` -M Carp ` --M Config ` --M Crypt::CBC ` -M Class::Accessor ` -M Class::XSAccessor ` -M Class::XSAccessor::Heavy ` +-M Config ` +-M Crypt::CBC ` -M Cwd ` -M Devel::GlobalDestruction ` -M Digest ` @@ -108,31 +118,30 @@ pp ` -M URI::Escape ` -M URI::http ` -M Unicode::Normalize ` --M XSLoader ` --M attributes ` --M base ` --M bytes ` --M constant ` --M enum ` --M feature ` --M integer ` --M locale ` --M lib ` --M mro ` --M overload ` --M overload::numbers ` --M overloading ` --M parent ` --M re ` --M strict ` --M utf8 ` --M vars ` --M warnings ` --M warnings::register ` -M Win32::API ` -M Win32::TieRegistry ` -M Win32::WinError ` -M Win32API::Registry ` --e -p ..\slic3r.pl -o ..\slic3r.par +-M XSLoader ` +-B ` +-M lib ` +-p ..\slic3r.pl -o ..\${output_file} -copy ..\slic3r.par "..\slic3r-${current_branch}-${APPVEYOR_BUILD_NUMBER}-$(git rev-parse --short HEAD).zip" +# switch renaming based on whether or not using packaged exe or zip +if ($exe) { + if (Test-Path variable:\APPVEYOR_BUILD_NUMBER) { + copy ..\slic3r.exe "..\slic3r-${current_branch}-${APPVEYOR_BUILD_NUMBER}-$(git rev-parse --short HEAD).exe" + del ..\slic3r.exe + } else { + copy ..\slic3r.exe "..\slic3r-${current_branch}.${current_date}.$(git rev-parse --short HEAD).exe" + del ..\slic3r.exe + } +} else { +# make this more useful for not being on the appveyor server + if (Test-Path variable:\APPVEYOR_BUILD_NUMBER) { + copy ..\slic3r.par "..\slic3r-${current_branch}-${APPVEYOR_BUILD_NUMBER}-$(git rev-parse --short HEAD).zip" + } else { + copy ..\slic3r.par "..\slic3r-${current_branch}.${current_date}.$(git rev-parse --short HEAD).zip" + del ../slic3r.par + } +} From 586d95b28a8e473bdcbd959f1e37d215ae531df5 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 2 Jan 2017 17:33:42 -0600 Subject: [PATCH 217/225] Minor revisions/updates for win32 packager (#3651) * more tweaks for the build server and packaging * more tweaks for the build server and packaging * fixed appveyor variable names * Revert to old regex * Fixed typo * Used wrong braces * Normalizing separators --- utils/package_win32.ps1 | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/utils/package_win32.ps1 b/utils/package_win32.ps1 index 6349590c3..1311c4a48 100644 --- a/utils/package_win32.ps1 +++ b/utils/package_win32.ps1 @@ -13,8 +13,14 @@ New-Variable -Name "current_date" -Value "$(Get-Date -UFormat '%Y.%m.%d')" New-Variable -Name "output_file" -Value "" git branch | foreach { - if ($_ -match "\*` (.*)"){ - $current_branch += $matches[1] + if ($env:APPVEYOR) { + if ($_ -match "` (.*)") { + $current_branch += $matches[1] + } + } else { + if ($_ -match "\*` (.*)"){ + $current_branch += $matches[1] + } } } if ($exe) { @@ -129,8 +135,8 @@ pp ` # switch renaming based on whether or not using packaged exe or zip if ($exe) { - if (Test-Path variable:\APPVEYOR_BUILD_NUMBER) { - copy ..\slic3r.exe "..\slic3r-${current_branch}-${APPVEYOR_BUILD_NUMBER}-$(git rev-parse --short HEAD).exe" + if ($env:APPVEYOR) { + copy ..\slic3r.exe "..\slic3r-${current_branch}.${current_date}.${env:APPVEYOR_BUILD_NUMBER}.$(git rev-parse --short HEAD).exe" del ..\slic3r.exe } else { copy ..\slic3r.exe "..\slic3r-${current_branch}.${current_date}.$(git rev-parse --short HEAD).exe" @@ -138,8 +144,8 @@ if ($exe) { } } else { # make this more useful for not being on the appveyor server - if (Test-Path variable:\APPVEYOR_BUILD_NUMBER) { - copy ..\slic3r.par "..\slic3r-${current_branch}-${APPVEYOR_BUILD_NUMBER}-$(git rev-parse --short HEAD).zip" + if ($env:APPVEYOR) { + copy ..\slic3r.par "..\slic3r-${current_branch}.${current_date}.${env:APPVEYOR_BUILD_NUMBER}.$(git rev-parse --short HEAD).zip" } else { copy ..\slic3r.par "..\slic3r-${current_branch}.${current_date}.$(git rev-parse --short HEAD).zip" del ../slic3r.par From f4bf6fef924159a9e220aeea8eb965473e82a002 Mon Sep 17 00:00:00 2001 From: Gege2B Date: Sun, 8 Jan 2017 22:46:48 +0100 Subject: [PATCH 218/225] Fix for #3655 - always print after gcode send --- lib/Slic3r/GUI/Plater.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 9e61630fb..b3335c68d 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1399,7 +1399,7 @@ sub send_gcode { # OctoPrint doesn't like Windows paths so we use basename() # Also, since we need to read from filesystem we process it through encode_path() file => [ $path, basename($path) ], - print => $self->{send_gcode_file} ? 1 : 0, + print => $self->{send_gcode_file_print} ? 1 : 0, ], ); From ee2d14fcd2ee30e19c34b15372c333df9d735b2a Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 11 Jan 2017 16:31:45 +0100 Subject: [PATCH 219/225] Bugfix: wrong alignment after cut by grid --- lib/Slic3r/GUI/Plater/ObjectCutDialog.pm | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm index 14a3500e0..cf4f3d99d 100644 --- a/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectCutDialog.pm @@ -193,9 +193,10 @@ sub new { mesh => $meshes->[$i], name => $o->name, ); - my $min = $v->mesh->bounding_box->min_point->clone; $o->center_around_origin; - my $i = $o->add_instance(offset => Slic3r::Pointf->new(@{$min}[X,Y])); + my $i = $o->add_instance( + offset => Slic3r::Pointf->new(@{$o->origin_translation->negative}[X,Y]), + ); $i->offset->translate( 5 * ceil(($i->offset->x - $bb->center->x) / $grid_x), 5 * ceil(($i->offset->y - $bb->center->y) / $grid_y), From 182d68ad85055e5019187fd3d890f432e543cc84 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 11 Jan 2017 18:55:48 +0100 Subject: [PATCH 220/225] Automatically switch to rectilinear when a pattern is used that doesn't support solid infill --- xs/src/libslic3r/Fill/Fill.hpp | 3 +++ xs/src/libslic3r/Fill/FillConcentric.hpp | 1 + xs/src/libslic3r/Fill/FillRectilinear.hpp | 6 ++++++ xs/src/libslic3r/LayerRegionFill.cpp | 9 +++++++++ 4 files changed, 19 insertions(+) diff --git a/xs/src/libslic3r/Fill/Fill.hpp b/xs/src/libslic3r/Fill/Fill.hpp index 151d61436..abb9e1c65 100644 --- a/xs/src/libslic3r/Fill/Fill.hpp +++ b/xs/src/libslic3r/Fill/Fill.hpp @@ -75,6 +75,9 @@ public: // Do not sort the fill lines to optimize the print head path? virtual bool no_sort() const { return false; }; + // Can this pattern be used for solid infill? + virtual bool can_solid() const { return false; }; + // Perform the fill. virtual Polylines fill_surface(const Surface &surface); diff --git a/xs/src/libslic3r/Fill/FillConcentric.hpp b/xs/src/libslic3r/Fill/FillConcentric.hpp index f19a5d2b1..bc973b757 100644 --- a/xs/src/libslic3r/Fill/FillConcentric.hpp +++ b/xs/src/libslic3r/Fill/FillConcentric.hpp @@ -19,6 +19,7 @@ protected: Polylines* polylines_out); virtual bool no_sort() const { return true; } + virtual bool can_solid() const { return true; }; }; } // namespace Slic3r diff --git a/xs/src/libslic3r/Fill/FillRectilinear.hpp b/xs/src/libslic3r/Fill/FillRectilinear.hpp index 6d493fbc7..d5d5b8ecf 100644 --- a/xs/src/libslic3r/Fill/FillRectilinear.hpp +++ b/xs/src/libslic3r/Fill/FillRectilinear.hpp @@ -12,6 +12,7 @@ class FillRectilinear : public Fill public: virtual Fill* clone() const { return new FillRectilinear(*this); }; virtual ~FillRectilinear() {} + virtual bool can_solid() const { return true; }; protected: virtual void _fill_surface_single( @@ -50,6 +51,7 @@ class FillAlignedRectilinear : public FillRectilinear public: virtual Fill* clone() const { return new FillAlignedRectilinear(*this); }; virtual ~FillAlignedRectilinear() {}; + virtual bool can_solid() const { return false; }; protected: // Keep the angle constant in all layers. @@ -61,6 +63,7 @@ class FillGrid : public FillRectilinear public: virtual Fill* clone() const { return new FillGrid(*this); }; virtual ~FillGrid() {} + virtual bool can_solid() const { return false; }; protected: // The grid fill will keep the angle constant between the layers,; see the implementation of Slic3r::Fill. @@ -78,6 +81,7 @@ class FillTriangles : public FillRectilinear public: virtual Fill* clone() const { return new FillTriangles(*this); }; virtual ~FillTriangles() {} + virtual bool can_solid() const { return false; }; protected: // The grid fill will keep the angle constant between the layers,; see the implementation of Slic3r::Fill. @@ -95,6 +99,7 @@ class FillStars : public FillRectilinear public: virtual Fill* clone() const { return new FillStars(*this); }; virtual ~FillStars() {} + virtual bool can_solid() const { return false; }; protected: // The grid fill will keep the angle constant between the layers,; see the implementation of Slic3r::Fill. @@ -112,6 +117,7 @@ class FillCubic : public FillRectilinear public: virtual Fill* clone() const { return new FillCubic(*this); }; virtual ~FillCubic() {} + virtual bool can_solid() const { return false; }; protected: // The grid fill will keep the angle constant between the layers,; see the implementation of Slic3r::Fill. diff --git a/xs/src/libslic3r/LayerRegionFill.cpp b/xs/src/libslic3r/LayerRegionFill.cpp index 6beb82e36..1899b7fcc 100644 --- a/xs/src/libslic3r/LayerRegionFill.cpp +++ b/xs/src/libslic3r/LayerRegionFill.cpp @@ -183,6 +183,15 @@ LayerRegion::make_fill() #else std::auto_ptr f = std::auto_ptr(Fill::new_from_type(fill_pattern)); #endif + + // switch to rectilinear if this pattern doesn't support solid infill + if (density > 0.9999f && !f->can_solid()) + #if SLIC3R_CPPVER >= 11 + f = std::unique_ptr(Fill::new_from_type(ipRectilinear)); + #else + f = std::auto_ptr(Fill::new_from_type(ipRectilinear)); + #endif + f->bounding_box = this->layer()->object()->bounding_box(); // calculate the actual flow we'll be using for this infill From c36a7c1e3dc675e1738cd82be6e9d7f61f18e452 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 11 Jan 2017 23:08:15 +0100 Subject: [PATCH 221/225] Fixed error in previous commit --- xs/src/libslic3r/LayerRegionFill.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xs/src/libslic3r/LayerRegionFill.cpp b/xs/src/libslic3r/LayerRegionFill.cpp index 1899b7fcc..0b4276439 100644 --- a/xs/src/libslic3r/LayerRegionFill.cpp +++ b/xs/src/libslic3r/LayerRegionFill.cpp @@ -185,7 +185,7 @@ LayerRegion::make_fill() #endif // switch to rectilinear if this pattern doesn't support solid infill - if (density > 0.9999f && !f->can_solid()) + if (density > 99 && !f->can_solid()) #if SLIC3R_CPPVER >= 11 f = std::unique_ptr(Fill::new_from_type(ipRectilinear)); #else From 1f920dc8470a42647db654e7d984e522a93538d2 Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Sun, 15 Jan 2017 13:28:02 -0600 Subject: [PATCH 222/225] Added note about issues and release version --- .github/CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 004ad75e2..601c44db4 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -2,7 +2,9 @@ Did you encounter an issue with using Slic3r? Fear not! This guide will help you There is a good chance that the issue, you have encountered, is already reported. Please check the [list of reported issues](https://github.com/alexrj/Slic3r/issues) before creating a new issue report. If you find an existing issue report, feel free to add further information to that report. -If possible, please include the following information when [reporting an issue](https://github.com/alexrj/Slic3r/issues/new): +If you are reporting an issue relating to a release version of Slic3r, it would help a lot if you could also confirm that the behavior is still present in the newest build [(windows)](https://bintray.com/lordofhyphens/Slic3r/slic3r_dev/). Otherwise your issue will be closed as soon as someone else isn't able to reproduce it on current master. + +When possible, please include the following information when [reporting an issue](https://github.com/alexrj/Slic3r/issues/new): * Slic3r version (See the about dialog for the version number. If running from git, please include the git commit ID from `git rev-parse HEAD` also.) * Operating system type + version * Steps to reproduce the issue, including: From 847392867df871bbc39ad69ee8b8753e736f57ca Mon Sep 17 00:00:00 2001 From: Joseph Lenox Date: Mon, 16 Jan 2017 13:45:25 -0600 Subject: [PATCH 223/225] Disable background processing by default. It causes more problems than it solves, especially with the new slice-on-preview code available. --- lib/Slic3r/GUI.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 760a0f330..b3c306850 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -67,7 +67,7 @@ our $Settings = { mode => 'simple', version_check => 1, autocenter => 1, - background_processing => 1, + background_processing => 0, # If set, the "Controller" tab for the control of the printer over serial line and the serial port settings are hidden. # By default, Prusa has the controller hidden. no_controller => 1, From 7eeb30d33d938db25dbbd51b23c9a126b0cfcdec Mon Sep 17 00:00:00 2001 From: Ralph Giles Date: Sun, 22 Jan 2017 15:04:42 -0800 Subject: [PATCH 224/225] Readme improvements (#3679) * Use svg badge for travis build status. This avoids bluriness with high-density displays. We're already using svg for the appveyor status. * Update README to use https urls. Both xkcd and freenode are available securely now. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b537fb9b5..d9f3cff52 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ _Q: Oh cool, a new RepRap slicer?_ A: Yes. -Slic3r [![Build Status](https://travis-ci.org/alexrj/Slic3r.png?branch=master)](https://travis-ci.org/alexrj/Slic3r) [![Build status](https://ci.appveyor.com/api/projects/status/8iqmeat6cj158vo6?svg=true)](https://ci.appveyor.com/project/lordofhyphens/slic3r) +Slic3r [![Build Status](https://travis-ci.org/alexrj/Slic3r.svg?branch=master)](https://travis-ci.org/alexrj/Slic3r) [![Build status](https://ci.appveyor.com/api/projects/status/8iqmeat6cj158vo6?svg=true)](https://ci.appveyor.com/project/lordofhyphens/slic3r) ====== Prebuilt Win32 builds: * https://bintray.com/lordofhyphens/Slic3r/slic3r_dev/view (from build server) @@ -20,7 +20,7 @@ See the [project homepage](http://slic3r.org/) at slic3r.org and the The core geometric algorithms and data structures are written in C++, and Perl is used for high-level flow abstraction, GUI and testing. -If you're wondering why Perl, see http://xkcd.com/224/ +If you're wondering why Perl, see https://xkcd.com/224/ The C++ API is public and its use in other projects is encouraged. The goal is to make Slic3r fully modular so that any part of its logic @@ -71,7 +71,7 @@ Sure! You can do the following to find things that are available to help with: * Items in the [TODO](https://github.com/alexrj/Slic3r/wiki/TODO) wiki page. * Please comment in the related github issue that you are working on it so that other people know. * Drop me a line at aar@cpan.org. -* You can also find me (rarely) in #reprap and in #slic3r on [FreeNode](http://webchat.freenode.net) with the nickname _Sound_. Another contributor, _LoH_, is also in both channels. +* You can also find me (rarely) in #reprap and in #slic3r on [FreeNode](https://webchat.freenode.net) with the nickname _Sound_. Another contributor, _LoH_, is also in both channels. * Add an [issue](https://github.com/alexrj/Slic3r/issues) to the github tracker if it isn't already present. Before sending patches and pull requests contact me (preferably through opening a github issue or commenting on an existing, related, issue) to discuss your proposed From d41e3c1ee4f14f8011dc034e57e54e719ae88fba Mon Sep 17 00:00:00 2001 From: Sebastianv650 Date: Thu, 26 Jan 2017 03:27:10 +0100 Subject: [PATCH 225/225] Pressure advance overrides print speeds patch (#3287) * Fix PressureRegulation.pm Check if a pressure advance is needed even if speed is unchanged, but a discharge happened before. Restore print speed after advance / discharge. Solves #3282 --- lib/Slic3r/GCode/PressureRegulator.pm | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/GCode/PressureRegulator.pm b/lib/Slic3r/GCode/PressureRegulator.pm index d03094ab2..a5bf6f61e 100644 --- a/lib/Slic3r/GCode/PressureRegulator.pm +++ b/lib/Slic3r/GCode/PressureRegulator.pm @@ -38,8 +38,8 @@ sub process { } elsif ($info->{extruding} && $info->{dist_XY} > 0) { # This is a print move. my $F = $args->{F} // $reader->F; - if ($F != $self->_last_print_F) { - # We are setting a (potentially) new speed, so we calculate the new advance amount. + if ($F != $self->_last_print_F || ($F == $self->_last_print_F && $self->_advance == 0)) { + # We are setting a (potentially) new speed or a discharge event happend since the last speed change, so we calculate the new advance amount. # First calculate relative flow rate (mm of filament over mm of travel) my $rel_flow_rate = $info->{dist_E} / $info->{dist_XY}; @@ -56,6 +56,7 @@ sub process { $self->_extrusion_axis, $new_E, $self->_unretract_speed; $new_gcode .= sprintf "G92 %s%.5f ; restore E\n", $self->_extrusion_axis, $reader->E if !$self->config->use_relative_e_distances; + $new_gcode .= sprintf "G1 F%.3f ; restore F\n", $F; $self->_advance($new_advance); } @@ -63,7 +64,7 @@ sub process { } } elsif (($info->{retracting} || $cmd eq 'G10') && $self->_advance != 0) { # We need to bring pressure to zero when retracting. - $new_gcode .= $self->_discharge($args->{F}); + $new_gcode .= $self->_discharge($args->{F}, $args->{F} // $reader->F); } $new_gcode .= "$info->{raw}\n"; @@ -77,13 +78,14 @@ sub process { } sub _discharge { - my ($self, $F) = @_; + my ($self, $F, $oldSpeed) = @_; my $new_E = ($self->config->use_relative_e_distances ? 0 : $self->reader->E) - $self->_advance; my $gcode = sprintf "G1 %s%.5f F%.3f ; pressure discharge\n", $self->_extrusion_axis, $new_E, $F // $self->_unretract_speed; $gcode .= sprintf "G92 %s%.5f ; restore E\n", $self->_extrusion_axis, $self->reader->E if !$self->config->use_relative_e_distances; + $gcode .= sprintf "G1 F%.3f ; restore F\n", $oldSpeed; $self->_advance(0); return $gcode;