mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Alessandro Ranellucci
						Alessandro Ranellucci