mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Unfinished work for decoupling GUI from the Print object (goal = more speed for manipulation, less memory usage)
This commit is contained in:
		
							parent
							
								
									f29d455319
								
							
						
					
					
						commit
						191de5d078
					
				
					 5 changed files with 223 additions and 166 deletions
				
			
		|  | @ -1131,6 +1131,15 @@ sub replace_options { | |||
|     return $string; | ||||
| } | ||||
| 
 | ||||
| # min object distance is max(duplicate_distance, clearance_radius) | ||||
| sub min_object_distance { | ||||
|     my $self = shift; | ||||
|      | ||||
|     return ($self->complete_objects && $self->extruder_clearance_radius > $self->duplicate_distance) | ||||
|         ? $self->extruder_clearance_radius | ||||
|         : $self->duplicate_distance; | ||||
| } | ||||
| 
 | ||||
| # CLASS METHODS: | ||||
| 
 | ||||
| sub write_ini { | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ use warnings; | |||
| use utf8; | ||||
| 
 | ||||
| use File::Basename qw(basename dirname); | ||||
| use Math::ConvexHull qw(convex_hull); | ||||
| use List::Util qw(max sum); | ||||
| use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 scale unscale); | ||||
| use Slic3r::Geometry::Clipper qw(JT_ROUND); | ||||
| use threads::shared qw(shared_clone); | ||||
|  | @ -155,8 +155,9 @@ sub new { | |||
|     EVT_COMMAND($self, -1, $THUMBNAIL_DONE_EVENT, sub { | ||||
|         my ($self, $event) = @_; | ||||
|         my ($obj_idx, $thumbnail) = @{$event->GetData}; | ||||
|         $self->{thumbnails}[$obj_idx] = $thumbnail; | ||||
|         $self->make_thumbnail2; | ||||
|         $self->{objects}[$obj_idx]->thumbnail($thumbnail); | ||||
|         $self->mesh(undef); | ||||
|         $self->on_thumbnail_made; | ||||
|     }); | ||||
|      | ||||
|     EVT_COMMAND($self, -1, $PROGRESS_BAR_EVENT, sub { | ||||
|  | @ -182,10 +183,7 @@ sub new { | |||
|     }); | ||||
|      | ||||
|     $self->_update_bed_size; | ||||
|     $self->{print} = Slic3r::Print->new; | ||||
|     $self->{thumbnails} = [];       # polygons, each one aligned to 0,0 | ||||
|     $self->{scale} = []; | ||||
|     $self->{object_previews} = [];  # [ obj_idx, copy_idx, positioned polygon ] | ||||
|     $self->{objects} = []; | ||||
|     $self->{selected_objects} = []; | ||||
|     $self->recenter; | ||||
|      | ||||
|  | @ -295,12 +293,26 @@ sub load_file { | |||
|      | ||||
|     my $process_dialog = Wx::ProgressDialog->new('Loading…', "Processing input file…", 100, $self, 0); | ||||
|     $process_dialog->Pulse; | ||||
|     local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); | ||||
|     $self->{print}->add_objects_from_file($input_file); | ||||
|     my $obj_idx = $#{$self->{print}->objects}; | ||||
|     $process_dialog->Destroy; | ||||
|      | ||||
|     $self->object_loaded($obj_idx); | ||||
|     local $SIG{__WARN__} = Slic3r::GUI::warning_catcher($self); | ||||
|     my $model = Slic3r::Model->read_from_file($input_file); | ||||
|     for my $i (0 .. $#{$model->objects}) { | ||||
|         my $object = Slic3r::GUI::Plater::Object->new( | ||||
|             name                    => basebane($input_file), | ||||
|             input_file              => $input_file, | ||||
|             input_file_object_id    => $i, | ||||
|             mesh                    => $model->objects->[$i]->mesh, | ||||
|             instances               => [ | ||||
|                 $model->objects->[$i]->instances | ||||
|                     ? (map $_->offset, @{$model->objects->[$i]->instances}) | ||||
|                     : [0,0], | ||||
|             ], | ||||
|         ); | ||||
|         push @{ $self->{objects} }, $object; | ||||
|         $self->object_loaded($#{ $self->{objects} }); | ||||
|     } | ||||
|      | ||||
|     $process_dialog->Destroy; | ||||
|     $self->statusbar->SetStatusText("Loaded $input_file"); | ||||
| } | ||||
| 
 | ||||
|  | @ -308,11 +320,10 @@ sub object_loaded { | |||
|     my $self = shift; | ||||
|     my ($obj_idx, %params) = @_; | ||||
|      | ||||
|     my $object = $self->{print}->objects->[$obj_idx]; | ||||
|     $self->{list}->InsertStringItem($obj_idx, basename($object->input_file)); | ||||
|     $self->{list}->SetItem($obj_idx, 1, "1"); | ||||
|     $self->{list}->SetItem($obj_idx, 2, "100%"); | ||||
|     push @{$self->{scale}}, 1; | ||||
|     my $object = $self->{objects}[$obj_idx]; | ||||
|     $self->{list}->InsertStringItem($obj_idx, $object->name); | ||||
|     $self->{list}->SetItem($obj_idx, 1, $object->instances_count); | ||||
|     $self->{list}->SetItem($obj_idx, 2, ($object->scale * 100) . "%"); | ||||
|      | ||||
|     $self->make_thumbnail($obj_idx); | ||||
|     $self->arrange unless $params{no_arrange}; | ||||
|  | @ -325,36 +336,13 @@ sub remove { | |||
|     my $self = shift; | ||||
|     my ($obj_idx) = @_; | ||||
|      | ||||
|     if (defined $obj_idx) { | ||||
|         $self->{print}->copies->[$obj_idx][$_] = undef | ||||
|             for 0 .. $#{ $self->{print}->copies->[$obj_idx] }; | ||||
|     } else { | ||||
|         foreach my $pobj (@{$self->{selected_objects}}) { | ||||
|             my ($obj_idx, $copy_idx) = ($pobj->[0], $pobj->[1]); | ||||
|             $self->{print}->copies->[$obj_idx][$copy_idx] = undef; | ||||
|         } | ||||
|     # if no object index is supplied, remove the selected one | ||||
|     if (!defined $obj_idx) { | ||||
|         ($obj_idx, undef) = $self->selected_object; | ||||
|     } | ||||
|      | ||||
|     my @objects_to_remove = (); | ||||
|     for my $obj_idx (0 .. $#{$self->{print}->objects}) { | ||||
|         my $copies = $self->{print}->copies->[$obj_idx]; | ||||
|          | ||||
|         # filter out removed copies | ||||
|         @$copies = grep defined $_, @$copies; | ||||
|          | ||||
|         # update copies count in list | ||||
|         $self->{list}->SetItem($obj_idx, 1, scalar @$copies); | ||||
|          | ||||
|         # if no copies are left, remove the object itself | ||||
|         push @objects_to_remove, $obj_idx if !@$copies; | ||||
|     } | ||||
|     for my $obj_idx (sort { $b <=> $a } @objects_to_remove) { | ||||
|         splice @{$self->{print}->objects}, $obj_idx, 1; | ||||
|         splice @{$self->{print}->copies}, $obj_idx, 1; | ||||
|         splice @{$self->{thumbnails}}, $obj_idx, 1; | ||||
|         splice @{$self->{scale}}, $obj_idx, 1; | ||||
|         $self->{list}->DeleteItem($obj_idx); | ||||
|     } | ||||
|     splice @{$self->{objects}}, $obj_idx, 1; | ||||
|     $self->{list}->DeleteItem($obj_idx); | ||||
|      | ||||
|     $self->{selected_objects} = []; | ||||
|     $self->selection_changed(0); | ||||
|  | @ -366,10 +354,7 @@ sub remove { | |||
| sub reset { | ||||
|     my $self = shift; | ||||
|      | ||||
|     @{$self->{print}->objects} = (); | ||||
|     @{$self->{print}->copies} = (); | ||||
|     @{$self->{thumbnails}} = (); | ||||
|     @{$self->{scale}} = (); | ||||
|     @{$self->{objects}} = (); | ||||
|     $self->{list}->DeleteAllItems; | ||||
|      | ||||
|     $self->{selected_objects} = []; | ||||
|  | @ -381,21 +366,21 @@ sub reset { | |||
| sub increase { | ||||
|     my $self = shift; | ||||
|      | ||||
|     my $obj_idx = $self->selected_object_idx; | ||||
|     my $copies = $self->{print}->copies->[$obj_idx]; | ||||
|     push @$copies, [ $copies->[-1]->[X] + scale 10, $copies->[-1]->[Y] + scale 10 ]; | ||||
|     $self->{list}->SetItem($obj_idx, 1, scalar @$copies); | ||||
|     my ($obj_idx, $object) = $self->selected_object; | ||||
|     my $instances = $object->instances; | ||||
|     push @$instances, [ $instances->[-1]->[X] + scale 10, $instances->[-1]->[Y] + scale 10 ]; | ||||
|     $self->{list}->SetItem($obj_idx, 1, $object->instances_count); | ||||
|     $self->arrange; | ||||
| } | ||||
| 
 | ||||
| sub decrease { | ||||
|     my $self = shift; | ||||
|      | ||||
|     my $obj_idx = $self->selected_object_idx; | ||||
|     my ($obj_idx, $object) = $self->selected_object; | ||||
|     $self->{selected_objects} = [ +(grep { $_->[0] == $obj_idx } @{$self->{object_previews}})[-1] ]; | ||||
|     $self->remove; | ||||
|      | ||||
|     if ($self->{print}->objects->[$obj_idx]) { | ||||
|     if ($self->{objects}[$obj_idx]) { | ||||
|         $self->{list}->Select($obj_idx, 0); | ||||
|         $self->{list}->Select($obj_idx, 1); | ||||
|     } | ||||
|  | @ -405,37 +390,14 @@ sub rotate { | |||
|     my $self = shift; | ||||
|     my ($angle) = @_; | ||||
|      | ||||
|     my $obj_idx = $self->selected_object_idx; | ||||
|     my $object = $self->{print}->objects->[$obj_idx]; | ||||
|     my ($obj_idx, $object) = $self->selected_object; | ||||
|      | ||||
|     if (!defined $angle) { | ||||
|         $angle = Wx::GetNumberFromUser("", "Enter the rotation angle:", "Rotate", 0, -364, 364, $self); | ||||
|         $angle = Wx::GetNumberFromUser("", "Enter the rotation angle:", "Rotate", $object->rotate, -364, 364, $self); | ||||
|         return if !$angle || $angle == -1; | ||||
|     } | ||||
|      | ||||
|     $self->statusbar->SetStatusText("Rotating object…"); | ||||
|     $self->statusbar->StartBusy; | ||||
|      | ||||
|     # rotate, realign to 0,0 and update size | ||||
|     $object->mesh->rotate($angle); | ||||
|     $object->mesh->align_to_origin; | ||||
|     $object->size([ $object->mesh->size ]); | ||||
|      | ||||
|     $self->make_thumbnail($obj_idx); | ||||
|     $self->recenter; | ||||
|     $self->{canvas}->Refresh; | ||||
|     $self->statusbar->StopBusy; | ||||
|     $self->statusbar->SetStatusText(""); | ||||
| } | ||||
| 
 | ||||
| sub arrange { | ||||
|     my $self = shift; | ||||
|      | ||||
|     eval { | ||||
|         $self->{print}->arrange_objects; | ||||
|     }; | ||||
|     # ignore arrange warnings on purpose | ||||
|      | ||||
|     $object->set_rotation($angle); | ||||
|     $self->recenter; | ||||
|     $self->{canvas}->Refresh; | ||||
| } | ||||
|  | @ -443,36 +405,41 @@ sub arrange { | |||
| sub changescale { | ||||
|     my $self = shift; | ||||
|      | ||||
|     my $obj_idx = $self->selected_object_idx; | ||||
|     my $scale = $self->{scale}[$obj_idx]; | ||||
|     my ($obj_idx, $object) = $self->selected_object; | ||||
|      | ||||
|     # max scale factor should be above 2540 to allow importing files exported in inches | ||||
|     $scale = Wx::GetNumberFromUser("", "Enter the scale % for the selected object:", "Scale", $scale*100, 0, 5000, $self); | ||||
|     $scale = Wx::GetNumberFromUser("", "Enter the scale % for the selected object:", "Scale", $object->scale*100, 0, 5000, $self); | ||||
|     return if !$scale || $scale == -1; | ||||
|      | ||||
|     $self->statusbar->SetStatusText("Scaling object…"); | ||||
|     $self->statusbar->StartBusy; | ||||
|      | ||||
|     my $object = $self->{print}->objects->[$obj_idx]; | ||||
|     my $mesh = $object->mesh; | ||||
|     $mesh->scale($scale/100 / $self->{scale}[$obj_idx]); | ||||
|     $object->mesh->align_to_origin; | ||||
|     $object->size([ $object->mesh->size ]); | ||||
|      | ||||
|     $self->{scale}[$obj_idx] = $scale/100; | ||||
|     $self->{list}->SetItem($obj_idx, 2, "$scale%"); | ||||
|      | ||||
|     $self->make_thumbnail($obj_idx); | ||||
|     $object->set_scale($scale); | ||||
|     $self->arrange; | ||||
|     $self->statusbar->StopBusy; | ||||
|     $self->statusbar->SetStatusText(""); | ||||
| } | ||||
| 
 | ||||
| sub arrange { | ||||
|     my $self = shift; | ||||
|      | ||||
|     my $total_parts = sum(map $_->instances_count, @{$self->{objects}}) or return; | ||||
|     my @size = (); | ||||
|     for my $a (X,Y) { | ||||
|         $size[$a] = max(map $_->thumbnail->size->[$a], @{$self->{objects}}); | ||||
|     } | ||||
|      | ||||
|     eval { | ||||
|         my $config = $self->skeinpanel->config; | ||||
|         my @positions = Slic3r::Geometry::arrange | ||||
|             ($total_parts, @size, @{$config->bed_size}, $config->min_object_distance); | ||||
|     }; | ||||
|     # ignore arrange warnings on purpose | ||||
|      | ||||
|     $self->recenter; | ||||
|     $self->{canvas}->Refresh; | ||||
| } | ||||
| 
 | ||||
| sub split_object { | ||||
|     my $self = shift; | ||||
|      | ||||
|     my $obj_idx = $self->selected_object_idx; | ||||
|     my $current_object = $self->{print}->objects->[$obj_idx]; | ||||
|     my $current_copies_num = @{$self->{print}->copies->[$obj_idx]}; | ||||
|     my ($obj_idx, $current_object) = $self->selected_object; | ||||
|     my $current_copies_num = $current_object->instances_count; | ||||
|     my $mesh = $current_object->mesh->clone; | ||||
|     $mesh->scale(&Slic3r::SCALING_FACTOR); | ||||
|      | ||||
|  | @ -670,8 +637,10 @@ sub make_model { | |||
|     my $self = shift; | ||||
|      | ||||
|     my $model = Slic3r::Model->new; | ||||
|     for my $obj_idx (0 .. $#{$self->{print}->objects}) { | ||||
|         my $mesh = $self->{print}->objects->[$obj_idx]->mesh->clone; | ||||
|     for my $obj_idx (0 .. $#{$self->{objects}}) { | ||||
|         my $object = $self->{objects}[$obj_idx]; | ||||
|         # TODO: reload file | ||||
|         my $mesh = $self->{print}->[$obj_idx]->mesh->clone; | ||||
|         $mesh->scale(&Slic3r::SCALING_FACTOR); | ||||
|         my $object = $model->add_object(vertices => $mesh->vertices); | ||||
|         $object->add_volume(facets => $mesh->facets); | ||||
|  | @ -689,27 +658,21 @@ sub make_thumbnail { | |||
|     my ($obj_idx) = @_; | ||||
|      | ||||
|     my $cb = sub { | ||||
|         my $object = $self->{print}->objects->[$obj_idx]; | ||||
|         my @points = map [ @$_[X,Y] ], @{$object->mesh->vertices}; | ||||
|         my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points)); | ||||
|         for (@$convex_hull) { | ||||
|             @$_ = map $self->to_pixel($_), @$_; | ||||
|         } | ||||
|         $convex_hull->simplify(0.3); | ||||
|         $self->{thumbnails}->[$obj_idx] = $convex_hull;  # ignored in multithread environment | ||||
|         my $object = $self->{objects}[$obj_idx]; | ||||
|         my $thumbnail = $object->make_thumbnail; | ||||
|          | ||||
|         if ($Slic3r::have_threads) { | ||||
|             Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $THUMBNAIL_DONE_EVENT, shared_clone([ $obj_idx, $convex_hull ]))); | ||||
|             Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $THUMBNAIL_DONE_EVENT, shared_clone([ $obj_idx, $thumbnail ]))); | ||||
|             threads->exit; | ||||
|         } else { | ||||
|             $self->make_thumbnail2; | ||||
|             $self->on_thumbnail_made; | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|     $Slic3r::have_threads ? threads->create($cb)->detach : $cb->(); | ||||
| } | ||||
| 
 | ||||
| sub make_thumbnail2 { | ||||
| sub on_thumbnail_made { | ||||
|     my $self = shift; | ||||
|     $self->recenter; | ||||
|     $self->{canvas}->Refresh; | ||||
|  | @ -719,8 +682,8 @@ sub recenter { | |||
|     my $self = shift; | ||||
|      | ||||
|     # calculate displacement needed to center the print | ||||
|     my @print_bb = $self->{print}->bounding_box; | ||||
|     @print_bb = (0,0,0,0) if !defined $print_bb[0]; | ||||
|     my @print_bb = (0,0,0,0); | ||||
|     @print_bb =  if !defined $print_bb[0]; | ||||
|     $self->{shift} = [ | ||||
|         ($self->{canvas}->GetSize->GetWidth  - ($self->to_pixel($print_bb[X2] + $print_bb[X1]))) / 2, | ||||
|         ($self->{canvas}->GetSize->GetHeight - ($self->to_pixel($print_bb[Y2] + $print_bb[Y1]))) / 2, | ||||
|  | @ -758,9 +721,6 @@ sub _update_bed_size { | |||
|     my $bed_largest_side = $bed_size->[X] > $bed_size->[Y] ? $bed_size->[X] : $bed_size->[Y]; | ||||
|     my $old_scaling_factor = $self->{scaling_factor}; | ||||
|     $self->{scaling_factor} = $canvas_side / $bed_largest_side; | ||||
|     if (defined $old_scaling_factor && $self->{scaling_factor} != $old_scaling_factor) { | ||||
|         $self->make_thumbnail($_) for 0..$#{$self->{thumbnails}}; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| # this is called on the canvas | ||||
|  | @ -936,9 +896,10 @@ sub selection_changed { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| sub selected_object_idx { | ||||
| sub selected_object { | ||||
|     my $self = shift; | ||||
|     return $self->{selected_objects}[0] ? $self->{selected_objects}[0][0] : $self->{list}->GetFirstSelected; | ||||
|     my $obj_idx = $self->{selected_objects}[0] ? $self->{selected_objects}[0][0] : $self->{list}->GetFirstSelected; | ||||
|     return ($obj_idx, $self->{objects}[$obj_idx]), | ||||
| } | ||||
| 
 | ||||
| sub statusbar { | ||||
|  | @ -989,4 +950,60 @@ sub OnDropFiles { | |||
|     $self->{window}->load_file($_) for @$filenames; | ||||
| } | ||||
| 
 | ||||
| package Slic3r::GUI::Plater::Object; | ||||
| use Moo; | ||||
| 
 | ||||
| use Math::ConvexHull qw(convex_hull); | ||||
| 
 | ||||
| has 'name'                  => (is => 'rw', required => 1); | ||||
| has 'input_file'            => (is => 'rw', required => 1); | ||||
| has 'input_file_object_id'  => (is => 'rw', required => 1); | ||||
| has 'mesh'                  => (is => 'rw', required => 1, trigger => 1); | ||||
| has 'size'                  => (is => 'rw'); | ||||
| has 'scale'                 => (is => 'rw', default => sub { 1 }); | ||||
| has 'rotate'                => (is => 'rw', default => sub { 0 }); | ||||
| has 'instances'             => (is => 'rw', default => sub { [] }); | ||||
| has 'thumbnail'             => (is => 'rw'); | ||||
| 
 | ||||
| sub _trigger_mesh { | ||||
|     my $self = shift; | ||||
|     $self->size($mesh->size) if $self->mesh; | ||||
| } | ||||
| 
 | ||||
| sub instances_count { | ||||
|     my $self = shift; | ||||
|     return scalar @{$self->instances}; | ||||
| } | ||||
| 
 | ||||
| sub make_thumbnail { | ||||
|     my $self = shift; | ||||
|      | ||||
|     my @points = map [ @$_[X,Y] ], @{$object->mesh->vertices}; | ||||
|     my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points)); | ||||
|     for (@$convex_hull) { | ||||
|         @$_ = map $self->to_pixel($_), @$_; | ||||
|     } | ||||
|     $convex_hull->simplify(0.3); | ||||
|      | ||||
|     $self->thumbnail($convex_hull);  # ignored in multi-threaded environments | ||||
|     $self->mesh(undef); | ||||
|     return $convex_hull; | ||||
| } | ||||
| 
 | ||||
| sub set_rotation { | ||||
|     my $self = shift; | ||||
|     my ($angle) = @_; | ||||
|      | ||||
|     $self->thumbnail->rotate($angle - $self->rotate); | ||||
|     $self->rotate($angle); | ||||
| } | ||||
| 
 | ||||
| sub set_scale { | ||||
|     my $self = shift; | ||||
|     my ($scale) = @_; | ||||
|      | ||||
|     $self->thumbnail->scale($scale - $self->scale); | ||||
|     $self->scale($scale); | ||||
| } | ||||
| 
 | ||||
| 1; | ||||
|  |  | |||
|  | @ -6,6 +6,18 @@ use Slic3r::Geometry qw(X Y Z); | |||
| has 'materials' => (is => 'ro', default => sub { {} }); | ||||
| has 'objects'   => (is => 'ro', default => sub { [] }); | ||||
| 
 | ||||
| sub read_from_file { | ||||
|     my $class = shift; | ||||
|     my ($input_file) = @_; | ||||
|      | ||||
|     my $model = $input_file =~ /\.stl$/i            ? Slic3r::Format::STL->read_file($input_file) | ||||
|               : $input_file =~ /\.obj$/i            ? Slic3r::Format::OBJ->read_file($input_file) | ||||
|               : $input_file =~ /\.amf(\.xml)?$/i    ? Slic3r::Format::AMF->read_file($input_file) | ||||
|               : die "Input file must have .stl, .obj or .amf(.xml) extension\n"; | ||||
|      | ||||
|     return $model; | ||||
| } | ||||
| 
 | ||||
| sub add_object { | ||||
|     my $self = shift; | ||||
|      | ||||
|  | @ -21,34 +33,15 @@ sub mesh { | |||
|     my $vertices = []; | ||||
|     my $facets = []; | ||||
|     foreach my $object (@{$self->objects}) { | ||||
|         my @instances = $object->instances ? @{$object->instances} : (undef); | ||||
|         foreach my $instance (@instances) { | ||||
|             my @vertices = @{$object->vertices}; | ||||
|             if ($instance) { | ||||
|                 # save Z coordinates, as rotation and translation discard them | ||||
|                 my @z = map $_->[Z], @vertices; | ||||
|                  | ||||
|                 if ($instance->rotation) { | ||||
|                     # transform vertex coordinates | ||||
|                     my $rad = Slic3r::Geometry::deg2rad($instance->rotation); | ||||
|                     @vertices = Slic3r::Geometry::rotate_points($rad, undef, @vertices); | ||||
|                 } | ||||
|                 @vertices = Slic3r::Geometry::move_points($instance->offset, @vertices); | ||||
|                  | ||||
|                 # reapply Z coordinates | ||||
|                 $vertices[$_][Z] = $z[$_] for 0 .. $#z; | ||||
|             } | ||||
|              | ||||
|             my $v_offset = @$vertices; | ||||
|             push @$vertices, @vertices; | ||||
|             foreach my $volume (@{$object->volumes}) { | ||||
|                 push @$facets, map { | ||||
|                     my $f = [@$_]; | ||||
|                     $f->[$_] += $v_offset for -3..-1; | ||||
|                     $f; | ||||
|                 } @{$volume->facets}; | ||||
|             } | ||||
|         } | ||||
|         my $mesh = $object->mesh; | ||||
|          | ||||
|         my $v_offset = @$vertices; | ||||
|         push @$vertices, @{$mesh->vertices}; | ||||
|         push @$facets, map { | ||||
|             my $f = [@$_]; | ||||
|             $f->[$_] += $v_offset for -3..-1; | ||||
|             $f; | ||||
|         } @{$mesh->facets}; | ||||
|     } | ||||
|      | ||||
|     return Slic3r::TriangleMesh->new( | ||||
|  | @ -87,6 +80,47 @@ sub add_instance { | |||
|     return $self->instances->[-1]; | ||||
| } | ||||
| 
 | ||||
| sub mesh { | ||||
|     my $self = shift; | ||||
|      | ||||
|     my $vertices = []; | ||||
|     my $facets = []; | ||||
|      | ||||
|     my @instances = $self->instances ? @{$self->instances} : (undef); | ||||
|     foreach my $instance (@instances) { | ||||
|         my @vertices = @{$self->vertices}; | ||||
|         if ($instance) { | ||||
|             # save Z coordinates, as rotation and translation discard them | ||||
|             my @z = map $_->[Z], @vertices; | ||||
|              | ||||
|             if ($instance->rotation) { | ||||
|                 # transform vertex coordinates | ||||
|                 my $rad = Slic3r::Geometry::deg2rad($instance->rotation); | ||||
|                 @vertices = Slic3r::Geometry::rotate_points($rad, undef, @vertices); | ||||
|             } | ||||
|             @vertices = Slic3r::Geometry::move_points($instance->offset, @vertices); | ||||
|              | ||||
|             # reapply Z coordinates | ||||
|             $vertices[$_][Z] = $z[$_] for 0 .. $#z; | ||||
|         } | ||||
|          | ||||
|         my $v_offset = @$vertices; | ||||
|         push @$vertices, @vertices; | ||||
|         foreach my $volume (@{$self->volumes}) { | ||||
|             push @$facets, map { | ||||
|                 my $f = [@$_]; | ||||
|                 $f->[$_] += $v_offset for -3..-1; | ||||
|                 $f; | ||||
|             } @{$volume->facets}; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     return Slic3r::TriangleMesh->new( | ||||
|         vertices => $vertices, | ||||
|         facets   => $facets, | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| package Slic3r::Model::Volume; | ||||
| use Moo; | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ use warnings; | |||
| 
 | ||||
| use Math::Clipper qw(); | ||||
| use Scalar::Util qw(reftype); | ||||
| use Slic3r::Geometry qw(A B polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices | ||||
| use Slic3r::Geometry qw(A B X Y MIN MAX polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices | ||||
|     polyline_lines move_points same_point); | ||||
| 
 | ||||
| # the constructor accepts an array(ref) of points | ||||
|  | @ -140,6 +140,13 @@ sub bounding_box { | |||
|     return Slic3r::Geometry::bounding_box($self); | ||||
| } | ||||
| 
 | ||||
| sub size { | ||||
|     my $self = shift; | ||||
|      | ||||
|     my @extents = $self->bounding_box; | ||||
|     return map $extents[$_][MAX] - $extents[$_][MIN], (X,Y); | ||||
| } | ||||
| 
 | ||||
| sub rotate { | ||||
|     my $self = shift; | ||||
|     my ($angle, $center) = @_; | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ use Moo; | |||
| 
 | ||||
| use File::Basename qw(basename fileparse); | ||||
| use File::Spec; | ||||
| use List::Util qw(max); | ||||
| use Math::ConvexHull 1.0.4 qw(convex_hull); | ||||
| use Slic3r::ExtrusionPath ':roles'; | ||||
| use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 PI scale unscale move_points); | ||||
|  | @ -83,10 +84,7 @@ sub add_objects_from_file { | |||
|     my $self = shift; | ||||
|     my ($input_file) = @_; | ||||
|      | ||||
|     my $model = $input_file =~ /\.stl$/i            ? Slic3r::Format::STL->read_file($input_file) | ||||
|               : $input_file =~ /\.obj$/i            ? Slic3r::Format::OBJ->read_file($input_file) | ||||
|               : $input_file =~ /\.amf(\.xml)?$/i    ? Slic3r::Format::AMF->read_file($input_file) | ||||
|               : die "Input file must have .stl, .obj or .amf(.xml) extension\n"; | ||||
|     my $model = Slic3r::Model->read_from_file($input_file); | ||||
|      | ||||
|     my @print_objects = $self->add_model($model); | ||||
|     $_->input_file($input_file) for @print_objects; | ||||
|  | @ -231,19 +229,11 @@ sub arrange_objects { | |||
|     my $self = shift; | ||||
| 
 | ||||
|     my $total_parts = scalar map @$_, @{$self->copies}; | ||||
|     my $partx = my $party = 0; | ||||
|     foreach my $object (@{$self->objects}) { | ||||
|         $partx = $object->size->[X] if $object->size->[X] > $partx; | ||||
|         $party = $object->size->[Y] if $object->size->[Y] > $party; | ||||
|     } | ||||
|      | ||||
|     # object distance is max(duplicate_distance, clearance_radius) | ||||
|     my $distance = $Slic3r::Config->complete_objects && $Slic3r::Config->extruder_clearance_radius > $Slic3r::Config->duplicate_distance | ||||
|         ? $Slic3r::Config->extruder_clearance_radius | ||||
|         : $Slic3r::Config->duplicate_distance; | ||||
|     my $partx = max(map $_->size->[X], @{$self->objects}); | ||||
|     my $party = max(map $_->size->[Y], @{$self->objects}); | ||||
|      | ||||
|     my @positions = Slic3r::Geometry::arrange | ||||
|         ($total_parts, $partx, $party, (map scale $_, @{$Slic3r::Config->bed_size}), scale $distance); | ||||
|         ($total_parts, $partx, $party, (map scale $_, @{$Slic3r::Config->bed_size}), scale $Slic3r::Config->min_object_distance); | ||||
|      | ||||
|     for my $obj_idx (0..$#{$self->objects}) { | ||||
|         @{$self->copies->[$obj_idx]} = splice @positions, 0, scalar @{$self->copies->[$obj_idx]}; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Alessandro Ranellucci
						Alessandro Ranellucci