From 1187f4846da7b8cc94de744a2e5af28011cc59c8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sun, 31 Mar 2013 19:40:25 +0200 Subject: [PATCH 01/22] Some Clipper optimizations --- lib/Slic3r/GUI/Plater.pm | 2 +- lib/Slic3r/Geometry/Clipper.pm | 34 +++++++++++++++++++++++----------- lib/Slic3r/Layer/Region.pm | 16 ++++------------ 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 331aecaae..be001cae7 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -5,9 +5,9 @@ use utf8; use File::Basename qw(basename dirname); use List::Util qw(max sum first); +use Math::Clipper qw(offset JT_ROUND); use Math::ConvexHull::MonotoneChain qw(convex_hull); use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 MIN MAX); -use Slic3r::Geometry::Clipper qw(offset JT_ROUND); use threads::shared qw(shared_clone); use Wx qw(:bitmap :brush :button :cursor :dialog :filedialog :font :keycode :icon :id :listctrl :misc :panel :pen :sizer :toolbar :window); use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL EVT_CHOICE); diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index 68e3d7c0e..bef2713da 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -14,12 +14,13 @@ our $clipper = Math::Clipper->new; sub safety_offset { my ($polygons, $factor) = @_; - return Math::Clipper::offset($polygons, $factor // (scale 1e-05), 100000, JT_MITER, 2); + return Math::Clipper::int_offset($polygons, $factor // (scale 1e-05), 100000, JT_MITER, 2); } sub safety_offset_ex { - # offset polygons and then apply holes to the right contours - return @{ union_ex([ safety_offset(@_) ]) }; + my ($polygons, $factor) = @_; + return map Slic3r::ExPolygon->new($_), + @{Math::Clipper::ex_int_offset($polygons, $factor // (scale 1e-05), 100000, JT_MITER, 2)}; } sub offset { @@ -28,13 +29,18 @@ sub offset { $joinType //= JT_MITER; $miterLimit //= 3; - my $offsets = Math::Clipper::offset($polygons, $distance, $scale, $joinType, $miterLimit); + my $offsets = Math::Clipper::int_offset($polygons, $distance, $scale, $joinType, $miterLimit); return @$offsets; } sub offset_ex { - # offset polygons and then apply holes to the right contours - return @{ union_ex([ offset(@_) ]) }; + my ($polygons, $distance, $scale, $joinType, $miterLimit) = @_; + $scale ||= 100000; + $joinType //= JT_MITER; + $miterLimit //= 3; + + my $offsets = Math::Clipper::ex_int_offset($polygons, $distance, $scale, $joinType, $miterLimit); + return map Slic3r::ExPolygon->new($_), @$offsets; } sub diff_ex { @@ -96,13 +102,19 @@ sub xor_ex { ]; } +sub ex_int_offset2 { + my ($polygons, $delta1, $delta2, $scale, $joinType, $miterLimit) = @_; + $scale ||= 100000; + $joinType //= JT_MITER; + $miterLimit //= 3; + + my $offsets = Math::Clipper::ex_int_offset2($polygons, $delta1, $delta2, $scale, $joinType, $miterLimit); + return map Slic3r::ExPolygon->new($_), @$offsets; +} + sub collapse_ex { my ($polygons, $width) = @_; - my @result = offset( - [ offset($polygons, -$width/2,) ], - +$width/2, - ); - return union_ex([@result]); + return ex_int_offset2($polygons, -$width/2, +$width/2); } sub simplify_polygon { diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index c13b32760..e4d8ee476 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -96,12 +96,9 @@ sub make_surfaces { # detect thin walls by offsetting slices by half extrusion inwards { my $width = $self->perimeter_flow->scaled_width; - my $outgrown = union_ex([ - Slic3r::Geometry::Clipper::offset( - [Slic3r::Geometry::Clipper::offset([ map @$_, map $_->expolygon, @{$self->slices} ], -$width)], - +$width, - ), - ]); + my $outgrown = [ + Slic3r::Geometry::Clipper::ex_int_offset2([ map @$_, map $_->expolygon, @{$self->slices} ], -$width, +$width), + ]; my $diff = diff_ex( [ map $_->p, @{$self->slices} ], [ map @$_, @$outgrown ], @@ -230,12 +227,7 @@ sub make_perimeters { # offsetting a polygon can result in one or many offset polygons my @new_offsets = (); foreach my $expolygon (@last_offsets) { - my @offsets = @{union_ex([ - Slic3r::Geometry::Clipper::offset( - [Slic3r::Geometry::Clipper::offset($expolygon, -1.5*$spacing)], - +0.5*$spacing, - ), - ])}; + my @offsets = Slic3r::Geometry::Clipper::ex_int_offset2($expolygon, -1.5*$spacing, +0.5*$spacing); push @new_offsets, @offsets; # where the above check collapses the expolygon, then there's no room for an inner loop From e602aad998a6f77487b0eec4a5d87031c5d01f4a Mon Sep 17 00:00:00 2001 From: Devin Grady Date: Mon, 1 Apr 2013 22:12:51 -0500 Subject: [PATCH 02/22] inspired by f5e48a3, a seems to work to fix 1076 --- lib/Slic3r/Print.pm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 2cf12bf63..a0ac953ba 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -151,7 +151,9 @@ sub validate { { my @points = map [ @$_[X,Y] ], map @{$_->vertices}, @{$self->objects->[$obj_idx]->meshes}; my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points)); - ($clearance) = offset([$convex_hull], scale $Slic3r::Config->extruder_clearance_radius / 2, 1, JT_ROUND); + ($clearance) = map Slic3r::Polygon->new($_), + Slic3r::Geometry::Clipper::offset( + [$convex_hull], scale $Slic3r::Config->extruder_clearance_radius / 2, 1, JT_ROUND); } for my $copy (@{$self->objects->[$obj_idx]->copies}) { my $copy_clearance = $clearance->clone; From d089d2b2d4064425280d791939568b3ab2d91eac Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 3 Apr 2013 19:06:33 +0200 Subject: [PATCH 03/22] Bugfix: crash introduced by wipe --- lib/Slic3r/GCode.pm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 92190cf7d..6728b5fd4 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -385,11 +385,11 @@ sub retract { my $travel = [undef, $params{move_z}, $retract->[2], "change layer and $comment"]; $gcode .= $self->G0(@$travel); } else { - if ($wipe_path) { + # check that we have a positive wipe length + if ($wipe_path && (my $total_wipe_length = $wipe_path->length)) { $self->speed('travel'); - # subdivide the retraction - my $total_wipe_length = $wipe_path->length; + # subdivide the retraction for (1 .. $#$wipe_path) { my $segment_length = $wipe_path->[$_-1]->distance_to($wipe_path->[$_]); $gcode .= $self->G1($wipe_path->[$_], undef, $retract->[2] * ($segment_length / $total_wipe_length), $retract->[3] . ";_WIPE"); From 88e70a59c72d0c06ccaaf0b45499249b12d27890 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 3 Apr 2013 19:08:12 +0200 Subject: [PATCH 04/22] Don't wipe if option is disabled --- lib/Slic3r/GCode.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 6728b5fd4..72744eba5 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -248,7 +248,7 @@ sub extrude_path { $gcode .= $self->G1($line->[B], undef, $e * $line_length, $description); } $self->wipe_path(Slic3r::Polyline->new([ reverse @{$path->points} ])) - if $Slic3r::Config->wipe; + if $self->extruder->wipe; } if ($Slic3r::Config->cooling) { @@ -358,7 +358,7 @@ sub retract { # wipe my $wipe_path; - if ($Slic3r::Config->wipe && $self->wipe_path) { + if ($self->extruder->wipe && $self->wipe_path) { $wipe_path = Slic3r::Polyline->new([ $self->last_pos, @{$self->wipe_path}[1..$#{$self->wipe_path}] ]) ->clip_start($self->extruder->scaled_wipe_distance); } From b725847a5131f5dc7e82a7eeda1783e9287580ea Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Wed, 3 Apr 2013 19:26:59 +0200 Subject: [PATCH 05/22] Bugfix: configuration wizard led to crash with simple mode. #1077 --- lib/Slic3r/GUI/SkeinPanel.pm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GUI/SkeinPanel.pm b/lib/Slic3r/GUI/SkeinPanel.pm index ef7403351..1bcaa4d90 100644 --- a/lib/Slic3r/GUI/SkeinPanel.pm +++ b/lib/Slic3r/GUI/SkeinPanel.pm @@ -243,7 +243,9 @@ sub config_wizard { return unless $self->check_unsaved_changes; if (my $config = Slic3r::GUI::ConfigWizard->new($self)->run) { - $_->select_default_preset for values %{$self->{options_tabs}}; + if ($self->{mode} eq 'expert') { + $_->select_default_preset for values %{$self->{options_tabs}}; + } $self->load_config($config); } } From 3afeb5c7b5896db30f0f673bbf228cedf5fe3110 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 4 Apr 2013 00:52:11 +0200 Subject: [PATCH 06/22] Second layer commands were written multiple times --- lib/Slic3r/Print.pm | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 2cf12bf63..bbccf7ed7 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -794,6 +794,7 @@ sub write_gcode { } $gcode .= $gcodegen->set_bed_temperature($Slic3r::Config->bed_temperature) if $Slic3r::Config->bed_temperature && $Slic3r::Config->bed_temperature != $Slic3r::Config->first_layer_bed_temperature; + $second_layer_things_done = 1; } # set new layer, but don't move Z as support material contact areas may need an intermediate one From b4be61b703a33c1b3f579c143a520680caaaed38 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 4 Apr 2013 01:17:44 +0200 Subject: [PATCH 07/22] Bugfix: time estimates for cooling were computed for each object separately instead of the whole actual layer. #1071 --- MANIFEST | 1 + lib/Slic3r.pm | 1 + lib/Slic3r/GCode/CoolingBuffer.pm | 77 +++++++++++++++++++++++++++++++ lib/Slic3r/Print.pm | 57 ++++++----------------- 4 files changed, 93 insertions(+), 43 deletions(-) create mode 100644 lib/Slic3r/GCode/CoolingBuffer.pm diff --git a/MANIFEST b/MANIFEST index 6e59ce2e5..e7498665e 100644 --- a/MANIFEST +++ b/MANIFEST @@ -24,6 +24,7 @@ lib/Slic3r/Format/AMF/Parser.pm lib/Slic3r/Format/OBJ.pm lib/Slic3r/Format/STL.pm lib/Slic3r/GCode.pm +lib/Slic3r/GCode/CoolingBuffer.pm lib/Slic3r/GCode/MotionPlanner.pm lib/Slic3r/Geometry.pm lib/Slic3r/Geometry/Clipper.pm diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 478165bc3..eddc2939b 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -45,6 +45,7 @@ use Slic3r::Format::AMF; use Slic3r::Format::OBJ; use Slic3r::Format::STL; use Slic3r::GCode; +use Slic3r::GCode::CoolingBuffer; use Slic3r::GCode::MotionPlanner; use Slic3r::Geometry qw(PI); use Slic3r::Layer; diff --git a/lib/Slic3r/GCode/CoolingBuffer.pm b/lib/Slic3r/GCode/CoolingBuffer.pm new file mode 100644 index 000000000..d88e6c2e0 --- /dev/null +++ b/lib/Slic3r/GCode/CoolingBuffer.pm @@ -0,0 +1,77 @@ +package Slic3r::GCode::CoolingBuffer; +use Moo; + +has 'config' => (is => 'ro', required => 1); +has 'gcodegen' => (is => 'ro', required => 1); +has 'gcode' => (is => 'rw', default => sub {""}); +has 'layer_id' => (is => 'rw'); +has 'last_z' => (is => 'rw'); +has 'min_print_speed' => (is => 'lazy'); + +sub _build_min_print_speed { + my $self = shift; + return 60 * $self->config->min_print_speed; +} + +sub append { + my $self = shift; + my ($gcode, $layer) = @_; + + my $return = ""; + if (defined $self->last_z && $self->last_z != $layer->print_z) { + $return = $self->flush; + $self->gcodegen->elapsed_time(0); + } + + $self->layer_id($layer->id); + $self->last_z($layer->print_z); + $self->gcode($self->gcode . $gcode); + + return $return; +} + +sub flush { + my $self = shift; + + my $gcode = $self->gcode; + $self->gcode(""); + + my $fan_speed = $self->config->fan_always_on ? $self->config->min_fan_speed : 0; + my $speed_factor = 1; + if ($self->config->cooling) { + my $layer_time = $self->gcodegen->elapsed_time; + Slic3r::debugf "Layer %d estimated printing time: %d seconds\n", $self->layer_id, $layer_time; + if ($layer_time < $self->config->slowdown_below_layer_time) { + $fan_speed = $self->config->max_fan_speed; + $speed_factor = $layer_time / $self->config->slowdown_below_layer_time; + } elsif ($layer_time < $self->config->fan_below_layer_time) { + $fan_speed = $self->config->max_fan_speed - ($self->config->max_fan_speed - $self->config->min_fan_speed) + * ($layer_time - $self->config->slowdown_below_layer_time) + / ($self->config->fan_below_layer_time - $self->config->slowdown_below_layer_time); #/ + } + Slic3r::debugf " fan = %d%%, speed = %d%%\n", $fan_speed, $speed_factor * 100; + + if ($speed_factor < 1) { + my $dec = $self->gcodegen->dec; + $gcode =~ s/^(?=.*? [XY])(?=.*? E)(?!;_WIPE)(?min_print_speed ? $self->min_print_speed : $new_speed) + /gexm; + } + $fan_speed = 0 if $self->layer_id < $self->config->disable_fan_first_layers; + } + $gcode = $self->gcodegen->set_fan($fan_speed) . $gcode; + + # bridge fan speed + if (!$self->config->cooling || $self->config->bridge_fan_speed == 0 || $self->layer_id < $self->config->disable_fan_first_layers) { + $gcode =~ s/^;_BRIDGE_FAN_(?:START|END)\n//gm; + } else { + $gcode =~ s/^;_BRIDGE_FAN_START\n/ $self->gcodegen->set_fan($self->config->bridge_fan_speed, 1) /gmex; + $gcode =~ s/^;_BRIDGE_FAN_END\n/ $self->gcodegen->set_fan($fan_speed, 1) /gmex; + } + $gcode =~ s/;_WIPE//g; + + return $gcode; +} + +1; diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 64747ca6f..189b665bd 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -716,8 +716,6 @@ sub write_gcode { multiple_extruders => (@{$self->extruders} > 1), layer_count => $self->layer_count, ); - my $min_print_speed = 60 * $Slic3r::Config->min_print_speed; - my $dec = $gcodegen->dec; print $fh "G21 ; set units to millimeters\n"; print $fh $gcodegen->set_fan(0, 1) if $Slic3r::Config->cooling && $Slic3r::Config->disable_fan_first_layers; @@ -801,7 +799,6 @@ sub write_gcode { # set new layer, but don't move Z as support material contact areas may need an intermediate one $gcode .= $gcodegen->change_layer($layer); - $gcodegen->elapsed_time(0); # prepare callback to call as soon as a Z command is generated $gcodegen->move_z_callback(sub { @@ -943,42 +940,6 @@ sub write_gcode { } } } - return if !$gcode; - - my $fan_speed = $Slic3r::Config->fan_always_on ? $Slic3r::Config->min_fan_speed : 0; - my $speed_factor = 1; - if ($Slic3r::Config->cooling) { - my $layer_time = $gcodegen->elapsed_time; - Slic3r::debugf "Layer %d estimated printing time: %d seconds\n", $layer->id, $layer_time; - if ($layer_time < $Slic3r::Config->slowdown_below_layer_time) { - $fan_speed = $Slic3r::Config->max_fan_speed; - $speed_factor = $layer_time / $Slic3r::Config->slowdown_below_layer_time; - } elsif ($layer_time < $Slic3r::Config->fan_below_layer_time) { - $fan_speed = $Slic3r::Config->max_fan_speed - ($Slic3r::Config->max_fan_speed - $Slic3r::Config->min_fan_speed) - * ($layer_time - $Slic3r::Config->slowdown_below_layer_time) - / ($Slic3r::Config->fan_below_layer_time - $Slic3r::Config->slowdown_below_layer_time); #/ - } - Slic3r::debugf " fan = %d%%, speed = %d%%\n", $fan_speed, $speed_factor * 100; - - if ($speed_factor < 1) { - $gcode =~ s/^(?=.*? [XY])(?=.*? E)(?!;_WIPE)(?id < $Slic3r::Config->disable_fan_first_layers; - } - $gcode = $gcodegen->set_fan($fan_speed) . $gcode; - - # bridge fan speed - if (!$Slic3r::Config->cooling || $Slic3r::Config->bridge_fan_speed == 0 || $layer->id < $Slic3r::Config->disable_fan_first_layers) { - $gcode =~ s/^;_BRIDGE_FAN_(?:START|END)\n//gm; - } else { - $gcode =~ s/^;_BRIDGE_FAN_START\n/ $gcodegen->set_fan($Slic3r::Config->bridge_fan_speed, 1) /gmex; - $gcode =~ s/^;_BRIDGE_FAN_END\n/ $gcodegen->set_fan($fan_speed, 1) /gmex; - } - $gcode =~ s/;_WIPE//g; - return $gcode; }; @@ -1001,6 +962,11 @@ sub write_gcode { print $fh $gcodegen->G0(Slic3r::Point->new(0,0), undef, 0, 'move to origin position for next object'); } + my $buffer = Slic3r::GCode::CoolingBuffer->new( + config => $Slic3r::Config, + gcodegen => $gcodegen, + ); + for my $layer (@{$self->objects->[$obj_idx]->layers}) { # if we are printing the bottom layer of an object, and we have already finished # another one, set first layer temperatures. this happens before the Z move @@ -1010,15 +976,20 @@ sub write_gcode { if $Slic3r::Config->first_layer_bed_temperature; $print_first_layer_temperature->(); } - print $fh $extrude_layer->($layer, [$copy]); + print $fh $buffer->append($extrude_layer->($layer, [$copy]), $layer); } + print $fh $buffer->flush; $finished_objects++; } } } else { - print $fh $extrude_layer->($_, $_->object->copies) - for sort { $a->print_z <=> $b->print_z } - map @{$_->layers}, @{$self->objects}; + my $buffer = Slic3r::GCode::CoolingBuffer->new( + config => $Slic3r::Config, + gcodegen => $gcodegen, + ); + print $fh $buffer->append($extrude_layer->($_, $_->object->copies), $_) + for sort { $a->print_z <=> $b->print_z } map @{$_->layers}, @{$self->objects}; + print $fh $buffer->flush; } # save statistic data From 7e51cbcf69f58da1a3c8ca9addbcf7f6c5b2e764 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 4 Apr 2013 01:24:40 +0200 Subject: [PATCH 08/22] Allow 0.1mm slots. #959 #1056 --- lib/Slic3r/Layer/Region.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Layer/Region.pm b/lib/Slic3r/Layer/Region.pm index c13b32760..f04b37131 100644 --- a/lib/Slic3r/Layer/Region.pm +++ b/lib/Slic3r/Layer/Region.pm @@ -139,7 +139,7 @@ sub _merge_loops { # winding order. # TODO: find a faster algorithm for this. my @loops = sort { $a->encloses_point($b->[0]) ? 0 : 1 } @$loops; # outer first - $safety_offset //= scale 0.1; + $safety_offset //= scale 0.0499; @loops = @{ safety_offset(\@loops, $safety_offset) }; my $expolygons = []; while (my $loop = shift @loops) { From 060d2da7fe0eaef472da9adcc0aed1871026c86c Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Sun, 7 Apr 2013 18:01:15 -0400 Subject: [PATCH 09/22] Small optimization on an incredibly hot codepath. --- lib/Slic3r/Geometry.pm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index 0d2d9df4d..dc30b3472 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -116,8 +116,7 @@ sub distance_between_points { } sub comparable_distance_between_points { - my ($p1, $p2) = @_; - return (($p1->[X] - $p2->[X])**2) + (($p1->[Y] - $p2->[Y])**2); + return (($_[0]->[X] - $_[1]->[X])**2) + (($_[0]->[Y] - $_[1]->[Y])**2); } sub point_line_distance { From 3e8c5804fe03296de07054294bccb187f8c39be3 Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Sun, 7 Apr 2013 18:13:40 -0400 Subject: [PATCH 10/22] Inline comparable_distance_between_points It was called on an incredibly hot codepath from a single place. At 12313276 calls on my test .stl, the sub call overhead alone was a significant perf hit. --- lib/Slic3r/Geometry.pm | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index dc30b3472..c6744a200 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -7,7 +7,7 @@ our @ISA = qw(Exporter); our @EXPORT_OK = qw( PI X Y Z A B X1 Y1 X2 Y2 MIN MAX epsilon slope line_atan lines_parallel line_point_belongs_to_segment points_coincide distance_between_points - comparable_distance_between_points chained_path_items chained_path_points + chained_path_items chained_path_points line_length midpoint point_in_polygon point_in_segment segment_in_segment point_is_on_left_of_segment polyline_lines polygon_lines nearest_point point_along_segment polygon_segment_having_point polygon_has_subsegment @@ -115,10 +115,6 @@ sub distance_between_points { return sqrt((($p1->[X] - $p2->[X])**2) + ($p1->[Y] - $p2->[Y])**2); } -sub comparable_distance_between_points { - return (($_[0]->[X] - $_[1]->[X])**2) + (($_[0]->[Y] - $_[1]->[Y])**2); -} - sub point_line_distance { my ($point, $line) = @_; return distance_between_points($point, $line->[A]) @@ -248,7 +244,7 @@ sub nearest_point_index { my ($nearest_point_index, $distance) = (); for my $i (0..$#$points) { - my $d = comparable_distance_between_points($point, $points->[$i]); + my $d = (($point->[X] - $points->[$i]->[X])**2) + (($point->[Y] - $points->[$i]->[Y])**2); if (!defined $distance || $d < $distance) { $nearest_point_index = $i; $distance = $d; From da0e67a891c76b1eccd090e01089c52394fdbbab Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Sun, 7 Apr 2013 18:28:08 -0400 Subject: [PATCH 11/22] Only look up $point's X and Y once, rather than once on every pass through the loop. (Those lookups are expensive) --- lib/Slic3r/Geometry.pm | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index c6744a200..d21cff330 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -243,8 +243,12 @@ sub nearest_point_index { my ($point, $points) = @_; my ($nearest_point_index, $distance) = (); + + my $point_x = $point->[X]; + my $point_y = $point->[Y]; + for my $i (0..$#$points) { - my $d = (($point->[X] - $points->[$i]->[X])**2) + (($point->[Y] - $points->[$i]->[Y])**2); + my $d = (($point_x - $points->[$i]->[X])**2) + (($point_y - $points->[$i]->[Y])**2); if (!defined $distance || $d < $distance) { $nearest_point_index = $i; $distance = $d; From e8ca1e59a6bd9df0407c151e24f5d3273625a958 Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Sun, 7 Apr 2013 19:44:32 -0400 Subject: [PATCH 12/22] no functional change. only return from one place for clarity --- lib/Slic3r/Geometry.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index d21cff330..e1a591025 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -252,7 +252,7 @@ sub nearest_point_index { if (!defined $distance || $d < $distance) { $nearest_point_index = $i; $distance = $d; - return $i if $distance < epsilon; + last if $distance < epsilon; } } return $nearest_point_index; From 7ec63321413f10f68f389140c7488b2bb7d14fac Mon Sep 17 00:00:00 2001 From: Jesse Vincent Date: Sun, 7 Apr 2013 19:53:15 -0400 Subject: [PATCH 13/22] split apart the math in nearest_point_index and short-circuit if we know the candidate is no good --- lib/Slic3r/Geometry.pm | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index e1a591025..9f0a631f4 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -248,13 +248,22 @@ sub nearest_point_index { my $point_y = $point->[Y]; for my $i (0..$#$points) { - my $d = (($point_x - $points->[$i]->[X])**2) + (($point_y - $points->[$i]->[Y])**2); - if (!defined $distance || $d < $distance) { - $nearest_point_index = $i; - $distance = $d; - last if $distance < epsilon; - } + my $d = ($point_x - $points->[$i]->[X])**2; + # If the X distance of the candidate is > than the total distance of the + # best previous candidate, we know we don't want it + next if (defined $distance && $d > $distance); + + # If the total distance of the candidate is > than the total distance of the + # best previous candidate, we know we don't want it + $d += ($point_y - $points->[$i]->[Y])**2; + next if (defined $distance && $d > $distance); + + $nearest_point_index = $i; + $distance = $d; + + last if $distance < epsilon; } + return $nearest_point_index; } From 94ed6cd2397c495f2d38b57dac3086e2cda8bdce Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 8 Apr 2013 22:23:51 +0200 Subject: [PATCH 14/22] Always move Z at travel speed. #1093 --- lib/Slic3r/GCode.pm | 5 ++++- t/fill.t | 5 +++++ t/retraction.t | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/Slic3r/GCode.pm b/lib/Slic3r/GCode.pm index 72744eba5..d2a667ef6 100644 --- a/lib/Slic3r/GCode.pm +++ b/lib/Slic3r/GCode.pm @@ -364,13 +364,13 @@ sub retract { } # prepare moves - $self->speed('retract'); my $retract = [undef, undef, -$length, $comment]; my $lift = ($self->extruder->retract_lift == 0 || defined $params{move_z}) && !$self->lifted ? undef : [undef, $self->z + $self->extruder->retract_lift, 0, 'lift plate during travel']; if (($Slic3r::Config->g0 || $Slic3r::Config->gcode_flavor eq 'mach3') && $params{travel_to}) { + $self->speed('travel'); if ($lift) { # combine lift and retract $lift->[2] = $retract->[2]; @@ -382,6 +382,7 @@ sub retract { } } elsif (($Slic3r::Config->g0 || $Slic3r::Config->gcode_flavor eq 'mach3') && defined $params{move_z}) { # combine Z change and retraction + $self->speed('travel'); my $travel = [undef, $params{move_z}, $retract->[2], "change layer and $comment"]; $gcode .= $self->G0(@$travel); } else { @@ -395,9 +396,11 @@ sub retract { $gcode .= $self->G1($wipe_path->[$_], undef, $retract->[2] * ($segment_length / $total_wipe_length), $retract->[3] . ";_WIPE"); } } else { + $self->speed('retract'); $gcode .= $self->G1(@$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']; $gcode .= $self->G0(@$travel); diff --git a/t/fill.t b/t/fill.t index cbffd7dab..7cc1e5d3f 100644 --- a/t/fill.t +++ b/t/fill.t @@ -39,6 +39,11 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } $surface->expolygon->rotate(Slic3r::Geometry::deg2rad($angle), [0,0]); my ($params, @paths) = $filler->fill_surface($surface, flow_spacing => 0.69, density => 0.4); is scalar @paths, 1, 'one continuous path'; + use Slic3r::SVG; + Slic3r::SVG::output("fill.svg", + expolygons => [$surface->expolygon], + polylines => [@paths], + );exit; } } diff --git a/t/retraction.t b/t/retraction.t index a23e6f818..1c028893d 100644 --- a/t/retraction.t +++ b/t/retraction.t @@ -52,6 +52,7 @@ my $test = sub { if !_eq($info->{dist_Z}, -$print->extruders->[$tool]->retract_lift); $lifted = 0; } + fail 'move Z at travel speed' if ($args->{F} // $self->F) != $conf->travel_speed * 60; } if ($info->{retracting}) { $retracted[$tool] = 1; @@ -89,6 +90,7 @@ my $test = sub { }; $config->set('first_layer_height', $config->layer_height); +$config->set('first_layer_speed', '100%'); $config->set('start_gcode', ''); # to avoid dealing with the nozzle lift in start G-code $config->set('retract_length', [1.5]); $config->set('retract_before_travel', [3]); From 8030eaaa04a98ad1a3d5711a4567b7106d3113db Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 9 Apr 2013 14:02:49 +0200 Subject: [PATCH 15/22] Remove debugging statements --- t/fill.t | 5 ----- 1 file changed, 5 deletions(-) diff --git a/t/fill.t b/t/fill.t index 7cc1e5d3f..cbffd7dab 100644 --- a/t/fill.t +++ b/t/fill.t @@ -39,11 +39,6 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } $surface->expolygon->rotate(Slic3r::Geometry::deg2rad($angle), [0,0]); my ($params, @paths) = $filler->fill_surface($surface, flow_spacing => 0.69, density => 0.4); is scalar @paths, 1, 'one continuous path'; - use Slic3r::SVG; - Slic3r::SVG::output("fill.svg", - expolygons => [$surface->expolygon], - polylines => [@paths], - );exit; } } From c2aa119c09350d1ea3d3cb1adb017d2dd2eddddd Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Tue, 9 Apr 2013 14:03:24 +0200 Subject: [PATCH 16/22] Require Boost::Geometry::Utils 0.08 --- Build.PL | 2 +- lib/Slic3r.pm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Build.PL b/Build.PL index b8921a8c0..c44c50842 100644 --- a/Build.PL +++ b/Build.PL @@ -7,7 +7,7 @@ my $build = Module::Build->new( dist_version => '0.1', license => 'perl', requires => { - 'Boost::Geometry::Utils' => '0.06', + 'Boost::Geometry::Utils' => '0.08', 'Encode::Locale' => '0', 'File::Basename' => '0', 'File::Spec' => '0', diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index eddc2939b..df04dc96b 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -29,7 +29,7 @@ our $var = "$FindBin::Bin/var"; use Encode; use Encode::Locale; -use Boost::Geometry::Utils 0.06; +use Boost::Geometry::Utils 0.08; use Moo 0.091009; use Slic3r::Config; From a0a54ea70697418ae6fb7939490dcf3c29ecd845 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 11 Apr 2013 19:36:49 +0200 Subject: [PATCH 17/22] Align rectilinear and line infill across layers. #712 --- lib/Slic3r/Fill/Rectilinear.pm | 50 ++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 20 deletions(-) diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index e1dcab0e6..a455b4aa1 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -3,6 +3,9 @@ use Moo; extends 'Slic3r::Fill::Base'; +has 'bounding_box' => (is => 'rw'); +has 'cache' => (is => 'rw', default => sub {{}}); + use Slic3r::Geometry qw(X1 Y1 X2 Y2 A B X Y scale unscale scaled_epsilon); sub fill_surface { @@ -18,37 +21,44 @@ sub fill_surface { return {} if !$expolygon_off; # skip some very small polygons (which shouldn't arrive here) my $bounding_box = [ $expolygon->bounding_box ]; + my $flow_spacing = $params{flow_spacing}; my $min_spacing = scale $params{flow_spacing}; my $distance_between_lines = $min_spacing / $params{density}; my $line_oscillation = $distance_between_lines - $min_spacing; - - my $flow_spacing = $params{flow_spacing}; - if ($params{density} == 1 && !$params{dont_adjust}) { - $distance_between_lines = $self->adjust_solid_spacing( - width => $bounding_box->[X2] - $bounding_box->[X1], - distance => $distance_between_lines, - ); - $flow_spacing = unscale $distance_between_lines; - } - - my $x = $bounding_box->[X1]; my $is_line_pattern = $self->isa('Slic3r::Fill::Line'); - my @vertical_lines = (); - for (my $i = 0; $x <= $bounding_box->[X2] + scaled_epsilon; $i++) { - my $vertical_line = Slic3r::Line->new([$x, $bounding_box->[Y2]], [$x, $bounding_box->[Y1]]); - if ($is_line_pattern && $i % 2) { - $vertical_line->[A][X] += $line_oscillation; - $vertical_line->[B][X] -= $line_oscillation; + + my $cache_id = sprintf "d%s_s%s_a%s", + $params{density}, $params{flow_spacing}, $rotate_vector->[0][0]; + + if (!$self->cache->{$cache_id} || !defined $self->bounding_box) { + if ($params{density} == 1 && !$params{dont_adjust}) { + $distance_between_lines = $self->adjust_solid_spacing( + width => $bounding_box->[X2] - $bounding_box->[X1], + distance => $distance_between_lines, + ); + $flow_spacing = unscale $distance_between_lines; } - push @vertical_lines, $vertical_line; - $x += $distance_between_lines; + + my $x = $bounding_box->[X1]; + my @vertical_lines = (); + for (my $i = 0; $x <= $bounding_box->[X2] + scaled_epsilon; $i++) { + my $vertical_line = Slic3r::Line->new([$x, $bounding_box->[Y2]], [$x, $bounding_box->[Y1]]); + if ($is_line_pattern && $i % 2) { + $vertical_line->[A][X] += $line_oscillation; + $vertical_line->[B][X] -= $line_oscillation; + } + push @vertical_lines, $vertical_line; + $x += $distance_between_lines; + } + + $self->cache->{$cache_id} = [@vertical_lines]; } # clip paths against a slightly offsetted expolygon, so that the first and last paths # are kept even if the expolygon has vertical sides my @paths = @{ Boost::Geometry::Utils::polygon_multi_linestring_intersection( +($expolygon->offset_ex(scaled_epsilon))[0], # TODO: we should use all the resulting expolygons and clip the linestrings to a multipolygon object - [ @vertical_lines ], + [ @{ $self->cache->{$cache_id} } ], ) }; # connect lines From bd3384525ec62ae8a512035a9adc4db46d652e5c Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 15 Apr 2013 11:33:24 +0200 Subject: [PATCH 18/22] Revert "Align rectilinear and line infill across layers. #712" This reverts commit a0a54ea70697418ae6fb7939490dcf3c29ecd845. --- lib/Slic3r/Fill/Rectilinear.pm | 50 ++++++++++++++-------------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index a455b4aa1..e1dcab0e6 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -3,9 +3,6 @@ use Moo; extends 'Slic3r::Fill::Base'; -has 'bounding_box' => (is => 'rw'); -has 'cache' => (is => 'rw', default => sub {{}}); - use Slic3r::Geometry qw(X1 Y1 X2 Y2 A B X Y scale unscale scaled_epsilon); sub fill_surface { @@ -21,44 +18,37 @@ sub fill_surface { return {} if !$expolygon_off; # skip some very small polygons (which shouldn't arrive here) my $bounding_box = [ $expolygon->bounding_box ]; - my $flow_spacing = $params{flow_spacing}; my $min_spacing = scale $params{flow_spacing}; my $distance_between_lines = $min_spacing / $params{density}; my $line_oscillation = $distance_between_lines - $min_spacing; + + my $flow_spacing = $params{flow_spacing}; + if ($params{density} == 1 && !$params{dont_adjust}) { + $distance_between_lines = $self->adjust_solid_spacing( + width => $bounding_box->[X2] - $bounding_box->[X1], + distance => $distance_between_lines, + ); + $flow_spacing = unscale $distance_between_lines; + } + + my $x = $bounding_box->[X1]; my $is_line_pattern = $self->isa('Slic3r::Fill::Line'); - - my $cache_id = sprintf "d%s_s%s_a%s", - $params{density}, $params{flow_spacing}, $rotate_vector->[0][0]; - - if (!$self->cache->{$cache_id} || !defined $self->bounding_box) { - if ($params{density} == 1 && !$params{dont_adjust}) { - $distance_between_lines = $self->adjust_solid_spacing( - width => $bounding_box->[X2] - $bounding_box->[X1], - distance => $distance_between_lines, - ); - $flow_spacing = unscale $distance_between_lines; + my @vertical_lines = (); + for (my $i = 0; $x <= $bounding_box->[X2] + scaled_epsilon; $i++) { + my $vertical_line = Slic3r::Line->new([$x, $bounding_box->[Y2]], [$x, $bounding_box->[Y1]]); + if ($is_line_pattern && $i % 2) { + $vertical_line->[A][X] += $line_oscillation; + $vertical_line->[B][X] -= $line_oscillation; } - - my $x = $bounding_box->[X1]; - my @vertical_lines = (); - for (my $i = 0; $x <= $bounding_box->[X2] + scaled_epsilon; $i++) { - my $vertical_line = Slic3r::Line->new([$x, $bounding_box->[Y2]], [$x, $bounding_box->[Y1]]); - if ($is_line_pattern && $i % 2) { - $vertical_line->[A][X] += $line_oscillation; - $vertical_line->[B][X] -= $line_oscillation; - } - push @vertical_lines, $vertical_line; - $x += $distance_between_lines; - } - - $self->cache->{$cache_id} = [@vertical_lines]; + push @vertical_lines, $vertical_line; + $x += $distance_between_lines; } # clip paths against a slightly offsetted expolygon, so that the first and last paths # are kept even if the expolygon has vertical sides my @paths = @{ Boost::Geometry::Utils::polygon_multi_linestring_intersection( +($expolygon->offset_ex(scaled_epsilon))[0], # TODO: we should use all the resulting expolygons and clip the linestrings to a multipolygon object - [ @{ $self->cache->{$cache_id} } ], + [ @vertical_lines ], ) }; # connect lines From c723c07f8c9537a7402bb67106d9353225f947bf Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 15 Apr 2013 19:03:02 +0200 Subject: [PATCH 19/22] Update unit test after recent conversion of Boost::Geometry::Utils to double coordinates --- t/polyclip.t | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/t/polyclip.t b/t/polyclip.t index 8877d4229..c25ad0fb5 100644 --- a/t/polyclip.t +++ b/t/polyclip.t @@ -144,8 +144,8 @@ is_deeply $intersection, [ [120, 120], [180, 160] ], 'internal lines are preserv my $intersections = $expolygon->clip_line($line); is_deeply $intersections, [ - [ [152, 287], [152, 214], ], - [ [152, 107], [152, 35] ], + [ [152.742, 288.086660915295], [152.742, 215.178843238354], ], + [ [152.742, 108.087506777797], [152.742, 35.1664774739315] ], ], 'line is clipped to square with hole'; } From 430c82591808cfa361d5d2a2cba5fc771282a8d0 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 18 Apr 2013 17:34:21 +0200 Subject: [PATCH 20/22] Align rectilinear and line infill across layers (new implementation). #712 --- lib/Slic3r/ExPolygon.pm | 5 --- lib/Slic3r/Fill.pm | 7 ++-- lib/Slic3r/Fill/Base.pm | 17 ++++------ lib/Slic3r/Fill/Honeycomb.pm | 5 ++- lib/Slic3r/Fill/Rectilinear.pm | 60 ++++++++++++++++++++++------------ lib/Slic3r/Geometry.pm | 6 ++-- lib/Slic3r/Print/Object.pm | 8 ++--- 7 files changed, 57 insertions(+), 51 deletions(-) diff --git a/lib/Slic3r/ExPolygon.pm b/lib/Slic3r/ExPolygon.pm index ac1d0fb35..fe2759526 100644 --- a/lib/Slic3r/ExPolygon.pm +++ b/lib/Slic3r/ExPolygon.pm @@ -161,11 +161,6 @@ sub bounding_box_polygon { ]); } -sub bounding_box_center { - my $self = shift; - return Slic3r::Geometry::bounding_box_center($self->contour); -} - sub clip_line { my $self = shift; my ($line) = @_; # line must be a Slic3r::Line object diff --git a/lib/Slic3r/Fill.pm b/lib/Slic3r/Fill.pm index de1d52ef6..7db23412a 100644 --- a/lib/Slic3r/Fill.pm +++ b/lib/Slic3r/Fill.pm @@ -39,10 +39,9 @@ sub filler { return $FillTypes{$filler}->new; } - if (!$self->fillers->{$filler}) { - my $f = $self->fillers->{$filler} = $FillTypes{$filler}->new; - $f->bounding_box([ $self->print->bounding_box ]) if $f->can('bounding_box'); - } + $self->fillers->{$filler} ||= $FillTypes{$filler}->new( + bounding_box => [ $self->print->bounding_box ], + ); return $self->fillers->{$filler}; } diff --git a/lib/Slic3r/Fill/Base.pm b/lib/Slic3r/Fill/Base.pm index 9c6996be3..c9f801f59 100644 --- a/lib/Slic3r/Fill/Base.pm +++ b/lib/Slic3r/Fill/Base.pm @@ -5,6 +5,7 @@ use Slic3r::Geometry qw(PI); has 'layer_id' => (is => 'rw'); has 'angle' => (is => 'rw', default => sub { $Slic3r::Config->fill_angle }); +has 'bounding_box' => (is => 'ro', required => 1); sub angles () { [0, PI/2] } @@ -15,7 +16,7 @@ sub infill_direction { # set infill angle my (@rotate, @shift); $rotate[0] = Slic3r::Geometry::deg2rad($self->angle); - $rotate[1] = $surface->expolygon->bounding_box_center; + $rotate[1] = Slic3r::Geometry::bounding_box_center($self->bounding_box); @shift = @{$rotate[1]}; if (defined $self->layer_id) { @@ -41,11 +42,9 @@ sub rotate_points { my @rotate = @{$rotate_vector->[0]}; my @shift = @{$rotate_vector->[1]}; - # rotate points as needed - if ($rotate[0]) { - $expolygon->rotate(@rotate); - $expolygon->translate(@shift); - } + # rotate points + $expolygon->rotate(@rotate); + $expolygon->translate(@shift); } sub rotate_points_back { @@ -54,10 +53,8 @@ sub rotate_points_back { my @rotate = @{$rotate_vector->[0]}; my @shift = @{$rotate_vector->[1]}; - if ($rotate[0]) { - @$paths = map [ Slic3r::Geometry::rotate_points(-$rotate[0], $rotate[1], @$_) ], - map [ Slic3r::Geometry::move_points([map -$_, @shift], @$_) ], @$paths; - } + @$paths = map [ Slic3r::Geometry::rotate_points(-$rotate[0], $rotate[1], @$_) ], + map [ Slic3r::Geometry::move_points([map -$_, @shift], @$_) ], @$paths; } sub adjust_solid_spacing { diff --git a/lib/Slic3r/Fill/Honeycomb.pm b/lib/Slic3r/Fill/Honeycomb.pm index 9e4ff8a27..1985ddb99 100644 --- a/lib/Slic3r/Fill/Honeycomb.pm +++ b/lib/Slic3r/Fill/Honeycomb.pm @@ -3,7 +3,6 @@ use Moo; extends 'Slic3r::Fill::Base'; -has 'bounding_box' => (is => 'rw'); has 'cache' => (is => 'rw', default => sub {{}}); use Slic3r::Geometry qw(PI X1 Y1 X2 Y2 X Y scale); @@ -25,7 +24,7 @@ sub fill_surface { my $cache_id = sprintf "d%s_s%s_a%s", $params{density}, $params{flow_spacing}, $rotate_vector->[0][0]; - if (!$self->cache->{$cache_id} || !defined $self->bounding_box) { + if (!$self->cache->{$cache_id}) { # hexagons math my $hex_side = $distance / (sqrt(3)/2); @@ -39,7 +38,7 @@ sub fill_surface { # adjust actual bounding box to the nearest multiple of our hex pattern # and align it so that it matches across layers - my $bounding_box = [ $self->bounding_box ? @{$self->bounding_box} : $expolygon->bounding_box ]; + my $bounding_box = [ @{$self->bounding_box} ]; # clone $bounding_box->[$_] = 0 for X1, Y1; { my $bb_polygon = Slic3r::Polygon->new_from_bounding_box($bounding_box); diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index e1dcab0e6..d5191a470 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -3,6 +3,8 @@ use Moo; extends 'Slic3r::Fill::Base'; +has 'cache' => (is => 'rw', default => sub {{}}); + use Slic3r::Geometry qw(X1 Y1 X2 Y2 A B X Y scale unscale scaled_epsilon); sub fill_surface { @@ -16,39 +18,55 @@ sub fill_surface { my ($expolygon_off) = $expolygon->offset_ex(scale $params{flow_spacing}/2); return {} if !$expolygon_off; # skip some very small polygons (which shouldn't arrive here) - my $bounding_box = [ $expolygon->bounding_box ]; + my $flow_spacing = $params{flow_spacing}; my $min_spacing = scale $params{flow_spacing}; my $distance_between_lines = $min_spacing / $params{density}; my $line_oscillation = $distance_between_lines - $min_spacing; - - my $flow_spacing = $params{flow_spacing}; - if ($params{density} == 1 && !$params{dont_adjust}) { - $distance_between_lines = $self->adjust_solid_spacing( - width => $bounding_box->[X2] - $bounding_box->[X1], - distance => $distance_between_lines, - ); - $flow_spacing = unscale $distance_between_lines; - } - - my $x = $bounding_box->[X1]; my $is_line_pattern = $self->isa('Slic3r::Fill::Line'); - my @vertical_lines = (); - for (my $i = 0; $x <= $bounding_box->[X2] + scaled_epsilon; $i++) { - my $vertical_line = Slic3r::Line->new([$x, $bounding_box->[Y2]], [$x, $bounding_box->[Y1]]); - if ($is_line_pattern && $i % 2) { - $vertical_line->[A][X] += $line_oscillation; - $vertical_line->[B][X] -= $line_oscillation; + + my $cache_id = sprintf "d%s_s%s_a%s", + $params{density}, $params{flow_spacing}, $rotate_vector->[0][0]; + + if (!$self->cache->{$cache_id}) { + # compute bounding box + my $bounding_box = $self->bounding_box; + { + my $bb_expolygon = Slic3r::ExPolygon->new(Slic3r::Polygon->new_from_bounding_box($bounding_box)); + $self->rotate_points($bb_expolygon, $rotate_vector); + $bounding_box = [ $bb_expolygon->bounding_box ]; } - push @vertical_lines, $vertical_line; - $x += $distance_between_lines; + + # define flow spacing according to requested density + if ($params{density} == 1 && !$params{dont_adjust}) { + $distance_between_lines = $self->adjust_solid_spacing( + width => $bounding_box->[X2] - $bounding_box->[X1], + distance => $distance_between_lines, + ); + $flow_spacing = unscale $distance_between_lines; + } + + # generate the basic pattern + my $x = $bounding_box->[X1]; + my @vertical_lines = (); + for (my $i = 0; $x <= $bounding_box->[X2] + scaled_epsilon; $i++) { + my $vertical_line = Slic3r::Line->new([$x, $bounding_box->[Y2]], [$x, $bounding_box->[Y1]]); + if ($is_line_pattern && $i % 2) { + $vertical_line->[A][X] += $line_oscillation; + $vertical_line->[B][X] -= $line_oscillation; + } + push @vertical_lines, $vertical_line; + $x += $distance_between_lines; + } + + $self->cache->{$cache_id} = [@vertical_lines]; } # clip paths against a slightly offsetted expolygon, so that the first and last paths # are kept even if the expolygon has vertical sides my @paths = @{ Boost::Geometry::Utils::polygon_multi_linestring_intersection( +($expolygon->offset_ex(scaled_epsilon))[0], # TODO: we should use all the resulting expolygons and clip the linestrings to a multipolygon object - [ @vertical_lines ], + [ @{ $self->cache->{$cache_id} } ], ) }; # connect lines diff --git a/lib/Slic3r/Geometry.pm b/lib/Slic3r/Geometry.pm index 9f0a631f4..4c1e23b9f 100644 --- a/lib/Slic3r/Geometry.pm +++ b/lib/Slic3r/Geometry.pm @@ -679,10 +679,10 @@ sub bounding_box { } sub bounding_box_center { - my @bounding_box = bounding_box(@_); + my ($bounding_box) = @_; return Slic3r::Point->new( - ($bounding_box[X2] + $bounding_box[X1]) / 2, - ($bounding_box[Y2] + $bounding_box[Y1]) / 2, + ($bounding_box->[X2] + $bounding_box->[X1]) / 2, + ($bounding_box->[Y2] + $bounding_box->[Y1]) / 2, ); } diff --git a/lib/Slic3r/Print/Object.pm b/lib/Slic3r/Print/Object.pm index 7968ac61f..9fbe353e8 100644 --- a/lib/Slic3r/Print/Object.pm +++ b/lib/Slic3r/Print/Object.pm @@ -894,6 +894,7 @@ sub generate_support_material { # generate paths for the pattern that we're going to use Slic3r::debugf "Generating patterns\n"; + my $fill = Slic3r::Fill->new(print => $self->print); my $support_patterns = []; my $support_interface_patterns = []; { @@ -908,10 +909,7 @@ sub generate_support_material { push @angles, $angles[0] + 90; } - my $filler = Slic3r::Fill->filler($pattern); - $filler->bounding_box([ Slic3r::Geometry::bounding_box([ map @$_, map @$_, @areas ]) ]) - if $filler->can('bounding_box'); - + my $filler = $fill->filler($pattern); my $make_pattern = sub { my ($expolygon, $density) = @_; @@ -996,7 +994,7 @@ sub generate_support_material { # make a solid base on bottom layer if ($layer_id == 0) { - my $filler = Slic3r::Fill->filler('rectilinear'); + my $filler = $fill->filler('rectilinear'); $filler->angle($Slic3r::Config->support_material_angle + 90); foreach my $expolygon (@$islands) { my @paths = $filler->fill_surface( From 850690cf790eec5282844b6d8521f610a62d40ef Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 18 Apr 2013 17:36:06 +0200 Subject: [PATCH 21/22] Minor optimization --- lib/Slic3r/Fill/Base.pm | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/Slic3r/Fill/Base.pm b/lib/Slic3r/Fill/Base.pm index c9f801f59..adc1e74c6 100644 --- a/lib/Slic3r/Fill/Base.pm +++ b/lib/Slic3r/Fill/Base.pm @@ -39,22 +39,20 @@ sub infill_direction { sub rotate_points { my $self = shift; my ($expolygon, $rotate_vector) = @_; - my @rotate = @{$rotate_vector->[0]}; - my @shift = @{$rotate_vector->[1]}; # rotate points - $expolygon->rotate(@rotate); - $expolygon->translate(@shift); + $expolygon->rotate(@{$rotate_vector->[0]}); + $expolygon->translate(@{$rotate_vector->[1]}); } sub rotate_points_back { my $self = shift; my ($paths, $rotate_vector) = @_; - my @rotate = @{$rotate_vector->[0]}; - my @shift = @{$rotate_vector->[1]}; + my @rotate = (-$rotate_vector->[0][0], $rotate_vector->[0][1]); + my $shift = [ map -$_, @{$rotate_vector->[1]} ]; - @$paths = map [ Slic3r::Geometry::rotate_points(-$rotate[0], $rotate[1], @$_) ], - map [ Slic3r::Geometry::move_points([map -$_, @shift], @$_) ], @$paths; + @$paths = map [ Slic3r::Geometry::rotate_points(@rotate, @$_) ], + map [ Slic3r::Geometry::move_points($shift, @$_) ], @$paths; } sub adjust_solid_spacing { From 01bd9040f54aa05bff04df36f2c4c4e6a3460603 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Thu, 18 Apr 2013 17:46:13 +0200 Subject: [PATCH 22/22] Fix syntax for ex_int_offset2() --- lib/Slic3r/Geometry/Clipper.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Slic3r/Geometry/Clipper.pm b/lib/Slic3r/Geometry/Clipper.pm index bef2713da..0c2cbfe35 100644 --- a/lib/Slic3r/Geometry/Clipper.pm +++ b/lib/Slic3r/Geometry/Clipper.pm @@ -113,8 +113,8 @@ sub ex_int_offset2 { } sub collapse_ex { - my ($polygons, $width) = @_; - return ex_int_offset2($polygons, -$width/2, +$width/2); + my ($polygons, $width) = @_;use XXX; YYY + return [ ex_int_offset2($polygons, -$width/2, +$width/2) ]; } sub simplify_polygon {