diff --git a/lib/Slic3r/GUI/3DScene.pm b/lib/Slic3r/GUI/3DScene.pm
index 794de1fc1..63d07ec6b 100644
--- a/lib/Slic3r/GUI/3DScene.pm
+++ b/lib/Slic3r/GUI/3DScene.pm
@@ -593,7 +593,7 @@ sub Resize {
         -$x/2, $x/2, -$y/2, $y/2,
         -$depth, 2*$depth,
     );
- 
+    
     glMatrixMode(GL_MODELVIEW);
 }
  
diff --git a/lib/Slic3r/GUI/Plater/2DToolpaths.pm b/lib/Slic3r/GUI/Plater/2DToolpaths.pm
index 953ecd5d0..2149d0205 100644
--- a/lib/Slic3r/GUI/Plater/2DToolpaths.pm
+++ b/lib/Slic3r/GUI/Plater/2DToolpaths.pm
@@ -84,6 +84,7 @@ sub reload_print {
     }
     
     $self->{canvas}->bb($self->print->total_bounding_box);
+    $self->{canvas}->Resize;
     
     my %z = ();  # z => 1
     foreach my $object (@{$self->{print}->objects}) {
@@ -119,11 +120,19 @@ use Wx::Event qw(EVT_PAINT EVT_SIZE EVT_IDLE EVT_MOUSEWHEEL EVT_MOUSE_EVENTS);
 use OpenGL qw(:glconstants :glfunctions :glufunctions :gluconstants);
 use base qw(Wx::GLCanvas Class::Accessor);
 use Wx::GLCanvas qw(:all);
-use List::Util qw(min first);
-use Slic3r::Geometry qw(scale unscale epsilon);
+use List::Util qw(min max first);
+use Slic3r::Geometry qw(scale unscale epsilon X Y);
 use Slic3r::Print::State ':steps';
 
-__PACKAGE__->mk_accessors(qw(print z layers color init bb _dirty));
+__PACKAGE__->mk_accessors(qw(
+    print z layers color init
+    bb
+    _camera_bb
+    _dirty
+    _zoom
+    _camera_target
+    _drag_start_xy
+));
 
 # make OpenGL::Array thread-safe
 {
@@ -136,6 +145,10 @@ sub new {
     
     my $self = $class->SUPER::new($parent);
     $self->print($print);
+    $self->_zoom(1);
+    
+    # 2D point in model space
+    $self->_camera_target(Slic3r::Pointf->new(0,0));
     
     EVT_PAINT($self, sub {
         my $dc = Wx::PaintDC->new($self);
@@ -145,13 +158,83 @@ sub new {
     EVT_IDLE($self, sub {
         return unless $self->_dirty;
         return if !$self->IsShownOnScreen;
-        $self->Resize( $self->GetSizeWH );
+        $self->Resize;
         $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));
+        $self->_zoom(1) if $self->_zoom > 1;
+        
+        # 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->_dirty(1);
+        $self->Resize;
+        $self->Refresh;
+    });
+    EVT_MOUSE_EVENTS($self, \&mouse_event);
     
     return $self;
 }
 
+sub mouse_event {
+    my ($self, $e) = @_;
+    
+    my $pos = Slic3r::Pointf->new($e->GetPositionXY);
+    if ($e->Entering && &Wx::wxMSW) {
+        # wxMSW needs focus in order to catch mouse wheel events
+        $self->SetFocus;
+    } elsif ($e->Dragging) {
+        if ($e->LeftIsDown || $e->MiddleIsDown || $e->RightIsDown) {
+            # if dragging, translate view
+            
+            if (defined $self->_drag_start_xy) {
+                my $move = $self->_drag_start_xy->vector_to($pos);  # in pixels
+                
+                # get viewport and camera size in order to convert pixel to model units
+                my ($x, $y) = $self->GetSizeWH;
+                my $camera_bb_size = $self->_camera_bb->size;
+                
+                my @translate = (
+                    -$move->x * $camera_bb_size->x / $x,
+                     $move->y * $camera_bb_size->y / $y,   # /**
+                );
+                
+                # keep camera_bb within total bb
+                if ($self->_camera_bb->x_min < $self->bb->x_min) {
+                    $translate[X] += $self->bb->x_min - $self->_camera_bb->x_min;
+                }
+                if ($self->_camera_bb->y_min < $self->bb->y_min) {
+                    $translate[Y] += $self->bb->y_min - $self->_camera_bb->y_min;
+                }
+                if ($self->_camera_bb->x_max > $self->bb->x_max) {
+                    $translate[X] -= $self->_camera_bb->x_max - $self->bb->x_max;
+                }
+                if ($self->_camera_bb->y_max > $self->bb->y_max) {
+                    $translate[Y] -= $self->_camera_bb->y_max - $self->bb->y_max;
+                }
+                
+                $self->_camera_target->translate(@translate);
+                
+                $self->Resize;
+                $self->Refresh;
+            }
+            $self->_drag_start_xy($pos);
+        }
+    } elsif ($e->LeftUp || $e->MiddleUp || $e->RightUp) {
+        $self->_drag_start_xy(undef);
+    } else {
+        $e->Skip();
+    }
+}
+
 sub set_z {
     my ($self, $z) = @_;
     
@@ -199,22 +282,6 @@ sub Render {
         return;
     }
     
-    glMatrixMode(GL_PROJECTION);
-    glLoadIdentity();
-    my $bb = $self->bb;
-    my ($x1, $y1, $x2, $y2) = ($bb->x_min, $bb->y_min, $bb->x_max, $bb->y_max);
-    my ($x, $y) = $self->GetSizeWH;
-    if (($x2 - $x1)/($y2 - $y1) > $x/$y) {
-        # adjust Y
-        my $new_y = $y * ($x2 - $x1) / $x;
-        $y1 = ($y2 + $y1)/2 - $new_y/2;
-        $y2 = $y1 + $new_y;
-    } else {
-        my $new_x = $x * ($y2 - $y1) / $y;
-        $x1 = ($x2 + $x1)/2 - $new_x/2;
-        $x2 = $x1 + $new_x;
-    }
-    glOrtho($x1, $x2, $y1, $y2, 0, 1);
     glDisable(GL_DEPTH_TEST);
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
@@ -400,12 +467,51 @@ sub SetCurrent {
 }
 
 sub Resize {
-    my ($self, $x, $y) = @_;
+    my ($self) = @_;
  
     return unless $self->GetContext;
+    return unless $self->bb;
+    $self->_dirty(0);
     
     $self->SetCurrent($self->GetContext);
+    my ($x, $y) = $self->GetSizeWH;
     glViewport(0, 0, $x, $y);
+    
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    
+    my $bb = $self->bb->clone;
+    
+    # center bounding box around origin before scaling it
+    my $bb_center = $bb->center;
+    $bb->translate(@{$bb_center->negative});
+    
+    # scale bounding box according to zoom factor
+    $bb->scale($self->_zoom);
+    
+    # reposition bounding box around original center
+    $bb->translate(@{$bb_center});
+    
+    # translate camera
+    $bb->translate(@{$self->_camera_target});
+    
+    # save camera
+    $self->_camera_bb($bb);
+    
+    my ($x1, $y1, $x2, $y2) = ($bb->x_min, $bb->y_min, $bb->x_max, $bb->y_max);
+    if (($x2 - $x1)/($y2 - $y1) > $x/$y) {
+        # adjust Y
+        my $new_y = $y * ($x2 - $x1) / $x;
+        $y1 = ($y2 + $y1)/2 - $new_y/2;
+        $y2 = $y1 + $new_y;
+    } else {
+        my $new_x = $x * ($y2 - $y1) / $y;
+        $x1 = ($x2 + $x1)/2 - $new_x/2;
+        $x2 = $x1 + $new_x;
+    }
+    glOrtho($x1, $x2, $y1, $y2, 0, 1);
+    
+    glMatrixMode(GL_MODELVIEW);
 }
 
 sub line {