User interface of the variable layer thickness. Certainly not finished yet,
but sufficient for evaluation of the prints.
This commit is contained in:
parent
2d030f3a3c
commit
46b44fc141
@ -53,10 +53,15 @@ __PACKAGE__->mk_accessors( qw(_quat _dirty init
|
||||
origin
|
||||
_mouse_pos
|
||||
_hover_volume_idx
|
||||
|
||||
_drag_volume_idx
|
||||
_drag_start_pos
|
||||
_drag_start_xy
|
||||
_dragged
|
||||
|
||||
layer_editing_enabled
|
||||
_layer_height_edited
|
||||
|
||||
_camera_type
|
||||
_camera_target
|
||||
_camera_distance
|
||||
@ -74,7 +79,7 @@ use constant SELECTED_COLOR => [0,1,0,1];
|
||||
use constant HOVER_COLOR => [0.4,0.9,0,1];
|
||||
|
||||
# phi / theta angles to orient the camera.
|
||||
use constant VIEW_ISO => [45.0,45.0];
|
||||
use constant VIEW_DEFAULT => [45.0,45.0];
|
||||
use constant VIEW_LEFT => [90.0,90.0];
|
||||
use constant VIEW_RIGHT => [-90.0,90.0];
|
||||
use constant VIEW_TOP => [0.0,0.0];
|
||||
@ -82,7 +87,11 @@ use constant VIEW_BOTTOM => [0.0,180.0];
|
||||
use constant VIEW_FRONT => [0.0,90.0];
|
||||
use constant VIEW_REAR => [180.0,90.0];
|
||||
|
||||
use constant GIMBAL_LOCK_THETA_MAX => 170;
|
||||
use constant MANIPULATION_IDLE => 0;
|
||||
use constant MANIPULATION_DRAGGING => 1;
|
||||
use constant MANIPULATION_LAYER_HEIGHT => 2;
|
||||
|
||||
use constant GIMBALL_LOCK_THETA_MAX => 170;
|
||||
|
||||
# make OpenGL::Array thread-safe
|
||||
{
|
||||
@ -129,6 +138,11 @@ sub new {
|
||||
# $self->_camera_type('perspective');
|
||||
$self->_camera_target(Slic3r::Pointf3->new(0,0,0));
|
||||
$self->_camera_distance(0.);
|
||||
|
||||
# Size of a layer height texture, used by a shader to color map the object print layers.
|
||||
$self->{layer_preview_z_texture_width} = 512;
|
||||
$self->{layer_preview_z_texture_height} = 512;
|
||||
$self->{layer_height_edit_band_width} = 2.;
|
||||
|
||||
$self->reset_objects;
|
||||
|
||||
@ -177,6 +191,16 @@ sub new {
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub _first_selected_object_id {
|
||||
my ($self) = @_;
|
||||
for my $i (0..$#{$self->volumes}) {
|
||||
if ($self->volumes->[$i]->selected) {
|
||||
return int($self->volumes->[$i]->select_group_id / 1000000);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
sub mouse_event {
|
||||
my ($self, $e) = @_;
|
||||
|
||||
@ -191,40 +215,65 @@ sub mouse_event {
|
||||
# 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;
|
||||
|
||||
# select volume in this 3D canvas
|
||||
if ($self->enable_picking) {
|
||||
$self->deselect_volumes;
|
||||
$self->select_volume($volume_idx);
|
||||
|
||||
if ($volume_idx != -1) {
|
||||
my $group_id = $self->volumes->[$volume_idx]->select_group_id;
|
||||
my @volumes;
|
||||
if ($group_id != -1) {
|
||||
$self->select_volume($_)
|
||||
for grep $self->volumes->[$_]->select_group_id == $group_id,
|
||||
0..$#{$self->volumes};
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
$self->Refresh;
|
||||
}
|
||||
|
||||
# propagate event through callback
|
||||
$self->on_select->($volume_idx)
|
||||
if $self->on_select;
|
||||
|
||||
if ($volume_idx != -1) {
|
||||
if ($e->LeftDown && $self->enable_moving) {
|
||||
$self->_drag_volume_idx($volume_idx);
|
||||
$self->_drag_start_pos($self->mouse_to_3d(@$pos));
|
||||
} elsif ($e->RightDown) {
|
||||
# if right clicking on volume, propagate event through callback
|
||||
$self->on_right_click->($e->GetPosition)
|
||||
if $self->on_right_click;
|
||||
|
||||
if (! $self->_layer_height_edited) {
|
||||
# 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.
|
||||
if ($self->enable_picking && ($volume_idx != -1 || ! $self->layer_editing_enabled)) {
|
||||
$self->deselect_volumes;
|
||||
$self->select_volume($volume_idx);
|
||||
|
||||
if ($volume_idx != -1) {
|
||||
my $group_id = $self->volumes->[$volume_idx]->select_group_id;
|
||||
my @volumes;
|
||||
if ($group_id != -1) {
|
||||
$self->select_volume($_)
|
||||
for grep $self->volumes->[$_]->select_group_id == $group_id,
|
||||
0..$#{$self->volumes};
|
||||
}
|
||||
}
|
||||
|
||||
$self->Refresh;
|
||||
}
|
||||
|
||||
# propagate event through callback
|
||||
$self->on_select->($volume_idx)
|
||||
if $self->on_select;
|
||||
|
||||
if ($volume_idx != -1) {
|
||||
if ($e->LeftDown && $self->enable_moving) {
|
||||
$self->_drag_volume_idx($volume_idx);
|
||||
$self->_drag_start_pos($self->mouse_to_3d(@$pos));
|
||||
} elsif ($e->RightDown) {
|
||||
# if right clicking on volume, propagate event through callback
|
||||
$self->on_right_click->($e->GetPosition)
|
||||
if $self->on_right_click;
|
||||
}
|
||||
}
|
||||
}
|
||||
} elsif ($e->Dragging && $e->LeftIsDown && defined($self->_drag_volume_idx)) {
|
||||
} elsif ($e->Dragging && $e->LeftIsDown && ! $self->_layer_height_edited && defined($self->_drag_volume_idx)) {
|
||||
# get new position at the same Z of the initial click point
|
||||
my $mouse_ray = $self->mouse_ray($e->GetX, $e->GetY);
|
||||
my $cur_pos = $mouse_ray->intersect_plane($self->_drag_start_pos->z);
|
||||
@ -249,14 +298,29 @@ sub mouse_event {
|
||||
$self->_dragged(1);
|
||||
$self->Refresh;
|
||||
} elsif ($e->Dragging) {
|
||||
if ($e->LeftIsDown) {
|
||||
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;
|
||||
}
|
||||
} elsif ($e->LeftIsDown) {
|
||||
# if dragging over blank area with left button, rotate
|
||||
if (defined $self->_drag_start_pos) {
|
||||
my $orig = $self->_drag_start_pos;
|
||||
if (TURNTABLE_MODE) {
|
||||
$self->_sphi($self->_sphi + ($pos->x - $orig->x) * TRACKBALLSIZE);
|
||||
$self->_stheta($self->_stheta - ($pos->y - $orig->y) * TRACKBALLSIZE); #-
|
||||
$self->_stheta(GIMBAL_LOCK_THETA_MAX) if $self->_stheta > GIMBAL_LOCK_THETA_MAX;
|
||||
$self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX;
|
||||
$self->_stheta(0) if $self->_stheta < 0;
|
||||
} else {
|
||||
my $size = $self->GetClientSize;
|
||||
@ -304,6 +368,7 @@ sub mouse_event {
|
||||
$self->_drag_start_pos(undef);
|
||||
$self->_drag_start_xy(undef);
|
||||
$self->_dragged(undef);
|
||||
$self->_layer_height_edited(undef);
|
||||
} elsif ($e->Moving) {
|
||||
$self->_mouse_pos($pos);
|
||||
$self->Refresh;
|
||||
@ -340,8 +405,8 @@ sub select_view {
|
||||
if (ref($direction)) {
|
||||
$dirvec = $direction;
|
||||
} else {
|
||||
if ($direction eq 'iso') {
|
||||
$dirvec = VIEW_ISO;
|
||||
if ($direction eq 'default') {
|
||||
$dirvec = VIEW_DEFAULT;
|
||||
} elsif ($direction eq 'left') {
|
||||
$dirvec = VIEW_LEFT;
|
||||
} elsif ($direction eq 'right') {
|
||||
@ -356,22 +421,18 @@ sub select_view {
|
||||
$dirvec = VIEW_REAR;
|
||||
}
|
||||
}
|
||||
|
||||
$self->_sphi($dirvec->[0]);
|
||||
$self->_stheta($dirvec->[1]);
|
||||
|
||||
# Avoid gimbal lock.
|
||||
$self->_stheta(GIMBAL_LOCK_THETA_MAX) if $self->_stheta > GIMBAL_LOCK_THETA_MAX;
|
||||
$self->_stheta(0) if $self->_stheta < 0;
|
||||
|
||||
# View everything.
|
||||
$self->volumes_bounding_box->defined
|
||||
? $self->zoom_to_volumes
|
||||
: $self->zoom_to_bed;
|
||||
|
||||
$self->on_viewport_changed->() if $self->on_viewport_changed;
|
||||
$self->_dirty(1);
|
||||
$self->Refresh;
|
||||
my $bb = $self->volumes_bounding_box;
|
||||
if (! $bb->empty) {
|
||||
$self->_sphi($dirvec->[0]);
|
||||
$self->_stheta($dirvec->[1]);
|
||||
# Avoid gimball lock.
|
||||
$self->_stheta(GIMBALL_LOCK_THETA_MAX) if $self->_stheta > GIMBALL_LOCK_THETA_MAX;
|
||||
$self->_stheta(0) if $self->_stheta < 0;
|
||||
# View everything.
|
||||
$self->zoom_to_bounding_box($bb);
|
||||
$self->on_viewport_changed->() if $self->on_viewport_changed;
|
||||
$self->Refresh;
|
||||
}
|
||||
}
|
||||
|
||||
sub zoom_to_bounding_box {
|
||||
@ -380,7 +441,7 @@ sub zoom_to_bounding_box {
|
||||
|
||||
# calculate the zoom factor needed to adjust viewport to
|
||||
# bounding box
|
||||
my $max_size = max(@{$bb->size}) * 1.05;
|
||||
my $max_size = max(@{$bb->size}) * 2;
|
||||
my $min_viewport_size = min($self->GetSizeWH);
|
||||
$self->_zoom($min_viewport_size / $max_size);
|
||||
|
||||
@ -725,12 +786,19 @@ sub InitGL {
|
||||
$self->init(1);
|
||||
|
||||
my $shader;
|
||||
# $shader = $self->{shader} = new Slic3r::GUI::GLShader;
|
||||
$shader = $self->{shader} = new Slic3r::GUI::GLShader;
|
||||
if ($self->{shader}) {
|
||||
my $info = $shader->Load($self->_fragment_shader, $self->_vertex_shader);
|
||||
print $info if $info;
|
||||
|
||||
($self->{layer_preview_z_texture_id}) = glGenTextures_p(1);
|
||||
glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
|
||||
glClearColor(0, 0, 0, 1);
|
||||
glColor3f(1, 0, 0);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
@ -808,11 +876,15 @@ sub Render {
|
||||
glLightfv_p(GL_LIGHT0, GL_POSITION, -0.5, -0.5, 1, 0);
|
||||
glLightfv_p(GL_LIGHT0, GL_SPECULAR, 0.2, 0.2, 0.2, 1);
|
||||
glLightfv_p(GL_LIGHT0, GL_DIFFUSE, 0.5, 0.5, 0.5, 1);
|
||||
|
||||
# Head light
|
||||
glLightfv_p(GL_LIGHT1, GL_POSITION, 1, 0, 1, 0);
|
||||
|
||||
if ($self->enable_picking) {
|
||||
# Render the object for picking.
|
||||
# FIXME This cannot possibly work in a multi-sampled context as the color gets mangled by the anti-aliasing.
|
||||
# Better to use software ray-casting on a bounding-box hierarchy.
|
||||
glDisable(GL_MULTISAMPLE);
|
||||
glDisable(GL_LIGHTING);
|
||||
$self->draw_volumes(1);
|
||||
glFlush();
|
||||
@ -839,6 +911,7 @@ sub Render {
|
||||
glFlush();
|
||||
glFinish();
|
||||
glEnable(GL_LIGHTING);
|
||||
glEnable(GL_MULTISAMPLE);
|
||||
}
|
||||
|
||||
# draw fixed background
|
||||
@ -938,7 +1011,7 @@ sub Render {
|
||||
|
||||
# draw objects
|
||||
$self->draw_volumes;
|
||||
|
||||
|
||||
# draw cutting plane
|
||||
if (defined $self->cutting_plane_z) {
|
||||
my $plane_z = $self->cutting_plane_z;
|
||||
@ -957,6 +1030,8 @@ sub Render {
|
||||
glEnable(GL_CULL_FACE);
|
||||
glDisable(GL_BLEND);
|
||||
}
|
||||
|
||||
$self->draw_active_object_annotations;
|
||||
|
||||
glFlush();
|
||||
|
||||
@ -967,18 +1042,64 @@ sub draw_volumes {
|
||||
# $fakecolor is a boolean indicating, that the objects shall be rendered in a color coding the object index for picking.
|
||||
my ($self, $fakecolor) = @_;
|
||||
|
||||
$self->{shader}->Enable if (! $fakecolor && $self->{shader});
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
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);
|
||||
|
||||
foreach my $volume_idx (0..$#{$self->volumes}) {
|
||||
my $volume = $self->volumes->[$volume_idx];
|
||||
|
||||
if ($fakecolor) {
|
||||
|
||||
my $shader_active = 0;
|
||||
if ($self->layer_editing_enabled && ! $fakecolor && $volume->selected && $self->{shader} && $volume->{layer_height_texture_data} && $volume->{layer_height_texture_cells}) {
|
||||
$self->{shader}->Enable;
|
||||
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');
|
||||
die if ! defined($z_to_texture_row_id);
|
||||
die if ! defined($z_texture_row_to_normalized_id);
|
||||
die if ! defined($z_cursor_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);
|
||||
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);
|
||||
if (1) {
|
||||
glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},
|
||||
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2,
|
||||
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
# glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
# glPixelStorei(GL_UNPACK_ROW_LENGTH, $self->{layer_preview_z_texture_width});
|
||||
glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr);
|
||||
glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->offset($self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4));
|
||||
} else {
|
||||
glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},
|
||||
0, GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr);
|
||||
glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width}/2, $self->{layer_preview_z_texture_height}/2,
|
||||
0, GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr + $self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4);
|
||||
}
|
||||
|
||||
# my $nlines = ceil($ncells / ($self->{layer_preview_z_texture_width} - 1));
|
||||
|
||||
$shader_active = 1;
|
||||
} elsif ($fakecolor) {
|
||||
# Object picking mode. Render the object with a color encoding the object index.
|
||||
my $r = ($volume_idx & 0x000000FF) >> 0;
|
||||
my $g = ($volume_idx & 0x0000FF00) >> 8;
|
||||
@ -1055,8 +1176,13 @@ sub draw_volumes {
|
||||
}
|
||||
|
||||
glVertexPointer_c(3, GL_FLOAT, 0, 0);
|
||||
glNormalPointer_c(GL_FLOAT, 0, 0);
|
||||
glNormalPointer_c(GL_FLOAT, 0, 0);
|
||||
glPopMatrix();
|
||||
|
||||
if ($shader_active) {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
$self->{shader}->Disable;
|
||||
}
|
||||
}
|
||||
glDisableClientState(GL_NORMAL_ARRAY);
|
||||
glDisable(GL_BLEND);
|
||||
@ -1069,8 +1195,93 @@ sub draw_volumes {
|
||||
glVertexPointer_c(3, GL_FLOAT, 0, 0);
|
||||
}
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
}
|
||||
|
||||
$self->{shader}->Disable if (! $fakecolor && $self->{shader});
|
||||
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) = @_;
|
||||
|
||||
return if (! $self->{shader} || ! $self->layer_editing_enabled);
|
||||
|
||||
my $volume;
|
||||
foreach my $volume_idx (0..$#{$self->volumes}) {
|
||||
my $v = $self->volumes->[$volume_idx];
|
||||
if ($v->selected && $v->{layer_height_texture_data} && $v->{layer_height_texture_cells}) {
|
||||
$volume = $v;
|
||||
last;
|
||||
}
|
||||
}
|
||||
return if (! $volume);
|
||||
|
||||
# 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);
|
||||
|
||||
$self->{shader}->Enable;
|
||||
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 $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);
|
||||
glBindTexture(GL_TEXTURE_2D, $self->{layer_preview_z_texture_id});
|
||||
glTexImage2D_c(GL_TEXTURE_2D, 0, GL_RGBA8, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},
|
||||
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
glTexImage2D_c(GL_TEXTURE_2D, 1, GL_RGBA8, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2,
|
||||
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
||||
glTexSubImage2D_c(GL_TEXTURE_2D, 0, 0, 0, $self->{layer_preview_z_texture_width}, $self->{layer_preview_z_texture_height},
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->ptr);
|
||||
glTexSubImage2D_c(GL_TEXTURE_2D, 1, 0, 0, $self->{layer_preview_z_texture_width} / 2, $self->{layer_preview_z_texture_height} / 2,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, $volume->{layer_height_texture_data}->offset($self->{layer_preview_z_texture_width} * $self->{layer_preview_z_texture_height} * 4));
|
||||
|
||||
# Render the color bar.
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
# 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.
|
||||
glPushMatrix();
|
||||
glLoadIdentity();
|
||||
# Paint the overlay.
|
||||
glBegin(GL_QUADS);
|
||||
glVertex3f($bar_left, $bar_bottom, 0);
|
||||
glVertex3f($bar_right, $bar_bottom, 0);
|
||||
glVertex3f($bar_right, $bar_top, $volume->{bounding_box}->z_max);
|
||||
glVertex3f($bar_left, $bar_top, $volume->{bounding_box}->z_max);
|
||||
glEnd();
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
$self->{shader}->Disable;
|
||||
|
||||
# Paint the graph.
|
||||
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 $layer_height = $print_object->config->get('layer_height');
|
||||
# Baseline
|
||||
glColor3f(0., 0., 0.);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / 0.45, $bar_bottom);
|
||||
glVertex2f($bar_left + $layer_height * ($bar_right - $bar_left) / 0.45, $bar_top);
|
||||
glEnd();
|
||||
# Curve
|
||||
glColor3f(0., 0., 1.);
|
||||
glBegin(GL_LINE_STRIP);
|
||||
for (my $i = 0; $i < int(@{$profile}); $i += 2) {
|
||||
my $z = $profile->[$i];
|
||||
my $h = $profile->[$i+1];
|
||||
glVertex3f($bar_left + $h * ($bar_right - $bar_left) / 0.45, $bar_bottom + $z * ($bar_top - $bar_bottom) / $max_z, $z);
|
||||
}
|
||||
glEnd();
|
||||
# Revert the matrices.
|
||||
glPopMatrix();
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
}
|
||||
|
||||
sub _report_opengl_state
|
||||
@ -1106,57 +1317,61 @@ sub _vertex_shader {
|
||||
return <<'VERTEX';
|
||||
#version 110
|
||||
|
||||
#define LIGHT_TOP_DIR 0., 1., 0.
|
||||
#define LIGHT_TOP_DIFFUSE 0.2
|
||||
#define LIGHT_TOP_SPECULAR 0.3
|
||||
|
||||
#define LIGHT_FRONT_DIR 0., 0., 1.
|
||||
#define LIGHT_FRONT_DIFFUSE 0.5
|
||||
#define LIGHT_FRONT_SPECULAR 0.3
|
||||
|
||||
#define INTENSITY_AMBIENT 0.1
|
||||
|
||||
uniform float z_to_texture_row;
|
||||
varying float intensity_specular;
|
||||
varying float intensity_tainted;
|
||||
varying float object_z;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 normal, lightDir, viewVector, halfVector;
|
||||
vec4 diffuse, ambient, globalAmbient, specular = vec4(0.0);
|
||||
float NdotL,NdotHV;
|
||||
|
||||
vec3 eye, normal, lightDir, viewVector, halfVector;
|
||||
float NdotL, NdotHV;
|
||||
|
||||
// eye = gl_ModelViewMatrixInverse[3].xyz;
|
||||
eye = vec3(0., 0., 1.);
|
||||
|
||||
// First transform the normal into eye space and normalize the result.
|
||||
normal = normalize(gl_NormalMatrix * gl_Normal);
|
||||
|
||||
// Now normalize the light's direction. Note that according to the OpenGL specification, the light is stored in eye space.
|
||||
// Also since we're talking about a directional light, the position field is actually direction.
|
||||
lightDir = normalize(vec3(gl_LightSource[0].position));
|
||||
lightDir = vec3(LIGHT_TOP_DIR);
|
||||
halfVector = normalize(lightDir + eye);
|
||||
|
||||
// Compute the cos of the angle between the normal and lights direction. The light is directional so the direction is constant for every vertex.
|
||||
// Since these two are normalized the cosine is the dot product. We also need to clamp the result to the [0,1] range.
|
||||
NdotL = max(dot(normal, lightDir), 0.0);
|
||||
|
||||
// Compute the diffuse, ambient and globalAmbient terms.
|
||||
// diffuse = NdotL * (gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse);
|
||||
// ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
|
||||
diffuse = NdotL * (gl_Color * gl_LightSource[0].diffuse);
|
||||
ambient = gl_Color * gl_LightSource[0].ambient;
|
||||
globalAmbient = gl_LightModel.ambient * gl_FrontMaterial.ambient;
|
||||
|
||||
// compute the specular term if NdotL is larger than zero
|
||||
if (NdotL > 0.0) {
|
||||
NdotHV = max(dot(normal, normalize(gl_LightSource[0].halfVector.xyz)),0.0);
|
||||
specular = gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV,gl_FrontMaterial.shininess);
|
||||
}
|
||||
|
||||
intensity_tainted = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE;
|
||||
intensity_specular = 0.;
|
||||
|
||||
// if (NdotL > 0.0)
|
||||
// intensity_specular = LIGHT_TOP_SPECULAR * pow(max(dot(normal, halfVector), 0.0), gl_FrontMaterial.shininess);
|
||||
|
||||
// Perform the same lighting calculation for the 2nd light source.
|
||||
lightDir = normalize(vec3(gl_LightSource[1].position));
|
||||
lightDir = vec3(LIGHT_FRONT_DIR);
|
||||
halfVector = normalize(lightDir + eye);
|
||||
NdotL = max(dot(normal, lightDir), 0.0);
|
||||
// diffuse += NdotL * (gl_FrontMaterial.diffuse * gl_LightSource[1].diffuse);
|
||||
// ambient += gl_FrontMaterial.ambient * gl_LightSource[1].ambient;
|
||||
diffuse += NdotL * (gl_Color * gl_LightSource[1].diffuse);
|
||||
ambient += gl_Color * gl_LightSource[1].ambient;
|
||||
intensity_tainted += NdotL * LIGHT_FRONT_DIFFUSE;
|
||||
|
||||
// compute the specular term if NdotL is larger than zero
|
||||
if (NdotL > 0.0) {
|
||||
NdotHV = max(dot(normal, normalize(gl_LightSource[1].halfVector.xyz)),0.0);
|
||||
specular += gl_FrontMaterial.specular * gl_LightSource[1].specular * pow(NdotHV,gl_FrontMaterial.shininess);
|
||||
}
|
||||
|
||||
gl_FrontColor = globalAmbient + diffuse + ambient + specular;
|
||||
gl_FrontColor.a = 1.;
|
||||
|
||||
gl_Position = ftransform();
|
||||
// compute the specular term if NdotL is larger than zero
|
||||
if (NdotL > 0.0)
|
||||
intensity_specular += LIGHT_FRONT_SPECULAR * pow(max(dot(normal, halfVector), 0.0), gl_FrontMaterial.shininess);
|
||||
|
||||
// Scaled to widths of the Z texture.
|
||||
object_z = gl_Vertex.z / gl_Vertex.w;
|
||||
|
||||
gl_Position = ftransform();
|
||||
}
|
||||
|
||||
VERTEX
|
||||
@ -1165,17 +1380,39 @@ VERTEX
|
||||
sub _fragment_shader {
|
||||
return <<'FRAGMENT';
|
||||
#version 110
|
||||
|
||||
#define M_PI 3.1415926535897932384626433832795
|
||||
|
||||
// 2D texture (1D texture split by the rows) of color along the object Z axis.
|
||||
uniform sampler2D z_texture;
|
||||
// Scaling from the Z texture rows coordinate to the normalized texture row coordinate.
|
||||
uniform float z_to_texture_row;
|
||||
uniform float z_texture_row_to_normalized;
|
||||
|
||||
varying float intensity_specular;
|
||||
varying float intensity_tainted;
|
||||
varying float object_z;
|
||||
uniform float z_cursor;
|
||||
uniform float z_cursor_band_width;
|
||||
|
||||
void main()
|
||||
{
|
||||
float layer_height = 0.25;
|
||||
float layer_height2 = 0.5 * layer_height;
|
||||
float layer_center = floor(object_z / layer_height) * layer_height + layer_height2;
|
||||
float intensity = cos(M_PI * 0.7 * (layer_center - object_z) / layer_height);
|
||||
gl_FragColor = gl_Color * intensity;
|
||||
float object_z_row = z_to_texture_row * object_z;
|
||||
// Index of the row in the texture.
|
||||
float z_texture_row = floor(object_z_row);
|
||||
// Normalized coordinate from 0. to 1.
|
||||
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;
|
||||
// Scale z_texture_row to normalized coordinates.
|
||||
// Sample the Z texture.
|
||||
gl_FragColor =
|
||||
vec4(intensity_specular, intensity_specular, intensity_specular, 1.) +
|
||||
// intensity_tainted * texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5)), -2.5);
|
||||
(1. - z_blend) * intensity_tainted * texture2D(z_texture, vec2(z_texture_col, z_texture_row_to_normalized * (z_texture_row + 0.5)), -200.) +
|
||||
z_blend * vec4(1., 1., 0., 0.);
|
||||
// and reset the transparency.
|
||||
gl_FragColor.a = 1.;
|
||||
}
|
||||
|
||||
@ -1189,6 +1426,8 @@ use Moo;
|
||||
has 'bounding_box' => (is => 'ro', required => 1);
|
||||
has 'origin' => (is => 'rw', default => sub { Slic3r::Pointf3->new(0,0,0) });
|
||||
has 'color' => (is => 'ro', required => 1);
|
||||
# An ID containing the object ID, volume ID and instance ID.
|
||||
has 'composite_id' => (is => 'rw', default => sub { -1 });
|
||||
# An ID for group selection. It may be the same for all meshes of all object instances, or for just a single object instance.
|
||||
has 'select_group_id' => (is => 'rw', default => sub { -1 });
|
||||
# An ID for group dragging. It may be the same for all meshes of all object instances, or for just a single object instance.
|
||||
@ -1210,6 +1449,26 @@ has 'tverts' => (is => 'rw');
|
||||
# The offsets stores tripples of (z_top, qverts_idx, tverts_idx) in a linear array.
|
||||
has 'offsets' => (is => 'rw');
|
||||
|
||||
# RGBA texture along the Z axis of an object, to visualize layers by stripes colored by their height.
|
||||
has 'layer_height_texture_data' => (is => 'rw');
|
||||
# Number of texture cells.
|
||||
has 'layer_height_texture_cells' => (is => 'rw');
|
||||
|
||||
sub object_idx {
|
||||
my ($self) = @_;
|
||||
return $self->composite_id / 1000000;
|
||||
}
|
||||
|
||||
sub volume_idx {
|
||||
my ($self) = @_;
|
||||
return ($self->composite_id / 1000) % 1000;
|
||||
}
|
||||
|
||||
sub instance_idx {
|
||||
my ($self) = @_;
|
||||
return $self->composite_id % 1000;
|
||||
}
|
||||
|
||||
sub transformed_bounding_box {
|
||||
my ($self) = @_;
|
||||
|
||||
@ -1235,8 +1494,6 @@ __PACKAGE__->mk_accessors(qw(
|
||||
color_by
|
||||
select_by
|
||||
drag_by
|
||||
volumes_by_object
|
||||
_objects_by_volumes
|
||||
));
|
||||
|
||||
sub new {
|
||||
@ -1246,14 +1503,12 @@ sub new {
|
||||
$self->color_by('volume'); # object | volume
|
||||
$self->select_by('object'); # object | volume | instance
|
||||
$self->drag_by('instance'); # object | instance
|
||||
$self->volumes_by_object({}); # obj_idx => [ volume_idx, volume_idx ... ]
|
||||
$self->_objects_by_volumes({}); # volume_idx => [ obj_idx, instance_idx ]
|
||||
|
||||
return $self;
|
||||
}
|
||||
|
||||
sub load_object {
|
||||
my ($self, $model, $obj_idx, $instance_idxs) = @_;
|
||||
my ($self, $model, $print, $obj_idx, $instance_idxs) = @_;
|
||||
|
||||
my $model_object;
|
||||
if ($model->isa('Slic3r::Model::Object')) {
|
||||
@ -1265,6 +1520,19 @@ sub load_object {
|
||||
}
|
||||
|
||||
$instance_idxs ||= [0..$#{$model_object->instances}];
|
||||
|
||||
# Object will have a single common layer height texture for all volumes.
|
||||
my $layer_height_texture_data;
|
||||
my $layer_height_texture_cells;
|
||||
if ($print && $obj_idx < $print->object_count) {
|
||||
# Generate the layer height texture. Allocate data for the 0th and 1st mipmap levels.
|
||||
$layer_height_texture_data = OpenGL::Array->new($self->{layer_preview_z_texture_width}*$self->{layer_preview_z_texture_height}*5, GL_UNSIGNED_BYTE);
|
||||
# $print->get_object($obj_idx)->update_layer_height_profile_from_ranges();
|
||||
$layer_height_texture_cells = $print->get_object($obj_idx)->generate_layer_height_texture(
|
||||
$layer_height_texture_data->ptr,
|
||||
$self->{layer_preview_z_texture_height},
|
||||
$self->{layer_preview_z_texture_width});
|
||||
}
|
||||
|
||||
my @volumes_idx = ();
|
||||
foreach my $volume_idx (0..$#{$model_object->volumes}) {
|
||||
@ -1287,16 +1555,18 @@ 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,
|
||||
);
|
||||
$v->composite_id($obj_idx*1000000 + $volume_idx*1000 + $instance_idx);
|
||||
if ($self->select_by eq 'object') {
|
||||
$v->select_group_id($obj_idx*1000000);
|
||||
} elsif ($self->select_by eq 'volume') {
|
||||
$v->select_group_id($obj_idx*1000000 + $volume_idx*1000);
|
||||
} elsif ($self->select_by eq 'instance') {
|
||||
$v->select_group_id($obj_idx*1000000 + $volume_idx*1000 + $instance_idx);
|
||||
$v->select_group_id($v->composite_id);
|
||||
}
|
||||
if ($self->drag_by eq 'object') {
|
||||
$v->drag_group_id($obj_idx*1000);
|
||||
@ -1304,15 +1574,18 @@ sub load_object {
|
||||
$v->drag_group_id($obj_idx*1000 + $instance_idx);
|
||||
}
|
||||
push @volumes_idx, my $scene_volume_idx = $#{$self->volumes};
|
||||
$self->_objects_by_volumes->{$scene_volume_idx} = [ $obj_idx, $volume_idx, $instance_idx ];
|
||||
|
||||
my $verts = Slic3r::GUI::_3DScene::GLVertexArray->new;
|
||||
$verts->load_mesh($mesh);
|
||||
$v->tverts($verts);
|
||||
|
||||
if (! $volume->modifier) {
|
||||
$v->layer_height_texture_data($layer_height_texture_data);
|
||||
$v->layer_height_texture_cells($layer_height_texture_cells);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$self->volumes_by_object->{$obj_idx} = [@volumes_idx];
|
||||
return @volumes_idx;
|
||||
}
|
||||
|
||||
@ -1651,19 +1924,4 @@ sub _extrusionentity_to_verts {
|
||||
$tverts);
|
||||
}
|
||||
|
||||
sub object_idx {
|
||||
my ($self, $volume_idx) = @_;
|
||||
return $self->_objects_by_volumes->{$volume_idx}[0];
|
||||
}
|
||||
|
||||
sub volume_idx {
|
||||
my ($self, $volume_idx) = @_;
|
||||
return $self->_objects_by_volumes->{$volume_idx}[1];
|
||||
}
|
||||
|
||||
sub instance_idx {
|
||||
my ($self, $volume_idx) = @_;
|
||||
return $self->_objects_by_volumes->{$volume_idx}[2];
|
||||
}
|
||||
|
||||
1;
|
||||
|
@ -8,10 +8,11 @@ use utf8;
|
||||
use File::Basename qw(basename dirname);
|
||||
use List::Util qw(sum first max);
|
||||
use Slic3r::Geometry qw(X Y Z MIN MAX scale unscale deg2rad);
|
||||
use LWP::UserAgent;
|
||||
use threads::shared qw(shared_clone);
|
||||
use Wx qw(:button :cursor :dialog :filedialog :keycode :icon :font :id :listctrl :misc
|
||||
:panel :sizer :toolbar :window wxTheApp :notebook :combobox);
|
||||
use Wx::Event qw(EVT_BUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
|
||||
use Wx::Event qw(EVT_BUTTON EVT_TOGGLEBUTTON EVT_COMMAND EVT_KEY_DOWN EVT_LIST_ITEM_ACTIVATED
|
||||
EVT_LIST_ITEM_DESELECTED EVT_LIST_ITEM_SELECTED EVT_MOUSE_EVENTS EVT_PAINT EVT_TOOL
|
||||
EVT_CHOICE EVT_COMBOBOX EVT_TIMER EVT_NOTEBOOK_PAGE_CHANGED);
|
||||
use base 'Wx::Panel';
|
||||
@ -30,6 +31,7 @@ use constant TB_SCALE => &Wx::NewId;
|
||||
use constant TB_SPLIT => &Wx::NewId;
|
||||
use constant TB_CUT => &Wx::NewId;
|
||||
use constant TB_SETTINGS => &Wx::NewId;
|
||||
use constant TB_LAYER_EDITING => &Wx::NewId;
|
||||
|
||||
# package variables to avoid passing lexicals to threads
|
||||
our $THUMBNAIL_DONE_EVENT : shared = Wx::NewEventType;
|
||||
@ -94,7 +96,7 @@ sub new {
|
||||
|
||||
# Initialize 3D plater
|
||||
if ($Slic3r::GUI::have_OpenGL) {
|
||||
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{config});
|
||||
$self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $self->{print}, $self->{config});
|
||||
$self->{preview_notebook}->AddPage($self->{canvas3D}, '3D');
|
||||
$self->{canvas3D}->set_on_select_object($on_select_object);
|
||||
$self->{canvas3D}->set_on_double_click($on_double_click);
|
||||
@ -154,6 +156,9 @@ sub new {
|
||||
$self->{htoolbar}->AddTool(TB_CUT, "Cut…", Wx::Bitmap->new($Slic3r::var->("package.png"), wxBITMAP_TYPE_PNG), '');
|
||||
$self->{htoolbar}->AddSeparator;
|
||||
$self->{htoolbar}->AddTool(TB_SETTINGS, "Settings…", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), '');
|
||||
|
||||
# FIXME add a button for layer editing
|
||||
$self->{htoolbar}->AddCheckTool(TB_LAYER_EDITING, "Layer editing", Wx::Bitmap->new($Slic3r::var->("cog.png"), wxBITMAP_TYPE_PNG), '');
|
||||
} else {
|
||||
my %tbar_buttons = (
|
||||
add => "Add…",
|
||||
@ -168,12 +173,15 @@ sub new {
|
||||
split => "Split",
|
||||
cut => "Cut…",
|
||||
settings => "Settings…",
|
||||
layer_editing => "Layer editing",
|
||||
);
|
||||
$self->{btoolbar} = Wx::BoxSizer->new(wxHORIZONTAL);
|
||||
for (qw(add remove reset arrange increase decrease rotate45ccw rotate45cw changescale split cut settings)) {
|
||||
$self->{"btn_$_"} = Wx::Button->new($self, -1, $tbar_buttons{$_}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
|
||||
$self->{btoolbar}->Add($self->{"btn_$_"});
|
||||
}
|
||||
$self->{"btn_layer_editing"} = Wx::ToggleButton->new($self, -1, $tbar_buttons{'layer_editing'}, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
|
||||
$self->{btoolbar}->Add($self->{"btn_layer_editing"});
|
||||
}
|
||||
|
||||
$self->{list} = Wx::ListView->new($self, -1, wxDefaultPosition, wxDefaultSize,
|
||||
@ -256,6 +264,7 @@ sub new {
|
||||
EVT_TOOL($self, TB_SPLIT, sub { $self->split_object; });
|
||||
EVT_TOOL($self, TB_CUT, sub { $_[0]->object_cut_dialog });
|
||||
EVT_TOOL($self, TB_SETTINGS, sub { $_[0]->object_settings_dialog });
|
||||
EVT_TOOL($self, TB_LAYER_EDITING, sub { $self->on_layer_editing_toggled($self->{htoolbar}->GetToolState(TB_LAYER_EDITING)); });
|
||||
} else {
|
||||
EVT_BUTTON($self, $self->{btn_add}, sub { $self->add; });
|
||||
EVT_BUTTON($self, $self->{btn_remove}, sub { $self->remove() }); # explicitly pass no argument to remove
|
||||
@ -269,6 +278,7 @@ sub new {
|
||||
EVT_BUTTON($self, $self->{btn_split}, sub { $self->split_object; });
|
||||
EVT_BUTTON($self, $self->{btn_cut}, sub { $_[0]->object_cut_dialog });
|
||||
EVT_BUTTON($self, $self->{btn_settings}, sub { $_[0]->object_settings_dialog });
|
||||
EVT_TOGGLEBUTTON($self, $self->{btn_layer_editing}, sub { $self->on_layer_editing_toggled($self->{btn_layer_editing}->GetValue); });
|
||||
}
|
||||
|
||||
$_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self))
|
||||
@ -464,6 +474,13 @@ sub _on_select_preset {
|
||||
$self->on_config_change($self->GetFrame->config);
|
||||
}
|
||||
|
||||
sub on_layer_editing_toggled {
|
||||
my ($self, $new_state) = @_;
|
||||
print "on_layer_editing_toggled $new_state\n";
|
||||
$self->{canvas3D}->layer_editing_enabled($new_state);
|
||||
$self->{canvas3D}->update;
|
||||
}
|
||||
|
||||
sub GetFrame {
|
||||
my ($self) = @_;
|
||||
return &Wx::GetTopLevelParent($self);
|
||||
@ -1481,6 +1498,8 @@ sub on_thumbnail_made {
|
||||
# (i.e. when an object is added/removed/moved/rotated/scaled)
|
||||
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);
|
||||
@ -1679,7 +1698,7 @@ sub object_list_changed {
|
||||
my $have_objects = @{$self->{objects}} ? 1 : 0;
|
||||
my $method = $have_objects ? 'Enable' : 'Disable';
|
||||
$self->{"btn_$_"}->$method
|
||||
for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode);
|
||||
for grep $self->{"btn_$_"}, qw(reset arrange reslice export_gcode export_stl print send_gcode layer_editing);
|
||||
|
||||
if ($self->{export_gcode_output_file} || $self->{send_gcode_file}) {
|
||||
$self->{btn_reslice}->Disable;
|
||||
@ -1712,6 +1731,7 @@ sub selection_changed {
|
||||
if ($self->{object_info_size}) { # have we already loaded the info pane?
|
||||
if ($have_sel) {
|
||||
my $model_object = $self->{model}->objects->[$obj_idx];
|
||||
$model_object->print_info;
|
||||
my $model_instance = $model_object->instances->[0];
|
||||
$self->{object_info_size}->SetLabel(sprintf("%.2f x %.2f x %.2f", @{$model_object->instance_bounding_box(0)->size}));
|
||||
$self->{object_info_materials}->SetLabel($model_object->materials_count);
|
||||
|
@ -12,7 +12,7 @@ use base qw(Slic3r::GUI::3DScene Class::Accessor);
|
||||
|
||||
sub new {
|
||||
my $class = shift;
|
||||
my ($parent, $objects, $model, $config) = @_;
|
||||
my ($parent, $objects, $model, $print, $config) = @_;
|
||||
|
||||
my $self = $class->SUPER::new($parent);
|
||||
$self->enable_picking(1);
|
||||
@ -22,6 +22,7 @@ sub new {
|
||||
|
||||
$self->{objects} = $objects;
|
||||
$self->{model} = $model;
|
||||
$self->{print} = $print;
|
||||
$self->{config} = $config;
|
||||
$self->{on_select_object} = sub {};
|
||||
$self->{on_instances_moved} = sub {};
|
||||
@ -31,7 +32,7 @@ sub new {
|
||||
|
||||
my $obj_idx = undef;
|
||||
if ($volume_idx != -1) {
|
||||
$obj_idx = $self->object_idx($volume_idx);
|
||||
$obj_idx = $self->volumes->[$volume_idx]->object_idx;
|
||||
}
|
||||
$self->{on_select_object}->($obj_idx)
|
||||
if $self->{on_select_object};
|
||||
@ -42,8 +43,8 @@ sub new {
|
||||
my %done = (); # prevent moving instances twice
|
||||
foreach my $volume_idx (@volume_idxs) {
|
||||
my $volume = $self->volumes->[$volume_idx];
|
||||
my $obj_idx = $self->object_idx($volume_idx);
|
||||
my $instance_idx = $self->instance_idx($volume_idx);
|
||||
my $obj_idx = $volume->object_idx;
|
||||
my $instance_idx = $volume->instance_idx;
|
||||
next if $done{"${obj_idx}_${instance_idx}"};
|
||||
$done{"${obj_idx}_${instance_idx}"} = 1;
|
||||
|
||||
@ -89,7 +90,7 @@ sub update {
|
||||
$self->update_bed_size;
|
||||
|
||||
foreach my $obj_idx (0..$#{$self->{model}->objects}) {
|
||||
my @volume_idxs = $self->load_object($self->{model}, $obj_idx);
|
||||
my @volume_idxs = $self->load_object($self->{model}, $self->{print}, $obj_idx);
|
||||
|
||||
if ($self->{objects}[$obj_idx]->selected) {
|
||||
$self->select_volume($_) for @volume_idxs;
|
||||
|
@ -114,7 +114,7 @@ sub new {
|
||||
my $canvas;
|
||||
if ($Slic3r::GUI::have_OpenGL) {
|
||||
$canvas = $self->{canvas} = Slic3r::GUI::3DScene->new($self);
|
||||
$canvas->load_object($self->{model_object}, undef, [0]);
|
||||
$canvas->load_object($self->{model_object}, undef, undef, [0]);
|
||||
$canvas->set_auto_bed_shape;
|
||||
$canvas->SetSize([500,500]);
|
||||
$canvas->SetMinSize($canvas->GetSize);
|
||||
@ -244,7 +244,7 @@ sub _update {
|
||||
}
|
||||
|
||||
$self->{canvas}->reset_objects;
|
||||
$self->{canvas}->load_object($_, undef, [0]) for @objects;
|
||||
$self->{canvas}->load_object($_, undef, undef, [0]) for @objects;
|
||||
$self->{canvas}->SetCuttingPlane(
|
||||
$self->{cut_options}{z},
|
||||
[@expolygons],
|
||||
|
@ -22,6 +22,7 @@ sub new {
|
||||
my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
|
||||
my $object = $self->{model_object} = $params{model_object};
|
||||
my $print_object = $self->{print_object} = $params{print_object};
|
||||
|
||||
# create TreeCtrl
|
||||
my $tree = $self->{tree} = Wx::TreeCtrl->new($self, -1, wxDefaultPosition, [300, 100],
|
||||
@ -82,7 +83,7 @@ sub new {
|
||||
$self->reload_tree($canvas->volume_idx($volume_idx));
|
||||
});
|
||||
|
||||
$canvas->load_object($self->{model_object}, undef, [0]);
|
||||
$canvas->load_object($self->{model_object}, undef, undef, [0]);
|
||||
$canvas->set_auto_bed_shape;
|
||||
$canvas->SetSize([500,500]);
|
||||
$canvas->zoom_to_volumes;
|
||||
|
Loading…
Reference in New Issue
Block a user