From d1f58cbed5d66d71d8de23f5efe959a594932ff8 Mon Sep 17 00:00:00 2001 From: Alessandro Ranellucci Date: Sat, 13 Dec 2014 20:41:03 +0100 Subject: [PATCH] Objects can be selected in 3D preview now. Double click and right click work as well --- lib/Slic3r/GUI/Plater.pm | 62 +++++++++++------- lib/Slic3r/GUI/Plater/2D.pm | 4 +- lib/Slic3r/GUI/Plater/3D.pm | 51 +++++++++++++-- lib/Slic3r/GUI/PreviewCanvas.pm | 110 ++++++++++++++++++++++++++++---- utils/view-mesh.pl | 1 + xs/xsp/Model.xsp | 2 + xs/xsp/Point.xsp | 4 ++ 7 files changed, 190 insertions(+), 44 deletions(-) diff --git a/lib/Slic3r/GUI/Plater.pm b/lib/Slic3r/GUI/Plater.pm index 3b0d037a2..82030388c 100644 --- a/lib/Slic3r/GUI/Plater.pm +++ b/lib/Slic3r/GUI/Plater.pm @@ -68,27 +68,25 @@ sub new { # Initialize preview notebook $self->{preview_notebook} = Wx::Notebook->new($self, -1, wxDefaultPosition, [335,335], wxNB_BOTTOM); - # Initialize 2D preview canvas - $self->{canvas} = Slic3r::GUI::Plater::2D->new($self->{preview_notebook}, wxDefaultSize, $self->{objects}, $self->{model}, $self->{config}); - $self->{preview_notebook}->AddPage($self->{canvas}, '2D'); - $self->{canvas}->on_select_object(sub { + # Initialize handlers for canvases + my $on_select_object = sub { my ($obj_idx) = @_; $self->select_object($obj_idx); - }); - $self->{canvas}->on_double_click(sub { + }; + my $on_double_click = sub { $self->object_settings_dialog if $self->selected_object; - }); - $self->{canvas}->on_right_click(sub { - my ($click_pos) = @_; + }; + my $on_right_click = sub { + my ($canvas, $click_pos) = @_; my ($obj_idx, $object) = $self->selected_object; return if !defined $obj_idx; my $menu = $self->object_menu; - $self->{canvas}->PopupMenu($menu, $click_pos); + $canvas->PopupMenu($menu, $click_pos); $menu->Destroy; - }); - $self->{canvas}->on_instance_moved(sub { + }; + my $on_instance_moved = sub { my ($obj_idx, $instance_idx) = @_; $self->update; @@ -100,12 +98,24 @@ sub new { } else { $self->resume_background_process; } - }); + }; + + # Initialize 2D preview canvas + $self->{canvas} = Slic3r::GUI::Plater::2D->new($self->{preview_notebook}, wxDefaultSize, $self->{objects}, $self->{model}, $self->{config}); + $self->{preview_notebook}->AddPage($self->{canvas}, '2D'); + $self->{canvas}->on_select_object($on_select_object); + $self->{canvas}->on_double_click($on_double_click); + $self->{canvas}->on_right_click(sub { $on_right_click->($self->{canvas}, @_); }); + $self->{canvas}->on_instance_moved($on_instance_moved); # Initialize 3D preview and toolpaths preview if ($Slic3r::GUI::have_OpenGL) { $self->{canvas3D} = Slic3r::GUI::Plater::3D->new($self->{preview_notebook}, $self->{objects}, $self->{model}, $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); + $self->{canvas3D}->set_on_right_click(sub { $on_right_click->($self->{canvas3D}, @_); }); + $self->{canvas3D}->set_on_instance_moved($on_instance_moved); $self->{toolpaths2D} = Slic3r::GUI::Plater::2DToolpaths->new($self->{preview_notebook}, $self->{print}); $self->{preview_notebook}->AddPage($self->{toolpaths2D}, 'Preview'); @@ -233,7 +243,7 @@ sub new { } $_->SetDropTarget(Slic3r::GUI::Plater::DropTarget->new($self)) - for $self, $self->{canvas}, $self->{list}; + for grep defined($_), $self, $self->{canvas}, $self->{canvas3D}, $self->{list}; EVT_COMMAND($self, -1, $THUMBNAIL_DONE_EVENT, sub { my ($self, $event) = @_; @@ -655,7 +665,7 @@ sub rotate { $self->selection_changed; # refresh info (size etc.) $self->update; - $self->{canvas}->Refresh; + $self->refresh_canvases; } sub flip { @@ -684,7 +694,7 @@ sub flip { $self->selection_changed; # refresh info (size etc.) $self->update; - $self->{canvas}->Refresh; + $self->refresh_canvases; } sub changescale { @@ -739,7 +749,7 @@ sub changescale { $self->selection_changed(1); # refresh info (size, volume etc.) $self->update; - $self->{canvas}->Refresh; + $self->refresh_canvases; } sub arrange { @@ -765,7 +775,7 @@ sub arrange { } else { $self->resume_background_process; } - $self->{canvas}->Refresh; + $self->refresh_canvases; } sub split_object { @@ -1146,7 +1156,7 @@ sub on_thumbnail_made { $self->{objects}[$obj_idx]->transform_thumbnail($self->{model}, $obj_idx); $self->update; - $self->{canvas}->Refresh; + $self->refresh_canvases; } # this method gets called whenever print center is changed or the objects' bounding box changes @@ -1158,8 +1168,7 @@ sub update { $self->{model}->center_instances_around_point($self->bed_centerf); } - $self->{canvas}->Refresh; - $self->{canvas3D}->update if $self->{canvas3D}; + $self->refresh_canvases; } sub on_extruders_change { @@ -1207,7 +1216,7 @@ sub list_item_deselected { if ($self->{list}->GetFirstSelected == -1) { $self->select_object(undef); - $self->{canvas}->Refresh; + $self->refresh_canvases; } } @@ -1217,7 +1226,7 @@ sub list_item_selected { my $obj_idx = $event->GetIndex; $self->select_object($obj_idx); - $self->{canvas}->Refresh; + $self->refresh_canvases; } sub list_item_activated { @@ -1385,6 +1394,13 @@ sub selected_object { return ($obj_idx, $self->{objects}[$obj_idx]), } +sub refresh_canvases { + my ($self) = @_; + + $self->{canvas}->Refresh; + $self->{canvas3D}->update if $self->{canvas3D}; +} + sub validate_config { my $self = shift; diff --git a/lib/Slic3r/GUI/Plater/2D.pm b/lib/Slic3r/GUI/Plater/2D.pm index e845cbe9a..c269420eb 100644 --- a/lib/Slic3r/GUI/Plater/2D.pm +++ b/lib/Slic3r/GUI/Plater/2D.pm @@ -210,14 +210,14 @@ sub mouse_event { } } $self->Refresh; - } elsif ($event->ButtonUp(&Wx::wxMOUSE_BTN_LEFT)) { + } elsif ($event->LeftUp) { $self->{on_instance_moved}->(@{ $self->{drag_object} }) if $self->{drag_object}; $self->Refresh; $self->{drag_start_pos} = undef; $self->{drag_object} = undef; $self->SetCursor(wxSTANDARD_CURSOR); - } elsif ($event->ButtonDClick) { + } elsif ($event->LeftDClick) { $self->{on_double_click}->(); } elsif ($event->Dragging) { return if !$self->{drag_start_pos}; # concurrency problems diff --git a/lib/Slic3r/GUI/Plater/3D.pm b/lib/Slic3r/GUI/Plater/3D.pm index 8f6960b7d..c7d6934a3 100644 --- a/lib/Slic3r/GUI/Plater/3D.pm +++ b/lib/Slic3r/GUI/Plater/3D.pm @@ -15,6 +15,7 @@ sub new { my ($parent, $objects, $model, $config) = @_; my $self = $class->SUPER::new($parent); + $self->enable_picking(1); $self->{objects} = $objects; $self->{model} = $model; @@ -24,23 +25,63 @@ sub new { $self->{on_right_click} = sub {}; $self->{on_instance_moved} = sub {}; - - return $self; } +sub set_on_select_object { + my ($self, $cb) = @_; + $self->on_select_object(sub { + my ($volume_idx) = @_; + + return $cb->(undef) if $volume_idx == -1; + my $obj_idx = $self->{_volumes_inv}{$volume_idx}; + return $cb->($obj_idx); + }); +} + +sub set_on_double_click { + my ($self, $cb) = @_; + $self->on_double_click($cb); +} + +sub set_on_right_click { + my ($self, $cb) = @_; + $self->on_right_click($cb); +} + +sub set_on_instance_moved { + my ($self, $cb) = @_; + $self->on_instance_moved(sub { + my ($volume_idx, $instance_idx) = @_; + + my $obj_idx = $self->{_volumes_inv}{$volume_idx}; + return $cb->($obj_idx, $instance_idx); + }); +} + sub update { my ($self) = @_; + $self->{_volumes} = {}; # obj_idx => [ volume_idx, volume_idx ] + $self->{_volumes_inv} = {}; # volume_idx => obj_idx $self->reset_objects; return if $self->{model}->objects_count == 0; $self->set_bounding_box($self->{model}->bounding_box); $self->set_bed_shape($self->{config}->bed_shape); - foreach my $model_object (@{$self->{model}->objects}) { - $self->load_object($model_object, 1); + foreach my $obj_idx (0..$#{$self->{model}->objects}) { + my $model_object = $self->{model}->get_object($obj_idx); + my @volume_idxs = $self->load_object($model_object, 1); + + # store mapping between canvas volumes and model objects + $self->{_volumes}{$obj_idx} = [ @volume_idxs ]; + $self->{_volumes_inv}{$_} = $obj_idx for @volume_idxs; + + if ($self->{objects}[$obj_idx]{selected}) { + $self->select_volume($_) for @volume_idxs; + } } } -1; +1; \ No newline at end of file diff --git a/lib/Slic3r/GUI/PreviewCanvas.pm b/lib/Slic3r/GUI/PreviewCanvas.pm index 369a1058b..ebe282079 100644 --- a/lib/Slic3r/GUI/PreviewCanvas.pm +++ b/lib/Slic3r/GUI/PreviewCanvas.pm @@ -14,6 +14,11 @@ use Wx::GLCanvas qw(:all); __PACKAGE__->mk_accessors( qw(quat dirty init mview_init object_bounding_box + enable_picking + on_select_object + on_double_click + on_right_click + on_instance_moved volumes initpos sphi stheta cutting_plane_z @@ -21,12 +26,14 @@ __PACKAGE__->mk_accessors( qw(quat dirty init mview_init bed_triangles bed_grid_lines origin + _mouse_gl_pos ) ); use constant TRACKBALLSIZE => 0.8; use constant TURNTABLE_MODE => 1; use constant GROUND_Z => 0.02; use constant SELECTED_COLOR => [0,1,0,1]; +use constant HOVER_COLOR => [0.8,0.8,0,1]; use constant COLORS => [ [1,1,0], [1,0.5,0.5], [0.5,1,0.5], [0.5,0.5,1] ]; # make OpenGL::Array thread-safe @@ -74,13 +81,49 @@ sub new { }); EVT_MOUSE_EVENTS($self, sub { my ($self, $e) = @_; - - if ($e->Dragging() && $e->LeftIsDown()) { - $self->handle_rotation($e); - } elsif ($e->Dragging() && $e->RightIsDown()) { - $self->handle_translation($e); - } elsif ($e->LeftUp() || $e->RightUp()) { + + my $pos = Slic3r::Pointf->new($e->GetPositionXY); + if ($e->LeftDClick) { + $self->on_double_click->() + if $self->on_double_click; + } elsif ($e->LeftDown || $e->RightDown) { + my $volume_idx = first { $self->volumes->[$_]->{hover} } 0..$#{$self->volumes}; + $volume_idx //= -1; + + # select volume in this 3D canvas + $self->select_volume($volume_idx); + $self->Refresh; + + # propagate event through callback + $self->on_select_object->($volume_idx) + if $self->on_select_object; + + if ($e->RightDown && $volume_idx != -1) { + # if right clicking on volume, propagate event through callback + $self->on_right_click->($e->GetPosition) + if $self->on_right_click; + } + } elsif ($e->Dragging) { + my $volume_idx = first { $self->volumes->[$_]->{hover} } 0..$#{$self->volumes}; + $volume_idx //= -1; + + if ($e->LeftIsDown && $volume_idx == -1) { + # if dragging over blank area with left button, rotate + $self->handle_rotation($e); + } elsif ($e->RightIsDown && $volume_idx == -1) { + # if dragging over blank area with right button, translate + $self->handle_translation($e); + } elsif ($e->LeftIsDown && $volume_idx != -1) { + # if dragging volume, move it + # TODO + } + } elsif ($e->LeftUp || $e->RightUp) { $self->initpos(undef); + } elsif ($e->Moving) { + my $glpos = $pos->clone; + $glpos->set_y($self->GetSize->GetHeight - $glpos->y); #)) + $self->_mouse_gl_pos($glpos); + $self->Refresh; } else { $e->Skip(); } @@ -163,9 +206,11 @@ sub load_object { # sort volumes: non-modifiers first my @volumes = sort { ($a->modifier // 0) <=> ($b->modifier // 0) } @{$object->volumes}; + my @volumes_idx = (); foreach my $volume (@volumes) { - my @instances = $all_instances ? @{$object->instances} : $object->instances->[0]; - foreach my $instance (@instances) { + my @instance_idxs = $all_instances ? (0..$#{$object->instances}) : (0); + foreach my $instance_idx (@instance_idxs) { + my $instance = $object->instances->[$instance_idx]; my $mesh = $volume->mesh->clone; $instance->transform_mesh($mesh); @@ -179,10 +224,12 @@ sub load_object { my $color = [ @{COLORS->[ $color_idx % scalar(@{&COLORS}) ]} ]; push @$color, $volume->modifier ? 0.5 : 1; push @{$self->volumes}, my $v = { - mesh => $mesh, - color => $color, - z_min => $z_min, + instance_idx => $instance_idx, + mesh => $mesh, + color => $color, + z_min => $z_min, }; + push @volumes_idx, $#{$self->volumes}; { my $vertices = $mesh->vertices; @@ -196,6 +243,16 @@ sub load_object { } } } + + return @volumes_idx; +} + +sub select_volume { + my ($self, $volume_idx) = @_; + + $_->{selected} = 0 for @{$self->volumes}; + $self->volumes->[$volume_idx]->{selected} = 1 + if $volume_idx != -1; } sub SetCuttingPlane { @@ -522,6 +579,23 @@ sub Render { my $center = $bb->center; glTranslatef(-$center->x, -$center->y, 0); #,, + if ($self->enable_picking) { + glDisable(GL_LIGHTING); + $self->draw_mesh(1); + glFlush(); + glFinish(); + + if (my $glpos = $self->_mouse_gl_pos) { + my $col = [ glReadPixels_p($glpos->x, $glpos->y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE) ]; + my $volume_idx = $col->[0] + $col->[1]*256 + $col->[2]*256*256; + $_->{hover} = 0 for @{$self->volumes}; + if ($volume_idx <= $#{$self->volumes}) { + $self->volumes->[$volume_idx]{hover} = 1; + } + } + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glEnable(GL_LIGHTING); + } # draw objects $self->draw_mesh; @@ -604,22 +678,30 @@ sub Render { } sub draw_mesh { - my $self = shift; + my ($self, $fakecolor) = @_; glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); - foreach my $volume (@{$self->volumes}) { + foreach my $volume_idx (0..$#{$self->volumes}) { + my $volume = $self->volumes->[$volume_idx]; glTranslatef(0, 0, -$volume->{z_min}); glVertexPointer_p(3, $volume->{verts}); glCullFace(GL_BACK); glNormalPointer_p($volume->{norms}); - if ($volume->{selected}) { + if ($fakecolor) { + my $r = ($volume_idx & 0x000000FF) >> 0; + my $g = ($volume_idx & 0x0000FF00) >> 8; + my $b = ($volume_idx & 0x00FF0000) >> 16; + glColor4f($r/255.0, $g/255.0, $b/255.0, 1); + } elsif ($volume->{selected}) { glColor4f(@{ &SELECTED_COLOR }); + } elsif ($volume->{hover}) { + glColor4f(@{ &HOVER_COLOR }); } else { glColor4f(@{ $volume->{color} }); } diff --git a/utils/view-mesh.pl b/utils/view-mesh.pl index a696d7c85..61d6748f6 100644 --- a/utils/view-mesh.pl +++ b/utils/view-mesh.pl @@ -32,6 +32,7 @@ my %opt = (); $model->add_default_instances; my $app = Slic3r::ViewMesh->new; + $app->{canvas}->enable_picking(1); $app->{canvas}->load_object($model->objects->[0]); $app->{canvas}->set_bounding_box($model->objects->[0]->bounding_box); $app->{canvas}->set_auto_bed_shape; diff --git a/xs/xsp/Model.xsp b/xs/xsp/Model.xsp index 8d795f7d6..2252d357d 100644 --- a/xs/xsp/Model.xsp +++ b/xs/xsp/Model.xsp @@ -20,6 +20,8 @@ void clear_objects(); size_t objects_count() %code%{ RETVAL = THIS->objects.size(); %}; + Ref get_object(int idx) + %code%{ RETVAL = THIS->objects.at(idx); %}; Ref get_material(t_model_material_id material_id) %code%{ diff --git a/xs/xsp/Point.xsp b/xs/xsp/Point.xsp index f058ead97..e02638a13 100644 --- a/xs/xsp/Point.xsp +++ b/xs/xsp/Point.xsp @@ -93,6 +93,10 @@ Point::coincides_with(point_sv) %code{% RETVAL = THIS->x; %}; double y() %code{% RETVAL = THIS->y; %}; + void set_x(double val) + %code{% THIS->x = val; %}; + void set_y(double val) + %code{% THIS->y = val; %}; void translate(double x, double y); void scale(double factor); void rotate(double angle, Pointf* center)