diff --git a/README.markdown b/README.markdown index 6d4285087..873b57333 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 7a372acca..fda8e4497 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -138,6 +138,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 1e7c3b3cd..f371cb1a5 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 1bd1799f2..a51dc78d2 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 07a56fa8e..e6460ff53 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -157,17 +157,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 a84ccc7ce..879495956 100644 --- a/lib/Slic3r/Skein.pm +++ b/lib/Slic3r/Skein.pm @@ -190,9 +190,9 @@ EOF } print $fh <<"EOF"; - EOF diff --git a/slic3r.pl b/slic3r.pl index 820f23f65..838f0237e 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: