From f2edfd1a765fc3c9d06d069505a224f3428dc207 Mon Sep 17 00:00:00 2001 From: Michael Moon Date: Tue, 6 Mar 2012 14:55:21 +1100 Subject: [PATCH] initial implementation of algorithm for #249 fix typo that put things in the wrong position use int() builtin instead of POSIX::floor() fix typo use alternate method of creating local routines remove aliases for new duplicate option use coderefs for linear interpolate function, make binary insertion sort inline add \n at end of die message regarding too many objects for print area fix case where no duplication is done fix whitespace according to slic3r coding style assume 200x200 bed area if center is 0,0 Some cleanup to the autoarrange duplication logic --- README.markdown | 5 +- lib/Slic3r.pm | 1 + lib/Slic3r/Config.pm | 20 +++++- lib/Slic3r/GUI/SkeinPanel.pm | 2 +- lib/Slic3r/Print.pm | 128 ++++++++++++++++++++++++++++++++--- lib/Slic3r/Skein.pm | 6 +- slic3r.pl | 5 +- 7 files changed, 146 insertions(+), 21 deletions(-) diff --git a/README.markdown b/README.markdown index 6d428508..873b5733 100644 --- a/README.markdown +++ b/README.markdown @@ -193,8 +193,9 @@ The author is Alessandro Ranellucci (me). Transform options: --scale Factor for scaling input object (default: 1) --rotate Rotation angle in degrees (0-360, default: 0) - --duplicate-x Number of items along X axis (1+, default: 1) - --duplicate-y Number of items along Y axis (1+, default: 1) + --duplicate Number of items with auto-arrange (1+, default: 1) + --duplicate-x Number of items along X axis for manual arrangement (1+, default: 1) + --duplicate-y Number of items along Y axis for manual arrangement (1+, default: 1) --duplicate-distance Distance in mm between copies (default: 6) Miscellaneous options: diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index 5c5e3b06..1bc687ac 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -137,6 +137,7 @@ our $skirt_height = 1; # layers # transform options our $scale = 1; our $rotate = 0; +our $duplicate = 1; our $duplicate_x = 1; our $duplicate_y = 1; our $duplicate_distance = 6; # mm diff --git a/lib/Slic3r/Config.pm b/lib/Slic3r/Config.pm index b879777a..ce2ec498 100644 --- a/lib/Slic3r/Config.pm +++ b/lib/Slic3r/Config.pm @@ -10,7 +10,7 @@ our $Options = { # miscellaneous options 'notes' => { label => 'Configuration notes', - cli => 'notes=s', + cli => 'notes=s', type => 's', multiline => 1, width => 350, @@ -392,6 +392,11 @@ our $Options = { cli => 'rotate=i', type => 'i', }, + 'duplicate' => { + label => 'Copies (auto arrange)', + cli => 'duplicate=i', + type => 'i', + }, 'duplicate_x' => { label => 'Copies along X', cli => 'duplicate-x=i', @@ -599,6 +604,10 @@ sub validate { die "Invalid value for --scale\n" if $Slic3r::scale <= 0; + # --duplicate + die "Invalid value for --duplicate\n" + if $Slic3r::duplicate < 1; + # --duplicate-x die "Invalid value for --duplicate-x\n" if $Slic3r::duplicate_x < 1; @@ -606,6 +615,11 @@ sub validate { # --duplicate-y die "Invalid value for --duplicate-y\n" if $Slic3r::duplicate_y < 1; + + # reflect actual quantity in 'duplicate' setting for use with output-filename-format, ie both --duplicate 15 and --duplicate-x 3 --duplicate-y 5 will make an appropriate filename + if ($Slic3r::duplicate == 1 && (($Slic3r::duplicate_x > 1) || ($Slic3r::duplicate_y > 1))) { + $Slic3r::duplicate = $Slic3r::duplicate_x * $Slic3r::duplicate_y; + } # --duplicate-distance die "Invalid value for --duplicate-distance\n" @@ -619,8 +633,8 @@ sub validate { die "Invalid value for --bridge-flow-ratio\n" if $Slic3r::bridge_flow_ratio <= 0; - $Slic3r::first_layer_temperature //= $Slic3r::temperature; #/ - $Slic3r::first_layer_bed_temperature //= $Slic3r::bed_temperature; #/ + $Slic3r::first_layer_temperature //= $Slic3r::temperature; #/ + $Slic3r::first_layer_bed_temperature //= $Slic3r::bed_temperature; #/ # G-code flavors $Slic3r::extrusion_axis = 'A' if $Slic3r::gcode_flavor eq 'mach3'; diff --git a/lib/Slic3r/GUI/SkeinPanel.pm b/lib/Slic3r/GUI/SkeinPanel.pm index 15232c2a..26cc9031 100644 --- a/lib/Slic3r/GUI/SkeinPanel.pm +++ b/lib/Slic3r/GUI/SkeinPanel.pm @@ -60,7 +60,7 @@ sub new { }, transform => { title => 'Transform', - options => [qw(scale rotate duplicate_x duplicate_y duplicate_distance)], + options => [qw(scale rotate duplicate duplicate_x duplicate_y duplicate_distance)], }, gcode => { title => 'Custom G-code', diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 23623e5e..b1fe9302 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -124,17 +124,125 @@ sub BUILD { my $self = shift; my $dist = scale $Slic3r::duplicate_distance; - $self->total_x_length($self->x_length * $Slic3r::duplicate_x + $dist * ($Slic3r::duplicate_x - 1)); - $self->total_y_length($self->y_length * $Slic3r::duplicate_y + $dist * ($Slic3r::duplicate_y - 1)); - - # generate offsets for copies - for my $x_copy (1..$Slic3r::duplicate_x) { - for my $y_copy (1..$Slic3r::duplicate_y) { - push @{$self->copies}, [ - ($self->x_length + scale $Slic3r::duplicate_distance) * ($x_copy-1), - ($self->y_length + scale $Slic3r::duplicate_distance) * ($y_copy-1), - ]; + + if ($Slic3r::duplicate_x > 1 || $Slic3r::duplicate_y > 1) { + $self->total_x_length($self->x_length * $Slic3r::duplicate_x + $dist * ($Slic3r::duplicate_x - 1)); + $self->total_y_length($self->y_length * $Slic3r::duplicate_y + $dist * ($Slic3r::duplicate_y - 1)); + + # generate offsets for copies + for my $x_copy (1..$Slic3r::duplicate_x) { + for my $y_copy (1..$Slic3r::duplicate_y) { + push @{$self->copies}, [ + ($self->x_length + $dist) * ($x_copy-1), + ($self->y_length + $dist) * ($y_copy-1), + ]; + } } + } elsif ($Slic3r::duplicate > 1) { + my $linint = sub { + my ($value, $oldmin, $oldmax, $newmin, $newmax) = @_; + return ($value - $oldmin) * ($newmax - $newmin) / ($oldmax - $oldmin) + $newmin; + }; + + # use center location to determine print area. assume X200 Y200 if center is 0,0 + # TODO: add user configuration for bed area with new gui + my $printx = $Slic3r::print_center->[X] * 2 || 200; + my $printy = $Slic3r::print_center->[Y] * 2 || 200; + + # use actual part size plus separation distance (half on each side) in spacing algorithm + my $partx = unscale($self->x_length) + $Slic3r::duplicate_distance; + my $party = unscale($self->y_length) + $Slic3r::duplicate_distance; + + # this is how many cells we have available into which to put parts + my $cellw = int($printx / $partx); + my $cellh = int($printy / $party); + die "$Slic3r::duplicate parts won't fit in your print area!\n" if $Slic3r::duplicate > ($cellw * $cellh); + + # width and height of space used by cells + my $w = $cellw * $partx; + my $h = $cellh * $party; + + # left and right border positions of space used by cells + my $l = ($printx - $w) / 2; + my $r = $l + $w; + + # top and bottom border positions + my $t = ($printy - $h) / 2; + my $b = $t + $h; + + # list of cells, sorted by distance from center + my @cellsorder; + + # work out distance for all cells, sort into list + for my $i (0..$cellw-1) { + for my $j (0..$cellh-1) { + my $cx = $linint->($i + 0.5, 0, $cellw, $l, $r); + my $cy = $linint->($j + 0.5, 0, $cellh, $t, $b); + + my $xd = abs(($printx / 2) - $cx); + my $yd = abs(($printy / 2) - $cy); + + my $c = { + location => [$cx, $cy], + index => [$i, $j], + distance => $xd * $xd + $yd * $yd - abs(($cellw / 2) - ($i + 0.5)), + }; + + BINARYINSERTIONSORT: { + my $index = $c->{distance}; + my $low = 0; + my $high = @cellsorder; + while ($low < $high) { + my $mid = ($low + (($high - $low) / 2)) | 0; + my $midval = $cellsorder[$mid]->[0]; + + if ($midval < $index) { + $low = $mid + 1; + } elsif ($midval > $index) { + $high = $mid; + } else { + splice @cellsorder, $mid, 0, [$index, $c]; + last BINARYINSERTIONSORT; + } + } + splice @cellsorder, $low, 0, [$index, $c]; + } + } + } + + # the extents of cells actually used by objects + my ($lx, $ty, $rx, $by) = (0, 0, 0, 0); + + # now find cells actually used by objects, map out the extents so we can position correctly + for my $i (1..$Slic3r::duplicate) { + my $c = $cellsorder[$i - 1]; + my $cx = $c->[1]->{index}->[0]; + my $cy = $c->[1]->{index}->[1]; + if ($i == 1) { + $lx = $rx = $cx; + $ty = $by = $cy; + } else { + $rx = $cx if $cx > $rx; + $lx = $cx if $cx < $lx; + $by = $cy if $cy > $by; + $ty = $cy if $cy < $ty; + } + } + # now we actually place objects into cells, positioned such that the left and bottom borders are at 0 + for my $i (1..$Slic3r::duplicate) { + my $c = shift @cellsorder; + my $cx = $c->[1]->{index}->[0] - $lx; + my $cy = $c->[1]->{index}->[1] - $ty; + + push @{$self->copies}, [scale($cx * $partx - (unscale($self->x_length) / 2)), scale($cy * $party - (unscale($self->y_length) / 2))]; + } + # save size of area used + $self->total_x_length(scale(($rx - $lx) * $partx)); + $self->total_y_length(scale(($by - $ty) * $party)); + } else { + $self->total_x_length($self->x_length); + $self->total_y_length($self->y_length); + push @{$self->copies}, [0, 0]; } } diff --git a/lib/Slic3r/Skein.pm b/lib/Slic3r/Skein.pm index 4930d716..cacc6f99 100644 --- a/lib/Slic3r/Skein.pm +++ b/lib/Slic3r/Skein.pm @@ -191,9 +191,9 @@ EOF } print $fh <<"EOF"; - EOF diff --git a/slic3r.pl b/slic3r.pl index 820f23f6..838f0237 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -216,8 +216,9 @@ Usage: slic3r.pl [ OPTIONS ] file.stl Transform options: --scale Factor for scaling input object (default: $Slic3r::scale) --rotate Rotation angle in degrees (0-360, default: $Slic3r::rotate) - --duplicate-x Number of items along X axis (1+, default: $Slic3r::duplicate_x) - --duplicate-y Number of items along Y axis (1+, default: $Slic3r::duplicate_y) + --duplicate Number of items with auto-arrange (1+, default: $Slic3r::duplicate) + --duplicate-x Number of items along X axis for manual arrangement (1+, default: $Slic3r::duplicate_x) + --duplicate-y Number of items along Y axis for manual arrangement (1+, default: $Slic3r::duplicate_y) --duplicate-distance Distance in mm between copies (default: $Slic3r::duplicate_distance) Miscellaneous options: