Browse Source

Objects can be selected in 3D preview now. Double click and right click work as well

Alessandro Ranellucci 10 years ago
parent
commit
d1f58cbed5

+ 39 - 23
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;
     

+ 2 - 2
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

+ 46 - 5
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;

+ 96 - 14
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} });
         }

+ 1 - 0
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;

+ 2 - 0
xs/xsp/Model.xsp

@@ -20,6 +20,8 @@
     void clear_objects();
     size_t objects_count()
         %code%{ RETVAL = THIS->objects.size(); %};
+    Ref<ModelObject> get_object(int idx)
+        %code%{ RETVAL = THIS->objects.at(idx); %};
 
     Ref<ModelMaterial> get_material(t_model_material_id material_id)
         %code%{

+ 4 - 0
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)