mirror of https://github.com/vitalif/Slic3r
commit
77f453acf8
3
MANIFEST
3
MANIFEST
|
@ -30,6 +30,7 @@ lib/Slic3r/GCode/MotionPlanner.pm
|
|||
lib/Slic3r/GCode/Reader.pm
|
||||
lib/Slic3r/GCode/SpiralVase.pm
|
||||
lib/Slic3r/Geometry.pm
|
||||
lib/Slic3r/Geometry/BoundingBox.pm
|
||||
lib/Slic3r/Geometry/Clipper.pm
|
||||
lib/Slic3r/GUI.pm
|
||||
lib/Slic3r/GUI/AboutDialog.pm
|
||||
|
@ -75,12 +76,14 @@ t/geometry.t
|
|||
t/layers.t
|
||||
t/loops.t
|
||||
t/polyclip.t
|
||||
t/print.t
|
||||
t/retraction.t
|
||||
t/serialize.t
|
||||
t/shells.t
|
||||
t/slice.t
|
||||
t/skirt_brim.t
|
||||
t/support.t
|
||||
t/svg.t
|
||||
t/vibrationlimit.t
|
||||
utils/amf-to-stl.pl
|
||||
utils/file_info.pl
|
||||
|
|
|
@ -7,7 +7,7 @@ A: Yes.
|
|||
## What's it?
|
||||
|
||||
Slic3r is a G-code generator for 3D printers. It's compatible with RepRaps,
|
||||
Makerbots, Ultimakers and many more machines.
|
||||
makerwares, Ultimakers and many more machines.
|
||||
|
||||
See the [project homepage](http://slic3r.org/) at slic3r.org and the
|
||||
[documentation](https://github.com/alexrj/Slic3r/wiki/Documentation) on the Slic3r wiki for more information.
|
||||
|
@ -112,7 +112,7 @@ The author of the Silk icon set is Mark James.
|
|||
(default: 100,100)
|
||||
--z-offset Additional height in mm to add to vertical coordinates
|
||||
(+/-, default: 0)
|
||||
--gcode-flavor The type of G-code to generate (reprap/teacup/makerbot/sailfish/mach3/no-extrusion,
|
||||
--gcode-flavor The type of G-code to generate (reprap/teacup/makerware/sailfish/mach3/no-extrusion,
|
||||
default: reprap)
|
||||
--use-relative-e-distances Enable this to get relative E values
|
||||
--gcode-arcs Use G2/G3 commands for native arcs (experimental, not supported
|
||||
|
|
|
@ -7,7 +7,7 @@ use strict;
|
|||
use warnings;
|
||||
require v5.10;
|
||||
|
||||
our $VERSION = "0.9.10-dev";
|
||||
our $VERSION = "0.9.11-dev";
|
||||
|
||||
our $debug = 0;
|
||||
sub debugf {
|
||||
|
@ -51,6 +51,8 @@ use Slic3r::GCode::MotionPlanner;
|
|||
use Slic3r::GCode::Reader;
|
||||
use Slic3r::GCode::SpiralVase;
|
||||
use Slic3r::Geometry qw(PI);
|
||||
use Slic3r::Geometry::BoundingBox;
|
||||
use Slic3r::Geometry::Clipper;
|
||||
use Slic3r::Layer;
|
||||
use Slic3r::Layer::Region;
|
||||
use Slic3r::Line;
|
||||
|
|
|
@ -74,8 +74,8 @@ our $Options = {
|
|||
tooltip => 'Some G/M-code commands, including temperature control and others, are not universal. Set this option to your printer\'s firmware to get a compatible output. The "No extrusion" flavor prevents Slic3r from exporting any extrusion value at all.',
|
||||
cli => 'gcode-flavor=s',
|
||||
type => 'select',
|
||||
values => [qw(reprap teacup makerbot sailfish mach3 no-extrusion)],
|
||||
labels => ['RepRap (Marlin/Sprinter)', 'Teacup', 'MakerBot', 'Sailfish', 'Mach3/EMC', 'No extrusion'],
|
||||
values => [qw(reprap teacup makerware sailfish mach3 no-extrusion)],
|
||||
labels => ['RepRap (Marlin/Sprinter/Repetier)', 'Teacup', 'MakerWare (MakerBot)', 'Sailfish (MakerBot)', 'Mach3/EMC', 'No extrusion'],
|
||||
default => 'reprap',
|
||||
},
|
||||
'use_relative_e_distances' => {
|
||||
|
@ -596,7 +596,7 @@ our $Options = {
|
|||
},
|
||||
'only_retract_when_crossing_perimeters' => {
|
||||
label => 'Only retract when crossing perimeters',
|
||||
tooltip => 'Disables retraction when travelling between infill paths inside the same island.',
|
||||
tooltip => 'Disables retraction when the travel path does not exceed the upper layer\'s perimeters (and thus any ooze will be probably invisible).',
|
||||
cli => 'only-retract-when-crossing-perimeters!',
|
||||
type => 'bool',
|
||||
default => 1,
|
||||
|
@ -1141,7 +1141,7 @@ sub set {
|
|||
my ($opt_key, $value, $deserialize) = @_;
|
||||
|
||||
# handle legacy options
|
||||
return if $opt_key ~~ @Ignore;
|
||||
return if first { $_ eq $opt_key } @Ignore;
|
||||
if ($opt_key =~ /^(extrusion_width|bottom_layer_speed|first_layer_height)_ratio$/) {
|
||||
$opt_key = $1;
|
||||
$opt_key =~ s/^bottom_layer_speed$/first_layer_speed/;
|
||||
|
@ -1150,6 +1150,9 @@ sub set {
|
|||
if ($opt_key eq 'threads' && !$Slic3r::have_threads) {
|
||||
$value = 1;
|
||||
}
|
||||
if ($opt_key eq 'gcode_flavor' && $value eq 'makerbot') {
|
||||
$value = 'makerware';
|
||||
}
|
||||
|
||||
# For historical reasons, the world's full of configs having these very low values;
|
||||
# to avoid unexpected behavior we need to ignore them. Banning these two hard-coded
|
||||
|
@ -1271,6 +1274,10 @@ sub validate {
|
|||
die "Invalid value for --top-solid-layers\n" if $self->top_solid_layers < 0;
|
||||
die "Invalid value for --bottom-solid-layers\n" if $self->bottom_solid_layers < 0;
|
||||
|
||||
# --gcode-flavor
|
||||
die "Invalid value for --gcode-flavor\n"
|
||||
if !first { $_ eq $self->gcode_flavor } @{$Options->{gcode_flavor}{values}};
|
||||
|
||||
# --print-center
|
||||
die "Invalid value for --print-center\n"
|
||||
if !ref $self->print_center
|
||||
|
@ -1413,7 +1420,7 @@ sub read_ini {
|
|||
|
||||
my $ini = { _ => {} };
|
||||
my $category = '_';
|
||||
while (my $_ = <$fh>) {
|
||||
while (<$fh>) {
|
||||
s/\R+$//;
|
||||
next if /^\s+/;
|
||||
next if /^$/;
|
||||
|
|
|
@ -335,11 +335,25 @@ sub align_to_origin {
|
|||
|
||||
my @bb = Slic3r::Geometry::bounding_box([ map @$_, map @$_, @{$self->expolygons} ]);
|
||||
$_->translate(-$bb[X1], -$bb[Y1]) for @{$self->expolygons};
|
||||
$self;
|
||||
}
|
||||
|
||||
sub scale {
|
||||
my $self = shift;
|
||||
$_->scale(@_) for @{$self->expolygons};
|
||||
$self;
|
||||
}
|
||||
|
||||
sub rotate {
|
||||
my $self = shift;
|
||||
$_->rotate(@_) for @{$self->expolygons};
|
||||
$self;
|
||||
}
|
||||
|
||||
sub translate {
|
||||
my $self = shift;
|
||||
$_->translate(@_) for @{$self->expolygons};
|
||||
$self;
|
||||
}
|
||||
|
||||
sub size {
|
||||
|
|
|
@ -14,6 +14,7 @@ has 'id' => (is => 'rw', required => 1);
|
|||
has $_ => (is => 'ro', required => 1) for @{&OPTIONS};
|
||||
|
||||
has 'bridge_flow' => (is => 'lazy');
|
||||
has 'e' => (is => 'rw', default => sub {0} );
|
||||
has 'retracted' => (is => 'rw', default => sub {0} );
|
||||
has 'restart_extra' => (is => 'rw', default => sub {0} );
|
||||
has 'e_per_mm3' => (is => 'lazy');
|
||||
|
@ -38,7 +39,9 @@ sub _build_retract_speed_mm_min {
|
|||
|
||||
sub _build_scaled_wipe_distance {
|
||||
my $self = shift;
|
||||
return scale $self->retract_length / $self->retract_speed * $Slic3r::Config->travel_speed;
|
||||
# reduce feedrate a bit; travel speed is often too high to move on existing material
|
||||
# too fast = ripping of existing material; too slow = short wipe path, thus more blob
|
||||
return scale($self->retract_length / $self->retract_speed * $Slic3r::Config->travel_speed * 0.8);
|
||||
}
|
||||
|
||||
sub make_flow {
|
||||
|
|
|
@ -79,13 +79,14 @@ sub fill_surface {
|
|||
$self->cache->{$cache_id} = [@polygons];
|
||||
}
|
||||
|
||||
# build polylines from polygons without re-appending the initial point:
|
||||
# consider polygons as polylines without re-appending the initial point:
|
||||
# this cuts the last segment on purpose, so that the jump to the next
|
||||
# path is more straight
|
||||
my @paths = map Slic3r::Polyline->new(@$_), map @$_, @{intersection_ex(
|
||||
$self->cache->{$cache_id},
|
||||
$expolygon,
|
||||
)};
|
||||
my @paths = map Slic3r::Polyline->new($_),
|
||||
@{ Boost::Geometry::Utils::polygon_multi_linestring_intersection(
|
||||
$expolygon,
|
||||
$self->cache->{$cache_id},
|
||||
) };
|
||||
|
||||
return { flow_spacing => $params{flow_spacing} },
|
||||
Slic3r::Polyline::Collection->new(polylines => \@paths)->chained_path;
|
||||
|
|
|
@ -8,7 +8,7 @@ sub read_file {
|
|||
Slic3r::open(\my $fh, '<', $file) or die "Failed to open $file\n";
|
||||
my $vertices = [];
|
||||
my $facets = [];
|
||||
while (my $_ = <$fh>) {
|
||||
while (<$fh>) {
|
||||
if (/^v ([^ ]+)\s+([^ ]+)\s+([^ ]+)/) {
|
||||
push @$vertices, [$1, $2, $3];
|
||||
} elsif (/^f (\d+).*? (\d+).*? (\d+).*?/) {
|
||||
|
|
|
@ -53,7 +53,7 @@ sub _read_ascii {
|
|||
my $facet;
|
||||
my %vertices_map = ();
|
||||
seek $fh, 0, 0;
|
||||
while (my $_ = <$fh>) {
|
||||
while (<$fh>) {
|
||||
if (!$facet) {
|
||||
/^\s*facet\s+normal\s+/ or next;
|
||||
$facet = []; # ignore normal
|
||||
|
@ -88,7 +88,7 @@ sub _read_binary {
|
|||
my %vertices_map = ();
|
||||
binmode $fh;
|
||||
seek $fh, 80 + 4, 0;
|
||||
while (read $fh, my $_, 4*4*3+2) {
|
||||
while (read $fh, $_, 4*4*3+2) {
|
||||
push @$facets, my $facet = [];
|
||||
for (unpack 'x[f3](a[f3])3') { # ignore normal
|
||||
my $vertex_idx;
|
||||
|
|
|
@ -22,7 +22,6 @@ has 'layer_mp' => (is => 'rw');
|
|||
has 'new_object' => (is => 'rw', default => sub {0});
|
||||
has 'straight_once' => (is => 'rw', default => sub {1});
|
||||
has 'extruder' => (is => 'rw');
|
||||
has 'extrusion_distance' => (is => 'rw', default => sub {0} );
|
||||
has 'elapsed_time' => (is => 'rw', default => sub {0} ); # seconds
|
||||
has 'total_extrusion_length' => (is => 'rw', default => sub {0} );
|
||||
has 'lifted' => (is => 'rw', default => sub {0} );
|
||||
|
@ -90,7 +89,7 @@ sub change_layer {
|
|||
}
|
||||
|
||||
my $gcode = "";
|
||||
if ($self->config->gcode_flavor =~ /^(?:makerbot|sailfish)$/) {
|
||||
if ($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
|
||||
$gcode .= sprintf "M73 P%s%s\n",
|
||||
int(99 * ($layer->id / ($self->layer_count - 1))),
|
||||
($self->config->gcode_comments ? ' ; update progress' : '');
|
||||
|
@ -284,7 +283,7 @@ sub travel_to {
|
|||
$travel->translate(-$self->shift_x, -$self->shift_y);
|
||||
|
||||
if ($travel->length < scale $self->extruder->retract_before_travel
|
||||
|| ($self->config->only_retract_when_crossing_perimeters && first { $_->encloses_line($travel, scaled_epsilon) } @{$self->layer->slices})
|
||||
|| ($self->config->only_retract_when_crossing_perimeters && first { $_->encloses_line($travel, scaled_epsilon) } @{$self->layer->upper_layer_slices})
|
||||
|| ($role == EXTR_ROLE_SUPPORTMATERIAL && $self->layer->support_islands_enclose_line($travel))
|
||||
) {
|
||||
$self->straight_once(0);
|
||||
|
@ -327,7 +326,7 @@ sub _plan {
|
|||
my $need_retract = !$self->config->only_retract_when_crossing_perimeters;
|
||||
if (!$need_retract) {
|
||||
$need_retract = 1;
|
||||
foreach my $slice (@{$self->layer->slices}) {
|
||||
foreach my $slice (@{$self->layer->upper_layer_slices}) {
|
||||
# discard the island if at any line is not enclosed in it
|
||||
next if first { !$slice->encloses_line($_, scaled_epsilon) } @travel;
|
||||
# okay, this island encloses the full travel path
|
||||
|
@ -341,7 +340,8 @@ sub _plan {
|
|||
|
||||
# append the actual path and return
|
||||
$self->speed('travel');
|
||||
$gcode .= join '', map $self->G0($_->[B], undef, 0, $comment || ""), @travel;
|
||||
# use G1 because we rely on paths being straight (G0 may make round paths)
|
||||
$gcode .= join '', map $self->G1($_->[B], undef, 0, $comment || ""), @travel;
|
||||
return $gcode;
|
||||
}
|
||||
|
||||
|
@ -419,7 +419,9 @@ sub retract {
|
|||
|
||||
# reset extrusion distance during retracts
|
||||
# this makes sure we leave sufficient precision in the firmware
|
||||
$gcode .= $self->reset_e if $self->config->gcode_flavor !~ /^(?:mach3|makerbot)$/;
|
||||
$gcode .= $self->reset_e;
|
||||
|
||||
$gcode .= "M103 ; extruder off\n" if $self->config->gcode_flavor eq 'makerware';
|
||||
|
||||
return $gcode;
|
||||
}
|
||||
|
@ -428,6 +430,7 @@ sub unretract {
|
|||
my $self = shift;
|
||||
|
||||
my $gcode = "";
|
||||
$gcode .= "M101 ; extruder on\n" if $self->config->gcode_flavor eq 'makerware';
|
||||
|
||||
if ($self->lifted) {
|
||||
$self->speed('travel');
|
||||
|
@ -438,7 +441,8 @@ sub unretract {
|
|||
my $to_unretract = $self->extruder->retracted + $self->extruder->restart_extra;
|
||||
if ($to_unretract) {
|
||||
$self->speed('retract');
|
||||
$gcode .= $self->G0(undef, undef, $to_unretract, "compensate retraction");
|
||||
# use G1 instead of G0 because G0 will blend the restart with the previous travel move
|
||||
$gcode .= $self->G1(undef, undef, $to_unretract, "compensate retraction");
|
||||
$self->extruder->retracted(0);
|
||||
$self->extruder->restart_extra(0);
|
||||
}
|
||||
|
@ -448,8 +452,9 @@ sub unretract {
|
|||
|
||||
sub reset_e {
|
||||
my $self = shift;
|
||||
return "" if $self->config->gcode_flavor =~ /^(?:mach3|makerware)$/;
|
||||
|
||||
$self->extrusion_distance(0);
|
||||
$self->extruder->e(0) if $self->extruder;
|
||||
return sprintf "G92 %s0%s\n", $self->config->extrusion_axis, ($self->config->gcode_comments ? ' ; reset extrusion distance' : '')
|
||||
if $self->config->extrusion_axis && !$self->config->use_relative_e_distances;
|
||||
}
|
||||
|
@ -546,10 +551,10 @@ sub _Gx {
|
|||
|
||||
# output extrusion distance
|
||||
if ($e && $self->config->extrusion_axis) {
|
||||
$self->extrusion_distance(0) if $self->config->use_relative_e_distances;
|
||||
$self->extrusion_distance($self->extrusion_distance + $e);
|
||||
$self->extruder->e(0) if $self->config->use_relative_e_distances;
|
||||
$self->extruder->e($self->extruder->e + $e);
|
||||
$self->total_extrusion_length($self->total_extrusion_length + $e);
|
||||
$gcode .= sprintf " %s%.5f", $self->config->extrusion_axis, $self->extrusion_distance;
|
||||
$gcode .= sprintf " %s%.5f", $self->config->extrusion_axis, $self->extruder->e;
|
||||
}
|
||||
|
||||
$gcode .= sprintf " ; %s", $comment if $comment && $self->config->gcode_comments;
|
||||
|
@ -586,18 +591,16 @@ sub set_extruder {
|
|||
|
||||
# set the new extruder
|
||||
$self->extruder($extruder);
|
||||
my $toolchange_gcode = sprintf "%s%d%s\n",
|
||||
($self->config->gcode_flavor =~ /^(?:makerbot|sailfish)$/ ? 'M108 T' : 'T'),
|
||||
$gcode .= sprintf "%s%d%s\n",
|
||||
($self->config->gcode_flavor eq 'makerware'
|
||||
? 'M135 T'
|
||||
: $self->config->gcode_flavor eq 'sailfish'
|
||||
? 'M108 T'
|
||||
: 'T'),
|
||||
$extruder->id,
|
||||
($self->config->gcode_comments ? ' ; change extruder' : '');
|
||||
|
||||
if ($self->config->gcode_flavor =~ /^(?:makerbot|sailfish)$/) {
|
||||
$gcode .= $self->reset_e;
|
||||
$gcode .= $toolchange_gcode;
|
||||
} else {
|
||||
$gcode .= $toolchange_gcode;
|
||||
$gcode .= $self->reset_e;
|
||||
}
|
||||
$gcode .= $self->reset_e;
|
||||
|
||||
return $gcode;
|
||||
}
|
||||
|
@ -611,12 +614,12 @@ sub set_fan {
|
|||
if ($speed == 0) {
|
||||
my $code = $self->config->gcode_flavor eq 'teacup'
|
||||
? 'M106 S0'
|
||||
: $self->config->gcode_flavor =~ /^(?:makerbot|sailfish)$/
|
||||
: $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/
|
||||
? 'M127'
|
||||
: 'M107';
|
||||
return sprintf "$code%s\n", ($self->config->gcode_comments ? ' ; disable fan' : '');
|
||||
} else {
|
||||
if ($self->config->gcode_flavor =~ /^(?:makerbot|sailfish)$/) {
|
||||
if ($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/) {
|
||||
return sprintf "M126%s\n", ($self->config->gcode_comments ? ' ; enable fan' : '');
|
||||
} else {
|
||||
return sprintf "M106 %s%d%s\n", ($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'),
|
||||
|
@ -631,14 +634,14 @@ sub set_temperature {
|
|||
my $self = shift;
|
||||
my ($temperature, $wait, $tool) = @_;
|
||||
|
||||
return "" if $wait && $self->config->gcode_flavor =~ /^(?:makerbot|sailfish)$/;
|
||||
return "" if $wait && $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/;
|
||||
|
||||
my ($code, $comment) = ($wait && $self->config->gcode_flavor ne 'teacup')
|
||||
? ('M109', 'wait for temperature to be reached')
|
||||
: ('M104', 'set temperature');
|
||||
my $gcode = sprintf "$code %s%d %s; $comment\n",
|
||||
($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature,
|
||||
(defined $tool && ($self->multiple_extruders || $self->config->gcode_flavor =~ /^(?:makerbot|sailfish)$/)) ? "T$tool " : "";
|
||||
(defined $tool && ($self->multiple_extruders || $self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/)) ? "T$tool " : "";
|
||||
|
||||
$gcode .= "M116 ; wait for temperature to be reached\n"
|
||||
if $self->config->gcode_flavor eq 'teacup' && $wait;
|
||||
|
@ -651,7 +654,7 @@ sub set_bed_temperature {
|
|||
my ($temperature, $wait) = @_;
|
||||
|
||||
my ($code, $comment) = ($wait && $self->config->gcode_flavor ne 'teacup')
|
||||
? (($self->config->gcode_flavor =~ /^(?:makerbot|sailfish)$/ ? 'M109' : 'M190'), 'wait for bed temperature to be reached')
|
||||
? (($self->config->gcode_flavor =~ /^(?:makerware|sailfish)$/ ? 'M109' : 'M190'), 'wait for bed temperature to be reached')
|
||||
: ('M140', 'set bed temperature');
|
||||
my $gcode = sprintf "$code %s%d ; $comment\n",
|
||||
($self->config->gcode_flavor eq 'mach3' ? 'P' : 'S'), $temperature;
|
||||
|
|
|
@ -18,8 +18,6 @@ sub append {
|
|||
my $self = shift;
|
||||
my ($gcode, $obj_id, $layer_id, $print_z) = @_;
|
||||
|
||||
# TODO: differentiate $obj_id between normal layers and support layers
|
||||
|
||||
my $return = "";
|
||||
if (exists $self->last_z->{$obj_id} && $self->last_z->{$obj_id} != $print_z) {
|
||||
$return = $self->flush;
|
||||
|
|
|
@ -253,6 +253,7 @@ sub warning_catcher {
|
|||
my ($self, $message_dialog) = @_;
|
||||
return sub {
|
||||
my $message = shift;
|
||||
return if $message =~ /GLUquadricObjPtr|Attempt to free unreferenced scalar/;
|
||||
my @params = ($message, 'Warning', wxOK | wxICON_WARNING);
|
||||
$message_dialog
|
||||
? $message_dialog->(@params)
|
||||
|
|
|
@ -159,6 +159,7 @@ sub new {
|
|||
EVT_COMMAND($self, -1, $THUMBNAIL_DONE_EVENT, sub {
|
||||
my ($self, $event) = @_;
|
||||
my ($obj_idx, $thumbnail) = @{$event->GetData};
|
||||
return if !$self->{objects}[$obj_idx]; # object was deleted before thumbnail generation completed
|
||||
$self->{objects}[$obj_idx]->thumbnail($thumbnail->clone);
|
||||
$self->on_thumbnail_made($obj_idx);
|
||||
});
|
||||
|
@ -325,7 +326,7 @@ sub load_file {
|
|||
$object->check_manifoldness;
|
||||
|
||||
# we only consider the rotation of the first instance for now
|
||||
$object->set_rotation($model->objects->[$i]->instances->[0]->rotation)
|
||||
$object->rotate($model->objects->[$i]->instances->[0]->rotation)
|
||||
if $model->objects->[$i]->instances;
|
||||
|
||||
push @{ $self->{objects} }, $object;
|
||||
|
@ -424,9 +425,10 @@ sub rotate {
|
|||
if (!defined $angle) {
|
||||
$angle = Wx::GetNumberFromUser("", "Enter the rotation angle:", "Rotate", $object->rotate, -364, 364, $self);
|
||||
return if !$angle || $angle == -1;
|
||||
$angle = 0 - $angle; # rotate clockwise (be consistent with button icon)
|
||||
}
|
||||
|
||||
$object->set_rotation($object->rotate + $angle);
|
||||
$object->rotate($object->rotate + $angle);
|
||||
$self->recenter;
|
||||
$self->{canvas}->Refresh;
|
||||
}
|
||||
|
@ -436,12 +438,15 @@ sub changescale {
|
|||
|
||||
my ($obj_idx, $object) = $self->selected_object;
|
||||
|
||||
# we need thumbnail to be computed before allowing scaling
|
||||
return if !$object->thumbnail;
|
||||
|
||||
# max scale factor should be above 2540 to allow importing files exported in inches
|
||||
my $scale = Wx::GetNumberFromUser("", "Enter the scale % for the selected object:", "Scale", $object->scale*100, 0, 100000, $self);
|
||||
return if !$scale || $scale == -1;
|
||||
|
||||
$self->{list}->SetItem($obj_idx, 2, "$scale%");
|
||||
$object->set_scale($scale / 100);
|
||||
$object->scale($scale / 100);
|
||||
$self->arrange;
|
||||
}
|
||||
|
||||
|
@ -451,7 +456,7 @@ sub arrange {
|
|||
my $total_parts = sum(map $_->instances_count, @{$self->{objects}}) or return;
|
||||
my @size = ();
|
||||
for my $a (X,Y) {
|
||||
$size[$a] = max(map $_->size->[$a], @{$self->{objects}});
|
||||
$size[$a] = max(map $_->transformed_size->[$a], @{$self->{objects}});
|
||||
}
|
||||
|
||||
eval {
|
||||
|
@ -699,11 +704,6 @@ sub make_model {
|
|||
foreach my $plater_object (@{$self->{objects}}) {
|
||||
my $model_object = $plater_object->get_model_object;
|
||||
|
||||
# if we need to alter the mesh, clone it first
|
||||
if ($plater_object->scale != 1) {
|
||||
$model_object = $model_object->clone;
|
||||
}
|
||||
|
||||
my $new_model_object = $model->add_object(
|
||||
vertices => $model_object->vertices,
|
||||
input_file => $plater_object->input_file,
|
||||
|
@ -716,9 +716,10 @@ sub make_model {
|
|||
);
|
||||
$model->set_material($volume->material_id || 0, {});
|
||||
}
|
||||
$new_model_object->scale($plater_object->scale);
|
||||
$new_model_object->align_to_origin;
|
||||
$new_model_object->add_instance(
|
||||
rotation => $plater_object->rotate, # around center point
|
||||
scaling_factor => $plater_object->scale,
|
||||
offset => Slic3r::Point->new($_),
|
||||
) for @{$plater_object->instances};
|
||||
}
|
||||
|
@ -767,15 +768,17 @@ sub recenter {
|
|||
my @print_bb = Slic3r::Geometry::bounding_box([
|
||||
map {
|
||||
my $obj = $_;
|
||||
map {
|
||||
my $instance = $_;
|
||||
$instance, [ map $instance->[$_] + $obj->size->[$_], X,Y ];
|
||||
} @{$obj->instances};
|
||||
my $bb = $obj->transformed_bounding_box;
|
||||
my @points = ($bb->min_point, $bb->max_point);
|
||||
map Slic3r::Geometry::move_points($_, @points), @{$obj->instances};
|
||||
} @{$self->{objects}},
|
||||
]);
|
||||
|
||||
# $self->{shift} contains the offset in pixels to add to object instances in order to center them
|
||||
# it is expressed in upwards Y
|
||||
$self->{shift} = [
|
||||
($self->{canvas}->GetSize->GetWidth - $self->to_pixel($print_bb[X2] + $print_bb[X1])) / 2,
|
||||
($self->{canvas}->GetSize->GetHeight - $self->to_pixel($print_bb[Y2] + $print_bb[Y1])) / 2,
|
||||
$self->to_pixel(-$print_bb[X1]) + ($self->{canvas}->GetSize->GetWidth - $self->to_pixel($print_bb[X2] - $print_bb[X1])) / 2,
|
||||
$self->to_pixel(-$print_bb[Y1]) + ($self->{canvas}->GetSize->GetHeight - $self->to_pixel($print_bb[Y2] - $print_bb[Y1])) / 2,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -809,10 +812,10 @@ sub _update_bed_size {
|
|||
|
||||
# supposing the preview canvas is square, calculate the scaling factor
|
||||
# to constrain print bed area inside preview
|
||||
my $bed_size = $self->{config}->bed_size;
|
||||
my $canvas_side = CANVAS_SIZE->[X]; # when the canvas is not rendered yet, its GetSize() method returns 0,0
|
||||
my $bed_largest_side = $bed_size->[X] > $bed_size->[Y] ? $bed_size->[X] : $bed_size->[Y];
|
||||
$self->{scaling_factor} = $canvas_side / $bed_largest_side;
|
||||
# when the canvas is not rendered yet, its GetSize() method returns 0,0
|
||||
$self->{scaling_factor} = CANVAS_SIZE->[X] / max(@{ $self->{config}->bed_size });
|
||||
$_->thumbnail_scaling_factor($self->{scaling_factor}) for @{ $self->{objects} };
|
||||
$self->recenter;
|
||||
}
|
||||
|
||||
# this is called on the canvas
|
||||
|
@ -865,9 +868,12 @@ sub repaint {
|
|||
next unless $object->thumbnail && @{$object->thumbnail->expolygons};
|
||||
for my $instance_idx (0 .. $#{$object->instances}) {
|
||||
my $instance = $object->instances->[$instance_idx];
|
||||
push @{$parent->{object_previews}}, [ $obj_idx, $instance_idx, $object->thumbnail->clone ];
|
||||
$_->translate(map $parent->to_pixel($instance->[$_]) + $parent->{shift}[$_], (X,Y))
|
||||
for @{$parent->{object_previews}->[-1][2]->expolygons};
|
||||
|
||||
my $thumbnail = $object->transformed_thumbnail
|
||||
->clone
|
||||
->translate(map $parent->to_pixel($instance->[$_]) + $parent->{shift}[$_], (X,Y));
|
||||
|
||||
push @{$parent->{object_previews}}, [ $obj_idx, $instance_idx, $thumbnail ];
|
||||
|
||||
my $drag_object = $self->{drag_object};
|
||||
if (defined $drag_object && $obj_idx == $drag_object->[0] && $instance_idx == $drag_object->[1]) {
|
||||
|
@ -939,7 +945,6 @@ sub mouse_event {
|
|||
my ($obj_idx, $instance_idx, $thumbnail) = @$preview;
|
||||
my $instance = $parent->{objects}[$obj_idx]->instances->[$instance_idx];
|
||||
$instance->[$_] = $parent->to_units($pos->[$_] - $self->{drag_start_pos}[$_] - $parent->{shift}[$_]) for X,Y;
|
||||
$instance = $parent->_y([$instance])->[0];
|
||||
$parent->Refresh;
|
||||
}
|
||||
} elsif ($event->Moving) {
|
||||
|
@ -1062,19 +1067,21 @@ sub OnDropFiles {
|
|||
package Slic3r::GUI::Plater::Object;
|
||||
use Moo;
|
||||
|
||||
use Math::ConvexHull::MonotoneChain qw(convex_hull);
|
||||
use Slic3r::Geometry qw(X Y Z);
|
||||
use Math::ConvexHull::MonotoneChain qw();
|
||||
use Slic3r::Geometry qw(X Y Z MIN MAX deg2rad);
|
||||
|
||||
has 'name' => (is => 'rw', required => 1);
|
||||
has 'input_file' => (is => 'rw', required => 1);
|
||||
has 'input_file_object_id' => (is => 'rw'); # undef means keep model object
|
||||
has 'model_object' => (is => 'rw', required => 1, trigger => 1);
|
||||
has 'size' => (is => 'rw');
|
||||
has 'scale' => (is => 'rw', default => sub { 1 });
|
||||
has 'rotate' => (is => 'rw', default => sub { 0 });
|
||||
has 'bounding_box' => (is => 'rw'); # 3D bb of original object (aligned to origin) with no rotation or scaling
|
||||
has 'convex_hull' => (is => 'rw'); # 2D convex hull of original object (aligned to origin) with no rotation or scaling
|
||||
has 'scale' => (is => 'rw', default => sub { 1 }, trigger => \&_transform_thumbnail);
|
||||
has 'rotate' => (is => 'rw', default => sub { 0 }, trigger => \&_transform_thumbnail); # around object center point
|
||||
has 'instances' => (is => 'rw', default => sub { [] }); # upward Y axis
|
||||
has 'thumbnail' => (is => 'rw');
|
||||
has 'thumbnail_scaling_factor' => (is => 'rw');
|
||||
has 'thumbnail' => (is => 'rw', trigger => \&_transform_thumbnail);
|
||||
has 'transformed_thumbnail' => (is => 'rw');
|
||||
has 'thumbnail_scaling_factor' => (is => 'rw', trigger => \&_transform_thumbnail);
|
||||
has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ]
|
||||
|
||||
# statistics
|
||||
|
@ -1086,10 +1093,14 @@ has 'is_manifold' => (is => 'rw');
|
|||
sub _trigger_model_object {
|
||||
my $self = shift;
|
||||
if ($self->model_object) {
|
||||
$self->model_object->align_to_origin;
|
||||
$self->bounding_box($self->model_object->bounding_box);
|
||||
|
||||
my $mesh = $self->model_object->mesh;
|
||||
$self->size([$mesh->size]);
|
||||
$self->convex_hull(Slic3r::Polygon->new(Math::ConvexHull::MonotoneChain::convex_hull($mesh->used_vertices)));
|
||||
$self->facets(scalar @{$mesh->facets});
|
||||
$self->vertices(scalar @{$mesh->vertices});
|
||||
|
||||
$self->materials($self->model_object->materials_count);
|
||||
}
|
||||
}
|
||||
|
@ -1128,54 +1139,58 @@ sub instances_count {
|
|||
sub make_thumbnail {
|
||||
my $self = shift;
|
||||
|
||||
my $mesh = $self->model_object->mesh;
|
||||
my $mesh = $self->model_object->mesh; # $self->model_object is already aligned to origin
|
||||
my $thumbnail = Slic3r::ExPolygon::Collection->new(
|
||||
expolygons => (@{$mesh->facets} <= 5000)
|
||||
? $mesh->horizontal_projection
|
||||
: [ Slic3r::ExPolygon->new(convex_hull($mesh->vertices)) ],
|
||||
: [ Slic3r::ExPolygon->new($self->convex_hull) ],
|
||||
);
|
||||
for (map @$_, map @$_, @{$thumbnail->expolygons}) {
|
||||
@$_ = map $_ * $self->thumbnail_scaling_factor, @$_;
|
||||
}
|
||||
|
||||
# only simplify expolygons larger than the threshold
|
||||
@{$thumbnail->expolygons} = map { ($_->area >= 1) ? $_->simplify(0.5) : $_ } @{$thumbnail->expolygons};
|
||||
foreach my $expolygon (@{$thumbnail->expolygons}) {
|
||||
$expolygon->rotate(Slic3r::Geometry::deg2rad($self->rotate));
|
||||
$expolygon->scale($self->scale);
|
||||
}
|
||||
@{$thumbnail->expolygons} = grep @$_, @{$thumbnail->expolygons};
|
||||
$thumbnail->align_to_origin;
|
||||
@{$thumbnail->expolygons} = grep @$_,
|
||||
map { ($_->area >= 1) ? $_->simplify(0.5) : $_ }
|
||||
@{$thumbnail->expolygons};
|
||||
|
||||
$self->thumbnail($thumbnail); # ignored in multi-threaded environments
|
||||
$self->free_model_object;
|
||||
|
||||
return $thumbnail;
|
||||
}
|
||||
|
||||
sub set_rotation {
|
||||
sub _transform_thumbnail {
|
||||
my $self = shift;
|
||||
my ($angle) = @_;
|
||||
|
||||
if ($self->thumbnail) {
|
||||
$self->thumbnail->rotate(Slic3r::Geometry::deg2rad($angle - $self->rotate));
|
||||
$self->thumbnail->align_to_origin;
|
||||
my $z_size = $self->size->[Z];
|
||||
$self->size([ (map $_ / $self->thumbnail_scaling_factor, @{$self->thumbnail->size}), $z_size ]);
|
||||
}
|
||||
$self->rotate($angle);
|
||||
return unless $self->thumbnail;
|
||||
my $t = $self->_apply_transform($self->thumbnail);
|
||||
$t->scale($self->thumbnail_scaling_factor);
|
||||
|
||||
$self->transformed_thumbnail($t);
|
||||
}
|
||||
|
||||
sub set_scale {
|
||||
# bounding box with applied rotation and scaling
|
||||
sub transformed_bounding_box {
|
||||
my $self = shift;
|
||||
my ($scale) = @_;
|
||||
|
||||
my $factor = $scale / $self->scale;
|
||||
return if $factor == 1;
|
||||
$self->size->[$_] *= $factor for X,Y,Z;
|
||||
if ($self->thumbnail) {
|
||||
$_->scale($factor) for @{$self->thumbnail->expolygons};
|
||||
$self->thumbnail->align_to_origin;
|
||||
}
|
||||
$self->scale($scale);
|
||||
my $bb = Slic3r::Geometry::BoundingBox->new_from_points($self->_apply_transform($self->convex_hull));
|
||||
$bb->extents->[Z] = $self->bounding_box->clone->extents->[Z];
|
||||
return $bb;
|
||||
}
|
||||
|
||||
sub _apply_transform {
|
||||
my $self = shift;
|
||||
my ($entity) = @_; # can be anything that implements ->clone(), ->rotate() and ->scale()
|
||||
|
||||
# the order of these transformations MUST be the same everywhere, including
|
||||
# in Slic3r::Print->add_model()
|
||||
return $entity
|
||||
->clone
|
||||
->rotate(deg2rad($self->rotate), $self->bounding_box->center_2D)
|
||||
->scale($self->scale);
|
||||
}
|
||||
|
||||
sub transformed_size {
|
||||
my $self = shift;
|
||||
return $self->transformed_bounding_box->size;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -78,7 +78,7 @@ sub get_properties {
|
|||
my $object = $self->{object};
|
||||
return [
|
||||
['Name' => $object->name],
|
||||
['Size' => sprintf "%.2f x %.2f x %.2f", @{$object->size}],
|
||||
['Size' => sprintf "%.2f x %.2f x %.2f", @{$object->transformed_size}],
|
||||
['Facets' => $object->facets],
|
||||
['Vertices' => $object->vertices],
|
||||
['Materials' => $object->materials],
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
package Slic3r::Geometry::BoundingBox;
|
||||
use Moo;
|
||||
use Slic3r::Geometry qw(X Y Z MIN MAX X1 Y1 X2 Y2);
|
||||
use Storable qw();
|
||||
|
||||
has 'extents' => (is => 'ro', required => 1);
|
||||
|
||||
sub clone { Storable::dclone($_[0]) }
|
||||
|
||||
# 2D
|
||||
sub new_from_points {
|
||||
my $class = shift;
|
||||
my ($points) = @_;
|
||||
|
||||
my $bb = [ Slic3r::Geometry::bounding_box($points) ];
|
||||
return $class->new(extents => [
|
||||
[ $bb->[X1], $bb->[X2] ],
|
||||
[ $bb->[Y1], $bb->[Y2] ],
|
||||
]);
|
||||
}
|
||||
|
||||
# 3D
|
||||
sub new_from_points_3D {
|
||||
my $class = shift;
|
||||
my ($points) = @_;
|
||||
|
||||
return $class->new(extents => [ Slic3r::Geometry::bounding_box_3D($points) ]);
|
||||
}
|
||||
|
||||
# four-arguments 2D bb
|
||||
sub bb {
|
||||
my $self = shift;
|
||||
my $extents = $self->extents;
|
||||
return [ $extents->[X][MIN], $extents->[Y][MIN], $extents->[X][MAX], $extents->[Y][MAX] ];
|
||||
}
|
||||
|
||||
sub polygon {
|
||||
my $self = shift;
|
||||
return Slic3r::Polygon->new_from_bounding_box($self->bb);
|
||||
}
|
||||
|
||||
# note to $self
|
||||
sub rotate {
|
||||
die "Rotating an axis-aligned bounding box doesn't make any sense";
|
||||
}
|
||||
|
||||
sub scale {
|
||||
my $self = shift;
|
||||
my ($factor) = @_;
|
||||
|
||||
for (@{$self->extents}) {
|
||||
$_ *= $factor for @$_[MIN,MAX];
|
||||
}
|
||||
|
||||
$self;
|
||||
}
|
||||
|
||||
sub size {
|
||||
my $self = shift;
|
||||
|
||||
my $extents = $self->extents;
|
||||
return [ map $extents->[$_][MAX] - $extents->[$_][MIN], grep $extents->[$_], (X,Y,Z) ];
|
||||
}
|
||||
|
||||
sub center {
|
||||
my $self = shift;
|
||||
|
||||
my $extents = $self->extents;
|
||||
return [ map +($extents->[$_][MAX] + $extents->[$_][MIN])/2, grep $extents->[$_], (X,Y,Z) ];
|
||||
}
|
||||
|
||||
sub center_2D {
|
||||
my $self = shift;
|
||||
return Slic3r::Point->new(@{$self->center}[X,Y]);
|
||||
}
|
||||
|
||||
sub min_point {
|
||||
my $self = shift;
|
||||
return Slic3r::Point->new($self->extents->[X][MIN], $self->extents->[Y][MIN]);
|
||||
}
|
||||
|
||||
sub max_point {
|
||||
my $self = shift;
|
||||
return Slic3r::Point->new($self->extents->[X][MAX], $self->extents->[Y][MAX]);
|
||||
}
|
||||
|
||||
1;
|
|
@ -35,6 +35,13 @@ sub support_material_contact_z {
|
|||
return $self->print_z - ($self->height - $self->support_material_contact_height) / &Slic3r::SCALING_FACTOR;
|
||||
}
|
||||
|
||||
sub upper_layer_slices {
|
||||
my $self = shift;
|
||||
|
||||
my $upper_layer = $self->object->layers->[ $self->id + 1 ] or return [];
|
||||
return $upper_layer->slices;
|
||||
}
|
||||
|
||||
sub region {
|
||||
my $self = shift;
|
||||
my ($region_id) = @_;
|
||||
|
|
|
@ -252,7 +252,7 @@ sub make_perimeters {
|
|||
)};
|
||||
|
||||
my @loops = ();
|
||||
foreach my $polynode (@$polynodes) {
|
||||
foreach my $polynode (@nodes) {
|
||||
push @loops, $traverse->($polynode->{children}, $depth+1, $is_contour);
|
||||
|
||||
my $polygon = Slic3r::Polygon->new($polynode->{outer} // [ reverse @{$polynode->{hole}} ]);
|
||||
|
@ -331,8 +331,12 @@ sub _fill_gaps {
|
|||
1,
|
||||
)};
|
||||
|
||||
# medial axis-based gap fill should benefit from detection of larger gaps too, so
|
||||
# we could try with 1.5*$w for example, but that doesn't work well for zigzag fill
|
||||
# because it tends to create very sparse points along the gap when the infill direction
|
||||
# is not parallel to the gap (1.5*$w thus may only work well with a straight line)
|
||||
my $w = $self->perimeter_flow->width;
|
||||
my @widths = (1.5 * $w, $w, 0.4 * $w); # worth trying 0.2 too?
|
||||
my @widths = ($w, 0.4 * $w); # worth trying 0.2 too?
|
||||
foreach my $width (@widths) {
|
||||
my $flow = $self->perimeter_flow->clone(width => $width);
|
||||
|
||||
|
@ -373,7 +377,7 @@ sub _fill_gaps {
|
|||
|
||||
push @{ $self->thin_fills },
|
||||
map {
|
||||
$_->polyline->simplify($flow->scaled_width / 3);
|
||||
$_->simplify($flow->scaled_width/3);
|
||||
$_->pack;
|
||||
}
|
||||
map Slic3r::ExtrusionPath->new(
|
||||
|
|
|
@ -119,6 +119,7 @@ sub arrange_objects {
|
|||
$object->add_instance(
|
||||
offset => $_,
|
||||
rotation => $instance->rotation,
|
||||
scaling_factor => $instance->scaling_factor,
|
||||
) for move_points($instance->offset, @positions);
|
||||
}
|
||||
}
|
||||
|
@ -213,6 +214,7 @@ sub mesh {
|
|||
my $mesh = $object->mesh->clone;
|
||||
if ($instance) {
|
||||
$mesh->rotate($instance->rotation);
|
||||
$mesh->scale($instance->scaling_factor);
|
||||
$mesh->align_to_origin;
|
||||
$mesh->move(@{$instance->offset});
|
||||
}
|
||||
|
@ -259,6 +261,7 @@ sub split_meshes {
|
|||
$new_object->add_instance(
|
||||
offset => [ $_->offset->[X] + $extents[X][MIN], $_->offset->[Y] + $extents[Y][MIN] ],
|
||||
rotation => $_->rotation,
|
||||
scaling_factor => $_->scaling_factor,
|
||||
) for @{ $object->instances // [] };
|
||||
}
|
||||
}
|
||||
|
@ -274,7 +277,7 @@ package Slic3r::Model::Object;
|
|||
use Moo;
|
||||
|
||||
use List::Util qw(first);
|
||||
use Slic3r::Geometry qw(X Y Z MIN move_points move_points_3D);
|
||||
use Slic3r::Geometry qw(X Y Z MIN MAX move_points move_points_3D);
|
||||
use Storable qw(dclone);
|
||||
|
||||
has 'input_file' => (is => 'rw');
|
||||
|
@ -338,13 +341,27 @@ sub extents {
|
|||
return Slic3r::Geometry::bounding_box_3D($self->used_vertices);
|
||||
}
|
||||
|
||||
sub center {
|
||||
my $self = shift;
|
||||
|
||||
my @extents = $self->extents;
|
||||
return [ map +($extents[$_][MAX] + $extents[$_][MIN])/2, X,Y,Z ];
|
||||
}
|
||||
|
||||
sub bounding_box {
|
||||
my $self = shift;
|
||||
return Slic3r::Geometry::BoundingBox->new(extents => [ $self->extents ]);
|
||||
}
|
||||
|
||||
sub align_to_origin {
|
||||
my $self = shift;
|
||||
|
||||
# calculate the displacements needed to
|
||||
# have lowest value for each axis at coordinate 0
|
||||
my @extents = $self->extents;
|
||||
$self->move(map -$extents[$_][MIN], X,Y,Z);
|
||||
my @shift = map -$extents[$_][MIN], X,Y,Z;
|
||||
$self->move(@shift);
|
||||
return @shift;
|
||||
}
|
||||
|
||||
sub move {
|
||||
|
@ -410,6 +427,7 @@ use Moo;
|
|||
|
||||
has 'object' => (is => 'ro', weak_ref => 1, required => 1);
|
||||
has 'rotation' => (is => 'rw', default => sub { 0 }); # around mesh center point
|
||||
has 'scaling_factor' => (is => 'rw', default => sub { 1 });
|
||||
has 'offset' => (is => 'rw'); # must be Slic3r::Point object
|
||||
|
||||
1;
|
||||
|
|
|
@ -35,6 +35,13 @@ sub distance_to {
|
|||
return Slic3r::Geometry::distance_between_points($self, $point);
|
||||
}
|
||||
|
||||
sub scale {
|
||||
my $self = shift;
|
||||
my ($factor) = @_;
|
||||
$_ *= $factor for @$self;
|
||||
$self;
|
||||
}
|
||||
|
||||
sub rotate {
|
||||
my $self = shift;
|
||||
my ($angle, $center) = @_;
|
||||
|
|
|
@ -156,11 +156,12 @@ sub translate {
|
|||
sub scale {
|
||||
my $self = shift;
|
||||
my ($factor) = @_;
|
||||
return if $factor == 1;
|
||||
|
||||
# transform point coordinates
|
||||
foreach my $point (@$self) {
|
||||
$point->[$_] *= $factor for X,Y;
|
||||
if ($factor != 1) {
|
||||
foreach my $point (@$self) {
|
||||
$point->[$_] *= $factor for X,Y;
|
||||
}
|
||||
}
|
||||
return $self;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ use File::Spec;
|
|||
use List::Util qw(max first);
|
||||
use Math::ConvexHull::MonotoneChain qw(convex_hull);
|
||||
use Slic3r::ExtrusionPath ':roles';
|
||||
use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 MIN MAX PI scale unscale move_points nearest_point);
|
||||
use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 MIN MAX PI scale unscale move_points
|
||||
nearest_point chained_path);
|
||||
use Slic3r::Geometry::Clipper qw(diff_ex union_ex union_pt intersection_ex offset
|
||||
offset2 traverse_pt JT_ROUND JT_SQUARE PFT_EVENODD);
|
||||
use Time::HiRes qw(gettimeofday tv_interval);
|
||||
|
@ -105,6 +106,9 @@ sub add_model {
|
|||
$model->split_meshes if $Slic3r::Config->avoid_crossing_perimeters && !$Slic3r::Config->complete_objects;
|
||||
|
||||
foreach my $object (@{ $model->objects }) {
|
||||
# we align object to origin before applying transformations
|
||||
my @align = $object->align_to_origin;
|
||||
|
||||
# extract meshes by material
|
||||
my @meshes = (); # by region_id
|
||||
foreach my $volume (@{$object->volumes}) {
|
||||
|
@ -119,21 +123,24 @@ sub add_model {
|
|||
foreach my $mesh (grep $_, @meshes) {
|
||||
$mesh->check_manifoldness;
|
||||
|
||||
# we ignore the per-instance rotation currently and only
|
||||
# the order of these transformations must be the same as the one used in plater
|
||||
# to make the object positioning consistent with the visual preview
|
||||
|
||||
# we ignore the per-instance transformations currently and only
|
||||
# consider the first one
|
||||
$mesh->rotate($object->instances->[0]->rotation, $mesh->center)
|
||||
if @{ $object->instances // [] };
|
||||
if ($object->instances && @{$object->instances}) {
|
||||
$mesh->rotate($object->instances->[0]->rotation, $object->center);
|
||||
$mesh->scale($object->instances->[0]->scaling_factor);
|
||||
}
|
||||
|
||||
$mesh->scale(1 / &Slic3r::SCALING_FACTOR);
|
||||
}
|
||||
|
||||
# align the object to origin; not sure this is required by the toolpath generation
|
||||
# algorithms, but it's good practice to avoid negative coordinates; it probably
|
||||
# provides also some better performance in infill generation
|
||||
my @extents = Slic3r::Geometry::bounding_box_3D([ map @{$_->used_vertices}, grep $_, @meshes ]);
|
||||
foreach my $mesh (grep $_, @meshes) {
|
||||
$mesh->move(map -$extents[$_][MIN], X,Y,Z);
|
||||
}
|
||||
# we also align object after transformations so that we only work with positive coordinates
|
||||
# and the assumption that bounding_box === size works
|
||||
my $bb = Slic3r::Geometry::BoundingBox->new_from_points_3D([ map @{$_->used_vertices}, grep $_, @meshes ]);
|
||||
my @align2 = map -$bb->extents->[$_][MIN], (X,Y,Z);
|
||||
$_->move(@align2) for grep $_, @meshes;
|
||||
|
||||
# initialize print object
|
||||
push @{$self->objects}, Slic3r::Print::Object->new(
|
||||
|
@ -141,10 +148,10 @@ sub add_model {
|
|||
meshes => [ @meshes ],
|
||||
copies => [
|
||||
$object->instances
|
||||
? (map [ (scale $_->offset->[X]) + $extents[X][MIN], (scale $_->offset->[Y]) + $extents[Y][MIN] ], @{$object->instances})
|
||||
? (map [ scale($_->offset->[X] - $align[X]) - $align2[X], scale($_->offset->[Y] - $align[Y]) - $align2[Y] ], @{$object->instances})
|
||||
: [0,0],
|
||||
],
|
||||
size => [ map $extents[$_][MAX] - $extents[$_][MIN], (X,Y,Z) ],
|
||||
size => $bb->size, # transformed size
|
||||
input_file => $object->input_file,
|
||||
layer_height_ranges => $object->layer_height_ranges,
|
||||
);
|
||||
|
@ -322,6 +329,13 @@ sub export_gcode {
|
|||
$status_cb->(10, "Processing triangulated mesh");
|
||||
$_->slice for @{$self->objects};
|
||||
|
||||
# remove empty layers and abort if there are no more
|
||||
# as some algorithms assume all objects have at least one layer
|
||||
# note: this will change object indexes
|
||||
@{$self->objects} = grep @{$_->layers}, @{$self->objects};
|
||||
die "No layers were detected. You might want to repair your STL file(s) or check their size and retry.\n"
|
||||
if !@{$self->objects};
|
||||
|
||||
if ($Slic3r::Config->resolution) {
|
||||
$status_cb->(15, "Simplifying input");
|
||||
$self->_simplify_slices(scale $Slic3r::Config->resolution);
|
||||
|
@ -480,13 +494,15 @@ sub export_svg {
|
|||
$self->init_extruders;
|
||||
|
||||
$_->slice for @{$self->objects};
|
||||
$self->arrange_objects;
|
||||
|
||||
my $output_file = $self->expanded_output_filepath($params{output_file});
|
||||
$output_file =~ s/\.gcode$/.svg/i;
|
||||
my $fh = $params{output_fh};
|
||||
if ($params{output_file}) {
|
||||
my $output_file = $self->expanded_output_filepath($params{output_file});
|
||||
$output_file =~ s/\.gcode$/.svg/i;
|
||||
Slic3r::open(\$fh, ">", $output_file) or die "Failed to open $output_file for writing\n";
|
||||
print "Exporting to $output_file..." unless $params{quiet};
|
||||
}
|
||||
|
||||
Slic3r::open(\my $fh, ">", $output_file) or die "Failed to open $output_file for writing\n";
|
||||
print "Exporting to $output_file...";
|
||||
my $print_size = $self->size;
|
||||
print $fh sprintf <<"EOF", unscale($print_size->[X]), unscale($print_size->[Y]);
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
|
@ -555,7 +571,7 @@ EOF
|
|||
|
||||
print $fh "</svg>\n";
|
||||
close $fh;
|
||||
print "Done.\n";
|
||||
print "Done.\n" unless $params{quiet};
|
||||
}
|
||||
|
||||
sub make_skirt {
|
||||
|
@ -704,7 +720,7 @@ sub write_gcode {
|
|||
multiple_extruders => (@{$self->extruders} > 1),
|
||||
layer_count => $self->layer_count,
|
||||
);
|
||||
print $fh "G21 ; set units to millimeters\n";
|
||||
print $fh "G21 ; set units to millimeters\n" if $Slic3r::Config->gcode_flavor ne 'makerware';
|
||||
print $fh $gcodegen->set_fan(0, 1) if $Slic3r::Config->cooling && $Slic3r::Config->disable_fan_first_layers;
|
||||
|
||||
# write start commands to file
|
||||
|
@ -722,15 +738,13 @@ sub write_gcode {
|
|||
printf $fh $gcodegen->set_temperature($self->extruders->[$t]->first_layer_temperature, 1, $t)
|
||||
if $self->extruders->[$t]->first_layer_temperature && $Slic3r::Config->start_gcode !~ /M(?:109|104)/i;
|
||||
}
|
||||
print $fh "G90 ; use absolute coordinates\n";
|
||||
print $fh "G90 ; use absolute coordinates\n" if $Slic3r::Config->gcode_flavor ne 'makerware';
|
||||
if ($Slic3r::Config->gcode_flavor =~ /^(?:reprap|teacup)$/) {
|
||||
printf $fh $gcodegen->reset_e;
|
||||
if ($Slic3r::Config->gcode_flavor =~ /^(?:reprap|teacup|makerbot|sailfish)$/) {
|
||||
if ($Slic3r::Config->use_relative_e_distances) {
|
||||
print $fh "M83 ; use relative distances for extrusion\n";
|
||||
} else {
|
||||
print $fh "M82 ; use absolute distances for extrusion\n";
|
||||
}
|
||||
if ($Slic3r::Config->use_relative_e_distances) {
|
||||
print $fh "M83 ; use relative distances for extrusion\n";
|
||||
} else {
|
||||
print $fh "M82 ; use absolute distances for extrusion\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -778,7 +792,7 @@ sub write_gcode {
|
|||
|
||||
# print objects from the smallest to the tallest to avoid collisions
|
||||
# when moving onto next object starting point
|
||||
my @obj_idx = sort { $self->objects->[$a]->layer_count <=> $self->objects->[$b]->layer_count } 0..$#{$self->objects};
|
||||
my @obj_idx = sort { $self->objects->[$a]->size->[Z] <=> $self->objects->[$b]->size->[Z] } 0..$#{$self->objects};
|
||||
|
||||
my $finished_objects = 0;
|
||||
for my $obj_idx (@obj_idx) {
|
||||
|
@ -820,18 +834,35 @@ sub write_gcode {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
# order objects using a nearest neighbor search
|
||||
my @obj_idx = chained_path([ map $_->copies->[0], @{$self->objects} ]);
|
||||
|
||||
# sort layers by Z
|
||||
my %layers = (); # print_z => [ [layers], [layers], [layers] ] by obj_idx
|
||||
foreach my $obj_idx (0 .. $#{$self->objects}) {
|
||||
my $object = $self->objects->[$obj_idx];
|
||||
foreach my $layer (@{$object->layers}, @{$object->support_layers}) {
|
||||
$layers{ $layer->print_z } ||= [];
|
||||
$layers{ $layer->print_z }[$obj_idx] ||= [];
|
||||
push @{$layers{ $layer->print_z }[$obj_idx]}, $layer;
|
||||
}
|
||||
}
|
||||
|
||||
my $buffer = Slic3r::GCode::CoolingBuffer->new(
|
||||
config => $Slic3r::Config,
|
||||
gcodegen => $gcodegen,
|
||||
);
|
||||
my @layers = sort { $a->print_z <=> $b->print_z } map { @{$_->layers}, @{$_->support_layers} } @{$self->objects};
|
||||
foreach my $layer (@layers) {
|
||||
print $fh $buffer->append(
|
||||
$layer_gcode->process_layer($layer, $layer->object->copies),
|
||||
$layer->object."",
|
||||
$layer->id,
|
||||
$layer->print_z,
|
||||
);
|
||||
foreach my $print_z (sort { $a <=> $b } keys %layers) {
|
||||
foreach my $obj_idx (@obj_idx) {
|
||||
foreach my $layer (@{ $layers{$print_z}[$obj_idx] // [] }) {
|
||||
print $fh $buffer->append(
|
||||
$layer_gcode->process_layer($layer, $layer->object->copies),
|
||||
$layer->object . ref($layer), # differentiate $obj_id between normal layers and support layers
|
||||
$layer->id,
|
||||
$layer->print_z,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
print $fh $buffer->flush;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use Slic3r::Surface ':types';
|
|||
has 'print' => (is => 'ro', weak_ref => 1, required => 1);
|
||||
has 'input_file' => (is => 'rw', required => 0);
|
||||
has 'meshes' => (is => 'rw', default => sub { [] }); # by region_id
|
||||
has 'size' => (is => 'rw', required => 1);
|
||||
has 'size' => (is => 'rw', required => 1); # XYZ in scaled coordinates
|
||||
has 'copies' => (is => 'rw', trigger => 1); # in scaled coordinates
|
||||
has 'layers' => (is => 'rw', default => sub { [] });
|
||||
has 'support_layers' => (is => 'rw', default => sub { [] });
|
||||
|
@ -191,7 +191,6 @@ sub slice {
|
|||
|
||||
# remove last layer(s) if empty
|
||||
pop @{$self->layers} while @{$self->layers} && (!map @{$_->lines}, @{$self->layers->[-1]->regions});
|
||||
die "Invalid or too thin input file: no layers could be generated\n" if !@{$self->layers};
|
||||
|
||||
foreach my $layer (@{ $self->layers }) {
|
||||
# make sure all layers contain layer region objects for all regions
|
||||
|
@ -278,9 +277,6 @@ sub slice {
|
|||
$self->layers->[$i]->id($i);
|
||||
}
|
||||
}
|
||||
|
||||
warn "No layers were detected. You might want to repair your STL file and retry.\n"
|
||||
if !@{$self->layers};
|
||||
}
|
||||
|
||||
sub make_perimeters {
|
||||
|
@ -388,7 +384,6 @@ sub detect_surfaces_type {
|
|||
1,
|
||||
);
|
||||
return map Slic3r::Surface->new(expolygon => $_, surface_type => $result_type),
|
||||
grep $_->is_printable($layerm->perimeter_flow->scaled_width),
|
||||
@$expolygons;
|
||||
};
|
||||
|
||||
|
@ -602,7 +597,8 @@ sub discover_horizontal_shells {
|
|||
for (my $i = 0; $i < $self->layer_count; $i++) {
|
||||
my $layerm = $self->layers->[$i]->regions->[$region_id];
|
||||
|
||||
if ($Slic3r::Config->solid_infill_every_layers && ($i % $Slic3r::Config->solid_infill_every_layers) == 0) {
|
||||
if ($Slic3r::Config->solid_infill_every_layers && $Slic3r::Config->fill_density > 0
|
||||
&& ($i % $Slic3r::Config->solid_infill_every_layers) == 0) {
|
||||
$_->surface_type(S_TYPE_INTERNALSOLID)
|
||||
for grep $_->surface_type == S_TYPE_INTERNAL, @{$layerm->fill_surfaces};
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ my %cuboids = (
|
|||
);
|
||||
|
||||
sub model {
|
||||
my ($model_name) = @_;
|
||||
my ($model_name, %params) = @_;
|
||||
|
||||
my ($vertices, $facets);
|
||||
if ($cuboids{$model_name}) {
|
||||
|
@ -27,12 +27,22 @@ sub model {
|
|||
$facets = [
|
||||
[0,1,2], [0,2,3], [4,5,6], [4,6,7], [0,4,7], [0,7,1], [1,7,6], [1,6,2], [2,6,5], [2,5,3], [4,0,3], [4,3,5],
|
||||
],
|
||||
} elsif ($model_name eq 'V') {
|
||||
$vertices = [
|
||||
[-14,0,20],[-14,15,20],[0,0,0],[0,15,0],[-4,0,20],[-4,15,20],[5,0,7.14286],[10,0,0],[24,0,20],[14,0,20],[10,15,0],[5,15,7.14286],[14,15,20],[24,15,20]
|
||||
];
|
||||
$facets = [
|
||||
[0,1,2],[2,1,3],[1,0,4],[5,1,4],[4,0,2],[6,4,2],[7,6,2],[8,9,7],[9,6,7],[2,3,7],[7,3,10],[1,5,3],[3,5,11],[11,12,13],[11,13,3],[3,13,10],[5,4,6],[11,5,6],[6,9,11],[11,9,12],[12,9,8],[13,12,8],[8,7,10],[13,8,10]
|
||||
],
|
||||
}
|
||||
|
||||
my $model = Slic3r::Model->new;
|
||||
my $object = $model->add_object(vertices => $vertices);
|
||||
$object->add_volume(facets => $facets);
|
||||
$object->add_instance(offset => [0,0]);
|
||||
$object->add_instance(
|
||||
offset => [0,0],
|
||||
rotation => $params{rotation} // 0,
|
||||
);
|
||||
return $model;
|
||||
}
|
||||
|
||||
|
@ -46,7 +56,7 @@ sub init_print {
|
|||
my $print = Slic3r::Print->new(config => $config);
|
||||
|
||||
$model_name = [$model_name] if ref($model_name) ne 'ARRAY';
|
||||
$print->add_model(model($_)) for @$model_name;
|
||||
$print->add_model(model($_, %params)) for @$model_name;
|
||||
$print->validate;
|
||||
|
||||
return $print;
|
||||
|
|
|
@ -408,6 +408,11 @@ sub extents {
|
|||
return Slic3r::Geometry::bounding_box_3D($self->used_vertices);
|
||||
}
|
||||
|
||||
sub bounding_box {
|
||||
my $self = shift;
|
||||
return Slic3r::Geometry::BoundingBox->new(extents => [ $self->extents ]);
|
||||
}
|
||||
|
||||
sub size {
|
||||
my $self = shift;
|
||||
return Slic3r::Geometry::size_3D($self->used_vertices);
|
||||
|
|
|
@ -173,7 +173,7 @@ $j
|
|||
(default: $config->{print_center}->[0],$config->{print_center}->[1])
|
||||
--z-offset Additional height in mm to add to vertical coordinates
|
||||
(+/-, default: $config->{z_offset})
|
||||
--gcode-flavor The type of G-code to generate (reprap/teacup/makerbot/sailfish/mach3/no-extrusion,
|
||||
--gcode-flavor The type of G-code to generate (reprap/teacup/makerware/sailfish/mach3/no-extrusion,
|
||||
default: $config->{gcode_flavor})
|
||||
--use-relative-e-distances Enable this to get relative E values
|
||||
--gcode-arcs Use G2/G3 commands for native arcs (experimental, not supported
|
||||
|
|
11
t/fill.t
11
t/fill.t
|
@ -2,7 +2,7 @@ use Test::More;
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
plan tests => 9;
|
||||
plan tests => 10;
|
||||
|
||||
BEGIN {
|
||||
use FindBin;
|
||||
|
@ -109,14 +109,17 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ }
|
|||
$config->set('top_solid_layers', 0);
|
||||
$config->set('bottom_solid_layers', 0);
|
||||
$config->set('solid_infill_below_area', 20000000);
|
||||
$config->set('solid_infill_every_layers', 2);
|
||||
|
||||
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
|
||||
my %layers_with_extrusion = ();
|
||||
Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub {
|
||||
my ($self, $cmd, $args, $info) = @_;
|
||||
|
||||
fail "solid_infill_below_area should be ignored when fill_density is 0"
|
||||
if $info->{extruding};
|
||||
$layers_with_extrusion{$self->Z} = 1 if $info->{extruding};
|
||||
});
|
||||
|
||||
ok !%layers_with_extrusion,
|
||||
"solid_infill_below_area and solid_infill_every_layers are ignored when fill_density is 0";
|
||||
}
|
||||
|
||||
__END__
|
||||
|
|
12
t/geometry.t
12
t/geometry.t
|
@ -2,7 +2,7 @@ use Test::More;
|
|||
use strict;
|
||||
use warnings;
|
||||
|
||||
plan tests => 23;
|
||||
plan tests => 24;
|
||||
|
||||
BEGIN {
|
||||
use FindBin;
|
||||
|
@ -173,4 +173,12 @@ is Slic3r::Geometry::can_connect_points(@$points, $polygons), 0, 'can_connect_po
|
|||
is_deeply $result, [ [10, 0], [5, 5], [0, 0], [10, 0] ], 'split_at_index';
|
||||
}
|
||||
|
||||
#==========================================================
|
||||
#==========================================================
|
||||
|
||||
{
|
||||
my $bb = Slic3r::Geometry::BoundingBox->new_from_points([ [0, 1], [10, 2], [20, 2] ]);
|
||||
$bb->scale(2);
|
||||
is_deeply $bb->extents, [ [0,40], [2,4] ], 'bounding box is scaled correctly';
|
||||
}
|
||||
|
||||
#==========================================================
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
use Test::More tests => 1;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
BEGIN {
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
}
|
||||
|
||||
use List::Util qw(first);
|
||||
use Slic3r;
|
||||
use Slic3r::Test;
|
||||
|
||||
{
|
||||
my $print = Slic3r::Test::init_print('20mm_cube', rotation => 45);
|
||||
ok !(first { $_ < 0 } map @$_, map @{$_->used_vertices}, grep $_, map @{$_->meshes}, @{$print->objects}),
|
||||
"object is still in positive coordinate space even after rotation";
|
||||
}
|
||||
|
||||
__END__
|
25
t/shells.t
25
t/shells.t
|
@ -1,4 +1,4 @@
|
|||
use Test::More tests => 2;
|
||||
use Test::More tests => 3;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
|
@ -45,4 +45,27 @@ use Slic3r::Test;
|
|||
ok $test->(), "proper number of shells is applied even when fill density is none";
|
||||
}
|
||||
|
||||
# issue #1161
|
||||
{
|
||||
my $config = Slic3r::Config->new_from_defaults;
|
||||
$config->set('layer_height', 0.3);
|
||||
$config->set('first_layer_height', '100%');
|
||||
$config->set('bottom_solid_layers', 0);
|
||||
$config->set('top_solid_layers', 3);
|
||||
$config->set('cooling', 0);
|
||||
$config->set('solid_infill_speed', 99);
|
||||
$config->set('top_solid_infill_speed', 99);
|
||||
|
||||
my $print = Slic3r::Test::init_print('V', config => $config);
|
||||
my %layers_with_solid_infill = (); # Z => 1
|
||||
Slic3r::GCode::Reader->new(gcode => Slic3r::Test::gcode($print))->parse(sub {
|
||||
my ($self, $cmd, $args, $info) = @_;
|
||||
|
||||
$layers_with_solid_infill{$self->Z} = 1
|
||||
if $info->{extruding} && ($args->{F} // $self->F) == $config->solid_infill_speed*60;
|
||||
});
|
||||
is scalar(map $layers_with_solid_infill{$_}, grep $_ <= 7.2, keys %layers_with_solid_infill), 3,
|
||||
"correct number of top solid shells is generated in V-shaped object";
|
||||
}
|
||||
|
||||
__END__
|
||||
|
|
18
t/support.t
18
t/support.t
|
@ -14,8 +14,24 @@ use Slic3r::Test;
|
|||
my $config = Slic3r::Config->new_from_defaults;
|
||||
$config->set('raft_layers', 3);
|
||||
$config->set('brim_width', 6);
|
||||
$config->set('skirts', 0);
|
||||
$config->set('support_material_extruder', 2);
|
||||
$config->set('layer_height', 0.4);
|
||||
$config->set('first_layer_height', '100%');
|
||||
my $print = Slic3r::Test::init_print('20mm_cube', config => $config);
|
||||
ok Slic3r::Test::gcode($print), 'no conflict between raft/support and brim';
|
||||
ok my $gcode = Slic3r::Test::gcode($print), 'no conflict between raft/support and brim';
|
||||
|
||||
my $tool = 0;
|
||||
Slic3r::GCode::Reader->new(gcode => $gcode)->parse(sub {
|
||||
my ($self, $cmd, $args, $info) = @_;
|
||||
|
||||
if ($cmd =~ /^T(\d+)/) {
|
||||
$tool = $1;
|
||||
} elsif ($info->{extruding} && $self->Z <= ($config->raft_layers * $config->layer_height)) {
|
||||
fail 'not extruding raft/brim with support material extruder'
|
||||
if $tool != ($config->support_material_extruder-1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
__END__
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
use Test::More tests => 1;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
BEGIN {
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
}
|
||||
|
||||
use Slic3r;
|
||||
use Slic3r::Test;
|
||||
|
||||
{
|
||||
my $print = Slic3r::Test::init_print('20mm_cube');
|
||||
eval {
|
||||
my $fh = IO::Scalar->new(\my $gcode);
|
||||
$print->export_svg(output_fh => $fh, quiet => 1);
|
||||
$fh->close;
|
||||
};
|
||||
ok !$@, 'successful SVG export';
|
||||
}
|
||||
|
||||
__END__
|
|
@ -0,0 +1,34 @@
|
|||
#!/usr/bin/perl
|
||||
# This script dumps a STL file into Perl syntax for writing tests
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
BEGIN {
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
}
|
||||
|
||||
use Slic3r;
|
||||
$|++;
|
||||
|
||||
$ARGV[0] or usage(1);
|
||||
|
||||
{
|
||||
my $model = Slic3r::Format::STL->read_file($ARGV[0]);
|
||||
my $mesh = $model->mesh;
|
||||
printf "VERTICES = %s\n", join ',', map "[$_->[0],$_->[1],$_->[2]]", @{$mesh->vertices};
|
||||
printf "FACETS = %s\n", join ',', map "[$_->[0],$_->[1],$_->[2]]", @{$mesh->facets};
|
||||
}
|
||||
|
||||
|
||||
sub usage {
|
||||
my ($exit_code) = @_;
|
||||
|
||||
print <<"EOF";
|
||||
Usage: dump-stl.pl file.stl
|
||||
EOF
|
||||
exit ($exit_code || 0);
|
||||
}
|
||||
|
||||
__END__
|
|
@ -22,7 +22,7 @@ _arguments -S \
|
|||
'*--nozzle-diameter[specify nozzle diameter]:nozzle diameter in mm' \
|
||||
'--print-center[specify print center coordinates]:print center coordinates in mm,mm' \
|
||||
'--z-offset[specify Z-axis offset]:Z-axis offset in mm' \
|
||||
'--gcode-flavor[specify the type of G-code to generate]:G-code flavor:(reprap teacup makerbot sailfish mach3 no-extrusion)' \
|
||||
'--gcode-flavor[specify the type of G-code to generate]:G-code flavor:(reprap teacup makerware sailfish mach3 no-extrusion)' \
|
||||
'(--use-relative-e-distances --no-use-relative-e-distances)'--{no-,}use-relative-e-distances'[disable/enable relative E values]' \
|
||||
'--extrusion-axis[specify letter associated with the extrusion axis]:extrusion axis letter' \
|
||||
'(--gcode-arcs --no-gcode-arcs)'--{no-,}gcode-arcs'[disable/enable G2/G3 commands for native arcs]' \
|
||||
|
|
Loading…
Reference in New Issue