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
This commit is contained in:
Michael Moon 2012-03-06 14:55:21 +11:00 committed by Alessandro Ranellucci
parent 7a786844f6
commit f2edfd1a76
7 changed files with 146 additions and 21 deletions

View File

@ -193,8 +193,9 @@ The author is Alessandro Ranellucci (me).
Transform options: Transform options:
--scale Factor for scaling input object (default: 1) --scale Factor for scaling input object (default: 1)
--rotate Rotation angle in degrees (0-360, default: 0) --rotate Rotation angle in degrees (0-360, default: 0)
--duplicate-x Number of items along X axis (1+, default: 1) --duplicate Number of items with auto-arrange (1+, default: 1)
--duplicate-y Number of items along Y axis (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) --duplicate-distance Distance in mm between copies (default: 6)
Miscellaneous options: Miscellaneous options:

View File

@ -137,6 +137,7 @@ our $skirt_height = 1; # layers
# transform options # transform options
our $scale = 1; our $scale = 1;
our $rotate = 0; our $rotate = 0;
our $duplicate = 1;
our $duplicate_x = 1; our $duplicate_x = 1;
our $duplicate_y = 1; our $duplicate_y = 1;
our $duplicate_distance = 6; # mm our $duplicate_distance = 6; # mm

View File

@ -10,7 +10,7 @@ our $Options = {
# miscellaneous options # miscellaneous options
'notes' => { 'notes' => {
label => 'Configuration notes', label => 'Configuration notes',
cli => 'notes=s', cli => 'notes=s',
type => 's', type => 's',
multiline => 1, multiline => 1,
width => 350, width => 350,
@ -392,6 +392,11 @@ our $Options = {
cli => 'rotate=i', cli => 'rotate=i',
type => 'i', type => 'i',
}, },
'duplicate' => {
label => 'Copies (auto arrange)',
cli => 'duplicate=i',
type => 'i',
},
'duplicate_x' => { 'duplicate_x' => {
label => 'Copies along X', label => 'Copies along X',
cli => 'duplicate-x=i', cli => 'duplicate-x=i',
@ -599,6 +604,10 @@ sub validate {
die "Invalid value for --scale\n" die "Invalid value for --scale\n"
if $Slic3r::scale <= 0; if $Slic3r::scale <= 0;
# --duplicate
die "Invalid value for --duplicate\n"
if $Slic3r::duplicate < 1;
# --duplicate-x # --duplicate-x
die "Invalid value for --duplicate-x\n" die "Invalid value for --duplicate-x\n"
if $Slic3r::duplicate_x < 1; if $Slic3r::duplicate_x < 1;
@ -607,6 +616,11 @@ sub validate {
die "Invalid value for --duplicate-y\n" die "Invalid value for --duplicate-y\n"
if $Slic3r::duplicate_y < 1; 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 # --duplicate-distance
die "Invalid value for --duplicate-distance\n" die "Invalid value for --duplicate-distance\n"
if $Slic3r::duplicate_distance < 1; if $Slic3r::duplicate_distance < 1;
@ -619,8 +633,8 @@ sub validate {
die "Invalid value for --bridge-flow-ratio\n" die "Invalid value for --bridge-flow-ratio\n"
if $Slic3r::bridge_flow_ratio <= 0; if $Slic3r::bridge_flow_ratio <= 0;
$Slic3r::first_layer_temperature //= $Slic3r::temperature; #/ $Slic3r::first_layer_temperature //= $Slic3r::temperature; #/
$Slic3r::first_layer_bed_temperature //= $Slic3r::bed_temperature; #/ $Slic3r::first_layer_bed_temperature //= $Slic3r::bed_temperature; #/
# G-code flavors # G-code flavors
$Slic3r::extrusion_axis = 'A' if $Slic3r::gcode_flavor eq 'mach3'; $Slic3r::extrusion_axis = 'A' if $Slic3r::gcode_flavor eq 'mach3';

View File

@ -60,7 +60,7 @@ sub new {
}, },
transform => { transform => {
title => '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 => { gcode => {
title => 'Custom G-code', title => 'Custom G-code',

View File

@ -124,17 +124,125 @@ sub BUILD {
my $self = shift; my $self = shift;
my $dist = scale $Slic3r::duplicate_distance; 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 if ($Slic3r::duplicate_x > 1 || $Slic3r::duplicate_y > 1) {
for my $x_copy (1..$Slic3r::duplicate_x) { $self->total_x_length($self->x_length * $Slic3r::duplicate_x + $dist * ($Slic3r::duplicate_x - 1));
for my $y_copy (1..$Slic3r::duplicate_y) { $self->total_y_length($self->y_length * $Slic3r::duplicate_y + $dist * ($Slic3r::duplicate_y - 1));
push @{$self->copies}, [
($self->x_length + scale $Slic3r::duplicate_distance) * ($x_copy-1), # generate offsets for copies
($self->y_length + scale $Slic3r::duplicate_distance) * ($y_copy-1), 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];
} }
} }

View File

@ -191,9 +191,9 @@ EOF
} }
print $fh <<"EOF"; print $fh <<"EOF";
<!-- <!--
Generated using Slic3r $Slic3r::VERSION Generated using Slic3r $Slic3r::VERSION
http://slic3r.org/ http://slic3r.org/
--> -->
</svg> </svg>
EOF EOF

View File

@ -216,8 +216,9 @@ Usage: slic3r.pl [ OPTIONS ] file.stl
Transform options: Transform options:
--scale Factor for scaling input object (default: $Slic3r::scale) --scale Factor for scaling input object (default: $Slic3r::scale)
--rotate Rotation angle in degrees (0-360, default: $Slic3r::rotate) --rotate Rotation angle in degrees (0-360, default: $Slic3r::rotate)
--duplicate-x Number of items along X axis (1+, default: $Slic3r::duplicate_x) --duplicate Number of items with auto-arrange (1+, default: $Slic3r::duplicate)
--duplicate-y Number of items along Y axis (1+, default: $Slic3r::duplicate_y) --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) --duplicate-distance Distance in mm between copies (default: $Slic3r::duplicate_distance)
Miscellaneous options: Miscellaneous options: