diff --git a/README.markdown b/README.markdown index 3abd61d7a..c5b3945f8 100644 --- a/README.markdown +++ b/README.markdown @@ -93,6 +93,7 @@ The author is Alessandro Ranellucci (me). and [input_filename] (default: [input_filename_base].gcode) --post-process Generated G-code will be processed with the supplied script; call this more than once to process through multiple scripts. + --export-svg Export a SVG file containing slices instead of G-code. Printer options: --nozzle-diameter Diameter of nozzle in mm (default: 0.5) diff --git a/lib/Slic3r/GUI.pm b/lib/Slic3r/GUI.pm index 39d3344c1..777ab0465 100644 --- a/lib/Slic3r/GUI.pm +++ b/lib/Slic3r/GUI.pm @@ -37,12 +37,14 @@ sub OnInit { $fileMenu->Append(3, "Slice…"); $fileMenu->Append(4, "Reslice"); $fileMenu->Append(5, "Slice and Save As…"); + $fileMenu->Append(6, "Export SVG…"); $menubar->Append($fileMenu, "&File"); EVT_MENU($frame, 1, sub { $panel->save_config }); EVT_MENU($frame, 2, sub { $panel->load_config }); EVT_MENU($frame, 3, sub { $panel->do_slice }); EVT_MENU($frame, 4, sub { $panel->do_slice(reslice => 1) }); EVT_MENU($frame, 5, sub { $panel->do_slice(save_as => 1) }); + EVT_MENU($frame, 6, sub { $panel->do_slice(save_as => 1, export_svg => 1) }); $box->SetSizeHints($frame); $frame->SetSizer($box); diff --git a/lib/Slic3r/GUI/SkeinPanel.pm b/lib/Slic3r/GUI/SkeinPanel.pm index bb6e6d24c..8972190b6 100644 --- a/lib/Slic3r/GUI/SkeinPanel.pm +++ b/lib/Slic3r/GUI/SkeinPanel.pm @@ -222,7 +222,8 @@ sub do_slice { } } elsif ($params{save_as}) { my $output_file = $skein->expanded_output_filepath; - my $dlg = Wx::FileDialog->new($self, 'Save gcode file as:', dirname($output_file), + $output_file =~ s/\.gcode$/.svg/i if $params{export_svg}; + my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:', dirname($output_file), basename($output_file), $gcode_wildcard, wxFD_SAVE); return if $dlg->ShowModal != wxID_OK; $skein->output_file($dlg->GetPath); @@ -237,15 +238,22 @@ sub do_slice { { my @warnings = (); local $SIG{__WARN__} = sub { push @warnings, $_[0] }; - $skein->go; + if ($params{export_svg}) { + $skein->export_svg; + } else { + $skein->go; + } $self->catch_warning->($_) for @warnings; } $process_dialog->Destroy; undef $process_dialog; - my $message = sprintf "%s was successfully sliced in %d minutes and %.3f seconds.", - $input_file_basename, int($skein->processing_time/60), - $skein->processing_time - int($skein->processing_time/60)*60; + my $message = "$input_file_basename was successfully sliced"; + $message .= sprintf " in %d minutes and %.3f seconds", + int($skein->processing_time/60), + $skein->processing_time - int($skein->processing_time/60)*60 + if $skein->processing_time; + $message .= "."; eval { $self->{growler}->notify(Event => 'SKEIN_DONE', Title => 'Slicing Done!', Message => $message) if ($self->{growler}); diff --git a/lib/Slic3r/Skein.pm b/lib/Slic3r/Skein.pm index 32d913d8d..32ad50d26 100644 --- a/lib/Slic3r/Skein.pm +++ b/lib/Slic3r/Skein.pm @@ -3,7 +3,7 @@ use Moo; use Config; use File::Basename qw(basename fileparse); -use Slic3r::Geometry qw(PI); +use Slic3r::Geometry qw(PI unscale); use Time::HiRes qw(gettimeofday tv_interval); use XXX; @@ -17,14 +17,9 @@ has 'output_file' => (is => 'rw', required => 0); has 'status_cb' => (is => 'rw', required => 0, default => sub { sub {} }); has 'processing_time' => (is => 'rw', required => 0); -sub go { +sub slice_input { my $self = shift; - my $t0 = [gettimeofday]; - # skein the STL into layers - # each layer has surfaces with holes - $self->status_cb->(5, "Processing input file " . $self->input_file); - $self->status_cb->(10, "Processing triangulated mesh"); my $print; if ($self->input_file =~ /\.stl$/i) { my $mesh = Slic3r::Format::STL->read_file($self->input_file); @@ -37,6 +32,17 @@ sub go { } else { die "Input file must have .stl or .amf(.xml) extension\n"; } +} + +sub go { + my $self = shift; + my $t0 = [gettimeofday]; + + # skein the STL into layers + # each layer has surfaces with holes + $self->status_cb->(5, "Processing input file " . $self->input_file); + $self->status_cb->(10, "Processing triangulated mesh"); + my $print = $self->slice_input; # make perimeters # this will add a set of extrusion loops to each layer @@ -152,6 +158,49 @@ sub go { $print->total_extrusion_length, $print->total_extrusion_volume; } +sub export_svg { + my $self = shift; + + my $print = $self->slice_input; + my $output_file = $self->expanded_output_filepath; + $output_file =~ s/\.gcode$/.svg/i; + + open my $fh, ">", $output_file or die "Failed to open $output_file for writing\n"; + print "Exporting to $output_file..."; + print $fh sprintf <<"EOF", unscale($print->total_x_length), unscale($print->total_y_length); + + + +EOF + + my $print_polygon = sub { + my ($polygon, $fill) = @_; + printf $fh qq{ }, + (join ' ', map { join ',', map unscale $_, @$_ } @$polygon), $fill; + }; + + foreach my $layer (@{$print->layers}) { + printf $fh qq{ \n}, $layer->id; + # sort slices so that the outermost ones come first + my @slices = sort { $a->expolygon->contour->encloses_point($b->expolygon->contour->[0]) ? 0 : 1 } @{$layer->slices}; + foreach my $slice (@slices) { + $print_polygon->($slice->expolygon->contour, 'black'); + $print_polygon->($_, 'white') for $slice->expolygon->holes; + } + print $fh qq{ \n}; + } + + print $fh <<"EOF"; + + +EOF + close $fh; + print "Done.\n"; +} + # this method will return the value of $self->output_file after expanding its # format variables with their values sub expanded_output_filepath { diff --git a/slic3r.pl b/slic3r.pl index 567ac0bdd..5bf6b778a 100755 --- a/slic3r.pl +++ b/slic3r.pl @@ -26,6 +26,7 @@ my %cli_options = (); 'load=s@' => \$opt{load}, 'ignore-nonexistent-config' => \$opt{ignore_nonexistent_config}, 'threads|j=i' => \$Slic3r::threads, + 'export-svg' => \$opt{export_svg}, ); foreach my $opt_key (keys %$Slic3r::Config::Options) { my $opt = $Slic3r::Config::Options->{$opt_key}; @@ -81,7 +82,11 @@ if (@ARGV) { printf "=> $message\n"; }, ); - $skein->go; + if ($opt{export_svg}) { + $skein->export_svg; + } else { + $skein->go; + } } } else { usage(1) unless $opt{save}; @@ -111,6 +116,7 @@ Usage: slic3r.pl [ OPTIONS ] file.stl and [input_filename] (default: $Slic3r::output_filename_format) --post-process Generated G-code will be processed with the supplied script; call this more than once to process through multiple scripts. + --export-svg Export a SVG file containing slices instead of G-code. Printer options: --nozzle-diameter Diameter of nozzle in mm (default: $Slic3r::nozzle_diameter)