mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-31 04:31:15 -06:00 
			
		
		
		
	Merge branch 'decouple-gui'
This commit is contained in:
		
						commit
						c2b7f40f40
					
				
					 12 changed files with 384 additions and 236 deletions
				
			
		|  | @ -1134,6 +1134,15 @@ sub replace_options { | ||||||
|     return $string; |     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: | # CLASS METHODS: | ||||||
| 
 | 
 | ||||||
| sub write_ini { | sub write_ini { | ||||||
|  |  | ||||||
|  | @ -72,7 +72,7 @@ sub write_file { | ||||||
|             foreach my $instance (@{$object->instances}) { |             foreach my $instance (@{$object->instances}) { | ||||||
|                 $instances .= sprintf qq{    <instance objectid="%d">\n}, $object_id; |                 $instances .= sprintf qq{    <instance objectid="%d">\n}, $object_id; | ||||||
|                 $instances .= sprintf qq{      <deltax>%s</deltax>\n}, $instance->offset->[X]; |                 $instances .= sprintf qq{      <deltax>%s</deltax>\n}, $instance->offset->[X]; | ||||||
|                 $instances .= sprintf qq{      <deltax>%s</deltax>\n}, $instance->offset->[Y]; |                 $instances .= sprintf qq{      <deltay>%s</deltay>\n}, $instance->offset->[Y]; | ||||||
|                 $instances .= sprintf qq{      <rz>%s</rz>\n}, $instance->rotation; |                 $instances .= sprintf qq{      <rz>%s</rz>\n}, $instance->rotation; | ||||||
|                 $instances .= sprintf qq{    </instance>\n}; |                 $instances .= sprintf qq{    </instance>\n}; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -107,7 +107,7 @@ sub end_document { | ||||||
|      |      | ||||||
|     foreach my $object_id (keys %{ $self->{_instances} }) { |     foreach my $object_id (keys %{ $self->{_instances} }) { | ||||||
|         my $new_object_id = $self->{_objects_map}{$object_id}; |         my $new_object_id = $self->{_objects_map}{$object_id}; | ||||||
|         if (!$new_object_id) { |         if (!defined $new_object_id) { | ||||||
|             warn "Undefined object $object_id referenced in constellation\n"; |             warn "Undefined object $object_id referenced in constellation\n"; | ||||||
|             next; |             next; | ||||||
|         } |         } | ||||||
|  | @ -115,7 +115,7 @@ sub end_document { | ||||||
|         foreach my $instance (@{ $self->{_instances}{$object_id} }) { |         foreach my $instance (@{ $self->{_instances}{$object_id} }) { | ||||||
|             $self->{_model}->objects->[$new_object_id]->add_instance( |             $self->{_model}->objects->[$new_object_id]->add_instance( | ||||||
|                 rotation => $instance->{rz} || 0, |                 rotation => $instance->{rz} || 0, | ||||||
|                 offset   => [ $instance->{deltax} || 0, $instance->{deltay} ], |                 offset   => [ $instance->{deltax} || 0, $instance->{deltay} || 0 ], | ||||||
|             ); |             ); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -4,8 +4,9 @@ use warnings; | ||||||
| use utf8; | use utf8; | ||||||
| 
 | 
 | ||||||
| use File::Basename qw(basename dirname); | use File::Basename qw(basename dirname); | ||||||
|  | use List::Util qw(max sum); | ||||||
| use Math::ConvexHull qw(convex_hull); | use Math::ConvexHull qw(convex_hull); | ||||||
| use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 scale unscale); | use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2); | ||||||
| use Slic3r::Geometry::Clipper qw(JT_ROUND); | use Slic3r::Geometry::Clipper qw(JT_ROUND); | ||||||
| use threads::shared qw(shared_clone); | use threads::shared qw(shared_clone); | ||||||
| use Wx qw(:bitmap :brush :button :cursor :dialog :filedialog :font :keycode :icon :id :listctrl :misc :panel :pen :sizer :toolbar :window); | use Wx qw(:bitmap :brush :button :cursor :dialog :filedialog :font :keycode :icon :id :listctrl :misc :panel :pen :sizer :toolbar :window); | ||||||
|  | @ -39,6 +40,8 @@ sub new { | ||||||
|     $self->{config} = Slic3r::Config->new_from_defaults(qw( |     $self->{config} = Slic3r::Config->new_from_defaults(qw( | ||||||
|         bed_size print_center complete_objects extruder_clearance_radius skirts skirt_distance |         bed_size print_center complete_objects extruder_clearance_radius skirts skirt_distance | ||||||
|     )); |     )); | ||||||
|  |     $self->{objects} = []; | ||||||
|  |     $self->{selected_objects} = []; | ||||||
|      |      | ||||||
|     $self->{canvas} = Wx::Panel->new($self, -1, wxDefaultPosition, CANVAS_SIZE, wxTAB_TRAVERSAL); |     $self->{canvas} = Wx::Panel->new($self, -1, wxDefaultPosition, CANVAS_SIZE, wxTAB_TRAVERSAL); | ||||||
|     $self->{canvas}->SetBackgroundColour(Wx::wxWHITE); |     $self->{canvas}->SetBackgroundColour(Wx::wxWHITE); | ||||||
|  | @ -155,8 +158,9 @@ sub new { | ||||||
|     EVT_COMMAND($self, -1, $THUMBNAIL_DONE_EVENT, sub { |     EVT_COMMAND($self, -1, $THUMBNAIL_DONE_EVENT, sub { | ||||||
|         my ($self, $event) = @_; |         my ($self, $event) = @_; | ||||||
|         my ($obj_idx, $thumbnail) = @{$event->GetData}; |         my ($obj_idx, $thumbnail) = @{$event->GetData}; | ||||||
|         $self->{thumbnails}[$obj_idx] = $thumbnail; |         $self->{objects}[$obj_idx]->thumbnail($thumbnail); | ||||||
|         $self->make_thumbnail2; |         $self->mesh(undef); | ||||||
|  |         $self->on_thumbnail_made; | ||||||
|     }); |     }); | ||||||
|      |      | ||||||
|     EVT_COMMAND($self, -1, $PROGRESS_BAR_EVENT, sub { |     EVT_COMMAND($self, -1, $PROGRESS_BAR_EVENT, sub { | ||||||
|  | @ -182,11 +186,6 @@ sub new { | ||||||
|     }); |     }); | ||||||
|      |      | ||||||
|     $self->_update_bed_size; |     $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->{selected_objects} = []; |  | ||||||
|     $self->recenter; |     $self->recenter; | ||||||
|      |      | ||||||
|     { |     { | ||||||
|  | @ -295,12 +294,31 @@ sub load_file { | ||||||
|      |      | ||||||
|     my $process_dialog = Wx::ProgressDialog->new('Loading…', "Processing input file…", 100, $self, 0); |     my $process_dialog = Wx::ProgressDialog->new('Loading…', "Processing input file…", 100, $self, 0); | ||||||
|     $process_dialog->Pulse; |     $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                    => basename($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], | ||||||
|  |             ], | ||||||
|  |         ); | ||||||
|  |          | ||||||
|  |         # we only consider the rotation of the first instance for now | ||||||
|  |         $object->set_rotation($model->objects->[$i]->instances->[0]->rotation) | ||||||
|  |             if $model->objects->[$i]->instances; | ||||||
|  |          | ||||||
|  |         push @{ $self->{objects} }, $object; | ||||||
|  |         $self->object_loaded($#{ $self->{objects} }, no_arrange => (@{$object->instances} > 1)); | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     $process_dialog->Destroy; | ||||||
|     $self->statusbar->SetStatusText("Loaded $input_file"); |     $self->statusbar->SetStatusText("Loaded $input_file"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -308,11 +326,10 @@ sub object_loaded { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|     my ($obj_idx, %params) = @_; |     my ($obj_idx, %params) = @_; | ||||||
|      |      | ||||||
|     my $object = $self->{print}->objects->[$obj_idx]; |     my $object = $self->{objects}[$obj_idx]; | ||||||
|     $self->{list}->InsertStringItem($obj_idx, basename($object->input_file)); |     $self->{list}->InsertStringItem($obj_idx, $object->name); | ||||||
|     $self->{list}->SetItem($obj_idx, 1, "1"); |     $self->{list}->SetItem($obj_idx, 1, $object->instances_count); | ||||||
|     $self->{list}->SetItem($obj_idx, 2, "100%"); |     $self->{list}->SetItem($obj_idx, 2, ($object->scale * 100) . "%"); | ||||||
|     push @{$self->{scale}}, 1; |  | ||||||
|      |      | ||||||
|     $self->make_thumbnail($obj_idx); |     $self->make_thumbnail($obj_idx); | ||||||
|     $self->arrange unless $params{no_arrange}; |     $self->arrange unless $params{no_arrange}; | ||||||
|  | @ -325,36 +342,13 @@ sub remove { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|     my ($obj_idx) = @_; |     my ($obj_idx) = @_; | ||||||
|      |      | ||||||
|     if (defined $obj_idx) { |     # if no object index is supplied, remove the selected one | ||||||
|         $self->{print}->copies->[$obj_idx][$_] = undef |     if (!defined $obj_idx) { | ||||||
|             for 0 .. $#{ $self->{print}->copies->[$obj_idx] }; |         ($obj_idx, undef) = $self->selected_object; | ||||||
|     } else { |  | ||||||
|         foreach my $pobj (@{$self->{selected_objects}}) { |  | ||||||
|             my ($obj_idx, $copy_idx) = ($pobj->[0], $pobj->[1]); |  | ||||||
|             $self->{print}->copies->[$obj_idx][$copy_idx] = undef; |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     my @objects_to_remove = (); |     splice @{$self->{objects}}, $obj_idx, 1; | ||||||
|     for my $obj_idx (0 .. $#{$self->{print}->objects}) { |     $self->{list}->DeleteItem($obj_idx); | ||||||
|         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); |  | ||||||
|     } |  | ||||||
|      |      | ||||||
|     $self->{selected_objects} = []; |     $self->{selected_objects} = []; | ||||||
|     $self->selection_changed(0); |     $self->selection_changed(0); | ||||||
|  | @ -366,10 +360,7 @@ sub remove { | ||||||
| sub reset { | sub reset { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|      |      | ||||||
|     @{$self->{print}->objects} = (); |     @{$self->{objects}} = (); | ||||||
|     @{$self->{print}->copies} = (); |  | ||||||
|     @{$self->{thumbnails}} = (); |  | ||||||
|     @{$self->{scale}} = (); |  | ||||||
|     $self->{list}->DeleteAllItems; |     $self->{list}->DeleteAllItems; | ||||||
|      |      | ||||||
|     $self->{selected_objects} = []; |     $self->{selected_objects} = []; | ||||||
|  | @ -381,61 +372,43 @@ sub reset { | ||||||
| sub increase { | sub increase { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|      |      | ||||||
|     my $obj_idx = $self->selected_object_idx; |     my ($obj_idx, $object) = $self->selected_object; | ||||||
|     my $copies = $self->{print}->copies->[$obj_idx]; |     my $instances = $object->instances; | ||||||
|     push @$copies, [ $copies->[-1]->[X] + scale 10, $copies->[-1]->[Y] + scale 10 ]; |     push @$instances, [ $instances->[-1]->[X] + 10, $instances->[-1]->[Y] + 10 ]; | ||||||
|     $self->{list}->SetItem($obj_idx, 1, scalar @$copies); |     $self->{list}->SetItem($obj_idx, 1, $object->instances_count); | ||||||
|     $self->arrange; |     $self->arrange; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| sub decrease { | sub decrease { | ||||||
|     my $self = shift; |     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] ]; |     if ($object->instances_count >= 2) { | ||||||
|     $self->remove; |         pop @{$object->instances}; | ||||||
|  |     } else { | ||||||
|  |         $self->remove; | ||||||
|  |     } | ||||||
|      |      | ||||||
|     if ($self->{print}->objects->[$obj_idx]) { |     if ($self->{objects}[$obj_idx]) { | ||||||
|         $self->{list}->Select($obj_idx, 0); |         $self->{list}->Select($obj_idx, 0); | ||||||
|         $self->{list}->Select($obj_idx, 1); |         $self->{list}->Select($obj_idx, 1); | ||||||
|     } |     } | ||||||
|  |     $self->recenter; | ||||||
|  |     $self->{canvas}->Refresh; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| sub rotate { | sub rotate { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|     my ($angle) = @_; |     my ($angle) = @_; | ||||||
|      |      | ||||||
|     my $obj_idx = $self->selected_object_idx; |     my ($obj_idx, $object) = $self->selected_object; | ||||||
|     my $object = $self->{print}->objects->[$obj_idx]; |  | ||||||
|      |      | ||||||
|     if (!defined $angle) { |     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; |         return if !$angle || $angle == -1; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     $self->statusbar->SetStatusText("Rotating object…"); |     $object->set_rotation($object->rotate + $angle); | ||||||
|     $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 |  | ||||||
|      |  | ||||||
|     $self->recenter; |     $self->recenter; | ||||||
|     $self->{canvas}->Refresh; |     $self->{canvas}->Refresh; | ||||||
| } | } | ||||||
|  | @ -443,38 +416,46 @@ sub arrange { | ||||||
| sub changescale { | sub changescale { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|      |      | ||||||
|     my $obj_idx = $self->selected_object_idx; |     my ($obj_idx, $object) = $self->selected_object; | ||||||
|     my $scale = $self->{scale}[$obj_idx]; |      | ||||||
|     # max scale factor should be above 2540 to allow importing files exported in inches |     # 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); |     my $scale = Wx::GetNumberFromUser("", "Enter the scale % for the selected object:", "Scale", $object->scale*100, 0, 5000, $self); | ||||||
|     return if !$scale || $scale == -1; |     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->{list}->SetItem($obj_idx, 2, "$scale%"); | ||||||
|      |     $object->set_scale($scale / 100); | ||||||
|     $self->make_thumbnail($obj_idx); |  | ||||||
|     $self->arrange; |     $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] = $self->to_units(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, $self->skeinpanel->config); | ||||||
|  |          | ||||||
|  |         @$_ = @{shift @positions} | ||||||
|  |             for map @{$_->instances}, @{$self->{objects}}; | ||||||
|  |     }; | ||||||
|  |     # ignore arrange warnings on purpose | ||||||
|  |      | ||||||
|  |     $self->recenter; | ||||||
|  |     $self->{canvas}->Refresh; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| sub split_object { | sub split_object { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|      |      | ||||||
|     my $obj_idx = $self->selected_object_idx; |     my ($obj_idx, $current_object) = $self->selected_object; | ||||||
|     my $current_object = $self->{print}->objects->[$obj_idx]; |     my $current_copies_num = $current_object->instances_count; | ||||||
|     my $current_copies_num = @{$self->{print}->copies->[$obj_idx]}; |     my $mesh = $current_object->get_mesh; | ||||||
|     my $mesh = $current_object->mesh->clone; |  | ||||||
|     $mesh->scale(&Slic3r::SCALING_FACTOR); |  | ||||||
|      |      | ||||||
|     my @new_meshes = $mesh->split_mesh; |     my @new_meshes = $mesh->split_mesh; | ||||||
|     if (@new_meshes == 1) { |     if (@new_meshes == 1) { | ||||||
|  | @ -488,11 +469,15 @@ sub split_object { | ||||||
|     $self->remove($obj_idx); |     $self->remove($obj_idx); | ||||||
|      |      | ||||||
|     foreach my $mesh (@new_meshes) { |     foreach my $mesh (@new_meshes) { | ||||||
|         my $object = $self->{print}->add_object_from_mesh($mesh); |         my $object = Slic3r::GUI::Plater::Object->new( | ||||||
|         $object->input_file($current_object->input_file); |             name                    => basename($current_object->input_file), | ||||||
|         my $new_obj_idx = $#{$self->{print}->objects}; |             input_file              => $current_object->input_file, | ||||||
|         push @{$self->{print}->copies->[$new_obj_idx]}, [0,0] for 2..$current_copies_num; |             input_file_object_id    => undef, | ||||||
|         $self->object_loaded($new_obj_idx, no_arrange => 1); |             mesh                    => $mesh, | ||||||
|  |             instances               => [ map [0,0], 1..$current_copies_num ], | ||||||
|  |         ); | ||||||
|  |         push @{ $self->{objects} }, $object; | ||||||
|  |         $self->object_loaded($#{ $self->{objects} }, no_arrange => 1); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     $self->arrange; |     $self->arrange; | ||||||
|  | @ -506,15 +491,13 @@ sub export_gcode { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     # set this before spawning the thread because ->config needs GetParent and it's not available there |     # get config before spawning the thread because ->config needs GetParent and it's not available there | ||||||
|     $self->{print}->config($self->skeinpanel->config); |     my $print = $self->_init_print; | ||||||
|     $self->{print}->extra_variables->{"${_}_preset"} = $self->skeinpanel->{options_tabs}{$_}->current_preset->{name} |  | ||||||
|         for qw(print filament printer); |  | ||||||
|      |      | ||||||
|     # select output file |     # select output file | ||||||
|     $self->{output_file} = $main::opt{output}; |     $self->{output_file} = $main::opt{output}; | ||||||
|     { |     { | ||||||
|         $self->{output_file} = $self->{print}->expanded_output_filepath($self->{output_file}); |         $self->{output_file} = $print->expanded_output_filepath($self->{output_file}, $self->{objects}[0]->input_file); | ||||||
|         my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', dirname($self->{output_file}), |         my $dlg = Wx::FileDialog->new($self, 'Save G-code file as:', dirname($self->{output_file}), | ||||||
|             basename($self->{output_file}), $Slic3r::GUI::SkeinPanel::gcode_wildcard, wxFD_SAVE); |             basename($self->{output_file}), $Slic3r::GUI::SkeinPanel::gcode_wildcard, wxFD_SAVE); | ||||||
|         if ($dlg->ShowModal != wxID_OK) { |         if ($dlg->ShowModal != wxID_OK) { | ||||||
|  | @ -529,6 +512,7 @@ sub export_gcode { | ||||||
|     if ($Slic3r::have_threads) { |     if ($Slic3r::have_threads) { | ||||||
|         $self->{export_thread} = threads->create(sub { |         $self->{export_thread} = threads->create(sub { | ||||||
|             $self->export_gcode2( |             $self->export_gcode2( | ||||||
|  |                 $print, | ||||||
|                 $self->{output_file}, |                 $self->{output_file}, | ||||||
|                 progressbar     => sub { Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROGRESS_BAR_EVENT, shared_clone([@_]))) }, |                 progressbar     => sub { Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $PROGRESS_BAR_EVENT, shared_clone([@_]))) }, | ||||||
|                 message_dialog  => sub { Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $MESSAGE_DIALOG_EVENT, shared_clone([@_]))) }, |                 message_dialog  => sub { Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $MESSAGE_DIALOG_EVENT, shared_clone([@_]))) }, | ||||||
|  | @ -549,6 +533,7 @@ sub export_gcode { | ||||||
|         }); |         }); | ||||||
|     } else { |     } else { | ||||||
|         $self->export_gcode2( |         $self->export_gcode2( | ||||||
|  |             $print, | ||||||
|             $self->{output_file}, |             $self->{output_file}, | ||||||
|             progressbar => sub { |             progressbar => sub { | ||||||
|                 my ($percent, $message) = @_; |                 my ($percent, $message) = @_; | ||||||
|  | @ -562,9 +547,20 @@ sub export_gcode { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | sub _init_print { | ||||||
|  |     my $self = shift; | ||||||
|  |      | ||||||
|  |     return Slic3r::Print->new( | ||||||
|  |         config => $self->skeinpanel->config, | ||||||
|  |         extra_variables => { | ||||||
|  |             map { $_ => $self->skeinpanel->{options_tabs}{$_}->current_preset->{name} } qw(print filament printer), | ||||||
|  |         }, | ||||||
|  |     ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| sub export_gcode2 { | sub export_gcode2 { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|     my ($output_file, %params) = @_; |     my ($print, $output_file, %params) = @_; | ||||||
|     $Slic3r::Geometry::Clipper::clipper = Math::Clipper->new; |     $Slic3r::Geometry::Clipper::clipper = Math::Clipper->new; | ||||||
|     local $SIG{'KILL'} = sub { |     local $SIG{'KILL'} = sub { | ||||||
|         Slic3r::debugf "Exporting cancelled; exiting thread...\n"; |         Slic3r::debugf "Exporting cancelled; exiting thread...\n"; | ||||||
|  | @ -572,8 +568,8 @@ sub export_gcode2 { | ||||||
|     } if $Slic3r::have_threads; |     } if $Slic3r::have_threads; | ||||||
|      |      | ||||||
|     eval { |     eval { | ||||||
|         my $print = $self->{print}; |  | ||||||
|         $print->config->validate; |         $print->config->validate; | ||||||
|  |         $print->add_model($self->make_model); | ||||||
|         $print->validate; |         $print->validate; | ||||||
|          |          | ||||||
|         { |         { | ||||||
|  | @ -582,7 +578,6 @@ sub export_gcode2 { | ||||||
|             my %params = ( |             my %params = ( | ||||||
|                 output_file => $output_file, |                 output_file => $output_file, | ||||||
|                 status_cb   => sub { $params{progressbar}->(@_) }, |                 status_cb   => sub { $params{progressbar}->(@_) }, | ||||||
|                 keep_meshes => 1, |  | ||||||
|             ); |             ); | ||||||
|             if ($params{export_svg}) { |             if ($params{export_svg}) { | ||||||
|                 $print->export_svg(%params); |                 $print->export_svg(%params); | ||||||
|  | @ -654,7 +649,7 @@ sub _get_export_file { | ||||||
|      |      | ||||||
|     my $output_file = $main::opt{output}; |     my $output_file = $main::opt{output}; | ||||||
|     { |     { | ||||||
|         $output_file = $self->{print}->expanded_output_filepath($output_file); |         $output_file = $self->_init_print->expanded_output_filepath($output_file, $self->{objects}[0]->input_file); | ||||||
|         $output_file =~ s/\.gcode$/$suffix/i; |         $output_file =~ s/\.gcode$/$suffix/i; | ||||||
|         my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file), |         my $dlg = Wx::FileDialog->new($self, "Save $format file as:", dirname($output_file), | ||||||
|             basename($output_file), $Slic3r::GUI::SkeinPanel::model_wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); |             basename($output_file), $Slic3r::GUI::SkeinPanel::model_wildcard, wxFD_SAVE | wxFD_OVERWRITE_PROMPT); | ||||||
|  | @ -672,16 +667,21 @@ sub make_model { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|      |      | ||||||
|     my $model = Slic3r::Model->new; |     my $model = Slic3r::Model->new; | ||||||
|     for my $obj_idx (0 .. $#{$self->{print}->objects}) { |     foreach my $object (@{$self->{objects}}) { | ||||||
|         my $mesh = $self->{print}->objects->[$obj_idx]->mesh->clone; |         my $mesh = $object->get_mesh; | ||||||
|         $mesh->scale(&Slic3r::SCALING_FACTOR); |         $mesh->scale($object->scale); | ||||||
|         my $object = $model->add_object(vertices => $mesh->vertices); |         my $model_object = $model->add_object( | ||||||
|         $object->add_volume(facets => $mesh->facets); |             vertices    => $mesh->vertices, | ||||||
|         for my $copy (@{$self->{print}->copies->[$obj_idx]}) { |             input_file  => $object->input_file, | ||||||
|             $object->add_instance(rotation => 0, offset => [ map unscale $_, @$copy ]); |         ); | ||||||
|         } |         $model_object->add_volume( | ||||||
|  |             facets      => $mesh->facets, | ||||||
|  |         ); | ||||||
|  |         $model_object->add_instance( | ||||||
|  |             rotation    => $object->rotate, | ||||||
|  |             offset      => [ @$_ ], | ||||||
|  |         ) for @{$object->instances}; | ||||||
|     } |     } | ||||||
|     # TODO: $model->align_to_origin; |  | ||||||
|      |      | ||||||
|     return $model; |     return $model; | ||||||
| } | } | ||||||
|  | @ -691,27 +691,21 @@ sub make_thumbnail { | ||||||
|     my ($obj_idx) = @_; |     my ($obj_idx) = @_; | ||||||
|      |      | ||||||
|     my $cb = sub { |     my $cb = sub { | ||||||
|         my $object = $self->{print}->objects->[$obj_idx]; |         my $object = $self->{objects}[$obj_idx]; | ||||||
|         my @points = map [ @$_[X,Y] ], @{$object->mesh->vertices}; |         my $thumbnail = $object->make_thumbnail(scaling_factor => $self->{scaling_factor}); | ||||||
|         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 |  | ||||||
|          |          | ||||||
|         if ($Slic3r::have_threads) { |         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; |             threads->exit; | ||||||
|         } else { |         } else { | ||||||
|             $self->make_thumbnail2; |             $self->on_thumbnail_made; | ||||||
|         } |         } | ||||||
|     }; |     }; | ||||||
|      |      | ||||||
|     $Slic3r::have_threads ? threads->create($cb)->detach : $cb->(); |     $Slic3r::have_threads ? threads->create($cb)->detach : $cb->(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| sub make_thumbnail2 { | sub on_thumbnail_made { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|     $self->recenter; |     $self->recenter; | ||||||
|     $self->{canvas}->Refresh; |     $self->{canvas}->Refresh; | ||||||
|  | @ -720,12 +714,21 @@ sub make_thumbnail2 { | ||||||
| sub recenter { | sub recenter { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|      |      | ||||||
|  |     return unless @{$self->{objects}}; | ||||||
|  |      | ||||||
|     # calculate displacement needed to center the print |     # calculate displacement needed to center the print | ||||||
|     my @print_bb = $self->{print}->bounding_box; |     my @print_bb = Slic3r::Geometry::bounding_box([ | ||||||
|     @print_bb = (0,0,0,0) if !defined $print_bb[0]; |         map { | ||||||
|  |             my $obj = $_; | ||||||
|  |             map { | ||||||
|  |                 my $instance = $_; | ||||||
|  |                 $instance, [ map $instance->[$_] + $obj->rotated_size->[$_], X,Y ]; | ||||||
|  |             } @{$obj->instances}; | ||||||
|  |         } @{$self->{objects}}, | ||||||
|  |     ]); | ||||||
|     $self->{shift} = [ |     $self->{shift} = [ | ||||||
|         ($self->{canvas}->GetSize->GetWidth  - ($self->to_pixel($print_bb[X2] + $print_bb[X1]))) / 2, |         ($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, |         ($self->{canvas}->GetSize->GetHeight - $self->to_pixel($print_bb[Y2] + $print_bb[Y1])) / 2, | ||||||
|     ]; |     ]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -758,18 +761,13 @@ sub _update_bed_size { | ||||||
|     my $bed_size = $self->{config}->bed_size; |     my $bed_size = $self->{config}->bed_size; | ||||||
|     my $canvas_side = CANVAS_SIZE->[X];  # when the canvas is not rendered yet, its GetSize() method returns 0,0 |     my $canvas_side = CANVAS_SIZE->[X];  # when the canvas is not rendered yet, its GetSize() method returns 0,0 | ||||||
|     my $bed_largest_side = $bed_size->[X] > $bed_size->[Y] ? $bed_size->[X] : $bed_size->[Y]; |     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; |     $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 | # this is called on the canvas | ||||||
| sub repaint { | sub repaint { | ||||||
|     my ($self, $event) = @_; |     my ($self, $event) = @_; | ||||||
|     my $parent = $self->GetParent; |     my $parent = $self->GetParent; | ||||||
|     my $print = $parent->{print}; |  | ||||||
|      |      | ||||||
|     my $dc = Wx::PaintDC->new($self); |     my $dc = Wx::PaintDC->new($self); | ||||||
|     my $size = $self->GetSize; |     my $size = $self->GetSize; | ||||||
|  | @ -786,7 +784,7 @@ sub repaint { | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     # draw print center |     # draw print center | ||||||
|     if (@{$print->objects}) { |     if (@{$parent->{objects}}) { | ||||||
|         $dc->SetPen($parent->{print_center_pen}); |         $dc->SetPen($parent->{print_center_pen}); | ||||||
|         $dc->DrawLine($size[X]/2, 0, $size[X]/2, $size[Y]); |         $dc->DrawLine($size[X]/2, 0, $size[X]/2, $size[Y]); | ||||||
|         $dc->DrawLine(0, $size[Y]/2, $size[X], $size[Y]/2); |         $dc->DrawLine(0, $size[Y]/2, $size[X], $size[Y]/2); | ||||||
|  | @ -802,7 +800,7 @@ sub repaint { | ||||||
|     $dc->DrawRectangle(0, 0, @size); |     $dc->DrawRectangle(0, 0, @size); | ||||||
|      |      | ||||||
|     # draw text if plate is empty |     # draw text if plate is empty | ||||||
|     if (!@{$print->objects}) { |     if (!@{$parent->{objects}}) { | ||||||
|         $dc->SetTextForeground(Wx::Colour->new(150,50,50)); |         $dc->SetTextForeground(Wx::Colour->new(150,50,50)); | ||||||
|         $dc->SetFont(Wx::Font->new(14, wxDEFAULT, wxNORMAL, wxNORMAL)); |         $dc->SetFont(Wx::Font->new(14, wxDEFAULT, wxNORMAL, wxNORMAL)); | ||||||
|         $dc->DrawLabel(CANVAS_TEXT, Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL); |         $dc->DrawLabel(CANVAS_TEXT, Wx::Rect->new(0, 0, $self->GetSize->GetWidth, $self->GetSize->GetHeight), wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL); | ||||||
|  | @ -811,15 +809,16 @@ sub repaint { | ||||||
|     # draw thumbnails |     # draw thumbnails | ||||||
|     $dc->SetPen(wxBLACK_PEN); |     $dc->SetPen(wxBLACK_PEN); | ||||||
|     @{$parent->{object_previews}} = (); |     @{$parent->{object_previews}} = (); | ||||||
|     for my $obj_idx (0 .. $#{$print->objects}) { |     for my $obj_idx (0 .. $#{$parent->{objects}}) { | ||||||
|         next unless $parent->{thumbnails}[$obj_idx]; |         my $object = $parent->{objects}[$obj_idx]; | ||||||
|         for my $copy_idx (0 .. $#{$print->copies->[$obj_idx]}) { |         next unless $object->thumbnail; | ||||||
|             my $copy = $print->copies->[$obj_idx][$copy_idx]; |         for my $instance_idx (0 .. $#{$object->instances}) { | ||||||
|             push @{$parent->{object_previews}}, [ $obj_idx, $copy_idx, $parent->{thumbnails}[$obj_idx]->clone ]; |             my $instance = $object->instances->[$instance_idx]; | ||||||
|             $parent->{object_previews}->[-1][2]->translate(map $parent->to_pixel($copy->[$_]) + $parent->{shift}[$_], (X,Y)); |             push @{$parent->{object_previews}}, [ $obj_idx, $instance_idx, $object->thumbnail->clone ]; | ||||||
|  |             $parent->{object_previews}->[-1][2]->translate(map $parent->to_pixel($instance->[$_]) + $parent->{shift}[$_], (X,Y)); | ||||||
|              |              | ||||||
|             my $drag_object = $self->{drag_object}; |             my $drag_object = $self->{drag_object}; | ||||||
|             if (defined $drag_object && $obj_idx == $drag_object->[0] && $copy_idx == $drag_object->[1]) { |             if (defined $drag_object && $obj_idx == $drag_object->[0] && $instance_idx == $drag_object->[1]) { | ||||||
|                 $dc->SetBrush($parent->{dragged_brush}); |                 $dc->SetBrush($parent->{dragged_brush}); | ||||||
|             } elsif (grep { $_->[0] == $obj_idx } @{$parent->{selected_objects}}) { |             } elsif (grep { $_->[0] == $obj_idx } @{$parent->{selected_objects}}) { | ||||||
|                 $dc->SetBrush($parent->{selected_brush}); |                 $dc->SetBrush($parent->{selected_brush}); | ||||||
|  | @ -829,7 +828,7 @@ sub repaint { | ||||||
|             $dc->DrawPolygon($parent->_y($parent->{object_previews}->[-1][2]), 0, 0); |             $dc->DrawPolygon($parent->_y($parent->{object_previews}->[-1][2]), 0, 0); | ||||||
|              |              | ||||||
|             # if sequential printing is enabled and we have more than one object |             # if sequential printing is enabled and we have more than one object | ||||||
|             if ($parent->{config}->complete_objects && (map @$_, @{$print->copies}) > 1) { |             if ($parent->{config}->complete_objects && (map @{$_->instances}, @{$parent->{objects}}) > 1) { | ||||||
|                 my $clearance = +($parent->{object_previews}->[-1][2]->offset($parent->{config}->extruder_clearance_radius / 2 * $parent->{scaling_factor}, 1, JT_ROUND))[0]; |                 my $clearance = +($parent->{object_previews}->[-1][2]->offset($parent->{config}->extruder_clearance_radius / 2 * $parent->{scaling_factor}, 1, JT_ROUND))[0]; | ||||||
|                 $dc->SetPen($parent->{clearance_pen}); |                 $dc->SetPen($parent->{clearance_pen}); | ||||||
|                 $dc->SetBrush($parent->{transparent_brush}); |                 $dc->SetBrush($parent->{transparent_brush}); | ||||||
|  | @ -853,7 +852,6 @@ sub repaint { | ||||||
| sub mouse_event { | sub mouse_event { | ||||||
|     my ($self, $event) = @_; |     my ($self, $event) = @_; | ||||||
|     my $parent = $self->GetParent; |     my $parent = $self->GetParent; | ||||||
|     my $print = $parent->{print}; |  | ||||||
|      |      | ||||||
|     my $point = $event->GetPosition; |     my $point = $event->GetPosition; | ||||||
|     my $pos = $parent->_y([[$point->x, $point->y]])->[0]; #]] |     my $pos = $parent->_y([[$point->x, $point->y]])->[0]; #]] | ||||||
|  | @ -862,12 +860,13 @@ sub mouse_event { | ||||||
|         $parent->{list}->Select($parent->{list}->GetFirstSelected, 0); |         $parent->{list}->Select($parent->{list}->GetFirstSelected, 0); | ||||||
|         $parent->selection_changed(0); |         $parent->selection_changed(0); | ||||||
|         for my $preview (@{$parent->{object_previews}}) { |         for my $preview (@{$parent->{object_previews}}) { | ||||||
|             if ($preview->[2]->encloses_point($pos)) { |             my ($obj_idx, $instance_idx, $thumbnail) = @$preview; | ||||||
|                 $parent->{selected_objects} = [$preview]; |             if ($thumbnail->encloses_point($pos)) { | ||||||
|                 $parent->{list}->Select($preview->[0], 1); |                 $parent->{selected_objects} = [ [$obj_idx, $instance_idx] ]; | ||||||
|  |                 $parent->{list}->Select($obj_idx, 1); | ||||||
|                 $parent->selection_changed(1); |                 $parent->selection_changed(1); | ||||||
|                 my $copy = $print->copies->[ $preview->[0] ]->[ $preview->[1] ]; |                 my $instance = $parent->{objects}[$obj_idx]->instances->[$instance_idx]; | ||||||
|                 $self->{drag_start_pos} = [ map $pos->[$_] - $parent->{shift}[$_] - $parent->to_pixel($copy->[$_]), X,Y ];   # displacement between the click and the copy's origin |                 $self->{drag_start_pos} = [ map $pos->[$_] - $parent->{shift}[$_] - $parent->to_pixel($instance->[$_]), X,Y ];   # displacement between the click and the instance origin | ||||||
|                 $self->{drag_object} = $preview; |                 $self->{drag_object} = $preview; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | @ -880,9 +879,11 @@ sub mouse_event { | ||||||
|         $self->SetCursor(wxSTANDARD_CURSOR); |         $self->SetCursor(wxSTANDARD_CURSOR); | ||||||
|     } elsif ($event->Dragging) { |     } elsif ($event->Dragging) { | ||||||
|         return if !$self->{drag_start_pos}; # concurrency problems |         return if !$self->{drag_start_pos}; # concurrency problems | ||||||
|         for my $obj ($self->{drag_object}) { |         for my $preview ($self->{drag_object}) { | ||||||
|             my $copy = $print->copies->[ $obj->[0] ]->[ $obj->[1] ]; |             my ($obj_idx, $instance_idx, $thumbnail) = @$preview; | ||||||
|             $copy->[$_] = $parent->to_scaled($pos->[$_] - $self->{drag_start_pos}[$_] - $parent->{shift}[$_]) for X,Y; |             my $instance = $parent->{objects}[$obj_idx]->instances->[$instance_idx]; | ||||||
|  |             $instance->[$_] = $parent->to_units($pos->[$_] - $self->{drag_start_pos}[$_] - $parent->{shift}[$_]) for X,Y; | ||||||
|  |             $instance = $parent->_y([$instance])->[0]; | ||||||
|             $parent->Refresh; |             $parent->Refresh; | ||||||
|         } |         } | ||||||
|     } elsif ($event->Moving) { |     } elsif ($event->Moving) { | ||||||
|  | @ -919,7 +920,7 @@ sub list_item_selected { | ||||||
| sub object_list_changed { | sub object_list_changed { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|      |      | ||||||
|     my $method = $self->{print} && @{$self->{print}->objects} ? 'Enable' : 'Disable'; |     my $method = @{$self->{objects}} ? 'Enable' : 'Disable'; | ||||||
|     $self->{"btn_$_"}->$method |     $self->{"btn_$_"}->$method | ||||||
|         for grep $self->{"btn_$_"}, qw(reset arrange export_gcode export_stl); |         for grep $self->{"btn_$_"}, qw(reset arrange export_gcode export_stl); | ||||||
| } | } | ||||||
|  | @ -938,9 +939,10 @@ sub selection_changed { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| sub selected_object_idx { | sub selected_object { | ||||||
|     my $self = shift; |     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 { | sub statusbar { | ||||||
|  | @ -950,12 +952,12 @@ sub statusbar { | ||||||
| 
 | 
 | ||||||
| sub to_pixel { | sub to_pixel { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|     return unscale $_[0] * $self->{scaling_factor}; |     return $_[0] * $self->{scaling_factor}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| sub to_scaled { | sub to_units { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|     return scale $_[0] / $self->{scaling_factor}; |     return $_[0] / $self->{scaling_factor}; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| sub _y { | sub _y { | ||||||
|  | @ -991,4 +993,91 @@ sub OnDropFiles { | ||||||
|     $self->{window}->load_file($_) for @$filenames; |     $self->{window}->load_file($_) for @$filenames; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | package Slic3r::GUI::Plater::Object; | ||||||
|  | use Moo; | ||||||
|  | 
 | ||||||
|  | use Math::ConvexHull qw(convex_hull); | ||||||
|  | use Slic3r::Geometry qw(X Y); | ||||||
|  | 
 | ||||||
|  | has 'name'                  => (is => 'rw', required => 1); | ||||||
|  | has 'input_file'            => (is => 'rw', required => 1); | ||||||
|  | has 'input_file_object_id'  => (is => 'rw');  # undef means keep mesh | ||||||
|  | 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 { [] }); # upward Y axis | ||||||
|  | has 'thumbnail'             => (is => 'rw'); | ||||||
|  | 
 | ||||||
|  | sub _trigger_mesh { | ||||||
|  |     my $self = shift; | ||||||
|  |     $self->size([$self->mesh->size]) if $self->mesh; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | sub get_mesh { | ||||||
|  |     my $self = shift; | ||||||
|  |      | ||||||
|  |     return $self->mesh->clone if $self->mesh; | ||||||
|  |     my $model = Slic3r::Model->read_from_file($self->input_file); | ||||||
|  |     return $model->objects->[$self->input_file_object_id]->mesh; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | sub instances_count { | ||||||
|  |     my $self = shift; | ||||||
|  |     return scalar @{$self->instances}; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | sub make_thumbnail { | ||||||
|  |     my $self = shift; | ||||||
|  |     my %params = @_; | ||||||
|  |      | ||||||
|  |     my @points = map [ @$_[X,Y] ], @{$self->mesh->vertices}; | ||||||
|  |     my $convex_hull = Slic3r::Polygon->new(convex_hull(\@points)); | ||||||
|  |     for (@$convex_hull) { | ||||||
|  |         @$_ = map $_ * $params{scaling_factor}, @$_; | ||||||
|  |     } | ||||||
|  |     $convex_hull->simplify(0.3); | ||||||
|  |     $convex_hull->rotate(Slic3r::Geometry::deg2rad($self->rotate)); | ||||||
|  |     $convex_hull->scale($self->scale); | ||||||
|  |     $convex_hull->align_to_origin; | ||||||
|  |      | ||||||
|  |     $self->thumbnail($convex_hull);  # ignored in multi-threaded environments | ||||||
|  |     $self->mesh(undef) if defined $self->input_file_object_id; | ||||||
|  |      | ||||||
|  |     return $convex_hull; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | sub set_rotation { | ||||||
|  |     my $self = shift; | ||||||
|  |     my ($angle) = @_; | ||||||
|  |      | ||||||
|  |     if ($self->thumbnail) { | ||||||
|  |         $self->thumbnail->rotate(Slic3r::Geometry::deg2rad($angle - $self->rotate)); | ||||||
|  |         $self->thumbnail->align_to_origin; | ||||||
|  |     } | ||||||
|  |     $self->rotate($angle); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | sub set_scale { | ||||||
|  |     my $self = shift; | ||||||
|  |     my ($scale) = @_; | ||||||
|  |      | ||||||
|  |     my $factor = $scale / $self->scale; | ||||||
|  |     return if $factor == 1; | ||||||
|  |     $self->size->[$_] *= $factor for X,Y; | ||||||
|  |     if ($self->thumbnail) { | ||||||
|  |         $self->thumbnail->scale($factor); | ||||||
|  |         $self->thumbnail->align_to_origin; | ||||||
|  |     } | ||||||
|  |     $self->scale($scale); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | sub rotated_size { | ||||||
|  |     my $self = shift; | ||||||
|  |      | ||||||
|  |     return Slic3r::Polygon->new([0,0], [$self->size->[X], 0], [@{$self->size}], [0, $self->size->[Y]]) | ||||||
|  |         ->rotate(Slic3r::Geometry::deg2rad($self->rotate)) | ||||||
|  |         ->size; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| 1; | 1; | ||||||
|  |  | ||||||
|  | @ -57,8 +57,8 @@ sub do_slice { | ||||||
|         $config->validate; |         $config->validate; | ||||||
| 
 | 
 | ||||||
|         # confirm slicing of more than one copies |         # confirm slicing of more than one copies | ||||||
|         my $copies = $Slic3r::Config->duplicate_grid->[X] * $Slic3r::Config->duplicate_grid->[Y]; |         my $copies = $config->duplicate_grid->[X] * $config->duplicate_grid->[Y]; | ||||||
|         $copies = $Slic3r::Config->duplicate if $Slic3r::Config->duplicate > 1; |         $copies = $config->duplicate if $config->duplicate > 1; | ||||||
|         if ($copies > 1) { |         if ($copies > 1) { | ||||||
|             my $confirmation = Wx::MessageDialog->new($self, "Are you sure you want to slice $copies copies?", |             my $confirmation = Wx::MessageDialog->new($self, "Are you sure you want to slice $copies copies?", | ||||||
|                                                       'Multiple Copies', wxICON_QUESTION | wxOK | wxCANCEL); |                                                       'Multiple Copies', wxICON_QUESTION | wxOK | wxCANCEL); | ||||||
|  |  | ||||||
|  | @ -882,7 +882,7 @@ sub douglas_peucker2 { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| sub arrange { | sub arrange { | ||||||
|     my ($total_parts, $partx, $party, $areax, $areay, $dist) = @_; |     my ($total_parts, $partx, $party, $areax, $areay, $dist, $Config) = @_; | ||||||
|      |      | ||||||
|     my $linint = sub { |     my $linint = sub { | ||||||
|         my ($value, $oldmin, $oldmax, $newmin, $newmax) = @_; |         my ($value, $oldmin, $oldmax, $newmin, $newmax) = @_; | ||||||
|  | @ -895,8 +895,13 @@ sub arrange { | ||||||
|      |      | ||||||
|     # margin needed for the skirt |     # margin needed for the skirt | ||||||
|     my $skirt_margin;		 |     my $skirt_margin;		 | ||||||
|     if ($Slic3r::Config->skirts > 0) { |     if ($Config->skirts > 0) { | ||||||
|         $skirt_margin = ($Slic3r::flow->spacing * $Slic3r::Config->skirts + $Slic3r::Config->skirt_distance) * 2; |         my $flow = Slic3r::Flow->new( | ||||||
|  |             layer_height    => $Config->first_layer_height, | ||||||
|  |             nozzle_diameter => $Config->nozzle_diameter->[0],  # TODO: actually look for the extruder used for skirt | ||||||
|  |             width           => $Config->first_layer_extrusion_width, | ||||||
|  |         ); | ||||||
|  |         $skirt_margin = ($flow->spacing * $Config->skirts + $Config->skirt_distance) * 2; | ||||||
|     } else { |     } else { | ||||||
|         $skirt_margin = 0;		 |         $skirt_margin = 0;		 | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -6,6 +6,18 @@ use Slic3r::Geometry qw(X Y Z); | ||||||
| has 'materials' => (is => 'ro', default => sub { {} }); | has 'materials' => (is => 'ro', default => sub { {} }); | ||||||
| has 'objects'   => (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 { | sub add_object { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|      |      | ||||||
|  | @ -18,43 +30,21 @@ sub add_object { | ||||||
| sub mesh { | sub mesh { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|      |      | ||||||
|     my $vertices = []; |     my @meshes = (); | ||||||
|     my $facets = []; |  | ||||||
|     foreach my $object (@{$self->objects}) { |     foreach my $object (@{$self->objects}) { | ||||||
|         my @instances = $object->instances ? @{$object->instances} : (undef); |         my @instances = $object->instances ? @{$object->instances} : (undef); | ||||||
|         foreach my $instance (@instances) { |         foreach my $instance (@instances) { | ||||||
|             my @vertices = @{$object->vertices}; |             my $mesh = $object->mesh->clone; | ||||||
|             if ($instance) { |             if ($instance) { | ||||||
|                 # save Z coordinates, as rotation and translation discard them |                 $mesh->rotate($instance->rotation); | ||||||
|                 my @z = map $_->[Z], @vertices; |                 $mesh->align_to_origin; | ||||||
|                  |                 $mesh->move(@{$instance->offset}); | ||||||
|                 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}; |  | ||||||
|             } |             } | ||||||
|  |             push @meshes, $mesh; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     return Slic3r::TriangleMesh->new( |     return Slic3r::TriangleMesh->merge(@meshes); | ||||||
|         vertices => $vertices, |  | ||||||
|         facets   => $facets, |  | ||||||
|     ); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| package Slic3r::Model::Material; | package Slic3r::Model::Material; | ||||||
|  | @ -66,6 +56,9 @@ has 'attributes'    => (is => 'rw', default => sub { {} }); | ||||||
| package Slic3r::Model::Object; | package Slic3r::Model::Object; | ||||||
| use Moo; | use Moo; | ||||||
| 
 | 
 | ||||||
|  | use Slic3r::Geometry qw(X Y Z); | ||||||
|  | 
 | ||||||
|  | has 'input_file' => (is => 'rw'); | ||||||
| has 'model'     => (is => 'ro', weak_ref => 1, required => 1); | has 'model'     => (is => 'ro', weak_ref => 1, required => 1); | ||||||
| has 'vertices'  => (is => 'ro', default => sub { [] }); | has 'vertices'  => (is => 'ro', default => sub { [] }); | ||||||
| has 'volumes'   => (is => 'ro', default => sub { [] }); | has 'volumes'   => (is => 'ro', default => sub { [] }); | ||||||
|  | @ -87,6 +80,20 @@ sub add_instance { | ||||||
|     return $self->instances->[-1]; |     return $self->instances->[-1]; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | sub mesh { | ||||||
|  |     my $self = shift; | ||||||
|  |      | ||||||
|  |     my $vertices = []; | ||||||
|  |     my $facets = []; | ||||||
|  |      | ||||||
|  |      | ||||||
|  |      | ||||||
|  |     return Slic3r::TriangleMesh->new( | ||||||
|  |         vertices => $self->vertices, | ||||||
|  |         facets   => [ map @{$_->facets}, @{$self->volumes} ], | ||||||
|  |     ); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| package Slic3r::Model::Volume; | package Slic3r::Model::Volume; | ||||||
| use Moo; | use Moo; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ use warnings; | ||||||
| use parent 'Slic3r::Polyline'; | use parent 'Slic3r::Polyline'; | ||||||
| 
 | 
 | ||||||
| use Slic3r::Geometry qw(polygon_lines polygon_remove_parallel_continuous_edges | use Slic3r::Geometry qw(polygon_lines polygon_remove_parallel_continuous_edges | ||||||
|     scale polygon_remove_acute_vertices polygon_segment_having_point point_in_polygon); |     polygon_remove_acute_vertices polygon_segment_having_point point_in_polygon); | ||||||
| use Slic3r::Geometry::Clipper qw(JT_MITER); | use Slic3r::Geometry::Clipper qw(JT_MITER); | ||||||
| 
 | 
 | ||||||
| sub lines { | sub lines { | ||||||
|  | @ -111,7 +111,7 @@ sub is_printable { | ||||||
|     # detect them and we would be discarding them. |     # detect them and we would be discarding them. | ||||||
|     my $p = $self->clone; |     my $p = $self->clone; | ||||||
|     $p->make_counter_clockwise; |     $p->make_counter_clockwise; | ||||||
|     return $p->offset(scale($flow_width || $Slic3r::flow->width) / 2) ? 1 : 0; |     return $p->offset(Slic3r::Geometry::scale($flow_width || $Slic3r::flow->width) / 2) ? 1 : 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| sub is_valid { | sub is_valid { | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ use warnings; | ||||||
| 
 | 
 | ||||||
| use Math::Clipper qw(); | use Math::Clipper qw(); | ||||||
| use Scalar::Util qw(reftype); | 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 X1 X2 Y1 Y2 polyline_remove_parallel_continuous_edges polyline_remove_acute_vertices | ||||||
|     polyline_lines move_points same_point); |     polyline_lines move_points same_point); | ||||||
| 
 | 
 | ||||||
| # the constructor accepts an array(ref) of points | # the constructor accepts an array(ref) of points | ||||||
|  | @ -140,6 +140,19 @@ sub bounding_box { | ||||||
|     return Slic3r::Geometry::bounding_box($self); |     return Slic3r::Geometry::bounding_box($self); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | sub size { | ||||||
|  |     my $self = shift; | ||||||
|  |      | ||||||
|  |     my @extents = $self->bounding_box; | ||||||
|  |     return [$extents[X2] - $extents[X1], $extents[Y2] - $extents[Y1]]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | sub align_to_origin { | ||||||
|  |     my $self = shift; | ||||||
|  |     my @bb = $self->bounding_box; | ||||||
|  |     return $self->translate(-$bb[X1], -$bb[Y1]); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| sub rotate { | sub rotate { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|     my ($angle, $center) = @_; |     my ($angle, $center) = @_; | ||||||
|  | @ -156,4 +169,16 @@ sub translate { | ||||||
|     return $self; |     return $self; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | sub scale { | ||||||
|  |     my $self = shift; | ||||||
|  |     my ($factor) = @_; | ||||||
|  |     return if $factor == 1; | ||||||
|  |      | ||||||
|  |     # transform point coordinates | ||||||
|  |     foreach my $point (@$self) { | ||||||
|  |         $point->[$_] *= $factor for X,Y; | ||||||
|  |     } | ||||||
|  |     return $self; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| 1; | 1; | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ use Moo; | ||||||
| 
 | 
 | ||||||
| use File::Basename qw(basename fileparse); | use File::Basename qw(basename fileparse); | ||||||
| use File::Spec; | use File::Spec; | ||||||
|  | use List::Util qw(max); | ||||||
| use Math::ConvexHull 1.0.4 qw(convex_hull); | use Math::ConvexHull 1.0.4 qw(convex_hull); | ||||||
| use Slic3r::ExtrusionPath ':roles'; | use Slic3r::ExtrusionPath ':roles'; | ||||||
| use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 PI scale unscale move_points nearest_point); | use Slic3r::Geometry qw(X Y Z X1 Y1 X2 Y2 PI scale unscale move_points nearest_point); | ||||||
|  | @ -86,10 +87,7 @@ sub add_objects_from_file { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|     my ($input_file) = @_; |     my ($input_file) = @_; | ||||||
|      |      | ||||||
|     my $model = $input_file =~ /\.stl$/i            ? Slic3r::Format::STL->read_file($input_file) |     my $model = Slic3r::Model->read_from_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 @print_objects = $self->add_model($model); |     my @print_objects = $self->add_model($model); | ||||||
|     $_->input_file($input_file) for @print_objects; |     $_->input_file($input_file) for @print_objects; | ||||||
|  | @ -110,11 +108,11 @@ sub add_model { | ||||||
|             $mesh->rotate($object->instances->[0]->rotation); |             $mesh->rotate($object->instances->[0]->rotation); | ||||||
|         } |         } | ||||||
|          |          | ||||||
|         push @print_objects, $self->add_object_from_mesh($mesh); |         push @print_objects, $self->add_object_from_mesh($mesh, input_file => $object->input_file); | ||||||
|          |          | ||||||
|         if ($object->instances) { |         if ($object->instances) { | ||||||
|             # replace the default [0,0] instance with the custom ones |             # replace the default [0,0] instance with the custom ones | ||||||
|             @{$self->copies->[-1]} = map [ scale $_->offset->[X], scale $_->offset->[X] ], @{$object->instances}; |             @{$self->copies->[-1]} = map [ scale $_->offset->[X], scale $_->offset->[Y] ], @{$object->instances}; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  | @ -123,7 +121,7 @@ sub add_model { | ||||||
| 
 | 
 | ||||||
| sub add_object_from_mesh { | sub add_object_from_mesh { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|     my ($mesh) = @_; |     my ($mesh, %attributes) = @_; | ||||||
|      |      | ||||||
|     $mesh->rotate($Slic3r::Config->rotate); |     $mesh->rotate($Slic3r::Config->rotate); | ||||||
|     $mesh->scale($Slic3r::Config->scale / &Slic3r::SCALING_FACTOR); |     $mesh->scale($Slic3r::Config->scale / &Slic3r::SCALING_FACTOR); | ||||||
|  | @ -133,6 +131,7 @@ sub add_object_from_mesh { | ||||||
|     my $object = Slic3r::Print::Object->new( |     my $object = Slic3r::Print::Object->new( | ||||||
|         mesh => $mesh, |         mesh => $mesh, | ||||||
|         size => [ $mesh->size ], |         size => [ $mesh->size ], | ||||||
|  |         %attributes, | ||||||
|     ); |     ); | ||||||
|      |      | ||||||
|     push @{$self->objects}, $object; |     push @{$self->objects}, $object; | ||||||
|  | @ -234,19 +233,11 @@ sub arrange_objects { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
| 
 | 
 | ||||||
|     my $total_parts = scalar map @$_, @{$self->copies}; |     my $total_parts = scalar map @$_, @{$self->copies}; | ||||||
|     my $partx = my $party = 0; |     my $partx = max(map $_->size->[X], @{$self->objects}); | ||||||
|     foreach my $object (@{$self->objects}) { |     my $party = max(map $_->size->[Y], @{$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 @positions = Slic3r::Geometry::arrange |     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, $self->config); | ||||||
|      |      | ||||||
|     for my $obj_idx (0..$#{$self->objects}) { |     for my $obj_idx (0..$#{$self->objects}) { | ||||||
|         @{$self->copies->[$obj_idx]} = splice @positions, 0, scalar @{$self->copies->[$obj_idx]}; |         @{$self->copies->[$obj_idx]} = splice @positions, 0, scalar @{$self->copies->[$obj_idx]}; | ||||||
|  | @ -798,11 +789,14 @@ sub total_extrusion_volume { | ||||||
|     return $self->total_extrusion_length * ($Slic3r::extruders->[0]->filament_diameter**2) * PI/4 / 1000; |     return $self->total_extrusion_length * ($Slic3r::extruders->[0]->filament_diameter**2) * PI/4 / 1000; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| # this method will return the value of $self->output_file after expanding its | # this method will return the supplied input file path after expanding its | ||||||
| # format variables with their values | # format variables with their values | ||||||
| sub expanded_output_filepath { | sub expanded_output_filepath { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|     my ($path) = @_; |     my ($path, $input_file) = @_; | ||||||
|  |      | ||||||
|  |     # if no input file was supplied, take the first one from our objects | ||||||
|  |     $input_file ||= $self->objects->[0]->input_file; | ||||||
|      |      | ||||||
|     # if output path is an existing directory, we take that and append |     # if output path is an existing directory, we take that and append | ||||||
|     # the specified filename format |     # the specified filename format | ||||||
|  | @ -810,7 +804,6 @@ sub expanded_output_filepath { | ||||||
| 
 | 
 | ||||||
|     # if no explicit output file was defined, we take the input |     # if no explicit output file was defined, we take the input | ||||||
|     # file directory and append the specified filename format |     # file directory and append the specified filename format | ||||||
|     my $input_file = $self->objects->[0]->input_file; |  | ||||||
|     $path ||= (fileparse($input_file))[1] . $Slic3r::Config->output_filename_format; |     $path ||= (fileparse($input_file))[1] . $Slic3r::Config->output_filename_format; | ||||||
|      |      | ||||||
|     my $input_filename = my $input_filename_base = basename($input_file); |     my $input_filename = my $input_filename_base = basename($input_file); | ||||||
|  |  | ||||||
|  | @ -70,6 +70,26 @@ sub BUILD { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | sub merge { | ||||||
|  |     my $class = shift; | ||||||
|  |     my @meshes = @_; | ||||||
|  |      | ||||||
|  |     my $vertices = []; | ||||||
|  |     my $facets = []; | ||||||
|  |      | ||||||
|  |     foreach my $mesh (@meshes) { | ||||||
|  |         my $v_offset = @$vertices; | ||||||
|  |         push @$vertices, @{$mesh->vertices}; | ||||||
|  |         push @$facets, map { | ||||||
|  |             my $f = [@$_]; | ||||||
|  |             $f->[$_] += $v_offset for -3..-1; | ||||||
|  |             $f; | ||||||
|  |         } @{$mesh->facets}; | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |     return $class->new(vertices => $vertices, facets => $facets); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| sub clone { | sub clone { | ||||||
|     my $self = shift; |     my $self = shift; | ||||||
|     return (ref $self)->new( |     return (ref $self)->new( | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								t/fill.t
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								t/fill.t
									
										
									
									
									
								
							|  | @ -36,7 +36,7 @@ sub scale_points (@) { map [scale $_->[X], scale $_->[Y]], @_ } | ||||||
|         expolygon       => Slic3r::ExPolygon->new([ scale_points [0,0], [50,0], [50,50], [0,50] ]), |         expolygon       => Slic3r::ExPolygon->new([ scale_points [0,0], [50,0], [50,50], [0,50] ]), | ||||||
|     ); |     ); | ||||||
|     foreach my $angle (0, 45) { |     foreach my $angle (0, 45) { | ||||||
|         $surface->expolygon->rotate($angle, [0,0]); |         $surface->expolygon->rotate(Slic3r::Geometry::deg2rad($angle), [0,0]); | ||||||
|         my ($params, @paths) = $filler->fill_surface($surface, flow_spacing => 0.69, density => 0.4); |         my ($params, @paths) = $filler->fill_surface($surface, flow_spacing => 0.69, density => 0.4); | ||||||
|         is scalar @paths, 1, 'one continuous path'; |         is scalar @paths, 1, 'one continuous path'; | ||||||
|     } |     } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Alessandro Ranellucci
						Alessandro Ranellucci