diff --git a/lib/Slic3r/Format/AMF.pm b/lib/Slic3r/Format/AMF.pm index 9426fcb6d..9aa0ca7c1 100644 --- a/lib/Slic3r/Format/AMF.pm +++ b/lib/Slic3r/Format/AMF.pm @@ -61,8 +61,13 @@ sub write_file { if ($object->name) { printf $fh qq{ %s\n}, $object->name; } + my $layer_height_profile = $object->layer_height_profile(); + my $layer_height_profile_pts = int(@{$layer_height_profile}); + if ($layer_height_profile_pts >= 4 && $layer_height_profile_pts % 2 == 0) { + # Store the layer height profile as a single semicolon separated list. + print $fh ' ', join(';', @{$layer_height_profile}), "\n"; + } #FIXME Store the layer height ranges (ModelObject::layer_height_ranges) - #FIXME Store the layer height profile. printf $fh qq{ \n}; printf $fh qq{ \n}; diff --git a/lib/Slic3r/Format/AMF/Parser.pm b/lib/Slic3r/Format/AMF/Parser.pm index ade2c44ac..0ea3affb0 100644 --- a/lib/Slic3r/Format/AMF/Parser.pm +++ b/lib/Slic3r/Format/AMF/Parser.pm @@ -98,7 +98,10 @@ sub end_element { $self->{_material} = undef; } elsif ($data->{LocalName} eq 'metadata') { my $value = $self->{_metadata_value}; - if ($self->{_metadata_type} =~ /^slic3r\.(.+)/) { + if ($self->{_metadata_type} eq 'slic3r.layer_height_profile') { + my @layer_height_profile = split(';', $value); + $self->{_object}->set_layer_height_profile(\@layer_height_profile) if ($self->{_object}); + } elsif ($self->{_metadata_type} =~ /^slic3r\.(.+)/) { my $opt_key = $1; if (exists $Slic3r::Config::Options->{$opt_key}) { my $config; @@ -114,10 +117,9 @@ sub end_element { } elsif ($opt_key eq 'modifier' && $self->{_volume}) { $self->{_volume}->set_modifier($value); } - } elsif ($self->{_metadata_type} eq 'name' && $self->{_volume}) { - $self->{_volume}->set_name($value); - } elsif ($self->{_metadata_type} eq 'name' && $self->{_object}) { - $self->{_object}->set_name($value); + } elsif ($self->{_metadata_type} eq 'name') { + my $obj = $self->{_volume} // $self->{_object}; + $obj->set_name($value) if $obj; } elsif ($self->{_material}) { $self->{_material}->set_attribute($self->{_metadata_type}, $value); } diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm index 9cd7cc1a3..ff90a9480 100644 --- a/lib/Slic3r/GUI/3DScene.pm +++ b/lib/Slic3r/GUI/3DScene.pm @@ -43,6 +43,7 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init on_double_click on_right_click on_move + on_model_update volumes _sphi _stheta cutting_plane_z @@ -94,6 +95,9 @@ use constant MANIPULATION_LAYER_HEIGHT => 2; use constant GIMBALL_LOCK_THETA_MAX => 170; +use constant VARIABLE_LAYER_THICKNESS_BAR_WIDTH => 70; +use constant VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT => 22; + # make OpenGL::Array thread-safe { no warnings 'redefine'; @@ -171,7 +175,7 @@ sub new { $self->{layer_height_edit_timer} = Wx::Timer->new($self, $self->{layer_height_edit_timer_id}); EVT_TIMER($self, $self->{layer_height_edit_timer_id}, sub { my ($self, $event) = @_; - return if ! $self->_layer_height_edited; + return if $self->_layer_height_edited != 1; return if $self->{layer_height_edit_last_object_id} == -1; $self->_variable_layer_thickness_action(undef); }); @@ -197,25 +201,60 @@ sub _first_selected_object_id { } # Returns an array with (left, top, right, bottom) of the variable layer thickness bar on the screen. -sub _variable_layer_thickness_bar_rect { +sub _variable_layer_thickness_bar_rect_screen { my ($self) = @_; my ($cw, $ch) = $self->GetSizeWH; - my $bar_width = 70; - return ($cw - $bar_width, 0, $cw, $ch); + return ($cw - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, 0, $cw, $ch - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT); +} + +sub _variable_layer_thickness_bar_rect_viewport { + my ($self) = @_; + my ($cw, $ch) = $self->GetSizeWH; + return ((0.5*$cw-VARIABLE_LAYER_THICKNESS_BAR_WIDTH)/$self->_zoom, (-0.5*$ch+VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT)/$self->_zoom, $cw/(2*$self->_zoom), $ch/(2*$self->_zoom)); +} + +# Returns an array with (left, top, right, bottom) of the variable layer thickness bar on the screen. +sub _variable_layer_thickness_reset_rect_screen { + my ($self) = @_; + my ($cw, $ch) = $self->GetSizeWH; + return ($cw - VARIABLE_LAYER_THICKNESS_BAR_WIDTH, $ch - VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT, $cw, $ch); +} + +sub _variable_layer_thickness_reset_rect_viewport { + my ($self) = @_; + my ($cw, $ch) = $self->GetSizeWH; + return ((0.5*$cw-VARIABLE_LAYER_THICKNESS_BAR_WIDTH)/$self->_zoom, -$ch/(2*$self->_zoom), $cw/(2*$self->_zoom), (-0.5*$ch+VARIABLE_LAYER_THICKNESS_RESET_BUTTON_HEIGHT)/$self->_zoom); } sub _variable_layer_thickness_bar_rect_mouse_inside { my ($self, $mouse_evt) = @_; - my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect; + my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen; + return $mouse_evt->GetX >= $bar_left && $mouse_evt->GetX <= $bar_right && $mouse_evt->GetY >= $bar_top && $mouse_evt->GetY <= $bar_bottom; +} + +sub _variable_layer_thickness_reset_rect_mouse_inside { + my ($self, $mouse_evt) = @_; + my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_reset_rect_screen; return $mouse_evt->GetX >= $bar_left && $mouse_evt->GetX <= $bar_right && $mouse_evt->GetY >= $bar_top && $mouse_evt->GetY <= $bar_bottom; } sub _variable_layer_thickness_bar_mouse_cursor_z { my ($self, $object_idx, $mouse_evt) = @_; - my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect; + my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen; return unscale($self->{print}->get_object($object_idx)->size->z) * ($bar_bottom - $mouse_evt->GetY - 1.) / ($bar_bottom - $bar_top); } +sub _variable_layer_thickness_bar_mouse_cursor_z_relative { + my ($self) = @_; + my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition()); + my ($bar_left, $bar_top, $bar_right, $bar_bottom) = $self->_variable_layer_thickness_bar_rect_screen; + return ($mouse_pos->x >= $bar_left && $mouse_pos->x <= $bar_right && $mouse_pos->y >= $bar_top && $mouse_pos->y <= $bar_bottom) ? + # Inside the bar. + ($bar_bottom - $mouse_pos->y - 1.) / ($bar_bottom - $bar_top - 1) : + # Outside the bar. + -1000.; +} + sub _variable_layer_thickness_action { my ($self, $mouse_event, $do_modification) = @_; # A volume is selected. Test, whether hovering over a layer thickness bar. @@ -224,6 +263,8 @@ sub _variable_layer_thickness_action { $self->{layer_height_edit_last_action} = $mouse_event->ShiftDown ? ($mouse_event->RightIsDown ? 3 : 2) : ($mouse_event->RightIsDown ? 0 : 1); } if ($self->{layer_height_edit_last_object_id} != -1) { + # Mark the volume as modified, so Print will pick its layer height profile? Where to mark it? + # Start a timer to refresh the print? schedule_background_process() ? $self->{print}->get_object($self->{layer_height_edit_last_object_id})->adjust_layer_height_profile( $self->{layer_height_edit_last_z}, $self->{layer_height_edit_strength}, @@ -263,6 +304,11 @@ sub mouse_event { # Start editing the layer height. $self->_layer_height_edited(1); $self->_variable_layer_thickness_action($e); + } elsif ($object_idx_selected != -1 && $self->_variable_layer_thickness_reset_rect_mouse_inside($e)) { + $self->{print}->get_object($self->{layer_height_edit_last_object_id})->reset_layer_height_profile; + # Index 2 means no editing, just wait for mouse up event. + $self->_layer_height_edited(2); + $self->Refresh; } else { # Select volume in this 3D canvas. # Don't deselect a volume if layer editing is enabled. We want the object to stay selected @@ -325,7 +371,7 @@ sub mouse_event { $self->Refresh; } elsif ($e->Dragging) { if ($self->_layer_height_edited && $object_idx_selected != -1) { - $self->_variable_layer_thickness_action($e); + $self->_variable_layer_thickness_action($e) if ($self->_layer_height_edited == 1); } elsif ($e->LeftIsDown) { # if dragging over blank area with left button, rotate if (defined $self->_drag_start_pos) { @@ -365,7 +411,12 @@ sub mouse_event { $self->_drag_start_xy($pos); } } elsif ($e->LeftUp || $e->MiddleUp || $e->RightUp) { - if ($self->on_move && defined($self->_drag_volume_idx) && $self->_dragged) { + if ($self->_layer_height_edited) { + $self->_layer_height_edited(undef); + $self->{layer_height_edit_timer}->Stop; + $self->on_model_update->() + if ($object_idx_selected != -1 && $self->on_model_update); + } elsif ($self->on_move && defined($self->_drag_volume_idx) && $self->_dragged) { # get all volumes belonging to the same group, if any my @volume_idxs; my $group_id = $self->volumes->[$self->_drag_volume_idx]->drag_group_id; @@ -381,8 +432,6 @@ sub mouse_event { $self->_drag_start_pos(undef); $self->_drag_start_xy(undef); $self->_dragged(undef); - $self->_layer_height_edited(undef); - $self->{layer_height_edit_timer}->Stop; } elsif ($e->Moving) { $self->_mouse_pos($pos); # Only refresh if picking is enabled, in that case the objects may get highlighted if the mouse cursor @@ -432,7 +481,7 @@ sub mouse_wheel_event { 0, ) if 0; $self->on_viewport_changed->() if $self->on_viewport_changed; - $self->_dirty(1); + $self->Resize($self->GetSizeWH) if $self->IsShownOnScreen; $self->Refresh; } @@ -1111,29 +1160,21 @@ sub draw_volumes { glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); - # The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth), - # where x, y is the window size divided by $self->_zoom. - my ($cw, $ch) = $self->GetSizeWH; - my $bar_width = 70; - my ($bar_left, $bar_right) = ((0.5 * $cw - $bar_width)/$self->_zoom, $cw/(2*$self->_zoom)); - my ($bar_bottom, $bar_top) = (-$ch/(2*$self->_zoom), $ch/(2*$self->_zoom)); - my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition()); - my $z_cursor_relative = ($mouse_pos->x < $cw - $bar_width) ? -1000. : - ($ch - $mouse_pos->y - 1.) / ($ch - 1); - + my $z_cursor_relative = $self->_variable_layer_thickness_bar_mouse_cursor_z_relative; foreach my $volume_idx (0..$#{$self->volumes}) { my $volume = $self->volumes->[$volume_idx]; my $shader_active = 0; if ($self->layer_editing_enabled && ! $fakecolor && $volume->selected && $self->{shader} && $volume->{layer_height_texture_data}) { my $print_object = $self->{print}->get_object(int($volume->select_group_id / 1000000)); - if (! defined($volume->{layer_height_texture_cells}) || $print_object->update_layer_height_profile) { - # Layer height profile was invalid before, now filled in with default data from layer height configuration - # and possibly from the layer height modifiers. Update the height texture. - $volume->{layer_height_texture_cells} = $print_object->generate_layer_height_texture( + { + # Update the height texture if the ModelObject::layer_height_texture is invalid. + my $ncells = $print_object->generate_layer_height_texture( $volume->{layer_height_texture_data}->ptr, $self->{layer_preview_z_texture_height}, - $self->{layer_preview_z_texture_width}); + $self->{layer_preview_z_texture_width}, + !defined($volume->{layer_height_texture_cells})); + $volume->{layer_height_texture_cells} = $ncells if $ncells > 0; } $self->{shader}->Enable; my $z_to_texture_row_id = $self->{shader}->Map('z_to_texture_row'); @@ -1272,45 +1313,77 @@ sub draw_volumes { glDisableClientState(GL_VERTEX_ARRAY); } +sub _load_image_set_texture { + my ($self, $file_name) = @_; + # Load a PNG with an alpha channel. + my $img = Wx::Image->new; + $img->LoadFile($Slic3r::var->($file_name), wxBITMAP_TYPE_PNG); + # Get RGB & alpha raw data from wxImage, interleave them into a Perl array. + my @rgb = unpack 'C*', $img->GetData(); + my @alpha = $img->HasAlpha ? unpack 'C*', $img->GetAlpha() : (255) x (int(@rgb) / 3); +# my @alpha = unpack 'C*', $img->GetAlpha(); + my $n_pixels = int(@alpha); + my @data = (0)x($n_pixels * 4); + for (my $i = 0; $i < $n_pixels; $i += 1) { + $data[$i*4 ] = $rgb[$i*3]; + $data[$i*4+1] = $rgb[$i*3+1]; + $data[$i*4+2] = $rgb[$i*3+2]; + $data[$i*4+3] = $alpha[$i]; + } + # Initialize a raw bitmap data. + my $params = { + loaded => 1, + valid => $n_pixels > 0, + width => $img->GetWidth, + height => $img->GetHeight, + data => OpenGL::Array->new_list(GL_UNSIGNED_BYTE, @data), + texture_id => glGenTextures_p(1) + }; + # Create and initialize a texture with the raw data. + glBindTexture(GL_TEXTURE_2D, $params->{texture_id}); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); + glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $params->{width}, $params->{height}, 0, GL_RGBA, GL_UNSIGNED_BYTE, $params->{data}->ptr); + glBindTexture(GL_TEXTURE_2D, 0); + return $params; +} + sub _variable_layer_thickness_load_overlay_image { my ($self) = @_; - - if (! $self->{layer_preview_annotation}->{loaded}) { - # Load a PNG with an alpha channel. - my $img = Wx::Image->new; - $img->LoadFile($Slic3r::var->("variable_layer_height_tooltip.png"), wxBITMAP_TYPE_PNG); - # Get RGB & alpha raw data from wxImage, interleave them into a Perl array. - my @rgb = unpack 'C*', $img->GetData(); - my @alpha = unpack 'C*', $img->GetAlpha(); - my $n_pixels = int(@alpha); - my @data = (0)x($n_pixels * 4); - for (my $i = 0; $i < $n_pixels; $i += 1) { - $data[$i*4 ] = $rgb[$i*3]; - $data[$i*4+1] = $rgb[$i*3+1]; - $data[$i*4+2] = $rgb[$i*3+2]; - $data[$i*4+3] = $alpha[$i]; - } - # Initialize a raw bitmap data. - my $params = $self->{layer_preview_annotation} = { - loaded => 1, - valid => $n_pixels > 0, - width => $img->GetWidth, - height => $img->GetHeight, - data => OpenGL::Array->new_list(GL_UNSIGNED_BYTE, @data), - texture_id => glGenTextures_p(1) - }; - # Create and initialize a texture with the raw data. - glBindTexture(GL_TEXTURE_2D, $params->{texture_id}); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); - glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $params->{width}, $params->{height}, 0, GL_RGBA, GL_UNSIGNED_BYTE, $params->{data}->ptr); - glBindTexture(GL_TEXTURE_2D, 0); - } - + $self->{layer_preview_annotation} = $self->_load_image_set_texture('variable_layer_height_tooltip.png') + if (! $self->{layer_preview_annotation}->{loaded}); return $self->{layer_preview_annotation}->{valid}; } +sub _variable_layer_thickness_load_reset_image { + my ($self) = @_; + $self->{layer_preview_reset_image} = $self->_load_image_set_texture('variable_layer_height_reset.png') + if (! $self->{layer_preview_reset_image}->{loaded}); + return $self->{layer_preview_reset_image}->{valid}; +} + +# Paint the tooltip. +sub _render_image { + my ($self, $image, $l, $r, $b, $t) = @_; + glColor4f(1.,1.,1.,1.); + glDisable(GL_LIGHTING); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, $image->{texture_id}); + glBegin(GL_QUADS); + glTexCoord2d(0.,1.); glVertex3f($l, $b, 0); + glTexCoord2d(1.,1.); glVertex3f($r, $b, 0); + glTexCoord2d(1.,0.); glVertex3f($r, $t, 0); + glTexCoord2d(0.,0.); glVertex3f($l, $t, 0); + glEnd(); + glBindTexture(GL_TEXTURE_2D, 0); + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + glEnable(GL_LIGHTING); +} + sub draw_active_object_annotations { # $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking. my ($self) = @_; @@ -1329,13 +1402,9 @@ sub draw_active_object_annotations { # The viewport and camera are set to complete view and glOrtho(-$x/2, $x/2, -$y/2, $y/2, -$depth, $depth), # where x, y is the window size divided by $self->_zoom. - my ($cw, $ch) = $self->GetSizeWH; - my $bar_width = 70; - my ($bar_left, $bar_right) = ((0.5 * $cw - $bar_width)/$self->_zoom, $cw/(2*$self->_zoom)); - my ($bar_bottom, $bar_top) = (-$ch/(2*$self->_zoom), $ch/(2*$self->_zoom)); - my $mouse_pos = $self->ScreenToClientPoint(Wx::GetMousePosition()); - my $z_cursor_relative = ($mouse_pos->x < $cw - $bar_width) ? -1000. : - ($ch - $mouse_pos->y - 1.) / ($ch - 1); + my ($bar_left, $bar_bottom, $bar_right, $bar_top) = $self->_variable_layer_thickness_bar_rect_viewport; + my ($reset_left, $reset_bottom, $reset_right, $reset_top) = $self->_variable_layer_thickness_reset_rect_viewport; + my $z_cursor_relative = $self->_variable_layer_thickness_bar_mouse_cursor_z_relative; $self->{shader}->Enable; my $z_to_texture_row_id = $self->{shader}->Map('z_to_texture_row'); @@ -1374,31 +1443,23 @@ sub draw_active_object_annotations { # Paint the tooltip. if ($self->_variable_layer_thickness_load_overlay_image) { - glColor4f(1.,1.,1.,1.); - glDisable(GL_LIGHTING); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_annotation}->{texture_id}); - glBegin(GL_QUADS); my $gap = 10/$self->_zoom; - my ($l, $r, $t, $b) = ($bar_left - $self->{layer_preview_annotation}->{width}/$self->_zoom - $gap, $bar_left - $gap, $bar_bottom + $self->{layer_preview_annotation}->{height}/$self->_zoom + $gap, $bar_bottom + $gap); - glTexCoord2d(0.,1.); glVertex3f($l, $b, 0); - glTexCoord2d(1.,1.); glVertex3f($r, $b, 0); - glTexCoord2d(1.,0.); glVertex3f($r, $t, 0); - glTexCoord2d(0.,0.); glVertex3f($l, $t, 0); - glEnd(); - glBindTexture(GL_TEXTURE_2D, 0); - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); - glEnable(GL_LIGHTING); + my ($l, $r, $b, $t) = ($bar_left - $self->{layer_preview_annotation}->{width}/$self->_zoom - $gap, $bar_left - $gap, $reset_bottom + $self->{layer_preview_annotation}->{height}/$self->_zoom + $gap, $reset_bottom + $gap); + $self->_render_image($self->{layer_preview_annotation}, $l, $r, $t, $b); + } + + # Paint the reset button. + if ($self->_variable_layer_thickness_load_reset_image) { + $self->_render_image($self->{layer_preview_reset_image}, $reset_left, $reset_right, $reset_bottom, $reset_top); } # Paint the graph. + #FIXME use the min / maximum layer height + #FIXME show some kind of legend. my $object_idx = int($volume->select_group_id / 1000000); my $print_object = $self->{print}->get_object($object_idx); my $max_z = unscale($print_object->size->z); - my $profile = $print_object->layer_height_profile; + my $profile = $print_object->model_object->layer_height_profile; my $layer_height = $print_object->config->get('layer_height'); # Baseline glColor3f(0., 0., 0.); @@ -1686,7 +1747,6 @@ sub load_object { # not correspond to the color of the filament. my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; $color->[3] = $volume->modifier ? 0.5 : 1; - print "Reloading object $volume_idx, $instance_idx\n"; push @{$self->volumes}, my $v = Slic3r::GUI::3DScene::Volume->new( bounding_box => $mesh->bounding_box, color => $color, diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 3f4c56cd4..972102d55 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -104,6 +104,12 @@ 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}->set_on_model_update(sub { + if ($Slic3r::GUI::Settings->{_}{background_processing}) { + $self->{apply_config_timer}->Stop if defined $self->{apply_config_timer}; + $self->async_apply_config(); + } + }); $self->{canvas3D}->on_viewport_changed(sub { $self->{preview3D}->canvas->set_viewport_from_scene($self->{canvas3D}); }); @@ -651,10 +657,9 @@ sub load_model_objects { my @obj_idx = (); foreach my $model_object (@model_objects) { my $o = $self->{model}->add_object($model_object); - - push @{ $self->{objects} }, Slic3r::GUI::Plater::Object->new( - name => basename($model_object->input_file), - ); + my $object_name = $model_object->name; + $object_name = basename($model_object->input_file) if ($object_name eq ''); + push @{ $self->{objects} }, Slic3r::GUI::Plater::Object->new(name => $object_name); push @obj_idx, $#{ $self->{objects} }; if ($model_object->instances_count == 0) { @@ -1088,6 +1093,9 @@ sub async_apply_config { # apply new config my $invalidated = $self->{print}->apply_config($self->GetFrame->config); + +# $self->{canvas3D}->Refresh if ($invalidated && $self->{canvas3D}->layer_editing_enabled); + $self->{canvas3D}->Refresh if ($self->{canvas3D}->layer_editing_enabled); return if !$Slic3r::GUI::Settings->{_}{background_processing}; @@ -1200,7 +1208,10 @@ sub reslice { my ($self) = @_; # Don't reslice if export of G-code or sending to OctoPrint is running. if ($Slic3r::have_threads && ! defined($self->{export_gcode_output_file}) && ! defined($self->{send_gcode_file})) { + # Stop the background processing threads, stop the async update timer. $self->stop_background_process; + # Rather perform one additional unnecessary update of the print object instead of skipping a pending async update. + $self->async_apply_config; $self->statusbar->SetCancelCallback(sub { $self->stop_background_process; $self->statusbar->SetStatusText("Slicing cancelled"); @@ -1534,8 +1545,6 @@ sub on_thumbnail_made { sub update { my ($self, $force_autocenter) = @_; - print "Platter - update\n"; - if ($Slic3r::GUI::Settings->{_}{autocenter} || $force_autocenter) { $self->{model}->center_instances_around_point($self->bed_centerf); } diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index ce56c67b4..2e108ff46 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -83,6 +83,11 @@ sub set_on_instances_moved { $self->{on_instances_moved} = $cb; } +sub set_on_model_update { + my ($self, $cb) = @_; + $self->on_model_update($cb); +} + sub update { my ($self) = @_; diff --git a/xs/src/libslic3r/Model.cpp b/xs/src/libslic3r/Model.cpp index e4a4c48a8..92b667a5f 100644 --- a/xs/src/libslic3r/Model.cpp +++ b/xs/src/libslic3r/Model.cpp @@ -329,8 +329,10 @@ ModelMaterial::apply(const t_model_material_attributes &attributes) } -ModelObject::ModelObject(Model *model) - : model(model), _bounding_box_valid(false) +ModelObject::ModelObject(Model *model) : + model(model), + _bounding_box_valid(false), + layer_height_profile_valid(false) {} ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volumes) @@ -340,6 +342,8 @@ ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volum volumes(), config(other.config), layer_height_ranges(other.layer_height_ranges), + layer_height_profile(other.layer_height_profile), + layer_height_profile_valid(other.layer_height_profile_valid), origin_translation(other.origin_translation), _bounding_box(other._bounding_box), _bounding_box_valid(other._bounding_box_valid), @@ -356,7 +360,7 @@ ModelObject::ModelObject(Model *model, const ModelObject &other, bool copy_volum this->add_instance(**i); } -ModelObject& ModelObject::operator= (ModelObject other) +ModelObject& ModelObject::operator=(ModelObject other) { this->swap(other); return *this; @@ -370,6 +374,8 @@ ModelObject::swap(ModelObject &other) std::swap(this->volumes, other.volumes); std::swap(this->config, other.config); std::swap(this->layer_height_ranges, other.layer_height_ranges); + std::swap(this->layer_height_profile, other.layer_height_profile); + std::swap(this->layer_height_profile_valid, other.layer_height_profile_valid); std::swap(this->origin_translation, other.origin_translation); std::swap(this->_bounding_box, other._bounding_box); std::swap(this->_bounding_box_valid, other._bounding_box_valid); diff --git a/xs/src/libslic3r/Model.hpp b/xs/src/libslic3r/Model.hpp index e6e98bc01..5e1e29cd5 100644 --- a/xs/src/libslic3r/Model.hpp +++ b/xs/src/libslic3r/Model.hpp @@ -117,6 +117,10 @@ public: // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers. // The pairs of are packed into a 1D array to simplify handling by the Perl XS. std::vector layer_height_profile; + // layer_height_profile is initialized when the layer editing mode is entered. + // Only if the user really modified the layer height, layer_height_profile_valid is set + // and used subsequently by the PrintObject. + bool layer_height_profile_valid; /* This vector accumulates the total translation applied to the object by the center_around_origin() method. Callers might want to apply the same translation diff --git a/xs/src/libslic3r/Print.cpp b/xs/src/libslic3r/Print.cpp index 539562a16..132eb560d 100644 --- a/xs/src/libslic3r/Print.cpp +++ b/xs/src/libslic3r/Print.cpp @@ -371,7 +371,7 @@ Print::add_model_object(ModelObject* model_object, int idx) this->objects[idx] = o = new PrintObject(this, model_object, bb); } else { o = new PrintObject(this, model_object, bb); - objects.push_back(o); + this->objects.push_back(o); // invalidate steps this->invalidate_step(psSkirt); @@ -528,8 +528,26 @@ Print::apply_config(DynamicPrintConfig config) this->clear_objects(); for (ModelObjectPtrs::iterator it = model_objects.begin(); it != model_objects.end(); ++it) { this->add_model_object(*it); + // Update layer_height_profile from the main thread as it may pull the data from the associated ModelObject. + this->objects.back()->update_layer_height_profile(); } invalidated = true; + } else { + // Check validity of the layer height profiles. + FOREACH_OBJECT(this, o) { + if (! (*o)->layer_height_profile_valid) { + // The layer_height_profile is not valid for some reason (updated by the user or invalidated due to some option change). + // Start slicing of this object from scratch. + (*o)->invalidate_all_steps(); + // Following line sets the layer_height_profile_valid flag. + (*o)->update_layer_height_profile(); + invalidated = true; + } else if (! step_done(posSlice)) { + // Update layer_height_profile from the main thread as it may pull the data from the associated ModelObject. + // Only update if the slicing was not finished yet. + (*o)->update_layer_height_profile(); + } + } } return invalidated; diff --git a/xs/src/libslic3r/Print.hpp b/xs/src/libslic3r/Print.hpp index 02b2ea363..30d49b236 100644 --- a/xs/src/libslic3r/Print.hpp +++ b/xs/src/libslic3r/Print.hpp @@ -85,7 +85,13 @@ public: // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers. // The pairs of are packed into a 1D array to simplify handling by the Perl XS. + // layer_height_profile must not be set by the background thread. std::vector layer_height_profile; + // There is a layer_height_profile at both PrintObject and ModelObject. The layer_height_profile at the ModelObject + // is used for interactive editing and for loading / storing into a project file (AMF file as of today). + // This flag indicates that the layer_height_profile at the UI has been updated, therefore the backend needs to get it. + // This flag is necessary as we cannot safely clear the layer_height_profile if the background calculation is running. + bool layer_height_profile_valid; // this is set to true when LayerRegion->slices is split in top/internal/bottom // so that next call to make_perimeters() performs a union() before computing loops @@ -145,10 +151,16 @@ public: bool invalidate_step(PrintObjectStep step); bool invalidate_all_steps(); + // To be used over the layer_height_profile of both the PrintObject and ModelObject + // to initialize the height profile with the height ranges. + bool update_layer_height_profile(std::vector &layer_height_profile) const; + // Process layer_height_ranges, the raft layers and first layer thickness into layer_height_profile. // The layer_height_profile may be later modified interactively by the user to refine layers at sloping surfaces. bool update_layer_height_profile(); + void reset_layer_height_profile(); + // Collect the slicing parameters, to be used by variable layer thickness algorithm, // by the interactive layer height editor and by the printing process itself. // The slicing parameters are dependent on various configuration values diff --git a/xs/src/libslic3r/PrintObject.cpp b/xs/src/libslic3r/PrintObject.cpp index 8091db973..0f426005d 100644 --- a/xs/src/libslic3r/PrintObject.cpp +++ b/xs/src/libslic3r/PrintObject.cpp @@ -29,7 +29,8 @@ namespace Slic3r { PrintObject::PrintObject(Print* print, ModelObject* model_object, const BoundingBoxf3 &modobj_bbox) : typed_slices(false), _print(print), - _model_object(model_object) + _model_object(model_object), + layer_height_profile_valid(false) { // Compute the translation to be applied to our meshes so that we work with smaller coordinates { @@ -216,8 +217,11 @@ PrintObject::invalidate_state_by_config_options(const std::vectorreset_layer_height_profile(); + } + else if (*opt_key == "xy_size_compensation") { steps.insert(posSlice); } else if (*opt_key == "support_material" || *opt_key == "support_material_angle" @@ -283,7 +287,8 @@ PrintObject::invalidate_state_by_config_options(const std::vectorinvalidate_all_steps(); + this->reset_layer_height_profile(); + return this->invalidate_all_steps(); } } @@ -950,21 +955,49 @@ SlicingParameters PrintObject::slicing_parameters() const unscale(this->size.z), this->print()->object_extruders()); } -bool PrintObject::update_layer_height_profile() +bool PrintObject::update_layer_height_profile(std::vector &layer_height_profile) const { bool updated = false; - if (this->layer_height_profile.empty()) { + + // If the layer height profile is not set, try to use the one stored at the ModelObject. + if (layer_height_profile.empty() && layer_height_profile.data() != this->model_object()->layer_height_profile.data()) { + layer_height_profile = this->model_object()->layer_height_profile; + updated = true; + } + + // Verify the layer_height_profile. + SlicingParameters slicing_params = this->slicing_parameters(); + if (! layer_height_profile.empty() && + // Must not be of even length. + ((layer_height_profile.size() & 1) != 0 || + // Last entry must be at the top of the object. + std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_height()) > 1e-3)) + layer_height_profile.clear(); + + if (layer_height_profile.empty()) { if (0) // if (this->layer_height_profile.empty()) - this->layer_height_profile = layer_height_profile_adaptive(this->slicing_parameters(), this->layer_height_ranges, + layer_height_profile = layer_height_profile_adaptive(slicing_params, this->layer_height_ranges, this->model_object()->volumes); else - this->layer_height_profile = layer_height_profile_from_ranges(this->slicing_parameters(), this->layer_height_ranges); + layer_height_profile = layer_height_profile_from_ranges(slicing_params, this->layer_height_ranges); updated = true; } return updated; } +// This must be called from the main thread as it modifies the layer_height_profile. +bool PrintObject::update_layer_height_profile() +{ + // If the layer height profile has been marked as invalid for some reason (modified at the UI level + // or invalidated due to the slicing parameters), clear it now. + if (! this->layer_height_profile_valid) { + this->layer_height_profile.clear(); + this->layer_height_profile_valid = true; + } + return this->update_layer_height_profile(this->layer_height_profile); +} + // 1) Decides Z positions of the layers, // 2) Initializes layers and their regions // 3) Slices the object meshes @@ -1267,4 +1300,13 @@ void PrintObject::_generate_support_material() support_material.generate(*this); } +void PrintObject::reset_layer_height_profile() +{ + // Reset the layer_heigth_profile. + this->layer_height_profile.clear(); + // Reset the source layer_height_profile if it exists at the ModelObject. + this->model_object()->layer_height_profile.clear(); + this->model_object()->layer_height_profile_valid = false; +} + } // namespace Slic3r diff --git a/xs/src/libslic3r/Slicing.cpp b/xs/src/libslic3r/Slicing.cpp index 2a070438b..6fb487ace 100644 --- a/xs/src/libslic3r/Slicing.cpp +++ b/xs/src/libslic3r/Slicing.cpp @@ -18,6 +18,26 @@ namespace Slic3r { +static const coordf_t MIN_LAYER_HEIGHT = 0.01; +static const coordf_t MIN_LAYER_HEIGHT_DEFAULT = 0.05; + +// Minimum layer height for the variable layer height algorithm. +inline coordf_t min_layer_height_from_nozzle(const PrintConfig &print_config, int idx_nozzle) +{ + coordf_t min_layer_height = print_config.min_layer_height.get_at(idx_nozzle - 1); + return (min_layer_height == 0.) ? MIN_LAYER_HEIGHT_DEFAULT : std::max(MIN_LAYER_HEIGHT, min_layer_height); +} + +// Maximum layer height for the variable layer height algorithm, 3/4 of a nozzle dimaeter by default, +// it should not be smaller than the minimum layer height. +inline coordf_t max_layer_height_from_nozzle(const PrintConfig &print_config, int idx_nozzle) +{ + coordf_t min_layer_height = min_layer_height_from_nozzle(print_config, idx_nozzle); + coordf_t max_layer_height = print_config.max_layer_height.get_at(idx_nozzle - 1); + coordf_t nozzle_dmr = print_config.nozzle_diameter.get_at(idx_nozzle - 1); + return std::max(min_layer_height, (max_layer_height == 0.) ? (0.75 * nozzle_dmr) : max_layer_height); +} + SlicingParameters SlicingParameters::create_from_config( const PrintConfig &print_config, const PrintObjectConfig &object_config, @@ -45,6 +65,31 @@ SlicingParameters SlicingParameters::create_from_config( params.base_raft_layers = object_config.raft_layers.value; params.soluble_interface = soluble_interface; + // Miniumum/maximum of the minimum layer height over all extruders. + params.min_layer_height = MIN_LAYER_HEIGHT; + params.max_layer_height = FLT_MAX; + if (object_config.support_material.value || params.base_raft_layers > 0) { + // Has some form of support. Add the support layers to the minimum / maximum layer height limits. + params.min_layer_height = std::max( + min_layer_height_from_nozzle(print_config, object_config.support_material_extruder), + min_layer_height_from_nozzle(print_config, object_config.support_material_interface_extruder)); + params.max_layer_height = std::min( + max_layer_height_from_nozzle(print_config, object_config.support_material_extruder), + max_layer_height_from_nozzle(print_config, object_config.support_material_interface_extruder)); + params.max_suport_layer_height = params.max_layer_height; + } + if (object_extruders.empty()) { + params.min_layer_height = std::max(params.min_layer_height, min_layer_height_from_nozzle(print_config, 0)); + params.max_layer_height = std::min(params.max_layer_height, max_layer_height_from_nozzle(print_config, 0)); + } else { + for (std::set::const_iterator it_extruder = object_extruders.begin(); it_extruder != object_extruders.end(); ++ it_extruder) { + params.min_layer_height = std::max(params.min_layer_height, min_layer_height_from_nozzle(print_config, *it_extruder)); + params.max_layer_height = std::min(params.max_layer_height, max_layer_height_from_nozzle(print_config, *it_extruder)); + } + } + params.min_layer_height = std::min(params.min_layer_height, params.layer_height); + params.max_layer_height = std::max(params.max_layer_height, params.layer_height); + if (! soluble_interface) { params.gap_raft_object = object_config.support_material_contact_distance.value; params.gap_object_support = object_config.support_material_contact_distance.value; @@ -101,12 +146,6 @@ SlicingParameters SlicingParameters::create_from_config( params.object_print_z_max += print_z; } - params.min_layer_height = std::min(params.layer_height, first_layer_height); - params.max_layer_height = std::max(params.layer_height, first_layer_height); - - //FIXME add it to the print configuration - params.min_layer_height = 0.05; - // Calculate the maximum layer height as 0.75 from the minimum nozzle diameter. if (! object_extruders.empty()) { coordf_t min_object_extruder_dmr = 1000000.; @@ -306,6 +345,7 @@ void adjust_layer_height_profile( return; assert(layer_height_profile.size() >= 2); + assert(std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_height()) < EPSILON); // 1) Get the current layer thickness at z. coordf_t current_layer_height = slicing_params.layer_height; @@ -355,21 +395,21 @@ void adjust_layer_height_profile( coordf_t lo = std::max(z_span_variable.first, z - 0.5 * band_width); coordf_t hi = std::min(z_span_variable.second, z + 0.5 * band_width); coordf_t z_step = 0.1; - size_t i = 0; - while (i < layer_height_profile.size() && layer_height_profile[i] < lo) - i += 2; - i -= 2; + size_t idx = 0; + while (idx < layer_height_profile.size() && layer_height_profile[idx] < lo) + idx += 2; + idx -= 2; std::vector profile_new; profile_new.reserve(layer_height_profile.size()); - assert(i >= 0 && i + 1 < layer_height_profile.size()); - profile_new.insert(profile_new.end(), layer_height_profile.begin(), layer_height_profile.begin() + i + 2); + assert(idx >= 0 && idx + 1 < layer_height_profile.size()); + profile_new.insert(profile_new.end(), layer_height_profile.begin(), layer_height_profile.begin() + idx + 2); coordf_t zz = lo; size_t i_resampled_start = profile_new.size(); while (zz < hi) { - size_t next = i + 2; - coordf_t z1 = layer_height_profile[i]; - coordf_t h1 = layer_height_profile[i + 1]; + size_t next = idx + 2; + coordf_t z1 = layer_height_profile[idx]; + coordf_t h1 = layer_height_profile[idx + 1]; coordf_t height = h1; if (next < layer_height_profile.size()) { coordf_t z2 = layer_height_profile[next]; @@ -409,22 +449,23 @@ void adjust_layer_height_profile( profile_new.push_back(clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, height)); } zz += z_step; - i = next; - while (i < layer_height_profile.size() && layer_height_profile[i] < zz) - i += 2; - i -= 2; + idx = next; + while (idx < layer_height_profile.size() && layer_height_profile[idx] < zz) + idx += 2; + idx -= 2; } - i += 2; - assert(i > 0); + idx += 2; + assert(idx > 0); size_t i_resampled_end = profile_new.size(); - if (i < layer_height_profile.size()) { - assert(zz >= layer_height_profile[i - 2]); - assert(zz <= layer_height_profile[i]); -// profile_new.push_back(zz); -// profile_new.push_back(layer_height_profile[i + 1]); - profile_new.insert(profile_new.end(), layer_height_profile.begin() + i, layer_height_profile.end()); - } + if (idx < layer_height_profile.size()) { + assert(zz >= layer_height_profile[idx - 2]); + assert(zz <= layer_height_profile[idx]); + profile_new.insert(profile_new.end(), layer_height_profile.begin() + idx, layer_height_profile.end()); + } + else if (profile_new[profile_new.size() - 2] + 0.5 * EPSILON < slicing_params.object_print_z_height()) { + profile_new.insert(profile_new.end(), layer_height_profile.end() - 2, layer_height_profile.end()); + } layer_height_profile = std::move(profile_new); if (action == LAYER_HEIGHT_EDIT_ACTION_SMOOTH) { @@ -448,6 +489,7 @@ void adjust_layer_height_profile( assert(layer_height_profile.size() > 2); assert(layer_height_profile.size() % 2 == 0); assert(layer_height_profile[0] == 0.); + assert(std::abs(layer_height_profile[layer_height_profile.size() - 2] - slicing_params.object_print_z_height()) < EPSILON); #ifdef _DEBUG for (size_t i = 2; i < layer_height_profile.size(); i += 2) assert(layer_height_profile[i - 2] <= layer_height_profile[i]); @@ -601,7 +643,7 @@ int generate_layer_height_texture( const Point3 &color2 = palette_raw[idx2]; coordf_t z = cell_to_z1 * coordf_t(cell); - assert(z >= lo && z <= hi); + assert(z >= lo - EPSILON && z <= hi + EPSILON); // Color mapping from layer height to RGB. Pointf3 color( diff --git a/xs/src/libslic3r/Slicing.hpp b/xs/src/libslic3r/Slicing.hpp index 5105b9518..0b48443b6 100644 --- a/xs/src/libslic3r/Slicing.hpp +++ b/xs/src/libslic3r/Slicing.hpp @@ -52,6 +52,11 @@ struct SlicingParameters // The regular layer height, applied for all but the first layer, if not overridden by layer ranges // or by the variable layer thickness table. coordf_t layer_height; + // Minimum / maximum layer height, to be used for the automatic adaptive layer height algorithm, + // or by an interactive layer height editor. + coordf_t min_layer_height; + coordf_t max_layer_height; + coordf_t max_suport_layer_height; // First layer height of the print, this may be used for the first layer of the raft // or for the first layer of the print. @@ -75,11 +80,6 @@ struct SlicingParameters // Gap when placing object over support. coordf_t gap_support_object; - // Minimum / maximum layer height, to be used for the automatic adaptive layer height algorithm, - // or by an interactive layer height editor. - coordf_t min_layer_height; - coordf_t max_layer_height; - // Bottom and top of the printed object. // If printed without a raft, object_print_z_min = 0 and object_print_z_max = object height. // Otherwise object_print_z_min is equal to the raft height. diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 1859779b9..db6773cb3 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -170,6 +170,10 @@ ModelMaterial::attributes() void set_layer_height_ranges(t_layer_height_ranges ranges) %code%{ THIS->layer_height_ranges = ranges; %}; + std::vector layer_height_profile() + %code%{ RETVAL = THIS->layer_height_profile_valid ? THIS->layer_height_profile : std::vector(); %}; + void set_layer_height_profile(std::vector profile) + %code%{ THIS->layer_height_profile = profile; THIS->layer_height_profile_valid = true; %}; Ref origin_translation() %code%{ RETVAL = &THIS->origin_translation; %}; diff --git a/xs/xsp/Print.xsp b/xs/xsp/Print.xsp index 5166aa96b..b70ff9c70 100644 --- a/xs/xsp/Print.xsp +++ b/xs/xsp/Print.xsp @@ -120,23 +120,39 @@ _constant() void _infill(); void _generate_support_material(); - bool update_layer_height_profile(); + std::vector get_layer_height_min_max() + %code%{ + SlicingParameters slicing_params = THIS->slicing_parameters(); + RETVAL.push_back(slicing_params.min_layer_height); + RETVAL.push_back(slicing_params.max_layer_height); + RETVAL.push_back(slicing_params.first_print_layer_height); + RETVAL.push_back(slicing_params.first_object_layer_height); + RETVAL.push_back(slicing_params.layer_height); + %}; void adjust_layer_height_profile(coordf_t z, coordf_t layer_thickness_delta, coordf_t band_width, int action) - %code%{ - THIS->update_layer_height_profile(); + %code%{ + THIS->update_layer_height_profile(THIS->model_object()->layer_height_profile); adjust_layer_height_profile( - THIS->slicing_parameters(), THIS->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action)); + THIS->slicing_parameters(), THIS->model_object()->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action)); + THIS->model_object()->layer_height_profile_valid = true; + THIS->layer_height_profile_valid = false; %}; + + void reset_layer_height_profile(); - int generate_layer_height_texture(void *data, int rows, int cols, bool level_of_detail_2nd_level = true) + int generate_layer_height_texture(void *data, int rows, int cols, bool force = true) %code%{ - THIS->update_layer_height_profile(); - SlicingParameters slicing_params = THIS->slicing_parameters(); - RETVAL = generate_layer_height_texture( - slicing_params, - generate_object_layers(slicing_params, THIS->layer_height_profile), - data, rows, cols, level_of_detail_2nd_level); + force |= THIS->update_layer_height_profile(THIS->model_object()->layer_height_profile); + if (force) { + SlicingParameters slicing_params = THIS->slicing_parameters(); + bool level_of_detail_2nd_level = true; + RETVAL = generate_layer_height_texture( + slicing_params, + generate_object_layers(slicing_params, THIS->model_object()->layer_height_profile), + data, rows, cols, level_of_detail_2nd_level); + } else + RETVAL = 0; %}; int ptr()