mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	Merge branch 'master' into sender
This commit is contained in:
		
						commit
						d2172b4383
					
				
					 47 changed files with 860 additions and 429 deletions
				
			
		|  | @ -243,7 +243,7 @@ sub validate { | |||
|         if !first { $_ eq $self->gcode_flavor } @{$Options->{gcode_flavor}{values}}; | ||||
|      | ||||
|     die "--use-firmware-retraction is only supported by Marlin firmware\n" | ||||
|         if $self->use_firmware_retraction && $self->gcode_flavor ne 'reprap'; | ||||
|         if $self->use_firmware_retraction && $self->gcode_flavor ne 'reprap' && $self->gcode_flavor ne 'machinekit'; | ||||
|      | ||||
|     die "--use-firmware-retraction is not compatible with --wipe\n" | ||||
|         if $self->use_firmware_retraction && first {$_} @{$self->wipe}; | ||||
|  |  | |||
|  | @ -49,9 +49,10 @@ sub make_fill { | |||
|      | ||||
|     Slic3r::debugf "Filling layer %d:\n", $layerm->id; | ||||
|      | ||||
|     my $fill_density        = $layerm->config->fill_density; | ||||
|     my $infill_flow         = $layerm->flow(FLOW_ROLE_INFILL); | ||||
|     my $solid_infill_flow   = $layerm->flow(FLOW_ROLE_SOLID_INFILL); | ||||
|     my $fill_density            = $layerm->config->fill_density; | ||||
|     my $infill_flow             = $layerm->flow(FLOW_ROLE_INFILL); | ||||
|     my $solid_infill_flow       = $layerm->flow(FLOW_ROLE_SOLID_INFILL); | ||||
|     my $top_solid_infill_flow   = $layerm->flow(FLOW_ROLE_TOP_SOLID_INFILL); | ||||
|      | ||||
|     my @surfaces = (); | ||||
|      | ||||
|  | @ -75,7 +76,7 @@ sub make_fill { | |||
|                 if ($groups[$i][0]->is_solid && (!$groups[$i][0]->is_bridge || $layerm->id == 0)) { | ||||
|                     $is_solid[$i] = 1; | ||||
|                     $fw[$i] = ($groups[$i][0]->surface_type == S_TYPE_TOP) | ||||
|                         ? $layerm->flow(FLOW_ROLE_TOP_SOLID_INFILL)->width | ||||
|                         ? $top_solid_infill_flow->width | ||||
|                         : $solid_infill_flow->width; | ||||
|                     $pattern[$i] = $groups[$i][0]->is_external | ||||
|                         ? $layerm->config->external_fill_pattern | ||||
|  |  | |||
|  | @ -584,11 +584,14 @@ sub wipe { | |||
|             $gcode .= $gcodegen->writer->extrude_to_xy( | ||||
|                 $gcodegen->point_to_gcode($line->b), | ||||
|                 -$dE, | ||||
|                 'retract' . ($gcodegen->enable_cooling_markers ? ';_WIPE' : ''), | ||||
|                 'wipe and retract' . ($gcodegen->enable_cooling_markers ? ';_WIPE' : ''), | ||||
|             ); | ||||
|             $retracted += $dE; | ||||
|         } | ||||
|         $gcodegen->writer->extruder->set_retracted($gcodegen->writer->extruder->retracted + $retracted); | ||||
|          | ||||
|         # prevent wiping again on same path | ||||
|         $self->path(undef); | ||||
|     } | ||||
|      | ||||
|     return $gcode; | ||||
|  |  | |||
|  | @ -34,7 +34,7 @@ our $have_LWP    = eval "use LWP::UserAgent; 1"; | |||
| 
 | ||||
| use Wx 0.9901 qw(:bitmap :dialog :icon :id :misc :systemsettings :toplevelwindow | ||||
|     :filedialog); | ||||
| use Wx::Event qw(EVT_IDLE); | ||||
| use Wx::Event qw(EVT_IDLE EVT_COMMAND); | ||||
| use base 'Wx::App'; | ||||
| 
 | ||||
| use constant FILE_WILDCARDS => { | ||||
|  | @ -70,6 +70,8 @@ our $medium_font = Wx::SystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); | |||
| $medium_font->SetPointSize(12); | ||||
| our $grey = Wx::Colour->new(200,200,200); | ||||
| 
 | ||||
| our $VERSION_CHECK_EVENT : shared = Wx::NewEventType; | ||||
| 
 | ||||
| sub OnInit { | ||||
|     my ($self) = @_; | ||||
|      | ||||
|  | @ -143,6 +145,25 @@ sub OnInit { | |||
|         } | ||||
|     }); | ||||
|      | ||||
|     EVT_COMMAND($self, -1, $VERSION_CHECK_EVENT, sub { | ||||
|         my ($self, $event) = @_; | ||||
|         my ($success, $response, $manual_check) = @{$event->GetData}; | ||||
|          | ||||
|         if ($success) { | ||||
|             if ($response =~ /^obsolete ?= ?([a-z0-9.-]+,)*\Q$Slic3r::VERSION\E(?:,|$)/) { | ||||
|                 my $res = Wx::MessageDialog->new(undef, "A new version is available. Do you want to open the Slic3r website now?", | ||||
|                     'Update', wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_INFORMATION | wxICON_ERROR)->ShowModal; | ||||
|                 Wx::LaunchDefaultBrowser('http://slic3r.org/') if $res == wxID_YES; | ||||
|             } else { | ||||
|                 Slic3r::GUI::show_info(undef, "You're using the latest version. No updates are available.") if $manual_check; | ||||
|             } | ||||
|             $Settings->{_}{last_version_check} = time(); | ||||
|             $self->save_settings; | ||||
|         } else { | ||||
|             Slic3r::GUI::show_error(undef, "Failed to check for updates. Try later.") if $manual_check; | ||||
|         } | ||||
|     }); | ||||
|      | ||||
|     return 1; | ||||
| } | ||||
| 
 | ||||
|  | @ -239,7 +260,7 @@ sub have_version_check { | |||
| } | ||||
| 
 | ||||
| sub check_version { | ||||
|     my ($self, %p) = @_; | ||||
|     my ($self, $manual_check) = @_; | ||||
|      | ||||
|     Slic3r::debugf "Checking for updates...\n"; | ||||
|      | ||||
|  | @ -248,19 +269,9 @@ sub check_version { | |||
|         my $ua = LWP::UserAgent->new; | ||||
|         $ua->timeout(10); | ||||
|         my $response = $ua->get('http://slic3r.org/updatecheck'); | ||||
|         if ($response->is_success) { | ||||
|             if ($response->decoded_content =~ /^obsolete ?= ?([a-z0-9.-]+,)*\Q$Slic3r::VERSION\E(?:,|$)/) { | ||||
|                 my $res = Wx::MessageDialog->new(undef, "A new version is available. Do you want to open the Slic3r website now?", | ||||
|                     'Update', wxYES_NO | wxCANCEL | wxYES_DEFAULT | wxICON_INFORMATION | wxICON_ERROR)->ShowModal; | ||||
|                 Wx::LaunchDefaultBrowser('http://slic3r.org/') if $res == wxID_YES; | ||||
|             } else { | ||||
|                 Slic3r::GUI::show_info(undef, "You're using the latest version. No updates are available.") if $p{manual}; | ||||
|             } | ||||
|             $Settings->{_}{last_version_check} = time(); | ||||
|             $self->save_settings; | ||||
|         } else { | ||||
|             Slic3r::GUI::show_error(undef, "Failed to check for updates. Try later.") if $p{manual}; | ||||
|         } | ||||
|         Wx::PostEvent($self, Wx::PlThreadEvent->new(-1, $VERSION_CHECK_EVENT, | ||||
|             threads::shared::shared_clone([ $response->is_success, $response->decoded_content, $manual_check ]))); | ||||
|          | ||||
|         Slic3r::thread_cleanup(); | ||||
|     })->detach; | ||||
| } | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init | |||
|                               enable_cutting | ||||
|                               enable_picking | ||||
|                               enable_moving | ||||
|                               on_viewport_changed | ||||
|                               on_hover | ||||
|                               on_select | ||||
|                               on_double_click | ||||
|  | @ -108,6 +109,7 @@ sub new { | |||
|             -($pos->y - $size->y/2) * ($zoom) / $self->_zoom, | ||||
|             0, | ||||
|         ) if 0; | ||||
|         $self->on_viewport_changed->() if $self->on_viewport_changed; | ||||
|         $self->_dirty(1); | ||||
|         $self->Refresh; | ||||
|     }); | ||||
|  | @ -207,6 +209,7 @@ sub mouse_event { | |||
|                     ); | ||||
|                     $self->_quat(mulquats($self->_quat, \@quat)); | ||||
|                 } | ||||
|                 $self->on_viewport_changed->() if $self->on_viewport_changed; | ||||
|                 $self->Refresh; | ||||
|             } | ||||
|             $self->_drag_start_pos($pos); | ||||
|  | @ -220,6 +223,7 @@ sub mouse_event { | |||
|                 $self->_camera_target->translate( | ||||
|                     @{$orig->vector_to($cur_pos)->negative}, | ||||
|                 ); | ||||
|                 $self->on_viewport_changed->() if $self->on_viewport_changed; | ||||
|                 $self->Refresh; | ||||
|             } | ||||
|             $self->_drag_start_xy($pos); | ||||
|  | @ -256,6 +260,17 @@ sub reset_objects { | |||
|     $self->_dirty(1); | ||||
| } | ||||
| 
 | ||||
| sub set_viewport_from_scene { | ||||
|     my ($self, $scene) = @_; | ||||
|      | ||||
|     $self->_sphi($scene->_sphi); | ||||
|     $self->_stheta($scene->_stheta); | ||||
|     $self->_camera_target($scene->_camera_target); | ||||
|     $self->_zoom($scene->_zoom); | ||||
|     $self->_quat($scene->_quat); | ||||
|     $self->_dirty(1); | ||||
| } | ||||
| 
 | ||||
| sub zoom_to_bounding_box { | ||||
|     my ($self, $bb) = @_; | ||||
|      | ||||
|  | @ -267,6 +282,8 @@ sub zoom_to_bounding_box { | |||
|      | ||||
|     # center view around bounding box center | ||||
|     $self->_camera_target($bb->center); | ||||
|      | ||||
|     $self->on_viewport_changed->() if $self->on_viewport_changed; | ||||
| } | ||||
| 
 | ||||
| sub zoom_to_bed { | ||||
|  | @ -347,25 +364,24 @@ sub set_bed_shape { | |||
|     } | ||||
|      | ||||
|     { | ||||
|         my @lines = (); | ||||
|         my @polylines = (); | ||||
|         for (my $x = $bed_bb->x_min; $x <= $bed_bb->x_max; $x += scale 10) { | ||||
|             push @lines, Slic3r::Polyline->new([$x,$bed_bb->y_min], [$x,$bed_bb->y_max]); | ||||
|             push @polylines, Slic3r::Polyline->new([$x,$bed_bb->y_min], [$x,$bed_bb->y_max]); | ||||
|         } | ||||
|         for (my $y = $bed_bb->y_min; $y <= $bed_bb->y_max; $y += scale 10) { | ||||
|             push @lines, Slic3r::Polyline->new([$bed_bb->x_min,$y], [$bed_bb->x_max,$y]); | ||||
|             push @polylines, Slic3r::Polyline->new([$bed_bb->x_min,$y], [$bed_bb->x_max,$y]); | ||||
|         } | ||||
|         # clip with a slightly grown expolygon because our lines lay on the contours and | ||||
|         # may get erroneously clipped | ||||
|         @lines = @{intersection_pl(\@lines, [ @{$expolygon->offset(+scaled_epsilon)} ])}; | ||||
|         my @lines = map Slic3r::Line->new(@$_[0,-1]), | ||||
|             @{intersection_pl(\@polylines, [ @{$expolygon->offset(+scaled_epsilon)} ])}; | ||||
|          | ||||
|         # append bed contours | ||||
|         foreach my $line (map @{$_->lines}, @$expolygon) { | ||||
|             push @lines, $line->as_polyline; | ||||
|         } | ||||
|         push @lines, map @{$_->lines}, @$expolygon; | ||||
|          | ||||
|         my @points = (); | ||||
|         foreach my $polyline (@lines) { | ||||
|             push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$polyline;  #)) | ||||
|         foreach my $line (@lines) { | ||||
|             push @points, map {+ unscale($_->x), unscale($_->y), GROUND_Z } @$line;  #)) | ||||
|         } | ||||
|         $self->bed_grid_lines(OpenGL::Array->new_list(GL_FLOAT, @points)); | ||||
|     } | ||||
|  | @ -572,9 +588,10 @@ sub Resize { | |||
|      | ||||
|     glMatrixMode(GL_PROJECTION); | ||||
|     glLoadIdentity(); | ||||
|     my $depth = 10 * max(@{ $self->max_bounding_box->size }); | ||||
|     glOrtho( | ||||
|         -$x/2, $x/2, -$y/2, $y/2, | ||||
|         -200, 10 * max(@{ $self->max_bounding_box->size }), | ||||
|         -$depth, 2*$depth, | ||||
|     ); | ||||
|   | ||||
|     glMatrixMode(GL_MODELVIEW); | ||||
|  |  | |||
|  | @ -307,7 +307,7 @@ sub _repaint_canvas { | |||
|         @polylines = @{intersection_pl(\@polylines, [$bed_polygon])}; | ||||
|          | ||||
|         $dc->SetPen(Wx::Pen->new(Wx::Colour->new(230,230,230), 1, wxSOLID)); | ||||
|         $dc->DrawLine(map @{$to_pixel->([map unscale($_), @$_])}, @$_) for @polylines; | ||||
|         $dc->DrawLine(map @{$to_pixel->([map unscale($_), @$_])}, @$_[0,-1]) for @polylines; | ||||
|     } | ||||
|      | ||||
|     # draw bed contour | ||||
|  |  | |||
|  | @ -255,7 +255,7 @@ sub _init_menubar { | |||
|             Wx::LaunchDefaultBrowser('http://slic3r.org/'); | ||||
|         }); | ||||
|         my $versioncheck = $self->_append_menu_item($helpMenu, "Check for &Updates...", 'Check for new Slic3r versions', sub { | ||||
|             wxTheApp->check_version(manual => 1); | ||||
|             wxTheApp->check_version(1); | ||||
|         }); | ||||
|         $versioncheck->Enable(wxTheApp->have_version_check); | ||||
|         $self->_append_menu_item($helpMenu, "Slic3r &Manual", 'Open the Slic3r manual in your browser', sub { | ||||
|  | @ -678,7 +678,7 @@ sub config { | |||
|     } else { | ||||
|         my $extruders_count = $self->{options_tabs}{printer}{extruders_count}; | ||||
|         $config->set("${_}_extruder", min($config->get("${_}_extruder"), $extruders_count)) | ||||
|             for qw(perimeter infill support_material support_material_interface); | ||||
|             for qw(perimeter infill solid_infill support_material support_material_interface); | ||||
|     } | ||||
|      | ||||
|     return $config; | ||||
|  |  | |||
|  | @ -96,6 +96,9 @@ sub new { | |||
|         $self->{canvas3D}->set_on_double_click($on_double_click); | ||||
|         $self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); }); | ||||
|         $self->{canvas3D}->set_on_instances_moved($on_instances_moved); | ||||
|         $self->{canvas3D}->on_viewport_changed(sub { | ||||
|             $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D}); | ||||
|         }); | ||||
|     } | ||||
|      | ||||
|     # Initialize 2D preview canvas | ||||
|  | @ -109,6 +112,9 @@ sub new { | |||
|     # Initialize 3D toolpaths preview | ||||
|     if ($Slic3r::GUI::have_OpenGL) { | ||||
|         $self->{preview3D} = Slic3r::GUI::Plater::3DPreview->new($self->{preview_notebook}, $self->{print}); | ||||
|         $self->{preview3D}->canvas->on_viewport_changed(sub { | ||||
|             $self->{canvas3D}->set_viewport_from_scene($self->{preview3D}->canvas); | ||||
|         }); | ||||
|         $self->{preview_notebook}->AddPage($self->{preview3D}, 'Preview'); | ||||
|         $self->{preview3D_page_idx} = $self->{preview_notebook}->GetPageCount-1; | ||||
|     } | ||||
|  | @ -988,9 +994,13 @@ sub pause_background_process { | |||
|      | ||||
|     if ($self->{process_thread} || $self->{export_thread}) { | ||||
|         Slic3r::pause_all_threads(); | ||||
|         return 1; | ||||
|     } elsif (defined $self->{apply_config_timer} && $self->{apply_config_timer}->IsRunning) { | ||||
|         $self->{apply_config_timer}->Stop; | ||||
|         return 1; | ||||
|     } | ||||
|      | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| sub resume_background_process { | ||||
|  | @ -1312,9 +1322,14 @@ sub update { | |||
|         $self->{model}->center_instances_around_point($self->bed_centerf); | ||||
|     } | ||||
|      | ||||
|     $self->pause_background_process; | ||||
|     my $running = $self->pause_background_process; | ||||
|     my $invalidated = $self->{print}->reload_model_instances(); | ||||
|     if ($invalidated) { | ||||
|      | ||||
|     # The mere fact that no steps were invalidated when reloading model instances  | ||||
|     # doesn't mean that all steps were done: for example, validation might have  | ||||
|     # failed upon previous instance move, so we have no running thread and no steps | ||||
|     # are invalidated on this move, thus we need to schedule a new run. | ||||
|     if ($invalidated || !$running) { | ||||
|         $self->schedule_background_process; | ||||
|     } else { | ||||
|         $self->resume_background_process; | ||||
|  | @ -1323,12 +1338,6 @@ sub update { | |||
|     $self->refresh_canvases; | ||||
| } | ||||
| 
 | ||||
| sub on_model_instances_changed { | ||||
|     my ($self) = @_; | ||||
|      | ||||
|      | ||||
| } | ||||
| 
 | ||||
| sub on_extruders_change { | ||||
|     my ($self, $num_extruders) = @_; | ||||
|      | ||||
|  |  | |||
|  | @ -276,7 +276,7 @@ sub update_bed_size { | |||
|             push @polylines, Slic3r::Polyline->new([$bb->x_min, $y], [$bb->x_max, $y]); | ||||
|         } | ||||
|         @polylines = @{intersection_pl(\@polylines, [$polygon])}; | ||||
|         $self->{grid} = [ map $self->scaled_points_to_pixel(\@$_, 1), @polylines ]; | ||||
|         $self->{grid} = [ map $self->scaled_points_to_pixel([ @$_[0,-1] ], 1), @polylines ]; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -291,7 +291,7 @@ sub Render { | |||
|             $brim_drawn = 1; | ||||
|         } | ||||
|         if ($self->print->step_done(STEP_SKIRT) | ||||
|             && ($self->print->config->skirt_height == -1 || $self->print->config->skirt_height > $layer->id) | ||||
|             && ($self->print->has_infinite_skirt() || $self->print->config->skirt_height > $layer->id) | ||||
|             && !$skirt_drawn) { | ||||
|             $self->color([0, 0, 0]); | ||||
|             $self->_draw(undef, $print_z, $_) for @{$self->print->skirt}; | ||||
|  |  | |||
|  | @ -37,9 +37,9 @@ sub new { | |||
|          | ||||
|         # buttons | ||||
|         $self->{btn_save_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/disk.png", wxBITMAP_TYPE_PNG),  | ||||
|             wxDefaultPosition, [16,16], wxBORDER_NONE); | ||||
|             wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); | ||||
|         $self->{btn_delete_preset} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new("$Slic3r::var/delete.png", wxBITMAP_TYPE_PNG),  | ||||
|             wxDefaultPosition, [16,16], wxBORDER_NONE); | ||||
|             wxDefaultPosition, wxDefaultSize, wxBORDER_NONE); | ||||
|         $self->{btn_save_preset}->SetToolTipString("Save current " . lc($self->title)); | ||||
|         $self->{btn_delete_preset}->SetToolTipString("Delete this preset"); | ||||
|         $self->{btn_delete_preset}->Disable; | ||||
|  | @ -745,11 +745,13 @@ sub _update { | |||
|             perimeter_speed small_perimeter_speed external_perimeter_speed); | ||||
|      | ||||
|     my $have_infill = $config->fill_density > 0; | ||||
|     # infill_extruder uses the same logic as in Print::extruders() | ||||
|     $self->get_field($_)->toggle($have_infill) | ||||
|         for qw(fill_pattern infill_every_layers infill_only_where_needed solid_infill_every_layers | ||||
|             solid_infill_below_area infill_extruder); | ||||
|      | ||||
|     my $have_solid_infill = ($config->top_solid_layers > 0) || ($config->bottom_solid_layers > 0); | ||||
|     # solid_infill_extruder uses the same logic as in Print::extruders() | ||||
|     $self->get_field($_)->toggle($have_solid_infill) | ||||
|         for qw(external_fill_pattern infill_first solid_infill_extruder solid_infill_extrusion_width | ||||
|             solid_infill_speed); | ||||
|  | @ -772,6 +774,7 @@ sub _update { | |||
|         for qw(skirt_distance skirt_height); | ||||
|      | ||||
|     my $have_brim = $config->brim_width > 0; | ||||
|     # perimeter_extruder uses the same logic as in Print::extruders() | ||||
|     $self->get_field('perimeter_extruder')->toggle($have_perimeters || $have_brim); | ||||
|      | ||||
|     my $have_support_material = $config->support_material || $config->raft_layers > 0; | ||||
|  |  | |||
|  | @ -286,7 +286,7 @@ sub process { | |||
|             # where 0.5*$pwidth < thickness < $pwidth, infill with width = 0.5*$pwidth | ||||
|             my @gap_sizes = ( | ||||
|                 [ $pwidth, 2*$pspacing, unscale 1.5*$pwidth ], | ||||
|                 [ 0.5*$pwidth, $pwidth, unscale 0.5*$pwidth ], | ||||
|                 [ 0.1*$pwidth, $pwidth, unscale 0.5*$pwidth ], | ||||
|             ); | ||||
|             foreach my $gap_size (@gap_sizes) { | ||||
|                 my @gap_fill = $self->_fill_gaps(@$gap_size, \@gaps); | ||||
|  | @ -311,12 +311,22 @@ sub process { | |||
|         # we offset by half the perimeter spacing (to get to the actual infill boundary) | ||||
|         # and then we offset back and forth by half the infill spacing to only consider the | ||||
|         # non-collapsing regions | ||||
|         my $inset = 0; | ||||
|         if ($loop_number == 0) { | ||||
|             # one loop | ||||
|             $inset += $ext_pspacing/2; | ||||
|         } elsif ($loop_number > 0) { | ||||
|             # two or more loops | ||||
|             $inset += $pspacing/2; | ||||
|         } | ||||
|         $inset -= $self->config->get_abs_value_over('infill_overlap', $pwidth); | ||||
|          | ||||
|         my $min_perimeter_infill_spacing = $ispacing * (1 - &Slic3r::INSET_OVERLAP_TOLERANCE); | ||||
|         $self->fill_surfaces->append($_) | ||||
|             for map Slic3r::Surface->new(expolygon => $_, surface_type => S_TYPE_INTERNAL),  # use a bogus surface type | ||||
|                 @{offset2_ex( | ||||
|                     [ map @{$_->simplify_p(&Slic3r::SCALED_RESOLUTION)}, @{union_ex(\@last)} ], | ||||
|                     -($pspacing/2 - $self->config->get_abs_value_over('infill_overlap', $pwidth) + $min_perimeter_infill_spacing/2), | ||||
|                     -$inset -$min_perimeter_infill_spacing/2, | ||||
|                     +$min_perimeter_infill_spacing/2, | ||||
|                 )}; | ||||
|     } | ||||
|  | @ -384,8 +394,8 @@ sub _traverse_loops { | |||
|             push @paths, Slic3r::ExtrusionPath->new( | ||||
|                 polyline        => $loop->polygon->split_at_first_point, | ||||
|                 role            => $role, | ||||
|                 mm3_per_mm      => $self->_mm3_per_mm, | ||||
|                 width           => $self->perimeter_flow->width, | ||||
|                 mm3_per_mm      => ($is_external ? $self->_ext_mm3_per_mm : $self->_mm3_per_mm), | ||||
|                 width           => ($is_external ? $self->ext_perimeter_flow->width : $self->perimeter_flow->width), | ||||
|                 height          => $self->layer_height, | ||||
|             ); | ||||
|         } | ||||
|  | @ -441,11 +451,14 @@ sub _traverse_loops { | |||
| sub _fill_gaps { | ||||
|     my ($self, $min, $max, $w, $gaps) = @_; | ||||
|      | ||||
|     $min *= (1 - &Slic3r::INSET_OVERLAP_TOLERANCE); | ||||
|      | ||||
|     my $this = diff_ex( | ||||
|         offset2([ map @$_, @$gaps ], -$min/2, +$min/2), | ||||
|         offset2([ map @$_, @$gaps ], -$max/2, +$max/2), | ||||
|         1, | ||||
|     ); | ||||
|      | ||||
|     my @polylines = map @{$_->medial_axis($max, $min/2)}, @$this; | ||||
|     return if !@polylines; | ||||
|      | ||||
|  |  | |||
|  | @ -89,7 +89,8 @@ sub export_gcode { | |||
|         $self->config->setenv; | ||||
|         for my $script (@{$self->config->post_process}) { | ||||
|             Slic3r::debugf "  '%s' '%s'\n", $script, $output_file; | ||||
|             if (!-x $script) { | ||||
|             # -x doesn't return true on Windows except for .exe files | ||||
|             if (($^O eq 'MSWin32') ? !(-e $script) : !(-x $script)) { | ||||
|                 die "The configured post-processing script is not executable: check permissions. ($script)\n"; | ||||
|             } | ||||
|             system($script, $output_file); | ||||
|  | @ -101,9 +102,6 @@ sub export_svg { | |||
|     my $self = shift; | ||||
|     my %params = @_; | ||||
|      | ||||
|     # is this needed? | ||||
|     $self->init_extruders; | ||||
|      | ||||
|     $_->slice for @{$self->objects}; | ||||
|      | ||||
|     my $fh = $params{output_fh}; | ||||
|  | @ -208,8 +206,7 @@ sub make_skirt { | |||
|     # checking whether we need to generate them | ||||
|     $self->skirt->clear; | ||||
|      | ||||
|     if (($self->config->skirts == 0 || $self->config->skirt_height == 0) | ||||
|         && (!$self->config->ooze_prevention || @{$self->extruders} == 1)) { | ||||
|     if (!$self->has_skirt) { | ||||
|         $self->set_step_done(STEP_SKIRT); | ||||
|         return; | ||||
|     } | ||||
|  | @ -219,7 +216,7 @@ sub make_skirt { | |||
|     # The skirt_height option from config is expressed in layers, but our | ||||
|     # object might have different layer heights, so we need to find the print_z | ||||
|     # of the highest layer involved. | ||||
|     # Note that unless skirt_height == -1 (which means it's printed on all layers) | ||||
|     # Note that unless has_infinite_skirt() == true | ||||
|     # the actual skirt might not reach this $skirt_height_z value since the print | ||||
|     # order of objects on each layer is not guaranteed and will not generally | ||||
|     # include the thickest object first. It is just guaranteed that a skirt is | ||||
|  | @ -227,10 +224,9 @@ sub make_skirt { | |||
|     # $skirt_height_z in this case is the highest possible skirt height for safety. | ||||
|     my $skirt_height_z = -1; | ||||
|     foreach my $object (@{$self->objects}) { | ||||
|         my $skirt_height = ($self->config->skirt_height == -1 || $self->config->ooze_prevention) | ||||
|         my $skirt_height = $self->has_infinite_skirt | ||||
|             ? scalar(@{$object->layers}) | ||||
|             : min($self->config->skirt_height, scalar(@{$object->layers})); | ||||
|          | ||||
|         my $highest_layer = $object->get_layer($skirt_height - 1); | ||||
|         $skirt_height_z = max($skirt_height_z, $highest_layer->print_z); | ||||
|     } | ||||
|  | @ -278,10 +274,13 @@ sub make_skirt { | |||
|     my @extruders_e_per_mm = (); | ||||
|     my $extruder_idx = 0; | ||||
|      | ||||
|     my $skirts = $self->config->skirts; | ||||
|     $skirts ||= 1 if $self->has_infinite_skirt; | ||||
|      | ||||
|     # draw outlines from outside to inside | ||||
|     # loop while we have less skirts than required or any extruder hasn't reached the min length if any | ||||
|     my $distance = scale max($self->config->skirt_distance, $self->config->brim_width); | ||||
|     for (my $i = $self->config->skirts; $i > 0; $i--) { | ||||
|     for (my $i = $skirts; $i > 0; $i--) { | ||||
|         $distance += scale $spacing; | ||||
|         my $loop = offset([$convex_hull], $distance, 1, JT_ROUND, scale(0.1))->[0]; | ||||
|         $self->skirt->append(Slic3r::ExtrusionLoop->new_from_paths( | ||||
|  | @ -428,10 +427,10 @@ sub expanded_output_filepath { | |||
|      | ||||
|     my $filename = my $filename_base = basename($input_file); | ||||
|     $filename_base =~ s/\.[^.]+$//;  # without suffix | ||||
|     my $extra = { | ||||
|         input_filename      => $filename, | ||||
|         input_filename_base => $filename_base, | ||||
|     }; | ||||
|      | ||||
|     # set filename in placeholder parser so that it's available also in custom G-code | ||||
|     $self->placeholder_parser->set(input_filename => $filename); | ||||
|     $self->placeholder_parser->set(input_filename_base => $filename_base); | ||||
|      | ||||
|     if ($path && -d $path) { | ||||
|         # if output path is an existing directory, we take that and append | ||||
|  | @ -447,7 +446,7 @@ sub expanded_output_filepath { | |||
|      | ||||
|     # make sure we use an up-to-date timestamp | ||||
|     $self->placeholder_parser->update_timestamp; | ||||
|     return $self->placeholder_parser->process($path, $extra); | ||||
|     return $self->placeholder_parser->process($path); | ||||
| } | ||||
| 
 | ||||
| # This method assigns extruders to the volumes having a material | ||||
|  |  | |||
|  | @ -195,12 +195,14 @@ sub export { | |||
|                 # no collision happens hopefully. | ||||
|                 if ($finished_objects > 0) { | ||||
|                     $gcodegen->set_origin(Slic3r::Pointf->new(map unscale $copy->[$_], X,Y)); | ||||
|                     $gcodegen->enable_cooling_markers(0);  # we're not filtering these moves through CoolingBuffer | ||||
|                     print $fh $gcodegen->retract; | ||||
|                     print $fh $gcodegen->travel_to( | ||||
|                         Slic3r::Point->new(0,0), | ||||
|                         undef, | ||||
|                         'move to origin position for next object', | ||||
|                     ); | ||||
|                     $gcodegen->enable_cooling_markers(1); | ||||
|                 } | ||||
|                  | ||||
|                 my @layers = sort { $a->print_z <=> $b->print_z } @{$object->layers}, @{$object->support_layers}; | ||||
|  | @ -217,6 +219,7 @@ sub export { | |||
|                 } | ||||
|                 $self->flush_filters; | ||||
|                 $finished_objects++; | ||||
|                 $self->_second_layer_things_done(0); | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|  | @ -249,6 +252,7 @@ sub export { | |||
|     print $fh $gcodegen->writer->set_fan(0); | ||||
|     printf $fh "%s\n", $gcodegen->placeholder_parser->process($self->config->end_gcode); | ||||
|     print $fh $gcodegen->writer->update_progress($gcodegen->layer_count, $gcodegen->layer_count, 1);  # 100% | ||||
|     print $fh $gcodegen->writer->postamble; | ||||
|      | ||||
|     # get filament stats | ||||
|     $self->print->clear_filament_stats; | ||||
|  | @ -299,7 +303,7 @@ sub process_layer { | |||
|     if (defined $self->_spiral_vase) { | ||||
|         $self->_spiral_vase->enable( | ||||
|             ($layer->id > 0 || $self->print->config->brim_width == 0) | ||||
|                 && ($layer->id >= $self->print->config->skirt_height && $self->print->config->skirt_height != -1) | ||||
|                 && ($layer->id >= $self->print->config->skirt_height && !$self->print->has_infinite_skirt) | ||||
|                 && !defined(first { $_->config->bottom_solid_layers > $layer->id } @{$layer->regions}) | ||||
|                 && !defined(first { @{$_->perimeters} > 1 } @{$layer->regions}) | ||||
|                 && !defined(first { @{$_->fills} > 0 } @{$layer->regions}) | ||||
|  | @ -332,14 +336,15 @@ sub process_layer { | |||
|     }) . "\n" if $self->print->config->layer_gcode; | ||||
|      | ||||
|     # extrude skirt | ||||
|     if (((values %{$self->_skirt_done}) < $self->print->config->skirt_height || $self->print->config->skirt_height == -1) | ||||
|         && !$self->_skirt_done->{$layer->print_z}) { | ||||
|     if (((values %{$self->_skirt_done}) < $self->print->config->skirt_height || $self->print->has_infinite_skirt) | ||||
|         && !$self->_skirt_done->{$layer->print_z} | ||||
|         && !$layer->isa('Slic3r::Layer::Support')) { | ||||
|         $self->_gcodegen->set_origin(Slic3r::Pointf->new(0,0)); | ||||
|         $self->_gcodegen->avoid_crossing_perimeters->use_external_mp(1); | ||||
|         my @extruder_ids = map { $_->id } @{$self->_gcodegen->writer->extruders}; | ||||
|         $gcode .= $self->_gcodegen->set_extruder($extruder_ids[0]); | ||||
|         # skip skirt if we have a large brim | ||||
|         if ($layer->id < $self->print->config->skirt_height || $self->print->config->skirt_height == -1) { | ||||
|         if ($layer->id < $self->print->config->skirt_height || $self->print->has_infinite_skirt) { | ||||
|             my $skirt_flow = $self->print->skirt_flow; | ||||
|              | ||||
|             # distribute skirt loops across all extruders | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ use List::Util qw(min max sum first); | |||
| use Slic3r::Flow ':roles'; | ||||
| use Slic3r::Geometry qw(X Y Z PI scale unscale chained_path); | ||||
| use Slic3r::Geometry::Clipper qw(diff diff_ex intersection intersection_ex union union_ex  | ||||
|     offset offset_ex offset2 offset2_ex CLIPPER_OFFSET_SCALE JT_MITER); | ||||
|     offset offset_ex offset2 offset2_ex intersection_ppl CLIPPER_OFFSET_SCALE JT_MITER); | ||||
| use Slic3r::Print::State ':steps'; | ||||
| use Slic3r::Surface ':types'; | ||||
| 
 | ||||
|  | @ -344,7 +344,6 @@ sub make_perimeters { | |||
|     my $self = shift; | ||||
|      | ||||
|     # prerequisites | ||||
|     $self->print->init_extruders; | ||||
|     $self->slice; | ||||
|      | ||||
|     return if $self->step_done(STEP_PERIMETERS); | ||||
|  | @ -369,51 +368,59 @@ sub make_perimeters { | |||
|         my $region = $self->print->regions->[$region_id]; | ||||
|         my $region_perimeters = $region->config->perimeters; | ||||
|          | ||||
|         if ($region->config->extra_perimeters && $region_perimeters > 0 && $region->config->fill_density > 0) { | ||||
|             for my $i (0 .. ($self->layer_count - 2)) { | ||||
|                 my $layerm          = $self->get_layer($i)->regions->[$region_id]; | ||||
|                 my $upper_layerm    = $self->get_layer($i+1)->regions->[$region_id]; | ||||
|                 my $perimeter_spacing       = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing; | ||||
|                 my $ext_perimeter_spacing   = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER)->scaled_spacing; | ||||
|                  | ||||
|                 my $overlap = $perimeter_spacing;  # one perimeter | ||||
|                  | ||||
|                 my $diff = diff( | ||||
|                     offset([ map @{$_->expolygon}, @{$layerm->slices} ], -($ext_perimeter_spacing + ($region_perimeters-1) * $perimeter_spacing)), | ||||
|                     offset([ map @{$_->expolygon}, @{$upper_layerm->slices} ], -$overlap), | ||||
|                 ); | ||||
|                 next if !@$diff; | ||||
|                 # if we need more perimeters, $diff should contain a narrow region that we can collapse | ||||
|                  | ||||
|                 # we use a higher miterLimit here to handle areas with acute angles | ||||
|                 # in those cases, the default miterLimit would cut the corner and we'd | ||||
|                 # get a triangle that would trigger a non-needed extra perimeter | ||||
|                 $diff = diff( | ||||
|                     $diff, | ||||
|                     offset2($diff, -$perimeter_spacing, +$perimeter_spacing, CLIPPER_OFFSET_SCALE, JT_MITER, 5), | ||||
|                     1, | ||||
|                 ); | ||||
|                 next if !@$diff; | ||||
|                 # diff contains the collapsed area | ||||
|                  | ||||
|                 foreach my $slice (@{$layerm->slices}) { | ||||
|                     my $extra_perimeters = 0; | ||||
|                     CYCLE: while (1) { | ||||
|                         # compute polygons representing the thickness of the hypotetical new internal perimeter | ||||
|                         # of our slice | ||||
|                         $extra_perimeters++; | ||||
|                         my $hypothetical_perimeter = diff( | ||||
|                             offset($slice->expolygon->arrayref, -($perimeter_spacing * ($region_perimeters + $extra_perimeters-1))), | ||||
|                             offset($slice->expolygon->arrayref, -($perimeter_spacing * ($region_perimeters + $extra_perimeters))), | ||||
|         next if !$region->config->extra_perimeters; | ||||
|         next if $region_perimeters == 0; | ||||
|         next if $region->config->fill_density == 0; | ||||
|          | ||||
|         for my $i (0 .. ($self->layer_count - 2)) { | ||||
|             my $layerm                  = $self->get_layer($i)->get_region($region_id); | ||||
|             my $upper_layerm            = $self->get_layer($i+1)->get_region($region_id); | ||||
|              | ||||
|             my $perimeter_spacing       = $layerm->flow(FLOW_ROLE_PERIMETER)->scaled_spacing; | ||||
|             my $ext_perimeter_flow      = $layerm->flow(FLOW_ROLE_EXTERNAL_PERIMETER); | ||||
|             my $ext_perimeter_width     = $ext_perimeter_flow->scaled_width; | ||||
|             my $ext_perimeter_spacing   = $ext_perimeter_flow->scaled_spacing; | ||||
|              | ||||
|             foreach my $slice (@{$layerm->slices}) { | ||||
|                 while (1) { | ||||
|                     # compute the total thickness of perimeters | ||||
|                     my $perimeters_thickness = $ext_perimeter_width/2 + $ext_perimeter_spacing/2 | ||||
|                         + ($region_perimeters-1 + $slice->extra_perimeters) * $perimeter_spacing; | ||||
|                      | ||||
|                     # define a critical area where we don't want the upper slice to fall into | ||||
|                     # (it should either lay over our perimeters or outside this area) | ||||
|                     my $critical_area_depth = $perimeter_spacing*1.5; | ||||
|                     my $critical_area = diff( | ||||
|                         offset($slice->expolygon->arrayref, -$perimeters_thickness), | ||||
|                         offset($slice->expolygon->arrayref, -($perimeters_thickness + $critical_area_depth)), | ||||
|                     ); | ||||
|                      | ||||
|                     # check whether a portion of the upper slices falls inside the critical area | ||||
|                     my $intersection = intersection_ppl( | ||||
|                         [ map $_->p, @{$upper_layerm->slices} ], | ||||
|                         $critical_area, | ||||
|                     ); | ||||
|                      | ||||
|                     # only add an additional loop if at least 30% of the slice loop would benefit from it | ||||
|                     my $total_loop_length = sum(map $_->length, map $_->p, @{$upper_layerm->slices}) // 0; | ||||
|                     my $total_intersection_length = sum(map $_->length, @$intersection) // 0; | ||||
|                     last unless $total_intersection_length > $total_loop_length*0.3; | ||||
|                      | ||||
|                     if (0) { | ||||
|                         require "Slic3r/SVG.pm"; | ||||
|                         Slic3r::SVG::output( | ||||
|                             "extra.svg", | ||||
|                             no_arrows   => 1, | ||||
|                             expolygons  => union_ex($critical_area), | ||||
|                             polylines   => [ map $_->split_at_first_point, map $_->p, @{$upper_layerm->slices} ], | ||||
|                         ); | ||||
|                         last CYCLE if !@$hypothetical_perimeter;  # no extra perimeter is possible | ||||
|                          | ||||
|                         # only add the perimeter if there's an intersection with the collapsed area | ||||
|                         last CYCLE if !@{ intersection($diff, $hypothetical_perimeter) }; | ||||
|                         Slic3r::debugf "  adding one more perimeter at layer %d\n", $layerm->id; | ||||
|                         $slice->extra_perimeters($extra_perimeters); | ||||
|                     } | ||||
|                      | ||||
|                     $slice->extra_perimeters($slice->extra_perimeters + 1); | ||||
|                 } | ||||
|                 Slic3r::debugf "  adding %d more perimeter(s) at layer %d\n", | ||||
|                     $slice->extra_perimeters, $layerm->id | ||||
|                     if $slice->extra_perimeters > 0; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -524,7 +531,6 @@ sub generate_support_material { | |||
|     my $self = shift; | ||||
|      | ||||
|     # prerequisites | ||||
|     $self->print->init_extruders; | ||||
|     $self->slice; | ||||
|      | ||||
|     return if $self->step_done(STEP_SUPPORTMATERIAL); | ||||
|  | @ -547,7 +553,7 @@ sub _support_material { | |||
|     my ($self) = @_; | ||||
|      | ||||
|     my $first_layer_flow = Slic3r::Flow->new_from_width( | ||||
|         width               => ($self->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width), | ||||
|         width               => ($self->print->config->first_layer_extrusion_width || $self->config->support_material_extrusion_width), | ||||
|         role                => FLOW_ROLE_SUPPORT_MATERIAL, | ||||
|         nozzle_diameter     => $self->print->config->nozzle_diameter->[ $self->config->support_material_extruder-1 ] | ||||
|                                 // $self->print->config->nozzle_diameter->[0], | ||||
|  | @ -650,7 +656,7 @@ sub detect_surfaces_type { | |||
|                  | ||||
|                 # if we have raft layers, consider bottom layer as a bridge | ||||
|                 # just like any other bottom surface lying on the void | ||||
|                 if ($self->config->raft_layers > 0) { | ||||
|                 if ($self->config->raft_layers > 0 && $self->config->support_material_contact_distance > 0) { | ||||
|                     $_->surface_type(S_TYPE_BOTTOMBRIDGE) for @bottom; | ||||
|                 } else { | ||||
|                     $_->surface_type(S_TYPE_BOTTOM) for @bottom; | ||||
|  | @ -714,61 +720,95 @@ sub clip_fill_surfaces { | |||
|     # We only want infill under ceilings; this is almost like an | ||||
|     # internal support material. | ||||
|      | ||||
|     my $additional_margin = scale 3*0; | ||||
|      | ||||
|     my $overhangs = [];  # arrayref of polygons | ||||
|     for my $layer_id (reverse 0..($self->layer_count - 1)) { | ||||
|         my $layer = $self->get_layer($layer_id); | ||||
|         my @layer_internal = ();  # arrayref of Surface objects | ||||
|         my @new_internal = ();    # arrayref of Surface objects | ||||
|     # proceed top-down skipping bottom layer | ||||
|     my $upper_internal = []; | ||||
|     for my $layer_id (reverse 1..($self->layer_count - 1)) { | ||||
|         my $layer       = $self->get_layer($layer_id); | ||||
|         my $lower_layer = $self->get_layer($layer_id-1); | ||||
|          | ||||
|         # clip this layer's internal surfaces to @overhangs | ||||
|         foreach my $layerm (@{$layer->regions}) { | ||||
|         # detect things that we need to support | ||||
|         my $overhangs = [];  # Polygons | ||||
|          | ||||
|         # we need to support any solid surface | ||||
|         push @$overhangs, map $_->p, | ||||
|             grep $_->is_solid, map @{$_->fill_surfaces}, @{$layer->regions}; | ||||
|          | ||||
|         # we also need to support perimeters when there's at least one full | ||||
|         # unsupported loop | ||||
|         { | ||||
|             # get perimeters area as the difference between slices and fill_surfaces | ||||
|             my $perimeters = diff( | ||||
|                 [ map @$_, @{$layer->slices} ], | ||||
|                 [ map $_->p, map @{$_->fill_surfaces}, @{$layer->regions} ], | ||||
|             ); | ||||
|              | ||||
|             # only consider the area that is not supported by lower perimeters | ||||
|             $perimeters = intersection( | ||||
|                 $perimeters, | ||||
|                 [ map $_->p, map @{$_->fill_surfaces}, @{$lower_layer->regions} ], | ||||
|                 1, | ||||
|             ); | ||||
|              | ||||
|             # only consider perimeter areas that are at least one extrusion width thick | ||||
|             my $pw = min(map $_->flow(FLOW_ROLE_PERIMETER)->scaled_width, @{$layer->regions}); | ||||
|             $perimeters = offset2($perimeters, -$pw, +$pw); | ||||
|              | ||||
|             # append such thick perimeters to the areas that need support | ||||
|             push @$overhangs, @$perimeters; | ||||
|         } | ||||
|          | ||||
|         # find new internal infill | ||||
|         $upper_internal = my $new_internal = intersection( | ||||
|             [ | ||||
|                 @$overhangs, | ||||
|                 @$upper_internal, | ||||
|             ], | ||||
|             [ | ||||
|                 # our current internal fill boundaries | ||||
|                 map $_->p, | ||||
|                     grep $_->surface_type == S_TYPE_INTERNAL || $_->surface_type == S_TYPE_INTERNALVOID, | ||||
|                         map @{$_->fill_surfaces}, @{$lower_layer->regions} | ||||
|             ], | ||||
|         ); | ||||
|          | ||||
|         # apply new internal infill to regions | ||||
|         foreach my $layerm (@{$lower_layer->regions}) { | ||||
|             my (@internal, @other) = (); | ||||
|             foreach my $surface (map $_->clone, @{$layerm->fill_surfaces}) { | ||||
|                 if ($surface->surface_type == S_TYPE_INTERNAL) { | ||||
|                 if ($surface->surface_type == S_TYPE_INTERNAL || $surface->surface_type == S_TYPE_INTERNALVOID) { | ||||
|                     push @internal, $surface; | ||||
|                 } else { | ||||
|                     push @other, $surface; | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|             # keep all the original internal surfaces to detect overhangs in this layer | ||||
|             push @layer_internal, @internal; | ||||
|              | ||||
|             push @new_internal, my @new = map Slic3r::Surface->new( | ||||
|             my @new = map Slic3r::Surface->new( | ||||
|                 expolygon       => $_, | ||||
|                 surface_type    => S_TYPE_INTERNAL, | ||||
|             ), | ||||
|                 @{intersection_ex( | ||||
|                     [ map $_->p, @internal ], | ||||
|                     $overhangs, | ||||
|                     $new_internal, | ||||
|                     1, | ||||
|                 )}; | ||||
|              | ||||
|             push @new, map Slic3r::Surface->new( | ||||
|             push @other, map Slic3r::Surface->new( | ||||
|                 expolygon       => $_, | ||||
|                 surface_type    => S_TYPE_INTERNALVOID, | ||||
|             ), | ||||
|                 @{diff_ex( | ||||
|                     [ map $_->p, @internal ], | ||||
|                     $overhangs, | ||||
|                     $new_internal, | ||||
|                     1, | ||||
|                 )}; | ||||
|              | ||||
|             # If there are voids it means that our internal infill is not adjacent to | ||||
|             # perimeters. In this case it would be nice to add a loop around infill to | ||||
|             # make it more robust and nicer. TODO. | ||||
|              | ||||
|             $layerm->fill_surfaces->clear; | ||||
|             $layerm->fill_surfaces->append($_) for (@new, @other); | ||||
|         } | ||||
|          | ||||
|         # get this layer's overhangs defined as the full slice minus the internal infill | ||||
|         # (thus we also consider perimeters) | ||||
|         if ($layer_id > 0) { | ||||
|             my $solid = diff( | ||||
|                 [ map $_->p, map @{$_->fill_surfaces}, @{$layer->regions} ], | ||||
|                 [ map $_->p, @layer_internal ], | ||||
|             ); | ||||
|             $overhangs = offset($solid, +$additional_margin); | ||||
|              | ||||
|             push @$overhangs, map $_->p, @new_internal;  # propagate upper overhangs | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -4,7 +4,7 @@ use warnings; | |||
| 
 | ||||
| require Exporter; | ||||
| our @ISA = qw(Exporter); | ||||
| our @EXPORT_OK   = qw(STEP_INIT_EXTRUDERS STEP_SLICE STEP_PERIMETERS STEP_PREPARE_INFILL  | ||||
| our @EXPORT_OK   = qw(STEP_SLICE STEP_PERIMETERS STEP_PREPARE_INFILL  | ||||
|                     STEP_INFILL STEP_SUPPORTMATERIAL STEP_SKIRT STEP_BRIM); | ||||
| our %EXPORT_TAGS = (steps => \@EXPORT_OK); | ||||
| 
 | ||||
|  |  | |||
|  | @ -723,6 +723,11 @@ sub generate_toolpaths { | |||
|         if (@$base) { | ||||
|             my $filler = $fillers{support}; | ||||
|             $filler->angle($angles[ ($layer_id) % @angles ]); | ||||
|              | ||||
|             # We don't use $base_flow->spacing because we need a constant spacing | ||||
|             # value that guarantees that all layers are correctly aligned. | ||||
|             $filler->spacing($flow->spacing); | ||||
|              | ||||
|             my $density     = $support_density; | ||||
|             my $base_flow   = $_flow; | ||||
|              | ||||
|  | @ -737,6 +742,10 @@ sub generate_toolpaths { | |||
|                 $filler->angle($self->object_config->support_material_angle + 90); | ||||
|                 $density        = 0.5; | ||||
|                 $base_flow      = $self->first_layer_flow; | ||||
|                  | ||||
|                 # use the proper spacing for first layer as we don't need to align | ||||
|                 # its pattern to the other layers | ||||
|                 $filler->spacing($base_flow->spacing); | ||||
|             } else { | ||||
|                 # draw a perimeter all around support infill | ||||
|                 # TODO: use brim ordering algorithm | ||||
|  | @ -753,10 +762,6 @@ sub generate_toolpaths { | |||
|                 $to_infill = offset_ex([ map @$_, @$to_infill ], -$_flow->scaled_spacing); | ||||
|             } | ||||
|              | ||||
|             # We don't use $base_flow->spacing because we need a constant spacing | ||||
|             # value that guarantees that all layers are correctly aligned. | ||||
|             $filler->spacing($flow->spacing); | ||||
|              | ||||
|             my $mm3_per_mm = $base_flow->mm3_per_mm; | ||||
|             foreach my $expolygon (@$to_infill) { | ||||
|                 my @p = $filler->fill_surface( | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Alessandro Ranellucci
						Alessandro Ranellucci