Store / retrieve layer height profile from the AMF file.

Reset the layer height profile when changing a print profile to an incompatible one.
Reset button on the layer height bar.
Fixed an update issue on zooming by a scroll wheel.
Fixed an issue when loading an AMF file: Object names are now retained.
This commit is contained in:
bubnikv 2017-02-09 14:56:13 +01:00
parent 61c0ae4e94
commit 88e34ff5de
14 changed files with 379 additions and 154 deletions

View file

@ -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,

View file

@ -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);
}

View file

@ -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) = @_;