Improvements in the manual layer width editor.

This commit is contained in:
bubnikv 2016-12-21 16:00:41 +01:00
parent 955bc957ba
commit 479f716625
4 changed files with 178 additions and 83 deletions

View File

@ -19,7 +19,8 @@ package Slic3r::GUI::3DScene::Base;
use strict;
use warnings;
use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS);
use Wx qw(:timer);
use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_ERASE_BACKGROUND EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS EVT_TIMER);
# must load OpenGL *before* Wx::GLCanvas
use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants);
use base qw(Wx::GLCanvas Class::Accessor);
@ -143,6 +144,10 @@ sub new {
$self->{layer_preview_z_texture_width} = 512;
$self->{layer_preview_z_texture_height} = 512;
$self->{layer_height_edit_band_width} = 2.;
$self->{layer_height_edit_strength} = 0.005;
$self->{layer_height_edit_last_object_id} = -1;
$self->{layer_height_edit_last_z} = 0.;
$self->{layer_height_edit_last_action} = 0;
$self->reset_objects;
@ -157,42 +162,24 @@ sub new {
$self->Resize( $self->GetSizeWH );
$self->Refresh;
});
EVT_MOUSEWHEEL($self, sub {
my ($self, $e) = @_;
# Calculate the zoom delta and apply it to the current zoom factor
my $zoom = $e->GetWheelRotation() / $e->GetWheelDelta();
$zoom = max(min($zoom, 4), -4);
$zoom /= 10;
$self->_zoom($self->_zoom / (1-$zoom));
# In order to zoom around the mouse point we need to translate
# the camera target
my $size = Slic3r::Pointf->new($self->GetSizeWH);
my $pos = Slic3r::Pointf->new($e->GetX, $size->y - $e->GetY); #-
$self->_camera_target->translate(
# ($pos - $size/2) represents the vector from the viewport center
# to the mouse point. By multiplying it by $zoom we get the new,
# transformed, length of such vector.
# Since we want that point to stay fixed, we move our camera target
# in the opposite direction by the delta of the length of such vector
# ($zoom - 1). We then scale everything by 1/$self->_zoom since
# $self->_camera_target is expressed in terms of model units.
-($pos->x - $size->x/2) * ($zoom) / $self->_zoom,
-($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;
});
EVT_MOUSEWHEEL($self, \&mouse_wheel_event);
EVT_MOUSE_EVENTS($self, \&mouse_event);
$self->{layer_height_edit_timer_id} = &Wx::NewId();
$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_edit_last_object_id} == -1;
$self->_variable_layer_thickness_action(undef, 1);
});
return $self;
}
sub Destroy {
my ($self) = @_;
$self->{layer_height_edit_timer}->Stop;
$self->DestroyGL;
return $self->SUPER::Destroy;
}
@ -207,43 +194,74 @@ sub _first_selected_object_id {
return -1;
}
# Returns an array with (left, top, right, bottom) of the variable layer thickness bar on the screen.
sub _variable_layer_thickness_bar_rect {
my ($self) = @_;
my ($cw, $ch) = $self->GetSizeWH;
my $bar_width = 70;
return ($cw - $bar_width, 0, $cw, $ch);
}
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;
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;
return unscale($self->{print}->get_object($object_idx)->size->z) * ($bar_bottom - $mouse_evt->GetY - 1.) / ($bar_bottom - $bar_top);
}
sub _variable_layer_thickness_action {
my ($self, $mouse_event, $do_modification) = @_;
# A volume is selected. Test, whether hovering over a layer thickness bar.
if (defined($mouse_event)) {
$self->{layer_height_edit_last_z} = $self->_variable_layer_thickness_bar_mouse_cursor_z($self->{layer_height_edit_last_object_id}, $mouse_event);
$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) {
$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},
$self->{layer_height_edit_band_width},
$self->{layer_height_edit_last_action});
$self->{print}->get_object($self->{layer_height_edit_last_object_id})->generate_layer_height_texture(
$self->volumes->[$self->{layer_height_edit_last_object_id}]->layer_height_texture_data->ptr,
$self->{layer_preview_z_texture_height},
$self->{layer_preview_z_texture_width});
$self->Refresh;
# Automatic action on mouse down with the same coordinate.
$self->{layer_height_edit_timer}->Start(100, wxTIMER_CONTINUOUS);
}
}
sub mouse_event {
my ($self, $e) = @_;
my $pos = Slic3r::Pointf->new($e->GetPositionXY);
my $object_idx_selected = $self->{layer_height_edit_last_object_id} = ($self->layer_editing_enabled && $self->{print}) ? $self->_first_selected_object_id : -1;
if ($e->Entering && &Wx::wxMSW) {
# wxMSW needs focus in order to catch mouse wheel events
$self->SetFocus;
} elsif ($e->LeftDClick) {
$self->on_double_click->()
if $self->on_double_click;
if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) {
} elsif ($self->on_double_click) {
$self->on_double_click->();
}
} elsif ($e->LeftDown || $e->RightDown) {
# If user pressed left or right button we first check whether this happened
# on a volume or not.
my $volume_idx = $self->_hover_volume_idx // -1;
$self->_layer_height_edited(0);
if ($self->layer_editing_enabled && $self->{print}) {
my $object_idx_selected = $self->_first_selected_object_id;
if ($object_idx_selected != -1) {
# A volume is selected. Test, whether hovering over a layer thickness bar.
my ($cw, $ch) = $self->GetSizeWH;
my $bar_width = 70;
if ($e->GetX >= $cw - $bar_width) {
# Start editing the layer height.
$self->_layer_height_edited(1);
my $z = unscale($self->{print}->get_object($object_idx_selected)->size->z) * ($ch - $e->GetY - 1.) / ($ch - 1);
# print "Modifying height profile at $z\n";
# $self->{print}->get_object($object_idx_selected)->adjust_layer_height_profile($z, $e->RightDown ? - 0.05 : 0.05, 2., 0);
$self->{print}->get_object($object_idx_selected)->generate_layer_height_texture(
$self->volumes->[$object_idx_selected]->layer_height_texture_data->ptr,
$self->{layer_preview_z_texture_height},
$self->{layer_preview_z_texture_width});
$self->Refresh;
}
}
}
if (! $self->_layer_height_edited) {
if ($object_idx_selected != -1 && $self->_variable_layer_thickness_bar_rect_mouse_inside($e)) {
# A volume is selected and the mouse is hovering over a layer thickness bar.
# Start editing the layer height.
$self->_layer_height_edited(1);
$self->_variable_layer_thickness_action($e, 1);
} else {
# Select volume in this 3D canvas.
# Don't deselect a volume if layer editing is enabled. We want the object to stay selected
# during the scene manipulation.
@ -304,21 +322,8 @@ sub mouse_event {
$self->_dragged(1);
$self->Refresh;
} elsif ($e->Dragging) {
if ($self->_layer_height_edited) {
my $object_idx_selected = $self->_first_selected_object_id;
if ($object_idx_selected != -1) {
# A volume is selected. Test, whether hovering over a layer thickness bar.
my ($cw, $ch) = $self->GetSizeWH;
my $z = unscale($self->{print}->get_object($object_idx_selected)->size->z) * ($ch - $e->GetY - 1.) / ($ch - 1);
# print "Modifying height profile at $z\n";
my $strength = 0.005;
$self->{print}->get_object($object_idx_selected)->adjust_layer_height_profile($z, $e->RightIsDown ? - $strength : $strength, 2., $e->ShiftDown ? 1 : 0);
$self->{print}->get_object($object_idx_selected)->generate_layer_height_texture(
$self->volumes->[$object_idx_selected]->layer_height_texture_data->ptr,
$self->{layer_preview_z_texture_height},
$self->{layer_preview_z_texture_width});
$self->Refresh;
}
if ($self->_layer_height_edited && $object_idx_selected != -1) {
$self->_variable_layer_thickness_action($e, 0);
} elsif ($e->LeftIsDown) {
# if dragging over blank area with left button, rotate
if (defined $self->_drag_start_pos) {
@ -375,6 +380,7 @@ sub mouse_event {
$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
@ -385,6 +391,49 @@ sub mouse_event {
}
}
sub mouse_wheel_event {
my ($self, $e) = @_;
if ($self->layer_editing_enabled && $self->{print}) {
my $object_idx_selected = $self->_first_selected_object_id;
if ($object_idx_selected != -1) {
# A volume is selected. Test, whether hovering over a layer thickness bar.
if ($self->_variable_layer_thickness_bar_rect_mouse_inside($e)) {
# Adjust the width of the selection.
$self->{layer_height_edit_band_width} = max(min($self->{layer_height_edit_band_width} * (1 + 0.1 * $e->GetWheelRotation() / $e->GetWheelDelta()), 10.), 1.5);
$self->Refresh;
return;
}
}
}
# Calculate the zoom delta and apply it to the current zoom factor
my $zoom = $e->GetWheelRotation() / $e->GetWheelDelta();
$zoom = max(min($zoom, 4), -4);
$zoom /= 10;
$self->_zoom($self->_zoom / (1-$zoom));
# In order to zoom around the mouse point we need to translate
# the camera target
my $size = Slic3r::Pointf->new($self->GetSizeWH);
my $pos = Slic3r::Pointf->new($e->GetX, $size->y - $e->GetY); #-
$self->_camera_target->translate(
# ($pos - $size/2) represents the vector from the viewport center
# to the mouse point. By multiplying it by $zoom we get the new,
# transformed, length of such vector.
# Since we want that point to stay fixed, we move our camera target
# in the opposite direction by the delta of the length of such vector
# ($zoom - 1). We then scale everything by 1/$self->_zoom since
# $self->_camera_target is expressed in terms of model units.
-($pos->x - $size->x/2) * ($zoom) / $self->_zoom,
-($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;
}
# Reset selection.
sub reset_objects {
my ($self) = @_;
@ -1084,14 +1133,17 @@ sub draw_volumes {
my $z_to_texture_row_id = $self->{shader}->Map('z_to_texture_row');
my $z_texture_row_to_normalized_id = $self->{shader}->Map('z_texture_row_to_normalized');
my $z_cursor_id = $self->{shader}->Map('z_cursor');
my $z_cursor_band_width_id = $self->{shader}->Map('z_cursor_band_width');
die if ! defined($z_to_texture_row_id);
die if ! defined($z_texture_row_to_normalized_id);
die if ! defined($z_cursor_id);
die if ! defined($z_cursor_band_width_id);
my $ncells = $volume->{layer_height_texture_cells};
my $z_max = $volume->{bounding_box}->z_max;
glUniform1fARB($z_to_texture_row_id, ($ncells - 1) / ($self->{layer_preview_z_texture_width} * $z_max));
glUniform1fARB($z_texture_row_to_normalized_id, 1. / $self->{layer_preview_z_texture_height});
glUniform1fARB($z_cursor_id, $z_max * $z_cursor_relative);
glUniform1fARB($z_cursor_band_width_id, $self->{layer_height_edit_band_width});
glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LEVEL, 0);
# glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
@ -1421,7 +1473,7 @@ void main()
float z_texture_col = object_z_row - z_texture_row;
// float z_blend = 0.5 + 0.5 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) / 3.)));
// float z_blend = 0.5 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor)))) + 0.5;
float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor)))) + 0.25;
float z_blend = 0.25 * cos(min(M_PI, abs(M_PI * (object_z - z_cursor) * 1.8 / z_cursor_band_width))) + 0.25;
// Scale z_texture_row to normalized coordinates.
// Sample the Z texture.
gl_FragColor =

View File

@ -290,7 +290,7 @@ void adjust_layer_height_profile(
coordf_t z,
coordf_t layer_thickness_delta,
coordf_t band_width,
int action)
LayerHeightEditActionType action)
{
// Constrain the profile variability by the 1st layer height.
std::pair<coordf_t, coordf_t> z_span_variable =
@ -320,8 +320,10 @@ void adjust_layer_height_profile(
// 2) Is it possible to apply the delta?
switch (action) {
case 0:
default:
case LAYER_HEIGHT_EDIT_ACTION_DECREASE:
layer_thickness_delta = - layer_thickness_delta;
// fallthrough
case LAYER_HEIGHT_EDIT_ACTION_INCREASE:
if (layer_thickness_delta > 0) {
if (current_layer_height >= slicing_params.max_layer_height - EPSILON)
return;
@ -332,12 +334,16 @@ void adjust_layer_height_profile(
layer_thickness_delta = std::max(layer_thickness_delta, slicing_params.min_layer_height - current_layer_height);
}
break;
case 1:
case LAYER_HEIGHT_EDIT_ACTION_REDUCE:
case LAYER_HEIGHT_EDIT_ACTION_SMOOTH:
layer_thickness_delta = std::abs(layer_thickness_delta);
layer_thickness_delta = std::min(layer_thickness_delta, std::abs(slicing_params.layer_height - current_layer_height));
if (layer_thickness_delta < EPSILON)
return;
break;
default:
assert(false);
break;
}
// 3) Densify the profile inside z +- band_width/2, remove duplicate Zs from the height profile inside the band.
@ -354,6 +360,7 @@ void adjust_layer_height_profile(
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);
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];
@ -368,11 +375,11 @@ void adjust_layer_height_profile(
coordf_t weight = std::abs(zz - z) < 0.5 * band_width ? (0.5 + 0.5 * cos(2. * M_PI * (zz - z) / band_width)) : 0.;
coordf_t height_new = height;
switch (action) {
case 0:
default:
case LAYER_HEIGHT_EDIT_ACTION_INCREASE:
case LAYER_HEIGHT_EDIT_ACTION_DECREASE:
height += weight * layer_thickness_delta;
break;
case 1:
case LAYER_HEIGHT_EDIT_ACTION_REDUCE:
{
coordf_t delta = height - slicing_params.layer_height;
coordf_t step = weight * layer_thickness_delta;
@ -382,6 +389,14 @@ void adjust_layer_height_profile(
height += step;
break;
}
case LAYER_HEIGHT_EDIT_ACTION_SMOOTH:
{
// Don't modify the profile during resampling process, do it at the next step.
break;
}
default:
assert(false);
break;
}
// Avoid entering a too short segment.
if (profile_new[profile_new.size() - 2] + EPSILON < zz) {
@ -396,15 +411,35 @@ void adjust_layer_height_profile(
}
i += 2;
assert(i > 0);
size_t i_resampled_end = profile_new.size();
if (i < layer_height_profile.size()) {
if (profile_new[profile_new.size() - 2] + z_step < layer_height_profile[i]) {
profile_new.push_back(profile_new[profile_new.size() - 2] + z_step);
profile_new.push_back(layer_height_profile[i + 1]);
}
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());
}
layer_height_profile = std::move(profile_new);
if (action == LAYER_HEIGHT_EDIT_ACTION_SMOOTH) {
size_t n_rounds = 6;
for (size_t i_round = 0; i_round < n_rounds; ++ i_round) {
profile_new = layer_height_profile;
for (size_t i = i_resampled_start; i < i_resampled_end; i += 2) {
coordf_t zz = profile_new[i];
coordf_t t = std::abs(zz - z) < 0.5 * band_width ? (0.25 + 0.25 * cos(2. * M_PI * (zz - z) / band_width)) : 0.;
assert(t >= 0. && t <= 0.5000001);
if (i == 0)
layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + t * profile_new[i + 3];
else if (i + 1 == profile_new.size())
layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + t * profile_new[i - 1];
else
layer_height_profile[i + 1] = (1. - t) * profile_new[i + 1] + 0.5 * t * (profile_new[i - 1] + profile_new[i + 3]);
}
}
}
assert(layer_height_profile.size() > 2);
assert(layer_height_profile.size() % 2 == 0);
assert(layer_height_profile[0] == 0.);

View File

@ -103,13 +103,21 @@ extern std::vector<coordf_t> layer_height_profile_adaptive(
const t_layer_height_ranges &layer_height_ranges,
const ModelVolumePtrs &volumes);
enum LayerHeightEditActionType {
LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0,
LAYER_HEIGHT_EDIT_ACTION_DECREASE = 1,
LAYER_HEIGHT_EDIT_ACTION_REDUCE = 2,
LAYER_HEIGHT_EDIT_ACTION_SMOOTH = 3
};
extern void adjust_layer_height_profile(
const SlicingParameters &slicing_params,
std::vector<coordf_t> &layer_height_profile,
coordf_t z,
coordf_t layer_thickness_delta,
coordf_t band_width,
int action);
LayerHeightEditActionType action);
// Produce object layers as pairs of low / high layer boundaries, stored into a linear vector.
// The object layers are based at z=0, ignoring the raft layers.

View File

@ -125,7 +125,7 @@ _constant()
%code%{
THIS->update_layer_height_profile();
adjust_layer_height_profile(
THIS->slicing_parameters(), THIS->layer_height_profile, z, layer_thickness_delta, band_width, action);
THIS->slicing_parameters(), THIS->layer_height_profile, z, layer_thickness_delta, band_width, LayerHeightEditActionType(action));
%};
int generate_layer_height_texture(void *data, int rows, int cols, bool level_of_detail_2nd_level = true)