mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-12 23:49:10 +08:00
"Reload from disk" - UI function overhaul (#4388)
* C++ backend work to support reloading modifier files * UI update preserving configs and volumes of modifiers (those are not reloaded) * clarifying variable names * Setting up variables in the GUI enviroment * Implementation of added variables in (new ModelVolume(*)) funcion * Implementation of new reload function * Overhaul of the reload function, also renaming of some variables * Rewriting the main loop of the reload function, explicitly differentiating between the original file and later added parts and modifiers pointing to other files * Whitespace cleanup * Added dialog to choose from different reload behaviors, added hide and default option in preferences, copied volumes are matched the new object's origin translation
This commit is contained in:
parent
c206ae77a3
commit
72db3392c8
@ -52,6 +52,7 @@ use Slic3r::GUI::Preset;
|
||||
use Slic3r::GUI::PresetEditor;
|
||||
use Slic3r::GUI::PresetEditorDialog;
|
||||
use Slic3r::GUI::SLAPrintOptions;
|
||||
use Slic3r::GUI::ReloadDialog;
|
||||
|
||||
our $have_OpenGL = eval "use Slic3r::GUI::3DScene; 1";
|
||||
our $have_LWP = eval "use LWP::UserAgent; 1";
|
||||
@ -91,7 +92,9 @@ our $Settings = {
|
||||
color_toolpaths_by => 'role',
|
||||
tabbed_preset_editors => 1,
|
||||
show_host => 0,
|
||||
nudge_val => 1
|
||||
nudge_val => 1,
|
||||
reload_hide_dialog => 0,
|
||||
reload_behavior => 0
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -1177,7 +1177,7 @@ sub add_tin {
|
||||
|
||||
sub load_file {
|
||||
my $self = shift;
|
||||
my ($input_file, $obj_idx) = @_;
|
||||
my ($input_file, $obj_idx_to_load) = @_;
|
||||
|
||||
$Slic3r::GUI::Settings->{recent}{skein_directory} = dirname($input_file);
|
||||
wxTheApp->save_settings;
|
||||
@ -1203,14 +1203,27 @@ sub load_file {
|
||||
}
|
||||
}
|
||||
|
||||
if (defined $obj_idx) {
|
||||
return () if $obj_idx >= $model->objects_count;
|
||||
@obj_idx = $self->load_model_objects($model->get_object($obj_idx));
|
||||
for my $obj_idx (0..($model->objects_count-1)) {
|
||||
my $object = $model->objects->[$obj_idx];
|
||||
$object->set_input_file($input_file);
|
||||
for my $vol_idx (0..($object->volumes_count-1)) {
|
||||
my $volume = $object->get_volume($vol_idx);
|
||||
$volume->set_input_file($input_file);
|
||||
$volume->set_input_file_obj_idx($obj_idx);
|
||||
$volume->set_input_file_obj_idx($vol_idx);
|
||||
}
|
||||
}
|
||||
|
||||
my $i = 0;
|
||||
|
||||
if (defined $obj_idx_to_load) {
|
||||
return () if $obj_idx_to_load >= $model->objects_count;
|
||||
@obj_idx = $self->load_model_objects($model->get_object($obj_idx_to_load));
|
||||
$i = $obj_idx_to_load;
|
||||
} else {
|
||||
@obj_idx = $self->load_model_objects(@{$model->objects});
|
||||
}
|
||||
|
||||
my $i = 0;
|
||||
foreach my $obj_idx (@obj_idx) {
|
||||
$self->{objects}[$obj_idx]->input_file($input_file);
|
||||
$self->{objects}[$obj_idx]->input_file_obj_idx($i++);
|
||||
@ -2306,26 +2319,141 @@ sub reload_from_disk {
|
||||
my ($obj_idx, $object) = $self->selected_object;
|
||||
return if !defined $obj_idx;
|
||||
|
||||
return if !$object->input_file
|
||||
|| !-e $object->input_file;
|
||||
if (!$object->input_file) {
|
||||
Slic3r::GUI::warning_catcher($self)->("The selected object couldn't be reloaded because it isn't referenced to its input file any more. This is the case after performing operations like cut or split.");
|
||||
return;
|
||||
}
|
||||
if (!-e $object->input_file) {
|
||||
Slic3r::GUI::warning_catcher($self)->("The selected object couldn't be reloaded because the file doesn't exist anymore on the disk.");
|
||||
return;
|
||||
}
|
||||
|
||||
# Only reload the selected object and not all objects from the input file.
|
||||
my @new_obj_idx = $self->load_file($object->input_file, $object->input_file_obj_idx);
|
||||
return if !@new_obj_idx;
|
||||
|
||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
foreach my $new_obj_idx (@new_obj_idx) {
|
||||
my $o = $self->{model}->objects->[$new_obj_idx];
|
||||
$o->clear_instances;
|
||||
$o->add_instance($_) for @{$model_object->instances};
|
||||
|
||||
if ($o->volumes_count == $model_object->volumes_count) {
|
||||
for my $i (0..($o->volumes_count-1)) {
|
||||
$o->get_volume($i)->config->apply($model_object->get_volume($i)->config);
|
||||
}
|
||||
if (!@new_obj_idx) {
|
||||
Slic3r::GUI::warning_catcher($self)->("The selected object couldn't be reloaded because the new file doesn't contain the object.");
|
||||
return;
|
||||
}
|
||||
|
||||
my $org_obj = $self->{model}->objects->[$obj_idx];
|
||||
|
||||
# check if the object is dependant of more than one file
|
||||
my $org_obj_has_modifiers=0;
|
||||
for my $i (0..($org_obj->volumes_count-1)) {
|
||||
if ($org_obj->input_file ne $org_obj->get_volume($i)->input_file) {
|
||||
$org_obj_has_modifiers=1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
my $reload_behavior = $Slic3r::GUI::Settings->{_}{reload_behavior};
|
||||
|
||||
# ask the user how to proceed, if option is selected in preferences
|
||||
if ($org_obj_has_modifiers && !$Slic3r::GUI::Settings->{_}{reload_hide_dialog}) {
|
||||
my $dlg = Slic3r::GUI::ReloadDialog->new(undef,$reload_behavior);
|
||||
my $res = $dlg->ShowModal;
|
||||
if ($res==wxID_CANCEL) {
|
||||
$self->remove($_) for @new_obj_idx;
|
||||
$dlg->Destroy;
|
||||
return;
|
||||
}
|
||||
$reload_behavior = $dlg->GetSelection;
|
||||
my $save = 0;
|
||||
if ($reload_behavior != $Slic3r::GUI::Settings->{_}{reload_behavior}) {
|
||||
$Slic3r::GUI::Settings->{_}{reload_behavior} = $reload_behavior;
|
||||
$save = 1;
|
||||
}
|
||||
if ($dlg->GetHideOnNext) {
|
||||
$Slic3r::GUI::Settings->{_}{reload_hide_dialog} = 1;
|
||||
$save = 1;
|
||||
}
|
||||
Slic3r::GUI->save_settings if $save;
|
||||
$dlg->Destroy;
|
||||
}
|
||||
|
||||
my $volume_unmatched=0;
|
||||
|
||||
foreach my $new_obj_idx (@new_obj_idx) {
|
||||
my $new_obj = $self->{model}->objects->[$new_obj_idx];
|
||||
$new_obj->clear_instances;
|
||||
$new_obj->add_instance($_) for @{$org_obj->instances};
|
||||
$new_obj->config->apply($org_obj->config);
|
||||
|
||||
my $new_vol_idx = 0;
|
||||
my $org_vol_idx = 0;
|
||||
my $new_vol_count=$new_obj->volumes_count;
|
||||
my $org_vol_count=$org_obj->volumes_count;
|
||||
|
||||
while ($new_vol_idx<=$new_vol_count-1) {
|
||||
if (($org_vol_idx<=$org_vol_count-1) && ($org_obj->get_volume($org_vol_idx)->input_file eq $new_obj->input_file)) {
|
||||
# apply config from the matching volumes
|
||||
$new_obj->get_volume($new_vol_idx++)->config->apply($org_obj->get_volume($org_vol_idx++)->config);
|
||||
} else {
|
||||
# reload has more volumes than original (first file), apply config from the first volume
|
||||
$new_obj->get_volume($new_vol_idx++)->config->apply($org_obj->get_volume(0)->config);
|
||||
$volume_unmatched=1;
|
||||
}
|
||||
}
|
||||
$org_vol_idx=$org_vol_count if $reload_behavior==2; # Reload behavior: discard
|
||||
while (($org_vol_idx<=$org_vol_count-1) && ($org_obj->get_volume($org_vol_idx)->input_file eq $new_obj->input_file)) {
|
||||
# original has more volumes (first file), skip those
|
||||
$org_vol_idx++;
|
||||
$volume_unmatched=1;
|
||||
}
|
||||
while ($org_vol_idx<=$org_vol_count-1) {
|
||||
if ($reload_behavior==1) { # Reload behavior: copy
|
||||
my $new_volume = $new_obj->add_volume($org_obj->get_volume($org_vol_idx));
|
||||
$new_volume->mesh->translate(@{$org_obj->origin_translation->negative});
|
||||
$new_volume->mesh->translate(@{$new_obj->origin_translation});
|
||||
if ($new_volume->name =~ m/link to path\z/) {
|
||||
my $new_name = $new_volume->name;
|
||||
$new_name =~ s/ - no link to path$/ - copied/;
|
||||
$new_volume->set_name($new_name);
|
||||
}elsif(!($new_volume->name =~ m/copied\z/)) {
|
||||
$new_volume->set_name($new_volume->name . " - copied");
|
||||
}
|
||||
}else{ # Reload behavior: Reload all, also fallback solution if ini was manually edited to a wrong value
|
||||
if ($org_obj->get_volume($org_vol_idx)->input_file) {
|
||||
my $model = eval { Slic3r::Model->read_from_file($org_obj->get_volume($org_vol_idx)->input_file) };
|
||||
if ($@) {
|
||||
$org_obj->get_volume($org_vol_idx)->set_input_file("");
|
||||
}elsif ($org_obj->get_volume($org_vol_idx)->input_file_obj_idx > ($model->objects_count-1)) {
|
||||
# Object Index for that part / modifier not found in current version of the file
|
||||
$org_obj->get_volume($org_vol_idx)->set_input_file("");
|
||||
}else{
|
||||
my $prt_mod_obj = $model->objects->[$org_obj->get_volume($org_vol_idx)->input_file_obj_idx];
|
||||
if ($org_obj->get_volume($org_vol_idx)->input_file_vol_idx > ($prt_mod_obj->volumes_count-1)) {
|
||||
# Volume Index for that part / modifier not found in current version of the file
|
||||
$org_obj->get_volume($org_vol_idx)->set_input_file("");
|
||||
}else{
|
||||
# all checks passed, load new mesh and copy metadata
|
||||
my $new_volume = $new_obj->add_volume($prt_mod_obj->get_volume($org_obj->get_volume($org_vol_idx)->input_file_vol_idx));
|
||||
$new_volume->set_input_file($org_obj->get_volume($org_vol_idx)->input_file);
|
||||
$new_volume->set_input_file_obj_idx($org_obj->get_volume($org_vol_idx)->input_file_obj_idx);
|
||||
$new_volume->set_input_file_vol_idx($org_obj->get_volume($org_vol_idx)->input_file_vol_idx);
|
||||
$new_volume->config->apply($org_obj->get_volume($org_vol_idx)->config);
|
||||
$new_volume->set_modifier($org_obj->get_volume($org_vol_idx)->modifier);
|
||||
$new_volume->mesh->translate(@{$new_obj->origin_translation});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$org_obj->get_volume($org_vol_idx)->input_file) {
|
||||
my $new_volume = $new_obj->add_volume($org_obj->get_volume($org_vol_idx)); # error -> copy old mesh
|
||||
$new_volume->mesh->translate(@{$org_obj->origin_translation->negative});
|
||||
$new_volume->mesh->translate(@{$new_obj->origin_translation});
|
||||
if ($new_volume->name =~ m/copied\z/) {
|
||||
my $new_name = $new_volume->name;
|
||||
$new_name =~ s/ - copied$/ - no link to path/;
|
||||
$new_volume->set_name($new_name);
|
||||
}elsif(!($new_volume->name =~ m/link to path\z/)) {
|
||||
$new_volume->set_name($new_volume->name . " - no link to path");
|
||||
}
|
||||
$volume_unmatched=1;
|
||||
}
|
||||
}
|
||||
$org_vol_idx++;
|
||||
}
|
||||
}
|
||||
|
||||
$self->remove($obj_idx);
|
||||
|
||||
# TODO: refresh object list which contains wrong count and scale
|
||||
@ -2336,8 +2464,17 @@ sub reload_from_disk {
|
||||
# When porting to C++ we'll probably have cleaner ways to do this.
|
||||
$self->make_thumbnail($_-1) for @new_obj_idx;
|
||||
|
||||
# update print
|
||||
$self->stop_background_process;
|
||||
$self->{print}->reload_object($_-1) for @new_obj_idx;
|
||||
$self->on_model_change;
|
||||
|
||||
# Empty the redo stack
|
||||
$self->{redo_stack} = [];
|
||||
|
||||
if ($volume_unmatched) {
|
||||
Slic3r::GUI::warning_catcher($self)->("At least 1 volume couldn't be matched between the original object and the reloaded one.");
|
||||
}
|
||||
}
|
||||
|
||||
sub export_object_stl {
|
||||
|
@ -353,11 +353,17 @@ sub on_btn_load {
|
||||
next;
|
||||
}
|
||||
|
||||
foreach my $object (@{$model->objects}) {
|
||||
foreach my $volume (@{$object->volumes}) {
|
||||
my $new_volume = $self->{model_object}->add_volume($volume);
|
||||
for my $obj_idx (0..($model->objects_count-1)) {
|
||||
my $object = $model->objects->[$obj_idx];
|
||||
for my $vol_idx (0..($object->volumes_count-1)) {
|
||||
my $new_volume = $self->{model_object}->add_volume($object->get_volume($vol_idx));
|
||||
$new_volume->set_modifier($is_modifier);
|
||||
$new_volume->set_name(basename($input_file));
|
||||
|
||||
# input_file needed to reload / update modifiers' volumes
|
||||
$new_volume->set_input_file($input_file);
|
||||
$new_volume->set_input_file_obj_idx($obj_idx);
|
||||
$new_volume->set_input_file_vol_idx($vol_idx);
|
||||
|
||||
# apply the same translation we applied to the object
|
||||
$new_volume->mesh->translate(@{$self->{model_object}->origin_translation});
|
||||
|
@ -92,6 +92,23 @@ sub new {
|
||||
tooltip => 'In 2D plater, Move objects using keyboard by nudge value of',
|
||||
default => $Slic3r::GUI::Settings->{_}{nudge_val},
|
||||
));
|
||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( # reload hide dialog
|
||||
opt_id => 'reload_hide_dialog',
|
||||
type => 'bool',
|
||||
label => 'Hide Dialog on Reload',
|
||||
tooltip => 'When checked, the dialog on reloading files with added parts & modifiers is suppressed. The reload is performed according to the option given in \'Default Reload Behavior\'',
|
||||
default => $Slic3r::GUI::Settings->{_}{reload_hide_dialog},
|
||||
));
|
||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( # default reload behavior
|
||||
opt_id => 'reload_behavior',
|
||||
type => 'select',
|
||||
label => 'Default Reload Behavior',
|
||||
tooltip => 'Choose the default behavior of the \'Reload from disk\' function regarding additional parts and modifiers.',
|
||||
labels => ['Reload all','Reload main, copy added','Reload main, discard added'],
|
||||
values => [0, 1, 2],
|
||||
default => $Slic3r::GUI::Settings->{_}{reload_behavior},
|
||||
width => 180,
|
||||
));
|
||||
$optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new( # colorscheme
|
||||
opt_id => 'colorscheme',
|
||||
type => 'select',
|
||||
@ -100,7 +117,7 @@ sub new {
|
||||
labels => ['Default','Solarized'], # add more schemes, if you want in ColorScheme.pm.
|
||||
values => ['getDefault','getSolarized'], # add more schemes, if you want - those are the names of the corresponding function in ColorScheme.pm.
|
||||
default => $Slic3r::GUI::Settings->{_}{colorscheme} // 'getDefault',
|
||||
width => 130,
|
||||
width => 180,
|
||||
));
|
||||
|
||||
my $sizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
|
60
lib/Slic3r/GUI/ReloadDialog.pm
Normal file
60
lib/Slic3r/GUI/ReloadDialog.pm
Normal file
@ -0,0 +1,60 @@
|
||||
# A tiny dialog to select how to reload an object that has additional parts or modifiers.
|
||||
|
||||
package Slic3r::GUI::ReloadDialog;
|
||||
use strict;
|
||||
use warnings;
|
||||
use utf8;
|
||||
|
||||
use Wx qw(:button :dialog :id :misc :sizer :choicebook wxTAB_TRAVERSAL);
|
||||
use Wx::Event qw(EVT_CLOSE);
|
||||
use base 'Wx::Dialog';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent,$default_selection) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, "Additional parts and modifiers detected", wxDefaultPosition, [350,100], wxDEFAULT_DIALOG_STYLE);
|
||||
|
||||
# label
|
||||
my $text = Wx::StaticText->new($self, -1, "Additional parts and modifiers are loaded in the current model. \n\nHow do you want to proceed?", wxDefaultPosition, wxDefaultSize);
|
||||
|
||||
# selector
|
||||
$self->{choice} = my $choice = Wx::Choice->new($self, -1, wxDefaultPosition, wxDefaultSize, []);
|
||||
$choice->Append("Reload all linked files");
|
||||
$choice->Append("Reload main file, copy added parts & modifiers");
|
||||
$choice->Append("Reload main file, discard added parts & modifiers");
|
||||
$choice->SetSelection($default_selection);
|
||||
|
||||
# checkbox
|
||||
$self->{checkbox} = my $checkbox = Wx::CheckBox->new($self, -1, "Don't ask again");
|
||||
|
||||
my $vsizer = Wx::BoxSizer->new(wxVERTICAL);
|
||||
my $hsizer = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
$vsizer->Add($text, 0, wxEXPAND | wxALL, 10);
|
||||
$vsizer->Add($choice, 0, wxEXPAND | wxALL, 10);
|
||||
$hsizer->Add($checkbox, 1, wxEXPAND | wxALL, 10);
|
||||
$hsizer->Add($self->CreateButtonSizer(wxOK | wxCANCEL), 0, wxEXPAND | wxALL, 10);
|
||||
$vsizer->Add($hsizer, 0, wxEXPAND | wxALL, 0);
|
||||
|
||||
$self->SetSizer($vsizer);
|
||||
$self->SetMinSize($self->GetSize);
|
||||
$vsizer->SetSizeHints($self);
|
||||
|
||||
# needed to actually free memory
|
||||
EVT_CLOSE($self, sub {
|
||||
$self->EndModal(wxID_CANCEL);
|
||||
$self->Destroy;
|
||||
});
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub GetSelection {
|
||||
my ($self) = @_;
|
||||
return $self->{choice}->GetSelection;
|
||||
}
|
||||
sub GetHideOnNext {
|
||||
my ($self) = @_;
|
||||
return $self->{checkbox}->GetValue;
|
||||
}
|
||||
|
||||
1;
|
@ -936,12 +936,18 @@ ModelObject::print_info() const
|
||||
|
||||
|
||||
ModelVolume::ModelVolume(ModelObject* object, const TriangleMesh &mesh)
|
||||
: mesh(mesh), modifier(false), object(object)
|
||||
: mesh(mesh), modifier(false), input_file(""), object(object)
|
||||
{}
|
||||
|
||||
ModelVolume::ModelVolume(ModelObject* object, const ModelVolume &other)
|
||||
: name(other.name), mesh(other.mesh), config(other.config),
|
||||
modifier(other.modifier), object(object)
|
||||
: name(other.name),
|
||||
mesh(other.mesh),
|
||||
config(other.config),
|
||||
modifier(other.modifier),
|
||||
input_file(other.input_file),
|
||||
input_file_obj_idx(other.input_file_obj_idx),
|
||||
input_file_vol_idx(other.input_file_vol_idx),
|
||||
object(object)
|
||||
{
|
||||
this->material_id(other.material_id());
|
||||
}
|
||||
@ -959,6 +965,10 @@ ModelVolume::swap(ModelVolume &other)
|
||||
std::swap(this->mesh, other.mesh);
|
||||
std::swap(this->config, other.config);
|
||||
std::swap(this->modifier, other.modifier);
|
||||
|
||||
std::swap(this->input_file, other.input_file);
|
||||
std::swap(this->input_file_obj_idx, other.input_file_obj_idx);
|
||||
std::swap(this->input_file_vol_idx, other.input_file_vol_idx);
|
||||
}
|
||||
|
||||
t_model_material_id
|
||||
|
@ -457,7 +457,12 @@ class ModelVolume
|
||||
DynamicPrintConfig config;
|
||||
///< Configuration parameters specific to an object model geometry or a modifier volume,
|
||||
///< overriding the global Slic3r settings and the ModelObject settings.
|
||||
|
||||
|
||||
/// Input file path needed for reloading the volume from disk
|
||||
std::string input_file; ///< Input file path
|
||||
int input_file_obj_idx; ///< Input file object index
|
||||
int input_file_vol_idx; ///< Input file volume index
|
||||
|
||||
bool modifier; ///< Is it an object to be printed, or a modifier volume?
|
||||
|
||||
/// Get the parent object owning this modifier volume.
|
||||
|
@ -258,6 +258,20 @@ ModelMaterial::attributes()
|
||||
%code%{ RETVAL = THIS->name; %};
|
||||
void set_name(std::string value)
|
||||
%code%{ THIS->name = value; %};
|
||||
|
||||
std::string input_file()
|
||||
%code%{ RETVAL = THIS->input_file; %};
|
||||
void set_input_file(std::string value)
|
||||
%code%{ THIS->input_file = value; %};
|
||||
int input_file_obj_idx()
|
||||
%code%{ RETVAL = THIS->input_file_obj_idx; %};
|
||||
void set_input_file_obj_idx(int obj_idx)
|
||||
%code%{ THIS->input_file_obj_idx = obj_idx; %};
|
||||
int input_file_vol_idx()
|
||||
%code%{ RETVAL = THIS->input_file_vol_idx; %};
|
||||
void set_input_file_vol_idx(int vol_idx)
|
||||
%code%{ THIS->input_file_vol_idx = vol_idx; %};
|
||||
|
||||
t_model_material_id material_id();
|
||||
void set_material_id(t_model_material_id material_id)
|
||||
%code%{ THIS->material_id(material_id); %};
|
||||
|
Loading…
x
Reference in New Issue
Block a user