From 5595839b31919fbf6d2b98e57f05c2ecee506980 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Mon, 5 Sep 2011 13:33:09 +0200 Subject: [PATCH] Command line interface --- README.markdown | 8 +-- lib/Slic3r.pm | 10 +++- lib/Slic3r/Fill/Rectilinear.pm | 27 ++++----- lib/Slic3r/Layer.pm | 23 ++++---- lib/Slic3r/Print.pm | 8 +-- lib/Slic3r/STL.pm | 12 ++-- slic3r.pl | 101 +++++++++++++++++++++++++++++++-- testcube20mm.stl | Bin 684 -> 0 bytes 8 files changed, 141 insertions(+), 48 deletions(-) delete mode 100644 testcube20mm.stl diff --git a/README.markdown b/README.markdown index 77bac9a64..414cc1fb2 100644 --- a/README.markdown +++ b/README.markdown @@ -32,17 +32,17 @@ Slic3r is able to: * read binary and ASCII STL files; * generate multiple perimeters (skins); -* generate rectilinear feed (100% solid for external surfaces or with customizable less density for inner surfaces); +* generate rectilinear fill (100% solid for external surfaces or with customizable less density for inner surfaces); * use relative or absolute extrusion commands; * center print around bed center point; * use different speed for bottom layer; * output relevant GCODE. -Roadmap include the following goals: +Roadmap includes the following goals: -* set up a command line interface and hide debug messages; * output some statistics; * allow the user to customize initial and final GCODE commands; +* retraction; * option for filling multiple solid layers near external surfaces; * support material for internal perimeters; * ability to infill in the direction of bridges; @@ -52,7 +52,7 @@ Roadmap include the following goals: ## Is it usable already? -Not yet, as I need to finish the command line interface. +Yes. I need to write a script to install dependencies. ## Can I help? diff --git a/lib/Slic3r.pm b/lib/Slic3r.pm index b0eaddb5c..1ae67b857 100644 --- a/lib/Slic3r.pm +++ b/lib/Slic3r.pm @@ -3,6 +3,11 @@ package Slic3r; use strict; use warnings; +our $debug = 0; +sub debugf { + printf @_ if $debug; +} + use Slic3r::ExtrusionPath; use Slic3r::Fill; use Slic3r::Layer; @@ -15,11 +20,12 @@ use Slic3r::Print; use Slic3r::STL; use Slic3r::Surface; + our $layer_height = 0.4; our $resolution = 0.1; our $perimeter_offsets = 3; our $fill_density = 0.2; # 1 = 100% -our $flow_width = 0.4; # TODO: verify this is a multiple of $resolution +our $flow_width = 0.4; our $temperature = 195; our $flow_rate = 60; # mm/sec @@ -27,7 +33,7 @@ our $print_feed_rate = 60; # mm/sec our $travel_feed_rate = 80; # mm/sec our $bottom_layer_speed_ratio = 0.6; -our $use_relative_e_distances = 1; +our $use_relative_e_distances = 0; our $print_center = [100,100]; # object will be centered around this point diff --git a/lib/Slic3r/Fill/Rectilinear.pm b/lib/Slic3r/Fill/Rectilinear.pm index 4e3be8072..0edd89f94 100644 --- a/lib/Slic3r/Fill/Rectilinear.pm +++ b/lib/Slic3r/Fill/Rectilinear.pm @@ -18,10 +18,9 @@ sub make_fill { # let's alternate fill direction my @axes = $layer->id % 2 == 0 ? (0,1) : (1,0); - printf " primary axis: %d\n", $axes[0]; foreach my $surface (@{ $layer->fill_surfaces }) { - printf " Processing surface %s:\n", $surface->id; + Slic3r::debugf " Processing surface %s:\n", $surface->id; my $polygon = $surface->mgp_polygon; # rotate surface as needed @@ -34,10 +33,6 @@ sub make_fill { my $distance_between_lines = $Slic3r::flow_width / $Slic3r::resolution / $density; my $number_of_lines = ($axes[0] == 0 ? $print->x_length : $print->y_length) / $distance_between_lines; - #printf "distance_between_lines = %f\n", $distance_between_lines; - #printf "number_of_lines = %d\n", $number_of_lines; - #printf "axes = %d, %d\n", @axes; - # this arrayref will hold intersection points of the fill grid with surface segments my $points = [ map [], 0..$number_of_lines-1 ]; foreach my $line (map $self->_lines_from_mgp_points($_), @{ $polygon->polygons }) { @@ -49,7 +44,7 @@ sub make_fill { # find out the coordinates my @coordinates = map @$_, @$line; - printf "Segment %d,%d - %d,%d\n", @coordinates; + Slic3r::debugf "Segment %d,%d - %d,%d\n", @coordinates; # get the extents of the segment along the primary axis my @line_c = sort ($coordinates[X1], $coordinates[X2]); @@ -59,15 +54,15 @@ sub make_fill { # if the segment is parallel to our ray, there will be two intersection points if ($line_c[0] == $line_c[1]) { - printf " Segment is parallel!\n"; + Slic3r::debugf " Segment is parallel!\n"; push @{ $points->[$i] }, $coordinates[Y1], $coordinates[Y2]; - printf " intersections at %f (%d) = %f, %f\n", $c, $i, $points->[$i][-2], $points->[$i][-1]; + Slic3r::debugf " intersections at %f (%d) = %f, %f\n", $c, $i, $points->[$i][-2], $points->[$i][-1]; } else { - printf " Segment NOT parallel!\n"; + Slic3r::debugf " Segment NOT parallel!\n"; # one point of intersection push @{ $points->[$i] }, $coordinates[Y1] + ($coordinates[Y2] - $coordinates[Y1]) * ($c - $coordinates[X1]) / ($coordinates[X2] - $coordinates[X1]); - printf " intersection at %f (%d) = %f\n", $c, $i, $points->[$i][-1]; + Slic3r::debugf " intersection at %f (%d) = %f\n", $c, $i, $points->[$i][-1]; } } } @@ -102,13 +97,13 @@ sub make_fill { # loop through rows ROW: for (my $i = 0; $i < $number_of_lines; $i++) { my $row = $points->[$i]; - printf "Processing row %d...\n", $i; + Slic3r::debugf "Processing row %d...\n", $i; if (!@$row) { - printf " no points\n"; + Slic3r::debugf " no points\n"; $stop_path->(); next ROW; } - printf " points = %s\n", join ', ', @$row; + Slic3r::debugf " points = %s\n", join ', ', @$row if $Slic3r::debug; # coordinate of current row my $c = ($i + 1) * $distance_between_lines; @@ -120,8 +115,8 @@ sub make_fill { my @connectable_points = $self->find_connectable_points($polygon, $path_points[-1], $c, $row); @connectable_points = reverse @connectable_points if $direction == 1; - printf " found %d connectable points = %s\n", scalar(@connectable_points), - join ', ', @connectable_points; + Slic3r::debugf " found %d connectable points = %s\n", scalar(@connectable_points), + join ', ', @connectable_points if $Slic3r::debug; if (!@connectable_points && @path_points && $path_points[-1][0] != $c) { # no connectable in this row diff --git a/lib/Slic3r/Layer.pm b/lib/Slic3r/Layer.pm index 7f84105a3..a81a250ae 100644 --- a/lib/Slic3r/Layer.pm +++ b/lib/Slic3r/Layer.pm @@ -181,8 +181,8 @@ sub merge_continuous_lines { # create new line my ($a, $b) = grep $_ ne $point, $line->points, $neighbor_line->points; my $new_line = $self->add_line($a, $b); - printf "Merging continuous lines %s and %s into %s\n", - $line->id, $neighbor_line->id, $new_line->id; + Slic3r::debugf "Merging continuous lines %s and %s into %s\n", + $line->id, $neighbor_line->id, $new_line->id if $Slic3r::debug; # delete merged lines $self->remove_line($_) for ($line, $neighbor_line); @@ -226,8 +226,8 @@ sub make_polylines { $cur_line = $next_line; } - printf "Discovered polyline of %d lines (%s)\n", scalar keys %points, - join('-', map $_->id, values %visited_lines); + Slic3r::debugf "Discovered polyline of %d lines (%s)\n", scalar keys %points, + join('-', map $_->id, values %visited_lines) if $Slic3r::debug; push @$polylines, Slic3r::Polyline::Closed->new(lines => [values %visited_lines]); } @@ -284,8 +284,9 @@ sub make_surfaces { $surface->surface_type('internal'); push @{ $self->surfaces }, $surface; - printf "New surface: %s (holes: %s)\n", - $surface->id, join(', ', map $_->id, @{$surface->holes}) || 'none'; + Slic3r::debugf "New surface: %s (holes: %s)\n", + $surface->id, join(', ', map $_->id, @{$surface->holes}) || 'none' + if $Slic3r::debug; } } } @@ -310,13 +311,13 @@ sub merge_contiguous_surfaces { # defensive programming if (@common_lines > 2) { - printf "Surfaces %s and %s share %d lines! How's it possible?\n", - $surface->id, $neighbor_surface->id, scalar @common_lines; + Slic3r::debugf "Surfaces %s and %s share %d lines! How's it possible?\n", + $surface->id, $neighbor_surface->id, scalar @common_lines if $Slic3r::debug; } - printf "Surfaces %s and %s share line/lines %s!\n", + Slic3r::debugf "Surfaces %s and %s share line/lines %s!\n", $surface->id, $neighbor_surface->id, - join(', ', map $_->id, @common_lines); + join(', ', map $_->id, @common_lines) if $Slic3r::debug; # defensive programming if ($surface->surface_type ne $neighbor_surface->surface_type) { @@ -342,7 +343,7 @@ sub merge_contiguous_surfaces { surface_type => $surface->surface_type, ); - printf " merging into new surface %s\n", $new_surface->id; + Slic3r::debugf " merging into new surface %s\n", $new_surface->id; push @{ $self->surfaces }, $new_surface; $self->remove_surface($_) for ($surface, $neighbor_surface); diff --git a/lib/Slic3r/Print.pm b/lib/Slic3r/Print.pm index 8f82565ba..4f503bdaf 100644 --- a/lib/Slic3r/Print.pm +++ b/lib/Slic3r/Print.pm @@ -50,8 +50,8 @@ sub extrude_perimeters { foreach my $layer (@{ $self->layers }) { $perimeter_extruder->make_perimeter($layer); - printf " generated paths: %s\n", - join ' ', map $_->id, @{ $layer->perimeters }; + Slic3r::debugf " generated paths: %s\n", + join ' ', map $_->id, @{ $layer->perimeters } if $Slic3r::debug; } } @@ -62,9 +62,9 @@ sub extrude_fills { foreach my $layer (@{ $self->layers }) { $fill_extruder->make_fill($self, $layer); - printf " generated %d paths: %s\n", + Slic3r::debugf " generated %d paths: %s\n", scalar @{ $layer->fills }, - join ' ', map $_->id, @{ $layer->fills }; + join ' ', map $_->id, @{ $layer->fills } if $Slic3r::debug; } } diff --git a/lib/Slic3r/STL.pm b/lib/Slic3r/STL.pm index 226b3832c..68070557f 100644 --- a/lib/Slic3r/STL.pm +++ b/lib/Slic3r/STL.pm @@ -39,7 +39,6 @@ sub parse_file { # calculate the displacements needed to # have lowest value for each axis at coordinate 0 my @shift = map 0 - $extents[$_][MIN], X,Y,Z; - printf "shift = %d, %d, %d\n", @shift[X,Y,Z]; # process facets foreach my $facet ($stl->part->facets) { @@ -77,7 +76,8 @@ sub parse_file { sub _facet { my $self = shift; my ($print, $normal, @vertices) = @_; - printf "\n==> FACET (%s):\n", join('-', map join(',', @$_), @vertices); + Slic3r::debugf "\n==> FACET (%s):\n", join('-', map join(',', @$_), @vertices) + if $Slic3r::debug; # find the vertical extents of the facet my ($min_z, $max_z) = (99999999, -99999999); @@ -85,15 +85,15 @@ sub _facet { $min_z = $vertex->[Z] if $vertex->[Z] < $min_z; $max_z = $vertex->[Z] if $vertex->[Z] > $max_z; } - printf "z: min = %.0f, max = %.0f\n", $min_z, $max_z; + Slic3r::debugf "z: min = %.0f, max = %.0f\n", $min_z, $max_z; # calculate the layer extents my ($min_layer, $max_layer) = map {$_ * $Slic3r::resolution / $Slic3r::layer_height} $min_z, $max_z; - printf "layers: min = %.0f, max = %.0f\n", $min_layer, $max_layer; + Slic3r::debugf "layers: min = %.0f, max = %.0f\n", $min_layer, $max_layer; # is the facet horizontal? if ($min_layer == $max_layer) { - printf "Facet is horizontal\n"; + Slic3r::debugf "Facet is horizontal\n"; my $layer = $print->layer($min_layer); my $surface = $layer->add_surface(@vertices); @@ -151,7 +151,7 @@ sub _facet { # check whether the two points coincide due to resolution rounding if ($intersection_points[0]->coincides_with($intersection_points[1])) { - printf "Points coincide; removing\n"; + Slic3r::debugf "Points coincide; removing\n"; $layer->remove_point($_) for @intersection_points; next; } diff --git a/slic3r.pl b/slic3r.pl index a76d64c3a..c89878805 100644 --- a/slic3r.pl +++ b/slic3r.pl @@ -8,17 +8,108 @@ BEGIN { use lib "$FindBin::Bin/lib"; } +use Getopt::Long; use Slic3r; use XXX; +my %opt; +GetOptions( + 'help' => sub { usage() }, + + 'debug' => \$Slic3r::debug, + 'o|output' => \$opt{output}, + + 'layer-height=f' => \$Slic3r::layer_height, + 'resolution=f' => \$Slic3r::resolution, + 'perimeters=i' => \$Slic3r::perimeter_offsets, + 'fill-density=f' => \$Slic3r::fill_density, + 'flow-width=f' => \$Slic3r::flow_width, + 'temperature=i' => \$Slic3r::temperature, + 'flow-rate=i' => \$Slic3r::flow_rate, + 'print-feed-rate=i' => \$Slic3r::print_feed_rate, + 'travel-feed-rate=i' => \$Slic3r::travel_feed_rate, + 'bottom-layer-speed-ratio=f' => \$Slic3r::bottom_layer_speed_ratio, + 'use-relative-e-distances' => \$Slic3r::use_relative_e_distances, + 'print-center=s' => \$Slic3r::print_center, + '', +); + +# validate configuration +{ + # --layer-height + die "Invalid value for --layer-height\n" + if $Slic3r::layer_height < 0; + die "--layer-height must be a multiple of print resolution\n" + if $Slic3r::layer_height / $Slic3r::resolution % 1 != 0; + + # --flow-width + die "Invalid value for --flow-width\n" + if $Slic3r::flow_width < 0; + die "--flow-width must be a multiple of print resolution\n" + if $Slic3r::flow_width / $Slic3r::resolution % 1 != 0; + + # --perimeters + die "Invalid value for --perimeters\n" + if $Slic3r::perimeter_offsets < 1; + + # --print-center + die "Invalid value for --print-center\n" + if !ref $Slic3r::print_center + && (!$Slic3r::print_center || $Slic3r::print_center !~ /^\d+,\d+$/); + $Slic3r::print_center = [ split /,/, $Slic3r::print_center ] + if !ref $Slic3r::print_center; + + # --fill-density + die "Invalid value for --fill-density\n" + if $Slic3r::fill_density < 0 || $Slic3r::fill_density > 1; +} + my $stl_parser = Slic3r::STL->new; -my $print = $stl_parser->parse_file("testcube20mm.stl"); +my $action = 'skein'; -$print->extrude_perimeters; -$print->extrude_fills; +if ($action eq 'skein') { + my $input_file = $ARGV[0] or usage(1); + die "Input file must have .stl extension\n" + if $input_file !~ /\.stl$/i; + + my $print = $stl_parser->parse_file($input_file); + $print->extrude_perimeters; + $print->extrude_fills; + + my $output_file = $input_file; + $output_file =~ s/\.stl$/.gcode/i; + $print->export_gcode($opt{output} || $output_file); +} -$print->export_gcode("testcube20mm.gcode"); +sub usage { + my ($exit_code) = @_; + + print <<"EOF"; +Usage: slic3r.pl [ OPTIONS ] file.stl -#XXX $print; + --help Output this usage screen and exit + --layer-height Layer height in mm (default: $Slic3r::layer_height) + --resolution Print resolution in mm (default: $Slic3r::resolution) + --perimeters Number of perimeters/horizontal skins + (range: 1+, default: $Slic3r::perimeter_offsets) + --fill-density Infill density (range: 0-1, default: $Slic3r::fill_density) + --flow-width Width of extruded flow in mm (default: $Slic3r::flow_width) + --flow-rate Speed of extrusion in mm/sec; should be equal to + --print-feed-rate (default: $Slic3r::flow_rate) + --print-feed-rate Speed of print moves in mm/sec (default: $Slic3r::print_feed_rate) + --travel-feed-rate Speed of non-print moves in mm/sec (default: $Slic3r::travel_feed_rate) + --bottom-layer-speed-ratio + Factor to increase/decrease speeds on bottom layer by + (default: $Slic3r::bottom_layer_speed_ratio) + --use-relative-e-distances + Use relative distances for extrusion in GCODE output + --print-center Coordinates of the point to center the print around + (default: 100,100) + --temperature Extrusion temperature (default: $Slic3r::temperature) + -o, --output File to output gcode to (default: .gcode) + +EOF + exit $exit_code || 0; +} __END__ diff --git a/testcube20mm.stl b/testcube20mm.stl deleted file mode 100644 index bf2e7e1678b2b04fab569ea9aa6d6f88f26ee6d3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 684 zcma)(O%8%E5QQ;OSKfiSLHF(;F5T$`5KBaU+LW^Jcs-WJh%*X0)sR3kq3yiy&2$>) zbz&~C+iyX&R<{;1qbi6?Ss$fYgE*;5iaJjVv5`$-m40n9RbpBgsZF9m+El2+S*EsG z1-~CeT^B|{01jjLyBY2uT}WpRz-PV$SKU!P?;U(%fv2~34XQ<+5e2FpQM4W>xax{7 z%f3^eAdlf5yxuf|tH=q1x^n+Dh72cohvNiS@wxc~&v1gzZJgk$Y43se`OIw^!BxB- Fz!O{qq$mIY