diff --git a/lib/Slic3r/GUI/PreviewCanvas.pm b/lib/Slic3r/GUI/PreviewCanvas.pm
index 25b5de93c..0e0d22c8f 100644
--- a/lib/Slic3r/GUI/PreviewCanvas.pm
+++ b/lib/Slic3r/GUI/PreviewCanvas.pm
@@ -15,6 +15,7 @@ use Wx::GLCanvas qw(:all);
 __PACKAGE__->mk_accessors( qw(quat dirty init mview_init
                               object_bounding_box
                               enable_picking
+                              enable_moving
                               on_select_object
                               on_double_click
                               on_right_click
@@ -27,6 +28,8 @@ __PACKAGE__->mk_accessors( qw(quat dirty init mview_init
                               bed_grid_lines
                               origin
                               _mouse_gl_pos
+                              _drag_volume_idx
+                              _drag_start_pos
                               ) );
 
 use constant TRACKBALLSIZE => 0.8;
@@ -74,8 +77,8 @@ sub new {
         
         my $zoom = ($e->GetWheelRotation() / $e->GetWheelDelta() / 10);
         $zoom = $zoom > 0 ?  (1.0 + $zoom) : 1 / (1.0 - $zoom);
-        my @pos3d = $self->mouse_to_3d($e->GetX(), $e->GetY());
-        $self->ZoomTo($zoom, $pos3d[0], $pos3d[1]);
+        my $pos3d = $self->mouse_to_3d($e->GetX(), $e->GetY());
+        $self->ZoomTo($zoom, $pos3d->x, $pos3d->y);  #))
         
         $self->Refresh;
     });
@@ -98,11 +101,28 @@ sub new {
             $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;
+            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)) {
+            # get volume being dragged
+            my $volume = $self->volumes->[$self->_drag_volume_idx];
+            
+            # get new position and calculate the move vector
+            my $cur_pos = $self->mouse_to_3d(@$pos);
+            my $vector = $self->_drag_start_pos->vector_to($cur_pos);
+            
+            # apply new temporary volume origin and ignore Z
+            $volume->{origin}->set_x(-$vector->x);
+            $volume->{origin}->set_y(-$vector->y);  #))
+            $self->Refresh;
         } elsif ($e->Dragging) {
             my $volume_idx = first { $self->volumes->[$_]->{hover} } 0..$#{$self->volumes};
             $volume_idx //= -1;
@@ -113,12 +133,16 @@ sub new {
             } 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);
+            
+            if ($self->on_instance_moved && defined $self->_drag_volume_idx) {
+                my $volume = $self->volumes->[$self->_drag_volume_idx];
+                $self->on_instance_moved($self->_drag_volume_idx, $volume->{instance_idx});
+            }
+            $self->_drag_volume_idx(undef);
+            $self->_drag_start_pos(undef);
         } elsif ($e->Moving) {
             my $glpos = $pos->clone;
             $glpos->set_y($self->GetSize->GetHeight - $glpos->y);   #))
@@ -227,7 +251,7 @@ sub load_object {
                 instance_idx    => $instance_idx,
                 mesh            => $mesh,
                 color           => $color,
-                z_min           => $z_min,
+                origin          => Slic3r::Pointf3->new(0,0,$z_min),
             };
             push @volumes_idx, $#{$self->volumes};
         
@@ -264,7 +288,7 @@ sub SetCuttingPlane {
     my @verts = ();
     foreach my $volume (@{$self->volumes}) {
         foreach my $volume (@{$self->volumes}) {
-            my $expolygons = $volume->{mesh}->slice([ $z + $volume->{z_min} ])->[0];
+            my $expolygons = $volume->{mesh}->slice([ $z + $volume->{origin}->z ])->[0];
             $expolygons = offset_ex([ map @$_, @$expolygons ], scale 0.1);
             
             foreach my $line (map @{$_->lines}, map @$_, @$expolygons) {
@@ -418,9 +442,9 @@ sub handle_translation {
     } else {
         my $new = $e->GetPosition();
         my $orig = $self->initpos;
-        my @orig3d = $self->mouse_to_3d($orig->x, $orig->y);             #)()
-        my @new3d = $self->mouse_to_3d($new->x, $new->y);                #)()
-        glTranslatef($new3d[0] - $orig3d[0], $new3d[1] - $orig3d[1], 0);
+        my $orig3d = $self->mouse_to_3d($orig->x, $orig->y);             #)()
+        my $new3d = $self->mouse_to_3d($new->x, $new->y);                #)()
+        glTranslatef($new3d->x - $orig3d->x, $new3d->y - $orig3d->y, 0); #--
         $self->initpos($new);
         $self->Refresh;
     }
@@ -434,7 +458,7 @@ sub mouse_to_3d {
     my @proj        = glGetDoublev_p(GL_PROJECTION_MATRIX);     # 16 items
 
     my @projected = gluUnProject_p($x, $viewport[3] - $y, 1.0, @mview, @proj, @viewport);
-    return @projected;
+    return Slic3r::Pointf3->new(@projected);
 }
 
 sub ZoomTo {
@@ -581,7 +605,7 @@ sub Render {
     
     if ($self->enable_picking) {
         glDisable(GL_LIGHTING);
-        $self->draw_mesh(1);
+        $self->draw_volumes(1);
         glFlush();
         glFinish();
         
@@ -599,7 +623,7 @@ sub Render {
         glEnable(GL_LIGHTING);
     }
     # draw objects
-    $self->draw_mesh;
+    $self->draw_volumes;
     
     # draw ground and axes
     glDisable(GL_LIGHTING);
@@ -679,7 +703,7 @@ sub Render {
     $self->SwapBuffers();
 }
 
-sub draw_mesh {
+sub draw_volumes {
     my ($self, $fakecolor) = @_;
     
     glEnable(GL_BLEND);
@@ -689,7 +713,7 @@ sub draw_mesh {
     
     foreach my $volume_idx (0..$#{$self->volumes}) {
         my $volume = $self->volumes->[$volume_idx];
-        glTranslatef(0, 0, -$volume->{z_min});
+        glTranslatef(@{$volume->{origin}->negative});
         
         glVertexPointer_p(3, $volume->{verts});
         
@@ -709,7 +733,7 @@ sub draw_mesh {
         }
         glDrawArrays(GL_TRIANGLES, 0, $volume->{verts}->elements / 3);
         
-        glTranslatef(0, 0, +$volume->{z_min});
+        glTranslatef(@{$volume->{origin}});
     }
     glDisableClientState(GL_NORMAL_ARRAY);
     glDisable(GL_BLEND);
diff --git a/utils/view-mesh.pl b/utils/view-mesh.pl
index 61d6748f6..58f641a92 100644
--- a/utils/view-mesh.pl
+++ b/utils/view-mesh.pl
@@ -20,6 +20,7 @@ my %opt = ();
     my %options = (
         'help'                  => sub { usage() },
         'cut=f'                 => \$opt{cut},
+        'enable-moving'         => \$opt{enable_moving},
     );
     GetOptions(%options) or usage(1);
     $ARGV[0] or usage(1);
@@ -33,6 +34,7 @@ my %opt = ();
     
     my $app = Slic3r::ViewMesh->new;
     $app->{canvas}->enable_picking(1);
+    $app->{canvas}->enable_moving($opt{enable_moving});
     $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/src/libslic3r/Point.cpp b/xs/src/libslic3r/Point.cpp
index 5f3f1da01..63fe63cfb 100644
--- a/xs/src/libslic3r/Point.cpp
+++ b/xs/src/libslic3r/Point.cpp
@@ -218,6 +218,12 @@ Point::negative() const
     return Point(-this->x, -this->y);
 }
 
+Vector
+Point::vector_to(const Point &point) const
+{
+    return Vector(point.x - this->x, point.y - this->y);
+}
+
 Point
 operator+(const Point& point1, const Point& point2)
 {
@@ -353,6 +359,27 @@ Pointf3::translate(double x, double y, double z)
     this->z += z;
 }
 
+double
+Pointf3::distance_to(const Pointf3 &point) const
+{
+    double dx = ((double)point.x - this->x);
+    double dy = ((double)point.y - this->y);
+    double dz = ((double)point.z - this->z);
+    return sqrt(dx*dx + dy*dy + dz*dz);
+}
+
+Pointf3
+Pointf3::negative() const
+{
+    return Pointf3(-this->x, -this->y, -this->z);
+}
+
+Vectorf3
+Pointf3::vector_to(const Pointf3 &point) const
+{
+    return Vectorf3(point.x - this->x, point.y - this->y, point.z - this->z);
+}
+
 #ifdef SLIC3RXS
 REGISTER_CLASS(Pointf3, "Pointf3");
 #endif
diff --git a/xs/src/libslic3r/Point.hpp b/xs/src/libslic3r/Point.hpp
index 271c9584e..6f4d317a4 100644
--- a/xs/src/libslic3r/Point.hpp
+++ b/xs/src/libslic3r/Point.hpp
@@ -53,6 +53,7 @@ class Point
     Point projection_onto(const MultiPoint &poly) const;
     Point projection_onto(const Line &line) const;
     Point negative() const;
+    Vector vector_to(const Point &point) const;
     
     #ifdef SLIC3RXS
     void from_SV(SV* point_sv);
@@ -96,6 +97,9 @@ class Pointf3 : public Pointf
     void scale(double factor);
     void translate(const Vectorf3 &vector);
     void translate(double x, double y, double z);
+    double distance_to(const Pointf3 &point) const;
+    Pointf3 negative() const;
+    Vectorf3 vector_to(const Pointf3 &point) const;
 };
 
 }
diff --git a/xs/xsp/Point.xsp b/xs/xsp/Point.xsp
index e02638a13..dbf2f9bac 100644
--- a/xs/xsp/Point.xsp
+++ b/xs/xsp/Point.xsp
@@ -121,4 +121,10 @@ Point::coincides_with(point_sv)
     void set_z(double val)
         %code{% THIS->z = val; %};
     void translate(double x, double y, double z);
+    double distance_to(Pointf3* point)
+        %code{% RETVAL = THIS->distance_to(*point); %};
+    Clone<Pointf3> negative()
+        %code{% RETVAL = THIS->negative(); %};
+    Clone<Pointf3> vector_to(Pointf3* point)
+        %code{% RETVAL = THIS->vector_to(*point); %};
 };