Ability to customize how materials are mapped to extruders. #1020

This commit is contained in:
Alessandro Ranellucci 2013-08-25 19:52:32 +02:00
parent cb0ee9729f
commit 026e0c06e4
6 changed files with 134 additions and 34 deletions

View file

@ -27,14 +27,14 @@ sub start_element {
$self->{_coordinate} = $data->{LocalName}; $self->{_coordinate} = $data->{LocalName};
} elsif ($data->{LocalName} eq 'volume') { } elsif ($data->{LocalName} eq 'volume') {
$self->{_volume} = $self->{_object}->add_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') { } elsif ($data->{LocalName} eq 'triangle') {
$self->{_triangle} = ["", "", ""]; $self->{_triangle} = ["", "", ""];
} elsif ($self->{_triangle} && $data->{LocalName} =~ /^v([123])$/ && $self->{_tree}[-1] eq 'triangle') { } elsif ($self->{_triangle} && $data->{LocalName} =~ /^v([123])$/ && $self->{_tree}[-1] eq 'triangle') {
$self->{_vertex_idx} = $1-1; $self->{_vertex_idx} = $1-1;
} elsif ($data->{LocalName} eq 'material') { } 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); $self->{_material} = $self->{_model}->set_material($material_id);
} elsif ($data->{LocalName} eq 'metadata' && $self->{_tree}[-1] eq 'material') { } elsif ($data->{LocalName} eq 'metadata' && $self->{_tree}[-1] eq 'material') {
$self->{_material_metadata_type} = $self->_get_attribute($data, 'type'); $self->{_material_metadata_type} = $self->_get_attribute($data, 'type');

View file

@ -389,8 +389,9 @@ sub load_file {
name => $basename, name => $basename,
input_file => $input_file, input_file => $input_file,
input_file_object_id => $i, input_file_object_id => $i,
model_object => $model->objects->[$i], model => $model,
mesh_stats => $model->objects->[$i]->mesh_stats, # so that we can free model_object model_object_idx => $i,
mesh_stats => $model->objects->[$i]->mesh_stats, # so that we can free model
instances => [ instances => [
$model->objects->[$i]->instances $model->objects->[$i]->instances
? (map $_->offset, @{$model->objects->[$i]->instances}) ? (map $_->offset, @{$model->objects->[$i]->instances})
@ -589,7 +590,8 @@ sub split_object {
name => basename($current_object->input_file), name => basename($current_object->input_file),
input_file => $current_object->input_file, input_file => $current_object->input_file,
input_file_object_id => undef, 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 ], instances => [ map $bb->min_point, 1..$current_copies_num ],
); );
push @{ $self->{objects} }, $object; push @{ $self->{objects} }, $object;
@ -629,6 +631,8 @@ sub export_gcode {
$self->statusbar->StartBusy; $self->statusbar->StartBusy;
$_->free_model_object for @{$self->{objects}};
# It looks like declaring a local $SIG{__WARN__} prevents the ugly # It looks like declaring a local $SIG{__WARN__} prevents the ugly
# "Attempt to free unreferenced scalar" warning... # "Attempt to free unreferenced scalar" warning...
local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self);
@ -788,6 +792,7 @@ sub make_model {
input_file => $plater_object->input_file, input_file => $plater_object->input_file,
config => $plater_object->config, config => $plater_object->config,
layer_height_ranges => $plater_object->layer_height_ranges, layer_height_ranges => $plater_object->layer_height_ranges,
material_mapping => $plater_object->material_mapping,
); );
foreach my $volume (@{$model_object->volumes}) { foreach my $volume (@{$model_object->volumes}) {
$new_model_object->add_volume( $new_model_object->add_volume(
@ -834,7 +839,6 @@ sub on_thumbnail_made {
my $self = shift; my $self = shift;
my ($obj_idx) = @_; my ($obj_idx) = @_;
$self->{objects}[$obj_idx]->free_model_object;
$self->recenter; $self->recenter;
$self->{canvas}->Refresh; $self->{canvas}->Refresh;
} }
@ -1221,7 +1225,8 @@ use Slic3r::Geometry qw(X Y Z MIN MAX deg2rad);
has 'name' => (is => 'rw', required => 1); has 'name' => (is => 'rw', required => 1);
has 'input_file' => (is => 'rw', required => 1); has 'input_file' => (is => 'rw', required => 1);
has 'input_file_object_id' => (is => 'rw'); # undef means keep model object 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 '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 '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); 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 'thumbnail_scaling_factor' => (is => 'rw', trigger => \&_transform_thumbnail);
has 'config' => (is => 'rw', default => sub { Slic3r::Config->new }); has 'config' => (is => 'rw', default => sub { Slic3r::Config->new });
has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ] 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 'mesh_stats' => (is => 'rw');
# statistics # statistics
@ -1242,16 +1248,17 @@ has 'is_manifold' => (is => 'rw');
sub _trigger_model_object { sub _trigger_model_object {
my $self = shift; my $self = shift;
if ($self->model_object) { if ($self->model && defined $self->model_object_idx) {
$self->model_object->align_to_origin; my $model_object = $self->model->objects->[$self->model_object_idx];
$self->bounding_box($self->model_object->bounding_box); $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->convex_hull(Slic3r::Polygon->new(@{Math::ConvexHull::MonotoneChain::convex_hull($mesh->used_vertices)}));
$self->facets(scalar @{$mesh->facets}); $self->facets(scalar @{$mesh->facets});
$self->vertices(scalar @{$mesh->vertices}); $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 # 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; 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 { sub get_model_object {
my $self = shift; my $self = shift;
my $object = $self->model_object; if ($self->model) {
if (!$object) { return $self->model->objects->[$self->model_object_idx];
my $model = Slic3r::Model->read_from_file($self->input_file);
$object = $model->objects->[$self->input_file_object_id];
} }
return $object;
return Slic3r::Model
->read_from_file($self->input_file)
->objects
->[$self->input_file_object_id];
} }
sub instances_count { sub instances_count {
@ -1312,7 +1322,7 @@ sub instances_count {
sub make_thumbnail { sub make_thumbnail {
my $self = shift; 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; my $thumbnail = Slic3r::ExPolygon::Collection->new;
if (@{$mesh->facets} <= 5000) { if (@{$mesh->facets} <= 5000) {
$thumbnail->append(@{ $mesh->horizontal_projection }); $thumbnail->append(@{ $mesh->horizontal_projection });
@ -1331,7 +1341,6 @@ sub make_thumbnail {
$thumbnail->scale(&Slic3r::SCALING_FACTOR); $thumbnail->scale(&Slic3r::SCALING_FACTOR);
$self->thumbnail($thumbnail); # ignored in multi-threaded environments $self->thumbnail($thumbnail); # ignored in multi-threaded environments
$self->free_model_object;
return $thumbnail; return $thumbnail;
} }

View file

@ -16,6 +16,7 @@ sub new {
$self->{tabpanel} = Wx::Notebook->new($self, -1, wxDefaultPosition, wxDefaultSize, wxNB_TOP | wxTAB_TRAVERSAL); $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->{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->{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); my $buttons = $self->CreateStdDialogButtonSizer(wxOK);
EVT_BUTTON($self, wxID_OK, sub { EVT_BUTTON($self, wxID_OK, sub {
@ -25,6 +26,7 @@ sub new {
# notify tabs # notify tabs
$self->{layers}->Closing; $self->{layers}->Closing;
$self->{materials}->Closing;
$self->EndModal(wxID_OK); $self->EndModal(wxID_OK);
}); });
@ -254,4 +256,68 @@ sub _get_ranges {
return sort { $a->[0] <=> $b->[0] } @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; 1;

View file

@ -285,6 +285,20 @@ sub print_info {
$_->print_info for @{$self->objects}; $_->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; package Slic3r::Model::Region;
use Moo; use Moo;
@ -306,6 +320,7 @@ has 'volumes' => (is => 'ro', default => sub { [] });
has 'instances' => (is => 'rw'); has 'instances' => (is => 'rw');
has 'config' => (is => 'rw', default => sub { Slic3r::Config->new }); has 'config' => (is => 'rw', default => sub { Slic3r::Config->new });
has 'layer_height_ranges' => (is => 'rw', default => sub { [] }); # [ z_min, z_max, layer_height ] 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 'mesh_stats' => (is => 'rw');
has '_bounding_box' => (is => 'rw'); has '_bounding_box' => (is => 'rw');
@ -430,6 +445,14 @@ sub materials_count {
return scalar keys %materials; 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 { sub facets_count {
my $self = shift; my $self = shift;
return sum(map $_->facets_count, @{$self->volumes}); return sum(map $_->facets_count, @{$self->volumes});

View file

@ -91,18 +91,6 @@ sub add_model {
my $self = shift; my $self = shift;
my ($model) = @_; 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 # optimization: if avoid_crossing_perimeters is enabled, split
# this mesh into distinct objects so that we reduce the complexity # this mesh into distinct objects so that we reduce the complexity
# of the graphs # of the graphs
@ -112,6 +100,7 @@ sub add_model {
# -- thing before the split. # -- thing before the split.
###$model->split_meshes if $Slic3r::Config->avoid_crossing_perimeters && !$Slic3r::Config->complete_objects; ###$model->split_meshes if $Slic3r::Config->avoid_crossing_perimeters && !$Slic3r::Config->complete_objects;
my %unmapped_materials = ();
foreach my $object (@{ $model->objects }) { foreach my $object (@{ $model->objects }) {
# we align object to origin before applying transformations # we align object to origin before applying transformations
my @align = $object->align_to_origin; my @align = $object->align_to_origin;
@ -119,13 +108,26 @@ sub add_model {
# extract meshes by material # extract meshes by material
my @meshes = (); # by region_id my @meshes = (); # by region_id
foreach my $volume (@{$object->volumes}) { 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; my $mesh = $volume->mesh->clone;
# should the object contain multiple volumes of the same material, merge them # should the object contain multiple volumes of the same material, merge them
$meshes[$region_id] = $meshes[$region_id] $meshes[$region_id] = $meshes[$region_id]
? Slic3r::TriangleMesh->merge($meshes[$region_id], $mesh) ? Slic3r::TriangleMesh->merge($meshes[$region_id], $mesh)
: $mesh; : $mesh;
} }
$self->regions->[$_] //= Slic3r::Print::Region->new for 0..$#meshes;
foreach my $mesh (grep $_, @meshes) { foreach my $mesh (grep $_, @meshes) {
$mesh->check_manifoldness; $mesh->check_manifoldness;

View file

@ -143,7 +143,7 @@ sub slice {
# process facets # process facets
for my $region_id (0 .. $#{$self->meshes}) { 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 %lines = (); # layer_id => [ lines ]
my $apply_lines = sub { my $apply_lines = sub {