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', license => 'perl',
requires => { requires => {
'CAD::Format::STL' => '0', 'CAD::Format::STL' => '0',
'File::Basename' => '0',
'Getopt::Long' => '0', 'Getopt::Long' => '0',
'Math::Clipper' => '1.00', 'Math::Clipper' => '1.01',
'Math::Geometry::Planar' => '0', 'Math::Geometry::Planar' => '0',
'Moo' => '0', 'Moo' => '0',
'Time::HiRes' => '0', 'Time::HiRes' => '0',
'XXX' => '0',
}, },
build_requires => { build_requires => {
'Test::More' => '0.10', '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 perl Build.PL
This will install any required dependency. If you want to install slic3r.pl This will install any required dependency. If you want the GUI, you should
in your system path, type this as root: 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 ./Build install

View File

@ -8,6 +8,7 @@ sub debugf {
printf @_ if $debug; printf @_ if $debug;
} }
use Slic3r::Config;
use Slic3r::Extruder; use Slic3r::Extruder;
use Slic3r::ExtrusionLoop; use Slic3r::ExtrusionLoop;
use Slic3r::ExtrusionPath; use Slic3r::ExtrusionPath;
@ -21,6 +22,7 @@ use Slic3r::Point;
use Slic3r::Polyline; use Slic3r::Polyline;
use Slic3r::Polyline::Closed; use Slic3r::Polyline::Closed;
use Slic3r::Print; use Slic3r::Print;
use Slic3r::Skein;
use Slic3r::STL; use Slic3r::STL;
use Slic3r::Surface; use Slic3r::Surface;
use Slic3r::Surface::Collection; 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 Getopt::Long;
use Slic3r; use Slic3r;
use Time::HiRes qw(gettimeofday tv_interval);
use XXX; use XXX;
use constant PI => 4 * atan2(1, 1);
my %opt; my %opt;
GetOptions( GetOptions(
'help' => sub { usage() }, 'help' => sub { usage() },
@ -59,7 +56,7 @@ GetOptions(
'skirt-distance=i' => \$Slic3r::skirt_distance, 'skirt-distance=i' => \$Slic3r::skirt_distance,
# transform options # transform options
'scale=i' => \$Slic3r::scale, 'scale=f' => \$Slic3r::scale,
'rotate=i' => \$Slic3r::rotate, 'rotate=i' => \$Slic3r::rotate,
'multiply-x=i' => \$Slic3r::multiply_x, 'multiply-x=i' => \$Slic3r::multiply_x,
'multiply-y=i' => \$Slic3r::multiply_y, 'multiply-y=i' => \$Slic3r::multiply_y,
@ -67,89 +64,24 @@ GetOptions(
); );
# validate configuration # validate configuration
{ Slic3r::Config->validate;
# --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 # start GUI
die "Invalid value for --filament-diameter\n" if (!@ARGV && eval "require Slic3r::GUI; 1") {
if $Slic3r::filament_diameter < 1; Slic3r::GUI->new->MainLoop;
exit;
# --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;
} }
my $action = 'skein'; my $action = 'skein';
if ($action eq 'skein') { if ($action eq 'skein') {
my $input_file = $ARGV[0] or usage(1); 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 $skein = Slic3r::Skein->new(
my $print = Slic3r::Print->new_from_stl($input_file); input_file => $input_file,
$print->extrude_perimeters; output_file => $opt{output},
$print->remove_small_features; );
$print->extrude_fills; $skein->go;
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;
} }
sub usage { sub usage {