From beb1baa0960470d63ddea10df321cf6cffd8b615 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 30 Dec 2013 18:28:41 +0100 Subject: [PATCH 01/19] Incomplete work for refactoring regions and flows --- lib/Slic3r/Config.pm | 16 +++ lib/Slic3r/Extruder.pm | 19 +-- lib/Slic3r/Flow.pm | 115 ++++++++-------- lib/Slic3r/GUI/Plater.pm | 1 - lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm | 7 +- lib/Slic3r/GUI/SkeinPanel.pm | 5 +- lib/Slic3r/Layer/Region.pm | 74 ++++------ lib/Slic3r/Model.pm | 3 +- lib/Slic3r/Print.pm | 130 ++++++++++++++---- lib/Slic3r/Print/Object.pm | 8 +- lib/Slic3r/Print/Region.pm | 58 +++++++- slic3r.pl | 5 +- xs/src/PrintConfig.hpp | 4 + 13 files changed, 297 insertions(+), 148 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 0db6e69e1..bd924448b 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -175,6 +175,22 @@ sub setenv { } } +sub equals { + my ($self, $other) = @_; + return @{ $self->diff($other) } == 0; +} + +sub diff { + my ($self, $other) = @_; + + my @diff = (); + foreach my $opt_key (sort @{$self->get_keys}) { + push @diff, $opt_key + if !$other->has($opt_key) || $other->serialize($opt_key) ne $self->serialize($opt_key); + } + return [@diff]; +} + # this method is idempotent by design sub validate { my $self = shift; diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm index 9444f6916..1025c3459 100644 --- a/lib/Slic3r/Extruder.pm +++ b/lib/Slic3r/Extruder.pm @@ -1,6 +1,12 @@ package Slic3r::Extruder; use Moo; +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT_OK = qw(EXTRUDER_ROLE_PERIMETER EXTRUDER_ROLE_INFILL EXTRUDER_ROLE_SUPPORT_MATERIAL + EXTRUDER_ROLE_SUPPORT_MATERIAL_INTERFACE); +our %EXPORT_TAGS = (roles => \@EXPORT_OK); + use Slic3r::Geometry qw(PI scale); use constant OPTIONS => [qw( @@ -14,7 +20,6 @@ has 'id' => (is => 'rw', required => 1); has $_ => (is => 'ro', required => 1) for @{&OPTIONS}; has 'config'=> (is => 'ro', required => 1); -has 'bridge_flow' => (is => 'lazy'); has 'E' => (is => 'rw', default => sub {0} ); has 'absolute_E' => (is => 'rw', default => sub {0} ); has 'retracted' => (is => 'rw', default => sub {0} ); @@ -23,14 +28,10 @@ has 'e_per_mm3' => (is => 'lazy'); has 'retract_speed_mm_min' => (is => 'lazy'); has '_mm3_per_mm_cache' => (is => 'ro', default => sub {{}}); -sub _build_bridge_flow { - my $self = shift; - - return Slic3r::Flow::Bridge->new( - nozzle_diameter => $self->nozzle_diameter, - bridge_flow_ratio => $self->config->bridge_flow_ratio, - ); -} +use constant EXTRUDER_ROLE_PERIMETER => 1; +use constant EXTRUDER_ROLE_INFILL => 2; +use constant EXTRUDER_ROLE_SUPPORT_MATERIAL => 3; +use constant EXTRUDER_ROLE_SUPPORT_MATERIAL_INTERFACE => 4; sub _build_e_per_mm3 { my $self = shift; diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index a02f7b34e..0a9747fa8 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -1,48 +1,73 @@ package Slic3r::Flow; use Moo; -use Slic3r::Geometry qw(PI scale); +require Exporter; +our @ISA = qw(Exporter); +our @EXPORT_OK = qw(FLOW_ROLE_PERIMETER FLOW_ROLE_INFILL FLOW_ROLE_SOLID_INFILL FLOW_ROLE_TOP_SOLID_INFILL + FLOW_ROLE_SUPPORT_MATERIAL FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE); +our %EXPORT_TAGS = (roles => \@EXPORT_OK); -has 'nozzle_diameter' => (is => 'ro', required => 1); -has 'layer_height' => (is => 'ro', required => 1); -has 'role' => (is => 'ro', default => sub { '' }); +use Slic3r::Geometry qw(PI); -has 'width' => (is => 'rwp', builder => 1); -has 'spacing' => (is => 'lazy'); +has 'width' => (is => 'ro'); +has 'spacing' => (is => 'ro'); has 'scaled_width' => (is => 'lazy'); has 'scaled_spacing' => (is => 'lazy'); -sub BUILD { - my $self = shift; +use constant FLOW_ROLE_PERIMETER => 1; +use constant FLOW_ROLE_INFILL => 2; +use constant FLOW_ROLE_SOLID_INFILL => 3; +use constant FLOW_ROLE_TOP_SOLID_INFILL => 4; +use constant FLOW_ROLE_SUPPORT_MATERIAL => 5; +use constant FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE => 6; + +sub BUILDARGS { + my ($self, %args) = @_; - if ($self->width =~ /^(\d+(?:\.\d+)?)%$/) { - $self->_set_width($self->layer_height * $1 / 100); + # the constructor can take two sets of arguments: + # - width (only absolute value), spacing + # - width (abs/%/0), role, nozzle_diameter, layer_height, bridge_flow_ratio + # (if bridge_flow_ratio == 0, we return a non-bridge flow) + + if (exists $args{role}) { + if ($args{width} eq '0') { + $args{width} = $self->_width(@args{qw(role nozzle_diameter layer_height bridge_flow_ratio)}); + } elsif ($args{width} =~ /^(\d+(?:\.\d+)?)%$/) { + $args{width} = $args{layer_height} * $1 / 100; + } + $args{spacing} = $self->_spacing(@args{qw(width nozzle_diameter layer_height bridge_flow_ratio)}); + %args = @args{qw(width spacing)}; } - $self->_set_width($self->_build_width) if $self->width == 0; # auto + + return {%args}; } -sub _build_width { - my $self = shift; +sub _width { + my ($self, $role, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_; + + if ($bridge_flow_ratio > 0) { + return sqrt($bridge_flow_ratio * ($nozzle_diameter**2)); + } # here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate - my $volume = ($self->nozzle_diameter**2) * PI/4; - my $shape_threshold = $self->nozzle_diameter * $self->layer_height + ($self->layer_height**2) * PI/4; + my $volume = ($nozzle_diameter**2) * PI/4; + my $shape_threshold = $nozzle_diameter * $layer_height + ($layer_height**2) * PI/4; my $width; if ($volume >= $shape_threshold) { # rectangle with semicircles at the ends - $width = (($self->nozzle_diameter**2) * PI + ($self->layer_height**2) * (4 - PI)) / (4 * $self->layer_height); + $width = (($nozzle_diameter**2) * PI + ($layer_height**2) * (4 - PI)) / (4 * $layer_height); } else { # rectangle with squished semicircles at the ends - $width = $self->nozzle_diameter * ($self->nozzle_diameter/$self->layer_height - 4/PI + 1); + $width = $nozzle_diameter * ($nozzle_diameter/$layer_height - 4/PI + 1); } - my $min = $self->nozzle_diameter * 1.05; + my $min = $nozzle_diameter * 1.05; my $max; - if ($self->role eq 'perimeter' || $self->role eq 'support_material') { - $min = $max = $self->nozzle_diameter; - } elsif ($self->role ne 'infill') { + if ($role == FLOW_ROLE_PERIMETER || $role == FLOW_ROLE_SUPPORT_MATERIAL) { + $min = $max = $nozzle_diameter; + } elsif ($role != FLOW_ROLE_INFILL) { # do not limit width for sparse infill so that we use full native flow for it - $max = $self->nozzle_diameter * 1.7; + $max = $nozzle_diameter * 1.7; } $width = $max if defined($max) && $width > $max; $width = $min if $width < $min; @@ -50,59 +75,41 @@ sub _build_width { return $width; } -sub _build_spacing { - my $self = shift; +sub _spacing { + my ($self, $width, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_; + + if ($bridge_flow_ratio > 0) { + return $width + 0.05; + } my $min_flow_spacing; - if ($self->width >= ($self->nozzle_diameter + $self->layer_height)) { + if ($width >= ($nozzle_diameter + $layer_height)) { # rectangle with semicircles at the ends - $min_flow_spacing = $self->width - $self->layer_height * (1 - PI/4); + $min_flow_spacing = $width - $layer_height * (1 - PI/4); } else { # rectangle with shrunk semicircles at the ends - $min_flow_spacing = $self->nozzle_diameter * (1 - PI/4) + $self->width * PI/4; + $min_flow_spacing = $nozzle_diameter * (1 - PI/4) + $width * PI/4; } - return $self->width - &Slic3r::OVERLAP_FACTOR * ($self->width - $min_flow_spacing); + return $width - &Slic3r::OVERLAP_FACTOR * ($width - $min_flow_spacing); } sub clone { my $self = shift; return (ref $self)->new( - nozzle_diameter => $self->nozzle_diameter, - layer_height => $self->layer_height, - @_, + width => $self->width, + spacing => $self->spacing, ); } sub _build_scaled_width { my $self = shift; - return scale $self->width; + return Slic3r::Geometry::scale($self->width); } sub _build_scaled_spacing { my $self = shift; - return scale $self->spacing; -} - - -package Slic3r::Flow::Bridge; -use Moo; -extends 'Slic3r::Flow'; - -# layer_height is not required in this case -has '+layer_height' => (is => 'ro', required => 0); -has 'bridge_flow_ratio' => (is => 'ro', required => 1); - -use Slic3r::Geometry qw(PI); - -sub _build_width { - my $self = shift; - return sqrt($self->bridge_flow_ratio * ($self->nozzle_diameter**2)); -} - -sub _build_spacing { - my $self = shift; - return $self->width + 0.05; + return Slic3r::Geometry::scale($self->spacing); } 1; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index ed85250a2..e0b0fcf4a 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -638,7 +638,6 @@ sub split_object { input_file => $current_model_object->input_file, config => $current_model_object->config->clone, layer_height_ranges => $current_model_object->layer_height_ranges, # TODO: clone this - material_mapping => $current_model_object->material_mapping, # TODO: clone this ); $model_object->add_volume( mesh => $mesh, diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm index 30a5dadeb..e681d7238 100644 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm @@ -324,7 +324,12 @@ sub Closing { my $self = shift; # save mappings into the plater object - $self->model_object->material_mapping($self->{mapping}); + foreach my $volume (@{$self->model_object}) { + if (defined $volume->material_id) { + my $config = $self->model_object->model->materials->{ $volume->material_id }->config; + $config->set('extruder', $self->{mapping}{ $volume->material_id }); + } + } } 1; diff --git a/lib/Slic3r/GUI/SkeinPanel.pm b/lib/Slic3r/GUI/SkeinPanel.pm index ee11147bd..7750794c0 100644 --- a/lib/Slic3r/GUI/SkeinPanel.pm +++ b/lib/Slic3r/GUI/SkeinPanel.pm @@ -146,7 +146,10 @@ sub quick_slice { } $model->center_instances_around_point($config->print_center); - $print->add_model_object($_) for @{ $model->objects }; + foreach my $model_object (@{$model->objects}) { + $print->auto_assign_extruders($model_object); + $print->add_model_object($model_object); + } $print->validate; # select output file diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 53f40a659..e065f561e 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -3,6 +3,7 @@ use Moo; use List::Util qw(sum first); use Slic3r::ExtrusionPath ':roles'; +use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(PI A B scale unscale chained_path points_coincide); use Slic3r::Geometry::Clipper qw(union_ex diff_ex intersection_ex offset offset2 offset2_ex union_pt diff intersection @@ -17,10 +18,6 @@ has 'layer' => ( handles => [qw(id slice_z print_z height flow config)], ); has 'region' => (is => 'ro', required => 1, handles => [qw(extruders)]); -has 'perimeter_flow' => (is => 'rw'); -has 'infill_flow' => (is => 'rw'); -has 'solid_infill_flow' => (is => 'rw'); -has 'top_infill_flow' => (is => 'rw'); has 'infill_area_threshold' => (is => 'lazy'); has 'overhang_width' => (is => 'lazy'); @@ -40,43 +37,26 @@ has 'perimeters' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collect # ordered collection of extrusion paths to fill surfaces has 'fills' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new }); -sub BUILD { - my $self = shift; - $self->_update_flows; -} - -sub _trigger_layer { - my $self = shift; - $self->_update_flows; -} - -sub _update_flows { - my $self = shift; - return if !$self->region; - - if ($self->id == 0) { - for (qw(perimeter infill solid_infill top_infill)) { - my $method = "${_}_flow"; - $self->$method - ($self->region->first_layer_flows->{$_} || $self->region->flows->{$_}); - } - } else { - $self->perimeter_flow($self->region->flows->{perimeter}); - $self->infill_flow($self->region->flows->{infill}); - $self->solid_infill_flow($self->region->flows->{solid_infill}); - $self->top_infill_flow($self->region->flows->{top_infill}); - } -} - sub _build_overhang_width { my $self = shift; - my $threshold_rad = PI/2 - atan2($self->perimeter_flow->width / $self->height / 2, 1); + my $threshold_rad = PI/2 - atan2($self->flow(FLOW_ROLE_PERIMETER)->width / $self->height / 2, 1); return scale($self->height * ((cos $threshold_rad) / (sin $threshold_rad))); } sub _build_infill_area_threshold { my $self = shift; - return $self->solid_infill_flow->scaled_spacing ** 2; + return $self->flow(FLOW_ROLE_SOLID_INFILL)->scaled_spacing ** 2; +} + +sub flow { + my ($self, $role, $bridge, $width) = @_; + return $self->region->flow( + $role, + $self->layer->height, + $bridge // 0, + $self->layer->id == 0, + $width, + ); } # build polylines from lines @@ -145,10 +125,11 @@ sub _merge_loops { sub make_perimeters { my $self = shift; - my $pwidth = $self->perimeter_flow->scaled_width; - my $pspacing = $self->perimeter_flow->scaled_spacing; - my $ispacing = $self->solid_infill_flow->scaled_spacing; - my $gap_area_threshold = $self->perimeter_flow->scaled_width ** 2; + my $perimeter_flow = $self->flow(FLOW_ROLE_PERIMETER); + my $pwidth = $perimeter_flow->scaled_width; + my $pspacing = $perimeter_flow->scaled_spacing; + my $ispacing = $self->flow(FLOW_ROLE_SOLID_INFILL)->scaled_spacing; + my $gap_area_threshold = $pwidth ** 2; $self->perimeters->clear; $self->fill_surfaces->clear; @@ -284,7 +265,7 @@ sub make_perimeters { push @loops, Slic3r::ExtrusionLoop->new( polygon => $polygon, role => $role, - flow_spacing => $self->perimeter_flow->spacing, + flow_spacing => $perimeter_flow->spacing, ); } return @loops; @@ -311,7 +292,7 @@ sub make_perimeters { next if $p->length <= $pspacing * 2; my %params = ( role => EXTR_ROLE_EXTERNAL_PERIMETER, - flow_spacing => $self->perimeter_flow->spacing, + flow_spacing => $perimeter_flow->spacing, ); push @paths, $p->isa('Slic3r::Polygon') ? Slic3r::ExtrusionLoop->new(polygon => $p, %params) @@ -345,10 +326,10 @@ sub _fill_gaps { # we could try with 1.5*$w for example, but that doesn't work well for zigzag fill # because it tends to create very sparse points along the gap when the infill direction # is not parallel to the gap (1.5*$w thus may only work well with a straight line) - my $w = $self->perimeter_flow->width; + my $w = $self->flow(FLOW_ROLE_PERIMETER)->width; my @widths = ($w, 0.4 * $w); # worth trying 0.2 too? foreach my $width (@widths) { - my $flow = $self->perimeter_flow->clone(width => $width); + my $flow = $self->flow(FLOW_ROLE_PERIMETER, 0, $width); # extract the gaps having this width my @this_width = map @{$_->offset_ex(+0.5*$flow->scaled_width)}, @@ -499,7 +480,10 @@ sub process_external_surfaces { sub _detect_bridge_direction { my ($self, $expolygon, $lower_layer) = @_; - my $grown = $expolygon->offset_ex(+$self->perimeter_flow->scaled_width); + my $perimeter_flow = $self->flow(FLOW_ROLE_PERIMETER); + my $infill_flow = $self->flow(FLOW_ROLE_INFILL); + + my $grown = $expolygon->offset_ex(+$perimeter_flow->scaled_width); my @lower = @{$lower_layer->slices}; # expolygons # detect what edges lie on lower slices @@ -554,7 +538,7 @@ sub _detect_bridge_direction { } } elsif (@edges) { # inset the bridge expolygon; we'll use this one to clip our test lines - my $inset = $expolygon->offset_ex($self->infill_flow->scaled_width); + my $inset = $expolygon->offset_ex($infill_flow->scaled_width); # detect anchors as intersection between our bridge expolygon and the lower slices my $anchors = intersection_ex( @@ -568,7 +552,7 @@ sub _detect_bridge_direction { # endpoints within anchors my %directions = (); # angle => score my $angle_increment = PI/36; # 5° - my $line_increment = $self->infill_flow->scaled_width; + my $line_increment = $infill_flow->scaled_width; for (my $angle = 0; $angle <= PI; $angle += $angle_increment) { # rotate everything - the center point doesn't matter $_->rotate($angle, [0,0]) for @$inset, @$anchors; diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index d9b5ef463..401deb47e 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -44,7 +44,6 @@ sub add_object { input_file => $object->input_file, config => $object->config, layer_height_ranges => $object->layer_height_ranges, # TODO: clone! - material_mapping => $object->material_mapping, # TODO: clone! ); foreach my $volume (@{$object->volumes}) { @@ -324,6 +323,7 @@ use Moo; has 'model' => (is => 'ro', weak_ref => 1, required => 1); has 'attributes' => (is => 'rw', default => sub { {} }); +has 'config' => (is => 'rw', default => sub { Slic3r::Config->new }); package Slic3r::Model::Object; use Moo; @@ -338,7 +338,6 @@ has 'volumes' => (is => 'ro', default => sub { [] }); has 'instances' => (is => 'rw'); has 'config' => (is => 'rw', default => sub { Slic3r::Config->new }); has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ] -has 'material_mapping' => (is => 'rw', default => sub { {} }); # { material_id => region_idx } has '_bounding_box' => (is => 'rw'); sub add_volume { diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 59d90af7d..ef32677ea 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -17,8 +17,6 @@ has 'objects' => (is => 'rw', default => sub {[]}); has 'status_cb' => (is => 'rw'); has 'extruders' => (is => 'rw', default => sub {[]}); has 'regions' => (is => 'rw', default => sub {[]}); -has 'support_material_flow' => (is => 'rw'); -has 'first_layer_support_material_flow' => (is => 'rw'); has 'has_support_material' => (is => 'lazy'); has '_state' => (is => 'ro', default => sub { Slic3r::Print::State->new }); @@ -88,30 +86,37 @@ sub add_model_object { my $self = shift; my ($object, $obj_idx) = @_; - # read the material mapping provided by the model object, if any - my %matmap = %{ $object->material_mapping || {} }; - $_-- for values %matmap; # extruders in the mapping are 1-indexed but we want 0-indexed - my %volumes = (); # region_id => [ volume_id, ... ] foreach my $volume_id (0..$#{$object->volumes}) { my $volume = $object->volumes->[$volume_id]; - # determine what region should this volume be mapped to - my $region_id; + # get the config applied to this volume + my $config; if (defined $volume->material_id) { - if (!exists $matmap{ $volume->material_id }) { - # there's no mapping between this material and a region - $matmap{ $volume->material_id } = scalar(@{ $self->regions }); - } - $region_id = $matmap{ $volume->material_id }; + my $config = $object->model->materials->{ $volume->material_id }->config; } else { - $region_id = 0; + $config = Slic3r::Config->new; } + + # find an existing print region with the same config + my $region_id; + foreach my $i (0..$#{$self->regions}) { + my $region = $self->regions->[$i]; + if ($config->equals($region->config)) { + $region_id = $i; + last; + } + } + + # if no region exists with the same config, create a new one + if (!defined $region_id) { + push @{$self->regions}, Slic3r::Print::Region->new(config => $config->clone); + $region_id = $#{$self->regions}; + } + + # assign volume to region $volumes{$region_id} //= []; push @{ $volumes{$region_id} }, $volume_id; - - # instantiate region if it does not exist - $self->regions->[$region_id] //= Slic3r::Print::Region->new; } # initialize print object @@ -632,7 +637,7 @@ sub make_skirt { my @extruded_length = (); # for each extruder # TODO: use each extruder's own flow - my $spacing = $self->objects->[0]->layers->[0]->regions->[0]->perimeter_flow->spacing; + my $spacing = $self->flow(FLOW_ROLE_SUPPORT_MATERIAL)->spacing; my $first_layer_height = $self->config->get_value('first_layer_height'); my @extruders_e_per_mm = (); @@ -673,7 +678,7 @@ sub make_brim { $self->brim->clear; # method must be idempotent - my $flow = $self->objects->[0]->layers->[0]->regions->[0]->perimeter_flow; + my $flow = $self->flow(FLOW_ROLE_SUPPORT_MATERIAL); my $grow_distance = $flow->scaled_width / 2; my @islands = (); # array of polygons @@ -747,15 +752,24 @@ sub write_gcode { for (qw(nozzle_diameter filament_diameter extrusion_multiplier)) { printf $fh "; %s = %s\n", $_, $self->config->$_->[0]; } - printf $fh "; perimeters extrusion width = %.2fmm\n", $self->regions->[0]->flows->{perimeter}->width; - printf $fh "; infill extrusion width = %.2fmm\n", $self->regions->[0]->flows->{infill}->width; - printf $fh "; solid infill extrusion width = %.2fmm\n", $self->regions->[0]->flows->{solid_infill}->width; - printf $fh "; top infill extrusion width = %.2fmm\n", $self->regions->[0]->flows->{top_infill}->width; - printf $fh "; support material extrusion width = %.2fmm\n", $self->support_material_flow->width - if $self->support_material_flow; - printf $fh "; first layer extrusion width = %.2fmm\n", $self->regions->[0]->first_layer_flows->{perimeter}->width - if $self->regions->[0]->first_layer_flows->{perimeter}; - print $fh "\n"; + + for my $region_id (0..$#{$self->regions}) { + printf $fh "; perimeters extrusion width = %.2fmm\n", + $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER)->width; + printf $fh "; infill extrusion width = %.2fmm\n", + $self->regions->[$region_id]->flow(FLOW_ROLE_INFILL)->width; + printf $fh "; solid infill extrusion width = %.2fmm\n", + $self->regions->[$region_id]->flow(FLOW_ROLE_SOLID_INFILL)->width; + printf $fh "; top infill extrusion width = %.2fmm\n", + $self->regions->[$region_id]->flow(FLOW_ROLE_TOP_SOLID_INFILL)->width; + printf $fh "; support material extrusion width = %.2fmm\n", + $self->flow(FLOW_ROLE_SUPPORT_MATERIAL)->width + if $self->support_material_flow; + printf $fh "; first layer extrusion width = %.2fmm\n", + $self->flow(FLOW_ROLE_SUPPORT_MATERIAL, 0, 1)->width + if $self->regions->[0]->first_layer_flows->{perimeter}; + print $fh "\n"; + } # set up our extruder object my $gcodegen = Slic3r::GCode->new( @@ -1012,4 +1026,64 @@ sub invalidate_step { keys %Slic3r::Print::State::prereqs; } +# This method assigns extruders to the volumes having a material +# but not having extruders set in the material config. +sub auto_assign_extruders { + my ($self, $model_object) = @_; + + my $extruders = scalar @{ $self->config->nozzle_diameter }; + foreach my $i (0..$#{$model_object->volumes}) { + my $volume = $model_object->volumes->[$i]; + if (defined $volume->material_id) { + my $material = $model_object->model->materials->{ $volume->material_id }; + my $config = $material->config; + $config->set_ifndef('perimeters_extruder', $i); + $config->set_ifndef('infill_extruder', $i); + $config->set_ifndef('support_material_extruder', $i); + $config->set_ifndef('support_material_interface_extruder', $i); + } + } +} + +sub flow { + my ($self, $role, $layer_height, $bridge, $first_layer, $width) = @_; + + $bridge //= 0; + $first_layer //= 0; + + # use the supplied custom width, if any + my $config_width = $width; + if (!defined $config_width) { + # get extrusion width from configuration + # (might be an absolute value, or a percent value, or zero for auto) + if ($first_layer) { + $config_width = $self->config->first_layer_extrusion_width; + } elsif ($role == FLOW_ROLE_SUPPORT_MATERIAL || $role == FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE) { + $config_width = $self->config->support_material_extrusion_width; + } else { + die "Unknown role $role"; + } + } + + # get the configured nozzle_diameter for the extruder associated + # to the flow role requested + my $extruder; # 1-based + if ($role == FLOW_ROLE_SUPPORT_MATERIAL) { + $config_width = $self->config->support_material_extruder; + } elsif ($role == FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE) { + $config_width = $self->config->support_material_interface_extruder; + } else { + die "Unknown role $role"; + } + my $nozzle_diameter = $self->config->nozzle_diameter->[$extruder-1]; + + return Slic3r::Flow->new( + width => $config_width, + role => $role, + nozzle_diameter => $nozzle_diameter, + layer_height => $layer_height, + bridge_flow_ratio => ($bridge ? $self->config->bridge_flow_ratio : 0), + ); +} + 1; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index d7d8f00db..f1edc98f8 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -861,9 +861,11 @@ sub generate_support_material { return unless ($self->config->support_material || $self->config->raft_layers > 0) && $self->layer_count >= 2; - Slic3r::Print::SupportMaterial - ->new(config => $self->config, flow => $self->print->support_material_flow) - ->generate($self); + my $s = Slic3r::Print::SupportMaterial->new( + config => $self->config, + flow => $self->print->support_material_flow, + ); + $s->generate($self); } sub _simplify_slices { diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm index 253fde2ba..0d3b844f4 100644 --- a/lib/Slic3r/Print/Region.pm +++ b/lib/Slic3r/Print/Region.pm @@ -1,8 +1,60 @@ package Slic3r::Print::Region; use Moo; -has 'extruders' => (is => 'rw', default => sub { {} }); # by role -has 'flows' => (is => 'rw', default => sub { {} }); # by role -has 'first_layer_flows' => (is => 'rw', default => sub { {} }); # by role +use Slic3r::Extruder ':roles'; +use Slic3r::Flow ':roles'; + +# A Print::Region object represents a group of volumes to print +# sharing the same config (including the same assigned extruder(s)) + +has 'print' => (is => 'ro', required => 1, weak_ref => 1); +has 'config' => (is => 'ro', required => 1); + +sub flow { + my ($self, $role, $layer_height, $bridge, $first_layer, $width) = @_; + + $bridge //= 0; + $first_layer //= 0; + + # use the supplied custom width, if any + my $config_width = $width; + if (!defined $config_width) { + # get extrusion width from configuration + # (might be an absolute value, or a percent value, or zero for auto) + if ($first_layer) { + $config_width = $self->config->first_layer_extrusion_width; + } elsif ($role == FLOW_ROLE_PERIMETER) { + $config_width = $self->config->perimeter_extrusion_width; + } elsif ($role == FLOW_ROLE_INFILL) { + $config_width = $self->config->infill_extrusion_width; + } elsif ($role == FLOW_ROLE_SOLID_INFILL) { + $config_width = $self->config->solid_infill_extrusion_width; + } elsif ($role == FLOW_ROLE_TOP_SOLID_INFILL) { + $config_width = $self->config->top_infill_extrusion_width; + } else { + die "Unknown role $role"; + } + } + + # get the configured nozzle_diameter for the extruder associated + # to the flow role requested + my $extruder; # 1-based + if ($role == FLOW_ROLE_PERIMETER) { + $config_width = $self->config->perimeter_extruder; + } elsif ($role == FLOW_ROLE_INFILL || $role == FLOW_ROLE_SOLID_INFILL || $role == FLOW_ROLE_TOP_SOLID_INFILL) { + $config_width = $self->config->infill_extruder; + } else { + die "Unknown role $role"; + } + my $nozzle_diameter = $self->print->config->nozzle_diameter->[$extruder-1]; + + return Slic3r::Flow->new( + width => $config_width, + role => $role, + nozzle_diameter => $nozzle_diameter, + layer_height => $layer_height, + bridge_flow_ratio => ($bridge ? $self->config->bridge_flow_ratio : 0), + ); +} 1; diff --git a/slic3r.pl b/slic3r.pl index c55bd19a6..0be1219d0 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -159,7 +159,10 @@ if (@ARGV) { # slicing from command line printf "=> %s\n", $message; }, ); - $print->add_model_object($_) for @{$model->objects}; + foreach my $model_object (@{$model->objects}) { + $print->auto_assign_extruders($model_object); + $print->add_model_object($model_object); + } undef $model; # free memory $print->validate; if ($opt{export_svg}) { diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index 4dd732150..a6bd71ec7 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -761,6 +761,7 @@ class PrintConfig : public StaticConfig Options["infill_extruder"].label = "Infill extruder"; Options["infill_extruder"].tooltip = "The extruder to use when printing infill."; Options["infill_extruder"].cli = "infill-extruder=i"; + Options["infill_extruder"].shortcut.push_back("extruder"); Options["infill_extrusion_width"].type = coFloatOrPercent; Options["infill_extrusion_width"].label = "Infill"; @@ -878,6 +879,7 @@ class PrintConfig : public StaticConfig Options["perimeter_extruder"].tooltip = "The extruder to use when printing perimeters."; Options["perimeter_extruder"].cli = "perimeter-extruder=i"; Options["perimeter_extruder"].aliases.push_back("perimeters_extruder"); + Options["perimeter_extruder"].shortcut.push_back("extruder"); Options["perimeter_extrusion_width"].type = coFloatOrPercent; Options["perimeter_extrusion_width"].label = "Perimeters"; @@ -1139,6 +1141,7 @@ class PrintConfig : public StaticConfig Options["support_material_extruder"].label = "Support material extruder"; Options["support_material_extruder"].tooltip = "The extruder to use when printing support material. This affects brim and raft too."; Options["support_material_extruder"].cli = "support-material-extruder=i"; + Options["support_material_extruder"].shortcut.push_back("extruder"); Options["support_material_extrusion_width"].type = coFloatOrPercent; Options["support_material_extrusion_width"].label = "Support material"; @@ -1150,6 +1153,7 @@ class PrintConfig : public StaticConfig Options["support_material_interface_extruder"].label = "Support material interface extruder"; Options["support_material_interface_extruder"].tooltip = "The extruder to use when printing support material interface. This affects raft too."; Options["support_material_interface_extruder"].cli = "support-material-interface-extruder=i"; + Options["support_material_interface_extruder"].shortcut.push_back("extruder"); Options["support_material_interface_layers"].type = coInt; Options["support_material_interface_layers"].label = "Interface layers"; From 83326845dd9d6d7fbceb5c03a487e4c4f77f5e83 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 31 Dec 2013 14:33:03 +0100 Subject: [PATCH 02/19] More incomplete work --- lib/Slic3r/Layer/Region.pm | 3 +- lib/Slic3r/Print.pm | 141 +++++++++++-------------------------- lib/Slic3r/Print/Object.pm | 27 ++++++- lib/Slic3r/Print/Region.pm | 2 +- xs/src/Config.cpp | 7 ++ xs/src/PrintConfig.hpp | 12 ++-- xs/t/15_config.t | 4 +- 7 files changed, 85 insertions(+), 111 deletions(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index e065f561e..1f27d143c 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -14,8 +14,7 @@ has 'layer' => ( is => 'ro', weak_ref => 1, required => 1, - trigger => 1, - handles => [qw(id slice_z print_z height flow config)], + handles => [qw(id slice_z print_z height config)], ); has 'region' => (is => 'ro', required => 1, handles => [qw(extruders)]); has 'infill_area_threshold' => (is => 'lazy'); diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index ef32677ea..98060342b 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -5,6 +5,7 @@ use File::Basename qw(basename fileparse); use File::Spec; use List::Util qw(min max first); use Slic3r::ExtrusionPath ':roles'; +use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 MIN MAX PI scale unscale move_points chained_path convex_hull); use Slic3r::Geometry::Clipper qw(diff_ex union_ex union_pt intersection_ex intersection offset @@ -96,6 +97,7 @@ sub add_model_object { my $config = $object->model->materials->{ $volume->material_id }->config; } else { $config = Slic3r::Config->new; + $config->set('extruder', 0); } # find an existing print region with the same config @@ -110,7 +112,10 @@ sub add_model_object { # if no region exists with the same config, create a new one if (!defined $region_id) { - push @{$self->regions}, Slic3r::Print::Region->new(config => $config->clone); + push @{$self->regions}, Slic3r::Print::Region->new( + print => $self, + config => $config->clone, + ); $region_id = $#{$self->regions}; } @@ -233,15 +238,19 @@ sub validate { sub init_extruders { my $self = shift; - # map regions to extruders (ghetto mapping for now) - my %extruder_mapping = map { $_ => $_ } 0..$#{$self->regions}; - # initialize all extruder(s) we need - my @used_extruders = ( - 0, - (map $self->config->get("${_}_extruder")-1, qw(perimeter infill support_material support_material_interface)), - (values %extruder_mapping), - ); + my @used_extruders = (); + foreach my $region (@{$self->regions}) { + push @used_extruders, + map $region->config->get("${_}_extruder")-1, + qw(perimeter infill); + } + foreach my $object (@{$self->objects}) { + push @used_extruders, + map $object->config->get("${_}_extruder")-1, + qw(support_material support_material_interface); + } + for my $extruder_id (keys %{{ map {$_ => 1} @used_extruders }}) { $self->extruders->[$extruder_id] = Slic3r::Extruder->new( config => $self->config, @@ -251,51 +260,8 @@ sub init_extruders { ); } - # calculate regions' flows - for my $region_id (0 .. $#{$self->regions}) { - my $region = $self->regions->[$region_id]; - - # per-role extruders and flows - for (qw(perimeter infill solid_infill top_infill)) { - my $extruder_name = $_; - $extruder_name =~ s/^(?:solid|top)_//; - $region->extruders->{$_} = ($self->regions_count > 1) - ? $self->extruders->[$extruder_mapping{$region_id}] - : $self->extruders->[$self->config->get("${extruder_name}_extruder")-1]; - $region->flows->{$_} = $region->extruders->{$_}->make_flow( - layer_height => $self->config->layer_height, - width => $self->config->get("${_}_extrusion_width") || $self->config->extrusion_width, - role => $_, - ); - $region->first_layer_flows->{$_} = $region->extruders->{$_}->make_flow( - layer_height => $self->config->get_value('first_layer_height'), - width => $self->config->first_layer_extrusion_width, - role => $_, - ) if $self->config->first_layer_extrusion_width; - } - } - - # calculate support material flow - # Note: we should calculate a different flow for support material interface - # TODO: support material layers have their own variable layer heights, so we - # probably need a DynamicFlow object that calculates flow on the fly - # (or the Flow object must support a mutable layer_height) - if ($self->has_support_material) { - my $extruder = $self->extruders->[$self->config->support_material_extruder-1]; - $self->support_material_flow($extruder->make_flow( - layer_height => $self->config->layer_height, # WRONG! - width => $self->config->support_material_extrusion_width || $self->config->extrusion_width, - role => 'support_material', - )); - $self->first_layer_support_material_flow($extruder->make_flow( - layer_height => $self->config->get_value('first_layer_height'), - width => $self->config->first_layer_extrusion_width, - role => 'support_material', - )); - } - # enforce tall skirt if using ooze_prevention - # NOTE: this is not idempotent (i.e. switching ooze_prevention off will not revert skirt settings) + # FIXME: this is not idempotent (i.e. switching ooze_prevention off will not revert skirt settings) if ($self->config->ooze_prevention && @{$self->extruders} > 1) { $self->config->set('skirt_height', -1); $self->config->set('skirts', 1) if $self->config->skirts == 0; @@ -636,8 +602,17 @@ sub make_skirt { my @extruded_length = (); # for each extruder + # skirt may be printed on several layers, having distinct layer heights, + # but loops must be aligned so can't vary width/spacing # TODO: use each extruder's own flow - my $spacing = $self->flow(FLOW_ROLE_SUPPORT_MATERIAL)->spacing; + my $flow = Slic3r::Flow->new( + width => ($self->config->first_layer_extrusion_width || $self->config->perimeter_extrusion_width), + role => FLOW_ROLE_PERIMETER, + nozzle_diameter => $self->config->nozzle_diameter->[0], + layer_height => $self->config->get_abs_value('first_layer_height'), + bridge_flow_ratio => 0, + ); + my $spacing = $flow->spacing; my $first_layer_height = $self->config->get_value('first_layer_height'); my @extruders_e_per_mm = (); @@ -678,7 +653,14 @@ sub make_brim { $self->brim->clear; # method must be idempotent - my $flow = $self->flow(FLOW_ROLE_SUPPORT_MATERIAL); + # brim is only printed on first layer and uses support material extruder + my $flow = Slic3r::Flow->new( + width => ($self->config->first_layer_extrusion_width || $self->config->perimeter_extrusion_width), + role => FLOW_ROLE_PERIMETER, + nozzle_diameter => $self->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ], + layer_height => $self->config->get_abs_value('first_layer_height'), + bridge_flow_ratio => 0, + ); my $grow_distance = $flow->scaled_width / 2; my @islands = (); # array of polygons @@ -763,11 +745,11 @@ sub write_gcode { printf $fh "; top infill extrusion width = %.2fmm\n", $self->regions->[$region_id]->flow(FLOW_ROLE_TOP_SOLID_INFILL)->width; printf $fh "; support material extrusion width = %.2fmm\n", - $self->flow(FLOW_ROLE_SUPPORT_MATERIAL)->width - if $self->support_material_flow; + $self->objects->[0]->support_material_flow->width + if $self->has_support_material; printf $fh "; first layer extrusion width = %.2fmm\n", - $self->flow(FLOW_ROLE_SUPPORT_MATERIAL, 0, 1)->width - if $self->regions->[0]->first_layer_flows->{perimeter}; + $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, 0, 1)->width + if ($self->regions->[$region_id]->config->first_layer_extrusion_width != 0); print $fh "\n"; } @@ -1045,45 +1027,4 @@ sub auto_assign_extruders { } } -sub flow { - my ($self, $role, $layer_height, $bridge, $first_layer, $width) = @_; - - $bridge //= 0; - $first_layer //= 0; - - # use the supplied custom width, if any - my $config_width = $width; - if (!defined $config_width) { - # get extrusion width from configuration - # (might be an absolute value, or a percent value, or zero for auto) - if ($first_layer) { - $config_width = $self->config->first_layer_extrusion_width; - } elsif ($role == FLOW_ROLE_SUPPORT_MATERIAL || $role == FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE) { - $config_width = $self->config->support_material_extrusion_width; - } else { - die "Unknown role $role"; - } - } - - # get the configured nozzle_diameter for the extruder associated - # to the flow role requested - my $extruder; # 1-based - if ($role == FLOW_ROLE_SUPPORT_MATERIAL) { - $config_width = $self->config->support_material_extruder; - } elsif ($role == FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE) { - $config_width = $self->config->support_material_interface_extruder; - } else { - die "Unknown role $role"; - } - my $nozzle_diameter = $self->config->nozzle_diameter->[$extruder-1]; - - return Slic3r::Flow->new( - width => $config_width, - role => $role, - nozzle_diameter => $nozzle_diameter, - layer_height => $layer_height, - bridge_flow_ratio => ($bridge ? $self->config->bridge_flow_ratio : 0), - ); -} - 1; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index f1edc98f8..b1ef32fad 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -2,6 +2,7 @@ package Slic3r::Print::Object; use Moo; use List::Util qw(min max sum first); +use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(X Y Z PI scale unscale deg2rad rad2deg scaled_epsilon chained_path); use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex offset offset_ex offset2 offset2_ex CLIPPER_OFFSET_SCALE JT_MITER); @@ -300,7 +301,7 @@ sub make_perimeters { for my $layer_id (0 .. $self->layer_count-2) { my $layerm = $self->layers->[$layer_id]->regions->[$region_id]; my $upper_layerm = $self->layers->[$layer_id+1]->regions->[$region_id]; - my $perimeter_spacing = $layerm->perimeter_flow->scaled_spacing; + my $perimeter_spacing = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing; my $overlap = $perimeter_spacing; # one perimeter @@ -862,8 +863,9 @@ sub generate_support_material { && $self->layer_count >= 2; my $s = Slic3r::Print::SupportMaterial->new( - config => $self->config, - flow => $self->print->support_material_flow, + config => $self->config, + flow => $self->support_material_flow, + interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE), ); $s->generate($self); } @@ -877,4 +879,23 @@ sub _simplify_slices { } } +sub support_material_flow { + my ($self, $role) = @_; + + $role //= FLOW_ROLE_SUPPORT_MATERIAL; + my $extruder = ($role == FLOW_ROLE_SUPPORT_MATERIAL) + ? $self->config->support_material_extruder + : $self->config->support_material_interface_extruder; + + # we use a bogus layer_height because we use the same flow for all + # support material layers + return Slic3r::Flow->new( + width => $self->config->support_material_extrusion_width, + role => $role, + nozzle_diameter => $self->print->config->nozzle_diameter->[$extruder-1], + layer_height => $self->config->layer_height, + bridge_flow_ratio => 0, + ); +} + 1; diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm index 0d3b844f4..559fce3c5 100644 --- a/lib/Slic3r/Print/Region.pm +++ b/lib/Slic3r/Print/Region.pm @@ -21,7 +21,7 @@ sub flow { if (!defined $config_width) { # get extrusion width from configuration # (might be an absolute value, or a percent value, or zero for auto) - if ($first_layer) { + if ($first_layer && $self->config->first_layer_extrusion_width != 0) { $config_width = $self->config->first_layer_extrusion_width; } elsif ($role == FLOW_ROLE_PERIMETER) { $config_width = $self->config->perimeter_extrusion_width; diff --git a/xs/src/Config.cpp b/xs/src/Config.cpp index 15ff4340c..828795c0c 100644 --- a/xs/src/Config.cpp +++ b/xs/src/Config.cpp @@ -131,6 +131,13 @@ ConfigBase::set(t_config_option_key opt_key, SV* value) { ConfigOption* opt = this->option(opt_key, true); if (opt == NULL) CONFESS("Trying to set non-existing option"); + ConfigOptionDef* optdef = &(*this->def)[opt_key]; + if (!optdef->shortcut.empty()) { + for (std::vector::iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it) + this->set(*it, value); + return; + } + if (ConfigOptionFloat* optv = dynamic_cast(opt)) { optv->value = SvNV(value); } else if (ConfigOptionFloats* optv = dynamic_cast(opt)) { diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index a6bd71ec7..17af9b4ee 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -576,6 +576,14 @@ class PrintConfig : public StaticConfig Options["extra_perimeters"].cli = "extra-perimeters!"; Options["extra_perimeters"].scope = "object"; + Options["extruder"].type = coInt; + Options["extruder"].label = "Extruder"; + Options["extruder"].cli = "extruder=i"; + Options["extruder"].shortcut.push_back("perimeter_extruder"); + Options["extruder"].shortcut.push_back("infill_extruder"); + Options["extruder"].shortcut.push_back("support_material_extruder"); + Options["extruder"].shortcut.push_back("support_material_interface_extruder"); + Options["extruder_clearance_height"].type = coFloat; Options["extruder_clearance_height"].label = "Height"; Options["extruder_clearance_height"].tooltip = "Set this to the vertical distance between your nozzle tip and (usually) the X carriage rods. In other words, this is the height of the clearance cylinder around your extruder, and it represents the maximum depth the extruder can peek before colliding with other printed objects."; @@ -761,7 +769,6 @@ class PrintConfig : public StaticConfig Options["infill_extruder"].label = "Infill extruder"; Options["infill_extruder"].tooltip = "The extruder to use when printing infill."; Options["infill_extruder"].cli = "infill-extruder=i"; - Options["infill_extruder"].shortcut.push_back("extruder"); Options["infill_extrusion_width"].type = coFloatOrPercent; Options["infill_extrusion_width"].label = "Infill"; @@ -879,7 +886,6 @@ class PrintConfig : public StaticConfig Options["perimeter_extruder"].tooltip = "The extruder to use when printing perimeters."; Options["perimeter_extruder"].cli = "perimeter-extruder=i"; Options["perimeter_extruder"].aliases.push_back("perimeters_extruder"); - Options["perimeter_extruder"].shortcut.push_back("extruder"); Options["perimeter_extrusion_width"].type = coFloatOrPercent; Options["perimeter_extrusion_width"].label = "Perimeters"; @@ -1141,7 +1147,6 @@ class PrintConfig : public StaticConfig Options["support_material_extruder"].label = "Support material extruder"; Options["support_material_extruder"].tooltip = "The extruder to use when printing support material. This affects brim and raft too."; Options["support_material_extruder"].cli = "support-material-extruder=i"; - Options["support_material_extruder"].shortcut.push_back("extruder"); Options["support_material_extrusion_width"].type = coFloatOrPercent; Options["support_material_extrusion_width"].label = "Support material"; @@ -1153,7 +1158,6 @@ class PrintConfig : public StaticConfig Options["support_material_interface_extruder"].label = "Support material interface extruder"; Options["support_material_interface_extruder"].tooltip = "The extruder to use when printing support material interface. This affects raft too."; Options["support_material_interface_extruder"].cli = "support-material-interface-extruder=i"; - Options["support_material_interface_extruder"].shortcut.push_back("extruder"); Options["support_material_interface_layers"].type = coInt; Options["support_material_interface_layers"].label = "Interface layers"; diff --git a/xs/t/15_config.t b/xs/t/15_config.t index 9a9943d3a..699f115f8 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 78; +use Test::More tests => 79; foreach my $config (Slic3r::Config->new, Slic3r::Config::Print->new) { $config->set('layer_height', 0.3); @@ -88,6 +88,8 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Print->new) { { my $config = Slic3r::Config->new; $config->set('perimeters', 2); + $config->set('solid_layers', 2); + is $config->get('top_solid_layers'), 2, 'shortcut'; # test that no crash happens when using set_deserialize() with a key that hasn't been set() yet $config->set_deserialize('filament_diameter', '3'); From 51b976721d7c321afaa8f021516c4a3bf847ea4c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 31 Dec 2013 15:52:37 +0100 Subject: [PATCH 03/19] Split PrintConfig into PrintObjectConfig and PrintRegionConfig --- xs/src/PrintConfig.cpp | 2 +- xs/src/PrintConfig.hpp | 859 +++++++++++++++++++++-------------------- 2 files changed, 449 insertions(+), 412 deletions(-) diff --git a/xs/src/PrintConfig.cpp b/xs/src/PrintConfig.cpp index 76cc9ef49..711740897 100644 --- a/xs/src/PrintConfig.cpp +++ b/xs/src/PrintConfig.cpp @@ -2,6 +2,6 @@ namespace Slic3r { -t_optiondef_map PrintConfig::PrintConfigDef = PrintConfig::build_def(); +t_optiondef_map PrintConfigDef::def = PrintConfigDef::build_def(); } diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index 17af9b4ee..38eb09ba1 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -37,417 +37,10 @@ template<> inline t_config_enum_values ConfigOptionEnum::get_enum return keys_map; } -class PrintConfig : public StaticConfig +class PrintConfigDef { public: - static t_optiondef_map PrintConfigDef; - - ConfigOptionBool avoid_crossing_perimeters; - ConfigOptionPoint bed_size; - ConfigOptionInt bed_temperature; - ConfigOptionInt bottom_solid_layers; - ConfigOptionFloat bridge_acceleration; - ConfigOptionInt bridge_fan_speed; - ConfigOptionFloat bridge_flow_ratio; - ConfigOptionFloat bridge_speed; - ConfigOptionFloat brim_width; - ConfigOptionBool complete_objects; - ConfigOptionBool cooling; - ConfigOptionFloat default_acceleration; - ConfigOptionInt disable_fan_first_layers; - ConfigOptionInt duplicate; - ConfigOptionFloat duplicate_distance; - ConfigOptionPoint duplicate_grid; - ConfigOptionString end_gcode; - ConfigOptionFloatOrPercent external_perimeter_speed; - ConfigOptionBool external_perimeters_first; - ConfigOptionBool extra_perimeters; - ConfigOptionFloat extruder_clearance_height; - ConfigOptionFloat extruder_clearance_radius; - ConfigOptionPoints extruder_offset; - ConfigOptionString extrusion_axis; - ConfigOptionFloats extrusion_multiplier; - ConfigOptionFloatOrPercent extrusion_width; - ConfigOptionBool fan_always_on; - ConfigOptionInt fan_below_layer_time; - ConfigOptionFloats filament_diameter; - ConfigOptionInt fill_angle; - ConfigOptionFloat fill_density; - ConfigOptionEnum fill_pattern; - ConfigOptionFloat first_layer_acceleration; - ConfigOptionInt first_layer_bed_temperature; - ConfigOptionFloatOrPercent first_layer_extrusion_width; - ConfigOptionFloatOrPercent first_layer_height; - ConfigOptionFloatOrPercent first_layer_speed; - ConfigOptionInts first_layer_temperature; - ConfigOptionBool g0; - ConfigOptionFloat gap_fill_speed; - ConfigOptionBool gcode_arcs; - ConfigOptionBool gcode_comments; - ConfigOptionEnum gcode_flavor; - ConfigOptionFloat infill_acceleration; - ConfigOptionInt infill_every_layers; - ConfigOptionInt infill_extruder; - ConfigOptionFloatOrPercent infill_extrusion_width; - ConfigOptionBool infill_first; - ConfigOptionBool infill_only_where_needed; - ConfigOptionFloat infill_speed; - ConfigOptionString layer_gcode; - ConfigOptionFloat layer_height; - ConfigOptionInt max_fan_speed; - ConfigOptionInt min_fan_speed; - ConfigOptionInt min_print_speed; - ConfigOptionFloat min_skirt_length; - ConfigOptionString notes; - ConfigOptionFloats nozzle_diameter; - ConfigOptionBool only_retract_when_crossing_perimeters; - ConfigOptionBool ooze_prevention; - ConfigOptionString output_filename_format; - ConfigOptionBool overhangs; - ConfigOptionFloat perimeter_acceleration; - ConfigOptionInt perimeter_extruder; - ConfigOptionFloatOrPercent perimeter_extrusion_width; - ConfigOptionFloat perimeter_speed; - ConfigOptionInt perimeters; - ConfigOptionStrings post_process; - ConfigOptionPoint print_center; - ConfigOptionInt raft_layers; - ConfigOptionBool randomize_start; - ConfigOptionFloat resolution; - ConfigOptionFloats retract_before_travel; - ConfigOptionBools retract_layer_change; - ConfigOptionFloats retract_length; - ConfigOptionFloats retract_length_toolchange; - ConfigOptionFloats retract_lift; - ConfigOptionFloats retract_restart_extra; - ConfigOptionFloats retract_restart_extra_toolchange; - ConfigOptionInts retract_speed; - ConfigOptionInt rotate; - ConfigOptionFloat scale; - ConfigOptionFloat skirt_distance; - ConfigOptionInt skirt_height; - ConfigOptionInt skirts; - ConfigOptionInt slowdown_below_layer_time; - ConfigOptionFloatOrPercent small_perimeter_speed; - ConfigOptionEnum solid_fill_pattern; - ConfigOptionFloat solid_infill_below_area; - ConfigOptionInt solid_infill_every_layers; - ConfigOptionFloatOrPercent solid_infill_extrusion_width; - ConfigOptionFloatOrPercent solid_infill_speed; - ConfigOptionInt solid_layers; - ConfigOptionBool spiral_vase; - ConfigOptionInt standby_temperature_delta; - ConfigOptionString start_gcode; - ConfigOptionBool start_perimeters_at_concave_points; - ConfigOptionBool start_perimeters_at_non_overhang; - ConfigOptionBool support_material; - ConfigOptionInt support_material_angle; - ConfigOptionInt support_material_enforce_layers; - ConfigOptionInt support_material_extruder; - ConfigOptionFloatOrPercent support_material_extrusion_width; - ConfigOptionInt support_material_interface_extruder; - ConfigOptionInt support_material_interface_layers; - ConfigOptionFloat support_material_interface_spacing; - ConfigOptionEnum support_material_pattern; - ConfigOptionFloat support_material_spacing; - ConfigOptionFloat support_material_speed; - ConfigOptionInt support_material_threshold; - ConfigOptionInts temperature; - ConfigOptionBool thin_walls; - ConfigOptionInt threads; - ConfigOptionString toolchange_gcode; - ConfigOptionFloatOrPercent top_infill_extrusion_width; - ConfigOptionFloatOrPercent top_solid_infill_speed; - ConfigOptionInt top_solid_layers; - ConfigOptionFloat travel_speed; - ConfigOptionBool use_firmware_retraction; - ConfigOptionBool use_relative_e_distances; - ConfigOptionFloat vibration_limit; - ConfigOptionBools wipe; - ConfigOptionFloat z_offset; - - PrintConfig() { - this->def = &PrintConfig::PrintConfigDef; - - this->avoid_crossing_perimeters.value = false; - this->bed_size.point = Pointf(200,200); - this->bed_temperature.value = 0; - this->bottom_solid_layers.value = 3; - this->bridge_acceleration.value = 0; - this->bridge_fan_speed.value = 100; - this->bridge_flow_ratio.value = 1; - this->bridge_speed.value = 60; - this->brim_width.value = 0; - this->complete_objects.value = false; - this->cooling.value = true; - this->default_acceleration.value = 0; - this->disable_fan_first_layers.value = 1; - this->duplicate.value = 1; - this->duplicate_distance.value = 6; - this->duplicate_grid.point = Pointf(1,1); - this->end_gcode.value = "M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n"; - this->external_perimeter_speed.value = 70; - this->external_perimeter_speed.percent = true; - this->external_perimeters_first.value = false; - this->extra_perimeters.value = true; - this->extruder_clearance_height.value = 20; - this->extruder_clearance_radius.value = 20; - this->extruder_offset.points.resize(1); - this->extruder_offset.points[0] = Pointf(0,0); - this->extrusion_axis.value = "E"; - this->extrusion_multiplier.values.resize(1); - this->extrusion_multiplier.values[0] = 1; - this->extrusion_width.value = 0; - this->extrusion_width.percent = false; - this->fan_always_on.value = false; - this->fan_below_layer_time.value = 60; - this->filament_diameter.values.resize(1); - this->filament_diameter.values[0] = 3; - this->fill_angle.value = 45; - this->fill_density.value = 0.4; - this->fill_pattern.value = ipHoneycomb; - this->first_layer_acceleration.value = 0; - this->first_layer_bed_temperature.value = 0; - this->first_layer_extrusion_width.value = 200; - this->first_layer_extrusion_width.percent = true; - this->first_layer_height.value = 0.35; - this->first_layer_height.percent = false; - this->first_layer_speed.value = 30; - this->first_layer_speed.percent = true; - this->first_layer_temperature.values.resize(1); - this->first_layer_temperature.values[0] = 200; - this->g0.value = false; - this->gap_fill_speed.value = 20; - this->gcode_arcs.value = false; - this->gcode_comments.value = false; - this->gcode_flavor.value = gcfRepRap; - this->infill_acceleration.value = 0; - this->infill_every_layers.value = 1; - this->infill_extruder.value = 1; - this->infill_extrusion_width.value = 0; - this->infill_extrusion_width.percent = false; - this->infill_first.value = false; - this->infill_only_where_needed.value = false; - this->infill_speed.value = 60; - this->layer_gcode.value = ""; - this->layer_height.value = 0.4; - this->max_fan_speed.value = 100; - this->min_fan_speed.value = 35; - this->min_print_speed.value = 10; - this->min_skirt_length.value = 0; - this->notes.value = ""; - this->nozzle_diameter.values.resize(1); - this->nozzle_diameter.values[0] = 0.5; - this->only_retract_when_crossing_perimeters.value = true; - this->ooze_prevention.value = false; - this->output_filename_format.value = "[input_filename_base].gcode"; - this->overhangs.value = true; - this->perimeter_acceleration.value = 0; - this->perimeter_extruder.value = 1; - this->perimeter_extrusion_width.value = 0; - this->perimeter_extrusion_width.percent = false; - this->perimeter_speed.value = 30; - this->perimeters.value = 3; - this->print_center.point = Pointf(100,100); - this->raft_layers.value = 0; - this->randomize_start.value = false; - this->resolution.value = 0; - this->retract_before_travel.values.resize(1); - this->retract_before_travel.values[0] = 2; - this->retract_layer_change.values.resize(1); - this->retract_layer_change.values[0] = true; - this->retract_length.values.resize(1); - this->retract_length.values[0] = 1; - this->retract_length_toolchange.values.resize(1); - this->retract_length_toolchange.values[0] = 10; - this->retract_lift.values.resize(1); - this->retract_lift.values[0] = 0; - this->retract_restart_extra.values.resize(1); - this->retract_restart_extra.values[0] = 0; - this->retract_restart_extra_toolchange.values.resize(1); - this->retract_restart_extra_toolchange.values[0] = 0; - this->retract_speed.values.resize(1); - this->retract_speed.values[0] = 30; - this->rotate.value = 0; - this->scale.value = 1; - this->skirt_distance.value = 6; - this->skirt_height.value = 1; - this->skirts.value = 1; - this->slowdown_below_layer_time.value = 30; - this->small_perimeter_speed.value = 30; - this->small_perimeter_speed.percent = false; - this->solid_fill_pattern.value = ipRectilinear; - this->solid_infill_below_area.value = 70; - this->solid_infill_every_layers.value = 0; - this->solid_infill_extrusion_width.value = 0; - this->solid_infill_extrusion_width.percent = false; - this->solid_infill_speed.value = 60; - this->solid_infill_speed.percent = false; - this->spiral_vase.value = false; - this->standby_temperature_delta.value = -5; - this->start_gcode.value = "G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n"; - this->start_perimeters_at_concave_points.value = false; - this->start_perimeters_at_non_overhang.value = false; - this->support_material.value = false; - this->support_material_angle.value = 0; - this->support_material_enforce_layers.value = 0; - this->support_material_extruder.value = 1; - this->support_material_extrusion_width.value = 0; - this->support_material_extrusion_width.percent = false; - this->support_material_interface_extruder.value = 1; - this->support_material_interface_layers.value = 3; - this->support_material_interface_spacing.value = 0; - this->support_material_pattern.value = ipHoneycomb; - this->support_material_spacing.value = 2.5; - this->support_material_speed.value = 60; - this->support_material_threshold.value = 0; - this->temperature.values.resize(1); - this->temperature.values[0] = 200; - this->thin_walls.value = true; - this->threads.value = 2; - this->toolchange_gcode.value = ""; - this->top_infill_extrusion_width.value = 0; - this->top_infill_extrusion_width.percent = false; - this->top_solid_infill_speed.value = 50; - this->top_solid_infill_speed.percent = false; - this->top_solid_layers.value = 3; - this->travel_speed.value = 130; - this->use_firmware_retraction.value = false; - this->use_relative_e_distances.value = false; - this->vibration_limit.value = 0; - this->wipe.values.resize(1); - this->wipe.values[0] = false; - this->z_offset.value = 0; - }; - - ConfigOption* option(const t_config_option_key opt_key, bool create = false) { - if (opt_key == "avoid_crossing_perimeters") return &this->avoid_crossing_perimeters; - if (opt_key == "bed_size") return &this->bed_size; - if (opt_key == "bed_temperature") return &this->bed_temperature; - if (opt_key == "bottom_solid_layers") return &this->bottom_solid_layers; - if (opt_key == "bridge_acceleration") return &this->bridge_acceleration; - if (opt_key == "bridge_fan_speed") return &this->bridge_fan_speed; - if (opt_key == "bridge_flow_ratio") return &this->bridge_flow_ratio; - if (opt_key == "bridge_speed") return &this->bridge_speed; - if (opt_key == "brim_width") return &this->brim_width; - if (opt_key == "complete_objects") return &this->complete_objects; - if (opt_key == "cooling") return &this->cooling; - if (opt_key == "default_acceleration") return &this->default_acceleration; - if (opt_key == "disable_fan_first_layers") return &this->disable_fan_first_layers; - if (opt_key == "duplicate") return &this->duplicate; - if (opt_key == "duplicate_distance") return &this->duplicate_distance; - if (opt_key == "duplicate_grid") return &this->duplicate_grid; - if (opt_key == "end_gcode") return &this->end_gcode; - if (opt_key == "external_perimeter_speed") return &this->external_perimeter_speed; - if (opt_key == "external_perimeters_first") return &this->external_perimeters_first; - if (opt_key == "extra_perimeters") return &this->extra_perimeters; - if (opt_key == "extruder_clearance_height") return &this->extruder_clearance_height; - if (opt_key == "extruder_clearance_radius") return &this->extruder_clearance_radius; - if (opt_key == "extruder_offset") return &this->extruder_offset; - if (opt_key == "extrusion_axis") return &this->extrusion_axis; - if (opt_key == "extrusion_multiplier") return &this->extrusion_multiplier; - if (opt_key == "extrusion_width") return &this->extrusion_width; - if (opt_key == "fan_always_on") return &this->fan_always_on; - if (opt_key == "fan_below_layer_time") return &this->fan_below_layer_time; - if (opt_key == "filament_diameter") return &this->filament_diameter; - if (opt_key == "fill_angle") return &this->fill_angle; - if (opt_key == "fill_density") return &this->fill_density; - if (opt_key == "fill_pattern") return &this->fill_pattern; - if (opt_key == "first_layer_acceleration") return &this->first_layer_acceleration; - if (opt_key == "first_layer_bed_temperature") return &this->first_layer_bed_temperature; - if (opt_key == "first_layer_extrusion_width") return &this->first_layer_extrusion_width; - if (opt_key == "first_layer_height") return &this->first_layer_height; - if (opt_key == "first_layer_speed") return &this->first_layer_speed; - if (opt_key == "first_layer_temperature") return &this->first_layer_temperature; - if (opt_key == "g0") return &this->g0; - if (opt_key == "gap_fill_speed") return &this->gap_fill_speed; - if (opt_key == "gcode_arcs") return &this->gcode_arcs; - if (opt_key == "gcode_comments") return &this->gcode_comments; - if (opt_key == "gcode_flavor") return &this->gcode_flavor; - if (opt_key == "infill_acceleration") return &this->infill_acceleration; - if (opt_key == "infill_every_layers") return &this->infill_every_layers; - if (opt_key == "infill_extruder") return &this->infill_extruder; - if (opt_key == "infill_extrusion_width") return &this->infill_extrusion_width; - if (opt_key == "infill_first") return &this->infill_first; - if (opt_key == "infill_only_where_needed") return &this->infill_only_where_needed; - if (opt_key == "infill_speed") return &this->infill_speed; - if (opt_key == "layer_gcode") return &this->layer_gcode; - if (opt_key == "layer_height") return &this->layer_height; - if (opt_key == "max_fan_speed") return &this->max_fan_speed; - if (opt_key == "min_fan_speed") return &this->min_fan_speed; - 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; - if (opt_key == "nozzle_diameter") return &this->nozzle_diameter; - if (opt_key == "only_retract_when_crossing_perimeters") return &this->only_retract_when_crossing_perimeters; - if (opt_key == "ooze_prevention") return &this->ooze_prevention; - if (opt_key == "output_filename_format") return &this->output_filename_format; - if (opt_key == "overhangs") return &this->overhangs; - if (opt_key == "perimeter_acceleration") return &this->perimeter_acceleration; - if (opt_key == "perimeter_extruder") return &this->perimeter_extruder; - if (opt_key == "perimeter_extrusion_width") return &this->perimeter_extrusion_width; - if (opt_key == "perimeter_speed") return &this->perimeter_speed; - if (opt_key == "perimeters") return &this->perimeters; - if (opt_key == "post_process") return &this->post_process; - if (opt_key == "print_center") return &this->print_center; - if (opt_key == "raft_layers") return &this->raft_layers; - if (opt_key == "randomize_start") return &this->randomize_start; - if (opt_key == "resolution") return &this->resolution; - if (opt_key == "retract_before_travel") return &this->retract_before_travel; - if (opt_key == "retract_layer_change") return &this->retract_layer_change; - if (opt_key == "retract_length") return &this->retract_length; - if (opt_key == "retract_length_toolchange") return &this->retract_length_toolchange; - if (opt_key == "retract_lift") return &this->retract_lift; - if (opt_key == "retract_restart_extra") return &this->retract_restart_extra; - if (opt_key == "retract_restart_extra_toolchange") return &this->retract_restart_extra_toolchange; - if (opt_key == "retract_speed") return &this->retract_speed; - if (opt_key == "rotate") return &this->rotate; - if (opt_key == "scale") return &this->scale; - if (opt_key == "skirt_distance") return &this->skirt_distance; - if (opt_key == "skirt_height") return &this->skirt_height; - if (opt_key == "skirts") return &this->skirts; - if (opt_key == "slowdown_below_layer_time") return &this->slowdown_below_layer_time; - if (opt_key == "small_perimeter_speed") return &this->small_perimeter_speed; - if (opt_key == "solid_fill_pattern") return &this->solid_fill_pattern; - if (opt_key == "solid_infill_below_area") return &this->solid_infill_below_area; - if (opt_key == "solid_infill_every_layers") return &this->solid_infill_every_layers; - if (opt_key == "solid_infill_extrusion_width") return &this->solid_infill_extrusion_width; - if (opt_key == "solid_infill_speed") return &this->solid_infill_speed; - if (opt_key == "solid_layers") return &this->solid_layers; - if (opt_key == "spiral_vase") return &this->spiral_vase; - if (opt_key == "standby_temperature_delta") return &this->standby_temperature_delta; - if (opt_key == "start_gcode") return &this->start_gcode; - if (opt_key == "start_perimeters_at_concave_points") return &this->start_perimeters_at_concave_points; - if (opt_key == "start_perimeters_at_non_overhang") return &this->start_perimeters_at_non_overhang; - if (opt_key == "support_material") return &this->support_material; - if (opt_key == "support_material_angle") return &this->support_material_angle; - if (opt_key == "support_material_enforce_layers") return &this->support_material_enforce_layers; - if (opt_key == "support_material_extruder") return &this->support_material_extruder; - if (opt_key == "support_material_extrusion_width") return &this->support_material_extrusion_width; - if (opt_key == "support_material_interface_extruder") return &this->support_material_interface_extruder; - if (opt_key == "support_material_interface_layers") return &this->support_material_interface_layers; - if (opt_key == "support_material_interface_spacing") return &this->support_material_interface_spacing; - if (opt_key == "support_material_pattern") return &this->support_material_pattern; - if (opt_key == "support_material_spacing") return &this->support_material_spacing; - if (opt_key == "support_material_speed") return &this->support_material_speed; - if (opt_key == "support_material_threshold") return &this->support_material_threshold; - if (opt_key == "temperature") return &this->temperature; - if (opt_key == "thin_walls") return &this->thin_walls; - if (opt_key == "threads") return &this->threads; - if (opt_key == "toolchange_gcode") return &this->toolchange_gcode; - if (opt_key == "top_infill_extrusion_width") return &this->top_infill_extrusion_width; - if (opt_key == "top_solid_infill_speed") return &this->top_solid_infill_speed; - if (opt_key == "top_solid_layers") return &this->top_solid_layers; - if (opt_key == "travel_speed") return &this->travel_speed; - if (opt_key == "use_firmware_retraction") return &this->use_firmware_retraction; - if (opt_key == "use_relative_e_distances") return &this->use_relative_e_distances; - if (opt_key == "vibration_limit") return &this->vibration_limit; - if (opt_key == "wipe") return &this->wipe; - if (opt_key == "z_offset") return &this->z_offset; - - if (create) throw "Attempt to create non-existing option in StaticConfig object"; - return NULL; - }; + static t_optiondef_map def; static t_optiondef_map build_def () { t_optiondef_map Options; @@ -1301,15 +894,459 @@ class PrintConfig : public StaticConfig }; }; +class PrintObjectConfig : public virtual StaticConfig +{ + public: + ConfigOptionFloatOrPercent extrusion_width; + ConfigOptionFloatOrPercent first_layer_height; + ConfigOptionFloat layer_height; + ConfigOptionInt raft_layers; + ConfigOptionBool support_material; + ConfigOptionInt support_material_angle; + ConfigOptionInt support_material_enforce_layers; + ConfigOptionInt support_material_extruder; + ConfigOptionFloatOrPercent support_material_extrusion_width; + ConfigOptionInt support_material_interface_extruder; + ConfigOptionInt support_material_interface_layers; + ConfigOptionFloat support_material_interface_spacing; + ConfigOptionEnum support_material_pattern; + ConfigOptionFloat support_material_spacing; + ConfigOptionFloat support_material_speed; + ConfigOptionInt support_material_threshold; + + PrintObjectConfig() { + this->def = &PrintConfigDef::def; + + this->extrusion_width.value = 0; + this->extrusion_width.percent = false; + this->first_layer_height.value = 0.35; + this->first_layer_height.percent = false; + this->layer_height.value = 0.4; + this->raft_layers.value = 0; + this->support_material.value = false; + this->support_material_angle.value = 0; + this->support_material_enforce_layers.value = 0; + this->support_material_extruder.value = 1; + this->support_material_extrusion_width.value = 0; + this->support_material_extrusion_width.percent = false; + this->support_material_interface_extruder.value = 1; + this->support_material_interface_layers.value = 3; + this->support_material_interface_spacing.value = 0; + this->support_material_pattern.value = ipHoneycomb; + this->support_material_spacing.value = 2.5; + this->support_material_speed.value = 60; + this->support_material_threshold.value = 0; + }; + + ConfigOption* option(const t_config_option_key opt_key, bool create = false) { + if (opt_key == "extrusion_width") return &this->extrusion_width; + if (opt_key == "first_layer_height") return &this->first_layer_height; + if (opt_key == "layer_height") return &this->layer_height; + if (opt_key == "raft_layers") return &this->raft_layers; + if (opt_key == "support_material") return &this->support_material; + if (opt_key == "support_material_angle") return &this->support_material_angle; + if (opt_key == "support_material_enforce_layers") return &this->support_material_enforce_layers; + if (opt_key == "support_material_extruder") return &this->support_material_extruder; + if (opt_key == "support_material_extrusion_width") return &this->support_material_extrusion_width; + if (opt_key == "support_material_interface_extruder") return &this->support_material_interface_extruder; + if (opt_key == "support_material_interface_layers") return &this->support_material_interface_layers; + if (opt_key == "support_material_interface_spacing") return &this->support_material_interface_spacing; + if (opt_key == "support_material_pattern") return &this->support_material_pattern; + if (opt_key == "support_material_spacing") return &this->support_material_spacing; + if (opt_key == "support_material_speed") return &this->support_material_speed; + if (opt_key == "support_material_threshold") return &this->support_material_threshold; + + if (create) throw "Attempt to create non-existing option in StaticConfig object"; + return NULL; + }; +}; + +class PrintRegionConfig : public virtual StaticConfig +{ + public: + ConfigOptionInt bottom_solid_layers; + ConfigOptionBool extra_perimeters; + ConfigOptionInt fill_angle; + ConfigOptionFloat fill_density; + ConfigOptionEnum fill_pattern; + ConfigOptionFloatOrPercent first_layer_extrusion_width; + ConfigOptionInt infill_extruder; + ConfigOptionFloatOrPercent infill_extrusion_width; + ConfigOptionInt infill_every_layers; + ConfigOptionBool infill_only_where_needed; + ConfigOptionInt perimeter_extruder; + ConfigOptionFloatOrPercent perimeter_extrusion_width; + ConfigOptionInt perimeters; + ConfigOptionEnum solid_fill_pattern; + ConfigOptionFloat solid_infill_below_area; + ConfigOptionFloatOrPercent solid_infill_extrusion_width; + ConfigOptionInt solid_infill_every_layers; + ConfigOptionInt solid_layers; + ConfigOptionBool thin_walls; + ConfigOptionFloatOrPercent top_infill_extrusion_width; + ConfigOptionInt top_solid_layers; + + PrintObjectConfig() { + this->def = &PrintConfigDef::def; + + this->bottom_solid_layers.value = 3; + this->extra_perimeters.value = true; + this->fill_angle.value = 45; + this->fill_density.value = 0.4; + this->fill_pattern.value = ipHoneycomb; + this->first_layer_extrusion_width.value = 200; + this->first_layer_extrusion_width.percent = true; + this->infill_extruder.value = 1; + this->infill_extrusion_width.value = 0; + this->infill_extrusion_width.percent = false; + this->infill_every_layers.value = 1; + this->infill_only_where_needed.value = false; + this->perimeter_extruder.value = 1; + this->perimeter_extrusion_width.value = 0; + this->perimeter_extrusion_width.percent = false; + this->perimeters.value = 3; + this->solid_fill_pattern.value = ipRectilinear; + this->solid_infill_below_area.value = 70; + this->solid_infill_extrusion_width.value = 0; + this->solid_infill_extrusion_width.percent = false; + this->solid_infill_every_layers.value = 0; + this->thin_walls.value = true; + this->top_infill_extrusion_width.value = 0; + this->top_infill_extrusion_width.percent = false; + this->top_solid_layers.value = 3; + }; + + ConfigOption* option(const t_config_option_key opt_key, bool create = false) { + if (opt_key == "bottom_solid_layers") return &this->bottom_solid_layers; + if (opt_key == "extra_perimeters") return &this->extra_perimeters; + if (opt_key == "fill_angle") return &this->fill_angle; + if (opt_key == "fill_density") return &this->fill_density; + if (opt_key == "fill_pattern") return &this->fill_pattern; + if (opt_key == "first_layer_extrusion_width") return &this->first_layer_extrusion_width; + if (opt_key == "infill_extruder") return &this->infill_extruder; + if (opt_key == "infill_extrusion_width") return &this->infill_extrusion_width; + if (opt_key == "infill_every_layers") return &this->infill_every_layers; + if (opt_key == "infill_only_where_needed") return &this->infill_only_where_needed; + if (opt_key == "perimeter_extruder") return &this->perimeter_extruder; + if (opt_key == "perimeter_extrusion_width") return &this->perimeter_extrusion_width; + if (opt_key == "perimeters") return &this->perimeters; + if (opt_key == "solid_fill_pattern") return &this->solid_fill_pattern; + if (opt_key == "solid_infill_below_area") return &this->solid_infill_below_area; + if (opt_key == "solid_infill_extrusion_width") return &this->solid_infill_extrusion_width; + if (opt_key == "solid_infill_every_layers") return &this->solid_infill_every_layers; + if (opt_key == "solid_layers") return &this->solid_layers; + if (opt_key == "thin_walls") return &this->thin_walls; + if (opt_key == "top_infill_extrusion_width") return &this->top_infill_extrusion_width; + if (opt_key == "top_solid_layers") return &this->top_solid_layers; + + if (create) throw "Attempt to create non-existing option in StaticConfig object"; + return NULL; + }; +}; + +class PrintConfig : public virtual StaticConfig +{ + public: + ConfigOptionBool avoid_crossing_perimeters; + ConfigOptionPoint bed_size; + ConfigOptionInt bed_temperature; + ConfigOptionFloat bridge_acceleration; + ConfigOptionInt bridge_fan_speed; + ConfigOptionFloat bridge_flow_ratio; + ConfigOptionFloat bridge_speed; + ConfigOptionFloat brim_width; + ConfigOptionBool complete_objects; + ConfigOptionBool cooling; + ConfigOptionFloat default_acceleration; + ConfigOptionInt disable_fan_first_layers; + ConfigOptionInt duplicate; + ConfigOptionFloat duplicate_distance; + ConfigOptionPoint duplicate_grid; + ConfigOptionString end_gcode; + ConfigOptionFloatOrPercent external_perimeter_speed; + ConfigOptionBool external_perimeters_first; + ConfigOptionFloat extruder_clearance_height; + ConfigOptionFloat extruder_clearance_radius; + ConfigOptionPoints extruder_offset; + ConfigOptionString extrusion_axis; + ConfigOptionFloats extrusion_multiplier; + ConfigOptionBool fan_always_on; + ConfigOptionInt fan_below_layer_time; + ConfigOptionFloats filament_diameter; + ConfigOptionFloat first_layer_acceleration; + ConfigOptionInt first_layer_bed_temperature; + ConfigOptionFloatOrPercent first_layer_speed; + ConfigOptionInts first_layer_temperature; + ConfigOptionBool g0; + ConfigOptionFloat gap_fill_speed; + ConfigOptionBool gcode_arcs; + ConfigOptionBool gcode_comments; + ConfigOptionEnum gcode_flavor; + ConfigOptionFloat infill_acceleration; + ConfigOptionBool infill_first; + ConfigOptionFloat infill_speed; + ConfigOptionString layer_gcode; + ConfigOptionInt max_fan_speed; + ConfigOptionInt min_fan_speed; + ConfigOptionInt min_print_speed; + ConfigOptionFloat min_skirt_length; + ConfigOptionString notes; + ConfigOptionFloats nozzle_diameter; + ConfigOptionBool only_retract_when_crossing_perimeters; + ConfigOptionBool ooze_prevention; + ConfigOptionString output_filename_format; + ConfigOptionBool overhangs; + ConfigOptionFloat perimeter_acceleration; + ConfigOptionFloat perimeter_speed; + ConfigOptionStrings post_process; + ConfigOptionPoint print_center; + ConfigOptionBool randomize_start; + ConfigOptionFloat resolution; + ConfigOptionFloats retract_before_travel; + ConfigOptionBools retract_layer_change; + ConfigOptionFloats retract_length; + ConfigOptionFloats retract_length_toolchange; + ConfigOptionFloats retract_lift; + ConfigOptionFloats retract_restart_extra; + ConfigOptionFloats retract_restart_extra_toolchange; + ConfigOptionInts retract_speed; + ConfigOptionInt rotate; + ConfigOptionFloat scale; + ConfigOptionFloat skirt_distance; + ConfigOptionInt skirt_height; + ConfigOptionInt skirts; + ConfigOptionInt slowdown_below_layer_time; + ConfigOptionFloatOrPercent small_perimeter_speed; + ConfigOptionFloatOrPercent solid_infill_speed; + ConfigOptionBool spiral_vase; + ConfigOptionInt standby_temperature_delta; + ConfigOptionString start_gcode; + ConfigOptionBool start_perimeters_at_concave_points; + ConfigOptionBool start_perimeters_at_non_overhang; + ConfigOptionInts temperature; + ConfigOptionInt threads; + ConfigOptionString toolchange_gcode; + ConfigOptionFloatOrPercent top_solid_infill_speed; + ConfigOptionFloat travel_speed; + ConfigOptionBool use_firmware_retraction; + ConfigOptionBool use_relative_e_distances; + ConfigOptionFloat vibration_limit; + ConfigOptionBools wipe; + ConfigOptionFloat z_offset; + + PrintConfig() { + this->def = &PrintConfig::PrintConfigDef; + + this->avoid_crossing_perimeters.value = false; + this->bed_size.point = Pointf(200,200); + this->bed_temperature.value = 0; + this->bridge_acceleration.value = 0; + this->bridge_fan_speed.value = 100; + this->bridge_flow_ratio.value = 1; + this->bridge_speed.value = 60; + this->brim_width.value = 0; + this->complete_objects.value = false; + this->cooling.value = true; + this->default_acceleration.value = 0; + this->disable_fan_first_layers.value = 1; + this->duplicate.value = 1; + this->duplicate_distance.value = 6; + this->duplicate_grid.point = Pointf(1,1); + this->end_gcode.value = "M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n"; + this->external_perimeter_speed.value = 70; + this->external_perimeter_speed.percent = true; + this->external_perimeters_first.value = false; + this->extruder_clearance_height.value = 20; + this->extruder_clearance_radius.value = 20; + this->extruder_offset.points.resize(1); + this->extruder_offset.points[0] = Pointf(0,0); + this->extrusion_axis.value = "E"; + this->extrusion_multiplier.values.resize(1); + this->extrusion_multiplier.values[0] = 1; + this->fan_always_on.value = false; + this->fan_below_layer_time.value = 60; + this->filament_diameter.values.resize(1); + this->filament_diameter.values[0] = 3; + this->first_layer_acceleration.value = 0; + this->first_layer_bed_temperature.value = 0; + this->first_layer_speed.value = 30; + this->first_layer_speed.percent = true; + this->first_layer_temperature.values.resize(1); + this->first_layer_temperature.values[0] = 200; + this->g0.value = false; + this->gap_fill_speed.value = 20; + this->gcode_arcs.value = false; + this->gcode_comments.value = false; + this->gcode_flavor.value = gcfRepRap; + this->infill_acceleration.value = 0; + this->infill_first.value = false; + this->infill_speed.value = 60; + this->layer_gcode.value = ""; + this->max_fan_speed.value = 100; + this->min_fan_speed.value = 35; + this->min_print_speed.value = 10; + this->min_skirt_length.value = 0; + this->notes.value = ""; + this->nozzle_diameter.values.resize(1); + this->nozzle_diameter.values[0] = 0.5; + this->only_retract_when_crossing_perimeters.value = true; + this->ooze_prevention.value = false; + this->output_filename_format.value = "[input_filename_base].gcode"; + this->overhangs.value = true; + this->perimeter_acceleration.value = 0; + this->perimeter_speed.value = 30; + this->print_center.point = Pointf(100,100); + this->randomize_start.value = false; + this->resolution.value = 0; + this->retract_before_travel.values.resize(1); + this->retract_before_travel.values[0] = 2; + this->retract_layer_change.values.resize(1); + this->retract_layer_change.values[0] = true; + this->retract_length.values.resize(1); + this->retract_length.values[0] = 1; + this->retract_length_toolchange.values.resize(1); + this->retract_length_toolchange.values[0] = 10; + this->retract_lift.values.resize(1); + this->retract_lift.values[0] = 0; + this->retract_restart_extra.values.resize(1); + this->retract_restart_extra.values[0] = 0; + this->retract_restart_extra_toolchange.values.resize(1); + this->retract_restart_extra_toolchange.values[0] = 0; + this->retract_speed.values.resize(1); + this->retract_speed.values[0] = 30; + this->rotate.value = 0; + this->scale.value = 1; + this->skirt_distance.value = 6; + this->skirt_height.value = 1; + this->skirts.value = 1; + this->slowdown_below_layer_time.value = 30; + this->small_perimeter_speed.value = 30; + this->small_perimeter_speed.percent = false; + this->solid_infill_speed.value = 60; + this->solid_infill_speed.percent = false; + this->spiral_vase.value = false; + this->standby_temperature_delta.value = -5; + this->start_gcode.value = "G28 ; home all axes\nG1 Z5 F5000 ; lift nozzle\n"; + this->start_perimeters_at_concave_points.value = false; + this->start_perimeters_at_non_overhang.value = false; + this->temperature.values.resize(1); + this->temperature.values[0] = 200; + this->threads.value = 2; + this->toolchange_gcode.value = ""; + this->top_solid_infill_speed.value = 50; + this->top_solid_infill_speed.percent = false; + this->travel_speed.value = 130; + this->use_firmware_retraction.value = false; + this->use_relative_e_distances.value = false; + this->vibration_limit.value = 0; + this->wipe.values.resize(1); + this->wipe.values[0] = false; + this->z_offset.value = 0; + }; + + ConfigOption* option(const t_config_option_key opt_key, bool create = false) { + if (opt_key == "avoid_crossing_perimeters") return &this->avoid_crossing_perimeters; + if (opt_key == "bed_size") return &this->bed_size; + if (opt_key == "bed_temperature") return &this->bed_temperature; + if (opt_key == "bridge_acceleration") return &this->bridge_acceleration; + if (opt_key == "bridge_fan_speed") return &this->bridge_fan_speed; + if (opt_key == "bridge_flow_ratio") return &this->bridge_flow_ratio; + if (opt_key == "bridge_speed") return &this->bridge_speed; + if (opt_key == "brim_width") return &this->brim_width; + if (opt_key == "complete_objects") return &this->complete_objects; + if (opt_key == "cooling") return &this->cooling; + if (opt_key == "default_acceleration") return &this->default_acceleration; + if (opt_key == "disable_fan_first_layers") return &this->disable_fan_first_layers; + if (opt_key == "duplicate") return &this->duplicate; + if (opt_key == "duplicate_distance") return &this->duplicate_distance; + if (opt_key == "duplicate_grid") return &this->duplicate_grid; + if (opt_key == "end_gcode") return &this->end_gcode; + if (opt_key == "external_perimeter_speed") return &this->external_perimeter_speed; + if (opt_key == "external_perimeters_first") return &this->external_perimeters_first; + if (opt_key == "extruder_clearance_height") return &this->extruder_clearance_height; + if (opt_key == "extruder_clearance_radius") return &this->extruder_clearance_radius; + if (opt_key == "extruder_offset") return &this->extruder_offset; + if (opt_key == "extrusion_axis") return &this->extrusion_axis; + if (opt_key == "extrusion_multiplier") return &this->extrusion_multiplier; + if (opt_key == "fan_always_on") return &this->fan_always_on; + if (opt_key == "fan_below_layer_time") return &this->fan_below_layer_time; + if (opt_key == "filament_diameter") return &this->filament_diameter; + if (opt_key == "first_layer_acceleration") return &this->first_layer_acceleration; + if (opt_key == "first_layer_bed_temperature") return &this->first_layer_bed_temperature; + if (opt_key == "first_layer_speed") return &this->first_layer_speed; + if (opt_key == "first_layer_temperature") return &this->first_layer_temperature; + if (opt_key == "g0") return &this->g0; + if (opt_key == "gap_fill_speed") return &this->gap_fill_speed; + if (opt_key == "gcode_arcs") return &this->gcode_arcs; + if (opt_key == "gcode_comments") return &this->gcode_comments; + if (opt_key == "gcode_flavor") return &this->gcode_flavor; + if (opt_key == "infill_acceleration") return &this->infill_acceleration; + if (opt_key == "infill_first") return &this->infill_first; + if (opt_key == "infill_speed") return &this->infill_speed; + if (opt_key == "layer_gcode") return &this->layer_gcode; + if (opt_key == "max_fan_speed") return &this->max_fan_speed; + if (opt_key == "min_fan_speed") return &this->min_fan_speed; + 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; + if (opt_key == "nozzle_diameter") return &this->nozzle_diameter; + if (opt_key == "only_retract_when_crossing_perimeters") return &this->only_retract_when_crossing_perimeters; + if (opt_key == "ooze_prevention") return &this->ooze_prevention; + if (opt_key == "output_filename_format") return &this->output_filename_format; + if (opt_key == "overhangs") return &this->overhangs; + if (opt_key == "perimeter_acceleration") return &this->perimeter_acceleration; + if (opt_key == "perimeter_speed") return &this->perimeter_speed; + if (opt_key == "post_process") return &this->post_process; + if (opt_key == "print_center") return &this->print_center; + if (opt_key == "randomize_start") return &this->randomize_start; + if (opt_key == "resolution") return &this->resolution; + if (opt_key == "retract_before_travel") return &this->retract_before_travel; + if (opt_key == "retract_layer_change") return &this->retract_layer_change; + if (opt_key == "retract_length") return &this->retract_length; + if (opt_key == "retract_length_toolchange") return &this->retract_length_toolchange; + if (opt_key == "retract_lift") return &this->retract_lift; + if (opt_key == "retract_restart_extra") return &this->retract_restart_extra; + if (opt_key == "retract_restart_extra_toolchange") return &this->retract_restart_extra_toolchange; + if (opt_key == "retract_speed") return &this->retract_speed; + if (opt_key == "rotate") return &this->rotate; + if (opt_key == "scale") return &this->scale; + if (opt_key == "skirt_distance") return &this->skirt_distance; + if (opt_key == "skirt_height") return &this->skirt_height; + if (opt_key == "skirts") return &this->skirts; + if (opt_key == "slowdown_below_layer_time") return &this->slowdown_below_layer_time; + if (opt_key == "small_perimeter_speed") return &this->small_perimeter_speed; + if (opt_key == "solid_infill_speed") return &this->solid_infill_speed; + if (opt_key == "spiral_vase") return &this->spiral_vase; + if (opt_key == "standby_temperature_delta") return &this->standby_temperature_delta; + if (opt_key == "start_gcode") return &this->start_gcode; + if (opt_key == "start_perimeters_at_concave_points") return &this->start_perimeters_at_concave_points; + if (opt_key == "start_perimeters_at_non_overhang") return &this->start_perimeters_at_non_overhang; + if (opt_key == "temperature") return &this->temperature; + if (opt_key == "threads") return &this->threads; + if (opt_key == "toolchange_gcode") return &this->toolchange_gcode; + if (opt_key == "top_solid_infill_speed") return &this->top_solid_infill_speed; + if (opt_key == "travel_speed") return &this->travel_speed; + if (opt_key == "use_firmware_retraction") return &this->use_firmware_retraction; + if (opt_key == "use_relative_e_distances") return &this->use_relative_e_distances; + if (opt_key == "vibration_limit") return &this->vibration_limit; + if (opt_key == "wipe") return &this->wipe; + if (opt_key == "z_offset") return &this->z_offset; + + if (create) throw "Attempt to create non-existing option in StaticConfig object"; + return NULL; + }; +}; + class DynamicPrintConfig : public DynamicConfig { public: DynamicPrintConfig() { - this->def = &PrintConfig::PrintConfigDef; + this->def = &PrintConfigDef::def; }; - }; +class FullPrintConfig : public PrintObjectConfig, public PrintRegionConfig, public PrintConfig {}; + } #endif From 0883d0f4ebc56425280868ba2d77c09657c13907 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 1 Jan 2014 17:29:15 +0100 Subject: [PATCH 04/19] More work here and there --- lib/Slic3r/Print.pm | 35 +++++++++++++++++++------------ lib/Slic3r/Print/Object.pm | 10 +-------- lib/Slic3r/Print/Region.pm | 2 +- xs/src/Config.cpp | 22 +++++++++++++++++++ xs/src/Config.hpp | 1 + xs/src/PrintConfig.hpp | 4 ++-- xs/xsp/Config.xsp | 43 +++++++++++++++++++++++++++++++------- 7 files changed, 85 insertions(+), 32 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 98060342b..4c9b9d79e 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -12,13 +12,14 @@ use Slic3r::Geometry::Clipper qw(diff_ex union_ex union_pt intersection_ex inter offset2 union union_pt_chained JT_ROUND JT_SQUARE); use Slic3r::Print::State ':steps'; -has 'config' => (is => 'rw', default => sub { Slic3r::Config->new_from_defaults }, trigger => \&init_config); +has 'config' => (is => 'rw', default => sub { Slic3r::Config::Print->new }, trigger => \&init_config); +has 'default_object_config' => (is => 'rw', default => sub { Slic3r::Config::PrintObject->new }); +has 'default_region_config' => (is => 'rw', default => sub { Slic3r::Config::PrintRegion->new }); has 'extra_variables' => (is => 'rw', default => sub {{}}); has 'objects' => (is => 'rw', default => sub {[]}); has 'status_cb' => (is => 'rw'); has 'extruders' => (is => 'rw', default => sub {[]}); has 'regions' => (is => 'rw', default => sub {[]}); -has 'has_support_material' => (is => 'lazy'); has '_state' => (is => 'ro', default => sub { Slic3r::Print::State->new }); # ordered collection of extrusion paths to build skirt loops @@ -70,11 +71,12 @@ sub apply_config { my ($self, $config) = @_; $self->config->apply($config); + $self->default_object_config->apply($config); + $self->default_region_config->apply($config); $self->init_config; - $_->init_config for @{$self->objects}; } -sub _build_has_support_material { +sub has_support_material { my $self = shift; return (first { $_->config->support_material } @{$self->objects}) || (first { $_->config->raft_layers > 0 } @{$self->objects}) @@ -91,13 +93,15 @@ sub add_model_object { foreach my $volume_id (0..$#{$object->volumes}) { my $volume = $object->volumes->[$volume_id]; - # get the config applied to this volume - my $config; + # get the config applied to this volume: start from our global defaults + my $config = Slic3r::Config::RegionConfig->new; + $config->apply($self->default_region_config); + + # override the defaults with per-object config and then with per-material config + $config->apply($object->config); if (defined $volume->material_id) { - my $config = $object->model->materials->{ $volume->material_id }->config; - } else { - $config = Slic3r::Config->new; - $config->set('extruder', 0); + my $material_config = $object->model->materials->{ $volume->material_id }->config; + $config->apply($material_config); } # find an existing print region with the same config @@ -112,10 +116,10 @@ sub add_model_object { # if no region exists with the same config, create a new one if (!defined $region_id) { - push @{$self->regions}, Slic3r::Print::Region->new( + push @{$self->regions}, my $r = Slic3r::Print::Region->new( print => $self, - config => $config->clone, ); + $r->config->apply($config); $region_id = $#{$self->regions}; } @@ -130,9 +134,14 @@ sub add_model_object { model_object => $object, region_volumes => [ map $volumes{$_}, 0..$#{$self->regions} ], copies => [ map Slic3r::Point->new_scale(@{ $_->offset }), @{ $object->instances } ], - config_overrides => $object->config, layer_height_ranges => $object->layer_height_ranges, ); + + # apply config to print object + $o->config->apply($self->default_object_config); + $o->config->apply($object->config); + + # store print object at the given position if (defined $obj_idx) { splice @{$self->objects}, $obj_idx, 0, $o; } else { diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index b1ef32fad..2e9f3dfe0 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -13,8 +13,7 @@ has 'print' => (is => 'ro', weak_ref => 1, required => 1); has 'model_object' => (is => 'ro', required => 1); has 'region_volumes' => (is => 'rw', default => sub { [] }); # by region_id has 'copies' => (is => 'ro'); # Slic3r::Point objects in scaled G-code coordinates -has 'config_overrides' => (is => 'rw', default => sub { Slic3r::Config->new }); -has 'config' => (is => 'rw'); +has 'config' => (is => 'rw', required => 1); # Slic3r::Config::PrintObject has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ] has 'size' => (is => 'rw'); # XYZ in scaled coordinates @@ -28,8 +27,6 @@ has '_state' => (is => 'ro', default => sub { Slic3r::Print::State->n sub BUILD { my $self = shift; - $self->init_config; - # translate meshes so that we work with smaller coordinates { # compute the bounding box of the supplied meshes @@ -97,11 +94,6 @@ sub delete_all_copies { $self->_trigger_copies; } -sub init_config { - my $self = shift; - $self->config(Slic3r::Config->merge($self->print->config, $self->config_overrides)); -} - sub layer_count { my $self = shift; return scalar @{ $self->layers }; diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm index 559fce3c5..2b5d445de 100644 --- a/lib/Slic3r/Print/Region.pm +++ b/lib/Slic3r/Print/Region.pm @@ -8,7 +8,7 @@ use Slic3r::Flow ':roles'; # sharing the same config (including the same assigned extruder(s)) has 'print' => (is => 'ro', required => 1, weak_ref => 1); -has 'config' => (is => 'ro', required => 1); +has 'config' => (is => 'ro', required => 1); # Slic3r::Config::RegionConfig sub flow { my ($self, $role, $layer_height, $bridge, $first_layer, $width) = @_; diff --git a/xs/src/Config.cpp b/xs/src/Config.cpp index 828795c0c..58c9343e2 100644 --- a/xs/src/Config.cpp +++ b/xs/src/Config.cpp @@ -35,6 +35,14 @@ ConfigBase::serialize(const t_config_option_key opt_key) { void ConfigBase::set_deserialize(const t_config_option_key opt_key, std::string str) { + if (this->def->count(opt_key) == 0) throw "Calling set_deserialize() on unknown option"; + ConfigOptionDef* optdef = &(*this->def)[opt_key]; + if (!optdef->shortcut.empty()) { + for (std::vector::iterator it = optdef->shortcut.begin(); it != optdef->shortcut.end(); ++it) + this->set_deserialize(*it, str); + return; + } + ConfigOption* opt = this->option(opt_key, true); assert(opt != NULL); opt->deserialize(str); @@ -61,6 +69,20 @@ ConfigBase::get_abs_value(const t_config_option_key opt_key) { } } +double +ConfigBase::get_abs_value(const t_config_option_key opt_key, double ratio_over) { + // get stored option value + ConfigOptionFloatOrPercent* opt = dynamic_cast(this->option(opt_key)); + assert(opt != NULL); + + // compute absolute value + if (opt->percent) { + return ratio_over * opt->value / 100; + } else { + return opt->value; + } +} + #ifdef SLIC3RXS SV* ConfigBase::as_hash() { diff --git a/xs/src/Config.hpp b/xs/src/Config.hpp index e222651c8..9b9a7bf95 100644 --- a/xs/src/Config.hpp +++ b/xs/src/Config.hpp @@ -393,6 +393,7 @@ class ConfigBase std::string serialize(const t_config_option_key opt_key); void set_deserialize(const t_config_option_key opt_key, std::string str); double get_abs_value(const t_config_option_key opt_key); + double get_abs_value(const t_config_option_key opt_key, double ratio_over); #ifdef SLIC3RXS SV* as_hash(); diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index 38eb09ba1..0c9678206 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -986,7 +986,7 @@ class PrintRegionConfig : public virtual StaticConfig ConfigOptionFloatOrPercent top_infill_extrusion_width; ConfigOptionInt top_solid_layers; - PrintObjectConfig() { + PrintRegionConfig() { this->def = &PrintConfigDef::def; this->bottom_solid_layers.value = 3; @@ -1135,7 +1135,7 @@ class PrintConfig : public virtual StaticConfig ConfigOptionFloat z_offset; PrintConfig() { - this->def = &PrintConfig::PrintConfigDef; + this->def = &PrintConfigDef::def; this->avoid_crossing_perimeters.value = false; this->bed_size.point = Pointf(200,200); diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 3f053061d..80b00c116 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -15,15 +15,13 @@ void set_deserialize(t_config_option_key opt_key, std::string str); std::string serialize(t_config_option_key opt_key); double get_abs_value(t_config_option_key opt_key); + double get_abs_value(t_config_option_key opt_key, double ratio_over); void apply(DynamicPrintConfig* other) %code{% THIS->apply(*other, true); %}; void apply_static(PrintConfig* other) %code{% THIS->apply(*other, true); %}; std::vector get_keys() %code{% THIS->keys(&RETVAL); %}; -%{ - -%} }; %name{Slic3r::Config::Print} class PrintConfig { @@ -36,13 +34,45 @@ void set_deserialize(t_config_option_key opt_key, std::string str); std::string serialize(t_config_option_key opt_key); double get_abs_value(t_config_option_key opt_key); + double get_abs_value(t_config_option_key opt_key, double ratio_over); void apply_dynamic(DynamicPrintConfig* other) %code{% THIS->apply(*other, true); %}; std::vector get_keys() %code{% THIS->keys(&RETVAL); %}; -%{ +}; -%} +%name{Slic3r::Config::Print} class PrintConfig { + PrintConfig(); + ~PrintConfig(); + bool has(t_config_option_key opt_key); + SV* as_hash(); + SV* get(t_config_option_key opt_key); + void set(t_config_option_key opt_key, SV* value); + void set_deserialize(t_config_option_key opt_key, std::string str); + std::string serialize(t_config_option_key opt_key); + double get_abs_value(t_config_option_key opt_key); + double get_abs_value(t_config_option_key opt_key, double ratio_over); + void apply_dynamic(DynamicPrintConfig* other) + %code{% THIS->apply(*other, true); %}; + std::vector get_keys() + %code{% THIS->keys(&RETVAL); %}; +}; + +%name{Slic3r::Config::Print} class PrintConfig { + PrintConfig(); + ~PrintConfig(); + bool has(t_config_option_key opt_key); + SV* as_hash(); + SV* get(t_config_option_key opt_key); + void set(t_config_option_key opt_key, SV* value); + void set_deserialize(t_config_option_key opt_key, std::string str); + std::string serialize(t_config_option_key opt_key); + double get_abs_value(t_config_option_key opt_key); + double get_abs_value(t_config_option_key opt_key, double ratio_over); + void apply_dynamic(DynamicPrintConfig* other) + %code{% THIS->apply(*other, true); %}; + std::vector get_keys() + %code{% THIS->keys(&RETVAL); %}; }; %package{Slic3r::Config}; @@ -53,8 +83,7 @@ PROTOTYPES: DISABLE SV* print_config_def() CODE: - PrintConfig config; - t_optiondef_map* def = config.def; + t_optiondef_map* def = &PrintConfigDef::def; HV* options_hv = newHV(); for (t_optiondef_map::iterator oit = def->begin(); oit != def->end(); ++oit) { From e2f1040a76e9ad3e7d08e9588dc527895c28e017 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 2 Jan 2014 10:44:54 +0100 Subject: [PATCH 05/19] More work --- lib/Slic3r.pm | 3 +++ lib/Slic3r/Config.pm | 27 +++++++++++++++++--- lib/Slic3r/Layer/Region.pm | 10 ++++---- lib/Slic3r/Print.pm | 14 +++++------ lib/Slic3r/Print/Object.pm | 44 ++++++++++++++++++-------------- lib/Slic3r/Print/Region.pm | 8 +++--- xs/src/PrintConfig.hpp | 13 +++++++--- xs/t/15_config.t | 6 ++--- xs/xsp/Config.xsp | 51 ++++++++++++++++++++++++++++++-------- xs/xsp/my.map | 3 +++ xs/xsp/typemap.xspt | 3 +++ 11 files changed, 126 insertions(+), 56 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 1a1fa6f70..8ab34ca34 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -132,7 +132,10 @@ sub thread_cleanup { # prevent destruction of shared objects no warnings 'redefine'; *Slic3r::Config::DESTROY = sub {}; + *Slic3r::Config::Full::DESTROY = sub {}; *Slic3r::Config::Print::DESTROY = sub {}; + *Slic3r::Config::PrintObject::DESTROY = sub {}; + *Slic3r::Config::PrintRegion::DESTROY = sub {}; *Slic3r::ExPolygon::DESTROY = sub {}; *Slic3r::ExPolygon::Collection::DESTROY = sub {}; *Slic3r::ExtrusionLoop::DESTROY = sub {}; diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index bd924448b..9f5de4f4b 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -15,16 +15,25 @@ our $Options = print_config_def(); { no strict 'refs'; for my $opt_key (keys %$Options) { - *{$opt_key} = sub { $_[0]->get($opt_key) }; + *{$opt_key} = sub { $_[0]->_get($opt_key) }; } } +sub _get { + my ($self, $opt_key) = @_; + use XXX; + if (!defined first { $_ eq $opt_key } @{$self->get_keys}) { + ZZZ $opt_key; + } + return $self->get($opt_key); +} + sub new_from_defaults { my $class = shift; my (@opt_keys) = @_; my $self = $class->new; - my $defaults = Slic3r::Config::Print->new; + my $defaults = Slic3r::Config::Full->new; if (@opt_keys) { $self->set($_, $defaults->get($_)) for @opt_keys; } else { @@ -309,7 +318,7 @@ sub validate { && !$self->default_acceleration; # general validation, quick and dirty - foreach my $opt_key (keys %$Options) { + foreach my $opt_key (@{$self->get_keys}) { my $opt = $Options->{$opt_key}; next unless defined $self->$opt_key; next unless defined $opt->{cli} && $opt->{cli} =~ /=(.+)$/; @@ -431,4 +440,16 @@ sub read_ini { return $ini; } +package Slic3r::Config::Print; +use parent 'Slic3r::Config'; + +package Slic3r::Config::PrintObject; +use parent 'Slic3r::Config'; + +package Slic3r::Config::PrintRegion; +use parent 'Slic3r::Config'; + +package Slic3r::Config::Full; +use parent 'Slic3r::Config'; + 1; diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index 1f27d143c..ce22d9c88 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -14,9 +14,9 @@ has 'layer' => ( is => 'ro', weak_ref => 1, required => 1, - handles => [qw(id slice_z print_z height config)], + handles => [qw(id slice_z print_z height object print)], ); -has 'region' => (is => 'ro', required => 1, handles => [qw(extruders)]); +has 'region' => (is => 'ro', required => 1, handles => [qw(config)]); has 'infill_area_threshold' => (is => 'lazy'); has 'overhang_width' => (is => 'lazy'); @@ -168,7 +168,7 @@ sub make_perimeters { @offsets = @{offset2(\@last, -(1.5*$pspacing - 1), +(0.5*$pspacing - 1))}; # look for gaps - if ($self->config->gap_fill_speed > 0 && $self->config->fill_density > 0) { + if ($self->print->config->gap_fill_speed > 0 && $self->config->fill_density > 0) { my $diff = diff_ex( offset(\@last, -0.5*$pspacing), offset(\@offsets, +0.5*$pspacing), @@ -277,8 +277,8 @@ sub make_perimeters { # we continue inwards after having finished the brim # TODO: add test for perimeter order @loops = reverse @loops - if $self->config->external_perimeters_first - || ($self->layer->id == 0 && $self->config->brim_width > 0); + if $self->print->config->external_perimeters_first + || ($self->layer->id == 0 && $self->print->config->brim_width > 0); # append perimeters $self->perimeters->append(@loops); diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 4c9b9d79e..d490597db 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -70,9 +70,9 @@ sub init_config { sub apply_config { my ($self, $config) = @_; - $self->config->apply($config); - $self->default_object_config->apply($config); - $self->default_region_config->apply($config); + $self->config->apply_dynamic($config); + $self->default_object_config->apply_dynamic($config); + $self->default_region_config->apply_dynamic($config); $self->init_config; } @@ -94,14 +94,14 @@ sub add_model_object { my $volume = $object->volumes->[$volume_id]; # get the config applied to this volume: start from our global defaults - my $config = Slic3r::Config::RegionConfig->new; + my $config = Slic3r::Config::PrintRegion->new; $config->apply($self->default_region_config); # override the defaults with per-object config and then with per-material config - $config->apply($object->config); + $config->apply_dynamic($object->config); if (defined $volume->material_id) { my $material_config = $object->model->materials->{ $volume->material_id }->config; - $config->apply($material_config); + $config->apply_dynamic($material_config); } # find an existing print region with the same config @@ -139,7 +139,7 @@ sub add_model_object { # apply config to print object $o->config->apply($self->default_object_config); - $o->config->apply($object->config); + $o->config->apply_dynamic($object->config); # store print object at the given position if (defined $obj_idx) { diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 2e9f3dfe0..600a0dcc8 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -13,7 +13,7 @@ has 'print' => (is => 'ro', weak_ref => 1, required => 1); has 'model_object' => (is => 'ro', required => 1); has 'region_volumes' => (is => 'rw', default => sub { [] }); # by region_id has 'copies' => (is => 'ro'); # Slic3r::Point objects in scaled G-code coordinates -has 'config' => (is => 'rw', required => 1); # Slic3r::Config::PrintObject +has 'config' => (is => 'ro', default => sub { Slic3r::Config::PrintObject->new }); has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ] has 'size' => (is => 'rw'); # XYZ in scaled coordinates @@ -273,8 +273,8 @@ sub slice { } # simplify slices if required - if ($self->config->resolution) { - $self->_simplify_slices(scale($self->config->resolution)); + if ($self->print->config->resolution) { + $self->_simplify_slices(scale($self->print->config->resolution)); } } @@ -288,8 +288,11 @@ sub make_perimeters { # but we don't generate any extra perimeter if fill density is zero, as they would be floating # inside the object - infill_only_where_needed should be the method of choice for printing # hollow objects - if ($self->config->extra_perimeters && $self->config->perimeters > 0 && $self->config->fill_density > 0) { - for my $region_id (0 .. ($self->print->regions_count-1)) { + for my $region_id (0 .. ($self->print->regions_count-1)) { + my $region = $self->print->regions->[$region_id]; + my $region_perimeters = $region->config->perimeters; + + if ($region->config->extra_perimeters && $region_perimeters > 0 && $region->config->fill_density > 0) { for my $layer_id (0 .. $self->layer_count-2) { my $layerm = $self->layers->[$layer_id]->regions->[$region_id]; my $upper_layerm = $self->layers->[$layer_id+1]->regions->[$region_id]; @@ -298,7 +301,7 @@ sub make_perimeters { my $overlap = $perimeter_spacing; # one perimeter my $diff = diff( - offset([ map @{$_->expolygon}, @{$layerm->slices} ], -($self->config->perimeters * $perimeter_spacing)), + offset([ map @{$_->expolygon}, @{$layerm->slices} ], -($region_perimeters * $perimeter_spacing)), offset([ map @{$_->expolygon}, @{$upper_layerm->slices} ], -$overlap), ); next if !@$diff; @@ -322,8 +325,8 @@ sub make_perimeters { # of our slice $extra_perimeters++; my $hypothetical_perimeter = diff( - offset($slice->expolygon->arrayref, -($perimeter_spacing * ($self->config->perimeters + $extra_perimeters-1))), - offset($slice->expolygon->arrayref, -($perimeter_spacing * ($self->config->perimeters + $extra_perimeters))), + offset($slice->expolygon->arrayref, -($perimeter_spacing * ($region_perimeters + $extra_perimeters-1))), + offset($slice->expolygon->arrayref, -($perimeter_spacing * ($region_perimeters + $extra_perimeters))), ); last CYCLE if !@$hypothetical_perimeter; # no extra perimeter is possible @@ -339,7 +342,7 @@ sub make_perimeters { } Slic3r::parallelize( - threads => $self->config->threads, + threads => $self->print->config->threads, items => sub { 0 .. ($self->layer_count-1) }, thread_cb => sub { my $q = shift; @@ -376,7 +379,7 @@ sub detect_surfaces_type { ); # collapse very narrow parts (using the safety offset in the diff is not enough) - my $offset = $layerm->perimeter_flow->scaled_width / 10; + my $offset = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_width / 10; return map Slic3r::Surface->new(expolygon => $_, surface_type => $result_type), @{ offset2_ex($diff, -$offset, +$offset) }; }; @@ -609,8 +612,8 @@ sub discover_horizontal_shells { for (my $i = 0; $i < $self->layer_count; $i++) { my $layerm = $self->layers->[$i]->regions->[$region_id]; - if ($self->config->solid_infill_every_layers && $self->config->fill_density > 0 - && ($i % $self->config->solid_infill_every_layers) == 0) { + if ($layerm->config->solid_infill_every_layers && $layerm->config->fill_density > 0 + && ($i % $layerm->config->solid_infill_every_layers) == 0) { $_->surface_type(S_TYPE_INTERNALSOLID) for @{$layerm->fill_surfaces->filter_by_type(S_TYPE_INTERNAL)}; } @@ -632,8 +635,8 @@ sub discover_horizontal_shells { Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == S_TYPE_TOP) ? 'top' : 'bottom'; my $solid_layers = ($type == S_TYPE_TOP) - ? $self->config->top_solid_layers - : $self->config->bottom_solid_layers; + ? $layerm->config->top_solid_layers + : $layerm->config->bottom_solid_layers; NEIGHBOR: for (my $n = ($type == S_TYPE_TOP) ? $i-1 : $i+1; abs($n - $i) <= $solid_layers-1; ($type == S_TYPE_TOP) ? $n-- : $n++) { @@ -668,7 +671,7 @@ sub discover_horizontal_shells { # get a triangle in $too_narrow; if we grow it below then the shell # would have a different shape from the external surface and we'd still # have the same angle, so the next shell would be grown even more and so on. - my $margin = 3 * $layerm->solid_infill_flow->scaled_width; # require at least this size + my $margin = 3 * $layerm->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width; # require at least this size my $too_narrow = diff( $new_internal_solid, offset2($new_internal_solid, -$margin, +$margin, CLIPPER_OFFSET_SCALE, JT_MITER, 5), @@ -677,7 +680,7 @@ sub discover_horizontal_shells { # if some parts are going to collapse, use a different strategy according to fill density if (@$too_narrow) { - if ($self->config->fill_density > 0) { + if ($layerm->config->fill_density > 0) { # if we have internal infill, grow the collapsing parts and add the extra area to # the neighbor layer as well as to our original surfaces so that we support this # additional area in the next shell too @@ -741,13 +744,16 @@ sub discover_horizontal_shells { # combine fill surfaces across layers sub combine_infill { my $self = shift; - return unless $self->config->infill_every_layers > 1 && $self->config->fill_density > 0; - my $every = $self->config->infill_every_layers; + + return unless defined first { $_->config->infill_every_layers > 1 && $_->config->fill_density > 0 } @{$self->print->regions}; my $layer_count = $self->layer_count; my @layer_heights = map $self->layers->[$_]->height, 0 .. $layer_count-1; for my $region_id (0 .. ($self->print->regions_count-1)) { + my $region = $self->print->regions->[$region_id]; + my $every = $region->config->infill_every_layers; + # limit the number of combined layers to the maximum height allowed by this regions' nozzle my $nozzle_diameter = $self->print->regions->[$region_id]->extruders->{infill}->nozzle_diameter; @@ -805,7 +811,7 @@ sub combine_infill { + $layerms[-1]->perimeter_flow->scaled_width / 2 # Because fill areas for rectilinear and honeycomb are grown # later to overlap perimeters, we need to counteract that too. - + (($type == S_TYPE_INTERNALSOLID || $self->config->fill_pattern =~ /(rectilinear|honeycomb)/) + + (($type == S_TYPE_INTERNALSOLID || $region->config->fill_pattern =~ /(rectilinear|honeycomb)/) ? $layerms[-1]->solid_infill_flow->scaled_width * &Slic3r::INFILL_OVERLAP_OVER_SPACING : 0) )}, @$intersection; diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm index 2b5d445de..4cb929e0b 100644 --- a/lib/Slic3r/Print/Region.pm +++ b/lib/Slic3r/Print/Region.pm @@ -8,7 +8,7 @@ use Slic3r::Flow ':roles'; # sharing the same config (including the same assigned extruder(s)) has 'print' => (is => 'ro', required => 1, weak_ref => 1); -has 'config' => (is => 'ro', required => 1); # Slic3r::Config::RegionConfig +has 'config' => (is => 'ro', default => sub { Slic3r::Config::PrintRegion->new}); sub flow { my ($self, $role, $layer_height, $bridge, $first_layer, $width) = @_; @@ -21,7 +21,7 @@ sub flow { if (!defined $config_width) { # get extrusion width from configuration # (might be an absolute value, or a percent value, or zero for auto) - if ($first_layer && $self->config->first_layer_extrusion_width != 0) { + if ($first_layer && $self->config->first_layer_extrusion_width ne '0') { $config_width = $self->config->first_layer_extrusion_width; } elsif ($role == FLOW_ROLE_PERIMETER) { $config_width = $self->config->perimeter_extrusion_width; @@ -40,9 +40,9 @@ sub flow { # to the flow role requested my $extruder; # 1-based if ($role == FLOW_ROLE_PERIMETER) { - $config_width = $self->config->perimeter_extruder; + $extruder = $self->config->perimeter_extruder; } elsif ($role == FLOW_ROLE_INFILL || $role == FLOW_ROLE_SOLID_INFILL || $role == FLOW_ROLE_TOP_SOLID_INFILL) { - $config_width = $self->config->infill_extruder; + $extruder = $self->config->infill_extruder; } else { die "Unknown role $role"; } diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index 0c9678206..4643c8b70 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -956,7 +956,6 @@ class PrintObjectConfig : public virtual StaticConfig if (opt_key == "support_material_speed") return &this->support_material_speed; if (opt_key == "support_material_threshold") return &this->support_material_threshold; - if (create) throw "Attempt to create non-existing option in StaticConfig object"; return NULL; }; }; @@ -1039,7 +1038,6 @@ class PrintRegionConfig : public virtual StaticConfig if (opt_key == "top_infill_extrusion_width") return &this->top_infill_extrusion_width; if (opt_key == "top_solid_layers") return &this->top_solid_layers; - if (create) throw "Attempt to create non-existing option in StaticConfig object"; return NULL; }; }; @@ -1332,7 +1330,6 @@ class PrintConfig : public virtual StaticConfig if (opt_key == "wipe") return &this->wipe; if (opt_key == "z_offset") return &this->z_offset; - if (create) throw "Attempt to create non-existing option in StaticConfig object"; return NULL; }; }; @@ -1345,7 +1342,15 @@ class DynamicPrintConfig : public DynamicConfig }; }; -class FullPrintConfig : public PrintObjectConfig, public PrintRegionConfig, public PrintConfig {}; +class FullPrintConfig : public PrintObjectConfig, public PrintRegionConfig, public PrintConfig { + ConfigOption* option(const t_config_option_key opt_key, bool create = false) { + ConfigOption* opt; + if ((opt = PrintObjectConfig::option(opt_key, create)) != NULL) return opt; + if ((opt = PrintRegionConfig::option(opt_key, create)) != NULL) return opt; + if ((opt = PrintConfig::option(opt_key, create)) != NULL) return opt; + return NULL; + }; +}; } diff --git a/xs/t/15_config.t b/xs/t/15_config.t index 699f115f8..4e8f6e68a 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -6,7 +6,7 @@ use warnings; use Slic3r::XS; use Test::More tests => 79; -foreach my $config (Slic3r::Config->new, Slic3r::Config::Print->new) { +foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) { $config->set('layer_height', 0.3); ok abs($config->get('layer_height') - 0.3) < 1e-4, 'set/get float'; is $config->serialize('layer_height'), '0.3', 'serialize float'; @@ -94,13 +94,13 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Print->new) { # test that no crash happens when using set_deserialize() with a key that hasn't been set() yet $config->set_deserialize('filament_diameter', '3'); - my $config2 = Slic3r::Config::Print->new; + my $config2 = Slic3r::Config::Full->new; $config2->apply_dynamic($config); is $config2->get('perimeters'), 2, 'apply_dynamic'; } { - my $config = Slic3r::Config::Print->new; + my $config = Slic3r::Config::Full->new; my $config2 = Slic3r::Config->new; $config2->apply_static($config); is $config2->get('perimeters'), Slic3r::Config::print_config_def()->{perimeters}{default}, 'apply_static and print_config_def'; diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 80b00c116..5c3966e13 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -15,7 +15,8 @@ void set_deserialize(t_config_option_key opt_key, std::string str); std::string serialize(t_config_option_key opt_key); double get_abs_value(t_config_option_key opt_key); - double get_abs_value(t_config_option_key opt_key, double ratio_over); + %name{get_abs_value_over} + double get_abs_value(t_config_option_key opt_key, double ratio_over); void apply(DynamicPrintConfig* other) %code{% THIS->apply(*other, true); %}; void apply_static(PrintConfig* other) @@ -34,16 +35,17 @@ void set_deserialize(t_config_option_key opt_key, std::string str); std::string serialize(t_config_option_key opt_key); double get_abs_value(t_config_option_key opt_key); - double get_abs_value(t_config_option_key opt_key, double ratio_over); + %name{get_abs_value_over} + double get_abs_value(t_config_option_key opt_key, double ratio_over); void apply_dynamic(DynamicPrintConfig* other) %code{% THIS->apply(*other, true); %}; std::vector get_keys() %code{% THIS->keys(&RETVAL); %}; }; -%name{Slic3r::Config::Print} class PrintConfig { - PrintConfig(); - ~PrintConfig(); +%name{Slic3r::Config::PrintRegion} class PrintRegionConfig { + PrintRegionConfig(); + ~PrintRegionConfig(); bool has(t_config_option_key opt_key); SV* as_hash(); SV* get(t_config_option_key opt_key); @@ -51,16 +53,19 @@ void set_deserialize(t_config_option_key opt_key, std::string str); std::string serialize(t_config_option_key opt_key); double get_abs_value(t_config_option_key opt_key); - double get_abs_value(t_config_option_key opt_key, double ratio_over); + %name{get_abs_value_over} + double get_abs_value(t_config_option_key opt_key, double ratio_over); + void apply(PrintRegionConfig* other) + %code{% THIS->apply(*other, true); %}; void apply_dynamic(DynamicPrintConfig* other) %code{% THIS->apply(*other, true); %}; std::vector get_keys() %code{% THIS->keys(&RETVAL); %}; }; -%name{Slic3r::Config::Print} class PrintConfig { - PrintConfig(); - ~PrintConfig(); +%name{Slic3r::Config::PrintObject} class PrintObjectConfig { + PrintObjectConfig(); + ~PrintObjectConfig(); bool has(t_config_option_key opt_key); SV* as_hash(); SV* get(t_config_option_key opt_key); @@ -68,7 +73,30 @@ void set_deserialize(t_config_option_key opt_key, std::string str); std::string serialize(t_config_option_key opt_key); double get_abs_value(t_config_option_key opt_key); - double get_abs_value(t_config_option_key opt_key, double ratio_over); + %name{get_abs_value_over} + double get_abs_value(t_config_option_key opt_key, double ratio_over); + void apply(PrintObjectConfig* other) + %code{% THIS->apply(*other, true); %}; + void apply_dynamic(DynamicPrintConfig* other) + %code{% THIS->apply(*other, true); %}; + std::vector get_keys() + %code{% THIS->keys(&RETVAL); %}; +}; + +%name{Slic3r::Config::Full} class FullPrintConfig { + FullPrintConfig(); + ~FullPrintConfig(); + bool has(t_config_option_key opt_key); + SV* as_hash(); + SV* get(t_config_option_key opt_key); + void set(t_config_option_key opt_key, SV* value); + void set_deserialize(t_config_option_key opt_key, std::string str); + std::string serialize(t_config_option_key opt_key); + double get_abs_value(t_config_option_key opt_key); + %name{get_abs_value_over} + double get_abs_value(t_config_option_key opt_key, double ratio_over); + void apply(PrintObjectConfig* other) + %code{% THIS->apply(*other, true); %}; void apply_dynamic(DynamicPrintConfig* other) %code{% THIS->apply(*other, true); %}; std::vector get_keys() @@ -83,7 +111,8 @@ PROTOTYPES: DISABLE SV* print_config_def() CODE: - t_optiondef_map* def = &PrintConfigDef::def; + FullPrintConfig config; + t_optiondef_map* def = config.def; HV* options_hv = newHV(); for (t_optiondef_map::iterator oit = def->begin(); oit != def->end(); ++oit) { diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 7c370ac10..666cbf616 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -2,7 +2,10 @@ std::vector T_STD_VECTOR_INT t_config_option_key T_STD_STRING DynamicPrintConfig* O_OBJECT +PrintObjectConfig* O_OBJECT +PrintRegionConfig* O_OBJECT PrintConfig* O_OBJECT +FullPrintConfig* O_OBJECT ZTable* O_OBJECT TriangleMesh* O_OBJECT Point* O_OBJECT diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index eb6302e7d..25c4c4d12 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -9,7 +9,10 @@ %typemap{AV*}; %typemap{Point*}; %typemap{DynamicPrintConfig*}; +%typemap{PrintObjectConfig*}; +%typemap{PrintRegionConfig*}; %typemap{PrintConfig*}; +%typemap{FullPrintConfig*}; %typemap{ExPolygon*}; %typemap{ExPolygonCollection*}; %typemap{Line*}; From 5bf0942f4559d5458bc3a50b1da76cd1bd0e7a16 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 2 Jan 2014 17:24:23 +0100 Subject: [PATCH 06/19] Satisfy test suite and CLI --- lib/Slic3r/Config.pm | 46 +++++-- lib/Slic3r/Extruder.pm | 4 +- lib/Slic3r/Fill.pm | 23 ++-- lib/Slic3r/Flow.pm | 5 +- lib/Slic3r/GCode.pm | 187 ++++++++++++++-------------- lib/Slic3r/GCode/ArcFitting.pm | 2 +- lib/Slic3r/GCode/CoolingBuffer.pm | 2 +- lib/Slic3r/GCode/Layer.pm | 67 +++++----- lib/Slic3r/Print.pm | 109 ++++++---------- lib/Slic3r/Print/Object.pm | 16 ++- lib/Slic3r/Print/Region.pm | 2 +- lib/Slic3r/Print/SupportMaterial.pm | 54 ++++---- lib/Slic3r/Test.pm | 11 +- slic3r.pl | 2 +- t/cooling.t | 10 +- t/gcode.t | 5 +- t/shells.t | 3 + t/support.t | 8 +- xs/src/PrintConfig.hpp | 15 ++- xs/xsp/Config.xsp | 1 + 20 files changed, 300 insertions(+), 272 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 9f5de4f4b..21fa2322b 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -15,18 +15,18 @@ our $Options = print_config_def(); { no strict 'refs'; for my $opt_key (keys %$Options) { - *{$opt_key} = sub { $_[0]->_get($opt_key) }; + *{$opt_key} = sub { $_[0]->get($opt_key) }; } } -sub _get { - my ($self, $opt_key) = @_; - use XXX; - if (!defined first { $_ eq $opt_key } @{$self->get_keys}) { - ZZZ $opt_key; - } - return $self->get($opt_key); -} +# sub _get { +# my ($self, $opt_key) = @_; +# use XXX; +# if (!defined first { $_ eq $opt_key } @{$self->get_keys}) { +# ZZZ $opt_key; +# } +# return $self->get($opt_key); +# } sub new_from_defaults { my $class = shift; @@ -200,7 +200,8 @@ sub diff { return [@diff]; } -# this method is idempotent by design +# this method is idempotent by design and only applies to ::DynamicConfig or ::Full +# objects because it performs cross checks sub validate { my $self = shift; @@ -317,6 +318,31 @@ sub validate { if ($self->perimeter_acceleration || $self->infill_acceleration || $self->bridge_acceleration || $self->first_layer_acceleration) && !$self->default_acceleration; + # --spiral-vase + if ($self->spiral_vase) { + # Note that we might want to have more than one perimeter on the bottom + # solid layers. + die "Can't make more than one perimeter when spiral vase mode is enabled\n" + if $self->perimeters > 1; + + die "Can't make less than one perimeter when spiral vase mode is enabled\n" + if $self->perimeters < 1; + + die "Spiral vase mode is not compatible with non-zero fill density\n" + if $self->fill_density > 0; + + die "Spiral vase mode is not compatible with top solid layers\n" + if $self->top_solid_layers > 0; + + die "Spiral vase mode is not compatible with support material\n" + if $self->support_material || $self->support_material_enforce_layers > 0; + + # This should be enforce automatically only on spiral layers and + # done on the others + die "Spiral vase mode is not compatible with retraction on layer change\n" + if defined first { $_ } @{ $self->retract_layer_change }; + } + # general validation, quick and dirty foreach my $opt_key (@{$self->get_keys}) { my $opt = $Options->{$opt_key}; diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm index 1025c3459..9b8b1dc97 100644 --- a/lib/Slic3r/Extruder.pm +++ b/lib/Slic3r/Extruder.pm @@ -18,7 +18,7 @@ use constant OPTIONS => [qw( has 'id' => (is => 'rw', required => 1); has $_ => (is => 'ro', required => 1) for @{&OPTIONS}; -has 'config'=> (is => 'ro', required => 1); +has 'use_relative_e_distances' => (is => 'ro', default => sub {0}); has 'E' => (is => 'rw', default => sub {0} ); has 'absolute_E' => (is => 'rw', default => sub {0} ); @@ -56,7 +56,7 @@ sub scaled_wipe_distance { sub extrude { my ($self, $E) = @_; - $self->E(0) if $self->config->use_relative_e_distances; + $self->E(0) if $self->use_relative_e_distances; $self->absolute_E($self->absolute_E + $E); return $self->E($self->E + $E); } diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 42cbea13f..7969431c5 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -1,6 +1,7 @@ package Slic3r::Fill; use Moo; +use Slic3r::ExtrusionPath ':roles'; use Slic3r::Fill::ArchimedeanChords; use Slic3r::Fill::Base; use Slic3r::Fill::Concentric; @@ -11,7 +12,7 @@ use Slic3r::Fill::Line; use Slic3r::Fill::OctagramSpiral; use Slic3r::Fill::PlanePath; use Slic3r::Fill::Rectilinear; -use Slic3r::ExtrusionPath ':roles'; +use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(X Y PI scale chained_path); use Slic3r::Geometry::Clipper qw(union union_ex diff diff_ex intersection_ex offset offset2); use Slic3r::Surface ':types'; @@ -50,7 +51,10 @@ sub make_fill { my ($layerm) = @_; Slic3r::debugf "Filling layer %d:\n", $layerm->id; - my $fill_density = $layerm->config->fill_density; + + my $fill_density = $layerm->config->fill_density; + my $infill_flow = $layerm->flow(FLOW_ROLE_INFILL); + my $solid_infill_flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL); my @surfaces = (); @@ -95,7 +99,7 @@ sub make_fill { # we are going to grow such regions by overlapping them with the void (if any) # TODO: detect and investigate whether there could be narrow regions without # any void neighbors - my $distance_between_surfaces = $layerm->solid_infill_flow->scaled_spacing * &Slic3r::INFILL_OVERLAP_OVER_SPACING; + my $distance_between_surfaces = $infill_flow->scaled_spacing * &Slic3r::INFILL_OVERLAP_OVER_SPACING; { my $collapsed = diff( [ map @{$_->expolygon}, @surfaces ], @@ -133,11 +137,10 @@ sub make_fill { my $filler = $layerm->config->fill_pattern; my $density = $fill_density; my $flow = ($surface->surface_type == S_TYPE_TOP) - ? $layerm->top_infill_flow + ? $layerm->flow(FLOW_ROLE_TOP_SOLID_INFILL) : $surface->is_solid - ? $layerm->solid_infill_flow - : $layerm->infill_flow; - my $flow_spacing = $flow->spacing; + ? $solid_infill_flow + : $infill_flow; my $is_bridge = $layerm->id > 0 && $surface->is_bridge; my $is_solid = $surface->is_solid; @@ -147,7 +150,7 @@ sub make_fill { $filler = $layerm->config->solid_fill_pattern; if ($is_bridge) { $filler = 'rectilinear'; - $flow_spacing = $layerm->extruders->{infill}->bridge_flow->spacing; + $flow = $layerm->flow(FLOW_ROLE_SOLID_INFILL, 1); } elsif ($surface->surface_type == S_TYPE_INTERNALSOLID) { $filler = 'rectilinear'; } @@ -155,6 +158,8 @@ sub make_fill { next SURFACE unless $density > 0; } + my $flow_spacing = $flow->spacing; + my $f = $self->filler($filler); $f->layer_id($layerm->id); $f->angle($layerm->config->fill_angle); @@ -166,7 +171,7 @@ sub make_fill { next unless @polylines; # ugly hack(tm) to get the right amount of flow (GCode.pm should be fixed) - $params->{flow_spacing} = $layerm->extruders->{infill}->bridge_flow->width if $is_bridge; + $params->{flow_spacing} = $flow->width if $is_bridge; # save into layer push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new; diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index 0a9747fa8..3930dce52 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -36,7 +36,10 @@ sub BUILDARGS { $args{width} = $args{layer_height} * $1 / 100; } $args{spacing} = $self->_spacing(@args{qw(width nozzle_diameter layer_height bridge_flow_ratio)}); - %args = @args{qw(width spacing)}; + %args = ( + width => $args{width}, + spacing => $args{spacing}, + ); } return {%args}; diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index da55942f6..66175260f 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -3,11 +3,12 @@ use Moo; use List::Util qw(min first); use Slic3r::ExtrusionPath ':roles'; +use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(epsilon scale unscale scaled_epsilon points_coincide PI X Y B); use Slic3r::Geometry::Clipper qw(union_ex); use Slic3r::Surface ':types'; -has 'config' => (is => 'ro', required => 1); +has 'print_config' => (is => 'ro', default => sub { Slic3r::Config::Print->new }); has 'extra_variables' => (is => 'rw', default => sub {{}}); has 'extruders' => (is => 'ro', required => 1); has 'multiple_extruders' => (is => 'lazy'); @@ -16,6 +17,7 @@ has 'enable_loop_clipping' => (is => 'rw', default => sub {1}); has 'enable_wipe' => (is => 'lazy'); # at least one extruder has wipe enabled has 'layer_count' => (is => 'ro', required => 1 ); has 'layer' => (is => 'rw'); +has 'region' => (is => 'rw'); has '_layer_islands' => (is => 'rw'); has '_upper_layer_islands' => (is => 'rw'); has '_layer_overhangs' => (is => 'ro', default => sub { Slic3r::ExPolygon::Collection->new }); @@ -23,6 +25,8 @@ has 'shift_x' => (is => 'rw', default => sub {0} ); has 'shift_y' => (is => 'rw', default => sub {0} ); has 'z' => (is => 'rw'); has 'speed' => (is => 'rw'); +has '_extrusion_axis' => (is => 'rw'); +has '_retract_lift' => (is => 'rw'); has 'speeds' => (is => 'lazy'); # mm/min has 'external_mp' => (is => 'rw'); @@ -38,12 +42,19 @@ has 'last_f' => (is => 'rw', default => sub {""}); has 'last_fan_speed' => (is => 'rw', default => sub {0}); has 'wipe_path' => (is => 'rw'); +sub BUILD { + my ($self) = @_; + + $self->_extrusion_axis($self->print_config->get_extrusion_axis); + $self->_retract_lift($self->print_config->retract_lift->[0]); +} + sub _build_speeds { my $self = shift; return { - map { $_ => 60 * $self->config->get_value("${_}_speed") } + map { $_ => 60 * $self->print_config->get_value("${_}_speed") } qw(travel perimeter small_perimeter external_perimeter infill - solid_infill top_solid_infill support_material bridge gap_fill retract), + solid_infill top_solid_infill bridge gap_fill retract), }; } @@ -59,7 +70,6 @@ my %role_speeds = ( &EXTR_ROLE_BRIDGE => 'bridge', &EXTR_ROLE_INTERNALBRIDGE => 'solid_infill', &EXTR_ROLE_SKIRT => 'perimeter', - &EXTR_ROLE_SUPPORTMATERIAL => 'support_material', &EXTR_ROLE_GAPFILL => 'gap_fill', ); @@ -97,29 +107,29 @@ sub change_layer { $self->_layer_islands($layer->islands); $self->_upper_layer_islands($layer->upper_layer ? $layer->upper_layer->islands : []); $self->_layer_overhangs->clear; - if ($layer->id > 0 && ($layer->config->overhangs || $self->config->start_perimeters_at_non_overhang)) { + if ($layer->id > 0 && ($self->print_config->overhangs || $self->print_config->start_perimeters_at_non_overhang)) { $self->_layer_overhangs->append( # clone ExPolygons because they come from Surface objects but will be used outside here map $_->expolygon, map @{$_->slices->filter_by_type(S_TYPE_BOTTOM)}, @{$layer->regions} ); } - if ($self->config->avoid_crossing_perimeters) { + if ($self->print_config->avoid_crossing_perimeters) { $self->layer_mp(Slic3r::GCode::MotionPlanner->new( islands => union_ex([ map @$_, @{$layer->slices} ], 1), )); } my $gcode = ""; - if ($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/) { + if ($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/) { $gcode .= sprintf "M73 P%s%s\n", int(99 * ($layer->id / ($self->layer_count - 1))), - ($self->config->gcode_comments ? ' ; update progress' : ''); + ($self->print_config->gcode_comments ? ' ; update progress' : ''); } - if ($self->config->first_layer_acceleration) { + if ($self->print_config->first_layer_acceleration) { if ($layer->id == 0) { - $gcode .= $self->set_acceleration($self->config->first_layer_acceleration); + $gcode .= $self->set_acceleration($self->print_config->first_layer_acceleration); } elsif ($layer->id == 1) { - $gcode .= $self->set_acceleration($self->config->default_acceleration); + $gcode .= $self->set_acceleration($self->print_config->default_acceleration); } } @@ -133,7 +143,7 @@ sub move_z { my $gcode = ""; - $z += $self->config->z_offset; + $z += $self->print_config->z_offset; my $current_z = $self->z; my $nominal_z = defined $current_z ? ($current_z - $self->lifted) : undef; @@ -181,11 +191,11 @@ sub extrude_loop { # find candidate starting points # start looking for concave vertices not being overhangs my @concave = (); - if ($self->config->start_perimeters_at_concave_points) { + if ($self->print_config->start_perimeters_at_concave_points) { @concave = $polygon->concave_points; } my @candidates = (); - if ($self->config->start_perimeters_at_non_overhang) { + if ($self->print_config->start_perimeters_at_non_overhang) { @candidates = grep !$self->_layer_overhangs->contains_point($_), @concave; } if (!@candidates) { @@ -193,7 +203,7 @@ sub extrude_loop { @candidates = @concave; if (!@candidates) { # if none, look for any non-overhang vertex - if ($self->config->start_perimeters_at_non_overhang) { + if ($self->print_config->start_perimeters_at_non_overhang) { @candidates = grep !$self->_layer_overhangs->contains_point($_), @$polygon; } if (!@candidates) { @@ -206,9 +216,9 @@ sub extrude_loop { # find the point of the loop that is closest to the current extruder position # or randomize if requested my $last_pos = $self->last_pos; - if ($self->config->randomize_start && $loop->role == EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER) { - $last_pos = Slic3r::Point->new(scale $self->config->print_center->[X], scale $self->config->bed_size->[Y]); - $last_pos->rotate(rand(2*PI), $self->config->print_center); + if ($self->print_config->randomize_start && $loop->role == EXTR_ROLE_CONTOUR_INTERNAL_PERIMETER) { + $last_pos = Slic3r::Point->new(scale $self->print_config->print_center->[X], scale $self->print_config->bed_size->[Y]); + $last_pos->rotate(rand(2*PI), $self->print_config->print_center); } # split the loop at the starting point and make a path @@ -224,7 +234,7 @@ sub extrude_loop { my @paths = (); # detect overhanging/bridging perimeters - if ($self->layer->config->overhangs && $extrusion_path->is_perimeter && $self->_layer_overhangs->count > 0) { + if ($self->layer->print->config->overhangs && $extrusion_path->is_perimeter && $self->_layer_overhangs->count > 0) { # get non-overhang paths by subtracting overhangs from the loop push @paths, map $_->clone, @@ -234,7 +244,7 @@ sub extrude_loop { push @paths, map { $_->role(EXTR_ROLE_OVERHANG_PERIMETER); - $_->flow_spacing($self->extruder->bridge_flow->width); + $_->flow_spacing($self->region->flow(FLOW_ROLE_PERIMETER, undef, 1)->width); $_ } map $_->clone, @@ -259,7 +269,7 @@ sub extrude_loop { $self->wipe_path($extrusion_path->polyline->clone) if $self->enable_wipe; # make a little move inwards before leaving loop - if ($loop->role == EXTR_ROLE_EXTERNAL_PERIMETER && defined $self->layer && $self->layer->object->config->perimeters > 1) { + if ($loop->role == EXTR_ROLE_EXTERNAL_PERIMETER && defined $self->layer && $self->region->config->perimeters > 1) { # detect angle between last and first segment # the side depends on the original winding order of the polygon (left for contours, right for holes) my @points = $was_clockwise ? (-2, 1) : (1, -2); @@ -299,13 +309,13 @@ sub extrude_path { # adjust acceleration my $acceleration; - if (!$self->config->first_layer_acceleration || $self->layer->id != 0) { - if ($self->config->perimeter_acceleration && $path->is_perimeter) { - $acceleration = $self->config->perimeter_acceleration; - } elsif ($self->config->infill_acceleration && $path->is_fill) { - $acceleration = $self->config->infill_acceleration; - } elsif ($self->config->infill_acceleration && $path->is_bridge) { - $acceleration = $self->config->bridge_acceleration; + if (!$self->print_config->first_layer_acceleration || $self->layer->id != 0) { + if ($self->print_config->perimeter_acceleration && $path->is_perimeter) { + $acceleration = $self->print_config->perimeter_acceleration; + } elsif ($self->print_config->infill_acceleration && $path->is_fill) { + $acceleration = $self->print_config->infill_acceleration; + } elsif ($self->print_config->infill_acceleration && $path->is_bridge) { + $acceleration = $self->print_config->bridge_acceleration; } $gcode .= $self->set_acceleration($acceleration) if $acceleration; } @@ -322,15 +332,13 @@ sub extrude_path { # calculate extrusion length per distance unit my $e = $self->extruder->e_per_mm3 * $area; - $e = 0 if !$self->config->extrusion_axis; + $e = 0 if !$self->_extrusion_axis; # set speed $self->speed( $params{speed} || $role_speeds{$path->role} || die "Unknown role: " . $path->role ); my $F = $self->speeds->{$self->speed} // $self->speed; if ($self->layer->id == 0) { - $F = $self->config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/ - ? sprintf("%.3f", $F * $1/100) - : $self->config->first_layer_speed * 60; + $F = $self->print_config->get_abs_value_over('first_layer_speed', $F/60) * 60; } # extrude arc or line @@ -350,12 +358,12 @@ sub extrude_path { $gcode .= sprintf "G1 X%.3f Y%.3f", ($point->x * &Slic3r::SCALING_FACTOR) + $self->shift_x - $self->extruder->extruder_offset->[X], ($point->y * &Slic3r::SCALING_FACTOR) + $self->shift_y - $self->extruder->extruder_offset->[Y]; #** - $gcode .= sprintf(" %s%.5f", $self->config->extrusion_axis, $E) + $gcode .= sprintf(" %s%.5f", $self->_extrusion_axis, $E) if $E; $gcode .= " F$local_F" if $local_F; $gcode .= " ; $description" - if $self->config->gcode_comments; + if $self->print_config->gcode_comments; $gcode .= "\n"; # only include F in the first line @@ -369,19 +377,14 @@ sub extrude_path { $gcode .= ";_BRIDGE_FAN_END\n" if $path->is_bridge; $self->last_pos($path->last_point); - if ($self->config->cooling) { + if ($self->print_config->cooling) { my $path_time = $path_length / $F * 60; - if ($self->layer->id == 0) { - $path_time = $self->config->first_layer_speed =~ /^(\d+(?:\.\d+)?)%$/ - ? $path_time / ($1/100) - : $path_length / $self->config->first_layer_speed * 60; - } $self->elapsed_time($self->elapsed_time + $path_time); } # reset acceleration - $gcode .= $self->set_acceleration($self->config->default_acceleration) - if $acceleration && $self->config->default_acceleration; + $gcode .= $self->set_acceleration($self->print_config->default_acceleration) + if $acceleration && $self->print_config->default_acceleration; return $gcode; } @@ -400,7 +403,7 @@ sub travel_to { # skip retraction if the travel move is contained in an island in the current layer # *and* in an island in the upper layer (so that the ooze will not be visible) if ($travel->length < scale $self->extruder->retract_before_travel - || ($self->config->only_retract_when_crossing_perimeters + || ($self->print_config->only_retract_when_crossing_perimeters && (first { $_->contains_line($travel) } @{$self->_upper_layer_islands}) && (first { $_->contains_line($travel) } @{$self->_layer_islands})) || (defined $role && $role == EXTR_ROLE_SUPPORTMATERIAL && (first { $_->contains_line($travel) } @{$self->layer->support_islands})) @@ -408,7 +411,7 @@ sub travel_to { $self->straight_once(0); $self->speed('travel'); $gcode .= $self->G0($point, undef, 0, $comment || ""); - } elsif (!$self->config->avoid_crossing_perimeters || $self->straight_once) { + } elsif (!$self->print_config->avoid_crossing_perimeters || $self->straight_once) { $self->straight_once(0); $gcode .= $self->retract; $self->speed('travel'); @@ -441,7 +444,7 @@ sub _plan { my @travel = @{$mp->shortest_path($self->last_pos, $point)->lines}; # if the path is not contained in a single island we need to retract - my $need_retract = !$self->config->only_retract_when_crossing_perimeters; + my $need_retract = !$self->print_config->only_retract_when_crossing_perimeters; if (!$need_retract) { $need_retract = 1; foreach my $island (@{$self->_upper_layer_islands}) { @@ -481,14 +484,14 @@ sub retract { if ($self->extruder->wipe && $self->wipe_path) { my @points = @{$self->wipe_path}; $wipe_path = Slic3r::Polyline->new($self->last_pos, @{$self->wipe_path}[1..$#{$self->wipe_path}]); - $wipe_path->clip_end($wipe_path->length - $self->extruder->scaled_wipe_distance($self->config->travel_speed)); + $wipe_path->clip_end($wipe_path->length - $self->extruder->scaled_wipe_distance($self->print_config->travel_speed)); } # prepare moves my $retract = [undef, undef, -$length, $comment]; - my $lift = ($self->extruder->retract_lift == 0 || defined $params{move_z}) && !$self->lifted + my $lift = ($self->_retract_lift == 0 || defined $params{move_z}) && !$self->lifted ? undef - : [undef, $self->z + $self->extruder->retract_lift, 0, 'lift plate during travel']; + : [undef, $self->z + $self->_retract_lift, 0, 'lift plate during travel']; # check that we have a positive wipe length if ($wipe_path) { @@ -500,7 +503,7 @@ sub retract { my $segment_length = $line->length; # reduce retraction length a bit to avoid effective retraction speed to be greater than the configured one # due to rounding - my $e = $retract->[2] * ($segment_length / $self->extruder->scaled_wipe_distance($self->config->travel_speed)) * 0.95; + my $e = $retract->[2] * ($segment_length / $self->extruder->scaled_wipe_distance($self->print_config->travel_speed)) * 0.95; $retracted += $e; $gcode .= $self->G1($line->b, undef, $e, $retract->[3] . ";_WIPE"); } @@ -510,7 +513,7 @@ sub retract { $self->speed('retract'); $gcode .= $self->G1(undef, undef, $retract->[2] - $retracted, $comment); } - } elsif ($self->config->use_firmware_retraction) { + } elsif ($self->print_config->use_firmware_retraction) { $gcode .= "G10 ; retract\n"; } else { $self->speed('retract'); @@ -518,23 +521,23 @@ sub retract { } if (!$self->lifted) { $self->speed('travel'); - if (defined $params{move_z} && $self->extruder->retract_lift > 0) { - my $travel = [undef, $params{move_z} + $self->extruder->retract_lift, 0, 'move to next layer (' . $self->layer->id . ') and lift']; + if (defined $params{move_z} && $self->_retract_lift > 0) { + my $travel = [undef, $params{move_z} + $self->_retract_lift, 0, 'move to next layer (' . $self->layer->id . ') and lift']; $gcode .= $self->G0(@$travel); - $self->lifted($self->extruder->retract_lift); + $self->lifted($self->_retract_lift); } elsif ($lift) { $gcode .= $self->G1(@$lift); } } $self->extruder->retracted($self->extruder->retracted + $length); $self->extruder->restart_extra($restart_extra); - $self->lifted($self->extruder->retract_lift) if $lift; + $self->lifted($self->_retract_lift) if $lift; # reset extrusion distance during retracts # this makes sure we leave sufficient precision in the firmware $gcode .= $self->reset_e; - $gcode .= "M103 ; extruder off\n" if $self->config->gcode_flavor eq 'makerware'; + $gcode .= "M103 ; extruder off\n" if $self->print_config->gcode_flavor eq 'makerware'; return $gcode; } @@ -543,7 +546,7 @@ sub unretract { my ($self) = @_; my $gcode = ""; - $gcode .= "M101 ; extruder on\n" if $self->config->gcode_flavor eq 'makerware'; + $gcode .= "M101 ; extruder on\n" if $self->print_config->gcode_flavor eq 'makerware'; if ($self->lifted) { $self->speed('travel'); @@ -554,15 +557,15 @@ sub unretract { my $to_unretract = $self->extruder->retracted + $self->extruder->restart_extra; if ($to_unretract) { $self->speed('retract'); - if ($self->config->use_firmware_retraction) { + if ($self->print_config->use_firmware_retraction) { $gcode .= "G11 ; unretract\n"; - } elsif ($self->config->extrusion_axis) { + } elsif ($self->_extrusion_axis) { # use G1 instead of G0 because G0 will blend the restart with the previous travel move $gcode .= sprintf "G1 %s%.5f F%.3f", - $self->config->extrusion_axis, + $self->_extrusion_axis, $self->extruder->extrude($to_unretract), $self->extruder->retract_speed_mm_min; - $gcode .= " ; compensate retraction" if $self->config->gcode_comments; + $gcode .= " ; compensate retraction" if $self->print_config->gcode_comments; $gcode .= "\n"; } $self->extruder->retracted(0); @@ -574,11 +577,11 @@ sub unretract { sub reset_e { my ($self) = @_; - return "" if $self->config->gcode_flavor =~ /^(?:mach3|makerware|sailfish)$/; + return "" if $self->print_config->gcode_flavor =~ /^(?:mach3|makerware|sailfish)$/; $self->extruder->E(0) if $self->extruder; - return sprintf "G92 %s0%s\n", $self->config->extrusion_axis, ($self->config->gcode_comments ? ' ; reset extrusion distance' : '') - if $self->config->extrusion_axis && !$self->config->use_relative_e_distances; + return sprintf "G92 %s0%s\n", $self->_extrusion_axis, ($self->print_config->gcode_comments ? ' ; reset extrusion distance' : '') + if $self->_extrusion_axis && !$self->print_config->use_relative_e_distances; } sub set_acceleration { @@ -586,12 +589,12 @@ sub set_acceleration { return "" if !$acceleration; return sprintf "M204 S%s%s\n", - $acceleration, ($self->config->gcode_comments ? ' ; adjust acceleration' : ''); + $acceleration, ($self->print_config->gcode_comments ? ' ; adjust acceleration' : ''); } sub G0 { my $self = shift; - return $self->G1(@_) if !($self->config->g0 || $self->config->gcode_flavor eq 'mach3'); + return $self->G1(@_) if !($self->print_config->g0 || $self->print_config->gcode_flavor eq 'mach3'); return $self->_G0_G1("G0", @_); } @@ -628,11 +631,11 @@ sub _Gx { $gcode .= sprintf " F%.3f", $F; # output extrusion distance - if ($e && $self->config->extrusion_axis) { - $gcode .= sprintf " %s%.5f", $self->config->extrusion_axis, $self->extruder->extrude($e); + if ($e && $self->_extrusion_axis) { + $gcode .= sprintf " %s%.5f", $self->_extrusion_axis, $self->extruder->extrude($e); } - $gcode .= " ; $comment" if $comment && $self->config->gcode_comments; + $gcode .= " ; $comment" if $comment && $self->print_config->gcode_comments; return "$gcode\n"; } @@ -653,8 +656,8 @@ sub set_extruder { $gcode .= $self->retract(toolchange => 1) if defined $self->extruder; # append custom toolchange G-code - if (defined $self->extruder && $self->config->toolchange_gcode) { - $gcode .= sprintf "%s\n", $self->replace_variables($self->config->toolchange_gcode, { + if (defined $self->extruder && $self->print_config->toolchange_gcode) { + $gcode .= sprintf "%s\n", $self->replace_variables($self->print_config->toolchange_gcode, { previous_extruder => $self->extruder->id, next_extruder => $extruder->id, }); @@ -669,24 +672,24 @@ sub set_extruder { ? $self->extruder->first_layer_temperature : $self->extruder->temperature; # we assume that heating is always slower than cooling, so no need to block - $gcode .= $self->set_temperature($temp + $self->config->standby_temperature_delta, 0); + $gcode .= $self->set_temperature($temp + $self->print_config->standby_temperature_delta, 0); } # set the new extruder $self->extruder($extruder); $gcode .= sprintf "%s%d%s\n", - ($self->config->gcode_flavor eq 'makerware' + ($self->print_config->gcode_flavor eq 'makerware' ? 'M135 T' - : $self->config->gcode_flavor eq 'sailfish' + : $self->print_config->gcode_flavor eq 'sailfish' ? 'M108 T' : 'T'), $extruder->id, - ($self->config->gcode_comments ? ' ; change extruder' : ''); + ($self->print_config->gcode_comments ? ' ; change extruder' : ''); $gcode .= $self->reset_e; # set the new extruder to the operating temperature - if ($self->config->ooze_prevention) { + if ($self->print_config->ooze_prevention) { my $temp = defined $self->layer && $self->layer->id == 0 ? $self->extruder->first_layer_temperature : $self->extruder->temperature; @@ -702,18 +705,18 @@ sub set_fan { if ($self->last_fan_speed != $speed || $dont_save) { $self->last_fan_speed($speed) if !$dont_save; if ($speed == 0) { - my $code = $self->config->gcode_flavor eq 'teacup' + my $code = $self->print_config->gcode_flavor eq 'teacup' ? 'M106 S0' - : $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/ + : $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/ ? 'M127' : 'M107'; - return sprintf "$code%s\n", ($self->config->gcode_comments ? ' ; disable fan' : ''); + return sprintf "$code%s\n", ($self->print_config->gcode_comments ? ' ; disable fan' : ''); } else { - if ($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/) { - return sprintf "M126%s\n", ($self->config->gcode_comments ? ' ; enable fan' : ''); + if ($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/) { + return sprintf "M126%s\n", ($self->print_config->gcode_comments ? ' ; enable fan' : ''); } else { - return sprintf "M106 %s%d%s\n", ($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'), - (255 * $speed / 100), ($self->config->gcode_comments ? ' ; enable fan' : ''); + return sprintf "M106 %s%d%s\n", ($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'), + (255 * $speed / 100), ($self->print_config->gcode_comments ? ' ; enable fan' : ''); } } } @@ -723,17 +726,17 @@ sub set_fan { sub set_temperature { my ($self, $temperature, $wait, $tool) = @_; - return "" if $wait && $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/; + return "" if $wait && $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/; - my ($code, $comment) = ($wait && $self->config->gcode_flavor ne 'teacup') + my ($code, $comment) = ($wait && $self->print_config->gcode_flavor ne 'teacup') ? ('M109', 'wait for temperature to be reached') : ('M104', 'set temperature'); my $gcode = sprintf "$code %s%d %s; $comment\n", - ($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature, - (defined $tool && ($self->multiple_extruders || $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/)) ? "T$tool " : ""; + ($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature, + (defined $tool && ($self->multiple_extruders || $self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/)) ? "T$tool " : ""; $gcode .= "M116 ; wait for temperature to be reached\n" - if $self->config->gcode_flavor eq 'teacup' && $wait; + if $self->print_config->gcode_flavor eq 'teacup' && $wait; return $gcode; } @@ -741,21 +744,21 @@ sub set_temperature { sub set_bed_temperature { my ($self, $temperature, $wait) = @_; - my ($code, $comment) = ($wait && $self->config->gcode_flavor ne 'teacup') - ? (($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/ ? 'M109' : 'M190'), 'wait for bed temperature to be reached') + my ($code, $comment) = ($wait && $self->print_config->gcode_flavor ne 'teacup') + ? (($self->print_config->gcode_flavor =~ /^(?:makerware|sailfish)$/ ? 'M109' : 'M190'), 'wait for bed temperature to be reached') : ('M140', 'set bed temperature'); my $gcode = sprintf "$code %s%d ; $comment\n", - ($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature; + ($self->print_config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature; $gcode .= "M116 ; wait for bed temperature to be reached\n" - if $self->config->gcode_flavor eq 'teacup' && $wait; + if $self->print_config->gcode_flavor eq 'teacup' && $wait; return $gcode; } sub replace_variables { my ($self, $string, $extra) = @_; - return $self->config->replace_options($string, { %{$self->extra_variables}, %{ $extra || {} } }); + return $self->print_config->replace_options($string, { %{$self->extra_variables}, %{ $extra || {} } }); } 1; diff --git a/lib/Slic3r/GCode/ArcFitting.pm b/lib/Slic3r/GCode/ArcFitting.pm index 52078bdc0..fe45bdb36 100644 --- a/lib/Slic3r/GCode/ArcFitting.pm +++ b/lib/Slic3r/GCode/ArcFitting.pm @@ -104,7 +104,7 @@ sub flush_path { $gcode .= sprintf " I%.3f J%.3f", map { unscale($arc_center->[$_] - $cur_path->[0][$_]) } (X,Y); my $E = 0; # TODO: compute E using $length - $gcode .= sprintf(" %s%.5f", $self->config->extrusion_axis, $E) + $gcode .= sprintf(" %s%.5f", $self->config->get_extrusion_axis, $E) if $E; my $F = 0; # TODO: extract F from original moves diff --git a/lib/Slic3r/GCode/CoolingBuffer.pm b/lib/Slic3r/GCode/CoolingBuffer.pm index d95b5fec1..91119fe3e 100644 --- a/lib/Slic3r/GCode/CoolingBuffer.pm +++ b/lib/Slic3r/GCode/CoolingBuffer.pm @@ -1,7 +1,7 @@ package Slic3r::GCode::CoolingBuffer; use Moo; -has 'config' => (is => 'ro', required => 1); +has 'config' => (is => 'ro', required => 1); # Slic3r::Config::Print has 'gcodegen' => (is => 'ro', required => 1); has 'gcode' => (is => 'rw', default => sub {""}); has 'elapsed_time' => (is => 'rw', default => sub {0}); diff --git a/lib/Slic3r/GCode/Layer.pm b/lib/Slic3r/GCode/Layer.pm index f888ddac2..f64b6a1ba 100644 --- a/lib/Slic3r/GCode/Layer.pm +++ b/lib/Slic3r/GCode/Layer.pm @@ -19,24 +19,24 @@ has '_last_obj_copy' => (is => 'rw'); sub _build_spiralvase { my $self = shift; - return $self->gcodegen->config->spiral_vase - ? Slic3r::GCode::SpiralVase->new(config => $self->gcodegen->config) + return $self->print->config->spiral_vase + ? Slic3r::GCode::SpiralVase->new(config => $self->print->config) : undef; } sub _build_vibration_limit { my $self = shift; - return $self->gcodegen->config->vibration_limit - ? Slic3r::GCode::VibrationLimit->new(config => $self->gcodegen->config) + return $self->print->config->vibration_limit + ? Slic3r::GCode::VibrationLimit->new(config => $self->print->config) : undef; } sub _build_arc_fitting { my $self = shift; - return $self->gcodegen->config->gcode_arcs - ? Slic3r::GCode::ArcFitting->new(config => $self->gcodegen->config) + return $self->print->config->gcode_arcs + ? Slic3r::GCode::ArcFitting->new(config => $self->print->config) : undef; } @@ -45,44 +45,46 @@ sub process_layer { my ($layer, $object_copies) = @_; my $gcode = ""; + my $object = $layer->object; + # check whether we're going to apply spiralvase logic my $spiralvase = defined $self->spiralvase - && ($layer->id > 0 || $self->gcodegen->config->brim_width == 0) - && ($layer->id >= $self->gcodegen->config->skirt_height && $self->gcodegen->config->skirt_height != -1) - && ($layer->id >= $self->gcodegen->config->bottom_solid_layers); + && ($layer->id > 0 || $self->print->config->brim_width == 0) + && ($layer->id >= $self->print->config->skirt_height && $self->print->config->skirt_height != -1) + && !defined(first { $_->config->bottom_solid_layers > $layer->id } @{$layer->regions}); # if we're going to apply spiralvase to this layer, disable loop clipping $self->gcodegen->enable_loop_clipping(!$spiralvase); if (!$self->second_layer_things_done && $layer->id == 1) { - for my $t (grep $self->extruders->[$_], 0 .. $#{$self->gcodegen->config->temperature}) { + for my $t (grep $self->extruders->[$_], 0 .. $#{$self->print->config->temperature}) { $gcode .= $self->gcodegen->set_temperature($self->extruders->[$t]->temperature, 0, $t) if $self->print->extruders->[$t]->temperature && $self->extruders->[$t]->temperature != $self->extruders->[$t]->first_layer_temperature; } - $gcode .= $self->gcodegen->set_bed_temperature($self->gcodegen->config->bed_temperature) - if $self->gcodegen->config->bed_temperature && $self->gcodegen->config->bed_temperature != $self->gcodegen->config->first_layer_bed_temperature; + $gcode .= $self->gcodegen->set_bed_temperature($self->print->config->bed_temperature) + if $self->print->config->bed_temperature && $self->print->config->bed_temperature != $self->print->config->first_layer_bed_temperature; $self->second_layer_things_done(1); } # set new layer - this will change Z and force a retraction if retract_layer_change is enabled $gcode .= $self->gcodegen->change_layer($layer); - $gcode .= $self->gcodegen->replace_variables($self->gcodegen->config->layer_gcode, { + $gcode .= $self->gcodegen->replace_variables($self->print->config->layer_gcode, { layer_num => $self->gcodegen->layer->id, - }) . "\n" if $self->gcodegen->config->layer_gcode; + }) . "\n" if $self->print->config->layer_gcode; # extrude skirt - if (((values %{$self->skirt_done}) < $self->gcodegen->config->skirt_height || $self->gcodegen->config->skirt_height == -1) + if (((values %{$self->skirt_done}) < $self->print->config->skirt_height || $self->print->config->skirt_height == -1) && !$self->skirt_done->{$layer->print_z}) { $self->gcodegen->set_shift(@{$self->shift}); $gcode .= $self->gcodegen->set_extruder($self->extruders->[0]); # skip skirt if we have a large brim - if ($layer->id < $self->gcodegen->config->skirt_height || $self->gcodegen->config->skirt_height == -1) { + if ($layer->id < $self->print->config->skirt_height || $self->print->config->skirt_height == -1) { # distribute skirt loops across all extruders my @skirt_loops = @{$self->print->skirt}; for my $i (0 .. $#skirt_loops) { # when printing layers > 0 ignore 'min_skirt_length' and # just use the 'skirts' setting; also just use the current extruder - last if ($layer->id > 0) && ($i >= $self->gcodegen->config->skirts); + last if ($layer->id > 0) && ($i >= $self->print->config->skirts); $gcode .= $self->gcodegen->set_extruder($self->extruders->[ ($i/@{$self->extruders}) % @{$self->extruders} ]) if $layer->id == 0; $gcode .= $self->gcodegen->extrude_loop($skirt_loops[$i], 'skirt'); @@ -94,7 +96,7 @@ sub process_layer { # extrude brim if (!$self->brim_done) { - $gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->gcodegen->config->support_material_extruder-1]); + $gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->print->objects->[0]->config->support_material_extruder-1]); $self->gcodegen->set_shift(@{$self->shift}); $gcode .= $self->gcodegen->extrude_loop($_, 'brim') for @{$self->print->brim}; $self->brim_done(1); @@ -109,15 +111,17 @@ sub process_layer { # extrude support material before other things because it might use a lower Z # and also because we avoid travelling on other things when printing it - if ($self->print->has_support_material && $layer->isa('Slic3r::Layer::Support')) { + if ($layer->isa('Slic3r::Layer::Support')) { if ($layer->support_interface_fills->count > 0) { - $gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->gcodegen->config->support_material_interface_extruder-1]); - $gcode .= $self->gcodegen->extrude_path($_, 'support material interface') + $gcode .= $self->gcodegen->set_extruder($self->extruders->[$object->config->support_material_interface_extruder-1]); + my %params = (speed => $object->config->support_material_speed*60); + $gcode .= $self->gcodegen->extrude_path($_, 'support material interface', %params) for @{$layer->support_interface_fills->chained_path_from($self->gcodegen->last_pos, 0)}; } if ($layer->support_fills->count > 0) { - $gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->gcodegen->config->support_material_extruder-1]); - $gcode .= $self->gcodegen->extrude_path($_, 'support material') + $gcode .= $self->gcodegen->set_extruder($self->extruders->[$object->config->support_material_extruder-1]); + my %params = (speed => $object->config->support_material_speed*60); + $gcode .= $self->gcodegen->extrude_path($_, 'support material', %params) for @{$layer->support_fills->chained_path_from($self->gcodegen->last_pos, 0)}; } } @@ -126,16 +130,17 @@ sub process_layer { my @region_ids = 0 .. ($self->print->regions_count-1); if ($self->gcodegen->multiple_extruders) { my $last_extruder = $self->gcodegen->extruder; - my $best_region_id = first { $self->print->regions->[$_]->extruders->{perimeter} eq $last_extruder } @region_ids; + my $best_region_id = first { $self->print->regions->[$_]->config->perimeter_extruder-1 eq $last_extruder } @region_ids; @region_ids = ($best_region_id, grep $_ != $best_region_id, @region_ids) if $best_region_id; } foreach my $region_id (@region_ids) { my $layerm = $layer->regions->[$region_id] or next; my $region = $self->print->regions->[$region_id]; + $self->gcodegen->region($region); my @islands = (); - if ($self->gcodegen->config->avoid_crossing_perimeters) { + if ($self->print->config->avoid_crossing_perimeters) { push @islands, { perimeters => [], fills => [] } for 1 .. (@{$layer->slices} || 1); # make sure we have at least one island hash to avoid failure of the -1 subscript below PERIMETER: foreach my $perimeter (@{$layerm->perimeters}) { @@ -166,8 +171,8 @@ sub process_layer { foreach my $island (@islands) { # give priority to infill if we were already using its extruder and it wouldn't # be good for perimeters - if ($self->gcodegen->config->infill_first - || ($self->gcodegen->multiple_extruders && $region->extruders->{infill} eq $self->gcodegen->extruder) && $region->extruders->{infill} ne $region->extruders->{perimeter}) { + if ($self->print->config->infill_first + || ($self->gcodegen->multiple_extruders && $region->config->infill_extruder-1 == $self->gcodegen->extruder->id && $region->config->infill_extruder != $region->config->perimeter_extruder)) { $gcode .= $self->_extrude_infill($island, $region); $gcode .= $self->_extrude_perimeters($island, $region); } else { @@ -184,11 +189,11 @@ sub process_layer { # apply vibration limit if enabled $gcode = $self->vibration_limit->process($gcode) - if $self->gcodegen->config->vibration_limit != 0; + if $self->print->config->vibration_limit != 0; # apply arc fitting if enabled $gcode = $self->arc_fitting->process($gcode) - if $self->gcodegen->config->gcode_arcs; + if $self->print->config->gcode_arcs; return $gcode; } @@ -200,7 +205,7 @@ sub _extrude_perimeters { return "" if !@{ $island->{perimeters} }; my $gcode = ""; - $gcode .= $self->gcodegen->set_extruder($region->extruders->{perimeter}); + $gcode .= $self->gcodegen->set_extruder($self->extruders->[$region->config->perimeter_extruder-1]); $gcode .= $self->gcodegen->extrude($_, 'perimeter') for @{ $island->{perimeters} }; return $gcode; } @@ -212,7 +217,7 @@ sub _extrude_infill { return "" if !@{ $island->{fills} }; my $gcode = ""; - $gcode .= $self->gcodegen->set_extruder($region->extruders->{infill}); + $gcode .= $self->gcodegen->set_extruder($self->extruders->[$region->config->infill_extruder-1]); for my $fill (@{ $island->{fills} }) { if ($fill->isa('Slic3r::ExtrusionPath::Collection')) { $gcode .= $self->gcodegen->extrude($_, 'fill') diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index d490597db..d44a06b6c 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -12,9 +12,9 @@ use Slic3r::Geometry::Clipper qw(diff_ex union_ex union_pt intersection_ex inter offset2 union union_pt_chained JT_ROUND JT_SQUARE); use Slic3r::Print::State ':steps'; -has 'config' => (is => 'rw', default => sub { Slic3r::Config::Print->new }, trigger => \&init_config); -has 'default_object_config' => (is => 'rw', default => sub { Slic3r::Config::PrintObject->new }); -has 'default_region_config' => (is => 'rw', default => sub { Slic3r::Config::PrintRegion->new }); +has 'config' => (is => 'ro', default => sub { Slic3r::Config::Print->new }); +has 'default_object_config' => (is => 'ro', default => sub { Slic3r::Config::PrintObject->new }); +has 'default_region_config' => (is => 'ro', default => sub { Slic3r::Config::PrintRegion->new }); has 'extra_variables' => (is => 'rw', default => sub {{}}); has 'objects' => (is => 'rw', default => sub {[]}); has 'status_cb' => (is => 'rw'); @@ -28,52 +28,12 @@ has 'skirt' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection-> # ordered collection of extrusion paths to build a brim has 'brim' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->new }); -sub BUILD { - my $self = shift; - - # call this manually because the 'default' coderef doesn't trigger the trigger - $self->init_config; -} - -# this method needs to be idempotent -sub init_config { - my $self = shift; - - # legacy with existing config files - $self->config->set('first_layer_height', $self->config->layer_height) - if !$self->config->first_layer_height; - $self->config->set_ifndef('small_perimeter_speed', $self->config->perimeter_speed); - $self->config->set_ifndef('bridge_speed', $self->config->infill_speed); - $self->config->set_ifndef('solid_infill_speed', $self->config->infill_speed); - $self->config->set_ifndef('top_solid_infill_speed', $self->config->solid_infill_speed); - $self->config->set_ifndef('top_solid_layers', $self->config->solid_layers); - $self->config->set_ifndef('bottom_solid_layers', $self->config->solid_layers); - - # G-code flavors - $self->config->set('extrusion_axis', 'A') if $self->config->gcode_flavor eq 'mach3'; - $self->config->set('extrusion_axis', '') if $self->config->gcode_flavor eq 'no-extrusion'; - - # enforce some settings when spiral_vase is set - if ($self->config->spiral_vase) { - $self->config->set('perimeters', 1); - $self->config->set('fill_density', 0); - $self->config->set('top_solid_layers', 0); - $self->config->set('support_material', 0); - $self->config->set('support_material_enforce_layers', 0); - $self->config->set('retract_layer_change', [0]); # TODO: only apply this to the spiral layers - } - - # force all retraction lift values to be the same - $self->config->set('retract_lift', [ map $self->config->retract_lift->[0], @{$self->config->retract_lift} ]); -} - sub apply_config { my ($self, $config) = @_; $self->config->apply_dynamic($config); $self->default_object_config->apply_dynamic($config); $self->default_region_config->apply_dynamic($config); - $self->init_config; } sub has_support_material { @@ -261,11 +221,21 @@ sub init_extruders { } for my $extruder_id (keys %{{ map {$_ => 1} @used_extruders }}) { + # make sure print config contains a value for all extruders + my %extruder_config = (); + foreach my $opt_key (@{&Slic3r::Extruder::OPTIONS}) { + my $value = $self->config->get($opt_key); + if (!defined $value->[$extruder_id]) { + $value->[$extruder_id] = $value->[0]; + $self->config->set($opt_key, $value); + } + $extruder_config{$opt_key} = $value->[$extruder_id]; + } + $self->extruders->[$extruder_id] = Slic3r::Extruder->new( - config => $self->config, id => $extruder_id, - map { $_ => $self->config->get($_)->[$extruder_id] // $self->config->get($_)->[0] } #/ - @{&Slic3r::Extruder::OPTIONS} + use_relative_e_distances => $self->config->use_relative_e_distances, + %extruder_config, ); } @@ -614,16 +584,17 @@ sub make_skirt { # skirt may be printed on several layers, having distinct layer heights, # but loops must be aligned so can't vary width/spacing # TODO: use each extruder's own flow + my $region0_config = $self->regions->[0]->config; + my $first_layer_height = $self->objects->[0]->config->get_value('first_layer_height'); my $flow = Slic3r::Flow->new( - width => ($self->config->first_layer_extrusion_width || $self->config->perimeter_extrusion_width), + width => ($region0_config->first_layer_extrusion_width || $region0_config->perimeter_extrusion_width), role => FLOW_ROLE_PERIMETER, nozzle_diameter => $self->config->nozzle_diameter->[0], - layer_height => $self->config->get_abs_value('first_layer_height'), + layer_height => $first_layer_height, bridge_flow_ratio => 0, ); my $spacing = $flow->spacing; - my $first_layer_height = $self->config->get_value('first_layer_height'); my @extruders_e_per_mm = (); my $extruder_idx = 0; @@ -664,10 +635,10 @@ sub make_brim { # brim is only printed on first layer and uses support material extruder my $flow = Slic3r::Flow->new( - width => ($self->config->first_layer_extrusion_width || $self->config->perimeter_extrusion_width), + width => ($self->regions->[0]->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width), role => FLOW_ROLE_PERIMETER, - nozzle_diameter => $self->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ], - layer_height => $self->config->get_abs_value('first_layer_height'), + nozzle_diameter => $self->config->nozzle_diameter->[ $self->objects->[0]->config->support_material_extruder-1 ], + layer_height => $self->objects->[0]->config->get_abs_value('first_layer_height'), bridge_flow_ratio => 0, ); @@ -737,34 +708,28 @@ sub write_gcode { print $fh "; $_\n" foreach split /\R/, $self->config->notes; print $fh "\n" if $self->config->notes; - for (qw(layer_height perimeters top_solid_layers bottom_solid_layers fill_density perimeter_speed infill_speed travel_speed)) { - printf $fh "; %s = %s\n", $_, $self->config->$_; - } - for (qw(nozzle_diameter filament_diameter extrusion_multiplier)) { - printf $fh "; %s = %s\n", $_, $self->config->$_->[0]; - } - + my $layer_height = $self->objects->[0]->config->layer_height; for my $region_id (0..$#{$self->regions}) { printf $fh "; perimeters extrusion width = %.2fmm\n", - $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER)->width; + $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, $layer_height)->width; printf $fh "; infill extrusion width = %.2fmm\n", - $self->regions->[$region_id]->flow(FLOW_ROLE_INFILL)->width; + $self->regions->[$region_id]->flow(FLOW_ROLE_INFILL, $layer_height)->width; printf $fh "; solid infill extrusion width = %.2fmm\n", - $self->regions->[$region_id]->flow(FLOW_ROLE_SOLID_INFILL)->width; + $self->regions->[$region_id]->flow(FLOW_ROLE_SOLID_INFILL, $layer_height)->width; printf $fh "; top infill extrusion width = %.2fmm\n", - $self->regions->[$region_id]->flow(FLOW_ROLE_TOP_SOLID_INFILL)->width; + $self->regions->[$region_id]->flow(FLOW_ROLE_TOP_SOLID_INFILL, $layer_height)->width; printf $fh "; support material extrusion width = %.2fmm\n", $self->objects->[0]->support_material_flow->width if $self->has_support_material; printf $fh "; first layer extrusion width = %.2fmm\n", - $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, 0, 1)->width - if ($self->regions->[$region_id]->config->first_layer_extrusion_width != 0); + $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 1)->width + if ($self->regions->[$region_id]->config->first_layer_extrusion_width ne '0'); print $fh "\n"; } # set up our extruder object my $gcodegen = Slic3r::GCode->new( - config => $self->config, + print_config => $self->config, extra_variables => $self->extra_variables, extruders => $self->extruders, # we should only pass the *used* extruders (but maintain the Tx indices right!) layer_count => $self->layer_count, @@ -941,13 +906,11 @@ sub write_gcode { $extruder->absolute_E, $extruder->extruded_volume/1000; } - if ($self->config->gcode_comments) { - # append full config - print $fh "\n"; - foreach my $opt_key (sort @{$self->config->get_keys}) { - next if $Slic3r::Config::Options->{$opt_key}{shortcut}; - printf $fh "; %s = %s\n", $opt_key, $self->config->serialize($opt_key); - } + # append full config + print $fh "\n"; + foreach my $opt_key (sort @{$self->config->get_keys}) { + next if $Slic3r::Config::Options->{$opt_key}{shortcut}; + printf $fh "; %s = %s\n", $opt_key, $self->config->serialize($opt_key); } # close our gcode file diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 600a0dcc8..bf4c2efba 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -521,13 +521,16 @@ sub clip_fill_surfaces { sub bridge_over_infill { my $self = shift; - return if $self->config->fill_density == 1; - for my $layer_id (1..$#{$self->layers}) { - my $layer = $self->layers->[$layer_id]; - my $lower_layer = $self->layers->[$layer_id-1]; + for my $region_id (0..$#{$self->print->regions}) { + my $fill_density = $self->print->regions->[$region_id]->config->fill_density; + next if $fill_density == 1 || $fill_density == 0; - foreach my $layerm (@{$layer->regions}) { + for my $layer_id (1..$#{$self->layers}) { + my $layer = $self->layers->[$layer_id]; + my $layerm = $layer->regions->[$region_id]; + my $lower_layer = $self->layers->[$layer_id-1]; + # compute the areas needing bridge math my @internal_solid = @{$layerm->fill_surfaces->filter_by_type(S_TYPE_INTERNALSOLID)}; my @lower_internal = map @{$_->fill_surfaces->filter_by_type(S_TYPE_INTERNAL)}, @{$lower_layer->regions}; @@ -861,7 +864,8 @@ sub generate_support_material { && $self->layer_count >= 2; my $s = Slic3r::Print::SupportMaterial->new( - config => $self->config, + print_config => $self->print->config, + object_config => $self->config, flow => $self->support_material_flow, interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE), ); diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm index 4cb929e0b..ff05d5bc9 100644 --- a/lib/Slic3r/Print/Region.pm +++ b/lib/Slic3r/Print/Region.pm @@ -53,7 +53,7 @@ sub flow { role => $role, nozzle_diameter => $nozzle_diameter, layer_height => $layer_height, - bridge_flow_ratio => ($bridge ? $self->config->bridge_flow_ratio : 0), + bridge_flow_ratio => ($bridge ? $self->print->config->bridge_flow_ratio : 0), ); } diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 291268bba..38949d22e 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -3,13 +3,15 @@ use Moo; use List::Util qw(sum min max); use Slic3r::ExtrusionPath ':roles'; +use Slic3r::Flow ':roles'; use Slic3r::Geometry qw(scale scaled_epsilon PI rad2deg deg2rad); use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ex offset2 intersection_pl); use Slic3r::Surface ':types'; -has 'config' => (is => 'rw', required => 1); -has 'flow' => (is => 'rw', required => 1); +has 'print_config' => (is => 'rw', required => 1); +has 'object_config' => (is => 'rw', required => 1); +has 'flow' => (is => 'rw', required => 1); use constant DEBUG_CONTACT_ONLY => 0; @@ -73,8 +75,8 @@ sub contact_area { # if user specified a custom angle threshold, convert it to radians my $threshold_rad; - if ($self->config->support_material_threshold) { - $threshold_rad = deg2rad($self->config->support_material_threshold + 1); # +1 makes the threshold inclusive + if ($self->object_config->support_material_threshold) { + $threshold_rad = deg2rad($self->object_config->support_material_threshold + 1); # +1 makes the threshold inclusive Slic3r::debugf "Threshold angle = %d°\n", rad2deg($threshold_rad); } @@ -86,9 +88,9 @@ sub contact_area { # so $layer_id == 0 means first object layer # and $layer->id == 0 means first print layer (including raft) - if ($self->config->raft_layers == 0) { + if ($self->object_config->raft_layers == 0) { next if $layer_id == 0; - } elsif (!$self->config->support_material) { + } elsif (!$self->object_config->support_material) { # if we are only going to generate raft just check # the 'overhangs' of the first object layer last if $layer_id > 0; @@ -110,8 +112,8 @@ sub contact_area { # If a threshold angle was specified, use a different logic for detecting overhangs. if (defined $threshold_rad - || $layer_id < $self->config->support_material_enforce_layers - || $self->config->raft_layers > 0) { + || $layer_id < $self->object_config->support_material_enforce_layers + || $self->object_config->raft_layers > 0) { my $d = defined $threshold_rad ? scale $lower_layer->height * ((cos $threshold_rad) / (sin $threshold_rad)) : 0; @@ -170,8 +172,8 @@ sub contact_area { # now apply the contact areas to the layer were they need to be made { # get the average nozzle diameter used on this layer - my @nozzle_diameters = map $_->nozzle_diameter, - map { $_->perimeter_flow, $_->solid_infill_flow } + my @nozzle_diameters = map $self->print_config->nozzle_diameter->[$_], + map { $_->config->perimeter_extruder-1, $_->config->infill_extruder-1 } @{$layer->regions}; my $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters; @@ -179,7 +181,7 @@ sub contact_area { ###$contact_z = $layer->print_z - $layer->height; # ignore this contact area if it's too low - next if $contact_z < $self->config->get_value('first_layer_height'); + next if $contact_z < $self->object_config->get_value('first_layer_height'); $contact{$contact_z} = [ @contact ]; $overhang{$contact_z} = [ @overhang ]; @@ -242,25 +244,25 @@ sub support_layers_z { # determine layer height for any non-contact layer # we use max() to prevent many ultra-thin layers to be inserted in case # layer_height > nozzle_diameter * 0.75 - my $nozzle_diameter = $self->flow->nozzle_diameter; + my $nozzle_diameter = $self->print_config->nozzle_diameter->[$self->object_config->support_material_extruder-1]; my $support_material_height = max($max_object_layer_height, $nozzle_diameter * 0.75); my @z = sort { $a <=> $b } @$contact_z, @$top_z, (map $_ + $nozzle_diameter, @$top_z); # enforce first layer height - my $first_layer_height = $self->config->get_value('first_layer_height'); + my $first_layer_height = $self->object_config->get_value('first_layer_height'); shift @z while @z && $z[0] <= $first_layer_height; unshift @z, $first_layer_height; # add raft layers by dividing the space between first layer and # first contact layer evenly - if ($self->config->raft_layers > 1 && @z >= 2) { + if ($self->object_config->raft_layers > 1 && @z >= 2) { # $z[1] is last raft layer (contact layer for the first layer object) - my $height = ($z[1] - $z[0]) / ($self->config->raft_layers - 1); + my $height = ($z[1] - $z[0]) / ($self->object_config->raft_layers - 1); splice @z, 1, 0, map { int($_*100)/100 } map { $z[0] + $height * $_ } - 0..($self->config->raft_layers - 1); + 0..($self->object_config->raft_layers - 1); } for (my $i = $#z; $i >= 0; $i--) { @@ -291,7 +293,7 @@ sub generate_interface_layers { # let's now generate interface layers below contact areas my %interface = (); # layer_id => [ polygons ] - my $interface_layers = $self->config->support_material_interface_layers; + my $interface_layers = $self->object_config->support_material_interface_layers; for my $layer_id (0 .. $#$support_z) { my $z = $support_z->[$layer_id]; my $this = $contact->{$z} // next; @@ -339,7 +341,7 @@ sub generate_base_layers { # in case we have no interface layers, look at upper contact # (1 interface layer means we only have contact layer, so $interface->{$i+1} is empty) my @upper_contact = (); - if ($self->config->support_material_interface_layers <= 1) { + if ($self->object_config->support_material_interface_layers <= 1) { @upper_contact = @{ $contact->{$support_z->[$i+1]} || [] }; } @@ -395,8 +397,8 @@ sub generate_toolpaths { Slic3r::debugf "Generating patterns\n"; # prepare fillers - my $pattern = $self->config->support_material_pattern; - my @angles = ($self->config->support_material_angle); + my $pattern = $self->object_config->support_material_pattern; + my @angles = ($self->object_config->support_material_angle); if ($pattern eq 'rectilinear-grid') { $pattern = 'rectilinear'; push @angles, $angles[0] + 90; @@ -407,10 +409,10 @@ sub generate_toolpaths { support => $object->fill_maker->filler($pattern), ); - my $interface_angle = $self->config->support_material_angle + 90; - my $interface_spacing = $self->config->support_material_interface_spacing + $flow->spacing; + my $interface_angle = $self->object_config->support_material_angle + 90; + my $interface_spacing = $self->object_config->support_material_interface_spacing + $flow->spacing; my $interface_density = $interface_spacing == 0 ? 1 : $flow->spacing / $interface_spacing; - my $support_spacing = $self->config->support_material_spacing + $flow->spacing; + my $support_spacing = $self->object_config->support_material_spacing + $flow->spacing; my $support_density = $support_spacing == 0 ? 1 : $flow->spacing / $support_spacing; my $process_layer = sub { @@ -441,7 +443,7 @@ sub generate_toolpaths { # contact my $contact_infill = []; - if ($self->config->support_material_interface_layers == 0) { + if ($self->object_config->support_material_interface_layers == 0) { # if no interface layers were requested we treat the contact layer # exactly as a generic base layer push @$base, @$contact; @@ -561,7 +563,7 @@ sub generate_toolpaths { # base flange if ($layer_id == 0) { $filler = $fillers{interface}; - $filler->angle($self->config->support_material_angle + 90); + $filler->angle($self->object_config->support_material_angle + 90); $density = 0.5; $flow_spacing = $object->print->first_layer_support_material_flow->spacing; } else { @@ -609,7 +611,7 @@ sub generate_toolpaths { }; Slic3r::parallelize( - threads => $self->config->threads, + threads => $self->print_config->threads, items => [ 0 .. $#{$object->support_layers} ], thread_cb => sub { my $q = shift; diff --git a/lib/Slic3r/Test.pm b/lib/Slic3r/Test.pm index 3a3944318..2e88268a7 100644 --- a/lib/Slic3r/Test.pm +++ b/lib/Slic3r/Test.pm @@ -99,20 +99,21 @@ sub model { sub init_print { my ($model_name, %params) = @_; - my $config = Slic3r::Config->new_from_defaults; + my $config = Slic3r::Config->new; $config->apply($params{config}) if $params{config}; $config->set('gcode_comments', 1) if $ENV{SLIC3R_TESTS_GCODE}; - my $print = Slic3r::Print->new(config => $config); + my $print = Slic3r::Print->new; + $print->apply_config($config); $model_name = [$model_name] if ref($model_name) ne 'ARRAY'; for my $model (map model($_, %params), @$model_name) { die "Unknown model in test" if !defined $model; if (defined $params{duplicate} && $params{duplicate} > 1) { - $model->duplicate($params{duplicate} // 1, $config->min_object_distance); + $model->duplicate($params{duplicate} // 1, $print->config->min_object_distance); } - $model->arrange_objects($config->min_object_distance); - $model->center_instances_around_point($config->print_center); + $model->arrange_objects($print->config->min_object_distance); + $model->center_instances_around_point($print->config->print_center); $print->add_model_object($_) for @{$model->objects}; } $print->validate; diff --git a/slic3r.pl b/slic3r.pl index 0be1219d0..beea80373 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -153,12 +153,12 @@ if (@ARGV) { # slicing from command line } my $print = Slic3r::Print->new( - config => $config, status_cb => sub { my ($percent, $message) = @_; printf "=> %s\n", $message; }, ); + $print->apply_config($config); foreach my $model_object (@{$model->objects}) { $print->auto_assign_extruders($model_object); $print->add_model_object($model_object); diff --git a/t/cooling.t b/t/cooling.t index 87d3dff39..6fc5cf71b 100644 --- a/t/cooling.t +++ b/t/cooling.t @@ -13,10 +13,14 @@ use Slic3r; use Slic3r::Test; sub buffer { - my $config = shift || Slic3r::Config->new_from_defaults; + my $config = shift || Slic3r::Config->new; + + my $print_config = Slic3r::Config::Print->new; + $print_config->apply_dynamic($config); + my $buffer = Slic3r::GCode::CoolingBuffer->new( - config => $config, - gcodegen => Slic3r::GCode->new(config => $config, layer_count => 10, extruders => []), + config => $print_config, + gcodegen => Slic3r::GCode->new(print_config => $print_config, layer_count => 10, extruders => []), ); return $buffer; } diff --git a/t/gcode.t b/t/gcode.t index c8db2f816..bd5677fc7 100644 --- a/t/gcode.t +++ b/t/gcode.t @@ -14,9 +14,8 @@ use Slic3r::Test; { my $gcodegen = Slic3r::GCode->new( - config => Slic3r::Config->new_from_defaults, - layer_count => 1, - extruders => [], + layer_count => 1, + extruders => [], ); $gcodegen->set_shift(10, 10); is_deeply $gcodegen->last_pos->arrayref, [scale -10, scale -10], 'last_pos is shifted correctly'; diff --git a/t/shells.t b/t/shells.t index 10da90556..541c7cb75 100644 --- a/t/shells.t +++ b/t/shells.t @@ -140,6 +140,9 @@ use Slic3r::Test; { my $config = Slic3r::Config->new_from_defaults; + $config->set('perimeters', 1); + $config->set('fill_density', 0); + $config->set('top_solid_layers', 0); $config->set('spiral_vase', 1); $config->set('bottom_solid_layers', 0); $config->set('skirts', 0); diff --git a/t/support.t b/t/support.t index b7d1810df..bd82d0ce1 100644 --- a/t/support.t +++ b/t/support.t @@ -21,16 +21,16 @@ use Slic3r::Test; my $test = sub { my $print = Slic3r::Test::init_print('20mm_cube', config => $config); $print->init_extruders; - my $flow = $print->support_material_flow; + my $flow = $print->objects->[0]->support_material_flow; my $support_z = Slic3r::Print::SupportMaterial - ->new(config => $config, flow => $flow) + ->new(object_config => $print->objects->[0]->config, print_config => $print->config, flow => $flow) ->support_layers_z(\@contact_z, \@top_z, $config->layer_height); is $support_z->[0], $config->first_layer_height, 'first layer height is honored'; is scalar(grep { $support_z->[$_]-$support_z->[$_-1] <= 0 } 1..$#$support_z), 0, 'no null or negative support layers'; - is scalar(grep { $support_z->[$_]-$support_z->[$_-1] > $flow->nozzle_diameter + epsilon } 1..$#$support_z), 0, + is scalar(grep { $support_z->[$_]-$support_z->[$_-1] > $config->nozzle_diameter->[0] + epsilon } 1..$#$support_z), 0, 'no layers thicker than nozzle diameter'; my $wrong_top_spacing = 0; @@ -40,7 +40,7 @@ use Slic3r::Test; # check that first support layer above this top surface is spaced with nozzle diameter $wrong_top_spacing = 1 - if ($support_z->[$layer_id+1] - $support_z->[$layer_id]) != $flow->nozzle_diameter; + if ($support_z->[$layer_id+1] - $support_z->[$layer_id]) != $config->nozzle_diameter->[0]; } ok !$wrong_top_spacing, 'layers above top surfaces are spaced correctly'; }; diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index 4643c8b70..cecd00a33 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -899,6 +899,7 @@ class PrintObjectConfig : public virtual StaticConfig public: ConfigOptionFloatOrPercent extrusion_width; ConfigOptionFloatOrPercent first_layer_height; + ConfigOptionBool infill_only_where_needed; ConfigOptionFloat layer_height; ConfigOptionInt raft_layers; ConfigOptionBool support_material; @@ -921,6 +922,7 @@ class PrintObjectConfig : public virtual StaticConfig this->extrusion_width.percent = false; this->first_layer_height.value = 0.35; this->first_layer_height.percent = false; + this->infill_only_where_needed.value = false; this->layer_height.value = 0.4; this->raft_layers.value = 0; this->support_material.value = false; @@ -941,6 +943,7 @@ class PrintObjectConfig : public virtual StaticConfig ConfigOption* option(const t_config_option_key opt_key, bool create = false) { if (opt_key == "extrusion_width") return &this->extrusion_width; if (opt_key == "first_layer_height") return &this->first_layer_height; + if (opt_key == "infill_only_where_needed") return &this->infill_only_where_needed; if (opt_key == "layer_height") return &this->layer_height; if (opt_key == "raft_layers") return &this->raft_layers; if (opt_key == "support_material") return &this->support_material; @@ -972,7 +975,6 @@ class PrintRegionConfig : public virtual StaticConfig ConfigOptionInt infill_extruder; ConfigOptionFloatOrPercent infill_extrusion_width; ConfigOptionInt infill_every_layers; - ConfigOptionBool infill_only_where_needed; ConfigOptionInt perimeter_extruder; ConfigOptionFloatOrPercent perimeter_extrusion_width; ConfigOptionInt perimeters; @@ -999,7 +1001,6 @@ class PrintRegionConfig : public virtual StaticConfig this->infill_extrusion_width.value = 0; this->infill_extrusion_width.percent = false; this->infill_every_layers.value = 1; - this->infill_only_where_needed.value = false; this->perimeter_extruder.value = 1; this->perimeter_extrusion_width.value = 0; this->perimeter_extrusion_width.percent = false; @@ -1025,7 +1026,6 @@ class PrintRegionConfig : public virtual StaticConfig if (opt_key == "infill_extruder") return &this->infill_extruder; if (opt_key == "infill_extrusion_width") return &this->infill_extrusion_width; if (opt_key == "infill_every_layers") return &this->infill_every_layers; - if (opt_key == "infill_only_where_needed") return &this->infill_only_where_needed; if (opt_key == "perimeter_extruder") return &this->perimeter_extruder; if (opt_key == "perimeter_extrusion_width") return &this->perimeter_extrusion_width; if (opt_key == "perimeters") return &this->perimeters; @@ -1332,6 +1332,15 @@ class PrintConfig : public virtual StaticConfig return NULL; }; + + std::string get_extrusion_axis() { + if (this->gcode_flavor == gcfMach3) { + return std::string("A"); + } else if (this->gcode_flavor == gcfNoExtrusion) { + return std::string(""); + } + return this->extrusion_axis; + } }; class DynamicPrintConfig : public DynamicConfig diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 5c3966e13..b7f6f3ec8 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -41,6 +41,7 @@ %code{% THIS->apply(*other, true); %}; std::vector get_keys() %code{% THIS->keys(&RETVAL); %}; + std::string get_extrusion_axis(); }; %name{Slic3r::Config::PrintRegion} class PrintRegionConfig { From 385e0e0974ce4b8de6f80ca7ac31970943912977 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 2 Jan 2014 17:54:18 +0100 Subject: [PATCH 07/19] Refactoring: new Slic3r::Print::Simple class for non-interactive slicing (used in CLI and Quick Slice) --- lib/Slic3r.pm | 1 + lib/Slic3r/GUI/SkeinPanel.pm | 72 ++++++------------ lib/Slic3r/Print/Simple.pm | 112 ++++++++++++++++++++++++++++ lib/Slic3r/Print/SupportMaterial.pm | 2 +- slic3r.pl | 52 ++++--------- 5 files changed, 151 insertions(+), 88 deletions(-) create mode 100644 lib/Slic3r/Print/Simple.pm diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 8ab34ca34..1c5ffa74c 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -68,6 +68,7 @@ use Slic3r::Polyline; use Slic3r::Print; use Slic3r::Print::Object; use Slic3r::Print::Region; +use Slic3r::Print::Simple; use Slic3r::Print::SupportMaterial; use Slic3r::Surface; use Slic3r::TriangleMesh; diff --git a/lib/Slic3r/GUI/SkeinPanel.pm b/lib/Slic3r/GUI/SkeinPanel.pm index 7750794c0..371698e0a 100644 --- a/lib/Slic3r/GUI/SkeinPanel.pm +++ b/lib/Slic3r/GUI/SkeinPanel.pm @@ -88,25 +88,15 @@ sub quick_slice { my $self = shift; my %params = @_; - my $process_dialog; + my $progress_dialog; eval { # validate configuration my $config = $self->config; $config->validate; - - # confirm slicing of more than one copies - my $copies = $config->duplicate_grid->[X] * $config->duplicate_grid->[Y]; - $copies = $config->duplicate if $config->duplicate > 1; - if ($copies > 1) { - my $confirmation = Wx::MessageDialog->new($self, "Are you sure you want to slice $copies copies?", - 'Multiple Copies', wxICON_QUESTION | wxOK | wxCANCEL); - return unless $confirmation->ShowModal == wxID_OK; - } # select input file - my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || ''; - my $input_file; + my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || $Slic3r::GUI::Settings->{recent}{config_directory} || ''; if (!$params{reslice}) { my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF):', $dir, "", MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST); if ($dialog->ShowModal != wxID_OK) { @@ -133,31 +123,23 @@ sub quick_slice { $Slic3r::GUI::Settings->{recent}{skein_directory} = dirname($input_file); Slic3r::GUI->save_settings; - my $print = $self->init_print; - my $model = eval { Slic3r::Model->read_from_file($input_file) }; - Slic3r::GUI::show_error($self, $@) if $@; + my $sprint = Slic3r::Print::Simple->new( + status_cb => sub { + my ($percent, $message) = @_; + return if &Wx::wxVERSION_STRING !~ / 2\.(8\.|9\.[2-9])/; + $progress_dialog->Update($percent, "$message…"); + }, + ); - if ($model->has_objects_with_no_instances) { - # apply a default position to all objects not having one - foreach my $object (@{$model->objects}) { - $object->add_instance(offset => [0,0]) if !defined $object->instances; - } - $model->arrange_objects($config->min_object_distance); - } - $model->center_instances_around_point($config->print_center); + $sprint->apply_config($config); + $sprint->set_model(Slic3r::Model->read_from_file($input_file)); - foreach my $model_object (@{$model->objects}) { - $print->auto_assign_extruders($model_object); - $print->add_model_object($model_object); - } - $print->validate; - # select output file - my $output_file = $main::opt{output}; + my $output_file; if ($params{reslice}) { $output_file = $last_output_file if defined $last_output_file; } elsif ($params{save_as}) { - $output_file = $print->expanded_output_filepath($output_file); + $output_file = $sprint->expanded_output_filepath; $output_file =~ s/\.gcode$/.svg/i if $params{export_svg}; my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:', Slic3r::GUI->output_path(dirname($output_file)), @@ -174,40 +156,32 @@ sub quick_slice { } # show processbar dialog - $process_dialog = Wx::ProgressDialog->new('Slicing…', "Processing $input_file_basename…", + $progress_dialog = Wx::ProgressDialog->new('Slicing…', "Processing $input_file_basename…", 100, $self, 0); - $process_dialog->Pulse; + $progress_dialog->Pulse; { my @warnings = (); local $SIG{__WARN__} = sub { push @warnings, $_[0] }; - my %export_params = ( - output_file => $output_file, - ); - $print->status_cb(sub { - my ($percent, $message) = @_; - if (&Wx::wxVERSION_STRING =~ / 2\.(8\.|9\.[2-9])/) { - $process_dialog->Update($percent, "$message…"); - } - }); + + $sprint->output_file($output_file); if ($params{export_svg}) { - $print->export_svg(%export_params); + $sprint->export_svg; } else { - $print->process; - $print->export_gcode(%export_params); + $sprint->export_gcode; } - $print->status_cb(undef); + $sprint->status_cb(undef); Slic3r::GUI::warning_catcher($self)->($_) for @warnings; } - $process_dialog->Destroy; - undef $process_dialog; + $progress_dialog->Destroy; + undef $progress_dialog; my $message = "$input_file_basename was successfully sliced."; &Wx::wxTheApp->notify($message); Wx::MessageDialog->new($self, $message, 'Slicing Done!', wxOK | wxICON_INFORMATION)->ShowModal; }; - Slic3r::GUI::catch_error($self, sub { $process_dialog->Destroy if $process_dialog }); + Slic3r::GUI::catch_error($self, sub { $progress_dialog->Destroy if $progress_dialog }); } sub repair_stl { diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm new file mode 100644 index 000000000..0bf497b7d --- /dev/null +++ b/lib/Slic3r/Print/Simple.pm @@ -0,0 +1,112 @@ +package Slic3r::Print::Simple; +use Moo; + +use Slic3r::Geometry qw(X Y); + +has '_print' => ( + is => 'ro', + default => sub { Slic3r::Print->new }, + handles => [qw(apply_config extruders expanded_output_filepath)], +); + +has 'duplicate' => ( + is => 'rw', + default => sub { 1 }, +); + +has 'scale' => ( + is => 'rw', + default => sub { 1 }, +); + +has 'rotate' => ( + is => 'rw', + default => sub { 0 }, +); + +has 'duplicate_grid' => ( + is => 'rw', + default => sub { [1,1] }, +); + +has 'status_cb' => ( + is => 'rw', + default => sub { sub {} }, +); + +has 'output_file' => ( + is => 'rw', +); + +sub set_model { + my ($self, $model) = @_; + + # make method idempotent so that the object is reusable + $self->_print->delete_all_objects; + + my $need_arrange = $model->has_objects_with_no_instances; + if ($need_arrange) { + # apply a default position to all objects not having one + foreach my $object (@{$model->objects}) { + $object->add_instance(offset => [0,0]) if !defined $object->instances; + } + } + + # apply scaling and rotation supplied from command line if any + foreach my $instance (map @{$_->instances}, @{$model->objects}) { + $instance->scaling_factor($instance->scaling_factor * $self->scale); + $instance->rotation($instance->rotation + $self->rotate); + } + # TODO: --scale --rotate, --duplicate* shouldn't be stored in config + + if ($self->duplicate_grid->[X] > 1 || $self->duplicate_grid->[Y] > 1) { + $model->duplicate_objects_grid($self->duplicate_grid, $self->_print->config->duplicate_distance); + } elsif ($need_arrange) { + $model->duplicate_objects($self->duplicate, $self->_print->config->min_object_distance); + } elsif ($self->duplicate > 1) { + # if all input objects have defined position(s) apply duplication to the whole model + $model->duplicate($self->duplicate, $self->_print->config->min_object_distance); + } + $model->center_instances_around_point($self->_print->config->print_center); + + foreach my $model_object (@{$model->objects}) { + $self->_print->auto_assign_extruders($model_object); + $self->_print->add_model_object($model_object); + } +} + +sub _before_export { + my ($self) = @_; + + $self->_print->status_cb($self->status_cb); + $self->_print->validate; +} + +sub _after_export { + my ($self) = @_; + + $self->_print->status_cb(undef); +} + +sub export_gcode { + my ($self) = @_; + + $self->_before_export; + + $self->_print->process; + $self->_print->export_gcode(output_file => $self->output_file); + + $self->_after_export; +} + +sub export_svg { + my ($self) = @_; + + $self->_before_export; + + $self->_print->export_svg(output_file => $self->output_file); + + $self->_after_export; +} + +1; diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 38949d22e..0dae19e18 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -107,7 +107,7 @@ sub contact_area { } else { my $lower_layer = $object->layers->[$layer_id-1]; foreach my $layerm (@{$layer->regions}) { - my $fw = $layerm->perimeter_flow->scaled_width; + my $fw = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_width; my $diff; # If a threshold angle was specified, use a different logic for detecting overhangs. diff --git a/slic3r.pl b/slic3r.pl index beea80373..e62c22b3a 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -12,7 +12,6 @@ use Getopt::Long qw(:config no_auto_abbrev); use List::Util qw(first); use POSIX qw(setlocale LC_NUMERIC); use Slic3r; -use Slic3r::Geometry qw(X Y); use Time::HiRes qw(gettimeofday tv_interval); $|++; @@ -122,55 +121,32 @@ if (@ARGV) { # slicing from command line $model = Slic3r::Model->read_from_file($input_file); } - my $need_arrange = $model->has_objects_with_no_instances; - if ($need_arrange) { - # apply a default position to all objects not having one - foreach my $object (@{$model->objects}) { - $object->add_instance(offset => [0,0]) if !defined $object->instances; - } - } - - # apply scaling and rotation supplied from command line if any - foreach my $instance (map @{$_->instances}, @{$model->objects}) { - $instance->scaling_factor($instance->scaling_factor * $config->scale); - $instance->rotation($instance->rotation + $config->rotate); - } - # TODO: --scale --rotate, --duplicate* shouldn't be stored in config - - if ($config->duplicate_grid->[X] > 1 || $config->duplicate_grid->[Y] > 1) { - $model->duplicate_objects_grid($config->duplicate_grid, $config->duplicate_distance); - } elsif ($need_arrange) { - $model->duplicate_objects($config->duplicate, $config->min_object_distance); - } elsif ($config->duplicate > 1) { - # if all input objects have defined position(s) apply duplication to the whole model - $model->duplicate($config->duplicate, $config->min_object_distance); - } - $model->center_instances_around_point($config->print_center); - if ($opt{info}) { $model->print_info; next; } - my $print = Slic3r::Print->new( - status_cb => sub { + my $sprint = Slic3r::Print::Simple->new( + scale => $config->scale, + rotate => $config->rotate, + duplicate => $config->duplicate, + duplicate_grid => $config->duplicate_grid, + status_cb => sub { my ($percent, $message) = @_; printf "=> %s\n", $message; }, + output_file => $opt{output}, ); - $print->apply_config($config); - foreach my $model_object (@{$model->objects}) { - $print->auto_assign_extruders($model_object); - $print->add_model_object($model_object); - } + + $sprint->apply_config($config); + $sprint->set_model($model); undef $model; # free memory - $print->validate; + if ($opt{export_svg}) { - $print->export_svg(output_file => $opt{output}); + $sprint->export_svg; } else { my $t0 = [gettimeofday]; - $print->process; - $print->export_gcode(output_file => $opt{output}); + $sprint->export_gcode; # output some statistics { @@ -180,7 +156,7 @@ if (@ARGV) { # slicing from command line } print map sprintf("Filament required: %.1fmm (%.1fcm3)\n", $_->absolute_E, $_->extruded_volume/1000), - @{$print->extruders}; + @{$sprint->extruders}; } } } else { From bfa2ee2770b3f3a49d7256be86d2afd1b5423c83 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 2 Jan 2014 18:01:21 +0100 Subject: [PATCH 08/19] Remove scale, rotate, duplicate, duplicate_grid from config (but leave them in CLI) --- lib/Slic3r/Config.pm | 11 +---------- lib/Slic3r/Print/Simple.pm | 1 - slic3r.pl | 25 +++++++++++++++++-------- xs/src/PrintConfig.hpp | 31 ------------------------------- 4 files changed, 18 insertions(+), 50 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 21fa2322b..41e5fa975 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -7,7 +7,7 @@ use List::Util qw(first); # cemetery of old config settings our @Ignore = qw(duplicate_x duplicate_y multiply_x multiply_y support_material_tool acceleration - adjust_overhang_flow standby_temperature); + adjust_overhang_flow standby_temperature scale rotate duplicate duplicate_grid); our $Options = print_config_def(); @@ -19,15 +19,6 @@ our $Options = print_config_def(); } } -# sub _get { -# my ($self, $opt_key) = @_; -# use XXX; -# if (!defined first { $_ eq $opt_key } @{$self->get_keys}) { -# ZZZ $opt_key; -# } -# return $self->get($opt_key); -# } - sub new_from_defaults { my $class = shift; my (@opt_keys) = @_; diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm index 0bf497b7d..a6f756343 100644 --- a/lib/Slic3r/Print/Simple.pm +++ b/lib/Slic3r/Print/Simple.pm @@ -57,7 +57,6 @@ sub set_model { $instance->scaling_factor($instance->scaling_factor * $self->scale); $instance->rotation($instance->rotation + $self->rotate); } - # TODO: --scale --rotate, --duplicate* shouldn't be stored in config if ($self->duplicate_grid->[X] > 1 || $self->duplicate_grid->[Y] > 1) { $model->duplicate_objects_grid($self->duplicate_grid, $self->_print->config->duplicate_distance); diff --git a/slic3r.pl b/slic3r.pl index e62c22b3a..bb2f7280e 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -37,6 +37,11 @@ my %cli_options = (); 'merge|m' => \$opt{merge}, 'repair' => \$opt{repair}, 'info' => \$opt{info}, + + 'scale=f' => \$opt{scale}, + 'rotate=i' => \$opt{rotate}, + 'duplicate=i' => \$opt{duplicate}, + 'duplicate-grid=s' => \$opt{duplicate_grid}, ); foreach my $opt_key (keys %{$Slic3r::Config::Options}) { my $cli = $Slic3r::Config::Options->{$opt_key}->{cli} or next; @@ -126,11 +131,15 @@ if (@ARGV) { # slicing from command line next; } + if (defined $opt{duplicate_grid}) { + $opt{duplicate_grid} = [ split /[,x]/, $opt{duplicate_grid}, 2 ]; + } + my $sprint = Slic3r::Print::Simple->new( - scale => $config->scale, - rotate => $config->rotate, - duplicate => $config->duplicate, - duplicate_grid => $config->duplicate_grid, + scale => $opt{scale} // 1, + rotate => $opt{rotate} // 0, + duplicate => $opt{duplicate} // 1, + duplicate_grid => $opt{duplicate_grid} // [1,1], status_cb => sub { my ($percent, $message) = @_; printf "=> %s\n", $message; @@ -394,11 +403,11 @@ $j (mm, default: $config->{brim_width}) Transform options: - --scale Factor for scaling input object (default: $config->{scale}) - --rotate Rotation angle in degrees (0-360, default: $config->{rotate}) - --duplicate Number of items with auto-arrange (1+, default: $config->{duplicate}) + --scale Factor for scaling input object (default: 1) + --rotate Rotation angle in degrees (0-360, default: 0) + --duplicate Number of items with auto-arrange (1+, default: 1) --bed-size Bed size, only used for auto-arrange (mm, default: $config->{bed_size}->[0],$config->{bed_size}->[1]) - --duplicate-grid Number of items with grid arrangement (default: $config->{duplicate_grid}->[0],$config->{duplicate_grid}->[1]) + --duplicate-grid Number of items with grid arrangement (default: 1,1) --duplicate-distance Distance in mm between copies (default: $config->{duplicate_distance}) Sequential printing options: diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index cecd00a33..dd9f2633c 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -126,11 +126,6 @@ class PrintConfigDef Options["disable_fan_first_layers"].cli = "disable-fan-first-layers=i"; Options["disable_fan_first_layers"].max = 1000; - Options["duplicate"].type = coInt; - Options["duplicate"].label = "Copies (autoarrange)"; - Options["duplicate"].cli = "duplicate=i"; - Options["duplicate"].min = 1; - Options["duplicate_distance"].type = coFloat; Options["duplicate_distance"].label = "Distance between copies"; Options["duplicate_distance"].tooltip = "Distance used for the auto-arrange feature of the plater."; @@ -138,10 +133,6 @@ class PrintConfigDef Options["duplicate_distance"].cli = "duplicate-distance=f"; Options["duplicate_distance"].aliases.push_back("multiply_distance"); - Options["duplicate_grid"].type = coPoint; - Options["duplicate_grid"].label = "Copies (grid)"; - Options["duplicate_grid"].cli = "duplicate-grid=s"; - Options["end_gcode"].type = coString; Options["end_gcode"].label = "End G-code"; Options["end_gcode"].tooltip = "This end procedure is inserted at the end of the output file. Note that you can use placeholder variables for all Slic3r settings."; @@ -584,16 +575,6 @@ class PrintConfigDef Options["retract_speed"].cli = "retract-speed=f@"; Options["retract_speed"].max = 1000; - Options["rotate"].type = coInt; - Options["rotate"].label = "Rotate"; - Options["rotate"].sidetext = "°"; - Options["rotate"].cli = "rotate=i"; - Options["rotate"].max = 359; - - Options["scale"].type = coFloat; - Options["scale"].label = "Scale"; - Options["scale"].cli = "scale=f"; - Options["skirt_distance"].type = coFloat; Options["skirt_distance"].label = "Distance from object"; Options["skirt_distance"].tooltip = "Distance between skirt and object(s). Set this to zero to attach the skirt to the object(s) and get a brim for better adhesion."; @@ -1057,9 +1038,7 @@ class PrintConfig : public virtual StaticConfig ConfigOptionBool cooling; ConfigOptionFloat default_acceleration; ConfigOptionInt disable_fan_first_layers; - ConfigOptionInt duplicate; ConfigOptionFloat duplicate_distance; - ConfigOptionPoint duplicate_grid; ConfigOptionString end_gcode; ConfigOptionFloatOrPercent external_perimeter_speed; ConfigOptionBool external_perimeters_first; @@ -1108,8 +1087,6 @@ class PrintConfig : public virtual StaticConfig ConfigOptionFloats retract_restart_extra; ConfigOptionFloats retract_restart_extra_toolchange; ConfigOptionInts retract_speed; - ConfigOptionInt rotate; - ConfigOptionFloat scale; ConfigOptionFloat skirt_distance; ConfigOptionInt skirt_height; ConfigOptionInt skirts; @@ -1147,9 +1124,7 @@ class PrintConfig : public virtual StaticConfig this->cooling.value = true; this->default_acceleration.value = 0; this->disable_fan_first_layers.value = 1; - this->duplicate.value = 1; this->duplicate_distance.value = 6; - this->duplicate_grid.point = Pointf(1,1); this->end_gcode.value = "M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n"; this->external_perimeter_speed.value = 70; this->external_perimeter_speed.percent = true; @@ -1212,8 +1187,6 @@ class PrintConfig : public virtual StaticConfig this->retract_restart_extra_toolchange.values[0] = 0; this->retract_speed.values.resize(1); this->retract_speed.values[0] = 30; - this->rotate.value = 0; - this->scale.value = 1; this->skirt_distance.value = 6; this->skirt_height.value = 1; this->skirts.value = 1; @@ -1255,9 +1228,7 @@ class PrintConfig : public virtual StaticConfig if (opt_key == "cooling") return &this->cooling; if (opt_key == "default_acceleration") return &this->default_acceleration; if (opt_key == "disable_fan_first_layers") return &this->disable_fan_first_layers; - if (opt_key == "duplicate") return &this->duplicate; if (opt_key == "duplicate_distance") return &this->duplicate_distance; - if (opt_key == "duplicate_grid") return &this->duplicate_grid; if (opt_key == "end_gcode") return &this->end_gcode; if (opt_key == "external_perimeter_speed") return &this->external_perimeter_speed; if (opt_key == "external_perimeters_first") return &this->external_perimeters_first; @@ -1306,8 +1277,6 @@ class PrintConfig : public virtual StaticConfig if (opt_key == "retract_restart_extra") return &this->retract_restart_extra; if (opt_key == "retract_restart_extra_toolchange") return &this->retract_restart_extra_toolchange; if (opt_key == "retract_speed") return &this->retract_speed; - if (opt_key == "rotate") return &this->rotate; - if (opt_key == "scale") return &this->scale; if (opt_key == "skirt_distance") return &this->skirt_distance; if (opt_key == "skirt_height") return &this->skirt_height; if (opt_key == "skirts") return &this->skirts; From 81663215c54ed97df49b93940fb89f2f77c75e58 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 2 Jan 2014 22:06:58 +0100 Subject: [PATCH 09/19] Adapt plater to the new split config --- lib/Slic3r/Config.pm | 23 +----- lib/Slic3r/GUI/Plater.pm | 10 ++- lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm | 66 ++++++++------- lib/Slic3r/Model.pm | 12 +-- lib/Slic3r/Print.pm | 82 ++++++++++++++++++- xs/src/Config.hpp | 2 +- xs/src/Print.cpp | 7 ++ xs/src/Print.hpp | 1 + xs/src/PrintConfig.hpp | 12 +-- xs/xsp/Config.xsp | 2 +- xs/xsp/Print.xsp | 1 + 11 files changed, 153 insertions(+), 65 deletions(-) diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index 41e5fa975..ae166b6c5 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -76,7 +76,7 @@ sub load { my ($file) = @_; my $ini = __PACKAGE__->read_ini($file); - my $config = __PACKAGE__->new; + my $config = $class->new; foreach my $opt_key (keys %{$ini->{_}}) { ($opt_key, my $value) = _handle_legacy($opt_key, $ini->{_}{$opt_key}); next if !defined $opt_key; @@ -88,7 +88,7 @@ sub load { sub clone { my $self = shift; - my $new = __PACKAGE__->new; + my $new = (ref $self)->new; $new->apply($self); return $new; } @@ -180,13 +180,14 @@ sub equals { return @{ $self->diff($other) } == 0; } +# this will *ignore* options not present in both configs sub diff { my ($self, $other) = @_; my @diff = (); foreach my $opt_key (sort @{$self->get_keys}) { push @diff, $opt_key - if !$other->has($opt_key) || $other->serialize($opt_key) ne $self->serialize($opt_key); + if $other->has($opt_key) && $other->serialize($opt_key) ne $self->serialize($opt_key); } return [@diff]; } @@ -265,27 +266,11 @@ sub validate { die "Invalid value for --infill-every-layers\n" if $self->infill_every_layers !~ /^\d+$/ || $self->infill_every_layers < 1; - # --scale - die "Invalid value for --scale\n" - if $self->scale <= 0; - # --bed-size die "Invalid value for --bed-size\n" if !ref $self->bed_size && (!$self->bed_size || $self->bed_size !~ /^\d+,\d+$/); - # --duplicate-grid - die "Invalid value for --duplicate-grid\n" - if !ref $self->duplicate_grid - && (!$self->duplicate_grid || $self->duplicate_grid !~ /^\d+,\d+$/); - - # --duplicate - die "Invalid value for --duplicate or --duplicate-grid\n" - if !$self->duplicate || $self->duplicate < 1 || !$self->duplicate_grid - || (grep !$_, @{$self->duplicate_grid}); - die "Use either --duplicate or --duplicate-grid (using both doesn't make sense)\n" - if $self->duplicate > 1 && $self->duplicate_grid && (grep $_ && $_ > 1, @{$self->duplicate_grid}); - # --skirt-height die "Invalid value for --skirt-height\n" if $self->skirt_height < -1; # -1 means as tall as the object diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index e0b0fcf4a..1fb606c3c 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -754,11 +754,15 @@ sub export_gcode2 { } if $Slic3r::have_threads; my $print = $self->{print}; - $print->apply_config($config); - $print->apply_extra_variables($extra_variables); + eval { - $print->config->validate; + # this will throw errors if config is not valid + $config->validate; + + $print->apply_config($config); + $print->apply_extra_variables($extra_variables); + $print->validate; { diff --git a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm index e681d7238..b2f5c3ce0 100644 --- a/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm +++ b/lib/Slic3r/GUI/Plater/ObjectSettingsDialog.pm @@ -114,7 +114,7 @@ sub update_optgroup { my $config = $self->model_object->config; my %categories = (); - foreach my $opt_key (keys %$config) { + foreach my $opt_key (@{$config->get_keys}) { my $category = $Slic3r::Config::Options->{$opt_key}{category}; $categories{$category} ||= []; push @{$categories{$category}}, $opt_key; @@ -288,31 +288,41 @@ sub new { # get unique materials used in this object $self->{materials} = [ $self->model_object->unique_materials ]; - # build an OptionsGroup - $self->{mapping} = { - (map { $self->{materials}[$_] => $_+1 } 0..$#{ $self->{materials} }), # defaults - %{$self->model_object->material_mapping}, - }; - my $optgroup = Slic3r::GUI::OptionsGroup->new( - parent => $self, - title => 'Extruders', - label_width => 300, - options => [ - map { - my $i = $_; - my $material_id = $self->{materials}[$i]; - { - opt_key => "material_extruder_$_", - type => 'i', - label => $self->model_object->model->get_material_name($material_id), - min => 1, - default => $self->{mapping}{$material_id}, - on_change => sub { $self->{mapping}{$material_id} = $_[0] }, - } - } 0..$#{ $self->{materials} } - ], - ); - $self->{sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 10); + # get the current mapping + $self->{mapping} = {}; + foreach my $material_id (@{ $self->{materials}}) { + my $config = $self->model_object->model->materials->{ $material_id }->config; + $self->{mapping}{$material_id} = ($config->perimeter_extruder // 0) + 1; + } + + if (@{$self->{materials}} > 0) { + # build an OptionsGroup + my $optgroup = Slic3r::GUI::OptionsGroup->new( + parent => $self, + title => 'Extruders', + label_width => 300, + options => [ + map { + my $i = $_; + my $material_id = $self->{materials}[$i]; + { + opt_key => "material_extruder_$_", + type => 'i', + label => $self->model_object->model->get_material_name($material_id), + min => 1, + default => $self->{mapping}{$material_id} // 1, + on_change => sub { $self->{mapping}{$material_id} = $_[0] }, + } + } 0..$#{ $self->{materials} } + ], + ); + $self->{sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 10); + } else { + my $label = Wx::StaticText->new($self, -1, "This object does not contain named materials.", + wxDefaultPosition, [-1, 25]); + $label->SetFont(Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT)); + $self->{sizer}->Add($label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10); + } $self->SetSizer($self->{sizer}); $self->{sizer}->SetSizeHints($self); @@ -324,10 +334,10 @@ sub Closing { my $self = shift; # save mappings into the plater object - foreach my $volume (@{$self->model_object}) { + foreach my $volume (@{$self->model_object->volumes}) { if (defined $volume->material_id) { my $config = $self->model_object->model->materials->{ $volume->material_id }->config; - $config->set('extruder', $self->{mapping}{ $volume->material_id }); + $config->set('extruder', $self->{mapping}{ $volume->material_id }-1); } } } diff --git a/lib/Slic3r/Model.pm b/lib/Slic3r/Model.pm index 401deb47e..3cbd97493 100644 --- a/lib/Slic3r/Model.pm +++ b/lib/Slic3r/Model.pm @@ -54,10 +54,11 @@ sub add_object { if (defined $volume->material_id) { # merge material attributes (should we rename materials in case of duplicates?) - $self->set_material($volume->material_id, { - %{ $object->model->materials->{$volume->material_id} }, - %{ $self->materials->{$volume->material_id} || {} }, - }); + my %attributes = %{ $object->model->materials->{$volume->material_id}->attributes }; + if (exists $self->materials->{$volume->material_id}) { + %attributes = (%attributes, %{ $self->materials->{$volume->material_id}->attributes }); + } + $self->set_material($volume->material_id, {%attributes}); } } @@ -478,7 +479,8 @@ sub unique_materials { my $self = shift; my %materials = (); - $materials{ $_->material_id // '_' } = 1 for @{$self->volumes}; + $materials{ $_->material_id } = 1 + for grep { defined $_->material_id } @{$self->volumes}; return sort keys %materials; } diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index d44a06b6c..c0ed2e11f 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -31,9 +31,87 @@ has 'brim' => (is => 'rw', default => sub { Slic3r::ExtrusionPath::Collection->n sub apply_config { my ($self, $config) = @_; - $self->config->apply_dynamic($config); + # handle changes to print config + my $print_diff = $self->config->diff($config); + if (@$print_diff) { + $self->config->apply_dynamic($config); + + # TODO: only invalidate changed steps + $self->_state->invalidate_all; + } + + # handle changes to object config defaults $self->default_object_config->apply_dynamic($config); + foreach my $object (@{$self->objects}) { + # we don't assume that $config contains a full ObjectConfig, + # so we base it on the current print-wise default + my $new = $self->default_object_config->clone; + + # we override the new config with object-specific options + $new->apply_dynamic($object->model_object->config); + + # check whether the new config is different from the current one + my $diff = $object->config->diff($new); + if (@$diff) { + $object->config->apply($new); + # TODO: only invalidate changed steps + $object->_state->invalidate_all; + } + } + + # handle changes to regions config defaults $self->default_region_config->apply_dynamic($config); + + # check whether after applying the new region config defaults to all existing regions + # they still have distinct configs; if not we need to re-add objects in order to + # merge the now-equal regions + + # first compute the transformed region configs + my @new_region_configs = (); + foreach my $region_id (0..$#{$self->regions}) { + my $new = $self->default_region_config->clone; + foreach my $object (@{$self->objects}) { + foreach my $volume_id (@{ $object->region_volumes->[$region_id] }) { + my $volume = $object->model_object->volumes->[$volume_id]; + next if !defined $volume->material_id; + my $material = $object->model_object->model->materials->{$volume->material_id}; + $new->apply_dynamic($material->config); + } + } + push @new_region_configs, $new; + } + + # then find the first pair of identical configs + my $have_identical_configs = 0; + my $region_diff = []; + for my $i (0..$#new_region_configs) { + for my $j (($i+1)..$#new_region_configs) { + if ($new_region_configs[$i]->equals($new_region_configs[$j])) { + $have_identical_configs = 1; + } + } + my $diff = $self->regions->[$i]->config->diff($new_region_configs[$i]); + push @$region_diff, @$diff; + } + + if ($have_identical_configs) { + # okay, the current subdivision of regions does not make sense anymore. + # we need to remove all objects and re-add them + my @model_objects = map $_->model_object, @{$self->object}; + $self->delete_all_objects; + $self->add_model_object($_) for @model_objects; + } elsif (@$region_diff > 0) { + # if there are no identical regions even after applying the change in + # region config defaults, but at least one region config option changed, + # store the new region configs and invalidate + # the affected step(s) + foreach my $region_id (0..$#{$self->regions}) { + $self->regions->[$region_id]->config->apply($new_region_configs[$region_id]); + } + + # TODO: only invalidate changed steps + $_->_state->invalidate_all for @{$self->objects}; + } } sub has_support_material { @@ -991,7 +1069,7 @@ sub auto_assign_extruders { if (defined $volume->material_id) { my $material = $model_object->model->materials->{ $volume->material_id }; my $config = $material->config; - $config->set_ifndef('perimeters_extruder', $i); + $config->set_ifndef('perimeter_extruder', $i); $config->set_ifndef('infill_extruder', $i); $config->set_ifndef('support_material_extruder', $i); $config->set_ifndef('support_material_interface_extruder', $i); diff --git a/xs/src/Config.hpp b/xs/src/Config.hpp index 9b9a7bf95..3af99a448 100644 --- a/xs/src/Config.hpp +++ b/xs/src/Config.hpp @@ -354,6 +354,7 @@ class ConfigOptionDef public: ConfigOptionType type; std::string label; + std::string full_label; std::string category; std::string tooltip; std::string sidetext; @@ -361,7 +362,6 @@ class ConfigOptionDef std::string scope; t_config_option_key ratio_over; bool multiline; - bool full_label; bool full_width; bool readonly; int height; diff --git a/xs/src/Print.cpp b/xs/src/Print.cpp index f5dd9fe7d..539206c49 100644 --- a/xs/src/Print.cpp +++ b/xs/src/Print.cpp @@ -33,4 +33,11 @@ PrintState::invalidate(PrintStep step) this->_done.erase(step); } +void +PrintState::invalidate_all() +{ + this->_started.clear(); + this->_done.clear(); +} + } diff --git a/xs/src/Print.hpp b/xs/src/Print.hpp index a79d544df..9e93408b2 100644 --- a/xs/src/Print.hpp +++ b/xs/src/Print.hpp @@ -22,6 +22,7 @@ class PrintState void set_started(PrintStep step); void set_done(PrintStep step); void invalidate(PrintStep step); + void invalidate_all(); }; } diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index dd9f2633c..8ac01f504 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -61,7 +61,7 @@ class PrintConfigDef Options["bed_temperature"].tooltip = "Bed temperature for layers after the first one. Set this to zero to disable bed temperature control commands in the output."; Options["bed_temperature"].sidetext = "°C"; Options["bed_temperature"].cli = "bed-temperature=i"; - Options["bed_temperature"].full_label = true; + Options["bed_temperature"].full_label = "Bed temperature"; Options["bed_temperature"].max = 300; Options["bottom_solid_layers"].type = coInt; @@ -70,7 +70,7 @@ class PrintConfigDef Options["bottom_solid_layers"].tooltip = "Number of solid layers to generate on bottom surfaces."; Options["bottom_solid_layers"].cli = "bottom-solid-layers=i"; Options["bottom_solid_layers"].scope = "object"; - Options["bottom_solid_layers"].full_label = true; + Options["bottom_solid_layers"].full_label = "Bottom solid layers"; Options["bridge_acceleration"].type = coFloat; Options["bridge_acceleration"].label = "Bridge"; @@ -346,7 +346,7 @@ class PrintConfigDef Options["infill_every_layers"].sidetext = "layers"; Options["infill_every_layers"].cli = "infill-every-layers=i"; Options["infill_every_layers"].scope = "object"; - Options["infill_every_layers"].full_label = true; + Options["infill_every_layers"].full_label = "Combine infill every n layers"; Options["infill_every_layers"].min = 1; Options["infill_extruder"].type = coInt; @@ -715,7 +715,7 @@ class PrintConfigDef Options["support_material_enforce_layers"].sidetext = "layers"; Options["support_material_enforce_layers"].cli = "support-material-enforce-layers=f"; Options["support_material_enforce_layers"].scope = "object"; - Options["support_material_enforce_layers"].full_label = true; + Options["support_material_enforce_layers"].full_label = "Enforce support for the first n layers"; Options["support_material_extruder"].type = coInt; Options["support_material_extruder"].label = "Support material extruder"; @@ -789,7 +789,7 @@ class PrintConfigDef Options["temperature"].tooltip = "Extruder temperature for layers after the first one. Set this to zero to disable temperature control commands in the output."; Options["temperature"].sidetext = "°C"; Options["temperature"].cli = "temperature=i@"; - Options["temperature"].full_label = true; + Options["temperature"].full_label = "Temperature"; Options["temperature"].max = 400; Options["thin_walls"].type = coBool; @@ -835,7 +835,7 @@ class PrintConfigDef Options["top_solid_layers"].tooltip = "Number of solid layers to generate on top surfaces."; Options["top_solid_layers"].cli = "top-solid-layers=i"; Options["top_solid_layers"].scope = "object"; - Options["top_solid_layers"].full_label = true; + Options["top_solid_layers"].full_label = "Top solid layers"; Options["travel_speed"].type = coFloat; Options["travel_speed"].label = "Travel"; diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index b7f6f3ec8..18deda295 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -142,6 +142,7 @@ print_config_def() } (void)hv_stores( hv, "type", newSVpv(opt_type, 0) ); (void)hv_stores( hv, "label", newSVpvn(optdef->label.c_str(), optdef->label.length()) ); + (void)hv_stores( hv, "full_label", newSVpvn(optdef->full_label.c_str(), optdef->full_label.length()) ); (void)hv_stores( hv, "category", newSVpvn(optdef->category.c_str(), optdef->category.length()) ); (void)hv_stores( hv, "tooltip", newSVpvn(optdef->tooltip.c_str(), optdef->tooltip.length()) ); (void)hv_stores( hv, "sidetext", newSVpvn(optdef->sidetext.c_str(), optdef->sidetext.length()) ); @@ -149,7 +150,6 @@ print_config_def() (void)hv_stores( hv, "scope", newSVpvn(optdef->scope.c_str(), optdef->scope.length()) ); (void)hv_stores( hv, "ratio_over", newSVpvn(optdef->ratio_over.c_str(), optdef->ratio_over.length()) ); (void)hv_stores( hv, "multiline", newSViv(optdef->multiline ? 1 : 0) ); - (void)hv_stores( hv, "full_label", newSViv(optdef->full_label ? 1 : 0) ); (void)hv_stores( hv, "full_width", newSViv(optdef->full_width ? 1 : 0) ); (void)hv_stores( hv, "readonly", newSViv(optdef->readonly ? 1 : 0) ); (void)hv_stores( hv, "height", newSViv(optdef->height) ); diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 0eef527ea..25f52a71c 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -13,6 +13,7 @@ void set_started(PrintStep step); void set_done(PrintStep step); void invalidate(PrintStep step); + void invalidate_all(); %{ %} From b9793b3f12ab5792232badd633098f41a14f4a5e Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 3 Jan 2014 00:34:30 +0100 Subject: [PATCH 10/19] Moved first_layer_extrusion_width to PrintConfig --- lib/Slic3r/Print.pm | 7 +++---- lib/Slic3r/Print/Object.pm | 17 ++++++++++++---- lib/Slic3r/Print/Region.pm | 4 ++-- lib/Slic3r/Print/SupportMaterial.pm | 31 ++++++++++++++++------------- xs/src/Config.hpp | 2 +- xs/src/PrintConfig.hpp | 8 ++++---- 6 files changed, 40 insertions(+), 29 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index c0ed2e11f..d5884c10c 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -662,10 +662,9 @@ sub make_skirt { # skirt may be printed on several layers, having distinct layer heights, # but loops must be aligned so can't vary width/spacing # TODO: use each extruder's own flow - my $region0_config = $self->regions->[0]->config; my $first_layer_height = $self->objects->[0]->config->get_value('first_layer_height'); my $flow = Slic3r::Flow->new( - width => ($region0_config->first_layer_extrusion_width || $region0_config->perimeter_extrusion_width), + width => ($self->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width), role => FLOW_ROLE_PERIMETER, nozzle_diameter => $self->config->nozzle_diameter->[0], layer_height => $first_layer_height, @@ -713,7 +712,7 @@ sub make_brim { # brim is only printed on first layer and uses support material extruder my $flow = Slic3r::Flow->new( - width => ($self->regions->[0]->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width), + width => ($self->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width), role => FLOW_ROLE_PERIMETER, nozzle_diameter => $self->config->nozzle_diameter->[ $self->objects->[0]->config->support_material_extruder-1 ], layer_height => $self->objects->[0]->config->get_abs_value('first_layer_height'), @@ -801,7 +800,7 @@ sub write_gcode { if $self->has_support_material; printf $fh "; first layer extrusion width = %.2fmm\n", $self->regions->[$region_id]->flow(FLOW_ROLE_PERIMETER, $layer_height, 0, 1)->width - if ($self->regions->[$region_id]->config->first_layer_extrusion_width ne '0'); + if $self->regions->[$region_id]->config->first_layer_extrusion_width; print $fh "\n"; } diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index bf4c2efba..84dc33b36 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -863,11 +863,20 @@ sub generate_support_material { return unless ($self->config->support_material || $self->config->raft_layers > 0) && $self->layer_count >= 2; + my $first_layer_flow = Slic3r::Flow->new( + width => ($self->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width), + role => FLOW_ROLE_SUPPORT_MATERIAL, + nozzle_diameter => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ], + layer_height => $self->config->get_abs_value('first_layer_height'), + bridge_flow_ratio => 0, + ); + my $s = Slic3r::Print::SupportMaterial->new( - print_config => $self->print->config, - object_config => $self->config, - flow => $self->support_material_flow, - interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE), + print_config => $self->print->config, + object_config => $self->config, + first_layer_flow => $first_layer_flow, + flow => $self->support_material_flow, + interface_flow => $self->support_material_flow(FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE), ); $s->generate($self); } diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm index ff05d5bc9..c587f515f 100644 --- a/lib/Slic3r/Print/Region.pm +++ b/lib/Slic3r/Print/Region.pm @@ -21,8 +21,8 @@ sub flow { if (!defined $config_width) { # get extrusion width from configuration # (might be an absolute value, or a percent value, or zero for auto) - if ($first_layer && $self->config->first_layer_extrusion_width ne '0') { - $config_width = $self->config->first_layer_extrusion_width; + if ($first_layer && $self->print->config->first_layer_extrusion_width) { + $config_width = $self->print->config->first_layer_extrusion_width; } elsif ($role == FLOW_ROLE_PERIMETER) { $config_width = $self->config->perimeter_extrusion_width; } elsif ($role == FLOW_ROLE_INFILL) { diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index 0dae19e18..b6d793ee1 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -9,9 +9,11 @@ use Slic3r::Geometry::Clipper qw(offset diff union union_ex intersection offset_ intersection_pl); use Slic3r::Surface ':types'; -has 'print_config' => (is => 'rw', required => 1); -has 'object_config' => (is => 'rw', required => 1); -has 'flow' => (is => 'rw', required => 1); +has 'print_config' => (is => 'rw', required => 1); +has 'object_config' => (is => 'rw', required => 1); +has 'flow' => (is => 'rw', required => 1); +has 'first_layer_flow' => (is => 'rw', required => 1); +has 'interface_flow' => (is => 'rw', required => 1); use constant DEBUG_CONTACT_ONLY => 0; @@ -385,11 +387,12 @@ sub clip_with_object { sub generate_toolpaths { my ($self, $object, $overhang, $contact, $interface, $base) = @_; - my $flow = $self->flow; + my $flow = $self->flow; + my $interface_flow = $self->interface_flow; # shape of contact area my $contact_loops = 1; - my $circle_radius = 1.5 * $flow->scaled_width; + my $circle_radius = 1.5 * $interface_flow->scaled_width; my $circle_distance = 3 * $circle_radius; my $circle = Slic3r::Polygon->new(map [ $circle_radius * cos $_, $circle_radius * sin $_ ], (5*PI/3, 4*PI/3, PI, 2*PI/3, PI/3, 0)); @@ -410,8 +413,8 @@ sub generate_toolpaths { ); my $interface_angle = $self->object_config->support_material_angle + 90; - my $interface_spacing = $self->object_config->support_material_interface_spacing + $flow->spacing; - my $interface_density = $interface_spacing == 0 ? 1 : $flow->spacing / $interface_spacing; + my $interface_spacing = $self->object_config->support_material_interface_spacing + $interface_flow->spacing; + my $interface_density = $interface_spacing == 0 ? 1 : $interface_flow->spacing / $interface_spacing; my $support_spacing = $self->object_config->support_material_spacing + $flow->spacing; my $support_density = $support_spacing == 0 ? 1 : $flow->spacing / $support_spacing; @@ -452,11 +455,11 @@ sub generate_toolpaths { my @loops0 = (); { # find centerline of the external loop of the contours - my @external_loops = @{offset($contact, -$flow->scaled_width/2)}; + my @external_loops = @{offset($contact, -$interface_flow->scaled_width/2)}; # only consider the loops facing the overhang { - my $overhang_with_margin = offset($overhang, +$flow->scaled_width/2); + my $overhang_with_margin = offset($overhang, +$interface_flow->scaled_width/2); @external_loops = grep { @{intersection_pl( [ $_->split_at_first_point ], @@ -476,8 +479,8 @@ sub generate_toolpaths { # make more loops my @loops = @loops0; for my $i (2..$contact_loops) { - my $d = ($i-1) * $flow->scaled_spacing; - push @loops, @{offset2(\@loops0, -$d -0.5*$flow->scaled_spacing, +0.5*$flow->scaled_spacing)}; + my $d = ($i-1) * $interface_flow->scaled_spacing; + push @loops, @{offset2(\@loops0, -$d -0.5*$interface_flow->scaled_spacing, +0.5*$interface_flow->scaled_spacing)}; } # clip such loops to the side oriented towards the object @@ -500,7 +503,7 @@ sub generate_toolpaths { @loops = map Slic3r::ExtrusionPath->new( polyline => $_, role => EXTR_ROLE_SUPPORTMATERIAL, - flow_spacing => $flow->spacing, + flow_spacing => $interface_flow->spacing, ), @loops; $layer->support_interface_fills->append(@loops); @@ -534,7 +537,7 @@ sub generate_toolpaths { my ($params, @p) = $fillers{interface}->fill_surface( Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL), density => $interface_density, - flow_spacing => $flow->spacing, + flow_spacing => $interface_flow->spacing, complete => 1, ); @@ -565,7 +568,7 @@ sub generate_toolpaths { $filler = $fillers{interface}; $filler->angle($self->object_config->support_material_angle + 90); $density = 0.5; - $flow_spacing = $object->print->first_layer_support_material_flow->spacing; + $flow_spacing = $self->first_layer_flow->spacing; } else { # draw a perimeter all around support infill # TODO: use brim ordering algorithm diff --git a/xs/src/Config.hpp b/xs/src/Config.hpp index 3af99a448..9306a18b2 100644 --- a/xs/src/Config.hpp +++ b/xs/src/Config.hpp @@ -374,7 +374,7 @@ class ConfigOptionDef std::vector enum_labels; t_config_enum_values enum_keys_map; - ConfigOptionDef() : multiline(false), full_label(false), full_width(false), readonly(false), + ConfigOptionDef() : multiline(false), full_width(false), readonly(false), height(-1), width(-1), min(INT_MIN), max(INT_MAX) {}; }; diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index 8ac01f504..05380ab16 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -952,7 +952,6 @@ class PrintRegionConfig : public virtual StaticConfig ConfigOptionInt fill_angle; ConfigOptionFloat fill_density; ConfigOptionEnum fill_pattern; - ConfigOptionFloatOrPercent first_layer_extrusion_width; ConfigOptionInt infill_extruder; ConfigOptionFloatOrPercent infill_extrusion_width; ConfigOptionInt infill_every_layers; @@ -976,8 +975,6 @@ class PrintRegionConfig : public virtual StaticConfig this->fill_angle.value = 45; this->fill_density.value = 0.4; this->fill_pattern.value = ipHoneycomb; - this->first_layer_extrusion_width.value = 200; - this->first_layer_extrusion_width.percent = true; this->infill_extruder.value = 1; this->infill_extrusion_width.value = 0; this->infill_extrusion_width.percent = false; @@ -1003,7 +1000,6 @@ class PrintRegionConfig : public virtual StaticConfig if (opt_key == "fill_angle") return &this->fill_angle; if (opt_key == "fill_density") return &this->fill_density; if (opt_key == "fill_pattern") return &this->fill_pattern; - if (opt_key == "first_layer_extrusion_width") return &this->first_layer_extrusion_width; if (opt_key == "infill_extruder") return &this->infill_extruder; if (opt_key == "infill_extrusion_width") return &this->infill_extrusion_width; if (opt_key == "infill_every_layers") return &this->infill_every_layers; @@ -1052,6 +1048,7 @@ class PrintConfig : public virtual StaticConfig ConfigOptionFloats filament_diameter; ConfigOptionFloat first_layer_acceleration; ConfigOptionInt first_layer_bed_temperature; + ConfigOptionFloatOrPercent first_layer_extrusion_width; ConfigOptionFloatOrPercent first_layer_speed; ConfigOptionInts first_layer_temperature; ConfigOptionBool g0; @@ -1142,6 +1139,8 @@ class PrintConfig : public virtual StaticConfig this->filament_diameter.values[0] = 3; this->first_layer_acceleration.value = 0; this->first_layer_bed_temperature.value = 0; + this->first_layer_extrusion_width.value = 200; + this->first_layer_extrusion_width.percent = true; this->first_layer_speed.value = 30; this->first_layer_speed.percent = true; this->first_layer_temperature.values.resize(1); @@ -1242,6 +1241,7 @@ class PrintConfig : public virtual StaticConfig if (opt_key == "filament_diameter") return &this->filament_diameter; if (opt_key == "first_layer_acceleration") return &this->first_layer_acceleration; if (opt_key == "first_layer_bed_temperature") return &this->first_layer_bed_temperature; + if (opt_key == "first_layer_extrusion_width") return &this->first_layer_extrusion_width; if (opt_key == "first_layer_speed") return &this->first_layer_speed; if (opt_key == "first_layer_temperature") return &this->first_layer_temperature; if (opt_key == "g0") return &this->g0; From 52ce6e4a7bfc8b223f68199cb79f910e26491914 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 3 Jan 2014 10:44:36 +0100 Subject: [PATCH 11/19] Some initial work for refactoring Print->extruders --- lib/Slic3r/Print.pm | 20 ++++++++++++-------- lib/Slic3r/Print/Object.pm | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index d5884c10c..f7283954c 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -18,7 +18,6 @@ has 'default_region_config' => (is => 'ro', default => sub { Slic3r::Config::Pr has 'extra_variables' => (is => 'rw', default => sub {{}}); has 'objects' => (is => 'rw', default => sub {[]}); has 'status_cb' => (is => 'rw'); -has 'extruders' => (is => 'rw', default => sub {[]}); has 'regions' => (is => 'rw', default => sub {[]}); has '_state' => (is => 'ro', default => sub { Slic3r::Print::State->new }); @@ -282,8 +281,9 @@ sub validate { } } -sub init_extruders { - my $self = shift; +# 0-based indices of used extruders +sub extruders { + my ($self) = @_; # initialize all extruder(s) we need my @used_extruders = (); @@ -298,7 +298,15 @@ sub init_extruders { qw(support_material support_material_interface); } - for my $extruder_id (keys %{{ map {$_ => 1} @used_extruders }}) { + my %h = map { $_ => 1 } @used_extruders; + return [ sort keys %h ]; +} + +sub init_extruders { + my $self = shift; + + # initialize all extruder(s) we need + for my $extruder_id (@{$self->extruders}) { # make sure print config contains a value for all extruders my %extruder_config = (); foreach my $opt_key (@{&Slic3r::Extruder::OPTIONS}) { @@ -845,10 +853,6 @@ sub write_gcode { } } - # always start with first extruder - # TODO: make sure we select the first *used* extruder - print $fh $gcodegen->set_extruder($self->extruders->[0]); - # initialize a motion planner for object-to-object travel moves if ($self->config->avoid_crossing_perimeters) { my $distance_from_objects = 1; diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 84dc33b36..fd07d73e5 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -758,7 +758,7 @@ sub combine_infill { my $every = $region->config->infill_every_layers; # limit the number of combined layers to the maximum height allowed by this regions' nozzle - my $nozzle_diameter = $self->print->regions->[$region_id]->extruders->{infill}->nozzle_diameter; + my $nozzle_diameter = $self->print->config->nozzle_diameter->[ $region->config->infill_extruder-1 ]; # define the combinations my @combine = (); # layer_id => thickness in layers From 8ed738d3f7dcc5ae3d47415eff331f0f1e874da0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 3 Jan 2014 18:27:46 +0100 Subject: [PATCH 12/19] More incomplete work for Flow/Extruder refactoring --- lib/Slic3r.pm | 1 - lib/Slic3r/Extruder.pm | 56 ++++++-------- lib/Slic3r/ExtrusionLoop.pm | 3 +- lib/Slic3r/Fill.pm | 12 +-- lib/Slic3r/Fill/Honeycomb.pm | 6 +- lib/Slic3r/Fill/Rectilinear.pm | 15 ++-- lib/Slic3r/Flow.pm | 115 ++++++++++++++++++++-------- lib/Slic3r/GCode.pm | 37 ++++----- lib/Slic3r/GCode/Layer.pm | 12 +-- lib/Slic3r/Layer/Region.pm | 19 ++--- lib/Slic3r/Print.pm | 48 +++++------- lib/Slic3r/Print/Object.pm | 4 +- lib/Slic3r/Print/Region.pm | 2 +- lib/Slic3r/Print/SupportMaterial.pm | 49 ++++++------ t/arcs.t | 6 +- t/fill.t | 4 +- xs/lib/Slic3r/XS.pm | 12 +-- xs/src/ExtrusionEntity.cpp | 3 +- xs/src/ExtrusionEntity.hpp | 3 +- xs/src/PrintConfig.hpp | 3 + xs/t/07_extrusionpath.t | 1 + xs/t/08_extrusionloop.t | 1 + xs/t/12_extrusionpathcollection.t | 6 +- xs/t/15_config.t | 15 +++- xs/xsp/Config.xsp | 2 +- xs/xsp/ExtrusionLoop.xsp | 24 ++---- xs/xsp/ExtrusionPath.xsp | 24 ++---- 27 files changed, 250 insertions(+), 233 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 1c5ffa74c..710e33e19 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -77,7 +77,6 @@ our $build = eval "use Slic3r::Build; 1"; use constant SCALING_FACTOR => 0.000001; use constant RESOLUTION => 0.0125; use constant SCALED_RESOLUTION => RESOLUTION / SCALING_FACTOR; -use constant OVERLAP_FACTOR => 1; use constant SMALL_PERIMETER_LENGTH => (6.5 / SCALING_FACTOR) * 2 * PI; use constant LOOP_CLIPPING_LENGTH_OVER_SPACING => 0.15; use constant INFILL_OVERLAP_OVER_SPACING => 0.45; diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm index 9b8b1dc97..2a8371cef 100644 --- a/lib/Slic3r/Extruder.pm +++ b/lib/Slic3r/Extruder.pm @@ -26,13 +26,26 @@ has 'retracted' => (is => 'rw', default => sub {0} ); has 'restart_extra' => (is => 'rw', default => sub {0} ); has 'e_per_mm3' => (is => 'lazy'); has 'retract_speed_mm_min' => (is => 'lazy'); -has '_mm3_per_mm_cache' => (is => 'ro', default => sub {{}}); use constant EXTRUDER_ROLE_PERIMETER => 1; use constant EXTRUDER_ROLE_INFILL => 2; use constant EXTRUDER_ROLE_SUPPORT_MATERIAL => 3; use constant EXTRUDER_ROLE_SUPPORT_MATERIAL_INTERFACE => 4; +sub new_from_config { + my ($class, $config, $extruder_id) = @_; + + my %conf = ( + id => $extruder_id, + use_relative_e_distances => $config->use_relative_e_distances, + ); + foreach my $opt_key (@{&OPTIONS}) { + my $value = $config->get($opt_key); + $conf{$opt_key} = $value->[$extruder_id] // $value->[0]; + } + return $class->new(%conf); +} + sub _build_e_per_mm3 { my $self = shift; return $self->extrusion_multiplier * (4 / (($self->filament_diameter ** 2) * PI)); @@ -43,6 +56,15 @@ sub _build_retract_speed_mm_min { return $self->retract_speed * 60; } +sub reset { + my ($self) = @_; + + $self->E(0); + $self->absolute_E(0); + $self->retracted(0); + $self->restart_extra(0); +} + sub scaled_wipe_distance { my ($self, $travel_speed) = @_; @@ -66,37 +88,9 @@ sub extruded_volume { return $self->absolute_E * ($self->filament_diameter**2) * PI/4; } -sub make_flow { - my $self = shift; - return Slic3r::Flow->new(nozzle_diameter => $self->nozzle_diameter, @_); -} - -sub mm3_per_mm { - my $self = shift; - my ($s, $h) = @_; - - my $cache_key = "${s}_${h}"; - if (!exists $self->_mm3_per_mm_cache->{$cache_key}) { - my $w_threshold = $h + $self->nozzle_diameter; - my $s_threshold = $w_threshold - &Slic3r::OVERLAP_FACTOR * ($w_threshold - ($w_threshold - $h * (1 - PI/4))); - - if ($s >= $s_threshold) { - # rectangle with semicircles at the ends - my $w = $s + &Slic3r::OVERLAP_FACTOR * $h * (1 - PI/4); - $self->_mm3_per_mm_cache->{$cache_key} = $w * $h + ($h**2) / 4 * (PI - 4); - } else { - # rectangle with shrunk semicircles at the ends - my $w = ($s + $self->nozzle_diameter * &Slic3r::OVERLAP_FACTOR * (PI/4 - 1)) / (1 + &Slic3r::OVERLAP_FACTOR * (PI/4 - 1)); - $self->_mm3_per_mm_cache->{$cache_key} = $self->nozzle_diameter * $h * (1 - PI/4) + $h * $w * PI/4; - } - } - return $self->_mm3_per_mm_cache->{$cache_key}; -} - sub e_per_mm { - my $self = shift; - my ($s, $h) = @_; - return $self->mm3_per_mm($s, $h) * $self->e_per_mm3; + my ($self, $mm3_per_mm) = @_; + return $mm3_per_mm * $self->e_per_mm3; } 1; diff --git a/lib/Slic3r/ExtrusionLoop.pm b/lib/Slic3r/ExtrusionLoop.pm index 57f380eec..7123b3a77 100644 --- a/lib/Slic3r/ExtrusionLoop.pm +++ b/lib/Slic3r/ExtrusionLoop.pm @@ -8,8 +8,7 @@ sub split_at { return Slic3r::ExtrusionPath->new( polyline => $self->polygon->split_at(@_), role => $self->role, - flow_spacing => $self->flow_spacing, - height => $self->height, + mm3_per_mm => $self->mm3_per_mm, ); } diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 7969431c5..45508169f 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -158,20 +158,17 @@ sub make_fill { next SURFACE unless $density > 0; } - my $flow_spacing = $flow->spacing; - my $f = $self->filler($filler); $f->layer_id($layerm->id); $f->angle($layerm->config->fill_angle); my ($params, @polylines) = $f->fill_surface( $surface, - density => $density, - flow_spacing => $flow_spacing, + density => $density, + flow => $flow, ); next unless @polylines; - # ugly hack(tm) to get the right amount of flow (GCode.pm should be fixed) - $params->{flow_spacing} = $flow->width if $is_bridge; + my $mm3_per_mm = $params->{flow}->mm3_per_mm($surface->thickness); # save into layer push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new; @@ -187,8 +184,7 @@ sub make_fill { : $is_solid ? (($surface->surface_type == S_TYPE_TOP) ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL) : EXTR_ROLE_FILL), - height => $surface->thickness, - flow_spacing => $params->{flow_spacing} || (warn "Warning: no flow_spacing was returned by the infill engine, please report this to the developer\n"), + mm3_per_mm => $mm3_per_mm, ), @polylines, ); push @fills_ordering_points, $polylines[0]->first_point; diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm index caaa15407..1e6df2742 100644 --- a/lib/Slic3r/Fill/Honeycomb.pm +++ b/lib/Slic3r/Fill/Honeycomb.pm @@ -17,11 +17,11 @@ sub fill_surface { my $rotate_vector = $self->infill_direction($surface); # cache hexagons math - my $cache_id = sprintf "d%s_s%s", $params{density}, $params{flow_spacing}; + my $cache_id = sprintf "d%s_s%s", $params{density}, $params{flow}->spacing; my $m; if (!($m = $self->cache->{$cache_id})) { $m = $self->cache->{$cache_id} = {}; - my $min_spacing = scale $params{flow_spacing}; + my $min_spacing = $params{flow}->scaled_spacing; $m->{distance} = $min_spacing / $params{density}; $m->{hex_side} = $m->{distance} / (sqrt(3)/2); $m->{hex_width} = $m->{distance} * 2; # $m->{hex_width} == $m->{hex_side} * sqrt(3); @@ -120,7 +120,7 @@ sub fill_surface { )}; } - return { flow_spacing => $params{flow_spacing} }, @paths; + return { flow => $params{flow} }, @paths; } 1; diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index 335bf5b23..39fa5b941 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -17,8 +17,8 @@ sub fill_surface { my $rotate_vector = $self->infill_direction($surface); $self->rotate_points($expolygon, $rotate_vector); - my $flow_spacing = $params{flow_spacing}; - my $min_spacing = scale $params{flow_spacing}; + my $flow = $params{flow}; + my $min_spacing = $flow->scaled_spacing; my $line_spacing = $min_spacing / $params{density}; my $line_oscillation = $line_spacing - $min_spacing; my $is_line_pattern = $self->isa('Slic3r::Fill::Line'); @@ -30,7 +30,12 @@ sub fill_surface { width => $bounding_box->size->[X], distance => $line_spacing, ); - $flow_spacing = unscale $line_spacing; + $flow = Slic3r::Flow->new_from_spacing( + spacing => unscale($line_spacing), + nozzle_diameter => $flow->nozzle_diameter, + layer_height => $surface->thickness, + bridge => $flow->bridge, + ); } else { # extend bounding box so that our pattern will be aligned with other layers $bounding_box->extents->[X][MIN] -= $bounding_box->x_min % $line_spacing; @@ -62,7 +67,7 @@ sub fill_surface { # connect lines unless ($params{dont_connect} || !@polylines) { # prevent calling leftmost_point() on empty collections - my ($expolygon_off) = @{$expolygon->offset_ex(scale $params{flow_spacing}/2)}; + my ($expolygon_off) = @{$expolygon->offset_ex($min_spacing/2)}; my $collection = Slic3r::Polyline::Collection->new(@polylines); @polylines = (); @@ -97,7 +102,7 @@ sub fill_surface { # paths must be rotated back $self->rotate_points_back(\@polylines, $rotate_vector); - return { flow_spacing => $flow_spacing }, @polylines; + return { flow => $flow }, @polylines; } 1; diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index 3930dce52..5b1502a73 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -9,8 +9,10 @@ our %EXPORT_TAGS = (roles => \@EXPORT_OK); use Slic3r::Geometry qw(PI); -has 'width' => (is => 'ro'); -has 'spacing' => (is => 'ro'); +has 'width' => (is => 'ro', required => 1); +has 'spacing' => (is => 'ro', required => 1); +has 'nozzle_diameter' => (is => 'ro', required => 1); +has 'bridge' => (is => 'ro', default => sub {0}); has 'scaled_width' => (is => 'lazy'); has 'scaled_spacing' => (is => 'lazy'); @@ -21,32 +23,67 @@ use constant FLOW_ROLE_TOP_SOLID_INFILL => 4; use constant FLOW_ROLE_SUPPORT_MATERIAL => 5; use constant FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE => 6; -sub BUILDARGS { - my ($self, %args) = @_; +use constant BRIDGE_EXTRA_SPACING => 0.05; +use constant OVERLAP_FACTOR => 1; + +sub new_from_width { + my ($class, %args) = @_; - # the constructor can take two sets of arguments: - # - width (only absolute value), spacing - # - width (abs/%/0), role, nozzle_diameter, layer_height, bridge_flow_ratio - # (if bridge_flow_ratio == 0, we return a non-bridge flow) - - if (exists $args{role}) { - if ($args{width} eq '0') { - $args{width} = $self->_width(@args{qw(role nozzle_diameter layer_height bridge_flow_ratio)}); - } elsif ($args{width} =~ /^(\d+(?:\.\d+)?)%$/) { - $args{width} = $args{layer_height} * $1 / 100; - } - $args{spacing} = $self->_spacing(@args{qw(width nozzle_diameter layer_height bridge_flow_ratio)}); - %args = ( - width => $args{width}, - spacing => $args{spacing}, - ); + if ($args{width} eq '0') { + $args{width} = _width(@args{qw(role nozzle_diameter layer_height bridge_flow_ratio)}); + } elsif ($args{width} =~ /^(\d+(?:\.\d+)?)%$/) { + $args{width} = $args{layer_height} * $1 / 100; } - return {%args}; + return $class->new( + width => $args{width}, + spacing => _spacing(@args{qw(width nozzle_diameter layer_height bridge_flow_ratio)}), + nozzle_diameter => $args{nozzle_diameter}, + bridge => ($args{bridge_flow_ratio} > 0) ? 1 : 0, + ); +} + +sub new_from_spacing { + my ($class, %args) = @_; + + return $class->new( + width => _width_from_spacing(@args{qw(spacing nozzle_diameter layer_height bridge)}), + spacing => $args{spacing}, + nozzle_diameter => $args{nozzle_diameter}, + bridge => $args{bridge}, + ); +} + +sub clone { + my $self = shift; + + return (ref $self)->new( + width => $self->width, + spacing => $self->spacing, + nozzle_diameter => $self->nozzle_diameter, + bridge => $self->bridge, + ); +} + +sub mm3_per_mm { + my ($self, $h) = @_; + + my $w = $self->width; + my $s = $self->spacing; + + if ($self->bridge) { + return ($s**2) * PI/4; + } elsif ($w >= ($self->nozzle_diameter + $h)) { + # rectangle with semicircles at the ends + return $w * $h + ($h**2) / 4 * (PI - 4); + } else { + # rectangle with shrunk semicircles at the ends + return $self->nozzle_diameter * $h * (1 - PI/4) + $h * $w * PI/4; + } } sub _width { - my ($self, $role, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_; + my ($role, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_; if ($bridge_flow_ratio > 0) { return sqrt($bridge_flow_ratio * ($nozzle_diameter**2)); @@ -78,11 +115,30 @@ sub _width { return $width; } +sub _width_from_spacing { + my ($s, $nozzle_diameter, $h, $bridge) = @_; + + if ($bridge) { + return $s - BRIDGE_EXTRA_SPACING; + } + + my $w_threshold = $h + $nozzle_diameter; + my $s_threshold = $w_threshold - OVERLAP_FACTOR * ($w_threshold - ($w_threshold - $h * (1 - PI/4))); + + if ($s >= $s_threshold) { + # rectangle with semicircles at the ends + return $s + OVERLAP_FACTOR * $h * (1 - PI/4); + } else { + # rectangle with shrunk semicircles at the ends + return ($s + $nozzle_diameter * OVERLAP_FACTOR * (PI/4 - 1)) / (1 + OVERLAP_FACTOR * (PI/4 - 1)); + } +} + sub _spacing { - my ($self, $width, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_; + my ($width, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_; if ($bridge_flow_ratio > 0) { - return $width + 0.05; + return $width + BRIDGE_EXTRA_SPACING; } my $min_flow_spacing; @@ -93,16 +149,7 @@ sub _spacing { # rectangle with shrunk semicircles at the ends $min_flow_spacing = $nozzle_diameter * (1 - PI/4) + $width * PI/4; } - return $width - &Slic3r::OVERLAP_FACTOR * ($width - $min_flow_spacing); -} - -sub clone { - my $self = shift; - - return (ref $self)->new( - width => $self->width, - spacing => $self->spacing, - ); + return $width - OVERLAP_FACTOR * ($width - $min_flow_spacing); } sub _build_scaled_width { diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 66175260f..a8f7526ad 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -10,8 +10,6 @@ use Slic3r::Surface ':types'; has 'print_config' => (is => 'ro', default => sub { Slic3r::Config::Print->new }); has 'extra_variables' => (is => 'rw', default => sub {{}}); -has 'extruders' => (is => 'ro', required => 1); -has 'multiple_extruders' => (is => 'lazy'); has 'standby_points' => (is => 'rw'); has 'enable_loop_clipping' => (is => 'rw', default => sub {1}); has 'enable_wipe' => (is => 'lazy'); # at least one extruder has wipe enabled @@ -27,6 +25,7 @@ has 'z' => (is => 'rw'); has 'speed' => (is => 'rw'); has '_extrusion_axis' => (is => 'rw'); has '_retract_lift' => (is => 'rw'); +has 'extruders' => (is => 'ro', default => sub {[]}); has 'speeds' => (is => 'lazy'); # mm/min has 'external_mp' => (is => 'rw'); @@ -49,6 +48,14 @@ sub BUILD { $self->_retract_lift($self->print_config->retract_lift->[0]); } +sub set_extruders { + my ($self, $extruder_ids) = @_; + + foreach my $i (@$extruder_ids) { + $self->extruders->[$i] = Slic3r::Extruder->new_from_config($self->print_config, $i); + } +} + sub _build_speeds { my $self = shift; return { @@ -73,7 +80,7 @@ my %role_speeds = ( &EXTR_ROLE_GAPFILL => 'gap_fill', ); -sub _build_multiple_extruders { +sub multiple_extruders { my $self = shift; return @{$self->extruders} > 1; } @@ -320,18 +327,8 @@ sub extrude_path { $gcode .= $self->set_acceleration($acceleration) if $acceleration; } - my $area; # mm^3 of extrudate per mm of tool movement - if ($path->is_bridge) { - my $s = $path->flow_spacing; - $area = ($s**2) * PI/4; - } else { - my $s = $path->flow_spacing; - my $h = (defined $path->height && $path->height != -1) ? $path->height : $self->layer->height; - $area = $self->extruder->mm3_per_mm($s, $h); - } - # calculate extrusion length per distance unit - my $e = $self->extruder->e_per_mm3 * $area; + my $e = $self->extruder->e_per_mm3 * $path->mm3_per_mm; $e = 0 if !$self->_extrusion_axis; # set speed @@ -640,14 +637,14 @@ sub _Gx { } sub set_extruder { - my ($self, $extruder) = @_; + my ($self, $extruder_id) = @_; # return nothing if this extruder was already selected - return "" if (defined $self->extruder) && ($self->extruder->id == $extruder->id); + return "" if (defined $self->extruder) && ($self->extruder->id == $extruder_id); # if we are running a single-extruder setup, just set the extruder and return nothing if (!$self->multiple_extruders) { - $self->extruder($extruder); + $self->extruder($self->extruders->[$extruder_id]); return ""; } @@ -659,7 +656,7 @@ sub set_extruder { if (defined $self->extruder && $self->print_config->toolchange_gcode) { $gcode .= sprintf "%s\n", $self->replace_variables($self->print_config->toolchange_gcode, { previous_extruder => $self->extruder->id, - next_extruder => $extruder->id, + next_extruder => $extruder_id, }); } @@ -676,14 +673,14 @@ sub set_extruder { } # set the new extruder - $self->extruder($extruder); + $self->extruder($self->extruders->[$extruder_id]); $gcode .= sprintf "%s%d%s\n", ($self->print_config->gcode_flavor eq 'makerware' ? 'M135 T' : $self->print_config->gcode_flavor eq 'sailfish' ? 'M108 T' : 'T'), - $extruder->id, + $extruder_id, ($self->print_config->gcode_comments ? ' ; change extruder' : ''); $gcode .= $self->reset_e; diff --git a/lib/Slic3r/GCode/Layer.pm b/lib/Slic3r/GCode/Layer.pm index f64b6a1ba..96029eaab 100644 --- a/lib/Slic3r/GCode/Layer.pm +++ b/lib/Slic3r/GCode/Layer.pm @@ -85,7 +85,7 @@ sub process_layer { # when printing layers > 0 ignore 'min_skirt_length' and # just use the 'skirts' setting; also just use the current extruder last if ($layer->id > 0) && ($i >= $self->print->config->skirts); - $gcode .= $self->gcodegen->set_extruder($self->extruders->[ ($i/@{$self->extruders}) % @{$self->extruders} ]) + $gcode .= $self->gcodegen->set_extruder(($i/@{$self->extruders}) % @{$self->extruders}) if $layer->id == 0; $gcode .= $self->gcodegen->extrude_loop($skirt_loops[$i], 'skirt'); } @@ -96,7 +96,7 @@ sub process_layer { # extrude brim if (!$self->brim_done) { - $gcode .= $self->gcodegen->set_extruder($self->extruders->[$self->print->objects->[0]->config->support_material_extruder-1]); + $gcode .= $self->gcodegen->set_extruder($self->print->objects->[0]->config->support_material_extruder-1); $self->gcodegen->set_shift(@{$self->shift}); $gcode .= $self->gcodegen->extrude_loop($_, 'brim') for @{$self->print->brim}; $self->brim_done(1); @@ -113,13 +113,13 @@ sub process_layer { # and also because we avoid travelling on other things when printing it if ($layer->isa('Slic3r::Layer::Support')) { if ($layer->support_interface_fills->count > 0) { - $gcode .= $self->gcodegen->set_extruder($self->extruders->[$object->config->support_material_interface_extruder-1]); + $gcode .= $self->gcodegen->set_extruder($object->config->support_material_interface_extruder-1); my %params = (speed => $object->config->support_material_speed*60); $gcode .= $self->gcodegen->extrude_path($_, 'support material interface', %params) for @{$layer->support_interface_fills->chained_path_from($self->gcodegen->last_pos, 0)}; } if ($layer->support_fills->count > 0) { - $gcode .= $self->gcodegen->set_extruder($self->extruders->[$object->config->support_material_extruder-1]); + $gcode .= $self->gcodegen->set_extruder($object->config->support_material_extruder-1); my %params = (speed => $object->config->support_material_speed*60); $gcode .= $self->gcodegen->extrude_path($_, 'support material', %params) for @{$layer->support_fills->chained_path_from($self->gcodegen->last_pos, 0)}; @@ -205,7 +205,7 @@ sub _extrude_perimeters { return "" if !@{ $island->{perimeters} }; my $gcode = ""; - $gcode .= $self->gcodegen->set_extruder($self->extruders->[$region->config->perimeter_extruder-1]); + $gcode .= $self->gcodegen->set_extruder($region->config->perimeter_extruder-1); $gcode .= $self->gcodegen->extrude($_, 'perimeter') for @{ $island->{perimeters} }; return $gcode; } @@ -217,7 +217,7 @@ sub _extrude_infill { return "" if !@{ $island->{fills} }; my $gcode = ""; - $gcode .= $self->gcodegen->set_extruder($self->extruders->[$region->config->infill_extruder-1]); + $gcode .= $self->gcodegen->set_extruder($region->config->infill_extruder-1); for my $fill (@{ $island->{fills} }) { if ($fill->isa('Slic3r::ExtrusionPath::Collection')) { $gcode .= $self->gcodegen->extrude($_, 'fill') diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index ce22d9c88..53b3834d3 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -125,6 +125,7 @@ sub make_perimeters { my $self = shift; my $perimeter_flow = $self->flow(FLOW_ROLE_PERIMETER); + my $mm3_per_mm = $perimeter_flow->mm3_per_mm($self->height); my $pwidth = $perimeter_flow->scaled_width; my $pspacing = $perimeter_flow->scaled_spacing; my $ispacing = $self->flow(FLOW_ROLE_SOLID_INFILL)->scaled_spacing; @@ -264,7 +265,7 @@ sub make_perimeters { push @loops, Slic3r::ExtrusionLoop->new( polygon => $polygon, role => $role, - flow_spacing => $perimeter_flow->spacing, + mm3_per_mm => $mm3_per_mm, ); } return @loops; @@ -290,8 +291,8 @@ sub make_perimeters { for my $p (@p) { next if $p->length <= $pspacing * 2; my %params = ( - role => EXTR_ROLE_EXTERNAL_PERIMETER, - flow_spacing => $perimeter_flow->spacing, + role => EXTR_ROLE_EXTERNAL_PERIMETER, + mm3_per_mm => $mm3_per_mm, ); push @paths, $p->isa('Slic3r::Polygon') ? Slic3r::ExtrusionLoop->new(polygon => $p, %params) @@ -339,8 +340,8 @@ sub _fill_gaps { # fill gaps using dynamic extrusion width, by treating them like thin polygons, # thus generating the skeleton and using it to fill them my %path_args = ( - role => EXTR_ROLE_SOLIDFILL, - flow_spacing => $flow->spacing, + role => EXTR_ROLE_SOLIDFILL, + mm3_per_mm => $flow->mm3_per_mm($self->height), ); $self->thin_fills->append(map { $_->isa('Slic3r::Polygon') @@ -360,9 +361,10 @@ sub _fill_gaps { foreach my $expolygon (@infill) { my ($params, @paths) = $filler->fill_surface( Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNALSOLID), - density => 1, - flow_spacing => $flow->spacing, + density => 1, + flow => $flow, ); + my $mm3_per_mm = $params->{flow}->mm3_per_mm($self->height); # Split polylines into lines so that the chained_path() search # at the final stage has more freedom and will choose starting @@ -379,8 +381,7 @@ sub _fill_gaps { @paths = map Slic3r::ExtrusionPath->new( polyline => Slic3r::Polyline->new(@$_), role => EXTR_ROLE_GAPFILL, - height => $self->height, - flow_spacing => $params->{flow_spacing}, + mm3_per_mm => $mm3_per_mm, ), @lines; $_->simplify($flow->scaled_width/3) for @paths; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index f7283954c..ebaba5ce1 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -305,26 +305,6 @@ sub extruders { sub init_extruders { my $self = shift; - # initialize all extruder(s) we need - for my $extruder_id (@{$self->extruders}) { - # make sure print config contains a value for all extruders - my %extruder_config = (); - foreach my $opt_key (@{&Slic3r::Extruder::OPTIONS}) { - my $value = $self->config->get($opt_key); - if (!defined $value->[$extruder_id]) { - $value->[$extruder_id] = $value->[0]; - $self->config->set($opt_key, $value); - } - $extruder_config{$opt_key} = $value->[$extruder_id]; - } - - $self->extruders->[$extruder_id] = Slic3r::Extruder->new( - id => $extruder_id, - use_relative_e_distances => $self->config->use_relative_e_distances, - %extruder_config, - ); - } - # enforce tall skirt if using ooze_prevention # FIXME: this is not idempotent (i.e. switching ooze_prevention off will not revert skirt settings) if ($self->config->ooze_prevention && @{$self->extruders} > 1) { @@ -671,7 +651,7 @@ sub make_skirt { # but loops must be aligned so can't vary width/spacing # TODO: use each extruder's own flow my $first_layer_height = $self->objects->[0]->config->get_value('first_layer_height'); - my $flow = Slic3r::Flow->new( + my $flow = Slic3r::Flow->new_from_width( width => ($self->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width), role => FLOW_ROLE_PERIMETER, nozzle_diameter => $self->config->nozzle_diameter->[0], @@ -679,6 +659,7 @@ sub make_skirt { bridge_flow_ratio => 0, ); my $spacing = $flow->spacing; + my $mm3_per_mm = $flow->mm3_per_mm($first_layer_height); my @extruders_e_per_mm = (); my $extruder_idx = 0; @@ -692,13 +673,16 @@ sub make_skirt { $self->skirt->append(Slic3r::ExtrusionLoop->new( polygon => Slic3r::Polygon->new(@$loop), role => EXTR_ROLE_SKIRT, - flow_spacing => $spacing, + mm3_per_mm => $mm3_per_mm, )); if ($self->config->min_skirt_length > 0) { - $extruded_length[$extruder_idx] ||= 0; - $extruders_e_per_mm[$extruder_idx] ||= $self->extruders->[$extruder_idx]->e_per_mm($spacing, $first_layer_height); - $extruded_length[$extruder_idx] += unscale $loop->length * $extruders_e_per_mm[$extruder_idx]; + $extruded_length[$extruder_idx] ||= 0; + if (!$extruders_e_per_mm[$extruder_idx]) { + my $extruder = Slic3r::Extruder->new_from_config($self->config, $extruder_idx); + $extruders_e_per_mm[$extruder_idx] = $extruder->e_per_mm($mm3_per_mm); + } + $extruded_length[$extruder_idx] += unscale $loop->length * $extruders_e_per_mm[$extruder_idx]; $i++ if defined first { ($extruded_length[$_] // 0) < $self->config->min_skirt_length } 0 .. $#{$self->extruders}; if ($extruded_length[$extruder_idx] >= $self->config->min_skirt_length) { if ($extruder_idx < $#{$self->extruders}) { @@ -719,13 +703,15 @@ sub make_brim { $self->brim->clear; # method must be idempotent # brim is only printed on first layer and uses support material extruder - my $flow = Slic3r::Flow->new( + my $first_layer_height = $self->objects->[0]->config->get_abs_value('first_layer_height'); + my $flow = Slic3r::Flow->new_from_width( width => ($self->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width), role => FLOW_ROLE_PERIMETER, nozzle_diameter => $self->config->nozzle_diameter->[ $self->objects->[0]->config->support_material_extruder-1 ], - layer_height => $self->objects->[0]->config->get_abs_value('first_layer_height'), + layer_height => $first_layer_height, bridge_flow_ratio => 0, ); + my $mm3_per_mm = $flow->mm3_per_mm($first_layer_height); my $grow_distance = $flow->scaled_width / 2; my @islands = (); # array of polygons @@ -768,7 +754,7 @@ sub make_brim { $self->brim->append(map Slic3r::ExtrusionLoop->new( polygon => Slic3r::Polygon->new(@$_), role => EXTR_ROLE_SKIRT, - flow_spacing => $flow->spacing, + mm3_per_mm => $mm3_per_mm, ), reverse @{union_pt_chained(\@loops)}); } @@ -816,9 +802,11 @@ sub write_gcode { my $gcodegen = Slic3r::GCode->new( print_config => $self->config, extra_variables => $self->extra_variables, - extruders => $self->extruders, # we should only pass the *used* extruders (but maintain the Tx indices right!) layer_count => $self->layer_count, ); + $gcodegen->set_extruders($self->extruders); + $gcodegen->set_extruder($self->extruders->[0]); + print $fh "G21 ; set units to millimeters\n" if $self->config->gcode_flavor ne 'makerware'; print $fh $gcodegen->set_fan(0, 1) if $self->config->cooling && $self->config->disable_fan_first_layers; @@ -833,7 +821,7 @@ sub write_gcode { return if $self->config->start_gcode =~ /M(?:109|104)/i; for my $t (0 .. $#{$self->extruders}) { - my $temp = $self->extruders->[$t]->first_layer_temperature; + my $temp = $self->config->first_layer_temperature->[$t] // $self->config->first_layer_temperature->[0]; $temp += $self->config->standby_temperature_delta if $self->config->ooze_prevention; printf $fh $gcodegen->set_temperature($temp, $wait, $t) if $temp > 0; } diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index fd07d73e5..2d9e9b53c 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -863,7 +863,7 @@ sub generate_support_material { return unless ($self->config->support_material || $self->config->raft_layers > 0) && $self->layer_count >= 2; - my $first_layer_flow = Slic3r::Flow->new( + my $first_layer_flow = Slic3r::Flow->new_from_width( width => ($self->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width), role => FLOW_ROLE_SUPPORT_MATERIAL, nozzle_diameter => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ], @@ -900,7 +900,7 @@ sub support_material_flow { # we use a bogus layer_height because we use the same flow for all # support material layers - return Slic3r::Flow->new( + return Slic3r::Flow->new_from_width( width => $self->config->support_material_extrusion_width, role => $role, nozzle_diameter => $self->print->config->nozzle_diameter->[$extruder-1], diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm index c587f515f..472968478 100644 --- a/lib/Slic3r/Print/Region.pm +++ b/lib/Slic3r/Print/Region.pm @@ -48,7 +48,7 @@ sub flow { } my $nozzle_diameter = $self->print->config->nozzle_diameter->[$extruder-1]; - return Slic3r::Flow->new( + return Slic3r::Flow->new_from_width( width => $config_width, role => $role, nozzle_diameter => $nozzle_diameter, diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index b6d793ee1..a88728e67 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -500,10 +500,11 @@ sub generate_toolpaths { ); # transform loops into ExtrusionPath objects + my $mm3_per_mm = $interface_flow->mm3_per_mm($layer->height); @loops = map Slic3r::ExtrusionPath->new( - polyline => $_, - role => EXTR_ROLE_SUPPORTMATERIAL, - flow_spacing => $interface_flow->spacing, + polyline => $_, + role => EXTR_ROLE_SUPPORTMATERIAL, + mm3_per_mm => $mm3_per_mm, ), @loops; $layer->support_interface_fills->append(@loops); @@ -536,16 +537,16 @@ sub generate_toolpaths { foreach my $expolygon (@{union_ex($interface)}) { my ($params, @p) = $fillers{interface}->fill_surface( Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL), - density => $interface_density, - flow_spacing => $interface_flow->spacing, - complete => 1, + density => $interface_density, + flow => $interface_flow, + complete => 1, ); + my $mm3_per_mm = $params->{flow}->mm3_per_mm($layer->height); push @paths, map Slic3r::ExtrusionPath->new( - polyline => Slic3r::Polyline->new(@$_), - role => EXTR_ROLE_SUPPORTMATERIAL, - height => undef, - flow_spacing => $params->{flow_spacing}, + polyline => Slic3r::Polyline->new(@$_), + role => EXTR_ROLE_SUPPORTMATERIAL, + mm3_per_mm => $mm3_per_mm, ), @p; } @@ -556,8 +557,8 @@ sub generate_toolpaths { if (@$base) { my $filler = $fillers{support}; $filler->angle($angles[ ($layer_id) % @angles ]); - my $density = $support_density; - my $flow_spacing = $flow->spacing; + my $density = $support_density; + my $base_flow = $flow; # TODO: use offset2_ex() my $to_infill = union_ex($base, 1); @@ -568,15 +569,15 @@ sub generate_toolpaths { $filler = $fillers{interface}; $filler->angle($self->object_config->support_material_angle + 90); $density = 0.5; - $flow_spacing = $self->first_layer_flow->spacing; + $base_flow = $self->first_layer_flow; } else { # draw a perimeter all around support infill # TODO: use brim ordering algorithm + my $mm3_per_mm = $flow->mm3_per_mm($layer->height); push @paths, map Slic3r::ExtrusionPath->new( - polyline => $_->split_at_first_point, - role => EXTR_ROLE_SUPPORTMATERIAL, - height => undef, - flow_spacing => $flow->spacing, + polyline => $_->split_at_first_point, + role => EXTR_ROLE_SUPPORTMATERIAL, + mm3_per_mm => $mm3_per_mm, ), map @$_, @$to_infill; # TODO: use offset2_ex() @@ -586,16 +587,16 @@ sub generate_toolpaths { foreach my $expolygon (@$to_infill) { my ($params, @p) = $filler->fill_surface( Slic3r::Surface->new(expolygon => $expolygon, surface_type => S_TYPE_INTERNAL), - density => $density, - flow_spacing => $flow_spacing, - complete => 1, + density => $density, + flow => $base_flow, + complete => 1, ); + my $mm3_per_mm = $params->{flow}->mm3_per_mm($layer->height); push @paths, map Slic3r::ExtrusionPath->new( - polyline => Slic3r::Polyline->new(@$_), - role => EXTR_ROLE_SUPPORTMATERIAL, - height => undef, - flow_spacing => $params->{flow_spacing}, + polyline => Slic3r::Polyline->new(@$_), + role => EXTR_ROLE_SUPPORTMATERIAL, + mm3_per_mm => $mm3_per_mm, ), @p; } diff --git a/t/arcs.t b/t/arcs.t index 92f83964b..2eac2acf6 100644 --- a/t/arcs.t +++ b/t/arcs.t @@ -21,7 +21,7 @@ use Slic3r::Geometry qw(scaled_epsilon scale X Y); [306517.1,219034.23], [286979.42,248012.49], [258001.16,267550.17], [222515.14,274714.47], [187029.11,267550.17], [158050.85,248012.49], [138513.17,219034.23], [131348.87,183548.2], [86948.77,175149.09], [119825.35,100585], - ), role => EXTR_ROLE_FILL, flow_spacing => 0.5); + ), role => EXTR_ROLE_FILL, mm3_per_mm => 0.5); my @paths = $path->detect_arcs(30); @@ -42,12 +42,12 @@ use Slic3r::Geometry qw(scaled_epsilon scale X Y); my $path1 = Slic3r::ExtrusionPath->new( polyline => Slic3r::Polyline->new(@points), role => EXTR_ROLE_FILL, - flow_spacing => 0.5, + mm3_per_mm => 0.5, ); my $path2 = Slic3r::ExtrusionPath->new( polyline => Slic3r::Polyline->new(reverse @points), role => EXTR_ROLE_FILL, - flow_spacing => 0.5, + mm3_per_mm => 0.5, ); my @paths1 = $path1->detect_arcs(10, scale 1); diff --git a/t/fill.t b/t/fill.t index 2be4b87c1..424b206df 100644 --- a/t/fill.t +++ b/t/fill.t @@ -134,7 +134,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } { my $collection = Slic3r::ExtrusionPath::Collection->new( - map Slic3r::ExtrusionPath->new(polyline => $_, role => 0), + map Slic3r::ExtrusionPath->new(polyline => $_, role => 0, mm3_per_mm => 1), Slic3r::Polyline->new([0,15], [0,18], [0,20]), Slic3r::Polyline->new([0,10], [0,8], [0,5]), ); @@ -146,7 +146,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } { my $collection = Slic3r::ExtrusionPath::Collection->new( - map Slic3r::ExtrusionPath->new(polyline => $_, role => 0), + map Slic3r::ExtrusionPath->new(polyline => $_, role => 0, mm3_per_mm => 1), Slic3r::Polyline->new([15,0], [10,0], [4,0]), Slic3r::Polyline->new([10,5], [15,5], [20,5]), ); diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index 9f761ba9a..9e0e678d8 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -97,8 +97,7 @@ sub new { return $class->_new( $args{polygon}, # required $args{role}, # required - $args{height} // -1, - $args{flow_spacing} // -1, + $args{mm3_per_mm} // -1, ); } @@ -108,8 +107,7 @@ sub clone { return (ref $self)->_new( $args{polygon} // $self->polygon, $args{role} // $self->role, - $args{height} // $self->height, - $args{flow_spacing} // $self->flow_spacing, + $args{mm3_per_mm} // $self->mm3_per_mm, ); } @@ -129,8 +127,7 @@ sub new { return $class->_new( $args{polyline}, # required $args{role}, # required - $args{height} // -1, - $args{flow_spacing} // -1, + $args{mm3_per_mm} // -1, ); } @@ -140,8 +137,7 @@ sub clone { return (ref $self)->_new( $args{polyline} // $self->polyline, $args{role} // $self->role, - $args{height} // $self->height, - $args{flow_spacing} // $self->flow_spacing, + $args{mm3_per_mm} // $self->mm3_per_mm, ); } diff --git a/xs/src/ExtrusionEntity.cpp b/xs/src/ExtrusionEntity.cpp index 9c5cd5d31..c6641a716 100644 --- a/xs/src/ExtrusionEntity.cpp +++ b/xs/src/ExtrusionEntity.cpp @@ -116,8 +116,7 @@ ExtrusionLoop::split_at_index(int index) const ExtrusionPath* path = new ExtrusionPath(); path->polyline = *poly; path->role = this->role; - path->height = this->height; - path->flow_spacing = this->flow_spacing; + path->mm3_per_mm = this->mm3_per_mm; delete poly; return path; diff --git a/xs/src/ExtrusionEntity.hpp b/xs/src/ExtrusionEntity.hpp index c763ad77e..381fd1fd1 100644 --- a/xs/src/ExtrusionEntity.hpp +++ b/xs/src/ExtrusionEntity.hpp @@ -31,8 +31,7 @@ class ExtrusionEntity virtual ExtrusionEntity* clone() const = 0; virtual ~ExtrusionEntity() {}; ExtrusionRole role; - double height; // vertical thickness of the extrusion expressed in mm - double flow_spacing; + double mm3_per_mm; // mm^3 of plastic per mm of linear head motion virtual void reverse() = 0; virtual Point* first_point() const = 0; virtual Point* last_point() const = 0; diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index 05380ab16..c5adff7c1 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -241,6 +241,7 @@ class PrintConfigDef Options["fill_pattern"].tooltip = "Fill pattern for general low-density infill."; Options["fill_pattern"].cli = "fill-pattern=s"; Options["fill_pattern"].scope = "object"; + Options["fill_pattern"].enum_keys_map = ConfigOptionEnum::get_enum_values(); Options["fill_pattern"].enum_values.push_back("rectilinear"); Options["fill_pattern"].enum_values.push_back("line"); Options["fill_pattern"].enum_values.push_back("concentric"); @@ -320,6 +321,7 @@ class PrintConfigDef Options["gcode_flavor"].label = "G-code flavor"; Options["gcode_flavor"].tooltip = "Some G/M-code commands, including temperature control and others, are not universal. Set this option to your printer's firmware to get a compatible output. The \"No extrusion\" flavor prevents Slic3r from exporting any extrusion value at all."; Options["gcode_flavor"].cli = "gcode-flavor=s"; + Options["gcode_flavor"].enum_keys_map = ConfigOptionEnum::get_enum_values(); Options["gcode_flavor"].enum_values.push_back("reprap"); Options["gcode_flavor"].enum_values.push_back("teacup"); Options["gcode_flavor"].enum_values.push_back("makerware"); @@ -613,6 +615,7 @@ class PrintConfigDef Options["solid_fill_pattern"].tooltip = "Fill pattern for top/bottom infill."; Options["solid_fill_pattern"].cli = "solid-fill-pattern=s"; Options["solid_fill_pattern"].scope = "object"; + Options["solid_fill_pattern"].enum_keys_map = ConfigOptionEnum::get_enum_values(); Options["solid_fill_pattern"].enum_values.push_back("rectilinear"); Options["solid_fill_pattern"].enum_values.push_back("concentric"); Options["solid_fill_pattern"].enum_values.push_back("hilbertcurve"); diff --git a/xs/t/07_extrusionpath.t b/xs/t/07_extrusionpath.t index 78e084ccd..1c2ed3f2d 100644 --- a/xs/t/07_extrusionpath.t +++ b/xs/t/07_extrusionpath.t @@ -15,6 +15,7 @@ my $points = [ my $path = Slic3r::ExtrusionPath->new( polyline => Slic3r::Polyline->new(@$points), role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, + mm3_per_mm => 1, ); isa_ok $path->polyline, 'Slic3r::Polyline::Ref', 'path polyline'; is_deeply $path->polyline->pp, $points, 'path points roundtrip'; diff --git a/xs/t/08_extrusionloop.t b/xs/t/08_extrusionloop.t index bd729361f..54ef72ca7 100644 --- a/xs/t/08_extrusionloop.t +++ b/xs/t/08_extrusionloop.t @@ -16,6 +16,7 @@ my $square = [ my $loop = Slic3r::ExtrusionLoop->new( polygon => Slic3r::Polygon->new(@$square), role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, + mm3_per_mm => 1, ); isa_ok $loop->polygon, 'Slic3r::Polygon::Ref', 'loop polygon'; is_deeply $loop->polygon->pp, $square, 'polygon points roundtrip'; diff --git a/xs/t/12_extrusionpathcollection.t b/xs/t/12_extrusionpathcollection.t index e28c04b6d..4d5b9c589 100644 --- a/xs/t/12_extrusionpathcollection.t +++ b/xs/t/12_extrusionpathcollection.t @@ -15,11 +15,13 @@ my $points = [ my $path = Slic3r::ExtrusionPath->new( polyline => Slic3r::Polyline->new(@$points), role => Slic3r::ExtrusionPath::EXTR_ROLE_EXTERNAL_PERIMETER, + mm3_per_mm => 1, ); my $loop = Slic3r::ExtrusionLoop->new( polygon => Slic3r::Polygon->new(@$points), role => Slic3r::ExtrusionPath::EXTR_ROLE_FILL, + mm3_per_mm => 1, ); my $collection = Slic3r::ExtrusionPath::Collection->new($path); @@ -49,7 +51,7 @@ is scalar(@{$collection->[1]}), 1, 'appended collection was duplicated'; { my $collection = Slic3r::ExtrusionPath::Collection->new( - map Slic3r::ExtrusionPath->new(polyline => $_, role => 0), + map Slic3r::ExtrusionPath->new(polyline => $_, role => 0, mm3_per_mm => 1), Slic3r::Polyline->new([0,15], [0,18], [0,20]), Slic3r::Polyline->new([0,10], [0,8], [0,5]), ); @@ -65,7 +67,7 @@ is scalar(@{$collection->[1]}), 1, 'appended collection was duplicated'; { my $collection = Slic3r::ExtrusionPath::Collection->new( - map Slic3r::ExtrusionPath->new(polyline => $_, role => 0), + map Slic3r::ExtrusionPath->new(polyline => $_, role => 0, mm3_per_mm => 1), Slic3r::Polyline->new([15,0], [10,0], [4,0]), Slic3r::Polyline->new([10,5], [15,5], [20,5]), ); diff --git a/xs/t/15_config.t b/xs/t/15_config.t index 4e8f6e68a..0d2080602 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 79; +use Test::More tests => 82; foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) { $config->set('layer_height', 0.3); @@ -49,6 +49,9 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) { $config->set_deserialize('gcode_flavor', 'mach3'); is $config->get('gcode_flavor'), 'mach3', 'deserialize enum'; + $config->set_deserialize('fill_pattern', 'line'); + is $config->get('fill_pattern'), 'line', 'deserialize enum'; + $config->set('extruder_offset', [[10,20],[30,45]]); is_deeply $config->get('extruder_offset'), [[10,20],[30,45]], 'set/get points'; is $config->serialize('extruder_offset'), '10x20,30x45', 'serialize points'; @@ -106,4 +109,14 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) { is $config2->get('perimeters'), Slic3r::Config::print_config_def()->{perimeters}{default}, 'apply_static and print_config_def'; } +{ + my $config = Slic3r::Config->new; + $config->set('fill_pattern', 'line'); + + my $config2 = Slic3r::Config->new; + $config2->set('fill_pattern', 'hilbertcurve'); + + is $config->get('fill_pattern'), 'line', 'no interferences between DynamicConfig objects'; +} + __END__ diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 18deda295..743b38ca1 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -19,7 +19,7 @@ double get_abs_value(t_config_option_key opt_key, double ratio_over); void apply(DynamicPrintConfig* other) %code{% THIS->apply(*other, true); %}; - void apply_static(PrintConfig* other) + void apply_static(FullPrintConfig* other) %code{% THIS->apply(*other, true); %}; std::vector get_keys() %code{% THIS->keys(&RETVAL); %}; diff --git a/xs/xsp/ExtrusionLoop.xsp b/xs/xsp/ExtrusionLoop.xsp index 9bef01d42..fbf4dfca3 100644 --- a/xs/xsp/ExtrusionLoop.xsp +++ b/xs/xsp/ExtrusionLoop.xsp @@ -28,18 +28,16 @@ %{ ExtrusionLoop* -_new(CLASS, polygon_sv, role, height, flow_spacing) +_new(CLASS, polygon_sv, role, mm3_per_mm) char* CLASS; SV* polygon_sv; ExtrusionRole role; - double height; - double flow_spacing; + double mm3_per_mm; CODE: RETVAL = new ExtrusionLoop (); RETVAL->polygon.from_SV_check(polygon_sv); RETVAL->role = role; - RETVAL->height = height; - RETVAL->flow_spacing = flow_spacing; + RETVAL->mm3_per_mm = mm3_per_mm; OUTPUT: RETVAL @@ -66,22 +64,12 @@ ExtrusionLoop::role(...) RETVAL double -ExtrusionLoop::height(...) +ExtrusionLoop::mm3_per_mm(...) CODE: if (items > 1) { - THIS->height = (double)SvNV(ST(1)); + THIS->mm3_per_mm = (double)SvNV(ST(1)); } - RETVAL = THIS->height; - OUTPUT: - RETVAL - -double -ExtrusionLoop::flow_spacing(...) - CODE: - if (items > 1) { - THIS->flow_spacing = (double)SvNV(ST(1)); - } - RETVAL = THIS->flow_spacing; + RETVAL = THIS->mm3_per_mm; OUTPUT: RETVAL diff --git a/xs/xsp/ExtrusionPath.xsp b/xs/xsp/ExtrusionPath.xsp index b4248214c..9d0abd70d 100644 --- a/xs/xsp/ExtrusionPath.xsp +++ b/xs/xsp/ExtrusionPath.xsp @@ -33,18 +33,16 @@ %{ ExtrusionPath* -_new(CLASS, polyline_sv, role, height, flow_spacing) +_new(CLASS, polyline_sv, role, mm3_per_mm) char* CLASS; SV* polyline_sv; ExtrusionRole role; - double height; - double flow_spacing; + double mm3_per_mm; CODE: RETVAL = new ExtrusionPath (); RETVAL->polyline.from_SV_check(polyline_sv); RETVAL->role = role; - RETVAL->height = height; - RETVAL->flow_spacing = flow_spacing; + RETVAL->mm3_per_mm = mm3_per_mm; OUTPUT: RETVAL @@ -71,22 +69,12 @@ ExtrusionPath::role(...) RETVAL double -ExtrusionPath::height(...) +ExtrusionPath::mm3_per_mm(...) CODE: if (items > 1) { - THIS->height = (double)SvNV(ST(1)); + THIS->mm3_per_mm = (double)SvNV(ST(1)); } - RETVAL = THIS->height; - OUTPUT: - RETVAL - -double -ExtrusionPath::flow_spacing(...) - CODE: - if (items > 1) { - THIS->flow_spacing = (double)SvNV(ST(1)); - } - RETVAL = THIS->flow_spacing; + RETVAL = THIS->mm3_per_mm; OUTPUT: RETVAL From a2cbb261cbef9b95b7e900fac3408fc3513fe52b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Fri, 3 Jan 2014 20:02:00 +0100 Subject: [PATCH 13/19] More work for refactoring Flow/Extruder --- lib/Slic3r.pm | 2 +- lib/Slic3r/Fill.pm | 4 +++- lib/Slic3r/Fill/Concentric.pm | 16 +++++++++++----- lib/Slic3r/Fill/PlanePath.pm | 5 +++-- lib/Slic3r/Fill/Rectilinear.pm | 2 +- lib/Slic3r/GCode.pm | 6 +++--- lib/Slic3r/Print.pm | 10 +++++++++- lib/Slic3r/Print/Simple.pm | 3 ++- slic3r.pl | 5 ++--- t/fill.t | 16 +++++++++++++--- 10 files changed, 48 insertions(+), 21 deletions(-) diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 710e33e19..f2bea526c 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -78,7 +78,7 @@ use constant SCALING_FACTOR => 0.000001; use constant RESOLUTION => 0.0125; use constant SCALED_RESOLUTION => RESOLUTION / SCALING_FACTOR; use constant SMALL_PERIMETER_LENGTH => (6.5 / SCALING_FACTOR) * 2 * PI; -use constant LOOP_CLIPPING_LENGTH_OVER_SPACING => 0.15; +use constant LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER => 0.15; use constant INFILL_OVERLAP_OVER_SPACING => 0.45; use constant EXTERNAL_INFILL_MARGIN => 3; diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 45508169f..c128d0eb3 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -168,7 +168,9 @@ sub make_fill { ); next unless @polylines; - my $mm3_per_mm = $params->{flow}->mm3_per_mm($surface->thickness); + my $h = $surface->thickness; + $h = $layerm->height if $h == -1; + my $mm3_per_mm = $params->{flow}->mm3_per_mm($h); # save into layer push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new; diff --git a/lib/Slic3r/Fill/Concentric.pm b/lib/Slic3r/Fill/Concentric.pm index 5649a8aa7..0bb1f5061 100644 --- a/lib/Slic3r/Fill/Concentric.pm +++ b/lib/Slic3r/Fill/Concentric.pm @@ -15,16 +15,22 @@ sub fill_surface { my $expolygon = $surface->expolygon; my $bounding_box = $expolygon->bounding_box; - my $min_spacing = scale $params{flow_spacing}; + my $flow = $params{flow}; + my $min_spacing = $flow->scaled_spacing; my $distance = $min_spacing / $params{density}; - my $flow_spacing = $params{flow_spacing}; + my $flow_spacing = $flow->spacing; if ($params{density} == 1 && !$params{dont_adjust}) { $distance = $self->adjust_solid_spacing( width => $bounding_box->size->[X], distance => $distance, ); - $flow_spacing = unscale $distance; + $flow = Slic3r::Flow->new_from_spacing( + spacing => unscale($distance), + nozzle_diameter => $flow->nozzle_diameter, + layer_height => $surface->thickness, + bridge => $flow->bridge, + ); } # compensate the overlap which is good for rectilinear but harmful for concentric @@ -48,11 +54,11 @@ sub fill_surface { } # clip the paths to avoid the extruder to get exactly on the first point of the loop - my $clip_length = scale $flow_spacing * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_SPACING; + my $clip_length = scale($flow->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER; $_->clip_end($clip_length) for @paths; # TODO: return ExtrusionLoop objects to get better chained paths - return { flow_spacing => $flow_spacing, no_sort => 1 }, @paths; + return { flow => $flow, no_sort => 1 }, @paths; } 1; diff --git a/lib/Slic3r/Fill/PlanePath.pm b/lib/Slic3r/Fill/PlanePath.pm index 0797fd10e..d393f9049 100644 --- a/lib/Slic3r/Fill/PlanePath.pm +++ b/lib/Slic3r/Fill/PlanePath.pm @@ -27,7 +27,8 @@ sub fill_surface { my $rotate_vector = $self->infill_direction($surface); $self->rotate_points($expolygon, $rotate_vector); - my $distance_between_lines = scale $params{flow_spacing} / $params{density} * $self->multiplier; + my $flow = $params{flow}; + my $distance_between_lines = $flow->scaled_spacing / $params{density} * $self->multiplier; my $bounding_box = $expolygon->bounding_box; (ref $self) =~ /::([^:]+)$/; @@ -54,7 +55,7 @@ sub fill_surface { # paths must be rotated back $self->rotate_points_back(\@paths, $rotate_vector); - return { flow_spacing => $params{flow_spacing} }, @paths; + return { flow => $flow }, @paths; } 1; diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index 39fa5b941..acf7c1090 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -17,7 +17,7 @@ sub fill_surface { my $rotate_vector = $self->infill_direction($surface); $self->rotate_points($expolygon, $rotate_vector); - my $flow = $params{flow}; + my $flow = $params{flow} or die "No flow supplied to fill_surface()"; my $min_spacing = $flow->scaled_spacing; my $line_spacing = $min_spacing / $params{density}; my $line_oscillation = $line_spacing - $min_spacing; diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index a8f7526ad..b1c05d95d 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -235,7 +235,7 @@ sub extrude_loop { # clip the path to avoid the extruder to get exactly on the first point of the loop; # if polyline was shorter than the clipping distance we'd get a null polyline, so # we discard it in that case - $extrusion_path->clip_end(scale $extrusion_path->flow_spacing * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_SPACING) + $extrusion_path->clip_end(scale($self->extruder->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER) if $self->enable_loop_clipping; return '' if !@{$extrusion_path->polyline}; @@ -251,7 +251,7 @@ sub extrude_loop { push @paths, map { $_->role(EXTR_ROLE_OVERHANG_PERIMETER); - $_->flow_spacing($self->region->flow(FLOW_ROLE_PERIMETER, undef, 1)->width); + $_->mm3_per_mm($self->region->flow(FLOW_ROLE_PERIMETER, undef, 1)->mm3_per_mm(undef)); $_ } map $_->clone, @@ -287,7 +287,7 @@ sub extrude_loop { # we make sure we don't exceed the segment length because we don't know # the rotation of the second segment so we might cross the object boundary my $first_segment = Slic3r::Line->new(@{$extrusion_path->polyline}[0,1]); - my $distance = min(scale $extrusion_path->flow_spacing, $first_segment->length); + my $distance = min(scale($self->extruder->nozzle_diameter), $first_segment->length); my $point = $first_segment->point_at($distance); $point->rotate($angle, $extrusion_path->first_point); diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index ebaba5ce1..f31e6665a 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -19,6 +19,8 @@ has 'extra_variables' => (is => 'rw', default => sub {{}}); has 'objects' => (is => 'rw', default => sub {[]}); has 'status_cb' => (is => 'rw'); has 'regions' => (is => 'rw', default => sub {[]}); +has 'total_used_filament' => (is => 'rw'); +has 'total_extruded_volume' => (is => 'rw'); has '_state' => (is => 'ro', default => sub { Slic3r::Print::State->new }); # ordered collection of extrusion paths to build skirt loops @@ -970,7 +972,13 @@ sub write_gcode { print $fh $gcodegen->set_fan(0); printf $fh "%s\n", $gcodegen->replace_variables($self->config->end_gcode); - foreach my $extruder (@{$self->extruders}) { + $self->total_used_filament(0); + $self->total_extruded_volume(0); + foreach my $extruder_id (@{$self->extruders}) { + my $extruder = $gcodegen->extruders->[$extruder_id]; + $self->total_used_filament($self->total_used_filament + $extruder->absolute_E); + $self->total_extruded_volume($self->total_extruded_volume + $extruder->extruded_volume); + printf $fh "; filament used = %.1fmm (%.1fcm3)\n", $extruder->absolute_E, $extruder->extruded_volume/1000; } diff --git a/lib/Slic3r/Print/Simple.pm b/lib/Slic3r/Print/Simple.pm index a6f756343..ded003063 100644 --- a/lib/Slic3r/Print/Simple.pm +++ b/lib/Slic3r/Print/Simple.pm @@ -6,7 +6,8 @@ use Slic3r::Geometry qw(X Y); has '_print' => ( is => 'ro', default => sub { Slic3r::Print->new }, - handles => [qw(apply_config extruders expanded_output_filepath)], + handles => [qw(apply_config extruders expanded_output_filepath + total_used_filament total_extruded_volume)], ); has 'duplicate' => ( diff --git a/slic3r.pl b/slic3r.pl index bb2f7280e..e27e7d323 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -163,9 +163,8 @@ if (@ARGV) { # slicing from command line printf "Done. Process took %d minutes and %.3f seconds\n", int($duration/60), ($duration - int($duration/60)*60); # % truncates to integer } - print map sprintf("Filament required: %.1fmm (%.1fcm3)\n", - $_->absolute_E, $_->extruded_volume/1000), - @{$sprint->extruders}; + printf "Filament required: %.1fmm (%.1fcm3)\n", + $sprint->total_used_filament, $sprint->total_extruded_volume/1000; } } } else { diff --git a/t/fill.t b/t/fill.t index 424b206df..8e112484a 100644 --- a/t/fill.t +++ b/t/fill.t @@ -43,9 +43,14 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } surface_type => S_TYPE_TOP, expolygon => $expolygon, ); + my $flow = Slic3r::Flow->new( + width => 0.69, + spacing => 0.69, + nozzle_diameter => 0.50, + ); foreach my $angle (0, 45) { $surface->expolygon->rotate(Slic3r::Geometry::deg2rad($angle), [0,0]); - my ($params, @paths) = $filler->fill_surface($surface, flow_spacing => 0.69, density => 0.4); + my ($params, @paths) = $filler->fill_surface($surface, flow => $flow, density => 0.4); is scalar @paths, 1, 'one continuous path'; } } @@ -62,14 +67,19 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } surface_type => S_TYPE_BOTTOM, expolygon => $expolygon, ); + my $flow = Slic3r::Flow->new( + width => $flow_spacing, + spacing => $flow_spacing, + nozzle_diameter => $flow_spacing, + ); my ($params, @paths) = $filler->fill_surface( $surface, - flow_spacing => $flow_spacing, + flow => $flow, density => $density // 1, ); # check whether any part was left uncovered - my @grown_paths = map @{Slic3r::Polyline->new(@$_)->grow(scale $params->{flow_spacing}/2)}, @paths; + my @grown_paths = map @{Slic3r::Polyline->new(@$_)->grow(scale $params->{flow}->spacing/2)}, @paths; my $uncovered = diff_ex([ @$expolygon ], [ @grown_paths ], 1); # ignore very small dots From 07b9b12475ddcc206252abb43cb457db06dd334b Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 5 Jan 2014 00:36:33 +0100 Subject: [PATCH 14/19] Make tests pass --- lib/Slic3r/Extruder.pm | 3 +-- lib/Slic3r/Flow.pm | 4 +-- lib/Slic3r/GCode.pm | 35 ++++++++++-------------- lib/Slic3r/GCode/Layer.pm | 16 ++++++----- lib/Slic3r/Print.pm | 16 ++++++----- lib/Slic3r/Print/Object.pm | 13 ++++----- lib/Slic3r/Print/Region.pm | 2 +- lib/Slic3r/Print/SupportMaterial.pm | 4 +-- t/combineinfill.t | 35 ++++++++++++++++++++++-- t/retraction.t | 18 ++++++------- t/support.t | 8 +++++- xs/src/Config.cpp | 32 ++++++++++++++++++---- xs/src/Config.hpp | 41 +++++++++++++++++++---------- xs/src/PrintConfig.hpp | 4 +-- xs/t/15_config.t | 5 +++- xs/xsp/Config.xsp | 5 ++++ 16 files changed, 159 insertions(+), 82 deletions(-) diff --git a/lib/Slic3r/Extruder.pm b/lib/Slic3r/Extruder.pm index 2a8371cef..514aec7c7 100644 --- a/lib/Slic3r/Extruder.pm +++ b/lib/Slic3r/Extruder.pm @@ -40,8 +40,7 @@ sub new_from_config { use_relative_e_distances => $config->use_relative_e_distances, ); foreach my $opt_key (@{&OPTIONS}) { - my $value = $config->get($opt_key); - $conf{$opt_key} = $value->[$extruder_id] // $value->[0]; + $conf{$opt_key} = $config->get_at($opt_key, $extruder_id); } return $class->new(%conf); } diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index 5b1502a73..f07be98f5 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -72,7 +72,7 @@ sub mm3_per_mm { my $s = $self->spacing; if ($self->bridge) { - return ($s**2) * PI/4; + return ($w**2) * PI/4; } elsif ($w >= ($self->nozzle_diameter + $h)) { # rectangle with semicircles at the ends return $w * $h + ($h**2) / 4 * (PI - 4); @@ -140,7 +140,7 @@ sub _spacing { if ($bridge_flow_ratio > 0) { return $width + BRIDGE_EXTRA_SPACING; } - + use XXX; ZZZ "here" if !defined $nozzle_diameter; my $min_flow_spacing; if ($width >= ($nozzle_diameter + $layer_height)) { # rectangle with semicircles at the ends diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index b1c05d95d..78324f3bd 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -12,7 +12,7 @@ has 'print_config' => (is => 'ro', default => sub { Slic3r::Config::Print- has 'extra_variables' => (is => 'rw', default => sub {{}}); has 'standby_points' => (is => 'rw'); has 'enable_loop_clipping' => (is => 'rw', default => sub {1}); -has 'enable_wipe' => (is => 'lazy'); # at least one extruder has wipe enabled +has 'enable_wipe' => (is => 'rw', default => sub {0}); # at least one extruder has wipe enabled has 'layer_count' => (is => 'ro', required => 1 ); has 'layer' => (is => 'rw'); has 'region' => (is => 'rw'); @@ -25,14 +25,13 @@ has 'z' => (is => 'rw'); has 'speed' => (is => 'rw'); has '_extrusion_axis' => (is => 'rw'); has '_retract_lift' => (is => 'rw'); -has 'extruders' => (is => 'ro', default => sub {[]}); - +has 'extruders' => (is => 'ro', default => sub {{}}); +has 'extruder' => (is => 'rw'); has 'speeds' => (is => 'lazy'); # mm/min has 'external_mp' => (is => 'rw'); has 'layer_mp' => (is => 'rw'); has 'new_object' => (is => 'rw', default => sub {0}); has 'straight_once' => (is => 'rw', default => sub {1}); -has 'extruder' => (is => 'rw'); has 'elapsed_time' => (is => 'rw', default => sub {0} ); # seconds has 'lifted' => (is => 'rw', default => sub {0} ); has 'last_pos' => (is => 'rw', default => sub { Slic3r::Point->new(0,0) } ); @@ -52,7 +51,8 @@ sub set_extruders { my ($self, $extruder_ids) = @_; foreach my $i (@$extruder_ids) { - $self->extruders->[$i] = Slic3r::Extruder->new_from_config($self->print_config, $i); + $self->extruders->{$i} = my $e = Slic3r::Extruder->new_from_config($self->print_config, $i); + $self->enable_wipe(1) if $e->wipe; } } @@ -82,12 +82,7 @@ my %role_speeds = ( sub multiple_extruders { my $self = shift; - return @{$self->extruders} > 1; -} - -sub _build_enable_wipe { - my $self = shift; - return (first { $_->wipe } @{$self->extruders}) ? 1 : 0; + return (keys %{$self->extruders}) > 1; } sub set_shift { @@ -248,14 +243,12 @@ sub extrude_loop { @{$extrusion_path->subtract_expolygons($self->_layer_overhangs)}; # get overhang paths by intersecting overhangs with the loop - push @paths, - map { - $_->role(EXTR_ROLE_OVERHANG_PERIMETER); - $_->mm3_per_mm($self->region->flow(FLOW_ROLE_PERIMETER, undef, 1)->mm3_per_mm(undef)); - $_ - } - map $_->clone, - @{$extrusion_path->intersect_expolygons($self->_layer_overhangs)}; + foreach my $path (@{$extrusion_path->intersect_expolygons($self->_layer_overhangs)}) { + $path = $path->clone; + $path->role(EXTR_ROLE_OVERHANG_PERIMETER); + $path->mm3_per_mm($self->region->flow(FLOW_ROLE_PERIMETER, undef, 1)->mm3_per_mm(undef)); + push @paths, $path; + } # reapply the nearest point search for starting point # (clone because the collection gets DESTROY'ed) @@ -644,7 +637,7 @@ sub set_extruder { # if we are running a single-extruder setup, just set the extruder and return nothing if (!$self->multiple_extruders) { - $self->extruder($self->extruders->[$extruder_id]); + $self->extruder($self->extruders->{$extruder_id}); return ""; } @@ -673,7 +666,7 @@ sub set_extruder { } # set the new extruder - $self->extruder($self->extruders->[$extruder_id]); + $self->extruder($self->extruders->{$extruder_id}); $gcode .= sprintf "%s%d%s\n", ($self->print_config->gcode_flavor eq 'makerware' ? 'M135 T' diff --git a/lib/Slic3r/GCode/Layer.pm b/lib/Slic3r/GCode/Layer.pm index 96029eaab..08de360ec 100644 --- a/lib/Slic3r/GCode/Layer.pm +++ b/lib/Slic3r/GCode/Layer.pm @@ -4,8 +4,8 @@ use Moo; use List::Util qw(first); use Slic3r::Geometry qw(X Y unscale); -has 'print' => (is => 'ro', required => 1, handles => [qw(extruders)]); -has 'gcodegen' => (is => 'ro', required => 1); +has 'print' => (is => 'ro', required => 1); +has 'gcodegen' => (is => 'ro', required => 1, handles => [qw(extruders)]); has 'shift' => (is => 'ro', default => sub { [0,0] }); has 'spiralvase' => (is => 'lazy'); @@ -57,9 +57,10 @@ sub process_layer { $self->gcodegen->enable_loop_clipping(!$spiralvase); if (!$self->second_layer_things_done && $layer->id == 1) { - for my $t (grep $self->extruders->[$_], 0 .. $#{$self->print->config->temperature}) { - $gcode .= $self->gcodegen->set_temperature($self->extruders->[$t]->temperature, 0, $t) - if $self->print->extruders->[$t]->temperature && $self->extruders->[$t]->temperature != $self->extruders->[$t]->first_layer_temperature; + for my $extruder_id (sort keys %{$self->extruders}) { + my $extruder = $self->extruders->{$extruder_id}; + $gcode .= $self->gcodegen->set_temperature($extruder->temperature, 0, $extruder->id) + if $extruder->temperature && $extruder->temperature != $extruder->first_layer_temperature; } $gcode .= $self->gcodegen->set_bed_temperature($self->print->config->bed_temperature) if $self->print->config->bed_temperature && $self->print->config->bed_temperature != $self->print->config->first_layer_bed_temperature; @@ -76,7 +77,8 @@ sub process_layer { if (((values %{$self->skirt_done}) < $self->print->config->skirt_height || $self->print->config->skirt_height == -1) && !$self->skirt_done->{$layer->print_z}) { $self->gcodegen->set_shift(@{$self->shift}); - $gcode .= $self->gcodegen->set_extruder($self->extruders->[0]); + my @extruder_ids = sort keys %{$self->extruders}; + $gcode .= $self->gcodegen->set_extruder($extruder_ids[0]); # skip skirt if we have a large brim if ($layer->id < $self->print->config->skirt_height || $self->print->config->skirt_height == -1) { # distribute skirt loops across all extruders @@ -85,7 +87,7 @@ sub process_layer { # when printing layers > 0 ignore 'min_skirt_length' and # just use the 'skirts' setting; also just use the current extruder last if ($layer->id > 0) && ($i >= $self->print->config->skirts); - $gcode .= $self->gcodegen->set_extruder(($i/@{$self->extruders}) % @{$self->extruders}) + $gcode .= $self->gcodegen->set_extruder(($i/@extruder_ids) % @extruder_ids) if $layer->id == 0; $gcode .= $self->gcodegen->extrude_loop($skirt_loops[$i], 'skirt'); } diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index f31e6665a..e24070354 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -709,7 +709,7 @@ sub make_brim { my $flow = Slic3r::Flow->new_from_width( width => ($self->config->first_layer_extrusion_width || $self->regions->[0]->config->perimeter_extrusion_width), role => FLOW_ROLE_PERIMETER, - nozzle_diameter => $self->config->nozzle_diameter->[ $self->objects->[0]->config->support_material_extruder-1 ], + nozzle_diameter => $self->config->get_at('nozzle_diameter', $self->objects->[0]->config->support_material_extruder-1), layer_height => $first_layer_height, bridge_flow_ratio => 0, ); @@ -807,7 +807,6 @@ sub write_gcode { layer_count => $self->layer_count, ); $gcodegen->set_extruders($self->extruders); - $gcodegen->set_extruder($self->extruders->[0]); print $fh "G21 ; set units to millimeters\n" if $self->config->gcode_flavor ne 'makerware'; print $fh $gcodegen->set_fan(0, 1) if $self->config->cooling && $self->config->disable_fan_first_layers; @@ -822,8 +821,8 @@ sub write_gcode { my ($wait) = @_; return if $self->config->start_gcode =~ /M(?:109|104)/i; - for my $t (0 .. $#{$self->extruders}) { - my $temp = $self->config->first_layer_temperature->[$t] // $self->config->first_layer_temperature->[0]; + for my $t (@{$self->extruders}) { + my $temp = $self->config->get_at('first_layer_temperature', $t); $temp += $self->config->standby_temperature_delta if $self->config->ooze_prevention; printf $fh $gcodegen->set_temperature($temp, $wait, $t) if $temp > 0; } @@ -873,9 +872,9 @@ sub write_gcode { if (@skirt_points) { my $outer_skirt = convex_hull(\@skirt_points); my @skirts = (); - foreach my $extruder (@{$self->extruders}) { + foreach my $extruder_id (@{$self->extruders}) { push @skirts, my $s = $outer_skirt->clone; - $s->translate(map scale($_), @{$extruder->extruder_offset}); + $s->translate(map scale($_), @{$self->config->get_at('extruder_offset', $extruder_id)}); } my $convex_hull = convex_hull([ map @$_, @skirts ]); $gcodegen->standby_points([ map $_->clone, map @$_, map $_->subdivide(scale 10), @{offset([$convex_hull], scale 3)} ]); @@ -888,6 +887,9 @@ sub write_gcode { gcodegen => $gcodegen, ); + # set initial extruder only after custom start G-code + print $fh $gcodegen->set_extruder($self->extruders->[0]); + # do all objects for each layer if ($self->config->complete_objects) { # print objects from the smallest to the tallest to avoid collisions @@ -975,7 +977,7 @@ sub write_gcode { $self->total_used_filament(0); $self->total_extruded_volume(0); foreach my $extruder_id (@{$self->extruders}) { - my $extruder = $gcodegen->extruders->[$extruder_id]; + my $extruder = $gcodegen->extruders->{$extruder_id}; $self->total_used_filament($self->total_used_filament + $extruder->absolute_E); $self->total_extruded_volume($self->total_extruded_volume + $extruder->extruded_volume); diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 2d9e9b53c..bf196c100 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -758,7 +758,7 @@ sub combine_infill { my $every = $region->config->infill_every_layers; # limit the number of combined layers to the maximum height allowed by this regions' nozzle - my $nozzle_diameter = $self->print->config->nozzle_diameter->[ $region->config->infill_extruder-1 ]; + my $nozzle_diameter = $self->print->config->get_at('nozzle_diameter', $region->config->infill_extruder-1); # define the combinations my @combine = (); # layer_id => thickness in layers @@ -810,12 +810,12 @@ sub combine_infill { # so let's remove those areas from all layers my @intersection_with_clearance = map @{$_->offset( - $layerms[-1]->solid_infill_flow->scaled_width / 2 - + $layerms[-1]->perimeter_flow->scaled_width / 2 + $layerms[-1]->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width / 2 + + $layerms[-1]->flow(FLOW_ROLE_PERIMETER)->scaled_width / 2 # Because fill areas for rectilinear and honeycomb are grown # later to overlap perimeters, we need to counteract that too. + (($type == S_TYPE_INTERNALSOLID || $region->config->fill_pattern =~ /(rectilinear|honeycomb)/) - ? $layerms[-1]->solid_infill_flow->scaled_width * &Slic3r::INFILL_OVERLAP_OVER_SPACING + ? $layerms[-1]->flow(FLOW_ROLE_SOLID_INFILL)->scaled_width * &Slic3r::INFILL_OVERLAP_OVER_SPACING : 0) )}, @$intersection; @@ -866,7 +866,8 @@ sub generate_support_material { my $first_layer_flow = Slic3r::Flow->new_from_width( width => ($self->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width), role => FLOW_ROLE_SUPPORT_MATERIAL, - nozzle_diameter => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ], + nozzle_diameter => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ] + // $self->print->config->nozzle_diameter->[0], layer_height => $self->config->get_abs_value('first_layer_height'), bridge_flow_ratio => 0, ); @@ -903,7 +904,7 @@ sub support_material_flow { return Slic3r::Flow->new_from_width( width => $self->config->support_material_extrusion_width, role => $role, - nozzle_diameter => $self->print->config->nozzle_diameter->[$extruder-1], + nozzle_diameter => $self->print->config->nozzle_diameter->[$extruder-1] // $self->print->config->nozzle_diameter->[0], layer_height => $self->config->layer_height, bridge_flow_ratio => 0, ); diff --git a/lib/Slic3r/Print/Region.pm b/lib/Slic3r/Print/Region.pm index 472968478..657aa34d3 100644 --- a/lib/Slic3r/Print/Region.pm +++ b/lib/Slic3r/Print/Region.pm @@ -46,7 +46,7 @@ sub flow { } else { die "Unknown role $role"; } - my $nozzle_diameter = $self->print->config->nozzle_diameter->[$extruder-1]; + my $nozzle_diameter = $self->print->config->get_at('nozzle_diameter', $extruder-1); return Slic3r::Flow->new_from_width( width => $config_width, diff --git a/lib/Slic3r/Print/SupportMaterial.pm b/lib/Slic3r/Print/SupportMaterial.pm index a88728e67..2c533d8d9 100644 --- a/lib/Slic3r/Print/SupportMaterial.pm +++ b/lib/Slic3r/Print/SupportMaterial.pm @@ -174,7 +174,7 @@ sub contact_area { # now apply the contact areas to the layer were they need to be made { # get the average nozzle diameter used on this layer - my @nozzle_diameters = map $self->print_config->nozzle_diameter->[$_], + my @nozzle_diameters = map $self->print_config->get_at('nozzle_diameter', $_), map { $_->config->perimeter_extruder-1, $_->config->infill_extruder-1 } @{$layer->regions}; my $nozzle_diameter = sum(@nozzle_diameters)/@nozzle_diameters; @@ -246,7 +246,7 @@ sub support_layers_z { # determine layer height for any non-contact layer # we use max() to prevent many ultra-thin layers to be inserted in case # layer_height > nozzle_diameter * 0.75 - my $nozzle_diameter = $self->print_config->nozzle_diameter->[$self->object_config->support_material_extruder-1]; + my $nozzle_diameter = $self->print_config->get_at('nozzle_diameter', $self->object_config->support_material_extruder-1); my $support_material_height = max($max_object_layer_height, $nozzle_diameter * 0.75); my @z = sort { $a <=> $b } @$contact_z, @$top_z, (map $_ + $nozzle_diameter, @$top_z); diff --git a/t/combineinfill.t b/t/combineinfill.t index bd80b0d30..62e22e6b2 100644 --- a/t/combineinfill.t +++ b/t/combineinfill.t @@ -11,10 +11,41 @@ use List::Util qw(first); use Slic3r; use Slic3r::Test; -plan skip_all => 'this test is currently disabled'; # needs to be adapted to the new API -plan tests => 3; +plan tests => 2; { + my $config = Slic3r::Config->new_from_defaults; + $config->set('layer_height', 0.2); + $config->set('first_layer_height', 0.2); + $config->set('nozzle_diameter', [0.5]); + $config->set('infill_every_layers', 2); + $config->set('infill_extruder', 2); + $config->set('top_solid_layers', 0); + $config->set('bottom_solid_layers', 0); + my $print = Slic3r::Test::init_print('20mm_cube', config => $config); + ok my $gcode = Slic3r::Test::gcode($print), "infill_every_layers does not crash"; + + my $tool = undef; + my %layer_infill = (); # layer_z => has_infill + Slic3r::GCode::Reader->new->parse($gcode, sub { + my ($self, $cmd, $args, $info) = @_; + + if ($cmd =~ /^T(\d+)/) { + $tool = $1; + } elsif ($cmd eq 'G1' && $info->{extruding} && $info->{dist_XY} > 0) { + $layer_infill{$self->Z} //= 0; + if ($tool == $config->infill_extruder-1) { + $layer_infill{$self->Z} = 1; + } + } + }); + my $layers_with_infill = grep $_, values %layer_infill; + $layers_with_infill--; # first layer is never combined + is $layers_with_infill, scalar(keys %layer_infill)/2, 'infill is only present in correct number of layers'; +} + +# the following needs to be adapted to the new API +if (0) { my $config = Slic3r::Config->new_from_defaults; $config->set('skirts', 0); $config->set('solid_layers', 0); diff --git a/t/retraction.t b/t/retraction.t index 636359149..3737f63cd 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -41,8 +41,8 @@ my $test = sub { if ($info->{dist_Z}) { # lift move or lift + change layer - if (_eq($info->{dist_Z}, $print->extruders->[$tool]->retract_lift) - || (_eq($info->{dist_Z}, $conf->layer_height + $print->extruders->[$tool]->retract_lift) && $print->extruders->[$tool]->retract_lift > 0)) { + if (_eq($info->{dist_Z}, $print->config->get_at('retract_lift', $tool)) + || (_eq($info->{dist_Z}, $conf->layer_height + $print->config->get_at('retract_lift', $tool)) && $print->config->get_at('retract_lift', $tool) > 0)) { fail 'only lifting while retracted' if !$retracted[$tool] && !($conf->g0 && $info->{retracting}); fail 'double lift' if $lifted; $lifted = 1; @@ -50,8 +50,8 @@ my $test = sub { if ($info->{dist_Z} < 0) { fail 'going down only after lifting' if !$lifted; fail 'going down by the same amount of the lift or by the amount needed to get to next layer' - if !_eq($info->{dist_Z}, -$print->extruders->[$tool]->retract_lift) - && !_eq($info->{dist_Z}, -$print->extruders->[$tool]->retract_lift + $conf->layer_height); + if !_eq($info->{dist_Z}, -$print->config->get_at('retract_lift', $tool)) + && !_eq($info->{dist_Z}, -$print->config->get_at('retract_lift', $tool) + $conf->layer_height); $lifted = 0; } fail 'move Z at travel speed' if ($args->{F} // $self->F) != $conf->travel_speed * 60; @@ -59,9 +59,9 @@ my $test = sub { if ($info->{retracting}) { $retracted[$tool] = 1; $retracted_length[$tool] += -$info->{dist_E}; - if (_eq($retracted_length[$tool], $print->extruders->[$tool]->retract_length)) { + if (_eq($retracted_length[$tool], $print->config->get_at('retract_length', $tool))) { # okay - } elsif (_eq($retracted_length[$tool], $print->extruders->[$tool]->retract_length_toolchange)) { + } elsif (_eq($retracted_length[$tool], $print->config->get_at('retract_length_toolchange', $tool))) { $wait_for_toolchange = 1; } else { fail 'retracted by the correct amount'; @@ -72,9 +72,9 @@ my $test = sub { if ($info->{extruding}) { fail 'only extruding while not lifted' if $lifted; if ($retracted[$tool]) { - my $expected_amount = $retracted_length[$tool] + $print->extruders->[$tool]->retract_restart_extra; + my $expected_amount = $retracted_length[$tool] + $print->config->get_at('retract_restart_extra', $tool); if ($changed_tool && $toolchange_count[$tool] > 1) { - $expected_amount = $print->extruders->[$tool]->retract_length_toolchange + $print->extruders->[$tool]->retract_restart_extra_toolchange; + $expected_amount = $print->config->get_at('retract_length_toolchange', $tool) + $print->config->get_at('retract_restart_extra_toolchange', $tool); $changed_tool = 0; } fail 'unretracted by the correct amount' @@ -83,7 +83,7 @@ my $test = sub { $retracted_length[$tool] = 0; } } - if ($info->{travel} && $info->{dist_XY} >= $print->extruders->[$tool]->retract_before_travel) { + if ($info->{travel} && $info->{dist_XY} >= $print->config->get_at('retract_before_travel', $tool)) { fail 'retracted before long travel move' if !$retracted[$tool]; } }); diff --git a/t/support.t b/t/support.t index bd82d0ce1..a1e7173d9 100644 --- a/t/support.t +++ b/t/support.t @@ -23,7 +23,13 @@ use Slic3r::Test; $print->init_extruders; my $flow = $print->objects->[0]->support_material_flow; my $support_z = Slic3r::Print::SupportMaterial - ->new(object_config => $print->objects->[0]->config, print_config => $print->config, flow => $flow) + ->new( + object_config => $print->objects->[0]->config, + print_config => $print->config, + flow => $flow, + interface_flow => $flow, + first_layer_flow => $flow, + ) ->support_layers_z(\@contact_z, \@top_z, $config->layer_height); is $support_z->[0], $config->first_layer_height, diff --git a/xs/src/Config.cpp b/xs/src/Config.cpp index 58c9343e2..32c6d6074 100644 --- a/xs/src/Config.cpp +++ b/xs/src/Config.cpp @@ -130,9 +130,9 @@ ConfigBase::get(t_config_option_key opt_key) { return optv->point.to_SV_pureperl(); } else if (ConfigOptionPoints* optv = dynamic_cast(opt)) { AV* av = newAV(); - av_fill(av, optv->points.size()-1); - for (Pointfs::iterator it = optv->points.begin(); it != optv->points.end(); ++it) - av_store(av, it - optv->points.begin(), it->to_SV_pureperl()); + av_fill(av, optv->values.size()-1); + for (Pointfs::iterator it = optv->values.begin(); it != optv->values.end(); ++it) + av_store(av, it - optv->values.begin(), it->to_SV_pureperl()); return newRV_noinc((SV*)av); } else if (ConfigOptionBool* optv = dynamic_cast(opt)) { return newSViv(optv->value ? 1 : 0); @@ -148,6 +148,28 @@ ConfigBase::get(t_config_option_key opt_key) { } } +SV* +ConfigBase::get_at(t_config_option_key opt_key, size_t i) { + ConfigOption* opt = this->option(opt_key); + if (opt == NULL) return &PL_sv_undef; + + if (ConfigOptionFloats* optv = dynamic_cast(opt)) { + return newSVnv(optv->get_at(i)); + } else if (ConfigOptionInts* optv = dynamic_cast(opt)) { + return newSViv(optv->get_at(i)); + } else if (ConfigOptionStrings* optv = dynamic_cast(opt)) { + // we don't serialize() because that would escape newlines + std::string val = optv->get_at(i); + return newSVpvn(val.c_str(), val.length()); + } else if (ConfigOptionPoints* optv = dynamic_cast(opt)) { + return optv->get_at(i).to_SV_pureperl(); + } else if (ConfigOptionBools* optv = dynamic_cast(opt)) { + return newSViv(optv->get_at(i) ? 1 : 0); + } else { + return &PL_sv_undef; + } +} + void ConfigBase::set(t_config_option_key opt_key, SV* value) { ConfigOption* opt = this->option(opt_key, true); @@ -193,14 +215,14 @@ ConfigBase::set(t_config_option_key opt_key, SV* value) { } else if (ConfigOptionPoint* optv = dynamic_cast(opt)) { optv->point.from_SV(value); } else if (ConfigOptionPoints* optv = dynamic_cast(opt)) { - optv->points.clear(); + optv->values.clear(); AV* av = (AV*)SvRV(value); const size_t len = av_len(av)+1; for (size_t i = 0; i < len; i++) { SV** elem = av_fetch(av, i, 0); Pointf point; point.from_SV(*elem); - optv->points.push_back(point); + optv->values.push_back(point); } } else if (ConfigOptionBool* optv = dynamic_cast(opt)) { optv->value = SvTRUE(value); diff --git a/xs/src/Config.hpp b/xs/src/Config.hpp index 9306a18b2..64229cb2c 100644 --- a/xs/src/Config.hpp +++ b/xs/src/Config.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include "Point.hpp" @@ -24,6 +25,22 @@ class ConfigOption { virtual void deserialize(std::string str) = 0; }; +template +class ConfigOptionVector +{ + public: + virtual ~ConfigOptionVector() {}; + std::vector values; + + T get_at(size_t i) { + try { + return this->values.at(i); + } catch (const std::out_of_range& oor) { + return this->values.front(); + } + }; +}; + class ConfigOptionFloat : public ConfigOption { public: @@ -43,10 +60,9 @@ class ConfigOptionFloat : public ConfigOption }; }; -class ConfigOptionFloats : public ConfigOption +class ConfigOptionFloats : public ConfigOption, public ConfigOptionVector { public: - std::vector values; std::string serialize() { std::ostringstream ss; @@ -86,10 +102,9 @@ class ConfigOptionInt : public ConfigOption }; }; -class ConfigOptionInts : public ConfigOption +class ConfigOptionInts : public ConfigOption, public ConfigOptionVector { public: - std::vector values; std::string serialize() { std::ostringstream ss; @@ -144,10 +159,9 @@ class ConfigOptionString : public ConfigOption }; // semicolon-separated strings -class ConfigOptionStrings : public ConfigOption +class ConfigOptionStrings : public ConfigOption, public ConfigOptionVector { public: - std::vector values; std::string serialize() { std::ostringstream ss; @@ -215,15 +229,14 @@ class ConfigOptionPoint : public ConfigOption }; }; -class ConfigOptionPoints : public ConfigOption +class ConfigOptionPoints : public ConfigOption, public ConfigOptionVector { public: - Pointfs points; std::string serialize() { std::ostringstream ss; - for (Pointfs::const_iterator it = this->points.begin(); it != this->points.end(); ++it) { - if (it - this->points.begin() != 0) ss << ","; + for (Pointfs::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { + if (it - this->values.begin() != 0) ss << ","; ss << it->x; ss << "x"; ss << it->y; @@ -232,13 +245,13 @@ class ConfigOptionPoints : public ConfigOption }; void deserialize(std::string str) { - this->points.clear(); + this->values.clear(); std::istringstream is(str); std::string point_str; while (std::getline(is, point_str, ',')) { Pointf point; sscanf(point_str.c_str(), "%lfx%lf", &point.x, &point.y); - this->points.push_back(point); + this->values.push_back(point); } }; }; @@ -260,10 +273,9 @@ class ConfigOptionBool : public ConfigOption }; }; -class ConfigOptionBools : public ConfigOption +class ConfigOptionBools : public ConfigOption, public ConfigOptionVector { public: - std::vector values; std::string serialize() { std::ostringstream ss; @@ -398,6 +410,7 @@ class ConfigBase #ifdef SLIC3RXS SV* as_hash(); SV* get(t_config_option_key opt_key); + SV* get_at(t_config_option_key opt_key, size_t i); void set(t_config_option_key opt_key, SV* value); #endif }; diff --git a/xs/src/PrintConfig.hpp b/xs/src/PrintConfig.hpp index c5adff7c1..cb44a007f 100644 --- a/xs/src/PrintConfig.hpp +++ b/xs/src/PrintConfig.hpp @@ -1131,8 +1131,8 @@ class PrintConfig : public virtual StaticConfig this->external_perimeters_first.value = false; this->extruder_clearance_height.value = 20; this->extruder_clearance_radius.value = 20; - this->extruder_offset.points.resize(1); - this->extruder_offset.points[0] = Pointf(0,0); + this->extruder_offset.values.resize(1); + this->extruder_offset.values[0] = Pointf(0,0); this->extrusion_axis.value = "E"; this->extrusion_multiplier.values.resize(1); this->extrusion_multiplier.values[0] = 1; diff --git a/xs/t/15_config.t b/xs/t/15_config.t index 0d2080602..c80f14408 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 82; +use Test::More tests => 88; foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) { $config->set('layer_height', 0.3); @@ -75,6 +75,9 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) { $config->set('wipe', [1,0]); is_deeply $config->get('wipe'), [1,0], 'set/get bools'; + is $config->get_at('wipe', 0), 1, 'get_at bools'; + is $config->get_at('wipe', 1), 0, 'get_at bools'; + is $config->get_at('wipe', 9), 1, 'get_at bools'; is $config->serialize('wipe'), '1,0', 'serialize bools'; $config->set_deserialize('wipe', '0,1,1'); is_deeply $config->get('wipe'), [0,1,1], 'deserialize bools'; diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 743b38ca1..79d9bd269 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -11,6 +11,7 @@ bool has(t_config_option_key opt_key); SV* as_hash(); SV* get(t_config_option_key opt_key); + SV* get_at(t_config_option_key opt_key, int i); void set(t_config_option_key opt_key, SV* value); void set_deserialize(t_config_option_key opt_key, std::string str); std::string serialize(t_config_option_key opt_key); @@ -31,6 +32,7 @@ bool has(t_config_option_key opt_key); SV* as_hash(); SV* get(t_config_option_key opt_key); + SV* get_at(t_config_option_key opt_key, int i); void set(t_config_option_key opt_key, SV* value); void set_deserialize(t_config_option_key opt_key, std::string str); std::string serialize(t_config_option_key opt_key); @@ -50,6 +52,7 @@ bool has(t_config_option_key opt_key); SV* as_hash(); SV* get(t_config_option_key opt_key); + SV* get_at(t_config_option_key opt_key, int i); void set(t_config_option_key opt_key, SV* value); void set_deserialize(t_config_option_key opt_key, std::string str); std::string serialize(t_config_option_key opt_key); @@ -70,6 +73,7 @@ bool has(t_config_option_key opt_key); SV* as_hash(); SV* get(t_config_option_key opt_key); + SV* get_at(t_config_option_key opt_key, int i); void set(t_config_option_key opt_key, SV* value); void set_deserialize(t_config_option_key opt_key, std::string str); std::string serialize(t_config_option_key opt_key); @@ -90,6 +94,7 @@ bool has(t_config_option_key opt_key); SV* as_hash(); SV* get(t_config_option_key opt_key); + SV* get_at(t_config_option_key opt_key, int i); void set(t_config_option_key opt_key, SV* value); void set_deserialize(t_config_option_key opt_key, std::string str); std::string serialize(t_config_option_key opt_key); From 4e76a9e4c9bd2fdc621b27013cf047e817365650 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 30 Dec 2013 19:00:32 +0100 Subject: [PATCH 15/19] Parse options from AMF metadata --- lib/Slic3r/Format/AMF/Parser.pm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/Slic3r/Format/AMF/Parser.pm b/lib/Slic3r/Format/AMF/Parser.pm index d392a8915..d67687f70 100644 --- a/lib/Slic3r/Format/AMF/Parser.pm +++ b/lib/Slic3r/Format/AMF/Parser.pm @@ -97,6 +97,12 @@ sub end_element { } elsif ($data->{LocalName} eq 'material') { $self->{_material} = undef; } elsif ($data->{LocalName} eq 'metadata' && $self->{_material}) { + if ($self->{_material_metadata_type} =~ /^slic3r\.(.+)/) { + my $opt_key = $1; + if (exists $Slic3r::Config::Options->{$opt_key}) { + $self->{_material}->set_deserialize($opt_key, $self->{_material}->attributes->{$opt_key}); + } + } $self->{_material_metadata_type} = undef; } elsif ($data->{LocalName} eq 'constellation') { $self->{_constellation} = undef; From 87342d324caa3e745325aff1508821b0dfd8abe8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 5 Jan 2014 01:25:39 +0100 Subject: [PATCH 16/19] Fix typo --- lib/Slic3r/Format/AMF/Parser.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Format/AMF/Parser.pm b/lib/Slic3r/Format/AMF/Parser.pm index d67687f70..c99d71800 100644 --- a/lib/Slic3r/Format/AMF/Parser.pm +++ b/lib/Slic3r/Format/AMF/Parser.pm @@ -100,7 +100,7 @@ sub end_element { if ($self->{_material_metadata_type} =~ /^slic3r\.(.+)/) { my $opt_key = $1; if (exists $Slic3r::Config::Options->{$opt_key}) { - $self->{_material}->set_deserialize($opt_key, $self->{_material}->attributes->{$opt_key}); + $self->{_material}->config->set_deserialize($opt_key, $self->{_material}->attributes->{$opt_key}); } } $self->{_material_metadata_type} = undef; From 036badf932291520ca7d3f7d9f8bad1c69a947aa Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 5 Jan 2014 13:16:13 +0100 Subject: [PATCH 17/19] Ported Flow to XS --- lib/Slic3r/Flow.pm | 167 ++------------------------------------------ lib/Slic3r/GCode.pm | 2 +- xs/MANIFEST | 4 ++ xs/lib/Slic3r/XS.pm | 28 ++++++++ xs/src/Config.cpp | 32 ++++----- xs/src/Config.hpp | 36 ++++++---- xs/src/Flow.cpp | 109 +++++++++++++++++++++++++++++ xs/src/Flow.hpp | 48 +++++++++++++ xs/src/Point.cpp | 4 +- xs/src/Point.hpp | 6 +- xs/src/myinit.h | 2 + xs/t/15_config.t | 5 +- xs/t/16_flow.t | 29 ++++++++ xs/xsp/Flow.xsp | 80 +++++++++++++++++++++ xs/xsp/my.map | 2 + xs/xsp/typemap.xspt | 7 ++ 16 files changed, 360 insertions(+), 201 deletions(-) create mode 100644 xs/src/Flow.cpp create mode 100644 xs/src/Flow.hpp create mode 100644 xs/t/16_flow.t create mode 100644 xs/xsp/Flow.xsp diff --git a/lib/Slic3r/Flow.pm b/lib/Slic3r/Flow.pm index f07be98f5..a7c8312c5 100644 --- a/lib/Slic3r/Flow.pm +++ b/lib/Slic3r/Flow.pm @@ -1,165 +1,12 @@ package Slic3r::Flow; -use Moo; +use strict; +use warnings; -require Exporter; -our @ISA = qw(Exporter); -our @EXPORT_OK = qw(FLOW_ROLE_PERIMETER FLOW_ROLE_INFILL FLOW_ROLE_SOLID_INFILL FLOW_ROLE_TOP_SOLID_INFILL - FLOW_ROLE_SUPPORT_MATERIAL FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE); +use parent qw(Exporter); + +our @EXPORT_OK = qw(FLOW_ROLE_PERIMETER FLOW_ROLE_INFILL FLOW_ROLE_SOLID_INFILL + FLOW_ROLE_TOP_SOLID_INFILL FLOW_ROLE_SUPPORT_MATERIAL + FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE); our %EXPORT_TAGS = (roles => \@EXPORT_OK); -use Slic3r::Geometry qw(PI); - -has 'width' => (is => 'ro', required => 1); -has 'spacing' => (is => 'ro', required => 1); -has 'nozzle_diameter' => (is => 'ro', required => 1); -has 'bridge' => (is => 'ro', default => sub {0}); -has 'scaled_width' => (is => 'lazy'); -has 'scaled_spacing' => (is => 'lazy'); - -use constant FLOW_ROLE_PERIMETER => 1; -use constant FLOW_ROLE_INFILL => 2; -use constant FLOW_ROLE_SOLID_INFILL => 3; -use constant FLOW_ROLE_TOP_SOLID_INFILL => 4; -use constant FLOW_ROLE_SUPPORT_MATERIAL => 5; -use constant FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE => 6; - -use constant BRIDGE_EXTRA_SPACING => 0.05; -use constant OVERLAP_FACTOR => 1; - -sub new_from_width { - my ($class, %args) = @_; - - if ($args{width} eq '0') { - $args{width} = _width(@args{qw(role nozzle_diameter layer_height bridge_flow_ratio)}); - } elsif ($args{width} =~ /^(\d+(?:\.\d+)?)%$/) { - $args{width} = $args{layer_height} * $1 / 100; - } - - return $class->new( - width => $args{width}, - spacing => _spacing(@args{qw(width nozzle_diameter layer_height bridge_flow_ratio)}), - nozzle_diameter => $args{nozzle_diameter}, - bridge => ($args{bridge_flow_ratio} > 0) ? 1 : 0, - ); -} - -sub new_from_spacing { - my ($class, %args) = @_; - - return $class->new( - width => _width_from_spacing(@args{qw(spacing nozzle_diameter layer_height bridge)}), - spacing => $args{spacing}, - nozzle_diameter => $args{nozzle_diameter}, - bridge => $args{bridge}, - ); -} - -sub clone { - my $self = shift; - - return (ref $self)->new( - width => $self->width, - spacing => $self->spacing, - nozzle_diameter => $self->nozzle_diameter, - bridge => $self->bridge, - ); -} - -sub mm3_per_mm { - my ($self, $h) = @_; - - my $w = $self->width; - my $s = $self->spacing; - - if ($self->bridge) { - return ($w**2) * PI/4; - } elsif ($w >= ($self->nozzle_diameter + $h)) { - # rectangle with semicircles at the ends - return $w * $h + ($h**2) / 4 * (PI - 4); - } else { - # rectangle with shrunk semicircles at the ends - return $self->nozzle_diameter * $h * (1 - PI/4) + $h * $w * PI/4; - } -} - -sub _width { - my ($role, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_; - - if ($bridge_flow_ratio > 0) { - return sqrt($bridge_flow_ratio * ($nozzle_diameter**2)); - } - - # here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate - my $volume = ($nozzle_diameter**2) * PI/4; - my $shape_threshold = $nozzle_diameter * $layer_height + ($layer_height**2) * PI/4; - my $width; - if ($volume >= $shape_threshold) { - # rectangle with semicircles at the ends - $width = (($nozzle_diameter**2) * PI + ($layer_height**2) * (4 - PI)) / (4 * $layer_height); - } else { - # rectangle with squished semicircles at the ends - $width = $nozzle_diameter * ($nozzle_diameter/$layer_height - 4/PI + 1); - } - - my $min = $nozzle_diameter * 1.05; - my $max; - if ($role == FLOW_ROLE_PERIMETER || $role == FLOW_ROLE_SUPPORT_MATERIAL) { - $min = $max = $nozzle_diameter; - } elsif ($role != FLOW_ROLE_INFILL) { - # do not limit width for sparse infill so that we use full native flow for it - $max = $nozzle_diameter * 1.7; - } - $width = $max if defined($max) && $width > $max; - $width = $min if $width < $min; - - return $width; -} - -sub _width_from_spacing { - my ($s, $nozzle_diameter, $h, $bridge) = @_; - - if ($bridge) { - return $s - BRIDGE_EXTRA_SPACING; - } - - my $w_threshold = $h + $nozzle_diameter; - my $s_threshold = $w_threshold - OVERLAP_FACTOR * ($w_threshold - ($w_threshold - $h * (1 - PI/4))); - - if ($s >= $s_threshold) { - # rectangle with semicircles at the ends - return $s + OVERLAP_FACTOR * $h * (1 - PI/4); - } else { - # rectangle with shrunk semicircles at the ends - return ($s + $nozzle_diameter * OVERLAP_FACTOR * (PI/4 - 1)) / (1 + OVERLAP_FACTOR * (PI/4 - 1)); - } -} - -sub _spacing { - my ($width, $nozzle_diameter, $layer_height, $bridge_flow_ratio) = @_; - - if ($bridge_flow_ratio > 0) { - return $width + BRIDGE_EXTRA_SPACING; - } - use XXX; ZZZ "here" if !defined $nozzle_diameter; - my $min_flow_spacing; - if ($width >= ($nozzle_diameter + $layer_height)) { - # rectangle with semicircles at the ends - $min_flow_spacing = $width - $layer_height * (1 - PI/4); - } else { - # rectangle with shrunk semicircles at the ends - $min_flow_spacing = $nozzle_diameter * (1 - PI/4) + $width * PI/4; - } - return $width - OVERLAP_FACTOR * ($width - $min_flow_spacing); -} - -sub _build_scaled_width { - my $self = shift; - return Slic3r::Geometry::scale($self->width); -} - -sub _build_scaled_spacing { - my $self = shift; - return Slic3r::Geometry::scale($self->spacing); -} - 1; diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 78324f3bd..1d1952309 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -246,7 +246,7 @@ sub extrude_loop { foreach my $path (@{$extrusion_path->intersect_expolygons($self->_layer_overhangs)}) { $path = $path->clone; $path->role(EXTR_ROLE_OVERHANG_PERIMETER); - $path->mm3_per_mm($self->region->flow(FLOW_ROLE_PERIMETER, undef, 1)->mm3_per_mm(undef)); + $path->mm3_per_mm($self->region->flow(FLOW_ROLE_PERIMETER, -1, 1)->mm3_per_mm(-1)); push @paths, $path; } diff --git a/xs/MANIFEST b/xs/MANIFEST index ef37e40cb..fc52e2497 100644 --- a/xs/MANIFEST +++ b/xs/MANIFEST @@ -22,6 +22,8 @@ src/ExtrusionEntity.cpp src/ExtrusionEntity.hpp src/ExtrusionEntityCollection.cpp src/ExtrusionEntityCollection.hpp +src/Flow.cpp +src/Flow.hpp src/Geometry.cpp src/Geometry.hpp src/Line.cpp @@ -65,6 +67,7 @@ t/12_extrusionpathcollection.t t/13_polylinecollection.t t/14_geometry.t t/15_config.t +t/16_flow.t xsp/Clipper.xsp xsp/Config.xsp xsp/ExPolygon.xsp @@ -72,6 +75,7 @@ xsp/ExPolygonCollection.xsp xsp/ExtrusionEntityCollection.xsp xsp/ExtrusionLoop.xsp xsp/ExtrusionPath.xsp +xsp/Flow.xsp xsp/Geometry.xsp xsp/Line.xsp xsp/my.map diff --git a/xs/lib/Slic3r/XS.pm b/xs/lib/Slic3r/XS.pm index 9e0e678d8..489aeb0e8 100644 --- a/xs/lib/Slic3r/XS.pm +++ b/xs/lib/Slic3r/XS.pm @@ -146,6 +146,34 @@ our @ISA = 'Slic3r::ExtrusionPath'; sub DESTROY {} +package Slic3r::Flow; + +sub new { + my ($class, %args) = @_; + + my $self = $class->_new( + @args{qw(width spacing nozzle_diameter)}, + ); + $self->set_bridge($args{bridge} // 0); + return $self; +} + +sub new_from_width { + my ($class, %args) = @_; + + return $class->_new_from_width( + @args{qw(role width nozzle_diameter layer_height bridge_flow_ratio)}, + ); +} + +sub new_from_spacing { + my ($class, %args) = @_; + + return $class->_new_from_spacing( + @args{qw(spacing nozzle_diameter layer_height bridge)}, + ); +} + package Slic3r::Surface; sub new { diff --git a/xs/src/Config.cpp b/xs/src/Config.cpp index 32c6d6074..20be9a3c2 100644 --- a/xs/src/Config.cpp +++ b/xs/src/Config.cpp @@ -50,22 +50,18 @@ ConfigBase::set_deserialize(const t_config_option_key opt_key, std::string str) double ConfigBase::get_abs_value(const t_config_option_key opt_key) { - // get option definition - assert(this->def->count(opt_key) != 0); - ConfigOptionDef* def = &(*this->def)[opt_key]; - assert(def->type == coFloatOrPercent); - - // get stored option value - ConfigOptionFloatOrPercent* opt = dynamic_cast(this->option(opt_key)); - assert(opt != NULL); - - // compute absolute value - if (opt->percent) { - ConfigOptionFloat* optbase = dynamic_cast(this->option(def->ratio_over)); - if (optbase == NULL) throw "ratio_over option not found"; - return optbase->value * opt->value / 100; + ConfigOption* opt = this->option(opt_key, false); + if (ConfigOptionFloatOrPercent* optv = dynamic_cast(opt)) { + // get option definition + assert(this->def->count(opt_key) != 0); + ConfigOptionDef* def = &(*this->def)[opt_key]; + + // compute absolute value over the absolute value of the base option + return optv->get_abs_value(this->get_abs_value(def->ratio_over)); + } else if (ConfigOptionFloat* optv = dynamic_cast(opt)) { + return optv->value; } else { - return opt->value; + throw "Not a valid option type for get_abs_value()"; } } @@ -76,11 +72,7 @@ ConfigBase::get_abs_value(const t_config_option_key opt_key, double ratio_over) assert(opt != NULL); // compute absolute value - if (opt->percent) { - return ratio_over * opt->value / 100; - } else { - return opt->value; - } + return opt->get_abs_value(ratio_over); } #ifdef SLIC3RXS diff --git a/xs/src/Config.hpp b/xs/src/Config.hpp index 64229cb2c..71f945349 100644 --- a/xs/src/Config.hpp +++ b/xs/src/Config.hpp @@ -21,7 +21,7 @@ typedef std::vector t_config_option_keys; class ConfigOption { public: virtual ~ConfigOption() {}; - virtual std::string serialize() = 0; + virtual std::string serialize() const = 0; virtual void deserialize(std::string str) = 0; }; @@ -49,7 +49,7 @@ class ConfigOptionFloat : public ConfigOption operator double() const { return this->value; }; - std::string serialize() { + std::string serialize() const { std::ostringstream ss; ss << this->value; return ss.str(); @@ -64,7 +64,7 @@ class ConfigOptionFloats : public ConfigOption, public ConfigOptionVector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { if (it - this->values.begin() != 0) ss << ","; @@ -91,7 +91,7 @@ class ConfigOptionInt : public ConfigOption operator int() const { return this->value; }; - std::string serialize() { + std::string serialize() const { std::ostringstream ss; ss << this->value; return ss.str(); @@ -106,7 +106,7 @@ class ConfigOptionInts : public ConfigOption, public ConfigOptionVector { public: - std::string serialize() { + std::string serialize() const { std::ostringstream ss; for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { if (it - this->values.begin() != 0) ss << ","; @@ -133,7 +133,7 @@ class ConfigOptionString : public ConfigOption operator std::string() const { return this->value; }; - std::string serialize() { + std::string serialize() const { std::string str = this->value; // s/\R/\\n/g @@ -163,7 +163,7 @@ class ConfigOptionStrings : public ConfigOption, public ConfigOptionVector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { if (it - this->values.begin() != 0) ss << ";"; @@ -189,7 +189,15 @@ class ConfigOptionFloatOrPercent : public ConfigOption bool percent; ConfigOptionFloatOrPercent() : value(0), percent(false) {}; - std::string serialize() { + double get_abs_value(double ratio_over) const { + if (this->percent) { + return ratio_over * this->value / 100; + } else { + return this->value; + } + }; + + std::string serialize() const { std::ostringstream ss; ss << this->value; std::string s(ss.str()); @@ -216,7 +224,7 @@ class ConfigOptionPoint : public ConfigOption operator Pointf() const { return this->point; }; - std::string serialize() { + std::string serialize() const { std::ostringstream ss; ss << this->point.x; ss << ","; @@ -233,7 +241,7 @@ class ConfigOptionPoints : public ConfigOption, public ConfigOptionVectorvalues.begin(); it != this->values.end(); ++it) { if (it - this->values.begin() != 0) ss << ","; @@ -264,7 +272,7 @@ class ConfigOptionBool : public ConfigOption operator bool() const { return this->value; }; - std::string serialize() { + std::string serialize() const { return std::string(this->value ? "1" : "0"); }; @@ -277,7 +285,7 @@ class ConfigOptionBools : public ConfigOption, public ConfigOptionVector { public: - std::string serialize() { + std::string serialize() const { std::ostringstream ss; for (std::vector::const_iterator it = this->values.begin(); it != this->values.end(); ++it) { if (it - this->values.begin() != 0) ss << ","; @@ -306,7 +314,7 @@ class ConfigOptionEnum : public ConfigOption operator T() const { return this->value; }; - std::string serialize() { + std::string serialize() const { t_config_enum_values enum_keys_map = ConfigOptionEnum::get_enum_values(); for (t_config_enum_values::iterator it = enum_keys_map.begin(); it != enum_keys_map.end(); ++it) { if (it->second == static_cast(this->value)) return it->first; @@ -333,7 +341,7 @@ class ConfigOptionEnumGeneric : public ConfigOption operator int() const { return this->value; }; - std::string serialize() { + std::string serialize() const { for (t_config_enum_values::iterator it = this->keys_map->begin(); it != this->keys_map->end(); ++it) { if (it->second == this->value) return it->first; } diff --git a/xs/src/Flow.cpp b/xs/src/Flow.cpp new file mode 100644 index 000000000..e5e7dc2be --- /dev/null +++ b/xs/src/Flow.cpp @@ -0,0 +1,109 @@ +#include "Flow.hpp" +#include + +namespace Slic3r { + +Flow +Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio) { + float w; + if (!width.percent && width.value == 0) { + w = Flow::_width(role, nozzle_diameter, height, bridge_flow_ratio); + } else { + w = width.get_abs_value(height); + } + + Flow flow(w, Flow::_spacing(w, nozzle_diameter, height, bridge_flow_ratio), nozzle_diameter); + if (bridge_flow_ratio > 0) flow.bridge = true; + return flow; +} + +Flow +Flow::new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge) { + float w = Flow::_width_from_spacing(spacing, nozzle_diameter, height, bridge); + Flow flow(w, spacing, nozzle_diameter); + flow.bridge = bridge; + return flow; +} + +double +Flow::mm3_per_mm(float h) { + if (this->bridge) { + return (this->width * this->width) * PI/4.0; + } else if (this->width >= (this->nozzle_diameter + h)) { + // rectangle with semicircles at the ends + return this->width * h + (h*h) / 4.0 * (PI-4.0); + } else { + // rectangle with shrunk semicircles at the ends + return this->nozzle_diameter * h * (1 - PI/4.0) + h * this->width * PI/4.0; + } +} + +float +Flow::_width(FlowRole role, float nozzle_diameter, float height, float bridge_flow_ratio) { + if (bridge_flow_ratio > 0) { + return sqrt(bridge_flow_ratio * (nozzle_diameter*nozzle_diameter)); + } + + // here we calculate a sane default by matching the flow speed (at the nozzle) and the feed rate + float volume = (nozzle_diameter*nozzle_diameter) * PI/4.0; + float shape_threshold = nozzle_diameter * height + (height*height) * PI/4.0; + float width; + if (volume >= shape_threshold) { + // rectangle with semicircles at the ends + width = ((nozzle_diameter*nozzle_diameter) * PI + (height*height) * (4.0 - PI)) / (4.0 * height); + } else { + // rectangle with squished semicircles at the ends + width = nozzle_diameter * (nozzle_diameter/height - 4.0/PI + 1); + } + + float min = nozzle_diameter * 1.05; + float max = -1; + if (role == frPerimeter || role == frSupportMaterial) { + min = max = nozzle_diameter; + } else if (role != frInfill) { + // do not limit width for sparse infill so that we use full native flow for it + max = nozzle_diameter * 1.7; + } + if (max != -1 && width > max) width = max; + if (width < min) width = min; + + return width; +} + + +float +Flow::_width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge) { + if (bridge) { + return spacing - BRIDGE_EXTRA_SPACING; + } + + float w_threshold = height + nozzle_diameter; + float s_threshold = w_threshold - OVERLAP_FACTOR * (w_threshold - (w_threshold - height * (1 - PI/4.0))); + + if (spacing >= s_threshold) { + // rectangle with semicircles at the ends + return spacing + OVERLAP_FACTOR * height * (1 - PI/4.0); + } else { + // rectangle with shrunk semicircles at the ends + return (spacing + nozzle_diameter * OVERLAP_FACTOR * (PI/4.0 - 1)) / (1 + OVERLAP_FACTOR * (PI/4.0 - 1)); + } +} + +float +Flow::_spacing(float width, float nozzle_diameter, float height, float bridge_flow_ratio) { + if (bridge_flow_ratio > 0) { + return width + BRIDGE_EXTRA_SPACING; + } + + float min_flow_spacing; + if (width >= (nozzle_diameter + height)) { + // rectangle with semicircles at the ends + min_flow_spacing = width - height * (1 - PI/4.0); + } else { + // rectangle with shrunk semicircles at the ends + min_flow_spacing = nozzle_diameter * (1 - PI/4.0) + width * PI/4.0; + } + return width - OVERLAP_FACTOR * (width - min_flow_spacing); +} + +} diff --git a/xs/src/Flow.hpp b/xs/src/Flow.hpp new file mode 100644 index 000000000..e98173dc9 --- /dev/null +++ b/xs/src/Flow.hpp @@ -0,0 +1,48 @@ +#ifndef slic3r_Flow_hpp_ +#define slic3r_Flow_hpp_ + +#include +#include "Config.hpp" +#include "ExtrusionEntity.hpp" + +namespace Slic3r { + +#define BRIDGE_EXTRA_SPACING 0.05 +#define OVERLAP_FACTOR 1.0 + +enum FlowRole { + frPerimeter, + frInfill, + frSolidInfill, + frTopSolidInfill, + frSupportMaterial, + frSupportMaterialInterface, +}; + +class Flow +{ + public: + float width; + float spacing; + float nozzle_diameter; + bool bridge; + coord_t scaled_width; + coord_t scaled_spacing; + + Flow(float _w, float _s, float _nd): width(_w), spacing(_s), nozzle_diameter(_nd), bridge(false) { + this->scaled_width = scale_(this->width); + this->scaled_spacing = scale_(this->spacing); + }; + double mm3_per_mm(float h); + static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio); + static Flow new_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); + + private: + static float _width(FlowRole role, float nozzle_diameter, float height, float bridge_flow_ratio); + static float _width_from_spacing(float spacing, float nozzle_diameter, float height, bool bridge); + static float _spacing(float width, float nozzle_diameter, float height, float bridge_flow_ratio); +}; + +} + +#endif diff --git a/xs/src/Point.cpp b/xs/src/Point.cpp index fbb3b06c6..d35b3f0f8 100644 --- a/xs/src/Point.cpp +++ b/xs/src/Point.cpp @@ -23,8 +23,8 @@ Point::rotate(double angle, Point* center) { double cur_x = (double)this->x; double cur_y = (double)this->y; - this->x = (long)round( (double)center->x + cos(angle) * (cur_x - (double)center->x) - sin(angle) * (cur_y - (double)center->y) ); - this->y = (long)round( (double)center->y + cos(angle) * (cur_y - (double)center->y) + sin(angle) * (cur_x - (double)center->x) ); + this->x = (coord_t)round( (double)center->x + cos(angle) * (cur_x - (double)center->x) - sin(angle) * (cur_y - (double)center->y) ); + this->y = (coord_t)round( (double)center->y + cos(angle) * (cur_y - (double)center->y) + sin(angle) * (cur_x - (double)center->x) ); } bool diff --git a/xs/src/Point.hpp b/xs/src/Point.hpp index 507d082c9..385f99cdb 100644 --- a/xs/src/Point.hpp +++ b/xs/src/Point.hpp @@ -17,9 +17,9 @@ typedef std::vector Pointfs; class Point { public: - long x; - long y; - explicit Point(long _x = 0, long _y = 0): x(_x), y(_y) {}; + coord_t x; + coord_t y; + explicit Point(coord_t _x = 0, coord_t _y = 0): x(_x), y(_y) {}; void scale(double factor); void translate(double x, double y); void rotate(double angle, Point* center); diff --git a/xs/src/myinit.h b/xs/src/myinit.h index 1ef522b44..fcbaca5c0 100644 --- a/xs/src/myinit.h +++ b/xs/src/myinit.h @@ -20,8 +20,10 @@ extern "C" { #define EPSILON 1e-4 #define SCALING_FACTOR 0.000001 +#define PI 3.141592653589793238 #define scale_(val) (val / SCALING_FACTOR) #define unscale(val) (val * SCALING_FACTOR) +typedef long coord_t; namespace Slic3r {} using namespace Slic3r; diff --git a/xs/t/15_config.t b/xs/t/15_config.t index c80f14408..ed60c3e6d 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -4,7 +4,7 @@ use strict; use warnings; use Slic3r::XS; -use Test::More tests => 88; +use Test::More tests => 89; foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) { $config->set('layer_height', 0.3); @@ -110,6 +110,9 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Full->new) { my $config2 = Slic3r::Config->new; $config2->apply_static($config); is $config2->get('perimeters'), Slic3r::Config::print_config_def()->{perimeters}{default}, 'apply_static and print_config_def'; + + $config->set('top_solid_infill_speed', 70); + is $config->get_abs_value('top_solid_infill_speed'), 70, 'get_abs_value() works when ratio_over references a floatOrPercent option'; } { diff --git a/xs/t/16_flow.t b/xs/t/16_flow.t new file mode 100644 index 000000000..e19430200 --- /dev/null +++ b/xs/t/16_flow.t @@ -0,0 +1,29 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +use Slic3r::XS; +use Test::More tests => 2; + +{ + my $flow = Slic3r::Flow->new_from_width( + role => Slic3r::Flow::FLOW_ROLE_PERIMETER, + width => '1', + nozzle_diameter => 0.5, + layer_height => 0.3, + bridge_flow_ratio => 1, + ); + isa_ok $flow, 'Slic3r::Flow', 'new_from_width'; +} + +{ + my $flow = Slic3r::Flow->new( + width => 1, + spacing => 0.95, + nozzle_diameter => 0.5, + ); + isa_ok $flow, 'Slic3r::Flow', 'new'; +} + +__END__ diff --git a/xs/xsp/Flow.xsp b/xs/xsp/Flow.xsp new file mode 100644 index 000000000..4ff5427fc --- /dev/null +++ b/xs/xsp/Flow.xsp @@ -0,0 +1,80 @@ +%module{Slic3r::XS}; + +%{ +#include +#include "Flow.hpp" +%} + +%name{Slic3r::Flow} class Flow { + ~Flow(); + %name{_new} Flow(float width, float spacing, float nozzle_diameter); + void set_bridge(bool bridge) + %code{% THIS->bridge = bridge; %}; + Flow* clone() + %code{% const char* CLASS = "Slic3r::Flow"; RETVAL = new Flow(*THIS); %}; + + float width() + %code{% RETVAL = THIS->width; %}; + float spacing() + %code{% RETVAL = THIS->spacing; %}; + float nozzle_diameter() + %code{% RETVAL = THIS->nozzle_diameter; %}; + bool bridge() + %code{% RETVAL = THIS->bridge; %}; + long scaled_width() + %code{% RETVAL = THIS->scaled_width; %}; + long scaled_spacing() + %code{% RETVAL = THIS->scaled_spacing; %}; + + double mm3_per_mm(float height); +%{ + +Flow* +_new_from_width(CLASS, role, width, nozzle_diameter, height, bridge_flow_ratio) + char* CLASS; + FlowRole role; + std::string width; + float nozzle_diameter; + float height; + float bridge_flow_ratio; + CODE: + ConfigOptionFloatOrPercent optwidth; + optwidth.deserialize(width); + RETVAL = new Flow(Flow::new_from_config_width(role, optwidth, nozzle_diameter, height, bridge_flow_ratio)); + OUTPUT: + RETVAL + +Flow* +_new_from_spacing(CLASS, spacing, nozzle_diameter, height, bridge) + char* CLASS; + float spacing; + float nozzle_diameter; + float height; + bool bridge; + CODE: + RETVAL = new Flow(Flow::new_from_spacing(spacing, nozzle_diameter, height, bridge)); + OUTPUT: + RETVAL + +%} +}; + +%package{Slic3r::Flow}; +%{ + +IV +_constant() + ALIAS: + FLOW_ROLE_PERIMETER = frPerimeter + FLOW_ROLE_INFILL = frInfill + FLOW_ROLE_SOLID_INFILL = frSolidInfill + FLOW_ROLE_TOP_SOLID_INFILL = frTopSolidInfill + FLOW_ROLE_SUPPORT_MATERIAL = frSupportMaterial + FLOW_ROLE_SUPPORT_MATERIAL_INTERFACE = frSupportMaterialInterface + PROTOTYPE: + CODE: + RETVAL = ix; + OUTPUT: RETVAL + +%} + diff --git a/xs/xsp/my.map b/xs/xsp/my.map index 666cbf616..6a02d9432 100644 --- a/xs/xsp/my.map +++ b/xs/xsp/my.map @@ -18,11 +18,13 @@ ExPolygonCollection* O_OBJECT ExtrusionEntityCollection* O_OBJECT ExtrusionPath* O_OBJECT ExtrusionLoop* O_OBJECT +Flow* O_OBJECT PrintState* O_OBJECT Surface* O_OBJECT SurfaceCollection* O_OBJECT ExtrusionRole T_UV +FlowRole T_UV PrintStep T_UV SurfaceType T_UV ClipperLib::JoinType T_UV diff --git a/xs/xsp/typemap.xspt b/xs/xsp/typemap.xspt index 25c4c4d12..46a037526 100644 --- a/xs/xsp/typemap.xspt +++ b/xs/xsp/typemap.xspt @@ -15,6 +15,7 @@ %typemap{FullPrintConfig*}; %typemap{ExPolygon*}; %typemap{ExPolygonCollection*}; +%typemap{Flow*}; %typemap{Line*}; %typemap{Polyline*}; %typemap{Polygon*}; @@ -43,6 +44,12 @@ $CVar = (ExtrusionRole)SvUV($PerlVar); %}; }; +%typemap{FlowRole}{parsed}{ + %cpp_type{FlowRole}; + %precall_code{% + $CVar = (FlowRole)SvUV($PerlVar); + %}; +}; %typemap{PrintStep}{parsed}{ %cpp_type{PrintStep}; %precall_code{% From 2754ddf2155343f1a5b26bbc80c44a80175b4779 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 5 Jan 2014 14:04:32 +0100 Subject: [PATCH 18/19] Fix positioning of loded AMF plates --- lib/Slic3r/Format/AMF/Parser.pm | 2 +- lib/Slic3r/GUI/Plater.pm | 66 ++++++++++++++++++--------------- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/lib/Slic3r/Format/AMF/Parser.pm b/lib/Slic3r/Format/AMF/Parser.pm index c99d71800..43dbf05a9 100644 --- a/lib/Slic3r/Format/AMF/Parser.pm +++ b/lib/Slic3r/Format/AMF/Parser.pm @@ -100,7 +100,7 @@ sub end_element { if ($self->{_material_metadata_type} =~ /^slic3r\.(.+)/) { my $opt_key = $1; if (exists $Slic3r::Config::Options->{$opt_key}) { - $self->{_material}->config->set_deserialize($opt_key, $self->{_material}->attributes->{$opt_key}); + $self->{_material}->config->set_deserialize($opt_key, $self->{_material}->attributes->{"slic3r.$opt_key"}); } } $self->{_material_metadata_type} = undef; diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 1fb606c3c..1926b17f0 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -387,59 +387,65 @@ sub load_file { my $model = eval { Slic3r::Model->read_from_file($input_file) }; Slic3r::GUI::show_error($self, $@) if $@; - $self->load_model_object($_) for @{$model->objects}; + $self->load_model_objects(@{$model->objects}); $process_dialog->Destroy; $self->statusbar->SetStatusText("Loaded " . basename($input_file)); } -sub load_model_object { - my ($self, $model_object) = @_; - - my $o = $self->{model}->add_object($model_object); - - push @{ $self->{objects} }, Slic3r::GUI::Plater::Object->new( - name => basename($model_object->input_file), - ); +sub load_model_objects { + my ($self, @model_objects) = @_; my $need_arrange = 0; - if (!defined $model_object->instances) { - # if object has no defined position(s) we need to rearrange everything after loading - $need_arrange = 1; + my @obj_idx = (); + foreach my $model_object (@model_objects) { + my $o = $self->{model}->add_object($model_object); - # add a default instance and center object around origin - $o->center_around_origin; - $o->add_instance(offset => [ @{$self->{config}->print_center} ]); - } + push @{ $self->{objects} }, Slic3r::GUI::Plater::Object->new( + name => basename($model_object->input_file), + ); + push @obj_idx, $#{ $self->{objects} }; - $self->{print}->add_model_object($o); + if (!defined $model_object->instances) { + # if object has no defined position(s) we need to rearrange everything after loading + $need_arrange = 1; + + # add a default instance and center object around origin + $o->center_around_origin; + $o->add_instance(offset => [ @{$self->{config}->print_center} ]); + } + + $self->{print}->add_model_object($o); + } # if user turned autocentering off, automatic arranging would disappoint them if (!$Slic3r::GUI::Settings->{_}{autocenter}) { $need_arrange = 0; } - $self->object_loaded($#{ $self->{objects} }, no_arrange => !$need_arrange); + $self->objects_loaded(\@obj_idx, no_arrange => !$need_arrange); } -sub object_loaded { +sub objects_loaded { my $self = shift; - my ($obj_idx, %params) = @_; + my ($obj_idxs, %params) = @_; - my $object = $self->{objects}[$obj_idx]; - my $model_object = $self->{model}->objects->[$obj_idx]; - $self->{list}->InsertStringItem($obj_idx, $object->name); - $self->{list}->SetItemFont($obj_idx, Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL)) - if $self->{list}->can('SetItemFont'); # legacy code for wxPerl < 0.9918 not supporting SetItemFont() + foreach my $obj_idx (@$obj_idxs) { + my $object = $self->{objects}[$obj_idx]; + my $model_object = $self->{model}->objects->[$obj_idx]; + $self->{list}->InsertStringItem($obj_idx, $object->name); + $self->{list}->SetItemFont($obj_idx, Wx::Font->new(10, wxDEFAULT, wxNORMAL, wxNORMAL)) + if $self->{list}->can('SetItemFont'); # legacy code for wxPerl < 0.9918 not supporting SetItemFont() - $self->{list}->SetItem($obj_idx, 1, $model_object->instances_count); - $self->{list}->SetItem($obj_idx, 2, ($model_object->instances->[0]->scaling_factor * 100) . "%"); + $self->{list}->SetItem($obj_idx, 1, $model_object->instances_count); + $self->{list}->SetItem($obj_idx, 2, ($model_object->instances->[0]->scaling_factor * 100) . "%"); - $self->make_thumbnail($obj_idx); + $self->make_thumbnail($obj_idx); + } $self->arrange unless $params{no_arrange}; $self->update; $self->{list}->Update; - $self->{list}->Select($obj_idx, 1); + $self->{list}->Select($obj_idxs->[-1], 1); $self->object_list_changed; } @@ -657,7 +663,7 @@ sub split_object { } # we need to center this single object around origin $model_object->center_around_origin; - $self->load_model_object($model_object); + $self->load_model_objects($model_object); } } From c523ddea35e3ac38386c0daaa83397073ef5f6ae Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 5 Jan 2014 14:58:41 +0100 Subject: [PATCH 19/19] Fix option names not showing in plater object settings --- xs/xsp/Config.xsp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 79d9bd269..461f1cd89 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -147,7 +147,8 @@ print_config_def() } (void)hv_stores( hv, "type", newSVpv(opt_type, 0) ); (void)hv_stores( hv, "label", newSVpvn(optdef->label.c_str(), optdef->label.length()) ); - (void)hv_stores( hv, "full_label", newSVpvn(optdef->full_label.c_str(), optdef->full_label.length()) ); + if (!optdef->full_label.empty()) + (void)hv_stores( hv, "full_label", newSVpvn(optdef->full_label.c_str(), optdef->full_label.length()) ); (void)hv_stores( hv, "category", newSVpvn(optdef->category.c_str(), optdef->category.length()) ); (void)hv_stores( hv, "tooltip", newSVpvn(optdef->tooltip.c_str(), optdef->tooltip.length()) ); (void)hv_stores( hv, "sidetext", newSVpvn(optdef->sidetext.c_str(), optdef->sidetext.length()) );