From 64061267c84c98e6508bdb4b7e7ad4ef288c864d Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci <aar@cpan.org> Date: Mon, 22 Dec 2014 16:47:35 +0100 Subject: [PATCH] Align infill across layers regardless of first-layer-specific extrusion width. Includes a good internal API refactoring and a fix to 3D honeycomb flow --- lib/Slic3r/Fill.pm | 52 ++++++++++++++++++++++++++++------ lib/Slic3r/Fill/3DHoneycomb.pm | 15 ++++------ lib/Slic3r/Fill/Base.pm | 5 ++++ lib/Slic3r/Fill/Concentric.pm | 16 +++-------- lib/Slic3r/Fill/Honeycomb.pm | 6 ++-- lib/Slic3r/Fill/PlanePath.pm | 5 ++-- lib/Slic3r/Fill/Rectilinear.pm | 12 ++------ xs/src/libslic3r/Flow.cpp | 2 +- 8 files changed, 67 insertions(+), 46 deletions(-) diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index 780717586..fb2dfe858 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -195,28 +195,66 @@ sub make_fill { next SURFACE unless $density > 0; } + # get filler object + my $f = $self->filler($filler); + + # calculate the actual flow we'll be using for this infill my $h = $surface->thickness == -1 ? $layerm->height : $surface->thickness; my $flow = $layerm->region->flow( $role, $h, - $is_bridge, + $is_bridge || $f->use_bridge_flow, $layerm->id == 0, -1, $layerm->object, ); - my $f = $self->filler($filler); + # calculate flow spacing for infill pattern generation + my $using_internal_flow = 0; + if (!$is_solid && !$is_bridge) { + # it's internal infill, so we can calculate a generic flow spacing + # for all layers, for avoiding the ugly effect of + # misaligned infill on first layer because of different extrusion width and + # layer height + my $internal_flow = $layerm->region->flow( + FLOW_ROLE_INFILL, + $layerm->object->config->layer_height, # TODO: handle infill_every_layers? + 0, # no bridge + 0, # no first layer + -1, # auto width + $layerm->object, + ); + $f->spacing($internal_flow->spacing); + $using_internal_flow = 1; + } else { + $f->spacing($flow->spacing); + } + $f->layer_id($layerm->id); $f->z($layerm->print_z); $f->angle(deg2rad($layerm->config->fill_angle)); - my ($params, @polylines) = $f->fill_surface( + $f->loop_clipping(scale($flow->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER); + my @polylines = $f->fill_surface( $surface, density => $density/100, - flow => $flow, layer_height => $h, ); next unless @polylines; + # calculate actual flow from spacing (which might have been adjusted by the infill + # pattern generator) + if ($using_internal_flow) { + # if we used the internal flow we're not doing a solid infill + # so we can safely ignore the slight variation that might have + # been applied to $f->flow_spacing + } else { + $flow = Slic3r::Flow->new_from_spacing( + spacing => $f->spacing, + nozzle_diameter => $flow->nozzle_diameter, + layer_height => $h, + bridge => $is_bridge || $f->use_bridge_flow, + ); + } my $mm3_per_mm = $flow->mm3_per_mm; # save into layer @@ -224,18 +262,16 @@ sub make_fill { my $role = $is_bridge ? EXTR_ROLE_BRIDGE : $is_solid ? (($surface->surface_type == S_TYPE_TOP) ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL) : EXTR_ROLE_FILL; - - my $extrusion_height = $is_bridge ? $flow->width : $h; push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new; - $collection->no_sort($params->{no_sort}); + $collection->no_sort($f->no_sort); $collection->append( map Slic3r::ExtrusionPath->new( polyline => $_, role => $role, mm3_per_mm => $mm3_per_mm, width => $flow->width, - height => $extrusion_height, + height => $flow->height, ), @polylines, ); } diff --git a/lib/Slic3r/Fill/3DHoneycomb.pm b/lib/Slic3r/Fill/3DHoneycomb.pm index bb37d0ea7..256437707 100644 --- a/lib/Slic3r/Fill/3DHoneycomb.pm +++ b/lib/Slic3r/Fill/3DHoneycomb.pm @@ -7,22 +7,17 @@ use POSIX qw(ceil fmod); use Slic3r::Geometry qw(scale scaled_epsilon); use Slic3r::Geometry::Clipper qw(intersection_pl); +# require bridge flow since most of this pattern hangs in air +sub use_bridge_flow { 1 } + sub fill_surface { my ($self, $surface, %params) = @_; - # use bridge flow since most of this pattern hangs in air - my $flow = Slic3r::Flow->new( - width => $params{flow}->width, - height => $params{flow}->height, - nozzle_diameter => $params{flow}->nozzle_diameter, - bridge => 1, - ); - my $expolygon = $surface->expolygon; my $bb = $expolygon->bounding_box; my $size = $bb->size; - my $distance = $flow->scaled_spacing / $params{density}; + my $distance = scale($self->spacing) / $params{density}; # align bounding box to a multiple of our honeycomb grid { @@ -71,7 +66,7 @@ sub fill_surface { } # TODO: return ExtrusionLoop objects to get better chained paths - return { flow => $flow}, @polylines; + return @polylines; } diff --git a/lib/Slic3r/Fill/Base.pm b/lib/Slic3r/Fill/Base.pm index 34b47243b..75c8e03e6 100644 --- a/lib/Slic3r/Fill/Base.pm +++ b/lib/Slic3r/Fill/Base.pm @@ -4,6 +4,8 @@ use Moo; has 'layer_id' => (is => 'rw'); has 'z' => (is => 'rw'); # in unscaled coordinates has 'angle' => (is => 'rw'); # in radians, ccw, 0 = East +has 'spacing' => (is => 'rw'); # in unscaled coordinates +has 'loop_clipping' => (is => 'rw', default => sub { 0 }); # in scaled coordinates has 'bounding_box' => (is => 'ro', required => 0); # Slic3r::Geometry::BoundingBox object sub adjust_solid_spacing { @@ -17,6 +19,9 @@ sub adjust_solid_spacing { return $params{distance} + $extra_space / ($number_of_lines - 1); } +sub no_sort { 0 } +sub use_bridge_flow { 0 } + package Slic3r::Fill::WithDirection; use Moo::Role; diff --git a/lib/Slic3r/Fill/Concentric.pm b/lib/Slic3r/Fill/Concentric.pm index f122bdbd5..17f3fa225 100644 --- a/lib/Slic3r/Fill/Concentric.pm +++ b/lib/Slic3r/Fill/Concentric.pm @@ -15,22 +15,15 @@ sub fill_surface { my $expolygon = $surface->expolygon; my $bounding_box = $expolygon->bounding_box; - my $flow = $params{flow}; - my $min_spacing = $flow->scaled_spacing; + my $min_spacing = scale($self->spacing); my $distance = $min_spacing / $params{density}; - 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 = Slic3r::Flow->new_from_spacing( - spacing => unscale($distance), - nozzle_diameter => $flow->nozzle_diameter, - layer_height => ($params{layer_height} or die "No layer_height supplied to fill_surface()"), - bridge => $flow->bridge, - ); + $self->spacing(unscale $distance); } # compensate the overlap which is good for rectilinear but harmful for concentric @@ -54,12 +47,11 @@ sub fill_surface { } # clip the paths to prevent the extruder from getting exactly on the first point of the loop - my $clip_length = scale($flow->nozzle_diameter) * &Slic3r::LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER; - $_->clip_end($clip_length) for @paths; + $_->clip_end($self->loop_clipping) for @paths; @paths = grep $_->is_valid, @paths; # remove empty paths (too short, thus eaten by clipping) # TODO: return ExtrusionLoop objects to get better chained paths - return { flow => $flow, no_sort => 1 }, @paths; + return @paths; } 1; diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm index 08c0b0710..0fb47db4d 100644 --- a/lib/Slic3r/Fill/Honeycomb.pm +++ b/lib/Slic3r/Fill/Honeycomb.pm @@ -18,11 +18,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}, $self->spacing; my $m; if (!($m = $self->cache->{$cache_id})) { $m = $self->cache->{$cache_id} = {}; - my $min_spacing = $params{flow}->scaled_spacing; + my $min_spacing = scale($self->spacing); $m->{distance} = $min_spacing / $params{density}; $m->{hex_side} = $m->{distance} / (sqrt(3)/2); $m->{hex_width} = $m->{distance} * 2; # $m->{hex_width} == $m->{hex_side} * sqrt(3); @@ -123,7 +123,7 @@ sub fill_surface { )}; } - return { flow => $params{flow} }, @paths; + return @paths; } 1; diff --git a/lib/Slic3r/Fill/PlanePath.pm b/lib/Slic3r/Fill/PlanePath.pm index 3f7e1118f..556835ec4 100644 --- a/lib/Slic3r/Fill/PlanePath.pm +++ b/lib/Slic3r/Fill/PlanePath.pm @@ -21,8 +21,7 @@ sub fill_surface { my $rotate_vector = $self->infill_direction($surface); $self->rotate_points($expolygon, $rotate_vector); - my $flow = $params{flow}; - my $distance_between_lines = $flow->scaled_spacing / $params{density} * $self->multiplier; + my $distance_between_lines = scale($self->spacing) / $params{density} * $self->multiplier; # align infill across layers using the object's bounding box my $bb_polygon = $self->bounding_box->polygon; @@ -75,7 +74,7 @@ sub fill_surface { $_->translate(@{$translate->negative}) for @paths; $self->rotate_points_back(\@paths, $rotate_vector); - return { flow => $flow }, @paths; + return @paths; } diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index 8e3eee3a8..ef459ab9d 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -18,8 +18,7 @@ sub fill_surface { my $rotate_vector = $self->infill_direction($surface); $self->rotate_points($expolygon, $rotate_vector); - my $flow = $params{flow} or die "No flow supplied to fill_surface()"; - my $min_spacing = $flow->scaled_spacing; + my $min_spacing = scale($self->spacing); my $line_spacing = $min_spacing / $params{density}; my $line_oscillation = $line_spacing - $min_spacing; my $is_line_pattern = $self->isa('Slic3r::Fill::Line'); @@ -31,12 +30,7 @@ sub fill_surface { width => $bounding_box->size->[X], distance => $line_spacing, ); - $flow = Slic3r::Flow->new_from_spacing( - spacing => unscale($line_spacing), - nozzle_diameter => $flow->nozzle_diameter, - layer_height => ($params{layer_height} or die "No layer_height supplied to fill_surface()"), - bridge => $flow->bridge, - ); + $self->spacing(unscale $line_spacing); } else { # extend bounding box so that our pattern will be aligned with other layers $bounding_box->merge_point(Slic3r::Point->new( @@ -105,7 +99,7 @@ sub fill_surface { # paths must be rotated back $self->rotate_points_back(\@polylines, $rotate_vector); - return { flow => $flow }, @polylines; + return @polylines; } 1; diff --git a/xs/src/libslic3r/Flow.cpp b/xs/src/libslic3r/Flow.cpp index 7f87f146e..b9af18184 100644 --- a/xs/src/libslic3r/Flow.cpp +++ b/xs/src/libslic3r/Flow.cpp @@ -13,7 +13,7 @@ Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &wid float w; if (bridge_flow_ratio > 0) { // if bridge flow was requested, calculate bridge width - w = Flow::_bridge_width(nozzle_diameter, bridge_flow_ratio); + height = w = Flow::_bridge_width(nozzle_diameter, bridge_flow_ratio); } else if (!width.percent && width.value == 0) { // if user left option to 0, calculate a sane default width w = Flow::_auto_width(role, nozzle_diameter, height);