From f6f81ea7511879e5b893ff152e831ccb5d507836 Mon Sep 17 00:00:00 2001 From: florens Date: Wed, 13 Aug 2014 19:07:20 +0200 Subject: [PATCH 01/64] added options for adaptive slicing --- lib/Slic3r.pm | 1 + lib/Slic3r/GUI/Tab.pm | 9 +++++++++ lib/Slic3r/Print/Object.pm | 7 ++++++- xs/src/libslic3r/PrintConfig.cpp | 13 +++++++++++++ xs/src/libslic3r/PrintConfig.hpp | 6 ++++++ xs/src/libslic3r/PrintObject.cpp | 4 +++- 6 files changed, 38 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 25db38c30..7ed5e3dd7 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -36,6 +36,7 @@ use Encode::Locale; use Moo 1.003001; use Slic3r::XS; # import all symbols (constants etc.) before they get parsed +use Slic3r::AdaptiveSlicing; use Slic3r::Config; use Slic3r::ExPolygon; use Slic3r::Extruder; diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 65d7dad6f..c7d4c11db 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -416,6 +416,7 @@ sub build { $self->init_config_options(qw( layer_height first_layer_height + adaptive_slicing cusp_value perimeters spiral_vase top_solid_layers bottom_solid_layers extra_perimeters avoid_crossing_perimeters thin_walls overhangs @@ -458,6 +459,8 @@ sub build { my $optgroup = $page->new_optgroup('Layer height'); $optgroup->append_single_option_line('layer_height'); $optgroup->append_single_option_line('first_layer_height'); + $optgroup->append_single_option_line('adaptive_slicing'); + $optgroup->append_single_option_line('cusp_value'); } { my $optgroup = $page->new_optgroup('Vertical shells'); @@ -694,6 +697,12 @@ sub _update { my $have_perimeters = $config->perimeters > 0; $self->get_field($_)->toggle($have_perimeters) for qw(extra_perimeters thin_walls overhangs seam_position external_perimeters_first); + + my $have_adaptive_slicing = $config->adaptive_slicing; + $self->get_field($_)->toggle($have_adaptive_slicing) + for qw(cusp_value); + $self->get_field($_)->toggle(!$have_adaptive_slicing) + for qw(layer_height); my $have_infill = $config->fill_density > 0; $self->get_field($_)->toggle($have_infill) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index a1aaff307..8cd5440af 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -107,7 +107,7 @@ sub slice { $self->clear_layers; # make layers taking custom heights into account - my $print_z = my $slice_z = my $height = my $id = 0; + my $print_z = my $slice_z = my $height = my $cusp_height = my $id = 0; my $first_object_layer_height = -1; my $first_object_layer_distance = -1; @@ -134,6 +134,11 @@ sub slice { # loop until we have at least one layer and the max slice_z reaches the object height my $max_z = unscale($self->size->z); while (($slice_z - $height) <= $max_z) { + + if ($self->config->adaptive_slicing) { + #dummy + } + # assign the default height to the layer according to the general settings $height = ($id == 0) ? $self->config->get_value('first_layer_height') diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 2e9d8a6bf..567acb11d 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -6,6 +6,11 @@ t_optiondef_map PrintConfigDef::build_def() { t_optiondef_map Options; + Options["adaptive_slicing"].type = coBool; + Options["adaptive_slicing"].label = "Use adaptive slicing"; + Options["adaptive_slicing"].tooltip = "Automatically determine layer heights by the objects topology instead of using the static value."; + Options["adaptive_slicing"].cli = "adaptive-slicing!"; + Options["avoid_crossing_perimeters"].type = coBool; Options["avoid_crossing_perimeters"].label = "Avoid crossing perimeters"; Options["avoid_crossing_perimeters"].tooltip = "Optimize travel moves in order to minimize the crossing of perimeters. This is mostly useful with Bowden extruders which suffer from oozing. This feature slows down both the print and the G-code generation."; @@ -78,6 +83,14 @@ PrintConfigDef::build_def() { Options["cooling"].tooltip = "This flag enables the automatic cooling logic that adjusts print speed and fan speed according to layer printing time."; Options["cooling"].cli = "cooling!"; + Options["cusp_value"].type = coFloat; + Options["cusp_value"].label = "Cusp value"; + Options["cusp_value"].tooltip = "This value determines the maximum deviaton from the objects original surface caused by the stair-stepping effect (approximation of the surface by discrete layers). Use a value between 0 (highest possible resolution) and [max_layer_height] (lowest possible resolution). Typical values are 0.1 - 0.2."; + Options["cusp_value"].sidetext = "mm"; + Options["cusp_value"].cli = "cusp_value=f"; + Options["cusp_value"].min = 0; + Options["cusp_value"].max = 1; + Options["default_acceleration"].type = coFloat; Options["default_acceleration"].label = "Default"; Options["default_acceleration"].tooltip = "This is the acceleration your printer will be reset to after the role-specific acceleration values are used (perimeter/infill). Set zero to prevent resetting acceleration at all."; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index cdc3d7173..160acd4d0 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -114,6 +114,8 @@ class StaticPrintConfig : public virtual StaticConfig class PrintObjectConfig : public virtual StaticPrintConfig { public: + ConfigOptionBool adaptive_slicing; + ConfigOptionFloat cusp_value; ConfigOptionBool dont_support_bridges; ConfigOptionFloatOrPercent extrusion_width; ConfigOptionFloatOrPercent first_layer_height; @@ -138,6 +140,8 @@ class PrintObjectConfig : public virtual StaticPrintConfig ConfigOptionFloat xy_size_compensation; PrintObjectConfig() : StaticPrintConfig() { + this->adaptive_slicing.value = false; + this->cusp_value.value = 0.15; this->dont_support_bridges.value = true; this->extrusion_width.value = 0; this->extrusion_width.percent = false; @@ -167,6 +171,8 @@ class PrintObjectConfig : public virtual StaticPrintConfig }; ConfigOption* option(const t_config_option_key opt_key, bool create = false) { + if (opt_key == "adaptive_slicing") return &this->adaptive_slicing; + if (opt_key == "cusp_value") return &this->cusp_value; if (opt_key == "dont_support_bridges") return &this->dont_support_bridges; if (opt_key == "extrusion_width") return &this->extrusion_width; if (opt_key == "first_layer_height") return &this->first_layer_height; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index d89686327..d561dc6aa 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -145,7 +145,9 @@ PrintObject::invalidate_state_by_config_options(const std::vector Date: Wed, 13 Aug 2014 19:09:58 +0200 Subject: [PATCH 02/64] created stump of the AdaptiveSlicing class that will provide the cusp computations --- lib/Slic3r/AdaptiveSlicing.pm | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 lib/Slic3r/AdaptiveSlicing.pm diff --git a/lib/Slic3r/AdaptiveSlicing.pm b/lib/Slic3r/AdaptiveSlicing.pm new file mode 100644 index 000000000..037754777 --- /dev/null +++ b/lib/Slic3r/AdaptiveSlicing.pm @@ -0,0 +1,53 @@ +package Slic3r::AdaptiveSlicing; +use Moo; + +use List::Util qw(min max); +use Slic3r::Geometry qw(X Y Z triangle_normal scale unscale); + +#private +has 'normal' => (is => 'ro', default => sub { [] }); # facet_id => [normal]; +has 'ordered_facets' => (is => 'ro', default => sub { [] }); # id => [facet_id, min_z, max_z]; +has 'current_facet' => (is => 'rw'); + +sub BUILD { + my $self = shift; + + my $facet_id = 0; + my $facets = $self->mesh->facets; + my $vertices = $self->mesh->vertices; + + # generate facet normals + foreach my $facet (@{$facets}) { + my $normal = triangle_normal(map $vertices->[$_], @$facet[-3..-1]); + # normalize length + my $normal_length = sqrt($normal->[0]**2 + $normal->[1]**2 + $normal->[2]**2); + + if($normal_length > 0) { + $self->normal->[$facet_id] = [ map $normal->[$_]/$normal_length, (X,Y,Z) ]; + }else{ # facet with area = 0 + $self->normal->[$facet_id] = [0 ,0 ,0]; + } + $facet_id++; + } + + # generate a list of facet_ids, containing maximum and minimum Z-Value of the facet, ordered by minimum Z + my @sort_facets; + + for ($facet_id = 0; $facet_id <= $#{$facets}; $facet_id++) { + my $a = $vertices->[$facets->[$facet_id]->[0]]->[Z]; + my $b = $vertices->[$facets->[$facet_id]->[1]]->[Z]; + my $c = $vertices->[$facets->[$facet_id]->[2]]->[Z]; + my $min_z = min($a, $b, $c); + my $max_z = max($a, $b, $c); + push @sort_facets, [$facet_id, $min_z, $max_z]; + } + @sort_facets = sort {$a->[1] <=> $b->[1]} @sort_facets; + for (my $i = 0; $i <= $#sort_facets; $i++) { + $self->ordered_facets->[$i] = $sort_facets[$i]; + } + # initialize pointer for cusp_height run + $self->current_facet(0); +} + + +1; \ No newline at end of file From 078f3af9c2d539b1a3fd122557fdd58073d6fa40 Mon Sep 17 00:00:00 2001 From: florens Date: Thu, 14 Aug 2014 16:36:35 +0200 Subject: [PATCH 03/64] added min and max_layer_height options to extruder to be used as limits for adaptive layer heights --- lib/Slic3r/GUI/Tab.pm | 7 ++++++- xs/src/libslic3r/PrintConfig.cpp | 12 ++++++++++++ xs/src/libslic3r/PrintConfig.hpp | 8 ++++++++ xs/src/libslic3r/PrintObject.cpp | 2 ++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index c7d4c11db..32e12a655 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -1013,7 +1013,7 @@ sub build { $self->_build_extruder_pages; } -sub _extruder_options { qw(nozzle_diameter extruder_offset retract_length retract_lift retract_speed retract_restart_extra retract_before_travel wipe +sub _extruder_options { qw(nozzle_diameter min_layer_height max_layer_height extruder_offset retract_length retract_lift retract_speed retract_restart_extra retract_before_travel wipe retract_layer_change retract_length_toolchange retract_restart_extra_toolchange) } sub _build_extruder_pages { @@ -1042,6 +1042,11 @@ sub _build_extruder_pages { my $optgroup = $page->new_optgroup('Size'); $optgroup->append_single_option_line('nozzle_diameter', $extruder_idx); } + { + my $optgroup = $page->new_optgroup('Limits'); + $optgroup->append_single_option_line($_, $extruder_idx) + for qw(min_layer_height max_layer_height); + } { my $optgroup = $page->new_optgroup('Position (for multi-extruder printers)'); $optgroup->append_single_option_line('extruder_offset', $extruder_idx); diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 567acb11d..8efa4399b 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -461,6 +461,12 @@ PrintConfigDef::build_def() { Options["max_fan_speed"].min = 0; Options["max_fan_speed"].max = 100; + Options["max_layer_height"].type = coFloats; + Options["max_layer_height"].label = "max"; + Options["max_layer_height"].tooltip = "This is the highest printable layer height for this extruder and limits the resolution for adaptive slicing. Typical values are slightly smaller than nozzle_diameter."; + Options["max_layer_height"].sidetext = "mm"; + Options["max_layer_height"].cli = "max-layer-height=f@"; + Options["min_fan_speed"].type = coInt; Options["min_fan_speed"].label = "Min"; Options["min_fan_speed"].tooltip = "This setting represents the minimum PWM your fan needs to work."; @@ -469,6 +475,12 @@ PrintConfigDef::build_def() { Options["min_fan_speed"].min = 0; Options["min_fan_speed"].max = 100; + Options["min_layer_height"].type = coFloats; + Options["min_layer_height"].label = "min"; + Options["min_layer_height"].tooltip = "TThis is the lowest printable layer height for this extruder and limits the resolution for adaptive slicing. Typical values are 0.1 or 0.05."; + Options["min_layer_height"].sidetext = "mm"; + Options["min_layer_height"].cli = "min-layer-height=f@"; + Options["min_print_speed"].type = coInt; Options["min_print_speed"].label = "Min print speed"; Options["min_print_speed"].tooltip = "Slic3r will not scale speed down below this speed."; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 160acd4d0..03e658597 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -348,7 +348,9 @@ class PrintConfig : public virtual StaticPrintConfig ConfigOptionBool infill_first; ConfigOptionString layer_gcode; ConfigOptionInt max_fan_speed; + ConfigOptionFloats max_layer_height; ConfigOptionInt min_fan_speed; + ConfigOptionFloats min_layer_height; ConfigOptionInt min_print_speed; ConfigOptionFloat min_skirt_length; ConfigOptionString notes; @@ -427,7 +429,11 @@ class PrintConfig : public virtual StaticPrintConfig this->infill_first.value = false; this->layer_gcode.value = ""; this->max_fan_speed.value = 100; + this->max_layer_height.values.resize(1); + this->max_layer_height.values[0] = 0.3; this->min_fan_speed.value = 35; + this->min_layer_height.values.resize(1); + this->min_layer_height.values[0] = 0.1; this->min_print_speed.value = 10; this->min_skirt_length.value = 0; this->notes.value = ""; @@ -508,7 +514,9 @@ class PrintConfig : public virtual StaticPrintConfig if (opt_key == "infill_first") return &this->infill_first; if (opt_key == "layer_gcode") return &this->layer_gcode; if (opt_key == "max_fan_speed") return &this->max_fan_speed; + if (opt_key == "max_layer_height") return &this->max_layer_height; if (opt_key == "min_fan_speed") return &this->min_fan_speed; + if (opt_key == "min_layer_height") return &this->min_layer_height; if (opt_key == "min_print_speed") return &this->min_print_speed; if (opt_key == "min_skirt_length") return &this->min_skirt_length; if (opt_key == "notes") return &this->notes; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index d561dc6aa..d210e821b 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -143,6 +143,8 @@ PrintObject::invalidate_state_by_config_options(const std::vector Date: Fri, 15 Aug 2014 18:21:28 +0200 Subject: [PATCH 04/64] implemented core adaptive slicing algorithm --- lib/Slic3r/AdaptiveSlicing.pm | 127 +++++++++++++++++++++++++++++++--- lib/Slic3r/Print/Object.pm | 58 ++++++++++++++-- 2 files changed, 170 insertions(+), 15 deletions(-) diff --git a/lib/Slic3r/AdaptiveSlicing.pm b/lib/Slic3r/AdaptiveSlicing.pm index 037754777..bc18a9ed6 100644 --- a/lib/Slic3r/AdaptiveSlicing.pm +++ b/lib/Slic3r/AdaptiveSlicing.pm @@ -4,8 +4,11 @@ use Moo; use List::Util qw(min max); use Slic3r::Geometry qw(X Y Z triangle_normal scale unscale); +# public +has 'mesh' => (is => 'ro', required => 1); + #private -has 'normal' => (is => 'ro', default => sub { [] }); # facet_id => [normal]; +has 'normal_z' => (is => 'ro', default => sub { [] }); # facet_id => [normal]; has 'ordered_facets' => (is => 'ro', default => sub { [] }); # id => [facet_id, min_z, max_z]; has 'current_facet' => (is => 'rw'); @@ -15,20 +18,22 @@ sub BUILD { my $facet_id = 0; my $facets = $self->mesh->facets; my $vertices = $self->mesh->vertices; + my $normals = $self->mesh->normals; + + # generate facet normals - foreach my $facet (@{$facets}) { - my $normal = triangle_normal(map $vertices->[$_], @$facet[-3..-1]); - # normalize length + for ($facet_id = 0; $facet_id <= $#{$facets}; $facet_id++) { + my $normal = $normals->[$facet_id]; my $normal_length = sqrt($normal->[0]**2 + $normal->[1]**2 + $normal->[2]**2); if($normal_length > 0) { - $self->normal->[$facet_id] = [ map $normal->[$_]/$normal_length, (X,Y,Z) ]; + $self->normal_z->[$facet_id] = $normal->[Z]/$normal_length; }else{ # facet with area = 0 - $self->normal->[$facet_id] = [0 ,0 ,0]; + $self->normal_z->[$facet_id] = [0 ,0 ,0]; } - $facet_id++; - } + } + # generate a list of facet_ids, containing maximum and minimum Z-Value of the facet, ordered by minimum Z my @sort_facets; @@ -39,7 +44,7 @@ sub BUILD { my $c = $vertices->[$facets->[$facet_id]->[2]]->[Z]; my $min_z = min($a, $b, $c); my $max_z = max($a, $b, $c); - push @sort_facets, [$facet_id, $min_z, $max_z]; + push @sort_facets, [$facet_id, scale $min_z, scale $max_z]; } @sort_facets = sort {$a->[1] <=> $b->[1]} @sort_facets; for (my $i = 0; $i <= $#sort_facets; $i++) { @@ -50,4 +55,108 @@ sub BUILD { } +sub cusp_height { + my $self = shift; + my ($z, $cusp_value, $min_height, $max_height) = @_; + + my $height = $max_height; + my $first_hit = 0; + + # find all facets intersecting the slice-layer + my $ordered_id = $self->current_facet; + while ($ordered_id <= $#{$self->ordered_facets}) { + + # facet's minimum is higher than slice_z -> end loop + if($self->ordered_facets->[$ordered_id]->[1] >= $z) { + last; + } + + # facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point + if($self->ordered_facets->[$ordered_id]->[2] > $z) { + # first event? + if(!$first_hit) { + $first_hit = 1; + $self->current_facet($ordered_id); + } + + #skip touching facets which could otherwise cause small cusp values + if($self->ordered_facets->[$ordered_id]->[2] <= $z+1) + { + $ordered_id++; + next; + } + + # compute cusp-height for this facet and store minimum of all heights + my $cusp = $self->_facet_cusp_height($ordered_id, $cusp_value); + $height = $cusp if($cusp < $height); + } + $ordered_id++; + } + # lower height limit due to printer capabilities + $height = $min_height if($height < $min_height); + + + # check for sloped facets inside the determined layer and correct height if necessary + if($height > $min_height){ + while ($ordered_id <= $#{$self->ordered_facets}) { + + # facet's minimum is higher than slice_z + height -> end loop + if($self->ordered_facets->[$ordered_id]->[1] >= ($z + scale $height)) { + last; + } + + #skip touching facets which could otherwise cause small cusp values + if($self->ordered_facets->[$ordered_id]->[2] <= $z+1) + { + $ordered_id++; + next; + } + + # Compute cusp-height for this facet and check against height. + my $cusp = $self->_facet_cusp_height($ordered_id, $cusp_value); + + my $z_diff = unscale ($self->ordered_facets->[$ordered_id]->[1] - $z); + + + # handle horizontal facets + if ($self->normal_z->[$self->ordered_facets->[$ordered_id]->[0]] > 0.999) { + Slic3r::debugf "cusp computation, height is reduced from %f", $height; + $height = $z_diff; + Slic3r::debugf "to %f due to near horizontal facet\n", $height; + }else{ + if( $cusp > $z_diff) { + if($cusp < $height) { + Slic3r::debugf "cusp computation, height is reduced from %f", $height; + $height = $cusp; + Slic3r::debugf "to %f due to new cusp height\n", $height; + } + }else{ + Slic3r::debugf "cusp computation, height is reduced from %f", $height; + $height = $z_diff; + Slic3r::debugf "to z-diff: %f\n", $height; + } + } + + $ordered_id++; + } + # lower height limit due to printer capabilities again + $height = $min_height if($height < $min_height); + } + + Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height; + + return $height; +} + +# computes the cusp height from a given facets normal and the cusp_value +sub _facet_cusp_height { + my $self = shift; + my ($ordered_id, $cusp_value) = @_; + + my $normal_z = $self->normal_z->[$self->ordered_facets->[$ordered_id]->[0]]; + my $cusp = ($normal_z == 0) ? 9999 : abs($cusp_value/$normal_z); + return $cusp; +} + + 1; \ No newline at end of file diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 8cd5440af..d0000e872 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -130,19 +130,65 @@ sub slice { $first_object_layer_height = $nozzle_diameter; $first_object_layer_distance = $distance; } + + # create stateful objects and variables for the adaptive slicing process + my @adaptive_slicing; + my $min_height = 0; + my $max_height = 0; + if ($self->config->adaptive_slicing) { + for my $region_id (0 .. ($self->region_count - 1)) { + my $mesh; + foreach my $volume_id (@{ $self->get_region_volumes($region_id) }) { + my $volume = $self->model_object->volumes->[$volume_id]; + next if $volume->modifier; + if (defined $mesh) { + $mesh->merge($volume->mesh); + } else { + $mesh = $volume->mesh->clone; + } + } + + $adaptive_slicing[$region_id] = Slic3r::AdaptiveSlicing->new( + mesh => $mesh + ); + } + + # determine min and max layer height from perimeter extruder capabilities. + if($self->region_count > 1) { # multimaterial object + $min_height = max(map {$self->print->config->get_at('min_layer_height', $_)} (0..($self->region_count-1))); + $max_height = min(map {$self->print->config->get_at('max_layer_height', $_)} (0..($self->region_count-1))); + }else{ #single material object + my $perimeter_extruder = $self->print->get_region(0)->config->get('perimeter_extruder')-1; + $min_height = $self->print->config->get_at('min_layer_height', $perimeter_extruder); + $max_height = $self->print->config->get_at('max_layer_height', $perimeter_extruder); + } + } # loop until we have at least one layer and the max slice_z reaches the object height my $max_z = unscale($self->size->z); while (($slice_z - $height) <= $max_z) { if ($self->config->adaptive_slicing) { - #dummy + my $cusp_value = $self->config->get_value('cusp_value'); + + Slic3r::debugf "\n Slice layer: %d\n", $id; + + # determine next layer height + for my $region_id (0 .. ($self->region_count - 1)) { + # get cusp height + my $cusp_height = $adaptive_slicing[$region_id]->cusp_height(scale $slice_z, $cusp_value, $min_height, $max_height); + + $height = ($id == 0) + ? $self->config->get_value('first_layer_height') + : $cusp_height; + } + + }else{ + # assign the default height to the layer according to the general settings + $height = ($id == 0) + ? $self->config->get_value('first_layer_height') + : $self->config->layer_height; } - - # assign the default height to the layer according to the general settings - $height = ($id == 0) - ? $self->config->get_value('first_layer_height') - : $self->config->layer_height; # look for an applicable custom range if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) { From 3930a19bb9e38e303a52a62a599c081a9fe374ab Mon Sep 17 00:00:00 2001 From: florens Date: Fri, 15 Aug 2014 19:26:47 +0200 Subject: [PATCH 05/64] added basic test cases for adaptive slicing algorithm --- lib/Slic3r/Test.pm | 10 ++++++ t/adaptive_slicing.t | 86 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 t/adaptive_slicing.t diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index 2a8d4039b..04a22ade8 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -111,6 +111,16 @@ sub mesh { $facets = [ [0,1,2],[3,4,5],[2,1,4],[2,4,3],[2,3,5],[2,5,0],[5,4,1],[5,1,0] ]; + } elsif ($name eq 'slopy_cube') { + $vertices = [ + [-10,-10,0], [-10,-10,20], [-10,10,0], [-10,10,20], [0,-10,10], [10,-10,0], [2.92893,-10,10], [10,-10,2.92893], + [0,-10,20], [10,10,0], [0,10,10], [0,10,20], [2.92893,10,10], [10,10,2.92893], + ]; + $facets = [ + [0,1,2], [2,1,3], [4,0,5], [4,1,0], [6,4,7], [7,4,5], [4,8,1], [0,2,5], [5,2,9], [2,10,9], [10,3,11], + [2,3,10], [9,10,12], [13,9,12], [3,1,8], [11,3,8], [10,11,8], [4,10,8], [6,12,10], [4,6,10], + [7,13,12], [6,7,12], [7,5,9], [13,7,9], + ]; } else { return undef; } diff --git a/t/adaptive_slicing.t b/t/adaptive_slicing.t new file mode 100644 index 000000000..9018d83f4 --- /dev/null +++ b/t/adaptive_slicing.t @@ -0,0 +1,86 @@ +use Test::More tests => 5; +use strict; +use warnings; + +BEGIN { + use FindBin; + use lib "$FindBin::Bin/../lib"; +} + +use List::Util qw(first); +use Slic3r; +use Slic3r::Test qw(_eq); +use Slic3r::Geometry qw(Z PI scale unscale); + +my $config = Slic3r::Config->new_from_defaults; + +my $test = sub { + my ($conf) = @_; + $conf ||= $config; + + my $print = Slic3r::Test::init_print('slopy_cube', config => $conf); + + my @z = (); + my @increments = (); + Slic3r::GCode::Reader->new->parse(Slic3r::Test::gcode($print), sub { + my ($self, $cmd, $args, $info) = @_; + + if ($info->{dist_Z}) { + push @z, 1*$args->{Z}; + push @increments, $info->{dist_Z}; + } + }); + + ok (_eq($z[0], $config->get_value('first_layer_height') + $config->z_offset), 'first layer height.'); + + ok (_eq($z[1], $config->get_value('first_layer_height') + $config->get('max_layer_height')->[0] + $config->z_offset), 'second layer height.'); + + cmp_ok((first { _eq($_, 10.0) } @z[1..$#z]), '>', 0, 'horizontal facet matched'); + + 1; +}; + + + + +my $print = Slic3r::Test::init_print('slopy_cube', config => $config); +$print->models->[0]->mesh->repair(); +my $adaptive_slicing = Slic3r::AdaptiveSlicing->new( + mesh => Slic3r::Test::mesh('slopy_cube') +); + + +subtest 'max cusp_height limited by extruder capabilities' => sub { + plan tests => 3; + + is ($adaptive_slicing->cusp_height(scale 1, 0.2, 0.1, 0.15), 0.15, 'low'); + is ($adaptive_slicing->cusp_height(scale 1, 0.2, 0.1, 0.4), 0.4, 'higher'); + is ($adaptive_slicing->cusp_height(scale 1, 0.2, 0.1, 0.65), 0.65, 'highest'); +}; + +subtest 'min cusp_height limited by extruder capabilities' => sub { + plan tests => 3; + + is ($adaptive_slicing->cusp_height(scale 4, 0.01, 0.1, 0.15), 0.1, 'low'); + is ($adaptive_slicing->cusp_height(scale 4, 0.02, 0.2, 0.4), 0.2, 'higher'); + is ($adaptive_slicing->cusp_height(scale 4, 0.01, 0.3, 0.65), 0.3, 'highest'); +}; + +subtest 'correct cusp_height depending on the facet normals' => sub { + plan tests => 3; + + ok (_eq($adaptive_slicing->cusp_height(scale 1, 0.1, 0.1, 0.5), 0.5), 'limit'); + ok (_eq($adaptive_slicing->cusp_height(scale 4, 0.1, 0.1, 0.5), 0.1414), '45deg facet, cusp_value: 0.1'); + ok (_eq($adaptive_slicing->cusp_height(scale 4, 0.15, 0.1, 0.5), 0.2121), '45deg facet, cusp_value: 0.15'); +}; + + +# 2.92893 ist lower slope edge +# distance to slope must be higher than min extruder cap. +# slopes cusp height must be greater than the distance to the slope +ok (_eq($adaptive_slicing->cusp_height(scale 2.798, 0.1, 0.1, 0.5), 0.1414), 'reducing cusp_height due to higher slopy facet'); + +# slopes cusp height must be smaller than the distance to the slope +ok (_eq($adaptive_slicing->cusp_height(scale 2.6289, 0.15, 0.1, 0.5), 0.3), 'reducing cusp_height to z-diff'); + +__END__ From 5bb1ffba0dd63bde24faf8edf43fd2e4f20b2c48 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Mon, 6 Jun 2016 14:32:27 +0200 Subject: [PATCH 06/64] Feature: try to match horizontal surfaces with adaptive slicing --- lib/Slic3r/AdaptiveSlicing.pm | 33 ++++++++++++++++++++++++++++++++ lib/Slic3r/GUI/Tab.pm | 5 +++-- lib/Slic3r/Print/Object.pm | 21 +++++++++++++++++++- t/adaptive_slicing.t | 3 ++- xs/src/libslic3r/PrintConfig.cpp | 10 ++++++++-- xs/src/libslic3r/PrintConfig.hpp | 2 ++ xs/src/libslic3r/PrintObject.cpp | 1 + 7 files changed, 69 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/AdaptiveSlicing.pm b/lib/Slic3r/AdaptiveSlicing.pm index bc18a9ed6..da624edac 100644 --- a/lib/Slic3r/AdaptiveSlicing.pm +++ b/lib/Slic3r/AdaptiveSlicing.pm @@ -6,6 +6,7 @@ use Slic3r::Geometry qw(X Y Z triangle_normal scale unscale); # public has 'mesh' => (is => 'ro', required => 1); +has 'size' => (is => 'ro', required => 1); #private has 'normal_z' => (is => 'ro', default => sub { [] }); # facet_id => [normal]; @@ -158,5 +159,37 @@ sub _facet_cusp_height { return $cusp; } +# Returns the distance to the next horizontal facet in Z-dir +# to consider horizontal object features in slice thickness +sub horizontal_facet_distance { + my $self = shift; + my ($z, $max_height) = @_; + $max_height = scale $max_height; + + my $ordered_id = $self->current_facet; + while ($ordered_id <= $#{$self->ordered_facets}) { + + # facet's minimum is higher than max forward distance -> end loop + if($self->ordered_facets->[$ordered_id]->[1] > $z+$max_height) { + last; + } + + # min_z == max_z -> horizontal facet + if($self->ordered_facets->[$ordered_id]->[1] > $z) { + if($self->ordered_facets->[$ordered_id]->[1] == $self->ordered_facets->[$ordered_id]->[2]) { + return unscale $self->ordered_facets->[$ordered_id]->[1] - $z; + } + } + + $ordered_id++; + } + + # objects maximum? + if($z + $max_height > $self->size) { + return max(unscale $self->size - $z, 0); + } + return unscale $max_height; +} + 1; \ No newline at end of file diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index 7325cb5ec..0b13c1e53 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -455,7 +455,7 @@ sub build { my $self = shift; $self->init_config_options(qw( - adaptive_slicing cusp_value + adaptive_slicing cusp_value match_horizontal_surfaces layer_height first_layer_height perimeters spiral_vase top_solid_layers bottom_solid_layers @@ -504,6 +504,7 @@ sub build { $optgroup->append_single_option_line('first_layer_height'); $optgroup->append_single_option_line('adaptive_slicing'); $optgroup->append_single_option_line('cusp_value'); + $optgroup->append_single_option_line('match_horizontal_surfaces'); } { my $optgroup = $page->new_optgroup('Vertical shells'); @@ -787,7 +788,7 @@ sub _update { my $have_adaptive_slicing = $config->adaptive_slicing; $self->get_field($_)->toggle($have_adaptive_slicing) - for qw(cusp_value); + for qw(cusp_value match_horizontal_surfaces); $self->get_field($_)->toggle(!$have_adaptive_slicing) for qw(layer_height); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 2fa90b4c6..a492b16be 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -108,7 +108,8 @@ sub slice { } $adaptive_slicing[$region_id] = Slic3r::AdaptiveSlicing->new( - mesh => $mesh + mesh => $mesh, + size => $self->size->z ); } @@ -136,6 +137,24 @@ sub slice { for my $region_id (0 .. ($self->region_count - 1)) { # get cusp height my $cusp_height = $adaptive_slicing[$region_id]->cusp_height(scale $slice_z, $cusp_value, $min_height, $max_height); + + # check for horizontal features and object size + if($self->config->get_value('match_horizontal_surfaces')) { + my $horizontal_dist = $adaptive_slicing[$region_id]->horizontal_facet_distance(scale $slice_z+$cusp_height, $min_height); + if(($horizontal_dist < $min_height) && ($horizontal_dist > 0)) { + Slic3r::debugf "Horizontal feature ahead, distance: %f\n", $horizontal_dist; + # can we shrink the current layer a bit? + if($cusp_height-($min_height-$horizontal_dist) > $min_height) { + # yes we can + $cusp_height = $cusp_height-($min_height-$horizontal_dist); + Slic3r::debugf "Shrink layer height to %f\n", $cusp_height; + }else{ + # no, current layer would become too thin + $cusp_height = $cusp_height+$horizontal_dist; + Slic3r::debugf "Widen layer height to %f\n", $cusp_height; + } + } + } $height = ($id == 0) ? $self->config->get_value('first_layer_height') diff --git a/t/adaptive_slicing.t b/t/adaptive_slicing.t index 9018d83f4..95c7df4e0 100644 --- a/t/adaptive_slicing.t +++ b/t/adaptive_slicing.t @@ -46,7 +46,8 @@ my $test = sub { my $print = Slic3r::Test::init_print('slopy_cube', config => $config); $print->models->[0]->mesh->repair(); my $adaptive_slicing = Slic3r::AdaptiveSlicing->new( - mesh => Slic3r::Test::mesh('slopy_cube') + mesh => Slic3r::Test::mesh('slopy_cube'), + size => 20 ); diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index cb52955a8..533f24dc6 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -585,6 +585,12 @@ PrintConfigDef::PrintConfigDef() def->min = 0; def->default_value = new ConfigOptionFloat(0.3); + def = this->add("match_horizontal_surfaces", coBool); + def->label = "Match horizontal surfaces"; + def->tooltip = "Try to match horizontal surfaces during the slicing process. Matching is not guaranteed, very small surfaces and multiple surfaces with low vertical distance might cause bad results."; + def->cli = "match-horizontal-surfaces!"; + def->default_value = new ConfigOptionBool(true); + def = this->add("max_fan_speed", coInt); def->label = "Max"; def->tooltip = "This setting represents the maximum speed of your fan."; @@ -599,7 +605,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "This is the highest printable layer height for this extruder and limits the resolution for adaptive slicing. Typical values are slightly smaller than nozzle_diameter."; def->sidetext = "mm"; def->cli = "max-layer-height=f@"; - //def->min = 0; + def->min = 0; { ConfigOptionFloats* opt = new ConfigOptionFloats(); opt->values.push_back(0.3); @@ -636,7 +642,7 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "This is the lowest printable layer height for this extruder and limits the resolution for adaptive slicing. Typical values are 0.1 or 0.05."; def->sidetext = "mm"; def->cli = "min-layer-height=f@"; - //def->min = 0; + def->min = 0; { ConfigOptionFloats* opt = new ConfigOptionFloats(); opt->values.push_back(0.15); diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 2521680a1..ed7a4fe5c 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -110,6 +110,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig ConfigOptionBool infill_only_where_needed; ConfigOptionBool interface_shells; ConfigOptionFloat layer_height; + ConfigOptionBool match_horizontal_surfaces; ConfigOptionInt raft_layers; ConfigOptionEnum seam_position; ConfigOptionBool support_material; @@ -141,6 +142,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig OPT_PTR(infill_only_where_needed); OPT_PTR(interface_shells); OPT_PTR(layer_height); + OPT_PTR(match_horizontal_surfaces); OPT_PTR(raft_layers); OPT_PTR(seam_position); OPT_PTR(support_material); diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index fa5d3705c..eb744985e 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -229,6 +229,7 @@ PrintObject::invalidate_state_by_config_options(const std::vector Date: Mon, 13 Jun 2016 15:23:03 +0200 Subject: [PATCH 07/64] catch empty region_volumes for adaptive_slicing layer generation --- lib/Slic3r/Print/Object.pm | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index a492b16be..2baa1c21a 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -107,10 +107,12 @@ sub slice { } } - $adaptive_slicing[$region_id] = Slic3r::AdaptiveSlicing->new( - mesh => $mesh, - size => $self->size->z - ); + if (defined $mesh) { + $adaptive_slicing[$region_id] = Slic3r::AdaptiveSlicing->new( + mesh => $mesh, + size => $self->size->z + ); + } } # determine min and max layer height from perimeter extruder capabilities. @@ -129,6 +131,7 @@ sub slice { while (($slice_z - $height) <= $max_z) { if ($self->config->adaptive_slicing) { + $height = 999; my $cusp_value = $self->config->get_value('cusp_value'); Slic3r::debugf "\n Slice layer: %d\n", $id; @@ -136,6 +139,7 @@ sub slice { # determine next layer height for my $region_id (0 .. ($self->region_count - 1)) { # get cusp height + next if(!defined $adaptive_slicing[$region_id]); my $cusp_height = $adaptive_slicing[$region_id]->cusp_height(scale $slice_z, $cusp_value, $min_height, $max_height); # check for horizontal features and object size @@ -158,7 +162,7 @@ sub slice { $height = ($id == 0) ? $self->config->get_value('first_layer_height') - : $cusp_height; + : min($cusp_height, $height); } }else{ From 99b830809cd628d39ce368c588457401fca43a40 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 9 Nov 2016 17:09:05 +0100 Subject: [PATCH 08/64] option to control layer height gradation for adaptive slicing --- lib/Slic3r/GUI/Tab.pm | 5 +++-- lib/Slic3r/Print/Object.pm | 6 ++++++ xs/src/libslic3r/PrintConfig.cpp | 8 ++++++++ xs/src/libslic3r/PrintConfig.hpp | 2 ++ xs/src/libslic3r/PrintObject.cpp | 1 + 5 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index e64e48d40..7e6214924 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -455,7 +455,7 @@ sub build { my $self = shift; $self->init_config_options(qw( - adaptive_slicing cusp_value match_horizontal_surfaces + adaptive_slicing adaptive_slicing_z_gradation cusp_value match_horizontal_surfaces layer_height first_layer_height perimeters spiral_vase top_solid_layers bottom_solid_layers @@ -504,6 +504,7 @@ sub build { $optgroup->append_single_option_line('first_layer_height'); $optgroup->append_single_option_line('adaptive_slicing'); $optgroup->append_single_option_line('cusp_value'); + $optgroup->append_single_option_line('adaptive_slicing_z_gradation'); $optgroup->append_single_option_line('match_horizontal_surfaces'); } { @@ -790,7 +791,7 @@ sub _update { my $have_adaptive_slicing = $config->adaptive_slicing; $self->get_field($_)->toggle($have_adaptive_slicing) - for qw(cusp_value match_horizontal_surfaces); + for qw(cusp_value adaptive_slicing_z_gradation match_horizontal_surfaces); $self->get_field($_)->toggle(!$have_adaptive_slicing) for qw(layer_height); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 66e156ddc..711d5cc12 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -163,6 +163,12 @@ sub slice { $height = ($id == 0) ? $self->config->get_value('first_layer_height') : min($cusp_height, $height); + + # apply z-gradation + my $gradation = $self->config->get_value('adaptive_slicing_z_gradation'); + if($gradation > 0) { + $height = $height - unscale((scale($height)) % (scale($gradation))); + } } }else{ diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 81bfcc080..d2d0aed52 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -14,6 +14,14 @@ PrintConfigDef::PrintConfigDef() def->cli = "adaptive-slicing!"; def->default_value = new ConfigOptionBool(false); + def = this->add("adaptive_slicing_z_gradation", coFloat); + def->label = "Min layer height gradation"; + def->tooltip = "Limit layer heights to a multiple of this value to avoid stepping inaccuracies at the Z-axis. Typical value for a Prusa i3, 1/16 micro-stepping is 0.004mm. Set zero do disable this option."; + def->sidetext = "mm"; + def->cli = "adaptive-slicing-z-gradation=f"; + def->min = 0; + def->default_value = new ConfigOptionFloat(0); + def = this->add("avoid_crossing_perimeters", coBool); def->label = "Avoid crossing perimeters"; def->tooltip = "Optimize travel moves in order to minimize the crossing of perimeters. This is mostly useful with Bowden extruders which suffer from oozing. This feature slows down both the print and the G-code generation."; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 49bdaefc4..f6e5dd6be 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -104,6 +104,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig { public: ConfigOptionBool adaptive_slicing; + ConfigOptionFloat adaptive_slicing_z_gradation; ConfigOptionFloat cusp_value; ConfigOptionBool dont_support_bridges; ConfigOptionFloatOrPercent extrusion_width; @@ -137,6 +138,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(adaptive_slicing); + OPT_PTR(adaptive_slicing_z_gradation); OPT_PTR(cusp_value); OPT_PTR(dont_support_bridges); OPT_PTR(extrusion_width); diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index eb744985e..c5fd9c1c8 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -228,6 +228,7 @@ PrintObject::invalidate_state_by_config_options(const std::vector Date: Wed, 9 Nov 2016 18:49:38 +0100 Subject: [PATCH 09/64] tests for horizontal feature detection --- t/adaptive_slicing.t | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/t/adaptive_slicing.t b/t/adaptive_slicing.t index 95c7df4e0..59354951b 100644 --- a/t/adaptive_slicing.t +++ b/t/adaptive_slicing.t @@ -1,4 +1,4 @@ -use Test::More tests => 5; +use Test::More tests => 8; use strict; use warnings; @@ -84,4 +84,38 @@ ok (_eq($adaptive_slicing->cusp_height(scale 2.798, 0.1, 0.1, 0.5), 0.1414), 'r # slopes cusp height must be smaller than the distance to the slope ok (_eq($adaptive_slicing->cusp_height(scale 2.6289, 0.15, 0.1, 0.5), 0.3), 'reducing cusp_height to z-diff'); +subtest 'horizontal planes' => sub { + plan tests => 3; + + print "facet_distance: " . $adaptive_slicing->horizontal_facet_distance(scale 1, 1.2) . "\n"; + ok (_eq($adaptive_slicing->horizontal_facet_distance(scale 1, 1.2), 1.2), 'max_height limit'); + ok (_eq($adaptive_slicing->horizontal_facet_distance(scale 8.5, 4), 1.5), 'normal horizontal facet'); + ok (_eq($adaptive_slicing->horizontal_facet_distance(scale 17, 5), 3.0), 'object maximum'); +}; + +# shrink current layer to fit another layer under horizontal facet +$config->set('start_gcode', ''); # to avoid dealing with the nozzle lift in start G-code +$config->set('z_offset', 0); + +$config->set('adaptive_slicing', 1); +$config->set('first_layer_height', 0.42893); # to catch lower slope edge +$config->set('nozzle_diameter', [0.5]); +$config->set('min_layer_height', [0.1]); +$config->set('max_layer_height', [0.5]); +$config->set('cusp_value', [0.19]); +# slope height: 7,07107 (2.92893 to 10) + +subtest 'shrink to match horizontal facets' => sub { + plan tests => 3; + $test->(); +}; + +# widen current layer to match horizontal facet +$config->set('cusp_value', [0.1]); + +subtest 'widen to match horizontal facets' => sub { + plan tests => 3; + $test->(); +}; + __END__ From b429763bb7ac8711a1a4069367629dc42b070262 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 9 Nov 2016 18:50:34 +0100 Subject: [PATCH 10/64] fixed incorrect scaling in horizontal facet detection --- lib/Slic3r/AdaptiveSlicing.pm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/Slic3r/AdaptiveSlicing.pm b/lib/Slic3r/AdaptiveSlicing.pm index da624edac..8bd989585 100644 --- a/lib/Slic3r/AdaptiveSlicing.pm +++ b/lib/Slic3r/AdaptiveSlicing.pm @@ -177,7 +177,7 @@ sub horizontal_facet_distance { # min_z == max_z -> horizontal facet if($self->ordered_facets->[$ordered_id]->[1] > $z) { if($self->ordered_facets->[$ordered_id]->[1] == $self->ordered_facets->[$ordered_id]->[2]) { - return unscale $self->ordered_facets->[$ordered_id]->[1] - $z; + return unscale($self->ordered_facets->[$ordered_id]->[1] - $z); } } @@ -185,11 +185,11 @@ sub horizontal_facet_distance { } # objects maximum? - if($z + $max_height > $self->size) { - return max(unscale $self->size - $z, 0); + if($z + $max_height > scale($self->size)) { + return max($self->size - unscale($z), 0); } + return unscale $max_height; } - 1; \ No newline at end of file From 71101634106aace56fa019487bed25765879c5b9 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 10 Nov 2016 16:43:23 +0100 Subject: [PATCH 11/64] test for adaptive layer height gradation --- t/adaptive_slicing.t | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/t/adaptive_slicing.t b/t/adaptive_slicing.t index 59354951b..b8b1ce575 100644 --- a/t/adaptive_slicing.t +++ b/t/adaptive_slicing.t @@ -1,4 +1,4 @@ -use Test::More tests => 8; +use Test::More tests => 9; use strict; use warnings; @@ -7,14 +7,16 @@ BEGIN { use lib "$FindBin::Bin/../lib"; } -use List::Util qw(first); +use List::Util qw(first sum); use Slic3r; use Slic3r::Test qw(_eq); use Slic3r::Geometry qw(Z PI scale unscale); +use Devel::Peek; + my $config = Slic3r::Config->new_from_defaults; -my $test = sub { +my $generate_gcode = sub { my ($conf) = @_; $conf ||= $config; @@ -31,6 +33,12 @@ my $test = sub { } }); + return (@z); +}; + +my $horizontal_feature_test = sub { + my (@z) = $generate_gcode->(); + ok (_eq($z[0], $config->get_value('first_layer_height') + $config->z_offset), 'first layer height.'); ok (_eq($z[1], $config->get_value('first_layer_height') + $config->get('max_layer_height')->[0] + $config->z_offset), 'second layer height.'); @@ -40,6 +48,17 @@ my $test = sub { 1; }; +my $height_gradation_test = sub { + my (@z) = $generate_gcode->(); + + my $gradation = $config->get('adaptive_slicing_z_gradation'); + # +1 is a "dirty fix" to avoid rounding issues with the modulo operator... + my @results = map {unscale((scale($_)+1) % scale($gradation))} @z; + + ok (_eq(sum(@results), 0), 'layer z is multiple of gradation ' . $gradation ); + + 1; +}; @@ -107,7 +126,7 @@ $config->set('cusp_value', [0.19]); subtest 'shrink to match horizontal facets' => sub { plan tests => 3; - $test->(); + $horizontal_feature_test->(); }; # widen current layer to match horizontal facet @@ -115,7 +134,15 @@ $config->set('cusp_value', [0.1]); subtest 'widen to match horizontal facets' => sub { plan tests => 3; - $test->(); + $horizontal_feature_test->(); +}; + +subtest 'layer height gradation' => sub { + plan tests => 5; + foreach my $gradation (0.001, 0.01, 0.02, 0.05, 0.08) { + $config->set('adaptive_slicing_z_gradation', $gradation); + $height_gradation_test->(); + } }; __END__ From d4597fc76e996782c5c8a86fad970b5a235f443d Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 19 Jan 2017 11:33:39 +0100 Subject: [PATCH 12/64] Basic 2D-control to visualize layer heights --- lib/Slic3r/GUI.pm | 2 + lib/Slic3r/GUI/Plater.pm | 42 +++- lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm | 62 +++++ lib/Slic3r/GUI/Plater/SplineControl.pm | 254 ++++++++++++++++++++ 4 files changed, 357 insertions(+), 3 deletions(-) create mode 100644 lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm create mode 100644 lib/Slic3r/GUI/Plater/SplineControl.pm diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 7f4a028a5..eb359181a 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -23,8 +23,10 @@ use Slic3r::GUI::Plater::3D; use Slic3r::GUI::Plater::3DPreview; use Slic3r::GUI::Plater::ObjectPartsPanel; use Slic3r::GUI::Plater::ObjectCutDialog; +use Slic3r::GUI::Plater::ObjectLayersDialog; use Slic3r::GUI::Plater::ObjectSettingsDialog; use Slic3r::GUI::Plater::OverrideSettingsPanel; +use Slic3r::GUI::Plater::SplineControl; use Slic3r::GUI::Preferences; use Slic3r::GUI::ProgressStatusBar; use Slic3r::GUI::Projector; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index fb1a36004..f7ca76bbc 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -27,6 +27,7 @@ use constant TB_45CCW => &Wx::NewId; use constant TB_SCALE => &Wx::NewId; use constant TB_SPLIT => &Wx::NewId; use constant TB_CUT => &Wx::NewId; +use constant TB_LAYERS => &Wx::NewId; use constant TB_SETTINGS => &Wx::NewId; # package variables to avoid passing lexicals to threads @@ -148,6 +149,7 @@ sub new { $self->{htoolbar}->AddTool(TB_SCALE, "Scale…", Wx::Bitmap->new($Slic3r::var->("arrow_out.png"), wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddTool(TB_SPLIT, "Split", Wx::Bitmap->new($Slic3r::var->("shape_ungroup.png"), wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new($Slic3r::var->("package.png"), wxBITMAP_TYPE_PNG), ''); + $self->{htoolbar}->AddTool(TB_LAYERS, "Layers…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddSeparator; $self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), ''); } else { @@ -163,10 +165,11 @@ sub new { changescale => "Scale…", split => "Split", cut => "Cut…", + layers => "Layers…", settings => "Settings…", ); $self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL); - for (qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings)) { + for (qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut layers settings)) { $self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); $self->{btoolbar}->Add($self->{"btn_$_"}); } @@ -217,6 +220,7 @@ sub new { changescale arrow_out.png split shape_ungroup.png cut package.png + layers cog.png settings cog.png ); for (grep $self->{"btn_$_"}, keys %icons) { @@ -248,6 +252,7 @@ sub new { EVT_TOOL($self, TB_SCALE, sub { $self->changescale(undef); }); EVT_TOOL($self, TB_SPLIT, sub { $self->split_object; }); EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog }); + EVT_TOOL($self, TB_LAYERS, sub { $_[0]->object_layers_dialog }); EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog }); } else { EVT_BUTTON($self, $self->{btn_add}, sub { $self->add; }); @@ -261,6 +266,7 @@ sub new { EVT_BUTTON($self, $self->{btn_changescale}, sub { $self->changescale(undef); }); EVT_BUTTON($self, $self->{btn_split}, sub { $self->split_object; }); EVT_BUTTON($self, $self->{btn_cut}, sub { $_[0]->object_cut_dialog }); + EVT_BUTTON($self, $self->{btn_layers}, sub { $_[0]->object_layers_dialog }); EVT_BUTTON($self, $self->{btn_settings}, sub { $_[0]->object_settings_dialog }); } @@ -1560,6 +1566,33 @@ sub object_cut_dialog { } } +sub object_layers_dialog { + my $self = shift; + my ($obj_idx) = @_; + + if (!defined $obj_idx) { + ($obj_idx, undef) = $self->selected_object; + } + + if (!$Slic3r::GUI::have_OpenGL) { + Slic3r::GUI::show_error($self, "Please install the OpenGL modules to use this feature (see build instructions)."); + return; + } + + my $dlg = Slic3r::GUI::Plater::ObjectLayersDialog->new($self, + object => $self->{objects}[$obj_idx], + model_object => $self->{model}->objects->[$obj_idx], + obj_idx => $obj_idx, + ); + return unless $dlg->ShowModal == wxID_OK; + + if (my @new_objects = $dlg->NewModelObjects) { + $self->remove($obj_idx); + $self->load_model_objects(grep defined($_), @new_objects); + $self->arrange; + } +} + sub object_settings_dialog { my $self = shift; my ($obj_idx) = @_; @@ -1626,11 +1659,11 @@ sub selection_changed { my $method = $have_sel ? 'Enable' : 'Disable'; $self->{"btn_$_"}->$method - for grep $self->{"btn_$_"}, qw(remove increase decrease rotate45cw rotate45ccw changescale split cut settings); + for grep $self->{"btn_$_"}, qw(remove increase decrease rotate45cw rotate45ccw changescale split cut layers settings); if ($self->{htoolbar}) { $self->{htoolbar}->EnableTool($_, $have_sel) - for (TB_REMOVE, TB_MORE, TB_FEWER, TB_45CW, TB_45CCW, TB_SCALE, TB_SPLIT, TB_CUT, TB_SETTINGS); + for (TB_REMOVE, TB_MORE, TB_FEWER, TB_45CW, TB_45CCW, TB_SCALE, TB_SPLIT, TB_CUT, TB_LAYERS, TB_SETTINGS); } if ($self->{object_info_size}) { # have we already loaded the info pane? @@ -1812,6 +1845,9 @@ sub object_menu { $frame->_append_menu_item($menu, "Cut…", 'Open the 3D cutting tool', sub { $self->object_cut_dialog; }, undef, 'package.png'); + $frame->_append_menu_item($menu, "Layers…", 'Open the dynamic layer height control', sub { + $self->object_layers_dialog; + }, undef, 'cog.png'); $menu->AppendSeparator(); $frame->_append_menu_item($menu, "Settings…", 'Open the object editor dialog', sub { $self->object_settings_dialog; diff --git a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm new file mode 100644 index 000000000..922f86526 --- /dev/null +++ b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm @@ -0,0 +1,62 @@ +package Slic3r::GUI::Plater::ObjectLayersDialog; +use strict; +use warnings; +use utf8; + +use Slic3r::Geometry qw(PI X scale unscale); +use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL); +use Wx::Event qw(EVT_CLOSE EVT_BUTTON); +use base 'Wx::Dialog'; + +sub new { + my $class = shift; + my ($parent, %params) = @_; + my $self = $class->SUPER::new($parent, -1, $params{object}->name, wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); + $self->{model_object} = $params{model_object}; + + + my $model_object = $self->{model_object} = $params{model_object}; + my $obj_idx = $self->{obj_idx} = $params{obj_idx}; + my $plater = $self->{plater} = $parent; + + # Initialize 3D toolpaths preview + if ($Slic3r::GUI::have_OpenGL) { + $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self, $plater->{print}); + #$self->{preview3D}->canvas->on_viewport_changed(sub { + # $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); + #}); + $self->{preview3D}->canvas->set_auto_bed_shape; + $self->{preview3D}->canvas->SetSize([500,500]); + $self->{preview3D}->canvas->SetMinSize($self->{preview3D}->canvas->GetSize); + $self->{preview3D}->load_print; + $self->{preview3D}->canvas->zoom_to_volumes; + } + + $self->{splineControl} = Slic3r::GUI::Plater::SplineControl->new($self, Wx::Size->new(100, 200)); + + my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); + $right_sizer->Add($self->{splineControl}, 1, wxEXPAND | wxALL, 0); + + $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); + $self->{sizer}->Add($self->{preview3D}, 1, wxEXPAND | wxTOP | wxBOTTOM, 0) if $self->{preview3D}; + $self->{sizer}->Add($right_sizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10); + + $self->SetSizerAndFit($self->{sizer}); + $self->SetSize([800, 600]); + $self->SetMinSize($self->GetSize); + + # init spline control values + my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); + + #IMPORTANT TODO: set correct min / max layer height from config!!!! + $self->{splineControl}->set_size_parameters(0.1, 0.4, unscale($object->size->z)); + + # get array of current Z coordinates for selected object + my @layer_heights = map $_->print_z, @{$object->layers}; + + $self->{splineControl}->set_interpolation_points(@layer_heights); + + return $self; +} + +1; diff --git a/lib/Slic3r/GUI/Plater/SplineControl.pm b/lib/Slic3r/GUI/Plater/SplineControl.pm new file mode 100644 index 000000000..f5afbe64e --- /dev/null +++ b/lib/Slic3r/GUI/Plater/SplineControl.pm @@ -0,0 +1,254 @@ +package Slic3r::GUI::Plater::SplineControl; +use strict; +use warnings; +use utf8; + +use List::Util qw(min max first); +use Slic3r::Geometry qw(X Y scale unscale convex_hull); +use Slic3r::Geometry::Clipper qw(offset JT_ROUND intersection_pl); +use Wx qw(:misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL); +use Wx::Event qw(EVT_MOUSE_EVENTS EVT_PAINT EVT_ERASE_BACKGROUND EVT_SIZE); +use base 'Wx::Panel'; + +sub new { + my $class = shift; + my ($parent, $size) = @_; + + my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, $size, wxTAB_TRAVERSAL); + # This has only effect on MacOS. On Windows and Linux/GTK, the background is painted by $self->repaint(). + $self->SetBackgroundColour(Wx::wxWHITE); + + + $self->{objects_brush} = Wx::Brush->new(Wx::Colour->new(210,210,210), wxSOLID); + $self->{selected_brush} = Wx::Brush->new(Wx::Colour->new(255,128,128), wxSOLID); + $self->{dragged_brush} = Wx::Brush->new(Wx::Colour->new(128,128,255), wxSOLID); + $self->{transparent_brush} = Wx::Brush->new(Wx::Colour->new(0,0,0), wxTRANSPARENT); + $self->{line_pen} = Wx::Pen->new(Wx::Colour->new(50,50,50), 1, wxSOLID); + $self->{print_center_pen} = Wx::Pen->new(Wx::Colour->new(200,200,200), 1, wxSOLID); + $self->{clearance_pen} = Wx::Pen->new(Wx::Colour->new(0,0,200), 1, wxSOLID); + $self->{skirt_pen} = Wx::Pen->new(Wx::Colour->new(150,150,150), 1, wxSOLID); + + $self->{user_drawn_background} = $^O ne 'darwin'; + + $self->{min_layer_height} = 0.12345678; + $self->{max_layer_height} = 0.4; + $self->{object_height} = 1.0; + $self->{interpolation_points} = (); + + + EVT_PAINT($self, \&repaint); + EVT_ERASE_BACKGROUND($self, sub {}) if $self->{user_drawn_background}; + EVT_MOUSE_EVENTS($self, \&mouse_event); + EVT_SIZE($self, sub { + $self->update_bed_size; + $self->Refresh; + }); + + return $self; +} + +sub repaint { + my ($self, $event) = @_; + + my $dc = Wx::AutoBufferedPaintDC->new($self); + my $size = $self->GetSize; + # Set axis orientation to leftRight, bottomUp and reset origin to the lower left corner + $dc->SetAxisOrientation(1, 1); + my @size = ($size->GetWidth, $size->GetHeight); + $dc->SetDeviceOrigin(0, $size[1]); + + print "repaint\n"; + + + if ($self->{user_drawn_background}) { + # On all systems the AutoBufferedPaintDC() achieves double buffering. + # On MacOS the background is erased, on Windows the background is not erased + # and on Linux/GTK the background is erased to gray color. + # Fill DC with the background on Windows & Linux/GTK. + my $brush_background = Wx::Brush->new(Wx::wxWHITE, wxSOLID); + $dc->SetPen(wxWHITE_PEN); + $dc->SetBrush($brush_background); + my $rect = $self->GetUpdateRegion()->GetBox(); + $dc->DrawRectangle($rect->GetLeft(), $rect->GetTop(), $rect->GetWidth(), $rect->GetHeight()); + } + + # draw scale (min and max indicator at the bottom) + $dc->SetTextForeground(Wx::Colour->new(0,0,0)); + $dc->SetFont(Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL)); + $dc->DrawLabel(sprintf('%.4g', $self->{min_layer_height}), Wx::Rect->new(0, 15, $size[0], -15), wxALIGN_LEFT | wxALIGN_TOP); + $dc->DrawLabel(sprintf('%.4g', $self->{max_layer_height}), Wx::Rect->new(0, 15, $size[0], -15), wxALIGN_RIGHT | wxALIGN_TOP); + + # draw current layers as lines + my $scaling_y = $size[1]/$self->{object_height}; + my $scaling_x = $size[0]/($self->{max_layer_height} - $self->{min_layer_height}); + my $last_z = 0.0; + foreach my $z (@{$self->{interpolation_points}}) { + my $layer_h = $z - $last_z; + $dc->SetPen($self->{line_pen}); + $dc->DrawLine(0, $z*$scaling_y, ($layer_h-$self->{min_layer_height})*$scaling_x, $z*$scaling_y); + $last_z = $z; + } + + $event->Skip; +} + +sub mouse_event { + my ($self, $event) = @_; + +# my $pos = $event->GetPosition; +# my $point = $self->point_to_model_units([ $pos->x, $pos->y ]); #]] +# if ($event->ButtonDown) { +# $self->{on_select_object}->(undef); +# # traverse objects and instances in reverse order, so that if they're overlapping +# # we get the one that gets drawn last, thus on top (as user expects that to move) +# OBJECTS: for my $obj_idx (reverse 0 .. $#{$self->{objects}}) { +# my $object = $self->{objects}->[$obj_idx]; +# for my $instance_idx (reverse 0 .. $#{ $object->instance_thumbnails }) { +# my $thumbnail = $object->instance_thumbnails->[$instance_idx]; +# if (defined first { $_->contour->contains_point($point) } @$thumbnail) { +# $self->{on_select_object}->($obj_idx); +# +# if ($event->LeftDown) { +# # start dragging +# my $instance = $self->{model}->objects->[$obj_idx]->instances->[$instance_idx]; +# my $instance_origin = [ map scale($_), @{$instance->offset} ]; +# $self->{drag_start_pos} = [ # displacement between the click and the instance origin in scaled model units +# $point->x - $instance_origin->[X], +# $point->y - $instance_origin->[Y], #- +# ]; +# $self->{drag_object} = [ $obj_idx, $instance_idx ]; +# } elsif ($event->RightDown) { +# $self->{on_right_click}->($pos); +# } +# +# last OBJECTS; +# } +# } +# } +# $self->Refresh; +# } elsif ($event->LeftUp) { +# $self->{on_instances_moved}->() +# if $self->{drag_object}; +# $self->{drag_start_pos} = undef; +# $self->{drag_object} = undef; +# $self->SetCursor(wxSTANDARD_CURSOR); +# } elsif ($event->LeftDClick) { +# $self->{on_double_click}->(); +# } elsif ($event->Dragging) { +# return if !$self->{drag_start_pos}; # concurrency problems +# my ($obj_idx, $instance_idx) = @{ $self->{drag_object} }; +# my $model_object = $self->{model}->objects->[$obj_idx]; +# $model_object->instances->[$instance_idx]->set_offset( +# Slic3r::Pointf->new( +# unscale($point->[X] - $self->{drag_start_pos}[X]), +# unscale($point->[Y] - $self->{drag_start_pos}[Y]), +# )); +# $model_object->update_bounding_box; +# $self->Refresh; +# } elsif ($event->Moving) { +# my $cursor = wxSTANDARD_CURSOR; +# if (defined first { $_->contour->contains_point($point) } map @$_, map @{$_->instance_thumbnails}, @{ $self->{objects} }) { +# $cursor = Wx::Cursor->new(wxCURSOR_HAND); +# } +# $self->SetCursor($cursor); +# } +} + +sub update_bed_size { + my $self = shift; + +# # when the canvas is not rendered yet, its GetSize() method returns 0,0 +# my $canvas_size = $self->GetSize; +# my ($canvas_w, $canvas_h) = ($canvas_size->GetWidth, $canvas_size->GetHeight); +# return if $canvas_w == 0; +# +# # get bed shape polygon +# $self->{bed_polygon} = my $polygon = Slic3r::Polygon->new_scale(@{$self->{config}->bed_shape}); +# my $bb = $polygon->bounding_box; +# my $size = $canvas_size; +# +# # calculate the scaling factor needed for constraining print bed area inside preview +# # scaling_factor is expressed in pixel / mm +# $self->{scaling_factor} = min($canvas_w / unscale($size->x), $canvas_h / unscale($size->y)); #) +# +# # calculate the displacement needed to center bed +# $self->{bed_origin} = [ +# $canvas_w/2 - (unscale($bb->x_max + $bb->x_min)/2 * $self->{scaling_factor}), +# $canvas_h - ($canvas_h/2 - (unscale($bb->y_max + $bb->y_min)/2 * $self->{scaling_factor})), +# ]; +# +# # calculate print center +# my $center = $bb->center; +# $self->{print_center} = [ unscale($center->x), unscale($center->y) ]; #)) +# +# # cache bed contours and grid +# { +# my $step = scale 10; # 1cm grid +# my @polylines = (); +# for (my $x = $bb->x_min - ($bb->x_min % $step) + $step; $x < $bb->x_max; $x += $step) { +# push @polylines, Slic3r::Polyline->new([$x, $bb->y_min], [$x, $bb->y_max]); +# } +# for (my $y = $bb->y_min - ($bb->y_min % $step) + $step; $y < $bb->y_max; $y += $step) { +# push @polylines, Slic3r::Polyline->new([$bb->x_min, $y], [$bb->x_max, $y]); +# } +# @polylines = @{intersection_pl(\@polylines, [$polygon])}; +# $self->{grid} = [ map $self->scaled_points_to_pixel([ @$_[0,-1] ], 1), @polylines ]; +# } +} + +# Set basic parameters for this control. +# min/max_layer_height are required to define the x-range, object_height is used to scale the y-range. +# Must be called if object selection changes. +sub set_size_parameters { + my ($self, $min_layer_height, $max_layer_height, $object_height) = @_; + + $self->{min_layer_height} = $min_layer_height; + $self->{max_layer_height} = $max_layer_height; + $self->{object_height} = $object_height; + + #$self->repaint; +} + +sub set_interpolation_points { + my ($self, @interpolation_points) = @_; + + $self->{interpolation_points} = [@interpolation_points]; + print "set_interpolation_points\n"; + $self->Refresh; + print "refresh\n"; +} + +# convert a model coordinate into a pixel coordinate +sub unscaled_point_to_pixel { + my ($self, $point) = @_; + + my $canvas_height = $self->GetSize->GetHeight; + my $zero = $self->{bed_origin}; + return [ + $point->[X] * $self->{scaling_factor} + $zero->[X], + $canvas_height - $point->[Y] * $self->{scaling_factor} + ($zero->[Y] - $canvas_height), + ]; +} + +sub scaled_points_to_pixel { + my ($self, $points, $unscale) = @_; + + my $result = []; + foreach my $point (@$points) { + $point = [ map unscale($_), @$point ] if $unscale; + push @$result, $self->unscaled_point_to_pixel($point); + } + return $result; +} + +sub point_to_model_units { + my ($self, $point) = @_; + + my $zero = $self->{bed_origin}; + return Slic3r::Point->new( + scale ($point->[X] - $zero->[X]) / $self->{scaling_factor}, + scale ($zero->[Y] - $point->[Y]) / $self->{scaling_factor}, + ); +} + +1; From e6d173f2ecda4961dc57646e8073650d8d8ab857 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 19 Jan 2017 13:40:09 +0100 Subject: [PATCH 13/64] experimental spline visualization --- lib/Slic3r/GUI/Plater/SplineControl.pm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/SplineControl.pm b/lib/Slic3r/GUI/Plater/SplineControl.pm index f5afbe64e..7a24312ef 100644 --- a/lib/Slic3r/GUI/Plater/SplineControl.pm +++ b/lib/Slic3r/GUI/Plater/SplineControl.pm @@ -82,12 +82,16 @@ sub repaint { my $scaling_y = $size[1]/$self->{object_height}; my $scaling_x = $size[0]/($self->{max_layer_height} - $self->{min_layer_height}); my $last_z = 0.0; + my @points = (); foreach my $z (@{$self->{interpolation_points}}) { my $layer_h = $z - $last_z; $dc->SetPen($self->{line_pen}); $dc->DrawLine(0, $z*$scaling_y, ($layer_h-$self->{min_layer_height})*$scaling_x, $z*$scaling_y); + push (@points, Wx::Point->new(($layer_h-$self->{min_layer_height})*$scaling_x, $z*$scaling_y)); $last_z = $z; } + + $dc->DrawSpline(\@points); $event->Skip; } @@ -213,9 +217,7 @@ sub set_interpolation_points { my ($self, @interpolation_points) = @_; $self->{interpolation_points} = [@interpolation_points]; - print "set_interpolation_points\n"; $self->Refresh; - print "refresh\n"; } # convert a model coordinate into a pixel coordinate From 069fe99c7a3fdc0ef179490e3ef97ea40aa19eaa Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 19 Jan 2017 13:40:54 +0100 Subject: [PATCH 14/64] scale layer height representation to correct min / max extruder capabilities --- lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm index 922f86526..b826b2269 100644 --- a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm @@ -4,6 +4,7 @@ use warnings; use utf8; use Slic3r::Geometry qw(PI X scale unscale); +use List::Util qw(min max sum first); use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL); use Wx::Event qw(EVT_CLOSE EVT_BUTTON); use base 'Wx::Dialog'; @@ -49,7 +50,20 @@ sub new { my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); #IMPORTANT TODO: set correct min / max layer height from config!!!! - $self->{splineControl}->set_size_parameters(0.1, 0.4, unscale($object->size->z)); + + # determine min and max layer height from perimeter extruder capabilities. + my %extruders; + for my $region_id (0 .. ($object->region_count - 1)) { + foreach (qw(perimeter_extruder infill_extruder solid_infill_extruder)) { + my $extruder_id = $self->{plater}->{print}->get_region($region_id)->config->get($_)-1; + $extruders{$extruder_id} = $extruder_id; + } + } + + my $min_height = max(map {$self->{plater}->{print}->config->get_at('min_layer_height', $_)} (values %extruders)); + my $max_height = min(map {$self->{plater}->{print}->config->get_at('max_layer_height', $_)} (values %extruders)); + + $self->{splineControl}->set_size_parameters($min_height, $max_height, unscale($object->size->z)); # get array of current Z coordinates for selected object my @layer_heights = map $_->print_z, @{$object->layers}; From 5ad2ec0f5fb606ecfbb1b110f3617722efa2bd4f Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Fri, 20 Jan 2017 09:45:18 +0100 Subject: [PATCH 15/64] helper functions to convert between canvas and object coordinates --- lib/Slic3r/GUI/Plater/SplineControl.pm | 84 ++++++++++++++------------ 1 file changed, 45 insertions(+), 39 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/SplineControl.pm b/lib/Slic3r/GUI/Plater/SplineControl.pm index 7a24312ef..33b96d797 100644 --- a/lib/Slic3r/GUI/Plater/SplineControl.pm +++ b/lib/Slic3r/GUI/Plater/SplineControl.pm @@ -52,10 +52,7 @@ sub repaint { my $dc = Wx::AutoBufferedPaintDC->new($self); my $size = $self->GetSize; - # Set axis orientation to leftRight, bottomUp and reset origin to the lower left corner - $dc->SetAxisOrientation(1, 1); my @size = ($size->GetWidth, $size->GetHeight); - $dc->SetDeviceOrigin(0, $size[1]); print "repaint\n"; @@ -75,19 +72,19 @@ sub repaint { # draw scale (min and max indicator at the bottom) $dc->SetTextForeground(Wx::Colour->new(0,0,0)); $dc->SetFont(Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL)); - $dc->DrawLabel(sprintf('%.4g', $self->{min_layer_height}), Wx::Rect->new(0, 15, $size[0], -15), wxALIGN_LEFT | wxALIGN_TOP); - $dc->DrawLabel(sprintf('%.4g', $self->{max_layer_height}), Wx::Rect->new(0, 15, $size[0], -15), wxALIGN_RIGHT | wxALIGN_TOP); + $dc->DrawLabel(sprintf('%.4g', $self->{min_layer_height}), Wx::Rect->new(0, $size[1]/2, $size[0], $size[1]/2), wxALIGN_LEFT | wxALIGN_BOTTOM); + $dc->DrawLabel(sprintf('%.4g', $self->{max_layer_height}), Wx::Rect->new(0, $size[1]/2, $size[0], $size[1]/2), wxALIGN_RIGHT | wxALIGN_BOTTOM); # draw current layers as lines - my $scaling_y = $size[1]/$self->{object_height}; - my $scaling_x = $size[0]/($self->{max_layer_height} - $self->{min_layer_height}); my $last_z = 0.0; my @points = (); foreach my $z (@{$self->{interpolation_points}}) { my $layer_h = $z - $last_z; $dc->SetPen($self->{line_pen}); - $dc->DrawLine(0, $z*$scaling_y, ($layer_h-$self->{min_layer_height})*$scaling_x, $z*$scaling_y); - push (@points, Wx::Point->new(($layer_h-$self->{min_layer_height})*$scaling_x, $z*$scaling_y)); + my $pl = $self->point_to_pixel(0, $z); + my $pr = $self->point_to_pixel($layer_h, $z); + $dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); + push (@points, $pr); $last_z = $z; } @@ -99,9 +96,15 @@ sub repaint { sub mouse_event { my ($self, $event) = @_; -# my $pos = $event->GetPosition; + my $pos = $event->GetPosition; + my @obj_pos = $self->pixel_to_point($pos); + #$pos->y = $self->GetSize->GetHeight - $pos->y; # my $point = $self->point_to_model_units([ $pos->x, $pos->y ]); #]] -# if ($event->ButtonDown) { + if ($event->ButtonDown) { + if ($event->LeftDown) { + # start dragging + $self->{drag_start_pos} = [$pos->x, $pos->y]; + } # $self->{on_select_object}->(undef); # # traverse objects and instances in reverse order, so that if they're overlapping # # we get the one that gets drawn last, thus on top (as user expects that to move) @@ -138,7 +141,8 @@ sub mouse_event { # $self->SetCursor(wxSTANDARD_CURSOR); # } elsif ($event->LeftDClick) { # $self->{on_double_click}->(); -# } elsif ($event->Dragging) { + } elsif ($event->Dragging) { + print "dragging, pos: " . $pos->x . ":" . $pos->y . "\n"; # return if !$self->{drag_start_pos}; # concurrency problems # my ($obj_idx, $instance_idx) = @{ $self->{drag_object} }; # my $model_object = $self->{model}->objects->[$obj_idx]; @@ -149,7 +153,7 @@ sub mouse_event { # )); # $model_object->update_bounding_box; # $self->Refresh; -# } elsif ($event->Moving) { + }# elsif ($event->Moving) { # my $cursor = wxSTANDARD_CURSOR; # if (defined first { $_->contour->contains_point($point) } map @$_, map @{$_->instance_thumbnails}, @{ $self->{objects} }) { # $cursor = Wx::Cursor->new(wxCURSOR_HAND); @@ -213,6 +217,7 @@ sub set_size_parameters { #$self->repaint; } +# Set the current layer height values as basis for user manipulation sub set_interpolation_points { my ($self, @interpolation_points) = @_; @@ -220,37 +225,38 @@ sub set_interpolation_points { $self->Refresh; } -# convert a model coordinate into a pixel coordinate -sub unscaled_point_to_pixel { - my ($self, $point) = @_; +# Takes a 2-tupel [layer_height (x), height(y)] and converts it +# into a Wx::Point in scaled canvas coordinates +sub point_to_pixel { + my ($self, @point) = @_; + + my $size = $self->GetSize; + my @size = ($size->GetWidth, $size->GetHeight); - my $canvas_height = $self->GetSize->GetHeight; - my $zero = $self->{bed_origin}; - return [ - $point->[X] * $self->{scaling_factor} + $zero->[X], - $canvas_height - $point->[Y] * $self->{scaling_factor} + ($zero->[Y] - $canvas_height), - ]; + my $scaling_y = $size[1]/$self->{object_height}; + my $scaling_x = $size[0]/($self->{max_layer_height} - $self->{min_layer_height}); + + my $x = ($point[0] - $self->{min_layer_height})*$scaling_x; + my $y = $size[1] - $point[1]*$scaling_y; # invert y-axis + + return Wx::Point->new(min(max($x, 0), $size[0]), min(max($y, 0), $size[1])); # limit to canvas size } -sub scaled_points_to_pixel { - my ($self, $points, $unscale) = @_; - - my $result = []; - foreach my $point (@$points) { - $point = [ map unscale($_), @$point ] if $unscale; - push @$result, $self->unscaled_point_to_pixel($point); - } - return $result; -} - -sub point_to_model_units { +# Takes a Wx::Point in scaled canvas coordinates and converts it +# into a 2-tupel [layer_height (x), height(y)] +sub pixel_to_point { my ($self, $point) = @_; - my $zero = $self->{bed_origin}; - return Slic3r::Point->new( - scale ($point->[X] - $zero->[X]) / $self->{scaling_factor}, - scale ($zero->[Y] - $point->[Y]) / $self->{scaling_factor}, - ); + my $size = $self->GetSize; + my @size = ($size->GetWidth, $size->GetHeight); + + my $scaling_y = $size[1]/$self->{object_height}; + my $scaling_x = $size[0]/($self->{max_layer_height} - $self->{min_layer_height}); + + my $x = $point->x/$scaling_x + $self->{min_layer_height}; + my $y = ($size[1] - $point->y)/$scaling_y; # invert y-axis + + return (min(max($x, $self->{min_layer_height}), $self->{max_layer_height}), min(max($y, 0), $self->{object_height})); # limit to object size and layer constraints } 1; From c3018d600a452dc086ff37f26eaaa2e610a4a519 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Fri, 20 Jan 2017 09:59:35 +0100 Subject: [PATCH 16/64] cache scaling factors --- lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm | 5 +- lib/Slic3r/GUI/Plater/SplineControl.pm | 93 +++++++-------------- 2 files changed, 29 insertions(+), 69 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm index b826b2269..76dffc548 100644 --- a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm @@ -49,8 +49,6 @@ sub new { # init spline control values my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); - #IMPORTANT TODO: set correct min / max layer height from config!!!! - # determine min and max layer height from perimeter extruder capabilities. my %extruders; for my $region_id (0 .. ($object->region_count - 1)) { @@ -59,7 +57,6 @@ sub new { $extruders{$extruder_id} = $extruder_id; } } - my $min_height = max(map {$self->{plater}->{print}->config->get_at('min_layer_height', $_)} (values %extruders)); my $max_height = min(map {$self->{plater}->{print}->config->get_at('max_layer_height', $_)} (values %extruders)); @@ -68,7 +65,7 @@ sub new { # get array of current Z coordinates for selected object my @layer_heights = map $_->print_z, @{$object->layers}; - $self->{splineControl}->set_interpolation_points(@layer_heights); + $self->{splineControl}->set_layer_points(@layer_heights); return $self; } diff --git a/lib/Slic3r/GUI/Plater/SplineControl.pm b/lib/Slic3r/GUI/Plater/SplineControl.pm index 33b96d797..f016fba93 100644 --- a/lib/Slic3r/GUI/Plater/SplineControl.pm +++ b/lib/Slic3r/GUI/Plater/SplineControl.pm @@ -18,29 +18,24 @@ sub new { # This has only effect on MacOS. On Windows and Linux/GTK, the background is painted by $self->repaint(). $self->SetBackgroundColour(Wx::wxWHITE); - - $self->{objects_brush} = Wx::Brush->new(Wx::Colour->new(210,210,210), wxSOLID); - $self->{selected_brush} = Wx::Brush->new(Wx::Colour->new(255,128,128), wxSOLID); - $self->{dragged_brush} = Wx::Brush->new(Wx::Colour->new(128,128,255), wxSOLID); - $self->{transparent_brush} = Wx::Brush->new(Wx::Colour->new(0,0,0), wxTRANSPARENT); $self->{line_pen} = Wx::Pen->new(Wx::Colour->new(50,50,50), 1, wxSOLID); - $self->{print_center_pen} = Wx::Pen->new(Wx::Colour->new(200,200,200), 1, wxSOLID); - $self->{clearance_pen} = Wx::Pen->new(Wx::Colour->new(0,0,200), 1, wxSOLID); - $self->{skirt_pen} = Wx::Pen->new(Wx::Colour->new(150,150,150), 1, wxSOLID); $self->{user_drawn_background} = $^O ne 'darwin'; - $self->{min_layer_height} = 0.12345678; + $self->{scaling_factor_x} = 1; + $self->{scaling_factor_y} = 1; + + $self->{min_layer_height} = 0.1; $self->{max_layer_height} = 0.4; $self->{object_height} = 1.0; - $self->{interpolation_points} = (); + $self->{layer_points} = (); EVT_PAINT($self, \&repaint); EVT_ERASE_BACKGROUND($self, sub {}) if $self->{user_drawn_background}; EVT_MOUSE_EVENTS($self, \&mouse_event); EVT_SIZE($self, sub { - $self->update_bed_size; + $self->update_canvas_size; $self->Refresh; }); @@ -78,7 +73,7 @@ sub repaint { # draw current layers as lines my $last_z = 0.0; my @points = (); - foreach my $z (@{$self->{interpolation_points}}) { + foreach my $z (@{$self->{layer_points}}) { my $layer_h = $z - $last_z; $dc->SetPen($self->{line_pen}); my $pl = $self->point_to_pixel(0, $z); @@ -162,46 +157,19 @@ sub mouse_event { # } } -sub update_bed_size { +sub update_canvas_size { my $self = shift; -# # when the canvas is not rendered yet, its GetSize() method returns 0,0 -# my $canvas_size = $self->GetSize; -# my ($canvas_w, $canvas_h) = ($canvas_size->GetWidth, $canvas_size->GetHeight); -# return if $canvas_w == 0; -# -# # get bed shape polygon -# $self->{bed_polygon} = my $polygon = Slic3r::Polygon->new_scale(@{$self->{config}->bed_shape}); -# my $bb = $polygon->bounding_box; -# my $size = $canvas_size; -# -# # calculate the scaling factor needed for constraining print bed area inside preview -# # scaling_factor is expressed in pixel / mm -# $self->{scaling_factor} = min($canvas_w / unscale($size->x), $canvas_h / unscale($size->y)); #) -# -# # calculate the displacement needed to center bed -# $self->{bed_origin} = [ -# $canvas_w/2 - (unscale($bb->x_max + $bb->x_min)/2 * $self->{scaling_factor}), -# $canvas_h - ($canvas_h/2 - (unscale($bb->y_max + $bb->y_min)/2 * $self->{scaling_factor})), -# ]; -# -# # calculate print center -# my $center = $bb->center; -# $self->{print_center} = [ unscale($center->x), unscale($center->y) ]; #)) -# -# # cache bed contours and grid -# { -# my $step = scale 10; # 1cm grid -# my @polylines = (); -# for (my $x = $bb->x_min - ($bb->x_min % $step) + $step; $x < $bb->x_max; $x += $step) { -# push @polylines, Slic3r::Polyline->new([$x, $bb->y_min], [$x, $bb->y_max]); -# } -# for (my $y = $bb->y_min - ($bb->y_min % $step) + $step; $y < $bb->y_max; $y += $step) { -# push @polylines, Slic3r::Polyline->new([$bb->x_min, $y], [$bb->x_max, $y]); -# } -# @polylines = @{intersection_pl(\@polylines, [$polygon])}; -# $self->{grid} = [ map $self->scaled_points_to_pixel([ @$_[0,-1] ], 1), @polylines ]; -# } + # when the canvas is not rendered yet, its GetSize() method returns 0,0 + my $canvas_size = $self->GetSize; + my ($canvas_w, $canvas_h) = ($canvas_size->GetWidth, $canvas_size->GetHeight); + return if $canvas_w == 0; + + my $size = $canvas_size; + my @size = ($size->GetWidth, $size->GetHeight); + + $self->{scaling_factor_x} = $size[0]/($self->{max_layer_height} - $self->{min_layer_height}); + $self->{scaling_factor_y} = $size[1]/$self->{object_height}; } # Set basic parameters for this control. @@ -213,15 +181,16 @@ sub set_size_parameters { $self->{min_layer_height} = $min_layer_height; $self->{max_layer_height} = $max_layer_height; $self->{object_height} = $object_height; - - #$self->repaint; + + $self->update_canvas_size; + $self->Refresh; } # Set the current layer height values as basis for user manipulation -sub set_interpolation_points { - my ($self, @interpolation_points) = @_; +sub set_layer_points { + my ($self, @layer_points) = @_; - $self->{interpolation_points} = [@interpolation_points]; + $self->{layer_points} = [@layer_points]; $self->Refresh; } @@ -233,11 +202,8 @@ sub point_to_pixel { my $size = $self->GetSize; my @size = ($size->GetWidth, $size->GetHeight); - my $scaling_y = $size[1]/$self->{object_height}; - my $scaling_x = $size[0]/($self->{max_layer_height} - $self->{min_layer_height}); - - my $x = ($point[0] - $self->{min_layer_height})*$scaling_x; - my $y = $size[1] - $point[1]*$scaling_y; # invert y-axis + my $x = ($point[0] - $self->{min_layer_height})*$self->{scaling_factor_x}; + my $y = $size[1] - $point[1]*$self->{scaling_factor_y}; # invert y-axis return Wx::Point->new(min(max($x, 0), $size[0]), min(max($y, 0), $size[1])); # limit to canvas size } @@ -250,11 +216,8 @@ sub pixel_to_point { my $size = $self->GetSize; my @size = ($size->GetWidth, $size->GetHeight); - my $scaling_y = $size[1]/$self->{object_height}; - my $scaling_x = $size[0]/($self->{max_layer_height} - $self->{min_layer_height}); - - my $x = $point->x/$scaling_x + $self->{min_layer_height}; - my $y = ($size[1] - $point->y)/$scaling_y; # invert y-axis + my $x = $point->x/$self->{scaling_factor_x} + $self->{min_layer_height}; + my $y = ($size[1] - $point->y)/$self->{scaling_factor_y}; # invert y-axis return (min(max($x, $self->{min_layer_height}), $self->{max_layer_height}), min(max($y, 0), $self->{object_height})); # limit to object size and layer constraints } From 747d2dfca47d7f52ef253472ac13d2ceed9811a9 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Fri, 20 Jan 2017 17:12:11 +0100 Subject: [PATCH 17/64] implemented quadratic control and interpolation --- lib/Slic3r/GUI/Plater/SplineControl.pm | 183 ++++++++++++++++++------- 1 file changed, 131 insertions(+), 52 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/SplineControl.pm b/lib/Slic3r/GUI/Plater/SplineControl.pm index f016fba93..d4614793f 100644 --- a/lib/Slic3r/GUI/Plater/SplineControl.pm +++ b/lib/Slic3r/GUI/Plater/SplineControl.pm @@ -19,6 +19,8 @@ sub new { $self->SetBackgroundColour(Wx::wxWHITE); $self->{line_pen} = Wx::Pen->new(Wx::Colour->new(50,50,50), 1, wxSOLID); + $self->{interactive_pen} = Wx::Pen->new(Wx::Colour->new(255,0,0), 1, wxSOLID); + $self->{resulting_pen} = Wx::Pen->new(Wx::Colour->new(0,255,0), 1, wxSOLID); $self->{user_drawn_background} = $^O ne 'darwin'; @@ -29,6 +31,8 @@ sub new { $self->{max_layer_height} = 0.4; $self->{object_height} = 1.0; $self->{layer_points} = (); + $self->{interactive_points} = (); + $self->{resulting_points} = (); EVT_PAINT($self, \&repaint); @@ -48,8 +52,6 @@ sub repaint { my $dc = Wx::AutoBufferedPaintDC->new($self); my $size = $self->GetSize; my @size = ($size->GetWidth, $size->GetHeight); - - print "repaint\n"; if ($self->{user_drawn_background}) { @@ -70,9 +72,25 @@ sub repaint { $dc->DrawLabel(sprintf('%.4g', $self->{min_layer_height}), Wx::Rect->new(0, $size[1]/2, $size[0], $size[1]/2), wxALIGN_LEFT | wxALIGN_BOTTOM); $dc->DrawLabel(sprintf('%.4g', $self->{max_layer_height}), Wx::Rect->new(0, $size[1]/2, $size[0], $size[1]/2), wxALIGN_RIGHT | wxALIGN_BOTTOM); - # draw current layers as lines + + # draw interpolated (user modified) layers as lines my $last_z = 0.0; my @points = (); + foreach my $z (@{$self->{interactive_points}}) { + my $layer_h = $z - $last_z; + $dc->SetPen($self->{interactive_pen}); + my $pl = $self->point_to_pixel(0, $z); + my $pr = $self->point_to_pixel($layer_h, $z); + $dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); + push (@points, $pr); + $last_z = $z; + } + + $dc->DrawSpline(\@points); + + # draw current layers as lines + $last_z = 0.0; + @points = (); foreach my $z (@{$self->{layer_points}}) { my $layer_h = $z - $last_z; $dc->SetPen($self->{line_pen}); @@ -85,6 +103,21 @@ sub repaint { $dc->DrawSpline(\@points); + # draw resulting layers as lines + $last_z = 0.0; + @points = (); + foreach my $z (@{$self->{resulting_points}}) { + my $layer_h = $z - $last_z; + $dc->SetPen($self->{resulting_pen}); + my $pl = $self->point_to_pixel(0, $z); + my $pr = $self->point_to_pixel($layer_h, $z); + $dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); + push (@points, $pr); + $last_z = $z; + } + + $dc->DrawSpline(\@points); + $event->Skip; } @@ -93,61 +126,29 @@ sub mouse_event { my $pos = $event->GetPosition; my @obj_pos = $self->pixel_to_point($pos); - #$pos->y = $self->GetSize->GetHeight - $pos->y; -# my $point = $self->point_to_model_units([ $pos->x, $pos->y ]); #]] + if ($event->ButtonDown) { if ($event->LeftDown) { # start dragging - $self->{drag_start_pos} = [$pos->x, $pos->y]; + $self->{drag_start_pos} = $pos; } -# $self->{on_select_object}->(undef); -# # traverse objects and instances in reverse order, so that if they're overlapping -# # we get the one that gets drawn last, thus on top (as user expects that to move) -# OBJECTS: for my $obj_idx (reverse 0 .. $#{$self->{objects}}) { -# my $object = $self->{objects}->[$obj_idx]; -# for my $instance_idx (reverse 0 .. $#{ $object->instance_thumbnails }) { -# my $thumbnail = $object->instance_thumbnails->[$instance_idx]; -# if (defined first { $_->contour->contains_point($point) } @$thumbnail) { -# $self->{on_select_object}->($obj_idx); -# -# if ($event->LeftDown) { -# # start dragging -# my $instance = $self->{model}->objects->[$obj_idx]->instances->[$instance_idx]; -# my $instance_origin = [ map scale($_), @{$instance->offset} ]; -# $self->{drag_start_pos} = [ # displacement between the click and the instance origin in scaled model units -# $point->x - $instance_origin->[X], -# $point->y - $instance_origin->[Y], #- -# ]; -# $self->{drag_object} = [ $obj_idx, $instance_idx ]; -# } elsif ($event->RightDown) { -# $self->{on_right_click}->($pos); -# } -# -# last OBJECTS; -# } -# } -# } -# $self->Refresh; -# } elsif ($event->LeftUp) { -# $self->{on_instances_moved}->() -# if $self->{drag_object}; -# $self->{drag_start_pos} = undef; -# $self->{drag_object} = undef; -# $self->SetCursor(wxSTANDARD_CURSOR); -# } elsif ($event->LeftDClick) { -# $self->{on_double_click}->(); + } elsif ($event->LeftUp) { + if($self->{drag_start_pos}) { + $self->{resulting_points} = $self->{interactive_points}; + $self->Refresh; + } + $self->{drag_start_pos} = undef; } elsif ($event->Dragging) { print "dragging, pos: " . $pos->x . ":" . $pos->y . "\n"; -# return if !$self->{drag_start_pos}; # concurrency problems -# my ($obj_idx, $instance_idx) = @{ $self->{drag_object} }; -# my $model_object = $self->{model}->objects->[$obj_idx]; -# $model_object->instances->[$instance_idx]->set_offset( -# Slic3r::Pointf->new( -# unscale($point->[X] - $self->{drag_start_pos}[X]), -# unscale($point->[Y] - $self->{drag_start_pos}[Y]), -# )); -# $model_object->update_bounding_box; -# $self->Refresh; + return if !$self->{drag_start_pos}; # concurrency problems + + my @start_pos = $self->pixel_to_point($self->{drag_start_pos}); + my $range = abs($start_pos[1] - $obj_pos[1]); + + # compute updated interactive layer heights + $self->interactive_curve($start_pos[1], $obj_pos[0], $range); + $self->Refresh; + }# elsif ($event->Moving) { # my $cursor = wxSTANDARD_CURSOR; # if (defined first { $_->contour->contains_point($point) } map @$_, map @{$_->instance_thumbnails}, @{ $self->{objects} }) { @@ -157,6 +158,7 @@ sub mouse_event { # } } +# Internal function to cache scaling factors sub update_canvas_size { my $self = shift; @@ -191,9 +193,86 @@ sub set_layer_points { my ($self, @layer_points) = @_; $self->{layer_points} = [@layer_points]; + $self->{interactive_points} = [@layer_points]; # Initialize to current values + $self->{resulting_points} = [@layer_points]; # Initialize to current values $self->Refresh; } + +sub interactive_curve { + my ($self, $mod_z, $target_layer_height, $range) = @_; + + $self->{interactive_points} = (); # reset interactive curve + + my $z = 0.0; + my $layer_h = $self->{resulting_points}[0]; + my $i = 0; + # copy points which are not going to be modified + while(($z+$self->{resulting_points}[$i] < $mod_z-$range) && ($i < @{$self->{resulting_points}})) { + $layer_h = $self->{resulting_points}[$i] - $z; + $z = $self->{resulting_points}[$i]; + push (@{$self->{interactive_points}}, $z); + $i +=1; + } + + print "last original z: " . $z . "\n"; + # interpolate next points + while($z < $self->{object_height}) { + $layer_h = $self->_interpolate_next_layer_h($z); + my $diff = $target_layer_height - $layer_h; + my $quadratic_factor = $self->_quadratic_factor($mod_z, $range, $z+$layer_h); + $layer_h += $diff * $quadratic_factor; + $z += $layer_h; + push (@{$self->{interactive_points}}, $z); + } + + # remove top layer if n-1 is higher than object_height!!! +} + +sub _interpolate_next_layer_h { + my ($self, $z) = @_; + my $layer_h = $self->{resulting_points}[0]; + my $array_size = @{$self->{resulting_points}}; + my $i = 1; + # find current layer + while(($self->{resulting_points}[$i] <= $z) && ($array_size-1 > $i)) { + $i += 1; + } + if($i == 1) {return $layer_h}; # first layer, nothing to interpolate + + $layer_h = $self->{resulting_points}[$i] - $self->{resulting_points}[$i-1]; + my $tmp = $layer_h; + # interpolate + if($array_size-1 > $i) { + my $next_layer_h = $self->{resulting_points}[$i+1] - $self->{resulting_points}[$i]; + $layer_h = $layer_h * ($self->{resulting_points}[$i] - $z)/$layer_h + $next_layer_h * min(1, ($z+$layer_h-$self->{resulting_points}[$i])/$next_layer_h); + } +# if(abs($layer_h - $tmp) > 0.001) { +# print "z: " . $z . "\n"; +# my $layer_i = $self->{resulting_points}[$i] - $self->{resulting_points}[$i-1]; +# my $layer_i1 = $self->{resulting_points}[$i+1] - $self->{resulting_points}[$i]; +# print "layer i: " . $layer_i . " -> " . $self->{resulting_points}[$i] . "\n"; +# print "layer i+1: " . $layer_i1 . " -> " . $self->{resulting_points}[$i+1] . "\n"; +# print "layer_h_1: " . $tmp . " layer_h_2: " . $layer_h . "\n\n"; +# } + return $layer_h; +} + +sub _quadratic_factor { + my ($self, $fixpoint, $range, $value) = @_; + + # avoid division by zero + $range = 0.00001 if $range <= 0; + + my $dist = abs($fixpoint - $value); + my $x = $dist/$range; # normalize + my $result = 1-($x*$x); + + print "fixpoint: " . $fixpoint . " range: " . $range . " value: " . $value . " result: " . $result . "\n"; + + return max(0, $result); +} + # Takes a 2-tupel [layer_height (x), height(y)] and converts it # into a Wx::Point in scaled canvas coordinates sub point_to_pixel { From 3409ad8ae96ef793d12fc7e60ef5a4257bf56ea9 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Fri, 20 Jan 2017 17:57:41 +0100 Subject: [PATCH 18/64] refresh layer control window if toolpath changes --- lib/Slic3r/GUI/Plater.pm | 22 +++++++++++---------- lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm | 5 +++++ 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index f7ca76bbc..b4fa04762 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -11,7 +11,7 @@ use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :panel :sizer :toolbar :window wxTheApp :notebook :combobox); use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL - EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED); + EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED EVT_CLOSE); use base 'Wx::Panel'; use constant TB_ADD => &Wx::NewId; @@ -988,6 +988,7 @@ sub async_apply_config { # reset preview canvases $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; $self->{preview3D}->reload_print if $self->{preview3D}; + $self->{ObjectLayersDialog}->reload_preview if $self->{ObjectLayersDialog}; # pause process thread before applying new config # since we don't want to touch data that is being used by the threads @@ -1063,6 +1064,7 @@ sub stop_background_process { $self->statusbar->SetStatusText(""); $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; $self->{preview3D}->reload_print if $self->{preview3D}; + $self->{ObjectLayersDialog}->reload_preview if $self->{ObjectLayersDialog}; if ($self->{process_thread}) { Slic3r::debugf "Killing background process.\n"; @@ -1204,6 +1206,7 @@ sub on_process_completed { return if $error; $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; $self->{preview3D}->reload_print if $self->{preview3D}; + $self->{ObjectLayersDialog}->reload_preview if $self->{ObjectLayersDialog}; # if we have an export filename, start a new thread for exporting G-code if ($self->{export_gcode_output_file}) { @@ -1579,18 +1582,18 @@ sub object_layers_dialog { return; } - my $dlg = Slic3r::GUI::Plater::ObjectLayersDialog->new($self, + $self->{ObjectLayersDialog} = Slic3r::GUI::Plater::ObjectLayersDialog->new($self, object => $self->{objects}[$obj_idx], model_object => $self->{model}->objects->[$obj_idx], obj_idx => $obj_idx, ); - return unless $dlg->ShowModal == wxID_OK; - - if (my @new_objects = $dlg->NewModelObjects) { - $self->remove($obj_idx); - $self->load_model_objects(grep defined($_), @new_objects); - $self->arrange; - } + $self->{ObjectLayersDialog}->Show(); + + EVT_CLOSE($self->{ObjectLayersDialog}, sub { + my ($dlg, $event) = @_; + $dlg->Destroy; + $self->{ObjectLayersDialog} = undef; + }); } sub object_settings_dialog { @@ -1735,7 +1738,6 @@ sub selected_object { sub refresh_canvases { my ($self) = @_; - $self->{canvas}->Refresh; $self->{canvas3D}->update if $self->{canvas3D}; $self->{preview3D}->reload_print if $self->{preview3D}; diff --git a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm index 76dffc548..6e8652aae 100644 --- a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm @@ -70,4 +70,9 @@ sub new { return $self; } +sub reload_preview { + my ($self) = @_; + $self->{preview3D}->reload_print; +} + 1; From c3f7a226a087835ec62d3de5394d1cf8d53bb77e Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 26 Jan 2017 08:44:34 +0100 Subject: [PATCH 19/64] Include external BSpline solver --- xs/src/BSpline/BSpline.cpp | 879 ++++++++++++++++++++++++++++++++++ xs/src/BSpline/BSpline.h | 452 +++++++++++++++++ xs/src/BSpline/BandedMatrix.h | 375 +++++++++++++++ xs/src/BSpline/COPYRIGHT | 44 ++ 4 files changed, 1750 insertions(+) create mode 100644 xs/src/BSpline/BSpline.cpp create mode 100644 xs/src/BSpline/BSpline.h create mode 100644 xs/src/BSpline/BandedMatrix.h create mode 100644 xs/src/BSpline/COPYRIGHT diff --git a/xs/src/BSpline/BSpline.cpp b/xs/src/BSpline/BSpline.cpp new file mode 100644 index 000000000..6cce57319 --- /dev/null +++ b/xs/src/BSpline/BSpline.cpp @@ -0,0 +1,879 @@ +// -*- mode: c++; c-basic-offset: 4; -*- +/************************************************************************ + * Copyright 2009 University Corporation for Atmospheric Research. + * All rights reserved. + * + * Use of this code is subject to the standard BSD license: + * + * http://www.opensource.org/licenses/bsd-license.html + * + * See the COPYRIGHT file in the source distribution for the license text, + * or see this web page: + * + * http://www.eol.ucar.edu/homes/granger/bspline/doc/ + * + *************************************************************************/ + +/** + * @file + * + * This file defines the implementation for the BSpline and BSplineBase + * templates. + **/ +#include "BSpline.h" +#include "BandedMatrix.h" + +#include +#include +#include +#include +#include +#include +#include + + + + + +/* + * Original BSplineBase.cpp start here + */ + +/* + * This class simulates a namespace for private symbols used by this template + * implementation which should not pollute the global namespace. + */ +class my +{ + public: + template static inline + T abs(const T t) + { + return (t < 0) ? -t : t; + } + + template static inline + const T& min(const T& a, + const T& b) + { + return (a < b) ? a : b; + } + + template static inline + const T& max(const T& a, + const T& b) + { + return (a > b) ? a : b; + } +}; + +////////////////////////////////////////////////////////////////////// +template class Matrix : public BandedMatrix +{ + public: + Matrix &operator +=(const Matrix &B) + { + Matrix &A = *this; + typename Matrix::size_type M = A.num_rows(); + typename Matrix::size_type N = A.num_cols(); + + assert(M==B.num_rows()); + assert(N==B.num_cols()); + + typename Matrix::size_type i, j; + for (i=0; i::operator= (e); + return *this; + } + + }; + ////////////////////////////////////////////////////////////////////// + // Our private state structure, which hides our use of some matrix + // template classes. + +template struct BSplineBaseP +{ + typedef Matrix MatrixT; + + MatrixT Q; // Holds P+Q and its factorization + std::vector X; + std::vector Nodes; +}; + +////////////////////////////////////////////////////////////////////// + +// This array contains the beta parameter for the boundary condition +// constraints. The boundary condition type--0, 1, or 2--is the first +// index into the array, followed by the index of the endpoints. See the +// Beta() method. +template const double BSplineBase::BoundaryConditions[3][4] = + { + // 0 1 M-1 M + { + -4, + -1, + -1, + -4 }, + { + 0, + 1, + 1, + 0 }, + { + 2, + -1, + -1, + 2 } }; +////////////////////////////////////////////////////////////////////// +template inline bool BSplineBase::Debug(int on) +{ + static bool debug = false; + if (on >= 0) + debug = (on > 0); + return debug; +} + +////////////////////////////////////////////////////////////////////// +template const double BSplineBase::BS_PI = 3.1415927; +////////////////////////////////////////////////////////////////////// +template const char * BSplineBase::ImplVersion() +{ + return ("$Id: BSpline.cpp 6352 2008-05-05 04:40:39Z martinc $"); +} + +////////////////////////////////////////////////////////////////////// +template const char * BSplineBase::IfaceVersion() +{ + return (_BSPLINEBASE_IFACE_ID); +} + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +template BSplineBase::~BSplineBase() +{ + delete base; +} + +// This is a member-wise copy except for replacing our +// private base structure with the source's, rather than just copying +// the pointer. But we use the compiler's default copy constructor for +// constructing our BSplineBaseP. +template BSplineBase::BSplineBase(const BSplineBase &bb) : + K(bb.K), BC(bb.BC), OK(bb.OK), base(new BSplineBaseP(*bb.base)) +{ + xmin = bb.xmin; + xmax = bb.xmax; + alpha = bb.alpha; + waveLength = bb.waveLength; + DX = bb.DX; + M = bb.M; + NX = base->X.size(); +} +////////////////////////////////////////////////////////////////////// +template BSplineBase::BSplineBase(const T *x, + int nx, + double wl, + int bc, + int num_nodes) : + NX(0), K(2), OK(false), base(new BSplineBaseP) +{ + setDomain(x, nx, wl, bc, num_nodes); +} + +////////////////////////////////////////////////////////////////////// +// Methods +template bool BSplineBase::setDomain(const T *x, + int nx, + double wl, + int bc, + int num_nodes) +{ + if ((nx <= 0) || (x == 0) || (wl< 0) || (bc< 0) || (bc> 2)) { + return false; + } + OK = false; + waveLength = wl; + BC = bc; + // Copy the x array into our storage. + base->X.resize(nx); + std::copy(x, x+nx, base->X.begin()); + NX = base->X.size(); + + // The Setup() method determines the number and size of node intervals. + if (Setup(num_nodes)) { + if (Debug()) { + std::cerr << "Using M node intervals: " << M << " of length DX: " + << DX << std::endl; + std::cerr << "X min: " << xmin << " ; X max: " << xmax << std::endl; + std::cerr << "Data points per interval: " << (float)NX/(float)M + << std::endl; + std::cerr << "Nodes per wavelength: " << (float)waveLength + /(float)DX << std::endl; + std::cerr << "Derivative constraint degree: " << K << std::endl; + } + + // Now we can calculate alpha and our Q matrix + alpha = Alpha(waveLength); + if (Debug()) { + std::cerr << "Cutoff wavelength: " << waveLength << " ; " + << "Alpha: " << alpha << std::endl; + std::cerr << "Calculating Q..." << std::endl; + } + calculateQ(); + if (Debug() && M < 30) { + std::cerr.fill(' '); + std::cerr.precision(2); + std::cerr.width(5); + std::cerr << base->Q << std::endl; + } + + if (Debug()) + std::cerr << "Calculating P..." << std::endl; + addP(); + if (Debug()) { + std::cerr << "Done." << std::endl; + if (M < 30) { + std::cerr << "Array Q after addition of P." << std::endl; + std::cerr << base->Q; + } + } + + // Now perform the LU factorization on Q + if (Debug()) + std::cerr << "Beginning LU factoring of P+Q..." << std::endl; + if (!factor()) { + if (Debug()) + std::cerr << "Factoring failed." << std::endl; + } else { + if (Debug()) + std::cerr << "Done." << std::endl; + OK = true; + } + } + return OK; +} +////////////////////////////////////////////////////////////////////// +/* + * Calculate the alpha parameter given a wavelength. + */ +template double BSplineBase::Alpha(double wl) +{ + // K is the degree of the derivative constraint: 1, 2, or 3 + double a = (double) (wl / (2 * BS_PI * DX)); + a *= a; // a^2 + if (K == 2) + a = a * a; // a^4 + else if (K == 3) + a = a * a * a; // a^6 + return a; +} +////////////////////////////////////////////////////////////////////// +/* + * Return the correct beta value given the node index. The value depends + * on the node index and the current boundary condition type. + */ +template inline double BSplineBase::Beta(int m) +{ + if (m > 1 && m < M-1) + return 0.0; + if (m >= M-1) + m -= M-3; + assert(0 <= BC && BC <= 2); + assert(0 <= m && m <= 3); + return BoundaryConditions[BC][m]; +} +////////////////////////////////////////////////////////////////////// +/* + * Given an array of y data points defined over the domain + * of x data points in this BSplineBase, create a BSpline + * object which contains the smoothed curve for the y array. + */ +template BSpline * BSplineBase::apply(const T *y) +{ + return new BSpline (*this, y); +} +////////////////////////////////////////////////////////////////////// +/* + * Evaluate the closed basis function at node m for value x, + * using the parameters for the current boundary conditions. + */ +template double BSplineBase::Basis(int m, + T x) +{ + double y = 0; + double xm = xmin + (m * DX); + double z = my::abs((double)(x - xm) / (double)DX); + if (z < 2.0) { + z = 2 - z; + y = 0.25 * (z*z*z); + z -= 1.0; + if (z > 0) + y -= (z*z*z); + } + + // Boundary conditions, if any, are an additional addend. + if (m == 0 || m == 1) + y += Beta(m) * Basis(-1, x); + else if (m == M-1 || m == M) + y += Beta(m) * Basis(M+1, x); + + return y; +} +////////////////////////////////////////////////////////////////////// +/* + * Evaluate the deriviative of the closed basis function at node m for + * value x, using the parameters for the current boundary conditions. + */ +template double BSplineBase::DBasis(int m, + T x) +{ + double dy = 0; + double xm = xmin + (m * DX); + double delta = (double)(x - xm) / (double)DX; + double z = my::abs(delta); + if (z < 2.0) { + z = 2.0 - z; + dy = 0.25 * z * z; + z -= 1.0; + + if (z > 0) { + dy -= z * z; + } + dy *= ((delta > 0) ? -1.0 : 1.0) * 3.0 / DX; + } + + // Boundary conditions, if any, are an additional addend. + if (m == 0 || m == 1) + dy += Beta(m) * DBasis(-1, x); + else if (m == M-1 || m == M) + dy += Beta(m) * DBasis(M+1, x); + + return dy; +} +////////////////////////////////////////////////////////////////////// +template double BSplineBase::qDelta(int m1, + int m2) +/* + * Return the integral of the product of the basis function derivative + * restricted to the node domain, 0 to M. + */ +{ + // These are the products of the Kth derivative of the + // normalized basis functions + // given a distance m nodes apart, qparts[K-1][m], 0 <= m <= 3 + // Each column is the integral over each unit domain, -2 to 2 + static const double qparts[3][4][4] = + { + { + { + 0.11250, + 0.63750, + 0.63750, + 0.11250 }, + { + 0.00000, + 0.13125, + -0.54375, + 0.13125 }, + { + 0.00000, + 0.00000, + -0.22500, + -0.22500 }, + { + 0.00000, + 0.00000, + 0.00000, + -0.01875 } }, + { + { + 0.75000, + 2.25000, + 2.25000, + 0.75000 }, + { + 0.00000, + -1.12500, + -1.12500, + -1.12500 }, + { + 0.00000, + 0.00000, + 0.00000, + 0.00000 }, + { + 0.00000, + 0.00000, + 0.00000, + 0.37500 } }, + { + { + 2.25000, + 20.25000, + 20.25000, + 2.25000 }, + { + 0.00000, + -6.75000, + -20.25000, + -6.75000 }, + { + 0.00000, + 0.00000, + 6.75000, + 6.75000 }, + { + 0.00000, + 0.00000, + 0.00000, + -2.25000 } } }; + + if (m1 > m2) + std::swap(m1, m2); + + if (m2 - m1 > 3) + return 0.0; + + double q = 0; + for (int m = my::max(m1-2, 0); m < my::min(m1+2, M); ++m) + q += qparts[K-1][m2-m1][m-m1+2]; + return q * alpha; +} +////////////////////////////////////////////////////////////////////// +template void BSplineBase::calculateQ() +{ + Matrix &Q = base->Q; + Q.setup(M+1, 3); + Q = 0; + if (alpha == 0) + return; + + // First fill in the q values without the boundary constraints. + int i; + for (i = 0; i <= M; ++i) { + Q[i][i] = qDelta(i, i); + for (int j = 1; j < 4 && i+j <= M; ++j) { + Q[i][i+j] = Q[i+j][i] = qDelta(i, i+j); + } + } + + // Now add the boundary constraints: + // First the upper left corner. + float b1, b2, q; + for (i = 0; i <= 1; ++i) { + b1 = Beta(i); + for (int j = i; j < i+4; ++j) { + b2 = Beta(j); + assert(j-i >= 0 && j - i < 4); + q = 0.0; + if (i+1 < 4) + q += b2*qDelta(-1, i); + if (j+1 < 4) + q += b1*qDelta(-1, j); + q += b1*b2*qDelta(-1, -1); + Q[j][i] = (Q[i][j] += q); + } + } + + // Then the lower right + for (i = M-1; i <= M; ++i) { + b1 = Beta(i); + for (int j = i - 3; j <= i; ++j) { + b2 = Beta(j); + q = 0.0; + if (M+1-i < 4) + q += b2*qDelta(i, M+1); + if (M+1-j < 4) + q += b1*qDelta(j, M+1); + q += b1*b2*qDelta(M+1, M+1); + Q[j][i] = (Q[i][j] += q); + } + } +} +////////////////////////////////////////////////////////////////////// +template void BSplineBase::addP() +{ + // Add directly to Q's elements + Matrix &P = base->Q; + std::vector &X = base->X; + + // For each data point, sum the product of the nearest, non-zero Basis + // nodes + int mx, m, n, i; + for (i = 0; i < NX; ++i) { + // Which node does this put us in? + T &x = X[i]; + mx = (int)((x - xmin) / DX); + + // Loop over the upper triangle of nonzero basis functions, + // and add in the products on each side of the diagonal. + for (m = my::max(0, mx-1); m <= my::min(M, mx+2); ++m) { + float pn; + float pm = Basis(m, x); + float sum = pm * pm; + P[m][m] += sum; + for (n = m+1; n <= my::min(M, mx+2); ++n) { + pn = Basis(n, x); + sum = pm * pn; + P[m][n] += sum; + P[n][m] += sum; + } + } + } +} +////////////////////////////////////////////////////////////////////// +template bool BSplineBase::factor() +{ + Matrix &LU = base->Q; + + if (LU_factor_banded(LU, 3) != 0) { + if (Debug()) + std::cerr << "LU_factor_banded() failed." << std::endl; + return false; + } + if (Debug() && M < 30) + std::cerr << "LU decomposition: " << std::endl << LU << std::endl; + return true; +} +////////////////////////////////////////////////////////////////////// +template inline double BSplineBase::Ratiod(int &ni, + double &deltax, + double &ratiof) +{ + deltax = (xmax - xmin) / ni; + ratiof = waveLength / deltax; + double ratiod = (double) NX / (double) (ni + 1); + return ratiod; +} +////////////////////////////////////////////////////////////////////// +// Setup the number of nodes (and hence deltax) for the given domain and +// cutoff wavelength. According to Ooyama, the derivative constraint +// approximates a lo-pass filter if the cutoff wavelength is about 4*deltax +// or more, but it should at least be 2*deltax. We can increase the number +// of nodes to increase the number of nodes per cutoff wavelength. +// However, to get a reasonable representation of the data, the setup +// enforces at least as many nodes as data points in the domain. (This +// constraint assumes reasonably even distribution of data points, since +// its really the density of data points which matters.) +// +// Return zero if the setup fails, non-zero otherwise. +// +// The algorithm in this routine is mostly taken from the FORTRAN +// implementation by James Franklin, NOAA/HRD. +// +template bool BSplineBase::Setup(int num_nodes) +{ + std::vector &X = base->X; + + // Find the min and max of the x domain + xmin = X[0]; + xmax = X[0]; + + int i; + for (i = 1; i < NX; ++i) { + if (X[i] < xmin) + xmin = X[i]; + else if (X[i] > xmax) + xmax = X[i]; + } + if (Debug()) + std::cerr << "Xmax=" << xmax << ", Xmin=" << xmin << std::endl; + + // Number of node intervals (number of spline nodes - 1). + int ni; + double deltax; + + if (num_nodes >= 2) { + // We've been told explicitly the number of nodes to use. + ni = num_nodes - 1; + if (waveLength == 0) { + waveLength = 1.0; + } + if (Debug()) + { + std::cerr << "Num nodes explicitly given as " << num_nodes + << ", wavelength set to " << waveLength << std::endl; + } + } else if (waveLength == 0) { + // Turn off frequency constraint and just set two node intervals per + // data point. + ni = NX * 2; + waveLength = 1; + if (Debug()) + { + std::cerr << "Frequency constraint disabled, using 2 intervals " + << "per node: " << ni << " intervals, wavelength=" + << waveLength << std::endl; + } + } else if (waveLength > xmax - xmin) { + if (Debug()) + { + std::cerr << "Wavelength " << waveLength << " exceeds X span: " + << xmin << " - " << xmax << std::endl; + } + return (false); + } else { + if (Debug()) + { + std::cerr << "Searching for a reasonable number of " + << "intervals for wavelength " << waveLength + << " while keeping at least 2 intervals per " + << "wavelength ..." << std::endl; + } + // Minimum acceptable number of node intervals per cutoff wavelength. + static const double fmin = 2.0; + + // Start out at a minimum number of intervals, meaning the maximum + // number of points per interval, then work up to the maximum + // number of intervals for which the intervals per wavelength is + // still adequate. I think the minimum must be more than 2 since + // the basis function is evaluated on multiple nodes. + ni = 5; + + double ratiof; // Nodes per wavelength for current deltax + double ratiod; // Points per node interval + + // Increase the number of node intervals until we reach the minimum + // number of intervals per cutoff wavelength, but only as long as + // we can maintain at least one point per interval. + do { + if (Ratiod(++ni, deltax, ratiof) < 1.0) + { + if (Debug()) + { + std::cerr << "At " << ni << " intervals, fewer than " + << "one point per interval, and " + << "intervals per wavelength is " + << ratiof << "." << std::endl; + } + return false; + } + } while (ratiof < fmin); + + // Now increase the number of intervals until we have at least 4 + // intervals per cutoff wavelength, but only as long as we can + // maintain at least 2 points per node interval. There's also no + // point to increasing the number of intervals if we already have + // 15 or more nodes per cutoff wavelength. + // + do { + if ((ratiod = Ratiod(++ni, deltax, ratiof)) < 1.0 || ratiof > 15.0) { + --ni; + break; + } + } while (ratiof < 4 || ratiod > 2.0); + + if (Debug()) + { + std::cerr << "Found " << ni << " intervals, " + << "length " << deltax << ", " + << ratiof << " nodes per wavelength " << waveLength + << ", " + << ratiod << " data points per interval." << std::endl; + } + } + + // Store the calculations in our state + M = ni; + DX = (xmax - xmin) / ni; + + return (true); +} +////////////////////////////////////////////////////////////////////// +template const T * BSplineBase::nodes(int *nn) +{ + if (base->Nodes.size() == 0) { + base->Nodes.reserve(M+1); + for (int i = 0; i <= M; ++i) { + base->Nodes.push_back(xmin + (i * DX)); + } + } + + if (nn) + *nn = base->Nodes.size(); + + assert(base->Nodes.size() == (unsigned)(M+1)); + return &base->Nodes[0]; +} +////////////////////////////////////////////////////////////////////// +template std::ostream &operator<<(std::ostream &out, + const std::vector &c) +{ + for (typename std::vector::const_iterator it = c.begin(); it < c.end(); ++it) + out << *it << ", "; + out << std::endl; + return out; +} + + + + + +/* + * Original BSpline.cpp start here + */ + + +////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////// +// BSpline Class +////////////////////////////////////////////////////////////////////// + +template struct BSplineP { + std::vector spline; + std::vector A; +}; + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +/* + * This BSpline constructor constructs and sets up a new base and + * solves for the spline curve coeffiecients all at once. + */ +template BSpline::BSpline(const T *x, + int nx, + const T *y, + double wl, + int bc_type, + int num_nodes) : + BSplineBase(x, nx, wl, bc_type, num_nodes), s(new BSplineP) { + solve(y); +} +////////////////////////////////////////////////////////////////////// +/* + * Create a new spline given a BSplineBase. + */ +template BSpline::BSpline(BSplineBase &bb, + const T *y) : + BSplineBase(bb), s(new BSplineP) { + solve(y); +} +////////////////////////////////////////////////////////////////////// +/* + * (Re)calculate the spline for the given set of y values. + */ +template bool BSpline::solve(const T *y) { + if (!OK) + return false; + + // Any previously calculated curve is now invalid. + s->spline.clear(); + OK = false; + + // Given an array of data points over x and its precalculated + // P+Q matrix, calculate the b vector and solve for the coefficients. + std::vector &B = s->A; + std::vector &A = s->A; + A.clear(); + A.resize(M+1); + + if (Debug()) + std::cerr << "Solving for B..." << std::endl; + + // Find the mean of these data + mean = 0.0; + int i; + for (i = 0; i < NX; ++i) { + mean += y[i]; + } + mean = mean / (double)NX; + if (Debug()) + std::cerr << "Mean for y: " << mean << std::endl; + + int mx, m, j; + for (j = 0; j < NX; ++j) { + // Which node does this put us in? + T &xj = base->X[j]; + T yj = y[j] - mean; + mx = (int)((xj - xmin) / DX); + + for (m = my::max(0, mx-1); m <= my::min(mx+2, M); ++m) { + B[m] += yj * Basis(m, xj); + } + } + + if (Debug() && M < 30) { + std::cerr << "Solution a for (P+Q)a = b" << std::endl; + std::cerr << " b: " << B << std::endl; + } + + // Now solve for the A vector in place. + if (LU_solve_banded(base->Q, A, 3) != 0) { + if (Debug()) + std::cerr << "LU_solve_banded() failed." << std::endl; + } else { + OK = true; + if (Debug()) + std::cerr << "Done." << std::endl; + if (Debug() && M < 30) { + std::cerr << " a: " << A << std::endl; + std::cerr << "LU factor of (P+Q) = " << std::endl << base->Q + << std::endl; + } + } + return (OK); +} +////////////////////////////////////////////////////////////////////// +template BSpline::~BSpline() { + delete s; +} +////////////////////////////////////////////////////////////////////// +template T BSpline::coefficient(int n) { + if (OK) + if (0 <= n && n <= M) + return s->A[n]; + return 0; +} +////////////////////////////////////////////////////////////////////// +template T BSpline::evaluate(T x) { + T y = 0; + if (OK) { + int n = (int)((x - xmin)/DX); + for (int i = my::max(0, n-1); i <= my::min(M, n+2); ++i) { + y += s->A[i] * Basis(i, x); + } + y += mean; + } + return y; +} +////////////////////////////////////////////////////////////////////// +template T BSpline::slope(T x) { + T dy = 0; + if (OK) { + int n = (int)((x - xmin)/DX); + for (int i = my::max(0, n-1); i <= my::min(M, n+2); ++i) { + dy += s->A[i] * DBasis(i, x); + } + } + return dy; +} + + + + + + + +/// Instantiate BSplineBase +template class BSplineBase; +//template class BSplineBase; + +/// Instantiate BSpline +template class BSpline; +//template class BSpline; diff --git a/xs/src/BSpline/BSpline.h b/xs/src/BSpline/BSpline.h new file mode 100644 index 000000000..9114385c1 --- /dev/null +++ b/xs/src/BSpline/BSpline.h @@ -0,0 +1,452 @@ +/* -*- mode: c++; c-basic-offset: 4; -*- */ +/************************************************************************ + * Copyright 2009 University Corporation for Atmospheric Research. + * All rights reserved. + * + * Use of this code is subject to the standard BSD license: + * + * http://www.opensource.org/licenses/bsd-license.html + * + * See the COPYRIGHT file in the source distribution for the license text, + * or see this web page: + * + * http://www.eol.ucar.edu/homes/granger/bspline/doc/ + * + *************************************************************************/ + +#ifndef BSPLINE_H +#define BSPLINE_H + +#include + + +/* + * Warning: original BSplineBase.h/cpp merged into BSpline.h/cpp to avoid dependency issues caused by Build::WithXSpp which tries to compile all .cpp files in /src + * Original BSplineBase.h starts here!!! + */ + + + +#ifndef _BSPLINEBASE_IFACE_ID +#define _BSPLINEBASE_IFACE_ID "$Id: BSpline.h 6353 2008-05-05 19:30:48Z martinc $" +#endif + +/** + * @file + * + * This file defines the BSpline library interface. + * + */ +template class BSpline; + +/* + * Opaque member structure to hide the matrix implementation. + */ +template struct BSplineBaseP; + +/** + * @class BSplineBase + * + * The base class for a spline object containing the nodes for a given + * domain, cutoff wavelength, and boundary condition. + + * To smooth a single curve, the BSpline interface contains a constructor + * which both sets up the domain and solves for the spline. Subsequent + * curves over the same domain can be created by apply()ing them to the + * BSpline object, where apply() is a BSplineBase method. [See apply().] + * New curves can also be smoothed within the same BSpline object by + * calling solve() with the new set of y values. [See BSpline.] A + * BSplineBase can be created on its own, in which case all of the + * computations dependent on the x values, boundary conditions, and cutoff + * wavelength have already been completed. + * + * The solution of the cubic b-spline is divided into two parts. The first + * is the setup of the domain given the x values, boundary conditions, and + * wavelength. The second is the solution of the spline for a set of y + * values corresponding to the x values in the domain. The first part is + * done in the creation of the BSplineBase object (or when calling the + * setDomain method). The second part is done when creating a BSpline + * object (or calling solve() on a BSpline object). + * + * A BSpline object can be created with either one of its constructors, or + * by calling apply() on an existing BSplineBase object. Once a spline has + * been solved, it can be evaluated at any x value. The following example + * creates a spline curve and evaluates it over the domain: + * +@verbatim + + vector x; + vector y; + { ... } + int bc = BSplineBase::BC_ZERO_SECOND; + BSpline::Debug = true; + BSpline spline (x.begin(), x.size(), y.begin(), wl, bc); + if (spline.ok()) + { + ostream_iterator of(cout, "\t "); + float xi = spline.Xmin(); + float xs = (spline.Xmax() - xi) / 2000.0; + for (; xi <= spline.Xmax(); xi += xs) + { + *of++ = spline.evaluate (xi); + } + } + +@endverbatim + * + * In the usual usage, the BSplineBase can compute a reasonable number of + * nodes for the spline, balancing between a few desirable factors. There + * needs to be at least 2 nodes per cutoff wavelength (preferably 4 or + * more) for the derivative constraint to reliably approximate a lo-pass + * filter. There should be at least 1 and preferably about 2 data points + * per node (measured just by their number and not by any check of the + * density of points across the domain). Lastly, of course, the fewer the + * nodes then the faster the computation of the spline. The computation of + * the number of nodes happens in the Setup() method during BSplineBase + * construction and when setDomain() is called. If the setup fails to find + * a desirable number of nodes, then the BSplineBase object will return + * false from ok(). + * + * The ok() method returns false when a BSplineBase or BSpline could not + * complete any operation successfully. In particular, as mentioned above, + * ok() will return false if some problem was detected with the domain + * values or if no reasonable number of nodes could be found for the given + * cutoff wavelength. Also, ok() on a BSpline object will return false if + * the matrix equation could not be solved, such as after BSpline + * construction or after a call to apply(). + * + * If letting Setup() determine the number of nodes is not acceptable, the + * constructors and setDomain() accept the parameter num_nodes. By + * default, num_nodes is passed as zero, forcing Setup() to calculate the + * number of nodes. However, if num_nodes is passed as 2 or greater, then + * Setup() will bypass its own algorithm and accept the given number of + * nodes instead. Obviously, it's up to the programmer to understand the + * affects of the number of nodes on the representation of the data and on + * the solution (or non-solution) of the spline. Remember to check the + * ok() method to detect when the spline solution has failed. + * + * The interface for the BSplineBase and BSpline templates is defined in + * the header file BSpline.h. The implementation is defined in BSpline.cpp. + * Source files which will instantiate the template should include the + * implementation file and @em not the interface. If the implementation + * for a specific type will be linked from elsewhere, such as a + * static library or Windows DLL, source files should only include the + * interface file. On Windows, applications should link with the import + * library BSpline.lib and make sure BSpline.dll is on the path. The DLL + * contains an implementation for BSpline and BSpline. + * For debugging, an application can include the implementation to get its + * own instantiation. + * + * The algorithm is based on the cubic spline described by Katsuyuki Ooyama + * in Montly Weather Review, Vol 115, October 1987. This implementation + * has benefited from comparisons with a previous FORTRAN implementation by + * James L. Franklin, NOAA/Hurricane Research Division. In particular, the + * algorithm in the Setup() method is based mostly on his implementation + * (VICSETUP). The Setup() method finds a suitable default for the number + * of nodes given a domain and cutoff frequency. This implementation + * adopts most of the same constraints, including a constraint that the + * cutoff wavelength not be greater than the span of the domain values: wl + * < max(x) - min(x). If this is not an acceptable constraint, then use the + * num_nodes parameter to specify the number of nodes explicitly. + * + * The cubic b-spline is formulated as the sum of some multiple of the + * basis function centered at each node in the domain. The number of nodes + * is determined by the desired cutoff wavelength and a desirable number of + * x values per node. The basis function is continuous and differentiable + * up to the second degree. A derivative constraint is included in the + * solution to achieve the effect of a low-pass frequency filter with the + * given cutoff wavelength. The derivative constraint can be disabled by + * specifying a wavelength value of zero, which reduces the analysis to a + * least squares fit to a cubic b-spline. The domain nodes, boundary + * constraints, and wavelength determine a linear system of equations, + * Qa=b, where a is the vector of basis function coefficients at each node. + * The coefficient vector is solved by first LU factoring along the + * diagonally banded matrix Q in BSplineBase. The BSpline object then + * computes the B vector for a set of y values and solves for the + * coefficient vector with the LU matrix. Only the diagonal bands are + * stored in memory and calculated during LU factoring and back + * substitution, and the basis function is evaluated as few times as + * possible in computing the diagonal matrix and B vector. + * + * @author Gary Granger (http://www.eol.ucar.edu/homes/granger) + * +@verbatim +Copyright (c) 1998-2009 +University Corporation for Atmospheric Research, UCAR +All rights reserved. +@endverbatim + **/ + +template +class BSplineBase +{ +public: + // Datum type + typedef T datum_type; + + /// Return a string describing the implementation version. + static const char *ImplVersion(); + + /// Return a string describing the interface version. + static const char *IfaceVersion(); + + /** + * Call this class method with a value greater than zero to enable + * debug messages, or with zero to disable messages. Calling with + * no arguments returns true if debugging enabled, else false. + */ + static bool Debug (int on = -1); + + /** + * Boundary condition types. + */ + enum BoundaryConditionTypes + { + /// Set the endpoints of the spline to zero. + BC_ZERO_ENDPOINTS = 0, + /// Set the first derivative of the spline to zero at the endpoints. + BC_ZERO_FIRST = 1, + /// Set the second derivative to zero. + BC_ZERO_SECOND = 2 + }; + +public: + + /** + * Construct a spline domain for the given set of x values, cutoff + * wavelength, and boundary condition type. The parameters are the + * same as for setDomain(). Call ok() to check whether domain + * setup succeeded after construction. + */ + BSplineBase (const T *x, int nx, + double wl, int bc_type = BC_ZERO_SECOND, + int num_nodes = 0); + + /// Copy constructor + BSplineBase (const BSplineBase &); + + /** + * Change the domain of this base. [If this is part of a BSpline + * object, this method {\em will not} change the existing curve or + * re-apply the smoothing to any set of y values.] + * + * The x values can be in any order, but they must be of sufficient + * density to support the requested cutoff wavelength. The setup of + * the domain may fail because of either inconsistency between the x + * density and the cutoff wavelength, or because the resulting matrix + * could not be factored. If setup fails, the method returns false. + * + * @param x The array of x values in the domain. + * @param nx The number of values in the @p x array. + * @param wl The cutoff wavelength, in the same units as the + * @p x values. A wavelength of zero disables + * the derivative constraint. + * @param bc_type The enumerated boundary condition type. If + * omitted it defaults to BC_ZERO_SECOND. + * @param num_nodes The number of nodes to use for the cubic b-spline. + * If less than 2 a reasonable number will be + * calculated automatically, if possible, taking + * into account the given cutoff wavelength. + * + * @see ok(). + */ + bool setDomain (const T *x, int nx, double wl, + int bc_type = BC_ZERO_SECOND, + int num_nodes = 0); + + /** + * Create a BSpline smoothed curve for the given set of NX y values. + * The returned object will need to be deleted by the caller. + * @param y The array of y values corresponding to each of the nX() + * x values in the domain. + * @see ok() + */ + BSpline *apply (const T *y); + + /** + * Return array of the node coordinates. Returns 0 if not ok(). The + * array of nodes returned by nodes() belongs to the object and should + * not be deleted; it will also be invalid if the object is destroyed. + */ + const T *nodes (int *nnodes); + + /** + * Return the number of nodes (one more than the number of intervals). + */ + int nNodes () { return M+1; } + + /** + * Number of original x values. + */ + int nX () { return NX; } + + /// Minimum x value found. + T Xmin () { return xmin; } + + /// Maximum x value found. + T Xmax () { return xmin + (M * DX); } + + /** + * Return the Alpha value for a given wavelength. Note that this + * depends on the current node interval length (DX). + */ + double Alpha (double wavelength); + + /** + * Return alpha currently in use by this domain. + */ + double Alpha () { return alpha; } + + /** + * Return the current state of the object, either ok or not ok. + * Use this method to test for valid state after construction or after + * a call to setDomain(). ok() will return false if either fail, such + * as when an appropriate number of nodes and node interval cannot be + * found for a given wavelength, or when the linear equation for the + * coefficients cannot be solved. + */ + bool ok () { return OK; } + + virtual ~BSplineBase(); + +protected: + + typedef BSplineBaseP Base; + + // Provided + double waveLength; // Cutoff wavelength (l sub c) + int NX; + int K; // Degree of derivative constraint (currently fixed at 2) + int BC; // Boundary conditions type (0,1,2) + + // Derived + T xmax; + T xmin; + int M; // Number of intervals (M+1 nodes) + double DX; // Interval length in same units as X + double alpha; + bool OK; + Base *base; // Hide more complicated state members + // from the public interface. + + bool Setup (int num_nodes = 0); + void calculateQ (); + double qDelta (int m1, int m2); + double Beta (int m); + void addP (); + bool factor (); + double Basis (int m, T x); + double DBasis (int m, T x); + + static const double BoundaryConditions[3][4]; + static const double BS_PI; + + double Ratiod (int&, double &, double &); +}; + + + +/* + * Original BSpline.h start here + */ + + + +template struct BSplineP; + + +/** + * Used to evaluate a BSpline. + * Inherits the BSplineBase domain information and interface and adds + * smoothing. See the BSplineBase documentation for a summary of the + * BSpline interface. + */ +template +class BSpline : public BSplineBase +{ +public: + /** + * Create a single spline with the parameters required to set up + * the domain and subsequently smooth the given set of y values. + * The y values must correspond to each of the values in the x array. + * If either the domain setup fails or the spline cannot be solved, + * the state will be set to not ok. + * + * @see ok(). + * + * @param x The array of x values in the domain. + * @param nx The number of values in the @p x array. + * @param y The array of y values corresponding to each of the + * nX() x values in the domain. + * @param wl The cutoff wavelength, in the same units as the + * @p x values. A wavelength of zero disables + * the derivative constraint. + * @param bc_type The enumerated boundary condition type. If + * omitted it defaults to BC_ZERO_SECOND. + * @param num_nodes The number of nodes to use for the cubic b-spline. + * If less than 2 a "reasonable" number will be + * calculated automatically, taking into account + * the given cutoff wavelength. + */ + BSpline (const T *x, int nx, /* independent variable */ + const T *y, /* dependent values @ ea X */ + double wl, /* cutoff wavelength */ + int bc_type = BSplineBase::BC_ZERO_SECOND, + int num_nodes = 0); + + /** + * A BSpline curve can be derived from a separate @p base and a set + * of data points @p y over that base. + */ + BSpline (BSplineBase &base, const T *y); + + /** + * Solve the spline curve for a new set of y values. Returns false + * if the solution fails. + * + * @param y The array of y values corresponding to each of the nX() + * x values in the domain. + */ + bool solve (const T *y); + + /** + * Return the evaluation of the smoothed curve + * at a particular @p x value. If current state is not ok(), returns 0. + */ + T evaluate (T x); + + /** + * Return the first derivative of the spline curve at the given @p x. + * Returns zero if the current state is not ok(). + */ + T slope (T x); + + /** + * Return the @p n-th basis coefficient, from 0 to M. If the current + * state is not ok(), or @p n is out of range, the method returns zero. + */ + T coefficient (int n); + + virtual ~BSpline(); + + using BSplineBase::Debug; + using BSplineBase::Basis; + using BSplineBase::DBasis; + +protected: + + using BSplineBase::OK; + using BSplineBase::M; + using BSplineBase::NX; + using BSplineBase::DX; + using BSplineBase::base; + using BSplineBase::xmin; + using BSplineBase::xmax; + + // Our hidden state structure + BSplineP *s; + T mean; // Fit without mean and add it in later + +}; + +#endif diff --git a/xs/src/BSpline/BandedMatrix.h b/xs/src/BSpline/BandedMatrix.h new file mode 100644 index 000000000..bbe526a8b --- /dev/null +++ b/xs/src/BSpline/BandedMatrix.h @@ -0,0 +1,375 @@ +/* -*- mode: c++; c-basic-offset: 4; -*- */ +/************************************************************************ + * Copyright 2009 University Corporation for Atmospheric Research. + * All rights reserved. + * + * Use of this code is subject to the standard BSD license: + * + * http://www.opensource.org/licenses/bsd-license.html + * + * See the COPYRIGHT file in the source distribution for the license text, + * or see this web page: + * + * http://www.eol.ucar.edu/homes/granger/bspline/doc/ + * + *************************************************************************/ + +/** + * Template for a diagonally banded matrix. + **/ +#ifndef _BANDEDMATRIX_ID +#define _BANDEDMATRIX_ID "$Id$" + +#include +#include +#include + +template class BandedMatrixRow; + + +template class BandedMatrix +{ +public: + typedef unsigned int size_type; + typedef T element_type; + + // Create a banded matrix with the same number of bands above and below + // the diagonal. + BandedMatrix (int N_ = 1, int nbands_off_diagonal = 0) : bands(0) + { + if (! setup (N_, nbands_off_diagonal)) + setup (); + } + + // Create a banded matrix by naming the first and last non-zero bands, + // where the diagonal is at zero, and bands below the diagonal are + // negative, bands above the diagonal are positive. + BandedMatrix (int N_, int first, int last) : bands(0) + { + if (! setup (N_, first, last)) + setup (); + } + + // Copy constructor + BandedMatrix (const BandedMatrix &b) : bands(0) + { + Copy (*this, b); + } + + inline bool setup (int N_ = 1, int noff = 0) + { + return setup (N_, -noff, noff); + } + + bool setup (int N_, int first, int last) + { + // Check our limits first and make sure they make sense. + // Don't change anything until we know it will work. + if (first > last || N_ <= 0) + return false; + + // Need at least as many N_ as columns and as rows in the bands. + if (N_ < abs(first) || N_ < abs(last)) + return false; + + top = last; + bot = first; + N = N_; + out_of_bounds = T(); + + // Finally setup the diagonal vectors + nbands = last - first + 1; + if (bands) delete[] bands; + bands = new std::vector[nbands]; + int i; + for (i = 0; i < nbands; ++i) + { + // The length of each array varies with its distance from the + // diagonal + int len = N - (abs(bot + i)); + bands[i].clear (); + bands[i].resize (len); + } + return true; + } + + BandedMatrix & operator= (const BandedMatrix &b) + { + return Copy (*this, b); + } + + BandedMatrix & operator= (const T &e) + { + int i; + for (i = 0; i < nbands; ++i) + { + std::fill_n (bands[i].begin(), bands[i].size(), e); + } + out_of_bounds = e; + return (*this); + } + + ~BandedMatrix () + { + if (bands) + delete[] bands; + } + +private: + // Return false if coordinates are out of bounds + inline bool check_bounds (int i, int j, int &v, int &e) const + { + v = (j - i) - bot; + e = (i >= j) ? j : i; + return !(v < 0 || v >= nbands || + e < 0 || (unsigned int)e >= bands[v].size()); + } + + static BandedMatrix & Copy (BandedMatrix &a, const BandedMatrix &b) + { + if (a.bands) delete[] a.bands; + a.top = b.top; + a.bot = b.bot; + a.N = b.N; + a.out_of_bounds = b.out_of_bounds; + a.nbands = a.top - a.bot + 1; + a.bands = new std::vector[a.nbands]; + int i; + for (i = 0; i < a.nbands; ++i) + { + a.bands[i] = b.bands[i]; + } + return a; + } + +public: + T &element (int i, int j) + { + int v, e; + if (check_bounds(i, j, v, e)) + return (bands[v][e]); + else + return out_of_bounds; + } + + const T &element (int i, int j) const + { + int v, e; + if (check_bounds(i, j, v, e)) + return (bands[v][e]); + else + return out_of_bounds; + } + + inline T & operator() (int i, int j) + { + return element (i-1,j-1); + } + + inline const T & operator() (int i, int j) const + { + return element (i-1,j-1); + } + + size_type num_rows() const { return N; } + + size_type num_cols() const { return N; } + + const BandedMatrixRow operator[] (int row) const + { + return BandedMatrixRow(*this, row); + } + + BandedMatrixRow operator[] (int row) + { + return BandedMatrixRow(*this, row); + } + + +private: + + int top; + int bot; + int nbands; + std::vector *bands; + int N; + T out_of_bounds; + +}; + + +template +std::ostream &operator<< (std::ostream &out, const BandedMatrix &m) +{ + unsigned int i, j; + for (i = 0; i < m.num_rows(); ++i) + { + for (j = 0; j < m.num_cols(); ++j) + { + out << m.element (i, j) << " "; + } + out << std::endl; + } + return out; +} + + + +/* + * Helper class for the intermediate in the [][] operation. + */ +template class BandedMatrixRow +{ +public: + BandedMatrixRow (const BandedMatrix &_m, int _row) : bm(_m), i(_row) + { } + + BandedMatrixRow (BandedMatrix &_m, int _row) : bm(_m), i(_row) + { } + + ~BandedMatrixRow () {} + + typename BandedMatrix::element_type & operator[] (int j) + { + return const_cast &>(bm).element (i, j); + } + + const typename BandedMatrix::element_type & operator[] (int j) const + { + return bm.element (i, j); + } + +private: + const BandedMatrix &bm; + int i; +}; + + +/* + * Vector multiplication + */ + +template +Vector operator* (const Matrix &m, const Vector &v) +{ + typename Matrix::size_type M = m.num_rows(); + typename Matrix::size_type N = m.num_cols(); + + assert (N <= v.size()); + //if (M > v.size()) + // return Vector(); + + Vector r(N); + for (unsigned int i = 0; i < M; ++i) + { + typename Matrix::element_type sum = 0; + for (unsigned int j = 0; j < N; ++j) + { + sum += m[i][j] * v[j]; + } + r[i] = sum; + } + return r; +} + + + +/* + * LU factor a diagonally banded matrix using Crout's algorithm, but + * limiting the trailing sub-matrix multiplication to the non-zero + * elements in the diagonal bands. Return nonzero if a problem occurs. + */ +template +int LU_factor_banded (MT &A, unsigned int bands) +{ + typename MT::size_type M = A.num_rows(); + typename MT::size_type N = A.num_cols(); + if (M != N) + return 1; + + typename MT::size_type i,j,k; + typename MT::element_type sum; + + for (j = 1; j <= N; ++j) + { + // Check for zero pivot + if ( A(j,j) == 0 ) + return 1; + + // Calculate rows above and on diagonal. A(1,j) remains as A(1,j). + for (i = (j > bands) ? j-bands : 1; i <= j; ++i) + { + sum = 0; + for (k = (j > bands) ? j-bands : 1; k < i; ++k) + { + sum += A(i,k)*A(k,j); + } + A(i,j) -= sum; + } + + // Calculate rows below the diagonal. + for (i = j+1; (i <= M) && (i <= j+bands); ++i) + { + sum = 0; + for (k = (i > bands) ? i-bands : 1; k < j; ++k) + { + sum += A(i,k)*A(k,j); + } + A(i,j) = (A(i,j) - sum) / A(j,j); + } + } + + return 0; +} + + + +/* + * Solving (LU)x = B. First forward substitute to solve for y, Ly = B. + * Then backwards substitute to find x, Ux = y. Return nonzero if a + * problem occurs. Limit the substitution sums to the elements on the + * bands above and below the diagonal. + */ +template +int LU_solve_banded(const MT &A, Vector &b, unsigned int bands) +{ + typename MT::size_type i,j; + typename MT::size_type M = A.num_rows(); + typename MT::size_type N = A.num_cols(); + typename MT::element_type sum; + + if (M != N || M == 0) + return 1; + + // Forward substitution to find y. The diagonals of the lower + // triangular matrix are taken to be 1. + for (i = 2; i <= M; ++i) + { + sum = b[i-1]; + for (j = (i > bands) ? i-bands : 1; j < i; ++j) + { + sum -= A(i,j)*b[j-1]; + } + b[i-1] = sum; + } + + // Now for the backward substitution + b[M-1] /= A(M,M); + for (i = M-1; i >= 1; --i) + { + if (A(i,i) == 0) // oops! + return 1; + sum = b[i-1]; + for (j = i+1; (j <= N) && (j <= i+bands); ++j) + { + sum -= A(i,j)*b[j-1]; + } + b[i-1] = sum / A(i,i); + } + + return 0; +} + + +#endif /* _BANDEDMATRIX_ID */ + diff --git a/xs/src/BSpline/COPYRIGHT b/xs/src/BSpline/COPYRIGHT new file mode 100644 index 000000000..50c77bb13 --- /dev/null +++ b/xs/src/BSpline/COPYRIGHT @@ -0,0 +1,44 @@ +Copyright (c) 1998-2009,2015 +University Corporation for Atmospheric Research, UCAR +All rights reserved. + +This software is licensed with the standard BSD license: + + http://www.opensource.org/licenses/bsd-license.html + +When citing this software, here is a suggested reference: + + This software is written by Gary Granger of the National Center for + Atmospheric Research (NCAR), sponsored by the National Science Foundation + (NSF). The algorithm is based on the cubic spline described by Katsuyuki + Ooyama in Montly Weather Review, Vol 115, October 1987. This + implementation has benefited from comparisons with a previous FORTRAN + implementation by James L. Franklin, NOAA/Hurricane Research Division. + +The text of the license is reproduced below: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the UCAR nor the names of its contributors may be + used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. From 97f4301398847151d60848b7fcf0012e627454c8 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 26 Jan 2017 08:49:48 +0100 Subject: [PATCH 20/64] Class to interpolate layer height distribution based on BSplines --- xs/MANIFEST | 3 + xs/lib/Slic3r/XS.pm | 1 + xs/src/libslic3r/LayerHeightSpline.cpp | 194 +++++++++++++++++++++++++ xs/src/libslic3r/LayerHeightSpline.hpp | 40 +++++ xs/src/perlglue.cpp | 1 + xs/xsp/LayerHeightSpline.xsp | 23 +++ xs/xsp/my.map | 3 + xs/xsp/typemap.xspt | 3 + 8 files changed, 268 insertions(+) create mode 100644 xs/src/libslic3r/LayerHeightSpline.cpp create mode 100644 xs/src/libslic3r/LayerHeightSpline.hpp create mode 100644 xs/xsp/LayerHeightSpline.xsp diff --git a/xs/MANIFEST b/xs/MANIFEST index 4e544d472..fcc323bb9 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -43,6 +43,8 @@ src/libslic3r/IO.hpp src/libslic3r/Layer.cpp src/libslic3r/Layer.hpp src/libslic3r/LayerRegion.cpp +src/libslic3r/LayerHeightSpline.hpp +src/libslic3r/LayerHeightSpline.cpp src/libslic3r/libslic3r.h src/libslic3r/Line.cpp src/libslic3r/Line.hpp @@ -139,6 +141,7 @@ xsp/Geometry.xsp xsp/GUI.xsp xsp/GUI_3DScene.xsp xsp/Layer.xsp +xsp/LayerHeightSpline.xsp xsp/Line.xsp xsp/Model.xsp xsp/MotionPlanner.xsp diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index 4f53966de..e0c0268a9 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -228,6 +228,7 @@ for my $class (qw( Slic3r::Layer Slic3r::Layer::Region Slic3r::Layer::Support + Slic3r::LayerHeightSpline Slic3r::Line Slic3r::Linef3 Slic3r::Model diff --git a/xs/src/libslic3r/LayerHeightSpline.cpp b/xs/src/libslic3r/LayerHeightSpline.cpp new file mode 100644 index 000000000..5b3e59885 --- /dev/null +++ b/xs/src/libslic3r/LayerHeightSpline.cpp @@ -0,0 +1,194 @@ +/* + * This class represents a set of layers and their heights. + * It is intended for smoothing the height distribution (avoid very thin + * layers next to thick layers) and to correctly interpolate higher layers if + * a layer height changes somewhere in a lower position at the object. + * Uses http://www.eol.ucar.edu/homes/granger/bspline/doc/ for spline computation. + */ + +#include "LayerHeightSpline.hpp" +#include // std::abs + +namespace Slic3r { + +LayerHeightSpline::LayerHeightSpline(coordf_t object_height) +: _object_height(object_height), _layer_height_spline(NULL) +{ + this->_is_valid = false; + this->_update_required = true; +} + +LayerHeightSpline::~LayerHeightSpline() +{ + if (this->_layer_height_spline) { + delete this->_layer_height_spline; + } +} + +/* + * Indicates whether the object has valid data and the spline was successfully computed or not. + */ +bool LayerHeightSpline::hasData() +{ + return this->_is_valid; +} + +/* + * Does this object expect new layer heights during the slice step or should + * we use the layer heights values provided by the spline? + * An update is required if a config option is changed which affects the layer height. + * An update is not required if the spline was modified by a user interaction. + */ +bool LayerHeightSpline::updateRequired() +{ + bool result = true; // update spline by default + if(!this->_update_required && this->_is_valid) { + result = false; + } + this->_update_required = true; // reset to default after request + return result; +} + +/* + * Don't require an update for exactly one iteration. + */ +void LayerHeightSpline::suppressUpdate() { + if (this->_is_valid) { + this->_update_required = false; + } +} + +/* + * Set absolute layer positions in object coordinates. + * Heights (thickness of each layer) is generated from this list. + */ +bool LayerHeightSpline::setLayers(std::vector layers) +{ + this->_original_layers = layers; + + // generate updated layer height list from layers + this->_internal_layer_heights.clear(); + coordf_t last_z = 0; + for (std::vector::const_iterator l = this->_original_layers.begin(); l != this->_original_layers.end(); ++l) { + this->_internal_layer_heights.push_back(*l-last_z); + last_z = *l; + } + + // add 0-values at both ends to achieve correct boundary conditions + this->_internal_layers = this->_original_layers; + this->_internal_layers.insert(this->_internal_layers.begin(), 0); // add z = 0 to the front + this->_internal_layers.push_back(this->_internal_layers.back()+1); // and object_height + 1 to the end + this->_internal_layer_heights.insert(this->_internal_layer_heights.begin(), 0); + this->_internal_layer_heights.push_back(0); + + return this->_updateBSpline(); +} + + +/* + * Update only the desired thickness of the layers, but not their positions! + * This modifies the y-values for the spline computation and only affects + * the resulting layers which can be obtained with getInterpolatedLayers. + * The argument vector must be of the same size as the layers vector. + */ +bool LayerHeightSpline::updateLayerHeights(std::vector heights) +{ + bool result = false; + + // do we receive the correct number of values? + if(heights.size() == this->_internal_layers.size()-2) { + this->_internal_layer_heights = heights; + // add leading an trailing 0-value + this->_internal_layer_heights.insert(this->_internal_layer_heights.begin(), 0); + this->_internal_layer_heights.push_back(0); + result = this->_updateBSpline(); + } + + return result; +} + +/* + * Reset the this object, remove database and interpolated results. + */ +void LayerHeightSpline::clear() +{ + this->_original_layers.clear(); + this->_internal_layers.clear(); + this->_internal_layer_heights.clear(); + delete this->_layer_height_spline; + this->_layer_height_spline = NULL; + this->_is_valid = false; +} + + +/* + * Get a full set of layer z-positions by interpolation along the spline. + */ +std::vector LayerHeightSpline::getInterpolatedLayers() const +{ + std::vector layers; + if(this->_is_valid) { + // preserve first layer for bed contact + layers.push_back(this->_original_layers[0]); + coordf_t z = this->_original_layers[0]; + coordf_t h; + coordf_t h_diff = 0; + coordf_t eps = 0.0001; + while(z <= this->_object_height) { + h = 0; + // find intersection between layer height and spline + do { + h += h_diff/2; + h = this->_layer_height_spline->evaluate(z+h); + h_diff = this->_layer_height_spline->evaluate(z+h) - h; + } while(std::abs(h_diff) > eps); + z += h; + layers.push_back(z); + } + // how to make sure, the last layer is not higher than object while maintaining between min/max layer height? + } + return layers; +} + +/* + * Evaluate interpolated layer height (thickness) at given z-position + */ +const coordf_t LayerHeightSpline::getLayerHeightAt(coordf_t height) +{ + coordf_t result = 0; + if (this->_is_valid) { + result = this->_layer_height_spline->evaluate(height); + } + return result; +} + +/* + * Internal method to re-compute the spline + */ +bool LayerHeightSpline::_updateBSpline() +{ + bool result = false; + //TODO: exception if not enough points? + + delete this->_layer_height_spline; + this->_layer_height_spline = new BSpline(&this->_internal_layers[0], + this->_internal_layers.size(), + &this->_internal_layer_heights[0], + 0, + 0, + 0); + + if (this->_layer_height_spline->ok()) { + result = true; + } else { + result = false; + std::cerr << "Spline setup failed." << std::endl; + } + + this->_is_valid = result; + + return result; +} + + +} diff --git a/xs/src/libslic3r/LayerHeightSpline.hpp b/xs/src/libslic3r/LayerHeightSpline.hpp new file mode 100644 index 000000000..ea02b93ae --- /dev/null +++ b/xs/src/libslic3r/LayerHeightSpline.hpp @@ -0,0 +1,40 @@ +#ifndef slic3r_LayerHeightSpline_hpp_ +#define slic3r_LayerHeightSpline_hpp_ + +#include "libslic3r.h" +#include "BSpline/BSpline.h" // Warning: original BSplineBase.h/cpp merged into BSpline.h/cpp to avoid dependency issues caused by Build::WithXSpp which tries to compile all .cpp files in /src + + +namespace Slic3r { + + +class LayerHeightSpline +{ + public: + LayerHeightSpline(coordf_t object_height); + ~LayerHeightSpline(); + bool hasData(); // indicate that we have valid data + bool updateRequired(); // indicate whether we want to generate a new spline from the layers + void suppressUpdate(); + bool setLayers(std::vector layers); + bool updateLayerHeights(std::vector heights); + void clear(); + std::vector getOriginalLayers() const { return this->_original_layers; }; + std::vector getInterpolatedLayers() const; + const coordf_t getLayerHeightAt(coordf_t height); + + private: + bool _updateBSpline(); + + coordf_t _object_height; + bool _is_valid; + bool _update_required; // this should be always true except if we want to generate new layers from this spline + std::vector _original_layers; + std::vector _internal_layers; + std::vector _internal_layer_heights; + BSpline *_layer_height_spline; +}; + +} + +#endif diff --git a/xs/src/perlglue.cpp b/xs/src/perlglue.cpp index d8621ac91..5cdd45688 100644 --- a/xs/src/perlglue.cpp +++ b/xs/src/perlglue.cpp @@ -20,6 +20,7 @@ REGISTER_CLASS(GCodeWriter, "GCode::Writer"); REGISTER_CLASS(Layer, "Layer"); REGISTER_CLASS(SupportLayer, "Layer::Support"); REGISTER_CLASS(LayerRegion, "Layer::Region"); +REGISTER_CLASS(LayerHeightSpline, "LayerHeightSpline"); REGISTER_CLASS(Line, "Line"); REGISTER_CLASS(Linef3, "Linef3"); REGISTER_CLASS(PerimeterGenerator, "Layer::PerimeterGenerator"); diff --git a/xs/xsp/LayerHeightSpline.xsp b/xs/xsp/LayerHeightSpline.xsp new file mode 100644 index 000000000..ad5c5e58e --- /dev/null +++ b/xs/xsp/LayerHeightSpline.xsp @@ -0,0 +1,23 @@ +%module{Slic3r::XS}; + +%{ +#include +#include "libslic3r/LayerHeightSpline.hpp" +%} + +%name{Slic3r::LayerHeightSpline} class LayerHeightSpline { + // owned by PrintObject, no constructor/destructor + + bool hasData(); + bool updateRequired(); + void suppressUpdate(); + bool setLayers(std::vector layers) + %code%{ RETVAL = THIS->setLayers(layers); %}; + bool updateLayerHeights(std::vector heights) + %code%{ RETVAL = THIS->updateLayerHeights(heights); %}; + void clear(); + std::vector getOriginalLayers(); + std::vector getInterpolatedLayers(); + coordf_t getLayerHeightAt(coordf_t height); + //%code%{ RETVAL = THIS->upper_layer; %}; +}; diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 5f87f51c2..7590ec90e 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -169,6 +169,9 @@ Ref O_OBJECT_SLIC3R_T SupportLayer* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T +LayerHeightSpline* O_OBJECT_SLIC3R +Ref O_OBJECT_SLIC3R_T + PlaceholderParser* 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 37d7a9620..8eb09db3b 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -129,6 +129,9 @@ %typemap{Layer*}; %typemap{Ref}{simple}; +%typemap{LayerHeightSpline*}; +%typemap{Ref}{simple}; + %typemap{SupportLayer*}; %typemap{Ref}{simple}; From 7293f56f9b9d6f0bf7b63730292396a6bc3db45f Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 26 Jan 2017 08:51:44 +0100 Subject: [PATCH 21/64] WX::Panel layer height spline representation and control element --- lib/Slic3r/GUI/Plater/SplineControl.pm | 209 ++++++++++++------------- 1 file changed, 99 insertions(+), 110 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/SplineControl.pm b/lib/Slic3r/GUI/Plater/SplineControl.pm index d4614793f..c824695f7 100644 --- a/lib/Slic3r/GUI/Plater/SplineControl.pm +++ b/lib/Slic3r/GUI/Plater/SplineControl.pm @@ -12,37 +12,50 @@ use base 'Wx::Panel'; sub new { my $class = shift; - my ($parent, $size) = @_; + my ($parent, $size, $object) = @_; my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, $size, wxTAB_TRAVERSAL); + + $self->{object} = $object; + # This has only effect on MacOS. On Windows and Linux/GTK, the background is painted by $self->repaint(). $self->SetBackgroundColour(Wx::wxWHITE); $self->{line_pen} = Wx::Pen->new(Wx::Colour->new(50,50,50), 1, wxSOLID); + $self->{original_pen} = Wx::Pen->new(Wx::Colour->new(200,200,200), 1, wxSOLID); $self->{interactive_pen} = Wx::Pen->new(Wx::Colour->new(255,0,0), 1, wxSOLID); - $self->{resulting_pen} = Wx::Pen->new(Wx::Colour->new(0,255,0), 1, wxSOLID); + $self->{resulting_pen} = Wx::Pen->new(Wx::Colour->new(50,255,50), 1, wxSOLID); $self->{user_drawn_background} = $^O ne 'darwin'; - + $self->{scaling_factor_x} = 1; $self->{scaling_factor_y} = 1; $self->{min_layer_height} = 0.1; $self->{max_layer_height} = 0.4; $self->{object_height} = 1.0; - $self->{layer_points} = (); - $self->{interactive_points} = (); - $self->{resulting_points} = (); + $self->{original_layers} = $object->layer_height_spline->getOriginalLayers; + $self->{original_interpolated_layers} = $object->layer_height_spline->getInterpolatedLayers; + $self->{interpolated_layers} = $object->layer_height_spline->getInterpolatedLayers; # Initialize to current values + + # initialize height vector + $self->{heights} = (); + $self->{interactive_heights} = (); + my $last_z = 0; + foreach my $z (@{$self->{original_layers}}) { + push (@{$self->{heights}}, $z - $last_z); + $last_z = $z; + } EVT_PAINT($self, \&repaint); EVT_ERASE_BACKGROUND($self, sub {}) if $self->{user_drawn_background}; EVT_MOUSE_EVENTS($self, \&mouse_event); EVT_SIZE($self, sub { - $self->update_canvas_size; + $self->_update_canvas_size; $self->Refresh; }); - + return $self; } @@ -71,14 +84,14 @@ sub repaint { $dc->SetFont(Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL)); $dc->DrawLabel(sprintf('%.4g', $self->{min_layer_height}), Wx::Rect->new(0, $size[1]/2, $size[0], $size[1]/2), wxALIGN_LEFT | wxALIGN_BOTTOM); $dc->DrawLabel(sprintf('%.4g', $self->{max_layer_height}), Wx::Rect->new(0, $size[1]/2, $size[0], $size[1]/2), wxALIGN_RIGHT | wxALIGN_BOTTOM); - - - # draw interpolated (user modified) layers as lines + + + # draw original layers as lines my $last_z = 0.0; my @points = (); - foreach my $z (@{$self->{interactive_points}}) { + foreach my $z (@{$self->{original_interpolated_layers}}) { my $layer_h = $z - $last_z; - $dc->SetPen($self->{interactive_pen}); + $dc->SetPen($self->{original_pen}); my $pl = $self->point_to_pixel(0, $z); my $pr = $self->point_to_pixel($layer_h, $z); $dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); @@ -86,27 +99,30 @@ sub repaint { $last_z = $z; } - $dc->DrawSpline(\@points); + $dc->DrawSpline(\@points); - # draw current layers as lines +# # draw interactive (user modified) layers as lines $last_z = 0.0; @points = (); - foreach my $z (@{$self->{layer_points}}) { - my $layer_h = $z - $last_z; - $dc->SetPen($self->{line_pen}); - my $pl = $self->point_to_pixel(0, $z); - my $pr = $self->point_to_pixel($layer_h, $z); - $dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); - push (@points, $pr); - $last_z = $z; + if($self->{interactive_heights}) { + foreach my $i (0..@{$self->{interactive_heights}}-1) { + my $z = $self->{original_layers}[$i]; + my $layer_h = $self->{interactive_heights}[$i]; + $dc->SetPen($self->{interactive_pen}); + my $pl = $self->point_to_pixel(0, $z); + my $pr = $self->point_to_pixel($layer_h, $z); + $dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); + push (@points, $pr); + } + + $dc->DrawSpline(\@points); } - $dc->DrawSpline(\@points); - + # draw resulting layers as lines $last_z = 0.0; @points = (); - foreach my $z (@{$self->{resulting_points}}) { + foreach my $z (@{$self->{interpolated_layers}}) { my $layer_h = $z - $last_z; $dc->SetPen($self->{resulting_pen}); my $pl = $self->point_to_pixel(0, $z); @@ -116,7 +132,20 @@ sub repaint { $last_z = $z; } - $dc->DrawSpline(\@points); +# $dc->DrawSpline(\@points); + + # Draw current BSpline + $dc->SetPen($self->{line_pen}); + @points = (); + foreach my $pixel (0..$size[1]) { + my @z = $self->pixel_to_point(Wx::Point->new(0, $pixel)); + #print "z: " . $z . "\n"; + my $h = $self->{object}->layer_height_spline->getLayerHeightAt($z[1]); + my $p = $self->point_to_pixel($h, $z[1]); + push (@points, $p); + #print "pixel: " . $pixel . "\n"; + } + $dc->DrawLines(\@points); $event->Skip; } @@ -134,19 +163,26 @@ sub mouse_event { } } elsif ($event->LeftUp) { if($self->{drag_start_pos}) { - $self->{resulting_points} = $self->{interactive_points}; + if($self->{interactive_heights}) { + $self->{heights} = $self->{interactive_heights}; + $self->{interactive_heights} = (); + # update spline database + $self->{object}->layer_height_spline->updateLayerHeights($self->{heights}); + $self->{interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; + } $self->Refresh; + $self->{object}->layer_height_spline->suppressUpdate; + $self->{on_layer_update}->(@{$self->{interpolated_layers}}); } $self->{drag_start_pos} = undef; } elsif ($event->Dragging) { - print "dragging, pos: " . $pos->x . ":" . $pos->y . "\n"; return if !$self->{drag_start_pos}; # concurrency problems my @start_pos = $self->pixel_to_point($self->{drag_start_pos}); my $range = abs($start_pos[1] - $obj_pos[1]); # compute updated interactive layer heights - $self->interactive_curve($start_pos[1], $obj_pos[0], $range); + $self->_interactive_curve($start_pos[1], $obj_pos[0], $range); $self->Refresh; }# elsif ($event->Moving) { @@ -158,8 +194,29 @@ sub mouse_event { # } } +# Set basic parameters for this control. +# min/max_layer_height are required to define the x-range, object_height is used to scale the y-range. +# Must be called if object selection changes. +sub set_size_parameters { + my ($self, $min_layer_height, $max_layer_height, $object_height) = @_; + + $self->{min_layer_height} = $min_layer_height; + $self->{max_layer_height} = $max_layer_height; + $self->{object_height} = $object_height; + + $self->_update_canvas_size; + $self->Refresh; +} + +# Callback to notify parent element if layers have changed and reslicing should be triggered +sub on_layer_update { + my ($self, $cb) = @_; + $self->{on_layer_update} = $cb; +} + + # Internal function to cache scaling factors -sub update_canvas_size { +sub _update_canvas_size { my $self = shift; # when the canvas is not rendered yet, its GetSize() method returns 0,0 @@ -174,89 +231,23 @@ sub update_canvas_size { $self->{scaling_factor_y} = $size[1]/$self->{object_height}; } -# Set basic parameters for this control. -# min/max_layer_height are required to define the x-range, object_height is used to scale the y-range. -# Must be called if object selection changes. -sub set_size_parameters { - my ($self, $min_layer_height, $max_layer_height, $object_height) = @_; - - $self->{min_layer_height} = $min_layer_height; - $self->{max_layer_height} = $max_layer_height; - $self->{object_height} = $object_height; - - $self->update_canvas_size; - $self->Refresh; -} - -# Set the current layer height values as basis for user manipulation -sub set_layer_points { - my ($self, @layer_points) = @_; - - $self->{layer_points} = [@layer_points]; - $self->{interactive_points} = [@layer_points]; # Initialize to current values - $self->{resulting_points} = [@layer_points]; # Initialize to current values - $self->Refresh; -} - - -sub interactive_curve { +sub _interactive_curve { my ($self, $mod_z, $target_layer_height, $range) = @_; - $self->{interactive_points} = (); # reset interactive curve + $self->{interactive_heights} = (); # reset interactive curve - my $z = 0.0; - my $layer_h = $self->{resulting_points}[0]; - my $i = 0; - # copy points which are not going to be modified - while(($z+$self->{resulting_points}[$i] < $mod_z-$range) && ($i < @{$self->{resulting_points}})) { - $layer_h = $self->{resulting_points}[$i] - $z; - $z = $self->{resulting_points}[$i]; - push (@{$self->{interactive_points}}, $z); - $i +=1; + # iterate over original points provided by spline + my $last_z = 0; + foreach my $i (0..@{$self->{heights}}-1 ) { + my $z = $self->{original_layers}[$i]; + my $layer_h = $self->{heights}[$i]; + my $quadratic_factor = $self->_quadratic_factor($mod_z, $range, $z); + my $diff = $target_layer_height - $layer_h; + $layer_h += $diff * $quadratic_factor; + push (@{$self->{interactive_heights}}, $layer_h); } - - print "last original z: " . $z . "\n"; - # interpolate next points - while($z < $self->{object_height}) { - $layer_h = $self->_interpolate_next_layer_h($z); - my $diff = $target_layer_height - $layer_h; - my $quadratic_factor = $self->_quadratic_factor($mod_z, $range, $z+$layer_h); - $layer_h += $diff * $quadratic_factor; - $z += $layer_h; - push (@{$self->{interactive_points}}, $z); - } - - # remove top layer if n-1 is higher than object_height!!! } -sub _interpolate_next_layer_h { - my ($self, $z) = @_; - my $layer_h = $self->{resulting_points}[0]; - my $array_size = @{$self->{resulting_points}}; - my $i = 1; - # find current layer - while(($self->{resulting_points}[$i] <= $z) && ($array_size-1 > $i)) { - $i += 1; - } - if($i == 1) {return $layer_h}; # first layer, nothing to interpolate - - $layer_h = $self->{resulting_points}[$i] - $self->{resulting_points}[$i-1]; - my $tmp = $layer_h; - # interpolate - if($array_size-1 > $i) { - my $next_layer_h = $self->{resulting_points}[$i+1] - $self->{resulting_points}[$i]; - $layer_h = $layer_h * ($self->{resulting_points}[$i] - $z)/$layer_h + $next_layer_h * min(1, ($z+$layer_h-$self->{resulting_points}[$i])/$next_layer_h); - } -# if(abs($layer_h - $tmp) > 0.001) { -# print "z: " . $z . "\n"; -# my $layer_i = $self->{resulting_points}[$i] - $self->{resulting_points}[$i-1]; -# my $layer_i1 = $self->{resulting_points}[$i+1] - $self->{resulting_points}[$i]; -# print "layer i: " . $layer_i . " -> " . $self->{resulting_points}[$i] . "\n"; -# print "layer i+1: " . $layer_i1 . " -> " . $self->{resulting_points}[$i+1] . "\n"; -# print "layer_h_1: " . $tmp . " layer_h_2: " . $layer_h . "\n\n"; -# } - return $layer_h; -} sub _quadratic_factor { my ($self, $fixpoint, $range, $value) = @_; @@ -268,8 +259,6 @@ sub _quadratic_factor { my $x = $dist/$range; # normalize my $result = 1-($x*$x); - print "fixpoint: " . $fixpoint . " range: " . $range . " value: " . $value . " result: " . $result . "\n"; - return max(0, $result); } From a3867b0be810838298c5177d1bdc143f72b91010 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 26 Jan 2017 09:22:04 +0100 Subject: [PATCH 22/64] Integration of spline based layer height postprocessing --- lib/Slic3r/GUI/Plater.pm | 2 +- lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm | 22 +- lib/Slic3r/Print/Object.pm | 222 +++++++++++--------- xs/src/libslic3r/Print.hpp | 4 +- xs/src/libslic3r/PrintObject.cpp | 3 +- xs/xsp/Print.xsp | 2 + 6 files changed, 143 insertions(+), 112 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index b4fa04762..b28304bab 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -149,9 +149,9 @@ sub new { $self->{htoolbar}->AddTool(TB_SCALE, "Scale…", Wx::Bitmap->new($Slic3r::var->("arrow_out.png"), wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddTool(TB_SPLIT, "Split", Wx::Bitmap->new($Slic3r::var->("shape_ungroup.png"), wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new($Slic3r::var->("package.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_LAYERS, "Layers…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddSeparator; $self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), ''); + $self->{htoolbar}->AddTool(TB_LAYERS, "Layers…", Wx::Bitmap->new($Slic3r::var->("layers.png"), wxBITMAP_TYPE_PNG), ''); } else { my %tbar_buttons = ( add => "Add…", diff --git a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm index 6e8652aae..295508e3f 100644 --- a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm @@ -4,6 +4,7 @@ use warnings; use utf8; use Slic3r::Geometry qw(PI X scale unscale); +use Slic3r::Print::State ':steps'; use List::Util qw(min max sum first); use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL); use Wx::Event qw(EVT_CLOSE EVT_BUTTON); @@ -14,12 +15,11 @@ sub new { my ($parent, %params) = @_; my $self = $class->SUPER::new($parent, -1, $params{object}->name, wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); $self->{model_object} = $params{model_object}; - - my $model_object = $self->{model_object} = $params{model_object}; my $obj_idx = $self->{obj_idx} = $params{obj_idx}; my $plater = $self->{plater} = $parent; - + my $object = $self->{object} = $self->{plater}->{print}->get_object($self->{obj_idx}); + # Initialize 3D toolpaths preview if ($Slic3r::GUI::have_OpenGL) { $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self, $plater->{print}); @@ -32,8 +32,8 @@ sub new { $self->{preview3D}->load_print; $self->{preview3D}->canvas->zoom_to_volumes; } - - $self->{splineControl} = Slic3r::GUI::Plater::SplineControl->new($self, Wx::Size->new(100, 200)); + + $self->{splineControl} = Slic3r::GUI::Plater::SplineControl->new($self, Wx::Size->new(200, 200), $object); my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); $right_sizer->Add($self->{splineControl}, 1, wxEXPAND | wxALL, 0); @@ -46,9 +46,7 @@ sub new { $self->SetSize([800, 600]); $self->SetMinSize($self->GetSize); - # init spline control values - my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); - + # init spline control values # determine min and max layer height from perimeter extruder capabilities. my %extruders; for my $region_id (0 .. ($object->region_count - 1)) { @@ -62,10 +60,12 @@ sub new { $self->{splineControl}->set_size_parameters($min_height, $max_height, unscale($object->size->z)); - # get array of current Z coordinates for selected object - my @layer_heights = map $_->print_z, @{$object->layers}; - $self->{splineControl}->set_layer_points(@layer_heights); + $self->{splineControl}->on_layer_update(sub { + # trigger re-slicing + $self->{object}->invalidate_step(STEP_SLICE); + $self->{plater}->start_background_process; + }); return $self; } diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 66e156ddc..bef7dbf84 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -89,118 +89,144 @@ sub slice { my $slice_z = 0; my $height = 0; my $cusp_height = 0; + my @layers = (); - # create stateful objects and variables for the adaptive slicing process - my @adaptive_slicing; - my $min_height = 0; - my $max_height = 0; - if ($self->config->adaptive_slicing) { - for my $region_id (0 .. ($self->region_count - 1)) { - my $mesh; - foreach my $volume_id (@{ $self->get_region_volumes($region_id) }) { - my $volume = $self->model_object->volumes->[$volume_id]; - next if $volume->modifier; - if (defined $mesh) { - $mesh->merge($volume->mesh); - } else { - $mesh = $volume->mesh->clone; - } - } - - if (defined $mesh) { - $adaptive_slicing[$region_id] = Slic3r::AdaptiveSlicing->new( - mesh => $mesh, - size => $self->size->z - ); - } - } - - # determine min and max layer height from perimeter extruder capabilities. - if($self->region_count > 1) { # multimaterial object - $min_height = max(map {$self->print->config->get_at('min_layer_height', $_)} (0..($self->region_count-1))); - $max_height = min(map {$self->print->config->get_at('max_layer_height', $_)} (0..($self->region_count-1))); - }else{ #single material object - my $perimeter_extruder = $self->print->get_region(0)->config->get('perimeter_extruder')-1; - $min_height = $self->print->config->get_at('min_layer_height', $perimeter_extruder); - $max_height = $self->print->config->get_at('max_layer_height', $perimeter_extruder); - } - } - - # loop until we have at least one layer and the max slice_z reaches the object height - my $max_z = unscale($self->size->z); - while (($slice_z - $height) <= $max_z) { - - if ($self->config->adaptive_slicing) { - $height = 999; - my $cusp_value = $self->config->get_value('cusp_value'); - - Slic3r::debugf "\n Slice layer: %d\n", $id; - - # determine next layer height - for my $region_id (0 .. ($self->region_count - 1)) { - # get cusp height - next if(!defined $adaptive_slicing[$region_id]); - my $cusp_height = $adaptive_slicing[$region_id]->cusp_height(scale $slice_z, $cusp_value, $min_height, $max_height); - - # check for horizontal features and object size - if($self->config->get_value('match_horizontal_surfaces')) { - my $horizontal_dist = $adaptive_slicing[$region_id]->horizontal_facet_distance(scale $slice_z+$cusp_height, $min_height); - if(($horizontal_dist < $min_height) && ($horizontal_dist > 0)) { - Slic3r::debugf "Horizontal feature ahead, distance: %f\n", $horizontal_dist; - # can we shrink the current layer a bit? - if($cusp_height-($min_height-$horizontal_dist) > $min_height) { - # yes we can - $cusp_height = $cusp_height-($min_height-$horizontal_dist); - Slic3r::debugf "Shrink layer height to %f\n", $cusp_height; - }else{ - # no, current layer would become too thin - $cusp_height = $cusp_height+$horizontal_dist; - Slic3r::debugf "Widen layer height to %f\n", $cusp_height; - } - } - } + if(!$self->layer_height_spline->updateRequired) { # layer heights are already generated, just update layers from spline + @layers = @{$self->layer_height_spline->getInterpolatedLayers}; + }else{ # create new set of layers + # create stateful objects and variables for the adaptive slicing process + my @adaptive_slicing; + my $min_height = 0; + my $max_height = 0; + if ($self->config->adaptive_slicing) { + for my $region_id (0 .. ($self->region_count - 1)) { + my $mesh; + foreach my $volume_id (@{ $self->get_region_volumes($region_id) }) { + my $volume = $self->model_object->volumes->[$volume_id]; + next if $volume->modifier; + if (defined $mesh) { + $mesh->merge($volume->mesh); + } else { + $mesh = $volume->mesh->clone; + } + } - $height = ($id == 0) - ? $self->config->get_value('first_layer_height') - : min($cusp_height, $height); - } - - }else{ - # assign the default height to the layer according to the general settings - $height = ($id == 0) - ? $self->config->get_value('first_layer_height') - : $self->config->layer_height; - } - - # look for an applicable custom range - if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) { - $height = $range->[2]; - - # if user set custom height to zero we should just skip the range and resume slicing over it - if ($height == 0) { - $slice_z += $range->[1] - $range->[0]; - next; + if (defined $mesh) { + $adaptive_slicing[$region_id] = Slic3r::AdaptiveSlicing->new( + mesh => $mesh, + size => $self->size->z + ); + } } - } + + # determine min and max layer height from perimeter extruder capabilities. + if($self->region_count > 1) { # multimaterial object + $min_height = max(map {$self->print->config->get_at('min_layer_height', $_)} (0..($self->region_count-1))); + $max_height = min(map {$self->print->config->get_at('max_layer_height', $_)} (0..($self->region_count-1))); + }else{ #single material object + my $perimeter_extruder = $self->print->get_region(0)->config->get('perimeter_extruder')-1; + $min_height = $self->print->config->get_at('min_layer_height', $perimeter_extruder); + $max_height = $self->print->config->get_at('max_layer_height', $perimeter_extruder); + } + } + + # loop until we have at least one layer and the max slice_z reaches the object height + my $max_z = unscale($self->size->z); + while (($slice_z) < $max_z) { + + if ($self->config->adaptive_slicing) { + $height = 999; + my $cusp_value = $self->config->get_value('cusp_value'); + + Slic3r::debugf "\n Slice layer: %d\n", $id; + + # determine next layer height + for my $region_id (0 .. ($self->region_count - 1)) { + # get cusp height + next if(!defined $adaptive_slicing[$region_id]); + my $cusp_height = $adaptive_slicing[$region_id]->cusp_height(scale $slice_z, $cusp_value, $min_height, $max_height); + + # check for horizontal features and object size + if($self->config->get_value('match_horizontal_surfaces')) { + my $horizontal_dist = $adaptive_slicing[$region_id]->horizontal_facet_distance(scale $slice_z+$cusp_height, $min_height); + if(($horizontal_dist < $min_height) && ($horizontal_dist > 0)) { + Slic3r::debugf "Horizontal feature ahead, distance: %f\n", $horizontal_dist; + # can we shrink the current layer a bit? + if($cusp_height-($min_height-$horizontal_dist) > $min_height) { + # yes we can + $cusp_height = $cusp_height-($min_height-$horizontal_dist); + Slic3r::debugf "Shrink layer height to %f\n", $cusp_height; + }else{ + # no, current layer would become too thin + $cusp_height = $cusp_height+$horizontal_dist; + Slic3r::debugf "Widen layer height to %f\n", $cusp_height; + } + } + } + + $height = ($id == 0) + ? $self->config->get_value('first_layer_height') + : min($cusp_height, $height); + } + + }else{ + # assign the default height to the layer according to the general settings + $height = ($id == 0) + ? $self->config->get_value('first_layer_height') + : $self->config->layer_height; + } + + # look for an applicable custom range + if (my $range = first { $_->[0] <= $slice_z && $_->[1] > $slice_z } @{$self->layer_height_ranges}) { + $height = $range->[2]; - if ($first_object_layer_height != -1 && !@{$self->layers}) { - $height = $first_object_layer_height; - $print_z += ($first_object_layer_distance - $height); + # if user set custom height to zero we should just skip the range and resume slicing over it + if ($height == 0) { + $slice_z += $range->[1] - $range->[0]; + next; + } + } + + # set first layer height if raft is active + if ($first_object_layer_height != -1 && !@layers) { + $height = $first_object_layer_height; + #$print_z += ($first_object_layer_distance - $height); + } + + $slice_z += $height; + $id++; + + # collect layers for spline smoothing + push (@layers, $slice_z); } - + $self->layer_height_spline->setLayers(\@layers); + if ($self->config->adaptive_slicing) { # smoothing after adaptive algorithm + $self->layer_height_spline->setLayers($self->layer_height_spline->getInterpolatedLayers); + } + } + + $id = 0; + if ($self->config->raft_layers > 0) { + $id = $self->config->raft_layers; + } + + # generate layer objects + $slice_z = 0; + foreach my $z (@layers) { + $height = $z - $slice_z; $print_z += $height; $slice_z += $height/2; - - ### Slic3r::debugf "Layer %d: height = %s; slice_z = %s; print_z = %s\n", $id, $height, $slice_z, $print_z; - + + Slic3r::debugf "Layer %d: height = %s; slice_z = %s; print_z = %s\n", $id, $height, $slice_z, $print_z; + $self->add_layer($id, $height, $print_z, $slice_z); if ($self->layer_count >= 2) { my $lc = $self->layer_count; $self->get_layer($lc - 2)->set_upper_layer($self->get_layer($lc - 1)); $self->get_layer($lc - 1)->set_lower_layer($self->get_layer($lc - 2)); } + $id++; - $slice_z += $height/2; # add the other half layer } } diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index fcf6d0a79..69b927b1b 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -12,7 +12,7 @@ #include "Layer.hpp" #include "Model.hpp" #include "PlaceholderParser.hpp" - +#include "LayerHeightSpline.hpp" namespace Slic3r { @@ -83,6 +83,8 @@ class PrintObject PrintObjectConfig config; t_layer_height_ranges layer_height_ranges; + LayerHeightSpline layer_height_spline; + // this is set to true when LayerRegion->slices is split in top/internal/bottom // so that next call to make_perimeters() performs a union() before computing loops bool typed_slices; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index eb744985e..5fedcabf3 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -8,7 +8,8 @@ namespace Slic3r { PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox) : typed_slices(false), _print(print), - _model_object(model_object) + _model_object(model_object), + layer_height_spline(modobj_bbox.size().z) { // Compute the translation to be applied to our meshes so that we work with smaller coordinates { diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 27d970a5b..a5369cf54 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -58,6 +58,8 @@ _constant() Points copies(); t_layer_height_ranges layer_height_ranges() %code%{ RETVAL = THIS->layer_height_ranges; %}; + Ref layer_height_spline() + %code%{ RETVAL = &THIS->layer_height_spline; %}; Ref size() %code%{ RETVAL = &THIS->size; %}; Clone bounding_box(); From 75335f48c08e0bf656324d2e5fbbb17f4485b425 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 26 Jan 2017 09:25:24 +0100 Subject: [PATCH 23/64] Changed adaptive slicing horizontal feature detection default to false --- 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 d69d35758..3ed618c00 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -591,7 +591,7 @@ PrintConfigDef::PrintConfigDef() def->label = "Match horizontal surfaces"; def->tooltip = "Try to match horizontal surfaces during the slicing process. Matching is not guaranteed, very small surfaces and multiple surfaces with low vertical distance might cause bad results."; def->cli = "match-horizontal-surfaces!"; - def->default_value = new ConfigOptionBool(true); + def->default_value = new ConfigOptionBool(false); def = this->add("max_fan_speed", coInt); def->label = "Max"; From 1dd90cf4307449cfec9959c0f02e8b6ebafe811d Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 26 Jan 2017 09:38:12 +0100 Subject: [PATCH 24/64] bugfix: smoothing was not applied after adaptive layer height generation --- lib/Slic3r/Print/Object.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index bef7dbf84..837fe9006 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -201,7 +201,7 @@ sub slice { } $self->layer_height_spline->setLayers(\@layers); if ($self->config->adaptive_slicing) { # smoothing after adaptive algorithm - $self->layer_height_spline->setLayers($self->layer_height_spline->getInterpolatedLayers); + @layers = @{$self->layer_height_spline->getInterpolatedLayers}; } } From 61a6261307cc6b81e620513009d4394135278ef7 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 26 Jan 2017 15:44:37 +0100 Subject: [PATCH 25/64] Height indicator to visualize which layer the user is currently modifying --- lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm | 64 ++++++++++++++++++--- lib/Slic3r/GUI/Plater/SplineControl.pm | 54 +++++++++++------ 2 files changed, 91 insertions(+), 27 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm index 295508e3f..502aa2d12 100644 --- a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm @@ -6,8 +6,8 @@ use utf8; use Slic3r::Geometry qw(PI X scale unscale); use Slic3r::Print::State ':steps'; use List::Util qw(min max sum first); -use Wx qw(wxTheApp :dialog :id :misc :sizer wxTAB_TRAVERSAL); -use Wx::Event qw(EVT_CLOSE EVT_BUTTON); +use Wx qw(wxTheApp :dialog :id :misc :sizer :slider :statictext wxTAB_TRAVERSAL); +use Wx::Event qw(EVT_CLOSE EVT_BUTTON EVT_SLIDER); use base 'Wx::Dialog'; sub new { @@ -23,9 +23,6 @@ sub new { # Initialize 3D toolpaths preview if ($Slic3r::GUI::have_OpenGL) { $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self, $plater->{print}); - #$self->{preview3D}->canvas->on_viewport_changed(sub { - # $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); - #}); $self->{preview3D}->canvas->set_auto_bed_shape; $self->{preview3D}->canvas->SetSize([500,500]); $self->{preview3D}->canvas->SetMinSize($self->{preview3D}->canvas->GetSize); @@ -33,14 +30,32 @@ sub new { $self->{preview3D}->canvas->zoom_to_volumes; } - $self->{splineControl} = Slic3r::GUI::Plater::SplineControl->new($self, Wx::Size->new(200, 200), $object); + $self->{splineControl} = Slic3r::GUI::Plater::SplineControl->new($self, Wx::Size->new(150, 200), $object); + +# my $cusp_slider = $self->{cusp_slider} = Wx::Slider->new( +# $self, -1, +# 0, # default +# 0, # min +# # we set max to a bogus non-zero value because the MSW implementation of wxSlider +# # will skip drawing the slider if max <= min: +# 1, # max +# wxDefaultPosition, +# wxDefaultSize, +# wxHORIZONTAL, +# ); + + #my $cusp_label = $self->{cusp_label} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, + # [150,-1], wxALIGN_CENTRE_HORIZONTAL); + #$cusp_label->SetFont($Slic3r::GUI::small_font); my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); $right_sizer->Add($self->{splineControl}, 1, wxEXPAND | wxALL, 0); + #$right_sizer->Add($cusp_slider, 0, wxEXPAND | wxALL, 0); + #$right_sizer->Add($cusp_label, 0, wxEXPAND | wxALL, 0); $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); - $self->{sizer}->Add($self->{preview3D}, 1, wxEXPAND | wxTOP | wxBOTTOM, 0) if $self->{preview3D}; - $self->{sizer}->Add($right_sizer, 0, wxEXPAND | wxTOP | wxBOTTOM, 10); + $self->{sizer}->Add($self->{preview3D}, 3, wxEXPAND | wxTOP | wxBOTTOM, 0) if $self->{preview3D}; + $self->{sizer}->Add($right_sizer, 1, wxEXPAND | wxTOP | wxBOTTOM, 10); $self->SetSizerAndFit($self->{sizer}); $self->SetSize([800, 600]); @@ -63,15 +78,48 @@ sub new { $self->{splineControl}->on_layer_update(sub { # trigger re-slicing + $self->{plater}->stop_background_process; $self->{object}->invalidate_step(STEP_SLICE); $self->{plater}->start_background_process; }); + $self->{splineControl}->on_z_indicator(sub { + my ($z) = @_; + #$self->{preview3D}->canvas->cutting_plane_z($z); + $self->{preview3D}->canvas->SetCuttingPlane($z, []); + $self->{preview3D}->canvas->Render; + }); + + # init cusp slider +# if($object->config->adaptive_slicing) { +# my $cusp_value = $object->config->get('cusp_value'); +# $cusp_label->SetLabel(sprintf 'Cusp value: %.2f mm', $cusp_value); +# $cusp_slider->SetRange(0, $max_height*100); +# $cusp_slider->SetValue($cusp_value*100); +# }else{ +# # disable slider +# $cusp_label->SetLabel("Cusp value: "); +# $cusp_label->Enable(0); +# $cusp_slider->Enable(0); +# } + +# EVT_SLIDER($self, $cusp_slider, sub { +# $self->{plater}->pause_background_process; +# my $cusp_value = $cusp_slider->GetValue/100; +# $cusp_label->SetLabel(sprintf 'Cusp value: %.2f mm', $cusp_value); +# my $success = $object->config->set('cusp_value', $cusp_value); +# # trigger re-slicing +# $self->{plater}->stop_background_process; +# $self->{object}->invalidate_step(STEP_SLICE); +# $self->{plater}->schedule_background_process; +# }); + return $self; } sub reload_preview { my ($self) = @_; + #$self->{splineControl}->update; $self->{preview3D}->reload_print; } diff --git a/lib/Slic3r/GUI/Plater/SplineControl.pm b/lib/Slic3r/GUI/Plater/SplineControl.pm index c824695f7..ba8885361 100644 --- a/lib/Slic3r/GUI/Plater/SplineControl.pm +++ b/lib/Slic3r/GUI/Plater/SplineControl.pm @@ -35,18 +35,8 @@ sub new { $self->{max_layer_height} = 0.4; $self->{object_height} = 1.0; - $self->{original_layers} = $object->layer_height_spline->getOriginalLayers; - $self->{original_interpolated_layers} = $object->layer_height_spline->getInterpolatedLayers; - $self->{interpolated_layers} = $object->layer_height_spline->getInterpolatedLayers; # Initialize to current values - - # initialize height vector - $self->{heights} = (); - $self->{interactive_heights} = (); - my $last_z = 0; - foreach my $z (@{$self->{original_layers}}) { - push (@{$self->{heights}}, $z - $last_z); - $last_z = $z; - } + # initialize values + $self->update; EVT_PAINT($self, \&repaint); EVT_ERASE_BACKGROUND($self, sub {}) if $self->{user_drawn_background}; @@ -185,13 +175,15 @@ sub mouse_event { $self->_interactive_curve($start_pos[1], $obj_pos[0], $range); $self->Refresh; - }# elsif ($event->Moving) { -# my $cursor = wxSTANDARD_CURSOR; -# if (defined first { $_->contour->contains_point($point) } map @$_, map @{$_->instance_thumbnails}, @{ $self->{objects} }) { -# $cursor = Wx::Cursor->new(wxCURSOR_HAND); -# } -# $self->SetCursor($cursor); -# } + } elsif ($event->Moving) { + if($self->{on_z_indicator}) { + $self->{on_z_indicator}->($obj_pos[1]); + } + } elsif ($event->Leaving) { + if($self->{on_z_indicator} && !$self->{drag_start_pos}) { + $self->{on_z_indicator}->(undef); + } + } } # Set basic parameters for this control. @@ -208,12 +200,36 @@ sub set_size_parameters { $self->Refresh; } +# Layers have been modified externally, re-initialize this control with new values +sub update { + my $self = shift; + + $self->{original_layers} = $self->{object}->layer_height_spline->getOriginalLayers; + $self->{original_interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; + $self->{interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; # Initialize to current values + + # initialize height vector + $self->{heights} = (); + $self->{interactive_heights} = (); + my $last_z = 0; + foreach my $z (@{$self->{original_layers}}) { + push (@{$self->{heights}}, $z - $last_z); + $last_z = $z; + } + $self->Refresh; +} + # Callback to notify parent element if layers have changed and reslicing should be triggered sub on_layer_update { my ($self, $cb) = @_; $self->{on_layer_update} = $cb; } +# Callback to tell parent element at which z-position the mouse currently hovers to update indicator in 3D-view +sub on_z_indicator { + my ($self, $cb) = @_; + $self->{on_z_indicator} = $cb; +} # Internal function to cache scaling factors sub _update_canvas_size { From d466617054c1b4cd8659cd1afbe6d6b7d5b8ea45 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 26 Jan 2017 16:22:46 +0100 Subject: [PATCH 26/64] New linear modificator for layer heights --- lib/Slic3r/GUI/Plater/SplineControl.pm | 73 +++++++++++++++++++++----- 1 file changed, 61 insertions(+), 12 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/SplineControl.pm b/lib/Slic3r/GUI/Plater/SplineControl.pm index ba8885361..7bc7cf91f 100644 --- a/lib/Slic3r/GUI/Plater/SplineControl.pm +++ b/lib/Slic3r/GUI/Plater/SplineControl.pm @@ -149,10 +149,14 @@ sub mouse_event { if ($event->ButtonDown) { if ($event->LeftDown) { # start dragging - $self->{drag_start_pos} = $pos; + $self->{left_drag_start_pos} = $pos; + } + if ($event->RightDown) { + # start dragging + $self->{right_drag_start_pos} = $pos; } } elsif ($event->LeftUp) { - if($self->{drag_start_pos}) { + if($self->{left_drag_start_pos}) { if($self->{interactive_heights}) { $self->{heights} = $self->{interactive_heights}; $self->{interactive_heights} = (); @@ -164,23 +168,44 @@ sub mouse_event { $self->{object}->layer_height_spline->suppressUpdate; $self->{on_layer_update}->(@{$self->{interpolated_layers}}); } - $self->{drag_start_pos} = undef; + $self->{left_drag_start_pos} = undef; + } elsif ($event->RightUp) { + if($self->{right_drag_start_pos}) { + if($self->{interactive_heights}) { + $self->{heights} = $self->{interactive_heights}; + $self->{interactive_heights} = (); + # update spline database + $self->{object}->layer_height_spline->updateLayerHeights($self->{heights}); + $self->{interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; + } + $self->Refresh; + $self->{object}->layer_height_spline->suppressUpdate; + $self->{on_layer_update}->(@{$self->{interpolated_layers}}); + } + $self->{right_drag_start_pos} = undef; } elsif ($event->Dragging) { - return if !$self->{drag_start_pos}; # concurrency problems + if($self->{left_drag_start_pos}) { - my @start_pos = $self->pixel_to_point($self->{drag_start_pos}); - my $range = abs($start_pos[1] - $obj_pos[1]); - - # compute updated interactive layer heights - $self->_interactive_curve($start_pos[1], $obj_pos[0], $range); - $self->Refresh; + my @start_pos = $self->pixel_to_point($self->{left_drag_start_pos}); + my $range = abs($start_pos[1] - $obj_pos[1]); + # compute updated interactive layer heights + $self->_interactive_quadratic_curve($start_pos[1], $obj_pos[0], $range); + $self->Refresh; + } elsif($self->{right_drag_start_pos}) { + my @start_pos = $self->pixel_to_point($self->{right_drag_start_pos}); + my $range = $obj_pos[1] - $start_pos[1]; + + # compute updated interactive layer heights + $self->_interactive_linear_curve($start_pos[1], $obj_pos[0], $range); + $self->Refresh; + } } elsif ($event->Moving) { if($self->{on_z_indicator}) { $self->{on_z_indicator}->($obj_pos[1]); } } elsif ($event->Leaving) { - if($self->{on_z_indicator} && !$self->{drag_start_pos}) { + if($self->{on_z_indicator} && !$self->{left_drag_start_pos}) { $self->{on_z_indicator}->(undef); } } @@ -247,7 +272,7 @@ sub _update_canvas_size { $self->{scaling_factor_y} = $size[1]/$self->{object_height}; } -sub _interactive_curve { +sub _interactive_quadratic_curve { my ($self, $mod_z, $target_layer_height, $range) = @_; $self->{interactive_heights} = (); # reset interactive curve @@ -264,6 +289,30 @@ sub _interactive_curve { } } +sub _interactive_linear_curve { + my ($self, $mod_z, $target_layer_height, $range) = @_; + + $self->{interactive_heights} = (); # reset interactive curve + my $from; + my $to; + + if($range >= 0) { + $from = $mod_z; + $to = $mod_z + $range; + }else{ + $from = $mod_z + $range; + $to = $mod_z; + } + + # iterate over original points provided by spline + foreach my $i (0..@{$self->{heights}}-1 ) { + if(($self->{original_layers}[$i]) >= $from && ($self->{original_layers}[$i]< $to)) { + push (@{$self->{interactive_heights}}, $target_layer_height); + }else{ + push (@{$self->{interactive_heights}}, $self->{heights}[$i]); + } + } +} sub _quadratic_factor { my ($self, $fixpoint, $range, $value) = @_; From 5beb10c4e3a9722ad9bdb12a246e387f4f2fc18b Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 26 Jan 2017 21:57:39 +0100 Subject: [PATCH 27/64] Include local-lib to testcase --- t/adaptive_slicing.t | 1 + 1 file changed, 1 insertion(+) diff --git a/t/adaptive_slicing.t b/t/adaptive_slicing.t index 59354951b..452665d11 100644 --- a/t/adaptive_slicing.t +++ b/t/adaptive_slicing.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); From c1e15cbc47056db1a48f1c3384dc663b5bb7025d Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 26 Jan 2017 22:12:02 +0100 Subject: [PATCH 28/64] Disabled testcases conflicting with spline smoothing (testing for the exact match of horizontal features) --- t/adaptive_slicing.t | 2 ++ 1 file changed, 2 insertions(+) diff --git a/t/adaptive_slicing.t b/t/adaptive_slicing.t index 452665d11..2a2f0b311 100644 --- a/t/adaptive_slicing.t +++ b/t/adaptive_slicing.t @@ -107,6 +107,7 @@ $config->set('cusp_value', [0.19]); # slope height: 7,07107 (2.92893 to 10) subtest 'shrink to match horizontal facets' => sub { + plan skip_all => 'spline smoothing currently prevents exact horizontal facet matching'; plan tests => 3; $test->(); }; @@ -115,6 +116,7 @@ subtest 'shrink to match horizontal facets' => sub { $config->set('cusp_value', [0.1]); subtest 'widen to match horizontal facets' => sub { + plan skip_all => 'spline smoothing currently prevents exact horizontal facet matching'; plan tests => 3; $test->(); }; From dc4ea7dbc2f806bed2e469cc5a7168c4668f85f5 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Fri, 27 Jan 2017 09:06:04 +0100 Subject: [PATCH 29/64] Merge issue: set correct axis (Z) for visualization plane. --- lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm index 502aa2d12..9962309c9 100644 --- a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm @@ -3,7 +3,7 @@ use strict; use warnings; use utf8; -use Slic3r::Geometry qw(PI X scale unscale); +use Slic3r::Geometry qw(PI X Y Z scale unscale); use Slic3r::Print::State ':steps'; use List::Util qw(min max sum first); use Wx qw(wxTheApp :dialog :id :misc :sizer :slider :statictext wxTAB_TRAVERSAL); @@ -86,7 +86,7 @@ sub new { $self->{splineControl}->on_z_indicator(sub { my ($z) = @_; #$self->{preview3D}->canvas->cutting_plane_z($z); - $self->{preview3D}->canvas->SetCuttingPlane($z, []); + $self->{preview3D}->canvas->SetCuttingPlane(Z, $z, []); $self->{preview3D}->canvas->Render; }); From c770d8fe2e6ba9eeb4e3e401a7940101d287caec Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Mon, 30 Jan 2017 12:04:07 +0100 Subject: [PATCH 30/64] Rename the Layers... button to Layer heights... --- lib/Slic3r/GUI/Plater.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 7e271bfb6..6bbb403ba 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -172,7 +172,7 @@ sub new { $self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new($Slic3r::var->("package.png"), wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddSeparator; $self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_LAYERS, "Layers…", Wx::Bitmap->new($Slic3r::var->("layers.png"), wxBITMAP_TYPE_PNG), ''); + $self->{htoolbar}->AddTool(TB_LAYERS, "Layer heights…", Wx::Bitmap->new($Slic3r::var->("layers.png"), wxBITMAP_TYPE_PNG), ''); } else { my %tbar_buttons = ( add => "Add…", @@ -186,7 +186,7 @@ sub new { changescale => "Scale…", split => "Split", cut => "Cut…", - layers => "Layers…", + layers => "Layer heights…", settings => "Settings…", ); $self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL); @@ -1981,7 +1981,7 @@ sub object_menu { $frame->_append_menu_item($menu, "Cut…", 'Open the 3D cutting tool', sub { $self->object_cut_dialog; }, undef, 'package.png'); - $frame->_append_menu_item($menu, "Layers…", 'Open the dynamic layer height control', sub { + $frame->_append_menu_item($menu, "Layer heights…", 'Open the dynamic layer height control', sub { $self->object_layers_dialog; }, undef, 'cog.png'); $menu->AppendSeparator(); From 3c3762b652ca85bf95205c4bd49b32f0409e40c7 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Mon, 6 Feb 2017 11:56:47 +0100 Subject: [PATCH 31/64] Bugfix: 'repair' meshes when initializing the AdaptiveSlicing object for multi-mesh objects --- lib/Slic3r/AdaptiveSlicing.pm | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/AdaptiveSlicing.pm b/lib/Slic3r/AdaptiveSlicing.pm index 8bd989585..eab981dd1 100644 --- a/lib/Slic3r/AdaptiveSlicing.pm +++ b/lib/Slic3r/AdaptiveSlicing.pm @@ -16,7 +16,8 @@ has 'current_facet' => (is => 'rw'); sub BUILD { my $self = shift; - my $facet_id = 0; + my $facet_id = 0; + $self->mesh->repair; my $facets = $self->mesh->facets; my $vertices = $self->mesh->vertices; my $normals = $self->mesh->normals; From 59dd5e4bca9270fb939701f1c4526bd701b511a1 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 8 Feb 2017 12:49:17 +0100 Subject: [PATCH 32/64] implemented volumetric error approach to control the adaptive layer height --- lib/Slic3r/AdaptiveSlicing.pm | 278 ++++++++++++++++++-- lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm | 83 +++--- lib/Slic3r/GUI/Plater/SplineControl.pm | 32 ++- lib/Slic3r/Print/Object.pm | 6 +- xs/src/libslic3r/LayerHeightSpline.cpp | 11 + xs/src/libslic3r/LayerHeightSpline.hpp | 10 + xs/xsp/LayerHeightSpline.xsp | 5 + 7 files changed, 358 insertions(+), 67 deletions(-) diff --git a/lib/Slic3r/AdaptiveSlicing.pm b/lib/Slic3r/AdaptiveSlicing.pm index eab981dd1..1537edf76 100644 --- a/lib/Slic3r/AdaptiveSlicing.pm +++ b/lib/Slic3r/AdaptiveSlicing.pm @@ -2,6 +2,7 @@ package Slic3r::AdaptiveSlicing; use Moo; use List::Util qw(min max); +use Math::Trig qw(asin acos deg2rad rad2deg); use Slic3r::Geometry qw(X Y Z triangle_normal scale unscale); # public @@ -57,6 +58,218 @@ sub BUILD { } +# Combined layer height, weighing between cusp and Ra factor +sub next_layer_height_area { + my $self = shift; + my ($z, $factor, $min_height, $max_height) = @_; + + # factor must be between 0-1, 0 is highest quality, 1 highest print speed + if($factor < 0 or $factor > 1) { + die "Speed / Quality factor must be in the interval [0:1]"; + } + + my $volume_factor = 0.12263; + my $delta_min = $volume_factor * $min_height * 2; + my $delta_max = $volume_factor * $max_height * 2 + 0.5*$max_height; + + my $area_value = $factor * ($delta_max-$delta_min) + $delta_min; + + my $height = $max_height; + my $first_hit = 0; + + # find all facets intersecting the slice-layer + my $ordered_id = $self->current_facet; + while ($ordered_id <= $#{$self->ordered_facets}) { + + # facet's minimum is higher than slice_z -> end loop + if($self->ordered_facets->[$ordered_id]->[1] >= $z) { + last; + } + + # facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point + if($self->ordered_facets->[$ordered_id]->[2] > $z) { + # first event? + if(!$first_hit) { + $first_hit = 1; + $self->current_facet($ordered_id); + } + + #skip touching facets which could otherwise cause small cusp values + if($self->ordered_facets->[$ordered_id]->[2] <= $z+1) + { + $ordered_id++; + next; + } + + # compute cusp-height for this facet and store minimum of all heights + my $area_h = $self->_facet_area_height($ordered_id, $area_value); + $area_h = max($min_height, min($max_height, $area_h)); + $height = $area_h if($area_h < $height); + } + $ordered_id++; + } + # lower height limit due to printer capabilities + $height = $min_height if($height < $min_height); + + + # check for sloped facets inside the determined layer and correct height if necessary + if($height > $min_height){ + while ($ordered_id <= $#{$self->ordered_facets}) { + + # facet's minimum is higher than slice_z + height -> end loop + if($self->ordered_facets->[$ordered_id]->[1] >= ($z + scale $height)) { + last; + } + + #skip touching facets which could otherwise cause small cusp values + if($self->ordered_facets->[$ordered_id]->[2] <= $z+1) + { + $ordered_id++; + next; + } + + # Compute cusp-height for this facet and check against height. + my $area_h = $self->_facet_area_height($ordered_id, $area_value); + $area_h = max($min_height, min($max_height, $area_h)); + + my $z_diff = unscale ($self->ordered_facets->[$ordered_id]->[1] - $z); + + +# # handle horizontal facets +# if ($self->normal_z->[$self->ordered_facets->[$ordered_id]->[0]] > 0.999) { +# Slic3r::debugf "cusp computation, height is reduced from %f", $height; +# $height = $z_diff; +# Slic3r::debugf "to %f due to near horizontal facet\n", $height; +# }else{ + if( $area_h > $z_diff) { + if($area_h < $height) { + Slic3r::debugf "cusp computation, height is reduced from %f", $height; + $height = $area_h; + Slic3r::debugf "to %f due to new cusp height\n", $height; + } + }else{ + Slic3r::debugf "cusp computation, height is reduced from %f", $height; + $height = $z_diff; + Slic3r::debugf "to z-diff: %f\n", $height; + } +# } + + $ordered_id++; + } + # lower height limit due to printer capabilities again + $height = $min_height if($height < $min_height); + } + + Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $area_value, $height; + + return $height; +} + +# Combined layer height, weighing between cusp and Ra factor +sub next_layer_height { + my $self = shift; + my ($z, $factor, $min_height, $max_height) = @_; + + # factor must be between 0-1, 0 is highest quality, 1 highest print speed + if($factor < 0 or $factor > 1) { + die "Speed / Quality factor must be in the interval [0:1]"; + } + + my $cusp_value = $factor * ($max_height-$min_height) + $min_height; + my $ra_value = 2 * $factor * ($max_height-$min_height) + $min_height; + + my $height = $max_height; + my $first_hit = 0; + + # find all facets intersecting the slice-layer + my $ordered_id = $self->current_facet; + while ($ordered_id <= $#{$self->ordered_facets}) { + + # facet's minimum is higher than slice_z -> end loop + if($self->ordered_facets->[$ordered_id]->[1] >= $z) { + last; + } + + # facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point + if($self->ordered_facets->[$ordered_id]->[2] > $z) { + # first event? + if(!$first_hit) { + $first_hit = 1; + $self->current_facet($ordered_id); + } + + #skip touching facets which could otherwise cause small cusp values + if($self->ordered_facets->[$ordered_id]->[2] <= $z+1) + { + $ordered_id++; + next; + } + + # compute cusp-height for this facet and store minimum of all heights + my $cusp = $self->_facet_cusp_height($ordered_id, $cusp_value); + my $ra = $self->_facet_ra_height($ordered_id, $cusp_value); + my $mixed_height = $self->_mixed_layer_height($ordered_id, $cusp_value, $ra_value, $min_height, $max_height, $factor); + $height = $mixed_height if($mixed_height < $height); + } + $ordered_id++; + } + # lower height limit due to printer capabilities + $height = $min_height if($height < $min_height); + + + # check for sloped facets inside the determined layer and correct height if necessary + if($height > $min_height){ + while ($ordered_id <= $#{$self->ordered_facets}) { + + # facet's minimum is higher than slice_z + height -> end loop + if($self->ordered_facets->[$ordered_id]->[1] >= ($z + scale $height)) { + last; + } + + #skip touching facets which could otherwise cause small cusp values + if($self->ordered_facets->[$ordered_id]->[2] <= $z+1) + { + $ordered_id++; + next; + } + + # Compute cusp-height for this facet and check against height. + my $cusp = $self->_facet_cusp_height($ordered_id, $cusp_value); + my $mixed_height = $self->_mixed_layer_height($ordered_id, $cusp_value, $ra_value, $min_height, $max_height, $factor); + + my $z_diff = unscale ($self->ordered_facets->[$ordered_id]->[1] - $z); + + +# # handle horizontal facets +# if ($self->normal_z->[$self->ordered_facets->[$ordered_id]->[0]] > 0.999) { +# Slic3r::debugf "cusp computation, height is reduced from %f", $height; +# $height = $z_diff; +# Slic3r::debugf "to %f due to near horizontal facet\n", $height; +# }else{ + if( $mixed_height > $z_diff) { + if($mixed_height < $height) { + Slic3r::debugf "cusp computation, height is reduced from %f", $height; + $height = $mixed_height; + Slic3r::debugf "to %f due to new cusp height\n", $height; + } + }else{ + Slic3r::debugf "cusp computation, height is reduced from %f", $height; + $height = $z_diff; + Slic3r::debugf "to z-diff: %f\n", $height; + } +# } + + $ordered_id++; + } + # lower height limit due to printer capabilities again + $height = $min_height if($height < $min_height); + } + + Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height; + + return $height; +} + sub cusp_height { my $self = shift; my ($z, $cusp_value, $min_height, $max_height) = @_; @@ -120,24 +333,24 @@ sub cusp_height { my $z_diff = unscale ($self->ordered_facets->[$ordered_id]->[1] - $z); - # handle horizontal facets - if ($self->normal_z->[$self->ordered_facets->[$ordered_id]->[0]] > 0.999) { +# # handle horizontal facets +# if ($self->normal_z->[$self->ordered_facets->[$ordered_id]->[0]] > 0.999) { +# Slic3r::debugf "cusp computation, height is reduced from %f", $height; +# $height = $z_diff; +# Slic3r::debugf "to %f due to near horizontal facet\n", $height; +# }else{ + if( $cusp > $z_diff) { + if($cusp < $height) { + Slic3r::debugf "cusp computation, height is reduced from %f", $height; + $height = $cusp; + Slic3r::debugf "to %f due to new cusp height\n", $height; + } + }else{ Slic3r::debugf "cusp computation, height is reduced from %f", $height; $height = $z_diff; - Slic3r::debugf "to %f due to near horizontal facet\n", $height; - }else{ - if( $cusp > $z_diff) { - if($cusp < $height) { - Slic3r::debugf "cusp computation, height is reduced from %f", $height; - $height = $cusp; - Slic3r::debugf "to %f due to new cusp height\n", $height; - } - }else{ - Slic3r::debugf "cusp computation, height is reduced from %f", $height; - $height = $z_diff; - Slic3r::debugf "to z-diff: %f\n", $height; - } + Slic3r::debugf "to z-diff: %f\n", $height; } +# } $ordered_id++; } @@ -150,6 +363,20 @@ sub cusp_height { return $height; } +sub _mixed_layer_height { + my ($self, $ordered_id, $cusp_value, $ra_value, $min_height, $max_height, $factor) = @_; + + my $cusp = max($min_height, min($max_height, $self->_facet_cusp_height($ordered_id, $cusp_value))); + my $ra = max($min_height, min($max_height, $self->_facet_ra_height($ordered_id, $ra_value))); + + my $normal_z = abs($self->normal_z->[$self->ordered_facets->[$ordered_id]->[0]]); + my $phi = asin($normal_z); + print "facet phi: " . rad2deg($phi) . " cusp_value: $cusp_value, ra_value: $ra_value\n"; + print "ra: $ra, cusp: $cusp\n\n"; + + return $factor * $cusp + (1-$factor) * $ra; +} + # computes the cusp height from a given facets normal and the cusp_value sub _facet_cusp_height { my $self = shift; @@ -160,6 +387,27 @@ sub _facet_cusp_height { return $cusp; } +sub _facet_ra_height { + my $self = shift; + my ($ordered_id, $ra_value) = @_; + + my $normal_z = abs($self->normal_z->[$self->ordered_facets->[$ordered_id]->[0]]); + #print "ra_value: $ra_value, facet normal: $normal_z\n"; + my $phi = asin($normal_z); + #print "phi: $phi\n"; + my $ra = $ra_value * cos($phi); + #print "ra: $ra\n\n"; + return $ra; +} + +sub _facet_area_height { + my ($self, $ordered_id, $area_value) = @_; + + my $normal_z = abs($self->normal_z->[$self->ordered_facets->[$ordered_id]->[0]]); + my $area_h = 1/(2*0.12263/$area_value + ($normal_z/2)/$area_value); + return $area_h; +} + # Returns the distance to the next horizontal facet in Z-dir # to consider horizontal object features in slice thickness sub horizontal_facet_distance { diff --git a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm index 9962309c9..f35d45fff 100644 --- a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm @@ -19,6 +19,8 @@ sub new { my $obj_idx = $self->{obj_idx} = $params{obj_idx}; my $plater = $self->{plater} = $parent; my $object = $self->{object} = $self->{plater}->{print}->get_object($self->{obj_idx}); + + $self->{update_spline_control} = 0; # Initialize 3D toolpaths preview if ($Slic3r::GUI::have_OpenGL) { @@ -32,26 +34,26 @@ sub new { $self->{splineControl} = Slic3r::GUI::Plater::SplineControl->new($self, Wx::Size->new(150, 200), $object); -# my $cusp_slider = $self->{cusp_slider} = Wx::Slider->new( -# $self, -1, -# 0, # default -# 0, # min -# # we set max to a bogus non-zero value because the MSW implementation of wxSlider -# # will skip drawing the slider if max <= min: -# 1, # max -# wxDefaultPosition, -# wxDefaultSize, -# wxHORIZONTAL, -# ); + my $cusp_slider = $self->{cusp_slider} = Wx::Slider->new( + $self, -1, + 0, # default + 0, # min + # we set max to a bogus non-zero value because the MSW implementation of wxSlider + # will skip drawing the slider if max <= min: + 1, # max + wxDefaultPosition, + wxDefaultSize, + wxHORIZONTAL, + ); - #my $cusp_label = $self->{cusp_label} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, - # [150,-1], wxALIGN_CENTRE_HORIZONTAL); - #$cusp_label->SetFont($Slic3r::GUI::small_font); + my $cusp_label = $self->{cusp_label} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, + [150,-1], wxALIGN_CENTRE_HORIZONTAL); + $cusp_label->SetFont($Slic3r::GUI::small_font); my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); $right_sizer->Add($self->{splineControl}, 1, wxEXPAND | wxALL, 0); - #$right_sizer->Add($cusp_slider, 0, wxEXPAND | wxALL, 0); - #$right_sizer->Add($cusp_label, 0, wxEXPAND | wxALL, 0); + $right_sizer->Add($cusp_slider, 0, wxEXPAND | wxALL, 0); + $right_sizer->Add($cusp_label, 0, wxEXPAND | wxALL, 0); $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); $self->{sizer}->Add($self->{preview3D}, 3, wxEXPAND | wxTOP | wxBOTTOM, 0) if $self->{preview3D}; @@ -91,36 +93,41 @@ sub new { }); # init cusp slider -# if($object->config->adaptive_slicing) { -# my $cusp_value = $object->config->get('cusp_value'); -# $cusp_label->SetLabel(sprintf 'Cusp value: %.2f mm', $cusp_value); -# $cusp_slider->SetRange(0, $max_height*100); -# $cusp_slider->SetValue($cusp_value*100); -# }else{ -# # disable slider -# $cusp_label->SetLabel("Cusp value: "); -# $cusp_label->Enable(0); -# $cusp_slider->Enable(0); -# } + if($object->config->adaptive_slicing) { + my $cusp_value = $object->config->get('cusp_value'); + $cusp_label->SetLabel(sprintf 'Cusp value: %.2f mm', $cusp_value); + $cusp_slider->SetRange(0, 100); + $cusp_slider->SetValue($cusp_value*100); + }else{ + # disable slider + $cusp_label->SetLabel("Cusp value: "); + $cusp_label->Enable(0); + $cusp_slider->Enable(0); + } -# EVT_SLIDER($self, $cusp_slider, sub { -# $self->{plater}->pause_background_process; -# my $cusp_value = $cusp_slider->GetValue/100; -# $cusp_label->SetLabel(sprintf 'Cusp value: %.2f mm', $cusp_value); -# my $success = $object->config->set('cusp_value', $cusp_value); -# # trigger re-slicing -# $self->{plater}->stop_background_process; -# $self->{object}->invalidate_step(STEP_SLICE); -# $self->{plater}->schedule_background_process; -# }); + EVT_SLIDER($self, $cusp_slider, sub { + $self->{plater}->pause_background_process; + my $cusp_value = $cusp_slider->GetValue/100; + $cusp_label->SetLabel(sprintf 'Cusp value: %.2f mm', $cusp_value); + my $success = $object->config->set('cusp_value', $cusp_value); + $object->layer_height_spline->setCuspValue($cusp_value); + # trigger re-slicing + $self->{plater}->stop_background_process; + $self->{object}->invalidate_step(STEP_SLICE); + $self->{plater}->schedule_background_process; + }); return $self; } sub reload_preview { my ($self) = @_; - #$self->{splineControl}->update; + $self->{splineControl}->update; $self->{preview3D}->reload_print; + if($self->{object}->layer_count-1 > 0) { + my $top_layer = $self->{object}->get_layer($self->{object}->layer_count-1); + $self->{preview3D}->set_z($top_layer->print_z); + } } 1; diff --git a/lib/Slic3r/GUI/Plater/SplineControl.pm b/lib/Slic3r/GUI/Plater/SplineControl.pm index 7bc7cf91f..e46030907 100644 --- a/lib/Slic3r/GUI/Plater/SplineControl.pm +++ b/lib/Slic3r/GUI/Plater/SplineControl.pm @@ -161,7 +161,9 @@ sub mouse_event { $self->{heights} = $self->{interactive_heights}; $self->{interactive_heights} = (); # update spline database - $self->{object}->layer_height_spline->updateLayerHeights($self->{heights}); + unless($self->{object}->layer_height_spline->updateLayerHeights($self->{heights})) { + die "Unable to update interpolated layers!\n"; + } $self->{interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; } $self->Refresh; @@ -175,7 +177,9 @@ sub mouse_event { $self->{heights} = $self->{interactive_heights}; $self->{interactive_heights} = (); # update spline database - $self->{object}->layer_height_spline->updateLayerHeights($self->{heights}); + unless($self->{object}->layer_height_spline->updateLayerHeights($self->{heights})) { + die "Unable to update interpolated layers!\n"; + } $self->{interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; } $self->Refresh; @@ -229,17 +233,19 @@ sub set_size_parameters { sub update { my $self = shift; - $self->{original_layers} = $self->{object}->layer_height_spline->getOriginalLayers; - $self->{original_interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; - $self->{interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; # Initialize to current values - - # initialize height vector - $self->{heights} = (); - $self->{interactive_heights} = (); - my $last_z = 0; - foreach my $z (@{$self->{original_layers}}) { - push (@{$self->{heights}}, $z - $last_z); - $last_z = $z; + if($self->{object}->layer_height_spline->layersUpdated) { + $self->{original_layers} = $self->{object}->layer_height_spline->getOriginalLayers; + $self->{original_interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; + $self->{interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; # Initialize to current values + + # initialize height vector + $self->{heights} = (); + $self->{interactive_heights} = (); + my $last_z = 0; + foreach my $z (@{$self->{original_layers}}) { + push (@{$self->{heights}}, $z - $last_z); + $last_z = $z; + } } $self->Refresh; } diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 0a1a709e6..5f050af9b 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -152,6 +152,10 @@ sub slice { if ($self->config->adaptive_slicing) { $height = 999; my $cusp_value = $self->config->get_value('cusp_value'); + if($self->layer_height_spline->getCuspValue >= 0) { + $self->config->set('cusp_value', $self->layer_height_spline->getCuspValue); + $cusp_value = $self->layer_height_spline->getCuspValue; + } Slic3r::debugf "\n Slice layer: %d\n", $id; @@ -159,7 +163,7 @@ sub slice { for my $region_id (0 .. ($self->region_count - 1)) { # get cusp height next if(!defined $adaptive_slicing[$region_id]); - my $cusp_height = $adaptive_slicing[$region_id]->cusp_height(scale $slice_z, $cusp_value, $min_height, $max_height); + my $cusp_height = $adaptive_slicing[$region_id]->next_layer_height_area(scale $slice_z, $cusp_value, $min_height, $max_height); # check for horizontal features and object size if($self->config->get_value('match_horizontal_surfaces')) { diff --git a/xs/src/libslic3r/LayerHeightSpline.cpp b/xs/src/libslic3r/LayerHeightSpline.cpp index 5b3e59885..c04cf2ce7 100644 --- a/xs/src/libslic3r/LayerHeightSpline.cpp +++ b/xs/src/libslic3r/LayerHeightSpline.cpp @@ -16,6 +16,9 @@ LayerHeightSpline::LayerHeightSpline(coordf_t object_height) { this->_is_valid = false; this->_update_required = true; + this->_layers_updated = false; + this->_layer_heights_updated = false; + this->_cusp_value = -1; } LayerHeightSpline::~LayerHeightSpline() @@ -81,6 +84,9 @@ bool LayerHeightSpline::setLayers(std::vector layers) this->_internal_layer_heights.insert(this->_internal_layer_heights.begin(), 0); this->_internal_layer_heights.push_back(0); + this->_layers_updated = true; + this->_layer_heights_updated = false; + return this->_updateBSpline(); } @@ -104,6 +110,9 @@ bool LayerHeightSpline::updateLayerHeights(std::vector heights) result = this->_updateBSpline(); } + this->_layers_updated = false; + this->_layer_heights_updated = true; + return result; } @@ -118,6 +127,8 @@ void LayerHeightSpline::clear() delete this->_layer_height_spline; this->_layer_height_spline = NULL; this->_is_valid = false; + this->_layers_updated = false; + this->_layer_heights_updated = false; } diff --git a/xs/src/libslic3r/LayerHeightSpline.hpp b/xs/src/libslic3r/LayerHeightSpline.hpp index ea02b93ae..0b618a5ac 100644 --- a/xs/src/libslic3r/LayerHeightSpline.hpp +++ b/xs/src/libslic3r/LayerHeightSpline.hpp @@ -18,17 +18,27 @@ class LayerHeightSpline void suppressUpdate(); bool setLayers(std::vector layers); bool updateLayerHeights(std::vector heights); + bool layersUpdated() const { return this->_layers_updated; }; // true if the basis set of layers was updated (by the slicing algorithm) + bool layerHeightsUpdated() const { return this->_layer_heights_updated; }; // true if the heights where updated (by the spline control user interface) void clear(); std::vector getOriginalLayers() const { return this->_original_layers; }; std::vector getInterpolatedLayers() const; const coordf_t getLayerHeightAt(coordf_t height); + void setCuspValue(coordf_t cusp_value) {this->_cusp_value = cusp_value;}; + coordf_t getCuspValue() {return this->_cusp_value;}; + private: bool _updateBSpline(); + + coordf_t _cusp_value; + coordf_t _object_height; bool _is_valid; bool _update_required; // this should be always true except if we want to generate new layers from this spline + bool _layers_updated; + bool _layer_heights_updated; std::vector _original_layers; std::vector _internal_layers; std::vector _internal_layer_heights; diff --git a/xs/xsp/LayerHeightSpline.xsp b/xs/xsp/LayerHeightSpline.xsp index ad5c5e58e..3f915230e 100644 --- a/xs/xsp/LayerHeightSpline.xsp +++ b/xs/xsp/LayerHeightSpline.xsp @@ -15,9 +15,14 @@ %code%{ RETVAL = THIS->setLayers(layers); %}; bool updateLayerHeights(std::vector heights) %code%{ RETVAL = THIS->updateLayerHeights(heights); %}; + bool layersUpdated(); + bool layerHeightsUpdated(); void clear(); std::vector getOriginalLayers(); std::vector getInterpolatedLayers(); coordf_t getLayerHeightAt(coordf_t height); //%code%{ RETVAL = THIS->upper_layer; %}; + + void setCuspValue(coordf_t cusp_value); + coordf_t getCuspValue(); }; From 67169254a048364047d90486fc20263b72a40629 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 15 Feb 2017 15:12:34 +0100 Subject: [PATCH 33/64] Workaround: ignore facets with invalid surface normal --- lib/Slic3r/AdaptiveSlicing.pm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/AdaptiveSlicing.pm b/lib/Slic3r/AdaptiveSlicing.pm index 1537edf76..4e6739a12 100644 --- a/lib/Slic3r/AdaptiveSlicing.pm +++ b/lib/Slic3r/AdaptiveSlicing.pm @@ -33,7 +33,9 @@ sub BUILD { if($normal_length > 0) { $self->normal_z->[$facet_id] = $normal->[Z]/$normal_length; }else{ # facet with area = 0 - $self->normal_z->[$facet_id] = [0 ,0 ,0]; + $self->normal_z->[$facet_id] = 0.01;#[0 ,0 ,0]; + print "facet with normal 0. p1: " . $vertices->[$facets->[$facet_id]->[0]]->[Z] . " p2: " . $vertices->[$facets->[$facet_id]->[1]]->[Z] . " p3: " . $vertices->[$facets->[$facet_id]->[2]]->[Z] . "\n"; + print "normal: " . $normal->[0] . ", " . $normal->[1] . ", " . $normal->[2] . "\n"; } } From b243f8e64fef249279f46b2b2ca4eef36988d8da Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 15 Feb 2017 15:14:55 +0100 Subject: [PATCH 34/64] Slider to control adaptive quality parameter --- lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm | 24 +++++++++++++++------ lib/Slic3r/GUI/Plater/SplineControl.pm | 22 ++++++++++--------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm index f35d45fff..3eb6eddf9 100644 --- a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm @@ -46,14 +46,26 @@ sub new { wxHORIZONTAL, ); - my $cusp_label = $self->{cusp_label} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, - [150,-1], wxALIGN_CENTRE_HORIZONTAL); + my $cusp_label = $self->{cusp_label} = Wx::StaticText->new($self, -1, " <-Quality", wxDefaultPosition, + [-1,-1], wxALIGN_LEFT); + my $value_label = $self->{value_label} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, + [50,-1], wxALIGN_CENTRE_HORIZONTAL | wxST_NO_AUTORESIZE); + my $speed_label = $self->{speed_label} = Wx::StaticText->new($self, -1, "Speed->", wxDefaultPosition, + [-1,-1], wxST_NO_AUTORESIZE | wxALIGN_RIGHT); $cusp_label->SetFont($Slic3r::GUI::small_font); + $value_label->SetFont($Slic3r::GUI::small_font); + $speed_label->SetFont($Slic3r::GUI::small_font); + + my $quality_label_sizer = Wx::BoxSizer->new(wxHORIZONTAL); + $quality_label_sizer->Add($cusp_label, 1, wxEXPAND | wxALL, 0); + $quality_label_sizer->Add($value_label, 1, wxEXPAND | wxALL, 0); + $quality_label_sizer->Add($speed_label, 1, wxEXPAND | wxALL, 0); my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); $right_sizer->Add($self->{splineControl}, 1, wxEXPAND | wxALL, 0); $right_sizer->Add($cusp_slider, 0, wxEXPAND | wxALL, 0); - $right_sizer->Add($cusp_label, 0, wxEXPAND | wxALL, 0); + $right_sizer->Add($quality_label_sizer, 0, wxEXPAND | wxALL, 0); + $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); $self->{sizer}->Add($self->{preview3D}, 3, wxEXPAND | wxTOP | wxBOTTOM, 0) if $self->{preview3D}; @@ -95,12 +107,12 @@ sub new { # init cusp slider if($object->config->adaptive_slicing) { my $cusp_value = $object->config->get('cusp_value'); - $cusp_label->SetLabel(sprintf 'Cusp value: %.2f mm', $cusp_value); + $value_label->SetLabel(sprintf '%.2f mm', $cusp_value); $cusp_slider->SetRange(0, 100); $cusp_slider->SetValue($cusp_value*100); }else{ # disable slider - $cusp_label->SetLabel("Cusp value: "); + $value_label->SetLabel(""); $cusp_label->Enable(0); $cusp_slider->Enable(0); } @@ -108,7 +120,7 @@ sub new { EVT_SLIDER($self, $cusp_slider, sub { $self->{plater}->pause_background_process; my $cusp_value = $cusp_slider->GetValue/100; - $cusp_label->SetLabel(sprintf 'Cusp value: %.2f mm', $cusp_value); + $value_label->SetLabel(sprintf '%.2f mm', $cusp_value); my $success = $object->config->set('cusp_value', $cusp_value); $object->layer_height_spline->setCuspValue($cusp_value); # trigger re-slicing diff --git a/lib/Slic3r/GUI/Plater/SplineControl.pm b/lib/Slic3r/GUI/Plater/SplineControl.pm index e46030907..8f1d7e5fe 100644 --- a/lib/Slic3r/GUI/Plater/SplineControl.pm +++ b/lib/Slic3r/GUI/Plater/SplineControl.pm @@ -24,7 +24,7 @@ sub new { $self->{line_pen} = Wx::Pen->new(Wx::Colour->new(50,50,50), 1, wxSOLID); $self->{original_pen} = Wx::Pen->new(Wx::Colour->new(200,200,200), 1, wxSOLID); $self->{interactive_pen} = Wx::Pen->new(Wx::Colour->new(255,0,0), 1, wxSOLID); - $self->{resulting_pen} = Wx::Pen->new(Wx::Colour->new(50,255,50), 1, wxSOLID); + $self->{resulting_pen} = Wx::Pen->new(Wx::Colour->new(5,120,160), 1, wxSOLID); $self->{user_drawn_background} = $^O ne 'darwin'; @@ -84,7 +84,7 @@ sub repaint { $dc->SetPen($self->{original_pen}); my $pl = $self->point_to_pixel(0, $z); my $pr = $self->point_to_pixel($layer_h, $z); - $dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); + #$dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); push (@points, $pr); $last_z = $z; } @@ -112,14 +112,16 @@ sub repaint { # draw resulting layers as lines $last_z = 0.0; @points = (); - foreach my $z (@{$self->{interpolated_layers}}) { - my $layer_h = $z - $last_z; - $dc->SetPen($self->{resulting_pen}); - my $pl = $self->point_to_pixel(0, $z); - my $pr = $self->point_to_pixel($layer_h, $z); - $dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); - push (@points, $pr); - $last_z = $z; + unless($self->{interactive_heights}) { + foreach my $z (@{$self->{interpolated_layers}}) { + my $layer_h = $z - $last_z; + $dc->SetPen($self->{resulting_pen}); + my $pl = $self->point_to_pixel(0, $z); + my $pr = $self->point_to_pixel($layer_h, $z); + $dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); + push (@points, $pr); + $last_z = $z; + } } # $dc->DrawSpline(\@points); From 4d81a6d017198fcebee17cff0f8d4ea8d8707ef6 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 15 Feb 2017 15:16:17 +0100 Subject: [PATCH 35/64] suppress re-generation of layer during gcode export --- 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 6bbb403ba..4edc0831b 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1260,6 +1260,12 @@ sub export_gcode { $self->object_list_changed; }); + # Supress re-initialization of spline based layer heights + #foreach my $object @{$self->{print}->objects} { + # $object->layer_height_spline->suppressUpdate; + # } + $_->layer_height_spline->suppressUpdate for @{$self->{print}->objects}; + # start background process, whose completion event handler # will detect $self->{export_gcode_output_file} and proceed with export $self->start_background_process; From dd46432e4ffa227530a90c96957393fb02b50f5c Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 16 Feb 2017 10:46:12 +0100 Subject: [PATCH 36/64] better rounding for layer height gradation --- lib/Slic3r/Print/Object.pm | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index dbf3b52b6..e46b05056 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -105,14 +105,23 @@ sub slice { my $height = 0; my $cusp_height = 0; my @layers = (); + + # determine min and max layer height from extruder capabilities. + my %extruders; + for my $region_id (0 .. ($self->region_count - 1)) { + foreach (qw(perimeter_extruder infill_extruder solid_infill_extruder)) { + my $extruder_id = $self->print->get_region($region_id)->config->get($_)-1; + $extruders{$extruder_id} = $extruder_id; + } + } + my $min_height = max(map {$self->print->config->get_at('min_layer_height', $_)} (values %extruders)); + my $max_height = min(map {$self->print->config->get_at('max_layer_height', $_)} (values %extruders)); if(!$self->layer_height_spline->updateRequired) { # layer heights are already generated, just update layers from spline @layers = @{$self->layer_height_spline->getInterpolatedLayers}; }else{ # create new set of layers # create stateful objects and variables for the adaptive slicing process my @adaptive_slicing; - my $min_height = 0; - my $max_height = 0; if ($self->config->adaptive_slicing) { for my $region_id (0 .. ($self->region_count - 1)) { my $mesh; @@ -133,16 +142,6 @@ sub slice { ); } } - - # determine min and max layer height from perimeter extruder capabilities. - if($self->region_count > 1) { # multimaterial object - $min_height = max(map {$self->print->config->get_at('min_layer_height', $_)} (0..($self->region_count-1))); - $max_height = min(map {$self->print->config->get_at('max_layer_height', $_)} (0..($self->region_count-1))); - }else{ #single material object - my $perimeter_extruder = $self->print->get_region(0)->config->get('perimeter_extruder')-1; - $min_height = $self->print->config->get_at('min_layer_height', $perimeter_extruder); - $max_height = $self->print->config->get_at('max_layer_height', $perimeter_extruder); - } } # loop until we have at least one layer and the max slice_z reaches the object height @@ -235,10 +234,16 @@ sub slice { foreach my $z (@layers) { $height = $z - $slice_z; - # apply z-gradation - if($gradation > 0) { - $height = $height - unscale((scale($height)) % (scale($gradation))); - } + # apply z-gradation + if($gradation > 0) { + my $gradation_effect = unscale((scale($height)) % (scale($gradation))); + if($gradation_effect > $gradation/2 && ($height + ($gradation-$gradation_effect)) <= $max_height) { # round up + $height = $height + ($gradation-$gradation_effect); + }else{ # round down + $height = $height - $gradation_effect; + } + #$height = $height - unscale((scale($height)) % (scale($gradation))); + } $print_z += $height; $slice_z += $height/2; From ece3a6e0adc42a271836733e36680bc4929f5b49 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 7 Mar 2017 12:20:37 +0100 Subject: [PATCH 37/64] Copy-constructor for LayerHeightSpline to use it for interactive interface --- lib/Slic3r.pm | 1 + lib/Slic3r/GUI/Plater/SplineControl.pm | 97 ++++++++++++++++++++------ xs/src/libslic3r/LayerHeightSpline.cpp | 35 +++++++--- xs/src/libslic3r/LayerHeightSpline.hpp | 1 + xs/xsp/LayerHeightSpline.xsp | 6 +- xs/xsp/my.map | 1 + xs/xsp/typemap.xspt | 1 + 7 files changed, 110 insertions(+), 32 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index ecba7ba8a..e2e7c50dd 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -226,6 +226,7 @@ sub thread_cleanup { *Slic3r::Geometry::BoundingBoxf::DESTROY = sub {}; *Slic3r::Geometry::BoundingBoxf3::DESTROY = sub {}; *Slic3r::Layer::PerimeterGenerator::DESTROY = sub {}; + *Slic3r::LayerHeightSpline::DESTROY = sub {}; *Slic3r::Line::DESTROY = sub {}; *Slic3r::Linef3::DESTROY = sub {}; *Slic3r::Model::DESTROY = sub {}; diff --git a/lib/Slic3r/GUI/Plater/SplineControl.pm b/lib/Slic3r/GUI/Plater/SplineControl.pm index 8f1d7e5fe..236b68903 100644 --- a/lib/Slic3r/GUI/Plater/SplineControl.pm +++ b/lib/Slic3r/GUI/Plater/SplineControl.pm @@ -79,33 +79,74 @@ sub repaint { # draw original layers as lines my $last_z = 0.0; my @points = (); - foreach my $z (@{$self->{original_interpolated_layers}}) { - my $layer_h = $z - $last_z; +# foreach my $z (@{$self->{original_interpolated_layers}}) { +# my $layer_h = $z - $last_z; +# $dc->SetPen($self->{original_pen}); +# my $pl = $self->point_to_pixel(0, $z); +# my $pr = $self->point_to_pixel($layer_h, $z); +# #$dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); +# push (@points, $pr); +# $last_z = $z; +# } +# +# $dc->DrawSpline(\@points); + if($self->{original_height_spline}) { + $last_z = 0.0; + @points = (); + #draw spline $dc->SetPen($self->{original_pen}); - my $pl = $self->point_to_pixel(0, $z); - my $pr = $self->point_to_pixel($layer_h, $z); - #$dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); - push (@points, $pr); - $last_z = $z; + @points = (); + foreach my $pixel (0..$size[1]) { + my @z = $self->pixel_to_point(Wx::Point->new(0, $pixel)); + my $h = $self->{original_height_spline}->getLayerHeightAt($z[1]); + my $p = $self->point_to_pixel($h, $z[1]); + push (@points, $p); + } + $dc->DrawLines(\@points); } - - $dc->DrawSpline(\@points); + + # # draw interactive (user modified) layers as lines - $last_z = 0.0; - @points = (); - if($self->{interactive_heights}) { - foreach my $i (0..@{$self->{interactive_heights}}-1) { - my $z = $self->{original_layers}[$i]; - my $layer_h = $self->{interactive_heights}[$i]; - $dc->SetPen($self->{interactive_pen}); - my $pl = $self->point_to_pixel(0, $z); - my $pr = $self->point_to_pixel($layer_h, $z); - $dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); - push (@points, $pr); +# $last_z = 0.0; +# @points = (); +# if($self->{interactive_heights}) { +# foreach my $i (0..@{$self->{interactive_heights}}-1) { +# my $z = $self->{original_layers}[$i]; +# my $layer_h = $self->{interactive_heights}[$i]; +# $dc->SetPen($self->{interactive_pen}); +# my $pl = $self->point_to_pixel(0, $z); +# my $pr = $self->point_to_pixel($layer_h, $z); +# $dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); +# push (@points, $pr); +# } +# +# $dc->DrawSpline(\@points); +# } + + if($self->{interactive_height_spline}) { + $last_z = 0.0; + @points = (); + # draw layer lines + foreach my $z (@{$self->{interactive_height_spline}->getInterpolatedLayers}) { + my $layer_h = $z - $last_z; + $dc->SetPen($self->{interactive_pen}); + my $pl = $self->point_to_pixel(0, $z); + my $pr = $self->point_to_pixel($layer_h, $z); + $dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); + $last_z = $z; + } + + #draw spline + $dc->SetPen($self->{interactive_pen}); + @points = (); + foreach my $pixel (0..$size[1]) { + my @z = $self->pixel_to_point(Wx::Point->new(0, $pixel)); + my $h = $self->{interactive_height_spline}->getLayerHeightAt($z[1]); + my $p = $self->point_to_pixel($h, $z[1]); + push (@points, $p); } - - $dc->DrawSpline(\@points); + $dc->DrawLines(\@points); } @@ -152,10 +193,12 @@ sub mouse_event { if ($event->LeftDown) { # start dragging $self->{left_drag_start_pos} = $pos; + $self->{interactive_height_spline} = $self->{object}->layer_height_spline->clone; } if ($event->RightDown) { # start dragging $self->{right_drag_start_pos} = $pos; + $self->{interactive_height_spline} = $self->{object}->layer_height_spline->clone; } } elsif ($event->LeftUp) { if($self->{left_drag_start_pos}) { @@ -171,6 +214,7 @@ sub mouse_event { $self->Refresh; $self->{object}->layer_height_spline->suppressUpdate; $self->{on_layer_update}->(@{$self->{interpolated_layers}}); + $self->{interactive_height_spline} = undef; } $self->{left_drag_start_pos} = undef; } elsif ($event->RightUp) { @@ -187,6 +231,7 @@ sub mouse_event { $self->Refresh; $self->{object}->layer_height_spline->suppressUpdate; $self->{on_layer_update}->(@{$self->{interpolated_layers}}); + $self->{interactive_height_spline} = undef; } $self->{right_drag_start_pos} = undef; } elsif ($event->Dragging) { @@ -197,6 +242,10 @@ sub mouse_event { # compute updated interactive layer heights $self->_interactive_quadratic_curve($start_pos[1], $obj_pos[0], $range); + + unless($self->{interactive_height_spline}->updateLayerHeights($self->{interactive_heights})) { + die "Unable to update interactive interpolated layers!\n"; + } $self->Refresh; } elsif($self->{right_drag_start_pos}) { my @start_pos = $self->pixel_to_point($self->{right_drag_start_pos}); @@ -204,6 +253,9 @@ sub mouse_event { # compute updated interactive layer heights $self->_interactive_linear_curve($start_pos[1], $obj_pos[0], $range); + unless($self->{interactive_height_spline}->updateLayerHeights($self->{interactive_heights})) { + die "Unable to update interactive interpolated layers!\n"; + } $self->Refresh; } } elsif ($event->Moving) { @@ -236,6 +288,7 @@ sub update { my $self = shift; if($self->{object}->layer_height_spline->layersUpdated) { + $self->{original_height_spline} = $self->{object}->layer_height_spline->clone; $self->{original_layers} = $self->{object}->layer_height_spline->getOriginalLayers; $self->{original_interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; $self->{interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; # Initialize to current values diff --git a/xs/src/libslic3r/LayerHeightSpline.cpp b/xs/src/libslic3r/LayerHeightSpline.cpp index c04cf2ce7..596a40932 100644 --- a/xs/src/libslic3r/LayerHeightSpline.cpp +++ b/xs/src/libslic3r/LayerHeightSpline.cpp @@ -21,6 +21,23 @@ LayerHeightSpline::LayerHeightSpline(coordf_t object_height) this->_cusp_value = -1; } +LayerHeightSpline::LayerHeightSpline(const LayerHeightSpline &other) +: _object_height(other._object_height), _layer_height_spline(NULL) +{ + + this->_original_layers = other._original_layers; + this->_internal_layers = other._internal_layers; + this->_internal_layer_heights = other._internal_layer_heights; + this->_is_valid = other._is_valid; + this->_update_required = other._update_required; + this->_layers_updated = other._layers_updated; + this->_layer_heights_updated = other._layer_heights_updated; + this->_cusp_value = other._cusp_value; + if(this->_is_valid) { + this->_updateBSpline(); + } +} + LayerHeightSpline::~LayerHeightSpline() { if (this->_layer_height_spline) { @@ -80,9 +97,9 @@ bool LayerHeightSpline::setLayers(std::vector layers) // add 0-values at both ends to achieve correct boundary conditions this->_internal_layers = this->_original_layers; this->_internal_layers.insert(this->_internal_layers.begin(), 0); // add z = 0 to the front - this->_internal_layers.push_back(this->_internal_layers.back()+1); // and object_height + 1 to the end - this->_internal_layer_heights.insert(this->_internal_layer_heights.begin(), 0); - this->_internal_layer_heights.push_back(0); + //this->_internal_layers.push_back(this->_internal_layers.back()+1); // and object_height + 1 to the end + this->_internal_layer_heights.insert(this->_internal_layer_heights.begin(), this->_internal_layer_heights[0]); + //this->_internal_layer_heights.push_back(0); this->_layers_updated = true; this->_layer_heights_updated = false; @@ -102,12 +119,14 @@ bool LayerHeightSpline::updateLayerHeights(std::vector heights) bool result = false; // do we receive the correct number of values? - if(heights.size() == this->_internal_layers.size()-2) { + if(heights.size() == this->_internal_layers.size()-1) { this->_internal_layer_heights = heights; - // add leading an trailing 0-value - this->_internal_layer_heights.insert(this->_internal_layer_heights.begin(), 0); - this->_internal_layer_heights.push_back(0); + // add leading and trailing 0-value + this->_internal_layer_heights.insert(this->_internal_layer_heights.begin(), this->_internal_layer_heights[0]); + //this->_internal_layer_heights.push_back(0); result = this->_updateBSpline(); + }else{ + std::cerr << "Unable to update layer heights. You provided " << heights.size() << " layers, but " << this->_internal_layers.size()-1 << " expected" << std::endl; } this->_layers_updated = false; @@ -186,7 +205,7 @@ bool LayerHeightSpline::_updateBSpline() this->_internal_layers.size(), &this->_internal_layer_heights[0], 0, - 0, + 1, 0); if (this->_layer_height_spline->ok()) { diff --git a/xs/src/libslic3r/LayerHeightSpline.hpp b/xs/src/libslic3r/LayerHeightSpline.hpp index 0b618a5ac..3fe9c09a4 100644 --- a/xs/src/libslic3r/LayerHeightSpline.hpp +++ b/xs/src/libslic3r/LayerHeightSpline.hpp @@ -12,6 +12,7 @@ class LayerHeightSpline { public: LayerHeightSpline(coordf_t object_height); + LayerHeightSpline(const LayerHeightSpline &other); ~LayerHeightSpline(); bool hasData(); // indicate that we have valid data bool updateRequired(); // indicate whether we want to generate a new spline from the layers diff --git a/xs/xsp/LayerHeightSpline.xsp b/xs/xsp/LayerHeightSpline.xsp index 3f915230e..a7b33608f 100644 --- a/xs/xsp/LayerHeightSpline.xsp +++ b/xs/xsp/LayerHeightSpline.xsp @@ -6,7 +6,10 @@ %} %name{Slic3r::LayerHeightSpline} class LayerHeightSpline { - // owned by PrintObject, no constructor/destructor + LayerHeightSpline(double object_height); + ~LayerHeightSpline(); + Clone clone() + %code%{ RETVAL = THIS; %}; bool hasData(); bool updateRequired(); @@ -21,7 +24,6 @@ std::vector getOriginalLayers(); std::vector getInterpolatedLayers(); coordf_t getLayerHeightAt(coordf_t height); - //%code%{ RETVAL = THIS->upper_layer; %}; void setCuspValue(coordf_t cusp_value); coordf_t getCuspValue(); diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 69c1831cc..66fa26e78 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -180,6 +180,7 @@ Ref O_OBJECT_SLIC3R_T LayerHeightSpline* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T +Clone O_OBJECT_SLIC3R_T PlaceholderParser* O_OBJECT_SLIC3R Ref O_OBJECT_SLIC3R_T diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index fbc2d03f5..7df7dfaf2 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -135,6 +135,7 @@ %typemap{LayerHeightSpline*}; %typemap{Ref}{simple}; +%typemap{Clone}{simple}; %typemap{SupportLayer*}; %typemap{Ref}{simple}; From 22ffb76fb1dcee1d5903e43586bb91d085ab4757 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 7 Mar 2017 12:51:58 +0100 Subject: [PATCH 38/64] Implemented adaptive layer height algorithm based on volumetric surface quality --- lib/Slic3r/AdaptiveSlicing.pm | 298 +++----------------- lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm | 34 +-- lib/Slic3r/GUI/Tab.pm | 6 +- lib/Slic3r/Print/Object.pm | 24 +- t/adaptive_slicing.t | 36 +-- xs/src/libslic3r/PrintConfig.cpp | 18 +- xs/src/libslic3r/PrintConfig.hpp | 4 +- xs/src/libslic3r/PrintObject.cpp | 2 +- 8 files changed, 103 insertions(+), 319 deletions(-) diff --git a/lib/Slic3r/AdaptiveSlicing.pm b/lib/Slic3r/AdaptiveSlicing.pm index 4e6739a12..c42f55d02 100644 --- a/lib/Slic3r/AdaptiveSlicing.pm +++ b/lib/Slic3r/AdaptiveSlicing.pm @@ -5,6 +5,15 @@ use List::Util qw(min max); use Math::Trig qw(asin acos deg2rad rad2deg); use Slic3r::Geometry qw(X Y Z triangle_normal scale unscale); +# This constant essentially describes the volumetric error at the surface which is induced +# by stacking "elliptic" extrusion threads. +# It is empirically determined by +# 1. measuring the surface profile of printed parts to find +# the ratio between layer height and profile height and then +# 2. computing the geometric difference between the model-surface and the elliptic profile. +# [Link to detailed description follows] +use constant SURFACE_CONST => 0.18403; + # public has 'mesh' => (is => 'ro', required => 1); has 'size' => (is => 'ro', required => 1); @@ -34,8 +43,8 @@ sub BUILD { $self->normal_z->[$facet_id] = $normal->[Z]/$normal_length; }else{ # facet with area = 0 $self->normal_z->[$facet_id] = 0.01;#[0 ,0 ,0]; - print "facet with normal 0. p1: " . $vertices->[$facets->[$facet_id]->[0]]->[Z] . " p2: " . $vertices->[$facets->[$facet_id]->[1]]->[Z] . " p3: " . $vertices->[$facets->[$facet_id]->[2]]->[Z] . "\n"; - print "normal: " . $normal->[0] . ", " . $normal->[1] . ", " . $normal->[2] . "\n"; + #print "facet with normal 0. p1: " . $vertices->[$facets->[$facet_id]->[0]]->[Z] . " p2: " . $vertices->[$facets->[$facet_id]->[1]]->[Z] . " p3: " . $vertices->[$facets->[$facet_id]->[2]]->[Z] . "\n"; + #print "normal: " . $normal->[0] . ", " . $normal->[1] . ", " . $normal->[2] . "\n"; } } @@ -55,130 +64,25 @@ sub BUILD { for (my $i = 0; $i <= $#sort_facets; $i++) { $self->ordered_facets->[$i] = $sort_facets[$i]; } - # initialize pointer for cusp_height run + # initialize pointer to iterate over the object $self->current_facet(0); } -# Combined layer height, weighing between cusp and Ra factor -sub next_layer_height_area { - my $self = shift; - my ($z, $factor, $min_height, $max_height) = @_; - - # factor must be between 0-1, 0 is highest quality, 1 highest print speed - if($factor < 0 or $factor > 1) { - die "Speed / Quality factor must be in the interval [0:1]"; - } - - my $volume_factor = 0.12263; - my $delta_min = $volume_factor * $min_height * 2; - my $delta_max = $volume_factor * $max_height * 2 + 0.5*$max_height; - - my $area_value = $factor * ($delta_max-$delta_min) + $delta_min; - - my $height = $max_height; - my $first_hit = 0; - - # find all facets intersecting the slice-layer - my $ordered_id = $self->current_facet; - while ($ordered_id <= $#{$self->ordered_facets}) { - - # facet's minimum is higher than slice_z -> end loop - if($self->ordered_facets->[$ordered_id]->[1] >= $z) { - last; - } - - # facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point - if($self->ordered_facets->[$ordered_id]->[2] > $z) { - # first event? - if(!$first_hit) { - $first_hit = 1; - $self->current_facet($ordered_id); - } - - #skip touching facets which could otherwise cause small cusp values - if($self->ordered_facets->[$ordered_id]->[2] <= $z+1) - { - $ordered_id++; - next; - } - - # compute cusp-height for this facet and store minimum of all heights - my $area_h = $self->_facet_area_height($ordered_id, $area_value); - $area_h = max($min_height, min($max_height, $area_h)); - $height = $area_h if($area_h < $height); - } - $ordered_id++; - } - # lower height limit due to printer capabilities - $height = $min_height if($height < $min_height); - - - # check for sloped facets inside the determined layer and correct height if necessary - if($height > $min_height){ - while ($ordered_id <= $#{$self->ordered_facets}) { - - # facet's minimum is higher than slice_z + height -> end loop - if($self->ordered_facets->[$ordered_id]->[1] >= ($z + scale $height)) { - last; - } - - #skip touching facets which could otherwise cause small cusp values - if($self->ordered_facets->[$ordered_id]->[2] <= $z+1) - { - $ordered_id++; - next; - } - - # Compute cusp-height for this facet and check against height. - my $area_h = $self->_facet_area_height($ordered_id, $area_value); - $area_h = max($min_height, min($max_height, $area_h)); - - my $z_diff = unscale ($self->ordered_facets->[$ordered_id]->[1] - $z); - - -# # handle horizontal facets -# if ($self->normal_z->[$self->ordered_facets->[$ordered_id]->[0]] > 0.999) { -# Slic3r::debugf "cusp computation, height is reduced from %f", $height; -# $height = $z_diff; -# Slic3r::debugf "to %f due to near horizontal facet\n", $height; -# }else{ - if( $area_h > $z_diff) { - if($area_h < $height) { - Slic3r::debugf "cusp computation, height is reduced from %f", $height; - $height = $area_h; - Slic3r::debugf "to %f due to new cusp height\n", $height; - } - }else{ - Slic3r::debugf "cusp computation, height is reduced from %f", $height; - $height = $z_diff; - Slic3r::debugf "to z-diff: %f\n", $height; - } -# } - - $ordered_id++; - } - # lower height limit due to printer capabilities again - $height = $min_height if($height < $min_height); - } - - Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $area_value, $height; - - return $height; -} - -# Combined layer height, weighing between cusp and Ra factor +# find height of the next layer by analyzing the angle of all facets intersecting the new layer sub next_layer_height { my $self = shift; - my ($z, $factor, $min_height, $max_height) = @_; + my ($z, $quality_factor, $min_height, $max_height) = @_; # factor must be between 0-1, 0 is highest quality, 1 highest print speed - if($factor < 0 or $factor > 1) { - die "Speed / Quality factor must be in the interval [0:1]"; + if($quality_factor < 0 or $quality_factor > 1) { + die "Speed / Quality factor must be in the interval [0:1]"; } + + my $delta_min = SURFACE_CONST * $min_height; + my $delta_max = SURFACE_CONST * $max_height + 0.5*$max_height; - my $cusp_value = $factor * ($max_height-$min_height) + $min_height; - my $ra_value = 2 * $factor * ($max_height-$min_height) + $min_height; + my $scaled_quality_factor = $quality_factor * ($delta_max-$delta_min) + $delta_min; my $height = $max_height; my $first_hit = 0; @@ -192,7 +96,7 @@ sub next_layer_height { last; } - # facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point + # facet's maximum is higher than slice_z -> store the first event for next layer_height call to begin at this point if($self->ordered_facets->[$ordered_id]->[2] > $z) { # first event? if(!$first_hit) { @@ -200,23 +104,20 @@ sub next_layer_height { $self->current_facet($ordered_id); } - #skip touching facets which could otherwise cause small cusp values + #skip touching facets which could otherwise cause small height values if($self->ordered_facets->[$ordered_id]->[2] <= $z+1) { $ordered_id++; next; } - # compute cusp-height for this facet and store minimum of all heights - my $cusp = $self->_facet_cusp_height($ordered_id, $cusp_value); - my $ra = $self->_facet_ra_height($ordered_id, $cusp_value); - my $mixed_height = $self->_mixed_layer_height($ordered_id, $cusp_value, $ra_value, $min_height, $max_height, $factor); - $height = $mixed_height if($mixed_height < $height); + # compute layer-height for this facet and store minimum of all heights + $height = min($height, $self->_facet_height($ordered_id, $scaled_quality_factor)); } $ordered_id++; } # lower height limit due to printer capabilities - $height = $min_height if($height < $min_height); + $height = max($min_height, $height); # check for sloped facets inside the determined layer and correct height if necessary @@ -228,16 +129,16 @@ sub next_layer_height { last; } - #skip touching facets which could otherwise cause small cusp values + #skip touching facets which could otherwise cause small height values if($self->ordered_facets->[$ordered_id]->[2] <= $z+1) { $ordered_id++; next; } - # Compute cusp-height for this facet and check against height. - my $cusp = $self->_facet_cusp_height($ordered_id, $cusp_value); - my $mixed_height = $self->_mixed_layer_height($ordered_id, $cusp_value, $ra_value, $min_height, $max_height, $factor); + # Compute new height for this facet and check against height. + my $reduced_height = $self->_facet_height($ordered_id, $scaled_quality_factor); + #$area_h = max($min_height, min($max_height, $area_h)); my $z_diff = unscale ($self->ordered_facets->[$ordered_id]->[1] - $z); @@ -248,14 +149,14 @@ sub next_layer_height { # $height = $z_diff; # Slic3r::debugf "to %f due to near horizontal facet\n", $height; # }else{ - if( $mixed_height > $z_diff) { - if($mixed_height < $height) { - Slic3r::debugf "cusp computation, height is reduced from %f", $height; - $height = $mixed_height; - Slic3r::debugf "to %f due to new cusp height\n", $height; + if( $reduced_height > $z_diff) { + if($reduced_height < $height) { + Slic3r::debugf "adaptive layer computation: height is reduced from %f", $height; + $height = $reduced_height; + Slic3r::debugf "to %f due to higher facet\n", $height; } }else{ - Slic3r::debugf "cusp computation, height is reduced from %f", $height; + Slic3r::debugf "adaptive layer computation: height is reduced from %f", $height; $height = $z_diff; Slic3r::debugf "to z-diff: %f\n", $height; } @@ -264,121 +165,16 @@ sub next_layer_height { $ordered_id++; } # lower height limit due to printer capabilities again - $height = $min_height if($height < $min_height); + $height = max($min_height, $height); } - Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height; + Slic3r::debugf "adaptive layer computation, layer-bottom at z:%f, quality_factor:%f, resulting layer height:%f\n", unscale $z, $quality_factor, $height; return $height; } -sub cusp_height { - my $self = shift; - my ($z, $cusp_value, $min_height, $max_height) = @_; - - my $height = $max_height; - my $first_hit = 0; - - # find all facets intersecting the slice-layer - my $ordered_id = $self->current_facet; - while ($ordered_id <= $#{$self->ordered_facets}) { - - # facet's minimum is higher than slice_z -> end loop - if($self->ordered_facets->[$ordered_id]->[1] >= $z) { - last; - } - - # facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point - if($self->ordered_facets->[$ordered_id]->[2] > $z) { - # first event? - if(!$first_hit) { - $first_hit = 1; - $self->current_facet($ordered_id); - } - - #skip touching facets which could otherwise cause small cusp values - if($self->ordered_facets->[$ordered_id]->[2] <= $z+1) - { - $ordered_id++; - next; - } - - # compute cusp-height for this facet and store minimum of all heights - my $cusp = $self->_facet_cusp_height($ordered_id, $cusp_value); - $height = $cusp if($cusp < $height); - } - $ordered_id++; - } - # lower height limit due to printer capabilities - $height = $min_height if($height < $min_height); - - - # check for sloped facets inside the determined layer and correct height if necessary - if($height > $min_height){ - while ($ordered_id <= $#{$self->ordered_facets}) { - - # facet's minimum is higher than slice_z + height -> end loop - if($self->ordered_facets->[$ordered_id]->[1] >= ($z + scale $height)) { - last; - } - - #skip touching facets which could otherwise cause small cusp values - if($self->ordered_facets->[$ordered_id]->[2] <= $z+1) - { - $ordered_id++; - next; - } - - # Compute cusp-height for this facet and check against height. - my $cusp = $self->_facet_cusp_height($ordered_id, $cusp_value); - - my $z_diff = unscale ($self->ordered_facets->[$ordered_id]->[1] - $z); -# # handle horizontal facets -# if ($self->normal_z->[$self->ordered_facets->[$ordered_id]->[0]] > 0.999) { -# Slic3r::debugf "cusp computation, height is reduced from %f", $height; -# $height = $z_diff; -# Slic3r::debugf "to %f due to near horizontal facet\n", $height; -# }else{ - if( $cusp > $z_diff) { - if($cusp < $height) { - Slic3r::debugf "cusp computation, height is reduced from %f", $height; - $height = $cusp; - Slic3r::debugf "to %f due to new cusp height\n", $height; - } - }else{ - Slic3r::debugf "cusp computation, height is reduced from %f", $height; - $height = $z_diff; - Slic3r::debugf "to z-diff: %f\n", $height; - } -# } - - $ordered_id++; - } - # lower height limit due to printer capabilities again - $height = $min_height if($height < $min_height); - } - - Slic3r::debugf "cusp computation, layer-bottom at z:%f, cusp_value:%f, resulting layer height:%f\n", unscale $z, $cusp_value, $height; - - return $height; -} - -sub _mixed_layer_height { - my ($self, $ordered_id, $cusp_value, $ra_value, $min_height, $max_height, $factor) = @_; - - my $cusp = max($min_height, min($max_height, $self->_facet_cusp_height($ordered_id, $cusp_value))); - my $ra = max($min_height, min($max_height, $self->_facet_ra_height($ordered_id, $ra_value))); - - my $normal_z = abs($self->normal_z->[$self->ordered_facets->[$ordered_id]->[0]]); - my $phi = asin($normal_z); - print "facet phi: " . rad2deg($phi) . " cusp_value: $cusp_value, ra_value: $ra_value\n"; - print "ra: $ra, cusp: $cusp\n\n"; - - return $factor * $cusp + (1-$factor) * $ra; -} - # computes the cusp height from a given facets normal and the cusp_value sub _facet_cusp_height { my $self = shift; @@ -389,25 +185,13 @@ sub _facet_cusp_height { return $cusp; } -sub _facet_ra_height { - my $self = shift; - my ($ordered_id, $ra_value) = @_; - - my $normal_z = abs($self->normal_z->[$self->ordered_facets->[$ordered_id]->[0]]); - #print "ra_value: $ra_value, facet normal: $normal_z\n"; - my $phi = asin($normal_z); - #print "phi: $phi\n"; - my $ra = $ra_value * cos($phi); - #print "ra: $ra\n\n"; - return $ra; -} -sub _facet_area_height { - my ($self, $ordered_id, $area_value) = @_; +sub _facet_height { + my ($self, $ordered_id, $scaled_quality_factor) = @_; my $normal_z = abs($self->normal_z->[$self->ordered_facets->[$ordered_id]->[0]]); - my $area_h = 1/(2*0.12263/$area_value + ($normal_z/2)/$area_value); - return $area_h; + my $height = $scaled_quality_factor/(SURFACE_CONST + $normal_z/2); + return ($normal_z == 0) ? 9999 : $height; } # Returns the distance to the next horizontal facet in Z-dir diff --git a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm index 3eb6eddf9..748080a9b 100644 --- a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm @@ -34,7 +34,7 @@ sub new { $self->{splineControl} = Slic3r::GUI::Plater::SplineControl->new($self, Wx::Size->new(150, 200), $object); - my $cusp_slider = $self->{cusp_slider} = Wx::Slider->new( + my $quality_slider = $self->{quality_slider} = Wx::Slider->new( $self, -1, 0, # default 0, # min @@ -46,24 +46,24 @@ sub new { wxHORIZONTAL, ); - my $cusp_label = $self->{cusp_label} = Wx::StaticText->new($self, -1, " <-Quality", wxDefaultPosition, + my $quality_label = $self->{quality_label} = Wx::StaticText->new($self, -1, " <-Quality", wxDefaultPosition, [-1,-1], wxALIGN_LEFT); my $value_label = $self->{value_label} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, [50,-1], wxALIGN_CENTRE_HORIZONTAL | wxST_NO_AUTORESIZE); my $speed_label = $self->{speed_label} = Wx::StaticText->new($self, -1, "Speed->", wxDefaultPosition, [-1,-1], wxST_NO_AUTORESIZE | wxALIGN_RIGHT); - $cusp_label->SetFont($Slic3r::GUI::small_font); + $quality_label->SetFont($Slic3r::GUI::small_font); $value_label->SetFont($Slic3r::GUI::small_font); $speed_label->SetFont($Slic3r::GUI::small_font); my $quality_label_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $quality_label_sizer->Add($cusp_label, 1, wxEXPAND | wxALL, 0); + $quality_label_sizer->Add($quality_label, 1, wxEXPAND | wxALL, 0); $quality_label_sizer->Add($value_label, 1, wxEXPAND | wxALL, 0); $quality_label_sizer->Add($speed_label, 1, wxEXPAND | wxALL, 0); my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); $right_sizer->Add($self->{splineControl}, 1, wxEXPAND | wxALL, 0); - $right_sizer->Add($cusp_slider, 0, wxEXPAND | wxALL, 0); + $right_sizer->Add($quality_slider, 0, wxEXPAND | wxALL, 0); $right_sizer->Add($quality_label_sizer, 0, wxEXPAND | wxALL, 0); @@ -104,25 +104,25 @@ sub new { $self->{preview3D}->canvas->Render; }); - # init cusp slider + # init quality slider if($object->config->adaptive_slicing) { - my $cusp_value = $object->config->get('cusp_value'); - $value_label->SetLabel(sprintf '%.2f mm', $cusp_value); - $cusp_slider->SetRange(0, 100); - $cusp_slider->SetValue($cusp_value*100); + my $quality_value = $object->config->get('adaptive_slicing_quality'); + $value_label->SetLabel(sprintf '%.2f', $quality_value); + $quality_slider->SetRange(0, 100); + $quality_slider->SetValue($quality_value*100); }else{ # disable slider $value_label->SetLabel(""); - $cusp_label->Enable(0); - $cusp_slider->Enable(0); + $quality_label->Enable(0); + $quality_slider->Enable(0); } - EVT_SLIDER($self, $cusp_slider, sub { + EVT_SLIDER($self, $quality_slider, sub { $self->{plater}->pause_background_process; - my $cusp_value = $cusp_slider->GetValue/100; - $value_label->SetLabel(sprintf '%.2f mm', $cusp_value); - my $success = $object->config->set('cusp_value', $cusp_value); - $object->layer_height_spline->setCuspValue($cusp_value); + my $quality_value = $quality_slider->GetValue/100; + $value_label->SetLabel(sprintf '%.2f', $quality_value); + my $success = $object->config->set('adaptive_slicing_quality', $quality_value); + $object->layer_height_spline->setCuspValue($quality_value); # trigger re-slicing $self->{plater}->stop_background_process; $self->{object}->invalidate_step(STEP_SLICE); diff --git a/lib/Slic3r/GUI/Tab.pm b/lib/Slic3r/GUI/Tab.pm index ab8fa0bde..f4e1e579f 100644 --- a/lib/Slic3r/GUI/Tab.pm +++ b/lib/Slic3r/GUI/Tab.pm @@ -461,7 +461,7 @@ sub build { my $self = shift; $self->init_config_options(qw( - adaptive_slicing adaptive_slicing_z_gradation cusp_value match_horizontal_surfaces + adaptive_slicing adaptive_slicing_quality adaptive_slicing_z_gradation match_horizontal_surfaces layer_height first_layer_height perimeters spiral_vase top_solid_layers bottom_solid_layers @@ -509,7 +509,7 @@ sub build { $optgroup->append_single_option_line('layer_height'); $optgroup->append_single_option_line('first_layer_height'); $optgroup->append_single_option_line('adaptive_slicing'); - $optgroup->append_single_option_line('cusp_value'); + $optgroup->append_single_option_line('adaptive_slicing_quality'); $optgroup->append_single_option_line('adaptive_slicing_z_gradation'); $optgroup->append_single_option_line('match_horizontal_surfaces'); } @@ -827,7 +827,7 @@ sub _update { my $have_adaptive_slicing = $config->adaptive_slicing; $self->get_field($_)->toggle($have_adaptive_slicing) - for qw(cusp_value adaptive_slicing_z_gradation match_horizontal_surfaces); + for qw(adaptive_slicing_quality adaptive_slicing_z_gradation match_horizontal_surfaces); $self->get_field($_)->toggle(!$have_adaptive_slicing) for qw(layer_height); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index e46b05056..cbb9de2ec 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -103,7 +103,7 @@ sub slice { my $slice_z = 0; my $height = 0; - my $cusp_height = 0; + my $adaptive_height = 0; my @layers = (); # determine min and max layer height from extruder capabilities. @@ -150,10 +150,10 @@ sub slice { if ($self->config->adaptive_slicing) { $height = 999; - my $cusp_value = $self->config->get_value('cusp_value'); + my $adaptive_quality = $self->config->get_value('adaptive_slicing_quality'); if($self->layer_height_spline->getCuspValue >= 0) { - $self->config->set('cusp_value', $self->layer_height_spline->getCuspValue); - $cusp_value = $self->layer_height_spline->getCuspValue; + $self->config->set('adaptive_slicing_quality', $self->layer_height_spline->getCuspValue); + $adaptive_quality = $self->layer_height_spline->getCuspValue; } Slic3r::debugf "\n Slice layer: %d\n", $id; @@ -162,29 +162,29 @@ sub slice { for my $region_id (0 .. ($self->region_count - 1)) { # get cusp height next if(!defined $adaptive_slicing[$region_id]); - my $cusp_height = $adaptive_slicing[$region_id]->next_layer_height_area(scale $slice_z, $cusp_value, $min_height, $max_height); + my $adaptive_height = $adaptive_slicing[$region_id]->next_layer_height(scale $slice_z, $adaptive_quality, $min_height, $max_height); # check for horizontal features and object size if($self->config->get_value('match_horizontal_surfaces')) { - my $horizontal_dist = $adaptive_slicing[$region_id]->horizontal_facet_distance(scale $slice_z+$cusp_height, $min_height); + my $horizontal_dist = $adaptive_slicing[$region_id]->horizontal_facet_distance(scale $slice_z+$adaptive_height, $min_height); if(($horizontal_dist < $min_height) && ($horizontal_dist > 0)) { Slic3r::debugf "Horizontal feature ahead, distance: %f\n", $horizontal_dist; # can we shrink the current layer a bit? - if($cusp_height-($min_height-$horizontal_dist) > $min_height) { + if($adaptive_height-($min_height-$horizontal_dist) > $min_height) { # yes we can - $cusp_height = $cusp_height-($min_height-$horizontal_dist); - Slic3r::debugf "Shrink layer height to %f\n", $cusp_height; + $adaptive_height = $adaptive_height-($min_height-$horizontal_dist); + Slic3r::debugf "Shrink layer height to %f\n", $adaptive_height; }else{ # no, current layer would become too thin - $cusp_height = $cusp_height+$horizontal_dist; - Slic3r::debugf "Widen layer height to %f\n", $cusp_height; + $adaptive_height = $adaptive_height+$horizontal_dist; + Slic3r::debugf "Widen layer height to %f\n", $adaptive_height; } } } $height = ($id == 0) ? $self->config->get_value('first_layer_height') - : min($cusp_height, $height); + : min($adaptive_height, $height); } }else{ diff --git a/t/adaptive_slicing.t b/t/adaptive_slicing.t index 6637c8db6..c1090fee9 100644 --- a/t/adaptive_slicing.t +++ b/t/adaptive_slicing.t @@ -71,38 +71,38 @@ my $adaptive_slicing = Slic3r::AdaptiveSlicing->new( ); -subtest 'max cusp_height limited by extruder capabilities' => sub { +subtest 'max layer_height limited by extruder capabilities' => sub { plan tests => 3; - is ($adaptive_slicing->cusp_height(scale 1, 0.2, 0.1, 0.15), 0.15, 'low'); - is ($adaptive_slicing->cusp_height(scale 1, 0.2, 0.1, 0.4), 0.4, 'higher'); - is ($adaptive_slicing->cusp_height(scale 1, 0.2, 0.1, 0.65), 0.65, 'highest'); + is ($adaptive_slicing->next_layer_height(scale 1, 0.2, 0.1, 0.15), 0.15, 'low'); + is ($adaptive_slicing->next_layer_height(scale 1, 0.2, 0.1, 0.4), 0.4, 'higher'); + is ($adaptive_slicing->next_layer_height(scale 1, 0.2, 0.1, 0.65), 0.65, 'highest'); }; -subtest 'min cusp_height limited by extruder capabilities' => sub { +subtest 'min layer_height limited by extruder capabilities' => sub { plan tests => 3; - is ($adaptive_slicing->cusp_height(scale 4, 0.01, 0.1, 0.15), 0.1, 'low'); - is ($adaptive_slicing->cusp_height(scale 4, 0.02, 0.2, 0.4), 0.2, 'higher'); - is ($adaptive_slicing->cusp_height(scale 4, 0.01, 0.3, 0.65), 0.3, 'highest'); + is ($adaptive_slicing->next_layer_height(scale 4, 0.01, 0.1, 0.15), 0.1, 'low'); + is ($adaptive_slicing->next_layer_height(scale 4, 0.02, 0.2, 0.4), 0.2, 'higher'); + is ($adaptive_slicing->next_layer_height(scale 4, 0.01, 0.3, 0.65), 0.3, 'highest'); }; -subtest 'correct cusp_height depending on the facet normals' => sub { +subtest 'correct layer_height depending on the facet normals' => sub { plan tests => 3; - ok (_eq($adaptive_slicing->cusp_height(scale 1, 0.1, 0.1, 0.5), 0.5), 'limit'); - ok (_eq($adaptive_slicing->cusp_height(scale 4, 0.1, 0.1, 0.5), 0.1414), '45deg facet, cusp_value: 0.1'); - ok (_eq($adaptive_slicing->cusp_height(scale 4, 0.15, 0.1, 0.5), 0.2121), '45deg facet, cusp_value: 0.15'); + ok (_eq($adaptive_slicing->next_layer_height(scale 1, 0.1, 0.1, 0.5), 0.5), 'limit'); + ok (_eq($adaptive_slicing->next_layer_height(scale 4, 0.2, 0.1, 0.5), 0.1546), '45deg facet, quality_value: 0.2'); + ok (_eq($adaptive_slicing->next_layer_height(scale 4, 0.5, 0.1, 0.5), 0.3352), '45deg facet, quality_value: 0.5'); }; # 2.92893 ist lower slope edge # distance to slope must be higher than min extruder cap. -# slopes cusp height must be greater than the distance to the slope -ok (_eq($adaptive_slicing->cusp_height(scale 2.798, 0.1, 0.1, 0.5), 0.1414), 'reducing cusp_height due to higher slopy facet'); +# slopes layer height must be greater than the distance to the slope +ok (_eq($adaptive_slicing->next_layer_height(scale 2.798, 0.2, 0.1, 0.5), 0.1546), 'reducing layer_height due to higher slopy facet'); -# slopes cusp height must be smaller than the distance to the slope -ok (_eq($adaptive_slicing->cusp_height(scale 2.6289, 0.15, 0.1, 0.5), 0.3), 'reducing cusp_height to z-diff'); +# slopes layer height must be smaller than the distance to the slope +ok (_eq($adaptive_slicing->next_layer_height(scale 2.6289, 0.15, 0.1, 0.5), 0.3), 'reducing layer_height to z-diff'); subtest 'horizontal planes' => sub { plan tests => 3; @@ -122,7 +122,7 @@ $config->set('first_layer_height', 0.42893); # to catch lower slope edge $config->set('nozzle_diameter', [0.5]); $config->set('min_layer_height', [0.1]); $config->set('max_layer_height', [0.5]); -$config->set('cusp_value', [0.19]); +$config->set('adaptive_slicing_quality', [0.19]); # slope height: 7,07107 (2.92893 to 10) subtest 'shrink to match horizontal facets' => sub { @@ -132,7 +132,7 @@ subtest 'shrink to match horizontal facets' => sub { }; # widen current layer to match horizontal facet -$config->set('cusp_value', [0.1]); +$config->set('adaptive_slicing_quality', [0.1]); subtest 'widen to match horizontal facets' => sub { plan skip_all => 'spline smoothing currently prevents exact horizontal facet matching'; diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index f30eef703..adce5737d 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -13,6 +13,15 @@ PrintConfigDef::PrintConfigDef() def->cli = "adaptive-slicing!"; def->default_value = new ConfigOptionBool(false); + def = this->add("adaptive_slicing_quality", coFloat); + def->label = "Adaptive quality"; + def->tooltip = "Controls the quality / printing time tradeoff for adaptive layer generation. 1 -> fastest printing with max layer height, 0 -> highest quality, min layer height"; + def->sidetext = ""; + def->cli = "adaptive_slicing_quality=f"; + def->min = 0; + def->max = 1; + def->default_value = new ConfigOptionFloat(0.15); + def = this->add("adaptive_slicing_z_gradation", coFloat); def->label = "Min layer height gradation"; def->tooltip = "Limit layer heights to a multiple of this value to avoid stepping inaccuracies at the Z-axis. Typical value for a Prusa i3, 1/16 micro-stepping is 0.004mm. Set zero do disable this option."; @@ -133,15 +142,6 @@ PrintConfigDef::PrintConfigDef() def->cli = "cooling!"; def->default_value = new ConfigOptionBool(true); - def = this->add("cusp_value", coFloat); - def->label = "Cusp value"; - def->tooltip = "This value determines the maximum deviaton from the objects original surface caused by the stair-stepping effect (approximation of the surface by discrete layers). Use a value between 0 (highest possible resolution) and [max_layer_height] (lowest possible resolution). Typical values are 0.1 - 0.2."; - def->sidetext = "mm"; - def->cli = "cusp_value=f"; - def->min = 0; - def->max = 1; - def->default_value = new ConfigOptionFloat(0.15); - def = this->add("default_acceleration", coFloat); def->label = "Default"; def->tooltip = "This is the acceleration your printer will be reset to after the role-specific acceleration values are used (perimeter/infill). Set zero to prevent resetting acceleration at all."; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index 3962264d0..d8657b2ea 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -141,7 +141,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig public: ConfigOptionBool adaptive_slicing; ConfigOptionFloat adaptive_slicing_z_gradation; - ConfigOptionFloat cusp_value; + ConfigOptionFloat adaptive_slicing_quality; ConfigOptionBool dont_support_bridges; ConfigOptionFloatOrPercent extrusion_width; ConfigOptionFloatOrPercent first_layer_height; @@ -175,7 +175,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig virtual ConfigOption* optptr(const t_config_option_key &opt_key, bool create = false) { OPT_PTR(adaptive_slicing); OPT_PTR(adaptive_slicing_z_gradation); - OPT_PTR(cusp_value); + OPT_PTR(adaptive_slicing_quality); OPT_PTR(dont_support_bridges); OPT_PTR(extrusion_width); OPT_PTR(first_layer_height); diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 2ea2ba369..9cc60aa98 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -246,7 +246,7 @@ PrintObject::invalidate_state_by_config_options(const std::vector Date: Tue, 28 Mar 2017 09:36:01 +0200 Subject: [PATCH 39/64] fix: forgot to remove AdaptiveSlicing include --- lib/Slic3r.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 5488fd3e4..abffe3c03 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -47,7 +47,6 @@ our $var = sub { decode_path($FindBin::Bin) . "/var/" . $_[0] }; use Moo 1.003001; use Slic3r::XS; # import all symbols (constants etc.) before they get parsed -use Slic3r::AdaptiveSlicing; use Slic3r::Config; use Slic3r::ExPolygon; use Slic3r::ExtrusionLoop; From 323afa36583aee03298c424601b1fe47ee8cdec2 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 28 Mar 2017 09:39:18 +0200 Subject: [PATCH 40/64] fix: deleted remaining line from merge --- lib/Slic3r/GUI/Plater.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 95d333789..9f1563d4b 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1676,7 +1676,6 @@ sub on_model_change { } $self->refresh_canvases; - my $running = $self->pause_background_process; my $invalidated = $self->{print}->reload_model_instances(); if ($Slic3r::GUI::Settings->{_}{background_processing}) { From eacc9cf7e80307b1316901ca6d53d1bedae0e48e Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 29 Mar 2017 08:18:07 +0200 Subject: [PATCH 41/64] Refactoring of the SplineControl gui element --- lib/Slic3r/GUI/Plater/SplineControl.pm | 349 +++++++++++-------------- 1 file changed, 148 insertions(+), 201 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/SplineControl.pm b/lib/Slic3r/GUI/Plater/SplineControl.pm index 236b68903..6de3ba0b3 100644 --- a/lib/Slic3r/GUI/Plater/SplineControl.pm +++ b/lib/Slic3r/GUI/Plater/SplineControl.pm @@ -4,8 +4,7 @@ use warnings; use utf8; use List::Util qw(min max first); -use Slic3r::Geometry qw(X Y scale unscale convex_hull); -use Slic3r::Geometry::Clipper qw(offset JT_ROUND intersection_pl); +use Slic3r::Geometry qw(X Y scale unscale); use Wx qw(:misc :pen :brush :sizer :font :cursor wxTAB_TRAVERSAL); use Wx::Event qw(EVT_MOUSE_EVENTS EVT_PAINT EVT_ERASE_BACKGROUND EVT_SIZE); use base 'Wx::Panel'; @@ -28,9 +27,9 @@ sub new { $self->{user_drawn_background} = $^O ne 'darwin'; + # scale plot data to actual canvas, documentation in set_size_parameters $self->{scaling_factor_x} = 1; $self->{scaling_factor_y} = 1; - $self->{min_layer_height} = 0.1; $self->{max_layer_height} = 0.4; $self->{object_height} = 1.0; @@ -76,113 +75,79 @@ sub repaint { $dc->DrawLabel(sprintf('%.4g', $self->{max_layer_height}), Wx::Rect->new(0, $size[1]/2, $size[0], $size[1]/2), wxALIGN_RIGHT | wxALIGN_BOTTOM); - # draw original layers as lines - my $last_z = 0.0; - my @points = (); -# foreach my $z (@{$self->{original_interpolated_layers}}) { -# my $layer_h = $z - $last_z; -# $dc->SetPen($self->{original_pen}); -# my $pl = $self->point_to_pixel(0, $z); -# my $pr = $self->point_to_pixel($layer_h, $z); -# #$dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); -# push (@points, $pr); -# $last_z = $z; -# } -# -# $dc->DrawSpline(\@points); + # draw original spline as reference if($self->{original_height_spline}) { - $last_z = 0.0; - @points = (); #draw spline - $dc->SetPen($self->{original_pen}); - @points = (); - foreach my $pixel (0..$size[1]) { - my @z = $self->pixel_to_point(Wx::Point->new(0, $pixel)); - my $h = $self->{original_height_spline}->getLayerHeightAt($z[1]); - my $p = $self->point_to_pixel($h, $z[1]); - push (@points, $p); - } - $dc->DrawLines(\@points); + $self->_draw_layer_height_spline($dc, $self->{original_height_spline}, $self->{original_pen}); } - - -# # draw interactive (user modified) layers as lines -# $last_z = 0.0; -# @points = (); -# if($self->{interactive_heights}) { -# foreach my $i (0..@{$self->{interactive_heights}}-1) { -# my $z = $self->{original_layers}[$i]; -# my $layer_h = $self->{interactive_heights}[$i]; -# $dc->SetPen($self->{interactive_pen}); -# my $pl = $self->point_to_pixel(0, $z); -# my $pr = $self->point_to_pixel($layer_h, $z); -# $dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); -# push (@points, $pr); -# } -# -# $dc->DrawSpline(\@points); -# } - + # draw interactive (currently modified by the user) layers as lines and spline if($self->{interactive_height_spline}) { - $last_z = 0.0; - @points = (); # draw layer lines - foreach my $z (@{$self->{interactive_height_spline}->getInterpolatedLayers}) { - my $layer_h = $z - $last_z; - $dc->SetPen($self->{interactive_pen}); - my $pl = $self->point_to_pixel(0, $z); - my $pr = $self->point_to_pixel($layer_h, $z); - $dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); - $last_z = $z; - } - - #draw spline - $dc->SetPen($self->{interactive_pen}); - @points = (); - foreach my $pixel (0..$size[1]) { - my @z = $self->pixel_to_point(Wx::Point->new(0, $pixel)); - my $h = $self->{interactive_height_spline}->getLayerHeightAt($z[1]); - my $p = $self->point_to_pixel($h, $z[1]); - push (@points, $p); - } - $dc->DrawLines(\@points); + my @interpolated_layers = @{$self->{interactive_height_spline}->getInterpolatedLayers}; + $self->_draw_layers_as_lines($dc, $self->{interactive_pen}, \@interpolated_layers); + + #draw spline + $self->_draw_layer_height_spline($dc, $self->{interactive_height_spline}, $self->{interactive_pen}); } - # draw resulting layers as lines - $last_z = 0.0; - @points = (); unless($self->{interactive_heights}) { - foreach my $z (@{$self->{interpolated_layers}}) { - my $layer_h = $z - $last_z; - $dc->SetPen($self->{resulting_pen}); - my $pl = $self->point_to_pixel(0, $z); - my $pr = $self->point_to_pixel($layer_h, $z); - $dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); - push (@points, $pr); - $last_z = $z; - } + $self->_draw_layers_as_lines($dc, $self->{resulting_pen}, $self->{interpolated_layers}); } -# $dc->DrawSpline(\@points); - - # Draw current BSpline - $dc->SetPen($self->{line_pen}); - @points = (); - foreach my $pixel (0..$size[1]) { - my @z = $self->pixel_to_point(Wx::Point->new(0, $pixel)); - #print "z: " . $z . "\n"; - my $h = $self->{object}->layer_height_spline->getLayerHeightAt($z[1]); - my $p = $self->point_to_pixel($h, $z[1]); - push (@points, $p); - #print "pixel: " . $pixel . "\n"; - } - $dc->DrawLines(\@points); + # Always draw current BSpline, gives a reference during a modification + $self->_draw_layer_height_spline($dc, $self->{object}->layer_height_spline, $self->{line_pen}); $event->Skip; } +# Set basic parameters for this control. +# min/max_layer_height are required to define the x-range, object_height is used to scale the y-range. +# Must be called if object selection changes. +sub set_size_parameters { + my ($self, $min_layer_height, $max_layer_height, $object_height) = @_; + + $self->{min_layer_height} = $min_layer_height; + $self->{max_layer_height} = $max_layer_height; + $self->{object_height} = $object_height; + + $self->_update_canvas_size; + $self->Refresh; +} + +# Layers have been modified externally, re-initialize this control with new values +sub update { + my $self = shift; + + if($self->{object}->layer_height_spline->layersUpdated || !$self->{heights}) { + $self->{original_height_spline} = $self->{object}->layer_height_spline->clone; # make a copy to display the unmodified original spline + $self->{original_layers} = $self->{object}->layer_height_spline->getOriginalLayers; + $self->{interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; # Initialize to current values + + # initialize height vector + $self->{heights} = (); + $self->{interactive_heights} = (); + foreach my $z (@{$self->{original_layers}}) { + push (@{$self->{heights}}, $self->{object}->layer_height_spline->getLayerHeightAt($z)); + } + } + $self->Refresh; +} + +# Callback to notify parent element if layers have changed and reslicing should be triggered +sub on_layer_update { + my ($self, $cb) = @_; + $self->{on_layer_update} = $cb; +} + +# Callback to tell parent element at which z-position the mouse currently hovers to update indicator in 3D-view +sub on_z_indicator { + my ($self, $cb) = @_; + $self->{on_z_indicator} = $cb; +} + + sub mouse_event { my ($self, $event) = @_; @@ -190,7 +155,7 @@ sub mouse_event { my @obj_pos = $self->pixel_to_point($pos); if ($event->ButtonDown) { - if ($event->LeftDown) { + if ($event->LeftDown) { # start dragging $self->{left_drag_start_pos} = $pos; $self->{interactive_height_spline} = $self->{object}->layer_height_spline->clone; @@ -202,51 +167,27 @@ sub mouse_event { } } elsif ($event->LeftUp) { if($self->{left_drag_start_pos}) { - if($self->{interactive_heights}) { - $self->{heights} = $self->{interactive_heights}; - $self->{interactive_heights} = (); - # update spline database - unless($self->{object}->layer_height_spline->updateLayerHeights($self->{heights})) { - die "Unable to update interpolated layers!\n"; - } - $self->{interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; - } - $self->Refresh; - $self->{object}->layer_height_spline->suppressUpdate; - $self->{on_layer_update}->(@{$self->{interpolated_layers}}); - $self->{interactive_height_spline} = undef; - } + $self->_modification_done; + } $self->{left_drag_start_pos} = undef; } elsif ($event->RightUp) { if($self->{right_drag_start_pos}) { - if($self->{interactive_heights}) { - $self->{heights} = $self->{interactive_heights}; - $self->{interactive_heights} = (); - # update spline database - unless($self->{object}->layer_height_spline->updateLayerHeights($self->{heights})) { - die "Unable to update interpolated layers!\n"; - } - $self->{interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; - } - $self->Refresh; - $self->{object}->layer_height_spline->suppressUpdate; - $self->{on_layer_update}->(@{$self->{interpolated_layers}}); - $self->{interactive_height_spline} = undef; + $self->_modification_done; } $self->{right_drag_start_pos} = undef; } elsif ($event->Dragging) { if($self->{left_drag_start_pos}) { - my @start_pos = $self->pixel_to_point($self->{left_drag_start_pos}); - my $range = abs($start_pos[1] - $obj_pos[1]); + my @start_pos = $self->pixel_to_point($self->{left_drag_start_pos}); + my $range = abs($start_pos[1] - $obj_pos[1]); - # compute updated interactive layer heights - $self->_interactive_quadratic_curve($start_pos[1], $obj_pos[0], $range); + # compute updated interactive layer heights + $self->_interactive_quadratic_curve($start_pos[1], $obj_pos[0], $range); - unless($self->{interactive_height_spline}->updateLayerHeights($self->{interactive_heights})) { - die "Unable to update interactive interpolated layers!\n"; - } - $self->Refresh; + unless($self->{interactive_height_spline}->updateLayerHeights($self->{interactive_heights})) { + die "Unable to update interactive interpolated layers!\n"; + } + $self->Refresh; } elsif($self->{right_drag_start_pos}) { my @start_pos = $self->pixel_to_point($self->{right_drag_start_pos}); my $range = $obj_pos[1] - $start_pos[1]; @@ -269,52 +210,23 @@ sub mouse_event { } } -# Set basic parameters for this control. -# min/max_layer_height are required to define the x-range, object_height is used to scale the y-range. -# Must be called if object selection changes. -sub set_size_parameters { - my ($self, $min_layer_height, $max_layer_height, $object_height) = @_; - - $self->{min_layer_height} = $min_layer_height; - $self->{max_layer_height} = $max_layer_height; - $self->{object_height} = $object_height; +# Push modified heights to the spline object and update after user modification +sub _modification_done { + my $self = shift; - $self->_update_canvas_size; - $self->Refresh; -} - -# Layers have been modified externally, re-initialize this control with new values -sub update { - my $self = shift; - - if($self->{object}->layer_height_spline->layersUpdated) { - $self->{original_height_spline} = $self->{object}->layer_height_spline->clone; - $self->{original_layers} = $self->{object}->layer_height_spline->getOriginalLayers; - $self->{original_interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; - $self->{interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; # Initialize to current values - - # initialize height vector - $self->{heights} = (); - $self->{interactive_heights} = (); - my $last_z = 0; - foreach my $z (@{$self->{original_layers}}) { - push (@{$self->{heights}}, $z - $last_z); - $last_z = $z; - } + if($self->{interactive_heights}) { + $self->{heights} = $self->{interactive_heights}; + $self->{interactive_heights} = (); + # update spline database + unless($self->{object}->layer_height_spline->updateLayerHeights($self->{heights})) { + die "Unable to update interpolated layers!\n"; + } + $self->{interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; } $self->Refresh; -} - -# Callback to notify parent element if layers have changed and reslicing should be triggered -sub on_layer_update { - my ($self, $cb) = @_; - $self->{on_layer_update} = $cb; -} - -# Callback to tell parent element at which z-position the mouse currently hovers to update indicator in 3D-view -sub on_z_indicator { - my ($self, $cb) = @_; - $self->{on_z_indicator} = $cb; + $self->{object}->layer_height_spline->suppressUpdate; + $self->{on_layer_update}->(@{$self->{interpolated_layers}}); + $self->{interactive_height_spline} = undef; } # Internal function to cache scaling factors @@ -333,25 +245,27 @@ sub _update_canvas_size { $self->{scaling_factor_y} = $size[1]/$self->{object_height}; } +# calculate new set of layers with quadaratic modifier for interactive display sub _interactive_quadratic_curve { - my ($self, $mod_z, $target_layer_height, $range) = @_; - - $self->{interactive_heights} = (); # reset interactive curve - - # iterate over original points provided by spline - my $last_z = 0; - foreach my $i (0..@{$self->{heights}}-1 ) { - my $z = $self->{original_layers}[$i]; - my $layer_h = $self->{heights}[$i]; - my $quadratic_factor = $self->_quadratic_factor($mod_z, $range, $z); - my $diff = $target_layer_height - $layer_h; - $layer_h += $diff * $quadratic_factor; - push (@{$self->{interactive_heights}}, $layer_h); - } + my ($self, $mod_z, $target_layer_height, $range) = @_; + + $self->{interactive_heights} = (); # reset interactive curve + + # iterate over original points provided by spline + my $last_z = 0; + foreach my $i (0..@{$self->{heights}}-1 ) { + my $z = $self->{original_layers}[$i]; + my $layer_h = $self->{heights}[$i]; + my $quadratic_factor = $self->_quadratic_factor($mod_z, $range, $z); + my $diff = $target_layer_height - $layer_h; + $layer_h += $diff * $quadratic_factor; + push (@{$self->{interactive_heights}}, $layer_h); + } } +# calculate new set of layers with linear modifier for interactive display sub _interactive_linear_curve { - my ($self, $mod_z, $target_layer_height, $range) = @_; + my ($self, $mod_z, $target_layer_height, $range) = @_; $self->{interactive_heights} = (); # reset interactive curve my $from; @@ -376,24 +290,57 @@ sub _interactive_linear_curve { } sub _quadratic_factor { - my ($self, $fixpoint, $range, $value) = @_; - - # avoid division by zero - $range = 0.00001 if $range <= 0; - - my $dist = abs($fixpoint - $value); - my $x = $dist/$range; # normalize - my $result = 1-($x*$x); - - return max(0, $result); + my ($self, $fixpoint, $range, $value) = @_; + + # avoid division by zero + $range = 0.00001 if $range <= 0; + + my $dist = abs($fixpoint - $value); + my $x = $dist/$range; # normalize + my $result = 1-($x*$x); + + return max(0, $result); +} + +# Draw a set of layers as lines +sub _draw_layers_as_lines { + my ($self, $dc, $pen, $layers) = @_; + + $dc->SetPen($pen); + my $last_z = 0.0; + foreach my $z (@$layers) { + my $layer_h = $z - $last_z; + my $pl = $self->point_to_pixel(0, $z); + my $pr = $self->point_to_pixel($layer_h, $z); + $dc->DrawLine($pl->x, $pl->y, $pr->x, $pr->y); + $last_z = $z; + } +} + +# Draw the resulting spline from a LayerHeightSpline object over the full canvas height +sub _draw_layer_height_spline { + my ($self, $dc, $layer_height_spline, $pen) = @_; + + my $size = $self->GetSize; + my @size = ($size->GetWidth, $size->GetHeight); + + $dc->SetPen($pen); + my @points = (); + foreach my $pixel (0..$size[1]) { + my @z = $self->pixel_to_point(Wx::Point->new(0, $pixel)); + my $h = $layer_height_spline->getLayerHeightAt($z[1]); + my $p = $self->point_to_pixel($h, $z[1]); + push (@points, $p); + } + $dc->DrawLines(\@points); } # Takes a 2-tupel [layer_height (x), height(y)] and converts it # into a Wx::Point in scaled canvas coordinates sub point_to_pixel { - my ($self, @point) = @_; - - my $size = $self->GetSize; + my ($self, @point) = @_; + + my $size = $self->GetSize; my @size = ($size->GetWidth, $size->GetHeight); my $x = ($point[0] - $self->{min_layer_height})*$self->{scaling_factor_x}; From d415ec108d5f32f2f431b9e96b40ee53d2968692 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 30 Mar 2017 16:05:26 +0200 Subject: [PATCH 42/64] Use config system correctly --- lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm | 5 ++--- xs/src/libslic3r/LayerHeightSpline.cpp | 2 -- xs/src/libslic3r/LayerHeightSpline.hpp | 6 ------ xs/src/libslic3r/PrintObject.cpp | 5 ----- xs/xsp/LayerHeightSpline.xsp | 3 --- 5 files changed, 2 insertions(+), 19 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm index 748080a9b..7c3a28e67 100644 --- a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm @@ -99,7 +99,6 @@ sub new { $self->{splineControl}->on_z_indicator(sub { my ($z) = @_; - #$self->{preview3D}->canvas->cutting_plane_z($z); $self->{preview3D}->canvas->SetCuttingPlane(Z, $z, []); $self->{preview3D}->canvas->Render; }); @@ -121,8 +120,8 @@ sub new { $self->{plater}->pause_background_process; my $quality_value = $quality_slider->GetValue/100; $value_label->SetLabel(sprintf '%.2f', $quality_value); - my $success = $object->config->set('adaptive_slicing_quality', $quality_value); - $object->layer_height_spline->setCuspValue($quality_value); + $self->{model_object}->config->set('adaptive_slicing_quality', $quality_value); + # trigger re-slicing $self->{plater}->stop_background_process; $self->{object}->invalidate_step(STEP_SLICE); diff --git a/xs/src/libslic3r/LayerHeightSpline.cpp b/xs/src/libslic3r/LayerHeightSpline.cpp index 596a40932..505d6d8db 100644 --- a/xs/src/libslic3r/LayerHeightSpline.cpp +++ b/xs/src/libslic3r/LayerHeightSpline.cpp @@ -18,7 +18,6 @@ LayerHeightSpline::LayerHeightSpline(coordf_t object_height) this->_update_required = true; this->_layers_updated = false; this->_layer_heights_updated = false; - this->_cusp_value = -1; } LayerHeightSpline::LayerHeightSpline(const LayerHeightSpline &other) @@ -32,7 +31,6 @@ LayerHeightSpline::LayerHeightSpline(const LayerHeightSpline &other) this->_update_required = other._update_required; this->_layers_updated = other._layers_updated; this->_layer_heights_updated = other._layer_heights_updated; - this->_cusp_value = other._cusp_value; if(this->_is_valid) { this->_updateBSpline(); } diff --git a/xs/src/libslic3r/LayerHeightSpline.hpp b/xs/src/libslic3r/LayerHeightSpline.hpp index 3fe9c09a4..9a72a748b 100644 --- a/xs/src/libslic3r/LayerHeightSpline.hpp +++ b/xs/src/libslic3r/LayerHeightSpline.hpp @@ -26,15 +26,9 @@ class LayerHeightSpline std::vector getInterpolatedLayers() const; const coordf_t getLayerHeightAt(coordf_t height); - void setCuspValue(coordf_t cusp_value) {this->_cusp_value = cusp_value;}; - coordf_t getCuspValue() {return this->_cusp_value;}; - private: bool _updateBSpline(); - - coordf_t _cusp_value; - coordf_t _object_height; bool _is_valid; bool _update_required; // this should be always true except if we want to generate new layers from this spline diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 7f9bf1764..b1df227a0 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -617,11 +617,6 @@ std::vector PrintObject::generate_object_layers(coordf_t first_layer_h if (this->config.adaptive_slicing.value) { height = 999; - // FIXME: this should de done directly via config at the dialog... - if(this->layer_height_spline.getCuspValue() >= 0) { - adaptive_quality = this->layer_height_spline.getCuspValue(); //FIXME: rename variable (cusp) - this->config.adaptive_slicing_quality.value = adaptive_quality; - } // determine next layer height height = as.next_layer_height(print_z, adaptive_quality, min_layer_height, max_layer_height); diff --git a/xs/xsp/LayerHeightSpline.xsp b/xs/xsp/LayerHeightSpline.xsp index a7b33608f..750c47b5e 100644 --- a/xs/xsp/LayerHeightSpline.xsp +++ b/xs/xsp/LayerHeightSpline.xsp @@ -24,7 +24,4 @@ std::vector getOriginalLayers(); std::vector getInterpolatedLayers(); coordf_t getLayerHeightAt(coordf_t height); - - void setCuspValue(coordf_t cusp_value); - coordf_t getCuspValue(); }; From 8ec0275967b4cd53226fda11f5adb42ad3e02dee Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 4 Apr 2017 16:24:18 +0200 Subject: [PATCH 43/64] fix for segfault caused by using the wrong LayerHeightSpline object --- lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm | 19 ++++++++++--------- xs/src/libslic3r/PrintObject.cpp | 7 +++++-- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm index e091d46fc..c17e1afee 100644 --- a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm @@ -18,7 +18,7 @@ sub new { my $model_object = $self->{model_object} = $params{model_object}; my $obj_idx = $self->{obj_idx} = $params{obj_idx}; my $plater = $self->{plater} = $parent; - my $object = $self->{object} = $self->{plater}->{print}->get_object($self->{obj_idx}); + my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); $self->{update_spline_control} = 0; @@ -93,9 +93,6 @@ sub new { $self->{splineControl}->on_layer_update(sub { # trigger re-slicing $self->_trigger_slicing; - #$self->{plater}->stop_background_process; - #$self->{object}->invalidate_step(STEP_SLICE); - #$self->{plater}->start_background_process; }); $self->{splineControl}->on_z_indicator(sub { @@ -105,7 +102,7 @@ sub new { }); # init quality slider - if($object->config->adaptive_slicing) { + if($object->config->get('adaptive_slicing')) { my $quality_value = $object->config->get('adaptive_slicing_quality'); $value_label->SetLabel(sprintf '%.2f', $quality_value); $quality_slider->SetRange(0, 100); @@ -131,8 +128,9 @@ sub new { sub _trigger_slicing { my ($self) = @_; + my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); + $self->{plater}->pause_background_process; $self->{plater}->stop_background_process; - $self->{object}->invalidate_step(STEP_SLICE); if (!$Slic3r::GUI::Settings->{_}{background_processing}) { $self->{plater}->statusbar->SetCancelCallback(sub { $self->{plater}->stop_background_process; @@ -143,6 +141,8 @@ sub _trigger_slicing { $self->{plater}->on_model_change; $self->{plater}->start_background_process; }else{ + $self->{plater}->{print}->reload_object($self->{obj_idx}); + $self->{plater}->on_model_change; $self->{plater}->schedule_background_process; } } @@ -151,10 +151,11 @@ sub reload_preview { my ($self) = @_; $self->{splineControl}->update; $self->{preview3D}->reload_print; - if($self->{object}->layer_count-1 > 0) { + my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); + if($object->layer_count-1 > 0) { # causes segfault... - #my $top_layer = $self->{object}->get_layer($self->{object}->layer_count-1); - #$self->{preview3D}->set_z($top_layer->print_z); + my $top_layer = $object->get_layer($object->layer_count-1); + $self->{preview3D}->set_z($top_layer->print_z); } } diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index da7cd2227..e1d144f96 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -317,6 +317,7 @@ PrintObject::invalidate_all_steps() for (std::set::const_iterator step = steps.begin(); step != steps.end(); ++step) { if (this->invalidate_step(*step)) invalidated = true; } + return invalidated; } @@ -671,14 +672,16 @@ std::vector PrintObject::generate_object_layers(coordf_t first_layer_h } } - // Store layer vector for interactive manipulation and push back to model + // Store layer vector for interactive manipulation this->layer_height_spline.setLayers(result); - this->_model_object->layer_height_spline = this->layer_height_spline; if (this->config.adaptive_slicing.value) { // smoothing after adaptive algorithm result = this->layer_height_spline.getInterpolatedLayers(); } } + // push modified spline object back to model + this->_model_object->layer_height_spline = this->layer_height_spline; + // apply z-gradation (this is redundant for static layer height...) coordf_t gradation = 1 / this->_print->config.z_steps_per_mm * 4; if(this->_print->config.z_steps_per_mm > 0) { From f24ca220d5dd57b4d7cfe444281511bd5c592270 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 4 Apr 2017 16:25:00 +0200 Subject: [PATCH 44/64] fix potential endless loop when evaluating the spline --- xs/src/libslic3r/LayerHeightSpline.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xs/src/libslic3r/LayerHeightSpline.cpp b/xs/src/libslic3r/LayerHeightSpline.cpp index f2997a1d9..1e8da6cfe 100644 --- a/xs/src/libslic3r/LayerHeightSpline.cpp +++ b/xs/src/libslic3r/LayerHeightSpline.cpp @@ -158,15 +158,18 @@ std::vector LayerHeightSpline::getInterpolatedLayers() const coordf_t z = this->_original_layers[0]; coordf_t h; coordf_t h_diff = 0; + coordf_t last_h_diff = 0; coordf_t eps = 0.0001; while(z <= this->_object_height) { h = 0; + h_diff = 0; // find intersection between layer height and spline do { + last_h_diff = h_diff; h += h_diff/2; h = this->_layer_height_spline->evaluate(z+h); h_diff = this->_layer_height_spline->evaluate(z+h) - h; - } while(std::abs(h_diff) > eps); + } while(std::abs(h_diff) > eps && std::abs(h_diff - last_h_diff) > eps); z += h; layers.push_back(z); } From 8f2ff28cd90592b8d1a672e32513e946055fd904 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 5 Apr 2017 11:06:17 +0200 Subject: [PATCH 45/64] replace quality option with percent based scale and use option sliders --- lib/Slic3r/GUI/OptionsGroup/Field.pm | 11 +++ lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm | 84 +++++++++------------ lib/Slic3r/GUI/PresetEditor.pm | 1 + t/adaptive_slicing.t | 24 +++--- xs/src/libslic3r/PrintConfig.cpp | 14 ++-- xs/src/libslic3r/PrintConfig.hpp | 2 +- xs/src/libslic3r/SlicingAdaptive.cpp | 4 +- 7 files changed, 71 insertions(+), 69 deletions(-) diff --git a/lib/Slic3r/GUI/OptionsGroup/Field.pm b/lib/Slic3r/GUI/OptionsGroup/Field.pm index 920d4cec1..e652c7550 100644 --- a/lib/Slic3r/GUI/OptionsGroup/Field.pm +++ b/lib/Slic3r/GUI/OptionsGroup/Field.pm @@ -576,6 +576,17 @@ sub get_value { return $self->slider->GetValue/$self->scale; } +# Update internal scaling +sub set_scale { + my ($self, $scale) = @_; + $self->disable_change_event(1); + my $current_value = $self->get_value; + $self->slider->SetRange($self->slider->GetMin / $self->scale * $scale, $self->slider->GetMax / $self->scale * $scale); + $self->scale($scale); + $self->set_value($current_value); + $self->disable_change_event(0); +} + sub _update_textctrl { my ($self) = @_; diff --git a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm index c17e1afee..e81204a7b 100644 --- a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm @@ -34,37 +34,47 @@ sub new { $self->{splineControl} = Slic3r::GUI::Plater::SplineControl->new($self, Wx::Size->new(150, 200), $model_object); - my $quality_slider = $self->{quality_slider} = Wx::Slider->new( - $self, -1, - 0, # default - 0, # min - # we set max to a bogus non-zero value because the MSW implementation of wxSlider - # will skip drawing the slider if max <= min: - 1, # max - wxDefaultPosition, - wxDefaultSize, - wxHORIZONTAL, + my $optgroup; + $optgroup = $self->{optgroup} = Slic3r::GUI::OptionsGroup->new( + parent => $self, + title => 'Adaptive quality %', + 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->{adaptive_quality} != $optgroup->get_value($opt_id)) { + $self->{adaptive_quality} = $optgroup->get_value($opt_id); + $self->{model_object}->config->set('adaptive_slicing_quality', $optgroup->get_value($opt_id)); + # trigger re-slicing + $self->_trigger_slicing; + } + }, + label_width => 0, ); - my $quality_label = $self->{quality_label} = Wx::StaticText->new($self, -1, " <-Quality", wxDefaultPosition, - [-1,-1], wxALIGN_LEFT); - my $value_label = $self->{value_label} = Wx::StaticText->new($self, -1, "", wxDefaultPosition, - [50,-1], wxALIGN_CENTRE_HORIZONTAL | wxST_NO_AUTORESIZE); - my $speed_label = $self->{speed_label} = Wx::StaticText->new($self, -1, "Speed->", wxDefaultPosition, - [-1,-1], wxST_NO_AUTORESIZE | wxALIGN_RIGHT); - $quality_label->SetFont($Slic3r::GUI::small_font); - $value_label->SetFont($Slic3r::GUI::small_font); - $speed_label->SetFont($Slic3r::GUI::small_font); + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'adaptive_slicing_quality', + type => 'slider', + label => '', + default => $object->config->get('adaptive_slicing_quality'), + min => 0, + max => 100, + full_width => 1, + )); + $optgroup->get_field('adaptive_slicing_quality')->set_scale(1); + $self->{adaptive_quality} = $object->config->get('adaptive_slicing_quality'); + # init quality slider + if(!$object->config->get('adaptive_slicing')) { + # disable slider + $optgroup->get_field('adaptive_slicing_quality')->disable; + } - my $quality_label_sizer = Wx::BoxSizer->new(wxHORIZONTAL); - $quality_label_sizer->Add($quality_label, 1, wxEXPAND | wxALL, 0); - $quality_label_sizer->Add($value_label, 1, wxEXPAND | wxALL, 0); - $quality_label_sizer->Add($speed_label, 1, wxEXPAND | wxALL, 0); my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); $right_sizer->Add($self->{splineControl}, 1, wxEXPAND | wxALL, 0); - $right_sizer->Add($quality_slider, 0, wxEXPAND | wxALL, 0); - $right_sizer->Add($quality_label_sizer, 0, wxEXPAND | wxALL, 0); + $right_sizer->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 0); $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); @@ -101,28 +111,6 @@ sub new { $self->{preview3D}->canvas->Render; }); - # init quality slider - if($object->config->get('adaptive_slicing')) { - my $quality_value = $object->config->get('adaptive_slicing_quality'); - $value_label->SetLabel(sprintf '%.2f', $quality_value); - $quality_slider->SetRange(0, 100); - $quality_slider->SetValue($quality_value*100); - }else{ - # disable slider - $value_label->SetLabel(""); - $quality_label->Enable(0); - $quality_slider->Enable(0); - } - - EVT_SLIDER($self, $quality_slider, sub { - my $quality_value = $quality_slider->GetValue/100; - $value_label->SetLabel(sprintf '%.2f', $quality_value); - $self->{model_object}->config->set('adaptive_slicing_quality', $quality_value); - - # trigger re-slicing - $self->_trigger_slicing; - }); - return $self; } @@ -141,7 +129,7 @@ sub _trigger_slicing { $self->{plater}->on_model_change; $self->{plater}->start_background_process; }else{ - $self->{plater}->{print}->reload_object($self->{obj_idx}); + $self->{plater}->{print}->reload_object($self->{obj_idx}); $self->{plater}->on_model_change; $self->{plater}->schedule_background_process; } diff --git a/lib/Slic3r/GUI/PresetEditor.pm b/lib/Slic3r/GUI/PresetEditor.pm index 85ba51c61..f6c8b6c48 100644 --- a/lib/Slic3r/GUI/PresetEditor.pm +++ b/lib/Slic3r/GUI/PresetEditor.pm @@ -484,6 +484,7 @@ sub build { $optgroup->append_single_option_line('first_layer_height'); $optgroup->append_single_option_line('adaptive_slicing'); $optgroup->append_single_option_line('adaptive_slicing_quality'); + $optgroup->get_field('adaptive_slicing_quality')->set_scale(1); $optgroup->append_single_option_line('match_horizontal_surfaces'); } { diff --git a/t/adaptive_slicing.t b/t/adaptive_slicing.t index 5928b5a3b..1406e4956 100644 --- a/t/adaptive_slicing.t +++ b/t/adaptive_slicing.t @@ -71,35 +71,35 @@ $adaptive_slicing->prepare(20); subtest 'max layer_height limited by extruder capabilities' => sub { plan tests => 3; - ok (_eq($adaptive_slicing->next_layer_height(1, 0.5, 0.1, 0.15), 0.15), 'low'); - ok (_eq($adaptive_slicing->next_layer_height(1, 0.2, 0.1, 0.4), 0.4), 'higher'); - ok (_eq($adaptive_slicing->next_layer_height(1, 0.2, 0.1, 0.65), 0.65), 'highest'); + ok (_eq($adaptive_slicing->next_layer_height(1, 50, 0.1, 0.15), 0.15), 'low'); + ok (_eq($adaptive_slicing->next_layer_height(1, 80, 0.1, 0.4), 0.4), 'higher'); + ok (_eq($adaptive_slicing->next_layer_height(1, 80, 0.1, 0.65), 0.65), 'highest'); }; subtest 'min layer_height limited by extruder capabilities' => sub { plan tests => 3; - ok (_eq($adaptive_slicing->next_layer_height(4, 0.01, 0.1, 0.15), 0.1), 'low'); - ok (_eq($adaptive_slicing->next_layer_height(4, 0.02, 0.2, 0.4), 0.2), 'higher'); - ok (_eq($adaptive_slicing->next_layer_height(4, 0.01, 0.3, 0.65), 0.3), 'highest'); + ok (_eq($adaptive_slicing->next_layer_height(4, 99, 0.1, 0.15), 0.1), 'low'); + ok (_eq($adaptive_slicing->next_layer_height(4, 98, 0.2, 0.4), 0.2), 'higher'); + ok (_eq($adaptive_slicing->next_layer_height(4, 99, 0.3, 0.65), 0.3), 'highest'); }; subtest 'correct layer_height depending on the facet normals' => sub { plan tests => 3; - ok (_eq($adaptive_slicing->next_layer_height(1, 0.1, 0.1, 0.5), 0.5), 'limit'); - ok (_eq($adaptive_slicing->next_layer_height(4, 0.2, 0.1, 0.5), 0.1546), '45deg facet, quality_value: 0.2'); - ok (_eq($adaptive_slicing->next_layer_height(4, 0.5, 0.1, 0.5), 0.3352), '45deg facet, quality_value: 0.5'); + ok (_eq($adaptive_slicing->next_layer_height(1, 90, 0.1, 0.5), 0.5), 'limit'); + ok (_eq($adaptive_slicing->next_layer_height(4, 80, 0.1, 0.5), 0.1546), '45deg facet, quality_value: 0.2'); + ok (_eq($adaptive_slicing->next_layer_height(4, 50, 0.1, 0.5), 0.3352), '45deg facet, quality_value: 0.5'); }; # 2.92893 ist lower slope edge # distance to slope must be higher than min extruder cap. # slopes layer height must be greater than the distance to the slope -ok (_eq($adaptive_slicing->next_layer_height(2.798, 0.2, 0.1, 0.5), 0.1546), 'reducing layer_height due to higher slopy facet'); +ok (_eq($adaptive_slicing->next_layer_height(2.798, 80, 0.1, 0.5), 0.1546), 'reducing layer_height due to higher slopy facet'); # slopes layer height must be smaller than the distance to the slope -ok (_eq($adaptive_slicing->next_layer_height(2.6289, 0.15, 0.1, 0.5), 0.3), 'reducing layer_height to z-diff'); +ok (_eq($adaptive_slicing->next_layer_height(2.6289, 85, 0.1, 0.5), 0.3), 'reducing layer_height to z-diff'); subtest 'horizontal planes' => sub { plan tests => 3; @@ -118,7 +118,7 @@ $config->set('first_layer_height', 0.42893); # to catch lower slope edge $config->set('nozzle_diameter', [0.5]); $config->set('min_layer_height', [0.1]); $config->set('max_layer_height', [0.5]); -$config->set('adaptive_slicing_quality', [0.19]); +$config->set('adaptive_slicing_quality', [81]); # slope height: 7,07107 (2.92893 to 10) subtest 'shrink to match horizontal facets' => sub { diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index ea5c82359..a5102ae2e 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -28,15 +28,17 @@ PrintConfigDef::PrintConfigDef() def->tooltip = "Automatically determine layer heights by the objects topology instead of using the static value."; def->cli = "adaptive-slicing!"; def->default_value = new ConfigOptionBool(false); - - def = this->add("adaptive_slicing_quality", coFloat); + + def = this->add("adaptive_slicing_quality", coPercent); def->label = "Adaptive quality"; - def->tooltip = "Controls the quality / printing time tradeoff for adaptive layer generation. 1 -> fastest printing with max layer height, 0 -> highest quality, min layer height"; - def->sidetext = ""; + def->tooltip = "Controls the quality / printing time tradeoff for adaptive layer generation. 0 -> fastest printing with max layer height, 100 -> highest quality, min layer height"; + def->sidetext = "%"; def->cli = "adaptive_slicing_quality=f"; def->min = 0; - def->max = 1; - def->default_value = new ConfigOptionFloat(0.15); + def->max = 100; + def->gui_type = "slider"; + def->width = 200; + def->default_value = new ConfigOptionPercent(75); def = this->add("avoid_crossing_perimeters", coBool); def->label = "Avoid crossing perimeters"; diff --git a/xs/src/libslic3r/PrintConfig.hpp b/xs/src/libslic3r/PrintConfig.hpp index e241d3d34..c6803c3cd 100644 --- a/xs/src/libslic3r/PrintConfig.hpp +++ b/xs/src/libslic3r/PrintConfig.hpp @@ -144,7 +144,7 @@ class PrintObjectConfig : public virtual StaticPrintConfig { public: ConfigOptionBool adaptive_slicing; - ConfigOptionFloat adaptive_slicing_quality; + ConfigOptionPercent adaptive_slicing_quality; ConfigOptionBool dont_support_bridges; ConfigOptionFloatOrPercent extrusion_width; ConfigOptionFloatOrPercent first_layer_height; diff --git a/xs/src/libslic3r/SlicingAdaptive.cpp b/xs/src/libslic3r/SlicingAdaptive.cpp index d9e3f2f2f..d0ceae95d 100644 --- a/xs/src/libslic3r/SlicingAdaptive.cpp +++ b/xs/src/libslic3r/SlicingAdaptive.cpp @@ -69,9 +69,9 @@ float SlicingAdaptive::next_layer_height(coordf_t z, coordf_t quality_factor, co float height = max_layer_height; // factor must be between 0-1, 0 is highest quality, 1 highest print speed. - // currently disabled but planned for the future + // factor must be between 0-1, 0 is highest quality, 1 highest print speed. // Invert the slider scale (100% should represent a very high quality for the user) - //float quality_factor = std::max(0.f, std::min(1.f, 1 - m_slicing_params.adaptive_slicing_quality/100.f)); + quality_factor = std::max(0.f, std::min(1.f, 1 - quality_factor/100.f)); float delta_min = SURFACE_CONST * min_layer_height; float delta_max = SURFACE_CONST * max_layer_height + 0.5 * max_layer_height; From 95d33df2f093a18445418fe82d2a38e44d3fc257 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 5 Apr 2017 18:53:49 +0200 Subject: [PATCH 46/64] moved interactive layer control to settings dialog --- lib/Slic3r/GUI.pm | 1 - lib/Slic3r/GUI/Plater.pm | 38 ++--- lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm | 150 ----------------- lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm | 158 +++++++++++++++++- lib/Slic3r/GUI/Plater/SplineControl.pm | 50 +++--- 5 files changed, 196 insertions(+), 201 deletions(-) delete mode 100644 lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 6df7343c2..a6b946f9e 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -25,7 +25,6 @@ use Slic3r::GUI::Plater::3D; use Slic3r::GUI::Plater::3DPreview; use Slic3r::GUI::Plater::ObjectPartsPanel; use Slic3r::GUI::Plater::ObjectCutDialog; -use Slic3r::GUI::Plater::ObjectLayersDialog; use Slic3r::GUI::Plater::ObjectSettingsDialog; use Slic3r::GUI::Plater::LambdaObjectDialog; use Slic3r::GUI::Plater::OverrideSettingsPanel; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index ef1409938..561634cd8 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -172,7 +172,7 @@ sub new { $self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new($Slic3r::var->("package.png"), wxBITMAP_TYPE_PNG), ''); $self->{htoolbar}->AddSeparator; $self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), ''); - $self->{htoolbar}->AddTool(TB_LAYERS, "Layer heights…", Wx::Bitmap->new($Slic3r::var->("layers.png"), wxBITMAP_TYPE_PNG), ''); + $self->{htoolbar}->AddTool(TB_LAYERS, "Layer heights…", Wx::Bitmap->new($Slic3r::var->("variable_layer_height.png"), wxBITMAP_TYPE_PNG), ''); } else { my %tbar_buttons = ( add => "Add…", @@ -1386,7 +1386,7 @@ sub async_apply_config { # reset preview canvases (invalidated contents will be hidden) $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; $self->{preview3D}->reload_print if $self->{preview3D}; - $self->{ObjectLayersDialog}->reload_preview if $self->{ObjectLayersDialog}; + $self->{AdaptiveLayersDialog}->reload_preview if $self->{AdaptiveLayersDialog}; if ($invalidated) { if (!$Slic3r::GUI::Settings->{_}{background_processing}) { @@ -1463,7 +1463,7 @@ sub stop_background_process { $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; $self->{preview3D}->reload_print if $self->{preview3D}; - $self->{ObjectLayersDialog}->reload_preview if $self->{ObjectLayersDialog}; + $self->{AdaptiveLayersDialog}->reload_preview if $self->{AdaptiveLayersDialog}; if ($self->{process_thread}) { Slic3r::debugf "Killing background process.\n"; @@ -1610,7 +1610,7 @@ sub on_process_completed { return if $error; $self->{toolpaths2D}->reload_print if $self->{toolpaths2D}; $self->{preview3D}->reload_print if $self->{preview3D}; - $self->{ObjectLayersDialog}->reload_preview if $self->{ObjectLayersDialog}; + $self->{AdaptiveLayersDialog}->reload_preview if $self->{AdaptiveLayersDialog}; # if we have an export filename, start a new thread for exporting G-code if ($self->{export_gcode_output_file}) { @@ -2108,32 +2108,12 @@ sub object_layers_dialog { my $self = shift; my ($obj_idx) = @_; - if (!defined $obj_idx) { - ($obj_idx, undef) = $self->selected_object; - } - - if (!$Slic3r::GUI::have_OpenGL) { - Slic3r::GUI::show_error($self, "Please install the OpenGL modules to use this feature (see build instructions)."); - return; - } - - $self->{ObjectLayersDialog} = Slic3r::GUI::Plater::ObjectLayersDialog->new($self, - object => $self->{objects}[$obj_idx], - model_object => $self->{model}->objects->[$obj_idx], - obj_idx => $obj_idx, - ); - $self->{ObjectLayersDialog}->Show(); - - EVT_CLOSE($self->{ObjectLayersDialog}, sub { - my ($dlg, $event) = @_; - $dlg->Destroy; - $self->{ObjectLayersDialog} = undef; - }); + $self->object_settings_dialog($obj_idx, adaptive_layers => 1); } sub object_settings_dialog { my $self = shift; - my ($obj_idx) = @_; + my ($obj_idx, %params) = @_; if (!defined $obj_idx) { ($obj_idx, undef) = $self->selected_object; @@ -2148,9 +2128,15 @@ sub object_settings_dialog { my $dlg = Slic3r::GUI::Plater::ObjectSettingsDialog->new($self, object => $self->{objects}[$obj_idx], model_object => $model_object, + obj_idx => $obj_idx, ); + # store pointer to the adaptive layer tab to push preview updates + $self->{AdaptiveLayersDialog} = $dlg->{adaptive_layers}; + # and jump directly to the tab if called by "promo-button" + $dlg->{tabpanel}->SetSelection(1) if $params{adaptive_layers}; $self->pause_background_process; $dlg->ShowModal; + $self->{AdaptiveLayersDialog} = undef; # update thumbnail since parts may have changed if ($dlg->PartsChanged) { diff --git a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm b/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm deleted file mode 100644 index e81204a7b..000000000 --- a/lib/Slic3r/GUI/Plater/ObjectLayersDialog.pm +++ /dev/null @@ -1,150 +0,0 @@ -package Slic3r::GUI::Plater::ObjectLayersDialog; -use strict; -use warnings; -use utf8; - -use Slic3r::Geometry qw(PI X Y Z scale unscale); -use Slic3r::Print::State ':steps'; -use List::Util qw(min max sum first); -use Wx qw(wxTheApp :dialog :id :misc :sizer :slider :statictext wxTAB_TRAVERSAL); -use Wx::Event qw(EVT_CLOSE EVT_BUTTON EVT_SLIDER); -use base 'Wx::Dialog'; - -sub new { - my $class = shift; - my ($parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, $params{object}->name, wxDefaultPosition, [500,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); - $self->{model_object} = $params{model_object}; - my $model_object = $self->{model_object} = $params{model_object}; - my $obj_idx = $self->{obj_idx} = $params{obj_idx}; - my $plater = $self->{plater} = $parent; - my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); - - $self->{update_spline_control} = 0; - - # Initialize 3D toolpaths preview - if ($Slic3r::GUI::have_OpenGL) { - $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self, $plater->{print}); - $self->{preview3D}->canvas->set_auto_bed_shape; - $self->{preview3D}->canvas->SetSize([500,500]); - $self->{preview3D}->canvas->SetMinSize($self->{preview3D}->canvas->GetSize); - $self->{preview3D}->load_print; - $self->{preview3D}->canvas->zoom_to_volumes; - } - - $self->{splineControl} = Slic3r::GUI::Plater::SplineControl->new($self, Wx::Size->new(150, 200), $model_object); - - my $optgroup; - $optgroup = $self->{optgroup} = Slic3r::GUI::OptionsGroup->new( - parent => $self, - title => 'Adaptive quality %', - 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->{adaptive_quality} != $optgroup->get_value($opt_id)) { - $self->{adaptive_quality} = $optgroup->get_value($opt_id); - $self->{model_object}->config->set('adaptive_slicing_quality', $optgroup->get_value($opt_id)); - # trigger re-slicing - $self->_trigger_slicing; - } - }, - label_width => 0, - ); - - $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( - opt_id => 'adaptive_slicing_quality', - type => 'slider', - label => '', - default => $object->config->get('adaptive_slicing_quality'), - min => 0, - max => 100, - full_width => 1, - )); - $optgroup->get_field('adaptive_slicing_quality')->set_scale(1); - $self->{adaptive_quality} = $object->config->get('adaptive_slicing_quality'); - # init quality slider - if(!$object->config->get('adaptive_slicing')) { - # disable slider - $optgroup->get_field('adaptive_slicing_quality')->disable; - } - - - my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); - $right_sizer->Add($self->{splineControl}, 1, wxEXPAND | wxALL, 0); - $right_sizer->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 0); - - - $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); - $self->{sizer}->Add($self->{preview3D}, 3, wxEXPAND | wxTOP | wxBOTTOM, 0) if $self->{preview3D}; - $self->{sizer}->Add($right_sizer, 1, wxEXPAND | wxTOP | wxBOTTOM, 10); - - $self->SetSizerAndFit($self->{sizer}); - $self->SetSize([800, 600]); - $self->SetMinSize($self->GetSize); - - # init spline control values - # determine min and max layer height from perimeter extruder capabilities. - my %extruders; - for my $region_id (0 .. ($object->region_count - 1)) { - foreach (qw(perimeter_extruder infill_extruder solid_infill_extruder)) { - my $extruder_id = $self->{plater}->{print}->get_region($region_id)->config->get($_)-1; - $extruders{$extruder_id} = $extruder_id; - } - } - my $min_height = max(map {$self->{plater}->{print}->config->get_at('min_layer_height', $_)} (values %extruders)); - my $max_height = min(map {$self->{plater}->{print}->config->get_at('max_layer_height', $_)} (values %extruders)); - - $self->{splineControl}->set_size_parameters($min_height, $max_height, unscale($object->size->z)); - - - $self->{splineControl}->on_layer_update(sub { - # trigger re-slicing - $self->_trigger_slicing; - }); - - $self->{splineControl}->on_z_indicator(sub { - my ($z) = @_; - $self->{preview3D}->canvas->SetCuttingPlane(Z, $z, []); - $self->{preview3D}->canvas->Render; - }); - - return $self; -} - -sub _trigger_slicing { - my ($self) = @_; - my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); - $self->{plater}->pause_background_process; - $self->{plater}->stop_background_process; - if (!$Slic3r::GUI::Settings->{_}{background_processing}) { - $self->{plater}->statusbar->SetCancelCallback(sub { - $self->{plater}->stop_background_process; - $self->{plater}->statusbar->SetStatusText("Slicing cancelled"); - $self->{plater}->preview_notebook->SetSelection(0); - }); - $self->{plater}->{print}->reload_object($self->{obj_idx}); - $self->{plater}->on_model_change; - $self->{plater}->start_background_process; - }else{ - $self->{plater}->{print}->reload_object($self->{obj_idx}); - $self->{plater}->on_model_change; - $self->{plater}->schedule_background_process; - } -} - -sub reload_preview { - my ($self) = @_; - $self->{splineControl}->update; - $self->{preview3D}->reload_print; - my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); - if($object->layer_count-1 > 0) { - # causes segfault... - my $top_layer = $object->get_layer($object->layer_count-1); - $self->{preview3D}->set_z($top_layer->print_z); - } -} - -1; diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm index c0979cd13..ba4d9999d 100644 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm @@ -14,11 +14,16 @@ use base 'Wx::Dialog'; sub new { my $class = shift; my ($parent, %params) = @_; - my $self = $class->SUPER::new($parent, -1, "Settings for " . $params{object}->name, wxDefaultPosition, [700,500], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); + my $self = $class->SUPER::new($parent, -1, "Settings for " . $params{object}->name, wxDefaultPosition, [1000,700], wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER); $self->{$_} = $params{$_} for keys %params; $self->{tabpanel} = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL); $self->{tabpanel}->AddPage($self->{parts} = Slic3r::GUI::Plater::ObjectPartsPanel->new($self->{tabpanel}, model_object => $params{model_object}), "Parts"); + $self->{tabpanel}->AddPage($self->{adaptive_layers} = Slic3r::GUI::Plater::ObjectDialog::AdaptiveLayersTab->new( $self->{tabpanel}, + plater => $parent, + model_object => $params{model_object}, + obj_idx => $params{obj_idx} + ), "Adaptive Layers"); $self->{tabpanel}->AddPage($self->{layers} = Slic3r::GUI::Plater::ObjectDialog::LayersTab->new($self->{tabpanel}), "Layers"); my $buttons = $self->CreateStdDialogButtonSizer(wxOK); @@ -62,6 +67,157 @@ sub model_object { return $self->GetParent->GetParent->{model_object}; } +package Slic3r::GUI::Plater::ObjectDialog::AdaptiveLayersTab; +use Slic3r::Geometry qw(X Y Z scale unscale); +use List::Util qw(min max sum first); +use Wx qw(wxTheApp :dialog :id :misc :sizer :systemsettings :statictext wxTAB_TRAVERSAL); +use base 'Slic3r::GUI::Plater::ObjectDialog::BaseTab'; + +sub new { + my $class = shift; + my ($parent, %params) = @_; + my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize); + my $model_object = $self->{model_object} = $params{model_object}; + my $obj_idx = $self->{obj_idx} = $params{obj_idx}; + my $plater = $self->{plater} = $params{plater}; + my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); + + # Initialize 3D toolpaths preview + if ($Slic3r::GUI::have_OpenGL) { + $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self, $plater->{print}); + $self->{preview3D}->canvas->set_auto_bed_shape; + $self->{preview3D}->canvas->SetSize([500,500]); + $self->{preview3D}->canvas->SetMinSize($self->{preview3D}->canvas->GetSize); + # object already processed? + wxTheApp->CallAfter(sub { + if (!$plater->{processed}) { + $self->_trigger_slicing; + }else{ + $self->{preview3D}->load_print; + $self->{preview3D}->canvas->zoom_to_volumes; + $self->{preview_zoomed} = 1; + } + }); + } + + $self->{splineControl} = Slic3r::GUI::Plater::SplineControl->new($self, Wx::Size->new(150, 200), $model_object); + + my $optgroup; + $optgroup = $self->{optgroup} = Slic3r::GUI::OptionsGroup->new( + parent => $self, + title => 'Adaptive quality %', + 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->{adaptive_quality} != $optgroup->get_value($opt_id)) { + $self->{adaptive_quality} = $optgroup->get_value($opt_id); + $self->{model_object}->config->set('adaptive_slicing_quality', $optgroup->get_value($opt_id)); + # trigger re-slicing + $self->_trigger_slicing; + } + }, + label_width => 0, + ); + + $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( + opt_id => 'adaptive_slicing_quality', + type => 'slider', + label => '', + default => $object->config->get('adaptive_slicing_quality'), + min => 0, + max => 100, + full_width => 1, + )); + $optgroup->get_field('adaptive_slicing_quality')->set_scale(1); + $self->{adaptive_quality} = $object->config->get('adaptive_slicing_quality'); + # init quality slider + if(!$object->config->get('adaptive_slicing')) { + # disable slider + $optgroup->get_field('adaptive_slicing_quality')->disable; + } + + my $right_sizer = Wx::BoxSizer->new(wxVERTICAL); + $right_sizer->Add($self->{splineControl}, 1, wxEXPAND | wxALL, 0); + $right_sizer->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 0); + + + $self->{sizer} = Wx::BoxSizer->new(wxHORIZONTAL); + $self->{sizer}->Add($self->{preview3D}, 3, wxEXPAND | wxTOP | wxBOTTOM, 0) if $self->{preview3D}; + $self->{sizer}->Add($right_sizer, 1, wxEXPAND | wxTOP | wxBOTTOM, 10); + + $self->SetSizerAndFit($self->{sizer}); + + # init spline control values + # determine min and max layer height from perimeter extruder capabilities. + my %extruders; + for my $region_id (0 .. ($object->region_count - 1)) { + foreach (qw(perimeter_extruder infill_extruder solid_infill_extruder)) { + my $extruder_id = $self->{plater}->{print}->get_region($region_id)->config->get($_)-1; + $extruders{$extruder_id} = $extruder_id; + } + } + my $min_height = max(map {$self->{plater}->{print}->config->get_at('min_layer_height', $_)} (values %extruders)); + my $max_height = min(map {$self->{plater}->{print}->config->get_at('max_layer_height', $_)} (values %extruders)); + + $self->{splineControl}->set_size_parameters($min_height, $max_height, unscale($object->size->z)); + + $self->{splineControl}->on_layer_update(sub { + # trigger re-slicing + $self->_trigger_slicing; + }); + + $self->{splineControl}->on_z_indicator(sub { + my ($z) = @_; + $self->{preview3D}->canvas->SetCuttingPlane(Z, $z, []); + $self->{preview3D}->canvas->Render; + }); + + return $self; +} + +# This is called by the plater after processing to update the preview and spline +sub reload_preview { + my ($self) = @_; + $self->{splineControl}->update; + $self->{preview3D}->reload_print; + my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); + if($object->layer_count-1 > 0) { + my $top_layer = $object->get_layer($object->layer_count-1); + $self->{preview3D}->set_z($top_layer->print_z); + if(!$self->{preview_zoomed}) { + $self->{preview3D}->canvas->set_auto_bed_shape; + $self->{preview3D}->canvas->zoom_to_volumes; + $self->{preview_zoomed} = 1; + } + } +} + +# Trigger background slicing at the plater +sub _trigger_slicing { + my ($self) = @_; + my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); + $self->{plater}->pause_background_process; + $self->{plater}->stop_background_process; + if (!$Slic3r::GUI::Settings->{_}{background_processing}) { + $self->{plater}->statusbar->SetCancelCallback(sub { + $self->{plater}->stop_background_process; + $self->{plater}->statusbar->SetStatusText("Slicing cancelled"); + $self->{plater}->preview_notebook->SetSelection(0); + }); + $self->{plater}->{print}->reload_object($self->{obj_idx}); + $self->{plater}->on_model_change; + $self->{plater}->start_background_process; + }else{ + $self->{plater}->{print}->reload_object($self->{obj_idx}); + $self->{plater}->on_model_change; + $self->{plater}->schedule_background_process; + } +} + + package Slic3r::GUI::Plater::ObjectDialog::LayersTab; use Wx qw(:dialog :id :misc :sizer :systemsettings); use Wx::Grid; diff --git a/lib/Slic3r/GUI/Plater/SplineControl.pm b/lib/Slic3r/GUI/Plater/SplineControl.pm index 6de3ba0b3..f940ff11d 100644 --- a/lib/Slic3r/GUI/Plater/SplineControl.pm +++ b/lib/Slic3r/GUI/Plater/SplineControl.pm @@ -16,6 +16,7 @@ sub new { my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, $size, wxTAB_TRAVERSAL); $self->{object} = $object; + $self->{is_valid} = 0; # This has only effect on MacOS. On Windows and Linux/GTK, the background is painted by $self->repaint(). $self->SetBackgroundColour(Wx::wxWHITE); @@ -74,31 +75,33 @@ sub repaint { $dc->DrawLabel(sprintf('%.4g', $self->{min_layer_height}), Wx::Rect->new(0, $size[1]/2, $size[0], $size[1]/2), wxALIGN_LEFT | wxALIGN_BOTTOM); $dc->DrawLabel(sprintf('%.4g', $self->{max_layer_height}), Wx::Rect->new(0, $size[1]/2, $size[0], $size[1]/2), wxALIGN_RIGHT | wxALIGN_BOTTOM); + if($self->{is_valid}) { - # draw original spline as reference - if($self->{original_height_spline}) { - #draw spline - $self->_draw_layer_height_spline($dc, $self->{original_height_spline}, $self->{original_pen}); - } - - # draw interactive (currently modified by the user) layers as lines and spline - if($self->{interactive_height_spline}) { - # draw layer lines - my @interpolated_layers = @{$self->{interactive_height_spline}->getInterpolatedLayers}; - $self->_draw_layers_as_lines($dc, $self->{interactive_pen}, \@interpolated_layers); - - #draw spline - $self->_draw_layer_height_spline($dc, $self->{interactive_height_spline}, $self->{interactive_pen}); + # draw original spline as reference + if($self->{original_height_spline}) { + #draw spline + $self->_draw_layer_height_spline($dc, $self->{original_height_spline}, $self->{original_pen}); + } + + # draw interactive (currently modified by the user) layers as lines and spline + if($self->{interactive_height_spline}) { + # draw layer lines + my @interpolated_layers = @{$self->{interactive_height_spline}->getInterpolatedLayers}; + $self->_draw_layers_as_lines($dc, $self->{interactive_pen}, \@interpolated_layers); + + #draw spline + $self->_draw_layer_height_spline($dc, $self->{interactive_height_spline}, $self->{interactive_pen}); + } + + # draw resulting layers as lines + unless($self->{interactive_heights}) { + $self->_draw_layers_as_lines($dc, $self->{resulting_pen}, $self->{interpolated_layers}); + } + + # Always draw current BSpline, gives a reference during a modification + $self->_draw_layer_height_spline($dc, $self->{object}->layer_height_spline, $self->{line_pen}); } - # draw resulting layers as lines - unless($self->{interactive_heights}) { - $self->_draw_layers_as_lines($dc, $self->{resulting_pen}, $self->{interpolated_layers}); - } - - # Always draw current BSpline, gives a reference during a modification - $self->_draw_layer_height_spline($dc, $self->{object}->layer_height_spline, $self->{line_pen}); - $event->Skip; } @@ -120,7 +123,7 @@ sub set_size_parameters { sub update { my $self = shift; - if($self->{object}->layer_height_spline->layersUpdated || !$self->{heights}) { + if(($self->{object}->layer_height_spline->layersUpdated || !$self->{heights}) && $self->{object}->layer_height_spline->hasData) { $self->{original_height_spline} = $self->{object}->layer_height_spline->clone; # make a copy to display the unmodified original spline $self->{original_layers} = $self->{object}->layer_height_spline->getOriginalLayers; $self->{interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; # Initialize to current values @@ -131,6 +134,7 @@ sub update { foreach my $z (@{$self->{original_layers}}) { push (@{$self->{heights}}, $self->{object}->layer_height_spline->getLayerHeightAt($z)); } + $self->{is_valid} = 1; } $self->Refresh; } From 6b9188a5683184b7d723ec06159ba1b6e3b89db0 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 6 Apr 2017 08:54:23 +0200 Subject: [PATCH 47/64] show only selected object in toolpath preview --- lib/Slic3r/GUI/Plater/3DPreview.pm | 34 ++++++++++++------- lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm | 4 +-- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/3DPreview.pm b/lib/Slic3r/GUI/Plater/3DPreview.pm index dddefee01..4d6af8287 100644 --- a/lib/Slic3r/GUI/Plater/3DPreview.pm +++ b/lib/Slic3r/GUI/Plater/3DPreview.pm @@ -73,15 +73,15 @@ sub new { } sub reload_print { - my ($self) = @_; + my ($self, $obj_idx) = @_; $self->canvas->reset_objects; $self->_loaded(0); - $self->load_print; + $self->load_print($obj_idx); } sub load_print { - my ($self) = @_; + my ($self, $obj_idx) = @_; return if $self->_loaded; @@ -98,10 +98,16 @@ sub load_print { my $z_idx; { my %z = (); # z => 1 - foreach my $object (@{$self->{print}->objects}) { - foreach my $layer (@{$object->layers}, @{$object->support_layers}) { + if(defined $obj_idx) { # Load only given object + foreach my $layer (@{$self->{print}->get_object($obj_idx)->layers}) { $z{$layer->print_z} = 1; } + }else{ # Load all objects on the plater + support material + foreach my $object (@{$self->{print}->objects}) { + foreach my $layer (@{$object->layers}, @{$object->support_layers}) { + $z{$layer->print_z} = 1; + } + } } $self->enabled(1); $self->{layers_z} = [ sort { $a <=> $b } keys %z ]; @@ -127,14 +133,18 @@ sub load_print { $self->canvas->colors([ $self->canvas->default_colors ]); } - # load skirt and brim - $self->canvas->load_print_toolpaths($self->print); - - foreach my $object (@{$self->print->objects}) { - $self->canvas->load_print_object_toolpaths($object); + if(defined $obj_idx) { # Load only one object + $self->canvas->load_print_object_toolpaths($self->{print}->get_object($obj_idx)); + }else{ # load all objects + # load skirt and brim + $self->canvas->load_print_toolpaths($self->print); - #my @volume_ids = $self->canvas->load_object($object->model_object); - #$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids; + foreach my $object (@{$self->print->objects}) { + $self->canvas->load_print_object_toolpaths($object); + + #my @volume_ids = $self->canvas->load_object($object->model_object); + #$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids; + } } $self->_loaded(1); } diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm index ba4d9999d..d6622125c 100644 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm @@ -93,7 +93,7 @@ sub new { if (!$plater->{processed}) { $self->_trigger_slicing; }else{ - $self->{preview3D}->load_print; + $self->{preview3D}->reload_print($obj_idx); $self->{preview3D}->canvas->zoom_to_volumes; $self->{preview_zoomed} = 1; } @@ -182,7 +182,7 @@ sub new { sub reload_preview { my ($self) = @_; $self->{splineControl}->update; - $self->{preview3D}->reload_print; + $self->{preview3D}->reload_print($self->{obj_idx}); my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); if($object->layer_count-1 > 0) { my $top_layer = $object->get_layer($object->layer_count-1); From 5eab886069420f2165ef7ce3f8053f03517e0619 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Mon, 10 Apr 2017 15:03:39 +0200 Subject: [PATCH 48/64] Use layer height spline from printObject and push updates to modelObject to avoid reloading --- lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm | 17 ++++++++++------- xs/xsp/Model.xsp | 2 ++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm index d6622125c..73a4eaa32 100644 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm @@ -69,6 +69,7 @@ sub model_object { package Slic3r::GUI::Plater::ObjectDialog::AdaptiveLayersTab; use Slic3r::Geometry qw(X Y Z scale unscale); +use Slic3r::Print::State ':steps'; use List::Util qw(min max sum first); use Wx qw(wxTheApp :dialog :id :misc :sizer :systemsettings :statictext wxTAB_TRAVERSAL); use base 'Slic3r::GUI::Plater::ObjectDialog::BaseTab'; @@ -80,7 +81,7 @@ sub new { my $model_object = $self->{model_object} = $params{model_object}; my $obj_idx = $self->{obj_idx} = $params{obj_idx}; my $plater = $self->{plater} = $params{plater}; - my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); + my $object = $self->{object} = $self->{plater}->{print}->get_object($self->{obj_idx}); # Initialize 3D toolpaths preview if ($Slic3r::GUI::have_OpenGL) { @@ -91,6 +92,7 @@ sub new { # object already processed? wxTheApp->CallAfter(sub { if (!$plater->{processed}) { + $object->layer_height_spline->suppressUpdate(); $self->_trigger_slicing; }else{ $self->{preview3D}->reload_print($obj_idx); @@ -100,7 +102,7 @@ sub new { }); } - $self->{splineControl} = Slic3r::GUI::Plater::SplineControl->new($self, Wx::Size->new(150, 200), $model_object); + $self->{splineControl} = Slic3r::GUI::Plater::SplineControl->new($self, Wx::Size->new(150, 200), $object); my $optgroup; $optgroup = $self->{optgroup} = Slic3r::GUI::OptionsGroup->new( @@ -115,6 +117,7 @@ sub new { if ($self->{adaptive_quality} != $optgroup->get_value($opt_id)) { $self->{adaptive_quality} = $optgroup->get_value($opt_id); $self->{model_object}->config->set('adaptive_slicing_quality', $optgroup->get_value($opt_id)); + $self->{object}->config->set('adaptive_slicing_quality', $optgroup->get_value($opt_id)); # trigger re-slicing $self->_trigger_slicing; } @@ -199,7 +202,9 @@ sub reload_preview { sub _trigger_slicing { my ($self) = @_; my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); - $self->{plater}->pause_background_process; + $self->{model_object}->set_layer_height_spline($self->{object}->layer_height_spline); # push modified spline object to model_object + $self->{model_object}->layer_height_spline->updateRequired; # make sure the model_object spline requires update + #$self->{plater}->pause_background_process; $self->{plater}->stop_background_process; if (!$Slic3r::GUI::Settings->{_}{background_processing}) { $self->{plater}->statusbar->SetCancelCallback(sub { @@ -207,12 +212,10 @@ sub _trigger_slicing { $self->{plater}->statusbar->SetStatusText("Slicing cancelled"); $self->{plater}->preview_notebook->SetSelection(0); }); - $self->{plater}->{print}->reload_object($self->{obj_idx}); - $self->{plater}->on_model_change; + $object->invalidate_step(STEP_SLICE); $self->{plater}->start_background_process; }else{ - $self->{plater}->{print}->reload_object($self->{obj_idx}); - $self->{plater}->on_model_change; + $object->invalidate_step(STEP_SLICE); $self->{plater}->schedule_background_process; } } diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 9d8179e79..01ba73bab 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -203,6 +203,8 @@ ModelMaterial::attributes() Ref layer_height_spline() %code%{ RETVAL = &THIS->layer_height_spline; %}; + void set_layer_height_spline(LayerHeightSpline* spline) + %code%{ THIS->layer_height_spline = *spline; %}; Ref origin_translation() %code%{ RETVAL = &THIS->origin_translation; %}; From 614f4d9ca48c08388689af4de8bad42510883bc0 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 18 Apr 2017 08:44:18 +0200 Subject: [PATCH 49/64] After opening the adaptive layer dialog, trigger reslicing only if necessary --- lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm | 10 +++++----- xs/src/libslic3r/Model.cpp | 8 -------- xs/src/libslic3r/PrintObject.cpp | 2 ++ 3 files changed, 7 insertions(+), 13 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm index 73a4eaa32..beb5699a2 100644 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm @@ -92,8 +92,7 @@ sub new { # object already processed? wxTheApp->CallAfter(sub { if (!$plater->{processed}) { - $object->layer_height_spline->suppressUpdate(); - $self->_trigger_slicing; + $self->_trigger_slicing(0); # trigger processing without invalidating STEP_SLICE to keep current height distribution }else{ $self->{preview3D}->reload_print($obj_idx); $self->{preview3D}->canvas->zoom_to_volumes; @@ -200,7 +199,8 @@ sub reload_preview { # Trigger background slicing at the plater sub _trigger_slicing { - my ($self) = @_; + my ($self, $invalidate) = @_; + $invalidate //= 1; my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); $self->{model_object}->set_layer_height_spline($self->{object}->layer_height_spline); # push modified spline object to model_object $self->{model_object}->layer_height_spline->updateRequired; # make sure the model_object spline requires update @@ -212,10 +212,10 @@ sub _trigger_slicing { $self->{plater}->statusbar->SetStatusText("Slicing cancelled"); $self->{plater}->preview_notebook->SetSelection(0); }); - $object->invalidate_step(STEP_SLICE); + $object->invalidate_step(STEP_SLICE) if($invalidate); $self->{plater}->start_background_process; }else{ - $object->invalidate_step(STEP_SLICE); + $object->invalidate_step(STEP_SLICE) if($invalidate); $self->{plater}->schedule_background_process; } } diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index e85b0c7c9..d3966e2be 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -515,8 +515,6 @@ ModelObject::add_instance() { ModelInstance* i = new ModelInstance(this); this->instances.push_back(i); - this->invalidate_bounding_box(); - this->layer_height_spline.setObjectHeight(this->raw_bounding_box().size().z); return i; } @@ -525,8 +523,6 @@ ModelObject::add_instance(const ModelInstance &other) { ModelInstance* i = new ModelInstance(this, other); this->instances.push_back(i); - this->invalidate_bounding_box(); - this->layer_height_spline.setObjectHeight(this->raw_bounding_box().size().z); return i; } @@ -536,10 +532,6 @@ ModelObject::delete_instance(size_t idx) ModelInstancePtrs::iterator i = this->instances.begin() + idx; delete *i; this->instances.erase(i); - if(!this->instances.empty()) { - this->layer_height_spline.setObjectHeight(this->raw_bounding_box().size().z); - } - this->invalidate_bounding_box(); } void diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index e1d144f96..783e4a8d1 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -580,6 +580,8 @@ std::vector PrintObject::generate_object_layers(coordf_t first_layer_h coordf_t print_z = first_layer_height; coordf_t height = first_layer_height; + // Update object size at the spline object to define upper border + this->layer_height_spline.setObjectHeight(unscale(this->size.z)); if(!this->layer_height_spline.updateRequired()) { // layer heights are already generated, just update layers from spline // we don't need to respect first layer here, it's correctly provided by the spline object result = this->layer_height_spline.getInterpolatedLayers(); From 802b631e2dfd6c7c7e73a3fed5e239849be099eb Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 18 Apr 2017 08:46:56 +0200 Subject: [PATCH 50/64] Margin around layer height plot --- lib/Slic3r/GUI/Plater/SplineControl.pm | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/SplineControl.pm b/lib/Slic3r/GUI/Plater/SplineControl.pm index f940ff11d..071b0ac4c 100644 --- a/lib/Slic3r/GUI/Plater/SplineControl.pm +++ b/lib/Slic3r/GUI/Plater/SplineControl.pm @@ -242,8 +242,10 @@ sub _update_canvas_size { my ($canvas_w, $canvas_h) = ($canvas_size->GetWidth, $canvas_size->GetHeight); return if $canvas_w == 0; - my $size = $canvas_size; - my @size = ($size->GetWidth, $size->GetHeight); + my $padding = $self->{canvas_padding} = 10; # border size in pixels + + my @size = ($canvas_w - 2*$padding, $canvas_h - 2*$padding); + $self->{canvas_size} = [@size]; $self->{scaling_factor_x} = $size[0]/($self->{max_layer_height} - $self->{min_layer_height}); $self->{scaling_factor_y} = $size[1]/$self->{object_height}; @@ -325,8 +327,7 @@ sub _draw_layers_as_lines { sub _draw_layer_height_spline { my ($self, $dc, $layer_height_spline, $pen) = @_; - my $size = $self->GetSize; - my @size = ($size->GetWidth, $size->GetHeight); + my @size = @{$self->{canvas_size}}; $dc->SetPen($pen); my @points = (); @@ -344,13 +345,12 @@ sub _draw_layer_height_spline { sub point_to_pixel { my ($self, @point) = @_; - my $size = $self->GetSize; - my @size = ($size->GetWidth, $size->GetHeight); + my @size = @{$self->{canvas_size}}; - my $x = ($point[0] - $self->{min_layer_height})*$self->{scaling_factor_x}; - my $y = $size[1] - $point[1]*$self->{scaling_factor_y}; # invert y-axis + my $x = ($point[0] - $self->{min_layer_height})*$self->{scaling_factor_x} + $self->{canvas_padding}; + my $y = $size[1] - $point[1]*$self->{scaling_factor_y} + $self->{canvas_padding}; # invert y-axis - return Wx::Point->new(min(max($x, 0), $size[0]), min(max($y, 0), $size[1])); # limit to canvas size + return Wx::Point->new(min(max($x, $self->{canvas_padding}), $size[0]+$self->{canvas_padding}), min(max($y, $self->{canvas_padding}), $size[1]+$self->{canvas_padding})); # limit to canvas size } # Takes a Wx::Point in scaled canvas coordinates and converts it @@ -358,10 +358,9 @@ sub point_to_pixel { sub pixel_to_point { my ($self, $point) = @_; - my $size = $self->GetSize; - my @size = ($size->GetWidth, $size->GetHeight); + my @size = @{$self->{canvas_size}}; - my $x = $point->x/$self->{scaling_factor_x} + $self->{min_layer_height}; + my $x = ($point->x-$self->{canvas_padding})/$self->{scaling_factor_x} + $self->{min_layer_height}; my $y = ($size[1] - $point->y)/$self->{scaling_factor_y}; # invert y-axis return (min(max($x, $self->{min_layer_height}), $self->{max_layer_height}), min(max($y, 0), $self->{object_height})); # limit to object size and layer constraints From 2c85797c1f3184968d3627105a4f76d20436bc39 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 18 Apr 2017 13:48:36 +0200 Subject: [PATCH 51/64] Introduce new step PosLayers to split layer generation and slicing --- lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm | 2 +- lib/Slic3r/GUI/Plater/SplineControl.pm | 1 - lib/Slic3r/Print/State.pm | 2 +- xs/src/libslic3r/LayerHeightSpline.cpp | 27 ------------------- xs/src/libslic3r/LayerHeightSpline.hpp | 5 +--- xs/src/libslic3r/Print.cpp | 5 ++-- xs/src/libslic3r/Print.hpp | 2 +- xs/src/libslic3r/PrintObject.cpp | 13 ++++++--- xs/xsp/LayerHeightSpline.xsp | 2 -- xs/xsp/Print.xsp | 1 + 10 files changed, 17 insertions(+), 43 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm index beb5699a2..1b6c558e1 100644 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm @@ -117,6 +117,7 @@ sub new { $self->{adaptive_quality} = $optgroup->get_value($opt_id); $self->{model_object}->config->set('adaptive_slicing_quality', $optgroup->get_value($opt_id)); $self->{object}->config->set('adaptive_slicing_quality', $optgroup->get_value($opt_id)); + $self->{object}->invalidate_step(STEP_LAYERS); # trigger re-slicing $self->_trigger_slicing; } @@ -203,7 +204,6 @@ sub _trigger_slicing { $invalidate //= 1; my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); $self->{model_object}->set_layer_height_spline($self->{object}->layer_height_spline); # push modified spline object to model_object - $self->{model_object}->layer_height_spline->updateRequired; # make sure the model_object spline requires update #$self->{plater}->pause_background_process; $self->{plater}->stop_background_process; if (!$Slic3r::GUI::Settings->{_}{background_processing}) { diff --git a/lib/Slic3r/GUI/Plater/SplineControl.pm b/lib/Slic3r/GUI/Plater/SplineControl.pm index 071b0ac4c..a6034c5a0 100644 --- a/lib/Slic3r/GUI/Plater/SplineControl.pm +++ b/lib/Slic3r/GUI/Plater/SplineControl.pm @@ -228,7 +228,6 @@ sub _modification_done { $self->{interpolated_layers} = $self->{object}->layer_height_spline->getInterpolatedLayers; } $self->Refresh; - $self->{object}->layer_height_spline->suppressUpdate; $self->{on_layer_update}->(@{$self->{interpolated_layers}}); $self->{interactive_height_spline} = undef; } diff --git a/lib/Slic3r/Print/State.pm b/lib/Slic3r/Print/State.pm index d242e3760..91d4a6ef9 100644 --- a/lib/Slic3r/Print/State.pm +++ b/lib/Slic3r/Print/State.pm @@ -5,7 +5,7 @@ use warnings; require Exporter; our @ISA = qw(Exporter); -our @EXPORT_OK = qw(STEP_SLICE STEP_PERIMETERS STEP_PREPARE_INFILL +our @EXPORT_OK = qw(STEP_LAYERS STEP_SLICE STEP_PERIMETERS STEP_PREPARE_INFILL STEP_INFILL STEP_SUPPORTMATERIAL STEP_SKIRT STEP_BRIM); our %EXPORT_TAGS = (steps => \@EXPORT_OK); diff --git a/xs/src/libslic3r/LayerHeightSpline.cpp b/xs/src/libslic3r/LayerHeightSpline.cpp index 1e8da6cfe..905f4ca81 100644 --- a/xs/src/libslic3r/LayerHeightSpline.cpp +++ b/xs/src/libslic3r/LayerHeightSpline.cpp @@ -15,7 +15,6 @@ LayerHeightSpline::LayerHeightSpline() : _object_height(0) { this->_is_valid = false; - this->_update_required = true; this->_layers_updated = false; this->_layer_heights_updated = false; } @@ -32,7 +31,6 @@ LayerHeightSpline& LayerHeightSpline::operator=(const LayerHeightSpline &other) this->_internal_layers = other._internal_layers; this->_internal_layer_heights = other._internal_layer_heights; this->_is_valid = other._is_valid; - this->_update_required = other._update_required; this->_layers_updated = other._layers_updated; this->_layer_heights_updated = other._layer_heights_updated; if(this->_is_valid) { @@ -49,31 +47,6 @@ bool LayerHeightSpline::hasData() return this->_is_valid; } -/* - * Does this object expect new layer heights during the slice step or should - * we use the layer heights values provided by the spline? - * An update is required if a config option is changed which affects the layer height. - * An update is not required if the spline was modified by a user interaction. - */ -bool LayerHeightSpline::updateRequired() -{ - bool result = true; // update spline by default - if(!this->_update_required && this->_is_valid) { - result = false; - } - this->_update_required = true; // reset to default after request - return result; -} - -/* - * Don't require an update for exactly one iteration. - */ -void LayerHeightSpline::suppressUpdate() { - if (this->_is_valid) { - this->_update_required = false; - } -} - /* * Set absolute layer positions in object coordinates. * Heights (thickness of each layer) is generated from this list. diff --git a/xs/src/libslic3r/LayerHeightSpline.hpp b/xs/src/libslic3r/LayerHeightSpline.hpp index 0c46744f0..e9253d182 100644 --- a/xs/src/libslic3r/LayerHeightSpline.hpp +++ b/xs/src/libslic3r/LayerHeightSpline.hpp @@ -14,9 +14,7 @@ class LayerHeightSpline LayerHeightSpline(const LayerHeightSpline &other); LayerHeightSpline& operator=(const LayerHeightSpline &other); void setObjectHeight(coordf_t object_height) { this->_object_height = object_height; }; - bool hasData(); // indicate that we have valid data - bool updateRequired(); // indicate whether we want to generate a new spline from the layers - void suppressUpdate(); + bool hasData(); // indicate that we have valid data; bool setLayers(std::vector layers); bool updateLayerHeights(std::vector heights); bool layersUpdated() const { return this->_layers_updated; }; // true if the basis set of layers was updated (by the slicing algorithm) @@ -31,7 +29,6 @@ class LayerHeightSpline coordf_t _object_height; bool _is_valid; - bool _update_required; // this should be always true except if we want to generate new layers from this spline bool _layers_updated; bool _layer_heights_updated; std::vector _original_layers; diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 35f6e13e4..2100f638d 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -168,8 +168,9 @@ Print::invalidate_state_by_config(const PrintConfigBase &config) || opt_key == "brim_connections_width") { steps.insert(psBrim); steps.insert(psSkirt); - } else if (opt_key == "nozzle_diameter" - || opt_key == "resolution" + } else if (opt_key == "nozzle_diameter") { + osteps.insert(posLayers); + } else if (opt_key == "resolution" || opt_key == "z_steps_per_mm") { osteps.insert(posSlice); } else if (opt_key == "avoid_crossing_perimeters" diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 7226cd390..196565b57 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -27,7 +27,7 @@ enum PrintStep { psSkirt, psBrim, }; enum PrintObjectStep { - posSlice, posPerimeters, posDetectSurfaces, + posLayers, posSlice, posPerimeters, posDetectSurfaces, posPrepareInfill, posInfill, posSupportMaterial, }; diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 783e4a8d1..5ca3087e1 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -224,11 +224,12 @@ PrintObject::invalidate_state_by_config(const PrintConfigBase &config) for (const t_config_option_key &opt_key : diff) { if (opt_key == "layer_height" || opt_key == "first_layer_height" - || opt_key == "xy_size_compensation" - || opt_key == "raft_layers" || opt_key == "adaptive_slicing" || opt_key == "adaptive_slicing_quality" || opt_key == "match_horizontal_surfaces") { + steps.insert(posLayers); + } else if (opt_key == "xy_size_compensation" + || opt_key == "raft_layers") { steps.insert(posSlice); } else if (opt_key == "support_material_contact_distance") { steps.insert(posSlice); @@ -299,6 +300,8 @@ PrintObject::invalidate_step(PrintObjectStep step) this->invalidate_step(posPerimeters); this->invalidate_step(posDetectSurfaces); this->invalidate_step(posSupportMaterial); + }else if (step == posLayers) { + this->invalidate_step(posSlice); } else if (step == posSupportMaterial) { this->_print->invalidate_step(psSkirt); this->_print->invalidate_step(psBrim); @@ -582,11 +585,11 @@ std::vector PrintObject::generate_object_layers(coordf_t first_layer_h // Update object size at the spline object to define upper border this->layer_height_spline.setObjectHeight(unscale(this->size.z)); - if(!this->layer_height_spline.updateRequired()) { // layer heights are already generated, just update layers from spline + if (this->state.is_done(posLayers)) { + // layer heights are already generated, just update layers from spline // we don't need to respect first layer here, it's correctly provided by the spline object result = this->layer_height_spline.getInterpolatedLayers(); }else{ // create new set of layers - // create stateful objects and variables for the adaptive slicing process SlicingAdaptive as; coordf_t adaptive_quality = this->config.adaptive_slicing_quality.value; @@ -679,6 +682,8 @@ std::vector PrintObject::generate_object_layers(coordf_t first_layer_h if (this->config.adaptive_slicing.value) { // smoothing after adaptive algorithm result = this->layer_height_spline.getInterpolatedLayers(); } + + this->state.set_done(posLayers); } // push modified spline object back to model diff --git a/xs/xsp/LayerHeightSpline.xsp b/xs/xsp/LayerHeightSpline.xsp index 9ff1571aa..67d7f1e11 100644 --- a/xs/xsp/LayerHeightSpline.xsp +++ b/xs/xsp/LayerHeightSpline.xsp @@ -12,8 +12,6 @@ void setObjectHeight(coordf_t object_height); bool hasData(); - bool updateRequired(); - void suppressUpdate(); bool setLayers(std::vector layers) %code%{ RETVAL = THIS->setLayers(layers); %}; bool updateLayerHeights(std::vector heights) diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 106bcc8b1..9397961dd 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -12,6 +12,7 @@ IV _constant() ALIAS: + STEP_LAYERS = posLayers STEP_SLICE = posSlice STEP_PERIMETERS = posPerimeters STEP_DETECT_SURFACES = posDetectSurfaces From acf4814eae7efce79125cfe6c7aecb7a5eba0495 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 18 Apr 2017 14:53:00 +0200 Subject: [PATCH 52/64] fix: removed forgotten codeline --- lib/Slic3r/GUI/Plater.pm | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 561634cd8..ca17a8ee0 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -1566,9 +1566,6 @@ sub export_gcode { $self->object_list_changed; }); - # Supress re-initialization of spline based layer heights - $_->layer_height_spline->suppressUpdate for @{$self->{print}->objects}; - # start background process, whose completion event handler # will detect $self->{export_gcode_output_file} and proceed with export $self->start_background_process; From 3836e12bd1b65c8ee4637adcd7778610dfc5f463 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 19 Apr 2017 08:44:29 +0200 Subject: [PATCH 53/64] Refactoring of spline object for better handling of first layer and boundary cases --- xs/src/libslic3r/LayerHeightSpline.cpp | 59 +++++++++++++------------- xs/src/libslic3r/LayerHeightSpline.hpp | 7 ++- 2 files changed, 32 insertions(+), 34 deletions(-) diff --git a/xs/src/libslic3r/LayerHeightSpline.cpp b/xs/src/libslic3r/LayerHeightSpline.cpp index 905f4ca81..d5c13f5e8 100644 --- a/xs/src/libslic3r/LayerHeightSpline.cpp +++ b/xs/src/libslic3r/LayerHeightSpline.cpp @@ -27,9 +27,8 @@ LayerHeightSpline::LayerHeightSpline(const LayerHeightSpline &other) LayerHeightSpline& LayerHeightSpline::operator=(const LayerHeightSpline &other) { this->_object_height = other._object_height; - this->_original_layers = other._original_layers; - this->_internal_layers = other._internal_layers; - this->_internal_layer_heights = other._internal_layer_heights; + this->_layers = other._layers; + this->_layer_heights = other._layer_heights; this->_is_valid = other._is_valid; this->_layers_updated = other._layers_updated; this->_layer_heights_updated = other._layer_heights_updated; @@ -53,23 +52,16 @@ bool LayerHeightSpline::hasData() */ bool LayerHeightSpline::setLayers(std::vector layers) { - this->_original_layers = layers; + this->_layers = layers; // generate updated layer height list from layers - this->_internal_layer_heights.clear(); + this->_layer_heights.clear(); coordf_t last_z = 0; - for (std::vector::const_iterator l = this->_original_layers.begin(); l != this->_original_layers.end(); ++l) { - this->_internal_layer_heights.push_back(*l-last_z); + for (std::vector::const_iterator l = this->_layers.begin(); l != this->_layers.end(); ++l) { + this->_layer_heights.push_back(*l-last_z); last_z = *l; } - // add 0-values at both ends to achieve correct boundary conditions - this->_internal_layers = this->_original_layers; - this->_internal_layers.insert(this->_internal_layers.begin(), 0); // add z = 0 to the front - //this->_internal_layers.push_back(this->_internal_layers.back()+1); // and object_height + 1 to the end - this->_internal_layer_heights.insert(this->_internal_layer_heights.begin(), this->_internal_layer_heights[0]); - //this->_internal_layer_heights.push_back(0); - this->_layers_updated = true; this->_layer_heights_updated = false; @@ -88,14 +80,11 @@ bool LayerHeightSpline::updateLayerHeights(std::vector heights) bool result = false; // do we receive the correct number of values? - if(heights.size() == this->_internal_layers.size()-1) { - this->_internal_layer_heights = heights; - // add leading and trailing 0-value - this->_internal_layer_heights.insert(this->_internal_layer_heights.begin(), this->_internal_layer_heights[0]); - //this->_internal_layer_heights.push_back(0); + if(heights.size() == this->_layers.size()) { + this->_layer_heights = heights; result = this->_updateBSpline(); }else{ - std::cerr << "Unable to update layer heights. You provided " << heights.size() << " layers, but " << this->_internal_layers.size()-1 << " expected" << std::endl; + std::cerr << "Unable to update layer heights. You provided " << heights.size() << " layers, but " << this->_layers.size()-1 << " expected" << std::endl; } this->_layers_updated = false; @@ -109,9 +98,8 @@ bool LayerHeightSpline::updateLayerHeights(std::vector heights) */ void LayerHeightSpline::clear() { - this->_original_layers.clear(); - this->_internal_layers.clear(); - this->_internal_layer_heights.clear(); + this->_layers.clear(); + this->_layer_heights.clear(); this->_layer_height_spline.reset(); this->_is_valid = false; this->_layers_updated = false; @@ -127,8 +115,8 @@ std::vector LayerHeightSpline::getInterpolatedLayers() const std::vector layers; if(this->_is_valid) { // preserve first layer for bed contact - layers.push_back(this->_original_layers[0]); - coordf_t z = this->_original_layers[0]; + layers.push_back(this->_layers[0]); + coordf_t z = this->_layers[0]; coordf_t h; coordf_t h_diff = 0; coordf_t last_h_diff = 0; @@ -143,7 +131,12 @@ std::vector LayerHeightSpline::getInterpolatedLayers() const h = this->_layer_height_spline->evaluate(z+h); h_diff = this->_layer_height_spline->evaluate(z+h) - h; } while(std::abs(h_diff) > eps && std::abs(h_diff - last_h_diff) > eps); - z += h; + + if(z+h > this->_object_height) { + z += this->_layer_height_spline->evaluate(layers.back()); // re-use last layer height if outside of defined range + }else{ + z += h; + } layers.push_back(z); } // how to make sure, the last layer is not higher than object while maintaining between min/max layer height? @@ -158,7 +151,13 @@ const coordf_t LayerHeightSpline::getLayerHeightAt(coordf_t height) { coordf_t result = 0; if (this->_is_valid) { - result = this->_layer_height_spline->evaluate(height); + if(height <= this->_layers[0]) { + result = this->_layers[0]; // return first_layer height + }else if (height > this->_layers.back()){ + result = this->_layer_height_spline->evaluate(this->_layers.back()); // repeat last value for height > last layer + }else{ + result = this->_layer_height_spline->evaluate(height); // return interpolated layer height + } } return result; } @@ -171,9 +170,9 @@ bool LayerHeightSpline::_updateBSpline() bool result = false; //TODO: exception if not enough points? - this->_layer_height_spline.reset(new BSpline(&this->_internal_layers[0], - this->_internal_layers.size(), - &this->_internal_layer_heights[0], + this->_layer_height_spline.reset(new BSpline(&this->_layers[1], + this->_layers.size()-1, + &this->_layer_heights[1], 0, 1, 0) diff --git a/xs/src/libslic3r/LayerHeightSpline.hpp b/xs/src/libslic3r/LayerHeightSpline.hpp index e9253d182..8fb554cbb 100644 --- a/xs/src/libslic3r/LayerHeightSpline.hpp +++ b/xs/src/libslic3r/LayerHeightSpline.hpp @@ -20,7 +20,7 @@ class LayerHeightSpline bool layersUpdated() const { return this->_layers_updated; }; // true if the basis set of layers was updated (by the slicing algorithm) bool layerHeightsUpdated() const { return this->_layer_heights_updated; }; // true if the heights where updated (by the spline control user interface) void clear(); - std::vector getOriginalLayers() const { return this->_original_layers; }; + std::vector getOriginalLayers() const { return this->_layers; }; std::vector getInterpolatedLayers() const; const coordf_t getLayerHeightAt(coordf_t height); @@ -31,9 +31,8 @@ class LayerHeightSpline bool _is_valid; bool _layers_updated; bool _layer_heights_updated; - std::vector _original_layers; - std::vector _internal_layers; - std::vector _internal_layer_heights; + std::vector _layers; + std::vector _layer_heights; std::unique_ptr> _layer_height_spline; }; From 8e6cb4014003009e8dab7afa1e73d07048cd085c Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 19 Apr 2017 10:10:33 +0200 Subject: [PATCH 54/64] Add category to adaptive slicing config options --- xs/src/libslic3r/PrintConfig.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xs/src/libslic3r/PrintConfig.cpp b/xs/src/libslic3r/PrintConfig.cpp index 3e73e4829..56485977e 100644 --- a/xs/src/libslic3r/PrintConfig.cpp +++ b/xs/src/libslic3r/PrintConfig.cpp @@ -25,12 +25,14 @@ PrintConfigDef::PrintConfigDef() def = this->add("adaptive_slicing", coBool); def->label = "Use adaptive slicing"; + def->category = "Layers and Perimeters"; def->tooltip = "Automatically determine layer heights by the objects topology instead of using the static value."; def->cli = "adaptive-slicing!"; def->default_value = new ConfigOptionBool(false); def = this->add("adaptive_slicing_quality", coPercent); def->label = "Adaptive quality"; + def->category = "Layers and Perimeters"; def->tooltip = "Controls the quality / printing time tradeoff for adaptive layer generation. 0 -> fastest printing with max layer height, 100 -> highest quality, min layer height"; def->sidetext = "%"; def->cli = "adaptive_slicing_quality=f"; From bdd3afa2ba1a82106ca59253d4a4912b068e5f02 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 19 Apr 2017 13:35:09 +0200 Subject: [PATCH 55/64] Compensate raft height for adaptive indicator plane --- lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm index 99b569684..9428c0cf8 100644 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm @@ -185,6 +185,12 @@ sub new { $self->{splineControl}->on_z_indicator(sub { my ($z) = @_; + + if($z) { # compensate raft height + my $top_layer = $self->{object}->get_layer($self->{object}->layer_count-1); + my $raft_height = max(0, $top_layer->print_z - unscale($self->{object}->size->z)); + $z += $raft_height; + } $self->{preview3D}->canvas->SetCuttingPlane(Z, $z, []); $self->{preview3D}->canvas->Render; }); From 6d063a4413bc6840c173f6b9e706743745cafedf Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Tue, 20 Jun 2017 10:45:29 +0200 Subject: [PATCH 56/64] bugfix: cache raft height for z-indicator in preview to avoid segfault when reslicing --- lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm index 9428c0cf8..b92b93912 100644 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm @@ -94,6 +94,9 @@ sub new { my $plater = $self->{plater} = $params{plater}; my $object = $self->{object} = $self->{plater}->{print}->get_object($self->{obj_idx}); + # store last raft height to correctly draw z-indicator plane during a running background job where the printObject is not valid + $self->{last_raft_height} = 0; + # Initialize 3D toolpaths preview if ($Slic3r::GUI::have_OpenGL) { $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self, $plater->{print}); @@ -187,9 +190,11 @@ sub new { my ($z) = @_; if($z) { # compensate raft height - my $top_layer = $self->{object}->get_layer($self->{object}->layer_count-1); - my $raft_height = max(0, $top_layer->print_z - unscale($self->{object}->size->z)); - $z += $raft_height; + if($self->{object}->layer_count > 0) { # printobject is not valid during toolpath generation but preview still shows last result + my $top_layer = $self->{object}->get_layer($self->{object}->layer_count-1); + $self->{last_raft_height} = max(0, $top_layer->print_z - unscale($self->{object}->size->z)); + } + $z += $self->{last_raft_height}; } $self->{preview3D}->canvas->SetCuttingPlane(Z, $z, []); $self->{preview3D}->canvas->Render; From ad9c4e2565844921065da73159d2c9f7899b06c9 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 21 Jun 2017 10:19:35 +0200 Subject: [PATCH 57/64] Bugfix: legacy return value exception caused problems with vertical surfaces --- t/adaptive_slicing.t | 8 ++++---- xs/src/libslic3r/SlicingAdaptive.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/t/adaptive_slicing.t b/t/adaptive_slicing.t index 1406e4956..64c3769fc 100644 --- a/t/adaptive_slicing.t +++ b/t/adaptive_slicing.t @@ -71,9 +71,9 @@ $adaptive_slicing->prepare(20); subtest 'max layer_height limited by extruder capabilities' => sub { plan tests => 3; - ok (_eq($adaptive_slicing->next_layer_height(1, 50, 0.1, 0.15), 0.15), 'low'); - ok (_eq($adaptive_slicing->next_layer_height(1, 80, 0.1, 0.4), 0.4), 'higher'); - ok (_eq($adaptive_slicing->next_layer_height(1, 80, 0.1, 0.65), 0.65), 'highest'); + ok (_eq($adaptive_slicing->next_layer_height(1, 20, 0.1, 0.15), 0.15), 'low'); + ok (_eq($adaptive_slicing->next_layer_height(1, 20, 0.1, 0.4), 0.4), 'higher'); + ok (_eq($adaptive_slicing->next_layer_height(1, 20, 0.1, 0.65), 0.65), 'highest'); }; subtest 'min layer_height limited by extruder capabilities' => sub { @@ -87,7 +87,7 @@ subtest 'min layer_height limited by extruder capabilities' => sub { subtest 'correct layer_height depending on the facet normals' => sub { plan tests => 3; - ok (_eq($adaptive_slicing->next_layer_height(1, 90, 0.1, 0.5), 0.5), 'limit'); + ok (_eq($adaptive_slicing->next_layer_height(1, 10, 0.1, 0.5), 0.5), 'limit'); ok (_eq($adaptive_slicing->next_layer_height(4, 80, 0.1, 0.5), 0.1546), '45deg facet, quality_value: 0.2'); ok (_eq($adaptive_slicing->next_layer_height(4, 50, 0.1, 0.5), 0.3352), '45deg facet, quality_value: 0.5'); }; diff --git a/xs/src/libslic3r/SlicingAdaptive.cpp b/xs/src/libslic3r/SlicingAdaptive.cpp index d0ceae95d..7cd25a0f4 100644 --- a/xs/src/libslic3r/SlicingAdaptive.cpp +++ b/xs/src/libslic3r/SlicingAdaptive.cpp @@ -176,7 +176,7 @@ float SlicingAdaptive::_layer_height_from_facet(int ordered_id, float scaled_qua { float normal_z = std::abs(m_face_normal_z[ordered_id]); float height = scaled_quality_factor/(SURFACE_CONST + normal_z/2); - return (normal_z == 0.f) ? 9999.f : height; + return height; } }; // namespace Slic3r From febe07d8e83a97e8de5abbd64a546ea68b905f21 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 21 Jun 2017 15:14:33 +0200 Subject: [PATCH 58/64] bugfix: better cache for raft height --- lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm index b92b93912..52a0b4d5f 100644 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm @@ -190,10 +190,6 @@ sub new { my ($z) = @_; if($z) { # compensate raft height - if($self->{object}->layer_count > 0) { # printobject is not valid during toolpath generation but preview still shows last result - my $top_layer = $self->{object}->get_layer($self->{object}->layer_count-1); - $self->{last_raft_height} = max(0, $top_layer->print_z - unscale($self->{object}->size->z)); - } $z += $self->{last_raft_height}; } $self->{preview3D}->canvas->SetCuttingPlane(Z, $z, []); @@ -210,8 +206,9 @@ sub reload_preview { $self->{preview3D}->reload_print($self->{obj_idx}); my $object = $self->{plater}->{print}->get_object($self->{obj_idx}); if($object->layer_count-1 > 0) { - my $top_layer = $object->get_layer($object->layer_count-1); - $self->{preview3D}->set_z($top_layer->print_z); + my $first_layer = $self->{object}->get_layer(0); + $self->{last_raft_height} = max(0, $first_layer->print_z - $first_layer->height); + $self->{preview3D}->set_z(unscale($self->{object}->size->z)); if(!$self->{preview_zoomed}) { $self->{preview3D}->canvas->set_auto_bed_shape; $self->{preview3D}->canvas->zoom_to_volumes; From a386136c3195e7a58440773ebf3fbbeec5019725 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 21 Jun 2017 15:14:54 +0200 Subject: [PATCH 59/64] removed legacy debugging code --- xs/src/libslic3r/PrintObject.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 5ca3087e1..8ffeddefb 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -664,14 +664,12 @@ std::vector PrintObject::generate_object_layers(coordf_t first_layer_h // we need to thicken last layer coordf_t new_h = result[last_layer] - result[last_layer-1]; new_h = std::min(min_nozzle_diameter, new_h - diff); // add (negativ) diff value - std::cout << new_h << std::endl; result[last_layer] = result[last_layer-1] + new_h; } else { // we need to reduce last layer coordf_t new_h = result[last_layer] - result[last_layer-1]; if(min_nozzle_diameter/2 < new_h) { //prevent generation of a too small layer new_h = std::max(min_nozzle_diameter/2, new_h - diff); // subtract (positive) diff value - std::cout << new_h << std::endl; result[last_layer] = result[last_layer-1] + new_h; } } From 49a5f7e9a4045c772e503c9c152cf61469f62279 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 21 Jun 2017 15:16:02 +0200 Subject: [PATCH 60/64] better (correct) boundary conditions for layer height spline --- xs/src/libslic3r/LayerHeightSpline.cpp | 15 ++++++++++++--- xs/src/libslic3r/LayerHeightSpline.hpp | 2 ++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/xs/src/libslic3r/LayerHeightSpline.cpp b/xs/src/libslic3r/LayerHeightSpline.cpp index d5c13f5e8..5fe16e2f9 100644 --- a/xs/src/libslic3r/LayerHeightSpline.cpp +++ b/xs/src/libslic3r/LayerHeightSpline.cpp @@ -170,9 +170,18 @@ bool LayerHeightSpline::_updateBSpline() bool result = false; //TODO: exception if not enough points? - this->_layer_height_spline.reset(new BSpline(&this->_layers[1], - this->_layers.size()-1, - &this->_layer_heights[1], + // copy layer vectors and duplicate a datapoint at the front / end to achieve correct boundary conditions + this->_spline_layers = this->_layers; + this->_spline_layers[0] = 0; + this->_spline_layers.push_back(this->_spline_layers.back()+1); + + this->_spline_layer_heights = this->_layer_heights; + this->_spline_layer_heights[0] = this->_spline_layer_heights[1]; // override fixed first layer height with first "real" layer + this->_spline_layer_heights.push_back(this->_spline_layer_heights.back()); + + this->_layer_height_spline.reset(new BSpline(&this->_spline_layers[0], + this->_spline_layers.size(), + &this->_spline_layer_heights[0], 0, 1, 0) diff --git a/xs/src/libslic3r/LayerHeightSpline.hpp b/xs/src/libslic3r/LayerHeightSpline.hpp index 8fb554cbb..214f2466c 100644 --- a/xs/src/libslic3r/LayerHeightSpline.hpp +++ b/xs/src/libslic3r/LayerHeightSpline.hpp @@ -33,6 +33,8 @@ class LayerHeightSpline bool _layer_heights_updated; std::vector _layers; std::vector _layer_heights; + std::vector _spline_layers; + std::vector _spline_layer_heights; std::unique_ptr> _layer_height_spline; }; From 546bc0f4d03594fc463df5ac959b60e6fcf2f785 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Wed, 21 Jun 2017 17:18:49 +0200 Subject: [PATCH 61/64] fixed merge problem --- 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 7ecc9923d..bc96f8c15 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -2529,7 +2529,7 @@ sub object_menu { wxTheApp->append_menu_item($menu, "Cut…", 'Open the 3D cutting tool', sub { $self->object_cut_dialog; }, undef, 'package.png'); - $frame->_append_menu_item($menu, "Layer heights…", 'Open the dynamic layer height control', sub { + wxTheApp->append_menu_item($menu, "Layer heights…", 'Open the dynamic layer height control', sub { $self->object_layers_dialog; }, undef, 'cog.png'); $menu->AppendSeparator(); From 718adf211b64f0fa0ece58176400cd28fc17de70 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Thu, 22 Jun 2017 10:03:49 +0200 Subject: [PATCH 62/64] merge-fix: min / max layer height settings not considered --- lib/Slic3r/GUI/PresetEditor.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/PresetEditor.pm b/lib/Slic3r/GUI/PresetEditor.pm index 7ca2fbc63..759a71442 100644 --- a/lib/Slic3r/GUI/PresetEditor.pm +++ b/lib/Slic3r/GUI/PresetEditor.pm @@ -1210,7 +1210,7 @@ sub options { use_firmware_retraction pressure_advance vibration_limit use_volumetric_e start_gcode end_gcode before_layer_gcode layer_gcode toolchange_gcode between_objects_gcode - nozzle_diameter extruder_offset + nozzle_diameter extruder_offset min_layer_height max_layer_height retract_length retract_lift retract_speed retract_restart_extra retract_before_travel retract_layer_change wipe retract_length_toolchange retract_restart_extra_toolchange retract_lift_above retract_lift_below printer_settings_id From b9a4a62a5a40dfa81408fa6423e66d1191bea92c Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Mon, 26 Jun 2017 10:40:31 +0200 Subject: [PATCH 63/64] Add missing variable layer height icon --- var/variable_layer_height.png | Bin 0 -> 209 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 var/variable_layer_height.png diff --git a/var/variable_layer_height.png b/var/variable_layer_height.png new file mode 100644 index 0000000000000000000000000000000000000000..2fbdd6920a32ef87b344a60f88d4e077a647de39 GIT binary patch literal 209 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^zbpD<_bdI{u9mbgZg z1m~xflqVLYGB~E>C#5QQ<|d}62BjvZR2H60wE-$h_H=O!(Kw&{=X`^_fnmU^23D6T z|NkHVz|K?O{7~QE-_Z@zCpI>Iv^P`}`P2Tg^+n4;W0%&Nn`h_oB zXMCTNA-w1R;@9m5&b!P``2Amf(pd!#&g=F^W;hsZV)z*wDqmKyKNDyjgQu&X%Q~lo FCIDq2QD*=E literal 0 HcmV?d00001 From ebe4fe80163c6d91d445a9b8cc8e4751d5bce501 Mon Sep 17 00:00:00 2001 From: Florens Wasserfall Date: Mon, 26 Jun 2017 12:18:39 +0200 Subject: [PATCH 64/64] Display layer height of currently highlighted layer in adaptive preview --- lib/Slic3r/GUI/Plater/SplineControl.pm | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/Plater/SplineControl.pm b/lib/Slic3r/GUI/Plater/SplineControl.pm index a6034c5a0..469842a40 100644 --- a/lib/Slic3r/GUI/Plater/SplineControl.pm +++ b/lib/Slic3r/GUI/Plater/SplineControl.pm @@ -33,6 +33,7 @@ sub new { $self->{scaling_factor_y} = 1; $self->{min_layer_height} = 0.1; $self->{max_layer_height} = 0.4; + $self->{mousover_layer_height} = undef; # display layer height at mousover position $self->{object_height} = 1.0; # initialize values @@ -73,7 +74,10 @@ sub repaint { $dc->SetTextForeground(Wx::Colour->new(0,0,0)); $dc->SetFont(Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL)); $dc->DrawLabel(sprintf('%.4g', $self->{min_layer_height}), Wx::Rect->new(0, $size[1]/2, $size[0], $size[1]/2), wxALIGN_LEFT | wxALIGN_BOTTOM); - $dc->DrawLabel(sprintf('%.4g', $self->{max_layer_height}), Wx::Rect->new(0, $size[1]/2, $size[0], $size[1]/2), wxALIGN_RIGHT | wxALIGN_BOTTOM); + $dc->DrawLabel(sprintf('%.2g', $self->{max_layer_height}), Wx::Rect->new(0, $size[1]/2, $size[0], $size[1]/2), wxALIGN_RIGHT | wxALIGN_BOTTOM); + if($self->{mousover_layer_height}){ + $dc->DrawLabel(sprintf('%4.2fmm', $self->{mousover_layer_height}), Wx::Rect->new(0, 0, $size[0], $size[1]), wxALIGN_RIGHT | wxALIGN_TOP); + } if($self->{is_valid}) { @@ -206,11 +210,17 @@ sub mouse_event { } elsif ($event->Moving) { if($self->{on_z_indicator}) { $self->{on_z_indicator}->($obj_pos[1]); + $self->{mousover_layer_height} = $self->{object}->layer_height_spline->getLayerHeightAt($obj_pos[1]); + $self->Refresh; + $self->Update; } } elsif ($event->Leaving) { if($self->{on_z_indicator} && !$self->{left_drag_start_pos}) { $self->{on_z_indicator}->(undef); } + $self->{mousover_layer_height} = undef; + $self->Refresh; + $self->Update; } }