This commit is contained in:
Alessandro Ranellucci 2011-10-03 11:55:32 +02:00
parent a311220c19
commit f2f9178e07
9 changed files with 478 additions and 83 deletions

View File

@ -8,11 +8,13 @@ my $build = Module::Build->new(
license => 'perl',
requires => {
'CAD::Format::STL' => '0',
'File::Basename' => '0',
'Getopt::Long' => '0',
'Math::Clipper' => '1.00',
'Math::Clipper' => '1.01',
'Math::Geometry::Planar' => '0',
'Moo' => '0',
'Time::HiRes' => '0',
'XXX' => '0',
},
build_requires => {
'Test::More' => '0.10',

View File

@ -63,8 +63,12 @@ Download the package, open a terminal and cd to its directory. Then type:
perl Build.PL
This will install any required dependency. If you want to install slic3r.pl
in your system path, type this as root:
This will install any required dependency. If you want the GUI, you should
also install Wx using the following command (as root):
cpan Wx
If you want to install slic3r.pl in your system path, type this as root:
./Build install

View File

@ -8,6 +8,7 @@ sub debugf {
printf @_ if $debug;
}
use Slic3r::Config;
use Slic3r::Extruder;
use Slic3r::ExtrusionLoop;
use Slic3r::ExtrusionPath;
@ -21,6 +22,7 @@ use Slic3r::Point;
use Slic3r::Polyline;
use Slic3r::Polyline::Closed;
use Slic3r::Print;
use Slic3r::Skein;
use Slic3r::STL;
use Slic3r::Surface;
use Slic3r::Surface::Collection;

72
lib/Slic3r/Config.pm Normal file
View File

@ -0,0 +1,72 @@
package Slic3r::Config;
use strict;
use warnings;
use constant PI => 4 * atan2(1, 1);
sub validate {
my $class = shift;
# --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;
# --filament-diameter
die "Invalid value for --filament-diameter\n"
if $Slic3r::filament_diameter < 1;
# --nozzle-diameter
die "Invalid value for --nozzle-diameter\n"
if $Slic3r::nozzle_diameter < 0;
die "--layer-height can't be greater than --nozzle-diameter\n"
if $Slic3r::layer_height > $Slic3r::nozzle_diameter;
$Slic3r::flow_width = ($Slic3r::nozzle_diameter**2)
* $Slic3r::thickness_ratio * PI / (4 * $Slic3r::layer_height);
my $max_flow_width = $Slic3r::layer_height + $Slic3r::nozzle_diameter;
if ($Slic3r::flow_width > $max_flow_width) {
$Slic3r::thickness_ratio = $max_flow_width / $Slic3r::flow_width;
$Slic3r::flow_width = $max_flow_width;
}
Slic3r::debugf "Flow width = $Slic3r::flow_width\n";
# --perimeters
die "Invalid value for --perimeters\n"
if $Slic3r::perimeter_offsets < 1;
# --solid-layers
die "Invalid value for --solid-layers\n"
if $Slic3r::solid_layers < 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;
# --scale
die "Invalid value for --scale\n"
if $Slic3r::scale <= 0;
# --multiply-x
die "Invalid value for --multiply-x\n"
if $Slic3r::multiply_x < 1;
# --multiply-y
die "Invalid value for --multiply-y\n"
if $Slic3r::multiply_y < 1;
# --multiply-distance
die "Invalid value for --multiply-distance\n"
if $Slic3r::multiply_distance < 1;
}
1;

44
lib/Slic3r/GUI.pm Normal file
View File

@ -0,0 +1,44 @@
package Slic3r::GUI;
use strict;
use warnings;
use Slic3r::GUI::OptionsGroup;
use Slic3r::GUI::SkeinPanel;
use Wx qw(:sizer :frame wxID_EXIT wxID_ABOUT);
use Wx::Event qw(EVT_MENU);
use base 'Wx::App';
sub OnInit {
my $self = shift;
#$self->SetIcon(Wx::Icon->new("path/to/my/icon.gif", wxBITMAP_TYPE_GIF) );
my $frame = Wx::Frame->new( undef, -1, 'Slic3r', [-1, -1], Wx::wxDefaultSize,
wxDEFAULT_FRAME_STYLE ^ (wxRESIZE_BORDER | wxMAXIMIZE_BOX) );
# menubar
my $menubar = Wx::MenuBar->new;
$frame->SetMenuBar($menubar);
EVT_MENU($frame, wxID_EXIT, sub {$_[0]->Close(1)});
EVT_MENU($frame, wxID_ABOUT, \&About);
my $panel = Slic3r::GUI::SkeinPanel->new($frame);
my $box = Wx::BoxSizer->new(wxVERTICAL);
$box->Add($panel, 0, wxALL, 20);
$frame->SetSizerAndFit($box);
$frame->Show;
}
sub About {
my $frame = shift;
my $info = Wx::AboutDialogInfo->new;
$info->SetName('Slic3r');
$info->AddDeveloper('Alessandro Ranellucci');
Wx::AboutBox($info);
}
1;

View File

@ -0,0 +1,50 @@
package Slic3r::GUI::OptionsGroup;
use strict;
use warnings;
use Wx qw(:sizer);
use Wx::Event qw(EVT_TEXT EVT_CHECKBOX);
use base 'Wx::StaticBoxSizer';
sub new {
my $class = shift;
my ($parent, %p) = @_;
my $box = Wx::StaticBox->new($parent, -1, $p{title});
my $self = $class->SUPER::new($box, wxVERTICAL);
my $grid_sizer = Wx::FlexGridSizer->new(scalar(@{$p{options}}), 2, 2, 0);
foreach my $opt (@{$p{options}}) {
my $label = Wx::StaticText->new($parent, -1, "$opt->{label}:", Wx::wxDefaultPosition, [180,-1]);
my $field;
if ($opt->{type} =~ /^(i|f)$/) {
$field = Wx::TextCtrl->new($parent, -1, ${$opt->{value}});
EVT_TEXT($parent, $field, sub { ${$opt->{value}} = $field->GetValue });
} elsif ($opt->{type} eq 'bool') {
$field = Wx::CheckBox->new($parent, -1, "");
$field->SetValue(${$opt->{value}});
EVT_TEXT($parent, $field, sub { ${$opt->{value}} = $field->GetValue });
} elsif ($opt->{type} eq 'point') {
$field = Wx::BoxSizer->new(wxHORIZONTAL);
my $field_size = Wx::Size->new(40, -1);
$field->Add($_) for (
Wx::StaticText->new($parent, -1, "x:"),
my $x_field = Wx::TextCtrl->new($parent, -1, ${$opt->{value}}->[0], Wx::wxDefaultPosition, $field_size),
Wx::StaticText->new($parent, -1, " y:"),
my $y_field = Wx::TextCtrl->new($parent, -1, ${$opt->{value}}->[1], Wx::wxDefaultPosition, $field_size),
);
EVT_TEXT($parent, $x_field, sub { ${$opt->{value}}->[0] = $x_field->GetValue });
EVT_TEXT($parent, $y_field, sub { ${$opt->{value}}->[1] = $y_field->GetValue });
} else {
die "Unsupported option type: " . $opt->{type};
}
$grid_sizer->Add($_) for $label, $field;
}
$self->Add($grid_sizer, 0, wxEXPAND);
return $self;
}
1;

View File

@ -0,0 +1,255 @@
package Slic3r::GUI::SkeinPanel;
use strict;
use warnings;
use File::Basename qw(basename);
use Wx qw(:sizer :progressdialog wxOK wxICON_INFORMATION wxICON_ERROR wxID_OK wxFD_OPEN);
use Wx::Event qw(EVT_BUTTON);
use base 'Wx::Panel';
sub new {
my $class = shift;
my ($parent) = @_;
my $self = $class->SUPER::new($parent, -1);
my %panels = (
printer => Slic3r::GUI::OptionsGroup->new($self,
title => 'Printer',
options => [
{
label => 'Nozzle diameter',
value => \$Slic3r::nozzle_diameter,
type => 'f',
},
{
label => 'Print center',
value => \$Slic3r::print_center,
type => 'point',
},
{
label => 'Use relative E distances',
value => \$Slic3r::use_relative_e_distances,
type => 'bool',
},
{
label => 'Z offset',
value => \$Slic3r::z_offset,
type => 'f',
},
],
),
filament => Slic3r::GUI::OptionsGroup->new($self,
title => 'Filament',
options => [
{
label => 'Diameter (mm)',
value => \$Slic3r::filament_diameter,
type => 'f',
},
{
label => 'Packing density (mm)',
value => \$Slic3r::filament_packing_density,
type => 'f',
},
],
),
speed => Slic3r::GUI::OptionsGroup->new($self,
title => 'Speed',
options => [
{
label => 'Print feed rate (mm/s)',
value => \$Slic3r::print_feed_rate,
type => 'f',
},
{
label => 'Travel feed rate (mm/s)',
value => \$Slic3r::travel_feed_rate,
type => 'f',
},
{
label => 'Perimeter feed rate (mm/s)',
value => \$Slic3r::perimeter_feed_rate,
type => 'f',
},
{
label => 'Bottom layer ratio',
value => \$Slic3r::bottom_layer_speed_ratio,
type => 'f',
},
],
),
accuracy => Slic3r::GUI::OptionsGroup->new($self,
title => 'Accuracy',
options => [
{
label => 'Layer height (mm)',
value => \$Slic3r::layer_height,
type => 'f',
},
],
),
print => Slic3r::GUI::OptionsGroup->new($self,
title => 'Print settings',
options => [
{
label => 'Perimeters',
value => \$Slic3r::perimeter_offsets,
type => 'i',
},
{
label => 'Solid layers',
value => \$Slic3r::solid_layers,
type => 'i',
},
{
label => 'Fill density',
value => \$Slic3r::fill_density,
type => 'f',
},
{
label => 'Fill angle (°)',
value => \$Slic3r::fill_angle,
type => 'i',
},
{
label => 'Temperature (°C)',
value => \$Slic3r::temperature,
type => 'i',
},
],
),
retract => Slic3r::GUI::OptionsGroup->new($self,
title => 'Retraction',
options => [
{
label => 'Length (mm)',
value => \$Slic3r::retract_length,
type => 'f',
},
{
label => 'Speed (mm/s)',
value => \$Slic3r::retract_speed,
type => 'i',
},
{
label => 'Extra length on restart (mm)',
value => \$Slic3r::retract_restart_extra,
type => 'f',
},
{
label => 'Minimum travel after retraction (mm)',
value => \$Slic3r::retract_before_travel,
type => 'f',
},
],
),
skirt => Slic3r::GUI::OptionsGroup->new($self,
title => 'Skirt',
options => [
{
label => 'Loops',
value => \$Slic3r::skirts,
type => 'i',
},
{
label => 'Distance from object (mm)',
value => \$Slic3r::skirt_distance,
type => 'i',
},
],
),
transform => Slic3r::GUI::OptionsGroup->new($self,
title => 'Transform',
options => [
{
label => 'Scale',
value => \$Slic3r::scale,
type => 'f',
},
{
label => 'Rotate (°)',
value => \$Slic3r::rotate,
type => 'i',
},
{
label => 'Multiply along X',
value => \$Slic3r::multiply_x,
type => 'i',
},
{
label => 'Multiply along Y',
value => \$Slic3r::multiply_y,
type => 'i',
},
{
label => 'Multiply distance',
value => \$Slic3r::multiply_distance,
type => 'i',
},
],
),
);
$panels{slice} = Wx::BoxSizer->new(wxVERTICAL);
$panels{slice}->Add(-1, 20); # empty space before button
my $slice_button = Wx::Button->new($self, -1, "Slice...");
$panels{slice}->Add($slice_button, 0, wxALIGN_CENTER);
EVT_BUTTON($self, $slice_button, \&do_slice);
my @cols = (
[qw(printer filament speed transform)], [qw(accuracy print retract skirt slice)],
);
my $sizer = Wx::BoxSizer->new(wxHORIZONTAL);
foreach my $col (@cols) {
my $vertical_sizer = Wx::BoxSizer->new(wxVERTICAL);
$vertical_sizer->Add($panels{$_}, 0, wxEXPAND | wxRIGHT, 10) for @$col;
$sizer->Add($vertical_sizer);
}
$sizer->SetSizeHints($self);
$self->SetSizer($sizer);
return $self;
}
sub do_slice {
my $self = shift;
eval {
# validate configuration
Slic3r::Config->validate;
# select input file
my $dialog = Wx::FileDialog->new($self, 'Choose a STL file to slice:', "", "", "*.stl", wxFD_OPEN);
return unless $dialog->ShowModal == wxID_OK;
my ($input_file) = $dialog->GetPaths;
my $input_file_basename = basename($input_file);
# show processbar dialog
my $process_dialog = Wx::ProgressDialog->new('Slicing...', "Processing $input_file_basename...",
100, $self, wxPD_APP_MODAL);
$process_dialog->Pulse;
my $skein = Slic3r::Skein->new(
input_file => $input_file,
);
$skein->go;
$process_dialog->Destroy;
Wx::MessageDialog->new($self, "$input_file_basename was successfully sliced.", 'Done!',
wxOK | wxICON_INFORMATION)->ShowModal;
};
if (my $err = $@) {
Wx::MessageDialog->new($self, $err, 'Error', wxOK | wxICON_ERROR)->ShowModal;
}
}
1;

34
lib/Slic3r/Skein.pm Normal file
View File

@ -0,0 +1,34 @@
package Slic3r::Skein;
use Moo;
use Time::HiRes qw(gettimeofday tv_interval);
has 'input_file' => (is => 'ro', required => 1);
has 'output_file' => (is => 'rw', required => 0);
sub go {
my $self = shift;
die "Input file must have .stl extension\n"
if $self->input_file !~ /\.stl$/i;
my $t0 = [gettimeofday];
my $print = Slic3r::Print->new_from_stl($self->input_file);
$print->extrude_perimeters;
$print->remove_small_features;
$print->extrude_fills;
if (!$self->output_file) {
my $output_file = $self->input_file;
$output_file =~ s/\.stl$/.gcode/i;
$self->output_file($output_file);
}
$print->export_gcode($self->output_file);
my $processing_time = tv_interval($t0);
printf "Done. Process took %d minutes and %.3f seconds\n",
int($processing_time/60), $processing_time - int($processing_time/60)*60;
}
1;

View File

@ -10,11 +10,8 @@ BEGIN {
use Getopt::Long;
use Slic3r;
use Time::HiRes qw(gettimeofday tv_interval);
use XXX;
use constant PI => 4 * atan2(1, 1);
my %opt;
GetOptions(
'help' => sub { usage() },
@ -59,7 +56,7 @@ GetOptions(
'skirt-distance=i' => \$Slic3r::skirt_distance,
# transform options
'scale=i' => \$Slic3r::scale,
'scale=f' => \$Slic3r::scale,
'rotate=i' => \$Slic3r::rotate,
'multiply-x=i' => \$Slic3r::multiply_x,
'multiply-y=i' => \$Slic3r::multiply_y,
@ -67,89 +64,24 @@ GetOptions(
);
# 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;
Slic3r::Config->validate;
# --filament-diameter
die "Invalid value for --filament-diameter\n"
if $Slic3r::filament_diameter < 1;
# --nozzle-diameter
die "Invalid value for --nozzle-diameter\n"
if $Slic3r::nozzle_diameter < 0;
die "--layer-height can't be greater than --nozzle-diameter\n"
if $Slic3r::layer_height > $Slic3r::nozzle_diameter;
$Slic3r::flow_width = ($Slic3r::nozzle_diameter**2)
* $Slic3r::thickness_ratio * PI / (4 * $Slic3r::layer_height);
my $max_flow_width = $Slic3r::layer_height + $Slic3r::nozzle_diameter;
if ($Slic3r::flow_width > $max_flow_width) {
$Slic3r::thickness_ratio = $max_flow_width / $Slic3r::flow_width;
$Slic3r::flow_width = $max_flow_width;
}
Slic3r::debugf "Flow width = $Slic3r::flow_width\n";
# --perimeters
die "Invalid value for --perimeters\n"
if $Slic3r::perimeter_offsets < 1;
# --solid-layers
die "Invalid value for --solid-layers\n"
if $Slic3r::solid_layers < 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;
# --scale
die "Invalid value for --scale\n"
if $Slic3r::scale <= 0;
# --multiply-x
die "Invalid value for --multiply-x\n"
if $Slic3r::multiply_x < 1;
# --multiply-y
die "Invalid value for --multiply-y\n"
if $Slic3r::multiply_y < 1;
# --multiply-distance
die "Invalid value for --multiply-distance\n"
if $Slic3r::multiply_distance < 1;
# start GUI
if (!@ARGV && eval "require Slic3r::GUI; 1") {
Slic3r::GUI->new->MainLoop;
exit;
}
my $action = 'skein';
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 $t0 = [gettimeofday];
my $print = Slic3r::Print->new_from_stl($input_file);
$print->extrude_perimeters;
$print->remove_small_features;
$print->extrude_fills;
my $output_file = $input_file;
$output_file =~ s/\.stl$/.gcode/i;
$print->export_gcode($opt{output} || $output_file);
my $processing_time = tv_interval($t0);
printf "Done. Process took %d minutes and %.3f seconds\n",
int($processing_time/60), $processing_time - int($processing_time/60)*60;
my $skein = Slic3r::Skein->new(
input_file => $input_file,
output_file => $opt{output},
);
$skein->go;
}
sub usage {