mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-14 02:07:54 -06:00
Align infill across layers regardless of first-layer-specific extrusion width. Includes a good internal API refactoring and a fix to 3D honeycomb flow
This commit is contained in:
parent
93507bfd49
commit
64061267c8
8 changed files with 67 additions and 46 deletions
|
@ -195,28 +195,66 @@ sub make_fill {
|
||||||
next SURFACE unless $density > 0;
|
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 $h = $surface->thickness == -1 ? $layerm->height : $surface->thickness;
|
||||||
my $flow = $layerm->region->flow(
|
my $flow = $layerm->region->flow(
|
||||||
$role,
|
$role,
|
||||||
$h,
|
$h,
|
||||||
$is_bridge,
|
$is_bridge || $f->use_bridge_flow,
|
||||||
$layerm->id == 0,
|
$layerm->id == 0,
|
||||||
-1,
|
-1,
|
||||||
$layerm->object,
|
$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->layer_id($layerm->id);
|
||||||
$f->z($layerm->print_z);
|
$f->z($layerm->print_z);
|
||||||
$f->angle(deg2rad($layerm->config->fill_angle));
|
$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,
|
$surface,
|
||||||
density => $density/100,
|
density => $density/100,
|
||||||
flow => $flow,
|
|
||||||
layer_height => $h,
|
layer_height => $h,
|
||||||
);
|
);
|
||||||
next unless @polylines;
|
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;
|
my $mm3_per_mm = $flow->mm3_per_mm;
|
||||||
|
|
||||||
# save into layer
|
# save into layer
|
||||||
|
@ -225,17 +263,15 @@ sub make_fill {
|
||||||
: $is_solid ? (($surface->surface_type == S_TYPE_TOP) ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL)
|
: $is_solid ? (($surface->surface_type == S_TYPE_TOP) ? EXTR_ROLE_TOPSOLIDFILL : EXTR_ROLE_SOLIDFILL)
|
||||||
: EXTR_ROLE_FILL;
|
: EXTR_ROLE_FILL;
|
||||||
|
|
||||||
my $extrusion_height = $is_bridge ? $flow->width : $h;
|
|
||||||
|
|
||||||
push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new;
|
push @fills, my $collection = Slic3r::ExtrusionPath::Collection->new;
|
||||||
$collection->no_sort($params->{no_sort});
|
$collection->no_sort($f->no_sort);
|
||||||
$collection->append(
|
$collection->append(
|
||||||
map Slic3r::ExtrusionPath->new(
|
map Slic3r::ExtrusionPath->new(
|
||||||
polyline => $_,
|
polyline => $_,
|
||||||
role => $role,
|
role => $role,
|
||||||
mm3_per_mm => $mm3_per_mm,
|
mm3_per_mm => $mm3_per_mm,
|
||||||
width => $flow->width,
|
width => $flow->width,
|
||||||
height => $extrusion_height,
|
height => $flow->height,
|
||||||
), @polylines,
|
), @polylines,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,22 +7,17 @@ use POSIX qw(ceil fmod);
|
||||||
use Slic3r::Geometry qw(scale scaled_epsilon);
|
use Slic3r::Geometry qw(scale scaled_epsilon);
|
||||||
use Slic3r::Geometry::Clipper qw(intersection_pl);
|
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 {
|
sub fill_surface {
|
||||||
my ($self, $surface, %params) = @_;
|
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 $expolygon = $surface->expolygon;
|
||||||
my $bb = $expolygon->bounding_box;
|
my $bb = $expolygon->bounding_box;
|
||||||
my $size = $bb->size;
|
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
|
# 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
|
# TODO: return ExtrusionLoop objects to get better chained paths
|
||||||
return { flow => $flow}, @polylines;
|
return @polylines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@ use Moo;
|
||||||
has 'layer_id' => (is => 'rw');
|
has 'layer_id' => (is => 'rw');
|
||||||
has 'z' => (is => 'rw'); # in unscaled coordinates
|
has 'z' => (is => 'rw'); # in unscaled coordinates
|
||||||
has 'angle' => (is => 'rw'); # in radians, ccw, 0 = East
|
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
|
has 'bounding_box' => (is => 'ro', required => 0); # Slic3r::Geometry::BoundingBox object
|
||||||
|
|
||||||
sub adjust_solid_spacing {
|
sub adjust_solid_spacing {
|
||||||
|
@ -17,6 +19,9 @@ sub adjust_solid_spacing {
|
||||||
return $params{distance} + $extra_space / ($number_of_lines - 1);
|
return $params{distance} + $extra_space / ($number_of_lines - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub no_sort { 0 }
|
||||||
|
sub use_bridge_flow { 0 }
|
||||||
|
|
||||||
|
|
||||||
package Slic3r::Fill::WithDirection;
|
package Slic3r::Fill::WithDirection;
|
||||||
use Moo::Role;
|
use Moo::Role;
|
||||||
|
|
|
@ -15,22 +15,15 @@ sub fill_surface {
|
||||||
my $expolygon = $surface->expolygon;
|
my $expolygon = $surface->expolygon;
|
||||||
my $bounding_box = $expolygon->bounding_box;
|
my $bounding_box = $expolygon->bounding_box;
|
||||||
|
|
||||||
my $flow = $params{flow};
|
my $min_spacing = scale($self->spacing);
|
||||||
my $min_spacing = $flow->scaled_spacing;
|
|
||||||
my $distance = $min_spacing / $params{density};
|
my $distance = $min_spacing / $params{density};
|
||||||
|
|
||||||
my $flow_spacing = $flow->spacing;
|
|
||||||
if ($params{density} == 1 && !$params{dont_adjust}) {
|
if ($params{density} == 1 && !$params{dont_adjust}) {
|
||||||
$distance = $self->adjust_solid_spacing(
|
$distance = $self->adjust_solid_spacing(
|
||||||
width => $bounding_box->size->[X],
|
width => $bounding_box->size->[X],
|
||||||
distance => $distance,
|
distance => $distance,
|
||||||
);
|
);
|
||||||
$flow = Slic3r::Flow->new_from_spacing(
|
$self->spacing(unscale $distance);
|
||||||
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,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# compensate the overlap which is good for rectilinear but harmful for concentric
|
# 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
|
# 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($self->loop_clipping) for @paths;
|
||||||
$_->clip_end($clip_length) for @paths;
|
|
||||||
@paths = grep $_->is_valid, @paths; # remove empty paths (too short, thus eaten by clipping)
|
@paths = grep $_->is_valid, @paths; # remove empty paths (too short, thus eaten by clipping)
|
||||||
|
|
||||||
# TODO: return ExtrusionLoop objects to get better chained paths
|
# TODO: return ExtrusionLoop objects to get better chained paths
|
||||||
return { flow => $flow, no_sort => 1 }, @paths;
|
return @paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -18,11 +18,11 @@ sub fill_surface {
|
||||||
my $rotate_vector = $self->infill_direction($surface);
|
my $rotate_vector = $self->infill_direction($surface);
|
||||||
|
|
||||||
# cache hexagons math
|
# 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;
|
my $m;
|
||||||
if (!($m = $self->cache->{$cache_id})) {
|
if (!($m = $self->cache->{$cache_id})) {
|
||||||
$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->{distance} = $min_spacing / $params{density};
|
||||||
$m->{hex_side} = $m->{distance} / (sqrt(3)/2);
|
$m->{hex_side} = $m->{distance} / (sqrt(3)/2);
|
||||||
$m->{hex_width} = $m->{distance} * 2; # $m->{hex_width} == $m->{hex_side} * sqrt(3);
|
$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;
|
1;
|
||||||
|
|
|
@ -21,8 +21,7 @@ sub fill_surface {
|
||||||
my $rotate_vector = $self->infill_direction($surface);
|
my $rotate_vector = $self->infill_direction($surface);
|
||||||
$self->rotate_points($expolygon, $rotate_vector);
|
$self->rotate_points($expolygon, $rotate_vector);
|
||||||
|
|
||||||
my $flow = $params{flow};
|
my $distance_between_lines = scale($self->spacing) / $params{density} * $self->multiplier;
|
||||||
my $distance_between_lines = $flow->scaled_spacing / $params{density} * $self->multiplier;
|
|
||||||
|
|
||||||
# align infill across layers using the object's bounding box
|
# align infill across layers using the object's bounding box
|
||||||
my $bb_polygon = $self->bounding_box->polygon;
|
my $bb_polygon = $self->bounding_box->polygon;
|
||||||
|
@ -75,7 +74,7 @@ sub fill_surface {
|
||||||
$_->translate(@{$translate->negative}) for @paths;
|
$_->translate(@{$translate->negative}) for @paths;
|
||||||
$self->rotate_points_back(\@paths, $rotate_vector);
|
$self->rotate_points_back(\@paths, $rotate_vector);
|
||||||
|
|
||||||
return { flow => $flow }, @paths;
|
return @paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,7 @@ sub fill_surface {
|
||||||
my $rotate_vector = $self->infill_direction($surface);
|
my $rotate_vector = $self->infill_direction($surface);
|
||||||
$self->rotate_points($expolygon, $rotate_vector);
|
$self->rotate_points($expolygon, $rotate_vector);
|
||||||
|
|
||||||
my $flow = $params{flow} or die "No flow supplied to fill_surface()";
|
my $min_spacing = scale($self->spacing);
|
||||||
my $min_spacing = $flow->scaled_spacing;
|
|
||||||
my $line_spacing = $min_spacing / $params{density};
|
my $line_spacing = $min_spacing / $params{density};
|
||||||
my $line_oscillation = $line_spacing - $min_spacing;
|
my $line_oscillation = $line_spacing - $min_spacing;
|
||||||
my $is_line_pattern = $self->isa('Slic3r::Fill::Line');
|
my $is_line_pattern = $self->isa('Slic3r::Fill::Line');
|
||||||
|
@ -31,12 +30,7 @@ sub fill_surface {
|
||||||
width => $bounding_box->size->[X],
|
width => $bounding_box->size->[X],
|
||||||
distance => $line_spacing,
|
distance => $line_spacing,
|
||||||
);
|
);
|
||||||
$flow = Slic3r::Flow->new_from_spacing(
|
$self->spacing(unscale $line_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,
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
# extend bounding box so that our pattern will be aligned with other layers
|
# extend bounding box so that our pattern will be aligned with other layers
|
||||||
$bounding_box->merge_point(Slic3r::Point->new(
|
$bounding_box->merge_point(Slic3r::Point->new(
|
||||||
|
@ -105,7 +99,7 @@ sub fill_surface {
|
||||||
# paths must be rotated back
|
# paths must be rotated back
|
||||||
$self->rotate_points_back(\@polylines, $rotate_vector);
|
$self->rotate_points_back(\@polylines, $rotate_vector);
|
||||||
|
|
||||||
return { flow => $flow }, @polylines;
|
return @polylines;
|
||||||
}
|
}
|
||||||
|
|
||||||
1;
|
1;
|
||||||
|
|
|
@ -13,7 +13,7 @@ Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &wid
|
||||||
float w;
|
float w;
|
||||||
if (bridge_flow_ratio > 0) {
|
if (bridge_flow_ratio > 0) {
|
||||||
// if bridge flow was requested, calculate bridge width
|
// 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) {
|
} else if (!width.percent && width.value == 0) {
|
||||||
// if user left option to 0, calculate a sane default width
|
// if user left option to 0, calculate a sane default width
|
||||||
w = Flow::_auto_width(role, nozzle_diameter, height);
|
w = Flow::_auto_width(role, nozzle_diameter, height);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue