Ability to customize how materials are mapped to extruders. #1020
This commit is contained in:
parent
cb0ee9729f
commit
026e0c06e4
6 changed files with 134 additions and 34 deletions
|
@ -27,14 +27,14 @@ sub start_element {
|
|||
$self->{_coordinate} = $data->{LocalName};
|
||||
} elsif ($data->{LocalName} eq 'volume') {
|
||||
$self->{_volume} = $self->{_object}->add_volume(
|
||||
material_id => $self->_get_attribute($data, 'materialid') || undef,
|
||||
material_id => $self->_get_attribute($data, 'materialid') // undef,
|
||||
);
|
||||
} elsif ($data->{LocalName} eq 'triangle') {
|
||||
$self->{_triangle} = ["", "", ""];
|
||||
} elsif ($self->{_triangle} && $data->{LocalName} =~ /^v([123])$/ && $self->{_tree}[-1] eq 'triangle') {
|
||||
$self->{_vertex_idx} = $1-1;
|
||||
} elsif ($data->{LocalName} eq 'material') {
|
||||
my $material_id = $self->_get_attribute($data, 'id') || '_';
|
||||
my $material_id = $self->_get_attribute($data, 'id') // '_';
|
||||
$self->{_material} = $self->{_model}->set_material($material_id);
|
||||
} elsif ($data->{LocalName} eq 'metadata' && $self->{_tree}[-1] eq 'material') {
|
||||
$self->{_material_metadata_type} = $self->_get_attribute($data, 'type');
|
||||
|
|
|
@ -389,8 +389,9 @@ sub load_file {
|
|||
name => $basename,
|
||||
input_file => $input_file,
|
||||
input_file_object_id => $i,
|
||||
model_object => $model->objects->[$i],
|
||||
mesh_stats => $model->objects->[$i]->mesh_stats, # so that we can free model_object
|
||||
model => $model,
|
||||
model_object_idx => $i,
|
||||
mesh_stats => $model->objects->[$i]->mesh_stats, # so that we can free model
|
||||
instances => [
|
||||
$model->objects->[$i]->instances
|
||||
? (map $_->offset, @{$model->objects->[$i]->instances})
|
||||
|
@ -589,7 +590,8 @@ sub split_object {
|
|||
name => basename($current_object->input_file),
|
||||
input_file => $current_object->input_file,
|
||||
input_file_object_id => undef,
|
||||
model_object => $model_object,
|
||||
model => $new_model,
|
||||
model_object_idx => $#{$new_model->objects},
|
||||
instances => [ map $bb->min_point, 1..$current_copies_num ],
|
||||
);
|
||||
push @{ $self->{objects} }, $object;
|
||||
|
@ -629,6 +631,8 @@ sub export_gcode {
|
|||
|
||||
$self->statusbar->StartBusy;
|
||||
|
||||
$_->free_model_object for @{$self->{objects}};
|
||||
|
||||
# It looks like declaring a local $SIG{__WARN__} prevents the ugly
|
||||
# "Attempt to free unreferenced scalar" warning...
|
||||
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
|
||||
|
@ -788,6 +792,7 @@ sub make_model {
|
|||
input_file => $plater_object->input_file,
|
||||
config => $plater_object->config,
|
||||
layer_height_ranges => $plater_object->layer_height_ranges,
|
||||
material_mapping => $plater_object->material_mapping,
|
||||
);
|
||||
foreach my $volume (@{$model_object->volumes}) {
|
||||
$new_model_object->add_volume(
|
||||
|
@ -834,7 +839,6 @@ sub on_thumbnail_made {
|
|||
my $self = shift;
|
||||
my ($obj_idx) = @_;
|
||||
|
||||
$self->{objects}[$obj_idx]->free_model_object;
|
||||
$self->recenter;
|
||||
$self->{canvas}->Refresh;
|
||||
}
|
||||
|
@ -1221,7 +1225,8 @@ use Slic3r::Geometry qw(X Y Z MIN MAX deg2rad);
|
|||
has 'name' => (is => 'rw', required => 1);
|
||||
has 'input_file' => (is => 'rw', required => 1);
|
||||
has 'input_file_object_id' => (is => 'rw'); # undef means keep model object
|
||||
has 'model_object' => (is => 'rw', required => 1, trigger => 1);
|
||||
has 'model' => (is => 'rw', required => 1, trigger => \&_trigger_model_object);
|
||||
has 'model_object_idx' => (is => 'rw', required => 1, trigger => \&_trigger_model_object);
|
||||
has 'bounding_box' => (is => 'rw'); # 3D bb of original object (aligned to origin) with no rotation or scaling
|
||||
has 'convex_hull' => (is => 'rw'); # 2D convex hull of original object (aligned to origin) with no rotation or scaling
|
||||
has 'scale' => (is => 'rw', default => sub { 1 }, trigger => \&_transform_thumbnail);
|
||||
|
@ -1232,6 +1237,7 @@ has 'transformed_thumbnail' => (is => 'rw');
|
|||
has 'thumbnail_scaling_factor' => (is => 'rw', trigger => \&_transform_thumbnail);
|
||||
has 'config' => (is => 'rw', default => sub { Slic3r::Config->new });
|
||||
has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ]
|
||||
has 'material_mapping' => (is => 'rw', default => sub { {} }); # { material_id => extruder_idx }
|
||||
has 'mesh_stats' => (is => 'rw');
|
||||
|
||||
# statistics
|
||||
|
@ -1242,16 +1248,17 @@ has 'is_manifold' => (is => 'rw');
|
|||
|
||||
sub _trigger_model_object {
|
||||
my $self = shift;
|
||||
if ($self->model_object) {
|
||||
$self->model_object->align_to_origin;
|
||||
$self->bounding_box($self->model_object->bounding_box);
|
||||
if ($self->model && defined $self->model_object_idx) {
|
||||
my $model_object = $self->model->objects->[$self->model_object_idx];
|
||||
$model_object->align_to_origin;
|
||||
$self->bounding_box($model_object->bounding_box);
|
||||
|
||||
my $mesh = $self->model_object->mesh;
|
||||
my $mesh = $model_object->mesh;
|
||||
$self->convex_hull(Slic3r::Polygon->new(@{Math::ConvexHull::MonotoneChain::convex_hull($mesh->used_vertices)}));
|
||||
$self->facets(scalar @{$mesh->facets});
|
||||
$self->vertices(scalar @{$mesh->vertices});
|
||||
|
||||
$self->materials($self->model_object->materials_count);
|
||||
$self->materials($model_object->materials_count);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1290,18 +1297,21 @@ sub free_model_object {
|
|||
|
||||
# only delete mesh from memory if we can retrieve it from the original file
|
||||
return unless $self->input_file && defined $self->input_file_object_id;
|
||||
$self->model_object(undef);
|
||||
$self->model(undef);
|
||||
$self->model_object_idx(undef);
|
||||
}
|
||||
|
||||
sub get_model_object {
|
||||
my $self = shift;
|
||||
|
||||
my $object = $self->model_object;
|
||||
if (!$object) {
|
||||
my $model = Slic3r::Model->read_from_file($self->input_file);
|
||||
$object = $model->objects->[$self->input_file_object_id];
|
||||
if ($self->model) {
|
||||
return $self->model->objects->[$self->model_object_idx];
|
||||
}
|
||||
return $object;
|
||||
|
||||
return Slic3r::Model
|
||||
->read_from_file($self->input_file)
|
||||
->objects
|
||||
->[$self->input_file_object_id];
|
||||
}
|
||||
|
||||
sub instances_count {
|
||||
|
@ -1312,7 +1322,7 @@ sub instances_count {
|
|||
sub make_thumbnail {
|
||||
my $self = shift;
|
||||
|
||||
my $mesh = $self->model_object->mesh; # $self->model_object is already aligned to origin
|
||||
my $mesh = $self->get_model_object->mesh; # $self->model_object is already aligned to origin
|
||||
my $thumbnail = Slic3r::ExPolygon::Collection->new;
|
||||
if (@{$mesh->facets} <= 5000) {
|
||||
$thumbnail->append(@{ $mesh->horizontal_projection });
|
||||
|
@ -1331,7 +1341,6 @@ sub make_thumbnail {
|
|||
|
||||
$thumbnail->scale(&Slic3r::SCALING_FACTOR);
|
||||
$self->thumbnail($thumbnail); # ignored in multi-threaded environments
|
||||
$self->free_model_object;
|
||||
|
||||
return $thumbnail;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ sub new {
|
|||
$self->{tabpanel} = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL);
|
||||
$self->{tabpanel}->AddPage($self->{settings} = Slic3r::GUI::Plater::ObjectDialog::SettingsTab->new($self->{tabpanel}, object => $self->{object}), "Settings");
|
||||
$self->{tabpanel}->AddPage($self->{layers} = Slic3r::GUI::Plater::ObjectDialog::LayersTab->new($self->{tabpanel}, object => $self->{object}), "Layers");
|
||||
$self->{tabpanel}->AddPage($self->{materials} = Slic3r::GUI::Plater::ObjectDialog::MaterialsTab->new($self->{tabpanel}, object => $self->{object}), "Materials");
|
||||
|
||||
my $buttons = $self->CreateStdDialogButtonSizer(wxOK);
|
||||
EVT_BUTTON($self, wxID_OK, sub {
|
||||
|
@ -25,6 +26,7 @@ sub new {
|
|||
|
||||
# notify tabs
|
||||
$self->{layers}->Closing;
|
||||
$self->{materials}->Closing;
|
||||
|
||||
$self->EndModal(wxID_OK);
|
||||
});
|
||||
|
@ -254,4 +256,68 @@ sub _get_ranges {
|
|||
return sort { $a->[0] <=> $b->[0] } @ranges;
|
||||
}
|
||||
|
||||
package Slic3r::GUI::Plater::ObjectDialog::MaterialsTab;
|
||||
use Wx qw(:dialog :id :misc :sizer :systemsettings :button :icon);
|
||||
use Wx::Grid;
|
||||
use Wx::Event qw(EVT_BUTTON);
|
||||
use base 'Wx::Panel';
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, %params) = @_;
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize);
|
||||
$self->{object} = $params{object};
|
||||
|
||||
$self->{sizer} = Wx::BoxSizer->new(wxVERTICAL);
|
||||
|
||||
# descriptive text
|
||||
{
|
||||
my $label = Wx::StaticText->new($self, -1, "In this section you can assign object materials to your extruders.",
|
||||
wxDefaultPosition, [-1, 25]);
|
||||
$label->SetFont(Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT));
|
||||
$self->{sizer}->Add($label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
|
||||
}
|
||||
|
||||
# get unique materials used in this object
|
||||
$self->{materials} = [ $self->{object}->get_model_object->unique_materials ];
|
||||
|
||||
# build an OptionsGroup
|
||||
$self->{mapping} = {
|
||||
(map { $self->{materials}[$_] => $_+1 } 0..$#{ $self->{materials} }), # defaults
|
||||
%{$self->{object}->material_mapping},
|
||||
};
|
||||
my $optgroup = Slic3r::GUI::OptionsGroup->new(
|
||||
parent => $self,
|
||||
title => 'Extruders',
|
||||
label_width => 300,
|
||||
options => [
|
||||
map {
|
||||
my $i = $_;
|
||||
my $material_id = $self->{materials}[$i];
|
||||
{
|
||||
opt_key => "material_extruder_$_",
|
||||
type => 'i',
|
||||
label => $self->{object}->get_model_object->model->get_material_name($material_id),
|
||||
min => 1,
|
||||
default => $self->{mapping}{$material_id},
|
||||
on_change => sub { $self->{mapping}{$material_id} = $_[0] },
|
||||
}
|
||||
} 0..$#{ $self->{materials} }
|
||||
],
|
||||
);
|
||||
$self->{sizer}->Add($optgroup->sizer, 0, wxEXPAND | wxALL, 10);
|
||||
|
||||
$self->SetSizer($self->{sizer});
|
||||
$self->{sizer}->SetSizeHints($self);
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub Closing {
|
||||
my $self = shift;
|
||||
|
||||
# save mappings into the plater object
|
||||
$self->{object}->material_mapping($self->{mapping});
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -285,6 +285,20 @@ sub print_info {
|
|||
$_->print_info for @{$self->objects};
|
||||
}
|
||||
|
||||
sub get_material_name {
|
||||
my $self = shift;
|
||||
my ($material_id) = @_;
|
||||
|
||||
my $name;
|
||||
if (exists $self->materials->{$material_id}) {
|
||||
$name //= $self->materials->{$material_id}->attributes->{$_} for qw(Name name);
|
||||
} elsif ($material_id eq '_') {
|
||||
$name = 'Default material';
|
||||
}
|
||||
$name //= $material_id;
|
||||
return $name;
|
||||
}
|
||||
|
||||
package Slic3r::Model::Region;
|
||||
use Moo;
|
||||
|
||||
|
@ -306,6 +320,7 @@ has 'volumes' => (is => 'ro', default => sub { [] });
|
|||
has 'instances' => (is => 'rw');
|
||||
has 'config' => (is => 'rw', default => sub { Slic3r::Config->new });
|
||||
has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ]
|
||||
has 'material_mapping' => (is => 'rw', default => sub { {} }); # { material_id => extruder_idx }
|
||||
has 'mesh_stats' => (is => 'rw');
|
||||
has '_bounding_box' => (is => 'rw');
|
||||
|
||||
|
@ -430,6 +445,14 @@ sub materials_count {
|
|||
return scalar keys %materials;
|
||||
}
|
||||
|
||||
sub unique_materials {
|
||||
my $self = shift;
|
||||
|
||||
my %materials = ();
|
||||
$materials{ $_->material_id // '_' } = 1 for @{$self->volumes};
|
||||
return sort keys %materials;
|
||||
}
|
||||
|
||||
sub facets_count {
|
||||
my $self = shift;
|
||||
return sum(map $_->facets_count, @{$self->volumes});
|
||||
|
|
|
@ -91,18 +91,6 @@ sub add_model {
|
|||
my $self = shift;
|
||||
my ($model) = @_;
|
||||
|
||||
# append/merge materials and preserve a mapping between the original material ID
|
||||
# and our numeric material index
|
||||
my %materials = ();
|
||||
{
|
||||
my @material_ids = sort keys %{$model->materials};
|
||||
@material_ids = (0) if !@material_ids;
|
||||
for (my $i = $self->regions_count; $i < @material_ids; $i++) {
|
||||
push @{$self->regions}, Slic3r::Print::Region->new;
|
||||
$materials{$material_ids[$i]} = $#{$self->regions};
|
||||
}
|
||||
}
|
||||
|
||||
# optimization: if avoid_crossing_perimeters is enabled, split
|
||||
# this mesh into distinct objects so that we reduce the complexity
|
||||
# of the graphs
|
||||
|
@ -112,6 +100,7 @@ sub add_model {
|
|||
# -- thing before the split.
|
||||
###$model->split_meshes if $Slic3r::Config->avoid_crossing_perimeters && !$Slic3r::Config->complete_objects;
|
||||
|
||||
my %unmapped_materials = ();
|
||||
foreach my $object (@{ $model->objects }) {
|
||||
# we align object to origin before applying transformations
|
||||
my @align = $object->align_to_origin;
|
||||
|
@ -119,13 +108,26 @@ sub add_model {
|
|||
# extract meshes by material
|
||||
my @meshes = (); # by region_id
|
||||
foreach my $volume (@{$object->volumes}) {
|
||||
my $region_id = defined $volume->material_id ? $materials{$volume->material_id} : 0;
|
||||
my $region_id;
|
||||
if (defined $volume->material_id) {
|
||||
if ($object->material_mapping) {
|
||||
$region_id = $object->material_mapping->{$volume->material_id} - 1
|
||||
if defined $object->material_mapping->{$volume->material_id};
|
||||
}
|
||||
$region_id //= $unmapped_materials{$volume->material_id};
|
||||
if (!defined $region_id) {
|
||||
$region_id = $unmapped_materials{$volume->material_id} = scalar(keys %unmapped_materials);
|
||||
}
|
||||
}
|
||||
$region_id //= 0;
|
||||
|
||||
my $mesh = $volume->mesh->clone;
|
||||
# should the object contain multiple volumes of the same material, merge them
|
||||
$meshes[$region_id] = $meshes[$region_id]
|
||||
? Slic3r::TriangleMesh->merge($meshes[$region_id], $mesh)
|
||||
: $mesh;
|
||||
}
|
||||
$self->regions->[$_] //= Slic3r::Print::Region->new for 0..$#meshes;
|
||||
|
||||
foreach my $mesh (grep $_, @meshes) {
|
||||
$mesh->check_manifoldness;
|
||||
|
|
|
@ -143,7 +143,7 @@ sub slice {
|
|||
|
||||
# process facets
|
||||
for my $region_id (0 .. $#{$self->meshes}) {
|
||||
my $mesh = $self->meshes->[$region_id]; # ignore undef meshes
|
||||
my $mesh = $self->meshes->[$region_id] // next; # ignore undef meshes
|
||||
|
||||
my %lines = (); # layer_id => [ lines ]
|
||||
my $apply_lines = sub {
|
||||
|
|
Loading…
Reference in a new issue