New command to combine multiple STL files into a single multi-material AMF file
This commit is contained in:
parent
bbe0a45b58
commit
77625894ff
@ -21,6 +21,7 @@ use constant MI_QUICK_SLICE => &Wx::NewId;
|
|||||||
use constant MI_REPEAT_QUICK => &Wx::NewId;
|
use constant MI_REPEAT_QUICK => &Wx::NewId;
|
||||||
use constant MI_QUICK_SAVE_AS => &Wx::NewId;
|
use constant MI_QUICK_SAVE_AS => &Wx::NewId;
|
||||||
use constant MI_SLICE_SVG => &Wx::NewId;
|
use constant MI_SLICE_SVG => &Wx::NewId;
|
||||||
|
use constant MI_COMBINE_STLS => &Wx::NewId;
|
||||||
|
|
||||||
use constant MI_PLATER_EXPORT_GCODE => &Wx::NewId;
|
use constant MI_PLATER_EXPORT_GCODE => &Wx::NewId;
|
||||||
use constant MI_PLATER_EXPORT_STL => &Wx::NewId;
|
use constant MI_PLATER_EXPORT_STL => &Wx::NewId;
|
||||||
@ -90,6 +91,8 @@ sub OnInit {
|
|||||||
$fileMenu->AppendSeparator();
|
$fileMenu->AppendSeparator();
|
||||||
$fileMenu->Append(MI_SLICE_SVG, "Slice to SV&G…\tCtrl+G", 'Slice file to SVG');
|
$fileMenu->Append(MI_SLICE_SVG, "Slice to SV&G…\tCtrl+G", 'Slice file to SVG');
|
||||||
$fileMenu->AppendSeparator();
|
$fileMenu->AppendSeparator();
|
||||||
|
$fileMenu->Append(MI_COMBINE_STLS, "Combine multi-material STL files…", 'Combine multiple STL files into a single multi-material AMF file');
|
||||||
|
$fileMenu->AppendSeparator();
|
||||||
$fileMenu->Append(wxID_EXIT, "&Quit", 'Quit Slic3r');
|
$fileMenu->Append(wxID_EXIT, "&Quit", 'Quit Slic3r');
|
||||||
EVT_MENU($frame, MI_LOAD_CONF, sub { $self->{skeinpanel}->load_config_file });
|
EVT_MENU($frame, MI_LOAD_CONF, sub { $self->{skeinpanel}->load_config_file });
|
||||||
EVT_MENU($frame, MI_EXPORT_CONF, sub { $self->{skeinpanel}->export_config });
|
EVT_MENU($frame, MI_EXPORT_CONF, sub { $self->{skeinpanel}->export_config });
|
||||||
@ -99,6 +102,7 @@ sub OnInit {
|
|||||||
EVT_MENU($frame, MI_QUICK_SAVE_AS, sub { $self->{skeinpanel}->do_slice(save_as => 1);
|
EVT_MENU($frame, MI_QUICK_SAVE_AS, sub { $self->{skeinpanel}->do_slice(save_as => 1);
|
||||||
$repeat->Enable(defined $Slic3r::GUI::SkeinPanel::last_input_file) });
|
$repeat->Enable(defined $Slic3r::GUI::SkeinPanel::last_input_file) });
|
||||||
EVT_MENU($frame, MI_SLICE_SVG, sub { $self->{skeinpanel}->do_slice(save_as => 1, export_svg => 1) });
|
EVT_MENU($frame, MI_SLICE_SVG, sub { $self->{skeinpanel}->do_slice(save_as => 1, export_svg => 1) });
|
||||||
|
EVT_MENU($frame, MI_COMBINE_STLS, sub { $self->{skeinpanel}->combine_stls });
|
||||||
EVT_MENU($frame, wxID_EXIT, sub {$_[0]->Close(0)});
|
EVT_MENU($frame, wxID_EXIT, sub {$_[0]->Close(0)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,7 +502,7 @@ sub export_gcode {
|
|||||||
{
|
{
|
||||||
$self->{output_file} = $print->expanded_output_filepath($self->{output_file}, $self->{objects}[0]->input_file);
|
$self->{output_file} = $print->expanded_output_filepath($self->{output_file}, $self->{objects}[0]->input_file);
|
||||||
my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', dirname($self->{output_file}),
|
my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', dirname($self->{output_file}),
|
||||||
basename($self->{output_file}), $Slic3r::GUI::SkeinPanel::gcode_wildcard, wxFD_SAVE);
|
basename($self->{output_file}), &Slic3r::GUI::SkeinPanel::FILE_WILDCARDS->{gcode}, wxFD_SAVE);
|
||||||
if ($dlg->ShowModal != wxID_OK) {
|
if ($dlg->ShowModal != wxID_OK) {
|
||||||
$dlg->Destroy;
|
$dlg->Destroy;
|
||||||
return;
|
return;
|
||||||
@ -654,7 +654,7 @@ sub _get_export_file {
|
|||||||
$output_file = $self->_init_print->expanded_output_filepath($output_file, $self->{objects}[0]->input_file);
|
$output_file = $self->_init_print->expanded_output_filepath($output_file, $self->{objects}[0]->input_file);
|
||||||
$output_file =~ s/\.gcode$/$suffix/i;
|
$output_file =~ s/\.gcode$/$suffix/i;
|
||||||
my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file),
|
my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file),
|
||||||
basename($output_file), $Slic3r::GUI::SkeinPanel::model_wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
basename($output_file), &Slic3r::GUI::SkeinPanel::MODEL_WILDCARD, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||||
if ($dlg->ShowModal != wxID_OK) {
|
if ($dlg->ShowModal != wxID_OK) {
|
||||||
$dlg->Destroy;
|
$dlg->Destroy;
|
||||||
return undef;
|
return undef;
|
||||||
|
@ -13,6 +13,16 @@ our $last_input_file;
|
|||||||
our $last_output_file;
|
our $last_output_file;
|
||||||
our $last_config;
|
our $last_config;
|
||||||
|
|
||||||
|
use constant FILE_WILDCARDS => {
|
||||||
|
stl => 'STL files (*.stl)|*.stl;*.STL',
|
||||||
|
obj => 'OBJ files (*.obj)|*.obj;*.OBJ',
|
||||||
|
amf => 'AMF files (*.amf)|*.amf;*.AMF;*.xml;*.XML',
|
||||||
|
ini => 'INI files *.ini|*.ini;*.INI',
|
||||||
|
gcode => 'G-code files *.gcode|*.gcode;*.GCODE|G-code files *.g|*.g;*.G',
|
||||||
|
svg => 'SVG files *.svg|*.svg;*.SVG',
|
||||||
|
};
|
||||||
|
use constant MODEL_WILDCARD => join '|', @{&FILE_WILDCARDS}{qw(stl obj amf)};
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
my $class = shift;
|
my $class = shift;
|
||||||
my ($parent) = @_;
|
my ($parent) = @_;
|
||||||
@ -41,11 +51,6 @@ sub new {
|
|||||||
return $self;
|
return $self;
|
||||||
}
|
}
|
||||||
|
|
||||||
our $model_wildcard = "STL files (*.stl)|*.stl;*.STL|OBJ files (*.obj)|*.obj;*.OBJ|AMF files (*.amf)|*.amf;*.AMF;*.xml;*.XML";
|
|
||||||
our $ini_wildcard = "INI files *.ini|*.ini;*.INI";
|
|
||||||
our $gcode_wildcard = "G-code files *.gcode|*.gcode;*.GCODE|G-code files *.g|*.g;*.G";
|
|
||||||
our $svg_wildcard = "SVG files *.svg|*.svg;*.SVG";
|
|
||||||
|
|
||||||
sub do_slice {
|
sub do_slice {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
my %params = @_;
|
my %params = @_;
|
||||||
@ -70,7 +75,7 @@ sub do_slice {
|
|||||||
|
|
||||||
my $input_file;
|
my $input_file;
|
||||||
if (!$params{reslice}) {
|
if (!$params{reslice}) {
|
||||||
my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF):', $dir, "", $model_wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
my $dialog = Wx::FileDialog->new($self, 'Choose a file to slice (STL/OBJ/AMF):', $dir, "", MODEL_WILDCARD, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||||
if ($dialog->ShowModal != wxID_OK) {
|
if ($dialog->ShowModal != wxID_OK) {
|
||||||
$dialog->Destroy;
|
$dialog->Destroy;
|
||||||
return;
|
return;
|
||||||
@ -107,7 +112,7 @@ sub do_slice {
|
|||||||
$output_file = $print->expanded_output_filepath($output_file);
|
$output_file = $print->expanded_output_filepath($output_file);
|
||||||
$output_file =~ s/\.gcode$/.svg/i if $params{export_svg};
|
$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),
|
my $dlg = Wx::FileDialog->new($self, 'Save ' . ($params{export_svg} ? 'SVG' : 'G-code') . ' file as:', dirname($output_file),
|
||||||
basename($output_file), $params{export_svg} ? $svg_wildcard : $gcode_wildcard, wxFD_SAVE);
|
basename($output_file), $params{export_svg} ? FILE_WILDCARDS->{svg} : FILE_WILDCARDS->{gcode}, wxFD_SAVE);
|
||||||
if ($dlg->ShowModal != wxID_OK) {
|
if ($dlg->ShowModal != wxID_OK) {
|
||||||
$dlg->Destroy;
|
$dlg->Destroy;
|
||||||
return;
|
return;
|
||||||
@ -172,7 +177,7 @@ sub export_config {
|
|||||||
my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
|
my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
|
||||||
my $filename = $last_config ? basename($last_config) : "config.ini";
|
my $filename = $last_config ? basename($last_config) : "config.ini";
|
||||||
my $dlg = Wx::FileDialog->new($self, 'Save configuration as:', $dir, $filename,
|
my $dlg = Wx::FileDialog->new($self, 'Save configuration as:', $dir, $filename,
|
||||||
$ini_wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
FILE_WILDCARDS->{ini}, wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
|
||||||
if ($dlg->ShowModal == wxID_OK) {
|
if ($dlg->ShowModal == wxID_OK) {
|
||||||
my $file = $dlg->GetPath;
|
my $file = $dlg->GetPath;
|
||||||
$Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
|
$Slic3r::GUI::Settings->{recent}{config_directory} = dirname($file);
|
||||||
@ -191,7 +196,7 @@ sub load_config_file {
|
|||||||
return unless $self->check_unsaved_changes;
|
return unless $self->check_unsaved_changes;
|
||||||
my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
|
my $dir = $last_config ? dirname($last_config) : $Slic3r::GUI::Settings->{recent}{config_directory} || $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
|
||||||
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini",
|
my $dlg = Wx::FileDialog->new($self, 'Select configuration to load:', $dir, "config.ini",
|
||||||
$ini_wildcard, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
FILE_WILDCARDS->{ini}, wxFD_OPEN | wxFD_FILE_MUST_EXIST);
|
||||||
return unless $dlg->ShowModal == wxID_OK;
|
return unless $dlg->ShowModal == wxID_OK;
|
||||||
($file) = $dlg->GetPaths;
|
($file) = $dlg->GetPaths;
|
||||||
$dlg->Destroy;
|
$dlg->Destroy;
|
||||||
@ -221,6 +226,58 @@ sub config_wizard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub combine_stls {
|
||||||
|
my $self = shift;
|
||||||
|
|
||||||
|
# get input files
|
||||||
|
my @input_files = ();
|
||||||
|
my $dir = $Slic3r::GUI::Settings->{recent}{skein_directory} || '';
|
||||||
|
{
|
||||||
|
my $dlg_message = 'Choose one or more files to combine (STL/OBJ)';
|
||||||
|
while (1) {
|
||||||
|
my $dialog = Wx::FileDialog->new($self, "$dlg_message:", $dir, "", MODEL_WILDCARD,
|
||||||
|
wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);
|
||||||
|
if ($dialog->ShowModal != wxID_OK) {
|
||||||
|
$dialog->Destroy;
|
||||||
|
last;
|
||||||
|
}
|
||||||
|
push @input_files, $dialog->GetPaths;
|
||||||
|
$dialog->Destroy;
|
||||||
|
$dlg_message .= " or hit Cancel if you have finished";
|
||||||
|
$dir = dirname($input_files[0]);
|
||||||
|
}
|
||||||
|
return if !@input_files;
|
||||||
|
}
|
||||||
|
|
||||||
|
# get output file
|
||||||
|
my $output_file = $input_files[0];
|
||||||
|
{
|
||||||
|
$output_file =~ s/\.(?:stl|obj)$/.amf.xml/i;
|
||||||
|
my $dlg = Wx::FileDialog->new($self, 'Save multi-material AMF file as:', dirname($output_file),
|
||||||
|
basename($output_file), FILE_WILDCARDS->{amf}, wxFD_SAVE);
|
||||||
|
if ($dlg->ShowModal != wxID_OK) {
|
||||||
|
$dlg->Destroy;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$output_file = $dlg->GetPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
my @models = map Slic3r::Model->read_from_file($_), @input_files;
|
||||||
|
my $new_model = Slic3r::Model->new;
|
||||||
|
my $new_object = $new_model->add_object;
|
||||||
|
for my $m (0 .. $#models) {
|
||||||
|
my $model = $models[$m];
|
||||||
|
$new_model->set_material($m, { Name => basename($input_files[$m]) });
|
||||||
|
$new_object->add_volume(
|
||||||
|
material_id => $m,
|
||||||
|
facets => $model->objects->[0]->volumes->[0]->facets,
|
||||||
|
vertices => $model->objects->[0]->vertices,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Slic3r::Format::AMF->write_file($output_file, $new_model);
|
||||||
|
}
|
||||||
|
|
||||||
=head2 config
|
=head2 config
|
||||||
|
|
||||||
This method collects all config values from the tabs and merges them into a single config object.
|
This method collects all config values from the tabs and merges them into a single config object.
|
||||||
|
@ -77,8 +77,20 @@ has 'instances' => (is => 'rw');
|
|||||||
|
|
||||||
sub add_volume {
|
sub add_volume {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
|
my %args = @_;
|
||||||
|
|
||||||
my $volume = Slic3r::Model::Volume->new(object => $self, @_);
|
if (my $vertices = delete $args{vertices}) {
|
||||||
|
my $v_offset = @{$self->vertices};
|
||||||
|
push @{$self->vertices}, @$vertices;
|
||||||
|
|
||||||
|
@{$args{facets}} = map {
|
||||||
|
my $f = [@$_];
|
||||||
|
$f->[$_] += $v_offset for -3..-1;
|
||||||
|
$f;
|
||||||
|
} @{$args{facets}};
|
||||||
|
}
|
||||||
|
|
||||||
|
my $volume = Slic3r::Model::Volume->new(object => $self, %args);
|
||||||
push @{$self->volumes}, $volume;
|
push @{$self->volumes}, $volume;
|
||||||
return $volume;
|
return $volume;
|
||||||
}
|
}
|
||||||
|
@ -35,19 +35,11 @@ my %opt = ();
|
|||||||
my $new_object = $new_model->add_object;
|
my $new_object = $new_model->add_object;
|
||||||
for my $m (0 .. $#models) {
|
for my $m (0 .. $#models) {
|
||||||
my $model = $models[$m];
|
my $model = $models[$m];
|
||||||
my $v_offset = @{$new_object->vertices};
|
$new_model->set_material($m, { Name => basename($ARGV[$m]) });
|
||||||
push @{$new_object->vertices}, @{$model->objects->[0]->vertices};
|
|
||||||
my @new_facets = map {
|
|
||||||
my $f = [@$_];
|
|
||||||
$f->[$_] += $v_offset for -3..-1;
|
|
||||||
$f;
|
|
||||||
} @{ $model->objects->[0]->volumes->[0]->facets };
|
|
||||||
|
|
||||||
my $material_id = scalar keys %{$new_model->materials};
|
|
||||||
$new_model->set_material($material_id, { Name => basename($ARGV[$m]) });
|
|
||||||
$new_object->add_volume(
|
$new_object->add_volume(
|
||||||
material_id => $material_id,
|
material_id => $m,
|
||||||
facets => [@new_facets],
|
facets => $model->objects->[0]->volumes->[0]->facets,
|
||||||
|
vertices => $model->objects->[0]->vertices,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user